| // 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 |