xref: /openbmc/bmcweb/http/routing.hpp (revision 8b2521a5)
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                 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                 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& onclose(Func f)
399     {
400         closeHandler = f;
401         return *this;
402     }
403 
404     template <typename Func>
405     self_t& onerror(Func f)
406     {
407         errorHandler = f;
408         return *this;
409     }
410 
411   protected:
412     std::function<void(crow::websocket::Connection&)> openHandler;
413     std::function<void(crow::websocket::Connection&, const std::string&, bool)>
414         messageHandler;
415     std::function<void(crow::websocket::Connection&, const std::string&)>
416         closeHandler;
417     std::function<void(crow::websocket::Connection&)> errorHandler;
418 };
419 
420 template <typename T>
421 struct RuleParameterTraits
422 {
423     using self_t = T;
424     WebSocketRule& websocket()
425     {
426         self_t* self = static_cast<self_t*>(this);
427         WebSocketRule* p = new WebSocketRule(self->rule);
428         self->ruleToUpgrade.reset(p);
429         return *p;
430     }
431 
432     self_t& name(std::string_view name) noexcept
433     {
434         self_t* self = static_cast<self_t*>(this);
435         self->nameStr = name;
436         return *self;
437     }
438 
439     self_t& methods(boost::beast::http::verb method)
440     {
441         self_t* self = static_cast<self_t*>(this);
442         std::optional<HttpVerb> verb = httpVerbFromBoost(method);
443         if (verb)
444         {
445             self->methodsBitfield = 1U << static_cast<size_t>(*verb);
446         }
447         return *self;
448     }
449 
450     template <typename... MethodArgs>
451     self_t& methods(boost::beast::http::verb method, MethodArgs... argsMethod)
452     {
453         self_t* self = static_cast<self_t*>(this);
454         methods(argsMethod...);
455         std::optional<HttpVerb> verb = httpVerbFromBoost(method);
456         if (verb)
457         {
458             self->methodsBitfield |= 1U << static_cast<size_t>(*verb);
459         }
460         return *self;
461     }
462 
463     self_t& notFound()
464     {
465         self_t* self = static_cast<self_t*>(this);
466         self->methodsBitfield = 1U << notFoundIndex;
467         return *self;
468     }
469 
470     self_t& methodNotAllowed()
471     {
472         self_t* self = static_cast<self_t*>(this);
473         self->methodsBitfield = 1U << methodNotAllowedIndex;
474         return *self;
475     }
476 
477     self_t& privileges(
478         const std::initializer_list<std::initializer_list<const char*>>& p)
479     {
480         self_t* self = static_cast<self_t*>(this);
481         for (const std::initializer_list<const char*>& privilege : p)
482         {
483             self->privilegesSet.emplace_back(privilege);
484         }
485         return *self;
486     }
487 
488     template <size_t N, typename... MethodArgs>
489     self_t& privileges(const std::array<redfish::Privileges, N>& p)
490     {
491         self_t* self = static_cast<self_t*>(this);
492         for (const redfish::Privileges& privilege : p)
493         {
494             self->privilegesSet.emplace_back(privilege);
495         }
496         return *self;
497     }
498 };
499 
500 class DynamicRule : public BaseRule, public RuleParameterTraits<DynamicRule>
501 {
502   public:
503     explicit DynamicRule(const std::string& ruleIn) : BaseRule(ruleIn)
504     {}
505 
506     void validate() override
507     {
508         if (!erasedHandler)
509         {
510             throw std::runtime_error(nameStr + (!nameStr.empty() ? ": " : "") +
511                                      "no handler for url " + rule);
512         }
513     }
514 
515     void handle(const Request& req,
516                 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
517                 const RoutingParams& params) override
518     {
519         erasedHandler(req, asyncResp, params);
520     }
521 
522     template <typename Func>
523     void operator()(Func f)
524     {
525         using boost::callable_traits::args_t;
526         constexpr size_t arity = std::tuple_size<args_t<Func>>::value;
527         constexpr auto is = std::make_integer_sequence<unsigned, arity>{};
528         erasedHandler = wrap(std::move(f), is);
529     }
530 
531     // enable_if Arg1 == request && Arg2 == Response
532     // enable_if Arg1 == request && Arg2 != response
533     // enable_if Arg1 != request
534 
535     template <typename Func, unsigned... Indices>
536     std::function<void(const Request&,
537                        const std::shared_ptr<bmcweb::AsyncResp>&,
538                        const RoutingParams&)>
539         wrap(Func f, std::integer_sequence<unsigned, Indices...> /*is*/)
540     {
541         using function_t = crow::utility::FunctionTraits<Func>;
542 
543         if (!black_magic::isParameterTagCompatible(
544                 black_magic::getParameterTag(rule.c_str()),
545                 black_magic::computeParameterTagFromArgsList<
546                     typename function_t::template arg<Indices>...>::value))
547         {
548             throw std::runtime_error("routeDynamic: Handler type is mismatched "
549                                      "with URL parameters: " +
550                                      rule);
551         }
552         auto ret = detail::routing_handler_call_helper::Wrapped<
553             Func, typename function_t::template arg<Indices>...>();
554         ret.template set<typename function_t::template arg<Indices>...>(
555             std::move(f));
556         return ret;
557     }
558 
559     template <typename Func>
560     void operator()(std::string name, Func&& f)
561     {
562         nameStr = std::move(name);
563         (*this).template operator()<Func>(std::forward(f));
564     }
565 
566   private:
567     std::function<void(const Request&,
568                        const std::shared_ptr<bmcweb::AsyncResp>&,
569                        const RoutingParams&)>
570         erasedHandler;
571 };
572 
573 template <typename... Args>
574 class TaggedRule :
575     public BaseRule,
576     public RuleParameterTraits<TaggedRule<Args...>>
577 {
578   public:
579     using self_t = TaggedRule<Args...>;
580 
581     explicit TaggedRule(const std::string& ruleIn) : BaseRule(ruleIn)
582     {}
583 
584     void validate() override
585     {
586         if (!handler)
587         {
588             throw std::runtime_error(nameStr + (!nameStr.empty() ? ": " : "") +
589                                      "no handler for url " + rule);
590         }
591     }
592 
593     template <typename Func>
594     typename std::enable_if<
595         black_magic::CallHelper<Func, black_magic::S<Args...>>::value,
596         void>::type
597         operator()(Func&& f)
598     {
599         static_assert(
600             black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
601                 black_magic::CallHelper<
602                     Func, black_magic::S<crow::Request, Args...>>::value,
603             "Handler type is mismatched with URL parameters");
604         static_assert(
605             !std::is_same<void, decltype(f(std::declval<Args>()...))>::value,
606             "Handler function cannot have void return type; valid return "
607             "types: "
608             "string, int, crow::response, nlohmann::json");
609 
610         handler = [f = std::forward<Func>(f)](
611                       const Request&,
612                       const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
613                       Args... args) { asyncResp->res.result(f(args...)); };
614     }
615 
616     template <typename Func>
617     typename std::enable_if<
618         !black_magic::CallHelper<Func, black_magic::S<Args...>>::value &&
619             black_magic::CallHelper<
620                 Func, black_magic::S<crow::Request, Args...>>::value,
621         void>::type
622         operator()(Func&& f)
623     {
624         static_assert(
625             black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
626                 black_magic::CallHelper<
627                     Func, black_magic::S<crow::Request, Args...>>::value,
628             "Handler type is mismatched with URL parameters");
629         static_assert(
630             !std::is_same<void, decltype(f(std::declval<crow::Request>(),
631                                            std::declval<Args>()...))>::value,
632             "Handler function cannot have void return type; valid return "
633             "types: "
634             "string, int, crow::response,nlohmann::json");
635 
636         handler = [f = std::forward<Func>(f)](
637                       const crow::Request& req,
638                       const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
639                       Args... args) { asyncResp->res.result(f(req, args...)); };
640     }
641 
642     template <typename Func>
643     typename std::enable_if<
644         !black_magic::CallHelper<Func, black_magic::S<Args...>>::value &&
645             !black_magic::CallHelper<
646                 Func, black_magic::S<crow::Request, Args...>>::value,
647         void>::type
648         operator()(Func&& f)
649     {
650         static_assert(
651             black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
652                 black_magic::CallHelper<
653                     Func, black_magic::S<crow::Request, Args...>>::value ||
654                 black_magic::CallHelper<
655                     Func, black_magic::S<crow::Request,
656                                          std::shared_ptr<bmcweb::AsyncResp>&,
657                                          Args...>>::value,
658             "Handler type is mismatched with URL parameters");
659         static_assert(
660             std::is_same<
661                 void,
662                 decltype(f(std::declval<crow::Request>(),
663                            std::declval<std::shared_ptr<bmcweb::AsyncResp>&>(),
664                            std::declval<Args>()...))>::value,
665             "Handler function with response argument should have void "
666             "return "
667             "type");
668 
669         handler = std::forward<Func>(f);
670     }
671 
672     template <typename Func>
673     void operator()(std::string_view name, Func&& f)
674     {
675         nameStr = name;
676         (*this).template operator()<Func>(std::forward(f));
677     }
678 
679     void handle(const Request& req,
680                 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
681                 const RoutingParams& params) override
682     {
683         detail::routing_handler_call_helper::Call<
684             detail::routing_handler_call_helper::CallParams<decltype(handler)>,
685             0, 0, 0, 0, black_magic::S<Args...>, black_magic::S<>>()(
686             detail::routing_handler_call_helper::CallParams<decltype(handler)>{
687                 handler, params, req, asyncResp});
688     }
689 
690   private:
691     std::function<void(const crow::Request&,
692                        const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>
693         handler;
694 };
695 
696 class Trie
697 {
698   public:
699     struct Node
700     {
701         unsigned ruleIndex{};
702         std::array<size_t, static_cast<size_t>(ParamType::MAX)>
703             paramChildrens{};
704         using ChildMap = boost::container::flat_map<
705             std::string, unsigned, std::less<>,
706             std::vector<std::pair<std::string, unsigned>>>;
707         ChildMap children;
708 
709         bool isSimpleNode() const
710         {
711             return ruleIndex == 0 &&
712                    std::all_of(std::begin(paramChildrens),
713                                std::end(paramChildrens),
714                                [](size_t x) { return x == 0U; });
715         }
716     };
717 
718     Trie() : nodes(1)
719     {}
720 
721   private:
722     void optimizeNode(Node* node)
723     {
724         for (size_t x : node->paramChildrens)
725         {
726             if (x == 0U)
727             {
728                 continue;
729             }
730             Node* child = &nodes[x];
731             optimizeNode(child);
732         }
733         if (node->children.empty())
734         {
735             return;
736         }
737         bool mergeWithChild = true;
738         for (const Node::ChildMap::value_type& kv : node->children)
739         {
740             Node* child = &nodes[kv.second];
741             if (!child->isSimpleNode())
742             {
743                 mergeWithChild = false;
744                 break;
745             }
746         }
747         if (mergeWithChild)
748         {
749             Node::ChildMap merged;
750             for (const Node::ChildMap::value_type& kv : node->children)
751             {
752                 Node* child = &nodes[kv.second];
753                 for (const Node::ChildMap::value_type& childKv :
754                      child->children)
755                 {
756                     merged[kv.first + childKv.first] = childKv.second;
757                 }
758             }
759             node->children = std::move(merged);
760             optimizeNode(node);
761         }
762         else
763         {
764             for (const Node::ChildMap::value_type& kv : node->children)
765             {
766                 Node* child = &nodes[kv.second];
767                 optimizeNode(child);
768             }
769         }
770     }
771 
772     void optimize()
773     {
774         optimizeNode(head());
775     }
776 
777   public:
778     void validate()
779     {
780         optimize();
781     }
782 
783     void findRouteIndexes(const std::string& reqUrl,
784                           std::vector<unsigned>& routeIndexes,
785                           const Node* node = nullptr, unsigned pos = 0) const
786     {
787         if (node == nullptr)
788         {
789             node = head();
790         }
791         for (const Node::ChildMap::value_type& kv : node->children)
792         {
793             const std::string& fragment = kv.first;
794             const Node* child = &nodes[kv.second];
795             if (pos >= reqUrl.size())
796             {
797                 if (child->ruleIndex != 0 && fragment != "/")
798                 {
799                     routeIndexes.push_back(child->ruleIndex);
800                 }
801                 findRouteIndexes(reqUrl, routeIndexes, child,
802                                  static_cast<unsigned>(pos + fragment.size()));
803             }
804             else
805             {
806                 if (reqUrl.compare(pos, fragment.size(), fragment) == 0)
807                 {
808                     findRouteIndexes(
809                         reqUrl, routeIndexes, child,
810                         static_cast<unsigned>(pos + fragment.size()));
811                 }
812             }
813         }
814     }
815 
816     std::pair<unsigned, RoutingParams>
817         find(std::string_view reqUrl, const Node* node = nullptr,
818              size_t pos = 0, RoutingParams* params = nullptr) const
819     {
820         RoutingParams empty;
821         if (params == nullptr)
822         {
823             params = &empty;
824         }
825 
826         unsigned found{};
827         RoutingParams matchParams;
828 
829         if (node == nullptr)
830         {
831             node = head();
832         }
833         if (pos == reqUrl.size())
834         {
835             return {node->ruleIndex, *params};
836         }
837 
838         auto updateFound =
839             [&found, &matchParams](std::pair<unsigned, RoutingParams>& ret) {
840             if (ret.first != 0U && (found == 0U || found > ret.first))
841             {
842                 found = ret.first;
843                 matchParams = std::move(ret.second);
844             }
845         };
846 
847         if (node->paramChildrens[static_cast<size_t>(ParamType::INT)] != 0U)
848         {
849             char c = reqUrl[pos];
850             if ((c >= '0' && c <= '9') || c == '+' || c == '-')
851             {
852                 char* eptr = nullptr;
853                 errno = 0;
854                 long long int value =
855                     std::strtoll(reqUrl.data() + pos, &eptr, 10);
856                 if (errno != ERANGE && eptr != reqUrl.data() + pos)
857                 {
858                     params->intParams.push_back(value);
859                     std::pair<unsigned, RoutingParams> ret =
860                         find(reqUrl,
861                              &nodes[node->paramChildrens[static_cast<size_t>(
862                                  ParamType::INT)]],
863                              static_cast<size_t>(eptr - reqUrl.data()), params);
864                     updateFound(ret);
865                     params->intParams.pop_back();
866                 }
867             }
868         }
869 
870         if (node->paramChildrens[static_cast<size_t>(ParamType::UINT)] != 0U)
871         {
872             char c = reqUrl[pos];
873             if ((c >= '0' && c <= '9') || c == '+')
874             {
875                 char* eptr = nullptr;
876                 errno = 0;
877                 unsigned long long int value =
878                     std::strtoull(reqUrl.data() + pos, &eptr, 10);
879                 if (errno != ERANGE && eptr != reqUrl.data() + pos)
880                 {
881                     params->uintParams.push_back(value);
882                     std::pair<unsigned, RoutingParams> ret =
883                         find(reqUrl,
884                              &nodes[node->paramChildrens[static_cast<size_t>(
885                                  ParamType::UINT)]],
886                              static_cast<size_t>(eptr - reqUrl.data()), params);
887                     updateFound(ret);
888                     params->uintParams.pop_back();
889                 }
890             }
891         }
892 
893         if (node->paramChildrens[static_cast<size_t>(ParamType::DOUBLE)] != 0U)
894         {
895             char c = reqUrl[pos];
896             if ((c >= '0' && c <= '9') || c == '+' || c == '-' || c == '.')
897             {
898                 char* eptr = nullptr;
899                 errno = 0;
900                 double value = std::strtod(reqUrl.data() + pos, &eptr);
901                 if (errno != ERANGE && eptr != reqUrl.data() + pos)
902                 {
903                     params->doubleParams.push_back(value);
904                     std::pair<unsigned, RoutingParams> ret =
905                         find(reqUrl,
906                              &nodes[node->paramChildrens[static_cast<size_t>(
907                                  ParamType::DOUBLE)]],
908                              static_cast<size_t>(eptr - reqUrl.data()), params);
909                     updateFound(ret);
910                     params->doubleParams.pop_back();
911                 }
912             }
913         }
914 
915         if (node->paramChildrens[static_cast<size_t>(ParamType::STRING)] != 0U)
916         {
917             size_t epos = pos;
918             for (; epos < reqUrl.size(); epos++)
919             {
920                 if (reqUrl[epos] == '/')
921                 {
922                     break;
923                 }
924             }
925 
926             if (epos != pos)
927             {
928                 params->stringParams.emplace_back(
929                     reqUrl.substr(pos, epos - pos));
930                 std::pair<unsigned, RoutingParams> ret =
931                     find(reqUrl,
932                          &nodes[node->paramChildrens[static_cast<size_t>(
933                              ParamType::STRING)]],
934                          epos, params);
935                 updateFound(ret);
936                 params->stringParams.pop_back();
937             }
938         }
939 
940         if (node->paramChildrens[static_cast<size_t>(ParamType::PATH)] != 0U)
941         {
942             size_t epos = reqUrl.size();
943 
944             if (epos != pos)
945             {
946                 params->stringParams.emplace_back(
947                     reqUrl.substr(pos, epos - pos));
948                 std::pair<unsigned, RoutingParams> ret =
949                     find(reqUrl,
950                          &nodes[node->paramChildrens[static_cast<size_t>(
951                              ParamType::PATH)]],
952                          epos, params);
953                 updateFound(ret);
954                 params->stringParams.pop_back();
955             }
956         }
957 
958         for (const Node::ChildMap::value_type& kv : node->children)
959         {
960             const std::string& fragment = kv.first;
961             const Node* child = &nodes[kv.second];
962 
963             if (reqUrl.compare(pos, fragment.size(), fragment) == 0)
964             {
965                 std::pair<unsigned, RoutingParams> ret =
966                     find(reqUrl, child, pos + fragment.size(), params);
967                 updateFound(ret);
968             }
969         }
970 
971         return {found, matchParams};
972     }
973 
974     void add(const std::string& url, unsigned ruleIndex)
975     {
976         size_t idx = 0;
977 
978         for (unsigned i = 0; i < url.size(); i++)
979         {
980             char c = url[i];
981             if (c == '<')
982             {
983                 const static std::array<std::pair<ParamType, std::string>, 7>
984                     paramTraits = {{
985                         {ParamType::INT, "<int>"},
986                         {ParamType::UINT, "<uint>"},
987                         {ParamType::DOUBLE, "<float>"},
988                         {ParamType::DOUBLE, "<double>"},
989                         {ParamType::STRING, "<str>"},
990                         {ParamType::STRING, "<string>"},
991                         {ParamType::PATH, "<path>"},
992                     }};
993 
994                 for (const std::pair<ParamType, std::string>& x : paramTraits)
995                 {
996                     if (url.compare(i, x.second.size(), x.second) == 0)
997                     {
998                         size_t index = static_cast<size_t>(x.first);
999                         if (nodes[idx].paramChildrens[index] == 0U)
1000                         {
1001                             unsigned newNodeIdx = newNode();
1002                             nodes[idx].paramChildrens[index] = newNodeIdx;
1003                         }
1004                         idx = nodes[idx].paramChildrens[index];
1005                         i += static_cast<unsigned>(x.second.size());
1006                         break;
1007                     }
1008                 }
1009 
1010                 i--;
1011             }
1012             else
1013             {
1014                 std::string piece(&c, 1);
1015                 if (nodes[idx].children.count(piece) == 0U)
1016                 {
1017                     unsigned newNodeIdx = newNode();
1018                     nodes[idx].children.emplace(piece, newNodeIdx);
1019                 }
1020                 idx = nodes[idx].children[piece];
1021             }
1022         }
1023         if (nodes[idx].ruleIndex != 0U)
1024         {
1025             throw std::runtime_error("handler already exists for " + url);
1026         }
1027         nodes[idx].ruleIndex = ruleIndex;
1028     }
1029 
1030   private:
1031     void debugNodePrint(Node* n, size_t level)
1032     {
1033         for (size_t i = 0; i < static_cast<size_t>(ParamType::MAX); i++)
1034         {
1035             if (n->paramChildrens[i] != 0U)
1036             {
1037                 BMCWEB_LOG_DEBUG << std::string(
1038                     2U * level, ' ') /*<< "("<<n->paramChildrens[i]<<") "*/;
1039                 switch (static_cast<ParamType>(i))
1040                 {
1041                     case ParamType::INT:
1042                         BMCWEB_LOG_DEBUG << "<int>";
1043                         break;
1044                     case ParamType::UINT:
1045                         BMCWEB_LOG_DEBUG << "<uint>";
1046                         break;
1047                     case ParamType::DOUBLE:
1048                         BMCWEB_LOG_DEBUG << "<float>";
1049                         break;
1050                     case ParamType::STRING:
1051                         BMCWEB_LOG_DEBUG << "<str>";
1052                         break;
1053                     case ParamType::PATH:
1054                         BMCWEB_LOG_DEBUG << "<path>";
1055                         break;
1056                     case ParamType::MAX:
1057                         BMCWEB_LOG_DEBUG << "<ERROR>";
1058                         break;
1059                 }
1060 
1061                 debugNodePrint(&nodes[n->paramChildrens[i]], level + 1);
1062             }
1063         }
1064         for (const Node::ChildMap::value_type& kv : n->children)
1065         {
1066             BMCWEB_LOG_DEBUG
1067                 << std::string(2U * level, ' ') /*<< "(" << kv.second << ") "*/
1068                 << kv.first;
1069             debugNodePrint(&nodes[kv.second], level + 1);
1070         }
1071     }
1072 
1073   public:
1074     void debugPrint()
1075     {
1076         debugNodePrint(head(), 0U);
1077     }
1078 
1079   private:
1080     const Node* head() const
1081     {
1082         return &nodes.front();
1083     }
1084 
1085     Node* head()
1086     {
1087         return &nodes.front();
1088     }
1089 
1090     unsigned newNode()
1091     {
1092         nodes.resize(nodes.size() + 1);
1093         return static_cast<unsigned>(nodes.size() - 1);
1094     }
1095 
1096     std::vector<Node> nodes;
1097 };
1098 
1099 class Router
1100 {
1101   public:
1102     Router() = default;
1103 
1104     DynamicRule& newRuleDynamic(const std::string& rule)
1105     {
1106         std::unique_ptr<DynamicRule> ruleObject =
1107             std::make_unique<DynamicRule>(rule);
1108         DynamicRule* ptr = ruleObject.get();
1109         allRules.emplace_back(std::move(ruleObject));
1110 
1111         return *ptr;
1112     }
1113 
1114     template <uint64_t N>
1115     typename black_magic::Arguments<N>::type::template rebind<TaggedRule>&
1116         newRuleTagged(const std::string& rule)
1117     {
1118         using RuleT = typename black_magic::Arguments<N>::type::template rebind<
1119             TaggedRule>;
1120         std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule);
1121         RuleT* ptr = ruleObject.get();
1122         allRules.emplace_back(std::move(ruleObject));
1123 
1124         return *ptr;
1125     }
1126 
1127     void internalAddRuleObject(const std::string& rule, BaseRule* ruleObject)
1128     {
1129         if (ruleObject == nullptr)
1130         {
1131             return;
1132         }
1133         for (size_t method = 0, methodBit = 1; method <= methodNotAllowedIndex;
1134              method++, methodBit <<= 1)
1135         {
1136             if ((ruleObject->methodsBitfield & methodBit) > 0U)
1137             {
1138                 perMethods[method].rules.emplace_back(ruleObject);
1139                 perMethods[method].trie.add(
1140                     rule, static_cast<unsigned>(
1141                               perMethods[method].rules.size() - 1U));
1142                 // directory case:
1143                 //   request to `/about' url matches `/about/' rule
1144                 if (rule.size() > 2 && rule.back() == '/')
1145                 {
1146                     perMethods[method].trie.add(
1147                         rule.substr(0, rule.size() - 1),
1148                         static_cast<unsigned>(perMethods[method].rules.size() -
1149                                               1));
1150                 }
1151             }
1152         }
1153     }
1154 
1155     void validate()
1156     {
1157         for (std::unique_ptr<BaseRule>& rule : allRules)
1158         {
1159             if (rule)
1160             {
1161                 std::unique_ptr<BaseRule> upgraded = rule->upgrade();
1162                 if (upgraded)
1163                 {
1164                     rule = std::move(upgraded);
1165                 }
1166                 rule->validate();
1167                 internalAddRuleObject(rule->rule, rule.get());
1168             }
1169         }
1170         for (PerMethod& perMethod : perMethods)
1171         {
1172             perMethod.trie.validate();
1173         }
1174     }
1175 
1176     struct FindRoute
1177     {
1178         BaseRule* rule = nullptr;
1179         RoutingParams params;
1180     };
1181 
1182     struct FindRouteResponse
1183     {
1184         std::string allowHeader;
1185         FindRoute route;
1186     };
1187 
1188     FindRoute findRouteByIndex(std::string_view url, size_t index) const
1189     {
1190         FindRoute route;
1191         if (index >= perMethods.size())
1192         {
1193             BMCWEB_LOG_CRITICAL << "Bad index???";
1194             return route;
1195         }
1196         const PerMethod& perMethod = perMethods[index];
1197         std::pair<unsigned, RoutingParams> found = perMethod.trie.find(url);
1198         if (found.first >= perMethod.rules.size())
1199         {
1200             throw std::runtime_error("Trie internal structure corrupted!");
1201         }
1202         // Found a 404 route, switch that in
1203         if (found.first != 0U)
1204         {
1205             route.rule = perMethod.rules[found.first];
1206             route.params = std::move(found.second);
1207         }
1208         return route;
1209     }
1210 
1211     FindRouteResponse findRoute(Request& req) const
1212     {
1213         FindRouteResponse findRoute;
1214 
1215         std::optional<HttpVerb> verb = httpVerbFromBoost(req.method());
1216         if (!verb)
1217         {
1218             return findRoute;
1219         }
1220         size_t reqMethodIndex = static_cast<size_t>(*verb);
1221         // Check to see if this url exists at any verb
1222         for (size_t perMethodIndex = 0; perMethodIndex <= maxVerbIndex;
1223              perMethodIndex++)
1224         {
1225             // Make sure it's safe to deference the array at that index
1226             static_assert(maxVerbIndex <
1227                           std::tuple_size_v<decltype(perMethods)>);
1228             FindRoute route =
1229                 findRouteByIndex(req.url().encoded_path(), perMethodIndex);
1230             if (route.rule == nullptr)
1231             {
1232                 continue;
1233             }
1234             if (!findRoute.allowHeader.empty())
1235             {
1236                 findRoute.allowHeader += ", ";
1237             }
1238             HttpVerb thisVerb = static_cast<HttpVerb>(perMethodIndex);
1239             findRoute.allowHeader += httpVerbToString(thisVerb);
1240             if (perMethodIndex == reqMethodIndex)
1241             {
1242                 findRoute.route = route;
1243             }
1244         }
1245         return findRoute;
1246     }
1247 
1248     static bool isUserPrivileged(
1249         Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1250         BaseRule& rule, const dbus::utility::DBusPropertiesMap& userInfoMap)
1251     {
1252         std::string userRole{};
1253         const std::string* userRolePtr = nullptr;
1254         const bool* remoteUser = nullptr;
1255         const bool* passwordExpired = nullptr;
1256 
1257         const bool success = sdbusplus::unpackPropertiesNoThrow(
1258             redfish::dbus_utils::UnpackErrorPrinter(), userInfoMap,
1259             "UserPrivilege", userRolePtr, "RemoteUser", remoteUser,
1260             "UserPasswordExpired", passwordExpired);
1261 
1262         if (!success)
1263         {
1264             asyncResp->res.result(
1265                 boost::beast::http::status::internal_server_error);
1266             return false;
1267         }
1268 
1269         if (userRolePtr != nullptr)
1270         {
1271             userRole = *userRolePtr;
1272             BMCWEB_LOG_DEBUG << "userName = " << req.session->username
1273                              << " userRole = " << *userRolePtr;
1274         }
1275 
1276         if (remoteUser == nullptr)
1277         {
1278             BMCWEB_LOG_ERROR << "RemoteUser property missing or wrong type";
1279             asyncResp->res.result(
1280                 boost::beast::http::status::internal_server_error);
1281             return false;
1282         }
1283         bool expired = false;
1284         if (passwordExpired == nullptr)
1285         {
1286             if (!*remoteUser)
1287             {
1288                 BMCWEB_LOG_ERROR
1289                     << "UserPasswordExpired property is expected for"
1290                        " local user but is missing or wrong type";
1291                 asyncResp->res.result(
1292                     boost::beast::http::status::internal_server_error);
1293                 return false;
1294             }
1295         }
1296         else
1297         {
1298             expired = *passwordExpired;
1299         }
1300 
1301         // Get the user's privileges from the role
1302         redfish::Privileges userPrivileges =
1303             redfish::getUserPrivileges(userRole);
1304 
1305         // Set isConfigureSelfOnly based on D-Bus results.  This
1306         // ignores the results from both pamAuthenticateUser and the
1307         // value from any previous use of this session.
1308         req.session->isConfigureSelfOnly = expired;
1309 
1310         // Modify privileges if isConfigureSelfOnly.
1311         if (req.session->isConfigureSelfOnly)
1312         {
1313             // Remove all privileges except ConfigureSelf
1314             userPrivileges = userPrivileges.intersection(
1315                 redfish::Privileges{"ConfigureSelf"});
1316             BMCWEB_LOG_DEBUG << "Operation limited to ConfigureSelf";
1317         }
1318 
1319         if (!rule.checkPrivileges(userPrivileges))
1320         {
1321             asyncResp->res.result(boost::beast::http::status::forbidden);
1322             if (req.session->isConfigureSelfOnly)
1323             {
1324                 redfish::messages::passwordChangeRequired(
1325                     asyncResp->res, crow::utility::urlFromPieces(
1326                                         "redfish", "v1", "AccountService",
1327                                         "Accounts", req.session->username));
1328             }
1329             return false;
1330         }
1331 
1332         req.userRole = userRole;
1333 
1334         return true;
1335     }
1336 
1337     template <typename CallbackFn>
1338     void afterGetUserInfo(Request&& req,
1339                           const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1340                           BaseRule& rule, CallbackFn&& callback,
1341                           const boost::system::error_code& ec,
1342                           const dbus::utility::DBusPropertiesMap& userInfoMap)
1343     {
1344         if (ec)
1345         {
1346             BMCWEB_LOG_ERROR << "GetUserInfo failed...";
1347             asyncResp->res.result(
1348                 boost::beast::http::status::internal_server_error);
1349             return;
1350         }
1351 
1352         if (!Router::isUserPrivileged(req, asyncResp, rule, userInfoMap))
1353         {
1354             // User is not privileged
1355             BMCWEB_LOG_ERROR << "Insufficient Privilege";
1356             asyncResp->res.result(boost::beast::http::status::forbidden);
1357             return;
1358         }
1359         callback(std::move(req));
1360     }
1361 
1362     template <typename CallbackFn>
1363     void validatePrivilege(Request&& req,
1364                            const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1365                            BaseRule& rule, CallbackFn&& callback)
1366     {
1367         if (req.session == nullptr)
1368         {
1369             return;
1370         }
1371         std::string username = req.session->username;
1372         crow::connections::systemBus->async_method_call(
1373             [this, req{std::move(req)}, asyncResp, &rule,
1374              callback(std::forward<CallbackFn>(callback))](
1375                 const boost::system::error_code& ec,
1376                 const dbus::utility::DBusPropertiesMap& userInfoMap) mutable {
1377             afterGetUserInfo(std::move(req), asyncResp, rule,
1378                              std::forward<CallbackFn>(callback), ec,
1379                              userInfoMap);
1380             },
1381             "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1382             "xyz.openbmc_project.User.Manager", "GetUserInfo", username);
1383     }
1384 
1385     template <typename Adaptor>
1386     void handleUpgrade(Request& req,
1387                        const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1388                        Adaptor&& adaptor)
1389     {
1390         std::optional<HttpVerb> verb = httpVerbFromBoost(req.method());
1391         if (!verb || static_cast<size_t>(*verb) >= perMethods.size())
1392         {
1393             asyncResp->res.result(boost::beast::http::status::not_found);
1394             return;
1395         }
1396         PerMethod& perMethod = perMethods[static_cast<size_t>(*verb)];
1397         Trie& trie = perMethod.trie;
1398         std::vector<BaseRule*>& rules = perMethod.rules;
1399 
1400         const std::pair<unsigned, RoutingParams>& found =
1401             trie.find(req.url().encoded_path());
1402         unsigned ruleIndex = found.first;
1403         if (ruleIndex == 0U)
1404         {
1405             BMCWEB_LOG_DEBUG << "Cannot match rules "
1406                              << req.url().encoded_path();
1407             asyncResp->res.result(boost::beast::http::status::not_found);
1408             return;
1409         }
1410 
1411         if (ruleIndex >= rules.size())
1412         {
1413             throw std::runtime_error("Trie internal structure corrupted!");
1414         }
1415 
1416         BaseRule& rule = *rules[ruleIndex];
1417         size_t methods = rule.getMethods();
1418         if ((methods & (1U << static_cast<size_t>(*verb))) == 0)
1419         {
1420             BMCWEB_LOG_DEBUG
1421                 << "Rule found but method mismatch: "
1422                 << req.url().encoded_path() << " with " << req.methodString()
1423                 << "(" << static_cast<uint32_t>(*verb) << ") / " << methods;
1424             asyncResp->res.result(boost::beast::http::status::not_found);
1425             return;
1426         }
1427 
1428         BMCWEB_LOG_DEBUG << "Matched rule (upgrade) '" << rule.rule << "' "
1429                          << static_cast<uint32_t>(*verb) << " / " << methods;
1430 
1431         // TODO(ed) This should be able to use std::bind_front, but it doesn't
1432         // appear to work with the std::move on adaptor.
1433         validatePrivilege(
1434             std::move(req), asyncResp, rule,
1435             [&rule, asyncResp, adaptor(std::forward<Adaptor>(adaptor))](
1436                 Request&& thisReq) mutable {
1437             rule.handleUpgrade(thisReq, asyncResp, std::move(adaptor));
1438             });
1439     }
1440 
1441     void handle(Request& req,
1442                 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1443     {
1444         std::optional<HttpVerb> verb = httpVerbFromBoost(req.method());
1445         if (!verb || static_cast<size_t>(*verb) >= perMethods.size())
1446         {
1447             asyncResp->res.result(boost::beast::http::status::not_found);
1448             return;
1449         }
1450 
1451         FindRouteResponse foundRoute = findRoute(req);
1452 
1453         if (foundRoute.route.rule == nullptr)
1454         {
1455             // Couldn't find a normal route with any verb, try looking for a 404
1456             // route
1457             if (foundRoute.allowHeader.empty())
1458             {
1459                 foundRoute.route =
1460                     findRouteByIndex(req.url().encoded_path(), notFoundIndex);
1461             }
1462             else
1463             {
1464                 // See if we have a method not allowed (405) handler
1465                 foundRoute.route = findRouteByIndex(req.url().encoded_path(),
1466                                                     methodNotAllowedIndex);
1467             }
1468         }
1469 
1470         // Fill in the allow header if it's valid
1471         if (!foundRoute.allowHeader.empty())
1472         {
1473 
1474             asyncResp->res.addHeader(boost::beast::http::field::allow,
1475                                      foundRoute.allowHeader);
1476         }
1477 
1478         // If we couldn't find a real route or a 404 route, return a generic
1479         // response
1480         if (foundRoute.route.rule == nullptr)
1481         {
1482             if (foundRoute.allowHeader.empty())
1483             {
1484                 asyncResp->res.result(boost::beast::http::status::not_found);
1485             }
1486             else
1487             {
1488                 asyncResp->res.result(
1489                     boost::beast::http::status::method_not_allowed);
1490             }
1491             return;
1492         }
1493 
1494         BaseRule& rule = *foundRoute.route.rule;
1495         RoutingParams params = std::move(foundRoute.route.params);
1496 
1497         BMCWEB_LOG_DEBUG << "Matched rule '" << rule.rule << "' "
1498                          << static_cast<uint32_t>(*verb) << " / "
1499                          << rule.getMethods();
1500 
1501         if (req.session == nullptr)
1502         {
1503             rule.handle(req, asyncResp, params);
1504             return;
1505         }
1506         validatePrivilege(
1507             std::move(req), asyncResp, rule,
1508             [&rule, asyncResp, params](Request&& thisReq) mutable {
1509             rule.handle(thisReq, asyncResp, params);
1510             });
1511     }
1512 
1513     void debugPrint()
1514     {
1515         for (size_t i = 0; i < perMethods.size(); i++)
1516         {
1517             BMCWEB_LOG_DEBUG << boost::beast::http::to_string(
1518                 static_cast<boost::beast::http::verb>(i));
1519             perMethods[i].trie.debugPrint();
1520         }
1521     }
1522 
1523     std::vector<const std::string*> getRoutes(const std::string& parent)
1524     {
1525         std::vector<const std::string*> ret;
1526 
1527         for (const PerMethod& pm : perMethods)
1528         {
1529             std::vector<unsigned> x;
1530             pm.trie.findRouteIndexes(parent, x);
1531             for (unsigned index : x)
1532             {
1533                 ret.push_back(&pm.rules[index]->rule);
1534             }
1535         }
1536         return ret;
1537     }
1538 
1539   private:
1540     struct PerMethod
1541     {
1542         std::vector<BaseRule*> rules;
1543         Trie trie;
1544         // rule index 0 has special meaning; preallocate it to avoid
1545         // duplication.
1546         PerMethod() : rules(1)
1547         {}
1548     };
1549 
1550     std::array<PerMethod, methodNotAllowedIndex + 1> perMethods;
1551     std::vector<std::unique_ptr<BaseRule>> allRules;
1552 };
1553 } // namespace crow
1554