blob: b2e8cc062e209ad2def69be5f7fbb234c2a6f2d5 [file] [log] [blame]
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: Copyright OpenBMC Authors
#pragma once
#include "bmcweb_config.h"
#include "http_connect_types.hpp"
#include "http_connection.hpp"
#include "io_context_singleton.hpp"
#include "logging.hpp"
#include "ssl_key_handler.hpp"
#include <boost/asio/ip/address.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/signal_set.hpp>
#include <boost/asio/ssl/context.hpp>
#include <boost/asio/ssl/stream.hpp>
#include <boost/asio/steady_timer.hpp>
#include <chrono>
#include <csignal>
#include <cstddef>
#include <ctime>
#include <functional>
#include <memory>
#include <string>
#include <utility>
#include <vector>
namespace crow
{
struct Acceptor
{
boost::asio::ip::tcp::acceptor acceptor;
HttpType httpType;
};
template <typename Handler, typename Adaptor = boost::asio::ip::tcp::socket>
class Server
{
using self_t = Server<Handler, Adaptor>;
public:
Server(Handler* handlerIn, std::vector<Acceptor>&& acceptorsIn) :
acceptors(std::move(acceptorsIn)),
// NOLINTNEXTLINE(misc-include-cleaner)
signals(getIoContext(), SIGINT, SIGTERM, SIGHUP), handler(handlerIn)
{}
void updateDateStr()
{
time_t lastTimeT = time(nullptr);
tm myTm{};
gmtime_r(&lastTimeT, &myTm);
dateStr.resize(100);
size_t dateStrSz = strftime(dateStr.data(), dateStr.size() - 1,
"%a, %d %b %Y %H:%M:%S GMT", &myTm);
dateStr.resize(dateStrSz);
}
void run()
{
loadCertificate();
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 dateStr;
};
for (const Acceptor& accept : acceptors)
{
BMCWEB_LOG_INFO(
"bmcweb server is running, local endpoint {}",
accept.acceptor.local_endpoint().address().to_string());
}
startAsyncWaitForSignal();
doAccept();
}
void loadCertificate()
{
if constexpr (BMCWEB_INSECURE_DISABLE_SSL)
{
return;
}
adaptorCtx = ensuressl::getSslServerContext();
}
void startAsyncWaitForSignal()
{
signals.async_wait(
[this](const boost::system::error_code& ec, int signalNo) {
if (ec)
{
BMCWEB_LOG_INFO("Error in signal handler{}", ec.message());
}
else
{
if (signalNo == SIGHUP)
{
BMCWEB_LOG_INFO("Receivied reload signal");
loadCertificate();
startAsyncWaitForSignal();
}
else
{
getIoContext().stop();
}
}
});
}
using SocketPtr = std::unique_ptr<Adaptor>;
void afterAccept(SocketPtr socket, HttpType httpType,
const boost::system::error_code& ec)
{
if (ec)
{
BMCWEB_LOG_ERROR("Failed to accept socket {}", ec);
return;
}
boost::asio::steady_timer timer(getIoContext());
if (adaptorCtx == nullptr)
{
adaptorCtx = std::make_shared<boost::asio::ssl::context>(
boost::asio::ssl::context::tls_server);
}
boost::asio::ssl::stream<Adaptor> stream(std::move(*socket),
*adaptorCtx);
using ConnectionType = Connection<Adaptor, Handler>;
auto connection = std::make_shared<ConnectionType>(
handler, httpType, std::move(timer), getCachedDateStr,
std::move(stream));
boost::asio::post(getIoContext(),
[connection] { connection->start(); });
doAccept();
}
void doAccept()
{
SocketPtr socket = std::make_unique<Adaptor>(getIoContext());
// Keep a raw pointer so when the socket is moved, the pointer is still
// valid
Adaptor* socketPtr = socket.get();
for (Acceptor& accept : acceptors)
{
accept.acceptor.async_accept(
*socketPtr,
std::bind_front(&self_t::afterAccept, this, std::move(socket),
accept.httpType));
}
}
private:
std::function<std::string()> getCachedDateStr;
std::vector<Acceptor> acceptors;
boost::asio::signal_set signals;
std::string dateStr;
Handler* handler;
std::shared_ptr<boost::asio::ssl::context> adaptorCtx;
};
} // namespace crow