diff --git a/src/mjson.c b/src/mjson.c index 55ba507..9dab996 100644 --- a/src/mjson.c +++ b/src/mjson.c @@ -964,7 +964,12 @@ void jsonrpc_return_errorv(struct jsonrpc_request *r, int code, mjson_printf(r->fn, r->fn_data, ",\"data\":"); mjson_vprintf(r->fn, r->fn_data, data_fmt, ap); } - mjson_printf(r->fn, r->fn_data, "}}\n"); + if (r->jsonrpc_2) { + mjson_printf(r->fn, r->fn_data, "%s", "},\"jsonrpc\":\"2.0\"}\n"); + } + else { + mjson_printf(r->fn, r->fn_data, "}}\n"); + } } void jsonrpc_return_error(struct jsonrpc_request *r, int code, @@ -984,6 +989,9 @@ void jsonrpc_return_successv(struct jsonrpc_request *r, const char *result_fmt, } else { mjson_printf(r->fn, r->fn_data, "%s", "null"); } + if (r->jsonrpc_2) { + mjson_printf(r->fn, r->fn_data, "%s", ",\"jsonrpc\":\"2.0\""); + } mjson_printf(r->fn, r->fn_data, "}\n"); } @@ -1000,7 +1008,7 @@ void jsonrpc_ctx_process(struct jsonrpc_ctx *ctx, const char *buf, int len, const char *result = NULL, *error = NULL; int result_sz = 0, error_sz = 0; struct jsonrpc_method *m = NULL; - struct jsonrpc_request r = {ctx, buf, len, 0, 0, 0, 0, 0, 0, fn, fn_data, ud}; + struct jsonrpc_request r = {ctx, buf, len, 0, 0, 0, 0, 0, 0, fn, fn_data, ud, 0}; // Is is a response frame? mjson_find(buf, len, "$.result", &result, &result_sz); @@ -1022,6 +1030,28 @@ void jsonrpc_ctx_process(struct jsonrpc_ctx *ctx, const char *buf, int len, mjson_find(buf, len, "$.id", &r.id, &r.id_len); mjson_find(buf, len, "$.params", &r.params, &r.params_len); + + const char* jsonrpc = NULL; + int jsonrpc_len = 0; + int jsonrpc_type = mjson_find(buf, len, "$.jsonrpc", &jsonrpc, &jsonrpc_len); + if (jsonrpc_len == 0) { + r.jsonrpc_2 = 0; + } + else if (jsonrpc_type != MJSON_TOK_STRING || + jsonrpc_len != 5 || + strncmp("\"2.0\"", jsonrpc, 5)) { + if (r.id_len != 0) { + mjson_printf(fn, fn_data, + "{\"jsonrpc\":\"2.0\",\"id\":%.*s,\"error\":{\"code\":-32600," + "\"message\":\"Value of jsonrpc must be the string 2.0\"}}\n", + r.id_len, r.id); + } + return; + } + else { + r.jsonrpc_2 = 1; + } + for (m = ctx->methods; m != NULL; m = m->next) { if (mjson_globmatch(m->method, m->method_sz, r.method + 1, r.method_len - 2) > 0) { diff --git a/src/mjson.h b/src/mjson.h index d8156e0..bfb438a 100644 --- a/src/mjson.h +++ b/src/mjson.h @@ -165,6 +165,7 @@ struct jsonrpc_request { mjson_print_fn_t fn; // Printer function void *fn_data; // Printer function data void *userdata; // Callback's user data as specified at export time + int jsonrpc_2; // Indicates if this is a jsonrpc 2.0 request or not }; struct jsonrpc_method { diff --git a/test/unit_test.c b/test/unit_test.c index 6d5f5bd..52c7b47 100644 --- a/test/unit_test.c +++ b/test/unit_test.c @@ -688,6 +688,31 @@ static void test_rpc(void) { fb.len = 0; jsonrpc_process(req, (int) strlen(req), mjson_print_fixed_buf, &fb, NULL); ASSERT(strcmp(buf, res) == 0); + + // Test jsonrpc 2.0 success + req = "{\"jsonrpc\":\"2.0\",\"id\": 2, \"method\": \"foo\",\"params\":[0,1.23]}\n"; + res = "{\"id\":2,\"result\":{\"x\":1.23,\"ud\":\"hi\"},\"jsonrpc\":\"2.0\"}\n"; + fb.len = 0; + jsonrpc_process(req, (int) strlen(req), mjson_print_fixed_buf, &fb, + (void *) "hi"); + ASSERT(strcmp(buf, res) == 0); + + // Test jsonrpc 2.0 error + req = "{\"jsonrpc\":\"2.0\",\"id\": 3, \"method\": \"foo1\",\"params\":[1,true]}\n"; + res = "{\"id\":3,\"error\":{\"code\":123,\"message\":\"\"},\"jsonrpc\":\"2.0\"}\n"; + fb.len = 0; + jsonrpc_process(req, (int) strlen(req), mjson_print_fixed_buf, &fb, NULL); + ASSERT(strcmp(buf, res) == 0); + + // Test jsonrpc 2.0 wrong value + req = "{\"jsonrpc\":\"1.0\",\"id\": 2, \"method\": \"foo\",\"params\":[0,1.23]}\n"; + res = "{\"jsonrpc\":\"2.0\",\"id\":2,\"error\":{\"code\":-32600,\"message\":\"" + "Value of jsonrpc must be the string 2.0\"}}\n"; + fb.len = 0; + jsonrpc_process(req, (int) strlen(req), mjson_print_fixed_buf, &fb, + (void *) "hi"); + ASSERT(strcmp(buf, res) == 0); + } static void test_merge(void) {