regress_rpc.c revision 290001
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	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
512end:
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 */
531static void
532rpc_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
566end:
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
580static void
581GotErrorCb(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
593done:
594	event_loopexit(NULL);
595}
596
597/* we just pause the rpc and continue it in the next callback */
598
599struct rpc_hook_ctx_ {
600	void *vbase;
601	void *ctx;
602};
603
604static int hook_pause_cb_called=0;
605
606static void
607rpc_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
615static int
616rpc_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
631static void
632rpc_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
668end:
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
683static void
684rpc_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
722end:
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
734static void
735rpc_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;
874end:
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
895struct 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