HttpServerConnection: actually handle requests

This commit is contained in:
Alexander A. Klimov 2019-02-14 17:27:17 +01:00
parent 7fe0431ada
commit 9ae1d732af
5 changed files with 50 additions and 22 deletions

View File

@ -5,6 +5,7 @@
#include "base/singleton.hpp" #include "base/singleton.hpp"
#include "base/exception.hpp" #include "base/exception.hpp"
#include <boost/algorithm/string/join.hpp> #include <boost/algorithm/string/join.hpp>
#include <boost/beast/http.hpp>
using namespace icinga; using namespace icinga;
@ -44,11 +45,17 @@ void HttpHandler::Register(const Url::Ptr& url, const HttpHandler::Ptr& handler)
handlers->Add(handler); handlers->Add(handler);
} }
void HttpHandler::ProcessRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response) void HttpHandler::ProcessRequest(
const ApiUser::Ptr& user,
boost::beast::http::request<boost::beast::http::string_body>& request,
boost::beast::http::response<boost::beast::http::string_body>& response
)
{ {
Dictionary::Ptr node = m_UrlTree; Dictionary::Ptr node = m_UrlTree;
std::vector<HttpHandler::Ptr> handlers; std::vector<HttpHandler::Ptr> handlers;
const std::vector<String>& path = request.RequestUrl->GetPath();
Url::Ptr url = new Url(request.target().to_string());
auto& path (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");
@ -81,7 +88,7 @@ void HttpHandler::ProcessRequest(const ApiUser::Ptr& user, HttpRequest& request,
Dictionary::Ptr params; Dictionary::Ptr params;
try { try {
params = HttpUtility::FetchRequestParameters(request); params = HttpUtility::FetchRequestParameters(url, request.body());
} catch (const std::exception& ex) { } catch (const std::exception& ex) {
HttpUtility::SendJsonError(response, params, 400, "Invalid request body: " + DiagnosticInformation(ex, false)); HttpUtility::SendJsonError(response, params, 400, "Invalid request body: " + DiagnosticInformation(ex, false));
return; return;
@ -89,16 +96,15 @@ void HttpHandler::ProcessRequest(const ApiUser::Ptr& user, HttpRequest& request,
bool processed = false; bool processed = false;
for (const HttpHandler::Ptr& handler : handlers) { for (const HttpHandler::Ptr& handler : handlers) {
if (handler->HandleRequest(user, request, response, params)) { if (handler->HandleRequest(user, request, url, response, params)) {
processed = true; processed = true;
break; break;
} }
} }
if (!processed) { if (!processed) {
String path = boost::algorithm::join(request.RequestUrl->GetPath(), "/"); HttpUtility::SendJsonError(response, params, 404, "The requested path '" + boost::algorithm::join(path, "/") +
HttpUtility::SendJsonError(response, params, 404, "The requested path '" + 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,10 +4,12 @@
#define HTTPHANDLER_H #define HTTPHANDLER_H
#include "remote/i2-remote.hpp" #include "remote/i2-remote.hpp"
#include "remote/url.hpp"
#include "remote/httpresponse.hpp" #include "remote/httpresponse.hpp"
#include "remote/apiuser.hpp" #include "remote/apiuser.hpp"
#include "base/registry.hpp" #include "base/registry.hpp"
#include <vector> #include <vector>
#include <boost/beast/http.hpp>
namespace icinga namespace icinga
{ {
@ -22,10 +24,20 @@ class HttpHandler : public Object
public: public:
DECLARE_PTR_TYPEDEFS(HttpHandler); DECLARE_PTR_TYPEDEFS(HttpHandler);
virtual bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response, const Dictionary::Ptr& params) = 0; virtual bool HandleRequest(
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
) = 0;
static void Register(const Url::Ptr& url, const HttpHandler::Ptr& handler); static void Register(const Url::Ptr& url, const HttpHandler::Ptr& handler);
static void ProcessRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response); static void ProcessRequest(
const ApiUser::Ptr& user,
boost::beast::http::request<boost::beast::http::string_body>& request,
boost::beast::http::response<boost::beast::http::string_body>& response
);
private: private:
static Dictionary::Ptr m_UrlTree; static Dictionary::Ptr m_UrlTree;

View File

@ -321,7 +321,20 @@ void ProcessRequest(
{ {
namespace http = boost::beast::http; namespace http = boost::beast::http;
HttpUtility::SendJsonError(response, nullptr, 503, "Unhandled exception" , ""); try {
CpuBoundWork handlingRequest (yc);
HttpHandler::ProcessRequest(authenticatedUser, request, response);
} catch (const std::exception& ex) {
http::response<http::string_body> response;
HttpUtility::SendJsonError(response, nullptr, 500, "Unhandled exception" , DiagnosticInformation(ex));
http::async_write(stream, response, yc);
stream.async_flush(yc);
return;
}
http::async_write(stream, response, yc); http::async_write(stream, response, yc);
stream.async_flush(yc); stream.async_flush(yc);

View File

@ -1,28 +1,23 @@
/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
#include "remote/httputility.hpp" #include "remote/httputility.hpp"
#include "remote/url.hpp"
#include "base/json.hpp" #include "base/json.hpp"
#include "base/logger.hpp" #include "base/logger.hpp"
#include <map> #include <map>
#include <string>
#include <vector> #include <vector>
#include <boost/beast/http.hpp> #include <boost/beast/http.hpp>
using namespace icinga; using namespace icinga;
Dictionary::Ptr HttpUtility::FetchRequestParameters(HttpRequest& request) Dictionary::Ptr HttpUtility::FetchRequestParameters(const Url::Ptr& url, const std::string& body)
{ {
Dictionary::Ptr result; Dictionary::Ptr result;
String body; if (!body.empty()) {
char buffer[1024];
size_t count;
while ((count = request.ReadBody(buffer, sizeof(buffer))) > 0)
body += String(buffer, buffer + count);
if (!body.IsEmpty()) {
Log(LogDebug, "HttpUtility") Log(LogDebug, "HttpUtility")
<< "Request body: '" << body << "'"; << "Request body: '" << body << '\'';
result = JsonDecode(body); result = JsonDecode(body);
} }
@ -31,7 +26,7 @@ Dictionary::Ptr HttpUtility::FetchRequestParameters(HttpRequest& request)
result = new Dictionary(); result = new Dictionary();
std::map<String, std::vector<String>> query; std::map<String, std::vector<String>> query;
for (const auto& kv : request.RequestUrl->GetQuery()) { for (const auto& kv : url->GetQuery()) {
query[kv.first].emplace_back(kv.second); query[kv.first].emplace_back(kv.second);
} }

View File

@ -5,7 +5,9 @@
#include "remote/httprequest.hpp" #include "remote/httprequest.hpp"
#include "remote/httpresponse.hpp" #include "remote/httpresponse.hpp"
#include "remote/url.hpp"
#include "base/dictionary.hpp" #include "base/dictionary.hpp"
#include <string>
#include <boost/beast/http.hpp> #include <boost/beast/http.hpp>
namespace icinga namespace icinga
@ -20,7 +22,7 @@ class HttpUtility
{ {
public: public:
static Dictionary::Ptr FetchRequestParameters(HttpRequest& request); static Dictionary::Ptr FetchRequestParameters(const Url::Ptr& url, const std::string& body);
static void SendJsonBody(HttpResponse& response, const Dictionary::Ptr& params, const Value& val); static void SendJsonBody(HttpResponse& response, const Dictionary::Ptr& params, const Value& val);
static void SendJsonBody(boost::beast::http::response<boost::beast::http::string_body>& response, const Dictionary::Ptr& params, const Value& val); static void SendJsonBody(boost::beast::http::response<boost::beast::http::string_body>& response, const Dictionary::Ptr& params, const Value& val);
static Value GetLastParameter(const Dictionary::Ptr& params, const String& key); static Value GetLastParameter(const Dictionary::Ptr& params, const String& key);