# SmartProxy Development Plan ## Implementation Plan: Socket Handler Function Support (Simplified) ### Overview Add support for custom socket handler functions with the simplest possible API - just pass a function that receives the socket. ### User Experience Goal ```typescript const proxy = new SmartProxy({ routes: [{ name: 'my-custom-protocol', match: { ports: 9000, domains: 'custom.example.com' }, action: { type: 'socket-handler', socketHandler: (socket) => { // User has full control of the socket socket.write('Welcome!\n'); socket.on('data', (data) => { socket.write(`Echo: ${data}`); }); } } }] }); ``` That's it. Simple and powerful. --- ## Phase 1: Minimal Type Changes ### 1.1 Add Socket Handler Action Type **File:** `ts/proxies/smart-proxy/models/route-types.ts` ```typescript // Update action type export type TRouteActionType = 'forward' | 'redirect' | 'block' | 'static' | 'socket-handler'; // Add simple socket handler type export type TSocketHandler = (socket: net.Socket) => void | Promise; // Extend IRouteAction export interface IRouteAction { // ... existing properties // Socket handler function (when type is 'socket-handler') socketHandler?: TSocketHandler; } ``` --- ## Phase 2: Simple Implementation ### 2.1 Update Route Connection Handler **File:** `ts/proxies/smart-proxy/route-connection-handler.ts` In the `handleConnection` method, add handling for socket-handler: ```typescript // After route matching... if (matchedRoute) { const action = matchedRoute.action; if (action.type === 'socket-handler') { if (!action.socketHandler) { logger.error('socket-handler action missing socketHandler function'); socket.destroy(); return; } try { // Simply call the handler with the socket const result = action.socketHandler(socket); // If it returns a promise, handle errors if (result instanceof Promise) { result.catch(error => { logger.error('Socket handler error:', error); if (!socket.destroyed) { socket.destroy(); } }); } } catch (error) { logger.error('Socket handler error:', error); if (!socket.destroyed) { socket.destroy(); } } return; // Done - user has control now } // ... rest of existing action handling } ``` --- ## Phase 3: Optional Context (If Needed) If users need more info, we can optionally pass a minimal context as a second parameter: ```typescript export type TSocketHandler = ( socket: net.Socket, context?: { route: IRouteConfig; clientIp: string; localPort: number; } ) => void | Promise; ``` Usage: ```typescript socketHandler: (socket, context) => { console.log(`Connection from ${context.clientIp} to port ${context.localPort}`); // Handle socket... } ``` --- ## Phase 4: Helper Utilities (Optional) ### 4.1 Common Patterns **File:** `ts/proxies/smart-proxy/utils/route-helpers.ts` ```typescript // Simple helper to create socket handler routes export function createSocketHandlerRoute( domains: string | string[], ports: TPortRange, handler: TSocketHandler, options?: { name?: string; priority?: number } ): IRouteConfig { return { name: options?.name || 'socket-handler-route', priority: options?.priority || 50, match: { domains, ports }, action: { type: 'socket-handler', socketHandler: handler } }; } // Pre-built handlers for common cases export const SocketHandlers = { // Simple echo server echo: (socket: net.Socket) => { socket.on('data', data => socket.write(data)); }, // TCP proxy proxy: (targetHost: string, targetPort: number) => (socket: net.Socket) => { const target = net.connect(targetPort, targetHost); socket.pipe(target); target.pipe(socket); socket.on('close', () => target.destroy()); target.on('close', () => socket.destroy()); }, // Line-based protocol lineProtocol: (handler: (line: string, socket: net.Socket) => void) => (socket: net.Socket) => { let buffer = ''; socket.on('data', (data) => { buffer += data.toString(); const lines = buffer.split('\n'); buffer = lines.pop() || ''; lines.forEach(line => handler(line, socket)); }); } }; ``` --- ## Usage Examples ### Example 1: Custom Protocol ```typescript { name: 'custom-protocol', match: { ports: 9000 }, action: { type: 'socket-handler', socketHandler: (socket) => { socket.write('READY\n'); socket.on('data', (data) => { const cmd = data.toString().trim(); if (cmd === 'PING') socket.write('PONG\n'); else if (cmd === 'QUIT') socket.end(); else socket.write('ERROR: Unknown command\n'); }); } } } ``` ### Example 2: Simple TCP Proxy ```typescript { name: 'tcp-proxy', match: { ports: 8080, domains: 'proxy.example.com' }, action: { type: 'socket-handler', socketHandler: SocketHandlers.proxy('backend.local', 3000) } } ``` ### Example 3: WebSocket with Custom Auth ```typescript { name: 'custom-websocket', match: { ports: [80, 443], path: '/ws' }, action: { type: 'socket-handler', socketHandler: async (socket) => { // Read HTTP headers const headers = await readHttpHeaders(socket); // Custom auth check if (!headers.authorization || !validateToken(headers.authorization)) { socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n'); socket.end(); return; } // Proceed with WebSocket upgrade const ws = new WebSocket(socket, headers); // ... handle WebSocket } } } ``` --- ## Benefits of This Approach 1. **Dead Simple API**: Just pass a function that gets the socket 2. **No New Classes**: No ForwardingHandler subclass needed 3. **Minimal Changes**: Only touches type definitions and one handler method 4. **Full Power**: Users have complete control over the socket 5. **Backward Compatible**: No changes to existing functionality 6. **Easy to Test**: Just test the socket handler functions directly --- ## Implementation Steps 1. Add `'socket-handler'` to `TRouteActionType` (5 minutes) 2. Add `socketHandler?: TSocketHandler` to `IRouteAction` (5 minutes) 3. Add socket-handler case in `RouteConnectionHandler.handleConnection()` (15 minutes) 4. Add helper functions (optional, 30 minutes) 5. Write tests (2 hours) 6. Update documentation (1 hour) **Total implementation time: ~4 hours** (vs 6 weeks for the complex version) --- ## What We're NOT Doing - ❌ Creating new ForwardingHandler classes - ❌ Complex context objects with utils - ❌ HTTP request handling for socket handlers - ❌ Complex protocol detection mechanisms - ❌ Middleware patterns - ❌ Lifecycle hooks Keep it simple. The user just wants to handle a socket. --- ## Success Criteria - ✅ Users can define a route with `type: 'socket-handler'` - ✅ Users can provide a function that receives the socket - ✅ The function is called when a connection matches the route - ✅ Error handling prevents crashes - ✅ No performance impact on existing routes - ✅ Clean, simple API that's easy to understand