BeamMP-Server/src/THeartbeatThread.cpp
awesome_milou 9d283738aa
Add preliminary work for HTTP health endpoint (#68)
* Add preliminary work for HTTP health endpoint

* Http: Fix infinite loop bug in Tx509KeypairGenerator::generateKey()

* update commandline

* Add TLS Support class for use with http server

* Add preliminary HTTP Server; TLS still broken; fix in later commit

* Fix TLS handshake, due to server being unable to serve key/certfile in 'Http.h/Http.cpp'; Cause was httlib not being threadsafe due to being a blocking http library

* Run clang format

* Add option to configure http server port via ServerConfig

* TConfig: add HTTPServerPort to config parsing step

* Fix SSL Cert / Key path not auto generating when not existing

* Add health endpoint; Fix SSL Cert serial no. not refreshing when regenerating

* Switch arround status codes in /health route

* Run clang format

Co-authored-by: Lion Kortlepel <development@kortlepel.com>
2021-12-05 18:24:55 +01:00

132 lines
5.3 KiB
C++

#include "THeartbeatThread.h"
#include "Client.h"
#include "Http.h"
//#include "SocketIO.h"
#include <sstream>
void THeartbeatThread::operator()() {
return;/*
RegisterThread("Heartbeat");
std::string Body;
std::string T;
// these are "hot-change" related variables
static std::string Last;
static std::chrono::high_resolution_clock::time_point LastNormalUpdateTime = std::chrono::high_resolution_clock::now();
bool isAuth = false;
while (!mShutdown) {
Body = GenerateCall();
// a hot-change occurs when a setting has changed, to update the backend of that change.
auto Now = std::chrono::high_resolution_clock::now();
bool Unchanged = Last == Body;
auto TimePassed = (Now - LastNormalUpdateTime);
auto Threshold = Unchanged ? 30 : 5;
if (TimePassed < std::chrono::seconds(Threshold)) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
continue;
}
beammp_debug("heartbeat (after " + std::to_string(std::chrono::duration_cast<std::chrono::seconds>(TimePassed).count()) + "s)");
Last = Body;
LastNormalUpdateTime = Now;
if (!Application::Settings.CustomIP.empty()) {
Body += "&ip=" + Application::Settings.CustomIP;
}
Body += "&pps=" + Application::PPS();
beammp_trace("heartbeat body: '" + Body + "'");
auto SentryReportError = [&](const std::string& transaction, int status) {
auto Lock = Sentry.CreateExclusiveContext();
Sentry.SetContext("heartbeat",
{ { "response-body", T },
{ "request-body", Body } });
Sentry.SetTransaction(transaction);
beammp_trace("sending log to sentry: " + std::to_string(status) + " for " + transaction);
Sentry.Log(SentryLevel::Error, "default", Http::Status::ToString(status) + " (" + std::to_string(status) + ")");
};
auto Target = "/heartbeat";
unsigned int ResponseCode = 0;
T = Http::POST(Application::GetBackendHostname(), 443, Target, Body, "application/x-www-form-urlencoded", &ResponseCode);
if ((T.substr(0, 2) != "20" && ResponseCode != 200) || ResponseCode != 200) {
beammp_trace("got " + T + " from backend");
SentryReportError(Application::GetBackendHostname() + Target, ResponseCode);
std::this_thread::sleep_for(std::chrono::milliseconds(500));
T = Http::POST(Application::GetBackup1Hostname(), 443, Target, Body, "application/x-www-form-urlencoded", &ResponseCode);
if ((T.substr(0, 2) != "20" && ResponseCode != 200) || ResponseCode != 200) {
SentryReportError(Application::GetBackup1Hostname() + Target, ResponseCode);
std::this_thread::sleep_for(std::chrono::milliseconds(500));
T = Http::POST(Application::GetBackup2Hostname(), 443, Target, Body, "application/x-www-form-urlencoded", &ResponseCode);
if ((T.substr(0, 2) != "20" && ResponseCode != 200) || ResponseCode != 200) {
beammp_warn("Backend system refused server! Server will not show in the public server list.");
isAuth = false;
SentryReportError(Application::GetBackup2Hostname() + Target, ResponseCode);
}
}
}
if (!isAuth) {
if (T == "2000") {
beammp_info(("Authenticated!"));
isAuth = true;
} else if (T == "200") {
beammp_info(("Resumed authenticated session!"));
isAuth = true;
}
}
//SocketIO::Get().SetAuthenticated(isAuth);
}
*/
}
std::string THeartbeatThread::GenerateCall() {
std::stringstream Ret;
Ret << "uuid=" << Application::Settings.Key
<< "&players=" << mServer.ClientCount()
<< "&maxplayers=" << Application::Settings.MaxPlayers
<< "&port=" << Application::Settings.Port
<< "&map=" << Application::Settings.MapName
<< "&private=" << (Application::Settings.Private ? "true" : "false")
<< "&version=" << Application::ServerVersionString()
<< "&clientversion=" << Application::ClientVersionString()
<< "&name=" << Application::Settings.ServerName
<< "&modlist=" << mResourceManager.TrimmedList()
<< "&modstotalsize=" << mResourceManager.MaxModSize()
<< "&modstotal=" << mResourceManager.ModsLoaded()
<< "&playerslist=" << GetPlayers()
<< "&desc=" << Application::Settings.ServerDesc;
return Ret.str();
}
THeartbeatThread::THeartbeatThread(TResourceManager& ResourceManager, TServer& Server)
: mResourceManager(ResourceManager)
, mServer(Server) {
Application::RegisterShutdownHandler([&] {
if (mThread.joinable()) {
mShutdown = true;
mThread.join();
}
});
Start();
}
std::string THeartbeatThread::GetPlayers() {
std::string Return;
mServer.ForEachClient([&](const std::weak_ptr<TClient>& ClientPtr) -> bool {
ReadLock Lock(mServer.GetClientMutex());
if (!ClientPtr.expired()) {
Return += ClientPtr.lock()->GetName() + ";";
}
return true;
});
return Return;
}
/*THeartbeatThread::~THeartbeatThread() {
}*/