@push.rocks/smartproxy 10.2.0 → 11.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/00_commitinfo_data.js +1 -1
- package/dist_ts/common/port80-adapter.d.ts +11 -0
- package/dist_ts/common/port80-adapter.js +61 -0
- package/dist_ts/examples/forwarding-example.d.ts +1 -0
- package/dist_ts/examples/forwarding-example.js +96 -0
- package/dist_ts/index.d.ts +1 -0
- package/dist_ts/index.js +3 -1
- package/dist_ts/smartproxy/classes.pp.connectionhandler.js +179 -30
- package/dist_ts/smartproxy/classes.pp.domainconfigmanager.d.ts +39 -0
- package/dist_ts/smartproxy/classes.pp.domainconfigmanager.js +172 -20
- package/dist_ts/smartproxy/classes.pp.interfaces.d.ts +3 -11
- package/dist_ts/smartproxy/classes.pp.portrangemanager.js +17 -10
- package/dist_ts/smartproxy/classes.pp.securitymanager.d.ts +19 -2
- package/dist_ts/smartproxy/classes.pp.securitymanager.js +27 -4
- package/dist_ts/smartproxy/classes.pp.timeoutmanager.js +3 -3
- package/dist_ts/smartproxy/classes.smartproxy.js +45 -13
- package/dist_ts/smartproxy/forwarding/domain-config.d.ts +12 -0
- package/dist_ts/smartproxy/forwarding/domain-config.js +12 -0
- package/dist_ts/smartproxy/forwarding/domain-manager.d.ts +86 -0
- package/dist_ts/smartproxy/forwarding/domain-manager.js +241 -0
- package/dist_ts/smartproxy/forwarding/forwarding.factory.d.ts +24 -0
- package/dist_ts/smartproxy/forwarding/forwarding.factory.js +137 -0
- package/dist_ts/smartproxy/forwarding/forwarding.handler.d.ts +55 -0
- package/dist_ts/smartproxy/forwarding/forwarding.handler.js +94 -0
- package/dist_ts/smartproxy/forwarding/http.handler.d.ts +25 -0
- package/dist_ts/smartproxy/forwarding/http.handler.js +123 -0
- package/dist_ts/smartproxy/forwarding/https-passthrough.handler.d.ts +24 -0
- package/dist_ts/smartproxy/forwarding/https-passthrough.handler.js +154 -0
- package/dist_ts/smartproxy/forwarding/https-terminate-to-http.handler.d.ts +36 -0
- package/dist_ts/smartproxy/forwarding/https-terminate-to-http.handler.js +229 -0
- package/dist_ts/smartproxy/forwarding/https-terminate-to-https.handler.d.ts +35 -0
- package/dist_ts/smartproxy/forwarding/https-terminate-to-https.handler.js +254 -0
- package/dist_ts/smartproxy/forwarding/index.d.ts +16 -0
- package/dist_ts/smartproxy/forwarding/index.js +23 -0
- package/dist_ts/smartproxy/types/forwarding.types.d.ts +104 -0
- package/dist_ts/smartproxy/types/forwarding.types.js +50 -0
- package/package.json +2 -2
- package/readme.md +158 -8
- package/readme.plan.md +471 -42
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/common/port80-adapter.ts +87 -0
- package/ts/examples/forwarding-example.ts +128 -0
- package/ts/index.ts +3 -0
- package/ts/smartproxy/classes.pp.connectionhandler.ts +231 -44
- package/ts/smartproxy/classes.pp.domainconfigmanager.ts +198 -24
- package/ts/smartproxy/classes.pp.interfaces.ts +3 -11
- package/ts/smartproxy/classes.pp.portrangemanager.ts +17 -10
- package/ts/smartproxy/classes.pp.securitymanager.ts +29 -5
- package/ts/smartproxy/classes.pp.timeoutmanager.ts +3 -3
- package/ts/smartproxy/classes.smartproxy.ts +68 -15
- package/ts/smartproxy/forwarding/domain-config.ts +28 -0
- package/ts/smartproxy/forwarding/domain-manager.ts +283 -0
- package/ts/smartproxy/forwarding/forwarding.factory.ts +155 -0
- package/ts/smartproxy/forwarding/forwarding.handler.ts +127 -0
- package/ts/smartproxy/forwarding/http.handler.ts +140 -0
- package/ts/smartproxy/forwarding/https-passthrough.handler.ts +182 -0
- package/ts/smartproxy/forwarding/https-terminate-to-http.handler.ts +264 -0
- package/ts/smartproxy/forwarding/https-terminate-to-https.handler.ts +292 -0
- package/ts/smartproxy/forwarding/index.ts +52 -0
- package/ts/smartproxy/types/forwarding.types.ts +162 -0
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import * as plugins from '../../plugins.js';
|
|
2
|
+
import { ForwardingHandler } from './forwarding.handler.js';
|
|
3
|
+
import type { IForwardConfig } from '../types/forwarding.types.js';
|
|
4
|
+
import { ForwardingHandlerEvents } from '../types/forwarding.types.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Handler for HTTPS termination with HTTPS backend
|
|
8
|
+
*/
|
|
9
|
+
export class HttpsTerminateToHttpsHandler extends ForwardingHandler {
|
|
10
|
+
private secureContext: plugins.tls.SecureContext | null = null;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Create a new HTTPS termination with HTTPS backend handler
|
|
14
|
+
* @param config The forwarding configuration
|
|
15
|
+
*/
|
|
16
|
+
constructor(config: IForwardConfig) {
|
|
17
|
+
super(config);
|
|
18
|
+
|
|
19
|
+
// Validate that this is an HTTPS terminate to HTTPS configuration
|
|
20
|
+
if (config.type !== 'https-terminate-to-https') {
|
|
21
|
+
throw new Error(`Invalid configuration type for HttpsTerminateToHttpsHandler: ${config.type}`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Initialize the handler, setting up TLS context
|
|
27
|
+
*/
|
|
28
|
+
public async initialize(): Promise<void> {
|
|
29
|
+
// We need to load or create TLS certificates for termination
|
|
30
|
+
if (this.config.https?.customCert) {
|
|
31
|
+
// Use custom certificate from configuration
|
|
32
|
+
this.secureContext = plugins.tls.createSecureContext({
|
|
33
|
+
key: this.config.https.customCert.key,
|
|
34
|
+
cert: this.config.https.customCert.cert
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
this.emit(ForwardingHandlerEvents.CERTIFICATE_LOADED, {
|
|
38
|
+
source: 'config',
|
|
39
|
+
domain: this.config.target.host
|
|
40
|
+
});
|
|
41
|
+
} else if (this.config.acme?.enabled) {
|
|
42
|
+
// Request certificate through ACME if needed
|
|
43
|
+
this.emit(ForwardingHandlerEvents.CERTIFICATE_NEEDED, {
|
|
44
|
+
domain: Array.isArray(this.config.target.host)
|
|
45
|
+
? this.config.target.host[0]
|
|
46
|
+
: this.config.target.host,
|
|
47
|
+
useProduction: this.config.acme.production || false
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// In a real implementation, we would wait for the certificate to be issued
|
|
51
|
+
// For now, we'll use a dummy context
|
|
52
|
+
this.secureContext = plugins.tls.createSecureContext({
|
|
53
|
+
key: '-----BEGIN PRIVATE KEY-----\nDummy key\n-----END PRIVATE KEY-----',
|
|
54
|
+
cert: '-----BEGIN CERTIFICATE-----\nDummy cert\n-----END CERTIFICATE-----'
|
|
55
|
+
});
|
|
56
|
+
} else {
|
|
57
|
+
throw new Error('HTTPS termination requires either a custom certificate or ACME enabled');
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Set the secure context for TLS termination
|
|
63
|
+
* Called when a certificate is available
|
|
64
|
+
* @param context The secure context
|
|
65
|
+
*/
|
|
66
|
+
public setSecureContext(context: plugins.tls.SecureContext): void {
|
|
67
|
+
this.secureContext = context;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Handle a TLS/SSL socket connection by terminating TLS and creating a new TLS connection to backend
|
|
72
|
+
* @param clientSocket The incoming socket from the client
|
|
73
|
+
*/
|
|
74
|
+
public handleConnection(clientSocket: plugins.net.Socket): void {
|
|
75
|
+
// Make sure we have a secure context
|
|
76
|
+
if (!this.secureContext) {
|
|
77
|
+
clientSocket.destroy(new Error('TLS secure context not initialized'));
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const remoteAddress = clientSocket.remoteAddress || 'unknown';
|
|
82
|
+
const remotePort = clientSocket.remotePort || 0;
|
|
83
|
+
|
|
84
|
+
// Create a TLS socket using our secure context
|
|
85
|
+
const tlsSocket = new plugins.tls.TLSSocket(clientSocket, {
|
|
86
|
+
secureContext: this.secureContext,
|
|
87
|
+
isServer: true
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
this.emit(ForwardingHandlerEvents.CONNECTED, {
|
|
91
|
+
remoteAddress,
|
|
92
|
+
remotePort,
|
|
93
|
+
tls: true
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Handle TLS errors
|
|
97
|
+
tlsSocket.on('error', (error) => {
|
|
98
|
+
this.emit(ForwardingHandlerEvents.ERROR, {
|
|
99
|
+
remoteAddress,
|
|
100
|
+
error: `TLS error: ${error.message}`
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
if (!tlsSocket.destroyed) {
|
|
104
|
+
tlsSocket.destroy();
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// The TLS socket will now emit HTTP traffic that can be processed
|
|
109
|
+
// In a real implementation, we would create an HTTP parser and handle
|
|
110
|
+
// the requests here, but for simplicity, we'll just forward the data
|
|
111
|
+
|
|
112
|
+
// Get the target from configuration
|
|
113
|
+
const target = this.getTargetFromConfig();
|
|
114
|
+
|
|
115
|
+
// Set up the connection to the HTTPS backend
|
|
116
|
+
const connectToBackend = () => {
|
|
117
|
+
const backendSocket = plugins.tls.connect({
|
|
118
|
+
host: target.host,
|
|
119
|
+
port: target.port,
|
|
120
|
+
// In a real implementation, we would configure TLS options
|
|
121
|
+
rejectUnauthorized: false // For testing only, never use in production
|
|
122
|
+
}, () => {
|
|
123
|
+
this.emit(ForwardingHandlerEvents.DATA_FORWARDED, {
|
|
124
|
+
direction: 'outbound',
|
|
125
|
+
target: `${target.host}:${target.port}`,
|
|
126
|
+
tls: true
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// Set up bidirectional data flow
|
|
130
|
+
tlsSocket.pipe(backendSocket);
|
|
131
|
+
backendSocket.pipe(tlsSocket);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
backendSocket.on('error', (error) => {
|
|
135
|
+
this.emit(ForwardingHandlerEvents.ERROR, {
|
|
136
|
+
remoteAddress,
|
|
137
|
+
error: `Backend connection error: ${error.message}`
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
if (!tlsSocket.destroyed) {
|
|
141
|
+
tlsSocket.destroy();
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// Handle close
|
|
146
|
+
backendSocket.on('close', () => {
|
|
147
|
+
if (!tlsSocket.destroyed) {
|
|
148
|
+
tlsSocket.destroy();
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// Set timeout
|
|
153
|
+
const timeout = this.getTimeout();
|
|
154
|
+
backendSocket.setTimeout(timeout);
|
|
155
|
+
|
|
156
|
+
backendSocket.on('timeout', () => {
|
|
157
|
+
this.emit(ForwardingHandlerEvents.ERROR, {
|
|
158
|
+
remoteAddress,
|
|
159
|
+
error: 'Backend connection timeout'
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
if (!backendSocket.destroyed) {
|
|
163
|
+
backendSocket.destroy();
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
// Wait for the TLS handshake to complete before connecting to backend
|
|
169
|
+
tlsSocket.on('secure', () => {
|
|
170
|
+
connectToBackend();
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// Handle close
|
|
174
|
+
tlsSocket.on('close', () => {
|
|
175
|
+
this.emit(ForwardingHandlerEvents.DISCONNECTED, {
|
|
176
|
+
remoteAddress
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
// Set timeout
|
|
181
|
+
const timeout = this.getTimeout();
|
|
182
|
+
tlsSocket.setTimeout(timeout);
|
|
183
|
+
|
|
184
|
+
tlsSocket.on('timeout', () => {
|
|
185
|
+
this.emit(ForwardingHandlerEvents.ERROR, {
|
|
186
|
+
remoteAddress,
|
|
187
|
+
error: 'TLS connection timeout'
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
if (!tlsSocket.destroyed) {
|
|
191
|
+
tlsSocket.destroy();
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Handle an HTTP request by forwarding to the HTTPS backend
|
|
198
|
+
* @param req The HTTP request
|
|
199
|
+
* @param res The HTTP response
|
|
200
|
+
*/
|
|
201
|
+
public handleHttpRequest(req: plugins.http.IncomingMessage, res: plugins.http.ServerResponse): void {
|
|
202
|
+
// Check if we should redirect to HTTPS
|
|
203
|
+
if (this.config.http?.redirectToHttps) {
|
|
204
|
+
this.redirectToHttps(req, res);
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Get the target from configuration
|
|
209
|
+
const target = this.getTargetFromConfig();
|
|
210
|
+
|
|
211
|
+
// Create custom headers with variable substitution
|
|
212
|
+
const variables = {
|
|
213
|
+
clientIp: req.socket.remoteAddress || 'unknown'
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
// Prepare headers, merging with any custom headers from config
|
|
217
|
+
const headers = this.applyCustomHeaders(req.headers, variables);
|
|
218
|
+
|
|
219
|
+
// Create the proxy request options
|
|
220
|
+
const options = {
|
|
221
|
+
hostname: target.host,
|
|
222
|
+
port: target.port,
|
|
223
|
+
path: req.url,
|
|
224
|
+
method: req.method,
|
|
225
|
+
headers,
|
|
226
|
+
// In a real implementation, we would configure TLS options
|
|
227
|
+
rejectUnauthorized: false // For testing only, never use in production
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
// Create the proxy request using HTTPS
|
|
231
|
+
const proxyReq = plugins.https.request(options, (proxyRes) => {
|
|
232
|
+
// Copy status code and headers from the proxied response
|
|
233
|
+
res.writeHead(proxyRes.statusCode || 500, proxyRes.headers);
|
|
234
|
+
|
|
235
|
+
// Pipe the proxy response to the client response
|
|
236
|
+
proxyRes.pipe(res);
|
|
237
|
+
|
|
238
|
+
// Track response size for logging
|
|
239
|
+
let responseSize = 0;
|
|
240
|
+
proxyRes.on('data', (chunk) => {
|
|
241
|
+
responseSize += chunk.length;
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
proxyRes.on('end', () => {
|
|
245
|
+
this.emit(ForwardingHandlerEvents.HTTP_RESPONSE, {
|
|
246
|
+
statusCode: proxyRes.statusCode,
|
|
247
|
+
headers: proxyRes.headers,
|
|
248
|
+
size: responseSize
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
// Handle errors in the proxy request
|
|
254
|
+
proxyReq.on('error', (error) => {
|
|
255
|
+
this.emit(ForwardingHandlerEvents.ERROR, {
|
|
256
|
+
remoteAddress: req.socket.remoteAddress,
|
|
257
|
+
error: `Proxy request error: ${error.message}`
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
// Send an error response if headers haven't been sent yet
|
|
261
|
+
if (!res.headersSent) {
|
|
262
|
+
res.writeHead(502, { 'Content-Type': 'text/plain' });
|
|
263
|
+
res.end(`Error forwarding request: ${error.message}`);
|
|
264
|
+
} else {
|
|
265
|
+
// Just end the response if headers have already been sent
|
|
266
|
+
res.end();
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
// Track request details for logging
|
|
271
|
+
let requestSize = 0;
|
|
272
|
+
req.on('data', (chunk) => {
|
|
273
|
+
requestSize += chunk.length;
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
// Log the request
|
|
277
|
+
this.emit(ForwardingHandlerEvents.HTTP_REQUEST, {
|
|
278
|
+
method: req.method,
|
|
279
|
+
url: req.url,
|
|
280
|
+
headers: req.headers,
|
|
281
|
+
remoteAddress: req.socket.remoteAddress,
|
|
282
|
+
target: `${target.host}:${target.port}`
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
// Pipe the client request to the proxy request
|
|
286
|
+
if (req.readable) {
|
|
287
|
+
req.pipe(proxyReq);
|
|
288
|
+
} else {
|
|
289
|
+
proxyReq.end();
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// Export types
|
|
2
|
+
export type {
|
|
3
|
+
ForwardingType,
|
|
4
|
+
IForwardConfig,
|
|
5
|
+
IForwardingHandler,
|
|
6
|
+
ITargetConfig,
|
|
7
|
+
IHttpOptions,
|
|
8
|
+
IHttpsOptions,
|
|
9
|
+
IAcmeForwardingOptions,
|
|
10
|
+
ISecurityOptions,
|
|
11
|
+
IAdvancedOptions
|
|
12
|
+
} from '../types/forwarding.types.js';
|
|
13
|
+
|
|
14
|
+
// Export values
|
|
15
|
+
export {
|
|
16
|
+
ForwardingHandlerEvents,
|
|
17
|
+
httpOnly,
|
|
18
|
+
tlsTerminateToHttp,
|
|
19
|
+
tlsTerminateToHttps,
|
|
20
|
+
httpsPassthrough
|
|
21
|
+
} from '../types/forwarding.types.js';
|
|
22
|
+
|
|
23
|
+
// Export domain configuration
|
|
24
|
+
export * from './domain-config.js';
|
|
25
|
+
|
|
26
|
+
// Export handlers
|
|
27
|
+
export { ForwardingHandler } from './forwarding.handler.js';
|
|
28
|
+
export { HttpForwardingHandler } from './http.handler.js';
|
|
29
|
+
export { HttpsPassthroughHandler } from './https-passthrough.handler.js';
|
|
30
|
+
export { HttpsTerminateToHttpHandler } from './https-terminate-to-http.handler.js';
|
|
31
|
+
export { HttpsTerminateToHttpsHandler } from './https-terminate-to-https.handler.js';
|
|
32
|
+
|
|
33
|
+
// Export factory
|
|
34
|
+
export { ForwardingHandlerFactory } from './forwarding.factory.js';
|
|
35
|
+
|
|
36
|
+
// Export manager
|
|
37
|
+
export { DomainManager, DomainManagerEvents } from './domain-manager.js';
|
|
38
|
+
|
|
39
|
+
// Helper functions as a convenience object
|
|
40
|
+
import {
|
|
41
|
+
httpOnly,
|
|
42
|
+
tlsTerminateToHttp,
|
|
43
|
+
tlsTerminateToHttps,
|
|
44
|
+
httpsPassthrough
|
|
45
|
+
} from '../types/forwarding.types.js';
|
|
46
|
+
|
|
47
|
+
export const helpers = {
|
|
48
|
+
httpOnly,
|
|
49
|
+
tlsTerminateToHttp,
|
|
50
|
+
tlsTerminateToHttps,
|
|
51
|
+
sniPassthrough: httpsPassthrough // Alias for backward compatibility
|
|
52
|
+
};
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import type * as plugins from '../../plugins.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The primary forwarding types supported by SmartProxy
|
|
5
|
+
*/
|
|
6
|
+
export type ForwardingType =
|
|
7
|
+
| 'http-only' // HTTP forwarding only (no HTTPS)
|
|
8
|
+
| 'https-passthrough' // Pass-through TLS traffic (SNI forwarding)
|
|
9
|
+
| 'https-terminate-to-http' // Terminate TLS and forward to HTTP backend
|
|
10
|
+
| 'https-terminate-to-https'; // Terminate TLS and forward to HTTPS backend
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Target configuration for forwarding
|
|
14
|
+
*/
|
|
15
|
+
export interface ITargetConfig {
|
|
16
|
+
host: string | string[]; // Support single host or round-robin
|
|
17
|
+
port: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* HTTP-specific options for forwarding
|
|
22
|
+
*/
|
|
23
|
+
export interface IHttpOptions {
|
|
24
|
+
enabled?: boolean; // Whether HTTP is enabled
|
|
25
|
+
redirectToHttps?: boolean; // Redirect HTTP to HTTPS
|
|
26
|
+
headers?: Record<string, string>; // Custom headers for HTTP responses
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* HTTPS-specific options for forwarding
|
|
31
|
+
*/
|
|
32
|
+
export interface IHttpsOptions {
|
|
33
|
+
customCert?: { // Use custom cert instead of auto-provisioned
|
|
34
|
+
key: string;
|
|
35
|
+
cert: string;
|
|
36
|
+
};
|
|
37
|
+
forwardSni?: boolean; // Forward SNI info in passthrough mode
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* ACME certificate handling options
|
|
42
|
+
*/
|
|
43
|
+
export interface IAcmeForwardingOptions {
|
|
44
|
+
enabled?: boolean; // Enable ACME certificate provisioning
|
|
45
|
+
maintenance?: boolean; // Auto-renew certificates
|
|
46
|
+
production?: boolean; // Use production ACME servers
|
|
47
|
+
forwardChallenges?: { // Forward ACME challenges
|
|
48
|
+
host: string;
|
|
49
|
+
port: number;
|
|
50
|
+
useTls?: boolean;
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Security options for forwarding
|
|
56
|
+
*/
|
|
57
|
+
export interface ISecurityOptions {
|
|
58
|
+
allowedIps?: string[]; // IPs allowed to connect
|
|
59
|
+
blockedIps?: string[]; // IPs blocked from connecting
|
|
60
|
+
maxConnections?: number; // Max simultaneous connections
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Advanced options for forwarding
|
|
65
|
+
*/
|
|
66
|
+
export interface IAdvancedOptions {
|
|
67
|
+
portRanges?: Array<{ from: number; to: number }>; // Allowed port ranges
|
|
68
|
+
networkProxyPort?: number; // Custom NetworkProxy port if using terminate mode
|
|
69
|
+
keepAlive?: boolean; // Enable TCP keepalive
|
|
70
|
+
timeout?: number; // Connection timeout in ms
|
|
71
|
+
headers?: Record<string, string>; // Custom headers with support for variables like {sni}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Unified forwarding configuration interface
|
|
76
|
+
*/
|
|
77
|
+
export interface IForwardConfig {
|
|
78
|
+
// Define the primary forwarding type - use-case driven approach
|
|
79
|
+
type: ForwardingType;
|
|
80
|
+
|
|
81
|
+
// Target configuration
|
|
82
|
+
target: ITargetConfig;
|
|
83
|
+
|
|
84
|
+
// Protocol options
|
|
85
|
+
http?: IHttpOptions;
|
|
86
|
+
https?: IHttpsOptions;
|
|
87
|
+
acme?: IAcmeForwardingOptions;
|
|
88
|
+
|
|
89
|
+
// Security and advanced options
|
|
90
|
+
security?: ISecurityOptions;
|
|
91
|
+
advanced?: IAdvancedOptions;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Event types emitted by forwarding handlers
|
|
96
|
+
*/
|
|
97
|
+
export enum ForwardingHandlerEvents {
|
|
98
|
+
CONNECTED = 'connected',
|
|
99
|
+
DISCONNECTED = 'disconnected',
|
|
100
|
+
ERROR = 'error',
|
|
101
|
+
DATA_FORWARDED = 'data-forwarded',
|
|
102
|
+
HTTP_REQUEST = 'http-request',
|
|
103
|
+
HTTP_RESPONSE = 'http-response',
|
|
104
|
+
CERTIFICATE_NEEDED = 'certificate-needed',
|
|
105
|
+
CERTIFICATE_LOADED = 'certificate-loaded'
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Base interface for forwarding handlers
|
|
110
|
+
*/
|
|
111
|
+
export interface IForwardingHandler extends plugins.EventEmitter {
|
|
112
|
+
initialize(): Promise<void>;
|
|
113
|
+
handleConnection(socket: plugins.net.Socket): void;
|
|
114
|
+
handleHttpRequest(req: plugins.http.IncomingMessage, res: plugins.http.ServerResponse): void;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Helper function types for common forwarding patterns
|
|
119
|
+
*/
|
|
120
|
+
export const httpOnly = (
|
|
121
|
+
partialConfig: Partial<IForwardConfig> & Pick<IForwardConfig, 'target'>
|
|
122
|
+
): IForwardConfig => ({
|
|
123
|
+
type: 'http-only',
|
|
124
|
+
target: partialConfig.target,
|
|
125
|
+
http: { enabled: true, ...(partialConfig.http || {}) },
|
|
126
|
+
...(partialConfig.security ? { security: partialConfig.security } : {}),
|
|
127
|
+
...(partialConfig.advanced ? { advanced: partialConfig.advanced } : {})
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
export const tlsTerminateToHttp = (
|
|
131
|
+
partialConfig: Partial<IForwardConfig> & Pick<IForwardConfig, 'target'>
|
|
132
|
+
): IForwardConfig => ({
|
|
133
|
+
type: 'https-terminate-to-http',
|
|
134
|
+
target: partialConfig.target,
|
|
135
|
+
https: { ...(partialConfig.https || {}) },
|
|
136
|
+
acme: { enabled: true, maintenance: true, ...(partialConfig.acme || {}) },
|
|
137
|
+
http: { enabled: true, redirectToHttps: true, ...(partialConfig.http || {}) },
|
|
138
|
+
...(partialConfig.security ? { security: partialConfig.security } : {}),
|
|
139
|
+
...(partialConfig.advanced ? { advanced: partialConfig.advanced } : {})
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
export const tlsTerminateToHttps = (
|
|
143
|
+
partialConfig: Partial<IForwardConfig> & Pick<IForwardConfig, 'target'>
|
|
144
|
+
): IForwardConfig => ({
|
|
145
|
+
type: 'https-terminate-to-https',
|
|
146
|
+
target: partialConfig.target,
|
|
147
|
+
https: { ...(partialConfig.https || {}) },
|
|
148
|
+
acme: { enabled: true, maintenance: true, ...(partialConfig.acme || {}) },
|
|
149
|
+
http: { enabled: true, redirectToHttps: true, ...(partialConfig.http || {}) },
|
|
150
|
+
...(partialConfig.security ? { security: partialConfig.security } : {}),
|
|
151
|
+
...(partialConfig.advanced ? { advanced: partialConfig.advanced } : {})
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
export const httpsPassthrough = (
|
|
155
|
+
partialConfig: Partial<IForwardConfig> & Pick<IForwardConfig, 'target'>
|
|
156
|
+
): IForwardConfig => ({
|
|
157
|
+
type: 'https-passthrough',
|
|
158
|
+
target: partialConfig.target,
|
|
159
|
+
https: { forwardSni: true, ...(partialConfig.https || {}) },
|
|
160
|
+
...(partialConfig.security ? { security: partialConfig.security } : {}),
|
|
161
|
+
...(partialConfig.advanced ? { advanced: partialConfig.advanced } : {})
|
|
162
|
+
});
|