
在NodeJS中设置代理
最近一个项目需要让 NodeJS 程序走代理,本来我以为只是简单的配置一下就行,没想到颇费了一番功夫,
NodeJS 默认不读取系统代理,又因为 NodeJS 发送网络请求的方法有多种,所以设置代理的方法也各不相同,在这里总结一下。
前提
我们先假设代理服务器的地址来自环境变量,这也是很多 web app 的标准做法。为了方便起见,就把它们叫做 HTTP_PROXY
和 HTTPS_PROXY
。
Fetch
最近的 NodeJS 支持 Fetch 接口了,它尽量和浏览器里的 Fetch保持一致,但如果真一致的话就没法设置代理了。幸好 NodeJS 开了个口子,添加了一个特有的参数dispatcher
,用来修改网络请求的参数,包括代理服务器。
我们可以借助 undici
包来设置dispatcher
:
npm install undici
设置 fetch 代理
用 ProxyAgent 来设置:
import { ProxyAgent } from "undici";
const proxyUrl = process.env.HTTPS_PROXY || process.env.HTTP_PROXY;
if (proxyUrl) {
await fetch(url, { dispatcher: new ProxyAgent(proxyUrl) });
}
全局设置 fetch 代理
setGlobalDispatcher
用来对整个 nodejs 程序中的 fetch 设置代理:
import { setGlobalDispatcher, ProxyAgent } from "undici";
const proxyUrl = process.env.HTTPS_PROXY || process.env.HTTP_PROXY;
if (proxyUrl) {
setGlobalDispatcher(new ProxyAgent(proxyUrl));
}
如果想取消代理,可以设置空的 Agent:
import { Agent, setGlobalDispatcher } from "undici";
setGlobalDispatcher(new Agent());
原理
setGlobalDispatcher
其实利用了 nodejs 的一个隐秘入口。NodeJS 的全局对象有一个 Symbol 设置全局的 dispatcher:
global[Symbol.for("undici.globalDispatcher.1")] = yourDispatcher;
这似乎没有写在 NodeJS 文档里(反正我没找到),想要围观详情的请移步:https://github.com/nodejs/node/issues/43187
除了设置代理,dispatcher 还能干什么
如果你导入 Agent
而不是 ProxyAgent
,那么还可以设置 keepAlive, timeout 等网络参数:
import { Agent } from "undici";
const res = await fetch("https://example.com", {
dispatcher: new Agent({
keepAliveTimeout: 10,
keepAliveMaxTimeout: 10,
}),
});
const json = await res.json();
console.log(json);
详情可以查看 undici 的文档。
NodeJS 原生模块 http 和 https
上面也提到了,NodeJS 不自带设置代理的方法,需要利用 http.Agent
和 https.Agent
动手开发。目前比较流行的软件包分别是 http-proxy-agent
和 https-proxy-agent
。
这是 https-proxy-agent 的一个例子:
import https from "https";
import { HttpsProxyAgent } from "https-proxy-agent";
const proxyUrl = process.env.HTTPS_PROXY || process.env.HTTP_PROXY;
if (proxyUrl) {
const agent = new HttpsProxyAgent(proxyUrl);
https.get(
{
host: "example.com",
path: "/",
agent,
},
(res) => {
// 处理响应
}
);
}
Axios
Axios 几乎算是 JavaScript 的行业标准了。它设置代理的方式有多种(优先级由低到高):
-
环境变量:Axios 本身就会读取环境变量
HTTP_PROXY
和HTTPS_PROXY
-
proxy
参数:import axios from "axios"; axios.get("https://example.com", { proxy: { host: "127.0.0.1", port: 7890, }, });
-
httpAgent
和httpsAgent
参数:你可以传入 NodeJS 自带的http.Agent
和https.Agent
来控制网络参数。我们这里用上一章提到的
http-proxy-agent
和https-proxy-agent
:import axios from "axios"; import { HttpsProxyAgent } from "https-proxy-agent"; import { HttpProxyAgent } from "http-proxy-agent"; const proxyUrl = process.env.HTTPS_PROXY || process.env.HTTP_PROXY; if (proxyUrl) { const httpsAgent = new HttpsProxyAgent(proxyUrl); const httpAgent = new HttpProxyAgent(proxyUrl); const instance = axios.create({ httpAgent, httpsAgent, }); }
具体可以参考 Axios 文档。
HTTPS 连接的限制
我发现如果用前两种方法设置 HTTP 代理,axios 无法建立 HTTPS 连接。原因是 axios 不会发送 CONNECT 方法,因此没法建立 TLS 连接(不了解 CONNECT 方法的可以看我写的这篇文章)。