• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

libbitcoin / libbitcoin-server / 42977519106

11 Nov 2026 07:10AM UTC coverage: 95.121% (+0.04%) from 95.086%
42977519106

push

github

web-flow
Merge pull request #1156 from evoskuil/master

Regenerate artifacts, brace macro invocations under conditionals.

2 of 2 new or added lines in 1 file covered. (100.0%)

21 existing lines in 4 files now uncovered.

12087 of 12707 relevant lines covered (95.12%)

252.21 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.44
/src/protocols/admin/protocol_admin.cpp
1
/**
2
 * Copyright (c) 2011-2026 libbitcoin developers
3
 *
4
 * This file is part of libbitcoin.
5
 *
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU Affero General Public License as published by
8
 * the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU Affero General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Affero General Public License
17
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
 */
19
#include <bitcoin/server/protocols/protocol_admin.hpp>
20

21
#include <atomic>
22
#include <chrono>
23
#include <bitcoin/server/define.hpp>
24
#include <bitcoin/server/parsers/parsers.hpp>
25
#include <bitcoin/server/serializers/serializers.hpp>
26
#include <bitcoin/server/utilities/utilities.hpp>
27

28
namespace libbitcoin {
29
namespace server {
30

31
using namespace system;
32
using namespace network;
33
using namespace network::http;
34
using namespace std::placeholders;
35
constexpr auto relaxed = std::memory_order_relaxed;
36

37
#define CLASS protocol_admin
38
#define SUBSCRIBE_ADMIN(method, ...) \
39
    subscribe<CLASS>(&CLASS::method, __VA_ARGS__)
40

41
// Limited by json number domain and sentinel bit.
42
static_assert(node::events::unknown < 53u);
43
static_assert(network::levels::verbose < 53u);
44

45
// Start.
46
// ----------------------------------------------------------------------------
47

48
void protocol_admin::start() NOEXCEPT
33 ✔
49
{
50
    BC_ASSERT(stranded());
33 ✔
51

52
    if (started())
33 ✔
53
        return;
54

55
    // Subscription methods.
56
    SUBSCRIBE_ADMIN(handle_get_log_subscribe, _1, _2, _3, _4);
33 ✔
57
    SUBSCRIBE_ADMIN(handle_get_event_subscribe, _1, _2, _3, _4);
33 ✔
58
    protocol_native::start();
33 ✔
59
}
60

61
void protocol_admin::stopping(const code& ec) NOEXCEPT
33 ✔
62
{
63
    BC_ASSERT(stranded());
33 ✔
64
    log_interest_->set(zero);
33 ✔
65
    admin_dispatcher_.stop(ec);
33 ✔
66
    protocol_native::stopping(ec);
33 ✔
67
}
33 ✔
68

69
// Dispatch.
70
// ----------------------------------------------------------------------------
71

72
bool protocol_admin::try_dispatch_object(const http::request& request) NOEXCEPT
21 ✔
73
{
74
    BC_ASSERT(stranded());
21 ✔
75

76
    BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
77
    const auto target = request.target();
21 ✔
78
    BC_POP_WARNING()
79

80
    rpc::request_t model{};
21 ✔
81
    if (const auto ec = admin_target(model, target))
21 ✔
82
    {
83
        // An unrecognized admin target may be a native interface request.
84
        if (ec == error::invalid_target)
5 ✔
85
            return protocol_native::try_dispatch_object(request);
5 ✔
86

87
        // Allow invalid interface target to be retried as a page request.
88
        return false;
89
    }
90

91
    // No media defaults injected for an http request.
92
    if (!admin_query(model, request))
16 ✔
93
    {
94
        send_bad_request();
4 ✔
95
        return true;
96
    }
97

98
    const auto media = strip_media(model);
12 ✔
99
    if (media == media_type::unknown)
12 ✔
100
    {
101
        send_not_acceptable();
1 ✔
102
        return true;
103
    }
104

105
    // Falls through to html page dispatch.
106
    if (media == media_type::text_html)
11 ✔
107
        return false;
108

109
    // The credential may be restricted to a subset of interface methods.
110
    if (!permitted(model.method))
10 ✔
111
    {
112
        send_forbidden();
1 ✔
113
        return true;
114
    }
115

116
    if (admin_dispatcher_.notify(model))
9 ✔
UNCOV
117
        send_bad_request();
×
118

119
    return true;
120
}
21 ✔
121

122
void protocol_admin::dispatch_websocket(const http::request& request) NOEXCEPT
20 ✔
123
{
124
    BC_ASSERT(stranded());
20 ✔
125

126
    // ws frames carry no headers, so ws authorization is enforced here, not
127
    // by the channel (established by basic auth on the upgrade request).
128
    if (!authorized())
20 ✔
129
    {
UNCOV
130
        send_unauthorized(request);
×
131
        return;
6 ✔
132
    }
133

134
    // Admin websocket interface supports only string requests.
135
    if (!request.body().contains<http::string_value>())
20 ✔
136
    {
UNCOV
137
        send_not_acceptable(request);
×
UNCOV
138
        return;
×
139
    }
140

141
    // Target with query string is passed via websocket string body.
142
    const auto target = request.body().get<http::string_value>();
20 ✔
143

144
    rpc::request_t model{};
20 ✔
145
    if (const auto ec = admin_target(model, target))
20 ✔
146
    {
147
        // An unrecognized admin target may be a native interface request.
148
        if (ec == error::invalid_target)
4 ✔
149
            protocol_native::dispatch_websocket(request);
3 ✔
150
        else
151
            send_bad_target(ec, request);
1 ✔
152

153
        return;
4 ✔
154
    }
155

156
    // Default to json by simulating a json accept header (format overrides).
157
    if (!admin_query(model, target, { media_type::application_json }))
16 ✔
158
    {
159
        send_bad_request(request);
1 ✔
160
        return;
161
    }
162

163
    if (strip_media(model) != media_type::application_json)
15 ✔
164
    {
165
        send_not_acceptable(request);
1 ✔
166
        return;
167
    }
168

169
    // The credential may be restricted to a subset of interface methods.
170
    if (!permitted(model.method))
14 ✔
171
    {
UNCOV
172
        send_forbidden(request);
×
173
        return;
174
    }
175

176
    if (admin_dispatcher_.notify(model))
14 ✔
UNCOV
177
        send_bad_request(request);
×
178
}
20 ✔
179

180
// Interface handlers.
181
// ----------------------------------------------------------------------------
182

183
bool protocol_admin::handle_get_log_subscribe(const code& ec,
48 ✔
184
    admin_interface::log_subscribe, uint8_t /*version*/,
185
    uint64_t filter) NOEXCEPT
186
{
187
    BC_ASSERT(stranded());
48 ✔
188

189
    if (stopped(ec))
48 ✔
190
        return false;
191

192
    // log outlives node, so cannot hold shared_ptr to the protocol.
193
    const auto previous = log_interest_->set(filter);
15 ✔
194
    if (!is_zero(filter) && !log_subscribed_)
15 ✔
195
    {
196
        auto handler = BIND_WEAK(handle_log, _1, _2, _3, _4);
10 ✔
197
        log.subscribe_messages(log_interest_, std::move(handler));
10 ✔
198
        log_subscribed_ = true;
10 ✔
199
    }
10 ✔
200

201
    send_json({ { "previous", previous } });
15 ✔
202
    return true;
15 ✔
203
}
204

205
bool protocol_admin::handle_get_event_subscribe(const code& ec,
41 ✔
206
    admin_interface::event_subscribe, uint8_t /*version*/,
207
    uint64_t filter) NOEXCEPT
208
{
209
    BC_ASSERT(stranded());
41 ✔
210

211
    if (stopped(ec))
41 ✔
212
        return false;
213

214
    // log outlives node, so cannot hold shared_ptr to the protocol.
215
    uint64_t previous{};
8 ✔
216
    if (update_filter(previous, filter, event_state_))
8 ✔
217
        log.subscribe_events(BIND_WEAK(handle_event, _1, _2, _3, _4));
6 ✔
218

219
    send_json({ { "previous", previous } });
8 ✔
220
    return true;
8 ✔
221
}
222

223
// Event handlers.
224
// ----------------------------------------------------------------------------
225

226
bool protocol_admin::handle_log(const code& ec, uint8_t level, time_t zulu,
5 ✔
227
    const std::string& message) NOEXCEPT
228
{
229
    if (stopped(ec))
5 ✔
230
        return false;
231

232
    if (websocket())
4 ✔
233
    {
234
        POST(do_log, level, zulu * 1'000_u64, message);
4 ✔
235
    }
236

237
    return true;
238
}
239

240
bool protocol_admin::handle_event(const code& ec, uint8_t event_,
4 ✔
241
    uint64_t value, const logger::time& point) NOEXCEPT
242
{
243
    bool filtered{};
4 ✔
244
    if (stopped(ec) || !get_filter(filtered, event_, event_state_))
4 ✔
245
        return false;
1 ✔
246

247
    if (!filtered || !websocket())
3 ✔
248
        return true;
1 ✔
249

250
    POST(do_event, event_, to_zulu(point), value);
2 ✔
251
    return true;
2 ✔
252
}
253

254
// Notification event handlers.
255
// ----------------------------------------------------------------------------
256
// Posted to the protocol strand.
257

258
void protocol_admin::do_log(uint8_t level, uint64_t zulu,
4 ✔
259
    const std::string& message) NOEXCEPT
260
{
261
    BC_ASSERT(stranded() && websocket());
4 ✔
262

263
    if (stopped())
4 ✔
264
        return;
265

266
    notify_json(
4 ✔
267
    {
268
        { "level", level },
269
        { "time", zulu },
270
        { "message", message }
271
    });
272
}
273

274
void protocol_admin::do_event(uint8_t event_, uint64_t zulu,
2 ✔
275
    uint64_t value_) NOEXCEPT
276
{
277
    BC_ASSERT(stranded() && websocket());
2 ✔
278

279
    if (stopped())
2 ✔
280
        return;
281

282
    notify_json(
2 ✔
283
    {
284
        { "event", event_ },
285
        { "value", value_ },
286
        { "time", zulu }
287
    });
288
}
289

290
// Utils.
291
// ----------------------------------------------------------------------------
292
// static/private
293

294
// zulu time in milliseconds since epoch (native js time format).
295
uint64_t protocol_admin::to_zulu(const logger::time& point) NOEXCEPT
2 ✔
296
{
297
    using namespace std::chrono;
298
    const auto now = logger::now();
2 ✔
299
    const auto delta = duration_cast<system_clock::duration>(now - point);
2 ✔
300
    const auto time = (system_clock::now() - delta).time_since_epoch();
2 ✔
301
    return duration_cast<milliseconds>(time).count();
2 ✔
302
}
303

304
// Unsubscribe if false.
305
bool protocol_admin::get_filter(bool& filtered, size_t bit,
4 ✔
306
    filter_t& filter) NOEXCEPT
307
{
308
    auto state = filter.load(relaxed);
309
    const auto next = set_left(state, zero, false);
4 ✔
310

311
    // Empty filter unregisters, failure implies a new filter arrived.
312
    if (is_zero(next) && filter.compare_exchange_strong(state, next, relaxed))
5 ✔
313
        return false;
314

315
    filtered = get_right(state, bit);
3 ✔
316
    return true;
3 ✔
317
}
318

319
// Subscribe if true.
320
bool protocol_admin::update_filter(uint64_t& prior, uint64_t value,
8 ✔
321
    filter_t& filter) NOEXCEPT
322
{
323
    const auto next = set_left(value, zero, true);
8 ✔
324
    const auto state = is_zero(value) ? filter.fetch_and(next, relaxed) :
8 ✔
325
        filter.exchange(next, relaxed);
326

327
    prior = set_left(state, zero, false);
8 ✔
328
    return !is_zero(value) && !get_left(state);
8 ✔
329
}
330

331
} // namespace server
332
} // namespace libbitcoin
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc