OLD | NEW |
| (Empty) |
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 * | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 * | |
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 | |
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 | |
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 | |
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 | |
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 | |
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*)"in
put") | |
454 != NULL); | |
455 assert(evrpc_add_hook(base, EVRPC_OUTPUT, rpc_hook_add_header, (void*)"o
utput") | |
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 | |
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 | |
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 | |
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 | |
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 } | |
OLD | NEW |