diff --git a/lib/remote/actionshandler.cpp b/lib/remote/actionshandler.cpp index 5ae5fdc80..47fdd9b3b 100644 --- a/lib/remote/actionshandler.cpp +++ b/lib/remote/actionshandler.cpp @@ -18,29 +18,26 @@ REGISTER_URLHANDLER("/v1/actions", ActionsHandler); bool ActionsHandler::HandleRequest( const WaitGroup::Ptr& waitGroup, AsioTlsStream& stream, - const ApiUser::Ptr& user, - boost::beast::http::request& request, - const Url::Ptr& url, - boost::beast::http::response& response, - const Dictionary::Ptr& params, + const HttpRequest& request, + HttpResponse& response, boost::asio::yield_context& yc, HttpServerConnection& server ) { namespace http = boost::beast::http; - if (url->GetPath().size() != 3) + if (request.Url()->GetPath().size() != 3) return false; if (request.method() != http::verb::post) return false; - String actionName = url->GetPath()[2]; + String actionName = request.Url()->GetPath()[2]; ApiAction::Ptr action = ApiAction::GetByName(actionName); if (!action) { - HttpUtility::SendJsonError(response, params, 404, "Action '" + actionName + "' does not exist."); + response.SendJsonError(request.Params(), 404, "Action '" + actionName + "' does not exist."); return true; } @@ -56,21 +53,21 @@ bool ActionsHandler::HandleRequest( qd.Permission = permission; try { - objs = FilterUtility::GetFilterTargets(qd, params, user); + objs = FilterUtility::GetFilterTargets(qd, request.Params(), request.User()); } catch (const std::exception& ex) { - HttpUtility::SendJsonError(response, params, 404, + response.SendJsonError(request.Params(), 404, "No objects found.", DiagnosticInformation(ex)); return true; } if (objs.empty()) { - HttpUtility::SendJsonError(response, params, 404, + response.SendJsonError(request.Params(), 404, "No objects found."); return true; } } else { - FilterUtility::CheckPermission(user, permission); + FilterUtility::CheckPermission(request.User(), permission); objs.emplace_back(nullptr); } @@ -79,19 +76,16 @@ bool ActionsHandler::HandleRequest( Log(LogNotice, "ApiActionHandler") << "Running action " << actionName; - bool verbose = false; + bool verbose = request.IsVerbose(); - ActionsHandler::AuthenticatedApiUser = user; + ActionsHandler::AuthenticatedApiUser = request.User(); Defer a ([]() { ActionsHandler::AuthenticatedApiUser = nullptr; }); - if (params) - verbose = HttpUtility::GetLastParameter(params, "verbose"); - std::shared_lock wgLock{*waitGroup, std::try_to_lock}; if (!wgLock) { - HttpUtility::SendJsonError(response, params, 503, "Shutting down."); + response.SendJsonError(request.Params(), 503, "Shutting down."); return true; } @@ -112,7 +106,7 @@ bool ActionsHandler::HandleRequest( } try { - results.emplace_back(action->Invoke(obj, params)); + results.emplace_back(action->Invoke(obj, request.Params())); } catch (const std::exception& ex) { Dictionary::Ptr fail = new Dictionary({ { "code", 500 }, @@ -161,7 +155,7 @@ bool ActionsHandler::HandleRequest( { "results", new Array(std::move(results)) } }); - HttpUtility::SendJsonBody(response, params, result); + response.SendJsonBody(result, request.IsPretty()); return true; } diff --git a/lib/remote/actionshandler.hpp b/lib/remote/actionshandler.hpp index fbf716797..83132eeec 100644 --- a/lib/remote/actionshandler.hpp +++ b/lib/remote/actionshandler.hpp @@ -18,11 +18,8 @@ public: bool HandleRequest( const WaitGroup::Ptr& waitGroup, AsioTlsStream& stream, - const ApiUser::Ptr& user, - boost::beast::http::request& request, - const Url::Ptr& url, - boost::beast::http::response& response, - const Dictionary::Ptr& params, + const HttpRequest& request, + HttpResponse& response, boost::asio::yield_context& yc, HttpServerConnection& server ) override; diff --git a/lib/remote/configfileshandler.cpp b/lib/remote/configfileshandler.cpp index 6c390e804..f91a48330 100644 --- a/lib/remote/configfileshandler.cpp +++ b/lib/remote/configfileshandler.cpp @@ -16,11 +16,8 @@ REGISTER_URLHANDLER("/v1/config/files", ConfigFilesHandler); bool ConfigFilesHandler::HandleRequest( const WaitGroup::Ptr&, AsioTlsStream& stream, - const ApiUser::Ptr& user, - boost::beast::http::request& request, - const Url::Ptr& url, - boost::beast::http::response& response, - const Dictionary::Ptr& params, + const HttpRequest& request, + HttpResponse& response, boost::asio::yield_context& yc, HttpServerConnection& server ) @@ -30,50 +27,50 @@ bool ConfigFilesHandler::HandleRequest( if (request.method() != http::verb::get) return false; - const std::vector& urlPath = url->GetPath(); + const std::vector& urlPath = request.Url()->GetPath(); if (urlPath.size() >= 4) - params->Set("package", urlPath[3]); + request.Params()->Set("package", urlPath[3]); if (urlPath.size() >= 5) - params->Set("stage", urlPath[4]); + request.Params()->Set("stage", urlPath[4]); if (urlPath.size() >= 6) { std::vector tmpPath(urlPath.begin() + 5, urlPath.end()); - params->Set("path", boost::algorithm::join(tmpPath, "/")); + request.Params()->Set("path", boost::algorithm::join(tmpPath, "/")); } 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'."); + response.SendJsonError(request.Params(), 400, "Invalid Accept header. Either remove the Accept header or set it to 'application/octet-stream'."); return true; } - FilterUtility::CheckPermission(user, "config/query"); + FilterUtility::CheckPermission(request.User(), "config/query"); - String packageName = HttpUtility::GetLastParameter(params, "package"); - String stageName = HttpUtility::GetLastParameter(params, "stage"); + String packageName = request.GetLastParameter("package"); + String stageName = request.GetLastParameter("stage"); if (!ConfigPackageUtility::ValidatePackageName(packageName)) { - HttpUtility::SendJsonError(response, params, 400, "Invalid package name."); + response.SendJsonError(request.Params(), 400, "Invalid package name."); return true; } if (!ConfigPackageUtility::ValidateStageName(stageName)) { - HttpUtility::SendJsonError(response, params, 400, "Invalid stage name."); + response.SendJsonError(request.Params(), 400, "Invalid stage name."); return true; } - String relativePath = HttpUtility::GetLastParameter(params, "path"); + String relativePath = request.GetLastParameter("path"); if (ConfigPackageUtility::ContainsDotDot(relativePath)) { - HttpUtility::SendJsonError(response, params, 400, "Path contains '..' (not allowed)."); + response.SendJsonError(request.Params(), 400, "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."); + response.SendJsonError(request.Params(), 404, "Path not found."); return true; } @@ -81,13 +78,11 @@ bool ConfigFilesHandler::HandleRequest( std::ifstream fp(path.CStr(), std::ifstream::in | std::ifstream::binary); fp.exceptions(std::ifstream::badbit); - String content((std::istreambuf_iterator(fp)), std::istreambuf_iterator()); response.result(http::status::ok); response.set(http::field::content_type, "application/octet-stream"); - response.body() = content; - response.content_length(response.body().size()); + response.body() << fp.rdbuf(); } catch (const std::exception& ex) { - HttpUtility::SendJsonError(response, params, 500, "Could not read file.", + response.SendJsonError(request.Params(), 500, "Could not read file.", DiagnosticInformation(ex)); } diff --git a/lib/remote/configfileshandler.hpp b/lib/remote/configfileshandler.hpp index a8826d8c1..0bb12488d 100644 --- a/lib/remote/configfileshandler.hpp +++ b/lib/remote/configfileshandler.hpp @@ -16,11 +16,8 @@ public: bool HandleRequest( const WaitGroup::Ptr& waitGroup, AsioTlsStream& stream, - const ApiUser::Ptr& user, - boost::beast::http::request& request, - const Url::Ptr& url, - boost::beast::http::response& response, - const Dictionary::Ptr& params, + const HttpRequest& request, + HttpResponse& response, boost::asio::yield_context& yc, HttpServerConnection& server ) override; diff --git a/lib/remote/configpackageshandler.cpp b/lib/remote/configpackageshandler.cpp index 7987092bc..619e88b8a 100644 --- a/lib/remote/configpackageshandler.cpp +++ b/lib/remote/configpackageshandler.cpp @@ -13,50 +13,41 @@ REGISTER_URLHANDLER("/v1/config/packages", ConfigPackagesHandler); bool ConfigPackagesHandler::HandleRequest( const WaitGroup::Ptr&, AsioTlsStream& stream, - const ApiUser::Ptr& user, - boost::beast::http::request& request, - const Url::Ptr& url, - boost::beast::http::response& response, - const Dictionary::Ptr& params, + const HttpRequest& request, + HttpResponse& response, boost::asio::yield_context& yc, HttpServerConnection& server ) { namespace http = boost::beast::http; - if (url->GetPath().size() > 4) + if (request.Url()->GetPath().size() > 4) return false; if (request.method() == http::verb::get) - HandleGet(user, request, url, response, params); + HandleGet(request, response); else if (request.method() == http::verb::post) - HandlePost(user, request, url, response, params); + HandlePost(request, response); else if (request.method() == http::verb::delete_) - HandleDelete(user, request, url, response, params); + HandleDelete(request, response); else return false; return true; } -void ConfigPackagesHandler::HandleGet( - const ApiUser::Ptr& user, - boost::beast::http::request& request, - const Url::Ptr& url, - boost::beast::http::response& response, - const Dictionary::Ptr& params -) +void ConfigPackagesHandler::HandleGet(const HttpRequest& request, HttpResponse& response) { namespace http = boost::beast::http; - FilterUtility::CheckPermission(user, "config/query"); + FilterUtility::CheckPermission(request.User(), "config/query"); std::vector packages; try { packages = ConfigPackageUtility::GetPackages(); } catch (const std::exception& ex) { - HttpUtility::SendJsonError(response, params, 500, "Could not retrieve packages.", + response.SendJsonError(request.Params(), 500, "Could not retrieve packages.", DiagnosticInformation(ex)); return; } @@ -86,28 +77,22 @@ void ConfigPackagesHandler::HandleGet( }); response.result(http::status::ok); - HttpUtility::SendJsonBody(response, params, result); + response.SendJsonBody(result, request.IsPretty()); } -void ConfigPackagesHandler::HandlePost( - const ApiUser::Ptr& user, - boost::beast::http::request& request, - const Url::Ptr& url, - boost::beast::http::response& response, - const Dictionary::Ptr& params -) +void ConfigPackagesHandler::HandlePost(const HttpRequest& request, HttpResponse& response) { namespace http = boost::beast::http; - FilterUtility::CheckPermission(user, "config/modify"); + FilterUtility::CheckPermission(request.User(), "config/modify"); - if (url->GetPath().size() >= 4) - params->Set("package", url->GetPath()[3]); + if (request.Url()->GetPath().size() >= 4) + request.Params()->Set("package", request.Url()->GetPath()[3]); - String packageName = HttpUtility::GetLastParameter(params, "package"); + String packageName = request.GetLastParameter("package"); if (!ConfigPackageUtility::ValidatePackageName(packageName)) { - HttpUtility::SendJsonError(response, params, 400, "Invalid package name '" + packageName + "'."); + response.SendJsonError(request.Params(), 400, "Invalid package name '" + packageName + "'."); return; } @@ -116,7 +101,7 @@ void ConfigPackagesHandler::HandlePost( ConfigPackageUtility::CreatePackage(packageName); } catch (const std::exception& ex) { - HttpUtility::SendJsonError(response, params, 500, "Could not create package '" + packageName + "'.", + response.SendJsonError(request.Params(), 500, "Could not create package '" + packageName + "'.", DiagnosticInformation(ex)); return; } @@ -132,35 +117,29 @@ void ConfigPackagesHandler::HandlePost( }); response.result(http::status::ok); - HttpUtility::SendJsonBody(response, params, result); + response.SendJsonBody(result, request.IsPretty()); } -void ConfigPackagesHandler::HandleDelete( - const ApiUser::Ptr& user, - boost::beast::http::request& request, - const Url::Ptr& url, - boost::beast::http::response& response, - const Dictionary::Ptr& params -) +void ConfigPackagesHandler::HandleDelete(const HttpRequest& request, HttpResponse& response) { namespace http = boost::beast::http; - FilterUtility::CheckPermission(user, "config/modify"); + FilterUtility::CheckPermission(request.User(), "config/modify"); - if (url->GetPath().size() >= 4) - params->Set("package", url->GetPath()[3]); + if (request.Url()->GetPath().size() >= 4) + request.Params()->Set("package", request.Url()->GetPath()[3]); - String packageName = HttpUtility::GetLastParameter(params, "package"); + String packageName = request.GetLastParameter("package"); if (!ConfigPackageUtility::ValidatePackageName(packageName)) { - HttpUtility::SendJsonError(response, params, 400, "Invalid package name '" + packageName + "'."); + response.SendJsonError(request.Params(), 400, "Invalid package name '" + packageName + "'."); return; } try { ConfigPackageUtility::DeletePackage(packageName); } catch (const std::exception& ex) { - HttpUtility::SendJsonError(response, params, 500, "Failed to delete package '" + packageName + "'.", + response.SendJsonError(request.Params(), 500, "Failed to delete package '" + packageName + "'.", DiagnosticInformation(ex)); return; } @@ -176,5 +155,5 @@ void ConfigPackagesHandler::HandleDelete( }); response.result(http::status::ok); - HttpUtility::SendJsonBody(response, params, result); + response.SendJsonBody(result, request.IsPretty()); } diff --git a/lib/remote/configpackageshandler.hpp b/lib/remote/configpackageshandler.hpp index 2bae0e265..95bcfacbc 100644 --- a/lib/remote/configpackageshandler.hpp +++ b/lib/remote/configpackageshandler.hpp @@ -16,37 +16,16 @@ public: bool HandleRequest( const WaitGroup::Ptr& waitGroup, AsioTlsStream& stream, - const ApiUser::Ptr& user, - boost::beast::http::request& request, - const Url::Ptr& url, - boost::beast::http::response& response, - const Dictionary::Ptr& params, + const HttpRequest& request, + HttpResponse& response, boost::asio::yield_context& yc, HttpServerConnection& server ) override; private: - void HandleGet( - const ApiUser::Ptr& user, - boost::beast::http::request& request, - const Url::Ptr& url, - boost::beast::http::response& response, - const Dictionary::Ptr& params - ); - void HandlePost( - const ApiUser::Ptr& user, - boost::beast::http::request& request, - const Url::Ptr& url, - boost::beast::http::response& response, - const Dictionary::Ptr& params - ); - void HandleDelete( - const ApiUser::Ptr& user, - boost::beast::http::request& request, - const Url::Ptr& url, - boost::beast::http::response& response, - const Dictionary::Ptr& params - ); + void HandleGet(const HttpRequest& request, HttpResponse& response); + 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 0dee5f25f..98baf1bb7 100644 --- a/lib/remote/configstageshandler.cpp +++ b/lib/remote/configstageshandler.cpp @@ -17,58 +17,49 @@ std::atomic ConfigStagesHandler::m_RunningPackageUpdates (false); bool ConfigStagesHandler::HandleRequest( const WaitGroup::Ptr&, AsioTlsStream& stream, - const ApiUser::Ptr& user, - boost::beast::http::request& request, - const Url::Ptr& url, - boost::beast::http::response& response, - const Dictionary::Ptr& params, + const HttpRequest& request, + HttpResponse& response, boost::asio::yield_context& yc, HttpServerConnection& server ) { namespace http = boost::beast::http; - if (url->GetPath().size() > 5) + if (request.Url()->GetPath().size() > 5) return false; if (request.method() == http::verb::get) - HandleGet(user, request, url, response, params); + HandleGet(request, response); else if (request.method() == http::verb::post) - HandlePost(user, request, url, response, params); + HandlePost(request, response); else if (request.method() == http::verb::delete_) - HandleDelete(user, request, url, response, params); + HandleDelete(request, response); else return false; return true; } -void ConfigStagesHandler::HandleGet( - const ApiUser::Ptr& user, - boost::beast::http::request& request, - const Url::Ptr& url, - boost::beast::http::response& response, - const Dictionary::Ptr& params -) +void ConfigStagesHandler::HandleGet(const HttpRequest& request, HttpResponse& response) { namespace http = boost::beast::http; - FilterUtility::CheckPermission(user, "config/query"); + FilterUtility::CheckPermission(request.User(), "config/query"); - if (url->GetPath().size() >= 4) - params->Set("package", url->GetPath()[3]); + if (request.Url()->GetPath().size() >= 4) + request.Params()->Set("package", request.Url()->GetPath()[3]); - if (url->GetPath().size() >= 5) - params->Set("stage", url->GetPath()[4]); + if (request.Url()->GetPath().size() >= 5) + request.Params()->Set("stage", request.Url()->GetPath()[4]); - String packageName = HttpUtility::GetLastParameter(params, "package"); - String stageName = HttpUtility::GetLastParameter(params, "stage"); + String packageName = request.GetLastParameter("package"); + String stageName = request.GetLastParameter("stage"); if (!ConfigPackageUtility::ValidatePackageName(packageName)) - return HttpUtility::SendJsonError(response, params, 400, "Invalid package name '" + packageName + "'."); + return response.SendJsonError(request.Params(), 400, "Invalid package name '" + packageName + "'."); if (!ConfigPackageUtility::ValidateStageName(stageName)) - return HttpUtility::SendJsonError(response, params, 400, "Invalid stage name '" + stageName + "'."); + return response.SendJsonError(request.Params(), 400, "Invalid stage name '" + stageName + "'."); ArrayData results; @@ -88,40 +79,34 @@ void ConfigStagesHandler::HandleGet( }); response.result(http::status::ok); - HttpUtility::SendJsonBody(response, params, result); + response.SendJsonBody(result, request.IsPretty()); } -void ConfigStagesHandler::HandlePost( - const ApiUser::Ptr& user, - boost::beast::http::request& request, - const Url::Ptr& url, - boost::beast::http::response& response, - const Dictionary::Ptr& params -) +void ConfigStagesHandler::HandlePost(const HttpRequest& request, HttpResponse& response) { namespace http = boost::beast::http; - FilterUtility::CheckPermission(user, "config/modify"); + FilterUtility::CheckPermission(request.User(), "config/modify"); - if (url->GetPath().size() >= 4) - params->Set("package", url->GetPath()[3]); + if (request.Url()->GetPath().size() >= 4) + request.Params()->Set("package", request.Url()->GetPath()[3]); - String packageName = HttpUtility::GetLastParameter(params, "package"); + String packageName = request.GetLastParameter("package"); if (!ConfigPackageUtility::ValidatePackageName(packageName)) - return HttpUtility::SendJsonError(response, params, 400, "Invalid package name '" + packageName + "'."); + return response.SendJsonError(request.Params(), 400, "Invalid package name '" + packageName + "'."); bool reload = true; - if (params->Contains("reload")) - reload = HttpUtility::GetLastParameter(params, "reload"); + if (request.Params()->Contains("reload")) + reload = request.GetLastParameter("reload"); bool activate = true; - if (params->Contains("activate")) - activate = HttpUtility::GetLastParameter(params, "activate"); + if (request.Params()->Contains("activate")) + activate = request.GetLastParameter("activate"); - Dictionary::Ptr files = params->Get("files"); + Dictionary::Ptr files = request.Params()->Get("files"); String stageName; @@ -133,7 +118,7 @@ void ConfigStagesHandler::HandlePost( BOOST_THROW_EXCEPTION(std::invalid_argument("Parameter 'reload' must be false when 'activate' is false.")); if (m_RunningPackageUpdates.exchange(true)) { - return HttpUtility::SendJsonError(response, params, 423, + return response.SendJsonError(request.Params(), 423, "Conflicting request, there is already an ongoing package update in progress. Please try it again later."); } @@ -146,7 +131,7 @@ void ConfigStagesHandler::HandlePost( /* 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 response.SendJsonError(request.Params(), 500, "Stage creation failed.", DiagnosticInformation(ex)); } @@ -171,40 +156,34 @@ void ConfigStagesHandler::HandlePost( }); response.result(http::status::ok); - HttpUtility::SendJsonBody(response, params, result); + response.SendJsonBody(result, request.IsPretty()); } -void ConfigStagesHandler::HandleDelete( - const ApiUser::Ptr& user, - boost::beast::http::request& request, - const Url::Ptr& url, - boost::beast::http::response& response, - const Dictionary::Ptr& params -) +void ConfigStagesHandler::HandleDelete(const HttpRequest& request, HttpResponse& response) { namespace http = boost::beast::http; - FilterUtility::CheckPermission(user, "config/modify"); + FilterUtility::CheckPermission(request.User(), "config/modify"); - if (url->GetPath().size() >= 4) - params->Set("package", url->GetPath()[3]); + if (request.Url()->GetPath().size() >= 4) + request.Params()->Set("package", request.Url()->GetPath()[3]); - if (url->GetPath().size() >= 5) - params->Set("stage", url->GetPath()[4]); + if (request.Url()->GetPath().size() >= 5) + request.Params()->Set("stage", request.Url()->GetPath()[4]); - String packageName = HttpUtility::GetLastParameter(params, "package"); - String stageName = HttpUtility::GetLastParameter(params, "stage"); + String packageName = request.GetLastParameter("package"); + String stageName = request.GetLastParameter("stage"); if (!ConfigPackageUtility::ValidatePackageName(packageName)) - return HttpUtility::SendJsonError(response, params, 400, "Invalid package name '" + packageName + "'."); + return response.SendJsonError(request.Params(), 400, "Invalid package name '" + packageName + "'."); if (!ConfigPackageUtility::ValidateStageName(stageName)) - return HttpUtility::SendJsonError(response, params, 400, "Invalid stage name '" + stageName + "'."); + return response.SendJsonError(request.Params(), 400, "Invalid stage name '" + stageName + "'."); try { ConfigPackageUtility::DeleteStage(packageName, stageName); } catch (const std::exception& ex) { - return HttpUtility::SendJsonError(response, params, 500, + return response.SendJsonError(request.Params(), 500, "Failed to delete stage '" + stageName + "' in package '" + packageName + "'.", DiagnosticInformation(ex)); } @@ -221,5 +200,5 @@ void ConfigStagesHandler::HandleDelete( }); response.result(http::status::ok); - HttpUtility::SendJsonBody(response, params, result); + response.SendJsonBody(result, request.IsPretty()); } diff --git a/lib/remote/configstageshandler.hpp b/lib/remote/configstageshandler.hpp index a26ddc49c..6dd644f05 100644 --- a/lib/remote/configstageshandler.hpp +++ b/lib/remote/configstageshandler.hpp @@ -17,37 +17,16 @@ public: bool HandleRequest( const WaitGroup::Ptr& waitGroup, AsioTlsStream& stream, - const ApiUser::Ptr& user, - boost::beast::http::request& request, - const Url::Ptr& url, - boost::beast::http::response& response, - const Dictionary::Ptr& params, + const HttpRequest& request, + HttpResponse& response, boost::asio::yield_context& yc, HttpServerConnection& server ) override; private: - void HandleGet( - const ApiUser::Ptr& user, - boost::beast::http::request& request, - const Url::Ptr& url, - boost::beast::http::response& response, - const Dictionary::Ptr& params - ); - void HandlePost( - const ApiUser::Ptr& user, - boost::beast::http::request& request, - const Url::Ptr& url, - boost::beast::http::response& response, - const Dictionary::Ptr& params - ); - void HandleDelete( - const ApiUser::Ptr& user, - boost::beast::http::request& request, - const Url::Ptr& url, - boost::beast::http::response& response, - const Dictionary::Ptr& params - ); + void HandleGet(const HttpRequest& request, HttpResponse& response); + void HandlePost(const HttpRequest& request, HttpResponse& response); + void HandleDelete(const HttpRequest& request, HttpResponse& response); static std::atomic m_RunningPackageUpdates; }; diff --git a/lib/remote/consolehandler.cpp b/lib/remote/consolehandler.cpp index c48821aae..6c4abcb25 100644 --- a/lib/remote/consolehandler.cpp +++ b/lib/remote/consolehandler.cpp @@ -56,18 +56,15 @@ static void EnsureFrameCleanupTimer() bool ConsoleHandler::HandleRequest( const WaitGroup::Ptr&, AsioTlsStream& stream, - const ApiUser::Ptr& user, - boost::beast::http::request& request, - const Url::Ptr& url, - boost::beast::http::response& response, - const Dictionary::Ptr& params, + const HttpRequest& request, + HttpResponse& response, boost::asio::yield_context& yc, HttpServerConnection& server ) { namespace http = boost::beast::http; - if (url->GetPath().size() != 3) + if (request.Url()->GetPath().size() != 3) return false; if (request.method() != http::verb::post) @@ -75,38 +72,37 @@ bool ConsoleHandler::HandleRequest( QueryDescription qd; - String methodName = url->GetPath()[2]; + String methodName = request.Url()->GetPath()[2]; - FilterUtility::CheckPermission(user, "console"); + FilterUtility::CheckPermission(request.User(), "console"); - String session = HttpUtility::GetLastParameter(params, "session"); + String session = request.GetLastParameter("session"); if (session.IsEmpty()) session = Utility::NewUniqueID(); - String command = HttpUtility::GetLastParameter(params, "command"); + String command = request.GetLastParameter("command"); - bool sandboxed = HttpUtility::GetLastParameter(params, "sandboxed"); + bool sandboxed = request.GetLastParameter("sandboxed"); ConfigObjectsSharedLock lock (std::try_to_lock); if (!lock) { - HttpUtility::SendJsonError(response, params, 503, "Icinga is reloading."); + response.SendJsonError(request.Params(), 503, "Icinga is reloading."); return true; } if (methodName == "execute-script") - return ExecuteScriptHelper(request, response, params, command, session, sandboxed); + return ExecuteScriptHelper(request, response, command, session, sandboxed); else if (methodName == "auto-complete-script") - return AutocompleteScriptHelper(request, response, params, command, session, sandboxed); + return AutocompleteScriptHelper(request, response, command, session, sandboxed); - HttpUtility::SendJsonError(response, params, 400, "Invalid method specified: " + methodName); + response.SendJsonError(request.Params(), 400, "Invalid method specified: " + methodName); return true; } -bool ConsoleHandler::ExecuteScriptHelper(boost::beast::http::request& request, - boost::beast::http::response& response, - const Dictionary::Ptr& params, const String& command, const String& session, bool sandboxed) +bool ConsoleHandler::ExecuteScriptHelper(const HttpRequest& request, HttpResponse& response, + const String& command, const String& session, bool sandboxed) { namespace http = boost::beast::http; @@ -174,14 +170,13 @@ bool ConsoleHandler::ExecuteScriptHelper(boost::beast::http::request& request, - boost::beast::http::response& response, - const Dictionary::Ptr& params, const String& command, const String& session, bool sandboxed) +bool ConsoleHandler::AutocompleteScriptHelper(const HttpRequest& request, HttpResponse& response, + const String& command, const String& session, bool sandboxed) { namespace http = boost::beast::http; @@ -213,7 +208,7 @@ bool ConsoleHandler::AutocompleteScriptHelper(boost::beast::http::request& request, - const Url::Ptr& url, - boost::beast::http::response& response, - const Dictionary::Ptr& params, + const HttpRequest& request, + HttpResponse& response, boost::asio::yield_context& yc, HttpServerConnection& server ) override; @@ -37,12 +34,10 @@ public: static std::vector GetAutocompletionSuggestions(const String& word, ScriptFrame& frame); private: - static bool ExecuteScriptHelper(boost::beast::http::request& request, - boost::beast::http::response& response, - const Dictionary::Ptr& params, const String& command, const String& session, bool sandboxed); - static bool AutocompleteScriptHelper(boost::beast::http::request& request, - boost::beast::http::response& response, - const Dictionary::Ptr& params, const String& command, const String& session, bool sandboxed); + static bool ExecuteScriptHelper(const HttpRequest& request, HttpResponse& response, + const String& command, const String& session, bool sandboxed); + static bool AutocompleteScriptHelper(const HttpRequest& request, HttpResponse& response, + const String& command, const String& session, bool sandboxed); }; diff --git a/lib/remote/createobjecthandler.cpp b/lib/remote/createobjecthandler.cpp index 119be1cd9..b12e2bf91 100644 --- a/lib/remote/createobjecthandler.cpp +++ b/lib/remote/createobjecthandler.cpp @@ -18,35 +18,32 @@ REGISTER_URLHANDLER("/v1/objects", CreateObjectHandler); bool CreateObjectHandler::HandleRequest( const WaitGroup::Ptr& waitGroup, AsioTlsStream& stream, - const ApiUser::Ptr& user, - boost::beast::http::request& request, - const Url::Ptr& url, - boost::beast::http::response& response, - const Dictionary::Ptr& params, + const HttpRequest& request, + HttpResponse& response, boost::asio::yield_context& yc, HttpServerConnection& server ) { namespace http = boost::beast::http; - if (url->GetPath().size() != 4) + if (request.Url()->GetPath().size() != 4) return false; if (request.method() != http::verb::put) return false; - Type::Ptr type = FilterUtility::TypeFromPluralName(url->GetPath()[2]); + Type::Ptr type = FilterUtility::TypeFromPluralName(request.Url()->GetPath()[2]); if (!type) { - HttpUtility::SendJsonError(response, params, 400, "Invalid type specified."); + response.SendJsonError(request.Params(), 400, "Invalid type specified."); return true; } - FilterUtility::CheckPermission(user, "objects/create/" + type->GetName()); + FilterUtility::CheckPermission(request.User(), "objects/create/" + type->GetName()); - String name = url->GetPath()[3]; - Array::Ptr templates = params->Get("templates"); - Dictionary::Ptr attrs = params->Get("attrs"); + String name = request.Url()->GetPath()[3]; + Array::Ptr templates = request.Params()->Get("templates"); + Dictionary::Ptr attrs = request.Params()->Get("attrs"); /* Put created objects into the local zone if not explicitly defined. * This allows additional zone members to sync the @@ -82,8 +79,8 @@ bool CreateObjectHandler::HandleRequest( bool ignoreOnError = false; - if (params->Contains("ignore_on_error")) - ignoreOnError = HttpUtility::GetLastParameter(params, "ignore_on_error"); + if (request.Params()->Contains("ignore_on_error")) + ignoreOnError = request.GetLastParameter("ignore_on_error"); Dictionary::Ptr result = new Dictionary({ { "results", new Array({ result1 }) } @@ -91,21 +88,18 @@ bool CreateObjectHandler::HandleRequest( String config; - bool verbose = false; - - if (params) - verbose = HttpUtility::GetLastParameter(params, "verbose"); + bool verbose = request.IsVerbose(); ConfigObjectsSharedLock lock (std::try_to_lock); if (!lock) { - HttpUtility::SendJsonError(response, params, 503, "Icinga is reloading"); + response.SendJsonError(request.Params(), 503, "Icinga is reloading"); return true; } std::shared_lock wgLock{*waitGroup, std::try_to_lock}; if (!wgLock) { - HttpUtility::SendJsonError(response, params, 503, "Shutting down."); + response.SendJsonError(request.Params(), 503, "Shutting down."); return true; } @@ -126,7 +120,7 @@ bool CreateObjectHandler::HandleRequest( result1->Set("status", "Object could not be created."); response.result(http::status::internal_server_error); - HttpUtility::SendJsonBody(response, params, result); + response.SendJsonBody(result, request.IsPretty()); return true; } @@ -143,7 +137,7 @@ bool CreateObjectHandler::HandleRequest( result1->Set("diagnostic_information", diagnosticInformation); response.result(http::status::internal_server_error); - HttpUtility::SendJsonBody(response, params, result); + response.SendJsonBody(result, request.IsPretty()); return true; } @@ -159,7 +153,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); + response.SendJsonBody(result, request.IsPretty()); return true; } diff --git a/lib/remote/createobjecthandler.hpp b/lib/remote/createobjecthandler.hpp index 3f6a705c2..317cf023c 100644 --- a/lib/remote/createobjecthandler.hpp +++ b/lib/remote/createobjecthandler.hpp @@ -16,11 +16,8 @@ public: bool HandleRequest( const WaitGroup::Ptr& waitGroup, AsioTlsStream& stream, - const ApiUser::Ptr& user, - boost::beast::http::request& request, - const Url::Ptr& url, - boost::beast::http::response& response, - const Dictionary::Ptr& params, + const HttpRequest& request, + HttpResponse& response, boost::asio::yield_context& yc, HttpServerConnection& server ) override; diff --git a/lib/remote/deleteobjecthandler.cpp b/lib/remote/deleteobjecthandler.cpp index 54d31f13d..253a73f16 100644 --- a/lib/remote/deleteobjecthandler.cpp +++ b/lib/remote/deleteobjecthandler.cpp @@ -18,27 +18,24 @@ REGISTER_URLHANDLER("/v1/objects", DeleteObjectHandler); bool DeleteObjectHandler::HandleRequest( const WaitGroup::Ptr& waitGroup, AsioTlsStream& stream, - const ApiUser::Ptr& user, - boost::beast::http::request& request, - const Url::Ptr& url, - boost::beast::http::response& response, - const Dictionary::Ptr& params, + const HttpRequest& request, + HttpResponse& response, boost::asio::yield_context& yc, HttpServerConnection& server ) { namespace http = boost::beast::http; - if (url->GetPath().size() < 3 || url->GetPath().size() > 4) + if (request.Url()->GetPath().size() < 3 || request.Url()->GetPath().size() > 4) return false; if (request.method() != http::verb::delete_) return false; - Type::Ptr type = FilterUtility::TypeFromPluralName(url->GetPath()[2]); + Type::Ptr type = FilterUtility::TypeFromPluralName(request.Url()->GetPath()[2]); if (!type) { - HttpUtility::SendJsonError(response, params, 400, "Invalid type specified."); + response.SendJsonError(request.Params(), 400, "Invalid type specified."); return true; } @@ -46,32 +43,32 @@ bool DeleteObjectHandler::HandleRequest( qd.Types.insert(type->GetName()); qd.Permission = "objects/delete/" + type->GetName(); - params->Set("type", type->GetName()); + request.Params()->Set("type", type->GetName()); - if (url->GetPath().size() >= 4) { + if (request.Url()->GetPath().size() >= 4) { String attr = type->GetName(); boost::algorithm::to_lower(attr); - params->Set(attr, url->GetPath()[3]); + request.Params()->Set(attr, request.Url()->GetPath()[3]); } std::vector objs; try { - objs = FilterUtility::GetFilterTargets(qd, params, user); + objs = FilterUtility::GetFilterTargets(qd, request.Params(), request.User()); } catch (const std::exception& ex) { - HttpUtility::SendJsonError(response, params, 404, + response.SendJsonError(request.Params(), 404, "No objects found.", DiagnosticInformation(ex)); return true; } - bool cascade = HttpUtility::GetLastParameter(params, "cascade"); - bool verbose = HttpUtility::GetLastParameter(params, "verbose"); + bool cascade = request.GetLastParameter("cascade"); + bool verbose = request.IsVerbose(); ConfigObjectsSharedLock lock (std::try_to_lock); if (!lock) { - HttpUtility::SendJsonError(response, params, 503, "Icinga is reloading"); + response.SendJsonError(request.Params(), 503, "Icinga is reloading"); return true; } @@ -81,7 +78,7 @@ bool DeleteObjectHandler::HandleRequest( std::shared_lock wgLock{*waitGroup, std::try_to_lock}; if (!wgLock) { - HttpUtility::SendJsonError(response, params, 503, "Shutting down."); + response.SendJsonError(request.Params(), 503, "Shutting down."); return true; } @@ -143,7 +140,7 @@ bool DeleteObjectHandler::HandleRequest( else response.result(http::status::ok); - HttpUtility::SendJsonBody(response, params, result); + response.SendJsonBody(result, request.IsPretty()); return true; } diff --git a/lib/remote/deleteobjecthandler.hpp b/lib/remote/deleteobjecthandler.hpp index 0f9643277..076f76704 100644 --- a/lib/remote/deleteobjecthandler.hpp +++ b/lib/remote/deleteobjecthandler.hpp @@ -16,11 +16,8 @@ public: bool HandleRequest( const WaitGroup::Ptr& waitGroup, AsioTlsStream& stream, - const ApiUser::Ptr& user, - boost::beast::http::request& request, - const Url::Ptr& url, - boost::beast::http::response& response, - const Dictionary::Ptr& params, + const HttpRequest& request, + HttpResponse& response, boost::asio::yield_context& yc, HttpServerConnection& server ) override; diff --git a/lib/remote/eventshandler.cpp b/lib/remote/eventshandler.cpp index 2cbee92f3..56fce0037 100644 --- a/lib/remote/eventshandler.cpp +++ b/lib/remote/eventshandler.cpp @@ -42,11 +42,8 @@ const String l_ApiQuery (""); bool EventsHandler::HandleRequest( const WaitGroup::Ptr&, AsioTlsStream& stream, - const ApiUser::Ptr& user, - boost::beast::http::request& request, - const Url::Ptr& url, - boost::beast::http::response& response, - const Dictionary::Ptr& params, + const HttpRequest& request, + HttpResponse& response, boost::asio::yield_context& yc, HttpServerConnection& server ) @@ -54,35 +51,35 @@ bool EventsHandler::HandleRequest( namespace asio = boost::asio; namespace http = boost::beast::http; - if (url->GetPath().size() != 2) + if (request.Url()->GetPath().size() != 2) return false; if (request.method() != http::verb::post) return false; if (request.version() == 10) { - HttpUtility::SendJsonError(response, params, 400, "HTTP/1.0 not supported for event streams."); + response.SendJsonError(request.Params(), 400, "HTTP/1.0 not supported for event streams."); return true; } - Array::Ptr types = params->Get("types"); + Array::Ptr types = request.Params()->Get("types"); if (!types) { - HttpUtility::SendJsonError(response, params, 400, "'types' query parameter is required."); + response.SendJsonError(request.Params(), 400, "'types' query parameter is required."); return true; } { ObjectLock olock(types); for (String type : types) { - FilterUtility::CheckPermission(user, "events/" + type); + FilterUtility::CheckPermission(request.User(), "events/" + type); } } - String queueName = HttpUtility::GetLastParameter(params, "queue"); + String queueName = request.GetLastParameter("queue"); if (queueName.IsEmpty()) { - HttpUtility::SendJsonError(response, params, 400, "'queue' query parameter is required."); + response.SendJsonError(request.Params(), 400, "'queue' query parameter is required."); return true; } @@ -99,7 +96,7 @@ bool EventsHandler::HandleRequest( } } - EventsSubscriber subscriber (std::move(eventTypes), HttpUtility::GetLastParameter(params, "filter"), l_ApiQuery); + EventsSubscriber subscriber (std::move(eventTypes), request.GetLastParameter("filter"), l_ApiQuery); server.StartStreaming(); diff --git a/lib/remote/eventshandler.hpp b/lib/remote/eventshandler.hpp index 49229733a..68a1f9844 100644 --- a/lib/remote/eventshandler.hpp +++ b/lib/remote/eventshandler.hpp @@ -17,11 +17,8 @@ public: bool HandleRequest( const WaitGroup::Ptr& waitGroup, AsioTlsStream& stream, - const ApiUser::Ptr& user, - boost::beast::http::request& request, - const Url::Ptr& url, - boost::beast::http::response& response, - const Dictionary::Ptr& params, + const HttpRequest& request, + HttpResponse& response, boost::asio::yield_context& yc, HttpServerConnection& server ) override; diff --git a/lib/remote/httphandler.cpp b/lib/remote/httphandler.cpp index 79571d760..5292123a5 100644 --- a/lib/remote/httphandler.cpp +++ b/lib/remote/httphandler.cpp @@ -49,9 +49,8 @@ void HttpHandler::Register(const Url::Ptr& url, const HttpHandler::Ptr& handler) void HttpHandler::ProcessRequest( const WaitGroup::Ptr& waitGroup, AsioTlsStream& stream, - const ApiUser::Ptr& user, - boost::beast::http::request& request, - boost::beast::http::response& response, + HttpRequest& request, + HttpResponse& response, boost::asio::yield_context& yc, HttpServerConnection& server ) @@ -59,8 +58,8 @@ void HttpHandler::ProcessRequest( Dictionary::Ptr node = m_UrlTree; std::vector handlers; - Url::Ptr url = new Url(std::string(request.target())); - auto& path (url->GetPath()); + request.DecodeUrl(); + auto& path (request.Url()->GetPath()); for (std::vector::size_type i = 0; i <= path.size(); i++) { Array::Ptr current_handlers = node->Get("handlers"); @@ -90,12 +89,10 @@ void HttpHandler::ProcessRequest( std::reverse(handlers.begin(), handlers.end()); - Dictionary::Ptr params; - try { - params = HttpUtility::FetchRequestParameters(url, request.body()); + request.DecodeParams(); } catch (const std::exception& ex) { - HttpUtility::SendJsonError(response, params, 400, "Invalid request body: " + DiagnosticInformation(ex, false)); + response.SendJsonError(nullptr, 400, "Invalid request body: " + DiagnosticInformation(ex, false)); return; } @@ -109,12 +106,20 @@ void HttpHandler::ProcessRequest( */ try { for (const HttpHandler::Ptr& handler : handlers) { - if (handler->HandleRequest(waitGroup, stream, user, request, url, response, params, yc, server)) { + if (handler->HandleRequest(waitGroup, stream, request, response, yc, server)) { processed = true; break; } } } catch (const std::exception& ex) { + /* This means we can't send an error response because the exception was thrown + * in the middle of a streaming response. We can't send any error response, so the + * only thing we can do is propagate it up. + */ + if (response.HasSerializationStarted()) { + throw; + } + Log(LogWarning, "HttpServerConnection") << "Error while processing HTTP request: " << ex.what(); @@ -122,7 +127,7 @@ void HttpHandler::ProcessRequest( } if (!processed) { - HttpUtility::SendJsonError(response, params, 404, "The requested path '" + boost::algorithm::join(path, "/") + + response.SendJsonError(request.Params(), 404, "The requested path '" + boost::algorithm::join(path, "/") + "' could not be found or the request method is not valid for this path."); return; } diff --git a/lib/remote/httphandler.hpp b/lib/remote/httphandler.hpp index ec67ae8a4..0d6bd12b8 100644 --- a/lib/remote/httphandler.hpp +++ b/lib/remote/httphandler.hpp @@ -4,8 +4,10 @@ #define HTTPHANDLER_H #include "remote/i2-remote.hpp" +#include "base/io-engine.hpp" #include "remote/url.hpp" #include "remote/httpserverconnection.hpp" +#include "remote/httpmessage.hpp" #include "remote/apiuser.hpp" #include "base/registry.hpp" #include "base/tlsstream.hpp" @@ -29,11 +31,8 @@ public: virtual bool HandleRequest( const WaitGroup::Ptr& waitGroup, AsioTlsStream& stream, - const ApiUser::Ptr& user, - boost::beast::http::request& request, - const Url::Ptr& url, - boost::beast::http::response& response, - const Dictionary::Ptr& params, + const HttpRequest& request, + HttpResponse& response, boost::asio::yield_context& yc, HttpServerConnection& server ) = 0; @@ -42,9 +41,8 @@ public: static void ProcessRequest( const WaitGroup::Ptr& waitGroup, AsioTlsStream& stream, - const ApiUser::Ptr& user, - boost::beast::http::request& request, - boost::beast::http::response& response, + HttpRequest& request, + HttpResponse& response, boost::asio::yield_context& yc, HttpServerConnection& server ); diff --git a/lib/remote/httpserverconnection.cpp b/lib/remote/httpserverconnection.cpp index 17e61f160..617515822 100644 --- a/lib/remote/httpserverconnection.cpp +++ b/lib/remote/httpserverconnection.cpp @@ -129,10 +129,9 @@ bool HttpServerConnection::Disconnected() static inline bool EnsureValidHeaders( - AsioTlsStream& stream, boost::beast::flat_buffer& buf, - boost::beast::http::parser& parser, - boost::beast::http::response& response, + HttpRequest& request, + HttpResponse& response, bool& shuttingDown, boost::asio::yield_context& yc ) @@ -147,7 +146,7 @@ bool EnsureValidHeaders( boost::system::error_code ec; - http::async_read_header(stream, buf, parser, yc[ec]); + request.ParseHeader(buf, yc[ec]); if (ec) { if (ec == boost::asio::error::operation_aborted) @@ -156,7 +155,7 @@ bool EnsureValidHeaders( errorMsg = ec.message(); httpError = true; } else { - switch (parser.get().version()) { + switch (request.version()) { case 10: case 11: break; @@ -168,21 +167,16 @@ bool EnsureValidHeaders( if (!errorMsg.IsEmpty() || httpError) { response.result(http::status::bad_request); - if (!httpError && parser.get()[http::field::accept] == "application/json") { - HttpUtility::SendJsonBody(response, nullptr, new Dictionary({ - { "error", 400 }, - { "status", String("Bad Request: ") + errorMsg } - })); + if (!httpError && request[http::field::accept] == "application/json") { + response.SendJsonError(400, "Bad Request: " + errorMsg); } else { response.set(http::field::content_type, "text/html"); - response.body() = String("

Bad Request

") + errorMsg + "

"; - response.content_length(response.body().size()); + response.body() << "

Bad Request

" << errorMsg << "

\r\n"; } response.set(http::field::connection, "close"); - http::async_write(stream, response, yc); - stream.async_flush(yc); + response.Flush(yc); return false; } @@ -192,28 +186,24 @@ bool EnsureValidHeaders( static inline void HandleExpect100( - AsioTlsStream& stream, - boost::beast::http::request& request, + const Shared::Ptr& stream, + const HttpRequest& request, boost::asio::yield_context& yc ) { namespace http = boost::beast::http; if (request[http::field::expect] == "100-continue") { - http::response response; - + HttpResponse response{stream}; response.result(http::status::continue_); - - http::async_write(stream, response, yc); - stream.async_flush(yc); + response.Flush(yc); } } static inline bool HandleAccessControl( - AsioTlsStream& stream, - boost::beast::http::request& request, - boost::beast::http::response& response, + const HttpRequest& request, + HttpResponse& response, boost::asio::yield_context& yc ) { @@ -240,12 +230,10 @@ bool HandleAccessControl( response.result(http::status::ok); response.set(http::field::access_control_allow_methods, "GET, POST, PUT, DELETE"); response.set(http::field::access_control_allow_headers, "Authorization, Content-Type, X-HTTP-Method-Override"); - response.body() = "Preflight OK"; - response.content_length(response.body().size()); + response.body() << "Preflight OK"; response.set(http::field::connection, "close"); - http::async_write(stream, response, yc); - stream.async_flush(yc); + response.Flush(yc); return false; } @@ -258,9 +246,8 @@ bool HandleAccessControl( static inline bool EnsureAcceptHeader( - AsioTlsStream& stream, - boost::beast::http::request& request, - boost::beast::http::response& response, + const HttpRequest& request, + HttpResponse& response, boost::asio::yield_context& yc ) { @@ -269,12 +256,10 @@ bool EnsureAcceptHeader( if (request.method() != http::verb::get && request[http::field::accept] != "application/json") { response.result(http::status::bad_request); response.set(http::field::content_type, "text/html"); - response.body() = "

Accept header is missing or not set to 'application/json'.

"; - response.content_length(response.body().size()); + response.body() << "

Accept header is missing or not set to 'application/json'.

\r\n"; response.set(http::field::connection, "close"); - http::async_write(stream, response, yc); - stream.async_flush(yc); + response.Flush(yc); return false; } @@ -284,16 +269,14 @@ bool EnsureAcceptHeader( static inline bool EnsureAuthenticatedUser( - AsioTlsStream& stream, - boost::beast::http::request& request, - ApiUser::Ptr& authenticatedUser, - boost::beast::http::response& response, + const HttpRequest& request, + HttpResponse& response, boost::asio::yield_context& yc ) { namespace http = boost::beast::http; - if (!authenticatedUser) { + if (!request.User()) { Log(LogWarning, "HttpServerConnection") << "Unauthorized request: " << request.method_string() << ' ' << request.target(); @@ -302,18 +285,13 @@ bool EnsureAuthenticatedUser( response.set(http::field::connection, "close"); if (request[http::field::accept] == "application/json") { - HttpUtility::SendJsonBody(response, nullptr, new Dictionary({ - { "error", 401 }, - { "status", "Unauthorized. Please check your user credentials." } - })); + response.SendJsonError(401, "Unauthorized. Please check your user credentials."); } else { response.set(http::field::content_type, "text/html"); - response.body() = "

Unauthorized. Please check your user credentials.

"; - response.content_length(response.body().size()); + response.body() << "

Unauthorized. Please check your user credentials.

\r\n"; } - http::async_write(stream, response, yc); - stream.async_flush(yc); + response.Flush(yc); return false; } @@ -323,11 +301,9 @@ bool EnsureAuthenticatedUser( static inline bool EnsureValidBody( - AsioTlsStream& stream, boost::beast::flat_buffer& buf, - boost::beast::http::parser& parser, - ApiUser::Ptr& authenticatedUser, - boost::beast::http::response& response, + HttpRequest& request, + HttpResponse& response, bool& shuttingDown, boost::asio::yield_context& yc ) @@ -336,7 +312,7 @@ bool EnsureValidBody( { size_t maxSize = 1024 * 1024; - Array::Ptr permissions = authenticatedUser->GetPermissions(); + Array::Ptr permissions = request.User()->GetPermissions(); if (permissions) { ObjectLock olock(permissions); @@ -366,7 +342,7 @@ bool EnsureValidBody( } } - parser.body_limit(maxSize); + request.Parser().body_limit(maxSize); } if (shuttingDown) @@ -374,7 +350,7 @@ bool EnsureValidBody( boost::system::error_code ec; - http::async_read(stream, buf, parser, yc[ec]); + request.ParseBody(buf, yc[ec]); if (ec) { if (ec == boost::asio::error::operation_aborted) @@ -389,21 +365,16 @@ bool EnsureValidBody( response.result(http::status::bad_request); - if (parser.get()[http::field::accept] == "application/json") { - HttpUtility::SendJsonBody(response, nullptr, new Dictionary({ - { "error", 400 }, - { "status", String("Bad Request: ") + ec.message() } - })); + if (request[http::field::accept] == "application/json") { + response.SendJsonError(400, "Bad Request: " + ec.message()); } else { response.set(http::field::content_type, "text/html"); - response.body() = String("

Bad Request

") + ec.message() + "

"; - response.content_length(response.body().size()); + response.body() << "

Bad Request

" << ec.message() << "

\r\n"; } response.set(http::field::connection, "close"); - http::async_write(stream, response, yc); - stream.async_flush(yc); + response.Flush(yc); return false; } @@ -414,9 +385,8 @@ bool EnsureValidBody( static inline bool ProcessRequest( AsioTlsStream& stream, - boost::beast::http::request& request, - ApiUser::Ptr& authenticatedUser, - boost::beast::http::response& response, + HttpRequest& request, + HttpResponse& response, HttpServerConnection& server, bool& hasStartedStreaming, const WaitGroup::Ptr& waitGroup, @@ -424,42 +394,29 @@ bool ProcessRequest( boost::asio::yield_context& yc ) { - namespace http = boost::beast::http; - try { // Cache the elapsed time to acquire a CPU semaphore used to detect extremely heavy workloads. auto start (std::chrono::steady_clock::now()); CpuBoundWork handlingRequest (yc); cpuBoundWorkTime = std::chrono::steady_clock::now() - start; - HttpHandler::ProcessRequest(waitGroup, stream, authenticatedUser, request, response, yc, server); + HttpHandler::ProcessRequest(waitGroup, stream, request, response, yc, server); } catch (const std::exception& ex) { - if (hasStartedStreaming) { - return false; - } - - auto sysErr (dynamic_cast(&ex)); - - if (sysErr && sysErr->code() == boost::asio::error::operation_aborted) { + /* This means we can't do anything with the connection anymore, so we can't send + * an error response. And since we don't know the state the stream is in, we have to + * just cause a disconnect here. + */ + if (response.HasSerializationStarted()) { throw; } - http::response response; - - HttpUtility::SendJsonError(response, nullptr, 500, "Unhandled exception" , DiagnosticInformation(ex)); - - http::async_write(stream, response, yc); - stream.async_flush(yc); - - return true; - } - - if (hasStartedStreaming) { + response.SendJsonError(request.Params(), 500, "Unhandled exception", DiagnosticInformation(ex)); + response.Flush(yc); return false; } - http::async_write(stream, response, yc); - stream.async_flush(yc); + response.body().Finish(); + response.Flush(yc); return true; } @@ -481,23 +438,21 @@ void HttpServerConnection::ProcessMessages(boost::asio::yield_context yc) while (m_WaitGroup->IsLockable()) { m_Seen = Utility::GetTime(); - http::parser parser; - http::response response; + HttpRequest request(m_Stream); + HttpResponse response(m_Stream); - parser.header_limit(1024 * 1024); - parser.body_limit(-1); + request.Parser().header_limit(1024 * 1024); + request.Parser().body_limit(-1); response.set(http::field::server, l_ServerHeader); - if (!EnsureValidHeaders(*m_Stream, buf, parser, response, m_ShuttingDown, yc)) { + if (!EnsureValidHeaders(buf, request, response, m_ShuttingDown, yc)) { break; } m_Seen = Utility::GetTime(); auto start (ch::steady_clock::now()); - auto& request (parser.get()); - { auto method (http::string_to_verb(request["X-Http-Method-Override"])); @@ -506,19 +461,20 @@ void HttpServerConnection::ProcessMessages(boost::asio::yield_context yc) } } - HandleExpect100(*m_Stream, request, yc); + HandleExpect100(m_Stream, request, yc); - auto authenticatedUser (m_ApiUser); - - if (!authenticatedUser) { - authenticatedUser = ApiUser::GetByAuthHeader(std::string(request[http::field::authorization])); + if (m_ApiUser) { + request.User(m_ApiUser); + } else { + auto user = ApiUser::GetByAuthHeader(std::string(request[http::field::authorization])); + request.User(user); } Log logMsg (LogInformation, "HttpServerConnection"); logMsg << "Request " << request.method_string() << ' ' << request.target() << " (from " << m_PeerAddress - << ", user: " << (authenticatedUser ? authenticatedUser->GetName() : "") + << ", user: " << (request.User() ? request.User()->GetName() : "") << ", agent: " << request[http::field::user_agent]; //operator[] - Returns the value for a field, or "" if it does not exist. ch::steady_clock::duration cpuBoundWorkTime(0); @@ -531,29 +487,29 @@ void HttpServerConnection::ProcessMessages(boost::asio::yield_context yc) logMsg << " took total " << ch::duration_cast(ch::steady_clock::now() - start).count() << "ms."; }); - if (!HandleAccessControl(*m_Stream, request, response, yc)) { + if (!HandleAccessControl(request, response, yc)) { break; } - if (!EnsureAcceptHeader(*m_Stream, request, response, yc)) { + if (!EnsureAcceptHeader(request, response, yc)) { break; } - if (!EnsureAuthenticatedUser(*m_Stream, request, authenticatedUser, response, yc)) { + if (!EnsureAuthenticatedUser(request, response, yc)) { break; } - if (!EnsureValidBody(*m_Stream, buf, parser, authenticatedUser, response, m_ShuttingDown, yc)) { + if (!EnsureValidBody(buf, request, response, m_ShuttingDown, yc)) { break; } m_Seen = std::numeric_limits::max(); - if (!ProcessRequest(*m_Stream, request, authenticatedUser, response, *this, m_HasStartedStreaming, m_WaitGroup, cpuBoundWorkTime, yc)) { + if (!ProcessRequest(*m_Stream, request, response, *this, m_HasStartedStreaming, m_WaitGroup, cpuBoundWorkTime, yc)) { break; } - if (request.version() != 11 || request[http::field::connection] == "close") { + if (!request.keep_alive()) { break; } } diff --git a/lib/remote/httputility.cpp b/lib/remote/httputility.cpp index a2142e5d8..a1a0a855a 100644 --- a/lib/remote/httputility.cpp +++ b/lib/remote/httputility.cpp @@ -3,40 +3,10 @@ #include "remote/httputility.hpp" #include "remote/url.hpp" #include "base/json.hpp" -#include "base/logger.hpp" -#include -#include -#include #include using namespace icinga; -Dictionary::Ptr HttpUtility::FetchRequestParameters(const Url::Ptr& url, const std::string& body) -{ - Dictionary::Ptr result; - - if (!body.empty()) { - Log(LogDebug, "HttpUtility") - << "Request body: '" << body << '\''; - - result = JsonDecode(body); - } - - if (!result) - result = new Dictionary(); - - std::map> query; - for (const auto& kv : url->GetQuery()) { - query[kv.first].emplace_back(kv.second); - } - - for (auto& kv : query) { - result->Set(kv.first, Array::FromVector(kv.second)); - } - - return result; -} - Value HttpUtility::GetLastParameter(const Dictionary::Ptr& params, const String& key) { Value varr = params->Get(key); @@ -51,30 +21,3 @@ Value HttpUtility::GetLastParameter(const Dictionary::Ptr& params, const String& else return arr->Get(arr->GetLength() - 1); } - -void HttpUtility::SendJsonBody(boost::beast::http::response& response, const Dictionary::Ptr& params, const Value& val) -{ - namespace http = boost::beast::http; - - response.set(http::field::content_type, "application/json"); - response.body() = JsonEncode(val, params && GetLastParameter(params, "pretty")); - response.content_length(response.body().size()); -} - -void HttpUtility::SendJsonError(boost::beast::http::response& response, - const Dictionary::Ptr& params, int code, const String& info, const String& diagnosticInformation) -{ - Dictionary::Ptr result = new Dictionary({ { "error", code } }); - - if (!info.IsEmpty()) { - result->Set("status", info); - } - - if (params && HttpUtility::GetLastParameter(params, "verbose") && !diagnosticInformation.IsEmpty()) { - result->Set("diagnostic_information", diagnosticInformation); - } - - response.result(code); - - HttpUtility::SendJsonBody(response, params, result); -} diff --git a/lib/remote/httputility.hpp b/lib/remote/httputility.hpp index 6465b4af9..8c5d04cd0 100644 --- a/lib/remote/httputility.hpp +++ b/lib/remote/httputility.hpp @@ -6,7 +6,6 @@ #include "remote/url.hpp" #include "base/dictionary.hpp" #include -#include namespace icinga { @@ -20,12 +19,7 @@ class HttpUtility { public: - static Dictionary::Ptr FetchRequestParameters(const Url::Ptr& url, const std::string& body); static Value GetLastParameter(const Dictionary::Ptr& params, const String& key); - - static void SendJsonBody(boost::beast::http::response& response, const Dictionary::Ptr& params, const Value& val); - static void SendJsonError(boost::beast::http::response& response, const Dictionary::Ptr& params, const int code, - const String& verbose = String(), const String& diagnosticInformation = String()); }; } diff --git a/lib/remote/infohandler.cpp b/lib/remote/infohandler.cpp index 5fc621cd8..2ffa6d4fd 100644 --- a/lib/remote/infohandler.cpp +++ b/lib/remote/infohandler.cpp @@ -11,36 +11,33 @@ REGISTER_URLHANDLER("/", InfoHandler); bool InfoHandler::HandleRequest( const WaitGroup::Ptr&, AsioTlsStream& stream, - const ApiUser::Ptr& user, - boost::beast::http::request& request, - const Url::Ptr& url, - boost::beast::http::response& response, - const Dictionary::Ptr& params, + const HttpRequest& request, + HttpResponse& response, boost::asio::yield_context& yc, HttpServerConnection& server ) { namespace http = boost::beast::http; - if (url->GetPath().size() > 2) + if (request.Url()->GetPath().size() > 2) return false; if (request.method() != http::verb::get) return false; - if (url->GetPath().empty()) { + if (request.Url()->GetPath().empty()) { response.result(http::status::found); response.set(http::field::location, "/v1"); return true; } - if (url->GetPath()[0] != "v1" || url->GetPath().size() != 1) + if (request.Url()->GetPath()[0] != "v1" || request.Url()->GetPath().size() != 1) return false; response.result(http::status::ok); std::vector permInfo; - Array::Ptr permissions = user->GetPermissions(); + Array::Ptr permissions = request.User()->GetPermissions(); if (permissions) { ObjectLock olock(permissions); @@ -63,7 +60,7 @@ bool InfoHandler::HandleRequest( if (request[http::field::accept] == "application/json") { Dictionary::Ptr result1 = new Dictionary({ - { "user", user->GetName() }, + { "user", request.User()->GetName() }, { "permissions", Array::FromVector(permInfo) }, { "version", Application::GetAppVersion() }, { "info", "More information about API requests is available in the documentation at https://icinga.com/docs/icinga2/latest/" } @@ -73,27 +70,27 @@ bool InfoHandler::HandleRequest( { "results", new Array({ result1 }) } }); - HttpUtility::SendJsonBody(response, params, result); + response.SendJsonBody(result, request.IsPretty()); } else { response.set(http::field::content_type, "text/html"); - String body = "Icinga 2

Hello from Icinga 2 (Version: " + Application::GetAppVersion() + ")!

"; - body += "

You are authenticated as " + user->GetName() + ". "; + auto & body = response.body(); + body << "Icinga 2

Hello from Icinga 2 (Version: " + << Application::GetAppVersion() << ")!

"; + body << "

You are authenticated as " << request.User()->GetName() << ". "; if (!permInfo.empty()) { - body += "Your user has the following permissions:

    "; + body << "Your user has the following permissions:

      "; for (const String& perm : permInfo) { - body += "
    • " + perm + "
    • "; + body << "
    • " << perm << "
    • "; } - body += "
    "; + body << "
"; } else - body += "Your user does not have any permissions.

"; + body << "Your user does not have any permissions.

"; - body += R"(

More information about API requests is available in the documentation.

)"; - response.body() = body; - response.content_length(response.body().size()); + body << R"(

More information about API requests is available in the documentation.

)"; } return true; diff --git a/lib/remote/infohandler.hpp b/lib/remote/infohandler.hpp index 7396f5ac9..f0f6499a3 100644 --- a/lib/remote/infohandler.hpp +++ b/lib/remote/infohandler.hpp @@ -16,11 +16,8 @@ public: bool HandleRequest( const WaitGroup::Ptr& waitGroup, AsioTlsStream& stream, - const ApiUser::Ptr& user, - boost::beast::http::request& request, - const Url::Ptr& url, - boost::beast::http::response& response, - const Dictionary::Ptr& params, + const HttpRequest& request, + HttpResponse& response, boost::asio::yield_context& yc, HttpServerConnection& server ) override; diff --git a/lib/remote/mallocinfohandler.cpp b/lib/remote/mallocinfohandler.cpp index f4c27cac4..b2ecf7c74 100644 --- a/lib/remote/mallocinfohandler.cpp +++ b/lib/remote/mallocinfohandler.cpp @@ -20,18 +20,15 @@ REGISTER_URLHANDLER("/v1/debug/malloc_info", MallocInfoHandler); bool MallocInfoHandler::HandleRequest( const WaitGroup::Ptr&, AsioTlsStream&, - const ApiUser::Ptr& user, - boost::beast::http::request& request, - const Url::Ptr& url, - boost::beast::http::response& response, - const Dictionary::Ptr& params, + const HttpRequest& request, + HttpResponse& response, boost::asio::yield_context&, HttpServerConnection& ) { namespace http = boost::beast::http; - if (url->GetPath().size() != 3) { + if (request.Url()->GetPath().size() != 3) { return false; } @@ -39,10 +36,10 @@ bool MallocInfoHandler::HandleRequest( return false; } - FilterUtility::CheckPermission(user, "debug"); + FilterUtility::CheckPermission(request.User(), "debug"); #ifndef HAVE_MALLOC_INFO - HttpUtility::SendJsonError(response, params, 501, "malloc_info(3) not available."); + response.SendJsonError(request.Params(), 501, "malloc_info(3) not available."); #else /* HAVE_MALLOC_INFO */ char* buf = nullptr; size_t bufSize = 0; @@ -87,8 +84,7 @@ bool MallocInfoHandler::HandleRequest( response.result(200); response.set(http::field::content_type, "application/xml"); - response.body() = std::string(buf, bufSize); - response.content_length(response.body().size()); + response.body() << std::string_view(buf, bufSize); #endif /* HAVE_MALLOC_INFO */ return true; diff --git a/lib/remote/mallocinfohandler.hpp b/lib/remote/mallocinfohandler.hpp index 9648fac9f..fc32341fa 100644 --- a/lib/remote/mallocinfohandler.hpp +++ b/lib/remote/mallocinfohandler.hpp @@ -15,11 +15,8 @@ public: bool HandleRequest( const WaitGroup::Ptr& waitGroup, AsioTlsStream& stream, - const ApiUser::Ptr& user, - boost::beast::http::request& request, - const Url::Ptr& url, - boost::beast::http::response& response, - const Dictionary::Ptr& params, + const HttpRequest& request, + HttpResponse& response, boost::asio::yield_context& yc, HttpServerConnection& server ) override; diff --git a/lib/remote/modifyobjecthandler.cpp b/lib/remote/modifyobjecthandler.cpp index c71be6a9a..fa26d293e 100644 --- a/lib/remote/modifyobjecthandler.cpp +++ b/lib/remote/modifyobjecthandler.cpp @@ -16,27 +16,24 @@ REGISTER_URLHANDLER("/v1/objects", ModifyObjectHandler); bool ModifyObjectHandler::HandleRequest( const WaitGroup::Ptr& waitGroup, AsioTlsStream& stream, - const ApiUser::Ptr& user, - boost::beast::http::request& request, - const Url::Ptr& url, - boost::beast::http::response& response, - const Dictionary::Ptr& params, + const HttpRequest& request, + HttpResponse& response, boost::asio::yield_context& yc, HttpServerConnection& server ) { namespace http = boost::beast::http; - if (url->GetPath().size() < 3 || url->GetPath().size() > 4) + if (request.Url()->GetPath().size() < 3 || request.Url()->GetPath().size() > 4) return false; if (request.method() != http::verb::post) return false; - Type::Ptr type = FilterUtility::TypeFromPluralName(url->GetPath()[2]); + Type::Ptr type = FilterUtility::TypeFromPluralName(request.Url()->GetPath()[2]); if (!type) { - HttpUtility::SendJsonError(response, params, 400, "Invalid type specified."); + response.SendJsonError(request.Params(), 400, "Invalid type specified."); return true; } @@ -44,29 +41,29 @@ bool ModifyObjectHandler::HandleRequest( qd.Types.insert(type->GetName()); qd.Permission = "objects/modify/" + type->GetName(); - params->Set("type", type->GetName()); + request.Params()->Set("type", type->GetName()); - if (url->GetPath().size() >= 4) { + if (request.Url()->GetPath().size() >= 4) { String attr = type->GetName(); boost::algorithm::to_lower(attr); - params->Set(attr, url->GetPath()[3]); + request.Params()->Set(attr, request.Url()->GetPath()[3]); } std::vector objs; try { - objs = FilterUtility::GetFilterTargets(qd, params, user); + objs = FilterUtility::GetFilterTargets(qd, request.Params(), request.User()); } catch (const std::exception& ex) { - HttpUtility::SendJsonError(response, params, 404, + response.SendJsonError(request.Params(), 404, "No objects found.", DiagnosticInformation(ex)); return true; } - Value attrsVal = params->Get("attrs"); + Value attrsVal = request.Params()->Get("attrs"); if (attrsVal.GetReflectionType() != Dictionary::TypeInstance && attrsVal.GetType() != ValueEmpty) { - HttpUtility::SendJsonError(response, params, 400, + response.SendJsonError(request.Params(), 400, "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; @@ -74,10 +71,10 @@ bool ModifyObjectHandler::HandleRequest( Dictionary::Ptr attrs = attrsVal; - Value restoreAttrsVal = params->Get("restore_attrs"); + Value restoreAttrsVal = request.Params()->Get("restore_attrs"); if (restoreAttrsVal.GetReflectionType() != Array::TypeInstance && restoreAttrsVal.GetType() != ValueEmpty) { - HttpUtility::SendJsonError(response, params, 400, + response.SendJsonError(request.Params(), 400, "Invalid type for 'restore_attrs' attribute specified. Array type is required."); return true; } @@ -85,21 +82,18 @@ bool ModifyObjectHandler::HandleRequest( Array::Ptr restoreAttrs = restoreAttrsVal; if (!(attrs || restoreAttrs)) { - HttpUtility::SendJsonError(response, params, 400, + response.SendJsonError(request.Params(), 400, "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; } - bool verbose = false; - - if (params) - verbose = HttpUtility::GetLastParameter(params, "verbose"); + bool verbose = request.IsVerbose(); ConfigObjectsSharedLock lock (std::try_to_lock); if (!lock) { - HttpUtility::SendJsonError(response, params, 503, "Icinga is reloading"); + response.SendJsonError(request.Params(), 503, "Icinga is reloading"); return true; } @@ -107,7 +101,7 @@ bool ModifyObjectHandler::HandleRequest( std::shared_lock wgLock{*waitGroup, std::try_to_lock}; if (!wgLock) { - HttpUtility::SendJsonError(response, params, 503, "Shutting down."); + response.SendJsonError(request.Params(), 503, "Shutting down."); return true; } @@ -185,7 +179,7 @@ bool ModifyObjectHandler::HandleRequest( }); response.result(http::status::ok); - HttpUtility::SendJsonBody(response, params, result); + response.SendJsonBody(result, request.IsPretty()); return true; } diff --git a/lib/remote/modifyobjecthandler.hpp b/lib/remote/modifyobjecthandler.hpp index f299acd6e..32ddf176c 100644 --- a/lib/remote/modifyobjecthandler.hpp +++ b/lib/remote/modifyobjecthandler.hpp @@ -16,11 +16,8 @@ public: bool HandleRequest( const WaitGroup::Ptr& waitGroup, AsioTlsStream& stream, - const ApiUser::Ptr& user, - boost::beast::http::request& request, - const Url::Ptr& url, - boost::beast::http::response& response, - const Dictionary::Ptr& params, + const HttpRequest& request, + HttpResponse& response, boost::asio::yield_context& yc, HttpServerConnection& server ) override; diff --git a/lib/remote/objectqueryhandler.cpp b/lib/remote/objectqueryhandler.cpp index f6f049e4e..a8491385a 100644 --- a/lib/remote/objectqueryhandler.cpp +++ b/lib/remote/objectqueryhandler.cpp @@ -91,27 +91,24 @@ Dictionary::Ptr ObjectQueryHandler::SerializeObjectAttrs(const Object::Ptr& obje bool ObjectQueryHandler::HandleRequest( const WaitGroup::Ptr&, AsioTlsStream& stream, - const ApiUser::Ptr& user, - boost::beast::http::request& request, - const Url::Ptr& url, - boost::beast::http::response& response, - const Dictionary::Ptr& params, + const HttpRequest& request, + HttpResponse& response, boost::asio::yield_context& yc, HttpServerConnection& server ) { namespace http = boost::beast::http; - if (url->GetPath().size() < 3 || url->GetPath().size() > 4) + if (request.Url()->GetPath().size() < 3 || request.Url()->GetPath().size() > 4) return false; if (request.method() != http::verb::get) return false; - Type::Ptr type = FilterUtility::TypeFromPluralName(url->GetPath()[2]); + Type::Ptr type = FilterUtility::TypeFromPluralName(request.Url()->GetPath()[2]); if (!type) { - HttpUtility::SendJsonError(response, params, 400, "Invalid type specified."); + response.SendJsonError(request.Params(), 400, "Invalid type specified."); return true; } @@ -122,45 +119,45 @@ bool ObjectQueryHandler::HandleRequest( Array::Ptr uattrs, ujoins, umetas; try { - uattrs = params->Get("attrs"); + uattrs = request.Params()->Get("attrs"); } catch (const std::exception&) { - HttpUtility::SendJsonError(response, params, 400, + response.SendJsonError(request.Params(), 400, "Invalid type for 'attrs' attribute specified. Array type is required."); return true; } try { - ujoins = params->Get("joins"); + ujoins = request.Params()->Get("joins"); } catch (const std::exception&) { - HttpUtility::SendJsonError(response, params, 400, + response.SendJsonError(request.Params(), 400, "Invalid type for 'joins' attribute specified. Array type is required."); return true; } try { - umetas = params->Get("meta"); + umetas = request.Params()->Get("meta"); } catch (const std::exception&) { - HttpUtility::SendJsonError(response, params, 400, + response.SendJsonError(request.Params(), 400, "Invalid type for 'meta' attribute specified. Array type is required."); return true; } - bool allJoins = HttpUtility::GetLastParameter(params, "all_joins"); + bool allJoins = request.GetLastParameter("all_joins"); - params->Set("type", type->GetName()); + request.Params()->Set("type", type->GetName()); - if (url->GetPath().size() >= 4) { + if (request.Url()->GetPath().size() >= 4) { String attr = type->GetName(); boost::algorithm::to_lower(attr); - params->Set(attr, url->GetPath()[3]); + request.Params()->Set(attr, request.Url()->GetPath()[3]); } std::vector objs; try { - objs = FilterUtility::GetFilterTargets(qd, params, user); + objs = FilterUtility::GetFilterTargets(qd, request.Params(), request.User()); } catch (const std::exception& ex) { - HttpUtility::SendJsonError(response, params, 404, + response.SendJsonError(request.Params(), 404, "No objects found.", DiagnosticInformation(ex)); return true; @@ -218,7 +215,7 @@ bool ObjectQueryHandler::HandleRequest( } else if (meta == "location") { metaAttrs.emplace_back("location", obj->GetSourceLocation()); } else { - HttpUtility::SendJsonError(response, params, 400, "Invalid field specified for meta: " + meta); + response.SendJsonError(request.Params(), 400, "Invalid field specified for meta: " + meta); return true; } } @@ -229,7 +226,7 @@ bool ObjectQueryHandler::HandleRequest( try { result1.emplace_back("attrs", SerializeObjectAttrs(obj, String(), uattrs, false, false)); } catch (const ScriptError& ex) { - HttpUtility::SendJsonError(response, params, 400, ex.what()); + response.SendJsonError(request.Params(), 400, ex.what()); return true; } @@ -240,14 +237,14 @@ bool ObjectQueryHandler::HandleRequest( int fid = type->GetFieldId(joinAttr); if (fid < 0) { - HttpUtility::SendJsonError(response, params, 400, "Invalid field specified for join: " + joinAttr); + response.SendJsonError(request.Params(), 400, "Invalid field specified for join: " + joinAttr); return true; } Field field = type->GetFieldInfo(fid); if (!(field.Attributes & FANavigation)) { - HttpUtility::SendJsonError(response, params, 400, "Not a joinable field: " + joinAttr); + response.SendJsonError(request.Params(), 400, "Not a joinable field: " + joinAttr); return true; } @@ -264,7 +261,7 @@ bool ObjectQueryHandler::HandleRequest( String permission = "objects/query/" + reflectionType->GetName(); std::unique_ptr permissionFilter; - granted = FilterUtility::HasPermission(user, permission, &permissionFilter); + granted = FilterUtility::HasPermission(request.User(), permission, &permissionFilter); it = typePermissions.insert({reflectionType.get(), std::make_pair(granted, std::move(permissionFilter))}).first; } @@ -304,7 +301,7 @@ bool ObjectQueryHandler::HandleRequest( try { joins.emplace_back(prefix, SerializeObjectAttrs(joinedObj, prefix, ujoins, true, allJoins)); } catch (const ScriptError& ex) { - HttpUtility::SendJsonError(response, params, 400, ex.what()); + response.SendJsonError(request.Params(), 400, ex.what()); return true; } } @@ -319,7 +316,7 @@ bool ObjectQueryHandler::HandleRequest( }); response.result(http::status::ok); - HttpUtility::SendJsonBody(response, params, result); + response.SendJsonBody(request.Params(), result); return true; } diff --git a/lib/remote/objectqueryhandler.hpp b/lib/remote/objectqueryhandler.hpp index 376eb661e..d26a9e1ca 100644 --- a/lib/remote/objectqueryhandler.hpp +++ b/lib/remote/objectqueryhandler.hpp @@ -16,11 +16,8 @@ public: bool HandleRequest( const WaitGroup::Ptr& waitGroup, AsioTlsStream& stream, - const ApiUser::Ptr& user, - boost::beast::http::request& request, - const Url::Ptr& url, - boost::beast::http::response& response, - const Dictionary::Ptr& params, + const HttpRequest& request, + HttpResponse& response, boost::asio::yield_context& yc, HttpServerConnection& server ) override; diff --git a/lib/remote/statushandler.cpp b/lib/remote/statushandler.cpp index bf14152f8..b5fb54fb8 100644 --- a/lib/remote/statushandler.cpp +++ b/lib/remote/statushandler.cpp @@ -71,18 +71,15 @@ public: bool StatusHandler::HandleRequest( const WaitGroup::Ptr&, AsioTlsStream& stream, - const ApiUser::Ptr& user, - boost::beast::http::request& request, - const Url::Ptr& url, - boost::beast::http::response& response, - const Dictionary::Ptr& params, + const HttpRequest& request, + HttpResponse& response, boost::asio::yield_context& yc, HttpServerConnection& server ) { namespace http = boost::beast::http; - if (url->GetPath().size() > 3) + if (request.Url()->GetPath().size() > 3) return false; if (request.method() != http::verb::get) @@ -93,17 +90,17 @@ bool StatusHandler::HandleRequest( qd.Provider = new StatusTargetProvider(); qd.Permission = "status/query"; - params->Set("type", "Status"); + request.Params()->Set("type", "Status"); - if (url->GetPath().size() >= 3) - params->Set("status", url->GetPath()[2]); + if (request.Url()->GetPath().size() >= 3) + request.Params()->Set("status", request.Url()->GetPath()[2]); std::vector objs; try { - objs = FilterUtility::GetFilterTargets(qd, params, user); + objs = FilterUtility::GetFilterTargets(qd, request.Params(), request.User()); } catch (const std::exception& ex) { - HttpUtility::SendJsonError(response, params, 404, + response.SendJsonError(request.Params(), 404, "No objects found.", DiagnosticInformation(ex)); return true; @@ -114,7 +111,7 @@ bool StatusHandler::HandleRequest( }); response.result(http::status::ok); - HttpUtility::SendJsonBody(response, params, result); + response.SendJsonBody(result, request.IsPretty()); return true; } diff --git a/lib/remote/statushandler.hpp b/lib/remote/statushandler.hpp index 109fd4881..1d05347d1 100644 --- a/lib/remote/statushandler.hpp +++ b/lib/remote/statushandler.hpp @@ -16,11 +16,8 @@ public: bool HandleRequest( const WaitGroup::Ptr& waitGroup, AsioTlsStream& stream, - const ApiUser::Ptr& user, - boost::beast::http::request& request, - const Url::Ptr& url, - boost::beast::http::response& response, - const Dictionary::Ptr& params, + const HttpRequest& request, + HttpResponse& response, boost::asio::yield_context& yc, HttpServerConnection& server ) override; diff --git a/lib/remote/templatequeryhandler.cpp b/lib/remote/templatequeryhandler.cpp index a68ad6dad..b8f4799e9 100644 --- a/lib/remote/templatequeryhandler.cpp +++ b/lib/remote/templatequeryhandler.cpp @@ -78,27 +78,24 @@ public: bool TemplateQueryHandler::HandleRequest( const WaitGroup::Ptr&, AsioTlsStream& stream, - const ApiUser::Ptr& user, - boost::beast::http::request& request, - const Url::Ptr& url, - boost::beast::http::response& response, - const Dictionary::Ptr& params, + const HttpRequest& request, + HttpResponse& response, boost::asio::yield_context& yc, HttpServerConnection& server ) { namespace http = boost::beast::http; - if (url->GetPath().size() < 3 || url->GetPath().size() > 4) + if (request.Url()->GetPath().size() < 3 || request.Url()->GetPath().size() > 4) return false; if (request.method() != http::verb::get) return false; - Type::Ptr type = FilterUtility::TypeFromPluralName(url->GetPath()[2]); + Type::Ptr type = FilterUtility::TypeFromPluralName(request.Url()->GetPath()[2]); if (!type) { - HttpUtility::SendJsonError(response, params, 400, "Invalid type specified."); + response.SendJsonError(request.Params(), 400, "Invalid type specified."); return true; } @@ -107,20 +104,20 @@ bool TemplateQueryHandler::HandleRequest( qd.Permission = "templates/query/" + type->GetName(); qd.Provider = new TemplateTargetProvider(); - params->Set("type", type->GetName()); + request.Params()->Set("type", type->GetName()); - if (url->GetPath().size() >= 4) { + if (request.Url()->GetPath().size() >= 4) { String attr = type->GetName(); boost::algorithm::to_lower(attr); - params->Set(attr, url->GetPath()[3]); + request.Params()->Set(attr, request.Url()->GetPath()[3]); } std::vector objs; try { - objs = FilterUtility::GetFilterTargets(qd, params, user, "tmpl"); + objs = FilterUtility::GetFilterTargets(qd, request.Params(), request.User(), "tmpl"); } catch (const std::exception& ex) { - HttpUtility::SendJsonError(response, params, 404, + response.SendJsonError(request.Params(), 404, "No templates found.", DiagnosticInformation(ex)); return true; @@ -131,7 +128,7 @@ bool TemplateQueryHandler::HandleRequest( }); response.result(http::status::ok); - HttpUtility::SendJsonBody(response, params, result); + response.SendJsonBody(result, request.IsPretty()); return true; } diff --git a/lib/remote/templatequeryhandler.hpp b/lib/remote/templatequeryhandler.hpp index 312cf4221..c62670610 100644 --- a/lib/remote/templatequeryhandler.hpp +++ b/lib/remote/templatequeryhandler.hpp @@ -16,11 +16,8 @@ public: bool HandleRequest( const WaitGroup::Ptr& waitGroup, AsioTlsStream& stream, - const ApiUser::Ptr& user, - boost::beast::http::request& request, - const Url::Ptr& url, - boost::beast::http::response& response, - const Dictionary::Ptr& params, + const HttpRequest& request, + HttpResponse& response, boost::asio::yield_context& yc, HttpServerConnection& server ) override; diff --git a/lib/remote/typequeryhandler.cpp b/lib/remote/typequeryhandler.cpp index b2184344d..5914b4e84 100644 --- a/lib/remote/typequeryhandler.cpp +++ b/lib/remote/typequeryhandler.cpp @@ -49,18 +49,15 @@ public: bool TypeQueryHandler::HandleRequest( const WaitGroup::Ptr&, AsioTlsStream& stream, - const ApiUser::Ptr& user, - boost::beast::http::request& request, - const Url::Ptr& url, - boost::beast::http::response& response, - const Dictionary::Ptr& params, + const HttpRequest& request, + HttpResponse& response, boost::asio::yield_context& yc, HttpServerConnection& server ) { namespace http = boost::beast::http; - if (url->GetPath().size() > 3) + if (request.Url()->GetPath().size() > 3) return false; if (request.method() != http::verb::get) @@ -71,20 +68,20 @@ bool TypeQueryHandler::HandleRequest( qd.Permission = "types"; qd.Provider = new TypeTargetProvider(); - if (params->Contains("type")) - params->Set("name", params->Get("type")); + if (request.Params()->Contains("type")) + request.Params()->Set("name", request.Params()->Get("type")); - params->Set("type", "Type"); + request.Params()->Set("type", "Type"); - if (url->GetPath().size() >= 3) - params->Set("name", url->GetPath()[2]); + if (request.Url()->GetPath().size() >= 3) + request.Params()->Set("name", request.Url()->GetPath()[2]); std::vector objs; try { - objs = FilterUtility::GetFilterTargets(qd, params, user); + objs = FilterUtility::GetFilterTargets(qd, request.Params(), request.User()); } catch (const std::exception& ex) { - HttpUtility::SendJsonError(response, params, 404, + response.SendJsonError(request.Params(), 404, "No objects found.", DiagnosticInformation(ex)); return true; @@ -151,7 +148,7 @@ bool TypeQueryHandler::HandleRequest( }); response.result(http::status::ok); - HttpUtility::SendJsonBody(response, params, result); + response.SendJsonBody(result, request.IsPretty()); return true; } diff --git a/lib/remote/typequeryhandler.hpp b/lib/remote/typequeryhandler.hpp index 45cbc38ec..e0567249c 100644 --- a/lib/remote/typequeryhandler.hpp +++ b/lib/remote/typequeryhandler.hpp @@ -16,11 +16,8 @@ public: bool HandleRequest( const WaitGroup::Ptr& waitGroup, AsioTlsStream& stream, - const ApiUser::Ptr& user, - boost::beast::http::request& request, - const Url::Ptr& url, - boost::beast::http::response& response, - const Dictionary::Ptr& params, + const HttpRequest& request, + HttpResponse& response, boost::asio::yield_context& yc, HttpServerConnection& server ) override; diff --git a/lib/remote/variablequeryhandler.cpp b/lib/remote/variablequeryhandler.cpp index 40552dd7d..47bde1add 100644 --- a/lib/remote/variablequeryhandler.cpp +++ b/lib/remote/variablequeryhandler.cpp @@ -59,18 +59,15 @@ public: bool VariableQueryHandler::HandleRequest( const WaitGroup::Ptr&, AsioTlsStream& stream, - const ApiUser::Ptr& user, - boost::beast::http::request& request, - const Url::Ptr& url, - boost::beast::http::response& response, - const Dictionary::Ptr& params, + const HttpRequest& request, + HttpResponse& response, boost::asio::yield_context& yc, HttpServerConnection& server ) { namespace http = boost::beast::http; - if (url->GetPath().size() > 3) + if (request.Url()->GetPath().size() > 3) return false; if (request.method() != http::verb::get) @@ -81,17 +78,17 @@ bool VariableQueryHandler::HandleRequest( qd.Permission = "variables"; qd.Provider = new VariableTargetProvider(); - params->Set("type", "Variable"); + request.Params()->Set("type", "Variable"); - if (url->GetPath().size() >= 3) - params->Set("variable", url->GetPath()[2]); + if (request.Url()->GetPath().size() >= 3) + request.Params()->Set("variable", request.Url()->GetPath()[2]); std::vector objs; try { - objs = FilterUtility::GetFilterTargets(qd, params, user, "variable"); + objs = FilterUtility::GetFilterTargets(qd, request.Params(), request.User(), "variable"); } catch (const std::exception& ex) { - HttpUtility::SendJsonError(response, params, 404, + response.SendJsonError(request.Params(), 404, "No variables found.", DiagnosticInformation(ex)); return true; @@ -115,7 +112,7 @@ bool VariableQueryHandler::HandleRequest( }); response.result(http::status::ok); - HttpUtility::SendJsonBody(response, params, result); + response.SendJsonBody(result, request.IsPretty()); return true; } diff --git a/lib/remote/variablequeryhandler.hpp b/lib/remote/variablequeryhandler.hpp index d145f5b59..3b7a522ae 100644 --- a/lib/remote/variablequeryhandler.hpp +++ b/lib/remote/variablequeryhandler.hpp @@ -16,11 +16,8 @@ public: bool HandleRequest( const WaitGroup::Ptr& waitGroup, AsioTlsStream& stream, - const ApiUser::Ptr& user, - boost::beast::http::request& request, - const Url::Ptr& url, - boost::beast::http::response& response, - const Dictionary::Ptr& params, + const HttpRequest& request, + HttpResponse& response, boost::asio::yield_context& yc, HttpServerConnection& server ) override;