diff --git a/lib/remote/actionshandler.cpp b/lib/remote/actionshandler.cpp index 442c2ccae..187847843 100644 --- a/lib/remote/actionshandler.cpp +++ b/lib/remote/actionshandler.cpp @@ -6,7 +6,9 @@ #include "remote/apiaction.hpp" #include "base/defer.hpp" #include "base/exception.hpp" +#include "base/generator.hpp" #include "base/logger.hpp" +#include #include using namespace icinga; @@ -70,13 +72,10 @@ bool ActionsHandler::HandleRequest( objs.emplace_back(nullptr); } - ArrayData results; - Log(LogNotice, "ApiActionHandler") << "Running action " << actionName; bool verbose = false; - if (params) verbose = HttpUtility::GetLastParameter(params, "verbose"); @@ -86,73 +85,43 @@ bool ActionsHandler::HandleRequest( return true; } - for (ConfigObject::Ptr obj : objs) { + auto generatorFunc = [&action, &user, ¶ms, &waitGroup, &wgLock, verbose]( + const ConfigObject::Ptr& obj + ) -> std::optional { if (!waitGroup->IsLockable()) { if (wgLock) { wgLock.unlock(); } - results.emplace_back(new Dictionary({ + return new Dictionary{ { "type", obj->GetReflectionType()->GetName() }, { "name", obj->GetName() }, { "code", 503 }, { "status", "Action skipped: Shutting down."} - })); - - continue; + }; } try { - results.emplace_back(action->Invoke(obj, user, params)); + return action->Invoke(obj, user, params); } catch (const std::exception& ex) { - Dictionary::Ptr fail = new Dictionary({ + Dictionary::Ptr fail = new Dictionary{ { "code", 500 }, { "status", "Action execution failed: '" + DiagnosticInformation(ex, false) + "'." } - }); + }; /* Exception for actions. Normally we would handle this inside SendJsonError(). */ if (verbose) fail->Set("diagnostic_information", DiagnosticInformation(ex)); - results.emplace_back(std::move(fail)); + return fail; } - } + }; - int statusCode = 500; - std::set okStatusCodes, nonOkStatusCodes; + Dictionary::Ptr result = new Dictionary{{"results", new ValueGenerator{objs, generatorFunc}}}; + result->Freeze(); - for (Dictionary::Ptr res : results) { - if (!res->Contains("code")) { - continue; - } - - auto code = res->Get("code"); - - if (code >= 200 && code <= 299) { - okStatusCodes.insert(code); - } else { - nonOkStatusCodes.insert(code); - } - } - - size_t okSize = okStatusCodes.size(); - size_t nonOkSize = nonOkStatusCodes.size(); - - if (okSize == 1u && nonOkSize == 0u) { - statusCode = *okStatusCodes.begin(); - } else if (nonOkSize == 1u) { - statusCode = *nonOkStatusCodes.begin(); - } else if (okSize >= 2u && nonOkSize == 0u) { - statusCode = 200; - } - - response.result(statusCode); - - Dictionary::Ptr result = new Dictionary({ - { "results", new Array(std::move(results)) } - }); - - HttpUtility::SendJsonBody(response, params, result); + response.result(http::status::ok); + HttpUtility::SendJsonBody(response, params, result, yc); return true; } diff --git a/lib/remote/configpackageshandler.cpp b/lib/remote/configpackageshandler.cpp index 7e0c7b02c..937eea2d5 100644 --- a/lib/remote/configpackageshandler.cpp +++ b/lib/remote/configpackageshandler.cpp @@ -28,7 +28,7 @@ bool ConfigPackagesHandler::HandleRequest( return false; if (request.method() == http::verb::get) - HandleGet(request, response); + HandleGet(request, response, yc); else if (request.method() == http::verb::post) HandlePost(request, response); else if (request.method() == http::verb::delete_) @@ -39,7 +39,7 @@ bool ConfigPackagesHandler::HandleRequest( return true; } -void ConfigPackagesHandler::HandleGet(const HttpRequest& request, HttpResponse& response) +void ConfigPackagesHandler::HandleGet(const HttpRequest& request, HttpResponse& response, boost::asio::yield_context& yc) { namespace http = boost::beast::http; @@ -79,12 +79,14 @@ void ConfigPackagesHandler::HandleGet(const HttpRequest& request, HttpResponse& } } - Dictionary::Ptr result = new Dictionary({ - { "results", new Array(std::move(results)) } - }); + Array::Ptr resultsArr = new Array(std::move(results)); + resultsArr->Freeze(); + + Dictionary::Ptr result = new Dictionary{{"results", resultsArr}}; + result->Freeze(); response.result(http::status::ok); - HttpUtility::SendJsonBody(response, params, result); + HttpUtility::SendJsonBody(response, params, result, yc); } void ConfigPackagesHandler::HandlePost(const HttpRequest& request, HttpResponse& response) diff --git a/lib/remote/configpackageshandler.hpp b/lib/remote/configpackageshandler.hpp index 172690f63..086ef5b70 100644 --- a/lib/remote/configpackageshandler.hpp +++ b/lib/remote/configpackageshandler.hpp @@ -21,7 +21,7 @@ public: ) override; private: - void HandleGet(const HttpRequest& request, HttpResponse& response); + void HandleGet(const HttpRequest& request, HttpResponse& response, boost::asio::yield_context& yc); void HandlePost(const HttpRequest& request, HttpResponse& response); void HandleDelete(const HttpRequest& request, HttpResponse& response); diff --git a/lib/remote/configstageshandler.cpp b/lib/remote/configstageshandler.cpp index b08270e56..1f3adcd1c 100644 --- a/lib/remote/configstageshandler.cpp +++ b/lib/remote/configstageshandler.cpp @@ -8,6 +8,7 @@ #include "base/application.hpp" #include "base/defer.hpp" #include "base/exception.hpp" +#include using namespace icinga; @@ -35,7 +36,7 @@ bool ConfigStagesHandler::HandleRequest( return false; if (request.method() == http::verb::get) - HandleGet(request, response); + HandleGet(request, response, yc); else if (request.method() == http::verb::post) HandlePost(request, response); else if (request.method() == http::verb::delete_) @@ -46,7 +47,7 @@ bool ConfigStagesHandler::HandleRequest( return true; } -void ConfigStagesHandler::HandleGet(const HttpRequest& request, HttpResponse& response) +void ConfigStagesHandler::HandleGet(const HttpRequest& request, HttpResponse& response, boost::asio::yield_context& yc) { namespace http = boost::beast::http; @@ -71,25 +72,22 @@ void ConfigStagesHandler::HandleGet(const HttpRequest& request, HttpResponse& re if (!ConfigPackageUtility::ValidateStageName(stageName)) return HttpUtility::SendJsonError(response, params, 400, "Invalid stage name '" + stageName + "'."); - ArrayData results; - std::vector > paths = ConfigPackageUtility::GetFiles(packageName, stageName); String prefixPath = ConfigPackageUtility::GetPackageDir() + "/" + packageName + "/" + stageName + "/"; - for (const auto& kv : paths) { - results.push_back(new Dictionary({ + auto generatorFunc = [&prefixPath](const std::pair& kv) -> std::optional { + return new Dictionary{ { "type", kv.second ? "directory" : "file" }, - { "name", kv.first.SubStr(prefixPath.GetLength()) } - })); - } + { "name", kv.first.SubStr(prefixPath.GetLength()) }, + }; + }; - Dictionary::Ptr result = new Dictionary({ - { "results", new Array(std::move(results)) } - }); + Dictionary::Ptr result = new Dictionary{{"results", new ValueGenerator{paths, generatorFunc}}}; + result->Freeze(); response.result(http::status::ok); - HttpUtility::SendJsonBody(response, params, result); + HttpUtility::SendJsonBody(response, params, result, yc); } void ConfigStagesHandler::HandlePost(const HttpRequest& request, HttpResponse& response) diff --git a/lib/remote/configstageshandler.hpp b/lib/remote/configstageshandler.hpp index ec333cc50..0e5b18307 100644 --- a/lib/remote/configstageshandler.hpp +++ b/lib/remote/configstageshandler.hpp @@ -21,7 +21,7 @@ public: ) override; private: - void HandleGet(const HttpRequest& request, HttpResponse& response); + void HandleGet(const HttpRequest& request, HttpResponse& response, boost::asio::yield_context& yc); void HandlePost(const HttpRequest& request, HttpResponse& response); void HandleDelete(const HttpRequest& request, HttpResponse& response); }; diff --git a/lib/remote/deleteobjecthandler.cpp b/lib/remote/deleteobjecthandler.cpp index cd99f7b28..f59bc04fc 100644 --- a/lib/remote/deleteobjecthandler.cpp +++ b/lib/remote/deleteobjecthandler.cpp @@ -9,6 +9,7 @@ #include "config/configitem.hpp" #include "base/exception.hpp" #include +#include #include using namespace icinga; @@ -74,32 +75,26 @@ bool DeleteObjectHandler::HandleRequest( return true; } - ArrayData results; - - bool success = true; - std::shared_lock wgLock{*waitGroup, std::try_to_lock}; if (!wgLock) { HttpUtility::SendJsonError(response, params, 503, "Shutting down."); return true; } - for (ConfigObject::Ptr obj : objs) { + auto generatorFunc = [&type, &waitGroup, &wgLock, cascade, verbose]( + const ConfigObject::Ptr& obj + ) -> std::optional { if (!waitGroup->IsLockable()) { if (wgLock) { wgLock.unlock(); } - results.emplace_back(new Dictionary({ + return new Dictionary{ { "type", type->GetName() }, { "name", obj->GetName() }, { "code", 503 }, { "status", "Action skipped: Shutting down."} - })); - - success = false; - - continue; + }; } int code; @@ -113,36 +108,30 @@ bool DeleteObjectHandler::HandleRequest( if (!ConfigObjectUtility::DeleteObject(obj, cascade, errors, diagnosticInformation)) { code = 500; status = "Object could not be deleted."; - success = false; } else { code = 200; status = "Object was deleted."; } - Dictionary::Ptr result = new Dictionary({ + Dictionary::Ptr result = new Dictionary{ { "type", type->GetName() }, { "name", obj->GetName() }, { "code", code }, { "status", status }, { "errors", errors } - }); + }; if (verbose) result->Set("diagnostic_information", diagnosticInformation); - results.push_back(result); - } + return result; + }; - Dictionary::Ptr result = new Dictionary({ - { "results", new Array(std::move(results)) } - }); + Dictionary::Ptr result = new Dictionary{{"results", new ValueGenerator{objs, generatorFunc}}}; + result->Freeze(); - if (!success) - response.result(http::status::internal_server_error); - else - response.result(http::status::ok); - - HttpUtility::SendJsonBody(response, params, result); + response.result(http::status::ok); + HttpUtility::SendJsonBody(response, params, result, yc); return true; } diff --git a/lib/remote/modifyobjecthandler.cpp b/lib/remote/modifyobjecthandler.cpp index 9264e3c64..dca08961f 100644 --- a/lib/remote/modifyobjecthandler.cpp +++ b/lib/remote/modifyobjecthandler.cpp @@ -6,7 +6,9 @@ #include "remote/filterutility.hpp" #include "remote/apiaction.hpp" #include "base/exception.hpp" +#include "base/generator.hpp" #include +#include #include using namespace icinga; @@ -102,31 +104,28 @@ bool ModifyObjectHandler::HandleRequest( return true; } - ArrayData results; - std::shared_lock wgLock{*waitGroup, std::try_to_lock}; if (!wgLock) { HttpUtility::SendJsonError(response, params, 503, "Shutting down."); return true; } - for (ConfigObject::Ptr obj : objs) { - Dictionary::Ptr result1 = new Dictionary(); + auto generatorFunc = [&waitGroup, &wgLock, &type, &attrs, &restoreAttrs, verbose]( + const ConfigObject::Ptr& obj + ) -> std::optional { + Dictionary::Ptr result = new Dictionary(); - result1->Set("type", type->GetName()); - result1->Set("name", obj->GetName()); + result->Set("type", type->GetName()); + result->Set("name", obj->GetName()); if (!waitGroup->IsLockable()) { if (wgLock) { wgLock.unlock(); } - result1->Set("code", 503); - result1->Set("status", "Action skipped: Shutting down."); - - results.emplace_back(std::move(result1)); - - continue; + result->Set("code", 503); + result->Set("status", "Action skipped: Shutting down."); + return result; } String key; @@ -144,14 +143,13 @@ bool ModifyObjectHandler::HandleRequest( } } } catch (const std::exception& ex) { - result1->Set("code", 500); - result1->Set("status", "Attribute '" + key + "' could not be restored: " + DiagnosticInformation(ex, false)); + result->Set("code", 500); + result->Set("status", "Attribute '" + key + "' could not be restored: " + DiagnosticInformation(ex, false)); if (verbose) - result1->Set("diagnostic_information", DiagnosticInformation(ex)); + result->Set("diagnostic_information", DiagnosticInformation(ex)); - results.push_back(std::move(result1)); - continue; + return result; } try { @@ -163,28 +161,25 @@ bool ModifyObjectHandler::HandleRequest( } } } catch (const std::exception& ex) { - result1->Set("code", 500); - result1->Set("status", "Attribute '" + key + "' could not be set: " + DiagnosticInformation(ex, false)); + result->Set("code", 500); + result->Set("status", "Attribute '" + key + "' could not be set: " + DiagnosticInformation(ex, false)); if (verbose) - result1->Set("diagnostic_information", DiagnosticInformation(ex)); + result->Set("diagnostic_information", DiagnosticInformation(ex)); - results.push_back(std::move(result1)); - continue; + return result; } - result1->Set("code", 200); - result1->Set("status", "Attributes updated."); + result->Set("code", 200); + result->Set("status", "Attributes updated."); + return result; + }; - results.push_back(std::move(result1)); - } - - Dictionary::Ptr result = new Dictionary({ - { "results", new Array(std::move(results)) } - }); + Dictionary::Ptr result = new Dictionary{{"results", new ValueGenerator{objs, generatorFunc}}}; + result->Freeze(); response.result(http::status::ok); - HttpUtility::SendJsonBody(response, params, result); + HttpUtility::SendJsonBody(response, params, result, yc); return true; } diff --git a/lib/remote/templatequeryhandler.cpp b/lib/remote/templatequeryhandler.cpp index 81261f02d..8a8ec1be1 100644 --- a/lib/remote/templatequeryhandler.cpp +++ b/lib/remote/templatequeryhandler.cpp @@ -125,12 +125,14 @@ bool TemplateQueryHandler::HandleRequest( return true; } - Dictionary::Ptr result = new Dictionary({ - { "results", new Array(std::move(objs)) } - }); + Array::Ptr resultArr = new Array(std::move(objs)); + resultArr->Freeze(); + + Dictionary::Ptr result = new Dictionary{{"results", resultArr}}; + result->Freeze(); response.result(http::status::ok); - HttpUtility::SendJsonBody(response, params, result); + HttpUtility::SendJsonBody(response, params, result, yc); return true; } diff --git a/lib/remote/typequeryhandler.cpp b/lib/remote/typequeryhandler.cpp index dda19cd12..a17406df8 100644 --- a/lib/remote/typequeryhandler.cpp +++ b/lib/remote/typequeryhandler.cpp @@ -4,8 +4,10 @@ #include "remote/httputility.hpp" #include "remote/filterutility.hpp" #include "base/configtype.hpp" +#include "base/generator.hpp" #include "base/scriptglobal.hpp" #include "base/logger.hpp" +#include #include using namespace icinga; @@ -89,23 +91,19 @@ bool TypeQueryHandler::HandleRequest( return true; } - ArrayData results; - - for (Type::Ptr obj : objs) { - Dictionary::Ptr result1 = new Dictionary(); - results.push_back(result1); - + auto generatorFunc = [](const Type::Ptr& obj) -> std::optional { + Dictionary::Ptr result = new Dictionary(); Dictionary::Ptr resultAttrs = new Dictionary(); - result1->Set("name", obj->GetName()); - result1->Set("plural_name", obj->GetPluralName()); + result->Set("name", obj->GetName()); + result->Set("plural_name", obj->GetPluralName()); if (obj->GetBaseType()) - result1->Set("base", obj->GetBaseType()->GetName()); - result1->Set("abstract", obj->IsAbstract()); - result1->Set("fields", resultAttrs); + result->Set("base", obj->GetBaseType()->GetName()); + result->Set("abstract", obj->IsAbstract()); + result->Set("fields", resultAttrs); Dictionary::Ptr prototype = dynamic_pointer_cast(obj->GetPrototype()); Array::Ptr prototypeKeys = new Array(); - result1->Set("prototype_keys", prototypeKeys); + result->Set("prototype_keys", prototypeKeys); if (prototype) { ObjectLock olock(prototype); @@ -143,14 +141,14 @@ bool TypeQueryHandler::HandleRequest( { "deprecated", static_cast(field.Attributes & FADeprecated) } })); } - } + return result; + }; - Dictionary::Ptr result = new Dictionary({ - { "results", new Array(std::move(results)) } - }); + Dictionary::Ptr result = new Dictionary{{"results", new ValueGenerator{objs, generatorFunc}}}; + result->Freeze(); response.result(http::status::ok); - HttpUtility::SendJsonBody(response, params, result); + HttpUtility::SendJsonBody(response, params, result, yc); return true; } diff --git a/lib/remote/variablequeryhandler.cpp b/lib/remote/variablequeryhandler.cpp index e96f6abf8..8b6d691cc 100644 --- a/lib/remote/variablequeryhandler.cpp +++ b/lib/remote/variablequeryhandler.cpp @@ -4,10 +4,12 @@ #include "remote/httputility.hpp" #include "remote/filterutility.hpp" #include "base/configtype.hpp" +#include "base/generator.hpp" #include "base/scriptglobal.hpp" #include "base/logger.hpp" #include "base/serializer.hpp" #include "base/namespace.hpp" +#include #include using namespace icinga; @@ -96,25 +98,26 @@ bool VariableQueryHandler::HandleRequest( return true; } - ArrayData results; + auto generatorFunc = [](const Dictionary::Ptr& var) -> std::optional { + if (var->Get("name") == "TicketSalt") { + // Returning a nullopt here will cause the generator to skip this entry. + // Meaning, when this variable wasn't the last element in the container, + // the generator will directly call us again with the next element. + return std::nullopt; + } - for (Dictionary::Ptr var : objs) { - if (var->Get("name") == "TicketSalt") - continue; - - results.emplace_back(new Dictionary({ + return new Dictionary{ { "name", var->Get("name") }, { "type", var->Get("type") }, { "value", Serialize(var->Get("value"), 0) } - })); - } + }; + }; - Dictionary::Ptr result = new Dictionary({ - { "results", new Array(std::move(results)) } - }); + Dictionary::Ptr result = new Dictionary{{"results", new ValueGenerator{objs, generatorFunc}}}; + result->Freeze(); response.result(http::status::ok); - HttpUtility::SendJsonBody(response, params, result); + HttpUtility::SendJsonBody(response, params, result, yc); return true; }