分析-WinHttpReceiveResponse失败问题追踪

8 篇文章 0 订阅
2 篇文章 0 订阅

        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接口,获取到了邮件数据。

HTML 5 Web Sockets is a powerful and effective technique for real-time information processing. There exists many techniques such as Poling, Long Poling and Streaming that are said to be better earier days. With web sockets, it shows a better outcome for the end user as well as a proper utilization of the server bandwidth. WebSocket is a web technology providing full-duplex communications channels over a single TCP connection. The WebSocket protocol was standardized by the IETF as RFC 6455 in 2011, and the WebSocket API for in Web IDL is being standardized by the W3C.WebSocket is designed to be implemented in web browsers and web servers, but it can be used by any client or server application. The WebSocket Protocol is an independent TCP-based protocol. Its only relationship to HTTP is that its handshake is interpreted by HTTP servers as an Upgrade request. The WebSocket protocol makes possible more interaction between a browser and a web site, facilitating live content and the creation of real-time games. This is made possible by providing a standardized way for the server to send content to the browser without being solicited by the client, and allowing for messages to be passed back and forth while keeping the connection open. In this way a two-way (bi-directional) ongoing conversation can take place between a browser and the server. A similar effect has been achieved in non-standardized ways using stop-gap technologies such as Comet.In addition, the communications are done over TCP port number 80, which is of benefit for those environments which block non-standard Internet connections using a firewall. WebSocket protocol is currently supported in several browsers including Google Chrome, Internet Explorer, Firefox, Safari and Opera. WebSocket also requires web applications on the server to support it. Here goes a comparison of polling vs Web Sockets.
WinHTTP提供以下功能: WinHttpAddRequestHeaders 向HTTP请求句柄添加一个或多个HTTP请求标头。 WinHttpCheckPlatform 确定WinHTTP是否支持当前平台。 WinHttpCloseHandle 关闭单个 HINTERNET句柄。 WinHttpConnect 指定HTTP请求的初始目标服务器。 WinHttpCrackUrl 将URL分为其组成部分,例如主机名和路径。 WinHttpCreateProxyResolver 创建WinHttpGetProxyForUrlEx使用的句柄。 WinHttpCreateUrl 从组件部分创建URL,例如主机名和路径。 WinHttpDetectAutoProxyConfigUrl 查找代理自动配置(PAC)文件的URL。此功能报告PAC文件的URL,但不下载该文件。 WinHttpFreeProxyResult 释放从以前的调用WinHttpGetProxyResult检索的数据。 WinHttpGetDefaultProxyConfiguration 从注册表中检索默认的WinHTTP代理配置。 WinHTTPGetIEProxyConfigForCurrentUser 获取当前用户的Internet Explorer(IE)代理配置。 WinHttpGetProxyForUrl 检索指定URL的代理信息。 WinHttpGetProxyForUrlEx 检索指定URL的代理信息。 WinHttpGetProxyResult 检索到调用的结果WinHttpGetProxyForUrlEx。 WinHttpOpen 初始化应用程序对WinHTTP功能的使用。 WinHttpOpenRequest 创建HTTP请求句柄。 WinHttpQueryAuthSchemes 返回服务器支持的授权方案。 WinHttpQueryDataAvailable 返回可立即与读取数据的字节数 WinHttpReadData。 WinHttpQueryHeaders 检索与HTTP请求相关联的头信息。 WinHttpQueryOption 在指定的句柄上查询Internet选项。 WinHttpReadData 从WinHttpOpenRequest函数打开的句柄中读取数据 。 WinHttpReceiveResponse 结束由WinHttpSendRequest启动的HTTP请求 。 WinHttpResetAutoProxy 重置自动代理。 WinHttpSendRequest 将指定的请求发送到HTTP服务器。 WinHttpSetCredentials 将所需的授权凭证传递给服务器。 WinHttpSetDefaultProxyConfiguration 在注册表中设置默认的WinHTTP代理配置。 WinHttpSetOption 设置Internet选项。 WinHttpSetStatusCallback 设置WinHTTP可以在操作过程中进行调用的回调函数。 WinHttpSetTimeouts 设置涉及HTTP事务的各种超时。 WinHttpTimeFromSystemTime 根据HTTP版本1.0规范格式化日期和时间。 WinHttpTimeToSystemTime 获取HTTP时间/日期字符串并将其转换为 SYSTEMTIME结构。 WinHttpWriteData 将请求数据写入HTTP服务器。 WinHttpWebSocketClose 关闭WebSocket连接。 WinHttpWebSocketCompleteUpgrade 完成由WinHttpSendRequest启动的WebSocket握手。 WinHttpWebSocketQueryCloseStatus 获取服务器发送的关闭状态。 WinHttpWebSocketReceive 从WebSocket连接接收数据。 WinHttpWebSocketSend 通过WebSocket连接发送数据。 WinHttpWebSocketShutdown 向WebSocket连接发送关闭框架
以下是一个使用WinHTTP函数库的示例代码,用于连接到指定的URL并查询错误文本: ```c++ #include <windows.h> #include <winhttp.h> #include <iostream> #pragma comment(lib, "winhttp.lib") int main() { // 创建一个WinHTTP会话句柄 HINTERNET hSession = WinHttpOpen(L"MyApp", WINHTTP_ACCESS_TYPE_NO_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0); if (hSession) { // 打开一个连接 HINTERNET hConnect = WinHttpConnect(hSession, L"www.example.com", INTERNET_DEFAULT_HTTPS_PORT, 0); if (hConnect) { // 创建一个请求句柄 HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"GET", L"/", NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE); if (hRequest) { // 发送请求 if (WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0)) { // 接收响应 if (WinHttpReceiveResponse(hRequest, NULL)) { // 获取响应状态码 DWORD dwStatusCode = 0; DWORD dwSize = sizeof(DWORD); if (WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, NULL, &dwStatusCode, &dwSize, NULL)) { std::cout << "Status code: " << dwStatusCode << std::endl; // 如果响应状态码不是200,则查询错误文本 if (dwStatusCode != 200) { DWORD dwError = GetLastError(); if (dwError == ERROR_WINHTTP_INVALID_SERVER_RESPONSE) { // 查询服务器响应的错误文本 DWORD dwErrorSize = 0; if (WinHttpQueryDataAvailable(hRequest, &dwErrorSize)) { LPSTR szError = new char[dwErrorSize + 1]; ZeroMemory(szError, dwErrorSize + 1); if (WinHttpReadData(hRequest, szError, dwErrorSize, NULL)) { std::cout << "Error text: " << szError << std::endl; } delete[] szError; } } else { std::cout << "Error code: " << dwError << std::endl; } } } } } // 关闭请求句柄 WinHttpCloseHandle(hRequest); } // 关闭连接句柄 WinHttpCloseHandle(hConnect); } // 关闭会话句柄 WinHttpCloseHandle(hSession); } return 0; } ``` 在上面的代码中,我们先创建一个WinHTTP会话句柄,然后使用 `WinHttpConnect` 函数打开一个连接,并使用 `WinHttpOpenRequest` 函数创建一个请求句柄。然后,我们使用 `WinHttpSendRequest` 发送请求,并使用 `WinHttpReceiveResponse` 接收响应。 接着,我们使用 `WinHttpQueryHeaders` 函数获取响应状态码,并检查是否为200。如果不是200,则使用 `GetLastError` 函数获取错误代码,并根据错误代码执行相应的操作。在这个例子中,如果错误代码为 `ERROR_WINHTTP_INVALID_SERVER_RESPONSE`,则使用 `WinHttpQueryDataAvailable` 和 `WinHttpReadData` 函数查询服务器响应的错误文本。 请注意,在实际开发中,你可能需要对不同的错误代码执行不同的处理操作,并且需要更详细的错误信息来调试问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值