xref: /openbmc/bmcweb/http/http_server.hpp (revision d5fb584a)
1 #pragma once
2 
3 #include "http_connection.hpp"
4 #include "logging.hpp"
5 #include "ssl_key_handler.hpp"
6 
7 #include <boost/asio/ip/address.hpp>
8 #include <boost/asio/ip/tcp.hpp>
9 #include <boost/asio/signal_set.hpp>
10 #include <boost/asio/ssl/context.hpp>
11 #include <boost/asio/ssl/stream.hpp>
12 #include <boost/asio/steady_timer.hpp>
13 
14 #include <atomic>
15 #include <chrono>
16 #include <cstdint>
17 #include <filesystem>
18 #include <future>
19 #include <memory>
20 #include <string>
21 #include <utility>
22 #include <vector>
23 
24 namespace crow
25 {
26 
27 template <typename Handler, typename Adaptor = boost::asio::ip::tcp::socket>
28 class Server
29 {
30   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)31     Server(Handler* handlerIn, boost::asio::ip::tcp::acceptor&& acceptorIn,
32            std::shared_ptr<boost::asio::ssl::context> adaptorCtxIn,
33            std::shared_ptr<boost::asio::io_context> io) :
34         ioService(std::move(io)),
35         acceptor(std::move(acceptorIn)),
36         signals(*ioService, SIGINT, SIGTERM, SIGHUP), handler(handlerIn),
37         adaptorCtx(std::move(adaptorCtxIn))
38     {}
39 
updateDateStr()40     void updateDateStr()
41     {
42         time_t lastTimeT = time(nullptr);
43         tm myTm{};
44 
45         gmtime_r(&lastTimeT, &myTm);
46 
47         dateStr.resize(100);
48         size_t dateStrSz = strftime(dateStr.data(), dateStr.size() - 1,
49                                     "%a, %d %b %Y %H:%M:%S GMT", &myTm);
50         dateStr.resize(dateStrSz);
51     }
52 
run()53     void run()
54     {
55         loadCertificate();
56         updateDateStr();
57 
58         getCachedDateStr = [this]() -> std::string {
59             static std::chrono::time_point<std::chrono::steady_clock>
60                 lastDateUpdate = std::chrono::steady_clock::now();
61             if (std::chrono::steady_clock::now() - lastDateUpdate >=
62                 std::chrono::seconds(10))
63             {
64                 lastDateUpdate = std::chrono::steady_clock::now();
65                 updateDateStr();
66             }
67             return dateStr;
68         };
69 
70         BMCWEB_LOG_INFO("bmcweb server is running, local endpoint {}",
71                         acceptor.local_endpoint().address().to_string());
72         startAsyncWaitForSignal();
73         doAccept();
74     }
75 
loadCertificate()76     void loadCertificate()
77     {
78         if constexpr (BMCWEB_INSECURE_DISABLE_SSL)
79         {
80             return;
81         }
82 
83         auto sslContext = ensuressl::getSslServerContext();
84 
85         adaptorCtx = sslContext;
86         handler->ssl(std::move(sslContext));
87     }
88 
startAsyncWaitForSignal()89     void startAsyncWaitForSignal()
90     {
91         signals.async_wait(
92             [this](const boost::system::error_code& ec, int signalNo) {
93             if (ec)
94             {
95                 BMCWEB_LOG_INFO("Error in signal handler{}", ec.message());
96             }
97             else
98             {
99                 if (signalNo == SIGHUP)
100                 {
101                     BMCWEB_LOG_INFO("Receivied reload signal");
102                     loadCertificate();
103                     boost::system::error_code ec2;
104                     acceptor.cancel(ec2);
105                     if (ec2)
106                     {
107                         BMCWEB_LOG_ERROR(
108                             "Error while canceling async operations:{}",
109                             ec2.message());
110                     }
111                     startAsyncWaitForSignal();
112                 }
113                 else
114                 {
115                     stop();
116                 }
117             }
118         });
119     }
120 
stop()121     void stop()
122     {
123         ioService->stop();
124     }
125 
doAccept()126     void doAccept()
127     {
128         if (ioService == nullptr)
129         {
130             BMCWEB_LOG_CRITICAL("IoService was null");
131             return;
132         }
133         boost::asio::steady_timer timer(*ioService);
134         std::shared_ptr<Connection<Adaptor, Handler>> connection;
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(*ioService, *adaptorCtx));
148         }
149         else
150         {
151             connection = std::make_shared<Connection<Adaptor, Handler>>(
152                 handler, std::move(timer), getCachedDateStr,
153                 Adaptor(*ioService));
154         }
155         acceptor.async_accept(
156             boost::beast::get_lowest_layer(connection->socket()),
157             [this, connection](const boost::system::error_code& ec) {
158             if (!ec)
159             {
160                 boost::asio::post(*ioService,
161                                   [connection] { connection->start(); });
162             }
163             doAccept();
164         });
165     }
166 
167   private:
168     std::shared_ptr<boost::asio::io_context> ioService;
169     std::function<std::string()> getCachedDateStr;
170     boost::asio::ip::tcp::acceptor acceptor;
171     boost::asio::signal_set signals;
172 
173     std::string dateStr;
174 
175     Handler* handler;
176 
177     std::shared_ptr<boost::asio::ssl::context> adaptorCtx;
178 };
179 } // namespace crow
180