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