@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
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Route matching utilities for SmartProxy components
|
|
3
|
+
*
|
|
4
|
+
* This file provides utility functions that use the unified matchers
|
|
5
|
+
* and additional route-specific utilities.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { DomainMatcher, PathMatcher, IpMatcher, HeaderMatcher } from './matchers/index.js';
|
|
9
|
+
import { RouteSpecificity } from './specificity.js';
|
|
10
|
+
import type { IRouteSpecificity } from './types.js';
|
|
11
|
+
import type { IRouteConfig } from '../../proxies/smart-proxy/models/route-types.js';
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Match domains from a route against a given domain
|
|
16
|
+
*
|
|
17
|
+
* @param domains Array or single domain pattern to match against
|
|
18
|
+
* @param domain Domain to match
|
|
19
|
+
* @returns Whether the domain matches any of the patterns
|
|
20
|
+
*/
|
|
21
|
+
export function matchRouteDomain(domains: string | string[] | undefined, domain: string | undefined): boolean {
|
|
22
|
+
// If no domains specified in the route, match all domains
|
|
23
|
+
if (!domains) {
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// If no domain in the request, can't match domain-specific routes
|
|
28
|
+
if (!domain) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const patterns = Array.isArray(domains) ? domains : [domains];
|
|
33
|
+
return patterns.some(pattern => DomainMatcher.match(pattern, domain));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Calculate route specificity score
|
|
40
|
+
* Higher score means more specific matching criteria
|
|
41
|
+
*
|
|
42
|
+
* @param match Match criteria to evaluate
|
|
43
|
+
* @returns Numeric specificity score
|
|
44
|
+
*/
|
|
45
|
+
export function calculateRouteSpecificity(match: {
|
|
46
|
+
domains?: string | string[];
|
|
47
|
+
path?: string;
|
|
48
|
+
clientIp?: string[];
|
|
49
|
+
tlsVersion?: string[];
|
|
50
|
+
headers?: Record<string, string | RegExp>;
|
|
51
|
+
}): number {
|
|
52
|
+
let score = 0;
|
|
53
|
+
|
|
54
|
+
// Path specificity using PathMatcher
|
|
55
|
+
if (match.path) {
|
|
56
|
+
score += PathMatcher.calculateSpecificity(match.path);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Domain specificity using DomainMatcher
|
|
60
|
+
if (match.domains) {
|
|
61
|
+
const domains = Array.isArray(match.domains) ? match.domains : [match.domains];
|
|
62
|
+
// Use the highest specificity among all domains
|
|
63
|
+
const domainScore = Math.max(...domains.map(d => DomainMatcher.calculateSpecificity(d)));
|
|
64
|
+
score += domainScore;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Headers specificity using HeaderMatcher
|
|
68
|
+
if (match.headers) {
|
|
69
|
+
const stringHeaders: Record<string, string> = {};
|
|
70
|
+
for (const [key, value] of Object.entries(match.headers)) {
|
|
71
|
+
stringHeaders[key] = value instanceof RegExp ? value.source : value;
|
|
72
|
+
}
|
|
73
|
+
score += HeaderMatcher.calculateSpecificity(stringHeaders);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Client IP adds some specificity
|
|
77
|
+
if (match.clientIp && match.clientIp.length > 0) {
|
|
78
|
+
// Use the first IP pattern for specificity
|
|
79
|
+
score += IpMatcher.calculateSpecificity(match.clientIp[0]);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// TLS version adds minimal specificity
|
|
83
|
+
if (match.tlsVersion && match.tlsVersion.length > 0) {
|
|
84
|
+
score += match.tlsVersion.length * 10;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return score;
|
|
88
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import type { IRouteConfig } from '../../proxies/smart-proxy/models/route-types.js';
|
|
2
|
+
import type { IRouteSpecificity } from './types.js';
|
|
3
|
+
import { DomainMatcher, PathMatcher, IpMatcher, HeaderMatcher } from './matchers/index.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Unified route specificity calculator
|
|
7
|
+
* Provides consistent specificity scoring across all routing components
|
|
8
|
+
*/
|
|
9
|
+
export class RouteSpecificity {
|
|
10
|
+
/**
|
|
11
|
+
* Calculate the total specificity score for a route
|
|
12
|
+
* Higher scores indicate more specific routes that should match first
|
|
13
|
+
*/
|
|
14
|
+
static calculate(route: IRouteConfig): IRouteSpecificity {
|
|
15
|
+
const specificity: IRouteSpecificity = {
|
|
16
|
+
pathSpecificity: 0,
|
|
17
|
+
domainSpecificity: 0,
|
|
18
|
+
ipSpecificity: 0,
|
|
19
|
+
headerSpecificity: 0,
|
|
20
|
+
tlsSpecificity: 0,
|
|
21
|
+
totalScore: 0
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// Path specificity
|
|
25
|
+
if (route.match.path) {
|
|
26
|
+
specificity.pathSpecificity = PathMatcher.calculateSpecificity(route.match.path);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Domain specificity
|
|
30
|
+
if (route.match.domains) {
|
|
31
|
+
const domains = Array.isArray(route.match.domains)
|
|
32
|
+
? route.match.domains
|
|
33
|
+
: [route.match.domains];
|
|
34
|
+
|
|
35
|
+
// Use the highest specificity among all domains
|
|
36
|
+
specificity.domainSpecificity = Math.max(
|
|
37
|
+
...domains.map(d => DomainMatcher.calculateSpecificity(d))
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// IP specificity (clientIp is an array of IPs)
|
|
42
|
+
if (route.match.clientIp && route.match.clientIp.length > 0) {
|
|
43
|
+
// Use the first IP pattern for specificity calculation
|
|
44
|
+
specificity.ipSpecificity = IpMatcher.calculateSpecificity(route.match.clientIp[0]);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Header specificity (convert RegExp values to strings)
|
|
48
|
+
if (route.match.headers) {
|
|
49
|
+
const stringHeaders: Record<string, string> = {};
|
|
50
|
+
for (const [key, value] of Object.entries(route.match.headers)) {
|
|
51
|
+
stringHeaders[key] = value instanceof RegExp ? value.source : value;
|
|
52
|
+
}
|
|
53
|
+
specificity.headerSpecificity = HeaderMatcher.calculateSpecificity(stringHeaders);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// TLS version specificity
|
|
57
|
+
if (route.match.tlsVersion && route.match.tlsVersion.length > 0) {
|
|
58
|
+
specificity.tlsSpecificity = route.match.tlsVersion.length * 10;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Calculate total score with weights
|
|
62
|
+
specificity.totalScore =
|
|
63
|
+
specificity.pathSpecificity * 3 + // Path is most important
|
|
64
|
+
specificity.domainSpecificity * 2 + // Domain is second
|
|
65
|
+
specificity.ipSpecificity * 1.5 + // IP is moderately important
|
|
66
|
+
specificity.headerSpecificity * 1 + // Headers are less important
|
|
67
|
+
specificity.tlsSpecificity * 0.5; // TLS is least important
|
|
68
|
+
|
|
69
|
+
return specificity;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Compare two routes and determine which is more specific
|
|
74
|
+
* @returns positive if route1 is more specific, negative if route2 is more specific, 0 if equal
|
|
75
|
+
*/
|
|
76
|
+
static compare(route1: IRouteConfig, route2: IRouteConfig): number {
|
|
77
|
+
const spec1 = this.calculate(route1);
|
|
78
|
+
const spec2 = this.calculate(route2);
|
|
79
|
+
|
|
80
|
+
// First compare by total score
|
|
81
|
+
if (spec1.totalScore !== spec2.totalScore) {
|
|
82
|
+
return spec1.totalScore - spec2.totalScore;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// If total scores are equal, compare by individual components
|
|
86
|
+
// Path is most important tiebreaker
|
|
87
|
+
if (spec1.pathSpecificity !== spec2.pathSpecificity) {
|
|
88
|
+
return spec1.pathSpecificity - spec2.pathSpecificity;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Then domain
|
|
92
|
+
if (spec1.domainSpecificity !== spec2.domainSpecificity) {
|
|
93
|
+
return spec1.domainSpecificity - spec2.domainSpecificity;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Then IP
|
|
97
|
+
if (spec1.ipSpecificity !== spec2.ipSpecificity) {
|
|
98
|
+
return spec1.ipSpecificity - spec2.ipSpecificity;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Then headers
|
|
102
|
+
if (spec1.headerSpecificity !== spec2.headerSpecificity) {
|
|
103
|
+
return spec1.headerSpecificity - spec2.headerSpecificity;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Finally TLS
|
|
107
|
+
return spec1.tlsSpecificity - spec2.tlsSpecificity;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Sort routes by specificity (most specific first)
|
|
112
|
+
*/
|
|
113
|
+
static sort(routes: IRouteConfig[]): IRouteConfig[] {
|
|
114
|
+
return [...routes].sort((a, b) => this.compare(b, a));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Find the most specific route from a list
|
|
119
|
+
*/
|
|
120
|
+
static findMostSpecific(routes: IRouteConfig[]): IRouteConfig | null {
|
|
121
|
+
if (routes.length === 0) return null;
|
|
122
|
+
|
|
123
|
+
return routes.reduce((most, current) =>
|
|
124
|
+
this.compare(current, most) > 0 ? current : most
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Check if a route has any matching criteria
|
|
130
|
+
*/
|
|
131
|
+
static hasMatchCriteria(route: IRouteConfig): boolean {
|
|
132
|
+
const match = route.match;
|
|
133
|
+
return !!(
|
|
134
|
+
match.domains ||
|
|
135
|
+
match.path ||
|
|
136
|
+
match.clientIp?.length ||
|
|
137
|
+
match.headers ||
|
|
138
|
+
match.tlsVersion?.length
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core routing types used throughout the routing system
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface IPathMatchResult {
|
|
6
|
+
matches: boolean;
|
|
7
|
+
params?: Record<string, string>;
|
|
8
|
+
pathMatch?: string;
|
|
9
|
+
pathRemainder?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface IRouteMatchResult {
|
|
13
|
+
matches: boolean;
|
|
14
|
+
score: number;
|
|
15
|
+
specificity: number;
|
|
16
|
+
matchedCriteria: string[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface IDomainMatchOptions {
|
|
20
|
+
allowWildcards?: boolean;
|
|
21
|
+
caseInsensitive?: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface IIpMatchOptions {
|
|
25
|
+
allowCidr?: boolean;
|
|
26
|
+
allowRanges?: boolean;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface IHeaderMatchOptions {
|
|
30
|
+
caseInsensitive?: boolean;
|
|
31
|
+
exactMatch?: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface IRouteSpecificity {
|
|
35
|
+
pathSpecificity: number;
|
|
36
|
+
domainSpecificity: number;
|
|
37
|
+
ipSpecificity: number;
|
|
38
|
+
headerSpecificity: number;
|
|
39
|
+
tlsSpecificity: number;
|
|
40
|
+
totalScore: number;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface IMatcher<T = any, O = any> {
|
|
44
|
+
match(pattern: string, value: string, options?: O): T | boolean;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface IAsyncMatcher<T = any, O = any> {
|
|
48
|
+
match(pattern: string, value: string, options?: O): Promise<T | boolean>;
|
|
49
|
+
}
|
package/ts/core/utils/index.ts
CHANGED
|
@@ -5,8 +5,6 @@
|
|
|
5
5
|
export * from './validation-utils.js';
|
|
6
6
|
export * from './ip-utils.js';
|
|
7
7
|
export * from './template-utils.js';
|
|
8
|
-
export * from './route-manager.js';
|
|
9
|
-
export * from './route-utils.js';
|
|
10
8
|
export * from './security-utils.js';
|
|
11
9
|
export * from './shared-security-manager.js';
|
|
12
10
|
export * from './websocket-utils.js';
|
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
import * as plugins from '../../plugins.js';
|
|
2
|
-
import {
|
|
3
|
-
matchIpPattern,
|
|
4
|
-
ipToNumber,
|
|
5
|
-
matchIpCidr
|
|
6
|
-
} from './route-utils.js';
|
|
2
|
+
import { IpMatcher } from '../routing/matchers/ip.js';
|
|
7
3
|
|
|
8
4
|
/**
|
|
9
5
|
* Security utilities for IP validation, rate limiting,
|
|
@@ -90,7 +86,7 @@ export function isIPAuthorized(
|
|
|
90
86
|
// First check if IP is blocked - blocked IPs take precedence
|
|
91
87
|
if (blockedIPs.length > 0) {
|
|
92
88
|
for (const pattern of blockedIPs) {
|
|
93
|
-
if (
|
|
89
|
+
if (IpMatcher.match(pattern, ip)) {
|
|
94
90
|
return false;
|
|
95
91
|
}
|
|
96
92
|
}
|
|
@@ -104,7 +100,7 @@ export function isIPAuthorized(
|
|
|
104
100
|
// Then check if IP is allowed in the explicit allow list
|
|
105
101
|
if (allowedIPs.length > 0) {
|
|
106
102
|
for (const pattern of allowedIPs) {
|
|
107
|
-
if (
|
|
103
|
+
if (IpMatcher.match(pattern, ip)) {
|
|
108
104
|
return true;
|
|
109
105
|
}
|
|
110
106
|
}
|
|
@@ -67,37 +67,6 @@ export function cleanupSocket(
|
|
|
67
67
|
});
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
/**
|
|
71
|
-
* Create a cleanup handler for paired sockets (client and server)
|
|
72
|
-
* @param clientSocket The client socket
|
|
73
|
-
* @param serverSocket The server socket (optional)
|
|
74
|
-
* @param onCleanup Optional callback when cleanup is done
|
|
75
|
-
* @returns A cleanup function that can be called multiple times safely
|
|
76
|
-
* @deprecated Use createIndependentSocketHandlers for better half-open support
|
|
77
|
-
*/
|
|
78
|
-
export function createSocketCleanupHandler(
|
|
79
|
-
clientSocket: plugins.net.Socket | plugins.tls.TLSSocket,
|
|
80
|
-
serverSocket?: plugins.net.Socket | plugins.tls.TLSSocket | null,
|
|
81
|
-
onCleanup?: (reason: string) => void
|
|
82
|
-
): (reason: string) => void {
|
|
83
|
-
let cleanedUp = false;
|
|
84
|
-
|
|
85
|
-
return (reason: string) => {
|
|
86
|
-
if (cleanedUp) return;
|
|
87
|
-
cleanedUp = true;
|
|
88
|
-
|
|
89
|
-
// Cleanup both sockets (old behavior - too aggressive)
|
|
90
|
-
cleanupSocket(clientSocket, 'client', { immediate: true });
|
|
91
|
-
if (serverSocket) {
|
|
92
|
-
cleanupSocket(serverSocket, 'server', { immediate: true });
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Call cleanup callback if provided
|
|
96
|
-
if (onCleanup) {
|
|
97
|
-
onCleanup(reason);
|
|
98
|
-
}
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
70
|
|
|
102
71
|
/**
|
|
103
72
|
* Create independent cleanup handlers for paired sockets that support half-open connections
|
|
@@ -278,19 +247,6 @@ export function setupBidirectionalForwarding(
|
|
|
278
247
|
return { cleanupClient, cleanupServer };
|
|
279
248
|
}
|
|
280
249
|
|
|
281
|
-
/**
|
|
282
|
-
* Pipe two sockets together with proper cleanup on either end
|
|
283
|
-
* @param socket1 First socket
|
|
284
|
-
* @param socket2 Second socket
|
|
285
|
-
*/
|
|
286
|
-
export function pipeSockets(
|
|
287
|
-
socket1: plugins.net.Socket | plugins.tls.TLSSocket,
|
|
288
|
-
socket2: plugins.net.Socket | plugins.tls.TLSSocket
|
|
289
|
-
): void {
|
|
290
|
-
socket1.pipe(socket2);
|
|
291
|
-
socket2.pipe(socket1);
|
|
292
|
-
}
|
|
293
|
-
|
|
294
250
|
/**
|
|
295
251
|
* Create a socket with immediate error handling to prevent crashes
|
|
296
252
|
* @param options Socket creation options
|
|
@@ -2,7 +2,7 @@ import * as plugins from '../../plugins.js';
|
|
|
2
2
|
import { ForwardingHandler } from './base-handler.js';
|
|
3
3
|
import type { IForwardConfig } from '../config/forwarding-types.js';
|
|
4
4
|
import { ForwardingHandlerEvents } from '../config/forwarding-types.js';
|
|
5
|
-
import {
|
|
5
|
+
import { setupSocketHandlers, createSocketWithErrorHandler, setupBidirectionalForwarding } from '../../core/utils/socket-utils.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Handler for HTTPS termination with HTTP backend
|
|
@@ -100,19 +100,30 @@ export class HttpsTerminateToHttpHandler extends ForwardingHandler {
|
|
|
100
100
|
let backendSocket: plugins.net.Socket | null = null;
|
|
101
101
|
let dataBuffer = Buffer.alloc(0);
|
|
102
102
|
let connectionEstablished = false;
|
|
103
|
+
let forwardingSetup = false;
|
|
103
104
|
|
|
104
|
-
//
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
105
|
+
// Set up initial error handling for TLS socket
|
|
106
|
+
const tlsCleanupHandler = (reason: string) => {
|
|
107
|
+
if (!forwardingSetup) {
|
|
108
|
+
// If forwarding not set up yet, emit disconnected and cleanup
|
|
109
|
+
this.emit(ForwardingHandlerEvents.DISCONNECTED, {
|
|
110
|
+
remoteAddress,
|
|
111
|
+
reason
|
|
112
|
+
});
|
|
113
|
+
dataBuffer = Buffer.alloc(0);
|
|
114
|
+
connectionEstablished = false;
|
|
115
|
+
|
|
116
|
+
if (!tlsSocket.destroyed) {
|
|
117
|
+
tlsSocket.destroy();
|
|
118
|
+
}
|
|
119
|
+
if (backendSocket && !backendSocket.destroyed) {
|
|
120
|
+
backendSocket.destroy();
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// If forwarding is setup, setupBidirectionalForwarding will handle cleanup
|
|
124
|
+
};
|
|
113
125
|
|
|
114
|
-
|
|
115
|
-
setupSocketHandlers(tlsSocket, handleClose, undefined, 'tls');
|
|
126
|
+
setupSocketHandlers(tlsSocket, tlsCleanupHandler, undefined, 'tls');
|
|
116
127
|
|
|
117
128
|
// Set timeout
|
|
118
129
|
const timeout = this.getTimeout();
|
|
@@ -123,7 +134,7 @@ export class HttpsTerminateToHttpHandler extends ForwardingHandler {
|
|
|
123
134
|
remoteAddress,
|
|
124
135
|
error: 'TLS connection timeout'
|
|
125
136
|
});
|
|
126
|
-
|
|
137
|
+
tlsCleanupHandler('timeout');
|
|
127
138
|
});
|
|
128
139
|
|
|
129
140
|
// Handle TLS data
|
|
@@ -172,30 +183,33 @@ export class HttpsTerminateToHttpHandler extends ForwardingHandler {
|
|
|
172
183
|
dataBuffer = Buffer.alloc(0);
|
|
173
184
|
}
|
|
174
185
|
|
|
175
|
-
//
|
|
176
|
-
|
|
177
|
-
|
|
186
|
+
// Now set up bidirectional forwarding with proper cleanup
|
|
187
|
+
forwardingSetup = true;
|
|
188
|
+
setupBidirectionalForwarding(tlsSocket, backendSocket!, {
|
|
189
|
+
onCleanup: (reason) => {
|
|
190
|
+
this.emit(ForwardingHandlerEvents.DISCONNECTED, {
|
|
191
|
+
remoteAddress,
|
|
192
|
+
reason
|
|
193
|
+
});
|
|
194
|
+
dataBuffer = Buffer.alloc(0);
|
|
195
|
+
connectionEstablished = false;
|
|
196
|
+
forwardingSetup = false;
|
|
197
|
+
},
|
|
198
|
+
enableHalfOpen: false // Close both when one closes
|
|
199
|
+
});
|
|
178
200
|
}
|
|
179
201
|
});
|
|
180
202
|
|
|
181
|
-
//
|
|
182
|
-
const newHandleClose = createSocketCleanupHandler(tlsSocket, backendSocket, (reason) => {
|
|
183
|
-
this.emit(ForwardingHandlerEvents.DISCONNECTED, {
|
|
184
|
-
remoteAddress,
|
|
185
|
-
reason
|
|
186
|
-
});
|
|
187
|
-
dataBuffer = Buffer.alloc(0);
|
|
188
|
-
connectionEstablished = false;
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
// Set up handlers for backend socket
|
|
192
|
-
setupSocketHandlers(backendSocket, newHandleClose, undefined, 'backend');
|
|
193
|
-
|
|
203
|
+
// Additional error logging for backend socket
|
|
194
204
|
backendSocket.on('error', (error) => {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
205
|
+
if (!connectionEstablished) {
|
|
206
|
+
// Connection failed during setup
|
|
207
|
+
this.emit(ForwardingHandlerEvents.ERROR, {
|
|
208
|
+
remoteAddress,
|
|
209
|
+
error: `Target connection error: ${error.message}`
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
// If connected, setupBidirectionalForwarding handles cleanup
|
|
199
213
|
});
|
|
200
214
|
}
|
|
201
215
|
});
|
|
@@ -2,7 +2,7 @@ import * as plugins from '../../plugins.js';
|
|
|
2
2
|
import { ForwardingHandler } from './base-handler.js';
|
|
3
3
|
import type { IForwardConfig } from '../config/forwarding-types.js';
|
|
4
4
|
import { ForwardingHandlerEvents } from '../config/forwarding-types.js';
|
|
5
|
-
import {
|
|
5
|
+
import { setupSocketHandlers, createSocketWithErrorHandler, setupBidirectionalForwarding } from '../../core/utils/socket-utils.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Handler for HTTPS termination with HTTPS backend
|
|
@@ -96,17 +96,26 @@ export class HttpsTerminateToHttpsHandler extends ForwardingHandler {
|
|
|
96
96
|
|
|
97
97
|
// Variable to track backend socket
|
|
98
98
|
let backendSocket: plugins.tls.TLSSocket | null = null;
|
|
99
|
+
let isConnectedToBackend = false;
|
|
99
100
|
|
|
100
|
-
//
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
101
|
+
// Set up initial error handling for TLS socket
|
|
102
|
+
const tlsCleanupHandler = (reason: string) => {
|
|
103
|
+
if (!isConnectedToBackend) {
|
|
104
|
+
// If backend not connected yet, just emit disconnected event
|
|
105
|
+
this.emit(ForwardingHandlerEvents.DISCONNECTED, {
|
|
106
|
+
remoteAddress,
|
|
107
|
+
reason
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// Cleanup TLS socket if needed
|
|
111
|
+
if (!tlsSocket.destroyed) {
|
|
112
|
+
tlsSocket.destroy();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// If connected to backend, setupBidirectionalForwarding will handle cleanup
|
|
116
|
+
};
|
|
107
117
|
|
|
108
|
-
|
|
109
|
-
setupSocketHandlers(tlsSocket, handleClose, undefined, 'tls');
|
|
118
|
+
setupSocketHandlers(tlsSocket, tlsCleanupHandler, undefined, 'tls');
|
|
110
119
|
|
|
111
120
|
// Set timeout
|
|
112
121
|
const timeout = this.getTimeout();
|
|
@@ -117,7 +126,7 @@ export class HttpsTerminateToHttpsHandler extends ForwardingHandler {
|
|
|
117
126
|
remoteAddress,
|
|
118
127
|
error: 'TLS connection timeout'
|
|
119
128
|
});
|
|
120
|
-
|
|
129
|
+
tlsCleanupHandler('timeout');
|
|
121
130
|
});
|
|
122
131
|
|
|
123
132
|
// Get the target from configuration
|
|
@@ -131,44 +140,55 @@ export class HttpsTerminateToHttpsHandler extends ForwardingHandler {
|
|
|
131
140
|
// In a real implementation, we would configure TLS options
|
|
132
141
|
rejectUnauthorized: false // For testing only, never use in production
|
|
133
142
|
}, () => {
|
|
143
|
+
isConnectedToBackend = true;
|
|
144
|
+
|
|
134
145
|
this.emit(ForwardingHandlerEvents.DATA_FORWARDED, {
|
|
135
146
|
direction: 'outbound',
|
|
136
147
|
target: `${target.host}:${target.port}`,
|
|
137
148
|
tls: true
|
|
138
149
|
});
|
|
139
150
|
|
|
140
|
-
// Set up bidirectional
|
|
141
|
-
tlsSocket
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
151
|
+
// Set up bidirectional forwarding with proper cleanup
|
|
152
|
+
setupBidirectionalForwarding(tlsSocket, backendSocket!, {
|
|
153
|
+
onCleanup: (reason) => {
|
|
154
|
+
this.emit(ForwardingHandlerEvents.DISCONNECTED, {
|
|
155
|
+
remoteAddress,
|
|
156
|
+
reason
|
|
157
|
+
});
|
|
158
|
+
},
|
|
159
|
+
enableHalfOpen: false // Close both when one closes
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// Set timeout for backend socket
|
|
163
|
+
backendSocket!.setTimeout(timeout);
|
|
164
|
+
|
|
165
|
+
backendSocket!.on('timeout', () => {
|
|
166
|
+
this.emit(ForwardingHandlerEvents.ERROR, {
|
|
167
|
+
remoteAddress,
|
|
168
|
+
error: 'Backend connection timeout'
|
|
169
|
+
});
|
|
170
|
+
// Let setupBidirectionalForwarding handle the cleanup
|
|
150
171
|
});
|
|
151
172
|
});
|
|
152
173
|
|
|
153
|
-
//
|
|
154
|
-
setupSocketHandlers(backendSocket, newHandleClose, undefined, 'backend');
|
|
155
|
-
|
|
174
|
+
// Handle backend connection errors
|
|
156
175
|
backendSocket.on('error', (error) => {
|
|
157
176
|
this.emit(ForwardingHandlerEvents.ERROR, {
|
|
158
177
|
remoteAddress,
|
|
159
178
|
error: `Backend connection error: ${error.message}`
|
|
160
179
|
});
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
180
|
+
|
|
181
|
+
if (!isConnectedToBackend) {
|
|
182
|
+
// Connection failed, clean up TLS socket
|
|
183
|
+
if (!tlsSocket.destroyed) {
|
|
184
|
+
tlsSocket.destroy();
|
|
185
|
+
}
|
|
186
|
+
this.emit(ForwardingHandlerEvents.DISCONNECTED, {
|
|
187
|
+
remoteAddress,
|
|
188
|
+
reason: `backend_connection_failed: ${error.message}`
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
// If connected, let setupBidirectionalForwarding handle cleanup
|
|
172
192
|
});
|
|
173
193
|
};
|
|
174
194
|
|
package/ts/index.ts
CHANGED
|
@@ -2,28 +2,18 @@
|
|
|
2
2
|
* SmartProxy main module exports
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
//
|
|
6
|
-
// Migrated to the new proxies structure
|
|
5
|
+
// NFTables proxy exports
|
|
7
6
|
export * from './proxies/nftables-proxy/index.js';
|
|
8
7
|
|
|
9
|
-
// Export HttpProxy elements
|
|
8
|
+
// Export HttpProxy elements
|
|
10
9
|
export { HttpProxy, CertificateManager, ConnectionPool, RequestHandler, WebSocketHandler } from './proxies/http-proxy/index.js';
|
|
11
10
|
export type { IMetricsTracker, MetricsTracker } from './proxies/http-proxy/index.js';
|
|
12
|
-
// Export models except IAcmeOptions to avoid conflict
|
|
13
11
|
export type { IHttpProxyOptions, ICertificateEntry, ILogger } from './proxies/http-proxy/models/types.js';
|
|
14
|
-
export {
|
|
15
|
-
|
|
16
|
-
// Backward compatibility exports (deprecated)
|
|
17
|
-
export { HttpProxy as NetworkProxy } from './proxies/http-proxy/index.js';
|
|
18
|
-
export type { IHttpProxyOptions as INetworkProxyOptions } from './proxies/http-proxy/models/types.js';
|
|
19
|
-
export { HttpProxyBridge as NetworkProxyBridge } from './proxies/smart-proxy/index.js';
|
|
20
|
-
|
|
21
|
-
// Certificate and Port80 modules have been removed - use SmartCertManager instead
|
|
22
|
-
// Redirect module has been removed - use route-based redirects instead
|
|
12
|
+
export { SharedRouteManager as HttpProxyRouteManager } from './core/routing/route-manager.js';
|
|
23
13
|
|
|
24
14
|
// Export SmartProxy elements selectively to avoid RouteManager ambiguity
|
|
25
15
|
export { SmartProxy, ConnectionManager, SecurityManager, TimeoutManager, TlsManager, HttpProxyBridge, RouteConnectionHandler, SmartCertManager } from './proxies/smart-proxy/index.js';
|
|
26
|
-
export { RouteManager } from './
|
|
16
|
+
export { SharedRouteManager as RouteManager } from './core/routing/route-manager.js';
|
|
27
17
|
// Export smart-proxy models
|
|
28
18
|
export type { ISmartProxyOptions, IConnectionRecord, IRouteConfig, IRouteMatch, IRouteAction, IRouteTls, IRouteContext } from './proxies/smart-proxy/models/index.js';
|
|
29
19
|
export type { TSmartProxyCertProvisionObject } from './proxies/smart-proxy/models/interfaces.js';
|