@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.
- package/dist_ts/00_commitinfo_data.js +2 -2
- package/dist_ts/core/models/index.d.ts +2 -0
- package/dist_ts/core/models/index.js +3 -1
- package/dist_ts/core/models/socket-types.d.ts +14 -0
- package/dist_ts/core/models/socket-types.js +15 -0
- package/dist_ts/core/models/wrapped-socket.d.ts +34 -0
- package/dist_ts/core/models/wrapped-socket.js +82 -0
- package/dist_ts/core/routing/index.d.ts +11 -0
- package/dist_ts/core/routing/index.js +17 -0
- package/dist_ts/core/routing/matchers/domain.d.ts +34 -0
- package/dist_ts/core/routing/matchers/domain.js +91 -0
- package/dist_ts/core/routing/matchers/header.d.ts +32 -0
- package/dist_ts/core/routing/matchers/header.js +94 -0
- package/dist_ts/core/routing/matchers/index.d.ts +18 -0
- package/dist_ts/core/routing/matchers/index.js +20 -0
- package/dist_ts/core/routing/matchers/ip.d.ts +53 -0
- package/dist_ts/core/routing/matchers/ip.js +169 -0
- package/dist_ts/core/routing/matchers/path.d.ts +44 -0
- package/dist_ts/core/routing/matchers/path.js +148 -0
- package/dist_ts/core/routing/route-manager.d.ts +88 -0
- package/dist_ts/core/routing/route-manager.js +342 -0
- package/dist_ts/core/routing/route-utils.d.ts +28 -0
- package/dist_ts/core/routing/route-utils.js +67 -0
- package/dist_ts/core/routing/specificity.d.ts +30 -0
- package/dist_ts/core/routing/specificity.js +115 -0
- package/dist_ts/core/routing/types.d.ts +41 -0
- package/dist_ts/core/routing/types.js +5 -0
- package/dist_ts/core/utils/index.d.ts +0 -2
- package/dist_ts/core/utils/index.js +1 -3
- package/dist_ts/core/utils/route-manager.d.ts +0 -30
- package/dist_ts/core/utils/route-manager.js +6 -47
- package/dist_ts/core/utils/route-utils.d.ts +2 -68
- package/dist_ts/core/utils/route-utils.js +21 -218
- package/dist_ts/core/utils/security-utils.js +4 -4
- package/dist_ts/core/utils/socket-utils.d.ts +0 -15
- package/dist_ts/core/utils/socket-utils.js +1 -35
- package/dist_ts/forwarding/handlers/https-terminate-to-http-handler.js +47 -32
- package/dist_ts/forwarding/handlers/https-terminate-to-https-handler.js +51 -32
- package/dist_ts/index.d.ts +2 -5
- package/dist_ts/index.js +5 -11
- package/dist_ts/proxies/http-proxy/http-proxy.d.ts +0 -1
- package/dist_ts/proxies/http-proxy/http-proxy.js +15 -60
- package/dist_ts/proxies/http-proxy/models/types.d.ts +0 -90
- package/dist_ts/proxies/http-proxy/models/types.js +1 -242
- package/dist_ts/proxies/http-proxy/request-handler.d.ts +3 -5
- package/dist_ts/proxies/http-proxy/request-handler.js +20 -171
- package/dist_ts/proxies/http-proxy/websocket-handler.d.ts +2 -5
- package/dist_ts/proxies/http-proxy/websocket-handler.js +15 -23
- package/dist_ts/proxies/index.d.ts +2 -2
- package/dist_ts/proxies/index.js +4 -3
- package/dist_ts/proxies/smart-proxy/connection-manager.d.ts +3 -1
- package/dist_ts/proxies/smart-proxy/connection-manager.js +15 -7
- package/dist_ts/proxies/smart-proxy/http-proxy-bridge.d.ts +2 -1
- package/dist_ts/proxies/smart-proxy/http-proxy-bridge.js +5 -2
- package/dist_ts/proxies/smart-proxy/index.d.ts +1 -1
- package/dist_ts/proxies/smart-proxy/index.js +2 -2
- package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +6 -2
- package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +1 -1
- package/dist_ts/proxies/smart-proxy/route-connection-handler.js +48 -25
- package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +1 -1
- package/dist_ts/proxies/smart-proxy/smart-proxy.js +15 -4
- package/dist_ts/proxies/smart-proxy/utils/route-utils.js +10 -43
- package/dist_ts/routing/router/http-router.d.ts +89 -0
- package/dist_ts/routing/router/http-router.js +205 -0
- package/dist_ts/routing/router/index.d.ts +2 -5
- package/dist_ts/routing/router/index.js +3 -4
- package/package.json +1 -1
- package/readme.delete.md +187 -0
- package/readme.hints.md +210 -1
- package/readme.plan.md +621 -0
- package/readme.routing.md +341 -0
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/core/models/index.ts +2 -0
- package/ts/core/models/socket-types.ts +21 -0
- package/ts/core/models/wrapped-socket.ts +99 -0
- package/ts/core/routing/index.ts +21 -0
- package/ts/core/routing/matchers/domain.ts +119 -0
- package/ts/core/routing/matchers/header.ts +120 -0
- package/ts/core/routing/matchers/index.ts +22 -0
- package/ts/core/routing/matchers/ip.ts +207 -0
- package/ts/core/routing/matchers/path.ts +184 -0
- package/ts/core/{utils → routing}/route-manager.ts +7 -57
- package/ts/core/routing/route-utils.ts +88 -0
- package/ts/core/routing/specificity.ts +141 -0
- package/ts/core/routing/types.ts +49 -0
- package/ts/core/utils/index.ts +0 -2
- package/ts/core/utils/security-utils.ts +3 -7
- package/ts/core/utils/socket-utils.ts +0 -44
- package/ts/forwarding/handlers/https-terminate-to-http-handler.ts +47 -33
- package/ts/forwarding/handlers/https-terminate-to-https-handler.ts +55 -35
- package/ts/index.ts +4 -14
- package/ts/proxies/http-proxy/http-proxy.ts +13 -68
- package/ts/proxies/http-proxy/models/types.ts +0 -324
- package/ts/proxies/http-proxy/request-handler.ts +15 -186
- package/ts/proxies/http-proxy/websocket-handler.ts +15 -26
- package/ts/proxies/index.ts +3 -2
- package/ts/proxies/smart-proxy/connection-manager.ts +15 -7
- package/ts/proxies/smart-proxy/http-proxy-bridge.ts +6 -2
- package/ts/proxies/smart-proxy/index.ts +1 -1
- package/ts/proxies/smart-proxy/models/interfaces.ts +8 -2
- package/ts/proxies/smart-proxy/route-connection-handler.ts +58 -30
- package/ts/proxies/smart-proxy/smart-proxy.ts +15 -3
- package/ts/proxies/smart-proxy/utils/route-utils.ts +11 -49
- package/ts/routing/router/http-router.ts +266 -0
- package/ts/routing/router/index.ts +3 -8
- package/readme.problems.md +0 -170
- package/ts/core/utils/route-utils.ts +0 -312
- package/ts/proxies/smart-proxy/route-manager.ts +0 -554
- package/ts/routing/router/proxy-router.ts +0 -437
- 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 //
|
|
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
|
-
|
|
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
|
-
//
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
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
|
|
3
|
+
import { type IHttpProxyOptions, type IWebSocketWithHeartbeat, type ILogger, createLogger } from './models/types.js';
|
|
4
4
|
import { ConnectionPool } from './connection-pool.js';
|
|
5
|
-
import {
|
|
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
|
|
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
|
|
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
|
|
33
|
+
// Initialize router if we have routes
|
|
35
34
|
if (routes.length > 0) {
|
|
36
|
-
this.
|
|
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.
|
|
48
|
-
this.
|
|
46
|
+
if (!this.router) {
|
|
47
|
+
this.router = new HttpRouter(routes, this.logger);
|
|
49
48
|
} else {
|
|
50
|
-
this.
|
|
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.
|
|
143
|
-
route = this.
|
|
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
|
-
//
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
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
|
package/ts/proxies/index.ts
CHANGED
|
@@ -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
|
-
|
|
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 {
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 '
|
|
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;
|