不能只用net/http+httputil.reverseproxy搞多协议中转,因其仅支持http/1.1文本流,会丢弃websocket升级头、误处理grpc的content-type、提前消费mqtt二进制帧,且不支持http/2;应改用fullproxy等原生支持多协议嗅探与透传的工具。

直接上结论:别用 net/http + httputil.ReverseProxy 搞多协议中转站,它对 WebSocket、gRPC、MQTT 等协议完全无感,转发必错。
为什么不能只靠 http.ReverseProxy
它只认 HTTP/1.1 文本流,遇到以下情况会直接崩溃或静默损坏:
- WebSocket 请求里的
Upgrade: websocket和Connection: Upgrade头被丢弃,客户端收 400 或连接秒关 - gRPC 的
Content-Type: application/grpc被当普通 POST 转发,下游报rpc error: code = Internal desc = transport: received the unexpected content-type "text/plain" - MQTT over WebSocket 或纯 TCP 流量,
io.ReadAll(req.Body)会提前消费二进制帧,后续解析全乱 - HTTP/2 流量(如 gRPC)必须用
http2.Transport,而默认http.Transport不支持
用 fullproxy 快速启动多协议中转
fullproxy 是专为多协议设计的 Go 工具,原生支持 HTTP、HTTPS、TCP、WebSocket、gRPC(通过 h2c 或 TLS),且不依赖外部框架。它把协议识别和路由逻辑下沉到连接建立阶段,不是靠请求头猜,而是靠 ALPN、TLS SNI、Upgrade 字段、甚至原始字节特征(如 gRPC 前缀 \x00\x00\x00\x00\x00)做分流。
安装后,用 YAML 配置即可启用混合协议中转:
circuits:
- name: "multi-protocol-gateway"
listeners:
- type: "tcp"
addr: ":8080"
protocol: "auto" # 自动探测 HTTP/WS/gRPC/TCP
routes:
- match:
protocol: "http"
forward: "http://127.0.0.1:9001"
- match:
protocol: "websocket"
forward: "ws://127.0.0.1:9002"
- match:
protocol: "grpc"
forward: "h2c://127.0.0.1:9003"
- match:
protocol: "tcp"
forward: "tcp://127.0.0.1:9004"
关键点:
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
-
protocol: "auto"启用连接层协议嗅探,不等完整请求体到达就决策 -
h2c://表示明文 HTTP/2(gRPC 默认),ws://表示 WebSocket 升级后透传 - 所有转发目标地址必须显式声明协议前缀,否则 fullproxy 不知道该用哪种 Transport
自己写时,协议识别必须在 conn.Read() 第一次调用前完成
如果你不打算用现成工具,而是手写中转逻辑,核心陷阱在于:不能等 *http.Request 构造完再判断协议——那时 WebSocket 和 gRPC 早已失败。
正确做法是监听原始 net.Conn,读取前几个字节做模式匹配:
- HTTP/1.x:检查是否以
GET、POST、CONNECT开头(注意空格) - WebSocket:检查是否有
Upgrade: websocket和Connection: Upgrade(需读完整 headers,但限制最多 4KB) - gRPC:检查前 5 字节是否为
\x00\x00\x00\x00\x00(length-prefixed frame header)或 TLS ALPN 协商结果为h2 - TCP 透传:其余全部归为 raw TCP,直接
io.Copy双向转发,不解析内容
错误示范:http.Serve(<conn>, handler)</conn> 后再从 r.Header 判断——这时 gRPC 请求已被 http.Server 拒绝并返回 400。
消息体编解码必须和协议严格对齐
中转站不是业务服务,不负责解包业务数据,但必须保证帧结构不被破坏。尤其注意:
- WebSocket 消息必须保持 opcode 和 masking 位不变,不能用
json.Unmarshal再json.Marshal回去(会丢 opcode) - gRPC 流式响应必须保留每个
DATA帧的 length prefix(4 字节大端),不能合并或拆分帧 - 自定义二进制协议(如 MQTT)必须先按协议规范分包(如读 2 字节长度字段),再整包透传,不能按行或按 \n 切割
- HTTP body 若含
Transfer-Encoding: chunked,中转时需完整保留 chunk 格式,不能转成Content-Length
最安全的做法:对非 HTTP 协议,一律走 raw byte copy;HTTP 协议仅操作 headers 和 status line,body 层不做任何重编码。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










