Multiple merge fixes, rebase, working Https::GET

This commit is contained in:
Lion Kortlepel 2021-08-17 14:33:03 +02:00
parent 5742ab0dad
commit 6462636b29
No known key found for this signature in database
GPG Key ID: 4322FF2B4C71259B
7 changed files with 50 additions and 128 deletions

View File

@ -21,6 +21,7 @@ struct Version {
uint8_t minor; uint8_t minor;
uint8_t patch; uint8_t patch;
Version(uint8_t major, uint8_t minor, uint8_t patch); Version(uint8_t major, uint8_t minor, uint8_t patch);
Version(const std::array<uint8_t, 3>& v);
std::string AsString(); std::string AsString();
}; };
@ -81,8 +82,8 @@ public:
static std::string GetBackup2Hostname() { return "backup2.beammp.com"; } static std::string GetBackup2Hostname() { return "backup2.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<int, 3> VersionStrToInts(const std::string& str); static std::array<uint8_t, 3> VersionStrToInts(const std::string& str);
static bool IsOutdated(const std::array<int, 3>& Current, const std::array<int, 3>& Newest); static bool IsOutdated(const Version& Current, const Version& Newest);
private: private:
static inline std::string mPPS; static inline std::string mPPS;
@ -163,4 +164,4 @@ void LogChatMessage(const std::string& name, int id, const std::string& msg);
std::string Comp(std::string Data); std::string Comp(std::string Data);
std::string DeComp(std::string Compressed); std::string DeComp(std::string Compressed);
std::string GetPlatformAgnosticErrorString(); std::string GetPlatformAgnosticErrorString();

View File

@ -5,8 +5,9 @@
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& host, int port, const std::string& target, unsigned int* status = nullptr);
std::string POST(const std::string& host, const std::string& target, const std::unordered_map<std::string, std::string>& fields, const std::string& body, bool json, int* status = nullptr); std::string POST(const std::string& host, int port, const std::string& target, const std::unordered_map<std::string, std::string>& fields, const std::string& body, const std::string& ContentType, unsigned int* status = nullptr);
namespace Status { namespace Status {
std::string ToString(int code); std::string ToString(int code);
} }
} const std::string ErrorString = "-1";
}

View File

@ -30,10 +30,14 @@ void Application::GracefullyShutdown() {
} }
} }
std::array<int, 3> Application::VersionStrToInts(const std::string& str) { std::string Application::ServerVersionString() {
std::array<int, 3> Version; return mVersion.AsString();
}
std::array<uint8_t, 3> Application::VersionStrToInts(const std::string& str) {
std::array<uint8_t, 3> Version;
std::stringstream ss(str); std::stringstream ss(str);
for (int& i : Version) { for (uint8_t& i : Version) {
std::string Part; std::string Part;
std::getline(ss, Part, '.'); std::getline(ss, Part, '.');
std::from_chars(&*Part.begin(), &*Part.begin() + Part.size(), i); std::from_chars(&*Part.begin(), &*Part.begin() + Part.size(), i);
@ -41,12 +45,13 @@ std::array<int, 3> Application::VersionStrToInts(const std::string& str) {
return Version; return Version;
} }
bool Application::IsOutdated(const std::array<int, 3>& Current, const std::array<int, 3>& Newest) { // FIXME: This should be used by operator< on Version
if (Newest[0] > Current[0]) { bool Application::IsOutdated(const Version& Current, const Version& Newest) {
if (Newest.major > Current.major) {
return true; return true;
} else if (Newest[0] == Current[0] && Newest[1] > Current[1]) { } else if (Newest.major == Current.major && Newest.minor > Current.minor) {
return true; return true;
} else if (Newest[0] == Current[0] && Newest[1] == Current[1] && Newest[2] > Current[2]) { } else if (Newest.major == Current.major && Newest.minor == Current.minor && Newest.patch > Current.patch) {
return true; return true;
} else { } else {
return false; return false;
@ -59,12 +64,10 @@ void Application::CheckForUpdates() {
auto Response = Http::GET(GetBackendHostname(), 443, "/v/s"); auto Response = Http::GET(GetBackendHostname(), 443, "/v/s");
bool Matches = std::regex_match(Response, VersionRegex); bool Matches = std::regex_match(Response, VersionRegex);
if (Matches) { if (Matches) {
auto MyVersion = VersionStrToInts(ServerVersion()); auto MyVersion = ServerVersion();
auto RemoteVersion = VersionStrToInts(Response); auto RemoteVersion = Version(VersionStrToInts(Response));
if (IsOutdated(MyVersion, RemoteVersion)) { if (IsOutdated(MyVersion, RemoteVersion)) {
std::string RealVersionString = std::to_string(RemoteVersion[0]) + "."; std::string RealVersionString = RemoteVersion.AsString();
RealVersionString += std::to_string(RemoteVersion[1]) + ".";
RealVersionString += std::to_string(RemoteVersion[2]);
warn(std::string(ANSI_YELLOW_BOLD) + "NEW VERSION OUT! There's a new version (v" + RealVersionString + ") of the BeamMP-Server available! For info on how to update your server, visit https://wiki.beammp.com/en/home/server-maintenance#updating-the-server." + std::string(ANSI_RESET)); warn(std::string(ANSI_YELLOW_BOLD) + "NEW VERSION OUT! There's a new version (v" + RealVersionString + ") of the BeamMP-Server available! For info on how to update your server, visit https://wiki.beammp.com/en/home/server-maintenance#updating-the-server." + std::string(ANSI_RESET));
} else { } else {
info("Server up-to-date!"); info("Server up-to-date!");
@ -149,10 +152,16 @@ Version::Version(uint8_t major, uint8_t minor, uint8_t patch)
, minor(minor) , minor(minor)
, patch(patch) { } , patch(patch) { }
Version::Version(const std::array<uint8_t, 3>& v)
: Version(v[0], v[1], v[2]) {
}
std::string Version::AsString() { std::string Version::AsString() {
std::stringstream ss {}; std::stringstream ss {};
ss << int(major) << "." << int(minor) << "." << int(patch); ss << int(major) << "." << int(minor) << "." << int(patch);
return ss.str(); return ss.str();
}
void LogChatMessage(const std::string& name, int id, const std::string& msg) { void LogChatMessage(const std::string& name, int id, const std::string& msg) {
std::stringstream ss; std::stringstream ss;
ss << "[CHAT] "; ss << "[CHAT] ";
@ -162,7 +171,6 @@ void LogChatMessage(const std::string& name, int id, const std::string& msg) {
ss << name << ""; ss << name << "";
} }
ss << msg; ss << msg;
} }
std::string GetPlatformAgnosticErrorString() { std::string GetPlatformAgnosticErrorString() {
@ -190,4 +198,4 @@ std::string GetPlatformAgnosticErrorString() {
#else // posix #else // posix
return std::strerror(errno); return std::strerror(errno);
#endif #endif
} }

View File

@ -15,94 +15,7 @@ namespace net = boost::asio; // from <boost/asio.hpp>
namespace ssl = net::ssl; // from <boost/asio/ssl.hpp> namespace ssl = net::ssl; // from <boost/asio/ssl.hpp>
using tcp = net::ip::tcp; // from <boost/asio/ip/tcp.hpp> using tcp = net::ip::tcp; // from <boost/asio/ip/tcp.hpp>
std::string Http::GET(const std::string& host, int port, const std::string& target, unsigned int* status) { std::string GenericRequest(http::verb verb, const std::string& host, int port, const std::string& target, const std::unordered_map<std::string, std::string>& fields, const std::string& body, const std::string& ContentType, unsigned int* status) {
try {
// Check command line arguments.
int version = 11;
// The io_context is required for all I/O
net::io_context ioc;
// The SSL context is required, and holds certificates
ssl::context ctx(ssl::context::tlsv12_client);
// This holds the root certificate used for verification
// we don't do / have this
// load_root_certificates(ctx);
// Verify the remote server's certificate
ctx.set_verify_mode(ssl::verify_none);
// These objects perform our I/O
tcp::resolver resolver(ioc);
beast::ssl_stream<beast::tcp_stream> stream(ioc, ctx);
// Set SNI Hostname (many hosts need this to handshake successfully)
if (!SSL_set_tlsext_host_name(stream.native_handle(), host.c_str())) {
beast::error_code ec { static_cast<int>(::ERR_get_error()), net::error::get_ssl_category() };
throw beast::system_error { ec };
}
// Look up the domain name
auto const results = resolver.resolve(host.c_str(), std::to_string(port));
// Make the connection on the IP address we get from a lookup
beast::get_lowest_layer(stream).connect(results);
// Perform the SSL handshake
stream.handshake(ssl::stream_base::client);
// Set up an HTTP GET request message
http::request<http::string_body> req { http::verb::get, target, version };
req.set(http::field::host, host);
req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
// Send the HTTP request to the remote host
http::write(stream, req);
// This buffer is used for reading and must be persisted
beast::flat_buffer buffer;
// Declare a container to hold the response
http::response<http::string_body> res;
// Receive the HTTP response
http::read(stream, buffer, res);
// Gracefully close the stream
beast::error_code ec;
stream.shutdown(ec);
if (ec == net::error::eof) {
// Rationale:
// http://stackoverflow.com/questions/25587403/boost-asio-ssl-async-shutdown-always-finishes-with-an-error
ec = {};
}
if (status) {
*status = res.base().result_int();
}
if (ec)
throw beast::system_error { ec };
// If we get here then the connection is closed gracefully
return std::string(res.body());
} catch (std::exception const& e) {
Application::Console().Write(__func__ + std::string(": ") + e.what());
return ErrorString;
}
}
std::string Http::POST(const std::string& host, int port, const std::string& target, const std::unordered_map<std::string, std::string>& fields, const std::string& body, const std::string& ContentType, unsigned int* status) {
try { try {
net::io_context io; net::io_context io;
@ -140,14 +53,13 @@ std::string Http::POST(const std::string& host, int port, const std::string& tar
} }
//} //}
stream.handshake(ssl::stream_base::client); stream.handshake(ssl::stream_base::client);
http::request<http::string_body> req { http::verb::post, target, 11 /* http 1.1 */ }; http::request<http::string_body> req { verb, target, 11 /* http 1.1 */ };
req.set(http::field::host, host); req.set(http::field::host, host);
if (!body.empty()) { if (!body.empty()) {
req.set(http::field::content_type, ContentType); // "application/json" req.set(http::field::content_type, ContentType); // "application/json"
// "application/x-www-form-urlencoded" // "application/x-www-form-urlencoded"
req.set(http::field::content_length, std::to_string(body.size())); req.set(http::field::content_length, std::to_string(body.size()));
req.body() = body; req.body() = body;
// info("body is " + body + " (" + req.body() + ")"); // info("body is " + body + " (" + req.body() + ")");
@ -215,10 +127,18 @@ std::string Http::POST(const std::string& host, int port, const std::string& tar
} catch (const std::exception& e) { } catch (const std::exception& e) {
Application::Console().Write(__func__ + std::string(": ") + e.what()); Application::Console().Write(__func__ + std::string(": ") + e.what());
return ErrorString; return Http::ErrorString;
} }
} }
std::string Http::GET(const std::string& host, int port, const std::string& target, unsigned int* status) {
return GenericRequest(http::verb::get, host, port, target, {}, {}, {}, status);
}
std::string Http::POST(const std::string& host, int port, const std::string& target, const std::unordered_map<std::string, std::string>& fields, const std::string& body, const std::string& ContentType, unsigned int* status) {
return GenericRequest(http::verb::post, host, port, target, fields, body, ContentType, status);
}
// RFC 2616, RFC 7231 // RFC 2616, RFC 7231
static std::map<size_t, const char*> Map = { static std::map<size_t, const char*> Map = {
{ 100, "Continue" }, { 100, "Continue" },
@ -291,4 +211,4 @@ std::string Http::Status::ToString(int code) {
} else { } else {
return "Unassigned"; return "Unassigned";
} }
} }

View File

@ -49,20 +49,19 @@ void THeartbeatThread::operator()() {
Sentry.Log(SentryLevel::Error, "default", Http::Status::ToString(status) + " (" + std::to_string(status) + ")"); Sentry.Log(SentryLevel::Error, "default", Http::Status::ToString(status) + " (" + std::to_string(status) + ")");
}; };
auto Target = "/heartbeat"; auto Target = "/heartbeat";
int ResponseCode = -1; unsigned int ResponseCode = 0;
T = Http::POST(Application::GetBackendHostname(), Target, {}, Body, false, &ResponseCode); T = Http::POST(Application::GetBackendHostname(), 443, Target, {}, Body, "application/x-www-form-urlencoded", &ResponseCode);
if (T.substr(0, 2) != "20" || ResponseCode != 200) { if (T.substr(0, 2) != "20" || ResponseCode != 200) {
trace("got " + T + " from backend"); trace("got " + T + " from backend");
SentryReportError(Application::GetBackendHostname() + Target, ResponseCode); SentryReportError(Application::GetBackendHostname() + Target, ResponseCode);
std::this_thread::sleep_for(std::chrono::milliseconds(500)); std::this_thread::sleep_for(std::chrono::milliseconds(500));
T = Http::POST(Application::GetBackup1Hostname(), Target, {}, Body, false, &ResponseCode); T = Http::POST(Application::GetBackup1Hostname(), 443, Target, {}, Body, "application/x-www-form-urlencoded", &ResponseCode);
if (T.substr(0, 2) != "20" || ResponseCode != 200) { if (T.substr(0, 2) != "20" || ResponseCode != 200) {
SentryReportError(Application::GetBackup1Hostname() + Target, ResponseCode); SentryReportError(Application::GetBackup1Hostname() + Target, ResponseCode);
std::this_thread::sleep_for(std::chrono::milliseconds(500)); std::this_thread::sleep_for(std::chrono::milliseconds(500));
T = Http::POST(Application::GetBackup2Hostname(), Target, {}, Body, false, &ResponseCode); T = Http::POST(Application::GetBackup2Hostname(), 443, Target, {}, Body, "application/x-www-form-urlencoded", &ResponseCode);
if (T.substr(0, 2) != "20" || ResponseCode != 200) { if (T.substr(0, 2) != "20" || ResponseCode != 200) {
warn("Backend system refused server! Server will not show in the public server list."); warn("Backend system refused server! Server will not show in the public server list.");
@ -130,4 +129,4 @@ std::string THeartbeatThread::GetPlayers() {
return Return; return Return;
} }
/*THeartbeatThread::~THeartbeatThread() { /*THeartbeatThread::~THeartbeatThread() {
}*/ }*/

View File

@ -72,17 +72,12 @@ void TLuaEngine::FolderList(const std::string& Path, bool HotSwap) {
for (const auto& entry : fs::directory_iterator(Path)) { for (const auto& entry : fs::directory_iterator(Path)) {
if (fs::is_directory(entry)) { if (fs::is_directory(entry)) {
RegisterFiles(entry.path(), HotSwap); RegisterFiles(entry.path(), HotSwap);
} }
} }
} }
void TLuaEngine::RegisterFiles(const fs::path& Path, bool HotSwap) { void TLuaEngine::RegisterFiles(const fs::path& Path, bool HotSwap) {
std::string Name = Path.filename().string(); std::string Name = Path.filename().string();
std::string Name = Path.substr(Path.find_last_of('/') + 1);
#else
#endif
if (!HotSwap) if (!HotSwap)
info(("Loading plugin : ") + Name); info(("Loading plugin : ") + Name);
std::vector<fs::path> Entries; std::vector<fs::path> Entries;
@ -111,11 +106,10 @@ void TLuaEngine::RegisterFiles(const fs::path& Path, bool HotSwap) {
} }
} }
bool TLuaEngine::IsNewFile(const std::string& Path) { bool TLuaEngine::IsNewFile(const std::string& Path) {
for (auto& Script : mLuaFiles) { for (auto& Script : mLuaFiles) {
if (fs::absolute(Path) == fs::absolute(Script->GetFileName())) if (fs::absolute(Path) == fs::absolute(Script->GetFileName()))
return false; return false;
} }
return true; return true;
} }

View File

@ -241,9 +241,9 @@ void TNetwork::Authentication(const TConnection& ClientConnection) {
auto RequestString = R"({"key":")" + Rc + "\"}"; auto RequestString = R"({"key":")" + Rc + "\"}";
auto Target = "/pkToUser"; auto Target = "/pkToUser";
int ResponseCode = -1; unsigned int ResponseCode = 0;
if (!Rc.empty()) { if (!Rc.empty()) {
Rc = Http::POST(Application::GetBackendUrlForAuth(), Target, {}, RequestString, true, &ResponseCode); Rc = Http::POST(Application::GetBackendUrlForAuth(), 443, Target, {}, RequestString, "application/json", &ResponseCode);
} }
json::Document AuthResponse; json::Document AuthResponse;
@ -272,7 +272,6 @@ void TNetwork::Authentication(const TConnection& ClientConnection) {
Sentry.Log(SentryLevel::Error, "default", "unexpected backend response (" + std::to_string(ResponseCode) + ")"); Sentry.Log(SentryLevel::Error, "default", "unexpected backend response (" + std::to_string(ResponseCode) + ")");
} }
return; return;
} }
if (AuthResponse["username"].IsString() && AuthResponse["roles"].IsString() if (AuthResponse["username"].IsString() && AuthResponse["roles"].IsString()
@ -931,4 +930,4 @@ std::string TNetwork::UDPRcvFromClient(sockaddr_in& client) const {
return ""; return "";
} }
return std::string(Ret.begin(), Ret.begin() + Rcv); return std::string(Ret.begin(), Ret.begin() + Rcv);
} }