GCC Code Coverage Report


Directory: src/
File: src/json_rpc.c
Date: 2024-04-25 03:45:42
Exec Total Coverage
Lines: 201 306 65.7%
Branches: 58 146 39.7%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2023 Egor Tensin <egor@tensin.name>
3 * This file is part of the "cimple" project.
4 * For details, see https://github.com/egor-tensin/cimple.
5 * Distributed under the MIT License.
6 */
7
8 #include "json_rpc.h"
9 #include "json.h"
10 #include "log.h"
11
12 #include <json-c/json_object.h>
13
14 #include <stdatomic.h>
15 #include <stdint.h>
16 #include <stdlib.h>
17 #include <string.h>
18
19 struct jsonrpc_request {
20 struct json_object *impl;
21 };
22
23 struct jsonrpc_response {
24 struct json_object *impl;
25 };
26
27 static const char *const jsonrpc_key_version = "jsonrpc";
28 static const char *const jsonrpc_key_id = "id";
29 static const char *const jsonrpc_key_method = "method";
30 static const char *const jsonrpc_key_params = "params";
31
32 static const char *const jsonrpc_value_version = "2.0";
33
34 46006 static int jsonrpc_check_version(struct json_object *obj)
35 {
36 46006 const char *key = jsonrpc_key_version;
37 46006 const char *version = NULL;
38
39 46006 int ret = libjson_get_string(obj, key, &version);
40
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 46006 times.
46006 if (ret < 0)
41 return ret;
42
43
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 46006 times.
46006 if (strcmp(version, jsonrpc_value_version)) {
44 log_err("JSON-RPC: invalid '%s' value: %s\n", key, version);
45 return -1;
46 }
47
48 46006 return 0;
49 }
50
51 64418 static int jsonrpc_set_version(struct json_object *obj)
52 {
53 64418 return libjson_set_string_const_key(obj, jsonrpc_key_version, jsonrpc_value_version);
54 }
55
56 static _Atomic int jsonrpc_id_counter = 1;
57
58 9206 int jsonrpc_generate_request_id(void)
59 {
60 9206 return jsonrpc_id_counter++;
61 }
62
63 18412 static int jsonrpc_check_id_type(struct json_object *id)
64 {
65
2/4
✓ Branch 1 taken 18412 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 18412 times.
18412 if (!json_object_is_type(id, json_type_string) && !json_object_is_type(id, json_type_int)) {
66 log_err("JSON-RPC: key '%s' must be either an integer or a string\n",
67 jsonrpc_key_id);
68 return -1;
69 }
70 18412 return 0;
71 }
72
73 46006 static int jsonrpc_check_id(struct json_object *obj, int required)
74 {
75 46006 const char *key = jsonrpc_key_id;
76
77
2/2
✓ Branch 1 taken 27594 times.
✓ Branch 2 taken 18412 times.
46006 if (!libjson_has(obj, key)) {
78
1/2
✓ Branch 0 taken 27594 times.
✗ Branch 1 not taken.
27594 if (!required)
79 27594 return 0;
80 log_err("JSON-RPC: key is missing: %s\n", key);
81 return -1;
82 }
83
84 18412 struct json_object *id = NULL;
85
86 18412 int ret = libjson_get(obj, key, &id);
87
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18412 times.
18412 if (ret < 0)
88 return ret;
89 18412 return jsonrpc_check_id_type(id);
90 }
91
92 9206 static int jsonrpc_set_id(struct json_object *obj, int id)
93 {
94 9206 return libjson_set_int_const_key(obj, jsonrpc_key_id, id);
95 }
96
97 36800 static int jsonrpc_check_method(struct json_object *obj)
98 {
99 36800 const char *key = jsonrpc_key_method;
100 36800 const char *method = NULL;
101 36800 return libjson_get_string(obj, key, &method);
102 }
103
104 36800 static int jsonrpc_set_method(struct json_object *obj, const char *method)
105 {
106 36800 return libjson_set_string_const_key(obj, jsonrpc_key_method, method);
107 }
108
109 27540 static int jsonrpc_check_params_type(struct json_object *params)
110 {
111
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 27540 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
27540 if (!json_object_is_type(params, json_type_object) &&
112 !json_object_is_type(params, json_type_array)) {
113 log_err("JSON-RPC: key '%s' must be either an object or an array\n",
114 jsonrpc_key_params);
115 return -1;
116 }
117 27540 return 0;
118 }
119
120 36800 static int jsonrpc_check_params(struct json_object *obj)
121 {
122 36800 const char *key = jsonrpc_key_params;
123
124
2/2
✓ Branch 1 taken 9260 times.
✓ Branch 2 taken 27540 times.
36800 if (!libjson_has(obj, key))
125 9260 return 0;
126
127 27540 struct json_object *params = NULL;
128
129 27540 int ret = libjson_get(obj, key, &params);
130
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 27540 times.
27540 if (ret < 0)
131 return ret;
132 27540 return jsonrpc_check_params_type(params);
133 }
134
135 static int jsonrpc_set_params(struct json_object *obj, struct json_object *params)
136 {
137 const char *key = jsonrpc_key_params;
138
139 int ret = jsonrpc_check_params_type(params);
140 if (ret < 0)
141 return ret;
142 return libjson_set_const_key(obj, key, params);
143 }
144
145 static const char *const jsonrpc_key_result = "result";
146 static const char *const jsonrpc_key_error = "error";
147
148 static const char *const jsonrpc_key_code = "code";
149 static const char *const jsonrpc_key_message = "message";
150
151 static int jsonrpc_check_error(struct json_object *obj)
152 {
153 const char *key = jsonrpc_key_error;
154 struct json_object *error = NULL;
155
156 int ret = libjson_get(obj, key, &error);
157 if (ret < 0)
158 return ret;
159
160 int64_t code = -1;
161
162 ret = libjson_get_int(error, jsonrpc_key_code, &code);
163 if (ret < 0) {
164 log_err("JSON-RPC: key is missing or not an integer: %s\n", jsonrpc_key_code);
165 return -1;
166 }
167
168 const char *message = NULL;
169
170 ret = libjson_get_string(error, jsonrpc_key_message, &message);
171 if (ret < 0) {
172 log_err("JSON-RPC: key is missing or not a string: %s\n", jsonrpc_key_message);
173 return -1;
174 }
175
176 return ret;
177 }
178
179 9206 static int jsonrpc_check_result_or_error(struct json_object *obj)
180 {
181 9206 const char *key_result = jsonrpc_key_result;
182 9206 const char *key_error = jsonrpc_key_error;
183
184
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 9206 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
9206 if (!libjson_has(obj, key_result) && !libjson_has(obj, key_error)) {
185 log_err("JSON-RPC: either '%s' or '%s' must be present\n", key_result, key_error);
186 return -1;
187 }
188
189
1/2
✓ Branch 1 taken 9206 times.
✗ Branch 2 not taken.
9206 if (libjson_has(obj, key_result))
190 9206 return 0;
191
192 return jsonrpc_check_error(obj);
193 }
194
195 36800 static int jsonrpc_request_create_internal(struct jsonrpc_request **_request, int *id,
196 const char *method, struct json_object *params)
197 {
198 36800 int ret = 0;
199
200 36800 struct jsonrpc_request *request = malloc(sizeof(struct jsonrpc_request));
201
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 36800 times.
36800 if (!request) {
202 log_errno("malloc");
203 ret = -1;
204 goto exit;
205 }
206
207 36800 ret = libjson_new_object(&request->impl);
208
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 36800 times.
36800 if (ret < 0)
209 goto free;
210
211 36800 ret = jsonrpc_set_version(request->impl);
212
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 36800 times.
36800 if (ret < 0)
213 goto free_impl;
214
215
2/2
✓ Branch 0 taken 9206 times.
✓ Branch 1 taken 27594 times.
36800 if (id) {
216 9206 ret = jsonrpc_set_id(request->impl, *id);
217
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9206 times.
9206 if (ret < 0)
218 goto free_impl;
219 }
220
221 36800 ret = jsonrpc_set_method(request->impl, method);
222
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 36800 times.
36800 if (ret < 0)
223 goto free_impl;
224
225
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 36800 times.
36800 if (params) {
226 ret = jsonrpc_set_params(request->impl, params);
227 if (ret < 0)
228 goto free_impl;
229 }
230
231 36800 *_request = request;
232 36800 goto exit;
233
234 free_impl:
235 libjson_free(request->impl);
236 free:
237 free(request);
238 36800 exit:
239 36800 return ret;
240 }
241
242 9206 int jsonrpc_request_create(struct jsonrpc_request **_request, int id, const char *method,
243 struct json_object *params)
244 {
245 9206 return jsonrpc_request_create_internal(_request, &id, method, params);
246 }
247
248 73600 void jsonrpc_request_destroy(struct jsonrpc_request *request)
249 {
250 73600 libjson_free(request->impl);
251 73600 free(request);
252 73600 }
253
254 27594 int jsonrpc_notification_create(struct jsonrpc_request **_request, const char *method,
255 struct json_object *params)
256 {
257 27594 return jsonrpc_request_create_internal(_request, NULL, method, params);
258 }
259
260 36800 int jsonrpc_request_is_notification(const struct jsonrpc_request *request)
261 {
262 36800 return !libjson_has(request->impl, jsonrpc_key_id);
263 }
264
265 36800 static int jsonrpc_request_from_json(struct jsonrpc_request **_request, struct json_object *impl)
266 {
267 36800 int ret = 0;
268
269 36800 ret = jsonrpc_check_version(impl);
270
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 36800 times.
36800 if (ret < 0)
271 return ret;
272 36800 ret = jsonrpc_check_id(impl, 0);
273
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 36800 times.
36800 if (ret < 0)
274 return ret;
275 36800 ret = jsonrpc_check_method(impl);
276
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 36800 times.
36800 if (ret < 0)
277 return ret;
278 36800 ret = jsonrpc_check_params(impl);
279
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 36800 times.
36800 if (ret < 0)
280 return ret;
281
282 36800 struct jsonrpc_request *request = malloc(sizeof(struct jsonrpc_request));
283
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 36800 times.
36800 if (!request) {
284 log_errno("malloc");
285 return -1;
286 }
287 36800 request->impl = impl;
288
289 36800 *_request = request;
290 36800 return ret;
291 }
292
293 36800 int jsonrpc_request_send(const struct jsonrpc_request *request, int fd)
294 {
295 36800 return libjson_send(request->impl, fd);
296 }
297
298 36800 int jsonrpc_request_recv(struct jsonrpc_request **request, int fd)
299 {
300 36800 struct json_object *impl = libjson_recv(fd);
301
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 36800 times.
36800 if (!impl) {
302 log_err("JSON-RPC: failed to receive request\n");
303 return -1;
304 }
305
306 36800 int ret = jsonrpc_request_from_json(request, impl);
307
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 36800 times.
36800 if (ret < 0)
308 goto free_impl;
309
310 36800 return ret;
311
312 free_impl:
313 libjson_free(impl);
314
315 return ret;
316 }
317
318 36800 const char *jsonrpc_request_get_method(const struct jsonrpc_request *request)
319 {
320 36800 const char *method = NULL;
321 36800 int ret = libjson_get_string(request->impl, jsonrpc_key_method, &method);
322
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 36800 times.
36800 if (ret < 0) {
323 /* Should never happen. */
324 return NULL;
325 }
326 36800 return method;
327 }
328
329 73440 static struct json_object *jsonrpc_request_create_params(struct jsonrpc_request *request)
330 {
331 73440 int ret = 0;
332 73440 const char *const key = jsonrpc_key_params;
333
334
2/2
✓ Branch 1 taken 27540 times.
✓ Branch 2 taken 45900 times.
73440 if (!libjson_has(request->impl, key)) {
335 27540 struct json_object *params = NULL;
336
337 27540 ret = libjson_new_object(&params);
338
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 27540 times.
27540 if (ret < 0)
339 return NULL;
340
341 27540 ret = libjson_set(request->impl, key, params);
342
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 27540 times.
27540 if (ret < 0) {
343 libjson_free(params);
344 return NULL;
345 }
346 27540 return params;
347 }
348
349 45900 struct json_object *params = NULL;
350 45900 ret = libjson_get(request->impl, key, &params);
351
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 45900 times.
45900 if (ret < 0)
352 return NULL;
353 45900 return params;
354 }
355
356 45900 int jsonrpc_request_get_param_string(const struct jsonrpc_request *request, const char *name,
357 const char **value)
358 {
359 45900 struct json_object *params = NULL;
360 45900 int ret = libjson_get(request->impl, jsonrpc_key_params, &params);
361
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 45900 times.
45900 if (ret < 0)
362 return ret;
363 45900 return libjson_get_string(params, name, value);
364 }
365
366 45900 int jsonrpc_request_set_param_string(struct jsonrpc_request *request, const char *name,
367 const char *value)
368 {
369 45900 struct json_object *params = jsonrpc_request_create_params(request);
370
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 45900 times.
45900 if (!params)
371 return -1;
372 45900 return libjson_set_string(params, name, value);
373 }
374
375 27540 int jsonrpc_request_get_param_int(const struct jsonrpc_request *request, const char *name,
376 int64_t *value)
377 {
378 27540 struct json_object *params = NULL;
379 27540 int ret = libjson_get(request->impl, jsonrpc_key_params, &params);
380
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 27540 times.
27540 if (ret < 0)
381 return ret;
382 27540 return libjson_get_int(params, name, value);
383 }
384
385 27540 int jsonrpc_request_set_param_int(struct jsonrpc_request *request, const char *name, int64_t value)
386 {
387 27540 struct json_object *params = jsonrpc_request_create_params(request);
388
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 27540 times.
27540 if (!params)
389 return -1;
390 27540 return libjson_set_int(params, name, value);
391 }
392
393 9206 const char *jsonrpc_response_to_string(const struct jsonrpc_response *response)
394 {
395 9206 return libjson_to_string_pretty(response->impl);
396 }
397
398 27618 int jsonrpc_response_create_internal(struct jsonrpc_response **_response,
399 const struct jsonrpc_request *request,
400 struct json_object *result, struct json_object *error)
401 {
402 27618 int ret = 0;
403
404 27618 struct jsonrpc_response *response = malloc(sizeof(struct jsonrpc_response));
405
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 27618 times.
27618 if (!response) {
406 log_errno("malloc");
407 ret = -1;
408 goto exit;
409 }
410
411 27618 ret = libjson_new_object(&response->impl);
412
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 27618 times.
27618 if (ret < 0)
413 goto free;
414
415 27618 ret = jsonrpc_set_version(response->impl);
416
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 27618 times.
27618 if (ret < 0)
417 goto free_impl;
418
419 27618 struct json_object *id = NULL;
420 27618 ret = libjson_clone(request->impl, jsonrpc_key_id, &id);
421
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 27618 times.
27618 if (ret < 0)
422 goto free_impl;
423
424 27618 ret = libjson_set(response->impl, jsonrpc_key_id, id);
425
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 27618 times.
27618 if (ret < 0) {
426 libjson_free(id);
427 goto free_impl;
428 }
429
430
2/2
✓ Branch 0 taken 9206 times.
✓ Branch 1 taken 18412 times.
27618 if (error) {
431 9206 ret = libjson_set_const_key(response->impl, jsonrpc_key_error, error);
432
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9206 times.
9206 if (ret < 0)
433 goto free_impl;
434 } else {
435 18412 ret = libjson_set_const_key(response->impl, jsonrpc_key_result, result);
436
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18412 times.
18412 if (ret < 0)
437 goto free_impl;
438 }
439
440 27618 *_response = response;
441 27618 goto exit;
442
443 free_impl:
444 libjson_free(response->impl);
445 free:
446 free(response);
447 27618 exit:
448 27618 return ret;
449 }
450
451 18412 int jsonrpc_response_create(struct jsonrpc_response **response,
452 const struct jsonrpc_request *request, struct json_object *result)
453 {
454 18412 return jsonrpc_response_create_internal(response, request, result, NULL);
455 }
456
457 36824 void jsonrpc_response_destroy(struct jsonrpc_response *response)
458 {
459 36824 libjson_free(response->impl);
460 36824 free(response);
461 36824 }
462
463 9206 int jsonrpc_error_create(struct jsonrpc_response **response, struct jsonrpc_request *request,
464 int code, const char *message)
465 {
466 9206 int ret = 0;
467 9206 struct json_object *error = NULL;
468
469 9206 ret = libjson_new_object(&error);
470
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9206 times.
9206 if (ret < 0)
471 return ret;
472
473 9206 ret = libjson_set_int_const_key(error, jsonrpc_key_code, code);
474
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9206 times.
9206 if (ret < 0)
475 goto free;
476 9206 ret = libjson_set_string_const_key(error, jsonrpc_key_message, message);
477
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9206 times.
9206 if (ret < 0)
478 goto free;
479
480 9206 ret = jsonrpc_response_create_internal(response, request, NULL, error);
481
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9206 times.
9206 if (ret < 0)
482 goto free;
483
484 9206 return ret;
485
486 free:
487 libjson_free(error);
488
489 return ret;
490 }
491
492 9206 int jsonrpc_response_is_error(const struct jsonrpc_response *response)
493 {
494 9206 return libjson_has(response->impl, jsonrpc_key_error);
495 }
496
497 9206 static int jsonrpc_response_from_json(struct jsonrpc_response **_response, struct json_object *impl)
498 {
499 9206 int ret = 0;
500
501 9206 ret = jsonrpc_check_version(impl);
502
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9206 times.
9206 if (ret < 0)
503 return ret;
504 9206 ret = jsonrpc_check_id(impl, 1);
505
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9206 times.
9206 if (ret < 0)
506 return ret;
507 9206 ret = jsonrpc_check_result_or_error(impl);
508
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9206 times.
9206 if (ret < 0)
509 return ret;
510
511 9206 struct jsonrpc_response *response = malloc(sizeof(struct jsonrpc_response));
512
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9206 times.
9206 if (!response) {
513 log_errno("malloc");
514 return -1;
515 }
516 9206 response->impl = impl;
517
518 9206 *_response = response;
519 9206 return ret;
520 }
521
522 9206 int jsonrpc_response_send(const struct jsonrpc_response *response, int fd)
523 {
524 9206 return libjson_send(response->impl, fd);
525 }
526
527 9206 int jsonrpc_response_recv(struct jsonrpc_response **response, int fd)
528 {
529 9206 struct json_object *impl = libjson_recv(fd);
530
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9206 times.
9206 if (!impl) {
531 log_err("JSON-RPC: failed to receive response\n");
532 return -1;
533 }
534
535 9206 int ret = jsonrpc_response_from_json(response, impl);
536
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9206 times.
9206 if (ret < 0)
537 goto free_impl;
538
539 9206 return ret;
540
541 free_impl:
542 libjson_free(impl);
543
544 return ret;
545 }
546