@push.rocks/smartproxy 27.22.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 +47 -0
- package/dist_rust/rustproxy_linux_amd64 +0 -0
- package/dist_rust/rustproxy_linux_amd64.tsrust-build.json +5 -5
- package/dist_rust/rustproxy_linux_arm64 +0 -0
- package/dist_rust/rustproxy_linux_arm64.tsrust-build.json +5 -5
- package/dist_ts/00_commitinfo_data.js +2 -2
- package/dist_ts/index.d.ts +1 -1
- package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +4 -2
- package/dist_ts/proxies/smart-proxy/models/route-types.d.ts +12 -6
- package/dist_ts/proxies/smart-proxy/models/route-types.js +1 -1
- 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 +46 -41
- 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 +14 -2
- package/dist_ts/proxies/smart-proxy/utils/route-validator.js +178 -43
- package/package.json +3 -3
- package/readme.md +74 -4
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/index.ts +1 -1
- package/ts/proxies/smart-proxy/models/interfaces.ts +4 -2
- package/ts/proxies/smart-proxy/models/route-types.ts +14 -6
- 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 +41 -40
- 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 +208 -51
package/changelog.md
CHANGED
|
@@ -1,5 +1,52 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2026-09-25 - 28.1.0
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- throw `RouteValidationError` with the per-route reasons when SmartProxy refuses a route set (routes)
|
|
8
|
+
- The constructor and `updateRoutes()` threw a plain `Error` that only counted the invalid routes, for example `Route validation failed: 1 route(s) have errors`; the reasons, such as `response.body requires an explicit content-type header`, went only to the log. A consumer that relays the error could neither show why a route was refused nor tell a refusal from an internal failure.
|
|
9
|
+
- `RouteValidationError` (exported with its entry type `IRouteValidationErrorEntry`) has the name `RouteValidationError` and an `errors` list with every rejected route: its `index` in the passed list, its `route` label and all its reasons. Its message names the first 10 routes with up to 5 reasons each, shortens long labels and reasons to 200 characters and counts the rest, so a large route set cannot produce an unbounded message.
|
|
10
|
+
- The same error comes from the TypeScript validation in the constructor and `updateRoutes()`, from challenge route validation in `start()` and `updateRoutes()`, and from `assertValidRoute()`. Challenge reasons no longer repeat the route name, which is now the entry's `route`.
|
|
11
|
+
- Checks that only the Rust engine makes, such as duplicate route ids or an `inboundProxyProtocol` policy without route-level or global `trustedProxyIPs`, reach `start()` and `updateRoutes()` as the same error. The engine answers a refused `start` or `updateRoutes` command with `errorCode: 'ROUTE_VALIDATION'` and the rejected routes in `errorData.routes`.
|
|
12
|
+
- The error is still an `Error`; only the message text changes.
|
|
13
|
+
- add `RouteValidator.getRouteLabels()` (routes)
|
|
14
|
+
- It labels each route by its name, else its id, else `route[<index>]`, and appends `[<index>]` to a name or id that several routes share. `RouteValidator.validateRoutes()` returns its reasons per route as a new `entries` list; route refusals from the Rust engine carry the same labels, matched to the sent routes by index.
|
|
15
|
+
|
|
16
|
+
### Fixes
|
|
17
|
+
|
|
18
|
+
- keep the reasons of every rejected route (routes)
|
|
19
|
+
- `RouteValidator.validateRoutes()` keyed its error map by `name || 'unnamed'`, so unnamed routes overwrote each other's reasons, and a route's own errors replaced the `Duplicate route name` error of a route with the same name. The map is now keyed by the unique labels above.
|
|
20
|
+
- The Rust engine refused an invalid route set at start with only a count (`Route validation failed with N errors`); it now reports every rejected route with its reasons.
|
|
21
|
+
|
|
22
|
+
- use `rustBinaryPath` (process)
|
|
23
|
+
- `ISmartProxyOptions.rustBinaryPath` was declared and documented as the first place to look for the Rust binary, but SmartProxy never passed it on and always located the binary by discovery.
|
|
24
|
+
- It is now the only candidate when set. A missing or non-executable path makes `start()` reject with a `RustBinaryLocatorError` naming the path, without falling back to discovery.
|
|
25
|
+
|
|
26
|
+
### Maintenance
|
|
27
|
+
|
|
28
|
+
- update `@push.rocks/smartrust` from `^1.9.0` to `^2.4.0` (dependencies)
|
|
29
|
+
- 2.4.0 carries the structured `errorData` of Rust error responses as `RustBridgeRequestError.responseErrorData`. The breaking change in 2.0.0, strict validation of an explicit `binaryPath`, applies to `rustBinaryPath` as described above; discovery through the environment variable, the platform packages and local paths is unchanged.
|
|
30
|
+
- regenerate `deno.lock` (tooling)
|
|
31
|
+
- The lockfile still listed the development tooling of 27.x (`tstest` 4, `tsbuild` 4) and pointed every package at the Verdaccio proxy through `tarball` URLs, which Deno 2.9 rejects when it installs against npmjs. It now matches `package.json` and carries no `tarball` URLs; `deno install --frozen` against npmjs succeeds with it.
|
|
32
|
+
|
|
33
|
+
## 2026-09-25 - 28.0.0
|
|
34
|
+
|
|
35
|
+
### Breaking Changes
|
|
36
|
+
|
|
37
|
+
- remove `action.advanced.testResponse` and the `IRouteTestResponse` type (routes)
|
|
38
|
+
- A route whose `advanced.testResponse` was set answered every request with that response, but only after the TypeScript validator had demanded a forward target and only when the connection reached the HTTP layer; a route without a target was closed before TLS or HTTP. The Rust handler also turned an invalid status into 200 and dropped invalid headers without a word.
|
|
39
|
+
- Use a route with `action.type: 'respond'` and `action.response` instead. The readme has a migration note.
|
|
40
|
+
|
|
41
|
+
### Features
|
|
42
|
+
|
|
43
|
+
- add the `respond` route action for fixed HTTP responses without an upstream (routes)
|
|
44
|
+
- `action.response` (exported type `IRouteFixedResponse`) sets `status` (200–599), `headers` and an optional UTF-8 `body` of at most 64 KiB; the limit is exported as `MAX_FIXED_RESPONSE_BODY_BYTES`. A non-empty body needs an explicit `content-type` header; 204 and 304 carry no body; hop-by-hop and computed headers (`connection`, `keep-alive`, `transfer-encoding`, `upgrade`, `te`, `trailer`, `proxy-connection`, `content-length`) are rejected. Both the TypeScript validators and the Rust route validation reject an invalid response, and SmartProxy refuses to start or update routes with one.
|
|
45
|
+
- A respond route answers plain HTTP, or HTTPS with `tls.mode: 'terminate'` and a static or `'auto'` certificate, over HTTP/1.1 and h2. Route security, rate limits, challenges and `maxConnections` apply before the response. Non-HTTP connections to a respond route are closed.
|
|
46
|
+
- Respond routes do not support UDP or HTTP/3 yet: `action.udp`, `match.transport` `'udp'` or `'all'` and any `match.protocol` other than `'http'` are rejected. `targets`, socket and datagram handlers and nftables forwarding are rejected as well.
|
|
47
|
+
- `getEligibleDomainsForCertificates()` includes the domains of respond routes with `certificate: 'auto'`.
|
|
48
|
+
- The Rust TCP listener accepts TLS, detects HTTP and hands the connection to the HTTP proxy through one shared path for `terminate`, `terminate-and-reencrypt` and respond routes; forward routes behave as before.
|
|
49
|
+
|
|
3
50
|
## 2026-09-24 - 27.22.0
|
|
4
51
|
|
|
5
52
|
### Features
|
|
Binary file
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"format": "tsrust.build-provenance.v2",
|
|
3
|
-
"binarySha256": "
|
|
3
|
+
"binarySha256": "215b6a6f4376e8db17e92423ee26fc904b18e4dcbd7a4f45fa278d9ff751a77e",
|
|
4
4
|
"buildInfo": {
|
|
5
5
|
"projectName": "@push.rocks/smartproxy",
|
|
6
|
-
"projectVersion": "
|
|
7
|
-
"gitCommit": "
|
|
6
|
+
"projectVersion": "28.1.0",
|
|
7
|
+
"gitCommit": "1366dbee20ee8d15391225a72e3ccf0bd9e1a393",
|
|
8
8
|
"gitDirty": false,
|
|
9
|
-
"builtAt": "2026-09-
|
|
10
|
-
"tsrustVersion": "1.
|
|
9
|
+
"builtAt": "2026-09-25T09:08:38.732Z",
|
|
10
|
+
"tsrustVersion": "1.14.0",
|
|
11
11
|
"binary": "rustproxy",
|
|
12
12
|
"target": "linux_amd64"
|
|
13
13
|
}
|
|
Binary file
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"format": "tsrust.build-provenance.v2",
|
|
3
|
-
"binarySha256": "
|
|
3
|
+
"binarySha256": "51cd3a5a0830160c897692216038967970fa8375253d2e62819125085a006fa9",
|
|
4
4
|
"buildInfo": {
|
|
5
5
|
"projectName": "@push.rocks/smartproxy",
|
|
6
|
-
"projectVersion": "
|
|
7
|
-
"gitCommit": "
|
|
6
|
+
"projectVersion": "28.1.0",
|
|
7
|
+
"gitCommit": "1366dbee20ee8d15391225a72e3ccf0bd9e1a393",
|
|
8
8
|
"gitDirty": false,
|
|
9
|
-
"builtAt": "2026-09-
|
|
10
|
-
"tsrustVersion": "1.
|
|
9
|
+
"builtAt": "2026-09-25T09:09:38.815Z",
|
|
10
|
+
"tsrustVersion": "1.14.0",
|
|
11
11
|
"binary": "rustproxy",
|
|
12
12
|
"target": "linux_arm64"
|
|
13
13
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@push.rocks/smartproxy',
|
|
6
|
-
version: '
|
|
6
|
+
version: '28.1.0',
|
|
7
7
|
description: 'A powerful proxy package with unified route-based configuration for high traffic management. Features include SSL/TLS support, flexible routing patterns, WebSocket handling, advanced security options, and automatic ACME certificate management.'
|
|
8
8
|
};
|
|
9
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
9
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSx3QkFBd0I7SUFDOUIsT0FBTyxFQUFFLFFBQVE7SUFDakIsV0FBVyxFQUFFLHFQQUFxUDtDQUNuUSxDQUFBIn0=
|
package/dist_ts/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export { SmartProxy } from './proxies/smart-proxy/index.js';
|
|
5
5
|
export { SharedRouteManager as RouteManager } from './core/routing/route-manager.js';
|
|
6
|
-
export type { ISmartProxyOptions, ISmartProxySecurityPolicy, ISmartProxyChallengeOptions, IConnectionRecord, IRouteConfig, IRouteMatch, IRouteAction, IRouteTls, IRouteContext, TDatagramHandler, IDatagramInfo, IActiveConnectionSnapshot, IActiveConnectionSnapshotOptions, IActiveConnectionSnapshotPage } from './proxies/smart-proxy/models/index.js';
|
|
6
|
+
export type { ISmartProxyOptions, ISmartProxySecurityPolicy, ISmartProxyChallengeOptions, IConnectionRecord, IRouteConfig, IRouteMatch, IRouteAction, IRouteFixedResponse, IRouteTls, IRouteContext, TDatagramHandler, IDatagramInfo, IActiveConnectionSnapshot, IActiveConnectionSnapshotOptions, IActiveConnectionSnapshotPage } from './proxies/smart-proxy/models/index.js';
|
|
7
7
|
export type { TSmartProxyCertProvisionObject, ICertProvisionEventComms, ICertificateIssuedEvent, ICertificateFailedEvent } from './proxies/smart-proxy/models/interfaces.js';
|
|
8
8
|
export * from './proxies/smart-proxy/utils/index.js';
|
|
9
9
|
export * from './core/models/common-types.js';
|
|
@@ -247,8 +247,10 @@ export interface ISmartProxyOptions {
|
|
|
247
247
|
*/
|
|
248
248
|
certStore?: ISmartProxyCertStore;
|
|
249
249
|
/**
|
|
250
|
-
* Path to the RustProxy binary.
|
|
251
|
-
*
|
|
250
|
+
* Path to the RustProxy binary. When set, it is the only candidate: `start()` rejects
|
|
251
|
+
* with a `RustBinaryLocatorError` if it is missing or not executable, without falling
|
|
252
|
+
* back to discovery. If not set, the binary is located automatically via env var,
|
|
253
|
+
* platform package, local build, or PATH.
|
|
252
254
|
*/
|
|
253
255
|
rustBinaryPath?: string;
|
|
254
256
|
}
|
|
@@ -4,7 +4,7 @@ export type { IRouteContext };
|
|
|
4
4
|
/**
|
|
5
5
|
* Supported action types for route configurations
|
|
6
6
|
*/
|
|
7
|
-
export type TRouteActionType = 'forward' | 'socket-handler';
|
|
7
|
+
export type TRouteActionType = 'forward' | 'socket-handler' | 'respond';
|
|
8
8
|
/**
|
|
9
9
|
* Socket handler function type
|
|
10
10
|
*/
|
|
@@ -184,12 +184,18 @@ export interface IRouteStaticFiles {
|
|
|
184
184
|
disableDirectoryListing?: boolean;
|
|
185
185
|
}
|
|
186
186
|
/**
|
|
187
|
-
*
|
|
187
|
+
* Fixed response answered by a `respond` route without contacting any upstream.
|
|
188
|
+
*
|
|
189
|
+
* - `status` must be an integer from 200 to 599.
|
|
190
|
+
* - `headers` must not contain hop-by-hop or computed headers (`connection`, `keep-alive`,
|
|
191
|
+
* `transfer-encoding`, `upgrade`, `te`, `trailer`, `proxy-connection`, `content-length`).
|
|
192
|
+
* - A non-empty `body` requires an explicit `content-type` header; 204 and 304 carry no body.
|
|
193
|
+
* - `body` is UTF-8 text of at most 65536 bytes.
|
|
188
194
|
*/
|
|
189
|
-
export interface
|
|
195
|
+
export interface IRouteFixedResponse {
|
|
190
196
|
status: number;
|
|
191
|
-
headers
|
|
192
|
-
body
|
|
197
|
+
headers?: Record<string, string>;
|
|
198
|
+
body?: string;
|
|
193
199
|
}
|
|
194
200
|
/**
|
|
195
201
|
* URL rewriting configuration
|
|
@@ -208,7 +214,6 @@ export interface IRouteAdvanced {
|
|
|
208
214
|
headers?: Record<string, string>;
|
|
209
215
|
keepAlive?: boolean;
|
|
210
216
|
staticFiles?: IRouteStaticFiles;
|
|
211
|
-
testResponse?: IRouteTestResponse;
|
|
212
217
|
urlRewrite?: IRouteUrlRewrite;
|
|
213
218
|
}
|
|
214
219
|
/**
|
|
@@ -254,6 +259,7 @@ export interface IRouteAction {
|
|
|
254
259
|
};
|
|
255
260
|
forwardingEngine?: 'node' | 'nftables';
|
|
256
261
|
nftables?: INfTablesOptions;
|
|
262
|
+
response?: IRouteFixedResponse;
|
|
257
263
|
socketHandler?: TSocketHandler;
|
|
258
264
|
datagramHandler?: TDatagramHandler;
|
|
259
265
|
sendProxyProtocol?: boolean;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import * as plugins from '../../../plugins.js';
|
|
2
2
|
// Configuration moved to models/interfaces.ts as ISmartProxyOptions
|
|
3
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
3
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUtdHlwZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi90cy9wcm94aWVzL3NtYXJ0LXByb3h5L21vZGVscy9yb3V0ZS10eXBlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLHFCQUFxQixDQUFDO0FBNGUvQyxvRUFBb0UifQ==
|
|
@@ -27,6 +27,23 @@ export interface IRustRouteConfig extends Omit<IRouteConfig, 'match' | 'action'>
|
|
|
27
27
|
match: IRustRouteMatch;
|
|
28
28
|
action: IRustRouteAction;
|
|
29
29
|
}
|
|
30
|
+
/** `errorCode` of a Rust `start` or `updateRoutes` response that refused its routes in validation. */
|
|
31
|
+
export declare const RUST_ROUTE_VALIDATION_ERROR_CODE = "ROUTE_VALIDATION";
|
|
32
|
+
/** One rejected route in the `errorData` of a `ROUTE_VALIDATION` response. */
|
|
33
|
+
export interface IRustRouteValidationIssue {
|
|
34
|
+
/** The route's position in the sent route list. */
|
|
35
|
+
index: number;
|
|
36
|
+
/**
|
|
37
|
+
* The engine's label of the route as it received it. The preprocessor names unnamed routes
|
|
38
|
+
* `route_<index>`, so TypeScript relabels issues by `index` and does not surface this label.
|
|
39
|
+
*/
|
|
40
|
+
route: string;
|
|
41
|
+
messages: string[];
|
|
42
|
+
}
|
|
43
|
+
/** `errorData` of a `ROUTE_VALIDATION` response. */
|
|
44
|
+
export interface IRustRouteValidationErrorData {
|
|
45
|
+
routes: IRustRouteValidationIssue[];
|
|
46
|
+
}
|
|
30
47
|
export interface IRustAcmeOptions extends Omit<IAcmeOptions, 'routeForwards'> {
|
|
31
48
|
}
|
|
32
49
|
export interface IRustChallengeOptions extends Required<Pick<ISmartProxyChallengeOptions, 'cookieSigningKey' | 'pendingCookieName' | 'clearanceCookieName' | 'reservedPathPrefix' | 'relaySocketPath' | 'relayTimeoutMs' | 'pendingTtlSeconds'>> {
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/** `errorCode` of a Rust `start` or `updateRoutes` response that refused its routes in validation. */
|
|
2
|
+
export const RUST_ROUTE_VALIDATION_ERROR_CODE = 'ROUTE_VALIDATION';
|
|
3
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicnVzdC10eXBlcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3RzL3Byb3hpZXMvc21hcnQtcHJveHkvbW9kZWxzL3J1c3QtdHlwZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBOENBLHNHQUFzRztBQUN0RyxNQUFNLENBQUMsTUFBTSxnQ0FBZ0MsR0FBRyxrQkFBa0IsQ0FBQyJ9
|
|
@@ -1,13 +1,29 @@
|
|
|
1
1
|
import * as plugins from '../../plugins.js';
|
|
2
|
-
import type
|
|
2
|
+
import { type IRustCertificateStatus, type IRustChallengeOptions, type IRustMetricsSnapshot, type IRustProxyOptions, type IRustMetricsHistory, type IRustRouteConfig, type IRustStatistics } from './models/rust-types.js';
|
|
3
3
|
import type { IActiveConnectionSnapshot, IActiveConnectionSnapshotOptions, IActiveConnectionSnapshotPage, ISmartProxySecurityPolicy } from './models/interfaces.js';
|
|
4
|
+
/**
|
|
5
|
+
* Turn a Rust `ROUTE_VALIDATION` refusal into a `RouteValidationError` carrying the
|
|
6
|
+
* per-route reasons from its `errorData`. Any other error is returned unchanged.
|
|
7
|
+
*
|
|
8
|
+
* `routeLabels` holds `RouteValidator.getRouteLabels()` of the caller's routes, one per sent
|
|
9
|
+
* route. Entries are relabelled by index: the preprocessor names unnamed routes for Rust, so
|
|
10
|
+
* the engine's own labels can differ from the ones the caller sees.
|
|
11
|
+
*/
|
|
12
|
+
export declare function toRouteValidationError(error: unknown, routeLabels: string[]): unknown;
|
|
13
|
+
export interface IRustProxyBridgeOptions {
|
|
14
|
+
/**
|
|
15
|
+
* Explicit path to the RustProxy binary. When set, it is the only candidate:
|
|
16
|
+
* `spawn()` rejects with a `RustBinaryLocatorError` if it is missing or not executable.
|
|
17
|
+
*/
|
|
18
|
+
binaryPath?: string;
|
|
19
|
+
}
|
|
4
20
|
/**
|
|
5
21
|
* Bridge between TypeScript SmartProxy and the Rust binary.
|
|
6
22
|
* Wraps @push.rocks/smartrust's RustBridge with type-safe command definitions.
|
|
7
23
|
*/
|
|
8
24
|
export declare class RustProxyBridge extends plugins.EventEmitter {
|
|
9
25
|
private bridge;
|
|
10
|
-
constructor();
|
|
26
|
+
constructor(options?: IRustProxyBridgeOptions);
|
|
11
27
|
/**
|
|
12
28
|
* Spawn the Rust binary in management mode.
|
|
13
29
|
* Returns true if the binary was found and spawned successfully.
|
|
@@ -23,9 +39,17 @@ export declare class RustProxyBridge extends plugins.EventEmitter {
|
|
|
23
39
|
* Whether the bridge is currently running.
|
|
24
40
|
*/
|
|
25
41
|
get running(): boolean;
|
|
26
|
-
|
|
42
|
+
/**
|
|
43
|
+
* @param routeLabels `RouteValidator.getRouteLabels()` of the routes before preprocessing,
|
|
44
|
+
* used to label a route validation refusal.
|
|
45
|
+
*/
|
|
46
|
+
startProxy(config: IRustProxyOptions, routeLabels: string[]): Promise<void>;
|
|
27
47
|
stopProxy(): Promise<void>;
|
|
28
|
-
|
|
48
|
+
/**
|
|
49
|
+
* @param routeLabels `RouteValidator.getRouteLabels()` of the routes before preprocessing,
|
|
50
|
+
* used to label a route validation refusal.
|
|
51
|
+
*/
|
|
52
|
+
updateRoutes(routes: IRustRouteConfig[], routeLabels: string[]): Promise<void>;
|
|
29
53
|
setSecurityPolicy(policy: ISmartProxySecurityPolicy): Promise<void>;
|
|
30
54
|
getMetrics(): Promise<IRustMetricsSnapshot>;
|
|
31
55
|
getMetricsHistory(seconds: number): Promise<IRustMetricsHistory>;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import * as plugins from '../../plugins.js';
|
|
2
2
|
import { logger } from '../../core/utils/logger.js';
|
|
3
|
+
import { RUST_ROUTE_VALIDATION_ERROR_CODE, } from './models/rust-types.js';
|
|
4
|
+
import { RouteValidationError } from './utils/route-validation-error.js';
|
|
3
5
|
const MAXIMUM_RUST_CONNECTION_ID = 18446744073709551615n;
|
|
4
6
|
const MAXIMUM_METRICS_HISTORY_SECONDS = 600;
|
|
5
7
|
function validateActiveConnectionSnapshotOptions(options) {
|
|
@@ -13,6 +15,52 @@ function validateActiveConnectionSnapshotOptions(options) {
|
|
|
13
15
|
throw new Error('Active connection snapshot afterId exceeds the Rust u64 range');
|
|
14
16
|
}
|
|
15
17
|
}
|
|
18
|
+
function isRustRouteValidationErrorData(data, sentRouteCount) {
|
|
19
|
+
if (typeof data !== 'object'
|
|
20
|
+
|| data === null
|
|
21
|
+
|| !('routes' in data)
|
|
22
|
+
|| !Array.isArray(data.routes)
|
|
23
|
+
|| data.routes.length === 0) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
return data.routes.every((issue) => typeof issue === 'object'
|
|
27
|
+
&& issue !== null
|
|
28
|
+
&& 'index' in issue
|
|
29
|
+
&& typeof issue.index === 'number'
|
|
30
|
+
&& Number.isSafeInteger(issue.index)
|
|
31
|
+
&& issue.index >= 0
|
|
32
|
+
&& issue.index < sentRouteCount
|
|
33
|
+
&& 'route' in issue
|
|
34
|
+
&& typeof issue.route === 'string'
|
|
35
|
+
&& 'messages' in issue
|
|
36
|
+
&& Array.isArray(issue.messages)
|
|
37
|
+
&& issue.messages.every((message) => typeof message === 'string'));
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Turn a Rust `ROUTE_VALIDATION` refusal into a `RouteValidationError` carrying the
|
|
41
|
+
* per-route reasons from its `errorData`. Any other error is returned unchanged.
|
|
42
|
+
*
|
|
43
|
+
* `routeLabels` holds `RouteValidator.getRouteLabels()` of the caller's routes, one per sent
|
|
44
|
+
* route. Entries are relabelled by index: the preprocessor names unnamed routes for Rust, so
|
|
45
|
+
* the engine's own labels can differ from the ones the caller sees.
|
|
46
|
+
*/
|
|
47
|
+
export function toRouteValidationError(error, routeLabels) {
|
|
48
|
+
if (!(error instanceof plugins.smartrust.RustBridgeRequestError)
|
|
49
|
+
|| error.responseErrorCode !== RUST_ROUTE_VALIDATION_ERROR_CODE) {
|
|
50
|
+
return error;
|
|
51
|
+
}
|
|
52
|
+
if (!isRustRouteValidationErrorData(error.responseErrorData, routeLabels.length)) {
|
|
53
|
+
return new Error(`RustProxy sent a route validation refusal with malformed errorData: ${error.message}`, {
|
|
54
|
+
cause: error,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
return new RouteValidationError('Route validation failed', error.responseErrorData.routes.map(({ index, messages }) => ({ index, route: routeLabels[index], messages })));
|
|
58
|
+
}
|
|
59
|
+
function assertOneLabelPerRoute(routes, routeLabels) {
|
|
60
|
+
if (routes.length !== routeLabels.length) {
|
|
61
|
+
throw new Error(`RustProxyBridge got ${routeLabels.length} route label(s) for ${routes.length} route(s); they must match one to one`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
16
64
|
/**
|
|
17
65
|
* Get the package root directory using import.meta.url.
|
|
18
66
|
* This file is at ts/proxies/smart-proxy/, so package root is 3 levels up.
|
|
@@ -59,10 +107,11 @@ function buildLocalPaths() {
|
|
|
59
107
|
*/
|
|
60
108
|
export class RustProxyBridge extends plugins.EventEmitter {
|
|
61
109
|
bridge;
|
|
62
|
-
constructor() {
|
|
110
|
+
constructor(options = {}) {
|
|
63
111
|
super();
|
|
64
112
|
this.bridge = new plugins.smartrust.RustBridge({
|
|
65
113
|
binaryName: 'rustproxy',
|
|
114
|
+
binaryPath: options.binaryPath,
|
|
66
115
|
envVarName: 'SMARTPROXY_RUST_BINARY',
|
|
67
116
|
platformPackagePrefix: '@push.rocks/smartproxy',
|
|
68
117
|
localPaths: buildLocalPaths(),
|
|
@@ -102,14 +151,34 @@ export class RustProxyBridge extends plugins.EventEmitter {
|
|
|
102
151
|
return this.bridge.running;
|
|
103
152
|
}
|
|
104
153
|
// --- Convenience methods for each management command ---
|
|
105
|
-
|
|
106
|
-
|
|
154
|
+
/**
|
|
155
|
+
* @param routeLabels `RouteValidator.getRouteLabels()` of the routes before preprocessing,
|
|
156
|
+
* used to label a route validation refusal.
|
|
157
|
+
*/
|
|
158
|
+
async startProxy(config, routeLabels) {
|
|
159
|
+
assertOneLabelPerRoute(config.routes, routeLabels);
|
|
160
|
+
try {
|
|
161
|
+
await this.bridge.sendCommand('start', { config });
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
throw toRouteValidationError(error, routeLabels);
|
|
165
|
+
}
|
|
107
166
|
}
|
|
108
167
|
async stopProxy() {
|
|
109
168
|
await this.bridge.sendCommand('stop', {});
|
|
110
169
|
}
|
|
111
|
-
|
|
112
|
-
|
|
170
|
+
/**
|
|
171
|
+
* @param routeLabels `RouteValidator.getRouteLabels()` of the routes before preprocessing,
|
|
172
|
+
* used to label a route validation refusal.
|
|
173
|
+
*/
|
|
174
|
+
async updateRoutes(routes, routeLabels) {
|
|
175
|
+
assertOneLabelPerRoute(routes, routeLabels);
|
|
176
|
+
try {
|
|
177
|
+
await this.bridge.sendCommand('updateRoutes', { routes });
|
|
178
|
+
}
|
|
179
|
+
catch (error) {
|
|
180
|
+
throw toRouteValidationError(error, routeLabels);
|
|
181
|
+
}
|
|
113
182
|
}
|
|
114
183
|
async setSecurityPolicy(policy) {
|
|
115
184
|
await this.bridge.sendCommand('setSecurityPolicy', { policy });
|
|
@@ -167,4 +236,4 @@ export class RustProxyBridge extends plugins.EventEmitter {
|
|
|
167
236
|
await this.bridge.sendCommand('setDatagramHandlerRelay', { socketPath });
|
|
168
237
|
}
|
|
169
238
|
}
|
|
170
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicnVzdC1wcm94eS1icmlkZ2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9wcm94aWVzL3NtYXJ0LXByb3h5L3J1c3QtcHJveHktYnJpZGdlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sa0JBQWtCLENBQUM7QUFDNUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBWXBELE1BQU0sMEJBQTBCLEdBQUcscUJBQTJCLENBQUM7QUFDL0QsTUFBTSwrQkFBK0IsR0FBRyxHQUFHLENBQUM7QUFFNUMsU0FBUyx1Q0FBdUMsQ0FBQyxPQUF5QztJQUN4RixJQUFJLE9BQU8sQ0FBQyxPQUFPLEtBQUssU0FBUyxFQUFFLENBQUM7UUFDbEMsT0FBTztJQUNULENBQUM7SUFDRCxJQUFJLE9BQU8sT0FBTyxDQUFDLE9BQU8sS0FBSyxRQUFRLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDO1FBQzFFLE1BQU0sSUFBSSxLQUFLLENBQUMsdUVBQXVFLENBQUMsQ0FBQztJQUMzRixDQUFDO0lBQ0QsSUFBSSxNQUFNLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxHQUFHLDBCQUEwQixFQUFFLENBQUM7UUFDekQsTUFBTSxJQUFJLEtBQUssQ0FBQywrREFBK0QsQ0FBQyxDQUFDO0lBQ25GLENBQUM7QUFDSCxDQUFDO0FBMkJEOzs7R0FHRztBQUNILFNBQVMsY0FBYztJQUNyQixNQUFNLE9BQU8sR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLGFBQWEsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7SUFDakYsT0FBTyxPQUFPLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxPQUFPLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxJQUFJLENB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
|
|
239
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicnVzdC1wcm94eS1icmlkZ2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9wcm94aWVzL3NtYXJ0LXByb3h5L3J1c3QtcHJveHktYnJpZGdlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sa0JBQWtCLENBQUM7QUFDNUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBQ3BELE9BQU8sRUFDTCxnQ0FBZ0MsR0FTakMsTUFBTSx3QkFBd0IsQ0FBQztBQUNoQyxPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSxtQ0FBbUMsQ0FBQztBQUd6RSxNQUFNLDBCQUEwQixHQUFHLHFCQUEyQixDQUFDO0FBQy9ELE1BQU0sK0JBQStCLEdBQUcsR0FBRyxDQUFDO0FBRTVDLFNBQVMsdUNBQXVDLENBQUMsT0FBeUM7SUFDeEYsSUFBSSxPQUFPLENBQUMsT0FBTyxLQUFLLFNBQVMsRUFBRSxDQUFDO1FBQ2xDLE9BQU87SUFDVCxDQUFDO0lBQ0QsSUFBSSxPQUFPLE9BQU8sQ0FBQyxPQUFPLEtBQUssUUFBUSxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQztRQUMxRSxNQUFNLElBQUksS0FBSyxDQUFDLHVFQUF1RSxDQUFDLENBQUM7SUFDM0YsQ0FBQztJQUNELElBQUksTUFBTSxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsR0FBRywwQkFBMEIsRUFBRSxDQUFDO1FBQ3pELE1BQU0sSUFBSSxLQUFLLENBQUMsK0RBQStELENBQUMsQ0FBQztJQUNuRixDQUFDO0FBQ0gsQ0FBQztBQUVELFNBQVMsOEJBQThCLENBQ3JDLElBQWEsRUFDYixjQUFzQjtJQUV0QixJQUNFLE9BQU8sSUFBSSxLQUFLLFFBQVE7V0FDckIsSUFBSSxLQUFLLElBQUk7V0FDYixDQUFDLENBQUMsUUFBUSxJQUFJLElBQUksQ0FBQztXQUNuQixDQUFD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
|