@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,437 +0,0 @@
1
- import * as plugins from '../../plugins.js';
2
- import type { IReverseProxyConfig } from '../../proxies/http-proxy/models/types.js';
3
-
4
- /**
5
- * Optional path pattern configuration that can be added to proxy configs
6
- */
7
- export interface PathPatternConfig {
8
- pathPattern?: string;
9
- }
10
-
11
- // Backward compatibility
12
- export type IPathPatternConfig = PathPatternConfig;
13
-
14
- /**
15
- * Interface for router result with additional metadata
16
- */
17
- export interface RouterResult {
18
- config: IReverseProxyConfig;
19
- pathMatch?: string;
20
- pathParams?: Record<string, string>;
21
- pathRemainder?: string;
22
- }
23
-
24
- // Backward compatibility
25
- export type IRouterResult = RouterResult;
26
-
27
- /**
28
- * Router for HTTP reverse proxy requests
29
- *
30
- * Supports the following domain matching patterns:
31
- * - Exact matches: "example.com"
32
- * - Wildcard subdomains: "*.example.com" (matches any subdomain of example.com)
33
- * - TLD wildcards: "example.*" (matches example.com, example.org, etc.)
34
- * - Complex wildcards: "*.lossless*" (matches any subdomain of any lossless domain)
35
- * - Default fallback: "*" (matches any unmatched domain)
36
- *
37
- * Also supports path pattern matching for each domain:
38
- * - Exact path: "/api/users"
39
- * - Wildcard paths: "/api/*"
40
- * - Path parameters: "/users/:id/profile"
41
- */
42
- export class ProxyRouter {
43
- // Store original configs for reference
44
- private reverseProxyConfigs: IReverseProxyConfig[] = [];
45
- // Default config to use when no match is found (optional)
46
- private defaultConfig?: IReverseProxyConfig;
47
- // Store path patterns separately since they're not in the original interface
48
- private pathPatterns: Map<IReverseProxyConfig, string> = new Map();
49
- // Logger interface
50
- private logger: {
51
- error: (message: string, data?: any) => void;
52
- warn: (message: string, data?: any) => void;
53
- info: (message: string, data?: any) => void;
54
- debug: (message: string, data?: any) => void;
55
- };
56
-
57
- constructor(
58
- configs?: IReverseProxyConfig[],
59
- logger?: {
60
- error: (message: string, data?: any) => void;
61
- warn: (message: string, data?: any) => void;
62
- info: (message: string, data?: any) => void;
63
- debug: (message: string, data?: any) => void;
64
- }
65
- ) {
66
- this.logger = logger || console;
67
- if (configs) {
68
- this.setNewProxyConfigs(configs);
69
- }
70
- }
71
-
72
- /**
73
- * Sets a new set of reverse configs to be routed to
74
- * @param reverseCandidatesArg Array of reverse proxy configurations
75
- */
76
- public setNewProxyConfigs(reverseCandidatesArg: IReverseProxyConfig[]): void {
77
- this.reverseProxyConfigs = [...reverseCandidatesArg];
78
-
79
- // Find default config if any (config with "*" as hostname)
80
- this.defaultConfig = this.reverseProxyConfigs.find(config => config.hostName === '*');
81
-
82
- this.logger.info(`Router initialized with ${this.reverseProxyConfigs.length} configs (${this.getHostnames().length} unique hosts)`);
83
- }
84
-
85
- /**
86
- * Routes a request based on hostname and path
87
- * @param req The incoming HTTP request
88
- * @returns The matching proxy config or undefined if no match found
89
- */
90
- public routeReq(req: plugins.http.IncomingMessage): IReverseProxyConfig {
91
- const result = this.routeReqWithDetails(req);
92
- return result ? result.config : undefined;
93
- }
94
-
95
- /**
96
- * Routes a request with detailed matching information
97
- * @param req The incoming HTTP request
98
- * @returns Detailed routing result including matched config and path information
99
- */
100
- public routeReqWithDetails(req: plugins.http.IncomingMessage): RouterResult | undefined {
101
- // Extract and validate host header
102
- const originalHost = req.headers.host;
103
- if (!originalHost) {
104
- this.logger.error('No host header found in request');
105
- return this.defaultConfig ? { config: this.defaultConfig } : undefined;
106
- }
107
-
108
- // Parse URL for path matching
109
- const parsedUrl = plugins.url.parse(req.url || '/');
110
- const urlPath = parsedUrl.pathname || '/';
111
-
112
- // Extract hostname without port
113
- const hostWithoutPort = originalHost.split(':')[0].toLowerCase();
114
-
115
- // First try exact hostname match
116
- const exactConfig = this.findConfigForHost(hostWithoutPort, urlPath);
117
- if (exactConfig) {
118
- return exactConfig;
119
- }
120
-
121
- // Try various wildcard patterns
122
- if (hostWithoutPort.includes('.')) {
123
- const domainParts = hostWithoutPort.split('.');
124
-
125
- // Try wildcard subdomain (*.example.com)
126
- if (domainParts.length > 2) {
127
- const wildcardDomain = `*.${domainParts.slice(1).join('.')}`;
128
- const wildcardConfig = this.findConfigForHost(wildcardDomain, urlPath);
129
- if (wildcardConfig) {
130
- return wildcardConfig;
131
- }
132
- }
133
-
134
- // Try TLD wildcard (example.*)
135
- const baseDomain = domainParts.slice(0, -1).join('.');
136
- const tldWildcardDomain = `${baseDomain}.*`;
137
- const tldWildcardConfig = this.findConfigForHost(tldWildcardDomain, urlPath);
138
- if (tldWildcardConfig) {
139
- return tldWildcardConfig;
140
- }
141
-
142
- // Try complex wildcard patterns
143
- const wildcardPatterns = this.findWildcardMatches(hostWithoutPort);
144
- for (const pattern of wildcardPatterns) {
145
- const wildcardConfig = this.findConfigForHost(pattern, urlPath);
146
- if (wildcardConfig) {
147
- return wildcardConfig;
148
- }
149
- }
150
- }
151
-
152
- // Fall back to default config if available
153
- if (this.defaultConfig) {
154
- this.logger.warn(`No specific config found for host: ${hostWithoutPort}, using default`);
155
- return { config: this.defaultConfig };
156
- }
157
-
158
- this.logger.error(`No config found for host: ${hostWithoutPort}`);
159
- return undefined;
160
- }
161
-
162
- /**
163
- * Find potential wildcard patterns that could match a given hostname
164
- * Handles complex patterns like "*.lossless*" or other partial matches
165
- * @param hostname The hostname to find wildcard matches for
166
- * @returns Array of potential wildcard patterns that could match
167
- */
168
- private findWildcardMatches(hostname: string): string[] {
169
- const patterns: string[] = [];
170
- const hostnameParts = hostname.split('.');
171
-
172
- // Find all configured hostnames that contain wildcards
173
- const wildcardConfigs = this.reverseProxyConfigs.filter(
174
- config => config.hostName.includes('*')
175
- );
176
-
177
- // Extract unique wildcard patterns
178
- const wildcardPatterns = [...new Set(
179
- wildcardConfigs.map(config => config.hostName.toLowerCase())
180
- )];
181
-
182
- // For each wildcard pattern, check if it could match the hostname
183
- // using simplified regex pattern matching
184
- for (const pattern of wildcardPatterns) {
185
- // Skip the default wildcard '*'
186
- if (pattern === '*') continue;
187
-
188
- // Skip already checked patterns (*.domain.com and domain.*)
189
- if (pattern.startsWith('*.') && pattern.indexOf('*', 2) === -1) continue;
190
- if (pattern.endsWith('.*') && pattern.indexOf('*') === pattern.length - 1) continue;
191
-
192
- // Convert wildcard pattern to regex
193
- const regexPattern = pattern
194
- .replace(/\./g, '\\.') // Escape dots
195
- .replace(/\*/g, '.*'); // Convert * to .* for regex
196
-
197
- // Create regex object with case insensitive flag
198
- const regex = new RegExp(`^${regexPattern}$`, 'i');
199
-
200
- // If hostname matches this complex pattern, add it to the list
201
- if (regex.test(hostname)) {
202
- patterns.push(pattern);
203
- }
204
- }
205
-
206
- return patterns;
207
- }
208
-
209
- /**
210
- * Find a config for a specific host and path
211
- */
212
- private findConfigForHost(hostname: string, path: string): RouterResult | undefined {
213
- // Find all configs for this hostname
214
- const configs = this.reverseProxyConfigs.filter(
215
- config => config.hostName.toLowerCase() === hostname.toLowerCase()
216
- );
217
-
218
- if (configs.length === 0) {
219
- return undefined;
220
- }
221
-
222
- // First try configs with path patterns
223
- const configsWithPaths = configs.filter(config => this.pathPatterns.has(config));
224
-
225
- // Sort by path pattern specificity - more specific first
226
- configsWithPaths.sort((a, b) => {
227
- const aPattern = this.pathPatterns.get(a) || '';
228
- const bPattern = this.pathPatterns.get(b) || '';
229
-
230
- // Exact patterns come before wildcard patterns
231
- const aHasWildcard = aPattern.includes('*');
232
- const bHasWildcard = bPattern.includes('*');
233
-
234
- if (aHasWildcard && !bHasWildcard) return 1;
235
- if (!aHasWildcard && bHasWildcard) return -1;
236
-
237
- // Longer patterns are considered more specific
238
- return bPattern.length - aPattern.length;
239
- });
240
-
241
- // Check each config with path pattern
242
- for (const config of configsWithPaths) {
243
- const pathPattern = this.pathPatterns.get(config);
244
- if (pathPattern) {
245
- const pathMatch = this.matchPath(path, pathPattern);
246
- if (pathMatch) {
247
- return {
248
- config,
249
- pathMatch: pathMatch.matched,
250
- pathParams: pathMatch.params,
251
- pathRemainder: pathMatch.remainder
252
- };
253
- }
254
- }
255
- }
256
-
257
- // If no path pattern matched, use the first config without a path pattern
258
- const configWithoutPath = configs.find(config => !this.pathPatterns.has(config));
259
- if (configWithoutPath) {
260
- return { config: configWithoutPath };
261
- }
262
-
263
- return undefined;
264
- }
265
-
266
- /**
267
- * Matches a URL path against a pattern
268
- * Supports:
269
- * - Exact matches: /users/profile
270
- * - Wildcards: /api/* (matches any path starting with /api/)
271
- * - Path parameters: /users/:id (captures id as a parameter)
272
- *
273
- * @param path The URL path to match
274
- * @param pattern The pattern to match against
275
- * @returns Match result with params and remainder, or null if no match
276
- */
277
- private matchPath(path: string, pattern: string): {
278
- matched: string;
279
- params: Record<string, string>;
280
- remainder: string;
281
- } | null {
282
- // Handle exact match
283
- if (path === pattern) {
284
- return {
285
- matched: pattern,
286
- params: {},
287
- remainder: ''
288
- };
289
- }
290
-
291
- // Handle wildcard match
292
- if (pattern.endsWith('/*')) {
293
- const prefix = pattern.slice(0, -2);
294
- if (path === prefix || path.startsWith(`${prefix}/`)) {
295
- return {
296
- matched: prefix,
297
- params: {},
298
- remainder: path.slice(prefix.length)
299
- };
300
- }
301
- return null;
302
- }
303
-
304
- // Handle path parameters
305
- const patternParts = pattern.split('/').filter(p => p);
306
- const pathParts = path.split('/').filter(p => p);
307
-
308
- // Too few path parts to match
309
- if (pathParts.length < patternParts.length) {
310
- return null;
311
- }
312
-
313
- const params: Record<string, string> = {};
314
-
315
- // Compare each part
316
- for (let i = 0; i < patternParts.length; i++) {
317
- const patternPart = patternParts[i];
318
- const pathPart = pathParts[i];
319
-
320
- // Handle parameter
321
- if (patternPart.startsWith(':')) {
322
- const paramName = patternPart.slice(1);
323
- params[paramName] = pathPart;
324
- continue;
325
- }
326
-
327
- // Handle wildcard at the end
328
- if (patternPart === '*' && i === patternParts.length - 1) {
329
- break;
330
- }
331
-
332
- // Handle exact match for this part
333
- if (patternPart !== pathPart) {
334
- return null;
335
- }
336
- }
337
-
338
- // Calculate the remainder - the unmatched path parts
339
- const remainderParts = pathParts.slice(patternParts.length);
340
- const remainder = remainderParts.length ? '/' + remainderParts.join('/') : '';
341
-
342
- // Calculate the matched path
343
- const matchedParts = patternParts.map((part, i) => {
344
- return part.startsWith(':') ? pathParts[i] : part;
345
- });
346
- const matched = '/' + matchedParts.join('/');
347
-
348
- return {
349
- matched,
350
- params,
351
- remainder
352
- };
353
- }
354
-
355
- /**
356
- * Gets all currently active proxy configurations
357
- * @returns Array of all active configurations
358
- */
359
- public getProxyConfigs(): IReverseProxyConfig[] {
360
- return [...this.reverseProxyConfigs];
361
- }
362
-
363
- /**
364
- * Gets all hostnames that this router is configured to handle
365
- * @returns Array of hostnames
366
- */
367
- public getHostnames(): string[] {
368
- const hostnames = new Set<string>();
369
- for (const config of this.reverseProxyConfigs) {
370
- if (config.hostName !== '*') {
371
- hostnames.add(config.hostName.toLowerCase());
372
- }
373
- }
374
- return Array.from(hostnames);
375
- }
376
-
377
- /**
378
- * Adds a single new proxy configuration
379
- * @param config The configuration to add
380
- * @param pathPattern Optional path pattern for route matching
381
- */
382
- public addProxyConfig(
383
- config: IReverseProxyConfig,
384
- pathPattern?: string
385
- ): void {
386
- this.reverseProxyConfigs.push(config);
387
-
388
- // Store path pattern if provided
389
- if (pathPattern) {
390
- this.pathPatterns.set(config, pathPattern);
391
- }
392
- }
393
-
394
- /**
395
- * Sets a path pattern for an existing config
396
- * @param config The existing configuration
397
- * @param pathPattern The path pattern to set
398
- * @returns Boolean indicating if the config was found and updated
399
- */
400
- public setPathPattern(
401
- config: IReverseProxyConfig,
402
- pathPattern: string
403
- ): boolean {
404
- const exists = this.reverseProxyConfigs.includes(config);
405
- if (exists) {
406
- this.pathPatterns.set(config, pathPattern);
407
- return true;
408
- }
409
- return false;
410
- }
411
-
412
- /**
413
- * Removes a proxy configuration by hostname
414
- * @param hostname The hostname to remove
415
- * @returns Boolean indicating whether any configs were removed
416
- */
417
- public removeProxyConfig(hostname: string): boolean {
418
- const initialCount = this.reverseProxyConfigs.length;
419
-
420
- // Find configs to remove
421
- const configsToRemove = this.reverseProxyConfigs.filter(
422
- config => config.hostName === hostname
423
- );
424
-
425
- // Remove them from the patterns map
426
- for (const config of configsToRemove) {
427
- this.pathPatterns.delete(config);
428
- }
429
-
430
- // Filter them out of the configs array
431
- this.reverseProxyConfigs = this.reverseProxyConfigs.filter(
432
- config => config.hostName !== hostname
433
- );
434
-
435
- return this.reverseProxyConfigs.length !== initialCount;
436
- }
437
- }