• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

CrowCpp / Crow / 982

28 Apr 2026 01:19PM UTC coverage: 87.453% (-0.02%) from 87.474%
982

Pull #1179

gh-actions

web-flow
Merge e678c6e7a into 21f8bcfa6
Pull Request #1179: Bump dawidd6/action-download-artifact from 20 to 21

4175 of 4774 relevant lines covered (87.45%)

155.28 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.75
/include/crow/routing.h
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()
5✔
36
            {}
5✔
37

38
            template<typename App, typename MW, typename... Middlewares>
39
            void push()
6✔
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;
6✔
45
                indices_.push_back(idx);
6✔
46
                push<App, Middlewares...>();
6✔
47
            }
6✔
48

49
            void merge_front(const detail::middleware_indices& other)
8✔
50
            {
51
                indices_.insert(indices_.begin(), other.indices_.cbegin(), other.indices_.cend());
8✔
52
            }
8✔
53

54
            void merge_back(const detail::middleware_indices& other)
7✔
55
            {
56
                indices_.insert(indices_.end(), other.indices_.cbegin(), other.indices_.cend());
7✔
57
            }
7✔
58

59
            void pop_back(const detail::middleware_indices& other)
7✔
60
            {
61
                indices_.resize(indices_.size() - other.indices_.size());
7✔
62
            }
7✔
63

64
            bool empty() const
19✔
65
            {
66
                return indices_.empty();
19✔
67
            }
68

69
            // Sorts indices and filters out duplicates to allow fast lookups with traversal
70
            void pack()
127✔
71
            {
72
                std::sort(indices_.begin(), indices_.end());
127✔
73
                indices_.erase(std::unique(indices_.begin(), indices_.end()), indices_.end());
127✔
74
            }
127✔
75

76
            const std::vector<int>& indices()
4✔
77
            {
78
                return indices_;
4✔
79
            }
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):
143✔
95
          rule_(std::move(rule))
143✔
96
        {}
143✔
97

98
        virtual ~BaseRule()=default;
143✔
99

100
        virtual void validate() = 0;
101

102
        void set_added()
126✔
103
        {
104
            added_ = true;
126✔
105
        }
126✔
106

107
        bool is_added()
149✔
108
        {
109
            return added_;
149✔
110
        }
111

112
        std::unique_ptr<BaseRule> upgrade()
129✔
113
        {
114
            if (rule_to_upgrade_)
129✔
115
                return std::move(rule_to_upgrade_);
11✔
116
            return {};
118✔
117
        }
118

119
        virtual void handle(request&, response&, const routing_params&) = 0;
120
        virtual void handle_upgrade(const request&, response& res, SocketAdaptor&&)
×
121
        {
122
            res = response(404);
×
123
            res.end();
×
124
        }
125
        virtual void handle_upgrade(const request&, response& res, UnixSocketAdaptor&&)
×
126
        {
127
            res = response(404);
×
128
            res.end();
×
129
        }
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()
245✔
139
        {
140
            return methods_;
245✔
141
        }
142

143
        template<typename F>
144
        void foreach_method(F f)
127✔
145
        {
146
            for (uint64_t method = 0, method_bit = 1; method < static_cast<uint64_t>(HTTPMethod::InternalMethodCount); method++, method_bit <<= 1)
4,412✔
147
            {
148
                if (methods_ & method_bit)
4,286✔
149
                    f(method);
130✔
150
            }
151
        }
126✔
152

153
        std::string custom_templates_base;
154

155
        const std::string& rule() { return rule_; }
127✔
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)
4✔
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);
4✔
206
                }
4✔
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)
4✔
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);
4✔
216
                }
4✔
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)
2✔
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);
2✔
226
                }
2✔
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)
3✔
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);
3✔
236
                }
3✔
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)
206✔
243
                {
244
                    cparams.handler(
207✔
245
                      cparams.req,
4✔
246
                      cparams.res,
247
                      cparams.params.template get<typename Args1::type>(Args1::pos)...);
5✔
248
                }
103✔
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)
2✔
256
                {
257
                    handler_ = ([f = std::move(f)](const request&, response& res, Args... args) {
6✔
258
                        res = response(f(args...));
2✔
259
                        res.end();
2✔
260
                    });
261
                }
2✔
262

263
                template<typename Req, typename... Args>
264
                struct req_handler_wrapper
265
                {
266
                    req_handler_wrapper(Func fun):
1✔
267
                      f(std::move(fun))
1✔
268
                    {
269
                    }
1✔
270

271
                    void operator()(const request& req, response& res, Args... args)
1✔
272
                    {
273
                        res = response(f(req, args...));
1✔
274
                        res.end();
1✔
275
                    }
1✔
276

277
                    Func f;
278
                };
279

280
                template<typename... Args>
281
                void set_(Func f, typename std::enable_if<
1✔
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));
1✔
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
                }
1✔
294

295
                template<typename... Args>
296
                void set_(Func f, typename std::enable_if<
1✔
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);
1✔
302
                }
1✔
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)
4✔
328
                {
329
                    detail::routing_handler_call_helper::call<
4✔
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<>>()(
4✔
335
                      detail::routing_handler_call_helper::call_params<
336
                        decltype(handler_)>{handler_, params, req, res});
4✔
337
                }
4✔
338
            };
339

340
        } // namespace routing_handler_call_helper
341
    }     // namespace detail
342

343

344
    class CatchallRule
345
    {
346
    public:
347
        /// @cond SKIP
348
        CatchallRule() {}
63✔
349

350
        template<typename Func>
351
        typename std::enable_if<black_magic::CallHelper<Func, black_magic::S<>>::value, void>::type
352
          operator()(Func&& f)
1✔
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) {
3✔
358
                res = response(f());
2✔
359
                res.end();
2✔
360
            });
361
        }
1✔
362

363
        template<typename Func>
364
        typename std::enable_if<
365
          !black_magic::CallHelper<Func, black_magic::S<>>::value &&
366
            black_magic::CallHelper<Func, black_magic::S<crow::request>>::value,
367
          void>::type
368
          operator()(Func&& f)
369
        {
370
            static_assert(!std::is_same<void, decltype(f(std::declval<crow::request>()))>::value,
371
                          "Handler function cannot have void return type; valid return types: string, int, crow::response, crow::returnable");
372

373
            handler_ = ([f = std::move(f)](const request& req, response& res) {
374
                res = response(f(req));
375
                res.end();
376
            });
377
        }
378

379
        template<typename Func>
380
        typename std::enable_if<
381
          !black_magic::CallHelper<Func, black_magic::S<>>::value &&
382
            !black_magic::CallHelper<Func, black_magic::S<crow::request>>::value &&
383
            black_magic::CallHelper<Func, black_magic::S<crow::response&>>::value,
384
          void>::type
385
          operator()(Func&& f)
1✔
386
        {
387
            static_assert(std::is_same<void, decltype(f(std::declval<crow::response&>()))>::value,
388
                          "Handler function with response argument should have void return type");
389
            handler_ = ([f = std::move(f)](const request&, response& res) {
1✔
390
                f(res);
1✔
391
            });
392
        }
1✔
393

394
        template<typename Func>
395
        typename std::enable_if<
396
          !black_magic::CallHelper<Func, black_magic::S<>>::value &&
397
            !black_magic::CallHelper<Func, black_magic::S<crow::request>>::value &&
398
            !black_magic::CallHelper<Func, black_magic::S<crow::response&>>::value,
399
          void>::type
400
          operator()(Func&& f)
1✔
401
        {
402
            static_assert(std::is_same<void, decltype(f(std::declval<crow::request>(), std::declval<crow::response&>()))>::value,
403
                          "Handler function with response argument should have void return type");
404

405
            handler_ = std::move(f);
1✔
406
        }
1✔
407
        /// @endcond
408
        bool has_handler()
24✔
409
        {
410
            return (handler_ != nullptr);
24✔
411
        }
412

413
    protected:
414
        friend class Router;
415

416
    private:
417
        std::function<void(const crow::request&, crow::response&)> handler_;
418
    };
419

420

421
    /// A rule dealing with websockets.
422

423
    ///
424
    /// Provides the interface for the user to put in the necessary handlers for a websocket to work.
425
    template<typename App>
426
    class WebSocketRule : public BaseRule
427
    {
428
        using self_t = WebSocketRule;
429

430
    public:
431
        WebSocketRule(std::string rule, App* app):
11✔
432
          BaseRule(std::move(rule)),
11✔
433
          app_(app),
11✔
434
          max_payload_(UINT64_MAX)
11✔
435
        {}
11✔
436

437
        void validate() override
11✔
438
        {}
11✔
439

440
        void handle(request&, response& res, const routing_params&) override
×
441
        {
442
            res = response(404);
×
443
            res.end();
×
444
        }
445

446
        void handle_upgrade(const request& req, response&, SocketAdaptor&& adaptor) override
39✔
447
        {
448
            max_payload_ = max_payload_override_ ? max_payload_ : app_->websocket_max_payload();
39✔
449
            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_);
39✔
450
        }
39✔
451

452
        void handle_upgrade(const request& req, response&, UnixSocketAdaptor&& adaptor) override
×
453
        {
454
            max_payload_ = max_payload_override_ ? max_payload_ : app_->websocket_max_payload();
×
455
            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_);
×
456
        }
457

458
#ifdef CROW_ENABLE_SSL
459
        void handle_upgrade(const request& req, response&, SSLAdaptor&& adaptor) override
×
460
        {
461
            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_);
×
462
        }
463
#endif
464

465
        /// Override the global payload limit for this single WebSocket rule
466
        self_t& max_payload(uint64_t max_payload)
467
        {
468
            max_payload_ = max_payload;
469
            max_payload_override_ = true;
470
            return *this;
471
        }
472

473
        self_t& subprotocols(const std::vector<std::string>& subprotocols)
1✔
474
        {
475
            subprotocols_ = subprotocols;
1✔
476
            return *this;
1✔
477
        }
478

479
        /// \brief Set functor that process a client request to open a WebSocket.
480
        ///     The required interface is:
481
        ///         void(crow::websocket::connection& conn)
482
        ///
483
        /// \param f Functor to set.
484
        ///
485
        template<typename Func>
486
        self_t& onopen(Func f)
11✔
487
        {
488
            open_handler_ = f;
11✔
489
            return *this;
11✔
490
        }
491

492
        /// \brief Set functor that process a client message.
493
        ///     The required interface is:
494
        ///         void(crow::websocket::connection& conn, const std::string& msgData, bool is_binary)
495
        ///
496
        /// \param f Functor to set.
497
        ///
498
        template<typename Func>
499
        self_t& onmessage(Func f)
9✔
500
        {
501
            message_handler_ = f;
9✔
502
            return *this;
9✔
503
        }
504

505
        /// \brief Set functor that process a client close.
506
        ///     The required interface is:
507
        ///         void(crow::websocket::connection& conn, const std::string& reason, uint16_t status_code)
508
        ///
509
        /// \param f Functor to set.
510
        ///
511
        template<typename Func>
512
        self_t& onclose(Func f)
11✔
513
        {
514
            close_handler_ = f;
11✔
515
            return *this;
11✔
516
        }
517

518
        /// \brief Set functor that process an error on this WebSocket.
519
        ///     The required interface is:
520
        ///         void(crow::websocket::connection& conn, const std::string& error_message)
521
        ///
522
        /// \param f Functor to set.
523
        ///
524
        template<typename Func>
525
        self_t& onerror(Func f)
526
        {
527
            error_handler_ = f;
528
            return *this;
529
        }
530

531
        /// \brief Set functor that process a client request to start a WebSocket.
532
        ///     The required interface is:
533
        ///         const crow::request& conn, std::optional<crow::response>& response, void** userData)
534
        ///
535
        /// \param callback Functor to set.
536
        ///
537
        self_t& onaccept(std::function<void(const crow::request&, std::optional<crow::response>&, void**)>&& callback)
10✔
538
        {
539
            accept_handler_ = std::move(callback);
10✔
540
            return *this;
10✔
541
        }
542

543
        /// \brief Set functor that process a client request to start a WebSocket.
544
        ///     The required interface is (**without response**):
545
        ///         const crow::request& conn, void** userData)
546
        ///
547
        /// \param callback Functor to set.
548
        ///
549
        self_t& onaccept(std::function<bool(const crow::request&, void**)>&& callback)
10✔
550
        {
551
            onaccept([callback](const crow::request& req, std::optional<crow::response>& res, void** p) {
48✔
552
                if (!callback(req, p))
38✔
553
                {
554
                    res = crow::response(400);
×
555
                }
556
            });
557
            return *this;
10✔
558
        }
559

560
        self_t& mirrorprotocols(bool mirror_protocols = true)
1✔
561
        {
562
            mirror_protocols_ = mirror_protocols;
1✔
563
            return *this;
1✔
564
        }
565

566
    protected:
567
        App* app_;
568
        std::function<void(crow::websocket::connection&)> open_handler_;
569
        std::function<void(crow::websocket::connection&, const std::string&, bool)> message_handler_;
570
        std::function<void(crow::websocket::connection&, const std::string&, uint16_t)> close_handler_;
571
        std::function<void(crow::websocket::connection&, const std::string&)> error_handler_;
572
        std::function<void(const crow::request&, std::optional<crow::response>&, void**)> accept_handler_;
573
        bool mirror_protocols_ = false;
574
        uint64_t max_payload_;
575
        bool max_payload_override_ = false;
576
        std::vector<std::string> subprotocols_;
577
    };
578

579
    /// Allows the user to assign parameters using functions.
580

581
    ///
582
    /// `rule.name("name").methods(HTTPMethod::POST)`
583
    template<typename T>
584
    struct RuleParameterTraits
585
    {
586
        using self_t = T;
587

588
        template<typename App>
589
        WebSocketRule<App>& websocket(App* app)
11✔
590
        {
591
            auto p = new WebSocketRule<App>(static_cast<self_t*>(this)->rule_, app);
11✔
592
            static_cast<self_t*>(this)->rule_to_upgrade_.reset(p);
11✔
593
            return *p;
11✔
594
        }
595

596
        self_t& name(std::string name) noexcept
1✔
597
        {
598
            static_cast<self_t*>(this)->name_ = std::move(name);
1✔
599
            return static_cast<self_t&>(*this);
1✔
600
        }
601

602
        self_t& methods(HTTPMethod method)
12✔
603
        {
604
            static_cast<self_t*>(this)->methods_ = 1ULL << static_cast<int>(method);
12✔
605
            return static_cast<self_t&>(*this);
12✔
606
        }
607

608
        template<typename... MethodArgs>
609
        self_t& methods(HTTPMethod method, MethodArgs... args_method)
3✔
610
        {
611
            methods(args_method...);
3✔
612
            static_cast<self_t*>(this)->methods_ |= 1ULL << static_cast<int>(method);
3✔
613
            return static_cast<self_t&>(*this);
3✔
614
        }
615

616
        /// Enable local middleware for this handler
617
        template<typename App, typename... Middlewares>
618
        self_t& middlewares()
3✔
619
        {
620
            static_cast<self_t*>(this)->mw_indices_.template push<App, Middlewares...>();
3✔
621
            return static_cast<self_t&>(*this);
3✔
622
        }
623
    };
624

625
    /// A rule that can change its parameters during runtime.
626
    class DynamicRule : public BaseRule, public RuleParameterTraits<DynamicRule>
627
    {
628
    public:
629
        DynamicRule(std::string rule):
5✔
630
          BaseRule(std::move(rule))
5✔
631
        {}
5✔
632

633
        void validate() override
5✔
634
        {
635
            if (!erased_handler_)
5✔
636
            {
637
                throw std::runtime_error(name_ + (!name_.empty() ? ": " : "") + "no handler for url " + rule_);
1✔
638
            }
639
        }
4✔
640

641
        void handle(request& req, response& res, const routing_params& params) override
4✔
642
        {
643
            if (!custom_templates_base.empty())
4✔
644
                mustache::set_base(custom_templates_base);
×
645
            else if (mustache::detail::get_template_base_directory_ref() != "templates")
4✔
646
                mustache::set_base("templates");
×
647
            erased_handler_(req, res, params);
4✔
648
        }
4✔
649

650
        template<typename Func>
651
        void operator()(Func f)
5✔
652
        {
653
#ifdef CROW_MSVC_WORKAROUND
654
            using function_t = utility::function_traits<decltype(&Func::operator())>;
655
#else
656
            using function_t = utility::function_traits<Func>;
657
#endif
658
            erased_handler_ = wrap(std::move(f), black_magic::gen_seq<function_t::arity>());
5✔
659
        }
4✔
660

661
        // enable_if Arg1 == request && Arg2 == response
662
        // enable_if Arg1 == request && Arg2 != resposne
663
        // enable_if Arg1 != request
664
#ifdef CROW_MSVC_WORKAROUND
665
        template<typename Func, size_t... Indices>
666
#else
667
        template<typename Func, unsigned... Indices>
668
#endif
669
        std::function<void(request&, response&, const routing_params&)>
670
          wrap(Func f, black_magic::seq<Indices...>)
5✔
671
        {
672
#ifdef CROW_MSVC_WORKAROUND
673
            using function_t = utility::function_traits<decltype(&Func::operator())>;
674
#else
675
            using function_t = utility::function_traits<Func>;
676
#endif
677
            if (!black_magic::is_parameter_tag_compatible(
5✔
678
                  black_magic::get_parameter_tag_runtime(rule_.c_str()),
679
                  black_magic::compute_parameter_tag_from_args_list<
680
                    typename function_t::template arg<Indices>...>::value))
681
            {
682
                throw std::runtime_error("route_dynamic: Handler type is mismatched with URL parameters: " + rule_);
1✔
683
            }
684
            auto ret = detail::routing_handler_call_helper::Wrapped<Func, typename function_t::template arg<Indices>...>();
4✔
685
            ret.template set_<
686
              typename function_t::template arg<Indices>...>(std::move(f));
4✔
687
            return ret;
8✔
688
        }
4✔
689

690
        template<typename Func>
691
        void operator()(std::string name, Func&& f)
692
        {
693
            name_ = std::move(name);
694
            (*this).template operator()<Func>(std::forward(f));
695
        }
696

697
    private:
698
        std::function<void(request&, response&, const routing_params&)> erased_handler_;
699
    };
700

701
    /// Default rule created when CROW_ROUTE is called.
702
    template<typename... Args>
703
    class TaggedRule : public BaseRule, public RuleParameterTraits<TaggedRule<Args...>>
704
    {
705
    public:
706
        using self_t = TaggedRule<Args...>;
707

708
        TaggedRule(std::string rule):
127✔
709
          BaseRule(std::move(rule))
127✔
710
        {}
127✔
711

712
        void validate() override
116✔
713
        {
714
            if (rule_.at(0) != '/')
116✔
715
                throw std::runtime_error("Internal error: Routes must start with a '/'");
1✔
716

717
            if (!handler_)
115✔
718
            {
719
                throw std::runtime_error(name_ + (!name_.empty() ? ": " : "") + "no handler for url " + rule_);
1✔
720
            }
721
        }
114✔
722

723
        template<typename Func>
724
        void operator()(Func&& f)
117✔
725
        {
726
            handler_ = ([f = std::move(f)](request& req, response& res, Args... args) {
416✔
727
                detail::wrapped_handler_call(req, res, f, std::forward<Args>(args)...);
202✔
728
            });
729
        }
117✔
730

731
        template<typename Func>
732
        void operator()(std::string name, Func&& f)
733
        {
734
            name_ = std::move(name);
735
            (*this).template operator()<Func>(std::forward(f));
736
        }
737

738
        void handle(request& req, response& res, const routing_params& params) override
202✔
739
        {
740
            if (!custom_templates_base.empty())
202✔
741
                mustache::set_base(custom_templates_base);
4✔
742
            else if (mustache::detail::get_template_base_directory_ref() != mustache::detail::get_global_template_base_directory_ref())
198✔
743
                mustache::set_base(mustache::detail::get_global_template_base_directory_ref());
69✔
744

745
            detail::routing_handler_call_helper::call<
202✔
746
              detail::routing_handler_call_helper::call_params<decltype(handler_)>,
747
              0, 0, 0, 0,
748
              black_magic::S<Args...>,
749
              black_magic::S<>>()(
202✔
750
              detail::routing_handler_call_helper::call_params<decltype(handler_)>{handler_, params, req, res});
202✔
751
        }
99✔
752

753
    private:
754
        std::function<void(crow::request&, crow::response&, Args...)> handler_;
755
    };
756

757
    using StaticRule = TaggedRule<>;
758

759
    constexpr size_t RULE_SPECIAL_REDIRECT_SLASH = 1;
760

761
    /// A search tree.
762
    class Trie
763
    {
764
    public:
765
        struct Node
766
        {
767
            size_t rule_index{};
768
            // Assign the index to the maximum 32 unsigned integer value by default so that any other number (specifically 0) is a valid BP id.
769
            size_t blueprint_index{INVALID_BP_ID};
770
            std::string key;
771
            ParamType param = ParamType::MAX; // MAX = No param.
772
            std::vector<Node> children;
773

774
            bool IsSimpleNode() const
2,943✔
775
            {
776
                return !rule_index &&
5,847✔
777
                       blueprint_index == INVALID_BP_ID &&
5,801✔
778
                       children.size() < 2 &&
2,898✔
779
                       param == ParamType::MAX &&
8,718✔
780
                       std::all_of(std::begin(children), std::end(children), [](const Node& x) {
8,615✔
781
                           return x.param == ParamType::MAX;
670✔
782
                       });
2,942✔
783
            }
784

785
            Node& add_child_node()
765✔
786
            {
787
                children.emplace_back();
765✔
788
                return children.back();
765✔
789
            }
790
        };
791

792

793
        Trie()
1,904✔
794
        {}
1,904✔
795

796
        /// Check whether or not the trie is empty.
797
        bool is_empty()
67✔
798
        {
799
            return head_.children.empty();
67✔
800
        }
801

802
        void optimize()
2,278✔
803
        {
804
            for (auto& child : head_.children)
2,354✔
805
            {
806
                optimizeNode(child);
76✔
807
            }
808
        }
2,278✔
809

810

811
    private:
812
        void optimizeNode(Node& node)
771✔
813
        {
814
            if (node.children.empty())
771✔
815
                return;
106✔
816
            if (node.IsSimpleNode())
665✔
817
            {
818
                auto children_temp = std::move(node.children);
540✔
819
                auto& child_temp = children_temp[0];
540✔
820
                node.key += child_temp.key;
540✔
821
                node.rule_index = child_temp.rule_index;
540✔
822
                node.blueprint_index = child_temp.blueprint_index;
540✔
823
                node.children = std::move(child_temp.children);
540✔
824
                optimizeNode(node);
540✔
825
            }
540✔
826
            else
827
            {
828
                for (auto& child : node.children)
280✔
829
                {
830
                    optimizeNode(child);
155✔
831
                }
832
            }
833
        }
834

835
        void debug_node_print(const Node& node, size_t level)
7✔
836
        {
837
            if (node.param != ParamType::MAX)
7✔
838
            {
839
                switch (node.param)
×
840
                {
841
                    case ParamType::INT:
×
842
                        CROW_LOG_DEBUG << std::string(3 * level, ' ') << "└➝ "
×
843
                                       << "<int>";
×
844
                        break;
×
845
                    case ParamType::UINT:
×
846
                        CROW_LOG_DEBUG << std::string(3 * level, ' ') << "└➝ "
×
847
                                       << "<uint>";
×
848
                        break;
×
849
                    case ParamType::DOUBLE:
×
850
                        CROW_LOG_DEBUG << std::string(3 * level, ' ') << "└➝ "
×
851
                                       << "<double>";
×
852
                        break;
×
853
                    case ParamType::STRING:
×
854
                        CROW_LOG_DEBUG << std::string(3 * level, ' ') << "└➝ "
×
855
                                       << "<string>";
×
856
                        break;
×
857
                    case ParamType::PATH:
×
858
                        CROW_LOG_DEBUG << std::string(3 * level, ' ') << "└➝ "
×
859
                                       << "<path>";
×
860
                        break;
×
861
                    default:
×
862
                        CROW_LOG_DEBUG << std::string(3 * level, ' ') << "└➝ "
×
863
                                       << "<ERROR>";
×
864
                        break;
×
865
                }
866
            }
867
            else
868
                CROW_LOG_DEBUG << std::string(3 * level, ' ') << "└➝ " << node.key;
21✔
869

870
            for (const auto& child : node.children)
9✔
871
            {
872
                debug_node_print(child, level + 1);
2✔
873
            }
874
        }
7✔
875

876
    public:
877
        void debug_print()
5✔
878
        {
879
            CROW_LOG_DEBUG << "└➙ ROOT";
5✔
880
            for (const auto& child : head_.children)
10✔
881
                debug_node_print(child, 1);
5✔
882
        }
5✔
883

884
        void validate()
2,278✔
885
        {
886
            if (!head_.IsSimpleNode())
2,278✔
887
                throw std::runtime_error("Internal error: Trie header should be simple!");
×
888
            optimize();
2,278✔
889
        }
2,278✔
890

891
        //Rule_index, Blueprint_index, routing_params
892
        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
1,285✔
893
        {
894
            //start params as an empty struct
895
            routing_params empty;
1,285✔
896
            if (params == nullptr)
1,285✔
897
                params = &empty;
813✔
898
            //same for blueprint vector
899
            std::vector<size_t> MT;
1,285✔
900
            if (blueprints == nullptr)
1,285✔
901
                blueprints = &MT;
813✔
902

903
            size_t found{};               //The rule index to be found
1,285✔
904
            std::vector<size_t> found_BP; //The Blueprint indices to be found
1,285✔
905
            routing_params match_params;    //supposedly the final matched parameters
1,285✔
906

907
            auto update_found = [&found, &found_BP, &match_params](routing_handle_result& ret) {
472✔
908
                found_BP = std::move(ret.blueprint_indices);
472✔
909
                if (ret.rule_index && (!found || found > ret.rule_index))
472✔
910
                {
911
                    found = ret.rule_index;
422✔
912
                    match_params = std::move(ret.r_params);
422✔
913
                }
914
            };
1,757✔
915

916
            //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
917
            if (pos == req_url.size())
1,285✔
918
            {
919
                found_BP = std::move(*blueprints);
296✔
920
                return routing_handle_result{node.rule_index, *blueprints, *params};
296✔
921
            }
922

923
            bool found_fragment = false;
989✔
924

925
            for (const auto& child : node.children)
1,648✔
926
            {
927
                if (child.param != ParamType::MAX)
659✔
928
                {
929
                    if (child.param == ParamType::INT)
13✔
930
                    {
931
                        char c = req_url[pos];
4✔
932
                        if ((c >= '0' && c <= '9') || c == '+' || c == '-')
4✔
933
                        {
934
                            char* eptr;
935
                            errno = 0;
4✔
936
                            long long int value = strtoll(req_url.data() + pos, &eptr, 10);
4✔
937
                            if (errno != ERANGE && eptr != req_url.data() + pos)
4✔
938
                            {
939
                                found_fragment = true;
4✔
940
                                params->int_params.push_back(value);
4✔
941
                                if (child.blueprint_index != INVALID_BP_ID) blueprints->push_back(child.blueprint_index);
4✔
942
                                auto ret = find(req_url, child, eptr - req_url.data(), params, blueprints);
4✔
943
                                update_found(ret);
4✔
944
                                params->int_params.pop_back();
4✔
945
                                if (!blueprints->empty()) blueprints->pop_back();
4✔
946
                            }
4✔
947
                        }
948
                    }
949

950
                    else if (child.param == ParamType::UINT)
9✔
951
                    {
952
                        char c = req_url[pos];
4✔
953
                        if ((c >= '0' && c <= '9') || c == '+')
4✔
954
                        {
955
                            char* eptr;
956
                            errno = 0;
4✔
957
                            unsigned long long int value = strtoull(req_url.data() + pos, &eptr, 10);
4✔
958
                            if (errno != ERANGE && eptr != req_url.data() + pos)
4✔
959
                            {
960
                                found_fragment = true;
4✔
961
                                params->uint_params.push_back(value);
4✔
962
                                if (child.blueprint_index != INVALID_BP_ID) blueprints->push_back(child.blueprint_index);
4✔
963
                                auto ret = find(req_url, child, eptr - req_url.data(), params, blueprints);
4✔
964
                                update_found(ret);
4✔
965
                                params->uint_params.pop_back();
4✔
966
                                if (!blueprints->empty()) blueprints->pop_back();
4✔
967
                            }
4✔
968
                        }
969
                    }
970

971
                    else if (child.param == ParamType::DOUBLE)
5✔
972
                    {
973
                        char c = req_url[pos];
2✔
974
                        if ((c >= '0' && c <= '9') || c == '+' || c == '-' || c == '.')
2✔
975
                        {
976
                            char* eptr;
977
                            errno = 0;
2✔
978
                            double value = strtod(req_url.data() + pos, &eptr);
2✔
979
                            if (errno != ERANGE && eptr != req_url.data() + pos)
2✔
980
                            {
981
                                found_fragment = true;
2✔
982
                                params->double_params.push_back(value);
2✔
983
                                if (child.blueprint_index != INVALID_BP_ID) blueprints->push_back(child.blueprint_index);
2✔
984
                                auto ret = find(req_url, child, eptr - req_url.data(), params, blueprints);
2✔
985
                                update_found(ret);
2✔
986
                                params->double_params.pop_back();
2✔
987
                                if (!blueprints->empty()) blueprints->pop_back();
2✔
988
                            }
2✔
989
                        }
990
                    }
991

992
                    else if (child.param == ParamType::STRING)
3✔
993
                    {
994
                        size_t epos = pos;
2✔
995
                        for (; epos < req_url.size(); epos++)
21✔
996
                        {
997
                            if (req_url[epos] == '/')
20✔
998
                                break;
1✔
999
                        }
1000

1001
                        if (epos != pos)
2✔
1002
                        {
1003
                            found_fragment = true;
2✔
1004
                            params->string_params.push_back(req_url.substr(pos, epos - pos));
2✔
1005
                            if (child.blueprint_index != INVALID_BP_ID) blueprints->push_back(child.blueprint_index);
2✔
1006
                            auto ret = find(req_url, child, epos, params, blueprints);
2✔
1007
                            update_found(ret);
2✔
1008
                            params->string_params.pop_back();
2✔
1009
                            if (!blueprints->empty()) blueprints->pop_back();
2✔
1010
                        }
2✔
1011
                    }
1012

1013
                    else if (child.param == ParamType::PATH)
1✔
1014
                    {
1015
                        size_t epos = req_url.size();
1✔
1016

1017
                        if (epos != pos)
1✔
1018
                        {
1019
                            found_fragment = true;
1✔
1020
                            params->string_params.push_back(req_url.substr(pos, epos - pos));
1✔
1021
                            if (child.blueprint_index != INVALID_BP_ID) blueprints->push_back(child.blueprint_index);
1✔
1022
                            auto ret = find(req_url, child, epos, params, blueprints);
1✔
1023
                            update_found(ret);
1✔
1024
                            params->string_params.pop_back();
1✔
1025
                            if (!blueprints->empty()) blueprints->pop_back();
1✔
1026
                        }
1✔
1027
                    }
1028
                }
1029

1030
                else
1031
                {
1032
                    const std::string& fragment = child.key;
646✔
1033
                    if (req_url.compare(pos, fragment.size(), fragment) == 0)
646✔
1034
                    {
1035
                        found_fragment = true;
459✔
1036
                        if (child.blueprint_index != INVALID_BP_ID) blueprints->push_back(child.blueprint_index);
459✔
1037
                        auto ret = find(req_url, child, pos + fragment.size(), params, blueprints);
459✔
1038
                        update_found(ret);
459✔
1039
                        if (!blueprints->empty()) blueprints->pop_back();
459✔
1040
                    }
459✔
1041
                }
1042
            }
1043

1044
            if (!found_fragment)
989✔
1045
                found_BP = std::move(*blueprints);
517✔
1046

1047
            return routing_handle_result{found, found_BP, match_params}; //Called after all the recursions have been done
989✔
1048
        }
1,285✔
1049

1050
        routing_handle_result find(const std::string& req_url) const
813✔
1051
        {
1052
            return find(req_url, head_);
813✔
1053
        }
1054

1055
        //This functions assumes any blueprint info passed is valid
1056
        void add(const std::string& url, size_t rule_index, unsigned bp_prefix_length = 0, size_t blueprint_index = INVALID_BP_ID)
132✔
1057
        {
1058
            auto idx = &head_;
132✔
1059

1060
            bool has_blueprint = bp_prefix_length != 0 && blueprint_index != INVALID_BP_ID;
132✔
1061

1062
            for (unsigned i = 0; i < url.size(); i++)
1,060✔
1063
            {
1064
                char c = url[i];
928✔
1065
                if (c == '<')
928✔
1066
                {
1067
                    static struct ParamTraits
1068
                    {
1069
                        ParamType type;
1070
                        std::string name;
1071
                    } paramTraits[] =
1072
                      {
1073
                        {ParamType::INT, "<int>"},
1074
                        {ParamType::UINT, "<uint>"},
1075
                        {ParamType::DOUBLE, "<float>"},
1076
                        {ParamType::DOUBLE, "<double>"},
1077
                        {ParamType::STRING, "<str>"},
1078
                        {ParamType::STRING, "<string>"},
1079
                        {ParamType::PATH, "<path>"},
1080
                      };
59✔
1081

1082
                    for (const auto& x : paramTraits)
333✔
1083
                    {
1084
                        if (url.compare(i, x.name.size(), x.name) == 0)
333✔
1085
                        {
1086
                            bool found = false;
55✔
1087
                            for (auto& child : idx->children)
55✔
1088
                            {
1089
                                if (child.param == x.type)
×
1090
                                {
1091
                                    idx = &child;
×
1092
                                    i += x.name.size();
×
1093
                                    found = true;
×
1094
                                    break;
×
1095
                                }
1096
                            }
1097
                            if (found)
55✔
1098
                                break;
×
1099

1100
                            auto new_node_idx = &idx->add_child_node();
55✔
1101
                            new_node_idx->param = x.type;
55✔
1102
                            idx = new_node_idx;
55✔
1103
                            i += x.name.size();
55✔
1104
                            break;
55✔
1105
                        }
1106
                    }
1107

1108
                    i--;
55✔
1109
                }
1110
                else
1111
                {
1112
                    //This part assumes the tree is unoptimized (every node has a max 1 character key)
1113
                    bool piece_found = false;
873✔
1114
                    for (auto& child : idx->children)
927✔
1115
                    {
1116
                        if (child.key[0] == c)
217✔
1117
                        {
1118
                            idx = &child;
163✔
1119
                            piece_found = true;
163✔
1120
                            break;
163✔
1121
                        }
1122
                    }
1123
                    if (!piece_found)
873✔
1124
                    {
1125
                        auto new_node_idx = &idx->add_child_node();
710✔
1126
                        new_node_idx->key = c;
710✔
1127
                        //The assumption here is that you'd only need to add a blueprint index if the tree didn't have the BP prefix.
1128
                        if (has_blueprint && i == bp_prefix_length)
710✔
1129
                            new_node_idx->blueprint_index = blueprint_index;
7✔
1130
                        idx = new_node_idx;
710✔
1131
                    }
1132
                }
1133
            }
1134

1135
            //check if the last node already has a value (exact url already in Trie)
1136
            if (idx->rule_index)
132✔
1137
                throw std::runtime_error("handler already exists for " + url);
1✔
1138
            idx->rule_index = rule_index;
131✔
1139
        }
131✔
1140

1141
    private:
1142
        Node head_;
1143
    };
1144

1145
    /// A blueprint can be considered a smaller section of a Crow app, specifically where the router is concerned.
1146

1147
    ///
1148
    /// You can use blueprints to assign a common prefix to rules' prefix, set custom static and template folders, and set a custom catchall route.
1149
    /// You can also assign nest blueprints for maximum Compartmentalization.
1150
    class Blueprint
1151
    {
1152
    public:
1153
        Blueprint(const std::string& prefix)
2✔
1154
          : prefix_(prefix),
2✔
1155
            static_dir_(prefix),
2✔
1156
            templates_dir_(prefix)
2✔
1157
            {}
2✔
1158

1159
        Blueprint(const std::string& prefix, const std::string& static_dir):
1160
          prefix_(prefix), static_dir_(static_dir){}
1161

1162
        Blueprint(const std::string& prefix, const std::string& static_dir, const std::string& templates_dir):
5✔
1163
          prefix_(prefix), static_dir_(static_dir), templates_dir_(templates_dir){}
5✔
1164

1165
        /*
1166
        Blueprint(Blueprint& other)
1167
        {
1168
            prefix_ = std::move(other.prefix_);
1169
            all_rules_ = std::move(other.all_rules_);
1170
        }
1171

1172
        Blueprint(const Blueprint& other)
1173
        {
1174
            prefix_ = other.prefix_;
1175
            all_rules_ = other.all_rules_;
1176
        }
1177
*/
1178
        Blueprint(Blueprint&& value)
1179
        {
1180
            *this = std::move(value);
1181
        }
1182

1183
        Blueprint& operator=(const Blueprint& value) = delete;
1184

1185
        Blueprint& operator=(Blueprint&& value) noexcept
1186
        {
1187
            prefix_ = std::move(value.prefix_);
1188
            static_dir_ = std::move(value.static_dir_);
1189
            templates_dir_ = std::move(value.templates_dir_);
1190
            all_rules_ = std::move(value.all_rules_);
1191
            catchall_rule_ = std::move(value.catchall_rule_);
1192
            blueprints_ = std::move(value.blueprints_);
1193
            mw_indices_ = std::move(value.mw_indices_);
1194
            return *this;
1195
        }
1196

1197
        bool operator==(const Blueprint& value)
1198
        {
1199
            return value.prefix() == prefix_;
1200
        }
1201

1202
        bool operator!=(const Blueprint& value)
1203
        {
1204
            return value.prefix() != prefix_;
1205
        }
1206

1207
        std::string prefix() const
29✔
1208
        {
1209
            return prefix_;
29✔
1210
        }
1211

1212
        std::string static_dir() const
6✔
1213
        {
1214
            return static_dir_;
6✔
1215
        }
1216

1217
        void set_added()
7✔
1218
        {
1219
            added_ = true;
7✔
1220
        }
7✔
1221

1222
        bool is_added()
7✔
1223
        {
1224
            return added_;
7✔
1225
        }
1226

1227
        DynamicRule& new_rule_dynamic(const std::string& rule)
1228
        {
1229
            std::string new_rule = '/' + prefix_ + rule;
1230
            auto ruleObject = new DynamicRule(std::move(new_rule));
1231
            ruleObject->custom_templates_base = templates_dir_;
1232
            all_rules_.emplace_back(ruleObject);
1233

1234
            return *ruleObject;
1235
        }
1236

1237
        template<uint64_t N>
1238
        typename black_magic::arguments<N>::type::template rebind<TaggedRule>& new_rule_tagged(const std::string& rule)
8✔
1239
        {
1240
            std::string new_rule = '/' + prefix_ + rule;
8✔
1241
            using RuleT = typename black_magic::arguments<N>::type::template rebind<TaggedRule>;
1242

1243
            auto ruleObject = new RuleT(std::move(new_rule));
8✔
1244
            ruleObject->custom_templates_base = templates_dir_;
8✔
1245
            all_rules_.emplace_back(ruleObject);
8✔
1246

1247
            return *ruleObject;
8✔
1248
        }
8✔
1249

1250
        void register_blueprint(Blueprint& blueprint)
4✔
1251
        {
1252
            if (blueprints_.empty() || std::find(blueprints_.begin(), blueprints_.end(), &blueprint) == blueprints_.end())
4✔
1253
            {
1254
                apply_blueprint(blueprint);
4✔
1255
                blueprints_.emplace_back(&blueprint);
4✔
1256
            }
1257
            else
1258
                throw std::runtime_error("blueprint \"" + blueprint.prefix_ + "\" already exists in blueprint \"" + prefix_ + '\"');
×
1259
        }
4✔
1260

1261

1262
        CatchallRule& catchall_rule()
9✔
1263
        {
1264
            return catchall_rule_;
9✔
1265
        }
1266

1267
        template<typename App, typename... Middlewares>
1268
        void middlewares()
2✔
1269
        {
1270
            mw_indices_.push<App, Middlewares...>();
2✔
1271
        }
2✔
1272

1273
    private:
1274
        void apply_blueprint(Blueprint& blueprint)
4✔
1275
        {
1276

1277
            blueprint.prefix_ = prefix_ + '/' + blueprint.prefix_;
4✔
1278
            blueprint.static_dir_ = static_dir_ + '/' + blueprint.static_dir_;
4✔
1279
            blueprint.templates_dir_ = templates_dir_ + '/' + blueprint.templates_dir_;
4✔
1280
            for (auto& rule : blueprint.all_rules_)
8✔
1281
            {
1282
                std::string new_rule = '/' + prefix_ + rule->rule_;
4✔
1283
                rule->rule_ = new_rule;
4✔
1284
            }
4✔
1285
            for (Blueprint* bp_child : blueprint.blueprints_)
4✔
1286
            {
1287
                Blueprint& bp_ref = *bp_child;
×
1288
                apply_blueprint(bp_ref);
×
1289
            }
1290
        }
4✔
1291

1292
        std::string prefix_;
1293
        std::string static_dir_;
1294
        std::string templates_dir_;
1295
        std::vector<std::unique_ptr<BaseRule>> all_rules_;
1296
        CatchallRule catchall_rule_;
1297
        std::vector<Blueprint*> blueprints_;
1298
        detail::middleware_indices mw_indices_;
1299
        bool added_{false};
1300

1301
        friend class Router;
1302
    };
1303

1304
    /// Handles matching requests to existing rules and upgrade requests.
1305
    class Router {
1306
    public:
1307
        bool using_ssl;
1308

1309
        Router() : using_ssl(false)
56✔
1310
        {}
56✔
1311

1312
        DynamicRule& new_rule_dynamic(const std::string& rule)
5✔
1313
        {
1314
            auto ruleObject = new DynamicRule(rule);
5✔
1315
            all_rules_.emplace_back(ruleObject);
5✔
1316

1317
            return *ruleObject;
5✔
1318
        }
1319

1320
        template<uint64_t N>
1321
        typename black_magic::arguments<N>::type::template rebind<TaggedRule>& new_rule_tagged(const std::string& rule)
115✔
1322
        {
1323
            using RuleT = typename black_magic::arguments<N>::type::template rebind<TaggedRule>;
1324

1325
            return new_rule<RuleT>(rule);
115✔
1326
        }
1327

1328
        template<typename RuleT=StaticRule>
1329
        auto& new_rule(const std::string& rule)
118✔
1330
        {
1331
            auto ruleObject = new RuleT(rule);
118✔
1332
            all_rules_.emplace_back(ruleObject);
118✔
1333

1334
            return *ruleObject;
118✔
1335
        }
1336

1337
        CatchallRule& catchall_rule()
2✔
1338
        {
1339
            return catchall_rule_;
2✔
1340
        }
1341

1342
        void internal_add_rule_object(const std::string& rule, BaseRule* ruleObject)
119✔
1343
        {
1344
            internal_add_rule_object(rule, ruleObject, INVALID_BP_ID, blueprints_);
119✔
1345
        }
118✔
1346

1347
        void internal_add_rule_object(const std::string& rule, BaseRule* ruleObject, const size_t& BP_index, std::vector<Blueprint*>& blueprints)
127✔
1348
        {
1349
            bool has_trailing_slash = false;
127✔
1350
            std::string rule_without_trailing_slash;
127✔
1351
            if (rule.size() > 1 && rule.back() == '/')
127✔
1352
            {
1353
                has_trailing_slash = true;
2✔
1354
                rule_without_trailing_slash = rule;
2✔
1355
                rule_without_trailing_slash.pop_back();
2✔
1356
            }
1357

1358
            ruleObject->mw_indices_.pack();
127✔
1359

1360
            ruleObject->foreach_method([&](int method) {
127✔
1361
                per_methods_[method].rules.emplace_back(ruleObject);
130✔
1362
                per_methods_[method].trie.add(rule, per_methods_[method].rules.size() - 1,
260✔
1363
                    BP_index != INVALID_BP_ID ? blueprints[BP_index]->prefix().length() : 0,
261✔
1364
                    BP_index);
1365

1366
                // directory case:
1367
                //   request to '/about' url matches '/about/' rule
1368
                if (has_trailing_slash)
129✔
1369
                {
1370
                    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);
2✔
1371
                }
1372
            });
129✔
1373

1374
            ruleObject->set_added();
126✔
1375
        }
127✔
1376

1377
        void register_blueprint(Blueprint& blueprint)
3✔
1378
        {
1379
            if (std::find(blueprints_.begin(), blueprints_.end(), &blueprint) == blueprints_.end())
3✔
1380
            {
1381
                blueprints_.emplace_back(&blueprint);
3✔
1382
            }
1383
            else
1384
                throw std::runtime_error("blueprint \"" + blueprint.prefix_ + "\" already exists in router");
×
1385
        }
3✔
1386

1387
        void get_recursive_child_methods(Blueprint* blueprint, std::vector<HTTPMethod>& methods)
×
1388
        {
1389
            //we only need to deal with children if the blueprint has absolutely no methods (meaning its index won't be added to the trie)
1390
            if (blueprint->static_dir_.empty() && blueprint->all_rules_.empty())
×
1391
            {
1392
                for (Blueprint* bp : blueprint->blueprints_)
×
1393
                {
1394
                    get_recursive_child_methods(bp, methods);
×
1395
                }
1396
            }
1397
            else if (!blueprint->static_dir_.empty())
×
1398
                methods.emplace_back(HTTPMethod::Get);
×
1399
            for (auto& rule : blueprint->all_rules_)
×
1400
            {
1401
                rule->foreach_method([&methods](unsigned method) {
×
1402
                    HTTPMethod method_final = static_cast<HTTPMethod>(method);
×
1403
                    if (std::find(methods.begin(), methods.end(), method_final) == methods.end())
×
1404
                        methods.emplace_back(method_final);
×
1405
                });
×
1406
            }
1407
        }
1408

1409
        void validate_bp()
39✔
1410
        {
1411
            //Take all the routes from the registered blueprints and add them to `all_rules_` to be processed.
1412
            detail::middleware_indices blueprint_mw;
39✔
1413
            validate_bp(blueprints_, blueprint_mw);
39✔
1414
        }
39✔
1415

1416
        void validate_bp(std::vector<Blueprint*> blueprints, detail::middleware_indices& current_mw)
46✔
1417
        {
1418
            for (unsigned i = 0; i < blueprints.size(); i++)
53✔
1419
            {
1420
                Blueprint* blueprint = blueprints[i];
7✔
1421

1422
                if (blueprint->is_added()) continue;
7✔
1423

1424
                if (blueprint->static_dir_ == "" && blueprint->all_rules_.empty())
7✔
1425
                {
1426
                    std::vector<HTTPMethod> methods;
×
1427
                    get_recursive_child_methods(blueprint, methods);
×
1428
                    for (HTTPMethod x : methods)
×
1429
                    {
1430
                        int method_index = static_cast<int>(x);
×
1431
                        per_methods_[method_index].trie.add(blueprint->prefix(), 0, blueprint->prefix().length(), method_index);
×
1432
                    }
1433
                }
1434

1435
                current_mw.merge_back(blueprint->mw_indices_);
7✔
1436
                for (auto& rule : blueprint->all_rules_)
15✔
1437
                {
1438
                    if (rule && !rule->is_added())
8✔
1439
                    {
1440
                        auto upgraded = rule->upgrade();
8✔
1441
                        if (upgraded)
8✔
1442
                            rule = std::move(upgraded);
×
1443
                        rule->validate();
8✔
1444
                        rule->mw_indices_.merge_front(current_mw);
8✔
1445
                        internal_add_rule_object(rule->rule(), rule.get(), i, blueprints);
8✔
1446
                    }
8✔
1447
                }
1448
                validate_bp(blueprint->blueprints_, current_mw);
7✔
1449
                current_mw.pop_back(blueprint->mw_indices_);
7✔
1450
                blueprint->set_added();
7✔
1451
            }
1452
        }
46✔
1453

1454
        void validate()
70✔
1455
        {
1456
            for (auto& rule : all_rules_)
208✔
1457
            {
1458
                if (rule && !rule->is_added())
141✔
1459
                {
1460
                    auto upgraded = rule->upgrade();
121✔
1461
                    if (upgraded)
121✔
1462
                        rule = std::move(upgraded);
11✔
1463
                    rule->validate();
121✔
1464
                    internal_add_rule_object(rule->rule(), rule.get());
119✔
1465
                }
121✔
1466
            }
1467
            for (auto& per_method : per_methods_)
2,345✔
1468
            {
1469
                per_method.trie.validate();
2,278✔
1470
            }
1471
        }
67✔
1472

1473
        // TODO maybe add actual_method
1474
        template<typename Adaptor>
1475
        void handle_upgrade(const request& req, response& res, Adaptor&& adaptor)
39✔
1476
        {
1477
            if (req.method >= HTTPMethod::InternalMethodCount)
39✔
1478
                return;
×
1479

1480
            auto& per_method = per_methods_[static_cast<int>(req.method)];
39✔
1481
            auto& rules = per_method.rules;
39✔
1482
            size_t rule_index = per_method.trie.find(req.url).rule_index;
39✔
1483

1484
            if (!rule_index)
39✔
1485
            {
1486
                for (auto& method : per_methods_)
×
1487
                {
1488
                    if (method.trie.find(req.url).rule_index)
×
1489
                    {
1490
                        CROW_LOG_DEBUG << "Cannot match method " << req.url << " " << method_name(req.method);
×
1491
                        res = response(405);
×
1492
                        res.end();
×
1493
                        return;
×
1494
                    }
1495
                }
1496

1497
                CROW_LOG_INFO << "Cannot match rules " << req.url;
×
1498
                res = response(404);
×
1499
                res.end();
×
1500
                return;
×
1501
            }
1502

1503
            if (rule_index >= rules.size())
39✔
1504
                throw std::runtime_error("Trie internal structure corrupted!");
×
1505

1506
            if (rule_index == RULE_SPECIAL_REDIRECT_SLASH)
39✔
1507
            {
1508
                CROW_LOG_INFO << "Redirecting to a url with trailing slash: " << req.url;
×
1509
                res = response(301);
×
1510
                res.add_header("Location", req.url + "/");
×
1511
                res.end();
×
1512
                return;
×
1513
            }
1514

1515
            CROW_LOG_DEBUG << "Matched rule (upgrade) '" << rules[rule_index]->rule_ << "' "
78✔
1516
                           << static_cast<uint64_t>(req.method) << " / "
39✔
1517
                           << rules[rule_index]->get_methods();
39✔
1518

1519
            try
1520
            {
1521
                rules[rule_index]->handle_upgrade(req, res, std::move(adaptor));
39✔
1522
            }
1523
            catch (...)
×
1524
            {
1525
                exception_handler_(res);
×
1526
                res.end();
×
1527
                return;
×
1528
            }
1529
        }
1530

1531
        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)
38✔
1532
        {
1533
            // This statement makes 3 assertions:
1534
            // 1. The index is above 0.
1535
            // 2. The index does not lie outside the given blueprint list.
1536
            // 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).
1537
            //
1538
            // 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".
1539
            //
1540
            // 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
1541
            auto verify_prefix = [&bp_i, &index, &blueprints, &found_bps]() {
16✔
1542
                return index > 0 &&
8✔
1543
                       bp_i[index] < blueprints.size() &&
24✔
1544
                       blueprints[bp_i[index]]->prefix().substr(0, found_bps[index - 1]->prefix().length() + 1).compare(std::string(found_bps[index - 1]->prefix() + '/')) == 0;
22✔
1545
            };
38✔
1546
            if (index < bp_i.size())
38✔
1547
            {
1548

1549
                if (verify_prefix())
16✔
1550
                {
1551
                    found_bps.push_back(blueprints[bp_i[index]]);
6✔
1552
                    get_found_bp(bp_i, found_bps.back()->blueprints_, found_bps, ++index);
6✔
1553
                }
1554
                else
1555
                {
1556
                    if (found_bps.size() < 2)
10✔
1557
                    {
1558
                        found_bps.clear();
10✔
1559
                        found_bps.push_back(blueprints_[bp_i[index]]);
10✔
1560
                    }
1561
                    else
1562
                    {
1563
                        found_bps.pop_back();
×
1564
                        Blueprint* last_element = found_bps.back();
×
1565
                        found_bps.push_back(last_element->blueprints_[bp_i[index]]);
×
1566
                    }
1567
                    get_found_bp(bp_i, found_bps.back()->blueprints_, found_bps, ++index);
10✔
1568
                }
1569
            }
1570
        }
38✔
1571

1572
        CatchallRule& get_catch_all(const routing_handle_result& found) {
11✔
1573
            std::vector<Blueprint*> bps_found;
11✔
1574
            get_found_bp(found.blueprint_indices, blueprints_, bps_found);
11✔
1575
            if (!bps_found.empty()) {
11✔
1576
                for (size_t i = bps_found.size() - 1; i > 0; i--)
5✔
1577
                {
1578
                    if (bps_found[i]->catchall_rule().has_handler()) {
3✔
1579
                        return bps_found[i]->catchall_rule();
2✔
1580
                    }
1581
                }
1582
            }
1583
            return catchall_rule_;
9✔
1584
        }
11✔
1585

1586
        std::string get_error(const routing_handle_result& found)
11✔
1587
        {
1588
            const std::string EMPTY;
11✔
1589

1590
            std::vector<Blueprint*> bps_found;
11✔
1591
            get_found_bp(found.blueprint_indices, blueprints_, bps_found);
11✔
1592
            if (!bps_found.empty()) {
11✔
1593
                for (size_t i = bps_found.size() - 1; i > 0; i--) {
5✔
1594
                    if (bps_found[i]->catchall_rule().has_handler()) {
3✔
1595
#ifdef CROW_ENABLE_DEBUG
1596
                        return std::string("Redirected to Blueprint \"" + bps_found[i]->prefix() + "\" Catchall rule");
2✔
1597
#else
1598
                        return EMPTY;
×
1599
#endif
1600
                    }
1601
                }
1602
            } else  if (catchall_rule_.has_handler()) {
7✔
1603
#ifdef CROW_ENABLE_DEBUG
1604
                return std::string("Redirected to global Catchall rule");
4✔
1605
#else
1606
                return EMPTY;
×
1607
#endif
1608
            }
1609
            return EMPTY;
7✔
1610
        }
11✔
1611

1612
        std::unique_ptr<routing_handle_result> handle_initial(request& req, response& res)
266✔
1613
        {
1614
            HTTPMethod method_actual = req.method;
266✔
1615

1616
            std::unique_ptr<routing_handle_result> found{
1617
              new routing_handle_result(
1618
                0,
1619
                std::vector<size_t>(),
532✔
1620
                routing_params(),
266✔
1621
                HTTPMethod::InternalMethodCount)}; // This is always returned to avoid a null pointer dereference.
532✔
1622

1623
            // NOTE(EDev): This most likely will never run since the parser should handle this situation and close the connection before it gets here.
1624
            if (CROW_UNLIKELY(req.method >= HTTPMethod::InternalMethodCount))
266✔
1625
                return found;
×
1626
            else if (req.method == HTTPMethod::Head)
266✔
1627
            {
1628
                *found = per_methods_[static_cast<int>(method_actual)].trie.find(req.url);
2✔
1629
                // support HEAD requests using GET if not defined as method for the requested URL
1630
                if (!found->rule_index)
2✔
1631
                {
1632
                    method_actual = HTTPMethod::Get;
1✔
1633
                    *found = per_methods_[static_cast<int>(method_actual)].trie.find(req.url);
1✔
1634
                    if (!found->rule_index) // If a route is still not found, return a 404 without executing the rest of the HEAD specific code.
1✔
1635
                    {
1636
                        CROW_LOG_DEBUG << "Cannot match rules " << req.url;
×
1637
                        res = response(404); //TODO(EDev): Should this redirect to catchall?
×
1638
                        res.end();
×
1639
                        return found;
×
1640
                    }
1641
                }
1642

1643
                res.skip_body = true;
2✔
1644
                found->method = method_actual;
2✔
1645
                return found;
2✔
1646
            }
1647
            else if (req.method == HTTPMethod::Options)
264✔
1648
            {
1649
                std::string allow = "OPTIONS, HEAD";
7✔
1650

1651
                if (req.url == "/*")
7✔
1652
                {
1653
                    for (int i = 0; i < static_cast<int>(HTTPMethod::InternalMethodCount); i++)
35✔
1654
                    {
1655
                        if (static_cast<int>(HTTPMethod::Head) == i)
34✔
1656
                            continue; // HEAD is always allowed
1✔
1657

1658
                        if (!per_methods_[i].trie.is_empty())
33✔
1659
                        {
1660
                            allow.append(", ");
4✔
1661
                            allow.append(method_name(static_cast<HTTPMethod>(i)));
4✔
1662
                        }
1663
                    }
1664
#ifdef CROW_RETURNS_OK_ON_HTTP_OPTIONS_REQUEST
1665
                    res = response(crow::status::OK);
1666
#else
1667
                    res = response(crow::status::NO_CONTENT);
1✔
1668
#endif
1669

1670
                    res.set_header("Allow", allow);
3✔
1671
                    res.end();
1✔
1672
                    found->method = method_actual;
1✔
1673
                    return found;
1✔
1674
                }
1675
                else
1676
                {
1677
                    bool rules_matched = false;
6✔
1678
                    for (int i = 0; i < static_cast<int>(HTTPMethod::InternalMethodCount); i++)
210✔
1679
                    {
1680
                        if (per_methods_[i].trie.find(req.url).rule_index)
204✔
1681
                        {
1682
                            rules_matched = true;
7✔
1683

1684
                            if (static_cast<int>(HTTPMethod::Head) == i)
7✔
1685
                                continue; // HEAD is always allowed
1✔
1686

1687
                            allow.append(", ");
6✔
1688
                            allow.append(method_name(static_cast<HTTPMethod>(i)));
6✔
1689
                        }
1690
                    }
1691
                    if (rules_matched)
6✔
1692
                    {
1693
#ifdef CROW_RETURNS_OK_ON_HTTP_OPTIONS_REQUEST
1694
                        res = response(crow::status::OK);
1695
#else
1696
                        res = response(crow::status::NO_CONTENT);
5✔
1697
#endif
1698
                        res.set_header("Allow", allow);
15✔
1699
                        res.end();
5✔
1700
                        found->method = method_actual;
5✔
1701
                        return found;
5✔
1702
                    }
1703
                    else
1704
                    {
1705
                        CROW_LOG_DEBUG << "Cannot match rules " << req.url;
1✔
1706
                        res = response(404); //TODO(EDev): Should this redirect to catchall?
1✔
1707
                        res.end();
1✔
1708
                        return found;
1✔
1709
                    }
1710
                }
1711
            }
7✔
1712
            else // Every request that isn't a HEAD or OPTIONS request
1713
            {
1714
                *found = per_methods_[static_cast<int>(method_actual)].trie.find(req.url);
257✔
1715
                // 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
1716
                if (!found->rule_index)
257✔
1717
                {
1718
                    for (auto& per_method : per_methods_)
319✔
1719
                    {
1720
                        if (per_method.trie.find(req.url).rule_index) //Route found, but in another method
310✔
1721
                        {
1722
                            res.code = 405;
2✔
1723
                            found->catch_all = true;
2✔
1724
                            CROW_LOG_DEBUG << "Cannot match method " << req.url << " "
4✔
1725
                                           << method_name(method_actual) << ". " << get_error(*found);
2✔
1726
                            return found;
2✔
1727
                        }
1728
                    }
1729
                    //Route does not exist anywhere
1730

1731
                    res.code = 404;
9✔
1732
                    found->catch_all = true;
9✔
1733
                    CROW_LOG_DEBUG << "Cannot match rules " << req.url << ". " << get_error(*found);
9✔
1734
                    return found;
9✔
1735
                }
1736

1737
                found->method = method_actual;
246✔
1738
                return found;
246✔
1739
            }
1740
        }
×
1741

1742
        template<typename App>
1743
        void handle(request& req, response& res, routing_handle_result found)
218✔
1744
        {
1745
            if (found.catch_all) {
218✔
1746
                auto catch_all = get_catch_all(found);
11✔
1747
                if (catch_all.has_handler()) {
11✔
1748
                    try
1749
                    {
1750
                        catch_all.handler_(req, res);
4✔
1751
                    }
1752
                    catch (...)
×
1753
                    {
1754
                        exception_handler_(res);
×
1755
                    }
1756
                }
1757
                res.end();
11✔
1758
            } else {
11✔
1759
                HTTPMethod method_actual = found.method;
207✔
1760
                const auto& rules = per_methods_[static_cast<int>(method_actual)].rules;
207✔
1761
                const size_t rule_index = found.rule_index;
207✔
1762

1763
                if (rule_index >= rules.size())
207✔
1764
                    throw std::runtime_error("Trie internal structure corrupted!");
×
1765
                if (rule_index == RULE_SPECIAL_REDIRECT_SLASH) {
207✔
1766
                    CROW_LOG_INFO << "Redirecting to a url with trailing slash: " << req.url;
1✔
1767
                    res = response(301);
1✔
1768
                    res.add_header("Location", req.url + "/");
3✔
1769
                    res.end();
1✔
1770
                } else {
1771
                    CROW_LOG_DEBUG << "Matched rule '" << rules[rule_index]->rule_ << "' " << static_cast<uint64_t>(req.
412✔
1772
                                      method) << " / " << rules[rule_index]->get_methods();
206✔
1773

1774
                    try {
1775
                        BaseRule &rule = *rules[rule_index];
206✔
1776
                        handle_rule<App>(rule, req, res, found.r_params);
206✔
1777
                    } catch (...) {
206✔
1778
                        exception_handler_(res);
103✔
1779
                        res.end();
103✔
1780
                    }
1781
                }
1782
            }
1783
        }
218✔
1784

1785
        template<typename App>
1786
        typename std::enable_if<std::tuple_size<typename App::mw_container_t>::value != 0, void>::type
1787
          handle_rule(BaseRule& rule, crow::request& req, crow::response& res, const crow::routing_params& rp)
19✔
1788
        {
1789
            if (!rule.mw_indices_.empty())
19✔
1790
            {
1791
                auto& ctx = *reinterpret_cast<typename App::context_t*>(req.middleware_context);
3✔
1792
                auto& container = *reinterpret_cast<typename App::mw_container_t*>(req.middleware_container);
3✔
1793
                detail::middleware_call_criteria_dynamic<false> crit_fwd(rule.mw_indices_.indices());
3✔
1794

1795
                auto glob_completion_handler = std::move(res.complete_request_handler_);
3✔
1796
                res.complete_request_handler_ = [] {};
5✔
1797

1798
                detail::middleware_call_helper<decltype(crit_fwd),
1799
                                               0, typename App::context_t, typename App::mw_container_t>(crit_fwd, container, req, res, ctx);
3✔
1800

1801
                if (res.completed_)
3✔
1802
                {
1803
                    glob_completion_handler();
2✔
1804
                    return;
2✔
1805
                }
1806

1807
                res.complete_request_handler_ = [&rule, &ctx, &container, &req, &res, glob_completion_handler] {
2✔
1808
                    detail::middleware_call_criteria_dynamic<true> crit_bwd(rule.mw_indices_.indices());
1✔
1809

1810
                    detail::after_handlers_call_helper<
1811
                      decltype(crit_bwd),
1812
                      std::tuple_size<typename App::mw_container_t>::value - 1,
1813
                      typename App::context_t,
1814
                      typename App::mw_container_t>(crit_bwd, container, ctx, req, res);
1✔
1815
                    glob_completion_handler();
1✔
1816
                };
1817
            }
3✔
1818
            rule.handle(req, res, rp);
17✔
1819
        }
1820

1821
        template<typename App>
1822
        typename std::enable_if<std::tuple_size<typename App::mw_container_t>::value == 0, void>::type
1823
          handle_rule(BaseRule& rule, crow::request& req, crow::response& res, const crow::routing_params& rp)
187✔
1824
        {
1825
            rule.handle(req, res, rp);
187✔
1826
        }
84✔
1827

1828
        void debug_print()
1✔
1829
        {
1830
            for (int i = 0; i < static_cast<int>(HTTPMethod::InternalMethodCount); i++)
35✔
1831
            {
1832
                Trie& trie_ = per_methods_[i].trie;
34✔
1833
                if (!trie_.is_empty())
34✔
1834
                {
1835
                    CROW_LOG_DEBUG << method_name(static_cast<HTTPMethod>(i));
5✔
1836
                    trie_.debug_print();
5✔
1837
                }
1838
            }
1839
        }
1✔
1840

1841
        std::vector<Blueprint*>& blueprints()
39✔
1842
        {
1843
            return blueprints_;
39✔
1844
        }
1845

1846
        std::function<void(crow::response&)>& exception_handler()
1✔
1847
        {
1848
            return exception_handler_;
1✔
1849
        }
1850

1851
        static void default_exception_handler(response& res)
102✔
1852
        {
1853
            // any uncaught exceptions become 500s
1854
            res = response(500);
102✔
1855

1856
            try
1857
            {
1858
                throw;
102✔
1859
            }
1860
            catch (const bad_request& e)
102✔
1861
            {
1862
                res = response (400);
101✔
1863
                res.body = e.what();
101✔
1864
            }
101✔
1865
            catch (const std::exception& e)
1✔
1866
            {
1867
                CROW_LOG_ERROR << "An uncaught exception occurred: " << e.what();
1✔
1868
            }
1✔
1869
            catch (...)
×
1870
            {
1871
                CROW_LOG_ERROR << "An uncaught exception occurred. The type was unknown so no information was available.";
×
1872
            }
×
1873
        }
102✔
1874

1875
    private:
1876
        CatchallRule catchall_rule_;
1877

1878
        struct PerMethod
1879
        {
1880
            std::vector<BaseRule*> rules;
1881
            Trie trie;
1882

1883
            // rule index 0, 1 has special meaning; preallocate it to avoid duplication.
1884
            PerMethod():
1,904✔
1885
              rules(2) {}
5,712✔
1886
        };
1887
        std::array<PerMethod, static_cast<int>(HTTPMethod::InternalMethodCount)> per_methods_;
1888
        std::vector<std::unique_ptr<BaseRule>> all_rules_;
1889
        std::vector<Blueprint*> blueprints_;
1890
        std::function<void(crow::response&)> exception_handler_ = &default_exception_handler;
56✔
1891
    };
1892
} // namespace crow
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc