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