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协议中文版-RFC2616HTTP1.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是否进行握手。原因在哪呢?问题虽然解决了,但是新的又来了。为什么?求高手赐教。

/// <summary>
        /// 点评奖金返现
        /// </summary>
        /// <param name="currentAnchor">跟踪点</param>       
        /// <param name="orderSerialId">订单Id</param>
        /// <param name="memberId">会员Id</param>
        /// <param name="ip">发送请求IP</param>
        /// <param name="amount">点评奖金金额</param>
        /// <returns></returns>
        private static bool SaveFinanceAmountFromInterface(HotelOrderAnchorEnum currentAnchor, string orderSerialId, int memberId, string ip, decimal amount)
        {
            //签名
            string serviceUrl = ConfigurationManager.AppSettings["FinanceServicesURL"];//接口地址
            string token = ConfigurationManager.AppSettings["FinanceServicesToken"];//接口账号           
            string servicesAction = ConfigurationManager.AppSettings["FinanceServicesAction"];//服务器名称

string errorMessage = string.Empty;
            bool bl = false;
            try
            {
                #region 构造请求XML
                StringBuilder XmlDoc = new StringBuilder();
                XmlDoc.AppendLine("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
                XmlDoc.AppendLine("<req><head>");
                XmlDoc.AppendFormat("<token>{0}</token>", token);//账号
                XmlDoc.AppendFormat("<action>{0}</action></head>", servicesAction);//服务名称
                XmlDoc.AppendLine("<body>");
                XmlDoc.AppendFormat("<mid>{0}</mid>", memberId);//会员ID
                XmlDoc.AppendFormat("<amount>{0}</amount>", amount);//奖金金额
                XmlDoc.AppendFormat("<oper>{0}</oper>", "系统");//操作人姓名
                XmlDoc.AppendFormat("<opdis>{0}</opdis>", "去哪儿点评奖金返现");//操作描述
                XmlDoc.AppendFormat("<orderno>{0}</orderno>", orderSerialId);//订单号                              
                XmlDoc.AppendFormat("<ip>{0}</ip>", ip);//请求IP                              
                XmlDoc.AppendFormat("<project>{0}</project>", 1002);//项目编号                              
                XmlDoc.AppendLine("</body></req>");
                #endregion
                string requestXML = XmlDoc.ToString();
                System.Net.HttpWebRequest myRequest = System.Net.WebRequest.Create(serviceUrl) as System.Net.HttpWebRequest;
                myRequest.ServicePoint.Expect100Continue = false;
                myRequest.ServicePoint.UseNagleAlgorithm = false;
                myRequest.Headers.Clear();  //清除http请求头信息
                myRequest.Timeout = 12000;   //超时时间12秒
                myRequest.Method = "POST";  //POST方式提交
                myRequest.ContentType = "text/xml;"; //编码格式
                byte[] data = System.Text.Encoding.GetEncoding("utf-8").GetBytes(requestXML);
                myRequest.ContentLength = data.Length;
                using (System.IO.Stream datasteam = myRequest.GetRequestStream())
                {
                    datasteam.Write(data, 0, data.Length);
                    datasteam.Close();
                }

string response = string.Empty;
                System.Net.HttpWebResponse myResponse = myRequest.GetResponse() as System.Net.HttpWebResponse;
                using (System.IO.StreamReader reader = new System.IO.StreamReader(
                                                       myResponse.GetResponseStream()
                                                     , System.Text.Encoding.GetEncoding("utf-8")))
                {
                    response = reader.ReadToEnd();
                    reader.Close();
                    System.Xml.XmlDocument objXML = new System.Xml.XmlDocument();
                    objXML.LoadXml(response);
                    System.Xml.XmlNode Node = objXML.SelectSingleNode("result/head/code");
                    if (Node != null && Node.InnerText == "0")//成功
                    {
                        bl = true;
                    }
                    else
                    {
                        bl = false;
                        System.Xml.XmlNode ErrNode = objXML.SelectSingleNode("result/head/codemark");
                        if (ErrNode != null && ErrNode.InnerText != "")
                        {
                            errorMessage = "对会员编号:<font color='green'>" + memberId + "</font>,"
                                + "订单号:<font color='green'>" + orderSerialId + "</font>,"
                                + "操作金额为:<font color='green'>" + amount + "</font>元,"
                                + "执行<font color='Green'>去哪儿点评奖金返现</font>类型操作时,"
                                + "发生错误:\r\n<font color='red'>" + ErrNode.InnerText + "</font>";
                        }
                        else
                        {
                            errorMessage = "对会员编号:<font color='green'>" + memberId + "</font>,"
                                + "订单号:<font color='green'>" + orderSerialId + "</font>,"
                                + "操作金额为:<font color='green'>" + amount + "</font>元,"
                                + "执行<font color='Green'>去哪儿点评奖金返现</font>类型操作时,"
                                + "发生错误:\r\n<font color='red'>" + "调用接口失败!" + "</font>";
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                errorMessage = "对会员编号:<font color='green'>" + memberId + "</font>,"
                               + "订单号:<font color='green'>" + orderSerialId + "</font>,"
                               + "操作金额为:<font color='green'>" + amount + "</font>元,"
                               + "执行<font color='Green'>去哪儿点评奖金返现</font>类型操作时,"
                               + "发生异常:\r\n<font color='red'>" + ex.Message + "#" + ex.StackTrace + "</font>";
            }
            finally
            {
                #region 发送错误邮件
                if (errorMessage != string.Empty)
                {
                    EmailSendService.SendExceptionEmailOfPriority("调用去哪儿点评奖金返现接口失败", errorMessage, EmailPriorityEnum.High);
                }
                #endregion
            }
            return bl;
        }

Expect:100-Continue & HTTP 417 Expectation[转]的更多相关文章

  1. HTTP/1.1 100 Continue - I 服了 You

    今天用 c 的 socket() 模拟发送http请求:上传一张图片到服务器. 在本地测试,本地电脑: xp, iis5.1 经过半天时间对 http post file 协议的了解,代码已经写好,测 ...

  2. 关于HTTP请求返回417 “Expectation Failed”

    在使用HttpClient默认情况下做POST的时候, HttpClient并不会直接就发起POST请求, 而是会分为俩步, 1.发送一个请求, 包含一个Expect:100-continue, 询问 ...

  3. php curl报错:417 - Expectation Failed

    当我在post提交的数据增加一段内容后会报错:417 - Expectation Failed. 查资料发现在使用curl做POST时,当post的数据大于1024字节时,curl并不会直接发起pos ...

  4. 【网络编程】——Lighttpd 返回HTTP/1.1 417 Expectation Failed

    最近在使用python 的 pcurl 发送 post 请求到服务端的时候[服务端使用的服务是Lighttpd],发现只要 post 请求的数据超过 1024 之后,就会返回如下错误: * Hostn ...

  5. visual studio 远程服务器返回了意外响应:(417)expectation failed

    解决方法: 修改devenv.exe.config文件,添加 <servicePointManager expect100Continue="false" /> C:\ ...

  6. 爬虫浅谈一:一个简单c#爬虫程序

    这篇文章只是简单展示一个基于HTTP请求如何抓取数据的文章,如觉得简单的朋友,后续我们再慢慢深入研究探讨. 图1: 如图1,我们工作过程中,无论平台网站还是企业官网,总少不了新闻展示.如某天产品经理跟 ...

  7. c# 获取网页的爬虫程序

    转载于:https://www.cnblogs.com/wzk153/p/9145684.html HtmlAgilityPack相关详解: https://www.cnblogs.com/asxin ...

  8. Node6.9.2 —— Http官网笔记整理

    欢迎指导与讨论 : ) 序章 本文概要:http.Agent代理.http.ClientRequest客户端请求.http.server服务器.http.ServerResponse服务器相应.htt ...

  9. C# 提交网页请求时出现如下错误: System.Net.WebException: 操作超时

    原因一: 连接超时时间 Timeout 以及写入Post数据超时时间 ReadWriteTimeout 设置得太短,一般要设置大于6000ms. 原因二: Expect100Continue 属性的值 ...

随机推荐

  1. 一道C语言面试题:判断字串是否可以通过重新排列字母使之对称

    题目:输入一个字符串,如“adcaeceeed”,判断是否可以通过重新排列使之可以输出对称字符串,如本例可以输出“adceeeecda”,返回True. 来源:某500强企业面试题目 思路:扫描字串, ...

  2. QT:窗口最小化时显示一个小浮标

    有些窗口在自身最小化时要在桌面上显示一个小浮标,让用户利用这个小浮标进行各种操作(例如迅雷的悬浮窗一样),我试着用QT实现一下这个功能. PS:本来以为这个功能很简单,却搞了我两个晚上,泪奔... 思 ...

  3. Windows Components Reference(Windows组件参考)

    原文 http://www.msfn.org/board/topic/127287-windows-components-reference/ green means the component na ...

  4. JS:函数多个参数默认值指定

    函数有一个参数时,以往这样定义(参数为p1): function mfun(p1){ … } 当需要为p1设定一个默认值时 function mfun(p1){ if(p1===undefined) ...

  5. 从txt中读入数据到数组中(fscanf)

    C-sources: #include<stdio.h> int main() { FILE* fp; //定义一个文件 fp = fopen("p5.txt",&qu ...

  6. 【LeetCode练习题】Reverse Linked List II

    Reverse Linked List II Reverse a linked list from position m to n. Do it in-place and in one-pass. F ...

  7. #include <boost/array.hpp>

    Boost的array,元素可以是std::string #include <iostream> #include <string> #include <boost/ar ...

  8. Python学习入门基础教程(learning Python)--5 Python文件处理

    本节主要讨论Python下的文件操作技术. 首先,要明白为何要学习或者说关系文件操作这件事?其实道理很简单,Python程序运行时,数据是存放在RAM里的,当Python程序运行结束后数据从RAM被清 ...

  9. 不使用TNS直连数据库的三种方式

    1.在当前目录下新建tnsnames.ora文件 如windows环境下,在C:\Users\Administrator目录下新建tnsnames.ora文件,内容如下:test =(descript ...

  10. 【最大流】【HDU3338】【Kakuro Extension】

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3338 题目大意:填数字,使白色区域的值得和等于有值得黑色区域的相对应的值,用网络流来做 题目思路:增加 ...