#pragma once #include #include #include #include #include #include #include #include #include #include "crow/settings.h" #include "crow/logging.h" #include "crow/utility.h" #include "crow/routing.h" #include "crow/middleware_context.h" #include "crow/http_request.h" #include "crow/http_server.h" #ifdef CROW_MSVC_WORKAROUND #define CROW_ROUTE(app, url) app.route_dynamic(url) #else #define CROW_ROUTE(app, url) app.route(url) #endif namespace crow { #ifdef CROW_ENABLE_SSL using ssl_context_t = boost::asio::ssl::context; #endif template class Crow { public: using self_t = Crow; using server_t = Server; #ifdef CROW_ENABLE_SSL using ssl_server_t = Server; #endif Crow() { } template void handle_upgrade(const request& req, response& res, Adaptor&& adaptor) { router_.handle_upgrade(req, res, adaptor); } void handle(const request& req, response& res) { router_.handle(req, res); } DynamicRule& route_dynamic(std::string&& rule) { return router_.new_rule_dynamic(std::move(rule)); } template auto route(std::string&& rule) -> typename std::result_of)(Router, std::string&&)>::type { return router_.new_rule_tagged(std::move(rule)); } self_t& port(std::uint16_t port) { port_ = port; return *this; } self_t& bindaddr(std::string bindaddr) { bindaddr_ = bindaddr; return *this; } self_t& multithreaded() { return concurrency(std::thread::hardware_concurrency()); } self_t& concurrency(std::uint16_t concurrency) { if (concurrency < 1) concurrency = 1; concurrency_ = concurrency; return *this; } void validate() { router_.validate(); } void notify_server_start() { std::unique_lock lock(start_mutex_); server_started_ = true; cv_started_.notify_all(); } void run() { validate(); #ifdef CROW_ENABLE_SSL if (use_ssl_) { ssl_server_ = std::move(std::unique_ptr(new ssl_server_t(this, bindaddr_, port_, &middlewares_, concurrency_, &ssl_context_))); ssl_server_->set_tick_function(tick_interval_, tick_function_); notify_server_start(); ssl_server_->run(); } else #endif { server_ = std::move(std::unique_ptr(new server_t(this, bindaddr_, port_, &middlewares_, concurrency_, nullptr))); server_->set_tick_function(tick_interval_, tick_function_); notify_server_start(); server_->run(); } } void stop() { #ifdef CROW_ENABLE_SSL if (use_ssl_) { ssl_server_->stop(); } else #endif { server_->stop(); } } void debug_print() { CROW_LOG_DEBUG << "Routing:"; router_.debug_print(); } self_t& loglevel(crow::LogLevel level) { crow::logger::setLogLevel(level); return *this; } #ifdef CROW_ENABLE_SSL self_t& ssl_file(const std::string& crt_filename, const std::string& key_filename) { use_ssl_ = true; ssl_context_.set_verify_mode(boost::asio::ssl::verify_peer); ssl_context_.use_certificate_file(crt_filename, ssl_context_t::pem); ssl_context_.use_private_key_file(key_filename, ssl_context_t::pem); ssl_context_.set_options( boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::no_sslv3 ); return *this; } self_t& ssl_file(const std::string& pem_filename) { use_ssl_ = true; ssl_context_.set_verify_mode(boost::asio::ssl::verify_peer); ssl_context_.load_verify_file(pem_filename); ssl_context_.set_options( boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::no_sslv3 ); return *this; } self_t& ssl(boost::asio::ssl::context&& ctx) { use_ssl_ = true; ssl_context_ = std::move(ctx); return *this; } bool use_ssl_{false}; ssl_context_t ssl_context_{boost::asio::ssl::context::sslv23}; #else template self_t& ssl_file(T&&, Remain&&...) { // We can't call .ssl() member function unless CROW_ENABLE_SSL is defined. static_assert( // make static_assert dependent to T; always false std::is_base_of::value, "Define CROW_ENABLE_SSL to enable ssl support."); return *this; } template self_t& ssl(T&&) { // We can't call .ssl() member function unless CROW_ENABLE_SSL is defined. static_assert( // make static_assert dependent to T; always false std::is_base_of::value, "Define CROW_ENABLE_SSL to enable ssl support."); return *this; } #endif // middleware using context_t = detail::context; template typename T::context& get_context(const request& req) { static_assert(black_magic::contains::value, "App doesn't have the specified middleware type."); auto& ctx = *reinterpret_cast(req.middleware_context); return ctx.template get(); } template T& get_middleware() { return utility::get_element_by_type(middlewares_); } template self_t& tick(Duration d, Func f) { tick_interval_ = std::chrono::duration_cast(d); tick_function_ = f; return *this; } void wait_for_server_start() { std::unique_lock lock(start_mutex_); if (server_started_) return; cv_started_.wait(lock); } private: uint16_t port_ = 80; uint16_t concurrency_ = 1; std::string bindaddr_ = "0.0.0.0"; Router router_; std::chrono::milliseconds tick_interval_; std::function tick_function_; std::tuple middlewares_; #ifdef CROW_ENABLE_SSL std::unique_ptr ssl_server_; #endif std::unique_ptr server_; bool server_started_{false}; std::condition_variable cv_started_; std::mutex start_mutex_; }; template using App = Crow; using SimpleApp = Crow<>; }