@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,675 +0,0 @@
1
- import * as plugins from '../../plugins.js';
2
- import {
3
- createLogger,
4
- } from './models/types.js';
5
- import { SharedRouteManager as RouteManager } from '../../core/routing/route-manager.js';
6
- import type {
7
- IHttpProxyOptions,
8
- ILogger
9
- } from './models/types.js';
10
- import type { IRouteConfig } from '../smart-proxy/models/route-types.js';
11
- import type { IRouteContext, IHttpRouteContext } from '../../core/models/route-context.js';
12
- import { createBaseRouteContext } from '../../core/models/route-context.js';
13
- import { CertificateManager } from './certificate-manager.js';
14
- import { ConnectionPool } from './connection-pool.js';
15
- import { RequestHandler, type IMetricsTracker } from './request-handler.js';
16
- import { WebSocketHandler } from './websocket-handler.js';
17
- import { HttpRouter } from '../../routing/router/index.js';
18
- import { cleanupSocket } from '../../core/utils/socket-utils.js';
19
- import { FunctionCache } from './function-cache.js';
20
- import { SecurityManager } from './security-manager.js';
21
- import { connectionLogDeduplicator } from '../../core/utils/log-deduplicator.js';
22
-
23
- /**
24
- * HttpProxy provides a reverse proxy with TLS termination, WebSocket support,
25
- * automatic certificate management, and high-performance connection pooling.
26
- * Handles all HTTP/HTTPS traffic including redirects, ACME challenges, and static routes.
27
- */
28
- export class HttpProxy implements IMetricsTracker {
29
- // Provide a minimal JSON representation to avoid circular references during deep equality checks
30
- public toJSON(): any {
31
- return {};
32
- }
33
- // Configuration
34
- public options: IHttpProxyOptions;
35
- public routes: IRouteConfig[] = [];
36
-
37
- // Server instances (HTTP/2 with HTTP/1 fallback)
38
- public httpsServer: plugins.http2.Http2SecureServer;
39
-
40
- // Core components
41
- private certificateManager: CertificateManager;
42
- private connectionPool: ConnectionPool;
43
- private requestHandler: RequestHandler;
44
- private webSocketHandler: WebSocketHandler;
45
- private router = new HttpRouter(); // Unified HTTP router
46
- private routeManager: RouteManager;
47
- private functionCache: FunctionCache;
48
- private securityManager: SecurityManager;
49
-
50
- // State tracking
51
- public socketMap = new plugins.lik.ObjectMap<plugins.net.Socket>();
52
- public activeContexts: Set<string> = new Set();
53
- public connectedClients: number = 0;
54
- public startTime: number = 0;
55
- public requestsServed: number = 0;
56
- public failedRequests: number = 0;
57
-
58
- // Tracking for SmartProxy integration
59
- private portProxyConnections: number = 0;
60
- private tlsTerminatedConnections: number = 0;
61
-
62
- // Timers
63
- private metricsInterval: NodeJS.Timeout;
64
- private connectionPoolCleanupInterval: NodeJS.Timeout;
65
-
66
- // Logger
67
- private logger: ILogger;
68
-
69
- /**
70
- * Creates a new HttpProxy instance
71
- */
72
- constructor(optionsArg: IHttpProxyOptions) {
73
- // Set default options
74
- this.options = {
75
- port: optionsArg.port,
76
- maxConnections: optionsArg.maxConnections || 10000,
77
- keepAliveTimeout: optionsArg.keepAliveTimeout || 120000, // 2 minutes
78
- headersTimeout: optionsArg.headersTimeout || 60000, // 1 minute
79
- logLevel: optionsArg.logLevel || 'info',
80
- cors: optionsArg.cors || {
81
- allowOrigin: '*',
82
- allowMethods: 'GET, POST, PUT, DELETE, OPTIONS',
83
- allowHeaders: 'Content-Type, Authorization',
84
- maxAge: 86400
85
- },
86
- // Defaults for SmartProxy integration
87
- connectionPoolSize: optionsArg.connectionPoolSize || 50,
88
- portProxyIntegration: optionsArg.portProxyIntegration || false,
89
- // Backend protocol (http1 or http2)
90
- backendProtocol: optionsArg.backendProtocol || 'http1',
91
- // Default ACME options
92
- acme: {
93
- enabled: optionsArg.acme?.enabled || false,
94
- port: optionsArg.acme?.port || 80,
95
- accountEmail: optionsArg.acme?.accountEmail || 'admin@example.com',
96
- useProduction: optionsArg.acme?.useProduction || false, // Default to staging for safety
97
- renewThresholdDays: optionsArg.acme?.renewThresholdDays || 30,
98
- autoRenew: optionsArg.acme?.autoRenew !== false, // Default to true
99
- certificateStore: optionsArg.acme?.certificateStore || './certs',
100
- skipConfiguredCerts: optionsArg.acme?.skipConfiguredCerts || false
101
- }
102
- };
103
-
104
- // Initialize logger
105
- this.logger = createLogger(this.options.logLevel);
106
-
107
- // Initialize route manager
108
- this.routeManager = new RouteManager({
109
- logger: this.logger,
110
- enableDetailedLogging: this.options.logLevel === 'debug',
111
- routes: []
112
- });
113
-
114
- // Initialize function cache
115
- this.functionCache = new FunctionCache(this.logger, {
116
- maxCacheSize: this.options.functionCacheSize || 1000,
117
- defaultTtl: this.options.functionCacheTtl || 5000
118
- });
119
-
120
- // Initialize security manager
121
- this.securityManager = new SecurityManager(
122
- this.logger,
123
- [],
124
- this.options.maxConnectionsPerIP || 100,
125
- this.options.connectionRateLimitPerMinute || 300
126
- );
127
-
128
- // Initialize other components
129
- this.certificateManager = new CertificateManager(this.options);
130
- this.connectionPool = new ConnectionPool(this.options);
131
- this.requestHandler = new RequestHandler(
132
- this.options,
133
- this.connectionPool,
134
- this.routeManager,
135
- this.functionCache,
136
- this.router
137
- );
138
- this.webSocketHandler = new WebSocketHandler(
139
- this.options,
140
- this.connectionPool,
141
- this.routes // Pass current routes to WebSocketHandler
142
- );
143
-
144
- // Connect request handler to this metrics tracker
145
- this.requestHandler.setMetricsTracker(this);
146
-
147
- // Initialize with any provided routes
148
- if (this.options.routes && this.options.routes.length > 0) {
149
- this.updateRouteConfigs(this.options.routes);
150
- }
151
- }
152
-
153
- /**
154
- * Implements IMetricsTracker interface to increment request counters
155
- */
156
- public incrementRequestsServed(): void {
157
- this.requestsServed++;
158
- }
159
-
160
- /**
161
- * Implements IMetricsTracker interface to increment failed request counters
162
- */
163
- public incrementFailedRequests(): void {
164
- this.failedRequests++;
165
- }
166
-
167
- /**
168
- * Returns the port number this HttpProxy is listening on
169
- * Useful for SmartProxy to determine where to forward connections
170
- */
171
- public getListeningPort(): number {
172
- // If the server is running, get the actual listening port
173
- if (this.httpsServer && this.httpsServer.address()) {
174
- const address = this.httpsServer.address();
175
- if (address && typeof address === 'object' && 'port' in address) {
176
- return address.port;
177
- }
178
- }
179
- // Fallback to configured port
180
- return this.options.port;
181
- }
182
-
183
- /**
184
- * Updates the server capacity settings
185
- * @param maxConnections Maximum number of simultaneous connections
186
- * @param keepAliveTimeout Keep-alive timeout in milliseconds
187
- * @param connectionPoolSize Size of the connection pool per backend
188
- */
189
- public updateCapacity(maxConnections?: number, keepAliveTimeout?: number, connectionPoolSize?: number): void {
190
- if (maxConnections !== undefined) {
191
- this.options.maxConnections = maxConnections;
192
- this.logger.info(`Updated max connections to ${maxConnections}`);
193
- }
194
-
195
- if (keepAliveTimeout !== undefined) {
196
- this.options.keepAliveTimeout = keepAliveTimeout;
197
-
198
- if (this.httpsServer) {
199
- // HTTP/2 servers have setTimeout method for timeout management
200
- this.httpsServer.setTimeout(keepAliveTimeout);
201
- this.logger.info(`Updated server timeout to ${keepAliveTimeout}ms`);
202
- }
203
- }
204
-
205
- if (connectionPoolSize !== undefined) {
206
- this.options.connectionPoolSize = connectionPoolSize;
207
- this.logger.info(`Updated connection pool size to ${connectionPoolSize}`);
208
-
209
- // Clean up excess connections in the pool
210
- this.connectionPool.cleanupConnectionPool();
211
- }
212
- }
213
-
214
- /**
215
- * Returns current server metrics
216
- * Useful for SmartProxy to determine which HttpProxy to use for load balancing
217
- */
218
- public getMetrics(): any {
219
- return {
220
- activeConnections: this.connectedClients,
221
- totalRequests: this.requestsServed,
222
- failedRequests: this.failedRequests,
223
- portProxyConnections: this.portProxyConnections,
224
- tlsTerminatedConnections: this.tlsTerminatedConnections,
225
- connectionPoolSize: this.connectionPool.getPoolStatus(),
226
- uptime: Math.floor((Date.now() - this.startTime) / 1000),
227
- memoryUsage: process.memoryUsage(),
228
- activeWebSockets: this.webSocketHandler.getConnectionInfo().activeConnections,
229
- functionCache: this.functionCache.getStats()
230
- };
231
- }
232
-
233
- /**
234
- * Starts the proxy server
235
- */
236
- public async start(): Promise<void> {
237
- this.startTime = Date.now();
238
-
239
- // Create HTTP/2 server with HTTP/1 fallback
240
- this.httpsServer = plugins.http2.createSecureServer(
241
- {
242
- key: this.certificateManager.getDefaultCertificates().key,
243
- cert: this.certificateManager.getDefaultCertificates().cert,
244
- allowHTTP1: true,
245
- ALPNProtocols: ['h2', 'http/1.1']
246
- }
247
- );
248
-
249
- // Track raw TCP connections for metrics and limits
250
- this.setupConnectionTracking();
251
-
252
- // Handle incoming HTTP/2 streams
253
- this.httpsServer.on('stream', (stream: plugins.http2.ServerHttp2Stream, headers: plugins.http2.IncomingHttpHeaders) => {
254
- this.requestHandler.handleHttp2(stream, headers);
255
- });
256
- // Handle HTTP/1.x fallback requests
257
- this.httpsServer.on('request', (req: plugins.http.IncomingMessage, res: plugins.http.ServerResponse) => {
258
- this.requestHandler.handleRequest(req, res);
259
- });
260
-
261
- // Share server with certificate manager for dynamic contexts
262
- // Cast to https.Server as Http2SecureServer is compatible for certificate contexts
263
- this.certificateManager.setHttpsServer(this.httpsServer as any);
264
- // Setup WebSocket support on HTTP/1 fallback
265
- this.webSocketHandler.initialize(this.httpsServer as any);
266
- // Start metrics logging
267
- this.setupMetricsCollection();
268
- // Start periodic connection pool cleanup
269
- this.connectionPoolCleanupInterval = this.connectionPool.setupPeriodicCleanup();
270
-
271
- // Start the server
272
- return new Promise((resolve) => {
273
- this.httpsServer.listen(this.options.port, () => {
274
- this.logger.info(`HttpProxy started on port ${this.options.port}`);
275
- resolve();
276
- });
277
- });
278
- }
279
-
280
- /**
281
- * Check if an address is a loopback address (IPv4 or IPv6)
282
- */
283
- private isLoopback(addr?: string): boolean {
284
- if (!addr) return false;
285
- // Check for IPv6 loopback
286
- if (addr === '::1') return true;
287
- // Handle IPv6-mapped IPv4 addresses
288
- if (addr.startsWith('::ffff:')) {
289
- addr = addr.substring(7);
290
- }
291
- // Check for IPv4 loopback range (127.0.0.0/8)
292
- return addr.startsWith('127.');
293
- }
294
-
295
- /**
296
- * Sets up tracking of TCP connections
297
- */
298
- private setupConnectionTracking(): void {
299
- this.httpsServer.on('connection', (connection: plugins.net.Socket) => {
300
- let remoteIP = connection.remoteAddress || '';
301
- const connectionId = Math.random().toString(36).substring(2, 15);
302
- const isFromSmartProxy = this.options.portProxyIntegration && this.isLoopback(connection.remoteAddress);
303
-
304
- // For SmartProxy connections, wait for CLIENT_IP header
305
- if (isFromSmartProxy) {
306
- const MAX_PREFACE = 256; // bytes - prevent DoS
307
- const HEADER_TIMEOUT_MS = 500; // timeout for header parsing
308
- let headerTimer: NodeJS.Timeout | undefined;
309
- let buffered = Buffer.alloc(0);
310
-
311
- const onData = (chunk: Buffer) => {
312
- buffered = Buffer.concat([buffered, chunk]);
313
-
314
- // Prevent unbounded growth
315
- if (buffered.length > MAX_PREFACE) {
316
- connection.removeListener('data', onData);
317
- if (headerTimer) clearTimeout(headerTimer);
318
- this.logger.warn('Header preface too large, closing connection');
319
- connection.destroy();
320
- return;
321
- }
322
-
323
- const idx = buffered.indexOf('\r\n');
324
- if (idx !== -1) {
325
- const headerLine = buffered.slice(0, idx).toString('utf8');
326
- if (headerLine.startsWith('CLIENT_IP:')) {
327
- remoteIP = headerLine.substring(10).trim();
328
- this.logger.debug(`Extracted client IP from SmartProxy: ${remoteIP}`);
329
- }
330
-
331
- // Clean up listener and timer
332
- connection.removeListener('data', onData);
333
- if (headerTimer) clearTimeout(headerTimer);
334
-
335
- // Put remaining data back onto the stream
336
- const remaining = buffered.slice(idx + 2);
337
- if (remaining.length > 0) {
338
- connection.unshift(remaining);
339
- }
340
-
341
- // Store the real IP on the connection
342
- connection._realRemoteIP = remoteIP;
343
-
344
- // Validate the real IP
345
- const ipValidation = this.securityManager.validateIP(remoteIP);
346
- if (!ipValidation.allowed) {
347
- connectionLogDeduplicator.log(
348
- 'ip-rejected',
349
- 'warn',
350
- `HttpProxy connection rejected (via SmartProxy)`,
351
- { remoteIP, reason: ipValidation.reason, component: 'http-proxy' },
352
- remoteIP
353
- );
354
- connection.destroy();
355
- return;
356
- }
357
-
358
- // Track connection by real IP
359
- this.securityManager.trackConnectionByIP(remoteIP, connectionId);
360
- }
361
- };
362
-
363
- // Set timeout for header parsing
364
- headerTimer = setTimeout(() => {
365
- connection.removeListener('data', onData);
366
- this.logger.warn('Header parsing timeout, closing connection');
367
- connection.destroy();
368
- }, HEADER_TIMEOUT_MS);
369
-
370
- // Unref the timer so it doesn't keep the process alive
371
- if (headerTimer.unref) headerTimer.unref();
372
-
373
- // Use prependListener to get data first
374
- connection.prependListener('data', onData);
375
- } else {
376
- // Direct connection - validate immediately
377
- const ipValidation = this.securityManager.validateIP(remoteIP);
378
- if (!ipValidation.allowed) {
379
- connectionLogDeduplicator.log(
380
- 'ip-rejected',
381
- 'warn',
382
- `HttpProxy connection rejected`,
383
- { remoteIP, reason: ipValidation.reason, component: 'http-proxy' },
384
- remoteIP
385
- );
386
- connection.destroy();
387
- return;
388
- }
389
-
390
- // Track connection by IP
391
- this.securityManager.trackConnectionByIP(remoteIP, connectionId);
392
- }
393
-
394
- // Then check global max connections
395
- if (this.socketMap.getArray().length >= this.options.maxConnections) {
396
- connectionLogDeduplicator.log(
397
- 'connection-rejected',
398
- 'warn',
399
- 'HttpProxy max connections reached',
400
- {
401
- reason: 'global-limit',
402
- currentConnections: this.socketMap.getArray().length,
403
- maxConnections: this.options.maxConnections,
404
- component: 'http-proxy'
405
- },
406
- 'http-proxy-global-limit'
407
- );
408
- connection.destroy();
409
- return;
410
- }
411
-
412
- // Add connection to tracking with metadata
413
- connection._connectionId = connectionId;
414
- connection._remoteIP = remoteIP;
415
- this.socketMap.add(connection);
416
- this.connectedClients = this.socketMap.getArray().length;
417
-
418
- // Check for connection from SmartProxy by inspecting the source port
419
- const localPort = connection.localPort || 0;
420
- const remotePort = connection.remotePort || 0;
421
-
422
- // If this connection is from a SmartProxy
423
- if (isFromSmartProxy) {
424
- this.portProxyConnections++;
425
- this.logger.debug(`New connection from SmartProxy for client ${remoteIP} (local: ${localPort}, remote: ${remotePort})`);
426
- } else {
427
- this.logger.debug(`New direct connection from ${remoteIP} (local: ${localPort}, remote: ${remotePort})`);
428
- }
429
-
430
- // Setup connection cleanup handlers
431
- const cleanupConnection = () => {
432
- if (this.socketMap.checkForObject(connection)) {
433
- this.socketMap.remove(connection);
434
- this.connectedClients = this.socketMap.getArray().length;
435
-
436
- // Remove IP tracking
437
- const connId = connection._connectionId;
438
- const connIP = connection._realRemoteIP || connection._remoteIP;
439
- if (connId && connIP) {
440
- this.securityManager.removeConnectionByIP(connIP, connId);
441
- }
442
-
443
- // If this was a SmartProxy connection, decrement the counter
444
- if (this.options.portProxyIntegration && connection.remoteAddress?.includes('127.0.0.1')) {
445
- this.portProxyConnections--;
446
- }
447
-
448
- this.logger.debug(`Connection closed from ${connIP || 'unknown'}. ${this.connectedClients} connections remaining`);
449
- }
450
- };
451
-
452
- connection.on('close', cleanupConnection);
453
- connection.on('error', (err) => {
454
- this.logger.debug('Connection error', err);
455
- cleanupConnection();
456
- });
457
- connection.on('end', cleanupConnection);
458
- });
459
-
460
- // Track TLS handshake completions
461
- this.httpsServer.on('secureConnection', (tlsSocket) => {
462
- this.tlsTerminatedConnections++;
463
- this.logger.debug('TLS handshake completed, connection secured');
464
- });
465
- }
466
-
467
- /**
468
- * Sets up metrics collection
469
- */
470
- private setupMetricsCollection(): void {
471
- this.metricsInterval = setInterval(() => {
472
- const uptime = Math.floor((Date.now() - this.startTime) / 1000);
473
- const metrics = {
474
- uptime,
475
- activeConnections: this.connectedClients,
476
- totalRequests: this.requestsServed,
477
- failedRequests: this.failedRequests,
478
- portProxyConnections: this.portProxyConnections,
479
- tlsTerminatedConnections: this.tlsTerminatedConnections,
480
- activeWebSockets: this.webSocketHandler.getConnectionInfo().activeConnections,
481
- memoryUsage: process.memoryUsage(),
482
- activeContexts: Array.from(this.activeContexts),
483
- connectionPool: this.connectionPool.getPoolStatus()
484
- };
485
-
486
- this.logger.debug('Proxy metrics', metrics);
487
- }, 60000); // Log metrics every minute
488
-
489
- // Don't keep process alive just for metrics
490
- if (this.metricsInterval.unref) {
491
- this.metricsInterval.unref();
492
- }
493
- }
494
-
495
- /**
496
- * Updates the route configurations - this is the primary method for configuring HttpProxy
497
- * @param routes The new route configurations to use
498
- */
499
- public async updateRouteConfigs(routes: IRouteConfig[]): Promise<void> {
500
- this.logger.info(`Updating route configurations (${routes.length} routes)`);
501
-
502
- // Update routes in RouteManager, modern router, WebSocketHandler, and SecurityManager
503
- this.routeManager.updateRoutes(routes);
504
- this.router.setRoutes(routes);
505
- this.webSocketHandler.setRoutes(routes);
506
- this.requestHandler.securityManager.setRoutes(routes);
507
- this.routes = routes;
508
-
509
- // Directly update the certificate manager with the new routes
510
- // This will extract domains and handle certificate provisioning
511
- this.certificateManager.updateRoutes(routes);
512
-
513
- // Collect all domains and certificates for configuration
514
- const currentHostnames = new Set<string>();
515
- const certificateUpdates = new Map<string, { cert: string, key: string }>();
516
-
517
- // Process each route to extract domain and certificate information
518
- for (const route of routes) {
519
- // Skip non-forward routes or routes without domains
520
- if (route.action.type !== 'forward' || !route.match.domains) {
521
- continue;
522
- }
523
-
524
- // Get domains from route
525
- const domains = Array.isArray(route.match.domains)
526
- ? route.match.domains
527
- : [route.match.domains];
528
-
529
- // Process each domain
530
- for (const domain of domains) {
531
- // Skip wildcard domains for direct host configuration
532
- if (domain.includes('*')) {
533
- continue;
534
- }
535
-
536
- currentHostnames.add(domain);
537
-
538
- // Check if we have a static certificate for this domain
539
- if (route.action.tls?.certificate && route.action.tls.certificate !== 'auto') {
540
- certificateUpdates.set(domain, {
541
- cert: route.action.tls.certificate.cert,
542
- key: route.action.tls.certificate.key
543
- });
544
- }
545
- }
546
- }
547
-
548
- // Update certificate cache with any static certificates
549
- for (const [domain, certData] of certificateUpdates.entries()) {
550
- try {
551
- this.certificateManager.updateCertificate(
552
- domain,
553
- certData.cert,
554
- certData.key
555
- );
556
-
557
- this.activeContexts.add(domain);
558
- } catch (error) {
559
- this.logger.error(`Failed to add SSL context for ${domain}`, error);
560
- }
561
- }
562
-
563
- // Clean up removed contexts
564
- for (const hostname of this.activeContexts) {
565
- if (!currentHostnames.has(hostname)) {
566
- this.logger.info(`Hostname ${hostname} removed from configuration`);
567
- this.activeContexts.delete(hostname);
568
- }
569
- }
570
-
571
- // Update the router with new routes
572
- this.router.setRoutes(routes);
573
-
574
- // Update WebSocket handler with new routes
575
- this.webSocketHandler.setRoutes(routes);
576
-
577
- this.logger.info(`Route configuration updated with ${routes.length} routes`);
578
- }
579
-
580
- // Legacy methods have been removed.
581
- // Please use updateRouteConfigs() directly with modern route-based configuration.
582
-
583
- /**
584
- * Adds default headers to be included in all responses
585
- */
586
- public async addDefaultHeaders(headersArg: { [key: string]: string }): Promise<void> {
587
- this.logger.info('Adding default headers', headersArg);
588
- this.requestHandler.setDefaultHeaders(headersArg);
589
- }
590
-
591
- /**
592
- * Stops the proxy server
593
- */
594
- public async stop(): Promise<void> {
595
- this.logger.info('Stopping HttpProxy server');
596
-
597
- // Clear intervals
598
- if (this.metricsInterval) {
599
- clearInterval(this.metricsInterval);
600
- }
601
-
602
- if (this.connectionPoolCleanupInterval) {
603
- clearInterval(this.connectionPoolCleanupInterval);
604
- }
605
-
606
- // Stop WebSocket handler
607
- this.webSocketHandler.shutdown();
608
-
609
- // Destroy request handler (cleans up intervals and caches)
610
- if (this.requestHandler && typeof this.requestHandler.destroy === 'function') {
611
- this.requestHandler.destroy();
612
- }
613
-
614
- // Close all tracked sockets
615
- const socketCleanupPromises = this.socketMap.getArray().map(socket =>
616
- cleanupSocket(socket, 'http-proxy-stop', { immediate: true })
617
- );
618
- await Promise.all(socketCleanupPromises);
619
-
620
- // Close all connection pool connections
621
- this.connectionPool.closeAllConnections();
622
-
623
- // Certificate management cleanup is handled by SmartCertManager
624
-
625
- // Flush any pending deduplicated logs
626
- connectionLogDeduplicator.flushAll();
627
-
628
- // Close the HTTPS server
629
- return new Promise((resolve) => {
630
- this.httpsServer.close(() => {
631
- this.logger.info('HttpProxy server stopped successfully');
632
- resolve();
633
- });
634
- });
635
- }
636
-
637
- /**
638
- * Requests a new certificate for a domain
639
- * This can be used to manually trigger certificate issuance
640
- * @param domain The domain to request a certificate for
641
- * @returns A promise that resolves when the request is submitted (not when the certificate is issued)
642
- */
643
- public async requestCertificate(domain: string): Promise<boolean> {
644
- this.logger.warn('requestCertificate is deprecated - use SmartCertManager instead');
645
- return false;
646
- }
647
-
648
- /**
649
- * Update certificate for a domain
650
- *
651
- * This method allows direct updates of certificates from external sources
652
- * like Port80Handler or custom certificate providers.
653
- *
654
- * @param domain The domain to update certificate for
655
- * @param certificate The new certificate (public key)
656
- * @param privateKey The new private key
657
- * @param expiryDate Optional expiry date
658
- */
659
- public updateCertificate(
660
- domain: string,
661
- certificate: string,
662
- privateKey: string,
663
- expiryDate?: Date
664
- ): void {
665
- this.logger.info(`Updating certificate for ${domain}`);
666
- this.certificateManager.updateCertificate(domain, certificate, privateKey);
667
- }
668
-
669
- /**
670
- * Gets all route configurations currently in use
671
- */
672
- public getRouteConfigs(): IRouteConfig[] {
673
- return this.routeManager.getRoutes();
674
- }
675
- }