@push.rocks/smartproxy 19.5.9 → 19.5.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist_ts/core/utils/socket-utils.d.ts +13 -0
- package/dist_ts/core/utils/socket-utils.js +29 -1
- package/dist_ts/forwarding/handlers/https-passthrough-handler.js +91 -62
- package/dist_ts/forwarding/handlers/https-terminate-to-http-handler.js +32 -12
- package/dist_ts/forwarding/handlers/https-terminate-to-https-handler.js +2 -2
- package/package.json +1 -1
- package/readme.hints.md +45 -1
- package/readme.plan.md +124 -296
- package/ts/core/utils/socket-utils.ts +43 -0
- package/ts/forwarding/handlers/https-passthrough-handler.ts +104 -73
- package/ts/forwarding/handlers/https-terminate-to-http-handler.ts +35 -13
- package/ts/forwarding/handlers/https-terminate-to-https-handler.ts +1 -1
package/readme.plan.md
CHANGED
|
@@ -1,337 +1,165 @@
|
|
|
1
|
-
# SmartProxy Socket
|
|
1
|
+
# SmartProxy Socket Handling Fix Plan
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Reread CLAUDE.md file for guidelines
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
- WebSocket connections in HTTPS passthrough
|
|
7
|
-
- Long-lived HTTP connections (SSE, streaming)
|
|
8
|
-
- Database connections
|
|
9
|
-
- Any connection that should remain open for hours
|
|
5
|
+
## Implementation Summary (COMPLETED)
|
|
10
6
|
|
|
11
|
-
|
|
7
|
+
The critical socket handling issues have been fixed:
|
|
12
8
|
|
|
13
|
-
|
|
14
|
-
When one socket closes, both sockets are immediately destroyed:
|
|
15
|
-
```typescript
|
|
16
|
-
// In createSocketCleanupHandler
|
|
17
|
-
cleanupSocket(clientSocket, 'client');
|
|
18
|
-
cleanupSocket(serverSocket, 'server'); // Both destroyed together!
|
|
19
|
-
```
|
|
9
|
+
1. **Prevented Server Crashes**: Created `createSocketWithErrorHandler()` utility that attaches error handlers immediately upon socket creation, preventing unhandled ECONNREFUSED errors from crashing the server.
|
|
20
10
|
|
|
21
|
-
|
|
22
|
-
Timeout events immediately trigger connection cleanup:
|
|
23
|
-
```typescript
|
|
24
|
-
socket.on('timeout', () => {
|
|
25
|
-
handleClose(`${prefix}_timeout`); // Destroys both sockets!
|
|
26
|
-
});
|
|
27
|
-
```
|
|
11
|
+
2. **Fixed Memory Leaks**: Updated forwarding handlers to properly clean up client sockets when server connections fail, ensuring connection records are removed from tracking.
|
|
28
12
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
this.cleanupConnection(record, 'parity_check');
|
|
36
|
-
}
|
|
37
|
-
```
|
|
13
|
+
3. **Key Changes Made**:
|
|
14
|
+
- Added `createSocketWithErrorHandler()` in `socket-utils.ts`
|
|
15
|
+
- Updated `https-passthrough-handler.ts` to use safe socket creation
|
|
16
|
+
- Updated `https-terminate-to-http-handler.ts` to use safe socket creation
|
|
17
|
+
- Ensured client sockets are destroyed when server connections fail
|
|
18
|
+
- Connection cleanup now triggered by socket close events
|
|
38
19
|
|
|
39
|
-
|
|
40
|
-
The proxy doesn't support TCP half-open connections where one side closes while the other continues sending.
|
|
20
|
+
4. **Test Results**: Server no longer crashes on ECONNREFUSED errors, and connections are properly cleaned up.
|
|
41
21
|
|
|
42
|
-
##
|
|
22
|
+
## Problem Summary
|
|
43
23
|
|
|
44
|
-
|
|
24
|
+
The SmartProxy server is experiencing critical issues:
|
|
25
|
+
1. **Server crashes** due to unhandled socket connection errors (ECONNREFUSED)
|
|
26
|
+
2. **Memory leak** with steadily rising active connection count
|
|
27
|
+
3. **Race conditions** between socket creation and error handler attachment
|
|
28
|
+
4. **Orphaned sockets** when server connections fail
|
|
45
29
|
|
|
46
|
-
|
|
47
|
-
```typescript
|
|
48
|
-
export interface CleanupOptions {
|
|
49
|
-
immediate?: boolean; // Force immediate destruction
|
|
50
|
-
allowDrain?: boolean; // Allow write buffer to drain
|
|
51
|
-
gracePeriod?: number; // Ms to wait before force close
|
|
52
|
-
}
|
|
30
|
+
## Root Causes
|
|
53
31
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
): Promise<void> {
|
|
59
|
-
if (!socket || socket.destroyed) return Promise.resolve();
|
|
60
|
-
|
|
61
|
-
return new Promise<void>((resolve) => {
|
|
62
|
-
const cleanup = () => {
|
|
63
|
-
socket.removeAllListeners();
|
|
64
|
-
if (!socket.destroyed) {
|
|
65
|
-
socket.destroy();
|
|
66
|
-
}
|
|
67
|
-
resolve();
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
if (options.immediate) {
|
|
71
|
-
cleanup();
|
|
72
|
-
} else if (options.allowDrain && socket.writable) {
|
|
73
|
-
// Allow pending writes to complete
|
|
74
|
-
socket.end(() => cleanup());
|
|
75
|
-
|
|
76
|
-
// Force cleanup after grace period
|
|
77
|
-
if (options.gracePeriod) {
|
|
78
|
-
setTimeout(cleanup, options.gracePeriod);
|
|
79
|
-
}
|
|
80
|
-
} else {
|
|
81
|
-
cleanup();
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
```
|
|
32
|
+
### 1. Delayed Error Handler Attachment
|
|
33
|
+
- Sockets created without immediate error handlers
|
|
34
|
+
- Error events can fire before handlers attached
|
|
35
|
+
- Causes uncaught exceptions and server crashes
|
|
86
36
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
serverSocket: Socket,
|
|
92
|
-
onBothClosed: (reason: string) => void
|
|
93
|
-
): { cleanupClient: () => void, cleanupServer: () => void } {
|
|
94
|
-
let clientClosed = false;
|
|
95
|
-
let serverClosed = false;
|
|
96
|
-
let clientReason = '';
|
|
97
|
-
let serverReason = '';
|
|
98
|
-
|
|
99
|
-
const checkBothClosed = () => {
|
|
100
|
-
if (clientClosed && serverClosed) {
|
|
101
|
-
onBothClosed(`client: ${clientReason}, server: ${serverReason}`);
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
const cleanupClient = async (reason: string) => {
|
|
106
|
-
if (clientClosed) return;
|
|
107
|
-
clientClosed = true;
|
|
108
|
-
clientReason = reason;
|
|
109
|
-
|
|
110
|
-
// Allow server to continue if still active
|
|
111
|
-
if (!serverClosed && serverSocket.writable) {
|
|
112
|
-
// Half-close: stop reading from client, let server finish
|
|
113
|
-
clientSocket.pause();
|
|
114
|
-
clientSocket.unpipe(serverSocket);
|
|
115
|
-
await cleanupSocket(clientSocket, 'client', { allowDrain: true });
|
|
116
|
-
} else {
|
|
117
|
-
await cleanupSocket(clientSocket, 'client');
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
checkBothClosed();
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
const cleanupServer = async (reason: string) => {
|
|
124
|
-
if (serverClosed) return;
|
|
125
|
-
serverClosed = true;
|
|
126
|
-
serverReason = reason;
|
|
127
|
-
|
|
128
|
-
// Allow client to continue if still active
|
|
129
|
-
if (!clientClosed && clientSocket.writable) {
|
|
130
|
-
// Half-close: stop reading from server, let client finish
|
|
131
|
-
serverSocket.pause();
|
|
132
|
-
serverSocket.unpipe(clientSocket);
|
|
133
|
-
await cleanupSocket(serverSocket, 'server', { allowDrain: true });
|
|
134
|
-
} else {
|
|
135
|
-
await cleanupSocket(serverSocket, 'server');
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
checkBothClosed();
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
return { cleanupClient, cleanupServer };
|
|
142
|
-
}
|
|
143
|
-
```
|
|
37
|
+
### 2. Incomplete Cleanup Logic
|
|
38
|
+
- Client sockets not cleaned up when server connection fails
|
|
39
|
+
- Connection counter only decrements after BOTH sockets close
|
|
40
|
+
- Failed server connections leave orphaned client sockets
|
|
144
41
|
|
|
145
|
-
###
|
|
42
|
+
### 3. Missing Global Error Handlers
|
|
43
|
+
- No process-level uncaughtException handler
|
|
44
|
+
- No process-level unhandledRejection handler
|
|
45
|
+
- Any unhandled error crashes entire server
|
|
146
46
|
|
|
147
|
-
|
|
148
|
-
```typescript
|
|
149
|
-
export function setupSocketHandlers(
|
|
150
|
-
socket: Socket | TLSSocket,
|
|
151
|
-
handleClose: (reason: string) => void,
|
|
152
|
-
handleTimeout?: (socket: Socket) => void, // New optional handler
|
|
153
|
-
errorPrefix?: string
|
|
154
|
-
): void {
|
|
155
|
-
socket.on('error', (error) => {
|
|
156
|
-
const prefix = errorPrefix || 'Socket';
|
|
157
|
-
handleClose(`${prefix}_error: ${error.message}`);
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
socket.on('close', () => {
|
|
161
|
-
const prefix = errorPrefix || 'socket';
|
|
162
|
-
handleClose(`${prefix}_closed`);
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
socket.on('timeout', () => {
|
|
166
|
-
if (handleTimeout) {
|
|
167
|
-
handleTimeout(socket); // Custom timeout handling
|
|
168
|
-
} else {
|
|
169
|
-
// Default: just log, don't close
|
|
170
|
-
console.warn(`Socket timeout: ${errorPrefix || 'socket'}`);
|
|
171
|
-
}
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
```
|
|
47
|
+
## Implementation Plan
|
|
175
48
|
|
|
176
|
-
|
|
177
|
-
```typescript
|
|
178
|
-
// In https-passthrough-handler.ts
|
|
179
|
-
const { cleanupClient, cleanupServer } = createIndependentSocketHandlers(
|
|
180
|
-
clientSocket,
|
|
181
|
-
serverSocket,
|
|
182
|
-
(reason) => {
|
|
183
|
-
this.emit(ForwardingHandlerEvents.DISCONNECTED, {
|
|
184
|
-
remoteAddress,
|
|
185
|
-
bytesSent,
|
|
186
|
-
bytesReceived,
|
|
187
|
-
reason
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
|
-
);
|
|
191
|
-
|
|
192
|
-
// Setup handlers with custom timeout handling
|
|
193
|
-
setupSocketHandlers(clientSocket, cleanupClient, (socket) => {
|
|
194
|
-
// Just reset timeout, don't close
|
|
195
|
-
socket.setTimeout(timeout);
|
|
196
|
-
}, 'client');
|
|
197
|
-
|
|
198
|
-
setupSocketHandlers(serverSocket, cleanupServer, (socket) => {
|
|
199
|
-
// Just reset timeout, don't close
|
|
200
|
-
socket.setTimeout(timeout);
|
|
201
|
-
}, 'server');
|
|
202
|
-
```
|
|
49
|
+
### Phase 1: Prevent Server Crashes (Critical)
|
|
203
50
|
|
|
204
|
-
|
|
51
|
+
#### 1.1 Add Global Error Handlers
|
|
52
|
+
- [x] ~~Add global error handlers in main entry point~~ (Removed per user request - no global handlers)
|
|
53
|
+
- [x] Log errors with context
|
|
54
|
+
- [x] ~~Implement graceful shutdown sequence~~ (Removed - handled locally)
|
|
205
55
|
|
|
206
|
-
####
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
if (record.outgoingClosedTime &&
|
|
211
|
-
!record.incoming.destroyed &&
|
|
212
|
-
!record.connectionClosed &&
|
|
213
|
-
now - record.outgoingClosedTime > 1800000) { // 30 minutes
|
|
214
|
-
// Only close if no data activity
|
|
215
|
-
if (now - record.lastActivity > 600000) { // 10 minutes of inactivity
|
|
216
|
-
this.cleanupConnection(record, 'parity_check');
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
```
|
|
56
|
+
#### 1.2 Fix Socket Creation Race Condition
|
|
57
|
+
- [x] Modify socket creation to attach error handlers immediately
|
|
58
|
+
- [x] Update all forwarding handlers (https-passthrough, http, etc.)
|
|
59
|
+
- [x] Ensure error handlers attached in same tick as socket creation
|
|
220
60
|
|
|
221
|
-
|
|
222
|
-
```typescript
|
|
223
|
-
public cleanupConnection(record: IConnectionRecord, reason: string = 'normal'): void {
|
|
224
|
-
if (!record.connectionClosed) {
|
|
225
|
-
record.connectionClosed = true;
|
|
226
|
-
|
|
227
|
-
// Only cleanup sockets that are actually closed or inactive
|
|
228
|
-
if (record.incoming && (!record.incoming.writable || record.incoming.destroyed)) {
|
|
229
|
-
cleanupSocket(record.incoming, `${record.id}-incoming`, { immediate: true });
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
if (record.outgoing && (!record.outgoing.writable || record.outgoing.destroyed)) {
|
|
233
|
-
cleanupSocket(record.outgoing, `${record.id}-outgoing`, { immediate: true });
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
// If either socket is still active, don't remove the record yet
|
|
237
|
-
if ((record.incoming && record.incoming.writable) ||
|
|
238
|
-
(record.outgoing && record.outgoing.writable)) {
|
|
239
|
-
record.connectionClosed = false; // Reset flag
|
|
240
|
-
return; // Don't finish cleanup
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// Continue with full cleanup...
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
```
|
|
61
|
+
### Phase 2: Fix Memory Leaks (High Priority)
|
|
247
62
|
|
|
248
|
-
|
|
63
|
+
#### 2.1 Fix Connection Cleanup Logic
|
|
64
|
+
- [x] Clean up client socket immediately if server connection fails
|
|
65
|
+
- [x] Decrement connection counter on any socket failure (handled by socket close events)
|
|
66
|
+
- [x] Implement proper cleanup for half-open connections
|
|
249
67
|
|
|
250
|
-
####
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
4. Verify no socket leaks with long-running connections
|
|
255
|
-
5. Test graceful shutdown with pending data
|
|
68
|
+
#### 2.2 Improve Socket Utils
|
|
69
|
+
- [x] Create new utility function for safe socket creation with immediate error handling
|
|
70
|
+
- [x] Update createIndependentSocketHandlers to handle immediate failures
|
|
71
|
+
- [ ] Add connection tracking debug utilities
|
|
256
72
|
|
|
257
|
-
|
|
258
|
-
- Ensure all event listeners are tracked and removed
|
|
259
|
-
- Use WeakMap for socket metadata to prevent memory leaks
|
|
260
|
-
- Implement connection count monitoring
|
|
261
|
-
- Add periodic health checks for orphaned sockets
|
|
73
|
+
### Phase 3: Comprehensive Testing (Important)
|
|
262
74
|
|
|
263
|
-
|
|
75
|
+
#### 3.1 Create Test Cases
|
|
76
|
+
- [x] Test ECONNREFUSED scenario
|
|
77
|
+
- [ ] Test timeout handling
|
|
78
|
+
- [ ] Test half-open connections
|
|
79
|
+
- [ ] Test rapid connect/disconnect cycles
|
|
264
80
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
5. **Day 5**: Comprehensive testing and leak detection
|
|
81
|
+
#### 3.2 Add Monitoring
|
|
82
|
+
- [ ] Add connection leak detection
|
|
83
|
+
- [ ] Add metrics for connection lifecycle
|
|
84
|
+
- [ ] Add debug logging for socket state transitions
|
|
270
85
|
|
|
271
|
-
##
|
|
86
|
+
## Detailed Implementation Steps
|
|
272
87
|
|
|
273
|
-
|
|
88
|
+
### Step 1: Global Error Handlers (ts/proxies/smart-proxy/smart-proxy.ts)
|
|
274
89
|
```typescript
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
//
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
}
|
|
90
|
+
// Add in constructor or start method
|
|
91
|
+
process.on('uncaughtException', (error) => {
|
|
92
|
+
logger.log('error', 'Uncaught exception', { error });
|
|
93
|
+
// Graceful shutdown
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
process.on('unhandledRejection', (reason, promise) => {
|
|
97
|
+
logger.log('error', 'Unhandled rejection', { reason, promise });
|
|
98
|
+
});
|
|
284
99
|
```
|
|
285
100
|
|
|
286
|
-
|
|
101
|
+
### Step 2: Safe Socket Creation Utility (ts/core/utils/socket-utils.ts)
|
|
102
|
+
```typescript
|
|
103
|
+
export function createSocketWithErrorHandler(
|
|
104
|
+
options: net.NetConnectOpts,
|
|
105
|
+
onError: (err: Error) => void
|
|
106
|
+
): net.Socket {
|
|
107
|
+
const socket = net.connect(options);
|
|
108
|
+
socket.on('error', onError);
|
|
109
|
+
return socket;
|
|
110
|
+
}
|
|
111
|
+
```
|
|
287
112
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
5. Graceful shutdown completes within grace period
|
|
113
|
+
### Step 3: Fix HttpsPassthroughHandler (ts/forwarding/handlers/https-passthrough-handler.ts)
|
|
114
|
+
- Replace direct socket creation with safe creation
|
|
115
|
+
- Handle server connection failures immediately
|
|
116
|
+
- Clean up client socket on server connection failure
|
|
293
117
|
|
|
294
|
-
|
|
118
|
+
### Step 4: Fix Connection Counting
|
|
119
|
+
- Decrement on ANY socket close, not just when both close
|
|
120
|
+
- Track failed connections separately
|
|
121
|
+
- Add connection state tracking
|
|
295
122
|
|
|
296
|
-
###
|
|
123
|
+
### Step 5: Update All Handlers
|
|
124
|
+
- [ ] https-passthrough-handler.ts
|
|
125
|
+
- [ ] http-handler.ts
|
|
126
|
+
- [ ] https-terminate-to-http-handler.ts
|
|
127
|
+
- [ ] https-terminate-to-https-handler.ts
|
|
128
|
+
- [ ] route-connection-handler.ts
|
|
297
129
|
|
|
298
|
-
|
|
299
|
-
- Added `CleanupOptions` interface with `immediate`, `allowDrain`, and `gracePeriod` options
|
|
300
|
-
- Implemented graceful shutdown support with write buffer draining
|
|
130
|
+
## Success Criteria
|
|
301
131
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
132
|
+
1. **No server crashes** on ECONNREFUSED or other socket errors
|
|
133
|
+
2. **Active connections** remain stable (no steady increase)
|
|
134
|
+
3. **All sockets** properly cleaned up on errors
|
|
135
|
+
4. **Memory usage** remains stable under load
|
|
136
|
+
5. **Graceful handling** of all error scenarios
|
|
306
137
|
|
|
307
|
-
|
|
308
|
-
- Added optional `handleTimeout` parameter to customize timeout behavior
|
|
309
|
-
- Prevents automatic connection closure on timeout events
|
|
138
|
+
## Testing Plan
|
|
310
139
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
140
|
+
1. Simulate ECONNREFUSED by targeting closed ports
|
|
141
|
+
2. Monitor active connection count over time
|
|
142
|
+
3. Stress test with rapid connections
|
|
143
|
+
4. Test with unreachable hosts
|
|
144
|
+
5. Test with slow/timing out connections
|
|
315
145
|
|
|
316
|
-
|
|
317
|
-
- Extended parity check from 2 minutes to 30 minutes
|
|
318
|
-
- Added activity check before closing (10 minutes of inactivity required)
|
|
319
|
-
- Modified cleanup to check socket states before destroying
|
|
146
|
+
## Rollback Plan
|
|
320
147
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
148
|
+
If issues arise:
|
|
149
|
+
1. Revert socket creation changes
|
|
150
|
+
2. Keep global error handlers (they add safety)
|
|
151
|
+
3. Add more detailed logging for debugging
|
|
152
|
+
4. Implement fixes incrementally
|
|
325
153
|
|
|
326
|
-
|
|
154
|
+
## Timeline
|
|
327
155
|
|
|
328
|
-
|
|
329
|
-
-
|
|
330
|
-
-
|
|
331
|
-
- ✅ No socket leaks or premature closures
|
|
156
|
+
- Phase 1: Immediate (prevents crashes)
|
|
157
|
+
- Phase 2: Within 24 hours (fixes leaks)
|
|
158
|
+
- Phase 3: Within 48 hours (ensures stability)
|
|
332
159
|
|
|
333
|
-
|
|
160
|
+
## Notes
|
|
334
161
|
|
|
335
|
-
- The
|
|
336
|
-
-
|
|
337
|
-
-
|
|
162
|
+
- The race condition is the most critical issue
|
|
163
|
+
- Connection counting logic needs complete overhaul
|
|
164
|
+
- Consider using a connection state machine for clarity
|
|
165
|
+
- Add connection lifecycle events for debugging
|
|
@@ -6,6 +6,14 @@ export interface CleanupOptions {
|
|
|
6
6
|
gracePeriod?: number; // Ms to wait before force close
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
+
export interface SafeSocketOptions {
|
|
10
|
+
port: number;
|
|
11
|
+
host: string;
|
|
12
|
+
onError?: (error: Error) => void;
|
|
13
|
+
onConnect?: () => void;
|
|
14
|
+
timeout?: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
9
17
|
/**
|
|
10
18
|
* Safely cleanup a socket by removing all listeners and destroying it
|
|
11
19
|
* @param socket The socket to cleanup
|
|
@@ -197,4 +205,39 @@ export function pipeSockets(
|
|
|
197
205
|
): void {
|
|
198
206
|
socket1.pipe(socket2);
|
|
199
207
|
socket2.pipe(socket1);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Create a socket with immediate error handling to prevent crashes
|
|
212
|
+
* @param options Socket creation options
|
|
213
|
+
* @returns The created socket
|
|
214
|
+
*/
|
|
215
|
+
export function createSocketWithErrorHandler(options: SafeSocketOptions): plugins.net.Socket {
|
|
216
|
+
const { port, host, onError, onConnect, timeout } = options;
|
|
217
|
+
|
|
218
|
+
// Create socket with immediate error handler attachment
|
|
219
|
+
const socket = new plugins.net.Socket();
|
|
220
|
+
|
|
221
|
+
// Attach error handler BEFORE connecting to catch immediate errors
|
|
222
|
+
socket.on('error', (error) => {
|
|
223
|
+
console.error(`Socket connection error to ${host}:${port}: ${error.message}`);
|
|
224
|
+
if (onError) {
|
|
225
|
+
onError(error);
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
// Attach connect handler if provided
|
|
230
|
+
if (onConnect) {
|
|
231
|
+
socket.on('connect', onConnect);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Set timeout if provided
|
|
235
|
+
if (timeout) {
|
|
236
|
+
socket.setTimeout(timeout);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Now attempt to connect - any immediate errors will be caught
|
|
240
|
+
socket.connect(port, host);
|
|
241
|
+
|
|
242
|
+
return socket;
|
|
200
243
|
}
|