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 #define MAX_PARAMS 100 41 #define MAX_REQS 100 42 43 struct req { 44 int error; 45 bool got_method; 46 bool got_id; 47 bool got_params; 48 struct spdk_jsonrpc_request *request; 49 struct spdk_json_val method; 50 struct spdk_json_val id; 51 struct spdk_json_val params[MAX_PARAMS]; 52 }; 53 54 static uint8_t g_buf[1000]; 55 static struct req g_reqs[MAX_REQS]; 56 static struct req *g_cur_req; 57 static struct spdk_json_val *g_params; 58 static size_t g_num_reqs; 59 60 #define PARSE_PASS(in, trailing) \ 61 memcpy(g_buf, in, sizeof(in) - 1); \ 62 g_num_reqs = 0; \ 63 g_cur_req = NULL; \ 64 CU_ASSERT(spdk_jsonrpc_parse_request(conn, g_buf, sizeof(in) - 1) == sizeof(in) - sizeof(trailing)); \ 65 if (g_cur_req && g_cur_req->request) { \ 66 free(g_cur_req->request->send_buf); \ 67 g_cur_req->request->send_buf = NULL; \ 68 } 69 70 #define PARSE_FAIL(in) \ 71 memcpy(g_buf, in, sizeof(in) - 1); \ 72 g_num_reqs = 0; \ 73 g_cur_req = 0; \ 74 CU_ASSERT(spdk_jsonrpc_parse_request(conn, g_buf, sizeof(in) - 1) < 0); \ 75 if (g_cur_req && g_cur_req->request) { \ 76 free(g_cur_req->request->send_buf); \ 77 g_cur_req->request->send_buf = NULL; \ 78 } 79 80 #define REQ_BEGIN(expected_error) \ 81 if (g_cur_req == NULL) { \ 82 g_cur_req = g_reqs; \ 83 } else { \ 84 g_cur_req++; \ 85 } \ 86 CU_ASSERT(g_cur_req - g_reqs <= (ptrdiff_t)g_num_reqs); \ 87 CU_ASSERT(g_cur_req->error == expected_error) 88 89 #define REQ_BEGIN_VALID() REQ_BEGIN(0) 90 #define REQ_BEGIN_INVALID(expected_error) REQ_BEGIN(expected_error) 91 92 #define REQ_METHOD(name) \ 93 CU_ASSERT(g_cur_req->got_method); \ 94 CU_ASSERT(spdk_json_strequal(&g_cur_req->method, name) == true) 95 96 #define REQ_METHOD_MISSING() \ 97 CU_ASSERT(g_cur_req->got_method == false) 98 99 #define REQ_ID_NUM(num) \ 100 CU_ASSERT(g_cur_req->got_id); \ 101 CU_ASSERT(g_cur_req->id.type == SPDK_JSON_VAL_NUMBER); \ 102 CU_ASSERT(memcmp(g_cur_req->id.start, num, sizeof(num) - 1) == 0) 103 104 #define REQ_ID_STRING(str) \ 105 CU_ASSERT(g_cur_req->got_id); \ 106 CU_ASSERT(g_cur_req->id.type == SPDK_JSON_VAL_STRING); \ 107 CU_ASSERT(memcmp(g_cur_req->id.start, str, sizeof(str) - 1) == 0) 108 109 #define REQ_ID_NULL() \ 110 CU_ASSERT(g_cur_req->got_id); \ 111 CU_ASSERT(g_cur_req->id.type == SPDK_JSON_VAL_NULL) 112 113 #define REQ_ID_MISSING() \ 114 CU_ASSERT(g_cur_req->got_id == false) 115 116 #define REQ_PARAMS_MISSING() \ 117 CU_ASSERT(g_cur_req->got_params == false) 118 119 #define REQ_PARAMS_BEGIN() \ 120 CU_ASSERT(g_cur_req->got_params); \ 121 g_params = g_cur_req->params 122 123 #define PARAM_ARRAY_BEGIN() \ 124 CU_ASSERT(g_params->type == SPDK_JSON_VAL_ARRAY_BEGIN); \ 125 g_params++ 126 127 #define PARAM_ARRAY_END() \ 128 CU_ASSERT(g_params->type == SPDK_JSON_VAL_ARRAY_END); \ 129 g_params++ 130 131 #define PARAM_OBJECT_BEGIN() \ 132 CU_ASSERT(g_params->type == SPDK_JSON_VAL_OBJECT_BEGIN); \ 133 g_params++ 134 135 #define PARAM_OBJECT_END() \ 136 CU_ASSERT(g_params->type == SPDK_JSON_VAL_OBJECT_END); \ 137 g_params++ 138 139 #define PARAM_NUM(num) \ 140 CU_ASSERT(g_params->type == SPDK_JSON_VAL_NUMBER); \ 141 CU_ASSERT(g_params->len == sizeof(num) - 1); \ 142 CU_ASSERT(memcmp(g_params->start, num, g_params->len) == 0); \ 143 g_params++ 144 145 #define PARAM_NAME(str) \ 146 CU_ASSERT(g_params->type == SPDK_JSON_VAL_NAME); \ 147 CU_ASSERT(g_params->len == sizeof(str) - 1); \ 148 CU_ASSERT(memcmp(g_params->start, str, g_params->len) == 0); \ 149 g_params++ 150 151 #define PARAM_STRING(str) \ 152 CU_ASSERT(g_params->type == SPDK_JSON_VAL_STRING); \ 153 CU_ASSERT(g_params->len == sizeof(str) - 1); \ 154 CU_ASSERT(memcmp(g_params->start, str, g_params->len) == 0); \ 155 g_params++ 156 157 #define FREE_REQUEST() \ 158 if (g_reqs->request) { \ 159 free(g_reqs->request->send_buf); \ 160 } \ 161 free(g_reqs->request); \ 162 g_reqs->request = NULL 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 const struct spdk_json_val *id = &request->id; 169 struct req *r; 170 171 SPDK_CU_ASSERT_FATAL(g_num_reqs != MAX_REQS); 172 r = &g_reqs[g_num_reqs++]; 173 174 r->request = request; 175 r->error = error; 176 177 if (method) { 178 r->got_method = true; 179 r->method = *method; 180 } else { 181 r->got_method = false; 182 } 183 184 if (params) { 185 r->got_params = true; 186 SPDK_CU_ASSERT_FATAL(spdk_json_val_len(params) < MAX_PARAMS); 187 memcpy(r->params, params, spdk_json_val_len(params) * sizeof(struct spdk_json_val)); 188 } else { 189 r->got_params = false; 190 } 191 192 if (id && id->type != SPDK_JSON_VAL_INVALID) { 193 r->got_id = true; 194 r->id = *id; 195 } else { 196 r->got_id = false; 197 } 198 } 199 200 void 201 spdk_jsonrpc_server_handle_error(struct spdk_jsonrpc_request *request, int error) 202 { 203 /* 204 * Map missing id to Null - this mirrors the behavior in the real 205 * spdk_jsonrpc_server_handle_error() function. 206 */ 207 if (request->id.type == SPDK_JSON_VAL_INVALID) { 208 request->id.type = SPDK_JSON_VAL_NULL; 209 } 210 211 ut_handle(request, error, NULL, NULL); 212 } 213 214 void 215 spdk_jsonrpc_server_handle_request(struct spdk_jsonrpc_request *request, 216 const struct spdk_json_val *method, const struct spdk_json_val *params) 217 { 218 ut_handle(request, 0, method, params); 219 } 220 221 void 222 spdk_jsonrpc_server_send_response(struct spdk_jsonrpc_server_conn *conn, 223 struct spdk_jsonrpc_request *request) 224 { 225 /* TODO */ 226 } 227 228 static void 229 test_parse_request(void) 230 { 231 struct spdk_jsonrpc_server *server; 232 struct spdk_jsonrpc_server_conn *conn; 233 234 server = calloc(1, sizeof(*server)); 235 SPDK_CU_ASSERT_FATAL(server != NULL); 236 237 conn = calloc(1, sizeof(*conn)); 238 SPDK_CU_ASSERT_FATAL(conn != NULL); 239 240 conn->server = server; 241 242 /* rpc call with positional parameters */ 243 PARSE_PASS("{\"jsonrpc\":\"2.0\",\"method\":\"subtract\",\"params\":[42,23],\"id\":1}", ""); 244 REQ_BEGIN_VALID(); 245 REQ_METHOD("subtract"); 246 REQ_ID_NUM("1"); 247 REQ_PARAMS_BEGIN(); 248 PARAM_ARRAY_BEGIN(); 249 PARAM_NUM("42"); 250 PARAM_NUM("23"); 251 PARAM_ARRAY_END(); 252 FREE_REQUEST(); 253 254 /* rpc call with named parameters */ 255 PARSE_PASS("{\"jsonrpc\": \"2.0\", \"method\": \"subtract\", \"params\": {\"subtrahend\": 23, \"minuend\": 42}, \"id\": 3}", 256 ""); 257 REQ_BEGIN_VALID(); 258 REQ_METHOD("subtract"); 259 REQ_ID_NUM("3"); 260 REQ_PARAMS_BEGIN(); 261 PARAM_OBJECT_BEGIN(); 262 PARAM_NAME("subtrahend"); 263 PARAM_NUM("23"); 264 PARAM_NAME("minuend"); 265 PARAM_NUM("42"); 266 PARAM_OBJECT_END(); 267 FREE_REQUEST(); 268 269 /* notification */ 270 PARSE_PASS("{\"jsonrpc\": \"2.0\", \"method\": \"update\", \"params\": [1,2,3,4,5]}", ""); 271 REQ_BEGIN_VALID(); 272 REQ_METHOD("update"); 273 REQ_ID_MISSING(); 274 REQ_PARAMS_BEGIN(); 275 PARAM_ARRAY_BEGIN(); 276 PARAM_NUM("1"); 277 PARAM_NUM("2"); 278 PARAM_NUM("3"); 279 PARAM_NUM("4"); 280 PARAM_NUM("5"); 281 PARAM_ARRAY_END(); 282 FREE_REQUEST(); 283 284 /* invalid JSON */ 285 PARSE_FAIL("{\"jsonrpc\": \"2.0\", \"method\": \"foobar, \"params\": \"bar\", \"baz]"); 286 REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_PARSE_ERROR); 287 REQ_METHOD_MISSING(); 288 REQ_ID_NULL(); 289 REQ_PARAMS_MISSING(); 290 FREE_REQUEST(); 291 292 /* invalid request (method must be a string; params must be array or object) */ 293 PARSE_PASS("{\"jsonrpc\": \"2.0\", \"method\": 1, \"params\": \"bar\"}", ""); 294 REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_INVALID_REQUEST); 295 REQ_METHOD_MISSING(); 296 REQ_ID_NULL(); 297 REQ_PARAMS_MISSING(); 298 FREE_REQUEST(); 299 300 /* batch, invalid JSON */ 301 PARSE_FAIL( 302 "[" 303 "{\"jsonrpc\": \"2.0\", \"method\": \"sum\", \"params\": [1,2,4], \"id\": \"1\"}," 304 "{\"jsonrpc\": \"2.0\", \"method\"" 305 "]"); 306 REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_PARSE_ERROR); 307 REQ_METHOD_MISSING(); 308 REQ_ID_NULL(); 309 REQ_PARAMS_MISSING(); 310 FREE_REQUEST(); 311 312 /* empty array */ 313 PARSE_PASS("[]", ""); 314 REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_INVALID_REQUEST); 315 REQ_METHOD_MISSING(); 316 REQ_ID_NULL(); 317 REQ_PARAMS_MISSING(); 318 FREE_REQUEST(); 319 320 /* batch - not supported */ 321 PARSE_PASS( 322 "[" 323 "{\"jsonrpc\": \"2.0\", \"method\": \"sum\", \"params\": [1,2,4], \"id\": \"1\"}," 324 "{\"jsonrpc\": \"2.0\", \"method\": \"notify_hello\", \"params\": [7]}," 325 "{\"jsonrpc\": \"2.0\", \"method\": \"subtract\", \"params\": [42,23], \"id\": \"2\"}," 326 "{\"foo\": \"boo\"}," 327 "{\"jsonrpc\": \"2.0\", \"method\": \"foo.get\", \"params\": {\"name\": \"myself\"}, \"id\": \"5\"}," 328 "{\"jsonrpc\": \"2.0\", \"method\": \"get_data\", \"id\": \"9\"}" 329 "]", ""); 330 331 REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_INVALID_REQUEST); 332 REQ_METHOD_MISSING(); 333 REQ_ID_NULL(); 334 REQ_PARAMS_MISSING(); 335 FREE_REQUEST(); 336 337 free(conn); 338 free(server); 339 } 340 341 static void 342 test_parse_request_streaming(void) 343 { 344 struct spdk_jsonrpc_server *server; 345 struct spdk_jsonrpc_server_conn *conn; 346 size_t len, i; 347 348 server = calloc(1, sizeof(*server)); 349 SPDK_CU_ASSERT_FATAL(server != NULL); 350 351 conn = calloc(1, sizeof(*conn)); 352 SPDK_CU_ASSERT_FATAL(conn != NULL); 353 354 conn->server = server; 355 356 /* 357 * Two valid requests end to end in the same buffer. 358 * Parse should return the first one and point to the beginning of the second one. 359 */ 360 PARSE_PASS( 361 "{\"jsonrpc\":\"2.0\",\"method\":\"a\",\"params\":[1],\"id\":1}" 362 "{\"jsonrpc\":\"2.0\",\"method\":\"b\",\"params\":[2],\"id\":2}", 363 "{\"jsonrpc\":\"2.0\",\"method\":\"b\",\"params\":[2],\"id\":2}"); 364 REQ_BEGIN_VALID(); 365 REQ_METHOD("a"); 366 REQ_ID_NUM("1"); 367 REQ_PARAMS_BEGIN(); 368 PARAM_ARRAY_BEGIN(); 369 PARAM_NUM("1"); 370 PARAM_ARRAY_END(); 371 FREE_REQUEST(); 372 373 /* Partial (but not invalid) requests - parse should not consume anything. */ 374 snprintf(g_buf, sizeof(g_buf), "%s", 375 "{\"jsonrpc\":\"2.0\",\"method\":\"b\",\"params\":[2],\"id\":2}"); 376 len = strlen(g_buf); 377 378 /* Try every partial length up to the full request length */ 379 for (i = 0; i < len; i++) { 380 int rc = spdk_jsonrpc_parse_request(conn, g_buf, i); 381 /* Partial request - no data consumed */ 382 CU_ASSERT(rc == 0); 383 FREE_REQUEST(); 384 } 385 386 /* Verify that full request can be parsed successfully */ 387 CU_ASSERT(spdk_jsonrpc_parse_request(conn, g_buf, len) == (ssize_t)len); 388 FREE_REQUEST(); 389 390 free(conn); 391 free(server); 392 } 393 394 int main(int argc, char **argv) 395 { 396 CU_pSuite suite = NULL; 397 unsigned int num_failures; 398 399 if (CU_initialize_registry() != CUE_SUCCESS) { 400 return CU_get_error(); 401 } 402 403 suite = CU_add_suite("jsonrpc", NULL, NULL); 404 if (suite == NULL) { 405 CU_cleanup_registry(); 406 return CU_get_error(); 407 } 408 409 if ( 410 CU_add_test(suite, "parse_request", test_parse_request) == NULL || 411 CU_add_test(suite, "parse_request_streaming", test_parse_request_streaming) == NULL) { 412 CU_cleanup_registry(); 413 return CU_get_error(); 414 } 415 416 CU_basic_set_mode(CU_BRM_VERBOSE); 417 418 CU_basic_run_tests(); 419 420 num_failures = CU_get_number_of_failures(); 421 CU_cleanup_registry(); 422 423 return num_failures; 424 } 425