Snova C4利用双HTTP连接 + Chunked transfer encoding 模拟实现一个全双工通信的TCP Socket。具体HTTP代理实现流程文字描述如下:
- 浏览器发送HTTP请求到本地C4;
- 本地C4创建两个HTTP连接到远端C4,其中一个携带加密后的HTTP请求内容,另一个则只携带读请求;
- (重要)远端C4收到第一个HTTP连接后,将HTTP请求解开,创建到目的Site的连接,发送具体HTTP请求,之后到本地C4连接马上返回;
- 远端C4收到第二个HTTP连接后,阻塞在第一个HTTP连接中创建的Socket读上,每读取一段内容,则将内容加密通过HTTP应答消息体返回给本地C4;注意这里的应答内容必须通过Chunked transfer encoding 方式发送;
- 本地C4收到第二个HTTP连接的应答后,不断读应答消息体,将解开的内容写回到浏览器。至此整个Proxy过程大致结束。
若是HTTPS代理的情况,稍有不同:
- 浏览器发送Connect请求到本地C4;
- 本地C4创建两个HTTP连接到远端C4,其中一个携带加密后的Connect请求内容,另一个则只携带读请求;
- (重要)远端C4收到第一个HTTP连接后,将Connect请求解开,创建到目的Site的连接,发送具体HTTP请求,之后到本地C4连接马上返回,携带“200 Established”应答内容;
- 远端C4收到第二个HTTP连接后,阻塞在第一个HTTP连接中创建的Socket读上,每读取一段内容,则将内容加密通过HTTP应答消息体返回给本地C4;注意这里的应答内容必须通过Chunked transfer encoding 方式发送;
- 本地C4收到第二个HTTP连接的应答后,不断读应答消息体,将解开的内容写回到浏览器;
- 同时本地C4不断读取浏览器连接Socket,将读取的内容通过第一个HTTP连接发送到远端C4;远端C4收到后,则不断写到socket中。
- 以上两步同时循环执行。
其实,Chunked transfer encoding默认在HTTP协议中是请求/应答都可以携带的,若在HTTP请求中也采用类似上述应答的方式,效果应当更好。不过在目前的几个PaaS平台中,几乎都不支持HTTP请求带Tansfer-Encoding。
最终的实现的效果相当不错,相比之前的心跳ping机制(也可以模拟全双工Socket通信),实时性大大提高,甚至访问视频网站这种对socket通信连接稳定性要求较高的场景也表现良好。
题外的话:大部分PaaS限制了一个HTTP请求的最大时长,一般是30s。所以在C4的实现中是用一点小技巧绕过这个限制,需要实现类似机制的朋友可以提前注意一下。