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