File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / libevent / test / regress_rpc.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 23:02:54 2012 UTC (12 years, 8 months ago) by misho
Branches: libevent, MAIN
CVS tags: v1_4_14bp0, v1_4_14b, HEAD
libevent

    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>