@push.rocks/smartproxy 3.0.61 → 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 +2 -2
- package/dist_ts/smartproxy.classes.networkproxy.d.ts +5 -4
- package/dist_ts/smartproxy.classes.networkproxy.js +193 -233
- 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 +225 -258
- 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,18 +1,26 @@
|
|
|
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
|
|
|
11
|
+
interface IWebSocketWithHeartbeat extends plugins.wsDefault {
|
|
12
|
+
lastPong: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
8
15
|
export class NetworkProxy {
|
|
9
|
-
// INSTANCE
|
|
10
16
|
public options: INetworkProxyOptions;
|
|
11
17
|
public proxyConfigs: plugins.tsclass.network.IReverseProxyConfig[] = [];
|
|
12
18
|
public httpsServer: plugins.https.Server;
|
|
13
19
|
public router = new ProxyRouter();
|
|
14
20
|
public socketMap = new plugins.lik.ObjectMap<plugins.net.Socket>();
|
|
15
21
|
public defaultHeaders: { [key: string]: string } = {};
|
|
22
|
+
public heartbeatInterval: NodeJS.Timeout;
|
|
23
|
+
private defaultCertificates: { key: string; cert: string };
|
|
16
24
|
|
|
17
25
|
public alreadyAddedReverseConfigs: {
|
|
18
26
|
[hostName: string]: plugins.tsclass.network.IReverseProxyConfig;
|
|
@@ -20,263 +28,90 @@ export class NetworkProxy {
|
|
|
20
28
|
|
|
21
29
|
constructor(optionsArg: INetworkProxyOptions) {
|
|
22
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
|
+
}
|
|
23
43
|
}
|
|
24
44
|
|
|
25
|
-
/**
|
|
26
|
-
* starts the proxyInstance
|
|
27
|
-
*/
|
|
28
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.
|
|
29
48
|
this.httpsServer = plugins.https.createServer(
|
|
30
|
-
// ================
|
|
31
|
-
// Spotted this keypair in the code?
|
|
32
|
-
// Don't get exited:
|
|
33
|
-
// It is an invalid default keypair.
|
|
34
|
-
// For proper requests custom domain level keypairs are used that are provided in the reverse config
|
|
35
|
-
// ================
|
|
36
49
|
{
|
|
37
|
-
key:
|
|
38
|
-
|
|
39
|
-
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
-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
-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
-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
-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
-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
-
/rnTxZUFH6vP3z9UNktmpxtnZSk67Pj6QAqZtgT0amXEpBlk7vBYSjHsyJ3+5R1y
|
|
46
|
-
oSjhAqeejq6M67NDOflrag5LSTkeTe4dqk0laVb1gjcse18AOlgf7pw5H79zclYH
|
|
47
|
-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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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
-
c8bV4ymk35fVJu/+pTZpPnMkYrFReso/uNn07y1iga/9mwkUBNrT+fWE7RzjT7H8
|
|
65
|
-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
-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
-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
-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
-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
-
pgVBDeJGKpPpP3scl7go5Y4KC6H+IeYaeCEk3hWW4robpHBzupkgpRLzmBopjRlN
|
|
71
|
-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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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
-
pjuQFQ5Ods+BwJ/Jbirb7l4GMAxfIbEPAkPTHpvswO0xcncSYxl0sSP/WIA6sbcM
|
|
84
|
-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
-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
-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
|
|
87
|
-
2TkK01XDDJURK5Lhuvc7WrK2rSJ/fK+0GA==
|
|
88
|
-
-----END PRIVATE KEY-----
|
|
89
|
-
`,
|
|
90
|
-
cert: `-----BEGIN CERTIFICATE-----
|
|
91
|
-
MIIEljCCAn4CCQDY+ZbC9FASVjANBgkqhkiG9w0BAQsFADANMQswCQYDVQQGEwJE
|
|
92
|
-
RTAeFw0xOTA5MjAxNjAxNDRaFw0yMDA5MTkxNjAxNDRaMA0xCzAJBgNVBAYTAkRF
|
|
93
|
-
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA4thf9JEK/epoXt8hFr8t
|
|
94
|
-
pkRzmaEkgbSKoOga3uGXDLvdNf3BzSIxZ8pzRhZfUnutcmW1thdz3wre/pEJR7oN
|
|
95
|
-
QsfixbLL8/oS5QeXKiUGX0Ssfdg4W0TsoLcRva+1AZsf38MfiUPhzh1/UW/rMywW
|
|
96
|
-
asazQwRZdkkXb4nKJ2IFZx22qnAD4/5Sug+sfeKoFBF/rzI2yK7rognt7kW2LHv6
|
|
97
|
-
rswHnZ1Z2P/gbhlZ/EhG9hFVRZwRLDscWKcuWcxkePDt2J1pDNqD6SYa6ZjGC3AE
|
|
98
|
-
TJw5iEA1bLQ9YvjDNpVYcf6ZvcSilIFjSQu5cs9sUbHGeKTrS5HzfeJXh1PfJyL8
|
|
99
|
-
X0Hu7UBSjfSudso3baE9FGiBFBW2cnXZKDZGtV8eq/qxPetOOgS09pVbNP6508WV
|
|
100
|
-
BR+rz98/VDZLZqcbZ2UpOuz4+kAKmbYE9GplxKQZZO7wWEox7Mid/uUdcqEo4QKn
|
|
101
|
-
no6ujOuzQzn5a2oOS0k5Hk3uHapNJWlW9YI3LHtfADpYH+6cOR+/c3JWBzQJ6AD7
|
|
102
|
-
muvNzA9mWXeHqLxMMP4pkmb7otzZYrEkodUqJgAQxcYhGh6XsCPfJ/D9RN734OJc
|
|
103
|
-
gleVXFI8Kz455HxCW19XNfz16k7T6kqhZ/6SOBbkxEuqg7oEthAP109ZZzgx4oDo
|
|
104
|
-
hQsw24TjLkI4SPIc7nr60UUCAwEAATANBgkqhkiG9w0BAQsFAAOCAgEAu0+zrg0C
|
|
105
|
-
mlSv4Yi24OwB7TBvx+WHesl1IilCUdTiiUMo3NumvsU9Dr3Jkd0jGqYI0eyH4gIt
|
|
106
|
-
KrhAveXfEw7tAOEHiYicmAdIFtyzh++ZWb8mgbBeqij1MP/76Jv+cc0lUqpfRo/A
|
|
107
|
-
qytAsPAILuyL1o1jh28JHcq+v+WYn/FEhjUlH6emhGKGlsAjhUPjzK8MEshNolhj
|
|
108
|
-
t2UXw9WB5B2xWvrqlNMy0F3NAZBkZ/+k21HZo6FmVi+q6OEGcOo7wJt6wrH/lko9
|
|
109
|
-
LxX96GC1JoN1Pfr2FoTKy1WHzrSfyGmDIUCrbaYQ58UuMOR+5eIPPdkf/030u5eX
|
|
110
|
-
xXhF2fBujD57E2zQGh/l2MrOjamcSo0+wYhOqlX3WNdaKNAzPqloBnF6w7eqLYde
|
|
111
|
-
h9He39ySmxjENwv3miOjEP1sBeMBSRfL/ckEonfK5uJgYA5nVMQ3ojUeDMZzLfFE
|
|
112
|
-
Ue2WHt+uPyYk7mMZfOrK2uHzI2/Coqj7lbfRodFwj+fCArYBck2NZannDPKA6X8V
|
|
113
|
-
TzJTbTCteOUUJTrcfZ0gGhGkF4nYLmX5OI+TPqrDJf0fZ+mzAEHzDDVXcBYpYRDr
|
|
114
|
-
r8d9QwrK+WaqVi2ofbMfMByVF72jgeJNa4nxwT9bVbu/Q1T2Lt+YPb4pQ7yCoUgS
|
|
115
|
-
JNj2Dr5H0XoLFFnvuvzcRbhlJ9J67JzR+7g=
|
|
116
|
-
-----END CERTIFICATE-----
|
|
117
|
-
`,
|
|
50
|
+
key: this.defaultCertificates.key,
|
|
51
|
+
cert: this.defaultCertificates.cert
|
|
118
52
|
},
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
messageArg: string = 'This route is not available on this server.',
|
|
127
|
-
headers: plugins.http.OutgoingHttpHeaders = {}
|
|
128
|
-
) => {
|
|
129
|
-
originResponse.writeHead(statusArg, messageArg);
|
|
130
|
-
originResponse.end(messageArg);
|
|
131
|
-
if (originRequest.socket !== originResponse.socket) {
|
|
132
|
-
console.log('hey, something is strange.');
|
|
53
|
+
(originRequest, originResponse) => {
|
|
54
|
+
this.handleRequest(originRequest, originResponse).catch((error) => {
|
|
55
|
+
console.error('Unhandled error in request handler:', error);
|
|
56
|
+
try {
|
|
57
|
+
originResponse.end();
|
|
58
|
+
} catch (err) {
|
|
59
|
+
// ignore errors during cleanup
|
|
133
60
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
console.log(
|
|
138
|
-
`got request: ${originRequest.headers.host}${plugins.url.parse(originRequest.url).path}`
|
|
139
|
-
);
|
|
140
|
-
const destinationConfig = this.router.routeReq(originRequest);
|
|
61
|
+
});
|
|
62
|
+
},
|
|
63
|
+
);
|
|
141
64
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
`${originRequest.headers.host} can't be routed properly. Terminating request.`
|
|
145
|
-
);
|
|
146
|
-
endOriginReqRes();
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
65
|
+
// Enable websockets
|
|
66
|
+
const wsServer = new plugins.ws.WebSocketServer({ server: this.httpsServer });
|
|
149
67
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
if (authHeader) {
|
|
157
|
-
if (!authHeader.includes('Basic ')) {
|
|
158
|
-
return endOriginReqRes(401, 'Authentication required', {
|
|
159
|
-
'WWW-Authenticate': 'Basic realm="Access to the staging site", charset="UTF-8"',
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
const authStringBase64 = originRequest.headers.authorization.replace('Basic ', '');
|
|
163
|
-
const authString: string = plugins.smartstring.base64.decode(authStringBase64);
|
|
164
|
-
const userPassArray = authString.split(':');
|
|
165
|
-
const user = userPassArray[0];
|
|
166
|
-
const pass = userPassArray[1];
|
|
167
|
-
if (user === authInfo.user && pass === authInfo.pass) {
|
|
168
|
-
console.log('request successfully authenticated');
|
|
169
|
-
} else {
|
|
170
|
-
return endOriginReqRes(403, 'Forbidden: Wrong credentials');
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
break;
|
|
174
|
-
default:
|
|
175
|
-
return endOriginReqRes(
|
|
176
|
-
403,
|
|
177
|
-
'Forbidden: unsupported authentication method configured. Please report to the admin.'
|
|
178
|
-
);
|
|
179
|
-
}
|
|
68
|
+
// Set up the heartbeat interval
|
|
69
|
+
this.heartbeatInterval = setInterval(() => {
|
|
70
|
+
wsServer.clients.forEach((ws: plugins.wsDefault) => {
|
|
71
|
+
const wsIncoming = ws as IWebSocketWithHeartbeat;
|
|
72
|
+
if (!wsIncoming.lastPong) {
|
|
73
|
+
wsIncoming.lastPong = Date.now();
|
|
180
74
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
destinationUrl = `http://${destinationConfig.destinationIp}:${destinationConfig.destinationPort}${originRequest.url}`;
|
|
75
|
+
if (Date.now() - wsIncoming.lastPong > 5 * 60 * 1000) {
|
|
76
|
+
console.log('Terminating websocket due to missing pong for 5 minutes.');
|
|
77
|
+
wsIncoming.terminate();
|
|
185
78
|
} else {
|
|
186
|
-
|
|
79
|
+
wsIncoming.ping();
|
|
187
80
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
const proxyResponse = await plugins.smartrequest.request(
|
|
191
|
-
destinationUrl,
|
|
192
|
-
{
|
|
193
|
-
method: originRequest.method,
|
|
194
|
-
headers: {
|
|
195
|
-
...originRequest.headers,
|
|
196
|
-
'X-Forwarded-Host': originRequest.headers.host,
|
|
197
|
-
'X-Forwarded-Proto': 'https',
|
|
198
|
-
},
|
|
199
|
-
keepAlive: true,
|
|
200
|
-
},
|
|
201
|
-
true, // lets make this streaming
|
|
202
|
-
(proxyRequest) => {
|
|
203
|
-
originRequest.on('data', (data) => {
|
|
204
|
-
proxyRequest.write(data);
|
|
205
|
-
});
|
|
206
|
-
originRequest.on('end', () => {
|
|
207
|
-
proxyRequest.end();
|
|
208
|
-
});
|
|
209
|
-
originRequest.on('error', () => {
|
|
210
|
-
proxyRequest.end();
|
|
211
|
-
});
|
|
212
|
-
originRequest.on('close', () => {
|
|
213
|
-
proxyRequest.end();
|
|
214
|
-
});
|
|
215
|
-
originRequest.on('timeout', () => {
|
|
216
|
-
proxyRequest.end();
|
|
217
|
-
originRequest.destroy();
|
|
218
|
-
});
|
|
219
|
-
proxyRequest.on('error', () => {
|
|
220
|
-
endOriginReqRes();
|
|
221
|
-
});
|
|
222
|
-
}
|
|
223
|
-
);
|
|
224
|
-
originResponse.statusCode = proxyResponse.statusCode;
|
|
225
|
-
console.log(proxyResponse.statusCode);
|
|
226
|
-
for (const defaultHeader of Object.keys(this.defaultHeaders)) {
|
|
227
|
-
originResponse.setHeader(defaultHeader, this.defaultHeaders[defaultHeader]);
|
|
228
|
-
}
|
|
229
|
-
for (const header of Object.keys(proxyResponse.headers)) {
|
|
230
|
-
originResponse.setHeader(header, proxyResponse.headers[header]);
|
|
231
|
-
}
|
|
232
|
-
proxyResponse.on('data', (data) => {
|
|
233
|
-
originResponse.write(data);
|
|
234
|
-
});
|
|
235
|
-
proxyResponse.on('end', () => {
|
|
236
|
-
originResponse.end();
|
|
237
|
-
});
|
|
238
|
-
proxyResponse.on('error', () => {
|
|
239
|
-
originResponse.destroy();
|
|
240
|
-
});
|
|
241
|
-
proxyResponse.on('close', () => {
|
|
242
|
-
originResponse.end();
|
|
243
|
-
});
|
|
244
|
-
proxyResponse.on('timeout', () => {
|
|
245
|
-
originResponse.end();
|
|
246
|
-
originResponse.destroy();
|
|
247
|
-
});
|
|
248
|
-
} catch (error) {
|
|
249
|
-
console.error('Error while processing request:', error);
|
|
250
|
-
endOriginReqRes(502, 'Bad Gateway: Error processing the request');
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
);
|
|
81
|
+
});
|
|
82
|
+
}, 60000); // runs every 1 minute
|
|
254
83
|
|
|
255
|
-
// Enable websockets
|
|
256
|
-
const wsServer = new plugins.ws.WebSocketServer({ server: this.httpsServer });
|
|
257
84
|
wsServer.on(
|
|
258
85
|
'connection',
|
|
259
|
-
|
|
86
|
+
(wsIncoming: IWebSocketWithHeartbeat, reqArg: plugins.http.IncomingMessage) => {
|
|
260
87
|
console.log(
|
|
261
|
-
`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}`,
|
|
262
89
|
);
|
|
263
90
|
|
|
264
|
-
|
|
91
|
+
wsIncoming.lastPong = Date.now();
|
|
92
|
+
wsIncoming.on('pong', () => {
|
|
93
|
+
wsIncoming.lastPong = Date.now();
|
|
94
|
+
});
|
|
265
95
|
|
|
96
|
+
let wsOutgoing: plugins.wsDefault;
|
|
266
97
|
const outGoingDeferred = plugins.smartpromise.defer();
|
|
267
98
|
|
|
99
|
+
// --- Improvement 2: Only call routeReq once ---
|
|
100
|
+
const wsDestinationConfig = this.router.routeReq(reqArg);
|
|
101
|
+
if (!wsDestinationConfig) {
|
|
102
|
+
wsIncoming.terminate();
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
268
105
|
try {
|
|
269
106
|
wsOutgoing = new plugins.wsDefault(
|
|
270
|
-
`ws://${
|
|
271
|
-
this.router.routeReq(reqArg).destinationPort
|
|
272
|
-
}${reqArg.url}`
|
|
107
|
+
`ws://${wsDestinationConfig.destinationIp}:${wsDestinationConfig.destinationPort}${reqArg.url}`,
|
|
273
108
|
);
|
|
274
109
|
console.log('wss proxy: initiated outgoing proxy');
|
|
275
110
|
wsOutgoing.on('open', async () => {
|
|
276
111
|
outGoingDeferred.resolve();
|
|
277
112
|
});
|
|
278
113
|
} catch (err) {
|
|
279
|
-
console.
|
|
114
|
+
console.error('Error initiating outgoing WebSocket:', err);
|
|
280
115
|
wsIncoming.terminate();
|
|
281
116
|
return;
|
|
282
117
|
}
|
|
@@ -301,23 +136,21 @@ JNj2Dr5H0XoLFFnvuvzcRbhlJ9J67JzR+7g=
|
|
|
301
136
|
const terminateWsOutgoing = () => {
|
|
302
137
|
if (wsOutgoing) {
|
|
303
138
|
wsOutgoing.terminate();
|
|
304
|
-
console.log('
|
|
139
|
+
console.log('Terminated outgoing ws.');
|
|
305
140
|
}
|
|
306
141
|
};
|
|
307
|
-
wsIncoming.on('error',
|
|
308
|
-
wsIncoming.on('close',
|
|
142
|
+
wsIncoming.on('error', terminateWsOutgoing);
|
|
143
|
+
wsIncoming.on('close', terminateWsOutgoing);
|
|
309
144
|
|
|
310
145
|
const terminateWsIncoming = () => {
|
|
311
146
|
if (wsIncoming) {
|
|
312
147
|
wsIncoming.terminate();
|
|
313
|
-
console.log('
|
|
148
|
+
console.log('Terminated incoming ws.');
|
|
314
149
|
}
|
|
315
150
|
};
|
|
316
|
-
wsOutgoing.on('error',
|
|
317
|
-
wsOutgoing.on('close',
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
}
|
|
151
|
+
wsOutgoing.on('error', terminateWsIncoming);
|
|
152
|
+
wsOutgoing.on('close', terminateWsIncoming);
|
|
153
|
+
},
|
|
321
154
|
);
|
|
322
155
|
|
|
323
156
|
this.httpsServer.keepAliveTimeout = 600 * 1000;
|
|
@@ -325,41 +158,174 @@ JNj2Dr5H0XoLFFnvuvzcRbhlJ9J67JzR+7g=
|
|
|
325
158
|
|
|
326
159
|
this.httpsServer.on('connection', (connection: plugins.net.Socket) => {
|
|
327
160
|
this.socketMap.add(connection);
|
|
328
|
-
console.log(`
|
|
161
|
+
console.log(`Added connection. Now ${this.socketMap.getArray().length} sockets connected.`);
|
|
329
162
|
const cleanupConnection = () => {
|
|
330
163
|
if (this.socketMap.checkForObject(connection)) {
|
|
331
164
|
this.socketMap.remove(connection);
|
|
332
|
-
console.log(`
|
|
165
|
+
console.log(`Removed connection. ${this.socketMap.getArray().length} sockets remaining.`);
|
|
333
166
|
connection.destroy();
|
|
334
167
|
}
|
|
335
168
|
};
|
|
336
|
-
connection.on('close',
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
connection.on('
|
|
340
|
-
cleanupConnection();
|
|
341
|
-
});
|
|
342
|
-
connection.on('end', () => {
|
|
343
|
-
cleanupConnection();
|
|
344
|
-
});
|
|
345
|
-
connection.on('timeout', () => {
|
|
346
|
-
cleanupConnection();
|
|
347
|
-
});
|
|
169
|
+
connection.on('close', cleanupConnection);
|
|
170
|
+
connection.on('error', cleanupConnection);
|
|
171
|
+
connection.on('end', cleanupConnection);
|
|
172
|
+
connection.on('timeout', cleanupConnection);
|
|
348
173
|
});
|
|
349
174
|
|
|
350
175
|
this.httpsServer.listen(this.options.port);
|
|
351
176
|
console.log(
|
|
352
|
-
`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}`,
|
|
353
178
|
);
|
|
354
179
|
}
|
|
355
180
|
|
|
356
|
-
|
|
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
|
+
) {
|
|
357
325
|
console.log(`got new proxy configs`);
|
|
358
326
|
this.proxyConfigs = proxyConfigsArg;
|
|
359
327
|
this.router.setNewProxyConfigs(proxyConfigsArg);
|
|
360
328
|
for (const hostCandidate of this.proxyConfigs) {
|
|
361
|
-
// console.log(hostCandidate);
|
|
362
|
-
|
|
363
329
|
const existingHostNameConfig = this.alreadyAddedReverseConfigs[hostCandidate.hostName];
|
|
364
330
|
|
|
365
331
|
if (!existingHostNameConfig) {
|
|
@@ -393,10 +359,11 @@ JNj2Dr5H0XoLFFnvuvzcRbhlJ9J67JzR+7g=
|
|
|
393
359
|
this.httpsServer.close(() => {
|
|
394
360
|
done.resolve();
|
|
395
361
|
});
|
|
396
|
-
|
|
362
|
+
for (const socket of this.socketMap.getArray()) {
|
|
397
363
|
socket.destroy();
|
|
398
|
-
}
|
|
364
|
+
}
|
|
399
365
|
await done.promise;
|
|
366
|
+
clearInterval(this.heartbeatInterval);
|
|
400
367
|
console.log('NetworkProxy -> OK: Server has been stopped and all connections closed.');
|
|
401
368
|
}
|
|
402
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 {};
|