@push.rocks/smartproxy 19.5.18 → 19.5.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/dist_ts/00_commitinfo_data.js +2 -2
  2. package/dist_ts/core/models/index.d.ts +2 -0
  3. package/dist_ts/core/models/index.js +3 -1
  4. package/dist_ts/core/models/socket-types.d.ts +14 -0
  5. package/dist_ts/core/models/socket-types.js +15 -0
  6. package/dist_ts/core/models/wrapped-socket.d.ts +34 -0
  7. package/dist_ts/core/models/wrapped-socket.js +82 -0
  8. package/dist_ts/core/routing/index.d.ts +11 -0
  9. package/dist_ts/core/routing/index.js +17 -0
  10. package/dist_ts/core/routing/matchers/domain.d.ts +34 -0
  11. package/dist_ts/core/routing/matchers/domain.js +91 -0
  12. package/dist_ts/core/routing/matchers/header.d.ts +32 -0
  13. package/dist_ts/core/routing/matchers/header.js +94 -0
  14. package/dist_ts/core/routing/matchers/index.d.ts +18 -0
  15. package/dist_ts/core/routing/matchers/index.js +20 -0
  16. package/dist_ts/core/routing/matchers/ip.d.ts +53 -0
  17. package/dist_ts/core/routing/matchers/ip.js +169 -0
  18. package/dist_ts/core/routing/matchers/path.d.ts +44 -0
  19. package/dist_ts/core/routing/matchers/path.js +148 -0
  20. package/dist_ts/core/routing/route-manager.d.ts +88 -0
  21. package/dist_ts/core/routing/route-manager.js +342 -0
  22. package/dist_ts/core/routing/route-utils.d.ts +28 -0
  23. package/dist_ts/core/routing/route-utils.js +67 -0
  24. package/dist_ts/core/routing/specificity.d.ts +30 -0
  25. package/dist_ts/core/routing/specificity.js +115 -0
  26. package/dist_ts/core/routing/types.d.ts +41 -0
  27. package/dist_ts/core/routing/types.js +5 -0
  28. package/dist_ts/core/utils/index.d.ts +0 -2
  29. package/dist_ts/core/utils/index.js +1 -3
  30. package/dist_ts/core/utils/route-manager.d.ts +0 -30
  31. package/dist_ts/core/utils/route-manager.js +6 -47
  32. package/dist_ts/core/utils/route-utils.d.ts +2 -68
  33. package/dist_ts/core/utils/route-utils.js +21 -218
  34. package/dist_ts/core/utils/security-utils.js +4 -4
  35. package/dist_ts/core/utils/socket-utils.d.ts +0 -15
  36. package/dist_ts/core/utils/socket-utils.js +1 -35
  37. package/dist_ts/forwarding/handlers/https-terminate-to-http-handler.js +47 -32
  38. package/dist_ts/forwarding/handlers/https-terminate-to-https-handler.js +51 -32
  39. package/dist_ts/index.d.ts +2 -5
  40. package/dist_ts/index.js +5 -11
  41. package/dist_ts/proxies/http-proxy/http-proxy.d.ts +0 -1
  42. package/dist_ts/proxies/http-proxy/http-proxy.js +15 -60
  43. package/dist_ts/proxies/http-proxy/models/types.d.ts +0 -90
  44. package/dist_ts/proxies/http-proxy/models/types.js +1 -242
  45. package/dist_ts/proxies/http-proxy/request-handler.d.ts +3 -5
  46. package/dist_ts/proxies/http-proxy/request-handler.js +20 -171
  47. package/dist_ts/proxies/http-proxy/websocket-handler.d.ts +2 -5
  48. package/dist_ts/proxies/http-proxy/websocket-handler.js +15 -23
  49. package/dist_ts/proxies/index.d.ts +2 -2
  50. package/dist_ts/proxies/index.js +4 -3
  51. package/dist_ts/proxies/smart-proxy/connection-manager.d.ts +3 -1
  52. package/dist_ts/proxies/smart-proxy/connection-manager.js +15 -7
  53. package/dist_ts/proxies/smart-proxy/http-proxy-bridge.d.ts +2 -1
  54. package/dist_ts/proxies/smart-proxy/http-proxy-bridge.js +5 -2
  55. package/dist_ts/proxies/smart-proxy/index.d.ts +1 -1
  56. package/dist_ts/proxies/smart-proxy/index.js +2 -2
  57. package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +6 -2
  58. package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +1 -1
  59. package/dist_ts/proxies/smart-proxy/route-connection-handler.js +48 -25
  60. package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +1 -1
  61. package/dist_ts/proxies/smart-proxy/smart-proxy.js +15 -4
  62. package/dist_ts/proxies/smart-proxy/utils/route-utils.js +10 -43
  63. package/dist_ts/routing/router/http-router.d.ts +89 -0
  64. package/dist_ts/routing/router/http-router.js +205 -0
  65. package/dist_ts/routing/router/index.d.ts +2 -5
  66. package/dist_ts/routing/router/index.js +3 -4
  67. package/package.json +1 -1
  68. package/readme.delete.md +187 -0
  69. package/readme.hints.md +210 -1
  70. package/readme.plan.md +621 -0
  71. package/readme.routing.md +341 -0
  72. package/ts/00_commitinfo_data.ts +1 -1
  73. package/ts/core/models/index.ts +2 -0
  74. package/ts/core/models/socket-types.ts +21 -0
  75. package/ts/core/models/wrapped-socket.ts +99 -0
  76. package/ts/core/routing/index.ts +21 -0
  77. package/ts/core/routing/matchers/domain.ts +119 -0
  78. package/ts/core/routing/matchers/header.ts +120 -0
  79. package/ts/core/routing/matchers/index.ts +22 -0
  80. package/ts/core/routing/matchers/ip.ts +207 -0
  81. package/ts/core/routing/matchers/path.ts +184 -0
  82. package/ts/core/{utils → routing}/route-manager.ts +7 -57
  83. package/ts/core/routing/route-utils.ts +88 -0
  84. package/ts/core/routing/specificity.ts +141 -0
  85. package/ts/core/routing/types.ts +49 -0
  86. package/ts/core/utils/index.ts +0 -2
  87. package/ts/core/utils/security-utils.ts +3 -7
  88. package/ts/core/utils/socket-utils.ts +0 -44
  89. package/ts/forwarding/handlers/https-terminate-to-http-handler.ts +47 -33
  90. package/ts/forwarding/handlers/https-terminate-to-https-handler.ts +55 -35
  91. package/ts/index.ts +4 -14
  92. package/ts/proxies/http-proxy/http-proxy.ts +13 -68
  93. package/ts/proxies/http-proxy/models/types.ts +0 -324
  94. package/ts/proxies/http-proxy/request-handler.ts +15 -186
  95. package/ts/proxies/http-proxy/websocket-handler.ts +15 -26
  96. package/ts/proxies/index.ts +3 -2
  97. package/ts/proxies/smart-proxy/connection-manager.ts +15 -7
  98. package/ts/proxies/smart-proxy/http-proxy-bridge.ts +6 -2
  99. package/ts/proxies/smart-proxy/index.ts +1 -1
  100. package/ts/proxies/smart-proxy/models/interfaces.ts +8 -2
  101. package/ts/proxies/smart-proxy/route-connection-handler.ts +58 -30
  102. package/ts/proxies/smart-proxy/smart-proxy.ts +15 -3
  103. package/ts/proxies/smart-proxy/utils/route-utils.ts +11 -49
  104. package/ts/routing/router/http-router.ts +266 -0
  105. package/ts/routing/router/index.ts +3 -8
  106. package/readme.problems.md +0 -170
  107. package/ts/core/utils/route-utils.ts +0 -312
  108. package/ts/proxies/smart-proxy/route-manager.ts +0 -554
  109. package/ts/routing/router/proxy-router.ts +0 -437
  110. package/ts/routing/router/route-router.ts +0 -482
@@ -1,170 +0,0 @@
1
- # SmartProxy Performance Issues Report
2
-
3
- ## Executive Summary
4
- This report identifies performance issues and blocking operations in the SmartProxy codebase that could impact scalability and responsiveness under high load.
5
-
6
- ## Critical Issues
7
-
8
- ### 1. **Synchronous Filesystem Operations**
9
- These operations block the event loop and should be replaced with async alternatives:
10
-
11
- #### Certificate Management
12
- - `ts/proxies/http-proxy/certificate-manager.ts:29`: `fs.existsSync()`
13
- - `ts/proxies/http-proxy/certificate-manager.ts:30`: `fs.mkdirSync()`
14
- - `ts/proxies/http-proxy/certificate-manager.ts:49-50`: `fs.readFileSync()` for loading certificates
15
-
16
- #### NFTables Proxy
17
- - `ts/proxies/nftables-proxy/nftables-proxy.ts`: Multiple uses of `execSync()` for system commands
18
- - `ts/proxies/nftables-proxy/nftables-proxy.ts`: Multiple `fs.writeFileSync()` and `fs.unlinkSync()` operations
19
-
20
- #### Certificate Store
21
- - `ts/proxies/smart-proxy/cert-store.ts:8`: `ensureDirSync()`
22
- - `ts/proxies/smart-proxy/cert-store.ts:15,31,76`: `fileExistsSync()`
23
- - `ts/proxies/smart-proxy/cert-store.ts:77`: `removeManySync()`
24
-
25
- ### 2. **Event Loop Blocking Operations**
26
-
27
- #### Busy Wait Loop
28
- - `ts/proxies/nftables-proxy/nftables-proxy.ts:235-238`:
29
- ```typescript
30
- const waitUntil = Date.now() + retryDelayMs;
31
- while (Date.now() < waitUntil) {
32
- // busy wait - blocks event loop completely
33
- }
34
- ```
35
- This is extremely problematic as it blocks the entire Node.js event loop.
36
-
37
- ### 3. **Potential Memory Leaks**
38
-
39
- #### Timer Management Issues
40
- Several timers are created without proper cleanup:
41
- - `ts/proxies/http-proxy/function-cache.ts`: `setInterval()` without storing reference for cleanup
42
- - `ts/proxies/http-proxy/request-handler.ts`: `setInterval()` for rate limit cleanup without cleanup
43
- - `ts/core/utils/shared-security-manager.ts`: `cleanupInterval` stored but no cleanup method
44
-
45
- #### Event Listener Accumulation
46
- - Multiple instances of event listeners being added without corresponding cleanup
47
- - Connection handlers add listeners without always removing them on connection close
48
-
49
- ### 4. **Connection Pool Management**
50
-
51
- #### ConnectionPool (ts/proxies/http-proxy/connection-pool.ts)
52
- **Good practices observed:**
53
- - Proper connection lifecycle management
54
- - Periodic cleanup of idle connections
55
- - Connection limits enforcement
56
-
57
- **Potential issues:**
58
- - No backpressure mechanism when pool is full
59
- - Synchronous sorting operation in `cleanupConnectionPool()` could be slow with many connections
60
-
61
- ### 5. **Resource Management Issues**
62
-
63
- #### Socket Cleanup
64
- - Some error paths don't properly clean up sockets
65
- - Missing `removeAllListeners()` in some error scenarios could lead to memory leaks
66
-
67
- #### Timeout Management
68
- - Inconsistent timeout handling across different components
69
- - Some sockets created without timeout settings
70
-
71
- ### 6. **JSON Operations on Large Objects**
72
- - `ts/proxies/smart-proxy/cert-store.ts:21`: `JSON.parse()` on certificate metadata
73
- - `ts/proxies/smart-proxy/cert-store.ts:71`: `JSON.stringify()` with pretty printing
74
- - `ts/proxies/http-proxy/function-cache.ts:76`: `JSON.stringify()` for cache keys (called frequently)
75
-
76
- ## Recommendations
77
-
78
- ### Immediate Actions (High Priority)
79
-
80
- 1. **Replace Synchronous Operations**
81
- ```typescript
82
- // Instead of:
83
- if (fs.existsSync(path)) { ... }
84
-
85
- // Use:
86
- try {
87
- await fs.promises.access(path);
88
- // file exists
89
- } catch {
90
- // file doesn't exist
91
- }
92
- ```
93
-
94
- 2. **Fix Busy Wait Loop**
95
- ```typescript
96
- // Instead of:
97
- while (Date.now() < waitUntil) { }
98
-
99
- // Use:
100
- await new Promise(resolve => setTimeout(resolve, retryDelayMs));
101
- ```
102
-
103
- 3. **Add Timer Cleanup**
104
- ```typescript
105
- class Component {
106
- private cleanupTimer?: NodeJS.Timeout;
107
-
108
- start() {
109
- this.cleanupTimer = setInterval(() => { ... }, 60000);
110
- }
111
-
112
- stop() {
113
- if (this.cleanupTimer) {
114
- clearInterval(this.cleanupTimer);
115
- this.cleanupTimer = undefined;
116
- }
117
- }
118
- }
119
- ```
120
-
121
- ### Medium Priority
122
-
123
- 1. **Optimize JSON Operations**
124
- - Cache JSON.stringify results for frequently used objects
125
- - Consider using faster hashing for cache keys (e.g., crypto.createHash)
126
- - Use streaming JSON parsers for large objects
127
-
128
- 2. **Improve Connection Pool**
129
- - Implement backpressure/queueing when pool is full
130
- - Use a heap or priority queue for connection management instead of sorting
131
-
132
- 3. **Standardize Resource Cleanup**
133
- - Create a base class for components with lifecycle management
134
- - Ensure all event listeners are removed on cleanup
135
- - Add abort controllers for better cancellation support
136
-
137
- ### Long-term Improvements
138
-
139
- 1. **Worker Threads**
140
- - Move CPU-intensive operations to worker threads
141
- - Consider using worker pools for NFTables operations
142
-
143
- 2. **Monitoring and Metrics**
144
- - Add performance monitoring for event loop lag
145
- - Track connection pool utilization
146
- - Monitor memory usage patterns
147
-
148
- 3. **Graceful Degradation**
149
- - Implement circuit breakers for backend connections
150
- - Add request queuing with overflow protection
151
- - Implement adaptive timeout strategies
152
-
153
- ## Impact Assessment
154
-
155
- These issues primarily affect:
156
- - **Scalability**: Blocking operations limit concurrent connection handling
157
- - **Responsiveness**: Event loop blocking causes latency spikes
158
- - **Stability**: Memory leaks could cause crashes under sustained load
159
- - **Resource Usage**: Inefficient resource management increases memory/CPU usage
160
-
161
- ## Testing Recommendations
162
-
163
- 1. Load test with high connection counts (10k+ concurrent)
164
- 2. Monitor event loop lag under stress
165
- 3. Test long-running scenarios to detect memory leaks
166
- 4. Benchmark with async vs sync operations to measure improvement
167
-
168
- ## Conclusion
169
-
170
- While SmartProxy has good architectural design and many best practices, the identified blocking operations and resource management issues could significantly impact performance under high load. The most critical issues (busy wait loop and synchronous filesystem operations) should be addressed immediately.
@@ -1,312 +0,0 @@
1
- /**
2
- * Route matching utilities for SmartProxy components
3
- *
4
- * Contains shared logic for domain matching, path matching, and IP matching
5
- * to be used by different proxy components throughout the system.
6
- */
7
-
8
- /**
9
- * Match a domain pattern against a domain
10
- *
11
- * @param pattern Domain pattern with optional wildcards (e.g., "*.example.com")
12
- * @param domain Domain to match against the pattern
13
- * @returns Whether the domain matches the pattern
14
- */
15
- export function matchDomain(pattern: string, domain: string): boolean {
16
- // Handle exact match (case-insensitive)
17
- if (pattern.toLowerCase() === domain.toLowerCase()) {
18
- return true;
19
- }
20
-
21
- // Handle wildcard pattern
22
- if (pattern.includes('*')) {
23
- const regexPattern = pattern
24
- .replace(/\./g, '\\.') // Escape dots
25
- .replace(/\*/g, '.*'); // Convert * to .*
26
-
27
- const regex = new RegExp(`^${regexPattern}$`, 'i');
28
- return regex.test(domain);
29
- }
30
-
31
- return false;
32
- }
33
-
34
- /**
35
- * Match domains from a route against a given domain
36
- *
37
- * @param domains Array or single domain pattern to match against
38
- * @param domain Domain to match
39
- * @returns Whether the domain matches any of the patterns
40
- */
41
- export function matchRouteDomain(domains: string | string[] | undefined, domain: string | undefined): boolean {
42
- // If no domains specified in the route, match all domains
43
- if (!domains) {
44
- return true;
45
- }
46
-
47
- // If no domain in the request, can't match domain-specific routes
48
- if (!domain) {
49
- return false;
50
- }
51
-
52
- const patterns = Array.isArray(domains) ? domains : [domains];
53
- return patterns.some(pattern => matchDomain(pattern, domain));
54
- }
55
-
56
- /**
57
- * Match a path pattern against a path
58
- *
59
- * @param pattern Path pattern with optional wildcards
60
- * @param path Path to match against the pattern
61
- * @returns Whether the path matches the pattern
62
- */
63
- export function matchPath(pattern: string, path: string): boolean {
64
- // Handle exact match
65
- if (pattern === path) {
66
- return true;
67
- }
68
-
69
- // Handle simple wildcard at the end (like /api/*)
70
- if (pattern.endsWith('*')) {
71
- const prefix = pattern.slice(0, -1);
72
- return path.startsWith(prefix);
73
- }
74
-
75
- // Handle more complex wildcard patterns
76
- if (pattern.includes('*')) {
77
- const regexPattern = pattern
78
- .replace(/\./g, '\\.') // Escape dots
79
- .replace(/\*/g, '.*') // Convert * to .*
80
- .replace(/\//g, '\\/'); // Escape slashes
81
-
82
- const regex = new RegExp(`^${regexPattern}$`);
83
- return regex.test(path);
84
- }
85
-
86
- return false;
87
- }
88
-
89
- /**
90
- * Parse CIDR notation into subnet and mask bits
91
- *
92
- * @param cidr CIDR string (e.g., "192.168.1.0/24")
93
- * @returns Object with subnet and bits, or null if invalid
94
- */
95
- export function parseCidr(cidr: string): { subnet: string; bits: number } | null {
96
- try {
97
- const [subnet, bitsStr] = cidr.split('/');
98
- const bits = parseInt(bitsStr, 10);
99
-
100
- if (isNaN(bits) || bits < 0 || bits > 32) {
101
- return null;
102
- }
103
-
104
- return { subnet, bits };
105
- } catch (e) {
106
- return null;
107
- }
108
- }
109
-
110
- /**
111
- * Convert an IP address to a numeric value
112
- *
113
- * @param ip IPv4 address string (e.g., "192.168.1.1")
114
- * @returns Numeric representation of the IP
115
- */
116
- export function ipToNumber(ip: string): number {
117
- // Handle IPv6-mapped IPv4 addresses (::ffff:192.168.1.1)
118
- if (ip.startsWith('::ffff:')) {
119
- ip = ip.slice(7);
120
- }
121
-
122
- const parts = ip.split('.').map(part => parseInt(part, 10));
123
- return (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8) | parts[3];
124
- }
125
-
126
- /**
127
- * Match an IP against a CIDR pattern
128
- *
129
- * @param cidr CIDR pattern (e.g., "192.168.1.0/24")
130
- * @param ip IP to match against the pattern
131
- * @returns Whether the IP is in the CIDR range
132
- */
133
- export function matchIpCidr(cidr: string, ip: string): boolean {
134
- const parsed = parseCidr(cidr);
135
- if (!parsed) {
136
- return false;
137
- }
138
-
139
- try {
140
- const { subnet, bits } = parsed;
141
-
142
- // Normalize IPv6-mapped IPv4 addresses
143
- const normalizedIp = ip.startsWith('::ffff:') ? ip.substring(7) : ip;
144
- const normalizedSubnet = subnet.startsWith('::ffff:') ? subnet.substring(7) : subnet;
145
-
146
- // Convert IP addresses to numeric values
147
- const ipNum = ipToNumber(normalizedIp);
148
- const subnetNum = ipToNumber(normalizedSubnet);
149
-
150
- // Calculate subnet mask
151
- const maskNum = ~(2 ** (32 - bits) - 1);
152
-
153
- // Check if IP is in subnet
154
- return (ipNum & maskNum) === (subnetNum & maskNum);
155
- } catch (e) {
156
- return false;
157
- }
158
- }
159
-
160
- /**
161
- * Match an IP pattern against an IP
162
- *
163
- * @param pattern IP pattern (exact, CIDR, or with wildcards)
164
- * @param ip IP to match against the pattern
165
- * @returns Whether the IP matches the pattern
166
- */
167
- export function matchIpPattern(pattern: string, ip: string): boolean {
168
- // Normalize IPv6-mapped IPv4 addresses
169
- const normalizedIp = ip.startsWith('::ffff:') ? ip.substring(7) : ip;
170
- const normalizedPattern = pattern.startsWith('::ffff:') ? pattern.substring(7) : pattern;
171
-
172
- // Handle exact match with all variations
173
- if (pattern === ip || normalizedPattern === normalizedIp ||
174
- pattern === normalizedIp || normalizedPattern === ip) {
175
- return true;
176
- }
177
-
178
- // Handle "all" wildcard
179
- if (pattern === '*' || normalizedPattern === '*') {
180
- return true;
181
- }
182
-
183
- // Handle CIDR notation (e.g., 192.168.1.0/24)
184
- if (pattern.includes('/')) {
185
- return matchIpCidr(pattern, normalizedIp) ||
186
- (normalizedPattern !== pattern && matchIpCidr(normalizedPattern, normalizedIp));
187
- }
188
-
189
- // Handle glob pattern (e.g., 192.168.1.*)
190
- if (pattern.includes('*')) {
191
- const regexPattern = pattern.replace(/\./g, '\\.').replace(/\*/g, '.*');
192
- const regex = new RegExp(`^${regexPattern}$`);
193
- if (regex.test(ip) || regex.test(normalizedIp)) {
194
- return true;
195
- }
196
-
197
- // If pattern was normalized, also test with normalized pattern
198
- if (normalizedPattern !== pattern) {
199
- const normalizedRegexPattern = normalizedPattern.replace(/\./g, '\\.').replace(/\*/g, '.*');
200
- const normalizedRegex = new RegExp(`^${normalizedRegexPattern}$`);
201
- return normalizedRegex.test(ip) || normalizedRegex.test(normalizedIp);
202
- }
203
- }
204
-
205
- return false;
206
- }
207
-
208
- /**
209
- * Match an IP against allowed and blocked IP patterns
210
- *
211
- * @param ip IP to check
212
- * @param ipAllowList Array of allowed IP patterns
213
- * @param ipBlockList Array of blocked IP patterns
214
- * @returns Whether the IP is allowed
215
- */
216
- export function isIpAuthorized(
217
- ip: string,
218
- ipAllowList: string[] = ['*'],
219
- ipBlockList: string[] = []
220
- ): boolean {
221
- // Check blocked IPs first
222
- if (ipBlockList.length > 0) {
223
- for (const pattern of ipBlockList) {
224
- if (matchIpPattern(pattern, ip)) {
225
- return false; // IP is blocked
226
- }
227
- }
228
- }
229
-
230
- // If there are allowed IPs, check them
231
- if (ipAllowList.length > 0) {
232
- // Special case: if '*' is in allowed IPs, all non-blocked IPs are allowed
233
- if (ipAllowList.includes('*')) {
234
- return true;
235
- }
236
-
237
- for (const pattern of ipAllowList) {
238
- if (matchIpPattern(pattern, ip)) {
239
- return true; // IP is allowed
240
- }
241
- }
242
- return false; // IP not in allowed list
243
- }
244
-
245
- // No allowed IPs specified, so IP is allowed by default
246
- return true;
247
- }
248
-
249
- /**
250
- * Match an HTTP header pattern against a header value
251
- *
252
- * @param pattern Expected header value (string or RegExp)
253
- * @param value Actual header value
254
- * @returns Whether the header matches the pattern
255
- */
256
- export function matchHeader(pattern: string | RegExp, value: string): boolean {
257
- if (typeof pattern === 'string') {
258
- return pattern === value;
259
- } else if (pattern instanceof RegExp) {
260
- return pattern.test(value);
261
- }
262
- return false;
263
- }
264
-
265
- /**
266
- * Calculate route specificity score
267
- * Higher score means more specific matching criteria
268
- *
269
- * @param match Match criteria to evaluate
270
- * @returns Numeric specificity score
271
- */
272
- export function calculateRouteSpecificity(match: {
273
- domains?: string | string[];
274
- path?: string;
275
- clientIp?: string[];
276
- tlsVersion?: string[];
277
- headers?: Record<string, string | RegExp>;
278
- }): number {
279
- let score = 0;
280
-
281
- // Path is very specific
282
- if (match.path) {
283
- // More specific if it doesn't use wildcards
284
- score += match.path.includes('*') ? 3 : 4;
285
- }
286
-
287
- // Domain is next most specific
288
- if (match.domains) {
289
- const domains = Array.isArray(match.domains) ? match.domains : [match.domains];
290
- // More domains or more specific domains (without wildcards) increase specificity
291
- score += domains.length;
292
- // Add bonus for exact domains (without wildcards)
293
- score += domains.some(d => !d.includes('*')) ? 1 : 0;
294
- }
295
-
296
- // Headers are quite specific
297
- if (match.headers) {
298
- score += Object.keys(match.headers).length * 2;
299
- }
300
-
301
- // Client IP adds some specificity
302
- if (match.clientIp && match.clientIp.length > 0) {
303
- score += 1;
304
- }
305
-
306
- // TLS version adds minimal specificity
307
- if (match.tlsVersion && match.tlsVersion.length > 0) {
308
- score += 1;
309
- }
310
-
311
- return score;
312
- }