欢迎光临

木子屋

标题: Http 请求头中的 Proxy-Connection[转]

作者: dnawo

日期: 2014-08-18 07:59 PM

分类: Win编程

内容: 平时用 Chrome 开发者工具抓包时，经常会见到 Proxy-Connection 这个请求头。之前一直没去了解什么情况下会产生它，也没去了解它有什么含义。最近看完《HTTP 权威指南》第四章「连接管理」和第六章「代理」之后，终于搞明白了这是因为给浏览器设置了代理（Proxy）。而神器 Fiddler 的抓包原理就是让浏览器请求走它开的本地代理，所以开了 Fiddler 必然会产生这个请求头。
代理改变了什么？
为了彻底弄清这个问题，我们先来看下设置浏览器代理之后，HTTP 请求头有那些变化。下面分别是设置代理前后访问同一 URL 的请求头（省略了无关内容）：
引用内容GET / HTTP/1.1
Host: www.example.com
Connection: keep-alive
引用内容GET http://www.example.com/ HTTP/1.1
Host: www.example.com
Proxy-Connection: keep-alive
设置代理之后，浏览器连接的是代理服务器，不再是目标服务器，这个变化单纯从请求头中无法看出。请求头中的变化有两点：第一行中的 request-URL 变成了完整路径；Connection 请求头被替换成了 Proxy-Connection。我们分别来看这两个变化。
为什么需要完整路径？
早期的 HTTP 设计中，浏览器直接与单个服务器进行对话，不存在虚拟主机。单个服务器总是知道自己的主机名和对应端口，为了避免冗余，浏览器只需要发送主机名之外的那部分 URI 就行了。代理出现之后，部分 URI 彻底杯具，代理服务器无法得知用户想要访问的URI在什么主机上。为此，HTTP/1.0 要求浏览器为代理请求发送完整的 URI，也就是说规范告诉浏览器的实现者必须这么做。
显式地给浏览器配置代理后，浏览器会为之后的请求使用完整 URI，解决了代理无法定位资源的问题。但是代理可以出现在连接的任何位置，很多代理对浏览器来说不可见，如反向代理或路由器代理。所以实际上，几乎所有的浏览器都会为每个请求加上内容为主机名的 HOST 请求头，来彻底解决虚拟主机问题。对于 HTTP/1.1 请求，HOST 请求头必须存在，否则会收到 400 错误；对于 HTTP/1.0 请求，如果连接的是代理服务器，使用相对 URI，并且没有 HOST 请求头，会发生错误。
Proxy-Connection 是什么？
HTTP 中的 Connection，用来对 HTTP 连接进行说明，多个说明使用英文逗号隔开，如：
引用内容GET / HTTP/1.1
Host: www.example.com
Connection: my-header, close, my-connection
My-Header: xxx
其中，「my-header」是本次请求中其它 Header 的名字（不区分大小写），表示这个 Header 只与当前连接有关。实际上，Connection 本身也只有与当前连接有关。当客户端和服务端存在一个或多个中间实体（如代理）时，每个请求报文都会从客户端（通常是浏览器）开始，逐跳发给服务器；服务器的响应报文，也会逐跳返回给客户端。通常，即使通过了重重代理，请求头都会原封不动的发给服务器，响应头也会原样被客户端收到。但 Connection，以及 Connection 定义的其它 Header，只是对上个节点和当前节点之间的连接进行说明，必须在报文转给下个节点之前删除，否则可能会引发后面要提到的问题。其它不能传递的 Header 还有Prxoy-Authenticate、Proxy-Connection、Transfer-Encoding 和 Upgrade。
「close」表示操作完成后需要关闭当前连接；Connection 还允许任何字符串作为它的值，如「my-connection」，用来存放自定义的连接说明。HTTP/1.0 默认不支持持久连接，很多 HTTP/1.0 的浏览器和服务器使用「Keep-Alive」这个自定义说明来协商持久连接：浏览器在请求头里加上 Connection: Keep-Alive，服务端返回同样的内容，这个连接就会被保持供后续使用。对于 HTTP/1.1，Connection: Keep-Alive 已经失去意义了，因为 HTTP/1.1 除了显式地将 Connection 指定为 close，默认都是持久连接。
有了上面的背景知识，我们来看问题。互联网上，存在着大量简陋并过时的代理服务器在继续工作，它们很可能无法理解 Connection——无论是请求报文还是响应报文中的 Connection。而代理服务器在遇到不认识的 Header 时，往往都会选择继续转发。大部分情况下这样做是对的，很多使用 HTTP 协议的应用软件扩展了 HTTP 头部，如果代理不传输扩展字段，这些软件将无法工作。
如果浏览器对这样的代理发送了 Connection: Keep-Alive，那么结果会变得很复杂。这个 Header 会被不理解它的代理原封不动的转给服务端，如果服务器也不能理解就还好，能理解就彻底杯具了。服务器并不知道 Keep-Alive 是由代理错误地转发而来，它会认为代理希望建立持久连接。这很常见，服务端同意了，也返回一个 Keep-Alive。代理就杯具了，它会一直傻等服务器关闭连接——实际上，服务端已经按照 Keep-Alive 指示保持了连接，即时数据回传完成，也不会关闭连接。另一方面，响应中的 Keep-Alive 也会被代理原样返给浏览器，浏览器收到 Keep-Alive 之后，会复用之前的连接发送剩下的请求，但代理不认为这个连接上还会有其他请求，请求被忽略。这样，浏览器会一直处于挂起状态，直到连接超时。
这个问题最根本的原因是代理服务器转发了禁止转发的 Header。但是要升级所有老旧的代理也不是件简单的事，所以浏览器厂商和代理实现者协商了一个变通的方案：首先，显式给浏览器设置代理后，浏览器会把请求头中的 Connection 替换为 Proxy-Connetion。这样，对于老旧的代理，它不认识这个 Header，会继续发给服务器，服务器也不认识，代理和服务器之间不会建立持久连接（不能正确处理 Connection 的都是 HTTP/1.0 代理），服务器不返回 Keep-Alive，代理和浏览器之间也不会建立持久连接。而对于新代理，它可以理解 Proxy-Connetion，会用 Connection 取代无意义的 Proxy-Connection，并将其发送给服务器，以收到预期的效果。
显然，如果浏览器并不知道连接中有老旧代理的存在，或者在老旧代理任意一侧有新代理的情况下，这种方案仍然无济于事。所以有时候服务器也会选择彻底忽略 HTTP/1.0 的 Keep-Alive 特性：对于 HTTP/1.0 请求，从不使用持久连接，也从不返回 Keep-Alive。
最后
通过上面的内容可以看到，浏览器对代理请求头的修改，都是为了尽可能的兼容网络中各种不规范的中转设备，使网络更健壮。
最后再提一句，用 Fiddler 和其它工具查看同一个请求头，会发现 Fiddler 显示的是 Connection，而其它工具显示的是 Proxy-Connection。这是因为大部分情况下，Fiddler 会把 Proxy-Connection 换回 Connection 来显示，只是展现上的差别而已。
原文链接：http://www.aikaiyuan.com/6326.html

+ 查看当前日志评论 (0)

登录

────────────

木子屋

PJBlog3 v3.2.9.518 Inside.

Processed In 1.086 ms

返回

标题: Http 请求头中的 Proxy-Connection[转]

你没有权限发表评论

────────────

木子屋

PJBlog3 v3.2.9.518 Inside.

Processed In 1.086 ms

返回

暂无评论

返回

────────────

木子屋

PJBlog3 v3.2.9.518 Inside.

Processed In 1.117 ms

返回