| // SPDX-License-Identifier: Apache-2.0 |
| // SPDX-FileCopyrightText: Copyright OpenBMC Authors |
| #pragma once |
| |
| #include "async_resp.hpp" |
| #include "http_connect_types.hpp" |
| #include "http_request.hpp" |
| #include "http_server.hpp" |
| #include "io_context_singleton.hpp" |
| #include "logging.hpp" |
| #include "routing.hpp" |
| #include "routing/dynamicrule.hpp" |
| #include "str_utility.hpp" |
| |
| #include <sys/socket.h> |
| #include <systemd/sd-daemon.h> |
| |
| #include <boost/asio/ip/tcp.hpp> |
| |
| #include <cstddef> |
| #include <cstdint> |
| #include <memory> |
| #include <optional> |
| #include <span> |
| #include <string> |
| #include <string_view> |
| #include <utility> |
| #include <vector> |
| |
| // NOLINTNEXTLINE(cppcoreguidelines-macro-usage, clang-diagnostic-unused-macros) |
| #define BMCWEB_ROUTE(app, url) \ |
| app.template route<crow::utility::getParameterTag(url)>(url) |
| |
| namespace crow |
| { |
| class App |
| { |
| public: |
| using raw_socket_t = boost::asio::ip::tcp::socket; |
| using server_type = Server<App, raw_socket_t>; |
| |
| template <typename Adaptor> |
| void handleUpgrade(const std::shared_ptr<Request>& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| Adaptor&& adaptor) |
| { |
| router.handleUpgrade(req, asyncResp, std::forward<Adaptor>(adaptor)); |
| } |
| |
| void handle(const std::shared_ptr<Request>& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) |
| { |
| router.handle(req, asyncResp); |
| } |
| |
| DynamicRule& routeDynamic(const std::string& rule) |
| { |
| return router.newRuleDynamic(rule); |
| } |
| |
| template <uint64_t Tag> |
| auto& route(std::string&& rule) |
| { |
| return router.newRuleTagged<Tag>(std::move(rule)); |
| } |
| |
| void validate() |
| { |
| router.validate(); |
| } |
| |
| void loadCertificate() |
| { |
| BMCWEB_LOG_DEBUG("Loading certificate"); |
| if (!server) |
| { |
| return; |
| } |
| server->loadCertificate(); |
| } |
| |
| static HttpType getHttpType(std::string_view socketTypeString) |
| { |
| if (socketTypeString == "http") |
| { |
| BMCWEB_LOG_DEBUG("Got http socket"); |
| return HttpType::HTTP; |
| } |
| if (socketTypeString == "https") |
| { |
| BMCWEB_LOG_DEBUG("Got https socket"); |
| return HttpType::HTTPS; |
| } |
| if (socketTypeString == "both") |
| { |
| BMCWEB_LOG_DEBUG("Got hybrid socket"); |
| return HttpType::BOTH; |
| } |
| |
| // all other types https |
| BMCWEB_LOG_ERROR("Unknown http type={} assuming HTTPS only", |
| socketTypeString); |
| return HttpType::HTTPS; |
| } |
| |
| static std::vector<Acceptor> setupSocket() |
| { |
| std::vector<Acceptor> acceptors; |
| char** names = nullptr; |
| int listenFdCount = sd_listen_fds_with_names(0, &names); |
| BMCWEB_LOG_DEBUG("Got {} sockets to open", listenFdCount); |
| |
| if (listenFdCount < 0) |
| { |
| BMCWEB_LOG_CRITICAL("Failed to read socket files"); |
| return acceptors; |
| } |
| int socketIndex = 0; |
| for (char* name : |
| std::span<char*>(names, static_cast<size_t>(listenFdCount))) |
| { |
| if (name == nullptr) |
| { |
| continue; |
| } |
| // name looks like bmcweb_443_https_auth |
| // Assume HTTPS as default |
| std::string socketName(name); |
| |
| std::vector<std::string> socknameComponents; |
| bmcweb::split(socknameComponents, socketName, '_'); |
| HttpType httpType = getHttpType(socknameComponents[2]); |
| |
| int listenFd = socketIndex + SD_LISTEN_FDS_START; |
| if (sd_is_socket_inet(listenFd, AF_UNSPEC, SOCK_STREAM, 1, 0) > 0) |
| { |
| BMCWEB_LOG_INFO("Starting webserver on socket handle {}", |
| listenFd); |
| acceptors.emplace_back(Acceptor{ |
| boost::asio::ip::tcp::acceptor( |
| getIoContext(), boost::asio::ip::tcp::v6(), listenFd), |
| httpType}); |
| } |
| socketIndex++; |
| } |
| |
| if (acceptors.empty()) |
| { |
| constexpr int defaultPort = 18080; |
| BMCWEB_LOG_INFO("Starting webserver on port {}", defaultPort); |
| using boost::asio::ip::tcp; |
| tcp::endpoint end(tcp::v6(), defaultPort); |
| tcp::acceptor acc(getIoContext(), end); |
| acceptors.emplace_back(std::move(acc), HttpType::HTTPS); |
| } |
| |
| return acceptors; |
| } |
| |
| void run() |
| { |
| validate(); |
| |
| std::vector<Acceptor> acceptors = setupSocket(); |
| |
| server.emplace(this, std::move(acceptors)); |
| server->run(); |
| } |
| |
| void debugPrint() |
| { |
| BMCWEB_LOG_DEBUG("Routing:"); |
| router.debugPrint(); |
| } |
| |
| std::vector<const std::string*> getRoutes() |
| { |
| const std::string root; |
| return router.getRoutes(root); |
| } |
| std::vector<const std::string*> getRoutes(const std::string& parent) |
| { |
| return router.getRoutes(parent); |
| } |
| |
| std::optional<server_type> server; |
| |
| Router router; |
| }; |
| } // namespace crow |
| using App = crow::App; |