@push.rocks/smartproxy 19.6.2 → 19.6.7
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/proxies/smart-proxy/connection-manager.d.ts +4 -7
- package/dist_ts/proxies/smart-proxy/connection-manager.js +22 -22
- package/dist_ts/proxies/smart-proxy/http-proxy-bridge.d.ts +4 -3
- package/dist_ts/proxies/smart-proxy/http-proxy-bridge.js +9 -9
- package/dist_ts/proxies/smart-proxy/metrics-collector.d.ts +68 -56
- package/dist_ts/proxies/smart-proxy/metrics-collector.js +226 -176
- package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +5 -0
- package/dist_ts/proxies/smart-proxy/models/metrics-types.d.ts +94 -48
- package/dist_ts/proxies/smart-proxy/nftables-manager.d.ts +4 -4
- package/dist_ts/proxies/smart-proxy/nftables-manager.js +6 -6
- package/dist_ts/proxies/smart-proxy/port-manager.d.ts +4 -7
- package/dist_ts/proxies/smart-proxy/port-manager.js +6 -9
- package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +4 -15
- package/dist_ts/proxies/smart-proxy/route-connection-handler.js +128 -128
- package/dist_ts/proxies/smart-proxy/security-manager.d.ts +3 -3
- package/dist_ts/proxies/smart-proxy/security-manager.js +9 -9
- package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +20 -13
- package/dist_ts/proxies/smart-proxy/smart-proxy.js +16 -13
- package/dist_ts/proxies/smart-proxy/throughput-tracker.d.ts +36 -0
- package/dist_ts/proxies/smart-proxy/throughput-tracker.js +117 -0
- package/dist_ts/proxies/smart-proxy/timeout-manager.d.ts +4 -3
- package/dist_ts/proxies/smart-proxy/timeout-manager.js +16 -16
- package/dist_ts/proxies/smart-proxy/tls-manager.d.ts +3 -3
- package/dist_ts/proxies/smart-proxy/tls-manager.js +12 -12
- package/package.json +8 -17
- package/readme.hints.md +0 -897
- package/readme.md +960 -54
- package/readme.plan.md +301 -562
- package/ts/proxies/smart-proxy/connection-manager.ts +23 -21
- package/ts/proxies/smart-proxy/http-proxy-bridge.ts +9 -8
- package/ts/proxies/smart-proxy/metrics-collector.ts +277 -189
- package/ts/proxies/smart-proxy/models/interfaces.ts +7 -0
- package/ts/proxies/smart-proxy/models/metrics-types.ts +93 -41
- package/ts/proxies/smart-proxy/nftables-manager.ts +5 -5
- package/ts/proxies/smart-proxy/port-manager.ts +6 -14
- package/ts/proxies/smart-proxy/route-connection-handler.ts +136 -136
- package/ts/proxies/smart-proxy/security-manager.ts +8 -8
- package/ts/proxies/smart-proxy/smart-proxy.ts +26 -35
- package/ts/proxies/smart-proxy/throughput-tracker.ts +144 -0
- package/ts/proxies/smart-proxy/timeout-manager.ts +16 -15
- package/ts/proxies/smart-proxy/tls-manager.ts +11 -11
- package/readme.connections.md +0 -724
- package/readme.delete.md +0 -187
- package/readme.memory-leaks-fixed.md +0 -45
- package/readme.metrics.md +0 -591
- package/readme.monitoring.md +0 -202
- package/readme.proxy-chain-summary.md +0 -112
- package/readme.proxy-protocol-example.md +0 -462
- package/readme.proxy-protocol.md +0 -415
- package/readme.routing.md +0 -341
- package/readme.websocket-keepalive-config.md +0 -140
- package/readme.websocket-keepalive-fix.md +0 -63
|
@@ -4,23 +4,16 @@ import { logger } from '../../core/utils/logger.js';
|
|
|
4
4
|
// Route checking functions have been removed
|
|
5
5
|
import type { IRouteConfig, IRouteAction } from './models/route-types.js';
|
|
6
6
|
import type { IRouteContext } from '../../core/models/route-context.js';
|
|
7
|
-
import { ConnectionManager } from './connection-manager.js';
|
|
8
|
-
import { SecurityManager } from './security-manager.js';
|
|
9
|
-
import { TlsManager } from './tls-manager.js';
|
|
10
|
-
import { HttpProxyBridge } from './http-proxy-bridge.js';
|
|
11
|
-
import { TimeoutManager } from './timeout-manager.js';
|
|
12
|
-
import { SharedRouteManager as RouteManager } from '../../core/routing/route-manager.js';
|
|
13
7
|
import { cleanupSocket, setupSocketHandlers, createSocketWithErrorHandler, setupBidirectionalForwarding } from '../../core/utils/socket-utils.js';
|
|
14
8
|
import { WrappedSocket } from '../../core/models/wrapped-socket.js';
|
|
15
9
|
import { getUnderlyingSocket } from '../../core/models/socket-types.js';
|
|
16
10
|
import { ProxyProtocolParser } from '../../core/utils/proxy-protocol.js';
|
|
11
|
+
import type { SmartProxy } from './smart-proxy.js';
|
|
17
12
|
|
|
18
13
|
/**
|
|
19
14
|
* Handles new connection processing and setup logic with support for route-based configuration
|
|
20
15
|
*/
|
|
21
16
|
export class RouteConnectionHandler {
|
|
22
|
-
private settings: ISmartProxyOptions;
|
|
23
|
-
|
|
24
17
|
// Note: Route context caching was considered but not implemented
|
|
25
18
|
// as route contexts are lightweight and should be created fresh
|
|
26
19
|
// for each connection to ensure accurate context data
|
|
@@ -29,16 +22,8 @@ export class RouteConnectionHandler {
|
|
|
29
22
|
public newConnectionSubject = new plugins.smartrx.rxjs.Subject<IConnectionRecord>();
|
|
30
23
|
|
|
31
24
|
constructor(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
private securityManager: SecurityManager,
|
|
35
|
-
private tlsManager: TlsManager,
|
|
36
|
-
private httpProxyBridge: HttpProxyBridge,
|
|
37
|
-
private timeoutManager: TimeoutManager,
|
|
38
|
-
private routeManager: RouteManager
|
|
39
|
-
) {
|
|
40
|
-
this.settings = settings;
|
|
41
|
-
}
|
|
25
|
+
private smartProxy: SmartProxy
|
|
26
|
+
) {}
|
|
42
27
|
|
|
43
28
|
|
|
44
29
|
/**
|
|
@@ -93,7 +78,7 @@ export class RouteConnectionHandler {
|
|
|
93
78
|
const wrappedSocket = new WrappedSocket(socket);
|
|
94
79
|
|
|
95
80
|
// If this is from a trusted proxy, log it
|
|
96
|
-
if (this.settings.proxyIPs?.includes(remoteIP)) {
|
|
81
|
+
if (this.smartProxy.settings.proxyIPs?.includes(remoteIP)) {
|
|
97
82
|
logger.log('debug', `Connection from trusted proxy ${remoteIP}, PROXY protocol parsing will be enabled`, {
|
|
98
83
|
remoteIP,
|
|
99
84
|
component: 'route-handler'
|
|
@@ -102,7 +87,7 @@ export class RouteConnectionHandler {
|
|
|
102
87
|
|
|
103
88
|
// Validate IP against rate limits and connection limits
|
|
104
89
|
// Note: For wrapped sockets, this will use the underlying socket IP until PROXY protocol is parsed
|
|
105
|
-
const ipValidation = this.securityManager.validateIP(wrappedSocket.remoteAddress || '');
|
|
90
|
+
const ipValidation = this.smartProxy.securityManager.validateIP(wrappedSocket.remoteAddress || '');
|
|
106
91
|
if (!ipValidation.allowed) {
|
|
107
92
|
logger.log('warn', `Connection rejected`, { remoteIP: wrappedSocket.remoteAddress, reason: ipValidation.reason, component: 'route-handler' });
|
|
108
93
|
cleanupSocket(wrappedSocket.socket, `rejected-${ipValidation.reason}`, { immediate: true });
|
|
@@ -110,7 +95,7 @@ export class RouteConnectionHandler {
|
|
|
110
95
|
}
|
|
111
96
|
|
|
112
97
|
// Create a new connection record with the wrapped socket
|
|
113
|
-
const record = this.connectionManager.createConnection(wrappedSocket);
|
|
98
|
+
const record = this.smartProxy.connectionManager.createConnection(wrappedSocket);
|
|
114
99
|
if (!record) {
|
|
115
100
|
// Connection was rejected due to limit - socket already destroyed by connection manager
|
|
116
101
|
return;
|
|
@@ -122,15 +107,15 @@ export class RouteConnectionHandler {
|
|
|
122
107
|
|
|
123
108
|
// Apply socket optimizations (apply to underlying socket)
|
|
124
109
|
const underlyingSocket = wrappedSocket.socket;
|
|
125
|
-
underlyingSocket.setNoDelay(this.settings.noDelay);
|
|
110
|
+
underlyingSocket.setNoDelay(this.smartProxy.settings.noDelay);
|
|
126
111
|
|
|
127
112
|
// Apply keep-alive settings if enabled
|
|
128
|
-
if (this.settings.keepAlive) {
|
|
129
|
-
underlyingSocket.setKeepAlive(true, this.settings.keepAliveInitialDelay);
|
|
113
|
+
if (this.smartProxy.settings.keepAlive) {
|
|
114
|
+
underlyingSocket.setKeepAlive(true, this.smartProxy.settings.keepAliveInitialDelay);
|
|
130
115
|
record.hasKeepAlive = true;
|
|
131
116
|
|
|
132
117
|
// Apply enhanced TCP keep-alive options if enabled
|
|
133
|
-
if (this.settings.enableKeepAliveProbes) {
|
|
118
|
+
if (this.smartProxy.settings.enableKeepAliveProbes) {
|
|
134
119
|
try {
|
|
135
120
|
// These are platform-specific and may not be available
|
|
136
121
|
if ('setKeepAliveProbes' in underlyingSocket) {
|
|
@@ -141,34 +126,34 @@ export class RouteConnectionHandler {
|
|
|
141
126
|
}
|
|
142
127
|
} catch (err) {
|
|
143
128
|
// Ignore errors - these are optional enhancements
|
|
144
|
-
if (this.settings.enableDetailedLogging) {
|
|
129
|
+
if (this.smartProxy.settings.enableDetailedLogging) {
|
|
145
130
|
logger.log('warn', `Enhanced TCP keep-alive settings not supported`, { connectionId, error: err, component: 'route-handler' });
|
|
146
131
|
}
|
|
147
132
|
}
|
|
148
133
|
}
|
|
149
134
|
}
|
|
150
135
|
|
|
151
|
-
if (this.settings.enableDetailedLogging) {
|
|
136
|
+
if (this.smartProxy.settings.enableDetailedLogging) {
|
|
152
137
|
logger.log('info',
|
|
153
138
|
`New connection from ${remoteIP} on port ${localPort}. ` +
|
|
154
139
|
`Keep-Alive: ${record.hasKeepAlive ? 'Enabled' : 'Disabled'}. ` +
|
|
155
|
-
`Active connections: ${this.connectionManager.getConnectionCount()}`,
|
|
140
|
+
`Active connections: ${this.smartProxy.connectionManager.getConnectionCount()}`,
|
|
156
141
|
{
|
|
157
142
|
connectionId,
|
|
158
143
|
remoteIP,
|
|
159
144
|
localPort,
|
|
160
145
|
keepAlive: record.hasKeepAlive ? 'Enabled' : 'Disabled',
|
|
161
|
-
activeConnections: this.connectionManager.getConnectionCount(),
|
|
146
|
+
activeConnections: this.smartProxy.connectionManager.getConnectionCount(),
|
|
162
147
|
component: 'route-handler'
|
|
163
148
|
}
|
|
164
149
|
);
|
|
165
150
|
} else {
|
|
166
151
|
logger.log('info',
|
|
167
|
-
`New connection from ${remoteIP} on port ${localPort}. Active connections: ${this.connectionManager.getConnectionCount()}`,
|
|
152
|
+
`New connection from ${remoteIP} on port ${localPort}. Active connections: ${this.smartProxy.connectionManager.getConnectionCount()}`,
|
|
168
153
|
{
|
|
169
154
|
remoteIP,
|
|
170
155
|
localPort,
|
|
171
|
-
activeConnections: this.connectionManager.getConnectionCount(),
|
|
156
|
+
activeConnections: this.smartProxy.connectionManager.getConnectionCount(),
|
|
172
157
|
component: 'route-handler'
|
|
173
158
|
}
|
|
174
159
|
);
|
|
@@ -187,10 +172,10 @@ export class RouteConnectionHandler {
|
|
|
187
172
|
let initialDataReceived = false;
|
|
188
173
|
|
|
189
174
|
// Check if any routes on this port require TLS handling
|
|
190
|
-
const allRoutes = this.routeManager.getRoutes();
|
|
175
|
+
const allRoutes = this.smartProxy.routeManager.getRoutes();
|
|
191
176
|
const needsTlsHandling = allRoutes.some(route => {
|
|
192
177
|
// Check if route matches this port
|
|
193
|
-
const matchesPort = this.routeManager.getRoutesForPort(localPort).includes(route);
|
|
178
|
+
const matchesPort = this.smartProxy.routeManager.getRoutesForPort(localPort).includes(route);
|
|
194
179
|
|
|
195
180
|
return matchesPort &&
|
|
196
181
|
route.action.type === 'forward' &&
|
|
@@ -229,7 +214,7 @@ export class RouteConnectionHandler {
|
|
|
229
214
|
}
|
|
230
215
|
|
|
231
216
|
// Always cleanup the connection record
|
|
232
|
-
this.connectionManager.cleanupConnection(record, reason);
|
|
217
|
+
this.smartProxy.connectionManager.cleanupConnection(record, reason);
|
|
233
218
|
},
|
|
234
219
|
undefined, // Use default timeout handler
|
|
235
220
|
'immediate-route-client'
|
|
@@ -244,9 +229,9 @@ export class RouteConnectionHandler {
|
|
|
244
229
|
// Set an initial timeout for handshake data
|
|
245
230
|
let initialTimeout: NodeJS.Timeout | null = setTimeout(() => {
|
|
246
231
|
if (!initialDataReceived) {
|
|
247
|
-
logger.log('warn', `No initial data received from ${record.remoteIP} after ${this.settings.initialDataTimeout}ms for connection ${connectionId}`, {
|
|
232
|
+
logger.log('warn', `No initial data received from ${record.remoteIP} after ${this.smartProxy.settings.initialDataTimeout}ms for connection ${connectionId}`, {
|
|
248
233
|
connectionId,
|
|
249
|
-
timeout: this.settings.initialDataTimeout,
|
|
234
|
+
timeout: this.smartProxy.settings.initialDataTimeout,
|
|
250
235
|
remoteIP: record.remoteIP,
|
|
251
236
|
component: 'route-handler'
|
|
252
237
|
});
|
|
@@ -260,14 +245,14 @@ export class RouteConnectionHandler {
|
|
|
260
245
|
});
|
|
261
246
|
if (record.incomingTerminationReason === null) {
|
|
262
247
|
record.incomingTerminationReason = 'initial_timeout';
|
|
263
|
-
this.connectionManager.incrementTerminationStat('incoming', 'initial_timeout');
|
|
248
|
+
this.smartProxy.connectionManager.incrementTerminationStat('incoming', 'initial_timeout');
|
|
264
249
|
}
|
|
265
250
|
socket.end();
|
|
266
|
-
this.connectionManager.cleanupConnection(record, 'initial_timeout');
|
|
251
|
+
this.smartProxy.connectionManager.cleanupConnection(record, 'initial_timeout');
|
|
267
252
|
}
|
|
268
253
|
}, 30000);
|
|
269
254
|
}
|
|
270
|
-
}, this.settings.initialDataTimeout!);
|
|
255
|
+
}, this.smartProxy.settings.initialDataTimeout!);
|
|
271
256
|
|
|
272
257
|
// Make sure timeout doesn't keep the process alive
|
|
273
258
|
if (initialTimeout.unref) {
|
|
@@ -275,7 +260,7 @@ export class RouteConnectionHandler {
|
|
|
275
260
|
}
|
|
276
261
|
|
|
277
262
|
// Set up error handler
|
|
278
|
-
socket.on('error', this.connectionManager.handleError('incoming', record));
|
|
263
|
+
socket.on('error', this.smartProxy.connectionManager.handleError('incoming', record));
|
|
279
264
|
|
|
280
265
|
// Add close/end handlers to catch immediate disconnections
|
|
281
266
|
socket.once('close', () => {
|
|
@@ -289,7 +274,7 @@ export class RouteConnectionHandler {
|
|
|
289
274
|
clearTimeout(initialTimeout);
|
|
290
275
|
initialTimeout = null;
|
|
291
276
|
}
|
|
292
|
-
this.connectionManager.cleanupConnection(record, 'closed_before_data');
|
|
277
|
+
this.smartProxy.connectionManager.cleanupConnection(record, 'closed_before_data');
|
|
293
278
|
}
|
|
294
279
|
});
|
|
295
280
|
|
|
@@ -311,7 +296,7 @@ export class RouteConnectionHandler {
|
|
|
311
296
|
// Handler for processing initial data (after potential PROXY protocol)
|
|
312
297
|
const processInitialData = (chunk: Buffer) => {
|
|
313
298
|
// Block non-TLS connections on port 443
|
|
314
|
-
if (!this.tlsManager.isTlsHandshake(chunk) && localPort === 443) {
|
|
299
|
+
if (!this.smartProxy.tlsManager.isTlsHandshake(chunk) && localPort === 443) {
|
|
315
300
|
logger.log('warn', `Non-TLS connection ${connectionId} detected on port 443. Terminating connection - only TLS traffic is allowed on standard HTTPS port.`, {
|
|
316
301
|
connectionId,
|
|
317
302
|
message: 'Terminating connection - only TLS traffic is allowed on standard HTTPS port.',
|
|
@@ -319,20 +304,20 @@ export class RouteConnectionHandler {
|
|
|
319
304
|
});
|
|
320
305
|
if (record.incomingTerminationReason === null) {
|
|
321
306
|
record.incomingTerminationReason = 'non_tls_blocked';
|
|
322
|
-
this.connectionManager.incrementTerminationStat('incoming', 'non_tls_blocked');
|
|
307
|
+
this.smartProxy.connectionManager.incrementTerminationStat('incoming', 'non_tls_blocked');
|
|
323
308
|
}
|
|
324
309
|
socket.end();
|
|
325
|
-
this.connectionManager.cleanupConnection(record, 'non_tls_blocked');
|
|
310
|
+
this.smartProxy.connectionManager.cleanupConnection(record, 'non_tls_blocked');
|
|
326
311
|
return;
|
|
327
312
|
}
|
|
328
313
|
|
|
329
314
|
// Check if this looks like a TLS handshake
|
|
330
315
|
let serverName = '';
|
|
331
|
-
if (this.tlsManager.isTlsHandshake(chunk)) {
|
|
316
|
+
if (this.smartProxy.tlsManager.isTlsHandshake(chunk)) {
|
|
332
317
|
record.isTLS = true;
|
|
333
318
|
|
|
334
319
|
// Check for ClientHello to extract SNI
|
|
335
|
-
if (this.tlsManager.isClientHello(chunk)) {
|
|
320
|
+
if (this.smartProxy.tlsManager.isClientHello(chunk)) {
|
|
336
321
|
// Create connection info for SNI extraction
|
|
337
322
|
const connInfo = {
|
|
338
323
|
sourceIp: record.remoteIP,
|
|
@@ -342,20 +327,20 @@ export class RouteConnectionHandler {
|
|
|
342
327
|
};
|
|
343
328
|
|
|
344
329
|
// Extract SNI
|
|
345
|
-
serverName = this.tlsManager.extractSNI(chunk, connInfo) || '';
|
|
330
|
+
serverName = this.smartProxy.tlsManager.extractSNI(chunk, connInfo) || '';
|
|
346
331
|
|
|
347
332
|
// Lock the connection to the negotiated SNI
|
|
348
333
|
record.lockedDomain = serverName;
|
|
349
334
|
|
|
350
335
|
// Check if we should reject connections without SNI
|
|
351
|
-
if (!serverName && this.settings.allowSessionTicket === false) {
|
|
336
|
+
if (!serverName && this.smartProxy.settings.allowSessionTicket === false) {
|
|
352
337
|
logger.log('warn', `No SNI detected in TLS ClientHello for connection ${connectionId}; sending TLS alert`, {
|
|
353
338
|
connectionId,
|
|
354
339
|
component: 'route-handler'
|
|
355
340
|
});
|
|
356
341
|
if (record.incomingTerminationReason === null) {
|
|
357
342
|
record.incomingTerminationReason = 'session_ticket_blocked_no_sni';
|
|
358
|
-
this.connectionManager.incrementTerminationStat(
|
|
343
|
+
this.smartProxy.connectionManager.incrementTerminationStat(
|
|
359
344
|
'incoming',
|
|
360
345
|
'session_ticket_blocked_no_sni'
|
|
361
346
|
);
|
|
@@ -369,11 +354,11 @@ export class RouteConnectionHandler {
|
|
|
369
354
|
} catch {
|
|
370
355
|
socket.end();
|
|
371
356
|
}
|
|
372
|
-
this.connectionManager.cleanupConnection(record, 'session_ticket_blocked_no_sni');
|
|
357
|
+
this.smartProxy.connectionManager.cleanupConnection(record, 'session_ticket_blocked_no_sni');
|
|
373
358
|
return;
|
|
374
359
|
}
|
|
375
360
|
|
|
376
|
-
if (this.settings.enableDetailedLogging) {
|
|
361
|
+
if (this.smartProxy.settings.enableDetailedLogging) {
|
|
377
362
|
logger.log('info', `TLS connection with SNI`, {
|
|
378
363
|
connectionId,
|
|
379
364
|
serverName: serverName || '(empty)',
|
|
@@ -399,7 +384,7 @@ export class RouteConnectionHandler {
|
|
|
399
384
|
record.hasReceivedInitialData = true;
|
|
400
385
|
|
|
401
386
|
// Check if this is from a trusted proxy and might have PROXY protocol
|
|
402
|
-
if (this.settings.proxyIPs?.includes(socket.remoteAddress || '') && this.settings.acceptProxyProtocol !== false) {
|
|
387
|
+
if (this.smartProxy.settings.proxyIPs?.includes(socket.remoteAddress || '') && this.smartProxy.settings.acceptProxyProtocol !== false) {
|
|
403
388
|
// Check if this starts with PROXY protocol
|
|
404
389
|
if (chunk.toString('ascii', 0, Math.min(6, chunk.length)).startsWith('PROXY ')) {
|
|
405
390
|
try {
|
|
@@ -463,7 +448,7 @@ export class RouteConnectionHandler {
|
|
|
463
448
|
const remoteIP = record.remoteIP;
|
|
464
449
|
|
|
465
450
|
// Check if this is an HTTP proxy port
|
|
466
|
-
const isHttpProxyPort = this.settings.useHttpProxy?.includes(localPort);
|
|
451
|
+
const isHttpProxyPort = this.smartProxy.settings.useHttpProxy?.includes(localPort);
|
|
467
452
|
|
|
468
453
|
// For HTTP proxy ports without TLS, skip domain check since domain info comes from HTTP headers
|
|
469
454
|
const skipDomainCheck = isHttpProxyPort && !record.isTLS;
|
|
@@ -482,7 +467,7 @@ export class RouteConnectionHandler {
|
|
|
482
467
|
};
|
|
483
468
|
|
|
484
469
|
// Find matching route
|
|
485
|
-
const routeMatch = this.routeManager.findMatchingRoute(routeContext);
|
|
470
|
+
const routeMatch = this.smartProxy.routeManager.findMatchingRoute(routeContext);
|
|
486
471
|
|
|
487
472
|
if (!routeMatch) {
|
|
488
473
|
logger.log('warn', `No route found for ${serverName || 'connection'} on port ${localPort} (connection: ${connectionId})`, {
|
|
@@ -499,10 +484,10 @@ export class RouteConnectionHandler {
|
|
|
499
484
|
});
|
|
500
485
|
|
|
501
486
|
// Check default security settings
|
|
502
|
-
const defaultSecuritySettings = this.settings.defaults?.security;
|
|
487
|
+
const defaultSecuritySettings = this.smartProxy.settings.defaults?.security;
|
|
503
488
|
if (defaultSecuritySettings) {
|
|
504
489
|
if (defaultSecuritySettings.ipAllowList && defaultSecuritySettings.ipAllowList.length > 0) {
|
|
505
|
-
const isAllowed = this.securityManager.isIPAuthorized(
|
|
490
|
+
const isAllowed = this.smartProxy.securityManager.isIPAuthorized(
|
|
506
491
|
remoteIP,
|
|
507
492
|
defaultSecuritySettings.ipAllowList,
|
|
508
493
|
defaultSecuritySettings.ipBlockList || []
|
|
@@ -515,17 +500,17 @@ export class RouteConnectionHandler {
|
|
|
515
500
|
component: 'route-handler'
|
|
516
501
|
});
|
|
517
502
|
socket.end();
|
|
518
|
-
this.connectionManager.cleanupConnection(record, 'ip_blocked');
|
|
503
|
+
this.smartProxy.connectionManager.cleanupConnection(record, 'ip_blocked');
|
|
519
504
|
return;
|
|
520
505
|
}
|
|
521
506
|
}
|
|
522
507
|
}
|
|
523
508
|
|
|
524
509
|
// Setup direct connection with default settings
|
|
525
|
-
if (this.settings.defaults?.target) {
|
|
510
|
+
if (this.smartProxy.settings.defaults?.target) {
|
|
526
511
|
// Use defaults from configuration
|
|
527
|
-
const targetHost = this.settings.defaults.target.host;
|
|
528
|
-
const targetPort = this.settings.defaults.target.port;
|
|
512
|
+
const targetHost = this.smartProxy.settings.defaults.target.host;
|
|
513
|
+
const targetPort = this.smartProxy.settings.defaults.target.port;
|
|
529
514
|
|
|
530
515
|
return this.setupDirectConnection(
|
|
531
516
|
socket,
|
|
@@ -543,7 +528,7 @@ export class RouteConnectionHandler {
|
|
|
543
528
|
component: 'route-handler'
|
|
544
529
|
});
|
|
545
530
|
socket.end();
|
|
546
|
-
this.connectionManager.cleanupConnection(record, 'no_default_target');
|
|
531
|
+
this.smartProxy.connectionManager.cleanupConnection(record, 'no_default_target');
|
|
547
532
|
return;
|
|
548
533
|
}
|
|
549
534
|
}
|
|
@@ -551,7 +536,7 @@ export class RouteConnectionHandler {
|
|
|
551
536
|
// A matching route was found
|
|
552
537
|
const route = routeMatch.route;
|
|
553
538
|
|
|
554
|
-
if (this.settings.enableDetailedLogging) {
|
|
539
|
+
if (this.smartProxy.settings.enableDetailedLogging) {
|
|
555
540
|
logger.log('info', `Route matched`, {
|
|
556
541
|
connectionId,
|
|
557
542
|
routeName: route.name || 'unnamed',
|
|
@@ -565,7 +550,7 @@ export class RouteConnectionHandler {
|
|
|
565
550
|
if (route.security) {
|
|
566
551
|
// Check IP allow/block lists
|
|
567
552
|
if (route.security.ipAllowList || route.security.ipBlockList) {
|
|
568
|
-
const isIPAllowed = this.securityManager.isIPAuthorized(
|
|
553
|
+
const isIPAllowed = this.smartProxy.securityManager.isIPAuthorized(
|
|
569
554
|
remoteIP,
|
|
570
555
|
route.security.ipAllowList || [],
|
|
571
556
|
route.security.ipBlockList || []
|
|
@@ -579,7 +564,7 @@ export class RouteConnectionHandler {
|
|
|
579
564
|
component: 'route-handler'
|
|
580
565
|
});
|
|
581
566
|
socket.end();
|
|
582
|
-
this.connectionManager.cleanupConnection(record, 'route_ip_blocked');
|
|
567
|
+
this.smartProxy.connectionManager.cleanupConnection(record, 'route_ip_blocked');
|
|
583
568
|
return;
|
|
584
569
|
}
|
|
585
570
|
}
|
|
@@ -588,7 +573,7 @@ export class RouteConnectionHandler {
|
|
|
588
573
|
if (route.security.maxConnections !== undefined) {
|
|
589
574
|
// TODO: Implement per-route connection tracking
|
|
590
575
|
// For now, log that this feature is not yet implemented
|
|
591
|
-
if (this.settings.enableDetailedLogging) {
|
|
576
|
+
if (this.smartProxy.settings.enableDetailedLogging) {
|
|
592
577
|
logger.log('warn', `Route ${route.name} has maxConnections=${route.security.maxConnections} configured but per-route connection limits are not yet implemented`, {
|
|
593
578
|
connectionId,
|
|
594
579
|
routeName: route.name,
|
|
@@ -633,7 +618,7 @@ export class RouteConnectionHandler {
|
|
|
633
618
|
component: 'route-handler'
|
|
634
619
|
});
|
|
635
620
|
socket.end();
|
|
636
|
-
this.connectionManager.cleanupConnection(record, 'unknown_action');
|
|
621
|
+
this.smartProxy.connectionManager.cleanupConnection(record, 'unknown_action');
|
|
637
622
|
}
|
|
638
623
|
}
|
|
639
624
|
|
|
@@ -658,7 +643,7 @@ export class RouteConnectionHandler {
|
|
|
658
643
|
// The application should NOT interfere with these connections
|
|
659
644
|
|
|
660
645
|
// Log the connection for monitoring purposes
|
|
661
|
-
if (this.settings.enableDetailedLogging) {
|
|
646
|
+
if (this.smartProxy.settings.enableDetailedLogging) {
|
|
662
647
|
logger.log('info', `NFTables forwarding (kernel-level)`, {
|
|
663
648
|
connectionId: record.id,
|
|
664
649
|
source: `${record.remoteIP}:${socket.remotePort}`,
|
|
@@ -680,7 +665,7 @@ export class RouteConnectionHandler {
|
|
|
680
665
|
// Additional NFTables-specific logging if configured
|
|
681
666
|
if (action.nftables) {
|
|
682
667
|
const nftConfig = action.nftables;
|
|
683
|
-
if (this.settings.enableDetailedLogging) {
|
|
668
|
+
if (this.smartProxy.settings.enableDetailedLogging) {
|
|
684
669
|
logger.log('info', `NFTables config`, {
|
|
685
670
|
connectionId: record.id,
|
|
686
671
|
protocol: nftConfig.protocol || 'tcp',
|
|
@@ -701,7 +686,7 @@ export class RouteConnectionHandler {
|
|
|
701
686
|
|
|
702
687
|
// Set up cleanup when the socket eventually closes
|
|
703
688
|
socket.once('close', () => {
|
|
704
|
-
this.connectionManager.cleanupConnection(record, 'nftables_closed');
|
|
689
|
+
this.smartProxy.connectionManager.cleanupConnection(record, 'nftables_closed');
|
|
705
690
|
});
|
|
706
691
|
|
|
707
692
|
return;
|
|
@@ -714,7 +699,7 @@ export class RouteConnectionHandler {
|
|
|
714
699
|
component: 'route-handler'
|
|
715
700
|
});
|
|
716
701
|
socket.end();
|
|
717
|
-
this.connectionManager.cleanupConnection(record, 'missing_target');
|
|
702
|
+
this.smartProxy.connectionManager.cleanupConnection(record, 'missing_target');
|
|
718
703
|
return;
|
|
719
704
|
}
|
|
720
705
|
|
|
@@ -738,7 +723,7 @@ export class RouteConnectionHandler {
|
|
|
738
723
|
if (typeof action.target.host === 'function') {
|
|
739
724
|
try {
|
|
740
725
|
targetHost = action.target.host(routeContext);
|
|
741
|
-
if (this.settings.enableDetailedLogging) {
|
|
726
|
+
if (this.smartProxy.settings.enableDetailedLogging) {
|
|
742
727
|
logger.log('info', `Dynamic host resolved to ${Array.isArray(targetHost) ? targetHost.join(', ') : targetHost} for connection ${connectionId}`, {
|
|
743
728
|
connectionId,
|
|
744
729
|
targetHost: Array.isArray(targetHost) ? targetHost.join(', ') : targetHost,
|
|
@@ -752,7 +737,7 @@ export class RouteConnectionHandler {
|
|
|
752
737
|
component: 'route-handler'
|
|
753
738
|
});
|
|
754
739
|
socket.end();
|
|
755
|
-
this.connectionManager.cleanupConnection(record, 'host_mapping_error');
|
|
740
|
+
this.smartProxy.connectionManager.cleanupConnection(record, 'host_mapping_error');
|
|
756
741
|
return;
|
|
757
742
|
}
|
|
758
743
|
} else {
|
|
@@ -769,7 +754,7 @@ export class RouteConnectionHandler {
|
|
|
769
754
|
if (typeof action.target.port === 'function') {
|
|
770
755
|
try {
|
|
771
756
|
targetPort = action.target.port(routeContext);
|
|
772
|
-
if (this.settings.enableDetailedLogging) {
|
|
757
|
+
if (this.smartProxy.settings.enableDetailedLogging) {
|
|
773
758
|
logger.log('info', `Dynamic port mapping from ${record.localPort} to ${targetPort} for connection ${connectionId}`, {
|
|
774
759
|
connectionId,
|
|
775
760
|
sourcePort: record.localPort,
|
|
@@ -786,7 +771,7 @@ export class RouteConnectionHandler {
|
|
|
786
771
|
component: 'route-handler'
|
|
787
772
|
});
|
|
788
773
|
socket.end();
|
|
789
|
-
this.connectionManager.cleanupConnection(record, 'port_mapping_error');
|
|
774
|
+
this.smartProxy.connectionManager.cleanupConnection(record, 'port_mapping_error');
|
|
790
775
|
return;
|
|
791
776
|
}
|
|
792
777
|
} else if (action.target.port === 'preserve') {
|
|
@@ -805,7 +790,7 @@ export class RouteConnectionHandler {
|
|
|
805
790
|
switch (action.tls.mode) {
|
|
806
791
|
case 'passthrough':
|
|
807
792
|
// For TLS passthrough, just forward directly
|
|
808
|
-
if (this.settings.enableDetailedLogging) {
|
|
793
|
+
if (this.smartProxy.settings.enableDetailedLogging) {
|
|
809
794
|
logger.log('info', `Using TLS passthrough to ${selectedHost}:${targetPort} for connection ${connectionId}`, {
|
|
810
795
|
connectionId,
|
|
811
796
|
targetHost: selectedHost,
|
|
@@ -827,8 +812,8 @@ export class RouteConnectionHandler {
|
|
|
827
812
|
case 'terminate':
|
|
828
813
|
case 'terminate-and-reencrypt':
|
|
829
814
|
// For TLS termination, use HttpProxy
|
|
830
|
-
if (this.httpProxyBridge.getHttpProxy()) {
|
|
831
|
-
if (this.settings.enableDetailedLogging) {
|
|
815
|
+
if (this.smartProxy.httpProxyBridge.getHttpProxy()) {
|
|
816
|
+
if (this.smartProxy.settings.enableDetailedLogging) {
|
|
832
817
|
logger.log('info', `Using HttpProxy for TLS termination to ${Array.isArray(action.target.host) ? action.target.host.join(', ') : action.target.host} for connection ${connectionId}`, {
|
|
833
818
|
connectionId,
|
|
834
819
|
targetHost: action.target.host,
|
|
@@ -838,13 +823,13 @@ export class RouteConnectionHandler {
|
|
|
838
823
|
|
|
839
824
|
// If we have an initial chunk with TLS data, start processing it
|
|
840
825
|
if (initialChunk && record.isTLS) {
|
|
841
|
-
this.httpProxyBridge.forwardToHttpProxy(
|
|
826
|
+
this.smartProxy.httpProxyBridge.forwardToHttpProxy(
|
|
842
827
|
connectionId,
|
|
843
828
|
socket,
|
|
844
829
|
record,
|
|
845
830
|
initialChunk,
|
|
846
|
-
this.settings.httpProxyPort || 8443,
|
|
847
|
-
(reason) => this.connectionManager.cleanupConnection(record, reason)
|
|
831
|
+
this.smartProxy.settings.httpProxyPort || 8443,
|
|
832
|
+
(reason) => this.smartProxy.connectionManager.cleanupConnection(record, reason)
|
|
848
833
|
);
|
|
849
834
|
return;
|
|
850
835
|
}
|
|
@@ -855,7 +840,7 @@ export class RouteConnectionHandler {
|
|
|
855
840
|
component: 'route-handler'
|
|
856
841
|
});
|
|
857
842
|
socket.end();
|
|
858
|
-
this.connectionManager.cleanupConnection(record, 'tls_error');
|
|
843
|
+
this.smartProxy.connectionManager.cleanupConnection(record, 'tls_error');
|
|
859
844
|
return;
|
|
860
845
|
} else {
|
|
861
846
|
logger.log('error', `HttpProxy not available for TLS termination for connection ${connectionId}`, {
|
|
@@ -863,29 +848,29 @@ export class RouteConnectionHandler {
|
|
|
863
848
|
component: 'route-handler'
|
|
864
849
|
});
|
|
865
850
|
socket.end();
|
|
866
|
-
this.connectionManager.cleanupConnection(record, 'no_http_proxy');
|
|
851
|
+
this.smartProxy.connectionManager.cleanupConnection(record, 'no_http_proxy');
|
|
867
852
|
return;
|
|
868
853
|
}
|
|
869
854
|
}
|
|
870
855
|
} else {
|
|
871
856
|
// No TLS settings - check if this port should use HttpProxy
|
|
872
|
-
const isHttpProxyPort = this.settings.useHttpProxy?.includes(record.localPort);
|
|
857
|
+
const isHttpProxyPort = this.smartProxy.settings.useHttpProxy?.includes(record.localPort);
|
|
873
858
|
|
|
874
859
|
// Debug logging
|
|
875
|
-
if (this.settings.enableDetailedLogging) {
|
|
876
|
-
logger.log('debug', `Checking HttpProxy forwarding: port=${record.localPort}, useHttpProxy=${JSON.stringify(this.settings.useHttpProxy)}, isHttpProxyPort=${isHttpProxyPort}, hasHttpProxy=${!!this.httpProxyBridge.getHttpProxy()}`, {
|
|
860
|
+
if (this.smartProxy.settings.enableDetailedLogging) {
|
|
861
|
+
logger.log('debug', `Checking HttpProxy forwarding: port=${record.localPort}, useHttpProxy=${JSON.stringify(this.smartProxy.settings.useHttpProxy)}, isHttpProxyPort=${isHttpProxyPort}, hasHttpProxy=${!!this.smartProxy.httpProxyBridge.getHttpProxy()}`, {
|
|
877
862
|
connectionId,
|
|
878
863
|
localPort: record.localPort,
|
|
879
|
-
useHttpProxy: this.settings.useHttpProxy,
|
|
864
|
+
useHttpProxy: this.smartProxy.settings.useHttpProxy,
|
|
880
865
|
isHttpProxyPort,
|
|
881
|
-
hasHttpProxy: !!this.httpProxyBridge.getHttpProxy(),
|
|
866
|
+
hasHttpProxy: !!this.smartProxy.httpProxyBridge.getHttpProxy(),
|
|
882
867
|
component: 'route-handler'
|
|
883
868
|
});
|
|
884
869
|
}
|
|
885
870
|
|
|
886
|
-
if (isHttpProxyPort && this.httpProxyBridge.getHttpProxy()) {
|
|
871
|
+
if (isHttpProxyPort && this.smartProxy.httpProxyBridge.getHttpProxy()) {
|
|
887
872
|
// Forward non-TLS connections to HttpProxy if configured
|
|
888
|
-
if (this.settings.enableDetailedLogging) {
|
|
873
|
+
if (this.smartProxy.settings.enableDetailedLogging) {
|
|
889
874
|
logger.log('info', `Using HttpProxy for non-TLS connection ${connectionId} on port ${record.localPort}`, {
|
|
890
875
|
connectionId,
|
|
891
876
|
port: record.localPort,
|
|
@@ -893,18 +878,18 @@ export class RouteConnectionHandler {
|
|
|
893
878
|
});
|
|
894
879
|
}
|
|
895
880
|
|
|
896
|
-
this.httpProxyBridge.forwardToHttpProxy(
|
|
881
|
+
this.smartProxy.httpProxyBridge.forwardToHttpProxy(
|
|
897
882
|
connectionId,
|
|
898
883
|
socket,
|
|
899
884
|
record,
|
|
900
885
|
initialChunk,
|
|
901
|
-
this.settings.httpProxyPort || 8443,
|
|
902
|
-
(reason) => this.connectionManager.cleanupConnection(record, reason)
|
|
886
|
+
this.smartProxy.settings.httpProxyPort || 8443,
|
|
887
|
+
(reason) => this.smartProxy.connectionManager.cleanupConnection(record, reason)
|
|
903
888
|
);
|
|
904
889
|
return;
|
|
905
890
|
} else {
|
|
906
891
|
// Basic forwarding
|
|
907
|
-
if (this.settings.enableDetailedLogging) {
|
|
892
|
+
if (this.smartProxy.settings.enableDetailedLogging) {
|
|
908
893
|
logger.log('info', `Using basic forwarding to ${Array.isArray(action.target.host) ? action.target.host.join(', ') : action.target.host}:${action.target.port} for connection ${connectionId}`, {
|
|
909
894
|
connectionId,
|
|
910
895
|
targetHost: action.target.host,
|
|
@@ -977,7 +962,7 @@ export class RouteConnectionHandler {
|
|
|
977
962
|
component: 'route-handler'
|
|
978
963
|
});
|
|
979
964
|
socket.destroy();
|
|
980
|
-
this.connectionManager.cleanupConnection(record, 'missing_handler');
|
|
965
|
+
this.smartProxy.connectionManager.cleanupConnection(record, 'missing_handler');
|
|
981
966
|
return;
|
|
982
967
|
}
|
|
983
968
|
|
|
@@ -1052,7 +1037,7 @@ export class RouteConnectionHandler {
|
|
|
1052
1037
|
if (!socket.destroyed) {
|
|
1053
1038
|
socket.destroy();
|
|
1054
1039
|
}
|
|
1055
|
-
this.connectionManager.cleanupConnection(record, 'handler_error');
|
|
1040
|
+
this.smartProxy.connectionManager.cleanupConnection(record, 'handler_error');
|
|
1056
1041
|
});
|
|
1057
1042
|
} else {
|
|
1058
1043
|
// For sync handlers, emit on next tick
|
|
@@ -1074,7 +1059,7 @@ export class RouteConnectionHandler {
|
|
|
1074
1059
|
if (!socket.destroyed) {
|
|
1075
1060
|
socket.destroy();
|
|
1076
1061
|
}
|
|
1077
|
-
this.connectionManager.cleanupConnection(record, 'handler_error');
|
|
1062
|
+
this.smartProxy.connectionManager.cleanupConnection(record, 'handler_error');
|
|
1078
1063
|
}
|
|
1079
1064
|
}
|
|
1080
1065
|
|
|
@@ -1095,19 +1080,19 @@ export class RouteConnectionHandler {
|
|
|
1095
1080
|
|
|
1096
1081
|
// Determine target host and port if not provided
|
|
1097
1082
|
const finalTargetHost =
|
|
1098
|
-
targetHost || record.targetHost || this.settings.defaults?.target?.host || 'localhost';
|
|
1083
|
+
targetHost || record.targetHost || this.smartProxy.settings.defaults?.target?.host || 'localhost';
|
|
1099
1084
|
|
|
1100
1085
|
// Determine target port
|
|
1101
1086
|
const finalTargetPort =
|
|
1102
1087
|
targetPort ||
|
|
1103
1088
|
record.targetPort ||
|
|
1104
|
-
(overridePort !== undefined ? overridePort : this.settings.defaults?.target?.port || 443);
|
|
1089
|
+
(overridePort !== undefined ? overridePort : this.smartProxy.settings.defaults?.target?.port || 443);
|
|
1105
1090
|
|
|
1106
1091
|
// Update record with final target information
|
|
1107
1092
|
record.targetHost = finalTargetHost;
|
|
1108
1093
|
record.targetPort = finalTargetPort;
|
|
1109
1094
|
|
|
1110
|
-
if (this.settings.enableDetailedLogging) {
|
|
1095
|
+
if (this.smartProxy.settings.enableDetailedLogging) {
|
|
1111
1096
|
logger.log('info', `Setting up direct connection ${connectionId} to ${finalTargetHost}:${finalTargetPort}`, {
|
|
1112
1097
|
connectionId,
|
|
1113
1098
|
targetHost: finalTargetHost,
|
|
@@ -1123,7 +1108,7 @@ export class RouteConnectionHandler {
|
|
|
1123
1108
|
};
|
|
1124
1109
|
|
|
1125
1110
|
// Preserve source IP if configured
|
|
1126
|
-
if (this.settings.defaults?.preserveSourceIP || this.settings.preserveSourceIP) {
|
|
1111
|
+
if (this.smartProxy.settings.defaults?.preserveSourceIP || this.smartProxy.settings.preserveSourceIP) {
|
|
1127
1112
|
connectionOptions.localAddress = record.remoteIP.replace('::ffff:', '');
|
|
1128
1113
|
}
|
|
1129
1114
|
|
|
@@ -1132,13 +1117,18 @@ export class RouteConnectionHandler {
|
|
|
1132
1117
|
record.bytesReceived += initialChunk.length;
|
|
1133
1118
|
record.pendingData.push(Buffer.from(initialChunk));
|
|
1134
1119
|
record.pendingDataSize = initialChunk.length;
|
|
1120
|
+
|
|
1121
|
+
// Record bytes for metrics
|
|
1122
|
+
if (this.smartProxy.metricsCollector) {
|
|
1123
|
+
this.smartProxy.metricsCollector.recordBytes(record.id, initialChunk.length, 0);
|
|
1124
|
+
}
|
|
1135
1125
|
}
|
|
1136
1126
|
|
|
1137
1127
|
// Create the target socket with immediate error handling
|
|
1138
1128
|
const targetSocket = createSocketWithErrorHandler({
|
|
1139
1129
|
port: finalTargetPort,
|
|
1140
1130
|
host: finalTargetHost,
|
|
1141
|
-
timeout: this.settings.connectionTimeout || 30000, // Connection timeout (default: 30s)
|
|
1131
|
+
timeout: this.smartProxy.settings.connectionTimeout || 30000, // Connection timeout (default: 30s)
|
|
1142
1132
|
onError: (error) => {
|
|
1143
1133
|
// Connection failed - clean up everything immediately
|
|
1144
1134
|
// Check if connection record is still valid (client might have disconnected)
|
|
@@ -1188,10 +1178,10 @@ export class RouteConnectionHandler {
|
|
|
1188
1178
|
}
|
|
1189
1179
|
|
|
1190
1180
|
// Clean up the connection record - this is critical!
|
|
1191
|
-
this.connectionManager.cleanupConnection(record, `connection_failed_${(error as any).code || 'unknown'}`);
|
|
1181
|
+
this.smartProxy.connectionManager.cleanupConnection(record, `connection_failed_${(error as any).code || 'unknown'}`);
|
|
1192
1182
|
},
|
|
1193
1183
|
onConnect: async () => {
|
|
1194
|
-
if (this.settings.enableDetailedLogging) {
|
|
1184
|
+
if (this.smartProxy.settings.enableDetailedLogging) {
|
|
1195
1185
|
logger.log('info', `Connection ${connectionId} established to target ${finalTargetHost}:${finalTargetPort}`, {
|
|
1196
1186
|
connectionId,
|
|
1197
1187
|
targetHost: finalTargetHost,
|
|
@@ -1204,11 +1194,11 @@ export class RouteConnectionHandler {
|
|
|
1204
1194
|
targetSocket.removeAllListeners('error');
|
|
1205
1195
|
|
|
1206
1196
|
// Add the normal error handler for established connections
|
|
1207
|
-
targetSocket.on('error', this.connectionManager.handleError('outgoing', record));
|
|
1197
|
+
targetSocket.on('error', this.smartProxy.connectionManager.handleError('outgoing', record));
|
|
1208
1198
|
|
|
1209
1199
|
// Check if we should send PROXY protocol header
|
|
1210
1200
|
const shouldSendProxyProtocol = record.routeConfig?.action?.sendProxyProtocol ||
|
|
1211
|
-
this.settings.sendProxyProtocol;
|
|
1201
|
+
this.smartProxy.settings.sendProxyProtocol;
|
|
1212
1202
|
|
|
1213
1203
|
if (shouldSendProxyProtocol) {
|
|
1214
1204
|
try {
|
|
@@ -1260,7 +1250,7 @@ export class RouteConnectionHandler {
|
|
|
1260
1250
|
if (record.pendingData.length > 0) {
|
|
1261
1251
|
const combinedData = Buffer.concat(record.pendingData);
|
|
1262
1252
|
|
|
1263
|
-
if (this.settings.enableDetailedLogging) {
|
|
1253
|
+
if (this.smartProxy.settings.enableDetailedLogging) {
|
|
1264
1254
|
console.log(
|
|
1265
1255
|
`[${connectionId}] Forwarding ${combinedData.length} bytes of initial data to target`
|
|
1266
1256
|
);
|
|
@@ -1274,7 +1264,7 @@ export class RouteConnectionHandler {
|
|
|
1274
1264
|
error: err.message,
|
|
1275
1265
|
component: 'route-handler'
|
|
1276
1266
|
});
|
|
1277
|
-
return this.connectionManager.cleanupConnection(record, 'write_error');
|
|
1267
|
+
return this.smartProxy.connectionManager.cleanupConnection(record, 'write_error');
|
|
1278
1268
|
}
|
|
1279
1269
|
});
|
|
1280
1270
|
|
|
@@ -1290,22 +1280,32 @@ export class RouteConnectionHandler {
|
|
|
1290
1280
|
setupBidirectionalForwarding(incomingSocket, targetSocket, {
|
|
1291
1281
|
onClientData: (chunk) => {
|
|
1292
1282
|
record.bytesReceived += chunk.length;
|
|
1293
|
-
this.timeoutManager.updateActivity(record);
|
|
1283
|
+
this.smartProxy.timeoutManager.updateActivity(record);
|
|
1284
|
+
|
|
1285
|
+
// Record bytes for metrics
|
|
1286
|
+
if (this.smartProxy.metricsCollector) {
|
|
1287
|
+
this.smartProxy.metricsCollector.recordBytes(record.id, chunk.length, 0);
|
|
1288
|
+
}
|
|
1294
1289
|
},
|
|
1295
1290
|
onServerData: (chunk) => {
|
|
1296
1291
|
record.bytesSent += chunk.length;
|
|
1297
|
-
this.timeoutManager.updateActivity(record);
|
|
1292
|
+
this.smartProxy.timeoutManager.updateActivity(record);
|
|
1293
|
+
|
|
1294
|
+
// Record bytes for metrics
|
|
1295
|
+
if (this.smartProxy.metricsCollector) {
|
|
1296
|
+
this.smartProxy.metricsCollector.recordBytes(record.id, 0, chunk.length);
|
|
1297
|
+
}
|
|
1298
1298
|
},
|
|
1299
1299
|
onCleanup: (reason) => {
|
|
1300
|
-
this.connectionManager.cleanupConnection(record, reason);
|
|
1300
|
+
this.smartProxy.connectionManager.cleanupConnection(record, reason);
|
|
1301
1301
|
},
|
|
1302
1302
|
enableHalfOpen: false // Default: close both when one closes (required for proxy chains)
|
|
1303
1303
|
});
|
|
1304
1304
|
|
|
1305
1305
|
// Apply timeouts if keep-alive is enabled
|
|
1306
1306
|
if (record.hasKeepAlive) {
|
|
1307
|
-
socket.setTimeout(this.settings.socketTimeout || 3600000);
|
|
1308
|
-
targetSocket.setTimeout(this.settings.socketTimeout || 3600000);
|
|
1307
|
+
socket.setTimeout(this.smartProxy.settings.socketTimeout || 3600000);
|
|
1308
|
+
targetSocket.setTimeout(this.smartProxy.settings.socketTimeout || 3600000);
|
|
1309
1309
|
}
|
|
1310
1310
|
|
|
1311
1311
|
// Log successful connection
|
|
@@ -1333,11 +1333,11 @@ export class RouteConnectionHandler {
|
|
|
1333
1333
|
};
|
|
1334
1334
|
|
|
1335
1335
|
// Create a renegotiation handler function
|
|
1336
|
-
const renegotiationHandler = this.tlsManager.createRenegotiationHandler(
|
|
1336
|
+
const renegotiationHandler = this.smartProxy.tlsManager.createRenegotiationHandler(
|
|
1337
1337
|
connectionId,
|
|
1338
1338
|
serverName,
|
|
1339
1339
|
connInfo,
|
|
1340
|
-
(_connectionId, reason) => this.connectionManager.cleanupConnection(record, reason)
|
|
1340
|
+
(_connectionId, reason) => this.smartProxy.connectionManager.cleanupConnection(record, reason)
|
|
1341
1341
|
);
|
|
1342
1342
|
|
|
1343
1343
|
// Store the handler in the connection record so we can remove it during cleanup
|
|
@@ -1346,7 +1346,7 @@ export class RouteConnectionHandler {
|
|
|
1346
1346
|
// Add the handler to the socket
|
|
1347
1347
|
socket.on('data', renegotiationHandler);
|
|
1348
1348
|
|
|
1349
|
-
if (this.settings.enableDetailedLogging) {
|
|
1349
|
+
if (this.smartProxy.settings.enableDetailedLogging) {
|
|
1350
1350
|
logger.log('info', `TLS renegotiation handler installed for connection ${connectionId} with SNI ${serverName}`, {
|
|
1351
1351
|
connectionId,
|
|
1352
1352
|
serverName,
|
|
@@ -1356,13 +1356,13 @@ export class RouteConnectionHandler {
|
|
|
1356
1356
|
}
|
|
1357
1357
|
|
|
1358
1358
|
// Set connection timeout
|
|
1359
|
-
record.cleanupTimer = this.timeoutManager.setupConnectionTimeout(record, (record, reason) => {
|
|
1359
|
+
record.cleanupTimer = this.smartProxy.timeoutManager.setupConnectionTimeout(record, (record, reason) => {
|
|
1360
1360
|
logger.log('warn', `Connection ${connectionId} from ${record.remoteIP} exceeded max lifetime, forcing cleanup`, {
|
|
1361
1361
|
connectionId,
|
|
1362
1362
|
remoteIP: record.remoteIP,
|
|
1363
1363
|
component: 'route-handler'
|
|
1364
1364
|
});
|
|
1365
|
-
this.connectionManager.cleanupConnection(record, reason);
|
|
1365
|
+
this.smartProxy.connectionManager.cleanupConnection(record, reason);
|
|
1366
1366
|
});
|
|
1367
1367
|
|
|
1368
1368
|
// Mark TLS handshake as complete for TLS connections
|
|
@@ -1377,14 +1377,14 @@ export class RouteConnectionHandler {
|
|
|
1377
1377
|
record.outgoingStartTime = Date.now();
|
|
1378
1378
|
|
|
1379
1379
|
// Apply socket optimizations
|
|
1380
|
-
targetSocket.setNoDelay(this.settings.noDelay);
|
|
1380
|
+
targetSocket.setNoDelay(this.smartProxy.settings.noDelay);
|
|
1381
1381
|
|
|
1382
1382
|
// Apply keep-alive settings if enabled
|
|
1383
|
-
if (this.settings.keepAlive) {
|
|
1384
|
-
targetSocket.setKeepAlive(true, this.settings.keepAliveInitialDelay);
|
|
1383
|
+
if (this.smartProxy.settings.keepAlive) {
|
|
1384
|
+
targetSocket.setKeepAlive(true, this.smartProxy.settings.keepAliveInitialDelay);
|
|
1385
1385
|
|
|
1386
1386
|
// Apply enhanced TCP keep-alive options if enabled
|
|
1387
|
-
if (this.settings.enableKeepAliveProbes) {
|
|
1387
|
+
if (this.smartProxy.settings.enableKeepAliveProbes) {
|
|
1388
1388
|
try {
|
|
1389
1389
|
if ('setKeepAliveProbes' in targetSocket) {
|
|
1390
1390
|
(targetSocket as any).setKeepAliveProbes(10);
|
|
@@ -1394,7 +1394,7 @@ export class RouteConnectionHandler {
|
|
|
1394
1394
|
}
|
|
1395
1395
|
} catch (err) {
|
|
1396
1396
|
// Ignore errors - these are optional enhancements
|
|
1397
|
-
if (this.settings.enableDetailedLogging) {
|
|
1397
|
+
if (this.smartProxy.settings.enableDetailedLogging) {
|
|
1398
1398
|
logger.log('warn', `Enhanced TCP keep-alive not supported for outgoing socket on connection ${connectionId}: ${err}`, {
|
|
1399
1399
|
connectionId,
|
|
1400
1400
|
error: err,
|
|
@@ -1406,16 +1406,16 @@ export class RouteConnectionHandler {
|
|
|
1406
1406
|
}
|
|
1407
1407
|
|
|
1408
1408
|
// Setup error handlers for incoming socket
|
|
1409
|
-
socket.on('error', this.connectionManager.handleError('incoming', record));
|
|
1409
|
+
socket.on('error', this.smartProxy.connectionManager.handleError('incoming', record));
|
|
1410
1410
|
|
|
1411
1411
|
// Handle timeouts with keep-alive awareness
|
|
1412
1412
|
socket.on('timeout', () => {
|
|
1413
1413
|
// For keep-alive connections, just log a warning instead of closing
|
|
1414
1414
|
if (record.hasKeepAlive) {
|
|
1415
|
-
logger.log('warn', `Timeout event on incoming keep-alive connection ${connectionId} from ${record.remoteIP} after ${plugins.prettyMs(this.settings.socketTimeout || 3600000)}. Connection preserved.`, {
|
|
1415
|
+
logger.log('warn', `Timeout event on incoming keep-alive connection ${connectionId} from ${record.remoteIP} after ${plugins.prettyMs(this.smartProxy.settings.socketTimeout || 3600000)}. Connection preserved.`, {
|
|
1416
1416
|
connectionId,
|
|
1417
1417
|
remoteIP: record.remoteIP,
|
|
1418
|
-
timeout: plugins.prettyMs(this.settings.socketTimeout || 3600000),
|
|
1418
|
+
timeout: plugins.prettyMs(this.smartProxy.settings.socketTimeout || 3600000),
|
|
1419
1419
|
status: 'Connection preserved',
|
|
1420
1420
|
component: 'route-handler'
|
|
1421
1421
|
});
|
|
@@ -1423,26 +1423,26 @@ export class RouteConnectionHandler {
|
|
|
1423
1423
|
}
|
|
1424
1424
|
|
|
1425
1425
|
// For non-keep-alive connections, proceed with normal cleanup
|
|
1426
|
-
logger.log('warn', `Timeout on incoming side for connection ${connectionId} from ${record.remoteIP} after ${plugins.prettyMs(this.settings.socketTimeout || 3600000)}`, {
|
|
1426
|
+
logger.log('warn', `Timeout on incoming side for connection ${connectionId} from ${record.remoteIP} after ${plugins.prettyMs(this.smartProxy.settings.socketTimeout || 3600000)}`, {
|
|
1427
1427
|
connectionId,
|
|
1428
1428
|
remoteIP: record.remoteIP,
|
|
1429
|
-
timeout: plugins.prettyMs(this.settings.socketTimeout || 3600000),
|
|
1429
|
+
timeout: plugins.prettyMs(this.smartProxy.settings.socketTimeout || 3600000),
|
|
1430
1430
|
component: 'route-handler'
|
|
1431
1431
|
});
|
|
1432
1432
|
if (record.incomingTerminationReason === null) {
|
|
1433
1433
|
record.incomingTerminationReason = 'timeout';
|
|
1434
|
-
this.connectionManager.incrementTerminationStat('incoming', 'timeout');
|
|
1434
|
+
this.smartProxy.connectionManager.incrementTerminationStat('incoming', 'timeout');
|
|
1435
1435
|
}
|
|
1436
|
-
this.connectionManager.cleanupConnection(record, 'timeout_incoming');
|
|
1436
|
+
this.smartProxy.connectionManager.cleanupConnection(record, 'timeout_incoming');
|
|
1437
1437
|
});
|
|
1438
1438
|
|
|
1439
1439
|
targetSocket.on('timeout', () => {
|
|
1440
1440
|
// For keep-alive connections, just log a warning instead of closing
|
|
1441
1441
|
if (record.hasKeepAlive) {
|
|
1442
|
-
logger.log('warn', `Timeout event on outgoing keep-alive connection ${connectionId} from ${record.remoteIP} after ${plugins.prettyMs(this.settings.socketTimeout || 3600000)}. Connection preserved.`, {
|
|
1442
|
+
logger.log('warn', `Timeout event on outgoing keep-alive connection ${connectionId} from ${record.remoteIP} after ${plugins.prettyMs(this.smartProxy.settings.socketTimeout || 3600000)}. Connection preserved.`, {
|
|
1443
1443
|
connectionId,
|
|
1444
1444
|
remoteIP: record.remoteIP,
|
|
1445
|
-
timeout: plugins.prettyMs(this.settings.socketTimeout || 3600000),
|
|
1445
|
+
timeout: plugins.prettyMs(this.smartProxy.settings.socketTimeout || 3600000),
|
|
1446
1446
|
status: 'Connection preserved',
|
|
1447
1447
|
component: 'route-handler'
|
|
1448
1448
|
});
|
|
@@ -1450,20 +1450,20 @@ export class RouteConnectionHandler {
|
|
|
1450
1450
|
}
|
|
1451
1451
|
|
|
1452
1452
|
// For non-keep-alive connections, proceed with normal cleanup
|
|
1453
|
-
logger.log('warn', `Timeout on outgoing side for connection ${connectionId} from ${record.remoteIP} after ${plugins.prettyMs(this.settings.socketTimeout || 3600000)}`, {
|
|
1453
|
+
logger.log('warn', `Timeout on outgoing side for connection ${connectionId} from ${record.remoteIP} after ${plugins.prettyMs(this.smartProxy.settings.socketTimeout || 3600000)}`, {
|
|
1454
1454
|
connectionId,
|
|
1455
1455
|
remoteIP: record.remoteIP,
|
|
1456
|
-
timeout: plugins.prettyMs(this.settings.socketTimeout || 3600000),
|
|
1456
|
+
timeout: plugins.prettyMs(this.smartProxy.settings.socketTimeout || 3600000),
|
|
1457
1457
|
component: 'route-handler'
|
|
1458
1458
|
});
|
|
1459
1459
|
if (record.outgoingTerminationReason === null) {
|
|
1460
1460
|
record.outgoingTerminationReason = 'timeout';
|
|
1461
|
-
this.connectionManager.incrementTerminationStat('outgoing', 'timeout');
|
|
1461
|
+
this.smartProxy.connectionManager.incrementTerminationStat('outgoing', 'timeout');
|
|
1462
1462
|
}
|
|
1463
|
-
this.connectionManager.cleanupConnection(record, 'timeout_outgoing');
|
|
1463
|
+
this.smartProxy.connectionManager.cleanupConnection(record, 'timeout_outgoing');
|
|
1464
1464
|
});
|
|
1465
1465
|
|
|
1466
1466
|
// Apply socket timeouts
|
|
1467
|
-
this.timeoutManager.applySocketTimeouts(record);
|
|
1467
|
+
this.smartProxy.timeoutManager.applySocketTimeouts(record);
|
|
1468
1468
|
}
|
|
1469
1469
|
}
|