@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
@@ -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
+ }
@@ -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';
@@ -17,3 +15,4 @@ export * from './lifecycle-component.js';
17
15
  export * from './binary-heap.js';
18
16
  export * from './enhanced-connection-pool.js';
19
17
  export * from './socket-utils.js';
18
+ export * from './proxy-protocol.js';
@@ -0,0 +1,246 @@
1
+ import * as plugins from '../../plugins.js';
2
+ import { logger } from './logger.js';
3
+
4
+ /**
5
+ * Interface representing parsed PROXY protocol information
6
+ */
7
+ export interface IProxyInfo {
8
+ protocol: 'TCP4' | 'TCP6' | 'UNKNOWN';
9
+ sourceIP: string;
10
+ sourcePort: number;
11
+ destinationIP: string;
12
+ destinationPort: number;
13
+ }
14
+
15
+ /**
16
+ * Interface for parse result including remaining data
17
+ */
18
+ export interface IProxyParseResult {
19
+ proxyInfo: IProxyInfo | null;
20
+ remainingData: Buffer;
21
+ }
22
+
23
+ /**
24
+ * Parser for PROXY protocol v1 (text format)
25
+ * Spec: https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
26
+ */
27
+ export class ProxyProtocolParser {
28
+ static readonly PROXY_V1_SIGNATURE = 'PROXY ';
29
+ static readonly MAX_HEADER_LENGTH = 107; // Max length for v1 header
30
+ static readonly HEADER_TERMINATOR = '\r\n';
31
+
32
+ /**
33
+ * Parse PROXY protocol v1 header from buffer
34
+ * Returns proxy info and remaining data after header
35
+ */
36
+ static parse(data: Buffer): IProxyParseResult {
37
+ // Check if buffer starts with PROXY signature
38
+ if (!data.toString('ascii', 0, 6).startsWith(this.PROXY_V1_SIGNATURE)) {
39
+ return {
40
+ proxyInfo: null,
41
+ remainingData: data
42
+ };
43
+ }
44
+
45
+ // Find header terminator
46
+ const headerEndIndex = data.indexOf(this.HEADER_TERMINATOR);
47
+ if (headerEndIndex === -1) {
48
+ // Header incomplete, need more data
49
+ if (data.length > this.MAX_HEADER_LENGTH) {
50
+ // Header too long, invalid
51
+ throw new Error('PROXY protocol header exceeds maximum length');
52
+ }
53
+ return {
54
+ proxyInfo: null,
55
+ remainingData: data
56
+ };
57
+ }
58
+
59
+ // Extract header line
60
+ const headerLine = data.toString('ascii', 0, headerEndIndex);
61
+ const remainingData = data.slice(headerEndIndex + 2); // Skip \r\n
62
+
63
+ // Parse header
64
+ const parts = headerLine.split(' ');
65
+
66
+ if (parts.length < 2) {
67
+ throw new Error(`Invalid PROXY protocol header format: ${headerLine}`);
68
+ }
69
+
70
+ const [signature, protocol] = parts;
71
+
72
+ // Validate protocol
73
+ if (!['TCP4', 'TCP6', 'UNKNOWN'].includes(protocol)) {
74
+ throw new Error(`Invalid PROXY protocol: ${protocol}`);
75
+ }
76
+
77
+ // For UNKNOWN protocol, ignore addresses
78
+ if (protocol === 'UNKNOWN') {
79
+ return {
80
+ proxyInfo: {
81
+ protocol: 'UNKNOWN',
82
+ sourceIP: '',
83
+ sourcePort: 0,
84
+ destinationIP: '',
85
+ destinationPort: 0
86
+ },
87
+ remainingData
88
+ };
89
+ }
90
+
91
+ // For TCP4/TCP6, we need all 6 parts
92
+ if (parts.length !== 6) {
93
+ throw new Error(`Invalid PROXY protocol header format: ${headerLine}`);
94
+ }
95
+
96
+ const [, , srcIP, dstIP, srcPort, dstPort] = parts;
97
+
98
+ // Validate and parse ports
99
+ const sourcePort = parseInt(srcPort, 10);
100
+ const destinationPort = parseInt(dstPort, 10);
101
+
102
+ if (isNaN(sourcePort) || sourcePort < 0 || sourcePort > 65535) {
103
+ throw new Error(`Invalid source port: ${srcPort}`);
104
+ }
105
+
106
+ if (isNaN(destinationPort) || destinationPort < 0 || destinationPort > 65535) {
107
+ throw new Error(`Invalid destination port: ${dstPort}`);
108
+ }
109
+
110
+ // Validate IP addresses
111
+ const protocolType = protocol as 'TCP4' | 'TCP6' | 'UNKNOWN';
112
+ if (!this.isValidIP(srcIP, protocolType)) {
113
+ throw new Error(`Invalid source IP for ${protocol}: ${srcIP}`);
114
+ }
115
+
116
+ if (!this.isValidIP(dstIP, protocolType)) {
117
+ throw new Error(`Invalid destination IP for ${protocol}: ${dstIP}`);
118
+ }
119
+
120
+ return {
121
+ proxyInfo: {
122
+ protocol: protocol as 'TCP4' | 'TCP6',
123
+ sourceIP: srcIP,
124
+ sourcePort,
125
+ destinationIP: dstIP,
126
+ destinationPort
127
+ },
128
+ remainingData
129
+ };
130
+ }
131
+
132
+ /**
133
+ * Generate PROXY protocol v1 header
134
+ */
135
+ static generate(info: IProxyInfo): Buffer {
136
+ if (info.protocol === 'UNKNOWN') {
137
+ return Buffer.from(`PROXY UNKNOWN\r\n`, 'ascii');
138
+ }
139
+
140
+ const header = `PROXY ${info.protocol} ${info.sourceIP} ${info.destinationIP} ${info.sourcePort} ${info.destinationPort}\r\n`;
141
+
142
+ if (header.length > this.MAX_HEADER_LENGTH) {
143
+ throw new Error('Generated PROXY protocol header exceeds maximum length');
144
+ }
145
+
146
+ return Buffer.from(header, 'ascii');
147
+ }
148
+
149
+ /**
150
+ * Validate IP address format
151
+ */
152
+ private static isValidIP(ip: string, protocol: 'TCP4' | 'TCP6' | 'UNKNOWN'): boolean {
153
+ if (protocol === 'TCP4') {
154
+ return plugins.net.isIPv4(ip);
155
+ } else if (protocol === 'TCP6') {
156
+ return plugins.net.isIPv6(ip);
157
+ }
158
+ return false;
159
+ }
160
+
161
+ /**
162
+ * Attempt to read a complete PROXY protocol header from a socket
163
+ * Returns null if no PROXY protocol detected or incomplete
164
+ */
165
+ static async readFromSocket(socket: plugins.net.Socket, timeout: number = 5000): Promise<IProxyParseResult | null> {
166
+ return new Promise((resolve) => {
167
+ let buffer = Buffer.alloc(0);
168
+ let resolved = false;
169
+
170
+ const cleanup = () => {
171
+ socket.removeListener('data', onData);
172
+ socket.removeListener('error', onError);
173
+ clearTimeout(timer);
174
+ };
175
+
176
+ const timer = setTimeout(() => {
177
+ if (!resolved) {
178
+ resolved = true;
179
+ cleanup();
180
+ resolve({
181
+ proxyInfo: null,
182
+ remainingData: buffer
183
+ });
184
+ }
185
+ }, timeout);
186
+
187
+ const onData = (chunk: Buffer) => {
188
+ buffer = Buffer.concat([buffer, chunk]);
189
+
190
+ // Check if we have enough data
191
+ if (!buffer.toString('ascii', 0, Math.min(6, buffer.length)).startsWith(this.PROXY_V1_SIGNATURE)) {
192
+ // Not PROXY protocol
193
+ resolved = true;
194
+ cleanup();
195
+ resolve({
196
+ proxyInfo: null,
197
+ remainingData: buffer
198
+ });
199
+ return;
200
+ }
201
+
202
+ // Try to parse
203
+ try {
204
+ const result = this.parse(buffer);
205
+ if (result.proxyInfo) {
206
+ // Successfully parsed
207
+ resolved = true;
208
+ cleanup();
209
+ resolve(result);
210
+ } else if (buffer.length > this.MAX_HEADER_LENGTH) {
211
+ // Header too long
212
+ resolved = true;
213
+ cleanup();
214
+ resolve({
215
+ proxyInfo: null,
216
+ remainingData: buffer
217
+ });
218
+ }
219
+ // Otherwise continue reading
220
+ } catch (error) {
221
+ // Parse error
222
+ logger.log('error', `PROXY protocol parse error: ${error.message}`);
223
+ resolved = true;
224
+ cleanup();
225
+ resolve({
226
+ proxyInfo: null,
227
+ remainingData: buffer
228
+ });
229
+ }
230
+ };
231
+
232
+ const onError = (error: Error) => {
233
+ logger.log('error', `Socket error while reading PROXY protocol: ${error.message}`);
234
+ resolved = true;
235
+ cleanup();
236
+ resolve({
237
+ proxyInfo: null,
238
+ remainingData: buffer
239
+ });
240
+ };
241
+
242
+ socket.on('data', onData);
243
+ socket.on('error', onError);
244
+ });
245
+ }
246
+ }
@@ -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 (matchIpPattern(pattern, ip)) {
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 (matchIpPattern(pattern, ip)) {
103
+ if (IpMatcher.match(pattern, ip)) {
108
104
  return true;
109
105
  }
110
106
  }
package/ts/index.ts CHANGED
@@ -2,28 +2,18 @@
2
2
  * SmartProxy main module exports
3
3
  */
4
4
 
5
- // Legacy exports (to maintain backward compatibility)
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 selectively to avoid RouteManager ambiguity
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 { RouteManager as HttpProxyRouteManager } from './proxies/http-proxy/models/types.js';
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 './proxies/smart-proxy/route-manager.js';
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';