Continue move to CMake
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
project(siadrive)
|
||||
include(ExternalProject)
|
||||
|
||||
cmake_minimum_required(VERSION 3.3)
|
||||
|
||||
@@ -16,4 +17,15 @@ set_target_properties(siadrive.api
|
||||
)
|
||||
if (MSVC OR MINGW)
|
||||
target_link_libraries(siadrive.api Shlwapi.lib)
|
||||
endif()
|
||||
|
||||
if (MSVC)
|
||||
#3rd-party
|
||||
set(3RD_PARTY_INCLUDES ${3RD_PARTY_INCLUDES} ${CMAKE_BINARY_DIR}/external/include)
|
||||
ExternalProject_Add(curl_project
|
||||
URL https://github.com/curl/curl/archive/curl-7_53_1.tar.gz
|
||||
PREFIX ${CMAKE_BINARY_DIR}/external/builds/curl
|
||||
CMAKE_ARGS -DCURL_STATICLIB=ON -DBUILD_TESTING=OFF -DBUILD_CURL_EXE=OFF -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/external/)
|
||||
add_dependencies(siadrive.api curl_project)
|
||||
target_link_libraries(siadrive.api ${CMAKE_BINARY_DIR}/external/lib/libcurl.lib)
|
||||
endif()
|
@@ -20,7 +20,9 @@ public:
|
||||
private:
|
||||
std::unique_ptr<CSiaCurl> _siaCurl;
|
||||
CSiaDriveConfig* _siaDriveConfig;
|
||||
#ifdef _WIN32
|
||||
HANDLE _stopEvent;
|
||||
#endif
|
||||
std::unique_ptr<std::thread> _thread;
|
||||
std::mutex _startStopMutex;
|
||||
std::function<void(const CSiaCurl&, CSiaDriveConfig*)> _AutoThreadCallback;
|
||||
|
@@ -44,6 +44,7 @@
|
||||
#include <sstring.h>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
@@ -166,32 +167,6 @@ inline static Hastings CalculateAverageUploadPrice(const std::vector<IHost>& hos
|
||||
return CalculateAveragePrice<IHost, Hastings>(hosts, [](const IHost& host)->Hastings { return host.GetUploadPrice(); });
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static T& ReplaceStringInPlace(T& subject, const T& search, const T& replace)
|
||||
{
|
||||
size_t pos = 0;
|
||||
while ((pos = subject.find(search, pos)) != SString::npos)
|
||||
{
|
||||
subject.replace(pos, search.length(), replace);
|
||||
pos += replace.length();
|
||||
}
|
||||
|
||||
return subject;
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
inline static T& ReplaceStringInPlace(T& subject, typename T::value_type* search, const T& replace)
|
||||
{
|
||||
return ReplaceStringInPlace(subject, T(search), replace);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline static T& ReplaceStringInPlace(T& subject, typename T::value_type* search, typename T::value_type* replace)
|
||||
{
|
||||
return ReplaceStringInPlace(subject, T(search), T(replace));
|
||||
}
|
||||
|
||||
BOOL SIADRIVE_EXPORTABLE RetryAction(std::function<BOOL()> func, std::uint16_t retryCount, const DWORD& retryDelay);
|
||||
|
||||
BOOL SIADRIVE_EXPORTABLE RetryDeleteFileIfExists(const SString& filePath);
|
||||
|
60
include/siadrive_api/siacurl.h
Normal file
60
include/siadrive_api/siacurl.h
Normal file
@@ -0,0 +1,60 @@
|
||||
#ifndef _SIACURL_H
|
||||
#define _SIACURL_H
|
||||
|
||||
#include <siacommon.h>
|
||||
|
||||
NS_BEGIN(Sia)
|
||||
NS_BEGIN(Api)
|
||||
|
||||
class SIADRIVE_EXPORTABLE CSiaCurl
|
||||
{
|
||||
public:
|
||||
enum class _SiaCurlError
|
||||
{
|
||||
Success,
|
||||
ServerVersionMismatch,
|
||||
InvalidRequiredVersion,
|
||||
NoResponse,
|
||||
HttpError,
|
||||
UnknownFailure
|
||||
};
|
||||
|
||||
typedef std::unordered_map<SString, SString> _HttpParameters;
|
||||
|
||||
public:
|
||||
CSiaCurl();
|
||||
|
||||
CSiaCurl(const SiaHostConfig& hostConfig);
|
||||
|
||||
public:
|
||||
~CSiaCurl();
|
||||
|
||||
private:
|
||||
Property(SiaHostConfig, HostConfig, public, public)
|
||||
|
||||
public:
|
||||
static SString UrlEncode(const SString& data, const bool& allowSlash = false);
|
||||
|
||||
private:
|
||||
static _SiaCurlError CheckApiError(const json& result);
|
||||
static _SiaCurlError CheckHttpError(const std::string& result);
|
||||
|
||||
private:
|
||||
std::string ConstructPath(const SString& relativePath) const;
|
||||
_SiaCurlError _Get(const SString& path, const _HttpParameters& parameters, json& response) const;
|
||||
bool CheckVersion(_SiaCurlError& error) const;
|
||||
_SiaCurlError ProcessResponse(const int& res, const int& httpCode, const std::string& result, json& response) const;
|
||||
|
||||
public:
|
||||
SString GetServerVersion() const;
|
||||
_SiaCurlError Get(const SString& path, json& result) const;
|
||||
_SiaCurlError Get(const SString& path, const _HttpParameters& parameters, json& result) const;
|
||||
_SiaCurlError Post(const SString& path, const _HttpParameters& parameters, json& response) const;
|
||||
};
|
||||
|
||||
typedef CSiaCurl::_SiaCurlError SiaCurlError;
|
||||
typedef CSiaCurl::_HttpParameters HttpParameters;
|
||||
|
||||
NS_END(2)
|
||||
|
||||
#endif //_SIACURL_H
|
@@ -1,4 +1,5 @@
|
||||
#include "autothread.h"
|
||||
#include <autothread.h>
|
||||
#include <siacurl.h>
|
||||
|
||||
using namespace Sia::Api;
|
||||
|
||||
|
230
src/siadrive_api/siacurl.cpp
Normal file
230
src/siadrive_api/siacurl.cpp
Normal file
@@ -0,0 +1,230 @@
|
||||
#include <siacurl.h>
|
||||
#include <curl/curl.h>
|
||||
|
||||
using namespace Sia::Api;
|
||||
|
||||
CSiaCurl::CSiaCurl()
|
||||
{
|
||||
SetHostConfig({ L"localhost", 9980, L""});
|
||||
}
|
||||
|
||||
CSiaCurl::CSiaCurl(const SiaHostConfig& hostConfig)
|
||||
{
|
||||
SetHostConfig(hostConfig);
|
||||
}
|
||||
|
||||
CSiaCurl::~CSiaCurl()
|
||||
{
|
||||
}
|
||||
|
||||
SString CSiaCurl::UrlEncode(const SString& data, const bool& allowSlash)
|
||||
{
|
||||
CURL* curlHandle = curl_easy_init();
|
||||
curl_easy_reset(curlHandle);
|
||||
|
||||
char* value = curl_easy_escape(curlHandle, SString::ToUtf8(data).c_str(), 0);
|
||||
SString ret = value;
|
||||
curl_free(value);
|
||||
if (allowSlash)
|
||||
{
|
||||
ret.Replace("%2F", "/");
|
||||
}
|
||||
|
||||
curl_easy_cleanup(curlHandle);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
SiaCurlError CSiaCurl::CheckApiError(const json& result)
|
||||
{
|
||||
SiaCurlError ret = SiaCurlError::Success;
|
||||
if (result.find("message") != result.end())
|
||||
{
|
||||
ret = SiaCurlError::UnknownFailure;
|
||||
|
||||
const std::string msg = result["message"].get<std::string>();
|
||||
if ((msg.length() >= 3))
|
||||
{
|
||||
if ((msg.substr(0, 3) == "404"))
|
||||
{
|
||||
ret = SiaCurlError::HttpError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string CSiaCurl::ConstructPath(const SString& relativePath) const
|
||||
{
|
||||
const std::string ret = "http://" + GetHostConfig().HostName + ":" + std::to_string(GetHostConfig().HostPort) + UrlEncode(relativePath, true);
|
||||
return ret;
|
||||
}
|
||||
|
||||
SiaCurlError CSiaCurl::CheckHttpError(const std::string& result)
|
||||
{
|
||||
if (result.length() && ((result.length() < 2) || (result[0] != '{')))
|
||||
{
|
||||
return SiaCurlError::HttpError;
|
||||
}
|
||||
|
||||
return SiaCurlError::Success;
|
||||
}
|
||||
|
||||
SiaCurlError CSiaCurl::ProcessResponse(const int& res, const int& httpCode, const std::string& result, json& response) const
|
||||
{
|
||||
SiaCurlError ret;
|
||||
if ((res == CURLE_OK) && ((httpCode >= 200) && (httpCode <300)))
|
||||
{
|
||||
ret = CheckHttpError(result);
|
||||
if (ApiSuccess(ret))
|
||||
{
|
||||
ret = (result.length() ? CheckApiError((response = json::parse(result.c_str()))) : SiaCurlError::Success);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((res == CURLE_COULDNT_RESOLVE_HOST) || (res == CURLE_COULDNT_CONNECT))
|
||||
{
|
||||
ret = SiaCurlError::NoResponse;
|
||||
}
|
||||
else if (httpCode)
|
||||
{
|
||||
ret = SiaCurlError::HttpError;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = SiaCurlError::UnknownFailure;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
SiaCurlError CSiaCurl::_Get(const SString& path, const HttpParameters& parameters, json& response) const
|
||||
{
|
||||
CURL* curlHandle = curl_easy_init();
|
||||
curl_easy_reset(curlHandle);
|
||||
SString url = ConstructPath(path);
|
||||
if (parameters.size())
|
||||
{
|
||||
url += "?";
|
||||
for (const auto& param : parameters)
|
||||
{
|
||||
if (url[url.Length() - 1] != '?')
|
||||
{
|
||||
url += "&";
|
||||
}
|
||||
url += (param.first + "=" + UrlEncode(param.second));
|
||||
}
|
||||
}
|
||||
curl_easy_setopt(curlHandle, CURLOPT_USERAGENT, "Sia-Agent");
|
||||
curl_easy_setopt(curlHandle, CURLOPT_URL, SString::ToUtf8(url).c_str());
|
||||
curl_easy_setopt(curlHandle, CURLOPT_WRITEFUNCTION, static_cast<size_t(*)(char*, size_t, size_t, void *)>([](char *buffer, size_t size, size_t nitems, void *outstream) -> size_t
|
||||
{
|
||||
(*reinterpret_cast<SString*>(outstream)) += std::string(buffer, size * nitems);
|
||||
return size * nitems;
|
||||
}));
|
||||
|
||||
SString result;
|
||||
curl_easy_setopt(curlHandle, CURLOPT_WRITEDATA, &result);
|
||||
const CURLcode res = curl_easy_perform(curlHandle);
|
||||
|
||||
long httpCode = 0;
|
||||
curl_easy_getinfo(curlHandle, CURLINFO_RESPONSE_CODE, &httpCode);
|
||||
|
||||
SiaCurlError ret = ProcessResponse(res, httpCode, result, response);
|
||||
curl_easy_cleanup(curlHandle);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool CSiaCurl::CheckVersion(SiaCurlError& error) const
|
||||
{
|
||||
error = SiaCurlError::InvalidRequiredVersion;
|
||||
if (GetHostConfig().RequiredVersion.Length())
|
||||
{
|
||||
error = SiaCurlError::NoResponse;
|
||||
const SString serverVersion = GetServerVersion();
|
||||
if (serverVersion.Length())
|
||||
{
|
||||
error = (serverVersion == GetHostConfig().RequiredVersion) ? SiaCurlError::Success : SiaCurlError::ServerVersionMismatch;
|
||||
}
|
||||
}
|
||||
|
||||
return ApiSuccess(error);
|
||||
}
|
||||
|
||||
SString CSiaCurl::GetServerVersion() const
|
||||
{
|
||||
json response;
|
||||
if (ApiSuccess(_Get(L"/daemon/version", {}, response)))
|
||||
{
|
||||
return response["version"].get<std::string>();
|
||||
}
|
||||
|
||||
return L"";
|
||||
}
|
||||
|
||||
SiaCurlError CSiaCurl::Get(const SString& path, json& response) const
|
||||
{
|
||||
SiaCurlError ret;
|
||||
if (CheckVersion(ret))
|
||||
{
|
||||
ret = _Get(path, {}, response);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
SiaCurlError CSiaCurl::Get(const SString& path, const HttpParameters& parameters, json& response) const
|
||||
{
|
||||
SiaCurlError ret;
|
||||
if (CheckVersion(ret))
|
||||
{
|
||||
ret = _Get(path, parameters, response);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
SiaCurlError CSiaCurl::Post(const SString& path, const HttpParameters& parameters, json& response) const
|
||||
{
|
||||
SiaCurlError ret;
|
||||
if (CheckVersion(ret))
|
||||
{
|
||||
CURL* curlHandle = curl_easy_init();
|
||||
curl_easy_reset(curlHandle);
|
||||
|
||||
curl_easy_setopt(curlHandle, CURLOPT_USERAGENT, "Sia-Agent");
|
||||
curl_easy_setopt(curlHandle, CURLOPT_URL, ConstructPath(path).c_str());
|
||||
curl_easy_setopt(curlHandle, CURLOPT_WRITEFUNCTION, static_cast<size_t(*)(char*, size_t, size_t, void *)>([](char *buffer, size_t size, size_t nitems, void *outstream) -> size_t
|
||||
{
|
||||
(*reinterpret_cast<SString*>(outstream)) += std::string(buffer, size * nitems);
|
||||
return size * nitems;
|
||||
}));
|
||||
|
||||
SString fields;
|
||||
for (const auto& param : parameters)
|
||||
{
|
||||
if (fields.Length())
|
||||
{
|
||||
fields += "&";
|
||||
}
|
||||
|
||||
fields += (param.first + "=" + param.second);
|
||||
}
|
||||
curl_easy_setopt(curlHandle, CURLOPT_POSTFIELDS, &fields);
|
||||
|
||||
SString result;
|
||||
curl_easy_setopt(curlHandle, CURLOPT_WRITEDATA, &result);
|
||||
const CURLcode res = curl_easy_perform(curlHandle);
|
||||
|
||||
long httpCode = 0;
|
||||
curl_easy_getinfo(curlHandle, CURLINFO_RESPONSE_CODE, &httpCode);
|
||||
|
||||
ret = ProcessResponse(res, httpCode, result, response);
|
||||
curl_easy_cleanup(curlHandle);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
Reference in New Issue
Block a user