xref: /openbmc/bmcweb/http/app.hpp (revision a529a6aa)
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/asio/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::asio::ssl::stream<boost::asio::ip::tcp::socket>;
36     using raw_socket_t = boost::asio::ip::tcp::socket;
37 
38     using socket_type = std::conditional_t<BMCWEB_INSECURE_DISABLE_SSL,
39                                            raw_socket_t, ssl_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(const std::shared_ptr<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(const std::shared_ptr<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     void loadCertificate()
87     {
88         BMCWEB_LOG_DEBUG("Loading certificate");
89         if (!server)
90         {
91             return;
92         }
93         server->loadCertificate();
94     }
95 
96     std::optional<boost::asio::ip::tcp::acceptor> setupSocket()
97     {
98         if (io == nullptr)
99         {
100             BMCWEB_LOG_CRITICAL("IO was nullptr?");
101             return std::nullopt;
102         }
103         constexpr int defaultPort = 18080;
104         int listenFd = sd_listen_fds(0);
105         if (listenFd == 1)
106         {
107             BMCWEB_LOG_INFO("attempting systemd socket activation");
108             if (sd_is_socket_inet(SD_LISTEN_FDS_START, AF_UNSPEC, SOCK_STREAM,
109                                   1, 0) != 0)
110             {
111                 BMCWEB_LOG_INFO("Starting webserver on socket handle {}",
112                                 SD_LISTEN_FDS_START);
113                 return boost::asio::ip::tcp::acceptor(
114                     *io, boost::asio::ip::tcp::v6(), SD_LISTEN_FDS_START);
115             }
116             BMCWEB_LOG_ERROR(
117                 "bad incoming socket, starting webserver on port {}",
118                 defaultPort);
119         }
120         BMCWEB_LOG_INFO("Starting webserver on port {}", defaultPort);
121         return boost::asio::ip::tcp::acceptor(
122             *io, boost::asio::ip::tcp::endpoint(
123                      boost::asio::ip::make_address("0.0.0.0"), defaultPort));
124     }
125 
126     void run()
127     {
128         validate();
129 
130         std::optional<boost::asio::ip::tcp::acceptor> acceptor = setupSocket();
131         if (!acceptor)
132         {
133             BMCWEB_LOG_CRITICAL("Couldn't start server");
134             return;
135         }
136         server.emplace(this, std::move(*acceptor), sslContext, io);
137         server->run();
138     }
139 
140     void stop()
141     {
142         io->stop();
143     }
144 
145     void debugPrint()
146     {
147         BMCWEB_LOG_DEBUG("Routing:");
148         router.debugPrint();
149     }
150 
151     std::vector<const std::string*> getRoutes()
152     {
153         const std::string root;
154         return router.getRoutes(root);
155     }
156     std::vector<const std::string*> getRoutes(const std::string& parent)
157     {
158         return router.getRoutes(parent);
159     }
160 
161     App& ssl(std::shared_ptr<boost::asio::ssl::context>&& ctx)
162     {
163         sslContext = std::move(ctx);
164         BMCWEB_LOG_INFO("app::ssl context use_count={}",
165                         sslContext.use_count());
166         return *this;
167     }
168 
169     std::shared_ptr<boost::asio::ssl::context> sslContext = nullptr;
170 
171     boost::asio::io_context& ioContext()
172     {
173         return *io;
174     }
175 
176   private:
177     std::shared_ptr<boost::asio::io_context> io;
178 
179     std::optional<server_type> server;
180 
181     Router router;
182 };
183 } // namespace crow
184 using App = crow::App;
185