121 lines
4.2 KiB
C++
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;
|
|
}
|