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