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