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