@push.rocks/smartproxy 5.1.0 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist_ts/classes.pp.interfaces.d.ts +23 -0
- package/dist_ts/classes.pp.networkproxybridge.d.ts +15 -1
- package/dist_ts/classes.pp.networkproxybridge.js +116 -21
- package/dist_ts/classes.pp.portproxy.d.ts +20 -4
- package/dist_ts/classes.pp.portproxy.js +321 -22
- package/dist_ts/index.d.ts +6 -6
- package/dist_ts/index.js +7 -7
- package/dist_ts/networkproxy/classes.np.certificatemanager.d.ts +77 -0
- package/dist_ts/networkproxy/classes.np.certificatemanager.js +354 -0
- package/dist_ts/networkproxy/classes.np.connectionpool.d.ts +47 -0
- package/dist_ts/networkproxy/classes.np.connectionpool.js +210 -0
- package/dist_ts/networkproxy/classes.np.networkproxy.d.ts +117 -0
- package/dist_ts/networkproxy/classes.np.networkproxy.js +375 -0
- package/dist_ts/networkproxy/classes.np.requesthandler.d.ts +51 -0
- package/dist_ts/networkproxy/classes.np.requesthandler.js +210 -0
- package/dist_ts/networkproxy/classes.np.types.d.ts +82 -0
- package/dist_ts/networkproxy/classes.np.types.js +35 -0
- package/dist_ts/networkproxy/classes.np.websockethandler.d.ts +38 -0
- package/dist_ts/networkproxy/classes.np.websockethandler.js +188 -0
- package/dist_ts/networkproxy/index.d.ts +6 -0
- package/dist_ts/networkproxy/index.js +8 -0
- package/dist_ts/nfttablesproxy/classes.nftablesproxy.d.ts +219 -0
- package/dist_ts/nfttablesproxy/classes.nftablesproxy.js +1542 -0
- package/dist_ts/port80handler/classes.port80handler.d.ts +260 -0
- package/dist_ts/port80handler/classes.port80handler.js +928 -0
- package/dist_ts/smartproxy/classes.pp.connectionhandler.d.ts +39 -0
- package/dist_ts/smartproxy/classes.pp.connectionhandler.js +754 -0
- package/dist_ts/smartproxy/classes.pp.connectionmanager.d.ts +78 -0
- package/dist_ts/smartproxy/classes.pp.connectionmanager.js +378 -0
- package/dist_ts/smartproxy/classes.pp.domainconfigmanager.d.ts +55 -0
- package/dist_ts/smartproxy/classes.pp.domainconfigmanager.js +103 -0
- package/dist_ts/smartproxy/classes.pp.interfaces.d.ts +133 -0
- package/dist_ts/smartproxy/classes.pp.interfaces.js +2 -0
- package/dist_ts/smartproxy/classes.pp.networkproxybridge.d.ts +57 -0
- package/dist_ts/smartproxy/classes.pp.networkproxybridge.js +306 -0
- package/dist_ts/smartproxy/classes.pp.portrangemanager.d.ts +56 -0
- package/dist_ts/smartproxy/classes.pp.portrangemanager.js +179 -0
- package/dist_ts/smartproxy/classes.pp.securitymanager.d.ts +47 -0
- package/dist_ts/smartproxy/classes.pp.securitymanager.js +126 -0
- package/dist_ts/smartproxy/classes.pp.snihandler.d.ts +153 -0
- package/dist_ts/smartproxy/classes.pp.snihandler.js +1053 -0
- package/dist_ts/smartproxy/classes.pp.timeoutmanager.d.ts +47 -0
- package/dist_ts/smartproxy/classes.pp.timeoutmanager.js +154 -0
- package/dist_ts/smartproxy/classes.pp.tlsalert.d.ts +149 -0
- package/dist_ts/smartproxy/classes.pp.tlsalert.js +225 -0
- package/dist_ts/smartproxy/classes.pp.tlsmanager.d.ts +57 -0
- package/dist_ts/smartproxy/classes.pp.tlsmanager.js +132 -0
- package/dist_ts/smartproxy/classes.smartproxy.d.ts +64 -0
- package/dist_ts/smartproxy/classes.smartproxy.js +567 -0
- package/package.json +1 -1
- package/ts/index.ts +6 -6
- package/ts/networkproxy/classes.np.certificatemanager.ts +398 -0
- package/ts/networkproxy/classes.np.connectionpool.ts +241 -0
- package/ts/networkproxy/classes.np.networkproxy.ts +469 -0
- package/ts/networkproxy/classes.np.requesthandler.ts +278 -0
- package/ts/networkproxy/classes.np.types.ts +123 -0
- package/ts/networkproxy/classes.np.websockethandler.ts +226 -0
- package/ts/networkproxy/index.ts +7 -0
- package/ts/{classes.port80handler.ts → port80handler/classes.port80handler.ts} +249 -1
- package/ts/{classes.pp.connectionhandler.ts → smartproxy/classes.pp.connectionhandler.ts} +1 -1
- package/ts/{classes.pp.connectionmanager.ts → smartproxy/classes.pp.connectionmanager.ts} +1 -1
- package/ts/{classes.pp.domainconfigmanager.ts → smartproxy/classes.pp.domainconfigmanager.ts} +1 -1
- package/ts/{classes.pp.interfaces.ts → smartproxy/classes.pp.interfaces.ts} +31 -5
- package/ts/{classes.pp.networkproxybridge.ts → smartproxy/classes.pp.networkproxybridge.ts} +129 -28
- package/ts/{classes.pp.securitymanager.ts → smartproxy/classes.pp.securitymanager.ts} +1 -1
- package/ts/{classes.pp.tlsmanager.ts → smartproxy/classes.pp.tlsmanager.ts} +1 -1
- package/ts/smartproxy/classes.smartproxy.ts +679 -0
- package/ts/classes.networkproxy.ts +0 -1730
- package/ts/classes.pp.acmemanager.ts +0 -149
- package/ts/classes.pp.portproxy.ts +0 -344
- /package/ts/{classes.nftablesproxy.ts → nfttablesproxy/classes.nftablesproxy.ts} +0 -0
- /package/ts/{classes.pp.portrangemanager.ts → smartproxy/classes.pp.portrangemanager.ts} +0 -0
- /package/ts/{classes.pp.snihandler.ts → smartproxy/classes.pp.snihandler.ts} +0 -0
- /package/ts/{classes.pp.timeoutmanager.ts → smartproxy/classes.pp.timeoutmanager.ts} +0 -0
- /package/ts/{classes.pp.tlsalert.ts → smartproxy/classes.pp.tlsalert.ts} +0 -0
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
import * as plugins from '../plugins.js';
|
|
2
|
+
import { type INetworkProxyOptions, type ILogger, createLogger, type IReverseProxyConfig } from './classes.np.types.js';
|
|
3
|
+
import { CertificateManager } from './classes.np.certificatemanager.js';
|
|
4
|
+
import { ConnectionPool } from './classes.np.connectionpool.js';
|
|
5
|
+
import { RequestHandler, type IMetricsTracker } from './classes.np.requesthandler.js';
|
|
6
|
+
import { WebSocketHandler } from './classes.np.websockethandler.js';
|
|
7
|
+
import { ProxyRouter } from '../classes.router.js';
|
|
8
|
+
import { Port80Handler } from '../port80handler/classes.port80handler.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* NetworkProxy provides a reverse proxy with TLS termination, WebSocket support,
|
|
12
|
+
* automatic certificate management, and high-performance connection pooling.
|
|
13
|
+
*/
|
|
14
|
+
export class NetworkProxy implements IMetricsTracker {
|
|
15
|
+
// Configuration
|
|
16
|
+
public options: INetworkProxyOptions;
|
|
17
|
+
public proxyConfigs: IReverseProxyConfig[] = [];
|
|
18
|
+
|
|
19
|
+
// Server instances
|
|
20
|
+
public httpsServer: plugins.https.Server;
|
|
21
|
+
|
|
22
|
+
// Core components
|
|
23
|
+
private certificateManager: CertificateManager;
|
|
24
|
+
private connectionPool: ConnectionPool;
|
|
25
|
+
private requestHandler: RequestHandler;
|
|
26
|
+
private webSocketHandler: WebSocketHandler;
|
|
27
|
+
private router = new ProxyRouter();
|
|
28
|
+
|
|
29
|
+
// State tracking
|
|
30
|
+
public socketMap = new plugins.lik.ObjectMap<plugins.net.Socket>();
|
|
31
|
+
public activeContexts: Set<string> = new Set();
|
|
32
|
+
public connectedClients: number = 0;
|
|
33
|
+
public startTime: number = 0;
|
|
34
|
+
public requestsServed: number = 0;
|
|
35
|
+
public failedRequests: number = 0;
|
|
36
|
+
|
|
37
|
+
// Tracking for PortProxy integration
|
|
38
|
+
private portProxyConnections: number = 0;
|
|
39
|
+
private tlsTerminatedConnections: number = 0;
|
|
40
|
+
|
|
41
|
+
// Timers
|
|
42
|
+
private metricsInterval: NodeJS.Timeout;
|
|
43
|
+
private connectionPoolCleanupInterval: NodeJS.Timeout;
|
|
44
|
+
|
|
45
|
+
// Logger
|
|
46
|
+
private logger: ILogger;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Creates a new NetworkProxy instance
|
|
50
|
+
*/
|
|
51
|
+
constructor(optionsArg: INetworkProxyOptions) {
|
|
52
|
+
// Set default options
|
|
53
|
+
this.options = {
|
|
54
|
+
port: optionsArg.port,
|
|
55
|
+
maxConnections: optionsArg.maxConnections || 10000,
|
|
56
|
+
keepAliveTimeout: optionsArg.keepAliveTimeout || 120000, // 2 minutes
|
|
57
|
+
headersTimeout: optionsArg.headersTimeout || 60000, // 1 minute
|
|
58
|
+
logLevel: optionsArg.logLevel || 'info',
|
|
59
|
+
cors: optionsArg.cors || {
|
|
60
|
+
allowOrigin: '*',
|
|
61
|
+
allowMethods: 'GET, POST, PUT, DELETE, OPTIONS',
|
|
62
|
+
allowHeaders: 'Content-Type, Authorization',
|
|
63
|
+
maxAge: 86400
|
|
64
|
+
},
|
|
65
|
+
// Defaults for PortProxy integration
|
|
66
|
+
connectionPoolSize: optionsArg.connectionPoolSize || 50,
|
|
67
|
+
portProxyIntegration: optionsArg.portProxyIntegration || false,
|
|
68
|
+
useExternalPort80Handler: optionsArg.useExternalPort80Handler || false,
|
|
69
|
+
// Default ACME options
|
|
70
|
+
acme: {
|
|
71
|
+
enabled: optionsArg.acme?.enabled || false,
|
|
72
|
+
port: optionsArg.acme?.port || 80,
|
|
73
|
+
contactEmail: optionsArg.acme?.contactEmail || 'admin@example.com',
|
|
74
|
+
useProduction: optionsArg.acme?.useProduction || false, // Default to staging for safety
|
|
75
|
+
renewThresholdDays: optionsArg.acme?.renewThresholdDays || 30,
|
|
76
|
+
autoRenew: optionsArg.acme?.autoRenew !== false, // Default to true
|
|
77
|
+
certificateStore: optionsArg.acme?.certificateStore || './certs',
|
|
78
|
+
skipConfiguredCerts: optionsArg.acme?.skipConfiguredCerts || false
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// Initialize logger
|
|
83
|
+
this.logger = createLogger(this.options.logLevel);
|
|
84
|
+
|
|
85
|
+
// Initialize components
|
|
86
|
+
this.certificateManager = new CertificateManager(this.options);
|
|
87
|
+
this.connectionPool = new ConnectionPool(this.options);
|
|
88
|
+
this.requestHandler = new RequestHandler(this.options, this.connectionPool, this.router);
|
|
89
|
+
this.webSocketHandler = new WebSocketHandler(this.options, this.connectionPool, this.router);
|
|
90
|
+
|
|
91
|
+
// Connect request handler to this metrics tracker
|
|
92
|
+
this.requestHandler.setMetricsTracker(this);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Implements IMetricsTracker interface to increment request counters
|
|
97
|
+
*/
|
|
98
|
+
public incrementRequestsServed(): void {
|
|
99
|
+
this.requestsServed++;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Implements IMetricsTracker interface to increment failed request counters
|
|
104
|
+
*/
|
|
105
|
+
public incrementFailedRequests(): void {
|
|
106
|
+
this.failedRequests++;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Returns the port number this NetworkProxy is listening on
|
|
111
|
+
* Useful for PortProxy to determine where to forward connections
|
|
112
|
+
*/
|
|
113
|
+
public getListeningPort(): number {
|
|
114
|
+
return this.options.port;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Updates the server capacity settings
|
|
119
|
+
* @param maxConnections Maximum number of simultaneous connections
|
|
120
|
+
* @param keepAliveTimeout Keep-alive timeout in milliseconds
|
|
121
|
+
* @param connectionPoolSize Size of the connection pool per backend
|
|
122
|
+
*/
|
|
123
|
+
public updateCapacity(maxConnections?: number, keepAliveTimeout?: number, connectionPoolSize?: number): void {
|
|
124
|
+
if (maxConnections !== undefined) {
|
|
125
|
+
this.options.maxConnections = maxConnections;
|
|
126
|
+
this.logger.info(`Updated max connections to ${maxConnections}`);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (keepAliveTimeout !== undefined) {
|
|
130
|
+
this.options.keepAliveTimeout = keepAliveTimeout;
|
|
131
|
+
|
|
132
|
+
if (this.httpsServer) {
|
|
133
|
+
this.httpsServer.keepAliveTimeout = keepAliveTimeout;
|
|
134
|
+
this.logger.info(`Updated keep-alive timeout to ${keepAliveTimeout}ms`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (connectionPoolSize !== undefined) {
|
|
139
|
+
this.options.connectionPoolSize = connectionPoolSize;
|
|
140
|
+
this.logger.info(`Updated connection pool size to ${connectionPoolSize}`);
|
|
141
|
+
|
|
142
|
+
// Clean up excess connections in the pool
|
|
143
|
+
this.connectionPool.cleanupConnectionPool();
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Returns current server metrics
|
|
149
|
+
* Useful for PortProxy to determine which NetworkProxy to use for load balancing
|
|
150
|
+
*/
|
|
151
|
+
public getMetrics(): any {
|
|
152
|
+
return {
|
|
153
|
+
activeConnections: this.connectedClients,
|
|
154
|
+
totalRequests: this.requestsServed,
|
|
155
|
+
failedRequests: this.failedRequests,
|
|
156
|
+
portProxyConnections: this.portProxyConnections,
|
|
157
|
+
tlsTerminatedConnections: this.tlsTerminatedConnections,
|
|
158
|
+
connectionPoolSize: this.connectionPool.getPoolStatus(),
|
|
159
|
+
uptime: Math.floor((Date.now() - this.startTime) / 1000),
|
|
160
|
+
memoryUsage: process.memoryUsage(),
|
|
161
|
+
activeWebSockets: this.webSocketHandler.getConnectionInfo().activeConnections
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Sets an external Port80Handler for certificate management
|
|
167
|
+
* This allows the NetworkProxy to use a centrally managed Port80Handler
|
|
168
|
+
* instead of creating its own
|
|
169
|
+
*
|
|
170
|
+
* @param handler The Port80Handler instance to use
|
|
171
|
+
*/
|
|
172
|
+
public setExternalPort80Handler(handler: Port80Handler): void {
|
|
173
|
+
// Connect it to the certificate manager
|
|
174
|
+
this.certificateManager.setExternalPort80Handler(handler);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Starts the proxy server
|
|
179
|
+
*/
|
|
180
|
+
public async start(): Promise<void> {
|
|
181
|
+
this.startTime = Date.now();
|
|
182
|
+
|
|
183
|
+
// Initialize Port80Handler if enabled and not using external handler
|
|
184
|
+
if (this.options.acme?.enabled && !this.options.useExternalPort80Handler) {
|
|
185
|
+
await this.certificateManager.initializePort80Handler();
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Create the HTTPS server
|
|
189
|
+
this.httpsServer = plugins.https.createServer(
|
|
190
|
+
{
|
|
191
|
+
key: this.certificateManager.getDefaultCertificates().key,
|
|
192
|
+
cert: this.certificateManager.getDefaultCertificates().cert,
|
|
193
|
+
SNICallback: (domain, cb) => this.certificateManager.handleSNI(domain, cb)
|
|
194
|
+
},
|
|
195
|
+
(req, res) => this.requestHandler.handleRequest(req, res)
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
// Configure server timeouts
|
|
199
|
+
this.httpsServer.keepAliveTimeout = this.options.keepAliveTimeout;
|
|
200
|
+
this.httpsServer.headersTimeout = this.options.headersTimeout;
|
|
201
|
+
|
|
202
|
+
// Setup connection tracking
|
|
203
|
+
this.setupConnectionTracking();
|
|
204
|
+
|
|
205
|
+
// Share HTTPS server with certificate manager
|
|
206
|
+
this.certificateManager.setHttpsServer(this.httpsServer);
|
|
207
|
+
|
|
208
|
+
// Setup WebSocket support
|
|
209
|
+
this.webSocketHandler.initialize(this.httpsServer);
|
|
210
|
+
|
|
211
|
+
// Start metrics collection
|
|
212
|
+
this.setupMetricsCollection();
|
|
213
|
+
|
|
214
|
+
// Setup connection pool cleanup interval
|
|
215
|
+
this.connectionPoolCleanupInterval = this.connectionPool.setupPeriodicCleanup();
|
|
216
|
+
|
|
217
|
+
// Start the server
|
|
218
|
+
return new Promise((resolve) => {
|
|
219
|
+
this.httpsServer.listen(this.options.port, () => {
|
|
220
|
+
this.logger.info(`NetworkProxy started on port ${this.options.port}`);
|
|
221
|
+
resolve();
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Sets up tracking of TCP connections
|
|
228
|
+
*/
|
|
229
|
+
private setupConnectionTracking(): void {
|
|
230
|
+
this.httpsServer.on('connection', (connection: plugins.net.Socket) => {
|
|
231
|
+
// Check if max connections reached
|
|
232
|
+
if (this.socketMap.getArray().length >= this.options.maxConnections) {
|
|
233
|
+
this.logger.warn(`Max connections (${this.options.maxConnections}) reached, rejecting new connection`);
|
|
234
|
+
connection.destroy();
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Add connection to tracking
|
|
239
|
+
this.socketMap.add(connection);
|
|
240
|
+
this.connectedClients = this.socketMap.getArray().length;
|
|
241
|
+
|
|
242
|
+
// Check for connection from PortProxy by inspecting the source port
|
|
243
|
+
const localPort = connection.localPort || 0;
|
|
244
|
+
const remotePort = connection.remotePort || 0;
|
|
245
|
+
|
|
246
|
+
// If this connection is from a PortProxy (usually indicated by it coming from localhost)
|
|
247
|
+
if (this.options.portProxyIntegration && connection.remoteAddress?.includes('127.0.0.1')) {
|
|
248
|
+
this.portProxyConnections++;
|
|
249
|
+
this.logger.debug(`New connection from PortProxy (local: ${localPort}, remote: ${remotePort})`);
|
|
250
|
+
} else {
|
|
251
|
+
this.logger.debug(`New direct connection (local: ${localPort}, remote: ${remotePort})`);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Setup connection cleanup handlers
|
|
255
|
+
const cleanupConnection = () => {
|
|
256
|
+
if (this.socketMap.checkForObject(connection)) {
|
|
257
|
+
this.socketMap.remove(connection);
|
|
258
|
+
this.connectedClients = this.socketMap.getArray().length;
|
|
259
|
+
|
|
260
|
+
// If this was a PortProxy connection, decrement the counter
|
|
261
|
+
if (this.options.portProxyIntegration && connection.remoteAddress?.includes('127.0.0.1')) {
|
|
262
|
+
this.portProxyConnections--;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
this.logger.debug(`Connection closed. ${this.connectedClients} connections remaining`);
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
connection.on('close', cleanupConnection);
|
|
270
|
+
connection.on('error', (err) => {
|
|
271
|
+
this.logger.debug('Connection error', err);
|
|
272
|
+
cleanupConnection();
|
|
273
|
+
});
|
|
274
|
+
connection.on('end', cleanupConnection);
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
// Track TLS handshake completions
|
|
278
|
+
this.httpsServer.on('secureConnection', (tlsSocket) => {
|
|
279
|
+
this.tlsTerminatedConnections++;
|
|
280
|
+
this.logger.debug('TLS handshake completed, connection secured');
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Sets up metrics collection
|
|
286
|
+
*/
|
|
287
|
+
private setupMetricsCollection(): void {
|
|
288
|
+
this.metricsInterval = setInterval(() => {
|
|
289
|
+
const uptime = Math.floor((Date.now() - this.startTime) / 1000);
|
|
290
|
+
const metrics = {
|
|
291
|
+
uptime,
|
|
292
|
+
activeConnections: this.connectedClients,
|
|
293
|
+
totalRequests: this.requestsServed,
|
|
294
|
+
failedRequests: this.failedRequests,
|
|
295
|
+
portProxyConnections: this.portProxyConnections,
|
|
296
|
+
tlsTerminatedConnections: this.tlsTerminatedConnections,
|
|
297
|
+
activeWebSockets: this.webSocketHandler.getConnectionInfo().activeConnections,
|
|
298
|
+
memoryUsage: process.memoryUsage(),
|
|
299
|
+
activeContexts: Array.from(this.activeContexts),
|
|
300
|
+
connectionPool: this.connectionPool.getPoolStatus()
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
this.logger.debug('Proxy metrics', metrics);
|
|
304
|
+
}, 60000); // Log metrics every minute
|
|
305
|
+
|
|
306
|
+
// Don't keep process alive just for metrics
|
|
307
|
+
if (this.metricsInterval.unref) {
|
|
308
|
+
this.metricsInterval.unref();
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Updates proxy configurations
|
|
314
|
+
*/
|
|
315
|
+
public async updateProxyConfigs(
|
|
316
|
+
proxyConfigsArg: plugins.tsclass.network.IReverseProxyConfig[]
|
|
317
|
+
): Promise<void> {
|
|
318
|
+
this.logger.info(`Updating proxy configurations (${proxyConfigsArg.length} configs)`);
|
|
319
|
+
|
|
320
|
+
// Update internal configs
|
|
321
|
+
this.proxyConfigs = proxyConfigsArg;
|
|
322
|
+
this.router.setNewProxyConfigs(proxyConfigsArg);
|
|
323
|
+
|
|
324
|
+
// Collect all hostnames for cleanup later
|
|
325
|
+
const currentHostNames = new Set<string>();
|
|
326
|
+
|
|
327
|
+
// Add/update SSL contexts for each host
|
|
328
|
+
for (const config of proxyConfigsArg) {
|
|
329
|
+
currentHostNames.add(config.hostName);
|
|
330
|
+
|
|
331
|
+
try {
|
|
332
|
+
// Update certificate in cache
|
|
333
|
+
this.certificateManager.updateCertificateCache(
|
|
334
|
+
config.hostName,
|
|
335
|
+
config.publicKey,
|
|
336
|
+
config.privateKey
|
|
337
|
+
);
|
|
338
|
+
|
|
339
|
+
this.activeContexts.add(config.hostName);
|
|
340
|
+
} catch (error) {
|
|
341
|
+
this.logger.error(`Failed to add SSL context for ${config.hostName}`, error);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Clean up removed contexts
|
|
346
|
+
for (const hostname of this.activeContexts) {
|
|
347
|
+
if (!currentHostNames.has(hostname)) {
|
|
348
|
+
this.logger.info(`Hostname ${hostname} removed from configuration`);
|
|
349
|
+
this.activeContexts.delete(hostname);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// Register domains with Port80Handler if available
|
|
354
|
+
const domainsForACME = Array.from(currentHostNames)
|
|
355
|
+
.filter(domain => !domain.includes('*')); // Skip wildcard domains
|
|
356
|
+
|
|
357
|
+
this.certificateManager.registerDomainsWithPort80Handler(domainsForACME);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Converts PortProxy domain configurations to NetworkProxy configs
|
|
362
|
+
* @param domainConfigs PortProxy domain configs
|
|
363
|
+
* @param sslKeyPair Default SSL key pair to use if not specified
|
|
364
|
+
* @returns Array of NetworkProxy configs
|
|
365
|
+
*/
|
|
366
|
+
public convertPortProxyConfigs(
|
|
367
|
+
domainConfigs: Array<{
|
|
368
|
+
domains: string[];
|
|
369
|
+
targetIPs?: string[];
|
|
370
|
+
allowedIPs?: string[];
|
|
371
|
+
}>,
|
|
372
|
+
sslKeyPair?: { key: string; cert: string }
|
|
373
|
+
): plugins.tsclass.network.IReverseProxyConfig[] {
|
|
374
|
+
const proxyConfigs: plugins.tsclass.network.IReverseProxyConfig[] = [];
|
|
375
|
+
|
|
376
|
+
// Use default certificates if not provided
|
|
377
|
+
const defaultCerts = this.certificateManager.getDefaultCertificates();
|
|
378
|
+
const sslKey = sslKeyPair?.key || defaultCerts.key;
|
|
379
|
+
const sslCert = sslKeyPair?.cert || defaultCerts.cert;
|
|
380
|
+
|
|
381
|
+
for (const domainConfig of domainConfigs) {
|
|
382
|
+
// Each domain in the domains array gets its own config
|
|
383
|
+
for (const domain of domainConfig.domains) {
|
|
384
|
+
// Skip non-hostname patterns (like IP addresses)
|
|
385
|
+
if (domain.match(/^\d+\.\d+\.\d+\.\d+$/) || domain === '*' || domain === 'localhost') {
|
|
386
|
+
continue;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
proxyConfigs.push({
|
|
390
|
+
hostName: domain,
|
|
391
|
+
destinationIps: domainConfig.targetIPs || ['localhost'],
|
|
392
|
+
destinationPorts: [this.options.port], // Use the NetworkProxy port
|
|
393
|
+
privateKey: sslKey,
|
|
394
|
+
publicKey: sslCert
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
this.logger.info(`Converted ${domainConfigs.length} PortProxy configs to ${proxyConfigs.length} NetworkProxy configs`);
|
|
400
|
+
return proxyConfigs;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Adds default headers to be included in all responses
|
|
405
|
+
*/
|
|
406
|
+
public async addDefaultHeaders(headersArg: { [key: string]: string }): Promise<void> {
|
|
407
|
+
this.logger.info('Adding default headers', headersArg);
|
|
408
|
+
this.requestHandler.setDefaultHeaders(headersArg);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Stops the proxy server
|
|
413
|
+
*/
|
|
414
|
+
public async stop(): Promise<void> {
|
|
415
|
+
this.logger.info('Stopping NetworkProxy server');
|
|
416
|
+
|
|
417
|
+
// Clear intervals
|
|
418
|
+
if (this.metricsInterval) {
|
|
419
|
+
clearInterval(this.metricsInterval);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
if (this.connectionPoolCleanupInterval) {
|
|
423
|
+
clearInterval(this.connectionPoolCleanupInterval);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// Stop WebSocket handler
|
|
427
|
+
this.webSocketHandler.shutdown();
|
|
428
|
+
|
|
429
|
+
// Close all tracked sockets
|
|
430
|
+
for (const socket of this.socketMap.getArray()) {
|
|
431
|
+
try {
|
|
432
|
+
socket.destroy();
|
|
433
|
+
} catch (error) {
|
|
434
|
+
this.logger.error('Error destroying socket', error);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// Close all connection pool connections
|
|
439
|
+
this.connectionPool.closeAllConnections();
|
|
440
|
+
|
|
441
|
+
// Stop Port80Handler if internally managed
|
|
442
|
+
await this.certificateManager.stopPort80Handler();
|
|
443
|
+
|
|
444
|
+
// Close the HTTPS server
|
|
445
|
+
return new Promise((resolve) => {
|
|
446
|
+
this.httpsServer.close(() => {
|
|
447
|
+
this.logger.info('NetworkProxy server stopped successfully');
|
|
448
|
+
resolve();
|
|
449
|
+
});
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* Requests a new certificate for a domain
|
|
455
|
+
* This can be used to manually trigger certificate issuance
|
|
456
|
+
* @param domain The domain to request a certificate for
|
|
457
|
+
* @returns A promise that resolves when the request is submitted (not when the certificate is issued)
|
|
458
|
+
*/
|
|
459
|
+
public async requestCertificate(domain: string): Promise<boolean> {
|
|
460
|
+
return this.certificateManager.requestCertificate(domain);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Gets all proxy configurations currently in use
|
|
465
|
+
*/
|
|
466
|
+
public getProxyConfigs(): plugins.tsclass.network.IReverseProxyConfig[] {
|
|
467
|
+
return [...this.proxyConfigs];
|
|
468
|
+
}
|
|
469
|
+
}
|