Annotation of embedaddon/libevent/evrpc.h, revision 1.1
1.1 ! misho 1: /*
! 2: * Copyright (c) 2006 Niels Provos <provos@citi.umich.edu>
! 3: * All rights reserved.
! 4: *
! 5: * Redistribution and use in source and binary forms, with or without
! 6: * modification, are permitted provided that the following conditions
! 7: * are met:
! 8: * 1. Redistributions of source code must retain the above copyright
! 9: * notice, this list of conditions and the following disclaimer.
! 10: * 2. Redistributions in binary form must reproduce the above copyright
! 11: * notice, this list of conditions and the following disclaimer in the
! 12: * documentation and/or other materials provided with the distribution.
! 13: * 3. The name of the author may not be used to endorse or promote products
! 14: * derived from this software without specific prior written permission.
! 15: *
! 16: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
! 17: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
! 18: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
! 19: * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
! 20: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
! 21: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
! 22: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
! 23: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
! 24: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
! 25: * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
! 26: */
! 27: #ifndef _EVRPC_H_
! 28: #define _EVRPC_H_
! 29:
! 30: #ifdef __cplusplus
! 31: extern "C" {
! 32: #endif
! 33:
! 34: /** @file evrpc.h
! 35: *
! 36: * This header files provides basic support for an RPC server and client.
! 37: *
! 38: * To support RPCs in a server, every supported RPC command needs to be
! 39: * defined and registered.
! 40: *
! 41: * EVRPC_HEADER(SendCommand, Request, Reply);
! 42: *
! 43: * SendCommand is the name of the RPC command.
! 44: * Request is the name of a structure generated by event_rpcgen.py.
! 45: * It contains all parameters relating to the SendCommand RPC. The
! 46: * server needs to fill in the Reply structure.
! 47: * Reply is the name of a structure generated by event_rpcgen.py. It
! 48: * contains the answer to the RPC.
! 49: *
! 50: * To register an RPC with an HTTP server, you need to first create an RPC
! 51: * base with:
! 52: *
! 53: * struct evrpc_base *base = evrpc_init(http);
! 54: *
! 55: * A specific RPC can then be registered with
! 56: *
! 57: * EVRPC_REGISTER(base, SendCommand, Request, Reply, FunctionCB, arg);
! 58: *
! 59: * when the server receives an appropriately formatted RPC, the user callback
! 60: * is invokved. The callback needs to fill in the reply structure.
! 61: *
! 62: * void FunctionCB(EVRPC_STRUCT(SendCommand)* rpc, void *arg);
! 63: *
! 64: * To send the reply, call EVRPC_REQUEST_DONE(rpc);
! 65: *
! 66: * See the regression test for an example.
! 67: */
! 68:
! 69: struct evbuffer;
! 70: struct event_base;
! 71: struct evrpc_req_generic;
! 72:
! 73: /* Encapsulates a request */
! 74: struct evrpc {
! 75: TAILQ_ENTRY(evrpc) next;
! 76:
! 77: /* the URI at which the request handler lives */
! 78: const char* uri;
! 79:
! 80: /* creates a new request structure */
! 81: void *(*request_new)(void);
! 82:
! 83: /* frees the request structure */
! 84: void (*request_free)(void *);
! 85:
! 86: /* unmarshals the buffer into the proper request structure */
! 87: int (*request_unmarshal)(void *, struct evbuffer *);
! 88:
! 89: /* creates a new reply structure */
! 90: void *(*reply_new)(void);
! 91:
! 92: /* creates a new reply structure */
! 93: void (*reply_free)(void *);
! 94:
! 95: /* verifies that the reply is valid */
! 96: int (*reply_complete)(void *);
! 97:
! 98: /* marshals the reply into a buffer */
! 99: void (*reply_marshal)(struct evbuffer*, void *);
! 100:
! 101: /* the callback invoked for each received rpc */
! 102: void (*cb)(struct evrpc_req_generic *, void *);
! 103: void *cb_arg;
! 104:
! 105: /* reference for further configuration */
! 106: struct evrpc_base *base;
! 107: };
! 108:
! 109: /** The type of a specific RPC Message
! 110: *
! 111: * @param rpcname the name of the RPC message
! 112: */
! 113: #define EVRPC_STRUCT(rpcname) struct evrpc_req__##rpcname
! 114:
! 115: struct evhttp_request;
! 116: struct evrpc_status;
! 117:
! 118: /* We alias the RPC specific structs to this voided one */
! 119: struct evrpc_req_generic {
! 120: /* the unmarshaled request object */
! 121: void *request;
! 122:
! 123: /* the empty reply object that needs to be filled in */
! 124: void *reply;
! 125:
! 126: /*
! 127: * the static structure for this rpc; that can be used to
! 128: * automatically unmarshal and marshal the http buffers.
! 129: */
! 130: struct evrpc *rpc;
! 131:
! 132: /*
! 133: * the http request structure on which we need to answer.
! 134: */
! 135: struct evhttp_request* http_req;
! 136:
! 137: /*
! 138: * callback to reply and finish answering this rpc
! 139: */
! 140: void (*done)(struct evrpc_req_generic* rpc);
! 141: };
! 142:
! 143: /** Creates the definitions and prototypes for an RPC
! 144: *
! 145: * You need to use EVRPC_HEADER to create structures and function prototypes
! 146: * needed by the server and client implementation. The structures have to be
! 147: * defined in an .rpc file and converted to source code via event_rpcgen.py
! 148: *
! 149: * @param rpcname the name of the RPC
! 150: * @param reqstruct the name of the RPC request structure
! 151: * @param replystruct the name of the RPC reply structure
! 152: * @see EVRPC_GENERATE()
! 153: */
! 154: #define EVRPC_HEADER(rpcname, reqstruct, rplystruct) \
! 155: EVRPC_STRUCT(rpcname) { \
! 156: struct reqstruct* request; \
! 157: struct rplystruct* reply; \
! 158: struct evrpc* rpc; \
! 159: struct evhttp_request* http_req; \
! 160: void (*done)(struct evrpc_status *, \
! 161: struct evrpc* rpc, void *request, void *reply); \
! 162: }; \
! 163: int evrpc_send_request_##rpcname(struct evrpc_pool *, \
! 164: struct reqstruct *, struct rplystruct *, \
! 165: void (*)(struct evrpc_status *, \
! 166: struct reqstruct *, struct rplystruct *, void *cbarg), \
! 167: void *);
! 168:
! 169: /** Generates the code for receiving and sending an RPC message
! 170: *
! 171: * EVRPC_GENERATE is used to create the code corresponding to sending
! 172: * and receiving a particular RPC message
! 173: *
! 174: * @param rpcname the name of the RPC
! 175: * @param reqstruct the name of the RPC request structure
! 176: * @param replystruct the name of the RPC reply structure
! 177: * @see EVRPC_HEADER()
! 178: */
! 179: #define EVRPC_GENERATE(rpcname, reqstruct, rplystruct) \
! 180: int evrpc_send_request_##rpcname(struct evrpc_pool *pool, \
! 181: struct reqstruct *request, struct rplystruct *reply, \
! 182: void (*cb)(struct evrpc_status *, \
! 183: struct reqstruct *, struct rplystruct *, void *cbarg), \
! 184: void *cbarg) { \
! 185: struct evrpc_status status; \
! 186: struct evrpc_request_wrapper *ctx; \
! 187: ctx = (struct evrpc_request_wrapper *) \
! 188: malloc(sizeof(struct evrpc_request_wrapper)); \
! 189: if (ctx == NULL) \
! 190: goto error; \
! 191: ctx->pool = pool; \
! 192: ctx->evcon = NULL; \
! 193: ctx->name = strdup(#rpcname); \
! 194: if (ctx->name == NULL) { \
! 195: free(ctx); \
! 196: goto error; \
! 197: } \
! 198: ctx->cb = (void (*)(struct evrpc_status *, \
! 199: void *, void *, void *))cb; \
! 200: ctx->cb_arg = cbarg; \
! 201: ctx->request = (void *)request; \
! 202: ctx->reply = (void *)reply; \
! 203: ctx->request_marshal = (void (*)(struct evbuffer *, void *))reqstruct##_marshal; \
! 204: ctx->reply_clear = (void (*)(void *))rplystruct##_clear; \
! 205: ctx->reply_unmarshal = (int (*)(void *, struct evbuffer *))rplystruct##_unmarshal; \
! 206: return (evrpc_make_request(ctx)); \
! 207: error: \
! 208: memset(&status, 0, sizeof(status)); \
! 209: status.error = EVRPC_STATUS_ERR_UNSTARTED; \
! 210: (*(cb))(&status, request, reply, cbarg); \
! 211: return (-1); \
! 212: }
! 213:
! 214: /** Provides access to the HTTP request object underlying an RPC
! 215: *
! 216: * Access to the underlying http object; can be used to look at headers or
! 217: * for getting the remote ip address
! 218: *
! 219: * @param rpc_req the rpc request structure provided to the server callback
! 220: * @return an struct evhttp_request object that can be inspected for
! 221: * HTTP headers or sender information.
! 222: */
! 223: #define EVRPC_REQUEST_HTTP(rpc_req) (rpc_req)->http_req
! 224:
! 225: /** Creates the reply to an RPC request
! 226: *
! 227: * EVRPC_REQUEST_DONE is used to answer a request; the reply is expected
! 228: * to have been filled in. The request and reply pointers become invalid
! 229: * after this call has finished.
! 230: *
! 231: * @param rpc_req the rpc request structure provided to the server callback
! 232: */
! 233: #define EVRPC_REQUEST_DONE(rpc_req) do { \
! 234: struct evrpc_req_generic *_req = (struct evrpc_req_generic *)(rpc_req); \
! 235: _req->done(_req); \
! 236: } while (0)
! 237:
! 238:
! 239: /* Takes a request object and fills it in with the right magic */
! 240: #define EVRPC_REGISTER_OBJECT(rpc, name, request, reply) \
! 241: do { \
! 242: (rpc)->uri = strdup(#name); \
! 243: if ((rpc)->uri == NULL) { \
! 244: fprintf(stderr, "failed to register object\n"); \
! 245: exit(1); \
! 246: } \
! 247: (rpc)->request_new = (void *(*)(void))request##_new; \
! 248: (rpc)->request_free = (void (*)(void *))request##_free; \
! 249: (rpc)->request_unmarshal = (int (*)(void *, struct evbuffer *))request##_unmarshal; \
! 250: (rpc)->reply_new = (void *(*)(void))reply##_new; \
! 251: (rpc)->reply_free = (void (*)(void *))reply##_free; \
! 252: (rpc)->reply_complete = (int (*)(void *))reply##_complete; \
! 253: (rpc)->reply_marshal = (void (*)(struct evbuffer*, void *))reply##_marshal; \
! 254: } while (0)
! 255:
! 256: struct evrpc_base;
! 257: struct evhttp;
! 258:
! 259: /* functions to start up the rpc system */
! 260:
! 261: /** Creates a new rpc base from which RPC requests can be received
! 262: *
! 263: * @param server a pointer to an existing HTTP server
! 264: * @return a newly allocated evrpc_base struct
! 265: * @see evrpc_free()
! 266: */
! 267: struct evrpc_base *evrpc_init(struct evhttp *server);
! 268:
! 269: /**
! 270: * Frees the evrpc base
! 271: *
! 272: * For now, you are responsible for making sure that no rpcs are ongoing.
! 273: *
! 274: * @param base the evrpc_base object to be freed
! 275: * @see evrpc_init
! 276: */
! 277: void evrpc_free(struct evrpc_base *base);
! 278:
! 279: /** register RPCs with the HTTP Server
! 280: *
! 281: * registers a new RPC with the HTTP server, each RPC needs to have
! 282: * a unique name under which it can be identified.
! 283: *
! 284: * @param base the evrpc_base structure in which the RPC should be
! 285: * registered.
! 286: * @param name the name of the RPC
! 287: * @param request the name of the RPC request structure
! 288: * @param reply the name of the RPC reply structure
! 289: * @param callback the callback that should be invoked when the RPC
! 290: * is received. The callback has the following prototype
! 291: * void (*callback)(EVRPC_STRUCT(Message)* rpc, void *arg)
! 292: * @param cbarg an additional parameter that can be passed to the callback.
! 293: * The parameter can be used to carry around state.
! 294: */
! 295: #define EVRPC_REGISTER(base, name, request, reply, callback, cbarg) \
! 296: do { \
! 297: struct evrpc* rpc = (struct evrpc *)calloc(1, sizeof(struct evrpc)); \
! 298: EVRPC_REGISTER_OBJECT(rpc, name, request, reply); \
! 299: evrpc_register_rpc(base, rpc, \
! 300: (void (*)(struct evrpc_req_generic*, void *))callback, cbarg); \
! 301: } while (0)
! 302:
! 303: int evrpc_register_rpc(struct evrpc_base *, struct evrpc *,
! 304: void (*)(struct evrpc_req_generic*, void *), void *);
! 305:
! 306: /**
! 307: * Unregisters an already registered RPC
! 308: *
! 309: * @param base the evrpc_base object from which to unregister an RPC
! 310: * @param name the name of the rpc to unregister
! 311: * @return -1 on error or 0 when successful.
! 312: * @see EVRPC_REGISTER()
! 313: */
! 314: #define EVRPC_UNREGISTER(base, name) evrpc_unregister_rpc(base, #name)
! 315:
! 316: int evrpc_unregister_rpc(struct evrpc_base *base, const char *name);
! 317:
! 318: /*
! 319: * Client-side RPC support
! 320: */
! 321:
! 322: struct evrpc_pool;
! 323: struct evhttp_connection;
! 324:
! 325: /**
! 326: * provides information about the completed RPC request.
! 327: */
! 328: struct evrpc_status {
! 329: #define EVRPC_STATUS_ERR_NONE 0
! 330: #define EVRPC_STATUS_ERR_TIMEOUT 1
! 331: #define EVRPC_STATUS_ERR_BADPAYLOAD 2
! 332: #define EVRPC_STATUS_ERR_UNSTARTED 3
! 333: #define EVRPC_STATUS_ERR_HOOKABORTED 4
! 334: int error;
! 335:
! 336: /* for looking at headers or other information */
! 337: struct evhttp_request *http_req;
! 338: };
! 339:
! 340: struct evrpc_request_wrapper {
! 341: TAILQ_ENTRY(evrpc_request_wrapper) next;
! 342:
! 343: /* pool on which this rpc request is being made */
! 344: struct evrpc_pool *pool;
! 345:
! 346: /* connection on which the request is being sent */
! 347: struct evhttp_connection *evcon;
! 348:
! 349: /* event for implementing request timeouts */
! 350: struct event ev_timeout;
! 351:
! 352: /* the name of the rpc */
! 353: char *name;
! 354:
! 355: /* callback */
! 356: void (*cb)(struct evrpc_status*, void *request, void *reply, void *arg);
! 357: void *cb_arg;
! 358:
! 359: void *request;
! 360: void *reply;
! 361:
! 362: /* unmarshals the buffer into the proper request structure */
! 363: void (*request_marshal)(struct evbuffer *, void *);
! 364:
! 365: /* removes all stored state in the reply */
! 366: void (*reply_clear)(void *);
! 367:
! 368: /* marshals the reply into a buffer */
! 369: int (*reply_unmarshal)(void *, struct evbuffer*);
! 370: };
! 371:
! 372: /** launches an RPC and sends it to the server
! 373: *
! 374: * EVRPC_MAKE_REQUEST() is used by the client to send an RPC to the server.
! 375: *
! 376: * @param name the name of the RPC
! 377: * @param pool the evrpc_pool that contains the connection objects over which
! 378: * the request should be sent.
! 379: * @param request a pointer to the RPC request structure - it contains the
! 380: * data to be sent to the server.
! 381: * @param reply a pointer to the RPC reply structure. It is going to be filled
! 382: * if the request was answered successfully
! 383: * @param cb the callback to invoke when the RPC request has been answered
! 384: * @param cbarg an additional argument to be passed to the client
! 385: * @return 0 on success, -1 on failure
! 386: */
! 387: #define EVRPC_MAKE_REQUEST(name, pool, request, reply, cb, cbarg) \
! 388: evrpc_send_request_##name(pool, request, reply, cb, cbarg)
! 389:
! 390: int evrpc_make_request(struct evrpc_request_wrapper *);
! 391:
! 392: /** creates an rpc connection pool
! 393: *
! 394: * a pool has a number of connections associated with it.
! 395: * rpc requests are always made via a pool.
! 396: *
! 397: * @param base a pointer to an struct event_based object; can be left NULL
! 398: * in singled-threaded applications
! 399: * @return a newly allocated struct evrpc_pool object
! 400: * @see evrpc_pool_free()
! 401: */
! 402: struct evrpc_pool *evrpc_pool_new(struct event_base *base);
! 403: /** frees an rpc connection pool
! 404: *
! 405: * @param pool a pointer to an evrpc_pool allocated via evrpc_pool_new()
! 406: * @see evrpc_pool_new()
! 407: */
! 408: void evrpc_pool_free(struct evrpc_pool *pool);
! 409: /*
! 410: * adds a connection over which rpc can be dispatched. the connection
! 411: * object must have been newly created.
! 412: */
! 413: void evrpc_pool_add_connection(struct evrpc_pool *,
! 414: struct evhttp_connection *);
! 415:
! 416: /**
! 417: * Sets the timeout in secs after which a request has to complete. The
! 418: * RPC is completely aborted if it does not complete by then. Setting
! 419: * the timeout to 0 means that it never timeouts and can be used to
! 420: * implement callback type RPCs.
! 421: *
! 422: * Any connection already in the pool will be updated with the new
! 423: * timeout. Connections added to the pool after set_timeout has be
! 424: * called receive the pool timeout only if no timeout has been set
! 425: * for the connection itself.
! 426: *
! 427: * @param pool a pointer to a struct evrpc_pool object
! 428: * @param timeout_in_secs the number of seconds after which a request should
! 429: * timeout and a failure be returned to the callback.
! 430: */
! 431: void evrpc_pool_set_timeout(struct evrpc_pool *pool, int timeout_in_secs);
! 432:
! 433: /**
! 434: * Hooks for changing the input and output of RPCs; this can be used to
! 435: * implement compression, authentication, encryption, ...
! 436: */
! 437:
! 438: enum EVRPC_HOOK_TYPE {
! 439: EVRPC_INPUT, /**< apply the function to an input hook */
! 440: EVRPC_OUTPUT /**< apply the function to an output hook */
! 441: };
! 442:
! 443: #ifndef WIN32
! 444: /** Deprecated alias for EVRPC_INPUT. Not available on windows, where it
! 445: * conflicts with platform headers. */
! 446: #define INPUT EVRPC_INPUT
! 447: /** Deprecated alias for EVRPC_OUTPUT. Not available on windows, where it
! 448: * conflicts with platform headers. */
! 449: #define OUTPUT EVRPC_OUTPUT
! 450: #endif
! 451:
! 452: /** adds a processing hook to either an rpc base or rpc pool
! 453: *
! 454: * If a hook returns -1, the processing is aborted.
! 455: *
! 456: * The add functions return handles that can be used for removing hooks.
! 457: *
! 458: * @param vbase a pointer to either struct evrpc_base or struct evrpc_pool
! 459: * @param hook_type either INPUT or OUTPUT
! 460: * @param cb the callback to call when the hook is activated
! 461: * @param cb_arg an additional argument for the callback
! 462: * @return a handle to the hook so it can be removed later
! 463: * @see evrpc_remove_hook()
! 464: */
! 465: void *evrpc_add_hook(void *vbase,
! 466: enum EVRPC_HOOK_TYPE hook_type,
! 467: int (*cb)(struct evhttp_request *, struct evbuffer *, void *),
! 468: void *cb_arg);
! 469:
! 470: /** removes a previously added hook
! 471: *
! 472: * @param vbase a pointer to either struct evrpc_base or struct evrpc_pool
! 473: * @param hook_type either INPUT or OUTPUT
! 474: * @param handle a handle returned by evrpc_add_hook()
! 475: * @return 1 on success or 0 on failure
! 476: * @see evrpc_add_hook()
! 477: */
! 478: int evrpc_remove_hook(void *vbase,
! 479: enum EVRPC_HOOK_TYPE hook_type,
! 480: void *handle);
! 481:
! 482: #ifdef __cplusplus
! 483: }
! 484: #endif
! 485:
! 486: #endif /* _EVRPC_H_ */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>