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