@push.rocks/smartproxy 19.6.6 → 19.6.8
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/dist_ts/core/models/wrapped-socket.d.ts +4 -0
- package/dist_ts/core/models/wrapped-socket.js +18 -1
- package/dist_ts/core/routing/matchers/path.js +3 -2
- package/dist_ts/proxies/smart-proxy/connection-manager.d.ts +4 -7
- package/dist_ts/proxies/smart-proxy/connection-manager.js +22 -22
- package/dist_ts/proxies/smart-proxy/http-proxy-bridge.d.ts +4 -3
- package/dist_ts/proxies/smart-proxy/http-proxy-bridge.js +9 -9
- package/dist_ts/proxies/smart-proxy/metrics-collector.d.ts +68 -56
- package/dist_ts/proxies/smart-proxy/metrics-collector.js +252 -176
- package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +6 -1
- package/dist_ts/proxies/smart-proxy/models/metrics-types.d.ts +99 -47
- package/dist_ts/proxies/smart-proxy/nftables-manager.d.ts +4 -4
- package/dist_ts/proxies/smart-proxy/nftables-manager.js +6 -6
- package/dist_ts/proxies/smart-proxy/port-manager.d.ts +4 -7
- package/dist_ts/proxies/smart-proxy/port-manager.js +6 -9
- package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +4 -15
- package/dist_ts/proxies/smart-proxy/route-connection-handler.js +133 -130
- package/dist_ts/proxies/smart-proxy/security-manager.d.ts +3 -3
- package/dist_ts/proxies/smart-proxy/security-manager.js +9 -9
- package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +20 -13
- package/dist_ts/proxies/smart-proxy/smart-proxy.js +16 -13
- package/dist_ts/proxies/smart-proxy/throughput-tracker.d.ts +36 -0
- package/dist_ts/proxies/smart-proxy/throughput-tracker.js +117 -0
- package/dist_ts/proxies/smart-proxy/timeout-manager.d.ts +5 -4
- package/dist_ts/proxies/smart-proxy/timeout-manager.js +20 -16
- package/dist_ts/proxies/smart-proxy/tls-manager.d.ts +3 -3
- package/dist_ts/proxies/smart-proxy/tls-manager.js +12 -12
- package/dist_ts/routing/router/http-router.js +2 -2
- package/package.json +1 -1
- package/readme.hints.md +0 -0
- package/readme.md +239 -73
- package/readme.plan.md +364 -0
- package/ts/core/models/wrapped-socket.ts +18 -0
- package/ts/core/routing/matchers/path.ts +2 -1
- package/ts/proxies/smart-proxy/connection-manager.ts +23 -21
- package/ts/proxies/smart-proxy/http-proxy-bridge.ts +9 -8
- package/ts/proxies/smart-proxy/metrics-collector.ts +305 -188
- package/ts/proxies/smart-proxy/models/interfaces.ts +8 -1
- package/ts/proxies/smart-proxy/models/metrics-types.ts +99 -41
- package/ts/proxies/smart-proxy/nftables-manager.ts +5 -5
- package/ts/proxies/smart-proxy/port-manager.ts +6 -14
- package/ts/proxies/smart-proxy/route-connection-handler.ts +141 -138
- package/ts/proxies/smart-proxy/security-manager.ts +8 -8
- package/ts/proxies/smart-proxy/smart-proxy.ts +26 -35
- package/ts/proxies/smart-proxy/throughput-tracker.ts +144 -0
- package/ts/proxies/smart-proxy/timeout-manager.ts +22 -16
- package/ts/proxies/smart-proxy/tls-manager.ts +11 -11
- package/ts/routing/router/http-router.ts +1 -1
package/readme.plan.md
ADDED
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
# SmartProxy Metrics Improvement Plan
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
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.
|
|
6
|
+
|
|
7
|
+
## 1. Core Issues with Current Implementation
|
|
8
|
+
|
|
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
|
|
14
|
+
|
|
15
|
+
## 2. Proposed Architecture
|
|
16
|
+
|
|
17
|
+
### A. Time-Series Throughput Tracking
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
interface IThroughputSample {
|
|
21
|
+
timestamp: number;
|
|
22
|
+
bytesIn: number;
|
|
23
|
+
bytesOut: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
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;
|
|
32
|
+
|
|
33
|
+
// Called on every data transfer
|
|
34
|
+
public recordBytes(bytesIn: number, bytesOut: number): void {
|
|
35
|
+
this.accumulatedBytesIn += bytesIn;
|
|
36
|
+
this.accumulatedBytesOut += bytesOut;
|
|
37
|
+
}
|
|
38
|
+
|
|
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);
|
|
57
|
+
}
|
|
58
|
+
|
|
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
|
+
};
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### B. Connection-Level Byte Tracking
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
// In ConnectionRecord, add:
|
|
87
|
+
interface IConnectionRecord {
|
|
88
|
+
// ... existing fields ...
|
|
89
|
+
|
|
90
|
+
// Byte counters with timestamps
|
|
91
|
+
bytesReceivedHistory: Array<{ timestamp: number; bytes: number }>;
|
|
92
|
+
bytesSentHistory: Array<{ timestamp: number; bytes: number }>;
|
|
93
|
+
|
|
94
|
+
// For efficiency, could use circular buffer
|
|
95
|
+
lastBytesReceivedUpdate: number;
|
|
96
|
+
lastBytesSentUpdate: number;
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### C. Enhanced Metrics Interface
|
|
101
|
+
|
|
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
|
+
};
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
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
|
|
206
|
+
|
|
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_
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## 6. Example Usage
|
|
228
|
+
|
|
229
|
+
```typescript
|
|
230
|
+
const proxy = new SmartProxy({
|
|
231
|
+
metrics: {
|
|
232
|
+
enabled: true,
|
|
233
|
+
sampleIntervalMs: 1000,
|
|
234
|
+
enableDetailedTracking: true
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
// Get metrics instance
|
|
239
|
+
const metrics = proxy.getMetrics();
|
|
240
|
+
|
|
241
|
+
// Connection metrics
|
|
242
|
+
console.log(`Active connections: ${metrics.connections.active()}`);
|
|
243
|
+
console.log(`Total connections: ${metrics.connections.total()}`);
|
|
244
|
+
|
|
245
|
+
// Throughput metrics
|
|
246
|
+
const instant = metrics.throughput.instant();
|
|
247
|
+
console.log(`Current: ${instant.in} bytes/sec in, ${instant.out} bytes/sec out`);
|
|
248
|
+
|
|
249
|
+
const recent = metrics.throughput.recent(); // Last 10 seconds
|
|
250
|
+
const average = metrics.throughput.average(); // Last 60 seconds
|
|
251
|
+
|
|
252
|
+
// Custom time window
|
|
253
|
+
const custom = metrics.throughput.custom(30); // Last 30 seconds
|
|
254
|
+
|
|
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
|
+
});
|
|
260
|
+
|
|
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
|
+
});
|
|
266
|
+
|
|
267
|
+
// Request metrics
|
|
268
|
+
console.log(`RPS: ${metrics.requests.perSecond()}`);
|
|
269
|
+
console.log(`RPM: ${metrics.requests.perMinute()}`);
|
|
270
|
+
|
|
271
|
+
// Totals
|
|
272
|
+
console.log(`Total bytes in: ${metrics.totals.bytesIn()}`);
|
|
273
|
+
console.log(`Total bytes out: ${metrics.totals.bytesOut()}`);
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
## 7. Prometheus Export Example
|
|
277
|
+
|
|
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
|
+
```
|
|
304
|
+
|
|
305
|
+
## 8. Migration Strategy
|
|
306
|
+
|
|
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
|
|
312
|
+
|
|
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)
|
|
319
|
+
|
|
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
|
|
325
|
+
|
|
326
|
+
## 9. Success Metrics
|
|
327
|
+
|
|
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
|
|
333
|
+
|
|
334
|
+
## 10. Future Enhancements
|
|
335
|
+
|
|
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
|
|
341
|
+
|
|
342
|
+
### Phase 6: Distributed Metrics
|
|
343
|
+
- Metrics aggregation across multiple proxies
|
|
344
|
+
- Distributed time-series storage
|
|
345
|
+
- Cross-proxy analytics
|
|
346
|
+
- Global dashboard support
|
|
347
|
+
|
|
348
|
+
## 11. Risks and Mitigations
|
|
349
|
+
|
|
350
|
+
### Risk: Memory Usage
|
|
351
|
+
- **Mitigation**: Circular buffers and configurable retention
|
|
352
|
+
- **Monitoring**: Track memory usage per metric type
|
|
353
|
+
|
|
354
|
+
### Risk: Performance Impact
|
|
355
|
+
- **Mitigation**: Efficient data structures and caching
|
|
356
|
+
- **Testing**: Load test with metrics enabled/disabled
|
|
357
|
+
|
|
358
|
+
### Risk: Data Accuracy
|
|
359
|
+
- **Mitigation**: Atomic operations and proper synchronization
|
|
360
|
+
- **Validation**: Compare with external monitoring tools
|
|
361
|
+
|
|
362
|
+
## Conclusion
|
|
363
|
+
|
|
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.
|
|
@@ -52,6 +52,9 @@ export class WrappedSocket {
|
|
|
52
52
|
if (prop === 'setProxyInfo') {
|
|
53
53
|
return target.setProxyInfo.bind(target);
|
|
54
54
|
}
|
|
55
|
+
if (prop === 'remoteFamily') {
|
|
56
|
+
return target.remoteFamily;
|
|
57
|
+
}
|
|
55
58
|
|
|
56
59
|
// For all other properties/methods, delegate to the underlying socket
|
|
57
60
|
const value = target.socket[prop as keyof plugins.net.Socket];
|
|
@@ -89,6 +92,21 @@ export class WrappedSocket {
|
|
|
89
92
|
return !!this.realClientIP;
|
|
90
93
|
}
|
|
91
94
|
|
|
95
|
+
/**
|
|
96
|
+
* Returns the address family of the remote IP
|
|
97
|
+
*/
|
|
98
|
+
get remoteFamily(): string | undefined {
|
|
99
|
+
const ip = this.realClientIP || this.socket.remoteAddress;
|
|
100
|
+
if (!ip) return undefined;
|
|
101
|
+
|
|
102
|
+
// Check if it's IPv6
|
|
103
|
+
if (ip.includes(':')) {
|
|
104
|
+
return 'IPv6';
|
|
105
|
+
}
|
|
106
|
+
// Otherwise assume IPv4
|
|
107
|
+
return 'IPv4';
|
|
108
|
+
}
|
|
109
|
+
|
|
92
110
|
/**
|
|
93
111
|
* Updates the real client information (called after parsing PROXY protocol)
|
|
94
112
|
*/
|
|
@@ -95,7 +95,8 @@ export class PathMatcher implements IMatcher<IPathMatchResult> {
|
|
|
95
95
|
if (normalizedPattern.includes('*') && match.length > paramNames.length + 1) {
|
|
96
96
|
const wildcardCapture = match[match.length - 1];
|
|
97
97
|
if (wildcardCapture) {
|
|
98
|
-
pathRemainder
|
|
98
|
+
// Ensure pathRemainder includes leading slash if it had one
|
|
99
|
+
pathRemainder = wildcardCapture.startsWith('/') ? wildcardCapture : '/' + wildcardCapture;
|
|
99
100
|
pathMatch = normalizedPath.substring(0, normalizedPath.length - wildcardCapture.length);
|
|
100
101
|
}
|
|
101
102
|
}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import * as plugins from '../../plugins.js';
|
|
2
|
-
import type { IConnectionRecord
|
|
3
|
-
import { SecurityManager } from './security-manager.js';
|
|
4
|
-
import { TimeoutManager } from './timeout-manager.js';
|
|
2
|
+
import type { IConnectionRecord } from './models/interfaces.js';
|
|
5
3
|
import { logger } from '../../core/utils/logger.js';
|
|
6
4
|
import { LifecycleComponent } from '../../core/utils/lifecycle-component.js';
|
|
7
5
|
import { cleanupSocket } from '../../core/utils/socket-utils.js';
|
|
8
6
|
import { WrappedSocket } from '../../core/models/wrapped-socket.js';
|
|
7
|
+
import type { SmartProxy } from './smart-proxy.js';
|
|
9
8
|
|
|
10
9
|
/**
|
|
11
10
|
* Manages connection lifecycle, tracking, and cleanup with performance optimizations
|
|
@@ -29,17 +28,15 @@ export class ConnectionManager extends LifecycleComponent {
|
|
|
29
28
|
private cleanupTimer: NodeJS.Timeout | null = null;
|
|
30
29
|
|
|
31
30
|
constructor(
|
|
32
|
-
private
|
|
33
|
-
private securityManager: SecurityManager,
|
|
34
|
-
private timeoutManager: TimeoutManager
|
|
31
|
+
private smartProxy: SmartProxy
|
|
35
32
|
) {
|
|
36
33
|
super();
|
|
37
34
|
|
|
38
35
|
// Set reasonable defaults for connection limits
|
|
39
|
-
this.maxConnections = settings.defaults?.security?.maxConnections || 10000;
|
|
36
|
+
this.maxConnections = smartProxy.settings.defaults?.security?.maxConnections || 10000;
|
|
40
37
|
|
|
41
38
|
// Start inactivity check timer if not disabled
|
|
42
|
-
if (!settings.disableInactivityCheck) {
|
|
39
|
+
if (!smartProxy.settings.disableInactivityCheck) {
|
|
43
40
|
this.startInactivityCheckTimer();
|
|
44
41
|
}
|
|
45
42
|
}
|
|
@@ -108,10 +105,10 @@ export class ConnectionManager extends LifecycleComponent {
|
|
|
108
105
|
*/
|
|
109
106
|
public trackConnection(connectionId: string, record: IConnectionRecord): void {
|
|
110
107
|
this.connectionRecords.set(connectionId, record);
|
|
111
|
-
this.securityManager.trackConnectionByIP(record.remoteIP, connectionId);
|
|
108
|
+
this.smartProxy.securityManager.trackConnectionByIP(record.remoteIP, connectionId);
|
|
112
109
|
|
|
113
110
|
// Schedule inactivity check
|
|
114
|
-
if (!this.settings.disableInactivityCheck) {
|
|
111
|
+
if (!this.smartProxy.settings.disableInactivityCheck) {
|
|
115
112
|
this.scheduleInactivityCheck(connectionId, record);
|
|
116
113
|
}
|
|
117
114
|
}
|
|
@@ -120,14 +117,14 @@ export class ConnectionManager extends LifecycleComponent {
|
|
|
120
117
|
* Schedule next inactivity check for a connection
|
|
121
118
|
*/
|
|
122
119
|
private scheduleInactivityCheck(connectionId: string, record: IConnectionRecord): void {
|
|
123
|
-
let timeout = this.settings.inactivityTimeout!;
|
|
120
|
+
let timeout = this.smartProxy.settings.inactivityTimeout!;
|
|
124
121
|
|
|
125
122
|
if (record.hasKeepAlive) {
|
|
126
|
-
if (this.settings.keepAliveTreatment === 'immortal') {
|
|
123
|
+
if (this.smartProxy.settings.keepAliveTreatment === 'immortal') {
|
|
127
124
|
// Don't schedule check for immortal connections
|
|
128
125
|
return;
|
|
129
|
-
} else if (this.settings.keepAliveTreatment === 'extended') {
|
|
130
|
-
const multiplier = this.settings.keepAliveInactivityMultiplier || 6;
|
|
126
|
+
} else if (this.smartProxy.settings.keepAliveTreatment === 'extended') {
|
|
127
|
+
const multiplier = this.smartProxy.settings.keepAliveInactivityMultiplier || 6;
|
|
131
128
|
timeout = timeout * multiplier;
|
|
132
129
|
}
|
|
133
130
|
}
|
|
@@ -172,7 +169,7 @@ export class ConnectionManager extends LifecycleComponent {
|
|
|
172
169
|
* Initiates cleanup once for a connection
|
|
173
170
|
*/
|
|
174
171
|
public initiateCleanupOnce(record: IConnectionRecord, reason: string = 'normal'): void {
|
|
175
|
-
if (this.settings.enableDetailedLogging) {
|
|
172
|
+
if (this.smartProxy.settings.enableDetailedLogging) {
|
|
176
173
|
logger.log('info', `Connection cleanup initiated`, {
|
|
177
174
|
connectionId: record.id,
|
|
178
175
|
remoteIP: record.remoteIP,
|
|
@@ -253,7 +250,12 @@ export class ConnectionManager extends LifecycleComponent {
|
|
|
253
250
|
this.nextInactivityCheck.delete(record.id);
|
|
254
251
|
|
|
255
252
|
// Track connection termination
|
|
256
|
-
this.securityManager.removeConnectionByIP(record.remoteIP, record.id);
|
|
253
|
+
this.smartProxy.securityManager.removeConnectionByIP(record.remoteIP, record.id);
|
|
254
|
+
|
|
255
|
+
// Remove from metrics tracking
|
|
256
|
+
if (this.smartProxy.metricsCollector) {
|
|
257
|
+
this.smartProxy.metricsCollector.removeConnection(record.id);
|
|
258
|
+
}
|
|
257
259
|
|
|
258
260
|
if (record.cleanupTimer) {
|
|
259
261
|
clearTimeout(record.cleanupTimer);
|
|
@@ -334,7 +336,7 @@ export class ConnectionManager extends LifecycleComponent {
|
|
|
334
336
|
this.connectionRecords.delete(record.id);
|
|
335
337
|
|
|
336
338
|
// Log connection details
|
|
337
|
-
if (this.settings.enableDetailedLogging) {
|
|
339
|
+
if (this.smartProxy.settings.enableDetailedLogging) {
|
|
338
340
|
logger.log('info',
|
|
339
341
|
`Connection terminated: ${record.remoteIP}:${record.localPort} (${reason}) - ` +
|
|
340
342
|
`${plugins.prettyMs(duration)}, IN: ${record.bytesReceived}B, OUT: ${record.bytesSent}B`,
|
|
@@ -414,7 +416,7 @@ export class ConnectionManager extends LifecycleComponent {
|
|
|
414
416
|
*/
|
|
415
417
|
public handleClose(side: 'incoming' | 'outgoing', record: IConnectionRecord) {
|
|
416
418
|
return () => {
|
|
417
|
-
if (this.settings.enableDetailedLogging) {
|
|
419
|
+
if (this.smartProxy.settings.enableDetailedLogging) {
|
|
418
420
|
logger.log('info', `Connection closed on ${side} side`, {
|
|
419
421
|
connectionId: record.id,
|
|
420
422
|
side,
|
|
@@ -553,9 +555,9 @@ export class ConnectionManager extends LifecycleComponent {
|
|
|
553
555
|
const inactivityTime = now - record.lastActivity;
|
|
554
556
|
|
|
555
557
|
// Use extended timeout for extended-treatment keep-alive connections
|
|
556
|
-
let effectiveTimeout = this.settings.inactivityTimeout!;
|
|
557
|
-
if (record.hasKeepAlive && this.settings.keepAliveTreatment === 'extended') {
|
|
558
|
-
const multiplier = this.settings.keepAliveInactivityMultiplier || 6;
|
|
558
|
+
let effectiveTimeout = this.smartProxy.settings.inactivityTimeout!;
|
|
559
|
+
if (record.hasKeepAlive && this.smartProxy.settings.keepAliveTreatment === 'extended') {
|
|
560
|
+
const multiplier = this.smartProxy.settings.keepAliveInactivityMultiplier || 6;
|
|
559
561
|
effectiveTimeout = effectiveTimeout * multiplier;
|
|
560
562
|
}
|
|
561
563
|
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import * as plugins from '../../plugins.js';
|
|
2
2
|
import { HttpProxy } from '../http-proxy/index.js';
|
|
3
3
|
import { setupBidirectionalForwarding } from '../../core/utils/socket-utils.js';
|
|
4
|
-
import type { IConnectionRecord
|
|
4
|
+
import type { IConnectionRecord } from './models/interfaces.js';
|
|
5
5
|
import type { IRouteConfig } from './models/route-types.js';
|
|
6
6
|
import { WrappedSocket } from '../../core/models/wrapped-socket.js';
|
|
7
|
+
import type { SmartProxy } from './smart-proxy.js';
|
|
7
8
|
|
|
8
9
|
export class HttpProxyBridge {
|
|
9
10
|
private httpProxy: HttpProxy | null = null;
|
|
10
11
|
|
|
11
|
-
constructor(private
|
|
12
|
+
constructor(private smartProxy: SmartProxy) {}
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Get the HttpProxy instance
|
|
@@ -21,18 +22,18 @@ export class HttpProxyBridge {
|
|
|
21
22
|
* Initialize HttpProxy instance
|
|
22
23
|
*/
|
|
23
24
|
public async initialize(): Promise<void> {
|
|
24
|
-
if (!this.httpProxy && this.settings.useHttpProxy && this.settings.useHttpProxy.length > 0) {
|
|
25
|
+
if (!this.httpProxy && this.smartProxy.settings.useHttpProxy && this.smartProxy.settings.useHttpProxy.length > 0) {
|
|
25
26
|
const httpProxyOptions: any = {
|
|
26
|
-
port: this.settings.httpProxyPort!,
|
|
27
|
+
port: this.smartProxy.settings.httpProxyPort!,
|
|
27
28
|
portProxyIntegration: true,
|
|
28
|
-
logLevel: this.settings.enableDetailedLogging ? 'debug' : 'info'
|
|
29
|
+
logLevel: this.smartProxy.settings.enableDetailedLogging ? 'debug' : 'info'
|
|
29
30
|
};
|
|
30
31
|
|
|
31
32
|
this.httpProxy = new HttpProxy(httpProxyOptions);
|
|
32
|
-
console.log(`Initialized HttpProxy on port ${this.settings.httpProxyPort}`);
|
|
33
|
+
console.log(`Initialized HttpProxy on port ${this.smartProxy.settings.httpProxyPort}`);
|
|
33
34
|
|
|
34
35
|
// Apply route configurations to HttpProxy
|
|
35
|
-
await this.syncRoutesToHttpProxy(this.settings.routes || []);
|
|
36
|
+
await this.syncRoutesToHttpProxy(this.smartProxy.settings.routes || []);
|
|
36
37
|
}
|
|
37
38
|
}
|
|
38
39
|
|
|
@@ -51,7 +52,7 @@ export class HttpProxyBridge {
|
|
|
51
52
|
: [route.match.ports];
|
|
52
53
|
|
|
53
54
|
return routePorts.some(port =>
|
|
54
|
-
this.settings.useHttpProxy?.includes(port)
|
|
55
|
+
this.smartProxy.settings.useHttpProxy?.includes(port)
|
|
55
56
|
);
|
|
56
57
|
})
|
|
57
58
|
.map(route => this.routeToHttpProxyConfig(route));
|