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_connect_types.hpp" 7 #include "http_request.hpp" 8 #include "http_server.hpp" 9 #include "io_context_singleton.hpp" 10 #include "logging.hpp" 11 #include "routing.hpp" 12 #include "routing/dynamicrule.hpp" 13 #include "str_utility.hpp" 14 15 #include <sys/socket.h> 16 #include <systemd/sd-daemon.h> 17 18 #include <boost/asio/ip/tcp.hpp> 19 20 #include <cstddef> 21 #include <cstdint> 22 #include <memory> 23 #include <optional> 24 #include <span> 25 #include <string> 26 #include <string_view> 27 #include <utility> 28 #include <vector> 29 30 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage, clang-diagnostic-unused-macros) 31 #define BMCWEB_ROUTE(app, url) \ 32 app.template route<crow::utility::getParameterTag(url)>(url) 33 34 namespace crow 35 { 36 class App 37 { 38 public: 39 using raw_socket_t = boost::asio::ip::tcp::socket; 40 using server_type = Server<App, raw_socket_t>; 41 42 template <typename Adaptor> handleUpgrade(const std::shared_ptr<Request> & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,Adaptor && adaptor)43 void handleUpgrade(const std::shared_ptr<Request>& req, 44 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 45 Adaptor&& adaptor) 46 { 47 router.handleUpgrade(req, asyncResp, std::forward<Adaptor>(adaptor)); 48 } 49 handle(const std::shared_ptr<Request> & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)50 void handle(const std::shared_ptr<Request>& req, 51 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 52 { 53 router.handle(req, asyncResp); 54 } 55 routeDynamic(const std::string & rule)56 DynamicRule& routeDynamic(const std::string& rule) 57 { 58 return router.newRuleDynamic(rule); 59 } 60 61 template <uint64_t Tag> route(std::string && rule)62 auto& route(std::string&& rule) 63 { 64 return router.newRuleTagged<Tag>(std::move(rule)); 65 } 66 validate()67 void validate() 68 { 69 router.validate(); 70 } 71 loadCertificate()72 void loadCertificate() 73 { 74 BMCWEB_LOG_DEBUG("Loading certificate"); 75 if (!server) 76 { 77 return; 78 } 79 server->loadCertificate(); 80 } 81 getHttpType(std::string_view socketTypeString)82 static HttpType getHttpType(std::string_view socketTypeString) 83 { 84 if (socketTypeString == "http") 85 { 86 BMCWEB_LOG_DEBUG("Got http socket"); 87 return HttpType::HTTP; 88 } 89 if (socketTypeString == "https") 90 { 91 BMCWEB_LOG_DEBUG("Got https socket"); 92 return HttpType::HTTPS; 93 } 94 if (socketTypeString == "both") 95 { 96 BMCWEB_LOG_DEBUG("Got hybrid socket"); 97 return HttpType::BOTH; 98 } 99 100 // all other types https 101 BMCWEB_LOG_ERROR("Unknown http type={} assuming HTTPS only", 102 socketTypeString); 103 return HttpType::HTTPS; 104 } 105 setupSocket()106 static std::vector<Acceptor> setupSocket() 107 { 108 std::vector<Acceptor> acceptors; 109 char** names = nullptr; 110 int listenFdCount = sd_listen_fds_with_names(0, &names); 111 BMCWEB_LOG_DEBUG("Got {} sockets to open", listenFdCount); 112 113 if (listenFdCount < 0) 114 { 115 BMCWEB_LOG_CRITICAL("Failed to read socket files"); 116 return acceptors; 117 } 118 int socketIndex = 0; 119 for (char* name : 120 std::span<char*>(names, static_cast<size_t>(listenFdCount))) 121 { 122 if (name == nullptr) 123 { 124 continue; 125 } 126 // name looks like bmcweb_443_https_auth 127 // Assume HTTPS as default 128 std::string socketName(name); 129 130 std::vector<std::string> socknameComponents; 131 bmcweb::split(socknameComponents, socketName, '_'); 132 HttpType httpType = getHttpType(socknameComponents[2]); 133 134 int listenFd = socketIndex + SD_LISTEN_FDS_START; 135 if (sd_is_socket_inet(listenFd, AF_UNSPEC, SOCK_STREAM, 1, 0) > 0) 136 { 137 BMCWEB_LOG_INFO("Starting webserver on socket handle {}", 138 listenFd); 139 acceptors.emplace_back(Acceptor{ 140 boost::asio::ip::tcp::acceptor( 141 getIoContext(), boost::asio::ip::tcp::v6(), listenFd), 142 httpType}); 143 } 144 socketIndex++; 145 } 146 147 if (acceptors.empty()) 148 { 149 constexpr int defaultPort = 18080; 150 BMCWEB_LOG_INFO("Starting webserver on port {}", defaultPort); 151 using boost::asio::ip::tcp; 152 tcp::endpoint end(tcp::v6(), defaultPort); 153 tcp::acceptor acc(getIoContext(), end); 154 acceptors.emplace_back(std::move(acc), HttpType::HTTPS); 155 } 156 157 return acceptors; 158 } 159 run()160 void run() 161 { 162 validate(); 163 164 std::vector<Acceptor> acceptors = setupSocket(); 165 166 server.emplace(this, std::move(acceptors)); 167 server->run(); 168 } 169 debugPrint()170 void debugPrint() 171 { 172 BMCWEB_LOG_DEBUG("Routing:"); 173 router.debugPrint(); 174 } 175 getRoutes()176 std::vector<const std::string*> getRoutes() 177 { 178 const std::string root; 179 return router.getRoutes(root); 180 } getRoutes(const std::string & parent)181 std::vector<const std::string*> getRoutes(const std::string& parent) 182 { 183 return router.getRoutes(parent); 184 } 185 186 std::optional<server_type> server; 187 188 Router router; 189 }; 190 } // namespace crow 191 using App = crow::App; 192