xref: /spdk/test/unit/lib/jsonrpc/jsonrpc_server.c/jsonrpc_server_ut.c (revision 9889ab2dc80e40dae92dcef361d53dcba722043d)
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright (c) Intel Corporation.
5  *   All rights reserved.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include "spdk/stdinc.h"
35 
36 #include "spdk_cunit.h"
37 
38 #include "jsonrpc/jsonrpc_server.c"
39 
40 static struct spdk_jsonrpc_request *g_request;
41 static int g_parse_error;
42 const struct spdk_json_val *g_method;
43 const struct spdk_json_val *g_params;
44 
45 const struct spdk_json_val *g_cur_param;
46 
47 #define PARSE_PASS(in, trailing) \
48 	CU_ASSERT(g_cur_param == NULL); \
49 	g_cur_param = NULL; \
50 	CU_ASSERT(spdk_jsonrpc_parse_request(conn, in, sizeof(in) - 1) == sizeof(in) - sizeof(trailing))
51 
52 #define REQ_BEGIN(expected_error) \
53 	if (expected_error != 0 ) { \
54 		CU_ASSERT(g_parse_error == expected_error); \
55 		CU_ASSERT(g_params == NULL); \
56 	}
57 
58 #define PARSE_FAIL(in) \
59 	CU_ASSERT(spdk_jsonrpc_parse_request(conn, in, sizeof(in) - 1) < 0);
60 
61 #define REQ_BEGIN_VALID() \
62 		REQ_BEGIN(0); \
63 		SPDK_CU_ASSERT_FATAL(g_params != NULL);
64 
65 #define REQ_BEGIN_INVALID(expected_error) \
66 	REQ_BEGIN(expected_error); \
67 	REQ_METHOD_MISSING(); \
68 	REQ_ID_MISSING(); \
69 	REQ_PARAMS_MISSING()
70 
71 
72 #define REQ_METHOD(name) \
73 	CU_ASSERT(g_method && spdk_json_strequal(g_method, name) == true)
74 
75 #define REQ_METHOD_MISSING() \
76 	CU_ASSERT(g_method == NULL)
77 
78 #define REQ_ID_NUM(num) \
79 	CU_ASSERT(g_request->id && g_request->id->type == SPDK_JSON_VAL_NUMBER); \
80 	CU_ASSERT(g_request->id && memcmp(g_request->id->start, num, sizeof(num) - 1) == 0)
81 
82 
83 #define REQ_ID_STRING(str) \
84 	CU_ASSERT(g_request->id && g_request->id->type == SPDK_JSON_VAL_STRING); \
85 	CU_ASSERT(g_request->id && memcmp(g_request->id->start, num, strlen(num) - 1) == 0))
86 
87 #define REQ_ID_NULL() \
88 	CU_ASSERT(g_request->id && g_request->id->type == SPDK_JSON_VAL_NULL)
89 
90 #define REQ_ID_MISSING() \
91 	CU_ASSERT(g_request->id == NULL)
92 
93 #define REQ_PARAMS_MISSING() \
94 	CU_ASSERT(g_params == NULL)
95 
96 #define REQ_PARAMS_BEGIN() \
97 	SPDK_CU_ASSERT_FATAL(g_params != NULL); \
98 	CU_ASSERT(g_cur_param == NULL); \
99 	g_cur_param = g_params
100 
101 #define PARAM_ARRAY_BEGIN() \
102 	CU_ASSERT(g_cur_param->type == SPDK_JSON_VAL_ARRAY_BEGIN); \
103 	g_cur_param++
104 
105 #define PARAM_ARRAY_END() \
106 	CU_ASSERT(g_cur_param->type == SPDK_JSON_VAL_ARRAY_END); \
107 	g_cur_param++
108 
109 #define PARAM_OBJECT_BEGIN() \
110 	CU_ASSERT(g_cur_param->type == SPDK_JSON_VAL_OBJECT_BEGIN); \
111 	g_cur_param++
112 
113 #define PARAM_OBJECT_END() \
114 	CU_ASSERT(g_cur_param->type == SPDK_JSON_VAL_OBJECT_END); \
115 	g_cur_param++
116 
117 #define PARAM_NUM(num) \
118 	CU_ASSERT(g_cur_param->type == SPDK_JSON_VAL_NUMBER); \
119 	CU_ASSERT(g_cur_param->len == sizeof(num) - 1); \
120 	CU_ASSERT(memcmp(g_cur_param->start, num, sizeof(num) - 1) == 0); \
121 	g_cur_param++
122 
123 #define PARAM_NAME(str) \
124 	CU_ASSERT(g_cur_param->type == SPDK_JSON_VAL_NAME); \
125 	CU_ASSERT(g_cur_param->len == sizeof(str) - 1); \
126 	CU_ASSERT(g_cur_param && memcmp(g_cur_param->start, str, sizeof(str) - 1) == 0); \
127 	g_cur_param++
128 
129 #define PARAM_STRING(str) \
130 	CU_ASSERT(g_cur_param->type == SPDK_JSON_VAL_STRING); \
131 	CU_ASSERT(g_cur_param->len == sizeof(str) - 1); \
132 	CU_ASSERT(memcmp(g_cur_param->start, str, g_params->len) == 0); \
133 	g_cur_param++
134 
135 #define FREE_REQUEST() \
136 	ut_jsonrpc_free_request(g_request, g_parse_error); \
137 	g_request = NULL; \
138 	g_cur_param = NULL; \
139 	g_parse_error = 0; \
140 	g_method = NULL; \
141 	g_cur_param = g_params = NULL
142 
143 static void
144 ut_jsonrpc_free_request(struct spdk_jsonrpc_request *request, int err)
145 {
146 	struct spdk_json_write_ctx *w;
147 
148 	if (!request) {
149 		return;
150 	}
151 
152 	/* Need to emulate response to get the response write contex free */
153 	if (err == 0) {
154 		w = spdk_jsonrpc_begin_result(request);
155 		spdk_json_write_string(w, "UT PASS response");
156 		spdk_jsonrpc_end_result(request, w);
157 	} else {
158 		spdk_jsonrpc_send_error_response_fmt(request, err, "UT error response");
159 	}
160 
161 	spdk_jsonrpc_free_request(request);
162 }
163 
164 static void
165 ut_handle(struct spdk_jsonrpc_request *request, int error, const struct spdk_json_val *method,
166 	  const struct spdk_json_val *params)
167 {
168 	CU_ASSERT(g_request == NULL);
169 	g_request = request;
170 	g_parse_error = error;
171 	g_method = method;
172 	g_params = params;
173 }
174 
175 void
176 spdk_jsonrpc_server_handle_error(struct spdk_jsonrpc_request *request, int error)
177 {
178 	ut_handle(request, error, NULL, NULL);
179 }
180 
181 void
182 spdk_jsonrpc_server_handle_request(struct spdk_jsonrpc_request *request,
183 				   const struct spdk_json_val *method, const struct spdk_json_val *params)
184 {
185 	ut_handle(request, 0, method, params);
186 }
187 
188 void
189 spdk_jsonrpc_server_send_response(struct spdk_jsonrpc_request *request)
190 {
191 }
192 
193 static void
194 test_parse_request(void)
195 {
196 	struct spdk_jsonrpc_server *server;
197 	struct spdk_jsonrpc_server_conn *conn;
198 
199 	server = calloc(1, sizeof(*server));
200 	SPDK_CU_ASSERT_FATAL(server != NULL);
201 
202 	conn = calloc(1, sizeof(*conn));
203 	SPDK_CU_ASSERT_FATAL(conn != NULL);
204 
205 	conn->server = server;
206 
207 	/* rpc call with no parameters. */
208 	PARSE_PASS("{   }", "");
209 	REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_INVALID_REQUEST);
210 	FREE_REQUEST();
211 
212 	/* rpc call with method that is not a string. */
213 	PARSE_PASS("{\"jsonrpc\":\"2.0\", \"method\": null  }", "");
214 	REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_INVALID_REQUEST);
215 	FREE_REQUEST();
216 
217 	/* rpc call with invalid JSON RPC version. */
218 	PARSE_PASS("{\"jsonrpc\":\"42\", \"method\": \"subtract\"}", "");
219 	REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_INVALID_REQUEST);
220 	FREE_REQUEST();
221 
222 	/* rpc call with embedded zeros. */
223 	PARSE_FAIL("{\"jsonrpc\":\"2.0\",\"method\":\"foo\",\"params\":{\"bar\": \"\0\0baz\"}}");
224 	REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_PARSE_ERROR);
225 	FREE_REQUEST();
226 
227 	/* rpc call with positional parameters */
228 	PARSE_PASS("{\"jsonrpc\":\"2.0\",\"method\":\"subtract\",\"params\":[42,23],\"id\":1}", "");
229 	REQ_BEGIN_VALID();
230 	REQ_METHOD("subtract");
231 	REQ_ID_NUM("1");
232 	REQ_PARAMS_BEGIN();
233 	PARAM_ARRAY_BEGIN();
234 	PARAM_NUM("42");
235 	PARAM_NUM("23");
236 	PARAM_ARRAY_END();
237 	FREE_REQUEST();
238 
239 	/* rpc call with named parameters */
240 	PARSE_PASS("{\"jsonrpc\": \"2.0\", \"method\": \"subtract\", \"params\": {\"subtrahend\": 23, \"minuend\": 42}, \"id\": 3}",
241 		   "");
242 	REQ_BEGIN_VALID();
243 	REQ_METHOD("subtract");
244 	REQ_ID_NUM("3");
245 	REQ_PARAMS_BEGIN();
246 	PARAM_OBJECT_BEGIN();
247 	PARAM_NAME("subtrahend");
248 	PARAM_NUM("23");
249 	PARAM_NAME("minuend");
250 	PARAM_NUM("42");
251 	PARAM_OBJECT_END();
252 	FREE_REQUEST();
253 
254 	/* notification */
255 	PARSE_PASS("{\"jsonrpc\": \"2.0\", \"method\": \"update\", \"params\": [1,2,3,4,5]}", "");
256 	REQ_BEGIN_VALID();
257 	REQ_METHOD("update");
258 	REQ_ID_MISSING();
259 	REQ_PARAMS_BEGIN();
260 	PARAM_ARRAY_BEGIN();
261 	PARAM_NUM("1");
262 	PARAM_NUM("2");
263 	PARAM_NUM("3");
264 	PARAM_NUM("4");
265 	PARAM_NUM("5");
266 	PARAM_ARRAY_END();
267 	FREE_REQUEST();
268 
269 	/* notification with explicit NULL ID. This is discouraged by JSON RPC spec but allowed. */
270 	PARSE_PASS("{\"jsonrpc\": \"2.0\", \"method\": \"update\", \"params\": [1,2,3,4,5], \"id\": null}",
271 		   "");
272 	REQ_BEGIN_VALID();
273 	REQ_METHOD("update");
274 	REQ_ID_NULL();
275 	REQ_PARAMS_BEGIN();
276 	PARAM_ARRAY_BEGIN();
277 	PARAM_NUM("1");
278 	PARAM_NUM("2");
279 	PARAM_NUM("3");
280 	PARAM_NUM("4");
281 	PARAM_NUM("5");
282 	PARAM_ARRAY_END();
283 	FREE_REQUEST();
284 
285 	/* invalid JSON */
286 	PARSE_FAIL("{\"jsonrpc\": \"2.0\", \"method\": \"foobar, \"params\": \"bar\", \"baz]");
287 	REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_PARSE_ERROR);
288 	FREE_REQUEST();
289 
290 	/* invalid request (method must be a string; params must be array or object) */
291 	PARSE_PASS("{\"jsonrpc\": \"2.0\", \"method\": 1, \"params\": \"bar\"}", "");
292 	REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_INVALID_REQUEST);
293 	FREE_REQUEST();
294 
295 	/* batch, invalid JSON */
296 	PARSE_FAIL(
297 		"["
298 		"{\"jsonrpc\": \"2.0\", \"method\": \"sum\", \"params\": [1,2,4], \"id\": \"1\"},"
299 		"{\"jsonrpc\": \"2.0\", \"method\""
300 		"]");
301 	REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_PARSE_ERROR);
302 	FREE_REQUEST();
303 
304 	/* empty array */
305 	PARSE_PASS("[]", "");
306 	REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_INVALID_REQUEST);
307 	FREE_REQUEST();
308 
309 	/* batch - not supported */
310 	PARSE_PASS(
311 		"["
312 		"{\"jsonrpc\": \"2.0\", \"method\": \"sum\", \"params\": [1,2,4], \"id\": \"1\"},"
313 		"{\"jsonrpc\": \"2.0\", \"method\": \"notify_hello\", \"params\": [7]},"
314 		"{\"jsonrpc\": \"2.0\", \"method\": \"subtract\", \"params\": [42,23], \"id\": \"2\"},"
315 		"{\"foo\": \"boo\"},"
316 		"{\"jsonrpc\": \"2.0\", \"method\": \"foo.get\", \"params\": {\"name\": \"myself\"}, \"id\": \"5\"},"
317 		"{\"jsonrpc\": \"2.0\", \"method\": \"get_data\", \"id\": \"9\"}"
318 		"]", "");
319 
320 	REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_INVALID_REQUEST);
321 	FREE_REQUEST();
322 
323 	CU_ASSERT(conn->outstanding_requests == 0);
324 	free(conn);
325 	free(server);
326 }
327 
328 static void
329 test_parse_request_streaming(void)
330 {
331 	struct spdk_jsonrpc_server *server;
332 	struct spdk_jsonrpc_server_conn *conn;
333 	const char *json_req;
334 	size_t len, i;
335 
336 	server = calloc(1, sizeof(*server));
337 	SPDK_CU_ASSERT_FATAL(server != NULL);
338 
339 	conn = calloc(1, sizeof(*conn));
340 	SPDK_CU_ASSERT_FATAL(conn != NULL);
341 
342 	conn->server = server;
343 
344 
345 	/*
346 	 * Two valid requests end to end in the same buffer.
347 	 * Parse should return the first one and point to the beginning of the second one.
348 	 */
349 	PARSE_PASS(
350 		"{\"jsonrpc\":\"2.0\",\"method\":\"a\",\"params\":[1],\"id\":1}"
351 		"{\"jsonrpc\":\"2.0\",\"method\":\"b\",\"params\":[2],\"id\":2}",
352 		"{\"jsonrpc\":\"2.0\",\"method\":\"b\",\"params\":[2],\"id\":2}");
353 
354 	REQ_BEGIN_VALID();
355 	REQ_METHOD("a");
356 	REQ_ID_NUM("1");
357 	REQ_PARAMS_BEGIN();
358 	PARAM_ARRAY_BEGIN();
359 	PARAM_NUM("1");
360 	PARAM_ARRAY_END();
361 	FREE_REQUEST();
362 
363 	/* Partial (but not invalid) requests - parse should not consume anything. */
364 	json_req = "    {\"jsonrpc\":\"2.0\",\"method\":\"b\",\"params\":[2],\"id\":2}";
365 	len = strlen(json_req);
366 
367 	/* Try every partial length up to the full request length */
368 	for (i = 0; i < len; i++) {
369 		int rc = spdk_jsonrpc_parse_request(conn, json_req, i);
370 		/* Partial request - no data consumed */
371 		CU_ASSERT(rc == 0);
372 		CU_ASSERT(g_request == NULL);
373 
374 		/* In case of faile, don't fload console with ussless CU assert fails. */
375 		FREE_REQUEST();
376 	}
377 
378 	/* Verify that full request can be parsed successfully */
379 	CU_ASSERT(spdk_jsonrpc_parse_request(conn, json_req, len) == (ssize_t)len);
380 	FREE_REQUEST();
381 
382 	CU_ASSERT(conn->outstanding_requests == 0);
383 	free(conn);
384 	free(server);
385 }
386 
387 int main(int argc, char **argv)
388 {
389 	CU_pSuite	suite = NULL;
390 	unsigned int	num_failures;
391 
392 	if (CU_initialize_registry() != CUE_SUCCESS) {
393 		return CU_get_error();
394 	}
395 
396 	suite = CU_add_suite("jsonrpc", NULL, NULL);
397 	if (suite == NULL) {
398 		CU_cleanup_registry();
399 		return CU_get_error();
400 	}
401 
402 	if (
403 		CU_add_test(suite, "parse_request", test_parse_request) == NULL ||
404 		CU_add_test(suite, "parse_request_streaming", test_parse_request_streaming) == NULL) {
405 		CU_cleanup_registry();
406 		return CU_get_error();
407 	}
408 	CU_basic_set_mode(CU_BRM_VERBOSE);
409 
410 	CU_basic_run_tests();
411 
412 	num_failures = CU_get_number_of_failures();
413 	CU_cleanup_registry();
414 
415 	/* This is for ASAN. Don't know why but if pointer is left in global varaible
416 	 * it won't be detected as leak. */
417 	g_request = NULL;
418 	return num_failures;
419 }
420