Crow  0.3
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 <boost/lexical_cast.hpp>
9 #include <vector>
10 
11 #include "crow/common.h"
12 #include "crow/http_response.h"
13 #include "crow/http_request.h"
14 #include "crow/utility.h"
15 #include "crow/logging.h"
16 #include "crow/websocket.h"
17 
18 namespace crow
19 {
20  /// A base class for all rules.
21 
22  /// Used to provide a common interface for code dealing with different types of rules.
23  /// A Rule provides a URL, allowed HTTP methods, and handlers.
24  class BaseRule
25  {
26  public:
27  BaseRule(std::string rule)
28  : rule_(std::move(rule))
29  {
30  }
31 
32  virtual ~BaseRule()
33  {
34  }
35 
36  virtual void validate() = 0;
37  std::unique_ptr<BaseRule> upgrade()
38  {
39  if (rule_to_upgrade_)
40  return std::move(rule_to_upgrade_);
41  return {};
42  }
43 
44  virtual void handle(const request&, response&, const routing_params&) = 0;
45  virtual void handle_upgrade(const request&, response& res, SocketAdaptor&&)
46  {
47  res = response(404);
48  res.end();
49  }
50 #ifdef CROW_ENABLE_SSL
51  virtual void handle_upgrade(const request&, response& res, SSLAdaptor&&)
52  {
53  res = response(404);
54  res.end();
55  }
56 #endif
57 
58  uint32_t get_methods()
59  {
60  return methods_;
61  }
62 
63  template <typename F>
64  void foreach_method(F f)
65  {
66  for(uint32_t method = 0, method_bit = 1; method < static_cast<uint32_t>(HTTPMethod::InternalMethodCount); method++, method_bit<<=1)
67  {
68  if (methods_ & method_bit)
69  f(method);
70  }
71  }
72 
73  const std::string& rule() { return rule_; }
74 
75  protected:
76  uint32_t methods_{1<<static_cast<int>(HTTPMethod::Get)};
77 
78  std::string rule_;
79  std::string name_;
80 
81  std::unique_ptr<BaseRule> rule_to_upgrade_;
82 
83  friend class Router;
84  template <typename T>
85  friend struct RuleParameterTraits;
86  };
87 
88 
89  namespace detail
90  {
91  namespace routing_handler_call_helper
92  {
93  template <typename T, int Pos>
94  struct call_pair
95  {
96  using type = T;
97  static const int pos = Pos;
98  };
99 
100  template <typename H1>
101  struct call_params
102  {
103  H1& handler;
104  const routing_params& params;
105  const request& req;
106  response& res;
107  };
108 
109  template <typename F, int NInt, int NUint, int NDouble, int NString, typename S1, typename S2>
110  struct call
111  {
112  };
113 
114  template <typename F, int NInt, int NUint, int NDouble, int NString, typename ... Args1, typename ... Args2>
115  struct call<F, NInt, NUint, NDouble, NString, black_magic::S<int64_t, Args1...>, black_magic::S<Args2...>>
116  {
117  void operator()(F cparams)
118  {
119  using pushed = typename black_magic::S<Args2...>::template push_back<call_pair<int64_t, NInt>>;
120  call<F, NInt+1, NUint, NDouble, NString,
121  black_magic::S<Args1...>, pushed>()(cparams);
122  }
123  };
124 
125  template <typename F, int NInt, int NUint, int NDouble, int NString, typename ... Args1, typename ... Args2>
126  struct call<F, NInt, NUint, NDouble, NString, black_magic::S<uint64_t, Args1...>, black_magic::S<Args2...>>
127  {
128  void operator()(F cparams)
129  {
130  using pushed = typename black_magic::S<Args2...>::template push_back<call_pair<uint64_t, NUint>>;
131  call<F, NInt, NUint+1, NDouble, NString,
132  black_magic::S<Args1...>, pushed>()(cparams);
133  }
134  };
135 
136  template <typename F, int NInt, int NUint, int NDouble, int NString, typename ... Args1, typename ... Args2>
137  struct call<F, NInt, NUint, NDouble, NString, black_magic::S<double, Args1...>, black_magic::S<Args2...>>
138  {
139  void operator()(F cparams)
140  {
141  using pushed = typename black_magic::S<Args2...>::template push_back<call_pair<double, NDouble>>;
142  call<F, NInt, NUint, NDouble+1, NString,
143  black_magic::S<Args1...>, pushed>()(cparams);
144  }
145  };
146 
147  template <typename F, int NInt, int NUint, int NDouble, int NString, typename ... Args1, typename ... Args2>
148  struct call<F, NInt, NUint, NDouble, NString, black_magic::S<std::string, Args1...>, black_magic::S<Args2...>>
149  {
150  void operator()(F cparams)
151  {
152  using pushed = typename black_magic::S<Args2...>::template push_back<call_pair<std::string, NString>>;
153  call<F, NInt, NUint, NDouble, NString+1,
154  black_magic::S<Args1...>, pushed>()(cparams);
155  }
156  };
157 
158  template <typename F, int NInt, int NUint, int NDouble, int NString, typename ... Args1>
159  struct call<F, NInt, NUint, NDouble, NString, black_magic::S<>, black_magic::S<Args1...>>
160  {
161  void operator()(F cparams)
162  {
163  cparams.handler(
164  cparams.req,
165  cparams.res,
166  cparams.params.template get<typename Args1::type>(Args1::pos)...
167  );
168  }
169  };
170 
171  template <typename Func, typename ... ArgsWrapped>
172  struct Wrapped
173  {
174  template <typename ... Args>
175  void set_(Func f, typename std::enable_if<
176  !std::is_same<typename std::tuple_element<0, std::tuple<Args..., void>>::type, const request&>::value
177  , int>::type = 0)
178  {
179  handler_ = (
180 #ifdef CROW_CAN_USE_CPP14
181  [f = std::move(f)]
182 #else
183  [f]
184 #endif
185  (const request&, response& res, Args... args){
186  res = response(f(args...));
187  res.end();
188  });
189  }
190 
191  template <typename Req, typename ... Args>
193  {
194  req_handler_wrapper(Func f)
195  : f(std::move(f))
196  {
197  }
198 
199  void operator()(const request& req, response& res, Args... args)
200  {
201  res = response(f(req, args...));
202  res.end();
203  }
204 
205  Func f;
206  };
207 
208  template <typename ... Args>
209  void set_(Func f, typename std::enable_if<
210  std::is_same<typename std::tuple_element<0, std::tuple<Args..., void>>::type, const request&>::value &&
211  !std::is_same<typename std::tuple_element<1, std::tuple<Args..., void, void>>::type, response&>::value
212  , int>::type = 0)
213  {
214  handler_ = req_handler_wrapper<Args...>(std::move(f));
215  /*handler_ = (
216  [f = std::move(f)]
217  (const request& req, response& res, Args... args){
218  res = response(f(req, args...));
219  res.end();
220  });*/
221  }
222 
223  template <typename ... Args>
224  void set_(Func f, typename std::enable_if<
225  std::is_same<typename std::tuple_element<0, std::tuple<Args..., void>>::type, const request&>::value &&
226  std::is_same<typename std::tuple_element<1, std::tuple<Args..., void, void>>::type, response&>::value
227  , int>::type = 0)
228  {
229  handler_ = std::move(f);
230  }
231 
232  template <typename ... Args>
234  {
235  using type = std::function<void(const crow::request&, crow::response&, Args...)>;
237  };
238 
239  template <typename ... Args>
240  struct handler_type_helper<const request&, Args...>
241  {
242  using type = std::function<void(const crow::request&, crow::response&, Args...)>;
244  };
245 
246  template <typename ... Args>
247  struct handler_type_helper<const request&, response&, Args...>
248  {
249  using type = std::function<void(const crow::request&, crow::response&, Args...)>;
251  };
252 
253  typename handler_type_helper<ArgsWrapped...>::type handler_;
254 
255  void operator()(const request& req, response& res, const routing_params& params)
256  {
259  decltype(handler_)>,
260  0, 0, 0, 0,
261  typename handler_type_helper<ArgsWrapped...>::args_type,
263  >()(
265  decltype(handler_)>
266  {handler_, params, req, res}
267  );
268  }
269  };
270 
271  }
272  }
273 
274 
276  {
277  public:
278  CatchallRule(){}
279 
280  template <typename Func>
281  typename std::enable_if<black_magic::CallHelper<Func, black_magic::S<>>::value, void>::type
282  operator()(Func&& f)
283  {
284  static_assert(!std::is_same<void, decltype(f())>::value,
285  "Handler function cannot have void return type; valid return types: string, int, crow::response, crow::returnable");
286 
287  handler_ = (
288 #ifdef CROW_CAN_USE_CPP14
289  [f = std::move(f)]
290 #else
291  [f]
292 #endif
293  (const request&, response& res){
294  res = response(f());
295  res.end();
296  });
297 
298  }
299 
300  template <typename Func>
301  typename std::enable_if<
304  void>::type
305  operator()(Func&& f)
306  {
307  static_assert(!std::is_same<void, decltype(f(std::declval<crow::request>()))>::value,
308  "Handler function cannot have void return type; valid return types: string, int, crow::response, crow::returnable");
309 
310  handler_ = (
311 #ifdef CROW_CAN_USE_CPP14
312  [f = std::move(f)]
313 #else
314  [f]
315 #endif
316  (const crow::request& req, crow::response& res){
317  res = response(f(req));
318  res.end();
319  });
320  }
321 
322  template <typename Func>
323  typename std::enable_if<
327  void>::type
328  operator()(Func&& f)
329  {
330  static_assert(std::is_same<void, decltype(f(std::declval<crow::response&>()))>::value,
331  "Handler function with response argument should have void return type");
332  handler_ = (
333 #ifdef CROW_CAN_USE_CPP14
334  [f = std::move(f)]
335 #else
336  [f]
337 #endif
338  (const crow::request&, crow::response& res){
339  f(res);
340  });
341  }
342 
343  template <typename Func>
344  typename std::enable_if<
348  void>::type
349  operator()(Func&& f)
350  {
351  static_assert(std::is_same<void, decltype(f(std::declval<crow::request>(), std::declval<crow::response&>()))>::value,
352  "Handler function with response argument should have void return type");
353 
354  handler_ = std::move(f);
355  }
356 
357  bool has_handler()
358  {
359  return (handler_ != nullptr);
360  }
361 
362  protected:
363  friend class Router;
364  private:
365  std::function<void(const crow::request&, crow::response&)> handler_;
366  };
367 
368 
369  /// A rule dealing with websockets.
370 
371  /// Provides the interface for the user to put in the necessary handlers for a websocket to work.
372  ///
373  class WebSocketRule : public BaseRule
374  {
375  using self_t = WebSocketRule;
376  public:
377  WebSocketRule(std::string rule)
378  : BaseRule(std::move(rule))
379  {
380  }
381 
382  void validate() override
383  {
384  }
385 
386  void handle(const request&, response& res, const routing_params&) override
387  {
388  res = response(404);
389  res.end();
390  }
391 
392  void handle_upgrade(const request& req, response&, SocketAdaptor&& adaptor) override
393  {
394  new crow::websocket::Connection<SocketAdaptor>(req, std::move(adaptor), open_handler_, message_handler_, close_handler_, error_handler_, accept_handler_);
395  }
396 #ifdef CROW_ENABLE_SSL
397  void handle_upgrade(const request& req, response&, SSLAdaptor&& adaptor) override
398  {
399  new crow::websocket::Connection<SSLAdaptor>(req, std::move(adaptor), open_handler_, message_handler_, close_handler_, error_handler_, accept_handler_);
400  }
401 #endif
402 
403  template <typename Func>
404  self_t& onopen(Func f)
405  {
406  open_handler_ = f;
407  return *this;
408  }
409 
410  template <typename Func>
411  self_t& onmessage(Func f)
412  {
413  message_handler_ = f;
414  return *this;
415  }
416 
417  template <typename Func>
418  self_t& onclose(Func f)
419  {
420  close_handler_ = f;
421  return *this;
422  }
423 
424  template <typename Func>
425  self_t& onerror(Func f)
426  {
427  error_handler_ = f;
428  return *this;
429  }
430 
431  template <typename Func>
432  self_t& onaccept(Func f)
433  {
434  accept_handler_ = f;
435  return *this;
436  }
437 
438  protected:
439  std::function<void(crow::websocket::connection&)> open_handler_;
440  std::function<void(crow::websocket::connection&, const std::string&, bool)> message_handler_;
441  std::function<void(crow::websocket::connection&, const std::string&)> close_handler_;
442  std::function<void(crow::websocket::connection&)> error_handler_;
443  std::function<bool(const crow::request&)> accept_handler_;
444  };
445 
446  /// Allows the user to assign parameters using functions.
447  ///
448  /// `rule.name("name").methods(HTTPMethod::POST)`
449  template <typename T>
451  {
452  using self_t = T;
453  WebSocketRule& websocket()
454  {
455  auto p =new WebSocketRule(static_cast<self_t*>(this)->rule_);
456  static_cast<self_t*>(this)->rule_to_upgrade_.reset(p);
457  return *p;
458  }
459 
460  self_t& name(std::string name) noexcept
461  {
462  static_cast<self_t*>(this)->name_ = std::move(name);
463  return static_cast<self_t&>(*this);
464  }
465 
466  self_t& methods(HTTPMethod method)
467  {
468  static_cast<self_t*>(this)->methods_ = 1 << static_cast<int>(method);
469  return static_cast<self_t&>(*this);
470  }
471 
472  template <typename ... MethodArgs>
473  self_t& methods(HTTPMethod method, MethodArgs ... args_method)
474  {
475  methods(args_method...);
476  static_cast<self_t*>(this)->methods_ |= 1 << static_cast<int>(method);
477  return static_cast<self_t&>(*this);
478  }
479 
480  };
481 
482  /// A rule that can change its parameters during runtime.
483  class DynamicRule : public BaseRule, public RuleParameterTraits<DynamicRule>
484  {
485  public:
486 
487  DynamicRule(std::string rule)
488  : BaseRule(std::move(rule))
489  {
490  }
491 
492  void validate() override
493  {
494  if (!erased_handler_)
495  {
496  throw std::runtime_error(name_ + (!name_.empty() ? ": " : "") + "no handler for url " + rule_);
497  }
498  }
499 
500  void handle(const request& req, response& res, const routing_params& params) override
501  {
502  erased_handler_(req, res, params);
503  }
504 
505  template <typename Func>
506  void operator()(Func f)
507  {
508 #ifdef CROW_MSVC_WORKAROUND
509  using function_t = utility::function_traits<decltype(&Func::operator())>;
510 #else
511  using function_t = utility::function_traits<Func>;
512 #endif
513  erased_handler_ = wrap(std::move(f), black_magic::gen_seq<function_t::arity>());
514  }
515 
516  // enable_if Arg1 == request && Arg2 == response
517  // enable_if Arg1 == request && Arg2 != resposne
518  // enable_if Arg1 != request
519 #ifdef CROW_MSVC_WORKAROUND
520  template <typename Func, size_t ... Indices>
521 #else
522  template <typename Func, unsigned ... Indices>
523 #endif
524  std::function<void(const request&, response&, const routing_params&)>
525  wrap(Func f, black_magic::seq<Indices...>)
526  {
527 #ifdef CROW_MSVC_WORKAROUND
528  using function_t = utility::function_traits<decltype(&Func::operator())>;
529 #else
530  using function_t = utility::function_traits<Func>;
531 #endif
532  if (!black_magic::is_parameter_tag_compatible(
533  black_magic::get_parameter_tag_runtime(rule_.c_str()),
535  typename function_t::template arg<Indices>...>::value))
536  {
537  throw std::runtime_error("route_dynamic: Handler type is mismatched with URL parameters: " + rule_);
538  }
540  ret.template set_<
541  typename function_t::template arg<Indices>...
542  >(std::move(f));
543  return ret;
544  }
545 
546  template <typename Func>
547  void operator()(std::string name, Func&& f)
548  {
549  name_ = std::move(name);
550  (*this).template operator()<Func>(std::forward(f));
551  }
552  private:
553  std::function<void(const request&, response&, const routing_params&)> erased_handler_;
554 
555  };
556 
557  /// Default rule created when CROW_ROUTE is called.
558  template <typename ... Args>
559  class TaggedRule : public BaseRule, public RuleParameterTraits<TaggedRule<Args...>>
560  {
561  public:
562  using self_t = TaggedRule<Args...>;
563 
564  TaggedRule(std::string rule)
565  : BaseRule(std::move(rule))
566  {
567  }
568 
569  void validate() override
570  {
571  if (!handler_)
572  {
573  throw std::runtime_error(name_ + (!name_.empty() ? ": " : "") + "no handler for url " + rule_);
574  }
575  }
576 
577  template <typename Func>
578  typename std::enable_if<black_magic::CallHelper<Func, black_magic::S<Args...>>::value, void>::type
579  operator()(Func&& f)
580  {
581  static_assert(black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
583  "Handler type is mismatched with URL parameters");
584  static_assert(!std::is_same<void, decltype(f(std::declval<Args>()...))>::value,
585  "Handler function cannot have void return type; valid return types: string, int, crow::response, crow::returnable");
586 
587  handler_ = (
588 #ifdef CROW_CAN_USE_CPP14
589  [f = std::move(f)]
590 #else
591  [f]
592 #endif
593  (const request&, response& res, Args ... args){
594  res = response(f(args...));
595  res.end();
596  });
597  }
598 
599  template <typename Func>
600  typename std::enable_if<
601  !black_magic::CallHelper<Func, black_magic::S<Args...>>::value &&
602  black_magic::CallHelper<Func, black_magic::S<crow::request, Args...>>::value,
603  void>::type
604  operator()(Func&& f)
605  {
606  static_assert(black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
608  "Handler type is mismatched with URL parameters");
609  static_assert(!std::is_same<void, decltype(f(std::declval<crow::request>(), std::declval<Args>()...))>::value,
610  "Handler function cannot have void return type; valid return types: string, int, crow::response, crow::returnable");
611 
612  handler_ = (
613 #ifdef CROW_CAN_USE_CPP14
614  [f = std::move(f)]
615 #else
616  [f]
617 #endif
618  (const crow::request& req, crow::response& res, Args ... args){
619  res = response(f(req, args...));
620  res.end();
621  });
622  }
623 
624  template <typename Func>
625  typename std::enable_if<
626  !black_magic::CallHelper<Func, black_magic::S<Args...>>::value &&
627  !black_magic::CallHelper<Func, black_magic::S<crow::request, Args...>>::value &&
628  black_magic::CallHelper<Func, black_magic::S<crow::response&, Args...>>::value,
629  void>::type
630  operator()(Func&& f)
631  {
632  static_assert(black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
634  ,
635  "Handler type is mismatched with URL parameters");
636  static_assert(std::is_same<void, decltype(f(std::declval<crow::response&>(), std::declval<Args>()...))>::value,
637  "Handler function with response argument should have void return type");
638  handler_ = (
639 #ifdef CROW_CAN_USE_CPP14
640  [f = std::move(f)]
641 #else
642  [f]
643 #endif
644  (const crow::request&, crow::response& res, Args ... args){
645  f(res, args...);
646  });
647  }
648 
649  template <typename Func>
650  typename std::enable_if<
651  !black_magic::CallHelper<Func, black_magic::S<Args...>>::value &&
652  !black_magic::CallHelper<Func, black_magic::S<crow::request, Args...>>::value &&
653  !black_magic::CallHelper<Func, black_magic::S<crow::response&, Args...>>::value,
654  void>::type
655  operator()(Func&& f)
656  {
657  static_assert(black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
660  ,
661  "Handler type is mismatched with URL parameters");
662  static_assert(std::is_same<void, decltype(f(std::declval<crow::request>(), std::declval<crow::response&>(), std::declval<Args>()...))>::value,
663  "Handler function with response argument should have void return type");
664 
665  handler_ = std::move(f);
666  }
667 
668  template <typename Func>
669  void operator()(std::string name, Func&& f)
670  {
671  name_ = std::move(name);
672  (*this).template operator()<Func>(std::forward(f));
673  }
674 
675  void handle(const request& req, response& res, const routing_params& params) override
676  {
679  decltype(handler_)>,
680  0, 0, 0, 0,
681  black_magic::S<Args...>,
683  >()(
685  decltype(handler_)>
686  {handler_, params, req, res}
687  );
688  }
689 
690  private:
691  std::function<void(const crow::request&, crow::response&, Args...)> handler_;
692 
693  };
694 
695  const int RULE_SPECIAL_REDIRECT_SLASH = 1;
696 
697  /// A search tree.
698  class Trie
699  {
700  public:
701  struct Node
702  {
703  unsigned rule_index{};
704  std::string key;
705  ParamType param = ParamType::MAX; // MAX = No param.
706  std::vector<Node*> children;
707 
708  bool IsSimpleNode() const
709  {
710  return
711  !rule_index &&
712  children.size() < 2 &&
713  param == ParamType::MAX &&
714  std::all_of(std::begin(children), std::end(children), [](Node* x){ return x->param == ParamType::MAX; });
715  }
716  };
717 
718 
719  Trie()
720  {
721  }
722 
723  ///Check whether or not the trie is empty.
724  bool is_empty()
725  {
726  return head_.children.empty();
727  }
728 
729  void optimize()
730  {
731  for (auto child: head_.children)
732  {
733  optimizeNode(child);
734  }
735  }
736 
737 
738  private:
739  void optimizeNode(Node* node)
740  {
741  if (node->children.empty())
742  return;
743  if (node->IsSimpleNode())
744  {
745  Node* child_temp = node->children[0];
746  node->key = node->key + child_temp->key;
747  node->rule_index = child_temp->rule_index;
748  node->children = std::move(child_temp->children);
749  delete(child_temp);
750  optimizeNode(node);
751  }
752  else
753  {
754  for(auto& child : node->children)
755  {
756  optimizeNode(child);
757  }
758  }
759  }
760 
761  void debug_node_print(Node* node, int level)
762  {
763  if (node->param != ParamType::MAX)
764  {
765  switch(node->param)
766  {
767  case ParamType::INT:
768  CROW_LOG_DEBUG << std::string(2*level, ' ') << "<int>";
769  break;
770  case ParamType::UINT:
771  CROW_LOG_DEBUG << std::string(2*level, ' ') << "<uint>";
772  break;
773  case ParamType::DOUBLE:
774  CROW_LOG_DEBUG << std::string(2*level, ' ') << "<double>";
775  break;
776  case ParamType::STRING:
777  CROW_LOG_DEBUG << std::string(2*level, ' ') << "<string>";
778  break;
779  case ParamType::PATH:
780  CROW_LOG_DEBUG << std::string(2*level, ' ') << "<path>";
781  break;
782  default:
783  CROW_LOG_DEBUG << std::string(2*level, ' ') << "<ERROR>";
784  break;
785  }
786  }
787  else
788  CROW_LOG_DEBUG << std::string(2*level, ' ') << node->key;
789 
790  for(auto& child : node->children)
791  {
792  debug_node_print(child, level+1);
793  }
794  }
795  public:
796 
797  void debug_print()
798  {
799  CROW_LOG_DEBUG << "HEAD";
800  for (auto& child : head_.children)
801  debug_node_print(child, 1);
802  }
803 
804  void validate()
805  {
806  if (!head_.IsSimpleNode())
807  throw std::runtime_error("Internal error: Trie header should be simple!");
808  optimize();
809  }
810 
811  std::pair<unsigned, routing_params> find(const std::string& req_url, const Node* node = nullptr, unsigned pos = 0, routing_params* params = nullptr) const
812  {
813  //start params as an empty struct
814  routing_params empty;
815  if (params == nullptr)
816  params = &empty;
817 
818  unsigned found{}; //The rule index to be found
819  routing_params match_params; //supposedly the final matched parameters
820 
821  //start from the head node
822  if (node == nullptr)
823  node = &head_;
824 
825  //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
826  if (pos == req_url.size())
827  return {node->rule_index, *params};
828 
829  auto update_found = [&found, &match_params](std::pair<unsigned, routing_params>& ret)
830  {
831  if (ret.first && (!found || found > ret.first))
832  {
833  found = ret.first;
834  match_params = std::move(ret.second);
835  }
836  };
837 
838 
839  for(auto& child : node->children)
840  {
841  if (child->param != ParamType::MAX)
842  {
843  if (child->param == ParamType::INT)
844  {
845  char c = req_url[pos];
846  if ((c >= '0' && c <= '9') || c == '+' || c == '-')
847  {
848  char* eptr;
849  errno = 0;
850  long long int value = strtoll(req_url.data()+pos, &eptr, 10);
851  if (errno != ERANGE && eptr != req_url.data()+pos)
852  {
853  params->int_params.push_back(value);
854  auto ret = find(req_url, child, eptr - req_url.data(), params);
855  update_found(ret);
856  params->int_params.pop_back();
857  }
858  }
859  }
860 
861  else if (child->param == ParamType::UINT)
862  {
863  char c = req_url[pos];
864  if ((c >= '0' && c <= '9') || c == '+')
865  {
866  char* eptr;
867  errno = 0;
868  unsigned long long int value = strtoull(req_url.data()+pos, &eptr, 10);
869  if (errno != ERANGE && eptr != req_url.data()+pos)
870  {
871  params->uint_params.push_back(value);
872  auto ret = find(req_url, child, eptr - req_url.data(), params);
873  update_found(ret);
874  params->uint_params.pop_back();
875  }
876  }
877  }
878 
879  else if (child->param == ParamType::DOUBLE)
880  {
881  char c = req_url[pos];
882  if ((c >= '0' && c <= '9') || c == '+' || c == '-' || c == '.')
883  {
884  char* eptr;
885  errno = 0;
886  double value = strtod(req_url.data()+pos, &eptr);
887  if (errno != ERANGE && eptr != req_url.data()+pos)
888  {
889  params->double_params.push_back(value);
890  auto ret = find(req_url, child, eptr - req_url.data(), params);
891  update_found(ret);
892  params->double_params.pop_back();
893  }
894  }
895  }
896 
897  else if (child->param == ParamType::STRING)
898  {
899  size_t epos = pos;
900  for(; epos < req_url.size(); epos ++)
901  {
902  if (req_url[epos] == '/')
903  break;
904  }
905 
906  if (epos != pos)
907  {
908  params->string_params.push_back(req_url.substr(pos, epos-pos));
909  auto ret = find(req_url, child, epos, params);
910  update_found(ret);
911  params->string_params.pop_back();
912  }
913  }
914 
915  else if (child->param == ParamType::PATH)
916  {
917  size_t epos = req_url.size();
918 
919  if (epos != pos)
920  {
921  params->string_params.push_back(req_url.substr(pos, epos-pos));
922  auto ret = find(req_url, child, epos, params);
923  update_found(ret);
924  params->string_params.pop_back();
925  }
926  }
927  }
928 
929  else
930  {
931  const std::string& fragment = child->key;
932  if (req_url.compare(pos, fragment.size(), fragment) == 0)
933  {
934  auto ret = find(req_url, child, pos + fragment.size(), params);
935  update_found(ret);
936  }
937  }
938  }
939  return {found, match_params}; //Called after all the recursions have been done
940  }
941 
942  void add(const std::string& url, unsigned rule_index)
943  {
944  Node* idx = &head_;
945 
946  for(unsigned i = 0; i < url.size(); i ++)
947  {
948  char c = url[i];
949  if (c == '<')
950  {
951  static struct ParamTraits
952  {
953  ParamType type;
954  std::string name;
955  } paramTraits[] =
956  {
957  { ParamType::INT, "<int>" },
958  { ParamType::UINT, "<uint>" },
959  { ParamType::DOUBLE, "<float>" },
960  { ParamType::DOUBLE, "<double>" },
961  { ParamType::STRING, "<str>" },
962  { ParamType::STRING, "<string>" },
963  { ParamType::PATH, "<path>" },
964  };
965 
966  for(auto& x:paramTraits)
967  {
968  if (url.compare(i, x.name.size(), x.name) == 0)
969  {
970  bool found = false;
971  for (Node* child : idx->children)
972  {
973  if (child->param == x.type)
974  {
975  idx = child;
976  i += x.name.size();
977  found = true;
978  break;
979  }
980  }
981  if (found)
982  break;
983 
984  auto new_node_idx = new_node(idx);
985  new_node_idx->param = x.type;
986  idx = new_node_idx;
987  i += x.name.size();
988  break;
989  }
990  }
991 
992  i --;
993  }
994  else
995  {
996  //This part assumes the tree is unoptimized (every node has a max 1 character key)
997  bool piece_found = false;
998  for (auto& child : idx->children)
999  {
1000  if (child->key[0] == c)
1001  {
1002  idx = child;
1003  piece_found = true;
1004  break;
1005  }
1006  }
1007  if (!piece_found)
1008  {
1009  auto new_node_idx = new_node(idx);
1010  new_node_idx->key = c;
1011  idx = new_node_idx;
1012  }
1013  }
1014  }
1015 
1016  //check if the last node already has a value (exact url already in Trie)
1017  if (idx->rule_index)
1018  throw std::runtime_error("handler already exists for " + url);
1019  idx->rule_index = rule_index;
1020  }
1021 
1022  size_t get_size()
1023  {
1024  return get_size(&head_);
1025  }
1026 
1027  size_t get_size(Node* node)
1028  {
1029  unsigned size = 8; //rule_index and param
1030  size += (node->key.size()); //each character in the key is 1 byte
1031  for (auto child: node->children)
1032  {
1033  size += get_size(child);
1034  }
1035  return size;
1036  }
1037 
1038 
1039  private:
1040 
1041  Node* new_node(Node* parent)
1042  {
1043  auto& children = parent->children;
1044  children.resize(children.size()+1);
1045  children[children.size()-1] = new Node();
1046  return children[children.size()-1];
1047  }
1048 
1049  Node head_;
1050  };
1051 
1052 
1053  /// Handles matching requests to existing rules and upgrade requests.
1054  class Router
1055  {
1056  public:
1057  Router()
1058  {
1059  }
1060 
1061  DynamicRule& new_rule_dynamic(const std::string& rule)
1062  {
1063  auto ruleObject = new DynamicRule(rule);
1064  all_rules_.emplace_back(ruleObject);
1065 
1066  return *ruleObject;
1067  }
1068 
1069  template <uint64_t N>
1070  typename black_magic::arguments<N>::type::template rebind<TaggedRule>& new_rule_tagged(const std::string& rule)
1071  {
1072  using RuleT = typename black_magic::arguments<N>::type::template rebind<TaggedRule>;
1073 
1074  auto ruleObject = new RuleT(rule);
1075  all_rules_.emplace_back(ruleObject);
1076 
1077  return *ruleObject;
1078  }
1079 
1080  CatchallRule& catchall_rule()
1081  {
1082  return catchall_rule_;
1083  }
1084 
1085  void internal_add_rule_object(const std::string& rule, BaseRule* ruleObject)
1086  {
1087  bool has_trailing_slash = false;
1088  std::string rule_without_trailing_slash;
1089  if (rule.size() > 1 && rule.back() == '/')
1090  {
1091  has_trailing_slash = true;
1092  rule_without_trailing_slash = rule;
1093  rule_without_trailing_slash.pop_back();
1094  }
1095 
1096  ruleObject->foreach_method([&](int method)
1097  {
1098  per_methods_[method].rules.emplace_back(ruleObject);
1099  per_methods_[method].trie.add(rule, per_methods_[method].rules.size() - 1);
1100 
1101  // directory case:
1102  // request to '/about' url matches '/about/' rule
1103  if (has_trailing_slash)
1104  {
1105  per_methods_[method].trie.add(rule_without_trailing_slash, RULE_SPECIAL_REDIRECT_SLASH);
1106  }
1107  });
1108 
1109  }
1110 
1111  void validate()
1112  {
1113  for(auto& rule:all_rules_)
1114  {
1115  if (rule)
1116  {
1117  auto upgraded = rule->upgrade();
1118  if (upgraded)
1119  rule = std::move(upgraded);
1120  rule->validate();
1121  internal_add_rule_object(rule->rule(), rule.get());
1122  }
1123  }
1124  for(auto& per_method:per_methods_)
1125  {
1126  per_method.trie.validate();
1127  }
1128  }
1129 
1130  //TODO maybe add actual_method
1131  template <typename Adaptor>
1132  void handle_upgrade(const request& req, response& res, Adaptor&& adaptor)
1133  {
1134  if (req.method >= HTTPMethod::InternalMethodCount)
1135  return;
1136 
1137  auto& per_method = per_methods_[static_cast<int>(req.method)];
1138  auto& rules = per_method.rules;
1139  unsigned rule_index = per_method.trie.find(req.url).first;
1140 
1141  if (!rule_index)
1142  {
1143  for (auto& per_method: per_methods_)
1144  {
1145  if (per_method.trie.find(req.url).first)
1146  {
1147  CROW_LOG_DEBUG << "Cannot match method " << req.url << " " << method_name(req.method);
1148  res = response(405);
1149  res.end();
1150  return;
1151  }
1152  }
1153 
1154  CROW_LOG_INFO << "Cannot match rules " << req.url;
1155  res = response(404);
1156  res.end();
1157  return;
1158  }
1159 
1160  if (rule_index >= rules.size())
1161  throw std::runtime_error("Trie internal structure corrupted!");
1162 
1163  if (rule_index == RULE_SPECIAL_REDIRECT_SLASH)
1164  {
1165  CROW_LOG_INFO << "Redirecting to a url with trailing slash: " << req.url;
1166  res = response(301);
1167 
1168  // TODO absolute url building
1169  if (req.get_header_value("Host").empty())
1170  {
1171  res.add_header("Location", req.url + "/");
1172  }
1173  else
1174  {
1175  res.add_header("Location", "http://" + req.get_header_value("Host") + req.url + "/");
1176  }
1177  res.end();
1178  return;
1179  }
1180 
1181  CROW_LOG_DEBUG << "Matched rule (upgrade) '" << rules[rule_index]->rule_ << "' " << static_cast<uint32_t>(req.method) << " / " << rules[rule_index]->get_methods();
1182 
1183  // any uncaught exceptions become 500s
1184  try
1185  {
1186  rules[rule_index]->handle_upgrade(req, res, std::move(adaptor));
1187  }
1188  catch(std::exception& e)
1189  {
1190  CROW_LOG_ERROR << "An uncaught exception occurred: " << e.what();
1191  res = response(500);
1192  res.end();
1193  return;
1194  }
1195  catch(...)
1196  {
1197  CROW_LOG_ERROR << "An uncaught exception occurred. The type was unknown so no information was available.";
1198  res = response(500);
1199  res.end();
1200  return;
1201  }
1202  }
1203 
1204  void handle(const request& req, response& res)
1205  {
1206  HTTPMethod method_actual = req.method;
1207  if (req.method >= HTTPMethod::InternalMethodCount)
1208  return;
1209  else if (req.method == HTTPMethod::Head)
1210  {
1211  method_actual = HTTPMethod::Get;
1212  res.is_head_response = true;
1213  }
1214  else if (req.method == HTTPMethod::Options)
1215  {
1216  std::string allow = "OPTIONS, HEAD, ";
1217 
1218  if (req.url == "/*")
1219  {
1220  for(int i = 0; i < static_cast<int>(HTTPMethod::InternalMethodCount); i ++)
1221  {
1222  if (!per_methods_[i].trie.is_empty())
1223  {
1224  allow += method_name(static_cast<HTTPMethod>(i)) + ", ";
1225  }
1226  }
1227  allow = allow.substr(0, allow.size()-2);
1228  res = response(204);
1229  res.set_header("Allow", allow);
1230  res.manual_length_header = true;
1231  res.end();
1232  return;
1233  }
1234  else
1235  {
1236  for(int i = 0; i < static_cast<int>(HTTPMethod::InternalMethodCount); i ++)
1237  {
1238  if (per_methods_[i].trie.find(req.url).first)
1239  {
1240  allow += method_name(static_cast<HTTPMethod>(i)) + ", ";
1241  }
1242  }
1243  if (allow != "OPTIONS, HEAD, ")
1244  {
1245  allow = allow.substr(0, allow.size()-2);
1246  res = response(204);
1247  res.set_header("Allow", allow);
1248  res.manual_length_header = true;
1249  res.end();
1250  return;
1251  }
1252  else
1253  {
1254  CROW_LOG_DEBUG << "Cannot match rules " << req.url;
1255  res = response(404);
1256  res.end();
1257  return;
1258  }
1259  }
1260  }
1261 
1262  auto& per_method = per_methods_[static_cast<int>(method_actual)];
1263  auto& trie = per_method.trie;
1264  auto& rules = per_method.rules;
1265 
1266  auto found = trie.find(req.url);
1267 
1268  unsigned rule_index = found.first;
1269 
1270  if (!rule_index)
1271  {
1272  for (auto& per_method: per_methods_)
1273  {
1274  if (per_method.trie.find(req.url).first)
1275  {
1276  CROW_LOG_DEBUG << "Cannot match method " << req.url << " " << method_name(method_actual);
1277  res = response(405);
1278  res.end();
1279  return;
1280  }
1281  }
1282 
1283  if (catchall_rule_.has_handler())
1284  {
1285  CROW_LOG_DEBUG << "Cannot match rules " << req.url << ". Redirecting to Catchall rule";
1286  catchall_rule_.handler_(req, res);
1287  }
1288  else
1289  {
1290  CROW_LOG_DEBUG << "Cannot match rules " << req.url;
1291  res = response(404);
1292  }
1293  res.end();
1294  return;
1295  }
1296 
1297  if (rule_index >= rules.size())
1298  throw std::runtime_error("Trie internal structure corrupted!");
1299 
1300  if (rule_index == RULE_SPECIAL_REDIRECT_SLASH)
1301  {
1302  CROW_LOG_INFO << "Redirecting to a url with trailing slash: " << req.url;
1303  res = response(301);
1304 
1305  // TODO absolute url building
1306  if (req.get_header_value("Host").empty())
1307  {
1308  res.add_header("Location", req.url + "/");
1309  }
1310  else
1311  {
1312  res.add_header("Location", "http://" + req.get_header_value("Host") + req.url + "/");
1313  }
1314  res.end();
1315  return;
1316  }
1317 
1318  CROW_LOG_DEBUG << "Matched rule '" << rules[rule_index]->rule_ << "' " << static_cast<uint32_t>(req.method) << " / " << rules[rule_index]->get_methods();
1319 
1320  // any uncaught exceptions become 500s
1321  try
1322  {
1323  rules[rule_index]->handle(req, res, found.second);
1324  }
1325  catch(std::exception& e)
1326  {
1327  CROW_LOG_ERROR << "An uncaught exception occurred: " << e.what();
1328  res = response(500);
1329  res.end();
1330  return;
1331  }
1332  catch(...)
1333  {
1334  CROW_LOG_ERROR << "An uncaught exception occurred. The type was unknown so no information was available.";
1335  res = response(500);
1336  res.end();
1337  return;
1338  }
1339  }
1340 
1341  void debug_print()
1342  {
1343  for(int i = 0; i < static_cast<int>(HTTPMethod::InternalMethodCount); i ++)
1344  {
1345  CROW_LOG_DEBUG << method_name(static_cast<HTTPMethod>(i));
1346  per_methods_[i].trie.debug_print();
1347  }
1348  }
1349 
1350  private:
1351  CatchallRule catchall_rule_;
1352 
1353  struct PerMethod
1354  {
1355  std::vector<BaseRule*> rules;
1356  Trie trie;
1357 
1358  // rule index 0, 1 has special meaning; preallocate it to avoid duplication.
1359  PerMethod() : rules(2) {}
1360  };
1361  std::array<PerMethod, static_cast<int>(HTTPMethod::InternalMethodCount)> per_methods_;
1362  std::vector<std::unique_ptr<BaseRule>> all_rules_;
1363 
1364  };
1365 }
crow::Trie::Node
Definition: routing.h:701
crow::TaggedRule
Default rule created when CROW_ROUTE is called.
Definition: routing.h:559
crow::black_magic::S
Definition: utility.h:240
crow::CatchallRule
Definition: routing.h:275
crow::black_magic::compute_parameter_tag_from_args_list
Definition: utility.h:151
crow::detail::routing_handler_call_helper::call_params
Definition: routing.h:101
crow::RuleParameterTraits
Definition: routing.h:450
crow::websocket::connection
A base class for websocket connection.
Definition: websocket.h:22
crow::utility::function_traits
Definition: utility.h:458
crow::request::url
std::string url
The endpoint without any parameters.
Definition: http_request.h:30
crow::WebSocketRule
A rule dealing with websockets.
Definition: routing.h:373
crow::request
An HTTP request.
Definition: http_request.h:26
crow::black_magic::CallHelper
Definition: utility.h:250
crow::Trie
A search tree.
Definition: routing.h:698
crow::response
HTTP response.
Definition: http_response.h:23
crow::black_magic::seq
Definition: utility.h:332
crow::DynamicRule
A rule that can change its parameters during runtime.
Definition: routing.h:483
crow::detail::routing_handler_call_helper::Wrapped
Definition: routing.h:172
crow::black_magic::arguments
Definition: utility.h:303
crow::detail::routing_handler_call_helper::Wrapped::handler_type_helper
Definition: routing.h:233
crow::black_magic::gen_seq
Definition: utility.h:343
crow::SocketAdaptor
A wrapper for the asio::ip::tcp::socket and asio::ssl::stream.
Definition: socket_adaptors.h:18
crow::BaseRule
A base class for all rules.
Definition: routing.h:24
crow::response::add_header
void add_header(std::string key, std::string value)
Add a new header to the response.
Definition: http_response.h:46
crow::response::manual_length_header
bool manual_length_header
Whether Crow should automatically add a "Content-Length" header.
Definition: http_response.h:36
crow::detail::routing_handler_call_helper::call_pair
Definition: routing.h:94
crow::Trie::is_empty
bool is_empty()
Check whether or not the trie is empty.
Definition: routing.h:724
crow::detail::routing_handler_call_helper::Wrapped::req_handler_wrapper
Definition: routing.h:192
crow::response::end
void end()
Set the response completion flag and call the handler (to send the response).
Definition: http_response.h:151
crow::websocket::Connection
A websocket connection.
Definition: websocket.h:59
crow::routing_params
Definition: common.h:83
crow::Router
Handles matching requests to existing rules and upgrade requests.
Definition: routing.h:1054
crow::detail::routing_handler_call_helper::call
Definition: routing.h:110
crow::response::set_header
void set_header(std::string key, std::string value)
Set the value of an existing header in the response.
Definition: http_response.h:39
crow::response::is_head_response
bool is_head_response
Whether this is a response to a HEAD request.
Definition: http_response.h:35