// FIXME: This leaks RPCSub objects for JSON-RPC. Shouldn't matter for anyone // sane. Json::Value doUnsubscribe (RPC::Context& context) { InfoSub::pointer ispSub; Json::Value jvResult (Json::objectValue); if (!context.infoSub && !context.params.isMember (jss::url)) { // Must be a JSON-RPC call. return rpcError (rpcINVALID_PARAMS); } if (context.params.isMember (jss::url)) { if (context.role != Role::ADMIN) return rpcError (rpcNO_PERMISSION); std::string strUrl = context.params[jss::url].asString (); ispSub = context.netOps.findRpcSub (strUrl); if (!ispSub) return jvResult; } else { ispSub = context.infoSub; } if (context.params.isMember (jss::streams)) { for (auto& it: context.params[jss::streams]) { if (it.isString ()) { std::string streamName = it.asString (); if (streamName == "server") context.netOps.unsubServer (ispSub->getSeq ()); else if (streamName == "ledger") context.netOps.unsubLedger (ispSub->getSeq ()); else if (streamName == "transactions") context.netOps.unsubTransactions (ispSub->getSeq ()); else if (streamName == "transactions_proposed" || streamName == "rt_transactions") // DEPRECATED context.netOps.unsubRTTransactions (ispSub->getSeq ()); else jvResult[jss::error] = "Unknown stream: " + streamName; } else { jvResult[jss::error] = "malformedSteam"; } } } if (context.params.isMember (jss::accounts_proposed) || context.params.isMember (jss::rt_accounts)) { auto accounts = RPC::parseAccountIds ( context.params.isMember (jss::accounts_proposed) ? context.params[jss::accounts_proposed] : context.params[jss::rt_accounts]); // DEPRECATED if (accounts.empty ()) jvResult[jss::error] = "malformedAccount"; else context.netOps.unsubAccount (ispSub, accounts, true); } if (context.params.isMember (jss::accounts)) { auto accounts = RPC::parseAccountIds (context.params[jss::accounts]); if (accounts.empty ()) jvResult[jss::error] = "malformedAccount"; else context.netOps.unsubAccount (ispSub, accounts, false); } if (!context.params.isMember (jss::books)) { } else if (!context.params[jss::books].isArray ()) { return rpcError (rpcINVALID_PARAMS); } else { for (auto& jv: context.params[jss::books]) { if (!jv.isObject () || !jv.isMember (jss::taker_pays) || !jv.isMember (jss::taker_gets) || !jv[jss::taker_pays].isObject () || !jv[jss::taker_gets].isObject ()) return rpcError (rpcINVALID_PARAMS); bool bBoth = (jv.isMember (jss::both) && jv[jss::both].asBool ()) || (jv.isMember (jss::both_sides) && jv[jss::both_sides].asBool ()); // both_sides is deprecated. Json::Value taker_pays = jv[jss::taker_pays]; Json::Value taker_gets = jv[jss::taker_gets]; Book book; // Parse mandatory currency. if (!taker_pays.isMember (jss::currency) || !to_currency ( book.in.currency, taker_pays[jss::currency].asString ())) { WriteLog (lsINFO, RPCHandler) << "Bad taker_pays currency."; return rpcError (rpcSRC_CUR_MALFORMED); } // Parse optional issuer. else if (((taker_pays.isMember (jss::issuer)) && (!taker_pays[jss::issuer].isString () || !to_issuer ( book.in.account, taker_pays[jss::issuer].asString ()))) // Don't allow illegal issuers. || !isConsistent (book.in) || noAccount() == book.in.account) { WriteLog (lsINFO, RPCHandler) << "Bad taker_pays issuer."; return rpcError (rpcSRC_ISR_MALFORMED); } // Parse mandatory currency. if (!taker_gets.isMember (jss::currency) || !to_currency (book.out.currency, taker_gets[jss::currency].asString ())) { WriteLog (lsINFO, RPCHandler) << "Bad taker_pays currency."; return rpcError (rpcSRC_CUR_MALFORMED); } // Parse optional issuer. else if (((taker_gets.isMember (jss::issuer)) && (!taker_gets[jss::issuer].isString () || !to_issuer (book.out.account, taker_gets[jss::issuer].asString ()))) // Don't allow illegal issuers. || !isConsistent (book.out) || noAccount() == book.out.account) { WriteLog (lsINFO, RPCHandler) << "Bad taker_gets issuer."; return rpcError (rpcDST_ISR_MALFORMED); } if (book.in == book.out) { WriteLog (lsINFO, RPCHandler) << "taker_gets same as taker_pays."; return rpcError (rpcBAD_MARKET); } context.netOps.unsubBook (ispSub->getSeq (), book); if (bBoth) context.netOps.unsubBook (ispSub->getSeq (), book); } } return jvResult; }
void PathRequests::updateAll (Ledger::ref inLedger, CancelCallback shouldCancel) { std::vector<PathRequest::wptr> requests; LoadEvent::autoptr event (getApp().getJobQueue().getLoadEventAP(jtPATH_FIND, "PathRequest::updateAll")); // Get the ledger and cache we should be using Ledger::pointer ledger = inLedger; RippleLineCache::pointer cache; { ScopedLockType sl (mLock); requests = mRequests; cache = getLineCache (ledger, true); } bool newRequests = getApp().getLedgerMaster().isNewPathRequest(); bool mustBreak = false; mJournal.trace << "updateAll seq=" << ledger->getLedgerSeq() << ", " << requests.size() << " requests"; int processed = 0, removed = 0; do { BOOST_FOREACH (PathRequest::wref wRequest, requests) { if (shouldCancel()) break; bool remove = true; PathRequest::pointer pRequest = wRequest.lock (); if (pRequest) { if (!pRequest->needsUpdate (newRequests, ledger->getLedgerSeq ())) remove = false; else { InfoSub::pointer ipSub = pRequest->getSubscriber (); if (ipSub) { ipSub->getConsumer ().charge (Resource::feePathFindUpdate); if (!ipSub->getConsumer ().warn ()) { Json::Value update = pRequest->doUpdate (cache, false); pRequest->updateComplete (); update["type"] = "path_find"; ipSub->send (update, false); remove = false; ++processed; } } } } if (remove) { PathRequest::pointer pRequest = wRequest.lock (); ScopedLockType sl (mLock); // Remove any dangling weak pointers or weak pointers that refer to this path request. std::vector<PathRequest::wptr>::iterator it = mRequests.begin(); while (it != mRequests.end()) { PathRequest::pointer itRequest = it->lock (); if (!itRequest || (itRequest == pRequest)) { ++removed; it = mRequests.erase (it); } else ++it; } } mustBreak = !newRequests && getApp().getLedgerMaster().isNewPathRequest(); if (mustBreak) // We weren't handling new requests and then there was a new request break; } if (mustBreak) { // a new request came in while we were working newRequests = true; } else if (newRequests) { // we only did new requests, so we always need a last pass newRequests = getApp().getLedgerMaster().isNewPathRequest(); } else { // check if there are any new requests, otherwise we are done newRequests = getApp().getLedgerMaster().isNewPathRequest(); if (!newRequests) // We did a full pass and there are no new requests return; } { // Get the latest requests, cache, and ledger for next pass ScopedLockType sl (mLock); if (mRequests.empty()) break; requests = mRequests; cache = getLineCache (ledger, false); } } while (!shouldCancel ()); mJournal.debug << "updateAll complete " << processed << " process and " << removed << " removed"; }
Json::Value doUnsubscribe (RPC::Context& context) { InfoSub::pointer ispSub; Json::Value jvResult (Json::objectValue); bool removeUrl {false}; if (! context.infoSub && ! context.params.isMember(jss::url)) { // Must be a JSON-RPC call. return rpcError(rpcINVALID_PARAMS); } if (context.params.isMember(jss::url)) { if (context.role != Role::ADMIN) return rpcError(rpcNO_PERMISSION); std::string strUrl = context.params[jss::url].asString (); ispSub = context.netOps.findRpcSub (strUrl); if (! ispSub) return jvResult; removeUrl = true; } else { ispSub = context.infoSub; } if (context.params.isMember (jss::streams)) { if (! context.params[jss::streams].isArray ()) return rpcError (rpcINVALID_PARAMS); for (auto& it: context.params[jss::streams]) { if (! it.isString()) return rpcError(rpcSTREAM_MALFORMED); std::string streamName = it.asString (); if (streamName == "server") { context.netOps.unsubServer (ispSub->getSeq ()); } else if (streamName == "ledger") { context.netOps.unsubLedger (ispSub->getSeq ()); } else if (streamName == "manifests") { context.netOps.unsubManifests (ispSub->getSeq ()); } else if (streamName == "transactions") { context.netOps.unsubTransactions (ispSub->getSeq ()); } else if (streamName == "transactions_proposed" || streamName == "rt_transactions") // DEPRECATED { context.netOps.unsubRTTransactions (ispSub->getSeq ()); } else if (streamName == "validations") { context.netOps.unsubValidations (ispSub->getSeq ()); } else if (streamName == "peer_status") { context.netOps.unsubPeerStatus (ispSub->getSeq ()); } else { return rpcError(rpcSTREAM_MALFORMED); } } } auto accountsProposed = context.params.isMember(jss::accounts_proposed) ? jss::accounts_proposed : jss::rt_accounts; // DEPRECATED if (context.params.isMember(accountsProposed)) { if (! context.params[accountsProposed].isArray()) return rpcError(rpcINVALID_PARAMS); auto ids = RPC::parseAccountIds(context.params[accountsProposed]); if (ids.empty()) return rpcError(rpcACT_MALFORMED); context.netOps.unsubAccount(ispSub, ids, true); } if (context.params.isMember(jss::accounts)) { if (! context.params[jss::accounts].isArray()) return rpcError(rpcINVALID_PARAMS); auto ids = RPC::parseAccountIds(context.params[jss::accounts]); if (ids.empty()) return rpcError(rpcACT_MALFORMED); context.netOps.unsubAccount(ispSub, ids, false); } if (context.params.isMember(jss::books)) { if (! context.params[jss::books].isArray()) return rpcError(rpcINVALID_PARAMS); for (auto& jv: context.params[jss::books]) { if (! jv.isObject() || ! jv.isMember(jss::taker_pays) || ! jv.isMember(jss::taker_gets) || ! jv[jss::taker_pays].isObject() || ! jv[jss::taker_gets].isObject()) { return rpcError(rpcINVALID_PARAMS); } Json::Value taker_pays = jv[jss::taker_pays]; Json::Value taker_gets = jv[jss::taker_gets]; Book book; // Parse mandatory currency. if (!taker_pays.isMember (jss::currency) || !to_currency ( book.in.currency, taker_pays[jss::currency].asString ())) { JLOG (context.j.info()) << "Bad taker_pays currency."; return rpcError (rpcSRC_CUR_MALFORMED); } // Parse optional issuer. else if (((taker_pays.isMember (jss::issuer)) && (!taker_pays[jss::issuer].isString () || !to_issuer ( book.in.account, taker_pays[jss::issuer].asString ()))) // Don't allow illegal issuers. || !isConsistent (book.in) || noAccount() == book.in.account) { JLOG (context.j.info()) << "Bad taker_pays issuer."; return rpcError (rpcSRC_ISR_MALFORMED); } // Parse mandatory currency. if (!taker_gets.isMember (jss::currency) || !to_currency (book.out.currency, taker_gets[jss::currency].asString ())) { JLOG (context.j.info()) << "Bad taker_gets currency."; return rpcError (rpcDST_AMT_MALFORMED); } // Parse optional issuer. else if (((taker_gets.isMember (jss::issuer)) && (!taker_gets[jss::issuer].isString () || !to_issuer (book.out.account, taker_gets[jss::issuer].asString ()))) // Don't allow illegal issuers. || !isConsistent (book.out) || noAccount() == book.out.account) { JLOG (context.j.info()) << "Bad taker_gets issuer."; return rpcError (rpcDST_ISR_MALFORMED); } if (book.in == book.out) { JLOG (context.j.info()) << "taker_gets same as taker_pays."; return rpcError (rpcBAD_MARKET); } context.netOps.unsubBook (ispSub->getSeq (), book); // both_sides is deprecated. if ((jv.isMember(jss::both) && jv[jss::both].asBool()) || (jv.isMember(jss::both_sides) && jv[jss::both_sides].asBool())) { context.netOps.unsubBook(ispSub->getSeq(), reversed(book)); } } } if (removeUrl) { context.netOps.tryRemoveRpcSub(context.params[jss::url].asString ()); } return jvResult; }