@push.rocks/smartproxy 22.4.2 → 23.0.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.
Files changed (101) hide show
  1. package/changelog.md +36 -0
  2. package/dist_rust/rustproxy +0 -0
  3. package/dist_ts/00_commitinfo_data.js +1 -1
  4. package/dist_ts/index.d.ts +1 -6
  5. package/dist_ts/index.js +3 -11
  6. package/dist_ts/protocols/common/fragment-handler.js +5 -1
  7. package/dist_ts/proxies/index.d.ts +1 -6
  8. package/dist_ts/proxies/index.js +2 -8
  9. package/dist_ts/proxies/smart-proxy/index.d.ts +5 -10
  10. package/dist_ts/proxies/smart-proxy/index.js +7 -13
  11. package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +5 -2
  12. package/dist_ts/proxies/smart-proxy/models/route-types.js +1 -1
  13. package/dist_ts/proxies/smart-proxy/route-preprocessor.d.ts +37 -0
  14. package/dist_ts/proxies/smart-proxy/route-preprocessor.js +103 -0
  15. package/dist_ts/proxies/smart-proxy/rust-binary-locator.d.ts +23 -0
  16. package/dist_ts/proxies/smart-proxy/rust-binary-locator.js +104 -0
  17. package/dist_ts/proxies/smart-proxy/rust-metrics-adapter.d.ts +74 -0
  18. package/dist_ts/proxies/smart-proxy/rust-metrics-adapter.js +146 -0
  19. package/dist_ts/proxies/smart-proxy/rust-proxy-bridge.d.ts +49 -0
  20. package/dist_ts/proxies/smart-proxy/rust-proxy-bridge.js +259 -0
  21. package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +39 -157
  22. package/dist_ts/proxies/smart-proxy/smart-proxy.js +224 -621
  23. package/dist_ts/proxies/smart-proxy/socket-handler-server.d.ts +45 -0
  24. package/dist_ts/proxies/smart-proxy/socket-handler-server.js +253 -0
  25. package/dist_ts/routing/index.d.ts +1 -1
  26. package/dist_ts/routing/index.js +3 -3
  27. package/dist_ts/routing/models/http-types.d.ts +119 -4
  28. package/dist_ts/routing/models/http-types.js +93 -5
  29. package/package.json +1 -1
  30. package/readme.md +444 -219
  31. package/ts/00_commitinfo_data.ts +1 -1
  32. package/ts/index.ts +4 -15
  33. package/ts/protocols/common/fragment-handler.ts +4 -0
  34. package/ts/proxies/index.ts +1 -12
  35. package/ts/proxies/smart-proxy/index.ts +6 -13
  36. package/ts/proxies/smart-proxy/models/interfaces.ts +6 -4
  37. package/ts/proxies/smart-proxy/models/route-types.ts +0 -2
  38. package/ts/proxies/smart-proxy/route-preprocessor.ts +122 -0
  39. package/ts/proxies/smart-proxy/rust-binary-locator.ts +112 -0
  40. package/ts/proxies/smart-proxy/rust-metrics-adapter.ts +161 -0
  41. package/ts/proxies/smart-proxy/rust-proxy-bridge.ts +310 -0
  42. package/ts/proxies/smart-proxy/smart-proxy.ts +282 -798
  43. package/ts/proxies/smart-proxy/socket-handler-server.ts +279 -0
  44. package/ts/routing/index.ts +2 -2
  45. package/ts/routing/models/http-types.ts +147 -4
  46. package/dist_ts/proxies/nftables-proxy/index.d.ts +0 -6
  47. package/dist_ts/proxies/nftables-proxy/index.js +0 -7
  48. package/dist_ts/proxies/nftables-proxy/models/errors.d.ts +0 -15
  49. package/dist_ts/proxies/nftables-proxy/models/errors.js +0 -28
  50. package/dist_ts/proxies/nftables-proxy/models/index.d.ts +0 -5
  51. package/dist_ts/proxies/nftables-proxy/models/index.js +0 -6
  52. package/dist_ts/proxies/nftables-proxy/models/interfaces.d.ts +0 -75
  53. package/dist_ts/proxies/nftables-proxy/models/interfaces.js +0 -5
  54. package/dist_ts/proxies/nftables-proxy/nftables-proxy.d.ts +0 -124
  55. package/dist_ts/proxies/nftables-proxy/nftables-proxy.js +0 -1374
  56. package/dist_ts/proxies/nftables-proxy/utils/index.d.ts +0 -9
  57. package/dist_ts/proxies/nftables-proxy/utils/index.js +0 -12
  58. package/dist_ts/proxies/nftables-proxy/utils/nft-command-executor.d.ts +0 -66
  59. package/dist_ts/proxies/nftables-proxy/utils/nft-command-executor.js +0 -131
  60. package/dist_ts/proxies/nftables-proxy/utils/nft-port-spec-normalizer.d.ts +0 -39
  61. package/dist_ts/proxies/nftables-proxy/utils/nft-port-spec-normalizer.js +0 -112
  62. package/dist_ts/proxies/nftables-proxy/utils/nft-rule-validator.d.ts +0 -59
  63. package/dist_ts/proxies/nftables-proxy/utils/nft-rule-validator.js +0 -130
  64. package/ts/proxies/http-proxy/connection-pool.ts +0 -228
  65. package/ts/proxies/http-proxy/context-creator.ts +0 -145
  66. package/ts/proxies/http-proxy/default-certificates.ts +0 -150
  67. package/ts/proxies/http-proxy/function-cache.ts +0 -279
  68. package/ts/proxies/http-proxy/handlers/index.ts +0 -5
  69. package/ts/proxies/http-proxy/http-proxy.ts +0 -669
  70. package/ts/proxies/http-proxy/http-request-handler.ts +0 -331
  71. package/ts/proxies/http-proxy/http2-request-handler.ts +0 -255
  72. package/ts/proxies/http-proxy/index.ts +0 -18
  73. package/ts/proxies/http-proxy/models/http-types.ts +0 -148
  74. package/ts/proxies/http-proxy/models/index.ts +0 -5
  75. package/ts/proxies/http-proxy/models/types.ts +0 -125
  76. package/ts/proxies/http-proxy/request-handler.ts +0 -878
  77. package/ts/proxies/http-proxy/security-manager.ts +0 -413
  78. package/ts/proxies/http-proxy/websocket-handler.ts +0 -581
  79. package/ts/proxies/nftables-proxy/index.ts +0 -6
  80. package/ts/proxies/nftables-proxy/models/errors.ts +0 -30
  81. package/ts/proxies/nftables-proxy/models/index.ts +0 -5
  82. package/ts/proxies/nftables-proxy/models/interfaces.ts +0 -94
  83. package/ts/proxies/nftables-proxy/nftables-proxy.ts +0 -1754
  84. package/ts/proxies/nftables-proxy/utils/index.ts +0 -38
  85. package/ts/proxies/nftables-proxy/utils/nft-command-executor.ts +0 -162
  86. package/ts/proxies/nftables-proxy/utils/nft-port-spec-normalizer.ts +0 -125
  87. package/ts/proxies/nftables-proxy/utils/nft-rule-validator.ts +0 -156
  88. package/ts/proxies/smart-proxy/acme-state-manager.ts +0 -112
  89. package/ts/proxies/smart-proxy/cert-store.ts +0 -92
  90. package/ts/proxies/smart-proxy/certificate-manager.ts +0 -895
  91. package/ts/proxies/smart-proxy/connection-manager.ts +0 -809
  92. package/ts/proxies/smart-proxy/http-proxy-bridge.ts +0 -213
  93. package/ts/proxies/smart-proxy/metrics-collector.ts +0 -453
  94. package/ts/proxies/smart-proxy/nftables-manager.ts +0 -271
  95. package/ts/proxies/smart-proxy/port-manager.ts +0 -358
  96. package/ts/proxies/smart-proxy/route-connection-handler.ts +0 -1712
  97. package/ts/proxies/smart-proxy/route-orchestrator.ts +0 -297
  98. package/ts/proxies/smart-proxy/security-manager.ts +0 -269
  99. package/ts/proxies/smart-proxy/throughput-tracker.ts +0 -138
  100. package/ts/proxies/smart-proxy/timeout-manager.ts +0 -196
  101. package/ts/proxies/smart-proxy/tls-manager.ts +0 -171
@@ -0,0 +1,310 @@
1
+ import * as plugins from '../../plugins.js';
2
+ import { logger } from '../../core/utils/logger.js';
3
+ import { RustBinaryLocator } from './rust-binary-locator.js';
4
+ import type { IRouteConfig } from './models/route-types.js';
5
+ import { ChildProcess, spawn } from 'child_process';
6
+ import { createInterface, Interface as ReadlineInterface } from 'readline';
7
+
8
+ /**
9
+ * Management request sent to the Rust binary via stdin.
10
+ */
11
+ interface IManagementRequest {
12
+ id: string;
13
+ method: string;
14
+ params: Record<string, any>;
15
+ }
16
+
17
+ /**
18
+ * Management response received from the Rust binary via stdout.
19
+ */
20
+ interface IManagementResponse {
21
+ id: string;
22
+ success: boolean;
23
+ result?: any;
24
+ error?: string;
25
+ }
26
+
27
+ /**
28
+ * Management event received from the Rust binary (unsolicited).
29
+ */
30
+ interface IManagementEvent {
31
+ event: string;
32
+ data: any;
33
+ }
34
+
35
+ /**
36
+ * Bridge between TypeScript SmartProxy and the Rust binary.
37
+ * Communicates via JSON-over-stdin/stdout IPC protocol.
38
+ */
39
+ export class RustProxyBridge extends plugins.EventEmitter {
40
+ private locator = new RustBinaryLocator();
41
+ private process: ChildProcess | null = null;
42
+ private readline: ReadlineInterface | null = null;
43
+ private pendingRequests = new Map<string, {
44
+ resolve: (value: any) => void;
45
+ reject: (error: Error) => void;
46
+ timer: NodeJS.Timeout;
47
+ }>();
48
+ private requestCounter = 0;
49
+ private isRunning = false;
50
+ private binaryPath: string | null = null;
51
+ private readonly requestTimeoutMs = 30000;
52
+
53
+ /**
54
+ * Spawn the Rust binary in management mode.
55
+ * Returns true if the binary was found and spawned successfully.
56
+ */
57
+ public async spawn(): Promise<boolean> {
58
+ this.binaryPath = await this.locator.findBinary();
59
+ if (!this.binaryPath) {
60
+ return false;
61
+ }
62
+
63
+ return new Promise<boolean>((resolve) => {
64
+ try {
65
+ this.process = spawn(this.binaryPath!, ['--management'], {
66
+ stdio: ['pipe', 'pipe', 'pipe'],
67
+ env: { ...process.env },
68
+ });
69
+
70
+ // Handle stderr (logging from Rust goes here)
71
+ const stderrHandler = (data: Buffer) => {
72
+ const lines = data.toString().split('\n').filter(l => l.trim());
73
+ for (const line of lines) {
74
+ logger.log('debug', `[rustproxy] ${line}`, { component: 'rust-bridge' });
75
+ }
76
+ };
77
+ this.process.stderr?.on('data', stderrHandler);
78
+
79
+ // Handle stdout (JSON IPC)
80
+ this.readline = createInterface({ input: this.process.stdout! });
81
+ this.readline.on('line', (line: string) => {
82
+ this.handleLine(line.trim());
83
+ });
84
+
85
+ // Handle process exit
86
+ this.process.on('exit', (code, signal) => {
87
+ logger.log('info', `RustProxy process exited (code=${code}, signal=${signal})`, { component: 'rust-bridge' });
88
+ this.cleanup();
89
+ this.emit('exit', code, signal);
90
+ });
91
+
92
+ this.process.on('error', (err) => {
93
+ logger.log('error', `RustProxy process error: ${err.message}`, { component: 'rust-bridge' });
94
+ this.cleanup();
95
+ resolve(false);
96
+ });
97
+
98
+ // Wait for the 'ready' event from Rust
99
+ const readyTimeout = setTimeout(() => {
100
+ logger.log('error', 'RustProxy did not send ready event within 10s', { component: 'rust-bridge' });
101
+ this.kill();
102
+ resolve(false);
103
+ }, 10000);
104
+
105
+ this.once('management:ready', () => {
106
+ clearTimeout(readyTimeout);
107
+ this.isRunning = true;
108
+ logger.log('info', 'RustProxy bridge connected', { component: 'rust-bridge' });
109
+ resolve(true);
110
+ });
111
+ } catch (err: any) {
112
+ logger.log('error', `Failed to spawn RustProxy: ${err.message}`, { component: 'rust-bridge' });
113
+ resolve(false);
114
+ }
115
+ });
116
+ }
117
+
118
+ /**
119
+ * Send a management command to the Rust process and wait for the response.
120
+ */
121
+ public async sendCommand(method: string, params: Record<string, any> = {}): Promise<any> {
122
+ if (!this.process || !this.isRunning) {
123
+ throw new Error('RustProxy bridge is not running');
124
+ }
125
+
126
+ const id = `req_${++this.requestCounter}`;
127
+ const request: IManagementRequest = { id, method, params };
128
+
129
+ return new Promise<any>((resolve, reject) => {
130
+ const timer = setTimeout(() => {
131
+ this.pendingRequests.delete(id);
132
+ reject(new Error(`RustProxy command '${method}' timed out after ${this.requestTimeoutMs}ms`));
133
+ }, this.requestTimeoutMs);
134
+
135
+ this.pendingRequests.set(id, { resolve, reject, timer });
136
+
137
+ const json = JSON.stringify(request) + '\n';
138
+ this.process!.stdin!.write(json, (err) => {
139
+ if (err) {
140
+ clearTimeout(timer);
141
+ this.pendingRequests.delete(id);
142
+ reject(new Error(`Failed to write to RustProxy stdin: ${err.message}`));
143
+ }
144
+ });
145
+ });
146
+ }
147
+
148
+ // Convenience methods for each management command
149
+
150
+ public async startProxy(config: any): Promise<void> {
151
+ await this.sendCommand('start', { config });
152
+ }
153
+
154
+ public async stopProxy(): Promise<void> {
155
+ await this.sendCommand('stop');
156
+ }
157
+
158
+ public async updateRoutes(routes: IRouteConfig[]): Promise<void> {
159
+ await this.sendCommand('updateRoutes', { routes });
160
+ }
161
+
162
+ public async getMetrics(): Promise<any> {
163
+ return this.sendCommand('getMetrics');
164
+ }
165
+
166
+ public async getStatistics(): Promise<any> {
167
+ return this.sendCommand('getStatistics');
168
+ }
169
+
170
+ public async provisionCertificate(routeName: string): Promise<void> {
171
+ await this.sendCommand('provisionCertificate', { routeName });
172
+ }
173
+
174
+ public async renewCertificate(routeName: string): Promise<void> {
175
+ await this.sendCommand('renewCertificate', { routeName });
176
+ }
177
+
178
+ public async getCertificateStatus(routeName: string): Promise<any> {
179
+ return this.sendCommand('getCertificateStatus', { routeName });
180
+ }
181
+
182
+ public async getListeningPorts(): Promise<number[]> {
183
+ const result = await this.sendCommand('getListeningPorts');
184
+ return result?.ports ?? [];
185
+ }
186
+
187
+ public async getNftablesStatus(): Promise<any> {
188
+ return this.sendCommand('getNftablesStatus');
189
+ }
190
+
191
+ public async setSocketHandlerRelay(socketPath: string): Promise<void> {
192
+ await this.sendCommand('setSocketHandlerRelay', { socketPath });
193
+ }
194
+
195
+ public async addListeningPort(port: number): Promise<void> {
196
+ await this.sendCommand('addListeningPort', { port });
197
+ }
198
+
199
+ public async removeListeningPort(port: number): Promise<void> {
200
+ await this.sendCommand('removeListeningPort', { port });
201
+ }
202
+
203
+ public async loadCertificate(domain: string, cert: string, key: string, ca?: string): Promise<void> {
204
+ await this.sendCommand('loadCertificate', { domain, cert, key, ca });
205
+ }
206
+
207
+ /**
208
+ * Kill the Rust process and clean up all stdio streams.
209
+ */
210
+ public kill(): void {
211
+ if (this.process) {
212
+ const proc = this.process;
213
+ this.process = null;
214
+ this.isRunning = false;
215
+
216
+ // Close readline (reads from stdout)
217
+ if (this.readline) {
218
+ this.readline.close();
219
+ this.readline = null;
220
+ }
221
+
222
+ // Reject pending requests
223
+ for (const [, pending] of this.pendingRequests) {
224
+ clearTimeout(pending.timer);
225
+ pending.reject(new Error('RustProxy process killed'));
226
+ }
227
+ this.pendingRequests.clear();
228
+
229
+ // Remove all listeners so nothing keeps references
230
+ proc.removeAllListeners();
231
+ proc.stdout?.removeAllListeners();
232
+ proc.stderr?.removeAllListeners();
233
+ proc.stdin?.removeAllListeners();
234
+
235
+ // Kill the process
236
+ try { proc.kill('SIGTERM'); } catch { /* already dead */ }
237
+
238
+ // Destroy all stdio pipes to free handles
239
+ try { proc.stdin?.destroy(); } catch { /* ignore */ }
240
+ try { proc.stdout?.destroy(); } catch { /* ignore */ }
241
+ try { proc.stderr?.destroy(); } catch { /* ignore */ }
242
+
243
+ // Unref process so Node doesn't wait for it
244
+ try { proc.unref(); } catch { /* ignore */ }
245
+
246
+ // Force kill after 5 seconds
247
+ setTimeout(() => {
248
+ try { proc.kill('SIGKILL'); } catch { /* already dead */ }
249
+ }, 5000).unref();
250
+ }
251
+ }
252
+
253
+ /**
254
+ * Whether the bridge is currently running.
255
+ */
256
+ public get running(): boolean {
257
+ return this.isRunning;
258
+ }
259
+
260
+ private handleLine(line: string): void {
261
+ if (!line) return;
262
+
263
+ let parsed: any;
264
+ try {
265
+ parsed = JSON.parse(line);
266
+ } catch {
267
+ logger.log('warn', `Non-JSON output from RustProxy: ${line}`, { component: 'rust-bridge' });
268
+ return;
269
+ }
270
+
271
+ // Check if it's an event (has 'event' field)
272
+ if ('event' in parsed) {
273
+ const event = parsed as IManagementEvent;
274
+ this.emit(`management:${event.event}`, event.data);
275
+ return;
276
+ }
277
+
278
+ // Otherwise it's a response (has 'id' field)
279
+ if ('id' in parsed) {
280
+ const response = parsed as IManagementResponse;
281
+ const pending = this.pendingRequests.get(response.id);
282
+ if (pending) {
283
+ clearTimeout(pending.timer);
284
+ this.pendingRequests.delete(response.id);
285
+ if (response.success) {
286
+ pending.resolve(response.result);
287
+ } else {
288
+ pending.reject(new Error(response.error || 'Unknown error from RustProxy'));
289
+ }
290
+ }
291
+ }
292
+ }
293
+
294
+ private cleanup(): void {
295
+ this.isRunning = false;
296
+ this.process = null;
297
+
298
+ if (this.readline) {
299
+ this.readline.close();
300
+ this.readline = null;
301
+ }
302
+
303
+ // Reject all pending requests
304
+ for (const [id, pending] of this.pendingRequests) {
305
+ clearTimeout(pending.timer);
306
+ pending.reject(new Error('RustProxy process exited'));
307
+ }
308
+ this.pendingRequests.clear();
309
+ }
310
+ }