From 914dc01222c54206096cf5c066c834b27e61add0 Mon Sep 17 00:00:00 2001 From: Giel van Schijndel Date: Wed, 10 Aug 2011 13:53:13 +0200 Subject: [PATCH] Use asynchronous I/O to handle RPC requests This allows more flexibility in the RPC code, e.g. making it easier to handle multiple simultaneous connections later on. Currently asynchronous I/O is only used to listen for and accept incoming connections. Asynchronous reading/writing is more involved. Signed-off-by: Giel van Schijndel --- src/bitcoinrpc.cpp | 120 +++++++++++++++++++++++++++++++-------------- 1 file changed, 82 insertions(+), 38 deletions(-) diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 8f4fb93a5..efcb39d70 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -14,6 +14,7 @@ #undef printf #include +#include #include #include #include @@ -21,6 +22,7 @@ #include #include #include +#include typedef boost::asio::ssl::stream SSLStream; #define printf OutputDebugStringF @@ -2641,6 +2643,75 @@ void ThreadRPCServer(void* parg) printf("ThreadRPCServer exited\n"); } +// Forward declaration required for RPCListen +static void RPCAcceptHandler(boost::shared_ptr acceptor, + ssl::context& context, + bool fUseSSL, + AcceptedConnection* conn, + const boost::system::error_code& error); + +/** + * Sets up I/O resources to accept and handle a new connection. + */ +static void RPCListen(boost::shared_ptr acceptor, + ssl::context& context, + const bool fUseSSL) +{ + + // Accept connection + AcceptedConnection* conn = new AcceptedConnection(acceptor->get_io_service(), context, fUseSSL); + + acceptor->async_accept( + conn->sslStream.lowest_layer(), + conn->peer, + boost::bind(&RPCAcceptHandler, + acceptor, + boost::ref(context), + fUseSSL, + conn, + boost::asio::placeholders::error)); +} + +/** + * Accept and handle incoming connection. + */ +static void RPCAcceptHandler(boost::shared_ptr acceptor, + ssl::context& context, + const bool fUseSSL, + AcceptedConnection* conn, + const boost::system::error_code& error) +{ + vnThreadsRunning[THREAD_RPCLISTENER]++; + + // Immediately start accepting new connections + RPCListen(acceptor, context, fUseSSL); + + // TODO: Actually handle errors + if (error) + { + delete conn; + } + + // Restrict callers by IP. It is important to + // do this before starting client thread, to filter out + // certain DoS and misbehaving clients. + else if (!ClientAllowed(conn->peer.address().to_string())) + { + // Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake. + if (!fUseSSL) + conn->stream << HTTPReply(403, "", false) << std::flush; + delete conn; + } + + // start HTTP client thread + else if (!CreateThread(ThreadRPCServer3, conn)) { + printf("Failed to create RPC server client thread\n"); + delete conn; + } + + vnThreadsRunning[THREAD_RPCLISTENER]--; +} + void ThreadRPCServer2(void* parg) { printf("ThreadRPCServer started\n"); @@ -2670,18 +2741,18 @@ void ThreadRPCServer2(void* parg) return; } - bool fUseSSL = GetBoolArg("-rpcssl"); + const bool fUseSSL = GetBoolArg("-rpcssl"); asio::ip::address bindAddress = mapArgs.count("-rpcallowip") ? asio::ip::address_v4::any() : asio::ip::address_v4::loopback(); asio::io_service io_service; ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", 8332)); - ip::tcp::acceptor acceptor(io_service); + boost::shared_ptr acceptor(new ip::tcp::acceptor(io_service)); try { - acceptor.open(endpoint.protocol()); - acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); - acceptor.bind(endpoint); - acceptor.listen(socket_base::max_connections); + acceptor->open(endpoint.protocol()); + acceptor->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); + acceptor->bind(endpoint); + acceptor->listen(socket_base::max_connections); } catch(boost::system::system_error &e) { @@ -2710,39 +2781,12 @@ void ThreadRPCServer2(void* parg) SSL_CTX_set_cipher_list(context.impl(), strCiphers.c_str()); } - loop - { - // Accept connection - AcceptedConnection *conn = - new AcceptedConnection(io_service, context, fUseSSL); - - vnThreadsRunning[THREAD_RPCLISTENER]--; - acceptor.accept(conn->sslStream.lowest_layer(), conn->peer); - vnThreadsRunning[THREAD_RPCLISTENER]++; - - if (fShutdown) - { - delete conn; - return; - } - - // Restrict callers by IP. It is important to - // do this before starting client thread, to filter out - // certain DoS and misbehaving clients. - if (!ClientAllowed(conn->peer.address().to_string())) - { - // Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake. - if (!fUseSSL) - conn->stream << HTTPReply(403, "", false) << std::flush; - delete conn; - } + RPCListen(acceptor, context, fUseSSL); - // start HTTP client thread - else if (!CreateThread(ThreadRPCServer3, conn)) { - printf("Failed to create RPC server client thread\n"); - delete conn; - } - } + vnThreadsRunning[THREAD_RPCLISTENER]--; + while (!fShutdown) + io_service.run_one(); + vnThreadsRunning[THREAD_RPCLISTENER]++; } void ThreadRPCServer3(void* parg)