Coverage Report

Created: 2026-01-30 17:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/bitcoin/src/httpserver.cpp
Line
Count
Source
1
// Copyright (c) 2015-present The Bitcoin Core developers
2
// Distributed under the MIT software license, see the accompanying
3
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5
#include <bitcoin-build-config.h> // IWYU pragma: keep
6
7
#include <httpserver.h>
8
9
#include <chainparamsbase.h>
10
#include <common/args.h>
11
#include <common/messages.h>
12
#include <common/url.h>
13
#include <compat/compat.h>
14
#include <logging.h>
15
#include <netbase.h>
16
#include <node/interface_ui.h>
17
#include <rpc/protocol.h>
18
#include <span.h>
19
#include <sync.h>
20
#include <util/check.h>
21
#include <util/signalinterrupt.h>
22
#include <util/sock.h>
23
#include <util/strencodings.h>
24
#include <util/thread.h>
25
#include <util/threadnames.h>
26
#include <util/time.h>
27
#include <util/translation.h>
28
29
#include <condition_variable>
30
#include <cstdio>
31
#include <cstdlib>
32
#include <deque>
33
#include <memory>
34
#include <optional>
35
#include <span>
36
#include <string>
37
#include <thread>
38
#include <unordered_map>
39
#include <vector>
40
41
#include <sys/types.h>
42
#include <sys/stat.h>
43
44
//! The set of sockets cannot be modified while waiting, so
45
//! the sleep time needs to be small to avoid new sockets stalling.
46
static constexpr auto SELECT_TIMEOUT{50ms};
47
48
//! Explicit alias for setting socket option methods.
49
static constexpr int SOCKET_OPTION_TRUE{1};
50
51
using common::InvalidPortErrMsg;
52
using http_bitcoin::HTTPRequest;
53
54
/** HTTP request work item */
55
class HTTPWorkItem final : public HTTPClosure
56
{
57
public:
58
    HTTPWorkItem(std::unique_ptr<HTTPRequest> _req, const std::string &_path, const HTTPRequestHandler& _func):
59
1.43k
        req(std::move(_req)), path(_path), func(_func)
60
1.43k
    {
61
1.43k
    }
62
    void operator()() override
63
1.43k
    {
64
1.43k
        func(req.get(), path);
65
1.43k
    }
66
67
    std::unique_ptr<HTTPRequest> req;
68
69
private:
70
    std::string path;
71
    HTTPRequestHandler func;
72
};
73
74
/** Simple work queue for distributing work over multiple threads.
75
 * Work items are simply callable objects.
76
 */
77
template <typename WorkItem>
78
class WorkQueue
79
{
80
private:
81
    Mutex cs;
82
    std::condition_variable cond GUARDED_BY(cs);
83
    std::deque<std::unique_ptr<WorkItem>> queue GUARDED_BY(cs);
84
    bool running GUARDED_BY(cs){true};
85
    const size_t maxDepth;
86
87
public:
88
280
    explicit WorkQueue(size_t _maxDepth) : maxDepth(_maxDepth)
89
280
    {
90
280
    }
91
    /** Precondition: worker threads have all stopped (they have been joined).
92
     */
93
0
    ~WorkQueue() = default;
94
    /** Enqueue a work item */
95
    bool Enqueue(WorkItem* item) EXCLUSIVE_LOCKS_REQUIRED(!cs)
96
1.43k
    {
97
1.43k
        LOCK(cs);
98
1.43k
        if (!running || queue.size() >= maxDepth) {
  Branch (98:13): [True: 0, False: 1.43k]
  Branch (98:25): [True: 0, False: 1.43k]
99
0
            return false;
100
0
        }
101
1.43k
        queue.emplace_back(std::unique_ptr<WorkItem>(item));
102
1.43k
        cond.notify_one();
103
1.43k
        return true;
104
1.43k
    }
105
    /** Thread function */
106
    void Run() EXCLUSIVE_LOCKS_REQUIRED(!cs)
107
1.12k
    {
108
2.55k
        while (true) {
  Branch (108:16): [Folded - Ignored]
109
2.55k
            std::unique_ptr<WorkItem> i;
110
2.55k
            {
111
2.55k
                WAIT_LOCK(cs, lock);
112
4.84k
                while (running && queue.empty())
  Branch (112:24): [True: 3.72k, False: 1.12k]
  Branch (112:35): [True: 2.29k, False: 1.43k]
113
2.29k
                    cond.wait(lock);
114
2.55k
                if (!running && queue.empty())
  Branch (114:21): [True: 1.12k, False: 1.43k]
  Branch (114:33): [True: 1.12k, False: 0]
115
1.12k
                    break;
116
1.43k
                i = std::move(queue.front());
117
1.43k
                queue.pop_front();
118
1.43k
            }
119
0
            (*i)();
120
1.43k
        }
121
1.12k
    }
122
    /** Interrupt and exit loops */
123
    void Interrupt() EXCLUSIVE_LOCKS_REQUIRED(!cs)
124
280
    {
125
280
        LOCK(cs);
126
280
        running = false;
127
280
        cond.notify_all();
128
280
    }
129
};
130
131
struct HTTPPathHandler
132
{
133
    HTTPPathHandler(std::string _prefix, bool _exactMatch, HTTPRequestHandler _handler):
134
560
        prefix(_prefix), exactMatch(_exactMatch), handler(_handler)
135
560
    {
136
560
    }
137
    std::string prefix;
138
    bool exactMatch;
139
    HTTPRequestHandler handler;
140
};
141
142
/** HTTP module state */
143
144
static std::unique_ptr<http_bitcoin::HTTPServer> g_http_server{nullptr};
145
//! List of subnets to allow RPC connections from
146
static std::vector<CSubNet> rpc_allow_subnets;
147
//! Work queue for handling longer requests off the event loop thread
148
static std::unique_ptr<WorkQueue<HTTPClosure>> g_work_queue{nullptr};
149
//! Handlers for (sub)paths
150
static GlobalMutex g_httppathhandlers_mutex;
151
static std::vector<HTTPPathHandler> pathHandlers GUARDED_BY(g_httppathhandlers_mutex);
152
153
/** Check if a network address is allowed to access the HTTP server */
154
static bool ClientAllowed(const CNetAddr& netaddr)
155
1.43k
{
156
1.43k
    if (!netaddr.IsValid())
  Branch (156:9): [True: 0, False: 1.43k]
157
0
        return false;
158
1.43k
    for(const CSubNet& subnet : rpc_allow_subnets)
  Branch (158:31): [True: 1.43k, False: 0]
159
1.43k
        if (subnet.Match(netaddr))
  Branch (159:13): [True: 1.43k, False: 0]
160
1.43k
            return true;
161
0
    return false;
162
1.43k
}
163
164
/** Initialize ACL list for HTTP server */
165
static bool InitHTTPAllowList()
166
280
{
167
280
    rpc_allow_subnets.clear();
168
280
    rpc_allow_subnets.emplace_back(LookupHost("127.0.0.1", false).value(), 8);  // always allow IPv4 local subnet
169
280
    rpc_allow_subnets.emplace_back(LookupHost("::1", false).value());  // always allow IPv6 localhost
170
280
    for (const std::string& strAllow : gArgs.GetArgs("-rpcallowip")) {
  Branch (170:38): [True: 0, False: 280]
171
0
        const CSubNet subnet{LookupSubNet(strAllow)};
172
0
        if (!subnet.IsValid()) {
  Branch (172:13): [True: 0, False: 0]
173
0
            uiInterface.ThreadSafeMessageBox(
174
0
                Untranslated(strprintf("Invalid -rpcallowip subnet specification: %s. Valid values are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0), a network/CIDR (e.g. 1.2.3.4/24), all ipv4 (0.0.0.0/0), or all ipv6 (::/0). RFC4193 is allowed only if -cjdnsreachable=0.", strAllow)),
175
0
                "", CClientUIInterface::MSG_ERROR);
176
0
            return false;
177
0
        }
178
0
        rpc_allow_subnets.push_back(subnet);
179
0
    }
180
280
    std::string strAllowed;
181
280
    for (const CSubNet& subnet : rpc_allow_subnets)
  Branch (181:32): [True: 560, False: 280]
182
560
        strAllowed += subnet.ToString() + " ";
183
280
    LogDebug(BCLog::HTTP, "Allowing HTTP connections from: %s\n", strAllowed);
184
280
    return true;
185
280
}
186
187
/** HTTP request method as string - use for logging only */
188
std::string_view RequestMethodString(HTTPRequestMethod m)
189
1.43k
{
190
1.43k
    switch (m) {
  Branch (190:13): [True: 0, False: 1.43k]
191
0
        using enum HTTPRequestMethod;
192
0
        case GET: return "GET";
  Branch (192:9): [True: 0, False: 1.43k]
193
1.43k
        case POST: return "POST";
  Branch (193:9): [True: 1.43k, False: 0]
194
0
        case HEAD: return "HEAD";
  Branch (194:9): [True: 0, False: 1.43k]
195
0
        case PUT: return "PUT";
  Branch (195:9): [True: 0, False: 1.43k]
196
0
        case UNKNOWN: return "unknown";
  Branch (196:9): [True: 0, False: 1.43k]
197
1.43k
    } // no default case, so the compiler can warn about missing cases
198
1.43k
    assert(false);
  Branch (198:5): [Folded - Ignored]
199
0
}
200
201
static void MaybeDispatchRequestToWorker(std::unique_ptr<HTTPRequest> hreq)
202
1.43k
{
203
    // Early address-based allow check
204
1.43k
    if (!ClientAllowed(hreq->GetPeer())) {
  Branch (204:9): [True: 0, False: 1.43k]
205
0
        LogDebug(BCLog::HTTP, "HTTP request from %s rejected: Client network is not allowed RPC access\n",
206
0
                 hreq->GetPeer().ToStringAddrPort());
207
0
        hreq->WriteReply(HTTP_FORBIDDEN);
208
0
        return;
209
0
    }
210
211
    // Early reject unknown HTTP methods
212
1.43k
    if (hreq->GetRequestMethod() == HTTPRequestMethod::UNKNOWN) {
  Branch (212:9): [True: 0, False: 1.43k]
213
0
        LogDebug(BCLog::HTTP, "HTTP request from %s rejected: Unknown HTTP request method\n",
214
0
                 hreq->GetPeer().ToStringAddrPort());
215
0
        hreq->WriteReply(HTTP_BAD_METHOD);
216
0
        return;
217
0
    }
218
219
    // Find registered handler for prefix
220
1.43k
    std::string strURI = hreq->GetURI();
221
1.43k
    std::string path;
222
1.43k
    LOCK(g_httppathhandlers_mutex);
223
1.43k
    std::vector<HTTPPathHandler>::const_iterator i = pathHandlers.begin();
224
1.43k
    std::vector<HTTPPathHandler>::const_iterator iend = pathHandlers.end();
225
2.55k
    for (; i != iend; ++i) {
  Branch (225:12): [True: 2.55k, False: 0]
226
2.55k
        bool match = false;
227
2.55k
        if (i->exactMatch)
  Branch (227:13): [True: 1.43k, False: 1.12k]
228
1.43k
            match = (strURI == i->prefix);
229
1.12k
        else
230
1.12k
            match = strURI.starts_with(i->prefix);
231
2.55k
        if (match) {
  Branch (231:13): [True: 1.43k, False: 1.12k]
232
1.43k
            path = strURI.substr(i->prefix.size());
233
1.43k
            break;
234
1.43k
        }
235
2.55k
    }
236
237
    // Dispatch to worker thread
238
1.43k
    if (i != iend) {
  Branch (238:9): [True: 1.43k, False: 0]
239
1.43k
        std::unique_ptr<HTTPWorkItem> item(new HTTPWorkItem(std::move(hreq), path, i->handler));
240
1.43k
        assert(g_work_queue);
  Branch (240:9): [True: 1.43k, False: 0]
241
1.43k
        if (g_work_queue->Enqueue(item.get())) {
  Branch (241:13): [True: 1.43k, False: 0]
242
1.43k
            (void)item.release(); /* if true, queue took ownership */
243
1.43k
        } else {
244
0
            LogWarning("Request rejected because http work queue depth exceeded, it can be increased with the -rpcworkqueue= setting");
245
0
            item->req->WriteReply(HTTP_SERVICE_UNAVAILABLE, "Work queue depth exceeded");
246
0
        }
247
1.43k
    } else {
248
0
        hreq->WriteReply(HTTP_NOT_FOUND);
249
0
    }
250
1.43k
}
251
252
static void RejectAllRequests(std::unique_ptr<http_bitcoin::HTTPRequest> hreq)
253
0
{
254
0
    LogDebug(BCLog::HTTP, "Rejecting request while shutting down\n");
255
0
    hreq->WriteReply(HTTP_SERVICE_UNAVAILABLE);
256
0
}
257
258
static std::vector<std::pair<std::string, uint16_t>> GetBindAddresses()
259
280
{
260
280
    uint16_t http_port{static_cast<uint16_t>(gArgs.GetIntArg("-rpcport", BaseParams().RPCPort()))};
261
280
    std::vector<std::pair<std::string, uint16_t>> endpoints;
262
263
    // Determine what addresses to bind to
264
    // To prevent misconfiguration and accidental exposure of the RPC
265
    // interface, require -rpcallowip and -rpcbind to both be specified
266
    // together. If either is missing, ignore both values, bind to localhost
267
    // instead, and log warnings.
268
280
    if (gArgs.GetArgs("-rpcallowip").empty() || gArgs.GetArgs("-rpcbind").empty()) { // Default to loopback if not allowing external IPs
  Branch (268:9): [True: 280, False: 0]
  Branch (268:9): [True: 280, False: 0]
  Branch (268:49): [True: 0, False: 0]
269
280
        endpoints.emplace_back("::1", http_port);
270
280
        endpoints.emplace_back("127.0.0.1", http_port);
271
280
        if (!gArgs.GetArgs("-rpcallowip").empty()) {
  Branch (271:13): [True: 0, False: 280]
272
0
            LogWarning("Option -rpcallowip was specified without -rpcbind; this doesn't usually make sense");
273
0
        }
274
280
        if (!gArgs.GetArgs("-rpcbind").empty()) {
  Branch (274:13): [True: 0, False: 280]
275
0
            LogWarning("Option -rpcbind was ignored because -rpcallowip was not specified, refusing to allow everyone to connect");
276
0
        }
277
280
    } else { // Specific bind addresses
278
0
        for (const std::string& strRPCBind : gArgs.GetArgs("-rpcbind")) {
  Branch (278:44): [True: 0, False: 0]
279
0
            uint16_t port{http_port};
280
0
            std::string host;
281
0
            if (!SplitHostPort(strRPCBind, port, host)) {
  Branch (281:17): [True: 0, False: 0]
282
0
                LogError("%s\n", InvalidPortErrMsg("-rpcbind", strRPCBind).original);
283
0
                return {}; // empty
284
0
            }
285
0
            endpoints.emplace_back(host, port);
286
0
        }
287
0
    }
288
280
    return endpoints;
289
280
}
290
291
/** Simple wrapper to set thread name and run work queue */
292
static void HTTPWorkQueueRun(WorkQueue<HTTPClosure>* queue, int worker_num)
293
1.12k
{
294
1.12k
    util::ThreadRename(strprintf("httpworker.%i", worker_num));
295
1.12k
    queue->Run();
296
1.12k
}
297
298
void RegisterHTTPHandler(const std::string &prefix, bool exactMatch, const HTTPRequestHandler &handler)
299
560
{
300
560
    LogDebug(BCLog::HTTP, "Registering HTTP handler for %s (exactmatch %d)\n", prefix, exactMatch);
301
560
    LOCK(g_httppathhandlers_mutex);
302
560
    pathHandlers.emplace_back(prefix, exactMatch, handler);
303
560
}
304
305
void UnregisterHTTPHandler(const std::string &prefix, bool exactMatch)
306
4.48k
{
307
4.48k
    LOCK(g_httppathhandlers_mutex);
308
4.48k
    std::vector<HTTPPathHandler>::iterator i = pathHandlers.begin();
309
4.48k
    std::vector<HTTPPathHandler>::iterator iend = pathHandlers.end();
310
4.48k
    for (; i != iend; ++i)
  Branch (310:12): [True: 560, False: 3.92k]
311
560
        if (i->prefix == prefix && i->exactMatch == exactMatch)
  Branch (311:13): [True: 560, False: 0]
  Branch (311:36): [True: 560, False: 0]
312
560
            break;
313
4.48k
    if (i != iend)
  Branch (313:9): [True: 560, False: 3.92k]
314
560
    {
315
560
        LogDebug(BCLog::HTTP, "Unregistering HTTP handler for %s (exactmatch %d)\n", prefix, exactMatch);
316
560
        pathHandlers.erase(i);
317
560
    }
318
4.48k
}
319
320
namespace http_bitcoin {
321
using util::Split;
322
323
std::optional<std::string> HTTPHeaders::Find(const std::string& key) const
324
18.5k
{
325
18.5k
    const auto it = m_map.find(key);
326
18.5k
    if (it == m_map.end()) return std::nullopt;
  Branch (326:9): [True: 14.2k, False: 4.30k]
327
4.30k
    return it->second;
328
18.5k
}
329
330
void HTTPHeaders::Write(const std::string& key, const std::string& value)
331
10.8k
{
332
    // If present, append value to list
333
10.8k
    const auto existing_value = Find(key);
334
10.8k
    if (existing_value) {
  Branch (334:9): [True: 0, False: 10.8k]
335
0
        m_map[key] = existing_value.value() + ", " + value;
336
10.8k
    } else {
337
10.8k
        m_map[key] = value;
338
10.8k
    }
339
10.8k
}
340
341
void HTTPHeaders::Remove(const std::string& key)
342
0
{
343
0
    m_map.erase(key);
344
0
}
345
346
bool HTTPHeaders::Read(util::LineReader& reader)
347
1.43k
{
348
    // Headers https://httpwg.org/specs/rfc9110.html#rfc.section.6.3
349
    // A sequence of Field Lines https://httpwg.org/specs/rfc9110.html#rfc.section.5.2
350
7.17k
    do {
351
7.17k
        auto maybe_line = reader.ReadLine();
352
7.17k
        if (!maybe_line) return false;
  Branch (352:13): [True: 0, False: 7.17k]
353
7.17k
        const std::string& line = *maybe_line;
354
355
        // An empty line indicates end of the headers section https://www.rfc-editor.org/rfc/rfc2616#section-4
356
7.17k
        if (line.length() == 0) break;
  Branch (356:13): [True: 1.43k, False: 5.73k]
357
358
        // Header line must have at least one ":"
359
        // keys are not allowed to have delimiters like ":" but values are
360
        // https://httpwg.org/specs/rfc9110.html#rfc.section.5.6.2
361
5.73k
        const size_t pos{line.find(':')};
362
5.73k
        if (pos == std::string::npos) throw std::runtime_error("HTTP header missing colon (:)");
  Branch (362:13): [True: 0, False: 5.73k]
363
364
        // Whitespace is optional
365
5.73k
        std::string key = util::TrimString(std::string_view(line).substr(0, pos));
366
5.73k
        std::string value = util::TrimString(std::string_view(line).substr(pos + 1));
367
5.73k
        Write(key, value);
368
5.73k
    } while (true);
  Branch (368:14): [Folded - Ignored]
369
370
1.43k
    return true;
371
1.43k
}
372
373
std::string HTTPHeaders::Stringify() const
374
1.70k
{
375
1.70k
    std::string out;
376
5.10k
    for (const auto& [key, value] : m_map) {
  Branch (376:35): [True: 5.10k, False: 1.70k]
377
5.10k
        out += key + ": " + value + "\r\n";
378
5.10k
    }
379
380
    // Headers are terminated by an empty line
381
1.70k
    out += "\r\n";
382
383
1.70k
    return out;
384
1.70k
}
385
386
std::string HTTPResponse::StringifyHeaders() const
387
1.70k
{
388
1.70k
    return strprintf("HTTP/%d.%d %d %s\r\n%s", m_version_major, m_version_minor, m_status, m_reason, m_headers.Stringify());
389
1.70k
}
390
391
bool HTTPRequest::LoadControlData(LineReader& reader)
392
192M
{
393
192M
    auto maybe_line = reader.ReadLine();
394
192M
    if (!maybe_line) return false;
  Branch (394:9): [True: 192M, False: 1.70k]
395
1.70k
    const std::string& request_line = *maybe_line;
396
397
    // Request Line aka Control Data https://httpwg.org/specs/rfc9110.html#rfc.section.6.2
398
    // Three words separated by spaces, terminated by \n or \r\n
399
1.70k
    if (request_line.length() < MIN_REQUEST_LINE_LENGTH) throw std::runtime_error("HTTP request line too short");
  Branch (399:9): [True: 69, False: 1.63k]
400
401
1.63k
    const std::vector<std::string_view> parts{Split<std::string_view>(request_line, " ")};
402
1.63k
    if (parts.size() != 3) throw std::runtime_error("HTTP request line malformed");
  Branch (402:9): [True: 116, False: 1.51k]
403
404
1.51k
    if (parts[0] == "GET") {
  Branch (404:9): [True: 0, False: 1.51k]
405
0
        m_method = HTTPRequestMethod::GET;
406
1.51k
    } else if (parts[0] == "POST") {
  Branch (406:16): [True: 1.43k, False: 83]
407
1.43k
        m_method = HTTPRequestMethod::POST;
408
1.43k
    } else if (parts[0] == "HEAD") {
  Branch (408:16): [True: 0, False: 83]
409
0
        m_method = HTTPRequestMethod::HEAD;
410
83
    } else if (parts[0] == "PUT") {
  Branch (410:16): [True: 0, False: 83]
411
0
        m_method = HTTPRequestMethod::PUT;
412
83
    } else {
413
83
        m_method = HTTPRequestMethod::UNKNOWN;
414
83
    }
415
416
1.51k
    m_target = parts[1];
417
418
1.51k
    if (parts[2].rfind("HTTP/") != 0) throw std::runtime_error("HTTP request line malformed");
  Branch (418:9): [True: 69, False: 1.44k]
419
1.44k
    const std::vector<std::string_view> version_parts{Split<std::string_view>(parts[2].substr(5), ".")};
420
1.44k
    if (version_parts.size() != 2) throw std::runtime_error("HTTP request line malformed");
  Branch (420:9): [True: 0, False: 1.44k]
421
1.44k
    auto major = ToIntegral<int>(version_parts[0]);
422
1.44k
    auto minor = ToIntegral<int>(version_parts[1]);
423
1.44k
    if (!major || !minor) throw std::runtime_error("HTTP request line malformed");
  Branch (423:9): [True: 14, False: 1.43k]
  Branch (423:19): [True: 0, False: 1.43k]
424
1.44k
    m_version_major = major.value();
425
1.44k
    m_version_minor = minor.value();
426
427
1.44k
    return true;
428
1.44k
}
429
430
bool HTTPRequest::LoadHeaders(LineReader& reader)
431
1.43k
{
432
1.43k
    return m_headers.Read(reader);
433
1.43k
}
434
435
bool HTTPRequest::LoadBody(LineReader& reader)
436
1.43k
{
437
    // https://httpwg.org/specs/rfc9112.html#message.body
438
439
1.43k
    auto transfer_encoding_header = m_headers.Find("Transfer-Encoding");
440
1.43k
    if (transfer_encoding_header && ToLower(transfer_encoding_header.value()) == "chunked") {
  Branch (440:9): [True: 0, False: 1.43k]
  Branch (440:9): [True: 0, False: 1.43k]
  Branch (440:37): [True: 0, False: 0]
441
        // Transfer-Encoding: https://datatracker.ietf.org/doc/html/rfc7230.html#section-3.3.1
442
        // Chunked Transfer Coding: https://datatracker.ietf.org/doc/html/rfc7230.html#section-4.1
443
        // see evhttp_handle_chunked_read() in libevent http.c
444
0
        while (reader.Remaining() > 0) {
  Branch (444:16): [True: 0, False: 0]
445
0
            auto maybe_chunk_size = reader.ReadLine();
446
0
            if (!maybe_chunk_size) return false;
  Branch (446:17): [True: 0, False: 0]
447
448
0
            const auto chunk_size{ToIntegral<uint64_t>(maybe_chunk_size.value(), /*base=*/16)};
449
0
            if (!chunk_size) throw std::runtime_error("Cannot parse chunk length value");
  Branch (449:17): [True: 0, False: 0]
450
451
0
            bool last_chunk{*chunk_size == 0};
452
453
0
            if (!last_chunk) {
  Branch (453:17): [True: 0, False: 0]
454
                // We are still expecting more data for this chunk
455
0
                if (reader.Remaining() < *chunk_size) {
  Branch (455:21): [True: 0, False: 0]
456
0
                    return false;
457
0
                }
458
                // Pack chunk onto body
459
0
                m_body += reader.ReadLength(*chunk_size);
460
0
            }
461
462
            // Even though every chunk size is explicitly declared,
463
            // they are still terminated by a CRLF we don't need.
464
0
            auto crlf = reader.ReadLine();
465
0
            if (!crlf || !crlf.value().empty()) throw std::runtime_error("Improperly terminated chunk");
  Branch (465:17): [True: 0, False: 0]
  Branch (465:26): [True: 0, False: 0]
466
467
0
            if (last_chunk) return true;
  Branch (467:17): [True: 0, False: 0]
468
0
        }
469
470
        // We read all the chunks but never got the last chunk, wait for client to send more
471
0
        return false;
472
1.43k
    } else {
473
        // No Content-length or Transfer-Encoding header means no body, see libevent evhttp_get_body()
474
1.43k
        auto content_length_value{m_headers.Find("Content-Length")};
475
1.43k
        if (!content_length_value) return true;
  Branch (475:13): [True: 0, False: 1.43k]
476
477
1.43k
        const auto content_length{ToIntegral<uint64_t>(content_length_value.value())};
478
1.43k
        if (!content_length) throw std::runtime_error("Cannot parse Content-Length value");
  Branch (478:13): [True: 0, False: 1.43k]
479
480
        // Not enough data in buffer for expected body
481
1.43k
        if (reader.Remaining() < *content_length) return false;
  Branch (481:13): [True: 0, False: 1.43k]
482
483
1.43k
        m_body = reader.ReadLength(*content_length);
484
485
1.43k
        return true;
486
1.43k
    }
487
1.43k
}
488
489
void HTTPRequest::WriteReply(HTTPStatusCode status, std::span<const std::byte> reply_body)
490
1.70k
{
491
1.70k
    HTTPResponse res;
492
493
    // Some response headers are determined in advance and stored in the request
494
1.70k
    res.m_headers = std::move(m_response_headers);
495
496
    // Response version matches request version
497
1.70k
    res.m_version_major = m_version_major;
498
1.70k
    res.m_version_minor = m_version_minor;
499
500
    // Add response code and look up reason string
501
1.70k
    res.m_status = status;
502
1.70k
    res.m_reason = HTTPReason.find(status)->second;
503
504
    // See libevent evhttp_response_needs_body()
505
    // Response headers are different if no body is needed
506
1.70k
    bool needs_body{status != HTTP_NO_CONTENT && (status < 100 || status >= 200)};
  Branch (506:21): [True: 1.70k, False: 0]
  Branch (506:51): [True: 0, False: 1.70k]
  Branch (506:67): [True: 1.70k, False: 0]
507
508
    // See libevent evhttp_make_header_response()
509
    // Expected response headers depend on protocol version
510
1.70k
    if (m_version_major == 1) {
  Branch (510:9): [True: 1.70k, False: 0]
511
        // HTTP/1.0
512
1.70k
        if (m_version_minor == 0) {
  Branch (512:13): [True: 0, False: 1.70k]
513
0
            auto connection_header{m_headers.Find("Connection")};
514
0
            if (connection_header && ToLower(connection_header.value()) == "keep-alive") {
  Branch (514:17): [True: 0, False: 0]
  Branch (514:17): [True: 0, False: 0]
  Branch (514:38): [True: 0, False: 0]
515
0
                res.m_headers.Write("Connection", "keep-alive");
516
0
                res.m_keep_alive = true;
517
0
            }
518
0
        }
519
520
        // HTTP/1.1
521
1.70k
        if (m_version_minor >= 1) {
  Branch (521:13): [True: 1.70k, False: 0]
522
1.70k
            const int64_t now_seconds{TicksSinceEpoch<std::chrono::seconds>(NodeClock::now())};
523
1.70k
            res.m_headers.Write("Date", FormatRFC1123DateTime(now_seconds));
524
525
1.70k
            if (needs_body) {
  Branch (525:17): [True: 1.70k, False: 0]
526
1.70k
                res.m_headers.Write("Content-Length", strprintf("%d", reply_body.size()));
527
1.70k
            }
528
529
            // Default for HTTP/1.1
530
1.70k
            res.m_keep_alive = true;
531
1.70k
        }
532
1.70k
    }
533
534
1.70k
    if (needs_body && !res.m_headers.Find("Content-Type")) {
  Branch (534:9): [True: 1.70k, False: 0]
  Branch (534:9): [True: 268, False: 1.43k]
  Branch (534:23): [True: 268, False: 1.43k]
535
        // Default type from libevent evhttp_new_object()
536
268
        res.m_headers.Write("Content-Type", "text/html; charset=ISO-8859-1");
537
268
    }
538
539
1.70k
    auto connection_header{m_headers.Find("Connection")};
540
1.70k
    if (connection_header && ToLower(connection_header.value()) == "close") {
  Branch (540:9): [True: 0, False: 1.70k]
  Branch (540:9): [True: 0, False: 1.70k]
  Branch (540:30): [True: 0, False: 0]
541
        // Might not exist already but we need to replace it, not append to it
542
0
        res.m_headers.Remove("Connection");
543
0
        res.m_headers.Write("Connection", "close");
544
0
        res.m_keep_alive = false;
545
0
    }
546
547
1.70k
    m_client->m_keep_alive = res.m_keep_alive;
548
549
    // Serialize the response headers
550
1.70k
    const std::string headers{res.StringifyHeaders()};
551
1.70k
    const auto headers_bytes{std::as_bytes(std::span(headers.begin(), headers.end()))};
552
553
1.70k
    bool send_buffer_was_empty{false};
554
    // Fill the send buffer with the complete serialized response headers + body
555
1.70k
    {
556
1.70k
        LOCK(m_client->m_send_mutex);
557
1.70k
        send_buffer_was_empty = m_client->m_send_buffer.empty();
558
1.70k
        m_client->m_send_buffer.insert(m_client->m_send_buffer.end(), headers_bytes.begin(), headers_bytes.end());
559
560
        // We've been using std::span up until now but it is finally time to copy
561
        // data. The original data will go out of scope when WriteReply() returns.
562
        // This is analogous to the memcpy() in libevent's evbuffer_add()
563
1.70k
        m_client->m_send_buffer.insert(m_client->m_send_buffer.end(), reply_body.begin(), reply_body.end());
564
1.70k
    }
565
566
1.70k
    LogDebug(
567
1.70k
        BCLog::HTTP,
568
1.70k
        "HTTPResponse (status code: %d size: %lld) added to send buffer for client %s (id=%lld)\n",
569
1.70k
        status,
570
1.70k
        headers_bytes.size() + reply_body.size(),
571
1.70k
        m_client->m_origin,
572
1.70k
        m_client->m_id);
573
574
    // If the send buffer was empty before we wrote this reply, we can try an
575
    // optimistic send akin to CConnman::PushMessage() in which we
576
    // push the data directly out the socket to client right now, instead
577
    // of waiting for the next iteration of the I/O loop.
578
1.70k
    if (send_buffer_was_empty) {
  Branch (578:9): [True: 1.70k, False: 0]
579
1.70k
        m_client->MaybeSendBytesFromBuffer();
580
1.70k
    } else {
581
        // Inform HTTPServer I/O that data is ready to be sent to this client
582
        // in the next loop iteration.
583
0
        m_client->m_send_ready = true;
584
0
    }
585
586
    // Signal to the I/O loop that we are ready to handle the next request.
587
1.70k
    m_client->m_req_busy = false;
588
1.70k
}
589
590
CService HTTPRequest::GetPeer() const
591
2.86k
{
592
2.86k
    return m_client->m_addr;
593
2.86k
}
594
595
std::optional<std::string> HTTPRequest::GetQueryParameter(const std::string& key) const
596
0
{
597
0
    return GetQueryParameterFromUri(GetURI(), key);
598
0
}
599
600
// See libevent http.c evhttp_parse_query_impl()
601
// and https://www.rfc-editor.org/rfc/rfc3986#section-3.4
602
std::optional<std::string> GetQueryParameterFromUri(const std::string& uri, const std::string& key)
603
0
{
604
    // Handle %XX encoding
605
0
    std::string decoded_uri{UrlDecode(uri)};
606
607
    // find query in URI
608
0
    size_t start = decoded_uri.find('?');
609
0
    if (start == std::string::npos) return std::nullopt;
  Branch (609:9): [True: 0, False: 0]
610
0
    size_t end = decoded_uri.find('#', start);
611
0
    if (end == std::string::npos) {
  Branch (611:9): [True: 0, False: 0]
612
0
        end = decoded_uri.length();
613
0
    }
614
0
    const std::string_view query{decoded_uri.data() + start + 1, end - start - 1};
615
    // find requested parameter in query
616
0
    const std::vector<std::string_view> params{Split<std::string_view>(query, "&")};
617
0
    for (const std::string_view& param : params) {
  Branch (617:40): [True: 0, False: 0]
618
0
        size_t delim = param.find('=');
619
0
        if (key == param.substr(0, delim)) {
  Branch (619:13): [True: 0, False: 0]
620
0
            if (delim == std::string::npos) {
  Branch (620:17): [True: 0, False: 0]
621
0
                return "";
622
0
            } else {
623
0
                return std::string(param.substr(delim + 1));
624
0
            }
625
0
        }
626
0
    }
627
0
    return std::nullopt;
628
0
}
629
630
std::pair<bool, std::string> HTTPRequest::GetHeader(const std::string& hdr) const
631
1.43k
{
632
1.43k
    std::optional<std::string> found{m_headers.Find(hdr)};
633
1.43k
    if (found.has_value()) {
  Branch (633:9): [True: 1.43k, False: 0]
634
1.43k
        return std::make_pair(true, found.value());
635
1.43k
    } else
636
0
        return std::make_pair(false, "");
637
1.43k
}
638
639
void HTTPRequest::WriteHeader(const std::string& hdr, const std::string& value)
640
1.43k
{
641
1.43k
    m_response_headers.Write(hdr, value);
642
1.43k
}
643
644
util::Result<void> HTTPServer::BindAndStartListening(const CService& to)
645
560
{
646
    // Create socket for listening for incoming connections
647
560
    sockaddr_storage storage;
648
560
    auto sa = static_cast<sockaddr*>(static_cast<void*>(&storage));
649
560
    socklen_t len{sizeof(storage)};
650
560
    if (!to.GetSockAddr(sa, &len)) {
  Branch (650:9): [True: 0, False: 560]
651
0
        return util::Error{Untranslated(strprintf("Bind address family for %s not supported", to.ToStringAddrPort()))};
652
0
    }
653
654
560
    std::unique_ptr<Sock> sock{CreateSock(to.GetSAFamily(), SOCK_STREAM, IPPROTO_TCP)};
655
560
    if (!sock) {
  Branch (655:9): [True: 0, False: 560]
656
0
        return util::Error{Untranslated(strprintf("Cannot create %s listen socket: %s",
657
0
                                                    to.ToStringAddrPort(),
658
0
                                                    NetworkErrorString(WSAGetLastError())))};
659
0
    }
660
661
560
    int socket_option_true{1};
662
663
    // Allow binding if the port is still in TIME_WAIT state after
664
    // the program was closed and restarted.
665
560
    if (sock->SetSockOpt(SOL_SOCKET, SO_REUSEADDR, &socket_option_true, sizeof(socket_option_true)) == SOCKET_ERROR) {
  Branch (665:9): [True: 0, False: 560]
666
0
        LogDebug(BCLog::HTTP,
667
0
                 "Cannot set SO_REUSEADDR on %s listen socket: %s, continuing anyway",
668
0
                 to.ToStringAddrPort(),
669
0
                 NetworkErrorString(WSAGetLastError()));
670
0
    }
671
672
    // some systems don't have IPV6_V6ONLY but are always v6only; others do have the option
673
    // and enable it by default or not. Try to enable it, if possible.
674
560
    if (to.IsIPv6()) {
  Branch (674:9): [True: 280, False: 280]
675
280
#ifdef IPV6_V6ONLY
676
280
        if (sock->SetSockOpt(IPPROTO_IPV6, IPV6_V6ONLY, &socket_option_true, sizeof(socket_option_true)) == SOCKET_ERROR) {
  Branch (676:13): [True: 0, False: 280]
677
0
            LogDebug(BCLog::HTTP,
678
0
                     "Cannot set IPV6_V6ONLY on %s listen socket: %s, continuing anyway",
679
0
                     to.ToStringAddrPort(),
680
0
                     NetworkErrorString(WSAGetLastError()));
681
0
        }
682
280
#endif
683
#ifdef WIN32
684
        int prot_level{PROTECTION_LEVEL_UNRESTRICTED};
685
        if (sock->SetSockOpt(IPPROTO_IPV6,
686
                             IPV6_PROTECTION_LEVEL,
687
                             &prot_level,
688
                             sizeof(prot_level)) == SOCKET_ERROR) {
689
            LogDebug(BCLog::HTTP,
690
                     "Cannot set IPV6_PROTECTION_LEVEL on %s listen socket: %s, continuing anyway",
691
                     to.ToStringAddrPort(),
692
                     NetworkErrorString(WSAGetLastError()));
693
        }
694
#endif
695
280
    }
696
697
560
    if (sock->Bind(sa, len) == SOCKET_ERROR) {
  Branch (697:9): [True: 0, False: 560]
698
0
        const int err{WSAGetLastError()};
699
0
        if (err == WSAEADDRINUSE) {
  Branch (699:13): [True: 0, False: 0]
700
0
            return util::Error{strprintf(_("Unable to bind to %s on this computer. %s is probably already running."),
701
0
                                            to.ToStringAddrPort(),
702
0
                                            CLIENT_NAME)};
703
0
        } else {
704
0
            return util::Error{strprintf(_("Unable to bind to %s on this computer (bind returned error %s)"),
705
0
                                            to.ToStringAddrPort(),
706
0
                                            NetworkErrorString(err))};
707
0
        }
708
0
    }
709
710
    // Listen for incoming connections
711
560
    if (sock->Listen(SOMAXCONN) == SOCKET_ERROR) {
  Branch (711:9): [True: 0, False: 560]
712
0
        return util::Error{strprintf(_("Cannot listen on %s: %s"),
713
0
                                        to.ToStringAddrPort(),
714
0
                                        NetworkErrorString(WSAGetLastError()))};
715
0
    }
716
717
560
    m_listen.emplace_back(std::move(sock));
718
719
560
    return {};
720
560
}
721
722
void HTTPServer::StopListening()
723
280
{
724
280
    m_listen.clear();
725
280
}
726
727
void HTTPServer::StartSocketsThreads()
728
280
{
729
280
    m_thread_socket_handler = std::thread(&util::TraceThread,
730
280
                                          "http",
731
280
                                          [this] { ThreadSocketHandler(); });
732
280
}
733
734
void HTTPServer::JoinSocketsThreads()
735
280
{
736
280
    if (m_thread_socket_handler.joinable()) {
  Branch (736:9): [True: 280, False: 0]
737
280
        m_thread_socket_handler.join();
738
280
    }
739
280
}
740
741
std::unique_ptr<Sock> HTTPServer::AcceptConnection(const Sock& listen_sock, CService& addr)
742
10.0k
{
743
    // Make sure we only operate on our own listening sockets
744
10.0k
    Assume(std::ranges::any_of(m_listen, [&](const auto& sock) { return sock.get() == &listen_sock; }));
745
746
10.0k
    sockaddr_storage storage;
747
10.0k
    socklen_t len{sizeof(storage)};
748
10.0k
    auto sa = static_cast<sockaddr*>(static_cast<void*>(&storage));
749
750
10.0k
    auto sock{listen_sock.Accept(sa, &len)};
751
752
10.0k
    if (!sock) {
  Branch (752:9): [True: 0, False: 10.0k]
753
0
        const int err{WSAGetLastError()};
754
0
        if (err != WSAEWOULDBLOCK) {
  Branch (754:13): [True: 0, False: 0]
755
0
            LogDebug(BCLog::HTTP,
756
0
                     "Cannot accept new connection: %s\n",
757
0
                     NetworkErrorString(err));
758
0
        }
759
0
        return {};
760
0
    }
761
762
10.0k
    if (!addr.SetSockAddr(sa, len)) {
  Branch (762:9): [True: 0, False: 10.0k]
763
0
        LogDebug(BCLog::HTTP,
764
0
                 "Unknown socket family\n");
765
0
    }
766
767
10.0k
    return sock;
768
10.0k
}
769
770
HTTPServer::Id HTTPServer::GetNewId()
771
10.0k
{
772
10.0k
    return m_next_id.fetch_add(1, std::memory_order_relaxed);
773
10.0k
}
774
775
void HTTPServer::NewSockAccepted(std::unique_ptr<Sock>&& sock, const CService& them)
776
10.0k
{
777
10.0k
    if (!sock->IsSelectable()) {
  Branch (777:9): [True: 0, False: 10.0k]
778
0
        LogDebug(BCLog::HTTP,
779
0
                 "connection from %s dropped: non-selectable socket\n",
780
0
                 them.ToStringAddrPort());
781
0
        return;
782
0
    }
783
784
    // According to the internet TCP_NODELAY is not carried into accepted sockets
785
    // on all platforms.  Set it again here just to be sure.
786
10.0k
    if (sock->SetSockOpt(IPPROTO_TCP, TCP_NODELAY, &SOCKET_OPTION_TRUE, sizeof(SOCKET_OPTION_TRUE)) == SOCKET_ERROR) {
  Branch (786:9): [True: 0, False: 10.0k]
787
0
        LogDebug(BCLog::HTTP, "connection from %s: unable to set TCP_NODELAY, continuing anyway\n",
788
0
                 them.ToStringAddrPort());
789
0
    }
790
791
10.0k
    const Id id{GetNewId()};
792
793
10.0k
    m_connected.push_back(std::make_shared<HTTPClient>(id, them, std::move(sock)));
794
    // Report back to the main thread
795
10.0k
    m_connected_size.fetch_add(1, std::memory_order_relaxed);
796
797
10.0k
    LogDebug(BCLog::HTTP,
798
10.0k
             "HTTP Connection accepted from %s (id=%d)\n",
799
10.0k
             them.ToStringAddrPort(), id);
800
10.0k
}
801
802
void HTTPServer::SocketHandlerConnected(const IOReadiness& io_readiness) const
803
179M
{
804
551M
    for (const auto& [sock, events] : io_readiness.events_per_sock) {
  Branch (804:37): [True: 551M, False: 179M]
805
551M
        if (m_interrupt_net) {
  Branch (805:13): [True: 274, False: 551M]
806
274
            return;
807
274
        }
808
809
551M
        auto it{io_readiness.httpclients_per_sock.find(sock)};
810
551M
        if (it == io_readiness.httpclients_per_sock.end()) {
  Branch (810:13): [True: 358M, False: 192M]
811
358M
            continue;
812
358M
        }
813
192M
        const std::shared_ptr<HTTPClient> client{it->second};
814
815
192M
        bool send_ready = events.occurred & Sock::SEND; // Sock::SEND could only be set if ShouldTryToSend() has returned true in GenerateWaitSockets().
816
192M
        bool recv_ready = events.occurred & Sock::RECV; // Sock::RECV could only be set if ShouldTryToRecv() has returned true in GenerateWaitSockets().
817
192M
        bool err_ready = events.occurred & Sock::ERR;
818
819
192M
        if (send_ready) {
  Branch (819:13): [True: 0, False: 192M]
820
            // Try to send as much data as is ready for this client.
821
            // If there's an error we can skip the receive phase for this client
822
            // because we need to disconnect.
823
0
            if (!client->MaybeSendBytesFromBuffer()) {
  Branch (823:17): [True: 0, False: 0]
824
0
                recv_ready = false;
825
0
            }
826
0
        }
827
828
192M
        if (recv_ready || err_ready) {
  Branch (828:13): [True: 192M, False: 318k]
  Branch (828:27): [True: 0, False: 318k]
829
192M
            std::byte buf[0x10000]; // typical socket buffer is 8K-64K
830
831
192M
            const ssize_t nrecv{WITH_LOCK(
832
192M
                client->m_sock_mutex,
833
192M
                return client->m_sock->Recv(buf, sizeof(buf), MSG_DONTWAIT);)};
834
835
192M
            if (nrecv < 0) { // In all cases (including -1 and 0) EventIOLoopCompletedForOne() should be executed after this, don't change the code to skip it.
  Branch (835:17): [True: 0, False: 192M]
836
0
                const int err = WSAGetLastError();
837
0
                if (err != WSAEWOULDBLOCK && err != WSAEMSGSIZE && err != WSAEINTR && err != WSAEINPROGRESS) {
  Branch (837:21): [True: 0, False: 0]
  Branch (837:46): [True: 0, False: 0]
  Branch (837:68): [True: 0, False: 0]
  Branch (837:87): [True: 0, False: 0]
838
0
                    LogDebug(
839
0
                        BCLog::HTTP,
840
0
                        "Permanent read error from %s (id=%lld): %s\n",
841
0
                        client->m_origin,
842
0
                        client->m_id,
843
0
                        NetworkErrorString(err));
844
0
                    client->m_disconnect = true;
845
0
                }
846
192M
            } else if (nrecv == 0) {
  Branch (846:24): [True: 192M, False: 1.82k]
847
192M
                LogDebug(
848
192M
                    BCLog::HTTP,
849
192M
                    "Received EOF from %s (id=%lld)\n",
850
192M
                    client->m_origin,
851
192M
                    client->m_id);
852
192M
                client->m_disconnect = true;
853
192M
            } else {
854
                // Reset idle timeout
855
1.82k
                client->m_idle_since = Now<SteadySeconds>();
856
857
                // Prevent disconnect until all requests are completely handled.
858
1.82k
                client->m_prevent_disconnect = true;
859
860
                // Copy data from socket buffer to client receive buffer
861
1.82k
                client->m_recv_buffer.insert(
862
1.82k
                    client->m_recv_buffer.end(),
863
1.82k
                    buf,
864
1.82k
                    buf + nrecv);
865
1.82k
            }
866
192M
        }
867
        // Process as much received data as we can.
868
        // This executes for every client whether or not reading or writing
869
        // took place because it also (might) parse a request we have already
870
        // received and pass it to a worker thread.
871
192M
        MaybeDispatchRequestsFromClient(client);
872
192M
    }
873
179M
}
874
875
void HTTPServer::SocketHandlerListening(const Sock::EventsPerSock& events_per_sock)
876
179M
{
877
358M
    for (const auto& sock : m_listen) {
  Branch (877:27): [True: 358M, False: 179M]
878
358M
        if (m_interrupt_net) {
  Branch (878:13): [True: 274, False: 358M]
879
274
            return;
880
274
        }
881
358M
        const auto it = events_per_sock.find(sock);
882
358M
        if (it != events_per_sock.end() && it->second.occurred & Sock::RECV) {
  Branch (882:13): [True: 358M, False: 0]
  Branch (882:13): [True: 10.0k, False: 358M]
  Branch (882:44): [True: 10.0k, False: 358M]
883
10.0k
            CService addr_accepted;
884
885
10.0k
            auto sock_accepted{AcceptConnection(*sock, addr_accepted)};
886
887
10.0k
            if (sock_accepted) {
  Branch (887:17): [True: 10.0k, False: 0]
888
10.0k
                NewSockAccepted(std::move(sock_accepted), addr_accepted);
889
10.0k
            }
890
10.0k
        }
891
358M
    }
892
179M
}
893
894
HTTPServer::IOReadiness HTTPServer::GenerateWaitSockets() const
895
179M
{
896
179M
    IOReadiness io_readiness;
897
898
358M
    for (const auto& sock : m_listen) {
  Branch (898:27): [True: 358M, False: 179M]
899
358M
        io_readiness.events_per_sock.emplace(sock, Sock::Events{Sock::RECV});
900
358M
    }
901
902
192M
    for (const auto& http_client : m_connected) {
  Branch (902:34): [True: 192M, False: 179M]
903
        // Safely copy the shared pointer to the socket
904
192M
        std::shared_ptr<Sock> sock{WITH_LOCK(http_client->m_sock_mutex, return http_client->m_sock;)};
905
906
        // Check if client is ready to send data. Don't try to receive again
907
        // until the send buffer is cleared (all data sent to client).
908
192M
        Sock::Event event = (http_client->m_send_ready ? Sock::SEND : Sock::RECV);
  Branch (908:30): [True: 0, False: 192M]
909
192M
        io_readiness.events_per_sock.emplace(sock, Sock::Events{event});
910
192M
        io_readiness.httpclients_per_sock.emplace(sock, http_client);
911
192M
    }
912
913
179M
    return io_readiness;
914
179M
}
915
916
void HTTPServer::ThreadSocketHandler()
917
280
{
918
179M
    while (!m_interrupt_net) {
  Branch (918:12): [True: 179M, False: 280]
919
        // Check for the readiness of the already connected sockets and the
920
        // listening sockets in one call ("readiness" as in poll(2) or
921
        // select(2)). If none are ready, wait for a short while and return
922
        // empty sets.
923
179M
        auto io_readiness{GenerateWaitSockets()};
924
179M
        if (io_readiness.events_per_sock.empty() ||
  Branch (924:13): [True: 0, False: 179M]
  Branch (924:13): [True: 0, False: 179M]
925
            // WaitMany() may as well be a static method, the context of the first Sock in the vector is not relevant.
926
179M
            !io_readiness.events_per_sock.begin()->first->WaitMany(SELECT_TIMEOUT,
  Branch (926:13): [True: 0, False: 179M]
927
179M
                                                                   io_readiness.events_per_sock)) {
928
0
            m_interrupt_net.sleep_for(SELECT_TIMEOUT);
929
0
        }
930
931
        // Service (send/receive) each of the already connected sockets.
932
179M
        SocketHandlerConnected(io_readiness);
933
934
        // Accept new connections from listening sockets.
935
179M
        SocketHandlerListening(io_readiness.events_per_sock);
936
937
        // Disconnect any clients that have been flagged.
938
179M
        DisconnectClients();
939
179M
    }
940
280
}
941
942
void HTTPServer::MaybeDispatchRequestsFromClient(std::shared_ptr<HTTPClient> client) const
943
192M
{
944
    // Try reading (potentially multiple) HTTP requests from the buffer
945
192M
    while (!client->m_recv_buffer.empty()) {
  Branch (945:12): [True: 192M, False: 329k]
946
        // Create a new request object and try to fill it with data from the receive buffer
947
192M
        auto req = std::make_unique<HTTPRequest>(client);
948
192M
        try {
949
            // Stop reading if we need more data from the client to parse a complete request
950
192M
            if (!client->ReadRequest(req)) break;
  Branch (950:17): [True: 192M, False: 1.70k]
951
192M
        } catch (const std::runtime_error& e) {
952
268
            LogDebug(
953
268
                BCLog::HTTP,
954
268
                "Error reading HTTP request from client %s (id=%lld): %s\n",
955
268
                client->m_origin,
956
268
                client->m_id,
957
268
                e.what());
958
959
            // We failed to read a complete request from the buffer
960
268
            req->WriteReply(HTTP_BAD_REQUEST);
961
268
            client->m_disconnect = true;
962
268
            break;
963
268
        }
964
965
        // We read a complete request from the buffer into the queue
966
1.43k
        LogDebug(
967
1.43k
            BCLog::HTTP,
968
1.43k
            "Received a %s request for %s from %s (id=%lld)\n",
969
1.43k
            RequestMethodString(req->m_method),
970
1.43k
            req->m_target,
971
1.43k
            client->m_origin,
972
1.43k
            client->m_id);
973
974
        // add request to client queue
975
1.43k
        client->m_req_queue.push_back(std::move(req));
976
1.43k
    }
977
978
    // If we are already handling a request from
979
    // this client, do nothing. We'll check again on the next I/O
980
    // loop iteration.
981
192M
    if (client->m_req_busy) return;
  Branch (981:9): [True: 4.55k, False: 192M]
982
983
    // Otherwise, if there is a pending request in the queue, handle it.
984
192M
    if (!client->m_req_queue.empty()) {
  Branch (984:9): [True: 1.43k, False: 192M]
985
1.43k
        client->m_req_busy = true;
986
1.43k
        m_request_dispatcher(std::move(client->m_req_queue.front()));
987
1.43k
        client->m_req_queue.pop_front();
988
1.43k
    }
989
192M
}
990
991
void HTTPServer::DisconnectClients()
992
179M
{
993
179M
    const auto now{Now<SteadySeconds>()};
994
179M
    size_t erased = std::erase_if(m_connected,
995
192M
                                  [&](auto& client) {
996
                                        // Disconnect this client if it is flagged individually or if the
997
                                        // server is flagged to disconnect all...
998
192M
                                        if (((client->m_disconnect || m_disconnect_all_clients) &&
  Branch (998:45): [True: 10.0k, False: 192M]
  Branch (998:47): [True: 192M, False: 330k]
  Branch (998:71): [True: 7, False: 330k]
999
                                            // ...but not if this client is specifically flagged to prevent disconnect!
1000
                                            // It is probably still busy.
1001
192M
                                            !client->m_prevent_disconnect) ||
  Branch (1001:45): [True: 9.95k, False: 192M]
1002
                                            // No matter what, always disconnect if client has timed out.
1003
192M
                                            now - client->m_idle_since > m_rpcservertimeout) {
  Branch (1003:45): [True: 100, False: 192M]
1004
10.0k
                                            LogDebug(BCLog::HTTP,
1005
10.0k
                                                     "Disconnected HTTP client %s (id=%d)\n",
1006
10.0k
                                                     client->m_origin,
1007
10.0k
                                                     client->m_id);
1008
10.0k
                                            return true;
1009
192M
                                        } else {
1010
192M
                                            return false;
1011
192M
                                        }});
1012
179M
    if (erased > 0) {
  Branch (1012:9): [True: 3.06k, False: 179M]
1013
        // Report back to the main thread
1014
3.06k
        m_connected_size.fetch_sub(erased, std::memory_order_relaxed);
1015
3.06k
    }
1016
179M
}
1017
1018
bool HTTPClient::ReadRequest(const std::unique_ptr<HTTPRequest>& req)
1019
192M
{
1020
192M
    LineReader reader(m_recv_buffer, MAX_HEADERS_SIZE);
1021
1022
192M
    if (!req->LoadControlData(reader)) return false;
  Branch (1022:9): [True: 192M, False: 1.70k]
1023
1.70k
    if (!req->LoadHeaders(reader)) return false;
  Branch (1023:9): [True: 0, False: 1.70k]
1024
1.70k
    if (!req->LoadBody(reader)) return false;
  Branch (1024:9): [True: 0, False: 1.70k]
1025
1026
    // Remove the bytes read out of the buffer.
1027
    // If one of the above calls throws an error, the caller must
1028
    // catch it and disconnect the client.
1029
1.70k
    m_recv_buffer.erase(
1030
1.70k
        m_recv_buffer.begin(),
1031
1.70k
        m_recv_buffer.begin() + (reader.it - reader.start));
1032
1033
1.70k
    return true;
1034
1.70k
}
1035
1036
bool HTTPClient::MaybeSendBytesFromBuffer()
1037
1.70k
{
1038
    // Send as much data from this client's buffer as we can
1039
1.70k
    LOCK(m_send_mutex);
1040
1.70k
    if (!m_send_buffer.empty()) {
  Branch (1040:9): [True: 1.70k, False: 0]
1041
        // Socket flags (See kernel docs for send(2) and tcp(7) for more details).
1042
        // MSG_NOSIGNAL: If the remote end of the connection is closed,
1043
        //               fail with EPIPE (an error) as opposed to triggering
1044
        //               SIGPIPE which terminates the process.
1045
        // MSG_DONTWAIT: Makes the send operation non-blocking regardless of socket blocking mode.
1046
        // MSG_MORE:     We do not set this flag here because http responses are usually
1047
        //               small and we want the kernel to send them right away. Setting MSG_MORE
1048
        //               would "cork" the socket to prevent sending out partial frames.
1049
1.70k
        int flags{MSG_NOSIGNAL | MSG_DONTWAIT};
1050
1051
        // Try to send bytes through socket
1052
1.70k
        ssize_t bytes_sent;
1053
1.70k
        {
1054
1.70k
            LOCK(m_sock_mutex);
1055
1.70k
            bytes_sent = m_sock->Send(m_send_buffer.data(),
1056
1.70k
                                      m_send_buffer.size(),
1057
1.70k
                                      flags);
1058
1.70k
        }
1059
1060
1.70k
        if (bytes_sent < 0) {
  Branch (1060:13): [True: 0, False: 1.70k]
1061
            // Something went wrong
1062
0
            const int err{WSAGetLastError()};
1063
            // These errors can be safely ignored, and we should try the send again
1064
            // on the next I/O loop. See send(2) for more details.
1065
            // EWOULDBLOCK: The requested operation would block.
1066
            //              The non-blocking socket operation cannot complete immediately.
1067
            // EMSGSIZE:    Message too large. The receive buffer is too small for the incoming message.
1068
            // EINTR:       Interrupted function call. The socket operation was interrupted by another thread.
1069
            // EINPROGRESS: A socket operation in already progress.
1070
0
            if (err == WSAEWOULDBLOCK || err == WSAEMSGSIZE || err == WSAEINTR || err == WSAEINPROGRESS) {
  Branch (1070:17): [True: 0, False: 0]
  Branch (1070:42): [True: 0, False: 0]
  Branch (1070:64): [True: 0, False: 0]
  Branch (1070:83): [True: 0, False: 0]
1071
0
                return true;
1072
0
            }
1073
1074
            // Unrecoverbale error, log and disconnect client.
1075
0
            LogDebug(
1076
0
                BCLog::HTTP,
1077
0
                "Error sending HTTP response data to client %s (id=%lld): %s\n",
1078
0
                m_origin,
1079
0
                m_id,
1080
0
                NetworkErrorString(err));
1081
1082
0
            m_send_ready = false;
1083
0
            m_prevent_disconnect = false;
1084
0
            m_disconnect = true;
1085
1086
            // Do not attempt to read from this client.
1087
0
            return false;
1088
0
        }
1089
1090
        // Successful send, remove sent bytes from our local buffer.
1091
1.70k
        Assume(static_cast<size_t>(bytes_sent) <= m_send_buffer.size());
1092
1.70k
        m_send_buffer.erase(m_send_buffer.begin(),
1093
1.70k
                            m_send_buffer.begin() + bytes_sent);
1094
1095
1.70k
        LogDebug(
1096
1.70k
            BCLog::HTTP,
1097
1.70k
            "Sent %d bytes to client %s (id=%lld)\n",
1098
1.70k
            bytes_sent,
1099
1.70k
            m_origin,
1100
1.70k
            m_id);
1101
1102
        // This check is inside the if(!empty) block meaning "there was data but now its gone".
1103
        // We shouldn't even be calling SendBytesFromBuffer() when the send buffer is empty,
1104
        // but for belt-and-suspenders, we don't want to modify the disconnect flags if SendBytesFromBuffer() was a no-op.
1105
1.70k
        if (m_send_buffer.empty()) {
  Branch (1105:13): [True: 1.70k, False: 0]
1106
1.70k
            m_send_ready = false;
1107
1.70k
            m_prevent_disconnect = false;
1108
1109
            // Our work is done here
1110
1.70k
            if (!m_keep_alive) {
  Branch (1110:17): [True: 0, False: 1.70k]
1111
0
                m_disconnect = true;
1112
                // Do not attempt to read from this client.
1113
0
                return false;
1114
0
            }
1115
1.70k
        } else {
1116
            // The send buffer isn't flushed yet, try to push more on the next loop.
1117
0
            m_send_ready = true;
1118
0
            m_prevent_disconnect = true;
1119
0
        }
1120
1.70k
    }
1121
1122
1.70k
    return true;
1123
1.70k
}
1124
1125
bool InitHTTPServer()
1126
280
{
1127
280
    if (!InitHTTPAllowList()) {
  Branch (1127:9): [True: 0, False: 280]
1128
0
        return false;
1129
0
    }
1130
1131
    // Create HTTPServer
1132
280
    g_http_server = std::make_unique<HTTPServer>(MaybeDispatchRequestToWorker);
1133
1134
280
    g_http_server->SetServerTimeout(std::chrono::seconds(gArgs.GetIntArg("-rpcservertimeout", DEFAULT_HTTP_SERVER_TIMEOUT)));
1135
1136
    // Bind HTTP server to specified addresses
1137
280
    std::vector<std::pair<std::string, uint16_t>> endpoints{GetBindAddresses()};
1138
280
    bool bind_success{false};
1139
840
    for (std::vector<std::pair<std::string, uint16_t>>::iterator i = endpoints.begin(); i != endpoints.end(); ++i) {
  Branch (1139:89): [True: 560, False: 280]
1140
560
        LogInfo("Binding RPC on address %s port %i\n", i->first, i->second);
1141
560
        const std::optional<CService> addr{Lookup(i->first, i->second, false)};
1142
560
        if (addr) {
  Branch (1142:13): [True: 560, False: 0]
1143
560
            if (addr->IsBindAny()) {
  Branch (1143:17): [True: 0, False: 560]
1144
0
                LogWarning("The RPC server is not safe to expose to untrusted networks such as the public internet\n");
1145
0
            }
1146
560
            auto result{g_http_server->BindAndStartListening(addr.value())};
1147
560
            if (!result) {
  Branch (1147:17): [True: 0, False: 560]
1148
0
                LogWarning("Binding RPC on address %s failed: %s\n", addr->ToStringAddrPort(), util::ErrorString(result).original);
1149
560
            } else {
1150
560
                bind_success = true;
1151
560
            }
1152
560
        } else {
1153
0
            LogWarning("Binding RPC on address %s port %i failed.\n", i->first, i->second);
1154
0
        }
1155
560
    }
1156
1157
280
    if (!bind_success) {
  Branch (1157:9): [True: 0, False: 280]
1158
0
        LogError("Unable to bind any endpoint for RPC server\n");
1159
0
        return false;
1160
0
    }
1161
1162
280
    LogDebug(BCLog::HTTP, "Initialized HTTP server\n");
1163
280
    int workQueueDepth = std::max((long)gArgs.GetIntArg("-rpcworkqueue", DEFAULT_HTTP_WORKQUEUE), 1L);
1164
280
    LogDebug(BCLog::HTTP, "creating work queue of depth %d\n", workQueueDepth);
1165
1166
280
    g_work_queue = std::make_unique<WorkQueue<HTTPClosure>>(workQueueDepth);
1167
1168
280
    return true;
1169
280
}
1170
1171
static std::vector<std::thread> g_thread_http_workers;
1172
1173
void StartHTTPServer()
1174
280
{
1175
280
    int rpcThreads = std::max((long)gArgs.GetIntArg("-rpcthreads", DEFAULT_HTTP_THREADS), 1L);
1176
280
    LogInfo("Starting HTTP server with %d worker threads\n", rpcThreads);
1177
280
    g_http_server->StartSocketsThreads();
1178
1179
1.40k
    for (int i = 0; i < rpcThreads; i++) {
  Branch (1179:21): [True: 1.12k, False: 280]
1180
1.12k
        g_thread_http_workers.emplace_back(HTTPWorkQueueRun, g_work_queue.get(), i);
1181
1.12k
    }
1182
280
}
1183
1184
void InterruptHTTPServer()
1185
280
{
1186
280
    LogDebug(BCLog::HTTP, "Interrupting HTTP server\n");
1187
280
    if (g_http_server) {
  Branch (1187:9): [True: 280, False: 0]
1188
        // Reject all new requests
1189
280
        g_http_server->SetRequestHandler(RejectAllRequests);
1190
280
    }
1191
280
    if (g_work_queue) {
  Branch (1191:9): [True: 280, False: 0]
1192
        // Stop workers, killing requests we haven't processed or responded to yet
1193
280
        g_work_queue->Interrupt();
1194
280
    }
1195
280
}
1196
1197
void StopHTTPServer()
1198
280
{
1199
280
    LogDebug(BCLog::HTTP, "Stopping HTTP server\n");
1200
280
    if (g_work_queue) {
  Branch (1200:9): [True: 280, False: 0]
1201
280
        LogDebug(BCLog::HTTP, "Waiting for HTTP worker threads to exit\n");
1202
1.12k
        for (auto& thread : g_thread_http_workers) {
  Branch (1202:27): [True: 1.12k, False: 280]
1203
1.12k
            thread.join();
1204
1.12k
        }
1205
280
        g_thread_http_workers.clear();
1206
280
    }
1207
280
    if (g_http_server) {
  Branch (1207:9): [True: 280, False: 0]
1208
        // Disconnect clients as their remaining responses are flushed
1209
280
        g_http_server->DisconnectAllClients();
1210
        // Wait for all disconnections
1211
53.2k
        while (g_http_server->GetConnectionsCount() != 0) {
  Branch (1211:16): [True: 52.9k, False: 280]
1212
52.9k
            std::this_thread::sleep_for(50ms);
1213
52.9k
        }
1214
        // Break sockman I/O loop: stop accepting connections, sending and receiving data
1215
280
        g_http_server->InterruptNet();
1216
        // Wait for sockman threads to exit
1217
280
        g_http_server->JoinSocketsThreads();
1218
        // Close all listening sockets
1219
280
        g_http_server->StopListening();
1220
280
    }
1221
280
    LogDebug(BCLog::HTTP, "Stopped HTTP server\n");
1222
280
}
1223
} // namespace http_bitcoin