1 /*
2  * Copyright (c) 2006-2007 Niels Provos <provos@citi.umich.edu>
3  * Copyright (c) 2007-2011 Niels Provos and Nick Mathewson
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 
28 /** @file rpc.h
29  *
30  * This header files provides basic support for an RPC server and client.
31  *
32  * To support RPCs in a server, every supported RPC command needs to be
33  * defined and registered.
34  *
35  * EVRPC_HEADER(SendCommand, Request, Reply);
36  *
37  * SendCommand is the name of the RPC command.
38  * Request is the name of a structure generated by event_rpcgen.py.
39  *   It contains all parameters relating to the SendCommand RPC.  The
40  *   server needs to fill in the Reply structure.
41  * Reply is the name of a structure generated by event_rpcgen.py.  It
42  *   contains the answer to the RPC.
43  *
44  * To register an RPC with an HTTP server, you need to first create an RPC
45  * base with:
46  *
47  *  evrpc_base* base = evrpc_init(http);
48  *
49  * A specific RPC can then be registered with
50  *
51  * EVRPC_REGISTER(base, SendCommand, Request, Reply,  FunctionCB, arg);
52  *
53  * when the server receives an appropriately formatted RPC, the user callback
54  * is invoked.   The callback needs to fill in the reply structure.
55  *
56  * void FunctionCB(EVRPC_STRUCT(SendCommand)* rpc, void* arg);
57  *
58  * To send the reply, call EVRPC_REQUEST_DONE(rpc);
59  *
60  * See the regression test for an example.
61  */
62 module deimos.event2.rpc;
63 
64 import deimos.event2._d_util;
65 
66 extern (C):
67 nothrow:
68 
69 /**
70    Determines if the member has been set in the message
71 
72    @param msg the message to inspect
73    @param member the member variable to test for presences
74    @return 1 if it's present or 0 otherwise.
75 */
76 int EVTAG_HAS(string member, T)(T msg) {
77 	return mixin("*msg." ~ member ~ "_set") == 1;
78 }
79 
80 /**
81    Assigns a value to the member in the message.
82 
83    @param msg the message to which to assign a value
84    @param member the name of the member variable
85    @param value the value to assign
86 */
87 void EVTAG_ASSIGN(string member, T, U)(T msg, U value) {
88 	mixin("*msg.base." ~ member ~ "_assign")(msg, value);
89 }
90 
91 /**
92    Assigns a value to the member in the message.
93 
94    @param msg the message to which to assign a value
95    @param member the name of the member variable
96    @param value the value to assign
97    @param len the length of the value
98 */
99 void EVTAG_ASSIGN_WITH_LEN(string member, T, U, V)(T msg, U value, V len) {
100 	mixin("*msg.base." ~ member ~ "_assign")(msg, value, len);
101 }
102 
103 /**
104    Returns the value for a member.
105 
106    @param msg the message from which to get the value
107    @param member the name of the member variable
108    @param pvalue a pointer to the variable to hold the value
109    @return 0 on success, -1 otherwise.
110 */
111 auto EVTAG_GET(string member, T, U)(T msg, U pvalue) {
112 	return mixin("*msg.base." ~ member ~ "_get")(msg, pvalue);
113 }
114 
115 /**
116    Returns the value for a member.
117 
118    @param msg the message from which to get the value
119    @param member the name of the member variable
120    @param pvalue a pointer to the variable to hold the value
121    @param plen a pointer to the length of the value
122    @return 0 on success, -1 otherwise.
123 */
124 auto EVTAG_GET(string member, T, U, V)(T msg, U pvalue, V plen) {
125 	return mixin("*msg.base." ~ member ~ "_get")(msg, pvalue, plen);
126 }
127 
128 /**
129    Adds a value to an array.
130 */
131 auto EVTAG_ARRAY_ADD_VALUE(string member, T, U)(T msg, U value) {
132 	return mixin("*msg.base." ~ member ~ "_add")(msg, value);
133 }
134 /**
135    Allocates a new entry in the array and returns it.
136 */
137 auto EVTAG_ARRAY_ADD(string member, T)(T msg) {
138 	return mixin("*msg.base." ~ member ~ "_add")(msg);
139 }
140 /**
141    Gets a variable at the specified offset from the array.
142 */
143 auto EVTAG_ARRAY_ADD_VALUE(string member, T, U, V)(T msg, U offset, V pvalue) {
144 	return mixin("*msg.base." ~ member ~ "_get")(msg, offset, pvalue);
145 }
146 /**
147    Returns the number of entries in the array.
148 */
149 auto EVTAG_ARRAY_LEN(string member, T)(T msg) {
150 	return mixin("msg." ~ member ~ "_length")(msg);
151 }
152 
153 
154 static import deimos.event2._opaque_structs;
155 
156 alias event_base = deimos.event2._opaque_structs.event_base;
157 alias evrpc_req_generic = deimos.event2._opaque_structs.evrpc_req_generic;
158 alias evrpc_request_wrapper = deimos.event2._opaque_structs.evrpc_request_wrapper;
159 alias evrpc = deimos.event2._opaque_structs.evrpc;
160 
161 /** The type of a specific RPC Message
162  *
163  * @param rpcname the name of the RPC message
164  */
165 template EVRPC_STRUCT(string rpcname) {
166 	enum EVRPC_STRUCT = "evrpc_req__" ~ rpcname;
167 }
168 
169 alias evhttp_request = deimos.event2._opaque_structs.evhttp_request;
170 alias evrpc_status = deimos.event2._opaque_structs.evrpc_status;
171 alias evrpc_hook_meta = deimos.event2._opaque_structs.evrpc_hook_meta;
172 
173 /** Creates the definitions and prototypes for an RPC
174  *
175  * You need to use EVRPC_HEADER to create structures and function prototypes
176  * needed by the server and client implementation.  The structures have to be
177  * defined in an .rpc file and converted to source code via event_rpcgen.py
178  *
179  * @param rpcname the name of the RPC
180  * @param reqthe name of the RPC request structure
181  * @param replythe name of the RPC reply structure
182  * @see EVRPC_GENERATE()
183  */
184 mixin template EVRPC_HEADER(string rpcname, reqstruct, rplystruct) {
185 	mixin(EVRPC_STRUCT!rpcname ~ q{
186 		evrpc_hook_meta* hook_meta;
187 		reqstruct* request;
188 		rplystruct* reply;
189 		evrpc* rpc;
190 		evhttp_request* http_req;
191 		evbuffer* rpc_data;
192 	} ~ "}");
193 	mixin("int evrpc_send_request_" ~ rpcname ~ q{(evrpc_pool*,
194 	    reqstruct*, rplystruct*,
195 	    ExternC!(void function(evrpc_status*,
196 			reqstruct*, rplystruct*, void* cbarg)) 
197 	    void*)} ~ ";"
198 	);
199 }
200 struct evrpc_pool;
201 
202 /** use EVRPC_GENERATE instead */
203 evrpc_request_wrapper* evrpc_make_request_ctx(
204 	evrpc_pool* pool, void* request, void* reply,
205 	const(char)* rpcname,
206 	ExternC!(void function(evbuffer*, void*)) req_marshal,
207 	ExternC!(void function(void*)) rpl_clear,
208 	ExternC!(int function(void*, evbuffer*)) rpl_unmarshal,
209 	ExternC!(void function(evrpc_status*, void*, void*, void*)) cb,
210 	void* cbarg);
211 
212 /** Creates a context structure that contains rpc specific information.
213  *
214  * EVRPC_MAKE_CTX is used to populate a RPC specific context that
215  * contains information about marshaling the RPC data types.
216  *
217  * @param rpcname the name of the RPC
218  * @param reqthe name of the RPC request structure
219  * @param replythe name of the RPC reply structure
220  * @param pool the evrpc_pool over which to make the request
221  * @param request a pointer to the RPC request structure object
222  * @param reply a pointer to the RPC reply structure object
223  * @param cb the callback function to call when the RPC has completed
224  * @param cbarg the argument to supply to the callback
225  */
226 auto EVRPC_MAKE_CTX(string rpcname, string reqstruct, string rplystruct, U, V)(
227     evrpc_pool* pool, void* request, void* reply, U cb, V cbarg) {
228 	return evrpc_make_request_ctx(pool, request, reply,
229 	    rpcname,
230 	    cast(ExternC!(void function(evbuffer*, void*)) )mixin(reqstruct ~ "_marshal"),
231 	    cast(ExternC!(void function(void*)) )mixin(rplystruct ~ "_clear"),
232 	    cast(ExternC!(int function(void*, evbuffer*)) )mixin(rplystruct ~ "_unmarshal"),
233 	    cast(ExternC!(void function(evrpc_status*, void*, void*, void*)) )cb,
234 	    cbarg);
235 }
236 
237 /** Generates the code for receiving and sending an RPC message
238  *
239  * EVRPC_GENERATE is used to create the code corresponding to sending
240  * and receiving a particular RPC message
241  *
242  * @param rpcname the name of the RPC
243  * @param reqthe name of the RPC request structure
244  * @param replythe name of the RPC reply structure
245  * @see EVRPC_HEADER()
246  */
247 mixin template EVRPC_GENERATE(string rpcname, string reqstruct, string rplystruct) {
248 	mixin("int evrpc_send_request_" ~ rpcname ~ "(evrpc_pool* pool,
249 	    reqstruct* request, " ~ rplystruct ~ "* reply,
250 	    void ExternC!(function(evrpc_status*, " ~ req ~ "*, " ~ rplystruct ~ "*, void* cbarg)) cb,
251 	    void* cbarg) {
252 	return evrpc_send_request_generic(pool, request, reply,
253 	    (ExternC!(void function(evrpc_status*, void*, void*, void*)) )cb,
254 	    cbarg,
255 	    " ~ rpcname ~ ",
256 	    (ExternC!(void function(evbuffer*, void*)) )" ~ reqstruct ~ "_marshal,
257 	    (ExternC!(void function(void*)) )" ~ rplystruct ~ "_clear,
258 	    (ExternC!(int function(void*, evbuffer*)) )" ~ rplystruct ~ "_unmarshal);
259 	}");
260 }
261 
262 
263 /** Provides access to the HTTP request object underlying an RPC
264  *
265  * Access to the underlying http object; can be used to look at headers or
266  * for getting the remote ip address
267  *
268  * @param rpc_req the rpc request structure provided to the server callback
269  * @return an evhttp_request object that can be inspected for
270  * HTTP headers or sender information.
271  */
272 auto EVRPC_REQUEST_HTTP(T)(T rpc_req) {
273 	return rpc_req.http_req;
274 }
275 
276 /** completes the server response to an rpc request */
277 void evrpc_request_done(evrpc_req_generic* req);
278 
279 /** accessors for request and reply */
280 void* evrpc_get_request(evrpc_req_generic* req);
281 void* evrpc_get_reply(evrpc_req_generic* req);
282 
283 /** Creates the reply to an RPC request
284  *
285  * EVRPC_REQUEST_DONE is used to answer a request; the reply is expected
286  * to have been filled in.  The request and reply pointers become invalid
287  * after this call has finished.
288  *
289  * @param rpc_req the rpc request structure provided to the server callback
290  */
291 void EVRPC_REQUEST_DONE(T)(rpc_req) {
292   evrpc_req_generic* _req = cast(evrpc_req_generic*)(rpc_req);
293   evrpc_request_done(_req);
294 }
295 
296 
297 struct evrpc_base;
298 struct evhttp;
299 
300 /* functions to start up the rpc system */
301 
302 /** Creates a new rpc base from which RPC requests can be received
303  *
304  * @param server a pointer to an existing HTTP server
305  * @return a newly allocated evrpc_base struct
306  * @see evrpc_free()
307  */
308 evrpc_base* evrpc_init(evhttp* server);
309 
310 /**
311  * Frees the evrpc base
312  *
313  * For now, you are responsible for making sure that no rpcs are ongoing.
314  *
315  * @param base the evrpc_base object to be freed
316  * @see evrpc_init
317  */
318 void evrpc_free(evrpc_base* base);
319 
320 /** register RPCs with the HTTP Server
321  *
322  * registers a new RPC with the HTTP server, each RPC needs to have
323  * a unique name under which it can be identified.
324  *
325  * @param base the evrpc_base structure in which the RPC should be
326  *  registered.
327  * @param name the name of the RPC
328  * @param request the name of the RPC request structure
329  * @param reply the name of the RPC reply structure
330  * @param callback the callback that should be invoked when the RPC
331  * is received.  The callback has the following prototype
332  *  void (*callback)(EVRPC_STRUCT(Message)* rpc, void* arg)
333  * @param cbarg an additional parameter that can be passed to the callback.
334  *  The parameter can be used to carry around state.
335  */
336 template EVRPC_REGISTER(string base, string name, string request, string reply,
337 	string callback, string cbarg
338 ) {
339 	enum EVRPC_REGISTER = "
340 	evrpc_register_generic(" ~ base ~ ", `" ~ name ~ "`,
341 	    (ExternC!(void function(evrpc_req_generic*, void*)) )" ~ callback ~ ", " ~ cbarg ~ ",
342 	    (ExternC!(void* function(void*)) )" ~ request ~ "_new, NULL,
343 	    (ExternC!(void function(void*)) )" ~ request ~ "_free,
344 	    (ExternC!(int function(void*, evbuffer*)) )" ~ request ~ "_unmarshal,
345 	    (ExternC!(void* function(void*)) )" ~ reply ~ "_new, NULL,
346 	    (ExternC!(void function(void*)) )" ~ reply ~ "_free,
347 	    (ExternC!(int function(void*)) )" ~ reply ~ "_complete,
348 	    (ExternC!(void function(evbuffer*, void*)) )" ~ reply ~ "_marshal)
349 	";
350 }
351 
352 /**
353    Low level function for registering an RPC with a server.
354 
355    Use EVRPC_REGISTER() instead.
356 
357    @see EVRPC_REGISTER()
358 */
359 int evrpc_register_rpc(evrpc_base*, evrpc*,
360     ExternC!(void function(evrpc_req_generic*, void*)) , void*);
361 
362 /**
363  * Unregisters an already registered RPC
364  *
365  * @param base the evrpc_base object from which to unregister an RPC
366  * @param name the name of the rpc to unregister
367  * @return -1 on error or 0 when successful.
368  * @see EVRPC_REGISTER()
369  */
370 alias evrpc_unregister_rpc EVRPC_UNREGISTER;
371 
372 int evrpc_unregister_rpc(evrpc_base* base, const(char)* name);
373 
374 /*
375  * Client-side RPC support
376  */
377 
378 struct evhttp_connection;
379 
380 /** launches an RPC and sends it to the server
381  *
382  * EVRPC_MAKE_REQUEST() is used by the client to send an RPC to the server.
383  *
384  * @param name the name of the RPC
385  * @param pool the evrpc_pool that contains the connection objects over which
386  *  the request should be sent.
387  * @param request a pointer to the RPC request structure - it contains the
388  *  data to be sent to the server.
389  * @param reply a pointer to the RPC reply structure.  It is going to be filled
390  *  if the request was answered successfully
391  * @param cb the callback to invoke when the RPC request has been answered
392  * @param cbarg an additional argument to be passed to the client
393  * @return 0 on success, -1 on failure
394  */
395 template ENRPC_MAKE_REQUEST(string name, string pool, string request,
396 	string reply, string cb, string cbarg
397 ) {
398 	enum EVRPC_MAKE_REQUEST = "evrpc_send_request_" ~ name ~ "((" ~ pool ~
399 		"), (" ~ request ~" ), (" ~ reply ~ "), (" ~ cb ~ "), (" ~ cbarg ~ "))";
400 }
401 
402 /**
403    Makes an RPC request based on the provided context.
404 
405    This is a low-level function and should not be used directly
406    unless a custom context object is provided.  Use EVRPC_MAKE_REQUEST()
407    instead.
408 
409    @param ctx a context from EVRPC_MAKE_CTX()
410    @returns 0 on success, -1 otherwise.
411    @see EVRPC_MAKE_REQUEST(), EVRPC_MAKE_CTX()
412 */
413 int evrpc_make_request(evrpc_request_wrapper* ctx);
414 
415 /** creates an rpc connection pool
416  *
417  * a pool has a number of connections associated with it.
418  * rpc requests are always made via a pool.
419  *
420  * @param base a pointer to an event_based object; can be left NULL
421  *  in singled-threaded applications
422  * @return a newly allocated evrpc_pool object
423  * @see evrpc_pool_free()
424  */
425 evrpc_pool* evrpc_pool_new(event_base* base);
426 /** frees an rpc connection pool
427  *
428  * @param pool a pointer to an evrpc_pool allocated via evrpc_pool_new()
429  * @see evrpc_pool_new()
430  */
431 void evrpc_pool_free(evrpc_pool* pool);
432 
433 /**
434  * Adds a connection over which rpc can be dispatched to the pool.
435  *
436  * The connection object must have been newly created.
437  *
438  * @param pool the pool to which to add the connection
439  * @param evcon the connection to add to the pool.
440  */
441 void evrpc_pool_add_connection(evrpc_pool* pool,
442     evhttp_connection* evcon);
443 
444 /**
445  * Removes a connection from the pool.
446  *
447  * The connection object must have been newly created.
448  *
449  * @param pool the pool from which to remove the connection
450  * @param evcon the connection to remove from the pool.
451  */
452 void evrpc_pool_remove_connection(evrpc_pool* pool,
453     evhttp_connection* evcon);
454 
455 /**
456  * Sets the timeout in secs after which a request has to complete.  The
457  * RPC is completely aborted if it does not complete by then.  Setting
458  * the timeout to 0 means that it never timeouts and can be used to
459  * implement callback type RPCs.
460  *
461  * Any connection already in the pool will be updated with the new
462  * timeout.  Connections added to the pool after set_timeout has be
463  * called receive the pool timeout only if no timeout has been set
464  * for the connection itself.
465  *
466  * @param pool a pointer to a evrpc_pool object
467  * @param timeout_in_secs the number of seconds after which a request should
468  *  timeout and a failure be returned to the callback.
469  */
470 void evrpc_pool_set_timeout(evrpc_pool* pool, int timeout_in_secs);
471 
472 /**
473  * Hooks for changing the input and output of RPCs; this can be used to
474  * implement compression, authentication, encryption, ...
475  */
476 
477 enum EVRPC_HOOK_TYPE {
478 	EVRPC_INPUT,		/**< apply the function to an input hook */
479 	EVRPC_OUTPUT		/**< apply the function to an output hook */
480 }
481 
482 /** Deprecated alias for EVRPC_INPUT.  Not available on windows, where it
483  * conflicts with platform headers. */
484 enum INPUT = EVRPC_HOOK_TYPE.EVRPC_INPUT;
485 /** Deprecated alias for EVRPC_OUTPUT.  Not available on windows, where it
486  * conflicts with platform headers. */
487 enum OUTPUT = EVRPC_HOOK_TYPE.EVRPC_OUTPUT;
488 
489 /**
490  * Return value from hook processing functions
491  */
492 
493 enum EVRPC_HOOK_RESULT {
494 	EVRPC_TERMINATE = -1,	/**< indicates the rpc should be terminated */
495 	EVRPC_CONTINUE = 0,	/**< continue processing the rpc */
496 	EVRPC_PAUSE = 1		/**< pause processing request until resumed */
497 };
498 
499 struct evbuffer;
500 /** adds a processing hook to either an rpc base or rpc pool
501  *
502  * If a hook returns TERMINATE, the processing is aborted. On CONTINUE,
503  * the request is immediately processed after the hook returns.  If the
504  * hook returns PAUSE, request processing stops until evrpc_resume_request()
505  * has been called.
506  *
507  * The add functions return handles that can be used for removing hooks.
508  *
509  * @param vbase a pointer to either evrpc_base or struct evrpc_pool
510  * @param hook_type either INPUT or OUTPUT
511  * @param cb the callback to call when the hook is activated
512  * @param cb_arg an additional argument for the callback
513  * @return a handle to the hook so it can be removed later
514  * @see evrpc_remove_hook()
515  */
516 void* evrpc_add_hook(void* vbase,
517     EVRPC_HOOK_TYPE hook_type,
518     ExternC!(int function(void*, evhttp_request*, evbuffer*, void*)) cb,
519     void* cb_arg);
520 
521 /** removes a previously added hook
522  *
523  * @param vbase a pointer to either evrpc_base or struct evrpc_pool
524  * @param hook_type either INPUT or OUTPUT
525  * @param handle a handle returned by evrpc_add_hook()
526  * @return 1 on success or 0 on failure
527  * @see evrpc_add_hook()
528  */
529 int evrpc_remove_hook(void* vbase,
530     EVRPC_HOOK_TYPE hook_type,
531     void* handle);
532 
533 /** resume a paused request
534  *
535  * @param vbase a pointer to either evrpc_base or struct evrpc_pool
536  * @param ctx the context pointer provided to the original hook call
537  */
538 int
539 evrpc_resume_request(void* vbase, void* ctx, EVRPC_HOOK_RESULT res);
540 
541 /** adds meta data to request
542  *
543  * evrpc_hook_add_meta() allows hooks to add meta data to a request. for
544  * a client request, the meta data can be inserted by an outgoing request hook
545  * and retrieved by the incoming request hook.
546  *
547  * @param ctx the context provided to the hook call
548  * @param key a NUL-terminated c-string
549  * @param data the data to be associated with the key
550  * @param data_size the size of the data
551  */
552 void evrpc_hook_add_meta(void* ctx, const(char)* key,
553     const(void)* data, size_t data_size);
554 
555 /** retrieves meta data previously associated
556  *
557  * evrpc_hook_find_meta() can be used to retrieve meta data associated to a
558  * request by a previous hook.
559  * @param ctx the context provided to the hook call
560  * @param key a NUL-terminated c-string
561  * @param data pointer to a data pointer that will contain the retrieved data
562  * @param data_size pointer to the size of the data
563  * @return 0 on success or -1 on failure
564  */
565 int evrpc_hook_find_meta(void* ctx, const(char)* key,
566     void* *data, size_t* data_size);
567 
568 /**
569  * returns the connection object associated with the request
570  *
571  * @param ctx the context provided to the hook call
572  * @return a pointer to the evhttp_connection object
573  */
574 evhttp_connection* evrpc_hook_get_connection(void* ctx);
575 
576 /**
577    Function for sending a generic RPC request.
578 
579    Do not call this function directly, use EVRPC_MAKE_REQUEST() instead.
580 
581    @see EVRPC_MAKE_REQUEST()
582  */
583 int evrpc_send_request_generic(evrpc_pool* pool,
584     void* request, void* reply,
585     ExternC!(void function(evrpc_status*, void*, void*, void*)) cb,
586     void* cb_arg,
587     const(char)* rpcname,
588     ExternC!(void function(evbuffer*, void*)) req_marshal,
589     ExternC!(void function(void*)) rpl_clear,
590     ExternC!(int function(void*, evbuffer*)) rpl_unmarshal);
591 
592 /**
593    Function for registering a generic RPC with the RPC base.
594 
595    Do not call this function directly, use EVRPC_REGISTER() instead.
596 
597    @see EVRPC_REGISTER()
598  */
599 int
600 evrpc_register_generic(evrpc_base* base, const(char)* name,
601     ExternC!(void function(evrpc_req_generic*, void*)) callback, void* cbarg,
602     ExternC!(void* function(void*)) req_new, void* req_new_arg, ExternC!(void function(void*)) req_free,
603     ExternC!(int function(void*, evbuffer*)) req_unmarshal,
604     ExternC!(void* function(void*)) rpl_new, void* rpl_new_arg, ExternC!(void function(void*)) rpl_free,
605     ExternC!(int function(void*)) rpl_complete,
606     ExternC!(void function(evbuffer*, void*)) rpl_marshal);
607 
608 /** accessors for obscure and undocumented functionality */
609 evrpc_pool* evrpc_request_get_pool(evrpc_request_wrapper* ctx);
610 void evrpc_request_set_pool(evrpc_request_wrapper* ctx,
611     evrpc_pool* pool);
612 void evrpc_request_set_cb(evrpc_request_wrapper* ctx,
613     ExternC!(void function(evrpc_status*, void* request, void* reply, void* arg)) cb,
614     void* cb_arg);