Http 请求头中的 Proxy-Connection

平时用 Chrome 开发者工具抓包时,经常会见到 Proxy-Connection 这个请求头。之前一直没去了解什么情况下会产生它,也没去了解它有什么含义。最近看完《HTTP 权威指南》第四章「连接管理」和第六章「代理」之后,终于搞明白了这是因为给浏览器设置了代理(Proxy)。而神器 Fiddler 的抓包原理就是让浏览器请求走它开的本地代理,所以开了 Fiddler 必然会产生这个请求头。

代理改变了什么?

为了彻底弄清这个问题,我们先来看下设置浏览器代理之后,HTTP 请求报文有哪些变化。下面分别是设置代理前后访问同一 URL 的请求报文(省略了无关内容):

BASHGET / 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 连接进行说明,多个说明使用英文逗号隔开,如:

BASHGET / 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 来显示,只是展现上的差别而已。

本文链接:https://imququ.com/post/the-proxy-connection-header-in-http-request.html参与评论 »

--EOF--

https://imququ.com/post/the-proxy-connection-header-in-http-request.html

 

时间: 2024-09-17 00:06:54

Http 请求头中的 Proxy-Connection的相关文章

判断请求头中是否含有某属性来判断是否是ajax请求

 本文为大家介绍下如何判断请求头中是否含有某属性来判断是否时ajax请求,具体示例如下 代码如下: <html>  <head>  <script language="javascript">    function cl()  {  var xmlhttp;    if (window.XMLHttpRequest)  {  xmlhttp=new XMLHttpRequest();  }  else  {  xmlhttp=new ActiveX

asp.net 中 通过ajax请求一般处理程序,可是请求头中的cookie

问题描述 asp.net 中 通过ajax请求一般处理程序,可是请求头中的cookie asp.net 中 通过ajax请求一般处理程序,可是请求头中的cookie只有一条 ,请问大侠们,cookie有限制吗?我并没有跨域请求啊 解决方案 asp.net Ajax Post 请求 一般处理程序 解决方案二: cookie 里边存储的是两个键值对,可是请求头中只携带了一个键值对.不知道为什么 解决方案三: cookie 里边存储的是两个键值对,可是请求头中只携带了一个键值对.不知道为什么 解决方案

判断请求头中是否含有某属性来判断是否是ajax请求_AJAX相关

复制代码 代码如下: <html> <head> <script language="javascript"> function cl() { var xmlhttp; if (window.XMLHttpRequest) { xmlhttp=new XMLHttpRequest(); } else { xmlhttp=new ActiveXObject("Microsoft.XMLHTTP"); } xmlhttp.open(&

Android系列之网络(二)----HTTP请求头与响应头

一.HTTP头引入: 正确的设置HTTP头部信息有助于搜索引擎判断网页及提升网站访问速度.通常HTTP消息包括:客户机向服务器的请求消息和服务器向客户机的响应消息.客户端向服务器发送一个请求,请求头包含请求的方法.URI.协议版本.以及包含请求修饰符.客户信息和内容的类似于MIME的消息结构.服务器以一个状态行作为响应,相应的内容包括消息协议的版本,成功或者错误编码加上包含服务器信息.实体元信息以及可能的实体内容.  Http协议定义了很多与服务器交互的方法,即HTTP请求的种类中,最基本的有4

HTTP的请求头标签If-Modified-Since

一直以来没有留意过HTTP请求头的IMS(If-Modified-Since)标签. 最近在分析Squid的access.log日志文件时,发现了一个现象. 就是即使是对同一个文件进行HTTP请求,第一次和第二次产生的网络流量数据也是不一致的. 在调查的过程中,逐渐了解了HTTP的If-Modified-Since的头标签的作用. 大家都知道客户端浏览器是有缓存的,里面存放之前访问过的一些网页文件. 例如IE,会把缓存文件存到"C:\Documents and Settings\zh2000g\

求解答java通过请求头如何得到文件类型后缀

问题描述 求解答java通过请求头如何得到文件类型后缀 java中,上传一个文件,首先要从文件的请求头中得到文件的内容类型,然后判断文件的扩展名,请知道的大神详细解答! 解决方案 貌似httpservletRequest'类能够获取请求头,查查api有你要的一切 解决方案二: 上传文件,直接从请求体中得到文件的全部信息啊,什么名字,内容,大小都能得到了 解决方案三: 直接使用httprequest就行,具体的方法可以查一下api 解决方案四: 使用commo-fileupload.jar 大概步

安卓 应用开发-安卓中如何获取http请求头?

问题描述 安卓中如何获取http请求头? 如题,最近在整免流.求大神解答如何用代码获取http请求头.............. 解决方案 HttpClient工具提供了设置和获取请求.响应头的方法的,详细参考:http://blog.csdn.net/z69183787/article/details/42966829 解决方案二: 例子: public void run() {7 //用HttpClient发送请求,分为五步8 HttpClient httpCient = new Defau

【AXIS2】关于在报文中添加请求头的问题,求高手帮忙

问题描述 项目是通过wsdl逆向生成服务端的现在生产的服务端,用soapui请求,不加请求头可以访问了,但是加了请求头就告诉我"DisallowedelementfoundinsideEnvelope:{http://www.xxx.com/soa/}HEADER"项目是半路接手的,我webService用的也很烂,wsdl完全看不懂无从下手啊...跪求高手帮忙,就剩这20分了,有点少了,不好意思.请各路高手提醒下,这个问题一般出在什么地方,应该从哪里入手解决,跪谢!这是用于逆向生成的

[转]HTTP协议及其请求头分析

众所周知,Internet的基本协议是TCP/IP协议,目前广泛采用的FTP.Archie Gopher等是建立在TCP/IP协议之上的应用层协议,不同的协议对应着不同的应用.    WWW 服务器使用的主要协议是HTTP协议,即超文体传输协议.由于HTTP协议支持的服务不限于WWW,还可以是其它服务,因而HTTP协议允许用 户在统一的界面下,采用不同的协议访问不同的服务,如FTP.Archie.SMTP.NNTP等.另外,HTTP协议还可用于名字服务器和分布式对象管 理.    HTTP的早期