From: Niels Provos Date: Fri, 17 Nov 2006 06:06:17 +0000 (+0000) Subject: make sure that the rpc callback receives an unmarshaled payload; X-Git-Tag: release-2.0.1-alpha~701 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=c4836d10539e9937d2ee0afce2675362dce01e00;p=libevent make sure that the rpc callback receives an unmarshaled payload; make sure that the rpc reply contains a good rpc structure, too. svn:r258 --- diff --git a/event_tagging.c b/event_tagging.c index b60d722e..ac4e3475 100644 --- a/event_tagging.c +++ b/event_tagging.c @@ -51,11 +51,14 @@ int decode_int(u_int32_t *pnumber, struct evbuffer *evbuf); -static struct evbuffer *_buf; +static struct evbuffer *_buf; /* not thread safe */ void evtag_init() { + if (_buf != NULL) + return; + if ((_buf = evbuffer_new()) == NULL) event_err(1, "%s: malloc", __func__); } diff --git a/evrpc.c b/evrpc.c index 4e9ba87d..74d6601a 100644 --- a/evrpc.c +++ b/evrpc.c @@ -59,18 +59,27 @@ #include "log.h" struct evrpc_base * -evrpc_init(struct evhttp* http_server) +evrpc_init(struct evhttp *http_server) { struct evrpc_base* base = calloc(1, sizeof(struct evrpc_base)); if (base == NULL) return (NULL); + /* we rely on the tagging sub system */ + evtag_init(); + TAILQ_INIT(&base->registered_rpcs); base->http_server = http_server; return (base); } +void +evrpc_free(struct evrpc_base *base) +{ + +} + void evrpc_request_cb(struct evhttp_request *, void *); void evrpc_request_done(struct evrpc_req_generic*); @@ -105,6 +114,8 @@ evrpc_register_rpc(struct evrpc_base *base, struct evrpc *rpc, constructed_uri, evrpc_request_cb, rpc); + + free(constructed_uri); return (0); } @@ -128,18 +139,14 @@ evrpc_request_cb(struct evhttp_request *req, void *arg) rpc_state->request = rpc->request_new(); if (rpc_state->request == NULL) goto error; + + rpc_state->rpc = rpc; + if (rpc->request_unmarshal( rpc_state->request, req->input_buffer) == -1) { /* we failed to parse the request; that's a bummer */ goto error; } - if (!rpc->request_complete(rpc_state->request)) { - /* - * we were able to parse the structure but not all required - * fields had been filled in. - */ - goto error; - } /* at this point, we have a well formed request, prepare the reply */ @@ -147,7 +154,6 @@ evrpc_request_cb(struct evhttp_request *req, void *arg) if (rpc_state->reply == NULL) goto error; - rpc_state->rpc = rpc; rpc_state->http_req = req; rpc_state->done = evrpc_request_done; @@ -170,7 +176,7 @@ evrpc_reqstate_free(struct evrpc_req_generic* rpc_state) struct evrpc *rpc = rpc_state->rpc; if (rpc_state->request != NULL) - rpc->request_free(rpc_state); + rpc->request_free(rpc_state->request); if (rpc_state->reply != NULL) rpc->reply_free(rpc_state->reply); free(rpc_state); @@ -184,7 +190,7 @@ evrpc_request_done(struct evrpc_req_generic* rpc_state) struct evrpc *rpc = rpc_state->rpc; struct evbuffer* data; - if (!rpc->reply_complete(rpc_state->reply)) { + if (rpc->reply_complete(rpc_state->reply) == -1) { /* the reply was not completely filled in. error out */ goto error; } diff --git a/evrpc.h b/evrpc.h index c46dcba9..18db7f1b 100644 --- a/evrpc.h +++ b/evrpc.h @@ -46,9 +46,6 @@ struct evrpc { /* unmarshals the buffer into the proper request structure */ int (*request_unmarshal)(void *, struct evbuffer *); - /* verifies that the unmarshaled buffer is complete */ - int (*request_complete)(void *); - /* creates a new reply structure */ void *(*reply_new)(void); @@ -109,9 +106,9 @@ EVRPC_STRUCT(rpcname) { \ * after this call has finished. */ #define EVRPC_REQUEST_DONE(rpc_req) do { \ - struct evrpc_req_generic *req = (struct evrpc_req_generic)(rpc_req); \ - req->done(req); \ -} + struct evrpc_req_generic *_req = (struct evrpc_req_generic *)(rpc_req); \ + _req->done(_req); \ +} while (0) /* Takes a request object and fills it in with the right magic */ @@ -123,7 +120,6 @@ EVRPC_STRUCT(rpcname) { \ (rpc)->request_new = (void *(*)(void))request##_new; \ (rpc)->request_free = (void (*)(void *))request##_free; \ (rpc)->request_unmarshal = (int (*)(void *, struct evbuffer *))request##_unmarshal; \ - (rpc)->request_complete = (int (*)(void *))request##_complete; \ (rpc)->reply_new = (void *(*)(void))reply##_new; \ (rpc)->reply_free = (void (*)(void *))reply##_free; \ (rpc)->reply_complete = (int (*)(void *))reply##_complete; \ diff --git a/http.c b/http.c index 4dfcd3fb..a7a1bdb1 100644 --- a/http.c +++ b/http.c @@ -932,7 +932,7 @@ evhttp_read_header(int fd, short what, void *arg) res = evhttp_parse_lines(req, evcon->input_buffer); if (res == -1) { /* Error while reading, terminate */ - event_warnx("%s: bad header lines on %d\n", __func__, fd); + event_debug(("%s: bad header lines on %d\n", __func__, fd)); evhttp_connection_fail(evcon); return; } else if (res == 0) { @@ -1190,6 +1190,11 @@ evhttp_response_code(struct evhttp_request *req, int code, const char *reason) void evhttp_send_page(struct evhttp_request *req, struct evbuffer *databuf) { + if (!req->major || !req->minor) { + req->major = 1; + req->minor = 1; + } + if (req->kind != EVHTTP_RESPONSE) evhttp_response_code(req, 200, "OK"); diff --git a/test/regress_http.c b/test/regress_http.c index b04a3639..395e0ab9 100644 --- a/test/regress_http.c +++ b/test/regress_http.c @@ -416,6 +416,57 @@ http_postrequest_done(struct evhttp_request *req, void *arg) event_loopexit(NULL); } +void +http_failure_readcb(struct bufferevent *bev, void *arg) +{ + const char *what = "400 Bad Request"; + if (evbuffer_find(bev->input, what, strlen(what)) != NULL) { + test_ok = 2; + event_loopexit(NULL); + } +} + +/* + * Testing that the HTTP server can deal with a malformed request. + */ +void +http_failure_test(void) +{ + struct bufferevent *bev; + int fd; + char *http_request; + short port = -1; + + test_ok = 0; + fprintf(stdout, "Testing Bad HTTP Request: "); + + http = http_setup(&port); + + fd = http_connect("127.0.0.1", port); + + /* Stupid thing to send a request */ + bev = bufferevent_new(fd, http_failure_readcb, http_writecb, + http_errorcb, NULL); + + http_request = "illegal request\r\n"; + + bufferevent_write(bev, http_request, strlen(http_request)); + + event_dispatch(); + + bufferevent_free(bev); + close(fd); + + evhttp_free(http); + + if (test_ok != 2) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + fprintf(stdout, "OK\n"); +} + void http_suite(void) @@ -423,4 +474,5 @@ http_suite(void) http_basic_test(); http_connection_test(); http_post_test(); + http_failure_test(); } diff --git a/test/regress_rpc.c b/test/regress_rpc.c index 09a59606..59113bf5 100644 --- a/test/regress_rpc.c +++ b/test/regress_rpc.c @@ -56,8 +56,11 @@ #include "evhttp.h" #include "log.h" #include "evrpc.h" + #include "regress.gen.h" +extern int test_ok; + static struct evhttp * http_setup(short *pport) { @@ -86,29 +89,194 @@ EVRPC_DEFINE(Message, msg, kill); void MessageCB(EVRPC_STRUCT(Message)* rpc, void *arg) { + struct kill* kill_reply = rpc->reply; + + /* we just want to fill in some non-sense */ + EVTAG_ASSIGN(kill_reply, weapon, "dagger"); + EVTAG_ASSIGN(kill_reply, action, "wave around like an idiot"); + + /* no reply to the RPC */ + EVRPC_REQUEST_DONE(rpc); } static void -rpc_basic_test(void) +rpc_setup(struct evhttp **phttp, short *pport, struct evrpc_base **pbase) { short port; struct evhttp *http = NULL; struct evrpc_base *base = NULL; - fprintf(stdout, "Testing Basic RPC Support: "); - http = http_setup(&port); base = evrpc_init(http); EVRPC_REGISTER(base, "Message", msg, kill, MessageCB, NULL); + + *phttp = http; + *pport = port; + *pbase = base; +} + +static void +rpc_postrequest_failure(struct evhttp_request *req, void *arg) +{ + if (req->response_code != HTTP_SERVUNAVAIL) { + + fprintf(stderr, "FAILED (response code)\n"); + exit(1); + } + + test_ok = 1; + event_loopexit(NULL); +} + +/* + * Test a malformed payload submitted as an RPC + */ + +static void +rpc_basic_test(void) +{ + short port; + struct evhttp *http = NULL; + struct evrpc_base *base = NULL; + struct evhttp_connection *evcon = NULL; + struct evhttp_request *req = NULL; + + fprintf(stdout, "Testing Basic RPC Support: "); + + rpc_setup(&http, &port, &base); + + evcon = evhttp_connection_new("127.0.0.1", port); + if (evcon == NULL) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + /* + * At this point, we want to schedule an HTTP POST request + * server using our make request method. + */ + + req = evhttp_request_new(rpc_postrequest_failure, NULL); + if (req == NULL) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + /* Add the information that we care about */ + evhttp_add_header(req->output_headers, "Host", "somehost"); + evbuffer_add_printf(req->output_buffer, "Some Nonsense"); + + if (evhttp_make_request(evcon, req, + EVHTTP_REQ_POST, + "/.rpc.Message") == -1) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + test_ok = 0; + + event_dispatch(); + + if (test_ok != 1) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + fprintf(stdout, "OK\n"); + + evhttp_free(http); +} + +static void +rpc_postrequest_done(struct evhttp_request *req, void *arg) +{ + struct kill* kill_reply = NULL; + + if (req->response_code != HTTP_OK) { + + fprintf(stderr, "FAILED (response code)\n"); + exit(1); + } + + kill_reply = kill_new(); + + if ((kill_unmarshal(kill_reply, req->input_buffer)) == -1) { + fprintf(stderr, "FAILED (unmarshal)\n"); + exit(1); + } + kill_free(kill_reply); + + test_ok = 1; + event_loopexit(NULL); +} + +static void +rpc_basic_message(void) +{ + short port; + struct evhttp *http = NULL; + struct evrpc_base *base = NULL; + struct evhttp_connection *evcon = NULL; + struct evhttp_request *req = NULL; + struct msg *msg; + + fprintf(stdout, "Testing Good RPC Post: "); + + rpc_setup(&http, &port, &base); + + evcon = evhttp_connection_new("127.0.0.1", port); + if (evcon == NULL) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + /* + * At this point, we want to schedule an HTTP POST request + * server using our make request method. + */ + + req = evhttp_request_new(rpc_postrequest_done, NULL); + if (req == NULL) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + /* Add the information that we care about */ + evhttp_add_header(req->output_headers, "Host", "somehost"); + + /* set up the basic message */ + msg = msg_new(); + EVTAG_ASSIGN(msg, from_name, "niels"); + EVTAG_ASSIGN(msg, to_name, "tester"); + msg_marshal(req->output_buffer, msg); + msg_free(msg); + + if (evhttp_make_request(evcon, req, + EVHTTP_REQ_POST, + "/.rpc.Message") == -1) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + test_ok = 0; + event_dispatch(); + if (test_ok != 1) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + fprintf(stdout, "OK\n"); + + evhttp_free(http); } void rpc_suite(void) { rpc_basic_test(); + rpc_basic_message(); }