@push.rocks/smartproxy 19.4.2 → 19.5.2
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 +1 -1
- package/dist_ts/proxies/http-proxy/handlers/index.d.ts +1 -2
- package/dist_ts/proxies/http-proxy/handlers/index.js +3 -3
- package/dist_ts/proxies/smart-proxy/certificate-manager.js +30 -25
- package/dist_ts/proxies/smart-proxy/models/route-types.d.ts +9 -40
- package/dist_ts/proxies/smart-proxy/models/route-types.js +1 -1
- package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +2 -10
- package/dist_ts/proxies/smart-proxy/route-connection-handler.js +69 -43
- package/dist_ts/proxies/smart-proxy/utils/index.d.ts +2 -2
- package/dist_ts/proxies/smart-proxy/utils/index.js +3 -3
- package/dist_ts/proxies/smart-proxy/utils/route-helpers.d.ts +61 -20
- package/dist_ts/proxies/smart-proxy/utils/route-helpers.js +240 -45
- package/dist_ts/proxies/smart-proxy/utils/route-patterns.d.ts +0 -18
- package/dist_ts/proxies/smart-proxy/utils/route-patterns.js +4 -43
- package/dist_ts/proxies/smart-proxy/utils/route-utils.js +14 -15
- package/dist_ts/proxies/smart-proxy/utils/route-validators.js +10 -31
- package/package.json +7 -7
- package/readme.hints.md +38 -1
- package/readme.plan.md +314 -382
- package/readme.plan2.md +764 -0
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/proxies/http-proxy/handlers/index.ts +1 -2
- package/ts/proxies/smart-proxy/certificate-manager.ts +29 -23
- package/ts/proxies/smart-proxy/models/route-types.ts +12 -56
- package/ts/proxies/smart-proxy/route-connection-handler.ts +73 -60
- package/ts/proxies/smart-proxy/utils/index.ts +0 -2
- package/ts/proxies/smart-proxy/utils/route-helpers.ts +278 -61
- package/ts/proxies/smart-proxy/utils/route-patterns.ts +6 -56
- package/ts/proxies/smart-proxy/utils/route-utils.ts +12 -15
- package/ts/proxies/smart-proxy/utils/route-validators.ts +9 -31
- package/ts/proxies/http-proxy/handlers/redirect-handler.ts +0 -105
- package/ts/proxies/http-proxy/handlers/static-handler.ts +0 -261
package/readme.plan.md
CHANGED
|
@@ -1,384 +1,316 @@
|
|
|
1
1
|
# SmartProxy Development Plan
|
|
2
2
|
|
|
3
|
-
##
|
|
4
|
-
|
|
5
|
-
###
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
4.
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
// Try to bind to preferred port
|
|
319
|
-
try {
|
|
320
|
-
// Temporary test binding
|
|
321
|
-
const server = plugins.net.createServer();
|
|
322
|
-
await new Promise<void>((resolve, reject) => {
|
|
323
|
-
server.listen(preferredPort, () => {
|
|
324
|
-
server.close();
|
|
325
|
-
resolve();
|
|
326
|
-
}).on('error', reject);
|
|
327
|
-
});
|
|
328
|
-
|
|
329
|
-
// If we get here, port is available
|
|
330
|
-
return preferredPort;
|
|
331
|
-
} catch (error) {
|
|
332
|
-
if (error.code === 'EADDRINUSE') {
|
|
333
|
-
// Port is unavailable, try fallback ports
|
|
334
|
-
for (const fallbackPort of [8080, 8081, 8082, 8083, 8084]) {
|
|
335
|
-
try {
|
|
336
|
-
// Test if we can bind to fallback
|
|
337
|
-
const server = plugins.net.createServer();
|
|
338
|
-
await new Promise<void>((resolve, reject) => {
|
|
339
|
-
server.listen(fallbackPort, () => {
|
|
340
|
-
server.close();
|
|
341
|
-
resolve();
|
|
342
|
-
}).on('error', reject);
|
|
343
|
-
});
|
|
344
|
-
|
|
345
|
-
logger.log('warn', `Primary ACME port ${preferredPort} is unavailable, using fallback port ${fallbackPort}`);
|
|
346
|
-
return fallbackPort;
|
|
347
|
-
} catch {
|
|
348
|
-
// Try next fallback
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
// All attempts failed
|
|
354
|
-
throw new Error(`Could not find an available port for ACME challenges`);
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
```
|
|
358
|
-
|
|
359
|
-
### Testing Strategy
|
|
360
|
-
1. **Unit Tests**:
|
|
361
|
-
- Test port binding intelligence
|
|
362
|
-
- Test route merging logic
|
|
363
|
-
- Test error handling mechanisms
|
|
364
|
-
- Test port reference counting
|
|
365
|
-
- Test orphaned port detection and cleanup
|
|
366
|
-
|
|
367
|
-
2. **Integration Tests**:
|
|
368
|
-
- Test multiple routes on the same port
|
|
369
|
-
- Test ACME challenges on ports with existing routes
|
|
370
|
-
- Test dynamic route addition and removal
|
|
371
|
-
- Test port lifecycle (bind → share → release)
|
|
372
|
-
- Test various recovery scenarios
|
|
373
|
-
|
|
374
|
-
3. **Stress Tests**:
|
|
375
|
-
- Test rapid route updates
|
|
376
|
-
- Test concurrent operations
|
|
377
|
-
- Test large scale route changes (add/remove many at once)
|
|
378
|
-
- Test frequent changes to see if ports are properly released
|
|
379
|
-
- Test recovery from port conflicts
|
|
380
|
-
|
|
381
|
-
### Release Plan
|
|
382
|
-
1. **19.4.0** - Phase 1 & 2: Port Manager and ACME Route Improvements
|
|
383
|
-
2. **19.5.0** - Phase 3: Enhanced Route Management
|
|
384
|
-
3. **19.6.0** - Phase 4: Improved Error Handling and Recovery
|
|
3
|
+
## Implementation Plan: Socket Handler Function Support (Simplified) ✅ COMPLETED
|
|
4
|
+
|
|
5
|
+
### Overview
|
|
6
|
+
Add support for custom socket handler functions with the simplest possible API - just pass a function that receives the socket.
|
|
7
|
+
|
|
8
|
+
### User Experience Goal
|
|
9
|
+
```typescript
|
|
10
|
+
const proxy = new SmartProxy({
|
|
11
|
+
routes: [{
|
|
12
|
+
name: 'my-custom-protocol',
|
|
13
|
+
match: { ports: 9000, domains: 'custom.example.com' },
|
|
14
|
+
action: {
|
|
15
|
+
type: 'socket-handler',
|
|
16
|
+
socketHandler: (socket) => {
|
|
17
|
+
// User has full control of the socket
|
|
18
|
+
socket.write('Welcome!\n');
|
|
19
|
+
socket.on('data', (data) => {
|
|
20
|
+
socket.write(`Echo: ${data}`);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}]
|
|
25
|
+
});
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
That's it. Simple and powerful.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Phase 1: Minimal Type Changes
|
|
33
|
+
|
|
34
|
+
### 1.1 Add Socket Handler Action Type
|
|
35
|
+
**File:** `ts/proxies/smart-proxy/models/route-types.ts`
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
// Update action type
|
|
39
|
+
export type TRouteActionType = 'forward' | 'redirect' | 'block' | 'static' | 'socket-handler';
|
|
40
|
+
|
|
41
|
+
// Add simple socket handler type
|
|
42
|
+
export type TSocketHandler = (socket: net.Socket) => void | Promise<void>;
|
|
43
|
+
|
|
44
|
+
// Extend IRouteAction
|
|
45
|
+
export interface IRouteAction {
|
|
46
|
+
// ... existing properties
|
|
47
|
+
|
|
48
|
+
// Socket handler function (when type is 'socket-handler')
|
|
49
|
+
socketHandler?: TSocketHandler;
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Phase 2: Simple Implementation
|
|
56
|
+
|
|
57
|
+
### 2.1 Update Route Connection Handler
|
|
58
|
+
**File:** `ts/proxies/smart-proxy/route-connection-handler.ts`
|
|
59
|
+
|
|
60
|
+
In the `handleConnection` method, add handling for socket-handler:
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
// After route matching...
|
|
64
|
+
if (matchedRoute) {
|
|
65
|
+
const action = matchedRoute.action;
|
|
66
|
+
|
|
67
|
+
if (action.type === 'socket-handler') {
|
|
68
|
+
if (!action.socketHandler) {
|
|
69
|
+
logger.error('socket-handler action missing socketHandler function');
|
|
70
|
+
socket.destroy();
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
// Simply call the handler with the socket
|
|
76
|
+
const result = action.socketHandler(socket);
|
|
77
|
+
|
|
78
|
+
// If it returns a promise, handle errors
|
|
79
|
+
if (result instanceof Promise) {
|
|
80
|
+
result.catch(error => {
|
|
81
|
+
logger.error('Socket handler error:', error);
|
|
82
|
+
if (!socket.destroyed) {
|
|
83
|
+
socket.destroy();
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
} catch (error) {
|
|
88
|
+
logger.error('Socket handler error:', error);
|
|
89
|
+
if (!socket.destroyed) {
|
|
90
|
+
socket.destroy();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return; // Done - user has control now
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// ... rest of existing action handling
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## Phase 3: Optional Context (If Needed)
|
|
104
|
+
|
|
105
|
+
If users need more info, we can optionally pass a minimal context as a second parameter:
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
export type TSocketHandler = (
|
|
109
|
+
socket: net.Socket,
|
|
110
|
+
context?: {
|
|
111
|
+
route: IRouteConfig;
|
|
112
|
+
clientIp: string;
|
|
113
|
+
localPort: number;
|
|
114
|
+
}
|
|
115
|
+
) => void | Promise<void>;
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Usage:
|
|
119
|
+
```typescript
|
|
120
|
+
socketHandler: (socket, context) => {
|
|
121
|
+
console.log(`Connection from ${context.clientIp} to port ${context.localPort}`);
|
|
122
|
+
// Handle socket...
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## Phase 4: Helper Utilities (Optional)
|
|
129
|
+
|
|
130
|
+
### 4.1 Common Patterns
|
|
131
|
+
**File:** `ts/proxies/smart-proxy/utils/route-helpers.ts`
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
// Simple helper to create socket handler routes
|
|
135
|
+
export function createSocketHandlerRoute(
|
|
136
|
+
domains: string | string[],
|
|
137
|
+
ports: TPortRange,
|
|
138
|
+
handler: TSocketHandler,
|
|
139
|
+
options?: { name?: string; priority?: number }
|
|
140
|
+
): IRouteConfig {
|
|
141
|
+
return {
|
|
142
|
+
name: options?.name || 'socket-handler-route',
|
|
143
|
+
priority: options?.priority || 50,
|
|
144
|
+
match: { domains, ports },
|
|
145
|
+
action: {
|
|
146
|
+
type: 'socket-handler',
|
|
147
|
+
socketHandler: handler
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Pre-built handlers for common cases
|
|
153
|
+
export const SocketHandlers = {
|
|
154
|
+
// Simple echo server
|
|
155
|
+
echo: (socket: net.Socket) => {
|
|
156
|
+
socket.on('data', data => socket.write(data));
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
// TCP proxy
|
|
160
|
+
proxy: (targetHost: string, targetPort: number) => (socket: net.Socket) => {
|
|
161
|
+
const target = net.connect(targetPort, targetHost);
|
|
162
|
+
socket.pipe(target);
|
|
163
|
+
target.pipe(socket);
|
|
164
|
+
socket.on('close', () => target.destroy());
|
|
165
|
+
target.on('close', () => socket.destroy());
|
|
166
|
+
},
|
|
167
|
+
|
|
168
|
+
// Line-based protocol
|
|
169
|
+
lineProtocol: (handler: (line: string, socket: net.Socket) => void) => (socket: net.Socket) => {
|
|
170
|
+
let buffer = '';
|
|
171
|
+
socket.on('data', (data) => {
|
|
172
|
+
buffer += data.toString();
|
|
173
|
+
const lines = buffer.split('\n');
|
|
174
|
+
buffer = lines.pop() || '';
|
|
175
|
+
lines.forEach(line => handler(line, socket));
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## Usage Examples
|
|
184
|
+
|
|
185
|
+
### Example 1: Custom Protocol
|
|
186
|
+
```typescript
|
|
187
|
+
{
|
|
188
|
+
name: 'custom-protocol',
|
|
189
|
+
match: { ports: 9000 },
|
|
190
|
+
action: {
|
|
191
|
+
type: 'socket-handler',
|
|
192
|
+
socketHandler: (socket) => {
|
|
193
|
+
socket.write('READY\n');
|
|
194
|
+
socket.on('data', (data) => {
|
|
195
|
+
const cmd = data.toString().trim();
|
|
196
|
+
if (cmd === 'PING') socket.write('PONG\n');
|
|
197
|
+
else if (cmd === 'QUIT') socket.end();
|
|
198
|
+
else socket.write('ERROR: Unknown command\n');
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Example 2: Simple TCP Proxy
|
|
206
|
+
```typescript
|
|
207
|
+
{
|
|
208
|
+
name: 'tcp-proxy',
|
|
209
|
+
match: { ports: 8080, domains: 'proxy.example.com' },
|
|
210
|
+
action: {
|
|
211
|
+
type: 'socket-handler',
|
|
212
|
+
socketHandler: SocketHandlers.proxy('backend.local', 3000)
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Example 3: WebSocket with Custom Auth
|
|
218
|
+
```typescript
|
|
219
|
+
{
|
|
220
|
+
name: 'custom-websocket',
|
|
221
|
+
match: { ports: [80, 443], path: '/ws' },
|
|
222
|
+
action: {
|
|
223
|
+
type: 'socket-handler',
|
|
224
|
+
socketHandler: async (socket) => {
|
|
225
|
+
// Read HTTP headers
|
|
226
|
+
const headers = await readHttpHeaders(socket);
|
|
227
|
+
|
|
228
|
+
// Custom auth check
|
|
229
|
+
if (!headers.authorization || !validateToken(headers.authorization)) {
|
|
230
|
+
socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
|
|
231
|
+
socket.end();
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Proceed with WebSocket upgrade
|
|
236
|
+
const ws = new WebSocket(socket, headers);
|
|
237
|
+
// ... handle WebSocket
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## Benefits of This Approach
|
|
246
|
+
|
|
247
|
+
1. **Dead Simple API**: Just pass a function that gets the socket
|
|
248
|
+
2. **No New Classes**: No ForwardingHandler subclass needed
|
|
249
|
+
3. **Minimal Changes**: Only touches type definitions and one handler method
|
|
250
|
+
4. **Full Power**: Users have complete control over the socket
|
|
251
|
+
5. **Backward Compatible**: No changes to existing functionality
|
|
252
|
+
6. **Easy to Test**: Just test the socket handler functions directly
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## Implementation Steps
|
|
257
|
+
|
|
258
|
+
1. Add `'socket-handler'` to `TRouteActionType` (5 minutes)
|
|
259
|
+
2. Add `socketHandler?: TSocketHandler` to `IRouteAction` (5 minutes)
|
|
260
|
+
3. Add socket-handler case in `RouteConnectionHandler.handleConnection()` (15 minutes)
|
|
261
|
+
4. Add helper functions (optional, 30 minutes)
|
|
262
|
+
5. Write tests (2 hours)
|
|
263
|
+
6. Update documentation (1 hour)
|
|
264
|
+
|
|
265
|
+
**Total implementation time: ~4 hours** (vs 6 weeks for the complex version)
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## What We're NOT Doing
|
|
270
|
+
|
|
271
|
+
- ❌ Creating new ForwardingHandler classes
|
|
272
|
+
- ❌ Complex context objects with utils
|
|
273
|
+
- ❌ HTTP request handling for socket handlers
|
|
274
|
+
- ❌ Complex protocol detection mechanisms
|
|
275
|
+
- ❌ Middleware patterns
|
|
276
|
+
- ❌ Lifecycle hooks
|
|
277
|
+
|
|
278
|
+
Keep it simple. The user just wants to handle a socket.
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## Success Criteria
|
|
283
|
+
|
|
284
|
+
- ✅ Users can define a route with `type: 'socket-handler'`
|
|
285
|
+
- ✅ Users can provide a function that receives the socket
|
|
286
|
+
- ✅ The function is called when a connection matches the route
|
|
287
|
+
- ✅ Error handling prevents crashes
|
|
288
|
+
- ✅ No performance impact on existing routes
|
|
289
|
+
- ✅ Clean, simple API that's easy to understand
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## Implementation Notes (Completed)
|
|
294
|
+
|
|
295
|
+
### What Was Implemented
|
|
296
|
+
1. **Type Definitions** - Added 'socket-handler' to TRouteActionType and TSocketHandler type
|
|
297
|
+
2. **Route Handler** - Added socket-handler case in RouteConnectionHandler switch statement
|
|
298
|
+
3. **Error Handling** - Both sync and async errors are caught and logged
|
|
299
|
+
4. **Initial Data Handling** - Initial chunks are re-emitted to handler's listeners
|
|
300
|
+
5. **Helper Functions** - Added createSocketHandlerRoute and pre-built handlers (echo, proxy, etc.)
|
|
301
|
+
6. **Full Test Coverage** - All test cases pass including async handlers and error handling
|
|
302
|
+
|
|
303
|
+
### Key Implementation Details
|
|
304
|
+
- Socket handlers require initial data from client to trigger routing (not TLS handshake)
|
|
305
|
+
- The handler receives the raw socket after route matching
|
|
306
|
+
- Both sync and async handlers are supported
|
|
307
|
+
- Errors in handlers terminate the connection gracefully
|
|
308
|
+
- Helper utilities provide common patterns (echo server, TCP proxy, line protocol)
|
|
309
|
+
|
|
310
|
+
### Usage Notes
|
|
311
|
+
- Clients must send initial data to trigger the handler (even just a newline)
|
|
312
|
+
- The socket is passed directly to the handler function
|
|
313
|
+
- Handler has complete control over the socket lifecycle
|
|
314
|
+
- No special context object needed - keeps it simple
|
|
315
|
+
|
|
316
|
+
**Total implementation time: ~3 hours**
|