xref: /openbmc/bmcweb/http/app.hpp (revision 56431b29998d58c43b101b5f55401e505c85be5e)
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 
47     template <typename Adaptor>
48     void handleUpgrade(const std::shared_ptr<Request>& req,
49                        const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
50                        Adaptor&& adaptor)
51     {
52         router.handleUpgrade(req, asyncResp, std::forward<Adaptor>(adaptor));
53     }
54 
55     void handle(const std::shared_ptr<Request>& req,
56                 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
57     {
58         router.handle(req, asyncResp);
59     }
60 
61     DynamicRule& routeDynamic(const std::string& rule)
62     {
63         return router.newRuleDynamic(rule);
64     }
65 
66     template <uint64_t Tag>
67     auto& route(std::string&& rule)
68     {
69         return router.newRuleTagged<Tag>(std::move(rule));
70     }
71 
72     void validate()
73     {
74         router.validate();
75     }
76 
77     void loadCertificate()
78     {
79         BMCWEB_LOG_DEBUG("Loading certificate");
80         if (!server)
81         {
82             return;
83         }
84         server->loadCertificate();
85     }
86 
87     std::optional<boost::asio::ip::tcp::acceptor> setupSocket()
88     {
89         if (io == nullptr)
90         {
91             BMCWEB_LOG_CRITICAL("IO was nullptr?");
92             return std::nullopt;
93         }
94         constexpr int defaultPort = 18080;
95         if (sd_listen_fds(0) == 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 debugPrint()
131     {
132         BMCWEB_LOG_DEBUG("Routing:");
133         router.debugPrint();
134     }
135 
136     std::vector<const std::string*> getRoutes()
137     {
138         const std::string root;
139         return router.getRoutes(root);
140     }
141     std::vector<const std::string*> getRoutes(const std::string& parent)
142     {
143         return router.getRoutes(parent);
144     }
145 
146     App& ssl(std::shared_ptr<boost::asio::ssl::context>&& ctx)
147     {
148         sslContext = std::move(ctx);
149         BMCWEB_LOG_INFO("app::ssl context use_count={}",
150                         sslContext.use_count());
151         return *this;
152     }
153 
154     std::shared_ptr<boost::asio::ssl::context> sslContext = nullptr;
155 
156     boost::asio::io_context& ioContext()
157     {
158         return *io;
159     }
160 
161   private:
162     std::shared_ptr<boost::asio::io_context> io;
163 
164     std::optional<server_type> server;
165 
166     Router router;
167 };
168 } // namespace crow
169 using App = crow::App;
170