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

libbitcoin / libbitcoin-server / 42675974756

09 Nov 2026 06:39PM UTC coverage: 95.065% (+0.1%) from 94.917%
42675974756

push

github

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

Check cpu extensions at startup, normalize http response path.

178 of 192 new or added lines in 10 files covered. (92.71%)

11983 of 12605 relevant lines covered (95.07%)

246.05 hits per line

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

96.63
/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,
415 ✔
41
    const method::get::cptr& get) NOEXCEPT
42
{
43
    BC_ASSERT(stranded());
415 ✔
44

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

48
    // Enforce http origin form for get.
49
    if (!is_origin_form(get->target()))
191 ✔
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()))
190 ✔
57
    {
58
        send_forbidden(*get);
2 ✔
59
        return;
2 ✔
60
    }
61

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

69
    // Senders respond to the cached request.
70
    set_request(get);
187 ✔
71

72
    // Always try API dispatch, false if unhandled.
73
    if (try_dispatch_object(*get))
187 ✔
74
        return;
75

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

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

90
    // Neither site is enabled and object dispatch doesn't support.
NEW
91
    send_not_implemented();
×
92
}
93

94
// Dispatch.
95
// ----------------------------------------------------------------------------
96

97
bool protocol_html::try_dispatch_object(const request&) NOEXCEPT
×
98
{
99
    return false;
×
100
}
101

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

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

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

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

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

171
    constexpr auto octet_stream = media_type::application_octet_stream;
172
    send_file(std::move(file), file_media_type(path, octet_stream));
3 ✔
173
}
10 ✔
174

175
// Notifiers (websocket).
176
// ----------------------------------------------------------------------------
177

178
void protocol_html::notify_json(system::json::value&& model, size_t size_hint,
9 ✔
179
    const request& request) NOEXCEPT
180
{
181
    BC_ASSERT(stranded() && websocket());
9 ✔
182
    response response{ status::ok, request.version() };
9 ✔
183
    response.body() = json_value
9 ✔
184
    {
185
        .model = std::move(model),
9 ✔
186
        .size_hint = size_hint
187
    };
9 ✔
188
    NOTIFY(std::move(response), handle_complete, _1, error::success);
9 ✔
189
}
9 ✔
190

191
void protocol_html::notify_text(std::string&& hexidecimal,
4 ✔
192
    const request& request) NOEXCEPT
193
{
194
    BC_ASSERT(stranded() && websocket());
4 ✔
195
    response response{ status::ok, request.version() };
4 ✔
196
    response.body() = std::move(hexidecimal);
4 ✔
197
    NOTIFY(std::move(response), handle_complete, _1, error::success);
4 ✔
198
}
4 ✔
199

200
void protocol_html::notify_chunk(system::data_chunk&& bytes,
3 ✔
201
    const request& request) NOEXCEPT
202
{
203
    BC_ASSERT(stranded() && websocket());
3 ✔
204
    response response{ status::ok, request.version() };
3 ✔
205
    response.body() = std::move(bytes);
3 ✔
206
    NOTIFY(std::move(response), handle_complete, _1, error::success);
3 ✔
207
}
3 ✔
208

209
void protocol_html::notify_empty(const request& request) NOEXCEPT
4 ✔
210
{
211
    BC_ASSERT(stranded() && websocket());
4 ✔
212
    response response{ status::ok, request.version() };
4 ✔
213
    response.body() = empty_value{};
4 ✔
214
    NOTIFY(std::move(response), handle_complete, _1, error::success);
4 ✔
215
}
4 ✔
216

217
// Utilities.
218
// ----------------------------------------------------------------------------
219

220
std::filesystem::path protocol_html::to_path(
18 ✔
221
    const std::string& target) const NOEXCEPT
222
{
223
    return system::to_path(target == "/" ? target + options_.default_ :
36 ✔
224
        target);
36 ✔
225
}
226

227
std::filesystem::path protocol_html::to_local_path(
12 ✔
228
    const std::string& target) const NOEXCEPT
229
{
230
    return sanitize_origin(options_.path, from_path(to_path(target)));
12 ✔
231
}
232

233
BC_POP_WARNING()
234
BC_POP_WARNING()
235
BC_POP_WARNING()
236

237
} // namespace server
238
} // 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