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

libbitcoin / libbitcoin-server / 41719031671

03 Nov 2026 09:08PM UTC coverage: 94.853% (+0.3%) from 94.513%
41719031671

push

github

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

Assert the bip54 forks in the native header context.

12035 of 12688 relevant lines covered (94.85%)

238.96 hits per line

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

91.88
/src/protocols/protocol_html.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_html.hpp>
20

21
#include <bitcoin/server/define.hpp>
22

23
namespace libbitcoin {
24
namespace server {
25

26
#define CLASS protocol_html
27
    
28
using namespace system;
29
using namespace network::http;
30
using namespace std::placeholders;
31

32
// Shared pointers required in handler parameters so closures control lifetime.
33
BC_PUSH_WARNING(NO_VALUE_OR_CONST_REF_SHARED_PTR)
34
BC_PUSH_WARNING(SMART_PTR_NOT_NEEDED)
35
BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
36

37
// Handle get method.
38
// ----------------------------------------------------------------------------
39

40
void protocol_html::handle_receive_get(const code& ec,
397 ✔
41
    const method::get::cptr& get) NOEXCEPT
42
{
43
    BC_ASSERT(stranded());
397 ✔
44

45
    if (stopped(ec))
397 ✔
46
        return;
47

48
    // Enforce http origin form for get.
49
    if (!is_origin_form(get->target()))
182 ✔
50
    {
51
        send_bad_target({}, *get);
1 ✔
52
        return;
1 ✔
53
    }
54

55
    // Enforce http origin policy (if any origins are configured).
56
    if (!is_allowed_origin(*get, get->version()))
181 ✔
57
    {
58
        send_forbidden(*get);
1 ✔
59
        return;
1 ✔
60
    }
61

62
    // Enforce http host header (if any hosts are configured).
63
    if (!is_allowed_host(*get, get->version()))
180 ✔
64
    {
65
        send_bad_host(*get);
1 ✔
66
        return;
1 ✔
67
    }
68

69
    // Always try API dispatch, false if unhandled.
70
    if (try_dispatch_object(*get))
179 ✔
71
        return;
72

73
    // Require file system dispatch if path is configured (always handles).
74
    if (!options_.path.empty())
16 ✔
75
    {
76
        dispatch_file(*get);
10 ✔
77
        return;
10 ✔
78
    }
79

80
    // Require embedded dispatch if site is configured (always handles).
81
    if (options_.pages.enabled())
6 ✔
82
    {
83
        dispatch_embedded(*get);
6 ✔
84
        return;
6 ✔
85
    }
86

87
    // Neither site is enabled and object dispatch doesn't support.
88
    send_not_implemented(*get);
×
89
}
90

91
// Dispatch.
92
// ----------------------------------------------------------------------------
93

94
bool protocol_html::try_dispatch_object(const request&) NOEXCEPT
×
95
{
96
    return false;
×
97
}
98

99
// Accept field (RFC 7231) and ?format=<format> parameter are ignored.
100
// Return embedded page based on file extension only (defaulting to html).
101
// The content-type field is determined by the requested file extension.
102
void protocol_html::dispatch_embedded(const request& request) NOEXCEPT
6 ✔
103
{
104
    const auto& pages = options_.pages;
6 ✔
105
    switch (const auto media = file_media_type(to_path(request.target()),
6 ✔
106
        media_type::text_html))
107
    {
108
        case media_type::text_html:
1 ✔
109
            send_span(pages.html(), media, request);
1 ✔
110
            break;
1 ✔
111
        case media_type::text_css:
1 ✔
112
            send_span(pages.css(), media, request);
1 ✔
113
            break;
1 ✔
114
        case media_type::application_javascript:
1 ✔
115
            send_span(pages.ecma(), media, request);
1 ✔
116
            break;
1 ✔
117
        case media_type::font_woff:
1 ✔
118
        case media_type::font_woff2:
119
            send_span(pages.font(), media, request);
1 ✔
120
            break;
1 ✔
121
        case media_type::image_png:
1 ✔
122
        case media_type::image_gif:
123
        case media_type::image_jpeg:
124
        case media_type::image_svg_xml:
125
        case media_type::image_x_icon:
126
            send_span(pages.icon(), media, request);
1 ✔
127
            break;
1 ✔
128
        default:
1 ✔
129
            send_not_found(request);
1 ✔
130
    }
131
}
6 ✔
132

133
// Accept field (media type) and ?format=<format> parameter are ignored.
134
// Return requested page only if there is a path file extension (.*).
135
// Otherwise return the sanitized home page (configured or default).
136
// The content-type field is determined by the requested file extension.
137
void protocol_html::dispatch_file(const request& request) NOEXCEPT
10 ✔
138
{
139
    // Empty path implies malformed target (terminal).
140
    auto path = to_local_path(request.target());
10 ✔
141
    if (path.empty())
10 ✔
142
    {
143
        send_bad_target({}, request);
5 ✔
144
        return;
5 ✔
145
    }
146

147
    // If no file extension it's REST on the single/default html page.
148
    if (!path.has_extension())
5 ✔
149
    {
150
        path = to_local_path();
2 ✔
151

152
        // Default html page (e.g. index.html) is not configured.
153
        if (path.empty())
2 ✔
154
        {
155
            send_not_implemented(request);
1 ✔
156
            return;
157
        }
158
    }
159

160
    // Get the single/default or explicitly requested page.
161
    auto file = get_file_body(path);
4 ✔
162
    if (!file.is_open())
4 ✔
163
    {
164
        send_not_found(request);
1 ✔
165
        return;
166
    }
167

168
    constexpr auto octet_stream = media_type::application_octet_stream;
169
    send_file(std::move(file), file_media_type(path, octet_stream), request);
3 ✔
170
}
10 ✔
171

172
// Senders.
173
// ----------------------------------------------------------------------------
174

175
constexpr auto data = media_type::application_octet_stream;
176
constexpr auto json = media_type::application_json;
177
constexpr auto text = media_type::text_plain;
178

179
void protocol_html::send_json(system::json::value&& model, size_t size_hint,
70 ✔
180
    const request& request) NOEXCEPT
181
{
182
    BC_ASSERT(stranded());
70 ✔
183
    response response{ status::ok, request.version() };
70 ✔
184
    add_common_headers(response, request);
70 ✔
185
    add_access_control_headers(response, request);
70 ✔
186
    response.set(field::content_type, from_media_type(json));
70 ✔
187
    response.body() = json_value
70 ✔
188
    {
189
        .model = std::move(model),
70 ✔
190
        .size_hint = size_hint
191
    };
70 ✔
192
    response.prepare_payload();
70 ✔
193
    SEND(std::move(response), handle_complete, _1, error::success);
70 ✔
194
}
70 ✔
195

196
void protocol_html::send_text(std::string&& hexidecimal,
28 ✔
197
    const request& request) NOEXCEPT
198
{
199
    BC_ASSERT(stranded());
28 ✔
200
    response response{ status::ok, request.version() };
28 ✔
201
    add_common_headers(response, request);
28 ✔
202
    add_access_control_headers(response, request);
28 ✔
203
    response.set(field::content_type, from_media_type(text));
28 ✔
204
    response.body() = std::move(hexidecimal);
28 ✔
205
    response.prepare_payload();
28 ✔
206
    SEND(std::move(response), handle_complete, _1, error::success);
28 ✔
207
}
28 ✔
208

209
void protocol_html::send_chunk(system::data_chunk&& bytes,
40 ✔
210
    const request& request) NOEXCEPT
211
{
212
    BC_ASSERT(stranded());
40 ✔
213
    response response{ status::ok, request.version() };
40 ✔
214
    add_common_headers(response, request);
40 ✔
215
    add_access_control_headers(response, request);
40 ✔
216
    response.set(field::content_type, from_media_type(data));
40 ✔
217
    response.body() = std::move(bytes);
40 ✔
218
    response.prepare_payload();
40 ✔
219
    SEND(std::move(response), handle_complete, _1, error::success);
40 ✔
220
}
40 ✔
221

222
void protocol_html::send_file(file&& file, media_type type,
3 ✔
223
    const request& request) NOEXCEPT
224
{
225
    BC_ASSERT(stranded());
3 ✔
226
    BC_ASSERT_MSG(file.is_open(), "sending closed file handle");
3 ✔
227
    response response{ status::ok, request.version() };
3 ✔
228
    add_common_headers(response, request);
3 ✔
229
    add_access_control_headers(response, request);
3 ✔
230
    response.set(field::content_type, from_media_type(type));
3 ✔
231
    response.body() = std::move(file);
3 ✔
232
    response.prepare_payload();
3 ✔
233
    SEND(std::move(response), handle_complete, _1, error::success);
3 ✔
234
}
3 ✔
235

236
void protocol_html::send_span(span_body::value_type&& span,
5 ✔
237
    media_type type, const request& request) NOEXCEPT
238
{
239
    BC_ASSERT(stranded());
5 ✔
240
    response response{ status::ok, request.version() };
5 ✔
241
    add_common_headers(response, request);
5 ✔
242
    add_access_control_headers(response, request);
5 ✔
243
    response.set(field::content_type, from_media_type(type));
5 ✔
244
    response.body() = std::move(span);
5 ✔
245
    response.prepare_payload();
5 ✔
246
    SEND(std::move(response), handle_complete, _1, error::success);
5 ✔
247
}
5 ✔
248

249
void protocol_html::send_buffer(buffer_body::value_type&& buffer,
×
250
    media_type type, const request& request) NOEXCEPT
251
{
252
    BC_ASSERT(stranded());
×
253
    response response{ status::ok, request.version() };
×
254
    add_common_headers(response, request);
×
255
    add_access_control_headers(response, request);
×
256
    response.set(field::content_type, from_media_type(type));
×
257
    response.body() = std::move(buffer);
×
258
    response.prepare_payload();
×
259
    SEND(std::move(response), handle_complete, _1, error::success);
×
260
}
×
261

262
void protocol_html::send_empty(const request& request) NOEXCEPT
6 ✔
263
{
264
    BC_ASSERT(stranded());
6 ✔
265
    response response{ status::ok, request.version() };
6 ✔
266
    add_common_headers(response, request);
6 ✔
267
    add_access_control_headers(response, request);
6 ✔
268
    ////response.set(field::content_type, media_type::text_plain);
269
    response.body() = empty_value{};
6 ✔
270
    response.prepare_payload();
6 ✔
271
    SEND(std::move(response), handle_complete, _1, error::success);
6 ✔
272
}
6 ✔
273

274
// Notifiers (websocket).
275
// ----------------------------------------------------------------------------
276

277
void protocol_html::notify_json(system::json::value&& model, size_t size_hint,
9 ✔
278
    const request& request) NOEXCEPT
279
{
280
    BC_ASSERT(stranded() && websocket());
9 ✔
281
    response response{ status::ok, request.version() };
9 ✔
282
    response.body() = json_value
9 ✔
283
    {
284
        .model = std::move(model),
9 ✔
285
        .size_hint = size_hint
286
    };
9 ✔
287
    NOTIFY(std::move(response), handle_complete, _1, error::success);
9 ✔
288
}
9 ✔
289

290
void protocol_html::notify_text(std::string&& hexidecimal,
4 ✔
291
    const request& request) NOEXCEPT
292
{
293
    BC_ASSERT(stranded() && websocket());
4 ✔
294
    response response{ status::ok, request.version() };
4 ✔
295
    response.body() = std::move(hexidecimal);
4 ✔
296
    NOTIFY(std::move(response), handle_complete, _1, error::success);
4 ✔
297
}
4 ✔
298

299
void protocol_html::notify_chunk(system::data_chunk&& bytes,
3 ✔
300
    const request& request) NOEXCEPT
301
{
302
    BC_ASSERT(stranded() && websocket());
3 ✔
303
    response response{ status::ok, request.version() };
3 ✔
304
    response.body() = std::move(bytes);
3 ✔
305
    NOTIFY(std::move(response), handle_complete, _1, error::success);
3 ✔
306
}
3 ✔
307

308
void protocol_html::notify_empty(const request& request) NOEXCEPT
4 ✔
309
{
310
    BC_ASSERT(stranded() && websocket());
4 ✔
311
    response response{ status::ok, request.version() };
4 ✔
312
    response.body() = empty_value{};
4 ✔
313
    NOTIFY(std::move(response), handle_complete, _1, error::success);
4 ✔
314
}
4 ✔
315

316
// Utilities.
317
// ----------------------------------------------------------------------------
318

319
std::filesystem::path protocol_html::to_path(
18 ✔
320
    const std::string& target) const NOEXCEPT
321
{
322
    return system::to_path(target == "/" ? target + options_.default_ :
36 ✔
323
        target);
36 ✔
324
}
325

326
std::filesystem::path protocol_html::to_local_path(
12 ✔
327
    const std::string& target) const NOEXCEPT
328
{
329
    return sanitize_origin(options_.path, from_path(to_path(target)));
12 ✔
330
}
331

332
BC_POP_WARNING()
333
BC_POP_WARNING()
334
BC_POP_WARNING()
335

336
} // namespace server
337
} // 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