mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-02-22 14:42:40 +01:00
plugin: Split the parsing from the request handling in the plugin
This is a preparatory step for the next commit which adds notification parsing as well. Signed-off-by: Christian Decker <decker.christian@gmail.com>
This commit is contained in:
parent
9b9f9e4837
commit
dc25c43945
1 changed files with 116 additions and 28 deletions
|
@ -234,6 +234,66 @@ static void plugin_request_queue(struct plugin *plugin,
|
|||
io_wake(plugin);
|
||||
}
|
||||
|
||||
static void plugin_notification_handle(struct plugin *plugin,
|
||||
const jsmntok_t *toks)
|
||||
{
|
||||
const jsmntok_t *methtok, *paramstok;
|
||||
|
||||
methtok = json_get_member(plugin->buffer, toks, "method");
|
||||
paramstok = json_get_member(plugin->buffer, toks, "params");
|
||||
|
||||
if (!methtok || !paramstok) {
|
||||
plugin_kill(plugin,
|
||||
"Malformed JSON-RPC notification missing "
|
||||
"\"method\" or \"params\": %.*s",
|
||||
toks->end - toks->start,
|
||||
plugin->buffer + toks->start);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Dispatch incoming notifications. This is currently limited
|
||||
* to just a few method types, should this ever become
|
||||
* unwieldy we can switch to the AUTODATA construction to
|
||||
* register notification handlers in a variety of places. */
|
||||
if (json_tok_streq(plugin->buffer, methtok, "log")) {
|
||||
plugin_log_handle(plugin, paramstok);
|
||||
} else {
|
||||
plugin_kill(plugin, "Unknown notification method %.*s",
|
||||
json_tok_len(methtok),
|
||||
json_tok_contents(plugin->buffer, methtok));
|
||||
}
|
||||
}
|
||||
|
||||
static void plugin_response_handle(struct plugin *plugin,
|
||||
const jsmntok_t *toks,
|
||||
const jsmntok_t *idtok)
|
||||
{
|
||||
struct plugin_request *request;
|
||||
u64 id;
|
||||
/* We only send u64 ids, so if this fails it's a critical error (note
|
||||
* that this also works if id is inside a JSON string!). */
|
||||
if (!json_to_u64(plugin->buffer, idtok, &id)) {
|
||||
plugin_kill(plugin,
|
||||
"JSON-RPC response \"id\"-field is not a u64");
|
||||
return;
|
||||
}
|
||||
|
||||
request = uintmap_get(&plugin->plugins->pending_requests, id);
|
||||
|
||||
if (!request) {
|
||||
plugin_kill(
|
||||
plugin,
|
||||
"Received a JSON-RPC response for non-existent request");
|
||||
return;
|
||||
}
|
||||
|
||||
/* We expect the request->cb to copy if needed */
|
||||
request->cb(request, plugin->buffer, toks, idtok, request->arg);
|
||||
|
||||
uintmap_del(&plugin->plugins->pending_requests, id);
|
||||
tal_free(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to parse a complete message from the plugin's buffer.
|
||||
*
|
||||
|
@ -242,11 +302,8 @@ static void plugin_request_queue(struct plugin *plugin,
|
|||
*/
|
||||
static bool plugin_read_json_one(struct plugin *plugin)
|
||||
{
|
||||
jsmntok_t *toks;
|
||||
bool valid;
|
||||
u64 id;
|
||||
const jsmntok_t *idtok;
|
||||
struct plugin_request *request;
|
||||
const jsmntok_t *toks, *jrtok, *idtok;
|
||||
|
||||
/* FIXME: This could be done more efficiently by storing the
|
||||
* toks and doing an incremental parse, like lightning-cli
|
||||
|
@ -269,32 +326,61 @@ static bool plugin_read_json_one(struct plugin *plugin)
|
|||
return false;
|
||||
}
|
||||
|
||||
jrtok = json_get_member(plugin->buffer, toks, "jsonrpc");
|
||||
idtok = json_get_member(plugin->buffer, toks, "id");
|
||||
|
||||
if (!jrtok) {
|
||||
plugin_kill(
|
||||
plugin,
|
||||
"JSON-RPC message does not contain \"jsonrpc\" field");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!idtok) {
|
||||
plugin_kill(plugin, "JSON-RPC response does not contain an \"id\"-field");
|
||||
return false;
|
||||
/* A Notification is a Request object without an "id"
|
||||
* member. A Request object that is a Notification
|
||||
* signifies the Client's lack of interest in the
|
||||
* corresponding Response object, and as such no
|
||||
* Response object needs to be returned to the
|
||||
* client. The Server MUST NOT reply to a
|
||||
* Notification, including those that are within a
|
||||
* batch request.
|
||||
*
|
||||
* https://www.jsonrpc.org/specification#notification
|
||||
*/
|
||||
plugin_notification_handle(plugin, toks);
|
||||
|
||||
} else {
|
||||
/* When a rpc call is made, the Server MUST reply with
|
||||
* a Response, except for in the case of
|
||||
* Notifications. The Response is expressed as a
|
||||
* single JSON Object, with the following members:
|
||||
*
|
||||
* - jsonrpc: A String specifying the version of the
|
||||
* JSON-RPC protocol. MUST be exactly "2.0".
|
||||
*
|
||||
* - result: This member is REQUIRED on success. This
|
||||
* member MUST NOT exist if there was an error
|
||||
* invoking the method. The value of this member is
|
||||
* determined by the method invoked on the Server.
|
||||
*
|
||||
* - error: This member is REQUIRED on error. This
|
||||
* member MUST NOT exist if there was no error
|
||||
* triggered during invocation.
|
||||
*
|
||||
* - id: This member is REQUIRED. It MUST be the same
|
||||
* as the value of the id member in the Request
|
||||
* Object. If there was an error in detecting the id
|
||||
* in the Request object (e.g. Parse error/Invalid
|
||||
* Request), it MUST be Null. Either the result
|
||||
* member or error member MUST be included, but both
|
||||
* members MUST NOT be included.
|
||||
*
|
||||
* https://www.jsonrpc.org/specification#response_object
|
||||
*/
|
||||
plugin_response_handle(plugin, toks, idtok);
|
||||
}
|
||||
|
||||
/* We only send u64 ids, so if this fails it's a critical error (note
|
||||
* that this also works if id is inside a JSON string!). */
|
||||
if (!json_to_u64(plugin->buffer, idtok, &id)) {
|
||||
plugin_kill(plugin, "JSON-RPC response \"id\"-field is not a u64");
|
||||
return false;
|
||||
}
|
||||
|
||||
request = uintmap_get(&plugin->plugins->pending_requests, id);
|
||||
|
||||
if (!request) {
|
||||
plugin_kill(plugin, "Received a JSON-RPC response for non-existent request");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* We expect the request->cb to copy if needed */
|
||||
request->cb(request, plugin->buffer, toks, idtok, request->arg);
|
||||
tal_free(request);
|
||||
uintmap_del(&plugin->plugins->pending_requests, id);
|
||||
|
||||
/* Move this object out of the buffer */
|
||||
memmove(plugin->buffer, plugin->buffer + toks[0].end,
|
||||
tal_count(plugin->buffer) - toks[0].end);
|
||||
|
@ -314,10 +400,12 @@ static struct io_plan *plugin_read_json(struct io_conn *conn UNUSED,
|
|||
/* Read and process all messages from the connection */
|
||||
do {
|
||||
success = plugin_read_json_one(plugin);
|
||||
} while (success);
|
||||
|
||||
if (plugin->stop)
|
||||
return io_close(plugin->stdout_conn);
|
||||
/* Processing the message from the plugin might have
|
||||
* resulted in it stopping, so let's check. */
|
||||
if (plugin->stop)
|
||||
return io_close(plugin->stdout_conn);
|
||||
} while (success);
|
||||
|
||||
/* Now read more from the connection */
|
||||
return io_read_partial(plugin->stdout_conn,
|
||||
|
|
Loading…
Add table
Reference in a new issue