@push.rocks/smartproxy 19.5.5 → 19.5.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/core/utils/enhanced-connection-pool.js +7 -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/lifecycle-component.js +23 -7
- 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/enhanced-connection-pool.ts +6 -1
- package/ts/core/utils/index.ts +1 -0
- package/ts/core/utils/lifecycle-component.ts +26 -6
- 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
|
@@ -9,6 +9,7 @@ export abstract class LifecycleComponent {
|
|
|
9
9
|
target: any;
|
|
10
10
|
event: string;
|
|
11
11
|
handler: Function;
|
|
12
|
+
actualHandler?: Function; // The actual handler registered (may be wrapped)
|
|
12
13
|
once?: boolean;
|
|
13
14
|
}> = [];
|
|
14
15
|
private childComponents: Set<LifecycleComponent> = new Set();
|
|
@@ -21,7 +22,11 @@ export abstract class LifecycleComponent {
|
|
|
21
22
|
protected setTimeout(handler: Function, timeout: number): NodeJS.Timeout {
|
|
22
23
|
if (this.isShuttingDown) {
|
|
23
24
|
// Return a dummy timer if shutting down
|
|
24
|
-
|
|
25
|
+
const dummyTimer = setTimeout(() => {}, 0);
|
|
26
|
+
if (typeof dummyTimer.unref === 'function') {
|
|
27
|
+
dummyTimer.unref();
|
|
28
|
+
}
|
|
29
|
+
return dummyTimer;
|
|
25
30
|
}
|
|
26
31
|
|
|
27
32
|
const wrappedHandler = () => {
|
|
@@ -33,6 +38,12 @@ export abstract class LifecycleComponent {
|
|
|
33
38
|
|
|
34
39
|
const timer = setTimeout(wrappedHandler, timeout);
|
|
35
40
|
this.timers.add(timer);
|
|
41
|
+
|
|
42
|
+
// Allow process to exit even with timer
|
|
43
|
+
if (typeof timer.unref === 'function') {
|
|
44
|
+
timer.unref();
|
|
45
|
+
}
|
|
46
|
+
|
|
36
47
|
return timer;
|
|
37
48
|
}
|
|
38
49
|
|
|
@@ -42,7 +53,12 @@ export abstract class LifecycleComponent {
|
|
|
42
53
|
protected setInterval(handler: Function, interval: number): NodeJS.Timeout {
|
|
43
54
|
if (this.isShuttingDown) {
|
|
44
55
|
// Return a dummy timer if shutting down
|
|
45
|
-
|
|
56
|
+
const dummyTimer = setInterval(() => {}, interval);
|
|
57
|
+
if (typeof dummyTimer.unref === 'function') {
|
|
58
|
+
dummyTimer.unref();
|
|
59
|
+
}
|
|
60
|
+
clearInterval(dummyTimer); // Clear immediately since we don't need it
|
|
61
|
+
return dummyTimer;
|
|
46
62
|
}
|
|
47
63
|
|
|
48
64
|
const wrappedHandler = () => {
|
|
@@ -121,11 +137,12 @@ export abstract class LifecycleComponent {
|
|
|
121
137
|
throw new Error('Target must support on() or addEventListener()');
|
|
122
138
|
}
|
|
123
139
|
|
|
124
|
-
// Store the original handler
|
|
140
|
+
// Store both the original handler and the actual handler registered
|
|
125
141
|
this.listeners.push({
|
|
126
142
|
target,
|
|
127
143
|
event,
|
|
128
144
|
handler,
|
|
145
|
+
actualHandler, // The handler that was actually registered (may be wrapped)
|
|
129
146
|
once: options?.once
|
|
130
147
|
});
|
|
131
148
|
}
|
|
@@ -208,12 +225,15 @@ export abstract class LifecycleComponent {
|
|
|
208
225
|
this.intervals.clear();
|
|
209
226
|
|
|
210
227
|
// Remove all event listeners
|
|
211
|
-
for (const { target, event, handler } of this.listeners) {
|
|
228
|
+
for (const { target, event, handler, actualHandler } of this.listeners) {
|
|
229
|
+
// Use actualHandler if available (for wrapped handlers), otherwise use the original handler
|
|
230
|
+
const handlerToRemove = actualHandler || handler;
|
|
231
|
+
|
|
212
232
|
// All listeners need to be removed, including 'once' listeners that might not have fired
|
|
213
233
|
if (typeof target.removeListener === 'function') {
|
|
214
|
-
target.removeListener(event,
|
|
234
|
+
target.removeListener(event, handlerToRemove);
|
|
215
235
|
} else if (typeof target.removeEventListener === 'function') {
|
|
216
|
-
target.removeEventListener(event,
|
|
236
|
+
target.removeEventListener(event, handlerToRemove);
|
|
217
237
|
}
|
|
218
238
|
}
|
|
219
239
|
this.listeners = [];
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import * as plugins from '../../plugins.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Safely cleanup a socket by removing all listeners and destroying it
|
|
5
|
+
* @param socket The socket to cleanup
|
|
6
|
+
* @param socketName Optional name for logging
|
|
7
|
+
*/
|
|
8
|
+
export function cleanupSocket(socket: plugins.net.Socket | plugins.tls.TLSSocket | null, socketName?: string): void {
|
|
9
|
+
if (!socket) return;
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
// Remove all event listeners
|
|
13
|
+
socket.removeAllListeners();
|
|
14
|
+
|
|
15
|
+
// Unpipe any streams
|
|
16
|
+
socket.unpipe();
|
|
17
|
+
|
|
18
|
+
// Destroy if not already destroyed
|
|
19
|
+
if (!socket.destroyed) {
|
|
20
|
+
socket.destroy();
|
|
21
|
+
}
|
|
22
|
+
} catch (err) {
|
|
23
|
+
console.error(`Error cleaning up socket${socketName ? ` (${socketName})` : ''}: ${err}`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Create a cleanup handler for paired sockets (client and server)
|
|
29
|
+
* @param clientSocket The client socket
|
|
30
|
+
* @param serverSocket The server socket (optional)
|
|
31
|
+
* @param onCleanup Optional callback when cleanup is done
|
|
32
|
+
* @returns A cleanup function that can be called multiple times safely
|
|
33
|
+
*/
|
|
34
|
+
export function createSocketCleanupHandler(
|
|
35
|
+
clientSocket: plugins.net.Socket | plugins.tls.TLSSocket,
|
|
36
|
+
serverSocket?: plugins.net.Socket | plugins.tls.TLSSocket | null,
|
|
37
|
+
onCleanup?: (reason: string) => void
|
|
38
|
+
): (reason: string) => void {
|
|
39
|
+
let cleanedUp = false;
|
|
40
|
+
|
|
41
|
+
return (reason: string) => {
|
|
42
|
+
if (cleanedUp) return;
|
|
43
|
+
cleanedUp = true;
|
|
44
|
+
|
|
45
|
+
// Cleanup both sockets
|
|
46
|
+
cleanupSocket(clientSocket, 'client');
|
|
47
|
+
if (serverSocket) {
|
|
48
|
+
cleanupSocket(serverSocket, 'server');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Call cleanup callback if provided
|
|
52
|
+
if (onCleanup) {
|
|
53
|
+
onCleanup(reason);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Setup socket error and close handlers with proper cleanup
|
|
60
|
+
* @param socket The socket to setup handlers for
|
|
61
|
+
* @param handleClose The cleanup function to call
|
|
62
|
+
* @param errorPrefix Optional prefix for error messages
|
|
63
|
+
*/
|
|
64
|
+
export function setupSocketHandlers(
|
|
65
|
+
socket: plugins.net.Socket | plugins.tls.TLSSocket,
|
|
66
|
+
handleClose: (reason: string) => void,
|
|
67
|
+
errorPrefix?: string
|
|
68
|
+
): void {
|
|
69
|
+
socket.on('error', (error) => {
|
|
70
|
+
const prefix = errorPrefix || 'Socket';
|
|
71
|
+
handleClose(`${prefix}_error: ${error.message}`);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
socket.on('close', () => {
|
|
75
|
+
const prefix = errorPrefix || 'socket';
|
|
76
|
+
handleClose(`${prefix}_closed`);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
socket.on('timeout', () => {
|
|
80
|
+
const prefix = errorPrefix || 'socket';
|
|
81
|
+
handleClose(`${prefix}_timeout`);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Pipe two sockets together with proper cleanup on either end
|
|
87
|
+
* @param socket1 First socket
|
|
88
|
+
* @param socket2 Second socket
|
|
89
|
+
*/
|
|
90
|
+
export function pipeSockets(
|
|
91
|
+
socket1: plugins.net.Socket | plugins.tls.TLSSocket,
|
|
92
|
+
socket2: plugins.net.Socket | plugins.tls.TLSSocket
|
|
93
|
+
): void {
|
|
94
|
+
socket1.pipe(socket2);
|
|
95
|
+
socket2.pipe(socket1);
|
|
96
|
+
}
|
|
@@ -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 { setupSocketHandlers } from '../../core/utils/socket-utils.js';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Handler for HTTP-only forwarding
|
|
@@ -40,12 +41,15 @@ export class HttpForwardingHandler extends ForwardingHandler {
|
|
|
40
41
|
const remoteAddress = socket.remoteAddress || 'unknown';
|
|
41
42
|
const localPort = socket.localPort || 80;
|
|
42
43
|
|
|
43
|
-
socket
|
|
44
|
+
// Set up socket handlers with proper cleanup
|
|
45
|
+
const handleClose = (reason: string) => {
|
|
44
46
|
this.emit(ForwardingHandlerEvents.DISCONNECTED, {
|
|
45
47
|
remoteAddress,
|
|
46
|
-
|
|
48
|
+
reason
|
|
47
49
|
});
|
|
48
|
-
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
setupSocketHandlers(socket, handleClose, 'http');
|
|
49
53
|
|
|
50
54
|
socket.on('error', (error) => {
|
|
51
55
|
this.emit(ForwardingHandlerEvents.ERROR, {
|
|
@@ -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, pipeSockets } from '../../core/utils/socket-utils.js';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Handler for HTTPS passthrough (SNI forwarding without termination)
|
|
@@ -50,35 +51,23 @@ export class HttpsPassthroughHandler extends ForwardingHandler {
|
|
|
50
51
|
// Create a connection to the target server
|
|
51
52
|
const serverSocket = plugins.net.connect(target.port, target.host);
|
|
52
53
|
|
|
53
|
-
//
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
remoteAddress,
|
|
57
|
-
error: `Target connection error: ${error.message}`
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
// Close the client socket if it's still open
|
|
61
|
-
if (!clientSocket.destroyed) {
|
|
62
|
-
clientSocket.destroy();
|
|
63
|
-
}
|
|
64
|
-
});
|
|
54
|
+
// Track data transfer for logging
|
|
55
|
+
let bytesSent = 0;
|
|
56
|
+
let bytesReceived = 0;
|
|
65
57
|
|
|
66
|
-
//
|
|
67
|
-
clientSocket
|
|
68
|
-
this.emit(ForwardingHandlerEvents.
|
|
58
|
+
// Create cleanup handler with our utility
|
|
59
|
+
const handleClose = createSocketCleanupHandler(clientSocket, serverSocket, (reason) => {
|
|
60
|
+
this.emit(ForwardingHandlerEvents.DISCONNECTED, {
|
|
69
61
|
remoteAddress,
|
|
70
|
-
|
|
62
|
+
bytesSent,
|
|
63
|
+
bytesReceived,
|
|
64
|
+
reason
|
|
71
65
|
});
|
|
72
|
-
|
|
73
|
-
// Close the server socket if it's still open
|
|
74
|
-
if (!serverSocket.destroyed) {
|
|
75
|
-
serverSocket.destroy();
|
|
76
|
-
}
|
|
77
66
|
});
|
|
78
67
|
|
|
79
|
-
//
|
|
80
|
-
|
|
81
|
-
|
|
68
|
+
// Setup error and close handlers for both sockets
|
|
69
|
+
setupSocketHandlers(serverSocket, handleClose, 'server');
|
|
70
|
+
setupSocketHandlers(clientSocket, handleClose, 'client');
|
|
82
71
|
|
|
83
72
|
// Forward data from client to server
|
|
84
73
|
clientSocket.on('data', (data) => {
|
|
@@ -128,48 +117,10 @@ export class HttpsPassthroughHandler extends ForwardingHandler {
|
|
|
128
117
|
});
|
|
129
118
|
});
|
|
130
119
|
|
|
131
|
-
// Handle connection close
|
|
132
|
-
const handleClose = () => {
|
|
133
|
-
if (!clientSocket.destroyed) {
|
|
134
|
-
clientSocket.destroy();
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
if (!serverSocket.destroyed) {
|
|
138
|
-
serverSocket.destroy();
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
this.emit(ForwardingHandlerEvents.DISCONNECTED, {
|
|
142
|
-
remoteAddress,
|
|
143
|
-
bytesSent,
|
|
144
|
-
bytesReceived
|
|
145
|
-
});
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
// Set up close handlers
|
|
149
|
-
clientSocket.on('close', handleClose);
|
|
150
|
-
serverSocket.on('close', handleClose);
|
|
151
|
-
|
|
152
120
|
// Set timeouts
|
|
153
121
|
const timeout = this.getTimeout();
|
|
154
122
|
clientSocket.setTimeout(timeout);
|
|
155
123
|
serverSocket.setTimeout(timeout);
|
|
156
|
-
|
|
157
|
-
// Handle timeouts
|
|
158
|
-
clientSocket.on('timeout', () => {
|
|
159
|
-
this.emit(ForwardingHandlerEvents.ERROR, {
|
|
160
|
-
remoteAddress,
|
|
161
|
-
error: 'Client connection timeout'
|
|
162
|
-
});
|
|
163
|
-
handleClose();
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
serverSocket.on('timeout', () => {
|
|
167
|
-
this.emit(ForwardingHandlerEvents.ERROR, {
|
|
168
|
-
remoteAddress,
|
|
169
|
-
error: 'Server connection timeout'
|
|
170
|
-
});
|
|
171
|
-
handleClose();
|
|
172
|
-
});
|
|
173
124
|
}
|
|
174
125
|
|
|
175
126
|
/**
|
|
@@ -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
|