import { core } from "ext:core/mod.js";
const { Buffer } = core.loadExtScript("ext:deno_node/internal/buffer.mjs");
const {
ERR_INVALID_ARG_TYPE,
ERR_PROXY_INVALID_CONFIG,
} = core.loadExtScript("ext:deno_node/internal/errors.ts");
const HTTP_PROXY_KEYS = ["http_proxy", "HTTP_PROXY"];
const HTTPS_PROXY_KEYS = ["https_proxy", "HTTPS_PROXY"];
const NO_PROXY_KEYS = ["no_proxy", "NO_PROXY"];
function isPlainObject(value) {
if (value === null || typeof value !== "object") return false;
if (Array.isArray(value)) return false;
return true;
}
function readEnvKey(env, keys) {
for (let i = 0; i < keys.length; i++) {
const k = keys[i];
if (env[k] !== undefined && env[k] !== "") {
return env[k];
}
}
return undefined;
}
function parseProxyUrl(raw, kind, mode) {
if (raw === undefined || raw === null || raw === "") return null;
if (typeof raw !== "string") {
if (mode === "env") {
throw new TypeError(`Invalid URL: ${raw}`);
}
throw new ERR_PROXY_INVALID_CONFIG(
`Invalid proxy URL for ${kind}: must be a string`,
);
}
if (/[\r\n]/.test(raw)) {
throw new ERR_PROXY_INVALID_CONFIG(`Invalid proxy URL: ${raw}`);
}
let url;
try {
url = new URL(raw);
} catch {
if (mode === "env") {
throw new TypeError(`Invalid URL: ${raw}`);
}
throw new ERR_PROXY_INVALID_CONFIG(`Invalid proxy URL: ${raw}`);
}
if (url.protocol !== "http:" && url.protocol !== "https:") {
if (mode === "env") {
throw new TypeError(`Invalid URL: ${raw}`);
}
throw new ERR_PROXY_INVALID_CONFIG(
`Invalid proxy URL: ${raw} (unsupported scheme ${url.protocol})`,
);
}
const username = url.username ? decodeURIComponent(url.username) : "";
const password = url.password ? decodeURIComponent(url.password) : "";
let hostname = url.hostname;
if (hostname.startsWith("[") && hostname.endsWith("]")) {
hostname = hostname.slice(1, -1);
}
return {
raw,
protocol: url.protocol,
hostname,
port: url.port ? Number(url.port) : (url.protocol === "https:" ? 443 : 80),
username,
password,
auth: username
? "Basic " +
Buffer.from(`${username}:${password}`).toString("base64")
: undefined,
};
}
function parseIPv4(s) {
const m = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(s);
if (!m) return null;
let acc = 0;
for (let i = 1; i <= 4; i++) {
const oct = Number(m[i]);
if (oct > 255) return null;
acc = (acc * 256) + oct;
}
return acc;
}
function parseIPv4Range(s) {
const slash = s.indexOf("/");
if (slash !== -1) {
const ip = parseIPv4(s.slice(0, slash));
if (ip === null) return null;
const bits = Number(s.slice(slash + 1));
if (!Number.isInteger(bits) || bits < 0 || bits > 32) return null;
const mask = bits === 0 ? 0 : (0xffffffff << (32 - bits)) >>> 0;
const lo = ip & mask;
const hi = lo | (~mask >>> 0);
return { lo, hi };
}
const dash = s.indexOf("-");
if (dash !== -1) {
const lo = parseIPv4(s.slice(0, dash));
const hi = parseIPv4(s.slice(dash + 1));
if (lo === null || hi === null || lo > hi) return null;
return { lo, hi };
}
return null;
}
function parseNoProxy(raw) {
if (!raw) return null;
if (typeof raw !== "string") return null;
const trimmed = raw.trim();
if (trimmed === "") return null;
if (trimmed === "*") return { all: true, entries: [] };
const entries = [];
const parts = trimmed.split(",");
for (let i = 0; i < parts.length; i++) {
const part = parts[i].trim();
if (part === "") continue;
let host = part;
let port = null;
if (host.startsWith("[")) {
const bracket = host.indexOf("]");
if (bracket !== -1) {
const rest = host.slice(bracket + 1);
host = host.slice(1, bracket);
if (rest.startsWith(":")) {
port = rest.slice(1);
}
}
} else {
const lastColon = host.lastIndexOf(":");
if (
lastColon !== -1 && host.indexOf(":") === lastColon &&
host.indexOf("/") === -1 && host.indexOf("-") === -1
) {
port = host.slice(lastColon + 1);
host = host.slice(0, lastColon);
}
}
const range = parseIPv4Range(host);
if (range) {
entries.push({
host: null,
ipRange: range,
port: port === null ? null : Number(port),
suffixMatch: false,
});
continue;
}
let suffixMatch = false;
if (host.startsWith("*.")) {
suffixMatch = true;
host = host.slice(2);
} else if (host.startsWith(".")) {
suffixMatch = true;
host = host.slice(1);
}
entries.push({
host: host.toLowerCase(),
ipRange: null,
port: port === null ? null : Number(port),
suffixMatch,
});
}
return { all: false, entries };
}
function stripIpv6Brackets(host) {
if (host && host.startsWith("[") && host.endsWith("]")) {
return host.slice(1, -1);
}
return host;
}
function shouldBypassProxy(noProxy, host, port) {
if (!noProxy) return false;
if (noProxy.all) return true;
const normalizedHost = stripIpv6Brackets(String(host || "")).toLowerCase();
const portNum = port == null ? null : Number(port);
const hostAsIp = parseIPv4(normalizedHost);
for (let i = 0; i < noProxy.entries.length; i++) {
const entry = noProxy.entries[i];
if (entry.port !== null && entry.port !== portNum) {
continue;
}
if (entry.ipRange !== null) {
if (
hostAsIp !== null &&
hostAsIp >= entry.ipRange.lo &&
hostAsIp <= entry.ipRange.hi
) {
return true;
}
continue;
}
if (entry.host === "" || entry.host === "*") {
return true;
}
if (entry.suffixMatch) {
if (
normalizedHost === entry.host ||
normalizedHost.endsWith("." + entry.host)
) {
return true;
}
} else {
if (normalizedHost === entry.host) {
return true;
}
if (normalizedHost.endsWith("." + entry.host)) {
return true;
}
}
}
return false;
}
function buildProxyConfig(env, mode) {
const httpRaw = readEnvKey(env, HTTP_PROXY_KEYS);
const httpsRaw = readEnvKey(env, HTTPS_PROXY_KEYS);
const noProxyRaw = readEnvKey(env, NO_PROXY_KEYS);
const http = parseProxyUrl(httpRaw, "http_proxy", mode);
const https = parseProxyUrl(httpsRaw, "https_proxy", mode);
if (http === null && https === null) return null;
return {
http,
https,
noProxy: parseNoProxy(noProxyRaw),
};
}
function selectProxy(config, protocol, host, port) {
if (!config) return null;
if (shouldBypassProxy(config.noProxy, host, port)) return null;
if (protocol === "https:" || protocol === "wss:") {
return config.https || config.http;
}
return config.http;
}
let globalProxyConfig = null;
let initializedFromEnv = false;
function maybeInitFromEnv() {
if (initializedFromEnv) return;
initializedFromEnv = true;
const env = (globalThis.process && globalThis.process.env) || {};
const cliOverride = globalThis[Symbol.for("Deno.internal.useEnvProxy")];
let enabled;
if (cliOverride === true || cliOverride === false) {
enabled = cliOverride;
} else {
const v = env.NODE_USE_ENV_PROXY;
enabled = v === "1" || v === "true";
}
if (!enabled) return;
const cfg = buildProxyConfig(env, "env");
if (cfg) globalProxyConfig = cfg;
}
function getGlobalProxyConfig() {
maybeInitFromEnv();
return globalProxyConfig;
}
function setGlobalProxyFromEnv(input) {
let env;
if (input === undefined) {
env = (globalThis.process && globalThis.process.env) || {};
} else if (!isPlainObject(input)) {
throw new ERR_INVALID_ARG_TYPE(
"proxyEnv",
"Object",
input,
);
} else {
env = input;
}
try {
maybeInitFromEnv();
} catch {
}
initializedFromEnv = true;
const newConfig = buildProxyConfig(env, "strict");
const prev = globalProxyConfig;
globalProxyConfig = newConfig;
let restored = false;
return function restore() {
if (restored) return;
restored = true;
globalProxyConfig = prev;
};
}
function resolveAgentProxyConfig(agent) {
if (agent && agent.__proxyConfig !== undefined) return agent.__proxyConfig;
maybeInitFromEnv();
return globalProxyConfig;
}
function initAgentProxy(agent, options) {
if (options && options.proxyEnv !== undefined && options.proxyEnv !== null) {
if (!isPlainObject(options.proxyEnv)) {
throw new ERR_INVALID_ARG_TYPE(
"options.proxyEnv",
"Object",
options.proxyEnv,
);
}
agent.__proxyConfig = buildProxyConfig(options.proxyEnv, "strict");
}
}
function hasGlobalProxy() {
maybeInitFromEnv();
return globalProxyConfig !== null;
}
function initFromStartupEnv() {
const env = (globalThis.process && globalThis.process.env) || {};
try {
const cfg = buildProxyConfig(env, "env");
if (cfg) globalProxyConfig = cfg;
} catch {
}
}
export {
buildProxyConfig,
getGlobalProxyConfig,
hasGlobalProxy,
initAgentProxy,
initFromStartupEnv,
parseNoProxy,
parseProxyUrl,
resolveAgentProxyConfig,
selectProxy,
setGlobalProxyFromEnv,
shouldBypassProxy,
};
export default {
buildProxyConfig,
getGlobalProxyConfig,
hasGlobalProxy,
initAgentProxy,
initFromStartupEnv,
parseNoProxy,
parseProxyUrl,
resolveAgentProxyConfig,
selectProxy,
setGlobalProxyFromEnv,
shouldBypassProxy,
};