2022-05-21 12:04:08 +02:00
|
|
|
#include "http.hpp"
|
2022-06-16 10:21:03 +02:00
|
|
|
#include "finally.hpp"
|
|
|
|
#include <curl/curl.h>
|
|
|
|
|
|
|
|
#pragma comment(lib, "ws2_32.lib")
|
2022-05-21 12:04:08 +02:00
|
|
|
|
|
|
|
namespace utils::http
|
|
|
|
{
|
2022-06-16 10:21:03 +02:00
|
|
|
namespace
|
2022-05-21 12:04:08 +02:00
|
|
|
{
|
2022-06-16 10:21:03 +02:00
|
|
|
struct progress_helper
|
|
|
|
{
|
2022-06-16 14:15:14 +02:00
|
|
|
const std::function<void(size_t, size_t)>* callback{};
|
2022-06-16 10:21:03 +02:00
|
|
|
std::exception_ptr exception{};
|
|
|
|
};
|
|
|
|
|
2022-06-16 14:15:14 +02:00
|
|
|
int progress_callback(void *clientp, const curl_off_t dltotal, const curl_off_t dlnow, const curl_off_t /*ultotal*/, const curl_off_t /*ulnow*/)
|
2022-06-16 10:21:03 +02:00
|
|
|
{
|
|
|
|
auto* helper = static_cast<progress_helper*>(clientp);
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
if (*helper->callback)
|
|
|
|
{
|
2022-06-16 14:15:14 +02:00
|
|
|
(*helper->callback)(dltotal, dlnow);
|
2022-06-16 10:21:03 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
catch(...)
|
|
|
|
{
|
|
|
|
helper->exception = std::current_exception();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t write_callback(void* contents, const size_t size, const size_t nmemb, void* userp)
|
|
|
|
{
|
|
|
|
auto* buffer = static_cast<std::string*>(userp);
|
|
|
|
|
|
|
|
const auto total_size = size * nmemb;
|
|
|
|
buffer->append(static_cast<char*>(contents), total_size);
|
|
|
|
return total_size;
|
|
|
|
}
|
|
|
|
}
|
2022-05-21 12:04:08 +02:00
|
|
|
|
2022-06-16 14:15:14 +02:00
|
|
|
std::optional<std::string> get_data(const std::string& url, const headers& headers, const std::function<void(size_t, size_t)>& callback)
|
2022-06-16 10:21:03 +02:00
|
|
|
{
|
|
|
|
curl_slist* header_list = nullptr;
|
|
|
|
auto* curl = curl_easy_init();
|
|
|
|
if (!curl)
|
2022-05-21 12:04:08 +02:00
|
|
|
{
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2022-06-16 10:21:03 +02:00
|
|
|
auto _ = finally([&]()
|
|
|
|
{
|
|
|
|
curl_slist_free_all(header_list);
|
|
|
|
curl_easy_cleanup(curl);
|
|
|
|
});
|
|
|
|
|
|
|
|
for(const auto& header : headers)
|
|
|
|
{
|
|
|
|
auto data = header.first + ": " + header.second;
|
|
|
|
header_list = curl_slist_append(header_list, data.data());
|
|
|
|
}
|
2022-05-21 12:04:08 +02:00
|
|
|
|
2022-06-16 10:21:03 +02:00
|
|
|
std::string buffer{};
|
|
|
|
progress_helper helper{};
|
|
|
|
helper.callback = &callback;
|
|
|
|
|
|
|
|
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header_list);
|
|
|
|
curl_easy_setopt(curl, CURLOPT_URL, url.data());
|
|
|
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
|
|
|
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer);
|
|
|
|
curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, progress_callback);
|
|
|
|
curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &helper);
|
|
|
|
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
|
|
|
|
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, true);
|
|
|
|
curl_easy_setopt(curl, CURLOPT_USERAGENT, "boiii/1.0");
|
|
|
|
curl_easy_setopt(curl, CURLOPT_FAILONERROR, true);
|
2022-05-21 12:04:08 +02:00
|
|
|
|
2022-06-16 10:21:03 +02:00
|
|
|
// Due to CURLOPT_FAILONERROR, CURLE_OK will not be met when the server returns 400 or 500
|
|
|
|
if (curl_easy_perform(curl) == CURLE_OK)
|
2022-05-21 12:04:08 +02:00
|
|
|
{
|
2022-06-16 10:21:03 +02:00
|
|
|
long http_code = 0;
|
|
|
|
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
|
2022-05-21 12:04:08 +02:00
|
|
|
|
2022-06-16 10:21:03 +02:00
|
|
|
if (http_code >= 200)
|
2022-05-21 12:04:08 +02:00
|
|
|
{
|
2022-06-16 10:21:03 +02:00
|
|
|
return { std::move(buffer) };
|
2022-05-21 12:04:08 +02:00
|
|
|
}
|
2022-06-16 10:21:03 +02:00
|
|
|
|
|
|
|
throw std::runtime_error("Bad status code " + std::to_string(http_code) + " met while trying to download file " + url);
|
2022-05-21 12:04:08 +02:00
|
|
|
}
|
|
|
|
|
2022-06-16 10:21:03 +02:00
|
|
|
if (helper.exception)
|
2022-05-21 12:04:08 +02:00
|
|
|
{
|
2022-06-16 10:21:03 +02:00
|
|
|
std::rethrow_exception(helper.exception);
|
2022-05-21 12:04:08 +02:00
|
|
|
}
|
|
|
|
|
2022-06-16 10:21:03 +02:00
|
|
|
return {};
|
2022-05-21 12:04:08 +02:00
|
|
|
}
|
|
|
|
|
2022-06-16 10:21:03 +02:00
|
|
|
std::future<std::optional<std::string>> get_data_async(const std::string& url, const headers& headers)
|
2022-05-21 12:04:08 +02:00
|
|
|
{
|
2022-06-16 10:21:03 +02:00
|
|
|
return std::async(std::launch::async, [url, headers]()
|
2022-05-21 12:04:08 +02:00
|
|
|
{
|
2022-06-16 10:21:03 +02:00
|
|
|
return get_data(url, headers);
|
2022-05-21 12:04:08 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|