diff options
author | Ed Tanous <edtanous@google.com> | 2023-04-06 23:10:02 +0300 |
---|---|---|
committer | Ed Tanous <ed@tanous.net> | 2023-06-09 22:13:40 +0300 |
commit | 08bbe1199f02d09f908cd3adcf4329e4bd67fd52 (patch) | |
tree | 9824a615569928f65523f13a876a0def301562dd /http/routing | |
parent | b90d14f220cba6de26dcf8749b2f8df062487d72 (diff) | |
download | bmcweb-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.hpp | 98 | ||||
-rw-r--r-- | http/routing/dynamicrule.hpp | 233 | ||||
-rw-r--r-- | http/routing/ruleparametertraits.hpp | 94 | ||||
-rw-r--r-- | http/routing/sserule.hpp | 79 | ||||
-rw-r--r-- | http/routing/taggedrule.hpp | 68 | ||||
-rw-r--r-- | http/routing/websocketrule.hpp | 108 |
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 |