xref: /openbmc/bmcweb/http/http_server.hpp (revision 40e9b92ec19acffb46f83a6e55b18974da5d708e)
1 // SPDX-License-Identifier: Apache-2.0
2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors
3 #pragma once
4 
5 #include "http_connection.hpp"
6 #include "logging.hpp"
7 #include "ssl_key_handler.hpp"
8 
9 #include <boost/asio/ip/address.hpp>
10 #include <boost/asio/ip/tcp.hpp>
11 #include <boost/asio/signal_set.hpp>
12 #include <boost/asio/ssl/context.hpp>
13 #include <boost/asio/ssl/stream.hpp>
14 #include <boost/asio/steady_timer.hpp>
15 #include <boost/beast/core/stream_traits.hpp>
16 
17 #include <atomic>
18 #include <chrono>
19 #include <cstdint>
20 #include <filesystem>
21 #include <future>
22 #include <memory>
23 #include <string>
24 #include <utility>
25 #include <vector>
26 
27 namespace crow
28 {
29 
30 template <typename Handler, typename Adaptor = boost::asio::ip::tcp::socket>
31 class Server
32 {
33     using self_t = Server<Handler, Adaptor>;
34 
35   public:
Server(Handler * handlerIn,boost::asio::ip::tcp::acceptor && acceptorIn,std::shared_ptr<boost::asio::ssl::context> adaptorCtxIn,std::shared_ptr<boost::asio::io_context> io)36     Server(Handler* handlerIn, boost::asio::ip::tcp::acceptor&& acceptorIn,
37            std::shared_ptr<boost::asio::ssl::context> adaptorCtxIn,
38            std::shared_ptr<boost::asio::io_context> io) :
39         ioService(std::move(io)), acceptor(std::move(acceptorIn)),
40         signals(*ioService, SIGINT, SIGTERM, SIGHUP), handler(handlerIn),
41         adaptorCtx(std::move(adaptorCtxIn))
42     {}
43 
updateDateStr()44     void updateDateStr()
45     {
46         time_t lastTimeT = time(nullptr);
47         tm myTm{};
48 
49         gmtime_r(&lastTimeT, &myTm);
50 
51         dateStr.resize(100);
52         size_t dateStrSz = strftime(dateStr.data(), dateStr.size() - 1,
53                                     "%a, %d %b %Y %H:%M:%S GMT", &myTm);
54         dateStr.resize(dateStrSz);
55     }
56 
run()57     void run()
58     {
59         loadCertificate();
60         updateDateStr();
61 
62         getCachedDateStr = [this]() -> std::string {
63             static std::chrono::time_point<std::chrono::steady_clock>
64                 lastDateUpdate = std::chrono::steady_clock::now();
65             if (std::chrono::steady_clock::now() - lastDateUpdate >=
66                 std::chrono::seconds(10))
67             {
68                 lastDateUpdate = std::chrono::steady_clock::now();
69                 updateDateStr();
70             }
71             return dateStr;
72         };
73 
74         BMCWEB_LOG_INFO("bmcweb server is running, local endpoint {}",
75                         acceptor.local_endpoint().address().to_string());
76         startAsyncWaitForSignal();
77         doAccept();
78     }
79 
loadCertificate()80     void loadCertificate()
81     {
82         if constexpr (BMCWEB_INSECURE_DISABLE_SSL)
83         {
84             return;
85         }
86 
87         auto sslContext = ensuressl::getSslServerContext();
88 
89         adaptorCtx = sslContext;
90         handler->ssl(std::move(sslContext));
91     }
92 
startAsyncWaitForSignal()93     void startAsyncWaitForSignal()
94     {
95         signals.async_wait(
96             [this](const boost::system::error_code& ec, int signalNo) {
97                 if (ec)
98                 {
99                     BMCWEB_LOG_INFO("Error in signal handler{}", ec.message());
100                 }
101                 else
102                 {
103                     if (signalNo == SIGHUP)
104                     {
105                         BMCWEB_LOG_INFO("Receivied reload signal");
106                         loadCertificate();
107                         startAsyncWaitForSignal();
108                     }
109                     else
110                     {
111                         stop();
112                     }
113                 }
114             });
115     }
116 
stop()117     void stop()
118     {
119         ioService->stop();
120     }
121     using Socket = boost::beast::lowest_layer_type<Adaptor>;
122     using SocketPtr = std::unique_ptr<Socket>;
123 
afterAccept(SocketPtr socket,const boost::system::error_code & ec)124     void afterAccept(SocketPtr socket, const boost::system::error_code& ec)
125     {
126         if (ec)
127         {
128             BMCWEB_LOG_ERROR("Failed to accept socket {}", ec);
129             return;
130         }
131 
132         boost::asio::steady_timer timer(*ioService);
133         std::shared_ptr<Connection<Adaptor, Handler>> connection;
134 
135         if constexpr (std::is_same<Adaptor,
136                                    boost::asio::ssl::stream<
137                                        boost::asio::ip::tcp::socket>>::value)
138         {
139             if (adaptorCtx == nullptr)
140             {
141                 BMCWEB_LOG_CRITICAL(
142                     "Asked to launch TLS socket but no context available");
143                 return;
144             }
145             connection = std::make_shared<Connection<Adaptor, Handler>>(
146                 handler, std::move(timer), getCachedDateStr,
147                 Adaptor(std::move(*socket), *adaptorCtx));
148         }
149         else
150         {
151             connection = std::make_shared<Connection<Adaptor, Handler>>(
152                 handler, std::move(timer), getCachedDateStr,
153                 Adaptor(std::move(*socket)));
154         }
155 
156         boost::asio::post(*ioService, [connection] { connection->start(); });
157 
158         doAccept();
159     }
160 
doAccept()161     void doAccept()
162     {
163         if (ioService == nullptr)
164         {
165             BMCWEB_LOG_CRITICAL("IoService was null");
166             return;
167         }
168 
169         SocketPtr socket = std::make_unique<Socket>(*ioService);
170         // Keep a raw pointer so when the socket is moved, the pointer is still
171         // valid
172         Socket* socketPtr = socket.get();
173 
174         acceptor.async_accept(
175             *socketPtr,
176             std::bind_front(&self_t::afterAccept, this, std::move(socket)));
177     }
178 
179   private:
180     std::shared_ptr<boost::asio::io_context> ioService;
181     std::function<std::string()> getCachedDateStr;
182     boost::asio::ip::tcp::acceptor acceptor;
183     boost::asio::signal_set signals;
184 
185     std::string dateStr;
186 
187     Handler* handler;
188 
189     std::shared_ptr<boost::asio::ssl::context> adaptorCtx;
190 };
191 } // namespace crow
192