diff --git a/src/http_client.cpp b/src/http_client.cpp new file mode 100644 index 0000000..dd990dc --- /dev/null +++ b/src/http_client.cpp @@ -0,0 +1,111 @@ +#include "http_client.h" +#include +#include + +// 回调函数用于接收数据 +static size_t write_callback(void* contents, size_t size, size_t nmemb, std::string* userp) { + size_t total_size = size * nmemb; + userp->append(static_cast(contents), total_size); + return total_size; +} + +class HttpClient::Impl { +public: + CURL* curl; + long timeout; + std::string user_agent; + bool follow_redirects; + + Impl() : timeout(30), + user_agent("TUT-Browser/1.0 (Terminal User Interface Browser)"), + follow_redirects(true) { + curl = curl_easy_init(); + if (!curl) { + throw std::runtime_error("Failed to initialize CURL"); + } + } + + ~Impl() { + if (curl) { + curl_easy_cleanup(curl); + } + } +}; + +HttpClient::HttpClient() : pImpl(std::make_unique()) {} + +HttpClient::~HttpClient() = default; + +HttpResponse HttpClient::fetch(const std::string& url) { + HttpResponse response; + response.status_code = 0; + + if (!pImpl->curl) { + response.error_message = "CURL not initialized"; + return response; + } + + // 重置选项 + curl_easy_reset(pImpl->curl); + + // 设置URL + curl_easy_setopt(pImpl->curl, CURLOPT_URL, url.c_str()); + + // 设置写回调 + std::string response_body; + curl_easy_setopt(pImpl->curl, CURLOPT_WRITEFUNCTION, write_callback); + curl_easy_setopt(pImpl->curl, CURLOPT_WRITEDATA, &response_body); + + // 设置超时 + curl_easy_setopt(pImpl->curl, CURLOPT_TIMEOUT, pImpl->timeout); + curl_easy_setopt(pImpl->curl, CURLOPT_CONNECTTIMEOUT, 10L); + + // 设置用户代理 + curl_easy_setopt(pImpl->curl, CURLOPT_USERAGENT, pImpl->user_agent.c_str()); + + // 设置是否跟随重定向 + if (pImpl->follow_redirects) { + curl_easy_setopt(pImpl->curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(pImpl->curl, CURLOPT_MAXREDIRS, 10L); + } + + // 支持 HTTPS + curl_easy_setopt(pImpl->curl, CURLOPT_SSL_VERIFYPEER, 1L); + curl_easy_setopt(pImpl->curl, CURLOPT_SSL_VERIFYHOST, 2L); + + // 执行请求 + CURLcode res = curl_easy_perform(pImpl->curl); + + if (res != CURLE_OK) { + response.error_message = curl_easy_strerror(res); + return response; + } + + // 获取响应码 + long http_code = 0; + curl_easy_getinfo(pImpl->curl, CURLINFO_RESPONSE_CODE, &http_code); + response.status_code = static_cast(http_code); + + // 获取 Content-Type + char* content_type = nullptr; + curl_easy_getinfo(pImpl->curl, CURLINFO_CONTENT_TYPE, &content_type); + if (content_type) { + response.content_type = content_type; + } + + response.body = std::move(response_body); + + return response; +} + +void HttpClient::set_timeout(long timeout_seconds) { + pImpl->timeout = timeout_seconds; +} + +void HttpClient::set_user_agent(const std::string& user_agent) { + pImpl->user_agent = user_agent; +} + +void HttpClient::set_follow_redirects(bool follow) { + pImpl->follow_redirects = follow; +} diff --git a/src/http_client.h b/src/http_client.h new file mode 100644 index 0000000..9793178 --- /dev/null +++ b/src/http_client.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include + +struct HttpResponse { + int status_code; + std::string body; + std::string content_type; + std::string error_message; + + bool is_success() const { + return status_code >= 200 && status_code < 300; + } +}; + +class HttpClient { +public: + HttpClient(); + ~HttpClient(); + + // 获取网页内容 + HttpResponse fetch(const std::string& url); + + // 设置超时(秒) + void set_timeout(long timeout_seconds); + + // 设置用户代理 + void set_user_agent(const std::string& user_agent); + + // 设置是否跟随重定向 + void set_follow_redirects(bool follow); + +private: + class Impl; + std::unique_ptr pImpl; +};