xref: /openbmc/bmcweb/http/app.hpp (revision 58d3aabc81589c43e15fe2dbbe71f82439d36318)
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