@push.rocks/smartproxy 19.5.4 → 19.5.5

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 (41) 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 +5 -2
  10. package/dist_ts/core/utils/index.js +6 -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/plugins.d.ts +2 -1
  14. package/dist_ts/plugins.js +3 -2
  15. package/dist_ts/proxies/http-proxy/certificate-manager.d.ts +15 -0
  16. package/dist_ts/proxies/http-proxy/certificate-manager.js +49 -2
  17. package/dist_ts/proxies/nftables-proxy/nftables-proxy.d.ts +10 -0
  18. package/dist_ts/proxies/nftables-proxy/nftables-proxy.js +53 -43
  19. package/dist_ts/proxies/smart-proxy/cert-store.js +22 -20
  20. package/dist_ts/proxies/smart-proxy/connection-manager.d.ts +37 -7
  21. package/dist_ts/proxies/smart-proxy/connection-manager.js +257 -180
  22. package/package.json +2 -2
  23. package/readme.hints.md +96 -1
  24. package/readme.plan.md +1135 -221
  25. package/readme.problems.md +167 -83
  26. package/ts/core/utils/async-utils.ts +275 -0
  27. package/ts/core/utils/binary-heap.ts +225 -0
  28. package/ts/core/utils/enhanced-connection-pool.ts +420 -0
  29. package/ts/core/utils/fs-utils.ts +270 -0
  30. package/ts/core/utils/index.ts +5 -2
  31. package/ts/core/utils/lifecycle-component.ts +231 -0
  32. package/ts/plugins.ts +2 -1
  33. package/ts/proxies/http-proxy/certificate-manager.ts +52 -1
  34. package/ts/proxies/nftables-proxy/nftables-proxy.ts +64 -79
  35. package/ts/proxies/smart-proxy/cert-store.ts +26 -20
  36. package/ts/proxies/smart-proxy/connection-manager.ts +291 -189
  37. package/readme.plan2.md +0 -764
  38. package/ts/common/eventUtils.ts +0 -34
  39. package/ts/common/types.ts +0 -91
  40. package/ts/core/utils/event-system.ts +0 -376
  41. package/ts/core/utils/event-utils.ts +0 -25
@@ -2,16 +2,30 @@ import * as plugins from '../../plugins.js';
2
2
  import { SecurityManager } from './security-manager.js';
3
3
  import { TimeoutManager } from './timeout-manager.js';
4
4
  import { logger } from '../../core/utils/logger.js';
5
+ import { LifecycleComponent } from '../../core/utils/lifecycle-component.js';
5
6
  /**
6
- * Manages connection lifecycle, tracking, and cleanup
7
+ * Manages connection lifecycle, tracking, and cleanup with performance optimizations
7
8
  */
8
- export class ConnectionManager {
9
+ export class ConnectionManager extends LifecycleComponent {
9
10
  constructor(settings, securityManager, timeoutManager) {
11
+ super();
10
12
  this.settings = settings;
11
13
  this.securityManager = securityManager;
12
14
  this.timeoutManager = timeoutManager;
13
15
  this.connectionRecords = new Map();
14
16
  this.terminationStats = { incoming: {}, outgoing: {} };
17
+ // Performance optimization: Track connections needing inactivity check
18
+ this.nextInactivityCheck = new Map();
19
+ this.cleanupBatchSize = 100;
20
+ // Cleanup queue for batched processing
21
+ this.cleanupQueue = new Set();
22
+ this.cleanupTimer = null;
23
+ // Set reasonable defaults for connection limits
24
+ this.maxConnections = settings.defaults?.security?.maxConnections || 10000;
25
+ // Start inactivity check timer if not disabled
26
+ if (!settings.disableInactivityCheck) {
27
+ this.startInactivityCheckTimer();
28
+ }
15
29
  }
16
30
  /**
17
31
  * Generate a unique connection ID
@@ -24,15 +38,26 @@ export class ConnectionManager {
24
38
  * Create and track a new connection
25
39
  */
26
40
  createConnection(socket) {
41
+ // Enforce connection limit
42
+ if (this.connectionRecords.size >= this.maxConnections) {
43
+ logger.log('warn', `Connection limit reached (${this.maxConnections}). Rejecting new connection.`, {
44
+ currentConnections: this.connectionRecords.size,
45
+ maxConnections: this.maxConnections,
46
+ component: 'connection-manager'
47
+ });
48
+ socket.destroy();
49
+ return null;
50
+ }
27
51
  const connectionId = this.generateConnectionId();
28
52
  const remoteIP = socket.remoteAddress || '';
29
53
  const localPort = socket.localPort || 0;
54
+ const now = Date.now();
30
55
  const record = {
31
56
  id: connectionId,
32
57
  incoming: socket,
33
58
  outgoing: null,
34
- incomingStartTime: Date.now(),
35
- lastActivity: Date.now(),
59
+ incomingStartTime: now,
60
+ lastActivity: now,
36
61
  connectionClosed: false,
37
62
  pendingData: [],
38
63
  pendingDataSize: 0,
@@ -59,6 +84,38 @@ export class ConnectionManager {
59
84
  trackConnection(connectionId, record) {
60
85
  this.connectionRecords.set(connectionId, record);
61
86
  this.securityManager.trackConnectionByIP(record.remoteIP, connectionId);
87
+ // Schedule inactivity check
88
+ if (!this.settings.disableInactivityCheck) {
89
+ this.scheduleInactivityCheck(connectionId, record);
90
+ }
91
+ }
92
+ /**
93
+ * Schedule next inactivity check for a connection
94
+ */
95
+ scheduleInactivityCheck(connectionId, record) {
96
+ let timeout = this.settings.inactivityTimeout;
97
+ if (record.hasKeepAlive) {
98
+ if (this.settings.keepAliveTreatment === 'immortal') {
99
+ // Don't schedule check for immortal connections
100
+ return;
101
+ }
102
+ else if (this.settings.keepAliveTreatment === 'extended') {
103
+ const multiplier = this.settings.keepAliveInactivityMultiplier || 6;
104
+ timeout = timeout * multiplier;
105
+ }
106
+ }
107
+ const checkTime = Date.now() + timeout;
108
+ this.nextInactivityCheck.set(connectionId, checkTime);
109
+ }
110
+ /**
111
+ * Start the inactivity check timer
112
+ */
113
+ startInactivityCheckTimer() {
114
+ // Check every 30 seconds for connections that need inactivity check
115
+ this.setInterval(() => {
116
+ this.performOptimizedInactivityCheck();
117
+ }, 30000);
118
+ // Note: LifecycleComponent's setInterval already calls unref()
62
119
  }
63
120
  /**
64
121
  * Get a connection by ID
@@ -83,14 +140,58 @@ export class ConnectionManager {
83
140
  */
84
141
  initiateCleanupOnce(record, reason = 'normal') {
85
142
  if (this.settings.enableDetailedLogging) {
86
- logger.log('info', `Connection cleanup initiated`, { connectionId: record.id, remoteIP: record.remoteIP, reason, component: 'connection-manager' });
143
+ logger.log('info', `Connection cleanup initiated`, {
144
+ connectionId: record.id,
145
+ remoteIP: record.remoteIP,
146
+ reason,
147
+ component: 'connection-manager'
148
+ });
87
149
  }
88
- if (record.incomingTerminationReason === null ||
89
- record.incomingTerminationReason === undefined) {
150
+ if (record.incomingTerminationReason == null) {
90
151
  record.incomingTerminationReason = reason;
91
152
  this.incrementTerminationStat('incoming', reason);
92
153
  }
93
- this.cleanupConnection(record, reason);
154
+ // Add to cleanup queue for batched processing
155
+ this.queueCleanup(record.id);
156
+ }
157
+ /**
158
+ * Queue a connection for cleanup
159
+ */
160
+ queueCleanup(connectionId) {
161
+ this.cleanupQueue.add(connectionId);
162
+ // Process immediately if queue is getting large
163
+ if (this.cleanupQueue.size >= this.cleanupBatchSize) {
164
+ this.processCleanupQueue();
165
+ }
166
+ else if (!this.cleanupTimer) {
167
+ // Otherwise, schedule batch processing
168
+ this.cleanupTimer = this.setTimeout(() => {
169
+ this.processCleanupQueue();
170
+ }, 100);
171
+ }
172
+ }
173
+ /**
174
+ * Process the cleanup queue in batches
175
+ */
176
+ processCleanupQueue() {
177
+ if (this.cleanupTimer) {
178
+ this.clearTimeout(this.cleanupTimer);
179
+ this.cleanupTimer = null;
180
+ }
181
+ const toCleanup = Array.from(this.cleanupQueue).slice(0, this.cleanupBatchSize);
182
+ this.cleanupQueue.clear();
183
+ for (const connectionId of toCleanup) {
184
+ const record = this.connectionRecords.get(connectionId);
185
+ if (record) {
186
+ this.cleanupConnection(record, record.incomingTerminationReason || 'normal');
187
+ }
188
+ }
189
+ // If there are more in queue, schedule next batch
190
+ if (this.cleanupQueue.size > 0) {
191
+ this.cleanupTimer = this.setTimeout(() => {
192
+ this.processCleanupQueue();
193
+ }, 10);
194
+ }
94
195
  }
95
196
  /**
96
197
  * Clean up a connection record
@@ -98,33 +199,47 @@ export class ConnectionManager {
98
199
  cleanupConnection(record, reason = 'normal') {
99
200
  if (!record.connectionClosed) {
100
201
  record.connectionClosed = true;
202
+ // Remove from inactivity check
203
+ this.nextInactivityCheck.delete(record.id);
101
204
  // Track connection termination
102
205
  this.securityManager.removeConnectionByIP(record.remoteIP, record.id);
103
206
  if (record.cleanupTimer) {
104
207
  clearTimeout(record.cleanupTimer);
105
208
  record.cleanupTimer = undefined;
106
209
  }
107
- // Detailed logging data
210
+ // Calculate metrics once
108
211
  const duration = Date.now() - record.incomingStartTime;
109
- const bytesReceived = record.bytesReceived;
110
- const bytesSent = record.bytesSent;
212
+ const logData = {
213
+ connectionId: record.id,
214
+ remoteIP: record.remoteIP,
215
+ localPort: record.localPort,
216
+ reason,
217
+ duration: plugins.prettyMs(duration),
218
+ bytes: { in: record.bytesReceived, out: record.bytesSent },
219
+ tls: record.isTLS,
220
+ keepAlive: record.hasKeepAlive,
221
+ usingNetworkProxy: record.usingNetworkProxy,
222
+ domainSwitches: record.domainSwitches || 0,
223
+ component: 'connection-manager'
224
+ };
111
225
  // Remove all data handlers to make sure we clean up properly
112
226
  if (record.incoming) {
113
227
  try {
114
- // Remove our safe data handler
115
228
  record.incoming.removeAllListeners('data');
116
- // Reset the handler references
117
229
  record.renegotiationHandler = undefined;
118
230
  }
119
231
  catch (err) {
120
- logger.log('error', `Error removing data handlers for connection ${record.id}: ${err}`, { connectionId: record.id, error: err, component: 'connection-manager' });
232
+ logger.log('error', `Error removing data handlers: ${err}`, {
233
+ connectionId: record.id,
234
+ error: err,
235
+ component: 'connection-manager'
236
+ });
121
237
  }
122
238
  }
123
- // Handle incoming socket
124
- this.cleanupSocket(record, 'incoming', record.incoming);
125
- // Handle outgoing socket
239
+ // Handle socket cleanup without delay
240
+ this.cleanupSocketImmediate(record, 'incoming', record.incoming);
126
241
  if (record.outgoing) {
127
- this.cleanupSocket(record, 'outgoing', record.outgoing);
242
+ this.cleanupSocketImmediate(record, 'outgoing', record.outgoing);
128
243
  }
129
244
  // Clear pendingData to avoid memory leaks
130
245
  record.pendingData = [];
@@ -133,26 +248,11 @@ export class ConnectionManager {
133
248
  this.connectionRecords.delete(record.id);
134
249
  // Log connection details
135
250
  if (this.settings.enableDetailedLogging) {
136
- logger.log('info', `Connection from ${record.remoteIP} on port ${record.localPort} terminated (${reason}). ` +
137
- `Duration: ${plugins.prettyMs(duration)}, Bytes IN: ${bytesReceived}, OUT: ${bytesSent}, ` +
138
- `TLS: ${record.isTLS ? 'Yes' : 'No'}, Keep-Alive: ${record.hasKeepAlive ? 'Yes' : 'No'}` +
139
- `${record.usingNetworkProxy ? ', Using NetworkProxy' : ''}` +
140
- `${record.domainSwitches ? `, Domain switches: ${record.domainSwitches}` : ''}`, {
141
- connectionId: record.id,
142
- remoteIP: record.remoteIP,
143
- localPort: record.localPort,
144
- reason,
145
- duration: plugins.prettyMs(duration),
146
- bytes: { in: bytesReceived, out: bytesSent },
147
- tls: record.isTLS,
148
- keepAlive: record.hasKeepAlive,
149
- usingNetworkProxy: record.usingNetworkProxy,
150
- domainSwitches: record.domainSwitches || 0,
151
- component: 'connection-manager'
152
- });
251
+ logger.log('info', `Connection terminated: ${record.remoteIP}:${record.localPort} (${reason}) - ` +
252
+ `${plugins.prettyMs(duration)}, IN: ${record.bytesReceived}B, OUT: ${record.bytesSent}B`, logData);
153
253
  }
154
254
  else {
155
- logger.log('info', `Connection from ${record.remoteIP} terminated (${reason}). Active connections: ${this.connectionRecords.size}`, {
255
+ logger.log('info', `Connection terminated: ${record.remoteIP} (${reason}). Active: ${this.connectionRecords.size}`, {
156
256
  connectionId: record.id,
157
257
  remoteIP: record.remoteIP,
158
258
  reason,
@@ -163,39 +263,21 @@ export class ConnectionManager {
163
263
  }
164
264
  }
165
265
  /**
166
- * Helper method to clean up a socket
266
+ * Helper method to clean up a socket immediately
167
267
  */
168
- cleanupSocket(record, side, socket) {
268
+ cleanupSocketImmediate(record, side, socket) {
169
269
  try {
170
270
  if (!socket.destroyed) {
171
- // Try graceful shutdown first, then force destroy after a short timeout
172
- socket.end();
173
- const socketTimeout = setTimeout(() => {
174
- try {
175
- if (!socket.destroyed) {
176
- socket.destroy();
177
- }
178
- }
179
- catch (err) {
180
- logger.log('error', `Error destroying ${side} socket for connection ${record.id}: ${err}`, { connectionId: record.id, side, error: err, component: 'connection-manager' });
181
- }
182
- }, 1000);
183
- // Ensure the timeout doesn't block Node from exiting
184
- if (socketTimeout.unref) {
185
- socketTimeout.unref();
186
- }
271
+ socket.destroy();
187
272
  }
188
273
  }
189
274
  catch (err) {
190
- logger.log('error', `Error closing ${side} socket for connection ${record.id}: ${err}`, { connectionId: record.id, side, error: err, component: 'connection-manager' });
191
- try {
192
- if (!socket.destroyed) {
193
- socket.destroy();
194
- }
195
- }
196
- catch (destroyErr) {
197
- logger.log('error', `Error destroying ${side} socket for connection ${record.id}: ${destroyErr}`, { connectionId: record.id, side, error: destroyErr, component: 'connection-manager' });
198
- }
275
+ logger.log('error', `Error destroying ${side} socket: ${err}`, {
276
+ connectionId: record.id,
277
+ side,
278
+ error: err,
279
+ component: 'connection-manager'
280
+ });
199
281
  }
200
282
  }
201
283
  /**
@@ -208,46 +290,37 @@ export class ConnectionManager {
208
290
  const now = Date.now();
209
291
  const connectionDuration = now - record.incomingStartTime;
210
292
  const lastActivityAge = now - record.lastActivity;
211
- if (code === 'ECONNRESET') {
212
- reason = 'econnreset';
213
- logger.log('warn', `ECONNRESET on ${side} connection from ${record.remoteIP}. Error: ${err.message}. Duration: ${plugins.prettyMs(connectionDuration)}, Last activity: ${plugins.prettyMs(lastActivityAge)}`, {
214
- connectionId: record.id,
215
- side,
216
- remoteIP: record.remoteIP,
217
- error: err.message,
218
- duration: plugins.prettyMs(connectionDuration),
219
- lastActivity: plugins.prettyMs(lastActivityAge),
220
- component: 'connection-manager'
221
- });
293
+ // Update activity tracking
294
+ if (side === 'incoming') {
295
+ record.lastActivity = now;
296
+ this.scheduleInactivityCheck(record.id, record);
222
297
  }
223
- else if (code === 'ETIMEDOUT') {
224
- reason = 'etimedout';
225
- logger.log('warn', `ETIMEDOUT on ${side} connection from ${record.remoteIP}. Error: ${err.message}. Duration: ${plugins.prettyMs(connectionDuration)}, Last activity: ${plugins.prettyMs(lastActivityAge)}`, {
226
- connectionId: record.id,
227
- side,
228
- remoteIP: record.remoteIP,
229
- error: err.message,
230
- duration: plugins.prettyMs(connectionDuration),
231
- lastActivity: plugins.prettyMs(lastActivityAge),
232
- component: 'connection-manager'
233
- });
234
- }
235
- else {
236
- logger.log('error', `Error on ${side} connection from ${record.remoteIP}: ${err.message}. Duration: ${plugins.prettyMs(connectionDuration)}, Last activity: ${plugins.prettyMs(lastActivityAge)}`, {
237
- connectionId: record.id,
238
- side,
239
- remoteIP: record.remoteIP,
240
- error: err.message,
241
- duration: plugins.prettyMs(connectionDuration),
242
- lastActivity: plugins.prettyMs(lastActivityAge),
243
- component: 'connection-manager'
244
- });
298
+ const errorData = {
299
+ connectionId: record.id,
300
+ side,
301
+ remoteIP: record.remoteIP,
302
+ error: err.message,
303
+ duration: plugins.prettyMs(connectionDuration),
304
+ lastActivity: plugins.prettyMs(lastActivityAge),
305
+ component: 'connection-manager'
306
+ };
307
+ switch (code) {
308
+ case 'ECONNRESET':
309
+ reason = 'econnreset';
310
+ logger.log('warn', `ECONNRESET on ${side}: ${record.remoteIP}`, errorData);
311
+ break;
312
+ case 'ETIMEDOUT':
313
+ reason = 'etimedout';
314
+ logger.log('warn', `ETIMEDOUT on ${side}: ${record.remoteIP}`, errorData);
315
+ break;
316
+ default:
317
+ logger.log('error', `Error on ${side}: ${record.remoteIP} - ${err.message}`, errorData);
245
318
  }
246
- if (side === 'incoming' && record.incomingTerminationReason === null) {
319
+ if (side === 'incoming' && record.incomingTerminationReason == null) {
247
320
  record.incomingTerminationReason = reason;
248
321
  this.incrementTerminationStat('incoming', reason);
249
322
  }
250
- else if (side === 'outgoing' && record.outgoingTerminationReason === null) {
323
+ else if (side === 'outgoing' && record.outgoingTerminationReason == null) {
251
324
  record.outgoingTerminationReason = reason;
252
325
  this.incrementTerminationStat('outgoing', reason);
253
326
  }
@@ -267,14 +340,13 @@ export class ConnectionManager {
267
340
  component: 'connection-manager'
268
341
  });
269
342
  }
270
- if (side === 'incoming' && record.incomingTerminationReason === null) {
343
+ if (side === 'incoming' && record.incomingTerminationReason == null) {
271
344
  record.incomingTerminationReason = 'normal';
272
345
  this.incrementTerminationStat('incoming', 'normal');
273
346
  }
274
- else if (side === 'outgoing' && record.outgoingTerminationReason === null) {
347
+ else if (side === 'outgoing' && record.outgoingTerminationReason == null) {
275
348
  record.outgoingTerminationReason = 'normal';
276
349
  this.incrementTerminationStat('outgoing', 'normal');
277
- // Record the time when outgoing socket closed.
278
350
  record.outgoingClosedTime = Date.now();
279
351
  }
280
352
  this.initiateCleanupOnce(record, 'closed_' + side);
@@ -293,18 +365,22 @@ export class ConnectionManager {
293
365
  return this.terminationStats;
294
366
  }
295
367
  /**
296
- * Check for stalled/inactive connections
368
+ * Optimized inactivity check - only checks connections that are due
297
369
  */
298
- performInactivityCheck() {
370
+ performOptimizedInactivityCheck() {
299
371
  const now = Date.now();
300
- const connectionIds = [...this.connectionRecords.keys()];
301
- for (const id of connectionIds) {
302
- const record = this.connectionRecords.get(id);
303
- if (!record)
304
- continue;
305
- // Skip inactivity check if disabled or for immortal keep-alive connections
306
- if (this.settings.disableInactivityCheck ||
307
- (record.hasKeepAlive && this.settings.keepAliveTreatment === 'immortal')) {
372
+ const connectionsToCheck = [];
373
+ // Find connections that need checking
374
+ for (const [connectionId, checkTime] of this.nextInactivityCheck) {
375
+ if (checkTime <= now) {
376
+ connectionsToCheck.push(connectionId);
377
+ }
378
+ }
379
+ // Process only connections that need checking
380
+ for (const connectionId of connectionsToCheck) {
381
+ const record = this.connectionRecords.get(connectionId);
382
+ if (!record || record.connectionClosed) {
383
+ this.nextInactivityCheck.delete(connectionId);
308
384
  continue;
309
385
  }
310
386
  const inactivityTime = now - record.lastActivity;
@@ -314,36 +390,36 @@ export class ConnectionManager {
314
390
  const multiplier = this.settings.keepAliveInactivityMultiplier || 6;
315
391
  effectiveTimeout = effectiveTimeout * multiplier;
316
392
  }
317
- if (inactivityTime > effectiveTimeout && !record.connectionClosed) {
393
+ if (inactivityTime > effectiveTimeout) {
318
394
  // For keep-alive connections, issue a warning first
319
395
  if (record.hasKeepAlive && !record.inactivityWarningIssued) {
320
- logger.log('warn', `Keep-alive connection ${id} from ${record.remoteIP} inactive for ${plugins.prettyMs(inactivityTime)}. Will close in 10 minutes if no activity.`, {
321
- connectionId: id,
396
+ logger.log('warn', `Keep-alive connection inactive: ${record.remoteIP}`, {
397
+ connectionId,
322
398
  remoteIP: record.remoteIP,
323
399
  inactiveFor: plugins.prettyMs(inactivityTime),
324
- closureWarning: '10 minutes',
325
400
  component: 'connection-manager'
326
401
  });
327
- // Set warning flag and add grace period
328
402
  record.inactivityWarningIssued = true;
329
- record.lastActivity = now - (effectiveTimeout - 600000);
403
+ // Reschedule check for 10 minutes later
404
+ this.nextInactivityCheck.set(connectionId, now + 600000);
330
405
  // Try to stimulate activity with a probe packet
331
406
  if (record.outgoing && !record.outgoing.destroyed) {
332
407
  try {
333
408
  record.outgoing.write(Buffer.alloc(0));
334
- if (this.settings.enableDetailedLogging) {
335
- logger.log('info', `Sent probe packet to test keep-alive connection ${id}`, { connectionId: id, component: 'connection-manager' });
336
- }
337
409
  }
338
410
  catch (err) {
339
- logger.log('error', `Error sending probe packet to connection ${id}: ${err}`, { connectionId: id, error: err, component: 'connection-manager' });
411
+ logger.log('error', `Error sending probe packet: ${err}`, {
412
+ connectionId,
413
+ error: err,
414
+ component: 'connection-manager'
415
+ });
340
416
  }
341
417
  }
342
418
  }
343
419
  else {
344
- // For non-keep-alive or after warning, close the connection
345
- logger.log('warn', `Closing inactive connection ${id} from ${record.remoteIP} (inactive for ${plugins.prettyMs(inactivityTime)}, keep-alive: ${record.hasKeepAlive ? 'Yes' : 'No'})`, {
346
- connectionId: id,
420
+ // Close the connection
421
+ logger.log('warn', `Closing inactive connection: ${record.remoteIP}`, {
422
+ connectionId,
347
423
  remoteIP: record.remoteIP,
348
424
  inactiveFor: plugins.prettyMs(inactivityTime),
349
425
  hasKeepAlive: record.hasKeepAlive,
@@ -352,23 +428,17 @@ export class ConnectionManager {
352
428
  this.cleanupConnection(record, 'inactivity');
353
429
  }
354
430
  }
355
- else if (inactivityTime <= effectiveTimeout && record.inactivityWarningIssued) {
356
- // If activity detected after warning, clear the warning
357
- if (this.settings.enableDetailedLogging) {
358
- logger.log('info', `Connection ${id} activity detected after inactivity warning`, {
359
- connectionId: id,
360
- component: 'connection-manager'
361
- });
362
- }
363
- record.inactivityWarningIssued = false;
431
+ else {
432
+ // Reschedule next check
433
+ this.scheduleInactivityCheck(connectionId, record);
364
434
  }
365
435
  // Parity check: if outgoing socket closed and incoming remains active
366
436
  if (record.outgoingClosedTime &&
367
437
  !record.incoming.destroyed &&
368
438
  !record.connectionClosed &&
369
439
  now - record.outgoingClosedTime > 120000) {
370
- logger.log('warn', `Parity check: Connection ${id} from ${record.remoteIP} has incoming socket still active ${plugins.prettyMs(now - record.outgoingClosedTime)} after outgoing socket closed`, {
371
- connectionId: id,
440
+ logger.log('warn', `Parity check failed: ${record.remoteIP}`, {
441
+ connectionId,
372
442
  remoteIP: record.remoteIP,
373
443
  timeElapsed: plugins.prettyMs(now - record.outgoingClosedTime),
374
444
  component: 'connection-manager'
@@ -377,65 +447,72 @@ export class ConnectionManager {
377
447
  }
378
448
  }
379
449
  }
450
+ /**
451
+ * Legacy method for backward compatibility
452
+ */
453
+ performInactivityCheck() {
454
+ this.performOptimizedInactivityCheck();
455
+ }
380
456
  /**
381
457
  * Clear all connections (for shutdown)
382
458
  */
383
- clearConnections() {
384
- // Create a copy of the keys to avoid modification during iteration
385
- const connectionIds = [...this.connectionRecords.keys()];
386
- // First pass: End all connections gracefully
387
- for (const id of connectionIds) {
388
- const record = this.connectionRecords.get(id);
389
- if (record) {
459
+ async clearConnections() {
460
+ // Delegate to LifecycleComponent's cleanup
461
+ await this.cleanup();
462
+ }
463
+ /**
464
+ * Override LifecycleComponent's onCleanup method
465
+ */
466
+ async onCleanup() {
467
+ // Process connections in batches to avoid blocking
468
+ const connections = Array.from(this.connectionRecords.values());
469
+ const batchSize = 100;
470
+ let index = 0;
471
+ const processBatch = () => {
472
+ const batch = connections.slice(index, index + batchSize);
473
+ for (const record of batch) {
390
474
  try {
391
- // Clear any timers
392
475
  if (record.cleanupTimer) {
393
476
  clearTimeout(record.cleanupTimer);
394
477
  record.cleanupTimer = undefined;
395
478
  }
396
- // End sockets gracefully
397
- if (record.incoming && !record.incoming.destroyed) {
398
- record.incoming.end();
479
+ // Immediate destruction
480
+ if (record.incoming) {
481
+ record.incoming.removeAllListeners();
482
+ if (!record.incoming.destroyed) {
483
+ record.incoming.destroy();
484
+ }
399
485
  }
400
- if (record.outgoing && !record.outgoing.destroyed) {
401
- record.outgoing.end();
486
+ if (record.outgoing) {
487
+ record.outgoing.removeAllListeners();
488
+ if (!record.outgoing.destroyed) {
489
+ record.outgoing.destroy();
490
+ }
402
491
  }
403
492
  }
404
493
  catch (err) {
405
- logger.log('error', `Error during graceful end of connection ${id}: ${err}`, { connectionId: id, error: err, component: 'connection-manager' });
494
+ logger.log('error', `Error during connection cleanup: ${err}`, {
495
+ connectionId: record.id,
496
+ error: err,
497
+ component: 'connection-manager'
498
+ });
406
499
  }
407
500
  }
408
- }
409
- // Short delay to allow graceful ends to process
410
- setTimeout(() => {
411
- // Second pass: Force destroy everything
412
- for (const id of connectionIds) {
413
- const record = this.connectionRecords.get(id);
414
- if (record) {
415
- try {
416
- // Remove all listeners to prevent memory leaks
417
- if (record.incoming) {
418
- record.incoming.removeAllListeners();
419
- if (!record.incoming.destroyed) {
420
- record.incoming.destroy();
421
- }
422
- }
423
- if (record.outgoing) {
424
- record.outgoing.removeAllListeners();
425
- if (!record.outgoing.destroyed) {
426
- record.outgoing.destroy();
427
- }
428
- }
429
- }
430
- catch (err) {
431
- logger.log('error', `Error during forced destruction of connection ${id}: ${err}`, { connectionId: id, error: err, component: 'connection-manager' });
432
- }
433
- }
501
+ index += batchSize;
502
+ // Continue with next batch if needed
503
+ if (index < connections.length) {
504
+ setImmediate(processBatch);
434
505
  }
435
- // Clear all maps
436
- this.connectionRecords.clear();
437
- this.terminationStats = { incoming: {}, outgoing: {} };
438
- }, 100);
506
+ else {
507
+ // Clear all maps
508
+ this.connectionRecords.clear();
509
+ this.nextInactivityCheck.clear();
510
+ this.cleanupQueue.clear();
511
+ this.terminationStats = { incoming: {}, outgoing: {} };
512
+ }
513
+ };
514
+ // Start batch processing
515
+ setImmediate(processBatch);
439
516
  }
440
517
  }
441
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29ubmVjdGlvbi1tYW5hZ2VyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vdHMvcHJveGllcy9zbWFydC1wcm94eS9jb25uZWN0aW9uLW1hbmFnZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxrQkFBa0IsQ0FBQztBQUU1QyxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFDeEQsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBQ3RELE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUVwRDs7R0FFRztBQUNILE1BQU0sT0FBTyxpQkFBaUI7SUFPNUIsWUFDVSxRQUE0QixFQUM1QixlQUFnQyxFQUNoQyxjQUE4QjtRQUY5QixhQUFRLEdBQVIsUUFBUSxDQUFvQjtRQUM1QixvQkFBZSxHQUFmLGVBQWUsQ0FBaUI7UUFDaEMsbUJBQWMsR0FBZCxjQUFjLENBQWdCO1FBVGhDLHNCQUFpQixHQUFtQyxJQUFJLEdBQUcsRUFBRSxDQUFDO1FBQzlELHFCQUFnQixHQUdwQixFQUFFLFFBQVEsRUFBRSxFQUFFLEVBQUUsUUFBUSxFQUFFLEVBQUUsRUFBRSxDQUFDO0lBTWhDLENBQUM7SUFFSjs7T0FFRztJQUNJLG9CQUFvQjtRQUN6QixPQUFPLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDLENBQUMsU0FBUyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUM7WUFDM0MsSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUMsQ0FBQyxTQUFTLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDO0lBQ3JELENBQUM7SUFFRDs7T0FFRztJQUNJLGdCQUFnQixDQUFDLE1BQTBCO1FBQ2hELE1BQU0sWUFBWSxHQUFHLElBQUksQ0FBQyxvQkFBb0IsRUFBRSxDQUFDO1FBQ2pELE1B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
518
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29ubmVjdGlvbi1tYW5hZ2VyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vdHMvcHJveGllcy9zbWFydC1wcm94eS9jb25uZWN0aW9uLW1hbmFnZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxrQkFBa0IsQ0FBQztBQUU1QyxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFDeEQsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBQ3RELE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUNwRCxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSx5Q0FBeUMsQ0FBQztBQUU3RTs7R0FFRztBQUNILE1BQU0sT0FBTyxpQkFBa0IsU0FBUSxrQkFBa0I7SUFrQnZELFlBQ1UsUUFBNEIsRUFDNUIsZUFBZ0MsRUFDaEMsY0FBOEI7UUFFdEMsS0FBSyxFQUFFLENBQUM7UUFKQSxhQUFRLEdBQVIsUUFBUSxDQUFvQjtRQUM1QixvQkFBZSxHQUFmLGVBQWUsQ0FBaUI7UUFDaEMsbUJBQWMsR0FBZCxjQUFjLENBQWdCO1FBcEJoQyxzQkFBaUIsR0FBbUMsSUFBSSxHQUFHLEVBQUUsQ0FBQztRQUM5RCxxQkFBZ0IsR0FHcEIsRUFBRSxRQUFRLEVBQUUsRUFBRSxFQUFFLFFBQVEsRUFBRSxFQUFFLEVBQUUsQ0FBQztRQUVuQyx1RUFBdUU7UUFDL0Qsd0JBQW1CLEdBQXdCLElBQUksR0FBRyxFQUFFLENBQUM7UUFJNUMscUJBQWdCLEdBQVcsR0FBRyxDQUFDO1FBRWhELHVDQUF1QztRQUMvQixpQkFBWSxHQUFnQixJQUFJLEdBQUcsRUFBRSxDQUFDO1FBQ3RDLGlCQUFZLEdBQTBCLElBQUksQ0FBQztRQVNqRCxnREFBZ0Q7UUFDaEQsSUFBSSxDQUFDLGNBQWMsR0FBRyxRQUFRLENBQUMsUUFBUSxFQUFFLFFBQVEsRUFBRSxjQUFjLElBQUks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