aboutsummaryrefslogtreecommitdiff
path: root/src/bitcoinrpc.cpp
diff options
context:
space:
mode:
authorGiel van Schijndel <me@mortis.eu>2011-08-10 13:53:13 +0200
committerGiel van Schijndel <me@mortis.eu>2012-05-25 07:27:24 +0200
commit914dc01222c54206096cf5c066c834b27e61add0 (patch)
tree9a0cdf5c2cee9cc0636b8ebcaf902ad12fc92f08 /src/bitcoinrpc.cpp
parent6b8a17119e30ddc5dd5b92ed77da49740c495ed6 (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.cpp120
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)