From c6494d82fa31913eb075b222293e87ae40c9f9b2 Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Wed, 27 Jun 2012 13:47:02 -0400 Subject: [PATCH 1/2] RPC: break out high level JSON-RPC req/resp into their own functions This prepares for JSON-RPC 2.0 batches. --- src/bitcoinrpc.cpp | 85 ++++++++++++++++++++++++++++++---------------- 1 file changed, 55 insertions(+), 30 deletions(-) diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 51690243..134c3be9 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -2552,7 +2552,7 @@ string JSONRPCRequest(const string& strMethod, const Array& params, const Value& return write_string(Value(request), false) + "\n"; } -string JSONRPCReply(const Value& result, const Value& error, const Value& id) +Object JSONRPCReplyObj(const Value& result, const Value& error, const Value& id) { Object reply; if (error.type() != null_type) @@ -2561,6 +2561,12 @@ string JSONRPCReply(const Value& result, const Value& error, const Value& id) reply.push_back(Pair("result", result)); reply.push_back(Pair("error", error)); reply.push_back(Pair("id", id)); + return reply; +} + +string JSONRPCReply(const Value& result, const Value& error, const Value& id) +{ + Object reply = JSONRPCReplyObj(result, error, id); return write_string(Value(reply), false) + "\n"; } @@ -2905,6 +2911,47 @@ void ThreadRPCServer2(void* parg) StopRequests(); } +class JSONRequest +{ +public: + Value id; + string strMethod; + Array params; + + JSONRequest() { id = Value::null; } + void parse(const Value& valRequest); +}; + +void JSONRequest::parse(const Value& valRequest) +{ + // Parse request + if (valRequest.type() != obj_type) + throw JSONRPCError(-32600, "Invalid Request object"); + const Object& request = valRequest.get_obj(); + + // Parse id now so errors from here on will have the id + id = find_value(request, "id"); + + // Parse method + Value valMethod = find_value(request, "method"); + if (valMethod.type() == null_type) + throw JSONRPCError(-32600, "Missing method"); + if (valMethod.type() != str_type) + throw JSONRPCError(-32600, "Method must be a string"); + strMethod = valMethod.get_str(); + if (strMethod != "getwork" && strMethod != "getmemorypool") + printf("ThreadRPCServer method=%s\n", strMethod.c_str()); + + // Parse params + Value valParams = find_value(request, "params"); + if (valParams.type() == array_type) + params = valParams.get_array(); + else if (valParams.type() == null_type) + params = Array(); + else + throw JSONRPCError(-32600, "Params must be an array"); +} + static CCriticalSection cs_THREAD_RPCHANDLER; void ThreadRPCServer3(void* parg) @@ -2954,52 +3001,30 @@ void ThreadRPCServer3(void* parg) if (mapHeaders["connection"] == "close") fRun = false; - Value id = Value::null; + JSONRequest jreq; try { // Parse request Value valRequest; if (!read_string(strRequest, valRequest) || valRequest.type() != obj_type) throw JSONRPCError(-32700, "Parse error"); - const Object& request = valRequest.get_obj(); - - // Parse id now so errors from here on will have the id - id = find_value(request, "id"); - - // Parse method - Value valMethod = find_value(request, "method"); - if (valMethod.type() == null_type) - throw JSONRPCError(-32600, "Missing method"); - if (valMethod.type() != str_type) - throw JSONRPCError(-32600, "Method must be a string"); - string strMethod = valMethod.get_str(); - if (strMethod != "getwork" && strMethod != "getmemorypool") - printf("ThreadRPCServer method=%s\n", strMethod.c_str()); - - // Parse params - Value valParams = find_value(request, "params"); - Array params; - if (valParams.type() == array_type) - params = valParams.get_array(); - else if (valParams.type() == null_type) - params = Array(); - else - throw JSONRPCError(-32600, "Params must be an array"); - Value result = tableRPC.execute(strMethod, params); + jreq.parse(valRequest); + + Value result = tableRPC.execute(jreq.strMethod, jreq.params); // Send reply - string strReply = JSONRPCReply(result, Value::null, id); + string strReply = JSONRPCReply(result, Value::null, jreq.id); conn->stream() << HTTPReply(200, strReply, fRun) << std::flush; } catch (Object& objError) { - ErrorReply(conn->stream(), objError, id); + ErrorReply(conn->stream(), objError, jreq.id); break; } catch (std::exception& e) { - ErrorReply(conn->stream(), JSONRPCError(-32700, e.what()), id); + ErrorReply(conn->stream(), JSONRPCError(-32700, e.what()), jreq.id); break; } } From 613389019e6e3c0e903f78f6186d598c5194c395 Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Sun, 24 Jun 2012 02:01:28 -0400 Subject: [PATCH 2/2] RPC: add support for JSON-RPC 2.0-style request batching If the top-level object is an array, it is assumed to be an array of JSON-RPC requests. An array is returned, containing one response (error or not) per request, in the order submitted. In a slight change in semantics, batched requests -always- return an HTTP 200 OK status, even ones full of invalid or incorrect requests. --- src/bitcoinrpc.cpp | 54 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 134c3be9..109cb92f 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -2952,6 +2952,39 @@ void JSONRequest::parse(const Value& valRequest) throw JSONRPCError(-32600, "Params must be an array"); } +static Object JSONRPCExecOne(const Value& req) +{ + Object rpc_result; + + JSONRequest jreq; + try { + jreq.parse(req); + + Value result = tableRPC.execute(jreq.strMethod, jreq.params); + rpc_result = JSONRPCReplyObj(result, Value::null, jreq.id); + } + catch (Object& objError) + { + rpc_result = JSONRPCReplyObj(Value::null, objError, jreq.id); + } + catch (std::exception& e) + { + rpc_result = JSONRPCReplyObj(Value::null, + JSONRPCError(-32700, e.what()), jreq.id); + } + + return rpc_result; +} + +static string JSONRPCExecBatch(const Array& vReq) +{ + Array ret; + for (unsigned int reqIdx = 0; reqIdx < vReq.size(); reqIdx++) + ret.push_back(JSONRPCExecOne(vReq[reqIdx])); + + return write_string(Value(ret), false) + "\n"; +} + static CCriticalSection cs_THREAD_RPCHANDLER; void ThreadRPCServer3(void* parg) @@ -3006,15 +3039,26 @@ void ThreadRPCServer3(void* parg) { // Parse request Value valRequest; - if (!read_string(strRequest, valRequest) || valRequest.type() != obj_type) + if (!read_string(strRequest, valRequest)) throw JSONRPCError(-32700, "Parse error"); - jreq.parse(valRequest); + string strReply; + + // singleton request + if (valRequest.type() == obj_type) { + jreq.parse(valRequest); - Value result = tableRPC.execute(jreq.strMethod, jreq.params); + Value result = tableRPC.execute(jreq.strMethod, jreq.params); - // Send reply - string strReply = JSONRPCReply(result, Value::null, jreq.id); + // Send reply + strReply = JSONRPCReply(result, Value::null, jreq.id); + + // array of requests + } else if (valRequest.type() == array_type) + strReply = JSONRPCExecBatch(valRequest.get_array()); + else + throw JSONRPCError(-32700, "Top-level object parse error"); + conn->stream() << HTTPReply(200, strReply, fRun) << std::flush; } catch (Object& objError)