diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index 621e3147e..93aec76f3 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -192,9 +192,7 @@ static struct command_result *json_stop(struct command *cmd, jout = json_out_new(tmpctx); json_out_start(jout, NULL, '{'); json_out_addstr(jout, "jsonrpc", "2.0"); - /* id may be a string or number, so copy direct. */ - memcpy(json_out_member_direct(jout, "id", strlen(cmd->id)), - cmd->id, strlen(cmd->id)); + json_out_add(jout, "id", cmd->id_is_string, "%s", cmd->id); json_out_addstr(jout, "result", "Shutdown complete"); json_out_end(jout, '}'); json_out_finished(jout); @@ -533,7 +531,10 @@ void json_notify_fmt(struct command *cmd, json_add_string(js, "jsonrpc", "2.0"); json_add_string(js, "method", "message"); json_object_start(js, "params"); - json_add_string(js, "id", cmd->id); + if (cmd->id_is_string) + json_add_string(js, "id", cmd->id); + else + json_add_jsonstr(js, "id", cmd->id, strlen(cmd->id)); json_add_string(js, "level", log_level_name(level)); json_add_string(js, "message", tal_vfmt(tmpctx, fmt, ap)); json_object_end(js); @@ -578,7 +579,10 @@ static struct json_stream *json_start(struct command *cmd) json_object_start(js, NULL); json_add_string(js, "jsonrpc", "2.0"); - json_add_jsonstr(js, "id", cmd->id, strlen(cmd->id)); + if (cmd->id_is_string) + json_add_string(js, "id", cmd->id); + else + json_add_jsonstr(js, "id", cmd->id, strlen(cmd->id)); return js; } @@ -892,9 +896,8 @@ parse_request(struct json_connection *jcon, const jsmntok_t tok[]) c->ld = jcon->ld; c->pending = false; c->json_stream = NULL; - c->id = tal_strndup(c, - json_tok_full(jcon->buffer, id), - json_tok_full_len(id)); + c->id_is_string = (id->type == JSMN_STRING); + c->id = json_strdup(c, jcon->buffer, id); c->mode = CMD_NORMAL; list_add_tail(&jcon->commands, &c->list); tal_add_destructor(c, destroy_command); diff --git a/lightningd/jsonrpc.h b/lightningd/jsonrpc.h index 659f65e3c..92b4e7722 100644 --- a/lightningd/jsonrpc.h +++ b/lightningd/jsonrpc.h @@ -27,6 +27,8 @@ struct command { struct lightningd *ld; /* The 'id' which we need to include in the response. */ const char *id; + /* If 'id' needs to be quoted (i.e. it's a string) */ + bool id_is_string; /* What command we're running (for logging) */ const struct json_command *json_cmd; /* The connection, or NULL if it closed. */ diff --git a/lightningd/plugin.c b/lightningd/plugin.c index 075b886e6..20c0b1c2f 100644 --- a/lightningd/plugin.c +++ b/lightningd/plugin.c @@ -1025,7 +1025,7 @@ static void json_stream_forward_change_id(struct json_stream *stream, const jsmntok_t *toks, const jsmntok_t *idtok, const char *new_id, - bool add_quotes) + bool new_id_is_str) { /* We copy everything, but replace the id. Special care has to * be taken when the id that is being replaced is a string. If @@ -1033,12 +1033,16 @@ static void json_stream_forward_change_id(struct json_stream *stream, * new_id into a string, or even worse, quote a string id * twice. */ size_t offset = 0; + bool add_quotes = false; if (idtok->type == JSMN_STRING) { - if (add_quotes) + if (new_id_is_str) add_quotes = false; else offset = 1; + } else { + if (new_id_is_str) + add_quotes = true; } json_stream_append(stream, buffer + toks->start, @@ -1063,7 +1067,7 @@ static void plugin_rpcmethod_cb(const char *buffer, response = json_stream_raw_for_cmd(cmd); json_stream_forward_change_id(response, buffer, toks, idtok, cmd->id, - false); + cmd->id_is_string); json_stream_double_cr(response); command_raw_complete(cmd, response); @@ -1089,7 +1093,8 @@ static void plugin_notify_cb(const char *buffer, json_add_tok(response, "method", methodtok, buffer); json_stream_append(response, ",\"params\":", strlen(",\"params\":")); json_stream_forward_change_id(response, buffer, - paramtoks, idtok, cmd->id, false); + paramtoks, idtok, cmd->id, + cmd->id_is_string); json_object_end(response); json_stream_double_cr(response); diff --git a/tests/test_misc.py b/tests/test_misc.py index 744469e66..b86449b5f 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -874,7 +874,7 @@ def test_cli(node_factory): assert 'help [command]\n List available commands, or give verbose help on one {command}' in out # Check JSON id is as expected - l1.daemon.wait_for_log("jsonrpc#[0-9]*: IN:id=\"cli:help#[0-9]*") + l1.daemon.wait_for_log("jsonrpc#[0-9]*: IN:id=cli:help#[0-9]*") # Test JSON output. out = subprocess.check_output(['cli/lightning-cli',