转:http://www.cnblogs.com/cxd4321/archive/2012/01/30/2331621.html
Expect:100-Continue & HTTP 417 Expectation
背景:今天调试火车票查询的代码,发现一个奇怪的事情,如果使用公司本地的代理,那么一切正常,如果使用的是公司台湾的代理,那么将出现以下错误:“The remote server returned an error.(417) Unkown”。很是奇怪啊,为什么换了服务器后,效果完全相反。反复查阅代码最终找出关键点,HTTP协议。
1.分析bug的原因
看着VS2010的错误信息:(417) Unkown。有一点经验的人就会联想到HTTP200啊,等等之类的东西。那个HTTP协议中的内容那么多,到时是哪一个属性出现的问题呢,我决定用wireshark抓包,进行比较。对比结果:用台湾代理时,抓包的结果中多处了一下字符串“Expect:100-Continue”。(wireshark使用)
于是查看关于HTTP1.1的相关网页。HTTP1.1协议中文版-RFC2616和 HTTP1.1协议英文版-RFC2616
在上两篇文章中可以看到以下一段描述:
The purpose of the 100 (Continue) status (see section 10.1.1) is to allow a client that is sending a request message with a request body to determine if the origin server is willing to accept the request (based on the request headers) before the client sends the request body. In some cases, it might either be inappropriate or highly inefficient for the client to send the body if the server will reject the message without looking at the body.
(100状态码(继续,见10.1.1节)的目的在于允许客户端判定服务器是否愿意接受客户端发来的消息主体(基于请求头域)在客户端发送此请求消息主体前。 在有些情况下,如果服务器拒绝查看消息主体,这时客户端发送消息主体是不合适的或会降低效率。)
就是说 Expect:100-Continue的作用是,设定Client 和 Server在Post数据前需要进行 “请求头域” 的数据匹配,相当于是握手。如果匹配则开始进行body 的内容,Post数据。否则,报错(417) Unkown。
说道这里看似原因已经找到。别急,再仔细看看RFC2616,里面有这样一段话:
“
Requirements for HTTP/1.1 proxies:
- If a proxy receives a request that includes an Expect request- header field with the "100-continue" expectation, and the proxy either knows that the next-hop server complies with HTTP/1.1 or higher, or does not know the HTTP version of the next-hop server, it MUST forward the request, including the Expect header field. - If the proxy knows that the version of the next-hop server is HTTP/1.0 or lower, it MUST NOT forward the request, and it MUST respond with a 417 (Expectation Failed) status. - Proxies SHOULD maintain a cache recording the HTTP version numbers received from recently-referenced next-hop servers. - A proxy MUST NOT forward a 100 (Continue) response if the request message was received from an HTTP/1.0 (or earlier) client and did not include an Expect request-header field with the "100-continue" expectation. This requirement overrides the general rule for forwarding of 1xx responses (see section ”
(对HTTP/1.1代理服务器的要求:
--- 若代理服务器接到一个请求,此请求包含值为"100-continue"的Expect请求头域,并且代理服务器可能知道下一站点的服务器遵循HTTP/1.1或更高版协议,或者不知道下一站点服务器的HTTP版本,那么它必须包含此Expect头域来转发此请求。
--- 若代理服务器知道下一站点服务器版本是HTTP/1.0或更低,则它不能转发此请求,并且它必须以417(期望失败)状态响应。
--- 代理服务器应当维护一个缓存,以记录最近访问下一站点服务器的HTTP版本号。
--- 若接收到的请求来自于版本是HTTP/1.0(或更低)的客户端,并且此请求不含值为"100-continue"的Expect请求头域,那么代理服务器不能转发100(继续)响应。)
由于我们使用的是代理服务器,那个还有一种原因不能忽略,就是如果目标网页的HTTP的版本号为1.0或之前的版本,而代理服务器的本版为1.1或以上。这么这是,代理服务器将不会转发我们的Post请求,并报错(417) Unkown。
再看wireshark的包信息,其中明确可以看出,协议的版本号为HTTP1.1。这样,我们基本上可以确定(417) Unkown的原因:
握手失败,请求头域类型不匹配。
2. 请求头域
问题一个接一个,在看看什么是上一节中提到的请求头域,点这里。在该文中有详细的介绍,我就不班门弄斧了。
3.解决方法
可以手动的设置config
<configuration>
<system.net>
<settings>
<servicePointManager expect100Continue="false" />
</settings>
</system.net>
</configuration>
详情见 MSDN。主要讲的是,手动设置Expect的值为false,即不进行握手,而直接Post数据。
4. 进一步分析
在C#中的HttpWebRequest 类可以设置请求头域的相关值。如Expect属性就是设置请求头域中Expect的值。但是你可以测试发现,怎么修改都无法控制Client 和 Server是否进行握手。原因在哪呢?问题虽然解决了,但是新的又来了。为什么?求高手赐教。