mirror of
https://github.com/cuberite/libevent.git
synced 2025-09-14 14:54:49 -04:00
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:
parent
1caff9bcc8
commit
c4836d1053
@ -51,11 +51,14 @@
|
|||||||
|
|
||||||
int decode_int(u_int32_t *pnumber, struct evbuffer *evbuf);
|
int decode_int(u_int32_t *pnumber, struct evbuffer *evbuf);
|
||||||
|
|
||||||
static struct evbuffer *_buf;
|
static struct evbuffer *_buf; /* not thread safe */
|
||||||
|
|
||||||
void
|
void
|
||||||
evtag_init()
|
evtag_init()
|
||||||
{
|
{
|
||||||
|
if (_buf != NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
if ((_buf = evbuffer_new()) == NULL)
|
if ((_buf = evbuffer_new()) == NULL)
|
||||||
event_err(1, "%s: malloc", __func__);
|
event_err(1, "%s: malloc", __func__);
|
||||||
}
|
}
|
||||||
|
28
evrpc.c
28
evrpc.c
@ -59,18 +59,27 @@
|
|||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
struct evrpc_base *
|
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));
|
struct evrpc_base* base = calloc(1, sizeof(struct evrpc_base));
|
||||||
if (base == NULL)
|
if (base == NULL)
|
||||||
return (NULL);
|
return (NULL);
|
||||||
|
|
||||||
|
/* we rely on the tagging sub system */
|
||||||
|
evtag_init();
|
||||||
|
|
||||||
TAILQ_INIT(&base->registered_rpcs);
|
TAILQ_INIT(&base->registered_rpcs);
|
||||||
base->http_server = http_server;
|
base->http_server = http_server;
|
||||||
|
|
||||||
return (base);
|
return (base);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
evrpc_free(struct evrpc_base *base)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void evrpc_request_cb(struct evhttp_request *, void *);
|
void evrpc_request_cb(struct evhttp_request *, void *);
|
||||||
void evrpc_request_done(struct evrpc_req_generic*);
|
void evrpc_request_done(struct evrpc_req_generic*);
|
||||||
|
|
||||||
@ -106,6 +115,8 @@ evrpc_register_rpc(struct evrpc_base *base, struct evrpc *rpc,
|
|||||||
evrpc_request_cb,
|
evrpc_request_cb,
|
||||||
rpc);
|
rpc);
|
||||||
|
|
||||||
|
free(constructed_uri);
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,18 +139,14 @@ evrpc_request_cb(struct evhttp_request *req, void *arg)
|
|||||||
rpc_state->request = rpc->request_new();
|
rpc_state->request = rpc->request_new();
|
||||||
if (rpc_state->request == NULL)
|
if (rpc_state->request == NULL)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
|
rpc_state->rpc = rpc;
|
||||||
|
|
||||||
if (rpc->request_unmarshal(
|
if (rpc->request_unmarshal(
|
||||||
rpc_state->request, req->input_buffer) == -1) {
|
rpc_state->request, req->input_buffer) == -1) {
|
||||||
/* we failed to parse the request; that's a bummer */
|
/* we failed to parse the request; that's a bummer */
|
||||||
goto error;
|
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 */
|
/* 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)
|
if (rpc_state->reply == NULL)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
rpc_state->rpc = rpc;
|
|
||||||
rpc_state->http_req = req;
|
rpc_state->http_req = req;
|
||||||
rpc_state->done = evrpc_request_done;
|
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;
|
struct evrpc *rpc = rpc_state->rpc;
|
||||||
|
|
||||||
if (rpc_state->request != NULL)
|
if (rpc_state->request != NULL)
|
||||||
rpc->request_free(rpc_state);
|
rpc->request_free(rpc_state->request);
|
||||||
if (rpc_state->reply != NULL)
|
if (rpc_state->reply != NULL)
|
||||||
rpc->reply_free(rpc_state->reply);
|
rpc->reply_free(rpc_state->reply);
|
||||||
free(rpc_state);
|
free(rpc_state);
|
||||||
@ -184,7 +190,7 @@ evrpc_request_done(struct evrpc_req_generic* rpc_state)
|
|||||||
struct evrpc *rpc = rpc_state->rpc;
|
struct evrpc *rpc = rpc_state->rpc;
|
||||||
struct evbuffer* data;
|
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 */
|
/* the reply was not completely filled in. error out */
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
10
evrpc.h
10
evrpc.h
@ -46,9 +46,6 @@ struct evrpc {
|
|||||||
/* unmarshals the buffer into the proper request structure */
|
/* unmarshals the buffer into the proper request structure */
|
||||||
int (*request_unmarshal)(void *, struct evbuffer *);
|
int (*request_unmarshal)(void *, struct evbuffer *);
|
||||||
|
|
||||||
/* verifies that the unmarshaled buffer is complete */
|
|
||||||
int (*request_complete)(void *);
|
|
||||||
|
|
||||||
/* creates a new reply structure */
|
/* creates a new reply structure */
|
||||||
void *(*reply_new)(void);
|
void *(*reply_new)(void);
|
||||||
|
|
||||||
@ -109,9 +106,9 @@ EVRPC_STRUCT(rpcname) { \
|
|||||||
* after this call has finished.
|
* after this call has finished.
|
||||||
*/
|
*/
|
||||||
#define EVRPC_REQUEST_DONE(rpc_req) do { \
|
#define EVRPC_REQUEST_DONE(rpc_req) do { \
|
||||||
struct evrpc_req_generic *req = (struct evrpc_req_generic)(rpc_req); \
|
struct evrpc_req_generic *_req = (struct evrpc_req_generic *)(rpc_req); \
|
||||||
req->done(req); \
|
_req->done(_req); \
|
||||||
}
|
} while (0)
|
||||||
|
|
||||||
|
|
||||||
/* Takes a request object and fills it in with the right magic */
|
/* 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_new = (void *(*)(void))request##_new; \
|
||||||
(rpc)->request_free = (void (*)(void *))request##_free; \
|
(rpc)->request_free = (void (*)(void *))request##_free; \
|
||||||
(rpc)->request_unmarshal = (int (*)(void *, struct evbuffer *))request##_unmarshal; \
|
(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_new = (void *(*)(void))reply##_new; \
|
||||||
(rpc)->reply_free = (void (*)(void *))reply##_free; \
|
(rpc)->reply_free = (void (*)(void *))reply##_free; \
|
||||||
(rpc)->reply_complete = (int (*)(void *))reply##_complete; \
|
(rpc)->reply_complete = (int (*)(void *))reply##_complete; \
|
||||||
|
7
http.c
7
http.c
@ -932,7 +932,7 @@ evhttp_read_header(int fd, short what, void *arg)
|
|||||||
res = evhttp_parse_lines(req, evcon->input_buffer);
|
res = evhttp_parse_lines(req, evcon->input_buffer);
|
||||||
if (res == -1) {
|
if (res == -1) {
|
||||||
/* Error while reading, terminate */
|
/* 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);
|
evhttp_connection_fail(evcon);
|
||||||
return;
|
return;
|
||||||
} else if (res == 0) {
|
} else if (res == 0) {
|
||||||
@ -1190,6 +1190,11 @@ evhttp_response_code(struct evhttp_request *req, int code, const char *reason)
|
|||||||
void
|
void
|
||||||
evhttp_send_page(struct evhttp_request *req, struct evbuffer *databuf)
|
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)
|
if (req->kind != EVHTTP_RESPONSE)
|
||||||
evhttp_response_code(req, 200, "OK");
|
evhttp_response_code(req, 200, "OK");
|
||||||
|
|
||||||
|
@ -416,6 +416,57 @@ http_postrequest_done(struct evhttp_request *req, void *arg)
|
|||||||
event_loopexit(NULL);
|
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
|
void
|
||||||
http_suite(void)
|
http_suite(void)
|
||||||
@ -423,4 +474,5 @@ http_suite(void)
|
|||||||
http_basic_test();
|
http_basic_test();
|
||||||
http_connection_test();
|
http_connection_test();
|
||||||
http_post_test();
|
http_post_test();
|
||||||
|
http_failure_test();
|
||||||
}
|
}
|
||||||
|
@ -56,8 +56,11 @@
|
|||||||
#include "evhttp.h"
|
#include "evhttp.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "evrpc.h"
|
#include "evrpc.h"
|
||||||
|
|
||||||
#include "regress.gen.h"
|
#include "regress.gen.h"
|
||||||
|
|
||||||
|
extern int test_ok;
|
||||||
|
|
||||||
static struct evhttp *
|
static struct evhttp *
|
||||||
http_setup(short *pport)
|
http_setup(short *pport)
|
||||||
{
|
{
|
||||||
@ -86,29 +89,194 @@ EVRPC_DEFINE(Message, msg, kill);
|
|||||||
void
|
void
|
||||||
MessageCB(EVRPC_STRUCT(Message)* rpc, void *arg)
|
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
|
static void
|
||||||
rpc_basic_test(void)
|
rpc_basic_test(void)
|
||||||
{
|
{
|
||||||
short port;
|
short port;
|
||||||
struct evhttp *http = NULL;
|
struct evhttp *http = NULL;
|
||||||
struct evrpc_base *base = NULL;
|
struct evrpc_base *base = NULL;
|
||||||
|
struct evhttp_connection *evcon = NULL;
|
||||||
|
struct evhttp_request *req = NULL;
|
||||||
|
|
||||||
fprintf(stdout, "Testing Basic RPC Support: ");
|
fprintf(stdout, "Testing Basic RPC Support: ");
|
||||||
|
|
||||||
http = http_setup(&port);
|
rpc_setup(&http, &port, &base);
|
||||||
base = evrpc_init(http);
|
|
||||||
|
|
||||||
EVRPC_REGISTER(base, "Message", msg, kill, MessageCB, NULL);
|
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();
|
event_dispatch();
|
||||||
|
|
||||||
|
if (test_ok != 1) {
|
||||||
|
fprintf(stdout, "FAILED\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
fprintf(stdout, "OK\n");
|
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
|
void
|
||||||
rpc_suite(void)
|
rpc_suite(void)
|
||||||
{
|
{
|
||||||
rpc_basic_test();
|
rpc_basic_test();
|
||||||
|
rpc_basic_message();
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user