Crow  1.1
A C++ microframework for the web
routing.h
1 #pragma once
2 
3 #include <cstdint>
4 #include <utility>
5 #include <tuple>
6 #include <unordered_map>
7 #include <memory>
8 #include <vector>
9 #include <algorithm>
10 #include <type_traits>
11 
12 #include "crow/common.h"
13 #include "crow/http_response.h"
14 #include "crow/http_request.h"
15 #include "crow/utility.h"
16 #include "crow/logging.h"
17 #include "crow/websocket.h"
18 #include "crow/mustache.h"
19 #include "crow/middleware.h"
20 
21 namespace crow // NOTE: Already documented in "crow/app.h"
22 {
23 
24  constexpr const uint16_t INVALID_BP_ID{((uint16_t)-1)};
25 
26  namespace detail
27  {
28  /// Typesafe wrapper for storing lists of middleware as their indices in the App
30  {
31  template<typename App>
32  void push()
33  {}
34 
35  template<typename App, typename MW, typename... Middlewares>
36  void push()
37  {
38  using MwContainer = typename App::mw_container_t;
39  static_assert(black_magic::has_type<MW, MwContainer>::value, "Middleware must be present in app");
40  static_assert(std::is_base_of<crow::ILocalMiddleware, MW>::value, "Middleware must extend ILocalMiddleware");
41  int idx = black_magic::tuple_index<MW, MwContainer>::value;
42  indices_.push_back(idx);
43  push<App, Middlewares...>();
44  }
45 
46  void merge_front(const detail::middleware_indices& other)
47  {
48  indices_.insert(indices_.begin(), other.indices_.cbegin(), other.indices_.cend());
49  }
50 
51  void merge_back(const detail::middleware_indices& other)
52  {
53  indices_.insert(indices_.end(), other.indices_.cbegin(), other.indices_.cend());
54  }
55 
56  void pop_back(const detail::middleware_indices& other)
57  {
58  indices_.resize(indices_.size() - other.indices_.size());
59  }
60 
61  bool empty() const
62  {
63  return indices_.empty();
64  }
65 
66  // Sorts indices and filters out duplicates to allow fast lookups with traversal
67  void pack()
68  {
69  std::sort(indices_.begin(), indices_.end());
70  indices_.erase(std::unique(indices_.begin(), indices_.end()), indices_.end());
71  }
72 
73  const std::vector<int>& indices()
74  {
75  return indices_;
76  }
77 
78  private:
79  std::vector<int> indices_;
80  };
81  } // namespace detail
82 
83  /// A base class for all rules.
84 
85  ///
86  /// Used to provide a common interface for code dealing with different types of rules.<br>
87  /// A Rule provides a URL, allowed HTTP methods, and handlers.
88  class BaseRule
89  {
90  public:
91  BaseRule(std::string rule):
92  rule_(std::move(rule))
93  {}
94 
95  virtual ~BaseRule()
96  {}
97 
98  virtual void validate() = 0;
99 
100  void set_added() {
101  added_ = true;
102  }
103 
104  bool is_added() {
105  return added_;
106  }
107 
108  std::unique_ptr<BaseRule> upgrade()
109  {
110  if (rule_to_upgrade_)
111  return std::move(rule_to_upgrade_);
112  return {};
113  }
114 
115  virtual void handle(request&, response&, const routing_params&) = 0;
116  virtual void handle_upgrade(const request&, response& res, SocketAdaptor&&)
117  {
118  res = response(404);
119  res.end();
120  }
121 #ifdef CROW_ENABLE_SSL
122  virtual void handle_upgrade(const request&, response& res, SSLAdaptor&&)
123  {
124  res = response(404);
125  res.end();
126  }
127 #endif
128 
129  uint32_t get_methods()
130  {
131  return methods_;
132  }
133 
134  template<typename F>
135  void foreach_method(F f)
136  {
137  for (uint32_t method = 0, method_bit = 1; method < static_cast<uint32_t>(HTTPMethod::InternalMethodCount); method++, method_bit <<= 1)
138  {
139  if (methods_ & method_bit)
140  f(method);
141  }
142  }
143 
144  std::string custom_templates_base;
145 
146  const std::string& rule() { return rule_; }
147 
148  protected:
149  uint32_t methods_{1 << static_cast<int>(HTTPMethod::Get)};
150 
151  std::string rule_;
152  std::string name_;
153  bool added_{false};
154 
155  std::unique_ptr<BaseRule> rule_to_upgrade_;
156 
157  detail::middleware_indices mw_indices_;
158 
159  friend class Router;
160  friend class Blueprint;
161  template<typename T>
162  friend struct RuleParameterTraits;
163  };
164 
165 
166  namespace detail
167  {
168  namespace routing_handler_call_helper
169  {
170  template<typename T, int Pos>
171  struct call_pair
172  {
173  using type = T;
174  static const int pos = Pos;
175  };
176 
177  template<typename H1>
178  struct call_params
179  {
180  H1& handler;
181  const routing_params& params;
182  request& req;
183  response& res;
184  };
185 
186  template<typename F, int NInt, int NUint, int NDouble, int NString, typename S1, typename S2>
187  struct call
188  {};
189 
190  template<typename F, int NInt, int NUint, int NDouble, int NString, typename... Args1, typename... Args2>
191  struct call<F, NInt, NUint, NDouble, NString, black_magic::S<int64_t, Args1...>, black_magic::S<Args2...>>
192  {
193  void operator()(F cparams)
194  {
195  using pushed = typename black_magic::S<Args2...>::template push_back<call_pair<int64_t, NInt>>;
196  call<F, NInt + 1, NUint, NDouble, NString, black_magic::S<Args1...>, pushed>()(cparams);
197  }
198  };
199 
200  template<typename F, int NInt, int NUint, int NDouble, int NString, typename... Args1, typename... Args2>
201  struct call<F, NInt, NUint, NDouble, NString, black_magic::S<uint64_t, Args1...>, black_magic::S<Args2...>>
202  {
203  void operator()(F cparams)
204  {
205  using pushed = typename black_magic::S<Args2...>::template push_back<call_pair<uint64_t, NUint>>;
206  call<F, NInt, NUint + 1, NDouble, NString, black_magic::S<Args1...>, pushed>()(cparams);
207  }
208  };
209 
210  template<typename F, int NInt, int NUint, int NDouble, int NString, typename... Args1, typename... Args2>
211  struct call<F, NInt, NUint, NDouble, NString, black_magic::S<double, Args1...>, black_magic::S<Args2...>>
212  {
213  void operator()(F cparams)
214  {
215  using pushed = typename black_magic::S<Args2...>::template push_back<call_pair<double, NDouble>>;
216  call<F, NInt, NUint, NDouble + 1, NString, black_magic::S<Args1...>, pushed>()(cparams);
217  }
218  };
219 
220  template<typename F, int NInt, int NUint, int NDouble, int NString, typename... Args1, typename... Args2>
221  struct call<F, NInt, NUint, NDouble, NString, black_magic::S<std::string, Args1...>, black_magic::S<Args2...>>
222  {
223  void operator()(F cparams)
224  {
225  using pushed = typename black_magic::S<Args2...>::template push_back<call_pair<std::string, NString>>;
226  call<F, NInt, NUint, NDouble, NString + 1, black_magic::S<Args1...>, pushed>()(cparams);
227  }
228  };
229 
230  template<typename F, int NInt, int NUint, int NDouble, int NString, typename... Args1>
231  struct call<F, NInt, NUint, NDouble, NString, black_magic::S<>, black_magic::S<Args1...>>
232  {
233  void operator()(F cparams)
234  {
235  cparams.handler(
236  cparams.req,
237  cparams.res,
238  cparams.params.template get<typename Args1::type>(Args1::pos)...);
239  }
240  };
241 
242  template<typename Func, typename... ArgsWrapped>
243  struct Wrapped
244  {
245  template<typename... Args>
246  void set_(Func f, typename std::enable_if<!std::is_same<typename std::tuple_element<0, std::tuple<Args..., void>>::type, const request&>::value, int>::type = 0)
247  {
248  handler_ = (
249 #ifdef CROW_CAN_USE_CPP14
250  [f = std::move(f)]
251 #else
252  [f]
253 #endif
254  (const request&, response& res, Args... args) {
255  res = response(f(args...));
256  res.end();
257  });
258  }
259 
260  template<typename Req, typename... Args>
262  {
263  req_handler_wrapper(Func f):
264  f(std::move(f))
265  {
266  }
267 
268  void operator()(const request& req, response& res, Args... args)
269  {
270  res = response(f(req, args...));
271  res.end();
272  }
273 
274  Func f;
275  };
276 
277  template<typename... Args>
278  void set_(Func f, typename std::enable_if<
279  std::is_same<typename std::tuple_element<0, std::tuple<Args..., void>>::type, const request&>::value &&
280  !std::is_same<typename std::tuple_element<1, std::tuple<Args..., void, void>>::type, response&>::value,
281  int>::type = 0)
282  {
283  handler_ = req_handler_wrapper<Args...>(std::move(f));
284  /*handler_ = (
285  [f = std::move(f)]
286  (const request& req, response& res, Args... args){
287  res = response(f(req, args...));
288  res.end();
289  });*/
290  }
291 
292  template<typename... Args>
293  void set_(Func f, typename std::enable_if<
294  std::is_same<typename std::tuple_element<0, std::tuple<Args..., void>>::type, const request&>::value &&
295  std::is_same<typename std::tuple_element<1, std::tuple<Args..., void, void>>::type, response&>::value,
296  int>::type = 0)
297  {
298  handler_ = std::move(f);
299  }
300 
301  template<typename... Args>
303  {
304  using type = std::function<void(const crow::request&, crow::response&, Args...)>;
305  using args_type = black_magic::S<typename black_magic::promote_t<Args>...>;
306  };
307 
308  template<typename... Args>
309  struct handler_type_helper<const request&, Args...>
310  {
311  using type = std::function<void(const crow::request&, crow::response&, Args...)>;
312  using args_type = black_magic::S<typename black_magic::promote_t<Args>...>;
313  };
314 
315  template<typename... Args>
316  struct handler_type_helper<const request&, response&, Args...>
317  {
318  using type = std::function<void(const crow::request&, crow::response&, Args...)>;
319  using args_type = black_magic::S<typename black_magic::promote_t<Args>...>;
320  };
321 
322  typename handler_type_helper<ArgsWrapped...>::type handler_;
323 
324  void operator()(request& req, response& res, const routing_params& params)
325  {
328  decltype(handler_)>,
329  0, 0, 0, 0,
330  typename handler_type_helper<ArgsWrapped...>::args_type,
331  black_magic::S<>>()(
333  decltype(handler_)>{handler_, params, req, res});
334  }
335  };
336 
337  } // namespace routing_handler_call_helper
338  } // namespace detail
339 
340 
342  {
343  public:
344  /// @cond SKIP
345  CatchallRule() {}
346 
347  template<typename Func>
348  typename std::enable_if<black_magic::CallHelper<Func, black_magic::S<>>::value, void>::type
349  operator()(Func&& f)
350  {
351  static_assert(!std::is_same<void, decltype(f())>::value,
352  "Handler function cannot have void return type; valid return types: string, int, crow::response, crow::returnable");
353 
354  handler_ = (
355 #ifdef CROW_CAN_USE_CPP14
356  [f = std::move(f)]
357 #else
358  [f]
359 #endif
360  (const request&, response& res) {
361  res = response(f());
362  res.end();
363  });
364  }
365 
366  template<typename Func>
367  typename std::enable_if<
368  !black_magic::CallHelper<Func, black_magic::S<>>::value &&
369  black_magic::CallHelper<Func, black_magic::S<crow::request>>::value,
370  void>::type
371  operator()(Func&& f)
372  {
373  static_assert(!std::is_same<void, decltype(f(std::declval<crow::request>()))>::value,
374  "Handler function cannot have void return type; valid return types: string, int, crow::response, crow::returnable");
375 
376  handler_ = (
377 #ifdef CROW_CAN_USE_CPP14
378  [f = std::move(f)]
379 #else
380  [f]
381 #endif
382  (const crow::request& req, crow::response& res) {
383  res = response(f(req));
384  res.end();
385  });
386  }
387 
388  template<typename Func>
389  typename std::enable_if<
390  !black_magic::CallHelper<Func, black_magic::S<>>::value &&
391  !black_magic::CallHelper<Func, black_magic::S<crow::request>>::value &&
392  black_magic::CallHelper<Func, black_magic::S<crow::response&>>::value,
393  void>::type
394  operator()(Func&& f)
395  {
396  static_assert(std::is_same<void, decltype(f(std::declval<crow::response&>()))>::value,
397  "Handler function with response argument should have void return type");
398  handler_ = (
399 #ifdef CROW_CAN_USE_CPP14
400  [f = std::move(f)]
401 #else
402  [f]
403 #endif
404  (const crow::request&, crow::response& res) {
405  f(res);
406  });
407  }
408 
409  template<typename Func>
410  typename std::enable_if<
411  !black_magic::CallHelper<Func, black_magic::S<>>::value &&
412  !black_magic::CallHelper<Func, black_magic::S<crow::request>>::value &&
413  !black_magic::CallHelper<Func, black_magic::S<crow::response&>>::value,
414  void>::type
415  operator()(Func&& f)
416  {
417  static_assert(std::is_same<void, decltype(f(std::declval<crow::request>(), std::declval<crow::response&>()))>::value,
418  "Handler function with response argument should have void return type");
419 
420  handler_ = std::move(f);
421  }
422  /// @endcond
423  bool has_handler()
424  {
425  return (handler_ != nullptr);
426  }
427 
428  protected:
429  friend class Router;
430 
431  private:
432  std::function<void(const crow::request&, crow::response&)> handler_;
433  };
434 
435 
436  /// A rule dealing with websockets.
437 
438  ///
439  /// Provides the interface for the user to put in the necessary handlers for a websocket to work.
440  template<typename App>
441  class WebSocketRule : public BaseRule
442  {
443  using self_t = WebSocketRule;
444 
445  public:
446  WebSocketRule(std::string rule, App* app):
447  BaseRule(std::move(rule)),
448  app_(app),
449  max_payload_(UINT64_MAX)
450  {}
451 
452  void validate() override
453  {}
454 
455  void handle(request&, response& res, const routing_params&) override
456  {
457  res = response(404);
458  res.end();
459  }
460 
461  void handle_upgrade(const request& req, response&, SocketAdaptor&& adaptor) override
462  {
463  max_payload_ = max_payload_override_ ? max_payload_ : app_->websocket_max_payload();
464  new crow::websocket::Connection<SocketAdaptor, App>(req, std::move(adaptor), app_, max_payload_, subprotocols_, open_handler_, message_handler_, close_handler_, error_handler_, accept_handler_);
465  }
466 #ifdef CROW_ENABLE_SSL
467  void handle_upgrade(const request& req, response&, SSLAdaptor&& adaptor) override
468  {
469  new crow::websocket::Connection<SSLAdaptor, App>(req, std::move(adaptor), app_, max_payload_, subprotocols_, open_handler_, message_handler_, close_handler_, error_handler_, accept_handler_);
470  }
471 #endif
472 
473  /// Override the global payload limit for this single WebSocket rule
475  {
476  max_payload_ = max_payload;
477  max_payload_override_ = true;
478  return *this;
479  }
480 
481  self_t& subprotocols(const std::vector<std::string>& subprotocols)
482  {
483  subprotocols_ = subprotocols;
484  return *this;
485  }
486 
487  template<typename Func>
488  self_t& onopen(Func f)
489  {
490  open_handler_ = f;
491  return *this;
492  }
493 
494  template<typename Func>
495  self_t& onmessage(Func f)
496  {
497  message_handler_ = f;
498  return *this;
499  }
500 
501  template<typename Func>
502  self_t& onclose(Func f)
503  {
504  close_handler_ = f;
505  return *this;
506  }
507 
508  template<typename Func>
509  self_t& onerror(Func f)
510  {
511  error_handler_ = f;
512  return *this;
513  }
514 
515  template<typename Func>
516  self_t& onaccept(Func f)
517  {
518  accept_handler_ = f;
519  return *this;
520  }
521 
522  protected:
523  App* app_;
524  std::function<void(crow::websocket::connection&)> open_handler_;
525  std::function<void(crow::websocket::connection&, const std::string&, bool)> message_handler_;
526  std::function<void(crow::websocket::connection&, const std::string&, uint16_t)> close_handler_;
527  std::function<void(crow::websocket::connection&, const std::string&)> error_handler_;
528  std::function<bool(const crow::request&, void**)> accept_handler_;
529  uint64_t max_payload_;
530  bool max_payload_override_ = false;
531  std::vector<std::string> subprotocols_;
532  };
533 
534  /// Allows the user to assign parameters using functions.
535 
536  ///
537  /// `rule.name("name").methods(HTTPMethod::POST)`
538  template<typename T>
540  {
541  using self_t = T;
542 
543  template<typename App>
544  WebSocketRule<App>& websocket(App* app)
545  {
546  auto p = new WebSocketRule<App>(static_cast<self_t*>(this)->rule_, app);
547  static_cast<self_t*>(this)->rule_to_upgrade_.reset(p);
548  return *p;
549  }
550 
551  self_t& name(std::string name) noexcept
552  {
553  static_cast<self_t*>(this)->name_ = std::move(name);
554  return static_cast<self_t&>(*this);
555  }
556 
557  self_t& methods(HTTPMethod method)
558  {
559  static_cast<self_t*>(this)->methods_ = 1 << static_cast<int>(method);
560  return static_cast<self_t&>(*this);
561  }
562 
563  template<typename... MethodArgs>
564  self_t& methods(HTTPMethod method, MethodArgs... args_method)
565  {
566  methods(args_method...);
567  static_cast<self_t*>(this)->methods_ |= 1 << static_cast<int>(method);
568  return static_cast<self_t&>(*this);
569  }
570 
571  /// Enable local middleware for this handler
572  template<typename App, typename... Middlewares>
573  self_t& middlewares()
574  {
575  static_cast<self_t*>(this)->mw_indices_.template push<App, Middlewares...>();
576  return static_cast<self_t&>(*this);
577  }
578  };
579 
580  /// A rule that can change its parameters during runtime.
581  class DynamicRule : public BaseRule, public RuleParameterTraits<DynamicRule>
582  {
583  public:
584  DynamicRule(std::string rule):
585  BaseRule(std::move(rule))
586  {}
587 
588  void validate() override
589  {
590  if (!erased_handler_)
591  {
592  throw std::runtime_error(name_ + (!name_.empty() ? ": " : "") + "no handler for url " + rule_);
593  }
594  }
595 
596  void handle(request& req, response& res, const routing_params& params) override
597  {
598  if (!custom_templates_base.empty())
599  mustache::set_base(custom_templates_base);
600  else if (mustache::detail::get_template_base_directory_ref() != "templates")
601  mustache::set_base("templates");
602  erased_handler_(req, res, params);
603  }
604 
605  template<typename Func>
606  void operator()(Func f)
607  {
608 #ifdef CROW_MSVC_WORKAROUND
609  using function_t = utility::function_traits<decltype(&Func::operator())>;
610 #else
611  using function_t = utility::function_traits<Func>;
612 #endif
613  erased_handler_ = wrap(std::move(f), black_magic::gen_seq<function_t::arity>());
614  }
615 
616  // enable_if Arg1 == request && Arg2 == response
617  // enable_if Arg1 == request && Arg2 != resposne
618  // enable_if Arg1 != request
619 #ifdef CROW_MSVC_WORKAROUND
620  template<typename Func, size_t... Indices>
621 #else
622  template<typename Func, unsigned... Indices>
623 #endif
624  std::function<void(request&, response&, const routing_params&)>
625  wrap(Func f, black_magic::seq<Indices...>)
626  {
627 #ifdef CROW_MSVC_WORKAROUND
628  using function_t = utility::function_traits<decltype(&Func::operator())>;
629 #else
630  using function_t = utility::function_traits<Func>;
631 #endif
632  if (!black_magic::is_parameter_tag_compatible(
633  black_magic::get_parameter_tag_runtime(rule_.c_str()),
634  black_magic::compute_parameter_tag_from_args_list<
635  typename function_t::template arg<Indices>...>::value))
636  {
637  throw std::runtime_error("route_dynamic: Handler type is mismatched with URL parameters: " + rule_);
638  }
640  ret.template set_<
641  typename function_t::template arg<Indices>...>(std::move(f));
642  return ret;
643  }
644 
645  template<typename Func>
646  void operator()(std::string name, Func&& f)
647  {
648  name_ = std::move(name);
649  (*this).template operator()<Func>(std::forward(f));
650  }
651 
652  private:
653  std::function<void(request&, response&, const routing_params&)> erased_handler_;
654  };
655 
656  /// Default rule created when CROW_ROUTE is called.
657  template<typename... Args>
658  class TaggedRule : public BaseRule, public RuleParameterTraits<TaggedRule<Args...>>
659  {
660  public:
661  using self_t = TaggedRule<Args...>;
662 
663  TaggedRule(std::string rule):
664  BaseRule(std::move(rule))
665  {}
666 
667  void validate() override
668  {
669  if (rule_.at(0) != '/')
670  throw std::runtime_error("Internal error: Routes must start with a '/'");
671 
672  if (!handler_)
673  {
674  throw std::runtime_error(name_ + (!name_.empty() ? ": " : "") + "no handler for url " + rule_);
675  }
676  }
677 
678  template<typename Func>
679  void operator()(Func&& f)
680  {
681  handler_ = (
682 #ifdef CROW_CAN_USE_CPP14
683  [f = std::move(f)]
684 #else
685  [f]
686 #endif
687  (crow::request& req, crow::response& res, Args... args) {
688  detail::wrapped_handler_call(req, res, f, std::forward<Args>(args)...);
689  });
690  }
691 
692  template<typename Func>
693  void operator()(std::string name, Func&& f)
694  {
695  name_ = std::move(name);
696  (*this).template operator()<Func>(std::forward(f));
697  }
698 
699  void handle(request& req, response& res, const routing_params& params) override
700  {
701  if (!custom_templates_base.empty())
702  mustache::set_base(custom_templates_base);
703  else if (mustache::detail::get_template_base_directory_ref() != mustache::detail::get_global_template_base_directory_ref())
704  mustache::set_base(mustache::detail::get_global_template_base_directory_ref());
705 
708  0, 0, 0, 0,
709  black_magic::S<Args...>,
710  black_magic::S<>>()(
711  detail::routing_handler_call_helper::call_params<decltype(handler_)>{handler_, params, req, res});
712  }
713 
714  private:
715  std::function<void(crow::request&, crow::response&, Args...)> handler_;
716  };
717 
718  const int RULE_SPECIAL_REDIRECT_SLASH = 1;
719 
720 
721  /// A search tree.
722  class Trie
723  {
724  public:
725  struct Node
726  {
727  uint16_t rule_index{};
728  // Assign the index to the maximum 32 unsigned integer value by default so that any other number (specifically 0) is a valid BP id.
729  uint16_t blueprint_index{INVALID_BP_ID};
730  std::string key;
731  ParamType param = ParamType::MAX; // MAX = No param.
732  std::vector<Node> children;
733 
734  bool IsSimpleNode() const
735  {
736  return !rule_index &&
737  blueprint_index == INVALID_BP_ID &&
738  children.size() < 2 &&
739  param == ParamType::MAX &&
740  std::all_of(std::begin(children), std::end(children), [](const Node& x) {
741  return x.param == ParamType::MAX;
742  });
743  }
744 
745  Node& add_child_node()
746  {
747  children.emplace_back();
748  return children.back();
749  }
750  };
751 
752 
753  Trie()
754  {}
755 
756  /// Check whether or not the trie is empty.
757  bool is_empty()
758  {
759  return head_.children.empty();
760  }
761 
762  void optimize()
763  {
764  for (auto& child : head_.children)
765  {
766  optimizeNode(child);
767  }
768  }
769 
770 
771  private:
772  void optimizeNode(Node& node)
773  {
774  if (node.children.empty())
775  return;
776  if (node.IsSimpleNode())
777  {
778  auto children_temp = std::move(node.children);
779  auto& child_temp = children_temp[0];
780  node.key += child_temp.key;
781  node.rule_index = child_temp.rule_index;
782  node.blueprint_index = child_temp.blueprint_index;
783  node.children = std::move(child_temp.children);
784  optimizeNode(node);
785  }
786  else
787  {
788  for (auto& child : node.children)
789  {
790  optimizeNode(child);
791  }
792  }
793  }
794 
795  void debug_node_print(const Node& node, int level)
796  {
797  if (node.param != ParamType::MAX)
798  {
799  switch (node.param)
800  {
801  case ParamType::INT:
802  CROW_LOG_DEBUG << std::string(3 * level, ' ') << "└➝ "
803  << "<int>";
804  break;
805  case ParamType::UINT:
806  CROW_LOG_DEBUG << std::string(3 * level, ' ') << "└➝ "
807  << "<uint>";
808  break;
809  case ParamType::DOUBLE:
810  CROW_LOG_DEBUG << std::string(3 * level, ' ') << "└➝ "
811  << "<double>";
812  break;
813  case ParamType::STRING:
814  CROW_LOG_DEBUG << std::string(3 * level, ' ') << "└➝ "
815  << "<string>";
816  break;
817  case ParamType::PATH:
818  CROW_LOG_DEBUG << std::string(3 * level, ' ') << "└➝ "
819  << "<path>";
820  break;
821  default:
822  CROW_LOG_DEBUG << std::string(3 * level, ' ') << "└➝ "
823  << "<ERROR>";
824  break;
825  }
826  }
827  else
828  CROW_LOG_DEBUG << std::string(3 * level, ' ') << "└➝ " << node.key;
829 
830  for (const auto& child : node.children)
831  {
832  debug_node_print(child, level + 1);
833  }
834  }
835 
836  public:
837  void debug_print()
838  {
839  CROW_LOG_DEBUG << "└➙ ROOT";
840  for (const auto& child : head_.children)
841  debug_node_print(child, 1);
842  }
843 
844  void validate()
845  {
846  if (!head_.IsSimpleNode())
847  throw std::runtime_error("Internal error: Trie header should be simple!");
848  optimize();
849  }
850 
851  //Rule_index, Blueprint_index, routing_params
852  routing_handle_result find(const std::string& req_url, const Node& node, unsigned pos = 0, routing_params* params = nullptr, std::vector<uint16_t>* blueprints = nullptr) const
853  {
854  //start params as an empty struct
855  routing_params empty;
856  if (params == nullptr)
857  params = &empty;
858  //same for blueprint vector
859  std::vector<uint16_t> MT;
860  if (blueprints == nullptr)
861  blueprints = &MT;
862 
863  uint16_t found{}; //The rule index to be found
864  std::vector<uint16_t> found_BP; //The Blueprint indices to be found
865  routing_params match_params; //supposedly the final matched parameters
866 
867  auto update_found = [&found, &found_BP, &match_params](routing_handle_result& ret) {
868  found_BP = std::move(ret.blueprint_indices);
869  if (ret.rule_index && (!found || found > ret.rule_index))
870  {
871  found = ret.rule_index;
872  match_params = std::move(ret.r_params);
873  }
874  };
875 
876  //if the function was called on a node at the end of the string (the last recursion), return the nodes rule index, and whatever params were passed to the function
877  if (pos == req_url.size())
878  {
879  found_BP = std::move(*blueprints);
880  return routing_handle_result{node.rule_index, *blueprints, *params};
881  }
882 
883  bool found_fragment = false;
884 
885  for (const auto& child : node.children)
886  {
887  if (child.param != ParamType::MAX)
888  {
889  if (child.param == ParamType::INT)
890  {
891  char c = req_url[pos];
892  if ((c >= '0' && c <= '9') || c == '+' || c == '-')
893  {
894  char* eptr;
895  errno = 0;
896  long long int value = strtoll(req_url.data() + pos, &eptr, 10);
897  if (errno != ERANGE && eptr != req_url.data() + pos)
898  {
899  found_fragment = true;
900  params->int_params.push_back(value);
901  if (child.blueprint_index != INVALID_BP_ID) blueprints->push_back(child.blueprint_index);
902  auto ret = find(req_url, child, eptr - req_url.data(), params, blueprints);
903  update_found(ret);
904  params->int_params.pop_back();
905  if (!blueprints->empty()) blueprints->pop_back();
906  }
907  }
908  }
909 
910  else if (child.param == ParamType::UINT)
911  {
912  char c = req_url[pos];
913  if ((c >= '0' && c <= '9') || c == '+')
914  {
915  char* eptr;
916  errno = 0;
917  unsigned long long int value = strtoull(req_url.data() + pos, &eptr, 10);
918  if (errno != ERANGE && eptr != req_url.data() + pos)
919  {
920  found_fragment = true;
921  params->uint_params.push_back(value);
922  if (child.blueprint_index != INVALID_BP_ID) blueprints->push_back(child.blueprint_index);
923  auto ret = find(req_url, child, eptr - req_url.data(), params, blueprints);
924  update_found(ret);
925  params->uint_params.pop_back();
926  if (!blueprints->empty()) blueprints->pop_back();
927  }
928  }
929  }
930 
931  else if (child.param == ParamType::DOUBLE)
932  {
933  char c = req_url[pos];
934  if ((c >= '0' && c <= '9') || c == '+' || c == '-' || c == '.')
935  {
936  char* eptr;
937  errno = 0;
938  double value = strtod(req_url.data() + pos, &eptr);
939  if (errno != ERANGE && eptr != req_url.data() + pos)
940  {
941  found_fragment = true;
942  params->double_params.push_back(value);
943  if (child.blueprint_index != INVALID_BP_ID) blueprints->push_back(child.blueprint_index);
944  auto ret = find(req_url, child, eptr - req_url.data(), params, blueprints);
945  update_found(ret);
946  params->double_params.pop_back();
947  if (!blueprints->empty()) blueprints->pop_back();
948  }
949  }
950  }
951 
952  else if (child.param == ParamType::STRING)
953  {
954  size_t epos = pos;
955  for (; epos < req_url.size(); epos++)
956  {
957  if (req_url[epos] == '/')
958  break;
959  }
960 
961  if (epos != pos)
962  {
963  found_fragment = true;
964  params->string_params.push_back(req_url.substr(pos, epos - pos));
965  if (child.blueprint_index != INVALID_BP_ID) blueprints->push_back(child.blueprint_index);
966  auto ret = find(req_url, child, epos, params, blueprints);
967  update_found(ret);
968  params->string_params.pop_back();
969  if (!blueprints->empty()) blueprints->pop_back();
970  }
971  }
972 
973  else if (child.param == ParamType::PATH)
974  {
975  size_t epos = req_url.size();
976 
977  if (epos != pos)
978  {
979  found_fragment = true;
980  params->string_params.push_back(req_url.substr(pos, epos - pos));
981  if (child.blueprint_index != INVALID_BP_ID) blueprints->push_back(child.blueprint_index);
982  auto ret = find(req_url, child, epos, params, blueprints);
983  update_found(ret);
984  params->string_params.pop_back();
985  if (!blueprints->empty()) blueprints->pop_back();
986  }
987  }
988  }
989 
990  else
991  {
992  const std::string& fragment = child.key;
993  if (req_url.compare(pos, fragment.size(), fragment) == 0)
994  {
995  found_fragment = true;
996  if (child.blueprint_index != INVALID_BP_ID) blueprints->push_back(child.blueprint_index);
997  auto ret = find(req_url, child, pos + fragment.size(), params, blueprints);
998  update_found(ret);
999  if (!blueprints->empty()) blueprints->pop_back();
1000  }
1001  }
1002  }
1003 
1004  if (!found_fragment)
1005  found_BP = std::move(*blueprints);
1006 
1007  return routing_handle_result{found, found_BP, match_params}; //Called after all the recursions have been done
1008  }
1009 
1010  routing_handle_result find(const std::string& req_url) const
1011  {
1012  return find(req_url, head_);
1013  }
1014 
1015  //This functions assumes any blueprint info passed is valid
1016  void add(const std::string& url, uint16_t rule_index, unsigned bp_prefix_length = 0, uint16_t blueprint_index = INVALID_BP_ID)
1017  {
1018  auto idx = &head_;
1019 
1020  bool has_blueprint = bp_prefix_length != 0 && blueprint_index != INVALID_BP_ID;
1021 
1022  for (unsigned i = 0; i < url.size(); i++)
1023  {
1024  char c = url[i];
1025  if (c == '<')
1026  {
1027  static struct ParamTraits
1028  {
1029  ParamType type;
1030  std::string name;
1031  } paramTraits[] =
1032  {
1033  {ParamType::INT, "<int>"},
1034  {ParamType::UINT, "<uint>"},
1035  {ParamType::DOUBLE, "<float>"},
1036  {ParamType::DOUBLE, "<double>"},
1037  {ParamType::STRING, "<str>"},
1038  {ParamType::STRING, "<string>"},
1039  {ParamType::PATH, "<path>"},
1040  };
1041 
1042  for (const auto& x : paramTraits)
1043  {
1044  if (url.compare(i, x.name.size(), x.name) == 0)
1045  {
1046  bool found = false;
1047  for (auto& child : idx->children)
1048  {
1049  if (child.param == x.type)
1050  {
1051  idx = &child;
1052  i += x.name.size();
1053  found = true;
1054  break;
1055  }
1056  }
1057  if (found)
1058  break;
1059 
1060  auto new_node_idx = &idx->add_child_node();
1061  new_node_idx->param = x.type;
1062  idx = new_node_idx;
1063  i += x.name.size();
1064  break;
1065  }
1066  }
1067 
1068  i--;
1069  }
1070  else
1071  {
1072  //This part assumes the tree is unoptimized (every node has a max 1 character key)
1073  bool piece_found = false;
1074  for (auto& child : idx->children)
1075  {
1076  if (child.key[0] == c)
1077  {
1078  idx = &child;
1079  piece_found = true;
1080  break;
1081  }
1082  }
1083  if (!piece_found)
1084  {
1085  auto new_node_idx = &idx->add_child_node();
1086  new_node_idx->key = c;
1087  //The assumption here is that you'd only need to add a blueprint index if the tree didn't have the BP prefix.
1088  if (has_blueprint && i == bp_prefix_length)
1089  new_node_idx->blueprint_index = blueprint_index;
1090  idx = new_node_idx;
1091  }
1092  }
1093  }
1094 
1095  //check if the last node already has a value (exact url already in Trie)
1096  if (idx->rule_index)
1097  throw std::runtime_error("handler already exists for " + url);
1098  idx->rule_index = rule_index;
1099  }
1100 
1101  private:
1102  Node head_;
1103  };
1104 
1105  /// A blueprint can be considered a smaller section of a Crow app, specifically where the router is concerned.
1106 
1107  ///
1108  /// You can use blueprints to assign a common prefix to rules' prefix, set custom static and template folders, and set a custom catchall route.
1109  /// You can also assign nest blueprints for maximum Compartmentalization.
1111  {
1112  public:
1113  Blueprint(const std::string& prefix)
1114  : prefix_(prefix),
1115  static_dir_(prefix),
1116  templates_dir_(prefix)
1117  {};
1118 
1119  Blueprint(const std::string& prefix, const std::string& static_dir):
1120  prefix_(prefix), static_dir_(static_dir){};
1121 
1122  Blueprint(const std::string& prefix, const std::string& static_dir, const std::string& templates_dir):
1123  prefix_(prefix), static_dir_(static_dir), templates_dir_(templates_dir){};
1124 
1125  /*
1126  Blueprint(Blueprint& other)
1127  {
1128  prefix_ = std::move(other.prefix_);
1129  all_rules_ = std::move(other.all_rules_);
1130  }
1131 
1132  Blueprint(const Blueprint& other)
1133  {
1134  prefix_ = other.prefix_;
1135  all_rules_ = other.all_rules_;
1136  }
1137 */
1138  Blueprint(Blueprint&& value)
1139  {
1140  *this = std::move(value);
1141  }
1142 
1143  Blueprint& operator=(const Blueprint& value) = delete;
1144 
1145  Blueprint& operator=(Blueprint&& value) noexcept
1146  {
1147  prefix_ = std::move(value.prefix_);
1148  static_dir_ = std::move(value.static_dir_);
1149  templates_dir_ = std::move(value.templates_dir_);
1150  all_rules_ = std::move(value.all_rules_);
1151  catchall_rule_ = std::move(value.catchall_rule_);
1152  blueprints_ = std::move(value.blueprints_);
1153  mw_indices_ = std::move(value.mw_indices_);
1154  return *this;
1155  }
1156 
1157  bool operator==(const Blueprint& value)
1158  {
1159  return value.prefix() == prefix_;
1160  }
1161 
1162  bool operator!=(const Blueprint& value)
1163  {
1164  return value.prefix() != prefix_;
1165  }
1166 
1167  std::string prefix() const
1168  {
1169  return prefix_;
1170  }
1171 
1172  std::string static_dir() const
1173  {
1174  return static_dir_;
1175  }
1176 
1177  void set_added() {
1178  added_ = true;
1179  }
1180 
1181  bool is_added() {
1182  return added_;
1183  }
1184 
1185  DynamicRule& new_rule_dynamic(const std::string& rule)
1186  {
1187  std::string new_rule = '/' + prefix_ + rule;
1188  auto ruleObject = new DynamicRule(std::move(new_rule));
1189  ruleObject->custom_templates_base = templates_dir_;
1190  all_rules_.emplace_back(ruleObject);
1191 
1192  return *ruleObject;
1193  }
1194 
1195  template<uint64_t N>
1196  typename black_magic::arguments<N>::type::template rebind<TaggedRule>& new_rule_tagged(const std::string& rule)
1197  {
1198  std::string new_rule = '/' + prefix_ + rule;
1199  using RuleT = typename black_magic::arguments<N>::type::template rebind<TaggedRule>;
1200 
1201  auto ruleObject = new RuleT(std::move(new_rule));
1202  ruleObject->custom_templates_base = templates_dir_;
1203  all_rules_.emplace_back(ruleObject);
1204 
1205  return *ruleObject;
1206  }
1207 
1208  void register_blueprint(Blueprint& blueprint)
1209  {
1210  if (blueprints_.empty() || std::find(blueprints_.begin(), blueprints_.end(), &blueprint) == blueprints_.end())
1211  {
1212  apply_blueprint(blueprint);
1213  blueprints_.emplace_back(&blueprint);
1214  }
1215  else
1216  throw std::runtime_error("blueprint \"" + blueprint.prefix_ + "\" already exists in blueprint \"" + prefix_ + '\"');
1217  }
1218 
1219 
1220  CatchallRule& catchall_rule()
1221  {
1222  return catchall_rule_;
1223  }
1224 
1225  template<typename App, typename... Middlewares>
1226  void middlewares()
1227  {
1228  mw_indices_.push<App, Middlewares...>();
1229  }
1230 
1231  private:
1232  void apply_blueprint(Blueprint& blueprint)
1233  {
1234 
1235  blueprint.prefix_ = prefix_ + '/' + blueprint.prefix_;
1236  blueprint.static_dir_ = static_dir_ + '/' + blueprint.static_dir_;
1237  blueprint.templates_dir_ = templates_dir_ + '/' + blueprint.templates_dir_;
1238  for (auto& rule : blueprint.all_rules_)
1239  {
1240  std::string new_rule = '/' + prefix_ + rule->rule_;
1241  rule->rule_ = new_rule;
1242  }
1243  for (Blueprint* bp_child : blueprint.blueprints_)
1244  {
1245  Blueprint& bp_ref = *bp_child;
1246  apply_blueprint(bp_ref);
1247  }
1248  }
1249 
1250  std::string prefix_;
1251  std::string static_dir_;
1252  std::string templates_dir_;
1253  std::vector<std::unique_ptr<BaseRule>> all_rules_;
1254  CatchallRule catchall_rule_;
1255  std::vector<Blueprint*> blueprints_;
1256  detail::middleware_indices mw_indices_;
1257  bool added_{false};
1258 
1259  friend class Router;
1260  };
1261 
1262  /// Handles matching requests to existing rules and upgrade requests.
1263  class Router
1264  {
1265  public:
1266  Router()
1267  {}
1268 
1269  DynamicRule& new_rule_dynamic(const std::string& rule)
1270  {
1271  auto ruleObject = new DynamicRule(rule);
1272  all_rules_.emplace_back(ruleObject);
1273 
1274  return *ruleObject;
1275  }
1276 
1277  template<uint64_t N>
1278  typename black_magic::arguments<N>::type::template rebind<TaggedRule>& new_rule_tagged(const std::string& rule)
1279  {
1280  using RuleT = typename black_magic::arguments<N>::type::template rebind<TaggedRule>;
1281 
1282  auto ruleObject = new RuleT(rule);
1283  all_rules_.emplace_back(ruleObject);
1284 
1285  return *ruleObject;
1286  }
1287 
1288  CatchallRule& catchall_rule()
1289  {
1290  return catchall_rule_;
1291  }
1292 
1293  void internal_add_rule_object(const std::string& rule, BaseRule* ruleObject)
1294  {
1295  internal_add_rule_object(rule, ruleObject, INVALID_BP_ID, blueprints_);
1296  }
1297 
1298  void internal_add_rule_object(const std::string& rule, BaseRule* ruleObject, const uint16_t& BP_index, std::vector<Blueprint*>& blueprints)
1299  {
1300  bool has_trailing_slash = false;
1301  std::string rule_without_trailing_slash;
1302  if (rule.size() > 1 && rule.back() == '/')
1303  {
1304  has_trailing_slash = true;
1305  rule_without_trailing_slash = rule;
1306  rule_without_trailing_slash.pop_back();
1307  }
1308 
1309  ruleObject->mw_indices_.pack();
1310 
1311  ruleObject->foreach_method([&](int method) {
1312  per_methods_[method].rules.emplace_back(ruleObject);
1313  per_methods_[method].trie.add(rule, per_methods_[method].rules.size() - 1, BP_index != INVALID_BP_ID ? blueprints[BP_index]->prefix().length() : 0, BP_index);
1314 
1315  // directory case:
1316  // request to '/about' url matches '/about/' rule
1317  if (has_trailing_slash)
1318  {
1319  per_methods_[method].trie.add(rule_without_trailing_slash, RULE_SPECIAL_REDIRECT_SLASH, BP_index != INVALID_BP_ID ? blueprints[BP_index]->prefix().length() : 0, BP_index);
1320  }
1321  });
1322 
1323  ruleObject->set_added();
1324  }
1325 
1326  void register_blueprint(Blueprint& blueprint)
1327  {
1328  if (std::find(blueprints_.begin(), blueprints_.end(), &blueprint) == blueprints_.end())
1329  {
1330  blueprints_.emplace_back(&blueprint);
1331  }
1332  else
1333  throw std::runtime_error("blueprint \"" + blueprint.prefix_ + "\" already exists in router");
1334  }
1335 
1336  void get_recursive_child_methods(Blueprint* blueprint, std::vector<HTTPMethod>& methods)
1337  {
1338  //we only need to deal with children if the blueprint has absolutely no methods (meaning its index won't be added to the trie)
1339  if (blueprint->static_dir_.empty() && blueprint->all_rules_.empty())
1340  {
1341  for (Blueprint* bp : blueprint->blueprints_)
1342  {
1343  get_recursive_child_methods(bp, methods);
1344  }
1345  }
1346  else if (!blueprint->static_dir_.empty())
1347  methods.emplace_back(HTTPMethod::Get);
1348  for (auto& rule : blueprint->all_rules_)
1349  {
1350  rule->foreach_method([&methods](unsigned method) {
1351  HTTPMethod method_final = static_cast<HTTPMethod>(method);
1352  if (std::find(methods.begin(), methods.end(), method_final) == methods.end())
1353  methods.emplace_back(method_final);
1354  });
1355  }
1356  }
1357 
1358  void validate_bp() {
1359  //Take all the routes from the registered blueprints and add them to `all_rules_` to be processed.
1360  detail::middleware_indices blueprint_mw;
1361  validate_bp(blueprints_, blueprint_mw);
1362  }
1363 
1364  void validate_bp(std::vector<Blueprint*> blueprints, detail::middleware_indices& current_mw)
1365  {
1366  for (unsigned i = 0; i < blueprints.size(); i++)
1367  {
1368  Blueprint* blueprint = blueprints[i];
1369 
1370  if (blueprint->is_added()) continue;
1371 
1372  if (blueprint->static_dir_ == "" && blueprint->all_rules_.empty())
1373  {
1374  std::vector<HTTPMethod> methods;
1375  get_recursive_child_methods(blueprint, methods);
1376  for (HTTPMethod x : methods)
1377  {
1378  int i = static_cast<int>(x);
1379  per_methods_[i].trie.add(blueprint->prefix(), 0, blueprint->prefix().length(), i);
1380  }
1381  }
1382 
1383  current_mw.merge_back(blueprint->mw_indices_);
1384  for (auto& rule : blueprint->all_rules_)
1385  {
1386  if (rule && !rule->is_added())
1387  {
1388  auto upgraded = rule->upgrade();
1389  if (upgraded)
1390  rule = std::move(upgraded);
1391  rule->validate();
1392  rule->mw_indices_.merge_front(current_mw);
1393  internal_add_rule_object(rule->rule(), rule.get(), i, blueprints);
1394  }
1395  }
1396  validate_bp(blueprint->blueprints_, current_mw);
1397  current_mw.pop_back(blueprint->mw_indices_);
1398  blueprint->set_added();
1399  }
1400  }
1401 
1402  void validate()
1403  {
1404  for (auto& rule : all_rules_)
1405  {
1406  if (rule && !rule->is_added())
1407  {
1408  auto upgraded = rule->upgrade();
1409  if (upgraded)
1410  rule = std::move(upgraded);
1411  rule->validate();
1412  internal_add_rule_object(rule->rule(), rule.get());
1413  }
1414  }
1415  for (auto& per_method : per_methods_)
1416  {
1417  per_method.trie.validate();
1418  }
1419  }
1420 
1421  // TODO maybe add actual_method
1422  template<typename Adaptor>
1423  void handle_upgrade(const request& req, response& res, Adaptor&& adaptor)
1424  {
1425  if (req.method >= HTTPMethod::InternalMethodCount)
1426  return;
1427 
1428  auto& per_method = per_methods_[static_cast<int>(req.method)];
1429  auto& rules = per_method.rules;
1430  unsigned rule_index = per_method.trie.find(req.url).rule_index;
1431 
1432  if (!rule_index)
1433  {
1434  for (auto& per_method : per_methods_)
1435  {
1436  if (per_method.trie.find(req.url).rule_index)
1437  {
1438  CROW_LOG_DEBUG << "Cannot match method " << req.url << " " << method_name(req.method);
1439  res = response(405);
1440  res.end();
1441  return;
1442  }
1443  }
1444 
1445  CROW_LOG_INFO << "Cannot match rules " << req.url;
1446  res = response(404);
1447  res.end();
1448  return;
1449  }
1450 
1451  if (rule_index >= rules.size())
1452  throw std::runtime_error("Trie internal structure corrupted!");
1453 
1454  if (rule_index == RULE_SPECIAL_REDIRECT_SLASH)
1455  {
1456  CROW_LOG_INFO << "Redirecting to a url with trailing slash: " << req.url;
1457  res = response(301);
1458 
1459  // TODO(ipkn) absolute url building
1460  if (req.get_header_value("Host").empty())
1461  {
1462  res.add_header("Location", req.url + "/");
1463  }
1464  else
1465  {
1466  res.add_header("Location", "http://" + req.get_header_value("Host") + req.url + "/");
1467  }
1468  res.end();
1469  return;
1470  }
1471 
1472  CROW_LOG_DEBUG << "Matched rule (upgrade) '" << rules[rule_index]->rule_ << "' " << static_cast<uint32_t>(req.method) << " / " << rules[rule_index]->get_methods();
1473 
1474  try
1475  {
1476  rules[rule_index]->handle_upgrade(req, res, std::move(adaptor));
1477  }
1478  catch (...)
1479  {
1480  exception_handler_(res);
1481  res.end();
1482  return;
1483  }
1484  }
1485 
1486  void get_found_bp(std::vector<uint16_t>& bp_i, std::vector<Blueprint*>& blueprints, std::vector<Blueprint*>& found_bps, uint16_t index = 0)
1487  {
1488  // This statement makes 3 assertions:
1489  // 1. The index is above 0.
1490  // 2. The index does not lie outside the given blueprint list.
1491  // 3. The next blueprint we're adding has a prefix that starts the same as the already added blueprint + a slash (the rest is irrelevant).
1492  //
1493  // This is done to prevent a blueprint that has a prefix of "bp_prefix2" to be assumed as a child of one that has "bp_prefix".
1494  //
1495  // If any of the assertions is untrue, we delete the last item added, and continue using the blueprint list of the blueprint found before, the topmost being the router's list
1496  auto verify_prefix = [&bp_i, &index, &blueprints, &found_bps]() {
1497  return index > 0 &&
1498  bp_i[index] < blueprints.size() &&
1499  blueprints[bp_i[index]]->prefix().substr(0, found_bps[index - 1]->prefix().length() + 1).compare(std::string(found_bps[index - 1]->prefix() + '/')) == 0;
1500  };
1501  if (index < bp_i.size())
1502  {
1503 
1504  if (verify_prefix())
1505  {
1506  found_bps.push_back(blueprints[bp_i[index]]);
1507  get_found_bp(bp_i, found_bps.back()->blueprints_, found_bps, ++index);
1508  }
1509  else
1510  {
1511  if (found_bps.size() < 2)
1512  {
1513  found_bps.clear();
1514  found_bps.push_back(blueprints_[bp_i[index]]);
1515  }
1516  else
1517  {
1518  found_bps.pop_back();
1519  Blueprint* last_element = found_bps.back();
1520  found_bps.push_back(last_element->blueprints_[bp_i[index]]);
1521  }
1522  get_found_bp(bp_i, found_bps.back()->blueprints_, found_bps, ++index);
1523  }
1524  }
1525  }
1526 
1527  /// Is used to handle errors, you insert the error code, found route, request, and response. and it'll either call the appropriate catchall route (considering the blueprint system) and send you a status string (which is mainly used for debug messages), or just set the response code to the proper error code.
1528  std::string get_error(unsigned short code, routing_handle_result& found, const request& req, response& res)
1529  {
1530  res.code = code;
1531  std::vector<Blueprint*> bps_found;
1532  get_found_bp(found.blueprint_indices, blueprints_, bps_found);
1533  for (int i = bps_found.size() - 1; i > 0; i--)
1534  {
1535  std::vector<uint16_t> bpi = found.blueprint_indices;
1536  if (bps_found[i]->catchall_rule().has_handler())
1537  {
1538  try
1539  {
1540  bps_found[i]->catchall_rule().handler_(req, res);
1541  }
1542  catch (...)
1543  {
1544  exception_handler_(res);
1545  }
1546 #ifdef CROW_ENABLE_DEBUG
1547  return std::string("Redirected to Blueprint \"" + bps_found[i]->prefix() + "\" Catchall rule");
1548 #else
1549  return std::string();
1550 #endif
1551  }
1552  }
1553  if (catchall_rule_.has_handler())
1554  {
1555  try
1556  {
1557  catchall_rule_.handler_(req, res);
1558  }
1559  catch (...)
1560  {
1561  exception_handler_(res);
1562  }
1563 #ifdef CROW_ENABLE_DEBUG
1564  return std::string("Redirected to global Catchall rule");
1565 #else
1566  return std::string();
1567 #endif
1568  }
1569  return std::string();
1570  }
1571 
1572  std::unique_ptr<routing_handle_result> handle_initial(request& req, response& res)
1573  {
1574  HTTPMethod method_actual = req.method;
1575 
1576  std::unique_ptr<routing_handle_result> found{
1578  0,
1579  std::vector<uint16_t>(),
1580  routing_params(),
1581  HTTPMethod::InternalMethodCount)}; // This is always returned to avoid a null pointer dereference.
1582 
1583  // NOTE(EDev): This most likely will never run since the parser should handle this situation and close the connection before it gets here.
1584  if (CROW_UNLIKELY(req.method >= HTTPMethod::InternalMethodCount))
1585  return found;
1586  else if (req.method == HTTPMethod::Head)
1587  {
1588  *found = per_methods_[static_cast<int>(method_actual)].trie.find(req.url);
1589  // support HEAD requests using GET if not defined as method for the requested URL
1590  if (!found->rule_index)
1591  {
1592  method_actual = HTTPMethod::Get;
1593  *found = per_methods_[static_cast<int>(method_actual)].trie.find(req.url);
1594  if (!found->rule_index) // If a route is still not found, return a 404 without executing the rest of the HEAD specific code.
1595  {
1596  CROW_LOG_DEBUG << "Cannot match rules " << req.url;
1597  res = response(404); //TODO(EDev): Should this redirect to catchall?
1598  res.end();
1599  return found;
1600  }
1601  }
1602 
1603  res.skip_body = true;
1604  found->method = method_actual;
1605  return found;
1606  }
1607  else if (req.method == HTTPMethod::Options)
1608  {
1609  std::string allow = "OPTIONS, HEAD, ";
1610 
1611  if (req.url == "/*")
1612  {
1613  for (int i = 0; i < static_cast<int>(HTTPMethod::InternalMethodCount); i++)
1614  {
1615  if (static_cast<int>(HTTPMethod::Head) == i)
1616  continue; // HEAD is always allowed
1617 
1618  if (!per_methods_[i].trie.is_empty())
1619  {
1620  allow += method_name(static_cast<HTTPMethod>(i)) + ", ";
1621  }
1622  }
1623  allow = allow.substr(0, allow.size() - 2);
1624  res = response(204);
1625  res.set_header("Allow", allow);
1626  res.end();
1627  found->method = method_actual;
1628  return found;
1629  }
1630  else
1631  {
1632  bool rules_matched = false;
1633  for (int i = 0; i < static_cast<int>(HTTPMethod::InternalMethodCount); i++)
1634  {
1635  if (per_methods_[i].trie.find(req.url).rule_index)
1636  {
1637  rules_matched = true;
1638 
1639  if (static_cast<int>(HTTPMethod::Head) == i)
1640  continue; // HEAD is always allowed
1641 
1642  allow += method_name(static_cast<HTTPMethod>(i)) + ", ";
1643  }
1644  }
1645  if (rules_matched)
1646  {
1647  allow = allow.substr(0, allow.size() - 2);
1648  res = response(204);
1649  res.set_header("Allow", allow);
1650  res.end();
1651  found->method = method_actual;
1652  return found;
1653  }
1654  else
1655  {
1656  CROW_LOG_DEBUG << "Cannot match rules " << req.url;
1657  res = response(404); //TODO(EDev): Should this redirect to catchall?
1658  res.end();
1659  return found;
1660  }
1661  }
1662  }
1663  else // Every request that isn't a HEAD or OPTIONS request
1664  {
1665  *found = per_methods_[static_cast<int>(method_actual)].trie.find(req.url);
1666  // TODO(EDev): maybe ending the else here would allow the requests coming from above (after removing the return statement) to be checked on whether they actually point to a route
1667  if (!found->rule_index)
1668  {
1669  for (auto& per_method : per_methods_)
1670  {
1671  if (per_method.trie.find(req.url).rule_index) //Route found, but in another method
1672  {
1673  const std::string error_message(get_error(405, *found, req, res));
1674  CROW_LOG_DEBUG << "Cannot match method " << req.url << " " << method_name(method_actual) << ". " << error_message;
1675  res.end();
1676  return found;
1677  }
1678  }
1679  //Route does not exist anywhere
1680 
1681  const std::string error_message(get_error(404, *found, req, res));
1682  CROW_LOG_DEBUG << "Cannot match rules " << req.url << ". " << error_message;
1683  res.end();
1684  return found;
1685  }
1686 
1687  found->method = method_actual;
1688  return found;
1689  }
1690  }
1691 
1692  template<typename App>
1693  void handle(request& req, response& res, routing_handle_result found)
1694  {
1695  HTTPMethod method_actual = found.method;
1696  auto& rules = per_methods_[static_cast<int>(method_actual)].rules;
1697  unsigned rule_index = found.rule_index;
1698 
1699  if (rule_index >= rules.size())
1700  throw std::runtime_error("Trie internal structure corrupted!");
1701 
1702  if (rule_index == RULE_SPECIAL_REDIRECT_SLASH)
1703  {
1704  CROW_LOG_INFO << "Redirecting to a url with trailing slash: " << req.url;
1705  res = response(301);
1706 
1707  // TODO(ipkn) absolute url building
1708  if (req.get_header_value("Host").empty())
1709  {
1710  res.add_header("Location", req.url + "/");
1711  }
1712  else
1713  {
1714  res.add_header("Location", "http://" + req.get_header_value("Host") + req.url + "/");
1715  }
1716  res.end();
1717  return;
1718  }
1719 
1720  CROW_LOG_DEBUG << "Matched rule '" << rules[rule_index]->rule_ << "' " << static_cast<uint32_t>(req.method) << " / " << rules[rule_index]->get_methods();
1721 
1722  try
1723  {
1724  auto& rule = rules[rule_index];
1725  handle_rule<App>(rule, req, res, found.r_params);
1726  }
1727  catch (...)
1728  {
1729  exception_handler_(res);
1730  res.end();
1731  return;
1732  }
1733  }
1734 
1735  template<typename App>
1736  typename std::enable_if<std::tuple_size<typename App::mw_container_t>::value != 0, void>::type
1737  handle_rule(BaseRule* rule, crow::request& req, crow::response& res, const crow::routing_params& rp)
1738  {
1739  if (!rule->mw_indices_.empty())
1740  {
1741  auto& ctx = *reinterpret_cast<typename App::context_t*>(req.middleware_context);
1742  auto& container = *reinterpret_cast<typename App::mw_container_t*>(req.middleware_container);
1743  detail::middleware_call_criteria_dynamic<false> crit_fwd(rule->mw_indices_.indices());
1744 
1745  auto glob_completion_handler = std::move(res.complete_request_handler_);
1746  res.complete_request_handler_ = [] {};
1747 
1748  detail::middleware_call_helper<decltype(crit_fwd),
1749  0, typename App::context_t, typename App::mw_container_t>(crit_fwd, container, req, res, ctx);
1750 
1751  if (res.completed_)
1752  {
1753  glob_completion_handler();
1754  return;
1755  }
1756 
1757  res.complete_request_handler_ = [&rule, &ctx, &container, &req, &res, glob_completion_handler] {
1758  detail::middleware_call_criteria_dynamic<true> crit_bwd(rule->mw_indices_.indices());
1759 
1760  detail::after_handlers_call_helper<
1761  decltype(crit_bwd),
1762  std::tuple_size<typename App::mw_container_t>::value - 1,
1763  typename App::context_t,
1764  typename App::mw_container_t>(crit_bwd, container, ctx, req, res);
1765  glob_completion_handler();
1766  };
1767  }
1768  rule->handle(req, res, rp);
1769  }
1770 
1771  template<typename App>
1772  typename std::enable_if<std::tuple_size<typename App::mw_container_t>::value == 0, void>::type
1773  handle_rule(BaseRule* rule, crow::request& req, crow::response& res, const crow::routing_params& rp)
1774  {
1775  rule->handle(req, res, rp);
1776  }
1777 
1778  void debug_print()
1779  {
1780  for (int i = 0; i < static_cast<int>(HTTPMethod::InternalMethodCount); i++)
1781  {
1782  Trie& trie_ = per_methods_[i].trie;
1783  if (!trie_.is_empty())
1784  {
1785  CROW_LOG_DEBUG << method_name(static_cast<HTTPMethod>(i));
1786  trie_.debug_print();
1787  }
1788  }
1789  }
1790 
1791  std::vector<Blueprint*>& blueprints()
1792  {
1793  return blueprints_;
1794  }
1795 
1796  std::function<void(crow::response&)>& exception_handler()
1797  {
1798  return exception_handler_;
1799  }
1800 
1801  static void default_exception_handler(response& res)
1802  {
1803  // any uncaught exceptions become 500s
1804  res = response(500);
1805 
1806  try
1807  {
1808  throw;
1809  }
1810  catch (const std::exception& e)
1811  {
1812  CROW_LOG_ERROR << "An uncaught exception occurred: " << e.what();
1813  }
1814  catch (...)
1815  {
1816  CROW_LOG_ERROR << "An uncaught exception occurred. The type was unknown so no information was available.";
1817  }
1818  }
1819 
1820  private:
1821  CatchallRule catchall_rule_;
1822 
1823  struct PerMethod
1824  {
1825  std::vector<BaseRule*> rules;
1826  Trie trie;
1827 
1828  // rule index 0, 1 has special meaning; preallocate it to avoid duplication.
1829  PerMethod():
1830  rules(2) {}
1831  };
1832  std::array<PerMethod, static_cast<int>(HTTPMethod::InternalMethodCount)> per_methods_;
1833  std::vector<std::unique_ptr<BaseRule>> all_rules_;
1834  std::vector<Blueprint*> blueprints_;
1835  std::function<void(crow::response&)> exception_handler_ = &default_exception_handler;
1836  };
1837 } // namespace crow
A base class for all rules.
Definition: routing.h:89
A blueprint can be considered a smaller section of a Crow app, specifically where the router is conce...
Definition: routing.h:1111
Definition: routing.h:342
The main server application class.
Definition: app.h:199
self_t & websocket_max_payload(uint64_t max_payload)
Set the default max payload size for websockets.
Definition: app.h:278
A rule that can change its parameters during runtime.
Definition: routing.h:582
Handles matching requests to existing rules and upgrade requests.
Definition: routing.h:1264
std::string get_error(unsigned short code, routing_handle_result &found, const request &req, response &res)
Is used to handle errors, you insert the error code, found route, request, and response....
Definition: routing.h:1528
Default rule created when CROW_ROUTE is called.
Definition: routing.h:659
A search tree.
Definition: routing.h:723
bool is_empty()
Check whether or not the trie is empty.
Definition: routing.h:757
A rule dealing with websockets.
Definition: routing.h:442
self_t & max_payload(uint64_t max_payload)
Override the global payload limit for this single WebSocket rule.
Definition: routing.h:474
A websocket connection.
Definition: websocket.h:106
This file includes the definition of the crow::mustache namespace and its members.
void set_base(const std::string &path)
Defines the templates directory path at route level. By default is templates/.
Definition: mustache.h:738
The main namespace of the library. In this namespace is defined the most important classes and functi...
Crow< Middlewares... > App
Alias of Crow<Middlewares...>. Useful if you want a instance of an Crow application that require Midd...
Definition: app.h:773
Allows the user to assign parameters using functions.
Definition: routing.h:540
self_t & middlewares()
Enable local middleware for this handler.
Definition: routing.h:573
Definition: socket_adaptors.h:107
A wrapper for the asio::ip::tcp::socket and asio::ssl::stream.
Definition: socket_adaptors.h:39
Definition: routing.h:726
Typesafe wrapper for storing lists of middleware as their indices in the App.
Definition: routing.h:30
An HTTP request.
Definition: http_request.h:36
std::string url
The endpoint without any parameters.
Definition: http_request.h:39
HTTP response.
Definition: http_response.h:34
void add_header(std::string key, std::string value)
Add a new header to the response.
Definition: http_response.h:58
bool skip_body
Whether this is a response to a HEAD request.
Definition: http_response.h:47
int code
The Status code for the response.
Definition: http_response.h:40
void set_header(std::string key, std::string value)
Set the value of an existing header in the response.
Definition: http_response.h:51
void end()
Set the response completion flag and call the handler (to send the response).
Definition: http_response.h:237
Definition: common.h:281
A base class for websocket connection.
Definition: websocket.h:62