diff options
author | Giel van Schijndel <me@mortis.eu> | 2011-08-10 13:53:13 +0200 |
---|---|---|
committer | Giel van Schijndel <me@mortis.eu> | 2012-05-25 07:27:24 +0200 |
commit | 914dc01222c54206096cf5c066c834b27e61add0 (patch) | |
tree | 9a0cdf5c2cee9cc0636b8ebcaf902ad12fc92f08 /src/bitcoinrpc.cpp | |
parent | 6b8a17119e30ddc5dd5b92ed77da49740c495ed6 (diff) |
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 <me@mortis.eu>
Diffstat (limited to 'src/bitcoinrpc.cpp')
-rw-r--r-- | src/bitcoinrpc.cpp | 120 |
1 files changed, 82 insertions, 38 deletions
diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 8f4fb93a5a..efcb39d70d 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -14,6 +14,7 @@ #undef printf #include <boost/asio.hpp> +#include <boost/bind.hpp> #include <boost/filesystem.hpp> #include <boost/iostreams/concepts.hpp> #include <boost/iostreams/stream.hpp> @@ -21,6 +22,7 @@ #include <boost/lexical_cast.hpp> #include <boost/asio/ssl.hpp> #include <boost/filesystem/fstream.hpp> +#include <boost/shared_ptr.hpp> typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> 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<ip::tcp::acceptor> 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<ip::tcp::acceptor> 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<ip::tcp::acceptor> 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<ip::tcp::acceptor> 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) |