From a0f603f608187ae0fc99e362c2635e838d2a7da0 Mon Sep 17 00:00:00 2001 From: Johannes Schmidt Date: Thu, 22 Jan 2026 12:41:21 +0100 Subject: [PATCH 1/2] Update names of HttpRequest and HttpResponse --- lib/remote/actionshandler.cpp | 4 ++-- lib/remote/actionshandler.hpp | 4 ++-- lib/remote/configfileshandler.cpp | 4 ++-- lib/remote/configfileshandler.hpp | 4 ++-- lib/remote/configpackageshandler.cpp | 10 ++++---- lib/remote/configpackageshandler.hpp | 10 ++++---- lib/remote/configstageshandler.cpp | 10 ++++---- lib/remote/configstageshandler.hpp | 10 ++++---- lib/remote/consolehandler.cpp | 8 +++---- lib/remote/consolehandler.hpp | 8 +++---- lib/remote/createobjecthandler.cpp | 4 ++-- lib/remote/createobjecthandler.hpp | 4 ++-- lib/remote/deleteobjecthandler.cpp | 4 ++-- lib/remote/deleteobjecthandler.hpp | 4 ++-- lib/remote/eventshandler.cpp | 4 ++-- lib/remote/eventshandler.hpp | 4 ++-- lib/remote/httphandler.cpp | 4 ++-- lib/remote/httphandler.hpp | 8 +++---- lib/remote/httpmessage.cpp | 36 ++++++++++++++-------------- lib/remote/httpmessage.hpp | 10 ++++---- lib/remote/httpserverconnection.cpp | 34 +++++++++++++------------- lib/remote/httputility.cpp | 4 ++-- lib/remote/httputility.hpp | 4 ++-- lib/remote/infohandler.cpp | 4 ++-- lib/remote/infohandler.hpp | 4 ++-- lib/remote/mallocinfohandler.cpp | 4 ++-- lib/remote/mallocinfohandler.hpp | 4 ++-- lib/remote/modifyobjecthandler.cpp | 4 ++-- lib/remote/modifyobjecthandler.hpp | 4 ++-- lib/remote/objectqueryhandler.cpp | 4 ++-- lib/remote/objectqueryhandler.hpp | 4 ++-- lib/remote/statushandler.cpp | 4 ++-- lib/remote/statushandler.hpp | 4 ++-- lib/remote/templatequeryhandler.cpp | 4 ++-- lib/remote/templatequeryhandler.hpp | 4 ++-- lib/remote/typequeryhandler.cpp | 4 ++-- lib/remote/typequeryhandler.hpp | 4 ++-- lib/remote/variablequeryhandler.cpp | 4 ++-- lib/remote/variablequeryhandler.hpp | 4 ++-- test/remote-httpmessage.cpp | 22 ++++++++--------- test/remote-httpserverconnection.cpp | 12 +++++----- 41 files changed, 147 insertions(+), 147 deletions(-) diff --git a/lib/remote/actionshandler.cpp b/lib/remote/actionshandler.cpp index b9853945b..0a23a3068 100644 --- a/lib/remote/actionshandler.cpp +++ b/lib/remote/actionshandler.cpp @@ -17,8 +17,8 @@ REGISTER_URLHANDLER("/v1/actions", ActionsHandler); bool ActionsHandler::HandleRequest( const WaitGroup::Ptr& waitGroup, - const HttpRequest& request, - HttpResponse& response, + const HttpApiRequest& request, + HttpApiResponse& response, boost::asio::yield_context& yc ) { diff --git a/lib/remote/actionshandler.hpp b/lib/remote/actionshandler.hpp index 3ba856f69..2abe755e3 100644 --- a/lib/remote/actionshandler.hpp +++ b/lib/remote/actionshandler.hpp @@ -17,8 +17,8 @@ public: bool HandleRequest( const WaitGroup::Ptr& waitGroup, - const HttpRequest& request, - HttpResponse& response, + const HttpApiRequest& request, + HttpApiResponse& response, boost::asio::yield_context& yc ) override; }; diff --git a/lib/remote/configfileshandler.cpp b/lib/remote/configfileshandler.cpp index 2bd540386..48be8d0db 100644 --- a/lib/remote/configfileshandler.cpp +++ b/lib/remote/configfileshandler.cpp @@ -15,8 +15,8 @@ REGISTER_URLHANDLER("/v1/config/files", ConfigFilesHandler); bool ConfigFilesHandler::HandleRequest( const WaitGroup::Ptr&, - const HttpRequest& request, - HttpResponse& response, + const HttpApiRequest& request, + HttpApiResponse& response, boost::asio::yield_context& yc ) { diff --git a/lib/remote/configfileshandler.hpp b/lib/remote/configfileshandler.hpp index 3294811c0..43f1512c0 100644 --- a/lib/remote/configfileshandler.hpp +++ b/lib/remote/configfileshandler.hpp @@ -15,8 +15,8 @@ public: bool HandleRequest( const WaitGroup::Ptr& waitGroup, - const HttpRequest& request, - HttpResponse& response, + const HttpApiRequest& request, + HttpApiResponse& response, boost::asio::yield_context& yc ) override; }; diff --git a/lib/remote/configpackageshandler.cpp b/lib/remote/configpackageshandler.cpp index 7e0c7b02c..4a88ef9f8 100644 --- a/lib/remote/configpackageshandler.cpp +++ b/lib/remote/configpackageshandler.cpp @@ -13,8 +13,8 @@ REGISTER_URLHANDLER("/v1/config/packages", ConfigPackagesHandler); bool ConfigPackagesHandler::HandleRequest( const WaitGroup::Ptr&, - const HttpRequest& request, - HttpResponse& response, + const HttpApiRequest& request, + HttpApiResponse& response, boost::asio::yield_context& yc ) { @@ -39,7 +39,7 @@ bool ConfigPackagesHandler::HandleRequest( return true; } -void ConfigPackagesHandler::HandleGet(const HttpRequest& request, HttpResponse& response) +void ConfigPackagesHandler::HandleGet(const HttpApiRequest& request, HttpApiResponse& response) { namespace http = boost::beast::http; @@ -87,7 +87,7 @@ void ConfigPackagesHandler::HandleGet(const HttpRequest& request, HttpResponse& HttpUtility::SendJsonBody(response, params, result); } -void ConfigPackagesHandler::HandlePost(const HttpRequest& request, HttpResponse& response) +void ConfigPackagesHandler::HandlePost(const HttpApiRequest& request, HttpApiResponse& response) { namespace http = boost::beast::http; @@ -137,7 +137,7 @@ void ConfigPackagesHandler::HandlePost(const HttpRequest& request, HttpResponse& HttpUtility::SendJsonBody(response, params, result); } -void ConfigPackagesHandler::HandleDelete(const HttpRequest& request, HttpResponse& response) +void ConfigPackagesHandler::HandleDelete(const HttpApiRequest& request, HttpApiResponse& response) { namespace http = boost::beast::http; diff --git a/lib/remote/configpackageshandler.hpp b/lib/remote/configpackageshandler.hpp index 172690f63..5abe6f0ee 100644 --- a/lib/remote/configpackageshandler.hpp +++ b/lib/remote/configpackageshandler.hpp @@ -15,15 +15,15 @@ public: bool HandleRequest( const WaitGroup::Ptr& waitGroup, - const HttpRequest& request, - HttpResponse& response, + const HttpApiRequest& request, + HttpApiResponse& response, boost::asio::yield_context& yc ) override; private: - void HandleGet(const HttpRequest& request, HttpResponse& response); - void HandlePost(const HttpRequest& request, HttpResponse& response); - void HandleDelete(const HttpRequest& request, HttpResponse& response); + void HandleGet(const HttpApiRequest& request, HttpApiResponse& response); + void HandlePost(const HttpApiRequest& request, HttpApiResponse& response); + void HandleDelete(const HttpApiRequest& request, HttpApiResponse& response); }; diff --git a/lib/remote/configstageshandler.cpp b/lib/remote/configstageshandler.cpp index b08270e56..09a4ed3c8 100644 --- a/lib/remote/configstageshandler.cpp +++ b/lib/remote/configstageshandler.cpp @@ -20,8 +20,8 @@ static std::mutex l_RunningPackageUpdatesMutex; // Protects the above two variab bool ConfigStagesHandler::HandleRequest( const WaitGroup::Ptr&, - const HttpRequest& request, - HttpResponse& response, + const HttpApiRequest& request, + HttpApiResponse& response, boost::asio::yield_context& yc ) { @@ -46,7 +46,7 @@ bool ConfigStagesHandler::HandleRequest( return true; } -void ConfigStagesHandler::HandleGet(const HttpRequest& request, HttpResponse& response) +void ConfigStagesHandler::HandleGet(const HttpApiRequest& request, HttpApiResponse& response) { namespace http = boost::beast::http; @@ -92,7 +92,7 @@ void ConfigStagesHandler::HandleGet(const HttpRequest& request, HttpResponse& re HttpUtility::SendJsonBody(response, params, result); } -void ConfigStagesHandler::HandlePost(const HttpRequest& request, HttpResponse& response) +void ConfigStagesHandler::HandlePost(const HttpApiRequest& request, HttpApiResponse& response) { namespace http = boost::beast::http; @@ -203,7 +203,7 @@ void ConfigStagesHandler::HandlePost(const HttpRequest& request, HttpResponse& r HttpUtility::SendJsonBody(response, params, result); } -void ConfigStagesHandler::HandleDelete(const HttpRequest& request, HttpResponse& response) +void ConfigStagesHandler::HandleDelete(const HttpApiRequest& request, HttpApiResponse& response) { namespace http = boost::beast::http; diff --git a/lib/remote/configstageshandler.hpp b/lib/remote/configstageshandler.hpp index ec333cc50..8bedce5aa 100644 --- a/lib/remote/configstageshandler.hpp +++ b/lib/remote/configstageshandler.hpp @@ -15,15 +15,15 @@ public: bool HandleRequest( const WaitGroup::Ptr& waitGroup, - const HttpRequest& request, - HttpResponse& response, + const HttpApiRequest& request, + HttpApiResponse& response, boost::asio::yield_context& yc ) override; private: - void HandleGet(const HttpRequest& request, HttpResponse& response); - void HandlePost(const HttpRequest& request, HttpResponse& response); - void HandleDelete(const HttpRequest& request, HttpResponse& response); + void HandleGet(const HttpApiRequest& request, HttpApiResponse& response); + void HandlePost(const HttpApiRequest& request, HttpApiResponse& response); + void HandleDelete(const HttpApiRequest& request, HttpApiResponse& response); }; } diff --git a/lib/remote/consolehandler.cpp b/lib/remote/consolehandler.cpp index 5184638d4..22bbe5911 100644 --- a/lib/remote/consolehandler.cpp +++ b/lib/remote/consolehandler.cpp @@ -54,8 +54,8 @@ static void EnsureFrameCleanupTimer() bool ConsoleHandler::HandleRequest( const WaitGroup::Ptr&, - const HttpRequest& request, - HttpResponse& response, + const HttpApiRequest& request, + HttpApiResponse& response, boost::asio::yield_context& yc ) { @@ -102,7 +102,7 @@ bool ConsoleHandler::HandleRequest( return true; } -bool ConsoleHandler::ExecuteScriptHelper(const HttpRequest& request, HttpResponse& response, +bool ConsoleHandler::ExecuteScriptHelper(const HttpApiRequest& request, HttpApiResponse& response, const String& command, const String& session, bool sandboxed) { namespace http = boost::beast::http; @@ -176,7 +176,7 @@ bool ConsoleHandler::ExecuteScriptHelper(const HttpRequest& request, HttpRespons return true; } -bool ConsoleHandler::AutocompleteScriptHelper(const HttpRequest& request, HttpResponse& response, +bool ConsoleHandler::AutocompleteScriptHelper(const HttpApiRequest& request, HttpApiResponse& response, const String& command, const String& session, bool sandboxed) { namespace http = boost::beast::http; diff --git a/lib/remote/consolehandler.hpp b/lib/remote/consolehandler.hpp index 30fb98f2e..90f1263dc 100644 --- a/lib/remote/consolehandler.hpp +++ b/lib/remote/consolehandler.hpp @@ -24,17 +24,17 @@ public: bool HandleRequest( const WaitGroup::Ptr& waitGroup, - const HttpRequest& request, - HttpResponse& response, + const HttpApiRequest& request, + HttpApiResponse& response, boost::asio::yield_context& yc ) override; static std::vector GetAutocompletionSuggestions(const String& word, ScriptFrame& frame); private: - static bool ExecuteScriptHelper(const HttpRequest& request, HttpResponse& response, + static bool ExecuteScriptHelper(const HttpApiRequest& request, HttpApiResponse& response, const String& command, const String& session, bool sandboxed); - static bool AutocompleteScriptHelper(const HttpRequest& request, HttpResponse& response, + static bool AutocompleteScriptHelper(const HttpApiRequest& request, HttpApiResponse& response, const String& command, const String& session, bool sandboxed); }; diff --git a/lib/remote/createobjecthandler.cpp b/lib/remote/createobjecthandler.cpp index beff9c987..b002d1cf7 100644 --- a/lib/remote/createobjecthandler.cpp +++ b/lib/remote/createobjecthandler.cpp @@ -17,8 +17,8 @@ REGISTER_URLHANDLER("/v1/objects", CreateObjectHandler); bool CreateObjectHandler::HandleRequest( const WaitGroup::Ptr& waitGroup, - const HttpRequest& request, - HttpResponse& response, + const HttpApiRequest& request, + HttpApiResponse& response, boost::asio::yield_context& yc ) { diff --git a/lib/remote/createobjecthandler.hpp b/lib/remote/createobjecthandler.hpp index 972d7b3bd..2d71c424f 100644 --- a/lib/remote/createobjecthandler.hpp +++ b/lib/remote/createobjecthandler.hpp @@ -15,8 +15,8 @@ public: bool HandleRequest( const WaitGroup::Ptr& waitGroup, - const HttpRequest& request, - HttpResponse& response, + const HttpApiRequest& request, + HttpApiResponse& response, boost::asio::yield_context& yc ) override; }; diff --git a/lib/remote/deleteobjecthandler.cpp b/lib/remote/deleteobjecthandler.cpp index cd99f7b28..652a66b4b 100644 --- a/lib/remote/deleteobjecthandler.cpp +++ b/lib/remote/deleteobjecthandler.cpp @@ -17,8 +17,8 @@ REGISTER_URLHANDLER("/v1/objects", DeleteObjectHandler); bool DeleteObjectHandler::HandleRequest( const WaitGroup::Ptr& waitGroup, - const HttpRequest& request, - HttpResponse& response, + const HttpApiRequest& request, + HttpApiResponse& response, boost::asio::yield_context& yc ) { diff --git a/lib/remote/deleteobjecthandler.hpp b/lib/remote/deleteobjecthandler.hpp index f969facda..e63bac6f6 100644 --- a/lib/remote/deleteobjecthandler.hpp +++ b/lib/remote/deleteobjecthandler.hpp @@ -15,8 +15,8 @@ public: bool HandleRequest( const WaitGroup::Ptr& waitGroup, - const HttpRequest& request, - HttpResponse& response, + const HttpApiRequest& request, + HttpApiResponse& response, boost::asio::yield_context& yc ) override; }; diff --git a/lib/remote/eventshandler.cpp b/lib/remote/eventshandler.cpp index b4eaf70f3..79a63b24f 100644 --- a/lib/remote/eventshandler.cpp +++ b/lib/remote/eventshandler.cpp @@ -41,8 +41,8 @@ const String l_ApiQuery (""); bool EventsHandler::HandleRequest( const WaitGroup::Ptr&, - const HttpRequest& request, - HttpResponse& response, + const HttpApiRequest& request, + HttpApiResponse& response, boost::asio::yield_context& yc ) { diff --git a/lib/remote/eventshandler.hpp b/lib/remote/eventshandler.hpp index 91d5ffe3f..006be673d 100644 --- a/lib/remote/eventshandler.hpp +++ b/lib/remote/eventshandler.hpp @@ -16,8 +16,8 @@ public: bool HandleRequest( const WaitGroup::Ptr& waitGroup, - const HttpRequest& request, - HttpResponse& response, + const HttpApiRequest& request, + HttpApiResponse& response, boost::asio::yield_context& yc ) override; }; diff --git a/lib/remote/httphandler.cpp b/lib/remote/httphandler.cpp index db27da31a..0dafad4c8 100644 --- a/lib/remote/httphandler.cpp +++ b/lib/remote/httphandler.cpp @@ -48,8 +48,8 @@ void HttpHandler::Register(const Url::Ptr& url, const HttpHandler::Ptr& handler) void HttpHandler::ProcessRequest( const WaitGroup::Ptr& waitGroup, - HttpRequest& request, - HttpResponse& response, + HttpApiRequest& request, + HttpApiResponse& response, boost::asio::yield_context& yc ) { diff --git a/lib/remote/httphandler.hpp b/lib/remote/httphandler.hpp index 77f7d4337..4e714fd43 100644 --- a/lib/remote/httphandler.hpp +++ b/lib/remote/httphandler.hpp @@ -30,16 +30,16 @@ public: virtual bool HandleRequest( const WaitGroup::Ptr& waitGroup, - const HttpRequest& request, - HttpResponse& response, + const HttpApiRequest& request, + HttpApiResponse& response, boost::asio::yield_context& yc ) = 0; static void Register(const Url::Ptr& url, const HttpHandler::Ptr& handler); static void ProcessRequest( const WaitGroup::Ptr& waitGroup, - HttpRequest& request, - HttpResponse& response, + HttpApiRequest& request, + HttpApiResponse& response, boost::asio::yield_context& yc ); diff --git a/lib/remote/httpmessage.cpp b/lib/remote/httpmessage.cpp index 18e5a3016..6ad761b8a 100644 --- a/lib/remote/httpmessage.cpp +++ b/lib/remote/httpmessage.cpp @@ -30,7 +30,7 @@ constexpr std::size_t l_FlushThreshold = 128UL * 1024UL; class HttpResponseJsonWriter : public AsyncJsonWriter { public: - explicit HttpResponseJsonWriter(HttpResponse& msg) : m_Message{msg} + explicit HttpResponseJsonWriter(HttpApiResponse& msg) : m_Message{msg} { m_Message.body().Start(); #if BOOST_VERSION >= 107000 @@ -59,51 +59,51 @@ public: } private: - HttpResponse& m_Message; + HttpApiResponse& m_Message; }; -HttpRequest::HttpRequest(Shared::Ptr stream) : m_Stream(std::move(stream)) +HttpApiRequest::HttpApiRequest(Shared::Ptr stream) : m_Stream(std::move(stream)) { } -void HttpRequest::ParseHeader(boost::beast::flat_buffer& buf, boost::asio::yield_context yc) +void HttpApiRequest::ParseHeader(boost::beast::flat_buffer& buf, boost::asio::yield_context yc) { boost::beast::http::async_read_header(*m_Stream, buf, m_Parser, yc); base() = m_Parser.get().base(); } -void HttpRequest::ParseBody(boost::beast::flat_buffer& buf, boost::asio::yield_context yc) +void HttpApiRequest::ParseBody(boost::beast::flat_buffer& buf, boost::asio::yield_context yc) { boost::beast::http::async_read(*m_Stream, buf, m_Parser, yc); body() = std::move(m_Parser.release().body()); } -ApiUser::Ptr HttpRequest::User() const +ApiUser::Ptr HttpApiRequest::User() const { return m_User; } -void HttpRequest::User(const ApiUser::Ptr& user) +void HttpApiRequest::User(const ApiUser::Ptr& user) { m_User = user; } -Url::Ptr HttpRequest::Url() const +Url::Ptr HttpApiRequest::Url() const { return m_Url; } -void HttpRequest::DecodeUrl() +void HttpApiRequest::DecodeUrl() { m_Url = new icinga::Url(std::string(target())); } -Dictionary::Ptr HttpRequest::Params() const +Dictionary::Ptr HttpApiRequest::Params() const { return m_Params; } -void HttpRequest::DecodeParams() +void HttpApiRequest::DecodeParams() { if (!m_Url) { DecodeUrl(); @@ -111,18 +111,18 @@ void HttpRequest::DecodeParams() m_Params = HttpUtility::FetchRequestParameters(m_Url, body()); } -HttpResponse::HttpResponse(Shared::Ptr stream, HttpServerConnection::Ptr server) +HttpApiResponse::HttpApiResponse(Shared::Ptr stream, HttpServerConnection::Ptr server) : m_Server(std::move(server)), m_Stream(std::move(stream)) { } -void HttpResponse::Clear() +void HttpApiResponse::Clear() { ASSERT(!m_SerializationStarted); boost::beast::http::response::operator=({}); } -void HttpResponse::Flush(boost::asio::yield_context yc) +void HttpApiResponse::Flush(boost::asio::yield_context yc) { if (!chunked() && !has_content_length()) { ASSERT(!m_SerializationStarted); @@ -149,7 +149,7 @@ void HttpResponse::Flush(boost::asio::yield_context yc) ASSERT(m_Serializer.is_done() || !body().Finished()); } -void HttpResponse::StartStreaming(bool checkForDisconnect) +void HttpApiResponse::StartStreaming(bool checkForDisconnect) { ASSERT(body().Size() == 0 && !m_SerializationStarted); body().Start(); @@ -161,13 +161,13 @@ void HttpResponse::StartStreaming(bool checkForDisconnect) } } -bool HttpResponse::IsClientDisconnected() const +bool HttpApiResponse::IsClientDisconnected() const { ASSERT(m_Server); return m_Server->Disconnected(); } -void HttpResponse::SendFile(const String& path, const boost::asio::yield_context& yc) +void HttpApiResponse::SendFile(const String& path, const boost::asio::yield_context& yc) { std::ifstream fp(path.CStr(), std::ifstream::in | std::ifstream::binary | std::ifstream::ate); fp.exceptions(std::ifstream::badbit | std::ifstream::eofbit); @@ -190,7 +190,7 @@ void HttpResponse::SendFile(const String& path, const boost::asio::yield_context } } -JsonEncoder HttpResponse::GetJsonEncoder(bool pretty) +JsonEncoder HttpApiResponse::GetJsonEncoder(bool pretty) { return JsonEncoder{std::make_shared(*this), pretty}; } diff --git a/lib/remote/httpmessage.hpp b/lib/remote/httpmessage.hpp index 10d00fd49..ef0f70bed 100644 --- a/lib/remote/httpmessage.hpp +++ b/lib/remote/httpmessage.hpp @@ -148,12 +148,12 @@ struct SerializableBody * * @ingroup remote */ -class HttpRequest : public boost::beast::http::request +class HttpApiRequest : public boost::beast::http::request { public: using ParserType = boost::beast::http::request_parser; - explicit HttpRequest(Shared::Ptr stream); + explicit HttpApiRequest(Shared::Ptr stream); /** * Parse the header of the response using the internal parser object. @@ -200,10 +200,10 @@ private: * * @ingroup remote */ -class HttpResponse : public boost::beast::http::response> +class HttpApiResponse : public boost::beast::http::response> { public: - explicit HttpResponse(Shared::Ptr stream, HttpServerConnection::Ptr server = nullptr); + explicit HttpApiResponse(Shared::Ptr stream, HttpServerConnection::Ptr server = nullptr); /* Delete the base class clear() which is inherited from the fields<> class and doesn't * clear things like the body or obviously our own members. @@ -270,7 +270,7 @@ public: JsonEncoder GetJsonEncoder(bool pretty = false); private: - using Serializer = boost::beast::http::response_serializer; + using Serializer = boost::beast::http::response_serializer; Serializer m_Serializer{*this}; bool m_SerializationStarted = false; diff --git a/lib/remote/httpserverconnection.cpp b/lib/remote/httpserverconnection.cpp index 6ae147452..c8af94a1c 100644 --- a/lib/remote/httpserverconnection.cpp +++ b/lib/remote/httpserverconnection.cpp @@ -122,7 +122,7 @@ void HttpServerConnection::StartDetectClientSideShutdown() * If this async_fill() then buffers more application data and not an immediate eof, we could * attempt to read another message before disconnecting. * - * This could either be done at the level of the handlers, via the @c HttpResponse class, or + * This could either be done at the level of the handlers, via the @c HttpApiResponse class, or * generally as a separate coroutine here in @c HttpServerConnection, both (mostly) side-effect * free and without affecting the state of the connection. * @@ -159,8 +159,8 @@ void HttpServerConnection::SetLivenessTimeout(std::chrono::milliseconds timeout) static inline bool EnsureValidHeaders( boost::beast::flat_buffer& buf, - HttpRequest& request, - HttpResponse& response, + HttpApiRequest& request, + HttpApiResponse& response, bool& shuttingDown, boost::asio::yield_context& yc ) @@ -216,14 +216,14 @@ bool EnsureValidHeaders( static inline void HandleExpect100( const Shared::Ptr& stream, - const HttpRequest& request, + const HttpApiRequest& request, boost::asio::yield_context& yc ) { namespace http = boost::beast::http; if (request[http::field::expect] == "100-continue") { - HttpResponse response{stream}; + HttpApiResponse response{stream}; response.result(http::status::continue_); response.Flush(yc); } @@ -231,8 +231,8 @@ void HandleExpect100( static inline bool HandleAccessControl( - const HttpRequest& request, - HttpResponse& response, + const HttpApiRequest& request, + HttpApiResponse& response, boost::asio::yield_context& yc ) { @@ -275,8 +275,8 @@ bool HandleAccessControl( static inline bool EnsureAcceptHeader( - const HttpRequest& request, - HttpResponse& response, + const HttpApiRequest& request, + HttpApiResponse& response, boost::asio::yield_context& yc ) { @@ -298,8 +298,8 @@ bool EnsureAcceptHeader( static inline bool EnsureAuthenticatedUser( - const HttpRequest& request, - HttpResponse& response, + const HttpApiRequest& request, + HttpApiResponse& response, boost::asio::yield_context& yc ) { @@ -331,8 +331,8 @@ bool EnsureAuthenticatedUser( static inline bool EnsureValidBody( boost::beast::flat_buffer& buf, - HttpRequest& request, - HttpResponse& response, + HttpApiRequest& request, + HttpApiResponse& response, bool& shuttingDown, boost::asio::yield_context& yc ) @@ -413,8 +413,8 @@ bool EnsureValidBody( static inline void ProcessRequest( - HttpRequest& request, - HttpResponse& response, + HttpApiRequest& request, + HttpApiResponse& response, const WaitGroup::Ptr& waitGroup, std::chrono::steady_clock::duration& cpuBoundWorkTime, boost::asio::yield_context& yc @@ -459,8 +459,8 @@ void HttpServerConnection::ProcessMessages(boost::asio::yield_context yc) while (m_WaitGroup->IsLockable()) { m_Seen = ch::steady_clock::now(); - HttpRequest request(m_Stream); - HttpResponse response(m_Stream, this); + HttpApiRequest request(m_Stream); + HttpApiResponse response(m_Stream, this); request.Parser().header_limit(1024 * 1024); request.Parser().body_limit(-1); diff --git a/lib/remote/httputility.cpp b/lib/remote/httputility.cpp index fbf95f2a9..15286fd5b 100644 --- a/lib/remote/httputility.cpp +++ b/lib/remote/httputility.cpp @@ -52,7 +52,7 @@ Value HttpUtility::GetLastParameter(const Dictionary::Ptr& params, const String& return arr->Get(arr->GetLength() - 1); } -void HttpUtility::SendJsonBody(HttpResponse& response, const Dictionary::Ptr& params, const Value& val) +void HttpUtility::SendJsonBody(HttpApiResponse& response, const Dictionary::Ptr& params, const Value& val) { namespace http = boost::beast::http; @@ -60,7 +60,7 @@ void HttpUtility::SendJsonBody(HttpResponse& response, const Dictionary::Ptr& pa response.GetJsonEncoder(params && GetLastParameter(params, "pretty")).Encode(val); } -void HttpUtility::SendJsonError(HttpResponse& response, +void HttpUtility::SendJsonError(HttpApiResponse& response, const Dictionary::Ptr& params, int code, const String& info, const String& diagnosticInformation) { Dictionary::Ptr result = new Dictionary({ { "error", code } }); diff --git a/lib/remote/httputility.hpp b/lib/remote/httputility.hpp index 6ad1720ea..76147aa4c 100644 --- a/lib/remote/httputility.hpp +++ b/lib/remote/httputility.hpp @@ -23,8 +23,8 @@ 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(HttpResponse& response, const Dictionary::Ptr& params, const Value& val); - static void SendJsonError(HttpResponse& response, const Dictionary::Ptr& params, const int code, + static void SendJsonBody(HttpApiResponse& response, const Dictionary::Ptr& params, const Value& val); + static void SendJsonError(HttpApiResponse& response, const Dictionary::Ptr& params, const int code, const String& info = {}, const String& diagnosticInformation = {}); static bool IsValidHeaderName(std::string_view name); diff --git a/lib/remote/infohandler.cpp b/lib/remote/infohandler.cpp index 52d7c4b26..893705b1a 100644 --- a/lib/remote/infohandler.cpp +++ b/lib/remote/infohandler.cpp @@ -10,8 +10,8 @@ REGISTER_URLHANDLER("/", InfoHandler); bool InfoHandler::HandleRequest( const WaitGroup::Ptr&, - const HttpRequest& request, - HttpResponse& response, + const HttpApiRequest& request, + HttpApiResponse& response, boost::asio::yield_context& yc ) { diff --git a/lib/remote/infohandler.hpp b/lib/remote/infohandler.hpp index e62a497ff..e061179ff 100644 --- a/lib/remote/infohandler.hpp +++ b/lib/remote/infohandler.hpp @@ -15,8 +15,8 @@ public: bool HandleRequest( const WaitGroup::Ptr& waitGroup, - const HttpRequest& request, - HttpResponse& response, + const HttpApiRequest& request, + HttpApiResponse& response, boost::asio::yield_context& yc ) override; }; diff --git a/lib/remote/mallocinfohandler.cpp b/lib/remote/mallocinfohandler.cpp index 4ca37d555..f8351d54f 100644 --- a/lib/remote/mallocinfohandler.cpp +++ b/lib/remote/mallocinfohandler.cpp @@ -19,8 +19,8 @@ REGISTER_URLHANDLER("/v1/debug/malloc_info", MallocInfoHandler); bool MallocInfoHandler::HandleRequest( const WaitGroup::Ptr&, - const HttpRequest& request, - HttpResponse& response, + const HttpApiRequest& request, + HttpApiResponse& response, boost::asio::yield_context& ) { diff --git a/lib/remote/mallocinfohandler.hpp b/lib/remote/mallocinfohandler.hpp index 10d8b162f..c14ab7776 100644 --- a/lib/remote/mallocinfohandler.hpp +++ b/lib/remote/mallocinfohandler.hpp @@ -14,8 +14,8 @@ public: bool HandleRequest( const WaitGroup::Ptr& waitGroup, - const HttpRequest& request, - HttpResponse& response, + const HttpApiRequest& request, + HttpApiResponse& response, boost::asio::yield_context& yc ) override; }; diff --git a/lib/remote/modifyobjecthandler.cpp b/lib/remote/modifyobjecthandler.cpp index 9264e3c64..71f6e068b 100644 --- a/lib/remote/modifyobjecthandler.cpp +++ b/lib/remote/modifyobjecthandler.cpp @@ -15,8 +15,8 @@ REGISTER_URLHANDLER("/v1/objects", ModifyObjectHandler); bool ModifyObjectHandler::HandleRequest( const WaitGroup::Ptr& waitGroup, - const HttpRequest& request, - HttpResponse& response, + const HttpApiRequest& request, + HttpApiResponse& response, boost::asio::yield_context& yc ) { diff --git a/lib/remote/modifyobjecthandler.hpp b/lib/remote/modifyobjecthandler.hpp index abc7f9735..c59509cc9 100644 --- a/lib/remote/modifyobjecthandler.hpp +++ b/lib/remote/modifyobjecthandler.hpp @@ -15,8 +15,8 @@ public: bool HandleRequest( const WaitGroup::Ptr& waitGroup, - const HttpRequest& request, - HttpResponse& response, + const HttpApiRequest& request, + HttpApiResponse& response, boost::asio::yield_context& yc ) override; }; diff --git a/lib/remote/objectqueryhandler.cpp b/lib/remote/objectqueryhandler.cpp index 4384abb55..10757aa1e 100644 --- a/lib/remote/objectqueryhandler.cpp +++ b/lib/remote/objectqueryhandler.cpp @@ -93,8 +93,8 @@ Dictionary::Ptr ObjectQueryHandler::SerializeObjectAttrs(const Object::Ptr& obje bool ObjectQueryHandler::HandleRequest( const WaitGroup::Ptr&, - const HttpRequest& request, - HttpResponse& response, + const HttpApiRequest& request, + HttpApiResponse& response, boost::asio::yield_context& yc ) { diff --git a/lib/remote/objectqueryhandler.hpp b/lib/remote/objectqueryhandler.hpp index 1c7d25afd..9a341c644 100644 --- a/lib/remote/objectqueryhandler.hpp +++ b/lib/remote/objectqueryhandler.hpp @@ -15,8 +15,8 @@ public: bool HandleRequest( const WaitGroup::Ptr& waitGroup, - const HttpRequest& request, - HttpResponse& response, + const HttpApiRequest& request, + HttpApiResponse& response, boost::asio::yield_context& yc ) override; diff --git a/lib/remote/statushandler.cpp b/lib/remote/statushandler.cpp index 8a16ad81e..b11ff616f 100644 --- a/lib/remote/statushandler.cpp +++ b/lib/remote/statushandler.cpp @@ -70,8 +70,8 @@ public: bool StatusHandler::HandleRequest( const WaitGroup::Ptr&, - const HttpRequest& request, - HttpResponse& response, + const HttpApiRequest& request, + HttpApiResponse& response, boost::asio::yield_context& yc ) { diff --git a/lib/remote/statushandler.hpp b/lib/remote/statushandler.hpp index dceb58ac2..6b18564f0 100644 --- a/lib/remote/statushandler.hpp +++ b/lib/remote/statushandler.hpp @@ -15,8 +15,8 @@ public: bool HandleRequest( const WaitGroup::Ptr& waitGroup, - const HttpRequest& request, - HttpResponse& response, + const HttpApiRequest& request, + HttpApiResponse& response, boost::asio::yield_context& yc ) override; }; diff --git a/lib/remote/templatequeryhandler.cpp b/lib/remote/templatequeryhandler.cpp index 81261f02d..c2fe11f72 100644 --- a/lib/remote/templatequeryhandler.cpp +++ b/lib/remote/templatequeryhandler.cpp @@ -77,8 +77,8 @@ public: bool TemplateQueryHandler::HandleRequest( const WaitGroup::Ptr&, - const HttpRequest& request, - HttpResponse& response, + const HttpApiRequest& request, + HttpApiResponse& response, boost::asio::yield_context& yc ) { diff --git a/lib/remote/templatequeryhandler.hpp b/lib/remote/templatequeryhandler.hpp index 3b3b58cc4..9372f0168 100644 --- a/lib/remote/templatequeryhandler.hpp +++ b/lib/remote/templatequeryhandler.hpp @@ -15,8 +15,8 @@ public: bool HandleRequest( const WaitGroup::Ptr& waitGroup, - const HttpRequest& request, - HttpResponse& response, + const HttpApiRequest& request, + HttpApiResponse& response, boost::asio::yield_context& yc ) override; }; diff --git a/lib/remote/typequeryhandler.cpp b/lib/remote/typequeryhandler.cpp index dda19cd12..d303e5c5d 100644 --- a/lib/remote/typequeryhandler.cpp +++ b/lib/remote/typequeryhandler.cpp @@ -48,8 +48,8 @@ public: bool TypeQueryHandler::HandleRequest( const WaitGroup::Ptr&, - const HttpRequest& request, - HttpResponse& response, + const HttpApiRequest& request, + HttpApiResponse& response, boost::asio::yield_context& yc ) { diff --git a/lib/remote/typequeryhandler.hpp b/lib/remote/typequeryhandler.hpp index f065d2471..0e1a9aedb 100644 --- a/lib/remote/typequeryhandler.hpp +++ b/lib/remote/typequeryhandler.hpp @@ -15,8 +15,8 @@ public: bool HandleRequest( const WaitGroup::Ptr& waitGroup, - const HttpRequest& request, - HttpResponse& response, + const HttpApiRequest& request, + HttpApiResponse& response, boost::asio::yield_context& yc ) override; }; diff --git a/lib/remote/variablequeryhandler.cpp b/lib/remote/variablequeryhandler.cpp index b67fe906f..b0bfc51de 100644 --- a/lib/remote/variablequeryhandler.cpp +++ b/lib/remote/variablequeryhandler.cpp @@ -60,8 +60,8 @@ public: bool VariableQueryHandler::HandleRequest( const WaitGroup::Ptr&, - const HttpRequest& request, - HttpResponse& response, + const HttpApiRequest& request, + HttpApiResponse& response, boost::asio::yield_context& yc ) { diff --git a/lib/remote/variablequeryhandler.hpp b/lib/remote/variablequeryhandler.hpp index b6706037e..ef745d4c0 100644 --- a/lib/remote/variablequeryhandler.hpp +++ b/lib/remote/variablequeryhandler.hpp @@ -15,8 +15,8 @@ public: bool HandleRequest( const WaitGroup::Ptr& waitGroup, - const HttpRequest& request, - HttpResponse& response, + const HttpApiRequest& request, + HttpApiResponse& response, boost::asio::yield_context& yc ) override; }; diff --git a/test/remote-httpmessage.cpp b/test/remote-httpmessage.cpp index 30adb44e3..96a43a357 100644 --- a/test/remote-httpmessage.cpp +++ b/test/remote-httpmessage.cpp @@ -49,7 +49,7 @@ BOOST_AUTO_TEST_CASE(request_parse) auto future = SpawnSynchronizedCoroutine([this, &requestOut](boost::asio::yield_context yc) { boost::beast::flat_buffer buf; - HttpRequest request(server); + HttpApiRequest request(server); BOOST_REQUIRE_NO_THROW(request.ParseHeader(buf, yc)); for (const auto& field : requestOut.base()) { @@ -71,7 +71,7 @@ BOOST_AUTO_TEST_CASE(request_parse) BOOST_AUTO_TEST_CASE(request_params) { - HttpRequest request(client); + HttpApiRequest request(client); // clang-format off request.body() = JsonEncode( new Dictionary{ @@ -119,7 +119,7 @@ BOOST_AUTO_TEST_CASE(request_params) BOOST_AUTO_TEST_CASE(response_clear) { - HttpResponse response(server); + HttpApiResponse response(server); response.result(http::status::bad_request); response.version(10); response.set(http::field::content_type, "text/html"); @@ -136,7 +136,7 @@ BOOST_AUTO_TEST_CASE(response_clear) BOOST_AUTO_TEST_CASE(response_flush_nothrow) { auto future = SpawnSynchronizedCoroutine([this](const boost::asio::yield_context& yc) { - HttpResponse response(server); + HttpApiResponse response(server); response.result(http::status::ok); server->lowest_layer().close(); @@ -153,7 +153,7 @@ BOOST_AUTO_TEST_CASE(response_flush_nothrow) BOOST_AUTO_TEST_CASE(response_flush_throw) { auto future = SpawnSynchronizedCoroutine([this](const boost::asio::yield_context& yc) { - HttpResponse response(server); + HttpApiResponse response(server); response.result(http::status::ok); server->lowest_layer().close(); @@ -171,7 +171,7 @@ BOOST_AUTO_TEST_CASE(response_flush_throw) BOOST_AUTO_TEST_CASE(response_write_empty) { auto future = SpawnSynchronizedCoroutine([this](boost::asio::yield_context yc) { - HttpResponse response(server); + HttpApiResponse response(server); response.result(http::status::ok); BOOST_REQUIRE_NO_THROW(response.Flush(yc)); @@ -197,7 +197,7 @@ BOOST_AUTO_TEST_CASE(response_write_empty) BOOST_AUTO_TEST_CASE(response_write_fixed) { auto future = SpawnSynchronizedCoroutine([this](boost::asio::yield_context yc) { - HttpResponse response(server); + HttpApiResponse response(server); response.result(http::status::ok); response.body() << "test"; @@ -225,7 +225,7 @@ BOOST_AUTO_TEST_CASE(response_write_chunked) { // NOLINTNEXTLINE(readability-function-cognitive-complexity) auto future = SpawnSynchronizedCoroutine([this](boost::asio::yield_context yc) { - HttpResponse response(server); + HttpApiResponse response(server); response.result(http::status::ok); response.StartStreaming(); @@ -263,7 +263,7 @@ BOOST_AUTO_TEST_CASE(response_write_chunked) BOOST_AUTO_TEST_CASE(response_sendjsonbody) { auto future = SpawnSynchronizedCoroutine([this](boost::asio::yield_context yc) { - HttpResponse response(server); + HttpApiResponse response(server); response.result(http::status::ok); HttpUtility::SendJsonBody(response, nullptr, new Dictionary{{"test", 1}}); @@ -292,7 +292,7 @@ BOOST_AUTO_TEST_CASE(response_sendjsonbody) BOOST_AUTO_TEST_CASE(response_sendjsonerror) { auto future = SpawnSynchronizedCoroutine([this](boost::asio::yield_context yc) { - HttpResponse response(server); + HttpApiResponse response(server); // This has to be overwritten in SendJsonError. response.result(http::status::ok); @@ -324,7 +324,7 @@ BOOST_AUTO_TEST_CASE(response_sendjsonerror) BOOST_AUTO_TEST_CASE(response_sendfile) { auto future = SpawnSynchronizedCoroutine([this](boost::asio::yield_context yc) { - HttpResponse response(server); + HttpApiResponse response(server); response.result(http::status::ok); BOOST_REQUIRE_NO_THROW(response.SendFile(m_CaCrtFile.string(), yc)); diff --git a/test/remote-httpserverconnection.cpp b/test/remote-httpserverconnection.cpp index a2cb9ff2a..4a37f0ec0 100644 --- a/test/remote-httpserverconnection.cpp +++ b/test/remote-httpserverconnection.cpp @@ -67,12 +67,12 @@ struct HttpServerConnectionFixture : TlsStreamFixture, ConfigurationCacheDirFixt class UnitTestHandler final : public HttpHandler { public: - using TestFn = std::function; + using TestFn = std::function; static void RegisterTestFn(std::string handle, TestFn fn) { testFns[std::move(handle)] = std::move(fn); } private: - bool HandleRequest(const WaitGroup::Ptr&, const HttpRequest& request, HttpResponse& response, + bool HandleRequest(const WaitGroup::Ptr&, const HttpApiRequest& request, HttpApiResponse& response, boost::asio::yield_context& yc) override { response.result(boost::beast::http::status::ok); @@ -381,7 +381,7 @@ BOOST_AUTO_TEST_CASE(wg_abort) CreateTestUsers(); SetupHttpServerConnection(true); - UnitTestHandler::RegisterTestFn("wgjoin", [this](HttpResponse& response, const boost::asio::yield_context&) { + UnitTestHandler::RegisterTestFn("wgjoin", [this](HttpApiResponse& response, const boost::asio::yield_context&) { response.body() << "test"; m_WaitGroup->Join(); }); @@ -421,7 +421,7 @@ BOOST_AUTO_TEST_CASE(client_shutdown) CreateTestUsers(); SetupHttpServerConnection(true); - UnitTestHandler::RegisterTestFn("stream", [](HttpResponse& response, const boost::asio::yield_context& yc) { + UnitTestHandler::RegisterTestFn("stream", [](HttpApiResponse& response, const boost::asio::yield_context& yc) { response.StartStreaming(); response.Flush(yc); @@ -470,7 +470,7 @@ BOOST_AUTO_TEST_CASE(handler_throw_error) CreateTestUsers(); SetupHttpServerConnection(true); - UnitTestHandler::RegisterTestFn("throw", [](HttpResponse& response, const boost::asio::yield_context&) { + UnitTestHandler::RegisterTestFn("throw", [](HttpApiResponse& response, const boost::asio::yield_context&) { response.StartStreaming(); response.body() << "test"; @@ -508,7 +508,7 @@ BOOST_AUTO_TEST_CASE(handler_throw_streaming) CreateTestUsers(); SetupHttpServerConnection(true); - UnitTestHandler::RegisterTestFn("throw", [](HttpResponse& response, const boost::asio::yield_context& yc) { + UnitTestHandler::RegisterTestFn("throw", [](HttpApiResponse& response, const boost::asio::yield_context& yc) { response.StartStreaming(); response.body() << "test"; From 1505f09ed66a8c5722fa869112b939cbf3a65eee Mon Sep 17 00:00:00 2001 From: Johannes Schmidt Date: Thu, 22 Jan 2026 12:41:33 +0100 Subject: [PATCH 2/2] Refactor HttpMessage into generalized templated types This adds generalized IncomingHttpMessage and OutgoingHttpMessage templates that support different types of streams (via a std::variant) and can both be used for either requests or responses. The tacked on metadata from the old HttpRequest and server connection from the old HttpServerConnection have been moved to HttpApi(Request|Response) classes that derive from the above generalized message types. --- lib/base/tlsstream.hpp | 7 +- lib/remote/httpmessage.cpp | 151 ++++++++++++++++------- lib/remote/httpmessage.hpp | 175 ++++++++++++++++++--------- lib/remote/objectqueryhandler.cpp | 2 +- test/remote-httpmessage.cpp | 2 +- test/remote-httpserverconnection.cpp | 6 +- 6 files changed, 237 insertions(+), 106 deletions(-) diff --git a/lib/base/tlsstream.hpp b/lib/base/tlsstream.hpp index 9eed8d3b1..4bff9d629 100644 --- a/lib/base/tlsstream.hpp +++ b/lib/base/tlsstream.hpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -122,9 +123,9 @@ private: } }; -typedef boost::asio::buffered_stream AsioTcpStream; -typedef std::pair::Ptr, Shared::Ptr> OptionalTlsStream; - +using AsioTcpStream = boost::asio::buffered_stream; +using OptionalTlsStream = std::pair::Ptr, Shared::Ptr>; +using AsioTlsOrTcpStream = std::variant::Ptr, Shared::Ptr>; } #endif /* TLSSTREAM_H */ diff --git a/lib/remote/httpmessage.cpp b/lib/remote/httpmessage.cpp index 6ad761b8a..c3b80dafc 100644 --- a/lib/remote/httpmessage.cpp +++ b/lib/remote/httpmessage.cpp @@ -1,7 +1,6 @@ /* Icinga 2 | (c) 2025 Icinga GmbH | GPLv2+ */ #include "remote/httpmessage.hpp" -#include "base/io-engine.hpp" #include "base/json.hpp" #include "remote/httputility.hpp" #include "remote/url.hpp" @@ -27,10 +26,15 @@ constexpr std::size_t l_FlushThreshold = 128UL * 1024UL; * * @ingroup base */ +template class HttpResponseJsonWriter : public AsyncJsonWriter { public: - explicit HttpResponseJsonWriter(HttpApiResponse& msg) : m_Message{msg} + HttpResponseJsonWriter(const HttpResponseJsonWriter&) = delete; + HttpResponseJsonWriter(HttpResponseJsonWriter&&) = delete; + HttpResponseJsonWriter& operator=(const HttpResponseJsonWriter&) = delete; + HttpResponseJsonWriter& operator=(HttpResponseJsonWriter&&) = delete; + explicit HttpResponseJsonWriter(Message& msg) : m_Message{msg} { m_Message.body().Start(); #if BOOST_VERSION >= 107000 @@ -59,23 +63,37 @@ public: } private: - HttpApiResponse& m_Message; + Message& m_Message; }; -HttpApiRequest::HttpApiRequest(Shared::Ptr stream) : m_Stream(std::move(stream)) +template +IncomingHttpMessage::IncomingHttpMessage(StreamVariant stream) + : m_Stream(std::move(stream)) { } -void HttpApiRequest::ParseHeader(boost::beast::flat_buffer& buf, boost::asio::yield_context yc) +template +void IncomingHttpMessage::ParseHeader( + boost::beast::flat_buffer& buf, + boost::asio::yield_context yc +) { - boost::beast::http::async_read_header(*m_Stream, buf, m_Parser, yc); - base() = m_Parser.get().base(); + std::visit([&](auto& stream) { boost::beast::http::async_read_header(*stream, buf, m_Parser, yc); }, m_Stream); + Base::base() = m_Parser.get().base(); } -void HttpApiRequest::ParseBody(boost::beast::flat_buffer& buf, boost::asio::yield_context yc) +template +void IncomingHttpMessage::ParseBody( + boost::beast::flat_buffer& buf, + boost::asio::yield_context yc +) +{ + std::visit([&](auto& stream) { boost::beast::http::async_read(*stream, buf, m_Parser, yc); }, m_Stream); + Base::body() = std::move(m_Parser.release().body()); +} + +HttpApiRequest::HttpApiRequest(Shared::Ptr stream) : IncomingHttpMessage(std::move(stream)) { - boost::beast::http::async_read(*m_Stream, buf, m_Parser, yc); - body() = std::move(m_Parser.release().body()); } ApiUser::Ptr HttpApiRequest::User() const @@ -111,49 +129,72 @@ void HttpApiRequest::DecodeParams() m_Params = HttpUtility::FetchRequestParameters(m_Url, body()); } -HttpApiResponse::HttpApiResponse(Shared::Ptr stream, HttpServerConnection::Ptr server) - : m_Server(std::move(server)), m_Stream(std::move(stream)) +template +OutgoingHttpMessage::OutgoingHttpMessage(StreamVariant stream) + : m_Stream(std::move(stream)) { } -void HttpApiResponse::Clear() +template +void OutgoingHttpMessage::Clear() { ASSERT(!m_SerializationStarted); - boost::beast::http::response::operator=({}); + Base::operator=({}); } -void HttpApiResponse::Flush(boost::asio::yield_context yc) +template +void OutgoingHttpMessage::Flush(boost::asio::yield_context yc, bool finish) { - if (!chunked() && !has_content_length()) { + if (!Base::chunked() && !Base::has_content_length()) { ASSERT(!m_SerializationStarted); - prepare_payload(); + Base::prepare_payload(); } - m_SerializationStarted = true; + std::visit( + [&](auto& stream) { + m_SerializationStarted = true; - if (!m_Serializer.is_header_done()) { - boost::beast::http::write_header(*m_Stream, m_Serializer); - } + if (!m_Serializer.is_header_done()) { + boost::beast::http::write_header(*stream, m_Serializer); + } - boost::system::error_code ec; - boost::beast::http::async_write(*m_Stream, m_Serializer, yc[ec]); - if (ec && ec != boost::beast::http::error::need_buffer) { - if (yc.ec_) { - *yc.ec_ = ec; - return; - } - BOOST_THROW_EXCEPTION(boost::system::system_error{ec}); - } - m_Stream->async_flush(yc); + if (finish) { + Base::body().Finish(); + } - ASSERT(m_Serializer.is_done() || !body().Finished()); + boost::system::error_code ec; + boost::beast::http::async_write(*stream, m_Serializer, yc[ec]); + if (ec && ec != boost::beast::http::error::need_buffer) { + if (yc.ec_) { + *yc.ec_ = ec; + return; + } + BOOST_THROW_EXCEPTION(boost::system::system_error{ec}); + } + stream->async_flush(yc); + + ASSERT(m_Serializer.is_done() || !Base::body().Finished()); + }, + m_Stream + ); +} + +template +void OutgoingHttpMessage::StartStreaming() +{ + ASSERT(Base::body().Size() == 0 && !m_SerializationStarted); + Base::body().Start(); + Base::chunked(true); +} + +HttpApiResponse::HttpApiResponse(Shared::Ptr stream, HttpServerConnection::Ptr server) + : OutgoingHttpMessage(std::move(stream)), m_Server(std::move(server)) +{ } void HttpApiResponse::StartStreaming(bool checkForDisconnect) { - ASSERT(body().Size() == 0 && !m_SerializationStarted); - body().Start(); - chunked(true); + OutgoingHttpMessage::StartStreaming(); if (checkForDisconnect) { ASSERT(m_Server); @@ -167,7 +208,11 @@ bool HttpApiResponse::IsClientDisconnected() const return m_Server->Disconnected(); } -void HttpApiResponse::SendFile(const String& path, const boost::asio::yield_context& yc) +template +void OutgoingHttpMessage::SendFile( + const String& path, + const boost::asio::yield_context& yc +) { std::ifstream fp(path.CStr(), std::ifstream::in | std::ifstream::binary | std::ifstream::ate); fp.exceptions(std::ifstream::badbit | std::ifstream::eofbit); @@ -175,22 +220,44 @@ void HttpApiResponse::SendFile(const String& path, const boost::asio::yield_cont std::uint64_t remaining = fp.tellg(); fp.seekg(0); - content_length(remaining); - body().Start(); + Base::content_length(remaining); + Base::body().Start(); while (remaining) { auto maxTransfer = std::min(remaining, static_cast(l_FlushThreshold)); - auto buf = *body().Buffer().prepare(maxTransfer).begin(); + using BodyBuffer = std::decay_t().Buffer())>; + using BufferOrSequence = typename BodyBuffer::mutable_buffers_type; + + boost::asio::mutable_buffer buf; + + if constexpr (!std::is_same_v) { + buf = *Base::body().Buffer().prepare(maxTransfer).begin(); + } else { + buf = Base::body().Buffer().prepare(maxTransfer); + } fp.read(static_cast(buf.data()), buf.size()); - body().Buffer().commit(buf.size()); + Base::body().Buffer().commit(buf.size()); remaining -= buf.size(); Flush(yc); } } -JsonEncoder HttpApiResponse::GetJsonEncoder(bool pretty) +template +JsonEncoder OutgoingHttpMessage::GetJsonEncoder(bool pretty) { - return JsonEncoder{std::make_shared(*this), pretty}; + return JsonEncoder{ + std::make_shared>>(*this), pretty + }; } + +// More general instantiations +template class icinga::OutgoingHttpMessage; +template class icinga::OutgoingHttpMessage; +template class icinga::IncomingHttpMessage; +template class icinga::IncomingHttpMessage; + +// Instantiations specifically for HttpApi(Request|Response) +template class icinga::IncomingHttpMessage::Ptr>>; +template class icinga::OutgoingHttpMessage::Ptr>>; diff --git a/lib/remote/httpmessage.hpp b/lib/remote/httpmessage.hpp index ef0f70bed..b26d48480 100644 --- a/lib/remote/httpmessage.hpp +++ b/lib/remote/httpmessage.hpp @@ -10,6 +10,7 @@ #include "remote/url.hpp" #include #include +#include namespace icinga { @@ -143,17 +144,17 @@ struct SerializableBody }; }; -/** - * A wrapper class for a boost::beast HTTP request - * - * @ingroup remote - */ -class HttpApiRequest : public boost::beast::http::request -{ -public: - using ParserType = boost::beast::http::request_parser; +using SerializableMultiBufferBody = SerializableBody; +using SerializableFlatBufferBody = SerializableBody; - explicit HttpApiRequest(Shared::Ptr stream); +template +class IncomingHttpMessage : public boost::beast::http::message +{ + using ParserType = boost::beast::http::parser; + using Base = boost::beast::http::message; + +public: + explicit IncomingHttpMessage(StreamVariant stream); /** * Parse the header of the response using the internal parser object. @@ -176,34 +177,23 @@ public: ParserType& Parser() { return m_Parser; } - [[nodiscard]] ApiUser::Ptr User() const; - void User(const ApiUser::Ptr& user); - - [[nodiscard]] icinga::Url::Ptr Url() const; - void DecodeUrl(); - - [[nodiscard]] Dictionary::Ptr Params() const; - void DecodeParams(); - private: - ApiUser::Ptr m_User; - Url::Ptr m_Url; - Dictionary::Ptr m_Params; - ParserType m_Parser; - Shared::Ptr m_Stream; + StreamVariant m_Stream; }; -/** - * A wrapper class for a boost::beast HTTP response - * - * @ingroup remote - */ -class HttpApiResponse : public boost::beast::http::response> +using IncomingHttpRequest = IncomingHttpMessage; +using IncomingHttpResponse = IncomingHttpMessage; + +template +class OutgoingHttpMessage : public boost::beast::http::message { + using Serializer = boost::beast::http::serializer; + using Base = boost::beast::http::message; + public: - explicit HttpApiResponse(Shared::Ptr stream, HttpServerConnection::Ptr server = nullptr); + explicit OutgoingHttpMessage(StreamVariant stream); /* Delete the base class clear() which is inherited from the fields<> class and doesn't * clear things like the body or obviously our own members. @@ -217,6 +207,32 @@ public: */ void Clear(); + /** + * Commits the specified number of bytes (previously obtained via @c prepare()) for reading. + * + * This function makes the specified number of bytes available in the body buffer for reading. + * + * @param size The number of bytes to commit + */ + void Commit(std::size_t size) { Base::body().Buffer().commit(size); } + + /** + * Prepare a buffer of the specified size for writing. + * + * The returned buffer serves just as a view onto the internal buffer sequence but does not actually + * own the memory. Thus, destroying the returned buffer will not free any memory it represents. + * + * @param size The size of the buffer to prepare + * + * @return A mutable buffer representing the prepared space + */ + auto Prepare(std::size_t size) { return Base::body().Buffer().prepare(size); } + + /** + * Enables chunked encoding. + */ + void StartStreaming(); + /** * Writes as much of the response as is currently available. * @@ -228,31 +244,10 @@ public: * * @param yc The yield_context for this operation */ - void Flush(boost::asio::yield_context yc); + void Flush(boost::asio::yield_context yc, bool finish = false); [[nodiscard]] bool HasSerializationStarted() const { return m_SerializationStarted; } - /** - * Enables chunked encoding. - * - * Optionally starts a coroutine that reads from the stream and checks for client-side - * disconnects. In this case, the stream can not be reused after the response has been - * sent and any further requests sent over the connections will be discarded, even if - * no client-side disconnect occurs. This requires that this object has been constructed - * with a valid HttpServerConnection::Ptr. - * - * @param checkForDisconnect Whether to start a coroutine to detect disconnects - */ - void StartStreaming(bool checkForDisconnect = false); - - /** - * Check if the server has initiated a disconnect. - * - * @note This requires that the message has been constructed with a pointer to the - * @c HttpServerConnection. - */ - [[nodiscard]] bool IsClientDisconnected() const; - /** * Sends the contents of a file. * @@ -270,12 +265,80 @@ public: JsonEncoder GetJsonEncoder(bool pretty = false); private: - using Serializer = boost::beast::http::response_serializer; Serializer m_Serializer{*this}; bool m_SerializationStarted = false; - HttpServerConnection::Ptr m_Server; - Shared::Ptr m_Stream; + StreamVariant m_Stream; }; +using OutgoingHttpRequest = OutgoingHttpMessage; +using OutgoingHttpResponse = OutgoingHttpMessage; + +class HttpApiRequest + : public IncomingHttpMessage::Ptr>> +{ +public: + explicit HttpApiRequest(Shared::Ptr stream); + + [[nodiscard]] ApiUser::Ptr User() const; + void User(const ApiUser::Ptr& user); + + [[nodiscard]] icinga::Url::Ptr Url() const; + void DecodeUrl(); + + [[nodiscard]] Dictionary::Ptr Params() const; + void DecodeParams(); + +private: + ApiUser::Ptr m_User; + Url::Ptr m_Url; + Dictionary::Ptr m_Params; +}; + +/** + * A wrapper class for a boost::beast HTTP response for the Icinga 2 API + * + * @ingroup remote + */ +class HttpApiResponse + : public OutgoingHttpMessage::Ptr>> +{ +public: + explicit HttpApiResponse(Shared::Ptr stream, HttpServerConnection::Ptr server = nullptr); + + /** + * Enables chunked encoding. + * + * Optionally starts a coroutine that reads from the stream and checks for client-side + * disconnects. In this case, the stream can not be reused after the response has been + * sent and any further requests sent over the connections will be discarded, even if + * no client-side disconnect occurs. This requires that this object has been constructed + * with a valid HttpServerConnection::Ptr. + * + * @param checkForDisconnect Whether to start a coroutine to detect disconnects + */ + void StartStreaming(bool checkForDisconnect); + + /** + * Check if the server has initiated a disconnect. + * + * @note This requires that the message has been constructed with a pointer to the + * @c HttpServerConnection. + */ + [[nodiscard]] bool IsClientDisconnected() const; + +private: + HttpServerConnection::Ptr m_Server; +}; + +// More general instantiations +extern template class OutgoingHttpMessage; +extern template class OutgoingHttpMessage; +extern template class IncomingHttpMessage; +extern template class IncomingHttpMessage; + +// Instantiations specifically for HttpApi(Request|Response) +extern template class IncomingHttpMessage::Ptr>>; +extern template class OutgoingHttpMessage::Ptr>>; + } // namespace icinga diff --git a/lib/remote/objectqueryhandler.cpp b/lib/remote/objectqueryhandler.cpp index 10757aa1e..fda93e1f0 100644 --- a/lib/remote/objectqueryhandler.cpp +++ b/lib/remote/objectqueryhandler.cpp @@ -328,7 +328,7 @@ bool ObjectQueryHandler::HandleRequest( response.result(http::status::ok); response.set(http::field::content_type, "application/json"); - response.StartStreaming(); + response.StartStreaming(false); Dictionary::Ptr results = new Dictionary{{"results", new ValueGenerator{generatorFunc}}}; results->Freeze(); diff --git a/test/remote-httpmessage.cpp b/test/remote-httpmessage.cpp index 96a43a357..62232a509 100644 --- a/test/remote-httpmessage.cpp +++ b/test/remote-httpmessage.cpp @@ -228,7 +228,7 @@ BOOST_AUTO_TEST_CASE(response_write_chunked) HttpApiResponse response(server); response.result(http::status::ok); - response.StartStreaming(); + response.StartStreaming(false); BOOST_REQUIRE_NO_THROW(response.Flush(yc)); BOOST_REQUIRE(response.HasSerializationStarted()); diff --git a/test/remote-httpserverconnection.cpp b/test/remote-httpserverconnection.cpp index 4a37f0ec0..1c3e747f8 100644 --- a/test/remote-httpserverconnection.cpp +++ b/test/remote-httpserverconnection.cpp @@ -422,7 +422,7 @@ BOOST_AUTO_TEST_CASE(client_shutdown) SetupHttpServerConnection(true); UnitTestHandler::RegisterTestFn("stream", [](HttpApiResponse& response, const boost::asio::yield_context& yc) { - response.StartStreaming(); + response.StartStreaming(false); response.Flush(yc); boost::asio::deadline_timer dt{IoEngine::Get().GetIoContext()}; @@ -471,7 +471,7 @@ BOOST_AUTO_TEST_CASE(handler_throw_error) SetupHttpServerConnection(true); UnitTestHandler::RegisterTestFn("throw", [](HttpApiResponse& response, const boost::asio::yield_context&) { - response.StartStreaming(); + response.StartStreaming(false); response.body() << "test"; boost::system::error_code ec{}; @@ -509,7 +509,7 @@ BOOST_AUTO_TEST_CASE(handler_throw_streaming) SetupHttpServerConnection(true); UnitTestHandler::RegisterTestFn("throw", [](HttpApiResponse& response, const boost::asio::yield_context& yc) { - response.StartStreaming(); + response.StartStreaming(false); response.body() << "test"; response.Flush(yc);