1 #pragma once 2 3 #include "http_connection.hpp" 4 #include "logging.hpp" 5 #include "ssl_key_handler.hpp" 6 7 #include <boost/asio/ip/address.hpp> 8 #include <boost/asio/ip/tcp.hpp> 9 #include <boost/asio/signal_set.hpp> 10 #include <boost/asio/ssl/context.hpp> 11 #include <boost/asio/ssl/stream.hpp> 12 #include <boost/asio/steady_timer.hpp> 13 #include <boost/beast/core/stream_traits.hpp> 14 15 #include <atomic> 16 #include <chrono> 17 #include <cstdint> 18 #include <filesystem> 19 #include <future> 20 #include <memory> 21 #include <string> 22 #include <utility> 23 #include <vector> 24 25 namespace crow 26 { 27 28 template <typename Handler, typename Adaptor = boost::asio::ip::tcp::socket> 29 class Server 30 { 31 using self_t = Server<Handler, Adaptor>; 32 33 public: 34 Server(Handler* handlerIn, boost::asio::ip::tcp::acceptor&& acceptorIn, 35 std::shared_ptr<boost::asio::ssl::context> adaptorCtxIn, 36 std::shared_ptr<boost::asio::io_context> io) : 37 ioService(std::move(io)), 38 acceptor(std::move(acceptorIn)), 39 signals(*ioService, SIGINT, SIGTERM, SIGHUP), handler(handlerIn), 40 adaptorCtx(std::move(adaptorCtxIn)) 41 {} 42 43 void updateDateStr() 44 { 45 time_t lastTimeT = time(nullptr); 46 tm myTm{}; 47 48 gmtime_r(&lastTimeT, &myTm); 49 50 dateStr.resize(100); 51 size_t dateStrSz = strftime(dateStr.data(), dateStr.size() - 1, 52 "%a, %d %b %Y %H:%M:%S GMT", &myTm); 53 dateStr.resize(dateStrSz); 54 } 55 56 void run() 57 { 58 loadCertificate(); 59 updateDateStr(); 60 61 getCachedDateStr = [this]() -> std::string { 62 static std::chrono::time_point<std::chrono::steady_clock> 63 lastDateUpdate = std::chrono::steady_clock::now(); 64 if (std::chrono::steady_clock::now() - lastDateUpdate >= 65 std::chrono::seconds(10)) 66 { 67 lastDateUpdate = std::chrono::steady_clock::now(); 68 updateDateStr(); 69 } 70 return dateStr; 71 }; 72 73 BMCWEB_LOG_INFO("bmcweb server is running, local endpoint {}", 74 acceptor.local_endpoint().address().to_string()); 75 startAsyncWaitForSignal(); 76 doAccept(); 77 } 78 79 void loadCertificate() 80 { 81 if constexpr (BMCWEB_INSECURE_DISABLE_SSL) 82 { 83 return; 84 } 85 86 auto sslContext = ensuressl::getSslServerContext(); 87 88 adaptorCtx = sslContext; 89 handler->ssl(std::move(sslContext)); 90 } 91 92 void startAsyncWaitForSignal() 93 { 94 signals.async_wait( 95 [this](const boost::system::error_code& ec, int signalNo) { 96 if (ec) 97 { 98 BMCWEB_LOG_INFO("Error in signal handler{}", ec.message()); 99 } 100 else 101 { 102 if (signalNo == SIGHUP) 103 { 104 BMCWEB_LOG_INFO("Receivied reload signal"); 105 loadCertificate(); 106 startAsyncWaitForSignal(); 107 } 108 else 109 { 110 stop(); 111 } 112 } 113 }); 114 } 115 116 void stop() 117 { 118 ioService->stop(); 119 } 120 using Socket = boost::beast::lowest_layer_type<Adaptor>; 121 using SocketPtr = std::unique_ptr<Socket>; 122 123 void afterAccept(SocketPtr socket, const boost::system::error_code& ec) 124 { 125 if (ec) 126 { 127 BMCWEB_LOG_ERROR("Failed to accept socket {}", ec); 128 return; 129 } 130 131 boost::asio::steady_timer timer(*ioService); 132 std::shared_ptr<Connection<Adaptor, Handler>> connection; 133 134 if constexpr (std::is_same<Adaptor, 135 boost::asio::ssl::stream< 136 boost::asio::ip::tcp::socket>>::value) 137 { 138 if (adaptorCtx == nullptr) 139 { 140 BMCWEB_LOG_CRITICAL( 141 "Asked to launch TLS socket but no context available"); 142 return; 143 } 144 connection = std::make_shared<Connection<Adaptor, Handler>>( 145 handler, std::move(timer), getCachedDateStr, 146 Adaptor(std::move(*socket), *adaptorCtx)); 147 } 148 else 149 { 150 connection = std::make_shared<Connection<Adaptor, Handler>>( 151 handler, std::move(timer), getCachedDateStr, 152 Adaptor(std::move(*socket))); 153 } 154 155 boost::asio::post(*ioService, [connection] { connection->start(); }); 156 157 doAccept(); 158 } 159 160 void doAccept() 161 { 162 if (ioService == nullptr) 163 { 164 BMCWEB_LOG_CRITICAL("IoService was null"); 165 return; 166 } 167 168 SocketPtr socket = std::make_unique<Socket>(*ioService); 169 // Keep a raw pointer so when the socket is moved, the pointer is still 170 // valid 171 Socket* socketPtr = socket.get(); 172 173 acceptor.async_accept( 174 *socketPtr, 175 std::bind_front(&self_t::afterAccept, this, std::move(socket))); 176 } 177 178 private: 179 std::shared_ptr<boost::asio::io_context> ioService; 180 std::function<std::string()> getCachedDateStr; 181 boost::asio::ip::tcp::acceptor acceptor; 182 boost::asio::signal_set signals; 183 184 std::string dateStr; 185 186 Handler* handler; 187 188 std::shared_ptr<boost::asio::ssl::context> adaptorCtx; 189 }; 190 } // namespace crow 191