@push.rocks/smartproxy 19.5.5 → 19.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist_ts/core/utils/index.d.ts +1 -0
- package/dist_ts/core/utils/index.js +2 -1
- package/dist_ts/core/utils/socket-utils.d.ts +28 -0
- package/dist_ts/core/utils/socket-utils.js +77 -0
- package/dist_ts/forwarding/handlers/http-handler.js +7 -4
- package/dist_ts/forwarding/handlers/https-passthrough-handler.js +14 -55
- package/dist_ts/forwarding/handlers/https-terminate-to-http-handler.js +52 -40
- package/dist_ts/forwarding/handlers/https-terminate-to-https-handler.js +31 -43
- package/dist_ts/proxies/http-proxy/connection-pool.js +4 -19
- package/dist_ts/proxies/http-proxy/http-proxy.js +3 -7
- package/dist_ts/proxies/smart-proxy/connection-manager.d.ts +0 -4
- package/dist_ts/proxies/smart-proxy/connection-manager.js +7 -30
- package/dist_ts/proxies/smart-proxy/http-proxy-bridge.js +13 -2
- package/dist_ts/proxies/smart-proxy/port-manager.js +3 -3
- package/dist_ts/proxies/smart-proxy/route-connection-handler.js +35 -4
- package/package.json +1 -1
- package/ts/core/utils/index.ts +1 -0
- package/ts/core/utils/socket-utils.ts +96 -0
- package/ts/forwarding/handlers/http-handler.ts +7 -3
- package/ts/forwarding/handlers/https-passthrough-handler.ts +13 -62
- package/ts/forwarding/handlers/https-terminate-to-http-handler.ts +58 -46
- package/ts/forwarding/handlers/https-terminate-to-https-handler.ts +38 -53
- package/ts/proxies/http-proxy/connection-pool.ts +3 -16
- package/ts/proxies/http-proxy/http-proxy.ts +2 -5
- package/ts/proxies/smart-proxy/connection-manager.ts +6 -28
- package/ts/proxies/smart-proxy/http-proxy-bridge.ts +15 -1
- package/ts/proxies/smart-proxy/port-manager.ts +2 -2
- package/ts/proxies/smart-proxy/route-connection-handler.ts +39 -4
|
@@ -2,6 +2,7 @@ import * as plugins from '../../plugins.js';
|
|
|
2
2
|
import { ForwardingHandler } from './base-handler.js';
|
|
3
3
|
import type { IForwardConfig } from '../config/forwarding-types.js';
|
|
4
4
|
import { ForwardingHandlerEvents } from '../config/forwarding-types.js';
|
|
5
|
+
import { createSocketCleanupHandler, setupSocketHandlers } from '../../core/utils/socket-utils.js';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Handler for HTTPS termination with HTTP backend
|
|
@@ -95,76 +96,87 @@ export class HttpsTerminateToHttpHandler extends ForwardingHandler {
|
|
|
95
96
|
tls: true
|
|
96
97
|
});
|
|
97
98
|
|
|
98
|
-
//
|
|
99
|
-
|
|
100
|
-
|
|
99
|
+
// Variables to track connections
|
|
100
|
+
let backendSocket: plugins.net.Socket | null = null;
|
|
101
|
+
let dataBuffer = Buffer.alloc(0);
|
|
102
|
+
let connectionEstablished = false;
|
|
103
|
+
|
|
104
|
+
// Create cleanup handler for all sockets
|
|
105
|
+
const handleClose = createSocketCleanupHandler(tlsSocket, backendSocket, (reason) => {
|
|
106
|
+
this.emit(ForwardingHandlerEvents.DISCONNECTED, {
|
|
101
107
|
remoteAddress,
|
|
102
|
-
|
|
108
|
+
reason
|
|
103
109
|
});
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
tlsSocket.destroy();
|
|
107
|
-
}
|
|
110
|
+
dataBuffer = Buffer.alloc(0);
|
|
111
|
+
connectionEstablished = false;
|
|
108
112
|
});
|
|
109
113
|
|
|
110
|
-
//
|
|
111
|
-
|
|
112
|
-
// the requests here, but for simplicity, we'll just log the data
|
|
114
|
+
// Set up error handling with our cleanup utility
|
|
115
|
+
setupSocketHandlers(tlsSocket, handleClose, 'tls');
|
|
113
116
|
|
|
114
|
-
|
|
117
|
+
// Set timeout
|
|
118
|
+
const timeout = this.getTimeout();
|
|
119
|
+
tlsSocket.setTimeout(timeout);
|
|
120
|
+
|
|
121
|
+
tlsSocket.on('timeout', () => {
|
|
122
|
+
this.emit(ForwardingHandlerEvents.ERROR, {
|
|
123
|
+
remoteAddress,
|
|
124
|
+
error: 'TLS connection timeout'
|
|
125
|
+
});
|
|
126
|
+
handleClose('timeout');
|
|
127
|
+
});
|
|
115
128
|
|
|
129
|
+
// Handle TLS data
|
|
116
130
|
tlsSocket.on('data', (data) => {
|
|
131
|
+
// If backend connection already established, just forward the data
|
|
132
|
+
if (connectionEstablished && backendSocket && !backendSocket.destroyed) {
|
|
133
|
+
backendSocket.write(data);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
117
137
|
// Append to buffer
|
|
118
138
|
dataBuffer = Buffer.concat([dataBuffer, data]);
|
|
119
139
|
|
|
120
140
|
// Very basic HTTP parsing - in a real implementation, use http-parser
|
|
121
|
-
if (dataBuffer.includes(Buffer.from('\r\n\r\n'))) {
|
|
141
|
+
if (dataBuffer.includes(Buffer.from('\r\n\r\n')) && !connectionEstablished) {
|
|
122
142
|
const target = this.getTargetFromConfig();
|
|
123
143
|
|
|
124
|
-
//
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
144
|
+
// Create backend connection
|
|
145
|
+
backendSocket = plugins.net.connect(target.port, target.host, () => {
|
|
146
|
+
connectionEstablished = true;
|
|
147
|
+
|
|
148
|
+
// Send buffered data
|
|
149
|
+
if (dataBuffer.length > 0) {
|
|
150
|
+
backendSocket!.write(dataBuffer);
|
|
151
|
+
dataBuffer = Buffer.alloc(0);
|
|
152
|
+
}
|
|
128
153
|
|
|
129
154
|
// Set up bidirectional data flow
|
|
130
|
-
tlsSocket.pipe(
|
|
131
|
-
|
|
155
|
+
tlsSocket.pipe(backendSocket!);
|
|
156
|
+
backendSocket!.pipe(tlsSocket);
|
|
132
157
|
});
|
|
133
158
|
|
|
134
|
-
|
|
159
|
+
// Update the cleanup handler with the backend socket
|
|
160
|
+
const newHandleClose = createSocketCleanupHandler(tlsSocket, backendSocket, (reason) => {
|
|
161
|
+
this.emit(ForwardingHandlerEvents.DISCONNECTED, {
|
|
162
|
+
remoteAddress,
|
|
163
|
+
reason
|
|
164
|
+
});
|
|
165
|
+
dataBuffer = Buffer.alloc(0);
|
|
166
|
+
connectionEstablished = false;
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// Set up handlers for backend socket
|
|
170
|
+
setupSocketHandlers(backendSocket, newHandleClose, 'backend');
|
|
171
|
+
|
|
172
|
+
backendSocket.on('error', (error) => {
|
|
135
173
|
this.emit(ForwardingHandlerEvents.ERROR, {
|
|
136
174
|
remoteAddress,
|
|
137
175
|
error: `Target connection error: ${error.message}`
|
|
138
176
|
});
|
|
139
|
-
|
|
140
|
-
if (!tlsSocket.destroyed) {
|
|
141
|
-
tlsSocket.destroy();
|
|
142
|
-
}
|
|
143
177
|
});
|
|
144
178
|
}
|
|
145
179
|
});
|
|
146
|
-
|
|
147
|
-
// Handle close
|
|
148
|
-
tlsSocket.on('close', () => {
|
|
149
|
-
this.emit(ForwardingHandlerEvents.DISCONNECTED, {
|
|
150
|
-
remoteAddress
|
|
151
|
-
});
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
// Set timeout
|
|
155
|
-
const timeout = this.getTimeout();
|
|
156
|
-
tlsSocket.setTimeout(timeout);
|
|
157
|
-
|
|
158
|
-
tlsSocket.on('timeout', () => {
|
|
159
|
-
this.emit(ForwardingHandlerEvents.ERROR, {
|
|
160
|
-
remoteAddress,
|
|
161
|
-
error: 'TLS connection timeout'
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
if (!tlsSocket.destroyed) {
|
|
165
|
-
tlsSocket.destroy();
|
|
166
|
-
}
|
|
167
|
-
});
|
|
168
180
|
}
|
|
169
181
|
|
|
170
182
|
/**
|
|
@@ -2,6 +2,7 @@ import * as plugins from '../../plugins.js';
|
|
|
2
2
|
import { ForwardingHandler } from './base-handler.js';
|
|
3
3
|
import type { IForwardConfig } from '../config/forwarding-types.js';
|
|
4
4
|
import { ForwardingHandlerEvents } from '../config/forwarding-types.js';
|
|
5
|
+
import { createSocketCleanupHandler, setupSocketHandlers } from '../../core/utils/socket-utils.js';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Handler for HTTPS termination with HTTPS backend
|
|
@@ -93,28 +94,38 @@ export class HttpsTerminateToHttpsHandler extends ForwardingHandler {
|
|
|
93
94
|
tls: true
|
|
94
95
|
});
|
|
95
96
|
|
|
96
|
-
//
|
|
97
|
-
|
|
98
|
-
|
|
97
|
+
// Variable to track backend socket
|
|
98
|
+
let backendSocket: plugins.tls.TLSSocket | null = null;
|
|
99
|
+
|
|
100
|
+
// Create cleanup handler for both sockets
|
|
101
|
+
const handleClose = createSocketCleanupHandler(tlsSocket, backendSocket, (reason) => {
|
|
102
|
+
this.emit(ForwardingHandlerEvents.DISCONNECTED, {
|
|
99
103
|
remoteAddress,
|
|
100
|
-
|
|
104
|
+
reason
|
|
101
105
|
});
|
|
102
|
-
|
|
103
|
-
if (!tlsSocket.destroyed) {
|
|
104
|
-
tlsSocket.destroy();
|
|
105
|
-
}
|
|
106
106
|
});
|
|
107
107
|
|
|
108
|
-
//
|
|
109
|
-
|
|
110
|
-
|
|
108
|
+
// Set up error handling with our cleanup utility
|
|
109
|
+
setupSocketHandlers(tlsSocket, handleClose, 'tls');
|
|
110
|
+
|
|
111
|
+
// Set timeout
|
|
112
|
+
const timeout = this.getTimeout();
|
|
113
|
+
tlsSocket.setTimeout(timeout);
|
|
114
|
+
|
|
115
|
+
tlsSocket.on('timeout', () => {
|
|
116
|
+
this.emit(ForwardingHandlerEvents.ERROR, {
|
|
117
|
+
remoteAddress,
|
|
118
|
+
error: 'TLS connection timeout'
|
|
119
|
+
});
|
|
120
|
+
handleClose('timeout');
|
|
121
|
+
});
|
|
111
122
|
|
|
112
123
|
// Get the target from configuration
|
|
113
124
|
const target = this.getTargetFromConfig();
|
|
114
125
|
|
|
115
126
|
// Set up the connection to the HTTPS backend
|
|
116
127
|
const connectToBackend = () => {
|
|
117
|
-
|
|
128
|
+
backendSocket = plugins.tls.connect({
|
|
118
129
|
host: target.host,
|
|
119
130
|
port: target.port,
|
|
120
131
|
// In a real implementation, we would configure TLS options
|
|
@@ -127,30 +138,29 @@ export class HttpsTerminateToHttpsHandler extends ForwardingHandler {
|
|
|
127
138
|
});
|
|
128
139
|
|
|
129
140
|
// Set up bidirectional data flow
|
|
130
|
-
tlsSocket.pipe(backendSocket);
|
|
131
|
-
backendSocket
|
|
141
|
+
tlsSocket.pipe(backendSocket!);
|
|
142
|
+
backendSocket!.pipe(tlsSocket);
|
|
132
143
|
});
|
|
133
144
|
|
|
145
|
+
// Update the cleanup handler with the backend socket
|
|
146
|
+
const newHandleClose = createSocketCleanupHandler(tlsSocket, backendSocket, (reason) => {
|
|
147
|
+
this.emit(ForwardingHandlerEvents.DISCONNECTED, {
|
|
148
|
+
remoteAddress,
|
|
149
|
+
reason
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// Set up handlers for backend socket
|
|
154
|
+
setupSocketHandlers(backendSocket, newHandleClose, 'backend');
|
|
155
|
+
|
|
134
156
|
backendSocket.on('error', (error) => {
|
|
135
157
|
this.emit(ForwardingHandlerEvents.ERROR, {
|
|
136
158
|
remoteAddress,
|
|
137
159
|
error: `Backend connection error: ${error.message}`
|
|
138
160
|
});
|
|
139
|
-
|
|
140
|
-
if (!tlsSocket.destroyed) {
|
|
141
|
-
tlsSocket.destroy();
|
|
142
|
-
}
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
// Handle close
|
|
146
|
-
backendSocket.on('close', () => {
|
|
147
|
-
if (!tlsSocket.destroyed) {
|
|
148
|
-
tlsSocket.destroy();
|
|
149
|
-
}
|
|
150
161
|
});
|
|
151
162
|
|
|
152
|
-
// Set timeout
|
|
153
|
-
const timeout = this.getTimeout();
|
|
163
|
+
// Set timeout for backend socket
|
|
154
164
|
backendSocket.setTimeout(timeout);
|
|
155
165
|
|
|
156
166
|
backendSocket.on('timeout', () => {
|
|
@@ -158,10 +168,7 @@ export class HttpsTerminateToHttpsHandler extends ForwardingHandler {
|
|
|
158
168
|
remoteAddress,
|
|
159
169
|
error: 'Backend connection timeout'
|
|
160
170
|
});
|
|
161
|
-
|
|
162
|
-
if (!backendSocket.destroyed) {
|
|
163
|
-
backendSocket.destroy();
|
|
164
|
-
}
|
|
171
|
+
newHandleClose('backend_timeout');
|
|
165
172
|
});
|
|
166
173
|
};
|
|
167
174
|
|
|
@@ -169,28 +176,6 @@ export class HttpsTerminateToHttpsHandler extends ForwardingHandler {
|
|
|
169
176
|
tlsSocket.on('secure', () => {
|
|
170
177
|
connectToBackend();
|
|
171
178
|
});
|
|
172
|
-
|
|
173
|
-
// Handle close
|
|
174
|
-
tlsSocket.on('close', () => {
|
|
175
|
-
this.emit(ForwardingHandlerEvents.DISCONNECTED, {
|
|
176
|
-
remoteAddress
|
|
177
|
-
});
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
// Set timeout
|
|
181
|
-
const timeout = this.getTimeout();
|
|
182
|
-
tlsSocket.setTimeout(timeout);
|
|
183
|
-
|
|
184
|
-
tlsSocket.on('timeout', () => {
|
|
185
|
-
this.emit(ForwardingHandlerEvents.ERROR, {
|
|
186
|
-
remoteAddress,
|
|
187
|
-
error: 'TLS connection timeout'
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
if (!tlsSocket.destroyed) {
|
|
191
|
-
tlsSocket.destroy();
|
|
192
|
-
}
|
|
193
|
-
});
|
|
194
179
|
}
|
|
195
180
|
|
|
196
181
|
/**
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as plugins from '../../plugins.js';
|
|
2
2
|
import { type IHttpProxyOptions, type IConnectionEntry, type ILogger, createLogger } from './models/types.js';
|
|
3
|
+
import { cleanupSocket } from '../../core/utils/socket-utils.js';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Manages a pool of backend connections for efficient reuse
|
|
@@ -133,14 +134,7 @@ export class ConnectionPool {
|
|
|
133
134
|
if ((connection.isIdle && now - connection.lastUsed > idleTimeout) ||
|
|
134
135
|
connections.length > (this.options.connectionPoolSize || 50)) {
|
|
135
136
|
|
|
136
|
-
|
|
137
|
-
if (!connection.socket.destroyed) {
|
|
138
|
-
connection.socket.end();
|
|
139
|
-
connection.socket.destroy();
|
|
140
|
-
}
|
|
141
|
-
} catch (err) {
|
|
142
|
-
this.logger.error(`Error destroying pooled connection to ${host}`, err);
|
|
143
|
-
}
|
|
137
|
+
cleanupSocket(connection.socket, `pool-${host}-idle`);
|
|
144
138
|
|
|
145
139
|
connections.shift(); // Remove from pool
|
|
146
140
|
removed++;
|
|
@@ -170,14 +164,7 @@ export class ConnectionPool {
|
|
|
170
164
|
this.logger.debug(`Closing ${connections.length} connections to ${host}`);
|
|
171
165
|
|
|
172
166
|
for (const connection of connections) {
|
|
173
|
-
|
|
174
|
-
if (!connection.socket.destroyed) {
|
|
175
|
-
connection.socket.end();
|
|
176
|
-
connection.socket.destroy();
|
|
177
|
-
}
|
|
178
|
-
} catch (error) {
|
|
179
|
-
this.logger.error(`Error closing connection to ${host}:`, error);
|
|
180
|
-
}
|
|
167
|
+
cleanupSocket(connection.socket, `pool-${host}-close`);
|
|
181
168
|
}
|
|
182
169
|
}
|
|
183
170
|
|
|
@@ -18,6 +18,7 @@ import { RequestHandler, type IMetricsTracker } from './request-handler.js';
|
|
|
18
18
|
import { WebSocketHandler } from './websocket-handler.js';
|
|
19
19
|
import { ProxyRouter } from '../../routing/router/index.js';
|
|
20
20
|
import { RouteRouter } from '../../routing/router/route-router.js';
|
|
21
|
+
import { cleanupSocket } from '../../core/utils/socket-utils.js';
|
|
21
22
|
import { FunctionCache } from './function-cache.js';
|
|
22
23
|
|
|
23
24
|
/**
|
|
@@ -520,11 +521,7 @@ export class HttpProxy implements IMetricsTracker {
|
|
|
520
521
|
|
|
521
522
|
// Close all tracked sockets
|
|
522
523
|
for (const socket of this.socketMap.getArray()) {
|
|
523
|
-
|
|
524
|
-
socket.destroy();
|
|
525
|
-
} catch (error) {
|
|
526
|
-
this.logger.error('Error destroying socket', error);
|
|
527
|
-
}
|
|
524
|
+
cleanupSocket(socket, 'http-proxy-stop');
|
|
528
525
|
}
|
|
529
526
|
|
|
530
527
|
// Close all connection pool connections
|
|
@@ -4,6 +4,7 @@ import { SecurityManager } from './security-manager.js';
|
|
|
4
4
|
import { TimeoutManager } from './timeout-manager.js';
|
|
5
5
|
import { logger } from '../../core/utils/logger.js';
|
|
6
6
|
import { LifecycleComponent } from '../../core/utils/lifecycle-component.js';
|
|
7
|
+
import { cleanupSocket } from '../../core/utils/socket-utils.js';
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Manages connection lifecycle, tracking, and cleanup with performance optimizations
|
|
@@ -278,10 +279,10 @@ export class ConnectionManager extends LifecycleComponent {
|
|
|
278
279
|
}
|
|
279
280
|
|
|
280
281
|
// Handle socket cleanup without delay
|
|
281
|
-
|
|
282
|
+
cleanupSocket(record.incoming, `${record.id}-incoming`);
|
|
282
283
|
|
|
283
284
|
if (record.outgoing) {
|
|
284
|
-
|
|
285
|
+
cleanupSocket(record.outgoing, `${record.id}-outgoing`);
|
|
285
286
|
}
|
|
286
287
|
|
|
287
288
|
// Clear pendingData to avoid memory leaks
|
|
@@ -313,23 +314,6 @@ export class ConnectionManager extends LifecycleComponent {
|
|
|
313
314
|
}
|
|
314
315
|
}
|
|
315
316
|
|
|
316
|
-
/**
|
|
317
|
-
* Helper method to clean up a socket immediately
|
|
318
|
-
*/
|
|
319
|
-
private cleanupSocketImmediate(record: IConnectionRecord, side: 'incoming' | 'outgoing', socket: plugins.net.Socket): void {
|
|
320
|
-
try {
|
|
321
|
-
if (!socket.destroyed) {
|
|
322
|
-
socket.destroy();
|
|
323
|
-
}
|
|
324
|
-
} catch (err) {
|
|
325
|
-
logger.log('error', `Error destroying ${side} socket: ${err}`, {
|
|
326
|
-
connectionId: record.id,
|
|
327
|
-
side,
|
|
328
|
-
error: err,
|
|
329
|
-
component: 'connection-manager'
|
|
330
|
-
});
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
317
|
|
|
334
318
|
/**
|
|
335
319
|
* Creates a generic error handler for incoming or outgoing sockets
|
|
@@ -552,19 +536,13 @@ export class ConnectionManager extends LifecycleComponent {
|
|
|
552
536
|
record.cleanupTimer = undefined;
|
|
553
537
|
}
|
|
554
538
|
|
|
555
|
-
// Immediate destruction
|
|
539
|
+
// Immediate destruction using socket-utils
|
|
556
540
|
if (record.incoming) {
|
|
557
|
-
record.incoming.
|
|
558
|
-
if (!record.incoming.destroyed) {
|
|
559
|
-
record.incoming.destroy();
|
|
560
|
-
}
|
|
541
|
+
cleanupSocket(record.incoming, `${record.id}-incoming-shutdown`);
|
|
561
542
|
}
|
|
562
543
|
|
|
563
544
|
if (record.outgoing) {
|
|
564
|
-
record.outgoing.
|
|
565
|
-
if (!record.outgoing.destroyed) {
|
|
566
|
-
record.outgoing.destroy();
|
|
567
|
-
}
|
|
545
|
+
cleanupSocket(record.outgoing, `${record.id}-outgoing-shutdown`);
|
|
568
546
|
}
|
|
569
547
|
} catch (err) {
|
|
570
548
|
logger.log('error', `Error during connection cleanup: ${err}`, {
|
|
@@ -128,10 +128,24 @@ export class HttpProxyBridge {
|
|
|
128
128
|
proxySocket.pipe(socket);
|
|
129
129
|
|
|
130
130
|
// Handle cleanup
|
|
131
|
+
let cleanedUp = false;
|
|
131
132
|
const cleanup = (reason: string) => {
|
|
133
|
+
if (cleanedUp) return;
|
|
134
|
+
cleanedUp = true;
|
|
135
|
+
|
|
136
|
+
// Remove all event listeners to prevent memory leaks
|
|
137
|
+
socket.removeAllListeners('end');
|
|
138
|
+
socket.removeAllListeners('error');
|
|
139
|
+
proxySocket.removeAllListeners('end');
|
|
140
|
+
proxySocket.removeAllListeners('error');
|
|
141
|
+
|
|
132
142
|
socket.unpipe(proxySocket);
|
|
133
143
|
proxySocket.unpipe(socket);
|
|
134
|
-
|
|
144
|
+
|
|
145
|
+
if (!proxySocket.destroyed) {
|
|
146
|
+
proxySocket.destroy();
|
|
147
|
+
}
|
|
148
|
+
|
|
135
149
|
cleanupCallback(reason);
|
|
136
150
|
};
|
|
137
151
|
|
|
@@ -2,6 +2,7 @@ import * as plugins from '../../plugins.js';
|
|
|
2
2
|
import type { ISmartProxyOptions } from './models/interfaces.js';
|
|
3
3
|
import { RouteConnectionHandler } from './route-connection-handler.js';
|
|
4
4
|
import { logger } from '../../core/utils/logger.js';
|
|
5
|
+
import { cleanupSocket } from '../../core/utils/socket-utils.js';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* PortManager handles the dynamic creation and removal of port listeners
|
|
@@ -64,8 +65,7 @@ export class PortManager {
|
|
|
64
65
|
const server = plugins.net.createServer((socket) => {
|
|
65
66
|
// Check if shutting down
|
|
66
67
|
if (this.isShuttingDown) {
|
|
67
|
-
socket
|
|
68
|
-
socket.destroy();
|
|
68
|
+
cleanupSocket(socket, 'port-manager-shutdown');
|
|
69
69
|
return;
|
|
70
70
|
}
|
|
71
71
|
|
|
@@ -9,7 +9,7 @@ import { TlsManager } from './tls-manager.js';
|
|
|
9
9
|
import { HttpProxyBridge } from './http-proxy-bridge.js';
|
|
10
10
|
import { TimeoutManager } from './timeout-manager.js';
|
|
11
11
|
import { RouteManager } from './route-manager.js';
|
|
12
|
-
import
|
|
12
|
+
import { cleanupSocket } from '../../core/utils/socket-utils.js';
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Handles new connection processing and setup logic with support for route-based configuration
|
|
@@ -84,8 +84,7 @@ export class RouteConnectionHandler {
|
|
|
84
84
|
const ipValidation = this.securityManager.validateIP(remoteIP);
|
|
85
85
|
if (!ipValidation.allowed) {
|
|
86
86
|
logger.log('warn', `Connection rejected`, { remoteIP, reason: ipValidation.reason, component: 'route-handler' });
|
|
87
|
-
socket.
|
|
88
|
-
socket.destroy();
|
|
87
|
+
cleanupSocket(socket, `rejected-${ipValidation.reason}`);
|
|
89
88
|
return;
|
|
90
89
|
}
|
|
91
90
|
|
|
@@ -822,6 +821,38 @@ export class RouteConnectionHandler {
|
|
|
822
821
|
return;
|
|
823
822
|
}
|
|
824
823
|
|
|
824
|
+
// Track event listeners added by the handler so we can clean them up
|
|
825
|
+
const originalOn = socket.on.bind(socket);
|
|
826
|
+
const originalOnce = socket.once.bind(socket);
|
|
827
|
+
const trackedListeners: Array<{event: string; listener: (...args: any[]) => void}> = [];
|
|
828
|
+
|
|
829
|
+
// Override socket.on to track listeners
|
|
830
|
+
socket.on = function(event: string, listener: (...args: any[]) => void) {
|
|
831
|
+
trackedListeners.push({event, listener});
|
|
832
|
+
return originalOn(event, listener);
|
|
833
|
+
} as any;
|
|
834
|
+
|
|
835
|
+
// Override socket.once to track listeners
|
|
836
|
+
socket.once = function(event: string, listener: (...args: any[]) => void) {
|
|
837
|
+
trackedListeners.push({event, listener});
|
|
838
|
+
return originalOnce(event, listener);
|
|
839
|
+
} as any;
|
|
840
|
+
|
|
841
|
+
// Set up automatic cleanup when socket closes
|
|
842
|
+
const cleanupHandler = () => {
|
|
843
|
+
// Remove all tracked listeners
|
|
844
|
+
for (const {event, listener} of trackedListeners) {
|
|
845
|
+
socket.removeListener(event, listener);
|
|
846
|
+
}
|
|
847
|
+
// Restore original methods
|
|
848
|
+
socket.on = originalOn;
|
|
849
|
+
socket.once = originalOnce;
|
|
850
|
+
};
|
|
851
|
+
|
|
852
|
+
// Listen for socket close to trigger cleanup
|
|
853
|
+
originalOnce('close', cleanupHandler);
|
|
854
|
+
originalOnce('error', cleanupHandler);
|
|
855
|
+
|
|
825
856
|
// Create route context for the handler
|
|
826
857
|
const routeContext = this.createRouteContext({
|
|
827
858
|
connectionId: record.id,
|
|
@@ -855,6 +886,8 @@ export class RouteConnectionHandler {
|
|
|
855
886
|
error: error.message,
|
|
856
887
|
component: 'route-handler'
|
|
857
888
|
});
|
|
889
|
+
// Remove all event listeners before destroying to prevent memory leaks
|
|
890
|
+
socket.removeAllListeners();
|
|
858
891
|
if (!socket.destroyed) {
|
|
859
892
|
socket.destroy();
|
|
860
893
|
}
|
|
@@ -875,6 +908,8 @@ export class RouteConnectionHandler {
|
|
|
875
908
|
error: error.message,
|
|
876
909
|
component: 'route-handler'
|
|
877
910
|
});
|
|
911
|
+
// Remove all event listeners before destroying to prevent memory leaks
|
|
912
|
+
socket.removeAllListeners();
|
|
878
913
|
if (!socket.destroyed) {
|
|
879
914
|
socket.destroy();
|
|
880
915
|
}
|
|
@@ -1229,7 +1264,7 @@ export class RouteConnectionHandler {
|
|
|
1229
1264
|
connectionId,
|
|
1230
1265
|
serverName,
|
|
1231
1266
|
connInfo,
|
|
1232
|
-
(
|
|
1267
|
+
(_connectionId, reason) => this.connectionManager.initiateCleanupOnce(record, reason)
|
|
1233
1268
|
);
|
|
1234
1269
|
|
|
1235
1270
|
// Store the handler in the connection record so we can remove it during cleanup
|