Bitcoin Core Fuzz Coverage Report

Coverage Report

Created: 2026-03-24 13:57

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