diff --git a/src/Ledger.cpp b/src/Ledger.cpp index 9fca1d4854..fe125c360d 100644 --- a/src/Ledger.cpp +++ b/src/Ledger.cpp @@ -320,6 +320,8 @@ void Ledger::saveAcceptedLedger(Ledger::pointer ledger) } } db->executeSQL("COMMIT TRANSACTION;"); + + theApp->getOPs().pubLedger(ledger); } Ledger::pointer Ledger::getSQL(const std::string& sql) diff --git a/src/NetworkOPs.cpp b/src/NetworkOPs.cpp index 02f033754b..9220d86d4c 100644 --- a/src/NetworkOPs.cpp +++ b/src/NetworkOPs.cpp @@ -719,15 +719,34 @@ void NetworkOPs::pubAccountInfo(const NewcoinAddress& naAccountID, const Json::V } } +void NetworkOPs::pubLedger(const Ledger::pointer& lpAccepted) +{ + if (!mSubLedger.empty()) + { + Json::Value jvObj(Json::objectValue); + + jvObj["type"] = "ledgerAccepted"; + jvObj["seq"] = lpAccepted->getLedgerSeq(); + jvObj["hash"] = lpAccepted->getHash().ToString(); + jvObj["time"] = Json::Value::UInt(lpAccepted->getCloseTimeNC()); + + boost::interprocess::sharable_lock sl(mMonitorLock); + BOOST_FOREACH(InfoSub* ispListener, mSubLedger) + { + ispListener->send(jvObj); + } + } +} + // // Monitoring // -void NetworkOPs::subAccountInfo(InfoSub* ispListener, const std::vector& vnaAccountIDs) +void NetworkOPs::subAccountInfo(InfoSub* ispListener, const boost::unordered_set& vnaAccountIDs) { boost::interprocess::scoped_lock sl(mMonitorLock); - BOOST_FOREACH(NewcoinAddress naAccountID, vnaAccountIDs) + BOOST_FOREACH(const NewcoinAddress& naAccountID, vnaAccountIDs) { subInfoMapType::iterator simIterator = mSubAccountInfo.find(naAccountID); if (simIterator == mSubAccountInfo.end()) @@ -746,11 +765,11 @@ void NetworkOPs::subAccountInfo(InfoSub* ispListener, const std::vector& vnaAccountIDs) +void NetworkOPs::unsubAccountInfo(InfoSub* ispListener, const boost::unordered_set& vnaAccountIDs) { boost::interprocess::scoped_lock sl(mMonitorLock); - BOOST_FOREACH(NewcoinAddress naAccountID, vnaAccountIDs) + BOOST_FOREACH(const NewcoinAddress& naAccountID, vnaAccountIDs) { subInfoMapType::iterator simIterator = mSubAccountInfo.find(naAccountID); if (simIterator == mSubAccountInfo.end()) @@ -772,7 +791,7 @@ void NetworkOPs::unsubAccountInfo(InfoSub* ispListener, const std::vector mSubLedger; public: NetworkOPs(boost::asio::io_service& io_service, LedgerMaster* pLedgerMaster); @@ -65,8 +69,8 @@ public: uint32 getCurrentLedgerID(); OperatingMode getOperatingMode() { return mMode; } inline bool available() { - // XXX don't consider network available till have a closed ledger. - return omDISCONNECTED != mMode; + // XXX Later this can be relaxed to omCONNECTED + return mMode >= omTRACKING; } uint256 getClosedLedger() @@ -155,17 +159,21 @@ public: // void pubAccountInfo(const NewcoinAddress& naAccountID, const Json::Value& jvObj); + void pubLedger(const Ledger::pointer& lpAccepted); // // Monitoring: subscriber side // // --> vnaAddress: empty = all - void subAccountInfo(InfoSub* ispListener, const std::vector& vnaAccountIDs); - void unsubAccountInfo(InfoSub* ispListener, const std::vector& vnaAccountIDs); + void subAccountInfo(InfoSub* ispListener, const boost::unordered_set& vnaAccountIDs); + void unsubAccountInfo(InfoSub* ispListener, const boost::unordered_set& vnaAccountIDs); - void subAccountChanges(InfoSub* ispListener, const uint256 uLedgerHash); - void unsubAccountChanges(InfoSub* ispListener); + // void subAccountChanges(InfoSub* ispListener, const uint256 uLedgerHash); + // void unsubAccountChanges(InfoSub* ispListener); + + bool subLedger(InfoSub* ispListener); + bool unsubLedger(InfoSub* ispListener); }; #endif diff --git a/src/RPCServer.cpp b/src/RPCServer.cpp index 381c91ab79..95f8af8bf7 100644 --- a/src/RPCServer.cpp +++ b/src/RPCServer.cpp @@ -398,7 +398,7 @@ Json::Value RPCServer::accountFromString(const uint256& uLedger, NewcoinAddress& } // account_email_set [] -Json::Value RPCServer::doAccountEmailSet(Json::Value ¶ms) +Json::Value RPCServer::doAccountEmailSet(const Json::Value ¶ms) { NewcoinAddress naSrcAccountID; NewcoinAddress naSeed; @@ -463,7 +463,7 @@ Json::Value RPCServer::doAccountEmailSet(Json::Value ¶ms) // account_info || // account_info || [] -Json::Value RPCServer::doAccountInfo(Json::Value ¶ms) +Json::Value RPCServer::doAccountInfo(const Json::Value ¶ms) { std::string strIdent = params[0u].asString(); bool bIndex; @@ -513,7 +513,7 @@ Json::Value RPCServer::doAccountInfo(Json::Value ¶ms) } // account_lines || [] -Json::Value RPCServer::doAccountLines(Json::Value ¶ms) +Json::Value RPCServer::doAccountLines(const Json::Value ¶ms) { // uint256 uClosed = mNetOps->getClosedLedger(); uint256 uCurrent = mNetOps->getCurrentLedger(); @@ -602,7 +602,7 @@ Json::Value RPCServer::doAccountLines(Json::Value ¶ms) } // account_message_set -Json::Value RPCServer::doAccountMessageSet(Json::Value& params) { +Json::Value RPCServer::doAccountMessageSet(const Json::Value& params) { NewcoinAddress naSrcAccountID; NewcoinAddress naSeed; uint256 uLedger = mNetOps->getCurrentLedger(); @@ -654,7 +654,7 @@ Json::Value RPCServer::doAccountMessageSet(Json::Value& params) { } // account_wallet_set [] -Json::Value RPCServer::doAccountWalletSet(Json::Value& params) { +Json::Value RPCServer::doAccountWalletSet(const Json::Value& params) { NewcoinAddress naSrcAccountID; NewcoinAddress naSeed; uint256 uLedger = mNetOps->getCurrentLedger(); @@ -707,7 +707,7 @@ Json::Value RPCServer::doAccountWalletSet(Json::Value& params) { return obj; } -Json::Value RPCServer::doConnect(Json::Value& params) +Json::Value RPCServer::doConnect(const Json::Value& params) { // connect [port] std::string strIp; @@ -735,7 +735,7 @@ Json::Value RPCServer::doConnect(Json::Value& params) } // credit_set [] [] -Json::Value RPCServer::doCreditSet(Json::Value& params) +Json::Value RPCServer::doCreditSet(const Json::Value& params) { NewcoinAddress naSeed; NewcoinAddress naSrcAccountID; @@ -798,7 +798,7 @@ Json::Value RPCServer::doCreditSet(Json::Value& params) } // data_delete -Json::Value RPCServer::doDataDelete(Json::Value& params) +Json::Value RPCServer::doDataDelete(const Json::Value& params) { std::string strKey = params[0u].asString(); @@ -817,7 +817,7 @@ Json::Value RPCServer::doDataDelete(Json::Value& params) } // data_fetch -Json::Value RPCServer::doDataFetch(Json::Value& params) +Json::Value RPCServer::doDataFetch(const Json::Value& params) { std::string strKey = params[0u].asString(); std::string strValue; @@ -832,7 +832,7 @@ Json::Value RPCServer::doDataFetch(Json::Value& params) } // data_store -Json::Value RPCServer::doDataStore(Json::Value& params) +Json::Value RPCServer::doDataStore(const Json::Value& params) { std::string strKey = params[0u].asString(); std::string strValue = params[1u].asString(); @@ -854,7 +854,7 @@ Json::Value RPCServer::doDataStore(Json::Value& params) // nickname_info // Note: Nicknames are not automatically looked up by commands as they are advisory and can be changed. -Json::Value RPCServer::doNicknameInfo(Json::Value& params) +Json::Value RPCServer::doNicknameInfo(const Json::Value& params) { uint256 uLedger = mNetOps->getCurrentLedger(); @@ -882,7 +882,7 @@ Json::Value RPCServer::doNicknameInfo(Json::Value& params) } // nickname_set [] [] -Json::Value RPCServer::doNicknameSet(Json::Value& params) +Json::Value RPCServer::doNicknameSet(const Json::Value& params) { NewcoinAddress naSrcAccountID; NewcoinAddress naSeed; @@ -967,7 +967,7 @@ Json::Value RPCServer::doNicknameSet(Json::Value& params) // password_fund [] // YYY Make making account default to first account for seed. -Json::Value RPCServer::doPasswordFund(Json::Value ¶ms) +Json::Value RPCServer::doPasswordFund(const Json::Value ¶ms) { NewcoinAddress naSrcAccountID; NewcoinAddress naDstAccountID; @@ -1017,7 +1017,7 @@ Json::Value RPCServer::doPasswordFund(Json::Value ¶ms) } // password_set [] -Json::Value RPCServer::doPasswordSet(Json::Value& params) +Json::Value RPCServer::doPasswordSet(const Json::Value& params) { NewcoinAddress naMasterSeed; NewcoinAddress naRegularSeed; @@ -1111,7 +1111,7 @@ Json::Value RPCServer::doPasswordSet(Json::Value& params) } } -Json::Value RPCServer::doPeers(Json::Value& params) +Json::Value RPCServer::doPeers(const Json::Value& params) { // peers Json::Value obj(Json::objectValue); @@ -1120,7 +1120,7 @@ Json::Value RPCServer::doPeers(Json::Value& params) } // send regular_seed paying_account account_id amount [currency] [send_max] [send_currency] -Json::Value RPCServer::doSend(Json::Value& params) +Json::Value RPCServer::doSend(const Json::Value& params) { NewcoinAddress naSeed; NewcoinAddress naSrcAccountID; @@ -1236,7 +1236,7 @@ Json::Value RPCServer::doSend(Json::Value& params) } } -Json::Value RPCServer::doServerInfo(Json::Value& params) +Json::Value RPCServer::doServerInfo(const Json::Value& params) { Json::Value ret(Json::objectValue); ret["info"]=theApp->getOPs().getServerInfo(); @@ -1244,7 +1244,7 @@ Json::Value RPCServer::doServerInfo(Json::Value& params) } // transit_set -Json::Value RPCServer::doTransitSet(Json::Value& params) +Json::Value RPCServer::doTransitSet(const Json::Value& params) { NewcoinAddress naSeed; NewcoinAddress naSrcAccountID; @@ -1314,7 +1314,7 @@ Json::Value RPCServer::doTransitSet(Json::Value& params) } } -Json::Value RPCServer::doTx(Json::Value& params) +Json::Value RPCServer::doTx(const Json::Value& params) { // tx // tx @@ -1341,7 +1341,7 @@ Json::Value RPCServer::doTx(Json::Value& params) } // ledger [id|current|lastclosed] [full] -Json::Value RPCServer::doLedger(Json::Value& params) +Json::Value RPCServer::doLedger(const Json::Value& params) { if (getParamCount(params) == 0) { @@ -1380,7 +1380,7 @@ Json::Value RPCServer::doLedger(Json::Value& params) // account_tx // account_tx -Json::Value RPCServer::doAccountTransactions(Json::Value& params) +Json::Value RPCServer::doAccountTransactions(const Json::Value& params) { std::string param; uint32 minLedger, maxLedger; @@ -1453,7 +1453,7 @@ Json::Value RPCServer::doAccountTransactions(Json::Value& params) } // unl_add | [] -Json::Value RPCServer::doUnlAdd(Json::Value& params) +Json::Value RPCServer::doUnlAdd(const Json::Value& params) { std::string strNode = params[0u].asString(); std::string strComment = (params.size() == 2) ? params[1u].asString() : ""; @@ -1478,7 +1478,7 @@ Json::Value RPCServer::doUnlAdd(Json::Value& params) // // NOTE: It is poor security to specify secret information on the command line. This information might be saved in the command // shell history file (e.g. .bash_history) and it may be leaked via the process status command (i.e. ps). -Json::Value RPCServer::doValidationCreate(Json::Value& params) { +Json::Value RPCServer::doValidationCreate(const Json::Value& params) { NewcoinAddress naSeed; Json::Value obj(Json::objectValue); @@ -1504,7 +1504,7 @@ Json::Value RPCServer::doValidationCreate(Json::Value& params) { // // NOTE: It is poor security to specify secret information on the command line. This information might be saved in the command // shell history file (e.g. .bash_history) and it may be leaked via the process status command (i.e. ps). -Json::Value RPCServer::doValidationSeed(Json::Value& params) { +Json::Value RPCServer::doValidationSeed(const Json::Value& params) { Json::Value obj(Json::objectValue); if (params.empty()) @@ -1559,7 +1559,7 @@ Json::Value RPCServer::accounts(const uint256& uLedger, const NewcoinAddress& na } // wallet_accounts -Json::Value RPCServer::doWalletAccounts(Json::Value& params) +Json::Value RPCServer::doWalletAccounts(const Json::Value& params) { NewcoinAddress naSeed; uint256 uLedger = mNetOps->getCurrentLedger(); @@ -1598,7 +1598,7 @@ Json::Value RPCServer::doWalletAccounts(Json::Value& params) } // wallet_add [] [] -Json::Value RPCServer::doWalletAdd(Json::Value& params) +Json::Value RPCServer::doWalletAdd(const Json::Value& params) { NewcoinAddress naMasterSeed; NewcoinAddress naRegularSeed; @@ -1703,7 +1703,7 @@ Json::Value RPCServer::doWalletAdd(Json::Value& params) // wallet_claim [] [] // // To provide an example to client writers, we do everything we expect a client to do here. -Json::Value RPCServer::doWalletClaim(Json::Value& params) +Json::Value RPCServer::doWalletClaim(const Json::Value& params) { NewcoinAddress naMasterSeed; NewcoinAddress naRegularSeed; @@ -1788,7 +1788,7 @@ Json::Value RPCServer::doWalletClaim(Json::Value& params) // We don't allow creating an account_id by default here because we want to make sure the person has a chance to write down the // master seed of the account to be created. // YYY Need annotation and source tag -Json::Value RPCServer::doWalletCreate(Json::Value& params) +Json::Value RPCServer::doWalletCreate(const Json::Value& params) { NewcoinAddress naSrcAccountID; NewcoinAddress naDstAccountID; @@ -1849,7 +1849,7 @@ Json::Value RPCServer::doWalletCreate(Json::Value& params) } // wallet_propose -Json::Value RPCServer::doWalletPropose(Json::Value& params) +Json::Value RPCServer::doWalletPropose(const Json::Value& params) { NewcoinAddress naSeed; NewcoinAddress naAccount; @@ -1869,7 +1869,7 @@ Json::Value RPCServer::doWalletPropose(Json::Value& params) } // wallet_seed [||] -Json::Value RPCServer::doWalletSeed(Json::Value& params) +Json::Value RPCServer::doWalletSeed(const Json::Value& params) { NewcoinAddress naSeed; @@ -1901,7 +1901,7 @@ Json::Value RPCServer::doWalletSeed(Json::Value& params) } // unl_delete | -Json::Value RPCServer::doUnlDelete(Json::Value& params) +Json::Value RPCServer::doUnlDelete(const Json::Value& params) { std::string strNode = params[0u].asString(); @@ -1921,7 +1921,7 @@ Json::Value RPCServer::doUnlDelete(Json::Value& params) } } -Json::Value RPCServer::doUnlList(Json::Value& params) +Json::Value RPCServer::doUnlList(const Json::Value& params) { Json::Value obj(Json::objectValue); @@ -1931,7 +1931,7 @@ Json::Value RPCServer::doUnlList(Json::Value& params) } // Populate the UNL from a local validators.txt file. -Json::Value RPCServer::doUnlLoad(Json::Value& params) +Json::Value RPCServer::doUnlLoad(const Json::Value& params) { if (theConfig.UNL_DEFAULT.empty() || !theApp->getUNL().nodeLoad(theConfig.UNL_DEFAULT)) { @@ -1942,7 +1942,7 @@ Json::Value RPCServer::doUnlLoad(Json::Value& params) } // Populate the UNL from newcoin.org's validators.txt file. -Json::Value RPCServer::doUnlNetwork(Json::Value& params) +Json::Value RPCServer::doUnlNetwork(const Json::Value& params) { theApp->getUNL().nodeNetwork(); @@ -1950,7 +1950,7 @@ Json::Value RPCServer::doUnlNetwork(Json::Value& params) } // unl_reset -Json::Value RPCServer::doUnlReset(Json::Value& params) +Json::Value RPCServer::doUnlReset(const Json::Value& params) { theApp->getUNL().nodeReset(); @@ -1958,14 +1958,14 @@ Json::Value RPCServer::doUnlReset(Json::Value& params) } // unl_score -Json::Value RPCServer::doUnlScore(Json::Value& params) +Json::Value RPCServer::doUnlScore(const Json::Value& params) { theApp->getUNL().nodeScore(); return "scoring requested"; } -Json::Value RPCServer::doStop(Json::Value& params) +Json::Value RPCServer::doStop(const Json::Value& params) { theApp->stop(); @@ -1975,7 +1975,7 @@ Json::Value RPCServer::doStop(Json::Value& params) // TODO: for now this simply checks if this is the admin account // TODO: need to prevent them hammering this over and over // TODO: maybe a better way is only allow admin from local host -Json::Value RPCServer::doLogin(Json::Value& params) +Json::Value RPCServer::doLogin(const Json::Value& params) { std::string username = params[0u].asString(); std::string password = params[1u].asString(); @@ -2054,7 +2054,8 @@ Json::Value RPCServer::doCommand(const std::string& command, Json::Value& params if (i < 0) { return RPCError(rpcUNKNOWN_COMMAND); - }else if (commandsA[i].mAdminRequired && mRole!=ADMIN) + } + else if (commandsA[i].mAdminRequired && mRole!=ADMIN) { return RPCError(rpcNO_PERMISSION); } diff --git a/src/RPCServer.h b/src/RPCServer.h index 532b1675f3..b9db630bb2 100644 --- a/src/RPCServer.h +++ b/src/RPCServer.h @@ -72,7 +72,7 @@ public: Json::Value RPCError(int iError); private: - typedef Json::Value (RPCServer::*doFuncPtr)(Json::Value ¶ms); + typedef Json::Value (RPCServer::*doFuncPtr)(const Json::Value ¶ms); enum { optNetwork = 1, // Need network optCurrent = 2+optNetwork, // Need current ledger @@ -116,54 +116,54 @@ private: Json::Value accountFromString(const uint256& uLedger, NewcoinAddress& naAccount, bool& bIndex, const std::string& strIdent, const int iIndex); - Json::Value doAccountEmailSet(Json::Value ¶ms); - Json::Value doAccountInfo(Json::Value& params); - Json::Value doAccountLines(Json::Value ¶ms); - Json::Value doAccountMessageSet(Json::Value ¶ms); - Json::Value doAccountTransactions(Json::Value& params); - Json::Value doAccountWalletSet(Json::Value ¶ms); - Json::Value doConnect(Json::Value& params); - Json::Value doCreditSet(Json::Value& params); - Json::Value doDataDelete(Json::Value& params); - Json::Value doDataFetch(Json::Value& params); - Json::Value doDataStore(Json::Value& params); - Json::Value doLedger(Json::Value& params); - Json::Value doNicknameInfo(Json::Value& params); - Json::Value doNicknameSet(Json::Value& params); - Json::Value doPasswordFund(Json::Value& params); - Json::Value doPasswordSet(Json::Value& params); - Json::Value doPeers(Json::Value& params); - Json::Value doSend(Json::Value& params); - Json::Value doServerInfo(Json::Value& params); - Json::Value doSessionClose(Json::Value& params); - Json::Value doSessionOpen(Json::Value& params); - Json::Value doStop(Json::Value& params); - Json::Value doTransitSet(Json::Value& params); - Json::Value doTx(Json::Value& params); + Json::Value doAccountEmailSet(const Json::Value ¶ms); + Json::Value doAccountInfo(const Json::Value& params); + Json::Value doAccountLines(const Json::Value ¶ms); + Json::Value doAccountMessageSet(const Json::Value ¶ms); + Json::Value doAccountTransactions(const Json::Value& params); + Json::Value doAccountWalletSet(const Json::Value ¶ms); + Json::Value doConnect(const Json::Value& params); + Json::Value doCreditSet(const Json::Value& params); + Json::Value doDataDelete(const Json::Value& params); + Json::Value doDataFetch(const Json::Value& params); + Json::Value doDataStore(const Json::Value& params); + Json::Value doLedger(const Json::Value& params); + Json::Value doNicknameInfo(const Json::Value& params); + Json::Value doNicknameSet(const Json::Value& params); + Json::Value doPasswordFund(const Json::Value& params); + Json::Value doPasswordSet(const Json::Value& params); + Json::Value doPeers(const Json::Value& params); + Json::Value doSend(const Json::Value& params); + Json::Value doServerInfo(const Json::Value& params); + Json::Value doSessionClose(const Json::Value& params); + Json::Value doSessionOpen(const Json::Value& params); + Json::Value doStop(const Json::Value& params); + Json::Value doTransitSet(const Json::Value& params); + Json::Value doTx(const Json::Value& params); - Json::Value doUnlAdd(Json::Value& params); - Json::Value doUnlDelete(Json::Value& params); - Json::Value doUnlFetch(Json::Value& params); - Json::Value doUnlList(Json::Value& params); - Json::Value doUnlLoad(Json::Value& params); - Json::Value doUnlNetwork(Json::Value& params); - Json::Value doUnlReset(Json::Value& params); - Json::Value doUnlScore(Json::Value& params); + Json::Value doUnlAdd(const Json::Value& params); + Json::Value doUnlDelete(const Json::Value& params); + Json::Value doUnlFetch(const Json::Value& params); + Json::Value doUnlList(const Json::Value& params); + Json::Value doUnlLoad(const Json::Value& params); + Json::Value doUnlNetwork(const Json::Value& params); + Json::Value doUnlReset(const Json::Value& params); + Json::Value doUnlScore(const Json::Value& params); - Json::Value doValidationCreate(Json::Value& params); - Json::Value doValidationSeed(Json::Value& params); + Json::Value doValidationCreate(const Json::Value& params); + Json::Value doValidationSeed(const Json::Value& params); - Json::Value doWalletAccounts(Json::Value& params); - Json::Value doWalletAdd(Json::Value& params); - Json::Value doWalletClaim(Json::Value& params); - Json::Value doWalletCreate(Json::Value& params); - Json::Value doWalletLock(Json::Value& params); - Json::Value doWalletPropose(Json::Value& params); - Json::Value doWalletSeed(Json::Value& params); - Json::Value doWalletUnlock(Json::Value& params); - Json::Value doWalletVerify(Json::Value& params); + Json::Value doWalletAccounts(const Json::Value& params); + Json::Value doWalletAdd(const Json::Value& params); + Json::Value doWalletClaim(const Json::Value& params); + Json::Value doWalletCreate(const Json::Value& params); + Json::Value doWalletLock(const Json::Value& params); + Json::Value doWalletPropose(const Json::Value& params); + Json::Value doWalletSeed(const Json::Value& params); + Json::Value doWalletUnlock(const Json::Value& params); + Json::Value doWalletVerify(const Json::Value& params); - Json::Value doLogin(Json::Value& params); + Json::Value doLogin(const Json::Value& params); public: typedef boost::shared_ptr pointer; diff --git a/src/WSDoor.cpp b/src/WSDoor.cpp index ce51bb5125..198180de7d 100644 --- a/src/WSDoor.cpp +++ b/src/WSDoor.cpp @@ -5,6 +5,7 @@ #include "Config.h" #include "Log.h" #include "NetworkOPs.h" +#include "NetworkOPs.h" #include "utils.h" #include @@ -12,6 +13,7 @@ #include #include #include +#include #include "../json/reader.h" #include "../json/writer.h" @@ -47,6 +49,11 @@ public: typedef websocketpp::WSDOOR_SERVER::handler::message_ptr message_ptr; protected: + typedef void (WSConnection::*doFuncPtr)(Json::Value& jvResult, const Json::Value &jvRequest); + + boost::mutex mLock; + boost::unordered_set mSubAccountInfo; + WSServerHandler* mHandler; connection_ptr mConnection; @@ -58,14 +65,20 @@ public: WSConnection(WSServerHandler* wshpHandler, connection_ptr cpConnection) : mHandler(wshpHandler), mConnection(cpConnection) { ; } - ~WSConnection() - { - // XXX Unsubscribe. - nothing(); - } + virtual ~WSConnection(); // Implement overridden functions from base class: void send(const Json::Value& jvObj); + + // Utilities + Json::Value invokeCommand(const Json::Value& jvRequest); + boost::unordered_set parseAccountIds(const Json::Value& jvArray); + + // Commands + void doAccountInfoSubscribe(Json::Value& jvResult, const Json::Value& jvRequest); + void doAccountInfoUnsubscribe(Json::Value& jvResult, const Json::Value& jvRequest); + void doLedgerSubcribe(Json::Value& jvResult, const Json::Value& jvRequest); + void doLedgerUnsubscribe(Json::Value& jvResult, const Json::Value& jvRequest); }; @@ -88,7 +101,8 @@ private: protected: boost::mutex mMapLock; - boost::unordered_map mMap; + // For each connection maintain an assoicated object to track subscriptions. + boost::unordered_map > mMap; public: WSServerHandler(boost::shared_ptr spCtx) : mCtx(spCtx) {} @@ -130,7 +144,6 @@ public: Log(lsINFO) << "Ws:: Object '" << jfwWriter.write(jvObj) << "'"; - send(cpClient, jfwWriter.write(jvObj)); } @@ -138,7 +151,7 @@ public: { boost::mutex::scoped_lock sl(mMapLock); - mMap[cpClient] = WSConnection(this, cpClient); + mMap[cpClient] = boost::make_shared(this, cpClient); } void on_close(connection_ptr cpClient) @@ -174,12 +187,7 @@ public: } else { - Json::Value jvResult(Json::objectValue); - - jvResult["type"] = "success"; - jvResult["value"] = mpMessage->get_payload(); - - send(cpClient, jvResult); + send(cpClient, mMap[cpClient]->invokeCommand(jvRequest)); } } @@ -250,9 +258,192 @@ void WSDoor::stop() } } +// +// WSConnection +// + +WSConnection::~WSConnection() +{ + theApp->getOPs().unsubLedger(this); + theApp->getOPs().unsubAccountInfo(this, mSubAccountInfo); +} + void WSConnection::send(const Json::Value& jvObj) { mHandler->send(mConnection, jvObj); } +// +// Utilities +// + +Json::Value WSConnection::invokeCommand(const Json::Value& jvRequest) +{ + static struct { + const char* pCommand; + doFuncPtr dfpFunc; + } commandsA[] = { + { "account_info_subscribe", &WSConnection::doAccountInfoSubscribe }, + { "account_info_unsubscribe", &WSConnection::doAccountInfoUnsubscribe }, + { "ledger_subscribe", &WSConnection::doLedgerSubcribe }, + { "ledger_unsubscribe", &WSConnection::doLedgerUnsubscribe }, + }; + + + if (!jvRequest.isMember("command")) + { + Json::Value jvResult(Json::objectValue); + + jvResult["type"] = "response"; + jvResult["result"] = "error"; + jvResult["error"] = "missingCommand"; + jvResult["command"] = jvRequest; + + return jvResult; + } + + std::string strCommand = jvRequest["command"].asString(); + + int i = NUMBER(commandsA); + + while (i-- && strCommand != commandsA[i].pCommand) + ; + + Json::Value jvResult(Json::objectValue); + + jvResult["type"] = "response"; + + if (i < 0) + { + jvResult["error"] = "unknownCommand"; // Unknown command. + } + else + { + (this->*(commandsA[i].dfpFunc))(jvResult, jvRequest); + } + + if (jvRequest.isMember("id")) + { + jvResult["id"] = jvRequest["id"]; + } + + if (jvResult.isMember("error")) + { + jvResult["result"] = "error"; + jvResult["request"] = jvRequest; + } + else + { + jvResult["result"] = "success"; + } + + return jvResult; +} + +boost::unordered_set WSConnection::parseAccountIds(const Json::Value& jvArray) +{ + boost::unordered_set usnaResult; + + for (Json::Value::const_iterator it = jvArray.begin(); it != jvArray.end(); it++) + { + NewcoinAddress naString; + + if (!(*it).isString() || !naString.setAccountID((*it).asString())) + { + usnaResult.clear(); + break; + } + else + { + (void) usnaResult.insert(naString); + } + } + + return usnaResult; +} + +// +// Commands +// + +void WSConnection::doAccountInfoSubscribe(Json::Value& jvResult, const Json::Value& jvRequest) +{ + if (!jvRequest.isMember("accounts")) + { + jvResult["error"] = "missingField"; + } + else if (jvResult["accounts"].empty()) + { + jvResult["error"] = "emptySet"; + } + else + { + boost::unordered_set usnaAccoundIds = parseAccountIds(jvRequest["accounts"]); + + if (usnaAccoundIds.empty()) + { + jvResult["error"] = "malformedAccount"; + } + else + { + boost::mutex::scoped_lock sl(mLock); + + BOOST_FOREACH(const NewcoinAddress& naAccountID, usnaAccoundIds) + { + mSubAccountInfo.insert(naAccountID); + } + + theApp->getOPs().subAccountInfo(this, usnaAccoundIds); + } + } +} + +void WSConnection::doAccountInfoUnsubscribe(Json::Value& jvResult, const Json::Value& jvRequest) +{ + if (!jvRequest.isMember("accounts")) + { + jvResult["error"] = "missingField"; + } + else if (jvResult["accounts"].empty()) + { + jvResult["error"] = "emptySet"; + } + else + { + boost::unordered_set usnaAccoundIds = parseAccountIds(jvRequest["accounts"]); + + if (usnaAccoundIds.empty()) + { + jvResult["error"] = "malformedAccount"; + } + else + { + boost::mutex::scoped_lock sl(mLock); + + BOOST_FOREACH(const NewcoinAddress& naAccountID, usnaAccoundIds) + { + mSubAccountInfo.erase(naAccountID); + } + + theApp->getOPs().unsubAccountInfo(this, usnaAccoundIds); + } + } +} + +void WSConnection::doLedgerSubcribe(Json::Value& jvResult, const Json::Value& jvRequest) +{ + if (!theApp->getOPs().subLedger(this)) + { + jvResult["error"] = "ledgerSubscribed"; + } +} + +void WSConnection::doLedgerUnsubscribe(Json::Value& jvResult, const Json::Value& jvRequest) +{ + if (!theApp->getOPs().unsubLedger(this)) + { + jvResult["error"] = "ledgerNotSubscribed"; + } +} + // vim:ts=4