blob: ccdd9eead2d8001747bb32d6ca1d64ad81fad361 [file] [log] [blame]
#ifndef THIRD_PARTY_GBMCWEB_HTTP_HTTP_SERVER_H_
#define THIRD_PARTY_GBMCWEB_HTTP_HTTP_SERVER_H_
#ifdef BMCWEB_ENABLE_GRPC
#include "absl/log/log.h"
#include "bmcweb_authorizer_singleton.h"
#endif
#include <chrono> // NOLINT
#include <csignal>
#include <cstdint>
#include <ctime>
#include <functional>
#include <memory>
#include <string>
#include <type_traits>
#include <utility>
#include "boost/asio/ip/address.hpp" // NOLINT
#include "boost/asio/ip/tcp.hpp" // NOLINT
#include "boost/asio/signal_set.hpp" // NOLINT
#include "boost/asio/ssl/context.hpp" // NOLINT
#include "boost/asio/steady_timer.hpp" // NOLINT
#include "boost/beast/ssl/ssl_stream.hpp" // NOLINT
#include "http_connection.hpp"
#include "logging.hpp"
namespace crow {
template <typename Handler, typename Adaptor = boost::asio::ip::tcp::socket>
class Server {
public:
Server(Handler* handlerIn,
std::unique_ptr<boost::asio::ip::tcp::acceptor>&& acceptorIn,
std::shared_ptr<boost::asio::ssl::context> adaptorCtxIn,
std::shared_ptr<boost::asio::io_context> io =
std::make_shared<boost::asio::io_context>())
: ioService(std::move(io)),
acceptor(std::move(acceptorIn)),
signals(*ioService, SIGHUP, SIGINT),
handler(handlerIn),
adaptorCtx(std::move(adaptorCtxIn)) {}
Server(Handler* handlerIn, const std::string& bindaddr, uint16_t port,
const std::shared_ptr<boost::asio::ssl::context>& adaptorCtxIn,
const std::shared_ptr<boost::asio::io_context>& io =
std::make_shared<boost::asio::io_context>())
: Server(handlerIn,
std::make_unique<boost::asio::ip::tcp::acceptor>(
*io, boost::asio::ip::tcp::endpoint(
boost::asio::ip::make_address(bindaddr), port)),
adaptorCtxIn, io) {}
Server(Handler* handlerIn, int existingSocket,
const std::shared_ptr<boost::asio::ssl::context>& adaptorCtxIn,
const std::shared_ptr<boost::asio::io_context>& io =
std::make_shared<boost::asio::io_context>())
: Server(handlerIn,
std::make_unique<boost::asio::ip::tcp::acceptor>(
*io, boost::asio::ip::tcp::v6(), existingSocket),
adaptorCtxIn, io) {}
~Server() { stop(); }
Server(const Server&) = default;
Server& operator=(const Server&) = default;
Server(Server&&) noexcept = default;
Server& operator=(Server&&) noexcept = default;
void updateDateStr() {
time_t last_time_t = time(nullptr);
tm my_tm{};
gmtime_r(&last_time_t, &my_tm);
dateStr.resize(100);
size_t date_str_sz =
strftime(&dateStr[0], 99, "%a, %d %b %Y %H:%M:%S GMT", &my_tm);
dateStr.resize(date_str_sz);
}
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 this->dateStr;
};
BMCWEB_LOG_INFO << "bmcweb server is running, local endpoint "
<< acceptor->local_endpoint().address().to_string();
startAsyncWaitForSignal();
doAccept();
}
void loadCertificate() {
#ifdef BMCWEB_ENABLE_SSL
namespace fs = std::filesystem;
// Cleanup older certificate file existing in the system
fs::path oldCert = "/home/root/server.pem";
if (fs::exists(oldCert)) {
fs::remove("/home/root/server.pem");
}
fs::path certPath = "/etc/ssl/certs/https/";
// if path does not exist create the path so that
// self signed certificate can be created in the
// path
if (!fs::exists(certPath)) {
fs::create_directories(certPath);
}
fs::path certFile = certPath / "server.pem";
BMCWEB_LOG_INFO << "Building SSL Context file=" << certFile.string();
std::string sslPemFile(certFile);
ensuressl::ensureOpensslKeyPresentAndValid(sslPemFile);
std::shared_ptr<boost::asio::ssl::context> sslContext =
ensuressl::getSslContext(sslPemFile);
adaptorCtx = sslContext;
handler->ssl(std::move(sslContext));
#endif
}
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";
#ifdef BMCWEB_ENABLE_GRPC
constexpr const char* persistentBasePrivilegesFolder =
"/var/google/authz_policies";
constexpr const char* rofsBasePrivilegesFolder =
"/usr/share/redfish_privileges";
::milotic::authz::BmcWebAuthorizerSingleton& authorizer =
::milotic::authz::BmcWebAuthorizerSingleton::GetInstance();
// Check persistent data first
authorizer.SetBasePrivilegesFolder(persistentBasePrivilegesFolder);
// If privilege registry does not exist, check rofs
if (!authorizer.IsBasePrivilegeRegistryFound()) {
LOG(WARNING) << "Could not find Privilege Registry at "
"/var/google/authz_policies";
authorizer.SetBasePrivilegesFolder(rofsBasePrivilegesFolder);
}
// reload authz config
authorizer.ReloadConfiguration();
#endif
loadCertificate();
boost::system::error_code ec2;
// NOLINTNEXTLINE(bugprone-unused-return-value)
acceptor->cancel(ec2);
if (ec2) {
BMCWEB_LOG_ERROR << "Error while canceling async operations:"
<< ec2.message();
}
this->startAsyncWaitForSignal();
} else {
stop();
}
}
});
}
void stop() { ioService->stop(); }
void doAccept() {
boost::asio::steady_timer timer(*ioService);
std::shared_ptr<Connection<Adaptor, Handler>> connection;
if constexpr (std::is_same<Adaptor,
boost::beast::ssl_stream<
boost::asio::ip::tcp::socket>>::value) {
connection = std::make_shared<Connection<Adaptor, Handler>>(
handler, std::move(timer), getCachedDateStr,
Adaptor(*ioService, *adaptorCtx));
} else {
connection = std::make_shared<Connection<Adaptor, Handler>>(
handler, std::move(timer), getCachedDateStr, Adaptor(*ioService));
}
acceptor->async_accept(
boost::beast::get_lowest_layer(connection->socket()),
[this, connection](const boost::system::error_code& ec) {
if (!ec) {
connection->setKeepAlive();
boost::asio::post(*this->ioService,
[connection] { connection->start(); });
}
doAccept();
});
}
private:
std::shared_ptr<boost::asio::io_context> ioService; // NOLINT
std::function<std::string()> getCachedDateStr; // NOLINT
std::unique_ptr<boost::asio::ip::tcp::acceptor> acceptor; // NOLINT
boost::asio::signal_set signals; // NOLINT
std::string dateStr; // NOLINT
Handler* handler; // NOLINT
std::shared_ptr<boost::asio::ssl::context> adaptorCtx; // NOLINT
};
} // namespace crow
#endif // THIRD_PARTY_GBMCWEB_HTTP_HTTP_SERVER_H_