mirror of
https://github.com/libevent/libevent.git
synced 2025-01-09 00:56:20 +08:00
8ac3c4c25b
We haven't had a convention for naming internal functions in -internal.h versus naming visible functions in include/**.h. This patch changes every function declared in a -internal.h file to be named ending with an underscore. Static function names are unaffected, since there's no risk of calling them from outside Libevent. This is an automatic conversion. The script that produced was made by running the following script over the output of ctags --c-kinds=pf -o - *-internal.h | cut -f 1 | sort| uniq (GNU ctags was required.) ===== #!/usr/bin/perl -w -n use strict; BEGIN { print "#!/usr/bin/perl -w -i -p\n\n"; } chomp; my $ident = $_; next if ($ident =~ /_$/); next if ($ident =~ /^TAILQ/); my $better = "${ident}_"; print "s/(?<![A-Za-z0-9_])$ident(?![A-Za-z0-9_])/$better/g;\n"; === And then running the script below that it generated over all === the .c and .h files again #!/usr/bin/perl -w -i -p s/(?<![A-Za-z0-9_])bufferevent_async_can_connect(?![A-Za-z0-9_])/bufferevent_async_can_connect_/g; s/(?<![A-Za-z0-9_])bufferevent_async_connect(?![A-Za-z0-9_])/bufferevent_async_connect_/g; s/(?<![A-Za-z0-9_])bufferevent_async_new(?![A-Za-z0-9_])/bufferevent_async_new_/g; s/(?<![A-Za-z0-9_])bufferevent_async_set_connected(?![A-Za-z0-9_])/bufferevent_async_set_connected_/g; s/(?<![A-Za-z0-9_])bufferevent_decref(?![A-Za-z0-9_])/bufferevent_decref_/g; s/(?<![A-Za-z0-9_])bufferevent_disable_hard(?![A-Za-z0-9_])/bufferevent_disable_hard_/g; s/(?<![A-Za-z0-9_])bufferevent_enable_locking(?![A-Za-z0-9_])/bufferevent_enable_locking_/g; s/(?<![A-Za-z0-9_])bufferevent_incref(?![A-Za-z0-9_])/bufferevent_incref_/g; s/(?<![A-Za-z0-9_])bufferevent_init_common(?![A-Za-z0-9_])/bufferevent_init_common_/g; s/(?<![A-Za-z0-9_])bufferevent_remove_from_rate_limit_group_internal(?![A-Za-z0-9_])/bufferevent_remove_from_rate_limit_group_internal_/g; s/(?<![A-Za-z0-9_])bufferevent_suspend_read(?![A-Za-z0-9_])/bufferevent_suspend_read_/g; s/(?<![A-Za-z0-9_])bufferevent_suspend_write(?![A-Za-z0-9_])/bufferevent_suspend_write_/g; s/(?<![A-Za-z0-9_])bufferevent_unsuspend_read(?![A-Za-z0-9_])/bufferevent_unsuspend_read_/g; s/(?<![A-Za-z0-9_])bufferevent_unsuspend_write(?![A-Za-z0-9_])/bufferevent_unsuspend_write_/g; s/(?<![A-Za-z0-9_])evbuffer_commit_read(?![A-Za-z0-9_])/evbuffer_commit_read_/g; s/(?<![A-Za-z0-9_])evbuffer_commit_write(?![A-Za-z0-9_])/evbuffer_commit_write_/g; s/(?<![A-Za-z0-9_])evbuffer_invoke_callbacks(?![A-Za-z0-9_])/evbuffer_invoke_callbacks_/g; s/(?<![A-Za-z0-9_])evbuffer_launch_read(?![A-Za-z0-9_])/evbuffer_launch_read_/g; s/(?<![A-Za-z0-9_])evbuffer_launch_write(?![A-Za-z0-9_])/evbuffer_launch_write_/g; s/(?<![A-Za-z0-9_])evbuffer_overlapped_new(?![A-Za-z0-9_])/evbuffer_overlapped_new_/g; s/(?<![A-Za-z0-9_])evbuffer_set_parent(?![A-Za-z0-9_])/evbuffer_set_parent_/g; s/(?<![A-Za-z0-9_])event_active_nolock(?![A-Za-z0-9_])/event_active_nolock_/g; s/(?<![A-Za-z0-9_])event_base_add_virtual(?![A-Za-z0-9_])/event_base_add_virtual_/g; s/(?<![A-Za-z0-9_])event_base_assert_ok(?![A-Za-z0-9_])/event_base_assert_ok_/g; s/(?<![A-Za-z0-9_])event_base_del_virtual(?![A-Za-z0-9_])/event_base_del_virtual_/g; s/(?<![A-Za-z0-9_])event_base_get_deferred_cb_queue(?![A-Za-z0-9_])/event_base_get_deferred_cb_queue_/g; s/(?<![A-Za-z0-9_])event_base_get_iocp(?![A-Za-z0-9_])/event_base_get_iocp_/g; s/(?<![A-Za-z0-9_])event_base_start_iocp(?![A-Za-z0-9_])/event_base_start_iocp_/g; s/(?<![A-Za-z0-9_])event_base_stop_iocp(?![A-Za-z0-9_])/event_base_stop_iocp_/g; s/(?<![A-Za-z0-9_])event_changelist_add(?![A-Za-z0-9_])/event_changelist_add_/g; s/(?<![A-Za-z0-9_])event_changelist_del(?![A-Za-z0-9_])/event_changelist_del_/g; s/(?<![A-Za-z0-9_])event_changelist_freemem(?![A-Za-z0-9_])/event_changelist_freemem_/g; s/(?<![A-Za-z0-9_])event_changelist_init(?![A-Za-z0-9_])/event_changelist_init_/g; s/(?<![A-Za-z0-9_])event_changelist_remove_all(?![A-Za-z0-9_])/event_changelist_remove_all_/g; s/(?<![A-Za-z0-9_])event_deferred_cb_cancel(?![A-Za-z0-9_])/event_deferred_cb_cancel_/g; s/(?<![A-Za-z0-9_])event_deferred_cb_init(?![A-Za-z0-9_])/event_deferred_cb_init_/g; s/(?<![A-Za-z0-9_])event_deferred_cb_queue_init(?![A-Za-z0-9_])/event_deferred_cb_queue_init_/g; s/(?<![A-Za-z0-9_])event_deferred_cb_schedule(?![A-Za-z0-9_])/event_deferred_cb_schedule_/g; s/(?<![A-Za-z0-9_])event_get_win32_extension_fns(?![A-Za-z0-9_])/event_get_win32_extension_fns_/g; s/(?<![A-Za-z0-9_])event_iocp_activate_overlapped(?![A-Za-z0-9_])/event_iocp_activate_overlapped_/g; s/(?<![A-Za-z0-9_])event_iocp_port_associate(?![A-Za-z0-9_])/event_iocp_port_associate_/g; s/(?<![A-Za-z0-9_])event_iocp_port_launch(?![A-Za-z0-9_])/event_iocp_port_launch_/g; s/(?<![A-Za-z0-9_])event_iocp_shutdown(?![A-Za-z0-9_])/event_iocp_shutdown_/g; s/(?<![A-Za-z0-9_])event_overlapped_init(?![A-Za-z0-9_])/event_overlapped_init_/g; s/(?<![A-Za-z0-9_])evhttp_connection_connect(?![A-Za-z0-9_])/evhttp_connection_connect_/g; s/(?<![A-Za-z0-9_])evhttp_connection_fail(?![A-Za-z0-9_])/evhttp_connection_fail_/g; s/(?<![A-Za-z0-9_])evhttp_connection_reset(?![A-Za-z0-9_])/evhttp_connection_reset_/g; s/(?<![A-Za-z0-9_])evhttp_parse_firstline(?![A-Za-z0-9_])/evhttp_parse_firstline_/g; s/(?<![A-Za-z0-9_])evhttp_parse_headers(?![A-Za-z0-9_])/evhttp_parse_headers_/g; s/(?<![A-Za-z0-9_])evhttp_response_code(?![A-Za-z0-9_])/evhttp_response_code_/g; s/(?<![A-Za-z0-9_])evhttp_send_page(?![A-Za-z0-9_])/evhttp_send_page_/g; s/(?<![A-Za-z0-9_])evhttp_start_read(?![A-Za-z0-9_])/evhttp_start_read_/g; s/(?<![A-Za-z0-9_])EVLOCK_TRY_LOCK(?![A-Za-z0-9_])/EVLOCK_TRY_LOCK_/g; s/(?<![A-Za-z0-9_])evmap_check_integrity(?![A-Za-z0-9_])/evmap_check_integrity_/g; s/(?<![A-Za-z0-9_])evmap_delete_all(?![A-Za-z0-9_])/evmap_delete_all_/g; s/(?<![A-Za-z0-9_])evmap_foreach_event(?![A-Za-z0-9_])/evmap_foreach_event_/g; s/(?<![A-Za-z0-9_])evmap_io_active(?![A-Za-z0-9_])/evmap_io_active_/g; s/(?<![A-Za-z0-9_])evmap_io_add(?![A-Za-z0-9_])/evmap_io_add_/g; s/(?<![A-Za-z0-9_])evmap_io_clear(?![A-Za-z0-9_])/evmap_io_clear_/g; s/(?<![A-Za-z0-9_])evmap_io_del(?![A-Za-z0-9_])/evmap_io_del_/g; s/(?<![A-Za-z0-9_])evmap_io_get_fdinfo(?![A-Za-z0-9_])/evmap_io_get_fdinfo_/g; s/(?<![A-Za-z0-9_])evmap_io_initmap(?![A-Za-z0-9_])/evmap_io_initmap_/g; s/(?<![A-Za-z0-9_])evmap_reinit(?![A-Za-z0-9_])/evmap_reinit_/g; s/(?<![A-Za-z0-9_])evmap_signal_active(?![A-Za-z0-9_])/evmap_signal_active_/g; s/(?<![A-Za-z0-9_])evmap_signal_add(?![A-Za-z0-9_])/evmap_signal_add_/g; s/(?<![A-Za-z0-9_])evmap_signal_clear(?![A-Za-z0-9_])/evmap_signal_clear_/g; s/(?<![A-Za-z0-9_])evmap_signal_del(?![A-Za-z0-9_])/evmap_signal_del_/g; s/(?<![A-Za-z0-9_])evmap_signal_initmap(?![A-Za-z0-9_])/evmap_signal_initmap_/g; s/(?<![A-Za-z0-9_])evrpc_hook_associate_meta(?![A-Za-z0-9_])/evrpc_hook_associate_meta_/g; s/(?<![A-Za-z0-9_])evrpc_hook_context_free(?![A-Za-z0-9_])/evrpc_hook_context_free_/g; s/(?<![A-Za-z0-9_])evrpc_hook_meta_new(?![A-Za-z0-9_])/evrpc_hook_meta_new_/g; s/(?<![A-Za-z0-9_])evrpc_reqstate_free(?![A-Za-z0-9_])/evrpc_reqstate_free_/g; s/(?<![A-Za-z0-9_])evsig_dealloc(?![A-Za-z0-9_])/evsig_dealloc_/g; s/(?<![A-Za-z0-9_])evsig_init(?![A-Za-z0-9_])/evsig_init_/g; s/(?<![A-Za-z0-9_])evsig_set_base(?![A-Za-z0-9_])/evsig_set_base_/g; s/(?<![A-Za-z0-9_])ev_token_bucket_get_tick(?![A-Za-z0-9_])/ev_token_bucket_get_tick_/g; s/(?<![A-Za-z0-9_])ev_token_bucket_init(?![A-Za-z0-9_])/ev_token_bucket_init_/g; s/(?<![A-Za-z0-9_])ev_token_bucket_update(?![A-Za-z0-9_])/ev_token_bucket_update_/g; s/(?<![A-Za-z0-9_])evutil_accept4(?![A-Za-z0-9_])/evutil_accept4_/g; s/(?<![A-Za-z0-9_])evutil_addrinfo_append(?![A-Za-z0-9_])/evutil_addrinfo_append_/g; s/(?<![A-Za-z0-9_])evutil_adjust_hints_for_addrconfig(?![A-Za-z0-9_])/evutil_adjust_hints_for_addrconfig_/g; s/(?<![A-Za-z0-9_])evutil_ersatz_socketpair(?![A-Za-z0-9_])/evutil_ersatz_socketpair_/g; s/(?<![A-Za-z0-9_])evutil_eventfd(?![A-Za-z0-9_])/evutil_eventfd_/g; s/(?<![A-Za-z0-9_])evutil_format_sockaddr_port(?![A-Za-z0-9_])/evutil_format_sockaddr_port_/g; s/(?<![A-Za-z0-9_])evutil_getaddrinfo_async(?![A-Za-z0-9_])/evutil_getaddrinfo_async_/g; s/(?<![A-Za-z0-9_])evutil_getaddrinfo_common(?![A-Za-z0-9_])/evutil_getaddrinfo_common_/g; s/(?<![A-Za-z0-9_])evutil_getenv(?![A-Za-z0-9_])/evutil_getenv_/g; s/(?<![A-Za-z0-9_])evutil_hex_char_to_int(?![A-Za-z0-9_])/evutil_hex_char_to_int_/g; s/(?<![A-Za-z0-9_])EVUTIL_ISALNUM(?![A-Za-z0-9_])/EVUTIL_ISALNUM_/g; s/(?<![A-Za-z0-9_])EVUTIL_ISALPHA(?![A-Za-z0-9_])/EVUTIL_ISALPHA_/g; s/(?<![A-Za-z0-9_])EVUTIL_ISDIGIT(?![A-Za-z0-9_])/EVUTIL_ISDIGIT_/g; s/(?<![A-Za-z0-9_])EVUTIL_ISLOWER(?![A-Za-z0-9_])/EVUTIL_ISLOWER_/g; s/(?<![A-Za-z0-9_])EVUTIL_ISPRINT(?![A-Za-z0-9_])/EVUTIL_ISPRINT_/g; s/(?<![A-Za-z0-9_])EVUTIL_ISSPACE(?![A-Za-z0-9_])/EVUTIL_ISSPACE_/g; s/(?<![A-Za-z0-9_])EVUTIL_ISUPPER(?![A-Za-z0-9_])/EVUTIL_ISUPPER_/g; s/(?<![A-Za-z0-9_])EVUTIL_ISXDIGIT(?![A-Za-z0-9_])/EVUTIL_ISXDIGIT_/g; s/(?<![A-Za-z0-9_])evutil_load_windows_system_library(?![A-Za-z0-9_])/evutil_load_windows_system_library_/g; s/(?<![A-Za-z0-9_])evutil_make_internal_pipe(?![A-Za-z0-9_])/evutil_make_internal_pipe_/g; s/(?<![A-Za-z0-9_])evutil_new_addrinfo(?![A-Za-z0-9_])/evutil_new_addrinfo_/g; s/(?<![A-Za-z0-9_])evutil_open_closeonexec(?![A-Za-z0-9_])/evutil_open_closeonexec_/g; s/(?<![A-Za-z0-9_])evutil_read_file(?![A-Za-z0-9_])/evutil_read_file_/g; s/(?<![A-Za-z0-9_])evutil_resolve(?![A-Za-z0-9_])/evutil_resolve_/g; s/(?<![A-Za-z0-9_])evutil_set_evdns_getaddrinfo_fn(?![A-Za-z0-9_])/evutil_set_evdns_getaddrinfo_fn_/g; s/(?<![A-Za-z0-9_])evutil_sockaddr_is_loopback(?![A-Za-z0-9_])/evutil_sockaddr_is_loopback_/g; s/(?<![A-Za-z0-9_])evutil_socket(?![A-Za-z0-9_])/evutil_socket_/g; s/(?<![A-Za-z0-9_])evutil_socket_connect(?![A-Za-z0-9_])/evutil_socket_connect_/g; s/(?<![A-Za-z0-9_])evutil_socket_finished_connecting(?![A-Za-z0-9_])/evutil_socket_finished_connecting_/g; s/(?<![A-Za-z0-9_])EVUTIL_TOLOWER(?![A-Za-z0-9_])/EVUTIL_TOLOWER_/g; s/(?<![A-Za-z0-9_])EVUTIL_TOUPPER(?![A-Za-z0-9_])/EVUTIL_TOUPPER_/g; s/(?<![A-Za-z0-9_])evutil_tv_to_msec(?![A-Za-z0-9_])/evutil_tv_to_msec_/g; s/(?<![A-Za-z0-9_])evutil_usleep(?![A-Za-z0-9_])/evutil_usleep_/g; s/(?<![A-Za-z0-9_])ht_improve_hash(?![A-Za-z0-9_])/ht_improve_hash_/g; s/(?<![A-Za-z0-9_])ht_string_hash(?![A-Za-z0-9_])/ht_string_hash_/g; s/(?<![A-Za-z0-9_])min_heap_adjust(?![A-Za-z0-9_])/min_heap_adjust_/g; s/(?<![A-Za-z0-9_])min_heap_ctor(?![A-Za-z0-9_])/min_heap_ctor_/g; s/(?<![A-Za-z0-9_])min_heap_dtor(?![A-Za-z0-9_])/min_heap_dtor_/g; s/(?<![A-Za-z0-9_])min_heap_elem_init(?![A-Za-z0-9_])/min_heap_elem_init_/g; s/(?<![A-Za-z0-9_])min_heap_elt_is_top(?![A-Za-z0-9_])/min_heap_elt_is_top_/g; s/(?<![A-Za-z0-9_])min_heap_empty(?![A-Za-z0-9_])/min_heap_empty_/g; s/(?<![A-Za-z0-9_])min_heap_erase(?![A-Za-z0-9_])/min_heap_erase_/g; s/(?<![A-Za-z0-9_])min_heap_pop(?![A-Za-z0-9_])/min_heap_pop_/g; s/(?<![A-Za-z0-9_])min_heap_push(?![A-Za-z0-9_])/min_heap_push_/g; s/(?<![A-Za-z0-9_])min_heap_reserve(?![A-Za-z0-9_])/min_heap_reserve_/g; s/(?<![A-Za-z0-9_])min_heap_size(?![A-Za-z0-9_])/min_heap_size_/g; s/(?<![A-Za-z0-9_])min_heap_top(?![A-Za-z0-9_])/min_heap_top_/g;
1176 lines
29 KiB
C
1176 lines
29 KiB
C
/*
|
|
* 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"
|
|
|
|
#ifdef _WIN32
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#include <winsock2.h>
|
|
#include <windows.h>
|
|
#undef WIN32_LEAN_AND_MEAN
|
|
#endif
|
|
|
|
#include <sys/types.h>
|
|
#ifndef _WIN32
|
|
#include <sys/socket.h>
|
|
#endif
|
|
#ifdef EVENT__HAVE_SYS_TIME_H
|
|
#include <sys/time.h>
|
|
#endif
|
|
#include <sys/queue.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#ifndef _WIN32
|
|
#include <unistd.h>
|
|
#endif
|
|
#include <errno.h>
|
|
#include <signal.h>
|
|
#include <string.h>
|
|
|
|
#include <sys/queue.h>
|
|
|
|
#include "event2/event.h"
|
|
#include "event2/event_struct.h"
|
|
#include "event2/rpc.h"
|
|
#include "event2/rpc_struct.h"
|
|
#include "evrpc-internal.h"
|
|
#include "event2/http.h"
|
|
#include "event2/buffer.h"
|
|
#include "event2/tag.h"
|
|
#include "event2/http_struct.h"
|
|
#include "event2/http_compat.h"
|
|
#include "event2/util.h"
|
|
#include "util-internal.h"
|
|
#include "log-internal.h"
|
|
#include "mm-internal.h"
|
|
|
|
struct evrpc_base *
|
|
evrpc_init(struct evhttp *http_server)
|
|
{
|
|
struct evrpc_base* base = mm_calloc(1, sizeof(struct evrpc_base));
|
|
if (base == NULL)
|
|
return (NULL);
|
|
|
|
/* we rely on the tagging sub system */
|
|
evtag_init();
|
|
|
|
TAILQ_INIT(&base->registered_rpcs);
|
|
TAILQ_INIT(&base->input_hooks);
|
|
TAILQ_INIT(&base->output_hooks);
|
|
|
|
TAILQ_INIT(&base->paused_requests);
|
|
|
|
base->http_server = http_server;
|
|
|
|
return (base);
|
|
}
|
|
|
|
void
|
|
evrpc_free(struct evrpc_base *base)
|
|
{
|
|
struct evrpc *rpc;
|
|
struct evrpc_hook *hook;
|
|
struct evrpc_hook_ctx *pause;
|
|
int r;
|
|
|
|
while ((rpc = TAILQ_FIRST(&base->registered_rpcs)) != NULL) {
|
|
r = evrpc_unregister_rpc(base, rpc->uri);
|
|
EVUTIL_ASSERT(r == 0);
|
|
}
|
|
while ((pause = TAILQ_FIRST(&base->paused_requests)) != NULL) {
|
|
TAILQ_REMOVE(&base->paused_requests, pause, next);
|
|
mm_free(pause);
|
|
}
|
|
while ((hook = TAILQ_FIRST(&base->input_hooks)) != NULL) {
|
|
r = evrpc_remove_hook(base, EVRPC_INPUT, hook);
|
|
EVUTIL_ASSERT(r);
|
|
}
|
|
while ((hook = TAILQ_FIRST(&base->output_hooks)) != NULL) {
|
|
r = evrpc_remove_hook(base, EVRPC_OUTPUT, hook);
|
|
EVUTIL_ASSERT(r);
|
|
}
|
|
mm_free(base);
|
|
}
|
|
|
|
void *
|
|
evrpc_add_hook(void *vbase,
|
|
enum EVRPC_HOOK_TYPE hook_type,
|
|
int (*cb)(void *, struct evhttp_request *, struct evbuffer *, void *),
|
|
void *cb_arg)
|
|
{
|
|
struct evrpc_hooks_ *base = vbase;
|
|
struct evrpc_hook_list *head = NULL;
|
|
struct evrpc_hook *hook = NULL;
|
|
switch (hook_type) {
|
|
case EVRPC_INPUT:
|
|
head = &base->in_hooks;
|
|
break;
|
|
case EVRPC_OUTPUT:
|
|
head = &base->out_hooks;
|
|
break;
|
|
default:
|
|
EVUTIL_ASSERT(hook_type == EVRPC_INPUT || hook_type == EVRPC_OUTPUT);
|
|
}
|
|
|
|
hook = mm_calloc(1, sizeof(struct evrpc_hook));
|
|
EVUTIL_ASSERT(hook != NULL);
|
|
|
|
hook->process = cb;
|
|
hook->process_arg = cb_arg;
|
|
TAILQ_INSERT_TAIL(head, hook, next);
|
|
|
|
return (hook);
|
|
}
|
|
|
|
static int
|
|
evrpc_remove_hook_internal(struct evrpc_hook_list *head, void *handle)
|
|
{
|
|
struct evrpc_hook *hook = NULL;
|
|
TAILQ_FOREACH(hook, head, next) {
|
|
if (hook == handle) {
|
|
TAILQ_REMOVE(head, hook, next);
|
|
mm_free(hook);
|
|
return (1);
|
|
}
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* remove the hook specified by the handle
|
|
*/
|
|
|
|
int
|
|
evrpc_remove_hook(void *vbase, enum EVRPC_HOOK_TYPE hook_type, void *handle)
|
|
{
|
|
struct evrpc_hooks_ *base = vbase;
|
|
struct evrpc_hook_list *head = NULL;
|
|
switch (hook_type) {
|
|
case EVRPC_INPUT:
|
|
head = &base->in_hooks;
|
|
break;
|
|
case EVRPC_OUTPUT:
|
|
head = &base->out_hooks;
|
|
break;
|
|
default:
|
|
EVUTIL_ASSERT(hook_type == EVRPC_INPUT || hook_type == EVRPC_OUTPUT);
|
|
}
|
|
|
|
return (evrpc_remove_hook_internal(head, handle));
|
|
}
|
|
|
|
static int
|
|
evrpc_process_hooks(struct evrpc_hook_list *head, void *ctx,
|
|
struct evhttp_request *req, struct evbuffer *evbuf)
|
|
{
|
|
struct evrpc_hook *hook;
|
|
TAILQ_FOREACH(hook, head, next) {
|
|
int res = hook->process(ctx, req, evbuf, hook->process_arg);
|
|
if (res != EVRPC_CONTINUE)
|
|
return (res);
|
|
}
|
|
|
|
return (EVRPC_CONTINUE);
|
|
}
|
|
|
|
static void evrpc_pool_schedule(struct evrpc_pool *pool);
|
|
static void evrpc_request_cb(struct evhttp_request *, void *);
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
|
|
static char *
|
|
evrpc_construct_uri(const char *uri)
|
|
{
|
|
char *constructed_uri;
|
|
size_t constructed_uri_len;
|
|
|
|
constructed_uri_len = strlen(EVRPC_URI_PREFIX) + strlen(uri) + 1;
|
|
if ((constructed_uri = mm_malloc(constructed_uri_len)) == NULL)
|
|
event_err(1, "%s: failed to register rpc at %s",
|
|
__func__, uri);
|
|
memcpy(constructed_uri, EVRPC_URI_PREFIX, strlen(EVRPC_URI_PREFIX));
|
|
memcpy(constructed_uri + strlen(EVRPC_URI_PREFIX), uri, strlen(uri));
|
|
constructed_uri[constructed_uri_len - 1] = '\0';
|
|
|
|
return (constructed_uri);
|
|
}
|
|
|
|
int
|
|
evrpc_register_rpc(struct evrpc_base *base, struct evrpc *rpc,
|
|
void (*cb)(struct evrpc_req_generic *, void *), void *cb_arg)
|
|
{
|
|
char *constructed_uri = evrpc_construct_uri(rpc->uri);
|
|
|
|
rpc->base = base;
|
|
rpc->cb = cb;
|
|
rpc->cb_arg = cb_arg;
|
|
|
|
TAILQ_INSERT_TAIL(&base->registered_rpcs, rpc, next);
|
|
|
|
evhttp_set_cb(base->http_server,
|
|
constructed_uri,
|
|
evrpc_request_cb,
|
|
rpc);
|
|
|
|
mm_free(constructed_uri);
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
evrpc_unregister_rpc(struct evrpc_base *base, const char *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);
|
|
|
|
mm_free(registered_uri);
|
|
|
|
mm_free((char *)rpc->uri);
|
|
mm_free(rpc);
|
|
return (0);
|
|
}
|
|
|
|
static int evrpc_pause_request(void *vbase, void *ctx,
|
|
void (*cb)(void *, enum EVRPC_HOOK_RESULT));
|
|
static void evrpc_request_cb_closure(void *, enum EVRPC_HOOK_RESULT);
|
|
|
|
static void
|
|
evrpc_request_cb(struct evhttp_request *req, void *arg)
|
|
{
|
|
struct evrpc *rpc = arg;
|
|
struct evrpc_req_generic *rpc_state = NULL;
|
|
|
|
/* let's verify the outside parameters */
|
|
if (req->type != EVHTTP_REQ_POST ||
|
|
evbuffer_get_length(req->input_buffer) <= 0)
|
|
goto error;
|
|
|
|
rpc_state = mm_calloc(1, sizeof(struct evrpc_req_generic));
|
|
if (rpc_state == NULL)
|
|
goto error;
|
|
rpc_state->rpc = rpc;
|
|
rpc_state->http_req = req;
|
|
rpc_state->rpc_data = NULL;
|
|
|
|
if (TAILQ_FIRST(&rpc->base->input_hooks) != NULL) {
|
|
int hook_res;
|
|
|
|
evrpc_hook_associate_meta_(&rpc_state->hook_meta, req->evcon);
|
|
|
|
/*
|
|
* allow hooks to modify the outgoing request
|
|
*/
|
|
hook_res = evrpc_process_hooks(&rpc->base->input_hooks,
|
|
rpc_state, req, req->input_buffer);
|
|
switch (hook_res) {
|
|
case EVRPC_TERMINATE:
|
|
goto error;
|
|
case EVRPC_PAUSE:
|
|
evrpc_pause_request(rpc->base, rpc_state,
|
|
evrpc_request_cb_closure);
|
|
return;
|
|
case EVRPC_CONTINUE:
|
|
break;
|
|
default:
|
|
EVUTIL_ASSERT(hook_res == EVRPC_TERMINATE ||
|
|
hook_res == EVRPC_CONTINUE ||
|
|
hook_res == EVRPC_PAUSE);
|
|
}
|
|
}
|
|
|
|
evrpc_request_cb_closure(rpc_state, EVRPC_CONTINUE);
|
|
return;
|
|
|
|
error:
|
|
if (rpc_state != NULL)
|
|
evrpc_reqstate_free_(rpc_state);
|
|
evhttp_send_error(req, HTTP_SERVUNAVAIL, NULL);
|
|
return;
|
|
}
|
|
|
|
static void
|
|
evrpc_request_cb_closure(void *arg, enum EVRPC_HOOK_RESULT hook_res)
|
|
{
|
|
struct evrpc_req_generic *rpc_state = arg;
|
|
struct evrpc *rpc;
|
|
struct evhttp_request *req;
|
|
|
|
EVUTIL_ASSERT(rpc_state);
|
|
rpc = rpc_state->rpc;
|
|
req = rpc_state->http_req;
|
|
|
|
if (hook_res == EVRPC_TERMINATE)
|
|
goto error;
|
|
|
|
/* 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);
|
|
|
|
return;
|
|
|
|
error:
|
|
if (rpc_state != NULL)
|
|
evrpc_reqstate_free_(rpc_state);
|
|
evhttp_send_error(req, HTTP_SERVUNAVAIL, NULL);
|
|
return;
|
|
}
|
|
|
|
|
|
void
|
|
evrpc_reqstate_free_(struct evrpc_req_generic* rpc_state)
|
|
{
|
|
struct evrpc *rpc;
|
|
EVUTIL_ASSERT(rpc_state != NULL);
|
|
rpc = rpc_state->rpc;
|
|
|
|
/* 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);
|
|
}
|
|
|
|
static void
|
|
evrpc_request_done_closure(void *, enum EVRPC_HOOK_RESULT);
|
|
|
|
void
|
|
evrpc_request_done(struct evrpc_req_generic *rpc_state)
|
|
{
|
|
struct evhttp_request *req;
|
|
struct evrpc *rpc;
|
|
|
|
EVUTIL_ASSERT(rpc_state);
|
|
|
|
req = rpc_state->http_req;
|
|
rpc = rpc_state->rpc;
|
|
|
|
if (rpc->reply_complete(rpc_state->reply) == -1) {
|
|
/* the reply was not completely filled in. error out */
|
|
goto error;
|
|
}
|
|
|
|
if ((rpc_state->rpc_data = evbuffer_new()) == NULL) {
|
|
/* out of memory */
|
|
goto error;
|
|
}
|
|
|
|
/* serialize the reply */
|
|
rpc->reply_marshal(rpc_state->rpc_data, rpc_state->reply);
|
|
|
|
if (TAILQ_FIRST(&rpc->base->output_hooks) != NULL) {
|
|
int hook_res;
|
|
|
|
evrpc_hook_associate_meta_(&rpc_state->hook_meta, req->evcon);
|
|
|
|
/* do hook based tweaks to the request */
|
|
hook_res = evrpc_process_hooks(&rpc->base->output_hooks,
|
|
rpc_state, req, rpc_state->rpc_data);
|
|
switch (hook_res) {
|
|
case EVRPC_TERMINATE:
|
|
goto error;
|
|
case EVRPC_PAUSE:
|
|
if (evrpc_pause_request(rpc->base, rpc_state,
|
|
evrpc_request_done_closure) == -1)
|
|
goto error;
|
|
return;
|
|
case EVRPC_CONTINUE:
|
|
break;
|
|
default:
|
|
EVUTIL_ASSERT(hook_res == EVRPC_TERMINATE ||
|
|
hook_res == EVRPC_CONTINUE ||
|
|
hook_res == EVRPC_PAUSE);
|
|
}
|
|
}
|
|
|
|
evrpc_request_done_closure(rpc_state, EVRPC_CONTINUE);
|
|
return;
|
|
|
|
error:
|
|
if (rpc_state != NULL)
|
|
evrpc_reqstate_free_(rpc_state);
|
|
evhttp_send_error(req, HTTP_SERVUNAVAIL, NULL);
|
|
return;
|
|
}
|
|
|
|
void *
|
|
evrpc_get_request(struct evrpc_req_generic *req)
|
|
{
|
|
return req->request;
|
|
}
|
|
|
|
void *
|
|
evrpc_get_reply(struct evrpc_req_generic *req)
|
|
{
|
|
return req->reply;
|
|
}
|
|
|
|
static void
|
|
evrpc_request_done_closure(void *arg, enum EVRPC_HOOK_RESULT hook_res)
|
|
{
|
|
struct evrpc_req_generic *rpc_state = arg;
|
|
struct evhttp_request *req;
|
|
EVUTIL_ASSERT(rpc_state);
|
|
req = rpc_state->http_req;
|
|
|
|
if (hook_res == EVRPC_TERMINATE)
|
|
goto error;
|
|
|
|
/* 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);
|
|
|
|
evrpc_reqstate_free_(rpc_state);
|
|
|
|
return;
|
|
|
|
error:
|
|
if (rpc_state != NULL)
|
|
evrpc_reqstate_free_(rpc_state);
|
|
evhttp_send_error(req, HTTP_SERVUNAVAIL, NULL);
|
|
return;
|
|
}
|
|
|
|
|
|
/* Client implementation of RPC site */
|
|
|
|
static int evrpc_schedule_request(struct evhttp_connection *connection,
|
|
struct evrpc_request_wrapper *ctx);
|
|
|
|
struct evrpc_pool *
|
|
evrpc_pool_new(struct event_base *base)
|
|
{
|
|
struct evrpc_pool *pool = mm_calloc(1, sizeof(struct evrpc_pool));
|
|
if (pool == NULL)
|
|
return (NULL);
|
|
|
|
TAILQ_INIT(&pool->connections);
|
|
TAILQ_INIT(&pool->requests);
|
|
|
|
TAILQ_INIT(&pool->paused_requests);
|
|
|
|
TAILQ_INIT(&pool->input_hooks);
|
|
TAILQ_INIT(&pool->output_hooks);
|
|
|
|
pool->base = base;
|
|
pool->timeout = -1;
|
|
|
|
return (pool);
|
|
}
|
|
|
|
static void
|
|
evrpc_request_wrapper_free(struct evrpc_request_wrapper *request)
|
|
{
|
|
if (request->hook_meta != NULL)
|
|
evrpc_hook_context_free_(request->hook_meta);
|
|
mm_free(request->name);
|
|
mm_free(request);
|
|
}
|
|
|
|
void
|
|
evrpc_pool_free(struct evrpc_pool *pool)
|
|
{
|
|
struct evhttp_connection *connection;
|
|
struct evrpc_request_wrapper *request;
|
|
struct evrpc_hook_ctx *pause;
|
|
struct evrpc_hook *hook;
|
|
int r;
|
|
|
|
while ((request = TAILQ_FIRST(&pool->requests)) != NULL) {
|
|
TAILQ_REMOVE(&pool->requests, request, next);
|
|
evrpc_request_wrapper_free(request);
|
|
}
|
|
|
|
while ((pause = TAILQ_FIRST(&pool->paused_requests)) != NULL) {
|
|
TAILQ_REMOVE(&pool->paused_requests, pause, next);
|
|
mm_free(pause);
|
|
}
|
|
|
|
while ((connection = TAILQ_FIRST(&pool->connections)) != NULL) {
|
|
TAILQ_REMOVE(&pool->connections, connection, next);
|
|
evhttp_connection_free(connection);
|
|
}
|
|
|
|
while ((hook = TAILQ_FIRST(&pool->input_hooks)) != NULL) {
|
|
r = evrpc_remove_hook(pool, EVRPC_INPUT, hook);
|
|
EVUTIL_ASSERT(r);
|
|
}
|
|
|
|
while ((hook = TAILQ_FIRST(&pool->output_hooks)) != NULL) {
|
|
r = evrpc_remove_hook(pool, EVRPC_OUTPUT, hook);
|
|
EVUTIL_ASSERT(r);
|
|
}
|
|
|
|
mm_free(pool);
|
|
}
|
|
|
|
/*
|
|
* Add a connection to the RPC pool. A request scheduled on the pool
|
|
* may use any available connection.
|
|
*/
|
|
|
|
void
|
|
evrpc_pool_add_connection(struct evrpc_pool *pool,
|
|
struct evhttp_connection *connection)
|
|
{
|
|
EVUTIL_ASSERT(connection->http_server == NULL);
|
|
TAILQ_INSERT_TAIL(&pool->connections, connection, next);
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
|
|
if (TAILQ_FIRST(&pool->requests) != NULL) {
|
|
struct evrpc_request_wrapper *request =
|
|
TAILQ_FIRST(&pool->requests);
|
|
TAILQ_REMOVE(&pool->requests, request, next);
|
|
evrpc_schedule_request(connection, request);
|
|
}
|
|
}
|
|
|
|
void
|
|
evrpc_pool_remove_connection(struct evrpc_pool *pool,
|
|
struct evhttp_connection *connection)
|
|
{
|
|
TAILQ_REMOVE(&pool->connections, connection, next);
|
|
}
|
|
|
|
void
|
|
evrpc_pool_set_timeout(struct evrpc_pool *pool, int timeout_in_secs)
|
|
{
|
|
struct evhttp_connection *evcon;
|
|
TAILQ_FOREACH(evcon, &pool->connections, next) {
|
|
evhttp_connection_set_timeout(evcon, timeout_in_secs);
|
|
}
|
|
pool->timeout = timeout_in_secs;
|
|
}
|
|
|
|
|
|
static void evrpc_reply_done(struct evhttp_request *, void *);
|
|
static void evrpc_request_timeout(evutil_socket_t, short, void *);
|
|
|
|
/*
|
|
* Finds a connection object associated with the pool that is currently
|
|
* idle and can be used to make a request.
|
|
*/
|
|
static struct 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
|
|
*/
|
|
|
|
static void evrpc_schedule_request_closure(void *ctx, enum EVRPC_HOOK_RESULT);
|
|
|
|
/*
|
|
* We assume that the ctx is no longer queued on the pool.
|
|
*/
|
|
static int
|
|
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;
|
|
|
|
evrpc_hook_associate_meta_(&ctx->hook_meta, connection);
|
|
|
|
/* 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);
|
|
}
|
|
}
|
|
|
|
evrpc_schedule_request_closure(ctx, EVRPC_CONTINUE);
|
|
return (0);
|
|
|
|
error:
|
|
memset(&status, 0, sizeof(status));
|
|
status.error = EVRPC_STATUS_ERR_UNSTARTED;
|
|
(*ctx->cb)(&status, ctx->request, ctx->reply, ctx->cb_arg);
|
|
evrpc_request_wrapper_free(ctx);
|
|
return (-1);
|
|
}
|
|
|
|
static void
|
|
evrpc_schedule_request_closure(void *arg, enum EVRPC_HOOK_RESULT hook_res)
|
|
{
|
|
struct evrpc_request_wrapper *ctx = arg;
|
|
struct evhttp_connection *connection = ctx->evcon;
|
|
struct evhttp_request *req = ctx->req;
|
|
struct evrpc_pool *pool = ctx->pool;
|
|
struct evrpc_status status;
|
|
char *uri = NULL;
|
|
int res = 0;
|
|
|
|
if (hook_res == EVRPC_TERMINATE)
|
|
goto error;
|
|
|
|
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);
|
|
|
|
if (res == -1)
|
|
goto error;
|
|
|
|
return;
|
|
|
|
error:
|
|
memset(&status, 0, sizeof(status));
|
|
status.error = EVRPC_STATUS_ERR_UNSTARTED;
|
|
(*ctx->cb)(&status, ctx->request, ctx->reply, ctx->cb_arg);
|
|
evrpc_request_wrapper_free(ctx);
|
|
}
|
|
|
|
/* we just queue the paused request on the pool under the req object */
|
|
static int
|
|
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);
|
|
|
|
pause->ctx = ctx;
|
|
pause->cb = cb;
|
|
|
|
TAILQ_INSERT_TAIL(&base->pause_requests, pause, next);
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
evrpc_resume_request(void *vbase, void *ctx, enum EVRPC_HOOK_RESULT res)
|
|
{
|
|
struct evrpc_hooks_ *base = vbase;
|
|
struct evrpc_pause_list *head = &base->pause_requests;
|
|
struct evrpc_hook_ctx *pause;
|
|
|
|
TAILQ_FOREACH(pause, head, next) {
|
|
if (pause->ctx == ctx)
|
|
break;
|
|
}
|
|
|
|
if (pause == NULL)
|
|
return (-1);
|
|
|
|
(*pause->cb)(pause->ctx, res);
|
|
TAILQ_REMOVE(head, pause, next);
|
|
mm_free(pause);
|
|
return (0);
|
|
}
|
|
|
|
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);
|
|
|
|
evrpc_pool_schedule(pool);
|
|
|
|
return (0);
|
|
}
|
|
|
|
|
|
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)
|
|
{
|
|
struct evrpc_request_wrapper *ctx = (struct evrpc_request_wrapper *)
|
|
mm_malloc(sizeof(struct evrpc_request_wrapper));
|
|
if (ctx == NULL)
|
|
return (NULL);
|
|
|
|
ctx->pool = pool;
|
|
ctx->hook_meta = NULL;
|
|
ctx->evcon = NULL;
|
|
ctx->name = mm_strdup(rpcname);
|
|
if (ctx->name == NULL) {
|
|
mm_free(ctx);
|
|
return (NULL);
|
|
}
|
|
ctx->cb = cb;
|
|
ctx->cb_arg = cbarg;
|
|
ctx->request = request;
|
|
ctx->reply = reply;
|
|
ctx->request_marshal = req_marshal;
|
|
ctx->reply_clear = rpl_clear;
|
|
ctx->reply_unmarshal = rpl_unmarshal;
|
|
|
|
return (ctx);
|
|
}
|
|
|
|
static void
|
|
evrpc_reply_done_closure(void *, enum EVRPC_HOOK_RESULT);
|
|
|
|
static void
|
|
evrpc_reply_done(struct evhttp_request *req, void *arg)
|
|
{
|
|
struct evrpc_request_wrapper *ctx = arg;
|
|
struct evrpc_pool *pool = ctx->pool;
|
|
int hook_res = EVRPC_CONTINUE;
|
|
|
|
/* 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
|
|
*/
|
|
if (req != NULL)
|
|
evhttp_request_own(req);
|
|
|
|
evrpc_pause_request(pool, ctx,
|
|
evrpc_reply_done_closure);
|
|
return;
|
|
default:
|
|
EVUTIL_ASSERT(hook_res == EVRPC_TERMINATE ||
|
|
hook_res == EVRPC_CONTINUE ||
|
|
hook_res == EVRPC_PAUSE);
|
|
}
|
|
}
|
|
|
|
evrpc_reply_done_closure(ctx, hook_res);
|
|
|
|
/* http request is being freed by underlying layer */
|
|
}
|
|
|
|
static void
|
|
evrpc_reply_done_closure(void *arg, enum EVRPC_HOOK_RESULT hook_res)
|
|
{
|
|
struct evrpc_request_wrapper *ctx = arg;
|
|
struct evhttp_request *req = ctx->req;
|
|
struct evrpc_pool *pool = ctx->pool;
|
|
struct evrpc_status status;
|
|
int res = -1;
|
|
|
|
memset(&status, 0, sizeof(status));
|
|
status.http_req = req;
|
|
|
|
/* we need to get the reply now */
|
|
if (req == NULL) {
|
|
status.error = EVRPC_STATUS_ERR_TIMEOUT;
|
|
} else if (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);
|
|
}
|
|
|
|
(*ctx->cb)(&status, ctx->request, ctx->reply, ctx->cb_arg);
|
|
|
|
evrpc_request_wrapper_free(ctx);
|
|
|
|
/* 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);
|
|
}
|
|
|
|
static void
|
|
evrpc_pool_schedule(struct evrpc_pool *pool)
|
|
{
|
|
struct evrpc_request_wrapper *ctx = TAILQ_FIRST(&pool->requests);
|
|
struct evhttp_connection *evcon;
|
|
|
|
/* if no requests are pending, we have no work */
|
|
if (ctx == NULL)
|
|
return;
|
|
|
|
if ((evcon = evrpc_pool_find_connection(pool)) != NULL) {
|
|
TAILQ_REMOVE(&pool->requests, ctx, next);
|
|
evrpc_schedule_request(evcon, ctx);
|
|
}
|
|
}
|
|
|
|
static void
|
|
evrpc_request_timeout(evutil_socket_t fd, short what, void *arg)
|
|
{
|
|
struct evrpc_request_wrapper *ctx = arg;
|
|
struct evhttp_connection *evcon = ctx->evcon;
|
|
EVUTIL_ASSERT(evcon != NULL);
|
|
|
|
evhttp_connection_fail_(evcon, EVCON_HTTP_TIMEOUT);
|
|
}
|
|
|
|
/*
|
|
* frees potential meta data associated with a request.
|
|
*/
|
|
|
|
static void
|
|
evrpc_meta_data_free(struct evrpc_meta_list *meta_data)
|
|
{
|
|
struct evrpc_meta *entry;
|
|
EVUTIL_ASSERT(meta_data != NULL);
|
|
|
|
while ((entry = TAILQ_FIRST(meta_data)) != NULL) {
|
|
TAILQ_REMOVE(meta_data, entry, next);
|
|
mm_free(entry->key);
|
|
mm_free(entry->data);
|
|
mm_free(entry);
|
|
}
|
|
}
|
|
|
|
static struct evrpc_hook_meta *
|
|
evrpc_hook_meta_new_(void)
|
|
{
|
|
struct evrpc_hook_meta *ctx;
|
|
ctx = mm_malloc(sizeof(struct evrpc_hook_meta));
|
|
EVUTIL_ASSERT(ctx != NULL);
|
|
|
|
TAILQ_INIT(&ctx->meta_data);
|
|
ctx->evcon = NULL;
|
|
|
|
return (ctx);
|
|
}
|
|
|
|
static void
|
|
evrpc_hook_associate_meta_(struct evrpc_hook_meta **pctx,
|
|
struct evhttp_connection *evcon)
|
|
{
|
|
struct evrpc_hook_meta *ctx = *pctx;
|
|
if (ctx == NULL)
|
|
*pctx = ctx = evrpc_hook_meta_new_();
|
|
ctx->evcon = evcon;
|
|
}
|
|
|
|
static void
|
|
evrpc_hook_context_free_(struct evrpc_hook_meta *ctx)
|
|
{
|
|
evrpc_meta_data_free(&ctx->meta_data);
|
|
mm_free(ctx);
|
|
}
|
|
|
|
/* Adds meta data */
|
|
void
|
|
evrpc_hook_add_meta(void *ctx, const char *key,
|
|
const void *data, size_t data_size)
|
|
{
|
|
struct evrpc_request_wrapper *req = ctx;
|
|
struct evrpc_hook_meta *store = NULL;
|
|
struct evrpc_meta *meta = NULL;
|
|
|
|
if ((store = req->hook_meta) == NULL)
|
|
store = req->hook_meta = evrpc_hook_meta_new_();
|
|
|
|
meta = mm_malloc(sizeof(struct evrpc_meta));
|
|
EVUTIL_ASSERT(meta != NULL);
|
|
meta->key = mm_strdup(key);
|
|
EVUTIL_ASSERT(meta->key != NULL);
|
|
meta->data_size = data_size;
|
|
meta->data = mm_malloc(data_size);
|
|
EVUTIL_ASSERT(meta->data != NULL);
|
|
memcpy(meta->data, data, data_size);
|
|
|
|
TAILQ_INSERT_TAIL(&store->meta_data, meta, next);
|
|
}
|
|
|
|
int
|
|
evrpc_hook_find_meta(void *ctx, const char *key, void **data, size_t *data_size)
|
|
{
|
|
struct evrpc_request_wrapper *req = ctx;
|
|
struct evrpc_meta *meta = NULL;
|
|
|
|
if (req->hook_meta == NULL)
|
|
return (-1);
|
|
|
|
TAILQ_FOREACH(meta, &req->hook_meta->meta_data, next) {
|
|
if (strcmp(meta->key, key) == 0) {
|
|
*data = meta->data;
|
|
*data_size = meta->data_size;
|
|
return (0);
|
|
}
|
|
}
|
|
|
|
return (-1);
|
|
}
|
|
|
|
struct evhttp_connection *
|
|
evrpc_hook_get_connection(void *ctx)
|
|
{
|
|
struct evrpc_request_wrapper *req = ctx;
|
|
return (req->hook_meta != NULL ? req->hook_meta->evcon : NULL);
|
|
}
|
|
|
|
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 *))
|
|
{
|
|
struct evrpc_status status;
|
|
struct evrpc_request_wrapper *ctx;
|
|
ctx = evrpc_make_request_ctx(pool, request, reply,
|
|
rpcname, req_marshal, rpl_clear, rpl_unmarshal, cb, cb_arg);
|
|
if (ctx == NULL)
|
|
goto error;
|
|
return (evrpc_make_request(ctx));
|
|
error:
|
|
memset(&status, 0, sizeof(status));
|
|
status.error = EVRPC_STATUS_ERR_UNSTARTED;
|
|
(*(cb))(&status, request, reply, cb_arg);
|
|
return (-1);
|
|
}
|
|
|
|
/** Takes a request object and fills it in with the right magic */
|
|
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 *))
|
|
{
|
|
struct evrpc* rpc = (struct evrpc *)mm_calloc(1, sizeof(struct evrpc));
|
|
if (rpc == NULL)
|
|
return (NULL);
|
|
rpc->uri = mm_strdup(name);
|
|
if (rpc->uri == NULL) {
|
|
mm_free(rpc);
|
|
return (NULL);
|
|
}
|
|
rpc->request_new = req_new;
|
|
rpc->request_new_arg = req_new_arg;
|
|
rpc->request_free = req_free;
|
|
rpc->request_unmarshal = req_unmarshal;
|
|
rpc->reply_new = rpl_new;
|
|
rpc->reply_new_arg = rpl_new_arg;
|
|
rpc->reply_free = rpl_free;
|
|
rpc->reply_complete = rpl_complete;
|
|
rpc->reply_marshal = rpl_marshal;
|
|
return (rpc);
|
|
}
|
|
|
|
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 *))
|
|
{
|
|
struct evrpc* rpc =
|
|
evrpc_register_object(name, req_new, req_new_arg, req_free, req_unmarshal,
|
|
rpl_new, rpl_new_arg, rpl_free, rpl_complete, rpl_marshal);
|
|
if (rpc == NULL)
|
|
return (-1);
|
|
evrpc_register_rpc(base, rpc,
|
|
(void (*)(struct evrpc_req_generic*, void *))callback, cbarg);
|
|
return (0);
|
|
}
|
|
|
|
/** accessors for obscure and undocumented functionality */
|
|
struct evrpc_pool *
|
|
evrpc_request_get_pool(struct evrpc_request_wrapper *ctx)
|
|
{
|
|
return (ctx->pool);
|
|
}
|
|
|
|
void
|
|
evrpc_request_set_pool(struct evrpc_request_wrapper *ctx,
|
|
struct evrpc_pool *pool)
|
|
{
|
|
ctx->pool = pool;
|
|
}
|
|
|
|
void
|
|
evrpc_request_set_cb(struct evrpc_request_wrapper *ctx,
|
|
void (*cb)(struct evrpc_status*, void *request, void *reply, void *arg),
|
|
void *cb_arg)
|
|
{
|
|
ctx->cb = cb;
|
|
ctx->cb_arg = cb_arg;
|
|
}
|