xref: /openbmc/bmcweb/http/routing/dynamicrule.hpp (revision baade6cd)
1 #pragma once
2 #include "baserule.hpp"
3 #include "ruleparametertraits.hpp"
4 #include "websocket.hpp"
5 
6 #include <boost/beast/http/verb.hpp>
7 
8 #include <functional>
9 #include <limits>
10 #include <string>
11 #include <type_traits>
12 
13 namespace crow
14 {
15 namespace detail
16 {
17 namespace routing_handler_call_helper
18 {
19 template <typename T, int Pos>
20 struct CallPair
21 {
22     using type = T;
23     static const int pos = Pos;
24 };
25 
26 template <typename H1>
27 struct CallParams
28 {
29     H1& handler;
30     const std::vector<std::string>& params;
31     const Request& req;
32     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp;
33 };
34 
35 template <typename F, int NString, typename S1, typename S2>
36 struct Call
37 {};
38 
39 template <typename F, int NString, typename... Args1, typename... Args2>
40 struct Call<F, NString, black_magic::S<std::string, Args1...>,
41             black_magic::S<Args2...>>
42 {
43     void operator()(F cparams)
44     {
45         using pushed = typename black_magic::S<Args2...>::template push_back<
46             CallPair<std::string, NString>>;
47         Call<F, NString + 1, black_magic::S<Args1...>, pushed>()(cparams);
48     }
49 };
50 
51 template <typename F, int NString, typename... Args1>
52 struct Call<F, NString, black_magic::S<>, black_magic::S<Args1...>>
53 {
54     void operator()(F cparams)
55     {
56         cparams.handler(cparams.req, cparams.asyncResp,
57                         cparams.params[Args1::pos]...);
58     }
59 };
60 
61 template <typename Func, typename... ArgsWrapped>
62 struct Wrapped
63 {
64     template <typename... Args>
65     void set(
66         Func f,
67         typename std::enable_if<
68             !std::is_same<
69                 typename std::tuple_element<0, std::tuple<Args..., void>>::type,
70                 const Request&>::value,
71             int>::type /*enable*/
72         = 0)
73     {
74         handler = [f = std::forward<Func>(f)](
75                       const Request&,
76                       const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
77                       Args... args) { asyncResp->res.result(f(args...)); };
78     }
79 
80     template <typename Req, typename... Args>
81     struct ReqHandlerWrapper
82     {
83         explicit ReqHandlerWrapper(Func fIn) : f(std::move(fIn)) {}
84 
85         void operator()(const Request& req,
86                         const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
87                         Args... args)
88         {
89             asyncResp->res.result(f(req, args...));
90         }
91 
92         Func f;
93     };
94 
95     template <typename... Args>
96     void set(
97         Func f,
98         typename std::enable_if<
99             std::is_same<
100                 typename std::tuple_element<0, std::tuple<Args..., void>>::type,
101                 const Request&>::value &&
102                 !std::is_same<typename std::tuple_element<
103                                   1, std::tuple<Args..., void, void>>::type,
104                               const std::shared_ptr<bmcweb::AsyncResp>&>::value,
105             int>::type /*enable*/
106         = 0)
107     {
108         handler = ReqHandlerWrapper<Args...>(std::move(f));
109         /*handler = (
110             [f = std::move(f)]
111             (const Request& req, Response& res, Args... args){
112                  res.result(f(req, args...));
113                  res.end();
114             });*/
115     }
116 
117     template <typename... Args>
118     void set(
119         Func f,
120         typename std::enable_if<
121             std::is_same<
122                 typename std::tuple_element<0, std::tuple<Args..., void>>::type,
123                 const Request&>::value &&
124                 std::is_same<typename std::tuple_element<
125                                  1, std::tuple<Args..., void, void>>::type,
126                              const std::shared_ptr<bmcweb::AsyncResp>&>::value,
127             int>::type /*enable*/
128         = 0)
129     {
130         handler = std::move(f);
131     }
132 
133     template <typename... Args>
134     struct HandlerTypeHelper
135     {
136         using type = std::function<void(
137             const crow::Request& /*req*/,
138             const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>;
139         using args_type = black_magic::S<Args...>;
140     };
141 
142     template <typename... Args>
143     struct HandlerTypeHelper<const Request&, Args...>
144     {
145         using type = std::function<void(
146             const crow::Request& /*req*/,
147             const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>;
148         using args_type = black_magic::S<Args...>;
149     };
150 
151     template <typename... Args>
152     struct HandlerTypeHelper<const Request&,
153                              const std::shared_ptr<bmcweb::AsyncResp>&, Args...>
154     {
155         using type = std::function<void(
156             const crow::Request& /*req*/,
157             const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>;
158         using args_type = black_magic::S<Args...>;
159     };
160 
161     typename HandlerTypeHelper<ArgsWrapped...>::type handler;
162 
163     void operator()(const Request& req,
164                     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
165                     const std::vector<std::string>& params)
166     {
167         detail::routing_handler_call_helper::Call<
168             detail::routing_handler_call_helper::CallParams<decltype(handler)>,
169             0, typename HandlerTypeHelper<ArgsWrapped...>::args_type,
170             black_magic::S<>>()(
171             detail::routing_handler_call_helper::CallParams<decltype(handler)>{
172                 handler, params, req, asyncResp});
173     }
174 };
175 } // namespace routing_handler_call_helper
176 } // namespace detail
177 
178 class DynamicRule : public BaseRule, public RuleParameterTraits<DynamicRule>
179 {
180   public:
181     explicit DynamicRule(const std::string& ruleIn) : BaseRule(ruleIn) {}
182 
183     void validate() override
184     {
185         if (!erasedHandler)
186         {
187             throw std::runtime_error("no handler for url " + rule);
188         }
189     }
190 
191     void handle(const Request& req,
192                 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
193                 const std::vector<std::string>& params) override
194     {
195         erasedHandler(req, asyncResp, params);
196     }
197 
198     template <typename Func>
199     void operator()(Func f)
200     {
201         using boost::callable_traits::args_t;
202         constexpr size_t arity = std::tuple_size<args_t<Func>>::value;
203         constexpr auto is = std::make_integer_sequence<unsigned, arity>{};
204         erasedHandler = wrap(std::move(f), is);
205     }
206 
207     // enable_if Arg1 == request && Arg2 == Response
208     // enable_if Arg1 == request && Arg2 != response
209     // enable_if Arg1 != request
210 
211     template <typename Func, unsigned... Indices>
212     std::function<void(const Request&,
213                        const std::shared_ptr<bmcweb::AsyncResp>&,
214                        const std::vector<std::string>&)>
215         wrap(Func f, std::integer_sequence<unsigned, Indices...> /*is*/)
216     {
217         using function_t = crow::utility::FunctionTraits<Func>;
218 
219         auto ret = detail::routing_handler_call_helper::Wrapped<
220             Func, typename function_t::template arg<Indices>...>();
221         ret.template set<typename function_t::template arg<Indices>...>(
222             std::move(f));
223         return ret;
224     }
225 
226   private:
227     std::function<void(const Request&,
228                        const std::shared_ptr<bmcweb::AsyncResp>&,
229                        const std::vector<std::string>&)>
230         erasedHandler;
231 };
232 
233 } // namespace crow
234