#include "connection/httpConnection.hpp" #include #include #include #include #include #include namespace { size_t write_to_string(void *contents, size_t size, size_t nmemb, void *userp) { size_t total = size * nmemb; auto *buffer = static_cast(userp); buffer->append(static_cast(contents), total); return total; } std::string trim(std::string value) { auto not_space = [](unsigned char c) { return std::isspace(c) == 0; }; value.erase(value.begin(), std::ranges::find_if(value, not_space)); value.erase(std::ranges::find_if(std::ranges::reverse_view(value), not_space).base(), value.end()); return value; } size_t header_to_map(char *buffer, size_t size, size_t nitems, void *userdata) { size_t total = size * nitems; auto *header_map = static_cast *>(userdata); std::string line(buffer, total); auto colon = line.find(':'); if (colon != std::string::npos) { auto key = trim(line.substr(0, colon)); auto value = trim(line.substr(colon + 1)); if (!key.empty()) { header_map->insert_or_assign(std::move(key), std::move(value)); } } return total; } } // namespace HttpResponse HttpConnection::get(const std::string &url, const std::map &headers, long timeout_ms) { return performRequest("GET", url, std::string(), headers, std::string(), timeout_ms); } HttpResponse HttpConnection::post(const std::string &url, const std::string &body, const std::map &headers, const std::string &content_type, long timeout_ms) { return performRequest("POST", url, body, headers, content_type, timeout_ms); } HttpResponse HttpConnection::performRequest(const std::string &method, const std::string &url, const std::string &body, const std::map &headers, const std::string &content_type, long timeout_ms) { HttpResponse response; CURL *curl = curl_easy_init(); if (!curl) { response.error = "curl_easy_init failed"; return response; } curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_to_string); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response.body); curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_to_map); curl_easy_setopt(curl, CURLOPT_HEADERDATA, &response.headers); curl_easy_setopt(curl, CURLOPT_USERAGENT, "bar/1.0"); if (timeout_ms > 0) { curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, timeout_ms); } if (method == "POST") { curl_easy_setopt(curl, CURLOPT_POST, 1L); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str()); curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, static_cast(body.size())); } else { curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); } struct curl_slist *header_list = nullptr; for (const auto &pair : headers) { std::string header = pair.first + ": " + pair.second; header_list = curl_slist_append(header_list, header.c_str()); } if (method == "POST" && !content_type.empty()) { std::string content_header = "Content-Type: " + content_type; header_list = curl_slist_append(header_list, content_header.c_str()); } if (header_list) { curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header_list); } auto result = curl_easy_perform(curl); if (result != CURLE_OK) { response.error = curl_easy_strerror(result); } curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response.status_code); if (header_list) { curl_slist_free_all(header_list); } curl_easy_cleanup(curl); return response; }