From 73e48e6595fe1d8c237ea77ef58af064f634e649 Mon Sep 17 00:00:00 2001 From: Nik Bougalis Date: Thu, 31 Mar 2016 09:57:03 -0700 Subject: [PATCH 1/8] Only object instances can have members (RIPD-1100) --- src/ripple/json/impl/json_value.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ripple/json/impl/json_value.cpp b/src/ripple/json/impl/json_value.cpp index 227511ee1a..27c0aae7c7 100644 --- a/src/ripple/json/impl/json_value.cpp +++ b/src/ripple/json/impl/json_value.cpp @@ -1002,6 +1002,9 @@ Value::removeMember ( std::string const& key ) bool Value::isMember ( const char* key ) const { + if (type_ != objectValue) + return false; + const Value* value = & ((*this)[key]); return value != &null; } From 06bfcad6718dcdd333ce9d53980a3a2fa5d8751f Mon Sep 17 00:00:00 2001 From: Nik Bougalis Date: Thu, 31 Mar 2016 00:26:40 -0700 Subject: [PATCH 2/8] Validate the tx_json field in sign_for (RIPD-1100) --- src/ripple/rpc/impl/TransactionSign.cpp | 11 +++++-- src/ripple/rpc/tests/JSONRPC.test.cpp | 39 +++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/ripple/rpc/impl/TransactionSign.cpp b/src/ripple/rpc/impl/TransactionSign.cpp index 1e6d8dd192..6b1474f90a 100644 --- a/src/ripple/rpc/impl/TransactionSign.cpp +++ b/src/ripple/rpc/impl/TransactionSign.cpp @@ -785,6 +785,9 @@ static Json::Value checkMultiSignFields (Json::Value const& jvRequest) Json::Value const& tx_json (jvRequest [jss::tx_json]); + if (!tx_json.isObject()) + return RPC::invalid_field_message (jss::tx_json); + // There are a couple of additional fields we need to check before // we serialize. If we serialize first then we generate less useful //error messages. @@ -878,13 +881,17 @@ Json::Value transactionSignFor ( RPC::invalid_field_message (accountField)); } - // If the tx_json.SigningPubKey field is missing, insert an empty one. - // RIPD-1036. if (! jvRequest.isMember (jss::tx_json)) return RPC::missing_field_error (jss::tx_json); { Json::Value& tx_json (jvRequest [jss::tx_json]); + + if (!tx_json.isObject()) + return RPC::object_field_error (jss::tx_json); + + // If the tx_json.SigningPubKey field is missing, + // insert an empty one. if (!tx_json.isMember (sfSigningPubKey.getJsonName())) tx_json[sfSigningPubKey.getJsonName()] = ""; } diff --git a/src/ripple/rpc/tests/JSONRPC.test.cpp b/src/ripple/rpc/tests/JSONRPC.test.cpp index 1965c957e2..bd6c7f247f 100644 --- a/src/ripple/rpc/tests/JSONRPC.test.cpp +++ b/src/ripple/rpc/tests/JSONRPC.test.cpp @@ -1112,6 +1112,45 @@ R"({ "Missing field 'tx_json.TransactionType'.", "Missing field 'tx_json.TransactionType'."}}, +{ "Invalid field 'tx_json': string instead of object", +R"({ + "command": "doesnt_matter", + "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", + "secret": "masterpassphrase", + "tx_json": "" +})", +{ +"Invalid field 'tx_json', not object.", +"Invalid field 'tx_json', not object.", +"Invalid field 'tx_json', not object.", +"Invalid field 'tx_json', not object."}}, + +{ "Invalid field 'tx_json': integer instead of object", +R"({ + "command": "doesnt_matter", + "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", + "secret": "masterpassphrase", + "tx_json": 20160331 +})", +{ +"Invalid field 'tx_json', not object.", +"Invalid field 'tx_json', not object.", +"Invalid field 'tx_json', not object.", +"Invalid field 'tx_json', not object."}}, + +{ "Invalid field 'tx_json': array instead of object", +R"({ + "command": "doesnt_matter", + "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", + "secret": "masterpassphrase", + "tx_json": [ "hello", "world" ] +})", +{ +"Invalid field 'tx_json', not object.", +"Invalid field 'tx_json', not object.", +"Invalid field 'tx_json', not object.", +"Invalid field 'tx_json', not object."}}, + { "Minimal submit_multisigned.", R"({ "command": "submit_multisigned", From 5c5ee6f763c31c5565502648499887e06d417b9f Mon Sep 17 00:00:00 2001 From: Nik Bougalis Date: Fri, 1 Apr 2016 14:32:00 -0700 Subject: [PATCH 3/8] Set version to 0.31.0-rc2 --- src/ripple/protocol/impl/BuildInfo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ripple/protocol/impl/BuildInfo.cpp b/src/ripple/protocol/impl/BuildInfo.cpp index 5e57f93524..f7b0adf7de 100644 --- a/src/ripple/protocol/impl/BuildInfo.cpp +++ b/src/ripple/protocol/impl/BuildInfo.cpp @@ -35,7 +35,7 @@ char const* getRawVersionString () // // The build version number (edit this for each release) // - "0.31.0-rc1" + "0.31.0-rc2" // // Must follow the format described here: // From dabc5567f726dbb689d3d135c9fdb4fdb2a95e62 Mon Sep 17 00:00:00 2001 From: David Schwartz Date: Mon, 11 Apr 2016 11:20:23 -0700 Subject: [PATCH 4/8] Small Websocket cleanups. --- src/ripple/websocket/AutoSocket.h | 4 ++-- src/ripple/websocket/Handler.h | 7 +++++-- src/websocketpp_02/src/sockets/autotls.hpp | 9 +++------ 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/ripple/websocket/AutoSocket.h b/src/ripple/websocket/AutoSocket.h index 3c13accddb..6d10246e4d 100644 --- a/src/ripple/websocket/AutoSocket.h +++ b/src/ripple/websocket/AutoSocket.h @@ -38,7 +38,7 @@ class AutoSocket public: using ssl_socket = boost::asio::ssl::stream; using endpoint_type = boost::asio::ip::tcp::socket::endpoint_type; - using socket_ptr = std::shared_ptr; + using socket_ptr = std::unique_ptr; using plain_socket = ssl_socket::next_layer_type; using lowest_layer_type = ssl_socket::lowest_layer_type; using handshake_type = ssl_socket::handshake_type; @@ -55,7 +55,7 @@ public: , mBuffer ((plainOnly || secureOnly) ? 0 : 4) , j_ (ripple::debugJournal()) { - mSocket = std::make_shared (s, c); + mSocket = std::make_unique (s, c); } AutoSocket ( diff --git a/src/ripple/websocket/Handler.h b/src/ripple/websocket/Handler.h index 6391b24561..4bd4e3c0d8 100644 --- a/src/ripple/websocket/Handler.h +++ b/src/ripple/websocket/Handler.h @@ -278,7 +278,7 @@ public: return; } - ptr = it->second; + ptr = std::move (it->second); // prevent the ConnectionImpl from being destroyed until we release // the lock mMap.erase (it); @@ -298,7 +298,10 @@ public: app_.getJobQueue ().addJob ( jtCLIENT, "WSClient::destroy", - [ptr] (Job&) { ConnectionImpl ::destroy(ptr); }); + [p = std::move(ptr)] (Job&) + { + ConnectionImpl ::destroy(std::move (p)); + }); } void message_job(std::string const& name, connection_ptr const& cpClient) diff --git a/src/websocketpp_02/src/sockets/autotls.hpp b/src/websocketpp_02/src/sockets/autotls.hpp index f0ae9fbb74..c18de9115e 100644 --- a/src/websocketpp_02/src/sockets/autotls.hpp +++ b/src/websocketpp_02/src/sockets/autotls.hpp @@ -133,18 +133,15 @@ public: callback(error); } - // note, this function for some reason shouldn't/doesn't need to be - // called for plain HTTP connections. not sure why. + // Only SSL conections actually need to be shut down bool shutdown() { boost::system::error_code ignored_ec; m_socket_ptr->async_shutdown( // Don't block on connection shutdown DJS std::bind( - &autotls::handle_shutdown, + &autotls::handle_shutdown, m_socket_ptr, - beast::asio::placeholders::error - ) - ); + beast::asio::placeholders::error)); if (ignored_ec) { return false; From 76d7c1c01ad6c53b67c3a5c9e488a75086da4082 Mon Sep 17 00:00:00 2001 From: Miguel Portilla Date: Fri, 15 Apr 2016 12:54:50 -0400 Subject: [PATCH 5/8] Fix secured Websocket closing. Websocketpp was incorrectly handling close before or during protocol negotiation. This issue addresses lingering CLOSE_WAIT file descriptors. --- src/websocketpp_02/src/connection.hpp | 2 ++ src/websocketpp_02/src/roles/server.hpp | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/websocketpp_02/src/connection.hpp b/src/websocketpp_02/src/connection.hpp index a03a88a6ee..df97c384e5 100644 --- a/src/websocketpp_02/src/connection.hpp +++ b/src/websocketpp_02/src/connection.hpp @@ -1404,6 +1404,8 @@ public: log_close_result(); } + else + m_state = session::state::CLOSED; // finally remove this connection from the endpoint's list. This will // remove the last shared pointer to the connection held by WS++. If we diff --git a/src/websocketpp_02/src/roles/server.hpp b/src/websocketpp_02/src/roles/server.hpp index 83858f03cf..ea37fc9b2b 100644 --- a/src/websocketpp_02/src/roles/server.hpp +++ b/src/websocketpp_02/src/roles/server.hpp @@ -914,7 +914,8 @@ void server::connection::handle_write_response( return; } - if (m_response.get_status_code() != http::status_code::SWITCHING_PROTOCOLS) { + if (m_response.get_status_code() != http::status_code::SWITCHING_PROTOCOLS || + m_connection.m_state == session::state::CLOSED) { if (m_version == -1) { // if this was not a websocket connection, we have written // the expected response and the connection can be closed. From 51850ded0519801b38406de50f042819e3db2546 Mon Sep 17 00:00:00 2001 From: Nik Bougalis Date: Fri, 8 Apr 2016 09:30:22 -0700 Subject: [PATCH 6/8] Improve watchdog start logic --- src/ripple/app/main/Main.cpp | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/src/ripple/app/main/Main.cpp b/src/ripple/app/main/Main.cpp index dbd8c9219d..d14a0c32e5 100644 --- a/src/ripple/app/main/Main.cpp +++ b/src/ripple/app/main/Main.cpp @@ -266,20 +266,6 @@ int run (int argc, char** argv) return 0; } - // Use a watchdog process unless we're invoking a stand alone type of mode - // - if (HaveSustain () - && !vm.count ("parameters") - && !vm.count ("fg") - && !vm.count ("standalone") - && !vm.count ("unittest")) - { - std::string logMe = DoSustain (); - - if (!logMe.empty ()) - std::cerr << logMe << std::endl; - } - // Run the unit tests if requested. // The unit tests will exit the application with an appropriate return code. // @@ -432,6 +418,14 @@ int run (int argc, char** argv) // No arguments. Run server. if (!vm.count ("parameters")) { + if (HaveSustain() && !vm.count ("fg") && !config->RUN_STANDALONE) + { + auto const ret = DoSustain (); + + if (!ret.empty ()) + std::cerr << "Watchdog: " << ret << std::endl; + } + if (vm.count ("debug")) setDebugJournalSink (logs->get("Debug")); From 4e64e3f1dda9e26b0b456efcb221549b429fb61d Mon Sep 17 00:00:00 2001 From: Nik Bougalis Date: Fri, 8 Apr 2016 09:39:09 -0700 Subject: [PATCH 7/8] Check file handle limit on startup (RIPD-442, RIPD-1024): Calculate the number of file descriptors that are needed during execution based on the configuration file, with a hard floor of 1024, adjusting the limit if possible. Refuse to run if enough fds are not available. Additionally, allow administrators to limit the number of incoming connections a configured port will accept. By default no limit is imposed. --- src/ripple/app/main/Application.cpp | 34 +++++++-- src/ripple/app/main/Application.h | 10 +-- src/ripple/app/main/Main.cpp | 76 ++++++++++++++----- src/ripple/app/misc/SHAMapStore.h | 3 + src/ripple/app/misc/SHAMapStoreImp.cpp | 14 +++- src/ripple/app/misc/SHAMapStoreImp.h | 3 + src/ripple/nodestore/Backend.h | 3 + src/ripple/nodestore/Database.h | 3 + .../nodestore/backend/MemoryFactory.cpp | 6 ++ src/ripple/nodestore/backend/NuDBFactory.cpp | 7 ++ src/ripple/nodestore/backend/NullFactory.cpp | 7 ++ .../nodestore/backend/RocksDBFactory.cpp | 11 ++- .../nodestore/backend/RocksDBQuickFactory.cpp | 12 ++- src/ripple/nodestore/impl/DatabaseImp.h | 13 +++- src/ripple/overlay/Overlay.h | 5 ++ src/ripple/overlay/impl/OverlayImpl.cpp | 6 ++ src/ripple/overlay/impl/OverlayImpl.h | 30 ++++---- src/ripple/server/Handler.h | 4 - src/ripple/server/Port.h | 5 ++ src/ripple/server/impl/PlainHTTPPeer.h | 9 ++- src/ripple/server/impl/Port.cpp | 43 ++++++++--- src/ripple/server/impl/SSLHTTPPeer.h | 9 ++- src/ripple/server/impl/ServerHandlerImp.cpp | 19 +++-- src/ripple/server/impl/ServerHandlerImp.h | 12 ++- src/ripple/server/tests/Server_test.cpp | 10 --- 25 files changed, 266 insertions(+), 88 deletions(-) diff --git a/src/ripple/app/main/Application.cpp b/src/ripple/app/main/Application.cpp index 84fcafde03..8252b15ad4 100644 --- a/src/ripple/app/main/Application.cpp +++ b/src/ripple/app/main/Application.cpp @@ -534,6 +534,7 @@ public: void signalStop() override; bool checkSigs() const override; void checkSigs(bool) override; + int fdlimit () const override; //-------------------------------------------------------------------------- @@ -1155,14 +1156,12 @@ ApplicationImp::doStart() void ApplicationImp::run() { + if (!config_->RUN_STANDALONE) { - if (!config_->RUN_STANDALONE) - { - // VFALCO NOTE This seems unnecessary. If we properly refactor the load - // manager then the deadlock detector can just always be "armed" - // - getLoadManager ().activateDeadlockDetector (); - } + // VFALCO NOTE This seems unnecessary. If we properly refactor the load + // manager then the deadlock detector can just always be "armed" + // + getLoadManager ().activateDeadlockDetector (); } m_stop.wait (); @@ -1200,6 +1199,27 @@ void ApplicationImp::checkSigs(bool check) checkSigs_ = check; } +int ApplicationImp::fdlimit() const +{ + // Standard handles, config file, misc I/O etc: + int needed = 128; + + // 1.5 times the configured peer limit for peer connections: + needed += static_cast(0.5 + (1.5 * m_overlay->limit())); + + // the number of fds needed by the backend (internally + // doubled if online delete is enabled). + needed += std::max(5, m_shaMapStore->fdlimit()); + + // One fd per incoming connection a port can accept, or + // if no limit is set, assume it'll handle 256 clients. + for(auto const& p : serverHandler_->setup().ports) + needed += std::max (256, p.limit); + + // The minimum number of file descriptors we need is 1024: + return std::max(1024, needed); +} + //------------------------------------------------------------------------------ void diff --git a/src/ripple/app/main/Application.h b/src/ripple/app/main/Application.h index 56db547764..cd3ea15f8b 100644 --- a/src/ripple/app/main/Application.h +++ b/src/ripple/app/main/Application.h @@ -151,13 +151,11 @@ public: virtual bool serverOkay (std::string& reason) = 0; virtual beast::Journal journal (std::string const& name) = 0; - /** Retrieve the "wallet database" - It looks like this is used to store the unique node list. - */ - // VFALCO TODO Rename, document this - // NOTE This will be replaced by class Validators - // + /* Returns the number of file descriptors the application wants */ + virtual int fdlimit () const = 0; + + /** Retrieve the "wallet database" */ virtual DatabaseCon& getWalletDB () = 0; }; diff --git a/src/ripple/app/main/Main.cpp b/src/ripple/app/main/Main.cpp index d14a0c32e5..6ba73f0667 100644 --- a/src/ripple/app/main/Main.cpp +++ b/src/ripple/app/main/Main.cpp @@ -54,23 +54,6 @@ namespace po = boost::program_options; namespace ripple { -void setupServer (Application& app) -{ -#ifdef RLIMIT_NOFILE - struct rlimit rl; - if (getrlimit(RLIMIT_NOFILE, &rl) == 0) - { - if (rl.rlim_cur != rl.rlim_max) - { - rl.rlim_cur = rl.rlim_max; - setrlimit(RLIMIT_NOFILE, &rl); - } - } -#endif - - app.setup (); -} - boost::filesystem::path getEntropyFile(Config const& config) { @@ -80,6 +63,47 @@ getEntropyFile(Config const& config) return boost::filesystem::path (path) / "random.seed"; } +bool +adjustDescriptorLimit(int needed) +{ +#ifdef RLIMIT_NOFILE + // Get the current limit, then adjust it to what we need. + struct rlimit rl; + + int available = 0; + + if (getrlimit(RLIMIT_NOFILE, &rl) == 0) + { + // If the limit is infnite, then we are good. + if (rl.rlim_cur == RLIM_INFINITY) + available = needed; + else + available = rl.rlim_cur; + + if (available < needed) + { + // Ignore the rlim_max, as the process may + // be configured to override it anyways. We + // ask for the number descriptors we need. + rl.rlim_cur = needed; + + if (setrlimit(RLIMIT_NOFILE, &rl) == 0) + available = rl.rlim_cur; + } + } + + if (needed > available) + { + std::cerr << "Insufficient number of file descriptors:\n"; + std::cerr << " Needed: " << needed << '\n'; + std::cerr << " Available: " << available << '\n'; + return false; + } +#endif + + return true; +} + void startServer (Application& app) { // @@ -418,6 +442,11 @@ int run (int argc, char** argv) // No arguments. Run server. if (!vm.count ("parameters")) { + // We want at least 1024 file descriptors. We'll + // tweak this further. + if (!adjustDescriptorLimit(1024)) + return -1; + if (HaveSustain() && !vm.count ("fg") && !config->RUN_STANDALONE) { auto const ret = DoSustain (); @@ -436,7 +465,16 @@ int run (int argc, char** argv) std::move(config), std::move(logs), std::move(timeKeeper)); - setupServer (*app); + app->setup (); + + // With our configuration parsed, ensure we have + // enough file descriptors available: + if (!adjustDescriptorLimit(app->fdlimit())) + { + StopSustain(); + return -1; + } + startServer (*app); return 0; } @@ -449,8 +487,6 @@ int run (int argc, char** argv) *logs); } -extern int run (int argc, char** argv); - } // ripple // Must be outside the namespace for obvious reasons diff --git a/src/ripple/app/misc/SHAMapStore.h b/src/ripple/app/misc/SHAMapStore.h index ad85c87cd7..935a50b0b4 100644 --- a/src/ripple/app/misc/SHAMapStore.h +++ b/src/ripple/app/misc/SHAMapStore.h @@ -73,6 +73,9 @@ public: /** Highest ledger that may be deleted. */ virtual LedgerIndex getCanDelete() = 0; + + /** The number of files that are needed. */ + virtual int fdlimit() const = 0; }; //------------------------------------------------------------------------------ diff --git a/src/ripple/app/misc/SHAMapStoreImp.cpp b/src/ripple/app/misc/SHAMapStoreImp.cpp index 205ae18c0a..aa37ba7459 100644 --- a/src/ripple/app/misc/SHAMapStoreImp.cpp +++ b/src/ripple/app/misc/SHAMapStoreImp.cpp @@ -221,6 +221,9 @@ SHAMapStoreImp::makeDatabase (std::string const& name, makeBackendRotating (state.writableDb)); std::shared_ptr archiveBackend ( makeBackendRotating (state.archiveDb)); + + fdlimit_ = writableBackend->fdlimit() + archiveBackend->fdlimit(); + std::unique_ptr dbr = makeDatabaseRotating (name, readThreads, writableBackend, archiveBackend); @@ -237,8 +240,9 @@ SHAMapStoreImp::makeDatabase (std::string const& name, } else { - db = NodeStore::Manager::instance().make_Database (name, scheduler_, nodeStoreJournal_, - readThreads, setup_.nodeDatabase); + db = NodeStore::Manager::instance().make_Database (name, scheduler_, + nodeStoreJournal_, readThreads, setup_.nodeDatabase); + fdlimit_ = db->fdlimit(); } return db; @@ -255,6 +259,12 @@ SHAMapStoreImp::onLedgerClosed( cond_.notify_one(); } +int +SHAMapStoreImp::fdlimit () const +{ + return fdlimit_; +} + bool SHAMapStoreImp::copyNode (std::uint64_t& nodeCount, SHAMapAbstractNode const& node) diff --git a/src/ripple/app/misc/SHAMapStoreImp.h b/src/ripple/app/misc/SHAMapStoreImp.h index 797db777c4..092692ef2c 100644 --- a/src/ripple/app/misc/SHAMapStoreImp.h +++ b/src/ripple/app/misc/SHAMapStoreImp.h @@ -107,6 +107,7 @@ private: TreeNodeCache* treeNodeCache_ = nullptr; DatabaseCon* transactionDb_ = nullptr; DatabaseCon* ledgerDb_ = nullptr; + int fdlimit_ = 0; public: bool rotating() const @@ -168,6 +169,8 @@ public: void onLedgerClosed (std::shared_ptr const& ledger) override; + int fdlimit() const override; + private: // callback for visitNodes bool copyNode (std::uint64_t& nodeCount, SHAMapAbstractNode const &node); diff --git a/src/ripple/nodestore/Backend.h b/src/ripple/nodestore/Backend.h index 463f84d840..2ee7088f2b 100644 --- a/src/ripple/nodestore/Backend.h +++ b/src/ripple/nodestore/Backend.h @@ -105,6 +105,9 @@ public: /** Perform consistency checks on database .*/ virtual void verify() = 0; + + /** Returns the number of file handles the backend expects to need */ + virtual int fdlimit() const = 0; }; } diff --git a/src/ripple/nodestore/Database.h b/src/ripple/nodestore/Database.h index b1356a392d..1134fd9622 100644 --- a/src/ripple/nodestore/Database.h +++ b/src/ripple/nodestore/Database.h @@ -147,6 +147,9 @@ public: virtual std::uint32_t getFetchHitCount () const = 0; virtual std::uint32_t getStoreSize () const = 0; virtual std::uint32_t getFetchSize () const = 0; + + /** Return the number of files needed by our backend */ + virtual int fdlimit() const = 0; }; } diff --git a/src/ripple/nodestore/backend/MemoryFactory.cpp b/src/ripple/nodestore/backend/MemoryFactory.cpp index c11fe8eaeb..db401bd739 100644 --- a/src/ripple/nodestore/backend/MemoryFactory.cpp +++ b/src/ripple/nodestore/backend/MemoryFactory.cpp @@ -178,6 +178,12 @@ public: verify() override { } + + int + fdlimit() const override + { + return 0; + } }; //------------------------------------------------------------------------------ diff --git a/src/ripple/nodestore/backend/NuDBFactory.cpp b/src/ripple/nodestore/backend/NuDBFactory.cpp index a4bc65cc07..506d41fd48 100644 --- a/src/ripple/nodestore/backend/NuDBFactory.cpp +++ b/src/ripple/nodestore/backend/NuDBFactory.cpp @@ -244,6 +244,13 @@ public: db_.open (dp, kp, lp, arena_alloc_size); } + + /** Returns the number of file handles the backend expects to need */ + int + fdlimit() const override + { + return 3; + } }; //------------------------------------------------------------------------------ diff --git a/src/ripple/nodestore/backend/NullFactory.cpp b/src/ripple/nodestore/backend/NullFactory.cpp index 63957f8b63..c08033bec4 100644 --- a/src/ripple/nodestore/backend/NullFactory.cpp +++ b/src/ripple/nodestore/backend/NullFactory.cpp @@ -98,6 +98,13 @@ public: { } + /** Returns the number of file handles the backend expects to need */ + int + fdlimit() const override + { + return 0; + } + private: }; diff --git a/src/ripple/nodestore/backend/RocksDBFactory.cpp b/src/ripple/nodestore/backend/RocksDBFactory.cpp index a772fbaf88..524b683a05 100644 --- a/src/ripple/nodestore/backend/RocksDBFactory.cpp +++ b/src/ripple/nodestore/backend/RocksDBFactory.cpp @@ -99,6 +99,7 @@ public: BatchWriter m_batch; std::string m_name; std::unique_ptr m_db; + int fdlimit_ = 2048; RocksDBBackend (int keyBytes, Section const& keyValues, Scheduler& scheduler, beast::Journal journal, RocksDBEnv* env) @@ -122,7 +123,8 @@ public: if (auto const v = get(keyValues, "filter_bits")) table_options.filter_policy.reset (rocksdb::NewBloomFilterPolicy (v)); - get_if_exists (keyValues, "open_files", options.max_open_files); + if (get_if_exists (keyValues, "open_files", options.max_open_files)) + fdlimit_ = options.max_open_files; if (keyValues.exists ("file_size_mb")) { @@ -361,6 +363,13 @@ public: verify() override { } + + /** Returns the number of file handles the backend expects to need */ + int + fdlimit() const override + { + return fdlimit_; + } }; //------------------------------------------------------------------------------ diff --git a/src/ripple/nodestore/backend/RocksDBQuickFactory.cpp b/src/ripple/nodestore/backend/RocksDBQuickFactory.cpp index f99468e1ae..f9f56872cc 100644 --- a/src/ripple/nodestore/backend/RocksDBQuickFactory.cpp +++ b/src/ripple/nodestore/backend/RocksDBQuickFactory.cpp @@ -95,6 +95,7 @@ public: size_t const m_keyBytes; std::string m_name; std::unique_ptr m_db; + int fdlimit_ = 2048; RocksDBQuickBackend (int keyBytes, Section const& keyValues, Scheduler& scheduler, beast::Journal journal, RocksDBQuickEnv* env) @@ -116,7 +117,6 @@ public: get_if_exists (keyValues, "style", style); get_if_exists (keyValues, "threads", threads); - // Set options rocksdb::Options options; options.create_if_missing = true; @@ -160,7 +160,8 @@ public: // options.memtable_factory.reset( // rocksdb::NewHashCuckooRepFactory(options.write_buffer_size)); - get_if_exists (keyValues, "open_files", options.max_open_files); + if (get_if_exists (keyValues, "open_files", options.max_open_files)) + fdlimit_ = options.max_open_files; if (keyValues.exists ("compression") && (get(keyValues, "compression") == 0)) @@ -363,6 +364,13 @@ public: verify() override { } + + /** Returns the number of file handles the backend expects to need */ + int + fdlimit() const override + { + return fdlimit_; + } }; //------------------------------------------------------------------------------ diff --git a/src/ripple/nodestore/impl/DatabaseImp.h b/src/ripple/nodestore/impl/DatabaseImp.h index 361747e668..796f3735b3 100644 --- a/src/ripple/nodestore/impl/DatabaseImp.h +++ b/src/ripple/nodestore/impl/DatabaseImp.h @@ -61,6 +61,8 @@ private: std::vector m_readThreads; bool m_readShut; uint64_t m_readGen; // current read generation + int fdlimit_; + public: DatabaseImp (std::string const& name, Scheduler& scheduler, @@ -83,8 +85,10 @@ public: , m_fetchSize (0) { for (int i = 0; i < readThreads; ++i) - m_readThreads.push_back (std::thread (&DatabaseImp::threadEntry, - this)); + m_readThreads.emplace_back (&DatabaseImp::threadEntry, this); + + if (m_backend) + fdlimit_ = m_backend->fdlimit(); } ~DatabaseImp () @@ -432,6 +436,11 @@ public: return m_fetchSize; } + int fdlimit() const override + { + return fdlimit_; + } + private: std::atomic m_storeCount; std::atomic m_fetchTotalCount; diff --git a/src/ripple/overlay/Overlay.h b/src/ripple/overlay/Overlay.h index 673079999c..3d940f3a4f 100644 --- a/src/ripple/overlay/Overlay.h +++ b/src/ripple/overlay/Overlay.h @@ -92,6 +92,11 @@ public: void connect (beast::IP::Endpoint const& address) = 0; + /** Returns the maximum number of peers we are configured to allow. */ + virtual + int + limit () = 0; + /** Returns the number of active peers. Active peers are only those peers that have completed the handshake and are using the peer protocol. diff --git a/src/ripple/overlay/impl/OverlayImpl.cpp b/src/ripple/overlay/impl/OverlayImpl.cpp index ef119e934a..77e3ec58c4 100644 --- a/src/ripple/overlay/impl/OverlayImpl.cpp +++ b/src/ripple/overlay/impl/OverlayImpl.cpp @@ -774,6 +774,12 @@ OverlayImpl::size() return ids_.size (); } +int +OverlayImpl::limit() +{ + return m_peerFinder->config().maxPeers; +} + Json::Value OverlayImpl::crawl() { diff --git a/src/ripple/overlay/impl/OverlayImpl.h b/src/ripple/overlay/impl/OverlayImpl.h index 0d96e44ed1..f905597a53 100644 --- a/src/ripple/overlay/impl/OverlayImpl.h +++ b/src/ripple/overlay/impl/OverlayImpl.h @@ -266,6 +266,22 @@ public: bool isInbound, int bytes); + /* The number of active peers on the network + Active peers are only those peers that have completed the handshake + and are running the Ripple protocol. + */ + std::size_t + size() override; + + int + limit () override; + + Json::Value + crawl() override; + + Json::Value + json() override; + private: std::shared_ptr makeRedirectResponse (PeerFinder::Slot::ptr const& slot, @@ -274,20 +290,6 @@ private: void connect (beast::IP::Endpoint const& remote_endpoint) override; - /* The number of active peers on the network - Active peers are only those peers that have completed the handshake - and are running the Ripple protocol. - */ - // VFALCO Why private? - std::size_t - size() override; - - Json::Value - crawl() override; - - Json::Value - json() override; - bool processRequest (beast::http::message const& req, Handoff& handoff); diff --git a/src/ripple/server/Handler.h b/src/ripple/server/Handler.h index e1ef130416..6cdb477602 100644 --- a/src/ripple/server/Handler.h +++ b/src/ripple/server/Handler.h @@ -39,10 +39,6 @@ class Server; */ struct Handler { - /** Called when the connection is accepted and we know remoteAddress. */ - // DEPRECATED - virtual void onAccept (Session& session) = 0; - /** Called when a connection is accepted. @return `true` If we should keep the connection. */ diff --git a/src/ripple/server/Port.h b/src/ripple/server/Port.h index b04c4e85a4..42ddcae986 100644 --- a/src/ripple/server/Port.h +++ b/src/ripple/server/Port.h @@ -51,6 +51,10 @@ struct Port std::string ssl_chain; std::shared_ptr context; + // How many incoming connections are allowed on this + // port in the range [0, 65535] where 0 means unlimited. + int limit = 0; + // Returns `true` if any websocket protocols are specified bool websockets() const; @@ -77,6 +81,7 @@ struct ParsedPort std::string ssl_key; std::string ssl_cert; std::string ssl_chain; + int limit = 0; boost::optional ip; boost::optional port; diff --git a/src/ripple/server/impl/PlainHTTPPeer.h b/src/ripple/server/impl/PlainHTTPPeer.h index 668ba476fe..07dcc494f9 100644 --- a/src/ripple/server/impl/PlainHTTPPeer.h +++ b/src/ripple/server/impl/PlainHTTPPeer.h @@ -73,7 +73,14 @@ PlainHTTPPeer::PlainHTTPPeer (Port const& port, Handler& handler, void PlainHTTPPeer::run () { - handler_.onAccept (session()); + if (!handler_.onAccept (session(), remote_address_)) + { + boost::asio::spawn (strand_, + std::bind (&PlainHTTPPeer::do_close, + shared_from_this())); + return; + } + if (! stream_.is_open()) return; diff --git a/src/ripple/server/impl/Port.cpp b/src/ripple/server/impl/Port.cpp index 5bccd6356c..08f006f305 100644 --- a/src/ripple/server/impl/Port.cpp +++ b/src/ripple/server/impl/Port.cpp @@ -19,9 +19,10 @@ #include #include +#include namespace ripple { - + bool Port::websockets() const { @@ -159,20 +160,22 @@ parse_Port (ParsedPort& port, Section const& section, std::ostream& log) auto const result = section.find("port"); if (result.second) { - auto const ul = std::stoul(result.first); - if (ul > std::numeric_limits::max()) + try { - log << "Value '" << result.first - << "' for key 'port' is out of range\n"; - Throw (); + port.port = + beast::lexicalCastThrow(result.first); + + // Port 0 is not supported + if (*port.port == 0) + Throw (); } - if (ul == 0) + catch (std::exception const& ex) { log << - "Value '0' for key 'port' is invalid\n"; - Throw (); + "Invalid value '" << result.first << "' for key " << + "'port' in [" << section.name() << "]\n"; + Throw(); } - port.port = static_cast(ul); } } @@ -186,6 +189,26 @@ parse_Port (ParsedPort& port, Section const& section, std::ostream& log) } } + { + auto const lim = get (section, "limit", "unlimited"); + + if (!beast::ci_equal (lim, "unlimited")) + { + try + { + port.limit = static_cast ( + beast::lexicalCastThrow(lim)); + } + catch (std::exception const& ex) + { + log << + "Invalid value '" << lim << "' for key " << + "'limit' in [" << section.name() << "]\n"; + Throw(); + } + } + } + populate (section, "admin", log, port.admin_ip, true, {}); populate (section, "secure_gateway", log, port.secure_gateway_ip, false, port.admin_ip.get_value_or({})); diff --git a/src/ripple/server/impl/SSLHTTPPeer.h b/src/ripple/server/impl/SSLHTTPPeer.h index 07eeb6e9d2..938e4aa925 100644 --- a/src/ripple/server/impl/SSLHTTPPeer.h +++ b/src/ripple/server/impl/SSLHTTPPeer.h @@ -78,7 +78,14 @@ SSLHTTPPeer::SSLHTTPPeer (Port const& port, Handler& handler, void SSLHTTPPeer::run() { - handler_.onAccept (session()); + if (!handler_.onAccept (session(), remote_address_)) + { + boost::asio::spawn (strand_, + std::bind (&SSLHTTPPeer::do_close, + shared_from_this())); + return; + } + if (! stream_.lowest_layer().is_open()) return; diff --git a/src/ripple/server/impl/ServerHandlerImp.cpp b/src/ripple/server/impl/ServerHandlerImp.cpp index df71d1ede0..7e45f92daa 100644 --- a/src/ripple/server/impl/ServerHandlerImp.cpp +++ b/src/ripple/server/impl/ServerHandlerImp.cpp @@ -94,15 +94,22 @@ ServerHandlerImp::onStop() //------------------------------------------------------------------------------ -void -ServerHandlerImp::onAccept (Session& session) -{ -} - bool ServerHandlerImp::onAccept (Session& session, boost::asio::ip::tcp::endpoint endpoint) { + std::lock_guard l(countlock_); + + auto const c = ++count_[session.port()]; + + if (session.port().limit && c >= session.port().limit) + { + JLOG (m_journal.trace) << + session.port().name << " is full; dropping " << + endpoint; + return false; + } + return true; } @@ -188,6 +195,8 @@ void ServerHandlerImp::onClose (Session& session, boost::system::error_code const&) { + std::lock_guard l(countlock_); + --count_[session.port()]; } void diff --git a/src/ripple/server/impl/ServerHandlerImp.h b/src/ripple/server/impl/ServerHandlerImp.h index 8d43c2086a..29e8f4db5e 100644 --- a/src/ripple/server/impl/ServerHandlerImp.h +++ b/src/ripple/server/impl/ServerHandlerImp.h @@ -28,9 +28,16 @@ #include #include #include +#include +#include namespace ripple { +bool operator< (Port const& lhs, Port const& rhs) +{ + return lhs.name < rhs.name; +} + // Private implementation class ServerHandlerImp : public ServerHandler @@ -47,6 +54,8 @@ private: beast::insight::Counter rpc_requests_; beast::insight::Event rpc_size_; beast::insight::Event rpc_time_; + std::mutex countlock_; + std::map, int> count_; public: ServerHandlerImp (Application& app, Stoppable& parent, @@ -79,9 +88,6 @@ private: // HTTP::Handler // - void - onAccept (Session& session) override; - bool onAccept (Session& session, boost::asio::ip::tcp::endpoint endpoint) override; diff --git a/src/ripple/server/tests/Server_test.cpp b/src/ripple/server/tests/Server_test.cpp index 2c02c04cc6..271f41a82c 100644 --- a/src/ripple/server/tests/Server_test.cpp +++ b/src/ripple/server/tests/Server_test.cpp @@ -97,11 +97,6 @@ public: struct TestHandler : Handler { - void - onAccept (Session& session) override - { - } - bool onAccept (Session& session, boost::asio::ip::tcp::endpoint endpoint) override @@ -304,11 +299,6 @@ public: { struct NullHandler : Handler { - void - onAccept (Session& session) override - { - } - bool onAccept (Session& session, boost::asio::ip::tcp::endpoint endpoint) override From a5d58566386fd86ae4c816c82085fe242b255d2c Mon Sep 17 00:00:00 2001 From: Nik Bougalis Date: Sun, 17 Apr 2016 18:02:02 -0700 Subject: [PATCH 8/8] Set version to 0.31.0 --- src/ripple/protocol/impl/BuildInfo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ripple/protocol/impl/BuildInfo.cpp b/src/ripple/protocol/impl/BuildInfo.cpp index f7b0adf7de..647ee1edaa 100644 --- a/src/ripple/protocol/impl/BuildInfo.cpp +++ b/src/ripple/protocol/impl/BuildInfo.cpp @@ -35,7 +35,7 @@ char const* getRawVersionString () // // The build version number (edit this for each release) // - "0.31.0-rc2" + "0.31.0" // // Must follow the format described here: //