Coverage Report

Created: 2025-01-24 06:22

/src/crow/include/crow/app.h
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * \file crow/app.h
3
 * \brief This file includes the definition of the crow::Crow class,
4
 * the crow::App and crow::SimpleApp aliases, and some macros.
5
 *
6
 * In this file are defined:
7
 * - crow::Crow
8
 * - crow::App
9
 * - crow::SimpleApp
10
 * - \ref CROW_ROUTE
11
 * - \ref CROW_BP_ROUTE
12
 * - \ref CROW_WEBSOCKET_ROUTE
13
 * - \ref CROW_MIDDLEWARES
14
 * - \ref CROW_CATCHALL_ROUTE
15
 * - \ref CROW_BP_CATCHALL_ROUTE
16
 */
17
18
#pragma once
19
20
#include <chrono>
21
#include <string>
22
#include <functional>
23
#include <memory>
24
#include <future>
25
#include <cstdint>
26
#include <type_traits>
27
#include <thread>
28
#include <condition_variable>
29
30
#include "crow/version.h"
31
#include "crow/settings.h"
32
#include "crow/logging.h"
33
#include "crow/utility.h"
34
#include "crow/routing.h"
35
#include "crow/middleware_context.h"
36
#include "crow/http_request.h"
37
#include "crow/http_server.h"
38
#include "crow/task_timer.h"
39
#include "crow/websocket.h"
40
#ifdef CROW_ENABLE_COMPRESSION
41
#include "crow/compression.h"
42
#endif // #ifdef CROW_ENABLE_COMPRESSION
43
44
45
#ifdef CROW_MSVC_WORKAROUND
46
47
#define CROW_ROUTE(app, url) app.route_dynamic(url) // See the documentation in the comment below.
48
#define CROW_BP_ROUTE(blueprint, url) blueprint.new_rule_dynamic(url) // See the documentation in the comment below.
49
50
#else // #ifdef CROW_MSVC_WORKAROUND
51
52
/**
53
 * \def CROW_ROUTE(app, url)
54
 * \brief Creates a route for app using a rule.
55
 *
56
 * It use crow::Crow::route_dynamic or crow::Crow::route to define
57
 * a rule for your application. It's usage is like this:
58
 *
59
 * ```cpp
60
 * auto app = crow::SimpleApp(); // or crow::App()
61
 * CROW_ROUTE(app, "/")
62
 * ([](){
63
 *     return "<h1>Hello, world!</h1>";
64
 * });
65
 * ```
66
 *
67
 * This is the recommended way to define routes in a crow application.
68
 * \see [Page of guide "Routes"](https://crowcpp.org/master/guides/routes/).
69
 */
70
0
#define CROW_ROUTE(app, url) app.template route<crow::black_magic::get_parameter_tag(url)>(url)
71
72
/**
73
 * \def CROW_BP_ROUTE(blueprint, url)
74
 * \brief Creates a route for a blueprint using a rule.
75
 *
76
 * It may use crow::Blueprint::new_rule_dynamic or
77
 * crow::Blueprint::new_rule_tagged to define a new rule for
78
 * an given blueprint. It's usage is similar
79
 * to CROW_ROUTE macro:
80
 *
81
 * ```cpp
82
 * crow::Blueprint my_bp();
83
 * CROW_BP_ROUTE(my_bp, "/")
84
 * ([](){
85
 *     return "<h1>Hello, world!</h1>";
86
 * });
87
 * ```
88
 *
89
 * This is the recommended way to define routes in a crow blueprint
90
 * because of its compile-time capabilities.
91
 *
92
 * \see [Page of the guide "Blueprints"](https://crowcpp.org/master/guides/blueprints/).
93
 */
94
#define CROW_BP_ROUTE(blueprint, url) blueprint.new_rule_tagged<crow::black_magic::get_parameter_tag(url)>(url)
95
96
/**
97
 * \def CROW_WEBSOCKET_ROUTE(app, url)
98
 * \brief Defines WebSocket route for app.
99
 *
100
 * It binds a WebSocket route to app. Easy solution to implement
101
 * WebSockets in your app. The usage syntax of this macro is
102
 * like this:
103
 *
104
 * ```cpp
105
 * auto app = crow::SimpleApp(); // or crow::App()
106
 * CROW_WEBSOCKET_ROUTE(app, "/ws")
107
 *     .onopen([&](crow::websocket::connection& conn){
108
 *                do_something();
109
 *            })
110
 *     .onclose([&](crow::websocket::connection& conn, const std::string& reason, uint16_t){
111
 *                 do_something();
112
 *             })
113
 *     .onmessage([&](crow::websocket::connection&, const std::string& data, bool is_binary){
114
 *                   if (is_binary)
115
 *                       do_something(data);
116
 *                   else
117
 *                       do_something_else(data);
118
 *               });
119
 * ```
120
 *
121
 * \see [Page of the guide "WebSockets"](https://crowcpp.org/master/guides/websockets/).
122
 */
123
#define CROW_WEBSOCKET_ROUTE(app, url) app.route<crow::black_magic::get_parameter_tag(url)>(url).websocket<std::remove_reference<decltype(app)>::type>(&app)
124
125
/**
126
 * \def CROW_MIDDLEWARES(app, ...)
127
 * \brief Enable a Middleware for an specific route in app
128
 * or blueprint.
129
 *
130
 * It defines the usage of a Middleware in one route. And it
131
 * can be used in both crow::SimpleApp (and crow::App) instances and
132
 * crow::Blueprint. Its usage syntax is like this:
133
 *
134
 * ```cpp
135
 * auto app = crow::SimpleApp(); // or crow::App()
136
 * CROW_ROUTE(app, "/with_middleware")
137
 * .CROW_MIDDLEWARES(app, LocalMiddleware) // Can be used more than one
138
 * ([]() {                                 // middleware.
139
 *     return "Hello world!";
140
 * });
141
 * ```
142
 *
143
 * \see [Page of the guide "Middlewares"](https://crowcpp.org/master/guides/middleware/).
144
 */
145
#define CROW_MIDDLEWARES(app, ...) template middlewares<typename std::remove_reference<decltype(app)>::type, __VA_ARGS__>()
146
147
#endif // #ifdef CROW_MSVC_WORKAROUND
148
149
/**
150
 * \def CROW_CATCHALL_ROUTE(app)
151
 * \brief Defines a custom catchall route for app using a
152
 * custom rule.
153
 *
154
 * It defines a handler when the client make a request for an
155
 * undefined route. Instead of just reply with a `404` status
156
 * code (default behavior), you can define a custom handler
157
 * using this macro.
158
 *
159
 * \see [Page of the guide "Routes" (Catchall routes)](https://crowcpp.org/master/guides/routes/#catchall-routes).
160
 */
161
#define CROW_CATCHALL_ROUTE(app) app.catchall_route()
162
163
/**
164
 * \def CROW_BP_CATCHALL_ROUTE(blueprint)
165
 * \brief Defines a custom catchall route for blueprint
166
 * using a custom rule.
167
 *
168
 * It defines a handler when the client make a request for an
169
 * undefined route in the blueprint.
170
 *
171
 * \see [Page of the guide "Blueprint" (Define a custom Catchall route)](https://crowcpp.org/master/guides/blueprints/#define-a-custom-catchall-route).
172
 */
173
#define CROW_BP_CATCHALL_ROUTE(blueprint) blueprint.catchall_rule()
174
175
176
/**
177
 * \namespace crow
178
 * \brief The main namespace of the library. In this namespace
179
 * is defined the most important classes and functions of the
180
 * library.
181
 *
182
 * Within this namespace, the Crow class, Router class, Connection
183
 * class, and other are defined.
184
 */
185
namespace crow
186
{
187
#ifdef CROW_ENABLE_SSL
188
    using ssl_context_t = asio::ssl::context;
189
#endif
190
    /**
191
     * \class Crow
192
     * \brief The main server application class.
193
     *
194
     * Use crow::SimpleApp or crow::App<Middleware1, Middleware2, etc...> instead of
195
     * directly instantiate this class.
196
     */
197
    template<typename... Middlewares>
198
    class Crow
199
    {
200
    public:
201
        /// \brief This is the crow application
202
        using self_t = Crow;
203
204
        /// \brief The HTTP server
205
        using server_t = Server<Crow, SocketAdaptor, Middlewares...>;
206
207
#ifdef CROW_ENABLE_SSL
208
        /// \brief An HTTP server that runs on SSL with an SSLAdaptor
209
        using ssl_server_t = Server<Crow, SSLAdaptor, Middlewares...>;
210
#endif
211
        Crow()
212
0
        {}
213
214
        /// \brief Construct Crow with a subset of middleware
215
        template<typename... Ts>
216
        Crow(Ts&&... ts):
217
          middlewares_(make_middleware_tuple(std::forward<Ts>(ts)...))
218
        {}
219
220
        /// \brief Process an Upgrade request
221
        ///
222
        /// Currently used to upgrade an HTTP connection to a WebSocket connection
223
        template<typename Adaptor>
224
        void handle_upgrade(const request& req, response& res, Adaptor&& adaptor)
225
0
        {
226
0
            router_.handle_upgrade(req, res, adaptor);
227
0
        }
228
229
        /// \brief Process only the method and URL of a request and provide a route (or an error response)
230
        std::unique_ptr<routing_handle_result> handle_initial(request& req, response& res)
231
0
        {
232
0
            return router_.handle_initial(req, res);
233
0
        }
234
235
        /// \brief Process the fully parsed request and generate a response for it
236
        void handle(request& req, response& res, std::unique_ptr<routing_handle_result>& found)
237
0
        {
238
0
            router_.handle<self_t>(req, res, *found);
239
0
        }
240
241
        /// \brief Process a fully parsed request from start to finish (primarily used for debugging)
242
        void handle_full(request& req, response& res)
243
        {
244
            auto found = handle_initial(req, res);
245
            if (found->rule_index)
246
                handle(req, res, found);
247
        }
248
249
        /// \brief Create a dynamic route using a rule (**Use CROW_ROUTE instead**)
250
        DynamicRule& route_dynamic(const std::string& rule)
251
        {
252
            return router_.new_rule_dynamic(rule);
253
        }
254
255
        /// \brief Create a route using a rule (**Use CROW_ROUTE instead**)
256
        template<uint64_t Tag>
257
        auto route(const std::string& rule)
258
          -> typename std::invoke_result<decltype(&Router::new_rule_tagged<Tag>), Router, const std::string&>::type
259
0
        {
260
0
            return router_.new_rule_tagged<Tag>(rule);
261
0
        }
Unexecuted instantiation: _ZN4crow4CrowIJEE5routeILm10EEENSt3__113invoke_resultIDTadsr6RouterE15new_rule_taggedIXT_EEEJNS_6RouterERKNS3_12basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEEE4typeESE_
Unexecuted instantiation: _ZN4crow4CrowIJEE5routeILm5EEENSt3__113invoke_resultIDTadsr6RouterE15new_rule_taggedIXT_EEEJNS_6RouterERKNS3_12basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEEE4typeESE_
262
263
        /// \brief Create a route for any requests without a proper route (**Use CROW_CATCHALL_ROUTE instead**)
264
        CatchallRule& catchall_route()
265
        {
266
            return router_.catchall_rule();
267
        }
268
269
        /// \brief Set the default max payload size for websockets
270
        self_t& websocket_max_payload(uint64_t max_payload)
271
        {
272
            max_payload_ = max_payload;
273
            return *this;
274
        }
275
276
        /// \brief Get the default max payload size for websockets
277
        uint64_t websocket_max_payload()
278
        {
279
            return max_payload_;
280
        }
281
282
        self_t& signal_clear()
283
        {
284
            signals_.clear();
285
            return *this;
286
        }
287
288
        self_t& signal_add(int signal_number)
289
        {
290
            signals_.push_back(signal_number);
291
            return *this;
292
        }
293
294
        std::vector<int> signals()
295
        {
296
            return signals_;
297
        }
298
299
        /// \brief Set the port that Crow will handle requests on
300
        self_t& port(std::uint16_t port)
301
0
        {
302
0
            port_ = port;
303
0
            return *this;
304
0
        }
305
306
        /// \brief Get the port that Crow will handle requests on
307
        std::uint16_t port() const
308
        {
309
            if (!server_started_)
310
            {
311
                return port_;
312
            }
313
#ifdef CROW_ENABLE_SSL
314
            if (ssl_used_)
315
            {
316
                return ssl_server_->port();
317
            }
318
            else
319
#endif
320
            {
321
                return server_->port();
322
            }
323
        }
324
325
        /// \brief Set the connection timeout in seconds (default is 5)
326
        self_t& timeout(std::uint8_t timeout)
327
        {
328
            timeout_ = timeout;
329
            return *this;
330
        }
331
332
        /// \brief Set the server name
333
        self_t& server_name(std::string server_name)
334
        {
335
            server_name_ = server_name;
336
            return *this;
337
        }
338
339
        /// \brief The IP address that Crow will handle requests on (default is 0.0.0.0)
340
        self_t& bindaddr(std::string bindaddr)
341
0
        {
342
0
            bindaddr_ = bindaddr;
343
0
            return *this;
344
0
        }
345
346
        /// \brief Get the address that Crow will handle requests on
347
        std::string bindaddr()
348
        {
349
            return bindaddr_;
350
        }
351
352
        /// \brief Run the server on multiple threads using all available threads
353
        self_t& multithreaded()
354
0
        {
355
0
            return concurrency(std::thread::hardware_concurrency());
356
0
        }
357
358
        /// \brief Run the server on multiple threads using a specific number
359
        self_t& concurrency(std::uint16_t concurrency)
360
0
        {
361
0
            if (concurrency < 2) // Crow can have a minimum of 2 threads running
362
0
                concurrency = 2;
363
0
            concurrency_ = concurrency;
364
0
            return *this;
365
0
        }
366
367
        /// \brief Get the number of threads that server is using
368
        std::uint16_t concurrency() const
369
        {
370
            return concurrency_;
371
        }
372
373
        /// \brief Set the server's log level
374
        ///
375
        /// Possible values are:
376
        /// - crow::LogLevel::Debug       (0)
377
        /// - crow::LogLevel::Info        (1)
378
        /// - crow::LogLevel::Warning     (2)
379
        /// - crow::LogLevel::Error       (3)
380
        /// - crow::LogLevel::Critical    (4)
381
        self_t& loglevel(LogLevel level)
382
        {
383
            crow::logger::setLogLevel(level);
384
            return *this;
385
        }
386
387
        /// \brief Set the response body size (in bytes) beyond which Crow automatically streams responses (Default is 1MiB)
388
        ///
389
        /// Any streamed response is unaffected by Crow's timer, and therefore won't timeout before a response is fully sent.
390
        self_t& stream_threshold(size_t threshold)
391
        {
392
            res_stream_threshold_ = threshold;
393
            return *this;
394
        }
395
396
        /// \brief Get the response body size (in bytes) beyond which Crow automatically streams responses
397
        size_t& stream_threshold()
398
0
        {
399
0
            return res_stream_threshold_;
400
0
        }
401
402
403
        self_t& register_blueprint(Blueprint& blueprint)
404
        {
405
            router_.register_blueprint(blueprint);
406
            return *this;
407
        }
408
409
        /// \brief Set the function to call to handle uncaught exceptions generated in routes (Default generates error 500).
410
        ///
411
        /// The function must have the following signature: void(crow::response&).
412
        /// It must set the response passed in argument to the function, which will be sent back to the client.
413
        /// See Router::default_exception_handler() for the default implementation.
414
        template<typename Func>
415
        self_t& exception_handler(Func&& f)
416
        {
417
            router_.exception_handler() = std::forward<Func>(f);
418
            return *this;
419
        }
420
421
        std::function<void(crow::response&)>& exception_handler()
422
        {
423
            return router_.exception_handler();
424
        }
425
426
        /// \brief Set a custom duration and function to run on every tick
427
        template<typename Duration, typename Func>
428
        self_t& tick(Duration d, Func f)
429
        {
430
            tick_interval_ = std::chrono::duration_cast<std::chrono::milliseconds>(d);
431
            tick_function_ = f;
432
            return *this;
433
        }
434
435
#ifdef CROW_ENABLE_COMPRESSION
436
437
        self_t& use_compression(compression::algorithm algorithm)
438
        {
439
            comp_algorithm_ = algorithm;
440
            compression_used_ = true;
441
            return *this;
442
        }
443
444
        compression::algorithm compression_algorithm()
445
        {
446
            return comp_algorithm_;
447
        }
448
449
        bool compression_used() const
450
        {
451
            return compression_used_;
452
        }
453
#endif
454
455
        /// \brief Apply blueprints
456
        void add_blueprint()
457
0
        {
458
#if defined(__APPLE__) || defined(__MACH__)
459
            if (router_.blueprints().empty()) return;
460
#endif
461
462
0
            for (Blueprint* bp : router_.blueprints())
463
0
            {
464
0
                if (bp->static_dir().empty()) {
465
0
                    CROW_LOG_ERROR << "Blueprint " << bp->prefix() << " and its sub-blueprints ignored due to empty static directory.";
466
0
                    continue;
467
0
                }
468
0
                auto static_dir_ = crow::utility::normalize_path(bp->static_dir());
469
470
0
                bp->new_rule_tagged<crow::black_magic::get_parameter_tag(CROW_STATIC_ENDPOINT)>(CROW_STATIC_ENDPOINT)([static_dir_](crow::response& res, std::string file_path_partial) {
471
0
                    utility::sanitize_filename(file_path_partial);
472
0
                    res.set_static_file_info_unsafe(static_dir_ + file_path_partial);
473
0
                    res.end();
474
0
                });
475
0
            }
476
477
0
            router_.validate_bp();
478
0
        }
479
480
        /// \brief Go through the rules, upgrade them if possible, and add them to the list of rules
481
        void add_static_dir()
482
0
        {
483
0
            if (are_static_routes_added()) return;
484
0
            auto static_dir_ = crow::utility::normalize_path(CROW_STATIC_DIRECTORY);
485
486
0
            route<crow::black_magic::get_parameter_tag(CROW_STATIC_ENDPOINT)>(CROW_STATIC_ENDPOINT)([static_dir_](crow::response& res, std::string file_path_partial) {
487
0
                utility::sanitize_filename(file_path_partial);
488
0
                res.set_static_file_info_unsafe(static_dir_ + file_path_partial);
489
0
                res.end();
490
0
            });
491
0
            set_static_routes_added();
492
0
        }
493
494
        /// \brief A wrapper for `validate()` in the router
495
        void validate()
496
0
        {
497
0
            router_.validate();
498
0
        }
499
500
        /// \brief Run the server
501
        void run()
502
0
        {
503
0
#ifndef CROW_DISABLE_STATIC_DIR
504
0
            add_blueprint();
505
0
            add_static_dir();
506
0
#endif
507
0
            validate();
508
509
0
            error_code ec;
510
0
            asio::ip::address addr = asio::ip::make_address(bindaddr_,ec);
511
0
            if (ec){
512
0
                CROW_LOG_ERROR << ec.message() << " - Can not create valid ip address from string: \"" << bindaddr_ << "\"";
513
0
                return;
514
0
            }
515
0
            tcp::endpoint endpoint(addr, port_);
516
#ifdef CROW_ENABLE_SSL
517
            if (ssl_used_)
518
            {
519
                router_.using_ssl = true;
520
                ssl_server_ = std::move(std::unique_ptr<ssl_server_t>(new ssl_server_t(this, endpoint, server_name_, &middlewares_, concurrency_, timeout_, &ssl_context_)));
521
                ssl_server_->set_tick_function(tick_interval_, tick_function_);
522
                ssl_server_->signal_clear();
523
                for (auto snum : signals_)
524
                {
525
                    ssl_server_->signal_add(snum);
526
                }
527
                notify_server_start();
528
                ssl_server_->run();
529
            }
530
            else
531
#endif
532
0
            {
533
0
                server_ = std::move(std::unique_ptr<server_t>(new server_t(this, endpoint, server_name_, &middlewares_, concurrency_, timeout_, nullptr)));
534
0
                server_->set_tick_function(tick_interval_, tick_function_);
535
0
                for (auto snum : signals_)
536
0
                {
537
0
                    server_->signal_add(snum);
538
0
                }
539
0
                notify_server_start();
540
0
                server_->run();
541
0
            }
542
0
        }
543
544
        /// \brief Non-blocking version of \ref run()
545
        ///
546
        /// The output from this method needs to be saved into a variable!
547
        /// Otherwise the call will be made on the same thread.
548
        std::future<void> run_async()
549
        {
550
            return std::async(std::launch::async, [&] {
551
                this->run();
552
            });
553
        }
554
555
        /// \brief Stop the server
556
        void stop()
557
        {
558
#ifdef CROW_ENABLE_SSL
559
            if (ssl_used_)
560
            {
561
                if (ssl_server_) { ssl_server_->stop(); }
562
            }
563
            else
564
#endif
565
            {
566
                // TODO(EDev): Move these 6 lines to a method in http_server.
567
                std::vector<crow::websocket::connection*> websockets_to_close = websockets_;
568
                for (auto websocket : websockets_to_close)
569
                {
570
                    CROW_LOG_INFO << "Quitting Websocket: " << websocket;
571
                    websocket->close("Server Application Terminated");
572
                }
573
                if (server_) { server_->stop(); }
574
            }
575
        }
576
577
        void add_websocket(crow::websocket::connection* conn)
578
        {
579
            websockets_.push_back(conn);
580
        }
581
582
        void remove_websocket(crow::websocket::connection* conn)
583
        {
584
            websockets_.erase(std::remove(websockets_.begin(), websockets_.end(), conn), websockets_.end());
585
        }
586
587
        /// \brief Print the routing paths defined for each HTTP method
588
        void debug_print()
589
        {
590
            CROW_LOG_DEBUG << "Routing:";
591
            router_.debug_print();
592
        }
593
594
595
#ifdef CROW_ENABLE_SSL
596
597
        /// \brief Use certificate and key files for SSL
598
        self_t& ssl_file(const std::string& crt_filename, const std::string& key_filename)
599
        {
600
            ssl_used_ = true;
601
            ssl_context_.set_verify_mode(asio::ssl::verify_peer);
602
            ssl_context_.set_verify_mode(asio::ssl::verify_client_once);
603
            ssl_context_.use_certificate_file(crt_filename, ssl_context_t::pem);
604
            ssl_context_.use_private_key_file(key_filename, ssl_context_t::pem);
605
            ssl_context_.set_options(
606
              asio::ssl::context::default_workarounds | asio::ssl::context::no_sslv2 | asio::ssl::context::no_sslv3);
607
            return *this;
608
        }
609
610
        /// \brief Use `.pem` file for SSL
611
        self_t& ssl_file(const std::string& pem_filename)
612
        {
613
            ssl_used_ = true;
614
            ssl_context_.set_verify_mode(asio::ssl::verify_peer);
615
            ssl_context_.set_verify_mode(asio::ssl::verify_client_once);
616
            ssl_context_.load_verify_file(pem_filename);
617
            ssl_context_.set_options(
618
              asio::ssl::context::default_workarounds | asio::ssl::context::no_sslv2 | asio::ssl::context::no_sslv3);
619
            return *this;
620
        }
621
622
        /// \brief Use certificate chain and key files for SSL
623
        self_t& ssl_chainfile(const std::string& crt_filename, const std::string& key_filename)
624
        {
625
            ssl_used_ = true;
626
            ssl_context_.set_verify_mode(asio::ssl::verify_peer);
627
            ssl_context_.set_verify_mode(asio::ssl::verify_client_once);
628
            ssl_context_.use_certificate_chain_file(crt_filename);
629
            ssl_context_.use_private_key_file(key_filename, ssl_context_t::pem);
630
            ssl_context_.set_options(
631
              asio::ssl::context::default_workarounds | asio::ssl::context::no_sslv2 | asio::ssl::context::no_sslv3);
632
            return *this;
633
        }
634
635
        self_t& ssl(asio::ssl::context&& ctx)
636
        {
637
            ssl_used_ = true;
638
            ssl_context_ = std::move(ctx);
639
            return *this;
640
        }
641
642
        bool ssl_used() const
643
        {
644
            return ssl_used_;
645
        }
646
#else
647
648
        template<typename T, typename... Remain>
649
        self_t& ssl_file(T&&, Remain&&...)
650
        {
651
            // We can't call .ssl() member function unless CROW_ENABLE_SSL is defined.
652
            static_assert(
653
              // make static_assert dependent to T; always false
654
              std::is_base_of<T, void>::value,
655
              "Define CROW_ENABLE_SSL to enable ssl support.");
656
            return *this;
657
        }
658
659
        template<typename T, typename... Remain>
660
        self_t& ssl_chainfile(T&&, Remain&&...)
661
        {
662
            // We can't call .ssl() member function unless CROW_ENABLE_SSL is defined.
663
            static_assert(
664
              // make static_assert dependent to T; always false
665
              std::is_base_of<T, void>::value,
666
              "Define CROW_ENABLE_SSL to enable ssl support.");
667
            return *this;
668
        }
669
670
        template<typename T>
671
        self_t& ssl(T&&)
672
        {
673
            // We can't call .ssl() member function unless CROW_ENABLE_SSL is defined.
674
            static_assert(
675
              // make static_assert dependent to T; always false
676
              std::is_base_of<T, void>::value,
677
              "Define CROW_ENABLE_SSL to enable ssl support.");
678
            return *this;
679
        }
680
681
        bool ssl_used() const
682
0
        {
683
0
            return false;
684
0
        }
685
#endif
686
687
        // middleware
688
        using context_t = detail::context<Middlewares...>;
689
        using mw_container_t = std::tuple<Middlewares...>;
690
        template<typename T>
691
        typename T::context& get_context(const request& req)
692
        {
693
            static_assert(black_magic::contains<T, Middlewares...>::value, "App doesn't have the specified middleware type.");
694
            auto& ctx = *reinterpret_cast<context_t*>(req.middleware_context);
695
            return ctx.template get<T>();
696
        }
697
698
        template<typename T>
699
        T& get_middleware()
700
        {
701
            return utility::get_element_by_type<T, Middlewares...>(middlewares_);
702
        }
703
704
        /// \brief Wait until the server has properly started
705
        std::cv_status wait_for_server_start(std::chrono::milliseconds wait_timeout = std::chrono::milliseconds(3000))
706
        {
707
            std::cv_status status = std::cv_status::no_timeout;
708
            auto wait_until = std::chrono::steady_clock::now() + wait_timeout;
709
            {
710
                std::unique_lock<std::mutex> lock(start_mutex_);
711
                while (!server_started_ && (status == std::cv_status::no_timeout))
712
                {
713
                    status = cv_started_.wait_until(lock, wait_until);
714
                }
715
            }
716
            
717
            if (status == std::cv_status::no_timeout)
718
            {
719
                if (server_)
720
                {
721
                    status = server_->wait_for_start(wait_until);
722
                }
723
#ifdef CROW_ENABLE_SSL
724
                else if (ssl_server_)
725
                {
726
                    status = ssl_server_->wait_for_start(wait_until);
727
                }
728
#endif
729
            }
730
            return status;
731
        }
732
733
    private:
734
        template<typename... Ts>
735
        std::tuple<Middlewares...> make_middleware_tuple(Ts&&... ts)
736
        {
737
            auto fwd = std::forward_as_tuple((ts)...);
738
            return std::make_tuple(
739
              std::forward<Middlewares>(
740
                black_magic::tuple_extract<Middlewares, decltype(fwd)>(fwd))...);
741
        }
742
743
        /// \brief Notify anything using \ref wait_for_server_start() to proceed
744
        void notify_server_start()
745
0
        {
746
0
            std::unique_lock<std::mutex> lock(start_mutex_);
747
0
            server_started_ = true;
748
0
            cv_started_.notify_all();
749
0
        }
750
751
0
        void set_static_routes_added() {
752
0
            static_routes_added_ = true;
753
0
        }
754
755
0
        bool are_static_routes_added() {
756
0
            return static_routes_added_;
757
0
        }
758
759
    private:
760
        std::uint8_t timeout_{5};
761
        uint16_t port_ = 80;
762
        uint16_t concurrency_ = 2;
763
        uint64_t max_payload_{UINT64_MAX};
764
        std::string server_name_ = std::string("Crow/") + VERSION;
765
        std::string bindaddr_ = "0.0.0.0";
766
        size_t res_stream_threshold_ = 1048576;
767
        Router router_;
768
        bool static_routes_added_{false};
769
770
#ifdef CROW_ENABLE_COMPRESSION
771
        compression::algorithm comp_algorithm_;
772
        bool compression_used_{false};
773
#endif
774
775
        std::chrono::milliseconds tick_interval_;
776
        std::function<void()> tick_function_;
777
778
        std::tuple<Middlewares...> middlewares_;
779
780
#ifdef CROW_ENABLE_SSL
781
        std::unique_ptr<ssl_server_t> ssl_server_;
782
        bool ssl_used_{false};
783
        ssl_context_t ssl_context_{asio::ssl::context::sslv23};
784
#endif
785
786
        std::unique_ptr<server_t> server_;
787
788
        std::vector<int> signals_{SIGINT, SIGTERM};
789
790
        bool server_started_{false};
791
        std::condition_variable cv_started_;
792
        std::mutex start_mutex_;
793
        std::vector<crow::websocket::connection*> websockets_;
794
    };
795
796
    /// \brief Alias of Crow<Middlewares...>. Useful if you want
797
    /// a instance of an Crow application that require Middlewares
798
    template<typename... Middlewares>
799
    using App = Crow<Middlewares...>;
800
801
    /// \brief Alias of Crow<>. Useful if you want a instance of
802
    /// an Crow application that doesn't require of Middlewares
803
    using SimpleApp = Crow<>;
804
} // namespace crow