把 SOCKS5 请求转换为 HTTP 隧道
完成与父级的 CONNECT 交换,先向客户端返回 SOCKS 应答,之后才转发应用字节。
SOCKS5 前端可以把 HTTP 代理当作自己的下一跳传输。它对客户端充当 SOCKS 服务器,对父级充当 HTTP CONNECT 客户端。两次控制交换都在应用数据流开始之前结束。
SOCKS CONNECT 请求本身已经给出了目标主机或地址以及端口。下面是一次示例性的 HTTP/1.1 父级交换:
client → adapter: SOCKS5 CONNECT service.example.net:443
adapter → parent:
CONNECT service.example.net:443 HTTP/1.1
Host: service.example.net:443
parent → adapter: HTTP/1.1 200 Connection Established
[end of headers]
adapter → client: SOCKS5 success reply
client ↔ adapter ↔ parent ↔ service: application bytes
在边界处转换结果
适配器完成父级要求的任何认证,并等待 CONNECT 的结果。成功的 2xx 响应才建立起通往父级的隧道。此时适配器才应把相应的 SOCKS 成功应答返回给下游客户端。
如果父级拒绝该操作,就发送一个已定义的 SOCKS 失败应答,然后立即终止客户端连接。SOCKS5 要求在该失败被察觉后的十秒内完成关闭。父级的 HTTP 状态应作为诊断信息保留在适配器内部。若把那条状态行当作应用数据转发,就等于把一个 HTTP 控制消息塞进了客户端期待 SOCKS 分帧的数据流。
在 CONNECT 成功之前,父级说的仍然是 HTTP,包括任何质询或错误应答的分帧。在成功结果的头部定界符之后,这条连接才变成隧道;此前并没有需要先消费的 HTTP 响应体。
保留头部之后的那部分字节
一次读取可能同时包含父级头部的结尾和来自目标端的字节。要把这些多出来的字节留给下游应用。由服务器先开口的协议可能在连接建立后立刻发送问候。
SOCKS 成功应答发出之后,就要双向转发应用数据流。适配器不需要让一条客户端消息与一条服务器消息交替出现,也不需要理解 TLS 记录才能把它们转发出去。它的协议工作只是建立并转换这两次隧道协商。