Skip to content

Commit f40537f

Browse files
committed
Refactor json/rpc body to remove rpc request/response redundancy.
1 parent ae11696 commit f40537f

File tree

9 files changed

+335
-225
lines changed

9 files changed

+335
-225
lines changed

Makefile.am

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,10 @@ include_bitcoin_network_impl_async_races_HEADERS = \
329329
include/bitcoin/network/impl/async/races/race_unity.ipp \
330330
include/bitcoin/network/impl/async/races/race_volume.ipp
331331

332+
include_bitcoin_network_impl_messages_jsondir = ${includedir}/bitcoin/network/impl/messages/json
333+
include_bitcoin_network_impl_messages_json_HEADERS = \
334+
include/bitcoin/network/impl/messages/json/body.ipp
335+
332336
include_bitcoin_network_impl_rpcdir = ${includedir}/bitcoin/network/impl/rpc
333337
include_bitcoin_network_impl_rpc_HEADERS = \
334338
include/bitcoin/network/impl/rpc/broadcaster.ipp \

builds/msvc/vs2022/libbitcoin-network/libbitcoin-network.vcxproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,7 @@
384384
<None Include="..\..\..\..\include\bitcoin\network\impl\async\races\race_volume.ipp" />
385385
<None Include="..\..\..\..\include\bitcoin\network\impl\async\subscriber.ipp" />
386386
<None Include="..\..\..\..\include\bitcoin\network\impl\async\unsubscriber.ipp" />
387+
<None Include="..\..\..\..\include\bitcoin\network\impl\messages\json\body.ipp" />
387388
<None Include="..\..\..\..\include\bitcoin\network\impl\rpc\broadcaster.ipp" />
388389
<None Include="..\..\..\..\include\bitcoin\network\impl\rpc\dispatcher.ipp" />
389390
<None Include="packages.config" />

builds/msvc/vs2022/libbitcoin-network/libbitcoin-network.vcxproj.filters

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -35,40 +35,46 @@
3535
<UniqueIdentifier>{564EB540-D6B6-425C-0000-0000000000F1}</UniqueIdentifier>
3636
</Filter>
3737
<Filter Include="include\bitcoin\network\impl\async\races">
38-
<UniqueIdentifier>{564EB540-D6B6-425C-0000-000000000003}</UniqueIdentifier>
38+
<UniqueIdentifier>{564EB540-D6B6-425C-0000-000000000004}</UniqueIdentifier>
3939
</Filter>
40-
<Filter Include="include\bitcoin\network\impl\rpc">
40+
<Filter Include="include\bitcoin\network\impl\messages">
4141
<UniqueIdentifier>{564EB540-D6B6-425C-0000-000000000002}</UniqueIdentifier>
4242
</Filter>
43+
<Filter Include="include\bitcoin\network\impl\messages\json">
44+
<UniqueIdentifier>{564EB540-D6B6-425C-0000-000000000005}</UniqueIdentifier>
45+
</Filter>
46+
<Filter Include="include\bitcoin\network\impl\rpc">
47+
<UniqueIdentifier>{564EB540-D6B6-425C-0000-000000000003}</UniqueIdentifier>
48+
</Filter>
4349
<Filter Include="include\bitcoin\network\log">
4450
<UniqueIdentifier>{564EB540-D6B6-425C-0000-000000000009}</UniqueIdentifier>
4551
</Filter>
4652
<Filter Include="include\bitcoin\network\messages">
4753
<UniqueIdentifier>{564EB540-D6B6-425C-0000-000000000010}</UniqueIdentifier>
4854
</Filter>
4955
<Filter Include="include\bitcoin\network\messages\http">
50-
<UniqueIdentifier>{564EB540-D6B6-425C-0000-000000000004}</UniqueIdentifier>
56+
<UniqueIdentifier>{564EB540-D6B6-425C-0000-000000000006}</UniqueIdentifier>
5157
</Filter>
5258
<Filter Include="include\bitcoin\network\messages\http\enums">
53-
<UniqueIdentifier>{564EB540-D6B6-425C-0000-000000000009}</UniqueIdentifier>
59+
<UniqueIdentifier>{564EB540-D6B6-425C-0000-000000000011}</UniqueIdentifier>
5460
</Filter>
5561
<Filter Include="include\bitcoin\network\messages\json">
56-
<UniqueIdentifier>{564EB540-D6B6-425C-0000-000000000005}</UniqueIdentifier>
62+
<UniqueIdentifier>{564EB540-D6B6-425C-0000-000000000007}</UniqueIdentifier>
5763
</Filter>
5864
<Filter Include="include\bitcoin\network\messages\monad">
59-
<UniqueIdentifier>{564EB540-D6B6-425C-0000-000000000006}</UniqueIdentifier>
65+
<UniqueIdentifier>{564EB540-D6B6-425C-0000-000000000008}</UniqueIdentifier>
6066
</Filter>
6167
<Filter Include="include\bitcoin\network\messages\peer">
62-
<UniqueIdentifier>{564EB540-D6B6-425C-0000-000000000007}</UniqueIdentifier>
68+
<UniqueIdentifier>{564EB540-D6B6-425C-0000-000000000009}</UniqueIdentifier>
6369
</Filter>
6470
<Filter Include="include\bitcoin\network\messages\peer\detail">
65-
<UniqueIdentifier>{564EB540-D6B6-425C-0000-000000000010}</UniqueIdentifier>
71+
<UniqueIdentifier>{564EB540-D6B6-425C-0000-0000000000A2}</UniqueIdentifier>
6672
</Filter>
6773
<Filter Include="include\bitcoin\network\messages\peer\enums">
68-
<UniqueIdentifier>{564EB540-D6B6-425C-0000-000000000011}</UniqueIdentifier>
74+
<UniqueIdentifier>{564EB540-D6B6-425C-0000-0000000000B2}</UniqueIdentifier>
6975
</Filter>
7076
<Filter Include="include\bitcoin\network\messages\rpc">
71-
<UniqueIdentifier>{564EB540-D6B6-425C-0000-000000000008}</UniqueIdentifier>
77+
<UniqueIdentifier>{564EB540-D6B6-425C-0000-000000000010}</UniqueIdentifier>
7278
</Filter>
7379
<Filter Include="include\bitcoin\network\net">
7480
<UniqueIdentifier>{564EB540-D6B6-425C-0000-0000000000A1}</UniqueIdentifier>
@@ -80,16 +86,16 @@
8086
<UniqueIdentifier>{564EB540-D6B6-425C-0000-0000000000C1}</UniqueIdentifier>
8187
</Filter>
8288
<Filter Include="include\bitcoin\network\rpc\enums">
83-
<UniqueIdentifier>{564EB540-D6B6-425C-0000-0000000000A2}</UniqueIdentifier>
89+
<UniqueIdentifier>{564EB540-D6B6-425C-0000-0000000000C2}</UniqueIdentifier>
8490
</Filter>
8591
<Filter Include="include\bitcoin\network\rpc\interfaces">
86-
<UniqueIdentifier>{564EB540-D6B6-425C-0000-0000000000B2}</UniqueIdentifier>
92+
<UniqueIdentifier>{564EB540-D6B6-425C-0000-0000000000D2}</UniqueIdentifier>
8793
</Filter>
8894
<Filter Include="include\bitcoin\network\sessions">
8995
<UniqueIdentifier>{564EB540-D6B6-425C-0000-0000000000D1}</UniqueIdentifier>
9096
</Filter>
9197
<Filter Include="resource">
92-
<UniqueIdentifier>{564EB540-D6B6-425C-0000-0000000000C2}</UniqueIdentifier>
98+
<UniqueIdentifier>{564EB540-D6B6-425C-0000-0000000000E2}</UniqueIdentifier>
9399
</Filter>
94100
<Filter Include="src">
95101
<UniqueIdentifier>{564EB540-D6B6-425C-0000-000000000000}</UniqueIdentifier>
@@ -895,6 +901,9 @@
895901
<None Include="..\..\..\..\include\bitcoin\network\impl\async\unsubscriber.ipp">
896902
<Filter>include\bitcoin\network\impl\async</Filter>
897903
</None>
904+
<None Include="..\..\..\..\include\bitcoin\network\impl\messages\json\body.ipp">
905+
<Filter>include\bitcoin\network\impl\messages\json</Filter>
906+
</None>
898907
<None Include="..\..\..\..\include\bitcoin\network\impl\rpc\broadcaster.ipp">
899908
<Filter>include\bitcoin\network\impl\rpc</Filter>
900909
</None>
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
/**
2+
* Copyright (c) 2011-2025 libbitcoin developers (see AUTHORS)
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+
#ifndef LIBBITCOIN_NETWORK_MESSAGES_JSON_BODY_IPP
20+
#define LIBBITCOIN_NETWORK_MESSAGES_JSON_BODY_IPP
21+
22+
#include <memory>
23+
#include <utility>
24+
#include <bitcoin/network/define.hpp>
25+
26+
namespace libbitcoin {
27+
namespace network {
28+
namespace json {
29+
30+
// json::body<>::reader
31+
// ----------------------------------------------------------------------------
32+
33+
TEMPLATE
34+
void CLASS::reader::init(const http::length_type& length,
35+
boost_code& ec) NOEXCEPT
36+
{
37+
BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
38+
const auto value = length.get_value_or(zero);
39+
BC_POP_WARNING()
40+
41+
using namespace system;
42+
if (is_limited<size_t>(value))
43+
{
44+
using namespace network::error;
45+
ec = to_http_code(http_error_t::buffer_overflow);
46+
return;
47+
}
48+
49+
BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
50+
expected_ = possible_narrow_cast<size_t>(value);
51+
BC_POP_WARNING()
52+
53+
parser_.reset();
54+
total_ = zero;
55+
ec.clear();
56+
}
57+
58+
TEMPLATE
59+
size_t CLASS::reader::put(const buffer_type& buffer, boost_code& ec) NOEXCEPT
60+
{
61+
using namespace system;
62+
using namespace network::error;
63+
64+
try
65+
{
66+
const auto data = pointer_cast<const char>(buffer.data());
67+
const auto parsed = parser_.write_some(data, buffer.size(), ec);
68+
69+
total_ = ceilinged_add(total_, parsed);
70+
if (!ec && total_ > expected_.value_or(max_size_t))
71+
ec = to_http_code(http_error_t::body_limit);
72+
73+
return parsed;
74+
}
75+
catch (const boost::system::system_error& e)
76+
{
77+
// Primary exception type for parsing operations.
78+
ec = e.code();
79+
}
80+
catch (...)
81+
{
82+
// As a catch-all we blame alloc.
83+
ec = to_http_code(http_error_t::bad_alloc);
84+
}
85+
86+
return {};
87+
}
88+
89+
TEMPLATE
90+
void CLASS::reader::finish(boost_code& ec) NOEXCEPT
91+
{
92+
BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
93+
parser_.finish(ec);
94+
BC_POP_WARNING()
95+
96+
if (ec) return;
97+
98+
try
99+
{
100+
value_.model = parser_.release();
101+
}
102+
catch (const boost::system::system_error& e)
103+
{
104+
// Primary exception type for parsing operations.
105+
ec = e.code();
106+
}
107+
catch (...)
108+
{
109+
// As a catch-all we blame alloc.
110+
using namespace network::error;
111+
ec = to_http_code(http_error_t::bad_alloc);
112+
}
113+
114+
parser_.reset();
115+
}
116+
117+
// json::body<>::writer
118+
// ----------------------------------------------------------------------------
119+
120+
TEMPLATE
121+
void CLASS::writer::init(boost_code& ec) NOEXCEPT
122+
{
123+
using namespace system;
124+
if (!value_.buffer)
125+
{
126+
// Caller controls max_size and other buffer behavior by assigning it.
127+
value_.buffer = emplace_shared<http::flat_buffer>(default_buffer);
128+
}
129+
else
130+
{
131+
// Caller has assigned the buffer (or just reused the response).
132+
value_.buffer->consume(value_.buffer->size());
133+
}
134+
135+
ec.clear();
136+
serializer_.reset(&value_.model);
137+
}
138+
139+
TEMPLATE
140+
CLASS::writer::out_buffer
141+
CLASS::writer::get(boost_code& ec) NOEXCEPT
142+
{
143+
ec.clear();
144+
if (serializer_.done())
145+
return {};
146+
147+
using namespace network::error;
148+
const auto size = value_.buffer->max_size();
149+
if (is_zero(size))
150+
{
151+
ec = to_http_code(http_error_t::buffer_overflow);
152+
return {};
153+
}
154+
155+
try
156+
{
157+
// Always prepares the configured max_size.
158+
const auto buffer = value_.buffer->prepare(size);
159+
const auto data = system::pointer_cast<char>(buffer.data());
160+
const auto view = serializer_.read(data, buffer.size());
161+
162+
// No progress (edge case).
163+
if (view.empty() && !serializer_.done())
164+
{
165+
ec = to_http_code(http_error_t::unexpected_body);
166+
return {};
167+
}
168+
169+
value_.buffer->commit(view.size());
170+
value_.buffer->consume(view.size());
171+
const auto more = !serializer_.done();
172+
return out_buffer{ std::make_pair(boost::asio::buffer(view), more) };
173+
}
174+
catch (const boost::system::system_error& e)
175+
{
176+
// Primary exception type for parsing operations.
177+
ec = e.code();
178+
}
179+
catch (...)
180+
{
181+
// As a catch-all we blame alloc.
182+
ec = to_http_code(http_error_t::bad_alloc);
183+
}
184+
185+
return {};
186+
}
187+
188+
} // namespace json
189+
} // namespace network
190+
} // namespace libbitcoin
191+
192+
#endif

include/bitcoin/network/messages/json/body.hpp

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -27,27 +27,30 @@ namespace libbitcoin {
2727
namespace network {
2828
namespace json {
2929

30+
/// Content passed to/from reader/writer via request/response.
31+
/// `static uint64_t size(const value_type&)` must be defined for beast to
32+
/// produce `content_length`, otherwise the response is chunked. Predeter-
33+
/// mining size would have the effect of eliminating the benefit of
34+
/// streaming serialize.
35+
struct json_value
36+
{
37+
/// JSON document object model to parse/serialize.
38+
boost::json::value model{};
39+
40+
/// Used by channel to resize reusable buffer.
41+
size_t size_hint{};
42+
43+
/// Writer serialization buffer (max size, allocated on write).
44+
mutable http::flat_buffer_ptr buffer{};
45+
};
46+
3047
/// boost::beast::http body for JSON messages.
3148
/// Because of the parser and serializer members, neither the reader nor writer
3249
/// is movable and as such must be in-place contructed (e.g. variant contruct).
50+
template <typename Value = json_value>
3351
struct BCT_API body
3452
{
35-
/// Content passed to/from reader/writer via request/response.
36-
/// `static uint64_t size(const value_type&)` must be defined for beast to
37-
/// produce `content_length`, otherwise the response is chunked. Predeter-
38-
/// mining size would have the effect of eliminating the benefit of
39-
/// streaming serialize.
40-
struct value_type
41-
{
42-
/// JSON DOM.
43-
boost::json::value model{};
44-
45-
/// Used by channel to resize reusable buffer.
46-
size_t size_hint{};
47-
48-
/// Writer serialization buffer (max size, allocated on write).
49-
mutable http::flat_buffer_ptr buffer{};
50-
};
53+
using value_type = Value;
5154

5255
class reader
5356
{
@@ -66,6 +69,7 @@ struct BCT_API body
6669
{
6770
}
6871

72+
virtual ~reader() = default;
6973
virtual void init(const http::length_type& length, boost_code& ec) NOEXCEPT;
7074
virtual size_t put(const buffer_type& buffer, boost_code& ec) NOEXCEPT;
7175
virtual void finish(boost_code& ec) NOEXCEPT;
@@ -97,6 +101,7 @@ struct BCT_API body
97101
{
98102
}
99103

104+
virtual ~writer() = default;
100105
virtual void init(boost_code& ec) NOEXCEPT;
101106
virtual out_buffer get(boost_code& ec) NOEXCEPT;
102107

@@ -117,10 +122,18 @@ namespace libbitcoin {
117122
namespace network {
118123
namespace http {
119124

120-
using json_body = json::body;
125+
using json_body = json::body<>;
121126

122127
} // namespace http
123128
} // namespace network
124129
} // namespace libbitcoin
125130

131+
#define TEMPLATE template <typename Value>
132+
#define CLASS body<Value>
133+
134+
#include <bitcoin/network/impl/messages/json/body.ipp>
135+
136+
#undef CLASS
137+
#undef TEMPLATE
138+
126139
#endif

0 commit comments

Comments
 (0)