- Remove hardcoded CPPHTTPLIB_OPENSSL_SUPPORT in HttpClient.hpp - Make SSL certificate verification conditional on OpenSSL availability - Add ws2_32 (Winsock) linking for all targets using httplib on Windows - Disable TestRunnerModule on Windows (requires Unix dlfcn.h) - Fix .gitignore to exclude GroveEngine symlink (local only) - Disable httplib OpenSSL auto-detection when OpenSSL not found This enables AISSIA to build successfully on Windows without OpenSSL, using HTTP-only mode for LLM API calls. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
210 lines
6.6 KiB
C++
210 lines
6.6 KiB
C++
#pragma once
|
|
|
|
/**
|
|
* @brief Simple HTTP Client wrapper around cpp-httplib
|
|
*
|
|
* Header-only wrapper for making HTTP requests to LLM APIs.
|
|
* Requires cpp-httplib and OpenSSL for HTTPS support.
|
|
*/
|
|
|
|
// Only enable OpenSSL support if it was found during CMake configuration
|
|
// CPPHTTPLIB_OPENSSL_SUPPORT is defined via target_compile_definitions when OpenSSL is available
|
|
#include <httplib.h>
|
|
#include <nlohmann/json.hpp>
|
|
#include <string>
|
|
#include <optional>
|
|
#include <map>
|
|
#include <spdlog/spdlog.h>
|
|
#include <spdlog/sinks/stdout_color_sinks.h>
|
|
|
|
namespace aissia {
|
|
|
|
using json = nlohmann::json;
|
|
|
|
struct HttpResponse {
|
|
int status = 0;
|
|
std::string body;
|
|
bool success = false;
|
|
std::string error;
|
|
};
|
|
|
|
class HttpClient {
|
|
public:
|
|
explicit HttpClient(const std::string& baseUrl, int timeoutSeconds = 30)
|
|
: m_baseUrl(baseUrl), m_timeoutSeconds(timeoutSeconds) {
|
|
|
|
m_logger = spdlog::get("HttpClient");
|
|
if (!m_logger) {
|
|
m_logger = spdlog::stdout_color_mt("HttpClient");
|
|
}
|
|
|
|
// Parse URL to extract host and determine HTTPS
|
|
if (baseUrl.find("https://") == 0) {
|
|
m_host = baseUrl.substr(8);
|
|
m_useSSL = true;
|
|
} else if (baseUrl.find("http://") == 0) {
|
|
m_host = baseUrl.substr(7);
|
|
m_useSSL = false;
|
|
} else {
|
|
m_host = baseUrl;
|
|
m_useSSL = false;
|
|
}
|
|
|
|
// Remove trailing path
|
|
auto slashPos = m_host.find('/');
|
|
if (slashPos != std::string::npos) {
|
|
m_host = m_host.substr(0, slashPos);
|
|
}
|
|
}
|
|
|
|
void setHeader(const std::string& key, const std::string& value) {
|
|
m_headers[key] = value;
|
|
}
|
|
|
|
void setBearerToken(const std::string& token) {
|
|
m_headers["Authorization"] = "Bearer " + token;
|
|
}
|
|
|
|
HttpResponse post(const std::string& path, const json& body) {
|
|
HttpResponse response;
|
|
|
|
try {
|
|
// Create client with full URL scheme for proper HTTPS support
|
|
std::string url = (m_useSSL ? "https://" : "http://") + m_host;
|
|
httplib::Client client(url);
|
|
|
|
// Only enable certificate verification if OpenSSL support is compiled in
|
|
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
|
if (m_useSSL) {
|
|
client.enable_server_certificate_verification(true);
|
|
}
|
|
#endif
|
|
|
|
client.set_connection_timeout(m_timeoutSeconds);
|
|
client.set_read_timeout(m_timeoutSeconds);
|
|
client.set_write_timeout(m_timeoutSeconds);
|
|
|
|
httplib::Headers headers;
|
|
headers.emplace("Content-Type", "application/json");
|
|
for (const auto& [key, value] : m_headers) {
|
|
headers.emplace(key, value);
|
|
}
|
|
|
|
std::string bodyStr = body.dump();
|
|
m_logger->debug("POST {} ({} bytes)", path, bodyStr.size());
|
|
|
|
auto result = client.Post(path, headers, bodyStr, "application/json");
|
|
|
|
if (result) {
|
|
response.status = result->status;
|
|
response.body = result->body;
|
|
response.success = (result->status >= 200 && result->status < 300);
|
|
|
|
if (!response.success) {
|
|
response.error = "HTTP " + std::to_string(result->status);
|
|
m_logger->warn("HTTP {} for {}: {}", result->status, path,
|
|
result->body.substr(0, 200));
|
|
}
|
|
} else {
|
|
response.error = httplib::to_string(result.error());
|
|
m_logger->error("HTTP request failed: {}", response.error);
|
|
}
|
|
|
|
} catch (const std::exception& e) {
|
|
response.error = e.what();
|
|
m_logger->error("HTTP exception: {}", e.what());
|
|
}
|
|
|
|
return response;
|
|
}
|
|
|
|
HttpResponse postMultipart(const std::string& path,
|
|
const httplib::MultipartFormDataItems& items) {
|
|
HttpResponse response;
|
|
|
|
try {
|
|
std::string url = (m_useSSL ? "https://" : "http://") + m_host;
|
|
httplib::Client client(url);
|
|
|
|
// Only enable certificate verification if OpenSSL support is compiled in
|
|
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
|
if (m_useSSL) {
|
|
client.enable_server_certificate_verification(true);
|
|
}
|
|
#endif
|
|
|
|
client.set_connection_timeout(m_timeoutSeconds);
|
|
client.set_read_timeout(m_timeoutSeconds);
|
|
|
|
httplib::Headers headers;
|
|
for (const auto& [key, value] : m_headers) {
|
|
headers.emplace(key, value);
|
|
}
|
|
|
|
auto result = client.Post(path, headers, items);
|
|
|
|
if (result) {
|
|
response.status = result->status;
|
|
response.body = result->body;
|
|
response.success = (result->status >= 200 && result->status < 300);
|
|
} else {
|
|
response.error = httplib::to_string(result.error());
|
|
}
|
|
|
|
} catch (const std::exception& e) {
|
|
response.error = e.what();
|
|
}
|
|
|
|
return response;
|
|
}
|
|
|
|
HttpResponse get(const std::string& path) {
|
|
HttpResponse response;
|
|
|
|
try {
|
|
std::string url = (m_useSSL ? "https://" : "http://") + m_host;
|
|
httplib::Client client(url);
|
|
|
|
// Only enable certificate verification if OpenSSL support is compiled in
|
|
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
|
if (m_useSSL) {
|
|
client.enable_server_certificate_verification(true);
|
|
}
|
|
#endif
|
|
|
|
client.set_connection_timeout(m_timeoutSeconds);
|
|
client.set_read_timeout(m_timeoutSeconds);
|
|
|
|
httplib::Headers headers;
|
|
for (const auto& [key, value] : m_headers) {
|
|
headers.emplace(key, value);
|
|
}
|
|
|
|
auto result = client.Get(path, headers);
|
|
|
|
if (result) {
|
|
response.status = result->status;
|
|
response.body = result->body;
|
|
response.success = (result->status >= 200 && result->status < 300);
|
|
} else {
|
|
response.error = httplib::to_string(result.error());
|
|
}
|
|
|
|
} catch (const std::exception& e) {
|
|
response.error = e.what();
|
|
}
|
|
|
|
return response;
|
|
}
|
|
|
|
private:
|
|
std::string m_baseUrl;
|
|
std::string m_host;
|
|
bool m_useSSL = false;
|
|
int m_timeoutSeconds;
|
|
std::map<std::string, std::string> m_headers;
|
|
std::shared_ptr<spdlog::logger> m_logger;
|
|
};
|
|
|
|
} // namespace aissia
|