@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.
Files changed (48) hide show
  1. package/dist_ts/core/models/wrapped-socket.d.ts +4 -0
  2. package/dist_ts/core/models/wrapped-socket.js +18 -1
  3. package/dist_ts/core/routing/matchers/path.js +3 -2
  4. package/dist_ts/proxies/smart-proxy/connection-manager.d.ts +4 -7
  5. package/dist_ts/proxies/smart-proxy/connection-manager.js +22 -22
  6. package/dist_ts/proxies/smart-proxy/http-proxy-bridge.d.ts +4 -3
  7. package/dist_ts/proxies/smart-proxy/http-proxy-bridge.js +9 -9
  8. package/dist_ts/proxies/smart-proxy/metrics-collector.d.ts +68 -56
  9. package/dist_ts/proxies/smart-proxy/metrics-collector.js +252 -176
  10. package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +6 -1
  11. package/dist_ts/proxies/smart-proxy/models/metrics-types.d.ts +99 -47
  12. package/dist_ts/proxies/smart-proxy/nftables-manager.d.ts +4 -4
  13. package/dist_ts/proxies/smart-proxy/nftables-manager.js +6 -6
  14. package/dist_ts/proxies/smart-proxy/port-manager.d.ts +4 -7
  15. package/dist_ts/proxies/smart-proxy/port-manager.js +6 -9
  16. package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +4 -15
  17. package/dist_ts/proxies/smart-proxy/route-connection-handler.js +133 -130
  18. package/dist_ts/proxies/smart-proxy/security-manager.d.ts +3 -3
  19. package/dist_ts/proxies/smart-proxy/security-manager.js +9 -9
  20. package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +20 -13
  21. package/dist_ts/proxies/smart-proxy/smart-proxy.js +16 -13
  22. package/dist_ts/proxies/smart-proxy/throughput-tracker.d.ts +36 -0
  23. package/dist_ts/proxies/smart-proxy/throughput-tracker.js +117 -0
  24. package/dist_ts/proxies/smart-proxy/timeout-manager.d.ts +5 -4
  25. package/dist_ts/proxies/smart-proxy/timeout-manager.js +20 -16
  26. package/dist_ts/proxies/smart-proxy/tls-manager.d.ts +3 -3
  27. package/dist_ts/proxies/smart-proxy/tls-manager.js +12 -12
  28. package/dist_ts/routing/router/http-router.js +2 -2
  29. package/package.json +1 -1
  30. package/readme.hints.md +0 -0
  31. package/readme.md +239 -73
  32. package/readme.plan.md +364 -0
  33. package/ts/core/models/wrapped-socket.ts +18 -0
  34. package/ts/core/routing/matchers/path.ts +2 -1
  35. package/ts/proxies/smart-proxy/connection-manager.ts +23 -21
  36. package/ts/proxies/smart-proxy/http-proxy-bridge.ts +9 -8
  37. package/ts/proxies/smart-proxy/metrics-collector.ts +305 -188
  38. package/ts/proxies/smart-proxy/models/interfaces.ts +8 -1
  39. package/ts/proxies/smart-proxy/models/metrics-types.ts +99 -41
  40. package/ts/proxies/smart-proxy/nftables-manager.ts +5 -5
  41. package/ts/proxies/smart-proxy/port-manager.ts +6 -14
  42. package/ts/proxies/smart-proxy/route-connection-handler.ts +141 -138
  43. package/ts/proxies/smart-proxy/security-manager.ts +8 -8
  44. package/ts/proxies/smart-proxy/smart-proxy.ts +26 -35
  45. package/ts/proxies/smart-proxy/throughput-tracker.ts +144 -0
  46. package/ts/proxies/smart-proxy/timeout-manager.ts +22 -16
  47. package/ts/proxies/smart-proxy/tls-manager.ts +11 -11
  48. package/ts/routing/router/http-router.ts +1 -1
@@ -1,211 +1,283 @@
1
1
  import * as plugins from '../../plugins.js';
2
+ import { ThroughputTracker } from './throughput-tracker.js';
2
3
  import { logger } from '../../core/utils/logger.js';
3
4
  /**
4
- * Collects and computes metrics for SmartProxy on-demand
5
+ * Collects and provides metrics for SmartProxy with clean API
5
6
  */
6
7
  export class MetricsCollector {
7
- constructor(smartProxy) {
8
+ constructor(smartProxy, config) {
8
9
  this.smartProxy = smartProxy;
9
- // RPS tracking (the only state we need to maintain)
10
+ // Request tracking
10
11
  this.requestTimestamps = [];
11
- this.RPS_WINDOW_SIZE = 60000; // 1 minute window
12
- this.MAX_TIMESTAMPS = 5000; // Maximum timestamps to keep
13
- // Optional caching for performance
14
- this.cachedMetrics = { timestamp: 0 };
15
- this.CACHE_TTL = 1000; // 1 second cache
16
- // Subscription will be set up in start() method
17
- }
18
- /**
19
- * Get the current number of active connections
20
- */
21
- getActiveConnections() {
22
- return this.smartProxy.connectionManager.getConnectionCount();
23
- }
24
- /**
25
- * Get connection counts grouped by route name
26
- */
27
- getConnectionsByRoute() {
28
- const now = Date.now();
29
- // Return cached value if fresh
30
- if (this.cachedMetrics.connectionsByRoute &&
31
- now - this.cachedMetrics.timestamp < this.CACHE_TTL) {
32
- return new Map(this.cachedMetrics.connectionsByRoute);
33
- }
34
- // Compute fresh value
35
- const routeCounts = new Map();
36
- const connections = this.smartProxy.connectionManager.getConnections();
37
- if (this.smartProxy.settings?.enableDetailedLogging) {
38
- logger.log('debug', `MetricsCollector: Computing route connections`, {
39
- totalConnections: connections.size,
40
- component: 'metrics'
41
- });
42
- }
43
- for (const [_, record] of connections) {
44
- // Try different ways to get the route name
45
- const routeName = record.routeName ||
46
- record.routeConfig?.name ||
47
- record.routeConfig?.routeName ||
48
- 'unknown';
49
- if (this.smartProxy.settings?.enableDetailedLogging) {
50
- logger.log('debug', `MetricsCollector: Connection route info`, {
51
- connectionId: record.id,
52
- routeName,
53
- hasRouteConfig: !!record.routeConfig,
54
- routeConfigName: record.routeConfig?.name,
55
- routeConfigKeys: record.routeConfig ? Object.keys(record.routeConfig) : [],
56
- component: 'metrics'
57
- });
12
+ this.totalRequests = 0;
13
+ // Connection byte tracking for per-route/IP metrics
14
+ this.connectionByteTrackers = new Map();
15
+ // Connection metrics implementation
16
+ this.connections = {
17
+ active: () => {
18
+ return this.smartProxy.connectionManager.getConnectionCount();
19
+ },
20
+ total: () => {
21
+ const stats = this.smartProxy.connectionManager.getTerminationStats();
22
+ let total = this.smartProxy.connectionManager.getConnectionCount();
23
+ for (const reason in stats.incoming) {
24
+ total += stats.incoming[reason];
25
+ }
26
+ return total;
27
+ },
28
+ byRoute: () => {
29
+ const routeCounts = new Map();
30
+ const connections = this.smartProxy.connectionManager.getConnections();
31
+ for (const [_, record] of connections) {
32
+ const routeName = record.routeName ||
33
+ record.routeConfig?.name ||
34
+ 'unknown';
35
+ const current = routeCounts.get(routeName) || 0;
36
+ routeCounts.set(routeName, current + 1);
37
+ }
38
+ return routeCounts;
39
+ },
40
+ byIP: () => {
41
+ const ipCounts = new Map();
42
+ for (const [_, record] of this.smartProxy.connectionManager.getConnections()) {
43
+ const ip = record.remoteIP;
44
+ const current = ipCounts.get(ip) || 0;
45
+ ipCounts.set(ip, current + 1);
46
+ }
47
+ return ipCounts;
48
+ },
49
+ topIPs: (limit = 10) => {
50
+ const ipCounts = this.connections.byIP();
51
+ return Array.from(ipCounts.entries())
52
+ .sort((a, b) => b[1] - a[1])
53
+ .slice(0, limit)
54
+ .map(([ip, count]) => ({ ip, count }));
58
55
  }
59
- const current = routeCounts.get(routeName) || 0;
60
- routeCounts.set(routeName, current + 1);
61
- }
62
- // Cache and return
63
- this.cachedMetrics.connectionsByRoute = routeCounts;
64
- this.cachedMetrics.timestamp = now;
65
- return new Map(routeCounts);
66
- }
67
- /**
68
- * Get connection counts grouped by IP address
69
- */
70
- getConnectionsByIP() {
71
- const now = Date.now();
72
- // Return cached value if fresh
73
- if (this.cachedMetrics.connectionsByIP &&
74
- now - this.cachedMetrics.timestamp < this.CACHE_TTL) {
75
- return new Map(this.cachedMetrics.connectionsByIP);
76
- }
77
- // Compute fresh value
78
- const ipCounts = new Map();
79
- for (const [_, record] of this.smartProxy.connectionManager.getConnections()) {
80
- const ip = record.remoteIP;
81
- const current = ipCounts.get(ip) || 0;
82
- ipCounts.set(ip, current + 1);
83
- }
84
- // Cache and return
85
- this.cachedMetrics.connectionsByIP = ipCounts;
86
- this.cachedMetrics.timestamp = now;
87
- return new Map(ipCounts);
88
- }
89
- /**
90
- * Get the total number of connections since proxy start
91
- */
92
- getTotalConnections() {
93
- // Get from termination stats
94
- const stats = this.smartProxy.connectionManager.getTerminationStats();
95
- let total = this.smartProxy.connectionManager.getConnectionCount(); // Add active connections
96
- // Add all terminated connections
97
- for (const reason in stats.incoming) {
98
- total += stats.incoming[reason];
99
- }
100
- return total;
101
- }
102
- /**
103
- * Get the current requests per second rate
104
- */
105
- getRequestsPerSecond() {
106
- const now = Date.now();
107
- const windowStart = now - this.RPS_WINDOW_SIZE;
108
- // Clean old timestamps
109
- this.requestTimestamps = this.requestTimestamps.filter(ts => ts > windowStart);
110
- // Calculate RPS based on window
111
- const requestsInWindow = this.requestTimestamps.length;
112
- return requestsInWindow / (this.RPS_WINDOW_SIZE / 1000);
56
+ };
57
+ // Throughput metrics implementation
58
+ this.throughput = {
59
+ instant: () => {
60
+ return this.throughputTracker.getRate(1);
61
+ },
62
+ recent: () => {
63
+ return this.throughputTracker.getRate(10);
64
+ },
65
+ average: () => {
66
+ return this.throughputTracker.getRate(60);
67
+ },
68
+ custom: (seconds) => {
69
+ return this.throughputTracker.getRate(seconds);
70
+ },
71
+ history: (seconds) => {
72
+ return this.throughputTracker.getHistory(seconds);
73
+ },
74
+ byRoute: (windowSeconds = 60) => {
75
+ const routeThroughput = new Map();
76
+ const now = Date.now();
77
+ const windowStart = now - (windowSeconds * 1000);
78
+ // Aggregate bytes by route with proper time calculation
79
+ const routeData = new Map();
80
+ for (const [_, tracker] of this.connectionByteTrackers) {
81
+ // Only include connections that were active within the window
82
+ if (tracker.lastUpdate > windowStart || tracker.startTime > windowStart) {
83
+ // Calculate the actual duration this connection was active within the window
84
+ const connectionStart = Math.max(tracker.startTime, windowStart);
85
+ const connectionEnd = tracker.lastUpdate;
86
+ const durationInWindow = (connectionEnd - connectionStart) / 1000; // Convert to seconds
87
+ if (durationInWindow > 0) {
88
+ const current = routeData.get(tracker.routeName) || { bytesIn: 0, bytesOut: 0, totalDuration: 0 };
89
+ current.bytesIn += tracker.bytesIn;
90
+ current.bytesOut += tracker.bytesOut;
91
+ current.totalDuration += durationInWindow;
92
+ routeData.set(tracker.routeName, current);
93
+ }
94
+ }
95
+ }
96
+ // Convert to rates (bytes per second)
97
+ for (const [route, data] of routeData) {
98
+ if (data.totalDuration > 0) {
99
+ routeThroughput.set(route, {
100
+ in: Math.round(data.bytesIn / data.totalDuration),
101
+ out: Math.round(data.bytesOut / data.totalDuration)
102
+ });
103
+ }
104
+ }
105
+ return routeThroughput;
106
+ },
107
+ byIP: (windowSeconds = 60) => {
108
+ const ipThroughput = new Map();
109
+ const now = Date.now();
110
+ const windowStart = now - (windowSeconds * 1000);
111
+ // Aggregate bytes by IP with proper time calculation
112
+ const ipData = new Map();
113
+ for (const [_, tracker] of this.connectionByteTrackers) {
114
+ // Only include connections that were active within the window
115
+ if (tracker.lastUpdate > windowStart || tracker.startTime > windowStart) {
116
+ // Calculate the actual duration this connection was active within the window
117
+ const connectionStart = Math.max(tracker.startTime, windowStart);
118
+ const connectionEnd = tracker.lastUpdate;
119
+ const durationInWindow = (connectionEnd - connectionStart) / 1000; // Convert to seconds
120
+ if (durationInWindow > 0) {
121
+ const current = ipData.get(tracker.remoteIP) || { bytesIn: 0, bytesOut: 0, totalDuration: 0 };
122
+ current.bytesIn += tracker.bytesIn;
123
+ current.bytesOut += tracker.bytesOut;
124
+ current.totalDuration += durationInWindow;
125
+ ipData.set(tracker.remoteIP, current);
126
+ }
127
+ }
128
+ }
129
+ // Convert to rates (bytes per second)
130
+ for (const [ip, data] of ipData) {
131
+ if (data.totalDuration > 0) {
132
+ ipThroughput.set(ip, {
133
+ in: Math.round(data.bytesIn / data.totalDuration),
134
+ out: Math.round(data.bytesOut / data.totalDuration)
135
+ });
136
+ }
137
+ }
138
+ return ipThroughput;
139
+ }
140
+ };
141
+ // Request metrics implementation
142
+ this.requests = {
143
+ perSecond: () => {
144
+ const now = Date.now();
145
+ const oneSecondAgo = now - 1000;
146
+ // Clean old timestamps
147
+ this.requestTimestamps = this.requestTimestamps.filter(ts => ts > now - 60000);
148
+ // Count requests in last second
149
+ const recentRequests = this.requestTimestamps.filter(ts => ts > oneSecondAgo);
150
+ return recentRequests.length;
151
+ },
152
+ perMinute: () => {
153
+ const now = Date.now();
154
+ const oneMinuteAgo = now - 60000;
155
+ // Count requests in last minute
156
+ const recentRequests = this.requestTimestamps.filter(ts => ts > oneMinuteAgo);
157
+ return recentRequests.length;
158
+ },
159
+ total: () => {
160
+ return this.totalRequests;
161
+ }
162
+ };
163
+ // Totals implementation
164
+ this.totals = {
165
+ bytesIn: () => {
166
+ let total = 0;
167
+ // Sum from all active connections
168
+ for (const [_, record] of this.smartProxy.connectionManager.getConnections()) {
169
+ total += record.bytesReceived;
170
+ }
171
+ // TODO: Add historical data from terminated connections
172
+ return total;
173
+ },
174
+ bytesOut: () => {
175
+ let total = 0;
176
+ // Sum from all active connections
177
+ for (const [_, record] of this.smartProxy.connectionManager.getConnections()) {
178
+ total += record.bytesSent;
179
+ }
180
+ // TODO: Add historical data from terminated connections
181
+ return total;
182
+ },
183
+ connections: () => {
184
+ return this.connections.total();
185
+ }
186
+ };
187
+ // Percentiles implementation (placeholder for now)
188
+ this.percentiles = {
189
+ connectionDuration: () => {
190
+ // TODO: Implement percentile calculations
191
+ return { p50: 0, p95: 0, p99: 0 };
192
+ },
193
+ bytesTransferred: () => {
194
+ // TODO: Implement percentile calculations
195
+ return {
196
+ in: { p50: 0, p95: 0, p99: 0 },
197
+ out: { p50: 0, p95: 0, p99: 0 }
198
+ };
199
+ }
200
+ };
201
+ this.sampleIntervalMs = config?.sampleIntervalMs || 1000;
202
+ this.retentionSeconds = config?.retentionSeconds || 3600;
203
+ this.throughputTracker = new ThroughputTracker(this.retentionSeconds);
113
204
  }
114
205
  /**
115
- * Record a new request for RPS tracking
206
+ * Record a new request
116
207
  */
117
- recordRequest() {
208
+ recordRequest(connectionId, routeName, remoteIP) {
118
209
  const now = Date.now();
119
210
  this.requestTimestamps.push(now);
120
- // Prevent unbounded growth - clean up more aggressively
121
- if (this.requestTimestamps.length > this.MAX_TIMESTAMPS) {
122
- // Keep only timestamps within the window
123
- const cutoff = now - this.RPS_WINDOW_SIZE;
211
+ this.totalRequests++;
212
+ // Initialize byte tracker for this connection
213
+ this.connectionByteTrackers.set(connectionId, {
214
+ connectionId,
215
+ routeName,
216
+ remoteIP,
217
+ bytesIn: 0,
218
+ bytesOut: 0,
219
+ startTime: now,
220
+ lastUpdate: now
221
+ });
222
+ // Cleanup old request timestamps
223
+ if (this.requestTimestamps.length > 5000) {
224
+ // First try to clean up old timestamps (older than 1 minute)
225
+ const cutoff = now - 60000;
124
226
  this.requestTimestamps = this.requestTimestamps.filter(ts => ts > cutoff);
125
- }
126
- }
127
- /**
128
- * Get total throughput (bytes transferred)
129
- */
130
- getThroughput() {
131
- let bytesIn = 0;
132
- let bytesOut = 0;
133
- // Sum bytes from all active connections
134
- for (const [_, record] of this.smartProxy.connectionManager.getConnections()) {
135
- bytesIn += record.bytesReceived;
136
- bytesOut += record.bytesSent;
137
- }
138
- return { bytesIn, bytesOut };
139
- }
140
- /**
141
- * Get throughput rate (bytes per second) for last minute
142
- */
143
- getThroughputRate() {
144
- const now = Date.now();
145
- let recentBytesIn = 0;
146
- let recentBytesOut = 0;
147
- // Calculate bytes transferred in last minute from active connections
148
- for (const [_, record] of this.smartProxy.connectionManager.getConnections()) {
149
- const connectionAge = now - record.incomingStartTime;
150
- if (connectionAge < 60000) { // Connection started within last minute
151
- recentBytesIn += record.bytesReceived;
152
- recentBytesOut += record.bytesSent;
153
- }
154
- else {
155
- // For older connections, estimate rate based on average
156
- const rate = connectionAge / 60000;
157
- recentBytesIn += record.bytesReceived / rate;
158
- recentBytesOut += record.bytesSent / rate;
227
+ // If still too many, enforce hard cap of 5000 most recent
228
+ if (this.requestTimestamps.length > 5000) {
229
+ this.requestTimestamps = this.requestTimestamps.slice(-5000);
159
230
  }
160
231
  }
161
- return {
162
- bytesInPerSec: Math.round(recentBytesIn / 60),
163
- bytesOutPerSec: Math.round(recentBytesOut / 60)
164
- };
165
- }
166
- /**
167
- * Get top IPs by connection count
168
- */
169
- getTopIPs(limit = 10) {
170
- const ipCounts = this.getConnectionsByIP();
171
- const sorted = Array.from(ipCounts.entries())
172
- .sort((a, b) => b[1] - a[1])
173
- .slice(0, limit)
174
- .map(([ip, connections]) => ({ ip, connections }));
175
- return sorted;
176
232
  }
177
233
  /**
178
- * Check if an IP has reached the connection limit
234
+ * Record bytes transferred for a connection
179
235
  */
180
- isIPBlocked(ip, maxConnectionsPerIP) {
181
- const ipCounts = this.getConnectionsByIP();
182
- const currentConnections = ipCounts.get(ip) || 0;
183
- return currentConnections >= maxConnectionsPerIP;
236
+ recordBytes(connectionId, bytesIn, bytesOut) {
237
+ // Update global throughput tracker
238
+ this.throughputTracker.recordBytes(bytesIn, bytesOut);
239
+ // Update connection-specific tracker
240
+ const tracker = this.connectionByteTrackers.get(connectionId);
241
+ if (tracker) {
242
+ tracker.bytesIn += bytesIn;
243
+ tracker.bytesOut += bytesOut;
244
+ tracker.lastUpdate = Date.now();
245
+ }
184
246
  }
185
247
  /**
186
- * Clean up old request timestamps
248
+ * Clean up tracking for a closed connection
187
249
  */
188
- cleanupOldRequests() {
189
- const cutoff = Date.now() - this.RPS_WINDOW_SIZE;
190
- this.requestTimestamps = this.requestTimestamps.filter(ts => ts > cutoff);
250
+ removeConnection(connectionId) {
251
+ this.connectionByteTrackers.delete(connectionId);
191
252
  }
192
253
  /**
193
- * Start the metrics collector and set up subscriptions
254
+ * Start the metrics collector
194
255
  */
195
256
  start() {
196
257
  if (!this.smartProxy.routeConnectionHandler) {
197
258
  throw new Error('MetricsCollector: RouteConnectionHandler not available');
198
259
  }
199
- // Subscribe to the newConnectionSubject from RouteConnectionHandler
260
+ // Start periodic sampling
261
+ this.samplingInterval = setInterval(() => {
262
+ this.throughputTracker.takeSample();
263
+ // Clean up old connection trackers (connections closed more than 5 minutes ago)
264
+ const cutoff = Date.now() - 300000;
265
+ for (const [id, tracker] of this.connectionByteTrackers) {
266
+ if (tracker.lastUpdate < cutoff) {
267
+ this.connectionByteTrackers.delete(id);
268
+ }
269
+ }
270
+ }, this.sampleIntervalMs);
271
+ // Subscribe to new connections
200
272
  this.connectionSubscription = this.smartProxy.routeConnectionHandler.newConnectionSubject.subscribe({
201
273
  next: (record) => {
202
- this.recordRequest();
203
- // Optional: Log connection details
274
+ const routeName = record.routeConfig?.name || 'unknown';
275
+ this.recordRequest(record.id, routeName, record.remoteIP);
204
276
  if (this.smartProxy.settings?.enableDetailedLogging) {
205
277
  logger.log('debug', `MetricsCollector: New connection recorded`, {
206
278
  connectionId: record.id,
207
279
  remoteIP: record.remoteIP,
208
- routeName: record.routeConfig?.name || 'unknown',
280
+ routeName,
209
281
  component: 'metrics'
210
282
  });
211
283
  }
@@ -220,9 +292,13 @@ export class MetricsCollector {
220
292
  logger.log('debug', 'MetricsCollector started', { component: 'metrics' });
221
293
  }
222
294
  /**
223
- * Stop the metrics collector and clean up resources
295
+ * Stop the metrics collector
224
296
  */
225
297
  stop() {
298
+ if (this.samplingInterval) {
299
+ clearInterval(this.samplingInterval);
300
+ this.samplingInterval = undefined;
301
+ }
226
302
  if (this.connectionSubscription) {
227
303
  this.connectionSubscription.unsubscribe();
228
304
  this.connectionSubscription = undefined;
@@ -230,10 +306,10 @@ export class MetricsCollector {
230
306
  logger.log('debug', 'MetricsCollector stopped', { component: 'metrics' });
231
307
  }
232
308
  /**
233
- * Alias for stop() for backward compatibility
309
+ * Alias for stop() for compatibility
234
310
  */
235
311
  destroy() {
236
312
  this.stop();
237
313
  }
238
314
  }
239
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWV0cmljcy1jb2xsZWN0b3IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9wcm94aWVzL3NtYXJ0LXByb3h5L21ldHJpY3MtY29sbGVjdG9yLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sa0JBQWtCLENBQUM7QUFHNUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBRXBEOztHQUVHO0FBQ0gsTUFBTSxPQUFPLGdCQUFnQjtJQWtCM0IsWUFDVSxVQUFzQjtRQUF0QixlQUFVLEdBQVYsVUFBVSxDQUFZO1FBbEJoQyxvREFBb0Q7UUFDNUMsc0JBQWlCLEdBQWEsRUFBRSxDQUFDO1FBQ3hCLG9CQUFlLEdBQUcsS0FBSyxDQUFDLENBQUMsa0JBQWtCO1FBQzNDLG1CQUFjLEdBQUcsSUFBSSxDQUFDLENBQUMsNkJBQTZCO1FBRXJFLG1DQUFtQztRQUMzQixrQkFBYSxHQUlqQixFQUFFLFNBQVMsRUFBRSxDQUFDLEVBQUUsQ0FBQztRQUVKLGNBQVMsR0FBRyxJQUFJLENBQUMsQ0FBQyxpQkFBaUI7UUFRbEQsZ0RBQWdEO0lBQ2xELENBQUM7SUFFRDs7T0FFRztJQUNJLG9CQUFvQjtRQUN6QixPQUFPLElBQUksQ0FBQyxVQUFVLENBQUMsaUJBQWlCLENBQUMsa0JBQWtCLEVBQUUsQ0FBQztJQUNoRSxDQUFDO0lBRUQ7O09BRUc7SUFDSSxxQkFBcUI7UUFDMUIsTUFBTSxHQUFHLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO1FBRXZCLCtCQUErQjtRQUMvQixJQUFJLElBQUksQ0FBQyxhQUFhLENBQUMsa0JBQWtCO1lBQ3JDLEdBQUcsR0FBRyxJQUFJLENBQUMsYUFBYSxDQUFDLFNBQVMsR0FBRyxJQUFJLENBQUMsU0FBUyxFQUFFLENBQUM7WUFDeEQsT0FBTyxJQUFJLEdBQUcsQ0FBQyxJQUFJLENBQUMsYUFBYSxDQUFDLGtCQUFrQixDQUFDLENBQUM7UUFD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
315
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWV0cmljcy1jb2xsZWN0b3IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9wcm94aWVzL3NtYXJ0LXByb3h5L21ldHJpY3MtY29sbGVjdG9yLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sa0JBQWtCLENBQUM7QUFRNUMsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDNUQsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBRXBEOztHQUVHO0FBQ0gsTUFBTSxPQUFPLGdCQUFnQjtJQW1CM0IsWUFDVSxVQUFzQixFQUM5QixNQUdDO1FBSk8sZUFBVSxHQUFWLFVBQVUsQ0FBWTtRQWhCaEMsbUJBQW1CO1FBQ1gsc0JBQWlCLEdBQWEsRUFBRSxDQUFDO1FBQ2pDLGtCQUFhLEdBQVcsQ0FBQyxDQUFDO1FBRWxDLG9EQUFvRDtRQUM1QywyQkFBc0IsR0FBRyxJQUFJLEdBQUcsRUFBd0IsQ0FBQztRQXNCakUsb0NBQW9DO1FBQzdCLGdCQUFXLEdBQUc7WUFDbkIsTUFBTSxFQUFFLEdBQVcsRUFBRTtnQkFDbkIsT0FBTyxJQUFJLENBQUMsVUFBVSxDQUFDLGlCQUFpQixDQUFDLGtCQUFrQixFQUFFLENBQUM7WUFDaEUsQ0FBQztZQUVELEtBQUssRUFBRSxHQUFXLEVBQUU7Z0JBQ2xCLE1BQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxVQUFVLENBQUMsaUJBQWlCLENBQUMsbUJBQW1CLEVBQUUsQ0FBQztnQkFDdEUsSUFBSSxLQUFLLEdBQUcsSUFBSSxDQUFDLFVBQVUsQ0FBQyxpQkFBaUIsQ0FBQyxrQkFBa0IsRUFBRSxDQUFDO2dCQUVuRSxLQUFLLE1BQU0sTUFBTSxJQUFJLEtBQUssQ0FBQyxRQUFRLEVBQUUsQ0FBQztvQkFDcEMsS0FBSyxJQUFJLEtBQUssQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDLENBQUM7Z0JBQ2xDLENBQUM7Z0JBRUQs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
@@ -76,6 +76,11 @@ export interface ISmartProxyOptions {
76
76
  extendedKeepAliveLifetime?: number;
77
77
  useHttpProxy?: number[];
78
78
  httpProxyPort?: number;
79
+ metrics?: {
80
+ enabled?: boolean;
81
+ sampleIntervalMs?: number;
82
+ retentionSeconds?: number;
83
+ };
79
84
  /**
80
85
  * Global ACME configuration options for SmartProxy
81
86
  *
@@ -111,7 +116,7 @@ export interface IConnectionRecord {
111
116
  outgoingClosedTime?: number;
112
117
  lockedDomain?: string;
113
118
  connectionClosed: boolean;
114
- cleanupTimer?: NodeJS.Timeout;
119
+ cleanupTimer?: NodeJS.Timeout | null;
115
120
  alertFallbackTimeout?: NodeJS.Timeout;
116
121
  lastActivity: number;
117
122
  pendingData: Buffer[];