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