HTTP代理与SPDY协议(转)
原文出处: fqrouter
HTTP代理是最经典最常见的代理协议。其用途非常广泛,普遍见于公司内网环境,一般员工都需要给浏览器配置一个HTTP代理才能访问互联网。起初,HTTP代理也用来翻越“功夫网”,但是因为“功夫网”不断发展,普通的HTTP代理早已无效了。但是,基于仍然有不少人使用明文的HTTP代理协议结合stunnel之类的软件进行加密FQ,有时这种代理又被称为HTTPS代理。
再后来,又出现了WebVpn via SPDY 之类的代理协议,特点是Chrome浏览器直接支持。再加上HTTP代理协议可以代理什么?是只能代理HTTP还是也可以代理HTTPS,还是可以用来实现SOCKS代理?总之,非常混乱。在fqsocks项目里用python实现了HTTP代理的各种主流变种,终于明白了不同称谓之后的真正含义。本文试图总结一二。
代理基础
所有的代理,其原理都是类似的。其网络拓扑结构都是这样的:
1
|
[客户端] <-TCP连接-> [代理] <-TCP连接-> [服务器] |
代理左手拿着与客户端的连接,右手拿着与服务器的连接,然后在两个TCP连接之间做数据的对拷。各种不同的代理协议,不同的只是TCP连接之上跑的是什么的协议,数据是怎么经过包装,拆包的。不存在客户端与服务器之间建立TCP连接的情况。只有VPN这种在IP包这一层工作的,才能实现客户端与服务器的之间连接。
HTTP流量,明文的代理连接
这是最简单的一种HTTP代理。其工作方式是客户端与代理之间建立的TCP连接是明文的,也就是不经过SSL加密的。在TCP连接中传输的数据就是明文的HTTP的POST和GET这些。对于这种代理工作方式,客户端发给服务器的包几乎不用修改发给代理,就可以获得几乎与服务器一样的返回。其收发包过程如下
1
2
|
[客户端] -HTTP GET-> [代理] -HTTP GET-> [服务器] [客户端] <-200 OK [代理] <-200 OK [服务器] |
HTTPS流量,明文的代理连接
在这种工作方式中,客户端与代理之间建立的TCP连接仍然是明文的,也就是不经过SSL加密的。但是客户端发出的请求不是HTTP请求,而是HTTPS请求。因为HTTPS请求是经过SSL加密的,所以做为代理来说,虽然客户端与其建立的TCP连接本身没有经过SSL加密,但是中间传输的数据确是SSL加密的。于是代理就无法通过检查HTTPS请求的内容知道目的服务器是什么。
所以客户端必须在发HTTPS请求之前用HTTP CONNECT请求告诉代理要连接的服务器是什么,然后等代理服务器左右手的TCP连接都握稳了,客户端就可以收发HTTPS流量了。代理在其中只是一个简单的在两个TCP连接中做数据对拷,并不知道客户端与服务器在干什么。事实上,利用HTTP CONNECT,客户端与服务器之间除了可以交换HTTPS的流量,任何流量都是可以的。经过HTTP CONNECT之后,客户端与代理的TCP连接就“相当于”是与服务器之间建立的TCP连接。当然我们前面已经说过了,这种直连只是一个假象。其收发包过程如下:
1
2
3
4
5
6
7
8
|
[客户端] -HTTP CONNECT a.b.c.d:port-> [代理] -TCP SYN-> [服务器] [客户端] <-200 OK- [代理] <-TCP SYN ACK- [服务器] # 这里的200 OK与下面的200 OK的含义不同 [客户端] -SSL CLIENT HELLO-> [代理] -SSL CLIENT HELLO-> [服务器] # SSL握手包1 [客户端] <-SSL SERVER HELLO- [代理] <-SSL SERVER HELLO /CERTIFICATE 等- [服务器] # SSL握手包2 [客户端] -SSL CLIENT CERTIFICATE等-> [代理] -SSL CLIENT CERTIFICATE等-> [服务器] # SSL握手包3 [客户端] <-SSL SERVER FINISHED- [代理] <-SSL SERVER FINISHED- [服务器] # SSL握手包4 [客户端] -SSL加密的HTTP GET-> [代理] -SSL加密的HTTP GET-> [服务器] [客户端] <-SSL加密的200 OK- [代理] <-SSL加密的200 OK- [服务器] |
因为CONNECT之后,这个左右手的TCP连接里跑什么流量,代理是完全不管的,所以这样也是可以的:
1
2
3
4
|
[客户端] -HTTP CONNECT a.b.c.d:port-> [代理] -TCP SYN-> [服务器] [客户端] <-200 OK- [代理] <-TCP SYN ACK- [服务器] # 这里的200 OK与下面的200 OK的含义不同 [客户端] -any request-> [代理] -any request-> [服务器] [客户端] <-any response- [代理] <-any response- [服务器] |
正因为CONNECT之后TCP连接可以用来跑任意的东西,当然也就可以用来再跑另外一个代理协议了,比如SOCKS代理。
只要SOCKS代理是运行在443这样HTTP CONNECT允许的端口,通过HTTP-CONNECT+SOCKS代理就可以在公司内网里访问HTTP之外的互联网内容了。
HTTP流量,SSL加密的代理连接
传统HTTP代理,客户端与代理之间是不经过加密的。因为GFW可以从明文的流量中知道你通过HTTP代理访问的目标服务器,所以可以从中切断连接。为了让HTTP代理协议继续承担FQ的重任,人们发明一种新的方式。其拓扑结构如下
1
|
[客户端] <-TCP连接-> [Stunnel客户端模式] <-TCP over SSL连接-> [Stunnel服务器模式] <-TCP连接-> [代理] <-TCP连接-> [服务器] |
虽然拓扑结构复杂了,但是概念其实是很简单的。因为客户端与代理都不直接支持SSL的TCP连接,所以通过Stunnel做一个转换,在客户端经过Stunnel的客户端模式进行加密,然后在代理上有用Stunnel的服务器段模式进行解密。这样GFW夹在中间看到的是经过SSL加密的流量,无法进行URL关键字过滤了。收发包的过程如下:
1
2
|
[客户端] -HTTP GET-> [Stunnel客户端模式] -SSL加密的HTTP GET-> [Stunnel服务器模式] -HTTP GET-> [代理] -HTTP GET-> [服务器] [客户端] <-200 OK- [Stunnel客户端模式] <-SSL加密的200 OK- [Stunnel服务器模式] <-200 OK- [代理] <-200 OK- [服务器] |
HTTPS流量,SSL加密的代理连接
传统的HTTP代理走HTTPS流量的时候,HTTP GET的内容经过SSL加密的,GFW无法做URL关键字过滤。但是客户端与代理之间的HTTP CONNECT仍然是明文传输的。GFW仍然可以根据CONNECT的目标域名或者IP地址来判断是不是访问了GFW不允许访问的网站。所以即便客户端发的是HTTPS请求,仍然有必要在客户端与代理之间进行SSL加密。拓扑结构与上面相同。收发包的过程如下:
1
2
3
4
|
[客户端] -HTTP CONNECT-> [Stunnel客户端模式] -SSL加密的HTTP CONNECT-> [Stunnel服务器模式] -HTTP CONNECT-> [代理] -TCP SYN-> [服务器] [客户端] <-200 OK- [Stunnel客户端模式] -SSL加密的200 OK-> [Stunnel服务器模式] <-200 OK- [代理] <-TCP SYN ACK- [服务器] # 这里的200 OK与下面的200 OK的含义不同 [客户端] -SSL CLIENT HELLO-> [Stunnel客户端模式] -SSL加密的SSL CLIENT HELLO-> [Stunnel服务器模式] -SSL CLIENT HELLO-> [代理] -SSL CLIENT HELLO-> [服务器] # SSL握手包1 ... 以下略去。完成SSL握手之后,数据是在双层的SSL加密之下传输的 |
很明显,HTTP代理加上SSL传输的方式可以有效地对付GFW的关键字检查。但是蛋疼的地方是本来是客户端,代理与服务器之间三方的事情,现在变成了五方会谈了。转手的次数越多,效率就越差。出错了,调试问题也越麻烦。除此之外,我们还额可以看到每个SSL连接建立需要四个包,两个来回。这个SSL握手的成本不是一次性的,是附加在每个被代理的连接上的。考虑到很多HTTP请求都是短连接,内容也很少。所以每次多做一次SSL握手,额外负担相比之下挺重的。
SPDY
SPDY是Google家提出来的协议。其核心内容是用一个TCP连接,跑多个HTTP的STREAM。对于用SPDY协议跑HTTP代理来说,其意义就在于之前是每个HTTP请求都要开连接关连接,用了SPDY之后,客户端与代理之间是保持长连接的,然后在这个连接里,代理访问不同的HTTP服务器,就是不同的HTTP STREAM。而且,用SPDY协议虽然没有强制但是一般客户端与代理之间是SSL连接的,所以GFW也无法对连接的内容做关键字过滤。相比Stunnel的方案,省去了每个连接额外的SSL过程,而且也省去了每个HTTP请求额外的TCP握手过程。所以从执行效率的角度来看,SPDY是非常理想的。
以一个最简单的HTTP GET为例SPDY的交互过程是这样的:
1
2
3
4
5
6
|
[客户端] -SSL CLIENT HELLLO-> [服务器] [客户端] <-SSL SERVER HELLO /CERTIFICATE/NPN (HTTP /1 .1, SPDY /3 , SPDY /2 )等 [服务器] # 服务器通过SSL的NPN扩展告诉客户端我这支持HTTP 1.1也支持SPDY 2和3 [客户端] -SSL CLIENT CERTIFICATE /NPN (SPDY /3 )等-> [服务器] # 客户端告诉服务器我选择SPDY3 [客户端] <-SSL SERVER FINISHED- [服务器] # SSL握手完成 [客户端] -SSL加密的SYN FRAME(HTTP GET)-> [服务器] # SYN FRAME是SPDY版的HTTP GET,意思是一样的 [客户端] <-SSL加密的SYN REPLY FRAME(200 OK)- [服务器] # SYN REPLAY FRAME是SPDY版的200 OK,意思是一样的 |
这里与最传统的HTTP GET过程的不同是:
- 经过了SSL加密,客户端与服务器直接处理了SSL的加解密而不是经过Stunnel转手
- SSL除了用来加密其NPN(Next Protocol Negotiation)扩展还用来沟通协议,所以同样一个443端口可以同时用来支持传统的HTTPS和新的SPDY协议
- 同一个SSL加密连接可以同时用来做多个HTTP GET,因为SYN FRAME与SYN REPLY FRAME的对应关系是通过Stream Id来完成的。而一个SSL连接中可以同时有多个Stream。
但是直接支持SPDY协议的服务器并不多,大部分都是Google自家的服务器。所以寄期望于所有的服务器都运行SPDY协议,从而GFW无法进行关键字检测是不现实,比寄期望与所有服务器都部署HTTPS还要不现实。单就反GFW关键字过滤来说,服务器支持HTTPS与SPDY并无区别。
HTTP流量,SPDY的代理连接
有了SPDY协议,客户端与代理之间就又多了一种选择,除了明文的不加密的传统的HTTP,和效率低下的Stunnel模式,还可以用Shrpx+Squid跑SPDY协议。其过程是这样的
1
2
3
4
5
6
7
8
|
[客户端] -SSL CLIENT HELLLO-> [代理] [客户端] <-SSL SERVER HELLO /CERTIFICATE/NPN (HTTP /1 .1, SPDY /3 , SPDY /2 )等 [代理] [客户端] -SSL CLIENT CERTIFICATE /NPN (SPDY /3 )等-> [代理] [客户端] <-SSL SERVER FINISHED- [代理] # 与代理建立好SSL连接,通过NPN选择了选择SPDY3协议 [客户端] -SSL加密的SYN FRAME(Stream1 HTTP GET)-> [代理] -HTTP GET-> [服务器1] [客户端] -SSL加密的SYN FRAME(Stream2 HTTP GET)-> [代理] -HTTP GET-> [服务器2] [客户端] <-SSL加密的SYN REPLY FRAME(Stream2 200 OK)- [代理] <-200 OK- [服务器2] [客户端] <-SSL加密的SYN REPLY FRAME(Stream1 200 OK)- [代理] <-200 OK- [服务器1] |
可见利用SPDY协议,客户端与代理之间的数据传输就是SSL加密的,而且避免了每个HTTP请求都建立新的TCP连接,重新进行SSL握手。而代理与服务器因为都在墙外,所以HTTP明文交流并没有任何问题。
HTTPS流量,SPDY的代理连接
SPDY协议的SYN FRAME除了支持HTTP GET/POST这些之外,HTTP CONNECT也是可以的。除了需要包装成SPDY的各种FRAME之外,HTTP协议自身的语义其实都是一样的。其交互过程是这样的:
1
2
3
4
5
6
7
8
|
[客户端] -SSL CLIENT HELLLO-> [代理] [客户端] <-SSL SERVER HELLO /CERTIFICATE/NPN (HTTP /1 .1, SPDY /3 , SPDY /2 )等 [代理] [客户端] -SSL CLIENT CERTIFICATE /NPN (SPDY /3 )等-> [代理] [客户端] <-SSL SERVER FINISHED- [代理] # 与代理建立好SSL连接,通过NPN选择了选择SPDY3协议 [客户端] -SSL加密的SYN FRAME(HTTP CONNECT)-> [代理] -TCP SYN-> [服务器] [客户端] <-SSL加密的SYN REPLY FRAME(200 OK)- [代理] <-TCP SYN ACK- [服务器] [客户端] -SSL加密的DATA FRAME(HTTP GET)-> [代理] -HTTP GET-> [服务器] [客户端] <-SSL加密的DATA FRAME(200 OK)- [代理] -200 OK-> [服务器] |
完成了HTTP CONNECT之后,对应的SPDY STREAM就是自由的了。无论发送什么样的DATA FRAME(SPDY的FRAME的一种,传输二进制数据流)给代理,代理就直接转给服务器。服务器无论返回什么样的数据,代理也以DATA FRAME的形式转给客户端。这个SPDY STREAM和一个TCP连接就一样了。除了可以用来发HTTP GET之外,跑什么样的协议都行了,代理反正也不管里面是什么,它只管转发DATA FRAME。
PAC文件
HTTP代理的一个流行用法是写一个PAC文件,描述什么样的时候走什么样的代理。经典的PAC文件只有两种返回值,PROXY和DIRECT。如果返回的是PROXY则走代理,DIRECT则是直连。这里的走代理当然是最传统的HTTP代理方式,也就是浏览器与代理之间是明文的连接,不经过SSL加密的。具体浏览器是用HTTP GET/POST还是CONNECT,取决于浏览器要访问的目标服务器是HTTP还是HTTPS的,和PAC文件无关。
Chrome在PAC的PROXY与DIRECT之外添加了第三种返回值,也就是HTTPS。这种返回值的含义与PROXY是一样,都是走代理。区别在于PROXY是明文的连接,HTTPS是SSL加密的连接。所以HTTPS这个返回值的歧义在于让人以为它只用来代理HTTPS流量。因为SPDY是基于SSL的NPN扩展的,所以当浏览器与代理进行SSL握手的时候,可以选择是用HTTP/1.1协议还是SPDY/2还是SPDY/3。于是HTTPS这个PAC返回值,还可以用来支持SPDY,前提当然是代理在NPN里说它支持SPDY,而且浏览器也知道SSL的NPN扩展,而且支持SPDY协议。
另外一个PAC的扩展是苹果给iOS的PAC文件添加的SOCKS返回值。利用这个返回值可以给iPhone配置SOCKS的全局代理。但是这个与本文讨论的HTTP代理就无关了。
总结
可见HTTP代理协议自身来说,就两种:HTTP GET/POST和HTTP CONNECT。一种用来代理HTTP的流量,另外一种用来代理HTTPS的流量。HTTP CONNECT除了可以用来走HTTPS之外,因为代理并不去解析其内容,所以事实上是一个通用的代理隧道。
另外一个维度是客户端与代理之间是一个什么样的连接:不加密的连接,SSL加密的连接,SSL加密同时运行SPDY协议的连接。
所以工作模式共有2*3=6种。
引申阅读: Google官方也给了一些SPDY做代理的收发包示意例子 http://www.chromium.org/spdy/spdy-proxy-examples
给fqrouter做一个广告。fqrouter支持HTTP代理,包括用SSL连接的HTTP代理和SPDY。
http://blog.jobbole.com/42763/
HTTP代理与SPDY协议(转)的更多相关文章
- 【转】SPDY协议
SPDY协议 - v3 原文:SPDY Protocol - Draft 3 翻译:邱鹏滔(QQ: 95350530,主页:www.fireflysource.com) 1 概述 HTTP协议的瓶颈在 ...
- 关于OkHttp–支持SPDY协议的高效HTTP库 com.squareup.okhttp
转载:http://liuzhichao.com/p/1707.html OkHttp–支持SPDY协议的高效HTTP库 柳志超博客 » Program » Andriod » OkHttp–支持SP ...
- 【Spdy协议简介】
一.SPDY协议诞生记 SPDY (SPDY 是 Speedy 的昵音,意思是更快)是 Google 开发的基于传输控制协议 (TCP) 的应用层协议 ,那么为什么要搞一个SPDY出来呢?距离万维网之 ...
- 小tips:TCP的三次握手、长连接、 短连接、 SPDY 协议
当网络通信时采用TCP协议时,在真正的读写操作之前,server与client之间必须建立一个连接,当读写操作完成后,双方不再需要这个连接时它们可以释放这个连接,连接的建立是需要三次握手的,而释放则需 ...
- iOS delegate, 代理/委托与协议.
之前知知道iOS协议怎么写, 以为真的跟特么java接口一样, 后来发现完全不是. 首先, 说说应用场景, 就是当你要用一个程序类, 或者说逻辑类, 去控制一个storyboard里面的label, ...
- Google的SPDY协议成为HTTP 2.0的基础
详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt384 据TNW援引 IFTF HTTP 工作组主席 Mark Notting ...
- Chrome 如何知道网站启用了SPDY 协议?
地址栏输入chrome://net-internals/#spdy 在host后查看协议,google和dropbox用https协议的开启了 3. 也可以通过安装插件来查看(SPDY Indicat ...
- Ubuntu 12.04 LTS 下配置 apache支持SPDY, 使用wireshark 抓包分析SPDY 协议
1.安装apache sudo apt-get install apache2 root@ubuntu:/etc/apache2/mods-enabled# apache2 -v Server ver ...
- 应用代理 socket TCP协议 的资料
http://blog.csdn.net/guowake/article/details/6615728 Linux下高并发socket最大连接数所受的各种限制 http://stackoverflo ...
随机推荐
- UE4的编程C++创建一个FPSproject(两)角色网格、动画、HUD、子弹类
立即归还,本文将总结所有这些整理UE4有关角色的网络格.动画.子弹类HUD一个简单的实现. (五)角色加入网格 Character类为我们默认创建了一个SkeletaMeshComponent组件,所 ...
- vb6源码后台点击任意窗口指定坐标XY位置,支持FLASH和一般的游戏
vb6源码后台点击任意窗口指定坐标XY位置,支持FLASH和一般的游戏,支持每个网页用不同的IP登陆,支持多线程自动点击 适合自动打怪,游戏练级,配合自动识图功能,自动验证码技术,可以实现更多功能. ...
- java android面试题分析总结
本文参考多处,一并感谢! http://www.blogjava.net/fanyingjie/archive/2007/06/27/126467.aspx http://baike.baidu.co ...
- 使用PageHeap.EXE或GFlags.EXE检查内存越界错误
必先利其器之一:使用PageHeap.EXE或GFlags.EXE检查内存越界错误 Article last modified on 2002-6-3 ------------------------ ...
- 使用VC++压缩解压缩文件夹
前言 项目中要用到一个压缩解压缩的模块, 看了很多文章和源代码, 都不是很称心, 现在把我自己实现的代码和大家分享. 要求: 1.使用Unicode(支持中文). 2.使用源代码.(不使用静态或 ...
- hdu2066一个人的旅行
枚举全部相邻城市,作为起点,多次spfa,然后每次在想去的城市中找出spfa后的距离起点最短的花费时间 #include <iostream> #include <cstring&g ...
- Xamarin 安装教程 支持Visual Studio 2013
本文的前提是你已经正确的安装了VS 2013. 本文的全部步骤在Win7 Ultimate 64系统上測试通过.支持VS 2013,我用的版本号是VS 2013 update2. 安装 1. ...
- ThinkPHP 3.2 开发过程
原文:ThinkPHP 3.2 开发过程 设置所有项目的公共配置Application\Common\Conf\config.php,SAE模式下配置文件为config_sae.php 配置默认的模块 ...
- ie浏览器提交参数和其它浏览器的区别
场景描述: 用户注册模块(ajax提交方式,post方法),在url后追加了一个参数,如:url+‘btnvalue=中文参数’,如此在非ie浏览器注册时,功能完好,但在ie下注册不成功.调式后发现在 ...
- 专注UI——有用技术:模糊搜索
在如今的项目中.须要做模糊搜索,在曾经技术的基础上非常快得完毕了第一版.大家先看看第一版的效果,我们一会做评论: 0基础: 我们可能部分源代码(附件中会有所有源代码) <span style=& ...