diff --git a/lib/remote/actionshandler.cpp b/lib/remote/actionshandler.cpp index 251f8a82e7f..7b61cd5b9d5 100644 --- a/lib/remote/actionshandler.cpp +++ b/lib/remote/actionshandler.cpp @@ -20,7 +20,7 @@ bool ActionsHandler::HandleRequest( boost::beast::http::response& response, const Dictionary::Ptr& params, boost::asio::yield_context& yc, - bool& hasStartedStreaming + HttpServerConnection& server ) { namespace http = boost::beast::http; diff --git a/lib/remote/actionshandler.hpp b/lib/remote/actionshandler.hpp index 9c046303581..c2465cf7e75 100644 --- a/lib/remote/actionshandler.hpp +++ b/lib/remote/actionshandler.hpp @@ -21,7 +21,7 @@ class ActionsHandler final : public HttpHandler boost::beast::http::response& response, const Dictionary::Ptr& params, boost::asio::yield_context& yc, - bool& hasStartedStreaming + HttpServerConnection& server ) override; }; diff --git a/lib/remote/configfileshandler.cpp b/lib/remote/configfileshandler.cpp index 05def86553f..d4c761c7b4d 100644 --- a/lib/remote/configfileshandler.cpp +++ b/lib/remote/configfileshandler.cpp @@ -20,7 +20,7 @@ bool ConfigFilesHandler::HandleRequest( boost::beast::http::response& response, const Dictionary::Ptr& params, boost::asio::yield_context& yc, - bool& hasStartedStreaming + HttpServerConnection& server ) { namespace http = boost::beast::http; diff --git a/lib/remote/configfileshandler.hpp b/lib/remote/configfileshandler.hpp index 1384c2b583b..ea48b1ef429 100644 --- a/lib/remote/configfileshandler.hpp +++ b/lib/remote/configfileshandler.hpp @@ -21,7 +21,7 @@ class ConfigFilesHandler final : public HttpHandler boost::beast::http::response& response, const Dictionary::Ptr& params, boost::asio::yield_context& yc, - bool& hasStartedStreaming + HttpServerConnection& server ) override; }; diff --git a/lib/remote/configpackageshandler.cpp b/lib/remote/configpackageshandler.cpp index 2505de36aee..d9cd9ec9b4f 100644 --- a/lib/remote/configpackageshandler.cpp +++ b/lib/remote/configpackageshandler.cpp @@ -18,7 +18,7 @@ bool ConfigPackagesHandler::HandleRequest( boost::beast::http::response& response, const Dictionary::Ptr& params, boost::asio::yield_context& yc, - bool& hasStartedStreaming + HttpServerConnection& server ) { namespace http = boost::beast::http; diff --git a/lib/remote/configpackageshandler.hpp b/lib/remote/configpackageshandler.hpp index 2ae184c6ab4..0a05ea10acf 100644 --- a/lib/remote/configpackageshandler.hpp +++ b/lib/remote/configpackageshandler.hpp @@ -21,7 +21,7 @@ class ConfigPackagesHandler final : public HttpHandler boost::beast::http::response& response, const Dictionary::Ptr& params, boost::asio::yield_context& yc, - bool& hasStartedStreaming + HttpServerConnection& server ) override; private: diff --git a/lib/remote/configstageshandler.cpp b/lib/remote/configstageshandler.cpp index 067181f2757..a3c570a2fe8 100644 --- a/lib/remote/configstageshandler.cpp +++ b/lib/remote/configstageshandler.cpp @@ -19,7 +19,7 @@ bool ConfigStagesHandler::HandleRequest( boost::beast::http::response& response, const Dictionary::Ptr& params, boost::asio::yield_context& yc, - bool& hasStartedStreaming + HttpServerConnection& server ) { namespace http = boost::beast::http; diff --git a/lib/remote/configstageshandler.hpp b/lib/remote/configstageshandler.hpp index 4ee35ad91cd..c6d6443667f 100644 --- a/lib/remote/configstageshandler.hpp +++ b/lib/remote/configstageshandler.hpp @@ -21,7 +21,7 @@ class ConfigStagesHandler final : public HttpHandler boost::beast::http::response& response, const Dictionary::Ptr& params, boost::asio::yield_context& yc, - bool& hasStartedStreaming + HttpServerConnection& server ) override; private: diff --git a/lib/remote/consolehandler.cpp b/lib/remote/consolehandler.cpp index 430a37cf3c4..0f0fddc8f60 100644 --- a/lib/remote/consolehandler.cpp +++ b/lib/remote/consolehandler.cpp @@ -61,7 +61,7 @@ bool ConsoleHandler::HandleRequest( boost::beast::http::response& response, const Dictionary::Ptr& params, boost::asio::yield_context& yc, - bool& hasStartedStreaming + HttpServerConnection& server ) { namespace http = boost::beast::http; diff --git a/lib/remote/consolehandler.hpp b/lib/remote/consolehandler.hpp index 339f3ed86a2..df0d77d0189 100644 --- a/lib/remote/consolehandler.hpp +++ b/lib/remote/consolehandler.hpp @@ -30,7 +30,7 @@ class ConsoleHandler final : public HttpHandler boost::beast::http::response& response, const Dictionary::Ptr& params, boost::asio::yield_context& yc, - bool& hasStartedStreaming + HttpServerConnection& server ) override; static std::vector GetAutocompletionSuggestions(const String& word, ScriptFrame& frame); diff --git a/lib/remote/createobjecthandler.cpp b/lib/remote/createobjecthandler.cpp index 1f7ab6710b8..c01b2364156 100644 --- a/lib/remote/createobjecthandler.cpp +++ b/lib/remote/createobjecthandler.cpp @@ -22,7 +22,7 @@ bool CreateObjectHandler::HandleRequest( boost::beast::http::response& response, const Dictionary::Ptr& params, boost::asio::yield_context& yc, - bool& hasStartedStreaming + HttpServerConnection& server ) { namespace http = boost::beast::http; diff --git a/lib/remote/createobjecthandler.hpp b/lib/remote/createobjecthandler.hpp index ff42a62e6ac..4bcf21b55bc 100644 --- a/lib/remote/createobjecthandler.hpp +++ b/lib/remote/createobjecthandler.hpp @@ -21,7 +21,7 @@ class CreateObjectHandler final : public HttpHandler boost::beast::http::response& response, const Dictionary::Ptr& params, boost::asio::yield_context& yc, - bool& hasStartedStreaming + HttpServerConnection& server ) override; }; diff --git a/lib/remote/deleteobjecthandler.cpp b/lib/remote/deleteobjecthandler.cpp index 15951588e01..2edb0e45519 100644 --- a/lib/remote/deleteobjecthandler.cpp +++ b/lib/remote/deleteobjecthandler.cpp @@ -22,7 +22,7 @@ bool DeleteObjectHandler::HandleRequest( boost::beast::http::response& response, const Dictionary::Ptr& params, boost::asio::yield_context& yc, - bool& hasStartedStreaming + HttpServerConnection& server ) { namespace http = boost::beast::http; diff --git a/lib/remote/deleteobjecthandler.hpp b/lib/remote/deleteobjecthandler.hpp index 3ecb0c71b56..19a46e475e9 100644 --- a/lib/remote/deleteobjecthandler.hpp +++ b/lib/remote/deleteobjecthandler.hpp @@ -21,7 +21,7 @@ class DeleteObjectHandler final : public HttpHandler boost::beast::http::response& response, const Dictionary::Ptr& params, boost::asio::yield_context& yc, - bool& hasStartedStreaming + HttpServerConnection& server ) override; }; diff --git a/lib/remote/eventqueue.cpp b/lib/remote/eventqueue.cpp index 6defc211482..017d3dc5795 100644 --- a/lib/remote/eventqueue.cpp +++ b/lib/remote/eventqueue.cpp @@ -5,6 +5,7 @@ #include "base/io-engine.hpp" #include "base/singleton.hpp" #include "base/logger.hpp" +#include "base/utility.hpp" #include using namespace icinga; @@ -102,19 +103,29 @@ Dictionary::Ptr EventQueue::WaitForEvent(void *client, double timeout) } } -Dictionary::Ptr EventQueue::WaitForEvent(void *client, boost::asio::yield_context yc) +Dictionary::Ptr EventQueue::WaitForEvent(void *client, boost::asio::yield_context yc, double timeout) { + double deadline = -1.0; + for (;;) { { - boost::mutex::scoped_lock lock(m_Mutex); - - auto it = m_Events.find(client); - ASSERT(it != m_Events.end()); - - if (!it->second.empty()) { - Dictionary::Ptr result = *it->second.begin(); - it->second.pop_front(); - return result; + boost::mutex::scoped_try_lock lock(m_Mutex); + + if (lock.owns_lock()) { + auto it = m_Events.find(client); + ASSERT(it != m_Events.end()); + + if (it->second.empty()) { + if (deadline == -1.0) { + deadline = Utility::GetTime() + timeout; + } else if (Utility::GetTime() >= deadline) { + return nullptr; + } + } else { + Dictionary::Ptr result = *it->second.begin(); + it->second.pop_front(); + return result; + } } } diff --git a/lib/remote/eventqueue.hpp b/lib/remote/eventqueue.hpp index 8f6a76c0b29..1a53baabb26 100644 --- a/lib/remote/eventqueue.hpp +++ b/lib/remote/eventqueue.hpp @@ -32,7 +32,7 @@ class EventQueue final : public Object void SetFilter(std::unique_ptr filter); Dictionary::Ptr WaitForEvent(void *client, double timeout = 5); - Dictionary::Ptr WaitForEvent(void *client, boost::asio::yield_context yc); + Dictionary::Ptr WaitForEvent(void *client, boost::asio::yield_context yc, double timeout = 5); static std::vector GetQueuesForType(const String& type); static void UnregisterIfUnused(const String& name, const EventQueue::Ptr& queue); diff --git a/lib/remote/eventshandler.cpp b/lib/remote/eventshandler.cpp index e6f895cf3ea..98e8153543a 100644 --- a/lib/remote/eventshandler.cpp +++ b/lib/remote/eventshandler.cpp @@ -25,7 +25,7 @@ bool EventsHandler::HandleRequest( boost::beast::http::response& response, const Dictionary::Ptr& params, boost::asio::yield_context& yc, - bool& hasStartedStreaming + HttpServerConnection& server ) { namespace asio = boost::asio; @@ -88,7 +88,7 @@ bool EventsHandler::HandleRequest( EventQueue::UnregisterIfUnused(queueName, queue); }); - hasStartedStreaming = true; + server.StartStreaming(); response.result(http::status::ok); response.set(http::field::content_type, "application/json"); @@ -101,19 +101,28 @@ bool EventsHandler::HandleRequest( } asio::const_buffer newLine ("\n", 1); + AsioConditionVariable dontLockOwnStrand (stream.get_io_service(), true); for (;;) { - String body = JsonEncode(queue->WaitForEvent(&request, yc)); + auto event (queue->WaitForEvent(&request, yc)); - boost::algorithm::replace_all(body, "\n", ""); + if (event) { + String body = JsonEncode(event); - asio::const_buffer payload (body.CStr(), body.GetLength()); + boost::algorithm::replace_all(body, "\n", ""); - IoBoundWorkSlot dontLockTheIoThreadWhileWriting (yc); + asio::const_buffer payload (body.CStr(), body.GetLength()); - asio::async_write(stream, payload, yc); - asio::async_write(stream, newLine, yc); - stream.async_flush(yc); + IoBoundWorkSlot dontLockTheIoThreadWhileWriting (yc); + + asio::async_write(stream, payload, yc); + asio::async_write(stream, newLine, yc); + stream.async_flush(yc); + } else if (server.Disconnected()) { + return true; + } else { + dontLockOwnStrand.Wait(yc); + } } } diff --git a/lib/remote/eventshandler.hpp b/lib/remote/eventshandler.hpp index cffeea5034f..c823415d328 100644 --- a/lib/remote/eventshandler.hpp +++ b/lib/remote/eventshandler.hpp @@ -22,7 +22,7 @@ class EventsHandler final : public HttpHandler boost::beast::http::response& response, const Dictionary::Ptr& params, boost::asio::yield_context& yc, - bool& hasStartedStreaming + HttpServerConnection& server ) override; }; diff --git a/lib/remote/httphandler.cpp b/lib/remote/httphandler.cpp index c089c00ea51..78e09d2ade1 100644 --- a/lib/remote/httphandler.cpp +++ b/lib/remote/httphandler.cpp @@ -51,7 +51,7 @@ void HttpHandler::ProcessRequest( boost::beast::http::request& request, boost::beast::http::response& response, boost::asio::yield_context& yc, - bool& hasStartedStreaming + HttpServerConnection& server ) { Dictionary::Ptr node = m_UrlTree; @@ -99,7 +99,7 @@ void HttpHandler::ProcessRequest( bool processed = false; for (const HttpHandler::Ptr& handler : handlers) { - if (handler->HandleRequest(stream, user, request, url, response, params, yc, hasStartedStreaming)) { + if (handler->HandleRequest(stream, user, request, url, response, params, yc, server)) { processed = true; break; } diff --git a/lib/remote/httphandler.hpp b/lib/remote/httphandler.hpp index cec5f58cd16..6c10d0affac 100644 --- a/lib/remote/httphandler.hpp +++ b/lib/remote/httphandler.hpp @@ -6,6 +6,7 @@ #include "remote/i2-remote.hpp" #include "remote/url.hpp" #include "remote/httpresponse.hpp" +#include "remote/httpserverconnection.hpp" #include "remote/apiuser.hpp" #include "base/registry.hpp" #include "base/tlsstream.hpp" @@ -34,7 +35,7 @@ class HttpHandler : public Object boost::beast::http::response& response, const Dictionary::Ptr& params, boost::asio::yield_context& yc, - bool& hasStartedStreaming + HttpServerConnection& server ) = 0; static void Register(const Url::Ptr& url, const HttpHandler::Ptr& handler); @@ -44,7 +45,7 @@ class HttpHandler : public Object boost::beast::http::request& request, boost::beast::http::response& response, boost::asio::yield_context& yc, - bool& hasStartedStreaming + HttpServerConnection& server ); private: diff --git a/lib/remote/httpserverconnection.cpp b/lib/remote/httpserverconnection.cpp index af516995281..c06e381e239 100644 --- a/lib/remote/httpserverconnection.cpp +++ b/lib/remote/httpserverconnection.cpp @@ -31,7 +31,7 @@ using namespace icinga; auto const l_ServerHeader ("Icinga/" + Application::GetAppVersion()); HttpServerConnection::HttpServerConnection(const String& identity, bool authenticated, const std::shared_ptr& stream) - : m_Stream(stream), m_Seen(Utility::GetTime()), m_IoStrand(stream->get_io_service()), m_ShuttingDown(false) + : m_Stream(stream), m_Seen(Utility::GetTime()), m_IoStrand(stream->get_io_service()), m_ShuttingDown(false), m_HasStartedStreaming(false) { if (authenticated) { m_ApiUser = ApiUser::GetByClientCN(identity); @@ -91,6 +91,34 @@ void HttpServerConnection::Disconnect() }); } +void HttpServerConnection::StartStreaming() +{ + namespace asio = boost::asio; + + m_HasStartedStreaming = true; + + HttpServerConnection::Ptr keepAlive (this); + + asio::spawn(m_IoStrand, [this, keepAlive](asio::yield_context yc) { + if (!m_ShuttingDown) { + char buf[128]; + asio::mutable_buffer readBuf (buf, 128); + boost::system::error_code ec; + + do { + m_Stream->async_read_some(readBuf, yc[ec]); + } while (!ec); + + Disconnect(); + } + }); +} + +bool HttpServerConnection::Disconnected() +{ + return m_ShuttingDown; +} + static inline bool EnsureValidHeaders( AsioTlsStream& stream, @@ -375,17 +403,17 @@ bool ProcessRequest( boost::beast::http::request& request, ApiUser::Ptr& authenticatedUser, boost::beast::http::response& response, + HttpServerConnection& server, + bool& hasStartedStreaming, boost::asio::yield_context& yc ) { namespace http = boost::beast::http; - bool hasStartedStreaming = false; - try { CpuBoundWork handlingRequest (yc); - HttpHandler::ProcessRequest(stream, authenticatedUser, request, response, yc, hasStartedStreaming); + HttpHandler::ProcessRequest(stream, authenticatedUser, request, response, yc, server); } catch (const std::exception& ex) { if (hasStartedStreaming) { return false; @@ -481,7 +509,7 @@ void HttpServerConnection::ProcessMessages(boost::asio::yield_context yc) m_Seen = std::numeric_limits::max(); - if (!ProcessRequest(*m_Stream, request, authenticatedUser, response, yc)) { + if (!ProcessRequest(*m_Stream, request, authenticatedUser, response, *this, m_HasStartedStreaming, yc)) { break; } diff --git a/lib/remote/httpserverconnection.hpp b/lib/remote/httpserverconnection.hpp index 7db97ed6820..b32db86a6c9 100644 --- a/lib/remote/httpserverconnection.hpp +++ b/lib/remote/httpserverconnection.hpp @@ -27,6 +27,9 @@ class HttpServerConnection final : public Object void Start(); void Disconnect(); + void StartStreaming(); + + bool Disconnected(); private: ApiUser::Ptr m_ApiUser; @@ -35,6 +38,7 @@ class HttpServerConnection final : public Object String m_PeerAddress; boost::asio::io_service::strand m_IoStrand; bool m_ShuttingDown; + bool m_HasStartedStreaming; void ProcessMessages(boost::asio::yield_context yc); void CheckLiveness(boost::asio::yield_context yc); diff --git a/lib/remote/infohandler.cpp b/lib/remote/infohandler.cpp index 38f99781d01..d9d6b45cbdd 100644 --- a/lib/remote/infohandler.cpp +++ b/lib/remote/infohandler.cpp @@ -16,7 +16,7 @@ bool InfoHandler::HandleRequest( boost::beast::http::response& response, const Dictionary::Ptr& params, boost::asio::yield_context& yc, - bool& hasStartedStreaming + HttpServerConnection& server ) { namespace http = boost::beast::http; diff --git a/lib/remote/infohandler.hpp b/lib/remote/infohandler.hpp index 346340d8180..e1fe983149f 100644 --- a/lib/remote/infohandler.hpp +++ b/lib/remote/infohandler.hpp @@ -21,7 +21,7 @@ class InfoHandler final : public HttpHandler boost::beast::http::response& response, const Dictionary::Ptr& params, boost::asio::yield_context& yc, - bool& hasStartedStreaming + HttpServerConnection& server ) override; }; diff --git a/lib/remote/modifyobjecthandler.cpp b/lib/remote/modifyobjecthandler.cpp index 2150903a1fa..3f360fe30e6 100644 --- a/lib/remote/modifyobjecthandler.cpp +++ b/lib/remote/modifyobjecthandler.cpp @@ -20,7 +20,7 @@ bool ModifyObjectHandler::HandleRequest( boost::beast::http::response& response, const Dictionary::Ptr& params, boost::asio::yield_context& yc, - bool& hasStartedStreaming + HttpServerConnection& server ) { namespace http = boost::beast::http; diff --git a/lib/remote/modifyobjecthandler.hpp b/lib/remote/modifyobjecthandler.hpp index c9bb2161248..f4693013f30 100644 --- a/lib/remote/modifyobjecthandler.hpp +++ b/lib/remote/modifyobjecthandler.hpp @@ -21,7 +21,7 @@ class ModifyObjectHandler final : public HttpHandler boost::beast::http::response& response, const Dictionary::Ptr& params, boost::asio::yield_context& yc, - bool& hasStartedStreaming + HttpServerConnection& server ) override; }; diff --git a/lib/remote/objectqueryhandler.cpp b/lib/remote/objectqueryhandler.cpp index 2d9b7984584..3f827037c37 100644 --- a/lib/remote/objectqueryhandler.cpp +++ b/lib/remote/objectqueryhandler.cpp @@ -95,7 +95,7 @@ bool ObjectQueryHandler::HandleRequest( boost::beast::http::response& response, const Dictionary::Ptr& params, boost::asio::yield_context& yc, - bool& hasStartedStreaming + HttpServerConnection& server ) { namespace http = boost::beast::http; diff --git a/lib/remote/objectqueryhandler.hpp b/lib/remote/objectqueryhandler.hpp index c35ff4066ce..691b2cfcf21 100644 --- a/lib/remote/objectqueryhandler.hpp +++ b/lib/remote/objectqueryhandler.hpp @@ -21,7 +21,7 @@ class ObjectQueryHandler final : public HttpHandler boost::beast::http::response& response, const Dictionary::Ptr& params, boost::asio::yield_context& yc, - bool& hasStartedStreaming + HttpServerConnection& server ) override; private: diff --git a/lib/remote/statushandler.cpp b/lib/remote/statushandler.cpp index cc725e4f421..1f3f61899f0 100644 --- a/lib/remote/statushandler.cpp +++ b/lib/remote/statushandler.cpp @@ -76,7 +76,7 @@ bool StatusHandler::HandleRequest( boost::beast::http::response& response, const Dictionary::Ptr& params, boost::asio::yield_context& yc, - bool& hasStartedStreaming + HttpServerConnection& server ) { namespace http = boost::beast::http; diff --git a/lib/remote/statushandler.hpp b/lib/remote/statushandler.hpp index 36f5efcf15c..c722ab3e2ee 100644 --- a/lib/remote/statushandler.hpp +++ b/lib/remote/statushandler.hpp @@ -21,7 +21,7 @@ class StatusHandler final : public HttpHandler boost::beast::http::response& response, const Dictionary::Ptr& params, boost::asio::yield_context& yc, - bool& hasStartedStreaming + HttpServerConnection& server ) override; }; diff --git a/lib/remote/templatequeryhandler.cpp b/lib/remote/templatequeryhandler.cpp index 38a22554011..e70dafb6501 100644 --- a/lib/remote/templatequeryhandler.cpp +++ b/lib/remote/templatequeryhandler.cpp @@ -83,7 +83,7 @@ bool TemplateQueryHandler::HandleRequest( boost::beast::http::response& response, const Dictionary::Ptr& params, boost::asio::yield_context& yc, - bool& hasStartedStreaming + HttpServerConnection& server ) { namespace http = boost::beast::http; diff --git a/lib/remote/templatequeryhandler.hpp b/lib/remote/templatequeryhandler.hpp index e9dec8896d4..503bc856060 100644 --- a/lib/remote/templatequeryhandler.hpp +++ b/lib/remote/templatequeryhandler.hpp @@ -21,7 +21,7 @@ class TemplateQueryHandler final : public HttpHandler boost::beast::http::response& response, const Dictionary::Ptr& params, boost::asio::yield_context& yc, - bool& hasStartedStreaming + HttpServerConnection& server ) override; }; diff --git a/lib/remote/typequeryhandler.cpp b/lib/remote/typequeryhandler.cpp index 07711377473..4e82653986c 100644 --- a/lib/remote/typequeryhandler.cpp +++ b/lib/remote/typequeryhandler.cpp @@ -54,7 +54,7 @@ bool TypeQueryHandler::HandleRequest( boost::beast::http::response& response, const Dictionary::Ptr& params, boost::asio::yield_context& yc, - bool& hasStartedStreaming + HttpServerConnection& server ) { namespace http = boost::beast::http; diff --git a/lib/remote/typequeryhandler.hpp b/lib/remote/typequeryhandler.hpp index 84c04185d1e..5489cb232d2 100644 --- a/lib/remote/typequeryhandler.hpp +++ b/lib/remote/typequeryhandler.hpp @@ -21,7 +21,7 @@ class TypeQueryHandler final : public HttpHandler boost::beast::http::response& response, const Dictionary::Ptr& params, boost::asio::yield_context& yc, - bool& hasStartedStreaming + HttpServerConnection& server ) override; }; diff --git a/lib/remote/variablequeryhandler.cpp b/lib/remote/variablequeryhandler.cpp index 05b503783d9..aef896e6fbb 100644 --- a/lib/remote/variablequeryhandler.cpp +++ b/lib/remote/variablequeryhandler.cpp @@ -64,7 +64,7 @@ bool VariableQueryHandler::HandleRequest( boost::beast::http::response& response, const Dictionary::Ptr& params, boost::asio::yield_context& yc, - bool& hasStartedStreaming + HttpServerConnection& server ) { namespace http = boost::beast::http; diff --git a/lib/remote/variablequeryhandler.hpp b/lib/remote/variablequeryhandler.hpp index ecc71a96aa0..48e73be356d 100644 --- a/lib/remote/variablequeryhandler.hpp +++ b/lib/remote/variablequeryhandler.hpp @@ -21,7 +21,7 @@ class VariableQueryHandler final : public HttpHandler boost::beast::http::response& response, const Dictionary::Ptr& params, boost::asio::yield_context& yc, - bool& hasStartedStreaming + HttpServerConnection& server ) override; };