o support string arrays in event_rpcgen
o change evrpc hooking to allow pausing of RPCs; this will make it possible for the hook to do some meaning ful work; this is not backwards compatible.
o allow an http request callback to take ownership of a request structure
+ o allow association of meta data with RPC requests for hook processing
Changes in 1.4.0:
o allow \r or \n individually to separate HTTP headers instead of the standard "\r\n"; from Charles Kerr.
struct evrpc_hook {
TAILQ_ENTRY(evrpc_hook) (next);
- /* returns -1; if the rpc should be aborted, is allowed to rewrite */
+ /* returns EVRPC_TERMINATE; if the rpc should be aborted.
+ * a hook is is allowed to rewrite the evbuffer
+ */
int (*process)(void *, struct evhttp_request *,
struct evbuffer *, void *);
void *process_arg;
void (*cb)(void *, enum EVRPC_HOOK_RESULT);
};
+struct evrpc_meta {
+ TAILQ_ENTRY(evrpc_meta) (next);
+ char *key;
+
+ void *data;
+ size_t data_size;
+};
+
+TAILQ_HEAD(evrpc_meta_list, evrpc_meta);
+
+/* frees the meta data associated with a request */
+static void evrpc_meta_data_free(struct evrpc_meta_list *meta_data);
+
#endif /* _EVRPC_INTERNAL_H_ */
rpc = rpc_state->rpc;
/* clean up all memory */
+ if (rpc_state->meta_data != NULL)
+ evrpc_meta_data_free(rpc_state->meta_data);
if (rpc_state->request != NULL)
rpc->request_free(rpc_state->request);
if (rpc_state->reply != NULL)
static void
evrpc_request_wrapper_free(struct evrpc_request_wrapper *request)
{
+ if (request->meta_data != NULL)
+ evrpc_meta_data_free(request->meta_data);
event_free(request->name);
event_free(request);
}
return (0);
}
+
+struct evrpc_request_wrapper *
+evrpc_send_request_generic(
+ struct evrpc_pool *pool, void *request, void *reply,
+ const char *rpcname,
+ void (*req_marshal)(struct evbuffer*, void *),
+ void (*rpl_clear)(void *),
+ int (*rpl_unmarshal)(void *, struct evbuffer *),
+ void (*cb)(struct evrpc_status *, void *, void *, void *),
+ void *cbarg)
+{
+ struct evrpc_request_wrapper *ctx = (struct evrpc_request_wrapper *)
+ event_malloc(sizeof(struct evrpc_request_wrapper));
+ if (ctx == NULL)
+ return (NULL);
+
+ ctx->pool = pool;
+ ctx->meta_data = NULL;
+ ctx->evcon = NULL;
+ ctx->name = event_strdup(rpcname);
+ if (ctx->name == NULL) {
+ event_free(ctx);
+ return (NULL);
+ }
+ ctx->cb = cb;
+ ctx->cb_arg = cbarg;
+ ctx->request = request;
+ ctx->reply = reply;
+ ctx->request_marshal = req_marshal;
+ ctx->reply_clear = rpl_clear;
+ ctx->reply_unmarshal = rpl_unmarshal;
+
+ return (ctx);
+}
+
static void
evrpc_reply_done_closure(void *, enum EVRPC_HOOK_RESULT);
evhttp_connection_fail(evcon, EVCON_HTTP_TIMEOUT);
}
+
+/*
+ * frees potential meta data associated with a request.
+ */
+
+static void
+evrpc_meta_data_free(struct evrpc_meta_list *meta_data)
+{
+ struct evrpc_meta *entry;
+ assert(meta_data != NULL);
+
+ while ((entry = TAILQ_FIRST(meta_data)) != NULL) {
+ TAILQ_REMOVE(meta_data, entry, next);
+ event_free(entry->key);
+ event_free(entry->data);
+ event_free(entry);
+ }
+
+ event_free(meta_data);
+}
+
+/* adds meta data */
+void
+evrpc_hook_add_meta(void *ctx, const char *key,
+ const void *data, size_t data_size)
+{
+ struct evrpc_request_wrapper *req = ctx;
+ struct evrpc_meta *meta = NULL;
+
+ if (req->meta_data == NULL) {
+ req->meta_data = event_malloc(sizeof(struct evrpc_meta_list));
+ assert(req->meta_data != NULL);
+ TAILQ_INIT(req->meta_data);
+ }
+
+ assert((meta = event_malloc(sizeof(struct evrpc_meta))) != NULL);
+ assert((meta->key = event_strdup(key)) != NULL);
+ meta->data_size = data_size;
+ assert((meta->data = event_malloc(data_size)) != NULL);
+ memcpy(meta->data, data, data_size);
+
+ TAILQ_INSERT_TAIL(req->meta_data, meta, next);
+}
+
+int
+evrpc_hook_find_meta(void *ctx, const char *key, void **data, size_t *data_size)
+{
+ struct evrpc_request_wrapper *req = ctx;
+ struct evrpc_meta *meta = NULL;
+
+ if (req->meta_data == NULL)
+ return (-1);
+
+ TAILQ_FOREACH(meta, req->meta_data, next) {
+ if (strcmp(meta->key, key) == 0) {
+ *data = meta->data;
+ *data_size = meta->data_size;
+ return (0);
+ }
+ }
+
+ return (-1);
+}
struct evhttp_request;
struct evrpc_status;
+struct evrpc_meta_list;
/* We alias the RPC specific structs to this voided one */
struct evrpc_req_generic {
+ /*
+ * allows association of meta data via hooks - needs to be
+ * synchronized with evrpc_request_wrapper
+ */
+ struct evrpc_meta_list *meta_data;
+
/* the unmarshaled request object */
void *request;
*/
#define EVRPC_HEADER(rpcname, reqstruct, rplystruct) \
EVRPC_STRUCT(rpcname) { \
+ struct evrpc_meta_list *meta_data; \
struct reqstruct* request; \
struct rplystruct* reply; \
struct evrpc* rpc; \
struct reqstruct *, struct rplystruct *, void *cbarg), \
void *);
+struct evrpc_pool;
+
+/** use EVRPC_GENERATE instead */
+struct evrpc_request_wrapper *evrpc_send_request_generic(
+ struct evrpc_pool *pool, void *request, void *reply,
+ const char *rpcname,
+ void (*req_marshal)(struct evbuffer*, void *),
+ void (*rpl_clear)(void *),
+ int (*rpl_unmarshal)(void *, struct evbuffer *),
+ void (*cb)(struct evrpc_status *, void *, void *, void *),
+ void *cbarg);
+
/** Generates the code for receiving and sending an RPC message
*
* EVRPC_GENERATE is used to create the code corresponding to sending
void *cbarg) { \
struct evrpc_status status; \
struct evrpc_request_wrapper *ctx; \
- ctx = (struct evrpc_request_wrapper *) \
- malloc(sizeof(struct evrpc_request_wrapper)); \
+ ctx = evrpc_send_request_generic(pool, request, reply, \
+ #rpcname, \
+ (void (*)(struct evbuffer *, void *))reqstruct##_marshal, \
+ (void (*)(void *))rplystruct##_clear, \
+ (int (*)(void *, struct evbuffer *))rplystruct##_unmarshal, \
+ (void (*)(struct evrpc_status *, void *, void *, void *))cb, \
+ cbarg); \
if (ctx == NULL) \
goto error; \
- ctx->pool = pool; \
- ctx->evcon = NULL; \
- ctx->name = strdup(#rpcname); \
- if (ctx->name == NULL) { \
- free(ctx); \
- goto error; \
- } \
- ctx->cb = (void (*)(struct evrpc_status *, \
- void *, void *, void *))cb; \
- ctx->cb_arg = cbarg; \
- ctx->request = (void *)request; \
- ctx->reply = (void *)reply; \
- ctx->request_marshal = (void (*)(struct evbuffer *, void *))reqstruct##_marshal; \
- ctx->reply_clear = (void (*)(void *))rplystruct##_clear; \
- ctx->reply_unmarshal = (int (*)(void *, struct evbuffer *))rplystruct##_unmarshal; \
return (evrpc_make_request(ctx)); \
error: \
memset(&status, 0, sizeof(status)); \
* Client-side RPC support
*/
-struct evrpc_pool;
struct evhttp_connection;
/**
};
struct evrpc_request_wrapper {
+ /*
+ * allows association of meta data via hooks - needs to be
+ * synchronized with evrpc_req_generic.
+ */
+ struct evrpc_meta_list *meta_data;
+
TAILQ_ENTRY(evrpc_request_wrapper) next;
/* pool on which this rpc request is being made */
int
evrpc_resume_request(void *vbase, void *ctx, enum EVRPC_HOOK_RESULT res);
+/** adds meta data to request
+ *
+ * evrpc_hook_add_meta() allows hooks to add meta data to a request. for
+ * a client requet, the meta data can be inserted by an outgoing request hook
+ * and retrieved by the incoming request hook.
+ *
+ * @param ctx the context provided to the hook call
+ * @param key a NUL-terminated c-string
+ * @param data the data to be associated with the key
+ * @param data_size the size of the data
+ */
+void evrpc_hook_add_meta(void *ctx, const char *key,
+ const void *data, size_t data_size);
+
+/** retrieves meta data previously associated
+ *
+ * evrpc_hook_find_meta() can be used to retrieve meta data associated to a
+ * request by a previous hook.
+ * @param ctx the context provided to the hook call
+ * @param key a NUL-terminated c-string
+ * @param data pointer to a data pointer that will contain the retrieved data
+ * @param data_size pointer tothe size of the data
+ * @return 0 on success or -1 on failure
+ */
+int evrpc_hook_find_meta(void *ctx, const char *key,
+ void **data, size_t *data_size);
+
#ifdef __cplusplus
}
#endif
evhttp_add_header(req->input_headers, "X-Hook", hook_type);
else
evhttp_add_header(req->output_headers, "X-Hook", hook_type);
- return (0);
+
+ return (EVRPC_CONTINUE);
+}
+
+static int
+rpc_hook_add_meta(void *ctx, struct evhttp_request *req,
+ struct evbuffer *evbuf, void *arg)
+{
+ evrpc_hook_add_meta(ctx, "meta", "test", 5);
+
+ return (EVRPC_CONTINUE);
}
static int
struct evbuffer *evbuf, void *arg)
{
const char *header = evhttp_find_header(req->input_headers, "X-Hook");
+ void *data = NULL;
+ size_t data_len = 0;
+
assert(header != NULL);
assert(strcmp(header, arg) == 0);
+
evhttp_remove_header(req->input_headers, "X-Hook");
evhttp_add_header(req->input_headers, "X-Pool-Hook", "ran");
- return (0);
+ assert(evrpc_hook_find_meta(ctx, "meta", &data, &data_len) == 0);
+ assert(data != NULL);
+ assert(data_len == 5);
+
+ return (EVRPC_CONTINUE);
}
static void
pool = rpc_pool_with_connection(port);
+ assert(evrpc_add_hook(pool, OUTPUT, rpc_hook_add_meta, NULL));
assert(evrpc_add_hook(pool, INPUT, rpc_hook_remove_header, (void*)"output"));
/* set up the basic message */