@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.
@@ -3,7 +3,7 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/smartproxy',
6
- version: '6.0.1',
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=
@@ -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.sslredirect.js';
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.sslredirect.js';
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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLDJDQUEyQyxDQUFDO0FBQzFELGNBQWMsMkNBQTJDLENBQUM7QUFDMUQsY0FBYywwQ0FBMEMsQ0FBQztBQUN6RCxjQUFjLDBCQUEwQixDQUFDO0FBQ3pDLGNBQWMsb0NBQW9DLENBQUM7QUFDbkQsY0FBYyx1Q0FBdUMsQ0FBQztBQUN0RCxjQUFjLHVDQUF1QyxDQUFDIn0=
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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5yZWRpcmVjdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL3JlZGlyZWN0L2NsYXNzZXMucmVkaXJlY3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxlQUFlLENBQUM7QUEwQ3pDLE1BQU0sT0FBTyxRQUFRO0lBV25COzs7T0FHRztJQUNILFlBQVksVUFRUixFQUFFO1FBcEJFLFVBQUssR0FBbUIsRUFBRSxDQUFDO1FBQzNCLGFBQVEsR0FBVyxFQUFFLENBQUM7UUFDdEIsY0FBUyxHQUFXLEdBQUcsQ0FBQztRQW1COUIsSUFBSSxPQUFPLENBQUMsUUFBUTtZQUFFLElBQUksQ0FBQyxRQUFRLEdBQUcsT0FBTyxDQUFDLFFBQVEsQ0FBQztRQUN2RCxJQUFJLE9BQU8sQ0FBQyxTQUFTO1lBQUUsSUFBSSxDQUFDLFNBQVMsR0FBRyxPQUFPLENBQUMsU0FBUyxDQUFDO1FBQzFELElBQUksT0FBTyxDQUFDLFVBQVU7WUFBRSxJQUFJLENBQUMsVUFBVSxHQUFHLE9BQU8sQ0FBQyxVQUFVLENBQUM7UUFDN0QsSUFBSSxPQUFPLENBQUMsS0FBSztZQUFFLElBQUksQ0FBQyxLQUFLLEdBQUcsT0FBTyxDQUFDLEtBQUssQ0FBQztJQUNoRCxDQUFDO0lBRUQ7O09BRUc7SUFDSSxPQUFPLENBQUMsSUFBa0I7UUFDL0IsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDeEIsQ0FBQztJQUVEOztPQUVHO0lBQ0ksVUFBVTtRQUNmLElBQUksQ0FBQyxLQUFLLEdBQUcsRUFBRSxDQUFDO0lBQ2xCLENBQUM7SUFFRDs7T0FFRztJQUNJLGFBQWEsQ0FBQyxPQUFzQztRQUN6RCxJQUFJLENBQUMsVUFBVSxHQUFHLE9BQU8sQ0FBQztJQUM1QixDQUFDO0lBRUQ7O09BRUc7SUFDSyxhQUFhLENBQ25CLE9BQXFDLEVBQ3JDLFFBQXFDLEVBQ3JDLFFBQTBCO1FBRTFC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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@push.rocks/smartproxy",
3
- "version": "6.0.1",
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",
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/smartproxy',
6
- version: '6.0.1',
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.sslredirect.js';
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
- }