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