Message ID | 20180808120334.10970-48-armbru@redhat.com |
---|---|
State | New |
Headers | show |
Series | json: Fixes, error reporting improvements, cleanups | expand |
On 08/08/2018 07:03 AM, Markus Armbruster wrote: > The last case where qobject_from_json() & friends return null without > setting an error is empty or blank input. Callers: > > * block.c's parse_json_protocol() reports "Could not parse the JSON > options". It's marked as a work-around, because it also covered > actual bugs, but they got fixed in the previous few commits. How would you trigger this? I guess that would be by using the pseud-json block specification spelled "json:" rather than the usual "json:{...}". > > * qobject_input_visitor_new_str() reports "JSON parse error". Also > marked as work-around. The recent fixes have made this unreachable, > because it currently gets called only for input starting with '{'. Indeed, no triggers to this. > > * check-qjson.c's empty_input() and blank_input() demonstrate the > behavior. > > * The other callers are not affected since they only pass input with > exactly one JSON value or, in the case of negative tests, one error. As long as sending back-to-back newlines to QMP does not treat the empty line as an error, you should be okay. (If sending two newlines in a row now results in a {"error":...} response from the server for the blank line, then you've regressed). > > Fail with "Expecting a JSON value" instead of returning null, and > simplify callers. > > Signed-off-by: Markus Armbruster <armbru@redhat.com> > --- Yay for getting rid of the inconsistent error reporting! Reviewed-by: Eric Blake <eblake@redhat.com>
Eric Blake <eblake@redhat.com> writes: > On 08/08/2018 07:03 AM, Markus Armbruster wrote: >> The last case where qobject_from_json() & friends return null without >> setting an error is empty or blank input. Callers: >> >> * block.c's parse_json_protocol() reports "Could not parse the JSON >> options". It's marked as a work-around, because it also covered >> actual bugs, but they got fixed in the previous few commits. > > How would you trigger this? $ qemu-system-x86_64 json:{} qemu-system-x86_64: Must specify either driver or file $ qemu-system-x86_64 json: qemu-system-x86_64: Could not parse the JSON options > I guess that would be by using the > pseud-json block specification spelled "json:" rather than the usual > "json:{...}". > >> >> * qobject_input_visitor_new_str() reports "JSON parse error". Also >> marked as work-around. The recent fixes have made this unreachable, >> because it currently gets called only for input starting with '{'. > > Indeed, no triggers to this. > >> >> * check-qjson.c's empty_input() and blank_input() demonstrate the >> behavior. >> >> * The other callers are not affected since they only pass input with >> exactly one JSON value or, in the case of negative tests, one error. > > As long as sending back-to-back newlines to QMP does not treat the > empty line as an error, you should be okay. (If sending two newlines > in a row now results in a {"error":...} response from the server for > the blank line, then you've regressed). QMP doesn't parse with qobject_from_json(), so it isn't affected. Permit me a digression on newlines. Newlines are like any other whitespace. Whitespace can be necessary to make the lexer emit a token. For instance, sending "123" without a newline to QMP does not produce a reply. The lexer is in state IN_DIGITS then. You can make it go to JSON_INTEGER and emit the token by sending a newline. This produces a reply. This doesn't match a (naive?) interactive users mental model of newline. When such a user hits newline, he expects a reply. If he doesn't get one, say because he miscounted his curlies, confusion ensues. A better designed protocol would avoid that trap. >> Fail with "Expecting a JSON value" instead of returning null, and >> simplify callers. >> >> Signed-off-by: Markus Armbruster <armbru@redhat.com> >> --- > > Yay for getting rid of the inconsistent error reporting! > > Reviewed-by: Eric Blake <eblake@redhat.com> Thanks!
diff --git a/block.c b/block.c index 39f373e035..b837684e3c 100644 --- a/block.c +++ b/block.c @@ -1478,11 +1478,6 @@ static QDict *parse_json_filename(const char *filename, Error **errp) options_obj = qobject_from_json(filename, errp); if (!options_obj) { - /* Work around qobject_from_json() lossage TODO fix that */ - if (errp && !*errp) { - error_setg(errp, "Could not parse the JSON options"); - return NULL; - } error_prepend(errp, "Could not parse the JSON options: "); return NULL; } diff --git a/qapi/qobject-input-visitor.c b/qapi/qobject-input-visitor.c index da57f4cc24..3e88b27f9e 100644 --- a/qapi/qobject-input-visitor.c +++ b/qapi/qobject-input-visitor.c @@ -725,11 +725,6 @@ Visitor *qobject_input_visitor_new_str(const char *str, if (is_json) { obj = qobject_from_json(str, errp); if (!obj) { - /* Work around qobject_from_json() lossage TODO fix that */ - if (errp && !*errp) { - error_setg(errp, "JSON parse error"); - return NULL; - } return NULL; } args = qobject_to(QDict, obj); diff --git a/qobject/qjson.c b/qobject/qjson.c index 7f69036487..b9ccae2c2a 100644 --- a/qobject/qjson.c +++ b/qobject/qjson.c @@ -70,6 +70,10 @@ static QObject *qobject_from_jsonv(const char *string, va_list *ap, json_message_parser_flush(&state.parser); json_message_parser_destroy(&state.parser); + if (!state.result && !state.err) { + error_setg(&state.err, "Expecting a JSON value"); + } + error_propagate(errp, state.err); return state.result; } diff --git a/tests/check-qjson.c b/tests/check-qjson.c index 833d220654..49490f678e 100644 --- a/tests/check-qjson.c +++ b/tests/check-qjson.c @@ -1240,13 +1240,21 @@ static void simple_interpolation(void) static void empty_input(void) { - QObject *obj = qobject_from_json("", &error_abort); + Error *err = NULL; + QObject *obj; + + obj = qobject_from_json("", &err); + error_free_or_abort(&err); g_assert(obj == NULL); } static void blank_input(void) { - QObject *obj = qobject_from_json("\n ", &error_abort); + Error *err = NULL; + QObject *obj; + + obj = qobject_from_json("\n ", &err); + error_free_or_abort(&err); g_assert(obj == NULL); }
The last case where qobject_from_json() & friends return null without setting an error is empty or blank input. Callers: * block.c's parse_json_protocol() reports "Could not parse the JSON options". It's marked as a work-around, because it also covered actual bugs, but they got fixed in the previous few commits. * qobject_input_visitor_new_str() reports "JSON parse error". Also marked as work-around. The recent fixes have made this unreachable, because it currently gets called only for input starting with '{'. * check-qjson.c's empty_input() and blank_input() demonstrate the behavior. * The other callers are not affected since they only pass input with exactly one JSON value or, in the case of negative tests, one error. Fail with "Expecting a JSON value" instead of returning null, and simplify callers. Signed-off-by: Markus Armbruster <armbru@redhat.com> --- block.c | 5 ----- qapi/qobject-input-visitor.c | 5 ----- qobject/qjson.c | 4 ++++ tests/check-qjson.c | 12 ++++++++++-- 4 files changed, 14 insertions(+), 12 deletions(-)