@push.rocks/smartproxy 22.4.2 → 22.6.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 +28 -0
- package/dist_rust/rustproxy +0 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/index.d.ts +1 -5
- package/dist_ts/index.js +3 -9
- package/dist_ts/protocols/common/fragment-handler.js +5 -1
- package/dist_ts/proxies/index.d.ts +1 -5
- package/dist_ts/proxies/index.js +2 -6
- package/dist_ts/proxies/smart-proxy/index.d.ts +5 -10
- package/dist_ts/proxies/smart-proxy/index.js +7 -13
- package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +5 -2
- package/dist_ts/proxies/smart-proxy/route-preprocessor.d.ts +37 -0
- package/dist_ts/proxies/smart-proxy/route-preprocessor.js +103 -0
- package/dist_ts/proxies/smart-proxy/rust-binary-locator.d.ts +23 -0
- package/dist_ts/proxies/smart-proxy/rust-binary-locator.js +104 -0
- package/dist_ts/proxies/smart-proxy/rust-metrics-adapter.d.ts +74 -0
- package/dist_ts/proxies/smart-proxy/rust-metrics-adapter.js +146 -0
- package/dist_ts/proxies/smart-proxy/rust-proxy-bridge.d.ts +49 -0
- package/dist_ts/proxies/smart-proxy/rust-proxy-bridge.js +259 -0
- package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +39 -157
- package/dist_ts/proxies/smart-proxy/smart-proxy.js +224 -621
- package/dist_ts/proxies/smart-proxy/socket-handler-server.d.ts +45 -0
- package/dist_ts/proxies/smart-proxy/socket-handler-server.js +253 -0
- package/dist_ts/routing/index.d.ts +1 -1
- package/dist_ts/routing/index.js +3 -3
- package/dist_ts/routing/models/http-types.d.ts +119 -4
- package/dist_ts/routing/models/http-types.js +93 -5
- package/package.json +1 -1
- package/readme.md +470 -219
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/index.ts +4 -12
- package/ts/protocols/common/fragment-handler.ts +4 -0
- package/ts/proxies/index.ts +1 -9
- package/ts/proxies/smart-proxy/index.ts +6 -13
- package/ts/proxies/smart-proxy/models/interfaces.ts +6 -4
- package/ts/proxies/smart-proxy/route-preprocessor.ts +122 -0
- package/ts/proxies/smart-proxy/rust-binary-locator.ts +112 -0
- package/ts/proxies/smart-proxy/rust-metrics-adapter.ts +161 -0
- package/ts/proxies/smart-proxy/rust-proxy-bridge.ts +310 -0
- package/ts/proxies/smart-proxy/smart-proxy.ts +282 -798
- package/ts/proxies/smart-proxy/socket-handler-server.ts +279 -0
- package/ts/routing/index.ts +2 -2
- package/ts/routing/models/http-types.ts +147 -4
- package/ts/proxies/http-proxy/connection-pool.ts +0 -228
- package/ts/proxies/http-proxy/context-creator.ts +0 -145
- package/ts/proxies/http-proxy/default-certificates.ts +0 -150
- package/ts/proxies/http-proxy/function-cache.ts +0 -279
- package/ts/proxies/http-proxy/handlers/index.ts +0 -5
- package/ts/proxies/http-proxy/http-proxy.ts +0 -669
- package/ts/proxies/http-proxy/http-request-handler.ts +0 -331
- package/ts/proxies/http-proxy/http2-request-handler.ts +0 -255
- package/ts/proxies/http-proxy/index.ts +0 -18
- package/ts/proxies/http-proxy/models/http-types.ts +0 -148
- package/ts/proxies/http-proxy/models/index.ts +0 -5
- package/ts/proxies/http-proxy/models/types.ts +0 -125
- package/ts/proxies/http-proxy/request-handler.ts +0 -878
- package/ts/proxies/http-proxy/security-manager.ts +0 -413
- package/ts/proxies/http-proxy/websocket-handler.ts +0 -581
- package/ts/proxies/smart-proxy/acme-state-manager.ts +0 -112
- package/ts/proxies/smart-proxy/cert-store.ts +0 -92
- package/ts/proxies/smart-proxy/certificate-manager.ts +0 -895
- package/ts/proxies/smart-proxy/connection-manager.ts +0 -809
- package/ts/proxies/smart-proxy/http-proxy-bridge.ts +0 -213
- package/ts/proxies/smart-proxy/metrics-collector.ts +0 -453
- package/ts/proxies/smart-proxy/nftables-manager.ts +0 -271
- package/ts/proxies/smart-proxy/port-manager.ts +0 -358
- package/ts/proxies/smart-proxy/route-connection-handler.ts +0 -1712
- package/ts/proxies/smart-proxy/route-orchestrator.ts +0 -297
- package/ts/proxies/smart-proxy/security-manager.ts +0 -269
- package/ts/proxies/smart-proxy/throughput-tracker.ts +0 -138
- package/ts/proxies/smart-proxy/timeout-manager.ts +0 -196
- package/ts/proxies/smart-proxy/tls-manager.ts +0 -171
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@push.rocks/smartproxy',
|
|
6
|
-
version: '22.
|
|
6
|
+
version: '22.6.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
|
}
|
package/ts/index.ts
CHANGED
|
@@ -5,15 +5,10 @@
|
|
|
5
5
|
// NFTables proxy exports
|
|
6
6
|
export * from './proxies/nftables-proxy/index.js';
|
|
7
7
|
|
|
8
|
-
// Export
|
|
9
|
-
export {
|
|
10
|
-
export type { IMetricsTracker, MetricsTracker } from './proxies/http-proxy/index.js';
|
|
11
|
-
export type { IHttpProxyOptions, ICertificateEntry, ILogger } from './proxies/http-proxy/models/types.js';
|
|
12
|
-
export { SharedRouteManager as HttpProxyRouteManager } from './core/routing/route-manager.js';
|
|
13
|
-
|
|
14
|
-
// Export SmartProxy elements selectively to avoid RouteManager ambiguity
|
|
15
|
-
export { SmartProxy, ConnectionManager, SecurityManager, TimeoutManager, TlsManager, HttpProxyBridge, RouteConnectionHandler, SmartCertManager } from './proxies/smart-proxy/index.js';
|
|
8
|
+
// Export SmartProxy elements
|
|
9
|
+
export { SmartProxy } from './proxies/smart-proxy/index.js';
|
|
16
10
|
export { SharedRouteManager as RouteManager } from './core/routing/route-manager.js';
|
|
11
|
+
|
|
17
12
|
// Export smart-proxy models
|
|
18
13
|
export type { ISmartProxyOptions, IConnectionRecord, IRouteConfig, IRouteMatch, IRouteAction, IRouteTls, IRouteContext } from './proxies/smart-proxy/models/index.js';
|
|
19
14
|
export type { TSmartProxyCertProvisionObject } from './proxies/smart-proxy/models/interfaces.js';
|
|
@@ -22,8 +17,6 @@ export * from './proxies/smart-proxy/utils/index.js';
|
|
|
22
17
|
// Original: export * from './smartproxy/classes.pp.snihandler.js'
|
|
23
18
|
// Now we export from the new module
|
|
24
19
|
export { SniHandler } from './tls/sni/sni-handler.js';
|
|
25
|
-
// Original: export * from './smartproxy/classes.pp.interfaces.js'
|
|
26
|
-
// Now we export from the new module (selectively to avoid conflicts)
|
|
27
20
|
|
|
28
21
|
// Core types and utilities
|
|
29
22
|
export * from './core/models/common-types.js';
|
|
@@ -32,8 +25,7 @@ export * from './core/models/common-types.js';
|
|
|
32
25
|
export type { IAcmeOptions } from './proxies/smart-proxy/models/interfaces.js';
|
|
33
26
|
|
|
34
27
|
// Modular exports for new architecture
|
|
35
|
-
// Certificate module has been removed - use SmartCertManager instead
|
|
36
28
|
export * as tls from './tls/index.js';
|
|
37
29
|
export * as routing from './routing/index.js';
|
|
38
30
|
export * as detection from './detection/index.js';
|
|
39
|
-
export * as protocols from './protocols/index.js';
|
|
31
|
+
export * as protocols from './protocols/index.js';
|
package/ts/proxies/index.ts
CHANGED
|
@@ -2,16 +2,8 @@
|
|
|
2
2
|
* Proxy implementations module
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
// Export HttpProxy with selective imports to avoid conflicts
|
|
6
|
-
export { HttpProxy, CertificateManager, ConnectionPool, RequestHandler, WebSocketHandler } from './http-proxy/index.js';
|
|
7
|
-
export type { IMetricsTracker, MetricsTracker } from './http-proxy/index.js';
|
|
8
|
-
// Export http-proxy models except IAcmeOptions
|
|
9
|
-
export type { IHttpProxyOptions, ICertificateEntry, ILogger } from './http-proxy/models/types.js';
|
|
10
|
-
// RouteManager has been unified - use SharedRouteManager from core/routing
|
|
11
|
-
export { SharedRouteManager as HttpProxyRouteManager } from '../core/routing/route-manager.js';
|
|
12
|
-
|
|
13
5
|
// Export SmartProxy with selective imports to avoid conflicts
|
|
14
|
-
export { SmartProxy
|
|
6
|
+
export { SmartProxy } from './smart-proxy/index.js';
|
|
15
7
|
export { SharedRouteManager as SmartProxyRouteManager } from '../core/routing/route-manager.js';
|
|
16
8
|
export * from './smart-proxy/utils/index.js';
|
|
17
9
|
// Export smart-proxy models except IAcmeOptions
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* SmartProxy implementation
|
|
3
3
|
*
|
|
4
|
-
* Version
|
|
4
|
+
* Version 23.0.0: Rust-backed proxy engine
|
|
5
5
|
*/
|
|
6
6
|
// Re-export models
|
|
7
7
|
export * from './models/index.js';
|
|
@@ -9,21 +9,14 @@ export * from './models/index.js';
|
|
|
9
9
|
// Export the main SmartProxy class
|
|
10
10
|
export { SmartProxy } from './smart-proxy.js';
|
|
11
11
|
|
|
12
|
-
// Export
|
|
13
|
-
export {
|
|
14
|
-
export {
|
|
15
|
-
export {
|
|
16
|
-
export {
|
|
17
|
-
export { HttpProxyBridge } from './http-proxy-bridge.js';
|
|
12
|
+
// Export Rust bridge and helpers
|
|
13
|
+
export { RustProxyBridge } from './rust-proxy-bridge.js';
|
|
14
|
+
export { RoutePreprocessor } from './route-preprocessor.js';
|
|
15
|
+
export { SocketHandlerServer } from './socket-handler-server.js';
|
|
16
|
+
export { RustMetricsAdapter } from './rust-metrics-adapter.js';
|
|
18
17
|
|
|
19
18
|
// Export route-based components
|
|
20
19
|
export { SharedRouteManager as RouteManager } from '../../core/routing/route-manager.js';
|
|
21
|
-
export { RouteConnectionHandler } from './route-connection-handler.js';
|
|
22
|
-
export { NFTablesManager } from './nftables-manager.js';
|
|
23
|
-
export { RouteOrchestrator } from './route-orchestrator.js';
|
|
24
|
-
|
|
25
|
-
// Export certificate management
|
|
26
|
-
export { SmartCertManager } from './certificate-manager.js';
|
|
27
20
|
|
|
28
21
|
// Export all helper functions from the utils directory
|
|
29
22
|
export * from './utils/index.js';
|
|
@@ -99,10 +99,6 @@ export interface ISmartProxyOptions {
|
|
|
99
99
|
keepAliveInactivityMultiplier?: number; // Multiplier for inactivity timeout for keep-alive connections
|
|
100
100
|
extendedKeepAliveLifetime?: number; // Extended lifetime for keep-alive connections (ms)
|
|
101
101
|
|
|
102
|
-
// HttpProxy integration
|
|
103
|
-
useHttpProxy?: number[]; // Array of ports to forward to HttpProxy
|
|
104
|
-
httpProxyPort?: number; // Port where HttpProxy is listening (default: 8443)
|
|
105
|
-
|
|
106
102
|
// Metrics configuration
|
|
107
103
|
metrics?: {
|
|
108
104
|
enabled?: boolean;
|
|
@@ -139,6 +135,12 @@ export interface ISmartProxyOptions {
|
|
|
139
135
|
* Default: true
|
|
140
136
|
*/
|
|
141
137
|
certProvisionFallbackToAcme?: boolean;
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Path to the RustProxy binary. If not set, the binary is located
|
|
141
|
+
* automatically via env var, platform package, local build, or PATH.
|
|
142
|
+
*/
|
|
143
|
+
rustBinaryPath?: string;
|
|
142
144
|
}
|
|
143
145
|
|
|
144
146
|
/**
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import type { IRouteConfig, IRouteAction, IRouteTarget } from './models/route-types.js';
|
|
2
|
+
import { logger } from '../../core/utils/logger.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Preprocesses routes before sending them to Rust.
|
|
6
|
+
*
|
|
7
|
+
* Strips non-serializable fields (functions, callbacks) and classifies
|
|
8
|
+
* routes that must be handled by TypeScript (socket-handler, dynamic host/port).
|
|
9
|
+
*/
|
|
10
|
+
export class RoutePreprocessor {
|
|
11
|
+
/**
|
|
12
|
+
* Map of route name/id → original route config (with JS functions preserved).
|
|
13
|
+
* Used by the socket handler server to look up the original handler.
|
|
14
|
+
*/
|
|
15
|
+
private originalRoutes = new Map<string, IRouteConfig>();
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Preprocess routes for the Rust binary.
|
|
19
|
+
*
|
|
20
|
+
* - Routes with `socketHandler` callbacks are marked as socket-handler type
|
|
21
|
+
* (Rust will relay these back to TS)
|
|
22
|
+
* - Routes with dynamic `host`/`port` functions are converted to socket-handler
|
|
23
|
+
* type (Rust relays, TS resolves the function)
|
|
24
|
+
* - Non-serializable fields are stripped
|
|
25
|
+
* - Original routes are preserved in the local map for handler lookup
|
|
26
|
+
*/
|
|
27
|
+
public preprocessForRust(routes: IRouteConfig[]): IRouteConfig[] {
|
|
28
|
+
this.originalRoutes.clear();
|
|
29
|
+
return routes.map((route, index) => this.preprocessRoute(route, index));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Get the original route config (with JS functions) by route name or id.
|
|
34
|
+
*/
|
|
35
|
+
public getOriginalRoute(routeKey: string): IRouteConfig | undefined {
|
|
36
|
+
return this.originalRoutes.get(routeKey);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Get all original routes that have socket handlers or dynamic functions.
|
|
41
|
+
*/
|
|
42
|
+
public getHandlerRoutes(): Map<string, IRouteConfig> {
|
|
43
|
+
return new Map(this.originalRoutes);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
private preprocessRoute(route: IRouteConfig, index: number): IRouteConfig {
|
|
47
|
+
const routeKey = route.name || route.id || `route_${index}`;
|
|
48
|
+
|
|
49
|
+
// Check if this route needs TS-side handling
|
|
50
|
+
const needsTsHandling = this.routeNeedsTsHandling(route);
|
|
51
|
+
|
|
52
|
+
if (needsTsHandling) {
|
|
53
|
+
// Store the original route for handler lookup
|
|
54
|
+
this.originalRoutes.set(routeKey, route);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Create a clean copy for Rust
|
|
58
|
+
const cleanRoute: IRouteConfig = {
|
|
59
|
+
...route,
|
|
60
|
+
action: this.cleanAction(route.action, routeKey, needsTsHandling),
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// Ensure we have a name for handler lookup
|
|
64
|
+
if (!cleanRoute.name && !cleanRoute.id) {
|
|
65
|
+
cleanRoute.name = routeKey;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return cleanRoute;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
private routeNeedsTsHandling(route: IRouteConfig): boolean {
|
|
72
|
+
// Socket handler routes always need TS
|
|
73
|
+
if (route.action.type === 'socket-handler' && route.action.socketHandler) {
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Routes with dynamic host/port functions need TS
|
|
78
|
+
if (route.action.targets) {
|
|
79
|
+
for (const target of route.action.targets) {
|
|
80
|
+
if (typeof target.host === 'function' || typeof target.port === 'function') {
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
private cleanAction(action: IRouteAction, routeKey: string, needsTsHandling: boolean): IRouteAction {
|
|
90
|
+
const cleanAction: IRouteAction = { ...action };
|
|
91
|
+
|
|
92
|
+
if (needsTsHandling) {
|
|
93
|
+
// Convert to socket-handler type for Rust (Rust will relay back to TS)
|
|
94
|
+
cleanAction.type = 'socket-handler';
|
|
95
|
+
// Remove the JS handler (not serializable)
|
|
96
|
+
delete (cleanAction as any).socketHandler;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Clean targets - replace functions with static values
|
|
100
|
+
if (cleanAction.targets) {
|
|
101
|
+
cleanAction.targets = cleanAction.targets.map(t => this.cleanTarget(t));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return cleanAction;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
private cleanTarget(target: IRouteTarget): IRouteTarget {
|
|
108
|
+
const clean: IRouteTarget = { ...target };
|
|
109
|
+
|
|
110
|
+
// Replace function host with placeholder
|
|
111
|
+
if (typeof clean.host === 'function') {
|
|
112
|
+
clean.host = 'localhost';
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Replace function port with placeholder
|
|
116
|
+
if (typeof clean.port === 'function') {
|
|
117
|
+
clean.port = 0;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return clean;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import * as plugins from '../../plugins.js';
|
|
2
|
+
import { logger } from '../../core/utils/logger.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Locates the RustProxy binary using a priority-ordered search strategy:
|
|
6
|
+
* 1. SMARTPROXY_RUST_BINARY environment variable
|
|
7
|
+
* 2. Platform-specific optional npm package
|
|
8
|
+
* 3. Local development build at ./rust/target/release/rustproxy
|
|
9
|
+
* 4. System PATH
|
|
10
|
+
*/
|
|
11
|
+
export class RustBinaryLocator {
|
|
12
|
+
private cachedPath: string | null = null;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Find the RustProxy binary path.
|
|
16
|
+
* Returns null if no binary is available.
|
|
17
|
+
*/
|
|
18
|
+
public async findBinary(): Promise<string | null> {
|
|
19
|
+
if (this.cachedPath !== null) {
|
|
20
|
+
return this.cachedPath;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const path = await this.searchBinary();
|
|
24
|
+
this.cachedPath = path;
|
|
25
|
+
return path;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Clear the cached binary path (e.g., after a failed launch).
|
|
30
|
+
*/
|
|
31
|
+
public clearCache(): void {
|
|
32
|
+
this.cachedPath = null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
private async searchBinary(): Promise<string | null> {
|
|
36
|
+
// 1. Environment variable override
|
|
37
|
+
const envPath = process.env.SMARTPROXY_RUST_BINARY;
|
|
38
|
+
if (envPath) {
|
|
39
|
+
if (await this.isExecutable(envPath)) {
|
|
40
|
+
logger.log('info', `RustProxy binary found via SMARTPROXY_RUST_BINARY: ${envPath}`, { component: 'rust-locator' });
|
|
41
|
+
return envPath;
|
|
42
|
+
}
|
|
43
|
+
logger.log('warn', `SMARTPROXY_RUST_BINARY set but not executable: ${envPath}`, { component: 'rust-locator' });
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// 2. Platform-specific optional npm package
|
|
47
|
+
const platformBinary = await this.findPlatformPackageBinary();
|
|
48
|
+
if (platformBinary) {
|
|
49
|
+
logger.log('info', `RustProxy binary found in platform package: ${platformBinary}`, { component: 'rust-locator' });
|
|
50
|
+
return platformBinary;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// 3. Local development build
|
|
54
|
+
const localPaths = [
|
|
55
|
+
plugins.path.resolve(process.cwd(), 'rust/target/release/rustproxy'),
|
|
56
|
+
plugins.path.resolve(process.cwd(), 'rust/target/debug/rustproxy'),
|
|
57
|
+
];
|
|
58
|
+
for (const localPath of localPaths) {
|
|
59
|
+
if (await this.isExecutable(localPath)) {
|
|
60
|
+
logger.log('info', `RustProxy binary found at local path: ${localPath}`, { component: 'rust-locator' });
|
|
61
|
+
return localPath;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// 4. System PATH
|
|
66
|
+
const systemPath = await this.findInPath('rustproxy');
|
|
67
|
+
if (systemPath) {
|
|
68
|
+
logger.log('info', `RustProxy binary found in system PATH: ${systemPath}`, { component: 'rust-locator' });
|
|
69
|
+
return systemPath;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
logger.log('error', 'No RustProxy binary found. Set SMARTPROXY_RUST_BINARY, install the platform package, or build with: cd rust && cargo build --release', { component: 'rust-locator' });
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
private async findPlatformPackageBinary(): Promise<string | null> {
|
|
77
|
+
const platform = process.platform;
|
|
78
|
+
const arch = process.arch;
|
|
79
|
+
const packageName = `@push.rocks/smartproxy-${platform}-${arch}`;
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
// Try to resolve the platform-specific package
|
|
83
|
+
const packagePath = require.resolve(`${packageName}/rustproxy`);
|
|
84
|
+
if (await this.isExecutable(packagePath)) {
|
|
85
|
+
return packagePath;
|
|
86
|
+
}
|
|
87
|
+
} catch {
|
|
88
|
+
// Package not installed - expected for development
|
|
89
|
+
}
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
private async isExecutable(filePath: string): Promise<boolean> {
|
|
94
|
+
try {
|
|
95
|
+
await plugins.fs.promises.access(filePath, plugins.fs.constants.X_OK);
|
|
96
|
+
return true;
|
|
97
|
+
} catch {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
private async findInPath(binaryName: string): Promise<string | null> {
|
|
103
|
+
const pathDirs = (process.env.PATH || '').split(plugins.path.delimiter);
|
|
104
|
+
for (const dir of pathDirs) {
|
|
105
|
+
const fullPath = plugins.path.join(dir, binaryName);
|
|
106
|
+
if (await this.isExecutable(fullPath)) {
|
|
107
|
+
return fullPath;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import type { IMetrics, IThroughputData, IThroughputHistoryPoint } from './models/metrics-types.js';
|
|
2
|
+
import type { RustProxyBridge } from './rust-proxy-bridge.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Adapts Rust JSON metrics to the IMetrics interface.
|
|
6
|
+
*
|
|
7
|
+
* Polls the Rust binary periodically via the bridge and caches the result.
|
|
8
|
+
* All IMetrics getters read from the cache synchronously.
|
|
9
|
+
*
|
|
10
|
+
* Rust Metrics JSON fields (camelCase via serde):
|
|
11
|
+
* activeConnections, totalConnections, bytesIn, bytesOut,
|
|
12
|
+
* throughputInBytesPerSec, throughputOutBytesPerSec,
|
|
13
|
+
* routes: { [routeName]: { activeConnections, totalConnections, bytesIn, bytesOut, ... } }
|
|
14
|
+
*/
|
|
15
|
+
export class RustMetricsAdapter implements IMetrics {
|
|
16
|
+
private bridge: RustProxyBridge;
|
|
17
|
+
private cache: any = null;
|
|
18
|
+
private pollTimer: ReturnType<typeof setInterval> | null = null;
|
|
19
|
+
private pollIntervalMs: number;
|
|
20
|
+
|
|
21
|
+
constructor(bridge: RustProxyBridge, pollIntervalMs = 1000) {
|
|
22
|
+
this.bridge = bridge;
|
|
23
|
+
this.pollIntervalMs = pollIntervalMs;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Poll Rust for metrics once. Can be awaited to ensure cache is fresh.
|
|
28
|
+
*/
|
|
29
|
+
public async poll(): Promise<void> {
|
|
30
|
+
try {
|
|
31
|
+
this.cache = await this.bridge.getMetrics();
|
|
32
|
+
} catch {
|
|
33
|
+
// Ignore poll errors (bridge may be shutting down)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public startPolling(): void {
|
|
38
|
+
if (this.pollTimer) return;
|
|
39
|
+
// Immediate first poll so cache is populated ASAP
|
|
40
|
+
this.poll();
|
|
41
|
+
this.pollTimer = setInterval(() => {
|
|
42
|
+
this.poll();
|
|
43
|
+
}, this.pollIntervalMs);
|
|
44
|
+
if (this.pollTimer.unref) {
|
|
45
|
+
this.pollTimer.unref();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
public stopPolling(): void {
|
|
50
|
+
if (this.pollTimer) {
|
|
51
|
+
clearInterval(this.pollTimer);
|
|
52
|
+
this.pollTimer = null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// --- IMetrics implementation ---
|
|
57
|
+
|
|
58
|
+
public connections = {
|
|
59
|
+
active: (): number => {
|
|
60
|
+
return this.cache?.activeConnections ?? 0;
|
|
61
|
+
},
|
|
62
|
+
total: (): number => {
|
|
63
|
+
return this.cache?.totalConnections ?? 0;
|
|
64
|
+
},
|
|
65
|
+
byRoute: (): Map<string, number> => {
|
|
66
|
+
const result = new Map<string, number>();
|
|
67
|
+
if (this.cache?.routes) {
|
|
68
|
+
for (const [name, rm] of Object.entries(this.cache.routes)) {
|
|
69
|
+
result.set(name, (rm as any).activeConnections ?? 0);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return result;
|
|
73
|
+
},
|
|
74
|
+
byIP: (): Map<string, number> => {
|
|
75
|
+
// Per-IP tracking not yet available from Rust
|
|
76
|
+
return new Map();
|
|
77
|
+
},
|
|
78
|
+
topIPs: (_limit?: number): Array<{ ip: string; count: number }> => {
|
|
79
|
+
// Per-IP tracking not yet available from Rust
|
|
80
|
+
return [];
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
public throughput = {
|
|
85
|
+
instant: (): IThroughputData => {
|
|
86
|
+
return {
|
|
87
|
+
in: this.cache?.throughputInBytesPerSec ?? 0,
|
|
88
|
+
out: this.cache?.throughputOutBytesPerSec ?? 0,
|
|
89
|
+
};
|
|
90
|
+
},
|
|
91
|
+
recent: (): IThroughputData => {
|
|
92
|
+
return this.throughput.instant();
|
|
93
|
+
},
|
|
94
|
+
average: (): IThroughputData => {
|
|
95
|
+
return this.throughput.instant();
|
|
96
|
+
},
|
|
97
|
+
custom: (_seconds: number): IThroughputData => {
|
|
98
|
+
return this.throughput.instant();
|
|
99
|
+
},
|
|
100
|
+
history: (_seconds: number): Array<IThroughputHistoryPoint> => {
|
|
101
|
+
// Throughput history not yet available from Rust
|
|
102
|
+
return [];
|
|
103
|
+
},
|
|
104
|
+
byRoute: (_windowSeconds?: number): Map<string, IThroughputData> => {
|
|
105
|
+
const result = new Map<string, IThroughputData>();
|
|
106
|
+
if (this.cache?.routes) {
|
|
107
|
+
for (const [name, rm] of Object.entries(this.cache.routes)) {
|
|
108
|
+
result.set(name, {
|
|
109
|
+
in: (rm as any).throughputInBytesPerSec ?? 0,
|
|
110
|
+
out: (rm as any).throughputOutBytesPerSec ?? 0,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return result;
|
|
115
|
+
},
|
|
116
|
+
byIP: (_windowSeconds?: number): Map<string, IThroughputData> => {
|
|
117
|
+
return new Map();
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
public requests = {
|
|
122
|
+
perSecond: (): number => {
|
|
123
|
+
// Rust tracks connections, not HTTP requests (TCP-level proxy)
|
|
124
|
+
return 0;
|
|
125
|
+
},
|
|
126
|
+
perMinute: (): number => {
|
|
127
|
+
return 0;
|
|
128
|
+
},
|
|
129
|
+
total: (): number => {
|
|
130
|
+
// Use total connections as a proxy for total requests
|
|
131
|
+
return this.cache?.totalConnections ?? 0;
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
public totals = {
|
|
136
|
+
bytesIn: (): number => {
|
|
137
|
+
return this.cache?.bytesIn ?? 0;
|
|
138
|
+
},
|
|
139
|
+
bytesOut: (): number => {
|
|
140
|
+
return this.cache?.bytesOut ?? 0;
|
|
141
|
+
},
|
|
142
|
+
connections: (): number => {
|
|
143
|
+
return this.cache?.totalConnections ?? 0;
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
public percentiles = {
|
|
148
|
+
connectionDuration: (): { p50: number; p95: number; p99: number } => {
|
|
149
|
+
return { p50: 0, p95: 0, p99: 0 };
|
|
150
|
+
},
|
|
151
|
+
bytesTransferred: (): {
|
|
152
|
+
in: { p50: number; p95: number; p99: number };
|
|
153
|
+
out: { p50: number; p95: number; p99: number };
|
|
154
|
+
} => {
|
|
155
|
+
return {
|
|
156
|
+
in: { p50: 0, p95: 0, p99: 0 },
|
|
157
|
+
out: { p50: 0, p95: 0, p99: 0 },
|
|
158
|
+
};
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
}
|