| #pragma once |
| |
| #include <chrono> |
| #include <cstdint> |
| #include <functional> |
| #include <future> |
| #include <memory> |
| #include <string> |
| #include <thread> |
| #include <utility> |
| #include "crow/http_request.h" |
| #include "crow/http_server.h" |
| #include "crow/logging.h" |
| #include "crow/middleware_context.h" |
| #include "crow/routing.h" |
| #include "crow/utility.h" |
| |
| #define CROW_ROUTE(app, url) \ |
| app.template route<crow::black_magic::get_parameter_tag(url)>(url) |
| |
| namespace crow { |
| #ifdef CROW_ENABLE_SSL |
| using ssl_context_t = boost::asio::ssl::context; |
| #endif |
| template <typename... Middlewares> |
| class Crow { |
| public: |
| using self_t = Crow; |
| using server_t = Server<Crow, SocketAdaptor, Middlewares...>; |
| #ifdef CROW_ENABLE_SSL |
| using ssl_server_t = Server<Crow, SSLAdaptor, Middlewares...>; |
| #endif |
| explicit Crow(std::shared_ptr<boost::asio::io_service> io = |
| std::make_shared<boost::asio::io_service>()) |
| : io_(std::move(io)) {} |
| ~Crow() { this->stop(); } |
| |
| template <typename Adaptor> |
| 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(rule); |
| } |
| |
| template <uint64_t Tag> |
| auto route(std::string&& rule) -> typename std::result_of< |
| decltype (&Router::new_rule_tagged<Tag>)(Router, std::string&&)>::type { |
| return router_.new_rule_tagged<Tag>(std::move(rule)); |
| } |
| |
| self_t& socket(int existing_socket) { |
| socket_ = existing_socket; |
| return *this; |
| } |
| |
| self_t& port(std::uint16_t port) { |
| port_ = port; |
| return *this; |
| } |
| |
| self_t& bindaddr(std::string bindaddr) { |
| bindaddr_ = bindaddr; |
| return *this; |
| } |
| |
| void validate() { router_.validate(); } |
| |
| void run() { |
| validate(); |
| #ifdef CROW_ENABLE_SSL |
| if (use_ssl_) { |
| if (-1 == socket_) { |
| ssl_server_ = std::move(std::make_unique<ssl_server_t>( |
| this, bindaddr_, port_, &middlewares_, &ssl_context_, io_)); |
| } else { |
| ssl_server_ = std::move(std::make_unique<ssl_server_t>( |
| this, socket_, &middlewares_, &ssl_context_, io_)); |
| } |
| ssl_server_->set_tick_function(tick_interval_, tick_function_); |
| ssl_server_->run(); |
| } else |
| #endif |
| { |
| if (-1 == socket_) { |
| server_ = std::move(std::make_unique<server_t>( |
| this, bindaddr_, port_, &middlewares_, nullptr, io_)); |
| } else { |
| server_ = std::move(std::make_unique<server_t>( |
| this, socket_, &middlewares_, nullptr, io_)); |
| } |
| server_->set_tick_function(tick_interval_, tick_function_); |
| server_->run(); |
| } |
| } |
| |
| void stop() { io_->stop(); } |
| |
| void debug_print() { |
| CROW_LOG_DEBUG << "Routing:"; |
| router_.debug_print(); |
| } |
| |
| std::vector<const std::string*> get_routes() { |
| // TODO(ed) Should this be /? |
| const std::string root(""); |
| return router_.get_routes(root); |
| } |
| std::vector<const std::string*> get_routes(const std::string& parent) { |
| return router_.get_routes(parent); |
| } |
| |
| #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 <typename T, typename... Remain> |
| 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<T, void>::value, |
| "Define CROW_ENABLE_SSL to enable ssl support."); |
| return *this; |
| } |
| |
| template <typename T> |
| 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<T, void>::value, |
| "Define CROW_ENABLE_SSL to enable ssl support."); |
| return *this; |
| } |
| #endif |
| |
| // middleware |
| using context_t = detail::context<Middlewares...>; |
| template <typename T> |
| typename T::context& get_context(const request& req) { |
| static_assert(black_magic::contains<T, Middlewares...>::value, |
| "App doesn't have the specified middleware type."); |
| auto& ctx = *reinterpret_cast<context_t*>(req.middleware_context); |
| return ctx.template get<T>(); |
| } |
| |
| template <typename T> |
| T& get_middleware() { |
| return utility::get_element_by_type<T, Middlewares...>(middlewares_); |
| } |
| |
| template <typename Duration, typename Func> |
| self_t& tick(Duration d, Func f) { |
| tick_interval_ = std::chrono::duration_cast<std::chrono::milliseconds>(d); |
| tick_function_ = f; |
| return *this; |
| } |
| |
| private: |
| std::shared_ptr<asio::io_service> io_; |
| uint16_t port_ = 80; |
| std::string bindaddr_ = "::"; |
| int socket_ = -1; |
| Router router_; |
| |
| std::chrono::milliseconds tick_interval_{}; |
| std::function<void()> tick_function_; |
| |
| std::tuple<Middlewares...> middlewares_; |
| |
| #ifdef CROW_ENABLE_SSL |
| std::unique_ptr<ssl_server_t> ssl_server_; |
| #endif |
| std::unique_ptr<server_t> server_; |
| }; |
| template <typename... Middlewares> |
| using App = Crow<Middlewares...>; |
| using SimpleApp = Crow<>; |
| } // namespace crow |