1275970Scy/*
2275970Scy * Copyright (c) 2003-2007 Niels Provos <provos@citi.umich.edu>
3275970Scy * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson
4275970Scy *
5275970Scy * Redistribution and use in source and binary forms, with or without
6275970Scy * modification, are permitted provided that the following conditions
7275970Scy * are met:
8275970Scy * 1. Redistributions of source code must retain the above copyright
9275970Scy *    notice, this list of conditions and the following disclaimer.
10275970Scy * 2. Redistributions in binary form must reproduce the above copyright
11275970Scy *    notice, this list of conditions and the following disclaimer in the
12275970Scy *    documentation and/or other materials provided with the distribution.
13275970Scy * 3. The name of the author may not be used to endorse or promote products
14275970Scy *    derived from this software without specific prior written permission.
15275970Scy *
16275970Scy * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17275970Scy * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18275970Scy * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19275970Scy * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20275970Scy * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21275970Scy * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22275970Scy * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23275970Scy * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24275970Scy * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25275970Scy * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26275970Scy */
27275970Scy
28275970Scy/* The old tests here need assertions to work. */
29275970Scy#undef NDEBUG
30275970Scy
31275970Scy#ifdef _WIN32
32275970Scy#include <winsock2.h>
33275970Scy#include <windows.h>
34275970Scy#endif
35275970Scy
36275970Scy#include "event2/event-config.h"
37275970Scy
38275970Scy#include <sys/types.h>
39275970Scy#include <sys/stat.h>
40275970Scy#ifdef EVENT__HAVE_SYS_TIME_H
41275970Scy#include <sys/time.h>
42275970Scy#endif
43275970Scy#include <sys/queue.h>
44275970Scy#ifndef _WIN32
45275970Scy#include <sys/socket.h>
46275970Scy#include <signal.h>
47275970Scy#include <unistd.h>
48275970Scy#include <netdb.h>
49275970Scy#endif
50275970Scy#include <fcntl.h>
51275970Scy#include <stdlib.h>
52275970Scy#include <stdio.h>
53275970Scy#include <string.h>
54275970Scy#include <errno.h>
55275970Scy#include <assert.h>
56275970Scy
57275970Scy#include "event2/buffer.h"
58275970Scy#include "event2/event.h"
59275970Scy#include "event2/event_compat.h"
60275970Scy#include "event2/http.h"
61275970Scy#include "event2/http_compat.h"
62275970Scy#include "event2/http_struct.h"
63275970Scy#include "event2/rpc.h"
64275970Scy#include "event2/rpc.h"
65275970Scy#include "event2/rpc_struct.h"
66275970Scy#include "event2/tag.h"
67275970Scy#include "log-internal.h"
68275970Scy
69275970Scy#include "regress.gen.h"
70275970Scy
71275970Scy#include "regress.h"
72275970Scy#include "regress_testutils.h"
73275970Scy
74275970Scy#ifndef NO_PYTHON_EXISTS
75275970Scy
76275970Scystatic struct evhttp *
77275970Scyhttp_setup(ev_uint16_t *pport)
78275970Scy{
79275970Scy	struct evhttp *myhttp;
80275970Scy	ev_uint16_t port;
81275970Scy	struct evhttp_bound_socket *sock;
82275970Scy
83275970Scy	myhttp = evhttp_new(NULL);
84275970Scy	if (!myhttp)
85275970Scy		event_errx(1, "Could not start web server");
86275970Scy
87275970Scy	/* Try a few different ports */
88275970Scy	sock = evhttp_bind_socket_with_handle(myhttp, "127.0.0.1", 0);
89275970Scy	if (!sock)
90275970Scy		event_errx(1, "Couldn't open web port");
91275970Scy
92275970Scy	port = regress_get_socket_port(evhttp_bound_socket_get_fd(sock));
93275970Scy
94275970Scy	*pport = port;
95275970Scy	return (myhttp);
96275970Scy}
97275970Scy
98275970ScyEVRPC_HEADER(Message, msg, kill)
99275970ScyEVRPC_HEADER(NeverReply, msg, kill)
100275970Scy
101275970ScyEVRPC_GENERATE(Message, msg, kill)
102275970ScyEVRPC_GENERATE(NeverReply, msg, kill)
103275970Scy
104275970Scystatic int need_input_hook = 0;
105275970Scystatic int need_output_hook = 0;
106275970Scy
107275970Scystatic void
108275970ScyMessageCb(EVRPC_STRUCT(Message)* rpc, void *arg)
109275970Scy{
110275970Scy	struct kill* kill_reply = rpc->reply;
111275970Scy
112275970Scy	if (need_input_hook) {
113275970Scy		struct evhttp_request* req = EVRPC_REQUEST_HTTP(rpc);
114275970Scy		const char *header = evhttp_find_header(
115275970Scy			req->input_headers, "X-Hook");
116275970Scy		assert(header);
117275970Scy		assert(strcmp(header, "input") == 0);
118275970Scy	}
119275970Scy
120275970Scy	/* we just want to fill in some non-sense */
121275970Scy	EVTAG_ASSIGN(kill_reply, weapon, "dagger");
122275970Scy	EVTAG_ASSIGN(kill_reply, action, "wave around like an idiot");
123275970Scy
124275970Scy	/* no reply to the RPC */
125275970Scy	EVRPC_REQUEST_DONE(rpc);
126275970Scy}
127275970Scy
128275970Scystatic EVRPC_STRUCT(NeverReply) *saved_rpc;
129275970Scy
130275970Scystatic void
131275970ScyNeverReplyCb(EVRPC_STRUCT(NeverReply)* rpc, void *arg)
132275970Scy{
133275970Scy	test_ok += 1;
134275970Scy	saved_rpc = rpc;
135275970Scy}
136275970Scy
137275970Scystatic void
138275970Scyrpc_setup(struct evhttp **phttp, ev_uint16_t *pport, struct evrpc_base **pbase)
139275970Scy{
140275970Scy	ev_uint16_t port;
141275970Scy	struct evhttp *http = NULL;
142275970Scy	struct evrpc_base *base = NULL;
143275970Scy
144275970Scy	http = http_setup(&port);
145275970Scy	base = evrpc_init(http);
146275970Scy
147275970Scy	EVRPC_REGISTER(base, Message, msg, kill, MessageCb, NULL);
148275970Scy	EVRPC_REGISTER(base, NeverReply, msg, kill, NeverReplyCb, NULL);
149275970Scy
150275970Scy	*phttp = http;
151275970Scy	*pport = port;
152275970Scy	*pbase = base;
153275970Scy
154275970Scy	need_input_hook = 0;
155275970Scy	need_output_hook = 0;
156275970Scy}
157275970Scy
158275970Scystatic void
159275970Scyrpc_teardown(struct evrpc_base *base)
160275970Scy{
161275970Scy	assert(EVRPC_UNREGISTER(base, Message) == 0);
162275970Scy	assert(EVRPC_UNREGISTER(base, NeverReply) == 0);
163275970Scy
164275970Scy	evrpc_free(base);
165275970Scy}
166275970Scy
167275970Scystatic void
168275970Scyrpc_postrequest_failure(struct evhttp_request *req, void *arg)
169275970Scy{
170275970Scy	if (req->response_code != HTTP_SERVUNAVAIL) {
171275970Scy
172275970Scy		fprintf(stderr, "FAILED (response code)\n");
173275970Scy		exit(1);
174275970Scy	}
175275970Scy
176275970Scy	test_ok = 1;
177275970Scy	event_loopexit(NULL);
178275970Scy}
179275970Scy
180275970Scy/*
181275970Scy * Test a malformed payload submitted as an RPC
182275970Scy */
183275970Scy
184275970Scystatic void
185275970Scyrpc_basic_test(void)
186275970Scy{
187275970Scy	ev_uint16_t port;
188275970Scy	struct evhttp *http = NULL;
189275970Scy	struct evrpc_base *base = NULL;
190275970Scy	struct evhttp_connection *evcon = NULL;
191275970Scy	struct evhttp_request *req = NULL;
192275970Scy
193275970Scy	rpc_setup(&http, &port, &base);
194275970Scy
195275970Scy	evcon = evhttp_connection_new("127.0.0.1", port);
196275970Scy	tt_assert(evcon);
197275970Scy
198275970Scy	/*
199275970Scy	 * At this point, we want to schedule an HTTP POST request
200275970Scy	 * server using our make request method.
201275970Scy	 */
202275970Scy
203275970Scy	req = evhttp_request_new(rpc_postrequest_failure, NULL);
204275970Scy	tt_assert(req);
205275970Scy
206275970Scy	/* Add the information that we care about */
207275970Scy	evhttp_add_header(req->output_headers, "Host", "somehost");
208275970Scy	evbuffer_add_printf(req->output_buffer, "Some Nonsense");
209275970Scy
210275970Scy	if (evhttp_make_request(evcon, req,
211275970Scy		EVHTTP_REQ_POST,
212275970Scy		"/.rpc.Message") == -1) {
213275970Scy		tt_abort();
214275970Scy	}
215275970Scy
216275970Scy	test_ok = 0;
217275970Scy
218275970Scy	event_dispatch();
219275970Scy
220275970Scy	evhttp_connection_free(evcon);
221275970Scy
222275970Scy	rpc_teardown(base);
223275970Scy
224275970Scy	tt_assert(test_ok == 1);
225275970Scy
226275970Scyend:
227275970Scy	evhttp_free(http);
228275970Scy}
229275970Scy
230275970Scystatic void
231275970Scyrpc_postrequest_done(struct evhttp_request *req, void *arg)
232275970Scy{
233275970Scy	struct kill* kill_reply = NULL;
234275970Scy
235275970Scy	if (req->response_code != HTTP_OK) {
236275970Scy		fprintf(stderr, "FAILED (response code)\n");
237275970Scy		exit(1);
238275970Scy	}
239275970Scy
240275970Scy	kill_reply = kill_new();
241275970Scy
242275970Scy	if ((kill_unmarshal(kill_reply, req->input_buffer)) == -1) {
243275970Scy		fprintf(stderr, "FAILED (unmarshal)\n");
244275970Scy		exit(1);
245275970Scy	}
246275970Scy
247275970Scy	kill_free(kill_reply);
248275970Scy
249275970Scy	test_ok = 1;
250275970Scy	event_loopexit(NULL);
251275970Scy}
252275970Scy
253275970Scystatic void
254275970Scyrpc_basic_message(void)
255275970Scy{
256275970Scy	ev_uint16_t port;
257275970Scy	struct evhttp *http = NULL;
258275970Scy	struct evrpc_base *base = NULL;
259275970Scy	struct evhttp_connection *evcon = NULL;
260275970Scy	struct evhttp_request *req = NULL;
261275970Scy	struct msg *msg;
262275970Scy
263275970Scy	rpc_setup(&http, &port, &base);
264275970Scy
265275970Scy	evcon = evhttp_connection_new("127.0.0.1", port);
266275970Scy	tt_assert(evcon);
267275970Scy
268275970Scy	/*
269275970Scy	 * At this point, we want to schedule an HTTP POST request
270275970Scy	 * server using our make request method.
271275970Scy	 */
272275970Scy
273275970Scy	req = evhttp_request_new(rpc_postrequest_done, NULL);
274275970Scy	if (req == NULL) {
275275970Scy		fprintf(stdout, "FAILED\n");
276275970Scy		exit(1);
277275970Scy	}
278275970Scy
279275970Scy	/* Add the information that we care about */
280275970Scy	evhttp_add_header(req->output_headers, "Host", "somehost");
281275970Scy
282275970Scy	/* set up the basic message */
283275970Scy	msg = msg_new();
284275970Scy	EVTAG_ASSIGN(msg, from_name, "niels");
285275970Scy	EVTAG_ASSIGN(msg, to_name, "tester");
286275970Scy	msg_marshal(req->output_buffer, msg);
287275970Scy	msg_free(msg);
288275970Scy
289275970Scy	if (evhttp_make_request(evcon, req,
290275970Scy		EVHTTP_REQ_POST,
291275970Scy		"/.rpc.Message") == -1) {
292275970Scy		fprintf(stdout, "FAILED\n");
293275970Scy		exit(1);
294275970Scy	}
295275970Scy
296275970Scy	test_ok = 0;
297275970Scy
298275970Scy	event_dispatch();
299275970Scy
300275970Scy	evhttp_connection_free(evcon);
301275970Scy
302275970Scy	rpc_teardown(base);
303275970Scy
304275970Scyend:
305275970Scy	evhttp_free(http);
306275970Scy}
307275970Scy
308275970Scystatic struct evrpc_pool *
309275970Scyrpc_pool_with_connection(ev_uint16_t port)
310275970Scy{
311275970Scy	struct evhttp_connection *evcon;
312275970Scy	struct evrpc_pool *pool;
313275970Scy
314275970Scy	pool = evrpc_pool_new(NULL);
315275970Scy	assert(pool != NULL);
316275970Scy
317275970Scy	evcon = evhttp_connection_new("127.0.0.1", port);
318275970Scy	assert(evcon != NULL);
319275970Scy
320275970Scy	evrpc_pool_add_connection(pool, evcon);
321275970Scy
322275970Scy	return (pool);
323275970Scy}
324275970Scy
325275970Scystatic void
326275970ScyGotKillCb(struct evrpc_status *status,
327275970Scy    struct msg *msg, struct kill *kill, void *arg)
328275970Scy{
329275970Scy	char *weapon;
330275970Scy	char *action;
331275970Scy
332275970Scy	if (need_output_hook) {
333275970Scy		struct evhttp_request *req = status->http_req;
334275970Scy		const char *header = evhttp_find_header(
335275970Scy			req->input_headers, "X-Pool-Hook");
336275970Scy		assert(header);
337275970Scy		assert(strcmp(header, "ran") == 0);
338275970Scy	}
339275970Scy
340275970Scy	if (status->error != EVRPC_STATUS_ERR_NONE)
341275970Scy		goto done;
342275970Scy
343275970Scy	if (EVTAG_GET(kill, weapon, &weapon) == -1) {
344275970Scy		fprintf(stderr, "get weapon\n");
345275970Scy		goto done;
346275970Scy	}
347275970Scy	if (EVTAG_GET(kill, action, &action) == -1) {
348275970Scy		fprintf(stderr, "get action\n");
349275970Scy		goto done;
350275970Scy	}
351275970Scy
352275970Scy	if (strcmp(weapon, "dagger"))
353275970Scy		goto done;
354275970Scy
355275970Scy	if (strcmp(action, "wave around like an idiot"))
356275970Scy		goto done;
357275970Scy
358275970Scy	test_ok += 1;
359275970Scy
360275970Scydone:
361275970Scy	event_loopexit(NULL);
362275970Scy}
363275970Scy
364275970Scystatic void
365275970ScyGotKillCbTwo(struct evrpc_status *status,
366275970Scy    struct msg *msg, struct kill *kill, void *arg)
367275970Scy{
368275970Scy	char *weapon;
369275970Scy	char *action;
370275970Scy
371275970Scy	if (status->error != EVRPC_STATUS_ERR_NONE)
372275970Scy		goto done;
373275970Scy
374275970Scy	if (EVTAG_GET(kill, weapon, &weapon) == -1) {
375275970Scy		fprintf(stderr, "get weapon\n");
376275970Scy		goto done;
377275970Scy	}
378275970Scy	if (EVTAG_GET(kill, action, &action) == -1) {
379275970Scy		fprintf(stderr, "get action\n");
380275970Scy		goto done;
381275970Scy	}
382275970Scy
383275970Scy	if (strcmp(weapon, "dagger"))
384275970Scy		goto done;
385275970Scy
386275970Scy	if (strcmp(action, "wave around like an idiot"))
387275970Scy		goto done;
388275970Scy
389275970Scy	test_ok += 1;
390275970Scy
391275970Scydone:
392275970Scy	if (test_ok == 2)
393275970Scy		event_loopexit(NULL);
394275970Scy}
395275970Scy
396275970Scystatic int
397275970Scyrpc_hook_add_header(void *ctx, struct evhttp_request *req,
398275970Scy    struct evbuffer *evbuf, void *arg)
399275970Scy{
400275970Scy	const char *hook_type = arg;
401275970Scy	if (strcmp("input", hook_type) == 0)
402275970Scy		evhttp_add_header(req->input_headers, "X-Hook", hook_type);
403275970Scy	else
404275970Scy		evhttp_add_header(req->output_headers, "X-Hook", hook_type);
405275970Scy
406275970Scy	assert(evrpc_hook_get_connection(ctx) != NULL);
407275970Scy
408275970Scy	return (EVRPC_CONTINUE);
409275970Scy}
410275970Scy
411275970Scystatic int
412275970Scyrpc_hook_add_meta(void *ctx, struct evhttp_request *req,
413275970Scy    struct evbuffer *evbuf, void *arg)
414275970Scy{
415275970Scy	evrpc_hook_add_meta(ctx, "meta", "test", 5);
416275970Scy
417275970Scy	assert(evrpc_hook_get_connection(ctx) != NULL);
418275970Scy
419275970Scy	return (EVRPC_CONTINUE);
420275970Scy}
421275970Scy
422275970Scystatic int
423275970Scyrpc_hook_remove_header(void *ctx, struct evhttp_request *req,
424275970Scy    struct evbuffer *evbuf, void *arg)
425275970Scy{
426275970Scy	const char *header = evhttp_find_header(req->input_headers, "X-Hook");
427275970Scy	void *data = NULL;
428275970Scy	size_t data_len = 0;
429275970Scy
430275970Scy	assert(header != NULL);
431275970Scy	assert(strcmp(header, arg) == 0);
432275970Scy
433275970Scy	evhttp_remove_header(req->input_headers, "X-Hook");
434275970Scy	evhttp_add_header(req->input_headers, "X-Pool-Hook", "ran");
435275970Scy
436275970Scy	assert(evrpc_hook_find_meta(ctx, "meta", &data, &data_len) == 0);
437275970Scy	assert(data != NULL);
438275970Scy	assert(data_len == 5);
439275970Scy
440275970Scy	assert(evrpc_hook_get_connection(ctx) != NULL);
441275970Scy
442275970Scy	return (EVRPC_CONTINUE);
443275970Scy}
444275970Scy
445275970Scystatic void
446275970Scyrpc_basic_client(void)
447275970Scy{
448275970Scy	ev_uint16_t port;
449275970Scy	struct evhttp *http = NULL;
450275970Scy	struct evrpc_base *base = NULL;
451275970Scy	struct evrpc_pool *pool = NULL;
452275970Scy	struct msg *msg = NULL;
453275970Scy	struct kill *kill = NULL;
454275970Scy
455275970Scy	rpc_setup(&http, &port, &base);
456275970Scy
457275970Scy	need_input_hook = 1;
458275970Scy	need_output_hook = 1;
459275970Scy
460275970Scy	assert(evrpc_add_hook(base, EVRPC_INPUT, rpc_hook_add_header, (void*)"input")
461275970Scy	    != NULL);
462275970Scy	assert(evrpc_add_hook(base, EVRPC_OUTPUT, rpc_hook_add_header, (void*)"output")
463275970Scy	    != NULL);
464275970Scy
465275970Scy	pool = rpc_pool_with_connection(port);
466275970Scy	tt_assert(pool);
467275970Scy
468275970Scy	assert(evrpc_add_hook(pool, EVRPC_OUTPUT, rpc_hook_add_meta, NULL));
469275970Scy	assert(evrpc_add_hook(pool, EVRPC_INPUT, rpc_hook_remove_header, (void*)"output"));
470275970Scy
471275970Scy	/* set up the basic message */
472275970Scy	msg = msg_new();
473275970Scy	tt_assert(msg);
474275970Scy	EVTAG_ASSIGN(msg, from_name, "niels");
475275970Scy	EVTAG_ASSIGN(msg, to_name, "tester");
476275970Scy
477275970Scy	kill = kill_new();
478275970Scy
479275970Scy	EVRPC_MAKE_REQUEST(Message, pool, msg, kill,  GotKillCb, NULL);
480275970Scy
481275970Scy	test_ok = 0;
482275970Scy
483275970Scy	event_dispatch();
484275970Scy
485275970Scy	tt_assert(test_ok == 1);
486275970Scy
487275970Scy	/* we do it twice to make sure that reuse works correctly */
488275970Scy	kill_clear(kill);
489275970Scy
490275970Scy	EVRPC_MAKE_REQUEST(Message, pool, msg, kill,  GotKillCb, NULL);
491275970Scy
492275970Scy	event_dispatch();
493275970Scy
494275970Scy	tt_assert(test_ok == 2);
495275970Scy
496275970Scy	/* we do it trice to make sure other stuff works, too */
497275970Scy	kill_clear(kill);
498275970Scy
499275970Scy	{
500275970Scy		struct evrpc_request_wrapper *ctx =
501275970Scy		    EVRPC_MAKE_CTX(Message, msg, kill,
502275970Scy			pool, msg, kill, GotKillCb, NULL);
503275970Scy		evrpc_make_request(ctx);
504275970Scy	}
505275970Scy
506275970Scy	event_dispatch();
507275970Scy
508275970Scy	rpc_teardown(base);
509275970Scy
510275970Scy	tt_assert(test_ok == 3);
511275970Scy
512275970Scyend:
513275970Scy	if (msg)
514275970Scy		msg_free(msg);
515275970Scy	if (kill)
516275970Scy		kill_free(kill);
517275970Scy
518275970Scy	if (pool)
519275970Scy		evrpc_pool_free(pool);
520275970Scy	if (http)
521275970Scy		evhttp_free(http);
522275970Scy
523275970Scy	need_input_hook = 0;
524275970Scy	need_output_hook = 0;
525275970Scy}
526275970Scy
527275970Scy/*
528275970Scy * We are testing that the second requests gets send over the same
529275970Scy * connection after the first RPCs completes.
530275970Scy */
531275970Scystatic void
532275970Scyrpc_basic_queued_client(void)
533275970Scy{
534275970Scy	ev_uint16_t port;
535275970Scy	struct evhttp *http = NULL;
536275970Scy	struct evrpc_base *base = NULL;
537275970Scy	struct evrpc_pool *pool = NULL;
538275970Scy	struct msg *msg=NULL;
539275970Scy	struct kill *kill_one=NULL, *kill_two=NULL;
540275970Scy
541275970Scy	rpc_setup(&http, &port, &base);
542275970Scy
543275970Scy	pool = rpc_pool_with_connection(port);
544275970Scy	tt_assert(pool);
545275970Scy
546275970Scy	/* set up the basic message */
547275970Scy	msg = msg_new();
548275970Scy	tt_assert(msg);
549275970Scy	EVTAG_ASSIGN(msg, from_name, "niels");
550275970Scy	EVTAG_ASSIGN(msg, to_name, "tester");
551275970Scy
552275970Scy	kill_one = kill_new();
553275970Scy	kill_two = kill_new();
554275970Scy
555275970Scy	EVRPC_MAKE_REQUEST(Message, pool, msg, kill_one,  GotKillCbTwo, NULL);
556275970Scy	EVRPC_MAKE_REQUEST(Message, pool, msg, kill_two,  GotKillCb, NULL);
557275970Scy
558275970Scy	test_ok = 0;
559275970Scy
560275970Scy	event_dispatch();
561275970Scy
562275970Scy	rpc_teardown(base);
563275970Scy
564275970Scy	tt_assert(test_ok == 2);
565275970Scy
566275970Scyend:
567275970Scy	if (msg)
568275970Scy		msg_free(msg);
569275970Scy	if (kill_one)
570275970Scy		kill_free(kill_one);
571275970Scy	if (kill_two)
572275970Scy		kill_free(kill_two);
573275970Scy
574275970Scy	if (pool)
575275970Scy		evrpc_pool_free(pool);
576275970Scy	if (http)
577275970Scy		evhttp_free(http);
578275970Scy}
579275970Scy
580275970Scystatic void
581275970ScyGotErrorCb(struct evrpc_status *status,
582275970Scy    struct msg *msg, struct kill *kill, void *arg)
583275970Scy{
584275970Scy	if (status->error != EVRPC_STATUS_ERR_TIMEOUT)
585275970Scy		goto done;
586275970Scy
587275970Scy	/* should never be complete but just to check */
588275970Scy	if (kill_complete(kill) == 0)
589275970Scy		goto done;
590275970Scy
591275970Scy	test_ok += 1;
592275970Scy
593275970Scydone:
594275970Scy	event_loopexit(NULL);
595275970Scy}
596275970Scy
597275970Scy/* we just pause the rpc and continue it in the next callback */
598275970Scy
599275970Scystruct rpc_hook_ctx_ {
600275970Scy	void *vbase;
601275970Scy	void *ctx;
602275970Scy};
603275970Scy
604275970Scystatic int hook_pause_cb_called=0;
605275970Scy
606275970Scystatic void
607275970Scyrpc_hook_pause_cb(evutil_socket_t fd, short what, void *arg)
608275970Scy{
609275970Scy	struct rpc_hook_ctx_ *ctx = arg;
610275970Scy	++hook_pause_cb_called;
611275970Scy	evrpc_resume_request(ctx->vbase, ctx->ctx, EVRPC_CONTINUE);
612275970Scy	free(arg);
613275970Scy}
614275970Scy
615275970Scystatic int
616275970Scyrpc_hook_pause(void *ctx, struct evhttp_request *req, struct evbuffer *evbuf,
617275970Scy    void *arg)
618275970Scy{
619275970Scy	struct rpc_hook_ctx_ *tmp = malloc(sizeof(*tmp));
620275970Scy	struct timeval tv;
621275970Scy
622275970Scy	assert(tmp != NULL);
623275970Scy	tmp->vbase = arg;
624275970Scy	tmp->ctx = ctx;
625275970Scy
626275970Scy	memset(&tv, 0, sizeof(tv));
627275970Scy	event_once(-1, EV_TIMEOUT, rpc_hook_pause_cb, tmp, &tv);
628275970Scy	return EVRPC_PAUSE;
629275970Scy}
630275970Scy
631275970Scystatic void
632275970Scyrpc_basic_client_with_pause(void)
633275970Scy{
634275970Scy	ev_uint16_t port;
635275970Scy	struct evhttp *http = NULL;
636275970Scy	struct evrpc_base *base = NULL;
637275970Scy	struct evrpc_pool *pool = NULL;
638275970Scy	struct msg *msg = NULL;
639275970Scy	struct kill *kill= NULL;
640275970Scy
641275970Scy	rpc_setup(&http, &port, &base);
642275970Scy
643275970Scy	assert(evrpc_add_hook(base, EVRPC_INPUT, rpc_hook_pause, base));
644275970Scy	assert(evrpc_add_hook(base, EVRPC_OUTPUT, rpc_hook_pause, base));
645275970Scy
646275970Scy	pool = rpc_pool_with_connection(port);
647275970Scy	tt_assert(pool);
648275970Scy	assert(evrpc_add_hook(pool, EVRPC_INPUT, rpc_hook_pause, pool));
649275970Scy	assert(evrpc_add_hook(pool, EVRPC_OUTPUT, rpc_hook_pause, pool));
650275970Scy
651275970Scy	/* set up the basic message */
652275970Scy	msg = msg_new();
653275970Scy	tt_assert(msg);
654275970Scy	EVTAG_ASSIGN(msg, from_name, "niels");
655275970Scy	EVTAG_ASSIGN(msg, to_name, "tester");
656275970Scy
657275970Scy	kill = kill_new();
658275970Scy
659275970Scy	EVRPC_MAKE_REQUEST(Message, pool, msg, kill, GotKillCb, NULL);
660275970Scy
661275970Scy	test_ok = 0;
662275970Scy
663275970Scy	event_dispatch();
664275970Scy
665275970Scy	tt_int_op(test_ok, ==, 1);
666275970Scy	tt_int_op(hook_pause_cb_called, ==, 4);
667275970Scy
668275970Scyend:
669275970Scy	if (base)
670275970Scy		rpc_teardown(base);
671275970Scy
672275970Scy	if (msg)
673275970Scy		msg_free(msg);
674275970Scy	if (kill)
675275970Scy		kill_free(kill);
676275970Scy
677275970Scy	if (pool)
678275970Scy		evrpc_pool_free(pool);
679275970Scy	if (http)
680275970Scy		evhttp_free(http);
681275970Scy}
682275970Scy
683275970Scystatic void
684275970Scyrpc_client_timeout(void)
685275970Scy{
686275970Scy	ev_uint16_t port;
687275970Scy	struct evhttp *http = NULL;
688275970Scy	struct evrpc_base *base = NULL;
689275970Scy	struct evrpc_pool *pool = NULL;
690275970Scy	struct msg *msg = NULL;
691275970Scy	struct kill *kill = NULL;
692275970Scy
693275970Scy	rpc_setup(&http, &port, &base);
694275970Scy
695275970Scy	pool = rpc_pool_with_connection(port);
696275970Scy	tt_assert(pool);
697275970Scy
698275970Scy	/* set the timeout to 1 second. */
699275970Scy	evrpc_pool_set_timeout(pool, 1);
700275970Scy
701275970Scy	/* set up the basic message */
702275970Scy	msg = msg_new();
703275970Scy	tt_assert(msg);
704275970Scy	EVTAG_ASSIGN(msg, from_name, "niels");
705275970Scy	EVTAG_ASSIGN(msg, to_name, "tester");
706275970Scy
707275970Scy	kill = kill_new();
708275970Scy
709275970Scy	EVRPC_MAKE_REQUEST(NeverReply, pool, msg, kill, GotErrorCb, NULL);
710275970Scy
711275970Scy	test_ok = 0;
712275970Scy
713275970Scy	event_dispatch();
714275970Scy
715275970Scy	/* free the saved RPC structure up */
716275970Scy	EVRPC_REQUEST_DONE(saved_rpc);
717275970Scy
718275970Scy	rpc_teardown(base);
719275970Scy
720275970Scy	tt_assert(test_ok == 2);
721275970Scy
722275970Scyend:
723275970Scy	if (msg)
724275970Scy		msg_free(msg);
725275970Scy	if (kill)
726275970Scy		kill_free(kill);
727275970Scy
728275970Scy	if (pool)
729275970Scy		evrpc_pool_free(pool);
730275970Scy	if (http)
731275970Scy		evhttp_free(http);
732275970Scy}
733275970Scy
734275970Scystatic void
735275970Scyrpc_test(void)
736275970Scy{
737275970Scy	struct msg *msg = NULL, *msg2 = NULL;
738275970Scy	struct kill *attack = NULL;
739275970Scy	struct run *run = NULL;
740275970Scy	struct evbuffer *tmp = evbuffer_new();
741275970Scy	struct timeval tv_start, tv_end;
742275970Scy	ev_uint32_t tag;
743275970Scy	int i;
744275970Scy
745275970Scy	msg = msg_new();
746275970Scy
747275970Scy	tt_assert(msg);
748275970Scy
749275970Scy	EVTAG_ASSIGN(msg, from_name, "niels");
750275970Scy	EVTAG_ASSIGN(msg, to_name, "phoenix");
751275970Scy
752275970Scy	if (EVTAG_GET(msg, attack, &attack) == -1) {
753275970Scy		tt_abort_msg("Failed to set kill message.");
754275970Scy	}
755275970Scy
756275970Scy	EVTAG_ASSIGN(attack, weapon, "feather");
757275970Scy	EVTAG_ASSIGN(attack, action, "tickle");
758275970Scy	for (i = 0; i < 3; ++i) {
759275970Scy		if (EVTAG_ARRAY_ADD_VALUE(attack, how_often, i) == NULL) {
760275970Scy			tt_abort_msg("Failed to add how_often.");
761275970Scy		}
762275970Scy	}
763275970Scy
764275970Scy	evutil_gettimeofday(&tv_start, NULL);
765275970Scy	for (i = 0; i < 1000; ++i) {
766275970Scy		run = EVTAG_ARRAY_ADD(msg, run);
767275970Scy		if (run == NULL) {
768275970Scy			tt_abort_msg("Failed to add run message.");
769275970Scy		}
770275970Scy		EVTAG_ASSIGN(run, how, "very fast but with some data in it");
771275970Scy		EVTAG_ASSIGN(run, fixed_bytes,
772275970Scy		    (ev_uint8_t*)"012345678901234567890123");
773275970Scy
774275970Scy		if (EVTAG_ARRAY_ADD_VALUE(
775275970Scy			    run, notes, "this is my note") == NULL) {
776275970Scy			tt_abort_msg("Failed to add note.");
777275970Scy		}
778275970Scy		if (EVTAG_ARRAY_ADD_VALUE(run, notes, "pps") == NULL) {
779275970Scy			tt_abort_msg("Failed to add note");
780275970Scy		}
781275970Scy
782275970Scy		EVTAG_ASSIGN(run, large_number, 0xdead0a0bcafebeefLL);
783275970Scy		EVTAG_ARRAY_ADD_VALUE(run, other_numbers, 0xdead0a0b);
784275970Scy		EVTAG_ARRAY_ADD_VALUE(run, other_numbers, 0xbeefcafe);
785275970Scy	}
786275970Scy
787275970Scy	if (msg_complete(msg) == -1)
788275970Scy		tt_abort_msg("Failed to make complete message.");
789275970Scy
790275970Scy	evtag_marshal_msg(tmp, 0xdeaf, msg);
791275970Scy
792275970Scy	if (evtag_peek(tmp, &tag) == -1)
793275970Scy		tt_abort_msg("Failed to peak tag.");
794275970Scy
795275970Scy	if (tag != 0xdeaf)
796275970Scy		TT_DIE(("Got incorrect tag: %0x.", (unsigned)tag));
797275970Scy
798275970Scy	msg2 = msg_new();
799275970Scy	if (evtag_unmarshal_msg(tmp, 0xdeaf, msg2) == -1)
800275970Scy		tt_abort_msg("Failed to unmarshal message.");
801275970Scy
802275970Scy	evutil_gettimeofday(&tv_end, NULL);
803275970Scy	evutil_timersub(&tv_end, &tv_start, &tv_end);
804275970Scy	TT_BLATHER(("(%.1f us/add) ",
805275970Scy		(float)tv_end.tv_sec/(float)i * 1000000.0 +
806275970Scy		tv_end.tv_usec / (float)i));
807275970Scy
808275970Scy	if (!EVTAG_HAS(msg2, from_name) ||
809275970Scy	    !EVTAG_HAS(msg2, to_name) ||
810275970Scy	    !EVTAG_HAS(msg2, attack)) {
811275970Scy		tt_abort_msg("Missing data structures.");
812275970Scy	}
813275970Scy
814275970Scy	if (EVTAG_GET(msg2, attack, &attack) == -1) {
815275970Scy		tt_abort_msg("Could not get attack.");
816275970Scy	}
817275970Scy
818275970Scy	if (EVTAG_ARRAY_LEN(msg2, run) != i) {
819275970Scy		tt_abort_msg("Wrong number of run messages.");
820275970Scy	}
821275970Scy
822275970Scy	/* get the very first run message */
823275970Scy	if (EVTAG_ARRAY_GET(msg2, run, 0, &run) == -1) {
824275970Scy		tt_abort_msg("Failed to get run msg.");
825275970Scy	} else {
826275970Scy		/* verify the notes */
827275970Scy		char *note_one, *note_two;
828275970Scy		ev_uint64_t large_number;
829275970Scy		ev_uint32_t short_number;
830275970Scy
831275970Scy		if (EVTAG_ARRAY_LEN(run, notes) != 2) {
832275970Scy			tt_abort_msg("Wrong number of note strings.");
833275970Scy		}
834275970Scy
835275970Scy		if (EVTAG_ARRAY_GET(run, notes, 0, &note_one) == -1 ||
836275970Scy		    EVTAG_ARRAY_GET(run, notes, 1, &note_two) == -1) {
837275970Scy			tt_abort_msg("Could not get note strings.");
838275970Scy		}
839275970Scy
840275970Scy		if (strcmp(note_one, "this is my note") ||
841275970Scy		    strcmp(note_two, "pps")) {
842275970Scy			tt_abort_msg("Incorrect note strings encoded.");
843275970Scy		}
844275970Scy
845275970Scy		if (EVTAG_GET(run, large_number, &large_number) == -1 ||
846275970Scy		    large_number != 0xdead0a0bcafebeefLL) {
847275970Scy			tt_abort_msg("Incorrrect large_number.");
848275970Scy		}
849275970Scy
850275970Scy		if (EVTAG_ARRAY_LEN(run, other_numbers) != 2) {
851275970Scy			tt_abort_msg("Wrong number of other_numbers.");
852275970Scy		}
853275970Scy
854275970Scy		if (EVTAG_ARRAY_GET(
855275970Scy			    run, other_numbers, 0, &short_number) == -1) {
856275970Scy			tt_abort_msg("Could not get short number.");
857275970Scy		}
858275970Scy		tt_uint_op(short_number, ==, 0xdead0a0b);
859275970Scy
860275970Scy	}
861275970Scy	tt_int_op(EVTAG_ARRAY_LEN(attack, how_often), ==, 3);
862275970Scy
863275970Scy	for (i = 0; i < 3; ++i) {
864275970Scy		ev_uint32_t res;
865275970Scy		if (EVTAG_ARRAY_GET(attack, how_often, i, &res) == -1) {
866275970Scy			TT_DIE(("Cannot get %dth how_often msg.", i));
867275970Scy		}
868275970Scy		if ((int)res != i) {
869275970Scy			TT_DIE(("Wrong message encoded %d != %d", i, res));
870275970Scy		}
871275970Scy	}
872275970Scy
873275970Scy	test_ok = 1;
874275970Scyend:
875275970Scy	if (msg)
876275970Scy		msg_free(msg);
877275970Scy	if (msg2)
878275970Scy		msg_free(msg2);
879275970Scy	if (tmp)
880275970Scy		evbuffer_free(tmp);
881275970Scy}
882275970Scy
883275970Scy#define RPC_LEGACY(name)						\
884275970Scy	{ #name, run_legacy_test_fn, TT_FORK|TT_NEED_BASE|TT_LEGACY,	\
885275970Scy		    &legacy_setup,					\
886275970Scy		    rpc_##name }
887275970Scy#else
888275970Scy/* NO_PYTHON_EXISTS */
889275970Scy
890275970Scy#define RPC_LEGACY(name) \
891275970Scy	{ #name, NULL, TT_SKIP, NULL, NULL }
892275970Scy
893275970Scy#endif
894275970Scy
895275970Scystruct testcase_t rpc_testcases[] = {
896275970Scy	RPC_LEGACY(basic_test),
897275970Scy	RPC_LEGACY(basic_message),
898275970Scy	RPC_LEGACY(basic_client),
899275970Scy	RPC_LEGACY(basic_queued_client),
900275970Scy	RPC_LEGACY(basic_client_with_pause),
901275970Scy	RPC_LEGACY(client_timeout),
902275970Scy	RPC_LEGACY(test),
903275970Scy
904275970Scy	END_OF_TESTCASES,
905275970Scy};
906