parent
97bb7ae49b
commit
ce4ab4b7cc
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -28,3 +28,6 @@
|
|||||||
[submodule "deps/stb"]
|
[submodule "deps/stb"]
|
||||||
path = deps/stb
|
path = deps/stb
|
||||||
url = https://github.com/nothings/stb.git
|
url = https://github.com/nothings/stb.git
|
||||||
|
[submodule "deps/curl"]
|
||||||
|
path = deps/curl
|
||||||
|
url = https://github.com/curl/curl.git
|
||||||
|
1
deps/curl
vendored
Submodule
1
deps/curl
vendored
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 7ce140ba97de1bf3e27299a72b0cc229c9e1364e
|
73
deps/premake/curl.lua
vendored
Normal file
73
deps/premake/curl.lua
vendored
Normal file
@ -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)
|
@ -1,48 +1,128 @@
|
|||||||
#include "http.hpp"
|
#include "http.hpp"
|
||||||
#include "nt.hpp"
|
#include <curl/curl.h>
|
||||||
#include <atlcomcli.h>
|
#include "finally.hpp"
|
||||||
|
|
||||||
|
#pragma comment(lib, "ws2_32.lib")
|
||||||
|
|
||||||
namespace utils::http
|
namespace utils::http
|
||||||
{
|
{
|
||||||
std::optional<std::string> get_data(const std::string& url)
|
namespace
|
||||||
{
|
{
|
||||||
CComPtr<IStream> stream;
|
struct progress_helper
|
||||||
|
{
|
||||||
|
const std::function<void(size_t)>* callback{};
|
||||||
|
std::exception_ptr exception{};
|
||||||
|
};
|
||||||
|
|
||||||
if (FAILED(URLOpenBlockingStreamA(nullptr, url.data(), &stream, 0, nullptr)))
|
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*/)
|
||||||
|
{
|
||||||
|
auto* helper = static_cast<progress_helper*>(clientp);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (*helper->callback)
|
||||||
|
{
|
||||||
|
(*helper->callback)(dlnow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> get_data(const std::string& url, const headers& headers,
|
||||||
|
const std::function<void(size_t)>& callback, const uint32_t retries)
|
||||||
|
{
|
||||||
|
curl_slist* header_list = nullptr;
|
||||||
|
auto* curl = curl_easy_init();
|
||||||
|
if (!curl)
|
||||||
{
|
{
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
char buffer[0x1000];
|
auto _ = utils::finally([&]()
|
||||||
std::string result;
|
|
||||||
|
|
||||||
HRESULT status{};
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
DWORD bytes_read = 0;
|
|
||||||
status = stream->Read(buffer, sizeof(buffer), &bytes_read);
|
|
||||||
|
|
||||||
if (bytes_read > 0)
|
|
||||||
{
|
{
|
||||||
result.append(buffer, bytes_read);
|
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, 0L);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_USERAGENT, "xlabs-updater/1.0");
|
||||||
|
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
|
||||||
|
|
||||||
|
for (auto i = 0u; i < retries + 1; ++i)
|
||||||
|
{
|
||||||
|
// Due to CURLOPT_FAILONERROR, CURLE_OK will not be met when the server returns 400 or 500
|
||||||
|
if (curl_easy_perform(curl) == CURLE_OK)
|
||||||
|
{
|
||||||
|
long http_code = 0;
|
||||||
|
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
|
||||||
|
|
||||||
|
if (http_code >= 200)
|
||||||
|
{
|
||||||
|
return { std::move(buffer) };
|
||||||
|
}
|
||||||
|
|
||||||
|
throw std::runtime_error(
|
||||||
|
"Bad status code " + std::to_string(http_code) + " met while trying to download file " + url);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (helper.exception)
|
||||||
|
{
|
||||||
|
std::rethrow_exception(helper.exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
long http_code = 0;
|
||||||
|
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
|
||||||
|
|
||||||
|
if (http_code > 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while (SUCCEEDED(status) && status != S_FALSE);
|
|
||||||
|
|
||||||
if (FAILED(status))
|
return {};
|
||||||
{
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {result};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::future<std::optional<std::string>> get_data_async(const std::string& url)
|
std::future<std::optional<std::string>> get_data_async(const std::string& url, const headers& headers)
|
||||||
{
|
{
|
||||||
return std::async(std::launch::async, [url]()
|
return std::async(std::launch::async, [url, headers]()
|
||||||
{
|
{
|
||||||
return get_data(url);
|
return get_data(url, headers);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
|
|
||||||
namespace utils::http
|
namespace utils::http
|
||||||
{
|
{
|
||||||
std::optional<std::string> get_data(const std::string& url);
|
using headers = std::unordered_map<std::string, std::string>;
|
||||||
std::future<std::optional<std::string>> get_data_async(const std::string& url);
|
|
||||||
|
std::optional<std::string> get_data(const std::string& url, const headers& headers = {}, const std::function<void(size_t)>& callback = {}, uint32_t retries = 2);
|
||||||
|
std::future<std::optional<std::string>> get_data_async(const std::string& url, const headers& headers = {});
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user