@push.rocks/smartproxy 28.0.0 → 28.1.0
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.
- package/changelog.md +30 -0
- package/dist_rust/rustproxy_linux_amd64 +0 -0
- package/dist_rust/rustproxy_linux_amd64.tsrust-build.json +4 -4
- package/dist_rust/rustproxy_linux_arm64 +0 -0
- package/dist_rust/rustproxy_linux_arm64.tsrust-build.json +4 -4
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +4 -2
- package/dist_ts/proxies/smart-proxy/models/rust-types.d.ts +17 -0
- package/dist_ts/proxies/smart-proxy/models/rust-types.js +3 -2
- package/dist_ts/proxies/smart-proxy/rust-proxy-bridge.d.ts +28 -4
- package/dist_ts/proxies/smart-proxy/rust-proxy-bridge.js +75 -6
- package/dist_ts/proxies/smart-proxy/smart-proxy.js +45 -40
- package/dist_ts/proxies/smart-proxy/utils/index.d.ts +2 -0
- package/dist_ts/proxies/smart-proxy/utils/index.js +3 -1
- package/dist_ts/proxies/smart-proxy/utils/route-validation-error.d.ts +24 -0
- package/dist_ts/proxies/smart-proxy/utils/route-validation-error.js +41 -0
- package/dist_ts/proxies/smart-proxy/utils/route-validator.d.ts +12 -2
- package/dist_ts/proxies/smart-proxy/utils/route-validator.js +56 -41
- package/package.json +2 -2
- package/readme.md +32 -3
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/proxies/smart-proxy/models/interfaces.ts +4 -2
- package/ts/proxies/smart-proxy/models/rust-types.ts +20 -0
- package/ts/proxies/smart-proxy/rust-proxy-bridge.ts +106 -13
- package/ts/proxies/smart-proxy/smart-proxy.ts +40 -39
- package/ts/proxies/smart-proxy/utils/index.ts +4 -0
- package/ts/proxies/smart-proxy/utils/route-validation-error.ts +60 -0
- package/ts/proxies/smart-proxy/utils/route-validator.ts +74 -49
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { logger } from '../../../core/utils/logger.js';
|
|
2
2
|
import type { IInboundProxyProtocolPolicy, IRouteConfig, IRouteMatch, IRouteAction, TPortRange, TTransportProtocol } from '../models/route-types.js';
|
|
3
|
+
import { RouteValidationError, type IRouteValidationErrorEntry } from './route-validation-error.js';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Validates route configurations for correctness and safety
|
|
@@ -323,62 +324,84 @@ export class RouteValidator {
|
|
|
323
324
|
}
|
|
324
325
|
|
|
325
326
|
/**
|
|
326
|
-
*
|
|
327
|
+
* Label every route for validation reports: its name, else its id, else `route[<index>]`.
|
|
328
|
+
* A name or id that several routes share gets `[<index>]` appended, so every label is unique.
|
|
327
329
|
*/
|
|
328
|
-
public static
|
|
329
|
-
const
|
|
330
|
-
|
|
330
|
+
public static getRouteLabels(routes: IRouteConfig[]): string[] {
|
|
331
|
+
const baseLabels = routes.map((route, index) => route.name || route.id || `route[${index}]`);
|
|
332
|
+
const labelCounts = new Map<string, number>();
|
|
333
|
+
for (const label of baseLabels) {
|
|
334
|
+
labelCounts.set(label, (labelCounts.get(label) ?? 0) + 1);
|
|
335
|
+
}
|
|
336
|
+
return baseLabels.map((label, index) => (labelCounts.get(label)! > 1 ? `${label}[${index}]` : label));
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Validate multiple route configurations.
|
|
341
|
+
*
|
|
342
|
+
* `entries` lists every invalid route by position and label; `errors` maps the same
|
|
343
|
+
* labels to the reasons.
|
|
344
|
+
*/
|
|
345
|
+
public static validateRoutes(routes: IRouteConfig[]): {
|
|
346
|
+
valid: boolean;
|
|
347
|
+
errors: Map<string, string[]>;
|
|
348
|
+
entries: IRouteValidationErrorEntry[];
|
|
349
|
+
} {
|
|
350
|
+
const messagesByIndex: string[][] = routes.map(() => []);
|
|
331
351
|
|
|
332
352
|
// Check for duplicate route names
|
|
333
353
|
const routeNames = new Set<string>();
|
|
334
|
-
|
|
354
|
+
routes.forEach((route, index) => {
|
|
335
355
|
if (route.name && routeNames.has(route.name)) {
|
|
336
|
-
|
|
337
|
-
existingErrors.push('Duplicate route name');
|
|
338
|
-
errorMap.set(route.name, existingErrors);
|
|
339
|
-
valid = false;
|
|
356
|
+
messagesByIndex[index].push('Duplicate route name');
|
|
340
357
|
}
|
|
341
358
|
if (route.name) {
|
|
342
359
|
routeNames.add(route.name);
|
|
343
360
|
}
|
|
344
|
-
}
|
|
361
|
+
});
|
|
345
362
|
|
|
346
363
|
// Validate each route
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
}
|
|
353
|
-
}
|
|
364
|
+
routes.forEach((route, index) => {
|
|
365
|
+
messagesByIndex[index].push(...this.validateRoute(route).errors);
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
const labels = this.getRouteLabels(routes);
|
|
354
369
|
|
|
355
370
|
// Check for conflicting routes
|
|
356
|
-
const
|
|
357
|
-
|
|
358
|
-
for (const conflict of conflicts) {
|
|
359
|
-
const existingErrors = errorMap.get(conflict.route) || [];
|
|
360
|
-
existingErrors.push(conflict.message);
|
|
361
|
-
errorMap.set(conflict.route, existingErrors);
|
|
362
|
-
}
|
|
363
|
-
valid = false;
|
|
371
|
+
for (const conflict of this.findRouteConflicts(routes, labels)) {
|
|
372
|
+
messagesByIndex[conflict.index].push(conflict.message);
|
|
364
373
|
}
|
|
365
374
|
|
|
366
|
-
|
|
375
|
+
const entries: IRouteValidationErrorEntry[] = [];
|
|
376
|
+
messagesByIndex.forEach((messages, index) => {
|
|
377
|
+
if (messages.length > 0) {
|
|
378
|
+
entries.push({ index, route: labels[index], messages });
|
|
379
|
+
}
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
return {
|
|
383
|
+
valid: entries.length === 0,
|
|
384
|
+
errors: new Map(entries.map((entry) => [entry.route, entry.messages])),
|
|
385
|
+
entries,
|
|
386
|
+
};
|
|
367
387
|
}
|
|
368
388
|
|
|
369
389
|
/**
|
|
370
390
|
* Find potential conflicts between routes
|
|
371
391
|
*/
|
|
372
|
-
private static findRouteConflicts(
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
const
|
|
377
|
-
|
|
378
|
-
|
|
392
|
+
private static findRouteConflicts(
|
|
393
|
+
routes: IRouteConfig[],
|
|
394
|
+
labels: string[],
|
|
395
|
+
): Array<{ index: number; message: string }> {
|
|
396
|
+
const conflicts: Array<{ index: number; message: string }> = [];
|
|
397
|
+
|
|
398
|
+
// Group route indices by port
|
|
399
|
+
const portMap = new Map<number, number[]>();
|
|
400
|
+
|
|
401
|
+
routes.forEach((route, index) => {
|
|
379
402
|
if (route.match?.ports) {
|
|
380
403
|
const ports = Array.isArray(route.match.ports) ? route.match.ports : [route.match.ports];
|
|
381
|
-
|
|
404
|
+
|
|
382
405
|
// Expand port ranges to individual ports
|
|
383
406
|
const expandedPorts: number[] = [];
|
|
384
407
|
for (const port of ports) {
|
|
@@ -390,25 +413,25 @@ export class RouteValidator {
|
|
|
390
413
|
}
|
|
391
414
|
}
|
|
392
415
|
}
|
|
393
|
-
|
|
416
|
+
|
|
394
417
|
for (const port of expandedPorts) {
|
|
395
418
|
const routesOnPort = portMap.get(port) || [];
|
|
396
|
-
routesOnPort.push(
|
|
419
|
+
routesOnPort.push(index);
|
|
397
420
|
portMap.set(port, routesOnPort);
|
|
398
421
|
}
|
|
399
422
|
}
|
|
400
|
-
}
|
|
423
|
+
});
|
|
401
424
|
|
|
402
425
|
// Check for conflicting catch-all routes on the same port
|
|
403
426
|
for (const [port, routesOnPort] of portMap) {
|
|
404
|
-
const catchAllRoutes = routesOnPort.filter(
|
|
405
|
-
this.isCatchAllDomainMatch(
|
|
427
|
+
const catchAllRoutes = routesOnPort.filter((index) =>
|
|
428
|
+
this.isCatchAllDomainMatch(routes[index].match) && !this.hasSourceScope(routes[index].match)
|
|
406
429
|
);
|
|
407
|
-
|
|
430
|
+
|
|
408
431
|
if (catchAllRoutes.length > 1) {
|
|
409
|
-
for (const
|
|
432
|
+
for (const index of catchAllRoutes) {
|
|
410
433
|
conflicts.push({
|
|
411
|
-
|
|
434
|
+
index,
|
|
412
435
|
message: `Multiple catch-all routes on port ${port}`
|
|
413
436
|
});
|
|
414
437
|
}
|
|
@@ -419,9 +442,9 @@ export class RouteValidator {
|
|
|
419
442
|
// a shorthand for the full port/transport listener policy, so every route
|
|
420
443
|
// sharing that listener must either all omit it or all use an identical one.
|
|
421
444
|
const listenerPolicies = new Map<string, { route: string; fingerprint: string }>();
|
|
422
|
-
|
|
445
|
+
routes.forEach((route, index) => {
|
|
423
446
|
if (!route.match?.ports) {
|
|
424
|
-
|
|
447
|
+
return;
|
|
425
448
|
}
|
|
426
449
|
|
|
427
450
|
for (const port of this.expandPorts(route.match.ports)) {
|
|
@@ -431,18 +454,18 @@ export class RouteValidator {
|
|
|
431
454
|
const existing = listenerPolicies.get(key);
|
|
432
455
|
if (!existing) {
|
|
433
456
|
listenerPolicies.set(key, {
|
|
434
|
-
route:
|
|
457
|
+
route: labels[index],
|
|
435
458
|
fingerprint,
|
|
436
459
|
});
|
|
437
460
|
} else if (existing.fingerprint !== fingerprint) {
|
|
438
461
|
conflicts.push({
|
|
439
|
-
|
|
462
|
+
index,
|
|
440
463
|
message: `Conflicting inbound PROXY protocol policy on ${transport} port ${port} (differs from ${existing.route})`,
|
|
441
464
|
});
|
|
442
465
|
}
|
|
443
466
|
}
|
|
444
467
|
}
|
|
445
|
-
}
|
|
468
|
+
});
|
|
446
469
|
|
|
447
470
|
return conflicts;
|
|
448
471
|
}
|
|
@@ -1031,12 +1054,14 @@ export function hasRequiredPropertiesForAction(route: IRouteConfig, actionType:
|
|
|
1031
1054
|
* Useful for immediate validation when creating routes
|
|
1032
1055
|
* @param route Route configuration to validate
|
|
1033
1056
|
* @returns The validated route configuration
|
|
1034
|
-
* @throws
|
|
1057
|
+
* @throws RouteValidationError if the route configuration is invalid
|
|
1035
1058
|
*/
|
|
1036
1059
|
export function assertValidRoute(route: IRouteConfig): IRouteConfig {
|
|
1037
1060
|
const validation = validateRouteConfig(route);
|
|
1038
1061
|
if (!validation.valid) {
|
|
1039
|
-
throw new
|
|
1062
|
+
throw new RouteValidationError('Invalid route configuration', [
|
|
1063
|
+
{ index: 0, route: RouteValidator.getRouteLabels([route])[0], messages: validation.errors },
|
|
1064
|
+
]);
|
|
1040
1065
|
}
|
|
1041
1066
|
return route;
|
|
1042
1067
|
}
|