Exercise real ConnectAttempt refusal, TLS handshake failure and timeout, HTTP rejection and timeout, and explicit stop. Cover PeerImp stop and graceful TLS shutdown with real loopback TLS streams, peerfinder slots, and bounded io_context runs.
Rearm the ConnectAttempt deadline before reading the HTTP upgrade response. This missing deadline predates Boost port #5570; the new stalled-response regression targets it. The real 15-second handshake and response timers run concurrently.
Formatting and whitespace checks pass. Build, runtime control, and coverage measurement are delegated to Claude using the existing release build.
(cherry picked from commit 28b8bc1a217e60284666417c9a45525bb14f86bd)
Clear the prior host error before arming a new deadline. On timeout, close the underlying transport instead of waiting for graceful TLS shutdown, and preserve timed_out through header completion.
Add bounded regression cases for fallback after connection refusal, a silent TLS response, and a stalled TLS handshake. Formatting and whitespace checks pass; runtime validation is delegated to Claude at the operator request.
(cherry picked from commit 750fa35b872747fcd6cf5957af3466464a02224c)
codecov/patch on #812 flagged the ported plumbing as uncovered because
nothing had ever tested it: StatsDCollector (0%), ResolverAsio (26%),
Spawn.h (50%). Three bounded suites:
- ripple.insight.StatsDCollector: a loopback UDP sink; one of each metric
plus a hook; asserts the datagrams after the 1 s flush tick (dispatch +
bind_executor posts, expires_after tick, async_send) and that teardown
(cancel() in try/catch, work guard, thread join) is prompt.
- ripple.basics.ResolverAsio: numeric host (results_type iteration), a
batch with an unparseable name (skipped without a callback, do_work
chaining), an unresolvable .invalid name (error -> empty list), stop()
with 200 names pending, stop_async() + stop().
- ripple.server.Spawn: the strand and plain-executor overloads, a real
suspension on the strand, and both branches of kPROPAGATE_EXCEPTIONS
(std::exception and a non-std throw escape io_context::run()).
Query is an aggregate; make_shared<Query>(a, b, c) needs C++20 parenthesised
aggregate initialisation, which GCC 11 accepts and CI's clang rejects
('no matching constructor'). Same code upstream, which builds with a newer
clang.
XRPLF #5570 rewrote expires_from_now(mTimeout, ec) as try/catch around
expires_after() and moved the async_wait into the catch block, so the
deadline was only registered when arming it threw. A request to a server
that never answers therefore never timed out: the header read stayed
pending and io_context::run() never returned. Caught by #677's
ripple.net.HTTPClient 'Socket cleanup after timeout', which hung the full
unit run deterministically on both the 1.88 and 1.91 builds (strace: no
timer pending on the test's reactor, handleDeadline never ran). Upstream
has the same code and no test for it.
Exercise raw stream and composed HTTP reads and writes after expiry, both on and outside a TS executor strand. Check one asynchronous timeout completion on the bound strand with zero bytes.
Covers boostorg/beast issues #2925 and #2941 without network traffic, sleeps, or a dependency patch. The actual suite passes 8 cases and 40 checks on Boost 1.91 with GCC 11 and Apple Clang; stock Boost 1.86 fails with bad_executor.
(cherry picked from commit cfaf019903d836c2dab3b396324e758281b3c510)
boost 1.91's conan recipe makes Boost.System header-only and no longer
exports a boost::system component. Two references broke conan graph
generation: nudb 2.0.8's recipe (bump to 2.0.9, whose recipe requires
boost::thread + boost::headers, matching XRPLF at boost 1.91) and our
cmake/deps/Boost.cmake link list (drop Boost::system, as XRPLF did in
their 1.90 bump). nudb is pulled from conancenter (not the unused vendored
external/nudb), so the version string is the whole fix.
XRPLF's 1.88->1.90->1.91 path is version-string only for the parts that
apply to xahaud; the sole code change was 1.90 dropping the now-header-only
Boost::system from the link list, which is cleanup — the target still
exists at 1.91, so ripple_boost is left as-is. Other deps in XRPLF's bump
commits (protobuf/sqlite/zlib/jemalloc/libarchive) are their versions, not
ours, and are not touched.
Resolves the 28 conflicts from cherry-picking XRPLF/rippled 1506e65558
(#5570) onto origin/dev:
- io_service -> io_context everywhere upstream renamed it, keeping the
xahaud-only code around those lines (UDP door in ServerImpl, sticky
endpoint selection in WorkBase with bind_executor instead of
strand.wrap, useSqLiteStore in PeerfinderManager, RPCSub after #677).
- src/xrpld/rpc/detail/RPCSub.cpp (upstream path) dropped; xahaud's copy
lives at src/xrpld/net/detail/RPCSub.cpp and its local io_service is
renamed there.
- conanfile.py: boost/1.88.0 in xahaud's override form, without_cobalt on
gcc/clang; boost::boost kept.
- Boost 1.88 removes the io_service typedef and address::from_string and
makes Boost.Process v2 the default: the #677 tests move to io_context,
executor_work_guard and make_address; Main.cpp includes the v1 process
headers it already uses instead of the umbrella.
Not yet compiled.
Raw result of cherry-picking XRPLF/rippled 1506e65558 onto origin/dev
c208a40ce8: 78 files applied clean, 28 in conflict (36 hunks), conflict
markers left in place so the merge is visible. Resolution follows in the
next commit. src/xrpld/rpc/detail/RPCSub.cpp is upstream's copy of a file
xahaud moved to src/xrpld/net/detail/RPCSub.cpp (delete/modify).
(cherry picked from commit 1506e65558818ea73a2233d2a55c1d1441d5fb10)