| import { ProxyAgent, fetch as undiciFetch } from "undici"; |
|
|
| const DEFAULT_TEST_URL = "https://google.com/"; |
| const DEFAULT_TIMEOUT_MS = 8000; |
|
|
| function getErrorMessage(err) { |
| if (!err) return "Unknown error"; |
| const base = err?.message || String(err); |
| const causeCode = err?.cause?.code || err?.code; |
| const causeMessage = err?.cause?.message; |
|
|
| if (causeMessage && causeMessage !== base) { |
| return causeCode ? `${base}: ${causeMessage} (${causeCode})` : `${base}: ${causeMessage}`; |
| } |
|
|
| if (causeCode && !base.includes(causeCode)) { |
| return `${base} (${causeCode})`; |
| } |
|
|
| return base; |
| } |
|
|
| function normalizeString(value) { |
| if (value === undefined || value === null) return ""; |
| return String(value).trim(); |
| } |
|
|
| export async function testProxyUrl({ proxyUrl, testUrl, timeoutMs } = {}) { |
| const normalizedProxyUrl = normalizeString(proxyUrl); |
| if (!normalizedProxyUrl) { |
| return { ok: false, status: 400, error: "proxyUrl is required" }; |
| } |
|
|
| const normalizedTestUrl = normalizeString(testUrl) || DEFAULT_TEST_URL; |
| const timeoutMsRaw = Number(timeoutMs); |
| const normalizedTimeoutMs = |
| Number.isFinite(timeoutMsRaw) && timeoutMsRaw > 0 |
| ? Math.min(timeoutMsRaw, 30000) |
| : DEFAULT_TIMEOUT_MS; |
|
|
| let dispatcher; |
|
|
| try { |
| try { |
| dispatcher = new ProxyAgent({ uri: normalizedProxyUrl }); |
| } catch (err) { |
| return { |
| ok: false, |
| status: 400, |
| error: `Invalid proxy URL: ${err?.message || String(err)}`, |
| }; |
| } |
|
|
| const controller = new AbortController(); |
| const startedAt = Date.now(); |
| const timer = setTimeout(() => controller.abort(), normalizedTimeoutMs); |
|
|
| try { |
| const res = await undiciFetch(normalizedTestUrl, { |
| method: "HEAD", |
| dispatcher, |
| signal: controller.signal, |
| headers: { |
| "User-Agent": "9Router", |
| }, |
| }); |
|
|
| return { |
| ok: res.ok, |
| status: res.status, |
| statusText: res.statusText, |
| url: normalizedTestUrl, |
| elapsedMs: Date.now() - startedAt, |
| }; |
| } catch (err) { |
| const message = |
| err?.name === "AbortError" |
| ? "Proxy test timed out" |
| : getErrorMessage(err); |
| return { ok: false, status: 500, error: message }; |
| } finally { |
| clearTimeout(timer); |
| } |
| } finally { |
| try { |
| await dispatcher?.close?.(); |
| } catch { |
| |
| } |
| } |
| } |
|
|