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 <chrono> 12 #include <cstdint> 13 #include <functional> 14 #include <future> 15 #include <memory> 16 #include <string> 17 #include <utility> 18 19 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 20 #define BMCWEB_ROUTE(app, url) \ 21 app.template route<crow::black_magic::getParameterTag(url)>(url) 22 23 namespace crow 24 { 25 #ifdef BMCWEB_ENABLE_SSL 26 using ssl_context_t = boost::asio::ssl::context; 27 #endif 28 class App 29 { 30 public: 31 #ifdef BMCWEB_ENABLE_SSL 32 using ssl_socket_t = boost::beast::ssl_stream<boost::asio::ip::tcp::socket>; 33 using ssl_server_t = Server<App, ssl_socket_t>; 34 #else 35 using socket_t = boost::asio::ip::tcp::socket; 36 using server_t = Server<App, socket_t>; 37 #endif 38 39 explicit App(std::shared_ptr<boost::asio::io_context> ioIn = 40 std::make_shared<boost::asio::io_context>()) : 41 io(std::move(ioIn)) 42 {} 43 ~App() 44 { 45 this->stop(); 46 } 47 48 App(const App&) = delete; 49 App(App&&) = delete; 50 App& operator=(const App&) = delete; 51 App& operator=(const App&&) = delete; 52 53 template <typename Adaptor> 54 void handleUpgrade(const Request& req, Response& res, Adaptor&& adaptor) 55 { 56 router.handleUpgrade(req, res, std::forward<Adaptor>(adaptor)); 57 } 58 59 void handle(Request& req, 60 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 61 { 62 router.handle(req, asyncResp); 63 } 64 65 DynamicRule& routeDynamic(std::string&& rule) 66 { 67 return router.newRuleDynamic(rule); 68 } 69 70 template <uint64_t Tag> 71 auto& route(std::string&& rule) 72 { 73 return router.newRuleTagged<Tag>(std::move(rule)); 74 } 75 76 App& socket(int existingSocket) 77 { 78 socketFd = existingSocket; 79 return *this; 80 } 81 82 App& port(std::uint16_t port) 83 { 84 portUint = port; 85 return *this; 86 } 87 88 App& bindaddr(std::string bindaddr) 89 { 90 bindaddrStr = std::move(bindaddr); 91 return *this; 92 } 93 94 void validate() 95 { 96 router.validate(); 97 } 98 99 void run() 100 { 101 validate(); 102 #ifdef BMCWEB_ENABLE_SSL 103 if (-1 == socketFd) 104 { 105 sslServer = std::make_unique<ssl_server_t>( 106 this, bindaddrStr, portUint, sslContext, io); 107 } 108 else 109 { 110 sslServer = 111 std::make_unique<ssl_server_t>(this, socketFd, sslContext, io); 112 } 113 sslServer->run(); 114 115 #else 116 117 if (-1 == socketFd) 118 { 119 server = std::move(std::make_unique<server_t>( 120 this, bindaddrStr, portUint, nullptr, io)); 121 } 122 else 123 { 124 server = std::move( 125 std::make_unique<server_t>(this, socketFd, nullptr, io)); 126 } 127 server->run(); 128 129 #endif 130 } 131 132 void stop() 133 { 134 io->stop(); 135 } 136 137 void debugPrint() 138 { 139 BMCWEB_LOG_DEBUG << "Routing:"; 140 router.debugPrint(); 141 } 142 143 std::vector<const std::string*> getRoutes() 144 { 145 const std::string root; 146 return router.getRoutes(root); 147 } 148 std::vector<const std::string*> getRoutes(const std::string& parent) 149 { 150 return router.getRoutes(parent); 151 } 152 153 #ifdef BMCWEB_ENABLE_SSL 154 App& sslFile(const std::string& crtFilename, const std::string& keyFilename) 155 { 156 sslContext = std::make_shared<ssl_context_t>( 157 boost::asio::ssl::context::tls_server); 158 sslContext->set_verify_mode(boost::asio::ssl::verify_peer); 159 sslContext->use_certificate_file(crtFilename, ssl_context_t::pem); 160 sslContext->use_private_key_file(keyFilename, ssl_context_t::pem); 161 sslContext->set_options(boost::asio::ssl::context::default_workarounds | 162 boost::asio::ssl::context::no_sslv2 | 163 boost::asio::ssl::context::no_sslv3 | 164 boost::asio::ssl::context::no_tlsv1 | 165 boost::asio::ssl::context::no_tlsv1_1); 166 return *this; 167 } 168 169 App& sslFile(const std::string& pemFilename) 170 { 171 sslContext = std::make_shared<ssl_context_t>( 172 boost::asio::ssl::context::tls_server); 173 sslContext->set_verify_mode(boost::asio::ssl::verify_peer); 174 sslContext->load_verify_file(pemFilename); 175 sslContext->set_options(boost::asio::ssl::context::default_workarounds | 176 boost::asio::ssl::context::no_sslv2 | 177 boost::asio::ssl::context::no_sslv3 | 178 boost::asio::ssl::context::no_tlsv1 | 179 boost::asio::ssl::context::no_tlsv1_1); 180 return *this; 181 } 182 183 App& ssl(std::shared_ptr<boost::asio::ssl::context>&& ctx) 184 { 185 sslContext = std::move(ctx); 186 BMCWEB_LOG_INFO << "app::ssl context use_count=" 187 << sslContext.use_count(); 188 return *this; 189 } 190 191 std::shared_ptr<ssl_context_t> sslContext = nullptr; 192 193 #else 194 template <typename T, typename... Remain> 195 App& ssl_file(T&&, Remain&&...) 196 { 197 // We can't call .ssl() member function unless BMCWEB_ENABLE_SSL is 198 // defined. 199 static_assert( 200 // make static_assert dependent to T; always false 201 std::is_base_of<T, void>::value, 202 "Define BMCWEB_ENABLE_SSL to enable ssl support."); 203 return *this; 204 } 205 206 template <typename T> 207 App& ssl(T&&) 208 { 209 // We can't call .ssl() member function unless BMCWEB_ENABLE_SSL is 210 // defined. 211 static_assert( 212 // make static_assert dependent to T; always false 213 std::is_base_of<T, void>::value, 214 "Define BMCWEB_ENABLE_SSL to enable ssl support."); 215 return *this; 216 } 217 #endif 218 219 private: 220 std::shared_ptr<boost::asio::io_context> io; 221 #ifdef BMCWEB_ENABLE_SSL 222 uint16_t portUint = 443; 223 #else 224 uint16_t portUint = 80; 225 #endif 226 std::string bindaddrStr = "0.0.0.0"; 227 int socketFd = -1; 228 Router router; 229 230 #ifdef BMCWEB_ENABLE_SSL 231 std::unique_ptr<ssl_server_t> sslServer; 232 #else 233 std::unique_ptr<server_t> server; 234 #endif 235 }; 236 } // namespace crow 237 using App = crow::App; 238