#pragma once #include "privileges.hpp" #include #include #include #include #include #include #include #include "http_request.h" #include "http_server.h" #include "logging.h" #include "middleware_context.h" #include "routing.h" #include "utility.h" #define BMCWEB_ROUTE(app, url) \ app.template route(url) namespace crow { #ifdef BMCWEB_ENABLE_SSL using ssl_context_t = boost::asio::ssl::context; #endif template class Crow { public: using self_t = Crow; #ifdef BMCWEB_ENABLE_SSL using ssl_socket_t = boost::beast::ssl_stream; using ssl_server_t = Server; #else using socket_t = boost::asio::ip::tcp::socket; using server_t = Server; #endif explicit Crow(std::shared_ptr ioIn = std::make_shared()) : io(std::move(ioIn)) { } ~Crow() { this->stop(); } template void handleUpgrade(const Request& req, Response& res, Adaptor&& adaptor) { router.handleUpgrade(req, res, std::move(adaptor)); } void handle(const Request& req, Response& res) { router.handle(req, res); } DynamicRule& routeDynamic(std::string&& rule) { return router.newRuleDynamic(rule); } template auto& route(std::string&& rule) { return router.newRuleTagged(std::move(rule)); } self_t& socket(int existing_socket) { socketFd = existing_socket; return *this; } self_t& port(std::uint16_t port) { portUint = port; return *this; } self_t& bindaddr(std::string bindaddr) { bindaddrStr = bindaddr; return *this; } void validate() { router.validate(); } void run() { validate(); #ifdef BMCWEB_ENABLE_SSL if (-1 == socketFd) { sslServer = std::move(std::make_unique( this, bindaddrStr, portUint, sslContext, &middlewares, io)); } else { sslServer = std::move(std::make_unique( this, socketFd, sslContext, &middlewares, io)); } sslServer->setTickFunction(tickInterval, tickFunction); sslServer->run(); #else if (-1 == socketFd) { server = std::move(std::make_unique( this, bindaddrStr, portUint, nullptr, &middlewares, io)); } else { server = std::move(std::make_unique( this, socketFd, nullptr, &middlewares, io)); } server->setTickFunction(tickInterval, tickFunction); server->run(); #endif } void stop() { io->stop(); } void debugPrint() { BMCWEB_LOG_DEBUG << "Routing:"; router.debugPrint(); } std::vector getRoutes() { // TODO(ed) Should this be /? const std::string root(""); return router.getRoutes(root); } std::vector getRoutes(const std::string& parent) { return router.getRoutes(parent); } #ifdef BMCWEB_ENABLE_SSL self_t& sslFile(const std::string& crt_filename, const std::string& key_filename) { sslContext = std::make_shared( boost::asio::ssl::context::tls_server); sslContext->set_verify_mode(boost::asio::ssl::verify_peer); sslContext->use_certificate_file(crt_filename, ssl_context_t::pem); sslContext->use_private_key_file(key_filename, ssl_context_t::pem); sslContext->set_options(boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::no_sslv3 | boost::asio::ssl::context::no_tlsv1 | boost::asio::ssl::context::no_tlsv1_1); return *this; } self_t& sslFile(const std::string& pem_filename) { sslContext = std::make_shared( boost::asio::ssl::context::tls_server); sslContext->set_verify_mode(boost::asio::ssl::verify_peer); sslContext->load_verify_file(pem_filename); sslContext->set_options(boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::no_sslv3 | boost::asio::ssl::context::no_tlsv1 | boost::asio::ssl::context::no_tlsv1_1); return *this; } self_t& ssl(std::shared_ptr&& ctx) { sslContext = std::move(ctx); BMCWEB_LOG_INFO << "app::ssl context use_count=" << sslContext.use_count(); return *this; } std::shared_ptr sslContext = nullptr; #else template self_t& ssl_file(T&&, Remain&&...) { // We can't call .ssl() member function unless BMCWEB_ENABLE_SSL is // defined. static_assert( // make static_assert dependent to T; always false std::is_base_of::value, "Define BMCWEB_ENABLE_SSL to enable ssl support."); return *this; } template self_t& ssl(T&&) { // We can't call .ssl() member function unless BMCWEB_ENABLE_SSL is // defined. static_assert( // make static_assert dependent to T; always false std::is_base_of::value, "Define BMCWEB_ENABLE_SSL to enable ssl support."); return *this; } #endif // middleware using context_t = detail::Context; template typename T::Context& getContext(const Request& req) { static_assert(black_magic::Contains::value, "App doesn't have the specified middleware type."); auto& ctx = *reinterpret_cast(req.middlewareContext); return ctx.template get(); } template T& getMiddleware() { return utility::getElementByType(middlewares); } template self_t& tick(Duration d, Func f) { tickInterval = std::chrono::duration_cast(d); tickFunction = f; return *this; } private: std::shared_ptr io; #ifdef BMCWEB_ENABLE_SSL uint16_t portUint = 443; #else uint16_t portUint = 80; #endif std::string bindaddrStr = "::"; int socketFd = -1; Router router; std::chrono::milliseconds tickInterval{}; std::function tickFunction; std::tuple middlewares; #ifdef BMCWEB_ENABLE_SSL std::unique_ptr sslServer; #else std::unique_ptr server; #endif }; template using App = Crow; using SimpleApp = Crow<>; } // namespace crow