blob: 86bf54b427fddef18ef3e9dd8867515305429f3f [file] [log] [blame]
#pragma once
#include <atomic>
#include <chrono>
#include <cstdint>
#include <future>
#include <memory>
#include <utility>
#include <vector>
#include "crow/timer_queue.h"
#include "crow/http_connection.h"
#include "crow/logging.h"
#include <boost/asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#ifdef CROW_ENABLE_SSL
#include <boost/asio/ssl.hpp>
#endif
namespace crow {
using namespace boost;
using tcp = asio::ip::tcp;
template <typename Handler, typename Adaptor = SocketAdaptor,
typename... Middlewares>
class Server {
public:
Server(Handler* handler, std::unique_ptr<tcp::acceptor>&& acceptor,
std::tuple<Middlewares...>* middlewares = nullptr,
typename Adaptor::context* adaptor_ctx = nullptr,
std::shared_ptr<boost::asio::io_service> io =
std::make_shared<boost::asio::io_service>())
: io_service_(std::move(io)),
acceptor_(std::move(acceptor)),
signals_(*io_service_, SIGINT, SIGTERM),
tick_timer_(*io_service_),
handler_(handler),
middlewares_(middlewares),
adaptor_ctx_(adaptor_ctx) {}
Server(Handler* handler, const std::string& bindaddr, uint16_t port,
std::tuple<Middlewares...>* middlewares = nullptr,
typename Adaptor::context* adaptor_ctx = nullptr,
std::shared_ptr<boost::asio::io_service> io =
std::make_shared<boost::asio::io_service>())
: 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,
typename Adaptor::context* adaptor_ctx = nullptr,
std::shared_ptr<boost::asio::io_service> io =
std::make_shared<boost::asio::io_service>())
: Server(handler,
std::make_unique<tcp::acceptor>(*io, boost::asio::ip::tcp::v6(),
existing_socket),
middlewares, adaptor_ctx, io) {}
void set_tick_function(std::chrono::milliseconds d, std::function<void()> f) {
tick_interval_ = d;
tick_function_ = f;
}
void on_tick() {
tick_function_();
tick_timer_.expires_from_now(
boost::posix_time::milliseconds(tick_interval_.count()));
tick_timer_.async_wait([this](const boost::system::error_code& ec) {
if (ec) {
return;
}
on_tick();
});
}
void update_date_str() {
auto last_time_t = time(0);
tm my_tm{};
#ifdef _MSC_VER
gmtime_s(&my_tm, &last_time_t);
#else
gmtime_r(&last_time_t, &my_tm);
#endif
date_str.resize(100);
size_t date_str_sz =
strftime(&date_str[0], 99, "%a, %d %b %Y %H:%M:%S GMT", &my_tm);
date_str.resize(date_str_sz);
};
void run() {
update_date_str();
get_cached_date_str_ = [this]() -> std::string {
static std::chrono::time_point<std::chrono::steady_clock>
last_date_update = std::chrono::steady_clock::now();
if (std::chrono::steady_clock::now() - last_date_update >=
std::chrono::seconds(10)) {
last_date_update = std::chrono::steady_clock::now();
update_date_str();
}
return this->date_str;
};
boost::asio::deadline_timer timer(*io_service_);
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;
}
timer_queue_.process();
timer.expires_from_now(boost::posix_time::seconds(1));
timer.async_wait(handler);
};
timer.async_wait(handler);
if (tick_function_ && tick_interval_.count() > 0) {
tick_timer_.expires_from_now(
boost::posix_time::milliseconds(tick_interval_.count()));
tick_timer_.async_wait([this](const boost::system::error_code& ec) {
if (ec) {
return;
}
on_tick();
});
}
CROW_LOG_INFO << server_name_ << " server is running, local endpoint "
<< acceptor_->local_endpoint();
signals_.async_wait([&](const boost::system::error_code& /*error*/,
int /*signal_number*/) { stop(); });
do_accept();
}
void stop() { io_service_->stop(); }
void do_accept() {
auto p = new Connection<Adaptor, Handler, Middlewares...>(
*io_service_, handler_, server_name_, middlewares_,
get_cached_date_str_, timer_queue_, adaptor_ctx_);
acceptor_->async_accept(p->socket(),
[this, p](boost::system::error_code ec) {
if (!ec) {
this->io_service_->post([p] { p->start(); });
} else {
delete p;
}
do_accept();
});
}
private:
std::shared_ptr<asio::io_service> io_service_;
detail::timer_queue timer_queue_;
std::function<std::string()> get_cached_date_str_;
std::unique_ptr<tcp::acceptor> acceptor_;
boost::asio::signal_set signals_;
boost::asio::deadline_timer tick_timer_;
std::string date_str;
Handler* handler_;
std::string server_name_ = "iBMC";
std::chrono::milliseconds tick_interval_{};
std::function<void()> tick_function_;
std::tuple<Middlewares...>* middlewares_;
#ifdef CROW_ENABLE_SSL
bool use_ssl_{false};
boost::asio::ssl::context ssl_context_{boost::asio::ssl::context::sslv23};
#endif
typename Adaptor::context* adaptor_ctx_;
}; // namespace crow
} // namespace crow