Skip to content

Commit

Permalink
Implement -rpcwait and -rpcwaittimeout
Browse files Browse the repository at this point in the history
This is a Bitcoin compatible implementation of -rpcwait and -rpcwaittimeout

Specifying -rpcwait for the rpc client will cause it to wait for the
time specified with -rpcwaittimeout (in seconds) with one second attempts
to connect. If -rpcwaittimeout is specified as zero or less, the wait is
indefinite. Note that the current default value of -rpcwaittimeout is zero.
  • Loading branch information
jamescowens committed Sep 27, 2021
1 parent fc26b1d commit e0ff19c
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 2 deletions.
9 changes: 9 additions & 0 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ extern bool fUseFastIndex;
// Dump addresses to banlist.dat every 5 minutes (300 s)
static constexpr int DUMP_BANS_INTERVAL = 300;

// RPC client default timeout.
extern constexpr int DEFAULT_WAIT_CLIENT_TIMEOUT = 0;


std::unique_ptr<BanMan> g_banman;

/**
Expand Down Expand Up @@ -534,6 +538,11 @@ void SetupServerArgs()
ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
argsman.AddArg("-rpcuser=<user>", "Username for JSON-RPC connections",
ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE, OptionsCategory::RPC);
argsman.AddArg("-rpcwait", "Wait for RPC server to start.",
ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
argsman.AddArg("-rpcwaittimeout=<n>", strprintf("Timeout in seconds to wait for the RPC server to start, or 0 for no "
"timeout. (default: %d)", DEFAULT_WAIT_CLIENT_TIMEOUT),
ArgsManager::ALLOW_INT, OptionsCategory::RPC);
argsman.AddArg("-rpcpassword=<pw>", "Password for JSON-RPC connections",
ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE, OptionsCategory::RPC);
argsman.AddArg("-rpcport=<port>", strprintf("Listen for JSON-RPC connections on <port> (default: %u, testnet: %u)",
Expand Down
27 changes: 25 additions & 2 deletions src/rpc/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ using namespace std;
using namespace boost;
using namespace boost::asio;

/** The default timeout for an RPC client to wait for the RPC server to start in seconds */
static constexpr int DEFAULT_WAIT_CLIENT_TIMEOUT = 0;

UniValue CallRPC(const string& strMethod, const UniValue& params)
{
if (!gArgs.IsArgSet("-rpcuser") || !gArgs.IsArgSet("-rpcpassword"))
Expand All @@ -47,8 +50,28 @@ UniValue CallRPC(const string& strMethod, const UniValue& params)
asio::ssl::stream<asio::ip::tcp::socket> sslStream(io_context, context);
SSLIOStreamDevice<asio::ip::tcp> d(sslStream, fUseSSL);
iostreams::stream< SSLIOStreamDevice<asio::ip::tcp> > stream(d);
if (!d.connect(gArgs.GetArg("-rpcconnect", "127.0.0.1"), gArgs.GetArg("-rpcport", ToString(GetDefaultRPCPort()))))
throw runtime_error("couldn't connect to server");

bool fWait = gArgs.GetBoolArg("-rpcwait", false); // -rpcwait means try until server has started or timeout is reached
int timeout = gArgs.GetArg("-rpcwaittimeout", DEFAULT_WAIT_CLIENT_TIMEOUT); // The max time to wait

std::chrono::seconds deadline = GetTime<std::chrono::seconds>() + std::chrono::seconds{timeout};

do {
// If connection succeeds, immediately break. No need to wait.
if (d.connect(gArgs.GetArg("-rpcconnect", "127.0.0.1"),
gArgs.GetArg("-rpcport", ToString(GetDefaultRPCPort())))) {
break;
}

std::chrono::seconds now = GetTime<std::chrono::seconds>();

// Note that timeout <= 0 means wait until connected with one second between connection attempts.
if (fWait && (timeout <= 0 || now < deadline)) {
UninterruptibleSleep(std::chrono::seconds{1});
} else {
throw runtime_error("couldn't connect to server");
}
} while (true);

// HTTP basic authentication
string strUserPass64 = EncodeBase64(gArgs.GetArg("-rpcuser", "dummy") + ":" + gArgs.GetArg("-rpcpassword", "dummy"));
Expand Down

0 comments on commit e0ff19c

Please sign in to comment.