1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
2 
3 #include "remote/eventshandler.hpp"
4 #include "remote/httputility.hpp"
5 #include "remote/filterutility.hpp"
6 #include "config/configcompiler.hpp"
7 #include "config/expression.hpp"
8 #include "base/defer.hpp"
9 #include "base/io-engine.hpp"
10 #include "base/objectlock.hpp"
11 #include "base/json.hpp"
12 #include <boost/asio/buffer.hpp>
13 #include <boost/asio/write.hpp>
14 #include <boost/algorithm/string/replace.hpp>
15 #include <map>
16 #include <set>
17 
18 using namespace icinga;
19 
20 REGISTER_URLHANDLER("/v1/events", EventsHandler);
21 
22 const std::map<String, EventType> l_EventTypes ({
23 	{"AcknowledgementCleared", EventType::AcknowledgementCleared},
24 	{"AcknowledgementSet", EventType::AcknowledgementSet},
25 	{"CheckResult", EventType::CheckResult},
26 	{"CommentAdded", EventType::CommentAdded},
27 	{"CommentRemoved", EventType::CommentRemoved},
28 	{"DowntimeAdded", EventType::DowntimeAdded},
29 	{"DowntimeRemoved", EventType::DowntimeRemoved},
30 	{"DowntimeStarted", EventType::DowntimeStarted},
31 	{"DowntimeTriggered", EventType::DowntimeTriggered},
32 	{"Flapping", EventType::Flapping},
33 	{"Notification", EventType::Notification},
34 	{"StateChange", EventType::StateChange},
35 	{"ObjectCreated", EventType::ObjectCreated},
36 	{"ObjectDeleted", EventType::ObjectDeleted},
37 	{"ObjectModified", EventType::ObjectModified}
38 });
39 
40 const String l_ApiQuery ("<API query>");
41 
HandleRequest(AsioTlsStream & stream,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,boost::asio::yield_context & yc,HttpServerConnection & server)42 bool EventsHandler::HandleRequest(
43 	AsioTlsStream& stream,
44 	const ApiUser::Ptr& user,
45 	boost::beast::http::request<boost::beast::http::string_body>& request,
46 	const Url::Ptr& url,
47 	boost::beast::http::response<boost::beast::http::string_body>& response,
48 	const Dictionary::Ptr& params,
49 	boost::asio::yield_context& yc,
50 	HttpServerConnection& server
51 )
52 {
53 	namespace asio = boost::asio;
54 	namespace http = boost::beast::http;
55 
56 	if (url->GetPath().size() != 2)
57 		return false;
58 
59 	if (request.method() != http::verb::post)
60 		return false;
61 
62 	if (request.version() == 10) {
63 		HttpUtility::SendJsonError(response, params, 400, "HTTP/1.0 not supported for event streams.");
64 		return true;
65 	}
66 
67 	Array::Ptr types = params->Get("types");
68 
69 	if (!types) {
70 		HttpUtility::SendJsonError(response, params, 400, "'types' query parameter is required.");
71 		return true;
72 	}
73 
74 	{
75 		ObjectLock olock(types);
76 		for (const String& type : types) {
77 			FilterUtility::CheckPermission(user, "events/" + type);
78 		}
79 	}
80 
81 	String queueName = HttpUtility::GetLastParameter(params, "queue");
82 
83 	if (queueName.IsEmpty()) {
84 		HttpUtility::SendJsonError(response, params, 400, "'queue' query parameter is required.");
85 		return true;
86 	}
87 
88 	std::set<EventType> eventTypes;
89 
90 	{
91 		ObjectLock olock(types);
92 		for (const String& type : types) {
93 			auto typeId (l_EventTypes.find(type));
94 
95 			if (typeId != l_EventTypes.end()) {
96 				eventTypes.emplace(typeId->second);
97 			}
98 		}
99 	}
100 
101 	EventsSubscriber subscriber (std::move(eventTypes), HttpUtility::GetLastParameter(params, "filter"), l_ApiQuery);
102 
103 	server.StartStreaming();
104 
105 	response.result(http::status::ok);
106 	response.set(http::field::content_type, "application/json");
107 
108 	IoBoundWorkSlot dontLockTheIoThread (yc);
109 
110 	http::async_write(stream, response, yc);
111 	stream.async_flush(yc);
112 
113 	asio::const_buffer newLine ("\n", 1);
114 
115 	for (;;) {
116 		auto event (subscriber.GetInbox()->Shift(yc));
117 
118 		if (event) {
119 			CpuBoundWork buildingResponse (yc);
120 
121 			String body = JsonEncode(event);
122 
123 			boost::algorithm::replace_all(body, "\n", "");
124 
125 			asio::const_buffer payload (body.CStr(), body.GetLength());
126 
127 			buildingResponse.Done();
128 
129 			asio::async_write(stream, payload, yc);
130 			asio::async_write(stream, newLine, yc);
131 			stream.async_flush(yc);
132 		} else if (server.Disconnected()) {
133 			return true;
134 		}
135 	}
136 }
137 
138