From dbdf68b2482e9cb22b3da40787955c2f4005b5ff Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Wed, 15 Oct 2014 00:41:10 -0700 Subject: [PATCH] Refactor HTTP::Server to support Universal Port: These changes are necessary to support the Universal port feature. Synopsis: * Persist HTTP peer io_service::work lifetime: This simplification eliminates any potential for bugs caused by incorrect lifetime management of the io_service::work object. * Restructure Door to prevent data races, and handle clean exit: The Server, Door, Door::detector, and Peer objects work together to correctly implement graceful stop and destructors that block until all child objects have been destroyed. Cleanups: * De-pimpl HTTP::Server * Rename ServerImpl data members * Tidy up HTTP::Port interface --- Builds/VisualStudio2013/RippleD.vcxproj | 4 + .../VisualStudio2013/RippleD.vcxproj.filters | 6 + src/ripple/app/main/ServerHandlerImp.cpp | 15 +- src/ripple/app/main/ServerHandlerImp.h | 2 +- src/ripple/http/Server.h | 91 +++-- src/ripple/http/impl/Door.cpp | 205 +++++++---- src/ripple/http/impl/Door.h | 99 ++++-- src/ripple/http/impl/Peer.h | 269 +++------------ src/ripple/http/impl/PlainPeer.h | 104 ++++++ src/ripple/http/impl/Port.cpp | 56 --- src/ripple/http/impl/SSLPeer.h | 123 +++++++ src/ripple/http/impl/Server.cpp | 66 ++-- src/ripple/http/impl/ServerImpl.cpp | 319 +++++------------- src/ripple/http/impl/ServerImpl.h | 88 ++--- src/ripple/http/tests/Server.test.cpp | 16 +- 15 files changed, 699 insertions(+), 764 deletions(-) create mode 100644 src/ripple/http/impl/PlainPeer.h create mode 100644 src/ripple/http/impl/SSLPeer.h diff --git a/Builds/VisualStudio2013/RippleD.vcxproj b/Builds/VisualStudio2013/RippleD.vcxproj index 47ce73943d..0843a149df 100644 --- a/Builds/VisualStudio2013/RippleD.vcxproj +++ b/Builds/VisualStudio2013/RippleD.vcxproj @@ -2375,6 +2375,8 @@ + + True @@ -2386,6 +2388,8 @@ + + diff --git a/Builds/VisualStudio2013/RippleD.vcxproj.filters b/Builds/VisualStudio2013/RippleD.vcxproj.filters index d4c7d45054..d59e3165b6 100644 --- a/Builds/VisualStudio2013/RippleD.vcxproj.filters +++ b/Builds/VisualStudio2013/RippleD.vcxproj.filters @@ -3381,6 +3381,9 @@ ripple\http\impl + + ripple\http\impl + ripple\http\impl @@ -3393,6 +3396,9 @@ ripple\http\impl + + ripple\http\impl + ripple\http\impl diff --git a/src/ripple/app/main/ServerHandlerImp.cpp b/src/ripple/app/main/ServerHandlerImp.cpp index 2ca7232f9d..6f87e4593d 100644 --- a/src/ripple/app/main/ServerHandlerImp.cpp +++ b/src/ripple/app/main/ServerHandlerImp.cpp @@ -37,7 +37,8 @@ ServerHandlerImp::ServerHandlerImp (Stoppable& parent, JobQueue& jobQueue, , m_journal (deprecatedLogs().journal("Server")) , m_jobQueue (jobQueue) , m_networkOPs (networkOPs) - , m_server (*this, deprecatedLogs().journal("Server")) + , m_server (HTTP::make_Server( + *this, deprecatedLogs().journal("Server"))) , setup_ (setup) { if (setup_.secure) @@ -49,7 +50,7 @@ ServerHandlerImp::ServerHandlerImp (Stoppable& parent, JobQueue& jobQueue, ServerHandlerImp::~ServerHandlerImp() { - m_server.stop(); + m_server = nullptr; } void @@ -77,9 +78,9 @@ ServerHandlerImp::setup (beast::Journal journal) port.port = ep.port(); port.context = m_context.get (); - HTTP::Ports ports; - ports.push_back (port); - m_server.setPorts (ports); + std::vector list; + list.push_back (port); + m_server->ports(list); } } else @@ -93,7 +94,7 @@ ServerHandlerImp::setup (beast::Journal journal) void ServerHandlerImp::onStop() { - m_server.stopAsync(); + m_server->close(); } //-------------------------------------------------------------------------- @@ -256,7 +257,7 @@ ServerHandlerImp::processRequest (std::string const& request, void ServerHandlerImp::onWrite (beast::PropertyStream::Map& map) { - m_server.onWrite (map); + m_server->onWrite (map); } //------------------------------------------------------------------------------ diff --git a/src/ripple/app/main/ServerHandlerImp.h b/src/ripple/app/main/ServerHandlerImp.h index b4a72f931f..9d698eb7b8 100644 --- a/src/ripple/app/main/ServerHandlerImp.h +++ b/src/ripple/app/main/ServerHandlerImp.h @@ -38,7 +38,7 @@ private: beast::Journal m_journal; JobQueue& m_jobQueue; NetworkOPs& m_networkOPs; - HTTP::Server m_server; + std::unique_ptr m_server; std::unique_ptr m_context; RPC::Setup setup_; diff --git a/src/ripple/http/Server.h b/src/ripple/http/Server.h index b1a0cc13cb..571cfaabcf 100644 --- a/src/ripple/http/Server.h +++ b/src/ripple/http/Server.h @@ -20,6 +20,7 @@ #ifndef RIPPLE_HTTP_SERVER_H_INCLUDED #define RIPPLE_HTTP_SERVER_H_INCLUDED +#include #include #include #include @@ -44,24 +45,20 @@ struct Port require_ssl }; - Security security; - std::uint16_t port; + Security security = Security::no_ssl; + std::uint16_t port = 0; beast::IP::Endpoint addr; - SSLContext* context; + SSLContext* context = nullptr; - Port (); - Port (Port const& other); - Port& operator= (Port const& other); + Port() = default; Port (std::uint16_t port_, beast::IP::Endpoint const& addr_, - Security security_, SSLContext* context_); + Security security_, SSLContext* context_); + + static + void + parse (Port& result, Section const& section, std::ostream& log); }; -bool operator== (Port const& lhs, Port const& rhs); -bool operator< (Port const& lhs, Port const& rhs); - -/** A set of listening ports settings. */ -typedef std::vector Ports; - //------------------------------------------------------------------------------ class Server; @@ -92,65 +89,57 @@ struct Handler //------------------------------------------------------------------------------ -class ServerImpl; - /** Multi-threaded, asynchronous HTTP server. */ class Server { public: - /** Create the server using the specified handler. */ - Server (Handler& handler, beast::Journal journal); - /** Destroy the server. - This blocks until the server stops. + The server is closed if it is not already closed. This call + blocks until the server has stopped. */ virtual - ~Server (); + ~Server() = default; /** Returns the Journal associated with the server. */ + virtual beast::Journal - journal () const; + journal() = 0; - /** Returns the listening ports settings. - Thread safety: - Safe to call from any thread. - Cannot be called concurrently with setPorts. - */ - Ports const& - getPorts () const; - - /** Set the listening ports settings. - These take effect immediately. Any current ports that are not in the - new set will be closed. Established connections will not be disturbed. - Thread safety: - Cannot be called concurrently. + /** Set the listening port settings. + This may only be called once. */ + virtual void - setPorts (Ports const& ports); + ports (std::vector const& v) = 0; - /** Notify the server to stop, without blocking. + virtual + void + onWrite (beast::PropertyStream::Map& map) = 0; + + /** Close the server. + The close is performed asynchronously. The handler will be notified + when the server has stopped. The server is considered stopped when + there are no pending I/O completion handlers and all connections + have closed. Thread safety: Safe to call concurrently from any thread. */ + virtual void - stopAsync (); + close() = 0; - /** Notify the server to stop, and block until the stop is complete. - The handler's onStopped method will be called when the stop completes. - Thread safety: - Cannot be called concurrently. - Cannot be called from the thread of execution of any Handler functions. - */ - void - stop (); - - void - onWrite (beast::PropertyStream::Map& map); - -private: - std::unique_ptr m_impl; + /** Parse configuration settings into a list of ports. */ + static + std::vector + parse (BasicConfig const& config, std::ostream& log); }; +/** Create the HTTP server using the specified handler. */ +std::unique_ptr +make_Server (Handler& handler, beast::Journal journal); + +//------------------------------------------------------------------------------ + } // HTTP } // ripple diff --git a/src/ripple/http/impl/Door.cpp b/src/ripple/http/impl/Door.cpp index ede98d6807..573dd07d16 100644 --- a/src/ripple/http/impl/Door.cpp +++ b/src/ripple/http/impl/Door.cpp @@ -18,15 +18,13 @@ //============================================================================== #include -#include +#include +#include #include #include #include -#include #include -#include - namespace ripple { namespace HTTP { @@ -83,123 +81,198 @@ detect_ssl (Socket& socket, StreamBuf& buf, Yield yield) //------------------------------------------------------------------------------ -Door::connection::connection (Door& door, socket_type&& socket, +Door::detector::detector (Door& door, socket_type&& socket, endpoint_type endpoint) : door_ (door) , socket_ (std::move(socket)) - , endpoint_ (endpoint) - , strand_ (door.io_service_) - , timer_ (door.io_service_) + , timer_ (socket_.get_io_service()) + , remote_endpoint_ (endpoint) { + door_.add(*this); } -// Work-around because we can't call shared_from_this in ctor -void -Door::connection::run() +Door::detector::~detector() { - boost::asio::spawn (strand_, std::bind (&connection::do_detect, + door_.remove(*this); +} + +void +Door::detector::run() +{ + boost::asio::spawn (door_.strand_, std::bind (&detector::do_detect, shared_from_this(), std::placeholders::_1)); - boost::asio::spawn (strand_, std::bind (&connection::do_timer, + boost::asio::spawn (door_.strand_, std::bind (&detector::do_timer, shared_from_this(), std::placeholders::_1)); } void -Door::connection::do_timer (yield_context yield) +Door::detector::close() +{ + error_code ec; + socket_.close(ec); + timer_.cancel(ec); +} + +void +Door::detector::do_timer (yield_context yield) { error_code ec; // ignored while (socket_.is_open()) { timer_.async_wait (yield[ec]); if (timer_.expires_from_now() <= std::chrono::seconds(0)) - socket_.close(); + socket_.close(ec); } } void -Door::connection::do_detect (boost::asio::yield_context yield) +Door::detector::do_detect (boost::asio::yield_context yield) { bool ssl; error_code ec; - boost::asio::streambuf buf; - timer_.expires_from_now (std::chrono::seconds(15)); - std::tie(ec, ssl) = detect_ssl (socket_, buf, yield); + beast::asio::streambuf buf(16); + timer_.expires_from_now(std::chrono::seconds(15)); + std::tie(ec, ssl) = detect_ssl(socket_, buf, yield); + error_code unused; + timer_.cancel(unused); if (! ec) - { - if (ssl) - { - auto const peer = std::make_shared (door_.server_, - door_.port_, door_.server_.journal(), endpoint_, - buf.data(), std::move(socket_)); - peer->accept(); - return; - } - - auto const peer = std::make_shared (door_.server_, - door_.port_, door_.server_.journal(), endpoint_, - buf.data(), std::move(socket_)); - peer->accept(); - return; - } - - socket_.close(); - timer_.cancel(); + return door_.create(ssl, std::move(buf), + std::move(socket_), remote_endpoint_); + if (ec != boost::asio::error::operation_aborted) + if (door_.server_.journal().trace) door_.server_.journal().trace << + "Error detecting ssl: " << ec.message() << + " from " << remote_endpoint_; } //------------------------------------------------------------------------------ Door::Door (boost::asio::io_service& io_service, - ServerImpl& impl, Port const& port) - : io_service_ (io_service) - , timer_ (io_service) - , acceptor_ (io_service, to_asio (port)) - , port_ (port) - , server_ (impl) + ServerImpl& server, Port const& port) + : port_(port) + , server_(server) + , acceptor_(io_service, to_asio(port), true) + , strand_(io_service) { server_.add (*this); error_code ec; - acceptor_.set_option (acceptor::reuse_address (true), ec); if (ec) { - server_.journal().error << + if (server_.journal().error) server_.journal().error << "Error setting acceptor socket option: " << ec.message(); } if (! ec) { - server_.journal().info << "Bound to endpoint " << - to_string (acceptor_.local_endpoint()); + if (server_.journal().info) server_.journal().info << + "Bound to endpoint " << to_string (acceptor_.local_endpoint()); } else { - server_.journal().error << "Error binding to endpoint " << - to_string (acceptor_.local_endpoint()) << - ", '" << ec.message() << "'"; + if (server_.journal().error) server_.journal().error << + "Error binding to endpoint " << + to_string (acceptor_.local_endpoint()) << + ", '" << ec.message() << "'"; } } -Door::~Door () +Door::~Door() { + { + // Block until all detector, Peer objects destroyed + std::unique_lock lock(mutex_); + while (! list_.empty()) + cond_.wait(lock); + } server_.remove (*this); } void -Door::listen() +Door::run() { - boost::asio::spawn (io_service_, std::bind (&Door::do_accept, + boost::asio::spawn (strand_, std::bind (&Door::do_accept, shared_from_this(), std::placeholders::_1)); } void -Door::cancel () +Door::close() { - acceptor_.cancel(); + if (! strand_.running_in_this_thread()) + return strand_.post(std::bind( + &Door::close, shared_from_this())); + error_code ec; + acceptor_.close(ec); + // Close all detector, Peer objects + for(auto& _ : list_) + _.close(); } //------------------------------------------------------------------------------ +void +Door::add (Child& c) +{ + std::lock_guard lock(mutex_); + list_.push_back(c); +} + +void +Door::remove (Child& c) +{ + std::lock_guard lock(mutex_); + list_.erase(list_.iterator_to(c)); + if (list_.empty()) + cond_.notify_all(); +} + +void +Door::create (bool ssl, beast::asio::streambuf&& buf, + socket_type&& socket, endpoint_type remote_address) +{ + if (server_.closed()) + return; + error_code ec; + switch (port_.security) + { + case Port::Security::no_ssl: + if (ssl) + ec = boost::system::errc::make_error_code ( + boost::system::errc::invalid_argument); + + case Port::Security::require_ssl: + if (! ssl) + ec = boost::system::errc::make_error_code ( + boost::system::errc::invalid_argument); + + case Port::Security::allow_ssl: + if (! ec) + { + if (ssl) + { + auto const peer = std::make_shared (*this, + server_.journal(), remote_address, buf.data(), + std::move(socket)); + peer->accept(); + return; + } + + auto const peer = std::make_shared (*this, + server_.journal(), remote_address, buf.data(), + std::move(socket)); + peer->accept(); + return; + } + break; + } + + if (ec) + if (server_.journal().trace) server_.journal().trace << + "Error detecting ssl: " << ec.message() << + " from " << remote_address; +} + void Door::do_accept (boost::asio::yield_context yield) { @@ -207,33 +280,35 @@ Door::do_accept (boost::asio::yield_context yield) { error_code ec; endpoint_type endpoint; - socket_type socket (io_service_); + socket_type socket (acceptor_.get_io_service()); acceptor_.async_accept (socket, endpoint, yield[ec]); + if (server_.closed()) + break; if (ec) { if (ec != boost::asio::error::operation_aborted) - server_.journal().error << + if (server_.journal().error) server_.journal().error << "accept: " << ec.message(); break; } if (port_.security == Port::Security::no_ssl) { - auto const peer = std::make_shared (server_, - port_, server_.journal(), endpoint, - boost::asio::null_buffers(), std::move(socket)); + auto const peer = std::make_shared (*this, + server_.journal(), endpoint, boost::asio::null_buffers(), + std::move(socket)); peer->accept(); } else if (port_.security == Port::Security::require_ssl) { - auto const peer = std::make_shared (server_, - port_, server_.journal(), endpoint, - boost::asio::null_buffers(), std::move(socket)); + auto const peer = std::make_shared (*this, + server_.journal(), endpoint, boost::asio::null_buffers(), + std::move(socket)); peer->accept(); } else { - auto const c = std::make_shared ( + auto const c = std::make_shared ( *this, std::move(socket), endpoint); c->run(); } diff --git a/src/ripple/http/impl/Door.h b/src/ripple/http/impl/Door.h index 96d4ef23fc..e25d5b20fe 100644 --- a/src/ripple/http/impl/Door.h +++ b/src/ripple/http/impl/Door.h @@ -22,19 +22,23 @@ #include #include +#include #include #include #include #include +#include #include +#include #include +#include namespace ripple { namespace HTTP { /** A listening socket. */ class Door - : public beast::List ::Node + : public ServerImpl::Child , public std::enable_shared_from_this { private: @@ -47,17 +51,58 @@ private: using endpoint_type = protocol_type::endpoint; using socket_type = protocol_type::socket; - boost::asio::io_service& io_service_; - boost::asio::basic_waitable_timer timer_; - acceptor_type acceptor_; + // Detects SSL on a socket + class detector + : public std::enable_shared_from_this + , public ServerImpl::Child + { + private: + Door& door_; + socket_type socket_; + timer_type timer_; + endpoint_type remote_endpoint_; + + public: + detector (Door& door, socket_type&& socket, + endpoint_type endpoint); + + ~detector(); + + void run(); + void close(); + + private: + void do_timer (yield_context yield); + void do_detect (yield_context yield); + }; + + using list_type = boost::intrusive::make_list >::type; + Port port_; ServerImpl& server_; + acceptor_type acceptor_; + boost::asio::io_service::strand strand_; + std::mutex mutex_; + std::condition_variable cond_; + list_type list_; public: Door (boost::asio::io_service& io_service, - ServerImpl& impl, Port const& port); + ServerImpl& server, Port const& port); - ~Door (); + /** Destroy the door. + Blocks until there are no pending I/O completion + handlers, and all connections have been destroyed. + close() must be called before the destructor. + */ + ~Door(); + + ServerImpl& + server() + { + return server_; + } Port const& port() const @@ -65,34 +110,24 @@ public: return port_; } - void listen(); - void cancel(); + // Work-around because we can't call shared_from_this in ctor + void run(); + + void add (Child& c); + + void remove (Child& c); + + /** Close the Door listening socket and connections. + The listening socket is closed, and all open connections + belonging to the Door are closed. + Thread Safety: + May be called concurrently + */ + void close(); private: - class connection - : public std::enable_shared_from_this - { - private: - Door& door_; - socket_type socket_; - endpoint_type endpoint_; - boost::asio::io_service::strand strand_; - timer_type timer_; - - public: - connection (Door& door, socket_type&& socket, - endpoint_type endpoint); - - void - run(); - - private: - void - do_timer (yield_context yield); - - void - do_detect (yield_context yield); - }; + void create (bool ssl, beast::asio::streambuf&& buf, + socket_type&& socket, endpoint_type remote_address); void do_accept (yield_context yield); }; diff --git a/src/ripple/http/impl/Peer.h b/src/ripple/http/impl/Peer.h index 112a0229cf..0ee6f4803e 100644 --- a/src/ripple/http/impl/Peer.h +++ b/src/ripple/http/impl/Peer.h @@ -28,7 +28,7 @@ #include // for is_short_read? #include #include -#include +#include #include #include #include @@ -45,21 +45,10 @@ namespace ripple { namespace HTTP { -//------------------------------------------------------------------------------ - -class BasicPeer - : public beast::List ::Node -{ -public: - virtual ~BasicPeer() = default; -}; - -//------------------------------------------------------------------------------ - /** Represents an active connection. */ template class Peer - : public BasicPeer + : public ServerImpl::Child , public Session { protected: @@ -92,11 +81,12 @@ protected: std::size_t used; }; - beast::Journal journal_; - ServerImpl& server_; + Door& door_; + boost::asio::io_service::work work_; boost::asio::io_service::strand strand_; waitable_timer timer_; endpoint_type endpoint_; + beast::Journal journal_; std::string id_; std::size_t nid_; @@ -109,7 +99,6 @@ protected: bool graceful_ = false; bool complete_ = false; std::shared_ptr detach_ref_; - boost::optional work_; boost::system::error_code ec_; clock_type::time_point when_; @@ -122,8 +111,9 @@ protected: public: template - Peer (ServerImpl& impl, Port const& port, beast::Journal journal, - endpoint_type endpoint, ConstBufferSequence const& buffers); + Peer (Door& door, boost::asio::io_service& io_service, + beast::Journal journal, endpoint_type endpoint, + ConstBufferSequence const& buffers); virtual ~Peer(); @@ -134,6 +124,8 @@ public: return *this; } + void close() override; + protected: Impl& impl() @@ -172,7 +164,7 @@ protected: beast::Journal journal() override { - return server_.journal(); + return door_.server().journal(); } beast::IP::Endpoint @@ -208,198 +200,26 @@ protected: //------------------------------------------------------------------------------ -class PlainPeer - : public Peer - , public std::enable_shared_from_this -{ -private: - friend class Peer ; - using socket_type = boost::asio::ip::tcp::socket; - socket_type socket_; - -public: - template - PlainPeer (ServerImpl& impl, Port const& port, beast::Journal journal, - endpoint_type endpoint, ConstBufferSequence const& buffers, - socket_type&& socket); - - void - accept(); - -private: - void - do_request(); - - void - do_close(); -}; - -template -PlainPeer::PlainPeer (ServerImpl& server, Port const& port, - beast::Journal journal, endpoint_type endpoint, - ConstBufferSequence const& buffers, - boost::asio::ip::tcp::socket&& socket) - : Peer (server, port, journal, endpoint, buffers) - , socket_(std::move(socket)) -{ -} - -void -PlainPeer::accept () -{ - server_.handler().onAccept (session()); - if (! socket_.is_open()) - return; - - boost::asio::spawn (strand_, std::bind (&PlainPeer::do_read, - shared_from_this(), std::placeholders::_1)); -} - -void -PlainPeer::do_request() -{ - // Perform half-close when Connection: close and not SSL - error_code ec; - if (! message_.keep_alive()) - socket_.shutdown (socket_type::shutdown_receive, ec); - - if (! ec) - { - ++request_count_; - server_.handler().onRequest (session()); - return; - } - - if (ec) - fail (ec, "request"); -} - -void -PlainPeer::do_close() -{ - error_code ec; - socket_.shutdown (socket_type::shutdown_send, ec); -} - -//------------------------------------------------------------------------------ - -class SSLPeer - : public Peer - , public std::enable_shared_from_this -{ -private: - friend class Peer ; - using next_layer_type = boost::asio::ip::tcp::socket; - using socket_type = boost::asio::ssl::stream ; - next_layer_type next_layer_; - socket_type socket_; - -public: - template - SSLPeer (ServerImpl& impl, Port const& port, beast::Journal journal, - endpoint_type endpoint, ConstBufferSequence const& buffers, - next_layer_type&& socket); - - void - accept(); - -private: - void - do_handshake (boost::asio::yield_context yield); - - void - do_request(); - - void - do_close(); - - void - on_shutdown (error_code ec); -}; - -template -SSLPeer::SSLPeer (ServerImpl& server, Port const& port, - beast::Journal journal, endpoint_type endpoint, - ConstBufferSequence const& buffers, - boost::asio::ip::tcp::socket&& socket) - : Peer (server, port, journal, endpoint, buffers) - , next_layer_ (std::move(socket)) - , socket_ (next_layer_, port.context->get()) -{ -} - -// Called when the acceptor accepts our socket. -void -SSLPeer::accept () -{ - server_.handler().onAccept (session()); - if (! next_layer_.is_open()) - return; - - boost::asio::spawn (strand_, std::bind (&SSLPeer::do_handshake, - shared_from_this(), std::placeholders::_1)); -} - -void -SSLPeer::do_handshake (boost::asio::yield_context yield) -{ - error_code ec; - std::size_t const bytes_transferred = socket_.async_handshake ( - socket_type::server, read_buf_.data(), yield[ec]); - if (ec) - return fail (ec, "handshake"); - read_buf_.consume (bytes_transferred); - boost::asio::spawn (strand_, std::bind (&SSLPeer::do_read, - shared_from_this(), std::placeholders::_1)); -} - -void -SSLPeer::do_request() -{ - ++request_count_; - server_.handler().onRequest (session()); -} - -void -SSLPeer::do_close() -{ - error_code ec; - socket_.async_shutdown (strand_.wrap (std::bind ( - &SSLPeer::on_shutdown, shared_from_this(), - std::placeholders::_1))); -} - -void -SSLPeer::on_shutdown (error_code ec) -{ - socket_.next_layer().close(ec); -} - -//------------------------------------------------------------------------------ - template template -Peer::Peer (ServerImpl& server, Port const& port, - beast::Journal journal, endpoint_type endpoint, - ConstBufferSequence const& buffers) - : journal_ (journal) - , server_ (server) - , strand_ (server_.get_io_service()) - , timer_ (server_.get_io_service()) +Peer::Peer (Door& door, boost::asio::io_service& io_service, + beast::Journal journal, endpoint_type endpoint, + ConstBufferSequence const& buffers) + : door_(door) + , work_ (io_service) + , strand_ (io_service) + , timer_ (io_service) , endpoint_ (endpoint) + , journal_ (journal) { - read_buf_.commit (boost::asio::buffer_copy (read_buf_.prepare ( + door_.add(*this); + read_buf_.commit(boost::asio::buffer_copy(read_buf_.prepare ( boost::asio::buffer_size (buffers)), buffers)); - static std::atomic sid; nid_ = ++sid; id_ = std::string("#") + std::to_string(nid_) + " "; - - server_.add (*this); - if (journal_.trace) journal_.trace << id_ << "accept: " << endpoint.address(); - when_ = clock_type::now(); when_str_ = beast::Time::getCurrentTime().formatted ( "%Y-%b-%d %H:%M:%S").toStdString(); @@ -417,15 +237,25 @@ Peer::~Peer() stat.bytes_in = bytes_in_; stat.bytes_out = bytes_out_; stat.ec = std::move (ec_); - server_.report (std::move (stat)); - - server_.handler().onClose (session(), ec_); - - server_.remove (*this); - + door_.server().report (std::move (stat)); + door_.server().handler().onClose (session(), ec_); if (journal_.trace) journal_.trace << id_ << "destroyed: " << request_count_ << ((request_count_ == 1) ? " request" : " requests"); + door_.remove (*this); +} + +template +void +Peer::close() +{ + if (! strand_.running_in_this_thread()) + return strand_.post(std::bind( + (void(Peer::*)(void))&Peer::close, + impl().shared_from_this())); + error_code ec; + timer_.cancel(ec); + impl().socket_.lowest_layer().close(ec); } //------------------------------------------------------------------------------ @@ -625,20 +455,9 @@ template void Peer::detach () { + // Maintain an additional reference while detached if (! detach_ref_) - { - assert (! work_); - - // Maintain an additional reference while detached detach_ref_ = impl().shared_from_this(); - - // Prevent the io_service from running out of work. - // The work object will be destroyed with the Peer - // after the Session is closed and handlers complete. - // - work_ = boost::in_place (std::ref ( - server_.get_io_service())); - } } // Called to indicate the response has been written (but not sent) @@ -647,12 +466,11 @@ void Peer::complete() { if (! strand_.running_in_this_thread()) - return server_.get_io_service().dispatch (strand_.wrap ( - std::bind (&Peer::complete, impl().shared_from_this()))); + return strand_.post(std::bind (&Peer::complete, + impl().shared_from_this())); // Reattach detach_ref_.reset(); - work_ = boost::none; message_ = beast::http::message{}; complete_ = true; @@ -671,13 +489,12 @@ void Peer::close (bool graceful) { if (! strand_.running_in_this_thread()) - return server_.get_io_service().dispatch (strand_.wrap ( - std::bind (&Peer::close, impl().shared_from_this(), - graceful))); + return strand_.post(std::bind( + (void(Peer::*)(bool))&Peer::close, + impl().shared_from_this(), graceful)); // Reattach detach_ref_.reset(); - work_ = boost::none; complete_ = true; diff --git a/src/ripple/http/impl/PlainPeer.h b/src/ripple/http/impl/PlainPeer.h new file mode 100644 index 0000000000..a551e010f3 --- /dev/null +++ b/src/ripple/http/impl/PlainPeer.h @@ -0,0 +1,104 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2013 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef RIPPLE_HTTP_PLAINPEER_H_INCLUDED +#define RIPPLE_HTTP_PLAINPEER_H_INCLUDED + +#include + +namespace ripple { +namespace HTTP { + +class PlainPeer + : public Peer + , public std::enable_shared_from_this +{ +private: + friend class Peer ; + using socket_type = boost::asio::ip::tcp::socket; + socket_type socket_; + +public: + template + PlainPeer (Door& door, beast::Journal journal, endpoint_type endpoint, + ConstBufferSequence const& buffers, socket_type&& socket); + + void + accept(); + +private: + void + do_request(); + + void + do_close(); +}; + +//------------------------------------------------------------------------------ + +template +PlainPeer::PlainPeer (Door& door, beast::Journal journal, + endpoint_type endpoint, ConstBufferSequence const& buffers, + boost::asio::ip::tcp::socket&& socket) + : Peer (door, socket.get_io_service(), journal, endpoint, buffers) + , socket_(std::move(socket)) +{ +} + +void +PlainPeer::accept () +{ + door_.server().handler().onAccept (session()); + if (! socket_.is_open()) + return; + + boost::asio::spawn (strand_, std::bind (&PlainPeer::do_read, + shared_from_this(), std::placeholders::_1)); +} + +void +PlainPeer::do_request() +{ + // Perform half-close when Connection: close and not SSL + error_code ec; + if (! message_.keep_alive()) + socket_.shutdown (socket_type::shutdown_receive, ec); + + if (! ec) + { + ++request_count_; + door_.server().handler().onRequest (session()); + return; + } + + if (ec) + fail (ec, "request"); +} + +void +PlainPeer::do_close() +{ + error_code ec; + socket_.shutdown (socket_type::shutdown_send, ec); +} + +} +} + +#endif diff --git a/src/ripple/http/impl/Port.cpp b/src/ripple/http/impl/Port.cpp index e9993a7077..0213bfc391 100644 --- a/src/ripple/http/impl/Port.cpp +++ b/src/ripple/http/impl/Port.cpp @@ -20,30 +20,6 @@ namespace ripple { namespace HTTP { -Port::Port () - : security (Security::no_ssl) - , port (0) - , context (nullptr) -{ -} - -Port::Port (Port const& other) - : security (other.security) - , port (other.port) - , addr (other.addr) - , context (other.context) -{ -} - -Port& Port::operator= (Port const& other) -{ - port = other.port; - addr = other.addr; - security = other.security; - context = other.context; - return *this; -} - Port::Port (std::uint16_t port_, beast::IP::Endpoint const& addr_, Security security_, SSLContext* context_) : security (security_) @@ -53,37 +29,5 @@ Port::Port (std::uint16_t port_, beast::IP::Endpoint const& addr_, { } -bool operator== (Port const& lhs, Port const& rhs) -{ - if (lhs.addr != rhs.addr) - return false; - if (lhs.port != rhs.port) - return false; - if (lhs.security != rhs.security) - return false; - // 'context' does not participate in the comparison - return true; -} - -bool operator< (Port const& lhs, Port const& rhs) -{ - if (lhs.addr > rhs.addr) - return false; - else if (lhs.addr < rhs.addr) - return true; - - if (lhs.port > rhs.port) - return false; - else if (lhs.port < rhs.port) - return true; - - if (lhs.security > rhs.security) - return false; - else if (lhs.security < rhs.security) - return true; - - return true; -} - } } diff --git a/src/ripple/http/impl/SSLPeer.h b/src/ripple/http/impl/SSLPeer.h new file mode 100644 index 0000000000..895a811b82 --- /dev/null +++ b/src/ripple/http/impl/SSLPeer.h @@ -0,0 +1,123 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2013 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef RIPPLE_HTTP_SSLPEER_H_INCLUDED +#define RIPPLE_HTTP_SSLPEER_H_INCLUDED + +#include + +namespace ripple { +namespace HTTP { + +class SSLPeer + : public Peer + , public std::enable_shared_from_this +{ +private: + friend class Peer ; + using next_layer_type = boost::asio::ip::tcp::socket; + using socket_type = boost::asio::ssl::stream ; + next_layer_type next_layer_; + socket_type socket_; + +public: + template + SSLPeer (Door& door, beast::Journal journal, endpoint_type endpoint, + ConstBufferSequence const& buffers, next_layer_type&& socket); + + void + accept(); + +private: + void + do_handshake (boost::asio::yield_context yield); + + void + do_request(); + + void + do_close(); + + void + on_shutdown (error_code ec); +}; + +//------------------------------------------------------------------------------ + +template +SSLPeer::SSLPeer (Door& door, beast::Journal journal, + endpoint_type endpoint, ConstBufferSequence const& buffers, + boost::asio::ip::tcp::socket&& socket) + : Peer (door, socket.get_io_service(), journal, endpoint, buffers) + , next_layer_ (std::move(socket)) + , socket_ (next_layer_, door_.port().context->get()) +{ +} + +// Called when the acceptor accepts our socket. +void +SSLPeer::accept () +{ + door_.server().handler().onAccept (session()); + if (! next_layer_.is_open()) + return; + + boost::asio::spawn (strand_, std::bind (&SSLPeer::do_handshake, + shared_from_this(), std::placeholders::_1)); +} + +void +SSLPeer::do_handshake (boost::asio::yield_context yield) +{ + error_code ec; + std::size_t const bytes_transferred = socket_.async_handshake ( + socket_type::server, read_buf_.data(), yield[ec]); + if (ec) + return fail (ec, "handshake"); + read_buf_.consume (bytes_transferred); + boost::asio::spawn (strand_, std::bind (&SSLPeer::do_read, + shared_from_this(), std::placeholders::_1)); +} + +void +SSLPeer::do_request() +{ + ++request_count_; + door_.server().handler().onRequest (session()); +} + +void +SSLPeer::do_close() +{ + error_code ec; + socket_.async_shutdown (strand_.wrap (std::bind ( + &SSLPeer::on_shutdown, shared_from_this(), + std::placeholders::_1))); +} + +void +SSLPeer::on_shutdown (error_code ec) +{ + socket_.next_layer().close(ec); +} + +} +} + +#endif diff --git a/src/ripple/http/impl/Server.cpp b/src/ripple/http/impl/Server.cpp index 70b6efa1f3..81e8cfd73b 100644 --- a/src/ripple/http/impl/Server.cpp +++ b/src/ripple/http/impl/Server.cpp @@ -20,54 +20,54 @@ #include #include #include // +#include namespace ripple { namespace HTTP { -Server::Server (Handler& handler, beast::Journal journal) - : m_impl (std::make_unique (*this, handler, journal)) +std::unique_ptr +make_Server (Handler& handler, beast::Journal journal) { + return std::make_unique(handler, journal); } -Server::~Server () -{ - stop(); -} - -beast::Journal -Server::journal () const -{ - return m_impl->journal(); -} - -Ports const& -Server::getPorts () const -{ - return m_impl->getPorts(); -} +//------------------------------------------------------------------------------ void -Server::setPorts (Ports const& ports) +Port::parse (Port& port, + Section const& section, std::ostream& log) { - m_impl->setPorts (ports); } -void -Server::stopAsync () +std::vector +Server::parse (BasicConfig const& config, std::ostream& log) { - m_impl->stop(false); -} + std::vector result; -void -Server::stop () -{ - m_impl->stop(true); -} + if (! config.exists("doors")) + { + log << + "Missing section: [doors]\n"; + return result; + } -void -Server::onWrite (beast::PropertyStream::Map& map) -{ - m_impl->onWrite (map); + Port common; + Port::parse (common, config["doors"], log); + + auto const& names = config.section("doors").values(); + for (auto const& name : names) + { + if (! config.exists(name)) + { + log << + "Missing section: [" << name << "]\n"; + //throw std:: + } + result.push_back(common); + Port::parse (result.back(), config[name], log); + } + + return result; } } diff --git a/src/ripple/http/impl/ServerImpl.cpp b/src/ripple/http/impl/ServerImpl.cpp index 66c282677b..9b0331a92d 100644 --- a/src/ripple/http/impl/ServerImpl.cpp +++ b/src/ripple/http/impl/ServerImpl.cpp @@ -18,8 +18,10 @@ //============================================================================== #include +#include #include #include +#include #include #include #include @@ -30,110 +32,129 @@ namespace ripple { namespace HTTP { -ServerImpl::ServerImpl (Server& server, - Handler& handler, beast::Journal journal) - : m_server (server) - , m_handler (handler) +ServerImpl::ServerImpl (Handler& handler, beast::Journal journal) + : handler_ (handler) , journal_ (journal) - , m_strand (io_service_) - , m_work (boost::in_place (std::ref (io_service_))) - , m_stopped (true) + , strand_ (io_service_) + , work_ (boost::in_place (std::ref (io_service_))) , hist_{} { thread_ = std::thread (std::bind ( &ServerImpl::run, this)); } -ServerImpl::~ServerImpl () +ServerImpl::~ServerImpl() { + close(); + { + // Block until all Door objects destroyed + std::unique_lock lock(mutex_); + while (! list_.empty()) + cond_.wait(lock); + } thread_.join(); } -Ports const& -ServerImpl::getPorts () const +void +ServerImpl::ports (std::vector const& ports) { - std::lock_guard lock (mutex_); - return state_.ports; + if (closed()) + throw std::logic_error("ports() on closed HTTP::Server"); + for(auto const& _ : ports) + std::make_shared( + io_service_, *this, _)->run(); } void -ServerImpl::setPorts (Ports const& ports) +ServerImpl::onWrite (beast::PropertyStream::Map& map) { std::lock_guard lock (mutex_); - state_.ports = ports; - update(); -} - -bool -ServerImpl::stopping () const -{ - return ! m_work; -} - -void -ServerImpl::stop (bool wait) -{ - if (! stopping()) + map ["active"] = list_.size(); { - m_work = boost::none; - update(); + std::string s; + for (int i = 0; i <= high_; ++i) + { + if (i) + s += ", "; + s += std::to_string (hist_[i]); + } + map ["hist"] = s; } + { + beast::PropertyStream::Set set ("history", map); + for (auto const& stat : stats_) + { + beast::PropertyStream::Map item (set); - if (wait) - m_stopped.wait(); + item ["id"] = stat.id; + item ["when"] = stat.when; + + { + std::stringstream ss; + ss << stat.elapsed; + item ["elapsed"] = ss.str(); + } + + item ["requests"] = stat.requests; + item ["bytes_in"] = stat.bytes_in; + item ["bytes_out"] = stat.bytes_out; + if (stat.ec) + item ["error"] = stat.ec.message(); + } + } +} + +void +ServerImpl::close() +{ + std::lock_guard lock(mutex_); + if (work_) + { + work_ = boost::none; + // Close all Door objects + for(auto& _ :list_) + _.close(); + } } //-------------------------------------------------------------------------- -// -// Server -// -Handler& -ServerImpl::handler() -{ - return m_handler; -} - -boost::asio::io_service& -ServerImpl::get_io_service() -{ - return io_service_; -} - -// Inserts the peer into our list of peers. We only remove it -// from the list inside the destructor of the Peer object. This -// way, the Peer can never outlive the server. -// void -ServerImpl::add (BasicPeer& peer) +ServerImpl::add (Child& child) { - std::lock_guard lock (mutex_); - state_.peers.push_back (peer); + std::lock_guard lock(mutex_); + list_.push_back(child); } void -ServerImpl::add (Door& door) +ServerImpl::remove (Child& child) { - std::lock_guard lock (mutex_); - state_.doors.push_back (door); + std::lock_guard lock(mutex_); + list_.erase(list_.iterator_to(child)); + if (list_.empty()) + { + handler_.onStopped(*this); + cond_.notify_all(); + } } -// Removes the peer from our list of peers. This is only called from -// the destructor of Peer. Essentially, the item in the list functions -// as a weak_ptr. -// -void -ServerImpl::remove (BasicPeer& peer) +bool +ServerImpl::closed() { - std::lock_guard lock (mutex_); - state_.peers.erase (state_.peers.iterator_to (peer)); + std::lock_guard lock(mutex_); + return ! work_; } void -ServerImpl::remove (Door& door) +ServerImpl::report (Stat&& stat) { + int const bucket = ceil_log2 (stat.requests); std::lock_guard lock (mutex_); - state_.doors.erase (state_.doors.iterator_to (door)); + ++hist_[bucket]; + high_ = std::max (high_, bucket); + if (stats_.size() >= historySize) + stats_.pop_back(); + stats_.emplace_front (std::move(stat)); } //-------------------------------------------------------------------------- @@ -164,179 +185,13 @@ ServerImpl::ceil_log2 (unsigned long long x) return y; } -void -ServerImpl::report (Stat&& stat) -{ - int const bucket = ceil_log2 (stat.requests); - std::lock_guard lock (mutex_); - ++hist_[bucket]; - high_ = std::max (high_, bucket); - if (stats_.size() >= historySize) - stats_.pop_back(); - stats_.emplace_front (std::move(stat)); -} - -void -ServerImpl::onWrite (beast::PropertyStream::Map& map) -{ - std::lock_guard lock (mutex_); - - // VFALCO TODO Write the list of doors - - map ["active"] = state_.peers.size(); - - { - std::string s; - for (int i = 0; i <= high_; ++i) - { - if (i) - s += ", "; - s += std::to_string (hist_[i]); - } - map ["hist"] = s; - } - - { - beast::PropertyStream::Set set ("history", map); - for (auto const& stat : stats_) - { - beast::PropertyStream::Map item (set); - - item ["id"] = stat.id; - item ["when"] = stat.when; - - { - std::stringstream ss; - ss << stat.elapsed; - item ["elapsed"] = ss.str(); - } - - item ["requests"] = stat.requests; - item ["bytes_in"] = stat.bytes_in; - item ["bytes_out"] = stat.bytes_out; - if (stat.ec) - item ["error"] = stat.ec.message(); - } - } -} - -//-------------------------------------------------------------------------- - -int -ServerImpl::compare (Port const& lhs, Port const& rhs) -{ - if (lhs < rhs) - return -1; - else if (rhs < lhs) - return 1; - return 0; -} - -void -ServerImpl::update() -{ - io_service_.post (m_strand.wrap (std::bind ( - &ServerImpl::on_update, this))); -} - -// Updates our Door list based on settings. -void -ServerImpl::on_update () -{ - /* - if (! m_strand.running_in_this_thread()) - io_service_.dispatch (m_strand.wrap (std::bind ( - &ServerImpl::update, this))); - */ - - if (! stopping()) - { - // Make a local copy to shorten the lock - // - Ports ports; - { - std::lock_guard lock (mutex_); - ports = state_.ports; - } - - std::sort (ports.begin(), ports.end()); - - // Walk the Door list and the Port list simultaneously and - // build a replacement Door vector which we will then swap in. - // - Doors doors; - Doors::iterator door (m_doors.begin()); - for (Ports::const_iterator port (ports.begin()); - port != ports.end(); ++port) - { - int comp = 0; - - while (door != m_doors.end() && - ((comp = compare (*port, (*door)->port())) > 0)) - { - (*door)->cancel(); - ++door; - } - - if (door != m_doors.end()) - { - if (comp < 0) - { - doors.push_back (std::make_shared ( - io_service_, *this, *port)); - doors.back()->listen(); - } - else - { - // old Port and new Port are the same - doors.push_back (*door); - ++door; - } - } - else - { - doors.push_back (std::make_shared ( - io_service_, *this, *port)); - doors.back()->listen(); - } - } - - // Any remaining Door objects are not in the new set, so cancel them. - // - for (;door != m_doors.end();) - (*door)->cancel(); - - m_doors.swap (doors); - } - else - { - // Cancel pending I/O on all doors. - // - for (Doors::iterator iter (m_doors.begin()); - iter != m_doors.end(); ++iter) - { - (*iter)->cancel(); - } - - // Remove our references to the old doors. - // - m_doors.resize (0); - } -} - -// Thread entry point to perform io_service work void ServerImpl::run() { static std::atomic id; - beast::Thread::setCurrentThreadName ( std::string("HTTP::Server #") + std::to_string (++id)); - io_service_.run(); - - m_stopped.signal(); - m_handler.onStopped (m_server); } } diff --git a/src/ripple/http/impl/ServerImpl.h b/src/ripple/http/impl/ServerImpl.h index b8addf8b34..43dd31a346 100644 --- a/src/ripple/http/impl/ServerImpl.h +++ b/src/ripple/http/impl/ServerImpl.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -52,9 +53,20 @@ struct Stat boost::system::error_code ec; }; -class ServerImpl +class ServerImpl : public Server { +public: + class Child : public boost::intrusive::list_base_hook < + boost::intrusive::link_mode > + { + public: + virtual void close() = 0; + }; + private: + using list_type = boost::intrusive::make_list >::type; + typedef std::chrono::system_clock clock_type; enum @@ -62,97 +74,71 @@ private: historySize = 100 }; - struct State - { - // Attributes for our listening ports - Ports ports; - - // All allocated Peer objects - beast::List peers; - - // All allocated Door objects - beast::List doors; - }; - typedef std::vector > Doors; - Server& m_server; - Handler& m_handler; + Handler& handler_; std::thread thread_; std::mutex mutable mutex_; std::condition_variable cond_; + list_type list_; beast::Journal journal_; boost::asio::io_service io_service_; - boost::asio::io_service::strand m_strand; - boost::optional m_work; - beast::WaitableEvent m_stopped; - State state_; - Doors m_doors; + boost::asio::io_service::strand strand_; + boost::optional work_; std::deque stats_; std::array hist_; int high_ = 0; public: - ServerImpl (Server& server, Handler& handler, beast::Journal journal); + ServerImpl (Handler& handler, beast::Journal journal); + ~ServerImpl (); beast::Journal - journal() const + journal() override { return journal_; } - Ports const& - getPorts () const; + void + ports (std::vector const& ports) override; void - setPorts (Ports const& ports); - - bool - stopping () const; + onWrite (beast::PropertyStream::Map& map) override; void - stop (bool wait); + close() override; +public: Handler& - handler(); + handler() + { + return handler_; + } boost::asio::io_service& - get_io_service(); + get_io_service() + { + return io_service_; + } void - add (BasicPeer& peer); + add (Child& child); void - add (Door& door); + remove (Child& child); - void - remove (BasicPeer& peer); - - void - remove (Door& door); + bool + closed(); void report (Stat&& stat); - void - onWrite (beast::PropertyStream::Map& map); - private: static int ceil_log2 (unsigned long long x); - static - int - compare (Port const& lhs, Port const& rhs); - - void - update(); - - void - on_update(); - void run(); }; diff --git a/src/ripple/http/tests/Server.test.cpp b/src/ripple/http/tests/Server.test.cpp index ff90feb80d..447c92331f 100644 --- a/src/ripple/http/tests/Server.test.cpp +++ b/src/ripple/http/tests/Server.test.cpp @@ -155,8 +155,6 @@ public: void test_request() { - testcase("request"); - boost::asio::io_service ios; typedef boost::asio::ip::tcp::socket socket; socket s (ios); @@ -189,8 +187,6 @@ public: void test_keepalive() { - testcase("keepalive"); - boost::asio::io_service ios; typedef boost::asio::ip::tcp::socket socket; socket s (ios); @@ -234,19 +230,19 @@ public: sink.severity (beast::Journal::Severity::kAll); beast::Journal journal {sink}; TestHandler handler; - Server s (handler, journal); - Ports ports; + auto s = make_Server (handler, journal); + std::vector list; std::unique_ptr c ( RippleSSLContext::createBare ()); - ports.emplace_back (testPort, beast::IP::Endpoint ( + list.emplace_back (testPort, beast::IP::Endpoint ( beast::IP::AddressV4 (127, 0, 0, 1), 0), Port::Security::no_ssl, c.get()); - s.setPorts (ports); + s->ports (list); test_request(); //test_keepalive(); - - s.stop(); + //s->close(); + s = nullptr; pass(); }