如何为代理中的HTTPS与WSS处理器设置不同读写截止时间
WSS与普通HTTPS流量区分+差异化超时设置方案
现有架构下不需要重构转发逻辑,三个可落地的方案按改造成本从低到高排序:
- 自适应动态超时+被动特征识别(推荐,无需解密流量,改造成本最低)
不需要在连接建立初期就判定连接类型,初始统一给所有HTTPS连接设置普通短超时,在双向流量拷贝逻辑上增加轻量检测钩子即可:- 自定义
net.Conn包装层,替换原有io.CopyBuffer操作的源/目标连接对象 - 连接建立后的前8KB流量(足够覆盖WebSocket握手+首帧阶段)做轻量特征匹配,不需要解密TLS:
- WSS握手完成后,客户端发送的WebSocket帧固定满足:首字节FIN位为1(值为
0x80),第二字节掩码位固定为1(值为0x80),常见文本帧首两字节为0x81 0x8*,二进制帧为0x82 0x8* - 服务端返回的WebSocket帧固定满足:第二字节掩码位为0(值为
0x00),常见文本帧首两字节为0x81 0x0*
- WSS握手完成后,客户端发送的WebSocket帧固定满足:首字节FIN位为1(值为
- 当两个方向的流量都匹配到上述WebSocket帧特征时,立刻将当前连接的读写超时重置为WSS专用长阈值,同时关闭后续特征检测,不产生额外性能损耗
- 如果在初始短超时周期内没有匹配到WSS特征,保持普通HTTPS短超时规则即可
核心逻辑参考代码(Go):
type WSSDetectConn struct { net.Conn isWSS bool clientSide bool // 标记是否是客户端侧连接(帧掩码位应为1) matchCount int // 双向特征匹配计数,满2则判定为WSS shortTimeout time.Duration longTimeout time.Duration } func (c *WSSDetectConn) Read(b []byte) (int, error) { n, err := c.Conn.Read(b) if !c.isWSS && n >= 2 { // 校验帧头特征 finSet := b[0]&0x80 == 0x80 maskBit := b[1]&0x80 == 0x80 if finSet && (maskBit == c.clientSide) { c.matchCount++ if c.matchCount >= 2 { c.isWSS = true c.SetDeadline(time.Now().Add(c.longTimeout)) } } } if !c.isWSS { c.SetDeadline(time.Now().Add(c.shortTimeout)) } return n, err } - 自定义
- CONNECT阶段前置初筛
HTTPS代理场景下客户端会先发送CONNECT请求建立隧道,可以提前从请求行中提取目标端口、域名做初筛:已知的WSS服务域名、常用WSS端口(如9001、6001等非443端口)可以直接设置长超时。该方案准确率不足,仅适合作为辅助规则。 - TLS中间人(MITM)解密识别
如果是内部测试场景,可以在代理侧实现TLS中间人解密,解密后和明文HTTP逻辑一致,直接识别101 Switching Protocols响应即可100%准确判定WebSocket升级。该方案需要客户端信任代理根证书,不适合公网透明代理场景。
注意:被动特征识别方案不要只匹配单方向流量,需要双向都匹配到对应帧特征后再切换超时,避免误判。
内容的提问来源于stack exchange,提问作者Romeo Mihalcea
相关产品推荐
相关产品推荐

