xref: /freebsd/contrib/libevent/test/regress_rpc.c (revision 61e21613)
1 /*
2  * Copyright (c) 2003-2007 Niels Provos <provos@citi.umich.edu>
3  * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson
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 /* The old tests here need assertions to work. */
29 #undef NDEBUG
30 
31 #ifdef _WIN32
32 #include <winsock2.h>
33 #include <windows.h>
34 #endif
35 
36 #include "event2/event-config.h"
37 
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #ifdef EVENT__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 "event2/buffer.h"
58 #include "event2/event.h"
59 #include "event2/event_compat.h"
60 #include "event2/http.h"
61 #include "event2/http_compat.h"
62 #include "event2/http_struct.h"
63 #include "event2/rpc.h"
64 #include "event2/rpc_struct.h"
65 #include "event2/tag.h"
66 #include "log-internal.h"
67 
68 #include "regress.gen.h"
69 
70 #include "regress.h"
71 #include "regress_testutils.h"
72 
73 #ifndef NO_PYTHON_EXISTS
74 
75 static struct evhttp *
76 http_setup(ev_uint16_t *pport)
77 {
78 	struct evhttp *myhttp;
79 	ev_uint16_t port;
80 	struct evhttp_bound_socket *sock;
81 
82 	myhttp = evhttp_new(NULL);
83 	if (!myhttp)
84 		event_errx(1, "Could not start web server");
85 
86 	/* Try a few different ports */
87 	sock = evhttp_bind_socket_with_handle(myhttp, "127.0.0.1", 0);
88 	if (!sock)
89 		event_errx(1, "Couldn't open web port");
90 
91 	port = regress_get_socket_port(evhttp_bound_socket_get_fd(sock));
92 
93 	*pport = port;
94 	return (myhttp);
95 }
96 
97 EVRPC_HEADER(Message, msg, kill)
98 EVRPC_HEADER(NeverReply, msg, kill)
99 
100 EVRPC_GENERATE(Message, msg, kill)
101 EVRPC_GENERATE(NeverReply, msg, kill)
102 
103 static int need_input_hook = 0;
104 static int need_output_hook = 0;
105 
106 static void
107 MessageCb(EVRPC_STRUCT(Message)* rpc, void *arg)
108 {
109 	struct kill* kill_reply = rpc->reply;
110 
111 	if (need_input_hook) {
112 		struct evhttp_request* req = EVRPC_REQUEST_HTTP(rpc);
113 		const char *header = evhttp_find_header(
114 			req->input_headers, "X-Hook");
115 		assert(header);
116 		assert(strcmp(header, "input") == 0);
117 	}
118 
119 	/* we just want to fill in some non-sense */
120 	EVTAG_ASSIGN(kill_reply, weapon, "dagger");
121 	EVTAG_ASSIGN(kill_reply, action, "wave around like an idiot");
122 
123 	/* no reply to the RPC */
124 	EVRPC_REQUEST_DONE(rpc);
125 }
126 
127 static EVRPC_STRUCT(NeverReply) *saved_rpc;
128 
129 static void
130 NeverReplyCb(EVRPC_STRUCT(NeverReply)* rpc, void *arg)
131 {
132 	test_ok += 1;
133 	saved_rpc = rpc;
134 }
135 
136 static void
137 rpc_setup(struct evhttp **phttp, ev_uint16_t *pport, struct evrpc_base **pbase)
138 {
139 	ev_uint16_t port;
140 	struct evhttp *http = NULL;
141 	struct evrpc_base *base = NULL;
142 
143 	http = http_setup(&port);
144 	base = evrpc_init(http);
145 
146 	EVRPC_REGISTER(base, Message, msg, kill, MessageCb, NULL);
147 	EVRPC_REGISTER(base, NeverReply, msg, kill, NeverReplyCb, NULL);
148 
149 	*phttp = http;
150 	*pport = port;
151 	*pbase = base;
152 
153 	need_input_hook = 0;
154 	need_output_hook = 0;
155 }
156 
157 static void
158 rpc_teardown(struct evrpc_base *base)
159 {
160 	assert(EVRPC_UNREGISTER(base, Message) == 0);
161 	assert(EVRPC_UNREGISTER(base, NeverReply) == 0);
162 
163 	evrpc_free(base);
164 }
165 
166 static void
167 rpc_postrequest_failure(struct evhttp_request *req, void *arg)
168 {
169 	if (req->response_code != HTTP_SERVUNAVAIL) {
170 
171 		fprintf(stderr, "FAILED (response code)\n");
172 		exit(1);
173 	}
174 
175 	test_ok = 1;
176 	event_loopexit(NULL);
177 }
178 
179 /*
180  * Test a malformed payload submitted as an RPC
181  */
182 
183 static void
184 rpc_basic_test(void)
185 {
186 	ev_uint16_t port;
187 	struct evhttp *http = NULL;
188 	struct evrpc_base *base = NULL;
189 	struct evhttp_connection *evcon = NULL;
190 	struct evhttp_request *req = NULL;
191 
192 	rpc_setup(&http, &port, &base);
193 
194 	evcon = evhttp_connection_new("127.0.0.1", port);
195 	tt_assert(evcon);
196 
197 	/*
198 	 * At this point, we want to schedule an HTTP POST request
199 	 * server using our make request method.
200 	 */
201 
202 	req = evhttp_request_new(rpc_postrequest_failure, NULL);
203 	tt_assert(req);
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 		tt_abort();
213 	}
214 
215 	test_ok = 0;
216 
217 	event_dispatch();
218 
219 	evhttp_connection_free(evcon);
220 
221 	rpc_teardown(base);
222 
223 	tt_assert(test_ok == 1);
224 
225 end:
226 	evhttp_free(http);
227 }
228 
229 static void
230 rpc_postrequest_done(struct evhttp_request *req, void *arg)
231 {
232 	struct kill* kill_reply = NULL;
233 
234 	if (req->response_code != HTTP_OK) {
235 		fprintf(stderr, "FAILED (response code)\n");
236 		exit(1);
237 	}
238 
239 	kill_reply = kill_new();
240 
241 	if ((kill_unmarshal(kill_reply, req->input_buffer)) == -1) {
242 		fprintf(stderr, "FAILED (unmarshal)\n");
243 		exit(1);
244 	}
245 
246 	kill_free(kill_reply);
247 
248 	test_ok = 1;
249 	event_loopexit(NULL);
250 }
251 
252 static void
253 rpc_basic_message(void)
254 {
255 	ev_uint16_t port;
256 	struct evhttp *http = NULL;
257 	struct evrpc_base *base = NULL;
258 	struct evhttp_connection *evcon = NULL;
259 	struct evhttp_request *req = NULL;
260 	struct msg *msg;
261 
262 	rpc_setup(&http, &port, &base);
263 
264 	evcon = evhttp_connection_new("127.0.0.1", port);
265 	tt_assert(evcon);
266 
267 	/*
268 	 * At this point, we want to schedule an HTTP POST request
269 	 * server using our make request method.
270 	 */
271 
272 	req = evhttp_request_new(rpc_postrequest_done, NULL);
273 	if (req == NULL) {
274 		fprintf(stdout, "FAILED\n");
275 		exit(1);
276 	}
277 
278 	/* Add the information that we care about */
279 	evhttp_add_header(req->output_headers, "Host", "somehost");
280 
281 	/* set up the basic message */
282 	msg = msg_new();
283 	EVTAG_ASSIGN(msg, from_name, "niels");
284 	EVTAG_ASSIGN(msg, to_name, "tester");
285 	msg_marshal(req->output_buffer, msg);
286 	msg_free(msg);
287 
288 	if (evhttp_make_request(evcon, req,
289 		EVHTTP_REQ_POST,
290 		"/.rpc.Message") == -1) {
291 		fprintf(stdout, "FAILED\n");
292 		exit(1);
293 	}
294 
295 	test_ok = 0;
296 
297 	event_dispatch();
298 
299 	evhttp_connection_free(evcon);
300 
301 	rpc_teardown(base);
302 
303 end:
304 	evhttp_free(http);
305 }
306 
307 static struct evrpc_pool *
308 rpc_pool_with_connection(ev_uint16_t port)
309 {
310 	struct evhttp_connection *evcon;
311 	struct evrpc_pool *pool;
312 
313 	pool = evrpc_pool_new(NULL);
314 	assert(pool != NULL);
315 
316 	evcon = evhttp_connection_new("127.0.0.1", port);
317 	assert(evcon != NULL);
318 
319 	evrpc_pool_add_connection(pool, evcon);
320 
321 	return (pool);
322 }
323 
324 static void
325 GotKillCb(struct evrpc_status *status,
326     struct msg *msg, struct kill *kill, void *arg)
327 {
328 	char *weapon;
329 	char *action;
330 
331 	if (need_output_hook) {
332 		struct evhttp_request *req = status->http_req;
333 		const char *header = evhttp_find_header(
334 			req->input_headers, "X-Pool-Hook");
335 		assert(header);
336 		assert(strcmp(header, "ran") == 0);
337 	}
338 
339 	if (status->error != EVRPC_STATUS_ERR_NONE)
340 		goto done;
341 
342 	if (EVTAG_GET(kill, weapon, &weapon) == -1) {
343 		fprintf(stderr, "get weapon\n");
344 		goto done;
345 	}
346 	if (EVTAG_GET(kill, action, &action) == -1) {
347 		fprintf(stderr, "get action\n");
348 		goto done;
349 	}
350 
351 	if (strcmp(weapon, "dagger"))
352 		goto done;
353 
354 	if (strcmp(action, "wave around like an idiot"))
355 		goto done;
356 
357 	test_ok += 1;
358 
359 done:
360 	event_loopexit(NULL);
361 }
362 
363 static void
364 GotKillCbTwo(struct evrpc_status *status,
365     struct msg *msg, struct kill *kill, void *arg)
366 {
367 	char *weapon;
368 	char *action;
369 
370 	if (status->error != EVRPC_STATUS_ERR_NONE)
371 		goto done;
372 
373 	if (EVTAG_GET(kill, weapon, &weapon) == -1) {
374 		fprintf(stderr, "get weapon\n");
375 		goto done;
376 	}
377 	if (EVTAG_GET(kill, action, &action) == -1) {
378 		fprintf(stderr, "get action\n");
379 		goto done;
380 	}
381 
382 	if (strcmp(weapon, "dagger"))
383 		goto done;
384 
385 	if (strcmp(action, "wave around like an idiot"))
386 		goto done;
387 
388 	test_ok += 1;
389 
390 done:
391 	if (test_ok == 2)
392 		event_loopexit(NULL);
393 }
394 
395 static int
396 rpc_hook_add_header(void *ctx, struct evhttp_request *req,
397     struct evbuffer *evbuf, void *arg)
398 {
399 	const char *hook_type = arg;
400 	if (strcmp("input", hook_type) == 0)
401 		evhttp_add_header(req->input_headers, "X-Hook", hook_type);
402 	else
403 		evhttp_add_header(req->output_headers, "X-Hook", hook_type);
404 
405 	assert(evrpc_hook_get_connection(ctx) != NULL);
406 
407 	return (EVRPC_CONTINUE);
408 }
409 
410 static int
411 rpc_hook_add_meta(void *ctx, struct evhttp_request *req,
412     struct evbuffer *evbuf, void *arg)
413 {
414 	evrpc_hook_add_meta(ctx, "meta", "test", 5);
415 
416 	assert(evrpc_hook_get_connection(ctx) != NULL);
417 
418 	return (EVRPC_CONTINUE);
419 }
420 
421 static int
422 rpc_hook_remove_header(void *ctx, struct evhttp_request *req,
423     struct evbuffer *evbuf, void *arg)
424 {
425 	const char *header = evhttp_find_header(req->input_headers, "X-Hook");
426 	void *data = NULL;
427 	size_t data_len = 0;
428 
429 	assert(header != NULL);
430 	assert(strcmp(header, arg) == 0);
431 
432 	evhttp_remove_header(req->input_headers, "X-Hook");
433 	evhttp_add_header(req->input_headers, "X-Pool-Hook", "ran");
434 
435 	assert(evrpc_hook_find_meta(ctx, "meta", &data, &data_len) == 0);
436 	assert(data != NULL);
437 	assert(data_len == 5);
438 
439 	assert(evrpc_hook_get_connection(ctx) != NULL);
440 
441 	return (EVRPC_CONTINUE);
442 }
443 
444 static void
445 rpc_basic_client(void)
446 {
447 	ev_uint16_t port;
448 	struct evhttp *http = NULL;
449 	struct evrpc_base *base = NULL;
450 	struct evrpc_pool *pool = NULL;
451 	struct msg *msg = NULL;
452 	struct kill *kill = NULL;
453 
454 	rpc_setup(&http, &port, &base);
455 
456 	need_input_hook = 1;
457 	need_output_hook = 1;
458 
459 	assert(evrpc_add_hook(base, EVRPC_INPUT, rpc_hook_add_header, (void*)"input")
460 	    != NULL);
461 	assert(evrpc_add_hook(base, EVRPC_OUTPUT, rpc_hook_add_header, (void*)"output")
462 	    != NULL);
463 
464 	pool = rpc_pool_with_connection(port);
465 	tt_assert(pool);
466 
467 	assert(evrpc_add_hook(pool, EVRPC_OUTPUT, rpc_hook_add_meta, NULL));
468 	assert(evrpc_add_hook(pool, EVRPC_INPUT, rpc_hook_remove_header, (void*)"output"));
469 
470 	/* set up the basic message */
471 	msg = msg_new();
472 	tt_assert(msg);
473 	EVTAG_ASSIGN(msg, from_name, "niels");
474 	EVTAG_ASSIGN(msg, to_name, "tester");
475 
476 	kill = kill_new();
477 
478 	EVRPC_MAKE_REQUEST(Message, pool, msg, kill,  GotKillCb, NULL);
479 
480 	test_ok = 0;
481 
482 	event_dispatch();
483 
484 	tt_assert(test_ok == 1);
485 
486 	/* we do it twice to make sure that reuse works correctly */
487 	kill_clear(kill);
488 
489 	EVRPC_MAKE_REQUEST(Message, pool, msg, kill,  GotKillCb, NULL);
490 
491 	event_dispatch();
492 
493 	tt_assert(test_ok == 2);
494 
495 	/* we do it trice to make sure other stuff works, too */
496 	kill_clear(kill);
497 
498 	{
499 		struct evrpc_request_wrapper *ctx =
500 		    EVRPC_MAKE_CTX(Message, msg, kill,
501 			pool, msg, kill, GotKillCb, NULL);
502 		evrpc_make_request(ctx);
503 	}
504 
505 	event_dispatch();
506 
507 	rpc_teardown(base);
508 
509 	tt_assert(test_ok == 3);
510 
511 end:
512 	if (msg)
513 		msg_free(msg);
514 	if (kill)
515 		kill_free(kill);
516 
517 	if (pool)
518 		evrpc_pool_free(pool);
519 	if (http)
520 		evhttp_free(http);
521 
522 	need_input_hook = 0;
523 	need_output_hook = 0;
524 }
525 
526 /*
527  * We are testing that the second requests gets send over the same
528  * connection after the first RPCs completes.
529  */
530 static void
531 rpc_basic_queued_client(void)
532 {
533 	ev_uint16_t port;
534 	struct evhttp *http = NULL;
535 	struct evrpc_base *base = NULL;
536 	struct evrpc_pool *pool = NULL;
537 	struct msg *msg=NULL;
538 	struct kill *kill_one=NULL, *kill_two=NULL;
539 
540 	rpc_setup(&http, &port, &base);
541 
542 	pool = rpc_pool_with_connection(port);
543 	tt_assert(pool);
544 
545 	/* set up the basic message */
546 	msg = msg_new();
547 	tt_assert(msg);
548 	EVTAG_ASSIGN(msg, from_name, "niels");
549 	EVTAG_ASSIGN(msg, to_name, "tester");
550 
551 	kill_one = kill_new();
552 	kill_two = kill_new();
553 
554 	EVRPC_MAKE_REQUEST(Message, pool, msg, kill_one,  GotKillCbTwo, NULL);
555 	EVRPC_MAKE_REQUEST(Message, pool, msg, kill_two,  GotKillCb, NULL);
556 
557 	test_ok = 0;
558 
559 	event_dispatch();
560 
561 	rpc_teardown(base);
562 
563 	tt_assert(test_ok == 2);
564 
565 end:
566 	if (msg)
567 		msg_free(msg);
568 	if (kill_one)
569 		kill_free(kill_one);
570 	if (kill_two)
571 		kill_free(kill_two);
572 
573 	if (pool)
574 		evrpc_pool_free(pool);
575 	if (http)
576 		evhttp_free(http);
577 }
578 
579 static void
580 GotErrorCb(struct evrpc_status *status,
581     struct msg *msg, struct kill *kill, void *arg)
582 {
583 	if (status->error != EVRPC_STATUS_ERR_TIMEOUT)
584 		goto done;
585 
586 	/* should never be complete but just to check */
587 	if (kill_complete(kill) == 0)
588 		goto done;
589 
590 	test_ok += 1;
591 
592 done:
593 	event_loopexit(NULL);
594 }
595 
596 /* we just pause the rpc and continue it in the next callback */
597 
598 struct rpc_hook_ctx_ {
599 	void *vbase;
600 	void *ctx;
601 };
602 
603 static int hook_pause_cb_called=0;
604 
605 static void
606 rpc_hook_pause_cb(evutil_socket_t fd, short what, void *arg)
607 {
608 	struct rpc_hook_ctx_ *ctx = arg;
609 	++hook_pause_cb_called;
610 	evrpc_resume_request(ctx->vbase, ctx->ctx, EVRPC_CONTINUE);
611 	free(arg);
612 }
613 
614 static int
615 rpc_hook_pause(void *ctx, struct evhttp_request *req, struct evbuffer *evbuf,
616     void *arg)
617 {
618 	struct rpc_hook_ctx_ *tmp = malloc(sizeof(*tmp));
619 	struct timeval tv;
620 
621 	assert(tmp != NULL);
622 	tmp->vbase = arg;
623 	tmp->ctx = ctx;
624 
625 	memset(&tv, 0, sizeof(tv));
626 	event_once(-1, EV_TIMEOUT, rpc_hook_pause_cb, tmp, &tv);
627 	return EVRPC_PAUSE;
628 }
629 
630 static void
631 rpc_basic_client_with_pause(void)
632 {
633 	ev_uint16_t port;
634 	struct evhttp *http = NULL;
635 	struct evrpc_base *base = NULL;
636 	struct evrpc_pool *pool = NULL;
637 	struct msg *msg = NULL;
638 	struct kill *kill= NULL;
639 
640 	rpc_setup(&http, &port, &base);
641 
642 	assert(evrpc_add_hook(base, EVRPC_INPUT, rpc_hook_pause, base));
643 	assert(evrpc_add_hook(base, EVRPC_OUTPUT, rpc_hook_pause, base));
644 
645 	pool = rpc_pool_with_connection(port);
646 	tt_assert(pool);
647 	assert(evrpc_add_hook(pool, EVRPC_INPUT, rpc_hook_pause, pool));
648 	assert(evrpc_add_hook(pool, EVRPC_OUTPUT, rpc_hook_pause, pool));
649 
650 	/* set up the basic message */
651 	msg = msg_new();
652 	tt_assert(msg);
653 	EVTAG_ASSIGN(msg, from_name, "niels");
654 	EVTAG_ASSIGN(msg, to_name, "tester");
655 
656 	kill = kill_new();
657 
658 	EVRPC_MAKE_REQUEST(Message, pool, msg, kill, GotKillCb, NULL);
659 
660 	test_ok = 0;
661 
662 	event_dispatch();
663 
664 	tt_int_op(test_ok, ==, 1);
665 	tt_int_op(hook_pause_cb_called, ==, 4);
666 
667 end:
668 	if (base)
669 		rpc_teardown(base);
670 
671 	if (msg)
672 		msg_free(msg);
673 	if (kill)
674 		kill_free(kill);
675 
676 	if (pool)
677 		evrpc_pool_free(pool);
678 	if (http)
679 		evhttp_free(http);
680 }
681 
682 static void
683 rpc_client_timeout(void)
684 {
685 	ev_uint16_t port;
686 	struct evhttp *http = NULL;
687 	struct evrpc_base *base = NULL;
688 	struct evrpc_pool *pool = NULL;
689 	struct msg *msg = NULL;
690 	struct kill *kill = NULL;
691 
692 	rpc_setup(&http, &port, &base);
693 
694 	pool = rpc_pool_with_connection(port);
695 	tt_assert(pool);
696 
697 	/* set the timeout to 1 second. */
698 	evrpc_pool_set_timeout(pool, 1);
699 
700 	/* set up the basic message */
701 	msg = msg_new();
702 	tt_assert(msg);
703 	EVTAG_ASSIGN(msg, from_name, "niels");
704 	EVTAG_ASSIGN(msg, to_name, "tester");
705 
706 	kill = kill_new();
707 
708 	EVRPC_MAKE_REQUEST(NeverReply, pool, msg, kill, GotErrorCb, NULL);
709 
710 	test_ok = 0;
711 
712 	event_dispatch();
713 
714 	/* free the saved RPC structure up */
715 	EVRPC_REQUEST_DONE(saved_rpc);
716 
717 	rpc_teardown(base);
718 
719 	tt_assert(test_ok == 2);
720 
721 end:
722 	if (msg)
723 		msg_free(msg);
724 	if (kill)
725 		kill_free(kill);
726 
727 	if (pool)
728 		evrpc_pool_free(pool);
729 	if (http)
730 		evhttp_free(http);
731 }
732 
733 static void
734 rpc_test(void)
735 {
736 	struct msg *msg = NULL, *msg2 = NULL;
737 	struct kill *attack = NULL;
738 	struct run *run = NULL;
739 	struct evbuffer *tmp = evbuffer_new();
740 	struct timeval tv_start, tv_end;
741 	ev_uint32_t tag;
742 	int i;
743 
744 	msg = msg_new();
745 
746 	tt_assert(msg);
747 
748 	EVTAG_ASSIGN(msg, from_name, "niels");
749 	EVTAG_ASSIGN(msg, to_name, "phoenix");
750 
751 	if (EVTAG_GET(msg, attack, &attack) == -1) {
752 		tt_abort_msg("Failed to set kill message.");
753 	}
754 
755 	EVTAG_ASSIGN(attack, weapon, "feather");
756 	EVTAG_ASSIGN(attack, action, "tickle");
757 	for (i = 0; i < 3; ++i) {
758 		if (EVTAG_ARRAY_ADD_VALUE(attack, how_often, i) == NULL) {
759 			tt_abort_msg("Failed to add how_often.");
760 		}
761 	}
762 
763 	evutil_gettimeofday(&tv_start, NULL);
764 	for (i = 0; i < 1000; ++i) {
765 		run = EVTAG_ARRAY_ADD(msg, run);
766 		if (run == NULL) {
767 			tt_abort_msg("Failed to add run message.");
768 		}
769 		EVTAG_ASSIGN(run, how, "very fast but with some data in it");
770 		EVTAG_ASSIGN(run, fixed_bytes,
771 		    (ev_uint8_t*)"012345678901234567890123");
772 
773 		if (EVTAG_ARRAY_ADD_VALUE(
774 			    run, notes, "this is my note") == NULL) {
775 			tt_abort_msg("Failed to add note.");
776 		}
777 		if (EVTAG_ARRAY_ADD_VALUE(run, notes, "pps") == NULL) {
778 			tt_abort_msg("Failed to add note");
779 		}
780 
781 		EVTAG_ASSIGN(run, large_number, 0xdead0a0bcafebeefLL);
782 		EVTAG_ARRAY_ADD_VALUE(run, other_numbers, 0xdead0a0b);
783 		EVTAG_ARRAY_ADD_VALUE(run, other_numbers, 0xbeefcafe);
784 	}
785 
786 	if (msg_complete(msg) == -1)
787 		tt_abort_msg("Failed to make complete message.");
788 
789 	evtag_marshal_msg(tmp, 0xdeaf, msg);
790 
791 	if (evtag_peek(tmp, &tag) == -1)
792 		tt_abort_msg("Failed to peak tag.");
793 
794 	if (tag != 0xdeaf)
795 		TT_DIE(("Got incorrect tag: %0x.", (unsigned)tag));
796 
797 	msg2 = msg_new();
798 	if (evtag_unmarshal_msg(tmp, 0xdeaf, msg2) == -1)
799 		tt_abort_msg("Failed to unmarshal message.");
800 
801 	evutil_gettimeofday(&tv_end, NULL);
802 	evutil_timersub(&tv_end, &tv_start, &tv_end);
803 	TT_BLATHER(("(%.1f us/add) ",
804 		(float)tv_end.tv_sec/(float)i * 1000000.0 +
805 		tv_end.tv_usec / (float)i));
806 
807 	if (!EVTAG_HAS(msg2, from_name) ||
808 	    !EVTAG_HAS(msg2, to_name) ||
809 	    !EVTAG_HAS(msg2, attack)) {
810 		tt_abort_msg("Missing data structures.");
811 	}
812 
813 	if (EVTAG_GET(msg2, attack, &attack) == -1) {
814 		tt_abort_msg("Could not get attack.");
815 	}
816 
817 	if (EVTAG_ARRAY_LEN(msg2, run) != i) {
818 		tt_abort_msg("Wrong number of run messages.");
819 	}
820 
821 	/* get the very first run message */
822 	if (EVTAG_ARRAY_GET(msg2, run, 0, &run) == -1) {
823 		tt_abort_msg("Failed to get run msg.");
824 	} else {
825 		/* verify the notes */
826 		char *note_one, *note_two;
827 		ev_uint64_t large_number;
828 		ev_uint32_t short_number;
829 
830 		if (EVTAG_ARRAY_LEN(run, notes) != 2) {
831 			tt_abort_msg("Wrong number of note strings.");
832 		}
833 
834 		if (EVTAG_ARRAY_GET(run, notes, 0, &note_one) == -1 ||
835 		    EVTAG_ARRAY_GET(run, notes, 1, &note_two) == -1) {
836 			tt_abort_msg("Could not get note strings.");
837 		}
838 
839 		if (strcmp(note_one, "this is my note") ||
840 		    strcmp(note_two, "pps")) {
841 			tt_abort_msg("Incorrect note strings encoded.");
842 		}
843 
844 		if (EVTAG_GET(run, large_number, &large_number) == -1 ||
845 		    large_number != 0xdead0a0bcafebeefLL) {
846 			tt_abort_msg("Incorrrect large_number.");
847 		}
848 
849 		if (EVTAG_ARRAY_LEN(run, other_numbers) != 2) {
850 			tt_abort_msg("Wrong number of other_numbers.");
851 		}
852 
853 		if (EVTAG_ARRAY_GET(
854 			    run, other_numbers, 0, &short_number) == -1) {
855 			tt_abort_msg("Could not get short number.");
856 		}
857 		tt_uint_op(short_number, ==, 0xdead0a0b);
858 
859 	}
860 	tt_int_op(EVTAG_ARRAY_LEN(attack, how_often), ==, 3);
861 
862 	for (i = 0; i < 3; ++i) {
863 		ev_uint32_t res;
864 		if (EVTAG_ARRAY_GET(attack, how_often, i, &res) == -1) {
865 			TT_DIE(("Cannot get %dth how_often msg.", i));
866 		}
867 		if ((int)res != i) {
868 			TT_DIE(("Wrong message encoded %d != %d", i, res));
869 		}
870 	}
871 
872 	test_ok = 1;
873 end:
874 	if (msg)
875 		msg_free(msg);
876 	if (msg2)
877 		msg_free(msg2);
878 	if (tmp)
879 		evbuffer_free(tmp);
880 }
881 
882 static void
883 rpc_invalid_type(void)
884 {
885 	ev_uint16_t port;
886 	struct evhttp *http = NULL;
887 	struct evrpc_base *base = NULL;
888 	struct evhttp_connection *evcon = NULL;
889 	struct evhttp_request *req = NULL;
890 
891 	rpc_setup(&http, &port, &base);
892 
893 	evcon = evhttp_connection_new("127.0.0.1", port);
894 	tt_assert(evcon);
895 
896 	/*
897 	 * At this point, we want to schedule an HTTP POST request
898 	 * server using our make request method.
899 	 */
900 
901 	req = evhttp_request_new(rpc_postrequest_failure, NULL);
902 	tt_assert(req);
903 
904 	/* Add the information that we care about */
905 	evhttp_add_header(req->output_headers, "Host", "somehost");
906 	evbuffer_add_printf(req->output_buffer, "Some Nonsense");
907 
908 	if (evhttp_make_request(evcon, req,
909 		EVHTTP_REQ_GET,
910 		"/.rpc.Message") == -1) {
911 		tt_abort();
912 	}
913 
914 	test_ok = 0;
915 
916 	event_dispatch();
917 
918 	evhttp_connection_free(evcon);
919 
920 	rpc_teardown(base);
921 
922 	tt_assert(test_ok == 1);
923 
924 end:
925 	evhttp_free(http);
926 }
927 
928 
929 #define RPC_LEGACY(name)						\
930 	{ #name, run_legacy_test_fn, TT_FORK|TT_NEED_BASE|TT_LEGACY,	\
931 		    &legacy_setup,					\
932 		    rpc_##name }
933 #else
934 /* NO_PYTHON_EXISTS */
935 
936 #define RPC_LEGACY(name) \
937 	{ #name, NULL, TT_SKIP, NULL, NULL }
938 
939 #endif
940 
941 struct testcase_t rpc_testcases[] = {
942 	RPC_LEGACY(basic_test),
943 	RPC_LEGACY(basic_message),
944 	RPC_LEGACY(basic_client),
945 	RPC_LEGACY(basic_queued_client),
946 	RPC_LEGACY(basic_client_with_pause),
947 	RPC_LEGACY(invalid_type),
948 	RPC_LEGACY(client_timeout),
949 	RPC_LEGACY(test),
950 
951 	END_OF_TESTCASES,
952 };
953