@push.rocks/smartproxy 19.5.4 → 19.5.6

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 (62) hide show
  1. package/dist_ts/core/utils/async-utils.d.ts +81 -0
  2. package/dist_ts/core/utils/async-utils.js +216 -0
  3. package/dist_ts/core/utils/binary-heap.d.ts +73 -0
  4. package/dist_ts/core/utils/binary-heap.js +193 -0
  5. package/dist_ts/core/utils/enhanced-connection-pool.d.ts +110 -0
  6. package/dist_ts/core/utils/enhanced-connection-pool.js +320 -0
  7. package/dist_ts/core/utils/fs-utils.d.ts +144 -0
  8. package/dist_ts/core/utils/fs-utils.js +252 -0
  9. package/dist_ts/core/utils/index.d.ts +6 -2
  10. package/dist_ts/core/utils/index.js +7 -3
  11. package/dist_ts/core/utils/lifecycle-component.d.ts +59 -0
  12. package/dist_ts/core/utils/lifecycle-component.js +195 -0
  13. package/dist_ts/core/utils/socket-utils.d.ts +28 -0
  14. package/dist_ts/core/utils/socket-utils.js +77 -0
  15. package/dist_ts/forwarding/handlers/http-handler.js +7 -4
  16. package/dist_ts/forwarding/handlers/https-passthrough-handler.js +14 -55
  17. package/dist_ts/forwarding/handlers/https-terminate-to-http-handler.js +52 -40
  18. package/dist_ts/forwarding/handlers/https-terminate-to-https-handler.js +31 -43
  19. package/dist_ts/plugins.d.ts +2 -1
  20. package/dist_ts/plugins.js +3 -2
  21. package/dist_ts/proxies/http-proxy/certificate-manager.d.ts +15 -0
  22. package/dist_ts/proxies/http-proxy/certificate-manager.js +49 -2
  23. package/dist_ts/proxies/http-proxy/connection-pool.js +4 -19
  24. package/dist_ts/proxies/http-proxy/http-proxy.js +3 -7
  25. package/dist_ts/proxies/nftables-proxy/nftables-proxy.d.ts +10 -0
  26. package/dist_ts/proxies/nftables-proxy/nftables-proxy.js +53 -43
  27. package/dist_ts/proxies/smart-proxy/cert-store.js +22 -20
  28. package/dist_ts/proxies/smart-proxy/connection-manager.d.ts +35 -9
  29. package/dist_ts/proxies/smart-proxy/connection-manager.js +243 -189
  30. package/dist_ts/proxies/smart-proxy/http-proxy-bridge.js +13 -2
  31. package/dist_ts/proxies/smart-proxy/port-manager.js +3 -3
  32. package/dist_ts/proxies/smart-proxy/route-connection-handler.js +35 -4
  33. package/package.json +2 -2
  34. package/readme.hints.md +96 -1
  35. package/readme.plan.md +1135 -221
  36. package/readme.problems.md +167 -83
  37. package/ts/core/utils/async-utils.ts +275 -0
  38. package/ts/core/utils/binary-heap.ts +225 -0
  39. package/ts/core/utils/enhanced-connection-pool.ts +420 -0
  40. package/ts/core/utils/fs-utils.ts +270 -0
  41. package/ts/core/utils/index.ts +6 -2
  42. package/ts/core/utils/lifecycle-component.ts +231 -0
  43. package/ts/core/utils/socket-utils.ts +96 -0
  44. package/ts/forwarding/handlers/http-handler.ts +7 -3
  45. package/ts/forwarding/handlers/https-passthrough-handler.ts +13 -62
  46. package/ts/forwarding/handlers/https-terminate-to-http-handler.ts +58 -46
  47. package/ts/forwarding/handlers/https-terminate-to-https-handler.ts +38 -53
  48. package/ts/plugins.ts +2 -1
  49. package/ts/proxies/http-proxy/certificate-manager.ts +52 -1
  50. package/ts/proxies/http-proxy/connection-pool.ts +3 -16
  51. package/ts/proxies/http-proxy/http-proxy.ts +2 -5
  52. package/ts/proxies/nftables-proxy/nftables-proxy.ts +64 -79
  53. package/ts/proxies/smart-proxy/cert-store.ts +26 -20
  54. package/ts/proxies/smart-proxy/connection-manager.ts +277 -197
  55. package/ts/proxies/smart-proxy/http-proxy-bridge.ts +15 -1
  56. package/ts/proxies/smart-proxy/port-manager.ts +2 -2
  57. package/ts/proxies/smart-proxy/route-connection-handler.ts +39 -4
  58. package/readme.plan2.md +0 -764
  59. package/ts/common/eventUtils.ts +0 -34
  60. package/ts/common/types.ts +0 -91
  61. package/ts/core/utils/event-system.ts +0 -376
  62. package/ts/core/utils/event-utils.ts +0 -25
@@ -3,6 +3,8 @@ import { promisify } from 'util';
3
3
  import * as fs from 'fs';
4
4
  import * as path from 'path';
5
5
  import * as os from 'os';
6
+ import { delay } from '../../core/utils/async-utils.js';
7
+ import { AsyncFileSystem } from '../../core/utils/fs-utils.js';
6
8
  import {
7
9
  NftBaseError,
8
10
  NftValidationError,
@@ -208,7 +210,7 @@ export class NfTablesProxy {
208
210
 
209
211
  // Wait before retry, unless it's the last attempt
210
212
  if (i < maxRetries - 1) {
211
- await new Promise(resolve => setTimeout(resolve, retryDelayMs));
213
+ await delay(retryDelayMs);
212
214
  }
213
215
  }
214
216
  }
@@ -218,8 +220,13 @@ export class NfTablesProxy {
218
220
 
219
221
  /**
220
222
  * Execute system command synchronously with multiple attempts
223
+ * @deprecated This method blocks the event loop and should be avoided. Use executeWithRetry instead.
224
+ * WARNING: This method contains a busy wait loop that will block the entire Node.js event loop!
221
225
  */
222
226
  private executeWithRetrySync(command: string, maxRetries = 3, retryDelayMs = 1000): string {
227
+ // Log deprecation warning
228
+ console.warn('[DEPRECATION WARNING] executeWithRetrySync blocks the event loop and should not be used. Consider using the async executeWithRetry method instead.');
229
+
223
230
  let lastError: Error | undefined;
224
231
 
225
232
  for (let i = 0; i < maxRetries; i++) {
@@ -231,10 +238,12 @@ export class NfTablesProxy {
231
238
 
232
239
  // Wait before retry, unless it's the last attempt
233
240
  if (i < maxRetries - 1) {
234
- // A naive sleep in sync context
241
+ // CRITICAL: This busy wait loop blocks the entire event loop!
242
+ // This is a temporary fallback for sync contexts only.
243
+ // TODO: Remove this method entirely and make all callers async
235
244
  const waitUntil = Date.now() + retryDelayMs;
236
245
  while (Date.now() < waitUntil) {
237
- // busy wait - not great, but this is a fallback method
246
+ // Busy wait - blocks event loop
238
247
  }
239
248
  }
240
249
  }
@@ -243,6 +252,26 @@ export class NfTablesProxy {
243
252
  throw new NftExecutionError(`Failed after ${maxRetries} attempts: ${lastError?.message || 'Unknown error'}`);
244
253
  }
245
254
 
255
+ /**
256
+ * Execute nftables commands with a temporary file
257
+ * This helper handles the common pattern of writing rules to a temp file,
258
+ * executing nftables with the file, and cleaning up
259
+ */
260
+ private async executeWithTempFile(rulesetContent: string): Promise<void> {
261
+ await AsyncFileSystem.writeFile(this.tempFilePath, rulesetContent);
262
+
263
+ try {
264
+ await this.executeWithRetry(
265
+ `${NfTablesProxy.NFT_CMD} -f ${this.tempFilePath}`,
266
+ this.settings.maxRetries,
267
+ this.settings.retryDelayMs
268
+ );
269
+ } finally {
270
+ // Always clean up the temp file
271
+ await AsyncFileSystem.remove(this.tempFilePath);
272
+ }
273
+ }
274
+
246
275
  /**
247
276
  * Checks if nftables is available and the required modules are loaded
248
277
  */
@@ -545,15 +574,8 @@ export class NfTablesProxy {
545
574
 
546
575
  // Only write and apply if we have rules to add
547
576
  if (rulesetContent) {
548
- // Write the ruleset to a temporary file
549
- fs.writeFileSync(this.tempFilePath, rulesetContent);
550
-
551
- // Apply the ruleset
552
- await this.executeWithRetry(
553
- `${NfTablesProxy.NFT_CMD} -f ${this.tempFilePath}`,
554
- this.settings.maxRetries,
555
- this.settings.retryDelayMs
556
- );
577
+ // Apply the ruleset using the helper
578
+ await this.executeWithTempFile(rulesetContent);
557
579
 
558
580
  this.log('info', `Added source IP filter rules for ${family}`);
559
581
 
@@ -566,9 +588,6 @@ export class NfTablesProxy {
566
588
  await this.verifyRuleApplication(rule);
567
589
  }
568
590
  }
569
-
570
- // Remove the temporary file
571
- fs.unlinkSync(this.tempFilePath);
572
591
  }
573
592
 
574
593
  return true;
@@ -663,13 +682,7 @@ export class NfTablesProxy {
663
682
 
664
683
  // Apply the rules if we have any
665
684
  if (rulesetContent) {
666
- fs.writeFileSync(this.tempFilePath, rulesetContent);
667
-
668
- await this.executeWithRetry(
669
- `${NfTablesProxy.NFT_CMD} -f ${this.tempFilePath}`,
670
- this.settings.maxRetries,
671
- this.settings.retryDelayMs
672
- );
685
+ await this.executeWithTempFile(rulesetContent);
673
686
 
674
687
  this.log('info', `Added advanced NAT rules for ${family}`);
675
688
 
@@ -682,9 +695,6 @@ export class NfTablesProxy {
682
695
  await this.verifyRuleApplication(rule);
683
696
  }
684
697
  }
685
-
686
- // Remove the temporary file
687
- fs.unlinkSync(this.tempFilePath);
688
698
  }
689
699
  }
690
700
 
@@ -816,15 +826,8 @@ export class NfTablesProxy {
816
826
 
817
827
  // Apply the ruleset if we have any rules
818
828
  if (rulesetContent) {
819
- // Write to temporary file
820
- fs.writeFileSync(this.tempFilePath, rulesetContent);
821
-
822
- // Apply the ruleset
823
- await this.executeWithRetry(
824
- `${NfTablesProxy.NFT_CMD} -f ${this.tempFilePath}`,
825
- this.settings.maxRetries,
826
- this.settings.retryDelayMs
827
- );
829
+ // Apply the ruleset using the helper
830
+ await this.executeWithTempFile(rulesetContent);
828
831
 
829
832
  this.log('info', `Added port forwarding rules for ${family}`);
830
833
 
@@ -837,9 +840,6 @@ export class NfTablesProxy {
837
840
  await this.verifyRuleApplication(rule);
838
841
  }
839
842
  }
840
-
841
- // Remove temporary file
842
- fs.unlinkSync(this.tempFilePath);
843
843
  }
844
844
 
845
845
  return true;
@@ -931,15 +931,7 @@ export class NfTablesProxy {
931
931
 
932
932
  // Apply the ruleset if we have any rules
933
933
  if (rulesetContent) {
934
- // Write to temporary file
935
- fs.writeFileSync(this.tempFilePath, rulesetContent);
936
-
937
- // Apply the ruleset
938
- await this.executeWithRetry(
939
- `${NfTablesProxy.NFT_CMD} -f ${this.tempFilePath}`,
940
- this.settings.maxRetries,
941
- this.settings.retryDelayMs
942
- );
934
+ await this.executeWithTempFile(rulesetContent);
943
935
 
944
936
  this.log('info', `Added port forwarding rules for ${family}`);
945
937
 
@@ -952,9 +944,6 @@ export class NfTablesProxy {
952
944
  await this.verifyRuleApplication(rule);
953
945
  }
954
946
  }
955
-
956
- // Remove temporary file
957
- fs.unlinkSync(this.tempFilePath);
958
947
  }
959
948
 
960
949
  return true;
@@ -1027,15 +1016,8 @@ export class NfTablesProxy {
1027
1016
 
1028
1017
  // Apply the ruleset if we have any rules
1029
1018
  if (rulesetContent) {
1030
- // Write to temporary file
1031
- fs.writeFileSync(this.tempFilePath, rulesetContent);
1032
-
1033
- // Apply the ruleset
1034
- await this.executeWithRetry(
1035
- `${NfTablesProxy.NFT_CMD} -f ${this.tempFilePath}`,
1036
- this.settings.maxRetries,
1037
- this.settings.retryDelayMs
1038
- );
1019
+ // Apply the ruleset using the helper
1020
+ await this.executeWithTempFile(rulesetContent);
1039
1021
 
1040
1022
  this.log('info', `Added QoS rules for ${family}`);
1041
1023
 
@@ -1048,9 +1030,6 @@ export class NfTablesProxy {
1048
1030
  await this.verifyRuleApplication(rule);
1049
1031
  }
1050
1032
  }
1051
-
1052
- // Remove temporary file
1053
- fs.unlinkSync(this.tempFilePath);
1054
1033
  }
1055
1034
 
1056
1035
  return true;
@@ -1615,25 +1594,27 @@ export class NfTablesProxy {
1615
1594
  // Apply the ruleset if we have any rules to delete
1616
1595
  if (rulesetContent) {
1617
1596
  // Write to temporary file
1618
- fs.writeFileSync(this.tempFilePath, rulesetContent);
1619
-
1620
- // Apply the ruleset
1621
- await this.executeWithRetry(
1622
- `${NfTablesProxy.NFT_CMD} -f ${this.tempFilePath}`,
1623
- this.settings.maxRetries,
1624
- this.settings.retryDelayMs
1625
- );
1626
-
1627
- this.log('info', 'Removed all added rules');
1628
-
1629
- // Mark all rules as removed
1630
- this.rules.forEach(rule => {
1631
- rule.added = false;
1632
- rule.verified = false;
1633
- });
1597
+ await AsyncFileSystem.writeFile(this.tempFilePath, rulesetContent);
1634
1598
 
1635
- // Remove temporary file
1636
- fs.unlinkSync(this.tempFilePath);
1599
+ try {
1600
+ // Apply the ruleset
1601
+ await this.executeWithRetry(
1602
+ `${NfTablesProxy.NFT_CMD} -f ${this.tempFilePath}`,
1603
+ this.settings.maxRetries,
1604
+ this.settings.retryDelayMs
1605
+ );
1606
+
1607
+ this.log('info', 'Removed all added rules');
1608
+
1609
+ // Mark all rules as removed
1610
+ this.rules.forEach(rule => {
1611
+ rule.added = false;
1612
+ rule.verified = false;
1613
+ });
1614
+ } finally {
1615
+ // Remove temporary file
1616
+ await AsyncFileSystem.remove(this.tempFilePath);
1617
+ }
1637
1618
  }
1638
1619
 
1639
1620
  // Clean up IP sets if we created any
@@ -1862,8 +1843,12 @@ export class NfTablesProxy {
1862
1843
 
1863
1844
  /**
1864
1845
  * Synchronous version of cleanSlate
1846
+ * @deprecated This method blocks the event loop and should be avoided. Use cleanSlate() instead.
1847
+ * WARNING: This method uses execSync which blocks the entire Node.js event loop!
1865
1848
  */
1866
1849
  public static cleanSlateSync(): void {
1850
+ console.warn('[DEPRECATION WARNING] cleanSlateSync blocks the event loop and should not be used. Consider using the async cleanSlate() method instead.');
1851
+
1867
1852
  try {
1868
1853
  // Check for rules with our comment pattern
1869
1854
  const stdout = execSync(`${NfTablesProxy.NFT_CMD} list ruleset`).toString();
@@ -1,36 +1,34 @@
1
1
  import * as plugins from '../../plugins.js';
2
+ import { AsyncFileSystem } from '../../core/utils/fs-utils.js';
2
3
  import type { ICertificateData } from './certificate-manager.js';
3
4
 
4
5
  export class CertStore {
5
6
  constructor(private certDir: string) {}
6
7
 
7
8
  public async initialize(): Promise<void> {
8
- await plugins.smartfile.fs.ensureDirSync(this.certDir);
9
+ await AsyncFileSystem.ensureDir(this.certDir);
9
10
  }
10
11
 
11
12
  public async getCertificate(routeName: string): Promise<ICertificateData | null> {
12
13
  const certPath = this.getCertPath(routeName);
13
14
  const metaPath = `${certPath}/meta.json`;
14
15
 
15
- if (!await plugins.smartfile.fs.fileExistsSync(metaPath)) {
16
+ if (!await AsyncFileSystem.exists(metaPath)) {
16
17
  return null;
17
18
  }
18
19
 
19
20
  try {
20
- const metaFile = await plugins.smartfile.SmartFile.fromFilePath(metaPath);
21
- const meta = JSON.parse(metaFile.contents.toString());
21
+ const meta = await AsyncFileSystem.readJSON(metaPath);
22
22
 
23
- const certFile = await plugins.smartfile.SmartFile.fromFilePath(`${certPath}/cert.pem`);
24
- const cert = certFile.contents.toString();
25
-
26
- const keyFile = await plugins.smartfile.SmartFile.fromFilePath(`${certPath}/key.pem`);
27
- const key = keyFile.contents.toString();
23
+ const [cert, key] = await Promise.all([
24
+ AsyncFileSystem.readFile(`${certPath}/cert.pem`),
25
+ AsyncFileSystem.readFile(`${certPath}/key.pem`)
26
+ ]);
28
27
 
29
28
  let ca: string | undefined;
30
29
  const caPath = `${certPath}/ca.pem`;
31
- if (await plugins.smartfile.fs.fileExistsSync(caPath)) {
32
- const caFile = await plugins.smartfile.SmartFile.fromFilePath(caPath);
33
- ca = caFile.contents.toString();
30
+ if (await AsyncFileSystem.exists(caPath)) {
31
+ ca = await AsyncFileSystem.readFile(caPath);
34
32
  }
35
33
 
36
34
  return {
@@ -51,14 +49,18 @@ export class CertStore {
51
49
  certData: ICertificateData
52
50
  ): Promise<void> {
53
51
  const certPath = this.getCertPath(routeName);
54
- await plugins.smartfile.fs.ensureDirSync(certPath);
52
+ await AsyncFileSystem.ensureDir(certPath);
55
53
 
56
- // Save certificate files
57
- await plugins.smartfile.memory.toFs(certData.cert, `${certPath}/cert.pem`);
58
- await plugins.smartfile.memory.toFs(certData.key, `${certPath}/key.pem`);
54
+ // Save certificate files in parallel
55
+ const savePromises = [
56
+ AsyncFileSystem.writeFile(`${certPath}/cert.pem`, certData.cert),
57
+ AsyncFileSystem.writeFile(`${certPath}/key.pem`, certData.key)
58
+ ];
59
59
 
60
60
  if (certData.ca) {
61
- await plugins.smartfile.memory.toFs(certData.ca, `${certPath}/ca.pem`);
61
+ savePromises.push(
62
+ AsyncFileSystem.writeFile(`${certPath}/ca.pem`, certData.ca)
63
+ );
62
64
  }
63
65
 
64
66
  // Save metadata
@@ -68,13 +70,17 @@ export class CertStore {
68
70
  savedAt: new Date().toISOString()
69
71
  };
70
72
 
71
- await plugins.smartfile.memory.toFs(JSON.stringify(meta, null, 2), `${certPath}/meta.json`);
73
+ savePromises.push(
74
+ AsyncFileSystem.writeJSON(`${certPath}/meta.json`, meta)
75
+ );
76
+
77
+ await Promise.all(savePromises);
72
78
  }
73
79
 
74
80
  public async deleteCertificate(routeName: string): Promise<void> {
75
81
  const certPath = this.getCertPath(routeName);
76
- if (await plugins.smartfile.fs.fileExistsSync(certPath)) {
77
- await plugins.smartfile.fs.removeManySync([certPath]);
82
+ if (await AsyncFileSystem.isDirectory(certPath)) {
83
+ await AsyncFileSystem.removeDir(certPath);
78
84
  }
79
85
  }
80
86