summaryrefslogtreecommitdiff
path: root/http/routing
diff options
context:
space:
mode:
authorEd Tanous <edtanous@google.com>2023-04-06 23:10:02 +0300
committerEd Tanous <ed@tanous.net>2023-06-09 22:13:40 +0300
commit08bbe1199f02d09f908cd3adcf4329e4bd67fd52 (patch)
tree9824a615569928f65523f13a876a0def301562dd /http/routing
parentb90d14f220cba6de26dcf8749b2f8df062487d72 (diff)
downloadbmcweb-08bbe1199f02d09f908cd3adcf4329e4bd67fd52.tar.xz
Break up router into separate files
The router is a giant behemoth. Start breaking it down into pieces. Tested: Redfish service validator passes. Signed-off-by: Ed Tanous <edtanous@google.com> Change-Id: I9d04f53a58ffce3ecbd88dded1aa6e9648d2a762
Diffstat (limited to 'http/routing')
-rw-r--r--http/routing/baserule.hpp98
-rw-r--r--http/routing/dynamicrule.hpp233
-rw-r--r--http/routing/ruleparametertraits.hpp94
-rw-r--r--http/routing/sserule.hpp79
-rw-r--r--http/routing/taggedrule.hpp68
-rw-r--r--http/routing/websocketrule.hpp108
6 files changed, 680 insertions, 0 deletions
diff --git a/http/routing/baserule.hpp b/http/routing/baserule.hpp
new file mode 100644
index 0000000000..0913020935
--- /dev/null
+++ b/http/routing/baserule.hpp
@@ -0,0 +1,98 @@
+#pragma once
+
+#include "async_resp.hpp"
+#include "http_request.hpp"
+#include "privileges.hpp"
+#include "verb.hpp"
+
+#include <boost/beast/ssl/ssl_stream.hpp>
+
+#include <memory>
+#include <string>
+
+namespace crow
+{
+class BaseRule
+{
+ public:
+ explicit BaseRule(const std::string& thisRule) : rule(thisRule) {}
+
+ virtual ~BaseRule() = default;
+
+ BaseRule(const BaseRule&) = delete;
+ BaseRule(BaseRule&&) = delete;
+ BaseRule& operator=(const BaseRule&) = delete;
+ BaseRule& operator=(const BaseRule&&) = delete;
+
+ virtual void validate() = 0;
+ std::unique_ptr<BaseRule> upgrade()
+ {
+ if (ruleToUpgrade)
+ {
+ return std::move(ruleToUpgrade);
+ }
+ return {};
+ }
+
+ virtual void handle(const Request& /*req*/,
+ const std::shared_ptr<bmcweb::AsyncResp>&,
+ const std::vector<std::string>&) = 0;
+#ifndef BMCWEB_ENABLE_SSL
+ virtual void
+ handleUpgrade(const Request& /*req*/,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ boost::asio::ip::tcp::socket&& /*adaptor*/)
+ {
+ asyncResp->res.result(boost::beast::http::status::not_found);
+ }
+#else
+ virtual void handleUpgrade(
+ const Request& /*req*/,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ boost::beast::ssl_stream<boost::asio::ip::tcp::socket>&& /*adaptor*/)
+ {
+ asyncResp->res.result(boost::beast::http::status::not_found);
+ }
+#endif
+
+ size_t getMethods() const
+ {
+ return methodsBitfield;
+ }
+
+ bool checkPrivileges(const redfish::Privileges& userPrivileges)
+ {
+ // If there are no privileges assigned, assume no privileges
+ // required
+ if (privilegesSet.empty())
+ {
+ return true;
+ }
+
+ for (const redfish::Privileges& requiredPrivileges : privilegesSet)
+ {
+ if (userPrivileges.isSupersetOf(requiredPrivileges))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ size_t methodsBitfield{1 << static_cast<size_t>(HttpVerb::Get)};
+ static_assert(std::numeric_limits<decltype(methodsBitfield)>::digits >
+ methodNotAllowedIndex,
+ "Not enough bits to store bitfield");
+
+ std::vector<redfish::Privileges> privilegesSet;
+
+ std::string rule;
+
+ std::unique_ptr<BaseRule> ruleToUpgrade;
+
+ friend class Router;
+ template <typename T>
+ friend struct RuleParameterTraits;
+};
+
+} // namespace crow
diff --git a/http/routing/dynamicrule.hpp b/http/routing/dynamicrule.hpp
new file mode 100644
index 0000000000..746765cf78
--- /dev/null
+++ b/http/routing/dynamicrule.hpp
@@ -0,0 +1,233 @@
+#pragma once
+#include "baserule.hpp"
+#include "ruleparametertraits.hpp"
+#include "websocket.hpp"
+
+#include <boost/beast/http/verb.hpp>
+
+#include <functional>
+#include <limits>
+#include <string>
+#include <type_traits>
+
+namespace crow
+{
+namespace detail
+{
+namespace routing_handler_call_helper
+{
+template <typename T, int Pos>
+struct CallPair
+{
+ using type = T;
+ static const int pos = Pos;
+};
+
+template <typename H1>
+struct CallParams
+{
+ H1& handler;
+ const std::vector<std::string>& params;
+ const Request& req;
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp;
+};
+
+template <typename F, int NString, typename S1, typename S2>
+struct Call
+{};
+
+template <typename F, int NString, typename... Args1, typename... Args2>
+struct Call<F, NString, black_magic::S<std::string, Args1...>,
+ black_magic::S<Args2...>>
+{
+ void operator()(F cparams)
+ {
+ using pushed = typename black_magic::S<Args2...>::template push_back<
+ CallPair<std::string, NString>>;
+ Call<F, NString + 1, black_magic::S<Args1...>, pushed>()(cparams);
+ }
+};
+
+template <typename F, int NString, typename... Args1>
+struct Call<F, NString, black_magic::S<>, black_magic::S<Args1...>>
+{
+ void operator()(F cparams)
+ {
+ cparams.handler(cparams.req, cparams.asyncResp,
+ cparams.params[Args1::pos]...);
+ }
+};
+
+template <typename Func, typename... ArgsWrapped>
+struct Wrapped
+{
+ template <typename... Args>
+ void set(
+ Func f,
+ typename std::enable_if<
+ !std::is_same<
+ typename std::tuple_element<0, std::tuple<Args..., void>>::type,
+ const Request&>::value,
+ int>::type /*enable*/
+ = 0)
+ {
+ handler = [f = std::forward<Func>(f)](
+ const Request&,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ Args... args) { asyncResp->res.result(f(args...)); };
+ }
+
+ template <typename Req, typename... Args>
+ struct ReqHandlerWrapper
+ {
+ explicit ReqHandlerWrapper(Func fIn) : f(std::move(fIn)) {}
+
+ void operator()(const Request& req,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ Args... args)
+ {
+ asyncResp->res.result(f(req, args...));
+ }
+
+ Func f;
+ };
+
+ template <typename... Args>
+ void set(
+ Func f,
+ typename std::enable_if<
+ std::is_same<
+ typename std::tuple_element<0, std::tuple<Args..., void>>::type,
+ const Request&>::value &&
+ !std::is_same<typename std::tuple_element<
+ 1, std::tuple<Args..., void, void>>::type,
+ const std::shared_ptr<bmcweb::AsyncResp>&>::value,
+ int>::type /*enable*/
+ = 0)
+ {
+ handler = ReqHandlerWrapper<Args...>(std::move(f));
+ /*handler = (
+ [f = std::move(f)]
+ (const Request& req, Response& res, Args... args){
+ res.result(f(req, args...));
+ res.end();
+ });*/
+ }
+
+ template <typename... Args>
+ void set(
+ Func f,
+ typename std::enable_if<
+ std::is_same<
+ typename std::tuple_element<0, std::tuple<Args..., void>>::type,
+ const Request&>::value &&
+ std::is_same<typename std::tuple_element<
+ 1, std::tuple<Args..., void, void>>::type,
+ const std::shared_ptr<bmcweb::AsyncResp>&>::value,
+ int>::type /*enable*/
+ = 0)
+ {
+ handler = std::move(f);
+ }
+
+ template <typename... Args>
+ struct HandlerTypeHelper
+ {
+ using type = std::function<void(
+ const crow::Request& /*req*/,
+ const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>;
+ using args_type = black_magic::S<Args...>;
+ };
+
+ template <typename... Args>
+ struct HandlerTypeHelper<const Request&, Args...>
+ {
+ using type = std::function<void(
+ const crow::Request& /*req*/,
+ const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>;
+ using args_type = black_magic::S<Args...>;
+ };
+
+ template <typename... Args>
+ struct HandlerTypeHelper<const Request&,
+ const std::shared_ptr<bmcweb::AsyncResp>&, Args...>
+ {
+ using type = std::function<void(
+ const crow::Request& /*req*/,
+ const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>;
+ using args_type = black_magic::S<Args...>;
+ };
+
+ typename HandlerTypeHelper<ArgsWrapped...>::type handler;
+
+ void operator()(const Request& req,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const std::vector<std::string>& params)
+ {
+ detail::routing_handler_call_helper::Call<
+ detail::routing_handler_call_helper::CallParams<decltype(handler)>,
+ 0, typename HandlerTypeHelper<ArgsWrapped...>::args_type,
+ black_magic::S<>>()(
+ detail::routing_handler_call_helper::CallParams<decltype(handler)>{
+ handler, params, req, asyncResp});
+ }
+};
+} // namespace routing_handler_call_helper
+} // namespace detail
+
+class DynamicRule : public BaseRule, public RuleParameterTraits<DynamicRule>
+{
+ public:
+ explicit DynamicRule(const std::string& ruleIn) : BaseRule(ruleIn) {}
+
+ void validate() override
+ {
+ if (!erasedHandler)
+ {
+ throw std::runtime_error("no handler for url " + rule);
+ }
+ }
+
+ void handle(const Request& req,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const std::vector<std::string>& params) override
+ {
+ erasedHandler(req, asyncResp, params);
+ }
+
+ template <typename Func>
+ void operator()(Func f)
+ {
+ using boost::callable_traits::args_t;
+ constexpr size_t arity = std::tuple_size<args_t<Func>>::value;
+ constexpr auto is = std::make_integer_sequence<unsigned, arity>{};
+ erasedHandler = wrap(std::move(f), is);
+ }
+
+ // enable_if Arg1 == request && Arg2 == Response
+ // enable_if Arg1 == request && Arg2 != response
+ // enable_if Arg1 != request
+
+ template <typename Func, unsigned... Indices>
+ std::function<void(const Request&,
+ const std::shared_ptr<bmcweb::AsyncResp>&,
+ const std::vector<std::string>&)>
+ wrap(Func f, std::integer_sequence<unsigned, Indices...> /*is*/)
+ {
+ using function_t = crow::utility::FunctionTraits<Func>;
+
+ auto ret = detail::routing_handler_call_helper::Wrapped<
+ Func, typename function_t::template arg<Indices>...>();
+ ret.template set<typename function_t::template arg<Indices>...>(
+ std::move(f));
+ return ret;
+ }
+
+ private:
+ std::function<void(const Request&,
+ const std::shared_ptr<bmcweb::AsyncResp>&,
+ const std::vector<std::string>&)>
+ erasedHandler;
+};
+
+} // namespace crow
diff --git a/http/routing/ruleparametertraits.hpp b/http/routing/ruleparametertraits.hpp
new file mode 100644
index 0000000000..bb3f563301
--- /dev/null
+++ b/http/routing/ruleparametertraits.hpp
@@ -0,0 +1,94 @@
+#pragma once
+
+#include "sserule.hpp"
+#include "websocketrule.hpp"
+
+#include <boost/beast/http/verb.hpp>
+
+#include <initializer_list>
+#include <optional>
+
+namespace crow
+{
+template <typename T>
+struct RuleParameterTraits
+{
+ using self_t = T;
+ WebSocketRule& websocket()
+ {
+ self_t* self = static_cast<self_t*>(this);
+ WebSocketRule* p = new WebSocketRule(self->rule);
+ p->privilegesSet = self->privilegesSet;
+ self->ruleToUpgrade.reset(p);
+ return *p;
+ }
+
+ SseSocketRule& serverSentEvent()
+ {
+ self_t* self = static_cast<self_t*>(this);
+ SseSocketRule* p = new SseSocketRule(self->rule);
+ self->ruleToUpgrade.reset(p);
+ return *p;
+ }
+
+ self_t& methods(boost::beast::http::verb method)
+ {
+ self_t* self = static_cast<self_t*>(this);
+ std::optional<HttpVerb> verb = httpVerbFromBoost(method);
+ if (verb)
+ {
+ self->methodsBitfield = 1U << static_cast<size_t>(*verb);
+ }
+ return *self;
+ }
+
+ template <typename... MethodArgs>
+ self_t& methods(boost::beast::http::verb method, MethodArgs... argsMethod)
+ {
+ self_t* self = static_cast<self_t*>(this);
+ methods(argsMethod...);
+ std::optional<HttpVerb> verb = httpVerbFromBoost(method);
+ if (verb)
+ {
+ self->methodsBitfield |= 1U << static_cast<size_t>(*verb);
+ }
+ return *self;
+ }
+
+ self_t& notFound()
+ {
+ self_t* self = static_cast<self_t*>(this);
+ self->methodsBitfield = 1U << notFoundIndex;
+ return *self;
+ }
+
+ self_t& methodNotAllowed()
+ {
+ self_t* self = static_cast<self_t*>(this);
+ self->methodsBitfield = 1U << methodNotAllowedIndex;
+ return *self;
+ }
+
+ self_t& privileges(
+ const std::initializer_list<std::initializer_list<const char*>>& p)
+ {
+ self_t* self = static_cast<self_t*>(this);
+ for (const std::initializer_list<const char*>& privilege : p)
+ {
+ self->privilegesSet.emplace_back(privilege);
+ }
+ return *self;
+ }
+
+ template <size_t N, typename... MethodArgs>
+ self_t& privileges(const std::array<redfish::Privileges, N>& p)
+ {
+ self_t* self = static_cast<self_t*>(this);
+ for (const redfish::Privileges& privilege : p)
+ {
+ self->privilegesSet.emplace_back(privilege);
+ }
+ return *self;
+ }
+};
+} // namespace crow
diff --git a/http/routing/sserule.hpp b/http/routing/sserule.hpp
new file mode 100644
index 0000000000..c0a4e504b3
--- /dev/null
+++ b/http/routing/sserule.hpp
@@ -0,0 +1,79 @@
+#pragma once
+
+#include "baserule.hpp"
+#include "http_request.hpp"
+#include "http_response.hpp"
+#include "server_sent_event.hpp"
+
+#include <boost/beast/http/verb.hpp>
+
+#include <functional>
+#include <memory>
+#include <string>
+
+namespace crow
+{
+
+class SseSocketRule : public BaseRule
+{
+ using self_t = SseSocketRule;
+
+ public:
+ explicit SseSocketRule(const std::string& ruleIn) : BaseRule(ruleIn) {}
+
+ void validate() override {}
+
+ void handle(const Request& /*req*/,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const std::vector<std::string>& /*params*/) override
+ {
+ asyncResp->res.result(boost::beast::http::status::not_found);
+ }
+
+#ifndef BMCWEB_ENABLE_SSL
+ void handleUpgrade(const Request& /*req*/,
+ const std::shared_ptr<bmcweb::AsyncResp>& /*asyncResp*/,
+ boost::asio::ip::tcp::socket&& adaptor) override
+ {
+ std::shared_ptr<
+ crow::sse_socket::ConnectionImpl<boost::asio::ip::tcp::socket>>
+ myConnection = std::make_shared<
+ crow::sse_socket::ConnectionImpl<boost::asio::ip::tcp::socket>>(
+ std::move(adaptor), openHandler, closeHandler);
+ myConnection->start();
+ }
+#else
+ void handleUpgrade(const Request& /*req*/,
+ const std::shared_ptr<bmcweb::AsyncResp>& /*asyncResp*/,
+ boost::beast::ssl_stream<boost::asio::ip::tcp::socket>&&
+ adaptor) override
+ {
+ std::shared_ptr<crow::sse_socket::ConnectionImpl<
+ boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>>
+ myConnection = std::make_shared<crow::sse_socket::ConnectionImpl<
+ boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>>(
+ std::move(adaptor), openHandler, closeHandler);
+ myConnection->start();
+ }
+#endif
+
+ template <typename Func>
+ self_t& onopen(Func f)
+ {
+ openHandler = f;
+ return *this;
+ }
+
+ template <typename Func>
+ self_t& onclose(Func f)
+ {
+ closeHandler = f;
+ return *this;
+ }
+
+ private:
+ std::function<void(crow::sse_socket::Connection&)> openHandler;
+ std::function<void(crow::sse_socket::Connection&)> closeHandler;
+};
+
+} // namespace crow
diff --git a/http/routing/taggedrule.hpp b/http/routing/taggedrule.hpp
new file mode 100644
index 0000000000..ef62ab08da
--- /dev/null
+++ b/http/routing/taggedrule.hpp
@@ -0,0 +1,68 @@
+#pragma once
+#include "baserule.hpp"
+#include "dynamicrule.hpp"
+#include "ruleparametertraits.hpp"
+
+#include <boost/beast/http/verb.hpp>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace crow
+{
+template <typename... Args>
+class TaggedRule :
+ public BaseRule,
+ public RuleParameterTraits<TaggedRule<Args...>>
+{
+ public:
+ using self_t = TaggedRule<Args...>;
+
+ explicit TaggedRule(const std::string& ruleIn) : BaseRule(ruleIn) {}
+
+ void validate() override
+ {
+ if (!handler)
+ {
+ throw std::runtime_error("no handler for url " + rule);
+ }
+ }
+
+ template <typename Func>
+ void operator()(Func&& f)
+ {
+ static_assert(
+ black_magic::CallHelper<
+ Func, black_magic::S<crow::Request,
+ std::shared_ptr<bmcweb::AsyncResp>&,
+ Args...>>::value,
+ "Handler type is mismatched with URL parameters");
+ static_assert(
+ std::is_same<
+ void,
+ decltype(f(std::declval<crow::Request>(),
+ std::declval<std::shared_ptr<bmcweb::AsyncResp>&>(),
+ std::declval<Args>()...))>::value,
+ "Handler function with response argument should have void return type");
+
+ handler = std::forward<Func>(f);
+ }
+
+ void handle(const Request& req,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const std::vector<std::string>& params) override
+ {
+ detail::routing_handler_call_helper::Call<
+ detail::routing_handler_call_helper::CallParams<decltype(handler)>,
+ 0, black_magic::S<Args...>, black_magic::S<>>()(
+ detail::routing_handler_call_helper::CallParams<decltype(handler)>{
+ handler, params, req, asyncResp});
+ }
+
+ private:
+ std::function<void(const crow::Request&,
+ const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>
+ handler;
+};
+} // namespace crow
diff --git a/http/routing/websocketrule.hpp b/http/routing/websocketrule.hpp
new file mode 100644
index 0000000000..c8f706db51
--- /dev/null
+++ b/http/routing/websocketrule.hpp
@@ -0,0 +1,108 @@
+#pragma once
+
+#include "baserule.hpp"
+#include "websocket.hpp"
+
+#include <boost/beast/http/verb.hpp>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace crow
+{
+class WebSocketRule : public BaseRule
+{
+ using self_t = WebSocketRule;
+
+ public:
+ explicit WebSocketRule(const std::string& ruleIn) : BaseRule(ruleIn) {}
+
+ void validate() override {}
+
+ void handle(const Request& /*req*/,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const std::vector<std::string>& /*params*/) override
+ {
+ asyncResp->res.result(boost::beast::http::status::not_found);
+ }
+
+#ifndef BMCWEB_ENABLE_SSL
+ void handleUpgrade(const Request& req,
+ const std::shared_ptr<bmcweb::AsyncResp>& /*asyncResp*/,
+ boost::asio::ip::tcp::socket&& adaptor) override
+ {
+ BMCWEB_LOG_DEBUG << "Websocket handles upgrade";
+ std::shared_ptr<
+ crow::websocket::ConnectionImpl<boost::asio::ip::tcp::socket>>
+ myConnection = std::make_shared<
+ crow::websocket::ConnectionImpl<boost::asio::ip::tcp::socket>>(
+ req, req.url(), std::move(adaptor), openHandler, messageHandler,
+ messageExHandler, closeHandler, errorHandler);
+ myConnection->start();
+ }
+#else
+ void handleUpgrade(const Request& req,
+ const std::shared_ptr<bmcweb::AsyncResp>& /*asyncResp*/,
+ boost::beast::ssl_stream<boost::asio::ip::tcp::socket>&&
+ adaptor) override
+ {
+ BMCWEB_LOG_DEBUG << "Websocket handles upgrade";
+ std::shared_ptr<crow::websocket::ConnectionImpl<
+ boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>>
+ myConnection = std::make_shared<crow::websocket::ConnectionImpl<
+ boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>>(
+ req, req.url(), std::move(adaptor), openHandler, messageHandler,
+ messageExHandler, closeHandler, errorHandler);
+ myConnection->start();
+ }
+#endif
+
+ template <typename Func>
+ self_t& onopen(Func f)
+ {
+ openHandler = f;
+ return *this;
+ }
+
+ template <typename Func>
+ self_t& onmessage(Func f)
+ {
+ messageHandler = f;
+ return *this;
+ }
+
+ template <typename Func>
+ self_t& onmessageex(Func f)
+ {
+ messageExHandler = f;
+ return *this;
+ }
+
+ template <typename Func>
+ self_t& onclose(Func f)
+ {
+ closeHandler = f;
+ return *this;
+ }
+
+ template <typename Func>
+ self_t& onerror(Func f)
+ {
+ errorHandler = f;
+ return *this;
+ }
+
+ protected:
+ std::function<void(crow::websocket::Connection&)> openHandler;
+ std::function<void(crow::websocket::Connection&, const std::string&, bool)>
+ messageHandler;
+ std::function<void(crow::websocket::Connection&, std::string_view,
+ crow::websocket::MessageType type,
+ std::function<void()>&& whenComplete)>
+ messageExHandler;
+ std::function<void(crow::websocket::Connection&, const std::string&)>
+ closeHandler;
+ std::function<void(crow::websocket::Connection&)> errorHandler;
+};
+} // namespace crow