]> granicus.if.org Git - libevent/commitdiff
allow association of meta data with RPC requests for hook processing
authorNiels Provos <provos@gmail.com>
Sat, 29 Dec 2007 22:45:54 +0000 (22:45 +0000)
committerNiels Provos <provos@gmail.com>
Sat, 29 Dec 2007 22:45:54 +0000 (22:45 +0000)
svn:r622

ChangeLog
evrpc-internal.h
evrpc.c
evrpc.h
test/regress_rpc.c

index bdcb6bab67e91f498255a989b111a3585edda12f..0a1293f73e3569801e9b22c3154bd49c11acd52f 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -32,6 +32,7 @@ Changes in current version:
  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.
index 74183530901b70edae8063527f1877380bd23ebd..bffe8a2c48432baea96d7770f7bdea7f5fe43658 100644 (file)
@@ -36,7 +36,9 @@ struct evrpc;
 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;
@@ -98,4 +100,17 @@ struct evrpc_hook_ctx {
        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_ */
diff --git a/evrpc.c b/evrpc.c
index 0fc4f5e04aa76dcf5ea787984097efec98d290bc..9fce691b0ac3ced9d39efe8d8f78551202a65dce 100644 (file)
--- a/evrpc.c
+++ b/evrpc.c
@@ -365,6 +365,8 @@ evrpc_reqstate_free(struct evrpc_req_generic* rpc_state)
        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)
@@ -477,6 +479,8 @@ evrpc_pool_new(struct event_base *base)
 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);
 }
@@ -749,6 +753,41 @@ evrpc_make_request(struct evrpc_request_wrapper *ctx)
        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);
 
@@ -864,3 +903,66 @@ evrpc_request_timeout(evutil_socket_t fd, short what, void *arg)
 
        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);
+}
diff --git a/evrpc.h b/evrpc.h
index a1dd02aadeff1b4fc54f3a0fd022d80d92e0a9a7..4f9a997072af4b54eed43d706fcfa662e7937e21 100644 (file)
--- a/evrpc.h
+++ b/evrpc.h
@@ -114,9 +114,16 @@ struct evrpc {
 
 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;
 
@@ -158,6 +165,7 @@ struct evrpc_req_generic {
  */
 #define EVRPC_HEADER(rpcname, reqstruct, rplystruct) \
 EVRPC_STRUCT(rpcname) {        \
+       struct evrpc_meta_list *meta_data; \
        struct reqstruct* request; \
        struct rplystruct* reply; \
        struct evrpc* rpc; \
@@ -172,6 +180,18 @@ int evrpc_send_request_##rpcname(struct evrpc_pool *, \
        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
@@ -190,25 +210,15 @@ int evrpc_send_request_##rpcname(struct evrpc_pool *pool, \
     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));                         \
@@ -325,7 +335,6 @@ int evrpc_unregister_rpc(struct evrpc_base *base, const char *name);
  * Client-side RPC support
  */
 
-struct evrpc_pool;
 struct evhttp_connection;
 
 /** 
@@ -344,6 +353,12 @@ struct evrpc_status {
 };
 
 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 */
@@ -500,6 +515,33 @@ int evrpc_remove_hook(void *vbase,
 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
index 1a6d2fd793a5385d9ad88faf07fa12ef19de5d23..68cf0c3bf6f50ac5c915c04d676c5f63d7488123 100644 (file)
@@ -417,7 +417,17 @@ rpc_hook_add_header(void *ctx, struct evhttp_request *req,
                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
@@ -425,12 +435,20 @@ rpc_hook_remove_header(void *ctx, struct evhttp_request *req,
     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
@@ -457,6 +475,7 @@ rpc_basic_client(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 */