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