Mercurial > minori
view src/core/http.cc @ 408:9323153786dc default tip
http: fix build fail under non-mac
| author | Paper <paper@tflc.us> |
|---|---|
| date | Wed, 21 Jan 2026 11:35:32 -0500 |
| parents | 2ae34a90f8d4 |
| children |
line wrap: on
line source
#include "core/http.h" #include "core/session.h" #include <QByteArray> #include <curl/curl.h> #include <iostream> #include <string> #include <vector> #include <cstring> namespace HTTP { std::string UrlEncode(const std::string &data) { std::string res; std::size_t sz = data.size(); std::size_t i; // output string will always be at least data.size() // so reserve that much space beforehand res.reserve(sz); for (i = 0; i < sz; i++) { // This works correctly for UTF-8 because of the way // the data is laid out. static const char lut[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.!~*'()"; unsigned char c = data[i]; if (std::memchr(lut, c, sizeof(lut) - 1)) { res.push_back(c); } else { static const char lut[] = "0123456789ABCDEF"; res.push_back('%'); res.push_back(lut[c >> 4]); res.push_back(lut[c & 15]); } } return res; } // NOTE: This function is not guaranteed to return // UTF-8, or even text at all. std::string UrlDecode(const std::string &data) { std::string res; const char *ptr = data.data(); std::size_t len = data.size(); // reserve space beforehand res.reserve(len); while (len > 0) { // find the next percent character // there's probably a better way to do this! const char *next = reinterpret_cast<const char *>(std::memchr(reinterpret_cast<const void *>(ptr), '%', len)); if (next) { res.insert(res.end(), ptr, next); len = (next - ptr); ptr = next; // now process the two hex chars if (len >= 3) { unsigned char hi, lo; auto hex_char_to_value = [](unsigned char x, unsigned char &v) { if (x >= 'A' && x <= 'F') { v = x - 'A' + 0xA; return true; } if (x >= 'a' && x <= 'f') { v = x - 'a' + 0xA; return true; } if (x >= '0' && x <= '9') { v = x - '0'; return true; } return false; }; if (hex_char_to_value(ptr[1], hi) && hex_char_to_value(ptr[2], lo)) { ptr += 3; len -= 3; } else { len = 0; } } else { // uh oh len = 0; } } else { res.insert(res.end(), ptr, ptr + len); //not needed: ptr += len; len = 0; } } return res; } std::string EncodeParamsList(std::string base, const std::map<std::string, std::string> ¶ms) { std::size_t count = 0; for (const auto ¶m : params) { base += (!count ? "?" : "&"); base += UrlEncode(param.first); base += "="; base += UrlEncode(param.second); count++; } return base; } static void SetCurlOpts(CURL *curl, const std::string &url, const std::vector<std::string> &headers, const std::string &data, Type type) { struct curl_slist *list; curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); list = NULL; for (const std::string &h : headers) list = curl_slist_append(list, h.c_str()); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list); switch (type) { case Type::Patch: curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PATCH"); [[fallthrough]]; case Type::Post: curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str()); break; case Type::Get: break; } curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA); curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L); // threading } static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userdata) { reinterpret_cast<QByteArray *>(userdata)->append(reinterpret_cast<char *>(contents), size * nmemb); return size * nmemb; } QByteArray Request(const std::string &url, const std::vector<std::string> &headers, const std::string &data, Type type) { QByteArray userdata; CURL *curl = curl_easy_init(); if (curl) { SetCurlOpts(curl, url, headers, data, type); /* Use our specific userdata & write callback * TODO can this just be a lambda instead */ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &userdata); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &WriteCallback); CURLcode res = curl_easy_perform(curl); session.IncrementRequests(); curl_easy_cleanup(curl); if (res != CURLE_OK) session.SetStatusBar(std::string("curl_easy_perform(curl) failed!: ") + curl_easy_strerror(res)); } return userdata; } /* this function is static */ size_t RequestThread::WriteCallback(void *contents, size_t size, size_t nmemb, void *userdata) { RequestThread *thread = reinterpret_cast<RequestThread *>(userdata); if (thread->cancelled_) return CURL_WRITEFUNC_ERROR; /* else, continue on as normal */ thread->array_.append(reinterpret_cast<char *>(contents), size * nmemb); return size * nmemb; } RequestThread::RequestThread(Type type, QObject *parent) : QThread(parent) { SetType(type); } RequestThread::RequestThread(const std::string &url, const std::vector<std::string> &headers, const std::string &data, Type type, QObject *parent) : QThread(parent) { SetUrl(url); SetData(data); SetHeaders(headers); SetType(type); } RequestThread::~RequestThread() { /* block until the function can safely exit */ Stop(); wait(); /* Kill off any curl thing we made */ if (curl_) curl_easy_cleanup(reinterpret_cast<CURL *>(curl_)); } void RequestThread::SetUrl(const std::string &url) { url_ = url; } void RequestThread::SetHeaders(const std::vector<std::string> &headers) { headers_ = headers; } void RequestThread::SetData(const std::string &data) { data_ = data; } void RequestThread::SetType(Type type) { type_ = type; } void RequestThread::run() { /* If we don't have a curl object, create one */ if (!curl_) { curl_ = reinterpret_cast<void *>(curl_easy_init()); } CURL *curl = reinterpret_cast<CURL *>(curl_); if (curl) { SetCurlOpts(curl, url_, headers_, data_, type_); curl_easy_setopt(curl, CURLOPT_WRITEDATA, this); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &RequestThread::WriteCallback); CURLcode res = curl_easy_perform(curl); session.IncrementRequests(); if (res != CURLE_OK && !(res == CURLE_WRITE_ERROR && cancelled_)) session.SetStatusBar(std::string("curl_easy_perform(curl) failed!: ") + curl_easy_strerror(res)); } emit ReceivedData(array_); /* Clear it out for any subsequent runs */ array_.clear(); } void RequestThread::Stop() { cancelled_ = true; } void Init() { curl_global_init(CURL_GLOBAL_ALL); } void Quit() { curl_global_cleanup(); } } // namespace HTTP
