Prepare autoupdating
This commit is contained in:
parent
221383d116
commit
6659650722
7
.gitmodules
vendored
7
.gitmodules
vendored
@ -4,3 +4,10 @@
|
|||||||
[submodule "deps/asmjit"]
|
[submodule "deps/asmjit"]
|
||||||
path = deps/asmjit
|
path = deps/asmjit
|
||||||
url = https://github.com/asmjit/asmjit.git
|
url = https://github.com/asmjit/asmjit.git
|
||||||
|
[submodule "deps/curl"]
|
||||||
|
path = deps/curl
|
||||||
|
url = https://github.com/curl/curl.git
|
||||||
|
[submodule "deps/zlib"]
|
||||||
|
path = deps/zlib
|
||||||
|
url = https://github.com/madler/zlib.git
|
||||||
|
branch = develop
|
||||||
|
1
deps/curl
vendored
Submodule
1
deps/curl
vendored
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit a8a4abb2ae97c9a42db0cb82c7f1c508b01d75f3
|
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)
|
43
deps/premake/minizip.lua
vendored
Normal file
43
deps/premake/minizip.lua
vendored
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
minizip = {
|
||||||
|
source = path.join(dependencies.basePath, "zlib/contrib/minizip"),
|
||||||
|
}
|
||||||
|
|
||||||
|
function minizip.import()
|
||||||
|
links { "minizip" }
|
||||||
|
zlib.import()
|
||||||
|
minizip.includes()
|
||||||
|
end
|
||||||
|
|
||||||
|
function minizip.includes()
|
||||||
|
includedirs {
|
||||||
|
minizip.source
|
||||||
|
}
|
||||||
|
|
||||||
|
zlib.includes()
|
||||||
|
end
|
||||||
|
|
||||||
|
function minizip.project()
|
||||||
|
project "minizip"
|
||||||
|
language "C"
|
||||||
|
|
||||||
|
minizip.includes()
|
||||||
|
|
||||||
|
files {
|
||||||
|
path.join(minizip.source, "*.h"),
|
||||||
|
path.join(minizip.source, "*.c"),
|
||||||
|
}
|
||||||
|
|
||||||
|
removefiles {
|
||||||
|
path.join(minizip.source, "miniunz.c"),
|
||||||
|
path.join(minizip.source, "minizip.c"),
|
||||||
|
}
|
||||||
|
|
||||||
|
defines {
|
||||||
|
"_CRT_SECURE_NO_DEPRECATE",
|
||||||
|
}
|
||||||
|
|
||||||
|
warnings "Off"
|
||||||
|
kind "StaticLib"
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(dependencies, minizip)
|
39
deps/premake/zlib.lua
vendored
Normal file
39
deps/premake/zlib.lua
vendored
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
zlib = {
|
||||||
|
source = path.join(dependencies.basePath, "zlib"),
|
||||||
|
}
|
||||||
|
|
||||||
|
function zlib.import()
|
||||||
|
links { "zlib" }
|
||||||
|
zlib.includes()
|
||||||
|
end
|
||||||
|
|
||||||
|
function zlib.includes()
|
||||||
|
includedirs {
|
||||||
|
zlib.source
|
||||||
|
}
|
||||||
|
|
||||||
|
defines {
|
||||||
|
"ZLIB_CONST",
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function zlib.project()
|
||||||
|
project "zlib"
|
||||||
|
language "C"
|
||||||
|
|
||||||
|
zlib.includes()
|
||||||
|
|
||||||
|
files {
|
||||||
|
path.join(zlib.source, "*.h"),
|
||||||
|
path.join(zlib.source, "*.c"),
|
||||||
|
}
|
||||||
|
|
||||||
|
defines {
|
||||||
|
"_CRT_SECURE_NO_DEPRECATE",
|
||||||
|
}
|
||||||
|
|
||||||
|
warnings "Off"
|
||||||
|
kind "StaticLib"
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(dependencies, zlib)
|
1
deps/zlib
vendored
Submodule
1
deps/zlib
vendored
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit ec3df00224d4b396e2ac6586ab5d25f673caa4c2
|
127
src/common/utils/com.cpp
Normal file
127
src/common/utils/com.cpp
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
#include "com.hpp"
|
||||||
|
#include "nt.hpp"
|
||||||
|
#include "string.hpp"
|
||||||
|
#include "finally.hpp"
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include <ShlObj.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace utils::com
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
[[maybe_unused]] class _
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
_()
|
||||||
|
{
|
||||||
|
if(FAILED(CoInitialize(nullptr)))
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Failed to initialize the component object model");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~_()
|
||||||
|
{
|
||||||
|
CoUninitialize();
|
||||||
|
}
|
||||||
|
} __;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool select_folder(std::string& out_folder, const std::string& title, const std::string& selected_folder)
|
||||||
|
{
|
||||||
|
CComPtr<IFileOpenDialog> file_dialog{};
|
||||||
|
if(FAILED(CoCreateInstance(CLSID_FileOpenDialog, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&file_dialog))))
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Failed to create co instance");
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD dw_options;
|
||||||
|
if(FAILED(file_dialog->GetOptions(&dw_options)))
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Failed to get options");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(FAILED(file_dialog->SetOptions(dw_options | FOS_PICKFOLDERS)))
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Failed to set options");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring wide_title(title.begin(), title.end());
|
||||||
|
if(FAILED(file_dialog->SetTitle(wide_title.data())))
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Failed to set title");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!selected_folder.empty())
|
||||||
|
{
|
||||||
|
file_dialog->ClearClientData();
|
||||||
|
|
||||||
|
std::wstring wide_selected_folder(selected_folder.begin(), selected_folder.end());
|
||||||
|
for (auto& chr : wide_selected_folder)
|
||||||
|
{
|
||||||
|
if (chr == L'/')
|
||||||
|
{
|
||||||
|
chr = L'\\';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IShellItem* shell_item = nullptr;
|
||||||
|
if(FAILED(SHCreateItemFromParsingName(wide_selected_folder.data(), NULL, IID_PPV_ARGS(&shell_item))))
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Failed to create item from parsing name");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FAILED(file_dialog->SetDefaultFolder(shell_item)))
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Failed to set default folder");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto result = file_dialog->Show(nullptr);
|
||||||
|
if(result == HRESULT_FROM_WIN32(ERROR_CANCELLED))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FAILED(result))
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Failed to show dialog");
|
||||||
|
}
|
||||||
|
|
||||||
|
CComPtr<IShellItem> result_item{};
|
||||||
|
if(FAILED(file_dialog->GetResult(&result_item)))
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Failed to get result");
|
||||||
|
}
|
||||||
|
|
||||||
|
PWSTR raw_path = nullptr;
|
||||||
|
if(FAILED(result_item->GetDisplayName(SIGDN_FILESYSPATH, &raw_path)))
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Failed to get path display name");
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto _ = finally([raw_path]()
|
||||||
|
{
|
||||||
|
CoTaskMemFree(raw_path);
|
||||||
|
});
|
||||||
|
|
||||||
|
const std::wstring result_path = raw_path;
|
||||||
|
out_folder = string::convert(result_path);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
CComPtr<IProgressDialog> create_progress_dialog()
|
||||||
|
{
|
||||||
|
CComPtr<IProgressDialog> progress_dialog{};
|
||||||
|
if(FAILED(CoCreateInstance(CLSID_ProgressDialog, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&progress_dialog))))
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Failed to create co instance");
|
||||||
|
}
|
||||||
|
|
||||||
|
return progress_dialog;
|
||||||
|
}
|
||||||
|
}
|
11
src/common/utils/com.hpp
Normal file
11
src/common/utils/com.hpp
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "nt.hpp"
|
||||||
|
#include <ShlObj.h>
|
||||||
|
#include <atlbase.h>
|
||||||
|
|
||||||
|
namespace utils::com
|
||||||
|
{
|
||||||
|
bool select_folder(std::string& out_folder, const std::string& title = "Select a Folder", const std::string& selected_folder = {});
|
||||||
|
CComPtr<IProgressDialog> create_progress_dialog();
|
||||||
|
}
|
168
src/common/utils/compression.cpp
Normal file
168
src/common/utils/compression.cpp
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
#include "memory.hpp"
|
||||||
|
#include "compression.hpp"
|
||||||
|
|
||||||
|
#include <zlib.h>
|
||||||
|
#include <zip.h>
|
||||||
|
|
||||||
|
#include "io.hpp"
|
||||||
|
#include "finally.hpp"
|
||||||
|
|
||||||
|
namespace utils::compression
|
||||||
|
{
|
||||||
|
namespace zlib
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
class zlib_stream
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
zlib_stream()
|
||||||
|
{
|
||||||
|
memset(&stream_, 0, sizeof(stream_));
|
||||||
|
valid_ = inflateInit(&stream_) == Z_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
zlib_stream(zlib_stream&&) = delete;
|
||||||
|
zlib_stream(const zlib_stream&) = delete;
|
||||||
|
zlib_stream& operator=(zlib_stream&&) = delete;
|
||||||
|
zlib_stream& operator=(const zlib_stream&) = delete;
|
||||||
|
|
||||||
|
~zlib_stream()
|
||||||
|
{
|
||||||
|
if (valid_)
|
||||||
|
{
|
||||||
|
inflateEnd(&stream_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
z_stream& get()
|
||||||
|
{
|
||||||
|
return stream_; //
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_valid() const
|
||||||
|
{
|
||||||
|
return valid_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool valid_{false};
|
||||||
|
z_stream stream_{};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string decompress(const std::string& data)
|
||||||
|
{
|
||||||
|
std::string buffer{};
|
||||||
|
zlib_stream stream_container{};
|
||||||
|
if (!stream_container.is_valid())
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret{};
|
||||||
|
size_t offset = 0;
|
||||||
|
static thread_local uint8_t dest[CHUNK] = {0};
|
||||||
|
auto& stream = stream_container.get();
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
const auto input_size = std::min(sizeof(dest), data.size() - offset);
|
||||||
|
stream.avail_in = static_cast<uInt>(input_size);
|
||||||
|
stream.next_in = reinterpret_cast<const Bytef*>(data.data()) + offset;
|
||||||
|
offset += stream.avail_in;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
stream.avail_out = sizeof(dest);
|
||||||
|
stream.next_out = dest;
|
||||||
|
|
||||||
|
ret = inflate(&stream, Z_NO_FLUSH);
|
||||||
|
if (ret != Z_OK && ret != Z_STREAM_END)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.insert(buffer.end(), dest, dest + sizeof(dest) - stream.avail_out);
|
||||||
|
}
|
||||||
|
while (stream.avail_out == 0);
|
||||||
|
}
|
||||||
|
while (ret != Z_STREAM_END);
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string compress(const std::string& data)
|
||||||
|
{
|
||||||
|
std::string result{};
|
||||||
|
auto length = compressBound(static_cast<uLong>(data.size()));
|
||||||
|
result.resize(length);
|
||||||
|
|
||||||
|
if (compress2(reinterpret_cast<Bytef*>(result.data()), &length,
|
||||||
|
reinterpret_cast<const Bytef*>(data.data()), static_cast<uLong>(data.size()),
|
||||||
|
Z_BEST_COMPRESSION) != Z_OK)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
result.resize(length);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace zip
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
bool add_file(zipFile& zip_file, const std::string& filename, const std::string& data)
|
||||||
|
{
|
||||||
|
const auto zip_64 = data.size() > 0xffffffff ? 1 : 0;
|
||||||
|
if (ZIP_OK != zipOpenNewFileInZip64(zip_file, filename.data(), nullptr, nullptr, 0, nullptr, 0, nullptr,
|
||||||
|
Z_DEFLATED, Z_BEST_COMPRESSION, zip_64))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto _ = finally([&zip_file]()
|
||||||
|
{
|
||||||
|
zipCloseFileInZip(zip_file);
|
||||||
|
});
|
||||||
|
|
||||||
|
return ZIP_OK == zipWriteInFileInZip(zip_file, data.data(), static_cast<unsigned>(data.size()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void archive::add(std::string filename, std::string data)
|
||||||
|
{
|
||||||
|
this->files_[std::move(filename)] = std::move(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool archive::write(const std::string& filename, const std::string& comment)
|
||||||
|
{
|
||||||
|
// Hack to create the directory :3
|
||||||
|
io::write_file(filename, {});
|
||||||
|
io::remove_file(filename);
|
||||||
|
|
||||||
|
auto* zip_file = zipOpen64(filename.data(), 0);
|
||||||
|
if (!zip_file)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto _ = finally([&zip_file, &comment]()
|
||||||
|
{
|
||||||
|
zipClose(zip_file, comment.empty() ? nullptr : comment.data());
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const auto& file : this->files_)
|
||||||
|
{
|
||||||
|
if (!add_file(zip_file, file.first, file.second))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
28
src/common/utils/compression.hpp
Normal file
28
src/common/utils/compression.hpp
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#define CHUNK 16384u
|
||||||
|
|
||||||
|
namespace utils::compression
|
||||||
|
{
|
||||||
|
namespace zlib
|
||||||
|
{
|
||||||
|
std::string compress(const std::string& data);
|
||||||
|
std::string decompress(const std::string& data);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace zip
|
||||||
|
{
|
||||||
|
class archive
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void add(std::string filename, std::string data);
|
||||||
|
bool write(const std::string& filename, const std::string& comment = {});
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unordered_map<std::string, std::string> files_;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
@ -1,48 +1,112 @@
|
|||||||
#include "http.hpp"
|
#include "http.hpp"
|
||||||
#include "nt.hpp"
|
#include "finally.hpp"
|
||||||
#include <atlcomcli.h>
|
#include <curl/curl.h>
|
||||||
|
|
||||||
|
#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
|
||||||
|
|
||||||
if (FAILED(URLOpenBlockingStreamA(nullptr, url.data(), &stream, 0, nullptr)))
|
|
||||||
{
|
{
|
||||||
return {};
|
const std::function<void(size_t)>* callback{};
|
||||||
}
|
std::exception_ptr exception{};
|
||||||
|
};
|
||||||
|
|
||||||
char buffer[0x1000];
|
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*/)
|
||||||
std::string result;
|
|
||||||
|
|
||||||
HRESULT status{};
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
{
|
||||||
DWORD bytes_read = 0;
|
auto* helper = static_cast<progress_helper*>(clientp);
|
||||||
status = stream->Read(buffer, sizeof(buffer), &bytes_read);
|
|
||||||
|
|
||||||
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<std::string*>(userp);
|
||||||
}
|
|
||||||
|
|
||||||
return {result};
|
const auto total_size = size * nmemb;
|
||||||
|
buffer->append(static_cast<char*>(contents), total_size);
|
||||||
|
return total_size;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
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 _ = 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);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, true);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_USERAGENT, "boiii/1.0");
|
||||||
|
curl_easy_setopt(curl, CURLOPT_FAILONERROR, true);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::future<std::optional<std::string>> get_data_async(const std::string& url, const headers& headers)
|
||||||
|
{
|
||||||
|
return std::async(std::launch::async, [url, headers]()
|
||||||
|
{
|
||||||
|
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 = {});
|
||||||
|
std::future<std::optional<std::string>> get_data_async(const std::string& url, const headers& headers = {});
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user