Files
bar/src/connection/httpConnection.cpp

121 lines
4.2 KiB
C++

#include "connection/httpConnection.hpp"
#include <algorithm>
#include <cctype>
#include <curl/curl.h>
#include <ranges>
#include <string>
#include <utility>
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<std::string *>(userp);
buffer->append(static_cast<char *>(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<std::map<std::string, std::string> *>(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<std::string, std::string> &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<std::string, std::string> &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<std::string, std::string> &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<long>(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;
}