项目里要访问一个只能通过 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?),以及有没有现成的社区实现可以复用,而不是自己糊一个。

做过的尝试

  1. setGlobalDispatcher(new ProxyAgent('socks5://127.0.0.1:1080')) —— undici 直接拒绝这个协议,ProxyAgent 只实现了 HTTP CONNECT 隧道,不解析 socks5。
  2. HTTPS_PROXY / HTTP_PROXY 环境变量设成 socks5://… —— undici 会读环境变量生成 dispatcher,但同样只认 http/https 协议。
  3. global-agent 的 bootstrap() —— 它替换的是 http.globalAgent / https.globalAgent,而 fetch 走的是 undici 的全局 dispatcher,两者完全不是一条路。
  4. socks-proxy-agent 创建 agent 后传给 https.request —— 这条是通的,但换到 fetch 上没有任何入口可以注入 agent(fetch 的第二个参数只认 dispatcher,不认 agent)。
  5. 试过 undici.Agent 的 connect 选项自定义 socket,但对「先建立隧道再发请求」这一步没有现成的钩子。

期望获得的结果

  • 一个业务代码不用改的方案:仍然 await fetch(url),只是底层出口换成 SOCKS5;
  • 如果确实做不到,希望明确「必须自己写 Dispatcher」这个结论,并指明实现它需要覆盖哪些接口、有没有已经维护得不错的第三方实现可以直接用。

实际得到的结果

现在只能把用到这个接口的那部分代码改成 axios + socks-proxy-agent,同一个项目里出现两套 HTTP 栈,错误处理和超时语义都要重写一遍。想先确认这是不是唯一的路。