@push.rocks/smartproxy 19.5.19 → 19.5.21
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/index.d.ts +2 -0
- package/dist_ts/core/models/index.js +3 -1
- package/dist_ts/core/models/socket-types.d.ts +14 -0
- package/dist_ts/core/models/socket-types.js +15 -0
- package/dist_ts/core/models/wrapped-socket.d.ts +34 -0
- package/dist_ts/core/models/wrapped-socket.js +82 -0
- package/dist_ts/core/routing/index.d.ts +11 -0
- package/dist_ts/core/routing/index.js +17 -0
- package/dist_ts/core/routing/matchers/domain.d.ts +34 -0
- package/dist_ts/core/routing/matchers/domain.js +91 -0
- package/dist_ts/core/routing/matchers/header.d.ts +32 -0
- package/dist_ts/core/routing/matchers/header.js +94 -0
- package/dist_ts/core/routing/matchers/index.d.ts +18 -0
- package/dist_ts/core/routing/matchers/index.js +20 -0
- package/dist_ts/core/routing/matchers/ip.d.ts +53 -0
- package/dist_ts/core/routing/matchers/ip.js +169 -0
- package/dist_ts/core/routing/matchers/path.d.ts +44 -0
- package/dist_ts/core/routing/matchers/path.js +148 -0
- package/dist_ts/core/routing/route-manager.d.ts +88 -0
- package/dist_ts/core/routing/route-manager.js +342 -0
- package/dist_ts/core/routing/route-utils.d.ts +28 -0
- package/dist_ts/core/routing/route-utils.js +67 -0
- package/dist_ts/core/routing/specificity.d.ts +30 -0
- package/dist_ts/core/routing/specificity.js +115 -0
- package/dist_ts/core/routing/types.d.ts +41 -0
- package/dist_ts/core/routing/types.js +5 -0
- package/dist_ts/core/utils/index.d.ts +1 -2
- package/dist_ts/core/utils/index.js +2 -3
- package/dist_ts/core/utils/proxy-protocol.d.ts +45 -0
- package/dist_ts/core/utils/proxy-protocol.js +201 -0
- package/dist_ts/core/utils/route-manager.d.ts +0 -30
- package/dist_ts/core/utils/route-manager.js +6 -47
- package/dist_ts/core/utils/route-utils.d.ts +2 -68
- package/dist_ts/core/utils/route-utils.js +21 -218
- package/dist_ts/core/utils/security-utils.js +4 -4
- package/dist_ts/index.d.ts +2 -5
- package/dist_ts/index.js +5 -11
- package/dist_ts/proxies/http-proxy/http-proxy.d.ts +0 -1
- package/dist_ts/proxies/http-proxy/http-proxy.js +15 -60
- package/dist_ts/proxies/http-proxy/models/types.d.ts +0 -90
- package/dist_ts/proxies/http-proxy/models/types.js +1 -242
- package/dist_ts/proxies/http-proxy/request-handler.d.ts +3 -5
- package/dist_ts/proxies/http-proxy/request-handler.js +20 -171
- package/dist_ts/proxies/http-proxy/websocket-handler.d.ts +2 -5
- package/dist_ts/proxies/http-proxy/websocket-handler.js +15 -23
- package/dist_ts/proxies/index.d.ts +2 -2
- package/dist_ts/proxies/index.js +4 -3
- package/dist_ts/proxies/smart-proxy/connection-manager.d.ts +3 -1
- package/dist_ts/proxies/smart-proxy/connection-manager.js +17 -7
- package/dist_ts/proxies/smart-proxy/http-proxy-bridge.d.ts +2 -1
- package/dist_ts/proxies/smart-proxy/http-proxy-bridge.js +5 -2
- package/dist_ts/proxies/smart-proxy/index.d.ts +1 -1
- package/dist_ts/proxies/smart-proxy/index.js +2 -2
- package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +7 -2
- package/dist_ts/proxies/smart-proxy/models/route-types.d.ts +1 -0
- package/dist_ts/proxies/smart-proxy/models/route-types.js +1 -1
- package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +1 -1
- package/dist_ts/proxies/smart-proxy/route-connection-handler.js +155 -35
- package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +1 -1
- package/dist_ts/proxies/smart-proxy/smart-proxy.js +15 -4
- package/dist_ts/proxies/smart-proxy/utils/route-utils.js +10 -43
- package/dist_ts/routing/router/http-router.d.ts +89 -0
- package/dist_ts/routing/router/http-router.js +205 -0
- package/dist_ts/routing/router/index.d.ts +2 -5
- package/dist_ts/routing/router/index.js +3 -4
- package/package.json +1 -1
- package/readme.delete.md +187 -0
- package/readme.hints.md +196 -1
- package/readme.plan.md +625 -0
- package/readme.proxy-chain-summary.md +112 -0
- package/readme.proxy-protocol-example.md +462 -0
- package/readme.proxy-protocol.md +415 -0
- package/readme.routing.md +341 -0
- package/ts/core/models/index.ts +2 -0
- package/ts/core/models/socket-types.ts +21 -0
- package/ts/core/models/wrapped-socket.ts +99 -0
- package/ts/core/routing/index.ts +21 -0
- package/ts/core/routing/matchers/domain.ts +119 -0
- package/ts/core/routing/matchers/header.ts +120 -0
- package/ts/core/routing/matchers/index.ts +22 -0
- package/ts/core/routing/matchers/ip.ts +207 -0
- package/ts/core/routing/matchers/path.ts +184 -0
- package/ts/core/{utils → routing}/route-manager.ts +7 -57
- package/ts/core/routing/route-utils.ts +88 -0
- package/ts/core/routing/specificity.ts +141 -0
- package/ts/core/routing/types.ts +49 -0
- package/ts/core/utils/index.ts +1 -2
- package/ts/core/utils/proxy-protocol.ts +246 -0
- package/ts/core/utils/security-utils.ts +3 -7
- package/ts/index.ts +4 -14
- package/ts/proxies/http-proxy/http-proxy.ts +13 -68
- package/ts/proxies/http-proxy/models/types.ts +0 -324
- package/ts/proxies/http-proxy/request-handler.ts +15 -186
- package/ts/proxies/http-proxy/websocket-handler.ts +15 -26
- package/ts/proxies/index.ts +3 -2
- package/ts/proxies/smart-proxy/connection-manager.ts +17 -7
- package/ts/proxies/smart-proxy/http-proxy-bridge.ts +6 -2
- package/ts/proxies/smart-proxy/index.ts +1 -1
- package/ts/proxies/smart-proxy/models/interfaces.ts +9 -2
- package/ts/proxies/smart-proxy/models/route-types.ts +3 -0
- package/ts/proxies/smart-proxy/route-connection-handler.ts +173 -42
- package/ts/proxies/smart-proxy/smart-proxy.ts +15 -3
- package/ts/proxies/smart-proxy/utils/route-utils.ts +11 -49
- package/ts/routing/router/http-router.ts +266 -0
- package/ts/routing/router/index.ts +3 -8
- package/readme.problems.md +0 -170
- package/ts/core/utils/route-utils.ts +0 -312
- package/ts/proxies/smart-proxy/route-manager.ts +0 -554
- package/ts/routing/router/proxy-router.ts +0 -437
- package/ts/routing/router/route-router.ts +0 -482
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import * as plugins from '../../plugins.js';
|
|
2
|
+
import type { IRouteConfig } from '../../proxies/smart-proxy/models/route-types.js';
|
|
3
|
+
import { DomainMatcher, PathMatcher } from '../../core/routing/matchers/index.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Interface for router result with additional metadata
|
|
7
|
+
*/
|
|
8
|
+
export interface RouterResult {
|
|
9
|
+
route: IRouteConfig;
|
|
10
|
+
pathMatch?: string;
|
|
11
|
+
pathParams?: Record<string, string>;
|
|
12
|
+
pathRemainder?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Logger interface for HttpRouter
|
|
18
|
+
*/
|
|
19
|
+
export interface ILogger {
|
|
20
|
+
debug?: (message: string, data?: any) => void;
|
|
21
|
+
info: (message: string, data?: any) => void;
|
|
22
|
+
warn: (message: string, data?: any) => void;
|
|
23
|
+
error: (message: string, data?: any) => void;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Unified HTTP Router for reverse proxy requests
|
|
28
|
+
*
|
|
29
|
+
* Domain matching patterns:
|
|
30
|
+
* - Exact matches: "example.com"
|
|
31
|
+
* - Wildcard subdomains: "*.example.com" (matches any subdomain of example.com)
|
|
32
|
+
* - TLD wildcards: "example.*" (matches example.com, example.org, etc.)
|
|
33
|
+
* - Complex wildcards: "*.lossless*" (matches any subdomain of any lossless domain)
|
|
34
|
+
* - Default fallback: "*" (matches any unmatched domain)
|
|
35
|
+
*
|
|
36
|
+
* Path pattern matching:
|
|
37
|
+
* - Exact path: "/api/users"
|
|
38
|
+
* - Wildcard paths: "/api/*"
|
|
39
|
+
* - Path parameters: "/users/:id/profile"
|
|
40
|
+
*/
|
|
41
|
+
export class HttpRouter {
|
|
42
|
+
// Store routes sorted by priority
|
|
43
|
+
private routes: IRouteConfig[] = [];
|
|
44
|
+
// Default route to use when no match is found (optional)
|
|
45
|
+
private defaultRoute?: IRouteConfig;
|
|
46
|
+
// Logger interface
|
|
47
|
+
private logger: ILogger;
|
|
48
|
+
|
|
49
|
+
constructor(
|
|
50
|
+
routes?: IRouteConfig[],
|
|
51
|
+
logger?: ILogger
|
|
52
|
+
) {
|
|
53
|
+
this.logger = logger || {
|
|
54
|
+
error: console.error.bind(console),
|
|
55
|
+
warn: console.warn.bind(console),
|
|
56
|
+
info: console.info.bind(console),
|
|
57
|
+
debug: console.debug?.bind(console)
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
if (routes) {
|
|
61
|
+
this.setRoutes(routes);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Sets a new set of routes
|
|
67
|
+
* @param routes Array of route configurations
|
|
68
|
+
*/
|
|
69
|
+
public setRoutes(routes: IRouteConfig[]): void {
|
|
70
|
+
this.routes = [...routes];
|
|
71
|
+
|
|
72
|
+
// Sort routes by priority (higher priority first)
|
|
73
|
+
this.routes.sort((a, b) => {
|
|
74
|
+
const priorityA = a.priority ?? 0;
|
|
75
|
+
const priorityB = b.priority ?? 0;
|
|
76
|
+
return priorityB - priorityA;
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// Find default route if any (route with "*" as domain)
|
|
80
|
+
this.defaultRoute = this.routes.find(route => {
|
|
81
|
+
const domains = Array.isArray(route.match.domains)
|
|
82
|
+
? route.match.domains
|
|
83
|
+
: route.match.domains ? [route.match.domains] : [];
|
|
84
|
+
return domains.includes('*');
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const uniqueDomains = this.getHostnames();
|
|
88
|
+
this.logger.info(`HttpRouter initialized with ${this.routes.length} routes (${uniqueDomains.length} unique hosts)`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Routes a request based on hostname and path
|
|
93
|
+
* @param req The incoming HTTP request
|
|
94
|
+
* @returns The matching route or undefined if no match found
|
|
95
|
+
*/
|
|
96
|
+
public routeReq(req: plugins.http.IncomingMessage): IRouteConfig | undefined {
|
|
97
|
+
const result = this.routeReqWithDetails(req);
|
|
98
|
+
return result ? result.route : undefined;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Routes a request with detailed matching information
|
|
103
|
+
* @param req The incoming HTTP request
|
|
104
|
+
* @returns Detailed routing result including matched route and path information
|
|
105
|
+
*/
|
|
106
|
+
public routeReqWithDetails(req: plugins.http.IncomingMessage): RouterResult | undefined {
|
|
107
|
+
// Extract and validate host header
|
|
108
|
+
const originalHost = req.headers.host;
|
|
109
|
+
if (!originalHost) {
|
|
110
|
+
this.logger.error('No host header found in request');
|
|
111
|
+
return this.defaultRoute ? { route: this.defaultRoute } : undefined;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Parse URL for path matching
|
|
115
|
+
const parsedUrl = plugins.url.parse(req.url || '/');
|
|
116
|
+
const urlPath = parsedUrl.pathname || '/';
|
|
117
|
+
|
|
118
|
+
// Extract hostname without port
|
|
119
|
+
const hostWithoutPort = originalHost.split(':')[0].toLowerCase();
|
|
120
|
+
|
|
121
|
+
// Find matching route
|
|
122
|
+
const matchingRoute = this.findMatchingRoute(hostWithoutPort, urlPath);
|
|
123
|
+
|
|
124
|
+
if (matchingRoute) {
|
|
125
|
+
return matchingRoute;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Fall back to default route if available
|
|
129
|
+
if (this.defaultRoute) {
|
|
130
|
+
this.logger.warn(`No specific route found for host: ${hostWithoutPort}, using default`);
|
|
131
|
+
return { route: this.defaultRoute };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
this.logger.error(`No route found for host: ${hostWithoutPort}`);
|
|
135
|
+
return undefined;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Find the best matching route for a given hostname and path
|
|
140
|
+
*/
|
|
141
|
+
private findMatchingRoute(hostname: string, path: string): RouterResult | undefined {
|
|
142
|
+
// Try each route in priority order
|
|
143
|
+
for (const route of this.routes) {
|
|
144
|
+
// Skip disabled routes
|
|
145
|
+
if (route.enabled === false) {
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Check domain match
|
|
150
|
+
if (route.match.domains) {
|
|
151
|
+
const domains = Array.isArray(route.match.domains)
|
|
152
|
+
? route.match.domains
|
|
153
|
+
: [route.match.domains];
|
|
154
|
+
|
|
155
|
+
// Check if any domain pattern matches
|
|
156
|
+
const domainMatches = domains.some(domain =>
|
|
157
|
+
DomainMatcher.match(domain, hostname)
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
if (!domainMatches) {
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Check path match if specified
|
|
166
|
+
if (route.match.path) {
|
|
167
|
+
const pathResult = PathMatcher.match(route.match.path, path);
|
|
168
|
+
if (pathResult.matches) {
|
|
169
|
+
return {
|
|
170
|
+
route,
|
|
171
|
+
pathMatch: path,
|
|
172
|
+
pathParams: pathResult.params,
|
|
173
|
+
pathRemainder: pathResult.pathRemainder
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
} else {
|
|
177
|
+
// No path specified, so domain match is sufficient
|
|
178
|
+
return { route };
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return undefined;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Gets all currently active route configurations
|
|
187
|
+
* @returns Array of all active routes
|
|
188
|
+
*/
|
|
189
|
+
public getRoutes(): IRouteConfig[] {
|
|
190
|
+
return [...this.routes];
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Gets all hostnames that this router is configured to handle
|
|
195
|
+
* @returns Array of unique hostnames
|
|
196
|
+
*/
|
|
197
|
+
public getHostnames(): string[] {
|
|
198
|
+
const hostnames = new Set<string>();
|
|
199
|
+
for (const route of this.routes) {
|
|
200
|
+
if (!route.match.domains) continue;
|
|
201
|
+
|
|
202
|
+
const domains = Array.isArray(route.match.domains)
|
|
203
|
+
? route.match.domains
|
|
204
|
+
: [route.match.domains];
|
|
205
|
+
|
|
206
|
+
for (const domain of domains) {
|
|
207
|
+
if (domain !== '*') {
|
|
208
|
+
hostnames.add(domain.toLowerCase());
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return Array.from(hostnames);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Adds a single new route configuration
|
|
217
|
+
* @param route The route configuration to add
|
|
218
|
+
*/
|
|
219
|
+
public addRoute(route: IRouteConfig): void {
|
|
220
|
+
this.routes.push(route);
|
|
221
|
+
|
|
222
|
+
// Re-sort routes by priority
|
|
223
|
+
this.routes.sort((a, b) => {
|
|
224
|
+
const priorityA = a.priority ?? 0;
|
|
225
|
+
const priorityB = b.priority ?? 0;
|
|
226
|
+
return priorityB - priorityA;
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Removes routes by domain pattern
|
|
232
|
+
* @param domain The domain pattern to remove routes for
|
|
233
|
+
* @returns Boolean indicating whether any routes were removed
|
|
234
|
+
*/
|
|
235
|
+
public removeRoutesByDomain(domain: string): boolean {
|
|
236
|
+
const initialCount = this.routes.length;
|
|
237
|
+
|
|
238
|
+
// Filter out routes that match the domain
|
|
239
|
+
this.routes = this.routes.filter(route => {
|
|
240
|
+
if (!route.match.domains) return true;
|
|
241
|
+
|
|
242
|
+
const domains = Array.isArray(route.match.domains)
|
|
243
|
+
? route.match.domains
|
|
244
|
+
: [route.match.domains];
|
|
245
|
+
|
|
246
|
+
return !domains.includes(domain);
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
return this.routes.length !== initialCount;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Remove a specific route by reference
|
|
254
|
+
* @param route The route to remove
|
|
255
|
+
* @returns Boolean indicating if the route was found and removed
|
|
256
|
+
*/
|
|
257
|
+
public removeRoute(route: IRouteConfig): boolean {
|
|
258
|
+
const index = this.routes.indexOf(route);
|
|
259
|
+
if (index !== -1) {
|
|
260
|
+
this.routes.splice(index, 1);
|
|
261
|
+
return true;
|
|
262
|
+
}
|
|
263
|
+
return false;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
}
|
|
@@ -2,11 +2,6 @@
|
|
|
2
2
|
* HTTP routing
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
// Export
|
|
6
|
-
export {
|
|
7
|
-
export type {
|
|
8
|
-
// Re-export the RouterResult and PathPatternConfig from proxy-router.js (legacy names maintained for compatibility)
|
|
9
|
-
export type { PathPatternConfig as ProxyPathPatternConfig, RouterResult as ProxyRouterResult } from './proxy-router.js';
|
|
10
|
-
|
|
11
|
-
export { RouteRouter } from './route-router.js';
|
|
12
|
-
export type { PathPatternConfig as RoutePathPatternConfig, RouterResult as RouteRouterResult } from './route-router.js';
|
|
5
|
+
// Export the unified HttpRouter
|
|
6
|
+
export { HttpRouter } from './http-router.js';
|
|
7
|
+
export type { RouterResult, ILogger } from './http-router.js';
|
package/readme.problems.md
DELETED
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
# SmartProxy Performance Issues Report
|
|
2
|
-
|
|
3
|
-
## Executive Summary
|
|
4
|
-
This report identifies performance issues and blocking operations in the SmartProxy codebase that could impact scalability and responsiveness under high load.
|
|
5
|
-
|
|
6
|
-
## Critical Issues
|
|
7
|
-
|
|
8
|
-
### 1. **Synchronous Filesystem Operations**
|
|
9
|
-
These operations block the event loop and should be replaced with async alternatives:
|
|
10
|
-
|
|
11
|
-
#### Certificate Management
|
|
12
|
-
- `ts/proxies/http-proxy/certificate-manager.ts:29`: `fs.existsSync()`
|
|
13
|
-
- `ts/proxies/http-proxy/certificate-manager.ts:30`: `fs.mkdirSync()`
|
|
14
|
-
- `ts/proxies/http-proxy/certificate-manager.ts:49-50`: `fs.readFileSync()` for loading certificates
|
|
15
|
-
|
|
16
|
-
#### NFTables Proxy
|
|
17
|
-
- `ts/proxies/nftables-proxy/nftables-proxy.ts`: Multiple uses of `execSync()` for system commands
|
|
18
|
-
- `ts/proxies/nftables-proxy/nftables-proxy.ts`: Multiple `fs.writeFileSync()` and `fs.unlinkSync()` operations
|
|
19
|
-
|
|
20
|
-
#### Certificate Store
|
|
21
|
-
- `ts/proxies/smart-proxy/cert-store.ts:8`: `ensureDirSync()`
|
|
22
|
-
- `ts/proxies/smart-proxy/cert-store.ts:15,31,76`: `fileExistsSync()`
|
|
23
|
-
- `ts/proxies/smart-proxy/cert-store.ts:77`: `removeManySync()`
|
|
24
|
-
|
|
25
|
-
### 2. **Event Loop Blocking Operations**
|
|
26
|
-
|
|
27
|
-
#### Busy Wait Loop
|
|
28
|
-
- `ts/proxies/nftables-proxy/nftables-proxy.ts:235-238`:
|
|
29
|
-
```typescript
|
|
30
|
-
const waitUntil = Date.now() + retryDelayMs;
|
|
31
|
-
while (Date.now() < waitUntil) {
|
|
32
|
-
// busy wait - blocks event loop completely
|
|
33
|
-
}
|
|
34
|
-
```
|
|
35
|
-
This is extremely problematic as it blocks the entire Node.js event loop.
|
|
36
|
-
|
|
37
|
-
### 3. **Potential Memory Leaks**
|
|
38
|
-
|
|
39
|
-
#### Timer Management Issues
|
|
40
|
-
Several timers are created without proper cleanup:
|
|
41
|
-
- `ts/proxies/http-proxy/function-cache.ts`: `setInterval()` without storing reference for cleanup
|
|
42
|
-
- `ts/proxies/http-proxy/request-handler.ts`: `setInterval()` for rate limit cleanup without cleanup
|
|
43
|
-
- `ts/core/utils/shared-security-manager.ts`: `cleanupInterval` stored but no cleanup method
|
|
44
|
-
|
|
45
|
-
#### Event Listener Accumulation
|
|
46
|
-
- Multiple instances of event listeners being added without corresponding cleanup
|
|
47
|
-
- Connection handlers add listeners without always removing them on connection close
|
|
48
|
-
|
|
49
|
-
### 4. **Connection Pool Management**
|
|
50
|
-
|
|
51
|
-
#### ConnectionPool (ts/proxies/http-proxy/connection-pool.ts)
|
|
52
|
-
**Good practices observed:**
|
|
53
|
-
- Proper connection lifecycle management
|
|
54
|
-
- Periodic cleanup of idle connections
|
|
55
|
-
- Connection limits enforcement
|
|
56
|
-
|
|
57
|
-
**Potential issues:**
|
|
58
|
-
- No backpressure mechanism when pool is full
|
|
59
|
-
- Synchronous sorting operation in `cleanupConnectionPool()` could be slow with many connections
|
|
60
|
-
|
|
61
|
-
### 5. **Resource Management Issues**
|
|
62
|
-
|
|
63
|
-
#### Socket Cleanup
|
|
64
|
-
- Some error paths don't properly clean up sockets
|
|
65
|
-
- Missing `removeAllListeners()` in some error scenarios could lead to memory leaks
|
|
66
|
-
|
|
67
|
-
#### Timeout Management
|
|
68
|
-
- Inconsistent timeout handling across different components
|
|
69
|
-
- Some sockets created without timeout settings
|
|
70
|
-
|
|
71
|
-
### 6. **JSON Operations on Large Objects**
|
|
72
|
-
- `ts/proxies/smart-proxy/cert-store.ts:21`: `JSON.parse()` on certificate metadata
|
|
73
|
-
- `ts/proxies/smart-proxy/cert-store.ts:71`: `JSON.stringify()` with pretty printing
|
|
74
|
-
- `ts/proxies/http-proxy/function-cache.ts:76`: `JSON.stringify()` for cache keys (called frequently)
|
|
75
|
-
|
|
76
|
-
## Recommendations
|
|
77
|
-
|
|
78
|
-
### Immediate Actions (High Priority)
|
|
79
|
-
|
|
80
|
-
1. **Replace Synchronous Operations**
|
|
81
|
-
```typescript
|
|
82
|
-
// Instead of:
|
|
83
|
-
if (fs.existsSync(path)) { ... }
|
|
84
|
-
|
|
85
|
-
// Use:
|
|
86
|
-
try {
|
|
87
|
-
await fs.promises.access(path);
|
|
88
|
-
// file exists
|
|
89
|
-
} catch {
|
|
90
|
-
// file doesn't exist
|
|
91
|
-
}
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
2. **Fix Busy Wait Loop**
|
|
95
|
-
```typescript
|
|
96
|
-
// Instead of:
|
|
97
|
-
while (Date.now() < waitUntil) { }
|
|
98
|
-
|
|
99
|
-
// Use:
|
|
100
|
-
await new Promise(resolve => setTimeout(resolve, retryDelayMs));
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
3. **Add Timer Cleanup**
|
|
104
|
-
```typescript
|
|
105
|
-
class Component {
|
|
106
|
-
private cleanupTimer?: NodeJS.Timeout;
|
|
107
|
-
|
|
108
|
-
start() {
|
|
109
|
-
this.cleanupTimer = setInterval(() => { ... }, 60000);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
stop() {
|
|
113
|
-
if (this.cleanupTimer) {
|
|
114
|
-
clearInterval(this.cleanupTimer);
|
|
115
|
-
this.cleanupTimer = undefined;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
### Medium Priority
|
|
122
|
-
|
|
123
|
-
1. **Optimize JSON Operations**
|
|
124
|
-
- Cache JSON.stringify results for frequently used objects
|
|
125
|
-
- Consider using faster hashing for cache keys (e.g., crypto.createHash)
|
|
126
|
-
- Use streaming JSON parsers for large objects
|
|
127
|
-
|
|
128
|
-
2. **Improve Connection Pool**
|
|
129
|
-
- Implement backpressure/queueing when pool is full
|
|
130
|
-
- Use a heap or priority queue for connection management instead of sorting
|
|
131
|
-
|
|
132
|
-
3. **Standardize Resource Cleanup**
|
|
133
|
-
- Create a base class for components with lifecycle management
|
|
134
|
-
- Ensure all event listeners are removed on cleanup
|
|
135
|
-
- Add abort controllers for better cancellation support
|
|
136
|
-
|
|
137
|
-
### Long-term Improvements
|
|
138
|
-
|
|
139
|
-
1. **Worker Threads**
|
|
140
|
-
- Move CPU-intensive operations to worker threads
|
|
141
|
-
- Consider using worker pools for NFTables operations
|
|
142
|
-
|
|
143
|
-
2. **Monitoring and Metrics**
|
|
144
|
-
- Add performance monitoring for event loop lag
|
|
145
|
-
- Track connection pool utilization
|
|
146
|
-
- Monitor memory usage patterns
|
|
147
|
-
|
|
148
|
-
3. **Graceful Degradation**
|
|
149
|
-
- Implement circuit breakers for backend connections
|
|
150
|
-
- Add request queuing with overflow protection
|
|
151
|
-
- Implement adaptive timeout strategies
|
|
152
|
-
|
|
153
|
-
## Impact Assessment
|
|
154
|
-
|
|
155
|
-
These issues primarily affect:
|
|
156
|
-
- **Scalability**: Blocking operations limit concurrent connection handling
|
|
157
|
-
- **Responsiveness**: Event loop blocking causes latency spikes
|
|
158
|
-
- **Stability**: Memory leaks could cause crashes under sustained load
|
|
159
|
-
- **Resource Usage**: Inefficient resource management increases memory/CPU usage
|
|
160
|
-
|
|
161
|
-
## Testing Recommendations
|
|
162
|
-
|
|
163
|
-
1. Load test with high connection counts (10k+ concurrent)
|
|
164
|
-
2. Monitor event loop lag under stress
|
|
165
|
-
3. Test long-running scenarios to detect memory leaks
|
|
166
|
-
4. Benchmark with async vs sync operations to measure improvement
|
|
167
|
-
|
|
168
|
-
## Conclusion
|
|
169
|
-
|
|
170
|
-
While SmartProxy has good architectural design and many best practices, the identified blocking operations and resource management issues could significantly impact performance under high load. The most critical issues (busy wait loop and synchronous filesystem operations) should be addressed immediately.
|