@push.rocks/smartproxy 19.5.19 → 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 (101) 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 +0 -2
  28. package/dist_ts/core/utils/index.js +1 -3
  29. package/dist_ts/core/utils/route-manager.d.ts +0 -30
  30. package/dist_ts/core/utils/route-manager.js +6 -47
  31. package/dist_ts/core/utils/route-utils.d.ts +2 -68
  32. package/dist_ts/core/utils/route-utils.js +21 -218
  33. package/dist_ts/core/utils/security-utils.js +4 -4
  34. package/dist_ts/index.d.ts +2 -5
  35. package/dist_ts/index.js +5 -11
  36. package/dist_ts/proxies/http-proxy/http-proxy.d.ts +0 -1
  37. package/dist_ts/proxies/http-proxy/http-proxy.js +15 -60
  38. package/dist_ts/proxies/http-proxy/models/types.d.ts +0 -90
  39. package/dist_ts/proxies/http-proxy/models/types.js +1 -242
  40. package/dist_ts/proxies/http-proxy/request-handler.d.ts +3 -5
  41. package/dist_ts/proxies/http-proxy/request-handler.js +20 -171
  42. package/dist_ts/proxies/http-proxy/websocket-handler.d.ts +2 -5
  43. package/dist_ts/proxies/http-proxy/websocket-handler.js +15 -23
  44. package/dist_ts/proxies/index.d.ts +2 -2
  45. package/dist_ts/proxies/index.js +4 -3
  46. package/dist_ts/proxies/smart-proxy/connection-manager.d.ts +3 -1
  47. package/dist_ts/proxies/smart-proxy/connection-manager.js +15 -7
  48. package/dist_ts/proxies/smart-proxy/http-proxy-bridge.d.ts +2 -1
  49. package/dist_ts/proxies/smart-proxy/http-proxy-bridge.js +5 -2
  50. package/dist_ts/proxies/smart-proxy/index.d.ts +1 -1
  51. package/dist_ts/proxies/smart-proxy/index.js +2 -2
  52. package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +6 -2
  53. package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +1 -1
  54. package/dist_ts/proxies/smart-proxy/route-connection-handler.js +48 -25
  55. package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +1 -1
  56. package/dist_ts/proxies/smart-proxy/smart-proxy.js +15 -4
  57. package/dist_ts/proxies/smart-proxy/utils/route-utils.js +10 -43
  58. package/dist_ts/routing/router/http-router.d.ts +89 -0
  59. package/dist_ts/routing/router/http-router.js +205 -0
  60. package/dist_ts/routing/router/index.d.ts +2 -5
  61. package/dist_ts/routing/router/index.js +3 -4
  62. package/package.json +1 -1
  63. package/readme.delete.md +187 -0
  64. package/readme.hints.md +189 -1
  65. package/readme.plan.md +621 -0
  66. package/readme.routing.md +341 -0
  67. package/ts/core/models/index.ts +2 -0
  68. package/ts/core/models/socket-types.ts +21 -0
  69. package/ts/core/models/wrapped-socket.ts +99 -0
  70. package/ts/core/routing/index.ts +21 -0
  71. package/ts/core/routing/matchers/domain.ts +119 -0
  72. package/ts/core/routing/matchers/header.ts +120 -0
  73. package/ts/core/routing/matchers/index.ts +22 -0
  74. package/ts/core/routing/matchers/ip.ts +207 -0
  75. package/ts/core/routing/matchers/path.ts +184 -0
  76. package/ts/core/{utils → routing}/route-manager.ts +7 -57
  77. package/ts/core/routing/route-utils.ts +88 -0
  78. package/ts/core/routing/specificity.ts +141 -0
  79. package/ts/core/routing/types.ts +49 -0
  80. package/ts/core/utils/index.ts +0 -2
  81. package/ts/core/utils/security-utils.ts +3 -7
  82. package/ts/index.ts +4 -14
  83. package/ts/proxies/http-proxy/http-proxy.ts +13 -68
  84. package/ts/proxies/http-proxy/models/types.ts +0 -324
  85. package/ts/proxies/http-proxy/request-handler.ts +15 -186
  86. package/ts/proxies/http-proxy/websocket-handler.ts +15 -26
  87. package/ts/proxies/index.ts +3 -2
  88. package/ts/proxies/smart-proxy/connection-manager.ts +15 -7
  89. package/ts/proxies/smart-proxy/http-proxy-bridge.ts +6 -2
  90. package/ts/proxies/smart-proxy/index.ts +1 -1
  91. package/ts/proxies/smart-proxy/models/interfaces.ts +8 -2
  92. package/ts/proxies/smart-proxy/route-connection-handler.ts +58 -30
  93. package/ts/proxies/smart-proxy/smart-proxy.ts +15 -3
  94. package/ts/proxies/smart-proxy/utils/route-utils.ts +11 -49
  95. package/ts/routing/router/http-router.ts +266 -0
  96. package/ts/routing/router/index.ts +3 -8
  97. package/readme.problems.md +0 -170
  98. package/ts/core/utils/route-utils.ts +0 -312
  99. package/ts/proxies/smart-proxy/route-manager.ts +0 -554
  100. package/ts/routing/router/proxy-router.ts +0 -437
  101. package/ts/routing/router/route-router.ts +0 -482
@@ -1,554 +0,0 @@
1
- import * as plugins from '../../plugins.js';
2
- import type {
3
- IRouteConfig,
4
- IRouteMatch,
5
- IRouteAction,
6
- TPortRange
7
- } from './models/route-types.js';
8
- import type {
9
- ISmartProxyOptions
10
- } from './models/interfaces.js';
11
-
12
- /**
13
- * Result of route matching
14
- */
15
- export interface IRouteMatchResult {
16
- route: IRouteConfig;
17
- // Additional match parameters (path, query, etc.)
18
- params?: Record<string, string>;
19
- }
20
-
21
- /**
22
- * The RouteManager handles all routing decisions based on connections and attributes
23
- */
24
- export class RouteManager extends plugins.EventEmitter {
25
- private routes: IRouteConfig[] = [];
26
- private portMap: Map<number, IRouteConfig[]> = new Map();
27
- private options: ISmartProxyOptions;
28
-
29
- constructor(options: ISmartProxyOptions) {
30
- super();
31
-
32
- // Store options
33
- this.options = options;
34
-
35
- // Initialize routes from either source
36
- this.updateRoutes(this.options.routes);
37
- }
38
-
39
- /**
40
- * Update routes with new configuration
41
- */
42
- public updateRoutes(routes: IRouteConfig[] = []): void {
43
- // Sort routes by priority (higher first)
44
- this.routes = [...(routes || [])].sort((a, b) => {
45
- const priorityA = a.priority ?? 0;
46
- const priorityB = b.priority ?? 0;
47
- return priorityB - priorityA;
48
- });
49
-
50
- // Rebuild port mapping for fast lookups
51
- this.rebuildPortMap();
52
- }
53
-
54
- /**
55
- * Rebuild the port mapping for fast lookups
56
- * Also logs information about the ports being listened on
57
- */
58
- private rebuildPortMap(): void {
59
- this.portMap.clear();
60
- this.portRangeCache.clear(); // Clear cache when rebuilding
61
-
62
- // Track ports for logging
63
- const portToRoutesMap = new Map<number, string[]>();
64
-
65
- for (const route of this.routes) {
66
- const ports = this.expandPortRange(route.match.ports);
67
-
68
- // Skip if no ports were found
69
- if (ports.length === 0) {
70
- console.warn(`Route ${route.name || 'unnamed'} has no valid ports to listen on`);
71
- continue;
72
- }
73
-
74
- for (const port of ports) {
75
- // Add to portMap for routing
76
- if (!this.portMap.has(port)) {
77
- this.portMap.set(port, []);
78
- }
79
- this.portMap.get(port)!.push(route);
80
-
81
- // Add to tracking for logging
82
- if (!portToRoutesMap.has(port)) {
83
- portToRoutesMap.set(port, []);
84
- }
85
- portToRoutesMap.get(port)!.push(route.name || 'unnamed');
86
- }
87
- }
88
-
89
- // Log summary of ports and routes
90
- const totalPorts = this.portMap.size;
91
- const totalRoutes = this.routes.length;
92
- console.log(`Route manager configured with ${totalRoutes} routes across ${totalPorts} ports`);
93
-
94
- // Log port details if detailed logging is enabled
95
- const enableDetailedLogging = this.options.enableDetailedLogging;
96
- if (enableDetailedLogging) {
97
- for (const [port, routes] of this.portMap.entries()) {
98
- console.log(`Port ${port}: ${routes.length} routes (${portToRoutesMap.get(port)!.join(', ')})`);
99
- }
100
- }
101
- }
102
-
103
- /**
104
- * Expand a port range specification into an array of individual ports
105
- * Uses caching to improve performance for frequently used port ranges
106
- *
107
- * @public - Made public to allow external code to interpret port ranges
108
- */
109
- public expandPortRange(portRange: TPortRange): number[] {
110
- // For simple number, return immediately
111
- if (typeof portRange === 'number') {
112
- return [portRange];
113
- }
114
-
115
- // Create a cache key for this port range
116
- const cacheKey = JSON.stringify(portRange);
117
-
118
- // Check if we have a cached result
119
- if (this.portRangeCache.has(cacheKey)) {
120
- return this.portRangeCache.get(cacheKey)!;
121
- }
122
-
123
- // Process the port range
124
- let result: number[] = [];
125
-
126
- if (Array.isArray(portRange)) {
127
- // Handle array of port objects or numbers
128
- result = portRange.flatMap(item => {
129
- if (typeof item === 'number') {
130
- return [item];
131
- } else if (typeof item === 'object' && 'from' in item && 'to' in item) {
132
- // Handle port range object - check valid range
133
- if (item.from > item.to) {
134
- console.warn(`Invalid port range: from (${item.from}) > to (${item.to})`);
135
- return [];
136
- }
137
-
138
- // Handle port range object
139
- const ports: number[] = [];
140
- for (let p = item.from; p <= item.to; p++) {
141
- ports.push(p);
142
- }
143
- return ports;
144
- }
145
- return [];
146
- });
147
- }
148
-
149
- // Cache the result
150
- this.portRangeCache.set(cacheKey, result);
151
-
152
- return result;
153
- }
154
-
155
- /**
156
- * Memoization cache for expanded port ranges
157
- */
158
- private portRangeCache: Map<string, number[]> = new Map();
159
-
160
- /**
161
- * Get all ports that should be listened on
162
- * This method automatically infers all required ports from route configurations
163
- */
164
- public getListeningPorts(): number[] {
165
- // Return the unique set of ports from all routes
166
- return Array.from(this.portMap.keys());
167
- }
168
-
169
- /**
170
- * Get all routes for a given port
171
- */
172
- public getRoutesForPort(port: number): IRouteConfig[] {
173
- return this.portMap.get(port) || [];
174
- }
175
-
176
- /**
177
- * Get all routes
178
- */
179
- public getAllRoutes(): IRouteConfig[] {
180
- return [...this.routes];
181
- }
182
-
183
- /**
184
- * Test if a pattern matches a domain using glob matching
185
- */
186
- private matchDomain(pattern: string, domain: string): boolean {
187
- // Convert glob pattern to regex
188
- const regexPattern = pattern
189
- .replace(/\./g, '\\.') // Escape dots
190
- .replace(/\*/g, '.*'); // Convert * to .*
191
-
192
- const regex = new RegExp(`^${regexPattern}$`, 'i');
193
- return regex.test(domain);
194
- }
195
-
196
- /**
197
- * Match a domain against all patterns in a route
198
- */
199
- private matchRouteDomain(route: IRouteConfig, domain: string): boolean {
200
- if (!route.match.domains) {
201
- // If no domains specified, match all domains
202
- return true;
203
- }
204
-
205
- const patterns = Array.isArray(route.match.domains)
206
- ? route.match.domains
207
- : [route.match.domains];
208
-
209
- return patterns.some(pattern => this.matchDomain(pattern, domain));
210
- }
211
-
212
- /**
213
- * Check if a client IP is allowed by a route's security settings
214
- * @deprecated Security is now checked in route-connection-handler.ts after route matching
215
- */
216
- private isClientIpAllowed(route: IRouteConfig, clientIp: string): boolean {
217
- const security = route.security;
218
-
219
- if (!security) {
220
- return true; // No security settings means allowed
221
- }
222
-
223
- // Check blocked IPs first
224
- if (security.ipBlockList && security.ipBlockList.length > 0) {
225
- for (const pattern of security.ipBlockList) {
226
- if (this.matchIpPattern(pattern, clientIp)) {
227
- return false; // IP is blocked
228
- }
229
- }
230
- }
231
-
232
- // If there are allowed IPs, check them
233
- if (security.ipAllowList && security.ipAllowList.length > 0) {
234
- for (const pattern of security.ipAllowList) {
235
- if (this.matchIpPattern(pattern, clientIp)) {
236
- return true; // IP is allowed
237
- }
238
- }
239
- return false; // IP not in allowed list
240
- }
241
-
242
- // No allowed IPs specified, so IP is allowed
243
- return true;
244
- }
245
-
246
- /**
247
- * Match an IP against a pattern
248
- */
249
- private matchIpPattern(pattern: string, ip: string): boolean {
250
- // Normalize IPv6-mapped IPv4 addresses
251
- const normalizedIp = ip.startsWith('::ffff:') ? ip.substring(7) : ip;
252
- const normalizedPattern = pattern.startsWith('::ffff:') ? pattern.substring(7) : pattern;
253
-
254
- // Handle exact match with normalized addresses
255
- if (pattern === ip || normalizedPattern === normalizedIp ||
256
- pattern === normalizedIp || normalizedPattern === ip) {
257
- return true;
258
- }
259
-
260
- // Handle CIDR notation (e.g., 192.168.1.0/24)
261
- if (pattern.includes('/')) {
262
- return this.matchIpCidr(pattern, normalizedIp) ||
263
- (normalizedPattern !== pattern && this.matchIpCidr(normalizedPattern, normalizedIp));
264
- }
265
-
266
- // Handle glob pattern (e.g., 192.168.1.*)
267
- if (pattern.includes('*')) {
268
- const regexPattern = pattern.replace(/\./g, '\\.').replace(/\*/g, '.*');
269
- const regex = new RegExp(`^${regexPattern}$`);
270
- if (regex.test(ip) || regex.test(normalizedIp)) {
271
- return true;
272
- }
273
-
274
- // If pattern was normalized, also test with normalized pattern
275
- if (normalizedPattern !== pattern) {
276
- const normalizedRegexPattern = normalizedPattern.replace(/\./g, '\\.').replace(/\*/g, '.*');
277
- const normalizedRegex = new RegExp(`^${normalizedRegexPattern}$`);
278
- return normalizedRegex.test(ip) || normalizedRegex.test(normalizedIp);
279
- }
280
- }
281
-
282
- return false;
283
- }
284
-
285
- /**
286
- * Match an IP against a CIDR pattern
287
- */
288
- private matchIpCidr(cidr: string, ip: string): boolean {
289
- try {
290
- // In a real implementation, you'd use a proper IP library
291
- // This is a simplified implementation
292
- const [subnet, bits] = cidr.split('/');
293
- const mask = parseInt(bits, 10);
294
-
295
- // Normalize IPv6-mapped IPv4 addresses
296
- const normalizedIp = ip.startsWith('::ffff:') ? ip.substring(7) : ip;
297
- const normalizedSubnet = subnet.startsWith('::ffff:') ? subnet.substring(7) : subnet;
298
-
299
- // Convert IP addresses to numeric values
300
- const ipNum = this.ipToNumber(normalizedIp);
301
- const subnetNum = this.ipToNumber(normalizedSubnet);
302
-
303
- // Calculate subnet mask
304
- const maskNum = ~(2 ** (32 - mask) - 1);
305
-
306
- // Check if IP is in subnet
307
- return (ipNum & maskNum) === (subnetNum & maskNum);
308
- } catch (e) {
309
- console.error(`Error matching IP ${ip} against CIDR ${cidr}:`, e);
310
- return false;
311
- }
312
- }
313
-
314
- /**
315
- * Convert an IP address to a numeric value
316
- */
317
- private ipToNumber(ip: string): number {
318
- // Normalize IPv6-mapped IPv4 addresses
319
- const normalizedIp = ip.startsWith('::ffff:') ? ip.substring(7) : ip;
320
-
321
- const parts = normalizedIp.split('.').map(part => parseInt(part, 10));
322
- return (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8) | parts[3];
323
- }
324
-
325
- /**
326
- * Find the matching route for a connection
327
- */
328
- public findMatchingRoute(options: {
329
- port: number;
330
- domain?: string;
331
- clientIp: string;
332
- path?: string;
333
- tlsVersion?: string;
334
- skipDomainCheck?: boolean;
335
- }): IRouteMatchResult | null {
336
- const { port, domain, clientIp, path, tlsVersion, skipDomainCheck } = options;
337
-
338
- // Get all routes for this port
339
- const routesForPort = this.getRoutesForPort(port);
340
-
341
- // Find the first matching route based on priority order
342
- for (const route of routesForPort) {
343
- // Check domain match
344
- // If the route has domain restrictions and we have a domain to check
345
- if (route.match.domains && !skipDomainCheck) {
346
- // If no domain was provided (non-TLS or no SNI), this route doesn't match
347
- if (!domain) {
348
- continue;
349
- }
350
- // If domain is provided but doesn't match the route's domains, skip
351
- if (!this.matchRouteDomain(route, domain)) {
352
- continue;
353
- }
354
- }
355
- // If route has no domain restrictions, it matches all domains
356
- // If skipDomainCheck is true, we skip domain validation for HTTP connections
357
-
358
- // Check path match if specified in both route and request
359
- if (path && route.match.path) {
360
- if (!this.matchPath(route.match.path, path)) {
361
- continue;
362
- }
363
- }
364
-
365
- // Check client IP match
366
- if (route.match.clientIp && !route.match.clientIp.some(pattern =>
367
- this.matchIpPattern(pattern, clientIp))) {
368
- continue;
369
- }
370
-
371
- // Check TLS version match
372
- if (tlsVersion && route.match.tlsVersion &&
373
- !route.match.tlsVersion.includes(tlsVersion)) {
374
- continue;
375
- }
376
-
377
- // All checks passed, this route matches
378
- // NOTE: Security is checked AFTER route matching in route-connection-handler.ts
379
- return { route };
380
- }
381
-
382
- return null;
383
- }
384
-
385
- /**
386
- * Match a path against a pattern
387
- */
388
- private matchPath(pattern: string, path: string): boolean {
389
- // Convert the glob pattern to a regex
390
- const regexPattern = pattern
391
- .replace(/\./g, '\\.') // Escape dots
392
- .replace(/\*/g, '.*') // Convert * to .*
393
- .replace(/\//g, '\\/'); // Escape slashes
394
-
395
- const regex = new RegExp(`^${regexPattern}$`);
396
- return regex.test(path);
397
- }
398
-
399
- /**
400
- * Domain-based configuration methods have been removed
401
- * as part of the migration to pure route-based configuration
402
- */
403
-
404
- /**
405
- * Validate the route configuration and return any warnings
406
- */
407
- public validateConfiguration(): string[] {
408
- const warnings: string[] = [];
409
- const duplicatePorts = new Map<number, number>();
410
-
411
- // Check for routes with the same exact match criteria
412
- for (let i = 0; i < this.routes.length; i++) {
413
- for (let j = i + 1; j < this.routes.length; j++) {
414
- const route1 = this.routes[i];
415
- const route2 = this.routes[j];
416
-
417
- // Check if route match criteria are the same
418
- if (this.areMatchesSimilar(route1.match, route2.match)) {
419
- warnings.push(
420
- `Routes "${route1.name || i}" and "${route2.name || j}" have similar match criteria. ` +
421
- `The route with higher priority (${Math.max(route1.priority || 0, route2.priority || 0)}) will be used.`
422
- );
423
- }
424
- }
425
- }
426
-
427
- // Check for routes that may never be matched due to priority
428
- for (let i = 0; i < this.routes.length; i++) {
429
- const route = this.routes[i];
430
- const higherPriorityRoutes = this.routes.filter(r =>
431
- (r.priority || 0) > (route.priority || 0));
432
-
433
- for (const higherRoute of higherPriorityRoutes) {
434
- if (this.isRouteShadowed(route, higherRoute)) {
435
- warnings.push(
436
- `Route "${route.name || i}" may never be matched because it is shadowed by ` +
437
- `higher priority route "${higherRoute.name || 'unnamed'}"`
438
- );
439
- break;
440
- }
441
- }
442
- }
443
-
444
- return warnings;
445
- }
446
-
447
- /**
448
- * Check if two route matches are similar (potential conflict)
449
- */
450
- private areMatchesSimilar(match1: IRouteMatch, match2: IRouteMatch): boolean {
451
- // Check port overlap
452
- const ports1 = new Set(this.expandPortRange(match1.ports));
453
- const ports2 = new Set(this.expandPortRange(match2.ports));
454
-
455
- let havePortOverlap = false;
456
- for (const port of ports1) {
457
- if (ports2.has(port)) {
458
- havePortOverlap = true;
459
- break;
460
- }
461
- }
462
-
463
- if (!havePortOverlap) {
464
- return false;
465
- }
466
-
467
- // Check domain overlap
468
- if (match1.domains && match2.domains) {
469
- const domains1 = Array.isArray(match1.domains) ? match1.domains : [match1.domains];
470
- const domains2 = Array.isArray(match2.domains) ? match2.domains : [match2.domains];
471
-
472
- // Check if any domain pattern from match1 could match any from match2
473
- let haveDomainOverlap = false;
474
- for (const domain1 of domains1) {
475
- for (const domain2 of domains2) {
476
- if (domain1 === domain2 ||
477
- (domain1.includes('*') || domain2.includes('*'))) {
478
- haveDomainOverlap = true;
479
- break;
480
- }
481
- }
482
- if (haveDomainOverlap) break;
483
- }
484
-
485
- if (!haveDomainOverlap) {
486
- return false;
487
- }
488
- } else if (match1.domains || match2.domains) {
489
- // One has domains, the other doesn't - they could overlap
490
- // The one with domains is more specific, so it's not exactly a conflict
491
- return false;
492
- }
493
-
494
- // Check path overlap
495
- if (match1.path && match2.path) {
496
- // This is a simplified check - in a real implementation,
497
- // you'd need to check if the path patterns could match the same paths
498
- return match1.path === match2.path ||
499
- match1.path.includes('*') ||
500
- match2.path.includes('*');
501
- } else if (match1.path || match2.path) {
502
- // One has a path, the other doesn't
503
- return false;
504
- }
505
-
506
- // If we get here, the matches have significant overlap
507
- return true;
508
- }
509
-
510
- /**
511
- * Check if a route is completely shadowed by a higher priority route
512
- */
513
- private isRouteShadowed(route: IRouteConfig, higherPriorityRoute: IRouteConfig): boolean {
514
- // If they don't have similar match criteria, no shadowing occurs
515
- if (!this.areMatchesSimilar(route.match, higherPriorityRoute.match)) {
516
- return false;
517
- }
518
-
519
- // If higher priority route has more specific criteria, no shadowing
520
- if (this.isRouteMoreSpecific(higherPriorityRoute.match, route.match)) {
521
- return false;
522
- }
523
-
524
- // If higher priority route is equally or less specific but has higher priority,
525
- // it shadows the lower priority route
526
- return true;
527
- }
528
-
529
- /**
530
- * Check if route1 is more specific than route2
531
- */
532
- private isRouteMoreSpecific(match1: IRouteMatch, match2: IRouteMatch): boolean {
533
- // Check if match1 has more specific criteria
534
- let match1Points = 0;
535
- let match2Points = 0;
536
-
537
- // Path is the most specific
538
- if (match1.path) match1Points += 3;
539
- if (match2.path) match2Points += 3;
540
-
541
- // Domain is next most specific
542
- if (match1.domains) match1Points += 2;
543
- if (match2.domains) match2Points += 2;
544
-
545
- // Client IP and TLS version are least specific
546
- if (match1.clientIp) match1Points += 1;
547
- if (match2.clientIp) match2Points += 1;
548
-
549
- if (match1.tlsVersion) match1Points += 1;
550
- if (match2.tlsVersion) match2Points += 1;
551
-
552
- return match1Points > match2Points;
553
- }
554
- }