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>
This commit is contained in:
awesome_milou
2021-12-05 18:24:55 +01:00
committed by GitHub
parent b33d50361c
commit 9d283738aa
11 changed files with 273 additions and 18 deletions

View File

@@ -40,6 +40,9 @@ public:
, ServerDesc("BeamMP Default Description")
, Resource("Resources")
, MapName("/levels/gridmap_v2/info.json")
, SSLKeyPath("./.ssl/HttpServer/key.pem")
, SSLCertPath("./.ssl/HttpServer/cert.pem")
, HTTPServerPort(8080)
, MaxPlayers(10)
, Private(true)
, MaxCars(1)
@@ -52,6 +55,8 @@ public:
std::string Resource;
std::string MapName;
std::string Key;
std::string SSLKeyPath;
std::string SSLCertPath;
int MaxPlayers;
bool Private;
int MaxCars;
@@ -61,6 +66,7 @@ public:
bool SendErrors;
bool SendErrorsMessageEnabled;
[[nodiscard]] bool HasCustomIP() const { return !CustomIP.empty(); }
int HTTPServerPort;
};
using TShutdownHandler = std::function<void()>;
@@ -162,6 +168,12 @@ void RegisterThread(const std::string& str);
Application::Console().Write(_this_location + std::string("[DEBUG] ") + (x)); \
} \
} while (false)
#define beammp_event(x) \
do { \
if (Application::Settings.DebugModeEnabled) { \
Application::Console().Write(_this_location + std::string("[EVENT] ") + (x)); \
} \
}while(false)
// for those times when you just need to ignore something :^)
// explicity disables a [[nodiscard]] warning
#define beammp_ignore(x) (void)x

View File

@@ -1,8 +1,20 @@
#pragma once
#include <Common.h>
#include <IThreaded.h>
#include <filesystem>
#include <httplib.h>
#include <openssl/pem.h>
#include <openssl/x509.h>
#include <string>
#include <unordered_map>
namespace fs = std::filesystem;
namespace Crypto {
constexpr size_t RSA_DEFAULT_KEYLENGTH { 2048 };
}
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);
@@ -10,4 +22,36 @@ namespace Status {
std::string ToString(int code);
}
const std::string ErrorString = "-1";
namespace Server {
void SetupEnvironment();
// todo: Add non TLS Server Instance, this one is TLS only
class THttpServerInstance : IThreaded {
public:
THttpServerInstance();
static fs::path KeyFilePath;
static fs::path CertFilePath;
protected:
void operator()();
private:
/**
* the shared pointer is necessary because httplib is a blocking library and due lacking thread-safety
* will "forget" about its environment, when configured across multiple threads.
* So we need to able to start the server (make it "listen()") in a single Thread.
*/
std::shared_ptr<httplib::SSLServer> mHttpLibServerInstancePtr;
};
// todo: all of these functions are likely unsafe,
// todo: replace with something that's managed by a domain specific crypto library
class Tx509KeypairGenerator {
public:
static long GenerateRandomId();
static bool EnsureTLSConfigExists();
static X509* GenerateCertificate(EVP_PKEY& pkey);
static EVP_PKEY* GenerateKey();
static void GenerateAndWriteToDisk(const fs::path& KeyFilePath, const fs::path& CertFilePath);
};
}
}

View File

@@ -20,7 +20,8 @@ private:
void PrintDebug();
void ParseOldFormat();
bool IsDefault();
bool mFailed { false };
std::string mConfigFileName;
};

View File

@@ -20,6 +20,9 @@
using TLuaStateId = std::string;
namespace fs = std::filesystem;
/**
* std::variant means, that TLuaArgTypes may be one of the Types listed as template args
*/
using TLuaArgTypes = std::variant<std::string, int, sol::variadic_args, bool>;
static constexpr size_t TLuaArgTypes_String = 0;
static constexpr size_t TLuaArgTypes_Int = 1;
@@ -114,11 +117,21 @@ public:
void EnsureStateExists(TLuaStateId StateId, const std::string& Name, bool DontCallOnInit = false);
void RegisterEvent(const std::string& EventName, TLuaStateId StateId, const std::string& FunctionName);
template <typename... ArgsT>
/**
*
* @tparam ArgsT Template Arguments for the event (Metadata) todo: figure out what this means
* @param EventName Name of the event
* @param IgnoreId
* @param Args
* @return
*/
[[nodiscard]] std::vector<std::shared_ptr<TLuaResult>> TriggerEvent(const std::string& EventName, TLuaStateId IgnoreId, ArgsT&&... Args) {
std::unique_lock Lock(mLuaEventsMutex);
if (mLuaEvents.find(EventName) == mLuaEvents.end()) {
beammp_event(EventName);
if (mLuaEvents.find(EventName) == mLuaEvents.end()) { // if no event handler is defined for 'EventName', return immediately
return {};
}
std::vector<std::shared_ptr<TLuaResult>> Results;
for (const auto& Event : mLuaEvents.at(EventName)) {
for (const auto& Function : Event.second) {
@@ -127,7 +140,7 @@ public:
}
}
}
return Results;
return Results; //
}
std::set<std::string> GetEventHandlersForState(const std::string& EventName, TLuaStateId StateId);
void CreateEventTimer(const std::string& EventName, TLuaStateId StateId, size_t IntervalMS);