xref: /libevent-2.1.12/evrpc.c (revision c010069b)
1f554234fSNiels Provos /*
2b85b710cSNick Mathewson  * Copyright (c) 2000-2007 Niels Provos <[email protected]>
3e49e2891SNick Mathewson  * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson
4f554234fSNiels Provos  *
5f554234fSNiels Provos  * Redistribution and use in source and binary forms, with or without
6f554234fSNiels Provos  * modification, are permitted provided that the following conditions
7f554234fSNiels Provos  * are met:
8f554234fSNiels Provos  * 1. Redistributions of source code must retain the above copyright
9f554234fSNiels Provos  *    notice, this list of conditions and the following disclaimer.
10f554234fSNiels Provos  * 2. Redistributions in binary form must reproduce the above copyright
11f554234fSNiels Provos  *    notice, this list of conditions and the following disclaimer in the
12f554234fSNiels Provos  *    documentation and/or other materials provided with the distribution.
13f554234fSNiels Provos  * 3. The name of the author may not be used to endorse or promote products
14f554234fSNiels Provos  *    derived from this software without specific prior written permission.
15f554234fSNiels Provos  *
16f554234fSNiels Provos  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17f554234fSNiels Provos  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18f554234fSNiels Provos  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19f554234fSNiels Provos  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20f554234fSNiels Provos  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21f554234fSNiels Provos  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22f554234fSNiels Provos  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23f554234fSNiels Provos  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24f554234fSNiels Provos  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25f554234fSNiels Provos  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26f554234fSNiels Provos  */
27ec347b92SNick Mathewson #include "event2/event-config.h"
280915ca0aSKevin Bowling #include "evconfig-private.h"
29f554234fSNiels Provos 
309f560bfaSNick Mathewson #ifdef _WIN32
31f554234fSNiels Provos #define WIN32_LEAN_AND_MEAN
32868f10e7SNiels Provos #include <winsock2.h>
337868ab5aSNick Mathewson #include <windows.h>
34f554234fSNiels Provos #undef WIN32_LEAN_AND_MEAN
35f554234fSNiels Provos #endif
36868f10e7SNiels Provos 
37f554234fSNiels Provos #include <sys/types.h>
389f560bfaSNick Mathewson #ifndef _WIN32
39fda1216bSNiels Provos #include <sys/socket.h>
40868f10e7SNiels Provos #endif
4168120d9bSNick Mathewson #ifdef EVENT__HAVE_SYS_TIME_H
42f554234fSNiels Provos #include <sys/time.h>
43f554234fSNiels Provos #endif
44f554234fSNiels Provos #include <sys/queue.h>
45f554234fSNiels Provos #include <stdio.h>
46f554234fSNiels Provos #include <stdlib.h>
479f560bfaSNick Mathewson #ifndef _WIN32
48f554234fSNiels Provos #include <unistd.h>
49f554234fSNiels Provos #endif
50f554234fSNiels Provos #include <errno.h>
51f554234fSNiels Provos #include <signal.h>
52f554234fSNiels Provos #include <string.h>
53f554234fSNiels Provos 
5400ecd1d8SNick Mathewson #include <sys/queue.h>
5500ecd1d8SNick Mathewson 
56ae09ac4aSNick Mathewson #include "event2/event.h"
57ae09ac4aSNick Mathewson #include "event2/event_struct.h"
5800ecd1d8SNick Mathewson #include "event2/rpc.h"
5900ecd1d8SNick Mathewson #include "event2/rpc_struct.h"
60f554234fSNiels Provos #include "evrpc-internal.h"
61de1c4392SNiels Provos #include "event2/http.h"
627defe4cbSNick Mathewson #include "event2/buffer.h"
637defe4cbSNick Mathewson #include "event2/tag.h"
64de1c4392SNiels Provos #include "event2/http_struct.h"
65de1c4392SNiels Provos #include "event2/http_compat.h"
66cd731b77SNick Mathewson #include "event2/util.h"
67cd731b77SNick Mathewson #include "util-internal.h"
68169321c9SNick Mathewson #include "log-internal.h"
697eb250e9SNick Mathewson #include "mm-internal.h"
70f554234fSNiels Provos 
71f554234fSNiels Provos struct evrpc_base *
evrpc_init(struct evhttp * http_server)72f554234fSNiels Provos evrpc_init(struct evhttp *http_server)
73f554234fSNiels Provos {
7449868b61SNick Mathewson 	struct evrpc_base* base = mm_calloc(1, sizeof(struct evrpc_base));
75f554234fSNiels Provos 	if (base == NULL)
76f554234fSNiels Provos 		return (NULL);
77f554234fSNiels Provos 
78c4836d10SNiels Provos 	/* we rely on the tagging sub system */
79c4836d10SNiels Provos 	evtag_init();
80c4836d10SNiels Provos 
81f554234fSNiels Provos 	TAILQ_INIT(&base->registered_rpcs);
8265236aa8SNiels Provos 	TAILQ_INIT(&base->input_hooks);
8365236aa8SNiels Provos 	TAILQ_INIT(&base->output_hooks);
84819d4a33SNiels Provos 
85819d4a33SNiels Provos 	TAILQ_INIT(&base->paused_requests);
86819d4a33SNiels Provos 
87f554234fSNiels Provos 	base->http_server = http_server;
88f554234fSNiels Provos 
89f554234fSNiels Provos 	return (base);
90f554234fSNiels Provos }
91f554234fSNiels Provos 
92c4836d10SNiels Provos void
evrpc_free(struct evrpc_base * base)93c4836d10SNiels Provos evrpc_free(struct evrpc_base *base)
94c4836d10SNiels Provos {
95621a1b29SNiels Provos 	struct evrpc *rpc;
9665236aa8SNiels Provos 	struct evrpc_hook *hook;
978c594168SNiels Provos 	struct evrpc_hook_ctx *pause;
98743f8665SNick Mathewson 	int r;
99c4836d10SNiels Provos 
100621a1b29SNiels Provos 	while ((rpc = TAILQ_FIRST(&base->registered_rpcs)) != NULL) {
101743f8665SNick Mathewson 		r = evrpc_unregister_rpc(base, rpc->uri);
1024b8f02f1SChristophe Fillot 		EVUTIL_ASSERT(r == 0);
103621a1b29SNiels Provos 	}
1048c594168SNiels Provos 	while ((pause = TAILQ_FIRST(&base->paused_requests)) != NULL) {
1058c594168SNiels Provos 		TAILQ_REMOVE(&base->paused_requests, pause, next);
1068c594168SNiels Provos 		mm_free(pause);
1078c594168SNiels Provos 	}
10865236aa8SNiels Provos 	while ((hook = TAILQ_FIRST(&base->input_hooks)) != NULL) {
109743f8665SNick Mathewson 		r = evrpc_remove_hook(base, EVRPC_INPUT, hook);
110743f8665SNick Mathewson 		EVUTIL_ASSERT(r);
11165236aa8SNiels Provos 	}
11265236aa8SNiels Provos 	while ((hook = TAILQ_FIRST(&base->output_hooks)) != NULL) {
113743f8665SNick Mathewson 		r = evrpc_remove_hook(base, EVRPC_OUTPUT, hook);
114743f8665SNick Mathewson 		EVUTIL_ASSERT(r);
11565236aa8SNiels Provos 	}
11649868b61SNick Mathewson 	mm_free(base);
117c4836d10SNiels Provos }
118c4836d10SNiels Provos 
11965236aa8SNiels Provos void *
evrpc_add_hook(void * vbase,enum EVRPC_HOOK_TYPE hook_type,int (* cb)(void *,struct evhttp_request *,struct evbuffer *,void *),void * cb_arg)1201d3a008aSNiels Provos evrpc_add_hook(void *vbase,
12165236aa8SNiels Provos     enum EVRPC_HOOK_TYPE hook_type,
122819d4a33SNiels Provos     int (*cb)(void *, struct evhttp_request *, struct evbuffer *, void *),
12365236aa8SNiels Provos     void *cb_arg)
12465236aa8SNiels Provos {
125cb9da0bfSNick Mathewson 	struct evrpc_hooks_ *base = vbase;
12665236aa8SNiels Provos 	struct evrpc_hook_list *head = NULL;
12765236aa8SNiels Provos 	struct evrpc_hook *hook = NULL;
12865236aa8SNiels Provos 	switch (hook_type) {
1292baaac7fSNick Mathewson 	case EVRPC_INPUT:
1301d3a008aSNiels Provos 		head = &base->in_hooks;
13165236aa8SNiels Provos 		break;
1322baaac7fSNick Mathewson 	case EVRPC_OUTPUT:
1331d3a008aSNiels Provos 		head = &base->out_hooks;
13465236aa8SNiels Provos 		break;
13565236aa8SNiels Provos 	default:
1362e36dbe1SNick Mathewson 		EVUTIL_ASSERT(hook_type == EVRPC_INPUT || hook_type == EVRPC_OUTPUT);
13765236aa8SNiels Provos 	}
13865236aa8SNiels Provos 
13949868b61SNick Mathewson 	hook = mm_calloc(1, sizeof(struct evrpc_hook));
1402e36dbe1SNick Mathewson 	EVUTIL_ASSERT(hook != NULL);
14165236aa8SNiels Provos 
14265236aa8SNiels Provos 	hook->process = cb;
14365236aa8SNiels Provos 	hook->process_arg = cb_arg;
14465236aa8SNiels Provos 	TAILQ_INSERT_TAIL(head, hook, next);
14565236aa8SNiels Provos 
14665236aa8SNiels Provos 	return (hook);
14765236aa8SNiels Provos }
14865236aa8SNiels Provos 
1491d3a008aSNiels Provos static int
evrpc_remove_hook_internal(struct evrpc_hook_list * head,void * handle)1501d3a008aSNiels Provos evrpc_remove_hook_internal(struct evrpc_hook_list *head, void *handle)
15165236aa8SNiels Provos {
15265236aa8SNiels Provos 	struct evrpc_hook *hook = NULL;
15365236aa8SNiels Provos 	TAILQ_FOREACH(hook, head, next) {
15465236aa8SNiels Provos 		if (hook == handle) {
15565236aa8SNiels Provos 			TAILQ_REMOVE(head, hook, next);
15649868b61SNick Mathewson 			mm_free(hook);
15765236aa8SNiels Provos 			return (1);
15865236aa8SNiels Provos 		}
15965236aa8SNiels Provos 	}
16065236aa8SNiels Provos 
16165236aa8SNiels Provos 	return (0);
16265236aa8SNiels Provos }
16365236aa8SNiels Provos 
1641d3a008aSNiels Provos /*
1651d3a008aSNiels Provos  * remove the hook specified by the handle
1661d3a008aSNiels Provos  */
1671d3a008aSNiels Provos 
1681d3a008aSNiels Provos int
evrpc_remove_hook(void * vbase,enum EVRPC_HOOK_TYPE hook_type,void * handle)1691d3a008aSNiels Provos evrpc_remove_hook(void *vbase, enum EVRPC_HOOK_TYPE hook_type, void *handle)
1701d3a008aSNiels Provos {
171cb9da0bfSNick Mathewson 	struct evrpc_hooks_ *base = vbase;
1721d3a008aSNiels Provos 	struct evrpc_hook_list *head = NULL;
1731d3a008aSNiels Provos 	switch (hook_type) {
1742baaac7fSNick Mathewson 	case EVRPC_INPUT:
1751d3a008aSNiels Provos 		head = &base->in_hooks;
1761d3a008aSNiels Provos 		break;
1772baaac7fSNick Mathewson 	case EVRPC_OUTPUT:
1781d3a008aSNiels Provos 		head = &base->out_hooks;
1791d3a008aSNiels Provos 		break;
1801d3a008aSNiels Provos 	default:
1812e36dbe1SNick Mathewson 		EVUTIL_ASSERT(hook_type == EVRPC_INPUT || hook_type == EVRPC_OUTPUT);
1821d3a008aSNiels Provos 	}
1831d3a008aSNiels Provos 
1841d3a008aSNiels Provos 	return (evrpc_remove_hook_internal(head, handle));
1851d3a008aSNiels Provos }
1861d3a008aSNiels Provos 
18765236aa8SNiels Provos static int
evrpc_process_hooks(struct evrpc_hook_list * head,void * ctx,struct evhttp_request * req,struct evbuffer * evbuf)188819d4a33SNiels Provos evrpc_process_hooks(struct evrpc_hook_list *head, void *ctx,
18965236aa8SNiels Provos     struct evhttp_request *req, struct evbuffer *evbuf)
19065236aa8SNiels Provos {
19165236aa8SNiels Provos 	struct evrpc_hook *hook;
19265236aa8SNiels Provos 	TAILQ_FOREACH(hook, head, next) {
193955c6abfSNiels Provos 		int res = hook->process(ctx, req, evbuf, hook->process_arg);
194955c6abfSNiels Provos 		if (res != EVRPC_CONTINUE)
195955c6abfSNiels Provos 			return (res);
19665236aa8SNiels Provos 	}
19765236aa8SNiels Provos 
198955c6abfSNiels Provos 	return (EVRPC_CONTINUE);
19965236aa8SNiels Provos }
20065236aa8SNiels Provos 
2012d028ef6SNiels Provos static void evrpc_pool_schedule(struct evrpc_pool *pool);
2022d028ef6SNiels Provos static void evrpc_request_cb(struct evhttp_request *, void *);
203f554234fSNiels Provos 
204f554234fSNiels Provos /*
205f554234fSNiels Provos  * Registers a new RPC with the HTTP server.   The evrpc object is expected
206f554234fSNiels Provos  * to have been filled in via the EVRPC_REGISTER_OBJECT macro which in turn
207f554234fSNiels Provos  * calls this function.
208f554234fSNiels Provos  */
209f554234fSNiels Provos 
210a3f122d6SNick Mathewson static char *
evrpc_construct_uri(const char * uri)211fda1216bSNiels Provos evrpc_construct_uri(const char *uri)
212f554234fSNiels Provos {
213f554234fSNiels Provos 	char *constructed_uri;
214545a6114SNick Mathewson 	size_t constructed_uri_len;
215f554234fSNiels Provos 
216fda1216bSNiels Provos 	constructed_uri_len = strlen(EVRPC_URI_PREFIX) + strlen(uri) + 1;
21749868b61SNick Mathewson 	if ((constructed_uri = mm_malloc(constructed_uri_len)) == NULL)
218f554234fSNiels Provos 		event_err(1, "%s: failed to register rpc at %s",
219fda1216bSNiels Provos 		    __func__, uri);
220f554234fSNiels Provos 	memcpy(constructed_uri, EVRPC_URI_PREFIX, strlen(EVRPC_URI_PREFIX));
221fda1216bSNiels Provos 	memcpy(constructed_uri + strlen(EVRPC_URI_PREFIX), uri, strlen(uri));
222f554234fSNiels Provos 	constructed_uri[constructed_uri_len - 1] = '\0';
223f554234fSNiels Provos 
224fda1216bSNiels Provos 	return (constructed_uri);
225fda1216bSNiels Provos }
226fda1216bSNiels Provos 
227fda1216bSNiels Provos int
evrpc_register_rpc(struct evrpc_base * base,struct evrpc * rpc,void (* cb)(struct evrpc_req_generic *,void *),void * cb_arg)228fda1216bSNiels Provos evrpc_register_rpc(struct evrpc_base *base, struct evrpc *rpc,
229fda1216bSNiels Provos     void (*cb)(struct evrpc_req_generic *, void *), void *cb_arg)
230fda1216bSNiels Provos {
231fda1216bSNiels Provos 	char *constructed_uri = evrpc_construct_uri(rpc->uri);
232fda1216bSNiels Provos 
23365236aa8SNiels Provos 	rpc->base = base;
234fda1216bSNiels Provos 	rpc->cb = cb;
235fda1216bSNiels Provos 	rpc->cb_arg = cb_arg;
236fda1216bSNiels Provos 
237f554234fSNiels Provos 	TAILQ_INSERT_TAIL(&base->registered_rpcs, rpc, next);
238f554234fSNiels Provos 
239f554234fSNiels Provos 	evhttp_set_cb(base->http_server,
240f554234fSNiels Provos 	    constructed_uri,
241f554234fSNiels Provos 	    evrpc_request_cb,
242f554234fSNiels Provos 	    rpc);
243f554234fSNiels Provos 
24449868b61SNick Mathewson 	mm_free(constructed_uri);
245c4836d10SNiels Provos 
246f554234fSNiels Provos 	return (0);
247f554234fSNiels Provos }
248f554234fSNiels Provos 
2490c280824SNiels Provos int
evrpc_unregister_rpc(struct evrpc_base * base,const char * name)2500c280824SNiels Provos evrpc_unregister_rpc(struct evrpc_base *base, const char *name)
2510c280824SNiels Provos {
2520c280824SNiels Provos 	char *registered_uri = NULL;
2530c280824SNiels Provos 	struct evrpc *rpc;
254743f8665SNick Mathewson 	int r;
2550c280824SNiels Provos 
2560c280824SNiels Provos 	/* find the right rpc; linear search might be slow */
2570c280824SNiels Provos 	TAILQ_FOREACH(rpc, &base->registered_rpcs, next) {
2580c280824SNiels Provos 		if (strcmp(rpc->uri, name) == 0)
2590c280824SNiels Provos 			break;
2600c280824SNiels Provos 	}
2610c280824SNiels Provos 	if (rpc == NULL) {
2620c280824SNiels Provos 		/* We did not find an RPC with this name */
2630c280824SNiels Provos 		return (-1);
2640c280824SNiels Provos 	}
2650c280824SNiels Provos 	TAILQ_REMOVE(&base->registered_rpcs, rpc, next);
2660c280824SNiels Provos 
2670c280824SNiels Provos 	registered_uri = evrpc_construct_uri(name);
2680c280824SNiels Provos 
2690c280824SNiels Provos 	/* remove the http server callback */
270743f8665SNick Mathewson 	r = evhttp_del_cb(base->http_server, registered_uri);
271743f8665SNick Mathewson 	EVUTIL_ASSERT(r == 0);
2720c280824SNiels Provos 
27349868b61SNick Mathewson 	mm_free(registered_uri);
2744b8f02f1SChristophe Fillot 
2754b8f02f1SChristophe Fillot 	mm_free((char *)rpc->uri);
2764b8f02f1SChristophe Fillot 	mm_free(rpc);
2770c280824SNiels Provos 	return (0);
2780c280824SNiels Provos }
2790c280824SNiels Provos 
280819d4a33SNiels Provos static int evrpc_pause_request(void *vbase, void *ctx,
281819d4a33SNiels Provos     void (*cb)(void *, enum EVRPC_HOOK_RESULT));
282819d4a33SNiels Provos static void evrpc_request_cb_closure(void *, enum EVRPC_HOOK_RESULT);
283819d4a33SNiels Provos 
2842d028ef6SNiels Provos static void
evrpc_request_cb(struct evhttp_request * req,void * arg)285f554234fSNiels Provos evrpc_request_cb(struct evhttp_request *req, void *arg)
286f554234fSNiels Provos {
287f554234fSNiels Provos 	struct evrpc *rpc = arg;
288f554234fSNiels Provos 	struct evrpc_req_generic *rpc_state = NULL;
289f554234fSNiels Provos 
290f554234fSNiels Provos 	/* let's verify the outside parameters */
291f554234fSNiels Provos 	if (req->type != EVHTTP_REQ_POST ||
292a8f6d961SNick Mathewson 	    evbuffer_get_length(req->input_buffer) <= 0)
293f554234fSNiels Provos 		goto error;
294f554234fSNiels Provos 
29549868b61SNick Mathewson 	rpc_state = mm_calloc(1, sizeof(struct evrpc_req_generic));
296819d4a33SNiels Provos 	if (rpc_state == NULL)
297819d4a33SNiels Provos 		goto error;
298819d4a33SNiels Provos 	rpc_state->rpc = rpc;
299819d4a33SNiels Provos 	rpc_state->http_req = req;
300819d4a33SNiels Provos 	rpc_state->rpc_data = NULL;
301819d4a33SNiels Provos 
3022460aa59SNiels Provos 	if (TAILQ_FIRST(&rpc->base->input_hooks) != NULL) {
3032460aa59SNiels Provos 		int hook_res;
3042460aa59SNiels Provos 
3058ac3c4c2SNick Mathewson 		evrpc_hook_associate_meta_(&rpc_state->hook_meta, req->evcon);
306819d4a33SNiels Provos 
30765236aa8SNiels Provos 		/*
3082460aa59SNiels Provos 		 * allow hooks to modify the outgoing request
30965236aa8SNiels Provos 		 */
310819d4a33SNiels Provos 		hook_res = evrpc_process_hooks(&rpc->base->input_hooks,
311819d4a33SNiels Provos 		    rpc_state, req, req->input_buffer);
312819d4a33SNiels Provos 		switch (hook_res) {
313819d4a33SNiels Provos 		case EVRPC_TERMINATE:
31465236aa8SNiels Provos 			goto error;
315819d4a33SNiels Provos 		case EVRPC_PAUSE:
316819d4a33SNiels Provos 			evrpc_pause_request(rpc->base, rpc_state,
317819d4a33SNiels Provos 			    evrpc_request_cb_closure);
318819d4a33SNiels Provos 			return;
319819d4a33SNiels Provos 		case EVRPC_CONTINUE:
320819d4a33SNiels Provos 			break;
321819d4a33SNiels Provos 		default:
3222e36dbe1SNick Mathewson 			EVUTIL_ASSERT(hook_res == EVRPC_TERMINATE ||
3232460aa59SNiels Provos 			    hook_res == EVRPC_CONTINUE ||
3242460aa59SNiels Provos 			    hook_res == EVRPC_PAUSE);
3252460aa59SNiels Provos 		}
326819d4a33SNiels Provos 	}
32765236aa8SNiels Provos 
328819d4a33SNiels Provos 	evrpc_request_cb_closure(rpc_state, EVRPC_CONTINUE);
329819d4a33SNiels Provos 	return;
330819d4a33SNiels Provos 
331819d4a33SNiels Provos error:
332*e05136c7SAzat Khuzhin 	if (rpc_state)
3338ac3c4c2SNick Mathewson 		evrpc_reqstate_free_(rpc_state);
33439906698SNick Mathewson 	evhttp_send_error(req, HTTP_SERVUNAVAIL, NULL);
335819d4a33SNiels Provos 	return;
336819d4a33SNiels Provos }
337819d4a33SNiels Provos 
338819d4a33SNiels Provos static void
evrpc_request_cb_closure(void * arg,enum EVRPC_HOOK_RESULT hook_res)339819d4a33SNiels Provos evrpc_request_cb_closure(void *arg, enum EVRPC_HOOK_RESULT hook_res)
340819d4a33SNiels Provos {
341819d4a33SNiels Provos 	struct evrpc_req_generic *rpc_state = arg;
3426056d6e0SNick Mathewson 	struct evrpc *rpc;
3436056d6e0SNick Mathewson 	struct evhttp_request *req;
3446056d6e0SNick Mathewson 
3457c11e51eSHarlan Stenn 	EVUTIL_ASSERT(rpc_state);
3466056d6e0SNick Mathewson 	rpc = rpc_state->rpc;
3476056d6e0SNick Mathewson 	req = rpc_state->http_req;
348819d4a33SNiels Provos 
349819d4a33SNiels Provos 	if (hook_res == EVRPC_TERMINATE)
350f554234fSNiels Provos 		goto error;
351f554234fSNiels Provos 
352f554234fSNiels Provos 	/* let's check that we can parse the request */
353755fbf16SShuo Chen 	rpc_state->request = rpc->request_new(rpc->request_new_arg);
354f554234fSNiels Provos 	if (rpc_state->request == NULL)
355f554234fSNiels Provos 		goto error;
356c4836d10SNiels Provos 
357f554234fSNiels Provos 	if (rpc->request_unmarshal(
358f554234fSNiels Provos 		    rpc_state->request, req->input_buffer) == -1) {
359f554234fSNiels Provos 		/* we failed to parse the request; that's a bummer */
360f554234fSNiels Provos 		goto error;
361f554234fSNiels Provos 	}
362f554234fSNiels Provos 
363f554234fSNiels Provos 	/* at this point, we have a well formed request, prepare the reply */
364f554234fSNiels Provos 
365755fbf16SShuo Chen 	rpc_state->reply = rpc->reply_new(rpc->reply_new_arg);
366f554234fSNiels Provos 	if (rpc_state->reply == NULL)
367f554234fSNiels Provos 		goto error;
368f554234fSNiels Provos 
369f554234fSNiels Provos 	/* give the rpc to the user; they can deal with it */
370f554234fSNiels Provos 	rpc->cb(rpc_state, rpc->cb_arg);
371f554234fSNiels Provos 
372f554234fSNiels Provos 	return;
373f554234fSNiels Provos 
374f554234fSNiels Provos error:
3758ac3c4c2SNick Mathewson 	evrpc_reqstate_free_(rpc_state);
37639906698SNick Mathewson 	evhttp_send_error(req, HTTP_SERVUNAVAIL, NULL);
377f554234fSNiels Provos 	return;
378f554234fSNiels Provos }
379f554234fSNiels Provos 
380819d4a33SNiels Provos 
381f554234fSNiels Provos void
evrpc_reqstate_free_(struct evrpc_req_generic * rpc_state)3828ac3c4c2SNick Mathewson evrpc_reqstate_free_(struct evrpc_req_generic* rpc_state)
383f554234fSNiels Provos {
384819d4a33SNiels Provos 	struct evrpc *rpc;
3852e36dbe1SNick Mathewson 	EVUTIL_ASSERT(rpc_state != NULL);
386819d4a33SNiels Provos 	rpc = rpc_state->rpc;
38744bd5ab4SNiels Provos 
388819d4a33SNiels Provos 	/* clean up all memory */
3892460aa59SNiels Provos 	if (rpc_state->hook_meta != NULL)
3908ac3c4c2SNick Mathewson 		evrpc_hook_context_free_(rpc_state->hook_meta);
391f554234fSNiels Provos 	if (rpc_state->request != NULL)
392c4836d10SNiels Provos 		rpc->request_free(rpc_state->request);
393f554234fSNiels Provos 	if (rpc_state->reply != NULL)
394f554234fSNiels Provos 		rpc->reply_free(rpc_state->reply);
395819d4a33SNiels Provos 	if (rpc_state->rpc_data != NULL)
396819d4a33SNiels Provos 		evbuffer_free(rpc_state->rpc_data);
39749868b61SNick Mathewson 	mm_free(rpc_state);
398f554234fSNiels Provos }
399819d4a33SNiels Provos 
400819d4a33SNiels Provos static void
401819d4a33SNiels Provos evrpc_request_done_closure(void *, enum EVRPC_HOOK_RESULT);
402f554234fSNiels Provos 
403f554234fSNiels Provos void
evrpc_request_done(struct evrpc_req_generic * rpc_state)404f554234fSNiels Provos evrpc_request_done(struct evrpc_req_generic *rpc_state)
405f554234fSNiels Provos {
4067c11e51eSHarlan Stenn 	struct evhttp_request *req;
4077c11e51eSHarlan Stenn 	struct evrpc *rpc;
4087c11e51eSHarlan Stenn 
4097c11e51eSHarlan Stenn 	EVUTIL_ASSERT(rpc_state);
4107c11e51eSHarlan Stenn 
4117c11e51eSHarlan Stenn 	req = rpc_state->http_req;
4127c11e51eSHarlan Stenn 	rpc = rpc_state->rpc;
413f554234fSNiels Provos 
414c4836d10SNiels Provos 	if (rpc->reply_complete(rpc_state->reply) == -1) {
415f554234fSNiels Provos 		/* the reply was not completely filled in.  error out */
416f554234fSNiels Provos 		goto error;
417f554234fSNiels Provos 	}
418f554234fSNiels Provos 
419819d4a33SNiels Provos 	if ((rpc_state->rpc_data = evbuffer_new()) == NULL) {
420f554234fSNiels Provos 		/* out of memory */
421f554234fSNiels Provos 		goto error;
422f554234fSNiels Provos 	}
423f554234fSNiels Provos 
424f554234fSNiels Provos 	/* serialize the reply */
425819d4a33SNiels Provos 	rpc->reply_marshal(rpc_state->rpc_data, rpc_state->reply);
426f554234fSNiels Provos 
4272460aa59SNiels Provos 	if (TAILQ_FIRST(&rpc->base->output_hooks) != NULL) {
4282460aa59SNiels Provos 		int hook_res;
4292460aa59SNiels Provos 
4308ac3c4c2SNick Mathewson 		evrpc_hook_associate_meta_(&rpc_state->hook_meta, req->evcon);
4312460aa59SNiels Provos 
43265236aa8SNiels Provos 		/* do hook based tweaks to the request */
433819d4a33SNiels Provos 		hook_res = evrpc_process_hooks(&rpc->base->output_hooks,
434819d4a33SNiels Provos 		    rpc_state, req, rpc_state->rpc_data);
435819d4a33SNiels Provos 		switch (hook_res) {
436819d4a33SNiels Provos 		case EVRPC_TERMINATE:
437819d4a33SNiels Provos 			goto error;
438819d4a33SNiels Provos 		case EVRPC_PAUSE:
439819d4a33SNiels Provos 			if (evrpc_pause_request(rpc->base, rpc_state,
440819d4a33SNiels Provos 				evrpc_request_done_closure) == -1)
441819d4a33SNiels Provos 				goto error;
442819d4a33SNiels Provos 			return;
443819d4a33SNiels Provos 		case EVRPC_CONTINUE:
444819d4a33SNiels Provos 			break;
445819d4a33SNiels Provos 		default:
4462e36dbe1SNick Mathewson 			EVUTIL_ASSERT(hook_res == EVRPC_TERMINATE ||
4472460aa59SNiels Provos 			    hook_res == EVRPC_CONTINUE ||
4482460aa59SNiels Provos 			    hook_res == EVRPC_PAUSE);
4492460aa59SNiels Provos 		}
450819d4a33SNiels Provos 	}
451819d4a33SNiels Provos 
452819d4a33SNiels Provos 	evrpc_request_done_closure(rpc_state, EVRPC_CONTINUE);
453819d4a33SNiels Provos 	return;
454819d4a33SNiels Provos 
455819d4a33SNiels Provos error:
4568ac3c4c2SNick Mathewson 	evrpc_reqstate_free_(rpc_state);
45739906698SNick Mathewson 	evhttp_send_error(req, HTTP_SERVUNAVAIL, NULL);
458819d4a33SNiels Provos 	return;
459819d4a33SNiels Provos }
460819d4a33SNiels Provos 
46107edf784SShuo Chen void *
evrpc_get_request(struct evrpc_req_generic * req)46207edf784SShuo Chen evrpc_get_request(struct evrpc_req_generic *req)
46307edf784SShuo Chen {
46407edf784SShuo Chen 	return req->request;
46507edf784SShuo Chen }
46607edf784SShuo Chen 
46707edf784SShuo Chen void *
evrpc_get_reply(struct evrpc_req_generic * req)46807edf784SShuo Chen evrpc_get_reply(struct evrpc_req_generic *req)
46907edf784SShuo Chen {
47007edf784SShuo Chen 	return req->reply;
47107edf784SShuo Chen }
47207edf784SShuo Chen 
473819d4a33SNiels Provos static void
evrpc_request_done_closure(void * arg,enum EVRPC_HOOK_RESULT hook_res)474819d4a33SNiels Provos evrpc_request_done_closure(void *arg, enum EVRPC_HOOK_RESULT hook_res)
475819d4a33SNiels Provos {
476819d4a33SNiels Provos 	struct evrpc_req_generic *rpc_state = arg;
4776056d6e0SNick Mathewson 	struct evhttp_request *req;
4787c11e51eSHarlan Stenn 	EVUTIL_ASSERT(rpc_state);
4796056d6e0SNick Mathewson 	req = rpc_state->http_req;
480819d4a33SNiels Provos 
481819d4a33SNiels Provos 	if (hook_res == EVRPC_TERMINATE)
48265236aa8SNiels Provos 		goto error;
48365236aa8SNiels Provos 
4848920ac4dSNiels Provos 	/* on success, we are going to transmit marshaled binary data */
4858920ac4dSNiels Provos 	if (evhttp_find_header(req->output_headers, "Content-Type") == NULL) {
4868920ac4dSNiels Provos 		evhttp_add_header(req->output_headers,
4878920ac4dSNiels Provos 		    "Content-Type", "application/octet-stream");
4888920ac4dSNiels Provos 	}
489819d4a33SNiels Provos 	evhttp_send_reply(req, HTTP_OK, "OK", rpc_state->rpc_data);
490f554234fSNiels Provos 
4918ac3c4c2SNick Mathewson 	evrpc_reqstate_free_(rpc_state);
492f554234fSNiels Provos 
493f554234fSNiels Provos 	return;
494f554234fSNiels Provos 
495f554234fSNiels Provos error:
4968ac3c4c2SNick Mathewson 	evrpc_reqstate_free_(rpc_state);
49739906698SNick Mathewson 	evhttp_send_error(req, HTTP_SERVUNAVAIL, NULL);
498f554234fSNiels Provos 	return;
499f554234fSNiels Provos }
500fda1216bSNiels Provos 
501819d4a33SNiels Provos 
502fda1216bSNiels Provos /* Client implementation of RPC site */
503fda1216bSNiels Provos 
504fda1216bSNiels Provos static int evrpc_schedule_request(struct evhttp_connection *connection,
505fda1216bSNiels Provos     struct evrpc_request_wrapper *ctx);
506fda1216bSNiels Provos 
507fda1216bSNiels Provos struct evrpc_pool *
evrpc_pool_new(struct event_base * base)5081d3a008aSNiels Provos evrpc_pool_new(struct event_base *base)
509fda1216bSNiels Provos {
51049868b61SNick Mathewson 	struct evrpc_pool *pool = mm_calloc(1, sizeof(struct evrpc_pool));
511fda1216bSNiels Provos 	if (pool == NULL)
512fda1216bSNiels Provos 		return (NULL);
513fda1216bSNiels Provos 
514fda1216bSNiels Provos 	TAILQ_INIT(&pool->connections);
515fda1216bSNiels Provos 	TAILQ_INIT(&pool->requests);
516fda1216bSNiels Provos 
517819d4a33SNiels Provos 	TAILQ_INIT(&pool->paused_requests);
518819d4a33SNiels Provos 
5191d3a008aSNiels Provos 	TAILQ_INIT(&pool->input_hooks);
5201d3a008aSNiels Provos 	TAILQ_INIT(&pool->output_hooks);
5211d3a008aSNiels Provos 
5221d3a008aSNiels Provos 	pool->base = base;
5232d028ef6SNiels Provos 	pool->timeout = -1;
5242d028ef6SNiels Provos 
525fda1216bSNiels Provos 	return (pool);
526fda1216bSNiels Provos }
527fda1216bSNiels Provos 
528ff43ed5bSNiels Provos static void
evrpc_request_wrapper_free(struct evrpc_request_wrapper * request)529ff43ed5bSNiels Provos evrpc_request_wrapper_free(struct evrpc_request_wrapper *request)
530ff43ed5bSNiels Provos {
5312460aa59SNiels Provos 	if (request->hook_meta != NULL)
5328ac3c4c2SNick Mathewson 		evrpc_hook_context_free_(request->hook_meta);
53349868b61SNick Mathewson 	mm_free(request->name);
53449868b61SNick Mathewson 	mm_free(request);
535ff43ed5bSNiels Provos }
536ff43ed5bSNiels Provos 
537fda1216bSNiels Provos void
evrpc_pool_free(struct evrpc_pool * pool)538fda1216bSNiels Provos evrpc_pool_free(struct evrpc_pool *pool)
539fda1216bSNiels Provos {
540fda1216bSNiels Provos 	struct evhttp_connection *connection;
541fda1216bSNiels Provos 	struct evrpc_request_wrapper *request;
542819d4a33SNiels Provos 	struct evrpc_hook_ctx *pause;
5431d3a008aSNiels Provos 	struct evrpc_hook *hook;
544743f8665SNick Mathewson 	int r;
545fda1216bSNiels Provos 
546fda1216bSNiels Provos 	while ((request = TAILQ_FIRST(&pool->requests)) != NULL) {
547fda1216bSNiels Provos 		TAILQ_REMOVE(&pool->requests, request, next);
548ff43ed5bSNiels Provos 		evrpc_request_wrapper_free(request);
549fda1216bSNiels Provos 	}
550fda1216bSNiels Provos 
551819d4a33SNiels Provos 	while ((pause = TAILQ_FIRST(&pool->paused_requests)) != NULL) {
552819d4a33SNiels Provos 		TAILQ_REMOVE(&pool->paused_requests, pause, next);
55349868b61SNick Mathewson 		mm_free(pause);
554819d4a33SNiels Provos 	}
555819d4a33SNiels Provos 
556fda1216bSNiels Provos 	while ((connection = TAILQ_FIRST(&pool->connections)) != NULL) {
557fda1216bSNiels Provos 		TAILQ_REMOVE(&pool->connections, connection, next);
558fda1216bSNiels Provos 		evhttp_connection_free(connection);
559fda1216bSNiels Provos 	}
560fda1216bSNiels Provos 
5611d3a008aSNiels Provos 	while ((hook = TAILQ_FIRST(&pool->input_hooks)) != NULL) {
562743f8665SNick Mathewson 		r = evrpc_remove_hook(pool, EVRPC_INPUT, hook);
563743f8665SNick Mathewson 		EVUTIL_ASSERT(r);
5641d3a008aSNiels Provos 	}
5651d3a008aSNiels Provos 
5661d3a008aSNiels Provos 	while ((hook = TAILQ_FIRST(&pool->output_hooks)) != NULL) {
567743f8665SNick Mathewson 		r = evrpc_remove_hook(pool, EVRPC_OUTPUT, hook);
568743f8665SNick Mathewson 		EVUTIL_ASSERT(r);
5691d3a008aSNiels Provos 	}
5701d3a008aSNiels Provos 
57149868b61SNick Mathewson 	mm_free(pool);
572fda1216bSNiels Provos }
573fda1216bSNiels Provos 
574ff43ed5bSNiels Provos /*
575ff43ed5bSNiels Provos  * Add a connection to the RPC pool.   A request scheduled on the pool
576ff43ed5bSNiels Provos  * may use any available connection.
577ff43ed5bSNiels Provos  */
578ff43ed5bSNiels Provos 
579fda1216bSNiels Provos void
evrpc_pool_add_connection(struct evrpc_pool * pool,struct evhttp_connection * connection)580fda1216bSNiels Provos evrpc_pool_add_connection(struct evrpc_pool *pool,
58185c4904bSNiels Provos     struct evhttp_connection *connection)
58285c4904bSNiels Provos {
5832e36dbe1SNick Mathewson 	EVUTIL_ASSERT(connection->http_server == NULL);
584fda1216bSNiels Provos 	TAILQ_INSERT_TAIL(&pool->connections, connection, next);
585fda1216bSNiels Provos 
586fda1216bSNiels Provos 	/*
5871d3a008aSNiels Provos 	 * associate an event base with this connection
5881d3a008aSNiels Provos 	 */
5891d3a008aSNiels Provos 	if (pool->base != NULL)
5901d3a008aSNiels Provos 		evhttp_connection_set_base(connection, pool->base);
5911d3a008aSNiels Provos 
5921d3a008aSNiels Provos 	/*
5932d028ef6SNiels Provos 	 * unless a timeout was specifically set for a connection,
5942d028ef6SNiels Provos 	 * the connection inherits the timeout from the pool.
5952d028ef6SNiels Provos 	 */
5966350e6c4SConstantine Verutin 	if (!evutil_timerisset(&connection->timeout))
5976350e6c4SConstantine Verutin 		evhttp_connection_set_timeout(connection, pool->timeout);
5982d028ef6SNiels Provos 
5992d028ef6SNiels Provos 	/*
600ff43ed5bSNiels Provos 	 * if we have any requests pending, schedule them with the new
601fda1216bSNiels Provos 	 * connections.
602fda1216bSNiels Provos 	 */
603fda1216bSNiels Provos 
604fda1216bSNiels Provos 	if (TAILQ_FIRST(&pool->requests) != NULL) {
605fda1216bSNiels Provos 		struct evrpc_request_wrapper *request =
606fda1216bSNiels Provos 		    TAILQ_FIRST(&pool->requests);
607fda1216bSNiels Provos 		TAILQ_REMOVE(&pool->requests, request, next);
608fda1216bSNiels Provos 		evrpc_schedule_request(connection, request);
609fda1216bSNiels Provos 	}
610fda1216bSNiels Provos }
611fda1216bSNiels Provos 
6122d028ef6SNiels Provos void
evrpc_pool_remove_connection(struct evrpc_pool * pool,struct evhttp_connection * connection)61385c4904bSNiels Provos evrpc_pool_remove_connection(struct evrpc_pool *pool,
61485c4904bSNiels Provos     struct evhttp_connection *connection)
61585c4904bSNiels Provos {
61685c4904bSNiels Provos 	TAILQ_REMOVE(&pool->connections, connection, next);
61785c4904bSNiels Provos }
61885c4904bSNiels Provos 
61985c4904bSNiels Provos void
evrpc_pool_set_timeout(struct evrpc_pool * pool,int timeout_in_secs)6202d028ef6SNiels Provos evrpc_pool_set_timeout(struct evrpc_pool *pool, int timeout_in_secs)
6212d028ef6SNiels Provos {
6222d028ef6SNiels Provos 	struct evhttp_connection *evcon;
6232d028ef6SNiels Provos 	TAILQ_FOREACH(evcon, &pool->connections, next) {
6246350e6c4SConstantine Verutin 		evhttp_connection_set_timeout(evcon, timeout_in_secs);
6252d028ef6SNiels Provos 	}
6262d028ef6SNiels Provos 	pool->timeout = timeout_in_secs;
6272d028ef6SNiels Provos }
6282d028ef6SNiels Provos 
629fda1216bSNiels Provos 
630fda1216bSNiels Provos static void evrpc_reply_done(struct evhttp_request *, void *);
6311120f04fSNick Mathewson static void evrpc_request_timeout(evutil_socket_t, short, void *);
632fda1216bSNiels Provos 
633fda1216bSNiels Provos /*
634fda1216bSNiels Provos  * Finds a connection object associated with the pool that is currently
635fda1216bSNiels Provos  * idle and can be used to make a request.
636fda1216bSNiels Provos  */
637fda1216bSNiels Provos static struct evhttp_connection *
evrpc_pool_find_connection(struct evrpc_pool * pool)638fda1216bSNiels Provos evrpc_pool_find_connection(struct evrpc_pool *pool)
639fda1216bSNiels Provos {
640fda1216bSNiels Provos 	struct evhttp_connection *connection;
641fda1216bSNiels Provos 	TAILQ_FOREACH(connection, &pool->connections, next) {
642fda1216bSNiels Provos 		if (TAILQ_FIRST(&connection->requests) == NULL)
643fda1216bSNiels Provos 			return (connection);
644fda1216bSNiels Provos 	}
645fda1216bSNiels Provos 
646fda1216bSNiels Provos 	return (NULL);
647fda1216bSNiels Provos }
648fda1216bSNiels Provos 
649fda1216bSNiels Provos /*
650819d4a33SNiels Provos  * Prototypes responsible for evrpc scheduling and hooking
651819d4a33SNiels Provos  */
652819d4a33SNiels Provos 
653819d4a33SNiels Provos static void evrpc_schedule_request_closure(void *ctx, enum EVRPC_HOOK_RESULT);
654819d4a33SNiels Provos 
655819d4a33SNiels Provos /*
656fda1216bSNiels Provos  * We assume that the ctx is no longer queued on the pool.
657fda1216bSNiels Provos  */
658fda1216bSNiels Provos static int
evrpc_schedule_request(struct evhttp_connection * connection,struct evrpc_request_wrapper * ctx)659fda1216bSNiels Provos evrpc_schedule_request(struct evhttp_connection *connection,
660fda1216bSNiels Provos     struct evrpc_request_wrapper *ctx)
661fda1216bSNiels Provos {
662ff43ed5bSNiels Provos 	struct evhttp_request *req = NULL;
6632d028ef6SNiels Provos 	struct evrpc_pool *pool = ctx->pool;
6643794534fSNiels Provos 	struct evrpc_status status;
665fda1216bSNiels Provos 
666fda1216bSNiels Provos 	if ((req = evhttp_request_new(evrpc_reply_done, ctx)) == NULL)
667fda1216bSNiels Provos 		goto error;
668fda1216bSNiels Provos 
669ff43ed5bSNiels Provos 	/* serialize the request data into the output buffer */
670ff43ed5bSNiels Provos 	ctx->request_marshal(req->output_buffer, ctx->request);
671ff43ed5bSNiels Provos 
6722d028ef6SNiels Provos 	/* we need to know the connection that we might have to abort */
6732d028ef6SNiels Provos 	ctx->evcon = connection;
6742d028ef6SNiels Provos 
675819d4a33SNiels Provos 	/* if we get paused we also need to know the request */
676819d4a33SNiels Provos 	ctx->req = req;
677819d4a33SNiels Provos 
6782460aa59SNiels Provos 	if (TAILQ_FIRST(&pool->output_hooks) != NULL) {
6792460aa59SNiels Provos 		int hook_res;
6802460aa59SNiels Provos 
6818ac3c4c2SNick Mathewson 		evrpc_hook_associate_meta_(&ctx->hook_meta, connection);
6822460aa59SNiels Provos 
6831d3a008aSNiels Provos 		/* apply hooks to the outgoing request */
684819d4a33SNiels Provos 		hook_res = evrpc_process_hooks(&pool->output_hooks,
685819d4a33SNiels Provos 		    ctx, req, req->output_buffer);
686819d4a33SNiels Provos 
687819d4a33SNiels Provos 		switch (hook_res) {
688819d4a33SNiels Provos 		case EVRPC_TERMINATE:
689819d4a33SNiels Provos 			goto error;
690819d4a33SNiels Provos 		case EVRPC_PAUSE:
691819d4a33SNiels Provos 			/* we need to be explicitly resumed */
692819d4a33SNiels Provos 			if (evrpc_pause_request(pool, ctx,
693819d4a33SNiels Provos 				evrpc_schedule_request_closure) == -1)
694819d4a33SNiels Provos 				goto error;
695819d4a33SNiels Provos 			return (0);
696819d4a33SNiels Provos 		case EVRPC_CONTINUE:
697819d4a33SNiels Provos 			/* we can just continue */
698819d4a33SNiels Provos 			break;
699819d4a33SNiels Provos 		default:
7002e36dbe1SNick Mathewson 			EVUTIL_ASSERT(hook_res == EVRPC_TERMINATE ||
7012460aa59SNiels Provos 			    hook_res == EVRPC_CONTINUE ||
7022460aa59SNiels Provos 			    hook_res == EVRPC_PAUSE);
7032460aa59SNiels Provos 		}
704819d4a33SNiels Provos 	}
705819d4a33SNiels Provos 
706819d4a33SNiels Provos 	evrpc_schedule_request_closure(ctx, EVRPC_CONTINUE);
707819d4a33SNiels Provos 	return (0);
708819d4a33SNiels Provos 
709819d4a33SNiels Provos error:
710819d4a33SNiels Provos 	memset(&status, 0, sizeof(status));
711819d4a33SNiels Provos 	status.error = EVRPC_STATUS_ERR_UNSTARTED;
712819d4a33SNiels Provos 	(*ctx->cb)(&status, ctx->request, ctx->reply, ctx->cb_arg);
713819d4a33SNiels Provos 	evrpc_request_wrapper_free(ctx);
714819d4a33SNiels Provos 	return (-1);
715819d4a33SNiels Provos }
716819d4a33SNiels Provos 
717819d4a33SNiels Provos static void
evrpc_schedule_request_closure(void * arg,enum EVRPC_HOOK_RESULT hook_res)718819d4a33SNiels Provos evrpc_schedule_request_closure(void *arg, enum EVRPC_HOOK_RESULT hook_res)
719819d4a33SNiels Provos {
720819d4a33SNiels Provos 	struct evrpc_request_wrapper *ctx = arg;
721819d4a33SNiels Provos 	struct evhttp_connection *connection = ctx->evcon;
722819d4a33SNiels Provos 	struct evhttp_request *req = ctx->req;
723819d4a33SNiels Provos 	struct evrpc_pool *pool = ctx->pool;
724819d4a33SNiels Provos 	struct evrpc_status status;
725819d4a33SNiels Provos 	char *uri = NULL;
726819d4a33SNiels Provos 	int res = 0;
727819d4a33SNiels Provos 
728819d4a33SNiels Provos 	if (hook_res == EVRPC_TERMINATE)
729819d4a33SNiels Provos 		goto error;
730819d4a33SNiels Provos 
731819d4a33SNiels Provos 	uri = evrpc_construct_uri(ctx->name);
732819d4a33SNiels Provos 	if (uri == NULL)
7331d3a008aSNiels Provos 		goto error;
7341d3a008aSNiels Provos 
7352d028ef6SNiels Provos 	if (pool->timeout > 0) {
7362d028ef6SNiels Provos 		/*
7372d028ef6SNiels Provos 		 * a timeout after which the whole rpc is going to be aborted.
7382d028ef6SNiels Provos 		 */
7392d028ef6SNiels Provos 		struct timeval tv;
740f74e7258SNick Mathewson 		evutil_timerclear(&tv);
7412d028ef6SNiels Provos 		tv.tv_sec = pool->timeout;
7422d028ef6SNiels Provos 		evtimer_add(&ctx->ev_timeout, &tv);
7432d028ef6SNiels Provos 	}
7442d028ef6SNiels Provos 
745ff43ed5bSNiels Provos 	/* start the request over the connection */
746ff43ed5bSNiels Provos 	res = evhttp_make_request(connection, req, EVHTTP_REQ_POST, uri);
74749868b61SNick Mathewson 	mm_free(uri);
748ff43ed5bSNiels Provos 
749ff43ed5bSNiels Provos 	if (res == -1)
750ff43ed5bSNiels Provos 		goto error;
751ff43ed5bSNiels Provos 
752819d4a33SNiels Provos 	return;
753fda1216bSNiels Provos 
754fda1216bSNiels Provos error:
7553794534fSNiels Provos 	memset(&status, 0, sizeof(status));
7563794534fSNiels Provos 	status.error = EVRPC_STATUS_ERR_UNSTARTED;
7573794534fSNiels Provos 	(*ctx->cb)(&status, ctx->request, ctx->reply, ctx->cb_arg);
758ff43ed5bSNiels Provos 	evrpc_request_wrapper_free(ctx);
759819d4a33SNiels Provos }
760819d4a33SNiels Provos 
761819d4a33SNiels Provos /* we just queue the paused request on the pool under the req object */
762819d4a33SNiels Provos static int
evrpc_pause_request(void * vbase,void * ctx,void (* cb)(void *,enum EVRPC_HOOK_RESULT))763819d4a33SNiels Provos evrpc_pause_request(void *vbase, void *ctx,
764819d4a33SNiels Provos     void (*cb)(void *, enum EVRPC_HOOK_RESULT))
765819d4a33SNiels Provos {
766cb9da0bfSNick Mathewson 	struct evrpc_hooks_ *base = vbase;
76749868b61SNick Mathewson 	struct evrpc_hook_ctx *pause = mm_malloc(sizeof(*pause));
768819d4a33SNiels Provos 	if (pause == NULL)
769fda1216bSNiels Provos 		return (-1);
770819d4a33SNiels Provos 
771819d4a33SNiels Provos 	pause->ctx = ctx;
772819d4a33SNiels Provos 	pause->cb = cb;
773819d4a33SNiels Provos 
774819d4a33SNiels Provos 	TAILQ_INSERT_TAIL(&base->pause_requests, pause, next);
775819d4a33SNiels Provos 	return (0);
776819d4a33SNiels Provos }
777819d4a33SNiels Provos 
778819d4a33SNiels Provos int
evrpc_resume_request(void * vbase,void * ctx,enum EVRPC_HOOK_RESULT res)779819d4a33SNiels Provos evrpc_resume_request(void *vbase, void *ctx, enum EVRPC_HOOK_RESULT res)
780819d4a33SNiels Provos {
781cb9da0bfSNick Mathewson 	struct evrpc_hooks_ *base = vbase;
782819d4a33SNiels Provos 	struct evrpc_pause_list *head = &base->pause_requests;
783819d4a33SNiels Provos 	struct evrpc_hook_ctx *pause;
784819d4a33SNiels Provos 
785819d4a33SNiels Provos 	TAILQ_FOREACH(pause, head, next) {
786819d4a33SNiels Provos 		if (pause->ctx == ctx)
787819d4a33SNiels Provos 			break;
788819d4a33SNiels Provos 	}
789819d4a33SNiels Provos 
790819d4a33SNiels Provos 	if (pause == NULL)
791819d4a33SNiels Provos 		return (-1);
792819d4a33SNiels Provos 
793819d4a33SNiels Provos 	(*pause->cb)(pause->ctx, res);
794819d4a33SNiels Provos 	TAILQ_REMOVE(head, pause, next);
79594ee1251SNick Mathewson 	mm_free(pause);
796819d4a33SNiels Provos 	return (0);
797fda1216bSNiels Provos }
798fda1216bSNiels Provos 
799fda1216bSNiels Provos int
evrpc_make_request(struct evrpc_request_wrapper * ctx)800fda1216bSNiels Provos evrpc_make_request(struct evrpc_request_wrapper *ctx)
801fda1216bSNiels Provos {
802fda1216bSNiels Provos 	struct evrpc_pool *pool = ctx->pool;
8032d028ef6SNiels Provos 
8042d028ef6SNiels Provos 	/* initialize the event structure for this rpc */
8055fbc7f0aSNick Mathewson 	evtimer_assign(&ctx->ev_timeout, pool->base, evrpc_request_timeout, ctx);
806fda1216bSNiels Provos 
807fda1216bSNiels Provos 	/* we better have some available connections on the pool */
8082e36dbe1SNick Mathewson 	EVUTIL_ASSERT(TAILQ_FIRST(&pool->connections) != NULL);
809fda1216bSNiels Provos 
810fda1216bSNiels Provos 	/*
811fda1216bSNiels Provos 	 * if no connection is available, we queue the request on the pool,
812fda1216bSNiels Provos 	 * the next time a connection is empty, the rpc will be send on that.
813fda1216bSNiels Provos 	 */
814fda1216bSNiels Provos 	TAILQ_INSERT_TAIL(&pool->requests, ctx, next);
8152d028ef6SNiels Provos 
8162d028ef6SNiels Provos 	evrpc_pool_schedule(pool);
8172d028ef6SNiels Provos 
818fda1216bSNiels Provos 	return (0);
819fda1216bSNiels Provos }
820fda1216bSNiels Provos 
8215a5609c7SNiels Provos 
8225a5609c7SNiels Provos struct evrpc_request_wrapper *
evrpc_make_request_ctx(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)823e8f450f2SNiels Provos evrpc_make_request_ctx(
8245a5609c7SNiels Provos 	struct evrpc_pool *pool, void *request, void *reply,
8255a5609c7SNiels Provos 	const char *rpcname,
8265a5609c7SNiels Provos 	void (*req_marshal)(struct evbuffer*, void *),
8275a5609c7SNiels Provos 	void (*rpl_clear)(void *),
8285a5609c7SNiels Provos 	int (*rpl_unmarshal)(void *, struct evbuffer *),
8295a5609c7SNiels Provos 	void (*cb)(struct evrpc_status *, void *, void *, void *),
8305a5609c7SNiels Provos 	void *cbarg)
8315a5609c7SNiels Provos {
8325a5609c7SNiels Provos 	struct evrpc_request_wrapper *ctx = (struct evrpc_request_wrapper *)
83349868b61SNick Mathewson 	    mm_malloc(sizeof(struct evrpc_request_wrapper));
8345a5609c7SNiels Provos 	if (ctx == NULL)
8355a5609c7SNiels Provos 		return (NULL);
8365a5609c7SNiels Provos 
8375a5609c7SNiels Provos 	ctx->pool = pool;
8382460aa59SNiels Provos 	ctx->hook_meta = NULL;
8395a5609c7SNiels Provos 	ctx->evcon = NULL;
84049868b61SNick Mathewson 	ctx->name = mm_strdup(rpcname);
8415a5609c7SNiels Provos 	if (ctx->name == NULL) {
84249868b61SNick Mathewson 		mm_free(ctx);
8435a5609c7SNiels Provos 		return (NULL);
8445a5609c7SNiels Provos 	}
8455a5609c7SNiels Provos 	ctx->cb = cb;
8465a5609c7SNiels Provos 	ctx->cb_arg = cbarg;
8475a5609c7SNiels Provos 	ctx->request = request;
8485a5609c7SNiels Provos 	ctx->reply = reply;
8495a5609c7SNiels Provos 	ctx->request_marshal = req_marshal;
8505a5609c7SNiels Provos 	ctx->reply_clear = rpl_clear;
8515a5609c7SNiels Provos 	ctx->reply_unmarshal = rpl_unmarshal;
8525a5609c7SNiels Provos 
8535a5609c7SNiels Provos 	return (ctx);
8545a5609c7SNiels Provos }
8555a5609c7SNiels Provos 
856fda1216bSNiels Provos static void
857819d4a33SNiels Provos evrpc_reply_done_closure(void *, enum EVRPC_HOOK_RESULT);
858819d4a33SNiels Provos 
859819d4a33SNiels Provos static void
evrpc_reply_done(struct evhttp_request * req,void * arg)860fda1216bSNiels Provos evrpc_reply_done(struct evhttp_request *req, void *arg)
861fda1216bSNiels Provos {
862fda1216bSNiels Provos 	struct evrpc_request_wrapper *ctx = arg;
8632d028ef6SNiels Provos 	struct evrpc_pool *pool = ctx->pool;
8642460aa59SNiels Provos 	int hook_res = EVRPC_CONTINUE;
8652d028ef6SNiels Provos 
8662d028ef6SNiels Provos 	/* cancel any timeout we might have scheduled */
8672d028ef6SNiels Provos 	event_del(&ctx->ev_timeout);
868ff43ed5bSNiels Provos 
869819d4a33SNiels Provos 	ctx->req = req;
870819d4a33SNiels Provos 
871819d4a33SNiels Provos 	/* we need to get the reply now */
872819d4a33SNiels Provos 	if (req == NULL) {
873819d4a33SNiels Provos 		evrpc_reply_done_closure(ctx, EVRPC_CONTINUE);
874819d4a33SNiels Provos 		return;
875819d4a33SNiels Provos 	}
876819d4a33SNiels Provos 
8772460aa59SNiels Provos 	if (TAILQ_FIRST(&pool->input_hooks) != NULL) {
8788ac3c4c2SNick Mathewson 		evrpc_hook_associate_meta_(&ctx->hook_meta, ctx->evcon);
8792460aa59SNiels Provos 
880819d4a33SNiels Provos 		/* apply hooks to the incoming request */
881819d4a33SNiels Provos 		hook_res = evrpc_process_hooks(&pool->input_hooks,
882819d4a33SNiels Provos 		    ctx, req, req->input_buffer);
883819d4a33SNiels Provos 
884819d4a33SNiels Provos 		switch (hook_res) {
885819d4a33SNiels Provos 		case EVRPC_TERMINATE:
886819d4a33SNiels Provos 		case EVRPC_CONTINUE:
8872460aa59SNiels Provos 			break;
888819d4a33SNiels Provos 		case EVRPC_PAUSE:
889955c6abfSNiels Provos 			/*
8902460aa59SNiels Provos 			 * if we get paused we also need to know the
8912460aa59SNiels Provos 			 * request.  unfortunately, the underlying
8922460aa59SNiels Provos 			 * layer is going to free it.  we need to
8932460aa59SNiels Provos 			 * request ownership explicitly
894955c6abfSNiels Provos 			 */
895955c6abfSNiels Provos 			evhttp_request_own(req);
896955c6abfSNiels Provos 
8972460aa59SNiels Provos 			evrpc_pause_request(pool, ctx,
8982460aa59SNiels Provos 			    evrpc_reply_done_closure);
899819d4a33SNiels Provos 			return;
900819d4a33SNiels Provos 		default:
9012e36dbe1SNick Mathewson 			EVUTIL_ASSERT(hook_res == EVRPC_TERMINATE ||
9022460aa59SNiels Provos 			    hook_res == EVRPC_CONTINUE ||
9032460aa59SNiels Provos 			    hook_res == EVRPC_PAUSE);
904819d4a33SNiels Provos 		}
9052460aa59SNiels Provos 	}
9062460aa59SNiels Provos 
9072460aa59SNiels Provos 	evrpc_reply_done_closure(ctx, hook_res);
908955c6abfSNiels Provos 
909955c6abfSNiels Provos 	/* http request is being freed by underlying layer */
910819d4a33SNiels Provos }
911819d4a33SNiels Provos 
912819d4a33SNiels Provos static void
evrpc_reply_done_closure(void * arg,enum EVRPC_HOOK_RESULT hook_res)913819d4a33SNiels Provos evrpc_reply_done_closure(void *arg, enum EVRPC_HOOK_RESULT hook_res)
914819d4a33SNiels Provos {
915819d4a33SNiels Provos 	struct evrpc_request_wrapper *ctx = arg;
916819d4a33SNiels Provos 	struct evhttp_request *req = ctx->req;
917819d4a33SNiels Provos 	struct evrpc_pool *pool = ctx->pool;
918819d4a33SNiels Provos 	struct evrpc_status status;
919819d4a33SNiels Provos 	int res = -1;
920819d4a33SNiels Provos 
9213794534fSNiels Provos 	memset(&status, 0, sizeof(status));
92265236aa8SNiels Provos 	status.http_req = req;
92365236aa8SNiels Provos 
924ff43ed5bSNiels Provos 	/* we need to get the reply now */
925819d4a33SNiels Provos 	if (req == NULL) {
9263794534fSNiels Provos 		status.error = EVRPC_STATUS_ERR_TIMEOUT;
927819d4a33SNiels Provos 	} else if (hook_res == EVRPC_TERMINATE) {
928819d4a33SNiels Provos 		status.error = EVRPC_STATUS_ERR_HOOKABORTED;
929819d4a33SNiels Provos 	} else {
930819d4a33SNiels Provos 		res = ctx->reply_unmarshal(ctx->reply, req->input_buffer);
931819d4a33SNiels Provos 		if (res == -1)
932819d4a33SNiels Provos 			status.error = EVRPC_STATUS_ERR_BADPAYLOAD;
9333794534fSNiels Provos 	}
9341d3a008aSNiels Provos 
9353794534fSNiels Provos 	if (res == -1) {
936ff43ed5bSNiels Provos 		/* clear everything that we might have written previously */
937ff43ed5bSNiels Provos 		ctx->reply_clear(ctx->reply);
938ff43ed5bSNiels Provos 	}
939ff43ed5bSNiels Provos 
9403794534fSNiels Provos 	(*ctx->cb)(&status, ctx->request, ctx->reply, ctx->cb_arg);
941ff43ed5bSNiels Provos 
942ff43ed5bSNiels Provos 	evrpc_request_wrapper_free(ctx);
943ff43ed5bSNiels Provos 
944e3fd294aSNick Mathewson 	/* the http layer owned the original request structure, but if we
945955c6abfSNiels Provos 	 * got paused, we asked for ownership and need to free it here. */
946955c6abfSNiels Provos 	if (req != NULL && evhttp_request_is_owned(req))
947955c6abfSNiels Provos 		evhttp_request_free(req);
9482d028ef6SNiels Provos 
9492d028ef6SNiels Provos 	/* see if we can schedule another request */
9502d028ef6SNiels Provos 	evrpc_pool_schedule(pool);
9512d028ef6SNiels Provos }
9522d028ef6SNiels Provos 
9532d028ef6SNiels Provos static void
evrpc_pool_schedule(struct evrpc_pool * pool)9542d028ef6SNiels Provos evrpc_pool_schedule(struct evrpc_pool *pool)
9552d028ef6SNiels Provos {
9562d028ef6SNiels Provos 	struct evrpc_request_wrapper *ctx = TAILQ_FIRST(&pool->requests);
9572d028ef6SNiels Provos 	struct evhttp_connection *evcon;
9582d028ef6SNiels Provos 
9592d028ef6SNiels Provos 	/* if no requests are pending, we have no work */
9602d028ef6SNiels Provos 	if (ctx == NULL)
9612d028ef6SNiels Provos 		return;
9622d028ef6SNiels Provos 
9632d028ef6SNiels Provos 	if ((evcon = evrpc_pool_find_connection(pool)) != NULL) {
9642d028ef6SNiels Provos 		TAILQ_REMOVE(&pool->requests, ctx, next);
9652d028ef6SNiels Provos 		evrpc_schedule_request(evcon, ctx);
9662d028ef6SNiels Provos 	}
9672d028ef6SNiels Provos }
9682d028ef6SNiels Provos 
9692d028ef6SNiels Provos static void
evrpc_request_timeout(evutil_socket_t fd,short what,void * arg)9701120f04fSNick Mathewson evrpc_request_timeout(evutil_socket_t fd, short what, void *arg)
9712d028ef6SNiels Provos {
9722d028ef6SNiels Provos 	struct evrpc_request_wrapper *ctx = arg;
9732d028ef6SNiels Provos 	struct evhttp_connection *evcon = ctx->evcon;
9742e36dbe1SNick Mathewson 	EVUTIL_ASSERT(evcon != NULL);
9752d028ef6SNiels Provos 
9767b077194SAzat Khuzhin 	evhttp_connection_fail_(evcon, EVREQ_HTTP_TIMEOUT);
977fda1216bSNiels Provos }
9785a5609c7SNiels Provos 
9795a5609c7SNiels Provos /*
9805a5609c7SNiels Provos  * frees potential meta data associated with a request.
9815a5609c7SNiels Provos  */
9825a5609c7SNiels Provos 
9835a5609c7SNiels Provos static void
evrpc_meta_data_free(struct evrpc_meta_list * meta_data)9845a5609c7SNiels Provos evrpc_meta_data_free(struct evrpc_meta_list *meta_data)
9855a5609c7SNiels Provos {
9865a5609c7SNiels Provos 	struct evrpc_meta *entry;
9872e36dbe1SNick Mathewson 	EVUTIL_ASSERT(meta_data != NULL);
9885a5609c7SNiels Provos 
9895a5609c7SNiels Provos 	while ((entry = TAILQ_FIRST(meta_data)) != NULL) {
9905a5609c7SNiels Provos 		TAILQ_REMOVE(meta_data, entry, next);
99149868b61SNick Mathewson 		mm_free(entry->key);
99249868b61SNick Mathewson 		mm_free(entry->data);
99349868b61SNick Mathewson 		mm_free(entry);
9945a5609c7SNiels Provos 	}
9955a5609c7SNiels Provos }
9965a5609c7SNiels Provos 
9972460aa59SNiels Provos static struct evrpc_hook_meta *
evrpc_hook_meta_new_(void)9988ac3c4c2SNick Mathewson evrpc_hook_meta_new_(void)
9992460aa59SNiels Provos {
10002460aa59SNiels Provos 	struct evrpc_hook_meta *ctx;
100149868b61SNick Mathewson 	ctx = mm_malloc(sizeof(struct evrpc_hook_meta));
10022e36dbe1SNick Mathewson 	EVUTIL_ASSERT(ctx != NULL);
10032460aa59SNiels Provos 
10042460aa59SNiels Provos 	TAILQ_INIT(&ctx->meta_data);
10052460aa59SNiels Provos 	ctx->evcon = NULL;
10062460aa59SNiels Provos 
10072460aa59SNiels Provos 	return (ctx);
10082460aa59SNiels Provos }
10092460aa59SNiels Provos 
10102460aa59SNiels Provos static void
evrpc_hook_associate_meta_(struct evrpc_hook_meta ** pctx,struct evhttp_connection * evcon)10118ac3c4c2SNick Mathewson evrpc_hook_associate_meta_(struct evrpc_hook_meta **pctx,
10122460aa59SNiels Provos     struct evhttp_connection *evcon)
10132460aa59SNiels Provos {
10142460aa59SNiels Provos 	struct evrpc_hook_meta *ctx = *pctx;
10152460aa59SNiels Provos 	if (ctx == NULL)
10168ac3c4c2SNick Mathewson 		*pctx = ctx = evrpc_hook_meta_new_();
10172460aa59SNiels Provos 	ctx->evcon = evcon;
10182460aa59SNiels Provos }
10192460aa59SNiels Provos 
10202460aa59SNiels Provos static void
evrpc_hook_context_free_(struct evrpc_hook_meta * ctx)10218ac3c4c2SNick Mathewson evrpc_hook_context_free_(struct evrpc_hook_meta *ctx)
10222460aa59SNiels Provos {
10232460aa59SNiels Provos 	evrpc_meta_data_free(&ctx->meta_data);
102449868b61SNick Mathewson 	mm_free(ctx);
10252460aa59SNiels Provos }
10262460aa59SNiels Provos 
10272460aa59SNiels Provos /* Adds meta data */
10285a5609c7SNiels Provos void
evrpc_hook_add_meta(void * ctx,const char * key,const void * data,size_t data_size)10295a5609c7SNiels Provos evrpc_hook_add_meta(void *ctx, const char *key,
10305a5609c7SNiels Provos     const void *data, size_t data_size)
10315a5609c7SNiels Provos {
10325a5609c7SNiels Provos 	struct evrpc_request_wrapper *req = ctx;
10332460aa59SNiels Provos 	struct evrpc_hook_meta *store = NULL;
10345a5609c7SNiels Provos 	struct evrpc_meta *meta = NULL;
10355a5609c7SNiels Provos 
10362460aa59SNiels Provos 	if ((store = req->hook_meta) == NULL)
10378ac3c4c2SNick Mathewson 		store = req->hook_meta = evrpc_hook_meta_new_();
10385a5609c7SNiels Provos 
1039743f8665SNick Mathewson 	meta = mm_malloc(sizeof(struct evrpc_meta));
1040743f8665SNick Mathewson 	EVUTIL_ASSERT(meta != NULL);
1041743f8665SNick Mathewson 	meta->key = mm_strdup(key);
1042743f8665SNick Mathewson 	EVUTIL_ASSERT(meta->key != NULL);
10435a5609c7SNiels Provos 	meta->data_size = data_size;
1044743f8665SNick Mathewson 	meta->data = mm_malloc(data_size);
1045743f8665SNick Mathewson 	EVUTIL_ASSERT(meta->data != NULL);
10465a5609c7SNiels Provos 	memcpy(meta->data, data, data_size);
10475a5609c7SNiels Provos 
10482460aa59SNiels Provos 	TAILQ_INSERT_TAIL(&store->meta_data, meta, next);
10495a5609c7SNiels Provos }
10505a5609c7SNiels Provos 
10515a5609c7SNiels Provos int
evrpc_hook_find_meta(void * ctx,const char * key,void ** data,size_t * data_size)10525a5609c7SNiels Provos evrpc_hook_find_meta(void *ctx, const char *key, void **data, size_t *data_size)
10535a5609c7SNiels Provos {
10545a5609c7SNiels Provos 	struct evrpc_request_wrapper *req = ctx;
10555a5609c7SNiels Provos 	struct evrpc_meta *meta = NULL;
10565a5609c7SNiels Provos 
10572460aa59SNiels Provos 	if (req->hook_meta == NULL)
10585a5609c7SNiels Provos 		return (-1);
10595a5609c7SNiels Provos 
10602460aa59SNiels Provos 	TAILQ_FOREACH(meta, &req->hook_meta->meta_data, next) {
10615a5609c7SNiels Provos 		if (strcmp(meta->key, key) == 0) {
10625a5609c7SNiels Provos 			*data = meta->data;
10635a5609c7SNiels Provos 			*data_size = meta->data_size;
10645a5609c7SNiels Provos 			return (0);
10655a5609c7SNiels Provos 		}
10665a5609c7SNiels Provos 	}
10675a5609c7SNiels Provos 
10685a5609c7SNiels Provos 	return (-1);
10695a5609c7SNiels Provos }
10702460aa59SNiels Provos 
10712460aa59SNiels Provos struct evhttp_connection *
evrpc_hook_get_connection(void * ctx)10722460aa59SNiels Provos evrpc_hook_get_connection(void *ctx)
10732460aa59SNiels Provos {
10742460aa59SNiels Provos 	struct evrpc_request_wrapper *req = ctx;
1075a83caa6bSNiels Provos 	return (req->hook_meta != NULL ? req->hook_meta->evcon : NULL);
10762460aa59SNiels Provos }
1077aa4b9257SNiels Provos 
1078a146af1dSNiels Provos int
evrpc_send_request_generic(struct evrpc_pool * pool,void * request,void * reply,void (* cb)(struct evrpc_status *,void *,void *,void *),void * cb_arg,const char * rpcname,void (* req_marshal)(struct evbuffer *,void *),void (* rpl_clear)(void *),int (* rpl_unmarshal)(void *,struct evbuffer *))1079a146af1dSNiels Provos evrpc_send_request_generic(struct evrpc_pool *pool,
1080a146af1dSNiels Provos     void *request, void *reply,
1081a146af1dSNiels Provos     void (*cb)(struct evrpc_status *, void *, void *, void *),
1082a146af1dSNiels Provos     void *cb_arg,
1083a146af1dSNiels Provos     const char *rpcname,
1084a146af1dSNiels Provos     void (*req_marshal)(struct evbuffer *, void *),
1085a146af1dSNiels Provos     void (*rpl_clear)(void *),
1086a146af1dSNiels Provos     int (*rpl_unmarshal)(void *, struct evbuffer *))
1087a146af1dSNiels Provos {
1088a146af1dSNiels Provos 	struct evrpc_status status;
1089a146af1dSNiels Provos 	struct evrpc_request_wrapper *ctx;
1090a146af1dSNiels Provos 	ctx = evrpc_make_request_ctx(pool, request, reply,
1091a146af1dSNiels Provos 	    rpcname, req_marshal, rpl_clear, rpl_unmarshal, cb, cb_arg);
1092a146af1dSNiels Provos 	if (ctx == NULL)
1093a146af1dSNiels Provos 		goto error;
1094a146af1dSNiels Provos 	return (evrpc_make_request(ctx));
1095a146af1dSNiels Provos error:
1096a146af1dSNiels Provos 	memset(&status, 0, sizeof(status));
1097a146af1dSNiels Provos 	status.error = EVRPC_STATUS_ERR_UNSTARTED;
1098a146af1dSNiels Provos 	(*(cb))(&status, request, reply, cb_arg);
1099a146af1dSNiels Provos 	return (-1);
1100a146af1dSNiels Provos }
1101a146af1dSNiels Provos 
1102a146af1dSNiels Provos /** Takes a request object and fills it in with the right magic */
1103a146af1dSNiels Provos static struct evrpc *
evrpc_register_object(const char * name,void * (* req_new)(void *),void * req_new_arg,void (* req_free)(void *),int (* req_unmarshal)(void *,struct evbuffer *),void * (* rpl_new)(void *),void * rpl_new_arg,void (* rpl_free)(void *),int (* rpl_complete)(void *),void (* rpl_marshal)(struct evbuffer *,void *))1104a146af1dSNiels Provos evrpc_register_object(const char *name,
1105755fbf16SShuo Chen     void *(*req_new)(void*), void *req_new_arg, void (*req_free)(void *),
1106a146af1dSNiels Provos     int (*req_unmarshal)(void *, struct evbuffer *),
1107755fbf16SShuo Chen     void *(*rpl_new)(void*), void *rpl_new_arg, void (*rpl_free)(void *),
1108a146af1dSNiels Provos     int (*rpl_complete)(void *),
1109a146af1dSNiels Provos     void (*rpl_marshal)(struct evbuffer *, void *))
1110a146af1dSNiels Provos {
1111a146af1dSNiels Provos 	struct evrpc* rpc = (struct evrpc *)mm_calloc(1, sizeof(struct evrpc));
1112a146af1dSNiels Provos 	if (rpc == NULL)
1113a146af1dSNiels Provos 		return (NULL);
1114a146af1dSNiels Provos 	rpc->uri = mm_strdup(name);
1115a146af1dSNiels Provos 	if (rpc->uri == NULL) {
1116a146af1dSNiels Provos 		mm_free(rpc);
1117a146af1dSNiels Provos 		return (NULL);
1118a146af1dSNiels Provos 	}
1119a146af1dSNiels Provos 	rpc->request_new = req_new;
1120755fbf16SShuo Chen 	rpc->request_new_arg = req_new_arg;
1121a146af1dSNiels Provos 	rpc->request_free = req_free;
1122a146af1dSNiels Provos 	rpc->request_unmarshal = req_unmarshal;
1123a146af1dSNiels Provos 	rpc->reply_new = rpl_new;
1124755fbf16SShuo Chen 	rpc->reply_new_arg = rpl_new_arg;
1125a146af1dSNiels Provos 	rpc->reply_free = rpl_free;
1126a146af1dSNiels Provos 	rpc->reply_complete = rpl_complete;
1127a146af1dSNiels Provos 	rpc->reply_marshal = rpl_marshal;
1128a146af1dSNiels Provos 	return (rpc);
1129a146af1dSNiels Provos }
1130a146af1dSNiels Provos 
1131a146af1dSNiels Provos int
evrpc_register_generic(struct evrpc_base * base,const char * name,void (* callback)(struct evrpc_req_generic *,void *),void * cbarg,void * (* req_new)(void *),void * req_new_arg,void (* req_free)(void *),int (* req_unmarshal)(void *,struct evbuffer *),void * (* rpl_new)(void *),void * rpl_new_arg,void (* rpl_free)(void *),int (* rpl_complete)(void *),void (* rpl_marshal)(struct evbuffer *,void *))1132a146af1dSNiels Provos evrpc_register_generic(struct evrpc_base *base, const char *name,
1133a146af1dSNiels Provos     void (*callback)(struct evrpc_req_generic *, void *), void *cbarg,
1134755fbf16SShuo Chen     void *(*req_new)(void *), void *req_new_arg, void (*req_free)(void *),
1135a146af1dSNiels Provos     int (*req_unmarshal)(void *, struct evbuffer *),
1136755fbf16SShuo Chen     void *(*rpl_new)(void *), void *rpl_new_arg, void (*rpl_free)(void *),
1137a146af1dSNiels Provos     int (*rpl_complete)(void *),
1138a146af1dSNiels Provos     void (*rpl_marshal)(struct evbuffer *, void *))
1139a146af1dSNiels Provos {
1140a146af1dSNiels Provos 	struct evrpc* rpc =
1141755fbf16SShuo Chen 	    evrpc_register_object(name, req_new, req_new_arg, req_free, req_unmarshal,
1142755fbf16SShuo Chen 		rpl_new, rpl_new_arg, rpl_free, rpl_complete, rpl_marshal);
1143a146af1dSNiels Provos 	if (rpc == NULL)
1144a146af1dSNiels Provos 		return (-1);
1145a146af1dSNiels Provos 	evrpc_register_rpc(base, rpc,
1146a146af1dSNiels Provos 	    (void (*)(struct evrpc_req_generic*, void *))callback, cbarg);
1147a146af1dSNiels Provos 	return (0);
1148a146af1dSNiels Provos }
1149a146af1dSNiels Provos 
1150aa4b9257SNiels Provos /** accessors for obscure and undocumented functionality */
1151aa4b9257SNiels Provos struct evrpc_pool *
evrpc_request_get_pool(struct evrpc_request_wrapper * ctx)1152aa4b9257SNiels Provos evrpc_request_get_pool(struct evrpc_request_wrapper *ctx)
1153aa4b9257SNiels Provos {
1154aa4b9257SNiels Provos 	return (ctx->pool);
1155aa4b9257SNiels Provos }
1156aa4b9257SNiels Provos 
1157aa4b9257SNiels Provos void
evrpc_request_set_pool(struct evrpc_request_wrapper * ctx,struct evrpc_pool * pool)1158aa4b9257SNiels Provos evrpc_request_set_pool(struct evrpc_request_wrapper *ctx,
1159aa4b9257SNiels Provos     struct evrpc_pool *pool)
1160aa4b9257SNiels Provos {
1161aa4b9257SNiels Provos 	ctx->pool = pool;
1162aa4b9257SNiels Provos }
1163aa4b9257SNiels Provos 
1164aa4b9257SNiels Provos void
evrpc_request_set_cb(struct evrpc_request_wrapper * ctx,void (* cb)(struct evrpc_status *,void * request,void * reply,void * arg),void * cb_arg)1165aa4b9257SNiels Provos evrpc_request_set_cb(struct evrpc_request_wrapper *ctx,
1166aa4b9257SNiels Provos     void (*cb)(struct evrpc_status*, void *request, void *reply, void *arg),
1167aa4b9257SNiels Provos     void *cb_arg)
1168aa4b9257SNiels Provos {
1169aa4b9257SNiels Provos 	ctx->cb = cb;
1170aa4b9257SNiels Provos 	ctx->cb_arg = cb_arg;
1171aa4b9257SNiels Provos }
1172