]> Git Repo - VerusCoin.git/blame - src/rpcserver.cpp
tests: GET requests cannot have request body, use POST in rest.py
[VerusCoin.git] / src / rpcserver.cpp
CommitLineData
69d605f4 1// Copyright (c) 2010 Satoshi Nakamoto
f914f1a7 2// Copyright (c) 2009-2014 The Bitcoin Core developers
77920402 3// Distributed under the MIT software license, see the accompanying
3a25a2b9 4// file COPYING or http://www.opensource.org/licenses/mit-license.php.
69d605f4 5
fb78cc23 6#include "rpcserver.h"
51ed9ec9
BD
7
8#include "base58.h"
0eeb4f5d 9#include "init.h"
4401b2d7
EL
10#include "random.h"
11#include "sync.h"
48ba56cd 12#include "ui_interface.h"
c037531d 13#include "util.h"
4401b2d7 14#include "utilstrencodings.h"
48ba56cd 15#ifdef ENABLE_WALLET
50c72f23 16#include "wallet/wallet.h"
48ba56cd 17#endif
fc72c078
S
18#include "asyncrpcqueue.h"
19
20#include <memory>
51ed9ec9 21
92f2c1fe 22#include <boost/algorithm/string.hpp>
69d605f4 23#include <boost/asio.hpp>
92f2c1fe 24#include <boost/asio/ssl.hpp>
914dc012 25#include <boost/bind.hpp>
95d888a6 26#include <boost/filesystem.hpp>
a0780ba0 27#include <boost/foreach.hpp>
69d605f4
WL
28#include <boost/iostreams/concepts.hpp>
29#include <boost/iostreams/stream.hpp>
914dc012 30#include <boost/shared_ptr.hpp>
4401b2d7 31#include <boost/signals2/signal.hpp>
ad49c256 32#include <boost/thread.hpp>
ed21d5bd 33
a10a6e2a 34#include <univalue.h>
5ce4c2a2 35
69d605f4 36using namespace boost::asio;
4401b2d7 37using namespace RPCServer;
40a158e1 38using namespace std;
69d605f4 39
f81ce5bd
GA
40static std::string strRPCUserColonPass;
41
ff6a7af1 42static bool fRPCRunning = false;
af82884a
DK
43static bool fRPCInWarmup = true;
44static std::string rpcWarmupStatus("RPC server started");
45static CCriticalSection cs_rpcWarmup;
46
77920402 47//! These are created by StartRPCThreads, destroyed in StopRPCThreads
a3241998 48static boost::asio::io_service* rpc_io_service = NULL;
92f2c1fe 49static map<string, boost::shared_ptr<deadline_timer> > deadlineTimers;
21eb5ada
GA
50static ssl::context* rpc_ssl_context = NULL;
51static boost::thread_group* rpc_worker_group = NULL;
a8db31c8 52static boost::asio::io_service::work *rpc_dummy_work = NULL;
ee219125 53static std::vector<CSubNet> rpc_allow_subnets; //!< List of subnets to allow RPC connections from
cef44941 54static std::vector< boost::shared_ptr<ip::tcp::acceptor> > rpc_acceptors;
e9205293 55
4401b2d7
EL
56static struct CRPCSignals
57{
58 boost::signals2::signal<void ()> Started;
59 boost::signals2::signal<void ()> Stopped;
60 boost::signals2::signal<void (const CRPCCommand&)> PreCommand;
61 boost::signals2::signal<void (const CRPCCommand&)> PostCommand;
62} g_rpcSignals;
63
64void RPCServer::OnStarted(boost::function<void ()> slot)
65{
66 g_rpcSignals.Started.connect(slot);
67}
68
69void RPCServer::OnStopped(boost::function<void ()> slot)
70{
71 g_rpcSignals.Stopped.connect(slot);
72}
73
74void RPCServer::OnPreCommand(boost::function<void (const CRPCCommand&)> slot)
75{
76 g_rpcSignals.PreCommand.connect(boost::bind(slot, _1));
77}
78
79void RPCServer::OnPostCommand(boost::function<void (const CRPCCommand&)> slot)
80{
81 g_rpcSignals.PostCommand.connect(boost::bind(slot, _1));
82}
83
d014114d
JS
84void RPCTypeCheck(const UniValue& params,
85 const list<UniValue::VType>& typesExpected,
cc6dfd1f 86 bool fAllowNull)
899d373b 87{
cc71666a 88 size_t i = 0;
d014114d 89 BOOST_FOREACH(UniValue::VType t, typesExpected)
899d373b
GA
90 {
91 if (params.size() <= i)
92 break;
93
d014114d 94 const UniValue& v = params[i];
ed21d5bd 95 if (!((v.type() == t) || (fAllowNull && (v.isNull()))))
899d373b
GA
96 {
97 string err = strprintf("Expected type %s, got %s",
ed21d5bd 98 uvTypeName(t), uvTypeName(v.type()));
738835d7 99 throw JSONRPCError(RPC_TYPE_ERROR, err);
899d373b
GA
100 }
101 i++;
102 }
103}
104
ed21d5bd
JG
105void RPCTypeCheckObj(const UniValue& o,
106 const map<string, UniValue::VType>& typesExpected,
cc6dfd1f 107 bool fAllowNull)
899d373b 108{
d014114d 109 BOOST_FOREACH(const PAIRTYPE(string, UniValue::VType)& t, typesExpected)
899d373b 110 {
d014114d 111 const UniValue& v = find_value(o, t.first);
ed21d5bd 112 if (!fAllowNull && v.isNull())
7d9d134b 113 throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing %s", t.first));
cc6dfd1f 114
ed21d5bd 115 if (!((v.type() == t.second) || (fAllowNull && (v.isNull()))))
899d373b
GA
116 {
117 string err = strprintf("Expected type %s for %s, got %s",
ed21d5bd 118 uvTypeName(t.second), t.first, uvTypeName(v.type()));
738835d7 119 throw JSONRPCError(RPC_TYPE_ERROR, err);
899d373b
GA
120 }
121 }
122}
123
d014114d 124CAmount AmountFromValue(const UniValue& value)
69d605f4 125{
84d1d5fd
WL
126 if (!value.isNum() && !value.isStr())
127 throw JSONRPCError(RPC_TYPE_ERROR, "Amount is not a number or string");
c66dff3d 128 CAmount amount;
fed500e2 129 if (!ParseFixedPoint(value.getValStr(), 8, &amount))
738835d7 130 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount");
c66dff3d
WL
131 if (!MoneyRange(amount))
132 throw JSONRPCError(RPC_TYPE_ERROR, "Amount out of range");
133 return amount;
69d605f4
WL
134}
135
851f58f9 136UniValue ValueFromAmount(const CAmount& amount)
69d605f4 137{
d5bf1afa
WL
138 bool sign = amount < 0;
139 int64_t n_abs = (sign ? -amount : amount);
140 int64_t quotient = n_abs / COIN;
141 int64_t remainder = n_abs % COIN;
142 return UniValue(UniValue::VNUM,
143 strprintf("%s%d.%08d", sign ? "-" : "", quotient, remainder));
69d605f4
WL
144}
145
d014114d 146uint256 ParseHashV(const UniValue& v, string strName)
463c9710
PT
147{
148 string strHex;
ed21d5bd 149 if (v.isStr())
463c9710
PT
150 strHex = v.get_str();
151 if (!IsHex(strHex)) // Note: IsHex("") is false
152 throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
153 uint256 result;
154 result.SetHex(strHex);
155 return result;
156}
d014114d 157uint256 ParseHashO(const UniValue& o, string strKey)
463c9710
PT
158{
159 return ParseHashV(find_value(o, strKey), strKey);
160}
d014114d 161vector<unsigned char> ParseHexV(const UniValue& v, string strName)
463c9710
PT
162{
163 string strHex;
ed21d5bd 164 if (v.isStr())
463c9710
PT
165 strHex = v.get_str();
166 if (!IsHex(strHex))
167 throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
168 return ParseHex(strHex);
169}
d014114d 170vector<unsigned char> ParseHexO(const UniValue& o, string strKey)
463c9710
PT
171{
172 return ParseHexV(find_value(o, strKey), strKey);
173}
69d605f4 174
74335bd3 175
77920402
MF
176/**
177 * Note: This interface may still be subject to change.
178 */
69d605f4 179
db954a65 180std::string CRPCTable::help(const std::string& strCommand) const
69d605f4 181{
69d605f4 182 string strRet;
6b5b7cbf 183 string category;
69d605f4 184 set<rpcfn_type> setDone;
6b5b7cbf
CL
185 vector<pair<string, const CRPCCommand*> > vCommands;
186
9862229d 187 for (map<string, const CRPCCommand*>::const_iterator mi = mapCommands.begin(); mi != mapCommands.end(); ++mi)
6b5b7cbf
CL
188 vCommands.push_back(make_pair(mi->second->category + mi->first, mi->second));
189 sort(vCommands.begin(), vCommands.end());
190
191 BOOST_FOREACH(const PAIRTYPE(string, const CRPCCommand*)& command, vCommands)
69d605f4 192 {
6b5b7cbf
CL
193 const CRPCCommand *pcmd = command.second;
194 string strMethod = pcmd->name;
69d605f4 195 // We already filter duplicates, but these deprecated screw up the sort order
47f48a65 196 if (strMethod.find("label") != string::npos)
69d605f4 197 continue;
bd9aebf1 198 if ((strCommand != "" || pcmd->category == "hidden") && strMethod != strCommand)
69d605f4
WL
199 continue;
200 try
201 {
851f58f9 202 UniValue params;
dc42bf52 203 rpcfn_type pfn = pcmd->actor;
69d605f4
WL
204 if (setDone.insert(pfn).second)
205 (*pfn)(params, true);
206 }
27df4123 207 catch (const std::exception& e)
69d605f4
WL
208 {
209 // Help text is returned in an exception
210 string strHelp = string(e.what());
211 if (strCommand == "")
6b5b7cbf 212 {
ab9dc75a 213 if (strHelp.find('\n') != string::npos)
69d605f4 214 strHelp = strHelp.substr(0, strHelp.find('\n'));
6b5b7cbf
CL
215
216 if (category != pcmd->category)
217 {
218 if (!category.empty())
219 strRet += "\n";
220 category = pcmd->category;
221 string firstLetter = category.substr(0,1);
222 boost::to_upper(firstLetter);
223 strRet += "== " + firstLetter + category.substr(1) + " ==\n";
224 }
225 }
69d605f4
WL
226 strRet += strHelp + "\n";
227 }
228 }
229 if (strRet == "")
7d9d134b 230 strRet = strprintf("help: unknown command: %s\n", strCommand);
69d605f4
WL
231 strRet = strRet.substr(0,strRet.size()-1);
232 return strRet;
233}
234
d014114d 235UniValue help(const UniValue& params, bool fHelp)
9862229d
PW
236{
237 if (fHelp || params.size() > 1)
238 throw runtime_error(
a6099ef3 239 "help ( \"command\" )\n"
240 "\nList all commands, or get help for a specified command.\n"
241 "\nArguments:\n"
242 "1. \"command\" (string, optional) The command to get help on\n"
243 "\nResult:\n"
244 "\"text\" (string) The help text\n"
245 );
9862229d
PW
246
247 string strCommand;
248 if (params.size() > 0)
249 strCommand = params[0].get_str();
250
251 return tableRPC.help(strCommand);
252}
253
69d605f4 254
d014114d 255UniValue stop(const UniValue& params, bool fHelp)
69d605f4 256{
6e65420b 257 // Accept the deprecated and ignored 'detach' boolean argument
3731f578 258 if (fHelp || params.size() > 1)
69d605f4 259 throw runtime_error(
92467073 260 "stop\n"
6c7cc8eb 261 "\nStop Zcash server.");
69d605f4 262 // Shutdown will take long enough that the response should get back
9247134e 263 StartShutdown();
6c7cc8eb 264 return "Zcash server stopping";
69d605f4
WL
265}
266
267
69d605f4 268
77920402
MF
269/**
270 * Call Table
271 */
e46704dd 272static const CRPCCommand vRPCCommands[] =
b9fb692d
JS
273{ // category name actor (function) okSafeMode
274 // --------------------- ------------------------ ----------------------- ----------
ab88ed93 275 /* Overall control/query calls */
b9fb692d
JS
276 { "control", "getinfo", &getinfo, true }, /* uses wallet if enabled */
277 { "control", "help", &help, true },
278 { "control", "stop", &stop, true },
ab88ed93
WL
279
280 /* P2P networking */
b9fb692d
JS
281 { "network", "getnetworkinfo", &getnetworkinfo, true },
282 { "network", "addnode", &addnode, true },
94ee48c4 283 { "network", "disconnectnode", &disconnectnode, true },
b9fb692d
JS
284 { "network", "getaddednodeinfo", &getaddednodeinfo, true },
285 { "network", "getconnectioncount", &getconnectioncount, true },
286 { "network", "getnettotals", &getnettotals, true },
287 { "network", "getpeerinfo", &getpeerinfo, true },
288 { "network", "ping", &ping, true },
ed3f13a0
JS
289 { "network", "setban", &setban, true },
290 { "network", "listbanned", &listbanned, true },
291 { "network", "clearbanned", &clearbanned, true },
ab88ed93
WL
292
293 /* Block chain and UTXO */
b9fb692d
JS
294 { "blockchain", "getblockchaininfo", &getblockchaininfo, true },
295 { "blockchain", "getbestblockhash", &getbestblockhash, true },
296 { "blockchain", "getblockcount", &getblockcount, true },
297 { "blockchain", "getblock", &getblock, true },
298 { "blockchain", "getblockhash", &getblockhash, true },
d3d5483e 299 { "blockchain", "getblockheader", &getblockheader, true },
b9fb692d
JS
300 { "blockchain", "getchaintips", &getchaintips, true },
301 { "blockchain", "getdifficulty", &getdifficulty, true },
302 { "blockchain", "getmempoolinfo", &getmempoolinfo, true },
303 { "blockchain", "getrawmempool", &getrawmempool, true },
304 { "blockchain", "gettxout", &gettxout, true },
59ed61b3
MC
305 { "blockchain", "gettxoutproof", &gettxoutproof, true },
306 { "blockchain", "verifytxoutproof", &verifytxoutproof, true },
b9fb692d
JS
307 { "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, true },
308 { "blockchain", "verifychain", &verifychain, true },
48ba56cd 309
4a85e067 310 /* Mining */
f3e49686 311#ifdef ENABLE_WALLET
b9fb692d 312 { "mining", "getblocktemplate", &getblocktemplate, true },
f3e49686 313#endif
b9fb692d 314 { "mining", "getmininginfo", &getmininginfo, true },
000499ae
JG
315 { "mining", "getlocalsolps", &getlocalsolps, true },
316 { "mining", "getnetworksolps", &getnetworksolps, true },
b9fb692d
JS
317 { "mining", "getnetworkhashps", &getnetworkhashps, true },
318 { "mining", "prioritisetransaction", &prioritisetransaction, true },
319 { "mining", "submitblock", &submitblock, true },
1b114e54 320 { "mining", "getblocksubsidy", &getblocksubsidy, true },
6b5b7cbf 321
8e8b6d70 322#ifdef ENABLE_MINING
6b5b7cbf 323 /* Coin generation */
b9fb692d
JS
324 { "generating", "getgenerate", &getgenerate, true },
325 { "generating", "setgenerate", &setgenerate, true },
326 { "generating", "generate", &generate, true },
6b5b7cbf 327#endif
ab88ed93
WL
328
329 /* Raw transactions */
b9fb692d
JS
330 { "rawtransactions", "createrawtransaction", &createrawtransaction, true },
331 { "rawtransactions", "decoderawtransaction", &decoderawtransaction, true },
332 { "rawtransactions", "decodescript", &decodescript, true },
333 { "rawtransactions", "getrawtransaction", &getrawtransaction, true },
334 { "rawtransactions", "sendrawtransaction", &sendrawtransaction, false },
335 { "rawtransactions", "signrawtransaction", &signrawtransaction, false }, /* uses wallet if enabled */
3d8013a0
MC
336#ifdef ENABLE_WALLET
337 { "rawtransactions", "fundrawtransaction", &fundrawtransaction, false },
338#endif
ab88ed93
WL
339
340 /* Utility functions */
b9fb692d
JS
341 { "util", "createmultisig", &createmultisig, true },
342 { "util", "validateaddress", &validateaddress, true }, /* uses wallet if enabled */
343 { "util", "verifymessage", &verifymessage, true },
344 { "util", "estimatefee", &estimatefee, true },
345 { "util", "estimatepriority", &estimatepriority, true },
4e16a724 346 { "util", "z_validateaddress", &z_validateaddress, true }, /* uses wallet if enabled */
4a85e067 347
bd9aebf1 348 /* Not shown in help */
b9fb692d
JS
349 { "hidden", "invalidateblock", &invalidateblock, true },
350 { "hidden", "reconsiderblock", &reconsiderblock, true },
351 { "hidden", "setmocktime", &setmocktime, true },
0f5954c4 352#ifdef ENABLE_WALLET
b9fb692d 353 { "hidden", "resendwallettransactions", &resendwallettransactions, true},
0f5954c4 354#endif
bd9aebf1 355
4a85e067
WL
356#ifdef ENABLE_WALLET
357 /* Wallet */
b9fb692d
JS
358 { "wallet", "addmultisigaddress", &addmultisigaddress, true },
359 { "wallet", "backupwallet", &backupwallet, true },
360 { "wallet", "dumpprivkey", &dumpprivkey, true },
361 { "wallet", "dumpwallet", &dumpwallet, true },
362 { "wallet", "encryptwallet", &encryptwallet, true },
363 { "wallet", "getaccountaddress", &getaccountaddress, true },
364 { "wallet", "getaccount", &getaccount, true },
365 { "wallet", "getaddressesbyaccount", &getaddressesbyaccount, true },
366 { "wallet", "getbalance", &getbalance, false },
367 { "wallet", "getnewaddress", &getnewaddress, true },
368 { "wallet", "getrawchangeaddress", &getrawchangeaddress, true },
369 { "wallet", "getreceivedbyaccount", &getreceivedbyaccount, false },
370 { "wallet", "getreceivedbyaddress", &getreceivedbyaddress, false },
371 { "wallet", "gettransaction", &gettransaction, false },
372 { "wallet", "getunconfirmedbalance", &getunconfirmedbalance, false },
373 { "wallet", "getwalletinfo", &getwalletinfo, false },
374 { "wallet", "importprivkey", &importprivkey, true },
375 { "wallet", "importwallet", &importwallet, true },
376 { "wallet", "importaddress", &importaddress, true },
377 { "wallet", "keypoolrefill", &keypoolrefill, true },
378 { "wallet", "listaccounts", &listaccounts, false },
379 { "wallet", "listaddressgroupings", &listaddressgroupings, false },
380 { "wallet", "listlockunspent", &listlockunspent, false },
381 { "wallet", "listreceivedbyaccount", &listreceivedbyaccount, false },
382 { "wallet", "listreceivedbyaddress", &listreceivedbyaddress, false },
383 { "wallet", "listsinceblock", &listsinceblock, false },
384 { "wallet", "listtransactions", &listtransactions, false },
385 { "wallet", "listunspent", &listunspent, false },
386 { "wallet", "lockunspent", &lockunspent, true },
387 { "wallet", "move", &movecmd, false },
388 { "wallet", "sendfrom", &sendfrom, false },
389 { "wallet", "sendmany", &sendmany, false },
390 { "wallet", "sendtoaddress", &sendtoaddress, false },
391 { "wallet", "setaccount", &setaccount, true },
392 { "wallet", "settxfee", &settxfee, true },
393 { "wallet", "signmessage", &signmessage, true },
394 { "wallet", "walletlock", &walletlock, true },
395 { "wallet", "walletpassphrasechange", &walletpassphrasechange, true },
396 { "wallet", "walletpassphrase", &walletpassphrase, true },
6962bb3d 397 { "wallet", "zcbenchmark", &zc_benchmark, true },
730790f7 398 { "wallet", "zcrawkeygen", &zc_raw_keygen, true },
b7e4abd6 399 { "wallet", "zcrawjoinsplit", &zc_raw_joinsplit, true },
1737627c 400 { "wallet", "zcrawreceive", &zc_raw_receive, true },
c1c45943 401 { "wallet", "zcsamplejoinsplit", &zc_sample_joinsplit, true },
6c41028f 402 { "wallet", "z_listreceivedbyaddress",&z_listreceivedbyaddress,false },
a0a3334c
S
403 { "wallet", "z_getbalance", &z_getbalance, false },
404 { "wallet", "z_gettotalbalance", &z_gettotalbalance, false },
6d2d045c 405 { "wallet", "z_sendmany", &z_sendmany, false },
fc72c078 406 { "wallet", "z_getoperationstatus", &z_getoperationstatus, true },
c1eae280 407 { "wallet", "z_getoperationresult", &z_getoperationresult, true },
34f0001c 408 { "wallet", "z_listoperationids", &z_listoperationids, true },
c1c45943 409 { "wallet", "z_getnewaddress", &z_getnewaddress, true },
e709997f 410 { "wallet", "z_listaddresses", &z_listaddresses, true },
c1c45943 411 { "wallet", "z_exportkey", &z_exportkey, true },
92444edc
S
412 { "wallet", "z_importkey", &z_importkey, true },
413 { "wallet", "z_exportwallet", &z_exportwallet, true },
414 { "wallet", "z_importwallet", &z_importwallet, true }
48ba56cd 415#endif // ENABLE_WALLET
69d605f4 416};
69d605f4 417
9862229d 418CRPCTable::CRPCTable()
dc42bf52 419{
dc42bf52
JG
420 unsigned int vcidx;
421 for (vcidx = 0; vcidx < (sizeof(vRPCCommands) / sizeof(vRPCCommands[0])); vcidx++)
422 {
e46704dd 423 const CRPCCommand *pcmd;
69d605f4 424
dc42bf52
JG
425 pcmd = &vRPCCommands[vcidx];
426 mapCommands[pcmd->name] = pcmd;
427 }
428}
69d605f4 429
db954a65 430const CRPCCommand *CRPCTable::operator[](const std::string& name) const
9862229d
PW
431{
432 map<string, const CRPCCommand*>::const_iterator it = mapCommands.find(name);
433 if (it == mapCommands.end())
434 return NULL;
435 return (*it).second;
436}
69d605f4 437
69d605f4 438
69d605f4
WL
439bool HTTPAuthorized(map<string, string>& mapHeaders)
440{
441 string strAuth = mapHeaders["authorization"];
442 if (strAuth.substr(0,6) != "Basic ")
443 return false;
444 string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64);
445 string strUserPass = DecodeBase64(strUserPass64);
42656ea2 446 return TimingResistantEqual(strUserPass, strRPCUserColonPass);
69d605f4
WL
447}
448
d014114d 449void ErrorReply(std::ostream& stream, const UniValue& objError, const UniValue& id)
69d605f4
WL
450{
451 // Send error reply from json-rpc error object
285746d3 452 int nStatus = HTTP_INTERNAL_SERVER_ERROR;
69d605f4 453 int code = find_value(objError, "code").get_int();
285746d3
WL
454 if (code == RPC_INVALID_REQUEST) nStatus = HTTP_BAD_REQUEST;
455 else if (code == RPC_METHOD_NOT_FOUND) nStatus = HTTP_NOT_FOUND;
ed21d5bd 456 string strReply = JSONRPCReply(NullUniValue, objError, id);
96c52695 457 stream << HTTPReply(nStatus, strReply, false) << std::flush;
69d605f4
WL
458}
459
21bf3d25 460CNetAddr BoostAsioToCNetAddr(boost::asio::ip::address address)
69d605f4 461{
ee219125 462 CNetAddr netaddr;
43b6dafa
GS
463 // Make sure that IPv4-compatible and IPv4-mapped IPv6 addresses are treated as IPv4 addresses
464 if (address.is_v6()
465 && (address.to_v6().is_v4_compatible()
466 || address.to_v6().is_v4_mapped()))
ee219125
WL
467 address = address.to_v6().to_v4();
468
469 if(address.is_v4())
470 {
471 boost::asio::ip::address_v4::bytes_type bytes = address.to_v4().to_bytes();
472 netaddr.SetRaw(NET_IPV4, &bytes[0]);
473 }
474 else
475 {
476 boost::asio::ip::address_v6::bytes_type bytes = address.to_v6().to_bytes();
477 netaddr.SetRaw(NET_IPV6, &bytes[0]);
478 }
479 return netaddr;
480}
481
482bool ClientAllowed(const boost::asio::ip::address& address)
483{
484 CNetAddr netaddr = BoostAsioToCNetAddr(address);
485 BOOST_FOREACH(const CSubNet &subnet, rpc_allow_subnets)
486 if (subnet.Match(netaddr))
69d605f4
WL
487 return true;
488 return false;
489}
490
a0780ba0
GS
491template <typename Protocol>
492class AcceptedConnectionImpl : public AcceptedConnection
493{
494public:
495 AcceptedConnectionImpl(
a3241998 496 boost::asio::io_service& io_service,
a0780ba0
GS
497 ssl::context &context,
498 bool fUseSSL) :
499 sslStream(io_service, context),
500 _d(sslStream, fUseSSL),
501 _stream(_d)
502 {
503 }
504
505 virtual std::iostream& stream()
506 {
507 return _stream;
508 }
509
510 virtual std::string peer_address_to_string() const
511 {
512 return peer.address().to_string();
513 }
e9205293 514
a0780ba0
GS
515 virtual void close()
516 {
517 _stream.close();
518 }
519
520 typename Protocol::endpoint peer;
a3241998 521 boost::asio::ssl::stream<typename Protocol::socket> sslStream;
a0780ba0
GS
522
523private:
524 SSLIOStreamDevice<Protocol> _d;
a3241998 525 boost::iostreams::stream< SSLIOStreamDevice<Protocol> > _stream;
e9205293
DJS
526};
527
21eb5ada 528void ServiceConnection(AcceptedConnection *conn);
69d605f4 529
77920402 530//! Forward declaration required for RPCListen
a0780ba0
GS
531template <typename Protocol, typename SocketAcceptorService>
532static void RPCAcceptHandler(boost::shared_ptr< basic_socket_acceptor<Protocol, SocketAcceptorService> > acceptor,
914dc012
GS
533 ssl::context& context,
534 bool fUseSSL,
1a445225 535 boost::shared_ptr< AcceptedConnection > conn,
914dc012
GS
536 const boost::system::error_code& error);
537
538/**
539 * Sets up I/O resources to accept and handle a new connection.
540 */
a0780ba0
GS
541template <typename Protocol, typename SocketAcceptorService>
542static void RPCListen(boost::shared_ptr< basic_socket_acceptor<Protocol, SocketAcceptorService> > acceptor,
914dc012
GS
543 ssl::context& context,
544 const bool fUseSSL)
545{
914dc012 546 // Accept connection
1a445225 547 boost::shared_ptr< AcceptedConnectionImpl<Protocol> > conn(new AcceptedConnectionImpl<Protocol>(acceptor->get_io_service(), context, fUseSSL));
914dc012
GS
548
549 acceptor->async_accept(
550 conn->sslStream.lowest_layer(),
551 conn->peer,
a0780ba0 552 boost::bind(&RPCAcceptHandler<Protocol, SocketAcceptorService>,
914dc012
GS
553 acceptor,
554 boost::ref(context),
555 fUseSSL,
556 conn,
0a0cd345 557 _1));
914dc012
GS
558}
559
fb78cc23 560
914dc012
GS
561/**
562 * Accept and handle incoming connection.
563 */
a0780ba0
GS
564template <typename Protocol, typename SocketAcceptorService>
565static void RPCAcceptHandler(boost::shared_ptr< basic_socket_acceptor<Protocol, SocketAcceptorService> > acceptor,
914dc012
GS
566 ssl::context& context,
567 const bool fUseSSL,
1a445225 568 boost::shared_ptr< AcceptedConnection > conn,
914dc012
GS
569 const boost::system::error_code& error)
570{
814efd6f 571 // Immediately start accepting new connections, except when we're cancelled or our socket is closed.
a3241998 572 if (error != boost::asio::error::operation_aborted && acceptor->is_open())
ad25804f 573 RPCListen(acceptor, context, fUseSSL);
914dc012 574
1a445225 575 AcceptedConnectionImpl<ip::tcp>* tcp_conn = dynamic_cast< AcceptedConnectionImpl<ip::tcp>* >(conn.get());
a0780ba0 576
914dc012
GS
577 if (error)
578 {
0a0cd345
WL
579 // TODO: Actually handle errors
580 LogPrintf("%s: Error: %s\n", __func__, error.message());
914dc012 581 }
914dc012
GS
582 // Restrict callers by IP. It is important to
583 // do this before starting client thread, to filter out
584 // certain DoS and misbehaving clients.
21eb5ada 585 else if (tcp_conn && !ClientAllowed(tcp_conn->peer.address()))
914dc012
GS
586 {
587 // Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake.
588 if (!fUseSSL)
16f33f16 589 conn->stream() << HTTPError(HTTP_FORBIDDEN, false) << std::flush;
1a445225 590 conn->close();
914dc012 591 }
21eb5ada 592 else {
1a445225 593 ServiceConnection(conn.get());
21eb5ada 594 conn->close();
914dc012 595 }
914dc012
GS
596}
597
deb3572a
WL
598static ip::tcp::endpoint ParseEndpoint(const std::string &strEndpoint, int defaultPort)
599{
600 std::string addr;
601 int port = defaultPort;
602 SplitHostPort(strEndpoint, port, addr);
a3241998 603 return ip::tcp::endpoint(boost::asio::ip::address::from_string(addr), port);
deb3572a
WL
604}
605
21eb5ada 606void StartRPCThreads()
69d605f4 607{
ee219125
WL
608 rpc_allow_subnets.clear();
609 rpc_allow_subnets.push_back(CSubNet("127.0.0.0/8")); // always allow IPv4 local subnet
610 rpc_allow_subnets.push_back(CSubNet("::1")); // always allow IPv6 localhost
611 if (mapMultiArgs.count("-rpcallowip"))
612 {
613 const vector<string>& vAllow = mapMultiArgs["-rpcallowip"];
614 BOOST_FOREACH(string strAllow, vAllow)
615 {
616 CSubNet subnet(strAllow);
617 if(!subnet.IsValid())
618 {
619 uiInterface.ThreadSafeMessageBox(
4278b1df 620 strprintf("Invalid -rpcallowip subnet specification: %s. Valid are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24).", strAllow),
ee219125
WL
621 "", CClientUIInterface::MSG_ERROR);
622 StartShutdown();
623 return;
624 }
625 rpc_allow_subnets.push_back(subnet);
626 }
627 }
628 std::string strAllowed;
629 BOOST_FOREACH(const CSubNet &subnet, rpc_allow_subnets)
630 strAllowed += subnet.ToString() + " ";
631 LogPrint("rpc", "Allowing RPC connections from: %s\n", strAllowed);
632
e957192c 633 if (mapArgs["-rpcpassword"] == "")
69d605f4 634 {
e957192c
WL
635 LogPrintf("No rpcpassword set - using random cookie authentication\n");
636 if (!GenerateAuthCookie(&strRPCUserColonPass)) {
637 uiInterface.ThreadSafeMessageBox(
638 _("Error: A fatal internal error occured, see debug.log for details"), // Same message as AbortNode
639 "", CClientUIInterface::MSG_ERROR);
640 StartShutdown();
641 return;
642 }
643 } else {
644 strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
69d605f4
WL
645 }
646
21eb5ada 647 assert(rpc_io_service == NULL);
a3241998 648 rpc_io_service = new boost::asio::io_service();
21eb5ada 649 rpc_ssl_context = new ssl::context(*rpc_io_service, ssl::context::sslv23);
69d605f4 650
3260b4c0 651 const bool fUseSSL = GetBoolArg("-rpcssl", false);
fbf9df2e 652
69d605f4
WL
653 if (fUseSSL)
654 {
683dc400 655 rpc_ssl_context->set_options(ssl::context::no_sslv2 | ssl::context::no_sslv3);
93fb7489 656
a3241998
CF
657 boost::filesystem::path pathCertFile(GetArg("-rpcsslcertificatechainfile", "server.cert"));
658 if (!pathCertFile.is_complete()) pathCertFile = boost::filesystem::path(GetDataDir()) / pathCertFile;
659 if (boost::filesystem::exists(pathCertFile)) rpc_ssl_context->use_certificate_chain_file(pathCertFile.string());
7d9d134b 660 else LogPrintf("ThreadRPCServer ERROR: missing server certificate file %s\n", pathCertFile.string());
93fb7489 661
a3241998
CF
662 boost::filesystem::path pathPKFile(GetArg("-rpcsslprivatekeyfile", "server.pem"));
663 if (!pathPKFile.is_complete()) pathPKFile = boost::filesystem::path(GetDataDir()) / pathPKFile;
664 if (boost::filesystem::exists(pathPKFile)) rpc_ssl_context->use_private_key_file(pathPKFile.string(), ssl::context::pem);
7d9d134b 665 else LogPrintf("ThreadRPCServer ERROR: missing server private key file %s\n", pathPKFile.string());
93fb7489 666
1728bf08 667 string strCiphers = GetArg("-rpcsslciphers", "TLSv1.2+HIGH:TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!3DES:@STRENGTH");
21eb5ada 668 SSL_CTX_set_cipher_list(rpc_ssl_context->impl(), strCiphers.c_str());
69d605f4 669 }
69d605f4 670
deb3572a
WL
671 std::vector<ip::tcp::endpoint> vEndpoints;
672 bool bBindAny = false;
84ce18ca 673 int defaultPort = GetArg("-rpcport", BaseParams().RPCPort());
deb3572a
WL
674 if (!mapArgs.count("-rpcallowip")) // Default to loopback if not allowing external IPs
675 {
a3241998
CF
676 vEndpoints.push_back(ip::tcp::endpoint(boost::asio::ip::address_v6::loopback(), defaultPort));
677 vEndpoints.push_back(ip::tcp::endpoint(boost::asio::ip::address_v4::loopback(), defaultPort));
deb3572a
WL
678 if (mapArgs.count("-rpcbind"))
679 {
680 LogPrintf("WARNING: option -rpcbind was ignored because -rpcallowip was not specified, refusing to allow everyone to connect\n");
681 }
682 } else if (mapArgs.count("-rpcbind")) // Specific bind address
683 {
684 BOOST_FOREACH(const std::string &addr, mapMultiArgs["-rpcbind"])
685 {
686 try {
687 vEndpoints.push_back(ParseEndpoint(addr, defaultPort));
688 }
27df4123 689 catch (const boost::system::system_error&)
deb3572a
WL
690 {
691 uiInterface.ThreadSafeMessageBox(
692 strprintf(_("Could not parse -rpcbind value %s as network address"), addr),
693 "", CClientUIInterface::MSG_ERROR);
694 StartShutdown();
695 return;
696 }
697 }
698 } else { // No specific bind address specified, bind to any
a3241998
CF
699 vEndpoints.push_back(ip::tcp::endpoint(boost::asio::ip::address_v6::any(), defaultPort));
700 vEndpoints.push_back(ip::tcp::endpoint(boost::asio::ip::address_v4::any(), defaultPort));
deb3572a 701 // Prefer making the socket dual IPv6/IPv4 instead of binding
45bfa137 702 // to both addresses separately.
deb3572a
WL
703 bBindAny = true;
704 }
7cf3d2cc 705
c1d79812
PW
706 bool fListening = false;
707 std::string strerr;
8db17607 708 std::string straddress;
deb3572a 709 BOOST_FOREACH(const ip::tcp::endpoint &endpoint, vEndpoints)
c1ecab81 710 {
deb3572a 711 try {
a3241998 712 boost::asio::ip::address bindAddress = endpoint.address();
8db17607
CF
713 straddress = bindAddress.to_string();
714 LogPrintf("Binding RPC on address %s port %i (IPv4+IPv6 bind any: %i)\n", straddress, endpoint.port(), bBindAny);
715 boost::system::error_code v6_only_error;
716 boost::shared_ptr<ip::tcp::acceptor> acceptor(new ip::tcp::acceptor(*rpc_io_service));
717
ad25804f
GS
718 acceptor->open(endpoint.protocol());
719 acceptor->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
deb3572a
WL
720
721 // Try making the socket dual IPv6/IPv4 when listening on the IPv6 "any" address
722 acceptor->set_option(boost::asio::ip::v6_only(
a3241998 723 !bBindAny || bindAddress != boost::asio::ip::address_v6::any()), v6_only_error);
deb3572a 724
ad25804f
GS
725 acceptor->bind(endpoint);
726 acceptor->listen(socket_base::max_connections);
727
21eb5ada 728 RPCListen(acceptor, *rpc_ssl_context, fUseSSL);
c1d79812
PW
729
730 fListening = true;
6afa4932 731 rpc_acceptors.push_back(acceptor);
77920402 732 // If dual IPv6/IPv4 bind successful, skip binding to IPv4 separately
a3241998 733 if(bBindAny && bindAddress == boost::asio::ip::address_v6::any() && !v6_only_error)
deb3572a
WL
734 break;
735 }
27df4123 736 catch (const boost::system::system_error& e)
deb3572a 737 {
8db17607
CF
738 LogPrintf("ERROR: Binding RPC on address %s port %i failed: %s\n", straddress, endpoint.port(), e.what());
739 strerr = strprintf(_("An error occurred while setting up the RPC address %s port %u for listening: %s"), straddress, endpoint.port(), e.what());
c1ecab81 740 }
c1d79812
PW
741 }
742
743 if (!fListening) {
5350ea41 744 uiInterface.ThreadSafeMessageBox(strerr, "", CClientUIInterface::MSG_ERROR);
07368a9e 745 StartShutdown();
c1ecab81
GS
746 return;
747 }
69d605f4 748
21eb5ada
GA
749 rpc_worker_group = new boost::thread_group();
750 for (int i = 0; i < GetArg("-rpcthreads", 4); i++)
a3241998 751 rpc_worker_group->create_thread(boost::bind(&boost::asio::io_service::run, rpc_io_service));
ff6a7af1 752 fRPCRunning = true;
4401b2d7 753 g_rpcSignals.Started();
fc72c078 754
008fccfa 755 // Launch one async rpc worker. The ability to launch multiple workers is not recommended at present and thus the option is disabled.
f86f625d 756 getAsyncRPCQueue()->addWorker();
008fccfa 757/*
8d08172d
S
758 int n = GetArg("-rpcasyncthreads", 1);
759 if (n<1) {
760 LogPrintf("ERROR: Invalid value %d for -rpcasyncthreads. Must be at least 1.\n", n);
761 strerr = strprintf(_("An error occurred while setting up the Async RPC threads, invalid parameter value of %d (must be at least 1)."), n);
762 uiInterface.ThreadSafeMessageBox(strerr, "", CClientUIInterface::MSG_ERROR);
763 StartShutdown();
764 return;
765 }
766 for (int i = 0; i < n; i++)
f86f625d 767 getAsyncRPCQueue()->addWorker();
008fccfa 768*/
21eb5ada
GA
769}
770
a8db31c8
WL
771void StartDummyRPCThread()
772{
773 if(rpc_io_service == NULL)
774 {
a3241998 775 rpc_io_service = new boost::asio::io_service();
a8db31c8
WL
776 /* Create dummy "work" to keep the thread from exiting when no timeouts active,
777 * see http://www.boost.org/doc/libs/1_51_0/doc/html/boost_asio/reference/io_service.html#boost_asio.reference.io_service.stopping_the_io_service_from_running_out_of_work */
a3241998 778 rpc_dummy_work = new boost::asio::io_service::work(*rpc_io_service);
a8db31c8 779 rpc_worker_group = new boost::thread_group();
a3241998 780 rpc_worker_group->create_thread(boost::bind(&boost::asio::io_service::run, rpc_io_service));
ff6a7af1 781 fRPCRunning = true;
a8db31c8
WL
782 }
783}
784
21eb5ada
GA
785void StopRPCThreads()
786{
787 if (rpc_io_service == NULL) return;
ff6a7af1
LD
788 // Set this to false first, so that longpolling loops will exit when woken up
789 fRPCRunning = false;
21eb5ada 790
cef44941
WL
791 // First, cancel all timers and acceptors
792 // This is not done automatically by ->stop(), and in some cases the destructor of
a3241998 793 // boost::asio::io_service can hang if this is skipped.
33e5b429 794 boost::system::error_code ec;
cef44941 795 BOOST_FOREACH(const boost::shared_ptr<ip::tcp::acceptor> &acceptor, rpc_acceptors)
33e5b429
WL
796 {
797 acceptor->cancel(ec);
798 if (ec)
7ff9d122 799 LogPrintf("%s: Warning: %s when cancelling acceptor\n", __func__, ec.message());
33e5b429 800 }
cef44941
WL
801 rpc_acceptors.clear();
802 BOOST_FOREACH(const PAIRTYPE(std::string, boost::shared_ptr<deadline_timer>) &timer, deadlineTimers)
33e5b429
WL
803 {
804 timer.second->cancel(ec);
805 if (ec)
7ff9d122 806 LogPrintf("%s: Warning: %s when cancelling timer\n", __func__, ec.message());
33e5b429 807 }
92f2c1fe 808 deadlineTimers.clear();
cef44941 809
e957192c
WL
810 DeleteAuthCookie();
811
21eb5ada 812 rpc_io_service->stop();
4401b2d7 813 g_rpcSignals.Stopped();
b2ba55c4
GA
814 if (rpc_worker_group != NULL)
815 rpc_worker_group->join_all();
a8db31c8 816 delete rpc_dummy_work; rpc_dummy_work = NULL;
21eb5ada
GA
817 delete rpc_worker_group; rpc_worker_group = NULL;
818 delete rpc_ssl_context; rpc_ssl_context = NULL;
819 delete rpc_io_service; rpc_io_service = NULL;
fc72c078
S
820
821 // Tells async queue to cancel all operations and shutdown.
3b54bf58 822 LogPrintf("%s: waiting for async rpc workers to stop\n", __func__);
f86f625d 823 getAsyncRPCQueue()->closeAndWait();
e9205293
DJS
824}
825
ff6a7af1
LD
826bool IsRPCRunning()
827{
828 return fRPCRunning;
829}
830
af82884a
DK
831void SetRPCWarmupStatus(const std::string& newStatus)
832{
833 LOCK(cs_rpcWarmup);
834 rpcWarmupStatus = newStatus;
835}
836
837void SetRPCWarmupFinished()
838{
839 LOCK(cs_rpcWarmup);
840 assert(fRPCInWarmup);
841 fRPCInWarmup = false;
842}
843
78bdc810
JS
844bool RPCIsInWarmup(std::string *outStatus)
845{
846 LOCK(cs_rpcWarmup);
847 if (outStatus)
848 *outStatus = rpcWarmupStatus;
849 return fRPCInWarmup;
850}
851
92f2c1fe
GA
852void RPCRunHandler(const boost::system::error_code& err, boost::function<void(void)> func)
853{
854 if (!err)
855 func();
856}
857
51ed9ec9 858void RPCRunLater(const std::string& name, boost::function<void(void)> func, int64_t nSeconds)
92f2c1fe
GA
859{
860 assert(rpc_io_service != NULL);
861
862 if (deadlineTimers.count(name) == 0)
863 {
864 deadlineTimers.insert(make_pair(name,
865 boost::shared_ptr<deadline_timer>(new deadline_timer(*rpc_io_service))));
866 }
a3241998 867 deadlineTimers[name]->expires_from_now(boost::posix_time::seconds(nSeconds));
92f2c1fe
GA
868 deadlineTimers[name]->async_wait(boost::bind(RPCRunHandler, _1, func));
869}
870
c6494d82
JG
871class JSONRequest
872{
873public:
851f58f9 874 UniValue id;
c6494d82 875 string strMethod;
851f58f9 876 UniValue params;
c6494d82 877
ed21d5bd 878 JSONRequest() { id = NullUniValue; }
d014114d 879 void parse(const UniValue& valRequest);
c6494d82
JG
880};
881
d014114d 882void JSONRequest::parse(const UniValue& valRequest)
c6494d82
JG
883{
884 // Parse request
ed21d5bd 885 if (!valRequest.isObject())
738835d7 886 throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object");
d014114d 887 const UniValue& request = valRequest.get_obj();
c6494d82
JG
888
889 // Parse id now so errors from here on will have the id
890 id = find_value(request, "id");
891
892 // Parse method
851f58f9 893 UniValue valMethod = find_value(request, "method");
ed21d5bd 894 if (valMethod.isNull())
738835d7 895 throw JSONRPCError(RPC_INVALID_REQUEST, "Missing method");
ed21d5bd 896 if (!valMethod.isStr())
738835d7 897 throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string");
c6494d82 898 strMethod = valMethod.get_str();
cf0c47b2 899 if (strMethod != "getblocktemplate")
28d4cff0 900 LogPrint("rpc", "ThreadRPCServer method=%s\n", SanitizeString(strMethod));
c6494d82
JG
901
902 // Parse params
851f58f9 903 UniValue valParams = find_value(request, "params");
ed21d5bd 904 if (valParams.isArray())
c6494d82 905 params = valParams.get_array();
ed21d5bd 906 else if (valParams.isNull())
d014114d 907 params = UniValue(UniValue::VARR);
c6494d82 908 else
738835d7 909 throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array");
c6494d82
JG
910}
911
fb78cc23 912
d014114d 913static UniValue JSONRPCExecOne(const UniValue& req)
61338901 914{
38fc4b70 915 UniValue rpc_result(UniValue::VOBJ);
61338901
JG
916
917 JSONRequest jreq;
918 try {
919 jreq.parse(req);
920
851f58f9 921 UniValue result = tableRPC.execute(jreq.strMethod, jreq.params);
ed21d5bd 922 rpc_result = JSONRPCReplyObj(result, NullUniValue, jreq.id);
61338901 923 }
d014114d 924 catch (const UniValue& objError)
61338901 925 {
ed21d5bd 926 rpc_result = JSONRPCReplyObj(NullUniValue, objError, jreq.id);
61338901 927 }
27df4123 928 catch (const std::exception& e)
61338901 929 {
ed21d5bd 930 rpc_result = JSONRPCReplyObj(NullUniValue,
738835d7 931 JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
61338901
JG
932 }
933
934 return rpc_result;
935}
936
d014114d 937static string JSONRPCExecBatch(const UniValue& vReq)
61338901 938{
bf3f5602 939 UniValue ret(UniValue::VARR);
cc71666a 940 for (size_t reqIdx = 0; reqIdx < vReq.size(); reqIdx++)
61338901
JG
941 ret.push_back(JSONRPCExecOne(vReq[reqIdx]));
942
ed21d5bd 943 return ret.write() + "\n";
61338901
JG
944}
945
854d0130
JG
946static bool HTTPReq_JSONRPC(AcceptedConnection *conn,
947 string& strRequest,
948 map<string, string>& mapHeaders,
949 bool fRun)
950{
951 // Check authorization
952 if (mapHeaders.count("authorization") == 0)
953 {
16f33f16 954 conn->stream() << HTTPError(HTTP_UNAUTHORIZED, false) << std::flush;
854d0130
JG
955 return false;
956 }
957
958 if (!HTTPAuthorized(mapHeaders))
959 {
960 LogPrintf("ThreadRPCServer incorrect password attempt from %s\n", conn->peer_address_to_string());
01094bd0 961 /* Deter brute-forcing
b05a89b2
LD
962 We don't support exposing the RPC port, so this shouldn't result
963 in a DoS. */
01094bd0 964 MilliSleep(250);
854d0130 965
16f33f16 966 conn->stream() << HTTPError(HTTP_UNAUTHORIZED, false) << std::flush;
854d0130
JG
967 return false;
968 }
969
970 JSONRequest jreq;
971 try
972 {
973 // Parse request
851f58f9 974 UniValue valRequest;
ed21d5bd 975 if (!valRequest.read(strRequest))
854d0130
JG
976 throw JSONRPCError(RPC_PARSE_ERROR, "Parse error");
977
978 string strReply;
979
980 // singleton request
ed21d5bd 981 if (valRequest.isObject()) {
854d0130
JG
982 jreq.parse(valRequest);
983
851f58f9 984 UniValue result = tableRPC.execute(jreq.strMethod, jreq.params);
854d0130
JG
985
986 // Send reply
ed21d5bd 987 strReply = JSONRPCReply(result, NullUniValue, jreq.id);
854d0130
JG
988
989 // array of requests
ed21d5bd 990 } else if (valRequest.isArray())
854d0130
JG
991 strReply = JSONRPCExecBatch(valRequest.get_array());
992 else
993 throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error");
994
e17151ad 995 conn->stream() << HTTPReplyHeader(HTTP_OK, fRun, strReply.size()) << strReply << std::flush;
854d0130 996 }
d014114d 997 catch (const UniValue& objError)
854d0130
JG
998 {
999 ErrorReply(conn->stream(), objError, jreq.id);
1000 return false;
1001 }
27df4123 1002 catch (const std::exception& e)
854d0130
JG
1003 {
1004 ErrorReply(conn->stream(), JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
1005 return false;
1006 }
1007 return true;
1008}
1009
21eb5ada 1010void ServiceConnection(AcceptedConnection *conn)
e9205293 1011{
96c52695 1012 bool fRun = true;
d138598f 1013 while (fRun && !ShutdownRequested())
21eb5ada 1014 {
2306dc4b 1015 int nProto = 0;
69d605f4 1016 map<string, string> mapHeaders;
fcf234fc
JG
1017 string strRequest, strMethod, strURI;
1018
1019 // Read HTTP request line
1020 if (!ReadHTTPRequestLine(conn->stream(), nProto, strMethod, strURI))
1021 break;
69d605f4 1022
2306dc4b 1023 // Read HTTP message headers and body
733177eb 1024 ReadHTTPMessage(conn->stream(), mapHeaders, strRequest, nProto, MAX_SIZE);
69d605f4 1025
854d0130 1026 // HTTP Keep-Alive is false; close connection immediately
7d2cb485 1027 if ((mapHeaders["connection"] == "close") || (!GetBoolArg("-rpckeepalive", true)))
96c52695 1028 fRun = false;
69d605f4 1029
e2655e0a 1030 // Process via JSON-RPC API
854d0130
JG
1031 if (strURI == "/") {
1032 if (!HTTPReq_JSONRPC(conn, strRequest, mapHeaders, fRun))
1033 break;
e2655e0a
JG
1034
1035 // Process via HTTP REST API
5dc713bf 1036 } else if (strURI.substr(0, 6) == "/rest/" && GetBoolArg("-rest", false)) {
97ee8665 1037 if (!HTTPReq_REST(conn, strURI, strRequest, mapHeaders, fRun))
e2655e0a
JG
1038 break;
1039
40a158e1 1040 } else {
7715c847 1041 conn->stream() << HTTPError(HTTP_NOT_FOUND, false) << std::flush;
e9205293 1042 break;
69d605f4
WL
1043 }
1044 }
1045}
1046
851f58f9 1047UniValue CRPCTable::execute(const std::string &strMethod, const UniValue &params) const
460c51fd 1048{
483672f7
FV
1049 // Return immediately if in warmup
1050 {
1051 LOCK(cs_rpcWarmup);
1052 if (fRPCInWarmup)
1053 throw JSONRPCError(RPC_IN_WARMUP, rpcWarmupStatus);
1054 }
1055
460c51fd
WL
1056 // Find method
1057 const CRPCCommand *pcmd = tableRPC[strMethod];
1058 if (!pcmd)
738835d7 1059 throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found");
69d605f4 1060
4401b2d7 1061 g_rpcSignals.PreCommand(*pcmd);
460c51fd
WL
1062
1063 try
1064 {
1065 // Execute
4401b2d7 1066 return pcmd->actor(params, false);
460c51fd 1067 }
27df4123 1068 catch (const std::exception& e)
460c51fd 1069 {
738835d7 1070 throw JSONRPCError(RPC_MISC_ERROR, e.what());
460c51fd 1071 }
4401b2d7
EL
1072
1073 g_rpcSignals.PostCommand(*pcmd);
460c51fd 1074}
69d605f4 1075
db954a65
PK
1076std::string HelpExampleCli(const std::string& methodname, const std::string& args)
1077{
58c4c0bb 1078 return "> zcash-cli " + methodname + " " + args + "\n";
bbb09365
WL
1079}
1080
db954a65
PK
1081std::string HelpExampleRpc(const std::string& methodname, const std::string& args)
1082{
bbb09365 1083 return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", "
3985a40d 1084 "\"method\": \"" + methodname + "\", \"params\": [" + args + "] }' -H 'content-type: text/plain;' http://127.0.0.1:8232/\n";
bbb09365
WL
1085}
1086
e46704dd 1087const CRPCTable tableRPC;
fc72c078
S
1088
1089// Return async rpc queue
1090std::shared_ptr<AsyncRPCQueue> getAsyncRPCQueue()
1091{
f86f625d 1092 return AsyncRPCQueue::sharedInstance();
fc72c078 1093}
This page took 0.554052 seconds and 4 git commands to generate.