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*)"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
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: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>