blob: e8cb762e126bdf145c1cc52f9de4cefbc83792f2 [file] [log] [blame]
#pragma once
#include <atomic>
#include <boost/asio/deadline_timer.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/signal_set.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <chrono>
#include <cstdint>
#include <future>
#include <memory>
#include <utility>
#include <vector>
#include "crow/http_connection.h"
#include "crow/logging.h"
#include "crow/timer_queue.h"
#ifdef BMCWEB_ENABLE_SSL
#include <boost/asio/ssl/context.hpp>
#include <boost/beast/experimental/core/ssl_stream.hpp>
#endif
namespace crow
{
using namespace boost;
using tcp = asio::ip::tcp;
template <typename Handler, typename Adaptor = boost::asio::ip::tcp::socket,
typename... Middlewares>
class Server
{
public:
Server(Handler* handler, std::unique_ptr<tcp::acceptor>&& acceptor,
std::tuple<Middlewares...>* middlewares = nullptr,
boost::asio::ssl::context* adaptor_ctx = nullptr,
std::shared_ptr<boost::asio::io_context> io =
std::make_shared<boost::asio::io_context>()) :
ioService(std::move(io)),
acceptor(std::move(acceptor)), signals(*ioService, SIGINT, SIGTERM),
tickTimer(*ioService), handler(handler), middlewares(middlewares),
adaptorCtx(adaptor_ctx)
{
}
Server(Handler* handler, const std::string& bindaddr, uint16_t port,
std::tuple<Middlewares...>* middlewares = nullptr,
boost::asio::ssl::context* adaptor_ctx = nullptr,
std::shared_ptr<boost::asio::io_context> io =
std::make_shared<boost::asio::io_context>()) :
Server(handler,
std::make_unique<tcp::acceptor>(
*io,
tcp::endpoint(
boost::asio::ip::address::from_string(bindaddr), port)),
middlewares, adaptor_ctx, io)
{
}
Server(Handler* handler, int existing_socket,
std::tuple<Middlewares...>* middlewares = nullptr,
boost::asio::ssl::context* adaptor_ctx = nullptr,
std::shared_ptr<boost::asio::io_context> io =
std::make_shared<boost::asio::io_context>()) :
Server(handler,
std::make_unique<tcp::acceptor>(*io, boost::asio::ip::tcp::v6(),
existing_socket),
middlewares, adaptor_ctx, io)
{
}
void setTickFunction(std::chrono::milliseconds d, std::function<void()> f)
{
tickInterval = d;
tickFunction = f;
}
void onTick()
{
tickFunction();
tickTimer.expires_from_now(
boost::posix_time::milliseconds(tickInterval.count()));
tickTimer.async_wait([this](const boost::system::error_code& ec) {
if (ec)
{
return;
}
onTick();
});
}
void updateDateStr()
{
auto lastTimeT = time(0);
tm myTm{};
#ifdef _MSC_VER
gmtime_s(&my_tm, &last_time_t);
#else
gmtime_r(&lastTimeT, &myTm);
#endif
dateStr.resize(100);
size_t dateStrSz =
strftime(&dateStr[0], 99, "%a, %d %b %Y %H:%M:%S GMT", &myTm);
dateStr.resize(dateStrSz);
};
void run()
{
updateDateStr();
getCachedDateStr = [this]() -> std::string {
static std::chrono::time_point<std::chrono::steady_clock>
lastDateUpdate = std::chrono::steady_clock::now();
if (std::chrono::steady_clock::now() - lastDateUpdate >=
std::chrono::seconds(10))
{
lastDateUpdate = std::chrono::steady_clock::now();
updateDateStr();
}
return this->dateStr;
};
boost::asio::deadline_timer timer(*ioService);
timer.expires_from_now(boost::posix_time::seconds(1));
std::function<void(const boost::system::error_code& ec)> handler;
handler = [&](const boost::system::error_code& ec) {
if (ec)
{
return;
}
timerQueue.process();
timer.expires_from_now(boost::posix_time::seconds(1));
timer.async_wait(handler);
};
timer.async_wait(handler);
if (tickFunction && tickInterval.count() > 0)
{
tickTimer.expires_from_now(
boost::posix_time::milliseconds(tickInterval.count()));
tickTimer.async_wait([this](const boost::system::error_code& ec) {
if (ec)
{
return;
}
onTick();
});
}
BMCWEB_LOG_INFO << serverName << " server is running, local endpoint "
<< acceptor->local_endpoint();
signals.async_wait([&](const boost::system::error_code& /*error*/,
int /*signal_number*/) { stop(); });
doAccept();
}
void stop()
{
ioService->stop();
}
void doAccept()
{
std::optional<Adaptor> adaptorTemp;
if constexpr (std::is_same<Adaptor,
boost::beast::ssl_stream<
boost::asio::ip::tcp::socket>>::value)
{
adaptorTemp = Adaptor(*ioService, *adaptorCtx);
}
else
{
adaptorTemp = Adaptor(*ioService);
}
Connection<Adaptor, Handler, Middlewares...>* p =
new Connection<Adaptor, Handler, Middlewares...>(
*ioService, handler, serverName, middlewares, getCachedDateStr,
timerQueue, std::move(adaptorTemp.value()));
acceptor->async_accept(p->socket().lowest_layer(),
[this, p](boost::system::error_code ec) {
if (!ec)
{
this->ioService->post(
[p] { p->start(); });
}
else
{
delete p;
}
doAccept();
});
}
private:
std::shared_ptr<asio::io_context> ioService;
detail::TimerQueue timerQueue;
std::function<std::string()> getCachedDateStr;
std::unique_ptr<tcp::acceptor> acceptor;
boost::asio::signal_set signals;
boost::asio::deadline_timer tickTimer;
std::string dateStr;
Handler* handler;
std::string serverName = "iBMC";
std::chrono::milliseconds tickInterval{};
std::function<void()> tickFunction;
std::tuple<Middlewares...>* middlewares;
#ifdef BMCWEB_ENABLE_SSL
bool useSsl{false};
#endif
boost::asio::ssl::context* adaptorCtx;
}; // namespace crow
} // namespace crow