一个小的代理包装器可能对 HTTPS 有效,但在用于服务器先说法的协议时会挂起。可疑的那一行往往是等待接收缓冲区以 \r\n\r\n 结束 的那一行。

TCP 给客户端一个字节流。一次接收调用可能返回半行 HTTP 状态行、一个完整的 CONNECT 响应,或者该响应后面紧跟隧道数据。并没有任何保证说一次读取会停在头部末尾。

在缓冲区内部找到边界

对于 HTTP/1.1 CONNECT 交换,持续累积字节,直到头部终止符出现在缓冲区中的任意位置。在它第一次出现的位置切分。从第一部分解析状态行和头部;把边界之后的所有内容单独保留。等待时施加头部大小上限和超时,并把空的套接字读取视为连接已关闭,而不是又一次循环的邀请。

最终成功的 2xx 响应会在这之后立刻把连接切换到隧道模式。它后面跟随的字节已经是隧道字节。不要丢弃它们,不要 等待 CONNECT 响应正文,也不要把 Content-Length 字段解读为可以消费新协议一部分的许可。如果存在信息性响应,需要在该最终响应之前处理。

包装器必须把剩余数据传递下去

只返回底层套接字会丢失握手解析器已经从中取出的字节。下一个读取方需要一个带缓冲的流:先交出保存下来的尾部,然后继续从套接字读取。对于服务器先发言的协议,丢失这段初始问候可能让双方无限期等待。

反过来,失败 CONNECT 响应的字节不是隧道问候。407 仍属于代理交换,需要处理认证;其他非成功状态需要相应的错误路径。

在通过 HTTP 代理适配 FTP 或 SSH 客户端时,这一点尤其重要。代理还必须允许所请求的目标端口,而 FTP 可能需要额外的数据连接。正确的头部解析解决了流的交接;它无法授予 CONNECT 权限,也无法提供 FTP 的第二个通道。

相关文章