Tests: add live integration test (optional via PRIVATEBIN_IT), fix WinHTTP host/port + TLS opts, robust JSON parser (meta.time_to_live), CTest wiring; Add LLVM/clang-cl coverage option and docs; add build_thinkpad.bat; README updates
This commit is contained in:
@ -1,11 +1,30 @@
|
||||
#include "http_client.h"
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#ifdef WINDOWS
|
||||
#include <windows.h>
|
||||
#include <winhttp.h>
|
||||
#pragma comment(lib, "winhttp.lib")
|
||||
static std::string last_winhttp_error(const char* where) {
|
||||
DWORD err = GetLastError();
|
||||
LPVOID lpMsgBuf = nullptr;
|
||||
FormatMessageA(
|
||||
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL,
|
||||
err,
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
(LPSTR)&lpMsgBuf,
|
||||
0, NULL);
|
||||
std::ostringstream os;
|
||||
os << "[WinHTTP] " << where << " failed, error=" << err;
|
||||
if (lpMsgBuf) {
|
||||
os << ": " << (char*)lpMsgBuf;
|
||||
LocalFree(lpMsgBuf);
|
||||
}
|
||||
return os.str();
|
||||
}
|
||||
|
||||
static std::wstring utf8_to_wide(const std::string& s) {
|
||||
if (s.empty()) return std::wstring();
|
||||
@ -56,14 +75,28 @@ bool HttpClient::get(const std::string& url, std::string& response) {
|
||||
WINHTTP_NO_PROXY_BYPASS, 0);
|
||||
|
||||
if (!hSession) {
|
||||
std::cerr << last_winhttp_error("WinHttpOpen(GET)") << std::endl;
|
||||
return false;
|
||||
}
|
||||
// Force modern TLS versions to avoid handshake failures on some hosts
|
||||
DWORD protocols = WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 |
|
||||
WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2 | 0x00002000 /* TLS1_3 if available */;
|
||||
WinHttpSetOption(hSession, WINHTTP_OPTION_SECURE_PROTOCOLS, &protocols, sizeof(protocols));
|
||||
// Disable HTTP/2 if it causes issues
|
||||
#ifdef WINHTTP_DISABLE_FEATURE_HTTP2
|
||||
DWORD features = WINHTTP_DISABLE_FEATURE_HTTP2;
|
||||
WinHttpSetOption(hSession, WINHTTP_OPTION_DISABLE_FEATURE, &features, sizeof(features));
|
||||
#endif
|
||||
|
||||
// Specify an HTTP server
|
||||
HINTERNET hConnect = WinHttpConnect(hSession, urlComp.lpszHostName,
|
||||
urlComp.nPort, 0);
|
||||
// Specify an HTTP server (host must be null-terminated; urlComp provides length)
|
||||
std::wstring host = (urlComp.lpszHostName && urlComp.dwHostNameLength > 0)
|
||||
? std::wstring(urlComp.lpszHostName, urlComp.dwHostNameLength)
|
||||
: std::wstring();
|
||||
INTERNET_PORT port = urlComp.nPort ? urlComp.nPort : ((urlComp.nScheme == INTERNET_SCHEME_HTTPS) ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT);
|
||||
HINTERNET hConnect = WinHttpConnect(hSession, host.c_str(), port, 0);
|
||||
|
||||
if (!hConnect) {
|
||||
std::cerr << last_winhttp_error("WinHttpConnect(GET)") << std::endl;
|
||||
WinHttpCloseHandle(hSession);
|
||||
return false;
|
||||
}
|
||||
@ -86,7 +119,9 @@ bool HttpClient::get(const std::string& url, std::string& response) {
|
||||
WINHTTP_FLAG_SECURE : 0);
|
||||
// Set headers per API requirement
|
||||
LPCWSTR headers = L"X-Requested-With: JSONHttpRequest\r\nAccept: application/json";
|
||||
WinHttpAddRequestHeaders(hRequest, headers, -1L, WINHTTP_ADDREQ_FLAG_ADD);
|
||||
if (!WinHttpAddRequestHeaders(hRequest, headers, -1L, WINHTTP_ADDREQ_FLAG_ADD)) {
|
||||
std::cerr << last_winhttp_error("WinHttpAddRequestHeaders(GET)") << std::endl;
|
||||
}
|
||||
|
||||
if (!hRequest) {
|
||||
WinHttpCloseHandle(hConnect);
|
||||
@ -99,6 +134,7 @@ bool HttpClient::get(const std::string& url, std::string& response) {
|
||||
WINHTTP_NO_ADDITIONAL_HEADERS, 0,
|
||||
WINHTTP_NO_REQUEST_DATA, 0,
|
||||
0, 0)) {
|
||||
std::cerr << last_winhttp_error("WinHttpSendRequest(GET)") << std::endl;
|
||||
WinHttpCloseHandle(hRequest);
|
||||
WinHttpCloseHandle(hConnect);
|
||||
WinHttpCloseHandle(hSession);
|
||||
@ -107,6 +143,7 @@ bool HttpClient::get(const std::string& url, std::string& response) {
|
||||
|
||||
// End the request
|
||||
if (!WinHttpReceiveResponse(hRequest, NULL)) {
|
||||
std::cerr << last_winhttp_error("WinHttpReceiveResponse(GET)") << std::endl;
|
||||
WinHttpCloseHandle(hRequest);
|
||||
WinHttpCloseHandle(hConnect);
|
||||
WinHttpCloseHandle(hSession);
|
||||
@ -122,6 +159,7 @@ bool HttpClient::get(const std::string& url, std::string& response) {
|
||||
// Check for available data
|
||||
dwSize = 0;
|
||||
if (!WinHttpQueryDataAvailable(hRequest, &dwSize)) {
|
||||
std::cerr << last_winhttp_error("WinHttpQueryDataAvailable(GET)") << std::endl;
|
||||
WinHttpCloseHandle(hRequest);
|
||||
WinHttpCloseHandle(hConnect);
|
||||
WinHttpCloseHandle(hSession);
|
||||
@ -140,6 +178,7 @@ bool HttpClient::get(const std::string& url, std::string& response) {
|
||||
// Read the data
|
||||
ZeroMemory(pszOutBuffer, dwSize + 1);
|
||||
if (!WinHttpReadData(hRequest, (LPVOID)pszOutBuffer, dwSize, &dwDownloaded)) {
|
||||
std::cerr << last_winhttp_error("WinHttpReadData(GET)") << std::endl;
|
||||
delete[] pszOutBuffer;
|
||||
WinHttpCloseHandle(hRequest);
|
||||
WinHttpCloseHandle(hConnect);
|
||||
@ -189,14 +228,26 @@ bool HttpClient::post(const std::string& url, const std::string& data, std::stri
|
||||
WINHTTP_NO_PROXY_BYPASS, 0);
|
||||
|
||||
if (!hSession) {
|
||||
std::cerr << last_winhttp_error("WinHttpOpen(POST)") << std::endl;
|
||||
return false;
|
||||
}
|
||||
DWORD protocols2 = WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 |
|
||||
WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2 | 0x00002000;
|
||||
WinHttpSetOption(hSession, WINHTTP_OPTION_SECURE_PROTOCOLS, &protocols2, sizeof(protocols2));
|
||||
#ifdef WINHTTP_DISABLE_FEATURE_HTTP2
|
||||
DWORD features2 = WINHTTP_DISABLE_FEATURE_HTTP2;
|
||||
WinHttpSetOption(hSession, WINHTTP_OPTION_DISABLE_FEATURE, &features2, sizeof(features2));
|
||||
#endif
|
||||
|
||||
// Specify an HTTP server
|
||||
HINTERNET hConnect = WinHttpConnect(hSession, urlComp.lpszHostName,
|
||||
urlComp.nPort, 0);
|
||||
// Specify an HTTP server (ensure host is null-terminated)
|
||||
std::wstring host2 = (urlComp.lpszHostName && urlComp.dwHostNameLength > 0)
|
||||
? std::wstring(urlComp.lpszHostName, urlComp.dwHostNameLength)
|
||||
: std::wstring();
|
||||
INTERNET_PORT port2 = urlComp.nPort ? urlComp.nPort : ((urlComp.nScheme == INTERNET_SCHEME_HTTPS) ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT);
|
||||
HINTERNET hConnect = WinHttpConnect(hSession, host2.c_str(), port2, 0);
|
||||
|
||||
if (!hConnect) {
|
||||
std::cerr << last_winhttp_error("WinHttpConnect(POST)") << std::endl;
|
||||
WinHttpCloseHandle(hSession);
|
||||
return false;
|
||||
}
|
||||
@ -211,6 +262,7 @@ bool HttpClient::post(const std::string& url, const std::string& data, std::stri
|
||||
WINHTTP_FLAG_SECURE : 0);
|
||||
|
||||
if (!hRequest) {
|
||||
std::cerr << "[WinHTTP] WinHttpOpenRequest(POST) failed" << std::endl;
|
||||
WinHttpCloseHandle(hConnect);
|
||||
WinHttpCloseHandle(hSession);
|
||||
return false;
|
||||
@ -224,6 +276,7 @@ bool HttpClient::post(const std::string& url, const std::string& data, std::stri
|
||||
WINHTTP_ADDREQ_FLAG_ADD);
|
||||
|
||||
if (!bResults) {
|
||||
std::cerr << last_winhttp_error("WinHttpAddRequestHeaders(POST)") << std::endl;
|
||||
WinHttpCloseHandle(hRequest);
|
||||
WinHttpCloseHandle(hConnect);
|
||||
WinHttpCloseHandle(hSession);
|
||||
@ -236,6 +289,7 @@ bool HttpClient::post(const std::string& url, const std::string& data, std::stri
|
||||
WINHTTP_NO_ADDITIONAL_HEADERS, 0,
|
||||
(LPVOID)data.c_str(), (DWORD)data.length(),
|
||||
(DWORD)data.length(), 0)) {
|
||||
std::cerr << last_winhttp_error("WinHttpSendRequest(POST)") << std::endl;
|
||||
WinHttpCloseHandle(hRequest);
|
||||
WinHttpCloseHandle(hConnect);
|
||||
WinHttpCloseHandle(hSession);
|
||||
@ -244,6 +298,7 @@ bool HttpClient::post(const std::string& url, const std::string& data, std::stri
|
||||
|
||||
// End the request
|
||||
if (!WinHttpReceiveResponse(hRequest, NULL)) {
|
||||
std::cerr << last_winhttp_error("WinHttpReceiveResponse(POST)") << std::endl;
|
||||
WinHttpCloseHandle(hRequest);
|
||||
WinHttpCloseHandle(hConnect);
|
||||
WinHttpCloseHandle(hSession);
|
||||
@ -259,6 +314,7 @@ bool HttpClient::post(const std::string& url, const std::string& data, std::stri
|
||||
// Check for available data
|
||||
dwSize = 0;
|
||||
if (!WinHttpQueryDataAvailable(hRequest, &dwSize)) {
|
||||
std::cerr << last_winhttp_error("WinHttpQueryDataAvailable(POST)") << std::endl;
|
||||
WinHttpCloseHandle(hRequest);
|
||||
WinHttpCloseHandle(hConnect);
|
||||
WinHttpCloseHandle(hSession);
|
||||
@ -277,6 +333,7 @@ bool HttpClient::post(const std::string& url, const std::string& data, std::stri
|
||||
// Read the data
|
||||
ZeroMemory(pszOutBuffer, dwSize + 1);
|
||||
if (!WinHttpReadData(hRequest, (LPVOID)pszOutBuffer, dwSize, &dwDownloaded)) {
|
||||
std::cerr << last_winhttp_error("WinHttpReadData(POST)") << std::endl;
|
||||
delete[] pszOutBuffer;
|
||||
WinHttpCloseHandle(hRequest);
|
||||
WinHttpCloseHandle(hConnect);
|
||||
@ -326,14 +383,26 @@ bool HttpClient::delete_req(const std::string& url, const std::string& data, std
|
||||
WINHTTP_NO_PROXY_BYPASS, 0);
|
||||
|
||||
if (!hSession) {
|
||||
std::cerr << last_winhttp_error("WinHttpOpen(DEL)") << std::endl;
|
||||
return false;
|
||||
}
|
||||
DWORD protocols3 = WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 |
|
||||
WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2 | 0x00002000;
|
||||
WinHttpSetOption(hSession, WINHTTP_OPTION_SECURE_PROTOCOLS, &protocols3, sizeof(protocols3));
|
||||
#ifdef WINHTTP_DISABLE_FEATURE_HTTP2
|
||||
DWORD features3 = WINHTTP_DISABLE_FEATURE_HTTP2;
|
||||
WinHttpSetOption(hSession, WINHTTP_OPTION_DISABLE_FEATURE, &features3, sizeof(features3));
|
||||
#endif
|
||||
|
||||
// Specify an HTTP server
|
||||
HINTERNET hConnect = WinHttpConnect(hSession, urlComp.lpszHostName,
|
||||
urlComp.nPort, 0);
|
||||
// Specify an HTTP server (ensure host is null-terminated)
|
||||
std::wstring host3 = (urlComp.lpszHostName && urlComp.dwHostNameLength > 0)
|
||||
? std::wstring(urlComp.lpszHostName, urlComp.dwHostNameLength)
|
||||
: std::wstring();
|
||||
INTERNET_PORT port3 = urlComp.nPort ? urlComp.nPort : ((urlComp.nScheme == INTERNET_SCHEME_HTTPS) ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT);
|
||||
HINTERNET hConnect = WinHttpConnect(hSession, host3.c_str(), port3, 0);
|
||||
|
||||
if (!hConnect) {
|
||||
std::cerr << last_winhttp_error("WinHttpConnect(DEL)") << std::endl;
|
||||
WinHttpCloseHandle(hSession);
|
||||
return false;
|
||||
}
|
||||
@ -348,6 +417,7 @@ bool HttpClient::delete_req(const std::string& url, const std::string& data, std
|
||||
WINHTTP_FLAG_SECURE : 0);
|
||||
|
||||
if (!hRequest) {
|
||||
std::cerr << "[WinHTTP] WinHttpOpenRequest(DEL) failed" << std::endl;
|
||||
WinHttpCloseHandle(hConnect);
|
||||
WinHttpCloseHandle(hSession);
|
||||
return false;
|
||||
@ -361,6 +431,7 @@ bool HttpClient::delete_req(const std::string& url, const std::string& data, std
|
||||
WINHTTP_ADDREQ_FLAG_ADD);
|
||||
|
||||
if (!bResults) {
|
||||
std::cerr << last_winhttp_error("WinHttpAddRequestHeaders(DEL)") << std::endl;
|
||||
WinHttpCloseHandle(hRequest);
|
||||
WinHttpCloseHandle(hConnect);
|
||||
WinHttpCloseHandle(hSession);
|
||||
@ -372,6 +443,7 @@ bool HttpClient::delete_req(const std::string& url, const std::string& data, std
|
||||
WINHTTP_NO_ADDITIONAL_HEADERS, 0,
|
||||
(LPVOID)data.c_str(), (DWORD)data.length(),
|
||||
(DWORD)data.length(), 0)) {
|
||||
std::cerr << last_winhttp_error("WinHttpSendRequest(DEL)") << std::endl;
|
||||
WinHttpCloseHandle(hRequest);
|
||||
WinHttpCloseHandle(hConnect);
|
||||
WinHttpCloseHandle(hSession);
|
||||
@ -380,6 +452,7 @@ bool HttpClient::delete_req(const std::string& url, const std::string& data, std
|
||||
|
||||
// End the request
|
||||
if (!WinHttpReceiveResponse(hRequest, NULL)) {
|
||||
std::cerr << last_winhttp_error("WinHttpReceiveResponse(DEL)") << std::endl;
|
||||
WinHttpCloseHandle(hRequest);
|
||||
WinHttpCloseHandle(hConnect);
|
||||
WinHttpCloseHandle(hSession);
|
||||
@ -395,6 +468,7 @@ bool HttpClient::delete_req(const std::string& url, const std::string& data, std
|
||||
// Check for available data
|
||||
dwSize = 0;
|
||||
if (!WinHttpQueryDataAvailable(hRequest, &dwSize)) {
|
||||
std::cerr << last_winhttp_error("WinHttpQueryDataAvailable(DEL)") << std::endl;
|
||||
WinHttpCloseHandle(hRequest);
|
||||
WinHttpCloseHandle(hConnect);
|
||||
WinHttpCloseHandle(hSession);
|
||||
@ -413,6 +487,7 @@ bool HttpClient::delete_req(const std::string& url, const std::string& data, std
|
||||
// Read the data
|
||||
ZeroMemory(pszOutBuffer, dwSize + 1);
|
||||
if (!WinHttpReadData(hRequest, (LPVOID)pszOutBuffer, dwSize, &dwDownloaded)) {
|
||||
std::cerr << last_winhttp_error("WinHttpReadData(DEL)") << std::endl;
|
||||
delete[] pszOutBuffer;
|
||||
WinHttpCloseHandle(hRequest);
|
||||
WinHttpCloseHandle(hConnect);
|
||||
|
||||
@ -105,8 +105,18 @@ bool JsonParser::parse_paste_json(const json& json_data,
|
||||
}
|
||||
}
|
||||
|
||||
// Extract expiration
|
||||
expiration = json_data.at("meta").at("expire");
|
||||
// Extract expiration; servers may return either meta.expire (string)
|
||||
// or meta.time_to_live (integer seconds). Both are optional for our use.
|
||||
try {
|
||||
expiration = json_data.at("meta").at("expire");
|
||||
} catch (...) {
|
||||
try {
|
||||
auto ttl = json_data.at("meta").at("time_to_live").get<int>();
|
||||
expiration = std::to_string(ttl);
|
||||
} catch (...) {
|
||||
expiration.clear();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (...) {
|
||||
|
||||
Reference in New Issue
Block a user