@push.rocks/smartproxy 3.1.0 → 3.1.3
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/assets/certs/cert.pem +19 -0
- package/assets/certs/key.pem +28 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/smartproxy.classes.networkproxy.d.ts +4 -5
- package/dist_ts/smartproxy.classes.networkproxy.js +172 -232
- package/dist_ts/smartproxy.classes.router.d.ts +0 -1
- package/dist_ts/smartproxy.classes.router.js +11 -2
- package/dist_ts/smartproxy.classes.sslredirect.d.ts +0 -1
- package/dist_ts/smartproxy.helpers.certificates.d.ts +5 -0
- package/dist_ts/smartproxy.helpers.certificates.js +23 -0
- package/dist_ts/smartproxy.portproxy.d.ts +0 -1
- package/package.json +18 -14
- package/readme.md +3 -1
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/smartproxy.classes.networkproxy.ts +202 -259
- package/ts/smartproxy.classes.router.ts +10 -1
- package/ts/smartproxy.helpers.certificates.ts +30 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -7
- package/dist/smartproxy.classes.proxyworker.d.ts +0 -23
- package/dist/smartproxy.classes.proxyworker.js +0 -262
- package/dist/smartproxy.classes.router.d.ts +0 -16
- package/dist/smartproxy.classes.router.js +0 -41
- package/dist/smartproxy.classes.smartproxy.d.ts +0 -17
- package/dist/smartproxy.classes.smartproxy.js +0 -43
- package/dist/smartproxy.plugins.d.ts +0 -17
- package/dist/smartproxy.plugins.js +0 -43
- package/dist/smartproxy.portproxy.d.ts +0 -6
- package/dist/smartproxy.portproxy.js +0 -38
- package/dist_ts/smartproxy.classes.proxyworker.d.ts +0 -33
- package/dist_ts/smartproxy.classes.proxyworker.js +0 -303
- package/dist_ts/smartproxy.classes.smartproxy.d.ts +0 -20
- package/dist_ts/smartproxy.classes.smartproxy.js +0 -38
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import * as plugins from './smartproxy.plugins.js';
|
|
2
2
|
import { ProxyRouter } from './smartproxy.classes.router.js';
|
|
3
|
+
import * as fs from 'fs';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
3
6
|
|
|
4
7
|
export interface INetworkProxyOptions {
|
|
5
8
|
port: number;
|
|
6
9
|
}
|
|
7
10
|
|
|
8
|
-
interface
|
|
11
|
+
interface IWebSocketWithHeartbeat extends plugins.wsDefault {
|
|
9
12
|
lastPong: number;
|
|
10
13
|
}
|
|
11
14
|
|
|
12
15
|
export class NetworkProxy {
|
|
13
|
-
// INSTANCE
|
|
14
16
|
public options: INetworkProxyOptions;
|
|
15
17
|
public proxyConfigs: plugins.tsclass.network.IReverseProxyConfig[] = [];
|
|
16
18
|
public httpsServer: plugins.https.Server;
|
|
@@ -18,6 +20,7 @@ export class NetworkProxy {
|
|
|
18
20
|
public socketMap = new plugins.lik.ObjectMap<plugins.net.Socket>();
|
|
19
21
|
public defaultHeaders: { [key: string]: string } = {};
|
|
20
22
|
public heartbeatInterval: NodeJS.Timeout;
|
|
23
|
+
private defaultCertificates: { key: string; cert: string };
|
|
21
24
|
|
|
22
25
|
public alreadyAddedReverseConfigs: {
|
|
23
26
|
[hostName: string]: plugins.tsclass.network.IReverseProxyConfig;
|
|
@@ -25,236 +28,38 @@ export class NetworkProxy {
|
|
|
25
28
|
|
|
26
29
|
constructor(optionsArg: INetworkProxyOptions) {
|
|
27
30
|
this.options = optionsArg;
|
|
31
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
32
|
+
const certPath = path.join(__dirname, '..', 'assets', 'certs');
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
this.defaultCertificates = {
|
|
36
|
+
key: fs.readFileSync(path.join(certPath, 'key.pem'), 'utf8'),
|
|
37
|
+
cert: fs.readFileSync(path.join(certPath, 'cert.pem'), 'utf8')
|
|
38
|
+
};
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.error('Error loading certificates:', error);
|
|
41
|
+
throw error;
|
|
42
|
+
}
|
|
28
43
|
}
|
|
29
44
|
|
|
30
|
-
/**
|
|
31
|
-
* starts the proxyInstance
|
|
32
|
-
*/
|
|
33
45
|
public async start() {
|
|
46
|
+
// Instead of marking the callback async (which Node won't await),
|
|
47
|
+
// we call our async handler and catch errors.
|
|
34
48
|
this.httpsServer = plugins.https.createServer(
|
|
35
|
-
// ================
|
|
36
|
-
// Spotted this keypair in the code?
|
|
37
|
-
// Don't get exited:
|
|
38
|
-
// It is an invalid default keypair.
|
|
39
|
-
// For proper requests custom domain level keypairs are used that are provided in the reverse config
|
|
40
|
-
// ================
|
|
41
49
|
{
|
|
42
|
-
key:
|
|
43
|
-
|
|
44
|
-
3yEWvy2mRHOZoSSBtIqg6Bre4ZcMu901/cHNIjFnynNGFl9Se61yZbW2F3PfCt7+
|
|
45
|
-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
-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
-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
-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
-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
-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
-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
-
NAnoAPua683MD2ZZd4eovEww/imSZvui3NlisSSh1SomABDFxiEaHpewI98n8P1E
|
|
53
|
-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
-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
-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
-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
-
pZk/lUpUjVEnu+OJPQXQ6f9Y6XoeSAqtvibgmuR+bJaZFMPAqQNTqjix99Aa7JNB
|
|
58
|
-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
-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
-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
-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
-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
-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
-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
-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
-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
-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
-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
-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
-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
-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
-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
-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
-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
-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
-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
-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
-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
-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
-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
-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
-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
-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
-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
-
orfBWUCPXSzPttmXBJbO+Nr+rP+86KvgdI/AT0vYFNdINomEjxsfpaxjOAaW0wfz
|
|
86
|
-
8jCyWKoZ0gJNEeK32GO5UA7dcgBHD3vQWa3lijo8COsznboaJe7M6PQpa/2S2H3+
|
|
87
|
-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
-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
-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
-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
-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
|
|
92
|
-
2TkK01XDDJURK5Lhuvc7WrK2rSJ/fK+0GA==
|
|
93
|
-
-----END PRIVATE KEY-----
|
|
94
|
-
`,
|
|
95
|
-
cert: `-----BEGIN CERTIFICATE-----
|
|
96
|
-
MIIEljCCAn4CCQDY+ZbC9FASVjANBgkqhkiG9w0BAQsFADANMQswCQYDVQQGEwJE
|
|
97
|
-
RTAeFw0xOTA5MjAxNjAxNDRaFw0yMDA5MTkxNjAxNDRaMA0xCzAJBgNVBAYTAkRF
|
|
98
|
-
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA4thf9JEK/epoXt8hFr8t
|
|
99
|
-
pkRzmaEkgbSKoOga3uGXDLvdNf3BzSIxZ8pzRhZfUnutcmW1thdz3wre/pEJR7oN
|
|
100
|
-
QsfixbLL8/oS5QeXKiUGX0Ssfdg4W0TsoLcRva+1AZsf38MfiUPhzh1/UW/rMywW
|
|
101
|
-
asazQwRZdkkXb4nKJ2IFZx22qnAD4/5Sug+sfeKoFBF/rzI2yK7rognt7kW2LHv6
|
|
102
|
-
rswHnZ1Z2P/gbhlZ/EhG9hFVRZwRLDscWKcuWcxkePDt2J1pDNqD6SYa6ZjGC3AE
|
|
103
|
-
TJw5iEA1bLQ9YvjDNpVYcf6ZvcSilIFjSQu5cs9sUbHGeKTrS5HzfeJXh1PfJyL8
|
|
104
|
-
X0Hu7UBSjfSudso3baE9FGiBFBW2cnXZKDZGtV8eq/qxPetOOgS09pVbNP6508WV
|
|
105
|
-
BR+rz98/VDZLZqcbZ2UpOuz4+kAKmbYE9GplxKQZZO7wWEox7Mid/uUdcqEo4QKn
|
|
106
|
-
no6ujOuzQzn5a2oOS0k5Hk3uHapNJWlW9YI3LHtfADpYH+6cOR+/c3JWBzQJ6AD7
|
|
107
|
-
muvNzA9mWXeHqLxMMP4pkmb7otzZYrEkodUqJgAQxcYhGh6XsCPfJ/D9RN734OJc
|
|
108
|
-
gleVXFI8Kz455HxCW19XNfz16k7T6kqhZ/6SOBbkxEuqg7oEthAP109ZZzgx4oDo
|
|
109
|
-
hQsw24TjLkI4SPIc7nr60UUCAwEAATANBgkqhkiG9w0BAQsFAAOCAgEAu0+zrg0C
|
|
110
|
-
mlSv4Yi24OwB7TBvx+WHesl1IilCUdTiiUMo3NumvsU9Dr3Jkd0jGqYI0eyH4gIt
|
|
111
|
-
KrhAveXfEw7tAOEHiYicmAdIFtyzh++ZWb8mgbBeqij1MP/76Jv+cc0lUqpfRo/A
|
|
112
|
-
qytAsPAILuyL1o1jh28JHcq+v+WYn/FEhjUlH6emhGKGlsAjhUPjzK8MEshNolhj
|
|
113
|
-
t2UXw9WB5B2xWvrqlNMy0F3NAZBkZ/+k21HZo6FmVi+q6OEGcOo7wJt6wrH/lko9
|
|
114
|
-
LxX96GC1JoN1Pfr2FoTKy1WHzrSfyGmDIUCrbaYQ58UuMOR+5eIPPdkf/030u5eX
|
|
115
|
-
xXhF2fBujD57E2zQGh/l2MrOjamcSo0+wYhOqlX3WNdaKNAzPqloBnF6w7eqLYde
|
|
116
|
-
h9He39ySmxjENwv3miOjEP1sBeMBSRfL/ckEonfK5uJgYA5nVMQ3ojUeDMZzLfFE
|
|
117
|
-
Ue2WHt+uPyYk7mMZfOrK2uHzI2/Coqj7lbfRodFwj+fCArYBck2NZannDPKA6X8V
|
|
118
|
-
TzJTbTCteOUUJTrcfZ0gGhGkF4nYLmX5OI+TPqrDJf0fZ+mzAEHzDDVXcBYpYRDr
|
|
119
|
-
r8d9QwrK+WaqVi2ofbMfMByVF72jgeJNa4nxwT9bVbu/Q1T2Lt+YPb4pQ7yCoUgS
|
|
120
|
-
JNj2Dr5H0XoLFFnvuvzcRbhlJ9J67JzR+7g=
|
|
121
|
-
-----END CERTIFICATE-----
|
|
122
|
-
`,
|
|
50
|
+
key: this.defaultCertificates.key,
|
|
51
|
+
cert: this.defaultCertificates.cert
|
|
123
52
|
},
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
*/
|
|
129
|
-
const endOriginReqRes = (
|
|
130
|
-
statusArg: number = 404,
|
|
131
|
-
messageArg: string = 'This route is not available on this server.',
|
|
132
|
-
headers: plugins.http.OutgoingHttpHeaders = {}
|
|
133
|
-
) => {
|
|
134
|
-
originResponse.writeHead(statusArg, messageArg);
|
|
135
|
-
originResponse.end(messageArg);
|
|
136
|
-
if (originRequest.socket !== originResponse.socket) {
|
|
137
|
-
console.log('hey, something is strange.');
|
|
138
|
-
}
|
|
139
|
-
originResponse.destroy();
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
console.log(
|
|
143
|
-
`got request: ${originRequest.headers.host}${plugins.url.parse(originRequest.url).path}`
|
|
144
|
-
);
|
|
145
|
-
const destinationConfig = this.router.routeReq(originRequest);
|
|
146
|
-
|
|
147
|
-
if (!destinationConfig) {
|
|
148
|
-
console.log(
|
|
149
|
-
`${originRequest.headers.host} can't be routed properly. Terminating request.`
|
|
150
|
-
);
|
|
151
|
-
endOriginReqRes();
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// authentication
|
|
156
|
-
if (destinationConfig.authentication) {
|
|
157
|
-
const authInfo = destinationConfig.authentication;
|
|
158
|
-
switch (authInfo.type) {
|
|
159
|
-
case 'Basic':
|
|
160
|
-
const authHeader = originRequest.headers.authorization;
|
|
161
|
-
if (authHeader) {
|
|
162
|
-
if (!authHeader.includes('Basic ')) {
|
|
163
|
-
return endOriginReqRes(401, 'Authentication required', {
|
|
164
|
-
'WWW-Authenticate': 'Basic realm="Access to the staging site", charset="UTF-8"',
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
const authStringBase64 = originRequest.headers.authorization.replace('Basic ', '');
|
|
168
|
-
const authString: string = plugins.smartstring.base64.decode(authStringBase64);
|
|
169
|
-
const userPassArray = authString.split(':');
|
|
170
|
-
const user = userPassArray[0];
|
|
171
|
-
const pass = userPassArray[1];
|
|
172
|
-
if (user === authInfo.user && pass === authInfo.pass) {
|
|
173
|
-
console.log('request successfully authenticated');
|
|
174
|
-
} else {
|
|
175
|
-
return endOriginReqRes(403, 'Forbidden: Wrong credentials');
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
break;
|
|
179
|
-
default:
|
|
180
|
-
return endOriginReqRes(
|
|
181
|
-
403,
|
|
182
|
-
'Forbidden: unsupported authentication method configured. Please report to the admin.'
|
|
183
|
-
);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
let destinationUrl: string;
|
|
188
|
-
if (destinationConfig) {
|
|
189
|
-
destinationUrl = `http://${destinationConfig.destinationIp}:${destinationConfig.destinationPort}${originRequest.url}`;
|
|
190
|
-
} else {
|
|
191
|
-
return endOriginReqRes();
|
|
192
|
-
}
|
|
193
|
-
console.log(destinationUrl);
|
|
194
|
-
try {
|
|
195
|
-
const proxyResponse = await plugins.smartrequest.request(
|
|
196
|
-
destinationUrl,
|
|
197
|
-
{
|
|
198
|
-
method: originRequest.method,
|
|
199
|
-
headers: {
|
|
200
|
-
...originRequest.headers,
|
|
201
|
-
'X-Forwarded-Host': originRequest.headers.host,
|
|
202
|
-
'X-Forwarded-Proto': 'https',
|
|
203
|
-
},
|
|
204
|
-
keepAlive: true,
|
|
205
|
-
},
|
|
206
|
-
true, // lets make this streaming (keepAlive)
|
|
207
|
-
(proxyRequest) => {
|
|
208
|
-
originRequest.on('data', (data) => {
|
|
209
|
-
proxyRequest.write(data);
|
|
210
|
-
});
|
|
211
|
-
originRequest.on('end', () => {
|
|
212
|
-
proxyRequest.end();
|
|
213
|
-
});
|
|
214
|
-
originRequest.on('error', () => {
|
|
215
|
-
proxyRequest.end();
|
|
216
|
-
});
|
|
217
|
-
originRequest.on('close', () => {
|
|
218
|
-
proxyRequest.end();
|
|
219
|
-
});
|
|
220
|
-
originRequest.on('timeout', () => {
|
|
221
|
-
proxyRequest.end();
|
|
222
|
-
originRequest.destroy();
|
|
223
|
-
});
|
|
224
|
-
proxyRequest.on('error', () => {
|
|
225
|
-
endOriginReqRes();
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
);
|
|
229
|
-
originResponse.statusCode = proxyResponse.statusCode;
|
|
230
|
-
console.log(proxyResponse.statusCode);
|
|
231
|
-
for (const defaultHeader of Object.keys(this.defaultHeaders)) {
|
|
232
|
-
originResponse.setHeader(defaultHeader, this.defaultHeaders[defaultHeader]);
|
|
233
|
-
}
|
|
234
|
-
for (const header of Object.keys(proxyResponse.headers)) {
|
|
235
|
-
originResponse.setHeader(header, proxyResponse.headers[header]);
|
|
236
|
-
}
|
|
237
|
-
proxyResponse.on('data', (data) => {
|
|
238
|
-
originResponse.write(data);
|
|
239
|
-
});
|
|
240
|
-
proxyResponse.on('end', () => {
|
|
241
|
-
originResponse.end();
|
|
242
|
-
});
|
|
243
|
-
proxyResponse.on('error', () => {
|
|
244
|
-
originResponse.destroy();
|
|
245
|
-
});
|
|
246
|
-
proxyResponse.on('close', () => {
|
|
247
|
-
originResponse.end();
|
|
248
|
-
});
|
|
249
|
-
proxyResponse.on('timeout', () => {
|
|
53
|
+
(originRequest, originResponse) => {
|
|
54
|
+
this.handleRequest(originRequest, originResponse).catch((error) => {
|
|
55
|
+
console.error('Unhandled error in request handler:', error);
|
|
56
|
+
try {
|
|
250
57
|
originResponse.end();
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
}
|
|
257
|
-
}
|
|
58
|
+
} catch (err) {
|
|
59
|
+
// ignore errors during cleanup
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
},
|
|
258
63
|
);
|
|
259
64
|
|
|
260
65
|
// Enable websockets
|
|
@@ -263,7 +68,7 @@ JNj2Dr5H0XoLFFnvuvzcRbhlJ9J67JzR+7g=
|
|
|
263
68
|
// Set up the heartbeat interval
|
|
264
69
|
this.heartbeatInterval = setInterval(() => {
|
|
265
70
|
wsServer.clients.forEach((ws: plugins.wsDefault) => {
|
|
266
|
-
const wsIncoming = ws as
|
|
71
|
+
const wsIncoming = ws as IWebSocketWithHeartbeat;
|
|
267
72
|
if (!wsIncoming.lastPong) {
|
|
268
73
|
wsIncoming.lastPong = Date.now();
|
|
269
74
|
}
|
|
@@ -278,9 +83,9 @@ JNj2Dr5H0XoLFFnvuvzcRbhlJ9J67JzR+7g=
|
|
|
278
83
|
|
|
279
84
|
wsServer.on(
|
|
280
85
|
'connection',
|
|
281
|
-
|
|
86
|
+
(wsIncoming: IWebSocketWithHeartbeat, reqArg: plugins.http.IncomingMessage) => {
|
|
282
87
|
console.log(
|
|
283
|
-
`wss proxy: got connection for wsc for https://${reqArg.headers.host}${reqArg.url}
|
|
88
|
+
`wss proxy: got connection for wsc for https://${reqArg.headers.host}${reqArg.url}`,
|
|
284
89
|
);
|
|
285
90
|
|
|
286
91
|
wsIncoming.lastPong = Date.now();
|
|
@@ -289,21 +94,24 @@ JNj2Dr5H0XoLFFnvuvzcRbhlJ9J67JzR+7g=
|
|
|
289
94
|
});
|
|
290
95
|
|
|
291
96
|
let wsOutgoing: plugins.wsDefault;
|
|
292
|
-
|
|
293
97
|
const outGoingDeferred = plugins.smartpromise.defer();
|
|
294
98
|
|
|
99
|
+
// --- Improvement 2: Only call routeReq once ---
|
|
100
|
+
const wsDestinationConfig = this.router.routeReq(reqArg);
|
|
101
|
+
if (!wsDestinationConfig) {
|
|
102
|
+
wsIncoming.terminate();
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
295
105
|
try {
|
|
296
106
|
wsOutgoing = new plugins.wsDefault(
|
|
297
|
-
`ws://${
|
|
298
|
-
this.router.routeReq(reqArg).destinationPort
|
|
299
|
-
}${reqArg.url}`
|
|
107
|
+
`ws://${wsDestinationConfig.destinationIp}:${wsDestinationConfig.destinationPort}${reqArg.url}`,
|
|
300
108
|
);
|
|
301
109
|
console.log('wss proxy: initiated outgoing proxy');
|
|
302
110
|
wsOutgoing.on('open', async () => {
|
|
303
111
|
outGoingDeferred.resolve();
|
|
304
112
|
});
|
|
305
113
|
} catch (err) {
|
|
306
|
-
console.
|
|
114
|
+
console.error('Error initiating outgoing WebSocket:', err);
|
|
307
115
|
wsIncoming.terminate();
|
|
308
116
|
return;
|
|
309
117
|
}
|
|
@@ -328,21 +136,21 @@ JNj2Dr5H0XoLFFnvuvzcRbhlJ9J67JzR+7g=
|
|
|
328
136
|
const terminateWsOutgoing = () => {
|
|
329
137
|
if (wsOutgoing) {
|
|
330
138
|
wsOutgoing.terminate();
|
|
331
|
-
console.log('
|
|
139
|
+
console.log('Terminated outgoing ws.');
|
|
332
140
|
}
|
|
333
141
|
};
|
|
334
|
-
wsIncoming.on('error',
|
|
335
|
-
wsIncoming.on('close',
|
|
142
|
+
wsIncoming.on('error', terminateWsOutgoing);
|
|
143
|
+
wsIncoming.on('close', terminateWsOutgoing);
|
|
336
144
|
|
|
337
145
|
const terminateWsIncoming = () => {
|
|
338
146
|
if (wsIncoming) {
|
|
339
147
|
wsIncoming.terminate();
|
|
340
|
-
console.log('
|
|
148
|
+
console.log('Terminated incoming ws.');
|
|
341
149
|
}
|
|
342
150
|
};
|
|
343
|
-
wsOutgoing.on('error',
|
|
344
|
-
wsOutgoing.on('close',
|
|
345
|
-
}
|
|
151
|
+
wsOutgoing.on('error', terminateWsIncoming);
|
|
152
|
+
wsOutgoing.on('close', terminateWsIncoming);
|
|
153
|
+
},
|
|
346
154
|
);
|
|
347
155
|
|
|
348
156
|
this.httpsServer.keepAliveTimeout = 600 * 1000;
|
|
@@ -350,35 +158,170 @@ JNj2Dr5H0XoLFFnvuvzcRbhlJ9J67JzR+7g=
|
|
|
350
158
|
|
|
351
159
|
this.httpsServer.on('connection', (connection: plugins.net.Socket) => {
|
|
352
160
|
this.socketMap.add(connection);
|
|
353
|
-
console.log(`
|
|
161
|
+
console.log(`Added connection. Now ${this.socketMap.getArray().length} sockets connected.`);
|
|
354
162
|
const cleanupConnection = () => {
|
|
355
163
|
if (this.socketMap.checkForObject(connection)) {
|
|
356
164
|
this.socketMap.remove(connection);
|
|
357
|
-
console.log(`
|
|
165
|
+
console.log(`Removed connection. ${this.socketMap.getArray().length} sockets remaining.`);
|
|
358
166
|
connection.destroy();
|
|
359
167
|
}
|
|
360
168
|
};
|
|
361
|
-
connection.on('close',
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
connection.on('
|
|
365
|
-
cleanupConnection();
|
|
366
|
-
});
|
|
367
|
-
connection.on('end', () => {
|
|
368
|
-
cleanupConnection();
|
|
369
|
-
});
|
|
370
|
-
connection.on('timeout', () => {
|
|
371
|
-
cleanupConnection();
|
|
372
|
-
});
|
|
169
|
+
connection.on('close', cleanupConnection);
|
|
170
|
+
connection.on('error', cleanupConnection);
|
|
171
|
+
connection.on('end', cleanupConnection);
|
|
172
|
+
connection.on('timeout', cleanupConnection);
|
|
373
173
|
});
|
|
374
174
|
|
|
375
175
|
this.httpsServer.listen(this.options.port);
|
|
376
176
|
console.log(
|
|
377
|
-
`NetworkProxy -> OK: now listening for new connections on port ${this.options.port}
|
|
177
|
+
`NetworkProxy -> OK: now listening for new connections on port ${this.options.port}`,
|
|
378
178
|
);
|
|
379
179
|
}
|
|
380
180
|
|
|
381
|
-
|
|
181
|
+
/**
|
|
182
|
+
* Internal async handler for processing HTTP/HTTPS requests.
|
|
183
|
+
*/
|
|
184
|
+
private async handleRequest(
|
|
185
|
+
originRequest: plugins.http.IncomingMessage,
|
|
186
|
+
originResponse: plugins.http.ServerResponse,
|
|
187
|
+
): Promise<void> {
|
|
188
|
+
const endOriginReqRes = (
|
|
189
|
+
statusArg: number = 404,
|
|
190
|
+
messageArg: string = 'This route is not available on this server.',
|
|
191
|
+
headers: plugins.http.OutgoingHttpHeaders = {},
|
|
192
|
+
) => {
|
|
193
|
+
originResponse.writeHead(statusArg, messageArg);
|
|
194
|
+
originResponse.end(messageArg);
|
|
195
|
+
if (originRequest.socket !== originResponse.socket) {
|
|
196
|
+
console.log('hey, something is strange.');
|
|
197
|
+
}
|
|
198
|
+
originResponse.destroy();
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
console.log(
|
|
202
|
+
`got request: ${originRequest.headers.host}${plugins.url.parse(originRequest.url).path}`,
|
|
203
|
+
);
|
|
204
|
+
const destinationConfig = this.router.routeReq(originRequest);
|
|
205
|
+
|
|
206
|
+
if (!destinationConfig) {
|
|
207
|
+
console.log(
|
|
208
|
+
`${originRequest.headers.host} can't be routed properly. Terminating request.`,
|
|
209
|
+
);
|
|
210
|
+
endOriginReqRes();
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// authentication
|
|
215
|
+
if (destinationConfig.authentication) {
|
|
216
|
+
const authInfo = destinationConfig.authentication;
|
|
217
|
+
switch (authInfo.type) {
|
|
218
|
+
case 'Basic': {
|
|
219
|
+
const authHeader = originRequest.headers.authorization;
|
|
220
|
+
if (!authHeader) {
|
|
221
|
+
return endOriginReqRes(401, 'Authentication required', {
|
|
222
|
+
'WWW-Authenticate': 'Basic realm="Access to the staging site", charset="UTF-8"',
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
if (!authHeader.includes('Basic ')) {
|
|
226
|
+
return endOriginReqRes(401, 'Authentication required', {
|
|
227
|
+
'WWW-Authenticate': 'Basic realm="Access to the staging site", charset="UTF-8"',
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
const authStringBase64 = authHeader.replace('Basic ', '');
|
|
231
|
+
const authString: string = plugins.smartstring.base64.decode(authStringBase64);
|
|
232
|
+
const userPassArray = authString.split(':');
|
|
233
|
+
const user = userPassArray[0];
|
|
234
|
+
const pass = userPassArray[1];
|
|
235
|
+
if (user === authInfo.user && pass === authInfo.pass) {
|
|
236
|
+
console.log('Request successfully authenticated');
|
|
237
|
+
} else {
|
|
238
|
+
return endOriginReqRes(403, 'Forbidden: Wrong credentials');
|
|
239
|
+
}
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
242
|
+
default:
|
|
243
|
+
return endOriginReqRes(
|
|
244
|
+
403,
|
|
245
|
+
'Forbidden: unsupported authentication method configured. Please report to the admin.',
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
let destinationUrl: string;
|
|
251
|
+
if (destinationConfig) {
|
|
252
|
+
destinationUrl = `http://${destinationConfig.destinationIp}:${destinationConfig.destinationPort}${originRequest.url}`;
|
|
253
|
+
} else {
|
|
254
|
+
return endOriginReqRes();
|
|
255
|
+
}
|
|
256
|
+
console.log(destinationUrl);
|
|
257
|
+
try {
|
|
258
|
+
const proxyResponse = await plugins.smartrequest.request(
|
|
259
|
+
destinationUrl,
|
|
260
|
+
{
|
|
261
|
+
method: originRequest.method,
|
|
262
|
+
headers: {
|
|
263
|
+
...originRequest.headers,
|
|
264
|
+
'X-Forwarded-Host': originRequest.headers.host,
|
|
265
|
+
'X-Forwarded-Proto': 'https',
|
|
266
|
+
},
|
|
267
|
+
keepAlive: true,
|
|
268
|
+
},
|
|
269
|
+
true, // streaming (keepAlive)
|
|
270
|
+
(proxyRequest) => {
|
|
271
|
+
originRequest.on('data', (data) => {
|
|
272
|
+
proxyRequest.write(data);
|
|
273
|
+
});
|
|
274
|
+
originRequest.on('end', () => {
|
|
275
|
+
proxyRequest.end();
|
|
276
|
+
});
|
|
277
|
+
originRequest.on('error', () => {
|
|
278
|
+
proxyRequest.end();
|
|
279
|
+
});
|
|
280
|
+
originRequest.on('close', () => {
|
|
281
|
+
proxyRequest.end();
|
|
282
|
+
});
|
|
283
|
+
originRequest.on('timeout', () => {
|
|
284
|
+
proxyRequest.end();
|
|
285
|
+
originRequest.destroy();
|
|
286
|
+
});
|
|
287
|
+
proxyRequest.on('error', () => {
|
|
288
|
+
endOriginReqRes();
|
|
289
|
+
});
|
|
290
|
+
},
|
|
291
|
+
);
|
|
292
|
+
originResponse.statusCode = proxyResponse.statusCode;
|
|
293
|
+
console.log(proxyResponse.statusCode);
|
|
294
|
+
for (const defaultHeader of Object.keys(this.defaultHeaders)) {
|
|
295
|
+
originResponse.setHeader(defaultHeader, this.defaultHeaders[defaultHeader]);
|
|
296
|
+
}
|
|
297
|
+
for (const header of Object.keys(proxyResponse.headers)) {
|
|
298
|
+
originResponse.setHeader(header, proxyResponse.headers[header]);
|
|
299
|
+
}
|
|
300
|
+
proxyResponse.on('data', (data) => {
|
|
301
|
+
originResponse.write(data);
|
|
302
|
+
});
|
|
303
|
+
proxyResponse.on('end', () => {
|
|
304
|
+
originResponse.end();
|
|
305
|
+
});
|
|
306
|
+
proxyResponse.on('error', () => {
|
|
307
|
+
originResponse.destroy();
|
|
308
|
+
});
|
|
309
|
+
proxyResponse.on('close', () => {
|
|
310
|
+
originResponse.end();
|
|
311
|
+
});
|
|
312
|
+
proxyResponse.on('timeout', () => {
|
|
313
|
+
originResponse.end();
|
|
314
|
+
originResponse.destroy();
|
|
315
|
+
});
|
|
316
|
+
} catch (error) {
|
|
317
|
+
console.error('Error while processing request:', error);
|
|
318
|
+
endOriginReqRes(502, 'Bad Gateway: Error processing the request');
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
public async updateProxyConfigs(
|
|
323
|
+
proxyConfigsArg: plugins.tsclass.network.IReverseProxyConfig[],
|
|
324
|
+
) {
|
|
382
325
|
console.log(`got new proxy configs`);
|
|
383
326
|
this.proxyConfigs = proxyConfigsArg;
|
|
384
327
|
this.router.setNewProxyConfigs(proxyConfigsArg);
|
|
@@ -416,11 +359,11 @@ JNj2Dr5H0XoLFFnvuvzcRbhlJ9J67JzR+7g=
|
|
|
416
359
|
this.httpsServer.close(() => {
|
|
417
360
|
done.resolve();
|
|
418
361
|
});
|
|
419
|
-
|
|
362
|
+
for (const socket of this.socketMap.getArray()) {
|
|
420
363
|
socket.destroy();
|
|
421
|
-
}
|
|
364
|
+
}
|
|
422
365
|
await done.promise;
|
|
423
366
|
clearInterval(this.heartbeatInterval);
|
|
424
367
|
console.log('NetworkProxy -> OK: Server has been stopped and all connections closed.');
|
|
425
368
|
}
|
|
426
|
-
}
|
|
369
|
+
}
|
|
@@ -16,9 +16,18 @@ export class ProxyRouter {
|
|
|
16
16
|
*/
|
|
17
17
|
public routeReq(req: plugins.http.IncomingMessage): plugins.tsclass.network.IReverseProxyConfig {
|
|
18
18
|
const originalHost = req.headers.host;
|
|
19
|
+
if (!originalHost) {
|
|
20
|
+
console.error('No host header found in request');
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
// Strip port from host if present
|
|
24
|
+
const hostWithoutPort = originalHost.split(':')[0];
|
|
19
25
|
const correspodingReverseProxyConfig = this.reverseProxyConfigs.find((reverseConfig) => {
|
|
20
|
-
return reverseConfig.hostName ===
|
|
26
|
+
return reverseConfig.hostName === hostWithoutPort;
|
|
21
27
|
});
|
|
28
|
+
if (!correspodingReverseProxyConfig) {
|
|
29
|
+
console.error(`No config found for host: ${hostWithoutPort}`);
|
|
30
|
+
}
|
|
22
31
|
return correspodingReverseProxyConfig;
|
|
23
32
|
}
|
|
24
33
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
|
|
5
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
|
|
7
|
+
export interface ICertificates {
|
|
8
|
+
privateKey: string;
|
|
9
|
+
publicKey: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function loadDefaultCertificates(): ICertificates {
|
|
13
|
+
try {
|
|
14
|
+
const certPath = path.join(__dirname, '..', 'assets', 'certs');
|
|
15
|
+
const privateKey = fs.readFileSync(path.join(certPath, 'key.pem'), 'utf8');
|
|
16
|
+
const publicKey = fs.readFileSync(path.join(certPath, 'cert.pem'), 'utf8');
|
|
17
|
+
|
|
18
|
+
if (!privateKey || !publicKey) {
|
|
19
|
+
throw new Error('Failed to load default certificates');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
privateKey,
|
|
24
|
+
publicKey
|
|
25
|
+
};
|
|
26
|
+
} catch (error) {
|
|
27
|
+
console.error('Error loading default certificates:', error);
|
|
28
|
+
throw error;
|
|
29
|
+
}
|
|
30
|
+
}
|
package/dist/index.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './smartproxy.classes.smartproxy';
|
package/dist/index.js
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
function __export(m) {
|
|
3
|
-
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
|
|
4
|
-
}
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
__export(require("./smartproxy.classes.smartproxy"));
|
|
7
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQUFBLHFEQUFnRCJ9
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/// <reference types="node" />
|
|
2
|
-
import * as plugins from './smartproxy.plugins';
|
|
3
|
-
import { SmartproxyRouter } from './smartproxy.classes.router';
|
|
4
|
-
export declare class ProxyWorker {
|
|
5
|
-
proxyConfigs: plugins.tsclass.network.IReverseProxyConfig[];
|
|
6
|
-
httpsServer: plugins.https.Server;
|
|
7
|
-
port: number;
|
|
8
|
-
router: SmartproxyRouter;
|
|
9
|
-
socketMap: plugins.lik.Objectmap<plugins.net.Socket>;
|
|
10
|
-
/**
|
|
11
|
-
* starts the proxyInstance
|
|
12
|
-
*/
|
|
13
|
-
start(): Promise<void>;
|
|
14
|
-
updateProxyConfigs(proxyConfigsArg: plugins.tsclass.network.IReverseProxyConfig[]): Promise<void>;
|
|
15
|
-
stop(): Promise<void>;
|
|
16
|
-
}
|
|
17
|
-
declare const proxyWorkerCalls: {
|
|
18
|
-
stop: () => Promise<void>;
|
|
19
|
-
start: () => Promise<void>;
|
|
20
|
-
updateReverseConfigs: (configArray: plugins.tsclass.network.IReverseProxyConfig[]) => Promise<void>;
|
|
21
|
-
};
|
|
22
|
-
export declare type TProxyWorkerCalls = typeof proxyWorkerCalls;
|
|
23
|
-
export {};
|