make sure that the rpc callback receives an unmarshaled payload;

make sure that the rpc reply contains a good rpc structure, too.


svn:r258
This commit is contained in:
Niels Provos 2006-11-17 06:06:17 +00:00
parent 1caff9bcc8
commit c4836d1053
6 changed files with 254 additions and 24 deletions

View File

@ -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__);
}

28
evrpc.c
View File

@ -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;
}

10
evrpc.h
View File

@ -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; \

7
http.c
View File

@ -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");

View File

@ -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();
}

View File

@ -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_setup(struct evhttp **phttp, short *pport, struct evrpc_base **pbase)
{
short port;
struct evhttp *http = NULL;
struct evrpc_base *base = NULL;
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: ");
http = http_setup(&port);
base = evrpc_init(http);
EVRPC_REGISTER(base, "Message", msg, kill, MessageCB, NULL);
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();
}