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