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. 构造函时和this指针

    通常this指针在对象构造完毕后才完全生成,而在构造函数执行过程中,对象还没有完全生成,所以this指针也是没有完全生成的,在构造函数中使用this指针会存在问题,应该尽量避免. 构造函数中可以访问对 ...

  2. 窥探 kernel --- 进程调度的目标,nice值,静态优先级,动态优先级,实时优先级

    http://blog.chinaunix.net/uid-24227137-id-3595610.html 窥探 kernel --- 进程调度的目标,nice值,静态优先级,动态优先级,实时优先级 ...

  3. public void Delete<T>(List<T> EntityList) where T : class, new() 这是什么意思

    就是说T必须是一个类(class)类型,不能是结构(structure)类型. 这是类型参数约束,.NET支持的类型参数约束有以下五种: where T : struct                ...

  4. C++求二叉树的最大高度差

    #include <iostream> #include <string.h> using namespace std; template<typename Type&g ...

  5. POJ 3468 A Simple Problem with Integers(线段树区间求和)

    Description You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. On ...

  6. javascript 中 offsetWidth,clientWidth;offsetHeight,clientHeight的区别

    javascript 中 offsetWidth 是对象的可见宽度,包滚动条等边线,会随窗口的显示大小改变 clientWidth.offsetWidth.clientHeight区别IE6.0.FF ...

  7. css系列教程--文本

    text-align:规定文本的水平对齐方式.left/right/center/justify(两端对齐)/inherit(继承) text-decoration:添加到文本的效果.下划线,中间线等 ...

  8. C++中socket编程

    原文:http://blog.csdn.net/cuiran/article/details/5854794 Server端 #include <WINSOCK2.H> #include ...

  9. web.config configSections自定义section

    1.web.config 配置文件设置 <configSections> <!-- For more information on Entity Framework configur ...

  10. getActionBar()空指针异常

    网上的各种解决方案已经不少了,但是不适合于我的,谷歌一种新的解决方案 you can directly specify it in manifest file 1 2 3 4 <applicat ...