@push.rocks/smartproxy 19.5.18 → 19.5.20

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/00_commitinfo_data.js +2 -2
  2. package/dist_ts/core/models/index.d.ts +2 -0
  3. package/dist_ts/core/models/index.js +3 -1
  4. package/dist_ts/core/models/socket-types.d.ts +14 -0
  5. package/dist_ts/core/models/socket-types.js +15 -0
  6. package/dist_ts/core/models/wrapped-socket.d.ts +34 -0
  7. package/dist_ts/core/models/wrapped-socket.js +82 -0
  8. package/dist_ts/core/routing/index.d.ts +11 -0
  9. package/dist_ts/core/routing/index.js +17 -0
  10. package/dist_ts/core/routing/matchers/domain.d.ts +34 -0
  11. package/dist_ts/core/routing/matchers/domain.js +91 -0
  12. package/dist_ts/core/routing/matchers/header.d.ts +32 -0
  13. package/dist_ts/core/routing/matchers/header.js +94 -0
  14. package/dist_ts/core/routing/matchers/index.d.ts +18 -0
  15. package/dist_ts/core/routing/matchers/index.js +20 -0
  16. package/dist_ts/core/routing/matchers/ip.d.ts +53 -0
  17. package/dist_ts/core/routing/matchers/ip.js +169 -0
  18. package/dist_ts/core/routing/matchers/path.d.ts +44 -0
  19. package/dist_ts/core/routing/matchers/path.js +148 -0
  20. package/dist_ts/core/routing/route-manager.d.ts +88 -0
  21. package/dist_ts/core/routing/route-manager.js +342 -0
  22. package/dist_ts/core/routing/route-utils.d.ts +28 -0
  23. package/dist_ts/core/routing/route-utils.js +67 -0
  24. package/dist_ts/core/routing/specificity.d.ts +30 -0
  25. package/dist_ts/core/routing/specificity.js +115 -0
  26. package/dist_ts/core/routing/types.d.ts +41 -0
  27. package/dist_ts/core/routing/types.js +5 -0
  28. package/dist_ts/core/utils/index.d.ts +0 -2
  29. package/dist_ts/core/utils/index.js +1 -3
  30. package/dist_ts/core/utils/route-manager.d.ts +0 -30
  31. package/dist_ts/core/utils/route-manager.js +6 -47
  32. package/dist_ts/core/utils/route-utils.d.ts +2 -68
  33. package/dist_ts/core/utils/route-utils.js +21 -218
  34. package/dist_ts/core/utils/security-utils.js +4 -4
  35. package/dist_ts/core/utils/socket-utils.d.ts +0 -15
  36. package/dist_ts/core/utils/socket-utils.js +1 -35
  37. package/dist_ts/forwarding/handlers/https-terminate-to-http-handler.js +47 -32
  38. package/dist_ts/forwarding/handlers/https-terminate-to-https-handler.js +51 -32
  39. package/dist_ts/index.d.ts +2 -5
  40. package/dist_ts/index.js +5 -11
  41. package/dist_ts/proxies/http-proxy/http-proxy.d.ts +0 -1
  42. package/dist_ts/proxies/http-proxy/http-proxy.js +15 -60
  43. package/dist_ts/proxies/http-proxy/models/types.d.ts +0 -90
  44. package/dist_ts/proxies/http-proxy/models/types.js +1 -242
  45. package/dist_ts/proxies/http-proxy/request-handler.d.ts +3 -5
  46. package/dist_ts/proxies/http-proxy/request-handler.js +20 -171
  47. package/dist_ts/proxies/http-proxy/websocket-handler.d.ts +2 -5
  48. package/dist_ts/proxies/http-proxy/websocket-handler.js +15 -23
  49. package/dist_ts/proxies/index.d.ts +2 -2
  50. package/dist_ts/proxies/index.js +4 -3
  51. package/dist_ts/proxies/smart-proxy/connection-manager.d.ts +3 -1
  52. package/dist_ts/proxies/smart-proxy/connection-manager.js +15 -7
  53. package/dist_ts/proxies/smart-proxy/http-proxy-bridge.d.ts +2 -1
  54. package/dist_ts/proxies/smart-proxy/http-proxy-bridge.js +5 -2
  55. package/dist_ts/proxies/smart-proxy/index.d.ts +1 -1
  56. package/dist_ts/proxies/smart-proxy/index.js +2 -2
  57. package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +6 -2
  58. package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +1 -1
  59. package/dist_ts/proxies/smart-proxy/route-connection-handler.js +48 -25
  60. package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +1 -1
  61. package/dist_ts/proxies/smart-proxy/smart-proxy.js +15 -4
  62. package/dist_ts/proxies/smart-proxy/utils/route-utils.js +10 -43
  63. package/dist_ts/routing/router/http-router.d.ts +89 -0
  64. package/dist_ts/routing/router/http-router.js +205 -0
  65. package/dist_ts/routing/router/index.d.ts +2 -5
  66. package/dist_ts/routing/router/index.js +3 -4
  67. package/package.json +1 -1
  68. package/readme.delete.md +187 -0
  69. package/readme.hints.md +210 -1
  70. package/readme.plan.md +621 -0
  71. package/readme.routing.md +341 -0
  72. package/ts/00_commitinfo_data.ts +1 -1
  73. package/ts/core/models/index.ts +2 -0
  74. package/ts/core/models/socket-types.ts +21 -0
  75. package/ts/core/models/wrapped-socket.ts +99 -0
  76. package/ts/core/routing/index.ts +21 -0
  77. package/ts/core/routing/matchers/domain.ts +119 -0
  78. package/ts/core/routing/matchers/header.ts +120 -0
  79. package/ts/core/routing/matchers/index.ts +22 -0
  80. package/ts/core/routing/matchers/ip.ts +207 -0
  81. package/ts/core/routing/matchers/path.ts +184 -0
  82. package/ts/core/{utils → routing}/route-manager.ts +7 -57
  83. package/ts/core/routing/route-utils.ts +88 -0
  84. package/ts/core/routing/specificity.ts +141 -0
  85. package/ts/core/routing/types.ts +49 -0
  86. package/ts/core/utils/index.ts +0 -2
  87. package/ts/core/utils/security-utils.ts +3 -7
  88. package/ts/core/utils/socket-utils.ts +0 -44
  89. package/ts/forwarding/handlers/https-terminate-to-http-handler.ts +47 -33
  90. package/ts/forwarding/handlers/https-terminate-to-https-handler.ts +55 -35
  91. package/ts/index.ts +4 -14
  92. package/ts/proxies/http-proxy/http-proxy.ts +13 -68
  93. package/ts/proxies/http-proxy/models/types.ts +0 -324
  94. package/ts/proxies/http-proxy/request-handler.ts +15 -186
  95. package/ts/proxies/http-proxy/websocket-handler.ts +15 -26
  96. package/ts/proxies/index.ts +3 -2
  97. package/ts/proxies/smart-proxy/connection-manager.ts +15 -7
  98. package/ts/proxies/smart-proxy/http-proxy-bridge.ts +6 -2
  99. package/ts/proxies/smart-proxy/index.ts +1 -1
  100. package/ts/proxies/smart-proxy/models/interfaces.ts +8 -2
  101. package/ts/proxies/smart-proxy/route-connection-handler.ts +58 -30
  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
@@ -4,11 +4,9 @@ import {
4
4
  type IHttpProxyOptions,
5
5
  type ILogger,
6
6
  createLogger,
7
- type IReverseProxyConfig,
8
- RouteManager
9
7
  } from './models/types.js';
8
+ import { SharedRouteManager as RouteManager } from '../../core/routing/route-manager.js';
10
9
  import { ConnectionPool } from './connection-pool.js';
11
- import { ProxyRouter } from '../../routing/router/index.js';
12
10
  import { ContextCreator } from './context-creator.js';
13
11
  import { HttpRequestHandler } from './http-request-handler.js';
14
12
  import { Http2RequestHandler } from './http2-request-handler.js';
@@ -48,10 +46,9 @@ export class RequestHandler {
48
46
  constructor(
49
47
  private options: IHttpProxyOptions,
50
48
  private connectionPool: ConnectionPool,
51
- private legacyRouter: ProxyRouter, // Legacy router for backward compatibility
52
49
  private routeManager?: RouteManager,
53
50
  private functionCache?: any, // FunctionCache - using any to avoid circular dependency
54
- private router?: any // RouteRouter - using any to avoid circular dependency
51
+ private router?: any // HttpRouter - using any to avoid circular dependency
55
52
  ) {
56
53
  this.logger = createLogger(options.logLevel || 'info');
57
54
  this.securityManager = new SecurityManager(this.logger);
@@ -373,7 +370,8 @@ export class RequestHandler {
373
370
  tlsVersion: req.socket.getTLSVersion?.() || undefined
374
371
  });
375
372
 
376
- matchingRoute = this.routeManager.findMatchingRoute(toBaseContext(routeContext));
373
+ const matchResult = this.routeManager.findMatchingRoute(toBaseContext(routeContext));
374
+ matchingRoute = matchResult?.route || null;
377
375
  } catch (err) {
378
376
  this.logger.error('Error finding matching route', err);
379
377
  }
@@ -581,86 +579,11 @@ export class RequestHandler {
581
579
  }
582
580
  }
583
581
 
584
- // Try modern router first, then fall back to legacy routing if needed
585
- if (this.router) {
586
- try {
587
- // Try to find a matching route using the modern router
588
- const route = this.router.routeReq(req);
589
- if (route && route.action.type === 'forward' && route.action.target) {
590
- // Handle this route similarly to RouteManager logic
591
- this.logger.debug(`Found matching route via modern router: ${route.name || 'unnamed'}`);
592
-
593
- // No need to do anything here, we'll continue with legacy routing
594
- // The routeManager would have already found this route if applicable
595
- }
596
- } catch (err) {
597
- this.logger.error('Error using modern router', err);
598
- // Continue with legacy routing
599
- }
600
- }
601
-
602
- // Fall back to legacy routing if no matching route found via RouteManager
603
- let proxyConfig: IReverseProxyConfig | undefined;
604
- try {
605
- proxyConfig = this.legacyRouter.routeReq(req);
606
- } catch (err) {
607
- this.logger.error('Error routing request with legacy router', err);
608
- res.statusCode = 500;
609
- res.end('Internal Server Error');
610
- if (this.metricsTracker) this.metricsTracker.incrementFailedRequests();
611
- return;
612
- }
613
- if (!proxyConfig) {
614
- this.logger.warn(`No proxy configuration for host: ${req.headers.host}`);
615
- res.statusCode = 404;
616
- res.end('Not Found: No proxy configuration for this host');
617
- if (this.metricsTracker) this.metricsTracker.incrementFailedRequests();
618
- return;
619
- }
620
- // Determine protocol to backend (per-domain override or global)
621
- const backendProto = proxyConfig.backendProtocol || this.options.backendProtocol;
622
- if (backendProto === 'http2') {
623
- const destination = this.connectionPool.getNextTarget(
624
- proxyConfig.destinationIps,
625
- proxyConfig.destinationPorts[0]
626
- );
627
- const key = `${destination.host}:${destination.port}`;
628
- let session = this.h2Sessions.get(key);
629
- if (!session || session.closed || (session as any).destroyed) {
630
- session = plugins.http2.connect(`http://${destination.host}:${destination.port}`);
631
- this.h2Sessions.set(key, session);
632
- session.on('error', () => this.h2Sessions.delete(key));
633
- session.on('close', () => this.h2Sessions.delete(key));
634
- }
635
- // Build headers for HTTP/2 request
636
- const hdrs: Record<string, any> = {
637
- ':method': req.method,
638
- ':path': req.url,
639
- ':authority': `${destination.host}:${destination.port}`
640
- };
641
- for (const [hk, hv] of Object.entries(req.headers)) {
642
- if (typeof hv === 'string') hdrs[hk] = hv;
643
- }
644
- const h2Stream = session.request(hdrs);
645
- req.pipe(h2Stream);
646
- h2Stream.on('response', (hdrs2: any) => {
647
- const status = (hdrs2[':status'] as number) || 502;
648
- res.statusCode = status;
649
- // Copy headers from HTTP/2 response to HTTP/1 response
650
- for (const [hk, hv] of Object.entries(hdrs2)) {
651
- if (!hk.startsWith(':') && hv != null) {
652
- res.setHeader(hk, hv as string | string[]);
653
- }
654
- }
655
- h2Stream.pipe(res);
656
- });
657
- h2Stream.on('error', (err) => {
658
- res.statusCode = 502;
659
- res.end(`Bad Gateway: ${err.message}`);
660
- if (this.metricsTracker) this.metricsTracker.incrementFailedRequests();
661
- });
662
- return;
663
- }
582
+ // If no route was found, return 404
583
+ this.logger.warn(`No route configuration for host: ${req.headers.host}`);
584
+ res.statusCode = 404;
585
+ res.end('Not Found: No route configuration for this host');
586
+ if (this.metricsTracker) this.metricsTracker.incrementFailedRequests();
664
587
  }
665
588
 
666
589
  /**
@@ -688,7 +611,8 @@ export class RequestHandler {
688
611
  let matchingRoute: IRouteConfig | null = null;
689
612
  if (this.routeManager) {
690
613
  try {
691
- matchingRoute = this.routeManager.findMatchingRoute(toBaseContext(routeContext));
614
+ const matchResult = this.routeManager.findMatchingRoute(toBaseContext(routeContext));
615
+ matchingRoute = matchResult?.route || null;
692
616
  } catch (err) {
693
617
  this.logger.error('Error finding matching route for HTTP/2 request', err);
694
618
  }
@@ -812,104 +736,9 @@ export class RequestHandler {
812
736
  const method = headers[':method'] || 'GET';
813
737
  const path = headers[':path'] || '/';
814
738
 
815
- // If configured to proxy to backends over HTTP/2, use HTTP/2 client sessions
816
- if (this.options.backendProtocol === 'http2') {
817
- const authority = headers[':authority'] as string || '';
818
- const host = authority.split(':')[0];
819
- const fakeReq: any = {
820
- headers: { host },
821
- method: headers[':method'],
822
- url: headers[':path'],
823
- socket: (stream.session as any).socket
824
- };
825
- // Try modern router first if available
826
- let route;
827
- if (this.router) {
828
- try {
829
- route = this.router.routeReq(fakeReq);
830
- if (route && route.action.type === 'forward' && route.action.target) {
831
- this.logger.debug(`Found matching HTTP/2 route via modern router: ${route.name || 'unnamed'}`);
832
- // The routeManager would have already found this route if applicable
833
- }
834
- } catch (err) {
835
- this.logger.error('Error using modern router for HTTP/2', err);
836
- }
837
- }
838
-
839
- // Fall back to legacy routing
840
- const proxyConfig = this.legacyRouter.routeReq(fakeReq);
841
- if (!proxyConfig) {
842
- stream.respond({ ':status': 404 });
843
- stream.end('Not Found');
844
- if (this.metricsTracker) this.metricsTracker.incrementFailedRequests();
845
- return;
846
- }
847
- const destination = this.connectionPool.getNextTarget(proxyConfig.destinationIps, proxyConfig.destinationPorts[0]);
848
-
849
- // Use the helper for HTTP/2 to HTTP/2 routing
850
- return Http2RequestHandler.handleHttp2WithHttp2Destination(
851
- stream,
852
- headers,
853
- destination,
854
- routeContext,
855
- this.h2Sessions,
856
- this.logger,
857
- this.metricsTracker
858
- );
859
- }
860
-
861
- try {
862
- // Determine host for routing
863
- const authority = headers[':authority'] as string || '';
864
- const host = authority.split(':')[0];
865
- // Fake request object for routing
866
- const fakeReq: any = {
867
- headers: { host },
868
- method,
869
- url: path,
870
- socket: (stream.session as any).socket
871
- };
872
- // Try modern router first if available
873
- if (this.router) {
874
- try {
875
- const route = this.router.routeReq(fakeReq);
876
- if (route && route.action.type === 'forward' && route.action.target) {
877
- this.logger.debug(`Found matching HTTP/2 route via modern router: ${route.name || 'unnamed'}`);
878
- // The routeManager would have already found this route if applicable
879
- }
880
- } catch (err) {
881
- this.logger.error('Error using modern router for HTTP/2', err);
882
- }
883
- }
884
-
885
- // Fall back to legacy routing
886
- const proxyConfig = this.legacyRouter.routeReq(fakeReq as any);
887
- if (!proxyConfig) {
888
- stream.respond({ ':status': 404 });
889
- stream.end('Not Found');
890
- if (this.metricsTracker) this.metricsTracker.incrementFailedRequests();
891
- return;
892
- }
893
-
894
- // Select backend target
895
- const destination = this.connectionPool.getNextTarget(
896
- proxyConfig.destinationIps,
897
- proxyConfig.destinationPorts[0]
898
- );
899
-
900
- // Use the helper for HTTP/2 to HTTP/1 routing
901
- return Http2RequestHandler.handleHttp2WithHttp1Destination(
902
- stream,
903
- headers,
904
- destination,
905
- routeContext,
906
- this.logger,
907
- this.metricsTracker
908
- );
909
- } catch (err: any) {
910
- stream.respond({ ':status': 500 });
911
- stream.end('Internal Server Error');
912
- if (this.metricsTracker) this.metricsTracker.incrementFailedRequests();
913
- }
739
+ // No route was found
740
+ stream.respond({ ':status': 404 });
741
+ stream.end('Not Found: No route configuration for this request');
742
+ if (this.metricsTracker) this.metricsTracker.incrementFailedRequests();
914
743
  }
915
744
  }
@@ -1,8 +1,8 @@
1
1
  import * as plugins from '../../plugins.js';
2
2
  import '../../core/models/socket-augmentation.js';
3
- import { type IHttpProxyOptions, type IWebSocketWithHeartbeat, type ILogger, createLogger, type IReverseProxyConfig } from './models/types.js';
3
+ import { type IHttpProxyOptions, type IWebSocketWithHeartbeat, type ILogger, createLogger } from './models/types.js';
4
4
  import { ConnectionPool } from './connection-pool.js';
5
- import { ProxyRouter, RouteRouter } from '../../routing/router/index.js';
5
+ import { HttpRouter } from '../../routing/router/index.js';
6
6
  import type { IRouteConfig } from '../smart-proxy/models/route-types.js';
7
7
  import type { IRouteContext } from '../../core/models/route-context.js';
8
8
  import { toBaseContext } from '../../core/models/route-context.js';
@@ -19,21 +19,20 @@ export class WebSocketHandler {
19
19
  private wsServer: plugins.ws.WebSocketServer | null = null;
20
20
  private logger: ILogger;
21
21
  private contextCreator: ContextCreator = new ContextCreator();
22
- private routeRouter: RouteRouter | null = null;
22
+ private router: HttpRouter | null = null;
23
23
  private securityManager: SecurityManager;
24
24
 
25
25
  constructor(
26
26
  private options: IHttpProxyOptions,
27
27
  private connectionPool: ConnectionPool,
28
- private legacyRouter: ProxyRouter, // Legacy router for backward compatibility
29
- private routes: IRouteConfig[] = [] // Routes for modern router
28
+ private routes: IRouteConfig[] = []
30
29
  ) {
31
30
  this.logger = createLogger(options.logLevel || 'info');
32
31
  this.securityManager = new SecurityManager(this.logger, routes);
33
32
 
34
- // Initialize modern router if we have routes
33
+ // Initialize router if we have routes
35
34
  if (routes.length > 0) {
36
- this.routeRouter = new RouteRouter(routes, this.logger);
35
+ this.router = new HttpRouter(routes, this.logger);
37
36
  }
38
37
  }
39
38
 
@@ -44,10 +43,10 @@ export class WebSocketHandler {
44
43
  this.routes = routes;
45
44
 
46
45
  // Initialize or update the route router
47
- if (!this.routeRouter) {
48
- this.routeRouter = new RouteRouter(routes, this.logger);
46
+ if (!this.router) {
47
+ this.router = new HttpRouter(routes, this.logger);
49
48
  } else {
50
- this.routeRouter.setRoutes(routes);
49
+ this.router.setRoutes(routes);
51
50
  }
52
51
 
53
52
  // Update the security manager
@@ -139,8 +138,8 @@ export class WebSocketHandler {
139
138
 
140
139
  // Try modern router first if available
141
140
  let route: IRouteConfig | undefined;
142
- if (this.routeRouter) {
143
- route = this.routeRouter.routeReq(req);
141
+ if (this.router) {
142
+ route = this.router.routeReq(req);
144
143
  }
145
144
 
146
145
  // Define destination variables
@@ -227,20 +226,10 @@ export class WebSocketHandler {
227
226
  return;
228
227
  }
229
228
  } else {
230
- // Fall back to legacy routing if no matching route found via modern router
231
- const proxyConfig = this.legacyRouter.routeReq(req);
232
-
233
- if (!proxyConfig) {
234
- this.logger.warn(`No proxy configuration for WebSocket host: ${req.headers.host}`);
235
- wsIncoming.close(1008, 'No proxy configuration for this host');
236
- return;
237
- }
238
-
239
- // Get destination target using round-robin if multiple targets
240
- destination = this.connectionPool.getNextTarget(
241
- proxyConfig.destinationIps,
242
- proxyConfig.destinationPorts[0]
243
- );
229
+ // No route found
230
+ this.logger.warn(`No route configuration for WebSocket host: ${req.headers.host}`);
231
+ wsIncoming.close(1008, 'No route configuration for this host');
232
+ return;
244
233
  }
245
234
 
246
235
  // Build target URL with potential path rewriting
@@ -7,11 +7,12 @@ export { HttpProxy, CertificateManager, ConnectionPool, RequestHandler, WebSocke
7
7
  export type { IMetricsTracker, MetricsTracker } from './http-proxy/index.js';
8
8
  // Export http-proxy models except IAcmeOptions
9
9
  export type { IHttpProxyOptions, ICertificateEntry, ILogger } from './http-proxy/models/types.js';
10
- export { RouteManager as HttpProxyRouteManager } from './http-proxy/models/types.js';
10
+ // RouteManager has been unified - use SharedRouteManager from core/routing
11
+ export { SharedRouteManager as HttpProxyRouteManager } from '../core/routing/route-manager.js';
11
12
 
12
13
  // Export SmartProxy with selective imports to avoid conflicts
13
14
  export { SmartProxy, ConnectionManager, SecurityManager, TimeoutManager, TlsManager, HttpProxyBridge, RouteConnectionHandler } from './smart-proxy/index.js';
14
- export { RouteManager as SmartProxyRouteManager } from './smart-proxy/route-manager.js';
15
+ export { SharedRouteManager as SmartProxyRouteManager } from '../core/routing/route-manager.js';
15
16
  export * from './smart-proxy/utils/index.js';
16
17
  // Export smart-proxy models except IAcmeOptions
17
18
  export type { ISmartProxyOptions, IConnectionRecord, IRouteConfig, IRouteMatch, IRouteAction, IRouteTls, IRouteContext } from './smart-proxy/models/index.js';
@@ -5,6 +5,7 @@ import { TimeoutManager } from './timeout-manager.js';
5
5
  import { logger } from '../../core/utils/logger.js';
6
6
  import { LifecycleComponent } from '../../core/utils/lifecycle-component.js';
7
7
  import { cleanupSocket } from '../../core/utils/socket-utils.js';
8
+ import { WrappedSocket } from '../../core/models/wrapped-socket.js';
8
9
 
9
10
  /**
10
11
  * Manages connection lifecycle, tracking, and cleanup with performance optimizations
@@ -53,8 +54,9 @@ export class ConnectionManager extends LifecycleComponent {
53
54
 
54
55
  /**
55
56
  * Create and track a new connection
57
+ * Accepts either a regular net.Socket or a WrappedSocket for transparent PROXY protocol support
56
58
  */
57
- public createConnection(socket: plugins.net.Socket): IConnectionRecord | null {
59
+ public createConnection(socket: plugins.net.Socket | WrappedSocket): IConnectionRecord | null {
58
60
  // Enforce connection limit
59
61
  if (this.connectionRecords.size >= this.maxConnections) {
60
62
  logger.log('warn', `Connection limit reached (${this.maxConnections}). Rejecting new connection.`, {
@@ -282,22 +284,26 @@ export class ConnectionManager extends LifecycleComponent {
282
284
  const cleanupPromises: Promise<void>[] = [];
283
285
 
284
286
  if (record.incoming) {
287
+ // Extract underlying socket if it's a WrappedSocket
288
+ const incomingSocket = record.incoming instanceof WrappedSocket ? record.incoming.socket : record.incoming;
285
289
  if (!record.incoming.writable || record.incoming.destroyed) {
286
290
  // Socket is not active, clean up immediately
287
- cleanupPromises.push(cleanupSocket(record.incoming, `${record.id}-incoming`, { immediate: true }));
291
+ cleanupPromises.push(cleanupSocket(incomingSocket, `${record.id}-incoming`, { immediate: true }));
288
292
  } else {
289
293
  // Socket is still active, allow graceful cleanup
290
- cleanupPromises.push(cleanupSocket(record.incoming, `${record.id}-incoming`, { allowDrain: true, gracePeriod: 5000 }));
294
+ cleanupPromises.push(cleanupSocket(incomingSocket, `${record.id}-incoming`, { allowDrain: true, gracePeriod: 5000 }));
291
295
  }
292
296
  }
293
297
 
294
298
  if (record.outgoing) {
299
+ // Extract underlying socket if it's a WrappedSocket
300
+ const outgoingSocket = record.outgoing instanceof WrappedSocket ? record.outgoing.socket : record.outgoing;
295
301
  if (!record.outgoing.writable || record.outgoing.destroyed) {
296
302
  // Socket is not active, clean up immediately
297
- cleanupPromises.push(cleanupSocket(record.outgoing, `${record.id}-outgoing`, { immediate: true }));
303
+ cleanupPromises.push(cleanupSocket(outgoingSocket, `${record.id}-outgoing`, { immediate: true }));
298
304
  } else {
299
305
  // Socket is still active, allow graceful cleanup
300
- cleanupPromises.push(cleanupSocket(record.outgoing, `${record.id}-outgoing`, { allowDrain: true, gracePeriod: 5000 }));
306
+ cleanupPromises.push(cleanupSocket(outgoingSocket, `${record.id}-outgoing`, { allowDrain: true, gracePeriod: 5000 }));
301
307
  }
302
308
  }
303
309
 
@@ -570,11 +576,13 @@ export class ConnectionManager extends LifecycleComponent {
570
576
  const shutdownPromises: Promise<void>[] = [];
571
577
 
572
578
  if (record.incoming) {
573
- shutdownPromises.push(cleanupSocket(record.incoming, `${record.id}-incoming-shutdown`, { immediate: true }));
579
+ const incomingSocket = record.incoming instanceof WrappedSocket ? record.incoming.socket : record.incoming;
580
+ shutdownPromises.push(cleanupSocket(incomingSocket, `${record.id}-incoming-shutdown`, { immediate: true }));
574
581
  }
575
582
 
576
583
  if (record.outgoing) {
577
- shutdownPromises.push(cleanupSocket(record.outgoing, `${record.id}-outgoing-shutdown`, { immediate: true }));
584
+ const outgoingSocket = record.outgoing instanceof WrappedSocket ? record.outgoing.socket : record.outgoing;
585
+ shutdownPromises.push(cleanupSocket(outgoingSocket, `${record.id}-outgoing-shutdown`, { immediate: true }));
578
586
  }
579
587
 
580
588
  // Don't wait for shutdown cleanup in this batch processing
@@ -3,6 +3,7 @@ import { HttpProxy } from '../http-proxy/index.js';
3
3
  import { setupBidirectionalForwarding } from '../../core/utils/socket-utils.js';
4
4
  import type { IConnectionRecord, ISmartProxyOptions } from './models/interfaces.js';
5
5
  import type { IRouteConfig } from './models/route-types.js';
6
+ import { WrappedSocket } from '../../core/models/wrapped-socket.js';
6
7
 
7
8
  export class HttpProxyBridge {
8
9
  private httpProxy: HttpProxy | null = null;
@@ -98,7 +99,7 @@ export class HttpProxyBridge {
98
99
  */
99
100
  public async forwardToHttpProxy(
100
101
  connectionId: string,
101
- socket: plugins.net.Socket,
102
+ socket: plugins.net.Socket | WrappedSocket,
102
103
  record: IConnectionRecord,
103
104
  initialChunk: Buffer,
104
105
  httpProxyPort: number,
@@ -125,7 +126,10 @@ export class HttpProxyBridge {
125
126
  }
126
127
 
127
128
  // Use centralized bidirectional forwarding
128
- setupBidirectionalForwarding(socket, proxySocket, {
129
+ // Extract underlying socket if it's a WrappedSocket
130
+ const underlyingSocket = socket instanceof WrappedSocket ? socket.socket : socket;
131
+
132
+ setupBidirectionalForwarding(underlyingSocket, proxySocket, {
129
133
  onClientData: (chunk) => {
130
134
  // Update stats if needed
131
135
  if (record) {
@@ -17,7 +17,7 @@ export { TlsManager } from './tls-manager.js';
17
17
  export { HttpProxyBridge } from './http-proxy-bridge.js';
18
18
 
19
19
  // Export route-based components
20
- export { RouteManager } from './route-manager.js';
20
+ export { SharedRouteManager as RouteManager } from '../../core/routing/route-manager.js';
21
21
  export { RouteConnectionHandler } from './route-connection-handler.js';
22
22
  export { NFTablesManager } from './nftables-manager.js';
23
23
 
@@ -1,4 +1,5 @@
1
1
  import * as plugins from '../../../plugins.js';
2
+ import type { WrappedSocket } from '../../../core/models/wrapped-socket.js';
2
3
  // Certificate types removed - define IAcmeOptions locally
3
4
  export interface IAcmeOptions {
4
5
  enabled?: boolean;
@@ -34,6 +35,11 @@ export interface ISmartProxyOptions {
34
35
  // Port configuration
35
36
  preserveSourceIP?: boolean; // Preserve client IP when forwarding
36
37
 
38
+ // PROXY protocol configuration
39
+ proxyIPs?: string[]; // List of trusted proxy IPs that can send PROXY protocol
40
+ acceptProxyProtocol?: boolean; // Global option to accept PROXY protocol (defaults based on proxyIPs)
41
+ sendProxyProtocol?: boolean; // Global option to send PROXY protocol to all targets
42
+
37
43
  // Global/default settings
38
44
  defaults?: {
39
45
  target?: {
@@ -128,8 +134,8 @@ export interface ISmartProxyOptions {
128
134
  */
129
135
  export interface IConnectionRecord {
130
136
  id: string; // Unique connection identifier
131
- incoming: plugins.net.Socket;
132
- outgoing: plugins.net.Socket | null;
137
+ incoming: plugins.net.Socket | WrappedSocket;
138
+ outgoing: plugins.net.Socket | WrappedSocket | null;
133
139
  incomingStartTime: number;
134
140
  outgoingStartTime?: number;
135
141
  outgoingClosedTime?: number;