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
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_, 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_, 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  template<typename Func>
482  self_t& onopen(Func f)
483  {
484  open_handler_ = f;
485  return *this;
486  }
487 
488  template<typename Func>
489  self_t& onmessage(Func f)
490  {
491  message_handler_ = f;
492  return *this;
493  }
494 
495  template<typename Func>
496  self_t& onclose(Func f)
497  {
498  close_handler_ = f;
499  return *this;
500  }
501 
502  template<typename Func>
503  self_t& onerror(Func f)
504  {
505  error_handler_ = f;
506  return *this;
507  }
508 
509  template<typename Func>
510  self_t& onaccept(Func f)
511  {
512  accept_handler_ = f;
513  return *this;
514  }
515 
516  protected:
517  App* app_;
518  std::function<void(crow::websocket::connection&)> open_handler_;
519  std::function<void(crow::websocket::connection&, const std::string&, bool)> message_handler_;
520  std::function<void(crow::websocket::connection&, const std::string&)> close_handler_;
521  std::function<void(crow::websocket::connection&, const std::string&)> error_handler_;
522  std::function<bool(const crow::request&, void**)> accept_handler_;
523  uint64_t max_payload_;
524  bool max_payload_override_ = false;
525  };
526 
527  /// Allows the user to assign parameters using functions.
528 
529  ///
530  /// `rule.name("name").methods(HTTPMethod::POST)`
531  template<typename T>
533  {
534  using self_t = T;
535 
536  template<typename App>
537  WebSocketRule<App>& websocket(App* app)
538  {
539  auto p = new WebSocketRule<App>(static_cast<self_t*>(this)->rule_, app);
540  static_cast<self_t*>(this)->rule_to_upgrade_.reset(p);
541  return *p;
542  }
543 
544  self_t& name(std::string name) noexcept
545  {
546  static_cast<self_t*>(this)->name_ = std::move(name);
547  return static_cast<self_t&>(*this);
548  }
549 
550  self_t& methods(HTTPMethod method)
551  {
552  static_cast<self_t*>(this)->methods_ = 1 << static_cast<int>(method);
553  return static_cast<self_t&>(*this);
554  }
555 
556  template<typename... MethodArgs>
557  self_t& methods(HTTPMethod method, MethodArgs... args_method)
558  {
559  methods(args_method...);
560  static_cast<self_t*>(this)->methods_ |= 1 << static_cast<int>(method);
561  return static_cast<self_t&>(*this);
562  }
563 
564  /// Enable local middleware for this handler
565  template<typename App, typename... Middlewares>
566  self_t& middlewares()
567  {
568  static_cast<self_t*>(this)->mw_indices_.template push<App, Middlewares...>();
569  return static_cast<self_t&>(*this);
570  }
571  };
572 
573  /// A rule that can change its parameters during runtime.
574  class DynamicRule : public BaseRule, public RuleParameterTraits<DynamicRule>
575  {
576  public:
577  DynamicRule(std::string rule):
578  BaseRule(std::move(rule))
579  {}
580 
581  void validate() override
582  {
583  if (!erased_handler_)
584  {
585  throw std::runtime_error(name_ + (!name_.empty() ? ": " : "") + "no handler for url " + rule_);
586  }
587  }
588 
589  void handle(request& req, response& res, const routing_params& params) override
590  {
591  if (!custom_templates_base.empty())
592  mustache::set_base(custom_templates_base);
593  else if (mustache::detail::get_template_base_directory_ref() != "templates")
594  mustache::set_base("templates");
595  erased_handler_(req, res, params);
596  }
597 
598  template<typename Func>
599  void operator()(Func f)
600  {
601 #ifdef CROW_MSVC_WORKAROUND
602  using function_t = utility::function_traits<decltype(&Func::operator())>;
603 #else
604  using function_t = utility::function_traits<Func>;
605 #endif
606  erased_handler_ = wrap(std::move(f), black_magic::gen_seq<function_t::arity>());
607  }
608 
609  // enable_if Arg1 == request && Arg2 == response
610  // enable_if Arg1 == request && Arg2 != resposne
611  // enable_if Arg1 != request
612 #ifdef CROW_MSVC_WORKAROUND
613  template<typename Func, size_t... Indices>
614 #else
615  template<typename Func, unsigned... Indices>
616 #endif
617  std::function<void(request&, response&, const routing_params&)>
618  wrap(Func f, black_magic::seq<Indices...>)
619  {
620 #ifdef CROW_MSVC_WORKAROUND
621  using function_t = utility::function_traits<decltype(&Func::operator())>;
622 #else
623  using function_t = utility::function_traits<Func>;
624 #endif
625  if (!black_magic::is_parameter_tag_compatible(
626  black_magic::get_parameter_tag_runtime(rule_.c_str()),
627  black_magic::compute_parameter_tag_from_args_list<
628  typename function_t::template arg<Indices>...>::value))
629  {
630  throw std::runtime_error("route_dynamic: Handler type is mismatched with URL parameters: " + rule_);
631  }
633  ret.template set_<
634  typename function_t::template arg<Indices>...>(std::move(f));
635  return ret;
636  }
637 
638  template<typename Func>
639  void operator()(std::string name, Func&& f)
640  {
641  name_ = std::move(name);
642  (*this).template operator()<Func>(std::forward(f));
643  }
644 
645  private:
646  std::function<void(request&, response&, const routing_params&)> erased_handler_;
647  };
648 
649  /// Default rule created when CROW_ROUTE is called.
650  template<typename... Args>
651  class TaggedRule : public BaseRule, public RuleParameterTraits<TaggedRule<Args...>>
652  {
653  public:
654  using self_t = TaggedRule<Args...>;
655 
656  TaggedRule(std::string rule):
657  BaseRule(std::move(rule))
658  {}
659 
660  void validate() override
661  {
662  if (rule_.at(0) != '/')
663  throw std::runtime_error("Internal error: Routes must start with a '/'");
664 
665  if (!handler_)
666  {
667  throw std::runtime_error(name_ + (!name_.empty() ? ": " : "") + "no handler for url " + rule_);
668  }
669  }
670 
671  template<typename Func>
672  void operator()(Func&& f)
673  {
674  handler_ = (
675 #ifdef CROW_CAN_USE_CPP14
676  [f = std::move(f)]
677 #else
678  [f]
679 #endif
680  (crow::request& req, crow::response& res, Args... args) {
681  detail::wrapped_handler_call(req, res, f, std::forward<Args>(args)...);
682  });
683  }
684 
685  template<typename Func>
686  void operator()(std::string name, Func&& f)
687  {
688  name_ = std::move(name);
689  (*this).template operator()<Func>(std::forward(f));
690  }
691 
692  void handle(request& req, response& res, const routing_params& params) override
693  {
694  if (!custom_templates_base.empty())
695  mustache::set_base(custom_templates_base);
696  else if (mustache::detail::get_template_base_directory_ref() != mustache::detail::get_global_template_base_directory_ref())
697  mustache::set_base(mustache::detail::get_global_template_base_directory_ref());
698 
701  0, 0, 0, 0,
702  black_magic::S<Args...>,
703  black_magic::S<>>()(
704  detail::routing_handler_call_helper::call_params<decltype(handler_)>{handler_, params, req, res});
705  }
706 
707  private:
708  std::function<void(crow::request&, crow::response&, Args...)> handler_;
709  };
710 
711  const int RULE_SPECIAL_REDIRECT_SLASH = 1;
712 
713 
714  /// A search tree.
715  class Trie
716  {
717  public:
718  struct Node
719  {
720  uint16_t rule_index{};
721  // Assign the index to the maximum 32 unsigned integer value by default so that any other number (specifically 0) is a valid BP id.
722  uint16_t blueprint_index{INVALID_BP_ID};
723  std::string key;
724  ParamType param = ParamType::MAX; // MAX = No param.
725  std::vector<Node> children;
726 
727  bool IsSimpleNode() const
728  {
729  return !rule_index &&
730  blueprint_index == INVALID_BP_ID &&
731  children.size() < 2 &&
732  param == ParamType::MAX &&
733  std::all_of(std::begin(children), std::end(children), [](const Node& x) {
734  return x.param == ParamType::MAX;
735  });
736  }
737 
738  Node& add_child_node()
739  {
740  children.emplace_back();
741  return children.back();
742  }
743  };
744 
745 
746  Trie()
747  {}
748 
749  /// Check whether or not the trie is empty.
750  bool is_empty()
751  {
752  return head_.children.empty();
753  }
754 
755  void optimize()
756  {
757  for (auto& child : head_.children)
758  {
759  optimizeNode(child);
760  }
761  }
762 
763 
764  private:
765  void optimizeNode(Node& node)
766  {
767  if (node.children.empty())
768  return;
769  if (node.IsSimpleNode())
770  {
771  auto children_temp = std::move(node.children);
772  auto& child_temp = children_temp[0];
773  node.key += child_temp.key;
774  node.rule_index = child_temp.rule_index;
775  node.blueprint_index = child_temp.blueprint_index;
776  node.children = std::move(child_temp.children);
777  optimizeNode(node);
778  }
779  else
780  {
781  for (auto& child : node.children)
782  {
783  optimizeNode(child);
784  }
785  }
786  }
787 
788  void debug_node_print(const Node& node, int level)
789  {
790  if (node.param != ParamType::MAX)
791  {
792  switch (node.param)
793  {
794  case ParamType::INT:
795  CROW_LOG_DEBUG << std::string(3 * level, ' ') << "└➝ "
796  << "<int>";
797  break;
798  case ParamType::UINT:
799  CROW_LOG_DEBUG << std::string(3 * level, ' ') << "└➝ "
800  << "<uint>";
801  break;
802  case ParamType::DOUBLE:
803  CROW_LOG_DEBUG << std::string(3 * level, ' ') << "└➝ "
804  << "<double>";
805  break;
806  case ParamType::STRING:
807  CROW_LOG_DEBUG << std::string(3 * level, ' ') << "└➝ "
808  << "<string>";
809  break;
810  case ParamType::PATH:
811  CROW_LOG_DEBUG << std::string(3 * level, ' ') << "└➝ "
812  << "<path>";
813  break;
814  default:
815  CROW_LOG_DEBUG << std::string(3 * level, ' ') << "└➝ "
816  << "<ERROR>";
817  break;
818  }
819  }
820  else
821  CROW_LOG_DEBUG << std::string(3 * level, ' ') << "└➝ " << node.key;
822 
823  for (const auto& child : node.children)
824  {
825  debug_node_print(child, level + 1);
826  }
827  }
828 
829  public:
830  void debug_print()
831  {
832  CROW_LOG_DEBUG << "└➙ ROOT";
833  for (const auto& child : head_.children)
834  debug_node_print(child, 1);
835  }
836 
837  void validate()
838  {
839  if (!head_.IsSimpleNode())
840  throw std::runtime_error("Internal error: Trie header should be simple!");
841  optimize();
842  }
843 
844  //Rule_index, Blueprint_index, routing_params
845  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
846  {
847  //start params as an empty struct
848  routing_params empty;
849  if (params == nullptr)
850  params = &empty;
851  //same for blueprint vector
852  std::vector<uint16_t> MT;
853  if (blueprints == nullptr)
854  blueprints = &MT;
855 
856  uint16_t found{}; //The rule index to be found
857  std::vector<uint16_t> found_BP; //The Blueprint indices to be found
858  routing_params match_params; //supposedly the final matched parameters
859 
860  auto update_found = [&found, &found_BP, &match_params](routing_handle_result& ret) {
861  found_BP = std::move(ret.blueprint_indices);
862  if (ret.rule_index && (!found || found > ret.rule_index))
863  {
864  found = ret.rule_index;
865  match_params = std::move(ret.r_params);
866  }
867  };
868 
869  //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
870  if (pos == req_url.size())
871  {
872  found_BP = std::move(*blueprints);
873  return routing_handle_result{node.rule_index, *blueprints, *params};
874  }
875 
876  bool found_fragment = false;
877 
878  for (const auto& child : node.children)
879  {
880  if (child.param != ParamType::MAX)
881  {
882  if (child.param == ParamType::INT)
883  {
884  char c = req_url[pos];
885  if ((c >= '0' && c <= '9') || c == '+' || c == '-')
886  {
887  char* eptr;
888  errno = 0;
889  long long int value = strtoll(req_url.data() + pos, &eptr, 10);
890  if (errno != ERANGE && eptr != req_url.data() + pos)
891  {
892  found_fragment = true;
893  params->int_params.push_back(value);
894  if (child.blueprint_index != INVALID_BP_ID) blueprints->push_back(child.blueprint_index);
895  auto ret = find(req_url, child, eptr - req_url.data(), params, blueprints);
896  update_found(ret);
897  params->int_params.pop_back();
898  if (!blueprints->empty()) blueprints->pop_back();
899  }
900  }
901  }
902 
903  else if (child.param == ParamType::UINT)
904  {
905  char c = req_url[pos];
906  if ((c >= '0' && c <= '9') || c == '+')
907  {
908  char* eptr;
909  errno = 0;
910  unsigned long long int value = strtoull(req_url.data() + pos, &eptr, 10);
911  if (errno != ERANGE && eptr != req_url.data() + pos)
912  {
913  found_fragment = true;
914  params->uint_params.push_back(value);
915  if (child.blueprint_index != INVALID_BP_ID) blueprints->push_back(child.blueprint_index);
916  auto ret = find(req_url, child, eptr - req_url.data(), params, blueprints);
917  update_found(ret);
918  params->uint_params.pop_back();
919  if (!blueprints->empty()) blueprints->pop_back();
920  }
921  }
922  }
923 
924  else if (child.param == ParamType::DOUBLE)
925  {
926  char c = req_url[pos];
927  if ((c >= '0' && c <= '9') || c == '+' || c == '-' || c == '.')
928  {
929  char* eptr;
930  errno = 0;
931  double value = strtod(req_url.data() + pos, &eptr);
932  if (errno != ERANGE && eptr != req_url.data() + pos)
933  {
934  found_fragment = true;
935  params->double_params.push_back(value);
936  if (child.blueprint_index != INVALID_BP_ID) blueprints->push_back(child.blueprint_index);
937  auto ret = find(req_url, child, eptr - req_url.data(), params, blueprints);
938  update_found(ret);
939  params->double_params.pop_back();
940  if (!blueprints->empty()) blueprints->pop_back();
941  }
942  }
943  }
944 
945  else if (child.param == ParamType::STRING)
946  {
947  size_t epos = pos;
948  for (; epos < req_url.size(); epos++)
949  {
950  if (req_url[epos] == '/')
951  break;
952  }
953 
954  if (epos != pos)
955  {
956  found_fragment = true;
957  params->string_params.push_back(req_url.substr(pos, epos - pos));
958  if (child.blueprint_index != INVALID_BP_ID) blueprints->push_back(child.blueprint_index);
959  auto ret = find(req_url, child, epos, params, blueprints);
960  update_found(ret);
961  params->string_params.pop_back();
962  if (!blueprints->empty()) blueprints->pop_back();
963  }
964  }
965 
966  else if (child.param == ParamType::PATH)
967  {
968  size_t epos = req_url.size();
969 
970  if (epos != pos)
971  {
972  found_fragment = true;
973  params->string_params.push_back(req_url.substr(pos, epos - pos));
974  if (child.blueprint_index != INVALID_BP_ID) blueprints->push_back(child.blueprint_index);
975  auto ret = find(req_url, child, epos, params, blueprints);
976  update_found(ret);
977  params->string_params.pop_back();
978  if (!blueprints->empty()) blueprints->pop_back();
979  }
980  }
981  }
982 
983  else
984  {
985  const std::string& fragment = child.key;
986  if (req_url.compare(pos, fragment.size(), fragment) == 0)
987  {
988  found_fragment = true;
989  if (child.blueprint_index != INVALID_BP_ID) blueprints->push_back(child.blueprint_index);
990  auto ret = find(req_url, child, pos + fragment.size(), params, blueprints);
991  update_found(ret);
992  if (!blueprints->empty()) blueprints->pop_back();
993  }
994  }
995  }
996 
997  if (!found_fragment)
998  found_BP = std::move(*blueprints);
999 
1000  return routing_handle_result{found, found_BP, match_params}; //Called after all the recursions have been done
1001  }
1002 
1003  routing_handle_result find(const std::string& req_url) const
1004  {
1005  return find(req_url, head_);
1006  }
1007 
1008  //This functions assumes any blueprint info passed is valid
1009  void add(const std::string& url, uint16_t rule_index, unsigned bp_prefix_length = 0, uint16_t blueprint_index = INVALID_BP_ID)
1010  {
1011  auto idx = &head_;
1012 
1013  bool has_blueprint = bp_prefix_length != 0 && blueprint_index != INVALID_BP_ID;
1014 
1015  for (unsigned i = 0; i < url.size(); i++)
1016  {
1017  char c = url[i];
1018  if (c == '<')
1019  {
1020  static struct ParamTraits
1021  {
1022  ParamType type;
1023  std::string name;
1024  } paramTraits[] =
1025  {
1026  {ParamType::INT, "<int>"},
1027  {ParamType::UINT, "<uint>"},
1028  {ParamType::DOUBLE, "<float>"},
1029  {ParamType::DOUBLE, "<double>"},
1030  {ParamType::STRING, "<str>"},
1031  {ParamType::STRING, "<string>"},
1032  {ParamType::PATH, "<path>"},
1033  };
1034 
1035  for (const auto& x : paramTraits)
1036  {
1037  if (url.compare(i, x.name.size(), x.name) == 0)
1038  {
1039  bool found = false;
1040  for (auto& child : idx->children)
1041  {
1042  if (child.param == x.type)
1043  {
1044  idx = &child;
1045  i += x.name.size();
1046  found = true;
1047  break;
1048  }
1049  }
1050  if (found)
1051  break;
1052 
1053  auto new_node_idx = &idx->add_child_node();
1054  new_node_idx->param = x.type;
1055  idx = new_node_idx;
1056  i += x.name.size();
1057  break;
1058  }
1059  }
1060 
1061  i--;
1062  }
1063  else
1064  {
1065  //This part assumes the tree is unoptimized (every node has a max 1 character key)
1066  bool piece_found = false;
1067  for (auto& child : idx->children)
1068  {
1069  if (child.key[0] == c)
1070  {
1071  idx = &child;
1072  piece_found = true;
1073  break;
1074  }
1075  }
1076  if (!piece_found)
1077  {
1078  auto new_node_idx = &idx->add_child_node();
1079  new_node_idx->key = c;
1080  //The assumption here is that you'd only need to add a blueprint index if the tree didn't have the BP prefix.
1081  if (has_blueprint && i == bp_prefix_length)
1082  new_node_idx->blueprint_index = blueprint_index;
1083  idx = new_node_idx;
1084  }
1085  }
1086  }
1087 
1088  //check if the last node already has a value (exact url already in Trie)
1089  if (idx->rule_index)
1090  throw std::runtime_error("handler already exists for " + url);
1091  idx->rule_index = rule_index;
1092  }
1093 
1094  private:
1095  Node head_;
1096  };
1097 
1098  /// A blueprint can be considered a smaller section of a Crow app, specifically where the router is conecerned.
1099 
1100  ///
1101  /// You can use blueprints to assign a common prefix to rules' prefix, set custom static and template folders, and set a custom catchall route.
1102  /// You can also assign nest blueprints for maximum Compartmentalization.
1104  {
1105  public:
1106  Blueprint(const std::string& prefix):
1107  prefix_(prefix){};
1108 
1109  Blueprint(const std::string& prefix, const std::string& static_dir):
1110  prefix_(prefix), static_dir_(static_dir){};
1111 
1112  Blueprint(const std::string& prefix, const std::string& static_dir, const std::string& templates_dir):
1113  prefix_(prefix), static_dir_(static_dir), templates_dir_(templates_dir){};
1114 
1115  /*
1116  Blueprint(Blueprint& other)
1117  {
1118  prefix_ = std::move(other.prefix_);
1119  all_rules_ = std::move(other.all_rules_);
1120  }
1121 
1122  Blueprint(const Blueprint& other)
1123  {
1124  prefix_ = other.prefix_;
1125  all_rules_ = other.all_rules_;
1126  }
1127 */
1128  Blueprint(Blueprint&& value)
1129  {
1130  *this = std::move(value);
1131  }
1132 
1133  Blueprint& operator=(const Blueprint& value) = delete;
1134 
1135  Blueprint& operator=(Blueprint&& value) noexcept
1136  {
1137  prefix_ = std::move(value.prefix_);
1138  static_dir_ = std::move(value.static_dir_);
1139  templates_dir_ = std::move(value.templates_dir_);
1140  all_rules_ = std::move(value.all_rules_);
1141  catchall_rule_ = std::move(value.catchall_rule_);
1142  blueprints_ = std::move(value.blueprints_);
1143  mw_indices_ = std::move(value.mw_indices_);
1144  return *this;
1145  }
1146 
1147  bool operator==(const Blueprint& value)
1148  {
1149  return value.prefix() == prefix_;
1150  }
1151 
1152  bool operator!=(const Blueprint& value)
1153  {
1154  return value.prefix() != prefix_;
1155  }
1156 
1157  std::string prefix() const
1158  {
1159  return prefix_;
1160  }
1161 
1162  std::string static_dir() const
1163  {
1164  return static_dir_;
1165  }
1166 
1167  void set_added() {
1168  added_ = true;
1169  }
1170 
1171  bool is_added() {
1172  return added_;
1173  }
1174 
1175  DynamicRule& new_rule_dynamic(const std::string& rule)
1176  {
1177  std::string new_rule = '/' + prefix_ + rule;
1178  auto ruleObject = new DynamicRule(std::move(new_rule));
1179  ruleObject->custom_templates_base = templates_dir_;
1180  all_rules_.emplace_back(ruleObject);
1181 
1182  return *ruleObject;
1183  }
1184 
1185  template<uint64_t N>
1186  typename black_magic::arguments<N>::type::template rebind<TaggedRule>& new_rule_tagged(const std::string& rule)
1187  {
1188  std::string new_rule = '/' + prefix_ + rule;
1189  using RuleT = typename black_magic::arguments<N>::type::template rebind<TaggedRule>;
1190 
1191  auto ruleObject = new RuleT(std::move(new_rule));
1192  ruleObject->custom_templates_base = templates_dir_;
1193  all_rules_.emplace_back(ruleObject);
1194 
1195  return *ruleObject;
1196  }
1197 
1198  void register_blueprint(Blueprint& blueprint)
1199  {
1200  if (blueprints_.empty() || std::find(blueprints_.begin(), blueprints_.end(), &blueprint) == blueprints_.end())
1201  {
1202  apply_blueprint(blueprint);
1203  blueprints_.emplace_back(&blueprint);
1204  }
1205  else
1206  throw std::runtime_error("blueprint \"" + blueprint.prefix_ + "\" already exists in blueprint \"" + prefix_ + '\"');
1207  }
1208 
1209 
1210  CatchallRule& catchall_rule()
1211  {
1212  return catchall_rule_;
1213  }
1214 
1215  template<typename App, typename... Middlewares>
1216  void middlewares()
1217  {
1218  mw_indices_.push<App, Middlewares...>();
1219  }
1220 
1221  private:
1222  void apply_blueprint(Blueprint& blueprint)
1223  {
1224 
1225  blueprint.prefix_ = prefix_ + '/' + blueprint.prefix_;
1226  blueprint.static_dir_ = static_dir_ + '/' + blueprint.static_dir_;
1227  blueprint.templates_dir_ = templates_dir_ + '/' + blueprint.templates_dir_;
1228  for (auto& rule : blueprint.all_rules_)
1229  {
1230  std::string new_rule = '/' + prefix_ + rule->rule_;
1231  rule->rule_ = new_rule;
1232  }
1233  for (Blueprint* bp_child : blueprint.blueprints_)
1234  {
1235  Blueprint& bp_ref = *bp_child;
1236  apply_blueprint(bp_ref);
1237  }
1238  }
1239 
1240  std::string prefix_;
1241  std::string static_dir_;
1242  std::string templates_dir_;
1243  std::vector<std::unique_ptr<BaseRule>> all_rules_;
1244  CatchallRule catchall_rule_;
1245  std::vector<Blueprint*> blueprints_;
1246  detail::middleware_indices mw_indices_;
1247  bool added_{false};
1248 
1249  friend class Router;
1250  };
1251 
1252  /// Handles matching requests to existing rules and upgrade requests.
1253  class Router
1254  {
1255  public:
1256  Router()
1257  {}
1258 
1259  DynamicRule& new_rule_dynamic(const std::string& rule)
1260  {
1261  auto ruleObject = new DynamicRule(rule);
1262  all_rules_.emplace_back(ruleObject);
1263 
1264  return *ruleObject;
1265  }
1266 
1267  template<uint64_t N>
1268  typename black_magic::arguments<N>::type::template rebind<TaggedRule>& new_rule_tagged(const std::string& rule)
1269  {
1270  using RuleT = typename black_magic::arguments<N>::type::template rebind<TaggedRule>;
1271 
1272  auto ruleObject = new RuleT(rule);
1273  all_rules_.emplace_back(ruleObject);
1274 
1275  return *ruleObject;
1276  }
1277 
1278  CatchallRule& catchall_rule()
1279  {
1280  return catchall_rule_;
1281  }
1282 
1283  void internal_add_rule_object(const std::string& rule, BaseRule* ruleObject)
1284  {
1285  internal_add_rule_object(rule, ruleObject, INVALID_BP_ID, blueprints_);
1286  }
1287 
1288  void internal_add_rule_object(const std::string& rule, BaseRule* ruleObject, const uint16_t& BP_index, std::vector<Blueprint*>& blueprints)
1289  {
1290  bool has_trailing_slash = false;
1291  std::string rule_without_trailing_slash;
1292  if (rule.size() > 1 && rule.back() == '/')
1293  {
1294  has_trailing_slash = true;
1295  rule_without_trailing_slash = rule;
1296  rule_without_trailing_slash.pop_back();
1297  }
1298 
1299  ruleObject->mw_indices_.pack();
1300 
1301  ruleObject->foreach_method([&](int method) {
1302  per_methods_[method].rules.emplace_back(ruleObject);
1303  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);
1304 
1305  // directory case:
1306  // request to '/about' url matches '/about/' rule
1307  if (has_trailing_slash)
1308  {
1309  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);
1310  }
1311  });
1312 
1313  ruleObject->set_added();
1314  }
1315 
1316  void register_blueprint(Blueprint& blueprint)
1317  {
1318  if (std::find(blueprints_.begin(), blueprints_.end(), &blueprint) == blueprints_.end())
1319  {
1320  blueprints_.emplace_back(&blueprint);
1321  }
1322  else
1323  throw std::runtime_error("blueprint \"" + blueprint.prefix_ + "\" already exists in router");
1324  }
1325 
1326  void get_recursive_child_methods(Blueprint* blueprint, std::vector<HTTPMethod>& methods)
1327  {
1328  //we only need to deal with children if the blueprint has absolutely no methods (meaning its index won't be added to the trie)
1329  if (blueprint->static_dir_.empty() && blueprint->all_rules_.empty())
1330  {
1331  for (Blueprint* bp : blueprint->blueprints_)
1332  {
1333  get_recursive_child_methods(bp, methods);
1334  }
1335  }
1336  else if (!blueprint->static_dir_.empty())
1337  methods.emplace_back(HTTPMethod::Get);
1338  for (auto& rule : blueprint->all_rules_)
1339  {
1340  rule->foreach_method([&methods](unsigned method) {
1341  HTTPMethod method_final = static_cast<HTTPMethod>(method);
1342  if (std::find(methods.begin(), methods.end(), method_final) == methods.end())
1343  methods.emplace_back(method_final);
1344  });
1345  }
1346  }
1347 
1348  void validate_bp() {
1349  //Take all the routes from the registered blueprints and add them to `all_rules_` to be processed.
1350  detail::middleware_indices blueprint_mw;
1351  validate_bp(blueprints_, blueprint_mw);
1352  }
1353 
1354  void validate_bp(std::vector<Blueprint*> blueprints, detail::middleware_indices& current_mw)
1355  {
1356  for (unsigned i = 0; i < blueprints.size(); i++)
1357  {
1358  Blueprint* blueprint = blueprints[i];
1359 
1360  if (blueprint->is_added()) continue;
1361 
1362  if (blueprint->static_dir_ == "" && blueprint->all_rules_.empty())
1363  {
1364  std::vector<HTTPMethod> methods;
1365  get_recursive_child_methods(blueprint, methods);
1366  for (HTTPMethod x : methods)
1367  {
1368  int i = static_cast<int>(x);
1369  per_methods_[i].trie.add(blueprint->prefix(), 0, blueprint->prefix().length(), i);
1370  }
1371  }
1372 
1373  current_mw.merge_back(blueprint->mw_indices_);
1374  for (auto& rule : blueprint->all_rules_)
1375  {
1376  if (rule && !rule->is_added())
1377  {
1378  auto upgraded = rule->upgrade();
1379  if (upgraded)
1380  rule = std::move(upgraded);
1381  rule->validate();
1382  rule->mw_indices_.merge_front(current_mw);
1383  internal_add_rule_object(rule->rule(), rule.get(), i, blueprints);
1384  }
1385  }
1386  validate_bp(blueprint->blueprints_, current_mw);
1387  current_mw.pop_back(blueprint->mw_indices_);
1388  blueprint->set_added();
1389  }
1390  }
1391 
1392  void validate()
1393  {
1394  for (auto& rule : all_rules_)
1395  {
1396  if (rule && !rule->is_added())
1397  {
1398  auto upgraded = rule->upgrade();
1399  if (upgraded)
1400  rule = std::move(upgraded);
1401  rule->validate();
1402  internal_add_rule_object(rule->rule(), rule.get());
1403  }
1404  }
1405  for (auto& per_method : per_methods_)
1406  {
1407  per_method.trie.validate();
1408  }
1409  }
1410 
1411  // TODO maybe add actual_method
1412  template<typename Adaptor>
1413  void handle_upgrade(const request& req, response& res, Adaptor&& adaptor)
1414  {
1415  if (req.method >= HTTPMethod::InternalMethodCount)
1416  return;
1417 
1418  auto& per_method = per_methods_[static_cast<int>(req.method)];
1419  auto& rules = per_method.rules;
1420  unsigned rule_index = per_method.trie.find(req.url).rule_index;
1421 
1422  if (!rule_index)
1423  {
1424  for (auto& per_method : per_methods_)
1425  {
1426  if (per_method.trie.find(req.url).rule_index)
1427  {
1428  CROW_LOG_DEBUG << "Cannot match method " << req.url << " " << method_name(req.method);
1429  res = response(405);
1430  res.end();
1431  return;
1432  }
1433  }
1434 
1435  CROW_LOG_INFO << "Cannot match rules " << req.url;
1436  res = response(404);
1437  res.end();
1438  return;
1439  }
1440 
1441  if (rule_index >= rules.size())
1442  throw std::runtime_error("Trie internal structure corrupted!");
1443 
1444  if (rule_index == RULE_SPECIAL_REDIRECT_SLASH)
1445  {
1446  CROW_LOG_INFO << "Redirecting to a url with trailing slash: " << req.url;
1447  res = response(301);
1448 
1449  // TODO(ipkn) absolute url building
1450  if (req.get_header_value("Host").empty())
1451  {
1452  res.add_header("Location", req.url + "/");
1453  }
1454  else
1455  {
1456  res.add_header("Location", "http://" + req.get_header_value("Host") + req.url + "/");
1457  }
1458  res.end();
1459  return;
1460  }
1461 
1462  CROW_LOG_DEBUG << "Matched rule (upgrade) '" << rules[rule_index]->rule_ << "' " << static_cast<uint32_t>(req.method) << " / " << rules[rule_index]->get_methods();
1463 
1464  try
1465  {
1466  rules[rule_index]->handle_upgrade(req, res, std::move(adaptor));
1467  }
1468  catch (...)
1469  {
1470  exception_handler_(res);
1471  res.end();
1472  return;
1473  }
1474  }
1475 
1476  void get_found_bp(std::vector<uint16_t>& bp_i, std::vector<Blueprint*>& blueprints, std::vector<Blueprint*>& found_bps, uint16_t index = 0)
1477  {
1478  // This statement makes 3 assertions:
1479  // 1. The index is above 0.
1480  // 2. The index does not lie outside the given blueprint list.
1481  // 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).
1482  //
1483  // 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".
1484  //
1485  // 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
1486  auto verify_prefix = [&bp_i, &index, &blueprints, &found_bps]() {
1487  return index > 0 &&
1488  bp_i[index] < blueprints.size() &&
1489  blueprints[bp_i[index]]->prefix().substr(0, found_bps[index - 1]->prefix().length() + 1).compare(std::string(found_bps[index - 1]->prefix() + '/')) == 0;
1490  };
1491  if (index < bp_i.size())
1492  {
1493 
1494  if (verify_prefix())
1495  {
1496  found_bps.push_back(blueprints[bp_i[index]]);
1497  get_found_bp(bp_i, found_bps.back()->blueprints_, found_bps, ++index);
1498  }
1499  else
1500  {
1501  if (found_bps.size() < 2)
1502  {
1503  found_bps.clear();
1504  found_bps.push_back(blueprints_[bp_i[index]]);
1505  }
1506  else
1507  {
1508  found_bps.pop_back();
1509  Blueprint* last_element = found_bps.back();
1510  found_bps.push_back(last_element->blueprints_[bp_i[index]]);
1511  }
1512  get_found_bp(bp_i, found_bps.back()->blueprints_, found_bps, ++index);
1513  }
1514  }
1515  }
1516 
1517  /// 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.
1518  std::string get_error(unsigned short code, routing_handle_result& found, const request& req, response& res)
1519  {
1520  res.code = code;
1521  std::vector<Blueprint*> bps_found;
1522  get_found_bp(found.blueprint_indices, blueprints_, bps_found);
1523  for (int i = bps_found.size() - 1; i > 0; i--)
1524  {
1525  std::vector<uint16_t> bpi = found.blueprint_indices;
1526  if (bps_found[i]->catchall_rule().has_handler())
1527  {
1528  try
1529  {
1530  bps_found[i]->catchall_rule().handler_(req, res);
1531  }
1532  catch (...)
1533  {
1534  exception_handler_(res);
1535  }
1536 #ifdef CROW_ENABLE_DEBUG
1537  return std::string("Redirected to Blueprint \"" + bps_found[i]->prefix() + "\" Catchall rule");
1538 #else
1539  return std::string();
1540 #endif
1541  }
1542  }
1543  if (catchall_rule_.has_handler())
1544  {
1545  try
1546  {
1547  catchall_rule_.handler_(req, res);
1548  }
1549  catch (...)
1550  {
1551  exception_handler_(res);
1552  }
1553 #ifdef CROW_ENABLE_DEBUG
1554  return std::string("Redirected to global Catchall rule");
1555 #else
1556  return std::string();
1557 #endif
1558  }
1559  return std::string();
1560  }
1561 
1562  std::unique_ptr<routing_handle_result> handle_initial(request& req, response& res)
1563  {
1564  HTTPMethod method_actual = req.method;
1565 
1566  std::unique_ptr<routing_handle_result> found{
1568  0,
1569  std::vector<uint16_t>(),
1570  routing_params(),
1571  HTTPMethod::InternalMethodCount)}; // This is always returned to avoid a null pointer dereference.
1572 
1573  // NOTE(EDev): This most likely will never run since the parser should handle this situation and close the connection before it gets here.
1574  if (CROW_UNLIKELY(req.method >= HTTPMethod::InternalMethodCount))
1575  return found;
1576  else if (req.method == HTTPMethod::Head)
1577  {
1578  *found = per_methods_[static_cast<int>(method_actual)].trie.find(req.url);
1579  // support HEAD requests using GET if not defined as method for the requested URL
1580  if (!found->rule_index)
1581  {
1582  method_actual = HTTPMethod::Get;
1583  *found = per_methods_[static_cast<int>(method_actual)].trie.find(req.url);
1584  if (!found->rule_index) // If a route is still not found, return a 404 without executing the rest of the HEAD specific code.
1585  {
1586  CROW_LOG_DEBUG << "Cannot match rules " << req.url;
1587  res = response(404); //TODO(EDev): Should this redirect to catchall?
1588  res.end();
1589  return found;
1590  }
1591  }
1592 
1593  res.skip_body = true;
1594  found->method = method_actual;
1595  return found;
1596  }
1597  else if (req.method == HTTPMethod::Options)
1598  {
1599  std::string allow = "OPTIONS, HEAD, ";
1600 
1601  if (req.url == "/*")
1602  {
1603  for (int i = 0; i < static_cast<int>(HTTPMethod::InternalMethodCount); i++)
1604  {
1605  if (static_cast<int>(HTTPMethod::Head) == i)
1606  continue; // HEAD is always allowed
1607 
1608  if (!per_methods_[i].trie.is_empty())
1609  {
1610  allow += method_name(static_cast<HTTPMethod>(i)) + ", ";
1611  }
1612  }
1613  allow = allow.substr(0, allow.size() - 2);
1614  res = response(204);
1615  res.set_header("Allow", allow);
1616  res.end();
1617  found->method = method_actual;
1618  return found;
1619  }
1620  else
1621  {
1622  bool rules_matched = false;
1623  for (int i = 0; i < static_cast<int>(HTTPMethod::InternalMethodCount); i++)
1624  {
1625  if (per_methods_[i].trie.find(req.url).rule_index)
1626  {
1627  rules_matched = true;
1628 
1629  if (static_cast<int>(HTTPMethod::Head) == i)
1630  continue; // HEAD is always allowed
1631 
1632  allow += method_name(static_cast<HTTPMethod>(i)) + ", ";
1633  }
1634  }
1635  if (rules_matched)
1636  {
1637  allow = allow.substr(0, allow.size() - 2);
1638  res = response(204);
1639  res.set_header("Allow", allow);
1640  res.end();
1641  found->method = method_actual;
1642  return found;
1643  }
1644  else
1645  {
1646  CROW_LOG_DEBUG << "Cannot match rules " << req.url;
1647  res = response(404); //TODO(EDev): Should this redirect to catchall?
1648  res.end();
1649  return found;
1650  }
1651  }
1652  }
1653  else // Every request that isn't a HEAD or OPTIONS request
1654  {
1655  *found = per_methods_[static_cast<int>(method_actual)].trie.find(req.url);
1656  // 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
1657  if (!found->rule_index)
1658  {
1659  for (auto& per_method : per_methods_)
1660  {
1661  if (per_method.trie.find(req.url).rule_index) //Route found, but in another method
1662  {
1663  const std::string error_message(get_error(405, *found, req, res));
1664  CROW_LOG_DEBUG << "Cannot match method " << req.url << " " << method_name(method_actual) << ". " << error_message;
1665  res.end();
1666  return found;
1667  }
1668  }
1669  //Route does not exist anywhere
1670 
1671  const std::string error_message(get_error(404, *found, req, res));
1672  CROW_LOG_DEBUG << "Cannot match rules " << req.url << ". " << error_message;
1673  res.end();
1674  return found;
1675  }
1676 
1677  found->method = method_actual;
1678  return found;
1679  }
1680  }
1681 
1682  template<typename App>
1683  void handle(request& req, response& res, routing_handle_result found)
1684  {
1685  HTTPMethod method_actual = found.method;
1686  auto& rules = per_methods_[static_cast<int>(method_actual)].rules;
1687  unsigned rule_index = found.rule_index;
1688 
1689  if (rule_index >= rules.size())
1690  throw std::runtime_error("Trie internal structure corrupted!");
1691 
1692  if (rule_index == RULE_SPECIAL_REDIRECT_SLASH)
1693  {
1694  CROW_LOG_INFO << "Redirecting to a url with trailing slash: " << req.url;
1695  res = response(301);
1696 
1697  // TODO(ipkn) absolute url building
1698  if (req.get_header_value("Host").empty())
1699  {
1700  res.add_header("Location", req.url + "/");
1701  }
1702  else
1703  {
1704  res.add_header("Location", "http://" + req.get_header_value("Host") + req.url + "/");
1705  }
1706  res.end();
1707  return;
1708  }
1709 
1710  CROW_LOG_DEBUG << "Matched rule '" << rules[rule_index]->rule_ << "' " << static_cast<uint32_t>(req.method) << " / " << rules[rule_index]->get_methods();
1711 
1712  try
1713  {
1714  auto& rule = rules[rule_index];
1715  handle_rule<App>(rule, req, res, found.r_params);
1716  }
1717  catch (...)
1718  {
1719  exception_handler_(res);
1720  res.end();
1721  return;
1722  }
1723  }
1724 
1725  template<typename App>
1726  typename std::enable_if<std::tuple_size<typename App::mw_container_t>::value != 0, void>::type
1727  handle_rule(BaseRule* rule, crow::request& req, crow::response& res, const crow::routing_params& rp)
1728  {
1729  if (!rule->mw_indices_.empty())
1730  {
1731  auto& ctx = *reinterpret_cast<typename App::context_t*>(req.middleware_context);
1732  auto& container = *reinterpret_cast<typename App::mw_container_t*>(req.middleware_container);
1733  detail::middleware_call_criteria_dynamic<false> crit_fwd(rule->mw_indices_.indices());
1734 
1735  auto glob_completion_handler = std::move(res.complete_request_handler_);
1736  res.complete_request_handler_ = [] {};
1737 
1738  detail::middleware_call_helper<decltype(crit_fwd),
1739  0, typename App::context_t, typename App::mw_container_t>(crit_fwd, container, req, res, ctx);
1740 
1741  if (res.completed_)
1742  {
1743  glob_completion_handler();
1744  return;
1745  }
1746 
1747  res.complete_request_handler_ = [&rule, &ctx, &container, &req, &res, glob_completion_handler] {
1748  detail::middleware_call_criteria_dynamic<true> crit_bwd(rule->mw_indices_.indices());
1749 
1750  detail::after_handlers_call_helper<
1751  decltype(crit_bwd),
1752  std::tuple_size<typename App::mw_container_t>::value - 1,
1753  typename App::context_t,
1754  typename App::mw_container_t>(crit_bwd, container, ctx, req, res);
1755  glob_completion_handler();
1756  };
1757  }
1758  rule->handle(req, res, rp);
1759  }
1760 
1761  template<typename App>
1762  typename std::enable_if<std::tuple_size<typename App::mw_container_t>::value == 0, void>::type
1763  handle_rule(BaseRule* rule, crow::request& req, crow::response& res, const crow::routing_params& rp)
1764  {
1765  rule->handle(req, res, rp);
1766  }
1767 
1768  void debug_print()
1769  {
1770  for (int i = 0; i < static_cast<int>(HTTPMethod::InternalMethodCount); i++)
1771  {
1772  Trie& trie_ = per_methods_[i].trie;
1773  if (!trie_.is_empty())
1774  {
1775  CROW_LOG_DEBUG << method_name(static_cast<HTTPMethod>(i));
1776  trie_.debug_print();
1777  }
1778  }
1779  }
1780 
1781  std::vector<Blueprint*>& blueprints()
1782  {
1783  return blueprints_;
1784  }
1785 
1786  std::function<void(crow::response&)>& exception_handler()
1787  {
1788  return exception_handler_;
1789  }
1790 
1791  static void default_exception_handler(response& res)
1792  {
1793  // any uncaught exceptions become 500s
1794  res = response(500);
1795 
1796  try
1797  {
1798  throw;
1799  }
1800  catch (const std::exception& e)
1801  {
1802  CROW_LOG_ERROR << "An uncaught exception occurred: " << e.what();
1803  }
1804  catch (...)
1805  {
1806  CROW_LOG_ERROR << "An uncaught exception occurred. The type was unknown so no information was available.";
1807  }
1808  }
1809 
1810  private:
1811  CatchallRule catchall_rule_;
1812 
1813  struct PerMethod
1814  {
1815  std::vector<BaseRule*> rules;
1816  Trie trie;
1817 
1818  // rule index 0, 1 has special meaning; preallocate it to avoid duplication.
1819  PerMethod():
1820  rules(2) {}
1821  };
1822  std::array<PerMethod, static_cast<int>(HTTPMethod::InternalMethodCount)> per_methods_;
1823  std::vector<std::unique_ptr<BaseRule>> all_rules_;
1824  std::vector<Blueprint*> blueprints_;
1825  std::function<void(crow::response&)> exception_handler_ = &default_exception_handler;
1826  };
1827 } // 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 conec...
Definition: routing.h:1104
Definition: routing.h:342
The main server application.
Definition: app.h:50
self_t & websocket_max_payload(uint64_t max_payload)
Set the default max payload size for websockets.
Definition: app.h:128
A rule that can change its parameters during runtime.
Definition: routing.h:575
Handles matching requests to existing rules and upgrade requests.
Definition: routing.h:1254
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:1518
Default rule created when CROW_ROUTE is called.
Definition: routing.h:652
A search tree.
Definition: routing.h:716
bool is_empty()
Check whether or not the trie is empty.
Definition: routing.h:750
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:73
Allows the user to assign parameters using functions.
Definition: routing.h:533
self_t & middlewares()
Enable local middleware for this handler.
Definition: routing.h:566
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:719
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:30