From af82884ab7c485c8b4c5ac93c308127c39c196be Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Wed, 29 Oct 2014 18:08:31 +0100 Subject: Add "warmup mode" for RPC server. Start the RPC server before doing all the (expensive) startup initialisations like loading the block index. Until the node is ready, return all calls immediately with a new error signalling "in warmup" with an appropriate status message (similar to the init message). This is useful for RPC clients to know that the server is there (e. g., they don't have to start it) but not yet available. It is used in Namecoin and Huntercoin already for some time, and there exists a UI hooked onto the RPC interface that actively uses this to its advantage. --- src/bitcoin-cli.cpp | 85 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 55 insertions(+), 30 deletions(-) (limited to 'src/bitcoin-cli.cpp') diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index 38fbc29faf..11840e62a8 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -46,6 +46,21 @@ std::string HelpMessageCli() // // Start // + +// +// Exception thrown on connection error. This error is used to determine +// when to wait if -rpcwait is given. +// +class CConnectionFailed : public std::runtime_error +{ +public: + + explicit inline CConnectionFailed(const std::string& msg) : + std::runtime_error(msg) + {} + +}; + static bool AppInitRPC(int argc, char* argv[]) { // @@ -101,15 +116,9 @@ Object CallRPC(const string& strMethod, const Array& params) SSLIOStreamDevice d(sslStream, fUseSSL); iostreams::stream< SSLIOStreamDevice > stream(d); - bool fWait = GetBoolArg("-rpcwait", false); // -rpcwait means try until server has started - do { - bool fConnected = d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", itostr(BaseParams().RPCPort()))); - if (fConnected) break; - if (fWait) - MilliSleep(1000); - else - throw runtime_error("couldn't connect to server"); - } while (fWait); + const bool fConnected = d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", itostr(BaseParams().RPCPort()))); + if (!fConnected) + throw CConnectionFailed("couldn't connect to server"); // HTTP basic authentication string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]); @@ -168,27 +177,43 @@ int CommandLineRPC(int argc, char *argv[]) std::vector strParams(&argv[2], &argv[argc]); Array params = RPCConvertValues(strMethod, strParams); - // Execute - Object reply = CallRPC(strMethod, params); - - // Parse reply - const Value& result = find_value(reply, "result"); - const Value& error = find_value(reply, "error"); - - if (error.type() != null_type) { - // Error - strPrint = "error: " + write_string(error, false); - int code = find_value(error.get_obj(), "code").get_int(); - nRet = abs(code); - } else { - // Result - if (result.type() == null_type) - strPrint = ""; - else if (result.type() == str_type) - strPrint = result.get_str(); - else - strPrint = write_string(result, true); - } + // Execute and handle connection failures with -rpcwait + const bool fWait = GetBoolArg("-rpcwait", false); + do { + try { + const Object reply = CallRPC(strMethod, params); + + // Parse reply + const Value& result = find_value(reply, "result"); + const Value& error = find_value(reply, "error"); + + if (error.type() != null_type) { + // Error + const int code = find_value(error.get_obj(), "code").get_int(); + if (fWait && code == RPC_IN_WARMUP) + throw CConnectionFailed("server in warmup"); + strPrint = "error: " + write_string(error, false); + nRet = abs(code); + } else { + // Result + if (result.type() == null_type) + strPrint = ""; + else if (result.type() == str_type) + strPrint = result.get_str(); + else + strPrint = write_string(result, true); + } + + // Connection succeeded, no need to retry. + break; + } + catch (const CConnectionFailed& e) { + if (fWait) + MilliSleep(1000); + else + throw; + } + } while (fWait); } catch (boost::thread_interrupted) { throw; -- cgit v1.2.3