@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
@@ -1,746 +1,349 @@
1
1
  import * as plugins from '../../plugins.js';
2
2
  import { logger } from '../../core/utils/logger.js';
3
- import { connectionLogDeduplicator } from '../../core/utils/log-deduplicator.js';
4
- // Importing required components
5
- import { ConnectionManager } from './connection-manager.js';
6
- import { SecurityManager } from './security-manager.js';
7
- import { TlsManager } from './tls-manager.js';
8
- import { HttpProxyBridge } from './http-proxy-bridge.js';
9
- import { TimeoutManager } from './timeout-manager.js';
10
- import { PortManager } from './port-manager.js';
3
+ // Rust bridge and helpers
4
+ import { RustProxyBridge } from './rust-proxy-bridge.js';
5
+ import { RustBinaryLocator } from './rust-binary-locator.js';
6
+ import { RoutePreprocessor } from './route-preprocessor.js';
7
+ import { SocketHandlerServer } from './socket-handler-server.js';
8
+ import { RustMetricsAdapter } from './rust-metrics-adapter.js';
9
+ // Route management
11
10
  import { SharedRouteManager as RouteManager } from '../../core/routing/route-manager.js';
12
- import { RouteConnectionHandler } from './route-connection-handler.js';
13
- import { NFTablesManager } from './nftables-manager.js';
14
- // Certificate manager
15
- import { SmartCertManager } from './certificate-manager.js';
16
- // Import mutex for route update synchronization
17
- import { Mutex } from './utils/mutex.js';
18
- // Import route validator
19
11
  import { RouteValidator } from './utils/route-validator.js';
20
- // Import route orchestrator for route management
21
- import { RouteOrchestrator } from './route-orchestrator.js';
22
- // Import ACME state manager
23
- import { AcmeStateManager } from './acme-state-manager.js';
24
- // Import metrics collector
25
- import { MetricsCollector } from './metrics-collector.js';
12
+ import { Mutex } from './utils/mutex.js';
26
13
  /**
27
- * SmartProxy - Pure route-based API
14
+ * SmartProxy - Rust-backed proxy engine with TypeScript configuration API.
28
15
  *
29
- * SmartProxy is a unified proxy system that works with routes to define connection handling behavior.
30
- * Each route contains matching criteria (ports, domains, etc.) and an action to take (forward, redirect, block).
31
- *
32
- * Configuration is provided through a set of routes, with each route defining:
33
- * - What to match (ports, domains, paths, client IPs)
34
- * - What to do with matching traffic (forward, redirect, block)
35
- * - How to handle TLS (passthrough, terminate, terminate-and-reencrypt)
36
- * - Security settings (IP restrictions, connection limits)
37
- * - Advanced options (timeout, headers, etc.)
16
+ * All networking (TCP, TLS, HTTP reverse proxy, connection management, security,
17
+ * NFTables) is handled by the Rust binary. TypeScript is only:
18
+ * - The npm module interface (types, route helpers)
19
+ * - The thin IPC wrapper (this class)
20
+ * - Socket-handler callback relay (for JS-defined handlers)
21
+ * - Certificate provisioning callbacks (certProvisionFunction)
38
22
  */
39
23
  export class SmartProxy extends plugins.EventEmitter {
40
- /**
41
- * Constructor for SmartProxy
42
- *
43
- * @param settingsArg Configuration options containing routes and other settings
44
- * Routes define how traffic is matched and handled, with each route having:
45
- * - match: criteria for matching traffic (ports, domains, paths, IPs)
46
- * - action: what to do with matched traffic (forward, redirect, block)
47
- *
48
- * Example:
49
- * ```ts
50
- * const proxy = new SmartProxy({
51
- * routes: [
52
- * {
53
- * match: {
54
- * ports: 443,
55
- * domains: ['example.com', '*.example.com']
56
- * },
57
- * action: {
58
- * type: 'forward',
59
- * target: { host: '10.0.0.1', port: 8443 },
60
- * tls: { mode: 'passthrough' }
61
- * }
62
- * }
63
- * ],
64
- * defaults: {
65
- * target: { host: 'localhost', port: 8080 },
66
- * security: { ipAllowList: ['*'] }
67
- * }
68
- * });
69
- * ```
70
- */
71
24
  constructor(settingsArg) {
72
25
  super();
73
- this.connectionLogger = null;
74
- this.isShuttingDown = false;
75
- // Certificate manager for ACME and static certificates
76
- this.certManager = null;
77
- // Global challenge route tracking
78
- this.globalChallengeRouteActive = false;
79
- // Track port usage across route updates
80
- this.portUsageMap = new Map();
81
- // Set reasonable defaults for all settings
26
+ this.socketHandlerServer = null;
27
+ this.stopping = false;
28
+ // Apply defaults
82
29
  this.settings = {
83
30
  ...settingsArg,
84
31
  initialDataTimeout: settingsArg.initialDataTimeout || 120000,
85
32
  socketTimeout: settingsArg.socketTimeout || 3600000,
86
- inactivityCheckInterval: settingsArg.inactivityCheckInterval || 60000,
87
33
  maxConnectionLifetime: settingsArg.maxConnectionLifetime || 86400000,
88
34
  inactivityTimeout: settingsArg.inactivityTimeout || 14400000,
89
35
  gracefulShutdownTimeout: settingsArg.gracefulShutdownTimeout || 30000,
90
- noDelay: settingsArg.noDelay !== undefined ? settingsArg.noDelay : true,
91
- keepAlive: settingsArg.keepAlive !== undefined ? settingsArg.keepAlive : true,
92
- keepAliveInitialDelay: settingsArg.keepAliveInitialDelay || 10000,
93
- maxPendingDataSize: settingsArg.maxPendingDataSize || 10 * 1024 * 1024,
94
- disableInactivityCheck: settingsArg.disableInactivityCheck || false,
95
- enableKeepAliveProbes: settingsArg.enableKeepAliveProbes !== undefined ? settingsArg.enableKeepAliveProbes : true,
96
- enableDetailedLogging: settingsArg.enableDetailedLogging || false,
97
- enableTlsDebugLogging: settingsArg.enableTlsDebugLogging || false,
98
- enableRandomizedTimeouts: settingsArg.enableRandomizedTimeouts || false,
99
36
  maxConnectionsPerIP: settingsArg.maxConnectionsPerIP || 100,
100
37
  connectionRateLimitPerMinute: settingsArg.connectionRateLimitPerMinute || 300,
101
38
  keepAliveTreatment: settingsArg.keepAliveTreatment || 'extended',
102
39
  keepAliveInactivityMultiplier: settingsArg.keepAliveInactivityMultiplier || 6,
103
40
  extendedKeepAliveLifetime: settingsArg.extendedKeepAliveLifetime || 7 * 24 * 60 * 60 * 1000,
104
- httpProxyPort: settingsArg.httpProxyPort || 8443,
105
41
  };
106
- // Normalize ACME options if provided (support both email and accountEmail)
42
+ // Normalize ACME options
107
43
  if (this.settings.acme) {
108
- // Support both 'email' and 'accountEmail' fields
109
44
  if (this.settings.acme.accountEmail && !this.settings.acme.email) {
110
45
  this.settings.acme.email = this.settings.acme.accountEmail;
111
46
  }
112
- // Set reasonable defaults for commonly used fields
113
47
  this.settings.acme = {
114
- enabled: this.settings.acme.enabled !== false, // Enable by default if acme object exists
48
+ enabled: this.settings.acme.enabled !== false,
115
49
  port: this.settings.acme.port || 80,
116
50
  email: this.settings.acme.email,
117
51
  useProduction: this.settings.acme.useProduction || false,
118
52
  renewThresholdDays: this.settings.acme.renewThresholdDays || 30,
119
- autoRenew: this.settings.acme.autoRenew !== false, // Enable by default
53
+ autoRenew: this.settings.acme.autoRenew !== false,
120
54
  certificateStore: this.settings.acme.certificateStore || './certs',
121
55
  skipConfiguredCerts: this.settings.acme.skipConfiguredCerts || false,
122
56
  renewCheckIntervalHours: this.settings.acme.renewCheckIntervalHours || 24,
123
57
  routeForwards: this.settings.acme.routeForwards || [],
124
- ...this.settings.acme // Preserve any additional fields
58
+ ...this.settings.acme,
125
59
  };
126
60
  }
127
- // Initialize component managers
128
- this.timeoutManager = new TimeoutManager(this);
129
- this.securityManager = new SecurityManager(this);
130
- this.connectionManager = new ConnectionManager(this);
131
- // Create the route manager with SharedRouteManager API
132
- // Create a logger adapter to match ILogger interface
133
- const loggerAdapter = {
134
- debug: (message, data) => logger.log('debug', message, data),
135
- info: (message, data) => logger.log('info', message, data),
136
- warn: (message, data) => logger.log('warn', message, data),
137
- error: (message, data) => logger.log('error', message, data)
138
- };
139
- // Validate initial routes
140
- if (this.settings.routes && this.settings.routes.length > 0) {
61
+ // Validate routes
62
+ if (this.settings.routes?.length) {
141
63
  const validation = RouteValidator.validateRoutes(this.settings.routes);
142
64
  if (!validation.valid) {
143
65
  RouteValidator.logValidationErrors(validation.errors);
144
66
  throw new Error(`Initial route validation failed: ${validation.errors.size} route(s) have errors`);
145
67
  }
146
68
  }
69
+ // Create logger adapter
70
+ const loggerAdapter = {
71
+ debug: (message, data) => logger.log('debug', message, data),
72
+ info: (message, data) => logger.log('info', message, data),
73
+ warn: (message, data) => logger.log('warn', message, data),
74
+ error: (message, data) => logger.log('error', message, data),
75
+ };
76
+ // Initialize components
147
77
  this.routeManager = new RouteManager({
148
78
  logger: loggerAdapter,
149
79
  enableDetailedLogging: this.settings.enableDetailedLogging,
150
- routes: this.settings.routes
80
+ routes: this.settings.routes,
151
81
  });
152
- // Create other required components
153
- this.tlsManager = new TlsManager(this);
154
- this.httpProxyBridge = new HttpProxyBridge(this);
155
- // Initialize connection handler with route support
156
- this.routeConnectionHandler = new RouteConnectionHandler(this);
157
- // Initialize port manager
158
- this.portManager = new PortManager(this);
159
- // Initialize NFTablesManager
160
- this.nftablesManager = new NFTablesManager(this);
161
- // Initialize route update mutex for synchronization
82
+ this.bridge = new RustProxyBridge();
83
+ this.preprocessor = new RoutePreprocessor();
84
+ this.metricsAdapter = new RustMetricsAdapter(this.bridge, this.settings.metrics?.sampleIntervalMs ?? 1000);
162
85
  this.routeUpdateLock = new Mutex();
163
- // Initialize ACME state manager
164
- this.acmeStateManager = new AcmeStateManager();
165
- // Initialize metrics collector with reference to this SmartProxy instance
166
- this.metricsCollector = new MetricsCollector(this, {
167
- sampleIntervalMs: this.settings.metrics?.sampleIntervalMs,
168
- retentionSeconds: this.settings.metrics?.retentionSeconds
169
- });
170
- // Initialize route orchestrator for managing route updates
171
- this.routeOrchestrator = new RouteOrchestrator(this.portManager, this.routeManager, this.httpProxyBridge, this.nftablesManager, null, // certManager will be set later
172
- loggerAdapter);
173
- }
174
- /**
175
- * Helper method to create and configure certificate manager
176
- * This ensures consistent setup including the required ACME callback
177
- */
178
- async createCertificateManager(routes, certStore = './certs', acmeOptions, initialState) {
179
- const certManager = new SmartCertManager(routes, certStore, acmeOptions, initialState);
180
- // Always set up the route update callback for ACME challenges
181
- certManager.setUpdateRoutesCallback(async (routes) => {
182
- await this.updateRoutes(routes);
183
- });
184
- // Connect with HttpProxy if available
185
- if (this.httpProxyBridge.getHttpProxy()) {
186
- certManager.setHttpProxy(this.httpProxyBridge.getHttpProxy());
187
- }
188
- // Set the ACME state manager
189
- certManager.setAcmeStateManager(this.acmeStateManager);
190
- // Pass down the global ACME config if available
191
- if (this.settings.acme) {
192
- certManager.setGlobalAcmeDefaults(this.settings.acme);
193
- }
194
- // Pass down the custom certificate provision function if available
195
- if (this.settings.certProvisionFunction) {
196
- certManager.setCertProvisionFunction(this.settings.certProvisionFunction);
197
- }
198
- // Pass down the fallback to ACME setting
199
- if (this.settings.certProvisionFallbackToAcme !== undefined) {
200
- certManager.setCertProvisionFallbackToAcme(this.settings.certProvisionFallbackToAcme);
201
- }
202
- await certManager.initialize();
203
- return certManager;
204
- }
205
- /**
206
- * Initialize certificate manager
207
- */
208
- async initializeCertificateManager() {
209
- // Extract global ACME options if any routes use auto certificates
210
- const autoRoutes = this.settings.routes.filter(r => r.action.tls?.certificate === 'auto');
211
- if (autoRoutes.length === 0 && !this.hasStaticCertRoutes()) {
212
- logger.log('info', 'No routes require certificate management', { component: 'certificate-manager' });
213
- return;
214
- }
215
- // Prepare ACME options with priority:
216
- // 1. Use top-level ACME config if available
217
- // 2. Fall back to first auto route's ACME config
218
- // 3. Otherwise use undefined
219
- let acmeOptions;
220
- if (this.settings.acme?.email) {
221
- // Use top-level ACME config
222
- acmeOptions = {
223
- email: this.settings.acme.email,
224
- useProduction: this.settings.acme.useProduction || false,
225
- port: this.settings.acme.port || 80
226
- };
227
- logger.log('info', `Using top-level ACME configuration with email: ${acmeOptions.email}`, { component: 'certificate-manager' });
228
- }
229
- else if (autoRoutes.length > 0) {
230
- // Check for route-level ACME config
231
- const routeWithAcme = autoRoutes.find(r => r.action.tls?.acme?.email);
232
- if (routeWithAcme?.action.tls?.acme) {
233
- const routeAcme = routeWithAcme.action.tls.acme;
234
- acmeOptions = {
235
- email: routeAcme.email,
236
- useProduction: routeAcme.useProduction || false,
237
- port: routeAcme.challengePort || 80
238
- };
239
- logger.log('info', `Using route-level ACME configuration from route '${routeWithAcme.name}' with email: ${acmeOptions.email}`, { component: 'certificate-manager' });
240
- }
241
- }
242
- // Validate we have required configuration
243
- if (autoRoutes.length > 0 && !acmeOptions?.email) {
244
- throw new Error('ACME email is required for automatic certificate provisioning. ' +
245
- 'Please provide email in either:\n' +
246
- '1. Top-level "acme" configuration\n' +
247
- '2. Individual route\'s "tls.acme" configuration');
248
- }
249
- // Use the helper method to create and configure the certificate manager
250
- this.certManager = await this.createCertificateManager(this.settings.routes, this.settings.acme?.certificateStore || './certs', acmeOptions);
251
- }
252
- /**
253
- * Check if we have routes with static certificates
254
- */
255
- hasStaticCertRoutes() {
256
- return this.settings.routes.some(r => r.action.tls?.certificate &&
257
- r.action.tls.certificate !== 'auto');
258
86
  }
259
87
  /**
260
- * Start the proxy server with support for both configuration types
88
+ * Start the proxy.
89
+ * Spawns the Rust binary, configures socket relay if needed, sends routes, handles cert provisioning.
261
90
  */
262
91
  async start() {
263
- // Don't start if already shutting down
264
- if (this.isShuttingDown) {
265
- logger.log('warn', "Cannot start SmartProxy while it's in the shutdown process");
266
- return;
267
- }
268
- // Validate the route configuration
269
- const configWarnings = this.routeManager.validateConfiguration();
270
- // Also validate ACME configuration
271
- const acmeWarnings = this.validateAcmeConfiguration();
272
- const allWarnings = [...configWarnings, ...acmeWarnings];
273
- if (allWarnings.length > 0) {
274
- logger.log('warn', `${allWarnings.length} configuration warnings found`, { count: allWarnings.length });
275
- for (const warning of allWarnings) {
276
- logger.log('warn', `${warning}`);
277
- }
278
- }
279
- // Get listening ports from RouteManager
280
- const listeningPorts = this.routeManager.getListeningPorts();
281
- // Initialize port usage tracking using RouteOrchestrator
282
- this.portUsageMap = this.routeOrchestrator.updatePortUsageMap(this.settings.routes);
283
- // Log port usage for startup
284
- logger.log('info', `SmartProxy starting with ${listeningPorts.length} ports: ${listeningPorts.join(', ')}`, {
285
- portCount: listeningPorts.length,
286
- ports: listeningPorts,
287
- component: 'smart-proxy'
288
- });
289
- // Provision NFTables rules for routes that use NFTables
290
- for (const route of this.settings.routes) {
291
- if (route.action.forwardingEngine === 'nftables') {
292
- await this.nftablesManager.provisionRoute(route);
293
- }
294
- }
295
- // Initialize and start HttpProxy if needed - before port binding
296
- if (this.settings.useHttpProxy && this.settings.useHttpProxy.length > 0) {
297
- await this.httpProxyBridge.initialize();
298
- await this.httpProxyBridge.start();
299
- }
300
- // Start port listeners using the PortManager BEFORE initializing certificate manager
301
- // This ensures all required ports are bound and ready when adding ACME challenge routes
302
- await this.portManager.addPorts(listeningPorts);
303
- // Initialize certificate manager AFTER port binding is complete
304
- // This ensures the ACME challenge port is already bound and ready when needed
305
- await this.initializeCertificateManager();
306
- // Connect certificate manager with HttpProxy if both are available
307
- if (this.certManager && this.httpProxyBridge.getHttpProxy()) {
308
- this.certManager.setHttpProxy(this.httpProxyBridge.getHttpProxy());
309
- }
310
- // Now that ports are listening, provision any required certificates
311
- if (this.certManager) {
312
- logger.log('info', 'Starting certificate provisioning now that ports are ready', { component: 'certificate-manager' });
313
- await this.certManager.provisionAllCertificates();
314
- }
315
- // Start the metrics collector now that all components are initialized
316
- this.metricsCollector.start();
317
- // Set up periodic connection logging and inactivity checks
318
- this.connectionLogger = setInterval(() => {
319
- // Immediately return if shutting down
320
- if (this.isShuttingDown)
92
+ // Spawn Rust binary
93
+ const spawned = await this.bridge.spawn();
94
+ if (!spawned) {
95
+ throw new Error('RustProxy binary not found. Set SMARTPROXY_RUST_BINARY env var, install the platform package, ' +
96
+ 'or build locally with: cd rust && cargo build --release');
97
+ }
98
+ // Handle unexpected exit (only emits error if not intentionally stopping)
99
+ this.bridge.on('exit', (code, signal) => {
100
+ if (this.stopping)
321
101
  return;
322
- // Perform inactivity check
323
- this.connectionManager.performInactivityCheck();
324
- // Log connection statistics
325
- const now = Date.now();
326
- let maxIncoming = 0;
327
- let maxOutgoing = 0;
328
- let tlsConnections = 0;
329
- let nonTlsConnections = 0;
330
- let completedTlsHandshakes = 0;
331
- let pendingTlsHandshakes = 0;
332
- let keepAliveConnections = 0;
333
- let httpProxyConnections = 0;
334
- // Get connection records for analysis
335
- const connectionRecords = this.connectionManager.getConnections();
336
- // Analyze active connections
337
- for (const record of connectionRecords.values()) {
338
- // Track connection stats
339
- if (record.isTLS) {
340
- tlsConnections++;
341
- if (record.tlsHandshakeComplete) {
342
- completedTlsHandshakes++;
343
- }
344
- else {
345
- pendingTlsHandshakes++;
346
- }
347
- }
348
- else {
349
- nonTlsConnections++;
350
- }
351
- if (record.hasKeepAlive) {
352
- keepAliveConnections++;
353
- }
354
- if (record.usingNetworkProxy) {
355
- httpProxyConnections++;
356
- }
357
- maxIncoming = Math.max(maxIncoming, now - record.incomingStartTime);
358
- if (record.outgoingStartTime) {
359
- maxOutgoing = Math.max(maxOutgoing, now - record.outgoingStartTime);
360
- }
361
- }
362
- // Get termination stats
363
- const terminationStats = this.connectionManager.getTerminationStats();
364
- // Log detailed stats
365
- logger.log('info', 'Connection statistics', {
366
- activeConnections: connectionRecords.size,
367
- tls: {
368
- total: tlsConnections,
369
- completed: completedTlsHandshakes,
370
- pending: pendingTlsHandshakes
371
- },
372
- nonTls: nonTlsConnections,
373
- keepAlive: keepAliveConnections,
374
- httpProxy: httpProxyConnections,
375
- longestRunning: {
376
- incoming: plugins.prettyMs(maxIncoming),
377
- outgoing: plugins.prettyMs(maxOutgoing)
378
- },
379
- terminationStats: {
380
- incoming: terminationStats.incoming,
381
- outgoing: terminationStats.outgoing
382
- },
383
- component: 'connection-manager'
384
- });
385
- }, this.settings.inactivityCheckInterval || 60000);
386
- // Make sure the interval doesn't keep the process alive
387
- if (this.connectionLogger.unref) {
388
- this.connectionLogger.unref();
389
- }
102
+ logger.log('error', `RustProxy exited unexpectedly (code=${code}, signal=${signal})`, { component: 'smart-proxy' });
103
+ this.emit('error', new Error(`RustProxy exited (code=${code}, signal=${signal})`));
104
+ });
105
+ // Check if any routes need TS-side handling (socket handlers, dynamic functions)
106
+ const hasHandlerRoutes = this.settings.routes.some((r) => (r.action.type === 'socket-handler' && r.action.socketHandler) ||
107
+ r.action.targets?.some((t) => typeof t.host === 'function' || typeof t.port === 'function'));
108
+ // Start socket handler relay server (but don't tell Rust yet - proxy not started)
109
+ if (hasHandlerRoutes) {
110
+ this.socketHandlerServer = new SocketHandlerServer(this.preprocessor);
111
+ await this.socketHandlerServer.start();
112
+ }
113
+ // Preprocess routes (strip JS functions, convert socket-handler routes)
114
+ const rustRoutes = this.preprocessor.preprocessForRust(this.settings.routes);
115
+ // Build Rust config
116
+ const config = this.buildRustConfig(rustRoutes);
117
+ // Start the Rust proxy
118
+ await this.bridge.startProxy(config);
119
+ // Now that Rust proxy is running, configure socket handler relay
120
+ if (this.socketHandlerServer) {
121
+ await this.bridge.setSocketHandlerRelay(this.socketHandlerServer.getSocketPath());
122
+ }
123
+ // Handle certProvisionFunction
124
+ await this.provisionCertificatesViaCallback();
125
+ // Start metrics polling
126
+ this.metricsAdapter.startPolling();
127
+ logger.log('info', 'SmartProxy started (Rust engine)', { component: 'smart-proxy' });
390
128
  }
391
129
  /**
392
- * Extract domain configurations from routes for certificate provisioning
393
- *
394
- * Note: This method has been removed as we now work directly with routes
395
- */
396
- /**
397
- * Stop the proxy server
130
+ * Stop the proxy.
398
131
  */
399
132
  async stop() {
400
- logger.log('info', 'SmartProxy shutting down...');
401
- this.isShuttingDown = true;
402
- this.portManager.setShuttingDown(true);
403
- // Stop certificate manager
404
- if (this.certManager) {
405
- await this.certManager.stop();
406
- logger.log('info', 'Certificate manager stopped');
407
- }
408
- // Stop NFTablesManager
409
- await this.nftablesManager.stop();
410
- logger.log('info', 'NFTablesManager stopped');
411
- // Stop the connection logger
412
- if (this.connectionLogger) {
413
- clearInterval(this.connectionLogger);
414
- this.connectionLogger = null;
415
- }
416
- // Stop all port listeners
417
- await this.portManager.closeAll();
418
- logger.log('info', 'All servers closed. Cleaning up active connections...');
419
- // Clean up all active connections
420
- await this.connectionManager.clearConnections();
421
- // Stop HttpProxy
422
- await this.httpProxyBridge.stop();
423
- // Clear ACME state manager
424
- this.acmeStateManager.clear();
425
- // Stop metrics collector
426
- this.metricsCollector.stop();
427
- // Clean up ProtocolDetector singleton
428
- const detection = await import('../../detection/index.js');
429
- detection.ProtocolDetector.destroy();
430
- // Flush any pending deduplicated logs
431
- connectionLogDeduplicator.flushAll();
432
- logger.log('info', 'SmartProxy shutdown complete.');
433
- }
434
- /**
435
- * Updates the domain configurations for the proxy
436
- *
437
- * Note: This legacy method has been removed. Use updateRoutes instead.
438
- */
439
- async updateDomainConfigs() {
440
- logger.log('warn', 'Method updateDomainConfigs() is deprecated. Use updateRoutes() instead.');
441
- throw new Error('updateDomainConfigs() is deprecated - use updateRoutes() instead');
442
- }
443
- /**
444
- * Verify the challenge route has been properly removed from routes
445
- */
446
- async verifyChallengeRouteRemoved() {
447
- const maxRetries = 10;
448
- const retryDelay = 100; // milliseconds
449
- for (let i = 0; i < maxRetries; i++) {
450
- // Check if the challenge route is still in the active routes
451
- const challengeRouteExists = this.settings.routes.some(r => r.name === 'acme-challenge');
452
- if (!challengeRouteExists) {
453
- try {
454
- logger.log('info', 'Challenge route successfully removed from routes');
455
- }
456
- catch (error) {
457
- // Silently handle logging errors
458
- console.log('[INFO] Challenge route successfully removed from routes');
459
- }
460
- return;
461
- }
462
- // Wait before retrying
463
- await plugins.smartdelay.delayFor(retryDelay);
464
- }
465
- const error = `Failed to verify challenge route removal after ${maxRetries} attempts`;
133
+ logger.log('info', 'SmartProxy shutting down...', { component: 'smart-proxy' });
134
+ this.stopping = true;
135
+ // Stop metrics polling
136
+ this.metricsAdapter.stopPolling();
137
+ // Remove exit listener before killing to avoid spurious error events
138
+ this.bridge.removeAllListeners('exit');
139
+ // Stop Rust proxy
466
140
  try {
467
- logger.log('error', error);
141
+ await this.bridge.stopProxy();
142
+ }
143
+ catch {
144
+ // Ignore if already stopped
468
145
  }
469
- catch (logError) {
470
- // Silently handle logging errors
471
- console.log(`[ERROR] ${error}`);
146
+ this.bridge.kill();
147
+ // Stop socket handler relay
148
+ if (this.socketHandlerServer) {
149
+ await this.socketHandlerServer.stop();
150
+ this.socketHandlerServer = null;
472
151
  }
473
- throw new Error(error);
152
+ logger.log('info', 'SmartProxy shutdown complete.', { component: 'smart-proxy' });
474
153
  }
475
154
  /**
476
- * Update routes with new configuration
477
- *
478
- * This method replaces the current route configuration with the provided routes.
479
- * It also provisions certificates for routes that require TLS termination and have
480
- * `certificate: 'auto'` set in their TLS configuration.
481
- *
482
- * @param newRoutes Array of route configurations to use
483
- *
484
- * Example:
485
- * ```ts
486
- * proxy.updateRoutes([
487
- * {
488
- * match: { ports: 443, domains: 'secure.example.com' },
489
- * action: {
490
- * type: 'forward',
491
- * target: { host: '10.0.0.1', port: 8443 },
492
- * tls: { mode: 'terminate', certificate: 'auto' }
493
- * }
494
- * }
495
- * ]);
496
- * ```
155
+ * Update routes atomically.
497
156
  */
498
157
  async updateRoutes(newRoutes) {
499
158
  return this.routeUpdateLock.runExclusive(async () => {
500
- try {
501
- logger.log('info', `Updating routes (${newRoutes.length} routes)`, {
502
- routeCount: newRoutes.length,
503
- component: 'smart-proxy'
504
- });
159
+ // Validate
160
+ const validation = RouteValidator.validateRoutes(newRoutes);
161
+ if (!validation.valid) {
162
+ RouteValidator.logValidationErrors(validation.errors);
163
+ throw new Error(`Route validation failed: ${validation.errors.size} route(s) have errors`);
505
164
  }
506
- catch (error) {
507
- // Silently handle logging errors
508
- console.log(`[INFO] Updating routes (${newRoutes.length} routes)`);
165
+ // Preprocess for Rust
166
+ const rustRoutes = this.preprocessor.preprocessForRust(newRoutes);
167
+ // Send to Rust
168
+ await this.bridge.updateRoutes(rustRoutes);
169
+ // Update local route manager
170
+ this.routeManager.updateRoutes(newRoutes);
171
+ // Update socket handler relay if handler routes changed
172
+ const hasHandlerRoutes = newRoutes.some((r) => (r.action.type === 'socket-handler' && r.action.socketHandler) ||
173
+ r.action.targets?.some((t) => typeof t.host === 'function' || typeof t.port === 'function'));
174
+ if (hasHandlerRoutes && !this.socketHandlerServer) {
175
+ this.socketHandlerServer = new SocketHandlerServer(this.preprocessor);
176
+ await this.socketHandlerServer.start();
177
+ await this.bridge.setSocketHandlerRelay(this.socketHandlerServer.getSocketPath());
509
178
  }
510
- // Update route orchestrator dependencies if cert manager changed
511
- if (this.certManager && !this.routeOrchestrator.getCertManager()) {
512
- this.routeOrchestrator.setCertManager(this.certManager);
179
+ else if (!hasHandlerRoutes && this.socketHandlerServer) {
180
+ await this.socketHandlerServer.stop();
181
+ this.socketHandlerServer = null;
513
182
  }
514
- // Delegate the complex route update logic to RouteOrchestrator
515
- const updateResult = await this.routeOrchestrator.updateRoutes(this.settings.routes, newRoutes, {
516
- acmePort: this.settings.acme?.port || 80,
517
- acmeOptions: this.certManager?.getAcmeOptions(),
518
- acmeState: this.certManager?.getState(),
519
- globalChallengeRouteActive: this.globalChallengeRouteActive,
520
- createCertificateManager: this.createCertificateManager.bind(this),
521
- verifyChallengeRouteRemoved: this.verifyChallengeRouteRemoved.bind(this)
522
- });
523
- // Update settings with the new routes
183
+ // Update stored routes
524
184
  this.settings.routes = newRoutes;
525
- // Update global state from orchestrator results
526
- this.globalChallengeRouteActive = updateResult.newChallengeRouteActive;
527
- // Update port usage map from orchestrator
528
- this.portUsageMap = updateResult.portUsageMap;
529
- // If certificate manager was recreated, update our reference
530
- if (updateResult.newCertManager) {
531
- this.certManager = updateResult.newCertManager;
532
- // Update the orchestrator's reference too
533
- this.routeOrchestrator.setCertManager(this.certManager);
534
- }
185
+ // Handle cert provisioning for new routes
186
+ await this.provisionCertificatesViaCallback();
187
+ logger.log('info', `Routes updated (${newRoutes.length} routes)`, { component: 'smart-proxy' });
535
188
  });
536
189
  }
537
190
  /**
538
- * Manually provision a certificate for a route
191
+ * Provision a certificate for a named route.
539
192
  */
540
193
  async provisionCertificate(routeName) {
541
- if (!this.certManager) {
542
- throw new Error('Certificate manager not initialized');
543
- }
544
- const route = this.settings.routes.find(r => r.name === routeName);
545
- if (!route) {
546
- throw new Error(`Route ${routeName} not found`);
547
- }
548
- await this.certManager.provisionCertificate(route);
194
+ await this.bridge.provisionCertificate(routeName);
549
195
  }
550
- // Port usage tracking methods moved to RouteOrchestrator
551
196
  /**
552
- * Force renewal of a certificate
197
+ * Force renewal of a certificate.
553
198
  */
554
199
  async renewCertificate(routeName) {
555
- if (!this.certManager) {
556
- throw new Error('Certificate manager not initialized');
557
- }
558
- await this.certManager.renewCertificate(routeName);
200
+ await this.bridge.renewCertificate(routeName);
559
201
  }
560
202
  /**
561
- * Get certificate status for a route
203
+ * Get certificate status for a route (async - calls Rust).
562
204
  */
563
- getCertificateStatus(routeName) {
564
- if (!this.certManager) {
565
- return undefined;
566
- }
567
- return this.certManager.getCertificateStatus(routeName);
205
+ async getCertificateStatus(routeName) {
206
+ return this.bridge.getCertificateStatus(routeName);
568
207
  }
569
208
  /**
570
- * Get proxy metrics with clean API
571
- *
572
- * @returns IMetrics interface with grouped metrics methods
209
+ * Get the metrics interface.
573
210
  */
574
211
  getMetrics() {
575
- return this.metricsCollector;
212
+ return this.metricsAdapter;
576
213
  }
577
214
  /**
578
- * Validates if a domain name is valid for certificate issuance
215
+ * Get statistics (async - calls Rust).
579
216
  */
580
- isValidDomain(domain) {
581
- // Very basic domain validation
582
- if (!domain || domain.length === 0) {
583
- return false;
584
- }
585
- // Check for wildcard domains (they can't get ACME certs)
586
- if (domain.includes('*')) {
587
- logger.log('warn', `Wildcard domains like "${domain}" are not supported for automatic ACME certificates`, { domain, component: 'certificate-manager' });
588
- return false;
589
- }
590
- // Check if domain has at least one dot and no invalid characters
591
- const validDomainRegex = /^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
592
- if (!validDomainRegex.test(domain)) {
593
- logger.log('warn', `Domain "${domain}" has invalid format for certificate issuance`, { domain, component: 'certificate-manager' });
594
- return false;
595
- }
596
- return true;
217
+ async getStatistics() {
218
+ return this.bridge.getStatistics();
597
219
  }
598
220
  /**
599
- * Add a new listening port without changing the route configuration
600
- *
601
- * This allows you to add a port listener without updating routes.
602
- * Useful for preparing to listen on a port before adding routes for it.
603
- *
604
- * @param port The port to start listening on
605
- * @returns Promise that resolves when the port is listening
221
+ * Add a listening port at runtime.
606
222
  */
607
223
  async addListeningPort(port) {
608
- return this.portManager.addPort(port);
224
+ await this.bridge.addListeningPort(port);
609
225
  }
610
226
  /**
611
- * Stop listening on a specific port without changing the route configuration
612
- *
613
- * This allows you to stop a port listener without updating routes.
614
- * Useful for temporary maintenance or port changes.
615
- *
616
- * @param port The port to stop listening on
617
- * @returns Promise that resolves when the port is closed
227
+ * Remove a listening port at runtime.
618
228
  */
619
229
  async removeListeningPort(port) {
620
- return this.portManager.removePort(port);
230
+ await this.bridge.removeListeningPort(port);
621
231
  }
622
232
  /**
623
- * Get a list of all ports currently being listened on
624
- *
625
- * @returns Array of port numbers
233
+ * Get all currently listening ports (async - calls Rust).
626
234
  */
627
- getListeningPorts() {
628
- return this.portManager.getListeningPorts();
235
+ async getListeningPorts() {
236
+ if (!this.bridge.running)
237
+ return [];
238
+ return this.bridge.getListeningPorts();
629
239
  }
630
240
  /**
631
- * Get statistics about current connections
632
- */
633
- getStatistics() {
634
- const connectionRecords = this.connectionManager.getConnections();
635
- const terminationStats = this.connectionManager.getTerminationStats();
636
- let tlsConnections = 0;
637
- let nonTlsConnections = 0;
638
- let keepAliveConnections = 0;
639
- let httpProxyConnections = 0;
640
- // Analyze active connections
641
- for (const record of connectionRecords.values()) {
642
- if (record.isTLS)
643
- tlsConnections++;
644
- else
645
- nonTlsConnections++;
646
- if (record.hasKeepAlive)
647
- keepAliveConnections++;
648
- if (record.usingNetworkProxy)
649
- httpProxyConnections++;
650
- }
651
- return {
652
- activeConnections: connectionRecords.size,
653
- tlsConnections,
654
- nonTlsConnections,
655
- keepAliveConnections,
656
- httpProxyConnections,
657
- terminationStats,
658
- acmeEnabled: !!this.certManager,
659
- port80HandlerPort: this.certManager ? 80 : null,
660
- routeCount: this.settings.routes.length,
661
- activePorts: this.portManager.getListeningPorts().length,
662
- listeningPorts: this.portManager.getListeningPorts()
663
- };
664
- }
665
- /**
666
- * Get a list of eligible domains for ACME certificates
241
+ * Get eligible domains for ACME certificates (sync - reads local routes).
667
242
  */
668
243
  getEligibleDomainsForCertificates() {
669
244
  const domains = [];
670
- // Get domains from routes
671
- const routes = this.settings.routes || [];
672
- for (const route of routes) {
245
+ for (const route of this.settings.routes || []) {
673
246
  if (!route.match.domains)
674
247
  continue;
675
- // Skip routes without TLS termination or auto certificates
676
248
  if (route.action.type !== 'forward' ||
677
249
  !route.action.tls ||
678
250
  route.action.tls.mode === 'passthrough' ||
679
251
  route.action.tls.certificate !== 'auto')
680
252
  continue;
681
- const routeDomains = Array.isArray(route.match.domains)
682
- ? route.match.domains
683
- : [route.match.domains];
684
- // Skip domains that can't be used with ACME
685
- const eligibleDomains = routeDomains.filter(domain => !domain.includes('*') && this.isValidDomain(domain));
686
- domains.push(...eligibleDomains);
253
+ const routeDomains = Array.isArray(route.match.domains) ? route.match.domains : [route.match.domains];
254
+ const eligible = routeDomains.filter((d) => !d.includes('*') && this.isValidDomain(d));
255
+ domains.push(...eligible);
687
256
  }
688
- // Legacy mode is no longer supported
689
257
  return domains;
690
258
  }
691
259
  /**
692
- * Get NFTables status
260
+ * Get NFTables status (async - calls Rust).
693
261
  */
694
262
  async getNfTablesStatus() {
695
- return this.nftablesManager.getStatus();
263
+ return this.bridge.getNftablesStatus();
696
264
  }
265
+ // --- Private helpers ---
697
266
  /**
698
- * Validate ACME configuration
267
+ * Build the Rust configuration object from TS settings.
699
268
  */
700
- validateAcmeConfiguration() {
701
- const warnings = [];
702
- // Check for routes with certificate: 'auto'
703
- const autoRoutes = this.settings.routes.filter(r => r.action.tls?.certificate === 'auto');
704
- if (autoRoutes.length === 0) {
705
- return warnings;
706
- }
707
- // Check if we have ACME email configuration
708
- const hasTopLevelEmail = this.settings.acme?.email;
709
- const routesWithEmail = autoRoutes.filter(r => r.action.tls?.acme?.email);
710
- if (!hasTopLevelEmail && routesWithEmail.length === 0) {
711
- warnings.push('Routes with certificate: "auto" require ACME email configuration. ' +
712
- 'Add email to either top-level "acme" config or individual route\'s "tls.acme" config.');
713
- }
714
- // Check for port 80 availability for challenges
715
- if (autoRoutes.length > 0) {
716
- const challengePort = this.settings.acme?.port || 80;
717
- const portsInUse = this.routeManager.getListeningPorts();
718
- if (!portsInUse.includes(challengePort)) {
719
- warnings.push(`Port ${challengePort} is not configured for any routes but is needed for ACME challenges. ` +
720
- `Add a route listening on port ${challengePort} or ensure it's accessible for HTTP-01 challenges.`);
721
- }
722
- }
723
- // Check for mismatched environments
724
- if (this.settings.acme?.useProduction) {
725
- const stagingRoutes = autoRoutes.filter(r => r.action.tls?.acme?.useProduction === false);
726
- if (stagingRoutes.length > 0) {
727
- warnings.push('Top-level ACME uses production but some routes use staging. ' +
728
- 'Consider aligning environments to avoid certificate issues.');
729
- }
730
- }
731
- // Check for wildcard domains with auto certificates
732
- for (const route of autoRoutes) {
733
- const domains = Array.isArray(route.match.domains)
734
- ? route.match.domains
735
- : [route.match.domains];
736
- const wildcardDomains = domains.filter(d => d?.includes('*'));
737
- if (wildcardDomains.length > 0) {
738
- warnings.push(`Route "${route.name}" has wildcard domain(s) ${wildcardDomains.join(', ')} ` +
739
- 'with certificate: "auto". Wildcard certificates require DNS-01 challenges, ' +
740
- 'which are not currently supported. Use static certificates instead.');
269
+ buildRustConfig(routes) {
270
+ return {
271
+ routes,
272
+ defaults: this.settings.defaults,
273
+ acme: this.settings.acme
274
+ ? {
275
+ enabled: this.settings.acme.enabled,
276
+ email: this.settings.acme.email,
277
+ useProduction: this.settings.acme.useProduction,
278
+ port: this.settings.acme.port,
279
+ renewThresholdDays: this.settings.acme.renewThresholdDays,
280
+ autoRenew: this.settings.acme.autoRenew,
281
+ certificateStore: this.settings.acme.certificateStore,
282
+ renewCheckIntervalHours: this.settings.acme.renewCheckIntervalHours,
283
+ }
284
+ : undefined,
285
+ connectionTimeout: this.settings.connectionTimeout,
286
+ initialDataTimeout: this.settings.initialDataTimeout,
287
+ socketTimeout: this.settings.socketTimeout,
288
+ maxConnectionLifetime: this.settings.maxConnectionLifetime,
289
+ gracefulShutdownTimeout: this.settings.gracefulShutdownTimeout,
290
+ maxConnectionsPerIp: this.settings.maxConnectionsPerIP,
291
+ connectionRateLimitPerMinute: this.settings.connectionRateLimitPerMinute,
292
+ keepAliveTreatment: this.settings.keepAliveTreatment,
293
+ keepAliveInactivityMultiplier: this.settings.keepAliveInactivityMultiplier,
294
+ extendedKeepAliveLifetime: this.settings.extendedKeepAliveLifetime,
295
+ acceptProxyProtocol: this.settings.acceptProxyProtocol,
296
+ sendProxyProtocol: this.settings.sendProxyProtocol,
297
+ };
298
+ }
299
+ /**
300
+ * For routes with certificate: 'auto', call certProvisionFunction if set.
301
+ * If the callback returns a cert object, load it into Rust.
302
+ * If it returns 'http01', let Rust handle ACME.
303
+ */
304
+ async provisionCertificatesViaCallback() {
305
+ const provisionFn = this.settings.certProvisionFunction;
306
+ if (!provisionFn)
307
+ return;
308
+ for (const route of this.settings.routes) {
309
+ if (route.action.tls?.certificate !== 'auto')
310
+ continue;
311
+ if (!route.match.domains)
312
+ continue;
313
+ const domains = Array.isArray(route.match.domains) ? route.match.domains : [route.match.domains];
314
+ for (const domain of domains) {
315
+ if (domain.includes('*'))
316
+ continue;
317
+ try {
318
+ const result = await provisionFn(domain);
319
+ if (result === 'http01') {
320
+ // Rust handles ACME for this domain
321
+ continue;
322
+ }
323
+ // Got a static cert object - load it into Rust
324
+ if (result && typeof result === 'object') {
325
+ const certObj = result;
326
+ await this.bridge.loadCertificate(domain, certObj.publicKey, certObj.privateKey);
327
+ logger.log('info', `Certificate loaded via provision function for ${domain}`, { component: 'smart-proxy' });
328
+ }
329
+ }
330
+ catch (err) {
331
+ logger.log('warn', `certProvisionFunction failed for ${domain}: ${err.message}`, { component: 'smart-proxy' });
332
+ // Fallback to ACME if enabled
333
+ if (this.settings.certProvisionFallbackToAcme !== false) {
334
+ logger.log('info', `Falling back to ACME for ${domain}`, { component: 'smart-proxy' });
335
+ }
336
+ }
741
337
  }
742
338
  }
743
- return warnings;
339
+ }
340
+ isValidDomain(domain) {
341
+ if (!domain || domain.length === 0)
342
+ return false;
343
+ if (domain.includes('*'))
344
+ return false;
345
+ const validDomainRegex = /^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
346
+ return validDomainRegex.test(domain);
744
347
  }
745
348
  }
746
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnQtcHJveHkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9wcm94aWVzL3NtYXJ0LXByb3h5L3NtYXJ0LXByb3h5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sa0JBQWtCLENBQUM7QUFDNUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBQ3BELE9BQU8sRUFBRSx5QkFBeUIsRUFBRSxNQUFNLHNDQUFzQyxDQUFDO0FBRWpGLGdDQUFnQztBQUNoQyxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUM1RCxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFDeEQsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBQzlDLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUN6RCxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDdEQsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBQ2hELE9BQU8sRUFBRSxrQkFBa0IsSUFBSSxZQUFZLEVBQUUsTUFBTSxxQ0FBcUMsQ0FBQztBQUN6RixPQUFPLEVBQUUsc0JBQXNCLEVBQUUsTUFBTSwrQkFBK0IsQ0FBQztBQUN2RSxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFFeEQsc0JBQXNCO0FBQ3RCLE9BQU8sRUFBRSxnQkFBZ0IsRUFBb0IsTUFBTSwwQkFBMEIsQ0FBQztBQVE5RSxnREFBZ0Q7QUFDaEQsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBRXpDLHlCQUF5QjtBQUN6QixPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUFFNUQsaURBQWlEO0FBQ2pELE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLHlCQUF5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
349
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnQtcHJveHkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9wcm94aWVzL3NtYXJ0LXByb3h5L3NtYXJ0LXByb3h5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sa0JBQWtCLENBQUM7QUFDNUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBRXBELDBCQUEwQjtBQUMxQixPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDekQsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDN0QsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDNUQsT0FBTyxFQUFFLG1CQUFtQixFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUFDakUsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFFL0QsbUJBQW1CO0FBQ25CLE9BQU8sRUFBRSxrQkFBa0IsSUFBSSxZQUFZLEVBQUUsTUFBTSxxQ0FBcUMsQ0FBQztBQUN6RixPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUFDNUQsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBT3pDOzs7Ozs7Ozs7R0FTRztBQUNILE1BQU0sT0FBTyxVQUFXLFNBQVEsT0FBTyxDQUFDLFlBQVk7SUFXbEQsWUFBWSxXQUErQjtRQUN6QyxLQUFLLEVBQUUsQ0FBQztRQU5GLHdCQUFtQixHQUErQixJQUFJLENBQUM7UUFHdkQsYUFBUSxHQUFHLEtBQUssQ0FBQztRQUt2QixpQkFBaUI7UUFDakIsSUFBSSxDQUFDLFFBQVEsR0FBRztZQUNkLEdBQUcsV0FBVztZQUNkLGtCQUFrQixFQUFFLFdBQVcsQ0FBQyxrQkFBa0IsSUFBSSxNQUFNO1lBQzVELGFBQWEsRUFBRSxXQUFXLENBQUMsYUFBYSxJQUFJLE9BQU87WUFD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