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