xref: /openbmc/bmcweb/http/http_server.hpp (revision 5dfb5b2d8826f568a0ad6f6430f01cd7a86ab07f)
1 #pragma once
2 
3 #include "http_connection.hpp"
4 #include "logging.hpp"
5 
6 #include <boost/asio/ip/address.hpp>
7 #include <boost/asio/ip/tcp.hpp>
8 #include <boost/asio/signal_set.hpp>
9 #include <boost/asio/ssl/context.hpp>
10 #include <boost/asio/steady_timer.hpp>
11 #include <boost/beast/ssl/ssl_stream.hpp>
12 #include <boost/date_time/posix_time/posix_time.hpp>
13 #include <ssl_key_handler.hpp>
14 
15 #include <atomic>
16 #include <chrono>
17 #include <cstdint>
18 #include <filesystem>
19 #include <future>
20 #include <memory>
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:
31     Server(Handler* handlerIn,
32            std::unique_ptr<boost::asio::ip::tcp::acceptor>&& acceptorIn,
33            std::shared_ptr<boost::asio::ssl::context> adaptorCtx,
34            std::shared_ptr<boost::asio::io_context> io =
35                std::make_shared<boost::asio::io_context>()) :
36         ioService(std::move(io)),
37         acceptor(std::move(acceptorIn)),
38         signals(*ioService, SIGINT, SIGTERM, SIGHUP), handler(handlerIn),
39         adaptorCtx(std::move(adaptorCtx))
40     {}
41 
42     Server(Handler* handlerIn, const std::string& bindaddr, uint16_t port,
43            const std::shared_ptr<boost::asio::ssl::context>& adaptorCtx,
44            const std::shared_ptr<boost::asio::io_context>& io =
45                std::make_shared<boost::asio::io_context>()) :
46         Server(handlerIn,
47                std::make_unique<boost::asio::ip::tcp::acceptor>(
48                    *io, boost::asio::ip::tcp::endpoint(
49                             boost::asio::ip::make_address(bindaddr), port)),
50                adaptorCtx, io)
51     {}
52 
53     Server(Handler* handlerIn, int existingSocket,
54            const std::shared_ptr<boost::asio::ssl::context>& adaptorCtx,
55            const std::shared_ptr<boost::asio::io_context>& io =
56                std::make_shared<boost::asio::io_context>()) :
57         Server(handlerIn,
58                std::make_unique<boost::asio::ip::tcp::acceptor>(
59                    *io, boost::asio::ip::tcp::v6(), existingSocket),
60                adaptorCtx, io)
61     {}
62 
63     void updateDateStr()
64     {
65         time_t lastTimeT = time(nullptr);
66         tm myTm{};
67 
68         gmtime_r(&lastTimeT, &myTm);
69 
70         dateStr.resize(100);
71         size_t dateStrSz =
72             strftime(&dateStr[0], 99, "%a, %d %b %Y %H:%M:%S GMT", &myTm);
73         dateStr.resize(dateStrSz);
74     }
75 
76     void run()
77     {
78         loadCertificate();
79         updateDateStr();
80 
81         getCachedDateStr = [this]() -> std::string {
82             static std::chrono::time_point<std::chrono::steady_clock>
83                 lastDateUpdate = std::chrono::steady_clock::now();
84             if (std::chrono::steady_clock::now() - lastDateUpdate >=
85                 std::chrono::seconds(10))
86             {
87                 lastDateUpdate = std::chrono::steady_clock::now();
88                 updateDateStr();
89             }
90             return this->dateStr;
91         };
92 
93         BMCWEB_LOG_INFO << "bmcweb server is running, local endpoint "
94                         << acceptor->local_endpoint();
95         startAsyncWaitForSignal();
96         doAccept();
97     }
98 
99     void loadCertificate()
100     {
101 #ifdef BMCWEB_ENABLE_SSL
102         namespace fs = std::filesystem;
103         // Cleanup older certificate file existing in the system
104         fs::path oldCert = "/home/root/server.pem";
105         if (fs::exists(oldCert))
106         {
107             fs::remove("/home/root/server.pem");
108         }
109         fs::path certPath = "/etc/ssl/certs/https/";
110         // if path does not exist create the path so that
111         // self signed certificate can be created in the
112         // path
113         if (!fs::exists(certPath))
114         {
115             fs::create_directories(certPath);
116         }
117         fs::path certFile = certPath / "server.pem";
118         BMCWEB_LOG_INFO << "Building SSL Context file=" << certFile;
119         std::string sslPemFile(certFile);
120         ensuressl::ensureOpensslKeyPresentAndValid(sslPemFile);
121         std::shared_ptr<boost::asio::ssl::context> sslContext =
122             ensuressl::getSslContext(sslPemFile);
123         adaptorCtx = sslContext;
124         handler->ssl(std::move(sslContext));
125 #endif
126     }
127 
128     void startAsyncWaitForSignal()
129     {
130         signals.async_wait([this](const boost::system::error_code& ec,
131                                   int signalNo) {
132             if (ec)
133             {
134                 BMCWEB_LOG_INFO << "Error in signal handler" << ec.message();
135             }
136             else
137             {
138                 if (signalNo == SIGHUP)
139                 {
140                     BMCWEB_LOG_INFO << "Receivied reload signal";
141                     loadCertificate();
142                     boost::system::error_code ec2;
143                     acceptor->cancel(ec2);
144                     if (ec2)
145                     {
146                         BMCWEB_LOG_ERROR
147                             << "Error while canceling async operations:"
148                             << ec2.message();
149                     }
150                     this->startAsyncWaitForSignal();
151                 }
152                 else
153                 {
154                     stop();
155                 }
156             }
157         });
158     }
159 
160     void stop()
161     {
162         ioService->stop();
163     }
164 
165     void doAccept()
166     {
167         std::optional<Adaptor> adaptorTemp;
168         boost::asio::steady_timer timer(*ioService);
169         if constexpr (std::is_same<Adaptor,
170                                    boost::beast::ssl_stream<
171                                        boost::asio::ip::tcp::socket>>::value)
172         {
173             adaptorTemp = Adaptor(*ioService, *adaptorCtx);
174             auto p = std::make_shared<Connection<Adaptor, Handler>>(
175                 handler, std::move(timer), getCachedDateStr,
176                 std::move(adaptorTemp.value()));
177 
178             acceptor->async_accept(p->socket().next_layer(),
179                                    [this, p](boost::system::error_code ec) {
180                                        if (!ec)
181                                        {
182                                            boost::asio::post(
183                                                *this->ioService,
184                                                [p] { p->start(); });
185                                        }
186                                        doAccept();
187                                    });
188         }
189         else
190         {
191             adaptorTemp = Adaptor(*ioService);
192             auto p = std::make_shared<Connection<Adaptor, Handler>>(
193                 handler, std::move(timer), getCachedDateStr,
194                 std::move(adaptorTemp.value()));
195 
196             acceptor->async_accept(
197                 p->socket(), [this, p](boost::system::error_code ec) {
198                     if (!ec)
199                     {
200                         boost::asio::post(*this->ioService,
201                                           [p] { p->start(); });
202                     }
203                     doAccept();
204                 });
205         }
206     }
207 
208   private:
209     std::shared_ptr<boost::asio::io_context> ioService;
210     std::function<std::string()> getCachedDateStr;
211     std::unique_ptr<boost::asio::ip::tcp::acceptor> acceptor;
212     boost::asio::signal_set signals;
213 
214     std::string dateStr;
215 
216     Handler* handler;
217 
218 #ifdef BMCWEB_ENABLE_SSL
219     bool useSsl{false};
220 #endif
221     std::shared_ptr<boost::asio::ssl::context> adaptorCtx;
222 };
223 } // namespace crow
224