websocket-proxy-http-request
v0.0.7
Published
通过WebSocket将外网流量转发到内网服务
Readme
WebSocket Proxy HTTP Request
通过WebSocket将外网HTTP流量转发到内网服务的高性能代理工具。
✨ 特性
- 🚀 高性能: P99延迟30ms,RPS达629
- 🔄 自动重连: 断线自动恢复,最多重试3次
- 💓 心跳保活: 30秒心跳间隔,保持连接稳定
- 🔐 安全认证: 支持Token认证(可选)
- 📊 完整日志: 请求全链路可追踪
- 🎯 类型安全: TypeScript完整类型定义
- 🛠️ 易于部署: 支持PM2和Docker
📖 快速开始
方式一:使用 npx(推荐)
无需安装,直接运行:
# 1. 克隆项目
git clone <repository-url>
cd websocket-proxy-http-request
# 2. 编译项目
npm install
npm run build
# 3. 启动代理服务器(外网)
npx ws-proxy-server
# 或
npx ws-proxy proxy-server
# 4. 启动客户端代理(内网,新终端)
npx ws-client-agent
# 或
npx ws-proxy client-agent方式二:本地安装
# 安装依赖
npm install
# 编译
npm run build配置
创建 .env.proxy (代理服务器):
PROXY_PORT=8080
WS_PORT=8081
AUTH_ENABLED=false
LOG_LEVEL=info创建 .env.client (客户端):
PROXY_WS_URL=ws://localhost:8081
LOCAL_TARGET_HOST=localhost
LOCAL_TARGET_PORT=3000
CLIENT_ID=client-001启动
使用 npx(推荐)
# 启动代理服务器(外网机器)
npx ws-proxy-server
# 或使用子命令
npx ws-proxy proxy-server
# 启动客户端(内网机器,新终端)
npx ws-client-agent
# 或使用子命令
npx ws-proxy client-agent使用 npm scripts
# 编译
npm run build
# 启动代理服务器(外网机器)
npm run start:proxy
# 启动客户端(内网机器)
npm run start:client直接运行
# 编译
npm run build
# 启动代理服务器(外网机器)
node dist/src/proxy-server.js
# 启动客户端(内网机器)
node dist/src/client-agent.js测试
使用自定义动态装箱/拆箱方式发送请求:
# GET请求示例(带查询参数)
curl -X POST http://localhost:8080/proxy \
-H "Content-Type: application/json" \
-d '{
"url": "http://localhost:9000/api/users",
"method": "get",
"params": {"name": "allen"}
}'
# POST请求示例
curl -X POST http://localhost:8080/proxy \
-H "Content-Type: application/json" \
-d '{
"url": "http://localhost:9000/api/users",
"method": "post",
"body": {"name": "allen", "age": 25}
}'
# 指定特定客户端(clientId路由)
curl -X POST http://localhost:8080/proxy \
-H "Content-Type: application/json" \
-d '{
"url": "http://localhost:9000/api/users",
"method": "get",
"clientId": "client-001"
}'请求参数:
url(必需): 内网服务器的完整URLmethod(必需): HTTP方法(get/post/put/delete等)params(可选): URL查询参数对象,自动序列化为查询字符串body(可选): 请求体,用于POST/PUT等方法clientId(可选): 指定客户端ID,将请求路由到特定客户端。如果不指定,将使用第一个可用客户端
按客户端 ID 路径代理
除了 POST /proxy 装箱接口,代理服务器也支持直接使用客户端 ID 作为路径前缀。该路由与 WebSocket 共用 WS_PORT,例如 1315:
GET http://170.106.115.192:1315/client-001/ -> client 本地目标服务 /
GET http://170.106.115.192:1315/client-001/aa -> client 本地目标服务 /aa
POST http://170.106.115.192:1315/client-001/api/users -> client 本地目标服务 /api/usersclient 的 .env.client 决定本地目标服务:
PROXY_WS_URL=ws://170.106.115.192:1315
LOCAL_TARGET_HOST=host.docker.internal
LOCAL_TARGET_PORT=3000
CLIENT_ID=client-001调用示例:
curl http://170.106.115.192:1315/client-001/health
curl -X POST http://170.106.115.192:1315/client-001/api/users \
-H "Content-Type: application/json" \
-d '{"name":"allen","age":25}'路径中的 client-001 必须与 client 注册时的 CLIENT_ID 一致。GET、POST、PUT、PATCH、DELETE 等 HTTP 方法都会按同样的路径规则转发;原有 POST /proxy 接口仍然保留。
健康检查
# 检查服务器状态
curl http://localhost:8080/health
# 响应包含服务器状态、客户端连接信息、内存使用等🏗️ 架构
外网HTTP请求 (8080端口)
↓
Proxy Server (装箱)
↓
WebSocket 传输 (8081端口)
↓
Client Agent (拆箱)
↓
内网HTTP请求 (3000端口)
↓
响应原路返回📊 性能指标
| 指标 | 数值 | 评级 | |------|------|------| | 平均响应时间 | 3.42ms | ⭐⭐⭐⭐⭐ | | P99延迟 | 30ms | ⭐⭐⭐⭐⭐ | | 吞吐量(RPS) | 629 | ⭐⭐⭐⭐⭐ | | 并发处理 | 50+ | ⭐⭐⭐⭐⭐ | | 请求成功率 | 100% | ⭐⭐⭐⭐⭐ |
🎯 核心功能
✅ 已实现
- [x] HTTP请求代理(所有方法)
- [x] WebSocket双向通信
- [x] 请求装箱/拆箱
- [x] 响应原路返回
- [x] 请求超时控制(30秒)
- [x] 心跳保活机制(30秒)
- [x] 断线自动重连(3次)
- [x] Token认证(可选)
- [x] 完整日志系统
- [x] 健康检查接口
- [x] 连接数统计
📁 项目结构
websocket-proxy-http-request/
├── src/
│ ├── proxy-server.ts # 代理服务器(核心)
│ ├── client-agent.ts # 客户端代理(核心)
│ ├── types/ # TypeScript类型
│ │ ├── protocol.ts # 消息协议
│ │ └── config.ts # 配置类型
│ ├── config/ # 配置管理
│ │ ├── proxy-server.config.ts
│ │ ├── client-agent.config.ts
│ │ └── logger.config.ts
│ └── utils/ # 工具函数
│ ├── logger.ts # 日志管理
│ └── id-generator.ts # ID生成
├── mock/ # Mock测试服务
├── test-client.js # 功能测试
├── test-performance.js # 性能测试
└── dist/ # 编译输出🔧 配置说明
代理服务器配置
| 配置项 | 说明 | 默认值 | |--------|------|--------| | PROXY_PORT | HTTP代理端口 | 8080 | | WS_PORT | WebSocket端口 | 8081 | | AUTH_ENABLED | 是否启用认证 | false | | REQUEST_TIMEOUT | 请求超时(ms) | 30000 | | HEARTBEAT_INTERVAL | 心跳间隔(ms) | 30000 |
客户端配置
| 配置项 | 说明 | 默认值 | |--------|------|--------| | PROXY_WS_URL | 代理服务器地址 | ws://localhost:8081 | | LOCAL_TARGET_HOST | 内网目标主机 | localhost | | LOCAL_TARGET_PORT | 内网目标端口 | 3000 | | CLIENT_ID | 客户端标识 | client-001 | | RECONNECT_ENABLED | 自动重连 | true |
🧪 测试
功能测试
# 运行完整功能测试
node test-client.js
# 结果:11/11 测试通过 ✅性能测试
# 运行性能测试
node test-performance.js
# 结果:
# - 平均响应时间: 3.42ms
# - P99延迟: 30ms
# - 吞吐量: 629 RPS🚀 部署
使用PM2
# 安装PM2
npm install -g pm2
# 启动服务
pm2 start dist/src/proxy-server.js --name proxy-server
pm2 start dist/src/client-agent.js --name client-agent
# 查看状态
pm2 status
# 查看日志
pm2 logs使用Docker
项目提供两个独立镜像:server 负责 HTTP 代理和 WebSocket 服务,client 负责连接 server 并访问内网目标服务。
1. 准备配置文件
在项目根目录创建 .env.proxy。该文件只在运行时注入,不会被 Dockerfile 复制进镜像:
PROXY_PORT=8080
WS_PORT=8081
AUTH_ENABLED=true
AUTH_TOKEN=replace-with-a-long-random-token
LOG_LEVEL=warn
LOG_TO_FILE=false创建 .env.client:
PROXY_WS_URL=ws://your-server-host:8081
AUTH_TOKEN=replace-with-the-same-token
LOCAL_TARGET_HOST=host.docker.internal
LOCAL_TARGET_PORT=3000
CLIENT_ID=client-001AUTH_TOKEN 必须使用随机长字符串,且 server/client 两端保持一致。不要把真实 token 提交到 Git 或写入 Dockerfile。
2. 构建镜像
docker build --pull -f Dockerfile.server -t websocket-proxy-server:local .
docker build --pull -f Dockerfile.client -t websocket-proxy-client:local .3. 启动 server
docker run -d --name websocket-proxy-server \
--restart unless-stopped \
--env-file .env.proxy \
-p 8080:8080 \
-p 8081:8081 \
websocket-proxy-server:local验证 server:
curl http://127.0.0.1:8080/health
docker logs --tail 100 websocket-proxy-server4. 启动 client
docker run -d --name websocket-proxy-client \
--restart unless-stopped \
--env-file .env.client \
websocket-proxy-client:local验证 client 是否已连接:
docker logs --tail 100 websocket-proxy-client
curl http://127.0.0.1:8080/health如果 client 访问的是 Linux 宿主机上的服务,需要额外增加:
--add-host host.docker.internal:host-gateway然后将 LOCAL_TARGET_HOST 设置为 host.docker.internal。如果目标服务也运行在 Docker 中,建议把 server、client 和目标服务加入同一个用户自定义 Docker network,并使用目标容器名访问,不要使用 --network host 绕过网络隔离。
停止和删除容器:
docker stop websocket-proxy-client websocket-proxy-server
docker rm websocket-proxy-client websocket-proxy-server发布到 Docker Hub
当前镜像构建和 server/client 互联已经验证,但本项目当前不适合直接以“生产就绪”的 latest 标签公开发布:本次 npm audit --omit=dev --audit-level=high 检测到 4 个 high 和 4 个 moderate 漏洞,且 axios、ws 等依赖存在可用修复版本。建议先完成依赖修复,再发布公开稳定标签。
发布前检查
npm audit --omit=dev --audit-level=high
npm audit fix
npm run build依赖升级后必须重新运行功能测试和 Docker 构建;如果审计仍有 high/critical 漏洞,不要发布 latest,可以先发布私有仓库或带明确 -dev / -rc 后缀的测试标签。
创建仓库并登录
建议在 Docker Hub 创建两个仓库:
websocket-proxy-serverwebsocket-proxy-client
登录时使用 Docker Hub Access Token,不要在命令行直接写密码:
docker login --username YOUR_DOCKERHUB_USERNAMECI 环境可使用 --password-stdin,并通过 CI Secret 注入 token:
printf '%s' "$DOCKERHUB_TOKEN" | docker login \
--username "$DOCKERHUB_USERNAME" \
--password-stdin构建并推送版本标签
将 YOUR_DOCKERHUB_USERNAME 替换为 Docker Hub 用户名,将 0.0.4 替换为实际发布版本:
export DOCKERHUB_USERNAME=YOUR_DOCKERHUB_USERNAME
export IMAGE_VERSION=0.0.4
docker build --pull -f Dockerfile.server \
-t "$DOCKERHUB_USERNAME/websocket-proxy-server:$IMAGE_VERSION" \
-t "$DOCKERHUB_USERNAME/websocket-proxy-server:latest" .
docker build --pull -f Dockerfile.client \
-t "$DOCKERHUB_USERNAME/websocket-proxy-client:$IMAGE_VERSION" \
-t "$DOCKERHUB_USERNAME/websocket-proxy-client:latest" .
docker push "$DOCKERHUB_USERNAME/websocket-proxy-server:$IMAGE_VERSION"
docker push "$DOCKERHUB_USERNAME/websocket-proxy-server:latest"
docker push "$DOCKERHUB_USERNAME/websocket-proxy-client:$IMAGE_VERSION"
docker push "$DOCKERHUB_USERNAME/websocket-proxy-client:latest"生产部署应固定版本标签,不要只依赖 latest:
docker pull "$DOCKERHUB_USERNAME/websocket-proxy-server:$IMAGE_VERSION"
docker pull "$DOCKERHUB_USERNAME/websocket-proxy-client:$IMAGE_VERSION"
docker image inspect \
"$DOCKERHUB_USERNAME/websocket-proxy-server:$IMAGE_VERSION" \
"$DOCKERHUB_USERNAME/websocket-proxy-client:$IMAGE_VERSION"
# 登录 Docker Hub 后执行镜像 CVE/SBOM 检查
docker scout cves "$DOCKERHUB_USERNAME/websocket-proxy-server:$IMAGE_VERSION"
docker scout cves "$DOCKERHUB_USERNAME/websocket-proxy-client:$IMAGE_VERSION"
docker scout sbom "$DOCKERHUB_USERNAME/websocket-proxy-server:$IMAGE_VERSION"多架构发布可以将 docker build 替换为 docker buildx build --platform linux/amd64,linux/arm64 --push,并同时指定版本标签和 latest 标签。
Docker 官方命令参考:docker login、docker push、Docker Hub repositories。
Docker Hub 发布安全检查
镜像层面的安全措施:
- 两个运行时镜像使用
nodejs非 root 用户。 .dockerignore排除了.env、.history、日志、测试和文档;运行时只复制dist、生产依赖和包元数据。- 当前构建检查未发现
.env、私钥或 token 文件进入镜像。 - Dockerfile 不包含认证 token;配置应通过
--env-file、环境变量或 Secret 注入。
仍需由部署方承担的风险:
AUTH_ENABLED默认是false;当前认证只保护 WebSocket client 注册,/proxy、/health和/:clientId/*没有 HTTP 认证。公网部署必须在反向代理、API Gateway 或防火墙层限制访问。- client 会按请求中的完整 URL 发起内网 HTTP 请求。未经限制时,任何能调用
/proxy的人都可能借此访问 client 网络可达的内网、云元数据或管理接口,属于 SSRF/横向访问风险。生产环境应使用网络出口控制和目标 URL allowlist;当前代码尚未实现 allowlist。 - 默认使用明文
http://和ws://。跨公网传输必须放在 HTTPS/WSS 反向代理、VPN 或私有网络内,不能直接暴露 8080/8081。 - server/client 会把 URL、请求头、请求体和响应信息写入控制台或日志,可能泄露
Authorization、Cookie 和业务数据。生产环境应避免敏感字段,并保护容器日志;LOG_TO_FILE=false不能关闭源码中的console.log。 - 当前没有 HTTP rate limit;WebSocket 也没有显式的消息大小上限,公网部署应增加网关限流和连接限制。
node:20-alpine使用可变 tag。正式供应链要求应固定基础镜像 digest,并在 CI 中执行镜像 CVE/SBOM 扫描。
因此,当前结论是:适合发布为经过说明的开发/测试镜像或私有镜像;不建议在依赖审计和公网访问控制完成前,把它作为生产就绪的公开 latest 镜像。
📚 文档
🎓 技术栈
- 运行时: Node.js 18+
- 语言: TypeScript 5.3+
- WebSocket: ws 8.16+
- HTTP服务: Express 4.18+
- HTTP客户端: Axios 1.6+
- 日志: Winston 3.11+
🔍 监控
健康检查
curl http://localhost:8080/health
# 返回:
{
"status": "ok",
"clients": 1,
"pendingRequests": 0
}日志
# 查看日志
tail -f logs/app.log
# 查看错误日志
tail -f logs/error.log💡 使用场景
- 远程开发: 在家访问公司内网开发环境
- 临时演示: 将内网测试环境暴露给客户
- 跨网络调试: 调试不同网络的服务
- 内网穿透: 将内网服务暴露到公网
🤝 贡献
欢迎提交Issue和Pull Request!
📄 许可证
MIT License
🎉 项目状态
- ✅ 功能完整: 所有核心和辅助功能已实现
- ✅ 测试通过: 11项功能测试 + 4项性能测试全部通过
- ✅ 性能优秀: P99延迟30ms,RPS达629
- ✅ 文档完善: 6份完整文档
- ⚠️ 发布状态: Docker 镜像可构建;公开生产发布需先完成依赖审计和网络访问控制
📞 联系方式
如有问题或建议,欢迎反馈!
⭐ 如果这个项目对你有帮助,请给个Star!
