Chain multiple proxies (HTTP/HTTPS/SOCKS4/SOCKS5/SSH) in strict order for Node.js. 多协议链式代理库
npm install multi-protocol-proxy-chainChain multiple proxies in strict order (HTTP/HTTPS/SOCKS4/SOCKS5/SSH) for Node.js. TypeScript-first library to build multi-hop forward proxy chains.
多协议链式代理库(HTTP/HTTPS/SOCKS4/SOCKS5/SSH),用于在 Node.js 中构建严格顺序的多跳前向代理链,提供 TypeScript 类型支持。
- ✅ Strict-order proxy chain: client → local → p0 → p1 → … → target
- ✅ Multi-protocol support: HTTP, HTTPS, SOCKS5, SOCKS4, SSH (raw passthrough)
- ✅ HTTP CONNECT tunneling and plain HTTP proxying
- ✅ TypeScript types and .d.ts declarations
- ✅ Async lifecycle: start() returns the actual bound URL; stopAll() awaits graceful shutdown
- ✅ Port selection helper: auto-find available port from a start value
- ✅ Cross-platform: Windows, macOS, Linux support
- ✅ EventEmitter: Listen to connection errors and lifecycle events
- ✅ Custom logger: Support custom logging functions
- 通过多级上游代理实现分层转发与出站路由
- 将 SOCKS5/SOCKS4 与 HTTP/HTTPS 混合链式连接
- 在受限网络中通过 HTTP CONNECT 建立隧道
- 将浏览器或 CLI 流量统一引导到本地入口后多跳转发
多协议链式代理库,支持严格顺序的代理链转发:
client -> local proxy -> proxies[0] -> proxies[1] -> ... -> target
``bash`
npm install multi-protocol-proxy-chain
- Node.js: >= 14.0.0
- 操作系统: Windows 10+, macOS 10.14+, Linux (所有主流发行版)
- 依赖: ssh2 (用于 SSH 代理支持)
本库完全支持跨平台运行:
- Windows: 使用 netstat -ano 进行端口检测lsof
- macOS: 使用 进行端口检测ss
- Linux: 优先使用 命令,回退到 lsof,最后使用绑定检测
- 其他 Unix-like 系统: 使用通用的绑定检测方法
所有平台都支持自动端口查找和代理链功能。
`ts
import { MultiProtocolProxyChain } from 'multi-protocol-proxy-chain';
const chain = new MultiProtocolProxyChain();
chain.setProxies([
{ protocol: 'http', host: '192.168.101.166', port: 7890 },
{ protocol: 'socks5', host: '128.121.59.167', port: 36168, userId: 'user', password: 'pass' },
]);
// 启动本地入口(例如 socks5)
(async () => {
try {
const url = await chain.start('socks5', 2000);
console.log('本地代理入口:', url);
// 输出: socks5://127.0.0.1:2000 (或自动找到的可用端口)
} catch (err) {
console.error('启动失败:', err);
}
})();
`
`ts
import { MultiProtocolProxyChain } from 'multi-protocol-proxy-chain';
const chain = new MultiProtocolProxyChain({
logger: {
log: (...args) => console.log('[ProxyChain]', ...args),
error: (...args) => console.error('[ProxyChain Error]', ...args),
},
});
// 设置代理链(严格按顺序)
chain.setProxies([
{ protocol: 'http', host: 'proxy1.example.com', port: 8080 },
{ protocol: 'socks5', host: 'proxy2.example.com', port: 1080, userId: 'user', password: 'pass' },
{ protocol: 'https', host: 'proxy3.example.com', port: 443, userId: 'user2', password: 'pass2' },
]);
// 监听错误事件
chain.on('error', (err) => {
console.error('代理链错误:', err);
});
(async () => {
try {
// 启动本地 SOCKS5 代理入口
const url = await chain.start('socks5', 2000, '127.0.0.1');
console.log('✅ 本地代理入口已启动:', url);
console.log('📝 使用示例:');
console.log( curl --socks5-hostname 127.0.0.1:2000 https://httpbin.org/ip);
// 也可以启动多个不同协议的入口
const httpUrl = await chain.start('http', 3128);
console.log('✅ HTTP 代理入口:', httpUrl);
} catch (err) {
console.error('❌ 启动失败:', err);
process.exit(1);
}
})();
// 优雅关闭
process.on('SIGINT', async () => {
console.log('\n收到退出信号,正在关闭代理入口...');
await chain.stopAll();
console.log('✅ 已关闭所有代理入口');
process.exit(0);
});
process.on('SIGTERM', async () => {
await chain.stopAll();
process.exit(0);
});
`
`ts
const chain = new MultiProtocolProxyChain();
chain.setProxies([/ ... /]);
// 启动 SOCKS5 入口(浏览器常用)
const socks5Url = await chain.start('socks5', 2000);
// 启动 HTTP 入口(支持 CONNECT 和普通 HTTP 请求)
const httpUrl = await chain.start('http', 3128);
// 启动 HTTPS 入口(需要 TLS 证书,本库使用自签名证书)
const httpsUrl = await chain.start('https', 8443);
// 启动 SOCKS4 入口
const socks4Url = await chain.start('socks4', 1080);
`
`ts
const chain = new MultiProtocolProxyChain();
try {
// 尝试启动代理
const url = await chain.start('socks5', 2000);
console.log('启动成功:', url);
} catch (err) {
if (err instanceof Error) {
if (err.message.includes('EADDRINUSE')) {
console.error('端口已被占用,尝试其他端口...');
// 可以尝试其他端口或使用 PortOccupiedCheck
} else if (err.message.includes('Unsupported')) {
console.error('不支持的协议');
} else {
console.error('启动失败:', err.message);
}
}
}
`
`ts
const chain = new MultiProtocolProxyChain();
// 监听代理链错误
chain.on('error', (err) => {
console.error('代理链连接错误:', err.message);
// 可以在这里实现重连逻辑
});
chain.setProxies([/ ... /]);
await chain.start('socks5', 2000);
`
#### 构造函数
`ts`
new MultiProtocolProxyChain(options?: {
logger?: {
log: (...args: any[]) => void;
error: (...args: any[]) => void;
};
})
参数:
- options.logger (可选): 自定义日志函数,默认使用 console
示例:
`ts`
const chain = new MultiProtocolProxyChain({
logger: {
log: (...args) => myLogger.info(...args),
error: (...args) => myLogger.error(...args),
},
});
#### setProxies(proxies: ProxySpec[])
设置上游代理链,顺序严格遵循数组顺序。每次调用会替换之前的代理链。
参数:
- proxies: 代理配置数组,按顺序执行
示例:
`ts`
chain.setProxies([
{ protocol: 'http', host: 'proxy1.com', port: 8080 },
{ protocol: 'socks5', host: 'proxy2.com', port: 1080, userId: 'user', password: 'pass' },
]);
#### start(protocol, port, host?): Promise
启动本地代理入口服务器。
参数:
- protocol: 'http' | 'https' | 'socks5' | 'socks4' | 'ssh' - 本地入口协议port
- : number - 起始端口号(如果被占用会自动查找下一个可用端口)host
- : string (可选) - 绑定地址,默认 '127.0.0.1'
返回:
- Promise - 返回实际绑定的代理 URL,例如 'socks5://127.0.0.1:2000'
示例:
`ts
const url = await chain.start('socks5', 2000);
// url: 'socks5://127.0.0.1:2000'
const url2 = await chain.start('http', 3128, '0.0.0.0');
// url2: 'http://0.0.0.0:3128'
`
#### stopAll(): Promise
停止所有已启动的本地代理入口,等待所有服务器优雅关闭。
示例:
`ts`
await chain.stopAll();
console.log('所有代理入口已关闭');
#### 事件
MultiProtocolProxyChain 继承自 EventEmitter,支持以下事件:
- 'error': 当代理链连接出错时触发`
ts`
chain.on('error', (err: Error) => {
console.error('代理链错误:', err);
});
`ts`
type ProxySpec = {
protocol: 'http' | 'https' | 'socks5' | 'socks4' | 'ssh';
host: string; // 代理服务器地址
port: number; // 代理服务器端口
userId?: string; // 认证用户名(可选)
password?: string; // 认证密码(可选)
};
协议说明:
- http: HTTP 代理,支持 CONNECT 隧道和普通 HTTP 请求https
- : HTTPS 代理(TLS 加密连接)socks5
- : SOCKS5 代理,支持用户名/密码认证socks4
- : SOCKS4 代理,支持用户名认证ssh
- : SSH 隧道代理(使用 ssh2 库)
端口占用检测工具类。
#### checkPort(initPort, maxPort?, host?): Promise
检查并返回一个未被占用的端口号。
参数:
- initPort: number - 起始端口号maxPort
- : number (可选) - 最大端口号,默认 65535host
- : string (可选) - 绑定检测的主机地址,默认 '127.0.0.1'
返回:
- Promise - 第一个可用的端口号
抛出:
- 如果从 initPort 到 maxPort 范围内没有可用端口,抛出错误
示例:
`ts
import { PortOccupiedCheck } from 'multi-protocol-proxy-chain';
const checker = new PortOccupiedCheck();
const port = await checker.checkPort(2000, 2100); // 在 2000-2100 范围内查找
console.log('可用端口:', port);
`
`ts
const chain = new MultiProtocolProxyChain();
chain.setProxies([
{ protocol: 'http', host: 'corporate-proxy.com', port: 8080 },
{ protocol: 'socks5', host: 'exit-node.com', port: 1080 },
]);
const url = await chain.start('socks5', 2000);
console.log('在浏览器中设置 SOCKS5 代理:', url);
// 浏览器设置: SOCKS5 127.0.0.1:2000
`
`ts
const chain = new MultiProtocolProxyChain();
chain.setProxies([/ ... /]);
await chain.start('http', 3128);
// 使用 curl
// curl --proxy http://127.0.0.1:3128 https://example.com
// 使用环境变量
// export http_proxy=http://127.0.0.1:3128
// export https_proxy=http://127.0.0.1:3128
`
`ts
import { HttpsProxyAgent } from 'https-proxy-agent';
const chain = new MultiProtocolProxyChain();
chain.setProxies([/ ... /]);
const url = await chain.start('http', 3128);
const agent = new HttpsProxyAgent(url);
// 在 axios 中使用
import axios from 'axios';
const response = await axios.get('https://api.example.com', {
httpsAgent: agent,
});
`
`ts`
// 通过 HTTP 代理 -> SOCKS5 代理 -> HTTPS 代理 -> 目标
chain.setProxies([
{ protocol: 'http', host: 'proxy1.com', port: 8080 },
{ protocol: 'socks5', host: 'proxy2.com', port: 1080, userId: 'user', password: 'pass' },
{ protocol: 'https', host: 'proxy3.com', port: 443 },
]);
- ⚠️ 重要: 代理链的顺序是严格的,数组中的第一个代理会先连接,然后依次连接后续代理
- 确保代理链的顺序正确,错误的顺序可能导致连接失败
- start() 方法会自动查找可用端口,从指定的 port 开始,最大到 65535PortOccupiedCheck.checkPort(initPort, maxPort)
- 如果需要限制端口范围,建议先使用 获取端口try/catch
- 在极端并发场景下,端口可能被抢占,建议使用 处理错误并重试
- ⚠️ HTTPS 代理: 默认 rejectUnauthorized: false,不验证上游 HTTPS 代理的证书
- 生产环境建议根据安全需求调整 TLS 验证策略
- SSH 代理需要正确的用户名和密码(或密钥)
- 代理链中的任何连接错误都会触发 error 事件error
- 建议监听 事件并实现适当的错误处理逻辑
- 网络问题可能导致代理链中断,需要实现重连机制
- 多级代理会增加延迟,每增加一级代理大约增加 50-200ms 延迟
- 建议在生产环境中监控代理链的性能和可用性
- 对于高并发场景,考虑使用连接池或限流机制
- 使用 stopAll() 确保所有服务器正确关闭SIGINT
- 在应用退出时(如 、SIGTERM)调用 stopAll()
- 未正确关闭可能导致端口占用问题
PortOccupiedCheck 是一个跨平台的端口可用性检测工具,支持:
- ✅ 跨平台支持: Windows (netstat), macOS (lsof), Linux (ss/lsof)
- ✅ 自动回退: 如果系统工具不可用,自动使用绑定检测方法
- ✅ 从起始端口开始逐个尝试,返回第一个可用端口
- ✅ 默认从 initPort 检查到 65535,可通过第二个参数设定上限
- ✅ 已被 start() 内部使用用于自动寻找可用端口
使用示例:
`ts
import { MultiProtocolProxyChain, PortOccupiedCheck } from 'multi-protocol-proxy-chain';
// 方式一:先检测端口范围,再启动(推荐用于生产环境)
const checker = new PortOccupiedCheck();
try {
const availablePort = await checker.checkPort(2000, 2100); // 限制在 2000-2100 范围内
console.log('找到可用端口:', availablePort);
const chain = new MultiProtocolProxyChain();
chain.setProxies([
{ protocol: 'http', host: '192.168.101.166', port: 7890 },
]);
const url = await chain.start('socks5', availablePort, '127.0.0.1');
console.log('本地代理入口:', url); // 例如:socks5://127.0.0.1:2003
} catch (err) {
console.error('端口检测失败:', err);
}
// 方式二:直接调用 start(内部会自动从起始端口查找可用端口)
const chain2 = new MultiProtocolProxyChain();
chain2.setProxies([/ ... /]);
const url2 = await chain2.start('socks5', 2000);
console.log('本地代理入口(自动选择):', url2);
`
跨平台端口检测机制:
- Windows: 使用 netstat -ano 快速检测端口占用lsof
- macOS: 使用 命令检测端口占用ss
- Linux: 优先使用 命令,回退到 lsof,最后使用绑定检测
- 其他平台: 使用通用的绑定检测方法(尝试绑定端口来判断是否可用)
所有平台在检测失败时都会自动回退到可靠的绑定检测方法,确保功能可用。
A: 是的,支持 IPv6 地址。在设置代理时可以使用 IPv6 地址,本地入口也支持绑定到 IPv6 地址。
`ts`
await chain.start('socks5', 2000, '::1'); // IPv6 localhost
A: 可以,可以多次调用 start() 启动不同协议或端口的入口。
`ts`
await chain.start('socks5', 2000);
await chain.start('http', 3128);
await chain.start('socks5', 2001); // 甚至可以启动多个相同协议的入口
A: 连接失败会触发 error 事件,并可能导致整个请求失败。建议监听 error 事件并实现重试或降级逻辑。
A: 可以使用以下方法测试:
`bash使用 curl 测试 SOCKS5 代理
curl --socks5-hostname 127.0.0.1:2000 https://httpbin.org/ip
$3
A: 每增加一级代理大约增加 50-200ms 延迟。对于大多数应用场景,3-5 级代理链是可以接受的。如果需要更高性能,建议:
- 减少代理链层级
- 选择地理位置较近的代理
- 使用更快的代理服务器
$3
A: 支持。所有协议都支持用户名/密码认证(通过
userId 和 password 字段)。`ts
chain.setProxies([
{ protocol: 'http', host: 'proxy.com', port: 8080, userId: 'user', password: 'pass' },
{ protocol: 'socks5', host: 'proxy2.com', port: 1080, userId: 'user2', password: 'pass2' },
]);
``ISC
欢迎提交 Issue 和 Pull Request!