mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
.
This commit is contained in:
@@ -171,6 +171,7 @@
|
||||
<ClCompile Include="src\utils.cpp" />
|
||||
<ClCompile Include="src\ValidationCollection.cpp" />
|
||||
<ClCompile Include="src\Wallet.cpp" />
|
||||
<ClCompile Include="src\WSConnection.cpp" />
|
||||
<ClCompile Include="src\WSDoor.cpp" />
|
||||
<ClCompile Include="websocketpp\src\base64\base64.cpp" />
|
||||
<ClCompile Include="websocketpp\src\md5\md5.c" />
|
||||
@@ -263,6 +264,7 @@
|
||||
<ClInclude Include="src\utils.h" />
|
||||
<ClInclude Include="src\ValidationCollection.h" />
|
||||
<ClInclude Include="src\Wallet.h" />
|
||||
<ClInclude Include="src\WSHandler.h" />
|
||||
<ClInclude Include="TimingService.h" />
|
||||
<ClInclude Include="ExtendedTransaction.h" />
|
||||
<ClInclude Include="util\pugiconfig.hpp" />
|
||||
|
||||
@@ -309,6 +309,15 @@
|
||||
<ClCompile Include="src\RangeSet.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\JobQueue.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\InstanceCounter.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\WSConnection.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="KnownNodeList.h">
|
||||
@@ -566,6 +575,9 @@
|
||||
<ClInclude Include="src\RPCHandler.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\WSHandler.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="wallet.xml" />
|
||||
|
||||
@@ -42,7 +42,7 @@ Application::Application() :
|
||||
mNetOps(mIOService, &mMasterLedger), mTempNodeCache("NodeCache", 16384, 90), mHashedObjectStore(16384, 300),
|
||||
mSNTPClient(mAuxService), mRpcDB(NULL), mTxnDB(NULL), mLedgerDB(NULL), mWalletDB(NULL),
|
||||
mHashNodeDB(NULL), mNetNodeDB(NULL),
|
||||
mConnectionPool(mIOService), mPeerDoor(NULL), mRPCDoor(NULL), mSweepTimer(mAuxService)
|
||||
mConnectionPool(mIOService), mPeerDoor(NULL), mRPCDoor(NULL), mSweepTimer(mAuxService), mRPCHandler(&mNetOps)
|
||||
{
|
||||
RAND_bytes(mNonce256.begin(), mNonce256.size());
|
||||
RAND_bytes(reinterpret_cast<unsigned char *>(&mNonceST), sizeof(mNonceST));
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#include "SNTPClient.h"
|
||||
#include "../database/database.h"
|
||||
#include "JobQueue.h"
|
||||
|
||||
#include "RPCHandler.h"
|
||||
|
||||
class RPCDoor;
|
||||
class PeerDoor;
|
||||
@@ -55,6 +55,7 @@ class Application
|
||||
HashedObjectStore mHashedObjectStore;
|
||||
SNTPClient mSNTPClient;
|
||||
JobQueue mJobQueue;
|
||||
RPCHandler mRPCHandler;
|
||||
|
||||
DatabaseCon *mRpcDB, *mTxnDB, *mLedgerDB, *mWalletDB, *mHashNodeDB, *mNetNodeDB;
|
||||
|
||||
@@ -96,6 +97,8 @@ public:
|
||||
ValidationCollection& getValidations() { return mValidations; }
|
||||
JobQueue& getJobQueue() { return mJobQueue; }
|
||||
SuppressionTable& getSuppression() { return mSuppressions; }
|
||||
RPCHandler& getRPCHandler() { return mRPCHandler; }
|
||||
|
||||
|
||||
bool isNew(const uint256& s) { return mSuppressions.addSuppression(s); }
|
||||
bool isNew(const uint256& s, uint64 p) { return mSuppressions.addSuppressionPeer(s, p); }
|
||||
|
||||
@@ -1612,18 +1612,182 @@ Json::Value RPCHandler::doRippleLinesGet(const Json::Value ¶ms)
|
||||
// submit private_key json
|
||||
Json::Value RPCHandler::doSubmit(const Json::Value& params)
|
||||
{
|
||||
RippleAddress naSeed;
|
||||
std::string txJSON= params[1u].asString();
|
||||
Json::Value txJSON;
|
||||
Json::Reader reader;
|
||||
if(reader.parse(params[1u].asString(),txJSON))
|
||||
{
|
||||
return handleJSONSubmit(params[0u].asString(), txJSON );
|
||||
}
|
||||
|
||||
if (!naSeed.setSeedGeneric(params[0u].asString()))
|
||||
return rpcError(rpcSRC_ACT_MALFORMED);
|
||||
}
|
||||
|
||||
Json::Value RPCHandler::handleJSONSubmit(std::string& key, Json::Value& txJSON)
|
||||
{
|
||||
return rpcError(rpcSRC_ACT_MALFORMED);
|
||||
/*
|
||||
Json::Value jvResult;
|
||||
RippleAddress naSeed;
|
||||
RippleAddress naAccount;
|
||||
|
||||
if (!naSeed.setSeedGeneric(key))
|
||||
{
|
||||
return rpcError(rpcBAD_SEED);
|
||||
}
|
||||
if (!txJSON.isMember("Account"))
|
||||
{
|
||||
return rpcError(rpcBAD_SEED);
|
||||
}
|
||||
if (!naAccount.setAccountID(txJSON["Account"].asString()))
|
||||
{
|
||||
return rpcError(rpcBAD_SEED);
|
||||
}
|
||||
|
||||
|
||||
Ledger::pointer lpCurrent = mNetOps->getCurrentLedger();
|
||||
SLE::pointer sleAccountRoot = mNetOps->getSLE(lpCurrent, Ledger::getAccountRootIndex(naAccount.getAccountID()));
|
||||
|
||||
if (!sleAccountRoot)
|
||||
{
|
||||
// XXX Ignore transactions for accounts not created.
|
||||
return rpcError(rpcBAD_SEED);
|
||||
}
|
||||
|
||||
bool bHaveAuthKey = false;
|
||||
RippleAddress naAuthorizedPublic;
|
||||
|
||||
|
||||
// TODO
|
||||
return rpcError(rpcSRC_ACT_MALFORMED);
|
||||
RippleAddress naSecret = RippleAddress::createSeedGeneric(key);
|
||||
RippleAddress naMasterGenerator = RippleAddress::createGeneratorPublic(naSecret);
|
||||
|
||||
// Find the index of Account from the master generator, so we can generate the public and private keys.
|
||||
RippleAddress naMasterAccountPublic;
|
||||
unsigned int iIndex = 0;
|
||||
bool bFound = false;
|
||||
|
||||
// Don't look at ledger entries to determine if the account exists. Don't want to leak to thin server that these accounts are
|
||||
// related.
|
||||
while (!bFound && iIndex != theConfig.ACCOUNT_PROBE_MAX)
|
||||
{
|
||||
naMasterAccountPublic.setAccountPublic(naMasterGenerator, iIndex);
|
||||
|
||||
Log(lsWARNING) << "authorize: " << iIndex << " : " << naMasterAccountPublic.humanAccountID() << " : " << naAccount.humanAccountID();
|
||||
|
||||
bFound = naAccount.getAccountID() == naMasterAccountPublic.getAccountID();
|
||||
if (!bFound)
|
||||
++iIndex;
|
||||
}
|
||||
|
||||
if (!bFound)
|
||||
{
|
||||
return rpcError(rpcBAD_SEED);
|
||||
}
|
||||
|
||||
// Use the generator to determine the associated public and private keys.
|
||||
RippleAddress naGenerator = RippleAddress::createGeneratorPublic(naSecret);
|
||||
RippleAddress naAccountPublic = RippleAddress::createAccountPublic(naGenerator, iIndex);
|
||||
RippleAddress naAccountPrivate = RippleAddress::createAccountPrivate(naGenerator, naSecret, iIndex);
|
||||
|
||||
if (bHaveAuthKey
|
||||
// The generated pair must match authorized...
|
||||
&& naAuthorizedPublic.getAccountID() != naAccountPublic.getAccountID()
|
||||
// ... or the master key must have been used.
|
||||
&& naAccount.getAccountID() != naAccountPublic.getAccountID())
|
||||
{
|
||||
// std::cerr << "iIndex: " << iIndex << std::endl;
|
||||
// std::cerr << "sfAuthorizedKey: " << strHex(asSrc->getAuthorizedKey().getAccountID()) << std::endl;
|
||||
// std::cerr << "naAccountPublic: " << strHex(naAccountPublic.getAccountID()) << std::endl;
|
||||
|
||||
return rpcError(rpcBAD_SEED);
|
||||
}
|
||||
|
||||
std::auto_ptr<STObject> sopTrans;
|
||||
|
||||
try
|
||||
{
|
||||
sopTrans = STObject::parseJson(jvRequest["transaction"]);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
jvResult["error"] = "malformedTransaction";
|
||||
jvResult["error_exception"] = e.what();
|
||||
return;
|
||||
}
|
||||
|
||||
sopTrans->setFieldVL(sfSigningPubKey, naAccountPublic.getAccountPublic());
|
||||
|
||||
SerializedTransaction::pointer stpTrans;
|
||||
|
||||
try
|
||||
{
|
||||
stpTrans = boost::make_shared<SerializedTransaction>(*sopTrans);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
jvResult["error"] = "invalidTransaction";
|
||||
jvResult["error_exception"] = e.what();
|
||||
return jvResult;
|
||||
}
|
||||
|
||||
stpTrans->sign(naAccountPrivate);
|
||||
|
||||
Transaction::pointer tpTrans;
|
||||
|
||||
try
|
||||
{
|
||||
tpTrans = boost::make_shared<Transaction>(stpTrans, false);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
jvResult["error"] = "internalTransaction";
|
||||
jvResult["error_exception"] = e.what();
|
||||
return(jvResult);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
tpTrans = mNetwork.submitTransaction(tpTrans);
|
||||
|
||||
if (!tpTrans) {
|
||||
jvResult["error"] = "invalidTransaction";
|
||||
jvResult["error_exception"] = "Unable to sterilize transaction.";
|
||||
return(jvResult);
|
||||
}
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
jvResult["error"] = "internalSubmit";
|
||||
jvResult["error_exception"] = e.what();
|
||||
return(jvResult);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
jvResult["transaction"] = tpTrans->getJson(0);
|
||||
|
||||
if (temUNCERTAIN != tpTrans->getResult())
|
||||
{
|
||||
std::string sToken;
|
||||
std::string sHuman;
|
||||
|
||||
transResultInfo(tpTrans->getResult(), sToken, sHuman);
|
||||
|
||||
jvResult["engine_result"] = sToken;
|
||||
jvResult["engine_result_code"] = tpTrans->getResult();
|
||||
jvResult["engine_result_message"] = sHuman;
|
||||
}
|
||||
return(jvResult);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
jvResult["error"] = "internalJson";
|
||||
jvResult["error_exception"] = e.what();
|
||||
return(jvResult);
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
// send regular_seed paying_account account_id amount [currency] [issuer] [send_max] [send_currency] [send_issuer]
|
||||
|
||||
@@ -160,6 +160,8 @@ public:
|
||||
Json::Value doCommand(const std::string& command, Json::Value& params,int role);
|
||||
Json::Value rpcError(int iError);
|
||||
|
||||
Json::Value handleJSONSubmit(std::string& key, Json::Value& txJSON);
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
734
src/WSConnection.cpp
Normal file
734
src/WSConnection.cpp
Normal file
@@ -0,0 +1,734 @@
|
||||
#include "WSConnection.h"
|
||||
#include "WSHandler.h"
|
||||
|
||||
#include "../json/reader.h"
|
||||
#include "../json/writer.h"
|
||||
//
|
||||
// WSConnection
|
||||
//
|
||||
|
||||
SETUP_LOG();
|
||||
|
||||
WSConnection::~WSConnection()
|
||||
{
|
||||
mNetwork.unsubTransaction(this);
|
||||
mNetwork.unsubLedger(this);
|
||||
mNetwork.unsubLedgerAccounts(this);
|
||||
mNetwork.unsubAccountInfo(this, mSubAccountInfo);
|
||||
mNetwork.unsubAccountTransaction(this, mSubAccountTransaction);
|
||||
}
|
||||
|
||||
void WSConnection::send(const Json::Value& jvObj)
|
||||
{
|
||||
mHandler->send(mConnection, jvObj);
|
||||
}
|
||||
|
||||
//
|
||||
// Utilities
|
||||
//
|
||||
|
||||
Json::Value WSConnection::invokeCommand(Json::Value& jvRequest)
|
||||
{
|
||||
static struct {
|
||||
const char* pCommand;
|
||||
doFuncPtr dfpFunc;
|
||||
} commandsA[] = {
|
||||
// Request-Response Commands:
|
||||
{ "ledger_accept", &WSConnection::doLedgerAccept },
|
||||
{ "ledger_closed", &WSConnection::doLedgerClosed },
|
||||
{ "ledger_current", &WSConnection::doLedgerCurrent },
|
||||
{ "ledger_entry", &WSConnection::doLedgerEntry },
|
||||
{ "submit", &WSConnection::doSubmit },
|
||||
{ "transaction_entry", &WSConnection::doTransactionEntry },
|
||||
{ "subscribe", &WSConnection::doSubscribe },
|
||||
{ "unsubscribe", &WSConnection::doUnsubscribe },
|
||||
|
||||
// deprecated
|
||||
{ "account_info_subscribe", &WSConnection::doAccountInfoSubscribe },
|
||||
{ "account_info_unsubscribe", &WSConnection::doAccountInfoUnsubscribe },
|
||||
{ "account_transaction_subscribe", &WSConnection::doAccountTransactionSubscribe },
|
||||
{ "account_transaction_unsubscribe", &WSConnection::doAccountTransactionUnsubscribe },
|
||||
{ "ledger_accounts_subscribe", &WSConnection::doLedgerAccountsSubcribe },
|
||||
{ "ledger_accounts_unsubscribe", &WSConnection::doLedgerAccountsUnsubscribe },
|
||||
{ "server_subscribe", &WSConnection::doServerSubscribe },
|
||||
{ "server_unsubscribe", &WSConnection::doServerUnsubscribe },
|
||||
{ "transaction_subscribe", &WSConnection::doTransactionSubcribe },
|
||||
{ "transaction_unsubscribe", &WSConnection::doTransactionUnsubscribe },
|
||||
};
|
||||
|
||||
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<RippleAddress> WSConnection::parseAccountIds(const Json::Value& jvArray)
|
||||
{
|
||||
boost::unordered_set<RippleAddress> usnaResult;
|
||||
|
||||
for (Json::Value::const_iterator it = jvArray.begin(); it != jvArray.end(); it++)
|
||||
{
|
||||
RippleAddress naString;
|
||||
|
||||
if (!(*it).isString() || !naString.setAccountID((*it).asString()))
|
||||
{
|
||||
usnaResult.clear();
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
(void) usnaResult.insert(naString);
|
||||
}
|
||||
}
|
||||
|
||||
return usnaResult;
|
||||
}
|
||||
|
||||
//
|
||||
// Commands
|
||||
//
|
||||
|
||||
/*
|
||||
server : Sends a message anytime the server status changes such as network connectivity.
|
||||
ledger : Sends a message at every ledger close.
|
||||
transactions : Sends a message for every transaction that makes it into a ledger.
|
||||
rt_transactions
|
||||
accounts
|
||||
rt_accounts
|
||||
*/
|
||||
// TODO
|
||||
void WSConnection::doSubscribe(Json::Value& jvResult, Json::Value& jvRequest)
|
||||
{
|
||||
if (jvRequest.isMember("streams"))
|
||||
{
|
||||
for (Json::Value::iterator it = jvRequest["streams"].begin(); it != jvRequest["streams"].end(); it++)
|
||||
{
|
||||
if ((*it).isString() )
|
||||
{
|
||||
std::string streamName=(*it).asString();
|
||||
|
||||
if(streamName=="server")
|
||||
{
|
||||
mNetwork.subLedgerAccounts(this);
|
||||
}else if(streamName=="ledger")
|
||||
{
|
||||
mNetwork.subLedgerAccounts(this);
|
||||
}else if(streamName=="transactions")
|
||||
{
|
||||
mNetwork.subTransaction(this);
|
||||
}else if(streamName=="rt_transactions")
|
||||
{
|
||||
mNetwork.subTransaction(this); // TODO
|
||||
}else
|
||||
{
|
||||
jvResult["error"] = str(boost::format("Unknown stream: %s") % streamName);
|
||||
}
|
||||
}else
|
||||
{
|
||||
jvResult["error"] = "malformedSteam";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (jvRequest.isMember("rt_accounts"))
|
||||
{
|
||||
boost::unordered_set<RippleAddress> usnaAccoundIds = parseAccountIds(jvRequest["rt_accounts"]);
|
||||
|
||||
if (usnaAccoundIds.empty())
|
||||
{
|
||||
jvResult["error"] = "malformedAccount";
|
||||
}else
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mLock);
|
||||
|
||||
BOOST_FOREACH(const RippleAddress& naAccountID, usnaAccoundIds)
|
||||
{
|
||||
mSubAccountInfo.insert(naAccountID);
|
||||
}
|
||||
|
||||
mNetwork.subAccountInfo(this, usnaAccoundIds);
|
||||
}
|
||||
}
|
||||
|
||||
if (jvRequest.isMember("accounts"))
|
||||
{
|
||||
boost::unordered_set<RippleAddress> usnaAccoundIds = parseAccountIds(jvRequest["accounts"]);
|
||||
|
||||
if (usnaAccoundIds.empty())
|
||||
{
|
||||
jvResult["error"] = "malformedAccount";
|
||||
}else
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mLock);
|
||||
|
||||
BOOST_FOREACH(const RippleAddress& naAccountID, usnaAccoundIds)
|
||||
{
|
||||
mSubAccountInfo.insert(naAccountID);
|
||||
}
|
||||
|
||||
mNetwork.subAccountInfo(this, usnaAccoundIds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WSConnection::doUnsubscribe(Json::Value& jvResult, Json::Value& jvRequest)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void WSConnection::doAccountInfoSubscribe(Json::Value& jvResult, Json::Value& jvRequest)
|
||||
{
|
||||
if (!jvRequest.isMember("accounts"))
|
||||
{
|
||||
jvResult["error"] = "missingField";
|
||||
}
|
||||
else if (jvRequest["accounts"].empty())
|
||||
{
|
||||
jvResult["error"] = "emptySet";
|
||||
}
|
||||
else
|
||||
{
|
||||
boost::unordered_set<RippleAddress> usnaAccoundIds = parseAccountIds(jvRequest["accounts"]);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void WSConnection::doAccountInfoUnsubscribe(Json::Value& jvResult, Json::Value& jvRequest)
|
||||
{
|
||||
if (!jvRequest.isMember("accounts"))
|
||||
{
|
||||
jvResult["error"] = "missingField";
|
||||
}
|
||||
else if (jvRequest["accounts"].empty())
|
||||
{
|
||||
jvResult["error"] = "emptySet";
|
||||
}
|
||||
else
|
||||
{
|
||||
boost::unordered_set<RippleAddress> usnaAccoundIds = parseAccountIds(jvRequest["accounts"]);
|
||||
|
||||
if (usnaAccoundIds.empty())
|
||||
{
|
||||
jvResult["error"] = "malformedAccount";
|
||||
}
|
||||
else
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mLock);
|
||||
|
||||
BOOST_FOREACH(const RippleAddress& naAccountID, usnaAccoundIds)
|
||||
{
|
||||
mSubAccountInfo.erase(naAccountID);
|
||||
}
|
||||
|
||||
mNetwork.unsubAccountInfo(this, usnaAccoundIds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WSConnection::doAccountTransactionSubscribe(Json::Value& jvResult, Json::Value& jvRequest)
|
||||
{
|
||||
if (!jvRequest.isMember("accounts"))
|
||||
{
|
||||
jvResult["error"] = "missingField";
|
||||
}
|
||||
else if (jvRequest["accounts"].empty())
|
||||
{
|
||||
jvResult["error"] = "emptySet";
|
||||
}
|
||||
else
|
||||
{
|
||||
boost::unordered_set<RippleAddress> usnaAccoundIds = parseAccountIds(jvRequest["accounts"]);
|
||||
|
||||
if (usnaAccoundIds.empty())
|
||||
{
|
||||
jvResult["error"] = "malformedAccount";
|
||||
}
|
||||
else
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mLock);
|
||||
|
||||
BOOST_FOREACH(const RippleAddress& naAccountID, usnaAccoundIds)
|
||||
{
|
||||
mSubAccountTransaction.insert(naAccountID);
|
||||
}
|
||||
|
||||
mNetwork.subAccountTransaction(this, usnaAccoundIds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WSConnection::doAccountTransactionUnsubscribe(Json::Value& jvResult, Json::Value& jvRequest)
|
||||
{
|
||||
if (!jvRequest.isMember("accounts"))
|
||||
{
|
||||
jvResult["error"] = "missingField";
|
||||
}
|
||||
else if (jvRequest["accounts"].empty())
|
||||
{
|
||||
jvResult["error"] = "emptySet";
|
||||
}
|
||||
else
|
||||
{
|
||||
boost::unordered_set<RippleAddress> usnaAccoundIds = parseAccountIds(jvRequest["accounts"]);
|
||||
|
||||
if (usnaAccoundIds.empty())
|
||||
{
|
||||
jvResult["error"] = "malformedAccount";
|
||||
}
|
||||
else
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mLock);
|
||||
|
||||
BOOST_FOREACH(const RippleAddress& naAccountID, usnaAccoundIds)
|
||||
{
|
||||
mSubAccountTransaction.erase(naAccountID);
|
||||
}
|
||||
|
||||
mNetwork.unsubAccountTransaction(this, usnaAccoundIds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WSConnection::doLedgerAccountsSubcribe(Json::Value& jvResult, Json::Value& jvRequest)
|
||||
{
|
||||
if (!mNetwork.subLedgerAccounts(this))
|
||||
{
|
||||
jvResult["error"] = "ledgerAccountsSubscribed";
|
||||
}
|
||||
}
|
||||
|
||||
void WSConnection::doLedgerAccountsUnsubscribe(Json::Value& jvResult, Json::Value& jvRequest)
|
||||
{
|
||||
if (!mNetwork.unsubLedgerAccounts(this))
|
||||
{
|
||||
jvResult["error"] = "ledgerAccountsNotSubscribed";
|
||||
}
|
||||
}
|
||||
|
||||
void WSConnection::doLedgerAccept(Json::Value& jvResult, Json::Value& jvRequest)
|
||||
{
|
||||
if (!theConfig.RUN_STANDALONE)
|
||||
{
|
||||
jvResult["error"] = "notStandAlone";
|
||||
}
|
||||
else
|
||||
{
|
||||
mNetwork.acceptLedger();
|
||||
|
||||
jvResult["ledger_current_index"] = mNetwork.getCurrentLedgerID();
|
||||
}
|
||||
}
|
||||
|
||||
void WSConnection::doLedgerClosed(Json::Value& jvResult, Json::Value& jvRequest)
|
||||
{
|
||||
uint256 uLedger = mNetwork.getClosedLedger();
|
||||
|
||||
jvResult["ledger_closed_index"] = mNetwork.getLedgerID(uLedger);
|
||||
jvResult["ledger_closed"] = uLedger.ToString();
|
||||
}
|
||||
|
||||
void WSConnection::doLedgerCurrent(Json::Value& jvResult, Json::Value& jvRequest)
|
||||
{
|
||||
jvResult["ledger_current_index"] = mNetwork.getCurrentLedgerID();
|
||||
}
|
||||
|
||||
void WSConnection::doLedgerEntry(Json::Value& jvResult, Json::Value& jvRequest)
|
||||
{
|
||||
NetworkOPs& noNetwork = mNetwork;
|
||||
uint256 uLedger = jvRequest.isMember("ledger_closed") ? uint256(jvRequest["ledger_closed"].asString()) : 0;
|
||||
uint32 uLedgerIndex = jvRequest.isMember("ledger_index") && jvRequest["ledger_index"].isNumeric() ? jvRequest["ledger_index"].asUInt() : 0;
|
||||
|
||||
Ledger::pointer lpLedger;
|
||||
|
||||
if (!!uLedger)
|
||||
{
|
||||
// Ledger directly specified.
|
||||
lpLedger = noNetwork.getLedgerByHash(uLedger);
|
||||
|
||||
if (!lpLedger)
|
||||
{
|
||||
jvResult["error"] = "ledgerNotFound";
|
||||
return;
|
||||
}
|
||||
|
||||
uLedgerIndex = lpLedger->getLedgerSeq(); // Set the current index, override if needed.
|
||||
}
|
||||
else if (!!uLedgerIndex)
|
||||
{
|
||||
lpLedger = noNetwork.getLedgerBySeq(uLedgerIndex);
|
||||
|
||||
if (!lpLedger)
|
||||
{
|
||||
jvResult["error"] = "ledgerNotFound"; // ledger_index from future?
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Default to current ledger.
|
||||
lpLedger = noNetwork.getCurrentLedger();
|
||||
uLedgerIndex = lpLedger->getLedgerSeq(); // Set the current index.
|
||||
}
|
||||
|
||||
if (lpLedger->isClosed())
|
||||
{
|
||||
if (!!uLedger)
|
||||
jvResult["ledger_closed"] = uLedger.ToString();
|
||||
|
||||
jvResult["ledger_closed_index"] = uLedgerIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
jvResult["ledger_current_index"] = uLedgerIndex;
|
||||
}
|
||||
|
||||
uint256 uNodeIndex;
|
||||
bool bNodeBinary = false;
|
||||
|
||||
if (jvRequest.isMember("index"))
|
||||
{
|
||||
// XXX Needs to provide proof.
|
||||
uNodeIndex.SetHex(jvRequest["index"].asString());
|
||||
bNodeBinary = true;
|
||||
}
|
||||
else if (jvRequest.isMember("account_root"))
|
||||
{
|
||||
RippleAddress naAccount;
|
||||
|
||||
if (!naAccount.setAccountID(jvRequest["account_root"].asString())
|
||||
|| !naAccount.getAccountID())
|
||||
{
|
||||
jvResult["error"] = "malformedAddress";
|
||||
}
|
||||
else
|
||||
{
|
||||
uNodeIndex = Ledger::getAccountRootIndex(naAccount.getAccountID());
|
||||
}
|
||||
}
|
||||
else if (jvRequest.isMember("directory"))
|
||||
{
|
||||
|
||||
if (!jvRequest.isObject())
|
||||
{
|
||||
uNodeIndex.SetHex(jvRequest["directory"].asString());
|
||||
}
|
||||
else if (jvRequest["directory"].isMember("sub_index")
|
||||
&& !jvRequest["directory"]["sub_index"].isIntegral())
|
||||
{
|
||||
jvResult["error"] = "malformedRequest";
|
||||
}
|
||||
else
|
||||
{
|
||||
uint64 uSubIndex = jvRequest["directory"].isMember("sub_index")
|
||||
? jvRequest["directory"]["sub_index"].asUInt()
|
||||
: 0;
|
||||
|
||||
if (jvRequest["directory"].isMember("dir_root"))
|
||||
{
|
||||
uint256 uDirRoot;
|
||||
|
||||
uDirRoot.SetHex(jvRequest["dir_root"].asString());
|
||||
|
||||
uNodeIndex = Ledger::getDirNodeIndex(uDirRoot, uSubIndex);
|
||||
}
|
||||
else if (jvRequest["directory"].isMember("owner"))
|
||||
{
|
||||
RippleAddress naOwnerID;
|
||||
|
||||
if (!naOwnerID.setAccountID(jvRequest["directory"]["owner"].asString()))
|
||||
{
|
||||
jvResult["error"] = "malformedAddress";
|
||||
}
|
||||
else
|
||||
{
|
||||
uint256 uDirRoot = Ledger::getOwnerDirIndex(naOwnerID.getAccountID());
|
||||
|
||||
uNodeIndex = Ledger::getDirNodeIndex(uDirRoot, uSubIndex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
jvResult["error"] = "malformedRequest";
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (jvRequest.isMember("generator"))
|
||||
{
|
||||
RippleAddress naGeneratorID;
|
||||
|
||||
if (!jvRequest.isObject())
|
||||
{
|
||||
uNodeIndex.SetHex(jvRequest["generator"].asString());
|
||||
}
|
||||
else if (!jvRequest["generator"].isMember("regular_seed"))
|
||||
{
|
||||
jvResult["error"] = "malformedRequest";
|
||||
}
|
||||
else if (!naGeneratorID.setSeedGeneric(jvRequest["generator"]["regular_seed"].asString()))
|
||||
{
|
||||
jvResult["error"] = "malformedAddress";
|
||||
}
|
||||
else
|
||||
{
|
||||
RippleAddress na0Public; // To find the generator's index.
|
||||
RippleAddress naGenerator = RippleAddress::createGeneratorPublic(naGeneratorID);
|
||||
|
||||
na0Public.setAccountPublic(naGenerator, 0);
|
||||
|
||||
uNodeIndex = Ledger::getGeneratorIndex(na0Public.getAccountID());
|
||||
}
|
||||
}
|
||||
else if (jvRequest.isMember("offer"))
|
||||
{
|
||||
RippleAddress naAccountID;
|
||||
|
||||
if (!jvRequest.isObject())
|
||||
{
|
||||
uNodeIndex.SetHex(jvRequest["offer"].asString());
|
||||
}
|
||||
else if (!jvRequest["offer"].isMember("account")
|
||||
|| !jvRequest["offer"].isMember("seq")
|
||||
|| !jvRequest["offer"]["seq"].isIntegral())
|
||||
{
|
||||
jvResult["error"] = "malformedRequest";
|
||||
}
|
||||
else if (!naAccountID.setAccountID(jvRequest["offer"]["account"].asString()))
|
||||
{
|
||||
jvResult["error"] = "malformedAddress";
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32 uSequence = jvRequest["offer"]["seq"].asUInt();
|
||||
|
||||
uNodeIndex = Ledger::getOfferIndex(naAccountID.getAccountID(), uSequence);
|
||||
}
|
||||
}
|
||||
else if (jvRequest.isMember("ripple_state"))
|
||||
{
|
||||
RippleAddress naA;
|
||||
RippleAddress naB;
|
||||
uint160 uCurrency;
|
||||
Json::Value jvRippleState = jvRequest["ripple_state"];
|
||||
|
||||
if (!jvRippleState.isMember("currency")
|
||||
|| !jvRippleState.isMember("accounts")
|
||||
|| !jvRippleState["accounts"].isArray()
|
||||
|| 2 != jvRippleState["accounts"].size()
|
||||
|| !jvRippleState["accounts"][0u].isString()
|
||||
|| !jvRippleState["accounts"][1u].isString()
|
||||
|| jvRippleState["accounts"][0u].asString() == jvRippleState["accounts"][1u].asString()
|
||||
) {
|
||||
|
||||
cLog(lsINFO)
|
||||
<< boost::str(boost::format("ledger_entry: ripple_state: accounts: %d currency: %d array: %d size: %d equal: %d")
|
||||
% jvRippleState.isMember("accounts")
|
||||
% jvRippleState.isMember("currency")
|
||||
% jvRippleState["accounts"].isArray()
|
||||
% jvRippleState["accounts"].size()
|
||||
% (jvRippleState["accounts"][0u].asString() == jvRippleState["accounts"][1u].asString())
|
||||
);
|
||||
|
||||
jvResult["error"] = "malformedRequest";
|
||||
}
|
||||
else if (!naA.setAccountID(jvRippleState["accounts"][0u].asString())
|
||||
|| !naB.setAccountID(jvRippleState["accounts"][1u].asString())) {
|
||||
jvResult["error"] = "malformedAddress";
|
||||
}
|
||||
else if (!STAmount::currencyFromString(uCurrency, jvRippleState["currency"].asString())) {
|
||||
jvResult["error"] = "malformedCurrency";
|
||||
}
|
||||
else
|
||||
{
|
||||
uNodeIndex = Ledger::getRippleStateIndex(naA, naB, uCurrency);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
jvResult["error"] = "unknownOption";
|
||||
}
|
||||
|
||||
if (!!uNodeIndex)
|
||||
{
|
||||
SLE::pointer sleNode = noNetwork.getSLE(lpLedger, uNodeIndex);
|
||||
|
||||
if (!sleNode)
|
||||
{
|
||||
// Not found.
|
||||
// XXX Should also provide proof.
|
||||
jvResult["error"] = "entryNotFound";
|
||||
}
|
||||
else if (bNodeBinary)
|
||||
{
|
||||
// XXX Should also provide proof.
|
||||
Serializer s;
|
||||
|
||||
sleNode->add(s);
|
||||
|
||||
jvResult["node_binary"] = strHex(s.peekData());
|
||||
jvResult["index"] = uNodeIndex.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
jvResult["node"] = sleNode->getJson(0);
|
||||
jvResult["index"] = uNodeIndex.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The objective is to allow the client to know the server's status. The only thing that show the server is fully operating is the
|
||||
// stream of ledger_closeds. Therefore, that is all that is provided. A client can drop servers that do not provide recent
|
||||
// ledger_closeds.
|
||||
void WSConnection::doServerSubscribe(Json::Value& jvResult, Json::Value& jvRequest)
|
||||
{
|
||||
if (!mNetwork.subLedger(this))
|
||||
{
|
||||
jvResult["error"] = "serverSubscribed";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (theConfig.RUN_STANDALONE)
|
||||
jvResult["stand_alone"] = 1;
|
||||
|
||||
if (NetworkOPs::omDISCONNECTED != mNetwork.getOperatingMode()) {
|
||||
jvResult["ledger_closed"] = mNetwork.getClosedLedger().ToString();
|
||||
jvResult["ledger_current_index"] = mNetwork.getCurrentLedgerID();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WSConnection::doServerUnsubscribe(Json::Value& jvResult, Json::Value& jvRequest)
|
||||
{
|
||||
if (!mNetwork.unsubLedger(this))
|
||||
{
|
||||
jvResult["error"] = "serverNotSubscribed";
|
||||
}
|
||||
}
|
||||
|
||||
void WSConnection::doRPC(Json::Value& jvResult, Json::Value& jvRequest)
|
||||
{
|
||||
if (jvRequest.isMember("command") && jvRequest.isMember("params"))
|
||||
{
|
||||
jvResult=theApp->getRPCHandler().doCommand(jvRequest["command"].asString(),jvRequest["params"],RPCHandler::GUEST);
|
||||
|
||||
}else jvResult["error"] = "fieldNotCommand";
|
||||
|
||||
}
|
||||
|
||||
// XXX Currently requires secret. Allow signed transaction as an alternative.
|
||||
void WSConnection::doSubmit(Json::Value& jvResult, Json::Value& jvRequest)
|
||||
{
|
||||
if (!jvRequest.isMember("tx_json"))
|
||||
{
|
||||
jvResult["error"] = "fieldNotFoundTransaction";
|
||||
}else if (!jvRequest.isMember("key"))
|
||||
{
|
||||
jvResult["error"] = "fieldNotFoundKey";
|
||||
}else jvResult=theApp->getRPCHandler().handleJSONSubmit(jvRequest["key"].asString(),jvRequest["tx_json"]);
|
||||
}
|
||||
|
||||
void WSConnection::doTransactionEntry(Json::Value& jvResult, Json::Value& jvRequest)
|
||||
{
|
||||
if (!jvRequest.isMember("transaction"))
|
||||
{
|
||||
jvResult["error"] = "fieldNotFoundTransaction";
|
||||
}
|
||||
if (!jvRequest.isMember("ledger_closed"))
|
||||
{
|
||||
jvResult["error"] = "notYetImplemented"; // XXX We don't support any transaction yet.
|
||||
}
|
||||
else
|
||||
{
|
||||
uint256 uTransID;
|
||||
// XXX Relying on trusted WSS client. Would be better to have a strict routine, returning success or failure.
|
||||
uTransID.SetHex(jvRequest["transaction"].asString());
|
||||
|
||||
uint256 uLedgerID;
|
||||
// XXX Relying on trusted WSS client. Would be better to have a strict routine, returning success or failure.
|
||||
uLedgerID.SetHex(jvRequest["ledger_closed"].asString());
|
||||
|
||||
Ledger::pointer lpLedger = theApp->getMasterLedger().getLedgerByHash(uLedgerID);
|
||||
|
||||
if (!lpLedger) {
|
||||
jvResult["error"] = "ledgerNotFound";
|
||||
}
|
||||
else
|
||||
{
|
||||
Transaction::pointer tpTrans;
|
||||
TransactionMetaSet::pointer tmTrans;
|
||||
|
||||
if (!lpLedger-> getTransaction(uTransID, tpTrans, tmTrans))
|
||||
{
|
||||
jvResult["error"] = "transactionNotFound";
|
||||
}
|
||||
else
|
||||
{
|
||||
jvResult["transaction"] = tpTrans->getJson(0);
|
||||
jvResult["metadata"] = tmTrans->getJson(0);
|
||||
// 'accounts'
|
||||
// 'engine_...'
|
||||
// 'ledger_...'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WSConnection::doTransactionSubcribe(Json::Value& jvResult, Json::Value& jvRequest)
|
||||
{
|
||||
if (!mNetwork.subTransaction(this))
|
||||
{
|
||||
jvResult["error"] = "TransactionsSubscribed";
|
||||
}
|
||||
}
|
||||
|
||||
void WSConnection::doTransactionUnsubscribe(Json::Value& jvResult, Json::Value& jvRequest)
|
||||
{
|
||||
if (!mNetwork.unsubTransaction(this))
|
||||
{
|
||||
jvResult["error"] = "TransactionsNotSubscribed";
|
||||
}
|
||||
}
|
||||
75
src/WSConnection.h
Normal file
75
src/WSConnection.h
Normal file
@@ -0,0 +1,75 @@
|
||||
#include "../websocketpp/src/sockets/tls.hpp"
|
||||
#include "../websocketpp/src/websocketpp.hpp"
|
||||
#include "WSDoor.h"
|
||||
#include "Application.h"
|
||||
|
||||
#include "Log.h"
|
||||
#include "NetworkOPs.h"
|
||||
|
||||
template <typename endpoint_type>
|
||||
class WSServerHandler;
|
||||
//
|
||||
// Storage for connection specific info
|
||||
// - Subscriptions
|
||||
//
|
||||
class WSConnection : public InfoSub
|
||||
{
|
||||
public:
|
||||
typedef websocketpp::WSDOOR_SERVER::handler::connection_ptr connection_ptr;
|
||||
typedef websocketpp::WSDOOR_SERVER::handler::message_ptr message_ptr;
|
||||
|
||||
protected:
|
||||
typedef void (WSConnection::*doFuncPtr)(Json::Value& jvResult, Json::Value &jvRequest);
|
||||
|
||||
boost::mutex mLock;
|
||||
boost::unordered_set<RippleAddress> mSubAccountInfo;
|
||||
boost::unordered_set<RippleAddress> mSubAccountTransaction;
|
||||
|
||||
WSServerHandler<websocketpp::WSDOOR_SERVER>* mHandler;
|
||||
connection_ptr mConnection;
|
||||
NetworkOPs& mNetwork;
|
||||
|
||||
public:
|
||||
// WSConnection()
|
||||
// : mHandler((WSServerHandler<websocketpp::WSDOOR_SERVER>*)(NULL)),
|
||||
// mConnection(connection_ptr()) { ; }
|
||||
|
||||
WSConnection(WSServerHandler<websocketpp::WSDOOR_SERVER>* wshpHandler, connection_ptr cpConnection)
|
||||
: mHandler(wshpHandler), mConnection(cpConnection), mNetwork(theApp->getOPs()) { ; }
|
||||
|
||||
virtual ~WSConnection();
|
||||
|
||||
// Implement overridden functions from base class:
|
||||
void send(const Json::Value& jvObj);
|
||||
|
||||
// Utilities
|
||||
Json::Value invokeCommand(Json::Value& jvRequest);
|
||||
boost::unordered_set<RippleAddress> parseAccountIds(const Json::Value& jvArray);
|
||||
|
||||
// Commands
|
||||
void doSubmit(Json::Value& jvResult, Json::Value& jvRequest);
|
||||
void doRPC(Json::Value& jvResult, Json::Value& jvRequest);
|
||||
void doSubscribe(Json::Value& jvResult, Json::Value& jvRequest);
|
||||
void doUnsubscribe(Json::Value& jvResult, Json::Value& jvRequest);
|
||||
|
||||
|
||||
|
||||
// deprecated
|
||||
void doLedgerAccept(Json::Value& jvResult, Json::Value& jvRequest);
|
||||
void doLedgerClosed(Json::Value& jvResult, Json::Value& jvRequest);
|
||||
void doLedgerCurrent(Json::Value& jvResult, Json::Value& jvRequest);
|
||||
void doLedgerEntry(Json::Value& jvResult, Json::Value& jvRequest);
|
||||
void doTransactionEntry(Json::Value& jvResult, Json::Value& jvRequest);
|
||||
|
||||
void doAccountInfoSubscribe(Json::Value& jvResult, Json::Value& jvRequest);
|
||||
void doAccountInfoUnsubscribe(Json::Value& jvResult, Json::Value& jvRequest);
|
||||
void doAccountTransactionSubscribe(Json::Value& jvResult, Json::Value& jvRequest);
|
||||
void doAccountTransactionUnsubscribe(Json::Value& jvResult, Json::Value& jvRequest);
|
||||
|
||||
void doServerSubscribe(Json::Value& jvResult, Json::Value& jvRequest);
|
||||
void doServerUnsubscribe(Json::Value& jvResult, Json::Value& jvRequest);
|
||||
void doLedgerAccountsSubcribe(Json::Value& jvResult, Json::Value& jvRequest);
|
||||
void doLedgerAccountsUnsubscribe(Json::Value& jvResult, Json::Value& jvRequest);
|
||||
void doTransactionSubcribe(Json::Value& jvResult, Json::Value& jvRequest);
|
||||
void doTransactionUnsubscribe(Json::Value& jvResult, Json::Value& jvRequest);
|
||||
};
|
||||
1079
src/WSDoor.cpp
1079
src/WSDoor.cpp
File diff suppressed because it is too large
Load Diff
124
src/WSHandler.h
Normal file
124
src/WSHandler.h
Normal file
@@ -0,0 +1,124 @@
|
||||
#ifndef __WSHANDLER__
|
||||
#define __WSHANDLER__
|
||||
|
||||
class WSConnection;
|
||||
|
||||
// A single instance of this object is made.
|
||||
// This instance dispatches all events. There is no per connection persistence.
|
||||
template <typename endpoint_type>
|
||||
class WSServerHandler : public endpoint_type::handler
|
||||
{
|
||||
public:
|
||||
typedef typename endpoint_type::handler::connection_ptr connection_ptr;
|
||||
typedef typename endpoint_type::handler::message_ptr message_ptr;
|
||||
|
||||
// Private reasons to close.
|
||||
enum {
|
||||
crTooSlow = 4000, // Client is too slow.
|
||||
};
|
||||
|
||||
private:
|
||||
boost::shared_ptr<boost::asio::ssl::context> mCtx;
|
||||
|
||||
protected:
|
||||
boost::mutex mMapLock;
|
||||
// For each connection maintain an associated object to track subscriptions.
|
||||
boost::unordered_map<connection_ptr, boost::shared_ptr<WSConnection> > mMap;
|
||||
|
||||
public:
|
||||
WSServerHandler(boost::shared_ptr<boost::asio::ssl::context> spCtx) : mCtx(spCtx) {}
|
||||
|
||||
boost::shared_ptr<boost::asio::ssl::context> on_tls_init()
|
||||
{
|
||||
return mCtx;
|
||||
}
|
||||
|
||||
void send(connection_ptr cpClient, message_ptr mpMessage)
|
||||
{
|
||||
try
|
||||
{
|
||||
cpClient->send(mpMessage->get_payload(), mpMessage->get_opcode());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
cpClient->close(websocketpp::close::status::value(crTooSlow), std::string("Client is too slow."));
|
||||
}
|
||||
}
|
||||
|
||||
void send(connection_ptr cpClient, const std::string& strMessage)
|
||||
{
|
||||
try
|
||||
{
|
||||
cLog(lsDEBUG) << "Ws:: Sending '" << strMessage << "'";
|
||||
|
||||
cpClient->send(strMessage);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
cpClient->close(websocketpp::close::status::value(crTooSlow), std::string("Client is too slow."));
|
||||
}
|
||||
}
|
||||
|
||||
void send(connection_ptr cpClient, const Json::Value& jvObj)
|
||||
{
|
||||
Json::FastWriter jfwWriter;
|
||||
|
||||
// cLog(lsDEBUG) << "Ws:: Object '" << jfwWriter.write(jvObj) << "'";
|
||||
|
||||
send(cpClient, jfwWriter.write(jvObj));
|
||||
}
|
||||
|
||||
void on_open(connection_ptr cpClient)
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mMapLock);
|
||||
|
||||
mMap[cpClient] = boost::make_shared<WSConnection>(this, cpClient);
|
||||
}
|
||||
|
||||
void on_close(connection_ptr cpClient)
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mMapLock);
|
||||
|
||||
mMap.erase(cpClient);
|
||||
}
|
||||
|
||||
void on_message(connection_ptr cpClient, message_ptr mpMessage)
|
||||
{
|
||||
Json::Value jvRequest;
|
||||
Json::Reader jrReader;
|
||||
|
||||
if (mpMessage->get_opcode() != websocketpp::frame::opcode::TEXT)
|
||||
{
|
||||
Json::Value jvResult(Json::objectValue);
|
||||
|
||||
jvResult["type"] = "error";
|
||||
jvResult["error"] = "wsTextRequired"; // We only accept text messages.
|
||||
|
||||
send(cpClient, jvResult);
|
||||
}
|
||||
else if (!jrReader.parse(mpMessage->get_payload(), jvRequest) || jvRequest.isNull() || !jvRequest.isObject())
|
||||
{
|
||||
Json::Value jvResult(Json::objectValue);
|
||||
|
||||
jvResult["type"] = "error";
|
||||
jvResult["error"] = "jsonInvalid"; // Received invalid json.
|
||||
jvResult["value"] = mpMessage->get_payload();
|
||||
|
||||
send(cpClient, jvResult);
|
||||
}
|
||||
else
|
||||
{
|
||||
send(cpClient, mMap[cpClient]->invokeCommand(jvRequest));
|
||||
}
|
||||
}
|
||||
|
||||
// Respond to http requests.
|
||||
void http(connection_ptr cpClient)
|
||||
{
|
||||
cpClient->set_body(
|
||||
"<!DOCTYPE html><html><head><title>" SYSTEM_NAME " Test</title></head>"
|
||||
"<body><h1>" SYSTEM_NAME " Test</h1><p>This page shows http(s) connectivity is working.</p></body></html>");
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user