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