mirror of
https://github.com/Icinga/icinga2.git
synced 2025-09-26 02:58:43 +02:00
HTTP: stream responses where appropriate
This commit is contained in:
parent
047f44a3b3
commit
e7c088b05b
@ -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 <optional>
|
||||
#include <set>
|
||||
|
||||
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<Value> {
|
||||
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<int> 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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "base/application.hpp"
|
||||
#include "base/defer.hpp"
|
||||
#include "base/exception.hpp"
|
||||
#include <optional>
|
||||
|
||||
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<std::pair<String, bool> > 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<String, bool>& kv) -> std::optional<Value> {
|
||||
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)
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "config/configitem.hpp"
|
||||
#include "base/exception.hpp"
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
|
||||
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<Value> {
|
||||
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;
|
||||
}
|
||||
|
@ -6,7 +6,9 @@
|
||||
#include "remote/filterutility.hpp"
|
||||
#include "remote/apiaction.hpp"
|
||||
#include "base/exception.hpp"
|
||||
#include "base/generator.hpp"
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
|
||||
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<Value> {
|
||||
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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 <optional>
|
||||
#include <set>
|
||||
|
||||
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<Value> {
|
||||
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<Dictionary>(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<bool>(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;
|
||||
}
|
||||
|
@ -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 <optional>
|
||||
#include <set>
|
||||
|
||||
using namespace icinga;
|
||||
@ -96,25 +98,26 @@ bool VariableQueryHandler::HandleRequest(
|
||||
return true;
|
||||
}
|
||||
|
||||
ArrayData results;
|
||||
auto generatorFunc = [](const Dictionary::Ptr& var) -> std::optional<Value> {
|
||||
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;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user