diff --git a/src/Http.cpp b/src/Http.cpp index cc91e04..467fd3c 100644 --- a/src/Http.cpp +++ b/src/Http.cpp @@ -36,10 +36,11 @@ std::string Http::GET(const std::string& host, int port, const std::string& targ 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) { httplib::SSLClient client(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(), body.c_str(), body.size(), ContentType.c_str()); + auto res = client.Post(target.c_str(), headers, body.c_str(), body.size(), ContentType.c_str()); if (res) { if (status) { *status = res->status; diff --git a/src/THeartbeatThread.cpp b/src/THeartbeatThread.cpp index 4311cf5..f5be47b 100644 --- a/src/THeartbeatThread.cpp +++ b/src/THeartbeatThread.cpp @@ -55,32 +55,70 @@ void THeartbeatThread::operator()() { auto Target = "/heartbeat"; unsigned int ResponseCode = 0; - bool Ok = true; json::Document Doc; - for (const auto& Hostname : Application::GetBackendUrlsInOrder()) { - T = Http::POST(Hostname, 443, Target, Body, "application/x-www-form-urlencoded", &ResponseCode, { { "api-v", "2" } }); - if ((T.substr(0, 2) != "20" && ResponseCode != 200) || ResponseCode != 200) { - beammp_trace("heartbeat to " + Hostname + " returned: " + T); - Application::SetSubsystemStatus("Heartbeat", Application::Status::Bad); - isAuth = false; - SentryReportError(Hostname + Target, ResponseCode); - Ok = false; + bool Ok = false; + for (const auto& Url : Application::GetBackendUrlsInOrder()) { + T = Http::POST(Url, 443, Target, Body, "application/x-www-form-urlencoded", &ResponseCode, { { "api-v", "2" } }); + beammp_trace(T); + Doc.Parse(T.data(), T.size()); + if (Doc.HasParseError() || !Doc.IsObject()) { + beammp_error("Backend response failed to parse as valid json"); + beammp_debug("Response was: `" + T + "`"); + Sentry.SetContext("JSON Response", { { "reponse", T } }); + SentryReportError(Url + Target, ResponseCode); + } else if (ResponseCode != 200) { + SentryReportError(Url + Target, ResponseCode); } else { - Application::SetSubsystemStatus("Heartbeat", Application::Status::Good); + // all ok Ok = true; break; } + std::this_thread::sleep_for(std::chrono::milliseconds(500)); } - if (!Ok) { - beammp_warn("Backend system refused server! Server will not show in the public server list."); + std::string Status {}; + std::string Code {}; + std::string Message {}; + const auto StatusKey = "status"; + const auto CodeKey = "code"; + const auto MessageKey = "msg"; + + if (Ok) { + if (Doc.HasMember(StatusKey) && Doc[StatusKey].IsString()) { + Status = Doc[StatusKey].GetString(); + } else { + Sentry.SetContext("JSON Response", { { StatusKey, "invalid string / missing" } }); + Ok = false; + } + if (Doc.HasMember(CodeKey) && Doc[CodeKey].IsString()) { + Code = Doc[CodeKey].GetString(); + } else { + Sentry.SetContext("JSON Response", { { CodeKey, "invalid string / missing" } }); + Ok = false; + } + if (Doc.HasMember(MessageKey) && Doc[MessageKey].IsString()) { + Message = Doc[MessageKey].GetString(); + } else { + Sentry.SetContext("JSON Response", { { MessageKey, "invalid string / missing" } }); + Ok = false; + } + if (!Ok) { + beammp_error("Missing/invalid json members in backend response"); + Sentry.LogError("Missing/invalid json members in backend response", __FILE__, std::to_string(__LINE__)); + } } - if (!isAuth) { - if (T == "2000") { + + if (Ok && !isAuth) { + if (Status == "2000") { beammp_info(("Authenticated!")); isAuth = true; - } else if (T == "200") { + } else if (Status == "200") { beammp_info(("Resumed authenticated session!")); isAuth = true; + } else { + if (Message.empty()) { + Message = "Backend didn't provide a reason"; + } + beammp_error("Backend REFUSED the auth key. " + Message); } } }