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