@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.
Files changed (33) hide show
  1. package/assets/certs/cert.pem +19 -0
  2. package/assets/certs/key.pem +28 -0
  3. package/dist_ts/00_commitinfo_data.js +1 -1
  4. package/dist_ts/smartproxy.classes.networkproxy.d.ts +4 -5
  5. package/dist_ts/smartproxy.classes.networkproxy.js +172 -232
  6. package/dist_ts/smartproxy.classes.router.d.ts +0 -1
  7. package/dist_ts/smartproxy.classes.router.js +11 -2
  8. package/dist_ts/smartproxy.classes.sslredirect.d.ts +0 -1
  9. package/dist_ts/smartproxy.helpers.certificates.d.ts +5 -0
  10. package/dist_ts/smartproxy.helpers.certificates.js +23 -0
  11. package/dist_ts/smartproxy.portproxy.d.ts +0 -1
  12. package/package.json +18 -14
  13. package/readme.md +3 -1
  14. package/ts/00_commitinfo_data.ts +1 -1
  15. package/ts/smartproxy.classes.networkproxy.ts +202 -259
  16. package/ts/smartproxy.classes.router.ts +10 -1
  17. package/ts/smartproxy.helpers.certificates.ts +30 -0
  18. package/dist/index.d.ts +0 -1
  19. package/dist/index.js +0 -7
  20. package/dist/smartproxy.classes.proxyworker.d.ts +0 -23
  21. package/dist/smartproxy.classes.proxyworker.js +0 -262
  22. package/dist/smartproxy.classes.router.d.ts +0 -16
  23. package/dist/smartproxy.classes.router.js +0 -41
  24. package/dist/smartproxy.classes.smartproxy.d.ts +0 -17
  25. package/dist/smartproxy.classes.smartproxy.js +0 -43
  26. package/dist/smartproxy.plugins.d.ts +0 -17
  27. package/dist/smartproxy.plugins.js +0 -43
  28. package/dist/smartproxy.portproxy.d.ts +0 -6
  29. package/dist/smartproxy.portproxy.js +0 -38
  30. package/dist_ts/smartproxy.classes.proxyworker.d.ts +0 -33
  31. package/dist_ts/smartproxy.classes.proxyworker.js +0 -303
  32. package/dist_ts/smartproxy.classes.smartproxy.d.ts +0 -20
  33. 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 WebSocketWithHeartbeat extends plugins.wsDefault {
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: `-----BEGIN PRIVATE KEY-----
43
- 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
- 3yEWvy2mRHOZoSSBtIqg6Bre4ZcMu901/cHNIjFnynNGFl9Se61yZbW2F3PfCt7+
45
- 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
- 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
- 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
- mMYLcARMnDmIQDVstD1i+MM2lVhx/pm9xKKUgWNJC7lyz2xRscZ4pOtLkfN94leH
49
- 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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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
- PkzyUVuU3jpWZkBE2VhXpDXlyW8xR/y1ZOaZZ//XcZTrZf57pGKFp30H/PlDPH3C
68
- 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
- c8bV4ymk35fVJu/+pTZpPnMkYrFReso/uNn07y1iga/9mwkUBNrT+fWE7RzjT7H8
70
- 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
- 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
- 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
- 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
- 3etjopWRd0I3ID+WkkGKjYWRQ1bkKjvkkj1v7bHenX17nfIp5WU1aXTMYUCMMszm
75
- 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
- v3+HQ7OkviX88QKCAQEAg5IJdfKKfindzYieM3WwjW8VkH4LdVLQSW3WlCkMkVgC
77
- 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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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
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
- async (originRequest, originResponse) => {
125
- /**
126
- * endRequest function
127
- * can be used to prematurely end a request
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
- originResponse.destroy();
252
- });
253
- } catch (error) {
254
- console.error('Error while processing request:', error);
255
- endOriginReqRes(502, 'Bad Gateway: Error processing the request');
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 WebSocketWithHeartbeat;
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
- async (wsIncoming: WebSocketWithHeartbeat, reqArg: plugins.http.IncomingMessage) => {
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://${this.router.routeReq(reqArg).destinationIp}:${
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.log(err);
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('terminated outgoing ws.');
139
+ console.log('Terminated outgoing ws.');
332
140
  }
333
141
  };
334
- wsIncoming.on('error', () => terminateWsOutgoing());
335
- wsIncoming.on('close', () => terminateWsOutgoing());
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('terminated incoming ws.');
148
+ console.log('Terminated incoming ws.');
341
149
  }
342
150
  };
343
- wsOutgoing.on('error', () => terminateWsIncoming());
344
- wsOutgoing.on('close', () => terminateWsIncoming());
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(`added connection. now ${this.socketMap.getArray().length} sockets connected.`);
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(`removed connection. ${this.socketMap.getArray().length} sockets remaining.`);
165
+ console.log(`Removed connection. ${this.socketMap.getArray().length} sockets remaining.`);
358
166
  connection.destroy();
359
167
  }
360
168
  };
361
- connection.on('close', () => {
362
- cleanupConnection();
363
- });
364
- connection.on('error', () => {
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
- public async updateProxyConfigs(proxyConfigsArg: plugins.tsclass.network.IReverseProxyConfig[]) {
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
- await this.socketMap.forEach(async (socket) => {
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 === originalHost;
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 {};