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
diff --git a/http/routing/baserule.hpp b/http/routing/baserule.hpp
new file mode 100644
index 0000000..0913020
--- /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 0000000..746765c
--- /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 0000000..bb3f563
--- /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 0000000..c0a4e50
--- /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 0000000..ef62ab0
--- /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 0000000..c8f706d
--- /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