Coverage Report

Created: 2026-10-28 06:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/bmcweb/http/http_server.hpp
Line
Count
Source
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: Copyright OpenBMC Authors
3
#pragma once
4
5
#include "bmcweb_config.h"
6
7
#include "http_connect_types.hpp"
8
#include "http_connection.hpp"
9
#include "io_context_singleton.hpp"
10
#include "logging.hpp"
11
#include "ssl_key_handler.hpp"
12
13
#include <boost/asio/ip/address.hpp>
14
#include <boost/asio/ip/tcp.hpp>
15
#include <boost/asio/signal_set.hpp>
16
#include <boost/asio/ssl/context.hpp>
17
#include <boost/asio/ssl/stream.hpp>
18
#include <boost/asio/steady_timer.hpp>
19
20
#include <chrono>
21
#include <csignal>
22
#include <cstddef>
23
#include <functional>
24
#include <memory>
25
#include <string>
26
#include <utility>
27
#include <vector>
28
29
namespace crow
30
{
31
32
struct Acceptor
33
{
34
    boost::asio::ip::tcp::acceptor acceptor;
35
    HttpType httpType;
36
};
37
38
template <typename Handler, typename Adaptor = boost::asio::ip::tcp::socket>
39
class Server
40
{
41
    using self_t = Server<Handler, Adaptor>;
42
43
  public:
44
    Server(Handler* handlerIn, std::vector<Acceptor>&& acceptorsIn) :
45
        getCachedDateStr(std::bind_front(&self_t::getCachedDateStrImpl, this)),
46
        acceptors(std::move(acceptorsIn)),
47
        // NOLINTNEXTLINE(misc-include-cleaner)
48
        signals(getIoContext(), SIGINT, SIGTERM, SIGHUP), handler(handlerIn)
49
    {}
50
51
    std::string getCachedDateStrImpl()
52
0
    {
53
0
        std::chrono::steady_clock::time_point now =
54
0
            std::chrono::steady_clock::now();
55
0
        if (now - lastDateUpdate >= std::chrono::seconds(10))
56
0
        {
57
0
            lastDateUpdate = now;
58
0
            using std::chrono::floor;
59
0
            using std::chrono::seconds;
60
0
            using std::chrono::system_clock;
61
0
            std::chrono::time_point<system_clock, seconds> systemNow =
62
0
                floor<seconds>(system_clock::now());
63
0
            dateStr = std::format("{:%a, %d %b %Y %H:%M:%S GMT}", systemNow);
64
0
        }
65
0
        return dateStr;
66
0
    }
67
68
    void run()
69
0
    {
70
0
        loadCertificate();
71
0
72
0
        for (const Acceptor& accept : acceptors)
73
0
        {
74
0
            BMCWEB_LOG_INFO(
75
0
                "bmcweb server is running, local endpoint {}",
76
0
                accept.acceptor.local_endpoint().address().to_string());
77
0
        }
78
0
        startAsyncWaitForSignal();
79
0
        doAccept();
80
0
    }
81
82
    void loadCertificate()
83
0
    {
84
        if constexpr (BMCWEB_INSECURE_DISABLE_SSL)
85
0
        {
86
0
            return;
87
0
        }
88
89
0
        adaptorCtx = ensuressl::getSslServerContext();
90
0
    }
91
92
    void afterWaitForSignal(const boost::system::error_code& ec, int signalNo)
93
0
    {
94
0
        if (ec)
95
0
        {
96
0
            BMCWEB_LOG_INFO("Error in signal handler{}", ec.message());
97
0
        }
98
0
        else
99
0
        {
100
0
            if (signalNo == SIGHUP)
101
0
            {
102
0
                BMCWEB_LOG_INFO("Received reload signal");
103
0
                loadCertificate();
104
0
                startAsyncWaitForSignal();
105
0
            }
106
0
            else
107
0
            {
108
0
                getIoContext().stop();
109
0
            }
110
0
        }
111
0
    }
112
113
    void startAsyncWaitForSignal()
114
0
    {
115
0
        signals.async_wait(std::bind_front(&self_t::afterWaitForSignal, this));
116
0
    }
117
118
    using SocketPtr = std::unique_ptr<Adaptor>;
119
120
    void afterAccept(Acceptor* acceptor, SocketPtr socket, HttpType httpType,
121
                     const boost::system::error_code& ec)
122
0
    {
123
0
        if (ec)
124
0
        {
125
0
            BMCWEB_LOG_ERROR("Failed to accept socket {}", ec);
126
0
            return;
127
0
        }
128
0
129
0
        boost::asio::steady_timer timer(getIoContext());
130
0
        if (adaptorCtx == nullptr)
131
0
        {
132
0
            adaptorCtx = std::make_shared<boost::asio::ssl::context>(
133
0
                boost::asio::ssl::context::tls_server);
134
0
        }
135
0
136
0
        boost::asio::ssl::stream<Adaptor> stream(std::move(*socket),
137
0
                                                 *adaptorCtx);
138
0
        using ConnectionType = Connection<Adaptor, Handler>;
139
0
        auto connection = std::make_shared<ConnectionType>(
140
0
            handler, httpType, std::move(timer), getCachedDateStr,
141
0
            std::move(stream));
142
0
143
0
        boost::asio::post(getIoContext(),
144
0
                          [connection] { connection->start(); });
145
0
        doAcceptOne(*acceptor);
146
0
    }
147
    void doAcceptOne(Acceptor& acceptor)
148
0
    {
149
0
        SocketPtr socket = std::make_unique<Adaptor>(getIoContext());
150
0
        Adaptor* socketPtr = socket.get();
151
0
        acceptor.acceptor.async_accept(
152
0
            *socketPtr, std::bind_front(&self_t::afterAccept, this, &acceptor,
153
0
                                        std::move(socket), acceptor.httpType));
154
0
    }
155
156
    void doAccept()
157
0
    {
158
0
        for (Acceptor& accept : acceptors)
159
0
        {
160
0
            doAcceptOne(accept);
161
0
        }
162
0
    }
163
164
  private:
165
    std::function<std::string()> getCachedDateStr;
166
    std::vector<Acceptor> acceptors;
167
    boost::asio::signal_set signals;
168
169
    std::string dateStr;
170
    std::chrono::steady_clock::time_point lastDateUpdate;
171
172
    Handler* handler;
173
174
    std::shared_ptr<boost::asio::ssl::context> adaptorCtx;
175
};
176
} // namespace crow