1 #pragma once 2 3 #include "async_resp.hpp" 4 #include "http_request.hpp" 5 #include "http_server.hpp" 6 #include "logging.hpp" 7 #include "privileges.hpp" 8 #include "routing.hpp" 9 #include "utility.hpp" 10 11 #include <systemd/sd-daemon.h> 12 13 #include <boost/asio/io_context.hpp> 14 #include <boost/asio/ip/tcp.hpp> 15 #include <boost/asio/ssl/context.hpp> 16 #include <boost/asio/ssl/stream.hpp> 17 18 #include <chrono> 19 #include <cstdint> 20 #include <functional> 21 #include <future> 22 #include <memory> 23 #include <string> 24 #include <utility> 25 26 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage, clang-diagnostic-unused-macros) 27 #define BMCWEB_ROUTE(app, url) \ 28 app.template route<crow::utility::getParameterTag(url)>(url) 29 30 namespace crow 31 { 32 class App 33 { 34 public: 35 using ssl_socket_t = boost::asio::ssl::stream<boost::asio::ip::tcp::socket>; 36 using raw_socket_t = boost::asio::ip::tcp::socket; 37 38 using socket_type = std::conditional_t<BMCWEB_INSECURE_DISABLE_SSL, 39 raw_socket_t, ssl_socket_t>; 40 using server_type = Server<App, socket_type>; 41 42 explicit App(std::shared_ptr<boost::asio::io_context> ioIn = 43 std::make_shared<boost::asio::io_context>()) : 44 io(std::move(ioIn)) 45 {} 46 ~App() 47 { 48 stop(); 49 } 50 51 App(const App&) = delete; 52 App(App&&) = delete; 53 App& operator=(const App&) = delete; 54 App& operator=(const App&&) = delete; 55 56 template <typename Adaptor> 57 void handleUpgrade(const std::shared_ptr<Request>& req, 58 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 59 Adaptor&& adaptor) 60 { 61 router.handleUpgrade(req, asyncResp, std::forward<Adaptor>(adaptor)); 62 } 63 64 void handle(const std::shared_ptr<Request>& req, 65 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 66 { 67 router.handle(req, asyncResp); 68 } 69 70 DynamicRule& routeDynamic(const std::string& rule) 71 { 72 return router.newRuleDynamic(rule); 73 } 74 75 template <uint64_t Tag> 76 auto& route(std::string&& rule) 77 { 78 return router.newRuleTagged<Tag>(std::move(rule)); 79 } 80 81 void validate() 82 { 83 router.validate(); 84 } 85 86 void loadCertificate() 87 { 88 BMCWEB_LOG_DEBUG("Loading certificate"); 89 if (!server) 90 { 91 return; 92 } 93 server->loadCertificate(); 94 } 95 96 std::optional<boost::asio::ip::tcp::acceptor> setupSocket() 97 { 98 if (io == nullptr) 99 { 100 BMCWEB_LOG_CRITICAL("IO was nullptr?"); 101 return std::nullopt; 102 } 103 constexpr int defaultPort = 18080; 104 int listenFd = sd_listen_fds(0); 105 if (listenFd == 1) 106 { 107 BMCWEB_LOG_INFO("attempting systemd socket activation"); 108 if (sd_is_socket_inet(SD_LISTEN_FDS_START, AF_UNSPEC, SOCK_STREAM, 109 1, 0) != 0) 110 { 111 BMCWEB_LOG_INFO("Starting webserver on socket handle {}", 112 SD_LISTEN_FDS_START); 113 return boost::asio::ip::tcp::acceptor( 114 *io, boost::asio::ip::tcp::v6(), SD_LISTEN_FDS_START); 115 } 116 BMCWEB_LOG_ERROR( 117 "bad incoming socket, starting webserver on port {}", 118 defaultPort); 119 } 120 BMCWEB_LOG_INFO("Starting webserver on port {}", defaultPort); 121 return boost::asio::ip::tcp::acceptor( 122 *io, boost::asio::ip::tcp::endpoint( 123 boost::asio::ip::make_address("0.0.0.0"), defaultPort)); 124 } 125 126 void run() 127 { 128 validate(); 129 130 std::optional<boost::asio::ip::tcp::acceptor> acceptor = setupSocket(); 131 if (!acceptor) 132 { 133 BMCWEB_LOG_CRITICAL("Couldn't start server"); 134 return; 135 } 136 server.emplace(this, std::move(*acceptor), sslContext, io); 137 server->run(); 138 } 139 140 void stop() 141 { 142 io->stop(); 143 } 144 145 void debugPrint() 146 { 147 BMCWEB_LOG_DEBUG("Routing:"); 148 router.debugPrint(); 149 } 150 151 std::vector<const std::string*> getRoutes() 152 { 153 const std::string root; 154 return router.getRoutes(root); 155 } 156 std::vector<const std::string*> getRoutes(const std::string& parent) 157 { 158 return router.getRoutes(parent); 159 } 160 161 App& ssl(std::shared_ptr<boost::asio::ssl::context>&& ctx) 162 { 163 sslContext = std::move(ctx); 164 BMCWEB_LOG_INFO("app::ssl context use_count={}", 165 sslContext.use_count()); 166 return *this; 167 } 168 169 std::shared_ptr<boost::asio::ssl::context> sslContext = nullptr; 170 171 boost::asio::io_context& ioContext() 172 { 173 return *io; 174 } 175 176 private: 177 std::shared_ptr<boost::asio::io_context> io; 178 179 std::optional<server_type> server; 180 181 Router router; 182 }; 183 } // namespace crow 184 using App = crow::App; 185