arn-browser 0.1.30 → 0.1.31
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
|
@@ -208,14 +208,7 @@ export async function startProxyServer({
|
|
|
208
208
|
proxy2: createHostMatcher([...PROXY_2_HOSTS, "proxy.multilogin.com", "multilogin.com"]),
|
|
209
209
|
};
|
|
210
210
|
|
|
211
|
-
// 2.
|
|
212
|
-
const selectedPort = await findAvailablePort(50001, 50010);
|
|
213
|
-
if (!selectedPort) {
|
|
214
|
-
console.error("░░ Critical Error: No available ports.");
|
|
215
|
-
return null;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
// 3. Build URLs
|
|
211
|
+
// 2. Build URLs
|
|
219
212
|
const buildURL = (data) => {
|
|
220
213
|
if (!data) return null; // Returns null if no config
|
|
221
214
|
const { type = "http", host, port, user, pass } = data;
|
|
@@ -230,7 +223,7 @@ export async function startProxyServer({
|
|
|
230
223
|
p2: buildURL(PROXY_2_DATA),
|
|
231
224
|
};
|
|
232
225
|
|
|
233
|
-
//
|
|
226
|
+
// 3. Fetch Details (Simplified Logic)
|
|
234
227
|
// We pass the URL (or null). The function handles the "Local" logic.
|
|
235
228
|
const [defaultDetails, p1Details, p2Details] = await Promise.all([
|
|
236
229
|
fetchProxyDetails(upstreamProxies.default, ip2LocationKey, retryDelayMs),
|
|
@@ -243,7 +236,7 @@ export async function startProxyServer({
|
|
|
243
236
|
if (upstreamProxies.p1 && !p1Details) { console.warn("░░ Warning: PROXY_1 configured but unreachable."); return null; }
|
|
244
237
|
if (upstreamProxies.p2 && !p2Details) { console.warn("░░ Warning: PROXY_2 configured but unreachable."); return null; }
|
|
245
238
|
|
|
246
|
-
//
|
|
239
|
+
// 4. Stats
|
|
247
240
|
const stats = {
|
|
248
241
|
DEFAULT_PROXY: { request: 0, srcTx: 0, srcRx: 0, trgTx: 0, trgRx: 0 },
|
|
249
242
|
NO_PROXY: { request: 0, srcTx: 0, srcRx: 0, trgTx: 0, trgRx: 0 },
|
|
@@ -261,72 +254,105 @@ export async function startProxyServer({
|
|
|
261
254
|
const connectionMap = {}; // Maps connectionId -> { type: "..." }
|
|
262
255
|
let serverRunning = false;
|
|
263
256
|
|
|
264
|
-
//
|
|
265
|
-
const
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
verbose: debug,
|
|
269
|
-
prepareRequestFunction: ({ hostname, connectionId }) => {
|
|
270
|
-
let proxyType = "DEFAULT_PROXY";
|
|
271
|
-
let upstreamUrl = upstreamProxies.default;
|
|
272
|
-
let isCustomResponse = false;
|
|
273
|
-
let customResponseData = null;
|
|
274
|
-
|
|
275
|
-
// Logic to determine Proxy Type
|
|
276
|
-
if (matchers.noProxy(hostname)) {
|
|
277
|
-
// A. Direct
|
|
278
|
-
proxyType = "NO_PROXY";
|
|
279
|
-
upstreamUrl = null;
|
|
280
|
-
} else if (matchers.proxy1(hostname) && upstreamProxies.p1) {
|
|
281
|
-
// C1. Proxy 1
|
|
282
|
-
proxyType = "PROXY_1";
|
|
283
|
-
upstreamUrl = upstreamProxies.p1;
|
|
284
|
-
} else if (matchers.proxy2(hostname) && upstreamProxies.p2) {
|
|
285
|
-
// C2. Proxy 2
|
|
286
|
-
proxyType = "PROXY_2";
|
|
287
|
-
upstreamUrl = upstreamProxies.p2;
|
|
288
|
-
}
|
|
257
|
+
// 5. Create server and bind with retry (handles race conditions between concurrent processes)
|
|
258
|
+
const PORT_RANGE_START = 50001;
|
|
259
|
+
const PORT_RANGE_END = 50200;
|
|
260
|
+
const MAX_BIND_RETRIES = 10;
|
|
289
261
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
else displayedIP = defaultDetails?.ip;
|
|
300
|
-
|
|
301
|
-
customResponseData = {
|
|
302
|
-
statusCode: 200,
|
|
303
|
-
headers: { "Content-Type": "text/plain", Connection: "close" },
|
|
304
|
-
body: displayedIP || "Unknown IP",
|
|
305
|
-
};
|
|
306
|
-
}
|
|
262
|
+
let selectedPort = null;
|
|
263
|
+
let server = null;
|
|
264
|
+
|
|
265
|
+
for (let attempt = 1; attempt <= MAX_BIND_RETRIES; attempt++) {
|
|
266
|
+
const candidatePort = await findAvailablePort(PORT_RANGE_START, PORT_RANGE_END);
|
|
267
|
+
if (!candidatePort) {
|
|
268
|
+
console.error("░░ Critical Error: No available ports in range " + PORT_RANGE_START + "-" + PORT_RANGE_END);
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
307
271
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
272
|
+
server = new ProxyChain.Server({
|
|
273
|
+
port: candidatePort,
|
|
274
|
+
host: "127.0.0.1",
|
|
275
|
+
verbose: debug,
|
|
276
|
+
prepareRequestFunction: ({ hostname, connectionId }) => {
|
|
277
|
+
let proxyType = "DEFAULT_PROXY";
|
|
278
|
+
let upstreamUrl = upstreamProxies.default;
|
|
279
|
+
let isCustomResponse = false;
|
|
280
|
+
let customResponseData = null;
|
|
281
|
+
|
|
282
|
+
// Logic to determine Proxy Type
|
|
283
|
+
if (matchers.noProxy(hostname)) {
|
|
284
|
+
// A. Direct
|
|
285
|
+
proxyType = "NO_PROXY";
|
|
286
|
+
upstreamUrl = null;
|
|
287
|
+
} else if (matchers.proxy1(hostname) && upstreamProxies.p1) {
|
|
288
|
+
// C1. Proxy 1
|
|
289
|
+
proxyType = "PROXY_1";
|
|
290
|
+
upstreamUrl = upstreamProxies.p1;
|
|
291
|
+
} else if (matchers.proxy2(hostname) && upstreamProxies.p2) {
|
|
292
|
+
// C2. Proxy 2
|
|
293
|
+
proxyType = "PROXY_2";
|
|
294
|
+
upstreamUrl = upstreamProxies.p2;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// B. IP Check Interception (Overrules standard routing for specific domain)
|
|
298
|
+
if (hostname === "ip.bablosoft.com") {
|
|
299
|
+
isCustomResponse = true;
|
|
300
|
+
let displayedIP;
|
|
301
|
+
if (proxyType === "PROXY_1") displayedIP = p1Details?.ip;
|
|
302
|
+
else if (proxyType === "PROXY_2") displayedIP = p2Details?.ip;
|
|
303
|
+
else if (proxyType === "NO_PROXY") displayedIP = "127.0.0.1";
|
|
304
|
+
else displayedIP = defaultDetails?.ip;
|
|
305
|
+
|
|
306
|
+
customResponseData = {
|
|
307
|
+
statusCode: 200,
|
|
308
|
+
headers: { "Content-Type": "text/plain", Connection: "close" },
|
|
309
|
+
body: displayedIP || "Unknown IP",
|
|
310
|
+
};
|
|
315
311
|
}
|
|
316
|
-
hostStatsMap[proxyType][hostname].req++;
|
|
317
|
-
}
|
|
318
312
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
313
|
+
// Record Stats
|
|
314
|
+
connectionMap[connectionId] = { type: proxyType, hostname: hostname };
|
|
315
|
+
if (host_stats && hostname) {
|
|
316
|
+
if (!hostStatsMap[proxyType]) hostStatsMap[proxyType] = {};
|
|
317
|
+
if (!hostStatsMap[proxyType][hostname]) {
|
|
318
|
+
hostStatsMap[proxyType][hostname] = { req: 0, srcTx: 0, srcRx: 0, trgTx: 0, trgRx: 0 };
|
|
319
|
+
}
|
|
320
|
+
hostStatsMap[proxyType][hostname].req++;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Return Decision
|
|
324
|
+
if (isCustomResponse) {
|
|
325
|
+
return { customResponseFunction: () => customResponseData };
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return {
|
|
329
|
+
upstreamProxyUrl: upstreamUrl,
|
|
330
|
+
requestAuthentication: false,
|
|
331
|
+
};
|
|
332
|
+
},
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
try {
|
|
336
|
+
await server.listen();
|
|
337
|
+
selectedPort = candidatePort;
|
|
338
|
+
serverRunning = true;
|
|
339
|
+
console.log(`░░ Local Proxy Started: http://127.0.0.1:${selectedPort}`);
|
|
340
|
+
break; // Successfully bound — exit retry loop
|
|
341
|
+
} catch (err) {
|
|
342
|
+
if (err.code === "EADDRINUSE" && attempt < MAX_BIND_RETRIES) {
|
|
343
|
+
console.warn(`░░ Port ${candidatePort} taken (race), retrying... (${attempt}/${MAX_BIND_RETRIES})`);
|
|
344
|
+
await sleep(50 + Math.random() * 100); // Small random delay to de-sync concurrent processes
|
|
345
|
+
continue;
|
|
322
346
|
}
|
|
347
|
+
console.error("░░ Failed to start proxy server:", err);
|
|
348
|
+
return null;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
323
351
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
},
|
|
329
|
-
});
|
|
352
|
+
if (!selectedPort || !server) {
|
|
353
|
+
console.error("░░ Critical Error: Could not bind to any port after " + MAX_BIND_RETRIES + " attempts.");
|
|
354
|
+
return null;
|
|
355
|
+
}
|
|
330
356
|
|
|
331
357
|
server.on("connectionClosed", ({ connectionId, stats: connStats }) => {
|
|
332
358
|
const connectionInfo = connectionMap[connectionId];
|
|
@@ -355,15 +381,6 @@ export async function startProxyServer({
|
|
|
355
381
|
delete connectionMap[connectionId];
|
|
356
382
|
});
|
|
357
383
|
|
|
358
|
-
try {
|
|
359
|
-
await server.listen();
|
|
360
|
-
serverRunning = true;
|
|
361
|
-
console.log(`░░ Local Proxy Started: http://127.0.0.1:${selectedPort}`);
|
|
362
|
-
} catch (err) {
|
|
363
|
-
console.error("░░ Failed to start proxy server:", err);
|
|
364
|
-
return null;
|
|
365
|
-
}
|
|
366
|
-
|
|
367
384
|
const formatBytes = (bytes) => {
|
|
368
385
|
if (bytes < 1024) return bytes + " B";
|
|
369
386
|
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(2) + " KB";
|
|
@@ -142,6 +142,9 @@ function sanitizeResponseHeaders(headers, logger, url) {
|
|
|
142
142
|
}
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
+
// Always ensure CORS header is present, even if server never sent it
|
|
146
|
+
cleaned["access-control-allow-origin"] = "*";
|
|
147
|
+
|
|
145
148
|
if (logger && stripped.length) console.log(`[stripGot] Response stripped ${stripped.length} headers: [${stripped.join(", ")}] → ${url}`);
|
|
146
149
|
return cleaned;
|
|
147
150
|
}
|