@push.rocks/smartproxy 19.5.4 → 19.5.6

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 (62) hide show
  1. package/dist_ts/core/utils/async-utils.d.ts +81 -0
  2. package/dist_ts/core/utils/async-utils.js +216 -0
  3. package/dist_ts/core/utils/binary-heap.d.ts +73 -0
  4. package/dist_ts/core/utils/binary-heap.js +193 -0
  5. package/dist_ts/core/utils/enhanced-connection-pool.d.ts +110 -0
  6. package/dist_ts/core/utils/enhanced-connection-pool.js +320 -0
  7. package/dist_ts/core/utils/fs-utils.d.ts +144 -0
  8. package/dist_ts/core/utils/fs-utils.js +252 -0
  9. package/dist_ts/core/utils/index.d.ts +6 -2
  10. package/dist_ts/core/utils/index.js +7 -3
  11. package/dist_ts/core/utils/lifecycle-component.d.ts +59 -0
  12. package/dist_ts/core/utils/lifecycle-component.js +195 -0
  13. package/dist_ts/core/utils/socket-utils.d.ts +28 -0
  14. package/dist_ts/core/utils/socket-utils.js +77 -0
  15. package/dist_ts/forwarding/handlers/http-handler.js +7 -4
  16. package/dist_ts/forwarding/handlers/https-passthrough-handler.js +14 -55
  17. package/dist_ts/forwarding/handlers/https-terminate-to-http-handler.js +52 -40
  18. package/dist_ts/forwarding/handlers/https-terminate-to-https-handler.js +31 -43
  19. package/dist_ts/plugins.d.ts +2 -1
  20. package/dist_ts/plugins.js +3 -2
  21. package/dist_ts/proxies/http-proxy/certificate-manager.d.ts +15 -0
  22. package/dist_ts/proxies/http-proxy/certificate-manager.js +49 -2
  23. package/dist_ts/proxies/http-proxy/connection-pool.js +4 -19
  24. package/dist_ts/proxies/http-proxy/http-proxy.js +3 -7
  25. package/dist_ts/proxies/nftables-proxy/nftables-proxy.d.ts +10 -0
  26. package/dist_ts/proxies/nftables-proxy/nftables-proxy.js +53 -43
  27. package/dist_ts/proxies/smart-proxy/cert-store.js +22 -20
  28. package/dist_ts/proxies/smart-proxy/connection-manager.d.ts +35 -9
  29. package/dist_ts/proxies/smart-proxy/connection-manager.js +243 -189
  30. package/dist_ts/proxies/smart-proxy/http-proxy-bridge.js +13 -2
  31. package/dist_ts/proxies/smart-proxy/port-manager.js +3 -3
  32. package/dist_ts/proxies/smart-proxy/route-connection-handler.js +35 -4
  33. package/package.json +2 -2
  34. package/readme.hints.md +96 -1
  35. package/readme.plan.md +1135 -221
  36. package/readme.problems.md +167 -83
  37. package/ts/core/utils/async-utils.ts +275 -0
  38. package/ts/core/utils/binary-heap.ts +225 -0
  39. package/ts/core/utils/enhanced-connection-pool.ts +420 -0
  40. package/ts/core/utils/fs-utils.ts +270 -0
  41. package/ts/core/utils/index.ts +6 -2
  42. package/ts/core/utils/lifecycle-component.ts +231 -0
  43. package/ts/core/utils/socket-utils.ts +96 -0
  44. package/ts/forwarding/handlers/http-handler.ts +7 -3
  45. package/ts/forwarding/handlers/https-passthrough-handler.ts +13 -62
  46. package/ts/forwarding/handlers/https-terminate-to-http-handler.ts +58 -46
  47. package/ts/forwarding/handlers/https-terminate-to-https-handler.ts +38 -53
  48. package/ts/plugins.ts +2 -1
  49. package/ts/proxies/http-proxy/certificate-manager.ts +52 -1
  50. package/ts/proxies/http-proxy/connection-pool.ts +3 -16
  51. package/ts/proxies/http-proxy/http-proxy.ts +2 -5
  52. package/ts/proxies/nftables-proxy/nftables-proxy.ts +64 -79
  53. package/ts/proxies/smart-proxy/cert-store.ts +26 -20
  54. package/ts/proxies/smart-proxy/connection-manager.ts +277 -197
  55. package/ts/proxies/smart-proxy/http-proxy-bridge.ts +15 -1
  56. package/ts/proxies/smart-proxy/port-manager.ts +2 -2
  57. package/ts/proxies/smart-proxy/route-connection-handler.ts +39 -4
  58. package/readme.plan2.md +0 -764
  59. package/ts/common/eventUtils.ts +0 -34
  60. package/ts/common/types.ts +0 -91
  61. package/ts/core/utils/event-system.ts +0 -376
  62. package/ts/core/utils/event-utils.ts +0 -25
package/readme.plan.md CHANGED
@@ -1,316 +1,1230 @@
1
- # SmartProxy Development Plan
1
+ # SmartProxy Performance Optimization Plan
2
2
 
3
- ## Implementation Plan: Socket Handler Function Support (Simplified) ✅ COMPLETED
3
+ ## Executive Summary
4
4
 
5
- ### Overview
6
- Add support for custom socket handler functions with the simplest possible API - just pass a function that receives the socket.
5
+ This plan addresses critical performance issues in SmartProxy that impact scalability, responsiveness, and stability. The approach is phased, starting with critical event loop blockers and progressing to long-term architectural improvements.
7
6
 
8
- ### User Experience Goal
7
+ ## Phase 1: Critical Issues (Week 1) ✅ COMPLETE
8
+
9
+ ### 1.1 Eliminate Busy Wait Loop ✅
10
+
11
+ **Issue**: `ts/proxies/nftables-proxy/nftables-proxy.ts:235-238` blocks the entire event loop
12
+
13
+ **Solution**:
9
14
  ```typescript
10
- const proxy = new SmartProxy({
11
- routes: [{
12
- name: 'my-custom-protocol',
13
- match: { ports: 9000, domains: 'custom.example.com' },
14
- action: {
15
- type: 'socket-handler',
16
- socketHandler: (socket) => {
17
- // User has full control of the socket
18
- socket.write('Welcome!\n');
19
- socket.on('data', (data) => {
20
- socket.write(`Echo: ${data}`);
21
- });
22
- }
15
+ // Create utility function in ts/core/utils/async-utils.ts
16
+ export async function delay(ms: number): Promise<void> {
17
+ return new Promise(resolve => setTimeout(resolve, ms));
18
+ }
19
+
20
+ // Replace busy wait in nftables-proxy.ts
21
+ // OLD:
22
+ const waitUntil = Date.now() + retryDelayMs;
23
+ while (Date.now() < waitUntil) { }
24
+
25
+ // NEW:
26
+ await delay(retryDelayMs);
27
+ ```
28
+
29
+ **Implementation**:
30
+ 1. Create `async-utils.ts` with common async utilities
31
+ 2. Replace all synchronous sleeps with async delay
32
+ 3. Ensure all calling functions are async
33
+
34
+ ### 1.2 Async Filesystem Operations ✅
35
+
36
+ **Issue**: Multiple synchronous filesystem operations blocking the event loop
37
+
38
+ **Solution Architecture**:
39
+ ```typescript
40
+ // Create ts/core/utils/fs-utils.ts
41
+ import * as plugins from '../../plugins.js';
42
+
43
+ export class AsyncFileSystem {
44
+ static async exists(path: string): Promise<boolean> {
45
+ try {
46
+ await plugins.fs.promises.access(path);
47
+ return true;
48
+ } catch {
49
+ return false;
23
50
  }
24
- }]
25
- });
51
+ }
52
+
53
+ static async ensureDir(path: string): Promise<void> {
54
+ await plugins.fs.promises.mkdir(path, { recursive: true });
55
+ }
56
+
57
+ static async readFile(path: string): Promise<string> {
58
+ return plugins.fs.promises.readFile(path, 'utf8');
59
+ }
60
+
61
+ static async writeFile(path: string, data: string): Promise<void> {
62
+ // Ensure directory exists
63
+ const dir = plugins.path.dirname(path);
64
+ await this.ensureDir(dir);
65
+ await plugins.fs.promises.writeFile(path, data);
66
+ }
67
+
68
+ static async remove(path: string): Promise<void> {
69
+ try {
70
+ await plugins.fs.promises.unlink(path);
71
+ } catch (error: any) {
72
+ if (error.code !== 'ENOENT') throw error;
73
+ }
74
+ }
75
+
76
+ static async readJSON<T>(path: string): Promise<T> {
77
+ const content = await this.readFile(path);
78
+ return JSON.parse(content);
79
+ }
80
+
81
+ static async writeJSON(path: string, data: any): Promise<void> {
82
+ await this.writeFile(path, JSON.stringify(data, null, 2));
83
+ }
84
+ }
26
85
  ```
27
86
 
28
- That's it. Simple and powerful.
87
+ **Migration Strategy**:
88
+
89
+ 1. **Certificate Manager** (`ts/proxies/http-proxy/certificate-manager.ts`)
90
+ ```typescript
91
+ // OLD:
92
+ constructor(private options: IHttpProxyOptions) {
93
+ if (!fs.existsSync(this.certDir)) {
94
+ fs.mkdirSync(this.certDir, { recursive: true });
95
+ }
96
+ }
97
+
98
+ // NEW:
99
+ private initialized = false;
100
+
101
+ constructor(private options: IHttpProxyOptions) {}
102
+
103
+ async initialize(): Promise<void> {
104
+ if (this.initialized) return;
105
+ await AsyncFileSystem.ensureDir(this.certDir);
106
+ this.initialized = true;
107
+ }
108
+
109
+ async getCertificate(domain: string): Promise<{ cert: string; key: string } | null> {
110
+ await this.initialize();
111
+ const certPath = path.join(this.certDir, `${domain}.crt`);
112
+ const keyPath = path.join(this.certDir, `${domain}.key`);
113
+
114
+ if (await AsyncFileSystem.exists(certPath) && await AsyncFileSystem.exists(keyPath)) {
115
+ const [cert, key] = await Promise.all([
116
+ AsyncFileSystem.readFile(certPath),
117
+ AsyncFileSystem.readFile(keyPath)
118
+ ]);
119
+ return { cert, key };
120
+ }
121
+ return null;
122
+ }
123
+ ```
29
124
 
30
- ---
125
+ 2. **Certificate Store** (`ts/proxies/smart-proxy/cert-store.ts`)
126
+ ```typescript
127
+ // Convert all methods to async
128
+ export class CertStore {
129
+ constructor(private storePath: string) {}
130
+
131
+ async init(): Promise<void> {
132
+ await AsyncFileSystem.ensureDir(this.storePath);
133
+ }
134
+
135
+ async hasCertificate(domain: string): Promise<boolean> {
136
+ const certPath = this.getCertificatePath(domain);
137
+ return AsyncFileSystem.exists(certPath);
138
+ }
139
+
140
+ async getCertificate(domain: string): Promise<ICertificateInfo | null> {
141
+ if (!await this.hasCertificate(domain)) return null;
142
+
143
+ const metaPath = path.join(this.getCertificatePath(domain), 'meta.json');
144
+ return AsyncFileSystem.readJSON<ICertificateInfo>(metaPath);
145
+ }
146
+ }
147
+ ```
31
148
 
32
- ## Phase 1: Minimal Type Changes
149
+ 3. **NFTables Proxy** (`ts/proxies/nftables-proxy/nftables-proxy.ts`)
150
+ ```typescript
151
+ // Replace execSync with execAsync
152
+ private async execNftCommand(command: string): Promise<string> {
153
+ const maxRetries = 3;
154
+ let lastError: Error | null = null;
155
+
156
+ for (let i = 0; i < maxRetries; i++) {
157
+ try {
158
+ const { stdout } = await this.execAsync(command);
159
+ return stdout;
160
+ } catch (err: any) {
161
+ lastError = err;
162
+ if (i < maxRetries - 1) {
163
+ await delay(this.retryDelayMs);
164
+ }
165
+ }
166
+ }
167
+
168
+ throw new NftExecutionError(`Failed after ${maxRetries} attempts: ${lastError?.message}`);
169
+ }
170
+ ```
33
171
 
34
- ### 1.1 Add Socket Handler Action Type
35
- **File:** `ts/proxies/smart-proxy/models/route-types.ts`
172
+ ## Dependencies Between Phases
36
173
 
174
+ ### Critical Path
175
+ ```
176
+ Phase 1.1 (Busy Wait) ─┐
177
+ ├─> Phase 2.1 (Timer Management) ─> Phase 3.2 (Worker Threads)
178
+ Phase 1.2 (Async FS) ──┘ │
179
+ ├─> Phase 4.1 (Monitoring)
180
+ Phase 2.2 (Connection Pool) ────────────────────────────┘
181
+ ```
182
+
183
+ ### Phase Dependencies
184
+ - **Phase 1** must complete before Phase 2 (foundation for async operations)
185
+ - **Phase 2.1** enables proper cleanup for Phase 3.2 worker threads
186
+ - **Phase 3** optimizations depend on stable async foundation
187
+ - **Phase 4** monitoring requires all components to be instrumented
188
+
189
+ ## Phase 2: Resource Management (Week 2) 🔨 IN PROGRESS
190
+
191
+ ### 2.1 Timer Lifecycle Management
192
+
193
+ **Issue**: Timers created without cleanup references causing memory leaks
194
+
195
+ **Solution Pattern**:
37
196
  ```typescript
38
- // Update action type
39
- export type TRouteActionType = 'forward' | 'redirect' | 'block' | 'static' | 'socket-handler';
197
+ // Create base class in ts/core/utils/lifecycle-component.ts
198
+ export abstract class LifecycleComponent {
199
+ private timers: Set<NodeJS.Timeout> = new Set();
200
+ private listeners: Array<{ target: any, event: string, handler: Function }> = [];
201
+ protected isShuttingDown = false;
40
202
 
41
- // Add simple socket handler type
42
- export type TSocketHandler = (socket: net.Socket) => void | Promise<void>;
203
+ protected setInterval(handler: Function, timeout: number): NodeJS.Timeout {
204
+ const timer = setInterval(() => {
205
+ if (!this.isShuttingDown) {
206
+ handler();
207
+ }
208
+ }, timeout);
209
+ this.timers.add(timer);
210
+ return timer;
211
+ }
43
212
 
44
- // Extend IRouteAction
45
- export interface IRouteAction {
46
- // ... existing properties
47
-
48
- // Socket handler function (when type is 'socket-handler')
49
- socketHandler?: TSocketHandler;
213
+ protected setTimeout(handler: Function, timeout: number): NodeJS.Timeout {
214
+ const timer = setTimeout(() => {
215
+ this.timers.delete(timer);
216
+ if (!this.isShuttingDown) {
217
+ handler();
218
+ }
219
+ }, timeout);
220
+ this.timers.add(timer);
221
+ return timer;
222
+ }
223
+
224
+ protected addEventListener(target: any, event: string, handler: Function): void {
225
+ target.on(event, handler);
226
+ this.listeners.push({ target, event, handler });
227
+ }
228
+
229
+ protected async cleanup(): Promise<void> {
230
+ this.isShuttingDown = true;
231
+
232
+ // Clear all timers
233
+ for (const timer of this.timers) {
234
+ clearInterval(timer);
235
+ clearTimeout(timer);
236
+ }
237
+ this.timers.clear();
238
+
239
+ // Remove all listeners
240
+ for (const { target, event, handler } of this.listeners) {
241
+ target.removeListener(event, handler);
242
+ }
243
+ this.listeners = [];
244
+ }
50
245
  }
51
246
  ```
52
247
 
53
- ---
54
-
55
- ## Phase 2: Simple Implementation
248
+ **Implementation**:
249
+ 1. Extend LifecycleComponent in:
250
+ - `HttpProxy`
251
+ - `SmartProxy`
252
+ - `ConnectionManager`
253
+ - `RequestHandler`
254
+ - `SharedSecurityManager`
255
+
256
+ 2. Replace direct timer/listener usage with lifecycle methods
56
257
 
57
- ### 2.1 Update Route Connection Handler
58
- **File:** `ts/proxies/smart-proxy/route-connection-handler.ts`
258
+ ### 2.2 Connection Pool Enhancement
59
259
 
60
- In the `handleConnection` method, add handling for socket-handler:
260
+ **Issue**: No backpressure mechanism and synchronous operations
61
261
 
262
+ **Solution**:
62
263
  ```typescript
63
- // After route matching...
64
- if (matchedRoute) {
65
- const action = matchedRoute.action;
264
+ // First, implement efficient BinaryHeap for O(log n) operations
265
+ // Create ts/core/utils/binary-heap.ts
266
+ export class BinaryHeap<T> {
267
+ private heap: T[] = [];
66
268
 
67
- if (action.type === 'socket-handler') {
68
- if (!action.socketHandler) {
69
- logger.error('socket-handler action missing socketHandler function');
70
- socket.destroy();
71
- return;
269
+ constructor(
270
+ private compareFn: (a: T, b: T) => number,
271
+ private extractKey?: (item: T) => string
272
+ ) {}
273
+
274
+ insert(item: T): void {
275
+ this.heap.push(item);
276
+ this.bubbleUp(this.heap.length - 1);
277
+ }
278
+
279
+ extract(): T | undefined {
280
+ if (this.heap.length === 0) return undefined;
281
+ if (this.heap.length === 1) return this.heap.pop();
282
+
283
+ const result = this.heap[0];
284
+ this.heap[0] = this.heap.pop()!;
285
+ this.bubbleDown(0);
286
+ return result;
287
+ }
288
+
289
+ extractIf(predicate: (item: T) => boolean): T | undefined {
290
+ const index = this.heap.findIndex(predicate);
291
+ if (index === -1) return undefined;
292
+
293
+ if (index === this.heap.length - 1) return this.heap.pop();
294
+
295
+ const result = this.heap[index];
296
+ this.heap[index] = this.heap.pop()!;
297
+
298
+ // Restore heap property
299
+ this.bubbleUp(index);
300
+ this.bubbleDown(index);
301
+ return result;
302
+ }
303
+
304
+ sizeFor(key: string): number {
305
+ if (!this.extractKey) return this.heap.length;
306
+ return this.heap.filter(item => this.extractKey!(item) === key).length;
307
+ }
308
+
309
+ private bubbleUp(index: number): void {
310
+ while (index > 0) {
311
+ const parentIndex = Math.floor((index - 1) / 2);
312
+ if (this.compareFn(this.heap[index], this.heap[parentIndex]) >= 0) break;
313
+
314
+ [this.heap[index], this.heap[parentIndex]] =
315
+ [this.heap[parentIndex], this.heap[index]];
316
+ index = parentIndex;
317
+ }
318
+ }
319
+
320
+ private bubbleDown(index: number): void {
321
+ while (true) {
322
+ let minIndex = index;
323
+ const leftChild = 2 * index + 1;
324
+ const rightChild = 2 * index + 2;
325
+
326
+ if (leftChild < this.heap.length &&
327
+ this.compareFn(this.heap[leftChild], this.heap[minIndex]) < 0) {
328
+ minIndex = leftChild;
329
+ }
330
+
331
+ if (rightChild < this.heap.length &&
332
+ this.compareFn(this.heap[rightChild], this.heap[minIndex]) < 0) {
333
+ minIndex = rightChild;
334
+ }
335
+
336
+ if (minIndex === index) break;
337
+
338
+ [this.heap[index], this.heap[minIndex]] =
339
+ [this.heap[minIndex], this.heap[index]];
340
+ index = minIndex;
341
+ }
342
+ }
343
+ }
344
+
345
+ // Enhanced connection pool with queue and heap
346
+ export class EnhancedConnectionPool extends LifecycleComponent {
347
+ private connectionQueue: Array<{
348
+ resolve: (socket: net.Socket) => void;
349
+ reject: (error: Error) => void;
350
+ host: string;
351
+ port: number;
352
+ timestamp: number;
353
+ }> = [];
354
+
355
+ private connectionHeap: BinaryHeap<IConnectionEntry>;
356
+ private metricsCollector: ConnectionMetrics;
357
+
358
+ constructor(options: IConnectionPoolOptions) {
359
+ super();
360
+
361
+ // Priority: least recently used connections first
362
+ this.connectionHeap = new BinaryHeap(
363
+ (a, b) => a.lastUsed - b.lastUsed,
364
+ (item) => item.poolKey
365
+ );
366
+
367
+ this.metricsCollector = new ConnectionMetrics();
368
+ this.startQueueProcessor();
369
+ }
370
+
371
+ private startQueueProcessor(): void {
372
+ // Process queue periodically to handle timeouts and retries
373
+ this.setInterval(() => {
374
+ const now = Date.now();
375
+ const timeout = this.options.connectionQueueTimeout || 30000;
376
+
377
+ // Remove timed out requests
378
+ this.connectionQueue = this.connectionQueue.filter(item => {
379
+ if (now - item.timestamp > timeout) {
380
+ item.reject(new Error(`Connection pool timeout for ${item.host}:${item.port}`));
381
+ this.metricsCollector.recordTimeout();
382
+ return false;
383
+ }
384
+ return true;
385
+ });
386
+
387
+ // Try to fulfill queued requests
388
+ this.processQueue();
389
+ }, 1000);
390
+ }
391
+
392
+ private processQueue(): void {
393
+ if (this.connectionQueue.length === 0) return;
394
+
395
+ // Group by destination
396
+ const grouped = new Map<string, typeof this.connectionQueue>();
397
+
398
+ for (const item of this.connectionQueue) {
399
+ const key = `${item.host}:${item.port}`;
400
+ if (!grouped.has(key)) grouped.set(key, []);
401
+ grouped.get(key)!.push(item);
72
402
  }
73
403
 
74
- try {
75
- // Simply call the handler with the socket
76
- const result = action.socketHandler(socket);
404
+ // Try to fulfill requests for each destination
405
+ for (const [poolKey, requests] of grouped) {
406
+ const available = this.connectionHeap.extractIf(
407
+ conn => conn.poolKey === poolKey && conn.isIdle && !conn.socket.destroyed
408
+ );
77
409
 
78
- // If it returns a promise, handle errors
79
- if (result instanceof Promise) {
80
- result.catch(error => {
81
- logger.error('Socket handler error:', error);
82
- if (!socket.destroyed) {
83
- socket.destroy();
84
- }
85
- });
410
+ if (available) {
411
+ const request = requests.shift()!;
412
+ this.connectionQueue = this.connectionQueue.filter(item => item !== request);
413
+
414
+ available.isIdle = false;
415
+ available.lastUsed = Date.now();
416
+ request.resolve(available.socket);
417
+
418
+ this.metricsCollector.recordReuse();
86
419
  }
87
- } catch (error) {
88
- logger.error('Socket handler error:', error);
89
- if (!socket.destroyed) {
90
- socket.destroy();
420
+ }
421
+ }
422
+
423
+ async getConnection(host: string, port: number): Promise<net.Socket> {
424
+ const poolKey = `${host}:${port}`;
425
+
426
+ // Try to get existing connection
427
+ let connection = this.connectionHeap.extractIf(
428
+ conn => conn.poolKey === poolKey && conn.isIdle && !conn.socket.destroyed
429
+ );
430
+
431
+ if (connection) {
432
+ connection.isIdle = false;
433
+ connection.lastUsed = Date.now();
434
+ this.metricsCollector.recordReuse();
435
+
436
+ // Validate connection is still alive
437
+ if (await this.validateConnection(connection.socket)) {
438
+ return connection.socket;
91
439
  }
440
+
441
+ // Connection is dead, try another
442
+ connection.socket.destroy();
443
+ return this.getConnection(host, port);
92
444
  }
445
+
446
+ // Check pool size
447
+ const poolSize = this.connectionHeap.sizeFor(poolKey);
448
+ if (poolSize < this.options.connectionPoolSize) {
449
+ return this.createConnection(host, port);
450
+ }
451
+
452
+ // Queue the request
453
+ return this.queueConnectionRequest(host, port);
454
+ }
455
+
456
+ private async validateConnection(socket: net.Socket): Promise<boolean> {
457
+ return new Promise((resolve) => {
458
+ if (socket.destroyed || !socket.readable || !socket.writable) {
459
+ resolve(false);
460
+ return;
461
+ }
462
+
463
+ // Try to write a TCP keepalive probe
464
+ const originalWrite = socket.write;
465
+ let writeError = false;
466
+
467
+ socket.write = function(data: any, encoding?: any, cb?: any) {
468
+ writeError = true;
469
+ return false;
470
+ };
471
+
472
+ socket.setNoDelay(true);
473
+ socket.setNoDelay(false);
474
+
475
+ socket.write = originalWrite;
476
+
477
+ resolve(!writeError);
478
+ });
479
+ }
480
+
481
+ returnConnection(socket: net.Socket, host: string, port: number): void {
482
+ const poolKey = `${host}:${port}`;
93
483
 
94
- return; // Done - user has control now
484
+ // Check for queued requests first
485
+ const queuedIndex = this.connectionQueue.findIndex(
486
+ item => item.host === host && item.port === port
487
+ );
488
+
489
+ if (queuedIndex >= 0) {
490
+ const queued = this.connectionQueue.splice(queuedIndex, 1)[0];
491
+ queued.resolve(socket);
492
+ this.metricsCollector.recordDirectHandoff();
493
+ return;
494
+ }
495
+
496
+ // Return to pool
497
+ this.connectionHeap.insert({
498
+ socket,
499
+ poolKey,
500
+ lastUsed: Date.now(),
501
+ isIdle: true,
502
+ created: Date.now()
503
+ });
504
+ }
505
+
506
+ getMetrics(): IConnectionPoolMetrics {
507
+ return {
508
+ ...this.metricsCollector.getMetrics(),
509
+ poolSize: this.connectionHeap.size(),
510
+ queueLength: this.connectionQueue.length
511
+ };
95
512
  }
96
-
97
- // ... rest of existing action handling
98
513
  }
99
514
  ```
100
515
 
101
- ---
516
+ ## Phase 3: Performance Optimizations (Week 3)
102
517
 
103
- ## Phase 3: Optional Context (If Needed)
518
+ ### 3.1 JSON Operations Optimization
104
519
 
105
- If users need more info, we can optionally pass a minimal context as a second parameter:
520
+ **Issue**: Frequent JSON.stringify for cache keys
106
521
 
522
+ **Solution**:
107
523
  ```typescript
108
- export type TSocketHandler = (
109
- socket: net.Socket,
110
- context?: {
111
- route: IRouteConfig;
112
- clientIp: string;
113
- localPort: number;
114
- }
115
- ) => void | Promise<void>;
524
+ // Create ts/core/utils/hash-utils.ts
525
+ import * as crypto from 'crypto';
526
+
527
+ export class HashUtils {
528
+ private static readonly objectCache = new WeakMap<object, string>();
529
+
530
+ static hashObject(obj: any): string {
531
+ // Check cache first
532
+ if (typeof obj === 'object' && obj !== null) {
533
+ const cached = this.objectCache.get(obj);
534
+ if (cached) return cached;
535
+ }
536
+
537
+ // Create stable string representation
538
+ const str = this.stableStringify(obj);
539
+ const hash = crypto.createHash('sha256').update(str).digest('hex').slice(0, 16);
540
+
541
+ // Cache if object
542
+ if (typeof obj === 'object' && obj !== null) {
543
+ this.objectCache.set(obj, hash);
544
+ }
545
+
546
+ return hash;
547
+ }
548
+
549
+ private static stableStringify(obj: any): string {
550
+ if (obj === null || typeof obj !== 'object') {
551
+ return JSON.stringify(obj);
552
+ }
553
+
554
+ if (Array.isArray(obj)) {
555
+ return '[' + obj.map(item => this.stableStringify(item)).join(',') + ']';
556
+ }
557
+
558
+ const keys = Object.keys(obj).sort();
559
+ const pairs = keys.map(key => `"${key}":${this.stableStringify(obj[key])}`);
560
+ return '{' + pairs.join(',') + '}';
561
+ }
562
+ }
563
+
564
+ // Update function-cache.ts
565
+ private computeContextHash(context: IRouteContext): string {
566
+ return HashUtils.hashObject({
567
+ domain: context.domain,
568
+ path: context.path,
569
+ clientIp: context.clientIp
570
+ });
571
+ }
116
572
  ```
117
573
 
118
- Usage:
574
+ ### 3.2 Worker Thread Integration
575
+
576
+ **Issue**: CPU-intensive operations blocking event loop
577
+
578
+ **Solution Architecture**:
119
579
  ```typescript
120
- socketHandler: (socket, context) => {
121
- console.log(`Connection from ${context.clientIp} to port ${context.localPort}`);
122
- // Handle socket...
580
+ // Create ts/core/workers/worker-pool.ts
581
+ import { Worker } from 'worker_threads';
582
+
583
+ export class WorkerPool {
584
+ private workers: Worker[] = [];
585
+ private queue: Array<{
586
+ task: any;
587
+ resolve: Function;
588
+ reject: Function;
589
+ }> = [];
590
+ private busyWorkers = new Set<Worker>();
591
+
592
+ constructor(
593
+ private workerScript: string,
594
+ private poolSize: number = 4
595
+ ) {
596
+ this.initializeWorkers();
597
+ }
598
+
599
+ async execute<T>(task: any): Promise<T> {
600
+ const worker = await this.getAvailableWorker();
601
+
602
+ return new Promise((resolve, reject) => {
603
+ const messageHandler = (result: any) => {
604
+ worker.off('message', messageHandler);
605
+ worker.off('error', errorHandler);
606
+ this.releaseWorker(worker);
607
+ resolve(result);
608
+ };
609
+
610
+ const errorHandler = (error: Error) => {
611
+ worker.off('message', messageHandler);
612
+ worker.off('error', errorHandler);
613
+ this.releaseWorker(worker);
614
+ reject(error);
615
+ };
616
+
617
+ worker.on('message', messageHandler);
618
+ worker.on('error', errorHandler);
619
+ worker.postMessage(task);
620
+ });
621
+ }
123
622
  }
124
- ```
125
623
 
126
- ---
624
+ // Create ts/core/workers/nftables-worker.ts
625
+ import { parentPort } from 'worker_threads';
626
+ import { exec } from 'child_process';
627
+ import { promisify } from 'util';
127
628
 
128
- ## Phase 4: Helper Utilities (Optional)
629
+ const execAsync = promisify(exec);
129
630
 
130
- ### 4.1 Common Patterns
131
- **File:** `ts/proxies/smart-proxy/utils/route-helpers.ts`
631
+ parentPort?.on('message', async (task) => {
632
+ try {
633
+ const result = await execAsync(task.command);
634
+ parentPort?.postMessage({ success: true, result });
635
+ } catch (error) {
636
+ parentPort?.postMessage({ success: false, error: error.message });
637
+ }
638
+ });
639
+ ```
640
+
641
+ ## Phase 4: Monitoring & Metrics (Week 4)
642
+
643
+ ### 4.1 Event Loop Monitoring
132
644
 
133
645
  ```typescript
134
- // Simple helper to create socket handler routes
135
- export function createSocketHandlerRoute(
136
- domains: string | string[],
137
- ports: TPortRange,
138
- handler: TSocketHandler,
139
- options?: { name?: string; priority?: number }
140
- ): IRouteConfig {
141
- return {
142
- name: options?.name || 'socket-handler-route',
143
- priority: options?.priority || 50,
144
- match: { domains, ports },
145
- action: {
146
- type: 'socket-handler',
147
- socketHandler: handler
148
- }
646
+ // Create ts/core/monitoring/performance-monitor.ts
647
+ export class PerformanceMonitor extends LifecycleComponent {
648
+ private metrics = {
649
+ eventLoopLag: [] as number[],
650
+ activeConnections: 0,
651
+ memoryUsage: {} as NodeJS.MemoryUsage,
652
+ cpuUsage: {} as NodeJS.CpuUsage
149
653
  };
654
+
655
+ start() {
656
+ // Monitor event loop lag
657
+ let lastCheck = process.hrtime.bigint();
658
+
659
+ this.setInterval(() => {
660
+ const now = process.hrtime.bigint();
661
+ const expectedInterval = 100n * 1000000n; // 100ms in nanoseconds
662
+ const actualInterval = now - lastCheck;
663
+ const lag = Number(actualInterval - expectedInterval) / 1000000; // Convert to ms
664
+
665
+ this.metrics.eventLoopLag.push(lag);
666
+ if (this.metrics.eventLoopLag.length > 100) {
667
+ this.metrics.eventLoopLag.shift();
668
+ }
669
+
670
+ lastCheck = now;
671
+ }, 100);
672
+
673
+ // Monitor system resources
674
+ this.setInterval(() => {
675
+ this.metrics.memoryUsage = process.memoryUsage();
676
+ this.metrics.cpuUsage = process.cpuUsage();
677
+ }, 5000);
678
+ }
679
+
680
+ getMetrics() {
681
+ const avgLag = this.metrics.eventLoopLag.reduce((a, b) => a + b, 0)
682
+ / this.metrics.eventLoopLag.length;
683
+
684
+ return {
685
+ eventLoopLag: {
686
+ current: this.metrics.eventLoopLag[this.metrics.eventLoopLag.length - 1],
687
+ average: avgLag,
688
+ max: Math.max(...this.metrics.eventLoopLag)
689
+ },
690
+ memory: this.metrics.memoryUsage,
691
+ cpu: this.metrics.cpuUsage,
692
+ activeConnections: this.metrics.activeConnections
693
+ };
694
+ }
150
695
  }
696
+ ```
697
+
698
+ ## Testing Strategy
699
+
700
+ ### Unit Tests
701
+ 1. Create tests for each new utility class
702
+ 2. Mock filesystem and network operations
703
+ 3. Test error scenarios and edge cases
704
+
705
+ ### Integration Tests
706
+ 1. Test async migration with real filesystem
707
+ 2. Verify timer cleanup on shutdown
708
+ 3. Test connection pool under load
151
709
 
152
- // Pre-built handlers for common cases
153
- export const SocketHandlers = {
154
- // Simple echo server
155
- echo: (socket: net.Socket) => {
156
- socket.on('data', data => socket.write(data));
157
- },
710
+ ### Performance Tests
711
+ ```typescript
712
+ // Create test/performance/event-loop-test.ts
713
+ import { tap, expect } from '@git.zone/tstest/tapbundle';
714
+
715
+ tap.test('should not block event loop', async () => {
716
+ const intervals: number[] = [];
717
+ let lastTime = Date.now();
158
718
 
159
- // TCP proxy
160
- proxy: (targetHost: string, targetPort: number) => (socket: net.Socket) => {
161
- const target = net.connect(targetPort, targetHost);
162
- socket.pipe(target);
163
- target.pipe(socket);
164
- socket.on('close', () => target.destroy());
165
- target.on('close', () => socket.destroy());
166
- },
719
+ const timer = setInterval(() => {
720
+ const now = Date.now();
721
+ intervals.push(now - lastTime);
722
+ lastTime = now;
723
+ }, 10);
724
+
725
+ // Run operations that might block
726
+ await runPotentiallyBlockingOperation();
727
+
728
+ clearInterval(timer);
167
729
 
168
- // Line-based protocol
169
- lineProtocol: (handler: (line: string, socket: net.Socket) => void) => (socket: net.Socket) => {
170
- let buffer = '';
171
- socket.on('data', (data) => {
172
- buffer += data.toString();
173
- const lines = buffer.split('\n');
174
- buffer = lines.pop() || '';
175
- lines.forEach(line => handler(line, socket));
176
- });
730
+ // Check that no interval exceeded 50ms (allowing some tolerance)
731
+ const maxInterval = Math.max(...intervals);
732
+ expect(maxInterval).toBeLessThan(50);
733
+ });
734
+ ```
735
+
736
+ ## Migration Timeline
737
+
738
+ ### Week 1: Critical Fixes
739
+ - Day 1-2: Fix busy wait loop
740
+ - Day 3-4: Convert critical sync operations
741
+ - Day 5: Testing and validation
742
+
743
+ ### Week 2: Resource Management
744
+ - Day 1-2: Implement LifecycleComponent
745
+ - Day 3-4: Migrate components
746
+ - Day 5: Connection pool enhancement
747
+
748
+ ### Week 3: Optimizations
749
+ - Day 1-2: JSON operation optimization
750
+ - Day 3-4: Worker thread integration
751
+ - Day 5: Performance testing
752
+
753
+ ### Week 4: Monitoring & Polish
754
+ - Day 1-2: Performance monitoring
755
+ - Day 3-4: Load testing
756
+ - Day 5: Documentation and release
757
+
758
+ ## Error Handling Strategy
759
+
760
+ ### Graceful Degradation
761
+ ```typescript
762
+ // Create ts/core/utils/error-handler.ts
763
+ export class ErrorHandler {
764
+ private static errorCounts = new Map<string, number>();
765
+ private static circuitBreakers = new Map<string, CircuitBreaker>();
766
+
767
+ static async withFallback<T>(
768
+ operation: () => Promise<T>,
769
+ fallback: () => Promise<T>,
770
+ context: string
771
+ ): Promise<T> {
772
+ const breaker = this.getCircuitBreaker(context);
773
+
774
+ if (breaker.isOpen()) {
775
+ return fallback();
776
+ }
777
+
778
+ try {
779
+ const result = await operation();
780
+ breaker.recordSuccess();
781
+ return result;
782
+ } catch (error) {
783
+ breaker.recordFailure();
784
+ this.recordError(context, error);
785
+
786
+ if (breaker.isOpen()) {
787
+ logger.warn(`Circuit breaker opened for ${context}`);
788
+ }
789
+
790
+ return fallback();
791
+ }
177
792
  }
178
- };
793
+
794
+ private static getCircuitBreaker(context: string): CircuitBreaker {
795
+ if (!this.circuitBreakers.has(context)) {
796
+ this.circuitBreakers.set(context, new CircuitBreaker({
797
+ failureThreshold: 5,
798
+ resetTimeout: 60000
799
+ }));
800
+ }
801
+ return this.circuitBreakers.get(context)!;
802
+ }
803
+ }
804
+
805
+ // Usage example in Certificate Manager
806
+ async getCertificate(domain: string): Promise<CertificateInfo | null> {
807
+ return ErrorHandler.withFallback(
808
+ // Try async operation
809
+ async () => {
810
+ await this.initialize();
811
+ return this.loadCertificateAsync(domain);
812
+ },
813
+ // Fallback to sync if needed
814
+ async () => {
815
+ logger.warn(`Falling back to sync certificate load for ${domain}`);
816
+ return this.loadCertificateSync(domain);
817
+ },
818
+ 'certificate-load'
819
+ );
820
+ }
179
821
  ```
180
822
 
181
- ---
823
+ ## Backward Compatibility
182
824
 
183
- ## Usage Examples
825
+ ### API Preservation
826
+ 1. **Maintain existing interfaces** - All public APIs remain unchanged
827
+ 2. **Progressive enhancement** - New features are opt-in via configuration
828
+ 3. **Sync method wrappers** - Provide sync-looking APIs that use async internally
184
829
 
185
- ### Example 1: Custom Protocol
186
830
  ```typescript
187
- {
188
- name: 'custom-protocol',
189
- match: { ports: 9000 },
190
- action: {
191
- type: 'socket-handler',
192
- socketHandler: (socket) => {
193
- socket.write('READY\n');
194
- socket.on('data', (data) => {
195
- const cmd = data.toString().trim();
196
- if (cmd === 'PING') socket.write('PONG\n');
197
- else if (cmd === 'QUIT') socket.end();
198
- else socket.write('ERROR: Unknown command\n');
199
- });
831
+ // Example: Maintaining backward compatibility
832
+ export class CertStore {
833
+ // Old sync API (deprecated but maintained)
834
+ getCertificateSync(domain: string): ICertificateInfo | null {
835
+ console.warn('getCertificateSync is deprecated, use getCertificate');
836
+ return this.syncFallbackGetCertificate(domain);
837
+ }
838
+
839
+ // New async API
840
+ async getCertificate(domain: string): Promise<ICertificateInfo | null> {
841
+ return this.asyncGetCertificate(domain);
842
+ }
843
+
844
+ // Smart detection for gradual migration
845
+ getCertificateAuto(domain: string, callback?: (err: Error | null, cert: ICertificateInfo | null) => void) {
846
+ if (callback) {
847
+ // Callback style for compatibility
848
+ this.getCertificate(domain)
849
+ .then(cert => callback(null, cert))
850
+ .catch(err => callback(err, null));
851
+ } else {
852
+ // Return promise for modern usage
853
+ return this.getCertificate(domain);
200
854
  }
201
855
  }
202
856
  }
203
857
  ```
204
858
 
205
- ### Example 2: Simple TCP Proxy
859
+ ### Configuration Compatibility
206
860
  ```typescript
207
- {
208
- name: 'tcp-proxy',
209
- match: { ports: 8080, domains: 'proxy.example.com' },
210
- action: {
211
- type: 'socket-handler',
212
- socketHandler: SocketHandlers.proxy('backend.local', 3000)
861
+ // Support both old and new configuration formats
862
+ interface SmartProxyOptions {
863
+ // Old options (maintained)
864
+ preserveSourceIP?: boolean;
865
+ defaultAllowedIPs?: string[];
866
+
867
+ // New performance options (added)
868
+ performance?: {
869
+ asyncFilesystem?: boolean;
870
+ enhancedConnectionPool?: boolean;
871
+ workerThreads?: boolean;
872
+ };
873
+ }
874
+ ```
875
+
876
+ ## Monitoring Dashboard
877
+
878
+ ### Real-time Metrics Visualization
879
+ ```typescript
880
+ // Create ts/core/monitoring/dashboard-server.ts
881
+ export class MonitoringDashboard {
882
+ private httpServer: http.Server;
883
+ private wsServer: WebSocket.Server;
884
+ private metricsHistory: MetricsHistory;
885
+
886
+ async start(port: number = 9090): Promise<void> {
887
+ this.httpServer = http.createServer(this.handleRequest.bind(this));
888
+ this.wsServer = new WebSocket.Server({ server: this.httpServer });
889
+
890
+ this.wsServer.on('connection', (ws) => {
891
+ // Send current metrics
892
+ ws.send(JSON.stringify({
893
+ type: 'initial',
894
+ data: this.metricsHistory.getLast(100)
895
+ }));
896
+
897
+ // Subscribe to updates
898
+ const interval = setInterval(() => {
899
+ if (ws.readyState === WebSocket.OPEN) {
900
+ ws.send(JSON.stringify({
901
+ type: 'update',
902
+ data: this.performanceMonitor.getMetrics()
903
+ }));
904
+ }
905
+ }, 1000);
906
+
907
+ ws.on('close', () => clearInterval(interval));
908
+ });
909
+
910
+ this.httpServer.listen(port);
911
+ logger.info(`Monitoring dashboard available at http://localhost:${port}`);
912
+ }
913
+
914
+ private handleRequest(req: http.IncomingMessage, res: http.ServerResponse) {
915
+ if (req.url === '/') {
916
+ res.writeHead(200, { 'Content-Type': 'text/html' });
917
+ res.end(this.getDashboardHTML());
918
+ } else if (req.url === '/metrics') {
919
+ res.writeHead(200, { 'Content-Type': 'application/json' });
920
+ res.end(JSON.stringify(this.performanceMonitor.getMetrics()));
921
+ }
922
+ }
923
+
924
+ private getDashboardHTML(): string {
925
+ return `
926
+ <!DOCTYPE html>
927
+ <html>
928
+ <head>
929
+ <title>SmartProxy Performance Monitor</title>
930
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
931
+ <style>
932
+ body { font-family: Arial, sans-serif; padding: 20px; }
933
+ .metric { display: inline-block; margin: 10px; padding: 15px; background: #f0f0f0; }
934
+ .chart-container { width: 45%; display: inline-block; margin: 2%; }
935
+ </style>
936
+ </head>
937
+ <body>
938
+ <h1>SmartProxy Performance Monitor</h1>
939
+
940
+ <div id="metrics">
941
+ <div class="metric">
942
+ <h3>Event Loop Lag</h3>
943
+ <div id="eventLoopLag">--</div>
944
+ </div>
945
+ <div class="metric">
946
+ <h3>Active Connections</h3>
947
+ <div id="activeConnections">--</div>
948
+ </div>
949
+ <div class="metric">
950
+ <h3>Memory Usage</h3>
951
+ <div id="memoryUsage">--</div>
952
+ </div>
953
+ <div class="metric">
954
+ <h3>Connection Pool</h3>
955
+ <div id="connectionPool">--</div>
956
+ </div>
957
+ </div>
958
+
959
+ <div class="chart-container">
960
+ <canvas id="eventLoopChart"></canvas>
961
+ </div>
962
+ <div class="chart-container">
963
+ <canvas id="connectionChart"></canvas>
964
+ </div>
965
+
966
+ <script>
967
+ const ws = new WebSocket('ws://localhost:9090');
968
+ const eventLoopData = [];
969
+ const connectionData = [];
970
+
971
+ // Initialize charts
972
+ const eventLoopChart = new Chart(document.getElementById('eventLoopChart'), {
973
+ type: 'line',
974
+ data: {
975
+ labels: [],
976
+ datasets: [{
977
+ label: 'Event Loop Lag (ms)',
978
+ data: eventLoopData,
979
+ borderColor: 'rgb(255, 99, 132)',
980
+ tension: 0.1
981
+ }]
982
+ },
983
+ options: {
984
+ responsive: true,
985
+ scales: { y: { beginAtZero: true } }
986
+ }
987
+ });
988
+
989
+ ws.onmessage = (event) => {
990
+ const message = JSON.parse(event.data);
991
+ updateMetrics(message.data);
992
+ };
993
+
994
+ function updateMetrics(metrics) {
995
+ document.getElementById('eventLoopLag').textContent =
996
+ metrics.eventLoopLag.current.toFixed(2) + ' ms';
997
+ document.getElementById('activeConnections').textContent =
998
+ metrics.activeConnections;
999
+ document.getElementById('memoryUsage').textContent =
1000
+ (metrics.memory.heapUsed / 1024 / 1024).toFixed(2) + ' MB';
1001
+
1002
+ // Update charts
1003
+ const now = new Date().toLocaleTimeString();
1004
+ eventLoopData.push(metrics.eventLoopLag.current);
1005
+ eventLoopChart.data.labels.push(now);
1006
+
1007
+ if (eventLoopData.length > 60) {
1008
+ eventLoopData.shift();
1009
+ eventLoopChart.data.labels.shift();
1010
+ }
1011
+
1012
+ eventLoopChart.update();
1013
+ }
1014
+ </script>
1015
+ </body>
1016
+ </html>
1017
+ `;
213
1018
  }
214
1019
  }
215
1020
  ```
216
1021
 
217
- ### Example 3: WebSocket with Custom Auth
1022
+ ## Performance Benchmarking
1023
+
1024
+ ### Benchmark Suite
218
1025
  ```typescript
219
- {
220
- name: 'custom-websocket',
221
- match: { ports: [80, 443], path: '/ws' },
222
- action: {
223
- type: 'socket-handler',
224
- socketHandler: async (socket) => {
225
- // Read HTTP headers
226
- const headers = await readHttpHeaders(socket);
1026
+ // Create test/performance/benchmark.ts
1027
+ import { SmartProxy } from '../../ts/index.js';
1028
+
1029
+ export class PerformanceBenchmark {
1030
+ async runConnectionStresTest(): Promise<BenchmarkResult> {
1031
+ const proxy = new SmartProxy({ /* config */ });
1032
+ await proxy.start();
1033
+
1034
+ const results = {
1035
+ connectionRate: 0,
1036
+ avgLatency: 0,
1037
+ maxConnections: 0,
1038
+ eventLoopLag: []
1039
+ };
1040
+
1041
+ // Monitor event loop during test
1042
+ const lagSamples: number[] = [];
1043
+ let lastCheck = process.hrtime.bigint();
1044
+ const monitor = setInterval(() => {
1045
+ const now = process.hrtime.bigint();
1046
+ const lag = Number(now - lastCheck - 100_000_000n) / 1_000_000;
1047
+ lagSamples.push(lag);
1048
+ lastCheck = now;
1049
+ }, 100);
1050
+
1051
+ // Create connections with increasing rate
1052
+ const startTime = Date.now();
1053
+ let connectionCount = 0;
1054
+
1055
+ for (let rate = 100; rate <= 10000; rate += 100) {
1056
+ const connections = await this.createConnections(rate);
1057
+ connectionCount += connections.length;
227
1058
 
228
- // Custom auth check
229
- if (!headers.authorization || !validateToken(headers.authorization)) {
230
- socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
231
- socket.end();
232
- return;
1059
+ // Check if performance degrades
1060
+ const avgLag = lagSamples.slice(-10).reduce((a, b) => a + b) / 10;
1061
+ if (avgLag > 50) {
1062
+ results.maxConnections = connectionCount;
1063
+ break;
233
1064
  }
234
1065
 
235
- // Proceed with WebSocket upgrade
236
- const ws = new WebSocket(socket, headers);
237
- // ... handle WebSocket
1066
+ await this.delay(1000);
238
1067
  }
1068
+
1069
+ clearInterval(monitor);
1070
+ await proxy.stop();
1071
+
1072
+ results.connectionRate = connectionCount / ((Date.now() - startTime) / 1000);
1073
+ results.avgLatency = this.calculateAvgLatency();
1074
+ results.eventLoopLag = lagSamples;
1075
+
1076
+ return results;
239
1077
  }
240
1078
  }
241
1079
  ```
242
1080
 
243
- ---
1081
+ ## Documentation Updates
1082
+
1083
+ ### API Documentation
1084
+ 1. **Update JSDoc comments** for all modified methods
1085
+ 2. **Add migration guide** for async transitions
1086
+ 3. **Performance tuning guide** with recommended settings
1087
+
1088
+ ### Example Updates
1089
+ ```typescript
1090
+ /**
1091
+ * Gets a certificate for the specified domain
1092
+ * @param domain - The domain to get certificate for
1093
+ * @returns Promise resolving to certificate info or null
1094
+ * @since v20.0.0 - Now returns Promise (breaking change)
1095
+ * @example
1096
+ * // Old way (deprecated)
1097
+ * const cert = certStore.getCertificateSync('example.com');
1098
+ *
1099
+ * // New way
1100
+ * const cert = await certStore.getCertificate('example.com');
1101
+ *
1102
+ * // Compatibility mode
1103
+ * certStore.getCertificateAuto('example.com', (err, cert) => {
1104
+ * if (err) console.error(err);
1105
+ * else console.log(cert);
1106
+ * });
1107
+ */
1108
+ async getCertificate(domain: string): Promise<ICertificateInfo | null> {
1109
+ // Implementation
1110
+ }
1111
+ ```
1112
+
1113
+ ## Rollback Strategy
1114
+
1115
+ Each phase is designed to be independently deployable with feature flags:
1116
+
1117
+ ```typescript
1118
+ export const PerformanceFlags = {
1119
+ useAsyncFilesystem: process.env.SMARTPROXY_ASYNC_FS !== 'false',
1120
+ useEnhancedPool: process.env.SMARTPROXY_ENHANCED_POOL === 'true',
1121
+ useWorkerThreads: process.env.SMARTPROXY_WORKERS === 'true',
1122
+ enableMonitoring: process.env.SMARTPROXY_MONITORING === 'true'
1123
+ };
1124
+ ```
1125
+
1126
+ ### Gradual Rollout Plan
1127
+ 1. **Development**: All flags enabled
1128
+ 2. **Staging**: Monitor metrics for 1 week
1129
+ 3. **Production**:
1130
+ - 10% traffic → 25% → 50% → 100%
1131
+ - Monitor key metrics at each stage
1132
+ - Rollback if metrics degrade
244
1133
 
245
- ## Benefits of This Approach
1134
+ ## Success Metrics
246
1135
 
247
- 1. **Dead Simple API**: Just pass a function that gets the socket
248
- 2. **No New Classes**: No ForwardingHandler subclass needed
249
- 3. **Minimal Changes**: Only touches type definitions and one handler method
250
- 4. **Full Power**: Users have complete control over the socket
251
- 5. **Backward Compatible**: No changes to existing functionality
252
- 6. **Easy to Test**: Just test the socket handler functions directly
1136
+ 1. **Event Loop Lag**: < 10ms average, < 50ms max
1137
+ 2. **Connection Handling**: Support 10k+ concurrent connections
1138
+ 3. **Memory Usage**: Stable under sustained load
1139
+ 4. **CPU Usage**: Efficient utilization across cores
1140
+ 5. **Response Time**: < 5ms overhead for proxy operations
253
1141
 
254
- ---
1142
+ ## Risk Mitigation
255
1143
 
256
- ## Implementation Steps
1144
+ 1. **Backward Compatibility**: Maintain existing APIs
1145
+ 2. **Gradual Rollout**: Use feature flags
1146
+ 3. **Monitoring**: Track metrics before/after changes
1147
+ 4. **Testing**: Comprehensive test coverage
1148
+ 5. **Documentation**: Update all API docs
257
1149
 
258
- 1. Add `'socket-handler'` to `TRouteActionType` (5 minutes)
259
- 2. Add `socketHandler?: TSocketHandler` to `IRouteAction` (5 minutes)
260
- 3. Add socket-handler case in `RouteConnectionHandler.handleConnection()` (15 minutes)
261
- 4. Add helper functions (optional, 30 minutes)
262
- 5. Write tests (2 hours)
263
- 6. Update documentation (1 hour)
1150
+ ## Summary of Key Optimizations
264
1151
 
265
- **Total implementation time: ~4 hours** (vs 6 weeks for the complex version)
1152
+ ### Immediate Impact (Phase 1)
1153
+ 1. **Eliminate busy wait loop** - Unblocks event loop immediately
1154
+ 2. **Async filesystem operations** - Prevents I/O blocking
1155
+ 3. **Proper error handling** - Graceful degradation with fallbacks
266
1156
 
267
- ---
1157
+ ### Performance Enhancements (Phase 2-3)
1158
+ 1. **Enhanced connection pooling** - O(log n) operations with BinaryHeap
1159
+ 2. **Resource lifecycle management** - Prevents memory leaks
1160
+ 3. **Worker threads** - Offloads CPU-intensive operations
1161
+ 4. **Optimized JSON operations** - Reduces parsing overhead
268
1162
 
269
- ## What We're NOT Doing
1163
+ ### Monitoring & Validation (Phase 4)
1164
+ 1. **Real-time dashboard** - Visual performance monitoring
1165
+ 2. **Event loop lag tracking** - Early warning system
1166
+ 3. **Automated benchmarking** - Regression prevention
270
1167
 
271
- - Creating new ForwardingHandler classes
272
- - ❌ Complex context objects with utils
273
- - ❌ HTTP request handling for socket handlers
274
- - ❌ Complex protocol detection mechanisms
275
- - ❌ Middleware patterns
276
- - ❌ Lifecycle hooks
1168
+ ## Implementation Checklist
277
1169
 
278
- Keep it simple. The user just wants to handle a socket.
1170
+ ### Phase 1: Critical Fixes (Priority: URGENT)
1171
+ - [ ] Create `ts/core/utils/async-utils.ts` with delay function
1172
+ - [ ] Fix busy wait loop in `nftables-proxy.ts`
1173
+ - [ ] Create `ts/core/utils/fs-utils.ts` with AsyncFileSystem class
1174
+ - [ ] Migrate `certificate-manager.ts` to async operations
1175
+ - [ ] Migrate `cert-store.ts` to async operations
1176
+ - [ ] Replace `execSync` with `execAsync` in `nftables-proxy.ts`
1177
+ - [ ] Add comprehensive unit tests for async operations
1178
+ - [ ] Performance test to verify event loop improvements
279
1179
 
280
- ---
1180
+ ### Phase 2: Resource Management
1181
+ - [x] Implement LifecycleComponent base class
1182
+ - [ ] Migrate components to extend LifecycleComponent
1183
+ - [x] Implement BinaryHeap data structure
1184
+ - [x] Create EnhancedConnectionPool with queue support
1185
+ - [x] Add connection validation and health checks
1186
+ - [ ] Implement proper timer cleanup in all components
1187
+ - [ ] Add integration tests for resource management
1188
+ - [x] Clean up legacy code (removed ts/common/, event-utils.ts, event-system.ts)
281
1189
 
282
- ## Success Criteria
1190
+ ### Phase 3: Performance Optimizations
1191
+ - [ ] Implement HashUtils for efficient object hashing
1192
+ - [ ] Create WorkerPool for CPU-intensive operations
1193
+ - [ ] Migrate NFTables operations to worker threads
1194
+ - [ ] Optimize JSON operations with caching
1195
+ - [ ] Add performance benchmarks
283
1196
 
284
- - Users can define a route with `type: 'socket-handler'`
285
- - Users can provide a function that receives the socket
286
- - The function is called when a connection matches the route
287
- - Error handling prevents crashes
288
- - No performance impact on existing routes
289
- - Clean, simple API that's easy to understand
1197
+ ### Phase 4: Monitoring & Polish
1198
+ - [ ] Implement PerformanceMonitor class
1199
+ - [ ] Create monitoring dashboard with WebSocket updates
1200
+ - [ ] Add comprehensive metrics collection
1201
+ - [ ] Document all API changes
1202
+ - [ ] Create migration guide
1203
+ - [ ] Update examples and tutorials
290
1204
 
291
- ---
1205
+ ## Next Steps
292
1206
 
293
- ## Implementation Notes (Completed)
1207
+ 1. **Immediate Action**: Fix the busy wait loop (blocks entire event loop)
1208
+ 2. **Code Review**: Review this plan with the team
1209
+ 3. **Feature Branch**: Create `feature/performance-optimization`
1210
+ 4. **Phase 1 Implementation**: Complete within 1 week
1211
+ 5. **Staging Deployment**: Test with real traffic patterns
1212
+ 6. **Gradual Rollout**: Use feature flags for production
1213
+ 7. **Monitor & Iterate**: Track metrics and adjust as needed
294
1214
 
295
- ### What Was Implemented
296
- 1. **Type Definitions** - Added 'socket-handler' to TRouteActionType and TSocketHandler type
297
- 2. **Route Handler** - Added socket-handler case in RouteConnectionHandler switch statement
298
- 3. **Error Handling** - Both sync and async errors are caught and logged
299
- 4. **Initial Data Handling** - Initial chunks are re-emitted to handler's listeners
300
- 5. **Helper Functions** - Added createSocketHandlerRoute and pre-built handlers (echo, proxy, etc.)
301
- 6. **Full Test Coverage** - All test cases pass including async handlers and error handling
1215
+ ## Expected Outcomes
302
1216
 
303
- ### Key Implementation Details
304
- - Socket handlers require initial data from client to trigger routing (not TLS handshake)
305
- - The handler receives the raw socket after route matching
306
- - Both sync and async handlers are supported
307
- - Errors in handlers terminate the connection gracefully
308
- - Helper utilities provide common patterns (echo server, TCP proxy, line protocol)
1217
+ After implementing all phases:
1218
+ - **10x improvement** in concurrent connection handling
1219
+ - **90% reduction** in event loop blocking
1220
+ - **50% reduction** in memory usage under load
1221
+ - **Zero memory leaks** with proper resource cleanup
1222
+ - **Real-time visibility** into performance metrics
1223
+ - **Graceful degradation** under extreme load
309
1224
 
310
- ### Usage Notes
311
- - Clients must send initial data to trigger the handler (even just a newline)
312
- - The socket is passed directly to the handler function
313
- - Handler has complete control over the socket lifecycle
314
- - No special context object needed - keeps it simple
1225
+ ## Version Plan
315
1226
 
316
- **Total implementation time: ~3 hours**
1227
+ - **v19.6.0**: Phase 1 (Critical fixes) - Backward compatible
1228
+ - **v19.7.0**: Phase 2 (Resource management) - Backward compatible
1229
+ - **v19.8.0**: Phase 3 (Optimizations) - Backward compatible
1230
+ - **v20.0.0**: Phase 4 (Full async) - Breaking changes with migration path