blob: 82ba9d014cf98fc48630a3c30e46da23747c77fe [file] [log] [blame]
Ed Tanous7045c8d2017-04-03 10:04:37 -07001#pragma once
2
Ed Tanous911ac312017-08-15 09:37:42 -07003#include <atomic>
Ed Tanouse278c182019-03-13 16:23:37 -07004#include <boost/asio/ip/address.hpp>
Ed Tanous3112a142018-11-29 15:45:10 -08005#include <boost/asio/ip/tcp.hpp>
6#include <boost/asio/signal_set.hpp>
Ed Tanous2f1ebcd2019-02-13 19:39:07 -08007#include <boost/asio/ssl/context.hpp>
Ed Tanous271584a2019-07-09 16:24:22 -07008#include <boost/asio/steady_timer.hpp>
Ed Tanouse278c182019-03-13 16:23:37 -07009#if BOOST_VERSION >= 107000
10#include <boost/beast/ssl/ssl_stream.hpp>
11#else
Ed Tanous2f1ebcd2019-02-13 19:39:07 -080012#include <boost/beast/experimental/core/ssl_stream.hpp>
Ed Tanouse278c182019-03-13 16:23:37 -070013#endif
14
Ed Tanous1abe55e2018-09-05 08:30:59 -070015#include <boost/date_time/posix_time/posix_time.hpp>
Ed Tanous3dac7492017-08-02 13:46:20 -070016#include <chrono>
Ed Tanous911ac312017-08-15 09:37:42 -070017#include <cstdint>
Marri Devender Rao5968cae2019-01-21 10:27:12 -060018#include <filesystem>
Ed Tanous911ac312017-08-15 09:37:42 -070019#include <future>
20#include <memory>
Marri Devender Rao5968cae2019-01-21 10:27:12 -060021#include <ssl_key_handler.hpp>
Ed Tanous911ac312017-08-15 09:37:42 -070022#include <utility>
23#include <vector>
Ed Tanous1abe55e2018-09-05 08:30:59 -070024
Ed Tanousc94ad492019-10-10 15:39:33 -070025#include "http_connection.h"
26#include "logging.h"
27#include "timer_queue.h"
Ed Tanous7045c8d2017-04-03 10:04:37 -070028
Ed Tanous1abe55e2018-09-05 08:30:59 -070029namespace crow
30{
Ed Tanous7045c8d2017-04-03 10:04:37 -070031using namespace boost;
32using tcp = asio::ip::tcp;
33
Ed Tanousceac6f72018-12-02 11:58:47 -080034template <typename Handler, typename Adaptor = boost::asio::ip::tcp::socket,
Ed Tanous7045c8d2017-04-03 10:04:37 -070035 typename... Middlewares>
Ed Tanous1abe55e2018-09-05 08:30:59 -070036class Server
37{
38 public:
39 Server(Handler* handler, std::unique_ptr<tcp::acceptor>&& acceptor,
Jason M. Billse3e29612019-09-13 08:05:13 -070040 std::shared_ptr<boost::asio::ssl::context> adaptor_ctx,
Ed Tanous1abe55e2018-09-05 08:30:59 -070041 std::tuple<Middlewares...>* middlewares = nullptr,
Ed Tanous8f626352018-12-19 14:51:54 -080042 std::shared_ptr<boost::asio::io_context> io =
43 std::make_shared<boost::asio::io_context>()) :
Ed Tanous1abe55e2018-09-05 08:30:59 -070044 ioService(std::move(io)),
Marri Devender Rao5968cae2019-01-21 10:27:12 -060045 acceptor(std::move(acceptor)),
46 signals(*ioService, SIGINT, SIGTERM, SIGHUP), tickTimer(*ioService),
Ed Tanous271584a2019-07-09 16:24:22 -070047 handler(handler), middlewares(middlewares), adaptorCtx(adaptor_ctx)
Ed Tanous1abe55e2018-09-05 08:30:59 -070048 {
49 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070050
Ed Tanous1abe55e2018-09-05 08:30:59 -070051 Server(Handler* handler, const std::string& bindaddr, uint16_t port,
Jason M. Billse3e29612019-09-13 08:05:13 -070052 std::shared_ptr<boost::asio::ssl::context> adaptor_ctx,
Ed Tanous1abe55e2018-09-05 08:30:59 -070053 std::tuple<Middlewares...>* middlewares = nullptr,
Ed Tanous8f626352018-12-19 14:51:54 -080054 std::shared_ptr<boost::asio::io_context> io =
55 std::make_shared<boost::asio::io_context>()) :
Ed Tanous1abe55e2018-09-05 08:30:59 -070056 Server(handler,
Ed Tanous9e6e1b22018-03-16 13:08:50 -070057 std::make_unique<tcp::acceptor>(
Ed Tanouse278c182019-03-13 16:23:37 -070058 *io, tcp::endpoint(boost::asio::ip::make_address(bindaddr),
59 port)),
Marri Devender Rao5968cae2019-01-21 10:27:12 -060060 adaptor_ctx, middlewares, io)
Ed Tanous1abe55e2018-09-05 08:30:59 -070061 {
Ed Tanous7045c8d2017-04-03 10:04:37 -070062 }
63
Ed Tanous1abe55e2018-09-05 08:30:59 -070064 Server(Handler* handler, int existing_socket,
Jason M. Billse3e29612019-09-13 08:05:13 -070065 std::shared_ptr<boost::asio::ssl::context> adaptor_ctx,
Ed Tanous1abe55e2018-09-05 08:30:59 -070066 std::tuple<Middlewares...>* middlewares = nullptr,
Ed Tanous8f626352018-12-19 14:51:54 -080067 std::shared_ptr<boost::asio::io_context> io =
68 std::make_shared<boost::asio::io_context>()) :
Ed Tanous1abe55e2018-09-05 08:30:59 -070069 Server(handler,
70 std::make_unique<tcp::acceptor>(*io, boost::asio::ip::tcp::v6(),
71 existing_socket),
Marri Devender Rao5968cae2019-01-21 10:27:12 -060072 adaptor_ctx, middlewares, io)
Ed Tanous1abe55e2018-09-05 08:30:59 -070073 {
74 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070075
Ed Tanous1abe55e2018-09-05 08:30:59 -070076 void setTickFunction(std::chrono::milliseconds d, std::function<void()> f)
77 {
78 tickInterval = d;
79 tickFunction = f;
80 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070081
Ed Tanous1abe55e2018-09-05 08:30:59 -070082 void onTick()
83 {
84 tickFunction();
Ed Tanous271584a2019-07-09 16:24:22 -070085 tickTimer.expires_after(
86 std::chrono::milliseconds(tickInterval.count()));
Ed Tanous1abe55e2018-09-05 08:30:59 -070087 tickTimer.async_wait([this](const boost::system::error_code& ec) {
88 if (ec)
89 {
90 return;
91 }
92 onTick();
93 });
94 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070095
Ed Tanous1abe55e2018-09-05 08:30:59 -070096 void updateDateStr()
97 {
Ed Tanous271584a2019-07-09 16:24:22 -070098 time_t lastTimeT = time(0);
Ed Tanous1abe55e2018-09-05 08:30:59 -070099 tm myTm{};
Ed Tanous7045c8d2017-04-03 10:04:37 -0700100
Ed Tanous1abe55e2018-09-05 08:30:59 -0700101 gmtime_r(&lastTimeT, &myTm);
Ed Tanous271584a2019-07-09 16:24:22 -0700102
Ed Tanous1abe55e2018-09-05 08:30:59 -0700103 dateStr.resize(100);
104 size_t dateStrSz =
105 strftime(&dateStr[0], 99, "%a, %d %b %Y %H:%M:%S GMT", &myTm);
106 dateStr.resize(dateStrSz);
107 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700108
Ed Tanous1abe55e2018-09-05 08:30:59 -0700109 void run()
110 {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600111 loadCertificate();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700112 updateDateStr();
Ed Tanous7045c8d2017-04-03 10:04:37 -0700113
Ed Tanous1abe55e2018-09-05 08:30:59 -0700114 getCachedDateStr = [this]() -> std::string {
115 static std::chrono::time_point<std::chrono::steady_clock>
116 lastDateUpdate = std::chrono::steady_clock::now();
117 if (std::chrono::steady_clock::now() - lastDateUpdate >=
118 std::chrono::seconds(10))
119 {
120 lastDateUpdate = std::chrono::steady_clock::now();
121 updateDateStr();
122 }
123 return this->dateStr;
124 };
Ed Tanous9e6e1b22018-03-16 13:08:50 -0700125
Ed Tanous271584a2019-07-09 16:24:22 -0700126 boost::asio::steady_timer timer(*ioService);
127 timer.expires_after(std::chrono::seconds(1));
Ed Tanous7045c8d2017-04-03 10:04:37 -0700128
Marri Devender Rao92e07bf2019-04-04 01:33:34 -0500129 std::function<void(const boost::system::error_code& ec)> timerHandler;
130 timerHandler = [&](const boost::system::error_code& ec) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700131 if (ec)
132 {
133 return;
134 }
135 timerQueue.process();
Ed Tanous271584a2019-07-09 16:24:22 -0700136 timer.expires_after(std::chrono::seconds(1));
Marri Devender Rao92e07bf2019-04-04 01:33:34 -0500137 timer.async_wait(timerHandler);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700138 };
Marri Devender Rao92e07bf2019-04-04 01:33:34 -0500139 timer.async_wait(timerHandler);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700140
Ed Tanous1abe55e2018-09-05 08:30:59 -0700141 if (tickFunction && tickInterval.count() > 0)
142 {
Ed Tanous271584a2019-07-09 16:24:22 -0700143 tickTimer.expires_after(
144 std::chrono::milliseconds(tickInterval.count()));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700145 tickTimer.async_wait([this](const boost::system::error_code& ec) {
146 if (ec)
147 {
148 return;
149 }
150 onTick();
151 });
152 }
153
154 BMCWEB_LOG_INFO << serverName << " server is running, local endpoint "
155 << acceptor->local_endpoint();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600156 startAsyncWaitForSignal();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700157 doAccept();
158 }
159
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600160 void loadCertificate()
161 {
162#ifdef BMCWEB_ENABLE_SSL
163 namespace fs = std::filesystem;
164 // Cleanup older certificate file existing in the system
165 fs::path oldCert = "/home/root/server.pem";
166 if (fs::exists(oldCert))
167 {
168 fs::remove("/home/root/server.pem");
169 }
170 fs::path certPath = "/etc/ssl/certs/https/";
171 // if path does not exist create the path so that
172 // self signed certificate can be created in the
173 // path
174 if (!fs::exists(certPath))
175 {
176 fs::create_directories(certPath);
177 }
178 fs::path certFile = certPath / "server.pem";
179 BMCWEB_LOG_INFO << "Building SSL Context file=" << certFile;
180 std::string sslPemFile(certFile);
181 ensuressl::ensureOpensslKeyPresentAndValid(sslPemFile);
182 std::shared_ptr<boost::asio::ssl::context> sslContext =
183 ensuressl::getSslContext(sslPemFile);
184 adaptorCtx = sslContext;
185 handler->ssl(std::move(sslContext));
186#endif
187 }
188
189 void startAsyncWaitForSignal()
190 {
191 signals.async_wait([this](const boost::system::error_code& ec,
192 int signalNo) {
193 if (ec)
194 {
195 BMCWEB_LOG_INFO << "Error in signal handler" << ec.message();
196 }
197 else
198 {
199 if (signalNo == SIGHUP)
200 {
201 BMCWEB_LOG_INFO << "Receivied reload signal";
202 loadCertificate();
Zbigniew Lukwinski7d0120b2019-10-15 09:12:45 +0200203 boost::system::error_code ec;
204 acceptor->cancel(ec);
205 if (ec)
206 {
207 BMCWEB_LOG_ERROR
208 << "Error while canceling async operations:"
209 << ec.message();
210 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600211 this->startAsyncWaitForSignal();
212 }
213 else
214 {
215 stop();
216 }
217 }
218 });
219 }
220
Ed Tanous1abe55e2018-09-05 08:30:59 -0700221 void stop()
222 {
223 ioService->stop();
224 }
225
226 void doAccept()
227 {
Ed Tanousceac6f72018-12-02 11:58:47 -0800228 std::optional<Adaptor> adaptorTemp;
229 if constexpr (std::is_same<Adaptor,
230 boost::beast::ssl_stream<
231 boost::asio::ip::tcp::socket>>::value)
232 {
233 adaptorTemp = Adaptor(*ioService, *adaptorCtx);
Ed Tanouse278c182019-03-13 16:23:37 -0700234 Connection<Adaptor, Handler, Middlewares...>* p =
235 new Connection<Adaptor, Handler, Middlewares...>(
236 *ioService, handler, serverName, middlewares,
237 getCachedDateStr, timerQueue,
238 std::move(adaptorTemp.value()));
239
240 acceptor->async_accept(p->socket().next_layer(),
241 [this, p](boost::system::error_code ec) {
242 if (!ec)
243 {
244 boost::asio::post(
245 *this->ioService,
246 [p] { p->start(); });
247 }
248 else
249 {
250 delete p;
251 }
252 doAccept();
253 });
Ed Tanousceac6f72018-12-02 11:58:47 -0800254 }
255 else
256 {
257 adaptorTemp = Adaptor(*ioService);
Ed Tanouse278c182019-03-13 16:23:37 -0700258 Connection<Adaptor, Handler, Middlewares...>* p =
259 new Connection<Adaptor, Handler, Middlewares...>(
260 *ioService, handler, serverName, middlewares,
261 getCachedDateStr, timerQueue,
262 std::move(adaptorTemp.value()));
263
264 acceptor->async_accept(
265 p->socket(), [this, p](boost::system::error_code ec) {
266 if (!ec)
267 {
268 boost::asio::post(*this->ioService,
269 [p] { p->start(); });
270 }
271 else
272 {
273 delete p;
274 }
275 doAccept();
276 });
Ed Tanousceac6f72018-12-02 11:58:47 -0800277 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700278 }
279
280 private:
Ed Tanous8f626352018-12-19 14:51:54 -0800281 std::shared_ptr<asio::io_context> ioService;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700282 detail::TimerQueue timerQueue;
283 std::function<std::string()> getCachedDateStr;
284 std::unique_ptr<tcp::acceptor> acceptor;
285 boost::asio::signal_set signals;
Ed Tanous271584a2019-07-09 16:24:22 -0700286 boost::asio::steady_timer tickTimer;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700287
288 std::string dateStr;
289
290 Handler* handler;
291 std::string serverName = "iBMC";
292
293 std::chrono::milliseconds tickInterval{};
294 std::function<void()> tickFunction;
295
296 std::tuple<Middlewares...>* middlewares;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700297
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700298#ifdef BMCWEB_ENABLE_SSL
Ed Tanous1abe55e2018-09-05 08:30:59 -0700299 bool useSsl{false};
Ed Tanous7045c8d2017-04-03 10:04:37 -0700300#endif
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600301 std::shared_ptr<boost::asio::ssl::context> adaptorCtx;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700302}; // namespace crow
303} // namespace crow