Coverage Report

Created: 2026-05-30 06:08

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