first stab at an rpc layer; this breaks the regression test.

svn:r254
This commit is contained in:
Niels Provos 2006-11-16 07:36:20 +00:00
parent 768aa15c77
commit f554234f74
11 changed files with 463 additions and 42 deletions

View File

@ -4,6 +4,7 @@ AUTOMAKE_OPTIONS = foreign no-dependencies
bin_SCRIPTS = event_rpcgen.py
EXTRA_DIST = acconfig.h event.h event-internal.h log.h evsignal.h evdns.3 \
evrpc.h evrpc-internal.h \
event.3 \
kqueue.c epoll_sub.c epoll.c select.c rtsig.c poll.c signal.c \
evport.c devpoll.c event_rpcgen.py \
@ -39,7 +40,8 @@ SYS_INCLUDES =
endif
libevent_la_SOURCES = event.c buffer.c evbuffer.c log.c event_tagging.c \
http.c evhttp.h http-internal.h evdns.c evdns.h $(SYS_SRC)
http.c evhttp.h http-internal.h evdns.c evdns.h evrpc.c \
evrpc.h evrpc-internal.h $(SYS_SRC)
libevent_la_LIBADD = @LTLIBOBJS@ $(SYS_LIBS)
libevent_la_LDFLAGS = -release @VERSION@ -version-info 1:3:0

View File

@ -53,6 +53,7 @@ typedef unsigned char u_char;
#define HTTP_MOVEPERM 301
#define HTTP_MOVETEMP 302
#define HTTP_NOTFOUND 404
#define HTTP_SERVUNAVAIL 503
struct evhttp;
struct evhttp_request;
@ -82,6 +83,50 @@ void evhttp_send_reply(struct evhttp_request *, int, const char *,
/* Interfaces for making requests */
enum evhttp_cmd_type { EVHTTP_REQ_GET, EVHTTP_REQ_POST, EVHTTP_REQ_HEAD };
enum evhttp_request_kind { EVHTTP_REQUEST, EVHTTP_RESPONSE };
/*
* the request structure that a server receives.
* WARNING: expect this structure to change. I will try to provide
* reasonable accessors.
*/
struct evhttp_request {
TAILQ_ENTRY(evhttp_request) next;
/* the connection object that this request belongs to */
struct evhttp_connection *evcon;
int flags;
#define EVHTTP_REQ_OWN_CONNECTION 0x0001
struct evkeyvalq *input_headers;
struct evkeyvalq *output_headers;
/* xxx: do we still need these? */
char *remote_host;
u_short remote_port;
enum evhttp_request_kind kind;
enum evhttp_cmd_type type;
char *uri; /* uri after HTTP request was parsed */
char major; /* HTTP Major number */
char minor; /* HTTP Minor number */
int got_firstline;
int response_code; /* HTTP Response code */
char *response_code_line; /* Readable response */
struct evbuffer *input_buffer; /* read data */
int ntoread;
struct evbuffer *output_buffer; /* outgoing post or data */
/* Callback */
void (*cb)(struct evhttp_request *, void *);
void *cb_arg;
};
/*
* Creates a new request object that needs to be filled in with the request
* parameters. The callback is executed when the request completed or an

45
evrpc-internal.h Normal file
View File

@ -0,0 +1,45 @@
/*
* Copyright (c) 2006 Niels Provos <provos@citi.umich.edu>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _EVRPC_INTERNAL_H_
#define _EVRPC_INTERNAL_H_
struct evrpc;
#define EVRPC_URI_PREFIX ".rpc."
struct evrpc_base {
/* the HTTP server under which we register our RPC calls */
struct evhttp* http_server;
/* a list of all RPCs registered with us */
TAILQ_HEAD(evrpc_list, evrpc) registered_rpcs;
};
struct evrpc_req_generic;
void evrpc_reqstate_free(struct evrpc_req_generic* rpc_state);
#endif /* _EVRPC_INTERNAL_H_ */

212
evrpc.c Normal file
View File

@ -0,0 +1,212 @@
/*
* Copyright (c) 2000-2004 Niels Provos <provos@citi.umich.edu>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#undef WIN32_LEAN_AND_MEAN
#include "misc.h"
#endif
#include <sys/types.h>
#include <sys/tree.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#else
#include <sys/_time.h>
#endif
#include <sys/queue.h>
#include <stdio.h>
#include <stdlib.h>
#ifndef WIN32
#include <unistd.h>
#endif
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <assert.h>
#include "event.h"
#include "evrpc.h"
#include "evrpc-internal.h"
#include "evhttp.h"
#include "log.h"
struct evrpc_base *
evrpc_init(struct evhttp* http_server)
{
struct evrpc_base* base = calloc(1, sizeof(struct evrpc_base));
if (base == NULL)
return (NULL);
TAILQ_INIT(&base->registered_rpcs);
base->http_server = http_server;
return (base);
}
void evrpc_request_cb(struct evhttp_request *, void *);
void evrpc_request_done(struct evrpc_req_generic*);
/*
* Registers a new RPC with the HTTP server. The evrpc object is expected
* to have been filled in via the EVRPC_REGISTER_OBJECT macro which in turn
* calls this function.
*/
int
evrpc_register_rpc(struct evrpc_base *base, struct evrpc *rpc,
void (*cb)(struct evrpc_req_generic *, void *), void *cb_arg)
{
char *constructed_uri;
int constructed_uri_len;
rpc->cb = cb;
rpc->cb_arg = cb_arg;
constructed_uri_len = strlen(EVRPC_URI_PREFIX) + strlen(rpc->uri) + 1;
if ((constructed_uri = malloc(constructed_uri_len)) == NULL)
event_err(1, "%s: failed to register rpc at %s",
__func__, rpc->uri);
memcpy(constructed_uri, EVRPC_URI_PREFIX, strlen(EVRPC_URI_PREFIX));
memcpy(constructed_uri + strlen(EVRPC_URI_PREFIX),
rpc->uri, strlen(rpc->uri));
constructed_uri[constructed_uri_len - 1] = '\0';
TAILQ_INSERT_TAIL(&base->registered_rpcs, rpc, next);
evhttp_set_cb(base->http_server,
constructed_uri,
evrpc_request_cb,
rpc);
return (0);
}
void
evrpc_request_cb(struct evhttp_request *req, void *arg)
{
struct evrpc *rpc = arg;
struct evrpc_req_generic *rpc_state = NULL;
/* let's verify the outside parameters */
if (req->type != EVHTTP_REQ_POST ||
EVBUFFER_LENGTH(req->input_buffer) <= 0)
goto error;
rpc_state = calloc(1, sizeof(struct evrpc_req_generic));
if (rpc_state == NULL)
goto error;
/* let's check that we can parse the request */
rpc_state->request = rpc->request_new();
if (rpc_state->request == NULL)
goto error;
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 */
rpc_state->reply = rpc->reply_new();
if (rpc_state->reply == NULL)
goto error;
rpc_state->rpc = rpc;
rpc_state->http_req = req;
rpc_state->done = evrpc_request_done;
/* give the rpc to the user; they can deal with it */
rpc->cb(rpc_state, rpc->cb_arg);
return;
error:
evrpc_reqstate_free(rpc_state);
evhttp_send_error(req, HTTP_SERVUNAVAIL, "Service Error");
return;
}
void
evrpc_reqstate_free(struct evrpc_req_generic* rpc_state)
{
struct evrpc *rpc = rpc_state->rpc;
/* clean up all memory */
if (rpc_state != NULL) {
if (rpc_state->request != NULL)
rpc->request_free(rpc_state);
if (rpc_state->reply != NULL)
rpc->reply_free(rpc_state->reply);
free(rpc_state);
}
}
void
evrpc_request_done(struct evrpc_req_generic* rpc_state)
{
struct evhttp_request *req = rpc_state->http_req;
struct evrpc *rpc = rpc_state->rpc;
struct evbuffer* data;
if (!rpc->reply_complete(rpc_state->reply)) {
/* the reply was not completely filled in. error out */
goto error;
}
if ((data = evbuffer_new()) == NULL) {
/* out of memory */
goto error;
}
/* serialize the reply */
rpc->reply_marshal(data, rpc_state->reply);
evhttp_send_reply(req, HTTP_OK, "OK", data);
evbuffer_free(data);
evrpc_reqstate_free(rpc_state);
return;
error:
evrpc_reqstate_free(rpc_state);
evhttp_send_error(req, HTTP_SERVUNAVAIL, "Service Error");
return;
}

151
evrpc.h Normal file
View File

@ -0,0 +1,151 @@
/*
* Copyright (c) 2006 Niels Provos <provos@citi.umich.edu>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _EVRPC_H_
#define _EVRPC_H_
struct evbuffer;
struct evrpc_req_generic;
/* Encapsulates a request */
struct evrpc {
TAILQ_ENTRY(evrpc) next;
/* the URI at which the request handler lives */
const char* uri;
/* creates a new request structure */
void *(*request_new)(void);
/* creates a new request structure */
void (*request_free)(void *);
/* 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);
/* creates a new reply structure */
void (*reply_free)(void *);
/* verifies that the reply is valid */
int (*reply_complete)(void *);
/* marshals the reply into a buffer */
void (*reply_marshal)(struct evbuffer*, void *);
/* the callback invoked for each received rpc */
void (*cb)(struct evrpc_req_generic *, void *);
void *cb_arg;
};
#define EVRPC_STRUCT(rpcname) struct evrpc_req__##rpcname
struct evhttp_request;
/* We alias the RPC specific structs to this voided one */
struct evrpc_req_generic {
/* the unmarshaled request object */
void *request;
/* the empty reply object that needs to be filled in */
void *reply;
/*
* the static structure for this rpc; that can be used to
* automatically unmarshal and marshal the http buffers.
*/
struct evrpc* rpc;
/*
* the http request structure on which we need to answer.
*/
struct evhttp_request* http_req;
/*
* callback to reply and finish answering this rpc
*/
void (*done)(struct evrpc_req_generic* rpc);
};
#define EVRPC_DEFINE(rpcname, reqstruct, rplystruct) \
EVRPC_STRUCT(rpcname) { \
struct reqstruct* request; \
struct rplystruct* reply; \
struct evrpc* rpc; \
void (*done)(struct evrpc* rpc, void *request, void *reply); \
}
/*
* EVRPC_REQUEST_DONE is used to answer a request; the reply is expected
* to have been filled in. The request and reply pointers become invalid
* 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); \
}
/* Takes a request object and fills it in with the right magic */
#define EVRPC_REGISTER_OBJECT(rpc, name, request, reply) \
do { \
(rpc)->uri = strdup(name); \
if ((rpc)->uri == NULL) \
event_err(1, "failed to register object"); \
(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; \
(rpc)->reply_marshal = (void (*)(struct evbuffer*, void *))reply##_marshal; \
} while(0)
struct evrpc_base;
struct evhttp;
/* functions to start up the rpc system */
struct evrpc_base *evrpc_init(struct evhttp *server);
/* this macro is used to register RPCs with the HTTP Server */
#define EVRPC_REGISTER(base, name, request, reply, callback, cbarg) \
do { \
struct evrpc* rpc = calloc(1, sizeof(struct evrpc)); \
EVRPC_REGISTER_OBJECT(rpc, name, request, reply); \
evrpc_register_rpc(base, rpc, \
(void (*)(struct evrpc_req_generic*, void *))callback, cbarg); \
} while (0)
int evrpc_register_rpc(struct evrpc_base *, struct evrpc *,
void (*)(struct evrpc_req_generic*, void *), void *);
#endif /* _EVRPC_H_ */

View File

@ -50,45 +50,6 @@ struct evhttp_connection {
void *cb_arg;
};
enum evhttp_request_kind { EVHTTP_REQUEST, EVHTTP_RESPONSE };
struct evhttp_request {
TAILQ_ENTRY(evhttp_request) next;
/* the connection object that this request belongs to */
struct evhttp_connection *evcon;
int flags;
#define EVHTTP_REQ_OWN_CONNECTION 0x0001
struct evkeyvalq *input_headers;
struct evkeyvalq *output_headers;
/* xxx: do we still need these? */
char *remote_host;
u_short remote_port;
enum evhttp_request_kind kind;
enum evhttp_cmd_type type;
char *uri; /* uri after HTTP request was parsed */
char major; /* HTTP Major number */
char minor; /* HTTP Minor number */
int got_firstline;
int response_code; /* HTTP Response code */
char *response_code_line; /* Readable response */
struct evbuffer *input_buffer; /* read data */
int ntoread;
struct evbuffer *output_buffer; /* outgoing post or data */
/* Callback */
void (*cb)(struct evhttp_request *, void *);
void *cb_arg;
};
struct evhttp_cb {
TAILQ_ENTRY(evhttp_cb) next;

2
http.c
View File

@ -1145,7 +1145,7 @@ evhttp_send(struct evhttp_request *req, struct evbuffer *databuf)
assert(TAILQ_FIRST(&evcon->requests) == req);
/* xxx: not sure if we really should expost the data buffer this way */
/* xxx: not sure if we really should expose the data buffer this way */
evbuffer_add_buffer(req->output_buffer, databuf);
/* Adds headers to the response */

View File

@ -14,6 +14,7 @@ test_eof_SOURCES = test-eof.c
test_weof_SOURCES = test-weof.c
test_time_SOURCES = test-time.c
regress_SOURCES = regress.c regress.h regress_http.c regress_dns.c \
regress_rpc.c \
regress.gen.c regress.gen.h
bench_SOURCES = bench.c

View File

@ -884,6 +884,8 @@ main (int argc, char **argv)
http_suite();
rpc_suite();
dns_suite();
test_simpleread();

View File

@ -34,6 +34,8 @@ extern "C" {
void http_suite(void);
void http_basic_test(void);
void rpc_suite(void);
void dns_suite(void);
#ifdef __cplusplus

View File

@ -65,7 +65,7 @@ static struct evhttp *http;
void http_basic_cb(struct evhttp_request *req, void *arg);
void http_post_cb(struct evhttp_request *req, void *arg);
struct evhttp *
static struct evhttp *
http_setup(short *pport)
{
int i;