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
76static struct evhttp *
77http_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
98EVRPC_HEADER(Message, msg, kill)
99EVRPC_HEADER(NeverReply, msg, kill)
100
101EVRPC_GENERATE(Message, msg, kill)
102EVRPC_GENERATE(NeverReply, msg, kill)
103
104static int need_input_hook = 0;
105static int need_output_hook = 0;
106
107static void
108MessageCb(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
128static EVRPC_STRUCT(NeverReply) *saved_rpc;
129
130static void
131NeverReplyCb(EVRPC_STRUCT(NeverReply)* rpc, void *arg)
132{
133	test_ok += 1;
134	saved_rpc = rpc;
135}
136
137static void
138rpc_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
158static void
159rpc_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
167static void
168rpc_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
184static void
185rpc_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
226end:
227	evhttp_free(http);
228}
229
230static void
231rpc_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
253static void
254rpc_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
304end:
305	evhttp_free(http);
306}
307
308static struct evrpc_pool *
309rpc_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
325static void
326GotKillCb(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
360done:
361	event_loopexit(NULL);
362}
363
364static void
365GotKillCbTwo(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
391done:
392	if (test_ok == 2)
393		event_loopexit(NULL);
394}
395
396static int
397rpc_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
411static int
412rpc_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
422static int
423rpc_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
445static void
446rpc_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
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	EVTAG_ASSIGN(msg, from_name, "niels");
473	EVTAG_ASSIGN(msg, to_name, "tester");
474
475	kill = kill_new();
476
477	EVRPC_MAKE_REQUEST(Message, pool, msg, kill,  GotKillCb, NULL);
478
479	test_ok = 0;
480
481	event_dispatch();
482
483	tt_assert(test_ok == 1);
484
485	/* we do it twice to make sure that reuse works correctly */
486	kill_clear(kill);
487
488	EVRPC_MAKE_REQUEST(Message, pool, msg, kill,  GotKillCb, NULL);
489
490	event_dispatch();
491
492	tt_assert(test_ok == 2);
493
494	/* we do it trice to make sure other stuff works, too */
495	kill_clear(kill);
496
497	{
498		struct evrpc_request_wrapper *ctx =
499		    EVRPC_MAKE_CTX(Message, msg, kill,
500			pool, msg, kill, GotKillCb, NULL);
501		evrpc_make_request(ctx);
502	}
503
504	event_dispatch();
505
506	rpc_teardown(base);
507
508	tt_assert(test_ok == 3);
509
510end:
511	if (msg)
512		msg_free(msg);
513	if (kill)
514		kill_free(kill);
515
516	if (pool)
517		evrpc_pool_free(pool);
518	if (http)
519		evhttp_free(http);
520
521	need_input_hook = 0;
522	need_output_hook = 0;
523}
524
525/*
526 * We are testing that the second requests gets send over the same
527 * connection after the first RPCs completes.
528 */
529static void
530rpc_basic_queued_client(void)
531{
532	ev_uint16_t port;
533	struct evhttp *http = NULL;
534	struct evrpc_base *base = NULL;
535	struct evrpc_pool *pool = NULL;
536	struct msg *msg=NULL;
537	struct kill *kill_one=NULL, *kill_two=NULL;
538
539	rpc_setup(&http, &port, &base);
540
541	pool = rpc_pool_with_connection(port);
542
543	/* set up the basic message */
544	msg = msg_new();
545	EVTAG_ASSIGN(msg, from_name, "niels");
546	EVTAG_ASSIGN(msg, to_name, "tester");
547
548	kill_one = kill_new();
549	kill_two = kill_new();
550
551	EVRPC_MAKE_REQUEST(Message, pool, msg, kill_one,  GotKillCbTwo, NULL);
552	EVRPC_MAKE_REQUEST(Message, pool, msg, kill_two,  GotKillCb, NULL);
553
554	test_ok = 0;
555
556	event_dispatch();
557
558	rpc_teardown(base);
559
560	tt_assert(test_ok == 2);
561
562end:
563	if (msg)
564		msg_free(msg);
565	if (kill_one)
566		kill_free(kill_one);
567	if (kill_two)
568		kill_free(kill_two);
569
570	if (pool)
571		evrpc_pool_free(pool);
572	if (http)
573		evhttp_free(http);
574}
575
576static void
577GotErrorCb(struct evrpc_status *status,
578    struct msg *msg, struct kill *kill, void *arg)
579{
580	if (status->error != EVRPC_STATUS_ERR_TIMEOUT)
581		goto done;
582
583	/* should never be complete but just to check */
584	if (kill_complete(kill) == 0)
585		goto done;
586
587	test_ok += 1;
588
589done:
590	event_loopexit(NULL);
591}
592
593/* we just pause the rpc and continue it in the next callback */
594
595struct _rpc_hook_ctx {
596	void *vbase;
597	void *ctx;
598};
599
600static int hook_pause_cb_called=0;
601
602static void
603rpc_hook_pause_cb(evutil_socket_t fd, short what, void *arg)
604{
605	struct _rpc_hook_ctx *ctx = arg;
606	++hook_pause_cb_called;
607	evrpc_resume_request(ctx->vbase, ctx->ctx, EVRPC_CONTINUE);
608	free(arg);
609}
610
611static int
612rpc_hook_pause(void *ctx, struct evhttp_request *req, struct evbuffer *evbuf,
613    void *arg)
614{
615	struct _rpc_hook_ctx *tmp = malloc(sizeof(*tmp));
616	struct timeval tv;
617
618	assert(tmp != NULL);
619	tmp->vbase = arg;
620	tmp->ctx = ctx;
621
622	memset(&tv, 0, sizeof(tv));
623	event_once(-1, EV_TIMEOUT, rpc_hook_pause_cb, tmp, &tv);
624	return EVRPC_PAUSE;
625}
626
627static void
628rpc_basic_client_with_pause(void)
629{
630	ev_uint16_t port;
631	struct evhttp *http = NULL;
632	struct evrpc_base *base = NULL;
633	struct evrpc_pool *pool = NULL;
634	struct msg *msg = NULL;
635	struct kill *kill= NULL;
636
637	rpc_setup(&http, &port, &base);
638
639	assert(evrpc_add_hook(base, EVRPC_INPUT, rpc_hook_pause, base));
640	assert(evrpc_add_hook(base, EVRPC_OUTPUT, rpc_hook_pause, base));
641
642	pool = rpc_pool_with_connection(port);
643
644	assert(evrpc_add_hook(pool, EVRPC_INPUT, rpc_hook_pause, pool));
645	assert(evrpc_add_hook(pool, EVRPC_OUTPUT, rpc_hook_pause, pool));
646
647	/* set up the basic message */
648	msg = msg_new();
649	EVTAG_ASSIGN(msg, from_name, "niels");
650	EVTAG_ASSIGN(msg, to_name, "tester");
651
652	kill = kill_new();
653
654	EVRPC_MAKE_REQUEST(Message, pool, msg, kill, GotKillCb, NULL);
655
656	test_ok = 0;
657
658	event_dispatch();
659
660	tt_int_op(test_ok, ==, 1);
661	tt_int_op(hook_pause_cb_called, ==, 4);
662
663end:
664	if (base)
665		rpc_teardown(base);
666
667	if (msg)
668		msg_free(msg);
669	if (kill)
670		kill_free(kill);
671
672	if (pool)
673		evrpc_pool_free(pool);
674	if (http)
675		evhttp_free(http);
676}
677
678static void
679rpc_client_timeout(void)
680{
681	ev_uint16_t port;
682	struct evhttp *http = NULL;
683	struct evrpc_base *base = NULL;
684	struct evrpc_pool *pool = NULL;
685	struct msg *msg = NULL;
686	struct kill *kill = NULL;
687
688	rpc_setup(&http, &port, &base);
689
690	pool = rpc_pool_with_connection(port);
691
692	/* set the timeout to 5 seconds */
693	evrpc_pool_set_timeout(pool, 5);
694
695	/* set up the basic message */
696	msg = msg_new();
697	EVTAG_ASSIGN(msg, from_name, "niels");
698	EVTAG_ASSIGN(msg, to_name, "tester");
699
700	kill = kill_new();
701
702	EVRPC_MAKE_REQUEST(NeverReply, pool, msg, kill, GotErrorCb, NULL);
703
704	test_ok = 0;
705
706	event_dispatch();
707
708	/* free the saved RPC structure up */
709	EVRPC_REQUEST_DONE(saved_rpc);
710
711	rpc_teardown(base);
712
713	tt_assert(test_ok == 2);
714
715end:
716	if (msg)
717		msg_free(msg);
718	if (kill)
719		kill_free(kill);
720
721	if (pool)
722		evrpc_pool_free(pool);
723	if (http)
724		evhttp_free(http);
725}
726
727static void
728rpc_test(void)
729{
730	struct msg *msg = NULL, *msg2 = NULL;
731	struct kill *attack = NULL;
732	struct run *run = NULL;
733	struct evbuffer *tmp = evbuffer_new();
734	struct timeval tv_start, tv_end;
735	ev_uint32_t tag;
736	int i;
737
738	msg = msg_new();
739
740	tt_assert(msg);
741
742	EVTAG_ASSIGN(msg, from_name, "niels");
743	EVTAG_ASSIGN(msg, to_name, "phoenix");
744
745	if (EVTAG_GET(msg, attack, &attack) == -1) {
746		tt_abort_msg("Failed to set kill message.");
747	}
748
749	EVTAG_ASSIGN(attack, weapon, "feather");
750	EVTAG_ASSIGN(attack, action, "tickle");
751	for (i = 0; i < 3; ++i) {
752		if (EVTAG_ARRAY_ADD_VALUE(attack, how_often, i) == NULL) {
753			tt_abort_msg("Failed to add how_often.");
754		}
755	}
756
757	evutil_gettimeofday(&tv_start, NULL);
758	for (i = 0; i < 1000; ++i) {
759		run = EVTAG_ARRAY_ADD(msg, run);
760		if (run == NULL) {
761			tt_abort_msg("Failed to add run message.");
762		}
763		EVTAG_ASSIGN(run, how, "very fast but with some data in it");
764		EVTAG_ASSIGN(run, fixed_bytes,
765		    (ev_uint8_t*)"012345678901234567890123");
766
767		if (EVTAG_ARRAY_ADD_VALUE(
768			    run, notes, "this is my note") == NULL) {
769			tt_abort_msg("Failed to add note.");
770		}
771		if (EVTAG_ARRAY_ADD_VALUE(run, notes, "pps") == NULL) {
772			tt_abort_msg("Failed to add note");
773		}
774
775		EVTAG_ASSIGN(run, large_number, 0xdead0a0bcafebeefLL);
776		EVTAG_ARRAY_ADD_VALUE(run, other_numbers, 0xdead0a0b);
777		EVTAG_ARRAY_ADD_VALUE(run, other_numbers, 0xbeefcafe);
778	}
779
780	if (msg_complete(msg) == -1)
781		tt_abort_msg("Failed to make complete message.");
782
783	evtag_marshal_msg(tmp, 0xdeaf, msg);
784
785	if (evtag_peek(tmp, &tag) == -1)
786		tt_abort_msg("Failed to peak tag.");
787
788	if (tag != 0xdeaf)
789		TT_DIE(("Got incorrect tag: %0x.", (unsigned)tag));
790
791	msg2 = msg_new();
792	if (evtag_unmarshal_msg(tmp, 0xdeaf, msg2) == -1)
793		tt_abort_msg("Failed to unmarshal message.");
794
795	evutil_gettimeofday(&tv_end, NULL);
796	evutil_timersub(&tv_end, &tv_start, &tv_end);
797	TT_BLATHER(("(%.1f us/add) ",
798		(float)tv_end.tv_sec/(float)i * 1000000.0 +
799		tv_end.tv_usec / (float)i));
800
801	if (!EVTAG_HAS(msg2, from_name) ||
802	    !EVTAG_HAS(msg2, to_name) ||
803	    !EVTAG_HAS(msg2, attack)) {
804		tt_abort_msg("Missing data structures.");
805	}
806
807	if (EVTAG_GET(msg2, attack, &attack) == -1) {
808		tt_abort_msg("Could not get attack.");
809	}
810
811	if (EVTAG_ARRAY_LEN(msg2, run) != i) {
812		tt_abort_msg("Wrong number of run messages.");
813	}
814
815	/* get the very first run message */
816	if (EVTAG_ARRAY_GET(msg2, run, 0, &run) == -1) {
817		tt_abort_msg("Failed to get run msg.");
818	} else {
819		/* verify the notes */
820		char *note_one, *note_two;
821		ev_uint64_t large_number;
822		ev_uint32_t short_number;
823
824		if (EVTAG_ARRAY_LEN(run, notes) != 2) {
825			tt_abort_msg("Wrong number of note strings.");
826		}
827
828		if (EVTAG_ARRAY_GET(run, notes, 0, &note_one) == -1 ||
829		    EVTAG_ARRAY_GET(run, notes, 1, &note_two) == -1) {
830			tt_abort_msg("Could not get note strings.");
831		}
832
833		if (strcmp(note_one, "this is my note") ||
834		    strcmp(note_two, "pps")) {
835			tt_abort_msg("Incorrect note strings encoded.");
836		}
837
838		if (EVTAG_GET(run, large_number, &large_number) == -1 ||
839		    large_number != 0xdead0a0bcafebeefLL) {
840			tt_abort_msg("Incorrrect large_number.");
841		}
842
843		if (EVTAG_ARRAY_LEN(run, other_numbers) != 2) {
844			tt_abort_msg("Wrong number of other_numbers.");
845		}
846
847		if (EVTAG_ARRAY_GET(
848			    run, other_numbers, 0, &short_number) == -1) {
849			tt_abort_msg("Could not get short number.");
850		}
851		tt_uint_op(short_number, ==, 0xdead0a0b);
852
853	}
854	tt_int_op(EVTAG_ARRAY_LEN(attack, how_often), ==, 3);
855
856	for (i = 0; i < 3; ++i) {
857		ev_uint32_t res;
858		if (EVTAG_ARRAY_GET(attack, how_often, i, &res) == -1) {
859			TT_DIE(("Cannot get %dth how_often msg.", i));
860		}
861		if ((int)res != i) {
862			TT_DIE(("Wrong message encoded %d != %d", i, res));
863		}
864	}
865
866	test_ok = 1;
867end:
868	if (msg)
869		msg_free(msg);
870	if (msg2)
871		msg_free(msg2);
872	if (tmp)
873		evbuffer_free(tmp);
874}
875
876#define RPC_LEGACY(name)						\
877	{ #name, run_legacy_test_fn, TT_FORK|TT_NEED_BASE|TT_LEGACY,	\
878		    &legacy_setup,					\
879		    rpc_##name }
880#else
881/* NO_PYTHON_EXISTS */
882
883#define RPC_LEGACY(name) \
884	{ #name, NULL, TT_SKIP, NULL, NULL }
885
886#endif
887
888struct testcase_t rpc_testcases[] = {
889	RPC_LEGACY(basic_test),
890	RPC_LEGACY(basic_message),
891	RPC_LEGACY(basic_client),
892	RPC_LEGACY(basic_queued_client),
893	RPC_LEGACY(basic_client_with_pause),
894	RPC_LEGACY(client_timeout),
895	RPC_LEGACY(test),
896
897	END_OF_TESTCASES,
898};
899