blob: fd01340853ecf5abdb8abfea7bbaf106bd0197ca [file] [log] [blame]
#pragma once
#include "privileges.hpp"
#include <chrono>
#include <cstdint>
#include <functional>
#include <future>
#include <memory>
#include <string>
#include <utility>
#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<crow::black_magic::get_parameter_tag(url)>(url)
namespace crow
{
#ifdef BMCWEB_ENABLE_SSL
using ssl_context_t = boost::asio::ssl::context;
#endif
template <typename... Middlewares> class Crow
{
public:
using self_t = Crow;
#ifdef BMCWEB_ENABLE_SSL
using ssl_socket_t = boost::beast::ssl_stream<boost::asio::ip::tcp::socket>;
using ssl_server_t = Server<Crow, ssl_socket_t, Middlewares...>;
#else
using socket_t = boost::asio::ip::tcp::socket;
using server_t = Server<Crow, socket_t, Middlewares...>;
#endif
explicit Crow(std::shared_ptr<boost::asio::io_context> ioIn =
std::make_shared<boost::asio::io_context>()) :
io(std::move(ioIn))
{
}
~Crow()
{
this->stop();
}
template <typename Adaptor>
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 <uint64_t Tag> auto& route(std::string&& rule)
{
return router.newRuleTagged<Tag>(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<ssl_server_t>(
this, bindaddrStr, portUint, sslContext, &middlewares, io));
}
else
{
sslServer = std::move(std::make_unique<ssl_server_t>(
this, socketFd, sslContext, &middlewares, io));
}
sslServer->setTickFunction(tickInterval, tickFunction);
sslServer->run();
#else
if (-1 == socketFd)
{
server = std::move(std::make_unique<server_t>(
this, bindaddrStr, portUint, nullptr, &middlewares, io));
}
else
{
server = std::move(std::make_unique<server_t>(
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<const std::string*> getRoutes()
{
// TODO(ed) Should this be /?
const std::string root("");
return router.getRoutes(root);
}
std::vector<const std::string*> 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<ssl_context_t>(
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<ssl_context_t>(
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<boost::asio::ssl::context>&& ctx)
{
sslContext = std::move(ctx);
BMCWEB_LOG_INFO << "app::ssl context use_count="
<< sslContext.use_count();
return *this;
}
std::shared_ptr<ssl_context_t> sslContext = nullptr;
#else
template <typename T, typename... Remain> 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<T, void>::value,
"Define BMCWEB_ENABLE_SSL to enable ssl support.");
return *this;
}
template <typename T> 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<T, void>::value,
"Define BMCWEB_ENABLE_SSL to enable ssl support.");
return *this;
}
#endif
// middleware
using context_t = detail::Context<Middlewares...>;
template <typename T> typename T::Context& getContext(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.middlewareContext);
return ctx.template get<T>();
}
template <typename T> T& getMiddleware()
{
return utility::getElementByType<T, Middlewares...>(middlewares);
}
template <typename Duration, typename Func> self_t& tick(Duration d, Func f)
{
tickInterval = std::chrono::duration_cast<std::chrono::milliseconds>(d);
tickFunction = f;
return *this;
}
private:
std::shared_ptr<asio::io_context> 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<void()> tickFunction;
std::tuple<Middlewares...> middlewares;
#ifdef BMCWEB_ENABLE_SSL
std::unique_ptr<ssl_server_t> sslServer;
#else
std::unique_ptr<server_t> server;
#endif
};
template <typename... Middlewares> using App = Crow<Middlewares...>;
using SimpleApp = Crow<>;
} // namespace crow