Use new HTTP message classes in HttpServerConnection and Handlers

This commit is contained in:
Johannes Schmidt 2025-07-23 09:37:05 +02:00
parent 3be451a621
commit 34cb20b242
37 changed files with 284 additions and 407 deletions

View File

@ -18,16 +18,16 @@ REGISTER_URLHANDLER("/v1/actions", ActionsHandler);
bool ActionsHandler::HandleRequest( bool ActionsHandler::HandleRequest(
const WaitGroup::Ptr& waitGroup, const WaitGroup::Ptr& waitGroup,
AsioTlsStream& stream, AsioTlsStream& stream,
const ApiUser::Ptr& user, const HttpRequest& request,
boost::beast::http::request<boost::beast::http::string_body>& request, HttpResponse& response,
const Url::Ptr& url,
boost::beast::http::response<boost::beast::http::string_body>& response,
const Dictionary::Ptr& params,
boost::asio::yield_context& yc, boost::asio::yield_context& yc,
HttpServerConnection& server HttpServerConnection& server
) )
{ {
namespace http = boost::beast::http; namespace http = boost::beast::http;
auto url = request.Url();
auto user = request.User();
auto params = request.Params();
if (url->GetPath().size() != 3) if (url->GetPath().size() != 3)
return false; return false;

View File

@ -18,11 +18,8 @@ public:
bool HandleRequest( bool HandleRequest(
const WaitGroup::Ptr& waitGroup, const WaitGroup::Ptr& waitGroup,
AsioTlsStream& stream, AsioTlsStream& stream,
const ApiUser::Ptr& user, const HttpRequest& request,
boost::beast::http::request<boost::beast::http::string_body>& request, HttpResponse& response,
const Url::Ptr& url,
boost::beast::http::response<boost::beast::http::string_body>& response,
const Dictionary::Ptr& params,
boost::asio::yield_context& yc, boost::asio::yield_context& yc,
HttpServerConnection& server HttpServerConnection& server
) override; ) override;

View File

@ -16,17 +16,18 @@ REGISTER_URLHANDLER("/v1/config/files", ConfigFilesHandler);
bool ConfigFilesHandler::HandleRequest( bool ConfigFilesHandler::HandleRequest(
const WaitGroup::Ptr&, const WaitGroup::Ptr&,
AsioTlsStream& stream, AsioTlsStream& stream,
const ApiUser::Ptr& user, const HttpRequest& request,
boost::beast::http::request<boost::beast::http::string_body>& request, HttpResponse& response,
const Url::Ptr& url,
boost::beast::http::response<boost::beast::http::string_body>& response,
const Dictionary::Ptr& params,
boost::asio::yield_context& yc, boost::asio::yield_context& yc,
HttpServerConnection& server HttpServerConnection& server
) )
{ {
namespace http = boost::beast::http; namespace http = boost::beast::http;
auto url = request.Url();
auto user = request.User();
auto params = request.Params();
if (request.method() != http::verb::get) if (request.method() != http::verb::get)
return false; return false;
@ -81,11 +82,9 @@ bool ConfigFilesHandler::HandleRequest(
std::ifstream fp(path.CStr(), std::ifstream::in | std::ifstream::binary); std::ifstream fp(path.CStr(), std::ifstream::in | std::ifstream::binary);
fp.exceptions(std::ifstream::badbit); fp.exceptions(std::ifstream::badbit);
String content((std::istreambuf_iterator<char>(fp)), std::istreambuf_iterator<char>());
response.result(http::status::ok); response.result(http::status::ok);
response.set(http::field::content_type, "application/octet-stream"); response.set(http::field::content_type, "application/octet-stream");
response.body() = content; response.body() << fp.rdbuf();
response.content_length(response.body().size());
} catch (const std::exception& ex) { } catch (const std::exception& ex) {
HttpUtility::SendJsonError(response, params, 500, "Could not read file.", HttpUtility::SendJsonError(response, params, 500, "Could not read file.",
DiagnosticInformation(ex)); DiagnosticInformation(ex));

View File

@ -16,11 +16,8 @@ public:
bool HandleRequest( bool HandleRequest(
const WaitGroup::Ptr& waitGroup, const WaitGroup::Ptr& waitGroup,
AsioTlsStream& stream, AsioTlsStream& stream,
const ApiUser::Ptr& user, const HttpRequest& request,
boost::beast::http::request<boost::beast::http::string_body>& request, HttpResponse& response,
const Url::Ptr& url,
boost::beast::http::response<boost::beast::http::string_body>& response,
const Dictionary::Ptr& params,
boost::asio::yield_context& yc, boost::asio::yield_context& yc,
HttpServerConnection& server HttpServerConnection& server
) override; ) override;

View File

@ -13,42 +13,41 @@ REGISTER_URLHANDLER("/v1/config/packages", ConfigPackagesHandler);
bool ConfigPackagesHandler::HandleRequest( bool ConfigPackagesHandler::HandleRequest(
const WaitGroup::Ptr&, const WaitGroup::Ptr&,
AsioTlsStream& stream, AsioTlsStream& stream,
const ApiUser::Ptr& user, const HttpRequest& request,
boost::beast::http::request<boost::beast::http::string_body>& request, HttpResponse& response,
const Url::Ptr& url,
boost::beast::http::response<boost::beast::http::string_body>& response,
const Dictionary::Ptr& params,
boost::asio::yield_context& yc, boost::asio::yield_context& yc,
HttpServerConnection& server HttpServerConnection& server
) )
{ {
namespace http = boost::beast::http; namespace http = boost::beast::http;
auto url = request.Url();
auto user = request.User();
auto params = request.Params();
if (url->GetPath().size() > 4) if (url->GetPath().size() > 4)
return false; return false;
if (request.method() == http::verb::get) if (request.method() == http::verb::get)
HandleGet(user, request, url, response, params); HandleGet(request, response);
else if (request.method() == http::verb::post) else if (request.method() == http::verb::post)
HandlePost(user, request, url, response, params); HandlePost(request, response);
else if (request.method() == http::verb::delete_) else if (request.method() == http::verb::delete_)
HandleDelete(user, request, url, response, params); HandleDelete(request, response);
else else
return false; return false;
return true; return true;
} }
void ConfigPackagesHandler::HandleGet( void ConfigPackagesHandler::HandleGet(const HttpRequest& request, HttpResponse& response)
const ApiUser::Ptr& user,
boost::beast::http::request<boost::beast::http::string_body>& request,
const Url::Ptr& url,
boost::beast::http::response<boost::beast::http::string_body>& response,
const Dictionary::Ptr& params
)
{ {
namespace http = boost::beast::http; namespace http = boost::beast::http;
auto url = request.Url();
auto user = request.User();
auto params = request.Params();
FilterUtility::CheckPermission(user, "config/query"); FilterUtility::CheckPermission(user, "config/query");
std::vector<String> packages; std::vector<String> packages;
@ -89,16 +88,14 @@ void ConfigPackagesHandler::HandleGet(
HttpUtility::SendJsonBody(response, params, result); HttpUtility::SendJsonBody(response, params, result);
} }
void ConfigPackagesHandler::HandlePost( void ConfigPackagesHandler::HandlePost(const HttpRequest& request, HttpResponse& response)
const ApiUser::Ptr& user,
boost::beast::http::request<boost::beast::http::string_body>& request,
const Url::Ptr& url,
boost::beast::http::response<boost::beast::http::string_body>& response,
const Dictionary::Ptr& params
)
{ {
namespace http = boost::beast::http; namespace http = boost::beast::http;
auto url = request.Url();
auto user = request.User();
auto params = request.Params();
FilterUtility::CheckPermission(user, "config/modify"); FilterUtility::CheckPermission(user, "config/modify");
if (url->GetPath().size() >= 4) if (url->GetPath().size() >= 4)
@ -135,16 +132,14 @@ void ConfigPackagesHandler::HandlePost(
HttpUtility::SendJsonBody(response, params, result); HttpUtility::SendJsonBody(response, params, result);
} }
void ConfigPackagesHandler::HandleDelete( void ConfigPackagesHandler::HandleDelete(const HttpRequest& request, HttpResponse& response)
const ApiUser::Ptr& user,
boost::beast::http::request<boost::beast::http::string_body>& request,
const Url::Ptr& url,
boost::beast::http::response<boost::beast::http::string_body>& response,
const Dictionary::Ptr& params
)
{ {
namespace http = boost::beast::http; namespace http = boost::beast::http;
auto url = request.Url();
auto user = request.User();
auto params = request.Params();
FilterUtility::CheckPermission(user, "config/modify"); FilterUtility::CheckPermission(user, "config/modify");
if (url->GetPath().size() >= 4) if (url->GetPath().size() >= 4)

View File

@ -16,37 +16,16 @@ public:
bool HandleRequest( bool HandleRequest(
const WaitGroup::Ptr& waitGroup, const WaitGroup::Ptr& waitGroup,
AsioTlsStream& stream, AsioTlsStream& stream,
const ApiUser::Ptr& user, const HttpRequest& request,
boost::beast::http::request<boost::beast::http::string_body>& request, HttpResponse& response,
const Url::Ptr& url,
boost::beast::http::response<boost::beast::http::string_body>& response,
const Dictionary::Ptr& params,
boost::asio::yield_context& yc, boost::asio::yield_context& yc,
HttpServerConnection& server HttpServerConnection& server
) override; ) override;
private: private:
void HandleGet( void HandleGet(const HttpRequest& request, HttpResponse& response);
const ApiUser::Ptr& user, void HandlePost(const HttpRequest& request, HttpResponse& response);
boost::beast::http::request<boost::beast::http::string_body>& request, void HandleDelete(const HttpRequest& request, HttpResponse& response);
const Url::Ptr& url,
boost::beast::http::response<boost::beast::http::string_body>& response,
const Dictionary::Ptr& params
);
void HandlePost(
const ApiUser::Ptr& user,
boost::beast::http::request<boost::beast::http::string_body>& request,
const Url::Ptr& url,
boost::beast::http::response<boost::beast::http::string_body>& response,
const Dictionary::Ptr& params
);
void HandleDelete(
const ApiUser::Ptr& user,
boost::beast::http::request<boost::beast::http::string_body>& request,
const Url::Ptr& url,
boost::beast::http::response<boost::beast::http::string_body>& response,
const Dictionary::Ptr& params
);
}; };

View File

@ -17,42 +17,41 @@ std::atomic<bool> ConfigStagesHandler::m_RunningPackageUpdates (false);
bool ConfigStagesHandler::HandleRequest( bool ConfigStagesHandler::HandleRequest(
const WaitGroup::Ptr&, const WaitGroup::Ptr&,
AsioTlsStream& stream, AsioTlsStream& stream,
const ApiUser::Ptr& user, const HttpRequest& request,
boost::beast::http::request<boost::beast::http::string_body>& request, HttpResponse& response,
const Url::Ptr& url,
boost::beast::http::response<boost::beast::http::string_body>& response,
const Dictionary::Ptr& params,
boost::asio::yield_context& yc, boost::asio::yield_context& yc,
HttpServerConnection& server HttpServerConnection& server
) )
{ {
namespace http = boost::beast::http; namespace http = boost::beast::http;
auto url = request.Url();
auto user = request.User();
auto params = request.Params();
if (url->GetPath().size() > 5) if (url->GetPath().size() > 5)
return false; return false;
if (request.method() == http::verb::get) if (request.method() == http::verb::get)
HandleGet(user, request, url, response, params); HandleGet(request, response);
else if (request.method() == http::verb::post) else if (request.method() == http::verb::post)
HandlePost(user, request, url, response, params); HandlePost(request, response);
else if (request.method() == http::verb::delete_) else if (request.method() == http::verb::delete_)
HandleDelete(user, request, url, response, params); HandleDelete(request, response);
else else
return false; return false;
return true; return true;
} }
void ConfigStagesHandler::HandleGet( void ConfigStagesHandler::HandleGet(const HttpRequest& request, HttpResponse& response)
const ApiUser::Ptr& user,
boost::beast::http::request<boost::beast::http::string_body>& request,
const Url::Ptr& url,
boost::beast::http::response<boost::beast::http::string_body>& response,
const Dictionary::Ptr& params
)
{ {
namespace http = boost::beast::http; namespace http = boost::beast::http;
auto url = request.Url();
auto user = request.User();
auto params = request.Params();
FilterUtility::CheckPermission(user, "config/query"); FilterUtility::CheckPermission(user, "config/query");
if (url->GetPath().size() >= 4) if (url->GetPath().size() >= 4)
@ -91,16 +90,14 @@ void ConfigStagesHandler::HandleGet(
HttpUtility::SendJsonBody(response, params, result); HttpUtility::SendJsonBody(response, params, result);
} }
void ConfigStagesHandler::HandlePost( void ConfigStagesHandler::HandlePost(const HttpRequest& request, HttpResponse& response)
const ApiUser::Ptr& user,
boost::beast::http::request<boost::beast::http::string_body>& request,
const Url::Ptr& url,
boost::beast::http::response<boost::beast::http::string_body>& response,
const Dictionary::Ptr& params
)
{ {
namespace http = boost::beast::http; namespace http = boost::beast::http;
auto url = request.Url();
auto user = request.User();
auto params = request.Params();
FilterUtility::CheckPermission(user, "config/modify"); FilterUtility::CheckPermission(user, "config/modify");
if (url->GetPath().size() >= 4) if (url->GetPath().size() >= 4)
@ -174,16 +171,14 @@ void ConfigStagesHandler::HandlePost(
HttpUtility::SendJsonBody(response, params, result); HttpUtility::SendJsonBody(response, params, result);
} }
void ConfigStagesHandler::HandleDelete( void ConfigStagesHandler::HandleDelete(const HttpRequest& request, HttpResponse& response)
const ApiUser::Ptr& user,
boost::beast::http::request<boost::beast::http::string_body>& request,
const Url::Ptr& url,
boost::beast::http::response<boost::beast::http::string_body>& response,
const Dictionary::Ptr& params
)
{ {
namespace http = boost::beast::http; namespace http = boost::beast::http;
auto url = request.Url();
auto user = request.User();
auto params = request.Params();
FilterUtility::CheckPermission(user, "config/modify"); FilterUtility::CheckPermission(user, "config/modify");
if (url->GetPath().size() >= 4) if (url->GetPath().size() >= 4)

View File

@ -17,37 +17,16 @@ public:
bool HandleRequest( bool HandleRequest(
const WaitGroup::Ptr& waitGroup, const WaitGroup::Ptr& waitGroup,
AsioTlsStream& stream, AsioTlsStream& stream,
const ApiUser::Ptr& user, const HttpRequest& request,
boost::beast::http::request<boost::beast::http::string_body>& request, HttpResponse& response,
const Url::Ptr& url,
boost::beast::http::response<boost::beast::http::string_body>& response,
const Dictionary::Ptr& params,
boost::asio::yield_context& yc, boost::asio::yield_context& yc,
HttpServerConnection& server HttpServerConnection& server
) override; ) override;
private: private:
void HandleGet( void HandleGet(const HttpRequest& request, HttpResponse& response);
const ApiUser::Ptr& user, void HandlePost(const HttpRequest& request, HttpResponse& response);
boost::beast::http::request<boost::beast::http::string_body>& request, void HandleDelete(const HttpRequest& request, HttpResponse& response);
const Url::Ptr& url,
boost::beast::http::response<boost::beast::http::string_body>& response,
const Dictionary::Ptr& params
);
void HandlePost(
const ApiUser::Ptr& user,
boost::beast::http::request<boost::beast::http::string_body>& request,
const Url::Ptr& url,
boost::beast::http::response<boost::beast::http::string_body>& response,
const Dictionary::Ptr& params
);
void HandleDelete(
const ApiUser::Ptr& user,
boost::beast::http::request<boost::beast::http::string_body>& request,
const Url::Ptr& url,
boost::beast::http::response<boost::beast::http::string_body>& response,
const Dictionary::Ptr& params
);
static std::atomic<bool> m_RunningPackageUpdates; static std::atomic<bool> m_RunningPackageUpdates;
}; };

View File

@ -56,17 +56,18 @@ static void EnsureFrameCleanupTimer()
bool ConsoleHandler::HandleRequest( bool ConsoleHandler::HandleRequest(
const WaitGroup::Ptr&, const WaitGroup::Ptr&,
AsioTlsStream& stream, AsioTlsStream& stream,
const ApiUser::Ptr& user, const HttpRequest& request,
boost::beast::http::request<boost::beast::http::string_body>& request, HttpResponse& response,
const Url::Ptr& url,
boost::beast::http::response<boost::beast::http::string_body>& response,
const Dictionary::Ptr& params,
boost::asio::yield_context& yc, boost::asio::yield_context& yc,
HttpServerConnection& server HttpServerConnection& server
) )
{ {
namespace http = boost::beast::http; namespace http = boost::beast::http;
auto url = request.Url();
auto user = request.User();
auto params = request.Params();
if (url->GetPath().size() != 3) if (url->GetPath().size() != 3)
return false; return false;
@ -96,17 +97,16 @@ bool ConsoleHandler::HandleRequest(
} }
if (methodName == "execute-script") 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") 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); HttpUtility::SendJsonError(response, params, 400, "Invalid method specified: " + methodName);
return true; return true;
} }
bool ConsoleHandler::ExecuteScriptHelper(boost::beast::http::request<boost::beast::http::string_body>& request, bool ConsoleHandler::ExecuteScriptHelper(const HttpRequest& request, HttpResponse& response,
boost::beast::http::response<boost::beast::http::string_body>& response, const String& command, const String& session, bool sandboxed)
const Dictionary::Ptr& params, const String& command, const String& session, bool sandboxed)
{ {
namespace http = boost::beast::http; namespace http = boost::beast::http;
@ -174,14 +174,13 @@ bool ConsoleHandler::ExecuteScriptHelper(boost::beast::http::request<boost::beas
}); });
response.result(http::status::ok); response.result(http::status::ok);
HttpUtility::SendJsonBody(response, params, result); HttpUtility::SendJsonBody(response, request.Params(), result);
return true; return true;
} }
bool ConsoleHandler::AutocompleteScriptHelper(boost::beast::http::request<boost::beast::http::string_body>& request, bool ConsoleHandler::AutocompleteScriptHelper(const HttpRequest& request, HttpResponse& response,
boost::beast::http::response<boost::beast::http::string_body>& response, const String& command, const String& session, bool sandboxed)
const Dictionary::Ptr& params, const String& command, const String& session, bool sandboxed)
{ {
namespace http = boost::beast::http; namespace http = boost::beast::http;
@ -213,7 +212,7 @@ bool ConsoleHandler::AutocompleteScriptHelper(boost::beast::http::request<boost:
}); });
response.result(http::status::ok); response.result(http::status::ok);
HttpUtility::SendJsonBody(response, params, result); HttpUtility::SendJsonBody(response, request.Params(), result);
return true; return true;
} }

View File

@ -25,11 +25,8 @@ public:
bool HandleRequest( bool HandleRequest(
const WaitGroup::Ptr& waitGroup, const WaitGroup::Ptr& waitGroup,
AsioTlsStream& stream, AsioTlsStream& stream,
const ApiUser::Ptr& user, const HttpRequest& request,
boost::beast::http::request<boost::beast::http::string_body>& request, HttpResponse& response,
const Url::Ptr& url,
boost::beast::http::response<boost::beast::http::string_body>& response,
const Dictionary::Ptr& params,
boost::asio::yield_context& yc, boost::asio::yield_context& yc,
HttpServerConnection& server HttpServerConnection& server
) override; ) override;
@ -37,12 +34,10 @@ public:
static std::vector<String> GetAutocompletionSuggestions(const String& word, ScriptFrame& frame); static std::vector<String> GetAutocompletionSuggestions(const String& word, ScriptFrame& frame);
private: private:
static bool ExecuteScriptHelper(boost::beast::http::request<boost::beast::http::string_body>& request, static bool ExecuteScriptHelper(const HttpRequest& request, HttpResponse& response,
boost::beast::http::response<boost::beast::http::string_body>& response, const String& command, const String& session, bool sandboxed);
const Dictionary::Ptr& params, const String& command, const String& session, bool sandboxed); static bool AutocompleteScriptHelper(const HttpRequest& request, HttpResponse& response,
static bool AutocompleteScriptHelper(boost::beast::http::request<boost::beast::http::string_body>& request, const String& command, const String& session, bool sandboxed);
boost::beast::http::response<boost::beast::http::string_body>& response,
const Dictionary::Ptr& params, const String& command, const String& session, bool sandboxed);
}; };

View File

@ -18,17 +18,18 @@ REGISTER_URLHANDLER("/v1/objects", CreateObjectHandler);
bool CreateObjectHandler::HandleRequest( bool CreateObjectHandler::HandleRequest(
const WaitGroup::Ptr& waitGroup, const WaitGroup::Ptr& waitGroup,
AsioTlsStream& stream, AsioTlsStream& stream,
const ApiUser::Ptr& user, const HttpRequest& request,
boost::beast::http::request<boost::beast::http::string_body>& request, HttpResponse& response,
const Url::Ptr& url,
boost::beast::http::response<boost::beast::http::string_body>& response,
const Dictionary::Ptr& params,
boost::asio::yield_context& yc, boost::asio::yield_context& yc,
HttpServerConnection& server HttpServerConnection& server
) )
{ {
namespace http = boost::beast::http; namespace http = boost::beast::http;
auto url = request.Url();
auto user = request.User();
auto params = request.Params();
if (url->GetPath().size() != 4) if (url->GetPath().size() != 4)
return false; return false;

View File

@ -16,11 +16,8 @@ public:
bool HandleRequest( bool HandleRequest(
const WaitGroup::Ptr& waitGroup, const WaitGroup::Ptr& waitGroup,
AsioTlsStream& stream, AsioTlsStream& stream,
const ApiUser::Ptr& user, const HttpRequest& request,
boost::beast::http::request<boost::beast::http::string_body>& request, HttpResponse& response,
const Url::Ptr& url,
boost::beast::http::response<boost::beast::http::string_body>& response,
const Dictionary::Ptr& params,
boost::asio::yield_context& yc, boost::asio::yield_context& yc,
HttpServerConnection& server HttpServerConnection& server
) override; ) override;

View File

@ -18,17 +18,18 @@ REGISTER_URLHANDLER("/v1/objects", DeleteObjectHandler);
bool DeleteObjectHandler::HandleRequest( bool DeleteObjectHandler::HandleRequest(
const WaitGroup::Ptr& waitGroup, const WaitGroup::Ptr& waitGroup,
AsioTlsStream& stream, AsioTlsStream& stream,
const ApiUser::Ptr& user, const HttpRequest& request,
boost::beast::http::request<boost::beast::http::string_body>& request, HttpResponse& response,
const Url::Ptr& url,
boost::beast::http::response<boost::beast::http::string_body>& response,
const Dictionary::Ptr& params,
boost::asio::yield_context& yc, boost::asio::yield_context& yc,
HttpServerConnection& server HttpServerConnection& server
) )
{ {
namespace http = boost::beast::http; namespace http = boost::beast::http;
auto url = request.Url();
auto user = request.User();
auto params = request.Params();
if (url->GetPath().size() < 3 || url->GetPath().size() > 4) if (url->GetPath().size() < 3 || url->GetPath().size() > 4)
return false; return false;

View File

@ -16,11 +16,8 @@ public:
bool HandleRequest( bool HandleRequest(
const WaitGroup::Ptr& waitGroup, const WaitGroup::Ptr& waitGroup,
AsioTlsStream& stream, AsioTlsStream& stream,
const ApiUser::Ptr& user, const HttpRequest& request,
boost::beast::http::request<boost::beast::http::string_body>& request, HttpResponse& response,
const Url::Ptr& url,
boost::beast::http::response<boost::beast::http::string_body>& response,
const Dictionary::Ptr& params,
boost::asio::yield_context& yc, boost::asio::yield_context& yc,
HttpServerConnection& server HttpServerConnection& server
) override; ) override;

View File

@ -42,11 +42,8 @@ const String l_ApiQuery ("<API query>");
bool EventsHandler::HandleRequest( bool EventsHandler::HandleRequest(
const WaitGroup::Ptr&, const WaitGroup::Ptr&,
AsioTlsStream& stream, AsioTlsStream& stream,
const ApiUser::Ptr& user, const HttpRequest& request,
boost::beast::http::request<boost::beast::http::string_body>& request, HttpResponse& response,
const Url::Ptr& url,
boost::beast::http::response<boost::beast::http::string_body>& response,
const Dictionary::Ptr& params,
boost::asio::yield_context& yc, boost::asio::yield_context& yc,
HttpServerConnection& server HttpServerConnection& server
) )
@ -54,6 +51,10 @@ bool EventsHandler::HandleRequest(
namespace asio = boost::asio; namespace asio = boost::asio;
namespace http = boost::beast::http; namespace http = boost::beast::http;
auto url = request.Url();
auto user = request.User();
auto params = request.Params();
if (url->GetPath().size() != 2) if (url->GetPath().size() != 2)
return false; return false;

View File

@ -17,11 +17,8 @@ public:
bool HandleRequest( bool HandleRequest(
const WaitGroup::Ptr& waitGroup, const WaitGroup::Ptr& waitGroup,
AsioTlsStream& stream, AsioTlsStream& stream,
const ApiUser::Ptr& user, const HttpRequest& request,
boost::beast::http::request<boost::beast::http::string_body>& request, HttpResponse& response,
const Url::Ptr& url,
boost::beast::http::response<boost::beast::http::string_body>& response,
const Dictionary::Ptr& params,
boost::asio::yield_context& yc, boost::asio::yield_context& yc,
HttpServerConnection& server HttpServerConnection& server
) override; ) override;

View File

@ -49,9 +49,8 @@ void HttpHandler::Register(const Url::Ptr& url, const HttpHandler::Ptr& handler)
void HttpHandler::ProcessRequest( void HttpHandler::ProcessRequest(
const WaitGroup::Ptr& waitGroup, const WaitGroup::Ptr& waitGroup,
AsioTlsStream& stream, AsioTlsStream& stream,
const ApiUser::Ptr& user, HttpRequest& request,
boost::beast::http::request<boost::beast::http::string_body>& request, HttpResponse& response,
boost::beast::http::response<boost::beast::http::string_body>& response,
boost::asio::yield_context& yc, boost::asio::yield_context& yc,
HttpServerConnection& server HttpServerConnection& server
) )
@ -59,8 +58,8 @@ void HttpHandler::ProcessRequest(
Dictionary::Ptr node = m_UrlTree; Dictionary::Ptr node = m_UrlTree;
std::vector<HttpHandler::Ptr> handlers; std::vector<HttpHandler::Ptr> handlers;
Url::Ptr url = new Url(std::string(request.target())); request.DecodeUrl();
auto& path (url->GetPath()); auto& path (request.Url()->GetPath());
for (std::vector<String>::size_type i = 0; i <= path.size(); i++) { for (std::vector<String>::size_type i = 0; i <= path.size(); i++) {
Array::Ptr current_handlers = node->Get("handlers"); Array::Ptr current_handlers = node->Get("handlers");
@ -90,12 +89,10 @@ void HttpHandler::ProcessRequest(
std::reverse(handlers.begin(), handlers.end()); std::reverse(handlers.begin(), handlers.end());
Dictionary::Ptr params;
try { try {
params = HttpUtility::FetchRequestParameters(url, request.body()); request.DecodeParams();
} catch (const std::exception& ex) { } catch (const std::exception& ex) {
HttpUtility::SendJsonError(response, params, 400, "Invalid request body: " + DiagnosticInformation(ex, false)); HttpUtility::SendJsonError(response, request.Params(), 400, "Invalid request body: " + DiagnosticInformation(ex, false));
return; return;
} }
@ -109,12 +106,20 @@ void HttpHandler::ProcessRequest(
*/ */
try { try {
for (const HttpHandler::Ptr& handler : handlers) { 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; processed = true;
break; break;
} }
} }
} catch (const std::exception& ex) { } 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") Log(LogWarning, "HttpServerConnection")
<< "Error while processing HTTP request: " << ex.what(); << "Error while processing HTTP request: " << ex.what();
@ -122,7 +127,7 @@ void HttpHandler::ProcessRequest(
} }
if (!processed) { if (!processed) {
HttpUtility::SendJsonError(response, params, 404, "The requested path '" + boost::algorithm::join(path, "/") + HttpUtility::SendJsonError(response, request.Params(), 404, "The requested path '" + boost::algorithm::join(path, "/") +
"' could not be found or the request method is not valid for this path."); "' could not be found or the request method is not valid for this path.");
return; return;
} }

View File

@ -4,8 +4,10 @@
#define HTTPHANDLER_H #define HTTPHANDLER_H
#include "remote/i2-remote.hpp" #include "remote/i2-remote.hpp"
#include "base/io-engine.hpp"
#include "remote/url.hpp" #include "remote/url.hpp"
#include "remote/httpserverconnection.hpp" #include "remote/httpserverconnection.hpp"
#include "remote/httpmessage.hpp"
#include "remote/apiuser.hpp" #include "remote/apiuser.hpp"
#include "base/registry.hpp" #include "base/registry.hpp"
#include "base/tlsstream.hpp" #include "base/tlsstream.hpp"
@ -29,11 +31,8 @@ public:
virtual bool HandleRequest( virtual bool HandleRequest(
const WaitGroup::Ptr& waitGroup, const WaitGroup::Ptr& waitGroup,
AsioTlsStream& stream, AsioTlsStream& stream,
const ApiUser::Ptr& user, const HttpRequest& request,
boost::beast::http::request<boost::beast::http::string_body>& request, HttpResponse& response,
const Url::Ptr& url,
boost::beast::http::response<boost::beast::http::string_body>& response,
const Dictionary::Ptr& params,
boost::asio::yield_context& yc, boost::asio::yield_context& yc,
HttpServerConnection& server HttpServerConnection& server
) = 0; ) = 0;
@ -42,9 +41,8 @@ public:
static void ProcessRequest( static void ProcessRequest(
const WaitGroup::Ptr& waitGroup, const WaitGroup::Ptr& waitGroup,
AsioTlsStream& stream, AsioTlsStream& stream,
const ApiUser::Ptr& user, HttpRequest& request,
boost::beast::http::request<boost::beast::http::string_body>& request, HttpResponse& response,
boost::beast::http::response<boost::beast::http::string_body>& response,
boost::asio::yield_context& yc, boost::asio::yield_context& yc,
HttpServerConnection& server HttpServerConnection& server
); );

View File

@ -129,10 +129,9 @@ bool HttpServerConnection::Disconnected()
static inline static inline
bool EnsureValidHeaders( bool EnsureValidHeaders(
AsioTlsStream& stream,
boost::beast::flat_buffer& buf, boost::beast::flat_buffer& buf,
boost::beast::http::parser<true, boost::beast::http::string_body>& parser, HttpRequest& request,
boost::beast::http::response<boost::beast::http::string_body>& response, HttpResponse& response,
bool& shuttingDown, bool& shuttingDown,
boost::asio::yield_context& yc boost::asio::yield_context& yc
) )
@ -147,7 +146,7 @@ bool EnsureValidHeaders(
boost::system::error_code ec; boost::system::error_code ec;
http::async_read_header(stream, buf, parser, yc[ec]); request.ParseHeader(buf, yc[ec]);
if (ec) { if (ec) {
if (ec == boost::asio::error::operation_aborted) if (ec == boost::asio::error::operation_aborted)
@ -156,7 +155,7 @@ bool EnsureValidHeaders(
errorMsg = ec.message(); errorMsg = ec.message();
httpError = true; httpError = true;
} else { } else {
switch (parser.get().version()) { switch (request.version()) {
case 10: case 10:
case 11: case 11:
break; break;
@ -168,21 +167,16 @@ bool EnsureValidHeaders(
if (!errorMsg.IsEmpty() || httpError) { if (!errorMsg.IsEmpty() || httpError) {
response.result(http::status::bad_request); response.result(http::status::bad_request);
if (!httpError && parser.get()[http::field::accept] == "application/json") { if (!httpError && request[http::field::accept] == "application/json") {
HttpUtility::SendJsonBody(response, nullptr, new Dictionary({ HttpUtility::SendJsonError(response, nullptr, 400, "Bad Request: " + errorMsg);
{ "error", 400 },
{ "status", String("Bad Request: ") + errorMsg }
}));
} else { } else {
response.set(http::field::content_type, "text/html"); response.set(http::field::content_type, "text/html");
response.body() = String("<h1>Bad Request</h1><p><pre>") + errorMsg + "</pre></p>"; response.body() << "<h1>Bad Request</h1><p><pre>" << errorMsg << "</pre></p>\r\n";
response.content_length(response.body().size());
} }
response.set(http::field::connection, "close"); response.set(http::field::connection, "close");
http::async_write(stream, response, yc); response.Flush(yc);
stream.async_flush(yc);
return false; return false;
} }
@ -192,28 +186,24 @@ bool EnsureValidHeaders(
static inline static inline
void HandleExpect100( void HandleExpect100(
AsioTlsStream& stream, const Shared<AsioTlsStream>::Ptr& stream,
boost::beast::http::request<boost::beast::http::string_body>& request, const HttpRequest& request,
boost::asio::yield_context& yc boost::asio::yield_context& yc
) )
{ {
namespace http = boost::beast::http; namespace http = boost::beast::http;
if (request[http::field::expect] == "100-continue") { if (request[http::field::expect] == "100-continue") {
http::response<http::string_body> response; HttpResponse response{stream};
response.result(http::status::continue_); response.result(http::status::continue_);
response.Flush(yc);
http::async_write(stream, response, yc);
stream.async_flush(yc);
} }
} }
static inline static inline
bool HandleAccessControl( bool HandleAccessControl(
AsioTlsStream& stream, const HttpRequest& request,
boost::beast::http::request<boost::beast::http::string_body>& request, HttpResponse& response,
boost::beast::http::response<boost::beast::http::string_body>& response,
boost::asio::yield_context& yc boost::asio::yield_context& yc
) )
{ {
@ -240,12 +230,10 @@ bool HandleAccessControl(
response.result(http::status::ok); response.result(http::status::ok);
response.set(http::field::access_control_allow_methods, "GET, POST, PUT, DELETE"); 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.set(http::field::access_control_allow_headers, "Authorization, Content-Type, X-HTTP-Method-Override");
response.body() = "Preflight OK"; response.body() << "Preflight OK";
response.content_length(response.body().size());
response.set(http::field::connection, "close"); response.set(http::field::connection, "close");
http::async_write(stream, response, yc); response.Flush(yc);
stream.async_flush(yc);
return false; return false;
} }
@ -258,9 +246,8 @@ bool HandleAccessControl(
static inline static inline
bool EnsureAcceptHeader( bool EnsureAcceptHeader(
AsioTlsStream& stream, const HttpRequest& request,
boost::beast::http::request<boost::beast::http::string_body>& request, HttpResponse& response,
boost::beast::http::response<boost::beast::http::string_body>& response,
boost::asio::yield_context& yc boost::asio::yield_context& yc
) )
{ {
@ -269,12 +256,10 @@ bool EnsureAcceptHeader(
if (request.method() != http::verb::get && request[http::field::accept] != "application/json") { if (request.method() != http::verb::get && request[http::field::accept] != "application/json") {
response.result(http::status::bad_request); response.result(http::status::bad_request);
response.set(http::field::content_type, "text/html"); response.set(http::field::content_type, "text/html");
response.body() = "<h1>Accept header is missing or not set to 'application/json'.</h1>"; response.body() << "<h1>Accept header is missing or not set to 'application/json'.</h1>\r\n";
response.content_length(response.body().size());
response.set(http::field::connection, "close"); response.set(http::field::connection, "close");
http::async_write(stream, response, yc); response.Flush(yc);
stream.async_flush(yc);
return false; return false;
} }
@ -284,16 +269,14 @@ bool EnsureAcceptHeader(
static inline static inline
bool EnsureAuthenticatedUser( bool EnsureAuthenticatedUser(
AsioTlsStream& stream, const HttpRequest& request,
boost::beast::http::request<boost::beast::http::string_body>& request, HttpResponse& response,
ApiUser::Ptr& authenticatedUser,
boost::beast::http::response<boost::beast::http::string_body>& response,
boost::asio::yield_context& yc boost::asio::yield_context& yc
) )
{ {
namespace http = boost::beast::http; namespace http = boost::beast::http;
if (!authenticatedUser) { if (!request.User()) {
Log(LogWarning, "HttpServerConnection") Log(LogWarning, "HttpServerConnection")
<< "Unauthorized request: " << request.method_string() << ' ' << request.target(); << "Unauthorized request: " << request.method_string() << ' ' << request.target();
@ -302,18 +285,13 @@ bool EnsureAuthenticatedUser(
response.set(http::field::connection, "close"); response.set(http::field::connection, "close");
if (request[http::field::accept] == "application/json") { if (request[http::field::accept] == "application/json") {
HttpUtility::SendJsonBody(response, nullptr, new Dictionary({ HttpUtility::SendJsonError(response, nullptr, 401, "Unauthorized. Please check your user credentials.");
{ "error", 401 },
{ "status", "Unauthorized. Please check your user credentials." }
}));
} else { } else {
response.set(http::field::content_type, "text/html"); response.set(http::field::content_type, "text/html");
response.body() = "<h1>Unauthorized. Please check your user credentials.</h1>"; response.body() << "<h1>Unauthorized. Please check your user credentials.</h1>\r\n";
response.content_length(response.body().size());
} }
http::async_write(stream, response, yc); response.Flush(yc);
stream.async_flush(yc);
return false; return false;
} }
@ -323,11 +301,9 @@ bool EnsureAuthenticatedUser(
static inline static inline
bool EnsureValidBody( bool EnsureValidBody(
AsioTlsStream& stream,
boost::beast::flat_buffer& buf, boost::beast::flat_buffer& buf,
boost::beast::http::parser<true, boost::beast::http::string_body>& parser, HttpRequest& request,
ApiUser::Ptr& authenticatedUser, HttpResponse& response,
boost::beast::http::response<boost::beast::http::string_body>& response,
bool& shuttingDown, bool& shuttingDown,
boost::asio::yield_context& yc boost::asio::yield_context& yc
) )
@ -336,7 +312,7 @@ bool EnsureValidBody(
{ {
size_t maxSize = 1024 * 1024; size_t maxSize = 1024 * 1024;
Array::Ptr permissions = authenticatedUser->GetPermissions(); Array::Ptr permissions = request.User()->GetPermissions();
if (permissions) { if (permissions) {
ObjectLock olock(permissions); ObjectLock olock(permissions);
@ -366,7 +342,7 @@ bool EnsureValidBody(
} }
} }
parser.body_limit(maxSize); request.Parser().body_limit(maxSize);
} }
if (shuttingDown) if (shuttingDown)
@ -374,7 +350,7 @@ bool EnsureValidBody(
boost::system::error_code ec; boost::system::error_code ec;
http::async_read(stream, buf, parser, yc[ec]); request.ParseBody(buf, yc[ec]);
if (ec) { if (ec) {
if (ec == boost::asio::error::operation_aborted) if (ec == boost::asio::error::operation_aborted)
@ -389,21 +365,16 @@ bool EnsureValidBody(
response.result(http::status::bad_request); response.result(http::status::bad_request);
if (parser.get()[http::field::accept] == "application/json") { if (request[http::field::accept] == "application/json") {
HttpUtility::SendJsonBody(response, nullptr, new Dictionary({ HttpUtility::SendJsonError(response, nullptr, 400, "Bad Request: " + ec.message());
{ "error", 400 },
{ "status", String("Bad Request: ") + ec.message() }
}));
} else { } else {
response.set(http::field::content_type, "text/html"); response.set(http::field::content_type, "text/html");
response.body() = String("<h1>Bad Request</h1><p><pre>") + ec.message() + "</pre></p>"; response.body() << "<h1>Bad Request</h1><p><pre>" << ec.message() << "</pre></p>\r\n";
response.content_length(response.body().size());
} }
response.set(http::field::connection, "close"); response.set(http::field::connection, "close");
http::async_write(stream, response, yc); response.Flush(yc);
stream.async_flush(yc);
return false; return false;
} }
@ -414,9 +385,8 @@ bool EnsureValidBody(
static inline static inline
bool ProcessRequest( bool ProcessRequest(
AsioTlsStream& stream, AsioTlsStream& stream,
boost::beast::http::request<boost::beast::http::string_body>& request, HttpRequest& request,
ApiUser::Ptr& authenticatedUser, HttpResponse& response,
boost::beast::http::response<boost::beast::http::string_body>& response,
HttpServerConnection& server, HttpServerConnection& server,
bool& hasStartedStreaming, bool& hasStartedStreaming,
const WaitGroup::Ptr& waitGroup, const WaitGroup::Ptr& waitGroup,
@ -424,33 +394,27 @@ bool ProcessRequest(
boost::asio::yield_context& yc boost::asio::yield_context& yc
) )
{ {
namespace http = boost::beast::http;
try { try {
// Cache the elapsed time to acquire a CPU semaphore used to detect extremely heavy workloads. // Cache the elapsed time to acquire a CPU semaphore used to detect extremely heavy workloads.
auto start (std::chrono::steady_clock::now()); auto start (std::chrono::steady_clock::now());
CpuBoundWork handlingRequest (yc); CpuBoundWork handlingRequest (yc);
cpuBoundWorkTime = std::chrono::steady_clock::now() - start; 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) { } catch (const std::exception& ex) {
if (hasStartedStreaming) { if (hasStartedStreaming) {
return false; return false;
} }
auto sysErr (dynamic_cast<const boost::system::system_error*>(&ex)); /* Since we don't know the state the stream is in, we can't send an error response and
* have to just cause a disconnect here.
if (sysErr && sysErr->code() == boost::asio::error::operation_aborted) { */
if (response.HasSerializationStarted()) {
throw; throw;
} }
http::response<http::string_body> response; HttpUtility::SendJsonError(response, request.Params(), 500, "Unhandled exception", DiagnosticInformation(ex));
response.Flush(yc);
HttpUtility::SendJsonError(response, nullptr, 500, "Unhandled exception" , DiagnosticInformation(ex));
http::async_write(stream, response, yc);
stream.async_flush(yc);
return true; return true;
} }
@ -458,8 +422,8 @@ bool ProcessRequest(
return false; return false;
} }
http::async_write(stream, response, yc); response.body().Finish();
stream.async_flush(yc); response.Flush(yc);
return true; return true;
} }
@ -481,23 +445,21 @@ void HttpServerConnection::ProcessMessages(boost::asio::yield_context yc)
while (m_WaitGroup->IsLockable()) { while (m_WaitGroup->IsLockable()) {
m_Seen = Utility::GetTime(); m_Seen = Utility::GetTime();
http::parser<true, http::string_body> parser; HttpRequest request(m_Stream);
http::response<http::string_body> response; HttpResponse response(m_Stream);
parser.header_limit(1024 * 1024); request.Parser().header_limit(1024 * 1024);
parser.body_limit(-1); request.Parser().body_limit(-1);
response.set(http::field::server, l_ServerHeader); 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; break;
} }
m_Seen = Utility::GetTime(); m_Seen = Utility::GetTime();
auto start (ch::steady_clock::now()); auto start (ch::steady_clock::now());
auto& request (parser.get());
{ {
auto method (http::string_to_verb(request["X-Http-Method-Override"])); auto method (http::string_to_verb(request["X-Http-Method-Override"]));
@ -506,19 +468,20 @@ void HttpServerConnection::ProcessMessages(boost::asio::yield_context yc)
} }
} }
HandleExpect100(*m_Stream, request, yc); HandleExpect100(m_Stream, request, yc);
auto authenticatedUser (m_ApiUser); if (m_ApiUser) {
request.User(m_ApiUser);
if (!authenticatedUser) { } else {
authenticatedUser = ApiUser::GetByAuthHeader(std::string(request[http::field::authorization])); auto user = ApiUser::GetByAuthHeader(std::string(request[http::field::authorization]));
request.User(user);
} }
Log logMsg (LogInformation, "HttpServerConnection"); Log logMsg (LogInformation, "HttpServerConnection");
logMsg << "Request " << request.method_string() << ' ' << request.target() logMsg << "Request " << request.method_string() << ' ' << request.target()
<< " (from " << m_PeerAddress << " (from " << m_PeerAddress
<< ", user: " << (authenticatedUser ? authenticatedUser->GetName() : "<unauthenticated>") << ", user: " << (request.User() ? request.User()->GetName() : "<unauthenticated>")
<< ", agent: " << request[http::field::user_agent]; //operator[] - Returns the value for a field, or "" if it does not exist. << ", 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); ch::steady_clock::duration cpuBoundWorkTime(0);
@ -531,29 +494,29 @@ void HttpServerConnection::ProcessMessages(boost::asio::yield_context yc)
logMsg << " took total " << ch::duration_cast<ch::milliseconds>(ch::steady_clock::now() - start).count() << "ms."; logMsg << " took total " << ch::duration_cast<ch::milliseconds>(ch::steady_clock::now() - start).count() << "ms.";
}); });
if (!HandleAccessControl(*m_Stream, request, response, yc)) { if (!HandleAccessControl(request, response, yc)) {
break; break;
} }
if (!EnsureAcceptHeader(*m_Stream, request, response, yc)) { if (!EnsureAcceptHeader(request, response, yc)) {
break; break;
} }
if (!EnsureAuthenticatedUser(*m_Stream, request, authenticatedUser, response, yc)) { if (!EnsureAuthenticatedUser(request, response, yc)) {
break; break;
} }
if (!EnsureValidBody(*m_Stream, buf, parser, authenticatedUser, response, m_ShuttingDown, yc)) { if (!EnsureValidBody(buf, request, response, m_ShuttingDown, yc)) {
break; break;
} }
m_Seen = std::numeric_limits<decltype(m_Seen)>::max(); m_Seen = std::numeric_limits<decltype(m_Seen)>::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; break;
} }
if (request.version() != 11 || request[http::field::connection] == "close") { if (!request.keep_alive()) {
break; break;
} }
} }

View File

@ -52,16 +52,15 @@ Value HttpUtility::GetLastParameter(const Dictionary::Ptr& params, const String&
return arr->Get(arr->GetLength() - 1); return arr->Get(arr->GetLength() - 1);
} }
void HttpUtility::SendJsonBody(boost::beast::http::response<boost::beast::http::string_body>& response, const Dictionary::Ptr& params, const Value& val) void HttpUtility::SendJsonBody(HttpResponse& response, const Dictionary::Ptr& params, const Value& val)
{ {
namespace http = boost::beast::http; namespace http = boost::beast::http;
response.set(http::field::content_type, "application/json"); response.set(http::field::content_type, "application/json");
response.body() = JsonEncode(val, params && GetLastParameter(params, "pretty")); response.GetJsonEncoder(params && GetLastParameter(params, "pretty")).Encode(val);
response.content_length(response.body().size());
} }
void HttpUtility::SendJsonError(boost::beast::http::response<boost::beast::http::string_body>& response, void HttpUtility::SendJsonError(HttpResponse& response,
const Dictionary::Ptr& params, int code, const String& info, const String& diagnosticInformation) const Dictionary::Ptr& params, int code, const String& info, const String& diagnosticInformation)
{ {
Dictionary::Ptr result = new Dictionary({ { "error", code } }); Dictionary::Ptr result = new Dictionary({ { "error", code } });

View File

@ -5,7 +5,7 @@
#include "remote/url.hpp" #include "remote/url.hpp"
#include "base/dictionary.hpp" #include "base/dictionary.hpp"
#include <boost/beast/http.hpp> #include "remote/httpmessage.hpp"
#include <string> #include <string>
namespace icinga namespace icinga
@ -23,8 +23,8 @@ public:
static Dictionary::Ptr FetchRequestParameters(const Url::Ptr& url, const std::string& body); static Dictionary::Ptr FetchRequestParameters(const Url::Ptr& url, const std::string& body);
static Value GetLastParameter(const Dictionary::Ptr& params, const String& key); static Value GetLastParameter(const Dictionary::Ptr& params, const String& key);
static void SendJsonBody(boost::beast::http::response<boost::beast::http::string_body>& response, const Dictionary::Ptr& params, const Value& val); static void SendJsonBody(HttpResponse& response, const Dictionary::Ptr& params, const Value& val);
static void SendJsonError(boost::beast::http::response<boost::beast::http::string_body>& response, const Dictionary::Ptr& params, const int code, static void SendJsonError(HttpResponse& response, const Dictionary::Ptr& params, const int code,
const String& verbose = String(), const String& diagnosticInformation = String()); const String& verbose = String(), const String& diagnosticInformation = String());
}; };

View File

@ -11,17 +11,18 @@ REGISTER_URLHANDLER("/", InfoHandler);
bool InfoHandler::HandleRequest( bool InfoHandler::HandleRequest(
const WaitGroup::Ptr&, const WaitGroup::Ptr&,
AsioTlsStream& stream, AsioTlsStream& stream,
const ApiUser::Ptr& user, const HttpRequest& request,
boost::beast::http::request<boost::beast::http::string_body>& request, HttpResponse& response,
const Url::Ptr& url,
boost::beast::http::response<boost::beast::http::string_body>& response,
const Dictionary::Ptr& params,
boost::asio::yield_context& yc, boost::asio::yield_context& yc,
HttpServerConnection& server HttpServerConnection& server
) )
{ {
namespace http = boost::beast::http; namespace http = boost::beast::http;
auto url = request.Url();
auto user = request.User();
auto params = request.Params();
if (url->GetPath().size() > 2) if (url->GetPath().size() > 2)
return false; return false;
@ -77,23 +78,23 @@ bool InfoHandler::HandleRequest(
} else { } else {
response.set(http::field::content_type, "text/html"); response.set(http::field::content_type, "text/html");
String body = "<html><head><title>Icinga 2</title></head><h1>Hello from Icinga 2 (Version: " + Application::GetAppVersion() + ")!</h1>"; auto & body = response.body();
body += "<p>You are authenticated as <b>" + user->GetName() + "</b>. "; body << "<html><head><title>Icinga 2</title></head><h1>Hello from Icinga 2 (Version: "
<< Application::GetAppVersion() << ")!</h1>"
<< "<p>You are authenticated as <b>" << user->GetName() << "</b>. ";
if (!permInfo.empty()) { if (!permInfo.empty()) {
body += "Your user has the following permissions:</p> <ul>"; body << "Your user has the following permissions:</p> <ul>";
for (const String& perm : permInfo) { for (const String& perm : permInfo) {
body += "<li>" + perm + "</li>"; body << "<li>" << perm << "</li>";
} }
body += "</ul>"; body << "</ul>";
} else } else
body += "Your user does not have any permissions.</p>"; body << "Your user does not have any permissions.</p>";
body += R"(<p>More information about API requests is available in the <a href="https://icinga.com/docs/icinga2/latest/" target="_blank">documentation</a>.</p></html>)"; body << R"(<p>More information about API requests is available in the <a href="https://icinga.com/docs/icinga2/latest/" target="_blank">documentation</a>.</p></html>)";
response.body() = body;
response.content_length(response.body().size());
} }
return true; return true;

View File

@ -16,11 +16,8 @@ public:
bool HandleRequest( bool HandleRequest(
const WaitGroup::Ptr& waitGroup, const WaitGroup::Ptr& waitGroup,
AsioTlsStream& stream, AsioTlsStream& stream,
const ApiUser::Ptr& user, const HttpRequest& request,
boost::beast::http::request<boost::beast::http::string_body>& request, HttpResponse& response,
const Url::Ptr& url,
boost::beast::http::response<boost::beast::http::string_body>& response,
const Dictionary::Ptr& params,
boost::asio::yield_context& yc, boost::asio::yield_context& yc,
HttpServerConnection& server HttpServerConnection& server
) override; ) override;

View File

@ -20,17 +20,18 @@ REGISTER_URLHANDLER("/v1/debug/malloc_info", MallocInfoHandler);
bool MallocInfoHandler::HandleRequest( bool MallocInfoHandler::HandleRequest(
const WaitGroup::Ptr&, const WaitGroup::Ptr&,
AsioTlsStream&, AsioTlsStream&,
const ApiUser::Ptr& user, const HttpRequest& request,
boost::beast::http::request<boost::beast::http::string_body>& request, HttpResponse& response,
const Url::Ptr& url,
boost::beast::http::response<boost::beast::http::string_body>& response,
const Dictionary::Ptr& params,
boost::asio::yield_context&, boost::asio::yield_context&,
HttpServerConnection& HttpServerConnection&
) )
{ {
namespace http = boost::beast::http; namespace http = boost::beast::http;
auto url = request.Url();
auto user = request.User();
auto params = request.Params();
if (url->GetPath().size() != 3) { if (url->GetPath().size() != 3) {
return false; return false;
} }
@ -87,8 +88,7 @@ bool MallocInfoHandler::HandleRequest(
response.result(200); response.result(200);
response.set(http::field::content_type, "application/xml"); response.set(http::field::content_type, "application/xml");
response.body() = std::string(buf, bufSize); response.body() << std::string_view(buf, bufSize);
response.content_length(response.body().size());
#endif /* HAVE_MALLOC_INFO */ #endif /* HAVE_MALLOC_INFO */
return true; return true;

View File

@ -15,11 +15,8 @@ public:
bool HandleRequest( bool HandleRequest(
const WaitGroup::Ptr& waitGroup, const WaitGroup::Ptr& waitGroup,
AsioTlsStream& stream, AsioTlsStream& stream,
const ApiUser::Ptr& user, const HttpRequest& request,
boost::beast::http::request<boost::beast::http::string_body>& request, HttpResponse& response,
const Url::Ptr& url,
boost::beast::http::response<boost::beast::http::string_body>& response,
const Dictionary::Ptr& params,
boost::asio::yield_context& yc, boost::asio::yield_context& yc,
HttpServerConnection& server HttpServerConnection& server
) override; ) override;

View File

@ -16,17 +16,18 @@ REGISTER_URLHANDLER("/v1/objects", ModifyObjectHandler);
bool ModifyObjectHandler::HandleRequest( bool ModifyObjectHandler::HandleRequest(
const WaitGroup::Ptr& waitGroup, const WaitGroup::Ptr& waitGroup,
AsioTlsStream& stream, AsioTlsStream& stream,
const ApiUser::Ptr& user, const HttpRequest& request,
boost::beast::http::request<boost::beast::http::string_body>& request, HttpResponse& response,
const Url::Ptr& url,
boost::beast::http::response<boost::beast::http::string_body>& response,
const Dictionary::Ptr& params,
boost::asio::yield_context& yc, boost::asio::yield_context& yc,
HttpServerConnection& server HttpServerConnection& server
) )
{ {
namespace http = boost::beast::http; namespace http = boost::beast::http;
auto url = request.Url();
auto user = request.User();
auto params = request.Params();
if (url->GetPath().size() < 3 || url->GetPath().size() > 4) if (url->GetPath().size() < 3 || url->GetPath().size() > 4)
return false; return false;

View File

@ -16,11 +16,8 @@ public:
bool HandleRequest( bool HandleRequest(
const WaitGroup::Ptr& waitGroup, const WaitGroup::Ptr& waitGroup,
AsioTlsStream& stream, AsioTlsStream& stream,
const ApiUser::Ptr& user, const HttpRequest& request,
boost::beast::http::request<boost::beast::http::string_body>& request, HttpResponse& response,
const Url::Ptr& url,
boost::beast::http::response<boost::beast::http::string_body>& response,
const Dictionary::Ptr& params,
boost::asio::yield_context& yc, boost::asio::yield_context& yc,
HttpServerConnection& server HttpServerConnection& server
) override; ) override;

View File

@ -91,17 +91,18 @@ Dictionary::Ptr ObjectQueryHandler::SerializeObjectAttrs(const Object::Ptr& obje
bool ObjectQueryHandler::HandleRequest( bool ObjectQueryHandler::HandleRequest(
const WaitGroup::Ptr&, const WaitGroup::Ptr&,
AsioTlsStream& stream, AsioTlsStream& stream,
const ApiUser::Ptr& user, const HttpRequest& request,
boost::beast::http::request<boost::beast::http::string_body>& request, HttpResponse& response,
const Url::Ptr& url,
boost::beast::http::response<boost::beast::http::string_body>& response,
const Dictionary::Ptr& params,
boost::asio::yield_context& yc, boost::asio::yield_context& yc,
HttpServerConnection& server HttpServerConnection& server
) )
{ {
namespace http = boost::beast::http; namespace http = boost::beast::http;
auto url = request.Url();
auto user = request.User();
auto params = request.Params();
if (url->GetPath().size() < 3 || url->GetPath().size() > 4) if (url->GetPath().size() < 3 || url->GetPath().size() > 4)
return false; return false;

View File

@ -16,11 +16,8 @@ public:
bool HandleRequest( bool HandleRequest(
const WaitGroup::Ptr& waitGroup, const WaitGroup::Ptr& waitGroup,
AsioTlsStream& stream, AsioTlsStream& stream,
const ApiUser::Ptr& user, const HttpRequest& request,
boost::beast::http::request<boost::beast::http::string_body>& request, HttpResponse& response,
const Url::Ptr& url,
boost::beast::http::response<boost::beast::http::string_body>& response,
const Dictionary::Ptr& params,
boost::asio::yield_context& yc, boost::asio::yield_context& yc,
HttpServerConnection& server HttpServerConnection& server
) override; ) override;

View File

@ -71,17 +71,18 @@ public:
bool StatusHandler::HandleRequest( bool StatusHandler::HandleRequest(
const WaitGroup::Ptr&, const WaitGroup::Ptr&,
AsioTlsStream& stream, AsioTlsStream& stream,
const ApiUser::Ptr& user, const HttpRequest& request,
boost::beast::http::request<boost::beast::http::string_body>& request, HttpResponse& response,
const Url::Ptr& url,
boost::beast::http::response<boost::beast::http::string_body>& response,
const Dictionary::Ptr& params,
boost::asio::yield_context& yc, boost::asio::yield_context& yc,
HttpServerConnection& server HttpServerConnection& server
) )
{ {
namespace http = boost::beast::http; namespace http = boost::beast::http;
auto url = request.Url();
auto user = request.User();
auto params = request.Params();
if (url->GetPath().size() > 3) if (url->GetPath().size() > 3)
return false; return false;

View File

@ -16,11 +16,8 @@ public:
bool HandleRequest( bool HandleRequest(
const WaitGroup::Ptr& waitGroup, const WaitGroup::Ptr& waitGroup,
AsioTlsStream& stream, AsioTlsStream& stream,
const ApiUser::Ptr& user, const HttpRequest& request,
boost::beast::http::request<boost::beast::http::string_body>& request, HttpResponse& response,
const Url::Ptr& url,
boost::beast::http::response<boost::beast::http::string_body>& response,
const Dictionary::Ptr& params,
boost::asio::yield_context& yc, boost::asio::yield_context& yc,
HttpServerConnection& server HttpServerConnection& server
) override; ) override;

View File

@ -78,17 +78,18 @@ public:
bool TemplateQueryHandler::HandleRequest( bool TemplateQueryHandler::HandleRequest(
const WaitGroup::Ptr&, const WaitGroup::Ptr&,
AsioTlsStream& stream, AsioTlsStream& stream,
const ApiUser::Ptr& user, const HttpRequest& request,
boost::beast::http::request<boost::beast::http::string_body>& request, HttpResponse& response,
const Url::Ptr& url,
boost::beast::http::response<boost::beast::http::string_body>& response,
const Dictionary::Ptr& params,
boost::asio::yield_context& yc, boost::asio::yield_context& yc,
HttpServerConnection& server HttpServerConnection& server
) )
{ {
namespace http = boost::beast::http; namespace http = boost::beast::http;
auto url = request.Url();
auto user = request.User();
auto params = request.Params();
if (url->GetPath().size() < 3 || url->GetPath().size() > 4) if (url->GetPath().size() < 3 || url->GetPath().size() > 4)
return false; return false;

View File

@ -16,11 +16,8 @@ public:
bool HandleRequest( bool HandleRequest(
const WaitGroup::Ptr& waitGroup, const WaitGroup::Ptr& waitGroup,
AsioTlsStream& stream, AsioTlsStream& stream,
const ApiUser::Ptr& user, const HttpRequest& request,
boost::beast::http::request<boost::beast::http::string_body>& request, HttpResponse& response,
const Url::Ptr& url,
boost::beast::http::response<boost::beast::http::string_body>& response,
const Dictionary::Ptr& params,
boost::asio::yield_context& yc, boost::asio::yield_context& yc,
HttpServerConnection& server HttpServerConnection& server
) override; ) override;

View File

@ -49,17 +49,18 @@ public:
bool TypeQueryHandler::HandleRequest( bool TypeQueryHandler::HandleRequest(
const WaitGroup::Ptr&, const WaitGroup::Ptr&,
AsioTlsStream& stream, AsioTlsStream& stream,
const ApiUser::Ptr& user, const HttpRequest& request,
boost::beast::http::request<boost::beast::http::string_body>& request, HttpResponse& response,
const Url::Ptr& url,
boost::beast::http::response<boost::beast::http::string_body>& response,
const Dictionary::Ptr& params,
boost::asio::yield_context& yc, boost::asio::yield_context& yc,
HttpServerConnection& server HttpServerConnection& server
) )
{ {
namespace http = boost::beast::http; namespace http = boost::beast::http;
auto url = request.Url();
auto user = request.User();
auto params = request.Params();
if (url->GetPath().size() > 3) if (url->GetPath().size() > 3)
return false; return false;

View File

@ -16,11 +16,8 @@ public:
bool HandleRequest( bool HandleRequest(
const WaitGroup::Ptr& waitGroup, const WaitGroup::Ptr& waitGroup,
AsioTlsStream& stream, AsioTlsStream& stream,
const ApiUser::Ptr& user, const HttpRequest& request,
boost::beast::http::request<boost::beast::http::string_body>& request, HttpResponse& response,
const Url::Ptr& url,
boost::beast::http::response<boost::beast::http::string_body>& response,
const Dictionary::Ptr& params,
boost::asio::yield_context& yc, boost::asio::yield_context& yc,
HttpServerConnection& server HttpServerConnection& server
) override; ) override;

View File

@ -59,17 +59,18 @@ public:
bool VariableQueryHandler::HandleRequest( bool VariableQueryHandler::HandleRequest(
const WaitGroup::Ptr&, const WaitGroup::Ptr&,
AsioTlsStream& stream, AsioTlsStream& stream,
const ApiUser::Ptr& user, const HttpRequest& request,
boost::beast::http::request<boost::beast::http::string_body>& request, HttpResponse& response,
const Url::Ptr& url,
boost::beast::http::response<boost::beast::http::string_body>& response,
const Dictionary::Ptr& params,
boost::asio::yield_context& yc, boost::asio::yield_context& yc,
HttpServerConnection& server HttpServerConnection& server
) )
{ {
namespace http = boost::beast::http; namespace http = boost::beast::http;
auto url = request.Url();
auto user = request.User();
auto params = request.Params();
if (url->GetPath().size() > 3) if (url->GetPath().size() > 3)
return false; return false;

View File

@ -16,11 +16,8 @@ public:
bool HandleRequest( bool HandleRequest(
const WaitGroup::Ptr& waitGroup, const WaitGroup::Ptr& waitGroup,
AsioTlsStream& stream, AsioTlsStream& stream,
const ApiUser::Ptr& user, const HttpRequest& request,
boost::beast::http::request<boost::beast::http::string_body>& request, HttpResponse& response,
const Url::Ptr& url,
boost::beast::http::response<boost::beast::http::string_body>& response,
const Dictionary::Ptr& params,
boost::asio::yield_context& yc, boost::asio::yield_context& yc,
HttpServerConnection& server HttpServerConnection& server
) override; ) override;