arn-browser 0.1.2 → 0.1.4
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
|
@@ -64,6 +64,8 @@ export interface ProxyServerOptions {
|
|
|
64
64
|
debug?: boolean;
|
|
65
65
|
/** Track proxy usage statistics (default: true) */
|
|
66
66
|
proxy_stats?: boolean;
|
|
67
|
+
/** Track hostname usage statistics (default: true) */
|
|
68
|
+
host_stats?: boolean;
|
|
67
69
|
}
|
|
68
70
|
|
|
69
71
|
/**
|
|
@@ -102,11 +104,17 @@ export interface ProxyServerController {
|
|
|
102
104
|
/** The public IP of Proxy 2 */
|
|
103
105
|
PROXY_2_IP: string | null;
|
|
104
106
|
|
|
107
|
+
/** Check if the proxy server is currently running */
|
|
108
|
+
isServerRunning: () => boolean;
|
|
109
|
+
|
|
105
110
|
/** Gracefully closes the proxy server */
|
|
106
111
|
closeServer: () => Promise<void>;
|
|
107
112
|
|
|
108
113
|
/** Returns formatted statistics for all proxy channels */
|
|
109
114
|
getProxyStats: () => Record<string, TrafficStats>;
|
|
115
|
+
|
|
116
|
+
/** Returns formatted statistics for hostnames per proxy channel */
|
|
117
|
+
getHostStats: () => Record<string, Record<string, TrafficStats>>;
|
|
110
118
|
}
|
|
111
119
|
|
|
112
120
|
/**
|
|
@@ -178,10 +178,11 @@ export async function startProxyServer({
|
|
|
178
178
|
ip2LocationKey = null,
|
|
179
179
|
debug = false,
|
|
180
180
|
proxy_stats = true,
|
|
181
|
+
host_stats = true,
|
|
181
182
|
}) {
|
|
182
183
|
// 1. Matchers
|
|
183
184
|
const matchers = {
|
|
184
|
-
noProxy: createHostMatcher([...NO_PROXY_HOSTS, "brave.com", "gvt1.com"]),
|
|
185
|
+
noProxy: createHostMatcher([...NO_PROXY_HOSTS, "%brave.com%", "%gvt1.com%"]),
|
|
185
186
|
proxy1: createHostMatcher([...PROXY_1_HOSTS, "proxy.multilogin.com", "multilogin.com"]),
|
|
186
187
|
proxy2: createHostMatcher([...PROXY_2_HOSTS, "proxy.multilogin.com", "multilogin.com"]),
|
|
187
188
|
};
|
|
@@ -227,7 +228,16 @@ export async function startProxyServer({
|
|
|
227
228
|
PROXY_1: { request: 0, Tx: 0, Rx: 0 },
|
|
228
229
|
PROXY_2: { request: 0, Tx: 0, Rx: 0 },
|
|
229
230
|
};
|
|
230
|
-
|
|
231
|
+
// hostStatsMap is now categorized by proxy type
|
|
232
|
+
const hostStatsMap = {
|
|
233
|
+
DEFAULT_PROXY: {},
|
|
234
|
+
NO_PROXY: {},
|
|
235
|
+
PROXY_1: {},
|
|
236
|
+
PROXY_2: {},
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
const connectionMap = {}; // Maps connectionId -> { type: "..." }
|
|
240
|
+
let serverRunning = false;
|
|
231
241
|
|
|
232
242
|
// 6. Server
|
|
233
243
|
const server = new ProxyChain.Server({
|
|
@@ -235,60 +245,88 @@ export async function startProxyServer({
|
|
|
235
245
|
host: "127.0.0.1",
|
|
236
246
|
verbose: debug,
|
|
237
247
|
prepareRequestFunction: ({ hostname, connectionId }) => {
|
|
238
|
-
|
|
248
|
+
let proxyType = "DEFAULT_PROXY";
|
|
249
|
+
let upstreamUrl = upstreamProxies.default;
|
|
250
|
+
let isCustomResponse = false;
|
|
251
|
+
let customResponseData = null;
|
|
252
|
+
|
|
253
|
+
// Logic to determine Proxy Type
|
|
239
254
|
if (matchers.noProxy(hostname)) {
|
|
240
|
-
|
|
241
|
-
|
|
255
|
+
// A. Direct
|
|
256
|
+
proxyType = "NO_PROXY";
|
|
257
|
+
upstreamUrl = null;
|
|
258
|
+
} else if (matchers.proxy1(hostname) && upstreamProxies.p1) {
|
|
259
|
+
// C1. Proxy 1
|
|
260
|
+
proxyType = "PROXY_1";
|
|
261
|
+
upstreamUrl = upstreamProxies.p1;
|
|
262
|
+
} else if (matchers.proxy2(hostname) && upstreamProxies.p2) {
|
|
263
|
+
// C2. Proxy 2
|
|
264
|
+
proxyType = "PROXY_2";
|
|
265
|
+
upstreamUrl = upstreamProxies.p2;
|
|
242
266
|
}
|
|
243
267
|
|
|
244
|
-
// B. IP Check Interception (
|
|
268
|
+
// B. IP Check Interception (Overrules standard routing for specific domain)
|
|
245
269
|
if (hostname === "ip.bablosoft.com") {
|
|
270
|
+
isCustomResponse = true;
|
|
271
|
+
// Inherit the proxyType determined above to fetch the correct IP details
|
|
272
|
+
// (e.g. if it matched PROXY_1 matchers, we show PROXY_1 IP)
|
|
246
273
|
let displayedIP;
|
|
247
|
-
|
|
248
|
-
if (
|
|
249
|
-
else if (
|
|
250
|
-
else displayedIP = defaultDetails?.ip;
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
body: displayedIP || "Unknown IP",
|
|
257
|
-
}),
|
|
274
|
+
if (proxyType === "PROXY_1") displayedIP = p1Details?.ip;
|
|
275
|
+
else if (proxyType === "PROXY_2") displayedIP = p2Details?.ip;
|
|
276
|
+
else if (proxyType === "NO_PROXY") displayedIP = "127.0.0.1";
|
|
277
|
+
else displayedIP = defaultDetails?.ip;
|
|
278
|
+
|
|
279
|
+
customResponseData = {
|
|
280
|
+
statusCode: 200,
|
|
281
|
+
headers: { "Content-Type": "text/plain", Connection: "close" },
|
|
282
|
+
body: displayedIP || "Unknown IP",
|
|
258
283
|
};
|
|
259
284
|
}
|
|
260
285
|
|
|
261
|
-
//
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
286
|
+
// Record Stats
|
|
287
|
+
connectionMap[connectionId] = { type: proxyType, hostname: hostname };
|
|
288
|
+
if (host_stats && hostname) {
|
|
289
|
+
// Ensure the type exists in map (it should, but safety first)
|
|
290
|
+
if (!hostStatsMap[proxyType]) hostStatsMap[proxyType] = {};
|
|
291
|
+
if (!hostStatsMap[proxyType][hostname]) {
|
|
292
|
+
hostStatsMap[proxyType][hostname] = { req: 0, Tx: 0, Rx: 0 };
|
|
293
|
+
}
|
|
294
|
+
hostStatsMap[proxyType][hostname].req++;
|
|
265
295
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
296
|
+
|
|
297
|
+
// Return Decision
|
|
298
|
+
if (isCustomResponse) {
|
|
299
|
+
return { customResponseFunction: () => customResponseData };
|
|
269
300
|
}
|
|
270
301
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
302
|
+
return {
|
|
303
|
+
upstreamProxyUrl: upstreamUrl,
|
|
304
|
+
requestAuthentication: false, // Auto-handle upstream auth via URL
|
|
305
|
+
};
|
|
274
306
|
},
|
|
275
307
|
});
|
|
276
308
|
|
|
277
309
|
server.on("connectionClosed", ({ connectionId, stats: connStats }) => {
|
|
278
310
|
const connectionInfo = connectionMap[connectionId];
|
|
279
|
-
if (connectionInfo
|
|
280
|
-
const { type } = connectionInfo;
|
|
281
|
-
if (stats[type]) {
|
|
311
|
+
if (connectionInfo) {
|
|
312
|
+
const { type, hostname } = connectionInfo;
|
|
313
|
+
if (proxy_stats && stats[type]) {
|
|
282
314
|
stats[type].request++;
|
|
283
315
|
stats[type].Tx += connStats.srcTxBytes;
|
|
284
316
|
stats[type].Rx += connStats.srcRxBytes;
|
|
285
317
|
}
|
|
318
|
+
// Update host stats with Tx/Rx on connection close
|
|
319
|
+
if (host_stats && hostname && hostStatsMap[type] && hostStatsMap[type][hostname]) {
|
|
320
|
+
hostStatsMap[type][hostname].Tx += connStats.srcTxBytes;
|
|
321
|
+
hostStatsMap[type][hostname].Rx += connStats.srcRxBytes;
|
|
322
|
+
}
|
|
286
323
|
}
|
|
287
324
|
delete connectionMap[connectionId];
|
|
288
325
|
});
|
|
289
326
|
|
|
290
327
|
try {
|
|
291
328
|
await server.listen();
|
|
329
|
+
serverRunning = true;
|
|
292
330
|
console.log(`✅ Local Proxy Started: http://127.0.0.1:${selectedPort}`);
|
|
293
331
|
} catch (err) {
|
|
294
332
|
console.error("❌ Failed to start proxy server:", err);
|
|
@@ -297,6 +335,41 @@ export async function startProxyServer({
|
|
|
297
335
|
|
|
298
336
|
const formatBytes = (bytes) => (bytes / 1024 / 1024).toFixed(2);
|
|
299
337
|
|
|
338
|
+
const getProxyStatsFormatted = () => {
|
|
339
|
+
const formatted = {};
|
|
340
|
+
for (const [key, val] of Object.entries(stats)) {
|
|
341
|
+
formatted[key] = {
|
|
342
|
+
req: val.request,
|
|
343
|
+
Tx: formatBytes(val.Tx) + " MB",
|
|
344
|
+
Rx: formatBytes(val.Rx) + " MB",
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
return formatted;
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
const getHostStatsFormatted = () => {
|
|
351
|
+
const result = {};
|
|
352
|
+
// Iterate over each proxy category
|
|
353
|
+
for (const [type, hosts] of Object.entries(hostStatsMap)) {
|
|
354
|
+
const sortedHosts = Object.entries(hosts)
|
|
355
|
+
.sort((a, b) => b[1].req - a[1].req) // Sort by request count descending
|
|
356
|
+
.reduce((acc, [host, hostData]) => {
|
|
357
|
+
acc[host] = {
|
|
358
|
+
req: hostData.req,
|
|
359
|
+
Tx: formatBytes(hostData.Tx) + " MB",
|
|
360
|
+
Rx: formatBytes(hostData.Rx) + " MB",
|
|
361
|
+
};
|
|
362
|
+
return acc;
|
|
363
|
+
}, {});
|
|
364
|
+
|
|
365
|
+
// Only include categories that have traffic
|
|
366
|
+
if (Object.keys(sortedHosts).length > 0) {
|
|
367
|
+
result[type] = sortedHosts;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
return result;
|
|
371
|
+
};
|
|
372
|
+
|
|
300
373
|
// 7. Return Controller
|
|
301
374
|
return {
|
|
302
375
|
port: selectedPort,
|
|
@@ -318,20 +391,24 @@ export async function startProxyServer({
|
|
|
318
391
|
PROXY_1_IP: p1Details?.ip || null,
|
|
319
392
|
PROXY_2_IP: p2Details?.ip || null,
|
|
320
393
|
|
|
394
|
+
isServerRunning: () => serverRunning,
|
|
395
|
+
|
|
321
396
|
closeServer: async () => {
|
|
322
397
|
await server.close(true);
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
Rx: formatBytes(val.Rx) + " MB",
|
|
332
|
-
};
|
|
398
|
+
serverRunning = false;
|
|
399
|
+
|
|
400
|
+
// Auto console.log stats on close
|
|
401
|
+
if (proxy_stats) {
|
|
402
|
+
console.log("📊 Proxy Stats:", getProxyStatsFormatted());
|
|
403
|
+
}
|
|
404
|
+
if (host_stats) {
|
|
405
|
+
console.log("🌐 Host Stats:", getHostStatsFormatted());
|
|
333
406
|
}
|
|
334
|
-
|
|
407
|
+
|
|
408
|
+
console.log("🔒 Proxy server closed.");
|
|
335
409
|
},
|
|
410
|
+
|
|
411
|
+
getProxyStats: getProxyStatsFormatted,
|
|
412
|
+
getHostStats: getHostStatsFormatted,
|
|
336
413
|
};
|
|
337
414
|
}
|