# devsapp-https-proxy **Repository Path**: ctyunfaas/devsapp-https-proxy ## Basic Information - **Project Name**: devsapp-https-proxy - **Description**: https-proxy · 正向代理(函数转发网关) - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-08-11 - **Last Updated**: 2026-09-08 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # https-proxy · MCP 正向代理(转发网关) MCP 正向代理,**同时支持 SSE 与 Streamable HTTP**。 目标后端由客户端决定(经 `X-Target-URL` header 指定),可转发到**任意** MCP server。 ## ⚠ 先理解:为什么是"网关"形态,而不是标准 HTTP 代理 标准 HTTP 正向代理对 HTTPS 走 `CONNECT` 隧道(需要 Hijack 底层 TCP 做双向字节流)。 **Serverless / HTTP 入口给不了底层 TCP 连接,做不了 `CONNECT`**——这是平台模型决定的。 因此在 Serverless 环境中,正向代理用**应用层转发网关**形态实现: - 客户端把目标地址放在 `X-Target-URL` header; - 网关读取目标、代为请求、把响应(含 SSE 流)**流式**转回客户端。 对**自定义客户端**,这和正向代理效果完全等价(目标由客户端决定)。 代价:**不兼容 `curl -x` / `HTTPS_PROXY` / 标准 MCP SDK 的代理配置**——那需要 `CONNECT`,得用常驻环境(见文末)。 ## 架构 ``` MCP 客户端 (自定义 Go) │ POST <网关>, Header: X-Target-URL: https://任意-mcp-server/mcp ▼ Serverless 平台:自定义域名 + 会话亲和(按 mcp-session-id 钉实例保活) ▼ ┌──────────────────────────────────┐ │ 网关实例:本代理 (main.go) │ 读 X-Target-URL → 动态流式转发 └──────────────────────────────────┘ │ 流式转发(FlushInterval = -1) ▼ 任意 MCP server(客户端指定) ``` ## 项目结构 ``` . ├── main.go # 入口:加载配置 → 构造网关 → 启动 HTTP 服务 ├── internal/ │ ├── config/ # 配置管理(环境变量 → Config 结构体) │ │ └── config.go │ ├── allowlist/ # 目标白名单:CIDR/IP/host 解析 + DNS 缓存匹配 │ │ ├── allowlist.go │ │ └── allowlist_test.go │ └── proxy/ # 转发网关核心:ReverseProxy + path/query 合并 │ ├── proxy.go │ └── proxy_test.go ├── client/ # Go 客户端示例(纯 net/http) │ └── main.go ├── cmd/mock-backend/ # 本地联调 mock MCP 后端 │ └── main.go ├── Dockerfile # 容器镜像 ├── Makefile # 构建/打包/部署脚本 ├── s.yaml # Serverless Devs 部署配置 └── .env.example # 环境变量示例 ``` ## 关键设计 | 决策 | 原因 | |---|---| | 目标从 `X-Target-URL` 动态读取 | 正向代理本质:目标由客户端决定(非固定后端) | | path/query 合并转发 | 目标 scheme/host/path 取自 `X-Target-URL`;入站请求的 query 会合并透传(同名 key 入站覆盖),`X-Target-URL` 未带 path 时回退入站 path | | `FlushInterval: -1` | SSE 事件实时下发,不被缓冲 | | 不设 `WriteTimeout/ReadTimeout` | SSE 长连接,设了会被掐断 | | `ALLOW_TARGETS` 白名单 | 正向代理是开放转发,生产**必须**限定目标,否则被滥用。支持 CIDR 网段 / 单 IP / host,域名会解析后按网段判断 | | 靠会话亲和保活实例 | 同一 `mcp-session-id` 钉到同一实例,session 状态和到目标的连接都维持得住 | ## 模块说明 | 模块 | 职责 | |---|---| | `internal/config` | 从环境变量加载运行配置(端口、白名单、TLS、连接池参数等) | | `internal/allowlist` | 白名单解析(CIDR/IP/host)、DNS 缓存、目标匹配;支持注入 mock DNS 便于测试 | | `internal/proxy` | 转发网关核心:封装 `httputil.ReverseProxy`,处理 `X-Target-URL` 校验、path/query 合并、SSE 流式、错误处理 | | `main.go` | 入口:组装配置 → 白名单 → 网关 → HTTP 服务 | | `client/` | Go 客户端示例(纯 `net/http`),通过 `X-Target-URL` 指定目标 | | `cmd/mock-backend/` | 本地联调用的 mock MCP 后端(Streamable HTTP + 旧 SSE) | ## 本地联调 ```bash # 1) 启动 mock 后端(模拟一个 MCP server,监听 :3000) go run -C . ./cmd/mock-backend # 2) 启动转发网关(监听 :9000) PORT=9000 go run -C . . # 3) 运行客户端:经网关访问 mock 后端(目标由 MCP_TARGET 指定) MCP_GATEWAY=http://localhost:9000 MCP_TARGET=http://localhost:3000/mcp go run -C . ./client # 期望: via gateway ... -> target ... 然后 session: ... + 逐条 stream chunk ``` ## 测试 ```bash make test # 或 go test ./... ``` 测试覆盖: - 白名单解析(CIDR/单IP/host/带端口host/空输入) - 白名单匹配(端口宽松/端口严格/CIDR网段/单IP/域名解析后按网段) - DNS 缓存(重复查询只调用一次 resolver) - path/query 合并(target 带/不带 path、query 覆盖、不修改原对象) ## 部署 ### 1. 打包(交叉编译 linux/amd64 + 赋可执行权限 + 打 zip) ```bash make package # 产物:dist/https-proxy.zip(内含可执行 bootstrap,权限 0755) ``` ### 2. 部署 ```bash s config add # 首次:配置云平台密钥 make deploy # 打包 + s deploy(配置见 s.yaml) ``` > 也可手动上传:`dist/https-proxy.zip` 在控制台创建函数 —— 运行环境选**自定义运行时 (Custom)**,启动命令 `/code/bootstrap`,HTTP 端口 `9000`,代码包传该 zip。 环境变量:`ALLOW_TARGETS=<允许的目标,逗号分隔>`。每条支持 host(端口宽松)、host:port、CIDR 网段(如 `192.168.0.0/16`)、单 IP(如 `10.0.0.1`);域名目标会解析 IP 后按网段判断。留空=允许任意(仅测试,开放转发有滥用风险) ### 3. ★ 配置会话亲和 函数配置 → 隔离性/亲和性 → 开启。按传输选: | 模式 | Session Key | 适用 | |---|---|---| | **MCP Streamable HTTP 亲和** | `mcp-session-id` | 新传输(推荐) | | **MCP SSE 亲和** | `session_id`(路径 `/sse`) | 旧 SSE | > 注意:目标可变时,亲和保证的是"同一 session 钉同一实例",实例内用连接池维护到不同目标的连接。同一 session 不会切换目标,所以亲和仍有效。 ### 4. 绑定自定义域名 + 调超时 - 域名路由指向该函数,允许 GET - 函数执行超时 / 网关响应超时 / 客户端读取超时 三处对齐调大 ## 边界与备选 - **不兼容标准代理协议**:不能 `curl -x` 或设 `HTTPS_PROXY`,标准 MCP 客户端 SDK 的代理配置也用不了。仅适合你自己的客户端。 - **开放转发风险**:`ALLOW_TARGETS` 必须设白名单,否则网关会被当成开放代理滥用。 - **若必须兼容标准 `HTTPS_PROXY`(任意客户端零改造)**:Serverless 做不了 `CONNECT`,只能用**有裸 TCP 入口的常驻环境**(VPS/ECS/轻量服务器/自管容器),gost 一行命令即可: ```bash gost -L=http://:8080 # 标准 HTTP/HTTPS 正向代理,自动处理 CONNECT ```