Skip to content

Commit

Permalink
Switch to curl (#405)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
WiserTixx authored Jan 12, 2025
2 parents 7dd6b41 + 7c864d9 commit cd29f25
Show file tree
Hide file tree
Showing 9 changed files with 81 additions and 55 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ set(PRJ_LIBRARIES
httplib::httplib
libzip::zip
OpenSSL::SSL OpenSSL::Crypto
CURL::libcurl
${LUA_LIBRARIES}
)

Expand All @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions include/Common.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,11 @@ class Application final {

static std::vector<std::string> 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<uint8_t, 3> VersionStrToInts(const std::string& str);
Expand Down
5 changes: 3 additions & 2 deletions include/Http.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <filesystem>
#include <string>
#include <unordered_map>
#include <curl/curl.h>

#if defined(BEAMMP_LINUX)
#pragma GCC diagnostic push
Expand All @@ -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<std::string, std::string>& headers = {});
namespace Status {
std::string ToString(int code);
}
Expand Down
3 changes: 3 additions & 0 deletions include/Profiling.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
#include <limits>
#include <unordered_map>

#undef max
#undef min

namespace prof {

using Duration = std::chrono::duration<double, std::milli>;
Expand Down
2 changes: 1 addition & 1 deletion src/Common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
113 changes: 66 additions & 47 deletions src/Http.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,67 +29,86 @@
#include <stdexcept>

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) {
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<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];

static size_t CurlWriteCallback(void* contents, size_t size, size_t nmemb, void* userp) {
std::string* Result = reinterpret_cast<std::string*>(userp);
std::string NewContents(reinterpret_cast<char*>(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<httplib::SSLClient> 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<httplib::SSLClient> 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<std::string, std::string>& 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
Expand Down
2 changes: 1 addition & 1 deletion src/THeartbeatThread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 + "`");
Expand Down
2 changes: 1 addition & 1 deletion src/TNetwork.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ std::shared_ptr<TClient> 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());
Expand Down
3 changes: 2 additions & 1 deletion vcpkg.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"nlohmann-json",
"openssl",
"rapidjson",
"sol2"
"sol2",
"curl"
]
}

0 comments on commit cd29f25

Please sign in to comment.