]> git.proxmox.com Git - ceph.git/blob - ceph/src/spdk/test/unit/lib/jsonrpc/jsonrpc_server.c/jsonrpc_server_ut.c
update source to Ceph Pacific 16.2.2
[ceph.git] / ceph / src / spdk / test / unit / lib / jsonrpc / jsonrpc_server.c / jsonrpc_server_ut.c
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(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(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 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 jsonrpc_server_handle_error(struct spdk_jsonrpc_request *request, int error)
177 {
178 ut_handle(request, error, NULL, NULL);
179 }
180
181 void
182 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 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 = 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(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 CU_set_error_action(CUEA_ABORT);
393 CU_initialize_registry();
394
395 suite = CU_add_suite("jsonrpc", NULL, NULL);
396
397 CU_ADD_TEST(suite, test_parse_request);
398 CU_ADD_TEST(suite, test_parse_request_streaming);
399 CU_basic_set_mode(CU_BRM_VERBOSE);
400
401 CU_basic_run_tests();
402
403 num_failures = CU_get_number_of_failures();
404 CU_cleanup_registry();
405
406 /* This is for ASAN. Don't know why but if pointer is left in global varaible
407 * it won't be detected as leak. */
408 g_request = NULL;
409 return num_failures;
410 }