From 08bbe1199f02d09f908cd3adcf4329e4bd67fd52 Mon Sep 17 00:00:00 2001 From: Ed Tanous Date: Thu, 6 Apr 2023 13:10:02 -0700 Subject: 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 Change-Id: I9d04f53a58ffce3ecbd88dded1aa6e9648d2a762 --- http/routing.hpp | 767 +---------------------------------- http/routing/baserule.hpp | 98 +++++ http/routing/dynamicrule.hpp | 233 +++++++++++ http/routing/ruleparametertraits.hpp | 94 +++++ http/routing/sserule.hpp | 79 ++++ http/routing/taggedrule.hpp | 68 ++++ http/routing/websocketrule.hpp | 108 +++++ 7 files changed, 686 insertions(+), 761 deletions(-) create mode 100644 http/routing/baserule.hpp create mode 100644 http/routing/dynamicrule.hpp create mode 100644 http/routing/ruleparametertraits.hpp create mode 100644 http/routing/sserule.hpp create mode 100644 http/routing/taggedrule.hpp create mode 100644 http/routing/websocketrule.hpp (limited to 'http') diff --git a/http/routing.hpp b/http/routing.hpp index f3bcfbb065..97daef1fcf 100644 --- a/http/routing.hpp +++ b/http/routing.hpp @@ -2,13 +2,18 @@ #include "async_resp.hpp" #include "common.hpp" +#include "dbus_privileges.hpp" #include "dbus_utility.hpp" #include "error_messages.hpp" #include "http_request.hpp" #include "http_response.hpp" #include "logging.hpp" #include "privileges.hpp" -#include "server_sent_event.hpp" +#include "routing/baserule.hpp" +#include "routing/dynamicrule.hpp" +#include "routing/sserule.hpp" +#include "routing/taggedrule.hpp" +#include "routing/websocketrule.hpp" #include "sessions.hpp" #include "utility.hpp" #include "utils/dbus_utils.hpp" @@ -33,601 +38,6 @@ 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 upgrade() - { - if (ruleToUpgrade) - { - return std::move(ruleToUpgrade); - } - return {}; - } - - virtual void handle(const Request& /*req*/, - const std::shared_ptr&, - const std::vector&) = 0; -#ifndef BMCWEB_ENABLE_SSL - virtual void - handleUpgrade(const Request& /*req*/, - const std::shared_ptr& 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& asyncResp, - boost::beast::ssl_stream&& /*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(HttpVerb::Get)}; - static_assert(std::numeric_limits::digits > - methodNotAllowedIndex, - "Not enough bits to store bitfield"); - - std::vector privilegesSet; - - std::string rule; - - std::unique_ptr ruleToUpgrade; - - friend class Router; - template - friend struct RuleParameterTraits; -}; - -namespace detail -{ -namespace routing_handler_call_helper -{ -template -struct CallPair -{ - using type = T; - static const int pos = Pos; -}; - -template -struct CallParams -{ - H1& handler; - const std::vector& params; - const Request& req; - const std::shared_ptr& asyncResp; -}; - -template -struct Call -{}; - -template -struct Call, - black_magic::S> -{ - void operator()(F cparams) - { - using pushed = typename black_magic::S::template push_back< - CallPair>; - Call, pushed>()(cparams); - } -}; - -template -struct Call, black_magic::S> -{ - void operator()(F cparams) - { - cparams.handler(cparams.req, cparams.asyncResp, - cparams.params[Args1::pos]...); - } -}; - -template -struct Wrapped -{ - template - void set( - Func f, - typename std::enable_if< - !std::is_same< - typename std::tuple_element<0, std::tuple>::type, - const Request&>::value, - int>::type /*enable*/ - = 0) - { - handler = [f = std::forward(f)]( - const Request&, - const std::shared_ptr& asyncResp, - Args... args) { asyncResp->res.result(f(args...)); }; - } - - template - struct ReqHandlerWrapper - { - explicit ReqHandlerWrapper(Func fIn) : f(std::move(fIn)) {} - - void operator()(const Request& req, - const std::shared_ptr& asyncResp, - Args... args) - { - asyncResp->res.result(f(req, args...)); - } - - Func f; - }; - - template - void set( - Func f, - typename std::enable_if< - std::is_same< - typename std::tuple_element<0, std::tuple>::type, - const Request&>::value && - !std::is_same>::type, - const std::shared_ptr&>::value, - int>::type /*enable*/ - = 0) - { - handler = ReqHandlerWrapper(std::move(f)); - /*handler = ( - [f = std::move(f)] - (const Request& req, Response& res, Args... args){ - res.result(f(req, args...)); - res.end(); - });*/ - } - - template - void set( - Func f, - typename std::enable_if< - std::is_same< - typename std::tuple_element<0, std::tuple>::type, - const Request&>::value && - std::is_same>::type, - const std::shared_ptr&>::value, - int>::type /*enable*/ - = 0) - { - handler = std::move(f); - } - - template - struct HandlerTypeHelper - { - using type = std::function&, Args...)>; - using args_type = black_magic::S; - }; - - template - struct HandlerTypeHelper - { - using type = std::function&, Args...)>; - using args_type = black_magic::S; - }; - - template - struct HandlerTypeHelper&, Args...> - { - using type = std::function&, Args...)>; - using args_type = black_magic::S; - }; - - typename HandlerTypeHelper::type handler; - - void operator()(const Request& req, - const std::shared_ptr& asyncResp, - const std::vector& params) - { - detail::routing_handler_call_helper::Call< - detail::routing_handler_call_helper::CallParams, - 0, typename HandlerTypeHelper::args_type, - black_magic::S<>>()( - detail::routing_handler_call_helper::CallParams{ - handler, params, req, asyncResp}); - } -}; -} // namespace routing_handler_call_helper -} // namespace detail - -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& asyncResp, - const std::vector& /*params*/) override - { - asyncResp->res.result(boost::beast::http::status::not_found); - } - -#ifndef BMCWEB_ENABLE_SSL - void handleUpgrade(const Request& req, - const std::shared_ptr& /*asyncResp*/, - boost::asio::ip::tcp::socket&& adaptor) override - { - BMCWEB_LOG_DEBUG << "Websocket handles upgrade"; - std::shared_ptr< - crow::websocket::ConnectionImpl> - myConnection = std::make_shared< - crow::websocket::ConnectionImpl>( - req, req.url(), std::move(adaptor), openHandler, messageHandler, - messageExHandler, closeHandler, errorHandler); - myConnection->start(); - } -#else - void handleUpgrade(const Request& req, - const std::shared_ptr& /*asyncResp*/, - boost::beast::ssl_stream&& - adaptor) override - { - BMCWEB_LOG_DEBUG << "Websocket handles upgrade"; - std::shared_ptr>> - myConnection = std::make_shared>>( - req, req.url(), std::move(adaptor), openHandler, messageHandler, - messageExHandler, closeHandler, errorHandler); - myConnection->start(); - } -#endif - - template - self_t& onopen(Func f) - { - openHandler = f; - return *this; - } - - template - self_t& onmessage(Func f) - { - messageHandler = f; - return *this; - } - - template - self_t& onmessageex(Func f) - { - messageExHandler = f; - return *this; - } - - template - self_t& onclose(Func f) - { - closeHandler = f; - return *this; - } - - template - self_t& onerror(Func f) - { - errorHandler = f; - return *this; - } - - protected: - std::function openHandler; - std::function - messageHandler; - std::function&& whenComplete)> - messageExHandler; - std::function - closeHandler; - std::function errorHandler; -}; - -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& asyncResp, - const std::vector& /*params*/) override - { - asyncResp->res.result(boost::beast::http::status::not_found); - } - -#ifndef BMCWEB_ENABLE_SSL - void handleUpgrade(const Request& /*req*/, - const std::shared_ptr& /*asyncResp*/, - boost::asio::ip::tcp::socket&& adaptor) override - { - std::shared_ptr< - crow::sse_socket::ConnectionImpl> - myConnection = std::make_shared< - crow::sse_socket::ConnectionImpl>( - std::move(adaptor), openHandler, closeHandler); - myConnection->start(); - } -#else - void handleUpgrade(const Request& /*req*/, - const std::shared_ptr& /*asyncResp*/, - boost::beast::ssl_stream&& - adaptor) override - { - std::shared_ptr>> - myConnection = std::make_shared>>( - std::move(adaptor), openHandler, closeHandler); - myConnection->start(); - } -#endif - - template - self_t& onopen(Func f) - { - openHandler = f; - return *this; - } - - template - self_t& onclose(Func f) - { - closeHandler = f; - return *this; - } - - private: - std::function openHandler; - std::function closeHandler; -}; - -template -struct RuleParameterTraits -{ - using self_t = T; - WebSocketRule& websocket() - { - self_t* self = static_cast(this); - WebSocketRule* p = new WebSocketRule(self->rule); - p->privilegesSet = self->privilegesSet; - self->ruleToUpgrade.reset(p); - return *p; - } - - SseSocketRule& serverSentEvent() - { - self_t* self = static_cast(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(this); - std::optional verb = httpVerbFromBoost(method); - if (verb) - { - self->methodsBitfield = 1U << static_cast(*verb); - } - return *self; - } - - template - self_t& methods(boost::beast::http::verb method, MethodArgs... argsMethod) - { - self_t* self = static_cast(this); - methods(argsMethod...); - std::optional verb = httpVerbFromBoost(method); - if (verb) - { - self->methodsBitfield |= 1U << static_cast(*verb); - } - return *self; - } - - self_t& notFound() - { - self_t* self = static_cast(this); - self->methodsBitfield = 1U << notFoundIndex; - return *self; - } - - self_t& methodNotAllowed() - { - self_t* self = static_cast(this); - self->methodsBitfield = 1U << methodNotAllowedIndex; - return *self; - } - - self_t& privileges( - const std::initializer_list>& p) - { - self_t* self = static_cast(this); - for (const std::initializer_list& privilege : p) - { - self->privilegesSet.emplace_back(privilege); - } - return *self; - } - - template - self_t& privileges(const std::array& p) - { - self_t* self = static_cast(this); - for (const redfish::Privileges& privilege : p) - { - self->privilegesSet.emplace_back(privilege); - } - return *self; - } -}; - -class DynamicRule : public BaseRule, public RuleParameterTraits -{ - 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& asyncResp, - const std::vector& params) override - { - erasedHandler(req, asyncResp, params); - } - - template - void operator()(Func f) - { - using boost::callable_traits::args_t; - constexpr size_t arity = std::tuple_size>::value; - constexpr auto is = std::make_integer_sequence{}; - erasedHandler = wrap(std::move(f), is); - } - - // enable_if Arg1 == request && Arg2 == Response - // enable_if Arg1 == request && Arg2 != response - // enable_if Arg1 != request - - template - std::function&, - const std::vector&)> - wrap(Func f, std::integer_sequence /*is*/) - { - using function_t = crow::utility::FunctionTraits; - - auto ret = detail::routing_handler_call_helper::Wrapped< - Func, typename function_t::template arg...>(); - ret.template set...>( - std::move(f)); - return ret; - } - - private: - std::function&, - const std::vector&)> - erasedHandler; -}; - -template -class TaggedRule : - public BaseRule, - public RuleParameterTraits> -{ - public: - using self_t = TaggedRule; - - explicit TaggedRule(const std::string& ruleIn) : BaseRule(ruleIn) {} - - void validate() override - { - if (!handler) - { - throw std::runtime_error("no handler for url " + rule); - } - } - - template - void operator()(Func&& f) - { - static_assert( - black_magic::CallHelper< - Func, black_magic::S&, - Args...>>::value, - "Handler type is mismatched with URL parameters"); - static_assert( - std::is_same< - void, - decltype(f(std::declval(), - std::declval&>(), - std::declval()...))>::value, - "Handler function with response argument should have void return type"); - - handler = std::forward(f); - } - - void handle(const Request& req, - const std::shared_ptr& asyncResp, - const std::vector& params) override - { - detail::routing_handler_call_helper::Call< - detail::routing_handler_call_helper::CallParams, - 0, black_magic::S, black_magic::S<>>()( - detail::routing_handler_call_helper::CallParams{ - handler, params, req, asyncResp}); - } - - private: - std::function&, Args...)> - handler; -}; - class Trie { public: @@ -1100,171 +510,6 @@ class Router return findRoute; } - // Populate session with user information. - static bool - populateUserInfo(Request& req, - const std::shared_ptr& asyncResp, - const dbus::utility::DBusPropertiesMap& userInfoMap) - { - const std::string* userRolePtr = nullptr; - const bool* remoteUser = nullptr; - const bool* passwordExpired = nullptr; - const std::vector* userGroups = nullptr; - - const bool success = sdbusplus::unpackPropertiesNoThrow( - redfish::dbus_utils::UnpackErrorPrinter(), userInfoMap, - "UserPrivilege", userRolePtr, "RemoteUser", remoteUser, - "UserPasswordExpired", passwordExpired, "UserGroups", userGroups); - - if (!success) - { - BMCWEB_LOG_ERROR << "Failed to unpack user properties."; - asyncResp->res.result( - boost::beast::http::status::internal_server_error); - return false; - } - - if (userRolePtr != nullptr) - { - req.session->userRole = *userRolePtr; - BMCWEB_LOG_DEBUG << "userName = " << req.session->username - << " userRole = " << *userRolePtr; - } - - if (remoteUser == nullptr) - { - BMCWEB_LOG_ERROR << "RemoteUser property missing or wrong type"; - asyncResp->res.result( - boost::beast::http::status::internal_server_error); - return false; - } - bool expired = false; - if (passwordExpired == nullptr) - { - if (!*remoteUser) - { - BMCWEB_LOG_ERROR - << "UserPasswordExpired property is expected for" - " local user but is missing or wrong type"; - asyncResp->res.result( - boost::beast::http::status::internal_server_error); - return false; - } - } - else - { - expired = *passwordExpired; - } - - // Set isConfigureSelfOnly based on D-Bus results. This - // ignores the results from both pamAuthenticateUser and the - // value from any previous use of this session. - req.session->isConfigureSelfOnly = expired; - - if (userGroups != nullptr) - { - // Populate session with user groups. - for (const auto& userGroup : *userGroups) - { - req.session->userGroups.emplace_back(userGroup); - } - } - - return true; - } - - static bool - isUserPrivileged(Request& req, - const std::shared_ptr& asyncResp, - BaseRule& rule) - { - // Get the user's privileges from the role - redfish::Privileges userPrivileges = - redfish::getUserPrivileges(*req.session); - - // Modify privileges if isConfigureSelfOnly. - if (req.session->isConfigureSelfOnly) - { - // Remove all privileges except ConfigureSelf - userPrivileges = userPrivileges.intersection( - redfish::Privileges{"ConfigureSelf"}); - BMCWEB_LOG_DEBUG << "Operation limited to ConfigureSelf"; - } - - if (!rule.checkPrivileges(userPrivileges)) - { - asyncResp->res.result(boost::beast::http::status::forbidden); - if (req.session->isConfigureSelfOnly) - { - redfish::messages::passwordChangeRequired( - asyncResp->res, - boost::urls::format( - "/redfish/v1/AccountService/Accounts/{}", - req.session->username)); - } - return false; - } - - req.userRole = req.session->userRole; - return true; - } - - template - void afterGetUserInfo(Request& req, - const std::shared_ptr& asyncResp, - BaseRule& rule, CallbackFn&& callback, - const boost::system::error_code& ec, - const dbus::utility::DBusPropertiesMap& userInfoMap) - { - if (ec) - { - BMCWEB_LOG_ERROR << "GetUserInfo failed..."; - asyncResp->res.result( - boost::beast::http::status::internal_server_error); - return; - } - - if (!populateUserInfo(req, asyncResp, userInfoMap)) - { - BMCWEB_LOG_ERROR << "Failed to populate user information"; - asyncResp->res.result( - boost::beast::http::status::internal_server_error); - return; - } - - if (!Router::isUserPrivileged(req, asyncResp, rule)) - { - // User is not privileged - BMCWEB_LOG_ERROR << "Insufficient Privilege"; - asyncResp->res.result(boost::beast::http::status::forbidden); - return; - } - callback(req); - } - - template - void validatePrivilege(Request& req, - const std::shared_ptr& asyncResp, - BaseRule& rule, CallbackFn&& callback) - { - if (req.session == nullptr) - { - return; - } - std::string username = req.session->username; - crow::connections::systemBus->async_method_call( - [this, &req, asyncResp, &rule, - callback(std::forward(callback))]( - const boost::system::error_code& ec, - const dbus::utility::DBusPropertiesMap& userInfoMap) mutable { - afterGetUserInfo(req, asyncResp, rule, - std::forward(callback), ec, - userInfoMap); - }, - "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user", - "xyz.openbmc_project.User.Manager", "GetUserInfo", username); - } - template void handleUpgrade(Request& req, const std::shared_ptr& asyncResp, 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 + +#include +#include + +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 upgrade() + { + if (ruleToUpgrade) + { + return std::move(ruleToUpgrade); + } + return {}; + } + + virtual void handle(const Request& /*req*/, + const std::shared_ptr&, + const std::vector&) = 0; +#ifndef BMCWEB_ENABLE_SSL + virtual void + handleUpgrade(const Request& /*req*/, + const std::shared_ptr& 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& asyncResp, + boost::beast::ssl_stream&& /*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(HttpVerb::Get)}; + static_assert(std::numeric_limits::digits > + methodNotAllowedIndex, + "Not enough bits to store bitfield"); + + std::vector privilegesSet; + + std::string rule; + + std::unique_ptr ruleToUpgrade; + + friend class Router; + template + 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 + +#include +#include +#include +#include + +namespace crow +{ +namespace detail +{ +namespace routing_handler_call_helper +{ +template +struct CallPair +{ + using type = T; + static const int pos = Pos; +}; + +template +struct CallParams +{ + H1& handler; + const std::vector& params; + const Request& req; + const std::shared_ptr& asyncResp; +}; + +template +struct Call +{}; + +template +struct Call, + black_magic::S> +{ + void operator()(F cparams) + { + using pushed = typename black_magic::S::template push_back< + CallPair>; + Call, pushed>()(cparams); + } +}; + +template +struct Call, black_magic::S> +{ + void operator()(F cparams) + { + cparams.handler(cparams.req, cparams.asyncResp, + cparams.params[Args1::pos]...); + } +}; + +template +struct Wrapped +{ + template + void set( + Func f, + typename std::enable_if< + !std::is_same< + typename std::tuple_element<0, std::tuple>::type, + const Request&>::value, + int>::type /*enable*/ + = 0) + { + handler = [f = std::forward(f)]( + const Request&, + const std::shared_ptr& asyncResp, + Args... args) { asyncResp->res.result(f(args...)); }; + } + + template + struct ReqHandlerWrapper + { + explicit ReqHandlerWrapper(Func fIn) : f(std::move(fIn)) {} + + void operator()(const Request& req, + const std::shared_ptr& asyncResp, + Args... args) + { + asyncResp->res.result(f(req, args...)); + } + + Func f; + }; + + template + void set( + Func f, + typename std::enable_if< + std::is_same< + typename std::tuple_element<0, std::tuple>::type, + const Request&>::value && + !std::is_same>::type, + const std::shared_ptr&>::value, + int>::type /*enable*/ + = 0) + { + handler = ReqHandlerWrapper(std::move(f)); + /*handler = ( + [f = std::move(f)] + (const Request& req, Response& res, Args... args){ + res.result(f(req, args...)); + res.end(); + });*/ + } + + template + void set( + Func f, + typename std::enable_if< + std::is_same< + typename std::tuple_element<0, std::tuple>::type, + const Request&>::value && + std::is_same>::type, + const std::shared_ptr&>::value, + int>::type /*enable*/ + = 0) + { + handler = std::move(f); + } + + template + struct HandlerTypeHelper + { + using type = std::function&, Args...)>; + using args_type = black_magic::S; + }; + + template + struct HandlerTypeHelper + { + using type = std::function&, Args...)>; + using args_type = black_magic::S; + }; + + template + struct HandlerTypeHelper&, Args...> + { + using type = std::function&, Args...)>; + using args_type = black_magic::S; + }; + + typename HandlerTypeHelper::type handler; + + void operator()(const Request& req, + const std::shared_ptr& asyncResp, + const std::vector& params) + { + detail::routing_handler_call_helper::Call< + detail::routing_handler_call_helper::CallParams, + 0, typename HandlerTypeHelper::args_type, + black_magic::S<>>()( + detail::routing_handler_call_helper::CallParams{ + handler, params, req, asyncResp}); + } +}; +} // namespace routing_handler_call_helper +} // namespace detail + +class DynamicRule : public BaseRule, public RuleParameterTraits +{ + 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& asyncResp, + const std::vector& params) override + { + erasedHandler(req, asyncResp, params); + } + + template + void operator()(Func f) + { + using boost::callable_traits::args_t; + constexpr size_t arity = std::tuple_size>::value; + constexpr auto is = std::make_integer_sequence{}; + erasedHandler = wrap(std::move(f), is); + } + + // enable_if Arg1 == request && Arg2 == Response + // enable_if Arg1 == request && Arg2 != response + // enable_if Arg1 != request + + template + std::function&, + const std::vector&)> + wrap(Func f, std::integer_sequence /*is*/) + { + using function_t = crow::utility::FunctionTraits; + + auto ret = detail::routing_handler_call_helper::Wrapped< + Func, typename function_t::template arg...>(); + ret.template set...>( + std::move(f)); + return ret; + } + + private: + std::function&, + const std::vector&)> + 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 + +#include +#include + +namespace crow +{ +template +struct RuleParameterTraits +{ + using self_t = T; + WebSocketRule& websocket() + { + self_t* self = static_cast(this); + WebSocketRule* p = new WebSocketRule(self->rule); + p->privilegesSet = self->privilegesSet; + self->ruleToUpgrade.reset(p); + return *p; + } + + SseSocketRule& serverSentEvent() + { + self_t* self = static_cast(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(this); + std::optional verb = httpVerbFromBoost(method); + if (verb) + { + self->methodsBitfield = 1U << static_cast(*verb); + } + return *self; + } + + template + self_t& methods(boost::beast::http::verb method, MethodArgs... argsMethod) + { + self_t* self = static_cast(this); + methods(argsMethod...); + std::optional verb = httpVerbFromBoost(method); + if (verb) + { + self->methodsBitfield |= 1U << static_cast(*verb); + } + return *self; + } + + self_t& notFound() + { + self_t* self = static_cast(this); + self->methodsBitfield = 1U << notFoundIndex; + return *self; + } + + self_t& methodNotAllowed() + { + self_t* self = static_cast(this); + self->methodsBitfield = 1U << methodNotAllowedIndex; + return *self; + } + + self_t& privileges( + const std::initializer_list>& p) + { + self_t* self = static_cast(this); + for (const std::initializer_list& privilege : p) + { + self->privilegesSet.emplace_back(privilege); + } + return *self; + } + + template + self_t& privileges(const std::array& p) + { + self_t* self = static_cast(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 + +#include +#include +#include + +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& asyncResp, + const std::vector& /*params*/) override + { + asyncResp->res.result(boost::beast::http::status::not_found); + } + +#ifndef BMCWEB_ENABLE_SSL + void handleUpgrade(const Request& /*req*/, + const std::shared_ptr& /*asyncResp*/, + boost::asio::ip::tcp::socket&& adaptor) override + { + std::shared_ptr< + crow::sse_socket::ConnectionImpl> + myConnection = std::make_shared< + crow::sse_socket::ConnectionImpl>( + std::move(adaptor), openHandler, closeHandler); + myConnection->start(); + } +#else + void handleUpgrade(const Request& /*req*/, + const std::shared_ptr& /*asyncResp*/, + boost::beast::ssl_stream&& + adaptor) override + { + std::shared_ptr>> + myConnection = std::make_shared>>( + std::move(adaptor), openHandler, closeHandler); + myConnection->start(); + } +#endif + + template + self_t& onopen(Func f) + { + openHandler = f; + return *this; + } + + template + self_t& onclose(Func f) + { + closeHandler = f; + return *this; + } + + private: + std::function openHandler; + std::function 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 + +#include +#include +#include + +namespace crow +{ +template +class TaggedRule : + public BaseRule, + public RuleParameterTraits> +{ + public: + using self_t = TaggedRule; + + explicit TaggedRule(const std::string& ruleIn) : BaseRule(ruleIn) {} + + void validate() override + { + if (!handler) + { + throw std::runtime_error("no handler for url " + rule); + } + } + + template + void operator()(Func&& f) + { + static_assert( + black_magic::CallHelper< + Func, black_magic::S&, + Args...>>::value, + "Handler type is mismatched with URL parameters"); + static_assert( + std::is_same< + void, + decltype(f(std::declval(), + std::declval&>(), + std::declval()...))>::value, + "Handler function with response argument should have void return type"); + + handler = std::forward(f); + } + + void handle(const Request& req, + const std::shared_ptr& asyncResp, + const std::vector& params) override + { + detail::routing_handler_call_helper::Call< + detail::routing_handler_call_helper::CallParams, + 0, black_magic::S, black_magic::S<>>()( + detail::routing_handler_call_helper::CallParams{ + handler, params, req, asyncResp}); + } + + private: + std::function&, 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 + +#include +#include +#include + +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& asyncResp, + const std::vector& /*params*/) override + { + asyncResp->res.result(boost::beast::http::status::not_found); + } + +#ifndef BMCWEB_ENABLE_SSL + void handleUpgrade(const Request& req, + const std::shared_ptr& /*asyncResp*/, + boost::asio::ip::tcp::socket&& adaptor) override + { + BMCWEB_LOG_DEBUG << "Websocket handles upgrade"; + std::shared_ptr< + crow::websocket::ConnectionImpl> + myConnection = std::make_shared< + crow::websocket::ConnectionImpl>( + req, req.url(), std::move(adaptor), openHandler, messageHandler, + messageExHandler, closeHandler, errorHandler); + myConnection->start(); + } +#else + void handleUpgrade(const Request& req, + const std::shared_ptr& /*asyncResp*/, + boost::beast::ssl_stream&& + adaptor) override + { + BMCWEB_LOG_DEBUG << "Websocket handles upgrade"; + std::shared_ptr>> + myConnection = std::make_shared>>( + req, req.url(), std::move(adaptor), openHandler, messageHandler, + messageExHandler, closeHandler, errorHandler); + myConnection->start(); + } +#endif + + template + self_t& onopen(Func f) + { + openHandler = f; + return *this; + } + + template + self_t& onmessage(Func f) + { + messageHandler = f; + return *this; + } + + template + self_t& onmessageex(Func f) + { + messageExHandler = f; + return *this; + } + + template + self_t& onclose(Func f) + { + closeHandler = f; + return *this; + } + + template + self_t& onerror(Func f) + { + errorHandler = f; + return *this; + } + + protected: + std::function openHandler; + std::function + messageHandler; + std::function&& whenComplete)> + messageExHandler; + std::function + closeHandler; + std::function errorHandler; +}; +} // namespace crow -- cgit v1.2.3