@push.rocks/smartproxy 19.5.26 → 19.6.1

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 (30) hide show
  1. package/dist_ts/plugins.d.ts +2 -1
  2. package/dist_ts/plugins.js +3 -2
  3. package/dist_ts/proxies/http-proxy/function-cache.d.ts +5 -0
  4. package/dist_ts/proxies/http-proxy/function-cache.js +19 -2
  5. package/dist_ts/proxies/http-proxy/http-proxy.js +5 -1
  6. package/dist_ts/proxies/http-proxy/request-handler.d.ts +5 -0
  7. package/dist_ts/proxies/http-proxy/request-handler.js +27 -2
  8. package/dist_ts/proxies/smart-proxy/metrics-collector.d.ts +80 -0
  9. package/dist_ts/proxies/smart-proxy/metrics-collector.js +239 -0
  10. package/dist_ts/proxies/smart-proxy/models/index.d.ts +1 -0
  11. package/dist_ts/proxies/smart-proxy/models/index.js +2 -1
  12. package/dist_ts/proxies/smart-proxy/models/metrics-types.d.ts +55 -0
  13. package/dist_ts/proxies/smart-proxy/models/metrics-types.js +2 -0
  14. package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +2 -2
  15. package/dist_ts/proxies/smart-proxy/route-connection-handler.js +14 -6
  16. package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +12 -2
  17. package/dist_ts/proxies/smart-proxy/smart-proxy.js +17 -1
  18. package/package.json +18 -8
  19. package/readme.md +118 -0
  20. package/readme.memory-leaks-fixed.md +45 -0
  21. package/readme.metrics.md +591 -0
  22. package/ts/plugins.ts +2 -0
  23. package/ts/proxies/http-proxy/function-cache.ts +21 -1
  24. package/ts/proxies/http-proxy/http-proxy.ts +5 -0
  25. package/ts/proxies/http-proxy/request-handler.ts +32 -1
  26. package/ts/proxies/smart-proxy/metrics-collector.ts +289 -0
  27. package/ts/proxies/smart-proxy/models/index.ts +1 -0
  28. package/ts/proxies/smart-proxy/models/metrics-types.ts +54 -0
  29. package/ts/proxies/smart-proxy/route-connection-handler.ts +18 -5
  30. package/ts/proxies/smart-proxy/smart-proxy.ts +27 -2
@@ -0,0 +1,239 @@
1
+ import * as plugins from '../../plugins.js';
2
+ import { logger } from '../../core/utils/logger.js';
3
+ /**
4
+ * Collects and computes metrics for SmartProxy on-demand
5
+ */
6
+ export class MetricsCollector {
7
+ constructor(smartProxy) {
8
+ this.smartProxy = smartProxy;
9
+ // RPS tracking (the only state we need to maintain)
10
+ 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
+ });
58
+ }
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);
113
+ }
114
+ /**
115
+ * Record a new request for RPS tracking
116
+ */
117
+ recordRequest() {
118
+ const now = Date.now();
119
+ 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;
124
+ 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;
159
+ }
160
+ }
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
+ }
177
+ /**
178
+ * Check if an IP has reached the connection limit
179
+ */
180
+ isIPBlocked(ip, maxConnectionsPerIP) {
181
+ const ipCounts = this.getConnectionsByIP();
182
+ const currentConnections = ipCounts.get(ip) || 0;
183
+ return currentConnections >= maxConnectionsPerIP;
184
+ }
185
+ /**
186
+ * Clean up old request timestamps
187
+ */
188
+ cleanupOldRequests() {
189
+ const cutoff = Date.now() - this.RPS_WINDOW_SIZE;
190
+ this.requestTimestamps = this.requestTimestamps.filter(ts => ts > cutoff);
191
+ }
192
+ /**
193
+ * Start the metrics collector and set up subscriptions
194
+ */
195
+ start() {
196
+ if (!this.smartProxy.routeConnectionHandler) {
197
+ throw new Error('MetricsCollector: RouteConnectionHandler not available');
198
+ }
199
+ // Subscribe to the newConnectionSubject from RouteConnectionHandler
200
+ this.connectionSubscription = this.smartProxy.routeConnectionHandler.newConnectionSubject.subscribe({
201
+ next: (record) => {
202
+ this.recordRequest();
203
+ // Optional: Log connection details
204
+ if (this.smartProxy.settings?.enableDetailedLogging) {
205
+ logger.log('debug', `MetricsCollector: New connection recorded`, {
206
+ connectionId: record.id,
207
+ remoteIP: record.remoteIP,
208
+ routeName: record.routeConfig?.name || 'unknown',
209
+ component: 'metrics'
210
+ });
211
+ }
212
+ },
213
+ error: (err) => {
214
+ logger.log('error', `MetricsCollector: Error in connection subscription`, {
215
+ error: err.message,
216
+ component: 'metrics'
217
+ });
218
+ }
219
+ });
220
+ logger.log('debug', 'MetricsCollector started', { component: 'metrics' });
221
+ }
222
+ /**
223
+ * Stop the metrics collector and clean up resources
224
+ */
225
+ stop() {
226
+ if (this.connectionSubscription) {
227
+ this.connectionSubscription.unsubscribe();
228
+ this.connectionSubscription = undefined;
229
+ }
230
+ logger.log('debug', 'MetricsCollector stopped', { component: 'metrics' });
231
+ }
232
+ /**
233
+ * Alias for stop() for backward compatibility
234
+ */
235
+ destroy() {
236
+ this.stop();
237
+ }
238
+ }
239
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWV0cmljcy1jb2xsZWN0b3IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9wcm94aWVzL3NtYXJ0LXByb3h5L21ldHJpY3MtY29sbGVjdG9yLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sa0JBQWtCLENBQUM7QUFHNUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBRXBEOztHQUVHO0FBQ0gsTUFBTSxPQUFPLGdCQUFnQjtJQWtCM0IsWUFDVSxVQUFzQjtRQUF0QixlQUFVLEdBQVYsVUFBVSxDQUFZO1FBbEJoQyxvREFBb0Q7UUFDNUMsc0JBQWlCLEdBQWEsRUFBRSxDQUFDO1FBQ3hCLG9CQUFlLEdBQUcsS0FBSyxDQUFDLENBQUMsa0JBQWtCO1FBQzNDLG1CQUFjLEdBQUcsSUFBSSxDQUFDLENBQUMsNkJBQTZCO1FBRXJFLG1DQUFtQztRQUMzQixrQkFBYSxHQUlqQixFQUFFLFNBQVMsRUFBRSxDQUFDLEVBQUUsQ0FBQztRQUVKLGNBQVMsR0FBRyxJQUFJLENBQUMsQ0FBQyxpQkFBaUI7UUFRbEQsZ0RBQWdEO0lBQ2xELENBQUM7SUFFRDs7T0FFRztJQUNJLG9CQUFvQjtRQUN6QixPQUFPLElBQUksQ0FBQyxVQUFVLENBQUMsaUJBQWlCLENBQUMsa0JBQWtCLEVBQUUsQ0FBQztJQUNoRSxDQUFDO0lBRUQ7O09BRUc7SUFDSSxxQkFBcUI7UUFDMUIsTUFBTSxHQUFHLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO1FBRXZCLCtCQUErQjtRQUMvQixJQUFJLElBQUksQ0FBQyxhQUFhLENBQUMsa0JBQWtCO1lBQ3JDLEdBQUcsR0FBRyxJQUFJLENBQUMsYUFBYSxDQUFDLFNBQVMsR0FBRyxJQUFJLENBQUMsU0FBUyxFQUFFLENBQUM7WUFDeEQsT0FBTyxJQUFJLEdBQUcsQ0FBQyxJQUFJLENBQUMsYUFBYSxDQUFDLGtCQUFrQixDQUFDLENBQUM7UUFD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
@@ -3,3 +3,4 @@
3
3
  */
4
4
  export type { ISmartProxyOptions, IConnectionRecord, TSmartProxyCertProvisionObject } from './interfaces.js';
5
5
  export * from './route-types.js';
6
+ export * from './metrics-types.js';
@@ -1,2 +1,3 @@
1
1
  export * from './route-types.js';
2
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi90cy9wcm94aWVzL3NtYXJ0LXByb3h5L21vZGVscy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFLQSxjQUFjLGtCQUFrQixDQUFDIn0=
2
+ export * from './metrics-types.js';
3
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi90cy9wcm94aWVzL3NtYXJ0LXByb3h5L21vZGVscy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFLQSxjQUFjLGtCQUFrQixDQUFDO0FBQ2pDLGNBQWMsb0JBQW9CLENBQUMifQ==
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Interface for proxy statistics and metrics
3
+ */
4
+ export interface IProxyStats {
5
+ /**
6
+ * Get the current number of active connections
7
+ */
8
+ getActiveConnections(): number;
9
+ /**
10
+ * Get connection counts grouped by route name
11
+ */
12
+ getConnectionsByRoute(): Map<string, number>;
13
+ /**
14
+ * Get connection counts grouped by IP address
15
+ */
16
+ getConnectionsByIP(): Map<string, number>;
17
+ /**
18
+ * Get the total number of connections since proxy start
19
+ */
20
+ getTotalConnections(): number;
21
+ /**
22
+ * Get the current requests per second rate
23
+ */
24
+ getRequestsPerSecond(): number;
25
+ /**
26
+ * Get total throughput (bytes transferred)
27
+ */
28
+ getThroughput(): {
29
+ bytesIn: number;
30
+ bytesOut: number;
31
+ };
32
+ }
33
+ /**
34
+ * Extended interface for additional metrics helpers
35
+ */
36
+ export interface IProxyStatsExtended extends IProxyStats {
37
+ /**
38
+ * Get throughput rate (bytes per second) for last minute
39
+ */
40
+ getThroughputRate(): {
41
+ bytesInPerSec: number;
42
+ bytesOutPerSec: number;
43
+ };
44
+ /**
45
+ * Get top IPs by connection count
46
+ */
47
+ getTopIPs(limit?: number): Array<{
48
+ ip: string;
49
+ connections: number;
50
+ }>;
51
+ /**
52
+ * Check if an IP has reached the connection limit
53
+ */
54
+ isIPBlocked(ip: string, maxConnectionsPerIP: number): boolean;
55
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWV0cmljcy10eXBlcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3RzL3Byb3hpZXMvc21hcnQtcHJveHkvbW9kZWxzL21ldHJpY3MtdHlwZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiJ9
@@ -1,5 +1,5 @@
1
1
  import * as plugins from '../../plugins.js';
2
- import type { ISmartProxyOptions } from './models/interfaces.js';
2
+ import type { IConnectionRecord, ISmartProxyOptions } from './models/interfaces.js';
3
3
  import { ConnectionManager } from './connection-manager.js';
4
4
  import { SecurityManager } from './security-manager.js';
5
5
  import { TlsManager } from './tls-manager.js';
@@ -17,7 +17,7 @@ export declare class RouteConnectionHandler {
17
17
  private timeoutManager;
18
18
  private routeManager;
19
19
  private settings;
20
- private routeContextCache;
20
+ newConnectionSubject: plugins.smartrx.rxjs.Subject<IConnectionRecord>;
21
21
  constructor(settings: ISmartProxyOptions, connectionManager: ConnectionManager, securityManager: SecurityManager, tlsManager: TlsManager, httpProxyBridge: HttpProxyBridge, timeoutManager: TimeoutManager, routeManager: RouteManager);
22
22
  /**
23
23
  * Create a route context object for port and host mapping functions