Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (C) 2016 Intel Corporation.
3 : : * All rights reserved.
4 : : */
5 : :
6 : : #include "spdk/stdinc.h"
7 : :
8 : : #include "spdk_internal/cunit.h"
9 : :
10 : : #include "jsonrpc/jsonrpc_server.c"
11 : :
12 : : static struct spdk_jsonrpc_request *g_request;
13 : : static int g_parse_error;
14 : : const struct spdk_json_val *g_method;
15 : : const struct spdk_json_val *g_params;
16 : :
17 : : const struct spdk_json_val *g_cur_param;
18 : :
19 : : #define PARSE_PASS(in, trailing) \
20 : : CU_ASSERT(g_cur_param == NULL); \
21 : : g_cur_param = NULL; \
22 : : CU_ASSERT(jsonrpc_parse_request(conn, in, sizeof(in) - 1) == sizeof(in) - sizeof(trailing))
23 : :
24 : : #define REQ_BEGIN(expected_error) \
25 : : if (expected_error != 0 ) { \
26 : : CU_ASSERT(g_parse_error == expected_error); \
27 : : CU_ASSERT(g_params == NULL); \
28 : : }
29 : :
30 : : #define PARSE_FAIL(in) \
31 : : CU_ASSERT(jsonrpc_parse_request(conn, in, sizeof(in) - 1) < 0);
32 : :
33 : : #define REQ_BEGIN_VALID() \
34 : : REQ_BEGIN(0); \
35 : : SPDK_CU_ASSERT_FATAL(g_params != NULL);
36 : :
37 : : #define REQ_BEGIN_INVALID(expected_error) \
38 : : REQ_BEGIN(expected_error); \
39 : : REQ_METHOD_MISSING(); \
40 : : REQ_ID_MISSING(); \
41 : : REQ_PARAMS_MISSING()
42 : :
43 : :
44 : : #define REQ_METHOD(name) \
45 : : CU_ASSERT(g_method && spdk_json_strequal(g_method, name) == true)
46 : :
47 : : #define REQ_METHOD_MISSING() \
48 : : CU_ASSERT(g_method == NULL)
49 : :
50 : : #define REQ_ID_NUM(num) \
51 : : CU_ASSERT(g_request->id && g_request->id->type == SPDK_JSON_VAL_NUMBER); \
52 : : CU_ASSERT(g_request->id && memcmp(g_request->id->start, num, sizeof(num) - 1) == 0)
53 : :
54 : :
55 : : #define REQ_ID_STRING(str) \
56 : : CU_ASSERT(g_request->id && g_request->id->type == SPDK_JSON_VAL_STRING); \
57 : : CU_ASSERT(g_request->id && memcmp(g_request->id->start, num, strlen(num) - 1) == 0))
58 : :
59 : : #define REQ_ID_NULL() \
60 : : CU_ASSERT(g_request->id && g_request->id->type == SPDK_JSON_VAL_NULL)
61 : :
62 : : #define REQ_ID_MISSING() \
63 : : CU_ASSERT(g_request->id == NULL)
64 : :
65 : : #define REQ_PARAMS_MISSING() \
66 : : CU_ASSERT(g_params == NULL)
67 : :
68 : : #define REQ_PARAMS_BEGIN() \
69 : : SPDK_CU_ASSERT_FATAL(g_params != NULL); \
70 : : CU_ASSERT(g_cur_param == NULL); \
71 : : g_cur_param = g_params
72 : :
73 : : #define PARAM_ARRAY_BEGIN() \
74 : : CU_ASSERT(g_cur_param->type == SPDK_JSON_VAL_ARRAY_BEGIN); \
75 : : g_cur_param++
76 : :
77 : : #define PARAM_ARRAY_END() \
78 : : CU_ASSERT(g_cur_param->type == SPDK_JSON_VAL_ARRAY_END); \
79 : : g_cur_param++
80 : :
81 : : #define PARAM_OBJECT_BEGIN() \
82 : : CU_ASSERT(g_cur_param->type == SPDK_JSON_VAL_OBJECT_BEGIN); \
83 : : g_cur_param++
84 : :
85 : : #define PARAM_OBJECT_END() \
86 : : CU_ASSERT(g_cur_param->type == SPDK_JSON_VAL_OBJECT_END); \
87 : : g_cur_param++
88 : :
89 : : #define PARAM_NUM(num) \
90 : : CU_ASSERT(g_cur_param->type == SPDK_JSON_VAL_NUMBER); \
91 : : CU_ASSERT(g_cur_param->len == sizeof(num) - 1); \
92 : : CU_ASSERT(memcmp(g_cur_param->start, num, sizeof(num) - 1) == 0); \
93 : : g_cur_param++
94 : :
95 : : #define PARAM_NAME(str) \
96 : : CU_ASSERT(g_cur_param->type == SPDK_JSON_VAL_NAME); \
97 : : CU_ASSERT(g_cur_param->len == sizeof(str) - 1); \
98 : : CU_ASSERT(g_cur_param && memcmp(g_cur_param->start, str, sizeof(str) - 1) == 0); \
99 : : g_cur_param++
100 : :
101 : : #define PARAM_STRING(str) \
102 : : CU_ASSERT(g_cur_param->type == SPDK_JSON_VAL_STRING); \
103 : : CU_ASSERT(g_cur_param->len == sizeof(str) - 1); \
104 : : CU_ASSERT(memcmp(g_cur_param->start, str, g_params->len) == 0); \
105 : : g_cur_param++
106 : :
107 : : #define FREE_REQUEST() \
108 : : ut_jsonrpc_free_request(g_request, g_parse_error); \
109 : : g_request = NULL; \
110 : : g_cur_param = NULL; \
111 : : g_parse_error = 0; \
112 : : g_method = NULL; \
113 : : g_cur_param = g_params = NULL
114 : :
115 : : static void
116 : 414 : ut_jsonrpc_free_request(struct spdk_jsonrpc_request *request, int err)
117 : : {
118 : : struct spdk_json_write_ctx *w;
119 : :
120 [ + + ]: 414 : if (!request) {
121 : 324 : return;
122 : : }
123 : :
124 : : /* Need to emulate response to get the response write contex free */
125 [ + + ]: 90 : if (err == 0) {
126 : 36 : w = spdk_jsonrpc_begin_result(request);
127 : 36 : spdk_json_write_string(w, "UT PASS response");
128 : 36 : spdk_jsonrpc_end_result(request, w);
129 : : } else {
130 : 54 : spdk_jsonrpc_send_error_response_fmt(request, err, "UT error response");
131 : : }
132 : :
133 : 90 : jsonrpc_free_request(request);
134 : : }
135 : :
136 : : static void
137 : 90 : ut_handle(struct spdk_jsonrpc_request *request, int error, const struct spdk_json_val *method,
138 : : const struct spdk_json_val *params)
139 : : {
140 : 90 : CU_ASSERT(g_request == NULL);
141 : 90 : g_request = request;
142 : 90 : g_parse_error = error;
143 : 90 : g_method = method;
144 : 90 : g_params = params;
145 : 90 : }
146 : :
147 : : void
148 : 54 : jsonrpc_server_handle_error(struct spdk_jsonrpc_request *request, int error)
149 : : {
150 : 54 : ut_handle(request, error, NULL, NULL);
151 : 54 : }
152 : :
153 : : void
154 : 36 : jsonrpc_server_handle_request(struct spdk_jsonrpc_request *request,
155 : : const struct spdk_json_val *method, const struct spdk_json_val *params)
156 : : {
157 : 36 : ut_handle(request, 0, method, params);
158 : 36 : }
159 : :
160 : : void
161 : 90 : jsonrpc_server_send_response(struct spdk_jsonrpc_request *request)
162 : : {
163 : 90 : }
164 : :
165 : : static void
166 : 6 : test_parse_request(void)
167 : : {
168 : : struct spdk_jsonrpc_server *server;
169 : : struct spdk_jsonrpc_server_conn *conn;
170 : :
171 : 6 : server = calloc(1, sizeof(*server));
172 [ - + ]: 6 : SPDK_CU_ASSERT_FATAL(server != NULL);
173 : :
174 : 6 : conn = calloc(1, sizeof(*conn));
175 [ - + ]: 6 : SPDK_CU_ASSERT_FATAL(conn != NULL);
176 [ - + ]: 6 : pthread_spin_init(&conn->queue_lock, PTHREAD_PROCESS_PRIVATE);
177 : 6 : STAILQ_INIT(&conn->outstanding_queue);
178 : :
179 : 6 : conn->server = server;
180 : :
181 : : /* rpc call with no parameters. */
182 : 6 : PARSE_PASS("{ }", "");
183 : 6 : REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_INVALID_REQUEST);
184 : 6 : FREE_REQUEST();
185 : :
186 : : /* rpc call with method that is not a string. */
187 : 6 : PARSE_PASS("{\"jsonrpc\":\"2.0\", \"method\": null }", "");
188 : 6 : REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_INVALID_REQUEST);
189 : 6 : FREE_REQUEST();
190 : :
191 : : /* rpc call with invalid JSON RPC version. */
192 : 6 : PARSE_PASS("{\"jsonrpc\":\"42\", \"method\": \"subtract\"}", "");
193 : 6 : REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_INVALID_REQUEST);
194 : 6 : FREE_REQUEST();
195 : :
196 : : /* rpc call with embedded zeros. */
197 : 6 : PARSE_FAIL("{\"jsonrpc\":\"2.0\",\"method\":\"foo\",\"params\":{\"bar\": \"\0\0baz\"}}");
198 : 6 : REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_PARSE_ERROR);
199 : 6 : FREE_REQUEST();
200 : :
201 : : /* rpc call with positional parameters */
202 : 6 : PARSE_PASS("{\"jsonrpc\":\"2.0\",\"method\":\"subtract\",\"params\":[42,23],\"id\":1}", "");
203 [ - + ]: 6 : REQ_BEGIN_VALID();
204 [ + - + - ]: 6 : REQ_METHOD("subtract");
205 [ + - + - : 6 : REQ_ID_NUM("1");
+ - + - ]
206 [ - + ]: 6 : REQ_PARAMS_BEGIN();
207 : 6 : PARAM_ARRAY_BEGIN();
208 [ - + ]: 6 : PARAM_NUM("42");
209 [ - + ]: 6 : PARAM_NUM("23");
210 : 6 : PARAM_ARRAY_END();
211 : 6 : FREE_REQUEST();
212 : :
213 : : /* rpc call with named parameters */
214 : 6 : PARSE_PASS("{\"jsonrpc\": \"2.0\", \"method\": \"subtract\", \"params\": {\"subtrahend\": 23, \"minuend\": 42}, \"id\": 3}",
215 : : "");
216 [ - + ]: 6 : REQ_BEGIN_VALID();
217 [ + - + - ]: 6 : REQ_METHOD("subtract");
218 [ + - + - : 6 : REQ_ID_NUM("3");
+ - + - ]
219 [ - + ]: 6 : REQ_PARAMS_BEGIN();
220 : 6 : PARAM_OBJECT_BEGIN();
221 [ + - + + : 6 : PARAM_NAME("subtrahend");
+ - ]
222 [ - + ]: 6 : PARAM_NUM("23");
223 [ + - + + : 6 : PARAM_NAME("minuend");
+ - ]
224 [ - + ]: 6 : PARAM_NUM("42");
225 : 6 : PARAM_OBJECT_END();
226 : 6 : FREE_REQUEST();
227 : :
228 : : /* notification */
229 : 6 : PARSE_PASS("{\"jsonrpc\": \"2.0\", \"method\": \"update\", \"params\": [1,2,3,4,5]}", "");
230 [ - + ]: 6 : REQ_BEGIN_VALID();
231 [ + - + - ]: 6 : REQ_METHOD("update");
232 : 6 : REQ_ID_MISSING();
233 [ - + ]: 6 : REQ_PARAMS_BEGIN();
234 : 6 : PARAM_ARRAY_BEGIN();
235 : 6 : PARAM_NUM("1");
236 : 6 : PARAM_NUM("2");
237 : 6 : PARAM_NUM("3");
238 : 6 : PARAM_NUM("4");
239 : 6 : PARAM_NUM("5");
240 : 6 : PARAM_ARRAY_END();
241 : 6 : FREE_REQUEST();
242 : :
243 : : /* notification with explicit NULL ID. This is discouraged by JSON RPC spec but allowed. */
244 : 6 : PARSE_PASS("{\"jsonrpc\": \"2.0\", \"method\": \"update\", \"params\": [1,2,3,4,5], \"id\": null}",
245 : : "");
246 [ - + ]: 6 : REQ_BEGIN_VALID();
247 [ + - + - ]: 6 : REQ_METHOD("update");
248 [ + - + - ]: 6 : REQ_ID_NULL();
249 [ - + ]: 6 : REQ_PARAMS_BEGIN();
250 : 6 : PARAM_ARRAY_BEGIN();
251 : 6 : PARAM_NUM("1");
252 : 6 : PARAM_NUM("2");
253 : 6 : PARAM_NUM("3");
254 : 6 : PARAM_NUM("4");
255 : 6 : PARAM_NUM("5");
256 : 6 : PARAM_ARRAY_END();
257 : 6 : FREE_REQUEST();
258 : :
259 : : /* invalid JSON */
260 : 6 : PARSE_FAIL("{\"jsonrpc\": \"2.0\", \"method\": \"foobar, \"params\": \"bar\", \"baz]");
261 : 6 : REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_PARSE_ERROR);
262 : 6 : FREE_REQUEST();
263 : :
264 : : /* invalid request (method must be a string; params must be array or object) */
265 : 6 : PARSE_PASS("{\"jsonrpc\": \"2.0\", \"method\": 1, \"params\": \"bar\"}", "");
266 : 6 : REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_INVALID_REQUEST);
267 : 6 : FREE_REQUEST();
268 : :
269 : : /* batch, invalid JSON */
270 : 6 : PARSE_FAIL(
271 : : "["
272 : : "{\"jsonrpc\": \"2.0\", \"method\": \"sum\", \"params\": [1,2,4], \"id\": \"1\"},"
273 : : "{\"jsonrpc\": \"2.0\", \"method\""
274 : : "]");
275 : 6 : REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_PARSE_ERROR);
276 : 6 : FREE_REQUEST();
277 : :
278 : : /* empty array */
279 : 6 : PARSE_PASS("[]", "");
280 : 6 : REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_INVALID_REQUEST);
281 : 6 : FREE_REQUEST();
282 : :
283 : : /* batch - not supported */
284 : 6 : PARSE_PASS(
285 : : "["
286 : : "{\"jsonrpc\": \"2.0\", \"method\": \"sum\", \"params\": [1,2,4], \"id\": \"1\"},"
287 : : "{\"jsonrpc\": \"2.0\", \"method\": \"notify_hello\", \"params\": [7]},"
288 : : "{\"jsonrpc\": \"2.0\", \"method\": \"subtract\", \"params\": [42,23], \"id\": \"2\"},"
289 : : "{\"foo\": \"boo\"},"
290 : : "{\"jsonrpc\": \"2.0\", \"method\": \"foo.get\", \"params\": {\"name\": \"myself\"}, \"id\": \"5\"},"
291 : : "{\"jsonrpc\": \"2.0\", \"method\": \"get_data\", \"id\": \"9\"}"
292 : : "]", "");
293 : :
294 : 6 : REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_INVALID_REQUEST);
295 : 6 : FREE_REQUEST();
296 : :
297 : 6 : CU_ASSERT(conn->outstanding_requests == 0);
298 : 6 : free(conn);
299 : 6 : free(server);
300 : 6 : }
301 : :
302 : : static void
303 : 6 : test_parse_request_streaming(void)
304 : : {
305 : : struct spdk_jsonrpc_server *server;
306 : : struct spdk_jsonrpc_server_conn *conn;
307 : : const char *json_req;
308 : : size_t len, i;
309 : :
310 : 6 : server = calloc(1, sizeof(*server));
311 [ - + ]: 6 : SPDK_CU_ASSERT_FATAL(server != NULL);
312 : :
313 : 6 : conn = calloc(1, sizeof(*conn));
314 [ - + ]: 6 : SPDK_CU_ASSERT_FATAL(conn != NULL);
315 [ - + ]: 6 : pthread_spin_init(&conn->queue_lock, PTHREAD_PROCESS_PRIVATE);
316 : 6 : STAILQ_INIT(&conn->outstanding_queue);
317 : :
318 : 6 : conn->server = server;
319 : :
320 : :
321 : : /*
322 : : * Two valid requests end to end in the same buffer.
323 : : * Parse should return the first one and point to the beginning of the second one.
324 : : */
325 : 6 : PARSE_PASS(
326 : : "{\"jsonrpc\":\"2.0\",\"method\":\"a\",\"params\":[1],\"id\":1}"
327 : : "{\"jsonrpc\":\"2.0\",\"method\":\"b\",\"params\":[2],\"id\":2}",
328 : : "{\"jsonrpc\":\"2.0\",\"method\":\"b\",\"params\":[2],\"id\":2}");
329 : :
330 [ - + ]: 6 : REQ_BEGIN_VALID();
331 [ + - + - ]: 6 : REQ_METHOD("a");
332 [ + - + - : 6 : REQ_ID_NUM("1");
+ - + - ]
333 [ - + ]: 6 : REQ_PARAMS_BEGIN();
334 : 6 : PARAM_ARRAY_BEGIN();
335 : 6 : PARAM_NUM("1");
336 : 6 : PARAM_ARRAY_END();
337 : 6 : FREE_REQUEST();
338 : :
339 : : /* Partial (but not invalid) requests - parse should not consume anything. */
340 : 6 : json_req = " {\"jsonrpc\":\"2.0\",\"method\":\"b\",\"params\":[2],\"id\":2}";
341 [ - + ]: 6 : len = strlen(json_req);
342 : :
343 : : /* Try every partial length up to the full request length */
344 [ + + ]: 330 : for (i = 0; i < len; i++) {
345 : 324 : int rc = jsonrpc_parse_request(conn, json_req, i);
346 : : /* Partial request - no data consumed */
347 : 324 : CU_ASSERT(rc == 0);
348 : 324 : CU_ASSERT(g_request == NULL);
349 : :
350 : : /* In case of failed, don't fload console with useless CU assert fails. */
351 : 324 : FREE_REQUEST();
352 : : }
353 : :
354 : : /* Verify that full request can be parsed successfully */
355 : 6 : CU_ASSERT(jsonrpc_parse_request(conn, json_req, len) == (ssize_t)len);
356 : 6 : FREE_REQUEST();
357 : :
358 : 6 : CU_ASSERT(conn->outstanding_requests == 0);
359 : 6 : free(conn);
360 : 6 : free(server);
361 : 6 : }
362 : :
363 : : int
364 : 6 : main(int argc, char **argv)
365 : : {
366 : 6 : CU_pSuite suite = NULL;
367 : : unsigned int num_failures;
368 : :
369 : 6 : CU_initialize_registry();
370 : :
371 : 6 : suite = CU_add_suite("jsonrpc", NULL, NULL);
372 : :
373 : 6 : CU_ADD_TEST(suite, test_parse_request);
374 : 6 : CU_ADD_TEST(suite, test_parse_request_streaming);
375 : :
376 : 6 : num_failures = spdk_ut_run_tests(argc, argv, NULL);
377 : :
378 : 6 : CU_cleanup_registry();
379 : :
380 : : /* This is for ASAN. Don't know why but if pointer is left in global variable
381 : : * it won't be detected as leak. */
382 : 6 : g_request = NULL;
383 : 6 : return num_failures;
384 : : }
|