Merge pull request #1471 from 810senpai114514/feat/no-proxy

feat: 支持 NO_PROXY 环境变量与 Linux 环境变量代理回退
This commit is contained in:
musi 2026-07-01 19:52:31 +08:00 committed by GitHub
commit 5ad41aba2b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -47,7 +47,11 @@ export async function getSystemProxyUrlForProtocol(protocol: "http" | "https" =
async function systemProxyUrlForRequest(url: URL): Promise<string | undefined> {
const cache = await readSystemProxy();
const server = proxyServerForRequest(cache.upstreamProxy, url.protocol === "https:" ? "https" : "http");
return server ? formatProxyUrl(server) : undefined;
if (server) return formatProxyUrl(server);
const envProxy = process.env.HTTPS_PROXY || process.env.https_proxy ||
process.env.HTTP_PROXY || process.env.http_proxy;
return envProxy ? envProxy.trim() : undefined;
}
async function readSystemProxy(): Promise<SystemProxyCache> {
@ -187,12 +191,65 @@ function isHttpUrl(url: URL): boolean {
function shouldBypassProxy(url: URL): boolean {
const hostname = normalizeHostname(url.hostname);
return hostname === "localhost" ||
if (hostname === "localhost" ||
hostname === "127.0.0.1" ||
hostname.startsWith("127.") ||
hostname === "0.0.0.0" ||
hostname === "::1" ||
hostname === "0:0:0:0:0:0:0:1";
hostname === "0:0:0:0:0:0:0:1") {
return true;
}
const noProxy = process.env.NO_PROXY || process.env.no_proxy;
if (!noProxy) return false;
const patterns = noProxy.split(",").map((s) => s.trim()).filter(Boolean);
for (const pattern of patterns) {
if (pattern === "*") return true;
if (pattern.startsWith(".")) {
if (hostname.endsWith(pattern.toLowerCase()) || hostname === pattern.slice(1).toLowerCase()) return true;
continue;
}
if (pattern.includes("/") && isPlainIp(hostname)) {
if (isInCidr(hostname, pattern)) return true;
continue;
}
if (hostname === pattern.toLowerCase()) return true;
}
return false;
}
function isPlainIp(s: string): boolean {
return /^\d+\.\d+\.\d+\.\d+$/.test(s);
}
function isInCidr(ip: string, cidr: string): boolean {
const [network, prefixStr] = cidr.split("/");
const prefix = parseInt(prefixStr, 10);
if (isNaN(prefix) || prefix < 0 || prefix > 32) return false;
const ipInt = ipToInt(ip);
const netInt = ipToInt(network);
if (ipInt === null || netInt === null) return false;
const mask = prefix === 0 ? 0 : (~0 << (32 - prefix)) >>> 0;
return (ipInt & mask) === (netInt & mask);
}
function ipToInt(ip: string): number | null {
const parts = ip.split(".");
if (parts.length !== 4) return null;
let result = 0;
for (const part of parts) {
const num = parseInt(part, 10);
if (isNaN(num) || num < 0 || num > 255) return null;
result = (result << 8) | num;
}
return result >>> 0;
}
function normalizeHostname(hostname: string): string {