bitcoin-cli: More detailed error reporting

Register a evhttp error handler to get a more detailed error message
if the HTTP request fails.
This commit is contained in:
Wladimir J. van der Laan 2016-09-14 15:08:34 +02:00
parent fec6af7440
commit 381826dfee

View File

@ -116,17 +116,42 @@ static bool AppInitRPC(int argc, char* argv[])
/** Reply structure for request_done to fill in */ /** Reply structure for request_done to fill in */
struct HTTPReply struct HTTPReply
{ {
HTTPReply(): status(0), error(-1) {}
int status; int status;
int error;
std::string body; 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) static void http_request_done(struct evhttp_request *req, void *ctx)
{ {
HTTPReply *reply = static_cast<HTTPReply*>(ctx); HTTPReply *reply = static_cast<HTTPReply*>(ctx);
if (req == NULL) { if (req == NULL) {
/* If req is NULL, it means an error occurred while connecting, but /* If req is NULL, it means an error occurred while connecting: the
* I'm not sure how to find out which one. We also don't really care. * error code will have been passed to http_error_cb.
*/ */
reply->status = 0; reply->status = 0;
return; return;
@ -145,6 +170,14 @@ static void http_request_done(struct evhttp_request *req, void *ctx)
} }
} }
#if LIBEVENT_VERSION_NUMBER >= 0x02010300
static void http_error_cb(enum evhttp_request_error err, void *ctx)
{
HTTPReply *reply = static_cast<HTTPReply*>(ctx);
reply->error = err;
}
#endif
UniValue CallRPC(const string& strMethod, const UniValue& params) UniValue CallRPC(const string& strMethod, const UniValue& params)
{ {
std::string host = GetArg("-rpcconnect", DEFAULT_RPCCONNECT); std::string host = GetArg("-rpcconnect", DEFAULT_RPCCONNECT);
@ -165,6 +198,9 @@ UniValue CallRPC(const string& strMethod, const UniValue& params)
struct evhttp_request *req = evhttp_request_new(http_request_done, (void*)&response); // TODO RAII struct evhttp_request *req = evhttp_request_new(http_request_done, (void*)&response); // TODO RAII
if (req == NULL) if (req == NULL)
throw runtime_error("create http request failed"); throw runtime_error("create http request failed");
#if LIBEVENT_VERSION_NUMBER >= 0x02010300
evhttp_request_set_error_cb(req, http_error_cb);
#endif
// Get credentials // Get credentials
std::string strRPCUserColonPass; std::string strRPCUserColonPass;
@ -204,7 +240,7 @@ UniValue CallRPC(const string& strMethod, const UniValue& params)
event_base_free(base); event_base_free(base);
if (response.status == 0) if (response.status == 0)
throw CConnectionFailed("couldn't connect to server"); throw CConnectionFailed(strprintf("couldn't connect to server (%d %s)", response.error, http_errorstring(response.error)));
else if (response.status == HTTP_UNAUTHORIZED) else if (response.status == HTTP_UNAUTHORIZED)
throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)"); throw 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) else if (response.status >= 400 && response.status != HTTP_BAD_REQUEST && response.status != HTTP_NOT_FOUND && response.status != HTTP_INTERNAL_SERVER_ERROR)