@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.
- package/changelog.md +109 -0
- package/dist_rust/rustproxy +0 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/core/utils/shared-security-manager.d.ts +17 -0
- package/dist_ts/core/utils/shared-security-manager.js +66 -1
- package/dist_ts/index.d.ts +1 -5
- package/dist_ts/index.js +3 -9
- package/dist_ts/protocols/common/fragment-handler.js +5 -1
- package/dist_ts/proxies/http-proxy/default-certificates.d.ts +54 -0
- package/dist_ts/proxies/http-proxy/default-certificates.js +127 -0
- package/dist_ts/proxies/http-proxy/http-proxy.d.ts +1 -1
- package/dist_ts/proxies/http-proxy/http-proxy.js +9 -14
- package/dist_ts/proxies/http-proxy/index.d.ts +5 -1
- package/dist_ts/proxies/http-proxy/index.js +6 -2
- package/dist_ts/proxies/http-proxy/security-manager.d.ts +4 -12
- package/dist_ts/proxies/http-proxy/security-manager.js +66 -99
- package/dist_ts/proxies/index.d.ts +1 -5
- package/dist_ts/proxies/index.js +2 -6
- package/dist_ts/proxies/nftables-proxy/index.d.ts +1 -0
- package/dist_ts/proxies/nftables-proxy/index.js +2 -1
- package/dist_ts/proxies/nftables-proxy/nftables-proxy.d.ts +4 -26
- package/dist_ts/proxies/nftables-proxy/nftables-proxy.js +84 -236
- package/dist_ts/proxies/nftables-proxy/utils/index.d.ts +9 -0
- package/dist_ts/proxies/nftables-proxy/utils/index.js +12 -0
- package/dist_ts/proxies/nftables-proxy/utils/nft-command-executor.d.ts +66 -0
- package/dist_ts/proxies/nftables-proxy/utils/nft-command-executor.js +131 -0
- package/dist_ts/proxies/nftables-proxy/utils/nft-port-spec-normalizer.d.ts +39 -0
- package/dist_ts/proxies/nftables-proxy/utils/nft-port-spec-normalizer.js +112 -0
- package/dist_ts/proxies/nftables-proxy/utils/nft-rule-validator.d.ts +59 -0
- package/dist_ts/proxies/nftables-proxy/utils/nft-rule-validator.js +130 -0
- package/dist_ts/proxies/smart-proxy/certificate-manager.js +4 -3
- package/dist_ts/proxies/smart-proxy/connection-manager.d.ts +13 -2
- package/dist_ts/proxies/smart-proxy/connection-manager.js +16 -6
- package/dist_ts/proxies/smart-proxy/http-proxy-bridge.js +35 -10
- package/dist_ts/proxies/smart-proxy/index.d.ts +5 -10
- package/dist_ts/proxies/smart-proxy/index.js +7 -13
- package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +5 -3
- package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +17 -0
- package/dist_ts/proxies/smart-proxy/route-connection-handler.js +72 -9
- package/dist_ts/proxies/smart-proxy/route-preprocessor.d.ts +37 -0
- package/dist_ts/proxies/smart-proxy/route-preprocessor.js +103 -0
- package/dist_ts/proxies/smart-proxy/rust-binary-locator.d.ts +23 -0
- package/dist_ts/proxies/smart-proxy/rust-binary-locator.js +104 -0
- package/dist_ts/proxies/smart-proxy/rust-metrics-adapter.d.ts +74 -0
- package/dist_ts/proxies/smart-proxy/rust-metrics-adapter.js +146 -0
- package/dist_ts/proxies/smart-proxy/rust-proxy-bridge.d.ts +49 -0
- package/dist_ts/proxies/smart-proxy/rust-proxy-bridge.js +259 -0
- package/dist_ts/proxies/smart-proxy/security-manager.d.ts +14 -12
- package/dist_ts/proxies/smart-proxy/security-manager.js +80 -74
- package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +39 -157
- package/dist_ts/proxies/smart-proxy/smart-proxy.js +224 -622
- package/dist_ts/proxies/smart-proxy/socket-handler-server.d.ts +45 -0
- package/dist_ts/proxies/smart-proxy/socket-handler-server.js +253 -0
- package/dist_ts/proxies/smart-proxy/tls-manager.d.ts +2 -9
- package/dist_ts/proxies/smart-proxy/tls-manager.js +3 -26
- package/dist_ts/proxies/smart-proxy/utils/index.d.ts +1 -1
- package/dist_ts/proxies/smart-proxy/utils/index.js +3 -4
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/api-helpers.d.ts +49 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/api-helpers.js +108 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/dynamic-helpers.d.ts +57 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/dynamic-helpers.js +89 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/http-helpers.d.ts +17 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/http-helpers.js +32 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/https-helpers.d.ts +68 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/https-helpers.js +117 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/index.d.ts +17 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/index.js +27 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/load-balancer-helpers.d.ts +63 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/load-balancer-helpers.js +105 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/nftables-helpers.d.ts +83 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/nftables-helpers.js +126 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/security-helpers.d.ts +47 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/security-helpers.js +66 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/socket-handlers.d.ts +70 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/socket-handlers.js +287 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/websocket-helpers.d.ts +46 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/websocket-helpers.js +67 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers.d.ts +4 -457
- package/dist_ts/proxies/smart-proxy/utils/route-helpers.js +6 -950
- package/dist_ts/proxies/smart-proxy/utils/route-utils.js +2 -2
- package/dist_ts/proxies/smart-proxy/utils/route-validator.d.ts +67 -1
- package/dist_ts/proxies/smart-proxy/utils/route-validator.js +251 -3
- package/dist_ts/routing/index.d.ts +1 -1
- package/dist_ts/routing/index.js +3 -3
- package/dist_ts/routing/models/http-types.d.ts +119 -4
- package/dist_ts/routing/models/http-types.js +93 -5
- package/npmextra.json +12 -6
- package/package.json +34 -24
- package/readme.hints.md +184 -1
- package/readme.md +580 -266
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/core/utils/shared-security-manager.ts +98 -13
- package/ts/index.ts +4 -12
- package/ts/protocols/common/fragment-handler.ts +4 -0
- package/ts/proxies/index.ts +1 -9
- package/ts/proxies/nftables-proxy/index.ts +1 -0
- package/ts/proxies/nftables-proxy/nftables-proxy.ts +116 -290
- package/ts/proxies/nftables-proxy/utils/index.ts +38 -0
- package/ts/proxies/nftables-proxy/utils/nft-command-executor.ts +162 -0
- package/ts/proxies/nftables-proxy/utils/nft-port-spec-normalizer.ts +125 -0
- package/ts/proxies/nftables-proxy/utils/nft-rule-validator.ts +156 -0
- package/ts/proxies/smart-proxy/index.ts +6 -13
- package/ts/proxies/smart-proxy/models/interfaces.ts +6 -5
- package/ts/proxies/smart-proxy/route-preprocessor.ts +122 -0
- package/ts/proxies/smart-proxy/rust-binary-locator.ts +112 -0
- package/ts/proxies/smart-proxy/rust-metrics-adapter.ts +161 -0
- package/ts/proxies/smart-proxy/rust-proxy-bridge.ts +310 -0
- package/ts/proxies/smart-proxy/smart-proxy.ts +282 -800
- package/ts/proxies/smart-proxy/socket-handler-server.ts +279 -0
- package/ts/proxies/smart-proxy/utils/index.ts +3 -5
- package/ts/proxies/smart-proxy/utils/route-helpers/api-helpers.ts +144 -0
- package/ts/proxies/smart-proxy/utils/route-helpers/dynamic-helpers.ts +124 -0
- package/ts/proxies/smart-proxy/utils/route-helpers/http-helpers.ts +40 -0
- package/ts/proxies/smart-proxy/utils/route-helpers/https-helpers.ts +163 -0
- package/ts/proxies/smart-proxy/utils/route-helpers/index.ts +62 -0
- package/ts/proxies/smart-proxy/utils/route-helpers/load-balancer-helpers.ts +154 -0
- package/ts/proxies/smart-proxy/utils/route-helpers/nftables-helpers.ts +202 -0
- package/ts/proxies/smart-proxy/utils/route-helpers/security-helpers.ts +96 -0
- package/ts/proxies/smart-proxy/utils/route-helpers/socket-handlers.ts +337 -0
- package/ts/proxies/smart-proxy/utils/route-helpers/websocket-helpers.ts +98 -0
- package/ts/proxies/smart-proxy/utils/route-helpers.ts +5 -1302
- package/ts/proxies/smart-proxy/utils/route-utils.ts +1 -1
- package/ts/proxies/smart-proxy/utils/route-validator.ts +274 -4
- package/ts/routing/index.ts +2 -2
- package/ts/routing/models/http-types.ts +147 -4
- package/ts/proxies/http-proxy/certificate-manager.ts +0 -244
- package/ts/proxies/http-proxy/connection-pool.ts +0 -228
- package/ts/proxies/http-proxy/context-creator.ts +0 -145
- package/ts/proxies/http-proxy/function-cache.ts +0 -279
- package/ts/proxies/http-proxy/handlers/index.ts +0 -5
- package/ts/proxies/http-proxy/http-proxy.ts +0 -675
- package/ts/proxies/http-proxy/http-request-handler.ts +0 -331
- package/ts/proxies/http-proxy/http2-request-handler.ts +0 -255
- package/ts/proxies/http-proxy/index.ts +0 -13
- package/ts/proxies/http-proxy/models/http-types.ts +0 -148
- package/ts/proxies/http-proxy/models/index.ts +0 -5
- package/ts/proxies/http-proxy/models/types.ts +0 -125
- package/ts/proxies/http-proxy/request-handler.ts +0 -878
- package/ts/proxies/http-proxy/security-manager.ts +0 -433
- package/ts/proxies/http-proxy/websocket-handler.ts +0 -581
- package/ts/proxies/smart-proxy/acme-state-manager.ts +0 -112
- package/ts/proxies/smart-proxy/cert-store.ts +0 -92
- package/ts/proxies/smart-proxy/certificate-manager.ts +0 -894
- package/ts/proxies/smart-proxy/connection-manager.ts +0 -796
- package/ts/proxies/smart-proxy/http-proxy-bridge.ts +0 -187
- package/ts/proxies/smart-proxy/metrics-collector.ts +0 -453
- package/ts/proxies/smart-proxy/nftables-manager.ts +0 -271
- package/ts/proxies/smart-proxy/port-manager.ts +0 -358
- package/ts/proxies/smart-proxy/route-connection-handler.ts +0 -1640
- package/ts/proxies/smart-proxy/route-orchestrator.ts +0 -297
- package/ts/proxies/smart-proxy/security-manager.ts +0 -257
- package/ts/proxies/smart-proxy/throughput-tracker.ts +0 -138
- package/ts/proxies/smart-proxy/timeout-manager.ts +0 -196
- package/ts/proxies/smart-proxy/tls-manager.ts +0 -207
- package/ts/proxies/smart-proxy/utils/route-validators.ts +0 -283
|
@@ -1,581 +0,0 @@
|
|
|
1
|
-
import * as plugins from '../../plugins.js';
|
|
2
|
-
import '../../core/models/socket-augmentation.js';
|
|
3
|
-
import { type IHttpProxyOptions, type IWebSocketWithHeartbeat, type ILogger, createLogger } from './models/types.js';
|
|
4
|
-
import { ConnectionPool } from './connection-pool.js';
|
|
5
|
-
import { HttpRouter } from '../../routing/router/index.js';
|
|
6
|
-
import type { IRouteConfig, IRouteTarget } from '../smart-proxy/models/route-types.js';
|
|
7
|
-
import type { IRouteContext } from '../../core/models/route-context.js';
|
|
8
|
-
import { toBaseContext } from '../../core/models/route-context.js';
|
|
9
|
-
import { ContextCreator } from './context-creator.js';
|
|
10
|
-
import { SecurityManager } from './security-manager.js';
|
|
11
|
-
import { TemplateUtils } from '../../core/utils/template-utils.js';
|
|
12
|
-
import { getMessageSize, toBuffer } from '../../core/utils/websocket-utils.js';
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Handles WebSocket connections and proxying
|
|
16
|
-
*/
|
|
17
|
-
export class WebSocketHandler {
|
|
18
|
-
private heartbeatInterval: NodeJS.Timeout | null = null;
|
|
19
|
-
private wsServer: plugins.ws.WebSocketServer | null = null;
|
|
20
|
-
private logger: ILogger;
|
|
21
|
-
private contextCreator: ContextCreator = new ContextCreator();
|
|
22
|
-
private router: HttpRouter | null = null;
|
|
23
|
-
private securityManager: SecurityManager;
|
|
24
|
-
|
|
25
|
-
constructor(
|
|
26
|
-
private options: IHttpProxyOptions,
|
|
27
|
-
private connectionPool: ConnectionPool,
|
|
28
|
-
private routes: IRouteConfig[] = []
|
|
29
|
-
) {
|
|
30
|
-
this.logger = createLogger(options.logLevel || 'info');
|
|
31
|
-
this.securityManager = new SecurityManager(this.logger, routes);
|
|
32
|
-
|
|
33
|
-
// Initialize router if we have routes
|
|
34
|
-
if (routes.length > 0) {
|
|
35
|
-
this.router = new HttpRouter(routes, this.logger);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Set the route configurations
|
|
41
|
-
*/
|
|
42
|
-
public setRoutes(routes: IRouteConfig[]): void {
|
|
43
|
-
this.routes = routes;
|
|
44
|
-
|
|
45
|
-
// Initialize or update the route router
|
|
46
|
-
if (!this.router) {
|
|
47
|
-
this.router = new HttpRouter(routes, this.logger);
|
|
48
|
-
} else {
|
|
49
|
-
this.router.setRoutes(routes);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// Update the security manager
|
|
53
|
-
this.securityManager.setRoutes(routes);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Select the appropriate target from the targets array based on sub-matching criteria
|
|
58
|
-
*/
|
|
59
|
-
private selectTarget(
|
|
60
|
-
targets: IRouteTarget[],
|
|
61
|
-
context: {
|
|
62
|
-
port: number;
|
|
63
|
-
path?: string;
|
|
64
|
-
headers?: Record<string, string>;
|
|
65
|
-
method?: string;
|
|
66
|
-
}
|
|
67
|
-
): IRouteTarget | null {
|
|
68
|
-
// Sort targets by priority (higher first)
|
|
69
|
-
const sortedTargets = [...targets].sort((a, b) => (b.priority || 0) - (a.priority || 0));
|
|
70
|
-
|
|
71
|
-
// Find the first matching target
|
|
72
|
-
for (const target of sortedTargets) {
|
|
73
|
-
if (!target.match) {
|
|
74
|
-
// No match criteria means this is a default/fallback target
|
|
75
|
-
return target;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// Check port match
|
|
79
|
-
if (target.match.ports && !target.match.ports.includes(context.port)) {
|
|
80
|
-
continue;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Check path match (supports wildcards)
|
|
84
|
-
if (target.match.path && context.path) {
|
|
85
|
-
const pathPattern = target.match.path.replace(/\*/g, '.*');
|
|
86
|
-
const pathRegex = new RegExp(`^${pathPattern}$`);
|
|
87
|
-
if (!pathRegex.test(context.path)) {
|
|
88
|
-
continue;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Check method match
|
|
93
|
-
if (target.match.method && context.method && !target.match.method.includes(context.method)) {
|
|
94
|
-
continue;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Check headers match
|
|
98
|
-
if (target.match.headers && context.headers) {
|
|
99
|
-
let headersMatch = true;
|
|
100
|
-
for (const [key, pattern] of Object.entries(target.match.headers)) {
|
|
101
|
-
const headerValue = context.headers[key.toLowerCase()];
|
|
102
|
-
if (!headerValue) {
|
|
103
|
-
headersMatch = false;
|
|
104
|
-
break;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
if (pattern instanceof RegExp) {
|
|
108
|
-
if (!pattern.test(headerValue)) {
|
|
109
|
-
headersMatch = false;
|
|
110
|
-
break;
|
|
111
|
-
}
|
|
112
|
-
} else if (headerValue !== pattern) {
|
|
113
|
-
headersMatch = false;
|
|
114
|
-
break;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
if (!headersMatch) {
|
|
118
|
-
continue;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// All criteria matched
|
|
123
|
-
return target;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// No matching target found, return the first target without match criteria (default)
|
|
127
|
-
return sortedTargets.find(t => !t.match) || null;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Initialize WebSocket server on an existing HTTPS server
|
|
132
|
-
*/
|
|
133
|
-
public initialize(server: plugins.https.Server): void {
|
|
134
|
-
// Create WebSocket server
|
|
135
|
-
this.wsServer = new plugins.ws.WebSocketServer({
|
|
136
|
-
server: server,
|
|
137
|
-
clientTracking: true
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
// Handle WebSocket connections
|
|
141
|
-
this.wsServer.on('connection', (wsIncoming: IWebSocketWithHeartbeat, req: plugins.http.IncomingMessage) => {
|
|
142
|
-
this.handleWebSocketConnection(wsIncoming, req);
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
// Start the heartbeat interval
|
|
146
|
-
this.startHeartbeat();
|
|
147
|
-
|
|
148
|
-
this.logger.info('WebSocket handler initialized');
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Start the heartbeat interval to check for inactive WebSocket connections
|
|
153
|
-
*/
|
|
154
|
-
private startHeartbeat(): void {
|
|
155
|
-
// Clean up existing interval if any
|
|
156
|
-
if (this.heartbeatInterval) {
|
|
157
|
-
clearInterval(this.heartbeatInterval);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// Set up the heartbeat interval (check every 30 seconds)
|
|
161
|
-
this.heartbeatInterval = setInterval(() => {
|
|
162
|
-
if (!this.wsServer || this.wsServer.clients.size === 0) {
|
|
163
|
-
return; // Skip if no active connections
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
this.logger.debug(`WebSocket heartbeat check for ${this.wsServer.clients.size} clients`);
|
|
167
|
-
|
|
168
|
-
this.wsServer.clients.forEach((ws: plugins.wsDefault) => {
|
|
169
|
-
const wsWithHeartbeat = ws as IWebSocketWithHeartbeat;
|
|
170
|
-
|
|
171
|
-
if (wsWithHeartbeat.isAlive === false) {
|
|
172
|
-
this.logger.debug('Terminating inactive WebSocket connection');
|
|
173
|
-
return wsWithHeartbeat.terminate();
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
wsWithHeartbeat.isAlive = false;
|
|
177
|
-
wsWithHeartbeat.ping();
|
|
178
|
-
});
|
|
179
|
-
}, 30000);
|
|
180
|
-
|
|
181
|
-
// Make sure the interval doesn't keep the process alive
|
|
182
|
-
if (this.heartbeatInterval.unref) {
|
|
183
|
-
this.heartbeatInterval.unref();
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* Handle a new WebSocket connection
|
|
189
|
-
*/
|
|
190
|
-
private handleWebSocketConnection(wsIncoming: IWebSocketWithHeartbeat, req: plugins.http.IncomingMessage): void {
|
|
191
|
-
this.logger.debug(`WebSocket connection initiated from ${req.headers.host}`);
|
|
192
|
-
|
|
193
|
-
try {
|
|
194
|
-
// Initialize heartbeat tracking
|
|
195
|
-
wsIncoming.isAlive = true;
|
|
196
|
-
wsIncoming.lastPong = Date.now();
|
|
197
|
-
|
|
198
|
-
// Handle pong messages to track liveness
|
|
199
|
-
wsIncoming.on('pong', () => {
|
|
200
|
-
wsIncoming.isAlive = true;
|
|
201
|
-
wsIncoming.lastPong = Date.now();
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
// Create a context for routing
|
|
205
|
-
const connectionId = `ws-${Date.now()}-${Math.floor(Math.random() * 10000)}`;
|
|
206
|
-
const routeContext = this.contextCreator.createHttpRouteContext(req, {
|
|
207
|
-
connectionId,
|
|
208
|
-
clientIp: req.socket.remoteAddress?.replace('::ffff:', '') || '0.0.0.0',
|
|
209
|
-
serverIp: req.socket.localAddress?.replace('::ffff:', '') || '0.0.0.0',
|
|
210
|
-
tlsVersion: req.socket.getTLSVersion?.() || undefined
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
// Try modern router first if available
|
|
214
|
-
let route: IRouteConfig | undefined;
|
|
215
|
-
if (this.router) {
|
|
216
|
-
route = this.router.routeReq(req);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// Define destination variables
|
|
220
|
-
let destination: { host: string; port: number };
|
|
221
|
-
|
|
222
|
-
// If we found a route with the modern router, use it
|
|
223
|
-
if (route && route.action.type === 'forward' && route.action.targets && route.action.targets.length > 0) {
|
|
224
|
-
this.logger.debug(`Found matching WebSocket route: ${route.name || 'unnamed'}`);
|
|
225
|
-
|
|
226
|
-
// Select the appropriate target from the targets array
|
|
227
|
-
const selectedTarget = this.selectTarget(route.action.targets, {
|
|
228
|
-
port: routeContext.port,
|
|
229
|
-
path: routeContext.path,
|
|
230
|
-
headers: routeContext.headers,
|
|
231
|
-
method: routeContext.method
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
if (!selectedTarget) {
|
|
235
|
-
this.logger.error(`No matching target found for route ${route.name}`);
|
|
236
|
-
wsIncoming.close(1003, 'No matching target');
|
|
237
|
-
return;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// Check if WebSockets are enabled for this route
|
|
241
|
-
if (route.action.websocket?.enabled === false) {
|
|
242
|
-
this.logger.debug(`WebSockets are disabled for route: ${route.name || 'unnamed'}`);
|
|
243
|
-
wsIncoming.close(1003, 'WebSockets not supported for this route');
|
|
244
|
-
return;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
// Check security restrictions if configured to authenticate WebSocket requests
|
|
248
|
-
if (route.action.websocket?.authenticateRequest !== false && route.security) {
|
|
249
|
-
if (!this.securityManager.isAllowed(route, toBaseContext(routeContext))) {
|
|
250
|
-
this.logger.warn(`WebSocket connection denied by security policy for ${routeContext.clientIp}`);
|
|
251
|
-
wsIncoming.close(1008, 'Access denied by security policy');
|
|
252
|
-
return;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
// Check origin restrictions if configured
|
|
256
|
-
const origin = req.headers.origin;
|
|
257
|
-
if (origin && route.action.websocket?.allowedOrigins && route.action.websocket.allowedOrigins.length > 0) {
|
|
258
|
-
const isAllowed = route.action.websocket.allowedOrigins.some(allowedOrigin => {
|
|
259
|
-
// Handle wildcards and template variables
|
|
260
|
-
if (allowedOrigin.includes('*') || allowedOrigin.includes('{')) {
|
|
261
|
-
const pattern = allowedOrigin.replace(/\*/g, '.*');
|
|
262
|
-
const resolvedPattern = TemplateUtils.resolveTemplateVariables(pattern, routeContext);
|
|
263
|
-
const regex = new RegExp(`^${resolvedPattern}$`);
|
|
264
|
-
return regex.test(origin);
|
|
265
|
-
}
|
|
266
|
-
return allowedOrigin === origin;
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
if (!isAllowed) {
|
|
270
|
-
this.logger.warn(`WebSocket origin ${origin} not allowed for route: ${route.name || 'unnamed'}`);
|
|
271
|
-
wsIncoming.close(1008, 'Origin not allowed');
|
|
272
|
-
return;
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
// Extract target information, resolving functions if needed
|
|
278
|
-
let targetHost: string | string[];
|
|
279
|
-
let targetPort: number;
|
|
280
|
-
|
|
281
|
-
try {
|
|
282
|
-
// Resolve host if it's a function
|
|
283
|
-
if (typeof selectedTarget.host === 'function') {
|
|
284
|
-
const resolvedHost = selectedTarget.host(toBaseContext(routeContext));
|
|
285
|
-
targetHost = resolvedHost;
|
|
286
|
-
this.logger.debug(`Resolved function-based host for WebSocket: ${Array.isArray(resolvedHost) ? resolvedHost.join(', ') : resolvedHost}`);
|
|
287
|
-
} else {
|
|
288
|
-
targetHost = selectedTarget.host;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
// Resolve port if it's a function
|
|
292
|
-
if (typeof selectedTarget.port === 'function') {
|
|
293
|
-
targetPort = selectedTarget.port(toBaseContext(routeContext));
|
|
294
|
-
this.logger.debug(`Resolved function-based port for WebSocket: ${targetPort}`);
|
|
295
|
-
} else {
|
|
296
|
-
targetPort = selectedTarget.port === 'preserve' ? routeContext.port : selectedTarget.port as number;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
// Select a single host if an array was provided
|
|
300
|
-
const selectedHost = Array.isArray(targetHost)
|
|
301
|
-
? targetHost[Math.floor(Math.random() * targetHost.length)]
|
|
302
|
-
: targetHost;
|
|
303
|
-
|
|
304
|
-
// Create a destination for the WebSocket connection
|
|
305
|
-
destination = {
|
|
306
|
-
host: selectedHost,
|
|
307
|
-
port: targetPort
|
|
308
|
-
};
|
|
309
|
-
|
|
310
|
-
this.logger.debug(`WebSocket destination resolved: ${selectedHost}:${targetPort}`);
|
|
311
|
-
} catch (err) {
|
|
312
|
-
this.logger.error(`Error evaluating function-based target for WebSocket: ${err}`);
|
|
313
|
-
wsIncoming.close(1011, 'Internal server error');
|
|
314
|
-
return;
|
|
315
|
-
}
|
|
316
|
-
} else {
|
|
317
|
-
// No route found
|
|
318
|
-
this.logger.warn(`No route configuration for WebSocket host: ${req.headers.host}`);
|
|
319
|
-
wsIncoming.close(1008, 'No route configuration for this host');
|
|
320
|
-
return;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
// Build target URL with potential path rewriting
|
|
324
|
-
// Determine protocol based on the target's configuration
|
|
325
|
-
// For WebSocket connections, we use ws for HTTP backends and wss for HTTPS backends
|
|
326
|
-
const isTargetSecure = destination.port === 443;
|
|
327
|
-
const protocol = isTargetSecure ? 'wss' : 'ws';
|
|
328
|
-
let targetPath = req.url || '/';
|
|
329
|
-
|
|
330
|
-
// Apply path rewriting if configured
|
|
331
|
-
if (route?.action.websocket?.rewritePath) {
|
|
332
|
-
const originalPath = targetPath;
|
|
333
|
-
targetPath = TemplateUtils.resolveTemplateVariables(
|
|
334
|
-
route.action.websocket.rewritePath,
|
|
335
|
-
{...routeContext, path: targetPath}
|
|
336
|
-
);
|
|
337
|
-
this.logger.debug(`WebSocket path rewritten: ${originalPath} -> ${targetPath}`);
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
const targetUrl = `${protocol}://${destination.host}:${destination.port}${targetPath}`;
|
|
341
|
-
|
|
342
|
-
this.logger.debug(`WebSocket connection from ${req.socket.remoteAddress} to ${targetUrl}`);
|
|
343
|
-
|
|
344
|
-
// Create headers for outgoing WebSocket connection
|
|
345
|
-
const headers: { [key: string]: string } = {};
|
|
346
|
-
|
|
347
|
-
// Copy relevant headers from incoming request
|
|
348
|
-
for (const [key, value] of Object.entries(req.headers)) {
|
|
349
|
-
if (value && typeof value === 'string' &&
|
|
350
|
-
key.toLowerCase() !== 'connection' &&
|
|
351
|
-
key.toLowerCase() !== 'upgrade' &&
|
|
352
|
-
key.toLowerCase() !== 'sec-websocket-key' &&
|
|
353
|
-
key.toLowerCase() !== 'sec-websocket-version') {
|
|
354
|
-
headers[key] = value;
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
// Always rewrite host header for WebSockets for consistency
|
|
359
|
-
headers['host'] = `${destination.host}:${destination.port}`;
|
|
360
|
-
|
|
361
|
-
// Add custom headers from route configuration
|
|
362
|
-
if (route?.action.websocket?.customHeaders) {
|
|
363
|
-
for (const [key, value] of Object.entries(route.action.websocket.customHeaders)) {
|
|
364
|
-
// Skip if header already exists and we're not overriding
|
|
365
|
-
if (headers[key.toLowerCase()] && !value.startsWith('!')) {
|
|
366
|
-
continue;
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
// Handle special delete directive (!delete)
|
|
370
|
-
if (value === '!delete') {
|
|
371
|
-
delete headers[key.toLowerCase()];
|
|
372
|
-
continue;
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
// Handle forced override (!value)
|
|
376
|
-
let finalValue: string;
|
|
377
|
-
if (value.startsWith('!') && value !== '!delete') {
|
|
378
|
-
// Keep the ! but resolve any templates in the rest
|
|
379
|
-
const templateValue = value.substring(1);
|
|
380
|
-
finalValue = '!' + TemplateUtils.resolveTemplateVariables(templateValue, routeContext);
|
|
381
|
-
} else {
|
|
382
|
-
// Resolve templates in the entire value
|
|
383
|
-
finalValue = TemplateUtils.resolveTemplateVariables(value, routeContext);
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
// Set the header
|
|
387
|
-
headers[key.toLowerCase()] = finalValue;
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
// Create WebSocket connection options
|
|
392
|
-
const wsOptions: any = {
|
|
393
|
-
headers: headers,
|
|
394
|
-
followRedirects: true
|
|
395
|
-
};
|
|
396
|
-
|
|
397
|
-
// Add subprotocols if configured
|
|
398
|
-
if (route?.action.websocket?.subprotocols && route.action.websocket.subprotocols.length > 0) {
|
|
399
|
-
wsOptions.protocols = route.action.websocket.subprotocols;
|
|
400
|
-
} else if (req.headers['sec-websocket-protocol']) {
|
|
401
|
-
// Pass through client requested protocols
|
|
402
|
-
wsOptions.protocols = req.headers['sec-websocket-protocol'].split(',').map(p => p.trim());
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
// Create outgoing WebSocket connection
|
|
406
|
-
this.logger.debug(`Creating WebSocket connection to ${targetUrl} with options:`, {
|
|
407
|
-
headers: wsOptions.headers,
|
|
408
|
-
protocols: wsOptions.protocols
|
|
409
|
-
});
|
|
410
|
-
const wsOutgoing = new plugins.wsDefault(targetUrl, wsOptions);
|
|
411
|
-
this.logger.debug(`WebSocket instance created, waiting for connection...`);
|
|
412
|
-
|
|
413
|
-
// Handle connection errors
|
|
414
|
-
wsOutgoing.on('error', (err) => {
|
|
415
|
-
this.logger.error(`WebSocket target connection error: ${err.message}`);
|
|
416
|
-
if (wsIncoming.readyState === wsIncoming.OPEN) {
|
|
417
|
-
wsIncoming.close(1011, 'Internal server error');
|
|
418
|
-
}
|
|
419
|
-
});
|
|
420
|
-
|
|
421
|
-
// Handle outgoing connection open
|
|
422
|
-
wsOutgoing.on('open', () => {
|
|
423
|
-
this.logger.debug(`WebSocket target connection opened to ${targetUrl}`);
|
|
424
|
-
// Set up custom ping interval if configured
|
|
425
|
-
let pingInterval: NodeJS.Timeout | null = null;
|
|
426
|
-
if (route?.action.websocket?.pingInterval && route.action.websocket.pingInterval > 0) {
|
|
427
|
-
pingInterval = setInterval(() => {
|
|
428
|
-
if (wsIncoming.readyState === wsIncoming.OPEN) {
|
|
429
|
-
wsIncoming.ping();
|
|
430
|
-
this.logger.debug(`Sent WebSocket ping to client for route: ${route.name || 'unnamed'}`);
|
|
431
|
-
}
|
|
432
|
-
}, route.action.websocket.pingInterval);
|
|
433
|
-
|
|
434
|
-
// Don't keep process alive just for pings
|
|
435
|
-
if (pingInterval.unref) pingInterval.unref();
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
// Set up custom ping timeout if configured
|
|
439
|
-
let pingTimeout: NodeJS.Timeout | null = null;
|
|
440
|
-
const pingTimeoutMs = route?.action.websocket?.pingTimeout || 60000; // Default 60s
|
|
441
|
-
|
|
442
|
-
// Define timeout function for cleaner code
|
|
443
|
-
const resetPingTimeout = () => {
|
|
444
|
-
if (pingTimeout) clearTimeout(pingTimeout);
|
|
445
|
-
pingTimeout = setTimeout(() => {
|
|
446
|
-
this.logger.debug(`WebSocket ping timeout for client connection on route: ${route?.name || 'unnamed'}`);
|
|
447
|
-
wsIncoming.terminate();
|
|
448
|
-
}, pingTimeoutMs);
|
|
449
|
-
|
|
450
|
-
// Don't keep process alive just for timeouts
|
|
451
|
-
if (pingTimeout.unref) pingTimeout.unref();
|
|
452
|
-
};
|
|
453
|
-
|
|
454
|
-
// Reset timeout on pong
|
|
455
|
-
wsIncoming.on('pong', () => {
|
|
456
|
-
wsIncoming.isAlive = true;
|
|
457
|
-
wsIncoming.lastPong = Date.now();
|
|
458
|
-
resetPingTimeout();
|
|
459
|
-
});
|
|
460
|
-
|
|
461
|
-
// Initial ping timeout
|
|
462
|
-
resetPingTimeout();
|
|
463
|
-
|
|
464
|
-
// Handle potential message size limits
|
|
465
|
-
const maxSize = route?.action.websocket?.maxPayloadSize || 0;
|
|
466
|
-
|
|
467
|
-
// Forward incoming messages to outgoing connection
|
|
468
|
-
wsIncoming.on('message', (data, isBinary) => {
|
|
469
|
-
this.logger.debug(`WebSocket forwarding message from client to target: ${data.toString()}`);
|
|
470
|
-
if (wsOutgoing.readyState === wsOutgoing.OPEN) {
|
|
471
|
-
// Check message size if limit is set
|
|
472
|
-
const messageSize = getMessageSize(data);
|
|
473
|
-
if (maxSize > 0 && messageSize > maxSize) {
|
|
474
|
-
this.logger.warn(`WebSocket message exceeds max size (${messageSize} > ${maxSize})`);
|
|
475
|
-
wsIncoming.close(1009, 'Message too big');
|
|
476
|
-
return;
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
wsOutgoing.send(data, { binary: isBinary });
|
|
480
|
-
} else {
|
|
481
|
-
this.logger.warn(`WebSocket target connection not open (state: ${wsOutgoing.readyState})`);
|
|
482
|
-
}
|
|
483
|
-
});
|
|
484
|
-
|
|
485
|
-
// Forward outgoing messages to incoming connection
|
|
486
|
-
wsOutgoing.on('message', (data, isBinary) => {
|
|
487
|
-
this.logger.debug(`WebSocket forwarding message from target to client: ${data.toString()}`);
|
|
488
|
-
if (wsIncoming.readyState === wsIncoming.OPEN) {
|
|
489
|
-
wsIncoming.send(data, { binary: isBinary });
|
|
490
|
-
} else {
|
|
491
|
-
this.logger.warn(`WebSocket client connection not open (state: ${wsIncoming.readyState})`);
|
|
492
|
-
}
|
|
493
|
-
});
|
|
494
|
-
|
|
495
|
-
// Handle closing of connections
|
|
496
|
-
wsIncoming.on('close', (code, reason) => {
|
|
497
|
-
this.logger.debug(`WebSocket client connection closed: ${code} ${reason}`);
|
|
498
|
-
if (wsOutgoing.readyState === wsOutgoing.OPEN) {
|
|
499
|
-
// Ensure code is a valid WebSocket close code number
|
|
500
|
-
const validCode = typeof code === 'number' && code >= 1000 && code <= 4999 ? code : 1000;
|
|
501
|
-
try {
|
|
502
|
-
const reasonString = reason ? toBuffer(reason).toString() : '';
|
|
503
|
-
wsOutgoing.close(validCode, reasonString);
|
|
504
|
-
} catch (err) {
|
|
505
|
-
this.logger.error('Error closing wsOutgoing:', err);
|
|
506
|
-
wsOutgoing.close(validCode);
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
// Clean up timers
|
|
511
|
-
if (pingInterval) clearInterval(pingInterval);
|
|
512
|
-
if (pingTimeout) clearTimeout(pingTimeout);
|
|
513
|
-
});
|
|
514
|
-
|
|
515
|
-
wsOutgoing.on('close', (code, reason) => {
|
|
516
|
-
this.logger.debug(`WebSocket target connection closed: ${code} ${reason}`);
|
|
517
|
-
if (wsIncoming.readyState === wsIncoming.OPEN) {
|
|
518
|
-
// Ensure code is a valid WebSocket close code number
|
|
519
|
-
const validCode = typeof code === 'number' && code >= 1000 && code <= 4999 ? code : 1000;
|
|
520
|
-
try {
|
|
521
|
-
const reasonString = reason ? toBuffer(reason).toString() : '';
|
|
522
|
-
wsIncoming.close(validCode, reasonString);
|
|
523
|
-
} catch (err) {
|
|
524
|
-
this.logger.error('Error closing wsIncoming:', err);
|
|
525
|
-
wsIncoming.close(validCode);
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
// Clean up timers
|
|
530
|
-
if (pingInterval) clearInterval(pingInterval);
|
|
531
|
-
if (pingTimeout) clearTimeout(pingTimeout);
|
|
532
|
-
});
|
|
533
|
-
|
|
534
|
-
this.logger.debug(`WebSocket connection established: ${req.headers.host} -> ${destination.host}:${destination.port}`);
|
|
535
|
-
});
|
|
536
|
-
|
|
537
|
-
} catch (error) {
|
|
538
|
-
this.logger.error(`Error handling WebSocket connection: ${error.message}`);
|
|
539
|
-
if (wsIncoming.readyState === wsIncoming.OPEN) {
|
|
540
|
-
wsIncoming.close(1011, 'Internal server error');
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
/**
|
|
546
|
-
* Get information about active WebSocket connections
|
|
547
|
-
*/
|
|
548
|
-
public getConnectionInfo(): { activeConnections: number } {
|
|
549
|
-
return {
|
|
550
|
-
activeConnections: this.wsServer ? this.wsServer.clients.size : 0
|
|
551
|
-
};
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
/**
|
|
555
|
-
* Shutdown the WebSocket handler
|
|
556
|
-
*/
|
|
557
|
-
public shutdown(): void {
|
|
558
|
-
// Stop heartbeat interval
|
|
559
|
-
if (this.heartbeatInterval) {
|
|
560
|
-
clearInterval(this.heartbeatInterval);
|
|
561
|
-
this.heartbeatInterval = null;
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
// Close all WebSocket connections
|
|
565
|
-
if (this.wsServer) {
|
|
566
|
-
this.logger.info(`Closing ${this.wsServer.clients.size} WebSocket connections`);
|
|
567
|
-
|
|
568
|
-
for (const client of this.wsServer.clients) {
|
|
569
|
-
try {
|
|
570
|
-
client.terminate();
|
|
571
|
-
} catch (error) {
|
|
572
|
-
this.logger.error('Error terminating WebSocket client', error);
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
// Close the server
|
|
577
|
-
this.wsServer.close();
|
|
578
|
-
this.wsServer = null;
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
}
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
import type { IRouteConfig } from './models/route-types.js';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Global state store for ACME operations
|
|
5
|
-
* Tracks active challenge routes and port allocations
|
|
6
|
-
*/
|
|
7
|
-
export class AcmeStateManager {
|
|
8
|
-
private activeChallengeRoutes: Map<string, IRouteConfig> = new Map();
|
|
9
|
-
private acmePortAllocations: Set<number> = new Set();
|
|
10
|
-
private primaryChallengeRoute: IRouteConfig | null = null;
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Check if a challenge route is active
|
|
14
|
-
*/
|
|
15
|
-
public isChallengeRouteActive(): boolean {
|
|
16
|
-
return this.activeChallengeRoutes.size > 0;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Register a challenge route as active
|
|
21
|
-
*/
|
|
22
|
-
public addChallengeRoute(route: IRouteConfig): void {
|
|
23
|
-
this.activeChallengeRoutes.set(route.name, route);
|
|
24
|
-
|
|
25
|
-
// Track the primary challenge route
|
|
26
|
-
if (!this.primaryChallengeRoute || route.priority > (this.primaryChallengeRoute.priority || 0)) {
|
|
27
|
-
this.primaryChallengeRoute = route;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// Track port allocations
|
|
31
|
-
const ports = Array.isArray(route.match.ports) ? route.match.ports : [route.match.ports];
|
|
32
|
-
ports.forEach(port => this.acmePortAllocations.add(port));
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Remove a challenge route
|
|
37
|
-
*/
|
|
38
|
-
public removeChallengeRoute(routeName: string): void {
|
|
39
|
-
const route = this.activeChallengeRoutes.get(routeName);
|
|
40
|
-
if (!route) return;
|
|
41
|
-
|
|
42
|
-
this.activeChallengeRoutes.delete(routeName);
|
|
43
|
-
|
|
44
|
-
// Update primary challenge route if needed
|
|
45
|
-
if (this.primaryChallengeRoute?.name === routeName) {
|
|
46
|
-
this.primaryChallengeRoute = null;
|
|
47
|
-
// Find new primary route with highest priority
|
|
48
|
-
let highestPriority = -1;
|
|
49
|
-
for (const [_, activeRoute] of this.activeChallengeRoutes) {
|
|
50
|
-
const priority = activeRoute.priority || 0;
|
|
51
|
-
if (priority > highestPriority) {
|
|
52
|
-
highestPriority = priority;
|
|
53
|
-
this.primaryChallengeRoute = activeRoute;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Update port allocations - only remove if no other routes use this port
|
|
59
|
-
const ports = Array.isArray(route.match.ports) ? route.match.ports : [route.match.ports];
|
|
60
|
-
ports.forEach(port => {
|
|
61
|
-
let portStillUsed = false;
|
|
62
|
-
for (const [_, activeRoute] of this.activeChallengeRoutes) {
|
|
63
|
-
const activePorts = Array.isArray(activeRoute.match.ports) ?
|
|
64
|
-
activeRoute.match.ports : [activeRoute.match.ports];
|
|
65
|
-
if (activePorts.includes(port)) {
|
|
66
|
-
portStillUsed = true;
|
|
67
|
-
break;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
if (!portStillUsed) {
|
|
71
|
-
this.acmePortAllocations.delete(port);
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Get all active challenge routes
|
|
78
|
-
*/
|
|
79
|
-
public getActiveChallengeRoutes(): IRouteConfig[] {
|
|
80
|
-
return Array.from(this.activeChallengeRoutes.values());
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Get the primary challenge route
|
|
85
|
-
*/
|
|
86
|
-
public getPrimaryChallengeRoute(): IRouteConfig | null {
|
|
87
|
-
return this.primaryChallengeRoute;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Check if a port is allocated for ACME
|
|
92
|
-
*/
|
|
93
|
-
public isPortAllocatedForAcme(port: number): boolean {
|
|
94
|
-
return this.acmePortAllocations.has(port);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Get all ACME ports
|
|
99
|
-
*/
|
|
100
|
-
public getAcmePorts(): number[] {
|
|
101
|
-
return Array.from(this.acmePortAllocations);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Clear all state (for shutdown or reset)
|
|
106
|
-
*/
|
|
107
|
-
public clear(): void {
|
|
108
|
-
this.activeChallengeRoutes.clear();
|
|
109
|
-
this.acmePortAllocations.clear();
|
|
110
|
-
this.primaryChallengeRoute = null;
|
|
111
|
-
}
|
|
112
|
-
}
|