1 /* 2 * Copyright (c) 2003-2006 Niels Provos <provos (at) citi.umich.edu> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #ifdef WIN32 29 #include <winsock2.h> 30 #include <windows.h> 31 #endif 32 33 #ifdef HAVE_CONFIG_H 34 #include "config.h" 35 #endif 36 37 #include <sys/types.h> 38 #include <sys/stat.h> 39 #ifdef HAVE_SYS_TIME_H 40 #include <sys/time.h> 41 #endif 42 #include <sys/queue.h> 43 #ifndef WIN32 44 #include <sys/socket.h> 45 #include <signal.h> 46 #include <unistd.h> 47 #include <netdb.h> 48 #endif 49 #include <fcntl.h> 50 #include <stdlib.h> 51 #include <stdio.h> 52 #include <string.h> 53 #include <errno.h> 54 #include <assert.h> 55 56 #include "event.h" 57 #include "evhttp.h" 58 #include "log.h" 59 #include "evrpc.h" 60 61 #include "regress.gen.h" 62 63 void rpc_suite(void); 64 65 extern int test_ok; 66 67 static struct evhttp * 68 http_setup(short *pport) 69 { 70 int i; 71 struct evhttp *myhttp; 72 short port = -1; 73 74 /* Try a few different ports */ 75 for (i = 0; i < 50; ++i) { 76 myhttp = evhttp_start("127.0.0.1", 8080 + i); 77 if (myhttp != NULL) { 78 port = 8080 + i; 79 break; 80 } 81 } 82 83 if (port == -1) 84 event_errx(1, "Could not start web server"); 85 86 *pport = port; 87 return (myhttp); 88 } 89 90 EVRPC_HEADER(Message, msg, kill); 91 EVRPC_HEADER(NeverReply, msg, kill); 92 93 EVRPC_GENERATE(Message, msg, kill); 94 EVRPC_GENERATE(NeverReply, msg, kill); 95 96 static int need_input_hook = 0; 97 static int need_output_hook = 0; 98 99 static void 100 MessageCb(EVRPC_STRUCT(Message)* rpc, void *arg) 101 { 102 struct kill* kill_reply = rpc->reply; 103 104 if (need_input_hook) { 105 struct evhttp_request* req = EVRPC_REQUEST_HTTP(rpc); 106 const char *header = evhttp_find_header( 107 req->input_headers, "X-Hook"); 108 assert(strcmp(header, "input") == 0); 109 } 110 111 /* we just want to fill in some non-sense */ 112 EVTAG_ASSIGN(kill_reply, weapon, "dagger"); 113 EVTAG_ASSIGN(kill_reply, action, "wave around like an idiot"); 114 115 /* no reply to the RPC */ 116 EVRPC_REQUEST_DONE(rpc); 117 } 118 119 static EVRPC_STRUCT(NeverReply) *saved_rpc; 120 121 static void 122 NeverReplyCb(EVRPC_STRUCT(NeverReply)* rpc, void *arg) 123 { 124 test_ok += 1; 125 saved_rpc = rpc; 126 } 127 128 static void 129 rpc_setup(struct evhttp **phttp, short *pport, struct evrpc_base **pbase) 130 { 131 short port; 132 struct evhttp *http = NULL; 133 struct evrpc_base *base = NULL; 134 135 http = http_setup(&port); 136 base = evrpc_init(http); 137 138 EVRPC_REGISTER(base, Message, msg, kill, MessageCb, NULL); 139 EVRPC_REGISTER(base, NeverReply, msg, kill, NeverReplyCb, NULL); 140 141 *phttp = http; 142 *pport = port; 143 *pbase = base; 144 145 need_input_hook = 0; 146 need_output_hook = 0; 147 } 148 149 static void 150 rpc_teardown(struct evrpc_base *base) 151 { 152 assert(EVRPC_UNREGISTER(base, Message) == 0); 153 assert(EVRPC_UNREGISTER(base, NeverReply) == 0); 154 155 evrpc_free(base); 156 } 157 158 static void 159 rpc_postrequest_failure(struct evhttp_request *req, void *arg) 160 { 161 if (req->response_code != HTTP_SERVUNAVAIL) { 162 163 fprintf(stderr, "FAILED (response code)\n"); 164 exit(1); 165 } 166 167 test_ok = 1; 168 event_loopexit(NULL); 169 } 170 171 /* 172 * Test a malformed payload submitted as an RPC 173 */ 174 175 static void 176 rpc_basic_test(void) 177 { 178 short port; 179 struct evhttp *http = NULL; 180 struct evrpc_base *base = NULL; 181 struct evhttp_connection *evcon = NULL; 182 struct evhttp_request *req = NULL; 183 184 fprintf(stdout, "Testing Basic RPC Support: "); 185 186 rpc_setup(&http, &port, &base); 187 188 evcon = evhttp_connection_new("127.0.0.1", port); 189 if (evcon == NULL) { 190 fprintf(stdout, "FAILED\n"); 191 exit(1); 192 } 193 194 /* 195 * At this point, we want to schedule an HTTP POST request 196 * server using our make request method. 197 */ 198 199 req = evhttp_request_new(rpc_postrequest_failure, NULL); 200 if (req == NULL) { 201 fprintf(stdout, "FAILED\n"); 202 exit(1); 203 } 204 205 /* Add the information that we care about */ 206 evhttp_add_header(req->output_headers, "Host", "somehost"); 207 evbuffer_add_printf(req->output_buffer, "Some Nonsense"); 208 209 if (evhttp_make_request(evcon, req, 210 EVHTTP_REQ_POST, 211 "/.rpc.Message") == -1) { 212 fprintf(stdout, "FAILED\n"); 213 exit(1); 214 } 215 216 test_ok = 0; 217 218 event_dispatch(); 219 220 evhttp_connection_free(evcon); 221 222 rpc_teardown(base); 223 224 if (test_ok != 1) { 225 fprintf(stdout, "FAILED\n"); 226 exit(1); 227 } 228 229 fprintf(stdout, "OK\n"); 230 231 evhttp_free(http); 232 } 233 234 static void 235 rpc_postrequest_done(struct evhttp_request *req, void *arg) 236 { 237 struct kill* kill_reply = NULL; 238 239 if (req->response_code != HTTP_OK) { 240 241 fprintf(stderr, "FAILED (response code)\n"); 242 exit(1); 243 } 244 245 kill_reply = kill_new(); 246 247 if ((kill_unmarshal(kill_reply, req->input_buffer)) == -1) { 248 fprintf(stderr, "FAILED (unmarshal)\n"); 249 exit(1); 250 } 251 252 kill_free(kill_reply); 253 254 test_ok = 1; 255 event_loopexit(NULL); 256 } 257 258 static void 259 rpc_basic_message(void) 260 { 261 short port; 262 struct evhttp *http = NULL; 263 struct evrpc_base *base = NULL; 264 struct evhttp_connection *evcon = NULL; 265 struct evhttp_request *req = NULL; 266 struct msg *msg; 267 268 fprintf(stdout, "Testing Good RPC Post: "); 269 270 rpc_setup(&http, &port, &base); 271 272 evcon = evhttp_connection_new("127.0.0.1", port); 273 if (evcon == NULL) { 274 fprintf(stdout, "FAILED\n"); 275 exit(1); 276 } 277 278 /* 279 * At this point, we want to schedule an HTTP POST request 280 * server using our make request method. 281 */ 282 283 req = evhttp_request_new(rpc_postrequest_done, NULL); 284 if (req == NULL) { 285 fprintf(stdout, "FAILED\n"); 286 exit(1); 287 } 288 289 /* Add the information that we care about */ 290 evhttp_add_header(req->output_headers, "Host", "somehost"); 291 292 /* set up the basic message */ 293 msg = msg_new(); 294 EVTAG_ASSIGN(msg, from_name, "niels"); 295 EVTAG_ASSIGN(msg, to_name, "tester"); 296 msg_marshal(req->output_buffer, msg); 297 msg_free(msg); 298 299 if (evhttp_make_request(evcon, req, 300 EVHTTP_REQ_POST, 301 "/.rpc.Message") == -1) { 302 fprintf(stdout, "FAILED\n"); 303 exit(1); 304 } 305 306 test_ok = 0; 307 308 event_dispatch(); 309 310 evhttp_connection_free(evcon); 311 312 rpc_teardown(base); 313 314 if (test_ok != 1) { 315 fprintf(stdout, "FAILED\n"); 316 exit(1); 317 } 318 319 fprintf(stdout, "OK\n"); 320 321 evhttp_free(http); 322 } 323 324 static struct evrpc_pool * 325 rpc_pool_with_connection(short port) 326 { 327 struct evhttp_connection *evcon; 328 struct evrpc_pool *pool; 329 330 pool = evrpc_pool_new(NULL); 331 assert(pool != NULL); 332 333 evcon = evhttp_connection_new("127.0.0.1", port); 334 assert(evcon != NULL); 335 336 evrpc_pool_add_connection(pool, evcon); 337 338 return (pool); 339 } 340 341 static void 342 GotKillCb(struct evrpc_status *status, 343 struct msg *msg, struct kill *kill, void *arg) 344 { 345 char *weapon; 346 char *action; 347 348 if (need_output_hook) { 349 struct evhttp_request *req = status->http_req; 350 const char *header = evhttp_find_header( 351 req->input_headers, "X-Pool-Hook"); 352 assert(strcmp(header, "ran") == 0); 353 } 354 355 if (status->error != EVRPC_STATUS_ERR_NONE) 356 goto done; 357 358 if (EVTAG_GET(kill, weapon, &weapon) == -1) { 359 fprintf(stderr, "get weapon\n"); 360 goto done; 361 } 362 if (EVTAG_GET(kill, action, &action) == -1) { 363 fprintf(stderr, "get action\n"); 364 goto done; 365 } 366 367 if (strcmp(weapon, "dagger")) 368 goto done; 369 370 if (strcmp(action, "wave around like an idiot")) 371 goto done; 372 373 test_ok += 1; 374 375 done: 376 event_loopexit(NULL); 377 } 378 379 static void 380 GotKillCbTwo(struct evrpc_status *status, 381 struct msg *msg, struct kill *kill, void *arg) 382 { 383 char *weapon; 384 char *action; 385 386 if (status->error != EVRPC_STATUS_ERR_NONE) 387 goto done; 388 389 if (EVTAG_GET(kill, weapon, &weapon) == -1) { 390 fprintf(stderr, "get weapon\n"); 391 goto done; 392 } 393 if (EVTAG_GET(kill, action, &action) == -1) { 394 fprintf(stderr, "get action\n"); 395 goto done; 396 } 397 398 if (strcmp(weapon, "dagger")) 399 goto done; 400 401 if (strcmp(action, "wave around like an idiot")) 402 goto done; 403 404 test_ok += 1; 405 406 done: 407 if (test_ok == 2) 408 event_loopexit(NULL); 409 } 410 411 static int 412 rpc_hook_add_header(struct evhttp_request *req, 413 struct evbuffer *evbuf, void *arg) 414 { 415 const char *hook_type = arg; 416 if (strcmp("input", hook_type) == 0) 417 evhttp_add_header(req->input_headers, "X-Hook", hook_type); 418 else 419 evhttp_add_header(req->output_headers, "X-Hook", hook_type); 420 return (0); 421 } 422 423 static int 424 rpc_hook_remove_header(struct evhttp_request *req, 425 struct evbuffer *evbuf, void *arg) 426 { 427 const char *header = evhttp_find_header(req->input_headers, "X-Hook"); 428 assert(header != NULL); 429 assert(strcmp(header, arg) == 0); 430 evhttp_remove_header(req->input_headers, "X-Hook"); 431 evhttp_add_header(req->input_headers, "X-Pool-Hook", "ran"); 432 433 return (0); 434 } 435 436 static void 437 rpc_basic_client(void) 438 { 439 short port; 440 struct evhttp *http = NULL; 441 struct evrpc_base *base = NULL; 442 struct evrpc_pool *pool = NULL; 443 struct msg *msg; 444 struct kill *kill; 445 446 fprintf(stdout, "Testing RPC Client: "); 447 448 rpc_setup(&http, &port, &base); 449 450 need_input_hook = 1; 451 need_output_hook = 1; 452 453 assert(evrpc_add_hook(base, EVRPC_INPUT, rpc_hook_add_header, (void*)"input") 454 != NULL); 455 assert(evrpc_add_hook(base, EVRPC_OUTPUT, rpc_hook_add_header, (void*)"output") 456 != NULL); 457 458 pool = rpc_pool_with_connection(port); 459 460 assert(evrpc_add_hook(pool, EVRPC_INPUT, rpc_hook_remove_header, (void*)"output")); 461 462 /* set up the basic message */ 463 msg = msg_new(); 464 EVTAG_ASSIGN(msg, from_name, "niels"); 465 EVTAG_ASSIGN(msg, to_name, "tester"); 466 467 kill = kill_new(); 468 469 EVRPC_MAKE_REQUEST(Message, pool, msg, kill, GotKillCb, NULL); 470 471 test_ok = 0; 472 473 event_dispatch(); 474 475 if (test_ok != 1) { 476 fprintf(stdout, "FAILED (1)\n"); 477 exit(1); 478 } 479 480 /* we do it twice to make sure that reuse works correctly */ 481 kill_clear(kill); 482 483 EVRPC_MAKE_REQUEST(Message, pool, msg, kill, GotKillCb, NULL); 484 485 event_dispatch(); 486 487 rpc_teardown(base); 488 489 if (test_ok != 2) { 490 fprintf(stdout, "FAILED (2)\n"); 491 exit(1); 492 } 493 494 fprintf(stdout, "OK\n"); 495 496 msg_free(msg); 497 kill_free(kill); 498 499 evrpc_pool_free(pool); 500 evhttp_free(http); 501 } 502 503 /* 504 * We are testing that the second requests gets send over the same 505 * connection after the first RPCs completes. 506 */ 507 static void 508 rpc_basic_queued_client(void) 509 { 510 short port; 511 struct evhttp *http = NULL; 512 struct evrpc_base *base = NULL; 513 struct evrpc_pool *pool = NULL; 514 struct msg *msg; 515 struct kill *kill_one, *kill_two; 516 517 fprintf(stdout, "Testing RPC (Queued) Client: "); 518 519 rpc_setup(&http, &port, &base); 520 521 pool = rpc_pool_with_connection(port); 522 523 /* set up the basic message */ 524 msg = msg_new(); 525 EVTAG_ASSIGN(msg, from_name, "niels"); 526 EVTAG_ASSIGN(msg, to_name, "tester"); 527 528 kill_one = kill_new(); 529 kill_two = kill_new(); 530 531 EVRPC_MAKE_REQUEST(Message, pool, msg, kill_one, GotKillCbTwo, NULL); 532 EVRPC_MAKE_REQUEST(Message, pool, msg, kill_two, GotKillCb, NULL); 533 534 test_ok = 0; 535 536 event_dispatch(); 537 538 rpc_teardown(base); 539 540 if (test_ok != 2) { 541 fprintf(stdout, "FAILED (1)\n"); 542 exit(1); 543 } 544 545 fprintf(stdout, "OK\n"); 546 547 msg_free(msg); 548 kill_free(kill_one); 549 kill_free(kill_two); 550 551 evrpc_pool_free(pool); 552 evhttp_free(http); 553 } 554 555 static void 556 GotErrorCb(struct evrpc_status *status, 557 struct msg *msg, struct kill *kill, void *arg) 558 { 559 if (status->error != EVRPC_STATUS_ERR_TIMEOUT) 560 goto done; 561 562 /* should never be complete but just to check */ 563 if (kill_complete(kill) == 0) 564 goto done; 565 566 test_ok += 1; 567 568 done: 569 event_loopexit(NULL); 570 } 571 572 static void 573 rpc_client_timeout(void) 574 { 575 short port; 576 struct evhttp *http = NULL; 577 struct evrpc_base *base = NULL; 578 struct evrpc_pool *pool = NULL; 579 struct msg *msg; 580 struct kill *kill; 581 582 fprintf(stdout, "Testing RPC Client Timeout: "); 583 584 rpc_setup(&http, &port, &base); 585 586 pool = rpc_pool_with_connection(port); 587 588 /* set the timeout to 5 seconds */ 589 evrpc_pool_set_timeout(pool, 5); 590 591 /* set up the basic message */ 592 msg = msg_new(); 593 EVTAG_ASSIGN(msg, from_name, "niels"); 594 EVTAG_ASSIGN(msg, to_name, "tester"); 595 596 kill = kill_new(); 597 598 EVRPC_MAKE_REQUEST(NeverReply, pool, msg, kill, GotErrorCb, NULL); 599 600 test_ok = 0; 601 602 event_dispatch(); 603 604 /* free the saved RPC structure up */ 605 EVRPC_REQUEST_DONE(saved_rpc); 606 607 rpc_teardown(base); 608 609 if (test_ok != 2) { 610 fprintf(stdout, "FAILED (1)\n"); 611 exit(1); 612 } 613 614 fprintf(stdout, "OK\n"); 615 616 msg_free(msg); 617 kill_free(kill); 618 619 evrpc_pool_free(pool); 620 evhttp_free(http); 621 } 622 623 void 624 rpc_suite(void) 625 { 626 rpc_basic_test(); 627 rpc_basic_message(); 628 rpc_basic_client(); 629 rpc_basic_queued_client(); 630 rpc_client_timeout(); 631 } 632