arn-browser 0.1.22 → 0.1.24
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/package.json
CHANGED
|
@@ -30,10 +30,14 @@ export interface GeoDetails {
|
|
|
30
30
|
export interface TrafficStats {
|
|
31
31
|
/** Number of requests handled */
|
|
32
32
|
req: number;
|
|
33
|
-
/** Data sent to client (
|
|
34
|
-
|
|
35
|
-
/** Data received from client (
|
|
36
|
-
|
|
33
|
+
/** Data sent to client (source Tx, formatted string) */
|
|
34
|
+
sTx: string;
|
|
35
|
+
/** Data received from client (source Rx, formatted string) */
|
|
36
|
+
sRx: string;
|
|
37
|
+
/** Data sent to upstream proxy (target Tx, formatted string). Only present when showTg is true. */
|
|
38
|
+
tTx?: string;
|
|
39
|
+
/** Data received from upstream proxy (target Rx, formatted string). Only present when showTg is true. */
|
|
40
|
+
tRx?: string;
|
|
37
41
|
}
|
|
38
42
|
|
|
39
43
|
/**
|
|
@@ -60,7 +64,7 @@ export interface ProxyServerOptions {
|
|
|
60
64
|
*/
|
|
61
65
|
ip2LocationKey?: string;
|
|
62
66
|
|
|
63
|
-
/** Retry delay in milliseconds between IP lookup attempts (default:
|
|
67
|
+
/** Retry delay in milliseconds between IP lookup attempts (default: 1000) */
|
|
64
68
|
retryDelayMs?: number;
|
|
65
69
|
|
|
66
70
|
/** Enable verbose logging (default: false) */
|
|
@@ -69,6 +73,8 @@ export interface ProxyServerOptions {
|
|
|
69
73
|
proxy_stats?: boolean;
|
|
70
74
|
/** Track hostname usage statistics (default: true) */
|
|
71
75
|
host_stats?: boolean;
|
|
76
|
+
/** Show target bytes (tTx/tRx) in stats output (default: false) */
|
|
77
|
+
tg_stats?: boolean;
|
|
72
78
|
}
|
|
73
79
|
|
|
74
80
|
/**
|
|
@@ -137,6 +143,6 @@ export function fetchPublicIP(proxyUrl: string | null): Promise<GeoDetails | nul
|
|
|
137
143
|
* Fetches proxy details (IP, country, city, timezone) with retries.
|
|
138
144
|
* @param proxyUrl The proxy URL string. Pass `null` for local fallback.
|
|
139
145
|
* @param apiKey Optional IP2Location API key.
|
|
140
|
-
* @param retryDelayMs Delay between retries in ms (default:
|
|
146
|
+
* @param retryDelayMs Delay between retries in ms (default: 1000).
|
|
141
147
|
*/
|
|
142
148
|
export function fetchProxyDetails(proxyUrl: string | null, apiKey?: string | null, retryDelayMs?: number): Promise<GeoDetails | null>;
|
|
@@ -10,8 +10,8 @@ import { SocksProxyAgent } from "socks-proxy-agent";
|
|
|
10
10
|
// SECTION 1: NETWORK HELPERS
|
|
11
11
|
// ==========================================
|
|
12
12
|
|
|
13
|
-
function nativeGet(url, agent = null, timeout = 5000) {
|
|
14
|
-
|
|
13
|
+
async function nativeGet(url, agent = null, timeout = 5000, retries = 5) {
|
|
14
|
+
const attempt = () => new Promise((resolve, reject) => {
|
|
15
15
|
const urlObj = new URL(url);
|
|
16
16
|
const requestModule = urlObj.protocol === "https:" ? https : http;
|
|
17
17
|
|
|
@@ -40,6 +40,15 @@ function nativeGet(url, agent = null, timeout = 5000) {
|
|
|
40
40
|
reject(new Error("Request Timed Out"));
|
|
41
41
|
});
|
|
42
42
|
});
|
|
43
|
+
|
|
44
|
+
for (let i = 1; i <= retries; i++) {
|
|
45
|
+
try {
|
|
46
|
+
return await attempt();
|
|
47
|
+
} catch (err) {
|
|
48
|
+
if (i === retries) throw err;
|
|
49
|
+
await sleep(1000);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
43
52
|
}
|
|
44
53
|
|
|
45
54
|
const isPortAvailable = (port) => {
|
|
@@ -101,7 +110,7 @@ export async function fetchPublicIP(proxyUrl) {
|
|
|
101
110
|
* 1. If proxyUrl is provided -> Fetch Real Data (IP + Timezone).
|
|
102
111
|
* 2. If proxyUrl is NULL/Undefined -> Return "Local" Mock immediately.
|
|
103
112
|
*/
|
|
104
|
-
export async function fetchProxyDetails(proxyUrl, apiKey = null, retryDelayMs =
|
|
113
|
+
export async function fetchProxyDetails(proxyUrl, apiKey = null, retryDelayMs = 1000) {
|
|
105
114
|
// --- AUTOMATIC LOCAL FALLBACK ---
|
|
106
115
|
if (!proxyUrl) {
|
|
107
116
|
return {
|
|
@@ -112,9 +121,9 @@ export async function fetchProxyDetails(proxyUrl, apiKey = null, retryDelayMs =
|
|
|
112
121
|
};
|
|
113
122
|
}
|
|
114
123
|
|
|
115
|
-
// --- EXTERNAL LOOKUP with retry (
|
|
124
|
+
// --- EXTERNAL LOOKUP with retry (5 attempts) ---
|
|
116
125
|
const providers = ["IP-API", "IP2Location"];
|
|
117
|
-
const MAX_RETRIES =
|
|
126
|
+
const MAX_RETRIES = 5;
|
|
118
127
|
|
|
119
128
|
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
|
120
129
|
for (const provider of providers) {
|
|
@@ -186,10 +195,11 @@ export async function startProxyServer({
|
|
|
186
195
|
PROXY_2_HOSTS = [],
|
|
187
196
|
NO_PROXY_HOSTS = [],
|
|
188
197
|
ip2LocationKey = null,
|
|
189
|
-
retryDelayMs =
|
|
198
|
+
retryDelayMs = 1000,
|
|
190
199
|
debug = false,
|
|
191
200
|
proxy_stats = true,
|
|
192
201
|
host_stats = true,
|
|
202
|
+
tg_stats = false,
|
|
193
203
|
}) {
|
|
194
204
|
// 1. Matchers
|
|
195
205
|
const matchers = {
|
|
@@ -360,16 +370,21 @@ export async function startProxyServer({
|
|
|
360
370
|
return (bytes / (1024 * 1024)).toFixed(3) + " MB";
|
|
361
371
|
};
|
|
362
372
|
|
|
373
|
+
|
|
374
|
+
|
|
363
375
|
const getProxyStatsFormatted = () => {
|
|
364
376
|
const formatted = {};
|
|
365
377
|
for (const [key, val] of Object.entries(stats)) {
|
|
366
|
-
|
|
378
|
+
const entry = {
|
|
367
379
|
req: val.request,
|
|
368
380
|
sTx: formatBytes(val.srcTx),
|
|
369
381
|
sRx: formatBytes(val.srcRx),
|
|
370
|
-
tTx: formatBytes(val.trgTx),
|
|
371
|
-
tRx: formatBytes(val.trgRx),
|
|
372
382
|
};
|
|
383
|
+
if (tg_stats) {
|
|
384
|
+
entry.tTx = formatBytes(val.trgTx);
|
|
385
|
+
entry.tRx = formatBytes(val.trgRx);
|
|
386
|
+
}
|
|
387
|
+
formatted[key] = entry;
|
|
373
388
|
}
|
|
374
389
|
return formatted;
|
|
375
390
|
};
|
|
@@ -380,13 +395,16 @@ export async function startProxyServer({
|
|
|
380
395
|
const sortedHosts = Object.entries(hosts)
|
|
381
396
|
.sort((a, b) => b[1].req - a[1].req)
|
|
382
397
|
.reduce((acc, [host, hostData]) => {
|
|
383
|
-
|
|
398
|
+
const entry = {
|
|
384
399
|
req: hostData.req,
|
|
385
400
|
sTx: formatBytes(hostData.srcTx),
|
|
386
401
|
sRx: formatBytes(hostData.srcRx),
|
|
387
|
-
tTx: formatBytes(hostData.trgTx),
|
|
388
|
-
tRx: formatBytes(hostData.trgRx),
|
|
389
402
|
};
|
|
403
|
+
if (tg_stats) {
|
|
404
|
+
entry.tTx = formatBytes(hostData.trgTx);
|
|
405
|
+
entry.tRx = formatBytes(hostData.trgRx);
|
|
406
|
+
}
|
|
407
|
+
acc[host] = entry;
|
|
390
408
|
return acc;
|
|
391
409
|
}, {});
|
|
392
410
|
|
|
@@ -425,11 +443,11 @@ export async function startProxyServer({
|
|
|
425
443
|
await server.close(true);
|
|
426
444
|
serverRunning = false;
|
|
427
445
|
|
|
428
|
-
// Wait up to
|
|
446
|
+
// Wait up to 5000ms (5 seconds) for all async 'connectionClosed' events to fire
|
|
429
447
|
// When sockets are destroyed, their 'close' events happen asynchronously
|
|
430
|
-
let maxWait =
|
|
448
|
+
let maxWait = 250; // 250 * 20ms = 5000ms
|
|
431
449
|
while (Object.keys(connectionMap).length > 0 && maxWait > 0) {
|
|
432
|
-
await new Promise((resolve) => setTimeout(resolve,
|
|
450
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
433
451
|
maxWait--;
|
|
434
452
|
}
|
|
435
453
|
|