项目里要访问一个只能通过 SOCKS5 出去的接口,Node 端用的是 18+ 自带的全局 fetch(undici 实现)。卡在代理上:undici 提供的是 ProxyAgent,只认 HTTP/HTTPS 代理,没有 SOCKS5 支持。
试了几种把全局出口改成 SOCKS5 的写法,都没能让内置 fetch 走代理:
// 1) 直接把 ProxyAgent 指到 socks5 import { setGlobalDispatcher, ProxyAgent } from 'undici'; setGlobalDispatcher(new ProxyAgent('socks5://127.0.0.1:1080')); // → 协议不被识别 // 2) 环境变量 // HTTPS_PROXY=socks5://127.0.0.1:1080 // 3) 老的 agent 方案 import { SocksProxyAgent } from 'socks-proxy-agent'; const agent = new SocksProxyAgent('socks5://127.0.0.1:1080'); // 这个 agent 只认 https.request,fetch 用的是全局 dispatcher,两者不是一套global-agent 的 bootstrap() 也一样:它改的是 http/https 模块的 agent,覆盖不到 undici。
想确认的是:有没有办法让内置 fetch 走 SOCKS5,而不用把业务代码整体换成 axios + agent,或者退回 node-fetch?
如果结论是「必须自己实现一个 Dispatcher」,那想了解需要覆盖哪些方法(dispatch、connect?),以及有没有现成的社区实现可以复用,而不是自己糊一个。
做过的尝试
setGlobalDispatcher(new ProxyAgent('socks5://127.0.0.1:1080'))—— undici 直接拒绝这个协议,ProxyAgent只实现了 HTTP CONNECT 隧道,不解析 socks5。HTTPS_PROXY/HTTP_PROXY环境变量设成socks5://…—— undici 会读环境变量生成 dispatcher,但同样只认 http/https 协议。global-agent的bootstrap()—— 它替换的是http.globalAgent/https.globalAgent,而fetch走的是 undici 的全局 dispatcher,两者完全不是一条路。socks-proxy-agent创建 agent 后传给https.request—— 这条是通的,但换到fetch上没有任何入口可以注入 agent(fetch的第二个参数只认dispatcher,不认agent)。- 试过
undici.Agent的connect选项自定义 socket,但对「先建立隧道再发请求」这一步没有现成的钩子。
期望获得的结果
- 一个业务代码不用改的方案:仍然
await fetch(url),只是底层出口换成 SOCKS5; - 如果确实做不到,希望明确「必须自己写 Dispatcher」这个结论,并指明实现它需要覆盖哪些接口、有没有已经维护得不错的第三方实现可以直接用。
实际得到的结果
现在只能把用到这个接口的那部分代码改成 axios + socks-proxy-agent,同一个项目里出现两套 HTTP 栈,错误处理和超时语义都要重写一遍。想先确认这是不是唯一的路。




