1275970Scy/* 2275970Scy * Copyright (c) 2003-2007 Niels Provos <provos@citi.umich.edu> 3275970Scy * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson 4275970Scy * 5275970Scy * Redistribution and use in source and binary forms, with or without 6275970Scy * modification, are permitted provided that the following conditions 7275970Scy * are met: 8275970Scy * 1. Redistributions of source code must retain the above copyright 9275970Scy * notice, this list of conditions and the following disclaimer. 10275970Scy * 2. Redistributions in binary form must reproduce the above copyright 11275970Scy * notice, this list of conditions and the following disclaimer in the 12275970Scy * documentation and/or other materials provided with the distribution. 13275970Scy * 3. The name of the author may not be used to endorse or promote products 14275970Scy * derived from this software without specific prior written permission. 15275970Scy * 16275970Scy * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17275970Scy * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18275970Scy * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19275970Scy * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20275970Scy * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21275970Scy * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22275970Scy * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23275970Scy * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24275970Scy * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25275970Scy * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26275970Scy */ 27275970Scy 28275970Scy/* The old tests here need assertions to work. */ 29275970Scy#undef NDEBUG 30275970Scy 31275970Scy#ifdef _WIN32 32275970Scy#include <winsock2.h> 33275970Scy#include <windows.h> 34275970Scy#endif 35275970Scy 36275970Scy#include "event2/event-config.h" 37275970Scy 38275970Scy#include <sys/types.h> 39275970Scy#include <sys/stat.h> 40275970Scy#ifdef EVENT__HAVE_SYS_TIME_H 41275970Scy#include <sys/time.h> 42275970Scy#endif 43275970Scy#include <sys/queue.h> 44275970Scy#ifndef _WIN32 45275970Scy#include <sys/socket.h> 46275970Scy#include <signal.h> 47275970Scy#include <unistd.h> 48275970Scy#include <netdb.h> 49275970Scy#endif 50275970Scy#include <fcntl.h> 51275970Scy#include <stdlib.h> 52275970Scy#include <stdio.h> 53275970Scy#include <string.h> 54275970Scy#include <errno.h> 55275970Scy#include <assert.h> 56275970Scy 57275970Scy#include "event2/buffer.h" 58275970Scy#include "event2/event.h" 59275970Scy#include "event2/event_compat.h" 60275970Scy#include "event2/http.h" 61275970Scy#include "event2/http_compat.h" 62275970Scy#include "event2/http_struct.h" 63275970Scy#include "event2/rpc.h" 64275970Scy#include "event2/rpc.h" 65275970Scy#include "event2/rpc_struct.h" 66275970Scy#include "event2/tag.h" 67275970Scy#include "log-internal.h" 68275970Scy 69275970Scy#include "regress.gen.h" 70275970Scy 71275970Scy#include "regress.h" 72275970Scy#include "regress_testutils.h" 73275970Scy 74275970Scy#ifndef NO_PYTHON_EXISTS 75275970Scy 76275970Scystatic struct evhttp * 77275970Scyhttp_setup(ev_uint16_t *pport) 78275970Scy{ 79275970Scy struct evhttp *myhttp; 80275970Scy ev_uint16_t port; 81275970Scy struct evhttp_bound_socket *sock; 82275970Scy 83275970Scy myhttp = evhttp_new(NULL); 84275970Scy if (!myhttp) 85275970Scy event_errx(1, "Could not start web server"); 86275970Scy 87275970Scy /* Try a few different ports */ 88275970Scy sock = evhttp_bind_socket_with_handle(myhttp, "127.0.0.1", 0); 89275970Scy if (!sock) 90275970Scy event_errx(1, "Couldn't open web port"); 91275970Scy 92275970Scy port = regress_get_socket_port(evhttp_bound_socket_get_fd(sock)); 93275970Scy 94275970Scy *pport = port; 95275970Scy return (myhttp); 96275970Scy} 97275970Scy 98275970ScyEVRPC_HEADER(Message, msg, kill) 99275970ScyEVRPC_HEADER(NeverReply, msg, kill) 100275970Scy 101275970ScyEVRPC_GENERATE(Message, msg, kill) 102275970ScyEVRPC_GENERATE(NeverReply, msg, kill) 103275970Scy 104275970Scystatic int need_input_hook = 0; 105275970Scystatic int need_output_hook = 0; 106275970Scy 107275970Scystatic void 108275970ScyMessageCb(EVRPC_STRUCT(Message)* rpc, void *arg) 109275970Scy{ 110275970Scy struct kill* kill_reply = rpc->reply; 111275970Scy 112275970Scy if (need_input_hook) { 113275970Scy struct evhttp_request* req = EVRPC_REQUEST_HTTP(rpc); 114275970Scy const char *header = evhttp_find_header( 115275970Scy req->input_headers, "X-Hook"); 116275970Scy assert(header); 117275970Scy assert(strcmp(header, "input") == 0); 118275970Scy } 119275970Scy 120275970Scy /* we just want to fill in some non-sense */ 121275970Scy EVTAG_ASSIGN(kill_reply, weapon, "dagger"); 122275970Scy EVTAG_ASSIGN(kill_reply, action, "wave around like an idiot"); 123275970Scy 124275970Scy /* no reply to the RPC */ 125275970Scy EVRPC_REQUEST_DONE(rpc); 126275970Scy} 127275970Scy 128275970Scystatic EVRPC_STRUCT(NeverReply) *saved_rpc; 129275970Scy 130275970Scystatic void 131275970ScyNeverReplyCb(EVRPC_STRUCT(NeverReply)* rpc, void *arg) 132275970Scy{ 133275970Scy test_ok += 1; 134275970Scy saved_rpc = rpc; 135275970Scy} 136275970Scy 137275970Scystatic void 138275970Scyrpc_setup(struct evhttp **phttp, ev_uint16_t *pport, struct evrpc_base **pbase) 139275970Scy{ 140275970Scy ev_uint16_t port; 141275970Scy struct evhttp *http = NULL; 142275970Scy struct evrpc_base *base = NULL; 143275970Scy 144275970Scy http = http_setup(&port); 145275970Scy base = evrpc_init(http); 146275970Scy 147275970Scy EVRPC_REGISTER(base, Message, msg, kill, MessageCb, NULL); 148275970Scy EVRPC_REGISTER(base, NeverReply, msg, kill, NeverReplyCb, NULL); 149275970Scy 150275970Scy *phttp = http; 151275970Scy *pport = port; 152275970Scy *pbase = base; 153275970Scy 154275970Scy need_input_hook = 0; 155275970Scy need_output_hook = 0; 156275970Scy} 157275970Scy 158275970Scystatic void 159275970Scyrpc_teardown(struct evrpc_base *base) 160275970Scy{ 161275970Scy assert(EVRPC_UNREGISTER(base, Message) == 0); 162275970Scy assert(EVRPC_UNREGISTER(base, NeverReply) == 0); 163275970Scy 164275970Scy evrpc_free(base); 165275970Scy} 166275970Scy 167275970Scystatic void 168275970Scyrpc_postrequest_failure(struct evhttp_request *req, void *arg) 169275970Scy{ 170275970Scy if (req->response_code != HTTP_SERVUNAVAIL) { 171275970Scy 172275970Scy fprintf(stderr, "FAILED (response code)\n"); 173275970Scy exit(1); 174275970Scy } 175275970Scy 176275970Scy test_ok = 1; 177275970Scy event_loopexit(NULL); 178275970Scy} 179275970Scy 180275970Scy/* 181275970Scy * Test a malformed payload submitted as an RPC 182275970Scy */ 183275970Scy 184275970Scystatic void 185275970Scyrpc_basic_test(void) 186275970Scy{ 187275970Scy ev_uint16_t port; 188275970Scy struct evhttp *http = NULL; 189275970Scy struct evrpc_base *base = NULL; 190275970Scy struct evhttp_connection *evcon = NULL; 191275970Scy struct evhttp_request *req = NULL; 192275970Scy 193275970Scy rpc_setup(&http, &port, &base); 194275970Scy 195275970Scy evcon = evhttp_connection_new("127.0.0.1", port); 196275970Scy tt_assert(evcon); 197275970Scy 198275970Scy /* 199275970Scy * At this point, we want to schedule an HTTP POST request 200275970Scy * server using our make request method. 201275970Scy */ 202275970Scy 203275970Scy req = evhttp_request_new(rpc_postrequest_failure, NULL); 204275970Scy tt_assert(req); 205275970Scy 206275970Scy /* Add the information that we care about */ 207275970Scy evhttp_add_header(req->output_headers, "Host", "somehost"); 208275970Scy evbuffer_add_printf(req->output_buffer, "Some Nonsense"); 209275970Scy 210275970Scy if (evhttp_make_request(evcon, req, 211275970Scy EVHTTP_REQ_POST, 212275970Scy "/.rpc.Message") == -1) { 213275970Scy tt_abort(); 214275970Scy } 215275970Scy 216275970Scy test_ok = 0; 217275970Scy 218275970Scy event_dispatch(); 219275970Scy 220275970Scy evhttp_connection_free(evcon); 221275970Scy 222275970Scy rpc_teardown(base); 223275970Scy 224275970Scy tt_assert(test_ok == 1); 225275970Scy 226275970Scyend: 227275970Scy evhttp_free(http); 228275970Scy} 229275970Scy 230275970Scystatic void 231275970Scyrpc_postrequest_done(struct evhttp_request *req, void *arg) 232275970Scy{ 233275970Scy struct kill* kill_reply = NULL; 234275970Scy 235275970Scy if (req->response_code != HTTP_OK) { 236275970Scy fprintf(stderr, "FAILED (response code)\n"); 237275970Scy exit(1); 238275970Scy } 239275970Scy 240275970Scy kill_reply = kill_new(); 241275970Scy 242275970Scy if ((kill_unmarshal(kill_reply, req->input_buffer)) == -1) { 243275970Scy fprintf(stderr, "FAILED (unmarshal)\n"); 244275970Scy exit(1); 245275970Scy } 246275970Scy 247275970Scy kill_free(kill_reply); 248275970Scy 249275970Scy test_ok = 1; 250275970Scy event_loopexit(NULL); 251275970Scy} 252275970Scy 253275970Scystatic void 254275970Scyrpc_basic_message(void) 255275970Scy{ 256275970Scy ev_uint16_t port; 257275970Scy struct evhttp *http = NULL; 258275970Scy struct evrpc_base *base = NULL; 259275970Scy struct evhttp_connection *evcon = NULL; 260275970Scy struct evhttp_request *req = NULL; 261275970Scy struct msg *msg; 262275970Scy 263275970Scy rpc_setup(&http, &port, &base); 264275970Scy 265275970Scy evcon = evhttp_connection_new("127.0.0.1", port); 266275970Scy tt_assert(evcon); 267275970Scy 268275970Scy /* 269275970Scy * At this point, we want to schedule an HTTP POST request 270275970Scy * server using our make request method. 271275970Scy */ 272275970Scy 273275970Scy req = evhttp_request_new(rpc_postrequest_done, NULL); 274275970Scy if (req == NULL) { 275275970Scy fprintf(stdout, "FAILED\n"); 276275970Scy exit(1); 277275970Scy } 278275970Scy 279275970Scy /* Add the information that we care about */ 280275970Scy evhttp_add_header(req->output_headers, "Host", "somehost"); 281275970Scy 282275970Scy /* set up the basic message */ 283275970Scy msg = msg_new(); 284275970Scy EVTAG_ASSIGN(msg, from_name, "niels"); 285275970Scy EVTAG_ASSIGN(msg, to_name, "tester"); 286275970Scy msg_marshal(req->output_buffer, msg); 287275970Scy msg_free(msg); 288275970Scy 289275970Scy if (evhttp_make_request(evcon, req, 290275970Scy EVHTTP_REQ_POST, 291275970Scy "/.rpc.Message") == -1) { 292275970Scy fprintf(stdout, "FAILED\n"); 293275970Scy exit(1); 294275970Scy } 295275970Scy 296275970Scy test_ok = 0; 297275970Scy 298275970Scy event_dispatch(); 299275970Scy 300275970Scy evhttp_connection_free(evcon); 301275970Scy 302275970Scy rpc_teardown(base); 303275970Scy 304275970Scyend: 305275970Scy evhttp_free(http); 306275970Scy} 307275970Scy 308275970Scystatic struct evrpc_pool * 309275970Scyrpc_pool_with_connection(ev_uint16_t port) 310275970Scy{ 311275970Scy struct evhttp_connection *evcon; 312275970Scy struct evrpc_pool *pool; 313275970Scy 314275970Scy pool = evrpc_pool_new(NULL); 315275970Scy assert(pool != NULL); 316275970Scy 317275970Scy evcon = evhttp_connection_new("127.0.0.1", port); 318275970Scy assert(evcon != NULL); 319275970Scy 320275970Scy evrpc_pool_add_connection(pool, evcon); 321275970Scy 322275970Scy return (pool); 323275970Scy} 324275970Scy 325275970Scystatic void 326275970ScyGotKillCb(struct evrpc_status *status, 327275970Scy struct msg *msg, struct kill *kill, void *arg) 328275970Scy{ 329275970Scy char *weapon; 330275970Scy char *action; 331275970Scy 332275970Scy if (need_output_hook) { 333275970Scy struct evhttp_request *req = status->http_req; 334275970Scy const char *header = evhttp_find_header( 335275970Scy req->input_headers, "X-Pool-Hook"); 336275970Scy assert(header); 337275970Scy assert(strcmp(header, "ran") == 0); 338275970Scy } 339275970Scy 340275970Scy if (status->error != EVRPC_STATUS_ERR_NONE) 341275970Scy goto done; 342275970Scy 343275970Scy if (EVTAG_GET(kill, weapon, &weapon) == -1) { 344275970Scy fprintf(stderr, "get weapon\n"); 345275970Scy goto done; 346275970Scy } 347275970Scy if (EVTAG_GET(kill, action, &action) == -1) { 348275970Scy fprintf(stderr, "get action\n"); 349275970Scy goto done; 350275970Scy } 351275970Scy 352275970Scy if (strcmp(weapon, "dagger")) 353275970Scy goto done; 354275970Scy 355275970Scy if (strcmp(action, "wave around like an idiot")) 356275970Scy goto done; 357275970Scy 358275970Scy test_ok += 1; 359275970Scy 360275970Scydone: 361275970Scy event_loopexit(NULL); 362275970Scy} 363275970Scy 364275970Scystatic void 365275970ScyGotKillCbTwo(struct evrpc_status *status, 366275970Scy struct msg *msg, struct kill *kill, void *arg) 367275970Scy{ 368275970Scy char *weapon; 369275970Scy char *action; 370275970Scy 371275970Scy if (status->error != EVRPC_STATUS_ERR_NONE) 372275970Scy goto done; 373275970Scy 374275970Scy if (EVTAG_GET(kill, weapon, &weapon) == -1) { 375275970Scy fprintf(stderr, "get weapon\n"); 376275970Scy goto done; 377275970Scy } 378275970Scy if (EVTAG_GET(kill, action, &action) == -1) { 379275970Scy fprintf(stderr, "get action\n"); 380275970Scy goto done; 381275970Scy } 382275970Scy 383275970Scy if (strcmp(weapon, "dagger")) 384275970Scy goto done; 385275970Scy 386275970Scy if (strcmp(action, "wave around like an idiot")) 387275970Scy goto done; 388275970Scy 389275970Scy test_ok += 1; 390275970Scy 391275970Scydone: 392275970Scy if (test_ok == 2) 393275970Scy event_loopexit(NULL); 394275970Scy} 395275970Scy 396275970Scystatic int 397275970Scyrpc_hook_add_header(void *ctx, struct evhttp_request *req, 398275970Scy struct evbuffer *evbuf, void *arg) 399275970Scy{ 400275970Scy const char *hook_type = arg; 401275970Scy if (strcmp("input", hook_type) == 0) 402275970Scy evhttp_add_header(req->input_headers, "X-Hook", hook_type); 403275970Scy else 404275970Scy evhttp_add_header(req->output_headers, "X-Hook", hook_type); 405275970Scy 406275970Scy assert(evrpc_hook_get_connection(ctx) != NULL); 407275970Scy 408275970Scy return (EVRPC_CONTINUE); 409275970Scy} 410275970Scy 411275970Scystatic int 412275970Scyrpc_hook_add_meta(void *ctx, struct evhttp_request *req, 413275970Scy struct evbuffer *evbuf, void *arg) 414275970Scy{ 415275970Scy evrpc_hook_add_meta(ctx, "meta", "test", 5); 416275970Scy 417275970Scy assert(evrpc_hook_get_connection(ctx) != NULL); 418275970Scy 419275970Scy return (EVRPC_CONTINUE); 420275970Scy} 421275970Scy 422275970Scystatic int 423275970Scyrpc_hook_remove_header(void *ctx, struct evhttp_request *req, 424275970Scy struct evbuffer *evbuf, void *arg) 425275970Scy{ 426275970Scy const char *header = evhttp_find_header(req->input_headers, "X-Hook"); 427275970Scy void *data = NULL; 428275970Scy size_t data_len = 0; 429275970Scy 430275970Scy assert(header != NULL); 431275970Scy assert(strcmp(header, arg) == 0); 432275970Scy 433275970Scy evhttp_remove_header(req->input_headers, "X-Hook"); 434275970Scy evhttp_add_header(req->input_headers, "X-Pool-Hook", "ran"); 435275970Scy 436275970Scy assert(evrpc_hook_find_meta(ctx, "meta", &data, &data_len) == 0); 437275970Scy assert(data != NULL); 438275970Scy assert(data_len == 5); 439275970Scy 440275970Scy assert(evrpc_hook_get_connection(ctx) != NULL); 441275970Scy 442275970Scy return (EVRPC_CONTINUE); 443275970Scy} 444275970Scy 445275970Scystatic void 446275970Scyrpc_basic_client(void) 447275970Scy{ 448275970Scy ev_uint16_t port; 449275970Scy struct evhttp *http = NULL; 450275970Scy struct evrpc_base *base = NULL; 451275970Scy struct evrpc_pool *pool = NULL; 452275970Scy struct msg *msg = NULL; 453275970Scy struct kill *kill = NULL; 454275970Scy 455275970Scy rpc_setup(&http, &port, &base); 456275970Scy 457275970Scy need_input_hook = 1; 458275970Scy need_output_hook = 1; 459275970Scy 460275970Scy assert(evrpc_add_hook(base, EVRPC_INPUT, rpc_hook_add_header, (void*)"input") 461275970Scy != NULL); 462275970Scy assert(evrpc_add_hook(base, EVRPC_OUTPUT, rpc_hook_add_header, (void*)"output") 463275970Scy != NULL); 464275970Scy 465275970Scy pool = rpc_pool_with_connection(port); 466275970Scy tt_assert(pool); 467275970Scy 468275970Scy assert(evrpc_add_hook(pool, EVRPC_OUTPUT, rpc_hook_add_meta, NULL)); 469275970Scy assert(evrpc_add_hook(pool, EVRPC_INPUT, rpc_hook_remove_header, (void*)"output")); 470275970Scy 471275970Scy /* set up the basic message */ 472275970Scy msg = msg_new(); 473275970Scy tt_assert(msg); 474275970Scy EVTAG_ASSIGN(msg, from_name, "niels"); 475275970Scy EVTAG_ASSIGN(msg, to_name, "tester"); 476275970Scy 477275970Scy kill = kill_new(); 478275970Scy 479275970Scy EVRPC_MAKE_REQUEST(Message, pool, msg, kill, GotKillCb, NULL); 480275970Scy 481275970Scy test_ok = 0; 482275970Scy 483275970Scy event_dispatch(); 484275970Scy 485275970Scy tt_assert(test_ok == 1); 486275970Scy 487275970Scy /* we do it twice to make sure that reuse works correctly */ 488275970Scy kill_clear(kill); 489275970Scy 490275970Scy EVRPC_MAKE_REQUEST(Message, pool, msg, kill, GotKillCb, NULL); 491275970Scy 492275970Scy event_dispatch(); 493275970Scy 494275970Scy tt_assert(test_ok == 2); 495275970Scy 496275970Scy /* we do it trice to make sure other stuff works, too */ 497275970Scy kill_clear(kill); 498275970Scy 499275970Scy { 500275970Scy struct evrpc_request_wrapper *ctx = 501275970Scy EVRPC_MAKE_CTX(Message, msg, kill, 502275970Scy pool, msg, kill, GotKillCb, NULL); 503275970Scy evrpc_make_request(ctx); 504275970Scy } 505275970Scy 506275970Scy event_dispatch(); 507275970Scy 508275970Scy rpc_teardown(base); 509275970Scy 510275970Scy tt_assert(test_ok == 3); 511275970Scy 512275970Scyend: 513275970Scy if (msg) 514275970Scy msg_free(msg); 515275970Scy if (kill) 516275970Scy kill_free(kill); 517275970Scy 518275970Scy if (pool) 519275970Scy evrpc_pool_free(pool); 520275970Scy if (http) 521275970Scy evhttp_free(http); 522275970Scy 523275970Scy need_input_hook = 0; 524275970Scy need_output_hook = 0; 525275970Scy} 526275970Scy 527275970Scy/* 528275970Scy * We are testing that the second requests gets send over the same 529275970Scy * connection after the first RPCs completes. 530275970Scy */ 531275970Scystatic void 532275970Scyrpc_basic_queued_client(void) 533275970Scy{ 534275970Scy ev_uint16_t port; 535275970Scy struct evhttp *http = NULL; 536275970Scy struct evrpc_base *base = NULL; 537275970Scy struct evrpc_pool *pool = NULL; 538275970Scy struct msg *msg=NULL; 539275970Scy struct kill *kill_one=NULL, *kill_two=NULL; 540275970Scy 541275970Scy rpc_setup(&http, &port, &base); 542275970Scy 543275970Scy pool = rpc_pool_with_connection(port); 544275970Scy tt_assert(pool); 545275970Scy 546275970Scy /* set up the basic message */ 547275970Scy msg = msg_new(); 548275970Scy tt_assert(msg); 549275970Scy EVTAG_ASSIGN(msg, from_name, "niels"); 550275970Scy EVTAG_ASSIGN(msg, to_name, "tester"); 551275970Scy 552275970Scy kill_one = kill_new(); 553275970Scy kill_two = kill_new(); 554275970Scy 555275970Scy EVRPC_MAKE_REQUEST(Message, pool, msg, kill_one, GotKillCbTwo, NULL); 556275970Scy EVRPC_MAKE_REQUEST(Message, pool, msg, kill_two, GotKillCb, NULL); 557275970Scy 558275970Scy test_ok = 0; 559275970Scy 560275970Scy event_dispatch(); 561275970Scy 562275970Scy rpc_teardown(base); 563275970Scy 564275970Scy tt_assert(test_ok == 2); 565275970Scy 566275970Scyend: 567275970Scy if (msg) 568275970Scy msg_free(msg); 569275970Scy if (kill_one) 570275970Scy kill_free(kill_one); 571275970Scy if (kill_two) 572275970Scy kill_free(kill_two); 573275970Scy 574275970Scy if (pool) 575275970Scy evrpc_pool_free(pool); 576275970Scy if (http) 577275970Scy evhttp_free(http); 578275970Scy} 579275970Scy 580275970Scystatic void 581275970ScyGotErrorCb(struct evrpc_status *status, 582275970Scy struct msg *msg, struct kill *kill, void *arg) 583275970Scy{ 584275970Scy if (status->error != EVRPC_STATUS_ERR_TIMEOUT) 585275970Scy goto done; 586275970Scy 587275970Scy /* should never be complete but just to check */ 588275970Scy if (kill_complete(kill) == 0) 589275970Scy goto done; 590275970Scy 591275970Scy test_ok += 1; 592275970Scy 593275970Scydone: 594275970Scy event_loopexit(NULL); 595275970Scy} 596275970Scy 597275970Scy/* we just pause the rpc and continue it in the next callback */ 598275970Scy 599275970Scystruct rpc_hook_ctx_ { 600275970Scy void *vbase; 601275970Scy void *ctx; 602275970Scy}; 603275970Scy 604275970Scystatic int hook_pause_cb_called=0; 605275970Scy 606275970Scystatic void 607275970Scyrpc_hook_pause_cb(evutil_socket_t fd, short what, void *arg) 608275970Scy{ 609275970Scy struct rpc_hook_ctx_ *ctx = arg; 610275970Scy ++hook_pause_cb_called; 611275970Scy evrpc_resume_request(ctx->vbase, ctx->ctx, EVRPC_CONTINUE); 612275970Scy free(arg); 613275970Scy} 614275970Scy 615275970Scystatic int 616275970Scyrpc_hook_pause(void *ctx, struct evhttp_request *req, struct evbuffer *evbuf, 617275970Scy void *arg) 618275970Scy{ 619275970Scy struct rpc_hook_ctx_ *tmp = malloc(sizeof(*tmp)); 620275970Scy struct timeval tv; 621275970Scy 622275970Scy assert(tmp != NULL); 623275970Scy tmp->vbase = arg; 624275970Scy tmp->ctx = ctx; 625275970Scy 626275970Scy memset(&tv, 0, sizeof(tv)); 627275970Scy event_once(-1, EV_TIMEOUT, rpc_hook_pause_cb, tmp, &tv); 628275970Scy return EVRPC_PAUSE; 629275970Scy} 630275970Scy 631275970Scystatic void 632275970Scyrpc_basic_client_with_pause(void) 633275970Scy{ 634275970Scy ev_uint16_t port; 635275970Scy struct evhttp *http = NULL; 636275970Scy struct evrpc_base *base = NULL; 637275970Scy struct evrpc_pool *pool = NULL; 638275970Scy struct msg *msg = NULL; 639275970Scy struct kill *kill= NULL; 640275970Scy 641275970Scy rpc_setup(&http, &port, &base); 642275970Scy 643275970Scy assert(evrpc_add_hook(base, EVRPC_INPUT, rpc_hook_pause, base)); 644275970Scy assert(evrpc_add_hook(base, EVRPC_OUTPUT, rpc_hook_pause, base)); 645275970Scy 646275970Scy pool = rpc_pool_with_connection(port); 647275970Scy tt_assert(pool); 648275970Scy assert(evrpc_add_hook(pool, EVRPC_INPUT, rpc_hook_pause, pool)); 649275970Scy assert(evrpc_add_hook(pool, EVRPC_OUTPUT, rpc_hook_pause, pool)); 650275970Scy 651275970Scy /* set up the basic message */ 652275970Scy msg = msg_new(); 653275970Scy tt_assert(msg); 654275970Scy EVTAG_ASSIGN(msg, from_name, "niels"); 655275970Scy EVTAG_ASSIGN(msg, to_name, "tester"); 656275970Scy 657275970Scy kill = kill_new(); 658275970Scy 659275970Scy EVRPC_MAKE_REQUEST(Message, pool, msg, kill, GotKillCb, NULL); 660275970Scy 661275970Scy test_ok = 0; 662275970Scy 663275970Scy event_dispatch(); 664275970Scy 665275970Scy tt_int_op(test_ok, ==, 1); 666275970Scy tt_int_op(hook_pause_cb_called, ==, 4); 667275970Scy 668275970Scyend: 669275970Scy if (base) 670275970Scy rpc_teardown(base); 671275970Scy 672275970Scy if (msg) 673275970Scy msg_free(msg); 674275970Scy if (kill) 675275970Scy kill_free(kill); 676275970Scy 677275970Scy if (pool) 678275970Scy evrpc_pool_free(pool); 679275970Scy if (http) 680275970Scy evhttp_free(http); 681275970Scy} 682275970Scy 683275970Scystatic void 684275970Scyrpc_client_timeout(void) 685275970Scy{ 686275970Scy ev_uint16_t port; 687275970Scy struct evhttp *http = NULL; 688275970Scy struct evrpc_base *base = NULL; 689275970Scy struct evrpc_pool *pool = NULL; 690275970Scy struct msg *msg = NULL; 691275970Scy struct kill *kill = NULL; 692275970Scy 693275970Scy rpc_setup(&http, &port, &base); 694275970Scy 695275970Scy pool = rpc_pool_with_connection(port); 696275970Scy tt_assert(pool); 697275970Scy 698275970Scy /* set the timeout to 1 second. */ 699275970Scy evrpc_pool_set_timeout(pool, 1); 700275970Scy 701275970Scy /* set up the basic message */ 702275970Scy msg = msg_new(); 703275970Scy tt_assert(msg); 704275970Scy EVTAG_ASSIGN(msg, from_name, "niels"); 705275970Scy EVTAG_ASSIGN(msg, to_name, "tester"); 706275970Scy 707275970Scy kill = kill_new(); 708275970Scy 709275970Scy EVRPC_MAKE_REQUEST(NeverReply, pool, msg, kill, GotErrorCb, NULL); 710275970Scy 711275970Scy test_ok = 0; 712275970Scy 713275970Scy event_dispatch(); 714275970Scy 715275970Scy /* free the saved RPC structure up */ 716275970Scy EVRPC_REQUEST_DONE(saved_rpc); 717275970Scy 718275970Scy rpc_teardown(base); 719275970Scy 720275970Scy tt_assert(test_ok == 2); 721275970Scy 722275970Scyend: 723275970Scy if (msg) 724275970Scy msg_free(msg); 725275970Scy if (kill) 726275970Scy kill_free(kill); 727275970Scy 728275970Scy if (pool) 729275970Scy evrpc_pool_free(pool); 730275970Scy if (http) 731275970Scy evhttp_free(http); 732275970Scy} 733275970Scy 734275970Scystatic void 735275970Scyrpc_test(void) 736275970Scy{ 737275970Scy struct msg *msg = NULL, *msg2 = NULL; 738275970Scy struct kill *attack = NULL; 739275970Scy struct run *run = NULL; 740275970Scy struct evbuffer *tmp = evbuffer_new(); 741275970Scy struct timeval tv_start, tv_end; 742275970Scy ev_uint32_t tag; 743275970Scy int i; 744275970Scy 745275970Scy msg = msg_new(); 746275970Scy 747275970Scy tt_assert(msg); 748275970Scy 749275970Scy EVTAG_ASSIGN(msg, from_name, "niels"); 750275970Scy EVTAG_ASSIGN(msg, to_name, "phoenix"); 751275970Scy 752275970Scy if (EVTAG_GET(msg, attack, &attack) == -1) { 753275970Scy tt_abort_msg("Failed to set kill message."); 754275970Scy } 755275970Scy 756275970Scy EVTAG_ASSIGN(attack, weapon, "feather"); 757275970Scy EVTAG_ASSIGN(attack, action, "tickle"); 758275970Scy for (i = 0; i < 3; ++i) { 759275970Scy if (EVTAG_ARRAY_ADD_VALUE(attack, how_often, i) == NULL) { 760275970Scy tt_abort_msg("Failed to add how_often."); 761275970Scy } 762275970Scy } 763275970Scy 764275970Scy evutil_gettimeofday(&tv_start, NULL); 765275970Scy for (i = 0; i < 1000; ++i) { 766275970Scy run = EVTAG_ARRAY_ADD(msg, run); 767275970Scy if (run == NULL) { 768275970Scy tt_abort_msg("Failed to add run message."); 769275970Scy } 770275970Scy EVTAG_ASSIGN(run, how, "very fast but with some data in it"); 771275970Scy EVTAG_ASSIGN(run, fixed_bytes, 772275970Scy (ev_uint8_t*)"012345678901234567890123"); 773275970Scy 774275970Scy if (EVTAG_ARRAY_ADD_VALUE( 775275970Scy run, notes, "this is my note") == NULL) { 776275970Scy tt_abort_msg("Failed to add note."); 777275970Scy } 778275970Scy if (EVTAG_ARRAY_ADD_VALUE(run, notes, "pps") == NULL) { 779275970Scy tt_abort_msg("Failed to add note"); 780275970Scy } 781275970Scy 782275970Scy EVTAG_ASSIGN(run, large_number, 0xdead0a0bcafebeefLL); 783275970Scy EVTAG_ARRAY_ADD_VALUE(run, other_numbers, 0xdead0a0b); 784275970Scy EVTAG_ARRAY_ADD_VALUE(run, other_numbers, 0xbeefcafe); 785275970Scy } 786275970Scy 787275970Scy if (msg_complete(msg) == -1) 788275970Scy tt_abort_msg("Failed to make complete message."); 789275970Scy 790275970Scy evtag_marshal_msg(tmp, 0xdeaf, msg); 791275970Scy 792275970Scy if (evtag_peek(tmp, &tag) == -1) 793275970Scy tt_abort_msg("Failed to peak tag."); 794275970Scy 795275970Scy if (tag != 0xdeaf) 796275970Scy TT_DIE(("Got incorrect tag: %0x.", (unsigned)tag)); 797275970Scy 798275970Scy msg2 = msg_new(); 799275970Scy if (evtag_unmarshal_msg(tmp, 0xdeaf, msg2) == -1) 800275970Scy tt_abort_msg("Failed to unmarshal message."); 801275970Scy 802275970Scy evutil_gettimeofday(&tv_end, NULL); 803275970Scy evutil_timersub(&tv_end, &tv_start, &tv_end); 804275970Scy TT_BLATHER(("(%.1f us/add) ", 805275970Scy (float)tv_end.tv_sec/(float)i * 1000000.0 + 806275970Scy tv_end.tv_usec / (float)i)); 807275970Scy 808275970Scy if (!EVTAG_HAS(msg2, from_name) || 809275970Scy !EVTAG_HAS(msg2, to_name) || 810275970Scy !EVTAG_HAS(msg2, attack)) { 811275970Scy tt_abort_msg("Missing data structures."); 812275970Scy } 813275970Scy 814275970Scy if (EVTAG_GET(msg2, attack, &attack) == -1) { 815275970Scy tt_abort_msg("Could not get attack."); 816275970Scy } 817275970Scy 818275970Scy if (EVTAG_ARRAY_LEN(msg2, run) != i) { 819275970Scy tt_abort_msg("Wrong number of run messages."); 820275970Scy } 821275970Scy 822275970Scy /* get the very first run message */ 823275970Scy if (EVTAG_ARRAY_GET(msg2, run, 0, &run) == -1) { 824275970Scy tt_abort_msg("Failed to get run msg."); 825275970Scy } else { 826275970Scy /* verify the notes */ 827275970Scy char *note_one, *note_two; 828275970Scy ev_uint64_t large_number; 829275970Scy ev_uint32_t short_number; 830275970Scy 831275970Scy if (EVTAG_ARRAY_LEN(run, notes) != 2) { 832275970Scy tt_abort_msg("Wrong number of note strings."); 833275970Scy } 834275970Scy 835275970Scy if (EVTAG_ARRAY_GET(run, notes, 0, ¬e_one) == -1 || 836275970Scy EVTAG_ARRAY_GET(run, notes, 1, ¬e_two) == -1) { 837275970Scy tt_abort_msg("Could not get note strings."); 838275970Scy } 839275970Scy 840275970Scy if (strcmp(note_one, "this is my note") || 841275970Scy strcmp(note_two, "pps")) { 842275970Scy tt_abort_msg("Incorrect note strings encoded."); 843275970Scy } 844275970Scy 845275970Scy if (EVTAG_GET(run, large_number, &large_number) == -1 || 846275970Scy large_number != 0xdead0a0bcafebeefLL) { 847275970Scy tt_abort_msg("Incorrrect large_number."); 848275970Scy } 849275970Scy 850275970Scy if (EVTAG_ARRAY_LEN(run, other_numbers) != 2) { 851275970Scy tt_abort_msg("Wrong number of other_numbers."); 852275970Scy } 853275970Scy 854275970Scy if (EVTAG_ARRAY_GET( 855275970Scy run, other_numbers, 0, &short_number) == -1) { 856275970Scy tt_abort_msg("Could not get short number."); 857275970Scy } 858275970Scy tt_uint_op(short_number, ==, 0xdead0a0b); 859275970Scy 860275970Scy } 861275970Scy tt_int_op(EVTAG_ARRAY_LEN(attack, how_often), ==, 3); 862275970Scy 863275970Scy for (i = 0; i < 3; ++i) { 864275970Scy ev_uint32_t res; 865275970Scy if (EVTAG_ARRAY_GET(attack, how_often, i, &res) == -1) { 866275970Scy TT_DIE(("Cannot get %dth how_often msg.", i)); 867275970Scy } 868275970Scy if ((int)res != i) { 869275970Scy TT_DIE(("Wrong message encoded %d != %d", i, res)); 870275970Scy } 871275970Scy } 872275970Scy 873275970Scy test_ok = 1; 874275970Scyend: 875275970Scy if (msg) 876275970Scy msg_free(msg); 877275970Scy if (msg2) 878275970Scy msg_free(msg2); 879275970Scy if (tmp) 880275970Scy evbuffer_free(tmp); 881275970Scy} 882275970Scy 883275970Scy#define RPC_LEGACY(name) \ 884275970Scy { #name, run_legacy_test_fn, TT_FORK|TT_NEED_BASE|TT_LEGACY, \ 885275970Scy &legacy_setup, \ 886275970Scy rpc_##name } 887275970Scy#else 888275970Scy/* NO_PYTHON_EXISTS */ 889275970Scy 890275970Scy#define RPC_LEGACY(name) \ 891275970Scy { #name, NULL, TT_SKIP, NULL, NULL } 892275970Scy 893275970Scy#endif 894275970Scy 895275970Scystruct testcase_t rpc_testcases[] = { 896275970Scy RPC_LEGACY(basic_test), 897275970Scy RPC_LEGACY(basic_message), 898275970Scy RPC_LEGACY(basic_client), 899275970Scy RPC_LEGACY(basic_queued_client), 900275970Scy RPC_LEGACY(basic_client_with_pause), 901275970Scy RPC_LEGACY(client_timeout), 902275970Scy RPC_LEGACY(test), 903275970Scy 904275970Scy END_OF_TESTCASES, 905275970Scy}; 906