From 1d90f53527e3b1276ad3a140d5a5653c17606f15 Mon Sep 17 00:00:00 2001 From: Tixx <83774803+WiserTixx@users.noreply.github.com> Date: Tue, 7 Jan 2025 12:13:34 +0100 Subject: [PATCH 1/4] Add curl --- CMakeLists.txt | 2 ++ vcpkg.json | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 43a71aff..71fe053b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -104,6 +104,7 @@ set(PRJ_LIBRARIES httplib::httplib libzip::zip OpenSSL::SSL OpenSSL::Crypto + CURL::libcurl ${LUA_LIBRARIES} ) @@ -116,6 +117,7 @@ find_package(httplib CONFIG REQUIRED) find_package(libzip CONFIG REQUIRED) find_package(RapidJSON CONFIG REQUIRED) find_package(sol2 CONFIG REQUIRED) +find_package(CURL CONFIG REQUIRED) add_subdirectory("deps/toml11") include_directories(include) diff --git a/vcpkg.json b/vcpkg.json index 723a24de..cc7e74f8 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -13,6 +13,7 @@ "nlohmann-json", "openssl", "rapidjson", - "sol2" + "sol2", + "curl" ] } From d7e75ae0c7a6b7a9eb775ab8996b50320a6eeda2 Mon Sep 17 00:00:00 2001 From: Tixx <83774803+WiserTixx@users.noreply.github.com> Date: Tue, 7 Jan 2025 12:18:30 +0100 Subject: [PATCH 2/4] Fix windows build --- include/Profiling.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/Profiling.h b/include/Profiling.h index c0b23c89..7117983a 100644 --- a/include/Profiling.h +++ b/include/Profiling.h @@ -6,6 +6,9 @@ #include #include +#undef max +#undef min + namespace prof { using Duration = std::chrono::duration; From 26f1be0a51b98f073b41a217b7078b55d681d01a Mon Sep 17 00:00:00 2001 From: Tixx <83774803+WiserTixx@users.noreply.github.com> Date: Thu, 9 Jan 2025 08:05:05 +0100 Subject: [PATCH 3/4] Switch to curl --- include/Common.h | 4 +- include/Http.h | 5 ++- src/Common.cpp | 2 +- src/Http.cpp | 113 +++++++++++++++++++++++++++-------------------- src/TNetwork.cpp | 2 +- 5 files changed, 73 insertions(+), 53 deletions(-) diff --git a/include/Common.h b/include/Common.h index 9a4eddef..5242dcb6 100644 --- a/include/Common.h +++ b/include/Common.h @@ -82,11 +82,11 @@ class Application final { static std::vector GetBackendUrlsInOrder() { return { - "backend.beammp.com", + "https://backend.beammp.com", }; } - static std::string GetBackendUrlForAuth() { return "auth.beammp.com"; } + static std::string GetBackendUrlForAuth() { return "https://auth.beammp.com"; } static std::string GetBackendUrlForSocketIO() { return "https://backend.beammp.com"; } static void CheckForUpdates(); static std::array VersionStrToInts(const std::string& str); diff --git a/include/Http.h b/include/Http.h index 894a6ae7..6f2e7cc9 100644 --- a/include/Http.h +++ b/include/Http.h @@ -23,6 +23,7 @@ #include #include #include +#include #if defined(BEAMMP_LINUX) #pragma GCC diagnostic push @@ -38,8 +39,8 @@ namespace fs = std::filesystem; namespace Http { -std::string GET(const std::string& host, int port, const std::string& target, unsigned int* status = nullptr); -std::string POST(const std::string& host, int port, const std::string& target, const std::string& body, const std::string& ContentType, unsigned int* status = nullptr, const httplib::Headers& headers = {}); +std::string GET(const std::string& url, unsigned int* status = nullptr); +std::string POST(const std::string& url, const std::string& body, const std::string& ContentType, unsigned int* status = nullptr, const std::map& headers = {}); namespace Status { std::string ToString(int code); } diff --git a/src/Common.cpp b/src/Common.cpp index 017e4124..d1355695 100644 --- a/src/Common.cpp +++ b/src/Common.cpp @@ -215,7 +215,7 @@ void Application::CheckForUpdates() { // checks current version against latest version std::regex VersionRegex { R"(\d+\.\d+\.\d+\n*)" }; for (const auto& url : GetBackendUrlsInOrder()) { - auto Response = Http::GET(url, 443, "/v/s"); + auto Response = Http::GET(url + "/v/s"); bool Matches = std::regex_match(Response, VersionRegex); if (Matches) { auto MyVersion = ServerVersion(); diff --git a/src/Http.cpp b/src/Http.cpp index 02a92136..19d73d22 100644 --- a/src/Http.cpp +++ b/src/Http.cpp @@ -29,67 +29,86 @@ #include using json = nlohmann::json; -struct Connection { - std::string host {}; - int port {}; - Connection() = default; - Connection(std::string host, int port) - : host(host) - , port(port) {}; -}; -constexpr uint8_t CONNECTION_AMOUNT = 10; -static thread_local uint8_t write_index = 0; -static thread_local std::array connections; -static thread_local std::array, CONNECTION_AMOUNT> clients; - -[[nodiscard]] static std::shared_ptr getClient(Connection connectionInfo) { - for (uint8_t i = 0; i < CONNECTION_AMOUNT; i++) { - if (connectionInfo.host == connections[i].host - && connectionInfo.port == connections[i].port) { - beammp_tracef("Old client reconnected, with ip {} and port {}", connectionInfo.host, connectionInfo.port); - return clients[i]; - } - } - uint8_t i = write_index; - write_index++; - write_index %= CONNECTION_AMOUNT; - clients[i] = std::make_shared(connectionInfo.host, connectionInfo.port); - connections[i] = { connectionInfo.host, connectionInfo.port }; - beammp_tracef("New client connected, with ip {} and port {}", connectionInfo.host, connectionInfo.port); - return clients[i]; + +static size_t CurlWriteCallback(void* contents, size_t size, size_t nmemb, void* userp) { + std::string* Result = reinterpret_cast(userp); + std::string NewContents(reinterpret_cast(contents), size * nmemb); + *Result += NewContents; + return size * nmemb; } -std::string Http::GET(const std::string& host, int port, const std::string& target, unsigned int* status) { - std::shared_ptr client = getClient({ host, port }); - client->enable_server_certificate_verification(false); - client->set_address_family(AF_INET); - auto res = client->Get(target.c_str()); - if (res) { +std::string Http::GET(const std::string& url, unsigned int* status) { + std::string Ret; + static thread_local CURL* curl = curl_easy_init(); + if (curl) { + CURLcode res; + char errbuf[CURL_ERROR_SIZE]; + curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteCallback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&Ret); + curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10); // seconds + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf); + errbuf[0] = 0; + res = curl_easy_perform(curl); + if (res != CURLE_OK) { + beammp_error("GET to " + url + " failed: " + std::string(curl_easy_strerror(res))); + beammp_error("Curl error: " + std::string(errbuf)); + return Http::ErrorString; + } + if (status) { - *status = res->status; + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, status); } - return res->body; + } else { + beammp_error("Curl easy init failed"); return Http::ErrorString; } + return Ret; } -std::string Http::POST(const std::string& host, int port, const std::string& target, const std::string& body, const std::string& ContentType, unsigned int* status, const httplib::Headers& headers) { - std::shared_ptr client = getClient({ host, port }); - client->set_read_timeout(std::chrono::seconds(10)); - beammp_assert(client->is_valid()); - client->enable_server_certificate_verification(false); - client->set_address_family(AF_INET); - auto res = client->Post(target.c_str(), headers, body.c_str(), body.size(), ContentType.c_str()); - if (res) { +std::string Http::POST(const std::string& url, const std::string& body, const std::string& ContentType, unsigned int* status, const std::map& headers) { + std::string Ret; + static thread_local CURL* curl = curl_easy_init(); + if (curl) { + CURLcode res; + char errbuf[CURL_ERROR_SIZE]; + curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteCallback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&Ret); + curl_easy_setopt(curl, CURLOPT_POST, 1); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str()); + curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, body.size()); + struct curl_slist* list = nullptr; + list = curl_slist_append(list, ("Content-Type: " + ContentType).c_str()); + + for (auto [header, value] : headers) { + list = curl_slist_append(list, (header + value).c_str()); + } + + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list); + curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10); // seconds + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf); + errbuf[0] = 0; + res = curl_easy_perform(curl); + curl_slist_free_all(list); + if (res != CURLE_OK) { + beammp_error("POST to " + url + " failed: " + std::string(curl_easy_strerror(res))); + beammp_error("Curl error: " + std::string(errbuf)); + return Http::ErrorString; + } + if (status) { - *status = res->status; + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, status); } - return res->body; + } else { - beammp_debug("POST failed: " + httplib::to_string(res.error())); + beammp_error("Curl easy init failed"); return Http::ErrorString; } + return Ret; } // RFC 2616, RFC 7231 diff --git a/src/TNetwork.cpp b/src/TNetwork.cpp index 51f4cd06..7f02d6d4 100644 --- a/src/TNetwork.cpp +++ b/src/TNetwork.cpp @@ -346,7 +346,7 @@ std::shared_ptr TNetwork::Authentication(TConnection&& RawConnection) { auto Target = "/pkToUser"; unsigned int ResponseCode = 0; - AuthResStr = Http::POST(Application::GetBackendUrlForAuth(), 443, Target, AuthReq.dump(), "application/json", &ResponseCode); + AuthResStr = Http::POST(Application::GetBackendUrlForAuth() + Target, AuthReq.dump(), "application/json", &ResponseCode); } catch (const std::exception& e) { beammp_debugf("Invalid json sent by client, kicking: {}", e.what()); From 7c864d94b38a9f05bcc58aeb470d3190207d0d2b Mon Sep 17 00:00:00 2001 From: Tixx <83774803+WiserTixx@users.noreply.github.com> Date: Sat, 11 Jan 2025 22:27:42 +0100 Subject: [PATCH 4/4] Fix heartbeat request --- src/THeartbeatThread.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/THeartbeatThread.cpp b/src/THeartbeatThread.cpp index ac0d7453..34e03460 100644 --- a/src/THeartbeatThread.cpp +++ b/src/THeartbeatThread.cpp @@ -66,7 +66,7 @@ void THeartbeatThread::operator()() { json::Document Doc; bool Ok = false; for (const auto& Url : Application::GetBackendUrlsInOrder()) { - T = Http::POST(Url, 443, Target, Body, "application/json", &ResponseCode, { { "api-v", "2" } }); + T = Http::POST(Url + Target, Body, "application/json", &ResponseCode, { { "api-v", "2" } }); if (!Application::Settings.getAsBool(Settings::Key::General_Private)) { beammp_debug("Backend response was: `" + T + "`");