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