整理代码

This commit is contained in:
蒲爽 2026-09-28 21:33:04 +08:00
commit c7b4e178ec
364 changed files with 59691 additions and 0 deletions

386
util/HTTPClient.h Normal file
View file

@ -0,0 +1,386 @@
#ifndef C99FBF1C_5805_40A3_963F_5BC1CA998C5C
#define C99FBF1C_5805_40A3_963F_5BC1CA998C5C
#include <Poco/Exception.h>
#include <Poco/Net/HTMLForm.h>
#include <Poco/Net/HTTPClientSession.h>
#include <Poco/Net/HTTPRequest.h>
#include <Poco/Net/HTTPResponse.h>
#include <Poco/Net/MessageHeader.h>
#include <Poco/StreamCopier.h>
#include <Poco/URI.h>
#include <cctype>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <map>
#include <memory>
#include <sstream>
#include <string>
#include <vector>
#include "Logger.h"
namespace Util
{
class HTTPClient
{
public:
struct HttpResponse
{
int statusCode;
std::string statusMessage;
std::string content;
std::string contentType;
std::map<std::string, std::string> headers;
bool success;
HttpResponse(): statusCode(0), success(false)
{
}
};
struct ProxyConfig
{
std::string host;
int port;
bool enabled;
ProxyConfig(): port(0), enabled(false)
{
}
ProxyConfig(const std::string& h, int p): host(h), port(p), enabled(true)
{
}
};
struct RequestConfig
{
int timeout;
std::string userAgent;
std::map<std::string, std::string> headers;
bool followRedirects;
ProxyConfig proxy;
RequestConfig(): timeout(30), userAgent("HTTPClient/1.0"), followRedirects(true)
{
}
};
static HttpResponse get(const std::string& url, const RequestConfig& config = RequestConfig())
{
return executeRequest("GET", url, "", config);
}
static bool downloadFile(const std::string& url, const std::string& localFilePath,
const RequestConfig& config = RequestConfig())
{
try
{
Poco::URI uri(url);
auto session = createSession(uri, config.proxy);
session->setTimeout(Poco::Timespan(config.timeout, 0));
std::string path = getPathAndQuery(uri);
Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_GET, path);
setupRequest(request, config);
session->sendRequest(request);
Poco::Net::HTTPResponse response;
std::istream& responseStream = session->receiveResponse(response);
if (response.getStatus() != Poco::Net::HTTPResponse::HTTP_OK)
{
std::cerr << "Download failed: HTTP " << response.getStatus() << std::endl;
return false;
}
std::ofstream file(localFilePath, std::ios::binary);
if (!file.is_open())
{
std::cerr << "Cannot open file: " << localFilePath << std::endl;
return false;
}
Poco::StreamCopier::copyStream(responseStream, file);
file.close();
return true;
}
catch (Poco::Exception& e)
{
std::cerr << "Download exception: " << e.displayText() << std::endl;
return false;
}
}
static HttpResponse postJson(const std::string& url, const std::string& jsonData,
const RequestConfig& config = RequestConfig())
{
RequestConfig jsonConfig = config;
jsonConfig.headers["Content-Type"] = "application/json";
return executeRequest("POST", url, jsonData, jsonConfig);
}
static HttpResponse postForm(const std::string& url, const std::map<std::string, std::string>& formData,
const RequestConfig& config = RequestConfig())
{
std::string formString = buildFormData(formData);
RequestConfig formConfig = config;
formConfig.headers["Content-Type"] = "application/x-www-form-urlencoded";
return executeRequest("POST", url, formString, formConfig);
}
static HttpResponse postFormWithPoco(const std::string& url, const std::map<std::string, std::string>& formData,
const RequestConfig& config = RequestConfig())
{
try
{
Poco::URI uri(url);
auto session = createSession(uri, config.proxy);
session->setTimeout(Poco::Timespan(config.timeout, 0));
std::string path = getPathAndQuery(uri);
Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_POST, path);
setupRequest(request, config);
request.setContentType("application/x-www-form-urlencoded");
Poco::Net::HTMLForm form;
for (const auto& field : formData)
{
form.set(field.first, field.second);
}
std::ostream& requestStream = session->sendRequest(request);
form.write(requestStream);
Poco::Net::HTTPResponse response;
std::istream& responseStream = session->receiveResponse(response);
HttpResponse result;
result.statusCode = response.getStatus();
result.statusMessage = response.getReason();
result.contentType = response.getContentType();
result.success = (result.statusCode == Poco::Net::HTTPResponse::HTTP_OK);
Poco::Net::NameValueCollection::ConstIterator it = response.begin();
while (it != response.end())
{
result.headers[it->first] = it->second;
++it;
}
Poco::StreamCopier::copyToString(responseStream, result.content);
return result;
}
catch (Poco::Exception& e)
{
std::cerr << "POST form exception: " << e.displayText() << std::endl;
HttpResponse result;
result.success = false;
result.statusMessage = e.displayText();
return result;
}
}
static HttpResponse post(const std::string& url, const std::string& data,
const std::string& contentType = "text/plain",
const RequestConfig& config = RequestConfig())
{
RequestConfig postConfig = config;
postConfig.headers["Content-Type"] = contentType;
return executeRequest("POST", url, data, postConfig);
}
static HttpResponse put(const std::string& url, const std::string& data = "",
const RequestConfig& config = RequestConfig())
{
return executeRequest("PUT", url, data, config);
}
static HttpResponse deleteRequest(const std::string& url, const RequestConfig& config = RequestConfig())
{
return executeRequest("DELETE", url, "", config);
}
static RequestConfig createProxyConfig(const std::string& proxyHost, int proxyPort,
const RequestConfig& baseConfig = RequestConfig())
{
RequestConfig config = baseConfig;
config.proxy = ProxyConfig(proxyHost, proxyPort);
return config;
}
static HttpResponse executeRequest(const std::string& method, const std::string& url, const std::string& data,
const RequestConfig& config)
{
HttpResponse result;
try
{
Poco::URI uri(url);
auto session = createSession(uri, config.proxy);
session->setTimeout(Poco::Timespan(config.timeout, 0));
std::string path = getPathAndQuery(uri);
Poco::Net::HTTPRequest request(method, path);
setupRequest(request, config);
if (!data.empty())
{
request.setContentLength(data.length());
std::ostream& requestStream = session->sendRequest(request);
requestStream << data;
}
else
{
session->sendRequest(request);
}
Poco::Net::HTTPResponse response;
std::istream& responseStream = session->receiveResponse(response);
result.statusCode = response.getStatus();
result.statusMessage = response.getReason();
result.contentType = response.getContentType();
result.success = (result.statusCode == Poco::Net::HTTPResponse::HTTP_OK);
Poco::Net::NameValueCollection::ConstIterator it = response.begin();
while (it != response.end())
{
result.headers[it->first] = it->second;
++it;
}
Poco::StreamCopier::copyToString(responseStream, result.content);
if (config.followRedirects && (result.statusCode == Poco::Net::HTTPResponse::HTTP_MOVED_PERMANENTLY ||
result.statusCode == Poco::Net::HTTPResponse::HTTP_FOUND ||
result.statusCode == Poco::Net::HTTPResponse::HTTP_SEE_OTHER))
{
auto locationIt = result.headers.find("Location");
if (locationIt != result.headers.end())
{
std::cout << "Redirecting to: " << locationIt->second << std::endl;
return executeRequest(method, locationIt->second, data, config);
}
}
}
catch (Poco::TimeoutException& e)
{
std::cerr << "Request timeout: " << e.displayText() << std::endl;
result.success = false;
result.statusMessage = "Timeout";
}
catch (Poco::Exception& e)
{
std::cerr << "HTTP exception: " << e.displayText() << std::endl;
result.success = false;
result.statusMessage = e.displayText();
}
catch (std::exception& e)
{
std::cerr << "Standard exception: " << e.what() << std::endl;
result.success = false;
result.statusMessage = e.what();
}
return result;
}
private:
static std::string urlEncode(const std::string& value)
{
std::ostringstream escaped;
escaped.fill('0');
escaped << std::hex;
for (char c : value)
{
if (std::isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~')
{
escaped << c;
}
else
{
escaped << '%' << std::setw(2) << int(static_cast<unsigned char>(c));
}
}
return escaped.str();
}
static std::string buildFormData(const std::map<std::string, std::string>& formData)
{
std::ostringstream formStream;
bool first = true;
for (const auto& field : formData)
{
if (!first)
{
formStream << "&";
}
formStream << urlEncode(field.first) << "=" << urlEncode(field.second);
first = false;
}
return formStream.str();
}
static std::unique_ptr<Poco::Net::HTTPClientSession> createSession(const Poco::URI& uri, const ProxyConfig& proxy)
{
std::unique_ptr<Poco::Net::HTTPClientSession> session;
logger().debug("HTTPClient url: %s", uri.toString());
if (proxy.enabled)
{
session = std::make_unique<Poco::Net::HTTPClientSession>(proxy.host, proxy.port);
session->setProxy(uri.getHost(), uri.getPort());
}
else
{
if (uri.getScheme() == "https")
{
logger().warning("HTTPClient 暂时不支持https,自动调整为使用http连接: %s", uri.toString());
session = std::make_unique<Poco::Net::HTTPClientSession>(uri.getHost(), uri.getPort());
}
else
{
session = std::make_unique<Poco::Net::HTTPClientSession>(uri.getHost(), uri.getPort());
}
}
return session;
}
static void setupRequest(Poco::Net::HTTPRequest& request, const RequestConfig& config)
{
request.setVersion(Poco::Net::HTTPMessage::HTTP_1_1);
request.set("User-Agent", config.userAgent);
request.set("Accept", "*/*");
request.set("Connection", "close");
for (const auto& header : config.headers)
{
request.set(header.first, header.second);
}
}
static std::string getPathAndQuery(const Poco::URI& uri)
{
std::string path = uri.getPathAndQuery();
if (path.empty())
{
path = "/";
}
return path;
}
};
}
#endif