4 * Copyright (c) Intel Corporation.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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
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.
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.
34 #include "spdk/stdinc.h"
36 #include "spdk_cunit.h"
38 #include "jsonrpc/jsonrpc_server.c"
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
;
45 const struct spdk_json_val
*g_cur_param
;
47 #define PARSE_PASS(in, trailing) \
48 CU_ASSERT(g_cur_param == NULL); \
50 CU_ASSERT(jsonrpc_parse_request(conn, in, sizeof(in) - 1) == sizeof(in) - sizeof(trailing))
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); \
58 #define PARSE_FAIL(in) \
59 CU_ASSERT(jsonrpc_parse_request(conn, in, sizeof(in) - 1) < 0);
61 #define REQ_BEGIN_VALID() \
63 SPDK_CU_ASSERT_FATAL(g_params != NULL);
65 #define REQ_BEGIN_INVALID(expected_error) \
66 REQ_BEGIN(expected_error); \
67 REQ_METHOD_MISSING(); \
72 #define REQ_METHOD(name) \
73 CU_ASSERT(g_method && spdk_json_strequal(g_method, name) == true)
75 #define REQ_METHOD_MISSING() \
76 CU_ASSERT(g_method == NULL)
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)
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))
87 #define REQ_ID_NULL() \
88 CU_ASSERT(g_request->id && g_request->id->type == SPDK_JSON_VAL_NULL)
90 #define REQ_ID_MISSING() \
91 CU_ASSERT(g_request->id == NULL)
93 #define REQ_PARAMS_MISSING() \
94 CU_ASSERT(g_params == NULL)
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
101 #define PARAM_ARRAY_BEGIN() \
102 CU_ASSERT(g_cur_param->type == SPDK_JSON_VAL_ARRAY_BEGIN); \
105 #define PARAM_ARRAY_END() \
106 CU_ASSERT(g_cur_param->type == SPDK_JSON_VAL_ARRAY_END); \
109 #define PARAM_OBJECT_BEGIN() \
110 CU_ASSERT(g_cur_param->type == SPDK_JSON_VAL_OBJECT_BEGIN); \
113 #define PARAM_OBJECT_END() \
114 CU_ASSERT(g_cur_param->type == SPDK_JSON_VAL_OBJECT_END); \
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); \
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); \
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); \
135 #define FREE_REQUEST() \
136 ut_jsonrpc_free_request(g_request, g_parse_error); \
138 g_cur_param = NULL; \
141 g_cur_param = g_params = NULL
144 ut_jsonrpc_free_request(struct spdk_jsonrpc_request
*request
, int err
)
146 struct spdk_json_write_ctx
*w
;
152 /* Need to emulate response to get the response write contex free */
154 w
= spdk_jsonrpc_begin_result(request
);
155 spdk_json_write_string(w
, "UT PASS response");
156 spdk_jsonrpc_end_result(request
, w
);
158 spdk_jsonrpc_send_error_response_fmt(request
, err
, "UT error response");
161 jsonrpc_free_request(request
);
165 ut_handle(struct spdk_jsonrpc_request
*request
, int error
, const struct spdk_json_val
*method
,
166 const struct spdk_json_val
*params
)
168 CU_ASSERT(g_request
== NULL
);
170 g_parse_error
= error
;
176 jsonrpc_server_handle_error(struct spdk_jsonrpc_request
*request
, int error
)
178 ut_handle(request
, error
, NULL
, NULL
);
182 jsonrpc_server_handle_request(struct spdk_jsonrpc_request
*request
,
183 const struct spdk_json_val
*method
, const struct spdk_json_val
*params
)
185 ut_handle(request
, 0, method
, params
);
189 jsonrpc_server_send_response(struct spdk_jsonrpc_request
*request
)
194 test_parse_request(void)
196 struct spdk_jsonrpc_server
*server
;
197 struct spdk_jsonrpc_server_conn
*conn
;
199 server
= calloc(1, sizeof(*server
));
200 SPDK_CU_ASSERT_FATAL(server
!= NULL
);
202 conn
= calloc(1, sizeof(*conn
));
203 SPDK_CU_ASSERT_FATAL(conn
!= NULL
);
205 conn
->server
= server
;
207 /* rpc call with no parameters. */
208 PARSE_PASS("{ }", "");
209 REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_INVALID_REQUEST
);
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
);
217 /* rpc call with invalid JSON RPC version. */
218 PARSE_PASS("{\"jsonrpc\":\"42\", \"method\": \"subtract\"}", "");
219 REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_INVALID_REQUEST
);
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
);
227 /* rpc call with positional parameters */
228 PARSE_PASS("{\"jsonrpc\":\"2.0\",\"method\":\"subtract\",\"params\":[42,23],\"id\":1}", "");
230 REQ_METHOD("subtract");
239 /* rpc call with named parameters */
240 PARSE_PASS("{\"jsonrpc\": \"2.0\", \"method\": \"subtract\", \"params\": {\"subtrahend\": 23, \"minuend\": 42}, \"id\": 3}",
243 REQ_METHOD("subtract");
246 PARAM_OBJECT_BEGIN();
247 PARAM_NAME("subtrahend");
249 PARAM_NAME("minuend");
255 PARSE_PASS("{\"jsonrpc\": \"2.0\", \"method\": \"update\", \"params\": [1,2,3,4,5]}", "");
257 REQ_METHOD("update");
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}",
273 REQ_METHOD("update");
286 PARSE_FAIL("{\"jsonrpc\": \"2.0\", \"method\": \"foobar, \"params\": \"bar\", \"baz]");
287 REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_PARSE_ERROR
);
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
);
295 /* batch, invalid JSON */
298 "{\"jsonrpc\": \"2.0\", \"method\": \"sum\", \"params\": [1,2,4], \"id\": \"1\"},"
299 "{\"jsonrpc\": \"2.0\", \"method\""
301 REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_PARSE_ERROR
);
305 PARSE_PASS("[]", "");
306 REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_INVALID_REQUEST
);
309 /* batch - not supported */
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\"}"
320 REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_INVALID_REQUEST
);
323 CU_ASSERT(conn
->outstanding_requests
== 0);
329 test_parse_request_streaming(void)
331 struct spdk_jsonrpc_server
*server
;
332 struct spdk_jsonrpc_server_conn
*conn
;
333 const char *json_req
;
336 server
= calloc(1, sizeof(*server
));
337 SPDK_CU_ASSERT_FATAL(server
!= NULL
);
339 conn
= calloc(1, sizeof(*conn
));
340 SPDK_CU_ASSERT_FATAL(conn
!= NULL
);
342 conn
->server
= server
;
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.
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}");
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
);
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 */
372 CU_ASSERT(g_request
== NULL
);
374 /* In case of faile, don't fload console with ussless CU assert fails. */
378 /* Verify that full request can be parsed successfully */
379 CU_ASSERT(jsonrpc_parse_request(conn
, json_req
, len
) == (ssize_t
)len
);
382 CU_ASSERT(conn
->outstanding_requests
== 0);
387 int main(int argc
, char **argv
)
389 CU_pSuite suite
= NULL
;
390 unsigned int num_failures
;
392 CU_set_error_action(CUEA_ABORT
);
393 CU_initialize_registry();
395 suite
= CU_add_suite("jsonrpc", NULL
, NULL
);
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
);
401 CU_basic_run_tests();
403 num_failures
= CU_get_number_of_failures();
404 CU_cleanup_registry();
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. */