WinInet实现HTTPS请求时HttpSendRequest返回成功但无有效请求问题
问题排查与修复
核心问题分析
- 字符集API混用:代码同时使用宽字符版本
InternetOpenW和ANSI版本InternetConnect、HttpOpenRequest,WinInet要求同一会话内API字符集必须统一,参数解析错误会导致请求无法正确发送。 - 错误处理逻辑错误:用
perror处理WinInet错误是错误的,perror仅适用于POSIX系统调用,Win32 API错误需通过GetLastError()获取,这导致你误判HttpSendRequest执行成功,实际可能存在隐藏错误。 - HTTPS证书验证拦截:WinInet默认严格验证SSL证书,若Flask服务器用自签名证书,WinInet会在握手阶段失败,请求无法到达服务器。
- nc乱码的原因:nc收到的乱码是HTTPS加密后的正常流量,nc不支持SSL解密,因此无法显示明文。
修复步骤
1. 统一字符集API
将所有WinInet API改为宽字符版本,避免编码冲突:
void connect(LPCWSTR hostname, LPCWSTR endpoint) { LPCWSTR userAgent = L"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:138.0) Gecko/20100101 Firefox/138.0"; HINTERNET hInternet = InternetOpenW(userAgent, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); if (hInternet == NULL) { printf("InternetOpenW failed, error: %lu\n", GetLastError()); exit(EXIT_FAILURE); } HINTERNET hConnect = InternetConnectW(hInternet, hostname, INTERNET_DEFAULT_HTTPS_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0); if (hConnect == NULL) { printf("InternetConnectW failed, error: %lu\n", GetLastError()); InternetCloseHandle(hInternet); exit(EXIT_FAILURE); } LPCWSTR accept[] = {L"application/json", NULL}; // 添加证书忽略标志(仅测试环境使用) HINTERNET hRequest = HttpOpenRequestW(hConnect, L"POST", endpoint, L"HTTP/1.1", NULL, accept, INTERNET_FLAG_SECURE | INTERNET_FLAG_IGNORE_CERT_CN_INVALID | INTERNET_FLAG_IGNORE_CERT_DATE_INVALID, 0); if (hRequest == NULL) { printf("HttpOpenRequestW failed, error: %lu\n", GetLastError()); InternetCloseHandle(hConnect); InternetCloseHandle(hInternet); exit(EXIT_FAILURE); } LPCWSTR headers = L"Content-Type: application/json\r\n"; if (HttpAddRequestHeadersW(hRequest, headers, -1L, HTTP_ADDREQ_FLAG_ADD) == FALSE) { printf("HttpAddRequestHeadersW failed, error: %lu\n", GetLastError()); InternetCloseHandle(hRequest); InternetCloseHandle(hConnect); InternetCloseHandle(hInternet); exit(EXIT_FAILURE); } std::wstring data = L"{\"sessionId\": \"importants.txt\", \"authToken\": \"Nothing Important Here.\"}"; if (HttpSendRequestW(hRequest, NULL, -1L, (LPVOID)data.c_str(), data.length() * sizeof(wchar_t)) == FALSE) { printf("HttpSendRequestW failed, error: %lu\n", GetLastError()); InternetCloseHandle(hRequest); InternetCloseHandle(hConnect); InternetCloseHandle(hInternet); exit(EXIT_FAILURE); } std::wstring response; wchar_t buffer[4096]; DWORD bytesRead; while (InternetReadFile(hRequest, buffer, sizeof(buffer) - sizeof(wchar_t), &bytesRead) && bytesRead > 0) { buffer[bytesRead / sizeof(wchar_t)] = L'\0'; response += buffer; } std::wcout << response << std::endl; // 按正确顺序关闭句柄:请求→连接→根会话 InternetCloseHandle(hRequest); InternetCloseHandle(hConnect); InternetCloseHandle(hInternet); }
2. 修正错误处理
替换所有perror为GetLastError()输出错误码,能精准定位API调用失败的具体原因,比如证书验证错误、连接超时等。
3. 处理Flask证书问题
- 测试环境:代码中添加的
INTERNET_FLAG_IGNORE_CERT_CN_INVALID和INTERNET_FLAG_IGNORE_CERT_DATE_INVALID可跳过自签名证书验证。 - 生产环境:必须使用合法SSL证书,或在Wine中导入自签名证书到系统信任存储。
4. 调整句柄关闭顺序
原代码关闭句柄顺序错误,需先关闭请求句柄hRequest,再关闭连接句柄hConnect,最后关闭根会话句柄hInternet,避免资源泄漏或请求中断。
5. 编译命令保持不变
x86_64-w64-mingw32-g++ harvester.cpp -o harvester.exe -l wininet -static-libstdc++ -static-libgcc
额外注意事项
- 若Flask服务器用HTTP而非HTTPS,需将端口改为80,移除所有SSL相关标志。
- 生产环境禁止忽略证书验证,否则会引发安全风险。
内容的提问来源于stack exchange,提问作者Sayan Ray
相关产品推荐
相关产品推荐

