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