From 1775a13378eb85a307f565ab1375e1645d3ce91f Mon Sep 17 00:00:00 2001 From: Federico Cecchetto Date: Mon, 31 Jan 2022 23:45:03 +0100 Subject: [PATCH] Use cURL so http requests don't get cached --- .gitmodules | 3 ++ deps/curl | 1 + deps/premake/curl.lua | 73 ++++++++++++++++++++++++++ src/common/utils/http.cpp | 106 ++++++++++++++++++++++++++++---------- src/common/utils/http.hpp | 6 ++- 5 files changed, 160 insertions(+), 29 deletions(-) create mode 160000 deps/curl create mode 100644 deps/premake/curl.lua diff --git a/.gitmodules b/.gitmodules index 64a8f14c..a8f01577 100644 --- a/.gitmodules +++ b/.gitmodules @@ -43,3 +43,6 @@ [submodule "deps/imgui"] path = deps/imgui url = https://github.com/fedddddd/imgui.git +[submodule "deps/curl"] + path = deps/curl + url = https://github.com/curl/curl.git diff --git a/deps/curl b/deps/curl new file mode 160000 index 00000000..7e30252e --- /dev/null +++ b/deps/curl @@ -0,0 +1 @@ +Subproject commit 7e30252ec34eb99c44b5bb8428e716b73ccbff4b diff --git a/deps/premake/curl.lua b/deps/premake/curl.lua new file mode 100644 index 00000000..8db164e1 --- /dev/null +++ b/deps/premake/curl.lua @@ -0,0 +1,73 @@ +curl = { + source = path.join(dependencies.basePath, "curl"), +} + +function curl.import() + links { "curl" } + + filter "toolset:msc*" + links { "Crypt32.lib" } + filter {} + + curl.includes() +end + +function curl.includes() +filter "toolset:msc*" + includedirs { + path.join(curl.source, "include"), + } + + defines { + "CURL_STRICTER", + "CURL_STATICLIB", + "CURL_DISABLE_LDAP", + } +filter {} +end + +function curl.project() + if not os.istarget("windows") then + return + end + + project "curl" + language "C" + + curl.includes() + + includedirs { + path.join(curl.source, "lib"), + } + + files { + path.join(curl.source, "lib/**.c"), + path.join(curl.source, "lib/**.h"), + } + + defines { + "BUILDING_LIBCURL", + } + + filter "toolset:msc*" + + defines { + "USE_SCHANNEL", + "USE_WINDOWS_SSPI", + "USE_THREADS_WIN32", + } + + filter "toolset:not msc*" + + defines { + "USE_GNUTLS", + "USE_THREADS_POSIX", + } + + filter {} + + warnings "Off" + kind "StaticLib" +end + +table.insert(dependencies, curl) \ No newline at end of file diff --git a/src/common/utils/http.cpp b/src/common/utils/http.cpp index 3cb59991..6a167260 100644 --- a/src/common/utils/http.cpp +++ b/src/common/utils/http.cpp @@ -1,48 +1,100 @@ #include "http.hpp" -#include "nt.hpp" -#include +#include +#include + +#pragma comment(lib, "ws2_32.lib") namespace utils::http { - std::optional get_data(const std::string& url) + namespace { - CComPtr stream; - - if (FAILED(URLOpenBlockingStreamA(nullptr, url.data(), &stream, 0, nullptr))) + struct progress_helper { - return {}; - } + const std::function* callback{}; + std::exception_ptr exception{}; + }; - char buffer[0x1000]; - std::string result; - - HRESULT status{}; - - do + 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*/) { - DWORD bytes_read = 0; - status = stream->Read(buffer, sizeof(buffer), &bytes_read); + auto* helper = static_cast(clientp); - if (bytes_read > 0) + try { - result.append(buffer, bytes_read); + if (*helper->callback) + { + (*helper->callback)(dlnow); + } + } + catch(...) + { + helper->exception = std::current_exception(); + return -1; } - } - while (SUCCEEDED(status) && status != S_FALSE); - if (FAILED(status)) + return 0; + } + + size_t write_callback(void* contents, const size_t size, const size_t nmemb, void* userp) { - return {}; - } + auto* buffer = static_cast(userp); - return {result}; + const auto total_size = size * nmemb; + buffer->append(static_cast(contents), total_size); + return total_size; + } } - std::future> get_data_async(const std::string& url) + std::optional get_data(const std::string& url, const headers& headers, const std::function& callback) { - return std::async(std::launch::async, [url]() + curl_slist* header_list = nullptr; + auto* curl = curl_easy_init(); + if (!curl) { - return get_data(url); + return {}; + } + + auto _ = gsl::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()); + } + + 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); + + if (curl_easy_perform(curl) == CURLE_OK) + { + return {std::move(buffer)}; + } + + if(helper.exception) + { + std::rethrow_exception(helper.exception); + } + + return {}; + } + + std::future> get_data_async(const std::string& url, const headers& headers) + { + return std::async(std::launch::async, [url, headers]() + { + return get_data(url, headers); }); } } diff --git a/src/common/utils/http.hpp b/src/common/utils/http.hpp index 65628a9f..b5248bc9 100644 --- a/src/common/utils/http.hpp +++ b/src/common/utils/http.hpp @@ -6,6 +6,8 @@ namespace utils::http { - std::optional get_data(const std::string& url); - std::future> get_data_async(const std::string& url); + using headers = std::unordered_map; + + std::optional get_data(const std::string& url, const headers& headers = {}, const std::function& callback = {}); + std::future> get_data_async(const std::string& url, const headers& headers = {}); }