/* * Copyright (c) 2000-2007 Niels Provos <provos@citi.umich.edu> * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ #include"event2/event-config.h" #include"evconfig-private.h"
/* * Registers a new RPC with the HTTP server. The evrpc object is expected * to have been filled in via the EVRPC_REGISTER_OBJECT macro which in turn * calls this function.
*/
int
evrpc_unregister_rpc(struct evrpc_base *base, constchar *name)
{ char *registered_uri = NULL; struct evrpc *rpc; int r;
/* find the right rpc; linear search might be slow */
TAILQ_FOREACH(rpc, &base->registered_rpcs, next) { if (strcmp(rpc->uri, name) == 0) break;
} if (rpc == NULL) { /* We did not find an RPC with this name */ return (-1);
}
TAILQ_REMOVE(&base->registered_rpcs, rpc, next);
registered_uri = evrpc_construct_uri(name);
/* remove the http server callback */
r = evhttp_del_cb(base->http_server, registered_uri);
EVUTIL_ASSERT(r == 0);
/* let's check that we can parse the request */
rpc_state->request = rpc->request_new(rpc->request_new_arg); if (rpc_state->request == NULL) goto error;
if (rpc->request_unmarshal(
rpc_state->request, req->input_buffer) == -1) { /* we failed to parse the request; that's a bummer */ goto error;
}
/* at this point, we have a well formed request, prepare the reply */
rpc_state->reply = rpc->reply_new(rpc->reply_new_arg); if (rpc_state->reply == NULL) goto error;
/* give the rpc to the user; they can deal with it */
rpc->cb(rpc_state, rpc->cb_arg);
/* clean up all memory */ if (rpc_state->hook_meta != NULL)
evrpc_hook_context_free_(rpc_state->hook_meta); if (rpc_state->request != NULL)
rpc->request_free(rpc_state->request); if (rpc_state->reply != NULL)
rpc->reply_free(rpc_state->reply); if (rpc_state->rpc_data != NULL)
evbuffer_free(rpc_state->rpc_data);
mm_free(rpc_state);
}
/* on success, we are going to transmit marshaled binary data */ if (evhttp_find_header(req->output_headers, "Content-Type") == NULL) {
evhttp_add_header(req->output_headers, "Content-Type", "application/octet-stream");
}
evhttp_send_reply(req, HTTP_OK, "OK", rpc_state->rpc_data);
/* * associate an event base with this connection
*/ if (pool->base != NULL)
evhttp_connection_set_base(connection, pool->base);
/* * unless a timeout was specifically set for a connection, * the connection inherits the timeout from the pool.
*/ if (!evutil_timerisset(&connection->timeout))
evhttp_connection_set_timeout(connection, pool->timeout);
/* * if we have any requests pending, schedule them with the new * connections.
*/
/* * Finds a connection object associated with the pool that is currently * idle and can be used to make a request.
*/ staticstruct evhttp_connection *
evrpc_pool_find_connection(struct evrpc_pool *pool)
{ struct evhttp_connection *connection;
TAILQ_FOREACH(connection, &pool->connections, next) { if (TAILQ_FIRST(&connection->requests) == NULL) return (connection);
}
return (NULL);
}
/* * Prototypes responsible for evrpc scheduling and hooking
*/
/* * We assume that the ctx is no longer queued on the pool.
*/ staticint
evrpc_schedule_request(struct evhttp_connection *connection, struct evrpc_request_wrapper *ctx)
{ struct evhttp_request *req = NULL; struct evrpc_pool *pool = ctx->pool; struct evrpc_status status;
if ((req = evhttp_request_new(evrpc_reply_done, ctx)) == NULL) goto error;
/* serialize the request data into the output buffer */
ctx->request_marshal(req->output_buffer, ctx->request);
/* we need to know the connection that we might have to abort */
ctx->evcon = connection;
/* if we get paused we also need to know the request */
ctx->req = req;
if (TAILQ_FIRST(&pool->output_hooks) != NULL) { int hook_res;
/* apply hooks to the outgoing request */
hook_res = evrpc_process_hooks(&pool->output_hooks,
ctx, req, req->output_buffer);
switch (hook_res) { case EVRPC_TERMINATE: goto error; case EVRPC_PAUSE: /* we need to be explicitly resumed */ if (evrpc_pause_request(pool, ctx,
evrpc_schedule_request_closure) == -1) goto error; return (0); case EVRPC_CONTINUE: /* we can just continue */ break; default:
EVUTIL_ASSERT(hook_res == EVRPC_TERMINATE ||
hook_res == EVRPC_CONTINUE ||
hook_res == EVRPC_PAUSE);
}
}
uri = evrpc_construct_uri(ctx->name); if (uri == NULL) goto error;
if (pool->timeout > 0) { /* * a timeout after which the whole rpc is going to be aborted.
*/ struct timeval tv;
evutil_timerclear(&tv);
tv.tv_sec = pool->timeout;
evtimer_add(&ctx->ev_timeout, &tv);
}
/* start the request over the connection */
res = evhttp_make_request(connection, req, EVHTTP_REQ_POST, uri);
mm_free(uri);
/* we just queue the paused request on the pool under the req object */ staticint
evrpc_pause_request(void *vbase, void *ctx, void (*cb)(void *, enum EVRPC_HOOK_RESULT))
{ struct evrpc_hooks_ *base = vbase; struct evrpc_hook_ctx *pause = mm_malloc(sizeof(*pause)); if (pause == NULL) return (-1);
int
evrpc_make_request(struct evrpc_request_wrapper *ctx)
{ struct evrpc_pool *pool = ctx->pool;
/* initialize the event structure for this rpc */
evtimer_assign(&ctx->ev_timeout, pool->base, evrpc_request_timeout, ctx);
/* we better have some available connections on the pool */
EVUTIL_ASSERT(TAILQ_FIRST(&pool->connections) != NULL);
/* * if no connection is available, we queue the request on the pool, * the next time a connection is empty, the rpc will be send on that.
*/
TAILQ_INSERT_TAIL(&pool->requests, ctx, next);
/* cancel any timeout we might have scheduled */
event_del(&ctx->ev_timeout);
ctx->req = req;
/* we need to get the reply now */ if (req == NULL) {
evrpc_reply_done_closure(ctx, EVRPC_CONTINUE); return;
}
if (TAILQ_FIRST(&pool->input_hooks) != NULL) {
evrpc_hook_associate_meta_(&ctx->hook_meta, ctx->evcon);
/* apply hooks to the incoming request */
hook_res = evrpc_process_hooks(&pool->input_hooks,
ctx, req, req->input_buffer);
switch (hook_res) { case EVRPC_TERMINATE: case EVRPC_CONTINUE: break; case EVRPC_PAUSE: /* * if we get paused we also need to know the * request. unfortunately, the underlying * layer is going to free it. we need to * request ownership explicitly
*/
evhttp_request_own(req);
/* we need to get the reply now */ if (req == NULL) {
status.error = EVRPC_STATUS_ERR_TIMEOUT;
} elseif (hook_res == EVRPC_TERMINATE) {
status.error = EVRPC_STATUS_ERR_HOOKABORTED;
} else {
res = ctx->reply_unmarshal(ctx->reply, req->input_buffer); if (res == -1)
status.error = EVRPC_STATUS_ERR_BADPAYLOAD;
}
if (res == -1) { /* clear everything that we might have written previously */
ctx->reply_clear(ctx->reply);
}
/* the http layer owned the original request structure, but if we
* got paused, we asked for ownership and need to free it here. */ if (req != NULL && evhttp_request_is_owned(req))
evhttp_request_free(req);
/* see if we can schedule another request */
evrpc_pool_schedule(pool);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.