@push.rocks/smartproxy 19.3.9 → 19.3.11
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/00_commitinfo_data.js +2 -2
- package/dist_ts/core/utils/index.d.ts +1 -0
- package/dist_ts/core/utils/index.js +2 -1
- package/dist_ts/core/utils/logger.d.ts +2 -0
- package/dist_ts/core/utils/logger.js +8 -0
- package/dist_ts/plugins.d.ts +3 -1
- package/dist_ts/plugins.js +4 -2
- package/dist_ts/proxies/http-proxy/handlers/static-handler.d.ts +1 -1
- package/dist_ts/proxies/http-proxy/handlers/static-handler.js +11 -3
- package/dist_ts/proxies/smart-proxy/certificate-manager.d.ts +1 -1
- package/dist_ts/proxies/smart-proxy/certificate-manager.js +28 -26
- package/dist_ts/proxies/smart-proxy/connection-manager.js +90 -27
- package/dist_ts/proxies/smart-proxy/route-connection-handler.js +272 -70
- package/dist_ts/proxies/smart-proxy/smart-proxy.js +46 -26
- package/package.json +2 -1
- package/readme.hints.md +24 -1
- package/readme.md +8 -2
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/core/utils/index.ts +1 -0
- package/ts/core/utils/logger.ts +10 -0
- package/ts/plugins.ts +4 -0
- package/ts/proxies/http-proxy/handlers/static-handler.ts +12 -2
- package/ts/proxies/smart-proxy/certificate-manager.ts +28 -26
- package/ts/proxies/smart-proxy/connection-manager.ts +95 -48
- package/ts/proxies/smart-proxy/route-connection-handler.ts +290 -142
- package/ts/proxies/smart-proxy/smart-proxy.ts +46 -27
|
@@ -4,6 +4,7 @@ import type { IRouteConfig, IRouteTls } from './models/route-types.js';
|
|
|
4
4
|
import type { IAcmeOptions } from './models/interfaces.js';
|
|
5
5
|
import { CertStore } from './cert-store.js';
|
|
6
6
|
import type { AcmeStateManager } from './acme-state-manager.js';
|
|
7
|
+
import { logger } from '../../core/utils/logger.js';
|
|
7
8
|
|
|
8
9
|
export interface ICertStatus {
|
|
9
10
|
domain: string;
|
|
@@ -125,15 +126,16 @@ export class SmartCertManager {
|
|
|
125
126
|
|
|
126
127
|
// Add challenge route once at initialization if not already active
|
|
127
128
|
if (!this.challengeRouteActive) {
|
|
128
|
-
|
|
129
|
+
logger.log('info', 'Adding ACME challenge route during initialization', { component: 'certificate-manager' });
|
|
129
130
|
await this.addChallengeRoute();
|
|
130
131
|
} else {
|
|
131
|
-
|
|
132
|
+
logger.log('info', 'Challenge route already active from previous instance', { component: 'certificate-manager' });
|
|
132
133
|
}
|
|
133
134
|
}
|
|
134
135
|
|
|
135
|
-
//
|
|
136
|
-
|
|
136
|
+
// Skip automatic certificate provisioning during initialization
|
|
137
|
+
// This will be called later after ports are listening
|
|
138
|
+
logger.log('info', 'Certificate manager initialized. Deferring certificate provisioning until after ports are listening.', { component: 'certificate-manager' });
|
|
137
139
|
|
|
138
140
|
// Start renewal timer
|
|
139
141
|
this.startRenewalTimer();
|
|
@@ -142,7 +144,7 @@ export class SmartCertManager {
|
|
|
142
144
|
/**
|
|
143
145
|
* Provision certificates for all routes that need them
|
|
144
146
|
*/
|
|
145
|
-
|
|
147
|
+
public async provisionAllCertificates(): Promise<void> {
|
|
146
148
|
const certRoutes = this.routes.filter(r =>
|
|
147
149
|
r.action.tls?.mode === 'terminate' ||
|
|
148
150
|
r.action.tls?.mode === 'terminate-and-reencrypt'
|
|
@@ -156,7 +158,7 @@ export class SmartCertManager {
|
|
|
156
158
|
try {
|
|
157
159
|
await this.provisionCertificate(route, true); // Allow concurrent since we're managing it here
|
|
158
160
|
} catch (error) {
|
|
159
|
-
|
|
161
|
+
logger.log('error', `Failed to provision certificate for route ${route.name}`, { routeName: route.name, error, component: 'certificate-manager' });
|
|
160
162
|
}
|
|
161
163
|
}
|
|
162
164
|
} finally {
|
|
@@ -175,13 +177,13 @@ export class SmartCertManager {
|
|
|
175
177
|
|
|
176
178
|
// Check if provisioning is already in progress (prevent concurrent provisioning)
|
|
177
179
|
if (!allowConcurrent && this.isProvisioning) {
|
|
178
|
-
|
|
180
|
+
logger.log('info', `Certificate provisioning already in progress, skipping ${route.name}`, { routeName: route.name, component: 'certificate-manager' });
|
|
179
181
|
return;
|
|
180
182
|
}
|
|
181
183
|
|
|
182
184
|
const domains = this.extractDomainsFromRoute(route);
|
|
183
185
|
if (domains.length === 0) {
|
|
184
|
-
|
|
186
|
+
logger.log('warn', `Route ${route.name} has TLS termination but no domains`, { routeName: route.name, component: 'certificate-manager' });
|
|
185
187
|
return;
|
|
186
188
|
}
|
|
187
189
|
|
|
@@ -218,7 +220,7 @@ export class SmartCertManager {
|
|
|
218
220
|
// Check if we already have a valid certificate
|
|
219
221
|
const existingCert = await this.certStore.getCertificate(routeName);
|
|
220
222
|
if (existingCert && this.isCertificateValid(existingCert)) {
|
|
221
|
-
|
|
223
|
+
logger.log('info', `Using existing valid certificate for ${primaryDomain}`, { domain: primaryDomain, component: 'certificate-manager' });
|
|
222
224
|
await this.applyCertificate(primaryDomain, existingCert);
|
|
223
225
|
this.updateCertStatus(routeName, 'valid', 'acme', existingCert);
|
|
224
226
|
return;
|
|
@@ -229,7 +231,7 @@ export class SmartCertManager {
|
|
|
229
231
|
this.globalAcmeDefaults?.renewThresholdDays ||
|
|
230
232
|
30;
|
|
231
233
|
|
|
232
|
-
|
|
234
|
+
logger.log('info', `Requesting ACME certificate for ${domains.join(', ')} (renew ${renewThreshold} days before expiry)`, { domains: domains.join(', '), renewThreshold, component: 'certificate-manager' });
|
|
233
235
|
this.updateCertStatus(routeName, 'pending', 'acme');
|
|
234
236
|
|
|
235
237
|
try {
|
|
@@ -251,7 +253,7 @@ export class SmartCertManager {
|
|
|
251
253
|
hasDnsChallenge;
|
|
252
254
|
|
|
253
255
|
if (shouldIncludeWildcard) {
|
|
254
|
-
|
|
256
|
+
logger.log('info', `Requesting wildcard certificate for ${primaryDomain} (DNS-01 available)`, { domain: primaryDomain, challengeType: 'DNS-01', component: 'certificate-manager' });
|
|
255
257
|
}
|
|
256
258
|
|
|
257
259
|
// Use smartacme to get certificate with optional wildcard
|
|
@@ -278,9 +280,9 @@ export class SmartCertManager {
|
|
|
278
280
|
await this.applyCertificate(primaryDomain, certData);
|
|
279
281
|
this.updateCertStatus(routeName, 'valid', 'acme', certData);
|
|
280
282
|
|
|
281
|
-
|
|
283
|
+
logger.log('info', `Successfully provisioned ACME certificate for ${primaryDomain}`, { domain: primaryDomain, component: 'certificate-manager' });
|
|
282
284
|
} catch (error) {
|
|
283
|
-
|
|
285
|
+
logger.log('error', `Failed to provision ACME certificate for ${primaryDomain}: ${error.message}`, { domain: primaryDomain, error: error.message, component: 'certificate-manager' });
|
|
284
286
|
this.updateCertStatus(routeName, 'error', 'acme', undefined, error.message);
|
|
285
287
|
throw error;
|
|
286
288
|
}
|
|
@@ -327,9 +329,9 @@ export class SmartCertManager {
|
|
|
327
329
|
await this.applyCertificate(domain, certData);
|
|
328
330
|
this.updateCertStatus(routeName, 'valid', 'static', certData);
|
|
329
331
|
|
|
330
|
-
|
|
332
|
+
logger.log('info', `Successfully loaded static certificate for ${domain}`, { domain, component: 'certificate-manager' });
|
|
331
333
|
} catch (error) {
|
|
332
|
-
|
|
334
|
+
logger.log('error', `Failed to provision static certificate for ${domain}: ${error.message}`, { domain, error: error.message, component: 'certificate-manager' });
|
|
333
335
|
this.updateCertStatus(routeName, 'error', 'static', undefined, error.message);
|
|
334
336
|
throw error;
|
|
335
337
|
}
|
|
@@ -340,7 +342,7 @@ export class SmartCertManager {
|
|
|
340
342
|
*/
|
|
341
343
|
private async applyCertificate(domain: string, certData: ICertificateData): Promise<void> {
|
|
342
344
|
if (!this.httpProxy) {
|
|
343
|
-
|
|
345
|
+
logger.log('warn', `HttpProxy not set, cannot apply certificate for domain ${domain}`, { domain, component: 'certificate-manager' });
|
|
344
346
|
return;
|
|
345
347
|
}
|
|
346
348
|
|
|
@@ -397,13 +399,13 @@ export class SmartCertManager {
|
|
|
397
399
|
private async addChallengeRoute(): Promise<void> {
|
|
398
400
|
// Check with state manager first
|
|
399
401
|
if (this.acmeStateManager && this.acmeStateManager.isChallengeRouteActive()) {
|
|
400
|
-
|
|
402
|
+
logger.log('info', 'Challenge route already active in global state, skipping', { component: 'certificate-manager' });
|
|
401
403
|
this.challengeRouteActive = true;
|
|
402
404
|
return;
|
|
403
405
|
}
|
|
404
406
|
|
|
405
407
|
if (this.challengeRouteActive) {
|
|
406
|
-
|
|
408
|
+
logger.log('info', 'Challenge route already active locally, skipping', { component: 'certificate-manager' });
|
|
407
409
|
return;
|
|
408
410
|
}
|
|
409
411
|
|
|
@@ -426,9 +428,9 @@ export class SmartCertManager {
|
|
|
426
428
|
this.acmeStateManager.addChallengeRoute(challengeRoute);
|
|
427
429
|
}
|
|
428
430
|
|
|
429
|
-
|
|
431
|
+
logger.log('info', 'ACME challenge route successfully added', { component: 'certificate-manager' });
|
|
430
432
|
} catch (error) {
|
|
431
|
-
|
|
433
|
+
logger.log('error', `Failed to add challenge route: ${error.message}`, { error: error.message, component: 'certificate-manager' });
|
|
432
434
|
if ((error as any).code === 'EADDRINUSE') {
|
|
433
435
|
throw new Error(`Port ${this.globalAcmeDefaults?.port || 80} is already in use for ACME challenges`);
|
|
434
436
|
}
|
|
@@ -441,7 +443,7 @@ export class SmartCertManager {
|
|
|
441
443
|
*/
|
|
442
444
|
private async removeChallengeRoute(): Promise<void> {
|
|
443
445
|
if (!this.challengeRouteActive) {
|
|
444
|
-
|
|
446
|
+
logger.log('info', 'Challenge route not active, skipping removal', { component: 'certificate-manager' });
|
|
445
447
|
return;
|
|
446
448
|
}
|
|
447
449
|
|
|
@@ -459,9 +461,9 @@ export class SmartCertManager {
|
|
|
459
461
|
this.acmeStateManager.removeChallengeRoute('acme-challenge');
|
|
460
462
|
}
|
|
461
463
|
|
|
462
|
-
|
|
464
|
+
logger.log('info', 'ACME challenge route successfully removed', { component: 'certificate-manager' });
|
|
463
465
|
} catch (error) {
|
|
464
|
-
|
|
466
|
+
logger.log('error', `Failed to remove challenge route: ${error.message}`, { error: error.message, component: 'certificate-manager' });
|
|
465
467
|
// Reset the flag even on error to avoid getting stuck
|
|
466
468
|
this.challengeRouteActive = false;
|
|
467
469
|
throw error;
|
|
@@ -491,11 +493,11 @@ export class SmartCertManager {
|
|
|
491
493
|
const cert = await this.certStore.getCertificate(routeName);
|
|
492
494
|
|
|
493
495
|
if (cert && !this.isCertificateValid(cert)) {
|
|
494
|
-
|
|
496
|
+
logger.log('info', `Certificate for ${routeName} needs renewal`, { routeName, component: 'certificate-manager' });
|
|
495
497
|
try {
|
|
496
498
|
await this.provisionCertificate(route);
|
|
497
499
|
} catch (error) {
|
|
498
|
-
|
|
500
|
+
logger.log('error', `Failed to renew certificate for ${routeName}: ${error.message}`, { routeName, error: error.message, component: 'certificate-manager' });
|
|
499
501
|
}
|
|
500
502
|
}
|
|
501
503
|
}
|
|
@@ -620,7 +622,7 @@ export class SmartCertManager {
|
|
|
620
622
|
|
|
621
623
|
// Always remove challenge route on shutdown
|
|
622
624
|
if (this.challengeRoute) {
|
|
623
|
-
|
|
625
|
+
logger.log('info', 'Removing ACME challenge route during shutdown', { component: 'certificate-manager' });
|
|
624
626
|
await this.removeChallengeRoute();
|
|
625
627
|
}
|
|
626
628
|
|
|
@@ -2,6 +2,7 @@ import * as plugins from '../../plugins.js';
|
|
|
2
2
|
import type { IConnectionRecord, ISmartProxyOptions } from './models/interfaces.js';
|
|
3
3
|
import { SecurityManager } from './security-manager.js';
|
|
4
4
|
import { TimeoutManager } from './timeout-manager.js';
|
|
5
|
+
import { logger } from '../../core/utils/logger.js';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Manages connection lifecycle, tracking, and cleanup
|
|
@@ -97,7 +98,7 @@ export class ConnectionManager {
|
|
|
97
98
|
*/
|
|
98
99
|
public initiateCleanupOnce(record: IConnectionRecord, reason: string = 'normal'): void {
|
|
99
100
|
if (this.settings.enableDetailedLogging) {
|
|
100
|
-
|
|
101
|
+
logger.log('info', `Connection cleanup initiated`, { connectionId: record.id, remoteIP: record.remoteIP, reason, component: 'connection-manager' });
|
|
101
102
|
}
|
|
102
103
|
|
|
103
104
|
if (
|
|
@@ -139,7 +140,7 @@ export class ConnectionManager {
|
|
|
139
140
|
// Reset the handler references
|
|
140
141
|
record.renegotiationHandler = undefined;
|
|
141
142
|
} catch (err) {
|
|
142
|
-
|
|
143
|
+
logger.log('error', `Error removing data handlers for connection ${record.id}: ${err}`, { connectionId: record.id, error: err, component: 'connection-manager' });
|
|
143
144
|
}
|
|
144
145
|
}
|
|
145
146
|
|
|
@@ -160,16 +161,36 @@ export class ConnectionManager {
|
|
|
160
161
|
|
|
161
162
|
// Log connection details
|
|
162
163
|
if (this.settings.enableDetailedLogging) {
|
|
163
|
-
|
|
164
|
-
`
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
164
|
+
logger.log('info',
|
|
165
|
+
`Connection from ${record.remoteIP} on port ${record.localPort} terminated (${reason}). ` +
|
|
166
|
+
`Duration: ${plugins.prettyMs(duration)}, Bytes IN: ${bytesReceived}, OUT: ${bytesSent}, ` +
|
|
167
|
+
`TLS: ${record.isTLS ? 'Yes' : 'No'}, Keep-Alive: ${record.hasKeepAlive ? 'Yes' : 'No'}` +
|
|
168
|
+
`${record.usingNetworkProxy ? ', Using NetworkProxy' : ''}` +
|
|
169
|
+
`${record.domainSwitches ? `, Domain switches: ${record.domainSwitches}` : ''}`,
|
|
170
|
+
{
|
|
171
|
+
connectionId: record.id,
|
|
172
|
+
remoteIP: record.remoteIP,
|
|
173
|
+
localPort: record.localPort,
|
|
174
|
+
reason,
|
|
175
|
+
duration: plugins.prettyMs(duration),
|
|
176
|
+
bytes: { in: bytesReceived, out: bytesSent },
|
|
177
|
+
tls: record.isTLS,
|
|
178
|
+
keepAlive: record.hasKeepAlive,
|
|
179
|
+
usingNetworkProxy: record.usingNetworkProxy,
|
|
180
|
+
domainSwitches: record.domainSwitches || 0,
|
|
181
|
+
component: 'connection-manager'
|
|
182
|
+
}
|
|
169
183
|
);
|
|
170
184
|
} else {
|
|
171
|
-
|
|
172
|
-
`
|
|
185
|
+
logger.log('info',
|
|
186
|
+
`Connection from ${record.remoteIP} terminated (${reason}). Active connections: ${this.connectionRecords.size}`,
|
|
187
|
+
{
|
|
188
|
+
connectionId: record.id,
|
|
189
|
+
remoteIP: record.remoteIP,
|
|
190
|
+
reason,
|
|
191
|
+
activeConnections: this.connectionRecords.size,
|
|
192
|
+
component: 'connection-manager'
|
|
193
|
+
}
|
|
173
194
|
);
|
|
174
195
|
}
|
|
175
196
|
}
|
|
@@ -189,7 +210,7 @@ export class ConnectionManager {
|
|
|
189
210
|
socket.destroy();
|
|
190
211
|
}
|
|
191
212
|
} catch (err) {
|
|
192
|
-
|
|
213
|
+
logger.log('error', `Error destroying ${side} socket for connection ${record.id}: ${err}`, { connectionId: record.id, side, error: err, component: 'connection-manager' });
|
|
193
214
|
}
|
|
194
215
|
}, 1000);
|
|
195
216
|
|
|
@@ -199,13 +220,13 @@ export class ConnectionManager {
|
|
|
199
220
|
}
|
|
200
221
|
}
|
|
201
222
|
} catch (err) {
|
|
202
|
-
|
|
223
|
+
logger.log('error', `Error closing ${side} socket for connection ${record.id}: ${err}`, { connectionId: record.id, side, error: err, component: 'connection-manager' });
|
|
203
224
|
try {
|
|
204
225
|
if (!socket.destroyed) {
|
|
205
226
|
socket.destroy();
|
|
206
227
|
}
|
|
207
228
|
} catch (destroyErr) {
|
|
208
|
-
|
|
229
|
+
logger.log('error', `Error destroying ${side} socket for connection ${record.id}: ${destroyErr}`, { connectionId: record.id, side, error: destroyErr, component: 'connection-manager' });
|
|
209
230
|
}
|
|
210
231
|
}
|
|
211
232
|
}
|
|
@@ -224,21 +245,36 @@ export class ConnectionManager {
|
|
|
224
245
|
|
|
225
246
|
if (code === 'ECONNRESET') {
|
|
226
247
|
reason = 'econnreset';
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
248
|
+
logger.log('warn', `ECONNRESET on ${side} connection from ${record.remoteIP}. Error: ${err.message}. Duration: ${plugins.prettyMs(connectionDuration)}, Last activity: ${plugins.prettyMs(lastActivityAge)}`, {
|
|
249
|
+
connectionId: record.id,
|
|
250
|
+
side,
|
|
251
|
+
remoteIP: record.remoteIP,
|
|
252
|
+
error: err.message,
|
|
253
|
+
duration: plugins.prettyMs(connectionDuration),
|
|
254
|
+
lastActivity: plugins.prettyMs(lastActivityAge),
|
|
255
|
+
component: 'connection-manager'
|
|
256
|
+
});
|
|
231
257
|
} else if (code === 'ETIMEDOUT') {
|
|
232
258
|
reason = 'etimedout';
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
259
|
+
logger.log('warn', `ETIMEDOUT on ${side} connection from ${record.remoteIP}. Error: ${err.message}. Duration: ${plugins.prettyMs(connectionDuration)}, Last activity: ${plugins.prettyMs(lastActivityAge)}`, {
|
|
260
|
+
connectionId: record.id,
|
|
261
|
+
side,
|
|
262
|
+
remoteIP: record.remoteIP,
|
|
263
|
+
error: err.message,
|
|
264
|
+
duration: plugins.prettyMs(connectionDuration),
|
|
265
|
+
lastActivity: plugins.prettyMs(lastActivityAge),
|
|
266
|
+
component: 'connection-manager'
|
|
267
|
+
});
|
|
237
268
|
} else {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
269
|
+
logger.log('error', `Error on ${side} connection from ${record.remoteIP}: ${err.message}. Duration: ${plugins.prettyMs(connectionDuration)}, Last activity: ${plugins.prettyMs(lastActivityAge)}`, {
|
|
270
|
+
connectionId: record.id,
|
|
271
|
+
side,
|
|
272
|
+
remoteIP: record.remoteIP,
|
|
273
|
+
error: err.message,
|
|
274
|
+
duration: plugins.prettyMs(connectionDuration),
|
|
275
|
+
lastActivity: plugins.prettyMs(lastActivityAge),
|
|
276
|
+
component: 'connection-manager'
|
|
277
|
+
});
|
|
242
278
|
}
|
|
243
279
|
|
|
244
280
|
if (side === 'incoming' && record.incomingTerminationReason === null) {
|
|
@@ -259,7 +295,12 @@ export class ConnectionManager {
|
|
|
259
295
|
public handleClose(side: 'incoming' | 'outgoing', record: IConnectionRecord) {
|
|
260
296
|
return () => {
|
|
261
297
|
if (this.settings.enableDetailedLogging) {
|
|
262
|
-
|
|
298
|
+
logger.log('info', `Connection closed on ${side} side`, {
|
|
299
|
+
connectionId: record.id,
|
|
300
|
+
side,
|
|
301
|
+
remoteIP: record.remoteIP,
|
|
302
|
+
component: 'connection-manager'
|
|
303
|
+
});
|
|
263
304
|
}
|
|
264
305
|
|
|
265
306
|
if (side === 'incoming' && record.incomingTerminationReason === null) {
|
|
@@ -321,11 +362,13 @@ export class ConnectionManager {
|
|
|
321
362
|
if (inactivityTime > effectiveTimeout && !record.connectionClosed) {
|
|
322
363
|
// For keep-alive connections, issue a warning first
|
|
323
364
|
if (record.hasKeepAlive && !record.inactivityWarningIssued) {
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
365
|
+
logger.log('warn', `Keep-alive connection ${id} from ${record.remoteIP} inactive for ${plugins.prettyMs(inactivityTime)}. Will close in 10 minutes if no activity.`, {
|
|
366
|
+
connectionId: id,
|
|
367
|
+
remoteIP: record.remoteIP,
|
|
368
|
+
inactiveFor: plugins.prettyMs(inactivityTime),
|
|
369
|
+
closureWarning: '10 minutes',
|
|
370
|
+
component: 'connection-manager'
|
|
371
|
+
});
|
|
329
372
|
|
|
330
373
|
// Set warning flag and add grace period
|
|
331
374
|
record.inactivityWarningIssued = true;
|
|
@@ -337,27 +380,30 @@ export class ConnectionManager {
|
|
|
337
380
|
record.outgoing.write(Buffer.alloc(0));
|
|
338
381
|
|
|
339
382
|
if (this.settings.enableDetailedLogging) {
|
|
340
|
-
|
|
383
|
+
logger.log('info', `Sent probe packet to test keep-alive connection ${id}`, { connectionId: id, component: 'connection-manager' });
|
|
341
384
|
}
|
|
342
385
|
} catch (err) {
|
|
343
|
-
|
|
386
|
+
logger.log('error', `Error sending probe packet to connection ${id}: ${err}`, { connectionId: id, error: err, component: 'connection-manager' });
|
|
344
387
|
}
|
|
345
388
|
}
|
|
346
389
|
} else {
|
|
347
390
|
// For non-keep-alive or after warning, close the connection
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
391
|
+
logger.log('warn', `Closing inactive connection ${id} from ${record.remoteIP} (inactive for ${plugins.prettyMs(inactivityTime)}, keep-alive: ${record.hasKeepAlive ? 'Yes' : 'No'})`, {
|
|
392
|
+
connectionId: id,
|
|
393
|
+
remoteIP: record.remoteIP,
|
|
394
|
+
inactiveFor: plugins.prettyMs(inactivityTime),
|
|
395
|
+
hasKeepAlive: record.hasKeepAlive,
|
|
396
|
+
component: 'connection-manager'
|
|
397
|
+
});
|
|
353
398
|
this.cleanupConnection(record, 'inactivity');
|
|
354
399
|
}
|
|
355
400
|
} else if (inactivityTime <= effectiveTimeout && record.inactivityWarningIssued) {
|
|
356
401
|
// If activity detected after warning, clear the warning
|
|
357
402
|
if (this.settings.enableDetailedLogging) {
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
403
|
+
logger.log('info', `Connection ${id} activity detected after inactivity warning`, {
|
|
404
|
+
connectionId: id,
|
|
405
|
+
component: 'connection-manager'
|
|
406
|
+
});
|
|
361
407
|
}
|
|
362
408
|
record.inactivityWarningIssued = false;
|
|
363
409
|
}
|
|
@@ -369,11 +415,12 @@ export class ConnectionManager {
|
|
|
369
415
|
!record.connectionClosed &&
|
|
370
416
|
now - record.outgoingClosedTime > 120000
|
|
371
417
|
) {
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
418
|
+
logger.log('warn', `Parity check: Connection ${id} from ${record.remoteIP} has incoming socket still active ${plugins.prettyMs(now - record.outgoingClosedTime)} after outgoing socket closed`, {
|
|
419
|
+
connectionId: id,
|
|
420
|
+
remoteIP: record.remoteIP,
|
|
421
|
+
timeElapsed: plugins.prettyMs(now - record.outgoingClosedTime),
|
|
422
|
+
component: 'connection-manager'
|
|
423
|
+
});
|
|
377
424
|
this.cleanupConnection(record, 'parity_check');
|
|
378
425
|
}
|
|
379
426
|
}
|
|
@@ -406,7 +453,7 @@ export class ConnectionManager {
|
|
|
406
453
|
record.outgoing.end();
|
|
407
454
|
}
|
|
408
455
|
} catch (err) {
|
|
409
|
-
|
|
456
|
+
logger.log('error', `Error during graceful end of connection ${id}: ${err}`, { connectionId: id, error: err, component: 'connection-manager' });
|
|
410
457
|
}
|
|
411
458
|
}
|
|
412
459
|
}
|
|
@@ -433,7 +480,7 @@ export class ConnectionManager {
|
|
|
433
480
|
}
|
|
434
481
|
}
|
|
435
482
|
} catch (err) {
|
|
436
|
-
|
|
483
|
+
logger.log('error', `Error during forced destruction of connection ${id}: ${err}`, { connectionId: id, error: err, component: 'connection-manager' });
|
|
437
484
|
}
|
|
438
485
|
}
|
|
439
486
|
}
|