Always call HttpUtility::SendJsonBody() with a boost::asio::yield_context

to respect `l_FlushThreshold` in all messages.

Especially `l_FlushThreshold` may now be lowered in the future
without re-evaluating all possible responses.

Side effect: all HTTP responses use chunked encoding now.
This commit is contained in:
Alexander A. Klimov 2026-03-19 15:07:51 +01:00
parent 3312ed7ec4
commit a56388940e
24 changed files with 107 additions and 116 deletions

View file

@ -37,7 +37,7 @@ bool ActionsHandler::HandleRequest(
ApiAction::Ptr action = ApiAction::GetByName(actionName);
if (!action) {
HttpUtility::SendJsonError(response, params, 404, "Action '" + actionName + "' does not exist.");
HttpUtility::SendJsonError(response, params, 404, yc, "Action '" + actionName + "' does not exist.");
return true;
}
@ -55,14 +55,14 @@ bool ActionsHandler::HandleRequest(
try {
objs = FilterUtility::GetFilterTargets(qd, params, user);
} catch (const std::exception& ex) {
HttpUtility::SendJsonError(response, params, 404,
HttpUtility::SendJsonError(response, params, 404, yc,
"No objects found.",
DiagnosticInformation(ex));
return true;
}
if (objs.empty()) {
HttpUtility::SendJsonError(response, params, 404,
HttpUtility::SendJsonError(response, params, 404, yc,
"No objects found.");
return true;
}
@ -83,7 +83,7 @@ bool ActionsHandler::HandleRequest(
std::shared_lock wgLock{*waitGroup, std::try_to_lock};
if (!wgLock) {
HttpUtility::SendJsonError(response, params, 503, "Shutting down.");
HttpUtility::SendJsonError(response, params, 503, yc, "Shutting down.");
return true;
}

View file

@ -44,7 +44,7 @@ bool ConfigFilesHandler::HandleRequest(
}
if (request[http::field::accept] == "application/json") {
HttpUtility::SendJsonError(response, params, 400, "Invalid Accept header. Either remove the Accept header or set it to 'application/octet-stream'.");
HttpUtility::SendJsonError(response, params, 400, yc, "Invalid Accept header. Either remove the Accept header or set it to 'application/octet-stream'.");
return true;
}
@ -54,26 +54,26 @@ bool ConfigFilesHandler::HandleRequest(
String stageName = HttpUtility::GetLastParameter(params, "stage");
if (!ConfigPackageUtility::ValidatePackageName(packageName)) {
HttpUtility::SendJsonError(response, params, 400, "Invalid package name.");
HttpUtility::SendJsonError(response, params, 400, yc, "Invalid package name.");
return true;
}
if (!ConfigPackageUtility::ValidateStageName(stageName)) {
HttpUtility::SendJsonError(response, params, 400, "Invalid stage name.");
HttpUtility::SendJsonError(response, params, 400, yc, "Invalid stage name.");
return true;
}
String relativePath = HttpUtility::GetLastParameter(params, "path");
if (ConfigPackageUtility::ContainsDotDot(relativePath)) {
HttpUtility::SendJsonError(response, params, 400, "Path contains '..' (not allowed).");
HttpUtility::SendJsonError(response, params, 400, yc, "Path contains '..' (not allowed).");
return true;
}
String path = ConfigPackageUtility::GetPackageDir() + "/" + packageName + "/" + stageName + "/" + relativePath;
if (!Utility::PathExists(path)) {
HttpUtility::SendJsonError(response, params, 404, "Path not found.");
HttpUtility::SendJsonError(response, params, 404, yc, "Path not found.");
return true;
}
@ -82,7 +82,7 @@ bool ConfigFilesHandler::HandleRequest(
response.set(http::field::content_type, "application/octet-stream");
response.SendFile(path, yc);
} catch (const std::exception& ex) {
HttpUtility::SendJsonError(response, params, 500, "Could not read file.",
HttpUtility::SendJsonError(response, params, 500, yc, "Could not read file.",
DiagnosticInformation(ex));
}

View file

@ -31,9 +31,9 @@ bool ConfigPackagesHandler::HandleRequest(
if (request.method() == http::verb::get)
HandleGet(request, response, yc);
else if (request.method() == http::verb::post)
HandlePost(request, response);
HandlePost(request, response, yc);
else if (request.method() == http::verb::delete_)
HandleDelete(request, response);
HandleDelete(request, response, yc);
else
return false;
@ -55,7 +55,7 @@ void ConfigPackagesHandler::HandleGet(const HttpApiRequest& request, HttpApiResp
try {
packages = ConfigPackageUtility::GetPackages();
} catch (const std::exception& ex) {
HttpUtility::SendJsonError(response, params, 500, "Could not retrieve packages.",
HttpUtility::SendJsonError(response, params, 500, yc, "Could not retrieve packages.",
DiagnosticInformation(ex));
return;
}
@ -90,7 +90,7 @@ void ConfigPackagesHandler::HandleGet(const HttpApiRequest& request, HttpApiResp
HttpUtility::SendJsonBody(response, params, result, yc);
}
void ConfigPackagesHandler::HandlePost(const HttpApiRequest& request, HttpApiResponse& response)
void ConfigPackagesHandler::HandlePost(const HttpApiRequest& request, HttpApiResponse& response, boost::asio::yield_context& yc)
{
namespace http = boost::beast::http;
@ -106,13 +106,13 @@ void ConfigPackagesHandler::HandlePost(const HttpApiRequest& request, HttpApiRes
String packageName = HttpUtility::GetLastParameter(params, "package");
if (!ConfigPackageUtility::ValidatePackageName(packageName)) {
HttpUtility::SendJsonError(response, params, 400, "Invalid package name '" + packageName + "'.");
HttpUtility::SendJsonError(response, params, 400, yc, "Invalid package name '" + packageName + "'.");
return;
}
ConfigObjectsSharedLock configObjectsSharedLock(std::try_to_lock);
if (!configObjectsSharedLock) {
HttpUtility::SendJsonError(response, params, 503, "Icinga is reloading");
HttpUtility::SendJsonError(response, params, 503, yc, "Icinga is reloading");
return;
}
@ -121,7 +121,7 @@ void ConfigPackagesHandler::HandlePost(const HttpApiRequest& request, HttpApiRes
ConfigPackageUtility::CreatePackage(packageName);
} catch (const std::exception& ex) {
HttpUtility::SendJsonError(response, params, 500, "Could not create package '" + packageName + "'.",
HttpUtility::SendJsonError(response, params, 500, yc, "Could not create package '" + packageName + "'.",
DiagnosticInformation(ex));
return;
}
@ -137,10 +137,10 @@ void ConfigPackagesHandler::HandlePost(const HttpApiRequest& request, HttpApiRes
});
response.result(http::status::ok);
HttpUtility::SendJsonBody(response, params, result);
HttpUtility::SendJsonBody(response, params, result, yc);
}
void ConfigPackagesHandler::HandleDelete(const HttpApiRequest& request, HttpApiResponse& response)
void ConfigPackagesHandler::HandleDelete(const HttpApiRequest& request, HttpApiResponse& response, boost::asio::yield_context& yc)
{
namespace http = boost::beast::http;
@ -156,20 +156,20 @@ void ConfigPackagesHandler::HandleDelete(const HttpApiRequest& request, HttpApiR
String packageName = HttpUtility::GetLastParameter(params, "package");
if (!ConfigPackageUtility::ValidatePackageName(packageName)) {
HttpUtility::SendJsonError(response, params, 400, "Invalid package name '" + packageName + "'.");
HttpUtility::SendJsonError(response, params, 400, yc, "Invalid package name '" + packageName + "'.");
return;
}
ConfigObjectsSharedLock lock(std::try_to_lock);
if (!lock) {
HttpUtility::SendJsonError(response, params, 503, "Icinga is reloading");
HttpUtility::SendJsonError(response, params, 503, yc, "Icinga is reloading");
return;
}
try {
ConfigPackageUtility::DeletePackage(packageName);
} catch (const std::exception& ex) {
HttpUtility::SendJsonError(response, params, 500, "Failed to delete package '" + packageName + "'.",
HttpUtility::SendJsonError(response, params, 500, yc, "Failed to delete package '" + packageName + "'.",
DiagnosticInformation(ex));
return;
}
@ -185,5 +185,5 @@ void ConfigPackagesHandler::HandleDelete(const HttpApiRequest& request, HttpApiR
});
response.result(http::status::ok);
HttpUtility::SendJsonBody(response, params, result);
HttpUtility::SendJsonBody(response, params, result, yc);
}

View file

@ -23,8 +23,8 @@ public:
private:
void HandleGet(const HttpApiRequest& request, HttpApiResponse& response, boost::asio::yield_context& yc);
void HandlePost(const HttpApiRequest& request, HttpApiResponse& response);
void HandleDelete(const HttpApiRequest& request, HttpApiResponse& response);
void HandlePost(const HttpApiRequest& request, HttpApiResponse& response, boost::asio::yield_context& yc);
void HandleDelete(const HttpApiRequest& request, HttpApiResponse& response, boost::asio::yield_context& yc);
};

View file

@ -38,9 +38,9 @@ bool ConfigStagesHandler::HandleRequest(
if (request.method() == http::verb::get)
HandleGet(request, response, yc);
else if (request.method() == http::verb::post)
HandlePost(request, response);
HandlePost(request, response, yc);
else if (request.method() == http::verb::delete_)
HandleDelete(request, response);
HandleDelete(request, response, yc);
else
return false;
@ -67,10 +67,10 @@ void ConfigStagesHandler::HandleGet(const HttpApiRequest& request, HttpApiRespon
String stageName = HttpUtility::GetLastParameter(params, "stage");
if (!ConfigPackageUtility::ValidatePackageName(packageName))
return HttpUtility::SendJsonError(response, params, 400, "Invalid package name '" + packageName + "'.");
return HttpUtility::SendJsonError(response, params, 400, yc, "Invalid package name '" + packageName + "'.");
if (!ConfigPackageUtility::ValidateStageName(stageName))
return HttpUtility::SendJsonError(response, params, 400, "Invalid stage name '" + stageName + "'.");
return HttpUtility::SendJsonError(response, params, 400, yc, "Invalid stage name '" + stageName + "'.");
std::vector<std::pair<String, bool> > paths = ConfigPackageUtility::GetFiles(packageName, stageName);
@ -90,7 +90,7 @@ void ConfigStagesHandler::HandleGet(const HttpApiRequest& request, HttpApiRespon
HttpUtility::SendJsonBody(response, params, result, yc);
}
void ConfigStagesHandler::HandlePost(const HttpApiRequest& request, HttpApiResponse& response)
void ConfigStagesHandler::HandlePost(const HttpApiRequest& request, HttpApiResponse& response, boost::asio::yield_context& yc)
{
namespace http = boost::beast::http;
@ -106,7 +106,7 @@ void ConfigStagesHandler::HandlePost(const HttpApiRequest& request, HttpApiRespo
String packageName = HttpUtility::GetLastParameter(params, "package");
if (!ConfigPackageUtility::ValidatePackageName(packageName))
return HttpUtility::SendJsonError(response, params, 400, "Invalid package name '" + packageName + "'.");
return HttpUtility::SendJsonError(response, params, 400, yc, "Invalid package name '" + packageName + "'.");
bool reload = true;
@ -131,7 +131,7 @@ void ConfigStagesHandler::HandlePost(const HttpApiRequest& request, HttpApiRespo
ConfigObjectsSharedLock configObjectsSharedLock(std::try_to_lock);
if (!configObjectsSharedLock) {
HttpUtility::SendJsonError(response, params, 503, "Icinga is reloading");
HttpUtility::SendJsonError(response, params, 503, yc, "Icinga is reloading");
return;
}
@ -152,7 +152,7 @@ void ConfigStagesHandler::HandlePost(const HttpApiRequest& request, HttpApiRespo
*/
if (l_RunningPackageUpdates && l_LastReloadFailedTime == currentReloadFailedTime) {
return HttpUtility::SendJsonError(
response, params, 423,
response, params, 423, yc,
"Conflicting request, there is already an ongoing package update in progress. Please try it again later."
);
}
@ -173,7 +173,7 @@ void ConfigStagesHandler::HandlePost(const HttpApiRequest& request, HttpApiRespo
/* validate the config. on success, activate stage and reload */
ConfigPackageUtility::AsyncTryActivateStage(packageName, stageName, activate, reload, resetPackageUpdates);
} catch (const std::exception& ex) {
return HttpUtility::SendJsonError(response, params, 500,
return HttpUtility::SendJsonError(response, params, 500, yc,
"Stage creation failed.",
DiagnosticInformation(ex));
}
@ -198,10 +198,10 @@ void ConfigStagesHandler::HandlePost(const HttpApiRequest& request, HttpApiRespo
});
response.result(http::status::ok);
HttpUtility::SendJsonBody(response, params, result);
HttpUtility::SendJsonBody(response, params, result, yc);
}
void ConfigStagesHandler::HandleDelete(const HttpApiRequest& request, HttpApiResponse& response)
void ConfigStagesHandler::HandleDelete(const HttpApiRequest& request, HttpApiResponse& response, boost::asio::yield_context& yc)
{
namespace http = boost::beast::http;
@ -221,21 +221,21 @@ void ConfigStagesHandler::HandleDelete(const HttpApiRequest& request, HttpApiRes
String stageName = HttpUtility::GetLastParameter(params, "stage");
if (!ConfigPackageUtility::ValidatePackageName(packageName))
return HttpUtility::SendJsonError(response, params, 400, "Invalid package name '" + packageName + "'.");
return HttpUtility::SendJsonError(response, params, 400, yc, "Invalid package name '" + packageName + "'.");
if (!ConfigPackageUtility::ValidateStageName(stageName))
return HttpUtility::SendJsonError(response, params, 400, "Invalid stage name '" + stageName + "'.");
return HttpUtility::SendJsonError(response, params, 400, yc, "Invalid stage name '" + stageName + "'.");
ConfigObjectsSharedLock lock(std::try_to_lock);
if (!lock) {
HttpUtility::SendJsonError(response, params, 503, "Icinga is reloading");
HttpUtility::SendJsonError(response, params, 503, yc, "Icinga is reloading");
return;
}
try {
ConfigPackageUtility::DeleteStage(packageName, stageName);
} catch (const std::exception& ex) {
return HttpUtility::SendJsonError(response, params, 500,
return HttpUtility::SendJsonError(response, params, 500, yc,
"Failed to delete stage '" + stageName + "' in package '" + packageName + "'.",
DiagnosticInformation(ex));
}
@ -252,5 +252,5 @@ void ConfigStagesHandler::HandleDelete(const HttpApiRequest& request, HttpApiRes
});
response.result(http::status::ok);
HttpUtility::SendJsonBody(response, params, result);
HttpUtility::SendJsonBody(response, params, result, yc);
}

View file

@ -23,8 +23,8 @@ public:
private:
void HandleGet(const HttpApiRequest& request, HttpApiResponse& response, boost::asio::yield_context& yc);
void HandlePost(const HttpApiRequest& request, HttpApiResponse& response);
void HandleDelete(const HttpApiRequest& request, HttpApiResponse& response);
void HandlePost(const HttpApiRequest& request, HttpApiResponse& response, boost::asio::yield_context& yc);
void HandleDelete(const HttpApiRequest& request, HttpApiResponse& response, boost::asio::yield_context& yc);
};
}

View file

@ -73,7 +73,7 @@ bool ConsoleHandler::HandleRequest(
const WaitGroup::Ptr&,
const HttpApiRequest& request,
HttpApiResponse& response,
boost::asio::yield_context&
boost::asio::yield_context& yc
)
{
namespace http = boost::beast::http;
@ -106,21 +106,21 @@ bool ConsoleHandler::HandleRequest(
ConfigObjectsSharedLock lock (std::try_to_lock);
if (!lock) {
HttpUtility::SendJsonError(response, params, 503, "Icinga is reloading.");
HttpUtility::SendJsonError(response, params, 503, yc, "Icinga is reloading.");
return true;
}
if (methodName == "execute-script")
return ExecuteScriptHelper(request, response, command, session, sandboxed);
return ExecuteScriptHelper(request, response, command, session, sandboxed, yc);
else if (methodName == "auto-complete-script")
return AutocompleteScriptHelper(request, response, command, session, sandboxed);
return AutocompleteScriptHelper(request, response, command, session, sandboxed, yc);
HttpUtility::SendJsonError(response, params, 400, "Invalid method specified: " + methodName);
HttpUtility::SendJsonError(response, params, 400, yc, "Invalid method specified: " + methodName);
return true;
}
bool ConsoleHandler::ExecuteScriptHelper(const HttpApiRequest& request, HttpApiResponse& response,
const String& command, const String& session, bool sandboxed)
const String& command, const String& session, bool sandboxed, boost::asio::yield_context& yc)
{
namespace http = boost::beast::http;
@ -133,7 +133,7 @@ bool ConsoleHandler::ExecuteScriptHelper(const HttpApiRequest& request, HttpApiR
std::unique_lock frameLock(lsf->Mutex, std::try_to_lock);
if (!frameLock) {
HttpUtility::SendJsonError(response, request.Params(), 409, "Session is currently in use by another request.");
HttpUtility::SendJsonError(response, request.Params(), 409, yc, "Session is currently in use by another request.");
return true;
}
@ -195,13 +195,13 @@ bool ConsoleHandler::ExecuteScriptHelper(const HttpApiRequest& request, HttpApiR
});
response.result(http::status::ok);
HttpUtility::SendJsonBody(response, request.Params(), result);
HttpUtility::SendJsonBody(response, request.Params(), result, yc);
return true;
}
bool ConsoleHandler::AutocompleteScriptHelper(const HttpApiRequest& request, HttpApiResponse& response,
const String& command, const String& session, bool sandboxed)
const String& command, const String& session, bool sandboxed, boost::asio::yield_context& yc)
{
namespace http = boost::beast::http;
@ -214,7 +214,7 @@ bool ConsoleHandler::AutocompleteScriptHelper(const HttpApiRequest& request, Htt
std::unique_lock frameLock(lsf->Mutex, std::try_to_lock);
if (!frameLock) {
HttpUtility::SendJsonError(response, request.Params(), 409, "Session is currently in use by another request.");
HttpUtility::SendJsonError(response, request.Params(), 409, yc, "Session is currently in use by another request.");
return true;
}
@ -240,7 +240,7 @@ bool ConsoleHandler::AutocompleteScriptHelper(const HttpApiRequest& request, Htt
});
response.result(http::status::ok);
HttpUtility::SendJsonBody(response, request.Params(), result);
HttpUtility::SendJsonBody(response, request.Params(), result, yc);
return true;
}

View file

@ -36,9 +36,9 @@ public:
private:
static bool ExecuteScriptHelper(const HttpApiRequest& request, HttpApiResponse& response,
const String& command, const String& session, bool sandboxed);
const String& command, const String& session, bool sandboxed, boost::asio::yield_context& yc);
static bool AutocompleteScriptHelper(const HttpApiRequest& request, HttpApiResponse& response,
const String& command, const String& session, bool sandboxed);
const String& command, const String& session, bool sandboxed, boost::asio::yield_context& yc);
};

View file

@ -20,7 +20,7 @@ bool CreateObjectHandler::HandleRequest(
const WaitGroup::Ptr& waitGroup,
const HttpApiRequest& request,
HttpApiResponse& response,
boost::asio::yield_context&
boost::asio::yield_context& yc
)
{
namespace http = boost::beast::http;
@ -38,7 +38,7 @@ bool CreateObjectHandler::HandleRequest(
Type::Ptr type = FilterUtility::TypeFromPluralName(url->GetPath()[2]);
if (!type) {
HttpUtility::SendJsonError(response, params, 400, "Invalid type specified.");
HttpUtility::SendJsonError(response, params, 400, yc, "Invalid type specified.");
return true;
}
@ -99,13 +99,13 @@ bool CreateObjectHandler::HandleRequest(
ConfigObjectsSharedLock lock (std::try_to_lock);
if (!lock) {
HttpUtility::SendJsonError(response, params, 503, "Icinga is reloading");
HttpUtility::SendJsonError(response, params, 503, yc, "Icinga is reloading");
return true;
}
std::shared_lock wgLock{*waitGroup, std::try_to_lock};
if (!wgLock) {
HttpUtility::SendJsonError(response, params, 503, "Shutting down.");
HttpUtility::SendJsonError(response, params, 503, yc, "Shutting down.");
return true;
}
@ -126,7 +126,7 @@ bool CreateObjectHandler::HandleRequest(
result1->Set("status", "Object could not be created.");
response.result(http::status::internal_server_error);
HttpUtility::SendJsonBody(response, params, result);
HttpUtility::SendJsonBody(response, params, result, yc);
return true;
}
@ -143,7 +143,7 @@ bool CreateObjectHandler::HandleRequest(
result1->Set("diagnostic_information", diagnosticInformation);
response.result(http::status::internal_server_error);
HttpUtility::SendJsonBody(response, params, result);
HttpUtility::SendJsonBody(response, params, result, yc);
return true;
}
@ -159,7 +159,7 @@ bool CreateObjectHandler::HandleRequest(
result1->Set("status", "Object was not created but 'ignore_on_error' was set to true");
response.result(http::status::ok);
HttpUtility::SendJsonBody(response, params, result);
HttpUtility::SendJsonBody(response, params, result, yc);
return true;
}

View file

@ -38,7 +38,7 @@ bool DeleteObjectHandler::HandleRequest(
Type::Ptr type = FilterUtility::TypeFromPluralName(url->GetPath()[2]);
if (!type) {
HttpUtility::SendJsonError(response, params, 400, "Invalid type specified.");
HttpUtility::SendJsonError(response, params, 400, yc, "Invalid type specified.");
return true;
}
@ -59,7 +59,7 @@ bool DeleteObjectHandler::HandleRequest(
try {
objs = FilterUtility::GetFilterTargets(qd, params, user);
} catch (const std::exception& ex) {
HttpUtility::SendJsonError(response, params, 404,
HttpUtility::SendJsonError(response, params, 404, yc,
"No objects found.",
DiagnosticInformation(ex));
return true;
@ -71,7 +71,7 @@ bool DeleteObjectHandler::HandleRequest(
ConfigObjectsSharedLock lock (std::try_to_lock);
if (!lock) {
HttpUtility::SendJsonError(response, params, 503, "Icinga is reloading");
HttpUtility::SendJsonError(response, params, 503, yc, "Icinga is reloading");
return true;
}
@ -81,7 +81,7 @@ bool DeleteObjectHandler::HandleRequest(
std::shared_lock wgLock{*waitGroup, std::try_to_lock};
if (!wgLock) {
HttpUtility::SendJsonError(response, params, 503, "Shutting down.");
HttpUtility::SendJsonError(response, params, 503, yc, "Shutting down.");
return true;
}

View file

@ -61,14 +61,14 @@ bool EventsHandler::HandleRequest(
return false;
if (request.version() == 10) {
HttpUtility::SendJsonError(response, params, 400, "HTTP/1.0 not supported for event streams.");
HttpUtility::SendJsonError(response, params, 400, yc, "HTTP/1.0 not supported for event streams.");
return true;
}
Array::Ptr types = params->Get("types");
if (!types) {
HttpUtility::SendJsonError(response, params, 400, "'types' query parameter is required.");
HttpUtility::SendJsonError(response, params, 400, yc, "'types' query parameter is required.");
return true;
}

View file

@ -91,7 +91,7 @@ void HttpHandler::ProcessRequest(
try {
request.DecodeParams();
} catch (const std::exception& ex) {
HttpUtility::SendJsonError(response, request.Params(), 400, "Invalid request body: " + DiagnosticInformation(ex, false));
HttpUtility::SendJsonError(response, request.Params(), 400, yc, "Invalid request body: " + DiagnosticInformation(ex, false));
return;
}
@ -131,7 +131,7 @@ void HttpHandler::ProcessRequest(
}
if (!processed) {
HttpUtility::SendJsonError(response, request.Params(), 404, "The requested path '" + boost::algorithm::join(path, "/") +
HttpUtility::SendJsonError(response, request.Params(), 404, yc, "The requested path '" + boost::algorithm::join(path, "/") +
"' could not be found or the request method is not valid for this path.");
return;
}

View file

@ -198,7 +198,7 @@ bool EnsureValidHeaders(
response.result(http::status::bad_request);
if (!httpError && request[http::field::accept] == "application/json") {
HttpUtility::SendJsonError(response, nullptr, 400, "Bad Request: " + errorMsg);
HttpUtility::SendJsonError(response, nullptr, 400, yc, "Bad Request: " + errorMsg);
} else {
response.set(http::field::content_type, "text/html");
response.body() << "<h1>Bad Request</h1><p><pre>" << errorMsg << "</pre></p>";
@ -315,7 +315,7 @@ bool EnsureAuthenticatedUser(
response.set(http::field::connection, "close");
if (request[http::field::accept] == "application/json") {
HttpUtility::SendJsonError(response, nullptr, 401, "Unauthorized. Please check your user credentials.");
HttpUtility::SendJsonError(response, nullptr, 401, yc, "Unauthorized. Please check your user credentials.");
} else {
response.set(http::field::content_type, "text/html");
response.body() << "<h1>Unauthorized. Please check your user credentials.</h1>";
@ -396,7 +396,7 @@ bool EnsureValidBody(
response.result(http::status::bad_request);
if (request[http::field::accept] == "application/json") {
HttpUtility::SendJsonError(response, nullptr, 400, "Bad Request: " + ec.message());
HttpUtility::SendJsonError(response, nullptr, 400, yc, "Bad Request: " + ec.message());
} else {
response.set(http::field::content_type, "text/html");
response.body() << "<h1>Bad Request</h1><p><pre>" << ec.message() << "</pre></p>";
@ -438,7 +438,7 @@ void ProcessRequest(
throw;
}
HttpUtility::SendJsonError(response, request.Params(), 500, "Unhandled exception", DiagnosticInformation(ex));
HttpUtility::SendJsonError(response, request.Params(), 500, yc, "Unhandled exception", DiagnosticInformation(ex));
}
response.Flush(yc);

View file

@ -75,16 +75,8 @@ void HttpUtility::SendJsonBody(HttpApiResponse& response, const Dictionary::Ptr&
response.GetJsonEncoder(params && GetLastParameter(params, "pretty")).Encode(val, &yc);
}
void HttpUtility::SendJsonBody(HttpApiResponse& response, const Dictionary::Ptr& params, const Value& val)
{
namespace http = boost::beast::http;
response.set(http::field::content_type, "application/json");
response.GetJsonEncoder(params && GetLastParameter(params, "pretty")).Encode(val);
}
void HttpUtility::SendJsonError(HttpApiResponse& response,
const Dictionary::Ptr& params, int code, const String& info, const String& diagnosticInformation)
void HttpUtility::SendJsonError(HttpApiResponse& response, const Dictionary::Ptr& params,
int code, boost::asio::yield_context& yc, const String& info, const String& diagnosticInformation)
{
Dictionary::Ptr result = new Dictionary({ { "error", code } });
@ -99,7 +91,7 @@ void HttpUtility::SendJsonError(HttpApiResponse& response,
response.Clear();
response.result(code);
HttpUtility::SendJsonBody(response, params, result);
HttpUtility::SendJsonBody(response, params, result, yc);
}
/**

View file

@ -26,9 +26,8 @@ public:
static Value GetLastParameter(const Dictionary::Ptr& params, const String& key);
static void SendJsonBody(HttpApiResponse& response, const Dictionary::Ptr& params, const Value& val, boost::asio::yield_context& yc);
static void SendJsonBody(HttpApiResponse& response, const Dictionary::Ptr& params, const Value& val);
static void SendJsonError(HttpApiResponse& response, const Dictionary::Ptr& params, const int code,
const String& info = {}, const String& diagnosticInformation = {});
boost::asio::yield_context& yc, const String& info = {}, const String& diagnosticInformation = {});
static bool IsValidHeaderName(std::string_view name);
static bool IsValidHeaderValue(std::string_view value);

View file

@ -13,7 +13,7 @@ bool InfoHandler::HandleRequest(
const WaitGroup::Ptr&,
const HttpApiRequest& request,
HttpApiResponse& response,
boost::asio::yield_context&
boost::asio::yield_context& yc
)
{
namespace http = boost::beast::http;
@ -73,7 +73,7 @@ bool InfoHandler::HandleRequest(
{ "results", new Array({ result1 }) }
});
HttpUtility::SendJsonBody(response, params, result);
HttpUtility::SendJsonBody(response, params, result, yc);
} else {
response.set(http::field::content_type, "text/html");

View file

@ -22,7 +22,7 @@ bool MallocInfoHandler::HandleRequest(
const WaitGroup::Ptr&,
const HttpApiRequest& request,
HttpApiResponse& response,
boost::asio::yield_context&
boost::asio::yield_context& yc
)
{
namespace http = boost::beast::http;
@ -42,7 +42,7 @@ bool MallocInfoHandler::HandleRequest(
FilterUtility::CheckPermission(user, "debug");
#ifndef HAVE_MALLOC_INFO
HttpUtility::SendJsonError(response, params, 501, "malloc_info(3) not available.");
HttpUtility::SendJsonError(response, params, 501, yc, "malloc_info(3) not available.");
#else /* HAVE_MALLOC_INFO */
char* buf = nullptr;
size_t bufSize = 0;

View file

@ -36,7 +36,7 @@ bool ModifyObjectHandler::HandleRequest(
Type::Ptr type = FilterUtility::TypeFromPluralName(url->GetPath()[2]);
if (!type) {
HttpUtility::SendJsonError(response, params, 400, "Invalid type specified.");
HttpUtility::SendJsonError(response, params, 400, yc, "Invalid type specified.");
return true;
}
@ -57,7 +57,7 @@ bool ModifyObjectHandler::HandleRequest(
try {
objs = FilterUtility::GetFilterTargets(qd, params, user);
} catch (const std::exception& ex) {
HttpUtility::SendJsonError(response, params, 404,
HttpUtility::SendJsonError(response, params, 404, yc,
"No objects found.",
DiagnosticInformation(ex));
return true;
@ -66,7 +66,7 @@ bool ModifyObjectHandler::HandleRequest(
Value attrsVal = params->Get("attrs");
if (attrsVal.GetReflectionType() != Dictionary::TypeInstance && attrsVal.GetType() != ValueEmpty) {
HttpUtility::SendJsonError(response, params, 400,
HttpUtility::SendJsonError(response, params, 400, yc,
"Invalid type for 'attrs' attribute specified. Dictionary type is required."
"Or is this a POST query and you missed adding a 'X-HTTP-Method-Override: GET' header?");
return true;
@ -77,7 +77,7 @@ bool ModifyObjectHandler::HandleRequest(
Value restoreAttrsVal = params->Get("restore_attrs");
if (restoreAttrsVal.GetReflectionType() != Array::TypeInstance && restoreAttrsVal.GetType() != ValueEmpty) {
HttpUtility::SendJsonError(response, params, 400,
HttpUtility::SendJsonError(response, params, 400, yc,
"Invalid type for 'restore_attrs' attribute specified. Array type is required.");
return true;
}
@ -85,7 +85,7 @@ bool ModifyObjectHandler::HandleRequest(
Array::Ptr restoreAttrs = restoreAttrsVal;
if (!(attrs || restoreAttrs)) {
HttpUtility::SendJsonError(response, params, 400,
HttpUtility::SendJsonError(response, params, 400, yc,
"Missing both 'attrs' and 'restore_attrs'. "
"Or is this a POST query and you missed adding a 'X-HTTP-Method-Override: GET' header?");
return true;
@ -99,7 +99,7 @@ bool ModifyObjectHandler::HandleRequest(
ConfigObjectsSharedLock lock (std::try_to_lock);
if (!lock) {
HttpUtility::SendJsonError(response, params, 503, "Icinga is reloading");
HttpUtility::SendJsonError(response, params, 503, yc, "Icinga is reloading");
return true;
}
@ -107,7 +107,7 @@ bool ModifyObjectHandler::HandleRequest(
std::shared_lock wgLock{*waitGroup, std::try_to_lock};
if (!wgLock) {
HttpUtility::SendJsonError(response, params, 503, "Shutting down.");
HttpUtility::SendJsonError(response, params, 503, yc, "Shutting down.");
return true;
}

View file

@ -114,7 +114,7 @@ bool ObjectQueryHandler::HandleRequest(
Type::Ptr type = FilterUtility::TypeFromPluralName(url->GetPath()[2]);
if (!type) {
HttpUtility::SendJsonError(response, params, 400, "Invalid type specified.");
HttpUtility::SendJsonError(response, params, 400, yc, "Invalid type specified.");
return true;
}
@ -127,7 +127,7 @@ bool ObjectQueryHandler::HandleRequest(
try {
uattrs = params->Get("attrs");
} catch (const std::exception&) {
HttpUtility::SendJsonError(response, params, 400,
HttpUtility::SendJsonError(response, params, 400, yc,
"Invalid type for 'attrs' attribute specified. Array type is required.");
return true;
}
@ -135,7 +135,7 @@ bool ObjectQueryHandler::HandleRequest(
try {
ujoins = params->Get("joins");
} catch (const std::exception&) {
HttpUtility::SendJsonError(response, params, 400,
HttpUtility::SendJsonError(response, params, 400, yc,
"Invalid type for 'joins' attribute specified. Array type is required.");
return true;
}
@ -143,7 +143,7 @@ bool ObjectQueryHandler::HandleRequest(
try {
umetas = params->Get("meta");
} catch (const std::exception&) {
HttpUtility::SendJsonError(response, params, 400,
HttpUtility::SendJsonError(response, params, 400, yc,
"Invalid type for 'meta' attribute specified. Array type is required.");
return true;
}
@ -158,7 +158,7 @@ bool ObjectQueryHandler::HandleRequest(
} else if (meta == "location") {
includeLocation = true;
} else {
HttpUtility::SendJsonError(response, params, 400, "Invalid field specified for meta: " + meta);
HttpUtility::SendJsonError(response, params, 400, yc, "Invalid field specified for meta: " + meta);
return true;
}
}
@ -179,7 +179,7 @@ bool ObjectQueryHandler::HandleRequest(
try {
objs = FilterUtility::GetFilterTargets(qd, params, user);
} catch (const std::exception& ex) {
HttpUtility::SendJsonError(response, params, 404,
HttpUtility::SendJsonError(response, params, 404, yc,
"No objects found.",
DiagnosticInformation(ex));
return true;

View file

@ -73,7 +73,7 @@ bool StatusHandler::HandleRequest(
const WaitGroup::Ptr&,
const HttpApiRequest& request,
HttpApiResponse& response,
boost::asio::yield_context&
boost::asio::yield_context& yc
)
{
namespace http = boost::beast::http;
@ -103,7 +103,7 @@ bool StatusHandler::HandleRequest(
try {
objs = FilterUtility::GetFilterTargets(qd, params, user);
} catch (const std::exception& ex) {
HttpUtility::SendJsonError(response, params, 404,
HttpUtility::SendJsonError(response, params, 404, yc,
"No objects found.",
DiagnosticInformation(ex));
return true;
@ -114,7 +114,7 @@ bool StatusHandler::HandleRequest(
});
response.result(http::status::ok);
HttpUtility::SendJsonBody(response, params, result);
HttpUtility::SendJsonBody(response, params, result, yc);
return true;
}

View file

@ -98,7 +98,7 @@ bool TemplateQueryHandler::HandleRequest(
Type::Ptr type = FilterUtility::TypeFromPluralName(url->GetPath()[2]);
if (!type) {
HttpUtility::SendJsonError(response, params, 400, "Invalid type specified.");
HttpUtility::SendJsonError(response, params, 400, yc, "Invalid type specified.");
return true;
}
@ -120,7 +120,7 @@ bool TemplateQueryHandler::HandleRequest(
try {
objs = FilterUtility::GetFilterTargets(qd, params, user, "tmpl");
} catch (const std::exception& ex) {
HttpUtility::SendJsonError(response, params, 404,
HttpUtility::SendJsonError(response, params, 404, yc,
"No templates found.",
DiagnosticInformation(ex));
return true;

View file

@ -82,7 +82,7 @@ bool TypeQueryHandler::HandleRequest(
try {
objs = FilterUtility::GetFilterTargets(qd, params, user);
} catch (const std::exception& ex) {
HttpUtility::SendJsonError(response, params, 404,
HttpUtility::SendJsonError(response, params, 404, yc,
"No objects found.",
DiagnosticInformation(ex));
return true;

View file

@ -92,7 +92,7 @@ bool VariableQueryHandler::HandleRequest(
try {
objs = FilterUtility::GetFilterTargets(qd, params, user, "variable");
} catch (const std::exception& ex) {
HttpUtility::SendJsonError(response, params, 404,
HttpUtility::SendJsonError(response, params, 404, yc,
"No variables found.",
DiagnosticInformation(ex));
return true;

View file

@ -267,7 +267,7 @@ BOOST_AUTO_TEST_CASE(response_sendjsonbody)
HttpApiResponse response(server);
response.result(http::status::ok);
HttpUtility::SendJsonBody(response, nullptr, new Dictionary{{"test", 1}});
HttpUtility::SendJsonBody(response, nullptr, new Dictionary{{"test", 1}}, yc);
BOOST_REQUIRE_NO_THROW(response.Flush(yc));
@ -285,7 +285,7 @@ BOOST_AUTO_TEST_CASE(response_sendjsonbody)
BOOST_REQUIRE(!ec);
BOOST_REQUIRE_EQUAL(parser.get().result(), http::status::ok);
BOOST_REQUIRE_EQUAL(parser.get().chunked(), false);
BOOST_REQUIRE_EQUAL(parser.get().chunked(), true);
Dictionary::Ptr body = JsonDecode(parser.get().body());
BOOST_REQUIRE_EQUAL(body->Get("test"), 1);
}
@ -298,7 +298,7 @@ BOOST_AUTO_TEST_CASE(response_sendjsonerror)
// This has to be overwritten in SendJsonError.
response.result(http::status::ok);
HttpUtility::SendJsonError(response, nullptr, 404, "Not found.");
HttpUtility::SendJsonError(response, nullptr, 404, yc, "Not found.");
BOOST_REQUIRE_NO_THROW(response.Flush(yc));
@ -316,7 +316,7 @@ BOOST_AUTO_TEST_CASE(response_sendjsonerror)
BOOST_REQUIRE(!ec);
BOOST_REQUIRE_EQUAL(parser.get().result(), http::status::not_found);
BOOST_REQUIRE_EQUAL(parser.get().chunked(), false);
BOOST_REQUIRE_EQUAL(parser.get().chunked(), true);
Dictionary::Ptr body = JsonDecode(parser.get().body());
BOOST_REQUIRE_EQUAL(body->Get("error"), 404);
BOOST_REQUIRE_EQUAL(body->Get("status"), "Not found.");