1*eabc0478Schristos /* $NetBSD: regress_rpc.c,v 1.6 2024/08/18 20:47:23 christos Exp $ */ 28585484eSchristos 38585484eSchristos /* 48585484eSchristos * Copyright (c) 2003-2007 Niels Provos <provos@citi.umich.edu> 58585484eSchristos * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson 68585484eSchristos * 78585484eSchristos * Redistribution and use in source and binary forms, with or without 88585484eSchristos * modification, are permitted provided that the following conditions 98585484eSchristos * are met: 108585484eSchristos * 1. Redistributions of source code must retain the above copyright 118585484eSchristos * notice, this list of conditions and the following disclaimer. 128585484eSchristos * 2. Redistributions in binary form must reproduce the above copyright 138585484eSchristos * notice, this list of conditions and the following disclaimer in the 148585484eSchristos * documentation and/or other materials provided with the distribution. 158585484eSchristos * 3. The name of the author may not be used to endorse or promote products 168585484eSchristos * derived from this software without specific prior written permission. 178585484eSchristos * 188585484eSchristos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 198585484eSchristos * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 208585484eSchristos * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 218585484eSchristos * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 228585484eSchristos * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 238585484eSchristos * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 248585484eSchristos * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 258585484eSchristos * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 268585484eSchristos * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 278585484eSchristos * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 288585484eSchristos */ 298585484eSchristos 308585484eSchristos /* The old tests here need assertions to work. */ 318585484eSchristos #undef NDEBUG 328585484eSchristos 338585484eSchristos #ifdef _WIN32 348585484eSchristos #include <winsock2.h> 358585484eSchristos #include <windows.h> 368585484eSchristos #endif 378585484eSchristos 388585484eSchristos #include "event2/event-config.h" 398585484eSchristos 408585484eSchristos #include <sys/types.h> 418585484eSchristos #include <sys/stat.h> 428585484eSchristos #ifdef EVENT__HAVE_SYS_TIME_H 438585484eSchristos #include <sys/time.h> 448585484eSchristos #endif 458585484eSchristos #include <sys/queue.h> 468585484eSchristos #ifndef _WIN32 478585484eSchristos #include <sys/socket.h> 488585484eSchristos #include <signal.h> 498585484eSchristos #include <unistd.h> 508585484eSchristos #include <netdb.h> 518585484eSchristos #endif 528585484eSchristos #include <fcntl.h> 538585484eSchristos #include <stdlib.h> 548585484eSchristos #include <stdio.h> 558585484eSchristos #include <string.h> 568585484eSchristos #include <errno.h> 578585484eSchristos #include <assert.h> 588585484eSchristos 598585484eSchristos #include "event2/buffer.h" 608585484eSchristos #include "event2/event.h" 618585484eSchristos #include "event2/event_compat.h" 628585484eSchristos #include "event2/http.h" 638585484eSchristos #include "event2/http_compat.h" 648585484eSchristos #include "event2/http_struct.h" 658585484eSchristos #include "event2/rpc.h" 668585484eSchristos #include "event2/rpc_struct.h" 678585484eSchristos #include "event2/tag.h" 688585484eSchristos #include "log-internal.h" 698585484eSchristos 708585484eSchristos #include "regress.gen.h" 718585484eSchristos 728585484eSchristos #include "regress.h" 738585484eSchristos #include "regress_testutils.h" 748585484eSchristos 758585484eSchristos #ifndef NO_PYTHON_EXISTS 768585484eSchristos 778585484eSchristos static struct evhttp * 788585484eSchristos http_setup(ev_uint16_t *pport) 798585484eSchristos { 808585484eSchristos struct evhttp *myhttp; 818585484eSchristos ev_uint16_t port; 828585484eSchristos struct evhttp_bound_socket *sock; 838585484eSchristos 848585484eSchristos myhttp = evhttp_new(NULL); 858585484eSchristos if (!myhttp) 868585484eSchristos event_errx(1, "Could not start web server"); 878585484eSchristos 888585484eSchristos /* Try a few different ports */ 898585484eSchristos sock = evhttp_bind_socket_with_handle(myhttp, "127.0.0.1", 0); 908585484eSchristos if (!sock) 918585484eSchristos event_errx(1, "Couldn't open web port"); 928585484eSchristos 938585484eSchristos port = regress_get_socket_port(evhttp_bound_socket_get_fd(sock)); 948585484eSchristos 958585484eSchristos *pport = port; 968585484eSchristos return (myhttp); 978585484eSchristos } 988585484eSchristos 998585484eSchristos EVRPC_HEADER(Message, msg, kill) 1008585484eSchristos EVRPC_HEADER(NeverReply, msg, kill) 1018585484eSchristos 1028585484eSchristos EVRPC_GENERATE(Message, msg, kill) 1038585484eSchristos EVRPC_GENERATE(NeverReply, msg, kill) 1048585484eSchristos 1058585484eSchristos static int need_input_hook = 0; 1068585484eSchristos static int need_output_hook = 0; 1078585484eSchristos 1088585484eSchristos static void 1098585484eSchristos MessageCb(EVRPC_STRUCT(Message)* rpc, void *arg) 1108585484eSchristos { 1118585484eSchristos struct kill* kill_reply = rpc->reply; 1128585484eSchristos 1138585484eSchristos if (need_input_hook) { 1148585484eSchristos struct evhttp_request* req = EVRPC_REQUEST_HTTP(rpc); 1158585484eSchristos const char *header = evhttp_find_header( 1168585484eSchristos req->input_headers, "X-Hook"); 1178585484eSchristos assert(header); 1188585484eSchristos assert(strcmp(header, "input") == 0); 1198585484eSchristos } 1208585484eSchristos 1218585484eSchristos /* we just want to fill in some non-sense */ 1228585484eSchristos EVTAG_ASSIGN(kill_reply, weapon, "dagger"); 1238585484eSchristos EVTAG_ASSIGN(kill_reply, action, "wave around like an idiot"); 1248585484eSchristos 1258585484eSchristos /* no reply to the RPC */ 1268585484eSchristos EVRPC_REQUEST_DONE(rpc); 1278585484eSchristos } 1288585484eSchristos 1298585484eSchristos static EVRPC_STRUCT(NeverReply) *saved_rpc; 1308585484eSchristos 1318585484eSchristos static void 1328585484eSchristos NeverReplyCb(EVRPC_STRUCT(NeverReply)* rpc, void *arg) 1338585484eSchristos { 1348585484eSchristos test_ok += 1; 1358585484eSchristos saved_rpc = rpc; 1368585484eSchristos } 1378585484eSchristos 1388585484eSchristos static void 1398585484eSchristos rpc_setup(struct evhttp **phttp, ev_uint16_t *pport, struct evrpc_base **pbase) 1408585484eSchristos { 1418585484eSchristos ev_uint16_t port; 1428585484eSchristos struct evhttp *http = NULL; 1438585484eSchristos struct evrpc_base *base = NULL; 1448585484eSchristos 1458585484eSchristos http = http_setup(&port); 1468585484eSchristos base = evrpc_init(http); 1478585484eSchristos 1488585484eSchristos EVRPC_REGISTER(base, Message, msg, kill, MessageCb, NULL); 1498585484eSchristos EVRPC_REGISTER(base, NeverReply, msg, kill, NeverReplyCb, NULL); 1508585484eSchristos 1518585484eSchristos *phttp = http; 1528585484eSchristos *pport = port; 1538585484eSchristos *pbase = base; 1548585484eSchristos 1558585484eSchristos need_input_hook = 0; 1568585484eSchristos need_output_hook = 0; 1578585484eSchristos } 1588585484eSchristos 1598585484eSchristos static void 1608585484eSchristos rpc_teardown(struct evrpc_base *base) 1618585484eSchristos { 1628585484eSchristos assert(EVRPC_UNREGISTER(base, Message) == 0); 1638585484eSchristos assert(EVRPC_UNREGISTER(base, NeverReply) == 0); 1648585484eSchristos 1658585484eSchristos evrpc_free(base); 1668585484eSchristos } 1678585484eSchristos 1688585484eSchristos static void 1698585484eSchristos rpc_postrequest_failure(struct evhttp_request *req, void *arg) 1708585484eSchristos { 1718585484eSchristos if (req->response_code != HTTP_SERVUNAVAIL) { 1728585484eSchristos 1738585484eSchristos fprintf(stderr, "FAILED (response code)\n"); 1748585484eSchristos exit(1); 1758585484eSchristos } 1768585484eSchristos 1778585484eSchristos test_ok = 1; 1788585484eSchristos event_loopexit(NULL); 1798585484eSchristos } 1808585484eSchristos 1818585484eSchristos /* 1828585484eSchristos * Test a malformed payload submitted as an RPC 1838585484eSchristos */ 1848585484eSchristos 1858585484eSchristos static void 1868585484eSchristos rpc_basic_test(void) 1878585484eSchristos { 1888585484eSchristos ev_uint16_t port; 1898585484eSchristos struct evhttp *http = NULL; 1908585484eSchristos struct evrpc_base *base = NULL; 1918585484eSchristos struct evhttp_connection *evcon = NULL; 1928585484eSchristos struct evhttp_request *req = NULL; 1938585484eSchristos 1948585484eSchristos rpc_setup(&http, &port, &base); 1958585484eSchristos 1968585484eSchristos evcon = evhttp_connection_new("127.0.0.1", port); 1978585484eSchristos tt_assert(evcon); 1988585484eSchristos 1998585484eSchristos /* 2008585484eSchristos * At this point, we want to schedule an HTTP POST request 2018585484eSchristos * server using our make request method. 2028585484eSchristos */ 2038585484eSchristos 2048585484eSchristos req = evhttp_request_new(rpc_postrequest_failure, NULL); 2058585484eSchristos tt_assert(req); 2068585484eSchristos 2078585484eSchristos /* Add the information that we care about */ 2088585484eSchristos evhttp_add_header(req->output_headers, "Host", "somehost"); 2098585484eSchristos evbuffer_add_printf(req->output_buffer, "Some Nonsense"); 2108585484eSchristos 2118585484eSchristos if (evhttp_make_request(evcon, req, 2128585484eSchristos EVHTTP_REQ_POST, 2138585484eSchristos "/.rpc.Message") == -1) { 2148585484eSchristos tt_abort(); 2158585484eSchristos } 2168585484eSchristos 2178585484eSchristos test_ok = 0; 2188585484eSchristos 2198585484eSchristos event_dispatch(); 2208585484eSchristos 2218585484eSchristos evhttp_connection_free(evcon); 2228585484eSchristos 2238585484eSchristos rpc_teardown(base); 2248585484eSchristos 2258585484eSchristos tt_assert(test_ok == 1); 2268585484eSchristos 2278585484eSchristos end: 2288585484eSchristos evhttp_free(http); 2298585484eSchristos } 2308585484eSchristos 2318585484eSchristos static void 2328585484eSchristos rpc_postrequest_done(struct evhttp_request *req, void *arg) 2338585484eSchristos { 2348585484eSchristos struct kill* kill_reply = NULL; 2358585484eSchristos 2368585484eSchristos if (req->response_code != HTTP_OK) { 2378585484eSchristos fprintf(stderr, "FAILED (response code)\n"); 2388585484eSchristos exit(1); 2398585484eSchristos } 2408585484eSchristos 2418585484eSchristos kill_reply = kill_new(); 2428585484eSchristos 2438585484eSchristos if ((kill_unmarshal(kill_reply, req->input_buffer)) == -1) { 2448585484eSchristos fprintf(stderr, "FAILED (unmarshal)\n"); 2458585484eSchristos exit(1); 2468585484eSchristos } 2478585484eSchristos 2488585484eSchristos kill_free(kill_reply); 2498585484eSchristos 2508585484eSchristos test_ok = 1; 2518585484eSchristos event_loopexit(NULL); 2528585484eSchristos } 2538585484eSchristos 2548585484eSchristos static void 2558585484eSchristos rpc_basic_message(void) 2568585484eSchristos { 2578585484eSchristos ev_uint16_t port; 2588585484eSchristos struct evhttp *http = NULL; 2598585484eSchristos struct evrpc_base *base = NULL; 2608585484eSchristos struct evhttp_connection *evcon = NULL; 2618585484eSchristos struct evhttp_request *req = NULL; 2628585484eSchristos struct msg *msg; 2638585484eSchristos 2648585484eSchristos rpc_setup(&http, &port, &base); 2658585484eSchristos 2668585484eSchristos evcon = evhttp_connection_new("127.0.0.1", port); 2678585484eSchristos tt_assert(evcon); 2688585484eSchristos 2698585484eSchristos /* 2708585484eSchristos * At this point, we want to schedule an HTTP POST request 2718585484eSchristos * server using our make request method. 2728585484eSchristos */ 2738585484eSchristos 2748585484eSchristos req = evhttp_request_new(rpc_postrequest_done, NULL); 2758585484eSchristos if (req == NULL) { 2768585484eSchristos fprintf(stdout, "FAILED\n"); 2778585484eSchristos exit(1); 2788585484eSchristos } 2798585484eSchristos 2808585484eSchristos /* Add the information that we care about */ 2818585484eSchristos evhttp_add_header(req->output_headers, "Host", "somehost"); 2828585484eSchristos 2838585484eSchristos /* set up the basic message */ 2848585484eSchristos msg = msg_new(); 2858585484eSchristos EVTAG_ASSIGN(msg, from_name, "niels"); 2868585484eSchristos EVTAG_ASSIGN(msg, to_name, "tester"); 2878585484eSchristos msg_marshal(req->output_buffer, msg); 2888585484eSchristos msg_free(msg); 2898585484eSchristos 2908585484eSchristos if (evhttp_make_request(evcon, req, 2918585484eSchristos EVHTTP_REQ_POST, 2928585484eSchristos "/.rpc.Message") == -1) { 2938585484eSchristos fprintf(stdout, "FAILED\n"); 2948585484eSchristos exit(1); 2958585484eSchristos } 2968585484eSchristos 2978585484eSchristos test_ok = 0; 2988585484eSchristos 2998585484eSchristos event_dispatch(); 3008585484eSchristos 3018585484eSchristos evhttp_connection_free(evcon); 3028585484eSchristos 3038585484eSchristos rpc_teardown(base); 3048585484eSchristos 3058585484eSchristos end: 3068585484eSchristos evhttp_free(http); 3078585484eSchristos } 3088585484eSchristos 3098585484eSchristos static struct evrpc_pool * 3108585484eSchristos rpc_pool_with_connection(ev_uint16_t port) 3118585484eSchristos { 3128585484eSchristos struct evhttp_connection *evcon; 3138585484eSchristos struct evrpc_pool *pool; 3148585484eSchristos 3158585484eSchristos pool = evrpc_pool_new(NULL); 3168585484eSchristos assert(pool != NULL); 3178585484eSchristos 3188585484eSchristos evcon = evhttp_connection_new("127.0.0.1", port); 3198585484eSchristos assert(evcon != NULL); 3208585484eSchristos 3218585484eSchristos evrpc_pool_add_connection(pool, evcon); 3228585484eSchristos 3238585484eSchristos return (pool); 3248585484eSchristos } 3258585484eSchristos 3268585484eSchristos static void 3278585484eSchristos GotKillCb(struct evrpc_status *status, 3288585484eSchristos struct msg *msg, struct kill *kill, void *arg) 3298585484eSchristos { 3308585484eSchristos char *weapon; 3318585484eSchristos char *action; 3328585484eSchristos 3338585484eSchristos if (need_output_hook) { 3348585484eSchristos struct evhttp_request *req = status->http_req; 3358585484eSchristos const char *header = evhttp_find_header( 3368585484eSchristos req->input_headers, "X-Pool-Hook"); 3378585484eSchristos assert(header); 3388585484eSchristos assert(strcmp(header, "ran") == 0); 3398585484eSchristos } 3408585484eSchristos 3418585484eSchristos if (status->error != EVRPC_STATUS_ERR_NONE) 3428585484eSchristos goto done; 3438585484eSchristos 3448585484eSchristos if (EVTAG_GET(kill, weapon, &weapon) == -1) { 3458585484eSchristos fprintf(stderr, "get weapon\n"); 3468585484eSchristos goto done; 3478585484eSchristos } 3488585484eSchristos if (EVTAG_GET(kill, action, &action) == -1) { 3498585484eSchristos fprintf(stderr, "get action\n"); 3508585484eSchristos goto done; 3518585484eSchristos } 3528585484eSchristos 3538585484eSchristos if (strcmp(weapon, "dagger")) 3548585484eSchristos goto done; 3558585484eSchristos 3568585484eSchristos if (strcmp(action, "wave around like an idiot")) 3578585484eSchristos goto done; 3588585484eSchristos 3598585484eSchristos test_ok += 1; 3608585484eSchristos 3618585484eSchristos done: 3628585484eSchristos event_loopexit(NULL); 3638585484eSchristos } 3648585484eSchristos 3658585484eSchristos static void 3668585484eSchristos GotKillCbTwo(struct evrpc_status *status, 3678585484eSchristos struct msg *msg, struct kill *kill, void *arg) 3688585484eSchristos { 3698585484eSchristos char *weapon; 3708585484eSchristos char *action; 3718585484eSchristos 3728585484eSchristos if (status->error != EVRPC_STATUS_ERR_NONE) 3738585484eSchristos goto done; 3748585484eSchristos 3758585484eSchristos if (EVTAG_GET(kill, weapon, &weapon) == -1) { 3768585484eSchristos fprintf(stderr, "get weapon\n"); 3778585484eSchristos goto done; 3788585484eSchristos } 3798585484eSchristos if (EVTAG_GET(kill, action, &action) == -1) { 3808585484eSchristos fprintf(stderr, "get action\n"); 3818585484eSchristos goto done; 3828585484eSchristos } 3838585484eSchristos 3848585484eSchristos if (strcmp(weapon, "dagger")) 3858585484eSchristos goto done; 3868585484eSchristos 3878585484eSchristos if (strcmp(action, "wave around like an idiot")) 3888585484eSchristos goto done; 3898585484eSchristos 3908585484eSchristos test_ok += 1; 3918585484eSchristos 3928585484eSchristos done: 3938585484eSchristos if (test_ok == 2) 3948585484eSchristos event_loopexit(NULL); 3958585484eSchristos } 3968585484eSchristos 3978585484eSchristos static int 3988585484eSchristos rpc_hook_add_header(void *ctx, struct evhttp_request *req, 3998585484eSchristos struct evbuffer *evbuf, void *arg) 4008585484eSchristos { 4018585484eSchristos const char *hook_type = arg; 4028585484eSchristos if (strcmp("input", hook_type) == 0) 4038585484eSchristos evhttp_add_header(req->input_headers, "X-Hook", hook_type); 4048585484eSchristos else 4058585484eSchristos evhttp_add_header(req->output_headers, "X-Hook", hook_type); 4068585484eSchristos 4078585484eSchristos assert(evrpc_hook_get_connection(ctx) != NULL); 4088585484eSchristos 4098585484eSchristos return (EVRPC_CONTINUE); 4108585484eSchristos } 4118585484eSchristos 4128585484eSchristos static int 4138585484eSchristos rpc_hook_add_meta(void *ctx, struct evhttp_request *req, 4148585484eSchristos struct evbuffer *evbuf, void *arg) 4158585484eSchristos { 4168585484eSchristos evrpc_hook_add_meta(ctx, "meta", "test", 5); 4178585484eSchristos 4188585484eSchristos assert(evrpc_hook_get_connection(ctx) != NULL); 4198585484eSchristos 4208585484eSchristos return (EVRPC_CONTINUE); 4218585484eSchristos } 4228585484eSchristos 4238585484eSchristos static int 4248585484eSchristos rpc_hook_remove_header(void *ctx, struct evhttp_request *req, 4258585484eSchristos struct evbuffer *evbuf, void *arg) 4268585484eSchristos { 4278585484eSchristos const char *header = evhttp_find_header(req->input_headers, "X-Hook"); 4288585484eSchristos void *data = NULL; 4298585484eSchristos size_t data_len = 0; 4308585484eSchristos 4318585484eSchristos assert(header != NULL); 4328585484eSchristos assert(strcmp(header, arg) == 0); 4338585484eSchristos 4348585484eSchristos evhttp_remove_header(req->input_headers, "X-Hook"); 4358585484eSchristos evhttp_add_header(req->input_headers, "X-Pool-Hook", "ran"); 4368585484eSchristos 4378585484eSchristos assert(evrpc_hook_find_meta(ctx, "meta", &data, &data_len) == 0); 4388585484eSchristos assert(data != NULL); 4398585484eSchristos assert(data_len == 5); 4408585484eSchristos 4418585484eSchristos assert(evrpc_hook_get_connection(ctx) != NULL); 4428585484eSchristos 4438585484eSchristos return (EVRPC_CONTINUE); 4448585484eSchristos } 4458585484eSchristos 4468585484eSchristos static void 4478585484eSchristos rpc_basic_client(void) 4488585484eSchristos { 4498585484eSchristos ev_uint16_t port; 4508585484eSchristos struct evhttp *http = NULL; 4518585484eSchristos struct evrpc_base *base = NULL; 4528585484eSchristos struct evrpc_pool *pool = NULL; 4538585484eSchristos struct msg *msg = NULL; 4548585484eSchristos struct kill *kill = NULL; 4558585484eSchristos 4568585484eSchristos rpc_setup(&http, &port, &base); 4578585484eSchristos 4588585484eSchristos need_input_hook = 1; 4598585484eSchristos need_output_hook = 1; 4608585484eSchristos 4618585484eSchristos assert(evrpc_add_hook(base, EVRPC_INPUT, rpc_hook_add_header, (void*)"input") 4628585484eSchristos != NULL); 4638585484eSchristos assert(evrpc_add_hook(base, EVRPC_OUTPUT, rpc_hook_add_header, (void*)"output") 4648585484eSchristos != NULL); 4658585484eSchristos 4668585484eSchristos pool = rpc_pool_with_connection(port); 467b8ecfcfeSchristos tt_assert(pool); 4688585484eSchristos 4698585484eSchristos assert(evrpc_add_hook(pool, EVRPC_OUTPUT, rpc_hook_add_meta, NULL)); 4708585484eSchristos assert(evrpc_add_hook(pool, EVRPC_INPUT, rpc_hook_remove_header, (void*)"output")); 4718585484eSchristos 4728585484eSchristos /* set up the basic message */ 4738585484eSchristos msg = msg_new(); 474b8ecfcfeSchristos tt_assert(msg); 4758585484eSchristos EVTAG_ASSIGN(msg, from_name, "niels"); 4768585484eSchristos EVTAG_ASSIGN(msg, to_name, "tester"); 4778585484eSchristos 4788585484eSchristos kill = kill_new(); 4798585484eSchristos 4808585484eSchristos EVRPC_MAKE_REQUEST(Message, pool, msg, kill, GotKillCb, NULL); 4818585484eSchristos 4828585484eSchristos test_ok = 0; 4838585484eSchristos 4848585484eSchristos event_dispatch(); 4858585484eSchristos 4868585484eSchristos tt_assert(test_ok == 1); 4878585484eSchristos 4888585484eSchristos /* we do it twice to make sure that reuse works correctly */ 4898585484eSchristos kill_clear(kill); 4908585484eSchristos 4918585484eSchristos EVRPC_MAKE_REQUEST(Message, pool, msg, kill, GotKillCb, NULL); 4928585484eSchristos 4938585484eSchristos event_dispatch(); 4948585484eSchristos 4958585484eSchristos tt_assert(test_ok == 2); 4968585484eSchristos 4978585484eSchristos /* we do it trice to make sure other stuff works, too */ 4988585484eSchristos kill_clear(kill); 4998585484eSchristos 5008585484eSchristos { 5018585484eSchristos struct evrpc_request_wrapper *ctx = 5028585484eSchristos EVRPC_MAKE_CTX(Message, msg, kill, 5038585484eSchristos pool, msg, kill, GotKillCb, NULL); 5048585484eSchristos evrpc_make_request(ctx); 5058585484eSchristos } 5068585484eSchristos 5078585484eSchristos event_dispatch(); 5088585484eSchristos 5098585484eSchristos rpc_teardown(base); 5108585484eSchristos 5118585484eSchristos tt_assert(test_ok == 3); 5128585484eSchristos 5138585484eSchristos end: 5148585484eSchristos if (msg) 5158585484eSchristos msg_free(msg); 5168585484eSchristos if (kill) 5178585484eSchristos kill_free(kill); 5188585484eSchristos 5198585484eSchristos if (pool) 5208585484eSchristos evrpc_pool_free(pool); 5218585484eSchristos if (http) 5228585484eSchristos evhttp_free(http); 5238585484eSchristos 5248585484eSchristos need_input_hook = 0; 5258585484eSchristos need_output_hook = 0; 5268585484eSchristos } 5278585484eSchristos 5288585484eSchristos /* 5298585484eSchristos * We are testing that the second requests gets send over the same 5308585484eSchristos * connection after the first RPCs completes. 5318585484eSchristos */ 5328585484eSchristos static void 5338585484eSchristos rpc_basic_queued_client(void) 5348585484eSchristos { 5358585484eSchristos ev_uint16_t port; 5368585484eSchristos struct evhttp *http = NULL; 5378585484eSchristos struct evrpc_base *base = NULL; 5388585484eSchristos struct evrpc_pool *pool = NULL; 5398585484eSchristos struct msg *msg=NULL; 5408585484eSchristos struct kill *kill_one=NULL, *kill_two=NULL; 5418585484eSchristos 5428585484eSchristos rpc_setup(&http, &port, &base); 5438585484eSchristos 5448585484eSchristos pool = rpc_pool_with_connection(port); 545b8ecfcfeSchristos tt_assert(pool); 5468585484eSchristos 5478585484eSchristos /* set up the basic message */ 5488585484eSchristos msg = msg_new(); 549b8ecfcfeSchristos tt_assert(msg); 5508585484eSchristos EVTAG_ASSIGN(msg, from_name, "niels"); 5518585484eSchristos EVTAG_ASSIGN(msg, to_name, "tester"); 5528585484eSchristos 5538585484eSchristos kill_one = kill_new(); 5548585484eSchristos kill_two = kill_new(); 5558585484eSchristos 5568585484eSchristos EVRPC_MAKE_REQUEST(Message, pool, msg, kill_one, GotKillCbTwo, NULL); 5578585484eSchristos EVRPC_MAKE_REQUEST(Message, pool, msg, kill_two, GotKillCb, NULL); 5588585484eSchristos 5598585484eSchristos test_ok = 0; 5608585484eSchristos 5618585484eSchristos event_dispatch(); 5628585484eSchristos 5638585484eSchristos rpc_teardown(base); 5648585484eSchristos 5658585484eSchristos tt_assert(test_ok == 2); 5668585484eSchristos 5678585484eSchristos end: 5688585484eSchristos if (msg) 5698585484eSchristos msg_free(msg); 5708585484eSchristos if (kill_one) 5718585484eSchristos kill_free(kill_one); 5728585484eSchristos if (kill_two) 5738585484eSchristos kill_free(kill_two); 5748585484eSchristos 5758585484eSchristos if (pool) 5768585484eSchristos evrpc_pool_free(pool); 5778585484eSchristos if (http) 5788585484eSchristos evhttp_free(http); 5798585484eSchristos } 5808585484eSchristos 5818585484eSchristos static void 5828585484eSchristos GotErrorCb(struct evrpc_status *status, 5838585484eSchristos struct msg *msg, struct kill *kill, void *arg) 5848585484eSchristos { 5858585484eSchristos if (status->error != EVRPC_STATUS_ERR_TIMEOUT) 5868585484eSchristos goto done; 5878585484eSchristos 5888585484eSchristos /* should never be complete but just to check */ 5898585484eSchristos if (kill_complete(kill) == 0) 5908585484eSchristos goto done; 5918585484eSchristos 5928585484eSchristos test_ok += 1; 5938585484eSchristos 5948585484eSchristos done: 5958585484eSchristos event_loopexit(NULL); 5968585484eSchristos } 5978585484eSchristos 5988585484eSchristos /* we just pause the rpc and continue it in the next callback */ 5998585484eSchristos 6008585484eSchristos struct rpc_hook_ctx_ { 6018585484eSchristos void *vbase; 6028585484eSchristos void *ctx; 6038585484eSchristos }; 6048585484eSchristos 6058585484eSchristos static int hook_pause_cb_called=0; 6068585484eSchristos 6078585484eSchristos static void 6088585484eSchristos rpc_hook_pause_cb(evutil_socket_t fd, short what, void *arg) 6098585484eSchristos { 6108585484eSchristos struct rpc_hook_ctx_ *ctx = arg; 6118585484eSchristos ++hook_pause_cb_called; 6128585484eSchristos evrpc_resume_request(ctx->vbase, ctx->ctx, EVRPC_CONTINUE); 6138585484eSchristos free(arg); 6148585484eSchristos } 6158585484eSchristos 6168585484eSchristos static int 6178585484eSchristos rpc_hook_pause(void *ctx, struct evhttp_request *req, struct evbuffer *evbuf, 6188585484eSchristos void *arg) 6198585484eSchristos { 6208585484eSchristos struct rpc_hook_ctx_ *tmp = malloc(sizeof(*tmp)); 6218585484eSchristos struct timeval tv; 6228585484eSchristos 6238585484eSchristos assert(tmp != NULL); 6248585484eSchristos tmp->vbase = arg; 6258585484eSchristos tmp->ctx = ctx; 6268585484eSchristos 6278585484eSchristos memset(&tv, 0, sizeof(tv)); 6288585484eSchristos event_once(-1, EV_TIMEOUT, rpc_hook_pause_cb, tmp, &tv); 6298585484eSchristos return EVRPC_PAUSE; 6308585484eSchristos } 6318585484eSchristos 6328585484eSchristos static void 6338585484eSchristos rpc_basic_client_with_pause(void) 6348585484eSchristos { 6358585484eSchristos ev_uint16_t port; 6368585484eSchristos struct evhttp *http = NULL; 6378585484eSchristos struct evrpc_base *base = NULL; 6388585484eSchristos struct evrpc_pool *pool = NULL; 6398585484eSchristos struct msg *msg = NULL; 6408585484eSchristos struct kill *kill= NULL; 6418585484eSchristos 6428585484eSchristos rpc_setup(&http, &port, &base); 6438585484eSchristos 6448585484eSchristos assert(evrpc_add_hook(base, EVRPC_INPUT, rpc_hook_pause, base)); 6458585484eSchristos assert(evrpc_add_hook(base, EVRPC_OUTPUT, rpc_hook_pause, base)); 6468585484eSchristos 6478585484eSchristos pool = rpc_pool_with_connection(port); 648b8ecfcfeSchristos tt_assert(pool); 6498585484eSchristos assert(evrpc_add_hook(pool, EVRPC_INPUT, rpc_hook_pause, pool)); 6508585484eSchristos assert(evrpc_add_hook(pool, EVRPC_OUTPUT, rpc_hook_pause, pool)); 6518585484eSchristos 6528585484eSchristos /* set up the basic message */ 6538585484eSchristos msg = msg_new(); 654b8ecfcfeSchristos tt_assert(msg); 6558585484eSchristos EVTAG_ASSIGN(msg, from_name, "niels"); 6568585484eSchristos EVTAG_ASSIGN(msg, to_name, "tester"); 6578585484eSchristos 6588585484eSchristos kill = kill_new(); 6598585484eSchristos 6608585484eSchristos EVRPC_MAKE_REQUEST(Message, pool, msg, kill, GotKillCb, NULL); 6618585484eSchristos 6628585484eSchristos test_ok = 0; 6638585484eSchristos 6648585484eSchristos event_dispatch(); 6658585484eSchristos 6668585484eSchristos tt_int_op(test_ok, ==, 1); 6678585484eSchristos tt_int_op(hook_pause_cb_called, ==, 4); 6688585484eSchristos 6698585484eSchristos end: 6708585484eSchristos if (base) 6718585484eSchristos rpc_teardown(base); 6728585484eSchristos 6738585484eSchristos if (msg) 6748585484eSchristos msg_free(msg); 6758585484eSchristos if (kill) 6768585484eSchristos kill_free(kill); 6778585484eSchristos 6788585484eSchristos if (pool) 6798585484eSchristos evrpc_pool_free(pool); 6808585484eSchristos if (http) 6818585484eSchristos evhttp_free(http); 6828585484eSchristos } 6838585484eSchristos 6848585484eSchristos static void 6858585484eSchristos rpc_client_timeout(void) 6868585484eSchristos { 6878585484eSchristos ev_uint16_t port; 6888585484eSchristos struct evhttp *http = NULL; 6898585484eSchristos struct evrpc_base *base = NULL; 6908585484eSchristos struct evrpc_pool *pool = NULL; 6918585484eSchristos struct msg *msg = NULL; 6928585484eSchristos struct kill *kill = NULL; 6938585484eSchristos 6948585484eSchristos rpc_setup(&http, &port, &base); 6958585484eSchristos 6968585484eSchristos pool = rpc_pool_with_connection(port); 697b8ecfcfeSchristos tt_assert(pool); 6988585484eSchristos 6998585484eSchristos /* set the timeout to 1 second. */ 7008585484eSchristos evrpc_pool_set_timeout(pool, 1); 7018585484eSchristos 7028585484eSchristos /* set up the basic message */ 7038585484eSchristos msg = msg_new(); 704b8ecfcfeSchristos tt_assert(msg); 7058585484eSchristos EVTAG_ASSIGN(msg, from_name, "niels"); 7068585484eSchristos EVTAG_ASSIGN(msg, to_name, "tester"); 7078585484eSchristos 7088585484eSchristos kill = kill_new(); 7098585484eSchristos 7108585484eSchristos EVRPC_MAKE_REQUEST(NeverReply, pool, msg, kill, GotErrorCb, NULL); 7118585484eSchristos 7128585484eSchristos test_ok = 0; 7138585484eSchristos 7148585484eSchristos event_dispatch(); 7158585484eSchristos 7168585484eSchristos /* free the saved RPC structure up */ 7178585484eSchristos EVRPC_REQUEST_DONE(saved_rpc); 7188585484eSchristos 7198585484eSchristos rpc_teardown(base); 7208585484eSchristos 7218585484eSchristos tt_assert(test_ok == 2); 7228585484eSchristos 7238585484eSchristos end: 7248585484eSchristos if (msg) 7258585484eSchristos msg_free(msg); 7268585484eSchristos if (kill) 7278585484eSchristos kill_free(kill); 7288585484eSchristos 7298585484eSchristos if (pool) 7308585484eSchristos evrpc_pool_free(pool); 7318585484eSchristos if (http) 7328585484eSchristos evhttp_free(http); 7338585484eSchristos } 7348585484eSchristos 7358585484eSchristos static void 7368585484eSchristos rpc_test(void) 7378585484eSchristos { 7388585484eSchristos struct msg *msg = NULL, *msg2 = NULL; 7398585484eSchristos struct kill *attack = NULL; 7408585484eSchristos struct run *run = NULL; 7418585484eSchristos struct evbuffer *tmp = evbuffer_new(); 7428585484eSchristos struct timeval tv_start, tv_end; 7438585484eSchristos ev_uint32_t tag; 7448585484eSchristos int i; 7458585484eSchristos 7468585484eSchristos msg = msg_new(); 7478585484eSchristos 7488585484eSchristos tt_assert(msg); 7498585484eSchristos 7508585484eSchristos EVTAG_ASSIGN(msg, from_name, "niels"); 7518585484eSchristos EVTAG_ASSIGN(msg, to_name, "phoenix"); 7528585484eSchristos 7538585484eSchristos if (EVTAG_GET(msg, attack, &attack) == -1) { 7548585484eSchristos tt_abort_msg("Failed to set kill message."); 7558585484eSchristos } 7568585484eSchristos 7578585484eSchristos EVTAG_ASSIGN(attack, weapon, "feather"); 7588585484eSchristos EVTAG_ASSIGN(attack, action, "tickle"); 7598585484eSchristos for (i = 0; i < 3; ++i) { 7608585484eSchristos if (EVTAG_ARRAY_ADD_VALUE(attack, how_often, i) == NULL) { 7618585484eSchristos tt_abort_msg("Failed to add how_often."); 7628585484eSchristos } 7638585484eSchristos } 7648585484eSchristos 7658585484eSchristos evutil_gettimeofday(&tv_start, NULL); 7668585484eSchristos for (i = 0; i < 1000; ++i) { 7678585484eSchristos run = EVTAG_ARRAY_ADD(msg, run); 7688585484eSchristos if (run == NULL) { 7698585484eSchristos tt_abort_msg("Failed to add run message."); 7708585484eSchristos } 7718585484eSchristos EVTAG_ASSIGN(run, how, "very fast but with some data in it"); 7728585484eSchristos EVTAG_ASSIGN(run, fixed_bytes, 7738585484eSchristos (ev_uint8_t*)"012345678901234567890123"); 7748585484eSchristos 7758585484eSchristos if (EVTAG_ARRAY_ADD_VALUE( 7768585484eSchristos run, notes, "this is my note") == NULL) { 7778585484eSchristos tt_abort_msg("Failed to add note."); 7788585484eSchristos } 7798585484eSchristos if (EVTAG_ARRAY_ADD_VALUE(run, notes, "pps") == NULL) { 7808585484eSchristos tt_abort_msg("Failed to add note"); 7818585484eSchristos } 7828585484eSchristos 7838585484eSchristos EVTAG_ASSIGN(run, large_number, 0xdead0a0bcafebeefLL); 7848585484eSchristos EVTAG_ARRAY_ADD_VALUE(run, other_numbers, 0xdead0a0b); 7858585484eSchristos EVTAG_ARRAY_ADD_VALUE(run, other_numbers, 0xbeefcafe); 7868585484eSchristos } 7878585484eSchristos 7888585484eSchristos if (msg_complete(msg) == -1) 7898585484eSchristos tt_abort_msg("Failed to make complete message."); 7908585484eSchristos 7918585484eSchristos evtag_marshal_msg(tmp, 0xdeaf, msg); 7928585484eSchristos 7938585484eSchristos if (evtag_peek(tmp, &tag) == -1) 7948585484eSchristos tt_abort_msg("Failed to peak tag."); 7958585484eSchristos 7968585484eSchristos if (tag != 0xdeaf) 7978585484eSchristos TT_DIE(("Got incorrect tag: %0x.", (unsigned)tag)); 7988585484eSchristos 7998585484eSchristos msg2 = msg_new(); 8008585484eSchristos if (evtag_unmarshal_msg(tmp, 0xdeaf, msg2) == -1) 8018585484eSchristos tt_abort_msg("Failed to unmarshal message."); 8028585484eSchristos 8038585484eSchristos evutil_gettimeofday(&tv_end, NULL); 8048585484eSchristos evutil_timersub(&tv_end, &tv_start, &tv_end); 8058585484eSchristos TT_BLATHER(("(%.1f us/add) ", 8068585484eSchristos (float)tv_end.tv_sec/(float)i * 1000000.0 + 8078585484eSchristos tv_end.tv_usec / (float)i)); 8088585484eSchristos 8098585484eSchristos if (!EVTAG_HAS(msg2, from_name) || 8108585484eSchristos !EVTAG_HAS(msg2, to_name) || 8118585484eSchristos !EVTAG_HAS(msg2, attack)) { 8128585484eSchristos tt_abort_msg("Missing data structures."); 8138585484eSchristos } 8148585484eSchristos 8158585484eSchristos if (EVTAG_GET(msg2, attack, &attack) == -1) { 8168585484eSchristos tt_abort_msg("Could not get attack."); 8178585484eSchristos } 8188585484eSchristos 8198585484eSchristos if (EVTAG_ARRAY_LEN(msg2, run) != i) { 8208585484eSchristos tt_abort_msg("Wrong number of run messages."); 8218585484eSchristos } 8228585484eSchristos 8238585484eSchristos /* get the very first run message */ 8248585484eSchristos if (EVTAG_ARRAY_GET(msg2, run, 0, &run) == -1) { 8258585484eSchristos tt_abort_msg("Failed to get run msg."); 8268585484eSchristos } else { 8278585484eSchristos /* verify the notes */ 8288585484eSchristos char *note_one, *note_two; 8298585484eSchristos ev_uint64_t large_number; 8308585484eSchristos ev_uint32_t short_number; 8318585484eSchristos 8328585484eSchristos if (EVTAG_ARRAY_LEN(run, notes) != 2) { 8338585484eSchristos tt_abort_msg("Wrong number of note strings."); 8348585484eSchristos } 8358585484eSchristos 8368585484eSchristos if (EVTAG_ARRAY_GET(run, notes, 0, ¬e_one) == -1 || 8378585484eSchristos EVTAG_ARRAY_GET(run, notes, 1, ¬e_two) == -1) { 8388585484eSchristos tt_abort_msg("Could not get note strings."); 8398585484eSchristos } 8408585484eSchristos 8418585484eSchristos if (strcmp(note_one, "this is my note") || 8428585484eSchristos strcmp(note_two, "pps")) { 8438585484eSchristos tt_abort_msg("Incorrect note strings encoded."); 8448585484eSchristos } 8458585484eSchristos 8468585484eSchristos if (EVTAG_GET(run, large_number, &large_number) == -1 || 8478585484eSchristos large_number != 0xdead0a0bcafebeefLL) { 8488585484eSchristos tt_abort_msg("Incorrrect large_number."); 8498585484eSchristos } 8508585484eSchristos 8518585484eSchristos if (EVTAG_ARRAY_LEN(run, other_numbers) != 2) { 8528585484eSchristos tt_abort_msg("Wrong number of other_numbers."); 8538585484eSchristos } 8548585484eSchristos 8558585484eSchristos if (EVTAG_ARRAY_GET( 8568585484eSchristos run, other_numbers, 0, &short_number) == -1) { 8578585484eSchristos tt_abort_msg("Could not get short number."); 8588585484eSchristos } 8598585484eSchristos tt_uint_op(short_number, ==, 0xdead0a0b); 8608585484eSchristos 8618585484eSchristos } 8628585484eSchristos tt_int_op(EVTAG_ARRAY_LEN(attack, how_often), ==, 3); 8638585484eSchristos 8648585484eSchristos for (i = 0; i < 3; ++i) { 8658585484eSchristos ev_uint32_t res; 8668585484eSchristos if (EVTAG_ARRAY_GET(attack, how_often, i, &res) == -1) { 8678585484eSchristos TT_DIE(("Cannot get %dth how_often msg.", i)); 8688585484eSchristos } 8698585484eSchristos if ((int)res != i) { 8708585484eSchristos TT_DIE(("Wrong message encoded %d != %d", i, res)); 8718585484eSchristos } 8728585484eSchristos } 8738585484eSchristos 8748585484eSchristos test_ok = 1; 8758585484eSchristos end: 8768585484eSchristos if (msg) 8778585484eSchristos msg_free(msg); 8788585484eSchristos if (msg2) 8798585484eSchristos msg_free(msg2); 8808585484eSchristos if (tmp) 8818585484eSchristos evbuffer_free(tmp); 8828585484eSchristos } 8838585484eSchristos 884*eabc0478Schristos static void 885*eabc0478Schristos rpc_invalid_type(void) 886*eabc0478Schristos { 887*eabc0478Schristos ev_uint16_t port; 888*eabc0478Schristos struct evhttp *http = NULL; 889*eabc0478Schristos struct evrpc_base *base = NULL; 890*eabc0478Schristos struct evhttp_connection *evcon = NULL; 891*eabc0478Schristos struct evhttp_request *req = NULL; 892*eabc0478Schristos 893*eabc0478Schristos rpc_setup(&http, &port, &base); 894*eabc0478Schristos 895*eabc0478Schristos evcon = evhttp_connection_new("127.0.0.1", port); 896*eabc0478Schristos tt_assert(evcon); 897*eabc0478Schristos 898*eabc0478Schristos /* 899*eabc0478Schristos * At this point, we want to schedule an HTTP POST request 900*eabc0478Schristos * server using our make request method. 901*eabc0478Schristos */ 902*eabc0478Schristos 903*eabc0478Schristos req = evhttp_request_new(rpc_postrequest_failure, NULL); 904*eabc0478Schristos tt_assert(req); 905*eabc0478Schristos 906*eabc0478Schristos /* Add the information that we care about */ 907*eabc0478Schristos evhttp_add_header(req->output_headers, "Host", "somehost"); 908*eabc0478Schristos evbuffer_add_printf(req->output_buffer, "Some Nonsense"); 909*eabc0478Schristos 910*eabc0478Schristos if (evhttp_make_request(evcon, req, 911*eabc0478Schristos EVHTTP_REQ_GET, 912*eabc0478Schristos "/.rpc.Message") == -1) { 913*eabc0478Schristos tt_abort(); 914*eabc0478Schristos } 915*eabc0478Schristos 916*eabc0478Schristos test_ok = 0; 917*eabc0478Schristos 918*eabc0478Schristos event_dispatch(); 919*eabc0478Schristos 920*eabc0478Schristos evhttp_connection_free(evcon); 921*eabc0478Schristos 922*eabc0478Schristos rpc_teardown(base); 923*eabc0478Schristos 924*eabc0478Schristos tt_assert(test_ok == 1); 925*eabc0478Schristos 926*eabc0478Schristos end: 927*eabc0478Schristos evhttp_free(http); 928*eabc0478Schristos } 929*eabc0478Schristos 930*eabc0478Schristos 9318585484eSchristos #define RPC_LEGACY(name) \ 9328585484eSchristos { #name, run_legacy_test_fn, TT_FORK|TT_NEED_BASE|TT_LEGACY, \ 9338585484eSchristos &legacy_setup, \ 9348585484eSchristos rpc_##name } 9358585484eSchristos #else 9368585484eSchristos /* NO_PYTHON_EXISTS */ 9378585484eSchristos 9388585484eSchristos #define RPC_LEGACY(name) \ 9398585484eSchristos { #name, NULL, TT_SKIP, NULL, NULL } 9408585484eSchristos 9418585484eSchristos #endif 9428585484eSchristos 9438585484eSchristos struct testcase_t rpc_testcases[] = { 9448585484eSchristos RPC_LEGACY(basic_test), 9458585484eSchristos RPC_LEGACY(basic_message), 9468585484eSchristos RPC_LEGACY(basic_client), 9478585484eSchristos RPC_LEGACY(basic_queued_client), 9488585484eSchristos RPC_LEGACY(basic_client_with_pause), 949*eabc0478Schristos RPC_LEGACY(invalid_type), 9508585484eSchristos RPC_LEGACY(client_timeout), 9518585484eSchristos RPC_LEGACY(test), 9528585484eSchristos 9538585484eSchristos END_OF_TESTCASES, 9548585484eSchristos }; 955