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