xref: /openbmc/bmcweb/http/routing.hpp (revision 9d192c74)
1 #pragma once
2 
3 #include "async_resp.hpp"
4 #include "common.hpp"
5 #include "dbus_utility.hpp"
6 #include "error_messages.hpp"
7 #include "http_request.hpp"
8 #include "http_response.hpp"
9 #include "logging.hpp"
10 #include "privileges.hpp"
11 #include "sessions.hpp"
12 #include "utility.hpp"
13 #include "utils/dbus_utils.hpp"
14 #include "verb.hpp"
15 #include "websocket.hpp"
16 
17 #include <boost/beast/ssl/ssl_stream.hpp>
18 #include <boost/container/flat_map.hpp>
19 #include <sdbusplus/unpack_properties.hpp>
20 
21 #include <cerrno>
22 #include <cstdint>
23 #include <cstdlib>
24 #include <limits>
25 #include <memory>
26 #include <optional>
27 #include <tuple>
28 #include <utility>
29 #include <vector>
30 
31 namespace crow
32 {
33 
34 class BaseRule
35 {
36   public:
37     explicit BaseRule(const std::string& thisRule) : rule(thisRule)
38     {}
39 
40     virtual ~BaseRule() = default;
41 
42     BaseRule(const BaseRule&) = delete;
43     BaseRule(BaseRule&&) = delete;
44     BaseRule& operator=(const BaseRule&) = delete;
45     BaseRule& operator=(const BaseRule&&) = delete;
46 
47     virtual void validate() = 0;
48     std::unique_ptr<BaseRule> upgrade()
49     {
50         if (ruleToUpgrade)
51         {
52             return std::move(ruleToUpgrade);
53         }
54         return {};
55     }
56 
57     virtual void handle(const Request& /*req*/,
58                         const std::shared_ptr<bmcweb::AsyncResp>&,
59                         const RoutingParams&) = 0;
60 #ifndef BMCWEB_ENABLE_SSL
61     virtual void
62         handleUpgrade(const Request& /*req*/,
63                       const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
64                       boost::asio::ip::tcp::socket&& /*adaptor*/)
65     {
66         asyncResp->res.result(boost::beast::http::status::not_found);
67     }
68 #else
69     virtual void handleUpgrade(
70         const Request& /*req*/,
71         const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
72         boost::beast::ssl_stream<boost::asio::ip::tcp::socket>&& /*adaptor*/)
73     {
74         asyncResp->res.result(boost::beast::http::status::not_found);
75     }
76 #endif
77 
78     size_t getMethods() const
79     {
80         return methodsBitfield;
81     }
82 
83     bool checkPrivileges(const redfish::Privileges& userPrivileges)
84     {
85         // If there are no privileges assigned, assume no privileges
86         // required
87         if (privilegesSet.empty())
88         {
89             return true;
90         }
91 
92         for (const redfish::Privileges& requiredPrivileges : privilegesSet)
93         {
94             if (userPrivileges.isSupersetOf(requiredPrivileges))
95             {
96                 return true;
97             }
98         }
99         return false;
100     }
101 
102     size_t methodsBitfield{1 << static_cast<size_t>(HttpVerb::Get)};
103     static_assert(std::numeric_limits<decltype(methodsBitfield)>::digits >
104                       methodNotAllowedIndex,
105                   "Not enough bits to store bitfield");
106 
107     std::vector<redfish::Privileges> privilegesSet;
108 
109     std::string rule;
110     std::string nameStr;
111 
112     std::unique_ptr<BaseRule> ruleToUpgrade;
113 
114     friend class Router;
115     template <typename T>
116     friend struct RuleParameterTraits;
117 };
118 
119 namespace detail
120 {
121 namespace routing_handler_call_helper
122 {
123 template <typename T, int Pos>
124 struct CallPair
125 {
126     using type = T;
127     static const int pos = Pos;
128 };
129 
130 template <typename H1>
131 struct CallParams
132 {
133     H1& handler;
134     const RoutingParams& params;
135     const Request& req;
136     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp;
137 };
138 
139 template <typename F, int NInt, int NUint, int NDouble, int NString,
140           typename S1, typename S2>
141 struct Call
142 {};
143 
144 template <typename F, int NInt, int NUint, int NDouble, int NString,
145           typename... Args1, typename... Args2>
146 struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<int64_t, Args1...>,
147             black_magic::S<Args2...>>
148 {
149     void operator()(F cparams)
150     {
151         using pushed = typename black_magic::S<Args2...>::template push_back<
152             CallPair<int64_t, NInt>>;
153         Call<F, NInt + 1, NUint, NDouble, NString, black_magic::S<Args1...>,
154              pushed>()(cparams);
155     }
156 };
157 
158 template <typename F, int NInt, int NUint, int NDouble, int NString,
159           typename... Args1, typename... Args2>
160 struct Call<F, NInt, NUint, NDouble, NString,
161             black_magic::S<uint64_t, Args1...>, black_magic::S<Args2...>>
162 {
163     void operator()(F cparams)
164     {
165         using pushed = typename black_magic::S<Args2...>::template push_back<
166             CallPair<uint64_t, NUint>>;
167         Call<F, NInt, NUint + 1, NDouble, NString, black_magic::S<Args1...>,
168              pushed>()(cparams);
169     }
170 };
171 
172 template <typename F, int NInt, int NUint, int NDouble, int NString,
173           typename... Args1, typename... Args2>
174 struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<double, Args1...>,
175             black_magic::S<Args2...>>
176 {
177     void operator()(F cparams)
178     {
179         using pushed = typename black_magic::S<Args2...>::template push_back<
180             CallPair<double, NDouble>>;
181         Call<F, NInt, NUint, NDouble + 1, NString, black_magic::S<Args1...>,
182              pushed>()(cparams);
183     }
184 };
185 
186 template <typename F, int NInt, int NUint, int NDouble, int NString,
187           typename... Args1, typename... Args2>
188 struct Call<F, NInt, NUint, NDouble, NString,
189             black_magic::S<std::string, Args1...>, black_magic::S<Args2...>>
190 {
191     void operator()(F cparams)
192     {
193         using pushed = typename black_magic::S<Args2...>::template push_back<
194             CallPair<std::string, NString>>;
195         Call<F, NInt, NUint, NDouble, NString + 1, black_magic::S<Args1...>,
196              pushed>()(cparams);
197     }
198 };
199 
200 template <typename F, int NInt, int NUint, int NDouble, int NString,
201           typename... Args1>
202 struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<>,
203             black_magic::S<Args1...>>
204 {
205     void operator()(F cparams)
206     {
207         cparams.handler(
208             cparams.req, cparams.asyncResp,
209             cparams.params.template get<typename Args1::type>(Args1::pos)...);
210     }
211 };
212 
213 template <typename Func, typename... ArgsWrapped>
214 struct Wrapped
215 {
216     template <typename... Args>
217     void set(
218         Func f,
219         typename std::enable_if<
220             !std::is_same<
221                 typename std::tuple_element<0, std::tuple<Args..., void>>::type,
222                 const Request&>::value,
223             int>::type /*enable*/
224         = 0)
225     {
226         handler = [f = std::forward<Func>(f)](
227                       const Request&,
228                       const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
229                       Args... args) { asyncResp->res.result(f(args...)); };
230     }
231 
232     template <typename Req, typename... Args>
233     struct ReqHandlerWrapper
234     {
235         explicit ReqHandlerWrapper(Func fIn) : f(std::move(fIn))
236         {}
237 
238         void operator()(const Request& req,
239                         const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
240                         Args... args)
241         {
242             asyncResp->res.result(f(req, args...));
243         }
244 
245         Func f;
246     };
247 
248     template <typename... Args>
249     void set(
250         Func f,
251         typename std::enable_if<
252             std::is_same<
253                 typename std::tuple_element<0, std::tuple<Args..., void>>::type,
254                 const Request&>::value &&
255                 !std::is_same<typename std::tuple_element<
256                                   1, std::tuple<Args..., void, void>>::type,
257                               const std::shared_ptr<bmcweb::AsyncResp>&>::value,
258             int>::type /*enable*/
259         = 0)
260     {
261         handler = ReqHandlerWrapper<Args...>(std::move(f));
262         /*handler = (
263             [f = std::move(f)]
264             (const Request& req, Response& res, Args... args){
265                  res.result(f(req, args...));
266                  res.end();
267             });*/
268     }
269 
270     template <typename... Args>
271     void set(
272         Func f,
273         typename std::enable_if<
274             std::is_same<
275                 typename std::tuple_element<0, std::tuple<Args..., void>>::type,
276                 const Request&>::value &&
277                 std::is_same<typename std::tuple_element<
278                                  1, std::tuple<Args..., void, void>>::type,
279                              const std::shared_ptr<bmcweb::AsyncResp>&>::value,
280             int>::type /*enable*/
281         = 0)
282     {
283         handler = std::move(f);
284     }
285 
286     template <typename... Args>
287     struct HandlerTypeHelper
288     {
289         using type = std::function<void(
290             const crow::Request& /*req*/,
291             const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>;
292         using args_type =
293             black_magic::S<typename black_magic::PromoteT<Args>...>;
294     };
295 
296     template <typename... Args>
297     struct HandlerTypeHelper<const Request&, Args...>
298     {
299         using type = std::function<void(
300             const crow::Request& /*req*/,
301             const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>;
302         using args_type =
303             black_magic::S<typename black_magic::PromoteT<Args>...>;
304     };
305 
306     template <typename... Args>
307     struct HandlerTypeHelper<const Request&,
308                              const std::shared_ptr<bmcweb::AsyncResp>&, Args...>
309     {
310         using type = std::function<void(
311             const crow::Request& /*req*/,
312             const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>;
313         using args_type =
314             black_magic::S<typename black_magic::PromoteT<Args>...>;
315     };
316 
317     typename HandlerTypeHelper<ArgsWrapped...>::type handler;
318 
319     void operator()(const Request& req,
320                     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
321                     const RoutingParams& params)
322     {
323         detail::routing_handler_call_helper::Call<
324             detail::routing_handler_call_helper::CallParams<decltype(handler)>,
325             0, 0, 0, 0, typename HandlerTypeHelper<ArgsWrapped...>::args_type,
326             black_magic::S<>>()(
327             detail::routing_handler_call_helper::CallParams<decltype(handler)>{
328                 handler, params, req, asyncResp});
329     }
330 };
331 } // namespace routing_handler_call_helper
332 } // namespace detail
333 
334 class WebSocketRule : public BaseRule
335 {
336     using self_t = WebSocketRule;
337 
338   public:
339     explicit WebSocketRule(const std::string& ruleIn) : BaseRule(ruleIn)
340     {}
341 
342     void validate() override
343     {}
344 
345     void handle(const Request& /*req*/,
346                 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
347                 const RoutingParams& /*params*/) override
348     {
349         asyncResp->res.result(boost::beast::http::status::not_found);
350     }
351 
352 #ifndef BMCWEB_ENABLE_SSL
353     void handleUpgrade(const Request& req,
354                        const std::shared_ptr<bmcweb::AsyncResp>& /*asyncResp*/,
355                        boost::asio::ip::tcp::socket&& adaptor) override
356     {
357         BMCWEB_LOG_DEBUG << "Websocket handles upgrade";
358         std::shared_ptr<
359             crow::websocket::ConnectionImpl<boost::asio::ip::tcp::socket>>
360             myConnection = std::make_shared<
361                 crow::websocket::ConnectionImpl<boost::asio::ip::tcp::socket>>(
362                 req, std::move(adaptor), openHandler, messageHandler,
363                 messageExHandler, closeHandler, errorHandler);
364         myConnection->start();
365     }
366 #else
367     void handleUpgrade(const Request& req,
368                        const std::shared_ptr<bmcweb::AsyncResp>& /*asyncResp*/,
369                        boost::beast::ssl_stream<boost::asio::ip::tcp::socket>&&
370                            adaptor) override
371     {
372         BMCWEB_LOG_DEBUG << "Websocket handles upgrade";
373         std::shared_ptr<crow::websocket::ConnectionImpl<
374             boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>>
375             myConnection = std::make_shared<crow::websocket::ConnectionImpl<
376                 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>>(
377                 req, std::move(adaptor), openHandler, messageHandler,
378                 messageExHandler, closeHandler, errorHandler);
379         myConnection->start();
380     }
381 #endif
382 
383     template <typename Func>
384     self_t& onopen(Func f)
385     {
386         openHandler = f;
387         return *this;
388     }
389 
390     template <typename Func>
391     self_t& onmessage(Func f)
392     {
393         messageHandler = f;
394         return *this;
395     }
396 
397     template <typename Func>
398     self_t& onmessageex(Func f)
399     {
400         messageExHandler = f;
401         return *this;
402     }
403 
404     template <typename Func>
405     self_t& onclose(Func f)
406     {
407         closeHandler = f;
408         return *this;
409     }
410 
411     template <typename Func>
412     self_t& onerror(Func f)
413     {
414         errorHandler = f;
415         return *this;
416     }
417 
418   protected:
419     std::function<void(crow::websocket::Connection&)> openHandler;
420     std::function<void(crow::websocket::Connection&, const std::string&, bool)>
421         messageHandler;
422     std::function<void(crow::websocket::Connection&, std::string_view,
423                        crow::websocket::MessageType type,
424                        std::function<void()>&& whenComplete)>
425         messageExHandler;
426     std::function<void(crow::websocket::Connection&, const std::string&)>
427         closeHandler;
428     std::function<void(crow::websocket::Connection&)> errorHandler;
429 };
430 
431 template <typename T>
432 struct RuleParameterTraits
433 {
434     using self_t = T;
435     WebSocketRule& websocket()
436     {
437         self_t* self = static_cast<self_t*>(this);
438         WebSocketRule* p = new WebSocketRule(self->rule);
439         p->privilegesSet = self->privilegesSet;
440         self->ruleToUpgrade.reset(p);
441         return *p;
442     }
443 
444     self_t& name(std::string_view name) noexcept
445     {
446         self_t* self = static_cast<self_t*>(this);
447         self->nameStr = name;
448         return *self;
449     }
450 
451     self_t& methods(boost::beast::http::verb method)
452     {
453         self_t* self = static_cast<self_t*>(this);
454         std::optional<HttpVerb> verb = httpVerbFromBoost(method);
455         if (verb)
456         {
457             self->methodsBitfield = 1U << static_cast<size_t>(*verb);
458         }
459         return *self;
460     }
461 
462     template <typename... MethodArgs>
463     self_t& methods(boost::beast::http::verb method, MethodArgs... argsMethod)
464     {
465         self_t* self = static_cast<self_t*>(this);
466         methods(argsMethod...);
467         std::optional<HttpVerb> verb = httpVerbFromBoost(method);
468         if (verb)
469         {
470             self->methodsBitfield |= 1U << static_cast<size_t>(*verb);
471         }
472         return *self;
473     }
474 
475     self_t& notFound()
476     {
477         self_t* self = static_cast<self_t*>(this);
478         self->methodsBitfield = 1U << notFoundIndex;
479         return *self;
480     }
481 
482     self_t& methodNotAllowed()
483     {
484         self_t* self = static_cast<self_t*>(this);
485         self->methodsBitfield = 1U << methodNotAllowedIndex;
486         return *self;
487     }
488 
489     self_t& privileges(
490         const std::initializer_list<std::initializer_list<const char*>>& p)
491     {
492         self_t* self = static_cast<self_t*>(this);
493         for (const std::initializer_list<const char*>& privilege : p)
494         {
495             self->privilegesSet.emplace_back(privilege);
496         }
497         return *self;
498     }
499 
500     template <size_t N, typename... MethodArgs>
501     self_t& privileges(const std::array<redfish::Privileges, N>& p)
502     {
503         self_t* self = static_cast<self_t*>(this);
504         for (const redfish::Privileges& privilege : p)
505         {
506             self->privilegesSet.emplace_back(privilege);
507         }
508         return *self;
509     }
510 };
511 
512 class DynamicRule : public BaseRule, public RuleParameterTraits<DynamicRule>
513 {
514   public:
515     explicit DynamicRule(const std::string& ruleIn) : BaseRule(ruleIn)
516     {}
517 
518     void validate() override
519     {
520         if (!erasedHandler)
521         {
522             throw std::runtime_error(nameStr + (!nameStr.empty() ? ": " : "") +
523                                      "no handler for url " + rule);
524         }
525     }
526 
527     void handle(const Request& req,
528                 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
529                 const RoutingParams& params) override
530     {
531         erasedHandler(req, asyncResp, params);
532     }
533 
534     template <typename Func>
535     void operator()(Func f)
536     {
537         using boost::callable_traits::args_t;
538         constexpr size_t arity = std::tuple_size<args_t<Func>>::value;
539         constexpr auto is = std::make_integer_sequence<unsigned, arity>{};
540         erasedHandler = wrap(std::move(f), is);
541     }
542 
543     // enable_if Arg1 == request && Arg2 == Response
544     // enable_if Arg1 == request && Arg2 != response
545     // enable_if Arg1 != request
546 
547     template <typename Func, unsigned... Indices>
548     std::function<void(const Request&,
549                        const std::shared_ptr<bmcweb::AsyncResp>&,
550                        const RoutingParams&)>
551         wrap(Func f, std::integer_sequence<unsigned, Indices...> /*is*/)
552     {
553         using function_t = crow::utility::FunctionTraits<Func>;
554 
555         if (!black_magic::isParameterTagCompatible(
556                 black_magic::getParameterTag(rule.c_str()),
557                 black_magic::computeParameterTagFromArgsList<
558                     typename function_t::template arg<Indices>...>::value))
559         {
560             throw std::runtime_error("routeDynamic: Handler type is mismatched "
561                                      "with URL parameters: " +
562                                      rule);
563         }
564         auto ret = detail::routing_handler_call_helper::Wrapped<
565             Func, typename function_t::template arg<Indices>...>();
566         ret.template set<typename function_t::template arg<Indices>...>(
567             std::move(f));
568         return ret;
569     }
570 
571     template <typename Func>
572     void operator()(std::string name, Func&& f)
573     {
574         nameStr = std::move(name);
575         (*this).template operator()<Func>(std::forward(f));
576     }
577 
578   private:
579     std::function<void(const Request&,
580                        const std::shared_ptr<bmcweb::AsyncResp>&,
581                        const RoutingParams&)>
582         erasedHandler;
583 };
584 
585 template <typename... Args>
586 class TaggedRule :
587     public BaseRule,
588     public RuleParameterTraits<TaggedRule<Args...>>
589 {
590   public:
591     using self_t = TaggedRule<Args...>;
592 
593     explicit TaggedRule(const std::string& ruleIn) : BaseRule(ruleIn)
594     {}
595 
596     void validate() override
597     {
598         if (!handler)
599         {
600             throw std::runtime_error(nameStr + (!nameStr.empty() ? ": " : "") +
601                                      "no handler for url " + rule);
602         }
603     }
604 
605     template <typename Func>
606     typename std::enable_if<
607         black_magic::CallHelper<Func, black_magic::S<Args...>>::value,
608         void>::type
609         operator()(Func&& f)
610     {
611         static_assert(
612             black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
613                 black_magic::CallHelper<
614                     Func, black_magic::S<crow::Request, Args...>>::value,
615             "Handler type is mismatched with URL parameters");
616         static_assert(
617             !std::is_same<void, decltype(f(std::declval<Args>()...))>::value,
618             "Handler function cannot have void return type; valid return "
619             "types: "
620             "string, int, crow::response, nlohmann::json");
621 
622         handler = [f = std::forward<Func>(f)](
623                       const Request&,
624                       const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
625                       Args... args) { asyncResp->res.result(f(args...)); };
626     }
627 
628     template <typename Func>
629     typename std::enable_if<
630         !black_magic::CallHelper<Func, black_magic::S<Args...>>::value &&
631             black_magic::CallHelper<
632                 Func, black_magic::S<crow::Request, Args...>>::value,
633         void>::type
634         operator()(Func&& f)
635     {
636         static_assert(
637             black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
638                 black_magic::CallHelper<
639                     Func, black_magic::S<crow::Request, Args...>>::value,
640             "Handler type is mismatched with URL parameters");
641         static_assert(
642             !std::is_same<void, decltype(f(std::declval<crow::Request>(),
643                                            std::declval<Args>()...))>::value,
644             "Handler function cannot have void return type; valid return "
645             "types: "
646             "string, int, crow::response,nlohmann::json");
647 
648         handler = [f = std::forward<Func>(f)](
649                       const crow::Request& req,
650                       const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
651                       Args... args) { asyncResp->res.result(f(req, args...)); };
652     }
653 
654     template <typename Func>
655     typename std::enable_if<
656         !black_magic::CallHelper<Func, black_magic::S<Args...>>::value &&
657             !black_magic::CallHelper<
658                 Func, black_magic::S<crow::Request, Args...>>::value,
659         void>::type
660         operator()(Func&& f)
661     {
662         static_assert(
663             black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
664                 black_magic::CallHelper<
665                     Func, black_magic::S<crow::Request, Args...>>::value ||
666                 black_magic::CallHelper<
667                     Func, black_magic::S<crow::Request,
668                                          std::shared_ptr<bmcweb::AsyncResp>&,
669                                          Args...>>::value,
670             "Handler type is mismatched with URL parameters");
671         static_assert(
672             std::is_same<
673                 void,
674                 decltype(f(std::declval<crow::Request>(),
675                            std::declval<std::shared_ptr<bmcweb::AsyncResp>&>(),
676                            std::declval<Args>()...))>::value,
677             "Handler function with response argument should have void "
678             "return "
679             "type");
680 
681         handler = std::forward<Func>(f);
682     }
683 
684     template <typename Func>
685     void operator()(std::string_view name, Func&& f)
686     {
687         nameStr = name;
688         (*this).template operator()<Func>(std::forward(f));
689     }
690 
691     void handle(const Request& req,
692                 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
693                 const RoutingParams& params) override
694     {
695         detail::routing_handler_call_helper::Call<
696             detail::routing_handler_call_helper::CallParams<decltype(handler)>,
697             0, 0, 0, 0, black_magic::S<Args...>, black_magic::S<>>()(
698             detail::routing_handler_call_helper::CallParams<decltype(handler)>{
699                 handler, params, req, asyncResp});
700     }
701 
702   private:
703     std::function<void(const crow::Request&,
704                        const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>
705         handler;
706 };
707 
708 class Trie
709 {
710   public:
711     struct Node
712     {
713         unsigned ruleIndex{};
714         std::array<size_t, static_cast<size_t>(ParamType::MAX)>
715             paramChildrens{};
716         using ChildMap = boost::container::flat_map<
717             std::string, unsigned, std::less<>,
718             std::vector<std::pair<std::string, unsigned>>>;
719         ChildMap children;
720 
721         bool isSimpleNode() const
722         {
723             return ruleIndex == 0 &&
724                    std::all_of(std::begin(paramChildrens),
725                                std::end(paramChildrens),
726                                [](size_t x) { return x == 0U; });
727         }
728     };
729 
730     Trie() : nodes(1)
731     {}
732 
733   private:
734     void optimizeNode(Node* node)
735     {
736         for (size_t x : node->paramChildrens)
737         {
738             if (x == 0U)
739             {
740                 continue;
741             }
742             Node* child = &nodes[x];
743             optimizeNode(child);
744         }
745         if (node->children.empty())
746         {
747             return;
748         }
749         bool mergeWithChild = true;
750         for (const Node::ChildMap::value_type& kv : node->children)
751         {
752             Node* child = &nodes[kv.second];
753             if (!child->isSimpleNode())
754             {
755                 mergeWithChild = false;
756                 break;
757             }
758         }
759         if (mergeWithChild)
760         {
761             Node::ChildMap merged;
762             for (const Node::ChildMap::value_type& kv : node->children)
763             {
764                 Node* child = &nodes[kv.second];
765                 for (const Node::ChildMap::value_type& childKv :
766                      child->children)
767                 {
768                     merged[kv.first + childKv.first] = childKv.second;
769                 }
770             }
771             node->children = std::move(merged);
772             optimizeNode(node);
773         }
774         else
775         {
776             for (const Node::ChildMap::value_type& kv : node->children)
777             {
778                 Node* child = &nodes[kv.second];
779                 optimizeNode(child);
780             }
781         }
782     }
783 
784     void optimize()
785     {
786         optimizeNode(head());
787     }
788 
789   public:
790     void validate()
791     {
792         optimize();
793     }
794 
795     void findRouteIndexes(const std::string& reqUrl,
796                           std::vector<unsigned>& routeIndexes,
797                           const Node* node = nullptr, unsigned pos = 0) const
798     {
799         if (node == nullptr)
800         {
801             node = head();
802         }
803         for (const Node::ChildMap::value_type& kv : node->children)
804         {
805             const std::string& fragment = kv.first;
806             const Node* child = &nodes[kv.second];
807             if (pos >= reqUrl.size())
808             {
809                 if (child->ruleIndex != 0 && fragment != "/")
810                 {
811                     routeIndexes.push_back(child->ruleIndex);
812                 }
813                 findRouteIndexes(reqUrl, routeIndexes, child,
814                                  static_cast<unsigned>(pos + fragment.size()));
815             }
816             else
817             {
818                 if (reqUrl.compare(pos, fragment.size(), fragment) == 0)
819                 {
820                     findRouteIndexes(
821                         reqUrl, routeIndexes, child,
822                         static_cast<unsigned>(pos + fragment.size()));
823                 }
824             }
825         }
826     }
827 
828     std::pair<unsigned, RoutingParams>
829         find(std::string_view reqUrl, const Node* node = nullptr,
830              size_t pos = 0, RoutingParams* params = nullptr) const
831     {
832         RoutingParams empty;
833         if (params == nullptr)
834         {
835             params = &empty;
836         }
837 
838         unsigned found{};
839         RoutingParams matchParams;
840 
841         if (node == nullptr)
842         {
843             node = head();
844         }
845         if (pos == reqUrl.size())
846         {
847             return {node->ruleIndex, *params};
848         }
849 
850         auto updateFound =
851             [&found, &matchParams](std::pair<unsigned, RoutingParams>& ret) {
852             if (ret.first != 0U && (found == 0U || found > ret.first))
853             {
854                 found = ret.first;
855                 matchParams = std::move(ret.second);
856             }
857         };
858 
859         if (node->paramChildrens[static_cast<size_t>(ParamType::INT)] != 0U)
860         {
861             char c = reqUrl[pos];
862             if ((c >= '0' && c <= '9') || c == '+' || c == '-')
863             {
864                 char* eptr = nullptr;
865                 errno = 0;
866                 long long int value =
867                     std::strtoll(reqUrl.data() + pos, &eptr, 10);
868                 if (errno != ERANGE && eptr != reqUrl.data() + pos)
869                 {
870                     params->intParams.push_back(value);
871                     std::pair<unsigned, RoutingParams> ret =
872                         find(reqUrl,
873                              &nodes[node->paramChildrens[static_cast<size_t>(
874                                  ParamType::INT)]],
875                              static_cast<size_t>(eptr - reqUrl.data()), params);
876                     updateFound(ret);
877                     params->intParams.pop_back();
878                 }
879             }
880         }
881 
882         if (node->paramChildrens[static_cast<size_t>(ParamType::UINT)] != 0U)
883         {
884             char c = reqUrl[pos];
885             if ((c >= '0' && c <= '9') || c == '+')
886             {
887                 char* eptr = nullptr;
888                 errno = 0;
889                 unsigned long long int value =
890                     std::strtoull(reqUrl.data() + pos, &eptr, 10);
891                 if (errno != ERANGE && eptr != reqUrl.data() + pos)
892                 {
893                     params->uintParams.push_back(value);
894                     std::pair<unsigned, RoutingParams> ret =
895                         find(reqUrl,
896                              &nodes[node->paramChildrens[static_cast<size_t>(
897                                  ParamType::UINT)]],
898                              static_cast<size_t>(eptr - reqUrl.data()), params);
899                     updateFound(ret);
900                     params->uintParams.pop_back();
901                 }
902             }
903         }
904 
905         if (node->paramChildrens[static_cast<size_t>(ParamType::DOUBLE)] != 0U)
906         {
907             char c = reqUrl[pos];
908             if ((c >= '0' && c <= '9') || c == '+' || c == '-' || c == '.')
909             {
910                 char* eptr = nullptr;
911                 errno = 0;
912                 double value = std::strtod(reqUrl.data() + pos, &eptr);
913                 if (errno != ERANGE && eptr != reqUrl.data() + pos)
914                 {
915                     params->doubleParams.push_back(value);
916                     std::pair<unsigned, RoutingParams> ret =
917                         find(reqUrl,
918                              &nodes[node->paramChildrens[static_cast<size_t>(
919                                  ParamType::DOUBLE)]],
920                              static_cast<size_t>(eptr - reqUrl.data()), params);
921                     updateFound(ret);
922                     params->doubleParams.pop_back();
923                 }
924             }
925         }
926 
927         if (node->paramChildrens[static_cast<size_t>(ParamType::STRING)] != 0U)
928         {
929             size_t epos = pos;
930             for (; epos < reqUrl.size(); epos++)
931             {
932                 if (reqUrl[epos] == '/')
933                 {
934                     break;
935                 }
936             }
937 
938             if (epos != pos)
939             {
940                 params->stringParams.emplace_back(
941                     reqUrl.substr(pos, epos - pos));
942                 std::pair<unsigned, RoutingParams> ret =
943                     find(reqUrl,
944                          &nodes[node->paramChildrens[static_cast<size_t>(
945                              ParamType::STRING)]],
946                          epos, params);
947                 updateFound(ret);
948                 params->stringParams.pop_back();
949             }
950         }
951 
952         if (node->paramChildrens[static_cast<size_t>(ParamType::PATH)] != 0U)
953         {
954             size_t epos = reqUrl.size();
955 
956             if (epos != pos)
957             {
958                 params->stringParams.emplace_back(
959                     reqUrl.substr(pos, epos - pos));
960                 std::pair<unsigned, RoutingParams> ret =
961                     find(reqUrl,
962                          &nodes[node->paramChildrens[static_cast<size_t>(
963                              ParamType::PATH)]],
964                          epos, params);
965                 updateFound(ret);
966                 params->stringParams.pop_back();
967             }
968         }
969 
970         for (const Node::ChildMap::value_type& kv : node->children)
971         {
972             const std::string& fragment = kv.first;
973             const Node* child = &nodes[kv.second];
974 
975             if (reqUrl.compare(pos, fragment.size(), fragment) == 0)
976             {
977                 std::pair<unsigned, RoutingParams> ret =
978                     find(reqUrl, child, pos + fragment.size(), params);
979                 updateFound(ret);
980             }
981         }
982 
983         return {found, matchParams};
984     }
985 
986     void add(const std::string& url, unsigned ruleIndex)
987     {
988         size_t idx = 0;
989 
990         for (unsigned i = 0; i < url.size(); i++)
991         {
992             char c = url[i];
993             if (c == '<')
994             {
995                 const static std::array<std::pair<ParamType, std::string>, 7>
996                     paramTraits = {{
997                         {ParamType::INT, "<int>"},
998                         {ParamType::UINT, "<uint>"},
999                         {ParamType::DOUBLE, "<float>"},
1000                         {ParamType::DOUBLE, "<double>"},
1001                         {ParamType::STRING, "<str>"},
1002                         {ParamType::STRING, "<string>"},
1003                         {ParamType::PATH, "<path>"},
1004                     }};
1005 
1006                 for (const std::pair<ParamType, std::string>& x : paramTraits)
1007                 {
1008                     if (url.compare(i, x.second.size(), x.second) == 0)
1009                     {
1010                         size_t index = static_cast<size_t>(x.first);
1011                         if (nodes[idx].paramChildrens[index] == 0U)
1012                         {
1013                             unsigned newNodeIdx = newNode();
1014                             nodes[idx].paramChildrens[index] = newNodeIdx;
1015                         }
1016                         idx = nodes[idx].paramChildrens[index];
1017                         i += static_cast<unsigned>(x.second.size());
1018                         break;
1019                     }
1020                 }
1021 
1022                 i--;
1023             }
1024             else
1025             {
1026                 std::string piece(&c, 1);
1027                 if (nodes[idx].children.count(piece) == 0U)
1028                 {
1029                     unsigned newNodeIdx = newNode();
1030                     nodes[idx].children.emplace(piece, newNodeIdx);
1031                 }
1032                 idx = nodes[idx].children[piece];
1033             }
1034         }
1035         if (nodes[idx].ruleIndex != 0U)
1036         {
1037             throw std::runtime_error("handler already exists for " + url);
1038         }
1039         nodes[idx].ruleIndex = ruleIndex;
1040     }
1041 
1042   private:
1043     void debugNodePrint(Node* n, size_t level)
1044     {
1045         for (size_t i = 0; i < static_cast<size_t>(ParamType::MAX); i++)
1046         {
1047             if (n->paramChildrens[i] != 0U)
1048             {
1049                 BMCWEB_LOG_DEBUG << std::string(
1050                     2U * level, ' ') /*<< "("<<n->paramChildrens[i]<<") "*/;
1051                 switch (static_cast<ParamType>(i))
1052                 {
1053                     case ParamType::INT:
1054                         BMCWEB_LOG_DEBUG << "<int>";
1055                         break;
1056                     case ParamType::UINT:
1057                         BMCWEB_LOG_DEBUG << "<uint>";
1058                         break;
1059                     case ParamType::DOUBLE:
1060                         BMCWEB_LOG_DEBUG << "<float>";
1061                         break;
1062                     case ParamType::STRING:
1063                         BMCWEB_LOG_DEBUG << "<str>";
1064                         break;
1065                     case ParamType::PATH:
1066                         BMCWEB_LOG_DEBUG << "<path>";
1067                         break;
1068                     case ParamType::MAX:
1069                         BMCWEB_LOG_DEBUG << "<ERROR>";
1070                         break;
1071                 }
1072 
1073                 debugNodePrint(&nodes[n->paramChildrens[i]], level + 1);
1074             }
1075         }
1076         for (const Node::ChildMap::value_type& kv : n->children)
1077         {
1078             BMCWEB_LOG_DEBUG
1079                 << std::string(2U * level, ' ') /*<< "(" << kv.second << ") "*/
1080                 << kv.first;
1081             debugNodePrint(&nodes[kv.second], level + 1);
1082         }
1083     }
1084 
1085   public:
1086     void debugPrint()
1087     {
1088         debugNodePrint(head(), 0U);
1089     }
1090 
1091   private:
1092     const Node* head() const
1093     {
1094         return &nodes.front();
1095     }
1096 
1097     Node* head()
1098     {
1099         return &nodes.front();
1100     }
1101 
1102     unsigned newNode()
1103     {
1104         nodes.resize(nodes.size() + 1);
1105         return static_cast<unsigned>(nodes.size() - 1);
1106     }
1107 
1108     std::vector<Node> nodes;
1109 };
1110 
1111 class Router
1112 {
1113   public:
1114     Router() = default;
1115 
1116     DynamicRule& newRuleDynamic(const std::string& rule)
1117     {
1118         std::unique_ptr<DynamicRule> ruleObject =
1119             std::make_unique<DynamicRule>(rule);
1120         DynamicRule* ptr = ruleObject.get();
1121         allRules.emplace_back(std::move(ruleObject));
1122 
1123         return *ptr;
1124     }
1125 
1126     template <uint64_t N>
1127     typename black_magic::Arguments<N>::type::template rebind<TaggedRule>&
1128         newRuleTagged(const std::string& rule)
1129     {
1130         using RuleT = typename black_magic::Arguments<N>::type::template rebind<
1131             TaggedRule>;
1132         std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule);
1133         RuleT* ptr = ruleObject.get();
1134         allRules.emplace_back(std::move(ruleObject));
1135 
1136         return *ptr;
1137     }
1138 
1139     void internalAddRuleObject(const std::string& rule, BaseRule* ruleObject)
1140     {
1141         if (ruleObject == nullptr)
1142         {
1143             return;
1144         }
1145         for (size_t method = 0, methodBit = 1; method <= methodNotAllowedIndex;
1146              method++, methodBit <<= 1)
1147         {
1148             if ((ruleObject->methodsBitfield & methodBit) > 0U)
1149             {
1150                 perMethods[method].rules.emplace_back(ruleObject);
1151                 perMethods[method].trie.add(
1152                     rule, static_cast<unsigned>(
1153                               perMethods[method].rules.size() - 1U));
1154                 // directory case:
1155                 //   request to `/about' url matches `/about/' rule
1156                 if (rule.size() > 2 && rule.back() == '/')
1157                 {
1158                     perMethods[method].trie.add(
1159                         rule.substr(0, rule.size() - 1),
1160                         static_cast<unsigned>(perMethods[method].rules.size() -
1161                                               1));
1162                 }
1163             }
1164         }
1165     }
1166 
1167     void validate()
1168     {
1169         for (std::unique_ptr<BaseRule>& rule : allRules)
1170         {
1171             if (rule)
1172             {
1173                 std::unique_ptr<BaseRule> upgraded = rule->upgrade();
1174                 if (upgraded)
1175                 {
1176                     rule = std::move(upgraded);
1177                 }
1178                 rule->validate();
1179                 internalAddRuleObject(rule->rule, rule.get());
1180             }
1181         }
1182         for (PerMethod& perMethod : perMethods)
1183         {
1184             perMethod.trie.validate();
1185         }
1186     }
1187 
1188     struct FindRoute
1189     {
1190         BaseRule* rule = nullptr;
1191         RoutingParams params;
1192     };
1193 
1194     struct FindRouteResponse
1195     {
1196         std::string allowHeader;
1197         FindRoute route;
1198     };
1199 
1200     FindRoute findRouteByIndex(std::string_view url, size_t index) const
1201     {
1202         FindRoute route;
1203         if (index >= perMethods.size())
1204         {
1205             BMCWEB_LOG_CRITICAL << "Bad index???";
1206             return route;
1207         }
1208         const PerMethod& perMethod = perMethods[index];
1209         std::pair<unsigned, RoutingParams> found = perMethod.trie.find(url);
1210         if (found.first >= perMethod.rules.size())
1211         {
1212             throw std::runtime_error("Trie internal structure corrupted!");
1213         }
1214         // Found a 404 route, switch that in
1215         if (found.first != 0U)
1216         {
1217             route.rule = perMethod.rules[found.first];
1218             route.params = std::move(found.second);
1219         }
1220         return route;
1221     }
1222 
1223     FindRouteResponse findRoute(Request& req) const
1224     {
1225         FindRouteResponse findRoute;
1226 
1227         std::optional<HttpVerb> verb = httpVerbFromBoost(req.method());
1228         if (!verb)
1229         {
1230             return findRoute;
1231         }
1232         size_t reqMethodIndex = static_cast<size_t>(*verb);
1233         // Check to see if this url exists at any verb
1234         for (size_t perMethodIndex = 0; perMethodIndex <= maxVerbIndex;
1235              perMethodIndex++)
1236         {
1237             // Make sure it's safe to deference the array at that index
1238             static_assert(maxVerbIndex <
1239                           std::tuple_size_v<decltype(perMethods)>);
1240             FindRoute route =
1241                 findRouteByIndex(req.url().encoded_path(), perMethodIndex);
1242             if (route.rule == nullptr)
1243             {
1244                 continue;
1245             }
1246             if (!findRoute.allowHeader.empty())
1247             {
1248                 findRoute.allowHeader += ", ";
1249             }
1250             HttpVerb thisVerb = static_cast<HttpVerb>(perMethodIndex);
1251             findRoute.allowHeader += httpVerbToString(thisVerb);
1252             if (perMethodIndex == reqMethodIndex)
1253             {
1254                 findRoute.route = route;
1255             }
1256         }
1257         return findRoute;
1258     }
1259 
1260     static bool isUserPrivileged(
1261         Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1262         BaseRule& rule, const dbus::utility::DBusPropertiesMap& userInfoMap)
1263     {
1264         std::string userRole{};
1265         const std::string* userRolePtr = nullptr;
1266         const bool* remoteUser = nullptr;
1267         const bool* passwordExpired = nullptr;
1268 
1269         const bool success = sdbusplus::unpackPropertiesNoThrow(
1270             redfish::dbus_utils::UnpackErrorPrinter(), userInfoMap,
1271             "UserPrivilege", userRolePtr, "RemoteUser", remoteUser,
1272             "UserPasswordExpired", passwordExpired);
1273 
1274         if (!success)
1275         {
1276             asyncResp->res.result(
1277                 boost::beast::http::status::internal_server_error);
1278             return false;
1279         }
1280 
1281         if (userRolePtr != nullptr)
1282         {
1283             userRole = *userRolePtr;
1284             BMCWEB_LOG_DEBUG << "userName = " << req.session->username
1285                              << " userRole = " << *userRolePtr;
1286         }
1287 
1288         if (remoteUser == nullptr)
1289         {
1290             BMCWEB_LOG_ERROR << "RemoteUser property missing or wrong type";
1291             asyncResp->res.result(
1292                 boost::beast::http::status::internal_server_error);
1293             return false;
1294         }
1295         bool expired = false;
1296         if (passwordExpired == nullptr)
1297         {
1298             if (!*remoteUser)
1299             {
1300                 BMCWEB_LOG_ERROR
1301                     << "UserPasswordExpired property is expected for"
1302                        " local user but is missing or wrong type";
1303                 asyncResp->res.result(
1304                     boost::beast::http::status::internal_server_error);
1305                 return false;
1306             }
1307         }
1308         else
1309         {
1310             expired = *passwordExpired;
1311         }
1312 
1313         // Get the user's privileges from the role
1314         redfish::Privileges userPrivileges =
1315             redfish::getUserPrivileges(userRole);
1316 
1317         // Set isConfigureSelfOnly based on D-Bus results.  This
1318         // ignores the results from both pamAuthenticateUser and the
1319         // value from any previous use of this session.
1320         req.session->isConfigureSelfOnly = expired;
1321 
1322         // Modify privileges if isConfigureSelfOnly.
1323         if (req.session->isConfigureSelfOnly)
1324         {
1325             // Remove all privileges except ConfigureSelf
1326             userPrivileges = userPrivileges.intersection(
1327                 redfish::Privileges{"ConfigureSelf"});
1328             BMCWEB_LOG_DEBUG << "Operation limited to ConfigureSelf";
1329         }
1330 
1331         if (!rule.checkPrivileges(userPrivileges))
1332         {
1333             asyncResp->res.result(boost::beast::http::status::forbidden);
1334             if (req.session->isConfigureSelfOnly)
1335             {
1336                 redfish::messages::passwordChangeRequired(
1337                     asyncResp->res, crow::utility::urlFromPieces(
1338                                         "redfish", "v1", "AccountService",
1339                                         "Accounts", req.session->username));
1340             }
1341             return false;
1342         }
1343 
1344         req.userRole = userRole;
1345 
1346         return true;
1347     }
1348 
1349     template <typename CallbackFn>
1350     void afterGetUserInfo(Request& req,
1351                           const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1352                           BaseRule& rule, CallbackFn&& callback,
1353                           const boost::system::error_code& ec,
1354                           const dbus::utility::DBusPropertiesMap& userInfoMap)
1355     {
1356         if (ec)
1357         {
1358             BMCWEB_LOG_ERROR << "GetUserInfo failed...";
1359             asyncResp->res.result(
1360                 boost::beast::http::status::internal_server_error);
1361             return;
1362         }
1363 
1364         if (!Router::isUserPrivileged(req, asyncResp, rule, userInfoMap))
1365         {
1366             // User is not privileged
1367             BMCWEB_LOG_ERROR << "Insufficient Privilege";
1368             asyncResp->res.result(boost::beast::http::status::forbidden);
1369             return;
1370         }
1371         callback(req);
1372     }
1373 
1374     template <typename CallbackFn>
1375     void validatePrivilege(Request& req,
1376                            const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1377                            BaseRule& rule, CallbackFn&& callback)
1378     {
1379         if (req.session == nullptr)
1380         {
1381             return;
1382         }
1383         std::string username = req.session->username;
1384         crow::connections::systemBus->async_method_call(
1385             [this, &req, asyncResp, &rule,
1386              callback(std::forward<CallbackFn>(callback))](
1387                 const boost::system::error_code& ec,
1388                 const dbus::utility::DBusPropertiesMap& userInfoMap) mutable {
1389             afterGetUserInfo(req, asyncResp, rule,
1390                              std::forward<CallbackFn>(callback), ec,
1391                              userInfoMap);
1392             },
1393             "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1394             "xyz.openbmc_project.User.Manager", "GetUserInfo", username);
1395     }
1396 
1397     template <typename Adaptor>
1398     void handleUpgrade(Request& req,
1399                        const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1400                        Adaptor&& adaptor)
1401     {
1402         std::optional<HttpVerb> verb = httpVerbFromBoost(req.method());
1403         if (!verb || static_cast<size_t>(*verb) >= perMethods.size())
1404         {
1405             asyncResp->res.result(boost::beast::http::status::not_found);
1406             return;
1407         }
1408         PerMethod& perMethod = perMethods[static_cast<size_t>(*verb)];
1409         Trie& trie = perMethod.trie;
1410         std::vector<BaseRule*>& rules = perMethod.rules;
1411 
1412         const std::pair<unsigned, RoutingParams>& found =
1413             trie.find(req.url().encoded_path());
1414         unsigned ruleIndex = found.first;
1415         if (ruleIndex == 0U)
1416         {
1417             BMCWEB_LOG_DEBUG << "Cannot match rules "
1418                              << req.url().encoded_path();
1419             asyncResp->res.result(boost::beast::http::status::not_found);
1420             return;
1421         }
1422 
1423         if (ruleIndex >= rules.size())
1424         {
1425             throw std::runtime_error("Trie internal structure corrupted!");
1426         }
1427 
1428         BaseRule& rule = *rules[ruleIndex];
1429         size_t methods = rule.getMethods();
1430         if ((methods & (1U << static_cast<size_t>(*verb))) == 0)
1431         {
1432             BMCWEB_LOG_DEBUG
1433                 << "Rule found but method mismatch: "
1434                 << req.url().encoded_path() << " with " << req.methodString()
1435                 << "(" << static_cast<uint32_t>(*verb) << ") / " << methods;
1436             asyncResp->res.result(boost::beast::http::status::not_found);
1437             return;
1438         }
1439 
1440         BMCWEB_LOG_DEBUG << "Matched rule (upgrade) '" << rule.rule << "' "
1441                          << static_cast<uint32_t>(*verb) << " / " << methods;
1442 
1443         // TODO(ed) This should be able to use std::bind_front, but it doesn't
1444         // appear to work with the std::move on adaptor.
1445         validatePrivilege(
1446             req, asyncResp, rule,
1447             [&rule, asyncResp, adaptor(std::forward<Adaptor>(adaptor))](
1448                 Request& thisReq) mutable {
1449             rule.handleUpgrade(thisReq, asyncResp, std::move(adaptor));
1450             });
1451     }
1452 
1453     void handle(Request& req,
1454                 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1455     {
1456         std::optional<HttpVerb> verb = httpVerbFromBoost(req.method());
1457         if (!verb || static_cast<size_t>(*verb) >= perMethods.size())
1458         {
1459             asyncResp->res.result(boost::beast::http::status::not_found);
1460             return;
1461         }
1462 
1463         FindRouteResponse foundRoute = findRoute(req);
1464 
1465         if (foundRoute.route.rule == nullptr)
1466         {
1467             // Couldn't find a normal route with any verb, try looking for a 404
1468             // route
1469             if (foundRoute.allowHeader.empty())
1470             {
1471                 foundRoute.route =
1472                     findRouteByIndex(req.url().encoded_path(), notFoundIndex);
1473             }
1474             else
1475             {
1476                 // See if we have a method not allowed (405) handler
1477                 foundRoute.route = findRouteByIndex(req.url().encoded_path(),
1478                                                     methodNotAllowedIndex);
1479             }
1480         }
1481 
1482         // Fill in the allow header if it's valid
1483         if (!foundRoute.allowHeader.empty())
1484         {
1485 
1486             asyncResp->res.addHeader(boost::beast::http::field::allow,
1487                                      foundRoute.allowHeader);
1488         }
1489 
1490         // If we couldn't find a real route or a 404 route, return a generic
1491         // response
1492         if (foundRoute.route.rule == nullptr)
1493         {
1494             if (foundRoute.allowHeader.empty())
1495             {
1496                 asyncResp->res.result(boost::beast::http::status::not_found);
1497             }
1498             else
1499             {
1500                 asyncResp->res.result(
1501                     boost::beast::http::status::method_not_allowed);
1502             }
1503             return;
1504         }
1505 
1506         BaseRule& rule = *foundRoute.route.rule;
1507         RoutingParams params = std::move(foundRoute.route.params);
1508 
1509         BMCWEB_LOG_DEBUG << "Matched rule '" << rule.rule << "' "
1510                          << static_cast<uint32_t>(*verb) << " / "
1511                          << rule.getMethods();
1512 
1513         if (req.session == nullptr)
1514         {
1515             rule.handle(req, asyncResp, params);
1516             return;
1517         }
1518         validatePrivilege(req, asyncResp, rule,
1519                           [&rule, asyncResp, params](Request& thisReq) mutable {
1520             rule.handle(thisReq, asyncResp, params);
1521         });
1522     }
1523 
1524     void debugPrint()
1525     {
1526         for (size_t i = 0; i < perMethods.size(); i++)
1527         {
1528             BMCWEB_LOG_DEBUG << boost::beast::http::to_string(
1529                 static_cast<boost::beast::http::verb>(i));
1530             perMethods[i].trie.debugPrint();
1531         }
1532     }
1533 
1534     std::vector<const std::string*> getRoutes(const std::string& parent)
1535     {
1536         std::vector<const std::string*> ret;
1537 
1538         for (const PerMethod& pm : perMethods)
1539         {
1540             std::vector<unsigned> x;
1541             pm.trie.findRouteIndexes(parent, x);
1542             for (unsigned index : x)
1543             {
1544                 ret.push_back(&pm.rules[index]->rule);
1545             }
1546         }
1547         return ret;
1548     }
1549 
1550   private:
1551     struct PerMethod
1552     {
1553         std::vector<BaseRule*> rules;
1554         Trie trie;
1555         // rule index 0 has special meaning; preallocate it to avoid
1556         // duplication.
1557         PerMethod() : rules(1)
1558         {}
1559     };
1560 
1561     std::array<PerMethod, methodNotAllowedIndex + 1> perMethods;
1562     std::vector<std::unique_ptr<BaseRule>> allRules;
1563 };
1564 } // namespace crow
1565