@push.rocks/smartproxy 19.5.17 → 19.5.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist_ts/00_commitinfo_data.js +2 -2
- package/dist_ts/core/utils/socket-utils.d.ts +17 -14
- package/dist_ts/core/utils/socket-utils.js +61 -37
- package/dist_ts/forwarding/handlers/https-terminate-to-http-handler.js +47 -32
- package/dist_ts/forwarding/handlers/https-terminate-to-https-handler.js +51 -32
- package/dist_ts/proxies/smart-proxy/http-proxy-bridge.js +21 -26
- package/dist_ts/proxies/smart-proxy/route-connection-handler.js +21 -53
- package/package.json +1 -1
- package/readme.hints.md +80 -1
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/core/utils/socket-utils.ts +85 -45
- package/ts/forwarding/handlers/https-terminate-to-http-handler.ts +47 -33
- package/ts/forwarding/handlers/https-terminate-to-https-handler.ts +55 -35
- package/ts/proxies/smart-proxy/http-proxy-bridge.ts +20 -30
- package/ts/proxies/smart-proxy/route-connection-handler.ts +20 -64
|
@@ -67,37 +67,6 @@ export function cleanupSocket(
|
|
|
67
67
|
});
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
/**
|
|
71
|
-
* Create a cleanup handler for paired sockets (client and server)
|
|
72
|
-
* @param clientSocket The client socket
|
|
73
|
-
* @param serverSocket The server socket (optional)
|
|
74
|
-
* @param onCleanup Optional callback when cleanup is done
|
|
75
|
-
* @returns A cleanup function that can be called multiple times safely
|
|
76
|
-
* @deprecated Use createIndependentSocketHandlers for better half-open support
|
|
77
|
-
*/
|
|
78
|
-
export function createSocketCleanupHandler(
|
|
79
|
-
clientSocket: plugins.net.Socket | plugins.tls.TLSSocket,
|
|
80
|
-
serverSocket?: plugins.net.Socket | plugins.tls.TLSSocket | null,
|
|
81
|
-
onCleanup?: (reason: string) => void
|
|
82
|
-
): (reason: string) => void {
|
|
83
|
-
let cleanedUp = false;
|
|
84
|
-
|
|
85
|
-
return (reason: string) => {
|
|
86
|
-
if (cleanedUp) return;
|
|
87
|
-
cleanedUp = true;
|
|
88
|
-
|
|
89
|
-
// Cleanup both sockets (old behavior - too aggressive)
|
|
90
|
-
cleanupSocket(clientSocket, 'client', { immediate: true });
|
|
91
|
-
if (serverSocket) {
|
|
92
|
-
cleanupSocket(serverSocket, 'server', { immediate: true });
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Call cleanup callback if provided
|
|
96
|
-
if (onCleanup) {
|
|
97
|
-
onCleanup(reason);
|
|
98
|
-
}
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
70
|
|
|
102
71
|
/**
|
|
103
72
|
* Create independent cleanup handlers for paired sockets that support half-open connections
|
|
@@ -109,7 +78,8 @@ export function createSocketCleanupHandler(
|
|
|
109
78
|
export function createIndependentSocketHandlers(
|
|
110
79
|
clientSocket: plugins.net.Socket | plugins.tls.TLSSocket,
|
|
111
80
|
serverSocket: plugins.net.Socket | plugins.tls.TLSSocket,
|
|
112
|
-
onBothClosed: (reason: string) => void
|
|
81
|
+
onBothClosed: (reason: string) => void,
|
|
82
|
+
options: { enableHalfOpen?: boolean } = {}
|
|
113
83
|
): { cleanupClient: (reason: string) => Promise<void>, cleanupServer: (reason: string) => Promise<void> } {
|
|
114
84
|
let clientClosed = false;
|
|
115
85
|
let serverClosed = false;
|
|
@@ -127,8 +97,13 @@ export function createIndependentSocketHandlers(
|
|
|
127
97
|
clientClosed = true;
|
|
128
98
|
clientReason = reason;
|
|
129
99
|
|
|
130
|
-
//
|
|
131
|
-
if (!serverClosed &&
|
|
100
|
+
// Default behavior: close both sockets when one closes (required for proxy chains)
|
|
101
|
+
if (!serverClosed && !options.enableHalfOpen) {
|
|
102
|
+
serverSocket.destroy();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Half-open support (opt-in only)
|
|
106
|
+
if (!serverClosed && serverSocket.writable && options.enableHalfOpen) {
|
|
132
107
|
// Half-close: stop reading from client, let server finish
|
|
133
108
|
clientSocket.pause();
|
|
134
109
|
clientSocket.unpipe(serverSocket);
|
|
@@ -145,8 +120,13 @@ export function createIndependentSocketHandlers(
|
|
|
145
120
|
serverClosed = true;
|
|
146
121
|
serverReason = reason;
|
|
147
122
|
|
|
148
|
-
//
|
|
149
|
-
if (!clientClosed &&
|
|
123
|
+
// Default behavior: close both sockets when one closes (required for proxy chains)
|
|
124
|
+
if (!clientClosed && !options.enableHalfOpen) {
|
|
125
|
+
clientSocket.destroy();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Half-open support (opt-in only)
|
|
129
|
+
if (!clientClosed && clientSocket.writable && options.enableHalfOpen) {
|
|
150
130
|
// Half-close: stop reading from server, let client finish
|
|
151
131
|
serverSocket.pause();
|
|
152
132
|
serverSocket.unpipe(clientSocket);
|
|
@@ -195,16 +175,76 @@ export function setupSocketHandlers(
|
|
|
195
175
|
}
|
|
196
176
|
|
|
197
177
|
/**
|
|
198
|
-
*
|
|
199
|
-
* @param
|
|
200
|
-
* @param
|
|
178
|
+
* Setup bidirectional data forwarding between two sockets with proper cleanup
|
|
179
|
+
* @param clientSocket The client/incoming socket
|
|
180
|
+
* @param serverSocket The server/outgoing socket
|
|
181
|
+
* @param handlers Object containing optional handlers for data and cleanup
|
|
182
|
+
* @returns Cleanup functions for both sockets
|
|
201
183
|
*/
|
|
202
|
-
export function
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
184
|
+
export function setupBidirectionalForwarding(
|
|
185
|
+
clientSocket: plugins.net.Socket | plugins.tls.TLSSocket,
|
|
186
|
+
serverSocket: plugins.net.Socket | plugins.tls.TLSSocket,
|
|
187
|
+
handlers: {
|
|
188
|
+
onClientData?: (chunk: Buffer) => void;
|
|
189
|
+
onServerData?: (chunk: Buffer) => void;
|
|
190
|
+
onCleanup: (reason: string) => void;
|
|
191
|
+
enableHalfOpen?: boolean;
|
|
192
|
+
}
|
|
193
|
+
): { cleanupClient: (reason: string) => Promise<void>, cleanupServer: (reason: string) => Promise<void> } {
|
|
194
|
+
// Set up cleanup handlers
|
|
195
|
+
const { cleanupClient, cleanupServer } = createIndependentSocketHandlers(
|
|
196
|
+
clientSocket,
|
|
197
|
+
serverSocket,
|
|
198
|
+
handlers.onCleanup,
|
|
199
|
+
{ enableHalfOpen: handlers.enableHalfOpen }
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
// Set up error and close handlers
|
|
203
|
+
setupSocketHandlers(clientSocket, cleanupClient, undefined, 'client');
|
|
204
|
+
setupSocketHandlers(serverSocket, cleanupServer, undefined, 'server');
|
|
205
|
+
|
|
206
|
+
// Set up data forwarding with backpressure handling
|
|
207
|
+
clientSocket.on('data', (chunk: Buffer) => {
|
|
208
|
+
if (handlers.onClientData) {
|
|
209
|
+
handlers.onClientData(chunk);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (serverSocket.writable) {
|
|
213
|
+
const flushed = serverSocket.write(chunk);
|
|
214
|
+
|
|
215
|
+
// Handle backpressure
|
|
216
|
+
if (!flushed) {
|
|
217
|
+
clientSocket.pause();
|
|
218
|
+
serverSocket.once('drain', () => {
|
|
219
|
+
if (!clientSocket.destroyed) {
|
|
220
|
+
clientSocket.resume();
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
serverSocket.on('data', (chunk: Buffer) => {
|
|
228
|
+
if (handlers.onServerData) {
|
|
229
|
+
handlers.onServerData(chunk);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (clientSocket.writable) {
|
|
233
|
+
const flushed = clientSocket.write(chunk);
|
|
234
|
+
|
|
235
|
+
// Handle backpressure
|
|
236
|
+
if (!flushed) {
|
|
237
|
+
serverSocket.pause();
|
|
238
|
+
clientSocket.once('drain', () => {
|
|
239
|
+
if (!serverSocket.destroyed) {
|
|
240
|
+
serverSocket.resume();
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
return { cleanupClient, cleanupServer };
|
|
208
248
|
}
|
|
209
249
|
|
|
210
250
|
/**
|
|
@@ -2,7 +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 {
|
|
5
|
+
import { setupSocketHandlers, createSocketWithErrorHandler, setupBidirectionalForwarding } from '../../core/utils/socket-utils.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Handler for HTTPS termination with HTTP backend
|
|
@@ -100,19 +100,30 @@ export class HttpsTerminateToHttpHandler extends ForwardingHandler {
|
|
|
100
100
|
let backendSocket: plugins.net.Socket | null = null;
|
|
101
101
|
let dataBuffer = Buffer.alloc(0);
|
|
102
102
|
let connectionEstablished = false;
|
|
103
|
+
let forwardingSetup = false;
|
|
103
104
|
|
|
104
|
-
//
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
105
|
+
// Set up initial error handling for TLS socket
|
|
106
|
+
const tlsCleanupHandler = (reason: string) => {
|
|
107
|
+
if (!forwardingSetup) {
|
|
108
|
+
// If forwarding not set up yet, emit disconnected and cleanup
|
|
109
|
+
this.emit(ForwardingHandlerEvents.DISCONNECTED, {
|
|
110
|
+
remoteAddress,
|
|
111
|
+
reason
|
|
112
|
+
});
|
|
113
|
+
dataBuffer = Buffer.alloc(0);
|
|
114
|
+
connectionEstablished = false;
|
|
115
|
+
|
|
116
|
+
if (!tlsSocket.destroyed) {
|
|
117
|
+
tlsSocket.destroy();
|
|
118
|
+
}
|
|
119
|
+
if (backendSocket && !backendSocket.destroyed) {
|
|
120
|
+
backendSocket.destroy();
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// If forwarding is setup, setupBidirectionalForwarding will handle cleanup
|
|
124
|
+
};
|
|
113
125
|
|
|
114
|
-
|
|
115
|
-
setupSocketHandlers(tlsSocket, handleClose, undefined, 'tls');
|
|
126
|
+
setupSocketHandlers(tlsSocket, tlsCleanupHandler, undefined, 'tls');
|
|
116
127
|
|
|
117
128
|
// Set timeout
|
|
118
129
|
const timeout = this.getTimeout();
|
|
@@ -123,7 +134,7 @@ export class HttpsTerminateToHttpHandler extends ForwardingHandler {
|
|
|
123
134
|
remoteAddress,
|
|
124
135
|
error: 'TLS connection timeout'
|
|
125
136
|
});
|
|
126
|
-
|
|
137
|
+
tlsCleanupHandler('timeout');
|
|
127
138
|
});
|
|
128
139
|
|
|
129
140
|
// Handle TLS data
|
|
@@ -172,30 +183,33 @@ export class HttpsTerminateToHttpHandler extends ForwardingHandler {
|
|
|
172
183
|
dataBuffer = Buffer.alloc(0);
|
|
173
184
|
}
|
|
174
185
|
|
|
175
|
-
//
|
|
176
|
-
|
|
177
|
-
|
|
186
|
+
// Now set up bidirectional forwarding with proper cleanup
|
|
187
|
+
forwardingSetup = true;
|
|
188
|
+
setupBidirectionalForwarding(tlsSocket, backendSocket!, {
|
|
189
|
+
onCleanup: (reason) => {
|
|
190
|
+
this.emit(ForwardingHandlerEvents.DISCONNECTED, {
|
|
191
|
+
remoteAddress,
|
|
192
|
+
reason
|
|
193
|
+
});
|
|
194
|
+
dataBuffer = Buffer.alloc(0);
|
|
195
|
+
connectionEstablished = false;
|
|
196
|
+
forwardingSetup = false;
|
|
197
|
+
},
|
|
198
|
+
enableHalfOpen: false // Close both when one closes
|
|
199
|
+
});
|
|
178
200
|
}
|
|
179
201
|
});
|
|
180
202
|
|
|
181
|
-
//
|
|
182
|
-
const newHandleClose = createSocketCleanupHandler(tlsSocket, backendSocket, (reason) => {
|
|
183
|
-
this.emit(ForwardingHandlerEvents.DISCONNECTED, {
|
|
184
|
-
remoteAddress,
|
|
185
|
-
reason
|
|
186
|
-
});
|
|
187
|
-
dataBuffer = Buffer.alloc(0);
|
|
188
|
-
connectionEstablished = false;
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
// Set up handlers for backend socket
|
|
192
|
-
setupSocketHandlers(backendSocket, newHandleClose, undefined, 'backend');
|
|
193
|
-
|
|
203
|
+
// Additional error logging for backend socket
|
|
194
204
|
backendSocket.on('error', (error) => {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
205
|
+
if (!connectionEstablished) {
|
|
206
|
+
// Connection failed during setup
|
|
207
|
+
this.emit(ForwardingHandlerEvents.ERROR, {
|
|
208
|
+
remoteAddress,
|
|
209
|
+
error: `Target connection error: ${error.message}`
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
// If connected, setupBidirectionalForwarding handles cleanup
|
|
199
213
|
});
|
|
200
214
|
}
|
|
201
215
|
});
|
|
@@ -2,7 +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 {
|
|
5
|
+
import { setupSocketHandlers, createSocketWithErrorHandler, setupBidirectionalForwarding } from '../../core/utils/socket-utils.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Handler for HTTPS termination with HTTPS backend
|
|
@@ -96,17 +96,26 @@ export class HttpsTerminateToHttpsHandler extends ForwardingHandler {
|
|
|
96
96
|
|
|
97
97
|
// Variable to track backend socket
|
|
98
98
|
let backendSocket: plugins.tls.TLSSocket | null = null;
|
|
99
|
+
let isConnectedToBackend = false;
|
|
99
100
|
|
|
100
|
-
//
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
101
|
+
// Set up initial error handling for TLS socket
|
|
102
|
+
const tlsCleanupHandler = (reason: string) => {
|
|
103
|
+
if (!isConnectedToBackend) {
|
|
104
|
+
// If backend not connected yet, just emit disconnected event
|
|
105
|
+
this.emit(ForwardingHandlerEvents.DISCONNECTED, {
|
|
106
|
+
remoteAddress,
|
|
107
|
+
reason
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// Cleanup TLS socket if needed
|
|
111
|
+
if (!tlsSocket.destroyed) {
|
|
112
|
+
tlsSocket.destroy();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// If connected to backend, setupBidirectionalForwarding will handle cleanup
|
|
116
|
+
};
|
|
107
117
|
|
|
108
|
-
|
|
109
|
-
setupSocketHandlers(tlsSocket, handleClose, undefined, 'tls');
|
|
118
|
+
setupSocketHandlers(tlsSocket, tlsCleanupHandler, undefined, 'tls');
|
|
110
119
|
|
|
111
120
|
// Set timeout
|
|
112
121
|
const timeout = this.getTimeout();
|
|
@@ -117,7 +126,7 @@ export class HttpsTerminateToHttpsHandler extends ForwardingHandler {
|
|
|
117
126
|
remoteAddress,
|
|
118
127
|
error: 'TLS connection timeout'
|
|
119
128
|
});
|
|
120
|
-
|
|
129
|
+
tlsCleanupHandler('timeout');
|
|
121
130
|
});
|
|
122
131
|
|
|
123
132
|
// Get the target from configuration
|
|
@@ -131,44 +140,55 @@ export class HttpsTerminateToHttpsHandler extends ForwardingHandler {
|
|
|
131
140
|
// In a real implementation, we would configure TLS options
|
|
132
141
|
rejectUnauthorized: false // For testing only, never use in production
|
|
133
142
|
}, () => {
|
|
143
|
+
isConnectedToBackend = true;
|
|
144
|
+
|
|
134
145
|
this.emit(ForwardingHandlerEvents.DATA_FORWARDED, {
|
|
135
146
|
direction: 'outbound',
|
|
136
147
|
target: `${target.host}:${target.port}`,
|
|
137
148
|
tls: true
|
|
138
149
|
});
|
|
139
150
|
|
|
140
|
-
// Set up bidirectional
|
|
141
|
-
tlsSocket
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
151
|
+
// Set up bidirectional forwarding with proper cleanup
|
|
152
|
+
setupBidirectionalForwarding(tlsSocket, backendSocket!, {
|
|
153
|
+
onCleanup: (reason) => {
|
|
154
|
+
this.emit(ForwardingHandlerEvents.DISCONNECTED, {
|
|
155
|
+
remoteAddress,
|
|
156
|
+
reason
|
|
157
|
+
});
|
|
158
|
+
},
|
|
159
|
+
enableHalfOpen: false // Close both when one closes
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// Set timeout for backend socket
|
|
163
|
+
backendSocket!.setTimeout(timeout);
|
|
164
|
+
|
|
165
|
+
backendSocket!.on('timeout', () => {
|
|
166
|
+
this.emit(ForwardingHandlerEvents.ERROR, {
|
|
167
|
+
remoteAddress,
|
|
168
|
+
error: 'Backend connection timeout'
|
|
169
|
+
});
|
|
170
|
+
// Let setupBidirectionalForwarding handle the cleanup
|
|
150
171
|
});
|
|
151
172
|
});
|
|
152
173
|
|
|
153
|
-
//
|
|
154
|
-
setupSocketHandlers(backendSocket, newHandleClose, undefined, 'backend');
|
|
155
|
-
|
|
174
|
+
// Handle backend connection errors
|
|
156
175
|
backendSocket.on('error', (error) => {
|
|
157
176
|
this.emit(ForwardingHandlerEvents.ERROR, {
|
|
158
177
|
remoteAddress,
|
|
159
178
|
error: `Backend connection error: ${error.message}`
|
|
160
179
|
});
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
180
|
+
|
|
181
|
+
if (!isConnectedToBackend) {
|
|
182
|
+
// Connection failed, clean up TLS socket
|
|
183
|
+
if (!tlsSocket.destroyed) {
|
|
184
|
+
tlsSocket.destroy();
|
|
185
|
+
}
|
|
186
|
+
this.emit(ForwardingHandlerEvents.DISCONNECTED, {
|
|
187
|
+
remoteAddress,
|
|
188
|
+
reason: `backend_connection_failed: ${error.message}`
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
// If connected, let setupBidirectionalForwarding handle cleanup
|
|
172
192
|
});
|
|
173
193
|
};
|
|
174
194
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as plugins from '../../plugins.js';
|
|
2
2
|
import { HttpProxy } from '../http-proxy/index.js';
|
|
3
|
+
import { setupBidirectionalForwarding } from '../../core/utils/socket-utils.js';
|
|
3
4
|
import type { IConnectionRecord, ISmartProxyOptions } from './models/interfaces.js';
|
|
4
5
|
import type { IRouteConfig } from './models/route-types.js';
|
|
5
6
|
|
|
@@ -123,36 +124,25 @@ export class HttpProxyBridge {
|
|
|
123
124
|
proxySocket.write(initialChunk);
|
|
124
125
|
}
|
|
125
126
|
|
|
126
|
-
//
|
|
127
|
-
socket
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
if (!proxySocket.destroyed) {
|
|
146
|
-
proxySocket.destroy();
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
cleanupCallback(reason);
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
socket.on('end', () => cleanup('socket_end'));
|
|
153
|
-
socket.on('error', () => cleanup('socket_error'));
|
|
154
|
-
proxySocket.on('end', () => cleanup('proxy_end'));
|
|
155
|
-
proxySocket.on('error', () => cleanup('proxy_error'));
|
|
127
|
+
// Use centralized bidirectional forwarding
|
|
128
|
+
setupBidirectionalForwarding(socket, proxySocket, {
|
|
129
|
+
onClientData: (chunk) => {
|
|
130
|
+
// Update stats if needed
|
|
131
|
+
if (record) {
|
|
132
|
+
record.bytesReceived += chunk.length;
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
onServerData: (chunk) => {
|
|
136
|
+
// Update stats if needed
|
|
137
|
+
if (record) {
|
|
138
|
+
record.bytesSent += chunk.length;
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
onCleanup: (reason) => {
|
|
142
|
+
cleanupCallback(reason);
|
|
143
|
+
},
|
|
144
|
+
enableHalfOpen: false // Close both when one closes (required for proxy chains)
|
|
145
|
+
});
|
|
156
146
|
}
|
|
157
147
|
|
|
158
148
|
/**
|
|
@@ -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 { cleanupSocket, createIndependentSocketHandlers, setupSocketHandlers, createSocketWithErrorHandler } from '../../core/utils/socket-utils.js';
|
|
12
|
+
import { cleanupSocket, createIndependentSocketHandlers, setupSocketHandlers, createSocketWithErrorHandler, setupBidirectionalForwarding } 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
|
|
@@ -1137,65 +1137,27 @@ export class RouteConnectionHandler {
|
|
|
1137
1137
|
record.pendingDataSize = 0;
|
|
1138
1138
|
}
|
|
1139
1139
|
|
|
1140
|
-
//
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1140
|
+
// Use centralized bidirectional forwarding setup
|
|
1141
|
+
setupBidirectionalForwarding(socket, targetSocket, {
|
|
1142
|
+
onClientData: (chunk) => {
|
|
1143
|
+
record.bytesReceived += chunk.length;
|
|
1144
|
+
this.timeoutManager.updateActivity(record);
|
|
1145
|
+
},
|
|
1146
|
+
onServerData: (chunk) => {
|
|
1147
|
+
record.bytesSent += chunk.length;
|
|
1148
|
+
this.timeoutManager.updateActivity(record);
|
|
1149
|
+
},
|
|
1150
|
+
onCleanup: (reason) => {
|
|
1145
1151
|
this.connectionManager.cleanupConnection(record, reason);
|
|
1146
|
-
}
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
// Setup socket handlers with custom timeout handling
|
|
1150
|
-
setupSocketHandlers(socket, cleanupClient, (sock) => {
|
|
1151
|
-
// Don't close on timeout for keep-alive connections
|
|
1152
|
-
if (record.hasKeepAlive) {
|
|
1153
|
-
sock.setTimeout(this.settings.socketTimeout || 3600000);
|
|
1154
|
-
}
|
|
1155
|
-
}, 'client');
|
|
1156
|
-
|
|
1157
|
-
setupSocketHandlers(targetSocket, cleanupServer, (sock) => {
|
|
1158
|
-
// Don't close on timeout for keep-alive connections
|
|
1159
|
-
if (record.hasKeepAlive) {
|
|
1160
|
-
sock.setTimeout(this.settings.socketTimeout || 3600000);
|
|
1161
|
-
}
|
|
1162
|
-
}, 'server');
|
|
1163
|
-
|
|
1164
|
-
// Forward data from client to target with backpressure handling
|
|
1165
|
-
socket.on('data', (chunk: Buffer) => {
|
|
1166
|
-
record.bytesReceived += chunk.length;
|
|
1167
|
-
this.timeoutManager.updateActivity(record);
|
|
1168
|
-
|
|
1169
|
-
if (targetSocket.writable) {
|
|
1170
|
-
const flushed = targetSocket.write(chunk);
|
|
1171
|
-
|
|
1172
|
-
// Handle backpressure
|
|
1173
|
-
if (!flushed) {
|
|
1174
|
-
socket.pause();
|
|
1175
|
-
targetSocket.once('drain', () => {
|
|
1176
|
-
socket.resume();
|
|
1177
|
-
});
|
|
1178
|
-
}
|
|
1179
|
-
}
|
|
1180
|
-
});
|
|
1181
|
-
|
|
1182
|
-
// Forward data from target to client with backpressure handling
|
|
1183
|
-
targetSocket.on('data', (chunk: Buffer) => {
|
|
1184
|
-
record.bytesSent += chunk.length;
|
|
1185
|
-
this.timeoutManager.updateActivity(record);
|
|
1186
|
-
|
|
1187
|
-
if (socket.writable) {
|
|
1188
|
-
const flushed = socket.write(chunk);
|
|
1189
|
-
|
|
1190
|
-
// Handle backpressure
|
|
1191
|
-
if (!flushed) {
|
|
1192
|
-
targetSocket.pause();
|
|
1193
|
-
socket.once('drain', () => {
|
|
1194
|
-
targetSocket.resume();
|
|
1195
|
-
});
|
|
1196
|
-
}
|
|
1197
|
-
}
|
|
1152
|
+
},
|
|
1153
|
+
enableHalfOpen: false // Default: close both when one closes (required for proxy chains)
|
|
1198
1154
|
});
|
|
1155
|
+
|
|
1156
|
+
// Apply timeouts if keep-alive is enabled
|
|
1157
|
+
if (record.hasKeepAlive) {
|
|
1158
|
+
socket.setTimeout(this.settings.socketTimeout || 3600000);
|
|
1159
|
+
targetSocket.setTimeout(this.settings.socketTimeout || 3600000);
|
|
1160
|
+
}
|
|
1199
1161
|
|
|
1200
1162
|
// Log successful connection
|
|
1201
1163
|
logger.log('info',
|
|
@@ -1354,11 +1316,5 @@ export class RouteConnectionHandler {
|
|
|
1354
1316
|
|
|
1355
1317
|
// Apply socket timeouts
|
|
1356
1318
|
this.timeoutManager.applySocketTimeouts(record);
|
|
1357
|
-
|
|
1358
|
-
// Track outgoing data for bytes counting (moved from the duplicate connect handler)
|
|
1359
|
-
targetSocket.on('data', (chunk: Buffer) => {
|
|
1360
|
-
record.bytesSent += chunk.length;
|
|
1361
|
-
this.timeoutManager.updateActivity(record);
|
|
1362
|
-
});
|
|
1363
1319
|
}
|
|
1364
1320
|
}
|