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