Skip to content

Commit 2d73549

Browse files
authored
Merge pull request #722 from evoskuil/master
Json-rpc related fixes and improvements.
2 parents 595c7d2 + 549cda7 commit 2d73549

File tree

8 files changed

+159
-53
lines changed

8 files changed

+159
-53
lines changed

include/bitcoin/network/error.hpp

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ enum error_t : uint8_t
301301
exhausted_variants,
302302
unknown_name,
303303

304-
// rpc error
304+
// query string parse error
305305
message_overflow,
306306
undefined_type,
307307
unexpected_method,
@@ -310,7 +310,17 @@ enum error_t : uint8_t
310310
extra_named,
311311
missing_array,
312312
missing_object,
313-
missing_parameter
313+
missing_parameter,
314+
315+
// json-rpc error
316+
jsonrpc_requires_method,
317+
jsonrpc_v1_requires_params,
318+
jsonrpc_v1_requires_array_params,
319+
jsonrpc_v1_requires_id,
320+
jsonrpc_reader_bad_buffer,
321+
jsonrpc_reader_stall,
322+
jsonrpc_reader_exception,
323+
jsonrpc_writer_exception
314324
};
315325

316326
// No current need for error_code equivalence mapping.
@@ -364,6 +374,9 @@ BCT_API code ws_to_error_code(const boost_code& ec) NOEXCEPT;
364374
/// 1:1 mapping of boost::json::error to network (or error::unknown).
365375
BCT_API code json_to_error_code(const boost_code& ec) NOEXCEPT;
366376

377+
/// 1:1 mapping of wrapped json-rpc codes back to network (or error::unknown).
378+
BCT_API code rpc_to_error_code(const boost_code& ec) NOEXCEPT;
379+
367380
} // namespace error
368381
} // namespace network
369382
} // namespace libbitcoin

include/bitcoin/network/impl/channels/channel_rpc.ipp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ inline void CLASS::handle_receive(const code& ec, size_t bytes,
107107
identity_ = request->message.id;
108108
version_ = request->message.jsonrpc;
109109

110-
LOGA("Rpc request: (" << bytes << ") bytes from [" << endpoint() << "] "
110+
LOGA("Rpc request : (" << bytes << ") bytes [" << endpoint() << "] "
111111
<< request->message.method << "(...).");
112112

113113
reading_ = false;
@@ -187,7 +187,7 @@ inline void CLASS::handle_send(const code& ec, size_t bytes,
187187
// Typically a noop, but handshake may pause channel here.
188188
handler(ec);
189189

190-
LOGA("Rpc response: (" << bytes << ") bytes to [" << endpoint() << "] "
190+
LOGA("Rpc response: (" << bytes << ") bytes [" << endpoint() << "] "
191191
<< response->message.error.value_or(rpc::result_t{}).message);
192192

193193
// Continue read loop (does not unpause or restart channel).

include/bitcoin/network/impl/messages/json_body.ipp

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -89,18 +89,17 @@ size_t CLASS::reader::put(const buffer_type& buffer, boost_code& ec) NOEXCEPT
8989
TEMPLATE
9090
void CLASS::reader::finish(boost_code& ec) NOEXCEPT
9191
{
92-
using namespace network::error;
93-
if (!done())
94-
{
95-
ec = to_http_code(http_error_t::partial_message);
96-
return;
97-
}
92+
ec.clear();
9893

99-
BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
100-
parser_.finish(ec);
101-
BC_POP_WARNING()
94+
// Calling parser.finish() when parser.done() results in error::incomplete.
95+
if (!parser_.done())
96+
{
97+
BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
98+
parser_.finish(ec);
99+
BC_POP_WARNING()
102100

103-
if (ec) return;
101+
if (ec) return;
102+
}
104103

105104
try
106105
{
@@ -114,6 +113,7 @@ void CLASS::reader::finish(boost_code& ec) NOEXCEPT
114113
catch (...)
115114
{
116115
// As a catch-all we blame alloc.
116+
using namespace network::error;
117117
ec = to_http_code(http_error_t::bad_alloc);
118118
}
119119

include/bitcoin/network/net/socket.hpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,13 +224,14 @@ class BCT_API socket
224224
{
225225
typedef std::shared_ptr<read_rpc> ptr;
226226

227-
read_rpc(rpc::request& request) NOEXCEPT
228-
: value{ request }, reader{ value }
227+
read_rpc(rpc::request& request, http::flat_buffer& buffer) NOEXCEPT
228+
: value{ request }, reader{ value }, buffer{ buffer }
229229
{
230230
}
231231

232232
rpc::request& value;
233233
rpc::reader reader;
234+
http::flat_buffer& buffer;
234235
};
235236

236237
struct write_rpc

src/error.cpp

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ DEFINE_ERROR_T_MESSAGE_MAP(error)
272272
{ exhausted_variants, "exhausted variants" },
273273
{ unknown_name, "unknown name" },
274274

275-
// rpc error
275+
// query string parse error
276276
{ message_overflow, "message overflow" },
277277
{ undefined_type, "undefined type" },
278278
{ unexpected_method, "unexpected method" },
@@ -281,7 +281,17 @@ DEFINE_ERROR_T_MESSAGE_MAP(error)
281281
{ extra_named, "extra named" },
282282
{ missing_array, "missing array" },
283283
{ missing_object, "missing object" },
284-
{ missing_parameter, "missing parameter" }
284+
{ missing_parameter, "missing parameter" },
285+
286+
// json-rpc error
287+
{ jsonrpc_requires_method, "jsonrpc requires method" },
288+
{ jsonrpc_v1_requires_params, "jsonrpc v1 requires params" },
289+
{ jsonrpc_v1_requires_array_params, "jsonrpc v1 requires array params" },
290+
{ jsonrpc_v1_requires_id, "jsonrpc v1 requires id" },
291+
{ jsonrpc_reader_bad_buffer, "jsonrpc reader bad buffer" },
292+
{ jsonrpc_reader_stall, "jsonrpc reader stall" },
293+
{ jsonrpc_reader_exception, "jsonrpc reader exception" },
294+
{ jsonrpc_writer_exception, "jsonrpc writer exception" }
285295
};
286296

287297
DEFINE_ERROR_T_CATEGORY(error, "network", "network code")
@@ -294,6 +304,7 @@ bool asio_is_canceled(const boost_code& ec) NOEXCEPT
294304
|| ec == boost::asio::error::operation_aborted;
295305
}
296306

307+
// TODO: change to cast model (like others).
297308
// The success and operation_canceled codes are the only expected in normal
298309
// operation, so these are first, to optimize the case where asio_is_canceled
299310
// is not used. boost_code overloads the `==` operator to include category.
@@ -440,15 +451,30 @@ code ssl_to_error_code(const boost_code& ec) NOEXCEPT
440451

441452
switch (static_cast<asio_ssl_stream_error_t>(ec.value()))
442453
{
443-
case asio_ssl_stream_error_t::stream_truncated: return error::tls_stream_truncated;
444-
case asio_ssl_stream_error_t::unspecified_system_error: return error::tls_unspecified_system_error;
445-
case asio_ssl_stream_error_t::unexpected_result: return error::tls_unexpected_result;
454+
case asio_ssl_stream_error_t::stream_truncated:
455+
return error::tls_stream_truncated;
456+
case asio_ssl_stream_error_t::unspecified_system_error:
457+
return error::tls_unspecified_system_error;
458+
case asio_ssl_stream_error_t::unexpected_result:
459+
return error::tls_unexpected_result;
446460

447461
// TODO: return ssl category generic error.
448462
default: return error::unknown;
449463
}
450464
}
451465

466+
// includes http codes
467+
code rpc_to_error_code(const boost_code& ec) NOEXCEPT
468+
{
469+
if (!ec)
470+
return error::success;
471+
472+
if (ec.category() == network::error::error_category::singleton)
473+
return { static_cast<error_t>(ec.value()) };
474+
475+
return http_to_error_code(ec);
476+
}
477+
452478
// includes json codes
453479
code http_to_error_code(const boost_code& ec) NOEXCEPT
454480
{

src/messages/rpc/body.cpp

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ put(const buffer_type& buffer, boost_code& ec) NOEXCEPT
5151

5252
if (is_null(buffer.data()))
5353
{
54-
ec = to_system_code(boost_error_t::bad_address);
54+
ec = code{ error::jsonrpc_reader_bad_buffer };
5555
return {};
5656
}
5757

@@ -67,7 +67,7 @@ put(const buffer_type& buffer, boost_code& ec) NOEXCEPT
6767
// There is no terminator (terminal).
6868
if (is_zero(parsed))
6969
{
70-
ec = to_http_code(http_error_t::end_of_stream);
70+
ec = code{ error::jsonrpc_reader_stall };
7171
return parsed;
7272
}
7373

@@ -115,33 +115,27 @@ finish(boost_code& ec) NOEXCEPT
115115
}
116116
catch (...)
117117
{
118-
// As a catch-all we blame alloc.
119-
ec = to_http_code(http_error_t::bad_alloc);
118+
ec = code{ error::jsonrpc_reader_exception };
120119
}
121120

122-
// Post-parse semantic validation.
123-
121+
// Set version default.
124122
if (value_.message.jsonrpc == version::undefined)
125123
value_.message.jsonrpc = version::v1;
126124

127-
if (value_.message.method.empty() ||
128-
!value_.message.params.has_value())
125+
// Post-parse semantic validation.
126+
if (value_.message.method.empty())
129127
{
130-
ec = to_system_code(boost_error_t::bad_message);
131-
return;
128+
ec = code{ error::jsonrpc_requires_method };
132129
}
133-
134-
if (value_.message.jsonrpc == version::v1)
130+
else if (value_.message.jsonrpc == version::v1)
135131
{
136-
if (!value_.message.id.has_value())
137-
ec = to_system_code(boost_error_t::bad_message);
132+
if (!value_.message.params.has_value())
133+
ec = code{ error::jsonrpc_v1_requires_params };
134+
else if (!value_.message.id.has_value())
135+
ec = code{ error::jsonrpc_v1_requires_id };
138136
else if (!std::holds_alternative<array_t>(
139137
value_.message.params.value()))
140-
ec = to_system_code(boost_error_t::bad_message);
141-
142-
// TODO: v1 batch is not allowed.
143-
////else if (value_.message.is_batch())
144-
//// ec = to_system_code(boost_error_t::bad_message);
138+
ec = code{ error::jsonrpc_v1_requires_array_params };
145139
}
146140
}
147141

@@ -190,8 +184,7 @@ init(boost_code& ec) NOEXCEPT
190184
}
191185
catch (...)
192186
{
193-
// As a catch-all we blame alloc.
194-
ec = to_http_code(http_error_t::bad_alloc);
187+
ec = code{ error::jsonrpc_writer_exception };
195188
return;
196189
}
197190

src/net/socket_rpc.cpp

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,7 @@ void socket::rpc_read(http::flat_buffer& buffer, rpc::request& request,
4343
count_handler&& handler) NOEXCEPT
4444
{
4545
boost_code ec{};
46-
const auto in = emplace_shared<read_rpc>(request);
47-
in->value.buffer = emplace_shared<http::flat_buffer>(buffer);
46+
const auto in = emplace_shared<read_rpc>(request, buffer);
4847
in->reader.init({}, ec);
4948

5049
boost::asio::dispatch(strand_,
@@ -60,15 +59,15 @@ void socket::do_rpc_read(boost_code ec, size_t total, const read_rpc::ptr& in,
6059

6160
if (ec)
6261
{
63-
// Json parser emits http and json codes.
64-
const auto code = error::http_to_error_code(ec);
62+
// Json parser emits rpc, http and json codes.
63+
const auto code = error::rpc_to_error_code(ec);
6564
if (code == error::unknown) logx("rpc-read", ec);
6665
handler(code, total);
6766
return;
6867
}
6968

7069
VARIANT_DISPATCH_METHOD(get_tcp(),
71-
async_read_some(in->value.buffer->prepare(size),
70+
async_read_some(in->buffer.prepare(size),
7271
std::bind(&socket::handle_rpc_read,
7372
shared_from_this(), _1, _2, total, in, handler)));
7473
}
@@ -93,12 +92,12 @@ void socket::handle_rpc_read(boost_code ec, size_t size, size_t total,
9392

9493
if (!ec)
9594
{
96-
in->value.buffer->commit(size);
97-
const auto data = in->value.buffer->data();
95+
in->buffer.commit(size);
96+
const auto data = in->buffer.data();
9897
const auto parsed = in->reader.put(data, ec);
9998
if (!ec)
10099
{
101-
in->value.buffer->consume(parsed);
100+
in->buffer.consume(parsed);
102101
if (in->reader.done())
103102
{
104103
in->reader.finish(ec);
@@ -138,8 +137,8 @@ void socket::do_rpc_write(boost_code ec, size_t total,
138137
const auto buffer = ec ? write_rpc::out_buffer{} : out->writer.get(ec);
139138
if (ec)
140139
{
141-
// Json serializer emits http and json codes.
142-
const auto code = error::http_to_error_code(ec);
140+
// Json serializer emits rpc, http and json codes.
141+
const auto code = error::rpc_to_error_code(ec);
143142
if (code == error::unknown) logx("rpc-write", ec);
144143
handler(code, total);
145144
return;

test/error.cpp

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1954,7 +1954,7 @@ BOOST_AUTO_TEST_CASE(error_t__code__unknown_name__true_expected_message)
19541954
BOOST_REQUIRE_EQUAL(ec.message(), "unknown name");
19551955
}
19561956

1957-
// rpc error
1957+
// query string parse error
19581958

19591959
BOOST_AUTO_TEST_CASE(error_t__code__message_overflow__true_expected_message)
19601960
{
@@ -2037,4 +2037,78 @@ BOOST_AUTO_TEST_CASE(error_t__code__missing_parameter__true_expected_message)
20372037
BOOST_REQUIRE_EQUAL(ec.message(), "missing parameter");
20382038
}
20392039

2040+
// json-rpc error
2041+
2042+
BOOST_AUTO_TEST_CASE(error_t__code__jsonrpc_requires_method__true_expected_message)
2043+
{
2044+
constexpr auto value = error::jsonrpc_requires_method;
2045+
const auto ec = code(value);
2046+
BOOST_REQUIRE(ec);
2047+
BOOST_REQUIRE(ec == value);
2048+
BOOST_REQUIRE_EQUAL(ec.message(), "jsonrpc requires method");
2049+
}
2050+
2051+
BOOST_AUTO_TEST_CASE(error_t__code__jsonrpc_v1_requires_params__true_expected_message)
2052+
{
2053+
constexpr auto value = error::jsonrpc_v1_requires_params;
2054+
const auto ec = code(value);
2055+
BOOST_REQUIRE(ec);
2056+
BOOST_REQUIRE(ec == value);
2057+
BOOST_REQUIRE_EQUAL(ec.message(), "jsonrpc v1 requires params");
2058+
}
2059+
2060+
BOOST_AUTO_TEST_CASE(error_t__code__jsonrpc_v1_requires_array_params__true_expected_message)
2061+
{
2062+
constexpr auto value = error::jsonrpc_v1_requires_array_params;
2063+
const auto ec = code(value);
2064+
BOOST_REQUIRE(ec);
2065+
BOOST_REQUIRE(ec == value);
2066+
BOOST_REQUIRE_EQUAL(ec.message(), "jsonrpc v1 requires array params");
2067+
}
2068+
2069+
BOOST_AUTO_TEST_CASE(error_t__code__jsonrpc_v1_requires_id__true_expected_message)
2070+
{
2071+
constexpr auto value = error::jsonrpc_v1_requires_id;
2072+
const auto ec = code(value);
2073+
BOOST_REQUIRE(ec);
2074+
BOOST_REQUIRE(ec == value);
2075+
BOOST_REQUIRE_EQUAL(ec.message(), "jsonrpc v1 requires id");
2076+
}
2077+
2078+
BOOST_AUTO_TEST_CASE(error_t__code__jsonrpc_reader_bad_buffer__true_expected_message)
2079+
{
2080+
constexpr auto value = error::jsonrpc_reader_bad_buffer;
2081+
const auto ec = code(value);
2082+
BOOST_REQUIRE(ec);
2083+
BOOST_REQUIRE(ec == value);
2084+
BOOST_REQUIRE_EQUAL(ec.message(), "jsonrpc reader bad buffer");
2085+
}
2086+
2087+
BOOST_AUTO_TEST_CASE(error_t__code__jsonrpc_reader_stall__true_expected_message)
2088+
{
2089+
constexpr auto value = error::jsonrpc_reader_stall;
2090+
const auto ec = code(value);
2091+
BOOST_REQUIRE(ec);
2092+
BOOST_REQUIRE(ec == value);
2093+
BOOST_REQUIRE_EQUAL(ec.message(), "jsonrpc reader stall");
2094+
}
2095+
2096+
BOOST_AUTO_TEST_CASE(error_t__code__jsonrpc_reader_exception__true_expected_message)
2097+
{
2098+
constexpr auto value = error::jsonrpc_reader_exception;
2099+
const auto ec = code(value);
2100+
BOOST_REQUIRE(ec);
2101+
BOOST_REQUIRE(ec == value);
2102+
BOOST_REQUIRE_EQUAL(ec.message(), "jsonrpc reader exception");
2103+
}
2104+
2105+
BOOST_AUTO_TEST_CASE(error_t__code__jsonrpc_writer_exception__true_expected_message)
2106+
{
2107+
constexpr auto value = error::jsonrpc_writer_exception;
2108+
const auto ec = code(value);
2109+
BOOST_REQUIRE(ec);
2110+
BOOST_REQUIRE(ec == value);
2111+
BOOST_REQUIRE_EQUAL(ec.message(), "jsonrpc writer exception");
2112+
}
2113+
20402114
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)