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