feat: Add HTTP/HTTPS client module

Implement HTTP client with libcurl for fetching web pages:
- Support for HTTP and HTTPS protocols
- Configurable timeout and user agent
- Automatic redirect following
- SSL certificate verification
- Pimpl pattern for implementation hiding

This module provides the foundation for web page retrieval
in the terminal browser.
This commit is contained in:
m1ngsama 2025-12-05 14:58:47 +08:00
parent 2d2711e250
commit a2a2aa5126
2 changed files with 148 additions and 0 deletions

111
src/http_client.cpp Normal file
View file

@ -0,0 +1,111 @@
#include "http_client.h"
#include <curl/curl.h>
#include <stdexcept>
// 回调函数用于接收数据
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<char*>(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<Impl>()) {}
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<int>(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;
}

37
src/http_client.h Normal file
View file

@ -0,0 +1,37 @@
#pragma once
#include <string>
#include <memory>
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<Impl> pImpl;
};