@push.rocks/smartproxy 6.0.1 → 7.0.1
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/index.d.ts +1 -1
- package/dist_ts/index.js +2 -2
- package/dist_ts/redirect/classes.redirect.d.ts +96 -0
- package/dist_ts/redirect/classes.redirect.js +194 -0
- package/package.json +1 -1
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/index.ts +1 -1
- package/ts/redirect/classes.redirect.ts +295 -0
- package/ts/classes.sslredirect.ts +0 -32
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@push.rocks/smartproxy',
|
|
6
|
-
version: '
|
|
6
|
+
version: '7.0.1',
|
|
7
7
|
description: 'A powerful proxy package that effectively handles high traffic, with features such as SSL/TLS support, port proxying, WebSocket handling, dynamic routing with authentication options, and automatic ACME certificate management.'
|
|
8
8
|
};
|
|
9
9
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSx3QkFBd0I7SUFDOUIsT0FBTyxFQUFFLE9BQU87SUFDaEIsV0FBVyxFQUFFLG1PQUFtTztDQUNqUCxDQUFBIn0=
|
package/dist_ts/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export * from './nfttablesproxy/classes.nftablesproxy.js';
|
|
2
2
|
export * from './networkproxy/classes.np.networkproxy.js';
|
|
3
3
|
export * from './port80handler/classes.port80handler.js';
|
|
4
|
-
export * from './classes.
|
|
4
|
+
export * from './redirect/classes.redirect.js';
|
|
5
5
|
export * from './smartproxy/classes.smartproxy.js';
|
|
6
6
|
export * from './smartproxy/classes.pp.snihandler.js';
|
|
7
7
|
export * from './smartproxy/classes.pp.interfaces.js';
|
package/dist_ts/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export * from './nfttablesproxy/classes.nftablesproxy.js';
|
|
2
2
|
export * from './networkproxy/classes.np.networkproxy.js';
|
|
3
3
|
export * from './port80handler/classes.port80handler.js';
|
|
4
|
-
export * from './classes.
|
|
4
|
+
export * from './redirect/classes.redirect.js';
|
|
5
5
|
export * from './smartproxy/classes.smartproxy.js';
|
|
6
6
|
export * from './smartproxy/classes.pp.snihandler.js';
|
|
7
7
|
export * from './smartproxy/classes.pp.interfaces.js';
|
|
8
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
8
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLDJDQUEyQyxDQUFDO0FBQzFELGNBQWMsMkNBQTJDLENBQUM7QUFDMUQsY0FBYywwQ0FBMEMsQ0FBQztBQUN6RCxjQUFjLGdDQUFnQyxDQUFDO0FBQy9DLGNBQWMsb0NBQW9DLENBQUM7QUFDbkQsY0FBYyx1Q0FBdUMsQ0FBQztBQUN0RCxjQUFjLHVDQUF1QyxDQUFDIn0=
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
export interface RedirectRule {
|
|
2
|
+
/**
|
|
3
|
+
* Optional protocol to match (http or https). If not specified, matches both.
|
|
4
|
+
*/
|
|
5
|
+
fromProtocol?: 'http' | 'https';
|
|
6
|
+
/**
|
|
7
|
+
* Optional hostname pattern to match. Can use * as wildcard.
|
|
8
|
+
* If not specified, matches all hosts.
|
|
9
|
+
*/
|
|
10
|
+
fromHost?: string;
|
|
11
|
+
/**
|
|
12
|
+
* Optional path prefix to match. If not specified, matches all paths.
|
|
13
|
+
*/
|
|
14
|
+
fromPath?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Target protocol for the redirect (http or https)
|
|
17
|
+
*/
|
|
18
|
+
toProtocol: 'http' | 'https';
|
|
19
|
+
/**
|
|
20
|
+
* Target hostname for the redirect. Can use $1, $2, etc. to reference
|
|
21
|
+
* captured groups from wildcard matches in fromHost.
|
|
22
|
+
*/
|
|
23
|
+
toHost: string;
|
|
24
|
+
/**
|
|
25
|
+
* Optional target path prefix. If not specified, keeps original path.
|
|
26
|
+
* Can use $path to reference the original path.
|
|
27
|
+
*/
|
|
28
|
+
toPath?: string;
|
|
29
|
+
/**
|
|
30
|
+
* HTTP status code for the redirect (301 for permanent, 302 for temporary)
|
|
31
|
+
*/
|
|
32
|
+
statusCode?: 301 | 302 | 307 | 308;
|
|
33
|
+
}
|
|
34
|
+
export declare class Redirect {
|
|
35
|
+
private httpServer?;
|
|
36
|
+
private httpsServer?;
|
|
37
|
+
private rules;
|
|
38
|
+
private httpPort;
|
|
39
|
+
private httpsPort;
|
|
40
|
+
private sslOptions?;
|
|
41
|
+
/**
|
|
42
|
+
* Create a new Redirect instance
|
|
43
|
+
* @param options Configuration options
|
|
44
|
+
*/
|
|
45
|
+
constructor(options?: {
|
|
46
|
+
httpPort?: number;
|
|
47
|
+
httpsPort?: number;
|
|
48
|
+
sslOptions?: {
|
|
49
|
+
key: Buffer;
|
|
50
|
+
cert: Buffer;
|
|
51
|
+
};
|
|
52
|
+
rules?: RedirectRule[];
|
|
53
|
+
});
|
|
54
|
+
/**
|
|
55
|
+
* Add a redirect rule
|
|
56
|
+
*/
|
|
57
|
+
addRule(rule: RedirectRule): void;
|
|
58
|
+
/**
|
|
59
|
+
* Remove all redirect rules
|
|
60
|
+
*/
|
|
61
|
+
clearRules(): void;
|
|
62
|
+
/**
|
|
63
|
+
* Set SSL options for HTTPS redirects
|
|
64
|
+
*/
|
|
65
|
+
setSslOptions(options: {
|
|
66
|
+
key: Buffer;
|
|
67
|
+
cert: Buffer;
|
|
68
|
+
}): void;
|
|
69
|
+
/**
|
|
70
|
+
* Process a request according to the configured rules
|
|
71
|
+
*/
|
|
72
|
+
private handleRequest;
|
|
73
|
+
/**
|
|
74
|
+
* Find a matching redirect rule for the given request
|
|
75
|
+
*/
|
|
76
|
+
private findMatchingRule;
|
|
77
|
+
/**
|
|
78
|
+
* Build the target URL for a redirect
|
|
79
|
+
*/
|
|
80
|
+
private buildTargetUrl;
|
|
81
|
+
/**
|
|
82
|
+
* Start the redirect server(s)
|
|
83
|
+
*/
|
|
84
|
+
start(): Promise<void>;
|
|
85
|
+
/**
|
|
86
|
+
* Stop the redirect server(s)
|
|
87
|
+
*/
|
|
88
|
+
stop(): Promise<void>;
|
|
89
|
+
}
|
|
90
|
+
export declare class SslRedirect {
|
|
91
|
+
private redirect;
|
|
92
|
+
port: number;
|
|
93
|
+
constructor(portArg: number);
|
|
94
|
+
start(): Promise<void>;
|
|
95
|
+
stop(): Promise<void>;
|
|
96
|
+
}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import * as plugins from '../plugins.js';
|
|
2
|
+
export class Redirect {
|
|
3
|
+
/**
|
|
4
|
+
* Create a new Redirect instance
|
|
5
|
+
* @param options Configuration options
|
|
6
|
+
*/
|
|
7
|
+
constructor(options = {}) {
|
|
8
|
+
this.rules = [];
|
|
9
|
+
this.httpPort = 80;
|
|
10
|
+
this.httpsPort = 443;
|
|
11
|
+
if (options.httpPort)
|
|
12
|
+
this.httpPort = options.httpPort;
|
|
13
|
+
if (options.httpsPort)
|
|
14
|
+
this.httpsPort = options.httpsPort;
|
|
15
|
+
if (options.sslOptions)
|
|
16
|
+
this.sslOptions = options.sslOptions;
|
|
17
|
+
if (options.rules)
|
|
18
|
+
this.rules = options.rules;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Add a redirect rule
|
|
22
|
+
*/
|
|
23
|
+
addRule(rule) {
|
|
24
|
+
this.rules.push(rule);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Remove all redirect rules
|
|
28
|
+
*/
|
|
29
|
+
clearRules() {
|
|
30
|
+
this.rules = [];
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Set SSL options for HTTPS redirects
|
|
34
|
+
*/
|
|
35
|
+
setSslOptions(options) {
|
|
36
|
+
this.sslOptions = options;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Process a request according to the configured rules
|
|
40
|
+
*/
|
|
41
|
+
handleRequest(request, response, protocol) {
|
|
42
|
+
const requestUrl = new URL(request.url || '/', `${protocol}://${request.headers.host || 'localhost'}`);
|
|
43
|
+
const host = requestUrl.hostname;
|
|
44
|
+
const path = requestUrl.pathname + requestUrl.search;
|
|
45
|
+
// Find matching rule
|
|
46
|
+
const matchedRule = this.findMatchingRule(protocol, host, path);
|
|
47
|
+
if (matchedRule) {
|
|
48
|
+
const targetUrl = this.buildTargetUrl(matchedRule, host, path);
|
|
49
|
+
console.log(`Redirecting ${protocol}://${host}${path} to ${targetUrl}`);
|
|
50
|
+
response.writeHead(matchedRule.statusCode || 302, {
|
|
51
|
+
Location: targetUrl,
|
|
52
|
+
});
|
|
53
|
+
response.end();
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
// No matching rule, send 404
|
|
57
|
+
response.writeHead(404, { 'Content-Type': 'text/plain' });
|
|
58
|
+
response.end('Not Found');
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Find a matching redirect rule for the given request
|
|
63
|
+
*/
|
|
64
|
+
findMatchingRule(protocol, host, path) {
|
|
65
|
+
return this.rules.find((rule) => {
|
|
66
|
+
// Check protocol match
|
|
67
|
+
if (rule.fromProtocol && rule.fromProtocol !== protocol) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
// Check host match
|
|
71
|
+
if (rule.fromHost) {
|
|
72
|
+
const pattern = rule.fromHost.replace(/\*/g, '(.*)');
|
|
73
|
+
const regex = new RegExp(`^${pattern}$`);
|
|
74
|
+
if (!regex.test(host)) {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// Check path match
|
|
79
|
+
if (rule.fromPath && !path.startsWith(rule.fromPath)) {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
return true;
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Build the target URL for a redirect
|
|
87
|
+
*/
|
|
88
|
+
buildTargetUrl(rule, originalHost, originalPath) {
|
|
89
|
+
let targetHost = rule.toHost;
|
|
90
|
+
// Replace wildcards in host
|
|
91
|
+
if (rule.fromHost && rule.fromHost.includes('*')) {
|
|
92
|
+
const pattern = rule.fromHost.replace(/\*/g, '(.*)');
|
|
93
|
+
const regex = new RegExp(`^${pattern}$`);
|
|
94
|
+
const matches = originalHost.match(regex);
|
|
95
|
+
if (matches) {
|
|
96
|
+
for (let i = 1; i < matches.length; i++) {
|
|
97
|
+
targetHost = targetHost.replace(`$${i}`, matches[i]);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// Build target path
|
|
102
|
+
let targetPath = originalPath;
|
|
103
|
+
if (rule.toPath) {
|
|
104
|
+
if (rule.toPath.includes('$path')) {
|
|
105
|
+
// Replace $path with original path, optionally removing the fromPath prefix
|
|
106
|
+
const pathSuffix = rule.fromPath ?
|
|
107
|
+
originalPath.substring(rule.fromPath.length) :
|
|
108
|
+
originalPath;
|
|
109
|
+
targetPath = rule.toPath.replace('$path', pathSuffix);
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
targetPath = rule.toPath;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return `${rule.toProtocol}://${targetHost}${targetPath}`;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Start the redirect server(s)
|
|
119
|
+
*/
|
|
120
|
+
async start() {
|
|
121
|
+
const tasks = [];
|
|
122
|
+
// Create and start HTTP server if we have a port
|
|
123
|
+
if (this.httpPort) {
|
|
124
|
+
this.httpServer = plugins.http.createServer((req, res) => this.handleRequest(req, res, 'http'));
|
|
125
|
+
const httpStartPromise = new Promise((resolve) => {
|
|
126
|
+
this.httpServer?.listen(this.httpPort, () => {
|
|
127
|
+
console.log(`HTTP redirect server started on port ${this.httpPort}`);
|
|
128
|
+
resolve();
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
tasks.push(httpStartPromise);
|
|
132
|
+
}
|
|
133
|
+
// Create and start HTTPS server if we have SSL options and a port
|
|
134
|
+
if (this.httpsPort && this.sslOptions) {
|
|
135
|
+
this.httpsServer = plugins.https.createServer(this.sslOptions, (req, res) => this.handleRequest(req, res, 'https'));
|
|
136
|
+
const httpsStartPromise = new Promise((resolve) => {
|
|
137
|
+
this.httpsServer?.listen(this.httpsPort, () => {
|
|
138
|
+
console.log(`HTTPS redirect server started on port ${this.httpsPort}`);
|
|
139
|
+
resolve();
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
tasks.push(httpsStartPromise);
|
|
143
|
+
}
|
|
144
|
+
// Wait for all servers to start
|
|
145
|
+
await Promise.all(tasks);
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Stop the redirect server(s)
|
|
149
|
+
*/
|
|
150
|
+
async stop() {
|
|
151
|
+
const tasks = [];
|
|
152
|
+
if (this.httpServer) {
|
|
153
|
+
const httpStopPromise = new Promise((resolve) => {
|
|
154
|
+
this.httpServer?.close(() => {
|
|
155
|
+
console.log('HTTP redirect server stopped');
|
|
156
|
+
resolve();
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
tasks.push(httpStopPromise);
|
|
160
|
+
}
|
|
161
|
+
if (this.httpsServer) {
|
|
162
|
+
const httpsStopPromise = new Promise((resolve) => {
|
|
163
|
+
this.httpsServer?.close(() => {
|
|
164
|
+
console.log('HTTPS redirect server stopped');
|
|
165
|
+
resolve();
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
tasks.push(httpsStopPromise);
|
|
169
|
+
}
|
|
170
|
+
await Promise.all(tasks);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
// For backward compatibility
|
|
174
|
+
export class SslRedirect {
|
|
175
|
+
constructor(portArg) {
|
|
176
|
+
this.port = portArg;
|
|
177
|
+
this.redirect = new Redirect({
|
|
178
|
+
httpPort: portArg,
|
|
179
|
+
rules: [{
|
|
180
|
+
fromProtocol: 'http',
|
|
181
|
+
toProtocol: 'https',
|
|
182
|
+
toHost: '$1',
|
|
183
|
+
statusCode: 302
|
|
184
|
+
}]
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
async start() {
|
|
188
|
+
await this.redirect.start();
|
|
189
|
+
}
|
|
190
|
+
async stop() {
|
|
191
|
+
await this.redirect.stop();
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5yZWRpcmVjdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL3JlZGlyZWN0L2NsYXNzZXMucmVkaXJlY3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxlQUFlLENBQUM7QUEwQ3pDLE1BQU0sT0FBTyxRQUFRO0lBV25COzs7T0FHRztJQUNILFlBQVksVUFRUixFQUFFO1FBcEJFLFVBQUssR0FBbUIsRUFBRSxDQUFDO1FBQzNCLGFBQVEsR0FBVyxFQUFFLENBQUM7UUFDdEIsY0FBUyxHQUFXLEdBQUcsQ0FBQztRQW1COUIsSUFBSSxPQUFPLENBQUMsUUFBUTtZQUFFLElBQUksQ0FBQyxRQUFRLEdBQUcsT0FBTyxDQUFDLFFBQVEsQ0FBQztRQUN2RCxJQUFJLE9BQU8sQ0FBQyxTQUFTO1lBQUUsSUFBSSxDQUFDLFNBQVMsR0FBRyxPQUFPLENBQUMsU0FBUyxDQUFDO1FBQzFELElBQUksT0FBTyxDQUFDLFVBQVU7WUFBRSxJQUFJLENBQUMsVUFBVSxHQUFHLE9BQU8sQ0FBQyxVQUFVLENBQUM7UUFDN0QsSUFBSSxPQUFPLENBQUMsS0FBSztZQUFFLElBQUksQ0FBQyxLQUFLLEdBQUcsT0FBTyxDQUFDLEtBQUssQ0FBQztJQUNoRCxDQUFDO0lBRUQ7O09BRUc7SUFDSSxPQUFPLENBQUMsSUFBa0I7UUFDL0IsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDeEIsQ0FBQztJQUVEOztPQUVHO0lBQ0ksVUFBVTtRQUNmLElBQUksQ0FBQyxLQUFLLEdBQUcsRUFBRSxDQUFDO0lBQ2xCLENBQUM7SUFFRDs7T0FFRztJQUNJLGFBQWEsQ0FBQyxPQUFzQztRQUN6RCxJQUFJLENBQUMsVUFBVSxHQUFHLE9BQU8sQ0FBQztJQUM1QixDQUFDO0lBRUQ7O09BRUc7SUFDSyxhQUFhLENBQ25CLE9BQXFDLEVBQ3JDLFFBQXFDLEVBQ3JDLFFBQTBCO1FBRTFCLE1BQU0sVUFBVSxHQUFHLElBQUksR0FBRyxDQUN4QixPQUFPLENBQUMsR0FBRyxJQUFJLEdBQUcsRUFDbEIsR0FBRyxRQUFRLE1BQU0sT0FBTyxDQUFDLE9BQU8sQ0FBQyxJQUFJLElBQUksV0FBVyxFQUFFLENBQ3ZELENBQUM7UUFFRixNQUFNLElBQUksR0FBRyxVQUFVLENBQUMsUUFBUSxDQUFDO1FBQ2pDLE1BQU0sSUFBSSxHQUFHLFVBQVUsQ0FBQyxRQUFRLEdBQUcsVUFBVSxDQUFDLE1BQU0sQ0FBQztRQUVyRCxxQkFBcUI7UUFDckIsTUFBTSxXQUFXLEdBQUcsSUFBSSxDQUFDLGdCQUFnQixDQUFDLFFBQVEsRUFBRSxJQUFJLEVBQUUsSUFBSSxDQUFDLENBQUM7UUFFaEUsSUFBSSxXQUFXLEVBQUUsQ0FBQztZQUNoQixNQUFNLFNBQVMsR0FBRyxJQUFJLENBQUMsY0FBYyxDQUFDLFdBQVcsRUFBRSxJQUFJLEVBQUUsSUFBSSxDQUFDLENBQUM7WUFFL0QsT0FBTyxDQUFDLEdBQUcsQ0FBQyxlQUFlLFFBQVEsTUFBTSxJQUFJLEdBQUcsSUFBSSxPQUFPLFNBQVMsRUFBRSxDQUFDLENBQUM7WUFFeEUsUUFBUSxDQUFDLFNBQVMsQ0FBQyxXQUFXLENBQUMsVUFBVSxJQUFJLEdBQUcsRUFBRTtnQkFDaEQsUUFBUSxFQUFFLFNBQVM7YUFDcEIsQ0FBQyxDQUFDO1lBQ0gsUUFBUSxDQUFDLEdBQUcsRUFBRSxDQUFDO1FBQ2pCLENBQUM7YUFBTSxDQUFDO1lBQ04sNkJBQTZCO1lBQzdCLFFBQVEsQ0FBQyxTQUFTLENBQUMsR0FBRyxFQUFFLEVBQUUsY0FBYyxFQUFFLFlBQVksRUFBRSxDQUFDLENBQUM7WUFDMUQsUUFBUSxDQUFDLEdBQUcsQ0FBQyxXQUFXLENBQUMsQ0FBQztRQUM1QixDQUFDO0lBQ0gsQ0FBQztJQUVEOztPQUVHO0lBQ0ssZ0JBQWdCLENBQ3RCLFFBQTBCLEVBQzFCLElBQVksRUFDWixJQUFZO1FBRVosT0FBTyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDLElBQUksRUFBRSxFQUFFO1lBQzlCLHVCQUF1QjtZQUN2QixJQUFJLElBQUksQ0FBQyxZQUFZLElBQUksSUFBSSxDQUFDLFlBQVksS0FBSyxRQUFRLEVBQUUsQ0FBQztnQkFDeEQsT0FBTyxLQUFLLENBQUM7WUFDZixDQUFDO1lBRUQsbUJBQW1CO1lBQ25CLElBQUksSUFBSSxDQUFDLFFBQVEsRUFBRSxDQUFDO2dCQUNsQixNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxLQUFLLEVBQUUsTUFBTSxDQUFDLENBQUM7Z0JBQ3JELE1BQU0sS0FBSyxHQUFHLElBQUksTUFBTSxDQUFDLElBQUksT0FBTyxHQUFHLENBQUMsQ0FBQztnQkFDekMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQztvQkFDdEIsT0FBTyxLQUFLLENBQUM7Z0JBQ2YsQ0FBQztZQUNILENBQUM7WUFFRCxtQkFBbUI7WUFDbkIsSUFBSSxJQUFJLENBQUMsUUFBUSxJQUFJLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQztnQkFDckQsT0FBTyxLQUFLLENBQUM7WUFDZixDQUFDO1lBRUQsT0FBTyxJQUFJLENBQUM7UUFDZCxDQUFDLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFRDs7T0FFRztJQUNLLGNBQWMsQ0FBQyxJQUFrQixFQUFFLFlBQW9CLEVBQUUsWUFBb0I7UUFDbkYsSUFBSSxVQUFVLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQztRQUU3Qiw0QkFBNEI7UUFDNUIsSUFBSSxJQUFJLENBQUMsUUFBUSxJQUFJLElBQUksQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUM7WUFDakQsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsS0FBSyxFQUFFLE1BQU0sQ0FBQyxDQUFDO1lBQ3JELE1BQU0sS0FBSyxHQUFHLElBQUksTUFBTSxDQUFDLElBQUksT0FBTyxHQUFHLENBQUMsQ0FBQztZQUN6QyxNQUFNLE9BQU8sR0FBRyxZQUFZLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxDQUFDO1lBRTFDLElBQUksT0FBTyxFQUFFLENBQUM7Z0JBQ1osS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLE9BQU8sQ0FBQyxNQUFNLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQztvQkFDeEMsVUFBVSxHQUFHLFVBQVUsQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLEVBQUUsRUFBRSxPQUFPLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDdkQsQ0FBQztZQUNILENBQUM7UUFDSCxDQUFDO1FBRUQsb0JBQW9CO1FBQ3BCLElBQUksVUFBVSxHQUFHLFlBQVksQ0FBQztRQUM5QixJQUFJLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztZQUNoQixJQUFJLElBQUksQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7Z0JBQ2xDLDRFQUE0RTtnQkFDNUUsTUFBTSxVQUFVLEdBQUcsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDO29CQUNoQyxZQUFZLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQztvQkFDOUMsWUFBWSxDQUFDO2dCQUVmLFVBQVUsR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxPQUFPLEVBQUUsVUFBVSxDQUFDLENBQUM7WUFDeEQsQ0FBQztpQkFBTSxDQUFDO2dCQUNOLFVBQVUsR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDO1lBQzNCLENBQUM7UUFDSCxDQUFDO1FBRUQsT0FBTyxHQUFHLElBQUksQ0FBQyxVQUFVLE1BQU0sVUFBVSxHQUFHLFVBQVUsRUFBRSxDQUFDO0lBQzNELENBQUM7SUFFRDs7T0FFRztJQUNJLEtBQUssQ0FBQyxLQUFLO1FBQ2hCLE1BQU0sS0FBSyxHQUFHLEVBQUUsQ0FBQztRQUVqQixpREFBaUQ7UUFDakQsSUFBSSxJQUFJLENBQUMsUUFBUSxFQUFFLENBQUM7WUFDbEIsSUFBSSxDQUFDLFVBQVUsR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLFlBQVksQ0FBQyxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsRUFBRSxDQUN2RCxJQUFJLENBQUMsYUFBYSxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsTUFBTSxDQUFDLENBQ3JDLENBQUM7WUFFRixNQUFNLGdCQUFnQixHQUFHLElBQUksT0FBTyxDQUFPLENBQUMsT0FBTyxFQUFFLEVBQUU7Z0JBQ3JELElBQUksQ0FBQyxVQUFVLEVBQUUsTUFBTSxDQUFDLElBQUksQ0FBQyxRQUFRLEVBQUUsR0FBRyxFQUFFO29CQUMxQyxPQUFPLENBQUMsR0FBRyxDQUFDLHdDQUF3QyxJQUFJLENBQUMsUUFBUSxFQUFFLENBQUMsQ0FBQztvQkFDckUsT0FBTyxFQUFFLENBQUM7Z0JBQ1osQ0FBQyxDQUFDLENBQUM7WUFDTCxDQUFDLENBQUMsQ0FBQztZQUVILEtBQUssQ0FBQyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztRQUMvQixDQUFDO1FBRUQsa0VBQWtFO1FBQ2xFLElBQUksSUFBSSxDQUFDLFNBQVMsSUFBSSxJQUFJLENBQUMsVUFBVSxFQUFFLENBQUM7WUFDdEMsSUFBSSxDQUFDLFdBQVcsR0FBRyxPQUFPLENBQUMsS0FBSyxDQUFDLFlBQVksQ0FBQyxJQUFJLENBQUMsVUFBVSxFQUFFLENBQUMsR0FBRyxFQUFFLEdBQUcsRUFBRSxFQUFFLENBQzFFLElBQUksQ0FBQyxhQUFhLENBQUMsR0FBRyxFQUFFLEdBQUcsRUFBRSxPQUFPLENBQUMsQ0FDdEMsQ0FBQztZQUVGLE1BQU0saUJBQWlCLEdBQUcsSUFBSSxPQUFPLENBQU8sQ0FBQyxPQUFPLEVBQUUsRUFBRTtnQkFDdEQsSUFBSSxDQUFDLFdBQVcsRUFBRSxNQUFNLENBQUMsSUFBSSxDQUFDLFNBQVMsRUFBRSxHQUFHLEVBQUU7b0JBQzVDLE9BQU8sQ0FBQyxHQUFHLENBQUMseUNBQXlDLElBQUksQ0FBQyxTQUFTLEVBQUUsQ0FBQyxDQUFDO29CQUN2RSxPQUFPLEVBQUUsQ0FBQztnQkFDWixDQUFDLENBQUMsQ0FBQztZQUNMLENBQUMsQ0FBQyxDQUFDO1lBRUgsS0FBSyxDQUFDLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDO1FBQ2hDLENBQUM7UUFFRCxnQ0FBZ0M7UUFDaEMsTUFBTSxPQUFPLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQzNCLENBQUM7SUFFRDs7T0FFRztJQUNJLEtBQUssQ0FBQyxJQUFJO1FBQ2YsTUFBTSxLQUFLLEdBQUcsRUFBRSxDQUFDO1FBRWpCLElBQUksSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDO1lBQ3BCLE1BQU0sZUFBZSxHQUFHLElBQUksT0FBTyxDQUFPLENBQUMsT0FBTyxFQUFFLEVBQUU7Z0JBQ3BELElBQUksQ0FBQyxVQUFVLEVBQUUsS0FBSyxDQUFDLEdBQUcsRUFBRTtvQkFDMUIsT0FBTyxDQUFDLEdBQUcsQ0FBQyw4QkFBOEIsQ0FBQyxDQUFDO29CQUM1QyxPQUFPLEVBQUUsQ0FBQztnQkFDWixDQUFDLENBQUMsQ0FBQztZQUNMLENBQUMsQ0FBQyxDQUFDO1lBQ0gsS0FBSyxDQUFDLElBQUksQ0FBQyxlQUFlLENBQUMsQ0FBQztRQUM5QixDQUFDO1FBRUQsSUFBSSxJQUFJLENBQUMsV0FBVyxFQUFFLENBQUM7WUFDckIsTUFBTSxnQkFBZ0IsR0FBRyxJQUFJLE9BQU8sQ0FBTyxDQUFDLE9BQU8sRUFBRSxFQUFFO2dCQUNyRCxJQUFJLENBQUMsV0FBVyxFQUFFLEtBQUssQ0FBQyxHQUFHLEVBQUU7b0JBQzNCLE9BQU8sQ0FBQyxHQUFHLENBQUMsK0JBQStCLENBQUMsQ0FBQztvQkFDN0MsT0FBTyxFQUFFLENBQUM7Z0JBQ1osQ0FBQyxDQUFDLENBQUM7WUFDTCxDQUFDLENBQUMsQ0FBQztZQUNILEtBQUssQ0FBQyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztRQUMvQixDQUFDO1FBRUQsTUFBTSxPQUFPLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQzNCLENBQUM7Q0FDRjtBQUVELDZCQUE2QjtBQUM3QixNQUFNLE9BQU8sV0FBVztJQUl0QixZQUFZLE9BQWU7UUFDekIsSUFBSSxDQUFDLElBQUksR0FBRyxPQUFPLENBQUM7UUFDcEIsSUFBSSxDQUFDLFFBQVEsR0FBRyxJQUFJLFFBQVEsQ0FBQztZQUMzQixRQUFRLEVBQUUsT0FBTztZQUNqQixLQUFLLEVBQUUsQ0FBQztvQkFDTixZQUFZLEVBQUUsTUFBTTtvQkFDcEIsVUFBVSxFQUFFLE9BQU87b0JBQ25CLE1BQU0sRUFBRSxJQUFJO29CQUNaLFVBQVUsRUFBRSxHQUFHO2lCQUNoQixDQUFDO1NBQ0gsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVNLEtBQUssQ0FBQyxLQUFLO1FBQ2hCLE1BQU0sSUFBSSxDQUFDLFFBQVEsQ0FBQyxLQUFLLEVBQUUsQ0FBQztJQUM5QixDQUFDO0lBRU0sS0FBSyxDQUFDLElBQUk7UUFDZixNQUFNLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxFQUFFLENBQUM7SUFDN0IsQ0FBQztDQUNGIn0=
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@push.rocks/smartproxy",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "A powerful proxy package that effectively handles high traffic, with features such as SSL/TLS support, port proxying, WebSocket handling, dynamic routing with authentication options, and automatic ACME certificate management.",
|
|
6
6
|
"main": "dist_ts/index.js",
|
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@push.rocks/smartproxy',
|
|
6
|
-
version: '
|
|
6
|
+
version: '7.0.1',
|
|
7
7
|
description: 'A powerful proxy package that effectively handles high traffic, with features such as SSL/TLS support, port proxying, WebSocket handling, dynamic routing with authentication options, and automatic ACME certificate management.'
|
|
8
8
|
}
|
package/ts/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export * from './nfttablesproxy/classes.nftablesproxy.js';
|
|
2
2
|
export * from './networkproxy/classes.np.networkproxy.js';
|
|
3
3
|
export * from './port80handler/classes.port80handler.js';
|
|
4
|
-
export * from './classes.
|
|
4
|
+
export * from './redirect/classes.redirect.js';
|
|
5
5
|
export * from './smartproxy/classes.smartproxy.js';
|
|
6
6
|
export * from './smartproxy/classes.pp.snihandler.js';
|
|
7
7
|
export * from './smartproxy/classes.pp.interfaces.js';
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import * as plugins from '../plugins.js';
|
|
2
|
+
|
|
3
|
+
export interface RedirectRule {
|
|
4
|
+
/**
|
|
5
|
+
* Optional protocol to match (http or https). If not specified, matches both.
|
|
6
|
+
*/
|
|
7
|
+
fromProtocol?: 'http' | 'https';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Optional hostname pattern to match. Can use * as wildcard.
|
|
11
|
+
* If not specified, matches all hosts.
|
|
12
|
+
*/
|
|
13
|
+
fromHost?: string;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Optional path prefix to match. If not specified, matches all paths.
|
|
17
|
+
*/
|
|
18
|
+
fromPath?: string;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Target protocol for the redirect (http or https)
|
|
22
|
+
*/
|
|
23
|
+
toProtocol: 'http' | 'https';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Target hostname for the redirect. Can use $1, $2, etc. to reference
|
|
27
|
+
* captured groups from wildcard matches in fromHost.
|
|
28
|
+
*/
|
|
29
|
+
toHost: string;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Optional target path prefix. If not specified, keeps original path.
|
|
33
|
+
* Can use $path to reference the original path.
|
|
34
|
+
*/
|
|
35
|
+
toPath?: string;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* HTTP status code for the redirect (301 for permanent, 302 for temporary)
|
|
39
|
+
*/
|
|
40
|
+
statusCode?: 301 | 302 | 307 | 308;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export class Redirect {
|
|
44
|
+
private httpServer?: plugins.http.Server;
|
|
45
|
+
private httpsServer?: plugins.https.Server;
|
|
46
|
+
private rules: RedirectRule[] = [];
|
|
47
|
+
private httpPort: number = 80;
|
|
48
|
+
private httpsPort: number = 443;
|
|
49
|
+
private sslOptions?: {
|
|
50
|
+
key: Buffer;
|
|
51
|
+
cert: Buffer;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Create a new Redirect instance
|
|
56
|
+
* @param options Configuration options
|
|
57
|
+
*/
|
|
58
|
+
constructor(options: {
|
|
59
|
+
httpPort?: number;
|
|
60
|
+
httpsPort?: number;
|
|
61
|
+
sslOptions?: {
|
|
62
|
+
key: Buffer;
|
|
63
|
+
cert: Buffer;
|
|
64
|
+
};
|
|
65
|
+
rules?: RedirectRule[];
|
|
66
|
+
} = {}) {
|
|
67
|
+
if (options.httpPort) this.httpPort = options.httpPort;
|
|
68
|
+
if (options.httpsPort) this.httpsPort = options.httpsPort;
|
|
69
|
+
if (options.sslOptions) this.sslOptions = options.sslOptions;
|
|
70
|
+
if (options.rules) this.rules = options.rules;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Add a redirect rule
|
|
75
|
+
*/
|
|
76
|
+
public addRule(rule: RedirectRule): void {
|
|
77
|
+
this.rules.push(rule);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Remove all redirect rules
|
|
82
|
+
*/
|
|
83
|
+
public clearRules(): void {
|
|
84
|
+
this.rules = [];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Set SSL options for HTTPS redirects
|
|
89
|
+
*/
|
|
90
|
+
public setSslOptions(options: { key: Buffer; cert: Buffer }): void {
|
|
91
|
+
this.sslOptions = options;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Process a request according to the configured rules
|
|
96
|
+
*/
|
|
97
|
+
private handleRequest(
|
|
98
|
+
request: plugins.http.IncomingMessage,
|
|
99
|
+
response: plugins.http.ServerResponse,
|
|
100
|
+
protocol: 'http' | 'https'
|
|
101
|
+
): void {
|
|
102
|
+
const requestUrl = new URL(
|
|
103
|
+
request.url || '/',
|
|
104
|
+
`${protocol}://${request.headers.host || 'localhost'}`
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
const host = requestUrl.hostname;
|
|
108
|
+
const path = requestUrl.pathname + requestUrl.search;
|
|
109
|
+
|
|
110
|
+
// Find matching rule
|
|
111
|
+
const matchedRule = this.findMatchingRule(protocol, host, path);
|
|
112
|
+
|
|
113
|
+
if (matchedRule) {
|
|
114
|
+
const targetUrl = this.buildTargetUrl(matchedRule, host, path);
|
|
115
|
+
|
|
116
|
+
console.log(`Redirecting ${protocol}://${host}${path} to ${targetUrl}`);
|
|
117
|
+
|
|
118
|
+
response.writeHead(matchedRule.statusCode || 302, {
|
|
119
|
+
Location: targetUrl,
|
|
120
|
+
});
|
|
121
|
+
response.end();
|
|
122
|
+
} else {
|
|
123
|
+
// No matching rule, send 404
|
|
124
|
+
response.writeHead(404, { 'Content-Type': 'text/plain' });
|
|
125
|
+
response.end('Not Found');
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Find a matching redirect rule for the given request
|
|
131
|
+
*/
|
|
132
|
+
private findMatchingRule(
|
|
133
|
+
protocol: 'http' | 'https',
|
|
134
|
+
host: string,
|
|
135
|
+
path: string
|
|
136
|
+
): RedirectRule | undefined {
|
|
137
|
+
return this.rules.find((rule) => {
|
|
138
|
+
// Check protocol match
|
|
139
|
+
if (rule.fromProtocol && rule.fromProtocol !== protocol) {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Check host match
|
|
144
|
+
if (rule.fromHost) {
|
|
145
|
+
const pattern = rule.fromHost.replace(/\*/g, '(.*)');
|
|
146
|
+
const regex = new RegExp(`^${pattern}$`);
|
|
147
|
+
if (!regex.test(host)) {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Check path match
|
|
153
|
+
if (rule.fromPath && !path.startsWith(rule.fromPath)) {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return true;
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Build the target URL for a redirect
|
|
163
|
+
*/
|
|
164
|
+
private buildTargetUrl(rule: RedirectRule, originalHost: string, originalPath: string): string {
|
|
165
|
+
let targetHost = rule.toHost;
|
|
166
|
+
|
|
167
|
+
// Replace wildcards in host
|
|
168
|
+
if (rule.fromHost && rule.fromHost.includes('*')) {
|
|
169
|
+
const pattern = rule.fromHost.replace(/\*/g, '(.*)');
|
|
170
|
+
const regex = new RegExp(`^${pattern}$`);
|
|
171
|
+
const matches = originalHost.match(regex);
|
|
172
|
+
|
|
173
|
+
if (matches) {
|
|
174
|
+
for (let i = 1; i < matches.length; i++) {
|
|
175
|
+
targetHost = targetHost.replace(`$${i}`, matches[i]);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Build target path
|
|
181
|
+
let targetPath = originalPath;
|
|
182
|
+
if (rule.toPath) {
|
|
183
|
+
if (rule.toPath.includes('$path')) {
|
|
184
|
+
// Replace $path with original path, optionally removing the fromPath prefix
|
|
185
|
+
const pathSuffix = rule.fromPath ?
|
|
186
|
+
originalPath.substring(rule.fromPath.length) :
|
|
187
|
+
originalPath;
|
|
188
|
+
|
|
189
|
+
targetPath = rule.toPath.replace('$path', pathSuffix);
|
|
190
|
+
} else {
|
|
191
|
+
targetPath = rule.toPath;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return `${rule.toProtocol}://${targetHost}${targetPath}`;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Start the redirect server(s)
|
|
200
|
+
*/
|
|
201
|
+
public async start(): Promise<void> {
|
|
202
|
+
const tasks = [];
|
|
203
|
+
|
|
204
|
+
// Create and start HTTP server if we have a port
|
|
205
|
+
if (this.httpPort) {
|
|
206
|
+
this.httpServer = plugins.http.createServer((req, res) =>
|
|
207
|
+
this.handleRequest(req, res, 'http')
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
const httpStartPromise = new Promise<void>((resolve) => {
|
|
211
|
+
this.httpServer?.listen(this.httpPort, () => {
|
|
212
|
+
console.log(`HTTP redirect server started on port ${this.httpPort}`);
|
|
213
|
+
resolve();
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
tasks.push(httpStartPromise);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Create and start HTTPS server if we have SSL options and a port
|
|
221
|
+
if (this.httpsPort && this.sslOptions) {
|
|
222
|
+
this.httpsServer = plugins.https.createServer(this.sslOptions, (req, res) =>
|
|
223
|
+
this.handleRequest(req, res, 'https')
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
const httpsStartPromise = new Promise<void>((resolve) => {
|
|
227
|
+
this.httpsServer?.listen(this.httpsPort, () => {
|
|
228
|
+
console.log(`HTTPS redirect server started on port ${this.httpsPort}`);
|
|
229
|
+
resolve();
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
tasks.push(httpsStartPromise);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Wait for all servers to start
|
|
237
|
+
await Promise.all(tasks);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Stop the redirect server(s)
|
|
242
|
+
*/
|
|
243
|
+
public async stop(): Promise<void> {
|
|
244
|
+
const tasks = [];
|
|
245
|
+
|
|
246
|
+
if (this.httpServer) {
|
|
247
|
+
const httpStopPromise = new Promise<void>((resolve) => {
|
|
248
|
+
this.httpServer?.close(() => {
|
|
249
|
+
console.log('HTTP redirect server stopped');
|
|
250
|
+
resolve();
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
tasks.push(httpStopPromise);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (this.httpsServer) {
|
|
257
|
+
const httpsStopPromise = new Promise<void>((resolve) => {
|
|
258
|
+
this.httpsServer?.close(() => {
|
|
259
|
+
console.log('HTTPS redirect server stopped');
|
|
260
|
+
resolve();
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
tasks.push(httpsStopPromise);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
await Promise.all(tasks);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// For backward compatibility
|
|
271
|
+
export class SslRedirect {
|
|
272
|
+
private redirect: Redirect;
|
|
273
|
+
port: number;
|
|
274
|
+
|
|
275
|
+
constructor(portArg: number) {
|
|
276
|
+
this.port = portArg;
|
|
277
|
+
this.redirect = new Redirect({
|
|
278
|
+
httpPort: portArg,
|
|
279
|
+
rules: [{
|
|
280
|
+
fromProtocol: 'http',
|
|
281
|
+
toProtocol: 'https',
|
|
282
|
+
toHost: '$1',
|
|
283
|
+
statusCode: 302
|
|
284
|
+
}]
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
public async start() {
|
|
289
|
+
await this.redirect.start();
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
public async stop() {
|
|
293
|
+
await this.redirect.stop();
|
|
294
|
+
}
|
|
295
|
+
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import * as plugins from './plugins.js';
|
|
2
|
-
|
|
3
|
-
export class SslRedirect {
|
|
4
|
-
httpServer: plugins.http.Server;
|
|
5
|
-
port: number;
|
|
6
|
-
constructor(portArg: number) {
|
|
7
|
-
this.port = portArg;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
public async start() {
|
|
11
|
-
this.httpServer = plugins.http.createServer((request, response) => {
|
|
12
|
-
const requestUrl = new URL(request.url, `http://${request.headers.host}`);
|
|
13
|
-
const completeUrlWithoutProtocol = `${requestUrl.host}${requestUrl.pathname}${requestUrl.search}`;
|
|
14
|
-
const redirectUrl = `https://${completeUrlWithoutProtocol}`;
|
|
15
|
-
console.log(`Got http request for http://${completeUrlWithoutProtocol}`);
|
|
16
|
-
console.log(`Redirecting to ${redirectUrl}`);
|
|
17
|
-
response.writeHead(302, {
|
|
18
|
-
Location: redirectUrl,
|
|
19
|
-
});
|
|
20
|
-
response.end();
|
|
21
|
-
});
|
|
22
|
-
this.httpServer.listen(this.port);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
public async stop() {
|
|
26
|
-
const done = plugins.smartpromise.defer();
|
|
27
|
-
this.httpServer.close(() => {
|
|
28
|
-
done.resolve();
|
|
29
|
-
});
|
|
30
|
-
await done.promise;
|
|
31
|
-
}
|
|
32
|
-
}
|