mirror of
https://github.com/BeamMP/BeamMP-Server.git
synced 2025-07-01 15:26:59 +00:00
Switch to curl (#405)
This PR converts all the http client code to use curl instead of httplib. This will hopefully do something about the connectivity issues with the backend and auth. --- By creating this pull request, I understand that code that is AI generated or otherwise automatically generated may be rejected without further discussion. I declare that I fully understand all code I pushed into this PR, and wrote all this code myself and own the rights to this code.
This commit is contained in:
commit
cd29f25435
@ -104,6 +104,7 @@ set(PRJ_LIBRARIES
|
|||||||
httplib::httplib
|
httplib::httplib
|
||||||
libzip::zip
|
libzip::zip
|
||||||
OpenSSL::SSL OpenSSL::Crypto
|
OpenSSL::SSL OpenSSL::Crypto
|
||||||
|
CURL::libcurl
|
||||||
${LUA_LIBRARIES}
|
${LUA_LIBRARIES}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -116,6 +117,7 @@ find_package(httplib CONFIG REQUIRED)
|
|||||||
find_package(libzip CONFIG REQUIRED)
|
find_package(libzip CONFIG REQUIRED)
|
||||||
find_package(RapidJSON CONFIG REQUIRED)
|
find_package(RapidJSON CONFIG REQUIRED)
|
||||||
find_package(sol2 CONFIG REQUIRED)
|
find_package(sol2 CONFIG REQUIRED)
|
||||||
|
find_package(CURL CONFIG REQUIRED)
|
||||||
add_subdirectory("deps/toml11")
|
add_subdirectory("deps/toml11")
|
||||||
|
|
||||||
include_directories(include)
|
include_directories(include)
|
||||||
|
@ -82,11 +82,11 @@ public:
|
|||||||
|
|
||||||
static std::vector<std::string> GetBackendUrlsInOrder() {
|
static std::vector<std::string> GetBackendUrlsInOrder() {
|
||||||
return {
|
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 std::string GetBackendUrlForSocketIO() { return "https://backend.beammp.com"; }
|
||||||
static void CheckForUpdates();
|
static void CheckForUpdates();
|
||||||
static std::array<uint8_t, 3> VersionStrToInts(const std::string& str);
|
static std::array<uint8_t, 3> VersionStrToInts(const std::string& str);
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <curl/curl.h>
|
||||||
|
|
||||||
#if defined(BEAMMP_LINUX)
|
#if defined(BEAMMP_LINUX)
|
||||||
#pragma GCC diagnostic push
|
#pragma GCC diagnostic push
|
||||||
@ -38,8 +39,8 @@
|
|||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
namespace Http {
|
namespace Http {
|
||||||
std::string GET(const std::string& host, int port, const std::string& target, unsigned int* status = nullptr);
|
std::string GET(const std::string& url, 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 POST(const std::string& url, const std::string& body, const std::string& ContentType, unsigned int* status = nullptr, const std::map<std::string, std::string>& headers = {});
|
||||||
namespace Status {
|
namespace Status {
|
||||||
std::string ToString(int code);
|
std::string ToString(int code);
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,9 @@
|
|||||||
#include <limits>
|
#include <limits>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#undef max
|
||||||
|
#undef min
|
||||||
|
|
||||||
namespace prof {
|
namespace prof {
|
||||||
|
|
||||||
using Duration = std::chrono::duration<double, std::milli>;
|
using Duration = std::chrono::duration<double, std::milli>;
|
||||||
|
@ -215,7 +215,7 @@ void Application::CheckForUpdates() {
|
|||||||
// checks current version against latest version
|
// checks current version against latest version
|
||||||
std::regex VersionRegex { R"(\d+\.\d+\.\d+\n*)" };
|
std::regex VersionRegex { R"(\d+\.\d+\.\d+\n*)" };
|
||||||
for (const auto& url : GetBackendUrlsInOrder()) {
|
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);
|
bool Matches = std::regex_match(Response, VersionRegex);
|
||||||
if (Matches) {
|
if (Matches) {
|
||||||
auto MyVersion = ServerVersion();
|
auto MyVersion = ServerVersion();
|
||||||
|
115
src/Http.cpp
115
src/Http.cpp
@ -29,67 +29,86 @@
|
|||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
using json = nlohmann::json;
|
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<Connection, CONNECTION_AMOUNT> connections;
|
|
||||||
static thread_local std::array<std::shared_ptr<httplib::SSLClient>, CONNECTION_AMOUNT> clients;
|
|
||||||
|
|
||||||
[[nodiscard]] static std::shared_ptr<httplib::SSLClient> getClient(Connection connectionInfo) {
|
static size_t CurlWriteCallback(void* contents, size_t size, size_t nmemb, void* userp) {
|
||||||
for (uint8_t i = 0; i < CONNECTION_AMOUNT; i++) {
|
std::string* Result = reinterpret_cast<std::string*>(userp);
|
||||||
if (connectionInfo.host == connections[i].host
|
std::string NewContents(reinterpret_cast<char*>(contents), size * nmemb);
|
||||||
&& connectionInfo.port == connections[i].port) {
|
*Result += NewContents;
|
||||||
beammp_tracef("Old client reconnected, with ip {} and port {}", connectionInfo.host, connectionInfo.port);
|
return size * nmemb;
|
||||||
return clients[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
uint8_t i = write_index;
|
|
||||||
write_index++;
|
|
||||||
write_index %= CONNECTION_AMOUNT;
|
|
||||||
clients[i] = std::make_shared<httplib::SSLClient>(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];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Http::GET(const std::string& host, int port, const std::string& target, unsigned int* status) {
|
std::string Http::GET(const std::string& url, unsigned int* status) {
|
||||||
std::shared_ptr<httplib::SSLClient> client = getClient({ host, port });
|
std::string Ret;
|
||||||
client->enable_server_certificate_verification(false);
|
static thread_local CURL* curl = curl_easy_init();
|
||||||
client->set_address_family(AF_INET);
|
if (curl) {
|
||||||
auto res = client->Get(target.c_str());
|
CURLcode res;
|
||||||
if (res) {
|
char errbuf[CURL_ERROR_SIZE];
|
||||||
if (status) {
|
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
||||||
*status = res->status;
|
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;
|
||||||
}
|
}
|
||||||
return res->body;
|
|
||||||
|
if (status) {
|
||||||
|
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, status);
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
beammp_error("Curl easy init failed");
|
||||||
return Http::ErrorString;
|
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::string Http::POST(const std::string& url, const std::string& body, const std::string& ContentType, unsigned int* status, const std::map<std::string, std::string>& headers) {
|
||||||
std::shared_ptr<httplib::SSLClient> client = getClient({ host, port });
|
std::string Ret;
|
||||||
client->set_read_timeout(std::chrono::seconds(10));
|
static thread_local CURL* curl = curl_easy_init();
|
||||||
beammp_assert(client->is_valid());
|
if (curl) {
|
||||||
client->enable_server_certificate_verification(false);
|
CURLcode res;
|
||||||
client->set_address_family(AF_INET);
|
char errbuf[CURL_ERROR_SIZE];
|
||||||
auto res = client->Post(target.c_str(), headers, body.c_str(), body.size(), ContentType.c_str());
|
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
||||||
if (res) {
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteCallback);
|
||||||
if (status) {
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&Ret);
|
||||||
*status = res->status;
|
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());
|
||||||
}
|
}
|
||||||
return res->body;
|
|
||||||
|
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) {
|
||||||
|
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, status);
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
beammp_debug("POST failed: " + httplib::to_string(res.error()));
|
beammp_error("Curl easy init failed");
|
||||||
return Http::ErrorString;
|
return Http::ErrorString;
|
||||||
}
|
}
|
||||||
|
return Ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
// RFC 2616, RFC 7231
|
// RFC 2616, RFC 7231
|
||||||
|
@ -66,7 +66,7 @@ void THeartbeatThread::operator()() {
|
|||||||
json::Document Doc;
|
json::Document Doc;
|
||||||
bool Ok = false;
|
bool Ok = false;
|
||||||
for (const auto& Url : Application::GetBackendUrlsInOrder()) {
|
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)) {
|
if (!Application::Settings.getAsBool(Settings::Key::General_Private)) {
|
||||||
beammp_debug("Backend response was: `" + T + "`");
|
beammp_debug("Backend response was: `" + T + "`");
|
||||||
|
@ -346,7 +346,7 @@ std::shared_ptr<TClient> TNetwork::Authentication(TConnection&& RawConnection) {
|
|||||||
auto Target = "/pkToUser";
|
auto Target = "/pkToUser";
|
||||||
|
|
||||||
unsigned int ResponseCode = 0;
|
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) {
|
} catch (const std::exception& e) {
|
||||||
beammp_debugf("Invalid json sent by client, kicking: {}", e.what());
|
beammp_debugf("Invalid json sent by client, kicking: {}", e.what());
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
"nlohmann-json",
|
"nlohmann-json",
|
||||||
"openssl",
|
"openssl",
|
||||||
"rapidjson",
|
"rapidjson",
|
||||||
"sol2"
|
"sol2",
|
||||||
|
"curl"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user