2012年11月25日星期日

一种HTTP模拟全双工Socket通信的实现

最近几天把很早之前构思过的一种HTTP模拟全双工Socket通信的方法在Snova/GSnova C4上实现了,效果不错,这里简单介绍一下实现方法。若有其他对此感兴趣的朋友,可以一起研究讨论。
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中。
  • 以上两步同时循环执行。
若是其他非HTTP协议的TCP代理,过程类似HTTPS的情况。

其实,Chunked transfer encoding默认在HTTP协议中是请求/应答都可以携带的,若在HTTP请求中也采用类似上述应答的方式,效果应当更好。不过在目前的几个PaaS平台中,几乎都不支持HTTP请求带Tansfer-Encoding。
最终的实现的效果相当不错,相比之前的心跳ping机制(也可以模拟全双工Socket通信),实时性大大提高,甚至访问视频网站这种对socket通信连接稳定性要求较高的场景也表现良好。
题外的话:大部分PaaS限制了一个HTTP请求的最大时长,一般是30s。所以在C4的实现中是用一点小技巧绕过这个限制,需要实现类似机制的朋友可以提前注意一下。