@push.rocks/smartproxy 19.5.19 → 19.5.21

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.
Files changed (110) hide show
  1. package/dist_ts/core/models/index.d.ts +2 -0
  2. package/dist_ts/core/models/index.js +3 -1
  3. package/dist_ts/core/models/socket-types.d.ts +14 -0
  4. package/dist_ts/core/models/socket-types.js +15 -0
  5. package/dist_ts/core/models/wrapped-socket.d.ts +34 -0
  6. package/dist_ts/core/models/wrapped-socket.js +82 -0
  7. package/dist_ts/core/routing/index.d.ts +11 -0
  8. package/dist_ts/core/routing/index.js +17 -0
  9. package/dist_ts/core/routing/matchers/domain.d.ts +34 -0
  10. package/dist_ts/core/routing/matchers/domain.js +91 -0
  11. package/dist_ts/core/routing/matchers/header.d.ts +32 -0
  12. package/dist_ts/core/routing/matchers/header.js +94 -0
  13. package/dist_ts/core/routing/matchers/index.d.ts +18 -0
  14. package/dist_ts/core/routing/matchers/index.js +20 -0
  15. package/dist_ts/core/routing/matchers/ip.d.ts +53 -0
  16. package/dist_ts/core/routing/matchers/ip.js +169 -0
  17. package/dist_ts/core/routing/matchers/path.d.ts +44 -0
  18. package/dist_ts/core/routing/matchers/path.js +148 -0
  19. package/dist_ts/core/routing/route-manager.d.ts +88 -0
  20. package/dist_ts/core/routing/route-manager.js +342 -0
  21. package/dist_ts/core/routing/route-utils.d.ts +28 -0
  22. package/dist_ts/core/routing/route-utils.js +67 -0
  23. package/dist_ts/core/routing/specificity.d.ts +30 -0
  24. package/dist_ts/core/routing/specificity.js +115 -0
  25. package/dist_ts/core/routing/types.d.ts +41 -0
  26. package/dist_ts/core/routing/types.js +5 -0
  27. package/dist_ts/core/utils/index.d.ts +1 -2
  28. package/dist_ts/core/utils/index.js +2 -3
  29. package/dist_ts/core/utils/proxy-protocol.d.ts +45 -0
  30. package/dist_ts/core/utils/proxy-protocol.js +201 -0
  31. package/dist_ts/core/utils/route-manager.d.ts +0 -30
  32. package/dist_ts/core/utils/route-manager.js +6 -47
  33. package/dist_ts/core/utils/route-utils.d.ts +2 -68
  34. package/dist_ts/core/utils/route-utils.js +21 -218
  35. package/dist_ts/core/utils/security-utils.js +4 -4
  36. package/dist_ts/index.d.ts +2 -5
  37. package/dist_ts/index.js +5 -11
  38. package/dist_ts/proxies/http-proxy/http-proxy.d.ts +0 -1
  39. package/dist_ts/proxies/http-proxy/http-proxy.js +15 -60
  40. package/dist_ts/proxies/http-proxy/models/types.d.ts +0 -90
  41. package/dist_ts/proxies/http-proxy/models/types.js +1 -242
  42. package/dist_ts/proxies/http-proxy/request-handler.d.ts +3 -5
  43. package/dist_ts/proxies/http-proxy/request-handler.js +20 -171
  44. package/dist_ts/proxies/http-proxy/websocket-handler.d.ts +2 -5
  45. package/dist_ts/proxies/http-proxy/websocket-handler.js +15 -23
  46. package/dist_ts/proxies/index.d.ts +2 -2
  47. package/dist_ts/proxies/index.js +4 -3
  48. package/dist_ts/proxies/smart-proxy/connection-manager.d.ts +3 -1
  49. package/dist_ts/proxies/smart-proxy/connection-manager.js +17 -7
  50. package/dist_ts/proxies/smart-proxy/http-proxy-bridge.d.ts +2 -1
  51. package/dist_ts/proxies/smart-proxy/http-proxy-bridge.js +5 -2
  52. package/dist_ts/proxies/smart-proxy/index.d.ts +1 -1
  53. package/dist_ts/proxies/smart-proxy/index.js +2 -2
  54. package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +7 -2
  55. package/dist_ts/proxies/smart-proxy/models/route-types.d.ts +1 -0
  56. package/dist_ts/proxies/smart-proxy/models/route-types.js +1 -1
  57. package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +1 -1
  58. package/dist_ts/proxies/smart-proxy/route-connection-handler.js +155 -35
  59. package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +1 -1
  60. package/dist_ts/proxies/smart-proxy/smart-proxy.js +15 -4
  61. package/dist_ts/proxies/smart-proxy/utils/route-utils.js +10 -43
  62. package/dist_ts/routing/router/http-router.d.ts +89 -0
  63. package/dist_ts/routing/router/http-router.js +205 -0
  64. package/dist_ts/routing/router/index.d.ts +2 -5
  65. package/dist_ts/routing/router/index.js +3 -4
  66. package/package.json +1 -1
  67. package/readme.delete.md +187 -0
  68. package/readme.hints.md +196 -1
  69. package/readme.plan.md +625 -0
  70. package/readme.proxy-chain-summary.md +112 -0
  71. package/readme.proxy-protocol-example.md +462 -0
  72. package/readme.proxy-protocol.md +415 -0
  73. package/readme.routing.md +341 -0
  74. package/ts/core/models/index.ts +2 -0
  75. package/ts/core/models/socket-types.ts +21 -0
  76. package/ts/core/models/wrapped-socket.ts +99 -0
  77. package/ts/core/routing/index.ts +21 -0
  78. package/ts/core/routing/matchers/domain.ts +119 -0
  79. package/ts/core/routing/matchers/header.ts +120 -0
  80. package/ts/core/routing/matchers/index.ts +22 -0
  81. package/ts/core/routing/matchers/ip.ts +207 -0
  82. package/ts/core/routing/matchers/path.ts +184 -0
  83. package/ts/core/{utils → routing}/route-manager.ts +7 -57
  84. package/ts/core/routing/route-utils.ts +88 -0
  85. package/ts/core/routing/specificity.ts +141 -0
  86. package/ts/core/routing/types.ts +49 -0
  87. package/ts/core/utils/index.ts +1 -2
  88. package/ts/core/utils/proxy-protocol.ts +246 -0
  89. package/ts/core/utils/security-utils.ts +3 -7
  90. package/ts/index.ts +4 -14
  91. package/ts/proxies/http-proxy/http-proxy.ts +13 -68
  92. package/ts/proxies/http-proxy/models/types.ts +0 -324
  93. package/ts/proxies/http-proxy/request-handler.ts +15 -186
  94. package/ts/proxies/http-proxy/websocket-handler.ts +15 -26
  95. package/ts/proxies/index.ts +3 -2
  96. package/ts/proxies/smart-proxy/connection-manager.ts +17 -7
  97. package/ts/proxies/smart-proxy/http-proxy-bridge.ts +6 -2
  98. package/ts/proxies/smart-proxy/index.ts +1 -1
  99. package/ts/proxies/smart-proxy/models/interfaces.ts +9 -2
  100. package/ts/proxies/smart-proxy/models/route-types.ts +3 -0
  101. package/ts/proxies/smart-proxy/route-connection-handler.ts +173 -42
  102. package/ts/proxies/smart-proxy/smart-proxy.ts +15 -3
  103. package/ts/proxies/smart-proxy/utils/route-utils.ts +11 -49
  104. package/ts/routing/router/http-router.ts +266 -0
  105. package/ts/routing/router/index.ts +3 -8
  106. package/readme.problems.md +0 -170
  107. package/ts/core/utils/route-utils.ts +0 -312
  108. package/ts/proxies/smart-proxy/route-manager.ts +0 -554
  109. package/ts/routing/router/proxy-router.ts +0 -437
  110. package/ts/routing/router/route-router.ts +0 -482
@@ -2,14 +2,18 @@ import * as plugins from '../../plugins.js';
2
2
  import type { IConnectionRecord, ISmartProxyOptions } from './models/interfaces.js';
3
3
  import { logger } from '../../core/utils/logger.js';
4
4
  // Route checking functions have been removed
5
- import type { IRouteConfig, IRouteAction, IRouteContext } from './models/route-types.js';
5
+ import type { IRouteConfig, IRouteAction } from './models/route-types.js';
6
+ import type { IRouteContext } from '../../core/models/route-context.js';
6
7
  import { ConnectionManager } from './connection-manager.js';
7
8
  import { SecurityManager } from './security-manager.js';
8
9
  import { TlsManager } from './tls-manager.js';
9
10
  import { HttpProxyBridge } from './http-proxy-bridge.js';
10
11
  import { TimeoutManager } from './timeout-manager.js';
11
- import { RouteManager } from './route-manager.js';
12
+ import { SharedRouteManager as RouteManager } from '../../core/routing/route-manager.js';
12
13
  import { cleanupSocket, createIndependentSocketHandlers, setupSocketHandlers, createSocketWithErrorHandler, setupBidirectionalForwarding } from '../../core/utils/socket-utils.js';
14
+ import { WrappedSocket } from '../../core/models/wrapped-socket.js';
15
+ import { getUnderlyingSocket } from '../../core/models/socket-types.js';
16
+ import { ProxyProtocolParser } from '../../core/utils/proxy-protocol.js';
13
17
 
14
18
  /**
15
19
  * Handles new connection processing and setup logic with support for route-based configuration
@@ -80,39 +84,52 @@ export class RouteConnectionHandler {
80
84
  const remoteIP = socket.remoteAddress || '';
81
85
  const localPort = socket.localPort || 0;
82
86
 
87
+ // Always wrap the socket to prepare for potential PROXY protocol
88
+ const wrappedSocket = new WrappedSocket(socket);
89
+
90
+ // If this is from a trusted proxy, log it
91
+ if (this.settings.proxyIPs?.includes(remoteIP)) {
92
+ logger.log('debug', `Connection from trusted proxy ${remoteIP}, PROXY protocol parsing will be enabled`, {
93
+ remoteIP,
94
+ component: 'route-handler'
95
+ });
96
+ }
97
+
83
98
  // Validate IP against rate limits and connection limits
84
- const ipValidation = this.securityManager.validateIP(remoteIP);
99
+ // Note: For wrapped sockets, this will use the underlying socket IP until PROXY protocol is parsed
100
+ const ipValidation = this.securityManager.validateIP(wrappedSocket.remoteAddress || '');
85
101
  if (!ipValidation.allowed) {
86
- logger.log('warn', `Connection rejected`, { remoteIP, reason: ipValidation.reason, component: 'route-handler' });
87
- cleanupSocket(socket, `rejected-${ipValidation.reason}`, { immediate: true });
102
+ logger.log('warn', `Connection rejected`, { remoteIP: wrappedSocket.remoteAddress, reason: ipValidation.reason, component: 'route-handler' });
103
+ cleanupSocket(wrappedSocket.socket, `rejected-${ipValidation.reason}`, { immediate: true });
88
104
  return;
89
105
  }
90
106
 
91
- // Create a new connection record
92
- const record = this.connectionManager.createConnection(socket);
107
+ // Create a new connection record with the wrapped socket
108
+ const record = this.connectionManager.createConnection(wrappedSocket);
93
109
  if (!record) {
94
110
  // Connection was rejected due to limit - socket already destroyed by connection manager
95
111
  return;
96
112
  }
97
113
  const connectionId = record.id;
98
114
 
99
- // Apply socket optimizations
100
- socket.setNoDelay(this.settings.noDelay);
115
+ // Apply socket optimizations (apply to underlying socket)
116
+ const underlyingSocket = wrappedSocket.socket;
117
+ underlyingSocket.setNoDelay(this.settings.noDelay);
101
118
 
102
119
  // Apply keep-alive settings if enabled
103
120
  if (this.settings.keepAlive) {
104
- socket.setKeepAlive(true, this.settings.keepAliveInitialDelay);
121
+ underlyingSocket.setKeepAlive(true, this.settings.keepAliveInitialDelay);
105
122
  record.hasKeepAlive = true;
106
123
 
107
124
  // Apply enhanced TCP keep-alive options if enabled
108
125
  if (this.settings.enableKeepAliveProbes) {
109
126
  try {
110
127
  // These are platform-specific and may not be available
111
- if ('setKeepAliveProbes' in socket) {
112
- (socket as any).setKeepAliveProbes(10);
128
+ if ('setKeepAliveProbes' in underlyingSocket) {
129
+ (underlyingSocket as any).setKeepAliveProbes(10);
113
130
  }
114
- if ('setKeepAliveInterval' in socket) {
115
- (socket as any).setKeepAliveInterval(1000);
131
+ if ('setKeepAliveInterval' in underlyingSocket) {
132
+ (underlyingSocket as any).setKeepAliveInterval(1000);
116
133
  }
117
134
  } catch (err) {
118
135
  // Ignore errors - these are optional enhancements
@@ -150,19 +167,19 @@ export class RouteConnectionHandler {
150
167
  }
151
168
 
152
169
  // Handle the connection - wait for initial data to determine if it's TLS
153
- this.handleInitialData(socket, record);
170
+ this.handleInitialData(wrappedSocket, record);
154
171
  }
155
172
 
156
173
  /**
157
174
  * Handle initial data from a connection to determine routing
158
175
  */
159
- private handleInitialData(socket: plugins.net.Socket, record: IConnectionRecord): void {
176
+ private handleInitialData(socket: plugins.net.Socket | WrappedSocket, record: IConnectionRecord): void {
160
177
  const connectionId = record.id;
161
178
  const localPort = record.localPort;
162
179
  let initialDataReceived = false;
163
180
 
164
181
  // Check if any routes on this port require TLS handling
165
- const allRoutes = this.routeManager.getAllRoutes();
182
+ const allRoutes = this.routeManager.getRoutes();
166
183
  const needsTlsHandling = allRoutes.some(route => {
167
184
  // Check if route matches this port
168
185
  const matchesPort = this.routeManager.getRoutesForPort(localPort).includes(route);
@@ -176,9 +193,11 @@ export class RouteConnectionHandler {
176
193
 
177
194
  // If no routes require TLS handling and it's not port 443, route immediately
178
195
  if (!needsTlsHandling && localPort !== 443) {
196
+ // Extract underlying socket for socket-utils functions
197
+ const underlyingSocket = getUnderlyingSocket(socket);
179
198
  // Set up proper socket handlers for immediate routing
180
199
  setupSocketHandlers(
181
- socket,
200
+ underlyingSocket,
182
201
  (reason) => {
183
202
  // Only cleanup if connection hasn't been fully established
184
203
  // Check if outgoing connection exists and is connected
@@ -277,17 +296,8 @@ export class RouteConnectionHandler {
277
296
  }
278
297
  });
279
298
 
280
- // First data handler to capture initial TLS handshake
281
- socket.once('data', (chunk: Buffer) => {
282
- // Clear the initial timeout since we've received data
283
- if (initialTimeout) {
284
- clearTimeout(initialTimeout);
285
- initialTimeout = null;
286
- }
287
-
288
- initialDataReceived = true;
289
- record.hasReceivedInitialData = true;
290
-
299
+ // Handler for processing initial data (after potential PROXY protocol)
300
+ const processInitialData = (chunk: Buffer) => {
291
301
  // Block non-TLS connections on port 443
292
302
  if (!this.tlsManager.isTlsHandshake(chunk) && localPort === 443) {
293
303
  logger.log('warn', `Non-TLS connection ${connectionId} detected on port 443. Terminating connection - only TLS traffic is allowed on standard HTTPS port.`, {
@@ -363,6 +373,67 @@ export class RouteConnectionHandler {
363
373
 
364
374
  // Find the appropriate route for this connection
365
375
  this.routeConnection(socket, record, serverName, chunk);
376
+ };
377
+
378
+ // First data handler to capture initial TLS handshake or PROXY protocol
379
+ socket.once('data', async (chunk: Buffer) => {
380
+ // Clear the initial timeout since we've received data
381
+ if (initialTimeout) {
382
+ clearTimeout(initialTimeout);
383
+ initialTimeout = null;
384
+ }
385
+
386
+ initialDataReceived = true;
387
+ record.hasReceivedInitialData = true;
388
+
389
+ // Check if this is from a trusted proxy and might have PROXY protocol
390
+ if (this.settings.proxyIPs?.includes(socket.remoteAddress || '') && this.settings.acceptProxyProtocol !== false) {
391
+ // Check if this starts with PROXY protocol
392
+ if (chunk.toString('ascii', 0, Math.min(6, chunk.length)).startsWith('PROXY ')) {
393
+ try {
394
+ const parseResult = ProxyProtocolParser.parse(chunk);
395
+
396
+ if (parseResult.proxyInfo) {
397
+ // Update the wrapped socket with real client info (if it's a WrappedSocket)
398
+ if (socket instanceof WrappedSocket) {
399
+ socket.setProxyInfo(parseResult.proxyInfo.sourceIP, parseResult.proxyInfo.sourcePort);
400
+ }
401
+
402
+ // Update connection record with real client info
403
+ record.remoteIP = parseResult.proxyInfo.sourceIP;
404
+ record.remotePort = parseResult.proxyInfo.sourcePort;
405
+
406
+ logger.log('info', `PROXY protocol parsed successfully`, {
407
+ connectionId,
408
+ realClientIP: parseResult.proxyInfo.sourceIP,
409
+ realClientPort: parseResult.proxyInfo.sourcePort,
410
+ proxyIP: socket.remoteAddress,
411
+ component: 'route-handler'
412
+ });
413
+
414
+ // Process remaining data if any
415
+ if (parseResult.remainingData.length > 0) {
416
+ processInitialData(parseResult.remainingData);
417
+ } else {
418
+ // Wait for more data
419
+ socket.once('data', processInitialData);
420
+ }
421
+ return;
422
+ }
423
+ } catch (error) {
424
+ logger.log('error', `Failed to parse PROXY protocol from trusted proxy`, {
425
+ connectionId,
426
+ error: error.message,
427
+ proxyIP: socket.remoteAddress,
428
+ component: 'route-handler'
429
+ });
430
+ // Continue processing as normal data
431
+ }
432
+ }
433
+ }
434
+
435
+ // Process as normal data (no PROXY protocol)
436
+ processInitialData(chunk);
366
437
  });
367
438
  }
368
439
 
@@ -370,7 +441,7 @@ export class RouteConnectionHandler {
370
441
  * Route the connection based on match criteria
371
442
  */
372
443
  private routeConnection(
373
- socket: plugins.net.Socket,
444
+ socket: plugins.net.Socket | WrappedSocket,
374
445
  record: IConnectionRecord,
375
446
  serverName: string,
376
447
  initialChunk?: Buffer
@@ -385,15 +456,21 @@ export class RouteConnectionHandler {
385
456
  // For HTTP proxy ports without TLS, skip domain check since domain info comes from HTTP headers
386
457
  const skipDomainCheck = isHttpProxyPort && !record.isTLS;
387
458
 
388
- // Find matching route
389
- const routeMatch = this.routeManager.findMatchingRoute({
459
+ // Create route context for matching
460
+ const routeContext: IRouteContext = {
390
461
  port: localPort,
391
- domain: serverName,
462
+ domain: skipDomainCheck ? undefined : serverName, // Skip domain if HTTP proxy without TLS
392
463
  clientIp: remoteIP,
464
+ serverIp: socket.localAddress || '',
393
465
  path: undefined, // We don't have path info at this point
466
+ isTls: record.isTLS,
394
467
  tlsVersion: undefined, // We don't extract TLS version yet
395
- skipDomainCheck: skipDomainCheck,
396
- });
468
+ timestamp: Date.now(),
469
+ connectionId: record.id
470
+ };
471
+
472
+ // Find matching route
473
+ const routeMatch = this.routeManager.findMatchingRoute(routeContext);
397
474
 
398
475
  if (!routeMatch) {
399
476
  logger.log('warn', `No route found for ${serverName || 'connection'} on port ${localPort} (connection: ${connectionId})`, {
@@ -552,7 +629,7 @@ export class RouteConnectionHandler {
552
629
  * Handle a forward action for a route
553
630
  */
554
631
  private handleForwardAction(
555
- socket: plugins.net.Socket,
632
+ socket: plugins.net.Socket | WrappedSocket,
556
633
  record: IConnectionRecord,
557
634
  route: IRouteConfig,
558
635
  initialChunk?: Buffer
@@ -869,7 +946,7 @@ export class RouteConnectionHandler {
869
946
  * Handle a socket-handler action for a route
870
947
  */
871
948
  private async handleSocketHandlerAction(
872
- socket: plugins.net.Socket,
949
+ socket: plugins.net.Socket | WrappedSocket,
873
950
  record: IConnectionRecord,
874
951
  route: IRouteConfig,
875
952
  initialChunk?: Buffer
@@ -933,8 +1010,9 @@ export class RouteConnectionHandler {
933
1010
  });
934
1011
 
935
1012
  try {
936
- // Call the handler with socket AND context
937
- const result = route.action.socketHandler(socket, routeContext);
1013
+ // Call the handler with the appropriate socket (extract underlying if needed)
1014
+ const handlerSocket = getUnderlyingSocket(socket);
1015
+ const result = route.action.socketHandler(handlerSocket, routeContext);
938
1016
 
939
1017
  // Handle async handlers properly
940
1018
  if (result instanceof Promise) {
@@ -988,7 +1066,7 @@ export class RouteConnectionHandler {
988
1066
  * Sets up a direct connection to the target
989
1067
  */
990
1068
  private setupDirectConnection(
991
- socket: plugins.net.Socket,
1069
+ socket: plugins.net.Socket | WrappedSocket,
992
1070
  record: IConnectionRecord,
993
1071
  serverName?: string,
994
1072
  initialChunk?: Buffer,
@@ -1094,7 +1172,7 @@ export class RouteConnectionHandler {
1094
1172
  // Clean up the connection record - this is critical!
1095
1173
  this.connectionManager.cleanupConnection(record, `connection_failed_${(error as any).code || 'unknown'}`);
1096
1174
  },
1097
- onConnect: () => {
1175
+ onConnect: async () => {
1098
1176
  if (this.settings.enableDetailedLogging) {
1099
1177
  logger.log('info', `Connection ${connectionId} established to target ${finalTargetHost}:${finalTargetPort}`, {
1100
1178
  connectionId,
@@ -1110,6 +1188,56 @@ export class RouteConnectionHandler {
1110
1188
  // Add the normal error handler for established connections
1111
1189
  targetSocket.on('error', this.connectionManager.handleError('outgoing', record));
1112
1190
 
1191
+ // Check if we should send PROXY protocol header
1192
+ const shouldSendProxyProtocol = record.routeConfig?.action?.sendProxyProtocol ||
1193
+ this.settings.sendProxyProtocol;
1194
+
1195
+ if (shouldSendProxyProtocol) {
1196
+ try {
1197
+ // Generate PROXY protocol header
1198
+ const proxyInfo = {
1199
+ protocol: (record.remoteIP.includes(':') ? 'TCP6' : 'TCP4') as 'TCP4' | 'TCP6',
1200
+ sourceIP: record.remoteIP,
1201
+ sourcePort: record.remotePort || socket.remotePort || 0,
1202
+ destinationIP: socket.localAddress || '',
1203
+ destinationPort: socket.localPort || 0
1204
+ };
1205
+
1206
+ const proxyHeader = ProxyProtocolParser.generate(proxyInfo);
1207
+
1208
+ // Send PROXY protocol header first
1209
+ await new Promise<void>((resolve, reject) => {
1210
+ targetSocket.write(proxyHeader, (err) => {
1211
+ if (err) {
1212
+ logger.log('error', `Failed to send PROXY protocol header`, {
1213
+ connectionId,
1214
+ error: err.message,
1215
+ component: 'route-handler'
1216
+ });
1217
+ reject(err);
1218
+ } else {
1219
+ logger.log('info', `PROXY protocol header sent to backend`, {
1220
+ connectionId,
1221
+ targetHost: finalTargetHost,
1222
+ targetPort: finalTargetPort,
1223
+ sourceIP: proxyInfo.sourceIP,
1224
+ sourcePort: proxyInfo.sourcePort,
1225
+ component: 'route-handler'
1226
+ });
1227
+ resolve();
1228
+ }
1229
+ });
1230
+ });
1231
+ } catch (error) {
1232
+ logger.log('error', `Error sending PROXY protocol header`, {
1233
+ connectionId,
1234
+ error: error.message,
1235
+ component: 'route-handler'
1236
+ });
1237
+ // Continue anyway - don't break the connection
1238
+ }
1239
+ }
1240
+
1113
1241
  // Flush any pending data to target
1114
1242
  if (record.pendingData.length > 0) {
1115
1243
  const combinedData = Buffer.concat(record.pendingData);
@@ -1138,7 +1266,10 @@ export class RouteConnectionHandler {
1138
1266
  }
1139
1267
 
1140
1268
  // Use centralized bidirectional forwarding setup
1141
- setupBidirectionalForwarding(socket, targetSocket, {
1269
+ // Extract underlying sockets for socket-utils functions
1270
+ const incomingSocket = getUnderlyingSocket(socket);
1271
+
1272
+ setupBidirectionalForwarding(incomingSocket, targetSocket, {
1142
1273
  onClientData: (chunk) => {
1143
1274
  record.bytesReceived += chunk.length;
1144
1275
  this.timeoutManager.updateActivity(record);
@@ -8,7 +8,7 @@ import { TlsManager } from './tls-manager.js';
8
8
  import { HttpProxyBridge } from './http-proxy-bridge.js';
9
9
  import { TimeoutManager } from './timeout-manager.js';
10
10
  import { PortManager } from './port-manager.js';
11
- import { RouteManager } from './route-manager.js';
11
+ import { SharedRouteManager as RouteManager } from '../../core/routing/route-manager.js';
12
12
  import { RouteConnectionHandler } from './route-connection-handler.js';
13
13
  import { NFTablesManager } from './nftables-manager.js';
14
14
 
@@ -162,8 +162,20 @@ export class SmartProxy extends plugins.EventEmitter {
162
162
  this.timeoutManager
163
163
  );
164
164
 
165
- // Create the route manager
166
- this.routeManager = new RouteManager(this.settings);
165
+ // Create the route manager with SharedRouteManager API
166
+ // Create a logger adapter to match ILogger interface
167
+ const loggerAdapter = {
168
+ debug: (message: string, data?: any) => logger.log('debug', message, data),
169
+ info: (message: string, data?: any) => logger.log('info', message, data),
170
+ warn: (message: string, data?: any) => logger.log('warn', message, data),
171
+ error: (message: string, data?: any) => logger.log('error', message, data)
172
+ };
173
+
174
+ this.routeManager = new RouteManager({
175
+ logger: loggerAdapter,
176
+ enableDetailedLogging: this.settings.enableDetailedLogging,
177
+ routes: this.settings.routes
178
+ });
167
179
 
168
180
 
169
181
  // Create other required components
@@ -92,6 +92,8 @@ export function mergeRouteConfigs(
92
92
  return mergedRoute;
93
93
  }
94
94
 
95
+ import { DomainMatcher, PathMatcher, HeaderMatcher } from '../../../core/routing/matchers/index.js';
96
+
95
97
  /**
96
98
  * Check if a route matches a domain
97
99
  * @param route The route to check
@@ -107,14 +109,7 @@ export function routeMatchesDomain(route: IRouteConfig, domain: string): boolean
107
109
  ? route.match.domains
108
110
  : [route.match.domains];
109
111
 
110
- return domains.some(d => {
111
- // Handle wildcard domains
112
- if (d.startsWith('*.')) {
113
- const suffix = d.substring(2);
114
- return domain.endsWith(suffix) && domain.split('.').length > suffix.split('.').length;
115
- }
116
- return d.toLowerCase() === domain.toLowerCase();
117
- });
112
+ return domains.some(d => DomainMatcher.match(d, domain));
118
113
  }
119
114
 
120
115
  /**
@@ -160,28 +155,7 @@ export function routeMatchesPath(route: IRouteConfig, path: string): boolean {
160
155
  return true; // No path specified means it matches any path
161
156
  }
162
157
 
163
- // Handle exact path
164
- if (route.match.path === path) {
165
- return true;
166
- }
167
-
168
- // Handle path prefix with trailing slash (e.g., /api/)
169
- if (route.match.path.endsWith('/') && path.startsWith(route.match.path)) {
170
- return true;
171
- }
172
-
173
- // Handle exact path match without trailing slash
174
- if (!route.match.path.endsWith('/') && path === route.match.path) {
175
- return true;
176
- }
177
-
178
- // Handle wildcard paths (e.g., /api/*)
179
- if (route.match.path.endsWith('*')) {
180
- const prefix = route.match.path.slice(0, -1);
181
- return path.startsWith(prefix);
182
- }
183
-
184
- return false;
158
+ return PathMatcher.match(route.match.path, path).matches;
185
159
  }
186
160
 
187
161
  /**
@@ -198,25 +172,13 @@ export function routeMatchesHeaders(
198
172
  return true; // No headers specified means it matches any headers
199
173
  }
200
174
 
201
- // Check each header in the route's match criteria
202
- return Object.entries(route.match.headers).every(([key, value]) => {
203
- // If the header isn't present in the request, it doesn't match
204
- if (!headers[key]) {
205
- return false;
206
- }
207
-
208
- // Handle exact match
209
- if (typeof value === 'string') {
210
- return headers[key] === value;
211
- }
212
-
213
- // Handle regex match
214
- if (value instanceof RegExp) {
215
- return value.test(headers[key]);
216
- }
217
-
218
- return false;
219
- });
175
+ // Convert RegExp patterns to strings for HeaderMatcher
176
+ const stringHeaders: Record<string, string> = {};
177
+ for (const [key, value] of Object.entries(route.match.headers)) {
178
+ stringHeaders[key] = value instanceof RegExp ? value.source : value;
179
+ }
180
+
181
+ return HeaderMatcher.matchAll(stringHeaders, headers);
220
182
  }
221
183
 
222
184
  /**