| #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_ |