1 #pragma once 2 3 #include "http_connection.hpp" 4 #include "logging.hpp" 5 6 #include <boost/asio/ip/address.hpp> 7 #include <boost/asio/ip/tcp.hpp> 8 #include <boost/asio/signal_set.hpp> 9 #include <boost/asio/ssl/context.hpp> 10 #include <boost/asio/steady_timer.hpp> 11 #include <boost/beast/ssl/ssl_stream.hpp> 12 #include <boost/date_time/posix_time/posix_time.hpp> 13 #include <ssl_key_handler.hpp> 14 15 #include <atomic> 16 #include <chrono> 17 #include <cstdint> 18 #include <filesystem> 19 #include <future> 20 #include <memory> 21 #include <utility> 22 #include <vector> 23 24 namespace crow 25 { 26 27 template <typename Handler, typename Adaptor = boost::asio::ip::tcp::socket> 28 class Server 29 { 30 public: 31 Server(Handler* handlerIn, 32 std::unique_ptr<boost::asio::ip::tcp::acceptor>&& acceptorIn, 33 std::shared_ptr<boost::asio::ssl::context> adaptorCtx, 34 std::shared_ptr<boost::asio::io_context> io = 35 std::make_shared<boost::asio::io_context>()) : 36 ioService(std::move(io)), 37 acceptor(std::move(acceptorIn)), 38 signals(*ioService, SIGINT, SIGTERM, SIGHUP), handler(handlerIn), 39 adaptorCtx(std::move(adaptorCtx)) 40 {} 41 42 Server(Handler* handlerIn, const std::string& bindaddr, uint16_t port, 43 const std::shared_ptr<boost::asio::ssl::context>& adaptorCtx, 44 const std::shared_ptr<boost::asio::io_context>& io = 45 std::make_shared<boost::asio::io_context>()) : 46 Server(handlerIn, 47 std::make_unique<boost::asio::ip::tcp::acceptor>( 48 *io, boost::asio::ip::tcp::endpoint( 49 boost::asio::ip::make_address(bindaddr), port)), 50 adaptorCtx, io) 51 {} 52 53 Server(Handler* handlerIn, int existingSocket, 54 const std::shared_ptr<boost::asio::ssl::context>& adaptorCtx, 55 const std::shared_ptr<boost::asio::io_context>& io = 56 std::make_shared<boost::asio::io_context>()) : 57 Server(handlerIn, 58 std::make_unique<boost::asio::ip::tcp::acceptor>( 59 *io, boost::asio::ip::tcp::v6(), existingSocket), 60 adaptorCtx, io) 61 {} 62 63 void updateDateStr() 64 { 65 time_t lastTimeT = time(nullptr); 66 tm myTm{}; 67 68 gmtime_r(&lastTimeT, &myTm); 69 70 dateStr.resize(100); 71 size_t dateStrSz = 72 strftime(&dateStr[0], 99, "%a, %d %b %Y %H:%M:%S GMT", &myTm); 73 dateStr.resize(dateStrSz); 74 } 75 76 void run() 77 { 78 loadCertificate(); 79 updateDateStr(); 80 81 getCachedDateStr = [this]() -> std::string { 82 static std::chrono::time_point<std::chrono::steady_clock> 83 lastDateUpdate = std::chrono::steady_clock::now(); 84 if (std::chrono::steady_clock::now() - lastDateUpdate >= 85 std::chrono::seconds(10)) 86 { 87 lastDateUpdate = std::chrono::steady_clock::now(); 88 updateDateStr(); 89 } 90 return this->dateStr; 91 }; 92 93 BMCWEB_LOG_INFO << "bmcweb server is running, local endpoint " 94 << acceptor->local_endpoint(); 95 startAsyncWaitForSignal(); 96 doAccept(); 97 } 98 99 void loadCertificate() 100 { 101 #ifdef BMCWEB_ENABLE_SSL 102 namespace fs = std::filesystem; 103 // Cleanup older certificate file existing in the system 104 fs::path oldCert = "/home/root/server.pem"; 105 if (fs::exists(oldCert)) 106 { 107 fs::remove("/home/root/server.pem"); 108 } 109 fs::path certPath = "/etc/ssl/certs/https/"; 110 // if path does not exist create the path so that 111 // self signed certificate can be created in the 112 // path 113 if (!fs::exists(certPath)) 114 { 115 fs::create_directories(certPath); 116 } 117 fs::path certFile = certPath / "server.pem"; 118 BMCWEB_LOG_INFO << "Building SSL Context file=" << certFile; 119 std::string sslPemFile(certFile); 120 ensuressl::ensureOpensslKeyPresentAndValid(sslPemFile); 121 std::shared_ptr<boost::asio::ssl::context> sslContext = 122 ensuressl::getSslContext(sslPemFile); 123 adaptorCtx = sslContext; 124 handler->ssl(std::move(sslContext)); 125 #endif 126 } 127 128 void startAsyncWaitForSignal() 129 { 130 signals.async_wait([this](const boost::system::error_code& ec, 131 int signalNo) { 132 if (ec) 133 { 134 BMCWEB_LOG_INFO << "Error in signal handler" << ec.message(); 135 } 136 else 137 { 138 if (signalNo == SIGHUP) 139 { 140 BMCWEB_LOG_INFO << "Receivied reload signal"; 141 loadCertificate(); 142 boost::system::error_code ec2; 143 acceptor->cancel(ec2); 144 if (ec2) 145 { 146 BMCWEB_LOG_ERROR 147 << "Error while canceling async operations:" 148 << ec2.message(); 149 } 150 this->startAsyncWaitForSignal(); 151 } 152 else 153 { 154 stop(); 155 } 156 } 157 }); 158 } 159 160 void stop() 161 { 162 ioService->stop(); 163 } 164 165 void doAccept() 166 { 167 boost::asio::steady_timer timer(*ioService); 168 std::shared_ptr<Connection<Adaptor, Handler>> connection; 169 if constexpr (std::is_same<Adaptor, 170 boost::beast::ssl_stream< 171 boost::asio::ip::tcp::socket>>::value) 172 { 173 connection = std::make_shared<Connection<Adaptor, Handler>>( 174 handler, std::move(timer), getCachedDateStr, 175 Adaptor(*ioService, *adaptorCtx)); 176 } 177 else 178 { 179 connection = std::make_shared<Connection<Adaptor, Handler>>( 180 handler, std::move(timer), getCachedDateStr, 181 Adaptor(*ioService)); 182 } 183 acceptor->async_accept( 184 boost::beast::get_lowest_layer(connection->socket()), 185 [this, connection](boost::system::error_code ec) { 186 if (!ec) 187 { 188 boost::asio::post(*this->ioService, 189 [connection] { connection->start(); }); 190 } 191 doAccept(); 192 }); 193 } 194 195 private: 196 std::shared_ptr<boost::asio::io_context> ioService; 197 std::function<std::string()> getCachedDateStr; 198 std::unique_ptr<boost::asio::ip::tcp::acceptor> acceptor; 199 boost::asio::signal_set signals; 200 201 std::string dateStr; 202 203 Handler* handler; 204 205 #ifdef BMCWEB_ENABLE_SSL 206 bool useSsl{false}; 207 #endif 208 std::shared_ptr<boost::asio::ssl::context> adaptorCtx; 209 }; 210 } // namespace crow 211