IE下Ajax请求偶发12152连接超时错误浅析
昨天被分到一个Bug,公司某产品在IE下偶尔会随机出现请求挂起,等待30秒后弹出超时错误(错误状态码:12152)的问题,而在FireFox或Chrome则从没有这样的问题。据说这个问题已经困扰他们四年多了,一直混着。直到最近在某个大客户的新环境中频频出现,才不得不专门找人( )解决。接到这个Bug,感觉就是某个经典的IE Repost问题,之前一直没有机会详细了解,借这个机会翻了一些资料,在这里总结一下。
产生原因
现在的HTTP连接,特别是HTTPS连接,为了提高性能,几乎全部采用Keep-Live模式。也就是连接建立起来后会存活一段时间(数秒到数十秒不等),这段时间内的请求会重用这个连接。根据HTTP 1.1 (RFC 2616)标准,连接路径上的代理服务器,负载均衡服务器,应用服务器等节点都可以随时中止这个连接。一般情况下如果网速较快,连接被中止时浏览器会立刻知道,下一个请求就建立新连接,不会出现明显问题。但在网速不佳的环境中,会出现一种边界场景:浏览器发出了一个请求,但在请求还没到服务器前,服务器端认为连接超时,中止了连接。这时浏览器会立刻得到一个请求超时错误,根据RFC 2616标准,浏览器这时应该无须用户干预,自动重新建立新的连接并且重发之前的请求。各大浏览器都遵循了这个规范,因此在FireFox或Chrome下,用户根本不会意识到有Keep-Live连接超时这回事。
This means that clients, servers, and proxies MUST be able to recover from asynchronous close events. Client software SHOULD reopen the transport connection and retransmit the aborted sequence of requests without user interaction so long as the request sequence is idempotent (see section 9.1.2).
但,IE的XMLHttpRequest实现(某COM组件)在重发请求时有个低级错误:只会重发header部分,而忘了重发原来请求的内容部分!并且,在header中,仍然包含了原来请求中的content length (内容大小)数据。服务器在接收到这个请求header后,就开始望眼欲穿地等待指定大小的请求内容,而IE根本不会继续发送任何内容。于是,一段时间后,用户收到了一个真正的连接超时错误。
这个问题,根据微软官方文档,确认在IE6-9中都存在,IE10和后续版本到底如何(以及IE10的兼容模式下到底如何),我暂时还没找到明确文档。
如果你已经开始惊叹IE的弱智,别急,这只是开胃菜,更无厘头的在后面。
微软在收到用户投诉后,发了一个hotfix ( http://support.microsoft.com/kb/895954/en-us ),题目翻译过来大概是《当你使用IE或其他程序重发post请求时,只有请求头会被发送》,全文没有任何一处提到12152错误码和上面的错误现象。最开始也不知道是哪位天才把上述问题和这个hotfix联系起来的。
在IE6下,用户需要下载安装这个hotfix。而IE7-9,则已经内置了这个hotfix。但是————这个hotfix默认是禁用的,用户需要手动在注册表中添加一个名为 FEATURE_SKIP_POST_RETRY_ON_INTERNETWRITEFILE_KB895954 ( !!!!) 的项目来开启它。具体操作可以参考 http://support.microsoft.com/kb/895954/zh-cn 的《如何启用此修补程序》一节,复制如下: