Electroneum
routing.h
Go to the documentation of this file.
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 "common.h"
12 #include "http_response.h"
13 #include "http_request.h"
14 #include "utility.h"
15 #include "logging.h"
16 #include "websocket.h"
17 
18 namespace crow
19 {
20  class BaseRule
21  {
22  public:
23  BaseRule(std::string rule)
24  : rule_(std::move(rule))
25  {
26  }
27 
28  virtual ~BaseRule()
29  {
30  }
31 
32  virtual void validate() = 0;
33  std::unique_ptr<BaseRule> upgrade()
34  {
35  if (rule_to_upgrade_)
36  return std::move(rule_to_upgrade_);
37  return {};
38  }
39 
40  virtual void handle(const request&, response&, const routing_params&) = 0;
41  virtual void handle_upgrade(const request&, response& res, SocketAdaptor&&)
42  {
43  res = response(404);
44  res.end();
45  }
46 #ifdef CROW_ENABLE_SSL
47  virtual void handle_upgrade(const request&, response& res, SSLAdaptor&&)
48  {
49  res = response(404);
50  res.end();
51  }
52 #endif
53 
54  uint32_t get_methods()
55  {
56  return methods_;
57  }
58 
59  protected:
60  uint32_t methods_{1<<(int)HTTPMethod::Get};
61 
62  std::string rule_;
63  std::string name_;
64 
65  std::unique_ptr<BaseRule> rule_to_upgrade_;
66 
67  friend class Router;
68  template <typename T>
69  friend struct RuleParameterTraits;
70  };
71 
72 
73  namespace detail
74  {
75  namespace routing_handler_call_helper
76  {
77  template <typename T, int Pos>
78  struct call_pair
79  {
80  using type = T;
81  static const int pos = Pos;
82  };
83 
84  template <typename H1>
85  struct call_params
86  {
87  H1& handler;
89  const request& req;
91  };
92 
93  template <typename F, int NInt, int NUint, int NDouble, int NString, typename S1, typename S2>
94  struct call
95  {
96  };
97 
98  template <typename F, int NInt, int NUint, int NDouble, int NString, typename ... Args1, typename ... Args2>
99  struct call<F, NInt, NUint, NDouble, NString, black_magic::S<int64_t, Args1...>, black_magic::S<Args2...>>
100  {
101  void operator()(F cparams)
102  {
103  using pushed = typename black_magic::S<Args2...>::template push_back<call_pair<int64_t, NInt>>;
104  call<F, NInt+1, NUint, NDouble, NString,
105  black_magic::S<Args1...>, pushed>()(cparams);
106  }
107  };
108 
109  template <typename F, int NInt, int NUint, int NDouble, int NString, typename ... Args1, typename ... Args2>
110  struct call<F, NInt, NUint, NDouble, NString, black_magic::S<uint64_t, Args1...>, black_magic::S<Args2...>>
111  {
112  void operator()(F cparams)
113  {
114  using pushed = typename black_magic::S<Args2...>::template push_back<call_pair<uint64_t, NUint>>;
115  call<F, NInt, NUint+1, NDouble, NString,
116  black_magic::S<Args1...>, pushed>()(cparams);
117  }
118  };
119 
120  template <typename F, int NInt, int NUint, int NDouble, int NString, typename ... Args1, typename ... Args2>
121  struct call<F, NInt, NUint, NDouble, NString, black_magic::S<double, Args1...>, black_magic::S<Args2...>>
122  {
123  void operator()(F cparams)
124  {
125  using pushed = typename black_magic::S<Args2...>::template push_back<call_pair<double, NDouble>>;
126  call<F, NInt, NUint, NDouble+1, NString,
127  black_magic::S<Args1...>, pushed>()(cparams);
128  }
129  };
130 
131  template <typename F, int NInt, int NUint, int NDouble, int NString, typename ... Args1, typename ... Args2>
132  struct call<F, NInt, NUint, NDouble, NString, black_magic::S<std::string, Args1...>, black_magic::S<Args2...>>
133  {
134  void operator()(F cparams)
135  {
136  using pushed = typename black_magic::S<Args2...>::template push_back<call_pair<std::string, NString>>;
137  call<F, NInt, NUint, NDouble, NString+1,
138  black_magic::S<Args1...>, pushed>()(cparams);
139  }
140  };
141 
142  template <typename F, int NInt, int NUint, int NDouble, int NString, typename ... Args1>
143  struct call<F, NInt, NUint, NDouble, NString, black_magic::S<>, black_magic::S<Args1...>>
144  {
145  void operator()(F cparams)
146  {
147  cparams.handler(
148  cparams.req,
149  cparams.res,
150  cparams.params.template get<typename Args1::type>(Args1::pos)...
151  );
152  }
153  };
154 
155  template <typename Func, typename ... ArgsWrapped>
156  struct Wrapped
157  {
158  template <typename ... Args>
159  void set2(Func f, typename std::enable_if<
160  !std::is_same<typename std::tuple_element<0, std::tuple<Args..., void>>::type, const request&>::value
161  , int>::type = 0)
162  {
163  handler_ = (
164 #ifdef CROW_CAN_USE_CPP14
165  [f = std::move(f)]
166 #else
167  [f]
168 #endif
169  (const request&, response& res, Args... args){
170  res = response(f(args...));
171  res.end();
172  });
173  }
174 
175  template <typename Req, typename ... Args>
177  {
179  : f(std::move(f))
180  {
181  }
182 
183  void operator()(const request& req, response& res, Args... args)
184  {
185  res = response(f(req, args...));
186  res.end();
187  }
188 
189  Func f;
190  };
191 
192  template <typename ... Args>
193  void set2(Func f, typename std::enable_if<
194  std::is_same<typename std::tuple_element<0, std::tuple<Args..., void>>::type, const request&>::value &&
195  !std::is_same<typename std::tuple_element<1, std::tuple<Args..., void, void>>::type, response&>::value
196  , int>::type = 0)
197  {
198  handler_ = req_handler_wrapper<Args...>(std::move(f));
199  /*handler_ = (
200  [f = std::move(f)]
201  (const request& req, response& res, Args... args){
202  res = response(f(req, args...));
203  res.end();
204  });*/
205  }
206 
207  template <typename ... Args>
208  void set2(Func f, typename std::enable_if<
209  std::is_same<typename std::tuple_element<0, std::tuple<Args..., void>>::type, const request&>::value &&
210  std::is_same<typename std::tuple_element<1, std::tuple<Args..., void, void>>::type, response&>::value
211  , int>::type = 0)
212  {
213  handler_ = std::move(f);
214  }
215 
216  template <typename ... Args>
218  {
219  using type = std::function<void(const crow::request&, crow::response&, Args...)>;
221  };
222 
223  template <typename ... Args>
224  struct handler_type_helper<const request&, Args...>
225  {
226  using type = std::function<void(const crow::request&, crow::response&, Args...)>;
228  };
229 
230  template <typename ... Args>
231  struct handler_type_helper<const request&, response&, Args...>
232  {
233  using type = std::function<void(const crow::request&, crow::response&, Args...)>;
235  };
236 
237  typename handler_type_helper<ArgsWrapped...>::type handler_;
238 
239  void operator()(const request& req, response& res, const routing_params& params)
240  {
243  decltype(handler_)>,
244  0, 0, 0, 0,
245  typename handler_type_helper<ArgsWrapped...>::args_type,
247  >()(
249  decltype(handler_)>
250  {handler_, params, req, res}
251  );
252  }
253  };
254 
255  }
256  }
257 
258  class WebSocketRule : public BaseRule
259  {
261  public:
262  WebSocketRule(std::string rule)
263  : BaseRule(std::move(rule))
264  {
265  }
266 
267  void validate() override
268  {
269  }
270 
271  void handle(const request&, response& res, const routing_params&) override
272  {
273  res = response(404);
274  res.end();
275  }
276 
277  void handle_upgrade(const request& req, response&, SocketAdaptor&& adaptor) override
278  {
280  }
281 #ifdef CROW_ENABLE_SSL
282  void handle_upgrade(const request& req, response&, SSLAdaptor&& adaptor) override
283  {
285  }
286 #endif
287 
288  template <typename Func>
289  self_t& onopen(Func f)
290  {
291  open_handler_ = f;
292  return *this;
293  }
294 
295  template <typename Func>
296  self_t& onmessage(Func f)
297  {
298  message_handler_ = f;
299  return *this;
300  }
301 
302  template <typename Func>
303  self_t& onclose(Func f)
304  {
305  close_handler_ = f;
306  return *this;
307  }
308 
309  template <typename Func>
310  self_t& onerror(Func f)
311  {
312  error_handler_ = f;
313  return *this;
314  }
315 
316  protected:
317  std::function<void(crow::websocket::connection&)> open_handler_;
318  std::function<void(crow::websocket::connection&, const std::string&, bool)> message_handler_;
319  std::function<void(crow::websocket::connection&, const std::string&)> close_handler_;
320  std::function<void(crow::websocket::connection&)> error_handler_;
321  };
322 
323  template <typename T>
325  {
326  using self_t = T;
328  {
329  auto p =new WebSocketRule(((self_t*)this)->rule_);
330  ((self_t*)this)->rule_to_upgrade_.reset(p);
331  return *p;
332  }
333 
334  self_t& name(std::string name) noexcept
335  {
336  ((self_t*)this)->name_ = std::move(name);
337  return (self_t&)*this;
338  }
339 
341  {
342  ((self_t*)this)->methods_ = 1 << (int)method;
343  return (self_t&)*this;
344  }
345 
346  template <typename ... MethodArgs>
347  self_t& methods(HTTPMethod method, MethodArgs ... args_method)
348  {
349  methods(args_method...);
350  ((self_t*)this)->methods_ |= 1 << (int)method;
351  return (self_t&)*this;
352  }
353 
354  };
355 
356  class DynamicRule : public BaseRule, public RuleParameterTraits<DynamicRule>
357  {
358  public:
359 
360  DynamicRule(std::string rule)
361  : BaseRule(std::move(rule))
362  {
363  }
364 
365  void validate() override
366  {
367  if (!erased_handler_)
368  {
369  throw std::runtime_error(name_ + (!name_.empty() ? ": " : "") + "no handler for url " + rule_);
370  }
371  }
372 
373  void handle(const request& req, response& res, const routing_params& params) override
374  {
375  erased_handler_(req, res, params);
376  }
377 
378  template <typename Func>
379  void operator()(Func f)
380  {
381 #ifdef CROW_MSVC_WORKAROUND
383 #else
384  using function_t = utility::function_traits<Func>;
385 #endif
387  }
388 
389  // enable_if Arg1 == request && Arg2 == response
390  // enable_if Arg1 == request && Arg2 != resposne
391  // enable_if Arg1 != request
392 #ifdef CROW_MSVC_WORKAROUND
393  template <typename Func, size_t ... Indices>
394 #else
395  template <typename Func, unsigned ... Indices>
396 #endif
397  std::function<void(const request&, response&, const routing_params&)>
399  {
400 #ifdef CROW_MSVC_WORKAROUND
402 #else
403  using function_t = utility::function_traits<Func>;
404 #endif
408  typename function_t::template arg<Indices>...>::value))
409  {
410  throw std::runtime_error("route_dynamic: Handler type is mismatched with URL parameters: " + rule_);
411  }
413  ret.template set2<typename function_t::template arg<Indices>...>(std::move(f));
414  return ret;
415  }
416 
417  template <typename Func>
418  void operator()(std::string name, Func&& f)
419  {
420  name_ = std::move(name);
421  (*this).template operator()<Func>(std::forward(f));
422  }
423  private:
424  std::function<void(const request&, response&, const routing_params&)> erased_handler_;
425 
426  };
427 
428  template <typename ... Args>
429  class TaggedRule : public BaseRule, public RuleParameterTraits<TaggedRule<Args...>>
430  {
431  public:
432  using self_t = TaggedRule<Args...>;
433 
434  TaggedRule(std::string rule)
435  : BaseRule(std::move(rule))
436  {
437  }
438 
439  void validate() override
440  {
441  if (!handler_)
442  {
443  throw std::runtime_error(name_ + (!name_.empty() ? ": " : "") + "no handler for url " + rule_);
444  }
445  }
446 
447  template <typename Func>
448  typename std::enable_if<black_magic::CallHelper<Func, black_magic::S<Args...>>::value, void>::type
449  operator()(Func&& f)
450  {
451  static_assert(black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
453  "Handler type is mismatched with URL parameters");
454  static_assert(!std::is_same<void, decltype(f(std::declval<Args>()...))>::value,
455  "Handler function cannot have void return type; valid return types: string, int, crow::resposne, crow::json::wvalue");
456 
457  handler_ = [f = std::move(f)](const request&, response& res, Args ... args){
458  res = response(f(args...));
459  res.end();
460  };
461  }
462 
463  template <typename Func>
464  typename std::enable_if<
465  !black_magic::CallHelper<Func, black_magic::S<Args...>>::value &&
466  black_magic::CallHelper<Func, black_magic::S<crow::request, Args...>>::value,
467  void>::type
468  operator()(Func&& f)
469  {
470  static_assert(black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
472  "Handler type is mismatched with URL parameters");
473  static_assert(!std::is_same<void, decltype(f(std::declval<crow::request>(), std::declval<Args>()...))>::value,
474  "Handler function cannot have void return type; valid return types: string, int, crow::resposne, crow::json::wvalue");
475 
476  handler_ = [f = std::move(f)](const crow::request& req, crow::response& res, Args ... args){
477  res = response(f(req, args...));
478  res.end();
479  };
480  }
481 
482  template <typename Func>
483  typename std::enable_if<
484  !black_magic::CallHelper<Func, black_magic::S<Args...>>::value &&
485  !black_magic::CallHelper<Func, black_magic::S<crow::request, Args...>>::value,
486  void>::type
487  operator()(Func&& f)
488  {
489  static_assert(black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
492  ,
493  "Handler type is mismatched with URL parameters");
494  static_assert(std::is_same<void, decltype(f(std::declval<crow::request>(), std::declval<crow::response&>(), std::declval<Args>()...))>::value,
495  "Handler function with response argument should have void return type");
496 
497  handler_ = std::move(f);
498  }
499 
500  template <typename Func>
501  void operator()(std::string name, Func&& f)
502  {
503  name_ = std::move(name);
504  (*this).template operator()<Func>(std::forward(f));
505  }
506 
507  void handle(const request& req, response& res, const routing_params& params) override
508  {
511  decltype(handler_)>,
512  0, 0, 0, 0,
513  black_magic::S<Args...>,
515  >()(
517  decltype(handler_)>
518  {handler_, params, req, res}
519  );
520  }
521 
522  private:
523  std::function<void(const crow::request&, crow::response&, Args...)> handler_;
524 
525  };
526 
528 
529  class Trie
530  {
531  public:
532  struct Node
533  {
534  unsigned rule_index{};
535  std::array<unsigned, (int)ParamType::MAX> param_childrens{};
536  std::unordered_map<std::string, unsigned> children;
537 
538  bool IsSimpleNode() const
539  {
540  return
541  !rule_index &&
542  std::all_of(
543  std::begin(param_childrens),
544  std::end(param_childrens),
545  [](unsigned x){ return !x; });
546  }
547  };
548 
549  Trie() : nodes_(1)
550  {
551  }
552 
553 private:
555  {
556  for(auto x : node->param_childrens)
557  {
558  if (!x)
559  continue;
560  Node* child = &nodes_[x];
561  optimizeNode(child);
562  }
563  if (node->children.empty())
564  return;
565  bool mergeWithChild = true;
566  for(auto& kv : node->children)
567  {
568  Node* child = &nodes_[kv.second];
569  if (!child->IsSimpleNode())
570  {
571  mergeWithChild = false;
572  break;
573  }
574  }
575  if (mergeWithChild)
576  {
577  decltype(node->children) merged;
578  for(auto& kv : node->children)
579  {
580  Node* child = &nodes_[kv.second];
581  for(auto& child_kv : child->children)
582  {
583  merged[kv.first + child_kv.first] = child_kv.second;
584  }
585  }
586  node->children = std::move(merged);
588  }
589  else
590  {
591  for(auto& kv : node->children)
592  {
593  Node* child = &nodes_[kv.second];
594  optimizeNode(child);
595  }
596  }
597  }
598 
599  void optimize()
600  {
601  optimizeNode(head());
602  }
603 
604 public:
605  void validate()
606  {
607  if (!head()->IsSimpleNode())
608  throw std::runtime_error("Internal error: Trie header should be simple!");
609  optimize();
610  }
611 
612  std::pair<unsigned, routing_params> find(const std::string& req_url, const Node* node = nullptr, unsigned pos = 0, routing_params* params = nullptr) const
613  {
614  routing_params empty;
615  if (params == nullptr)
616  params = &empty;
617 
618  unsigned found{};
619  routing_params match_params;
620 
621  if (node == nullptr)
622  node = head();
623  if (pos == req_url.size())
624  return {node->rule_index, *params};
625 
626  auto update_found = [&found, &match_params](std::pair<unsigned, routing_params>& ret)
627  {
628  if (ret.first && (!found || found > ret.first))
629  {
630  found = ret.first;
631  match_params = std::move(ret.second);
632  }
633  };
634 
635  if (node->param_childrens[(int)ParamType::INT])
636  {
637  char c = req_url[pos];
638  if ((c >= '0' && c <= '9') || c == '+' || c == '-')
639  {
640  char* eptr;
641  errno = 0;
642  long long int value = strtoll(req_url.data()+pos, &eptr, 10);
643  if (errno != ERANGE && eptr != req_url.data()+pos)
644  {
645  params->int_params.push_back(value);
646  auto ret = find(req_url, &nodes_[node->param_childrens[(int)ParamType::INT]], eptr - req_url.data(), params);
647  update_found(ret);
648  params->int_params.pop_back();
649  }
650  }
651  }
652 
653  if (node->param_childrens[(int)ParamType::UINT])
654  {
655  char c = req_url[pos];
656  if ((c >= '0' && c <= '9') || c == '+')
657  {
658  char* eptr;
659  errno = 0;
660  unsigned long long int value = strtoull(req_url.data()+pos, &eptr, 10);
661  if (errno != ERANGE && eptr != req_url.data()+pos)
662  {
663  params->uint_params.push_back(value);
664  auto ret = find(req_url, &nodes_[node->param_childrens[(int)ParamType::UINT]], eptr - req_url.data(), params);
665  update_found(ret);
666  params->uint_params.pop_back();
667  }
668  }
669  }
670 
671  if (node->param_childrens[(int)ParamType::DOUBLE])
672  {
673  char c = req_url[pos];
674  if ((c >= '0' && c <= '9') || c == '+' || c == '-' || c == '.')
675  {
676  char* eptr;
677  errno = 0;
678  double value = strtod(req_url.data()+pos, &eptr);
679  if (errno != ERANGE && eptr != req_url.data()+pos)
680  {
681  params->double_params.push_back(value);
682  auto ret = find(req_url, &nodes_[node->param_childrens[(int)ParamType::DOUBLE]], eptr - req_url.data(), params);
683  update_found(ret);
684  params->double_params.pop_back();
685  }
686  }
687  }
688 
689  if (node->param_childrens[(int)ParamType::STRING])
690  {
691  size_t epos = pos;
692  for(; epos < req_url.size(); epos ++)
693  {
694  if (req_url[epos] == '/')
695  break;
696  }
697 
698  if (epos != pos)
699  {
700  params->string_params.push_back(req_url.substr(pos, epos-pos));
701  auto ret = find(req_url, &nodes_[node->param_childrens[(int)ParamType::STRING]], epos, params);
702  update_found(ret);
703  params->string_params.pop_back();
704  }
705  }
706 
707  if (node->param_childrens[(int)ParamType::PATH])
708  {
709  size_t epos = req_url.size();
710 
711  if (epos != pos)
712  {
713  params->string_params.push_back(req_url.substr(pos, epos-pos));
714  auto ret = find(req_url, &nodes_[node->param_childrens[(int)ParamType::PATH]], epos, params);
715  update_found(ret);
716  params->string_params.pop_back();
717  }
718  }
719 
720  for(auto& kv : node->children)
721  {
722  const std::string& fragment = kv.first;
723  const Node* child = &nodes_[kv.second];
724 
725  if (req_url.compare(pos, fragment.size(), fragment) == 0)
726  {
727  auto ret = find(req_url, child, pos + fragment.size(), params);
728  update_found(ret);
729  }
730  }
731 
732  return {found, match_params};
733  }
734 
735  void add(const std::string& url, unsigned rule_index)
736  {
737  unsigned idx{0};
738 
739  for(unsigned i = 0; i < url.size(); i ++)
740  {
741  char c = url[i];
742  if (c == '<')
743  {
744  static struct ParamTraits
745  {
746  ParamType type;
747  std::string name;
748  } paramTraits[] =
749  {
750  { ParamType::INT, "<int>" },
751  { ParamType::UINT, "<uint>" },
752  { ParamType::DOUBLE, "<float>" },
753  { ParamType::DOUBLE, "<double>" },
754  { ParamType::STRING, "<str>" },
755  { ParamType::STRING, "<string>" },
756  { ParamType::PATH, "<path>" },
757  };
758 
759  for(auto& x:paramTraits)
760  {
761  if (url.compare(i, x.name.size(), x.name) == 0)
762  {
763  if (!nodes_[idx].param_childrens[(int)x.type])
764  {
765  auto new_node_idx = new_node();
766  nodes_[idx].param_childrens[(int)x.type] = new_node_idx;
767  }
768  idx = nodes_[idx].param_childrens[(int)x.type];
769  i += x.name.size();
770  break;
771  }
772  }
773 
774  i --;
775  }
776  else
777  {
778  std::string piece(&c, 1);
779  if (!nodes_[idx].children.count(piece))
780  {
781  auto new_node_idx = new_node();
782  nodes_[idx].children.emplace(piece, new_node_idx);
783  }
784  idx = nodes_[idx].children[piece];
785  }
786  }
787  if (nodes_[idx].rule_index)
788  throw std::runtime_error("handler already exists for " + url);
789  nodes_[idx].rule_index = rule_index;
790  }
791  private:
792  void debug_node_print(Node* n, int level)
793  {
794  for(int i = 0; i < (int)ParamType::MAX; i ++)
795  {
796  if (n->param_childrens[i])
797  {
798  CROW_LOG_DEBUG << std::string(2*level, ' ') /*<< "("<<n->param_childrens[i]<<") "*/;
799  switch((ParamType)i)
800  {
801  case ParamType::INT:
802  CROW_LOG_DEBUG << "<int>";
803  break;
804  case ParamType::UINT:
805  CROW_LOG_DEBUG << "<uint>";
806  break;
807  case ParamType::DOUBLE:
808  CROW_LOG_DEBUG << "<float>";
809  break;
810  case ParamType::STRING:
811  CROW_LOG_DEBUG << "<str>";
812  break;
813  case ParamType::PATH:
814  CROW_LOG_DEBUG << "<path>";
815  break;
816  default:
817  CROW_LOG_DEBUG << "<ERROR>";
818  break;
819  }
820 
821  debug_node_print(&nodes_[n->param_childrens[i]], level+1);
822  }
823  }
824  for(auto& kv : n->children)
825  {
826  CROW_LOG_DEBUG << std::string(2*level, ' ') /*<< "(" << kv.second << ") "*/ << kv.first;
827  debug_node_print(&nodes_[kv.second], level+1);
828  }
829  }
830 
831  public:
832  void debug_print()
833  {
834  debug_node_print(head(), 0);
835  }
836 
837  private:
838  const Node* head() const
839  {
840  return &nodes_.front();
841  }
842 
844  {
845  return &nodes_.front();
846  }
847 
848  unsigned new_node()
849  {
850  nodes_.resize(nodes_.size()+1);
851  return nodes_.size() - 1;
852  }
853 
854  std::vector<Node> nodes_;
855  };
856 
857  class Router
858  {
859  public:
860  Router() : rules_(2)
861  {
862  }
863 
864  DynamicRule& new_rule_dynamic(const std::string& rule)
865  {
866  auto ruleObject = new DynamicRule(rule);
867 
868  internal_add_rule_object(rule, ruleObject);
869 
870  return *ruleObject;
871  }
872 
873  template <uint64_t N>
874  typename black_magic::arguments<N>::type::template rebind<TaggedRule>& new_rule_tagged(const std::string& rule)
875  {
876  using RuleT = typename black_magic::arguments<N>::type::template rebind<TaggedRule>;
877  auto ruleObject = new RuleT(rule);
878 
879  internal_add_rule_object(rule, ruleObject);
880 
881  return *ruleObject;
882  }
883 
884  void internal_add_rule_object(const std::string& rule, BaseRule* ruleObject)
885  {
886  rules_.emplace_back(ruleObject);
887  trie_.add(rule, rules_.size() - 1);
888 
889  // directory case:
890  // request to `/about' url matches `/about/' rule
891  if (rule.size() > 1 && rule.back() == '/')
892  {
893  std::string rule_without_trailing_slash = rule;
894  rule_without_trailing_slash.pop_back();
895  trie_.add(rule_without_trailing_slash, RULE_SPECIAL_REDIRECT_SLASH);
896  }
897  }
898 
899  void validate()
900  {
901  trie_.validate();
902  for(auto& rule:rules_)
903  {
904  if (rule)
905  {
906  auto upgraded = rule->upgrade();
907  if (upgraded)
908  rule = std::move(upgraded);
909  rule->validate();
910  }
911  }
912  }
913 
914  template <typename Adaptor>
915  void handle_upgrade(const request& req, response& res, Adaptor&& adaptor)
916  {
917  auto found = trie_.find(req.url);
918  unsigned rule_index = found.first;
919  if (!rule_index)
920  {
921  CROW_LOG_DEBUG << "Cannot match rules " << req.url;
922  res = response(404);
923  res.end();
924  return;
925  }
926 
927  if (rule_index >= rules_.size())
928  throw std::runtime_error("Trie internal structure corrupted!");
929 
930  if (rule_index == RULE_SPECIAL_REDIRECT_SLASH)
931  {
932  CROW_LOG_INFO << "Redirecting to a url with trailing slash: " << req.url;
933  res = response(301);
934 
935  // TODO absolute url building
936  if (req.get_header_value("Host").empty())
937  {
938  res.add_header("Location", req.url + "/");
939  }
940  else
941  {
942  res.add_header("Location", "http://" + req.get_header_value("Host") + req.url + "/");
943  }
944  res.end();
945  return;
946  }
947 
948  if ((rules_[rule_index]->get_methods() & (1<<(uint32_t)req.method)) == 0)
949  {
950  CROW_LOG_DEBUG << "Rule found but method mismatch: " << req.url << " with " << method_name(req.method) << "(" << (uint32_t)req.method << ") / " << rules_[rule_index]->get_methods();
951  res = response(404);
952  res.end();
953  return;
954  }
955 
956  CROW_LOG_DEBUG << "Matched rule (upgrade) '" << rules_[rule_index]->rule_ << "' " << (uint32_t)req.method << " / " << rules_[rule_index]->get_methods();
957 
958  // any uncaught exceptions become 500s
959  try
960  {
961  rules_[rule_index]->handle_upgrade(req, res, std::move(adaptor));
962  }
963  catch(std::exception& e)
964  {
965  CROW_LOG_ERROR << "An uncaught exception occurred: " << e.what();
966  res = response(500);
967  res.end();
968  return;
969  }
970  catch(...)
971  {
972  CROW_LOG_ERROR << "An uncaught exception occurred. The type was unknown so no information was available.";
973  res = response(500);
974  res.end();
975  return;
976  }
977  }
978 
979  void handle(const request& req, response& res)
980  {
981  auto found = trie_.find(req.url);
982 
983  unsigned rule_index = found.first;
984  CROW_LOG_DEBUG << "???" << rule_index;
985 
986  if (!rule_index)
987  {
988  CROW_LOG_DEBUG << "Cannot match rules " << req.url;
989  res = response(404);
990  res.end();
991  return;
992  }
993 
994  if (rule_index >= rules_.size())
995  throw std::runtime_error("Trie internal structure corrupted!");
996 
997  if (rule_index == RULE_SPECIAL_REDIRECT_SLASH)
998  {
999  CROW_LOG_INFO << "Redirecting to a url with trailing slash: " << req.url;
1000  res = response(301);
1001 
1002  // TODO absolute url building
1003  if (req.get_header_value("Host").empty())
1004  {
1005  res.add_header("Location", req.url + "/");
1006  }
1007  else
1008  {
1009  res.add_header("Location", "http://" + req.get_header_value("Host") + req.url + "/");
1010  }
1011  res.end();
1012  return;
1013  }
1014 
1015  if ((rules_[rule_index]->get_methods() & (1<<(uint32_t)req.method)) == 0)
1016  {
1017  CROW_LOG_DEBUG << "Rule found but method mismatch: " << req.url << " with " << method_name(req.method) << "(" << (uint32_t)req.method << ") / " << rules_[rule_index]->get_methods();
1018  res = response(404);
1019  res.end();
1020  return;
1021  }
1022 
1023  CROW_LOG_DEBUG << "Matched rule '" << rules_[rule_index]->rule_ << "' " << (uint32_t)req.method << " / " << rules_[rule_index]->get_methods();
1024 
1025  // any uncaught exceptions become 500s
1026  try
1027  {
1028  rules_[rule_index]->handle(req, res, found.second);
1029  }
1030  catch(std::exception& e)
1031  {
1032  CROW_LOG_ERROR << "An uncaught exception occurred: " << e.what();
1033  res = response(500);
1034  res.end();
1035  return;
1036  }
1037  catch(...)
1038  {
1039  CROW_LOG_ERROR << "An uncaught exception occurred. The type was unknown so no information was available.";
1040  res = response(500);
1041  res.end();
1042  return;
1043  }
1044  }
1045 
1047  {
1048  trie_.debug_print();
1049  }
1050 
1051  private:
1052  std::vector<std::unique_ptr<BaseRule>> rules_;
1054  };
1055 }
Definition: http_response.h:12
std::function< void(crow::websocket::connection &, const std::string &)> close_handler_
Definition: routing.h:319
std::function< void(const crow::request &, crow::response &, Args...)> type
Definition: routing.h:219
void handle_upgrade(const request &req, response &, SocketAdaptor &&adaptor) override
Definition: routing.h:277
void operator()(std::string name, Func &&f)
Definition: routing.h:501
uint32_t get_methods()
Definition: routing.h:54
TaggedRule(std::string rule)
Definition: routing.h:434
void operator()(const request &req, response &res, const routing_params &params)
Definition: routing.h:239
black_magic::arguments< N >::type::template rebind< TaggedRule > & new_rule_tagged(const std::string &rule)
Definition: routing.h:874
Definition: socket_adaptors.h:12
const Node * head() const
Definition: routing.h:838
uint32_t methods_
Definition: routing.h:60
const uint32_t T[512]
Definition: groestl_tables.h:34
Definition: routing.h:324
virtual void handle_upgrade(const request &, response &res, SocketAdaptor &&)
Definition: routing.h:41
virtual void validate()=0
unsigned new_node()
Definition: routing.h:848
Definition: routing.h:20
Definition: http_request.h:23
const request & req
Definition: routing.h:89
#define F(w, k)
Definition: sha512-blocks.c:61
Definition: routing.h:429
boost::make_recursive_variant< std::nullptr_t, std::string, int, double, bool, uint64_t, int64_t, uint32_t, internal::lambda_t< boost::recursive_variant_ >, std::shared_ptr< internal::object_t< boost::recursive_variant_ > >, internal::map< const std::string, boost::recursive_variant_ >, std::vector< boost::recursive_variant_ > >::type node
Definition: mstch.hpp:116
self_t & onmessage(Func f)
Definition: routing.h:296
Definition: routing.h:857
std::function< void(const crow::request &, crow::response &, Args...)> type
Definition: routing.h:233
void validate() override
Definition: routing.h:439
self_t & onerror(Func f)
Definition: routing.h:310
virtual ~BaseRule()
Definition: routing.h:28
Definition: routing.h:529
const int RULE_SPECIAL_REDIRECT_SLASH
Definition: routing.h:527
void optimizeNode(Node *node)
Definition: routing.h:554
Definition: block_queue.cpp:41
std::vector< std::unique_ptr< BaseRule > > rules_
Definition: routing.h:1052
void operator()(Func f)
Definition: routing.h:379
std::unique_ptr< BaseRule > upgrade()
Definition: routing.h:33
void validate() override
Definition: routing.h:365
void validate() override
Definition: routing.h:267
void operator()(const request &req, response &res, Args... args)
Definition: routing.h:183
Definition: utility.h:237
Trie trie_
Definition: routing.h:1053
void handle_upgrade(const request &req, response &res, Adaptor &&adaptor)
Definition: routing.h:915
Definition: websocket.h:28
std::function< void(crow::websocket::connection &)> error_handler_
Definition: routing.h:320
const std::string & get_header_value(const std::string &key) const
Definition: http_request.h:50
ParamType
Definition: common.h:58
void set2(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)
Definition: routing.h:159
std::function< void(crow::websocket::connection &, const std::string &, bool)> message_handler_
Definition: routing.h:318
std::array< unsigned,(int) ParamType::MAX > param_childrens
Definition: routing.h:535
Definition: utility.h:455
bool IsSimpleNode() const
Definition: routing.h:538
Trie()
Definition: routing.h:549
void set2(Func f, typename std::enable_if< std::is_same< typename std::tuple_element< 0, std::tuple< Args..., void >>::type, const request &>::value &&!std::is_same< typename std::tuple_element< 1, std::tuple< Args..., void, void >>::type, response &>::value, int >::type=0)
Definition: routing.h:193
Definition: utility.h:247
void handle(const request &req, response &res, const routing_params &params) override
Definition: routing.h:373
declaration and default definition for the functions used the API
void handle(const request &req, response &res)
Definition: routing.h:979
const routing_params & params
Definition: routing.h:88
std::string url
Definition: http_request.h:27
void debug_print()
Definition: routing.h:1046
std::function< void(const crow::request &, crow::response &, Args...)> type
Definition: routing.h:226
std::vector< Node > nodes_
Definition: routing.h:854
void debug_print()
Definition: routing.h:832
handler_type_helper< ArgsWrapped... >::type handler_
Definition: routing.h:237
std::unique_ptr< BaseRule > rule_to_upgrade_
Definition: routing.h:65
unsigned rule_index
Definition: routing.h:534
Definition: common.h:69
self_t & onopen(Func f)
Definition: routing.h:289
#define CROW_LOG_ERROR
Definition: logging.h:130
std::enable_if< !black_magic::CallHelper< Func, black_magic::S< Args... > >::value &&!black_magic::CallHelper< Func, black_magic::S< crow::request, Args... > >::value, void >::type operator()(Func &&f)
Definition: routing.h:487
type
Definition: json.h:74
static uint64_t get_parameter_tag_runtime(const char *s, unsigned p=0)
Definition: utility.h:191
Definition: utility.h:329
HTTPMethod method
Definition: http_request.h:25
std::unordered_map< std::string, unsigned > children
Definition: routing.h:536
#define CROW_LOG_DEBUG
Definition: logging.h:139
void handle(const request &req, response &res, const routing_params &params) override
Definition: routing.h:507
std::enable_if< black_magic::CallHelper< Func, black_magic::S< Args... > >::value, void >::type operator()(Func &&f)
Definition: routing.h:449
std::string name_
Definition: routing.h:63
void debug_node_print(Node *n, int level)
Definition: routing.h:792
void internal_add_rule_object(const std::string &rule, BaseRule *ruleObject)
Definition: routing.h:884
void validate()
Definition: routing.h:899
WebSocketRule & websocket()
Definition: routing.h:327
std::enable_if< !black_magic::CallHelper< Func, black_magic::S< Args... > >::value &&black_magic::CallHelper< Func, black_magic::S< crow::request, Args... > >::value, void >::type operator()(Func &&f)
Definition: routing.h:468
Definition: routing.h:532
void optimize()
Definition: routing.h:599
self_t & onclose(Func f)
Definition: routing.h:303
self_t & name(std::string name) noexcept
Definition: routing.h:334
void add(const std::string &url, unsigned rule_index)
Definition: routing.h:735
std::function< void(const request &, response &, const routing_params &)> wrap(Func f, black_magic::seq< Indices... >)
Definition: routing.h:398
DynamicRule(std::string rule)
Definition: routing.h:360
Definition: ci_map.h:7
std::function< void(crow::websocket::connection &)> open_handler_
Definition: routing.h:317
void operator()(std::string name, Func &&f)
Definition: routing.h:418
void validate()
Definition: routing.h:605
std::pair< unsigned, routing_params > find(const std::string &req_url, const Node *node=nullptr, unsigned pos=0, routing_params *params=nullptr) const
Definition: routing.h:612
BaseRule(std::string rule)
Definition: routing.h:23
virtual void handle(const request &, response &, const routing_params &)=0
Node * head()
Definition: routing.h:843
Definition: routing.h:356
const char * name
Definition: simplewallet.cpp:180
void end()
Definition: http_response.h:93
void add_header(std::string key, std::string value)
Definition: http_response.h:29
Definition: utility.h:340
std::function< void(const crow::request &, crow::response &, Args...)> handler_
Definition: routing.h:523
self_t & methods(HTTPMethod method)
Definition: routing.h:340
static bool is_parameter_tag_compatible(uint64_t a, uint64_t b)
Definition: utility.h:167
void set2(Func f, typename std::enable_if< std::is_same< typename std::tuple_element< 0, std::tuple< Args..., void >>::type, const request &>::value &&std::is_same< typename std::tuple_element< 1, std::tuple< Args..., void, void >>::type, response &>::value, int >::type=0)
Definition: routing.h:208
std::string method_name(HTTPMethod method)
Definition: common.h:34
self_t & methods(HTTPMethod method, MethodArgs ... args_method)
Definition: routing.h:347
HTTPMethod
Definition: common.h:11
std::function< void(const request &, response &, const routing_params &)> erased_handler_
Definition: routing.h:424
Definition: routing.h:258
Router()
Definition: routing.h:860
static const int pos
Definition: routing.h:81
std::string rule_
Definition: routing.h:62
void handle(const request &, response &res, const routing_params &) override
Definition: routing.h:271
#define CROW_LOG_INFO
Definition: logging.h:136
Definition: utility.h:300
DynamicRule & new_rule_dynamic(const std::string &rule)
Definition: routing.h:864
WebSocketRule(std::string rule)
Definition: routing.h:262