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

CrowCpp / Crow / 867

07 Dec 2025 04:58PM UTC coverage: 87.302% (+0.02%) from 87.283%
867

Pull #1117

gh-actions

web-flow
Merge a0c6ed4fc into 1aa3fbb4a
Pull Request #1117: Alternative to uint16 t cast

11 of 11 new or added lines in 1 file covered. (100.0%)

52 existing lines in 1 file now uncovered.

4132 of 4733 relevant lines covered (87.3%)

271.71 hits per line

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

85.84
/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
#include <limits>
14

15
#include "crow/common.h"
16
#include "crow/http_response.h"
17
#include "crow/http_request.h"
18
#include "crow/utility.h"
19
#include "crow/logging.h"
20
#include "crow/exceptions.h"
21
#include "crow/websocket.h"
22
#include "crow/mustache.h"
23
#include "crow/middleware.h"
24

25
namespace crow // NOTE: Already documented in "crow/app.h"
26
{
27

28
    constexpr size_t INVALID_BP_ID{SIZE_MAX};
29

30
    namespace detail
31
    {
32
        /// Typesafe wrapper for storing lists of middleware as their indices in the App
33
        struct middleware_indices
34
        {
35
            template<typename App>
36
            void push()
10✔
37
            {}
10✔
38

39
            template<typename App, typename MW, typename... Middlewares>
40
            void push()
12✔
41
            {
42
                using MwContainer = typename App::mw_container_t;
43
                static_assert(black_magic::has_type<MW, MwContainer>::value, "Middleware must be present in app");
44
                static_assert(std::is_base_of<crow::ILocalMiddleware, MW>::value, "Middleware must extend ILocalMiddleware");
45
                int idx = black_magic::tuple_index<MW, MwContainer>::value;
12✔
46
                indices_.push_back(idx);
12✔
47
                push<App, Middlewares...>();
12✔
48
            }
12✔
49

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

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

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

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

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

77
            const std::vector<int>& indices()
8✔
78
            {
79
                return indices_;
8✔
80
            }
81

82
        private:
83
            std::vector<int> indices_;
84
        };
85
    } // namespace detail
86

87
    /// A base class for all rules.
88

89
    ///
90
    /// Used to provide a common interface for code dealing with different types of rules.<br>
91
    /// A Rule provides a URL, allowed HTTP methods, and handlers.
92
    class BaseRule
93
    {
94
    public:
95
        BaseRule(std::string rule):
276✔
96
          rule_(std::move(rule))
276✔
97
        {}
276✔
98

99
        virtual ~BaseRule()=default;
276✔
100

101
        virtual void validate() = 0;
102

103
        void set_added()
244✔
104
        {
105
            added_ = true;
244✔
106
        }
244✔
107

108
        bool is_added()
286✔
109
        {
110
            return added_;
286✔
111
        }
112

113
        std::unique_ptr<BaseRule> upgrade()
250✔
114
        {
115
            if (rule_to_upgrade_)
250✔
116
                return std::move(rule_to_upgrade_);
20✔
117
            return {};
230✔
118
        }
119

120
        virtual void handle(request&, response&, const routing_params&) = 0;
UNCOV
121
        virtual void handle_upgrade(const request&, response& res, SocketAdaptor&&)
×
122
        {
123
            res = response(404);
×
UNCOV
124
            res.end();
×
125
        }
UNCOV
126
        virtual void handle_upgrade(const request&, response& res, UnixSocketAdaptor&&)
×
127
        {
128
            res = response(404);
×
UNCOV
129
            res.end();
×
130
        }
131
#ifdef CROW_ENABLE_SSL
UNCOV
132
        virtual void handle_upgrade(const request&, response& res, SSLAdaptor&&)
×
133
        {
134
            res = response(404);
×
UNCOV
135
            res.end();
×
136
        }
137
#endif
138

139
        uint32_t get_methods()
428✔
140
        {
141
            return methods_;
428✔
142
        }
143

144
        template<typename F>
145
        void foreach_method(F f)
246✔
146
        {
147
            for (uint32_t method = 0, method_bit = 1; method < static_cast<uint32_t>(HTTPMethod::InternalMethodCount); method++, method_bit <<= 1)
8,544✔
148
            {
149
                if (methods_ & method_bit)
8,300✔
150
                    f(method);
252✔
151
            }
152
        }
244✔
153

154
        std::string custom_templates_base;
155

156
        const std::string& rule() { return rule_; }
246✔
157

158
    protected:
159
        uint32_t methods_{1 << static_cast<int>(HTTPMethod::Get)};
160

161
        std::string rule_;
162
        std::string name_;
163
        bool added_{false};
164

165
        std::unique_ptr<BaseRule> rule_to_upgrade_;
166

167
        detail::middleware_indices mw_indices_;
168

169
        friend class Router;
170
        friend class Blueprint;
171
        template<typename T>
172
        friend struct RuleParameterTraits;
173
    };
174

175

176
    namespace detail
177
    {
178
        namespace routing_handler_call_helper
179
        {
180
            template<typename T, int Pos>
181
            struct call_pair
182
            {
183
                using type = T;
184
                static const int pos = Pos;
185
            };
186

187
            template<typename H1>
188
            struct call_params
189
            {
190
                H1& handler;
191
                const routing_params& params;
192
                request& req;
193
                response& res;
194
            };
195

196
            template<typename F, int NInt, int NUint, int NDouble, int NString, typename S1, typename S2>
197
            struct call
198
            {};
199

200
            template<typename F, int NInt, int NUint, int NDouble, int NString, typename... Args1, typename... Args2>
201
            struct call<F, NInt, NUint, NDouble, NString, black_magic::S<int64_t, Args1...>, black_magic::S<Args2...>>
202
            {
203
                void operator()(F cparams)
8✔
204
                {
205
                    using pushed = typename black_magic::S<Args2...>::template push_back<call_pair<int64_t, NInt>>;
206
                    call<F, NInt + 1, NUint, NDouble, NString, black_magic::S<Args1...>, pushed>()(cparams);
8✔
207
                }
8✔
208
            };
209

210
            template<typename F, int NInt, int NUint, int NDouble, int NString, typename... Args1, typename... Args2>
211
            struct call<F, NInt, NUint, NDouble, NString, black_magic::S<uint64_t, Args1...>, black_magic::S<Args2...>>
212
            {
213
                void operator()(F cparams)
8✔
214
                {
215
                    using pushed = typename black_magic::S<Args2...>::template push_back<call_pair<uint64_t, NUint>>;
216
                    call<F, NInt, NUint + 1, NDouble, NString, black_magic::S<Args1...>, pushed>()(cparams);
8✔
217
                }
8✔
218
            };
219

220
            template<typename F, int NInt, int NUint, int NDouble, int NString, typename... Args1, typename... Args2>
221
            struct call<F, NInt, NUint, NDouble, NString, black_magic::S<double, Args1...>, black_magic::S<Args2...>>
222
            {
223
                void operator()(F cparams)
4✔
224
                {
225
                    using pushed = typename black_magic::S<Args2...>::template push_back<call_pair<double, NDouble>>;
226
                    call<F, NInt, NUint, NDouble + 1, NString, black_magic::S<Args1...>, pushed>()(cparams);
4✔
227
                }
4✔
228
            };
229

230
            template<typename F, int NInt, int NUint, int NDouble, int NString, typename... Args1, typename... Args2>
231
            struct call<F, NInt, NUint, NDouble, NString, black_magic::S<std::string, Args1...>, black_magic::S<Args2...>>
232
            {
233
                void operator()(F cparams)
6✔
234
                {
235
                    using pushed = typename black_magic::S<Args2...>::template push_back<call_pair<std::string, NString>>;
236
                    call<F, NInt, NUint, NDouble, NString + 1, black_magic::S<Args1...>, pushed>()(cparams);
6✔
237
                }
6✔
238
            };
239

240
            template<typename F, int NInt, int NUint, int NDouble, int NString, typename... Args1>
241
            struct call<F, NInt, NUint, NDouble, NString, black_magic::S<>, black_magic::S<Args1...>>
242
            {
243
                void operator()(F cparams)
410✔
244
                {
245
                    cparams.handler(
412✔
246
                      cparams.req,
8✔
247
                      cparams.res,
248
                      cparams.params.template get<typename Args1::type>(Args1::pos)...);
10✔
249
                }
204✔
250
            };
251

252
            template<typename Func, typename... ArgsWrapped>
253
            struct Wrapped
254
            {
255
                template<typename... Args>
256
                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)
4✔
257
                {
258
                    handler_ = ([f = std::move(f)](const request&, response& res, Args... args) {
12✔
259
                        res = response(f(args...));
4✔
260
                        res.end();
4✔
261
                    });
262
                }
4✔
263

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

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

278
                    Func f;
279
                };
280

281
                template<typename... Args>
282
                void set_(Func f, typename std::enable_if<
2✔
283
                                    std::is_same<typename std::tuple_element<0, std::tuple<Args..., void>>::type, const request&>::value &&
284
                                      !std::is_same<typename std::tuple_element<1, std::tuple<Args..., void, void>>::type, response&>::value,
285
                                    int>::type = 0)
286
                {
287
                    handler_ = req_handler_wrapper<Args...>(std::move(f));
2✔
288
                    /*handler_ = (
289
                        [f = std::move(f)]
290
                        (const request& req, response& res, Args... args){
291
                             res = response(f(req, args...));
292
                             res.end();
293
                        });*/
294
                }
2✔
295

296
                template<typename... Args>
297
                void set_(Func f, typename std::enable_if<
2✔
298
                                    std::is_same<typename std::tuple_element<0, std::tuple<Args..., void>>::type, const request&>::value &&
299
                                      std::is_same<typename std::tuple_element<1, std::tuple<Args..., void, void>>::type, response&>::value,
300
                                    int>::type = 0)
301
                {
302
                    handler_ = std::move(f);
2✔
303
                }
2✔
304

305
                template<typename... Args>
306
                struct handler_type_helper
307
                {
308
                    using type = std::function<void(const crow::request&, crow::response&, Args...)>;
309
                    using args_type = black_magic::S<typename black_magic::promote_t<Args>...>;
310
                };
311

312
                template<typename... Args>
313
                struct handler_type_helper<const request&, Args...>
314
                {
315
                    using type = std::function<void(const crow::request&, crow::response&, Args...)>;
316
                    using args_type = black_magic::S<typename black_magic::promote_t<Args>...>;
317
                };
318

319
                template<typename... Args>
320
                struct handler_type_helper<const request&, response&, Args...>
321
                {
322
                    using type = std::function<void(const crow::request&, crow::response&, Args...)>;
323
                    using args_type = black_magic::S<typename black_magic::promote_t<Args>...>;
324
                };
325

326
                typename handler_type_helper<ArgsWrapped...>::type handler_;
327

328
                void operator()(request& req, response& res, const routing_params& params)
8✔
329
                {
330
                    detail::routing_handler_call_helper::call<
8✔
331
                      detail::routing_handler_call_helper::call_params<
332
                        decltype(handler_)>,
333
                      0, 0, 0, 0,
334
                      typename handler_type_helper<ArgsWrapped...>::args_type,
335
                      black_magic::S<>>()(
8✔
336
                      detail::routing_handler_call_helper::call_params<
337
                        decltype(handler_)>{handler_, params, req, res});
8✔
338
                }
8✔
339
            };
340

341
        } // namespace routing_handler_call_helper
342
    }     // namespace detail
343

344

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

351
        template<typename Func>
352
        typename std::enable_if<black_magic::CallHelper<Func, black_magic::S<>>::value, void>::type
353
          operator()(Func&& f)
2✔
354
        {
355
            static_assert(!std::is_same<void, decltype(f())>::value,
356
                          "Handler function cannot have void return type; valid return types: string, int, crow::response, crow::returnable");
357

358
            handler_ = ([f = std::move(f)](const request&, response& res) {
6✔
359
                res = response(f());
4✔
360
                res.end();
4✔
361
            });
362
        }
2✔
363

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

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

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

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

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

414
    protected:
415
        friend class Router;
416

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

421

422
    /// A rule dealing with websockets.
423

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

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

438
        void validate() override
20✔
439
        {}
20✔
440

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

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

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

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

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

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

480
        template<typename Func>
481
        self_t& onopen(Func f)
20✔
482
        {
483
            open_handler_ = f;
20✔
484
            return *this;
20✔
485
        }
486

487
        template<typename Func>
488
        self_t& onmessage(Func f)
16✔
489
        {
490
            message_handler_ = f;
16✔
491
            return *this;
16✔
492
        }
493

494
        template<typename Func>
495
        self_t& onclose(Func f)
20✔
496
        {
497
            close_handler_ = f;
20✔
498
            return *this;
20✔
499
        }
500

501
        template<typename Func>
502
        self_t& onerror(Func f)
503
        {
504
            error_handler_ = f;
505
            return *this;
506
        }
507

508

509
        self_t& onaccept(std::function<void(const crow::request&, std::optional<crow::response>&, void**)>&& callback)
18✔
510
        {
511
            accept_handler_ = std::move(callback);
18✔
512
            return *this;
18✔
513
        }
514

515
        self_t& onaccept(std::function<bool(const crow::request&, void**)>&& callback)
18✔
516
        {
517
            onaccept([callback](const crow::request& req, std::optional<crow::response>& res, void** p) {
34✔
518
                if (!callback(req, p))
16✔
519
                {
UNCOV
520
                    res = crow::response(400);
×
521
                }
522
            });
523
            return *this;
18✔
524
        }
525

526
        self_t& mirrorprotocols(bool mirror_protocols = true)
2✔
527
        {
528
            mirror_protocols_ = mirror_protocols;
2✔
529
            return *this;
2✔
530
        }
531

532
    protected:
533
        App* app_;
534
        std::function<void(crow::websocket::connection&)> open_handler_;
535
        std::function<void(crow::websocket::connection&, const std::string&, bool)> message_handler_;
536
        std::function<void(crow::websocket::connection&, const std::string&, uint16_t)> close_handler_;
537
        std::function<void(crow::websocket::connection&, const std::string&)> error_handler_;
538
        std::function<void(const crow::request&, std::optional<crow::response>&, void**)> accept_handler_;
539
        bool mirror_protocols_ = false;
540
        uint64_t max_payload_;
541
        bool max_payload_override_ = false;
542
        std::vector<std::string> subprotocols_;
543
    };
544

545
    /// Allows the user to assign parameters using functions.
546

547
    ///
548
    /// `rule.name("name").methods(HTTPMethod::POST)`
549
    template<typename T>
550
    struct RuleParameterTraits
551
    {
552
        using self_t = T;
553

554
        template<typename App>
555
        WebSocketRule<App>& websocket(App* app)
20✔
556
        {
557
            auto p = new WebSocketRule<App>(static_cast<self_t*>(this)->rule_, app);
20✔
558
            static_cast<self_t*>(this)->rule_to_upgrade_.reset(p);
20✔
559
            return *p;
20✔
560
        }
561

562
        self_t& name(std::string name) noexcept
2✔
563
        {
564
            static_cast<self_t*>(this)->name_ = std::move(name);
2✔
565
            return static_cast<self_t&>(*this);
2✔
566
        }
567

568
        self_t& methods(HTTPMethod method)
24✔
569
        {
570
            static_cast<self_t*>(this)->methods_ = 1 << static_cast<int>(method);
24✔
571
            return static_cast<self_t&>(*this);
24✔
572
        }
573

574
        template<typename... MethodArgs>
575
        self_t& methods(HTTPMethod method, MethodArgs... args_method)
6✔
576
        {
577
            methods(args_method...);
6✔
578
            static_cast<self_t*>(this)->methods_ |= 1 << static_cast<int>(method);
6✔
579
            return static_cast<self_t&>(*this);
6✔
580
        }
581

582
        /// Enable local middleware for this handler
583
        template<typename App, typename... Middlewares>
584
        self_t& middlewares()
6✔
585
        {
586
            static_cast<self_t*>(this)->mw_indices_.template push<App, Middlewares...>();
6✔
587
            return static_cast<self_t&>(*this);
6✔
588
        }
589
    };
590

591
    /// A rule that can change its parameters during runtime.
592
    class DynamicRule : public BaseRule, public RuleParameterTraits<DynamicRule>
593
    {
594
    public:
595
        DynamicRule(std::string rule):
10✔
596
          BaseRule(std::move(rule))
10✔
597
        {}
10✔
598

599
        void validate() override
10✔
600
        {
601
            if (!erased_handler_)
10✔
602
            {
603
                throw std::runtime_error(name_ + (!name_.empty() ? ": " : "") + "no handler for url " + rule_);
2✔
604
            }
605
        }
8✔
606

607
        void handle(request& req, response& res, const routing_params& params) override
8✔
608
        {
609
            if (!custom_templates_base.empty())
8✔
UNCOV
610
                mustache::set_base(custom_templates_base);
×
611
            else if (mustache::detail::get_template_base_directory_ref() != "templates")
8✔
612
                mustache::set_base("templates");
16✔
613
            erased_handler_(req, res, params);
8✔
614
        }
8✔
615

616
        template<typename Func>
617
        void operator()(Func f)
10✔
618
        {
619
#ifdef CROW_MSVC_WORKAROUND
620
            using function_t = utility::function_traits<decltype(&Func::operator())>;
621
#else
622
            using function_t = utility::function_traits<Func>;
623
#endif
624
            erased_handler_ = wrap(std::move(f), black_magic::gen_seq<function_t::arity>());
10✔
625
        }
8✔
626

627
        // enable_if Arg1 == request && Arg2 == response
628
        // enable_if Arg1 == request && Arg2 != resposne
629
        // enable_if Arg1 != request
630
#ifdef CROW_MSVC_WORKAROUND
631
        template<typename Func, size_t... Indices>
632
#else
633
        template<typename Func, unsigned... Indices>
634
#endif
635
        std::function<void(request&, response&, const routing_params&)>
636
          wrap(Func f, black_magic::seq<Indices...>)
10✔
637
        {
638
#ifdef CROW_MSVC_WORKAROUND
639
            using function_t = utility::function_traits<decltype(&Func::operator())>;
640
#else
641
            using function_t = utility::function_traits<Func>;
642
#endif
643
            if (!black_magic::is_parameter_tag_compatible(
10✔
644
                  black_magic::get_parameter_tag_runtime(rule_.c_str()),
645
                  black_magic::compute_parameter_tag_from_args_list<
646
                    typename function_t::template arg<Indices>...>::value))
647
            {
648
                throw std::runtime_error("route_dynamic: Handler type is mismatched with URL parameters: " + rule_);
2✔
649
            }
650
            auto ret = detail::routing_handler_call_helper::Wrapped<Func, typename function_t::template arg<Indices>...>();
8✔
651
            ret.template set_<
652
              typename function_t::template arg<Indices>...>(std::move(f));
8✔
653
            return ret;
16✔
654
        }
8✔
655

656
        template<typename Func>
657
        void operator()(std::string name, Func&& f)
658
        {
659
            name_ = std::move(name);
660
            (*this).template operator()<Func>(std::forward(f));
661
        }
662

663
    private:
664
        std::function<void(request&, response&, const routing_params&)> erased_handler_;
665
    };
666

667
    /// Default rule created when CROW_ROUTE is called.
668
    template<typename... Args>
669
    class TaggedRule : public BaseRule, public RuleParameterTraits<TaggedRule<Args...>>
670
    {
671
    public:
672
        using self_t = TaggedRule<Args...>;
673

674
        TaggedRule(std::string rule):
246✔
675
          BaseRule(std::move(rule))
246✔
676
        {}
246✔
677

678
        void validate() override
226✔
679
        {
680
            if (rule_.at(0) != '/')
226✔
681
                throw std::runtime_error("Internal error: Routes must start with a '/'");
2✔
682

683
            if (!handler_)
224✔
684
            {
685
                throw std::runtime_error(name_ + (!name_.empty() ? ": " : "") + "no handler for url " + rule_);
2✔
686
            }
687
        }
222✔
688

689
        template<typename Func>
690
        void operator()(Func&& f)
228✔
691
        {
692
            handler_ = ([f = std::move(f)](request& req, response& res, Args... args) {
822✔
693
                detail::wrapped_handler_call(req, res, f, std::forward<Args>(args)...);
402✔
694
            });
695
        }
228✔
696

697
        template<typename Func>
698
        void operator()(std::string name, Func&& f)
699
        {
700
            name_ = std::move(name);
701
            (*this).template operator()<Func>(std::forward(f));
702
        }
703

704
        void handle(request& req, response& res, const routing_params& params) override
402✔
705
        {
706
            if (!custom_templates_base.empty())
402✔
707
                mustache::set_base(custom_templates_base);
8✔
708
            else if (mustache::detail::get_template_base_directory_ref() != mustache::detail::get_global_template_base_directory_ref())
394✔
709
                mustache::set_base(mustache::detail::get_global_template_base_directory_ref());
178✔
710

711
            detail::routing_handler_call_helper::call<
402✔
712
              detail::routing_handler_call_helper::call_params<decltype(handler_)>,
713
              0, 0, 0, 0,
714
              black_magic::S<Args...>,
715
              black_magic::S<>>()(
402✔
716
              detail::routing_handler_call_helper::call_params<decltype(handler_)>{handler_, params, req, res});
402✔
717
        }
196✔
718

719
    private:
720
        std::function<void(crow::request&, crow::response&, Args...)> handler_;
721
    };
722

723
    constexpr size_t RULE_SPECIAL_REDIRECT_SLASH = 1;
724

725
    /// A search tree.
726
    class Trie
727
    {
728
    public:
729
        struct Node
730
        {
731
            size_t rule_index{};
732
            // Assign the index to the maximum 32 unsigned integer value by default so that any other number (specifically 0) is a valid BP id.
733
            size_t blueprint_index{INVALID_BP_ID};
734
            std::string key;
735
            ParamType param = ParamType::MAX; // MAX = No param.
736
            std::vector<Node> children;
737

738
            bool IsSimpleNode() const
5,576✔
739
            {
740
                return !rule_index &&
11,078✔
741
                       blueprint_index == INVALID_BP_ID &&
10,994✔
742
                       children.size() < 2 &&
5,490✔
743
                       param == ParamType::MAX &&
16,514✔
744
                       std::all_of(std::begin(children), std::end(children), [](const Node& x) {
16,318✔
745
                           return x.param == ParamType::MAX;
1,300✔
746
                       });
5,572✔
747
            }
748

749
            Node& add_child_node()
1,490✔
750
            {
751
                children.emplace_back();
1,490✔
752
                return children.back();
1,490✔
753
            }
754
        };
755

756

757
        Trie()
3,672✔
758
        {}
3,672✔
759

760
        /// Check whether or not the trie is empty.
761
        bool is_empty()
134✔
762
        {
763
            return head_.children.empty();
134✔
764
        }
765

766
        void optimize()
4,282✔
767
        {
768
            for (auto& child : head_.children)
4,426✔
769
            {
770
                optimizeNode(child);
144✔
771
            }
772
        }
4,282✔
773

774

775
    private:
776
        void optimizeNode(Node& node)
1,498✔
777
        {
778
            if (node.children.empty())
1,498✔
779
                return;
204✔
780
            if (node.IsSimpleNode())
1,294✔
781
            {
782
                auto children_temp = std::move(node.children);
1,052✔
783
                auto& child_temp = children_temp[0];
1,052✔
784
                node.key += child_temp.key;
1,052✔
785
                node.rule_index = child_temp.rule_index;
1,052✔
786
                node.blueprint_index = child_temp.blueprint_index;
1,052✔
787
                node.children = std::move(child_temp.children);
1,052✔
788
                optimizeNode(node);
1,052✔
789
            }
1,052✔
790
            else
791
            {
792
                for (auto& child : node.children)
544✔
793
                {
794
                    optimizeNode(child);
302✔
795
                }
796
            }
797
        }
798

799
        void debug_node_print(const Node& node, size_t level)
14✔
800
        {
801
            if (node.param != ParamType::MAX)
14✔
802
            {
UNCOV
803
                switch (node.param)
×
804
                {
805
                    case ParamType::INT:
×
806
                        CROW_LOG_DEBUG << std::string(3 * level, ' ') << "└➝ "
×
807
                                       << "<int>";
×
808
                        break;
×
809
                    case ParamType::UINT:
×
810
                        CROW_LOG_DEBUG << std::string(3 * level, ' ') << "└➝ "
×
811
                                       << "<uint>";
×
812
                        break;
×
813
                    case ParamType::DOUBLE:
×
814
                        CROW_LOG_DEBUG << std::string(3 * level, ' ') << "└➝ "
×
815
                                       << "<double>";
×
816
                        break;
×
817
                    case ParamType::STRING:
×
818
                        CROW_LOG_DEBUG << std::string(3 * level, ' ') << "└➝ "
×
819
                                       << "<string>";
×
820
                        break;
×
821
                    case ParamType::PATH:
×
822
                        CROW_LOG_DEBUG << std::string(3 * level, ' ') << "└➝ "
×
823
                                       << "<path>";
×
824
                        break;
×
825
                    default:
×
826
                        CROW_LOG_DEBUG << std::string(3 * level, ' ') << "└➝ "
×
827
                                       << "<ERROR>";
×
UNCOV
828
                        break;
×
829
                }
830
            }
831
            else
832
                CROW_LOG_DEBUG << std::string(3 * level, ' ') << "└➝ " << node.key;
42✔
833

834
            for (const auto& child : node.children)
18✔
835
            {
836
                debug_node_print(child, level + 1);
4✔
837
            }
838
        }
14✔
839

840
    public:
841
        void debug_print()
10✔
842
        {
843
            CROW_LOG_DEBUG << "└➙ ROOT";
10✔
844
            for (const auto& child : head_.children)
20✔
845
                debug_node_print(child, 1);
10✔
846
        }
10✔
847

848
        void validate()
4,284✔
849
        {
850
            if (!head_.IsSimpleNode())
4,284✔
UNCOV
851
                throw std::runtime_error("Internal error: Trie header should be simple!");
×
852
            optimize();
4,278✔
853
        }
4,282✔
854

855
        //Rule_index, Blueprint_index, routing_params
856
        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
2,326✔
857
        {
858
            //start params as an empty struct
859
            routing_params empty;
2,326✔
860
            if (params == nullptr)
2,326✔
861
                params = &empty;
1,504✔
862
            //same for blueprint vector
863
            std::vector<size_t> MT;
2,326✔
864
            if (blueprints == nullptr)
2,326✔
865
                blueprints = &MT;
1,504✔
866

867
            size_t found{};               //The rule index to be found
2,326✔
868
            std::vector<size_t> found_BP; //The Blueprint indices to be found
2,326✔
869
            routing_params match_params;    //supposedly the final matched parameters
2,326✔
870

871
            auto update_found = [&found, &found_BP, &match_params](routing_handle_result& ret) {
822✔
872
                found_BP = std::move(ret.blueprint_indices);
822✔
873
                if (ret.rule_index && (!found || found > ret.rule_index))
822✔
874
                {
875
                    found = ret.rule_index;
722✔
876
                    match_params = std::move(ret.r_params);
722✔
877
                }
878
            };
3,148✔
879

880
            //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
881
            if (pos == req_url.size())
2,326✔
882
            {
883
                found_BP = std::move(*blueprints);
470✔
884
                return routing_handle_result{node.rule_index, *blueprints, *params};
470✔
885
            }
886

887
            bool found_fragment = false;
1,856✔
888

889
            for (const auto& child : node.children)
3,052✔
890
            {
891
                if (child.param != ParamType::MAX)
1,196✔
892
                {
893
                    if (child.param == ParamType::INT)
26✔
894
                    {
895
                        char c = req_url[pos];
8✔
896
                        if ((c >= '0' && c <= '9') || c == '+' || c == '-')
8✔
897
                        {
898
                            char* eptr;
899
                            errno = 0;
8✔
900
                            long long int value = strtoll(req_url.data() + pos, &eptr, 10);
8✔
901
                            if (errno != ERANGE && eptr != req_url.data() + pos)
8✔
902
                            {
903
                                found_fragment = true;
8✔
904
                                params->int_params.push_back(value);
8✔
905
                                if (child.blueprint_index != INVALID_BP_ID) blueprints->push_back(child.blueprint_index);
8✔
906
                                auto ret = find(req_url, child, eptr - req_url.data(), params, blueprints);
8✔
907
                                update_found(ret);
8✔
908
                                params->int_params.pop_back();
8✔
909
                                if (!blueprints->empty()) blueprints->pop_back();
8✔
910
                            }
8✔
911
                        }
912
                    }
913

914
                    else if (child.param == ParamType::UINT)
18✔
915
                    {
916
                        char c = req_url[pos];
8✔
917
                        if ((c >= '0' && c <= '9') || c == '+')
8✔
918
                        {
919
                            char* eptr;
920
                            errno = 0;
8✔
921
                            unsigned long long int value = strtoull(req_url.data() + pos, &eptr, 10);
8✔
922
                            if (errno != ERANGE && eptr != req_url.data() + pos)
8✔
923
                            {
924
                                found_fragment = true;
8✔
925
                                params->uint_params.push_back(value);
8✔
926
                                if (child.blueprint_index != INVALID_BP_ID) blueprints->push_back(child.blueprint_index);
8✔
927
                                auto ret = find(req_url, child, eptr - req_url.data(), params, blueprints);
8✔
928
                                update_found(ret);
8✔
929
                                params->uint_params.pop_back();
8✔
930
                                if (!blueprints->empty()) blueprints->pop_back();
8✔
931
                            }
8✔
932
                        }
933
                    }
934

935
                    else if (child.param == ParamType::DOUBLE)
10✔
936
                    {
937
                        char c = req_url[pos];
4✔
938
                        if ((c >= '0' && c <= '9') || c == '+' || c == '-' || c == '.')
4✔
939
                        {
940
                            char* eptr;
941
                            errno = 0;
4✔
942
                            double value = strtod(req_url.data() + pos, &eptr);
4✔
943
                            if (errno != ERANGE && eptr != req_url.data() + pos)
4✔
944
                            {
945
                                found_fragment = true;
4✔
946
                                params->double_params.push_back(value);
4✔
947
                                if (child.blueprint_index != INVALID_BP_ID) blueprints->push_back(child.blueprint_index);
4✔
948
                                auto ret = find(req_url, child, eptr - req_url.data(), params, blueprints);
4✔
949
                                update_found(ret);
4✔
950
                                params->double_params.pop_back();
4✔
951
                                if (!blueprints->empty()) blueprints->pop_back();
4✔
952
                            }
4✔
953
                        }
954
                    }
955

956
                    else if (child.param == ParamType::STRING)
6✔
957
                    {
958
                        size_t epos = pos;
4✔
959
                        for (; epos < req_url.size(); epos++)
42✔
960
                        {
961
                            if (req_url[epos] == '/')
40✔
962
                                break;
2✔
963
                        }
964

965
                        if (epos != pos)
4✔
966
                        {
967
                            found_fragment = true;
4✔
968
                            params->string_params.push_back(req_url.substr(pos, epos - pos));
4✔
969
                            if (child.blueprint_index != INVALID_BP_ID) blueprints->push_back(child.blueprint_index);
4✔
970
                            auto ret = find(req_url, child, epos, params, blueprints);
4✔
971
                            update_found(ret);
4✔
972
                            params->string_params.pop_back();
4✔
973
                            if (!blueprints->empty()) blueprints->pop_back();
4✔
974
                        }
4✔
975
                    }
976

977
                    else if (child.param == ParamType::PATH)
2✔
978
                    {
979
                        size_t epos = req_url.size();
2✔
980

981
                        if (epos != pos)
2✔
982
                        {
983
                            found_fragment = true;
2✔
984
                            params->string_params.push_back(req_url.substr(pos, epos - pos));
2✔
985
                            if (child.blueprint_index != INVALID_BP_ID) blueprints->push_back(child.blueprint_index);
2✔
986
                            auto ret = find(req_url, child, epos, params, blueprints);
2✔
987
                            update_found(ret);
2✔
988
                            params->string_params.pop_back();
2✔
989
                            if (!blueprints->empty()) blueprints->pop_back();
2✔
990
                        }
2✔
991
                    }
992
                }
993

994
                else
995
                {
996
                    const std::string& fragment = child.key;
1,170✔
997
                    if (req_url.compare(pos, fragment.size(), fragment) == 0)
1,170✔
998
                    {
999
                        found_fragment = true;
796✔
1000
                        if (child.blueprint_index != INVALID_BP_ID) blueprints->push_back(child.blueprint_index);
796✔
1001
                        auto ret = find(req_url, child, pos + fragment.size(), params, blueprints);
796✔
1002
                        update_found(ret);
796✔
1003
                        if (!blueprints->empty()) blueprints->pop_back();
796✔
1004
                    }
796✔
1005
                }
1006
            }
1007

1008
            if (!found_fragment)
1,856✔
1009
                found_BP = std::move(*blueprints);
1,034✔
1010

1011
            return routing_handle_result{found, found_BP, match_params}; //Called after all the recursions have been done
1,856✔
1012
        }
2,326✔
1013

1014
        routing_handle_result find(const std::string& req_url) const
1,504✔
1015
        {
1016
            return find(req_url, head_);
1,504✔
1017
        }
1018

1019
        //This functions assumes any blueprint info passed is valid
1020
        void add(const std::string& url, size_t rule_index, unsigned bp_prefix_length = 0, size_t blueprint_index = INVALID_BP_ID)
256✔
1021
        {
1022
            auto idx = &head_;
256✔
1023

1024
            bool has_blueprint = bp_prefix_length != 0 && blueprint_index != INVALID_BP_ID;
256✔
1025

1026
            for (unsigned i = 0; i < url.size(); i++)
2,068✔
1027
            {
1028
                char c = url[i];
1,812✔
1029
                if (c == '<')
1,812✔
1030
                {
1031
                    static struct ParamTraits
1032
                    {
1033
                        ParamType type;
1034
                        std::string name;
1035
                    } paramTraits[] =
1036
                      {
1037
                        {ParamType::INT, "<int>"},
1038
                        {ParamType::UINT, "<uint>"},
1039
                        {ParamType::DOUBLE, "<float>"},
1040
                        {ParamType::DOUBLE, "<double>"},
1041
                        {ParamType::STRING, "<str>"},
1042
                        {ParamType::STRING, "<string>"},
1043
                        {ParamType::PATH, "<path>"},
1044
                      };
114✔
1045

1046
                    for (const auto& x : paramTraits)
638✔
1047
                    {
1048
                        if (url.compare(i, x.name.size(), x.name) == 0)
638✔
1049
                        {
1050
                            bool found = false;
106✔
1051
                            for (auto& child : idx->children)
106✔
1052
                            {
UNCOV
1053
                                if (child.param == x.type)
×
1054
                                {
1055
                                    idx = &child;
×
1056
                                    i += x.name.size();
×
1057
                                    found = true;
×
UNCOV
1058
                                    break;
×
1059
                                }
1060
                            }
1061
                            if (found)
106✔
UNCOV
1062
                                break;
×
1063

1064
                            auto new_node_idx = &idx->add_child_node();
106✔
1065
                            new_node_idx->param = x.type;
106✔
1066
                            idx = new_node_idx;
106✔
1067
                            i += x.name.size();
106✔
1068
                            break;
106✔
1069
                        }
1070
                    }
1071

1072
                    i--;
106✔
1073
                }
1074
                else
1075
                {
1076
                    //This part assumes the tree is unoptimized (every node has a max 1 character key)
1077
                    bool piece_found = false;
1,706✔
1078
                    for (auto& child : idx->children)
1,814✔
1079
                    {
1080
                        if (child.key[0] == c)
430✔
1081
                        {
1082
                            idx = &child;
322✔
1083
                            piece_found = true;
322✔
1084
                            break;
322✔
1085
                        }
1086
                    }
1087
                    if (!piece_found)
1,706✔
1088
                    {
1089
                        auto new_node_idx = &idx->add_child_node();
1,384✔
1090
                        new_node_idx->key = c;
1,384✔
1091
                        //The assumption here is that you'd only need to add a blueprint index if the tree didn't have the BP prefix.
1092
                        if (has_blueprint && i == bp_prefix_length)
1,384✔
1093
                            new_node_idx->blueprint_index = blueprint_index;
14✔
1094
                        idx = new_node_idx;
1,384✔
1095
                    }
1096
                }
1097
            }
1098

1099
            //check if the last node already has a value (exact url already in Trie)
1100
            if (idx->rule_index)
256✔
1101
                throw std::runtime_error("handler already exists for " + url);
2✔
1102
            idx->rule_index = rule_index;
254✔
1103
        }
254✔
1104

1105
    private:
1106
        Node head_;
1107
    };
1108

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

1111
    ///
1112
    /// You can use blueprints to assign a common prefix to rules' prefix, set custom static and template folders, and set a custom catchall route.
1113
    /// You can also assign nest blueprints for maximum Compartmentalization.
1114
    class Blueprint
1115
    {
1116
    public:
1117
        Blueprint(const std::string& prefix)
4✔
1118
          : prefix_(prefix),
4✔
1119
            static_dir_(prefix),
4✔
1120
            templates_dir_(prefix)
4✔
1121
            {}
4✔
1122

1123
        Blueprint(const std::string& prefix, const std::string& static_dir):
1124
          prefix_(prefix), static_dir_(static_dir){}
1125

1126
        Blueprint(const std::string& prefix, const std::string& static_dir, const std::string& templates_dir):
10✔
1127
          prefix_(prefix), static_dir_(static_dir), templates_dir_(templates_dir){}
10✔
1128

1129
        /*
1130
        Blueprint(Blueprint& other)
1131
        {
1132
            prefix_ = std::move(other.prefix_);
1133
            all_rules_ = std::move(other.all_rules_);
1134
        }
1135

1136
        Blueprint(const Blueprint& other)
1137
        {
1138
            prefix_ = other.prefix_;
1139
            all_rules_ = other.all_rules_;
1140
        }
1141
*/
1142
        Blueprint(Blueprint&& value)
1143
        {
1144
            *this = std::move(value);
1145
        }
1146

1147
        Blueprint& operator=(const Blueprint& value) = delete;
1148

1149
        Blueprint& operator=(Blueprint&& value) noexcept
1150
        {
1151
            prefix_ = std::move(value.prefix_);
1152
            static_dir_ = std::move(value.static_dir_);
1153
            templates_dir_ = std::move(value.templates_dir_);
1154
            all_rules_ = std::move(value.all_rules_);
1155
            catchall_rule_ = std::move(value.catchall_rule_);
1156
            blueprints_ = std::move(value.blueprints_);
1157
            mw_indices_ = std::move(value.mw_indices_);
1158
            return *this;
1159
        }
1160

1161
        bool operator==(const Blueprint& value)
1162
        {
1163
            return value.prefix() == prefix_;
1164
        }
1165

1166
        bool operator!=(const Blueprint& value)
1167
        {
1168
            return value.prefix() != prefix_;
1169
        }
1170

1171
        std::string prefix() const
58✔
1172
        {
1173
            return prefix_;
58✔
1174
        }
1175

1176
        std::string static_dir() const
12✔
1177
        {
1178
            return static_dir_;
12✔
1179
        }
1180

1181
        void set_added()
14✔
1182
        {
1183
            added_ = true;
14✔
1184
        }
14✔
1185

1186
        bool is_added()
14✔
1187
        {
1188
            return added_;
14✔
1189
        }
1190

1191
        DynamicRule& new_rule_dynamic(const std::string& rule)
1192
        {
1193
            std::string new_rule = '/' + prefix_ + rule;
1194
            auto ruleObject = new DynamicRule(std::move(new_rule));
1195
            ruleObject->custom_templates_base = templates_dir_;
1196
            all_rules_.emplace_back(ruleObject);
1197

1198
            return *ruleObject;
1199
        }
1200

1201
        template<uint64_t N>
1202
        typename black_magic::arguments<N>::type::template rebind<TaggedRule>& new_rule_tagged(const std::string& rule)
16✔
1203
        {
1204
            std::string new_rule = '/' + prefix_ + rule;
16✔
1205
            using RuleT = typename black_magic::arguments<N>::type::template rebind<TaggedRule>;
1206

1207
            auto ruleObject = new RuleT(std::move(new_rule));
16✔
1208
            ruleObject->custom_templates_base = templates_dir_;
16✔
1209
            all_rules_.emplace_back(ruleObject);
16✔
1210

1211
            return *ruleObject;
16✔
1212
        }
16✔
1213

1214
        void register_blueprint(Blueprint& blueprint)
8✔
1215
        {
1216
            if (blueprints_.empty() || std::find(blueprints_.begin(), blueprints_.end(), &blueprint) == blueprints_.end())
8✔
1217
            {
1218
                apply_blueprint(blueprint);
8✔
1219
                blueprints_.emplace_back(&blueprint);
8✔
1220
            }
1221
            else
UNCOV
1222
                throw std::runtime_error("blueprint \"" + blueprint.prefix_ + "\" already exists in blueprint \"" + prefix_ + '\"');
×
1223
        }
8✔
1224

1225

1226
        CatchallRule& catchall_rule()
18✔
1227
        {
1228
            return catchall_rule_;
18✔
1229
        }
1230

1231
        template<typename App, typename... Middlewares>
1232
        void middlewares()
4✔
1233
        {
1234
            mw_indices_.push<App, Middlewares...>();
4✔
1235
        }
4✔
1236

1237
    private:
1238
        void apply_blueprint(Blueprint& blueprint)
8✔
1239
        {
1240

1241
            blueprint.prefix_ = prefix_ + '/' + blueprint.prefix_;
8✔
1242
            blueprint.static_dir_ = static_dir_ + '/' + blueprint.static_dir_;
8✔
1243
            blueprint.templates_dir_ = templates_dir_ + '/' + blueprint.templates_dir_;
8✔
1244
            for (auto& rule : blueprint.all_rules_)
16✔
1245
            {
1246
                std::string new_rule = '/' + prefix_ + rule->rule_;
8✔
1247
                rule->rule_ = new_rule;
8✔
1248
            }
8✔
1249
            for (Blueprint* bp_child : blueprint.blueprints_)
8✔
1250
            {
1251
                Blueprint& bp_ref = *bp_child;
×
UNCOV
1252
                apply_blueprint(bp_ref);
×
1253
            }
1254
        }
8✔
1255

1256
        std::string prefix_;
1257
        std::string static_dir_;
1258
        std::string templates_dir_;
1259
        std::vector<std::unique_ptr<BaseRule>> all_rules_;
1260
        CatchallRule catchall_rule_;
1261
        std::vector<Blueprint*> blueprints_;
1262
        detail::middleware_indices mw_indices_;
1263
        bool added_{false};
1264

1265
        friend class Router;
1266
    };
1267

1268
    /// Handles matching requests to existing rules and upgrade requests.
1269
    class Router {
1270
    public:
1271
        bool using_ssl;
1272

1273
        Router() : using_ssl(false)
108✔
1274
        {}
108✔
1275

1276
        DynamicRule& new_rule_dynamic(const std::string& rule)
10✔
1277
        {
1278
            auto ruleObject = new DynamicRule(rule);
10✔
1279
            all_rules_.emplace_back(ruleObject);
10✔
1280

1281
            return *ruleObject;
10✔
1282
        }
1283

1284
        template<uint64_t N>
1285
        typename black_magic::arguments<N>::type::template rebind<TaggedRule>& new_rule_tagged(const std::string& rule)
228✔
1286
        {
1287
            using RuleT = typename black_magic::arguments<N>::type::template rebind<TaggedRule>;
1288

1289
            auto ruleObject = new RuleT(rule);
228✔
1290
            all_rules_.emplace_back(ruleObject);
228✔
1291

1292
            return *ruleObject;
228✔
1293
        }
1294

1295
        CatchallRule& catchall_rule()
4✔
1296
        {
1297
            return catchall_rule_;
4✔
1298
        }
1299

1300
        void internal_add_rule_object(const std::string& rule, BaseRule* ruleObject)
230✔
1301
        {
1302
            internal_add_rule_object(rule, ruleObject, INVALID_BP_ID, blueprints_);
230✔
1303
        }
228✔
1304

1305
        void internal_add_rule_object(const std::string& rule, BaseRule* ruleObject, const size_t& BP_index, std::vector<Blueprint*>& blueprints)
246✔
1306
        {
1307
            bool has_trailing_slash = false;
246✔
1308
            std::string rule_without_trailing_slash;
246✔
1309
            if (rule.size() > 1 && rule.back() == '/')
246✔
1310
            {
1311
                has_trailing_slash = true;
4✔
1312
                rule_without_trailing_slash = rule;
4✔
1313
                rule_without_trailing_slash.pop_back();
4✔
1314
            }
1315

1316
            ruleObject->mw_indices_.pack();
246✔
1317

1318
            ruleObject->foreach_method([&](int method) {
246✔
1319
                per_methods_[method].rules.emplace_back(ruleObject);
252✔
1320
                per_methods_[method].trie.add(rule, per_methods_[method].rules.size() - 1,
504✔
1321
                    BP_index != INVALID_BP_ID ? blueprints[BP_index]->prefix().length() : 0,
506✔
1322
                    BP_index);
1323

1324
                // directory case:
1325
                //   request to '/about' url matches '/about/' rule
1326
                if (has_trailing_slash)
250✔
1327
                {
1328
                    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);
4✔
1329
                }
1330
            });
250✔
1331

1332
            ruleObject->set_added();
244✔
1333
        }
246✔
1334

1335
        void register_blueprint(Blueprint& blueprint)
6✔
1336
        {
1337
            if (std::find(blueprints_.begin(), blueprints_.end(), &blueprint) == blueprints_.end())
6✔
1338
            {
1339
                blueprints_.emplace_back(&blueprint);
6✔
1340
            }
1341
            else
UNCOV
1342
                throw std::runtime_error("blueprint \"" + blueprint.prefix_ + "\" already exists in router");
×
1343
        }
6✔
1344

UNCOV
1345
        void get_recursive_child_methods(Blueprint* blueprint, std::vector<HTTPMethod>& methods)
×
1346
        {
1347
            //we only need to deal with children if the blueprint has absolutely no methods (meaning its index won't be added to the trie)
UNCOV
1348
            if (blueprint->static_dir_.empty() && blueprint->all_rules_.empty())
×
1349
            {
UNCOV
1350
                for (Blueprint* bp : blueprint->blueprints_)
×
1351
                {
UNCOV
1352
                    get_recursive_child_methods(bp, methods);
×
1353
                }
1354
            }
1355
            else if (!blueprint->static_dir_.empty())
×
1356
                methods.emplace_back(HTTPMethod::Get);
×
UNCOV
1357
            for (auto& rule : blueprint->all_rules_)
×
1358
            {
1359
                rule->foreach_method([&methods](unsigned method) {
×
1360
                    HTTPMethod method_final = static_cast<HTTPMethod>(method);
×
1361
                    if (std::find(methods.begin(), methods.end(), method_final) == methods.end())
×
1362
                        methods.emplace_back(method_final);
×
UNCOV
1363
                });
×
1364
            }
1365
        }
1366

1367
        void validate_bp()
74✔
1368
        {
1369
            //Take all the routes from the registered blueprints and add them to `all_rules_` to be processed.
1370
            detail::middleware_indices blueprint_mw;
74✔
1371
            validate_bp(blueprints_, blueprint_mw);
74✔
1372
        }
74✔
1373

1374
        void validate_bp(std::vector<Blueprint*> blueprints, detail::middleware_indices& current_mw)
88✔
1375
        {
1376
            for (unsigned i = 0; i < blueprints.size(); i++)
102✔
1377
            {
1378
                Blueprint* blueprint = blueprints[i];
14✔
1379

1380
                if (blueprint->is_added()) continue;
14✔
1381

1382
                if (blueprint->static_dir_ == "" && blueprint->all_rules_.empty())
14✔
1383
                {
1384
                    std::vector<HTTPMethod> methods;
×
1385
                    get_recursive_child_methods(blueprint, methods);
×
UNCOV
1386
                    for (HTTPMethod x : methods)
×
1387
                    {
1388
                        int method_index = static_cast<int>(x);
×
UNCOV
1389
                        per_methods_[method_index].trie.add(blueprint->prefix(), 0, blueprint->prefix().length(), method_index);
×
1390
                    }
1391
                }
1392

1393
                current_mw.merge_back(blueprint->mw_indices_);
14✔
1394
                for (auto& rule : blueprint->all_rules_)
30✔
1395
                {
1396
                    if (rule && !rule->is_added())
16✔
1397
                    {
1398
                        auto upgraded = rule->upgrade();
16✔
1399
                        if (upgraded)
16✔
UNCOV
1400
                            rule = std::move(upgraded);
×
1401
                        rule->validate();
16✔
1402
                        rule->mw_indices_.merge_front(current_mw);
16✔
1403
                        internal_add_rule_object(rule->rule(), rule.get(), i, blueprints);
16✔
1404
                    }
16✔
1405
                }
1406
                validate_bp(blueprint->blueprints_, current_mw);
14✔
1407
                current_mw.pop_back(blueprint->mw_indices_);
14✔
1408
                blueprint->set_added();
14✔
1409
            }
1410
        }
88✔
1411

1412
        void validate()
132✔
1413
        {
1414
            for (auto& rule : all_rules_)
396✔
1415
            {
1416
                if (rule && !rule->is_added())
270✔
1417
                {
1418
                    auto upgraded = rule->upgrade();
234✔
1419
                    if (upgraded)
234✔
1420
                        rule = std::move(upgraded);
20✔
1421
                    rule->validate();
234✔
1422
                    internal_add_rule_object(rule->rule(), rule.get());
230✔
1423
                }
234✔
1424
            }
1425
            for (auto& per_method : per_methods_)
4,408✔
1426
            {
1427
                per_method.trie.validate();
4,282✔
1428
            }
1429
        }
126✔
1430

1431
        // TODO maybe add actual_method
1432
        template<typename Adaptor>
1433
        void handle_upgrade(const request& req, response& res, Adaptor&& adaptor)
18✔
1434
        {
1435
            if (req.method >= HTTPMethod::InternalMethodCount)
18✔
UNCOV
1436
                return;
×
1437

1438
            auto& per_method = per_methods_[static_cast<int>(req.method)];
18✔
1439
            auto& rules = per_method.rules;
18✔
1440
            size_t rule_index = per_method.trie.find(req.url).rule_index;
18✔
1441

1442
            if (!rule_index)
18✔
1443
            {
UNCOV
1444
                for (auto& method : per_methods_)
×
1445
                {
UNCOV
1446
                    if (method.trie.find(req.url).rule_index)
×
1447
                    {
1448
                        CROW_LOG_DEBUG << "Cannot match method " << req.url << " " << method_name(req.method);
×
1449
                        res = response(405);
×
1450
                        res.end();
×
UNCOV
1451
                        return;
×
1452
                    }
1453
                }
1454

1455
                CROW_LOG_INFO << "Cannot match rules " << req.url;
×
1456
                res = response(404);
×
1457
                res.end();
×
UNCOV
1458
                return;
×
1459
            }
1460

1461
            if (rule_index >= rules.size())
18✔
UNCOV
1462
                throw std::runtime_error("Trie internal structure corrupted!");
×
1463

1464
            if (rule_index == RULE_SPECIAL_REDIRECT_SLASH)
18✔
1465
            {
1466
                CROW_LOG_INFO << "Redirecting to a url with trailing slash: " << req.url;
×
1467
                res = response(301);
×
1468
                res.add_header("Location", req.url + "/");
×
1469
                res.end();
×
UNCOV
1470
                return;
×
1471
            }
1472

1473
            CROW_LOG_DEBUG << "Matched rule (upgrade) '" << rules[rule_index]->rule_ << "' "
36✔
1474
                           << static_cast<uint32_t>(req.method) << " / "
18✔
1475
                           << rules[rule_index]->get_methods();
18✔
1476

1477
            try
1478
            {
1479
                rules[rule_index]->handle_upgrade(req, res, std::move(adaptor));
18✔
1480
            }
UNCOV
1481
            catch (...)
×
1482
            {
1483
                exception_handler_(res);
×
1484
                res.end();
×
UNCOV
1485
                return;
×
1486
            }
1487
        }
1488

1489
        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)
76✔
1490
        {
1491
            // This statement makes 3 assertions:
1492
            // 1. The index is above 0.
1493
            // 2. The index does not lie outside the given blueprint list.
1494
            // 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).
1495
            //
1496
            // 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".
1497
            //
1498
            // 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
1499
            auto verify_prefix = [&bp_i, &index, &blueprints, &found_bps]() {
32✔
1500
                return index > 0 &&
16✔
1501
                       bp_i[index] < blueprints.size() &&
48✔
1502
                       blueprints[bp_i[index]]->prefix().substr(0, found_bps[index - 1]->prefix().length() + 1).compare(std::string(found_bps[index - 1]->prefix() + '/')) == 0;
44✔
1503
            };
76✔
1504
            if (index < bp_i.size())
76✔
1505
            {
1506

1507
                if (verify_prefix())
32✔
1508
                {
1509
                    found_bps.push_back(blueprints[bp_i[index]]);
12✔
1510
                    get_found_bp(bp_i, found_bps.back()->blueprints_, found_bps, ++index);
12✔
1511
                }
1512
                else
1513
                {
1514
                    if (found_bps.size() < 2)
20✔
1515
                    {
1516
                        found_bps.clear();
20✔
1517
                        found_bps.push_back(blueprints_[bp_i[index]]);
20✔
1518
                    }
1519
                    else
1520
                    {
1521
                        found_bps.pop_back();
×
1522
                        Blueprint* last_element = found_bps.back();
×
UNCOV
1523
                        found_bps.push_back(last_element->blueprints_[bp_i[index]]);
×
1524
                    }
1525
                    get_found_bp(bp_i, found_bps.back()->blueprints_, found_bps, ++index);
20✔
1526
                }
1527
            }
1528
        }
76✔
1529

1530
        CatchallRule& get_catch_all(const routing_handle_result& found) {
22✔
1531
            std::vector<Blueprint*> bps_found;
22✔
1532
            get_found_bp(found.blueprint_indices, blueprints_, bps_found);
22✔
1533
            if (!bps_found.empty()) {
22✔
1534
                for (size_t i = bps_found.size() - 1; i > 0; i--)
10✔
1535
                {
1536
                    if (bps_found[i]->catchall_rule().has_handler()) {
6✔
1537
                        return bps_found[i]->catchall_rule();
4✔
1538
                    }
1539
                }
1540
            }
1541
            return catchall_rule_;
18✔
1542
        }
22✔
1543

1544
        std::string get_error(const routing_handle_result& found)
22✔
1545
        {
1546
            const std::string EMPTY;
22✔
1547

1548
            std::vector<Blueprint*> bps_found;
22✔
1549
            get_found_bp(found.blueprint_indices, blueprints_, bps_found);
22✔
1550
            if (!bps_found.empty()) {
22✔
1551
                for (size_t i = bps_found.size() - 1; i > 0; i--) {
10✔
1552
                    if (bps_found[i]->catchall_rule().has_handler()) {
6✔
1553
#ifdef CROW_ENABLE_DEBUG
1554
                        return std::string("Redirected to Blueprint \"" + bps_found[i]->prefix() + "\" Catchall rule");
4✔
1555
#else
UNCOV
1556
                        return EMPTY;
×
1557
#endif
1558
                    }
1559
                }
1560
            } else  if (catchall_rule_.has_handler()) {
14✔
1561
#ifdef CROW_ENABLE_DEBUG
1562
                return std::string("Redirected to global Catchall rule");
8✔
1563
#else
UNCOV
1564
                return EMPTY;
×
1565
#endif
1566
            }
1567
            return EMPTY;
14✔
1568
        }
22✔
1569

1570
        std::unique_ptr<routing_handle_result> handle_initial(request& req, response& res)
470✔
1571
        {
1572
            HTTPMethod method_actual = req.method;
470✔
1573

1574
            std::unique_ptr<routing_handle_result> found{
1575
              new routing_handle_result(
1576
                0,
1577
                std::vector<size_t>(),
940✔
1578
                routing_params(),
470✔
1579
                HTTPMethod::InternalMethodCount)}; // This is always returned to avoid a null pointer dereference.
940✔
1580

1581
            // NOTE(EDev): This most likely will never run since the parser should handle this situation and close the connection before it gets here.
1582
            if (CROW_UNLIKELY(req.method >= HTTPMethod::InternalMethodCount))
470✔
UNCOV
1583
                return found;
×
1584
            else if (req.method == HTTPMethod::Head)
470✔
1585
            {
1586
                *found = per_methods_[static_cast<int>(method_actual)].trie.find(req.url);
4✔
1587
                // support HEAD requests using GET if not defined as method for the requested URL
1588
                if (!found->rule_index)
4✔
1589
                {
1590
                    method_actual = HTTPMethod::Get;
2✔
1591
                    *found = per_methods_[static_cast<int>(method_actual)].trie.find(req.url);
2✔
1592
                    if (!found->rule_index) // If a route is still not found, return a 404 without executing the rest of the HEAD specific code.
2✔
1593
                    {
1594
                        CROW_LOG_DEBUG << "Cannot match rules " << req.url;
×
1595
                        res = response(404); //TODO(EDev): Should this redirect to catchall?
×
1596
                        res.end();
×
UNCOV
1597
                        return found;
×
1598
                    }
1599
                }
1600

1601
                res.skip_body = true;
4✔
1602
                found->method = method_actual;
4✔
1603
                return found;
4✔
1604
            }
1605
            else if (req.method == HTTPMethod::Options)
466✔
1606
            {
1607
                std::string allow = "OPTIONS, HEAD";
14✔
1608

1609
                if (req.url == "/*")
14✔
1610
                {
1611
                    for (int i = 0; i < static_cast<int>(HTTPMethod::InternalMethodCount); i++)
70✔
1612
                    {
1613
                        if (static_cast<int>(HTTPMethod::Head) == i)
68✔
1614
                            continue; // HEAD is always allowed
2✔
1615

1616
                        if (!per_methods_[i].trie.is_empty())
66✔
1617
                        {
1618
                            allow.append(", ");
8✔
1619
                            allow.append(method_name(static_cast<HTTPMethod>(i)));
8✔
1620
                        }
1621
                    }
1622
#ifdef CROW_RETURNS_OK_ON_HTTP_OPTIONS_REQUEST
1623
                    res = response(crow::status::OK);
1624
#else
1625
                    res = response(crow::status::NO_CONTENT);
2✔
1626
#endif
1627

1628
                    res.set_header("Allow", allow);
6✔
1629
                    res.end();
2✔
1630
                    found->method = method_actual;
2✔
1631
                    return found;
2✔
1632
                }
1633
                else
1634
                {
1635
                    bool rules_matched = false;
12✔
1636
                    for (int i = 0; i < static_cast<int>(HTTPMethod::InternalMethodCount); i++)
420✔
1637
                    {
1638
                        if (per_methods_[i].trie.find(req.url).rule_index)
408✔
1639
                        {
1640
                            rules_matched = true;
14✔
1641

1642
                            if (static_cast<int>(HTTPMethod::Head) == i)
14✔
1643
                                continue; // HEAD is always allowed
2✔
1644

1645
                            allow.append(", ");
12✔
1646
                            allow.append(method_name(static_cast<HTTPMethod>(i)));
12✔
1647
                        }
1648
                    }
1649
                    if (rules_matched)
12✔
1650
                    {
1651
#ifdef CROW_RETURNS_OK_ON_HTTP_OPTIONS_REQUEST
1652
                        res = response(crow::status::OK);
1653
#else
1654
                        res = response(crow::status::NO_CONTENT);
10✔
1655
#endif
1656
                        res.set_header("Allow", allow);
30✔
1657
                        res.end();
10✔
1658
                        found->method = method_actual;
10✔
1659
                        return found;
10✔
1660
                    }
1661
                    else
1662
                    {
1663
                        CROW_LOG_DEBUG << "Cannot match rules " << req.url;
2✔
1664
                        res = response(404); //TODO(EDev): Should this redirect to catchall?
2✔
1665
                        res.end();
2✔
1666
                        return found;
2✔
1667
                    }
1668
                }
1669
            }
14✔
1670
            else // Every request that isn't a HEAD or OPTIONS request
1671
            {
1672
                *found = per_methods_[static_cast<int>(method_actual)].trie.find(req.url);
452✔
1673
                // 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
1674
                if (!found->rule_index)
452✔
1675
                {
1676
                    for (auto& per_method : per_methods_)
638✔
1677
                    {
1678
                        if (per_method.trie.find(req.url).rule_index) //Route found, but in another method
620✔
1679
                        {
1680
                            res.code = 405;
4✔
1681
                            found->catch_all = true;
4✔
1682
                            CROW_LOG_DEBUG << "Cannot match method " << req.url << " "
8✔
1683
                                           << method_name(method_actual) << ". " << get_error(*found);
4✔
1684
                            return found;
4✔
1685
                        }
1686
                    }
1687
                    //Route does not exist anywhere
1688

1689
                    res.code = 404;
18✔
1690
                    found->catch_all = true;
18✔
1691
                    CROW_LOG_DEBUG << "Cannot match rules " << req.url << ". " << get_error(*found);
18✔
1692
                    return found;
18✔
1693
                }
1694

1695
                found->method = method_actual;
430✔
1696
                return found;
430✔
1697
            }
UNCOV
1698
        }
×
1699

1700
        template<typename App>
1701
        void handle(request& req, response& res, routing_handle_result found)
434✔
1702
        {
1703
            if (found.catch_all) {
434✔
1704
                auto catch_all = get_catch_all(found);
22✔
1705
                if (catch_all.has_handler()) {
22✔
1706
                    try
1707
                    {
1708
                        catch_all.handler_(req, res);
8✔
1709
                    }
UNCOV
1710
                    catch (...)
×
1711
                    {
UNCOV
1712
                        exception_handler_(res);
×
1713
                    }
1714
                }
1715
                res.end();
22✔
1716
            } else {
22✔
1717
                HTTPMethod method_actual = found.method;
412✔
1718
                const auto& rules = per_methods_[static_cast<int>(method_actual)].rules;
412✔
1719
                const size_t rule_index = found.rule_index;
412✔
1720

1721
                if (rule_index >= rules.size())
412✔
UNCOV
1722
                    throw std::runtime_error("Trie internal structure corrupted!");
×
1723
                if (rule_index == RULE_SPECIAL_REDIRECT_SLASH) {
412✔
1724
                    CROW_LOG_INFO << "Redirecting to a url with trailing slash: " << req.url;
2✔
1725
                    res = response(301);
2✔
1726
                    res.add_header("Location", req.url + "/");
6✔
1727
                    res.end();
2✔
1728
                } else {
1729
                    CROW_LOG_DEBUG << "Matched rule '" << rules[rule_index]->rule_ << "' " << static_cast<uint32_t>(req.
820✔
1730
                                      method) << " / " << rules[rule_index]->get_methods();
410✔
1731

1732
                    try {
1733
                        BaseRule &rule = *rules[rule_index];
410✔
1734
                        handle_rule<App>(rule, req, res, found.r_params);
410✔
1735
                    } catch (...) {
412✔
1736
                        exception_handler_(res);
206✔
1737
                        res.end();
206✔
1738
                    }
1739
                }
1740
            }
1741
        }
434✔
1742

1743
        template<typename App>
1744
        typename std::enable_if<std::tuple_size<typename App::mw_container_t>::value != 0, void>::type
1745
          handle_rule(BaseRule& rule, crow::request& req, crow::response& res, const crow::routing_params& rp)
38✔
1746
        {
1747
            if (!rule.mw_indices_.empty())
38✔
1748
            {
1749
                auto& ctx = *reinterpret_cast<typename App::context_t*>(req.middleware_context);
6✔
1750
                auto& container = *reinterpret_cast<typename App::mw_container_t*>(req.middleware_container);
6✔
1751
                detail::middleware_call_criteria_dynamic<false> crit_fwd(rule.mw_indices_.indices());
6✔
1752

1753
                auto glob_completion_handler = std::move(res.complete_request_handler_);
6✔
1754
                res.complete_request_handler_ = [] {};
10✔
1755

1756
                detail::middleware_call_helper<decltype(crit_fwd),
1757
                                               0, typename App::context_t, typename App::mw_container_t>(crit_fwd, container, req, res, ctx);
6✔
1758

1759
                if (res.completed_)
6✔
1760
                {
1761
                    glob_completion_handler();
4✔
1762
                    return;
4✔
1763
                }
1764

1765
                res.complete_request_handler_ = [&rule, &ctx, &container, &req, &res, glob_completion_handler] {
4✔
1766
                    detail::middleware_call_criteria_dynamic<true> crit_bwd(rule.mw_indices_.indices());
2✔
1767

1768
                    detail::after_handlers_call_helper<
1769
                      decltype(crit_bwd),
1770
                      std::tuple_size<typename App::mw_container_t>::value - 1,
1771
                      typename App::context_t,
1772
                      typename App::mw_container_t>(crit_bwd, container, ctx, req, res);
2✔
1773
                    glob_completion_handler();
2✔
1774
                };
1775
            }
6✔
1776
            rule.handle(req, res, rp);
34✔
1777
        }
1778

1779
        template<typename App>
1780
        typename std::enable_if<std::tuple_size<typename App::mw_container_t>::value == 0, void>::type
1781
          handle_rule(BaseRule& rule, crow::request& req, crow::response& res, const crow::routing_params& rp)
372✔
1782
        {
1783
            rule.handle(req, res, rp);
372✔
1784
        }
166✔
1785

1786
        void debug_print()
2✔
1787
        {
1788
            for (int i = 0; i < static_cast<int>(HTTPMethod::InternalMethodCount); i++)
70✔
1789
            {
1790
                Trie& trie_ = per_methods_[i].trie;
68✔
1791
                if (!trie_.is_empty())
68✔
1792
                {
1793
                    CROW_LOG_DEBUG << method_name(static_cast<HTTPMethod>(i));
10✔
1794
                    trie_.debug_print();
10✔
1795
                }
1796
            }
1797
        }
2✔
1798

1799
        std::vector<Blueprint*>& blueprints()
74✔
1800
        {
1801
            return blueprints_;
74✔
1802
        }
1803

1804
        std::function<void(crow::response&)>& exception_handler()
2✔
1805
        {
1806
            return exception_handler_;
2✔
1807
        }
1808

1809
        static void default_exception_handler(response& res)
204✔
1810
        {
1811
            // any uncaught exceptions become 500s
1812
            res = response(500);
204✔
1813

1814
            try
1815
            {
1816
                throw;
204✔
1817
            }
1818
            catch (const bad_request& e)
204✔
1819
            {
1820
                res = response (400);
202✔
1821
                res.body = e.what();
202✔
1822
            }
202✔
1823
            catch (const std::exception& e)
2✔
1824
            {
1825
                CROW_LOG_ERROR << "An uncaught exception occurred: " << e.what();
2✔
1826
            }
2✔
UNCOV
1827
            catch (...)
×
1828
            {
1829
                CROW_LOG_ERROR << "An uncaught exception occurred. The type was unknown so no information was available.";
×
UNCOV
1830
            }
×
1831
        }
204✔
1832

1833
    private:
1834
        CatchallRule catchall_rule_;
1835

1836
        struct PerMethod
1837
        {
1838
            std::vector<BaseRule*> rules;
1839
            Trie trie;
1840

1841
            // rule index 0, 1 has special meaning; preallocate it to avoid duplication.
1842
            PerMethod():
3,672✔
1843
              rules(2) {}
11,016✔
1844
        };
1845
        std::array<PerMethod, static_cast<int>(HTTPMethod::InternalMethodCount)> per_methods_;
1846
        std::vector<std::unique_ptr<BaseRule>> all_rules_;
1847
        std::vector<Blueprint*> blueprints_;
1848
        std::function<void(crow::response&)> exception_handler_ = &default_exception_handler;
108✔
1849
    };
1850
} // 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