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