xref: /openbmc/bmcweb/http/http_server.hpp (revision 46f780f777ea8169f2c7d992aa2e979ed228b9b7)
140e9b92eSEd Tanous // SPDX-License-Identifier: Apache-2.0
240e9b92eSEd Tanous // SPDX-FileCopyrightText: Copyright OpenBMC Authors
304e438cbSEd Tanous #pragma once
404e438cbSEd Tanous 
5d7857201SEd Tanous #include "bmcweb_config.h"
6d7857201SEd Tanous 
7*796ba93bSEd Tanous #include "http_connect_types.hpp"
804e438cbSEd Tanous #include "http_connection.hpp"
9*796ba93bSEd Tanous #include "io_context_singleton.hpp"
1004e438cbSEd Tanous #include "logging.hpp"
113ccb3adbSEd Tanous #include "ssl_key_handler.hpp"
1204e438cbSEd Tanous 
1304e438cbSEd Tanous #include <boost/asio/ip/address.hpp>
1404e438cbSEd Tanous #include <boost/asio/ip/tcp.hpp>
1504e438cbSEd Tanous #include <boost/asio/signal_set.hpp>
1604e438cbSEd Tanous #include <boost/asio/ssl/context.hpp>
17003301a2SEd Tanous #include <boost/asio/ssl/stream.hpp>
1804e438cbSEd Tanous #include <boost/asio/steady_timer.hpp>
1904e438cbSEd Tanous 
2004e438cbSEd Tanous #include <chrono>
21d7857201SEd Tanous #include <csignal>
22d7857201SEd Tanous #include <cstddef>
23d7857201SEd Tanous #include <ctime>
24d7857201SEd Tanous #include <functional>
2504e438cbSEd Tanous #include <memory>
26099225ccSEd Tanous #include <string>
2704e438cbSEd Tanous #include <utility>
28*796ba93bSEd Tanous #include <vector>
2904e438cbSEd Tanous 
3004e438cbSEd Tanous namespace crow
3104e438cbSEd Tanous {
3204e438cbSEd Tanous 
33*796ba93bSEd Tanous struct Acceptor
34*796ba93bSEd Tanous {
35*796ba93bSEd Tanous     boost::asio::ip::tcp::acceptor acceptor;
36*796ba93bSEd Tanous     HttpType httpType;
37*796ba93bSEd Tanous };
38*796ba93bSEd Tanous 
3904e438cbSEd Tanous template <typename Handler, typename Adaptor = boost::asio::ip::tcp::socket>
4004e438cbSEd Tanous class Server
4104e438cbSEd Tanous {
423281bcf1SEd Tanous     using self_t = Server<Handler, Adaptor>;
433281bcf1SEd Tanous 
4404e438cbSEd Tanous   public:
Server(Handler * handlerIn,std::vector<Acceptor> && acceptorsIn)45*796ba93bSEd Tanous     Server(Handler* handlerIn, std::vector<Acceptor>&& acceptorsIn) :
46*796ba93bSEd Tanous         acceptors(std::move(acceptorsIn)),
47*796ba93bSEd Tanous 
48d7857201SEd Tanous         // NOLINTNEXTLINE(misc-include-cleaner)
49*796ba93bSEd Tanous         signals(getIoContext(), SIGINT, SIGTERM, SIGHUP), handler(handlerIn)
5004e438cbSEd Tanous     {}
5104e438cbSEd Tanous 
updateDateStr()5204e438cbSEd Tanous     void updateDateStr()
5304e438cbSEd Tanous     {
5404e438cbSEd Tanous         time_t lastTimeT = time(nullptr);
5504e438cbSEd Tanous         tm myTm{};
5604e438cbSEd Tanous 
5704e438cbSEd Tanous         gmtime_r(&lastTimeT, &myTm);
5804e438cbSEd Tanous 
5904e438cbSEd Tanous         dateStr.resize(100);
600f83707dSEd Tanous         size_t dateStrSz = strftime(dateStr.data(), dateStr.size() - 1,
6189492a15SPatrick Williams                                     "%a, %d %b %Y %H:%M:%S GMT", &myTm);
6204e438cbSEd Tanous         dateStr.resize(dateStrSz);
6304e438cbSEd Tanous     }
6404e438cbSEd Tanous 
run()6504e438cbSEd Tanous     void run()
6604e438cbSEd Tanous     {
6704e438cbSEd Tanous         loadCertificate();
6804e438cbSEd Tanous         updateDateStr();
6904e438cbSEd Tanous 
7004e438cbSEd Tanous         getCachedDateStr = [this]() -> std::string {
7104e438cbSEd Tanous             static std::chrono::time_point<std::chrono::steady_clock>
7204e438cbSEd Tanous                 lastDateUpdate = std::chrono::steady_clock::now();
7304e438cbSEd Tanous             if (std::chrono::steady_clock::now() - lastDateUpdate >=
7404e438cbSEd Tanous                 std::chrono::seconds(10))
7504e438cbSEd Tanous             {
7604e438cbSEd Tanous                 lastDateUpdate = std::chrono::steady_clock::now();
7704e438cbSEd Tanous                 updateDateStr();
7804e438cbSEd Tanous             }
7921b4aba4SEd Tanous             return dateStr;
8004e438cbSEd Tanous         };
8104e438cbSEd Tanous 
82*796ba93bSEd Tanous         for (const Acceptor& accept : acceptors)
83*796ba93bSEd Tanous         {
84*796ba93bSEd Tanous             BMCWEB_LOG_INFO(
85*796ba93bSEd Tanous                 "bmcweb server is running, local endpoint {}",
86*796ba93bSEd Tanous                 accept.acceptor.local_endpoint().address().to_string());
87*796ba93bSEd Tanous         }
8804e438cbSEd Tanous         startAsyncWaitForSignal();
8904e438cbSEd Tanous         doAccept();
9004e438cbSEd Tanous     }
9104e438cbSEd Tanous 
loadCertificate()9204e438cbSEd Tanous     void loadCertificate()
9304e438cbSEd Tanous     {
9425b54dbaSEd Tanous         if constexpr (BMCWEB_INSECURE_DISABLE_SSL)
958db83747SEd Tanous         {
968db83747SEd Tanous             return;
978db83747SEd Tanous         }
98d5fb584aSAbhilash Raju 
99*796ba93bSEd Tanous         adaptorCtx = ensuressl::getSslServerContext();
10004e438cbSEd Tanous     }
10104e438cbSEd Tanous 
startAsyncWaitForSignal()10204e438cbSEd Tanous     void startAsyncWaitForSignal()
10304e438cbSEd Tanous     {
104002d39b4SEd Tanous         signals.async_wait(
105002d39b4SEd Tanous             [this](const boost::system::error_code& ec, int signalNo) {
10604e438cbSEd Tanous                 if (ec)
10704e438cbSEd Tanous                 {
10862598e31SEd Tanous                     BMCWEB_LOG_INFO("Error in signal handler{}", ec.message());
10904e438cbSEd Tanous                 }
11004e438cbSEd Tanous                 else
11104e438cbSEd Tanous                 {
11204e438cbSEd Tanous                     if (signalNo == SIGHUP)
11304e438cbSEd Tanous                     {
11462598e31SEd Tanous                         BMCWEB_LOG_INFO("Receivied reload signal");
11504e438cbSEd Tanous                         loadCertificate();
11621b4aba4SEd Tanous                         startAsyncWaitForSignal();
11704e438cbSEd Tanous                     }
11804e438cbSEd Tanous                     else
11904e438cbSEd Tanous                     {
120*796ba93bSEd Tanous                         getIoContext().stop();
12104e438cbSEd Tanous                     }
12204e438cbSEd Tanous                 }
12304e438cbSEd Tanous             });
12404e438cbSEd Tanous     }
12504e438cbSEd Tanous 
126*796ba93bSEd Tanous     using SocketPtr = std::unique_ptr<Adaptor>;
12704e438cbSEd Tanous 
afterAccept(SocketPtr socket,HttpType httpType,const boost::system::error_code & ec)128*796ba93bSEd Tanous     void afterAccept(SocketPtr socket, HttpType httpType,
129*796ba93bSEd Tanous                      const boost::system::error_code& ec)
13004e438cbSEd Tanous     {
1313281bcf1SEd Tanous         if (ec)
1328db83747SEd Tanous         {
1333281bcf1SEd Tanous             BMCWEB_LOG_ERROR("Failed to accept socket {}", ec);
1348db83747SEd Tanous             return;
1358db83747SEd Tanous         }
1363281bcf1SEd Tanous 
137*796ba93bSEd Tanous         boost::asio::steady_timer timer(getIoContext());
1388db83747SEd Tanous         if (adaptorCtx == nullptr)
1398db83747SEd Tanous         {
140*796ba93bSEd Tanous             adaptorCtx = std::make_shared<boost::asio::ssl::context>(
141*796ba93bSEd Tanous                 boost::asio::ssl::context::tls_server);
14288e1612bSEd Tanous         }
1433281bcf1SEd Tanous 
144*796ba93bSEd Tanous         boost::asio::ssl::stream<Adaptor> stream(std::move(*socket),
145*796ba93bSEd Tanous                                                  *adaptorCtx);
146*796ba93bSEd Tanous         using ConnectionType = Connection<Adaptor, Handler>;
147*796ba93bSEd Tanous         auto connection = std::make_shared<ConnectionType>(
148*796ba93bSEd Tanous             handler, httpType, std::move(timer), getCachedDateStr,
149*796ba93bSEd Tanous             std::move(stream));
150*796ba93bSEd Tanous 
151*796ba93bSEd Tanous         boost::asio::post(getIoContext(),
152*796ba93bSEd Tanous                           [connection] { connection->start(); });
1533281bcf1SEd Tanous 
15404e438cbSEd Tanous         doAccept();
1553281bcf1SEd Tanous     }
1563281bcf1SEd Tanous 
doAccept()1573281bcf1SEd Tanous     void doAccept()
1583281bcf1SEd Tanous     {
159*796ba93bSEd Tanous         SocketPtr socket = std::make_unique<Adaptor>(getIoContext());
1603281bcf1SEd Tanous         // Keep a raw pointer so when the socket is moved, the pointer is still
1613281bcf1SEd Tanous         // valid
162*796ba93bSEd Tanous         Adaptor* socketPtr = socket.get();
163*796ba93bSEd Tanous         for (Acceptor& accept : acceptors)
164*796ba93bSEd Tanous         {
165*796ba93bSEd Tanous             accept.acceptor.async_accept(
1663281bcf1SEd Tanous                 *socketPtr,
167*796ba93bSEd Tanous                 std::bind_front(&self_t::afterAccept, this, std::move(socket),
168*796ba93bSEd Tanous                                 accept.httpType));
169*796ba93bSEd Tanous         }
17004e438cbSEd Tanous     }
17104e438cbSEd Tanous 
17204e438cbSEd Tanous   private:
17304e438cbSEd Tanous     std::function<std::string()> getCachedDateStr;
174*796ba93bSEd Tanous     std::vector<Acceptor> acceptors;
17504e438cbSEd Tanous     boost::asio::signal_set signals;
17604e438cbSEd Tanous 
17704e438cbSEd Tanous     std::string dateStr;
17804e438cbSEd Tanous 
17904e438cbSEd Tanous     Handler* handler;
18004e438cbSEd Tanous 
18104e438cbSEd Tanous     std::shared_ptr<boost::asio::ssl::context> adaptorCtx;
1825dfb5b2dSEd Tanous };
18304e438cbSEd Tanous } // namespace crow
184