xref: /netbsd-src/external/bsd/libevent/dist/test/regress_rpc.c (revision a5847cc334d9a7029f6352b847e9e8d71a0f9e0c)
1 /*	$NetBSD: regress_rpc.c,v 1.1.1.1 2009/11/02 10:01:04 plunky Exp $	*/
2 /*
3  * Copyright (c) 2003-2006 Niels Provos <provos@citi.umich.edu>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. The name of the author may not be used to endorse or promote products
15  *    derived from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #ifdef WIN32
30 #include <winsock2.h>
31 #include <windows.h>
32 #endif
33 
34 #ifdef HAVE_CONFIG_H
35 #include "config.h"
36 #endif
37 
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #ifdef 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 "event.h"
58 #include "evhttp.h"
59 #include "log.h"
60 #include "evrpc.h"
61 
62 #include "regress.gen.h"
63 
64 void rpc_suite(void);
65 
66 extern int test_ok;
67 
68 static struct evhttp *
69 http_setup(short *pport)
70 {
71 	int i;
72 	struct evhttp *myhttp;
73 	short port = -1;
74 
75 	/* Try a few different ports */
76 	for (i = 0; i < 50; ++i) {
77 		myhttp = evhttp_start("127.0.0.1", 8080 + i);
78 		if (myhttp != NULL) {
79 			port = 8080 + i;
80 			break;
81 		}
82 	}
83 
84 	if (port == -1)
85 		event_errx(1, "Could not start web server");
86 
87 	*pport = port;
88 	return (myhttp);
89 }
90 
91 EVRPC_HEADER(Message, msg, kill);
92 EVRPC_HEADER(NeverReply, msg, kill);
93 
94 EVRPC_GENERATE(Message, msg, kill);
95 EVRPC_GENERATE(NeverReply, msg, kill);
96 
97 static int need_input_hook = 0;
98 static int need_output_hook = 0;
99 
100 static void
101 MessageCb(EVRPC_STRUCT(Message)* rpc, void *arg)
102 {
103 	struct kill* kill_reply = rpc->reply;
104 
105 	if (need_input_hook) {
106 		struct evhttp_request* req = EVRPC_REQUEST_HTTP(rpc);
107 		const char *header = evhttp_find_header(
108 			req->input_headers, "X-Hook");
109 		assert(strcmp(header, "input") == 0);
110 	}
111 
112 	/* we just want to fill in some non-sense */
113 	EVTAG_ASSIGN(kill_reply, weapon, "dagger");
114 	EVTAG_ASSIGN(kill_reply, action, "wave around like an idiot");
115 
116 	/* no reply to the RPC */
117 	EVRPC_REQUEST_DONE(rpc);
118 }
119 
120 static EVRPC_STRUCT(NeverReply) *saved_rpc;
121 
122 static void
123 NeverReplyCb(EVRPC_STRUCT(NeverReply)* rpc, void *arg)
124 {
125 	test_ok += 1;
126 	saved_rpc = rpc;
127 }
128 
129 static void
130 rpc_setup(struct evhttp **phttp, short *pport, struct evrpc_base **pbase)
131 {
132 	short port;
133 	struct evhttp *http = NULL;
134 	struct evrpc_base *base = NULL;
135 
136 	http = http_setup(&port);
137 	base = evrpc_init(http);
138 
139 	EVRPC_REGISTER(base, Message, msg, kill, MessageCb, NULL);
140 	EVRPC_REGISTER(base, NeverReply, msg, kill, NeverReplyCb, NULL);
141 
142 	*phttp = http;
143 	*pport = port;
144 	*pbase = base;
145 
146 	need_input_hook = 0;
147 	need_output_hook = 0;
148 }
149 
150 static void
151 rpc_teardown(struct evrpc_base *base)
152 {
153 	assert(EVRPC_UNREGISTER(base, Message) == 0);
154 	assert(EVRPC_UNREGISTER(base, NeverReply) == 0);
155 
156 	evrpc_free(base);
157 }
158 
159 static void
160 rpc_postrequest_failure(struct evhttp_request *req, void *arg)
161 {
162 	if (req->response_code != HTTP_SERVUNAVAIL) {
163 
164 		fprintf(stderr, "FAILED (response code)\n");
165 		exit(1);
166 	}
167 
168 	test_ok = 1;
169 	event_loopexit(NULL);
170 }
171 
172 /*
173  * Test a malformed payload submitted as an RPC
174  */
175 
176 static void
177 rpc_basic_test(void)
178 {
179 	short port;
180 	struct evhttp *http = NULL;
181 	struct evrpc_base *base = NULL;
182 	struct evhttp_connection *evcon = NULL;
183 	struct evhttp_request *req = NULL;
184 
185 	fprintf(stdout, "Testing Basic RPC Support: ");
186 
187 	rpc_setup(&http, &port, &base);
188 
189 	evcon = evhttp_connection_new("127.0.0.1", port);
190 	if (evcon == NULL) {
191 		fprintf(stdout, "FAILED\n");
192 		exit(1);
193 	}
194 
195 	/*
196 	 * At this point, we want to schedule an HTTP POST request
197 	 * server using our make request method.
198 	 */
199 
200 	req = evhttp_request_new(rpc_postrequest_failure, NULL);
201 	if (req == NULL) {
202 		fprintf(stdout, "FAILED\n");
203 		exit(1);
204 	}
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 		fprintf(stdout, "FAILED\n");
214 		exit(1);
215 	}
216 
217 	test_ok = 0;
218 
219 	event_dispatch();
220 
221 	evhttp_connection_free(evcon);
222 
223 	rpc_teardown(base);
224 
225 	if (test_ok != 1) {
226 		fprintf(stdout, "FAILED\n");
227 		exit(1);
228 	}
229 
230 	fprintf(stdout, "OK\n");
231 
232 	evhttp_free(http);
233 }
234 
235 static void
236 rpc_postrequest_done(struct evhttp_request *req, void *arg)
237 {
238 	struct kill* kill_reply = NULL;
239 
240 	if (req->response_code != HTTP_OK) {
241 
242 		fprintf(stderr, "FAILED (response code)\n");
243 		exit(1);
244 	}
245 
246 	kill_reply = kill_new();
247 
248 	if ((kill_unmarshal(kill_reply, req->input_buffer)) == -1) {
249 		fprintf(stderr, "FAILED (unmarshal)\n");
250 		exit(1);
251 	}
252 
253 	kill_free(kill_reply);
254 
255 	test_ok = 1;
256 	event_loopexit(NULL);
257 }
258 
259 static void
260 rpc_basic_message(void)
261 {
262 	short port;
263 	struct evhttp *http = NULL;
264 	struct evrpc_base *base = NULL;
265 	struct evhttp_connection *evcon = NULL;
266 	struct evhttp_request *req = NULL;
267 	struct msg *msg;
268 
269 	fprintf(stdout, "Testing Good RPC Post: ");
270 
271 	rpc_setup(&http, &port, &base);
272 
273 	evcon = evhttp_connection_new("127.0.0.1", port);
274 	if (evcon == NULL) {
275 		fprintf(stdout, "FAILED\n");
276 		exit(1);
277 	}
278 
279 	/*
280 	 * At this point, we want to schedule an HTTP POST request
281 	 * server using our make request method.
282 	 */
283 
284 	req = evhttp_request_new(rpc_postrequest_done, NULL);
285 	if (req == NULL) {
286 		fprintf(stdout, "FAILED\n");
287 		exit(1);
288 	}
289 
290 	/* Add the information that we care about */
291 	evhttp_add_header(req->output_headers, "Host", "somehost");
292 
293 	/* set up the basic message */
294 	msg = msg_new();
295 	EVTAG_ASSIGN(msg, from_name, "niels");
296 	EVTAG_ASSIGN(msg, to_name, "tester");
297 	msg_marshal(req->output_buffer, msg);
298 	msg_free(msg);
299 
300 	if (evhttp_make_request(evcon, req,
301 		EVHTTP_REQ_POST,
302 		"/.rpc.Message") == -1) {
303 		fprintf(stdout, "FAILED\n");
304 		exit(1);
305 	}
306 
307 	test_ok = 0;
308 
309 	event_dispatch();
310 
311 	evhttp_connection_free(evcon);
312 
313 	rpc_teardown(base);
314 
315 	if (test_ok != 1) {
316 		fprintf(stdout, "FAILED\n");
317 		exit(1);
318 	}
319 
320 	fprintf(stdout, "OK\n");
321 
322 	evhttp_free(http);
323 }
324 
325 static struct evrpc_pool *
326 rpc_pool_with_connection(short port)
327 {
328 	struct evhttp_connection *evcon;
329 	struct evrpc_pool *pool;
330 
331 	pool = evrpc_pool_new(NULL);
332 	assert(pool != NULL);
333 
334 	evcon = evhttp_connection_new("127.0.0.1", port);
335 	assert(evcon != NULL);
336 
337 	evrpc_pool_add_connection(pool, evcon);
338 
339 	return (pool);
340 }
341 
342 static void
343 GotKillCb(struct evrpc_status *status,
344     struct msg *msg, struct kill *kill, void *arg)
345 {
346 	char *weapon;
347 	char *action;
348 
349 	if (need_output_hook) {
350 		struct evhttp_request *req = status->http_req;
351 		const char *header = evhttp_find_header(
352 			req->input_headers, "X-Pool-Hook");
353 		assert(strcmp(header, "ran") == 0);
354 	}
355 
356 	if (status->error != EVRPC_STATUS_ERR_NONE)
357 		goto done;
358 
359 	if (EVTAG_GET(kill, weapon, &weapon) == -1) {
360 		fprintf(stderr, "get weapon\n");
361 		goto done;
362 	}
363 	if (EVTAG_GET(kill, action, &action) == -1) {
364 		fprintf(stderr, "get action\n");
365 		goto done;
366 	}
367 
368 	if (strcmp(weapon, "dagger"))
369 		goto done;
370 
371 	if (strcmp(action, "wave around like an idiot"))
372 		goto done;
373 
374 	test_ok += 1;
375 
376 done:
377 	event_loopexit(NULL);
378 }
379 
380 static void
381 GotKillCbTwo(struct evrpc_status *status,
382     struct msg *msg, struct kill *kill, void *arg)
383 {
384 	char *weapon;
385 	char *action;
386 
387 	if (status->error != EVRPC_STATUS_ERR_NONE)
388 		goto done;
389 
390 	if (EVTAG_GET(kill, weapon, &weapon) == -1) {
391 		fprintf(stderr, "get weapon\n");
392 		goto done;
393 	}
394 	if (EVTAG_GET(kill, action, &action) == -1) {
395 		fprintf(stderr, "get action\n");
396 		goto done;
397 	}
398 
399 	if (strcmp(weapon, "dagger"))
400 		goto done;
401 
402 	if (strcmp(action, "wave around like an idiot"))
403 		goto done;
404 
405 	test_ok += 1;
406 
407 done:
408 	if (test_ok == 2)
409 		event_loopexit(NULL);
410 }
411 
412 static int
413 rpc_hook_add_header(struct evhttp_request *req,
414     struct evbuffer *evbuf, void *arg)
415 {
416 	const char *hook_type = arg;
417 	if (strcmp("input", hook_type) == 0)
418 		evhttp_add_header(req->input_headers, "X-Hook", hook_type);
419 	else
420 		evhttp_add_header(req->output_headers, "X-Hook", hook_type);
421 	return (0);
422 }
423 
424 static int
425 rpc_hook_remove_header(struct evhttp_request *req,
426     struct evbuffer *evbuf, void *arg)
427 {
428 	const char *header = evhttp_find_header(req->input_headers, "X-Hook");
429 	assert(header != NULL);
430 	assert(strcmp(header, arg) == 0);
431 	evhttp_remove_header(req->input_headers, "X-Hook");
432 	evhttp_add_header(req->input_headers, "X-Pool-Hook", "ran");
433 
434 	return (0);
435 }
436 
437 static void
438 rpc_basic_client(void)
439 {
440 	short port;
441 	struct evhttp *http = NULL;
442 	struct evrpc_base *base = NULL;
443 	struct evrpc_pool *pool = NULL;
444 	struct msg *msg;
445 	struct kill *kill;
446 
447 	fprintf(stdout, "Testing RPC Client: ");
448 
449 	rpc_setup(&http, &port, &base);
450 
451 	need_input_hook = 1;
452 	need_output_hook = 1;
453 
454 	assert(evrpc_add_hook(base, EVRPC_INPUT, rpc_hook_add_header, (void*)"input")
455 	    != NULL);
456 	assert(evrpc_add_hook(base, EVRPC_OUTPUT, rpc_hook_add_header, (void*)"output")
457 	    != NULL);
458 
459 	pool = rpc_pool_with_connection(port);
460 
461 	assert(evrpc_add_hook(pool, EVRPC_INPUT, rpc_hook_remove_header, (void*)"output"));
462 
463 	/* set up the basic message */
464 	msg = msg_new();
465 	EVTAG_ASSIGN(msg, from_name, "niels");
466 	EVTAG_ASSIGN(msg, to_name, "tester");
467 
468 	kill = kill_new();
469 
470 	EVRPC_MAKE_REQUEST(Message, pool, msg, kill,  GotKillCb, NULL);
471 
472 	test_ok = 0;
473 
474 	event_dispatch();
475 
476 	if (test_ok != 1) {
477 		fprintf(stdout, "FAILED (1)\n");
478 		exit(1);
479 	}
480 
481 	/* we do it twice to make sure that reuse works correctly */
482 	kill_clear(kill);
483 
484 	EVRPC_MAKE_REQUEST(Message, pool, msg, kill,  GotKillCb, NULL);
485 
486 	event_dispatch();
487 
488 	rpc_teardown(base);
489 
490 	if (test_ok != 2) {
491 		fprintf(stdout, "FAILED (2)\n");
492 		exit(1);
493 	}
494 
495 	fprintf(stdout, "OK\n");
496 
497 	msg_free(msg);
498 	kill_free(kill);
499 
500 	evrpc_pool_free(pool);
501 	evhttp_free(http);
502 }
503 
504 /*
505  * We are testing that the second requests gets send over the same
506  * connection after the first RPCs completes.
507  */
508 static void
509 rpc_basic_queued_client(void)
510 {
511 	short port;
512 	struct evhttp *http = NULL;
513 	struct evrpc_base *base = NULL;
514 	struct evrpc_pool *pool = NULL;
515 	struct msg *msg;
516 	struct kill *kill_one, *kill_two;
517 
518 	fprintf(stdout, "Testing RPC (Queued) Client: ");
519 
520 	rpc_setup(&http, &port, &base);
521 
522 	pool = rpc_pool_with_connection(port);
523 
524 	/* set up the basic message */
525 	msg = msg_new();
526 	EVTAG_ASSIGN(msg, from_name, "niels");
527 	EVTAG_ASSIGN(msg, to_name, "tester");
528 
529 	kill_one = kill_new();
530 	kill_two = kill_new();
531 
532 	EVRPC_MAKE_REQUEST(Message, pool, msg, kill_one,  GotKillCbTwo, NULL);
533 	EVRPC_MAKE_REQUEST(Message, pool, msg, kill_two,  GotKillCb, NULL);
534 
535 	test_ok = 0;
536 
537 	event_dispatch();
538 
539 	rpc_teardown(base);
540 
541 	if (test_ok != 2) {
542 		fprintf(stdout, "FAILED (1)\n");
543 		exit(1);
544 	}
545 
546 	fprintf(stdout, "OK\n");
547 
548 	msg_free(msg);
549 	kill_free(kill_one);
550 	kill_free(kill_two);
551 
552 	evrpc_pool_free(pool);
553 	evhttp_free(http);
554 }
555 
556 static void
557 GotErrorCb(struct evrpc_status *status,
558     struct msg *msg, struct kill *kill, void *arg)
559 {
560 	if (status->error != EVRPC_STATUS_ERR_TIMEOUT)
561 		goto done;
562 
563 	/* should never be complete but just to check */
564 	if (kill_complete(kill) == 0)
565 		goto done;
566 
567 	test_ok += 1;
568 
569 done:
570 	event_loopexit(NULL);
571 }
572 
573 static void
574 rpc_client_timeout(void)
575 {
576 	short port;
577 	struct evhttp *http = NULL;
578 	struct evrpc_base *base = NULL;
579 	struct evrpc_pool *pool = NULL;
580 	struct msg *msg;
581 	struct kill *kill;
582 
583 	fprintf(stdout, "Testing RPC Client Timeout: ");
584 
585 	rpc_setup(&http, &port, &base);
586 
587 	pool = rpc_pool_with_connection(port);
588 
589 	/* set the timeout to 5 seconds */
590 	evrpc_pool_set_timeout(pool, 5);
591 
592 	/* set up the basic message */
593 	msg = msg_new();
594 	EVTAG_ASSIGN(msg, from_name, "niels");
595 	EVTAG_ASSIGN(msg, to_name, "tester");
596 
597 	kill = kill_new();
598 
599 	EVRPC_MAKE_REQUEST(NeverReply, pool, msg, kill, GotErrorCb, NULL);
600 
601 	test_ok = 0;
602 
603 	event_dispatch();
604 
605 	/* free the saved RPC structure up */
606 	EVRPC_REQUEST_DONE(saved_rpc);
607 
608 	rpc_teardown(base);
609 
610 	if (test_ok != 2) {
611 		fprintf(stdout, "FAILED (1)\n");
612 		exit(1);
613 	}
614 
615 	fprintf(stdout, "OK\n");
616 
617 	msg_free(msg);
618 	kill_free(kill);
619 
620 	evrpc_pool_free(pool);
621 	evhttp_free(http);
622 }
623 
624 void
625 rpc_suite(void)
626 {
627 	rpc_basic_test();
628 	rpc_basic_message();
629 	rpc_basic_client();
630 	rpc_basic_queued_client();
631 	rpc_client_timeout();
632 }
633