From 7c9a06de5a7ea340b69755a917a4fc16ee13ad23 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Sat, 6 Dec 2014 14:37:48 -0800 Subject: [PATCH] MOVEONLY: Move CallRPC into its own file --- src/Makefile.am | 4 ++ src/bitcoin-cli.cpp | 163 +------------------------------------------ src/callrpc.cpp | 151 +++++++++++++++++++++++++++++++++++++++ src/callrpc.h | 34 +++++++++ src/support/events.h | 26 ++----- 5 files changed, 195 insertions(+), 183 deletions(-) create mode 100644 src/callrpc.cpp create mode 100644 src/callrpc.h diff --git a/src/Makefile.am b/src/Makefile.am index 7f04d64f9a..3086c5eee5 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -81,6 +81,7 @@ BITCOIN_CORE_H = \ base58.h \ bloom.h \ blockencodings.h \ + callrpc.h \ chain.h \ chainparams.h \ chainparamsbase.h \ @@ -328,6 +329,8 @@ libbitcoin_util_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) libbitcoin_util_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_util_a_SOURCES = \ support/lockedpool.cpp \ + support/events.cpp \ + callrpc.cpp \ chainparamsbase.cpp \ clientversion.cpp \ compat/glibc_sanity.cpp \ @@ -338,6 +341,7 @@ libbitcoin_util_a_SOURCES = \ support/cleanse.cpp \ sync.cpp \ threadinterrupt.cpp \ + uint256.cpp \ util.cpp \ utilmoneystr.cpp \ utilstrencodings.cpp \ diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index acbe2ff54f..187fd35522 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -3,6 +3,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include "callrpc.h" #if defined(HAVE_CONFIG_H) #include "config/bitcoin-config.h" #endif @@ -16,18 +17,10 @@ #include #include - -#include -#include #include "support/events.h" - #include -static const char DEFAULT_RPCCONNECT[] = "127.0.0.1"; -static const int DEFAULT_HTTP_CLIENT_TIMEOUT=900; -static const bool DEFAULT_NAMED=false; static const int CONTINUE_EXECUTION=-1; - std::string HelpMessageCli() { const auto defaultBaseParams = CreateBaseChainParams(CBaseChainParams::MAIN); @@ -54,20 +47,6 @@ std::string HelpMessageCli() // Start // -// -// Exception thrown on connection error. This error is used to determine -// when to wait if -rpcwait is given. -// -class CConnectionFailed : public std::runtime_error -{ -public: - - explicit inline CConnectionFailed(const std::string& msg) : - std::runtime_error(msg) - {} - -}; - // // This function returns either one of EXIT_ codes when it's expected to stop the process or // CONTINUE_EXECUTION when it's expected to continue further. @@ -122,146 +101,6 @@ static int AppInitRPC(int argc, char* argv[]) return CONTINUE_EXECUTION; } - -/** Reply structure for request_done to fill in */ -struct HTTPReply -{ - HTTPReply(): status(0), error(-1) {} - - int status; - int error; - std::string body; -}; - -const char *http_errorstring(int code) -{ - switch(code) { -#if LIBEVENT_VERSION_NUMBER >= 0x02010300 - case EVREQ_HTTP_TIMEOUT: - return "timeout reached"; - case EVREQ_HTTP_EOF: - return "EOF reached"; - case EVREQ_HTTP_INVALID_HEADER: - return "error while reading header, or invalid header"; - case EVREQ_HTTP_BUFFER_ERROR: - return "error encountered while reading or writing"; - case EVREQ_HTTP_REQUEST_CANCEL: - return "request was canceled"; - case EVREQ_HTTP_DATA_TOO_LONG: - return "response body is larger than allowed"; -#endif - default: - return "unknown"; - } -} - -static void http_request_done(struct evhttp_request *req, void *ctx) -{ - HTTPReply *reply = static_cast(ctx); - - if (req == NULL) { - /* If req is NULL, it means an error occurred while connecting: the - * error code will have been passed to http_error_cb. - */ - reply->status = 0; - return; - } - - reply->status = evhttp_request_get_response_code(req); - - struct evbuffer *buf = evhttp_request_get_input_buffer(req); - if (buf) - { - size_t size = evbuffer_get_length(buf); - const char *data = (const char*)evbuffer_pullup(buf, size); - if (data) - reply->body = std::string(data, size); - evbuffer_drain(buf, size); - } -} - -#if LIBEVENT_VERSION_NUMBER >= 0x02010300 -static void http_error_cb(enum evhttp_request_error err, void *ctx) -{ - HTTPReply *reply = static_cast(ctx); - reply->error = err; -} -#endif - -UniValue CallRPC(const std::string& strMethod, const UniValue& params) -{ - std::string host = GetArg("-rpcconnect", DEFAULT_RPCCONNECT); - int port = GetArg("-rpcport", BaseParams().RPCPort()); - - // Obtain event base - raii_event_base base = obtain_event_base(); - - // Synchronously look up hostname - raii_evhttp_connection evcon = obtain_evhttp_connection_base(base.get(), host, port); - evhttp_connection_set_timeout(evcon.get(), GetArg("-rpcclienttimeout", DEFAULT_HTTP_CLIENT_TIMEOUT)); - - HTTPReply response; - raii_evhttp_request req = obtain_evhttp_request(http_request_done, (void*)&response); - if (req == NULL) - throw std::runtime_error("create http request failed"); -#if LIBEVENT_VERSION_NUMBER >= 0x02010300 - evhttp_request_set_error_cb(req.get(), http_error_cb); -#endif - - // Get credentials - std::string strRPCUserColonPass; - if (GetArg("-rpcpassword", "") == "") { - // Try fall back to cookie-based authentication if no password is provided - if (!GetAuthCookie(&strRPCUserColonPass)) { - throw std::runtime_error(strprintf( - _("Could not locate RPC credentials. No authentication cookie could be found, and no rpcpassword is set in the configuration file (%s)"), - GetConfigFile(GetArg("-conf", BITCOIN_CONF_FILENAME)).string().c_str())); - - } - } else { - strRPCUserColonPass = GetArg("-rpcuser", "") + ":" + GetArg("-rpcpassword", ""); - } - - struct evkeyvalq* output_headers = evhttp_request_get_output_headers(req.get()); - assert(output_headers); - evhttp_add_header(output_headers, "Host", host.c_str()); - evhttp_add_header(output_headers, "Connection", "close"); - evhttp_add_header(output_headers, "Authorization", (std::string("Basic ") + EncodeBase64(strRPCUserColonPass)).c_str()); - - // Attach request data - std::string strRequest = JSONRPCRequestObj(strMethod, params, 1).write() + "\n"; - struct evbuffer* output_buffer = evhttp_request_get_output_buffer(req.get()); - assert(output_buffer); - evbuffer_add(output_buffer, strRequest.data(), strRequest.size()); - - int r = evhttp_make_request(evcon.get(), req.get(), EVHTTP_REQ_POST, "/"); - req.release(); // ownership moved to evcon in above call - if (r != 0) { - throw CConnectionFailed("send http request failed"); - } - - event_base_dispatch(base.get()); - - if (response.status == 0) - throw CConnectionFailed(strprintf("couldn't connect to server: %s (code %d)\n(make sure server is running and you are connecting to the correct RPC port)", http_errorstring(response.error), response.error)); - else if (response.status == HTTP_UNAUTHORIZED) - throw std::runtime_error("incorrect rpcuser or rpcpassword (authorization failed)"); - else if (response.status >= 400 && response.status != HTTP_BAD_REQUEST && response.status != HTTP_NOT_FOUND && response.status != HTTP_INTERNAL_SERVER_ERROR) - throw std::runtime_error(strprintf("server returned HTTP error %d", response.status)); - else if (response.body.empty()) - throw std::runtime_error("no response from server"); - - // Parse reply - UniValue valReply(UniValue::VSTR); - if (!valReply.read(response.body)) - throw std::runtime_error("couldn't parse reply from server"); - const UniValue& reply = valReply.get_obj(); - if (reply.empty()) - throw std::runtime_error("expected reply to have result, error and id properties"); - - return reply; -} - int CommandLineRPC(int argc, char *argv[]) { std::string strPrint; diff --git a/src/callrpc.cpp b/src/callrpc.cpp new file mode 100644 index 0000000000..fd576ff472 --- /dev/null +++ b/src/callrpc.cpp @@ -0,0 +1,151 @@ +#include "chainparamsbase.h" +#include "callrpc.h" +#include "util.h" +#include "utilstrencodings.h" +#include "rpc/protocol.h" + +#include "support/events.h" + +#include "rpc/client.h" + +#include +#include + +/** Reply structure for request_done to fill in */ +struct HTTPReply +{ + HTTPReply(): status(0), error(-1) {} + + int status; + int error; + std::string body; +}; + +const char *http_errorstring(int code) +{ + switch(code) { +#if LIBEVENT_VERSION_NUMBER >= 0x02010300 + case EVREQ_HTTP_TIMEOUT: + return "timeout reached"; + case EVREQ_HTTP_EOF: + return "EOF reached"; + case EVREQ_HTTP_INVALID_HEADER: + return "error while reading header, or invalid header"; + case EVREQ_HTTP_BUFFER_ERROR: + return "error encountered while reading or writing"; + case EVREQ_HTTP_REQUEST_CANCEL: + return "request was canceled"; + case EVREQ_HTTP_DATA_TOO_LONG: + return "response body is larger than allowed"; +#endif + default: + return "unknown"; + } +} + +static void http_request_done(struct evhttp_request *req, void *ctx) +{ + HTTPReply *reply = static_cast(ctx); + + if (req == NULL) { + /* If req is NULL, it means an error occurred while connecting: the + * error code will have been passed to http_error_cb. + */ + reply->status = 0; + return; + } + + reply->status = evhttp_request_get_response_code(req); + + struct evbuffer *buf = evhttp_request_get_input_buffer(req); + if (buf) + { + size_t size = evbuffer_get_length(buf); + const char *data = (const char*)evbuffer_pullup(buf, size); + if (data) + reply->body = std::string(data, size); + evbuffer_drain(buf, size); + } +} + +#if LIBEVENT_VERSION_NUMBER >= 0x02010300 +static void http_error_cb(enum evhttp_request_error err, void *ctx) +{ + HTTPReply *reply = static_cast(ctx); + reply->error = err; +} +#endif + +UniValue CallRPC(const std::string& strMethod, const UniValue& params) +{ + std::string host = GetArg("-rpcconnect", DEFAULT_RPCCONNECT); + int port = GetArg("-rpcport", BaseParams().RPCPort()); + + // Obtain event base + raii_event_base base = obtain_event_base(); + + // Synchronously look up hostname + raii_evhttp_connection evcon = obtain_evhttp_connection_base(base.get(), host, port); + evhttp_connection_set_timeout(evcon.get(), GetArg("-rpcclienttimeout", DEFAULT_HTTP_CLIENT_TIMEOUT)); + + HTTPReply response; + raii_evhttp_request req = obtain_evhttp_request(http_request_done, (void*)&response); + if (req == NULL) + throw std::runtime_error("create http request failed"); +#if LIBEVENT_VERSION_NUMBER >= 0x02010300 + evhttp_request_set_error_cb(req.get(), http_error_cb); +#endif + + // Get credentials + std::string strRPCUserColonPass; + if (GetArg("-rpcpassword", "") == "") { + // Try fall back to cookie-based authentication if no password is provided + if (!GetAuthCookie(&strRPCUserColonPass)) { + throw std::runtime_error(strprintf( + _("Could not locate RPC credentials. No authentication cookie could be found, and no rpcpassword is set in the configuration file (%s)"), + GetConfigFile(GetArg("-conf", BITCOIN_CONF_FILENAME)).string().c_str())); + + } + } else { + strRPCUserColonPass = GetArg("-rpcuser", "") + ":" + GetArg("-rpcpassword", ""); + } + + struct evkeyvalq* output_headers = evhttp_request_get_output_headers(req.get()); + assert(output_headers); + evhttp_add_header(output_headers, "Host", host.c_str()); + evhttp_add_header(output_headers, "Connection", "close"); + evhttp_add_header(output_headers, "Authorization", (std::string("Basic ") + EncodeBase64(strRPCUserColonPass)).c_str()); + + // Attach request data + std::string strRequest = JSONRPCRequestObj(strMethod, params, 1).write() + "\n"; + struct evbuffer* output_buffer = evhttp_request_get_output_buffer(req.get()); + assert(output_buffer); + evbuffer_add(output_buffer, strRequest.data(), strRequest.size()); + + int r = evhttp_make_request(evcon.get(), req.get(), EVHTTP_REQ_POST, "/"); + req.release(); // ownership moved to evcon in above call + if (r != 0) { + throw CConnectionFailed("send http request failed"); + } + + event_base_dispatch(base.get()); + + if (response.status == 0) + throw CConnectionFailed(strprintf("couldn't connect to server: %s (code %d)\n(make sure server is running and you are connecting to the correct RPC port)", http_errorstring(response.error), response.error)); + else if (response.status == HTTP_UNAUTHORIZED) + throw std::runtime_error("incorrect rpcuser or rpcpassword (authorization failed)"); + else if (response.status >= 400 && response.status != HTTP_BAD_REQUEST && response.status != HTTP_NOT_FOUND && response.status != HTTP_INTERNAL_SERVER_ERROR) + throw std::runtime_error(strprintf("server returned HTTP error %d", response.status)); + else if (response.body.empty()) + throw std::runtime_error("no response from server"); + + // Parse reply + UniValue valReply(UniValue::VSTR); + if (!valReply.read(response.body)) + throw std::runtime_error("couldn't parse reply from server"); + const UniValue& reply = valReply.get_obj(); + if (reply.empty()) + throw std::runtime_error("expected reply to have result, error and id properties"); + + return reply; +} diff --git a/src/callrpc.h b/src/callrpc.h new file mode 100644 index 0000000000..497370f33f --- /dev/null +++ b/src/callrpc.h @@ -0,0 +1,34 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_CALLRPC_H +#define BITCOIN_CALLRPC_H + +#include +#include + +#include + +static const bool DEFAULT_NAMED=false; +static const char DEFAULT_RPCCONNECT[] = "127.0.0.1"; +static const int DEFAULT_HTTP_CLIENT_TIMEOUT=900; + +// +// Exception thrown on connection error. This error is used to determine +// when to wait if -rpcwait is given. +// +class CConnectionFailed : public std::runtime_error +{ +public: + + explicit inline CConnectionFailed(const std::string& msg) : + std::runtime_error(msg) + {} + +}; + +UniValue CallRPC(const std::string& strMethod, const UniValue& params); + +#endif // BITCOIN_CALLRPC_H diff --git a/src/support/events.h b/src/support/events.h index 4f2f3cf9ef..96bd14512c 100644 --- a/src/support/events.h +++ b/src/support/events.h @@ -27,30 +27,14 @@ MAKE_RAII(evhttp); MAKE_RAII(evhttp_request); MAKE_RAII(evhttp_connection); -raii_event_base obtain_event_base() { - auto result = raii_event_base(event_base_new()); - if (!result.get()) - throw std::runtime_error("cannot create event_base"); - return result; -} +raii_event_base obtain_event_base(); -raii_event obtain_event(struct event_base* base, evutil_socket_t s, short events, event_callback_fn cb, void* arg) { - return raii_event(event_new(base, s, events, cb, arg)); -} +raii_event obtain_event(struct event_base* base, evutil_socket_t s, short events, event_callback_fn cb, void* arg); -raii_evhttp obtain_evhttp(struct event_base* base) { - return raii_evhttp(evhttp_new(base)); -} +raii_evhttp obtain_evhttp(struct event_base* base); -raii_evhttp_request obtain_evhttp_request(void(*cb)(struct evhttp_request *, void *), void *arg) { - return raii_evhttp_request(evhttp_request_new(cb, arg)); -} +raii_evhttp_request obtain_evhttp_request(void(*cb)(struct evhttp_request *, void *), void *arg); -raii_evhttp_connection obtain_evhttp_connection_base(struct event_base* base, std::string host, uint16_t port) { - auto result = raii_evhttp_connection(evhttp_connection_base_new(base, NULL, host.c_str(), port)); - if (!result.get()) - throw std::runtime_error("create connection failed"); - return result; -} +raii_evhttp_connection obtain_evhttp_connection_base(struct event_base* base, std::string host, uint16_t port); #endif // BITCOIN_SUPPORT_EVENTS_H