Windows中的WinHttp库提供了比较完善的访问HTTP资源的接口API,一次在使用WinHTTP爬取QQ邮箱过程中,WinHttpReceiveResponse的调用总是失败,于是对此问题进行跟踪。
开始分析QQ邮箱的HTTP交互协议时,用到了代理工具Fiddler,方便查看网络请求数据包内容,同时代码中也设置使用Fiddler提供的HTTP代理,这样方便查看代码发送/接收的数据包与浏览器发送/接收的数据包之间的差别,方便定位问题。
一切似乎都很顺利,很快代码开发完成,可以正常爬取QQ邮箱邮件数据。但当在代码中取消Fiddler代理时,却出现无法爬取邮件的问题,其它请求都很正常,只有下载邮件的链接无法请求成功,问题出现在调用WinHttpReceiveResponse上,该函数返回失败,错误码12152,查阅相关错误描述:
ERROR_WINHTTP_INVALID_SERVER_RESPONSE | The server response could not be parsed. |
意思是服务器返回的Response的格式不对,无法解析。再次使用Fiddler代理,又可以正常爬取邮件。查看Fiddler日志记录发现如下:
所以,QQ邮箱服务器返回的Response Header信息格式确实有问题,缺少了一个字段名,通过Chrome浏览器抓包查看也证实了这一点(需要点击View source查看原始数据):
再用WireShark抓包查看,也是存在这样的问题:
但通过Firefox浏览器抓包查看并未出现(即使开启查看原始数据):
可见,Firefox的原始数据并非真的原始数据,渣!
通过Windbg + IDA的一系列跟踪调查,最后定位问题出现在WinHttp.dll依赖的webio.dll模块中。
也就是说,当webio.dll解析Response响应头时,当出现一行以“:”冒号开头的字符串时,获取的字段名长度是0,导致解析失败,返回错误码0x3A,经过WINHTTP_ERROR_FROM_WIN32函数对0x3A错误码进行转换,得到错误码12152。
查阅相关解决方案,通过WinHttpSetOption设置如下属性:
WINHTTP_OPTION_UNSAFE_HEADER_PARSING
This option is reserved for internal use and should not be called.
但我的设置并未解决该问题。
最后通过QQ邮箱其它URL接口,获取到了邮件数据。