From 9be40b6679154fe1ad83ded7774881398e082b0e Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Sun, 24 Jun 2012 16:37:48 -0700 Subject: [PATCH 1/5] Get rid of unneeded comments. Elements are in index key. --- src/LedgerFormats.cpp | 2 -- src/TransactionEngine.cpp | 3 --- 2 files changed, 5 deletions(-) diff --git a/src/LedgerFormats.cpp b/src/LedgerFormats.cpp index afee90b4c9..711c9c258e 100644 --- a/src/LedgerFormats.cpp +++ b/src/LedgerFormats.cpp @@ -35,14 +35,12 @@ LedgerEntryFormat LedgerFormats[]= }, { "GeneratorMap", ltGENERATOR_MAP, { { S_FIELD(Flags), STI_UINT32, SOE_FLAGS, 0 }, -// { S_FIELD(GeneratorID), STI_HASH160, SOE_REQUIRED, 0 }, { S_FIELD(Generator), STI_VL, SOE_REQUIRED, 0 }, { S_FIELD(Extensions), STI_TL, SOE_IFFLAG, 0x01000000 }, { sfInvalid, NULL, STI_DONE, SOE_NEVER, -1 } } }, { "Nickname", ltNICKNAME, { { S_FIELD(Flags), STI_UINT32, SOE_FLAGS, 0 }, -// { S_FIELD(Nickname), STI_HASH256, SOE_REQUIRED, 0 }, { S_FIELD(Account), STI_ACCOUNT, SOE_REQUIRED, 0 }, { S_FIELD(MinimumOffer), STI_AMOUNT, SOE_IFFLAG, 1 }, { S_FIELD(Extensions), STI_TL, SOE_IFFLAG, 0x01000000 }, diff --git a/src/TransactionEngine.cpp b/src/TransactionEngine.cpp index 4c5841d33b..90495b3f4e 100644 --- a/src/TransactionEngine.cpp +++ b/src/TransactionEngine.cpp @@ -264,7 +264,6 @@ TransactionEngineResult TransactionEngine::setAuthorized(const SerializedTransac sleGen = boost::make_shared(ltGENERATOR_MAP); sleGen->setIndex(Ledger::getGeneratorIndex(hGeneratorID)); -// sleGen->setIFieldH160(sfGeneratorID, hGeneratorID); sleGen->setIFieldVL(sfGenerator, vucCipher); accounts.push_back(std::make_pair(taaCREATE, sleGen)); @@ -944,8 +943,6 @@ TransactionEngineResult TransactionEngine::doNicknameSet(const SerializedTransac if (bMinOffer && !saMinOffer.isZero()) sleNickname->setIFieldAmount(sfMinimumOffer, saMinOffer); -// sleNickname->setIFieldH256(sfNickname, uNickname); - accounts.push_back(std::make_pair(taaCREATE, sleNickname)); } From 04972f78d79df476ba44522880db67197864e431 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Sun, 24 Jun 2012 19:58:17 -0700 Subject: [PATCH 2/5] Add #defines for vt100 colors for debugging. --- src/utils.h | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/utils.h b/src/utils.h index 302d5cfc37..1d15a1a9b0 100644 --- a/src/utils.h +++ b/src/utils.h @@ -26,6 +26,49 @@ extern uint64_t htobe64(uint64_t value); #endif +#define vt_f_black "\033[30m" +#define vt_f_red "\033[31m" +#define vt_f_green "\033[32m" +#define vt_f_yellow "\033[33m" +#define vt_f_blue "\033[34m" +#define vt_f_megenta "\033[35m" +#define vt_f_cyan "\033[36m" +#define vt_f_white "\033[37m" +#define vt_f_default "\033[39m" + +#define vt_b_black "\033[40m" +#define vt_b_red "\033[41m" +#define vt_b_green "\033[42m" +#define vt_b_yellow "\033[43m" +#define vt_b_blue "\033[44m" +#define vt_b_megenta "\033[45m" +#define vt_b_cyan "\033[46m" +#define vt_b_white "\033[47m" +#define vt_b_default "\033[49m" + +#define vt_f_bold_black "\033[1m\033[30m" +#define vt_f_bold_red "\033[1m\033[31m" +#define vt_f_bold_green "\033[1m\033[32m" +#define vt_f_bold_yellow "\033[1m\033[33m" +#define vt_f_bold_blue "\033[1m\033[34m" +#define vt_f_bold_megenta "\033[1m\033[35m" +#define vt_f_bold_cyan "\033[1m\033[36m" +#define vt_f_bold_white "\033[1m\033[37m" +#define vt_f_bold_default "\033[1m\033[39m" + +#define vt_bold "\033[1m" +#define vt_dim "\033[2m" // does not work for xterm +#define vt_normal "\033[22m" // intensity + +#define vt_n_enable "\033[7m" // negative +#define vt_n_disable "\033[27m" + +#define vt_u_single "\033[4m" // underline +#define vt_u_double "\033[21m" // does not work for xterm +#define vt_u_disable "\033[24m" + +#define vt_reset vt_f_default vt_b_default vt_normal vt_n_disable vt_u_disable + boost::posix_time::ptime ptEpoch(); int iToSeconds(boost::posix_time::ptime ptWhen); boost::posix_time::ptime ptFromSeconds(int iSeconds); From 2fc6bbb6fc73accde36fa2e1bfbf501f1b86f3e4 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Sun, 24 Jun 2012 20:01:37 -0700 Subject: [PATCH 3/5] Cosmetic. --- src/RPCServer.cpp | 128 +++++++++++++++++++++++----------------------- 1 file changed, 63 insertions(+), 65 deletions(-) diff --git a/src/RPCServer.cpp b/src/RPCServer.cpp index 958b72f801..381c91ab79 100644 --- a/src/RPCServer.cpp +++ b/src/RPCServer.cpp @@ -1,16 +1,4 @@ -#include - -#include -#include -#include -#include - -#include - -#include "../json/reader.h" -#include "../json/writer.h" - #include "RPCServer.h" #include "RequestParser.h" #include "HttpReply.h" @@ -25,6 +13,18 @@ #include "utils.h" #include "Log.h" +#include + +#include +#include +#include +#include + +#include + +#include "../json/reader.h" +#include "../json/writer.h" + RPCServer::RPCServer(boost::asio::io_service& io_service , NetworkOPs* nopNetwork) : mNetOps(nopNetwork), mSocket(io_service) { @@ -74,7 +74,6 @@ Json::Value RPCServer::RPCError(int iError) { rpcWRONG_PASSWORD, "wrongPassword", "Wrong password." }, { rpcWRONG_SEED, "wrongSeed", "The regular key does not point as the master key." }, { rpcNO_PERMISSION, "noPermission", "You don't have permission for this command." }, - }; int i; @@ -97,7 +96,7 @@ Json::Value RPCServer::RPCError(int iError) void RPCServer::connected() { //std::cout << "RPC request" << std::endl; - if(mSocket.remote_endpoint().address().to_string()=="127.0.0.1") mRole=ADMIN; + if (mSocket.remote_endpoint().address().to_string()=="127.0.0.1") mRole=ADMIN; else mRole=GUEST; mSocket.async_read_some(boost::asio::buffer(mReadBuffer), @@ -281,13 +280,14 @@ Json::Value RPCServer::authorize(const uint256& uLedger, NewcoinAddress naMasterGenerator; - if(asSrc->bHaveAuthorizedKey()) + if (asSrc->bHaveAuthorizedKey()) { Json::Value obj = getMasterGenerator(uLedger, naRegularSeed, naMasterGenerator); if (!obj.empty()) return obj; - }else + } + else { // Try the seed as a master seed. naMasterGenerator = NewcoinAddress::createGeneratorPublic(naRegularSeed); @@ -765,7 +765,7 @@ Json::Value RPCServer::doCreditSet(Json::Value& params) NewcoinAddress naMasterGenerator; NewcoinAddress naAccountPublic; NewcoinAddress naAccountPrivate; - AccountState::pointer asSrc; + AccountState::pointer asSrc; STAmount saSrcBalance; Json::Value obj = authorize(uLedger, naSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, saSrcBalance, theConfig.FEE_DEFAULT, asSrc, naMasterGenerator); @@ -1166,7 +1166,7 @@ Json::Value RPCServer::doSend(Json::Value& params) NewcoinAddress naVerifyGenerator; NewcoinAddress naAccountPublic; NewcoinAddress naAccountPrivate; - AccountState::pointer asSrc; + AccountState::pointer asSrc; STAmount saSrcBalance; Json::Value obj = authorize(uLedger, naSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, saSrcBalance, saFee, asSrc, naVerifyGenerator); @@ -1278,7 +1278,7 @@ Json::Value RPCServer::doTransitSet(Json::Value& params) NewcoinAddress naMasterGenerator; NewcoinAddress naAccountPublic; NewcoinAddress naAccountPrivate; - AccountState::pointer asSrc; + AccountState::pointer asSrc; STAmount saSrcBalance; Json::Value obj = authorize(uLedger, naSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, saSrcBalance, theConfig.FEE_DEFAULT, asSrc, naMasterGenerator); @@ -1630,7 +1630,7 @@ Json::Value RPCServer::doWalletAdd(Json::Value& params) NewcoinAddress naAccountPublic; NewcoinAddress naAccountPrivate; - AccountState::pointer asSrc; + AccountState::pointer asSrc; STAmount saSrcBalance; Json::Value obj = authorize(uLedger, naRegularSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, saSrcBalance, theConfig.FEE_ACCOUNT_CREATE, asSrc, naMasterGenerator); @@ -1721,7 +1721,7 @@ Json::Value RPCServer::doWalletClaim(Json::Value& params) else { // Building: - // peer_wallet_claim + // peer_wallet_claim // [] // // @@ -1813,7 +1813,7 @@ Json::Value RPCServer::doWalletCreate(Json::Value& params) } // Trying to build: - // peer_wallet_create [] [] + // peer_wallet_create [] [] NewcoinAddress naMasterGenerator; NewcoinAddress naAccountPublic; @@ -1965,7 +1965,7 @@ Json::Value RPCServer::doUnlScore(Json::Value& params) return "scoring requested"; } -Json::Value RPCServer::doStop(Json::Value& params) +Json::Value RPCServer::doStop(Json::Value& params) { theApp->stop(); @@ -1980,7 +1980,7 @@ Json::Value RPCServer::doLogin(Json::Value& params) std::string username = params[0u].asString(); std::string password = params[1u].asString(); - if(username==theConfig.RPC_USER && password==theConfig.RPC_PASSWORD) + if (username==theConfig.RPC_USER && password==theConfig.RPC_PASSWORD) { //mRole=ADMIN; return "logged in"; @@ -2002,48 +2002,48 @@ Json::Value RPCServer::doCommand(const std::string& command, Json::Value& params bool mAdminRequired; unsigned int iOptions; } commandsA[] = { - { "account_email_set", &RPCServer::doAccountEmailSet, 2, 3, true,optCurrent }, - { "account_info", &RPCServer::doAccountInfo, 1, 2, false,optCurrent }, - { "account_lines", &RPCServer::doAccountLines, 1, 2, true,optCurrent|optClosed }, - { "account_message_set", &RPCServer::doAccountMessageSet, 3, 3, true,optCurrent }, - { "account_tx", &RPCServer::doAccountTransactions, 2, 3, true,optNetwork }, - { "account_wallet_set", &RPCServer::doAccountWalletSet, 2, 3, true,optCurrent }, - { "connect", &RPCServer::doConnect, 1, 2, true }, - { "credit_set", &RPCServer::doCreditSet, 4, 6, true,optCurrent }, - { "data_delete", &RPCServer::doDataDelete, 1, 1, true }, - { "data_fetch", &RPCServer::doDataFetch, 1, 1, true }, - { "data_store", &RPCServer::doDataStore, 2, 2, true }, - { "ledger", &RPCServer::doLedger, 0, 2, false,optNetwork }, - { "nickname_info", &RPCServer::doNicknameInfo, 1, 1, true,optCurrent }, - { "nickname_set", &RPCServer::doNicknameSet, 2, 3, true,optCurrent }, - { "password_fund", &RPCServer::doPasswordFund, 2, 3, true,optCurrent }, - { "password_set", &RPCServer::doPasswordSet, 2, 3, true,optNetwork }, - { "peers", &RPCServer::doPeers, 0, 0, true }, - { "send", &RPCServer::doSend, 3, 7, false, optCurrent }, - { "server_info", &RPCServer::doServerInfo, 0, 0, true }, - { "stop", &RPCServer::doStop, 0, 0, true }, - { "transit_set", &RPCServer::doTransitSet, 5, 5, true, optCurrent }, - { "tx", &RPCServer::doTx, 1, 1, true }, + { "account_email_set", &RPCServer::doAccountEmailSet, 2, 3, true, optCurrent }, + { "account_info", &RPCServer::doAccountInfo, 1, 2, false, optCurrent }, + { "account_lines", &RPCServer::doAccountLines, 1, 2, true, optCurrent|optClosed }, + { "account_message_set", &RPCServer::doAccountMessageSet, 3, 3, true, optCurrent }, + { "account_tx", &RPCServer::doAccountTransactions, 2, 3, true, optNetwork }, + { "account_wallet_set", &RPCServer::doAccountWalletSet, 2, 3, true, optCurrent }, + { "connect", &RPCServer::doConnect, 1, 2, true }, + { "credit_set", &RPCServer::doCreditSet, 4, 6, true, optCurrent }, + { "data_delete", &RPCServer::doDataDelete, 1, 1, true }, + { "data_fetch", &RPCServer::doDataFetch, 1, 1, true }, + { "data_store", &RPCServer::doDataStore, 2, 2, true }, + { "ledger", &RPCServer::doLedger, 0, 2, false, optNetwork }, + { "nickname_info", &RPCServer::doNicknameInfo, 1, 1, true, optCurrent }, + { "nickname_set", &RPCServer::doNicknameSet, 2, 3, true, optCurrent }, + { "password_fund", &RPCServer::doPasswordFund, 2, 3, true, optCurrent }, + { "password_set", &RPCServer::doPasswordSet, 2, 3, true, optNetwork }, + { "peers", &RPCServer::doPeers, 0, 0, true }, + { "send", &RPCServer::doSend, 3, 7, false, optCurrent }, + { "server_info", &RPCServer::doServerInfo, 0, 0, true }, + { "stop", &RPCServer::doStop, 0, 0, true }, + { "transit_set", &RPCServer::doTransitSet, 5, 5, true, optCurrent }, + { "tx", &RPCServer::doTx, 1, 1, true }, - { "unl_add", &RPCServer::doUnlAdd, 1, 2, true }, - { "unl_delete", &RPCServer::doUnlDelete, 1, 1, true }, - { "unl_list", &RPCServer::doUnlList, 0, 0, true }, - { "unl_load", &RPCServer::doUnlLoad, 0, 0, true }, - { "unl_network", &RPCServer::doUnlNetwork, 0, 0, true }, - { "unl_reset", &RPCServer::doUnlReset, 0, 0, true }, - { "unl_score", &RPCServer::doUnlScore, 0, 0, true }, + { "unl_add", &RPCServer::doUnlAdd, 1, 2, true }, + { "unl_delete", &RPCServer::doUnlDelete, 1, 1, true }, + { "unl_list", &RPCServer::doUnlList, 0, 0, true }, + { "unl_load", &RPCServer::doUnlLoad, 0, 0, true }, + { "unl_network", &RPCServer::doUnlNetwork, 0, 0, true }, + { "unl_reset", &RPCServer::doUnlReset, 0, 0, true }, + { "unl_score", &RPCServer::doUnlScore, 0, 0, true }, - { "validation_create", &RPCServer::doValidationCreate, 0, 1, false }, - { "validation_seed", &RPCServer::doValidationSeed, 0, 1, false }, + { "validation_create", &RPCServer::doValidationCreate, 0, 1, false }, + { "validation_seed", &RPCServer::doValidationSeed, 0, 1, false }, - { "wallet_accounts", &RPCServer::doWalletAccounts, 1, 1, false, optCurrent }, - { "wallet_add", &RPCServer::doWalletAdd, 3, 5, false, optCurrent }, - { "wallet_claim", &RPCServer::doWalletClaim, 2, 4, false, optNetwork }, - { "wallet_create", &RPCServer::doWalletCreate, 3, 4, false, optCurrent }, - { "wallet_propose", &RPCServer::doWalletPropose, 0, 0, false, }, - { "wallet_seed", &RPCServer::doWalletSeed, 0, 1, false, }, + { "wallet_accounts", &RPCServer::doWalletAccounts, 1, 1, false, optCurrent }, + { "wallet_add", &RPCServer::doWalletAdd, 3, 5, false, optCurrent }, + { "wallet_claim", &RPCServer::doWalletClaim, 2, 4, false, optNetwork }, + { "wallet_create", &RPCServer::doWalletCreate, 3, 4, false, optCurrent }, + { "wallet_propose", &RPCServer::doWalletPropose, 0, 0, false, }, + { "wallet_seed", &RPCServer::doWalletSeed, 0, 1, false, }, - { "login", &RPCServer::doLogin, 2, 2, true }, + { "login", &RPCServer::doLogin, 2, 2, true }, }; int i = NUMBER(commandsA); @@ -2088,13 +2088,11 @@ void RPCServer::sendReply() boost::asio::placeholders::error)); } - - void RPCServer::handle_write(const boost::system::error_code& e) { //std::cout << "async_write complete " << e << std::endl; - if(!e) + if (!e) { // Initiate graceful connection closure. boost::system::error_code ignored_ec; From 1cf9156fc33ab9169fb60cf7b4684694e9ff7fee Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Sun, 24 Jun 2012 20:02:55 -0700 Subject: [PATCH 4/5] Add some monitoring support to NetworkOPs. --- src/NetworkOPs.cpp | 96 ++++++++++++++++++++++++++++++++++++++++++++-- src/NetworkOPs.h | 38 ++++++++++++++++-- 2 files changed, 127 insertions(+), 7 deletions(-) diff --git a/src/NetworkOPs.cpp b/src/NetworkOPs.cpp index ebf6284143..d381d8392d 100644 --- a/src/NetworkOPs.cpp +++ b/src/NetworkOPs.cpp @@ -1,15 +1,16 @@ #include "NetworkOPs.h" -#include -#include - #include "utils.h" #include "Application.h" #include "Transaction.h" #include "LedgerConsensus.h" #include "LedgerTiming.h" #include "Log.h" +#include "NewcoinAddress.h" + +#include +#include // This is the primary interface into the "client" portion of the program. // Code that wants to do normal operations on the network such as @@ -553,7 +554,6 @@ bool NetworkOPs::recvPropose(uint32 proposeSeq, const uint256& proposeHash, } return mConsensus->peerPosition(proposal); - } SHAMap::pointer NetworkOPs::getTXMap(const uint256& hash) @@ -663,4 +663,92 @@ Json::Value NetworkOPs::getServerInfo() return info; } +// +// Monitoring:: publisher side +// + +void NetworkOPs::pubAccountInfo(const NewcoinAddress& naAccountID, const Json::Value& jvObj) +{ + boost::interprocess::sharable_lock sl(mMonitorLock); + + subInfoMapType::iterator simIterator = mSubAccountInfo.find(naAccountID); + + if (simIterator == mSubAccountInfo.end()) + { + // Address not found do nothing. + nothing(); + } + else + { + // Found it. + BOOST_FOREACH(InfoSub* ispListener, simIterator->second) + { + ispListener->send(jvObj); + } + } +} + +// +// Monitoring +// + +void NetworkOPs::subAccountInfo(InfoSub* ispListener, const std::vector& vnaAccountIDs) +{ + boost::interprocess::scoped_lock sl(mMonitorLock); + + BOOST_FOREACH(NewcoinAddress naAccountID, vnaAccountIDs) + { + subInfoMapType::iterator simIterator = mSubAccountInfo.find(naAccountID); + if (simIterator == mSubAccountInfo.end()) + { + // Not found + boost::unordered_set usisElement; + + usisElement.insert(ispListener); + mSubAccountInfo.insert(simIterator, make_pair(naAccountID, usisElement)); + } + else + { + // Found + simIterator->second.insert(ispListener); + } + } +} + +void NetworkOPs::unsubAccountInfo(InfoSub* ispListener, const std::vector& vnaAccountIDs) +{ + boost::interprocess::scoped_lock sl(mMonitorLock); + + BOOST_FOREACH(NewcoinAddress naAccountID, vnaAccountIDs) + { + subInfoMapType::iterator simIterator = mSubAccountInfo.find(naAccountID); + if (simIterator == mSubAccountInfo.end()) + { + // Not found. Done. + nothing(); + } + else + { + // Found + simIterator->second.erase(ispListener); + + if (simIterator->second.empty()) + { + // Don't need hash entry. + mSubAccountInfo.erase(simIterator); + } + } + } +} + + +void NetworkOPs::subAccountChanges(InfoSub* ispListener, const uint256 uLedgerHash) +{ +} + +void NetworkOPs::unsubAccountChanges(InfoSub* ispListener) +{ +} + + // vim:ts=4 diff --git a/src/NetworkOPs.h b/src/NetworkOPs.h index 3925f150be..278808b799 100644 --- a/src/NetworkOPs.h +++ b/src/NetworkOPs.h @@ -1,13 +1,16 @@ #ifndef __NETWORK_OPS__ #define __NETWORK_OPS__ -#include "LedgerMaster.h" #include "AccountState.h" -#include "RippleState.h" +#include "LedgerMaster.h" #include "NicknameState.h" +#include "RippleState.h" #include "SerializedValidation.h" -// #include +#include +#include +#include +#include // Operations that clients may wish to perform against the network // Master operational handler, server sequencer, network tracker @@ -15,6 +18,12 @@ class Peer; class LedgerConsensus; +class InfoSub +{ +public: + virtual void send(const Json::Value& jvObj) = 0; +}; + class NetworkOPs { public: @@ -41,6 +50,12 @@ protected: void setMode(OperatingMode); + typedef boost::unordered_map > subInfoMapType; + typedef boost::unordered_map >::iterator subInfoMapIterator; + + boost::interprocess::interprocess_upgradable_mutex mMonitorLock; + subInfoMapType mSubAccountInfo; + public: NetworkOPs(boost::asio::io_service& io_service, LedgerMaster* pLedgerMaster); @@ -133,6 +148,23 @@ public: // client information retrieval functions std::vector< std::pair > getAffectedAccounts(const NewcoinAddress& account, uint32 minLedger, uint32 maxLedger); + + // + // Monitoring: publisher side + // + + void pubAccountInfo(const NewcoinAddress& naAccountID, const Json::Value& jvObj); + + // + // Monitoring: subscriber side + // + + // --> vnaAddress: empty = all + void subAccountInfo(InfoSub* ispListener, const std::vector& vnaAccountIDs); + void unsubAccountInfo(InfoSub* ispListener, const std::vector& vnaAccountIDs); + + void subAccountChanges(InfoSub* ispListener, const uint256 uLedgerHash); + void unsubAccountChanges(InfoSub* ispListener); }; #endif From 9e95955fc7d00c017cb1e93cc64f11c3ca1dcd9c Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Sun, 24 Jun 2012 20:03:17 -0700 Subject: [PATCH 5/5] Work on WS server. --- src/WSDoor.cpp | 196 +++++++++++++++++++++++++++++++++++++++++++------ src/WSDoor.h | 4 +- 2 files changed, 176 insertions(+), 24 deletions(-) diff --git a/src/WSDoor.cpp b/src/WSDoor.cpp index 0d5c71ac41..01b395b3fe 100644 --- a/src/WSDoor.cpp +++ b/src/WSDoor.cpp @@ -1,18 +1,31 @@ #include "WSDoor.h" -#include - -#include -#include - #include "Application.h" #include "Config.h" #include "Log.h" +#include "NetworkOPs.h" #include "utils.h" -using namespace std; -using namespace boost::asio::ip; +#include + +#include +#include +#include + +#include "../json/reader.h" +#include "../json/writer.h" + +// +// This is a light weight, untrusted interface for web clients. +// For now we don't provide proof. Later we will. +// + +// +// Strategy: +// - We only talk to NetworkOPs (so we will work even in thin mode) +// - NetworkOPs is smart enough to subscribe and or pass back messages +// // Generate DH for SSL connection. static DH* handleTmpDh(SSL* ssl, int is_export, int iKeyLength) @@ -20,17 +33,64 @@ static DH* handleTmpDh(SSL* ssl, int is_export, int iKeyLength) return 512 == iKeyLength ? theApp->getWallet().getDh512() : theApp->getWallet().getDh1024(); } +template +class WSServerHandler; + +// +// Storage for connection specific info +// - Subscriptions +// +class WSConnection : public InfoSub +{ +public: + typedef typename websocketpp::WSDOOR_SERVER::handler::connection_ptr connection_ptr; + typedef typename websocketpp::WSDOOR_SERVER::handler::message_ptr message_ptr; + +protected: + WSServerHandler* mHandler; + connection_ptr mConnection; + +public: + WSConnection() + : mHandler((WSServerHandler*)(NULL)), + mConnection(connection_ptr()) { ; } + + WSConnection(WSServerHandler* wshpHandler, connection_ptr cpConnection) + : mHandler(wshpHandler), mConnection(cpConnection) { ; } + + ~WSConnection() + { + // XXX Unsubscribe. + nothing(); + } + + // Implement overriden functions from base class: + void send(const Json::Value& jvObj); +}; + + // A single instance of this object is made. // This instance dispatches all events. There is no per connection persistency. template -class WSServerHandler : public endpoint_type::handler { -private: - boost::shared_ptr mCtx; - +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 mCtx; + +protected: + boost::mutex mMapLock; + boost::unordered_map mMap; + +public: WSServerHandler(boost::shared_ptr spCtx) : mCtx(spCtx) {} boost::shared_ptr on_tls_init() @@ -38,17 +98,103 @@ public: return mCtx; } - void on_message(connection_ptr con, message_ptr msg) { - con->send(msg->get_payload(), msg->get_opcode()); + 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 + { + Log(lsINFO) << "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; + + Log(lsINFO) << "Ws:: Object '" << jfwWriter.write(jvObj) << "'"; + + + send(cpClient, jfwWriter.write(jvObj)); + } + + void on_open(connection_ptr cpClient) + { + boost::mutex::scoped_lock sl(mMapLock); + + mMap[cpClient] = 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 + { + Json::Value jvResult(Json::objectValue); + + jvResult["type"] = "success"; + jvResult["value"] = mpMessage->get_payload(); + + send(cpClient, jvResult); + } } - void http(connection_ptr con) { - con->set_body("WebSocket++ TLS certificate test

WebSocket++ TLS certificate test

This is an HTTP(S) page served by a WebSocket++ server for the purposes of confirming that certificates are working since browsers normally silently ignore certificate issues.

"); + // Respond to http requests. + void http(connection_ptr cpClient) + { + cpClient->set_body( + "" SYSTEM_NAME " Test" + "

" SYSTEM_NAME " Test

This page shows http(s) connectivity is working.

"); } }; void WSDoor::startListening() { + // Generate a single SSL context for use by all connections. boost::shared_ptr mCtx; mCtx = boost::make_shared(boost::asio::ssl::context::sslv23); @@ -59,20 +205,21 @@ void WSDoor::startListening() SSL_CTX_set_tmp_dh_callback(mCtx->native_handle(), handleTmpDh); + // Construct a single handler for all requests. websocketpp::WSDOOR_SERVER::handler::ptr handler(new WSServerHandler(mCtx)); + // Construct a websocket server. mEndpoint = new websocketpp::WSDOOR_SERVER(handler); // mEndpoint->alog().unset_level(websocketpp::log::alevel::ALL); // mEndpoint->elog().unset_level(websocketpp::log::elevel::ALL); - Log(lsINFO) << "listening>"; + // Call the main-event-loop of the websocket server. + mEndpoint->listen( + boost::asio::ip::tcp::endpoint( + boost::asio::ip::address().from_string(theConfig.WEBSOCKET_IP), theConfig.WEBSOCKET_PORT)); - mEndpoint->listen(boost::asio::ip::tcp::endpoint(address().from_string(theConfig.WEBSOCKET_IP), theConfig.WEBSOCKET_PORT)); - - free(mEndpoint); - - Log(lsINFO) << "listening<"; + delete mEndpoint; } WSDoor* WSDoor::createWSDoor() @@ -97,10 +244,15 @@ void WSDoor::stop() { if (mThread) { - mEndpoint->stop(); // XXX Make this thread safe + mEndpoint->stop(); mThread->join(); } } +void WSConnection::send(const Json::Value& jvObj) +{ + mHandler->send(mConnection, jvObj); +} + // vim:ts=4 diff --git a/src/WSDoor.h b/src/WSDoor.h index f44de663f3..af90ddaf88 100644 --- a/src/WSDoor.h +++ b/src/WSDoor.h @@ -19,8 +19,8 @@ class WSDoor { private: - websocketpp::WSDOOR_SERVER* mEndpoint; - boost::thread* mThread; + websocketpp::WSDOOR_SERVER* mEndpoint; + boost::thread* mThread; void startListening();