diff --git a/libs/libblade/Makefile.am b/libs/libblade/Makefile.am index fe4c96b108..f3ad18d3e0 100644 --- a/libs/libblade/Makefile.am +++ b/libs/libblade/Makefile.am @@ -11,18 +11,20 @@ libunqlite_la_CFLAGS = -DUNQLITE_ENABLE_THREADS libunqlite_la_LIBADD = -lpthread lib_LTLIBRARIES = libblade.la -libblade_la_SOURCES = src/blade.c src/blade_stack.c src/blade_peer.c src/bpcp.c src/blade_datastore.c +libblade_la_SOURCES = src/blade.c src/blade_stack.c +libblade_la_SOURCES += src/blade_datastore.c +libblade_la_SOURCES += src/blade_identity.c src/blade_module.c src/blade_connection.c +libblade_la_SOURCES += src/blade_session.c src/blade_protocol.c src/blade_space.c src/blade_method.c +libblade_la_SOURCES += src/blade_module_wss.c src/blade_module_chat.c libblade_la_CFLAGS = $(AM_CFLAGS) $(AM_CPPFLAGS) -libblade_la_LDFLAGS = -version-info 0:1:0 -lncurses -lpthread -lm $(AM_LDFLAGS) +libblade_la_LDFLAGS = -version-info 0:1:0 -lncurses -lpthread -lm -lconfig $(AM_LDFLAGS) libblade_la_LIBADD = libunqlite.la library_includedir = $(prefix)/include -library_include_HEADERS = src/include/blade.h src/include/blade_types.h src/include/blade_stack.h src/include/blade_peer.h src/include/bpcp.h +library_include_HEADERS = src/include/blade.h src/include/blade_types.h src/include/blade_stack.h library_include_HEADERS += src/include/blade_datastore.h +library_include_HEADERS += src/include/blade_identity.h src/include/blade_module.h src/include/blade_connection.h +library_include_HEADERS += src/include/blade_session.h src/include/blade_protocol.h src/include/blade_space.h src/include/blade_method.h library_include_HEADERS += src/include/unqlite.h test/tap.h tests: libblade.la $(MAKE) -C test tests - - - - diff --git a/libs/libblade/src/blade_connection.c b/libs/libblade/src/blade_connection.c new file mode 100644 index 0000000000..d757845b6b --- /dev/null +++ b/libs/libblade/src/blade_connection.c @@ -0,0 +1,550 @@ +/* + * Copyright (c) 2017, Shane Bryldt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * 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. + * + * * Neither the name of the original author; nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT OWNER + * OR CONTRIBUTORS 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 "blade.h" + +struct blade_connection_s { + blade_handle_t *handle; + ks_pool_t *pool; + + void *transport_init_data; + void *transport_data; + blade_transport_callbacks_t *transport_callbacks; + + ks_bool_t shutdown; + blade_connection_direction_t direction; + ks_thread_t *state_thread; + blade_connection_state_t state; + + const char *id; + ks_rwl_t *lock; + + ks_q_t *sending; + + const char *session; +}; + +void *blade_connection_state_thread(ks_thread_t *thread, void *data); +ks_status_t blade_connection_state_on_disconnect(blade_connection_t *bc); +ks_status_t blade_connection_state_on_new(blade_connection_t *bc); +ks_status_t blade_connection_state_on_connect(blade_connection_t *bc); +ks_status_t blade_connection_state_on_attach(blade_connection_t *bc); +ks_status_t blade_connection_state_on_detach(blade_connection_t *bc); +ks_status_t blade_connection_state_on_ready(blade_connection_t *bc); + + +KS_DECLARE(ks_status_t) blade_connection_create(blade_connection_t **bcP, + blade_handle_t *bh, + void *transport_init_data, + blade_transport_callbacks_t *transport_callbacks) +{ + blade_connection_t *bc = NULL; + ks_pool_t *pool = NULL; + uuid_t id; + + ks_assert(bcP); + ks_assert(bh); + ks_assert(transport_callbacks); + + pool = blade_handle_pool_get(bh); + + bc = ks_pool_alloc(pool, sizeof(blade_connection_t)); + bc->handle = bh; + bc->pool = pool; + bc->transport_init_data = transport_init_data; + bc->transport_callbacks = transport_callbacks; + + ks_uuid(&id); + bc->id = ks_uuid_str(pool, &id); + ks_assert(bc->id); + + ks_rwl_create(&bc->lock, pool); + ks_assert(bc->lock); + + ks_q_create(&bc->sending, pool, 0); + ks_assert(bc->sending); + + *bcP = bc; + + ks_log(KS_LOG_DEBUG, "Created\n"); + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_connection_destroy(blade_connection_t **bcP) +{ + blade_connection_t *bc = NULL; + + ks_assert(bcP); + ks_assert(*bcP); + + bc = *bcP; + + blade_connection_shutdown(bc); + + ks_q_destroy(&bc->sending); + + ks_rwl_destroy(&bc->lock); + + ks_pool_free(bc->pool, &bc->id); + + ks_pool_free(bc->pool, bcP); + + ks_log(KS_LOG_DEBUG, "Destroyed\n"); + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_connection_startup(blade_connection_t *bc, blade_connection_direction_t direction) +{ + ks_assert(bc); + + bc->direction = direction; + blade_connection_state_set(bc, BLADE_CONNECTION_STATE_NONE); + + if (ks_thread_create_ex(&bc->state_thread, + blade_connection_state_thread, + bc, + KS_THREAD_FLAG_DEFAULT, + KS_THREAD_DEFAULT_STACK, + KS_PRI_NORMAL, + bc->pool) != KS_STATUS_SUCCESS) { + // @todo error logging + return KS_STATUS_FAIL; + } + + ks_log(KS_LOG_DEBUG, "Started\n"); + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_connection_shutdown(blade_connection_t *bc) +{ + cJSON *json = NULL; + + ks_assert(bc); + + if (bc->state_thread) { + bc->shutdown = KS_TRUE; + ks_thread_join(bc->state_thread); + ks_pool_free(bc->pool, &bc->state_thread); + bc->shutdown = KS_FALSE; + } + + if (bc->session) ks_pool_free(bc->pool, &bc->session); + + while (ks_q_trypop(bc->sending, (void **)&json) == KS_STATUS_SUCCESS && json) cJSON_Delete(json); + + ks_log(KS_LOG_DEBUG, "Stopped\n"); + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(blade_handle_t *) blade_connection_handle_get(blade_connection_t *bc) +{ + ks_assert(bc); + + return bc->handle; +} + +KS_DECLARE(ks_pool_t *) blade_connection_pool_get(blade_connection_t *bc) +{ + ks_assert(bc); + + return bc->pool; +} + +KS_DECLARE(const char *) blade_connection_id_get(blade_connection_t *bc) +{ + ks_assert(bc); + + return bc->id; +} + +KS_DECLARE(ks_status_t) blade_connection_read_lock(blade_connection_t *bc, ks_bool_t block) +{ + ks_status_t ret = KS_STATUS_SUCCESS; + + ks_assert(bc); + + if (block) ret = ks_rwl_read_lock(bc->lock); + else ret = ks_rwl_try_read_lock(bc->lock); + return ret; +} + +KS_DECLARE(ks_status_t) blade_connection_read_unlock(blade_connection_t *bc) +{ + ks_assert(bc); + + return ks_rwl_read_unlock(bc->lock); +} + +KS_DECLARE(ks_status_t) blade_connection_write_lock(blade_connection_t *bc, ks_bool_t block) +{ + ks_status_t ret = KS_STATUS_SUCCESS; + + ks_assert(bc); + + if (block) ret = ks_rwl_write_lock(bc->lock); + else ret = ks_rwl_try_write_lock(bc->lock); + return ret; +} + +KS_DECLARE(ks_status_t) blade_connection_write_unlock(blade_connection_t *bc) +{ + ks_assert(bc); + + return ks_rwl_write_unlock(bc->lock); +} + + +KS_DECLARE(void *) blade_connection_transport_init_get(blade_connection_t *bc) +{ + ks_assert(bc); + + return bc->transport_init_data; +} + +KS_DECLARE(void *) blade_connection_transport_get(blade_connection_t *bc) +{ + ks_assert(bc); + + return bc->transport_data; +} + +KS_DECLARE(void) blade_connection_transport_set(blade_connection_t *bc, void *transport_data) +{ + ks_assert(bc); + + bc->transport_data = transport_data; +} + +blade_transport_state_callback_t blade_connection_state_callback_lookup(blade_connection_t *bc, blade_connection_state_t state) +{ + blade_transport_state_callback_t callback = NULL; + + ks_assert(bc); + + switch (state) { + case BLADE_CONNECTION_STATE_DISCONNECT: + if (bc->direction == BLADE_CONNECTION_DIRECTION_INBOUND) callback = bc->transport_callbacks->onstate_disconnect_inbound; + else if(bc->direction == BLADE_CONNECTION_DIRECTION_OUTBOUND) callback = bc->transport_callbacks->onstate_disconnect_outbound; + break; + case BLADE_CONNECTION_STATE_NEW: + if (bc->direction == BLADE_CONNECTION_DIRECTION_INBOUND) callback = bc->transport_callbacks->onstate_new_inbound; + else if(bc->direction == BLADE_CONNECTION_DIRECTION_OUTBOUND) callback = bc->transport_callbacks->onstate_new_outbound; + break; + case BLADE_CONNECTION_STATE_CONNECT: + if (bc->direction == BLADE_CONNECTION_DIRECTION_INBOUND) callback = bc->transport_callbacks->onstate_connect_inbound; + else if(bc->direction == BLADE_CONNECTION_DIRECTION_OUTBOUND) callback = bc->transport_callbacks->onstate_connect_outbound; + break; + case BLADE_CONNECTION_STATE_ATTACH: + if (bc->direction == BLADE_CONNECTION_DIRECTION_INBOUND) callback = bc->transport_callbacks->onstate_attach_inbound; + else if(bc->direction == BLADE_CONNECTION_DIRECTION_OUTBOUND) callback = bc->transport_callbacks->onstate_attach_outbound; + break; + case BLADE_CONNECTION_STATE_DETACH: + if (bc->direction == BLADE_CONNECTION_DIRECTION_INBOUND) callback = bc->transport_callbacks->onstate_detach_inbound; + else if(bc->direction == BLADE_CONNECTION_DIRECTION_OUTBOUND) callback = bc->transport_callbacks->onstate_detach_outbound; + break; + case BLADE_CONNECTION_STATE_READY: + if (bc->direction == BLADE_CONNECTION_DIRECTION_INBOUND) callback = bc->transport_callbacks->onstate_ready_inbound; + else if(bc->direction == BLADE_CONNECTION_DIRECTION_OUTBOUND) callback = bc->transport_callbacks->onstate_ready_outbound; + break; + default: break; + } + + return callback; +} + +KS_DECLARE(void) blade_connection_state_set(blade_connection_t *bc, blade_connection_state_t state) +{ + blade_transport_state_callback_t callback = NULL; + blade_connection_state_hook_t hook = BLADE_CONNECTION_STATE_HOOK_SUCCESS; + + ks_assert(bc); + + callback = blade_connection_state_callback_lookup(bc, state); + + if (callback) hook = callback(bc, BLADE_CONNECTION_STATE_CONDITION_PRE); + + bc->state = state; + + if (hook == BLADE_CONNECTION_STATE_HOOK_DISCONNECT) blade_connection_disconnect(bc); +} + +KS_DECLARE(void) blade_connection_disconnect(blade_connection_t *bc) +{ + ks_assert(bc); + + if (bc->state != BLADE_CONNECTION_STATE_DETACH && bc->state != BLADE_CONNECTION_STATE_DISCONNECT) { + ks_log(KS_LOG_DEBUG, "Connection (%s) disconnecting\n", bc->id); + blade_connection_state_set(bc, BLADE_CONNECTION_STATE_DETACH); + } +} + +KS_DECLARE(ks_status_t) blade_connection_sending_push(blade_connection_t *bc, cJSON *json) +{ + cJSON *json_copy = NULL; + + ks_assert(bc); + ks_assert(json); + + json_copy = cJSON_Duplicate(json, 1); + return ks_q_push(bc->sending, json_copy); +} + +KS_DECLARE(ks_status_t) blade_connection_sending_pop(blade_connection_t *bc, cJSON **json) +{ + ks_assert(bc); + ks_assert(json); + + return ks_q_trypop(bc->sending, (void **)json); +} + +KS_DECLARE(const char *) blade_connection_session_get(blade_connection_t *bc) +{ + ks_assert(bc); + + return bc->session; +} + +KS_DECLARE(void) blade_connection_session_set(blade_connection_t *bc, const char *id) +{ + ks_assert(bc); + + if (bc->session) ks_pool_free(bc->pool, &bc->session); + bc->session = ks_pstrdup(bc->pool, id); +} + +void *blade_connection_state_thread(ks_thread_t *thread, void *data) +{ + blade_connection_t *bc = NULL; + blade_connection_state_t state; + + ks_assert(thread); + ks_assert(data); + + bc = (blade_connection_t *)data; + + while (!bc->shutdown) { + state = bc->state; + + switch (state) { + case BLADE_CONNECTION_STATE_DISCONNECT: + blade_connection_state_on_disconnect(bc); + break; + case BLADE_CONNECTION_STATE_NEW: + blade_connection_state_on_new(bc); + break; + case BLADE_CONNECTION_STATE_CONNECT: + blade_connection_state_on_connect(bc); + break; + case BLADE_CONNECTION_STATE_ATTACH: + blade_connection_state_on_attach(bc); + break; + case BLADE_CONNECTION_STATE_DETACH: + blade_connection_state_on_detach(bc); + break; + case BLADE_CONNECTION_STATE_READY: + blade_connection_state_on_ready(bc); + break; + default: break; + } + + if (state == BLADE_CONNECTION_STATE_DISCONNECT) break; + } + + return NULL; +} + +ks_status_t blade_connection_state_on_disconnect(blade_connection_t *bc) +{ + blade_transport_state_callback_t callback = NULL; + + ks_assert(bc); + + blade_handle_connections_remove(bc); + + callback = blade_connection_state_callback_lookup(bc, BLADE_CONNECTION_STATE_DISCONNECT); + if (callback) callback(bc, BLADE_CONNECTION_STATE_CONDITION_POST); + + blade_connection_destroy(&bc); + + return KS_STATUS_SUCCESS; +} + +ks_status_t blade_connection_state_on_new(blade_connection_t *bc) +{ + blade_transport_state_callback_t callback = NULL; + blade_connection_state_hook_t hook = BLADE_CONNECTION_STATE_HOOK_SUCCESS; + + ks_assert(bc); + + callback = blade_connection_state_callback_lookup(bc, BLADE_CONNECTION_STATE_NEW); + if (callback) hook = callback(bc, BLADE_CONNECTION_STATE_CONDITION_POST); + + if (hook == BLADE_CONNECTION_STATE_HOOK_DISCONNECT) blade_connection_disconnect(bc); + else if (hook == BLADE_CONNECTION_STATE_HOOK_SUCCESS) { + blade_connection_state_set(bc, BLADE_CONNECTION_STATE_CONNECT); + } + + return KS_STATUS_SUCCESS; +} + +ks_status_t blade_connection_state_on_connect(blade_connection_t *bc) +{ + blade_transport_state_callback_t callback = NULL; + blade_connection_state_hook_t hook = BLADE_CONNECTION_STATE_HOOK_SUCCESS; + + ks_assert(bc); + + callback = blade_connection_state_callback_lookup(bc, BLADE_CONNECTION_STATE_CONNECT); + if (callback) hook = callback(bc, BLADE_CONNECTION_STATE_CONDITION_POST); + + if (hook == BLADE_CONNECTION_STATE_HOOK_DISCONNECT) blade_connection_disconnect(bc); + else if (hook == BLADE_CONNECTION_STATE_HOOK_SUCCESS) { + blade_connection_state_set(bc, BLADE_CONNECTION_STATE_ATTACH); + } + + return KS_STATUS_SUCCESS; +} + +ks_status_t blade_connection_state_on_attach(blade_connection_t *bc) +{ + blade_transport_state_callback_t callback = NULL; + blade_connection_state_hook_t hook = BLADE_CONNECTION_STATE_HOOK_SUCCESS; + + ks_assert(bc); + + callback = blade_connection_state_callback_lookup(bc, BLADE_CONNECTION_STATE_ATTACH); + if (callback) hook = callback(bc, BLADE_CONNECTION_STATE_CONDITION_POST); + + if (hook == BLADE_CONNECTION_STATE_HOOK_DISCONNECT) blade_connection_disconnect(bc); + else if (hook == BLADE_CONNECTION_STATE_HOOK_SUCCESS) { + // @todo this is adding a second lock, since we keep it locked in the callback to allow finishing, we don't want get locking here... + // or just try unlocking twice to confirm... + blade_session_t *bs = blade_handle_sessions_get(bc->handle, bc->session); + ks_assert(bs); // should not happen because bs should still be locked + + blade_session_connections_add(bs, bc->id); + + blade_connection_state_set(bc, BLADE_CONNECTION_STATE_READY); + blade_session_state_set(bs, BLADE_SESSION_STATE_READY); // @todo only set this if it's not already in the READY state from prior connection + + blade_session_read_unlock(bs); // unlock the session we locked obtaining it above + blade_session_read_unlock(bs); // unlock the session we expect to be locked during the callback to ensure we can finish attaching + } + + return KS_STATUS_SUCCESS; +} + +ks_status_t blade_connection_state_on_detach(blade_connection_t *bc) +{ + blade_transport_state_callback_t callback = NULL; + + ks_assert(bc); + + callback = blade_connection_state_callback_lookup(bc, BLADE_CONNECTION_STATE_DETACH); + if (callback) callback(bc, BLADE_CONNECTION_STATE_CONDITION_POST); + + if (bc->session) { + blade_session_t *bs = blade_handle_sessions_get(bc->handle, bc->session); + ks_assert(bs); + + blade_session_connections_remove(bs, bc->id); + blade_session_read_unlock(bs); + // keep bc->session for later in case something triggers a reconnect later and needs the old session id for a hint + } + blade_connection_state_set(bc, BLADE_CONNECTION_STATE_DISCONNECT); + + return KS_STATUS_SUCCESS; +} + +ks_status_t blade_connection_state_on_ready(blade_connection_t *bc) +{ + blade_transport_state_callback_t callback = NULL; + blade_connection_state_hook_t hook = BLADE_CONNECTION_STATE_HOOK_SUCCESS; + cJSON *json = NULL; + blade_session_t *bs = NULL; + ks_bool_t done = KS_FALSE; + + ks_assert(bc); + + while (blade_connection_sending_pop(bc, &json) == KS_STATUS_SUCCESS && json) { + ks_status_t ret = bc->transport_callbacks->onsend(bc, json); + cJSON_Delete(json); + + if (ret != KS_STATUS_SUCCESS) { + blade_connection_disconnect(bc); + break; + } + } + + while (!done) { + if (bc->transport_callbacks->onreceive(bc, &json) != KS_STATUS_SUCCESS) { + blade_connection_disconnect(bc); + break; + } + + if (!(done = (json == NULL))) { + if (!bs) { + bs = blade_handle_sessions_get(bc->handle, bc->session); + ks_assert(bs); + } + blade_session_receiving_push(bs, json); + cJSON_Delete(json); + json = NULL; + } + } + if (bs) blade_session_read_unlock(bs); + + callback = blade_connection_state_callback_lookup(bc, BLADE_CONNECTION_STATE_READY); + if (callback) hook = callback(bc, BLADE_CONNECTION_STATE_CONDITION_POST); + + if (hook == BLADE_CONNECTION_STATE_HOOK_DISCONNECT) blade_connection_disconnect(bc); + else ks_sleep_ms(1); + + return KS_STATUS_SUCCESS; +} + + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: + */ diff --git a/libs/libblade/src/blade_datastore.c b/libs/libblade/src/blade_datastore.c index d97cd9840d..9401a6e577 100644 --- a/libs/libblade/src/blade_datastore.c +++ b/libs/libblade/src/blade_datastore.c @@ -34,14 +34,12 @@ #include "blade.h" -typedef enum { - BDS_NONE = 0, - BDS_MYPOOL = (1 << 0), -} bdspvt_flag_t; - struct blade_datastore_s { - bdspvt_flag_t flags; ks_pool_t *pool; + ks_thread_pool_t *tpool; + + const char *config_database_path; + unqlite *db; }; @@ -58,8 +56,6 @@ typedef struct blade_datastore_fetch_userdata_s blade_datastore_fetch_userdata_t KS_DECLARE(ks_status_t) blade_datastore_destroy(blade_datastore_t **bdsP) { blade_datastore_t *bds = NULL; - bdspvt_flag_t flags; - ks_pool_t *pool; ks_assert(bdsP); @@ -68,41 +64,65 @@ KS_DECLARE(ks_status_t) blade_datastore_destroy(blade_datastore_t **bdsP) ks_assert(bds); - flags = bds->flags; - pool = bds->pool; - - if (bds->db) { - unqlite_close(bds->db); - bds->db = NULL; - } + blade_datastore_shutdown(bds); ks_pool_free(bds->pool, &bds); - if (pool && (flags & BDS_MYPOOL)) ks_pool_close(&pool); - return KS_STATUS_SUCCESS; } -KS_DECLARE(ks_status_t) blade_datastore_create(blade_datastore_t **bdsP, ks_pool_t *pool) +KS_DECLARE(ks_status_t) blade_datastore_create(blade_datastore_t **bdsP, ks_pool_t *pool, ks_thread_pool_t *tpool) { - bdspvt_flag_t newflags = BDS_NONE; blade_datastore_t *bds = NULL; - if (!pool) { - newflags |= BDS_MYPOOL; - ks_pool_open(&pool); - ks_assert(pool); - } - + ks_assert(bdsP); + ks_assert(pool); + ks_assert(tpool); + bds = ks_pool_alloc(pool, sizeof(*bds)); - bds->flags = newflags; bds->pool = pool; + bds->tpool = tpool; *bdsP = bds; - if (unqlite_open(&bds->db, NULL, UNQLITE_OPEN_IN_MEMORY) != UNQLITE_OK) { + return KS_STATUS_SUCCESS; +} + +ks_status_t blade_datastore_config(blade_datastore_t *bds, config_setting_t *config) +{ + config_setting_t *tmp; + config_setting_t *database = NULL; + const char *config_database_path = NULL; + + ks_assert(bds); + + if (!config) return KS_STATUS_FAIL; + if (!config_setting_is_group(config)) return KS_STATUS_FAIL; + + database = config_setting_get_member(config, "database"); + if (!database) return KS_STATUS_FAIL; + tmp = config_lookup_from(database, "path"); + if (!tmp) return KS_STATUS_FAIL; + if (config_setting_type(tmp) != CONFIG_TYPE_STRING) return KS_STATUS_FAIL; + config_database_path = config_setting_get_string(tmp); + + if (bds->config_database_path) ks_pool_free(bds->pool, &bds->config_database_path); + bds->config_database_path = ks_pstrdup(bds->pool, config_database_path); + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_datastore_startup(blade_datastore_t *bds, config_setting_t *config) +{ + ks_assert(bds); + + // @todo check if already started + + if (blade_datastore_config(bds, config) != KS_STATUS_SUCCESS) return KS_STATUS_FAIL; + + if (unqlite_open(&bds->db, bds->config_database_path, UNQLITE_OPEN_CREATE) != UNQLITE_OK) { const char *errbuf = NULL; blade_datastore_error(bds, &errbuf, NULL); - ks_log(KS_LOG_ERROR, "BDS Error: %s\n", errbuf); + ks_log(KS_LOG_ERROR, "BDS Open Error: %s\n", errbuf); return KS_STATUS_FAIL; } @@ -113,12 +133,21 @@ KS_DECLARE(ks_status_t) blade_datastore_create(blade_datastore_t **bdsP, ks_pool return KS_STATUS_SUCCESS; } -KS_DECLARE(void) blade_datastore_pulse(blade_datastore_t *bds, int32_t timeout) +KS_DECLARE(ks_status_t) blade_datastore_shutdown(blade_datastore_t *bds) { ks_assert(bds); - ks_assert(timeout >= 0); + + if (bds->db) { + unqlite_close(bds->db); + bds->db = NULL; + } + + if (bds->config_database_path) ks_pool_free(bds->pool, &bds->config_database_path); + + return KS_STATUS_SUCCESS; } + KS_DECLARE(void) blade_datastore_error(blade_datastore_t *bds, const char **buffer, int32_t *buffer_length) { ks_assert(bds); @@ -147,7 +176,7 @@ KS_DECLARE(ks_status_t) blade_datastore_store(blade_datastore_t *bds, const void else { const char *errbuf; blade_datastore_error(bds, &errbuf, NULL); - ks_log(KS_LOG_ERROR, "BDS Error: %s\n", errbuf); + ks_log(KS_LOG_ERROR, "BDS Store Error: %s\n", errbuf); ret = KS_STATUS_FAIL; } @@ -196,10 +225,11 @@ KS_DECLARE(ks_status_t) blade_datastore_fetch(blade_datastore_t *bds, if (rc != UNQLITE_OK) { if (rc == UNQLITE_BUSY) ret = KS_STATUS_TIMEOUT; + else if(rc == UNQLITE_NOTFOUND) ret = KS_STATUS_NOT_FOUND; else { const char *errbuf; blade_datastore_error(bds, &errbuf, NULL); - ks_log(KS_LOG_ERROR, "BDS Error: %s\n", errbuf); + ks_log(KS_LOG_ERROR, "BDS Fetch Error: %s\n", errbuf); ret = KS_STATUS_FAIL; } diff --git a/libs/libblade/src/blade_identity.c b/libs/libblade/src/blade_identity.c new file mode 100644 index 0000000000..5c2cecafad --- /dev/null +++ b/libs/libblade/src/blade_identity.c @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2017, Shane Bryldt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * 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. + * + * * Neither the name of the original author; nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT OWNER + * OR CONTRIBUTORS 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 "blade.h" + +struct blade_identity_s { + ks_pool_t *pool; + + const char *uri; + + const char *components; + const char *name; + const char *domain; + const char *resource; + ks_hash_t *parameters; +}; + + +KS_DECLARE(ks_status_t) blade_identity_create(blade_identity_t **biP, ks_pool_t *pool) +{ + blade_identity_t *bi = NULL; + + ks_assert(biP); + ks_assert(pool); + + bi = ks_pool_alloc(pool, sizeof(blade_identity_t)); + bi->pool = pool; + *biP = bi; + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_identity_destroy(blade_identity_t **biP) +{ + blade_identity_t *bi = NULL; + + ks_assert(biP); + ks_assert(*biP); + + bi = *biP; + if (bi->uri) { + ks_pool_free(bi->pool, &bi->uri); + ks_pool_free(bi->pool, &bi->components); + } + if (bi->parameters) ks_hash_destroy(&bi->parameters); + + ks_pool_free(bi->pool, biP); + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_identity_parse(blade_identity_t *bi, const char *uri) +{ + char *tmp = NULL; + char *tmp2 = NULL; + + ks_assert(bi); + ks_assert(uri); + + ks_log(KS_LOG_DEBUG, "Parsing URI: %s\n", uri); + + if (bi->uri) { + ks_pool_free(bi->pool, &bi->uri); + ks_pool_free(bi->pool, &bi->components); + } + bi->uri = ks_pstrdup(bi->pool, uri); + bi->components = tmp = ks_pstrdup(bi->pool, uri); + + bi->name = tmp; + if (!(tmp = strchr(tmp, '@'))) return KS_STATUS_FAIL; + *tmp++ = '\0'; + + bi->domain = tmp2 = tmp; + if ((tmp = strchr(tmp, '/'))) { + *tmp++ = '\0'; + bi->resource = tmp2 = tmp; + } else tmp = tmp2; + + if ((tmp = strchr(tmp, '?'))) { + *tmp++ = '\0'; + + while (tmp) { + char *key = tmp; + char *val = NULL; + if (!(tmp = strchr(tmp, '='))) return KS_STATUS_FAIL; + *tmp++ = '\0'; + val = tmp; + if ((tmp = strchr(tmp, '&'))) { + *tmp++ = '\0'; + } + + if (!bi->parameters) { + ks_hash_create(&bi->parameters, KS_HASH_MODE_CASE_INSENSITIVE, KS_HASH_FLAG_NOLOCK | KS_HASH_FLAG_DUP_CHECK, bi->pool); + ks_assert(bi->parameters); + } + ks_hash_insert(bi->parameters, key, val); + } + } + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(const char *) blade_identity_uri(blade_identity_t *bi) +{ + ks_assert(bi); + + return bi->uri; +} + +KS_DECLARE(const char *) blade_identity_parameter_get(blade_identity_t *bi, const char *key) +{ + ks_assert(bi); + ks_assert(key); + + return (const char *)ks_hash_search(bi->parameters, (void *)key, KS_UNLOCKED); +} + + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: + */ diff --git a/libs/libblade/src/blade_method.c b/libs/libblade/src/blade_method.c new file mode 100644 index 0000000000..ac09c0d109 --- /dev/null +++ b/libs/libblade/src/blade_method.c @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2017, Shane Bryldt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * 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. + * + * * Neither the name of the original author; nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT OWNER + * OR CONTRIBUTORS 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 "blade.h" + +struct blade_method_s { + blade_handle_t *handle; + ks_pool_t *pool; + + blade_space_t *space; + const char *name; + + blade_request_callback_t callback; + // @todo more fun descriptive information about the call for remote registrations +}; + + +KS_DECLARE(ks_status_t) blade_method_create(blade_method_t **bmP, blade_space_t *bs, const char *name, blade_request_callback_t callback) +{ + blade_handle_t *bh = NULL; + blade_method_t *bm = NULL; + ks_pool_t *pool = NULL; + + ks_assert(bmP); + ks_assert(bs); + ks_assert(name); + + bh = blade_space_handle_get(bs); + ks_assert(bh); + + pool = blade_handle_pool_get(bh); + ks_assert(pool); + + bm = ks_pool_alloc(pool, sizeof(blade_method_t)); + bm->handle = bh; + bm->pool = pool; + bm->space = bs; + bm->name = name; // @todo dup and keep copy? should mostly be literals + bm->callback = callback; + + *bmP = bm; + + ks_log(KS_LOG_DEBUG, "Method Created: %s.%s\n", blade_space_path_get(bs), name); + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_method_destroy(blade_method_t **bmP) +{ + blade_method_t *bm = NULL; + + ks_assert(bmP); + ks_assert(*bmP); + + bm = *bmP; + + ks_log(KS_LOG_DEBUG, "Method Destroyed: %s.%s\n", blade_space_path_get(bm->space), bm->name); + + ks_pool_free(bm->pool, bmP); + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(const char *) blade_method_name_get(blade_method_t *bm) +{ + ks_assert(bm); + + return bm->name; +} + +KS_DECLARE(blade_request_callback_t) blade_method_callback_get(blade_method_t *bm) +{ + ks_assert(bm); + + return bm->callback; +} + + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: + */ diff --git a/libs/libblade/src/blade_module.c b/libs/libblade/src/blade_module.c new file mode 100644 index 0000000000..403748b0fc --- /dev/null +++ b/libs/libblade/src/blade_module.c @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2017, Shane Bryldt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * 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. + * + * * Neither the name of the original author; nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT OWNER + * OR CONTRIBUTORS 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 "blade.h" + +struct blade_module_s { + blade_handle_t *handle; + ks_pool_t *pool; + + void *module_data; + blade_module_callbacks_t *module_callbacks; +}; + + +KS_DECLARE(ks_status_t) blade_module_create(blade_module_t **bmP, blade_handle_t *bh, void *module_data, blade_module_callbacks_t *module_callbacks) +{ + blade_module_t *bm = NULL; + ks_pool_t *pool = NULL; + + ks_assert(bmP); + ks_assert(bh); + ks_assert(module_data); + ks_assert(module_callbacks); + + pool = blade_handle_pool_get(bh); + + bm = ks_pool_alloc(pool, sizeof(blade_module_t)); + bm->handle = bh; + bm->pool = pool; + bm->module_data = module_data; + bm->module_callbacks = module_callbacks; + *bmP = bm; + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_module_destroy(blade_module_t **bmP) +{ + blade_module_t *bm = NULL; + + ks_assert(bmP); + ks_assert(*bmP); + + bm = *bmP; + + ks_pool_free(bm->pool, bmP); + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(blade_handle_t *) blade_module_handle_get(blade_module_t *bm) +{ + ks_assert(bm); + + return bm->handle; +} + +KS_DECLARE(void *) blade_module_data_get(blade_module_t *bm) +{ + ks_assert(bm); + + return bm->module_data; +} + + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: + */ diff --git a/libs/libblade/src/blade_module_chat.c b/libs/libblade/src/blade_module_chat.c new file mode 100644 index 0000000000..a9fbfc3b3a --- /dev/null +++ b/libs/libblade/src/blade_module_chat.c @@ -0,0 +1,432 @@ +/* + * Copyright (c) 2017, Shane Bryldt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * 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. + * + * * Neither the name of the original author; nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT OWNER + * OR CONTRIBUTORS 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 "blade.h" + +typedef struct blade_module_chat_s blade_module_chat_t; + +struct blade_module_chat_s { + blade_handle_t *handle; + ks_pool_t *pool; + ks_thread_pool_t *tpool; + blade_module_t *module; + blade_module_callbacks_t *module_callbacks; + + const char *session_state_callback_id; + list_t participants; +}; + + +ks_status_t blade_module_chat_create(blade_module_chat_t **bm_chatP, blade_handle_t *bh); +ks_status_t blade_module_chat_destroy(blade_module_chat_t **bm_chatP); + +// @todo remove exporting this, it's only temporary until DSO loading is in place so wss module can be loaded +KS_DECLARE(ks_status_t) blade_module_chat_on_load(blade_module_t **bmP, blade_handle_t *bh); +KS_DECLARE(ks_status_t) blade_module_chat_on_unload(blade_module_t *bm); +KS_DECLARE(ks_status_t) blade_module_chat_on_startup(blade_module_t *bm, config_setting_t *config); +KS_DECLARE(ks_status_t) blade_module_chat_on_shutdown(blade_module_t *bm); + +void blade_module_chat_on_session_state(blade_session_t *bs, blade_session_state_condition_t condition, void *data); + +ks_bool_t blade_chat_join_request_handler(blade_module_t *bm, blade_request_t *breq); +ks_bool_t blade_chat_leave_request_handler(blade_module_t *bm, blade_request_t *breq); +ks_bool_t blade_chat_send_request_handler(blade_module_t *bm, blade_request_t *breq); + +static blade_module_callbacks_t g_module_chat_callbacks = +{ + blade_module_chat_on_load, + blade_module_chat_on_unload, + blade_module_chat_on_startup, + blade_module_chat_on_shutdown, +}; + + + +ks_status_t blade_module_chat_create(blade_module_chat_t **bm_chatP, blade_handle_t *bh) +{ + blade_module_chat_t *bm_chat = NULL; + ks_pool_t *pool = NULL; + + ks_assert(bm_chatP); + ks_assert(bh); + + pool = blade_handle_pool_get(bh); + + bm_chat = ks_pool_alloc(pool, sizeof(blade_module_chat_t)); + bm_chat->handle = bh; + bm_chat->pool = pool; + bm_chat->tpool = blade_handle_tpool_get(bh); + bm_chat->session_state_callback_id = NULL; + list_init(&bm_chat->participants); + + blade_module_create(&bm_chat->module, bh, bm_chat, &g_module_chat_callbacks); + bm_chat->module_callbacks = &g_module_chat_callbacks; + + *bm_chatP = bm_chat; + + ks_log(KS_LOG_DEBUG, "Created\n"); + + return KS_STATUS_SUCCESS; +} + +ks_status_t blade_module_chat_destroy(blade_module_chat_t **bm_chatP) +{ + blade_module_chat_t *bm_chat = NULL; + + ks_assert(bm_chatP); + ks_assert(*bm_chatP); + + bm_chat = *bm_chatP; + + blade_module_chat_on_shutdown(bm_chat->module); + + list_destroy(&bm_chat->participants); + + blade_module_destroy(&bm_chat->module); + + ks_pool_free(bm_chat->pool, bm_chatP); + + ks_log(KS_LOG_DEBUG, "Destroyed\n"); + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_module_chat_on_load(blade_module_t **bmP, blade_handle_t *bh) +{ + blade_module_chat_t *bm_chat = NULL; + + ks_assert(bmP); + ks_assert(bh); + + blade_module_chat_create(&bm_chat, bh); + ks_assert(bm_chat); + + *bmP = bm_chat->module; + + ks_log(KS_LOG_DEBUG, "Loaded\n"); + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_module_chat_on_unload(blade_module_t *bm) +{ + blade_module_chat_t *bm_chat = NULL; + + ks_assert(bm); + + bm_chat = blade_module_data_get(bm); + + blade_module_chat_destroy(&bm_chat); + + ks_log(KS_LOG_DEBUG, "Unloaded\n"); + + return KS_STATUS_SUCCESS; +} + +ks_status_t blade_module_chat_config(blade_module_chat_t *bm_chat, config_setting_t *config) +{ + config_setting_t *chat = NULL; + + ks_assert(bm_chat); + ks_assert(config); + + if (!config_setting_is_group(config)) { + ks_log(KS_LOG_DEBUG, "!config_setting_is_group(config)\n"); + return KS_STATUS_FAIL; + } + + chat = config_setting_get_member(config, "chat"); + if (chat) { + } + + + // Configuration is valid, now assign it to the variables that are used + // If the configuration was invalid, then this does not get changed + + ks_log(KS_LOG_DEBUG, "Configured\n"); + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_module_chat_on_startup(blade_module_t *bm, config_setting_t *config) +{ + blade_module_chat_t *bm_chat = NULL; + blade_space_t *space = NULL; + blade_method_t *method = NULL; + + ks_assert(bm); + ks_assert(config); + + bm_chat = (blade_module_chat_t *)blade_module_data_get(bm); + + if (blade_module_chat_config(bm_chat, config) != KS_STATUS_SUCCESS) { + ks_log(KS_LOG_DEBUG, "blade_module_chat_config failed\n"); + return KS_STATUS_FAIL; + } + + blade_space_create(&space, bm_chat->handle, bm, "blade.chat"); + ks_assert(space); + + blade_method_create(&method, space, "join", blade_chat_join_request_handler); + ks_assert(method); + blade_space_methods_add(space, method); + + blade_method_create(&method, space, "leave", blade_chat_leave_request_handler); + ks_assert(method); + blade_space_methods_add(space, method); + + blade_method_create(&method, space, "send", blade_chat_send_request_handler); + ks_assert(method); + blade_space_methods_add(space, method); + + blade_handle_space_register(space); + + blade_handle_session_state_callback_register(blade_module_handle_get(bm), bm, blade_module_chat_on_session_state, &bm_chat->session_state_callback_id); + + ks_log(KS_LOG_DEBUG, "Started\n"); + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_module_chat_on_shutdown(blade_module_t *bm) +{ + blade_module_chat_t *bm_chat = NULL; + + ks_assert(bm); + + bm_chat = (blade_module_chat_t *)blade_module_data_get(bm); + ks_assert(bm_chat); + + if (bm_chat->session_state_callback_id) blade_handle_session_state_callback_unregister(blade_module_handle_get(bm), bm_chat->session_state_callback_id); + bm_chat->session_state_callback_id = NULL; + + ks_log(KS_LOG_DEBUG, "Stopped\n"); + + return KS_STATUS_SUCCESS; +} + +void blade_module_chat_on_session_state(blade_session_t *bs, blade_session_state_condition_t condition, void *data) +{ + blade_module_t *bm = NULL; + blade_module_chat_t *bm_chat = NULL; + + ks_assert(bs); + ks_assert(data); + + bm = (blade_module_t *)data; + bm_chat = (blade_module_chat_t *)blade_module_data_get(bm); + ks_assert(bm_chat); + + if (blade_session_state_get(bs) == BLADE_SESSION_STATE_HANGUP && condition == BLADE_SESSION_STATE_CONDITION_PRE) { + cJSON *props = NULL; + + ks_log(KS_LOG_DEBUG, "Removing session from chat participants if present\n"); + + props = blade_session_properties_get(bs); + ks_assert(props); + + cJSON_DeleteItemFromObject(props, "blade.chat.participant"); + + list_delete(&bm_chat->participants, blade_session_id_get(bs)); // @todo make copy of session id instead and search manually, also free the id + } +} + +ks_bool_t blade_chat_join_request_handler(blade_module_t *bm, blade_request_t *breq) +{ + blade_module_chat_t *bm_chat = NULL; + blade_session_t *bs = NULL; + cJSON *res = NULL; + cJSON *props = NULL; + cJSON *props_participant = NULL; + + ks_assert(bm); + ks_assert(breq); + + ks_log(KS_LOG_DEBUG, "Request Received!\n"); + + bm_chat = (blade_module_chat_t *)blade_module_data_get(bm); + ks_assert(bm_chat); + + bs = blade_handle_sessions_get(breq->handle, breq->session_id); + ks_assert(bs); + + // @todo properties only used to demonstrate a flexible container for session data, should just rely on the participants list/hash + blade_session_properties_write_lock(bs, KS_TRUE); + + props = blade_session_properties_get(bs); + ks_assert(props); + + props_participant = cJSON_GetObjectItem(props, "blade.chat.participant"); + if (props_participant && props_participant->type == cJSON_True) { + ks_log(KS_LOG_DEBUG, "Session (%s) attempted to join chat but is already a participant\n", blade_session_id_get(bs)); + blade_rpc_error_create(breq->pool, &res, NULL, breq->message_id, -10000, "Already a participant of chat"); + } else { + ks_log(KS_LOG_DEBUG, "Session (%s) joined chat\n", blade_session_id_get(bs)); + + if (props_participant) props_participant->type = cJSON_True; + else cJSON_AddTrueToObject(props, "blade.chat.participant"); + + list_append(&bm_chat->participants, blade_session_id_get(bs)); // @todo make copy of session id instead and cleanup when removed + + blade_rpc_response_create(breq->pool, &res, NULL, breq->message_id); + + // @todo create an event to send to participants when a session joins and leaves, send after main response though + } + + blade_session_properties_write_unlock(bs); + + blade_session_send(bs, res, NULL); + + blade_session_read_unlock(bs); + + cJSON_Delete(res); + + return KS_FALSE; +} + +ks_bool_t blade_chat_leave_request_handler(blade_module_t *bm, blade_request_t *breq) +{ + blade_module_chat_t *bm_chat = NULL; + blade_session_t *bs = NULL; + cJSON *res = NULL; + cJSON *props = NULL; + cJSON *props_participant = NULL; + + ks_assert(bm); + ks_assert(breq); + + ks_log(KS_LOG_DEBUG, "Request Received!\n"); + + bm_chat = (blade_module_chat_t *)blade_module_data_get(bm); + ks_assert(bm_chat); + + bs = blade_handle_sessions_get(breq->handle, breq->session_id); + ks_assert(bs); + + blade_session_properties_write_lock(bs, KS_TRUE); + + props = blade_session_properties_get(bs); + ks_assert(props); + + props_participant = cJSON_GetObjectItem(props, "blade.chat.participant"); + if (!props_participant || props_participant->type == cJSON_False) { + ks_log(KS_LOG_DEBUG, "Session (%s) attempted to leave chat but is not a participant\n", blade_session_id_get(bs)); + blade_rpc_error_create(breq->pool, &res, NULL, breq->message_id, -10000, "Not a participant of chat"); + } else { + ks_log(KS_LOG_DEBUG, "Session (%s) left chat\n", blade_session_id_get(bs)); + + cJSON_DeleteItemFromObject(props, "blade.chat.participant"); + + list_delete(&bm_chat->participants, blade_session_id_get(bs)); // @todo make copy of session id instead and search manually, also free the id + + blade_rpc_response_create(breq->pool, &res, NULL, breq->message_id); + + // @todo create an event to send to participants when a session joins and leaves, send after main response though + } + + blade_session_properties_write_unlock(bs); + + blade_session_send(bs, res, NULL); + + blade_session_read_unlock(bs); + + cJSON_Delete(res); + + return KS_FALSE; +} + +ks_bool_t blade_chat_send_request_handler(blade_module_t *bm, blade_request_t *breq) +{ + blade_module_chat_t *bm_chat = NULL; + blade_session_t *bs = NULL; + cJSON *params = NULL; + cJSON *res = NULL; + cJSON *event = NULL; + const char *message = NULL; + ks_bool_t sendevent = KS_FALSE; + + ks_assert(bm); + ks_assert(breq); + + ks_log(KS_LOG_DEBUG, "Request Received!\n"); + + bm_chat = (blade_module_chat_t *)blade_module_data_get(bm); + ks_assert(bm_chat); + + params = cJSON_GetObjectItem(breq->message, "params"); // @todo cache this in blade_request_t for quicker/easier access + if (!params) { + ks_log(KS_LOG_DEBUG, "Session (%s) attempted to send chat message with no 'params' object\n", blade_session_id_get(bs)); + blade_rpc_error_create(breq->pool, &res, NULL, breq->message_id, -32602, "Missing params object"); + } else if (!(message = cJSON_GetObjectCstr(params, "message"))) { + ks_log(KS_LOG_DEBUG, "Session (%s) attempted to send chat message with no 'message'\n", blade_session_id_get(bs)); + blade_rpc_error_create(breq->pool, &res, NULL, breq->message_id, -32602, "Missing params message string"); + } + + bs = blade_handle_sessions_get(breq->handle, breq->session_id); + ks_assert(bs); + + if (!res) { + blade_rpc_response_create(breq->pool, &res, NULL, breq->message_id); + sendevent = KS_TRUE; + } + blade_session_send(bs, res, NULL); + + blade_session_read_unlock(bs); + + cJSON_Delete(res); + + if (sendevent) { + blade_rpc_event_create(breq->pool, &event, &res, "blade.chat.message"); + ks_assert(event); + cJSON_AddStringToObject(res, "from", breq->session_id); // @todo should really be the identity, but we don't have that in place yet + cJSON_AddStringToObject(res, "message", message); + + blade_handle_sessions_send(breq->handle, &bm_chat->participants, NULL, event); + + cJSON_Delete(event); + } + + return KS_FALSE; +} + + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: + */ diff --git a/libs/libblade/src/blade_module_wss.c b/libs/libblade/src/blade_module_wss.c new file mode 100644 index 0000000000..107a8420d3 --- /dev/null +++ b/libs/libblade/src/blade_module_wss.c @@ -0,0 +1,1321 @@ +/* + * Copyright (c) 2017, Shane Bryldt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * 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. + * + * * Neither the name of the original author; nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT OWNER + * OR CONTRIBUTORS 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 "blade.h" + +#define BLADE_MODULE_WSS_TRANSPORT_NAME "wss" +#define BLADE_MODULE_WSS_ENDPOINTS_MULTIHOME_MAX 16 + +typedef struct blade_module_wss_s blade_module_wss_t; +typedef struct blade_transport_wss_s blade_transport_wss_t; +typedef struct blade_transport_wss_init_s blade_transport_wss_init_t; + +struct blade_module_wss_s { + blade_handle_t *handle; + ks_pool_t *pool; + ks_thread_pool_t *tpool; + blade_module_t *module; + blade_module_callbacks_t *module_callbacks; + blade_transport_callbacks_t *transport_callbacks; + + ks_sockaddr_t config_wss_endpoints_ipv4[BLADE_MODULE_WSS_ENDPOINTS_MULTIHOME_MAX]; + ks_sockaddr_t config_wss_endpoints_ipv6[BLADE_MODULE_WSS_ENDPOINTS_MULTIHOME_MAX]; + int32_t config_wss_endpoints_ipv4_length; + int32_t config_wss_endpoints_ipv6_length; + int32_t config_wss_endpoints_backlog; + + ks_bool_t shutdown; + + ks_thread_t *listeners_thread; + struct pollfd *listeners_poll; + int32_t listeners_count; + + list_t connected; // @todo consider keeping this only as the list of connection id's, since the handle retains the pointer lookup +}; + +struct blade_transport_wss_s { + blade_module_wss_t *module; + ks_pool_t *pool; + + ks_socket_t sock; + kws_t *kws; +}; + +struct blade_transport_wss_init_s { + blade_module_wss_t *module; + ks_pool_t *pool; + + ks_socket_t sock; + const char *session_id; +}; + + + +ks_status_t blade_module_wss_create(blade_module_wss_t **bm_wssP, blade_handle_t *bh); +ks_status_t blade_module_wss_destroy(blade_module_wss_t **bm_wssP); + +// @todo remove exporting this, it's only temporary until DSO loading is in place so wss module can be loaded +KS_DECLARE(ks_status_t) blade_module_wss_on_load(blade_module_t **bmP, blade_handle_t *bh); +KS_DECLARE(ks_status_t) blade_module_wss_on_unload(blade_module_t *bm); +KS_DECLARE(ks_status_t) blade_module_wss_on_startup(blade_module_t *bm, config_setting_t *config); +KS_DECLARE(ks_status_t) blade_module_wss_on_shutdown(blade_module_t *bm); + +ks_status_t blade_module_wss_listen(blade_module_wss_t *bm, ks_sockaddr_t *addr); +void *blade_module_wss_listeners_thread(ks_thread_t *thread, void *data); + + + +ks_status_t blade_transport_wss_create(blade_transport_wss_t **bt_wssP, blade_module_wss_t *bm_wss, ks_socket_t sock); +ks_status_t blade_transport_wss_destroy(blade_transport_wss_t **bt_wssP); + +ks_status_t blade_transport_wss_on_connect(blade_connection_t **bcP, blade_module_t *bm, blade_identity_t *target, const char *session_id); +blade_connection_rank_t blade_transport_wss_on_rank(blade_connection_t *bc, blade_identity_t *target); + +ks_status_t blade_transport_wss_on_send(blade_connection_t *bc, cJSON *json); +ks_status_t blade_transport_wss_on_receive(blade_connection_t *bc, cJSON **json); + +blade_connection_state_hook_t blade_transport_wss_on_state_disconnect(blade_connection_t *bc, blade_connection_state_condition_t condition); +blade_connection_state_hook_t blade_transport_wss_on_state_new_inbound(blade_connection_t *bc, blade_connection_state_condition_t condition); +blade_connection_state_hook_t blade_transport_wss_on_state_new_outbound(blade_connection_t *bc, blade_connection_state_condition_t condition); +blade_connection_state_hook_t blade_transport_wss_on_state_connect_inbound(blade_connection_t *bc, blade_connection_state_condition_t condition); +blade_connection_state_hook_t blade_transport_wss_on_state_connect_outbound(blade_connection_t *bc, blade_connection_state_condition_t condition); +blade_connection_state_hook_t blade_transport_wss_on_state_attach_inbound(blade_connection_t *bc, blade_connection_state_condition_t condition); +blade_connection_state_hook_t blade_transport_wss_on_state_attach_outbound(blade_connection_t *bc, blade_connection_state_condition_t condition); +blade_connection_state_hook_t blade_transport_wss_on_state_detach_inbound(blade_connection_t *bc, blade_connection_state_condition_t condition); +blade_connection_state_hook_t blade_transport_wss_on_state_detach_outbound(blade_connection_t *bc, blade_connection_state_condition_t condition); +blade_connection_state_hook_t blade_transport_wss_on_state_ready_inbound(blade_connection_t *bc, blade_connection_state_condition_t condition); +blade_connection_state_hook_t blade_transport_wss_on_state_ready_outbound(blade_connection_t *bc, blade_connection_state_condition_t condition); + + + +ks_status_t blade_transport_wss_init_create(blade_transport_wss_init_t **bt_wssiP, blade_module_wss_t *bm_wss, ks_socket_t sock, const char *session_id); +ks_status_t blade_transport_wss_init_destroy(blade_transport_wss_init_t **bt_wssiP); + + +ks_bool_t blade_test_echo_request_handler(blade_module_t *bm, blade_request_t *breq); +ks_bool_t blade_test_echo_response_handler(blade_response_t *bres); + + +static blade_module_callbacks_t g_module_wss_callbacks = +{ + blade_module_wss_on_load, + blade_module_wss_on_unload, + blade_module_wss_on_startup, + blade_module_wss_on_shutdown, +}; + +static blade_transport_callbacks_t g_transport_wss_callbacks = +{ + blade_transport_wss_on_connect, + blade_transport_wss_on_rank, + blade_transport_wss_on_send, + blade_transport_wss_on_receive, + + blade_transport_wss_on_state_disconnect, + blade_transport_wss_on_state_disconnect, + blade_transport_wss_on_state_new_inbound, + blade_transport_wss_on_state_new_outbound, + blade_transport_wss_on_state_connect_inbound, + blade_transport_wss_on_state_connect_outbound, + blade_transport_wss_on_state_attach_inbound, + blade_transport_wss_on_state_attach_outbound, + blade_transport_wss_on_state_detach_inbound, + blade_transport_wss_on_state_detach_outbound, + blade_transport_wss_on_state_ready_inbound, + blade_transport_wss_on_state_ready_outbound, +}; + + + +ks_status_t blade_module_wss_create(blade_module_wss_t **bm_wssP, blade_handle_t *bh) +{ + blade_module_wss_t *bm_wss = NULL; + ks_pool_t *pool = NULL; + + ks_assert(bm_wssP); + ks_assert(bh); + + pool = blade_handle_pool_get(bh); + + bm_wss = ks_pool_alloc(pool, sizeof(blade_module_wss_t)); + bm_wss->handle = bh; + bm_wss->pool = pool; + bm_wss->tpool = blade_handle_tpool_get(bh); + + blade_module_create(&bm_wss->module, bh, bm_wss, &g_module_wss_callbacks); + bm_wss->module_callbacks = &g_module_wss_callbacks; + bm_wss->transport_callbacks = &g_transport_wss_callbacks; + + list_init(&bm_wss->connected); + + *bm_wssP = bm_wss; + + ks_log(KS_LOG_DEBUG, "Created\n"); + + return KS_STATUS_SUCCESS; +} + +ks_status_t blade_module_wss_destroy(blade_module_wss_t **bm_wssP) +{ + blade_module_wss_t *bm_wss = NULL; + + ks_assert(bm_wssP); + ks_assert(*bm_wssP); + + bm_wss = *bm_wssP; + + blade_module_wss_on_shutdown(bm_wss->module); + + blade_module_destroy(&bm_wss->module); + + list_destroy(&bm_wss->connected); + + ks_pool_free(bm_wss->pool, bm_wssP); + + ks_log(KS_LOG_DEBUG, "Destroyed\n"); + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_module_wss_on_load(blade_module_t **bmP, blade_handle_t *bh) +{ + blade_module_wss_t *bm_wss = NULL; + + ks_assert(bmP); + ks_assert(bh); + + blade_module_wss_create(&bm_wss, bh); + ks_assert(bm_wss); + + *bmP = bm_wss->module; + + ks_log(KS_LOG_DEBUG, "Loaded\n"); + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_module_wss_on_unload(blade_module_t *bm) +{ + blade_module_wss_t *bm_wss = NULL; + + ks_assert(bm); + + bm_wss = blade_module_data_get(bm); + + blade_module_wss_destroy(&bm_wss); + + ks_log(KS_LOG_DEBUG, "Unloaded\n"); + + return KS_STATUS_SUCCESS; +} + +ks_status_t blade_transport_wss_init_create(blade_transport_wss_init_t **bt_wssiP, blade_module_wss_t *bm_wss, ks_socket_t sock, const char *session_id) +{ + blade_transport_wss_init_t *bt_wssi = NULL; + + ks_assert(bt_wssiP); + ks_assert(bm_wss); + ks_assert(sock != KS_SOCK_INVALID); + + bt_wssi = ks_pool_alloc(bm_wss->pool, sizeof(blade_transport_wss_init_t)); + bt_wssi->module = bm_wss; + bt_wssi->pool = bm_wss->pool; + bt_wssi->sock = sock; + if (session_id) bt_wssi->session_id = ks_pstrdup(bt_wssi->pool, session_id); + + *bt_wssiP = bt_wssi; + + ks_log(KS_LOG_DEBUG, "Created\n"); + + return KS_STATUS_SUCCESS; +} + +ks_status_t blade_transport_wss_init_destroy(blade_transport_wss_init_t **bt_wssiP) +{ + blade_transport_wss_init_t *bt_wssi = NULL; + + ks_assert(bt_wssiP); + ks_assert(*bt_wssiP); + + bt_wssi = *bt_wssiP; + + if (bt_wssi->session_id) ks_pool_free(bt_wssi->pool, &bt_wssi->session_id); + + ks_pool_free(bt_wssi->pool, bt_wssiP); + + ks_log(KS_LOG_DEBUG, "Destroyed\n"); + + return KS_STATUS_SUCCESS; +} + +ks_status_t blade_module_wss_config(blade_module_wss_t *bm_wss, config_setting_t *config) +{ + config_setting_t *wss = NULL; + config_setting_t *wss_endpoints = NULL; + config_setting_t *wss_endpoints_ipv4 = NULL; + config_setting_t *wss_endpoints_ipv6 = NULL; + config_setting_t *wss_ssl = NULL; + config_setting_t *element; + config_setting_t *tmp1; + config_setting_t *tmp2; + ks_sockaddr_t config_wss_endpoints_ipv4[BLADE_MODULE_WSS_ENDPOINTS_MULTIHOME_MAX]; + ks_sockaddr_t config_wss_endpoints_ipv6[BLADE_MODULE_WSS_ENDPOINTS_MULTIHOME_MAX]; + int32_t config_wss_endpoints_ipv4_length = 0; + int32_t config_wss_endpoints_ipv6_length = 0; + int32_t config_wss_endpoints_backlog = 8; + + ks_assert(bm_wss); + ks_assert(config); + + if (!config_setting_is_group(config)) { + ks_log(KS_LOG_DEBUG, "!config_setting_is_group(config)\n"); + return KS_STATUS_FAIL; + } + + wss = config_setting_get_member(config, "wss"); + if (wss) { + wss_endpoints = config_setting_get_member(wss, "endpoints"); + if (!wss_endpoints) { + ks_log(KS_LOG_DEBUG, "!wss_endpoints\n"); + return KS_STATUS_FAIL; + } + wss_endpoints_ipv4 = config_lookup_from(wss_endpoints, "ipv4"); + wss_endpoints_ipv6 = config_lookup_from(wss_endpoints, "ipv6"); + if (wss_endpoints_ipv4) { + if (config_setting_type(wss_endpoints_ipv4) != CONFIG_TYPE_LIST) return KS_STATUS_FAIL; + if ((config_wss_endpoints_ipv4_length = config_setting_length(wss_endpoints_ipv4)) > BLADE_MODULE_WSS_ENDPOINTS_MULTIHOME_MAX) + return KS_STATUS_FAIL; + + for (int32_t index = 0; index < config_wss_endpoints_ipv4_length; ++index) { + element = config_setting_get_elem(wss_endpoints_ipv4, index); + tmp1 = config_lookup_from(element, "address"); + tmp2 = config_lookup_from(element, "port"); + if (!tmp1 || !tmp2) return KS_STATUS_FAIL; + if (config_setting_type(tmp1) != CONFIG_TYPE_STRING) return KS_STATUS_FAIL; + if (config_setting_type(tmp2) != CONFIG_TYPE_INT) return KS_STATUS_FAIL; + + if (ks_addr_set(&config_wss_endpoints_ipv4[index], + config_setting_get_string(tmp1), + config_setting_get_int(tmp2), + AF_INET) != KS_STATUS_SUCCESS) return KS_STATUS_FAIL; + ks_log(KS_LOG_DEBUG, + "Binding to IPV4 %s on port %d\n", + ks_addr_get_host(&config_wss_endpoints_ipv4[index]), + ks_addr_get_port(&config_wss_endpoints_ipv4[index])); + } + } + if (wss_endpoints_ipv6) { + if (config_setting_type(wss_endpoints_ipv6) != CONFIG_TYPE_LIST) return KS_STATUS_FAIL; + if ((config_wss_endpoints_ipv6_length = config_setting_length(wss_endpoints_ipv6)) > BLADE_MODULE_WSS_ENDPOINTS_MULTIHOME_MAX) + return KS_STATUS_FAIL; + + for (int32_t index = 0; index < config_wss_endpoints_ipv6_length; ++index) { + element = config_setting_get_elem(wss_endpoints_ipv6, index); + tmp1 = config_lookup_from(element, "address"); + tmp2 = config_lookup_from(element, "port"); + if (!tmp1 || !tmp2) return KS_STATUS_FAIL; + if (config_setting_type(tmp1) != CONFIG_TYPE_STRING) return KS_STATUS_FAIL; + if (config_setting_type(tmp2) != CONFIG_TYPE_INT) return KS_STATUS_FAIL; + + + if (ks_addr_set(&config_wss_endpoints_ipv6[index], + config_setting_get_string(tmp1), + config_setting_get_int(tmp2), + AF_INET6) != KS_STATUS_SUCCESS) return KS_STATUS_FAIL; + ks_log(KS_LOG_DEBUG, + "Binding to IPV6 %s on port %d\n", + ks_addr_get_host(&config_wss_endpoints_ipv6[index]), + ks_addr_get_port(&config_wss_endpoints_ipv6[index])); + } + } + if (config_wss_endpoints_ipv4_length + config_wss_endpoints_ipv6_length <= 0) return KS_STATUS_FAIL; + tmp1 = config_lookup_from(wss_endpoints, "backlog"); + if (tmp1) { + if (config_setting_type(tmp1) != CONFIG_TYPE_INT) return KS_STATUS_FAIL; + config_wss_endpoints_backlog = config_setting_get_int(tmp1); + } + wss_ssl = config_setting_get_member(wss, "ssl"); + if (wss_ssl) { + // @todo: SSL stuffs from wss_ssl into config_wss_ssl envelope + } + } + + + // Configuration is valid, now assign it to the variables that are used + // If the configuration was invalid, then this does not get changed + for (int32_t index = 0; index < config_wss_endpoints_ipv4_length; ++index) + bm_wss->config_wss_endpoints_ipv4[index] = config_wss_endpoints_ipv4[index]; + for (int32_t index = 0; index < config_wss_endpoints_ipv6_length; ++index) + bm_wss->config_wss_endpoints_ipv6[index] = config_wss_endpoints_ipv6[index]; + bm_wss->config_wss_endpoints_ipv4_length = config_wss_endpoints_ipv4_length; + bm_wss->config_wss_endpoints_ipv6_length = config_wss_endpoints_ipv6_length; + bm_wss->config_wss_endpoints_backlog = config_wss_endpoints_backlog; + //bm_wss->config_wss_ssl = config_wss_ssl; + + ks_log(KS_LOG_DEBUG, "Configured\n"); + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_module_wss_on_startup(blade_module_t *bm, config_setting_t *config) +{ + blade_module_wss_t *bm_wss = NULL; + blade_space_t *space = NULL; + blade_method_t *method = NULL; + + ks_assert(bm); + ks_assert(config); + + bm_wss = (blade_module_wss_t *)blade_module_data_get(bm); + + if (blade_module_wss_config(bm_wss, config) != KS_STATUS_SUCCESS) { + ks_log(KS_LOG_DEBUG, "blade_module_wss_config failed\n"); + return KS_STATUS_FAIL; + } + + for (int32_t index = 0; index < bm_wss->config_wss_endpoints_ipv4_length; ++index) { + if (blade_module_wss_listen(bm_wss, &bm_wss->config_wss_endpoints_ipv4[index]) != KS_STATUS_SUCCESS) { + ks_log(KS_LOG_DEBUG, "blade_module_wss_listen (v4) failed\n"); + return KS_STATUS_FAIL; + } + } + for (int32_t index = 0; index < bm_wss->config_wss_endpoints_ipv6_length; ++index) { + if (blade_module_wss_listen(bm_wss, &bm_wss->config_wss_endpoints_ipv6[index]) != KS_STATUS_SUCCESS) { + ks_log(KS_LOG_DEBUG, "blade_module_wss_listen (v6) failed\n"); + return KS_STATUS_FAIL; + } + } + + if (ks_thread_create_ex(&bm_wss->listeners_thread, + blade_module_wss_listeners_thread, + bm_wss, + KS_THREAD_FLAG_DEFAULT, + KS_THREAD_DEFAULT_STACK, + KS_PRI_NORMAL, + bm_wss->pool) != KS_STATUS_SUCCESS) return KS_STATUS_FAIL; + + blade_handle_transport_register(bm_wss->handle, bm, BLADE_MODULE_WSS_TRANSPORT_NAME, bm_wss->transport_callbacks); + + + blade_space_create(&space, bm_wss->handle, bm, "blade.test"); + ks_assert(space); + + blade_method_create(&method, space, "echo", blade_test_echo_request_handler); + ks_assert(method); + + blade_space_methods_add(space, method); + + blade_handle_space_register(space); + + ks_log(KS_LOG_DEBUG, "Started\n"); + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_module_wss_on_shutdown(blade_module_t *bm) +{ + blade_module_wss_t *bm_wss = NULL; + blade_connection_t *bc = NULL; + ks_bool_t stopped = KS_FALSE; + + ks_assert(bm); + + bm_wss = (blade_module_wss_t *)blade_module_data_get(bm); + + blade_handle_transport_unregister(bm_wss->handle, BLADE_MODULE_WSS_TRANSPORT_NAME); + + if (bm_wss->listeners_thread) { + bm_wss->shutdown = KS_TRUE; + ks_thread_join(bm_wss->listeners_thread); + ks_pool_free(bm_wss->pool, &bm_wss->listeners_thread); + bm_wss->shutdown = KS_FALSE; + stopped = KS_TRUE; + } + + for (int32_t index = 0; index < bm_wss->listeners_count; ++index) { + ks_socket_t sock = bm_wss->listeners_poll[index].fd; + ks_socket_shutdown(sock, SHUT_RDWR); + ks_socket_close(&sock); + } + bm_wss->listeners_count = 0; + if (bm_wss->listeners_poll) ks_pool_free(bm_wss->pool, &bm_wss->listeners_poll); + + if (list_size(&bm_wss->connected) > 0) { + // this approach to shutdown is cleaner, ensures connections will detach from sessions and be destroyed all in the same places + list_iterator_start(&bm_wss->connected); + while (list_iterator_hasnext(&bm_wss->connected)) { + bc = (blade_connection_t *)list_iterator_next(&bm_wss->connected); + blade_connection_disconnect(bc); + } + list_iterator_stop(&bm_wss->connected); + while (list_size(&bm_wss->connected) > 0) ks_sleep_ms(100); + } + + if (stopped) ks_log(KS_LOG_DEBUG, "Stopped\n"); + + return KS_STATUS_SUCCESS; +} + +ks_status_t blade_module_wss_listen(blade_module_wss_t *bm_wss, ks_sockaddr_t *addr) +{ + ks_socket_t listener = KS_SOCK_INVALID; + int32_t listener_index = -1; + ks_status_t ret = KS_STATUS_SUCCESS; + + ks_assert(bm_wss); + ks_assert(addr); + + if ((listener = socket(addr->family, SOCK_STREAM, IPPROTO_TCP)) == KS_SOCK_INVALID) { + ks_log(KS_LOG_DEBUG, "listener == KS_SOCK_INVALID\n"); + ret = KS_STATUS_FAIL; + goto done; + } + + ks_socket_option(listener, SO_REUSEADDR, KS_TRUE); + ks_socket_option(listener, TCP_NODELAY, KS_TRUE); + if (addr->family == AF_INET6) ks_socket_option(listener, IPV6_V6ONLY, KS_TRUE); + + if (ks_addr_bind(listener, addr) != KS_STATUS_SUCCESS) { + ks_log(KS_LOG_DEBUG, "ks_addr_bind(listener, addr) != KS_STATUS_SUCCESS\n"); + ret = KS_STATUS_FAIL; + goto done; + } + + if (listen(listener, bm_wss->config_wss_endpoints_backlog) != 0) { + ks_log(KS_LOG_DEBUG, "listen(listener, backlog) != 0\n"); + ret = KS_STATUS_FAIL; + goto done; + } + + listener_index = bm_wss->listeners_count++; + bm_wss->listeners_poll = (struct pollfd *)ks_pool_resize(bm_wss->pool, + bm_wss->listeners_poll, + sizeof(struct pollfd) * bm_wss->listeners_count); + ks_assert(bm_wss->listeners_poll); + bm_wss->listeners_poll[listener_index].fd = listener; + bm_wss->listeners_poll[listener_index].events = POLLIN | POLLERR; + + ks_log(KS_LOG_DEBUG, "Bound %s on port %d at index %d\n", ks_addr_get_host(addr), ks_addr_get_port(addr), listener_index); + + done: + if (ret != KS_STATUS_SUCCESS) { + if (listener != KS_SOCK_INVALID) { + ks_socket_shutdown(listener, SHUT_RDWR); + ks_socket_close(&listener); + } + } + return ret; +} + +void *blade_module_wss_listeners_thread(ks_thread_t *thread, void *data) +{ + blade_module_wss_t *bm_wss = NULL; + blade_transport_wss_init_t *bt_wss_init = NULL; + blade_connection_t *bc = NULL; + + ks_assert(thread); + ks_assert(data); + + bm_wss = (blade_module_wss_t *)data; + + ks_log(KS_LOG_DEBUG, "Started\n"); + while (!bm_wss->shutdown) { + // @todo take exact timeout from a setting in config_wss_endpoints + if (ks_poll(bm_wss->listeners_poll, bm_wss->listeners_count, 100) > 0) { + for (int32_t index = 0; index < bm_wss->listeners_count; ++index) { + ks_socket_t sock = KS_SOCK_INVALID; + + if (bm_wss->listeners_poll[index].revents & POLLERR) { + // @todo: error handling, just skip the listener for now, it might recover, could skip X times before closing? + ks_log(KS_LOG_DEBUG, "POLLERR on index %d\n", index); + continue; + } + if (!(bm_wss->listeners_poll[index].revents & POLLIN)) continue; + + if ((sock = accept(bm_wss->listeners_poll[index].fd, NULL, NULL)) == KS_SOCK_INVALID) { + // @todo: error handling, just skip the socket for now as most causes are because remote side became unreachable + ks_log(KS_LOG_DEBUG, "Accept failed on index %d\n", index); + continue; + } + + // @todo getsockname and getpeername (getpeername can be skipped if passing to accept instead) + + ks_log(KS_LOG_DEBUG, "Socket accepted\n", index); + + blade_transport_wss_init_create(&bt_wss_init, bm_wss, sock, NULL); + ks_assert(bt_wss_init); + + blade_connection_create(&bc, bm_wss->handle, bt_wss_init, bm_wss->transport_callbacks); + ks_assert(bc); + + blade_connection_read_lock(bc, KS_TRUE); + + if (blade_connection_startup(bc, BLADE_CONNECTION_DIRECTION_INBOUND) != KS_STATUS_SUCCESS) { + ks_log(KS_LOG_DEBUG, "Connection (%s) startup failed\n", blade_connection_id_get(bc)); + blade_connection_destroy(&bc); + blade_transport_wss_init_destroy(&bt_wss_init); + ks_socket_close(&sock); + continue; + } + ks_log(KS_LOG_DEBUG, "Connection (%s) started\n", blade_connection_id_get(bc)); + + blade_handle_connections_add(bc); + list_append(&bm_wss->connected, bc); + blade_connection_state_set(bc, BLADE_CONNECTION_STATE_NEW); + + blade_connection_read_unlock(bc); + } + } + } + ks_log(KS_LOG_DEBUG, "Stopped\n"); + + return NULL; +} + + + +ks_status_t blade_transport_wss_create(blade_transport_wss_t **bt_wssP, blade_module_wss_t *bm_wss, ks_socket_t sock) +{ + blade_transport_wss_t *bt_wss = NULL; + + ks_assert(bt_wssP); + ks_assert(bm_wss); + ks_assert(sock != KS_SOCK_INVALID); + + bt_wss = ks_pool_alloc(bm_wss->pool, sizeof(blade_transport_wss_t)); + bt_wss->module = bm_wss; + bt_wss->pool = bm_wss->pool; + bt_wss->sock = sock; + + *bt_wssP = bt_wss; + + ks_log(KS_LOG_DEBUG, "Created\n"); + + return KS_STATUS_SUCCESS; +} + +ks_status_t blade_transport_wss_destroy(blade_transport_wss_t **bt_wssP) +{ + blade_transport_wss_t *bt_wss = NULL; + + ks_assert(bt_wssP); + ks_assert(*bt_wssP); + + bt_wss = *bt_wssP; + + if (bt_wss->kws) kws_destroy(&bt_wss->kws); + else ks_socket_close(&bt_wss->sock); + + ks_pool_free(bt_wss->pool, bt_wssP); + + ks_log(KS_LOG_DEBUG, "Destroyed\n"); + + return KS_STATUS_SUCCESS; +} + +ks_status_t blade_transport_wss_on_connect(blade_connection_t **bcP, blade_module_t *bm, blade_identity_t *target, const char *session_id) +{ + ks_status_t ret = KS_STATUS_SUCCESS; + blade_module_wss_t *bm_wss = NULL; + ks_sockaddr_t addr; + ks_socket_t sock = KS_SOCK_INVALID; + int family = AF_INET; + const char *ip = NULL; + const char *portstr = NULL; + ks_port_t port = 1234; + blade_transport_wss_init_t *bt_wss_init = NULL; + blade_connection_t *bc = NULL; + + ks_assert(bcP); + ks_assert(bm); + ks_assert(target); + + bm_wss = (blade_module_wss_t *)blade_module_data_get(bm); + + *bcP = NULL; + + ks_log(KS_LOG_DEBUG, "Connect Callback: %s\n", blade_identity_uri(target)); + + // @todo completely rework all of this once more is known about connecting when an identity has no explicit transport details but this transport + // has been choosen anyway + ip = blade_identity_parameter_get(target, "host"); + portstr = blade_identity_parameter_get(target, "port"); + if (!ip) { + // @todo: temporary, this should fall back on DNS SRV or whatever else can turn "a@b.com" into an ip (and port?) to connect to + // also need to deal with hostname lookup, so identities with wss transport need to have a host parameter that is an IP for the moment + ks_log(KS_LOG_DEBUG, "No host provided\n"); + ret = KS_STATUS_FAIL; + goto done; + } + + // @todo wrap this code to get address family from string IP between IPV4 and IPV6, and put it in libks somewhere + { + ks_size_t len = strlen(ip); + + if (len <= 3) { + ks_log(KS_LOG_DEBUG, "Invalid host provided\n"); + ret = KS_STATUS_FAIL; + goto done; + } + if (ip[1] == '.' || ip[2] == '.' || (len > 3 && ip[3] == '.')) family = AF_INET; + else family = AF_INET6; + } + + if (portstr) { + int p = atoi(portstr); + if (p > 0 && p <= UINT16_MAX) port = p; + } + + ks_log(KS_LOG_DEBUG, "Connecting to %s on port %d\n", ip, port); + + ks_addr_set(&addr, ip, port, family); + if ((sock = ks_socket_connect(SOCK_STREAM, IPPROTO_TCP, &addr)) == KS_SOCK_INVALID) { + // @todo: error handling, just fail for now as most causes are because remote side became unreachable + ks_log(KS_LOG_DEBUG, "Connect failed\n"); + ret = KS_STATUS_FAIL; + goto done; + } + + ks_log(KS_LOG_DEBUG, "Socket connected\n"); + + blade_transport_wss_init_create(&bt_wss_init, bm_wss, sock, session_id); + ks_assert(bt_wss_init); + + blade_connection_create(&bc, bm_wss->handle, bt_wss_init, bm_wss->transport_callbacks); + ks_assert(bc); + + if (blade_connection_startup(bc, BLADE_CONNECTION_DIRECTION_OUTBOUND) != KS_STATUS_SUCCESS) { + ks_log(KS_LOG_DEBUG, "Connection (%s) startup failed\n", blade_connection_id_get(bc)); + blade_connection_destroy(&bc); + blade_transport_wss_init_destroy(&bt_wss_init); + ks_socket_close(&sock); + ret = KS_STATUS_FAIL; + goto done; + } + ks_log(KS_LOG_DEBUG, "Connection (%s) started\n", blade_connection_id_get(bc)); + // @todo make sure it's sensible to be mixing outbound and inbound connections in the same list, but this allows entering the destruction pipeline + // for module shutdown, disconnects and errors without special considerations + blade_handle_connections_add(bc); + list_append(&bm_wss->connected, bc); + + blade_connection_state_set(bc, BLADE_CONNECTION_STATE_NEW); + + *bcP = bc; + + done: + return ret; +} + +blade_connection_rank_t blade_transport_wss_on_rank(blade_connection_t *bc, blade_identity_t *target) +{ + ks_assert(bc); + ks_assert(target); + + return BLADE_CONNECTION_RANK_POOR; +} + +ks_status_t blade_transport_wss_write(blade_transport_wss_t *bt_wss, cJSON *json) +{ + ks_status_t ret = KS_STATUS_SUCCESS; + char *json_str = cJSON_PrintUnformatted(json); + ks_size_t json_str_len = 0; + if (!json_str) { + ks_log(KS_LOG_DEBUG, "Failed to generate json string\n"); + ret = KS_STATUS_FAIL; + goto done; + } + // @todo determine if WSOC_TEXT null terminates when read_frame is called, or if it's safe to include like this + json_str_len = strlen(json_str) + 1; + if (kws_write_frame(bt_wss->kws, WSOC_TEXT, json_str, json_str_len) != json_str_len) { + ks_log(KS_LOG_DEBUG, "Failed to write frame\n"); + ret = KS_STATUS_FAIL; + goto done; + } + ks_log(KS_LOG_DEBUG, "Frame written %d bytes\n", json_str_len); + + done: + if (json_str) free(json_str); + + return ret; +} + +ks_status_t blade_transport_wss_on_send(blade_connection_t *bc, cJSON *json) +{ + blade_transport_wss_t *bt_wss = NULL; + + ks_assert(bc); + ks_assert(json); + + bt_wss = (blade_transport_wss_t *)blade_connection_transport_get(bc); + + return blade_transport_wss_write(bt_wss, json); +} + +ks_status_t blade_transport_wss_read(blade_transport_wss_t *bt_wss, cJSON **json) +{ + // @todo get exact timeout from service config? + int32_t poll_flags = ks_wait_sock(bt_wss->sock, 100, KS_POLL_READ | KS_POLL_ERROR); + + *json = NULL; + + if (poll_flags & KS_POLL_ERROR) { + ks_log(KS_LOG_DEBUG, "POLLERR\n"); + return KS_STATUS_FAIL; + } + if (poll_flags & KS_POLL_READ) { + kws_opcode_t opcode; + uint8_t *frame_data = NULL; + ks_ssize_t frame_data_len = kws_read_frame(bt_wss->kws, &opcode, &frame_data); + + if (frame_data_len <= 0) { + // @todo error logging, strerror(ks_errno()) + // 0 means socket closed with WS_NONE, which closes websocket with no additional reason + // -1 means socket closed with a general failure + // -2 means nonblocking wait + // other values are based on WS_XXX reasons + // negative values are based on reasons, except for -1 is but -2 is nonblocking wait, and + ks_log(KS_LOG_DEBUG, "Failed to read frame\n"); + return KS_STATUS_FAIL; + } + ks_log(KS_LOG_DEBUG, "Frame read %d bytes\n", frame_data_len); + + if (!(*json = cJSON_Parse((char *)frame_data))) { + ks_log(KS_LOG_DEBUG, "Failed to parse frame\n"); + return KS_STATUS_FAIL; + } + } + return KS_STATUS_SUCCESS; +} + +ks_status_t blade_transport_wss_on_receive(blade_connection_t *bc, cJSON **json) +{ + blade_transport_wss_t *bt_wss = NULL; + + ks_assert(bc); + ks_assert(json); + + bt_wss = (blade_transport_wss_t *)blade_connection_transport_get(bc); + + return blade_transport_wss_read(bt_wss, json); +} + +ks_status_t blade_transport_wss_rpc_error_send(blade_connection_t *bc, const char *id, int32_t code, const char *message) +{ + ks_status_t ret = KS_STATUS_SUCCESS; + blade_transport_wss_t *bt_wss = NULL; + cJSON *json = NULL; + + ks_assert(bc); + ks_assert(id); + ks_assert(message); + + bt_wss = (blade_transport_wss_t *)blade_connection_transport_get(bc); + + blade_rpc_error_create(blade_connection_pool_get(bc), &json, NULL, id, code, message); + + if (blade_transport_wss_write(bt_wss, json) != KS_STATUS_SUCCESS) { + ks_log(KS_LOG_DEBUG, "Failed to write error message\n"); + ret = KS_STATUS_FAIL; + } + + cJSON_Delete(json); + return ret; +} + +blade_connection_state_hook_t blade_transport_wss_on_state_disconnect(blade_connection_t *bc, blade_connection_state_condition_t condition) +{ + blade_transport_wss_t *bt_wss = NULL; + blade_transport_wss_init_t *bt_wss_init = NULL; + + ks_assert(bc); + + ks_log(KS_LOG_DEBUG, "State Callback: %d\n", (int32_t)condition); + + if (condition == BLADE_CONNECTION_STATE_CONDITION_PRE) return BLADE_CONNECTION_STATE_HOOK_SUCCESS; + + bt_wss = (blade_transport_wss_t *)blade_connection_transport_get(bc); + bt_wss_init = (blade_transport_wss_init_t *)blade_connection_transport_init_get(bc); + + list_delete(&bt_wss->module->connected, bc); + + if (bt_wss_init) blade_transport_wss_init_destroy(&bt_wss_init); + if (bt_wss) blade_transport_wss_destroy(&bt_wss); + + return BLADE_CONNECTION_STATE_HOOK_SUCCESS; +} + +blade_connection_state_hook_t blade_transport_wss_on_state_new_inbound(blade_connection_t *bc, blade_connection_state_condition_t condition) +{ + blade_transport_wss_t *bt_wss = NULL; + blade_transport_wss_init_t *bt_wss_init = NULL; + + ks_assert(bc); + + ks_log(KS_LOG_DEBUG, "State Callback: %d\n", (int32_t)condition); + + if (condition == BLADE_CONNECTION_STATE_CONDITION_PRE) return BLADE_CONNECTION_STATE_HOOK_SUCCESS; + + bt_wss_init = (blade_transport_wss_init_t *)blade_connection_transport_init_get(bc); + + blade_transport_wss_create(&bt_wss, bt_wss_init->module, bt_wss_init->sock); + ks_assert(bt_wss); + + blade_connection_transport_set(bc, bt_wss); + + return BLADE_CONNECTION_STATE_HOOK_SUCCESS; +} + +blade_connection_state_hook_t blade_transport_wss_on_state_new_outbound(blade_connection_t *bc, blade_connection_state_condition_t condition) +{ + blade_transport_wss_t *bt_wss = NULL; + blade_transport_wss_init_t *bt_wss_init = NULL; + + ks_assert(bc); + + ks_log(KS_LOG_DEBUG, "State Callback: %d\n", (int32_t)condition); + + if (condition == BLADE_CONNECTION_STATE_CONDITION_PRE) return BLADE_CONNECTION_STATE_HOOK_SUCCESS; + + bt_wss_init = (blade_transport_wss_init_t *)blade_connection_transport_init_get(bc); + + blade_transport_wss_create(&bt_wss, bt_wss_init->module, bt_wss_init->sock); + ks_assert(bt_wss); + + blade_connection_transport_set(bc, bt_wss); + + return BLADE_CONNECTION_STATE_HOOK_SUCCESS; +} + +blade_connection_state_hook_t blade_transport_wss_on_state_connect_inbound(blade_connection_t *bc, blade_connection_state_condition_t condition) +{ + blade_transport_wss_t *bt_wss = NULL; + + ks_assert(bc); + + ks_log(KS_LOG_DEBUG, "State Callback: %d\n", (int32_t)condition); + + if (condition == BLADE_CONNECTION_STATE_CONDITION_PRE) return BLADE_CONNECTION_STATE_HOOK_SUCCESS; + + bt_wss = (blade_transport_wss_t *)blade_connection_transport_get(bc); + + // @todo: SSL init stuffs based on data from config to pass into kws_init + if (kws_init(&bt_wss->kws, bt_wss->sock, NULL, NULL, KWS_BLOCK, bt_wss->pool) != KS_STATUS_SUCCESS) { + ks_log(KS_LOG_DEBUG, "Failed websocket init\n"); + return BLADE_CONNECTION_STATE_HOOK_DISCONNECT; + } + + return BLADE_CONNECTION_STATE_HOOK_SUCCESS; +} + +blade_connection_state_hook_t blade_transport_wss_on_state_connect_outbound(blade_connection_t *bc, blade_connection_state_condition_t condition) +{ + blade_transport_wss_t *bt_wss = NULL; + + ks_assert(bc); + + ks_log(KS_LOG_DEBUG, "State Callback: %d\n", (int32_t)condition); + + if (condition == BLADE_CONNECTION_STATE_CONDITION_PRE) return BLADE_CONNECTION_STATE_HOOK_SUCCESS; + + bt_wss = (blade_transport_wss_t *)blade_connection_transport_get(bc); + + // @todo: SSL init stuffs based on data from config to pass into kws_init + if (kws_init(&bt_wss->kws, bt_wss->sock, NULL, "/blade:blade.invalid:blade", KWS_BLOCK, bt_wss->pool) != KS_STATUS_SUCCESS) { + ks_log(KS_LOG_DEBUG, "Failed websocket init\n"); + return BLADE_CONNECTION_STATE_HOOK_DISCONNECT; + } + + return BLADE_CONNECTION_STATE_HOOK_SUCCESS; +} + +blade_connection_state_hook_t blade_transport_wss_on_state_attach_inbound(blade_connection_t *bc, blade_connection_state_condition_t condition) +{ + blade_connection_state_hook_t ret = BLADE_CONNECTION_STATE_HOOK_SUCCESS; + blade_transport_wss_t *bt_wss = NULL; + ks_pool_t *pool = NULL; + cJSON *json_req = NULL; + cJSON *json_res = NULL; + cJSON *json_params = NULL; + cJSON *json_result = NULL; + //cJSON *error = NULL; + blade_session_t *bs = NULL; + blade_handle_t *bh = NULL; + const char *jsonrpc = NULL; + const char *id = NULL; + const char *method = NULL; + const char *sid = NULL; + ks_time_t timeout; + + ks_assert(bc); + + bh = blade_connection_handle_get(bc); + ks_assert(bh); + + ks_log(KS_LOG_DEBUG, "State Callback: %d\n", (int32_t)condition); + + if (condition == BLADE_CONNECTION_STATE_CONDITION_PRE) return BLADE_CONNECTION_STATE_HOOK_SUCCESS; + + bt_wss = (blade_transport_wss_t *)blade_connection_transport_get(bc); + + pool = blade_connection_pool_get(bc); + + // @todo very temporary, really need monotonic clock and get timeout delay and sleep delay from config + timeout = ks_time_now() + (5 * KS_USEC_PER_SEC); + while (blade_transport_wss_read(bt_wss, &json_req) == KS_STATUS_SUCCESS) { + if (json_req) break; + ks_sleep_ms(250); + if (ks_time_now() >= timeout) break; + } + + if (!json_req) { + ks_log(KS_LOG_DEBUG, "Failed to receive message before timeout\n"); + blade_transport_wss_rpc_error_send(bc, NULL, -32600, "Timeout while expecting request"); + ret = BLADE_CONNECTION_STATE_HOOK_DISCONNECT; + goto done; + } + + // @todo validation wrapper for request and response/error to confirm jsonrpc and provide enum for output as to which it is + jsonrpc = cJSON_GetObjectCstr(json_req, "jsonrpc"); // @todo check for definitions of these keys and fixed values + if (!jsonrpc || strcmp(jsonrpc, "2.0")) { + ks_log(KS_LOG_DEBUG, "Received message is not the expected protocol\n"); + blade_transport_wss_rpc_error_send(bc, NULL, -32600, "Invalid request, missing 'jsonrpc' field"); + ret = BLADE_CONNECTION_STATE_HOOK_DISCONNECT; + goto done; + } + + id = cJSON_GetObjectCstr(json_req, "id"); // @todo switch to number if we are not using a uuid for message id + if (!id) { + ks_log(KS_LOG_DEBUG, "Received message is missing 'id'\n"); + blade_transport_wss_rpc_error_send(bc, NULL, -32600, "Invalid request, missing 'id' field"); + ret = BLADE_CONNECTION_STATE_HOOK_DISCONNECT; + goto done; + } + + method = cJSON_GetObjectCstr(json_req, "method"); + if (!method || strcasecmp(method, "blade.session.attach")) { + ks_log(KS_LOG_DEBUG, "Received message is missing 'method' or is an unexpected method\n"); + blade_transport_wss_rpc_error_send(bc, id, -32601, "Missing or unexpected 'method' field"); + ret = BLADE_CONNECTION_STATE_HOOK_DISCONNECT; + goto done; + } + + json_params = cJSON_GetObjectItem(json_req, "params"); + if (json_params) { + sid = cJSON_GetObjectCstr(json_params, "session-id"); + if (sid) { + // @todo validate uuid format by parsing, not currently available in uuid functions, send -32602 (invalid params) if invalid + ks_log(KS_LOG_DEBUG, "Session (%s) requested\n", sid); + } + } + + if (sid) { + bs = blade_handle_sessions_get(bh, sid); // bs comes out read locked if not null to prevent it being cleaned up before we are done + if (bs) { + if (blade_session_terminating(bs)) { + blade_session_read_unlock(bs); + ks_log(KS_LOG_DEBUG, "Session (%s) terminating\n", blade_session_id_get(bs)); + bs = NULL; + } else { + ks_log(KS_LOG_DEBUG, "Session (%s) located\n", blade_session_id_get(bs)); + } + } + } + + if (!bs) { + blade_session_create(&bs, bh); + ks_assert(bs); + + ks_log(KS_LOG_DEBUG, "Session (%s) created\n", blade_session_id_get(bs)); + + blade_session_read_lock(bs, KS_TRUE); // this will be done by blade_handle_sessions_get() otherwise + + if (blade_session_startup(bs) != KS_STATUS_SUCCESS) { + ks_log(KS_LOG_DEBUG, "Session (%s) startup failed\n", blade_session_id_get(bs)); + blade_transport_wss_rpc_error_send(bc, id, -32603, "Internal error, session could not be started"); + blade_session_read_unlock(bs); + blade_session_destroy(&bs); + ret = BLADE_CONNECTION_STATE_HOOK_DISCONNECT; + goto done; + } + ks_log(KS_LOG_DEBUG, "Session (%s) started\n", blade_session_id_get(bs)); + blade_handle_sessions_add(bs); + } + + // @todo wrapper to generate request and response + blade_rpc_response_create(pool, &json_res, &json_result, id); + ks_assert(json_res); + + cJSON_AddStringToObject(json_result, "session-id", blade_session_id_get(bs)); + + // @todo send response + if (blade_transport_wss_write(bt_wss, json_res) != KS_STATUS_SUCCESS) { + ks_log(KS_LOG_DEBUG, "Failed to write response message\n"); + ret = BLADE_CONNECTION_STATE_HOOK_DISCONNECT; + goto done; + } + + blade_connection_session_set(bc, blade_session_id_get(bs)); + + done: + // @note the state machine expects if we return SUCCESS, that the session assigned to the connection will be read locked to ensure that the state + // machine can finish attaching the session, if you BYPASS then you can handle everything here in the callback, but this should be fairly standard + // behaviour to simply go as far as assigning a session to the connection and let the system handle the rest + if (json_req) cJSON_Delete(json_req); + if (json_res) cJSON_Delete(json_res); + return ret; +} + +blade_connection_state_hook_t blade_transport_wss_on_state_attach_outbound(blade_connection_t *bc, blade_connection_state_condition_t condition) +{ + blade_connection_state_hook_t ret = BLADE_CONNECTION_STATE_HOOK_SUCCESS; + blade_handle_t *bh = NULL; + blade_transport_wss_t *bt_wss = NULL; + blade_transport_wss_init_t *bt_wss_init = NULL; + ks_pool_t *pool = NULL; + cJSON *json_req = NULL; + cJSON *json_params = NULL; + cJSON *json_res = NULL; + const char *mid = NULL; + ks_time_t timeout; + const char *jsonrpc = NULL; + const char *id = NULL; + cJSON *json_error = NULL; + cJSON *json_result = NULL; + const char *sid = NULL; + blade_session_t *bs = NULL; + + ks_assert(bc); + + ks_log(KS_LOG_DEBUG, "State Callback: %d\n", (int32_t)condition); + + if (condition == BLADE_CONNECTION_STATE_CONDITION_PRE) return BLADE_CONNECTION_STATE_HOOK_SUCCESS; + + bh = blade_connection_handle_get(bc); + bt_wss = (blade_transport_wss_t *)blade_connection_transport_get(bc); + bt_wss_init = (blade_transport_wss_init_t *)blade_connection_transport_init_get(bc); + pool = blade_connection_pool_get(bc); + + + blade_rpc_request_create(pool, &json_req, &json_params, &mid, "blade.session.attach"); + ks_assert(json_req); + + if (bt_wss_init->session_id) cJSON_AddStringToObject(json_params, "session-id", bt_wss_init->session_id); + + ks_log(KS_LOG_DEBUG, "Session (%s) requested\n", (bt_wss_init->session_id ? bt_wss_init->session_id : "none")); + + if (blade_transport_wss_write(bt_wss, json_req) != KS_STATUS_SUCCESS) { + ks_log(KS_LOG_DEBUG, "Failed to write request message\n"); + ret = BLADE_CONNECTION_STATE_HOOK_DISCONNECT; + goto done; + } + + + timeout = ks_time_now() + (5 * KS_USEC_PER_SEC); + while (blade_transport_wss_read(bt_wss, &json_res) == KS_STATUS_SUCCESS) { + if (json_res) break; + ks_sleep_ms(250); + if (ks_time_now() >= timeout) break; + } + + if (!json_res) { + ks_log(KS_LOG_DEBUG, "Failed to receive message before timeout\n"); + ret = BLADE_CONNECTION_STATE_HOOK_DISCONNECT; + goto done; + } + + // @todo validation wrapper for request and response/error to confirm jsonrpc and provide enum for output as to which it is + jsonrpc = cJSON_GetObjectCstr(json_res, "jsonrpc"); // @todo check for definitions of these keys and fixed values + if (!jsonrpc || strcmp(jsonrpc, "2.0")) { + ks_log(KS_LOG_DEBUG, "Received message is not the expected protocol\n"); + ret = BLADE_CONNECTION_STATE_HOOK_DISCONNECT; + goto done; + } + + id = cJSON_GetObjectCstr(json_res, "id"); // @todo switch to number if we are not using a uuid for message id + if (!id || strcasecmp(mid, id)) { + ks_log(KS_LOG_DEBUG, "Received message has missing or unexpected 'id'\n"); + ret = BLADE_CONNECTION_STATE_HOOK_DISCONNECT; + goto done; + } + + json_error = cJSON_GetObjectItem(json_res, "error"); + if (json_error) { + ks_log(KS_LOG_DEBUG, "Error message ... add the details\n"); + ret = BLADE_CONNECTION_STATE_HOOK_DISCONNECT; + goto done; + } + + json_result = cJSON_GetObjectItem(json_res, "result"); + if (!json_result) { + ks_log(KS_LOG_DEBUG, "Received message is missing 'result'\n"); + ret = BLADE_CONNECTION_STATE_HOOK_DISCONNECT; + goto done; + } + + sid = cJSON_GetObjectCstr(json_result, "session-id"); + if (!sid) { + ks_log(KS_LOG_DEBUG, "Received message 'result' is missing 'session-id'\n"); + ret = BLADE_CONNECTION_STATE_HOOK_DISCONNECT; + goto done; + } + + if (sid) { + // @todo validate uuid format by parsing, not currently available in uuid functions + bs = blade_handle_sessions_get(bh, sid); // bs comes out read locked if not null to prevent it being cleaned up before we are done + if (bs) { + ks_log(KS_LOG_DEBUG, "Session (%s) located\n", blade_session_id_get(bs)); + } + } + + if (!bs) { + blade_session_create(&bs, bh); // @todo let sid be passed to constructor, NULL to generate + ks_assert(bs); + + blade_session_id_set(bs, sid); + + ks_log(KS_LOG_DEBUG, "Session (%s) created\n", blade_session_id_get(bs)); + + blade_session_read_lock(bs, KS_TRUE); // this will be done by blade_handle_sessions_get() otherwise + + if (blade_session_startup(bs) != KS_STATUS_SUCCESS) { + ks_log(KS_LOG_DEBUG, "Session (%s) startup failed\n", blade_session_id_get(bs)); + blade_session_read_unlock(bs); + blade_session_destroy(&bs); + ret = BLADE_CONNECTION_STATE_HOOK_DISCONNECT; + goto done; + } + ks_log(KS_LOG_DEBUG, "Session (%s) started\n", blade_session_id_get(bs)); + blade_handle_sessions_add(bs); + } + + blade_connection_session_set(bc, blade_session_id_get(bs)); + + done: + if (json_req) cJSON_Delete(json_req); + if (json_res) cJSON_Delete(json_res); + return ret; +} + +blade_connection_state_hook_t blade_transport_wss_on_state_detach_inbound(blade_connection_t *bc, blade_connection_state_condition_t condition) +{ + ks_assert(bc); + + ks_log(KS_LOG_DEBUG, "State Callback: %d\n", (int32_t)condition); + + return BLADE_CONNECTION_STATE_HOOK_SUCCESS; +} + +blade_connection_state_hook_t blade_transport_wss_on_state_detach_outbound(blade_connection_t *bc, blade_connection_state_condition_t condition) +{ + ks_assert(bc); + + ks_log(KS_LOG_DEBUG, "State Callback: %d\n", (int32_t)condition); + + return BLADE_CONNECTION_STATE_HOOK_SUCCESS; +} + +blade_connection_state_hook_t blade_transport_wss_on_state_ready_inbound(blade_connection_t *bc, blade_connection_state_condition_t condition) +{ + ks_assert(bc); + + if (condition == BLADE_CONNECTION_STATE_CONDITION_PRE) { + ks_log(KS_LOG_DEBUG, "State Callback: %d\n", (int32_t)condition); + } + + return BLADE_CONNECTION_STATE_HOOK_SUCCESS; +} + +blade_connection_state_hook_t blade_transport_wss_on_state_ready_outbound(blade_connection_t *bc, blade_connection_state_condition_t condition) +{ + ks_assert(bc); + + if (condition == BLADE_CONNECTION_STATE_CONDITION_PRE) { + blade_session_t *bs = NULL; + cJSON *req = NULL; + + ks_log(KS_LOG_DEBUG, "State Callback: %d\n", (int32_t)condition); + + bs = blade_handle_sessions_get(blade_connection_handle_get(bc), blade_connection_session_get(bc)); + ks_assert(bs); + + blade_rpc_request_create(blade_connection_pool_get(bc), &req, NULL, NULL, "blade.test.echo"); + blade_session_send(bs, req, blade_test_echo_response_handler); + + blade_session_read_unlock(bs); + } + + return BLADE_CONNECTION_STATE_HOOK_SUCCESS; +} + + + +ks_bool_t blade_test_echo_request_handler(blade_module_t *bm, blade_request_t *breq) +{ + blade_session_t *bs = NULL; + cJSON *res = NULL; + + ks_assert(bm); + ks_assert(breq); + + ks_log(KS_LOG_DEBUG, "Request Received!\n"); + + bs = blade_handle_sessions_get(breq->handle, breq->session_id); + ks_assert(bs); + + blade_rpc_response_create(breq->pool, &res, NULL, breq->message_id); + blade_session_send(bs, res, NULL); + + blade_session_read_unlock(bs); + + return KS_FALSE; +} + +ks_bool_t blade_test_echo_response_handler(blade_response_t *bres) +{ + ks_assert(bres); + + ks_log(KS_LOG_DEBUG, "Response Received!\n"); + + return KS_FALSE; +} + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: + */ diff --git a/libs/libblade/src/blade_peer.c b/libs/libblade/src/blade_peer.c deleted file mode 100644 index 8bb96ae676..0000000000 --- a/libs/libblade/src/blade_peer.c +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (c) 2007-2014, Anthony Minessale II - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * 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. - * - * * Neither the name of the original author; nor the names of any contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "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 COPYRIGHT OWNER - * OR CONTRIBUTORS 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 "blade.h" - -#define KS_DHT_TPOOL_MIN 2 -#define KS_DHT_TPOOL_MAX 8 -#define KS_DHT_TPOOL_STACK (1024 * 256) -#define KS_DHT_TPOOL_IDLE 10 - -typedef enum { - BP_NONE = 0, - BP_MYPOOL = (1 << 0), - BP_MYTPOOL = (1 << 1) -} bppvt_flag_t; - -struct blade_peer_s { - bppvt_flag_t flags; - ks_pool_t *pool; - ks_thread_pool_t *tpool; - ks_dht_t *dht; -}; - - -KS_DECLARE(ks_status_t) blade_peer_destroy(blade_peer_t **bpP) -{ - blade_peer_t *bp = NULL; - bppvt_flag_t flags; - ks_pool_t *pool; - - ks_assert(bpP); - - bp = *bpP; - *bpP = NULL; - - ks_assert(bp); - - flags = bp->flags; - pool = bp->pool; - - if (bp->dht) ks_dht_destroy(&bp->dht); - if (bp->tpool && (flags & BP_MYTPOOL)) ks_thread_pool_destroy(&bp->tpool); - - ks_pool_free(bp->pool, &bp); - - if (pool && (flags & BP_MYPOOL)) ks_pool_close(&pool); - - return KS_STATUS_SUCCESS; -} - -KS_DECLARE(ks_status_t) blade_peer_create(blade_peer_t **bpP, ks_pool_t *pool, ks_thread_pool_t *tpool, ks_dht_nodeid_t *nodeid) -{ - bppvt_flag_t newflags = BP_NONE; - blade_peer_t *bp = NULL; - ks_dht_t *dht = NULL; - - if (!pool) { - newflags |= BP_MYPOOL; - ks_pool_open(&pool); - ks_assert(pool); - } - if (!tpool) { - newflags |= BP_MYTPOOL; - ks_thread_pool_create(&tpool, BLADE_PEER_TPOOL_MIN, BLADE_PEER_TPOOL_MAX, BLADE_PEER_TPOOL_STACK, KS_PRI_NORMAL, BLADE_PEER_TPOOL_IDLE); - ks_assert(tpool); - } - ks_dht_create(&dht, pool, tpool, nodeid); - ks_assert(dht); - - bp = ks_pool_alloc(pool, sizeof(*bp)); - bp->flags = newflags; - bp->pool = pool; - bp->tpool = tpool; - bp->dht = dht; - *bpP = bp; - - return KS_STATUS_SUCCESS; -} - -KS_DECLARE(ks_dht_nodeid_t *) blade_peer_myid(blade_peer_t *bp) -{ - ks_assert(bp); - ks_assert(bp->dht); - - return &bp->dht->nodeid; -} - -KS_DECLARE(void) blade_peer_autoroute(blade_peer_t *bp, ks_bool_t autoroute, ks_port_t port) -{ - ks_assert(bp); - - ks_dht_autoroute(bp->dht, autoroute, port); -} - -KS_DECLARE(ks_status_t) blade_peer_bind(blade_peer_t *bp, const ks_sockaddr_t *addr, ks_dht_endpoint_t **endpoint) -{ - ks_assert(bp); - ks_assert(addr); - - return ks_dht_bind(bp->dht, addr, endpoint); -} - -KS_DECLARE(void) blade_peer_pulse(blade_peer_t *bp, int32_t timeout) -{ - ks_assert(bp); - ks_assert(timeout >= 0); - - ks_dht_pulse(bp->dht, timeout); -} - -/* For Emacs: - * Local Variables: - * mode:c - * indent-tabs-mode:t - * tab-width:4 - * c-basic-offset:4 - * End: - * For VIM: - * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: - */ diff --git a/libs/libblade/src/blade_protocol.c b/libs/libblade/src/blade_protocol.c new file mode 100644 index 0000000000..2752ce9fff --- /dev/null +++ b/libs/libblade/src/blade_protocol.c @@ -0,0 +1,297 @@ +/* + * Copyright (c) 2017, Shane Bryldt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * 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. + * + * * Neither the name of the original author; nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT OWNER + * OR CONTRIBUTORS 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 "blade.h" + +KS_DECLARE(ks_status_t) blade_request_create(blade_request_t **breqP, + blade_handle_t *bh, + const char *session_id, + cJSON *json, + blade_response_callback_t callback) +{ + blade_request_t *breq = NULL; + ks_pool_t *pool = NULL; + + ks_assert(breqP); + ks_assert(bh); + ks_assert(session_id); + ks_assert(json); + + pool = blade_handle_pool_get(bh); + ks_assert(pool); + + breq = ks_pool_alloc(pool, sizeof(blade_request_t)); + breq->handle = bh; + breq->pool = pool; + breq->session_id = ks_pstrdup(pool, session_id); + breq->message = cJSON_Duplicate(json, 1); + breq->message_id = cJSON_GetObjectCstr(breq->message, "id"); + breq->callback = callback; + + *breqP = breq; + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_request_destroy(blade_request_t **breqP) +{ + blade_request_t *breq = NULL; + + ks_assert(breqP); + ks_assert(*breqP); + + breq = *breqP; + + ks_pool_free(breq->pool, (void **)&breq->session_id); + cJSON_Delete(breq->message); + + ks_pool_free(breq->pool, breqP); + + return KS_STATUS_SUCCESS; +} + + +KS_DECLARE(ks_status_t) blade_response_create(blade_response_t **bresP, + blade_handle_t *bh, + const char *session_id, + blade_request_t *breq, + cJSON *json) +{ + blade_response_t *bres = NULL; + ks_pool_t *pool = NULL; + + ks_assert(bresP); + ks_assert(bh); + ks_assert(session_id); + ks_assert(breq); + ks_assert(json); + + pool = blade_handle_pool_get(bh); + ks_assert(pool); + + bres = ks_pool_alloc(pool, sizeof(blade_response_t)); + bres->handle = bh; + bres->pool = pool; + bres->session_id = ks_pstrdup(pool, session_id); + bres->request = breq; + bres->message = cJSON_Duplicate(json, 1); + + *bresP = bres; + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_response_destroy(blade_response_t **bresP) +{ + blade_response_t *bres = NULL; + + ks_assert(bresP); + ks_assert(*bresP); + + bres = *bresP; + + ks_pool_free(bres->pool, (void **)&bres->session_id); + blade_request_destroy(&bres->request); + cJSON_Delete(bres->message); + + ks_pool_free(bres->pool, bresP); + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_event_create(blade_event_t **bevP, + blade_handle_t *bh, + const char *session_id, + cJSON *json) +{ + blade_event_t *bev = NULL; + ks_pool_t *pool = NULL; + + ks_assert(bevP); + ks_assert(bh); + ks_assert(session_id); + ks_assert(json); + + pool = blade_handle_pool_get(bh); + ks_assert(pool); + + bev = ks_pool_alloc(pool, sizeof(blade_event_t)); + bev->handle = bh; + bev->pool = pool; + bev->session_id = ks_pstrdup(pool, session_id); + bev->message = cJSON_Duplicate(json, 1); + + *bevP = bev; + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_event_destroy(blade_event_t **bevP) +{ + blade_event_t *bev = NULL; + + ks_assert(bevP); + ks_assert(*bevP); + + bev = *bevP; + + ks_pool_free(bev->pool, (void **)&bev->session_id); + cJSON_Delete(bev->message); + + ks_pool_free(bev->pool, bevP); + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_rpc_request_create(ks_pool_t *pool, cJSON **json, cJSON **params, const char **id, const char *method) +{ + cJSON *root = NULL; + cJSON *p = NULL; + uuid_t msgid; + const char *mid = NULL; + + ks_assert(pool); + ks_assert(json); + ks_assert(method); + + root = cJSON_CreateObject(); + + cJSON_AddStringToObject(root, "jsonrpc", "2.0"); + + ks_uuid(&msgid); + mid = ks_uuid_str(pool, &msgid); + cJSON_AddStringToObject(root, "id", mid); + ks_pool_free(pool, &mid); + + cJSON_AddStringToObject(root, "method", method); + + p = cJSON_CreateObject(); + cJSON_AddItemToObject(root, "params", p); + + *json = root; + if (params) *params = p; + if (id) *id = cJSON_GetObjectCstr(root, "id"); + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_rpc_response_create(ks_pool_t *pool, cJSON **json, cJSON **result, const char *id) +{ + cJSON *root = NULL; + cJSON *r = NULL; + + ks_assert(pool); + ks_assert(json); + ks_assert(id); + + root = cJSON_CreateObject(); + + cJSON_AddStringToObject(root, "jsonrpc", "2.0"); + + cJSON_AddStringToObject(root, "id", id); + + r = cJSON_CreateObject(); + cJSON_AddItemToObject(root, "result", r); + + *json = root; + if (result) *result = r; + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_rpc_error_create(ks_pool_t *pool, cJSON **json, cJSON **error, const char *id, int32_t code, const char *message) +{ + cJSON *root = NULL; + cJSON *e = NULL; + + ks_assert(pool); + ks_assert(json); + ks_assert(id); + ks_assert(message); + + root = cJSON_CreateObject(); + + cJSON_AddStringToObject(root, "jsonrpc", "2.0"); + + cJSON_AddStringToObject(root, "id", id); + + e = cJSON_CreateObject(); + cJSON_AddNumberToObject(e, "code", code); + cJSON_AddStringToObject(e, "message", message); + cJSON_AddItemToObject(root, "error", e); + + *json = root; + if (error) *error = e; + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_rpc_event_create(ks_pool_t *pool, cJSON **json, cJSON **result, const char *event) +{ + cJSON *root = NULL; + cJSON *b = NULL; + cJSON *r = NULL; + + ks_assert(pool); + ks_assert(json); + ks_assert(event); + + root = cJSON_CreateObject(); + + cJSON_AddStringToObject(root, "jsonrpc", "2.0"); + + b = cJSON_CreateObject(); + cJSON_AddStringToObject(b, "event", event); + cJSON_AddItemToObject(root, "blade", b); + + if (result) { + r = cJSON_CreateObject(); + cJSON_AddItemToObject(root, "result", r); + *result = r; + } + + *json = root; + + return KS_STATUS_SUCCESS; +} + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: + */ diff --git a/libs/libblade/src/blade_session.c b/libs/libblade/src/blade_session.c new file mode 100644 index 0000000000..f284d90045 --- /dev/null +++ b/libs/libblade/src/blade_session.c @@ -0,0 +1,748 @@ +/* + * Copyright (c) 2017, Shane Bryldt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * 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. + * + * * Neither the name of the original author; nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT OWNER + * OR CONTRIBUTORS 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 "blade.h" + +struct blade_session_s { + blade_handle_t *handle; + ks_pool_t *pool; + + ks_bool_t shutdown; + ks_thread_t *state_thread; + blade_session_state_t state; + + const char *id; + ks_rwl_t *lock; + list_t connections; + ks_time_t ttl; + + ks_q_t *sending; + ks_q_t *receiving; + + cJSON *properties; + ks_rwl_t *properties_lock; +}; + +void *blade_session_state_thread(ks_thread_t *thread, void *data); +ks_status_t blade_session_state_on_destroy(blade_session_t *bs); +ks_status_t blade_session_state_on_hangup(blade_session_t *bs); +ks_status_t blade_session_state_on_ready(blade_session_t *bs); +ks_status_t blade_session_process(blade_session_t *bs, cJSON *json); + +KS_DECLARE(ks_status_t) blade_session_create(blade_session_t **bsP, blade_handle_t *bh) +{ + blade_session_t *bs = NULL; + ks_pool_t *pool = NULL; + uuid_t id; + + ks_assert(bsP); + ks_assert(bh); + + pool = blade_handle_pool_get(bh); + + bs = ks_pool_alloc(pool, sizeof(blade_session_t)); + bs->handle = bh; + bs->pool = pool; + + ks_uuid(&id); + bs->id = ks_uuid_str(pool, &id); + + ks_rwl_create(&bs->lock, pool); + ks_assert(bs->lock); + + list_init(&bs->connections); + ks_q_create(&bs->sending, pool, 0); + ks_assert(bs->sending); + ks_q_create(&bs->receiving, pool, 0); + ks_assert(bs->receiving); + + bs->properties = cJSON_CreateObject(); + ks_assert(bs->properties); + ks_rwl_create(&bs->properties_lock, pool); + ks_assert(bs->properties_lock); + + *bsP = bs; + + ks_log(KS_LOG_DEBUG, "Created\n"); + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_session_destroy(blade_session_t **bsP) +{ + blade_session_t *bs = NULL; + + ks_assert(bsP); + ks_assert(*bsP); + + bs = *bsP; + + blade_session_shutdown(bs); + + cJSON_Delete(bs->properties); + bs->properties = NULL; + ks_rwl_destroy(&bs->properties_lock); + + list_destroy(&bs->connections); + ks_q_destroy(&bs->receiving); + ks_q_destroy(&bs->sending); + + ks_rwl_destroy(&bs->lock); + + ks_pool_free(bs->pool, &bs->id); + + ks_pool_free(bs->pool, bsP); + + ks_log(KS_LOG_DEBUG, "Destroyed\n"); + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_session_startup(blade_session_t *bs) +{ + ks_assert(bs); + + blade_session_state_set(bs, BLADE_SESSION_STATE_NONE); + + if (ks_thread_create_ex(&bs->state_thread, + blade_session_state_thread, + bs, + KS_THREAD_FLAG_DEFAULT, + KS_THREAD_DEFAULT_STACK, + KS_PRI_NORMAL, + bs->pool) != KS_STATUS_SUCCESS) { + // @todo error logging + return KS_STATUS_FAIL; + } + + ks_log(KS_LOG_DEBUG, "Started\n"); + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_session_shutdown(blade_session_t *bs) +{ + cJSON *json = NULL; + + ks_assert(bs); + + if (bs->state_thread) { + bs->shutdown = KS_TRUE; + ks_thread_join(bs->state_thread); + ks_pool_free(bs->pool, &bs->state_thread); + bs->shutdown = KS_FALSE; + } + + while (ks_q_trypop(bs->sending, (void **)&json) == KS_STATUS_SUCCESS && json) cJSON_Delete(json); + while (ks_q_trypop(bs->receiving, (void **)&json) == KS_STATUS_SUCCESS && json) cJSON_Delete(json); + + list_iterator_start(&bs->connections); + while (list_iterator_hasnext(&bs->connections)) { + const char *id = (const char *)list_iterator_next(&bs->connections); + ks_pool_free(bs->pool, &id); + } + list_iterator_stop(&bs->connections); + list_clear(&bs->connections); + + ks_log(KS_LOG_DEBUG, "Stopped\n"); + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(blade_handle_t *) blade_session_handle_get(blade_session_t *bs) +{ + ks_assert(bs); + + return bs->handle; +} + +KS_DECLARE(ks_pool_t *) blade_session_pool_get(blade_session_t *bs) +{ + ks_assert(bs); + + return bs->pool; +} + +KS_DECLARE(const char *) blade_session_id_get(blade_session_t *bs) +{ + ks_assert(bs); + + return bs->id; +} + +KS_DECLARE(void) blade_session_id_set(blade_session_t *bs, const char *id) +{ + ks_assert(bs); + ks_assert(id); + + if (bs->id) ks_pool_free(bs->pool, &bs->id); + bs->id = ks_pstrdup(bs->pool, id); +} + +KS_DECLARE(blade_session_state_t) blade_session_state_get(blade_session_t *bs) +{ + ks_assert(bs); + + return bs->state; +} + +KS_DECLARE(cJSON *) blade_session_properties_get(blade_session_t *bs) +{ + ks_assert(bs); + + return bs->properties; +} + +KS_DECLARE(ks_status_t) blade_session_read_lock(blade_session_t *bs, ks_bool_t block) +{ + ks_status_t ret = KS_STATUS_SUCCESS; + + ks_assert(bs); + + if (block) ret = ks_rwl_read_lock(bs->lock); + else ret = ks_rwl_try_read_lock(bs->lock); + return ret; +} + +KS_DECLARE(ks_status_t) blade_session_read_unlock(blade_session_t *bs) +{ + ks_assert(bs); + + return ks_rwl_read_unlock(bs->lock); +} + +KS_DECLARE(ks_status_t) blade_session_write_lock(blade_session_t *bs, ks_bool_t block) +{ + ks_status_t ret = KS_STATUS_SUCCESS; + + ks_assert(bs); + + if (block) ret = ks_rwl_write_lock(bs->lock); + else ret = ks_rwl_try_write_lock(bs->lock); + return ret; +} + +KS_DECLARE(ks_status_t) blade_session_write_unlock(blade_session_t *bs) +{ + ks_assert(bs); + + return ks_rwl_write_unlock(bs->lock); +} + + +KS_DECLARE(ks_status_t) blade_session_properties_read_lock(blade_session_t *bs, ks_bool_t block) +{ + ks_status_t ret = KS_STATUS_SUCCESS; + + ks_assert(bs); + + if (block) ret = ks_rwl_read_lock(bs->properties_lock); + else ret = ks_rwl_try_read_lock(bs->properties_lock); + return ret; +} + +KS_DECLARE(ks_status_t) blade_session_properties_read_unlock(blade_session_t *bs) +{ + ks_assert(bs); + + return ks_rwl_read_unlock(bs->properties_lock); +} + +KS_DECLARE(ks_status_t) blade_session_properties_write_lock(blade_session_t *bs, ks_bool_t block) +{ + ks_status_t ret = KS_STATUS_SUCCESS; + + ks_assert(bs); + + if (block) ret = ks_rwl_write_lock(bs->properties_lock); + else ret = ks_rwl_try_write_lock(bs->properties_lock); + return ret; +} + +KS_DECLARE(ks_status_t) blade_session_properties_write_unlock(blade_session_t *bs) +{ + ks_assert(bs); + + return ks_rwl_write_unlock(bs->properties_lock); +} + + +KS_DECLARE(void) blade_session_state_set(blade_session_t *bs, blade_session_state_t state) +{ + ks_assert(bs); + + bs->state = state; + + blade_handle_session_state_callbacks_execute(bs, BLADE_SESSION_STATE_CONDITION_PRE); +} + +KS_DECLARE(void) blade_session_hangup(blade_session_t *bs) +{ + ks_assert(bs); + + if (bs->state != BLADE_SESSION_STATE_HANGUP && bs->state != BLADE_SESSION_STATE_DESTROY) { + ks_log(KS_LOG_DEBUG, "Session (%s) hanging up\n", bs->id); + blade_session_state_set(bs, BLADE_SESSION_STATE_HANGUP); + } +} + +KS_DECLARE(ks_bool_t) blade_session_terminating(blade_session_t *bs) +{ + ks_assert(bs); + + return bs->state == BLADE_SESSION_STATE_HANGUP || bs->state == BLADE_SESSION_STATE_DESTROY; +} + +KS_DECLARE(ks_status_t) blade_session_connections_add(blade_session_t *bs, const char *id) +{ + ks_status_t ret = KS_STATUS_SUCCESS; + const char *cid = NULL; + + ks_assert(bs); + + cid = ks_pstrdup(bs->pool, id); + ks_assert(cid); + + list_append(&bs->connections, cid); + + ks_log(KS_LOG_DEBUG, "Session (%s) connection added (%s)\n", bs->id, id); + + bs->ttl = 0; + + return ret; +} + +KS_DECLARE(ks_status_t) blade_session_connections_remove(blade_session_t *bs, const char *id) +{ + ks_status_t ret = KS_STATUS_SUCCESS; + uint32_t size = 0; + + ks_assert(bs); + + size = list_size(&bs->connections); + for (uint32_t i = 0; i < size; ++i) { + const char *cid = (const char *)list_get_at(&bs->connections, i); + if (!strcasecmp(cid, id)) { + ks_log(KS_LOG_DEBUG, "Session (%s) connection removed (%s)\n", bs->id, id); + list_delete_at(&bs->connections, i); + ks_pool_free(bs->pool, &cid); + break; + } + } + + if (list_size(&bs->connections) == 0) bs->ttl = ks_time_now() + (5 * KS_USEC_PER_SEC); + + return ret; +} + +ks_status_t blade_session_connections_choose(blade_session_t *bs, cJSON *json, blade_connection_t **bcP) +{ + blade_connection_t *bc = NULL; + const char *cid = NULL; + + ks_assert(bs); + ks_assert(json); + ks_assert(bcP); + + // @todo may be multiple connections, for now let's just assume there will be only one + // later there will need to be a way to pick which connection to use + cid = list_get_at(&bs->connections, 0); + if (!cid) { + // no connections available + return KS_STATUS_FAIL; + } + + bc = blade_handle_connections_get(bs->handle, cid); + if (!bc) { + // @todo error logging... this shouldn't happen + return KS_STATUS_FAIL; + } + // @todo make sure the connection is in the READY state before allowing it to be choosen, just in case it is detaching or not quite fully attached + + *bcP = bc; + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_session_sending_push(blade_session_t *bs, cJSON *json) +{ + cJSON *json_copy = NULL; + + ks_assert(bs); + ks_assert(json); + + json_copy = cJSON_Duplicate(json, 1); + return ks_q_push(bs->sending, json_copy); +} + +KS_DECLARE(ks_status_t) blade_session_sending_pop(blade_session_t *bs, cJSON **json) +{ + ks_assert(bs); + ks_assert(json); + + return ks_q_trypop(bs->sending, (void **)json); +} + +KS_DECLARE(ks_status_t) blade_session_receiving_push(blade_session_t *bs, cJSON *json) +{ + cJSON *json_copy = NULL; + + ks_assert(bs); + ks_assert(json); + + json_copy = cJSON_Duplicate(json, 1); + return ks_q_push(bs->receiving, json_copy); +} + +KS_DECLARE(ks_status_t) blade_session_receiving_pop(blade_session_t *bs, cJSON **json) +{ + ks_assert(bs); + ks_assert(json); + + return ks_q_trypop(bs->receiving, (void **)json); +} + + +void *blade_session_state_thread(ks_thread_t *thread, void *data) +{ + blade_session_t *bs = NULL; + blade_session_state_t state; + cJSON *json = NULL; + + ks_assert(thread); + ks_assert(data); + + bs = (blade_session_t *)data; + + while (!bs->shutdown) { + + state = bs->state; + + if (!list_empty(&bs->connections)) { + while (blade_session_sending_pop(bs, &json) == KS_STATUS_SUCCESS && json) { + blade_connection_t *bc = NULL; + if (blade_session_connections_choose(bs, json, &bc) == KS_STATUS_SUCCESS) { + blade_connection_sending_push(bc, json); + blade_connection_read_unlock(bc); + } + cJSON_Delete(json); + } + } + + blade_handle_session_state_callbacks_execute(bs, BLADE_SESSION_STATE_CONDITION_POST); + + switch (state) { + case BLADE_SESSION_STATE_DESTROY: + blade_session_state_on_destroy(bs); + return NULL; + case BLADE_SESSION_STATE_HANGUP: + blade_session_state_on_hangup(bs); + break; + case BLADE_SESSION_STATE_CONNECT: + ks_log(KS_LOG_DEBUG, "Session (%s) state connect\n", bs->id); + ks_sleep_ms(1000); + break; + case BLADE_SESSION_STATE_ATTACH: + ks_log(KS_LOG_DEBUG, "Session (%s) state attach\n", bs->id); + ks_sleep_ms(1000); + break; + case BLADE_SESSION_STATE_DETACH: + ks_log(KS_LOG_DEBUG, "Session (%s) state detach\n", bs->id); + ks_sleep_ms(1000); + break; + case BLADE_SESSION_STATE_READY: + blade_session_state_on_ready(bs); + break; + default: break; + } + + if (list_empty(&bs->connections) && + bs->ttl > 0 && + bs->state != BLADE_SESSION_STATE_HANGUP && + bs->state != BLADE_SESSION_STATE_DESTROY && + ks_time_now() >= bs->ttl) { + ks_log(KS_LOG_DEBUG, "Session (%s) TTL timeout\n", bs->id); + blade_session_hangup(bs); + } + } + + return NULL; +} + +ks_status_t blade_session_state_on_destroy(blade_session_t *bs) +{ + ks_assert(bs); + + ks_log(KS_LOG_DEBUG, "Session (%s) state destroy\n", bs->id); + + blade_handle_sessions_remove(bs); + blade_session_destroy(&bs); + + // @todo ignoring returns for now, see what makes sense later + return KS_STATUS_SUCCESS; +} + +ks_status_t blade_session_state_on_hangup(blade_session_t *bs) +{ + ks_assert(bs); + + ks_log(KS_LOG_DEBUG, "Session (%s) state hangup\n", bs->id); + + list_iterator_start(&bs->connections); + while (list_iterator_hasnext(&bs->connections)) { + const char *cid = (const char *)list_iterator_next(&bs->connections); + blade_connection_t *bc = blade_handle_connections_get(bs->handle, cid); + ks_assert(bc); + + blade_connection_disconnect(bc); + blade_connection_read_unlock(bc); + } + list_iterator_stop(&bs->connections); + + while (!list_empty(&bs->connections)) ks_sleep(100); + + blade_session_state_set(bs, BLADE_SESSION_STATE_DESTROY); + + return KS_STATUS_SUCCESS; +} + +ks_status_t blade_session_state_on_ready(blade_session_t *bs) +{ + cJSON *json = NULL; + + ks_assert(bs); + + //ks_log(KS_LOG_DEBUG, "Session (%s) state ready\n", bs->id); + + // @todo for now only process messages if there is a connection available + if (list_size(&bs->connections) > 0) { + // @todo may only want to pop once per call to give sending a chance to keep up + while (blade_session_receiving_pop(bs, &json) == KS_STATUS_SUCCESS && json) { + blade_session_process(bs, json); + cJSON_Delete(json); + } + } + + ks_sleep_ms(1); + return KS_STATUS_SUCCESS; +} + + +KS_DECLARE(ks_status_t) blade_session_send(blade_session_t *bs, cJSON *json, blade_response_callback_t callback) +{ + blade_request_t *request = NULL; + const char *method = NULL; + const char *id = NULL; + + ks_assert(bs); + ks_assert(json); + + method = cJSON_GetObjectCstr(json, "method"); + id = cJSON_GetObjectCstr(json, "id"); + if (!id) { + cJSON *blade = NULL; + const char *event = NULL; + + blade = cJSON_GetObjectItem(json, "blade"); + event = cJSON_GetObjectCstr(blade, "event"); + + ks_log(KS_LOG_DEBUG, "Session (%s) sending event (%s)\n", bs->id, event); + } else if (method) { + // @note This is scenario 1 + // 1) Sending a request (client: method caller or consumer) + ks_log(KS_LOG_DEBUG, "Session (%s) sending request (%s) for %s\n", bs->id, id, method); + + blade_request_create(&request, bs->handle, bs->id, json, callback); + ks_assert(request); + + // @todo set request TTL and figure out when requests are checked for expiration (separate thread in the handle?) + blade_handle_requests_add(request); + } else { + // @note This is scenario 3 + // 3) Sending a response or error (server: method callee or provider) + ks_log(KS_LOG_DEBUG, "Session (%s) sending response (%s)\n", bs->id, id); + } + + if (list_empty(&bs->connections)) { + // @todo cache the blade_request_t here if it exists to gaurentee it's cached before a response could be received + blade_session_sending_push(bs, json); + } else { + blade_connection_t *bc = NULL; + if (blade_session_connections_choose(bs, json, &bc) != KS_STATUS_SUCCESS) return KS_STATUS_FAIL; + // @todo cache the blade_request_t here if it exists to gaurentee it's cached before a response could be received + blade_connection_sending_push(bc, json); + blade_connection_read_unlock(bc); + } + + return KS_STATUS_SUCCESS; +} + +ks_status_t blade_session_process(blade_session_t *bs, cJSON *json) +{ + blade_request_t *breq = NULL; + blade_response_t *bres = NULL; + blade_event_t *bev = NULL; + const char *jsonrpc = NULL; + cJSON *blade = NULL; + const char *blade_event = NULL; + const char *id = NULL; + const char *method = NULL; + ks_bool_t disconnect = KS_FALSE; + + ks_assert(bs); + ks_assert(json); + + ks_log(KS_LOG_DEBUG, "Session (%s) processing\n", bs->id); + + + jsonrpc = cJSON_GetObjectCstr(json, "jsonrpc"); + if (!jsonrpc || strcmp(jsonrpc, "2.0")) { + ks_log(KS_LOG_DEBUG, "Received message is not the expected protocol\n"); + // @todo send error response, code = -32600 (invalid request) + // @todo hangup session entirely? + return KS_STATUS_FAIL; + } + + blade = cJSON_GetObjectItem(json, "blade"); + if (blade) { + blade_event = cJSON_GetObjectCstr(blade, "event"); + } + + if (blade_event) { + blade_event_callback_t callback = blade_handle_event_lookup(blade_session_handle_get(bs), blade_event); + if (!callback) { + ks_log(KS_LOG_DEBUG, "Received event message with no event callback '%s'\n", blade_event); + } else { + ks_log(KS_LOG_DEBUG, "Session (%s) processing event %s\n", bs->id, blade_event); + + blade_event_create(&bev, bs->handle, bs->id, json); + ks_assert(bev); + + disconnect = callback(bev); + + blade_event_destroy(&bev); + } + } else { + id = cJSON_GetObjectCstr(json, "id"); + if (!id) { + ks_log(KS_LOG_DEBUG, "Received non-event message is missing 'id'\n"); + // @todo send error response, code = -32600 (invalid request) + // @todo hangup session entirely? + return KS_STATUS_FAIL; + } + + method = cJSON_GetObjectCstr(json, "method"); + if (method) { + // @note This is scenario 2 + // 2) Receiving a request (server: method callee or provider) + blade_space_t *tmp_space = NULL; + blade_method_t *tmp_method = NULL; + blade_request_callback_t callback = NULL; + char *space_name = ks_pstrdup(bs->pool, method); + char *method_name = strrchr(space_name, '.'); + + ks_log(KS_LOG_DEBUG, "Session (%s) receiving request (%s) for %s\n", bs->id, id, method); + + if (!method_name || method_name == space_name) { + ks_log(KS_LOG_DEBUG, "Received unparsable method\n"); + ks_pool_free(bs->pool, (void **)&space_name); + // @todo send error response, code = -32601 (method not found) + return KS_STATUS_FAIL; + } + *method_name = '\0'; + method_name++; // @todo check if can be postfixed safely on previous assignment, can't recall + + ks_log(KS_LOG_DEBUG, "Looking for space %s\n", space_name); + + tmp_space = blade_handle_space_lookup(bs->handle, space_name); + if (tmp_space) { + ks_log(KS_LOG_DEBUG, "Looking for method %s\n", method_name); + tmp_method = blade_space_methods_get(tmp_space, method_name); + } + + ks_pool_free(bs->pool, (void **)&space_name); + + if (!tmp_method) { + ks_log(KS_LOG_DEBUG, "Received unknown method\n"); + // @todo send error response, code = -32601 (method not found) + return KS_STATUS_FAIL; + } + callback = blade_method_callback_get(tmp_method); + ks_assert(callback); + + blade_request_create(&breq, bs->handle, bs->id, json, NULL); + ks_assert(breq); + + disconnect = callback(blade_space_module_get(tmp_space), breq); + + blade_request_destroy(&breq); + } else { + // @note This is scenario 4 + // 4) Receiving a response or error (client: method caller or consumer) + + ks_log(KS_LOG_DEBUG, "Session (%s) receiving response (%s)\n", bs->id, id); + + breq = blade_handle_requests_get(bs->handle, id); + if (!breq) { + // @todo hangup session entirely? + return KS_STATUS_FAIL; + } + blade_handle_requests_remove(breq); + + blade_response_create(&bres, bs->handle, bs->id, breq, json); + ks_assert(bres); + + disconnect = breq->callback(bres); + + blade_response_destroy(&bres); + } + } + + if (disconnect) { + // @todo hangup session entirely? + return KS_STATUS_FAIL; + } + + return KS_STATUS_SUCCESS; +} + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: + */ diff --git a/libs/libblade/src/blade_space.c b/libs/libblade/src/blade_space.c new file mode 100644 index 0000000000..33931c2453 --- /dev/null +++ b/libs/libblade/src/blade_space.c @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2017, Shane Bryldt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * 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. + * + * * Neither the name of the original author; nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT OWNER + * OR CONTRIBUTORS 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 "blade.h" + +struct blade_space_s { + blade_handle_t *handle; + ks_pool_t *pool; + blade_module_t *module; + + const char *path; + ks_hash_t *methods; +}; + + +KS_DECLARE(ks_status_t) blade_space_create(blade_space_t **bsP, blade_handle_t *bh, blade_module_t *bm, const char *path) +{ + blade_space_t *bs = NULL; + ks_pool_t *pool = NULL; + + ks_assert(bsP); + ks_assert(bh); + ks_assert(path); + + pool = blade_handle_pool_get(bh); + + bs = ks_pool_alloc(pool, sizeof(blade_space_t)); + bs->handle = bh; + bs->pool = pool; + bs->module = bm; + bs->path = path; // @todo dup and keep copy? should mostly be literals + ks_hash_create(&bs->methods, KS_HASH_MODE_CASE_INSENSITIVE, KS_HASH_FLAG_NOLOCK | KS_HASH_FLAG_DUP_CHECK, bs->pool); + ks_assert(bs); + + *bsP = bs; + + ks_log(KS_LOG_DEBUG, "Space Created: %s\n", path); + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_space_destroy(blade_space_t **bsP) +{ + blade_space_t *bs = NULL; + ks_hash_iterator_t *it = NULL; + + ks_assert(bsP); + ks_assert(*bsP); + + bs = *bsP; + + for (it = ks_hash_first(bs->methods, KS_UNLOCKED); it; it = ks_hash_next(&it)) { + void *key = NULL; + blade_method_t *value = NULL; + + ks_hash_this(it, (const void **)&key, NULL, (void **)&value); + blade_method_destroy(&value); + } + + ks_hash_destroy(&bs->methods); + + ks_log(KS_LOG_DEBUG, "Space Destroyed: %s\n", bs->path); + + ks_pool_free(bs->pool, bsP); + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(blade_handle_t *) blade_space_handle_get(blade_space_t *bs) +{ + ks_assert(bs); + + return bs->handle; +} + +KS_DECLARE(blade_module_t *) blade_space_module_get(blade_space_t *bs) +{ + ks_assert(bs); + + return bs->module; +} + +KS_DECLARE(const char *) blade_space_path_get(blade_space_t *bs) +{ + ks_assert(bs); + + return bs->path; +} + +KS_DECLARE(ks_status_t) blade_space_methods_add(blade_space_t *bs, blade_method_t *bm) +{ + ks_status_t ret = KS_STATUS_SUCCESS; + const char *name = NULL; + blade_method_t *bm_old = NULL; + + ks_assert(bs); + ks_assert(bm); + + name = blade_method_name_get(bm); + ks_assert(name); + + ks_hash_write_lock(bs->methods); + bm_old = ks_hash_search(bs->methods, (void *)name, KS_UNLOCKED); + if (bm_old) ks_hash_remove(bs->methods, (void *)name); + ret = ks_hash_insert(bs->methods, (void *)name, (void *)bm); + ks_hash_write_unlock(bs->methods); + + if (bm_old) blade_method_destroy(&bm_old); + + return ret; +} + +KS_DECLARE(ks_status_t) blade_space_methods_remove(blade_space_t *bs, blade_method_t *bm) +{ + ks_status_t ret = KS_STATUS_SUCCESS; + const char *name = NULL; + + ks_assert(bs); + ks_assert(bm); + + name = blade_method_name_get(bm); + ks_assert(name); + + ks_hash_write_lock(bs->methods); + ks_hash_remove(bs->methods, (void *)name); + ks_hash_write_unlock(bs->methods); + + return ret; +} + +KS_DECLARE(blade_method_t *) blade_space_methods_get(blade_space_t *bs, const char *name) +{ + blade_method_t *bm = NULL; + + ks_assert(bs); + ks_assert(name); + + ks_hash_read_lock(bs->methods); + bm = ks_hash_search(bs->methods, (void *)name, KS_UNLOCKED); + ks_hash_read_unlock(bs->methods); + + return bm; +} + + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: + */ diff --git a/libs/libblade/src/blade_stack.c b/libs/libblade/src/blade_stack.c index d7cf76235c..3ffb6ed65f 100644 --- a/libs/libblade/src/blade_stack.c +++ b/libs/libblade/src/blade_stack.c @@ -1,23 +1,23 @@ /* * Copyright (c) 2007-2014, Anthony Minessale II * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: - * + * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * * 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. - * + * * * Neither the name of the original author; nor the names of any contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. - * - * + * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR @@ -43,11 +43,180 @@ struct blade_handle_s { bhpvt_flag_t flags; ks_pool_t *pool; ks_thread_pool_t *tpool; - blade_peer_t *peer; + + config_setting_t *config_directory; + config_setting_t *config_datastore; + + ks_hash_t *transports; // registered transports exposed by modules, NOT active connections + ks_hash_t *spaces; // registered method spaces exposed by modules + // registered event callback registry + // @todo should probably use a blade_handle_event_registration_t and contain optional userdata to pass from registration back into the callback, like + // a blade_module_t to get at inner module data for events that service modules may need to subscribe to between each other + ks_hash_t *events; + + //blade_identity_t *identity; blade_datastore_t *datastore; + + // @todo insert on connection creations, remove on connection destructions, key based on a UUID for the connection + ks_hash_t *connections; // active connections keyed by connection id + + // @todo insert on session creations, remove on session destructions, key based on a UUID for the session + ks_hash_t *sessions; // active sessions keyed by session id + ks_hash_t *session_state_callbacks; + + // @todo another hash with sessions keyed by the remote identity without parameters for quick lookup by target identity on sending? + ks_hash_t *requests; // outgoing requests waiting for a response keyed by the message id }; +typedef struct blade_handle_transport_registration_s blade_handle_transport_registration_t; +struct blade_handle_transport_registration_s { + ks_pool_t *pool; + + blade_module_t *module; + blade_transport_callbacks_t *callbacks; +}; + +KS_DECLARE(ks_status_t) blade_handle_transport_registration_create(blade_handle_transport_registration_t **bhtrP, + ks_pool_t *pool, + blade_module_t *module, + blade_transport_callbacks_t *callbacks) +{ + blade_handle_transport_registration_t *bhtr = NULL; + + ks_assert(bhtrP); + ks_assert(pool); + ks_assert(module); + ks_assert(callbacks); + + bhtr = ks_pool_alloc(pool, sizeof(blade_handle_transport_registration_t)); + bhtr->pool = pool; + bhtr->module = module; + bhtr->callbacks = callbacks; + + *bhtrP = bhtr; + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_handle_transport_registration_destroy(blade_handle_transport_registration_t **bhtrP) +{ + blade_handle_transport_registration_t *bhtr = NULL; + + ks_assert(bhtrP); + + bhtr = *bhtrP; + *bhtrP = NULL; + + ks_assert(bhtr); + + ks_pool_free(bhtr->pool, &bhtr); + + return KS_STATUS_SUCCESS; +} + + +typedef struct blade_handle_session_state_callback_registration_s blade_handle_session_state_callback_registration_t; +struct blade_handle_session_state_callback_registration_s { + ks_pool_t *pool; + + const char *id; + void *data; + blade_session_state_callback_t callback; +}; + +ks_status_t blade_handle_session_state_callback_registration_create(blade_handle_session_state_callback_registration_t **bhsscrP, + ks_pool_t *pool, + void *data, + blade_session_state_callback_t callback) +{ + blade_handle_session_state_callback_registration_t *bhsscr = NULL; + uuid_t uuid; + + ks_assert(bhsscrP); + ks_assert(pool); + ks_assert(callback); + + ks_uuid(&uuid); + + bhsscr = ks_pool_alloc(pool, sizeof(blade_handle_session_state_callback_registration_t)); + bhsscr->pool = pool; + bhsscr->id = ks_uuid_str(pool, &uuid); + bhsscr->data = data; + bhsscr->callback = callback; + + *bhsscrP = bhsscr; + + return KS_STATUS_SUCCESS; +} + +ks_status_t blade_handle_session_state_callback_registration_destroy(blade_handle_session_state_callback_registration_t **bhsscrP) +{ + blade_handle_session_state_callback_registration_t *bhsscr = NULL; + + ks_assert(bhsscrP); + + bhsscr = *bhsscrP; + *bhsscrP = NULL; + + ks_assert(bhsscr); + + ks_pool_free(bhsscr->pool, &bhsscr->id); + + ks_pool_free(bhsscr->pool, &bhsscr); + + return KS_STATUS_SUCCESS; +} + + + +KS_DECLARE(ks_status_t) blade_handle_create(blade_handle_t **bhP, ks_pool_t *pool, ks_thread_pool_t *tpool) +{ + bhpvt_flag_t newflags = BH_NONE; + blade_handle_t *bh = NULL; + + ks_assert(bhP); + + if (!pool) { + newflags |= BH_MYPOOL; + ks_pool_open(&pool); + } + if (!tpool) { + newflags |= BH_MYTPOOL; + ks_thread_pool_create(&tpool, BLADE_HANDLE_TPOOL_MIN, BLADE_HANDLE_TPOOL_MAX, BLADE_HANDLE_TPOOL_STACK, KS_PRI_NORMAL, BLADE_HANDLE_TPOOL_IDLE); + ks_assert(tpool); + } + + bh = ks_pool_alloc(pool, sizeof(blade_handle_t)); + bh->flags = newflags; + bh->pool = pool; + bh->tpool = tpool; + + ks_hash_create(&bh->transports, KS_HASH_MODE_CASE_INSENSITIVE, KS_HASH_FLAG_NOLOCK | KS_HASH_FLAG_DUP_CHECK, bh->pool); + ks_assert(bh->transports); + ks_hash_create(&bh->spaces, KS_HASH_MODE_CASE_INSENSITIVE, KS_HASH_FLAG_NOLOCK | KS_HASH_FLAG_DUP_CHECK, bh->pool); + ks_assert(bh->spaces); + ks_hash_create(&bh->events, KS_HASH_MODE_CASE_INSENSITIVE, KS_HASH_FLAG_NOLOCK | KS_HASH_FLAG_DUP_CHECK, bh->pool); + ks_assert(bh->events); + + ks_hash_create(&bh->connections, KS_HASH_MODE_CASE_INSENSITIVE, KS_HASH_FLAG_NOLOCK | KS_HASH_FLAG_DUP_CHECK, bh->pool); + ks_assert(bh->connections); + + ks_hash_create(&bh->sessions, KS_HASH_MODE_CASE_INSENSITIVE, KS_HASH_FLAG_NOLOCK | KS_HASH_FLAG_DUP_CHECK, bh->pool); + ks_assert(bh->sessions); + ks_hash_create(&bh->session_state_callbacks, KS_HASH_MODE_CASE_INSENSITIVE, KS_HASH_FLAG_NOLOCK | KS_HASH_FLAG_DUP_CHECK, bh->pool); + ks_assert(bh->session_state_callbacks); + + ks_hash_create(&bh->requests, KS_HASH_MODE_CASE_INSENSITIVE, KS_HASH_FLAG_NOLOCK | KS_HASH_FLAG_DUP_CHECK, bh->pool); + ks_assert(bh->requests); + + *bhP = bh; + + ks_log(KS_LOG_DEBUG, "Created\n"); + + return KS_STATUS_SUCCESS; +} + KS_DECLARE(ks_status_t) blade_handle_destroy(blade_handle_t **bhP) { blade_handle_t *bh = NULL; @@ -64,11 +233,20 @@ KS_DECLARE(ks_status_t) blade_handle_destroy(blade_handle_t **bhP) flags = bh->flags; pool = bh->pool; - if (bh->datastore) blade_datastore_destroy(&bh->datastore); + blade_handle_shutdown(bh); + + ks_hash_destroy(&bh->requests); + ks_hash_destroy(&bh->session_state_callbacks); + ks_hash_destroy(&bh->sessions); + ks_hash_destroy(&bh->connections); + ks_hash_destroy(&bh->events); + ks_hash_destroy(&bh->spaces); + ks_hash_destroy(&bh->transports); - blade_peer_destroy(&bh->peer); if (bh->tpool && (flags & BH_MYTPOOL)) ks_thread_pool_destroy(&bh->tpool); + ks_log(KS_LOG_DEBUG, "Destroyed\n"); + ks_pool_free(bh->pool, &bh); if (pool && (flags & BH_MYPOOL)) { @@ -78,99 +256,595 @@ KS_DECLARE(ks_status_t) blade_handle_destroy(blade_handle_t **bhP) return KS_STATUS_SUCCESS; } -KS_DECLARE(ks_status_t) blade_handle_create(blade_handle_t **bhP, ks_pool_t *pool, ks_thread_pool_t *tpool, const char *nodeid) +ks_status_t blade_handle_config(blade_handle_t *bh, config_setting_t *config) { - bhpvt_flag_t newflags = BH_NONE; - blade_handle_t *bh = NULL; - ks_dht_nodeid_t nid; + config_setting_t *directory = NULL; + config_setting_t *datastore = NULL; - ks_assert(nodeid); - ks_assert(strlen(nodeid) == (KS_DHT_NODEID_SIZE * 2)); + ks_assert(bh); - if (!pool) { - newflags |= BH_MYPOOL; - ks_pool_open(&pool); - } - if (!tpool) { - newflags |= BH_MYTPOOL; - ks_thread_pool_create(&tpool, BLADE_HANDLE_TPOOL_MIN, BLADE_HANDLE_TPOOL_MAX, BLADE_HANDLE_TPOOL_STACK, KS_PRI_NORMAL, BLADE_HANDLE_TPOOL_IDLE); - ks_assert(tpool); - } - - bh = ks_pool_alloc(pool, sizeof(*bh)); - bh->flags = newflags; - bh->pool = pool; - bh->tpool = tpool; - ks_dht_dehex(nid.id, nodeid, KS_DHT_NODEID_SIZE); - blade_peer_create(&bh->peer, bh->pool, bh->tpool, &nid); + if (!config) return KS_STATUS_FAIL; + if (!config_setting_is_group(config)) return KS_STATUS_FAIL; - *bhP = bh; + directory = config_setting_get_member(config, "directory"); + + datastore = config_setting_get_member(config, "datastore"); + //if (datastore && !config_setting_is_group(datastore)) return KS_STATUS_FAIL; + + + bh->config_directory = directory; + bh->config_datastore = datastore; return KS_STATUS_SUCCESS; } -KS_DECLARE(void) blade_handle_myid(blade_handle_t *bh, char *buffer) +KS_DECLARE(ks_status_t) blade_handle_startup(blade_handle_t *bh, config_setting_t *config) { - ks_dht_nodeid_t *nodeid = NULL; - ks_assert(bh); - ks_assert(bh->peer); - nodeid = blade_peer_myid(bh->peer); - ks_dht_hex(nodeid->id, buffer, KS_DHT_NODEID_SIZE); + if (blade_handle_config(bh, config) != KS_STATUS_SUCCESS) { + ks_log(KS_LOG_DEBUG, "blade_handle_config failed\n"); + return KS_STATUS_FAIL; + } + + if (bh->config_datastore && !blade_handle_datastore_available(bh)) { + blade_datastore_create(&bh->datastore, bh->pool, bh->tpool); + ks_assert(bh->datastore); + if (blade_datastore_startup(bh->datastore, bh->config_datastore) != KS_STATUS_SUCCESS) { + ks_log(KS_LOG_DEBUG, "blade_datastore_startup failed\n"); + return KS_STATUS_FAIL; + } + } + + // @todo load DSOs + + // @todo call onload and onstartup callbacks for modules from DSOs + + return KS_STATUS_SUCCESS; } -KS_DECLARE(void) blade_handle_autoroute(blade_handle_t *bh, ks_bool_t autoroute, ks_port_t port) +KS_DECLARE(ks_status_t) blade_handle_shutdown(blade_handle_t *bh) { - ks_assert(bh); - ks_assert(bh->peer); + ks_hash_iterator_t *it = NULL; - blade_peer_autoroute(bh->peer, autoroute, port); + ks_assert(bh); + + for (it = ks_hash_first(bh->requests, KS_UNLOCKED); it; it = ks_hash_next(&it)) { + void *key = NULL; + blade_request_t *value = NULL; + + ks_hash_this(it, (const void **)&key, NULL, (void **)&value); + ks_hash_remove(bh->requests, key); + + blade_request_destroy(&value); + } + + for (it = ks_hash_first(bh->sessions, KS_UNLOCKED); it; it = ks_hash_next(&it)) { + void *key = NULL; + blade_session_t *value = NULL; + + ks_hash_this(it, (const void **)&key, NULL, (void **)&value); + //ks_hash_remove(bh->sessions, key); + + blade_session_hangup(value); + } + while (ks_hash_count(bh->sessions) > 0) ks_sleep_ms(100); + + // @todo call onshutdown and onunload callbacks for modules from DSOs, which will unregister transports and spaces, and will disconnect remaining + // unattached connections + + for (it = ks_hash_first(bh->events, KS_UNLOCKED); it; it = ks_hash_next(&it)) { + void *key = NULL; + blade_event_callback_t *value = NULL; + + ks_hash_this(it, (const void **)&key, NULL, (void **)&value); + blade_handle_event_unregister(bh, (const char *)key); + } + + for (it = ks_hash_first(bh->spaces, KS_UNLOCKED); it; it = ks_hash_next(&it)) { + void *key = NULL; + blade_space_t *value = NULL; + + ks_hash_this(it, (const void **)&key, NULL, (void **)&value); + blade_handle_space_unregister(value); + } + + // @todo unload DSOs + + if (blade_handle_datastore_available(bh)) blade_datastore_destroy(&bh->datastore); + + return KS_STATUS_SUCCESS; } -KS_DECLARE(ks_status_t) blade_handle_bind(blade_handle_t *bh, const char *ip, ks_port_t port, ks_dht_endpoint_t **endpoint) +KS_DECLARE(ks_pool_t *) blade_handle_pool_get(blade_handle_t *bh) { - ks_sockaddr_t addr; - int family = AF_INET; - ks_assert(bh); - ks_assert(ip); - ks_assert(port); - - if (ip[1] != '.' && ip[2] != '.' && ip[3] != '.') family = AF_INET6; - - ks_addr_set(&addr, ip, port, family); - return blade_peer_bind(bh->peer, &addr, endpoint); + return bh->pool; } -KS_DECLARE(void) blade_handle_pulse(blade_handle_t *bh, int32_t timeout) +KS_DECLARE(ks_thread_pool_t *) blade_handle_tpool_get(blade_handle_t *bh) { ks_assert(bh); - ks_assert(timeout >= 0); + return bh->tpool; +} - blade_peer_pulse(bh->peer, timeout); - if (bh->datastore) blade_datastore_pulse(bh->datastore, timeout); +KS_DECLARE(ks_status_t) blade_handle_transport_register(blade_handle_t *bh, blade_module_t *bm, const char *name, blade_transport_callbacks_t *callbacks) +{ + blade_handle_transport_registration_t *bhtr = NULL; + blade_handle_transport_registration_t *bhtr_old = NULL; + + ks_assert(bh); + ks_assert(bm); + ks_assert(name); + ks_assert(callbacks); + + // @todo reduce blade_handle_t parameter, pull from blade_module_t parameter + + blade_handle_transport_registration_create(&bhtr, bh->pool, bm, callbacks); + ks_assert(bhtr); + + ks_hash_write_lock(bh->transports); + bhtr_old = ks_hash_search(bh->transports, (void *)name, KS_UNLOCKED); + if (bhtr_old) ks_hash_remove(bh->transports, (void *)name); + ks_hash_insert(bh->transports, (void *)name, bhtr); + ks_hash_write_unlock(bh->transports); + + if (bhtr_old) blade_handle_transport_registration_destroy(&bhtr_old); + + ks_log(KS_LOG_DEBUG, "Transport Registered: %s\n", name); + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_handle_transport_unregister(blade_handle_t *bh, const char *name) +{ + blade_handle_transport_registration_t *bhtr = NULL; + + ks_assert(bh); + ks_assert(name); + + ks_hash_write_lock(bh->transports); + bhtr = ks_hash_search(bh->transports, (void *)name, KS_UNLOCKED); + if (bhtr) ks_hash_remove(bh->transports, (void *)name); + ks_hash_write_unlock(bh->transports); + + if (bhtr) { + blade_handle_transport_registration_destroy(&bhtr); + ks_log(KS_LOG_DEBUG, "Transport Unregistered: %s\n", name); + } + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_handle_space_register(blade_space_t *bs) +{ + blade_handle_t *bh = NULL; + const char *path = NULL; + blade_space_t *bs_old = NULL; + + ks_assert(bs); + + bh = blade_space_handle_get(bs); + ks_assert(bh); + + path = blade_space_path_get(bs); + ks_assert(path); + + ks_hash_write_lock(bh->spaces); + bs_old = ks_hash_search(bh->spaces, (void *)path, KS_UNLOCKED); + if (bs_old) ks_hash_remove(bh->spaces, (void *)path); + ks_hash_insert(bh->spaces, (void *)path, bs); + ks_hash_write_unlock(bh->spaces); + + if (bs_old) blade_space_destroy(&bs_old); + + ks_log(KS_LOG_DEBUG, "Space Registered: %s\n", path); + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_handle_space_unregister(blade_space_t *bs) +{ + blade_handle_t *bh = NULL; + const char *path = NULL; + + ks_assert(bs); + + bh = blade_space_handle_get(bs); + ks_assert(bh); + + path = blade_space_path_get(bs); + ks_assert(path); + + ks_hash_write_lock(bh->spaces); + bs = ks_hash_search(bh->spaces, (void *)path, KS_UNLOCKED); + if (bs) ks_hash_remove(bh->spaces, (void *)path); + ks_hash_write_unlock(bh->spaces); + + if (bs) { + blade_space_destroy(&bs); + ks_log(KS_LOG_DEBUG, "Space Unregistered: %s\n", path); + } + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(blade_space_t *) blade_handle_space_lookup(blade_handle_t *bh, const char *path) +{ + blade_space_t *bs = NULL; + + ks_assert(bh); + ks_assert(path); + + ks_hash_read_lock(bh->spaces); + bs = ks_hash_search(bh->spaces, (void *)path, KS_UNLOCKED); + ks_hash_read_unlock(bh->spaces); + + return bs; +} + +KS_DECLARE(ks_status_t) blade_handle_event_register(blade_handle_t *bh, const char *event, blade_event_callback_t callback) +{ + ks_assert(bh); + ks_assert(event); + ks_assert(callback); + + ks_hash_write_lock(bh->events); + ks_hash_insert(bh->events, (void *)event, (void *)(intptr_t)callback); + ks_hash_write_unlock(bh->events); + + ks_log(KS_LOG_DEBUG, "Event Registered: %s\n", event); + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_handle_event_unregister(blade_handle_t *bh, const char *event) +{ + ks_bool_t removed = KS_FALSE; + + ks_assert(bh); + ks_assert(event); + + ks_hash_write_lock(bh->events); + if (ks_hash_remove(bh->events, (void *)event)) removed = KS_TRUE; + ks_hash_write_unlock(bh->events); + + if (removed) { + ks_log(KS_LOG_DEBUG, "Event Unregistered: %s\n", event); + } + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(blade_event_callback_t) blade_handle_event_lookup(blade_handle_t *bh, const char *event) +{ + blade_event_callback_t callback = NULL; + + ks_assert(bh); + ks_assert(event); + + ks_hash_read_lock(bh->events); + callback = (blade_event_callback_t)(intptr_t)ks_hash_search(bh->events, (void *)event, KS_UNLOCKED); + ks_hash_read_unlock(bh->events); + + return callback; +} + +KS_DECLARE(ks_status_t) blade_handle_connect(blade_handle_t *bh, blade_connection_t **bcP, blade_identity_t *target, const char *session_id) +{ + ks_status_t ret = KS_STATUS_SUCCESS; + blade_handle_transport_registration_t *bhtr = NULL; + const char *tname = NULL; + + ks_assert(bh); + ks_assert(target); + + // @todo this should take a callback, and push this to a queue to be processed async from another thread on the handle + // which will allow the onconnect callback to block while doing things like DNS lookups without having unknown + // impact depending on the caller thread + + ks_hash_read_lock(bh->transports); + + tname = blade_identity_parameter_get(target, "transport"); + if (tname) { + bhtr = ks_hash_search(bh->transports, (void *)tname, KS_UNLOCKED); + if (!bhtr) { + // @todo error logging, target has an explicit transport that is not available in the local transports registry + // discuss later whether this scenario should still attempt other transports when target is explicit + // @note discussions indicate that by default messages should favor relaying through a master service, unless + // an existing direct connection already exists to the target (which if the target is the master node, then there is + // no conflict of proper routing). This also applies to routing for identities which relate to groups, relaying should + // most often occur through a master service, however there may be scenarios that exist where an existing session + // exists dedicated to faster delivery for a group (IE, through an ampq cluster directly, such as master services + // syncing with each other through a pub/sub). There is also the potential that instead of a separate session, the + // current session with a master service may be able to have another connection attached which represents access through + // amqp, which in turn acts as a preferred router for only group identities + // This information does not directly apply to connecting, but should be noted for the next level up where you simply + // send a message which will not actually connect, only check for existing sessions for the target and master service + // @note relaying by master services should take a slightly different path, when they receive something not for the + // master service itself, it should relay this on to all other master services, which in turn all including original + // receiver pass on to any sessions matching an identity that is part of the group, alternatively they can use a pub/sub + // like amqp to relay between the master services more efficiently than using the websocket to send every master service + // session the message individually + } + } else { + for (ks_hash_iterator_t *it = ks_hash_first(bh->transports, KS_UNLOCKED); it; it = ks_hash_next(&it)) { + // @todo use onrank (or replace with whatever method is used for determining what transport to use) and keep highest ranked callbacks + } + } + ks_hash_read_unlock(bh->transports); + + // @todo need to be able to get to the blade_module_t from the callbacks, may require envelope around registration of callbacks to include module + // this is required because onconnect transport callback needs to be able to get back to the module data to create the connection being returned + if (bhtr) ret = bhtr->callbacks->onconnect(bcP, bhtr->module, target, session_id); + else ret = KS_STATUS_FAIL; + + return ret; } -KS_DECLARE(void) blade_handle_datastore_start(blade_handle_t *bh) +KS_DECLARE(blade_connection_t *) blade_handle_connections_get(blade_handle_t *bh, const char *cid) +{ + blade_connection_t *bc = NULL; + + ks_assert(bh); + ks_assert(cid); + + ks_hash_read_lock(bh->connections); + bc = ks_hash_search(bh->connections, (void *)cid, KS_UNLOCKED); + if (bc && blade_connection_read_lock(bc, KS_FALSE) != KS_STATUS_SUCCESS) bc = NULL; + ks_hash_read_unlock(bh->connections); + + return bc; +} + +KS_DECLARE(ks_status_t) blade_handle_connections_add(blade_connection_t *bc) +{ + ks_status_t ret = KS_STATUS_SUCCESS; + blade_handle_t *bh = NULL; + + ks_assert(bc); + + bh = blade_connection_handle_get(bc); + ks_assert(bh); + + ks_hash_write_lock(bh->connections); + ret = ks_hash_insert(bh->connections, (void *)blade_connection_id_get(bc), bc); + ks_hash_write_unlock(bh->connections); + + return ret; +} + +KS_DECLARE(ks_status_t) blade_handle_connections_remove(blade_connection_t *bc) +{ + ks_status_t ret = KS_STATUS_SUCCESS; + blade_handle_t *bh = NULL; + + ks_assert(bc); + + bh = blade_connection_handle_get(bc); + ks_assert(bh); + + blade_connection_write_lock(bc, KS_TRUE); + + ks_hash_write_lock(bh->connections); + if (ks_hash_remove(bh->connections, (void *)blade_connection_id_get(bc)) == NULL) ret = KS_STATUS_FAIL; + ks_hash_write_unlock(bh->connections); + + blade_connection_write_unlock(bc); + + // @todo call bh->connection_callbacks + + return ret; +} + +KS_DECLARE(blade_session_t *) blade_handle_sessions_get(blade_handle_t *bh, const char *sid) +{ + blade_session_t *bs = NULL; + + ks_assert(bh); + ks_assert(sid); + + ks_hash_read_lock(bh->sessions); + bs = ks_hash_search(bh->sessions, (void *)sid, KS_UNLOCKED); + if (bs && blade_session_read_lock(bs, KS_FALSE) != KS_STATUS_SUCCESS) bs = NULL; + ks_hash_read_unlock(bh->sessions); + + return bs; +} + +KS_DECLARE(ks_status_t) blade_handle_sessions_add(blade_session_t *bs) +{ + ks_status_t ret = KS_STATUS_SUCCESS; + blade_handle_t *bh = NULL; + + ks_assert(bs); + + bh = blade_session_handle_get(bs); + ks_assert(bh); + + ks_hash_write_lock(bh->sessions); + ret = ks_hash_insert(bh->sessions, (void *)blade_session_id_get(bs), bs); + ks_hash_write_unlock(bh->sessions); + + return ret; +} + +KS_DECLARE(ks_status_t) blade_handle_sessions_remove(blade_session_t *bs) +{ + ks_status_t ret = KS_STATUS_SUCCESS; + blade_handle_t *bh = NULL; + + ks_assert(bs); + + bh = blade_session_handle_get(bs); + ks_assert(bh); + + blade_session_write_lock(bs, KS_TRUE); + + ks_hash_write_lock(bh->sessions); + if (ks_hash_remove(bh->sessions, (void *)blade_session_id_get(bs)) == NULL) ret = KS_STATUS_FAIL; + ks_hash_write_unlock(bh->sessions); + + blade_session_write_unlock(bs); + + return ret; +} + +KS_DECLARE(void) blade_handle_sessions_send(blade_handle_t *bh, list_t *sessions, const char *exclude, cJSON *json) +{ + blade_session_t *bs = NULL; + + ks_assert(bh); + ks_assert(sessions); + ks_assert(json); + + list_iterator_start(sessions); + while (list_iterator_hasnext(sessions)) { + const char *sessionid = list_iterator_next(sessions); + if (exclude && !strcmp(exclude, sessionid)) continue; + bs = blade_handle_sessions_get(bh, sessionid); + if (!bs) { + ks_log(KS_LOG_DEBUG, "This should not happen\n"); + continue; + } + blade_session_send(bs, json, NULL); + blade_session_read_unlock(bs); + } + list_iterator_stop(sessions); +} + +KS_DECLARE(ks_status_t) blade_handle_session_state_callback_register(blade_handle_t *bh, void *data, blade_session_state_callback_t callback, const char **id) +{ + ks_status_t ret = KS_STATUS_SUCCESS; + blade_handle_session_state_callback_registration_t *bhsscr = NULL; + + ks_assert(bh); + ks_assert(callback); + ks_assert(id); + + blade_handle_session_state_callback_registration_create(&bhsscr, blade_handle_pool_get(bh), data, callback); + ks_assert(bhsscr); + + ks_hash_write_lock(bh->session_state_callbacks); + ret = ks_hash_insert(bh->session_state_callbacks, (void *)bhsscr->id, bhsscr); + ks_hash_write_unlock(bh->session_state_callbacks); + + *id = bhsscr->id; + + return ret; +} + +KS_DECLARE(ks_status_t) blade_handle_session_state_callback_unregister(blade_handle_t *bh, const char *id) +{ + ks_status_t ret = KS_STATUS_SUCCESS; + blade_handle_session_state_callback_registration_t *bhsscr = NULL; + + ks_assert(bh); + ks_assert(id); + + ks_hash_write_lock(bh->session_state_callbacks); + bhsscr = (blade_handle_session_state_callback_registration_t *)ks_hash_remove(bh->session_state_callbacks, (void *)id); + if (!bhsscr) ret = KS_STATUS_FAIL; + ks_hash_write_lock(bh->session_state_callbacks); + + if (bhsscr) blade_handle_session_state_callback_registration_destroy(&bhsscr); + + return ret; +} + +KS_DECLARE(void) blade_handle_session_state_callbacks_execute(blade_session_t *bs, blade_session_state_condition_t condition) +{ + blade_handle_t *bh = NULL; + ks_hash_iterator_t *it = NULL; + + ks_assert(bs); + + if (blade_session_state_get(bs) == BLADE_SESSION_STATE_NONE) return; + + bh = blade_session_handle_get(bs); + ks_assert(bh); + + ks_hash_read_lock(bh->session_state_callbacks); + for (it = ks_hash_first(bh->session_state_callbacks, KS_UNLOCKED); it; it = ks_hash_next(&it)) { + void *key = NULL; + blade_handle_session_state_callback_registration_t *value = NULL; + + ks_hash_this(it, (const void **)&key, NULL, (void **)&value); + + value->callback(bs, condition, value->data); + } + ks_hash_read_unlock(bh->session_state_callbacks); +} + + +KS_DECLARE(blade_request_t *) blade_handle_requests_get(blade_handle_t *bh, const char *mid) +{ + blade_request_t *br = NULL; + + ks_assert(bh); + ks_assert(mid); + + ks_hash_read_lock(bh->requests); + br = ks_hash_search(bh->requests, (void *)mid, KS_UNLOCKED); + ks_hash_read_unlock(bh->requests); + + return br; +} + +KS_DECLARE(ks_status_t) blade_handle_requests_add(blade_request_t *br) +{ + ks_status_t ret = KS_STATUS_SUCCESS; + blade_handle_t *bh = NULL; + + ks_assert(br); + + bh = br->handle; + ks_assert(bh); + + ks_hash_write_lock(bh->requests); + ret = ks_hash_insert(bh->requests, (void *)br->message_id, br); + ks_hash_write_unlock(bh->requests); + + return ret; +} + +KS_DECLARE(ks_status_t) blade_handle_requests_remove(blade_request_t *br) +{ + ks_status_t ret = KS_STATUS_SUCCESS; + blade_handle_t *bh = NULL; + + ks_assert(br); + + bh = br->handle; + ks_assert(bh); + + ks_hash_write_lock(bh->requests); + if (ks_hash_remove(bh->requests, (void *)br->message_id) == NULL) ret = KS_STATUS_FAIL; + ks_hash_write_unlock(bh->requests); + + return ret; +} + + + +KS_DECLARE(ks_bool_t) blade_handle_datastore_available(blade_handle_t *bh) { ks_assert(bh); - if (bh->datastore) return; - - blade_datastore_create(&bh->datastore, bh->pool); + return bh->datastore != NULL; } KS_DECLARE(ks_status_t) blade_handle_datastore_store(blade_handle_t *bh, const void *key, int32_t key_length, const void *data, int64_t data_length) { ks_assert(bh); - ks_assert(bh->datastore); ks_assert(key); ks_assert(key_length > 0); ks_assert(data); ks_assert(data_length > 0); - + + if (!blade_handle_datastore_available(bh)) return KS_STATUS_INACTIVE; + return blade_datastore_store(bh->datastore, key, key_length, data, data_length); } @@ -181,11 +855,12 @@ KS_DECLARE(ks_status_t) blade_handle_datastore_fetch(blade_handle_t *bh, void *userdata) { ks_assert(bh); - ks_assert(bh->datastore); ks_assert(callback); ks_assert(key); ks_assert(key_length > 0); - + + if (!blade_handle_datastore_available(bh)) return KS_STATUS_INACTIVE; + return blade_datastore_fetch(bh->datastore, callback, key, key_length, userdata); } diff --git a/libs/libblade/src/bpcp.c b/libs/libblade/src/bpcp.c deleted file mode 100644 index f61180ae64..0000000000 --- a/libs/libblade/src/bpcp.c +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (c) 2007-2014, Anthony Minessale II - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * 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. - * - * * Neither the name of the original author; nor the names of any contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "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 COPYRIGHT OWNER - * OR CONTRIBUTORS 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 "blade.h" - - -/* - -Find bootstrap addr. -Make a WSS connection to get validated and get group keys. -Broadcast/Announce existence. - - -HEADER - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Ver |r|R|U|U| Channel no | Packet Length | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| SEQ | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| PAYLOAD ...... | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - -r = IS Response -R = IS Retransmission -U = Unused - -PAYLOAD - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Instruction | Datatype | Length | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| PAYLOAD ..... | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - - */ - - -typedef struct bpcp_header_s { - uint32_t header; - uint64_t seq; -} bpcp_header_t; - -typedef struct bpcp_channel_nfo_s { - char *channel_name; - unsigned char key[crypto_generichash_BYTES]; - uint32_t ttl; -} bpcp_channel_nfo_t; - -typedef struct bpcp_handle_s { - ks_socket_t sock; - ks_sockaddr_t local_addr; - ks_sockaddr_t bootstrap_addr; - ks_hash_t *channel_nfo_hash; -} bpcp_handle_t; - -KS_DECLARE(ks_status_t) bpcp_create(bpcp_handle_t **handle, - const char *local_addr, ks_port_t local_port, - const char *bootstrap_addr, ks_port_t bootstrap_port) -{ - return KS_STATUS_SUCCESS; -} - -/* For Emacs: - * Local Variables: - * mode:c - * indent-tabs-mode:t - * tab-width:4 - * c-basic-offset:4 - * End: - * For VIM: - * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: - */ diff --git a/libs/libblade/src/include/blade.h b/libs/libblade/src/include/blade.h index 4d44ea07fb..c94c085511 100644 --- a/libs/libblade/src/include/blade.h +++ b/libs/libblade/src/include/blade.h @@ -1,23 +1,23 @@ /* * Copyright (c) 2007-2014, Anthony Minessale II * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: - * + * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * * 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. - * + * * * Neither the name of the original author; nor the names of any contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. - * - * + * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR @@ -36,12 +36,18 @@ #include #include #include +#include #include "unqlite.h" #include "blade_types.h" #include "blade_stack.h" -#include "blade_peer.h" +#include "blade_identity.h" +#include "blade_module.h" +#include "blade_connection.h" +#include "blade_session.h" +#include "blade_protocol.h" #include "blade_datastore.h" -#include "bpcp.h" +#include "blade_space.h" +#include "blade_method.h" KS_BEGIN_EXTERN_C diff --git a/libs/libblade/src/include/blade_connection.h b/libs/libblade/src/include/blade_connection.h new file mode 100644 index 0000000000..8b8660f9cb --- /dev/null +++ b/libs/libblade/src/include/blade_connection.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2017, Shane Bryldt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * 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. + * + * * Neither the name of the original author; nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT OWNER + * OR CONTRIBUTORS 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. + */ + +#ifndef _BLADE_CONNECTION_H_ +#define _BLADE_CONNECTION_H_ +#include + +KS_BEGIN_EXTERN_C +KS_DECLARE(ks_status_t) blade_connection_create(blade_connection_t **bcP, + blade_handle_t *bh, + void *transport_data, + blade_transport_callbacks_t *transport_callbacks); +KS_DECLARE(ks_status_t) blade_connection_destroy(blade_connection_t **bcP); +KS_DECLARE(ks_status_t) blade_connection_startup(blade_connection_t *bc, blade_connection_direction_t direction); +KS_DECLARE(ks_status_t) blade_connection_shutdown(blade_connection_t *bc); +KS_DECLARE(blade_handle_t *) blade_connection_handle_get(blade_connection_t *bc); +KS_DECLARE(ks_pool_t *) blade_connection_pool_get(blade_connection_t *bc); +KS_DECLARE(const char *) blade_connection_id_get(blade_connection_t *bc); +KS_DECLARE(ks_status_t) blade_connection_read_lock(blade_connection_t *bc, ks_bool_t block); +KS_DECLARE(ks_status_t) blade_connection_read_unlock(blade_connection_t *bc); +KS_DECLARE(ks_status_t) blade_connection_write_lock(blade_connection_t *bc, ks_bool_t block); +KS_DECLARE(ks_status_t) blade_connection_write_unlock(blade_connection_t *bc); +KS_DECLARE(void *) blade_connection_transport_init_get(blade_connection_t *bc); +KS_DECLARE(void *) blade_connection_transport_get(blade_connection_t *bc); +KS_DECLARE(void) blade_connection_transport_set(blade_connection_t *bc, void *transport_data); +KS_DECLARE(void) blade_connection_state_set(blade_connection_t *bc, blade_connection_state_t state); +KS_DECLARE(void) blade_connection_disconnect(blade_connection_t *bc); +KS_DECLARE(blade_connection_rank_t) blade_connection_rank(blade_connection_t *bc, blade_identity_t *target); +KS_DECLARE(ks_status_t) blade_connection_sending_push(blade_connection_t *bc, cJSON *json); +KS_DECLARE(ks_status_t) blade_connection_sending_pop(blade_connection_t *bc, cJSON **json); +KS_DECLARE(const char *) blade_connection_session_get(blade_connection_t *bc); +KS_DECLARE(void) blade_connection_session_set(blade_connection_t *bc, const char *id); +KS_END_EXTERN_C + +#endif + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: + */ diff --git a/libs/libblade/src/include/blade_datastore.h b/libs/libblade/src/include/blade_datastore.h index 62918a3a41..b42ce736ee 100644 --- a/libs/libblade/src/include/blade_datastore.h +++ b/libs/libblade/src/include/blade_datastore.h @@ -36,9 +36,11 @@ #include KS_BEGIN_EXTERN_C -KS_DECLARE(ks_status_t) blade_datastore_create(blade_datastore_t **bdsP, ks_pool_t *pool); +KS_DECLARE(ks_status_t) blade_datastore_create(blade_datastore_t **bdsP, ks_pool_t *pool, ks_thread_pool_t *tpool); KS_DECLARE(ks_status_t) blade_datastore_destroy(blade_datastore_t **bdsP); -KS_DECLARE(void) blade_datastore_pulse(blade_datastore_t *bds, int32_t timeout); +KS_DECLARE(ks_status_t) blade_datastore_startup(blade_datastore_t *bds, config_setting_t *config); +KS_DECLARE(ks_status_t) blade_datastore_shutdown(blade_datastore_t *bds); + KS_DECLARE(void) blade_datastore_error(blade_datastore_t *bds, const char **buffer, int32_t *buffer_length); KS_DECLARE(ks_status_t) blade_datastore_store(blade_datastore_t *bds, const void *key, int32_t key_length, const void *data, int64_t data_length); KS_DECLARE(ks_status_t) blade_datastore_fetch(blade_datastore_t *bds, diff --git a/libs/libblade/src/include/blade_peer.h b/libs/libblade/src/include/blade_identity.h similarity index 70% rename from libs/libblade/src/include/blade_peer.h rename to libs/libblade/src/include/blade_identity.h index 3c0bb5925d..8d8329459e 100644 --- a/libs/libblade/src/include/blade_peer.h +++ b/libs/libblade/src/include/blade_identity.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007-2014, Anthony Minessale II + * Copyright (c) 2017, Shane Bryldt * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,22 +31,16 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef _BPCP_H_ -#define _BPCP_H_ +#ifndef _BLADE_IDENTITY_H_ +#define _BLADE_IDENTITY_H_ #include -#define BLADE_PEER_TPOOL_MIN 2 -#define BLADE_PEER_TPOOL_MAX 8 -#define BLADE_PEER_TPOOL_STACK (1024 * 256) -#define BLADE_PEER_TPOOL_IDLE 10 - KS_BEGIN_EXTERN_C -KS_DECLARE(ks_status_t) blade_peer_create(blade_peer_t **bpP, ks_pool_t *pool, ks_thread_pool_t *tpool, ks_dht_nodeid_t *nodeid); -KS_DECLARE(ks_status_t) blade_peer_destroy(blade_peer_t **bpP); -KS_DECLARE(ks_dht_nodeid_t *) blade_peer_myid(blade_peer_t *bp); -KS_DECLARE(void) blade_peer_autoroute(blade_peer_t *bp, ks_bool_t autoroute, ks_port_t port); -KS_DECLARE(ks_status_t) blade_peer_bind(blade_peer_t *bp, const ks_sockaddr_t *addr, ks_dht_endpoint_t **endpoint); -KS_DECLARE(void) blade_peer_pulse(blade_peer_t *bp, int32_t timeout); +KS_DECLARE(ks_status_t) blade_identity_create(blade_identity_t **biP, ks_pool_t *pool); +KS_DECLARE(ks_status_t) blade_identity_destroy(blade_identity_t **biP); +KS_DECLARE(ks_status_t) blade_identity_parse(blade_identity_t *bi, const char *uri); +KS_DECLARE(const char *) blade_identity_uri(blade_identity_t *bi); +KS_DECLARE(const char *) blade_identity_parameter_get(blade_identity_t *bi, const char *key); KS_END_EXTERN_C #endif diff --git a/libs/libblade/src/include/blade_method.h b/libs/libblade/src/include/blade_method.h new file mode 100644 index 0000000000..74bf87d13f --- /dev/null +++ b/libs/libblade/src/include/blade_method.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2017, Shane Bryldt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * 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. + * + * * Neither the name of the original author; nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT OWNER + * OR CONTRIBUTORS 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. + */ + +#ifndef _BLADE_METHOD_H_ +#define _BLADE_METHOD_H_ +#include + +KS_BEGIN_EXTERN_C +KS_DECLARE(ks_status_t) blade_method_create(blade_method_t **bmP, blade_space_t *bs, const char *name, blade_request_callback_t callback); +KS_DECLARE(ks_status_t) blade_method_destroy(blade_method_t **bmP); +KS_DECLARE(const char *) blade_method_name_get(blade_method_t *bm); +KS_DECLARE(blade_request_callback_t) blade_method_callback_get(blade_method_t *bm); +KS_END_EXTERN_C + +#endif + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: + */ diff --git a/libs/libblade/src/include/blade_module.h b/libs/libblade/src/include/blade_module.h new file mode 100644 index 0000000000..e5fc558f16 --- /dev/null +++ b/libs/libblade/src/include/blade_module.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2017, Shane Bryldt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * 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. + * + * * Neither the name of the original author; nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT OWNER + * OR CONTRIBUTORS 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. + */ + +#ifndef _BLADE_MODULE_H_ +#define _BLADE_MODULE_H_ +#include + +KS_BEGIN_EXTERN_C +KS_DECLARE(ks_status_t) blade_module_create(blade_module_t **bmP, blade_handle_t *bh, void *module_data, blade_module_callbacks_t *module_callbacks); +KS_DECLARE(ks_status_t) blade_module_destroy(blade_module_t **bmP); +KS_DECLARE(blade_handle_t *) blade_module_handle_get(blade_module_t *bm); +KS_DECLARE(void *) blade_module_data_get(blade_module_t *bm); + +// @todo very temporary, this is just here to get the wss module loaded until DSO is in place +KS_DECLARE(ks_status_t) blade_module_wss_on_load(blade_module_t **bmP, blade_handle_t *bh); +KS_DECLARE(ks_status_t) blade_module_wss_on_unload(blade_module_t *bm); +KS_DECLARE(ks_status_t) blade_module_wss_on_startup(blade_module_t *bm, config_setting_t *config); +KS_DECLARE(ks_status_t) blade_module_wss_on_shutdown(blade_module_t *bm); + +KS_DECLARE(ks_status_t) blade_module_chat_on_load(blade_module_t **bmP, blade_handle_t *bh); +KS_DECLARE(ks_status_t) blade_module_chat_on_unload(blade_module_t *bm); +KS_DECLARE(ks_status_t) blade_module_chat_on_startup(blade_module_t *bm, config_setting_t *config); +KS_DECLARE(ks_status_t) blade_module_chat_on_shutdown(blade_module_t *bm); +KS_END_EXTERN_C + +#endif + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: + */ diff --git a/libs/libblade/src/include/blade_protocol.h b/libs/libblade/src/include/blade_protocol.h new file mode 100644 index 0000000000..cb4c6dff9b --- /dev/null +++ b/libs/libblade/src/include/blade_protocol.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2017, Shane Bryldt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * 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. + * + * * Neither the name of the original author; nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT OWNER + * OR CONTRIBUTORS 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. + */ + +#ifndef _BLADE_PROTOCOL_H_ +#define _BLADE_PROTOCOL_H_ +#include + +KS_BEGIN_EXTERN_C +KS_DECLARE(ks_status_t) blade_request_create(blade_request_t **breqP, + blade_handle_t *bh, + const char *session_id, + cJSON *json, + blade_response_callback_t callback); +KS_DECLARE(ks_status_t) blade_request_destroy(blade_request_t **breqP); +KS_DECLARE(ks_status_t) blade_response_create(blade_response_t **bresP, blade_handle_t *bh, const char *session_id, blade_request_t *breq, cJSON *json); +KS_DECLARE(ks_status_t) blade_response_destroy(blade_response_t **bresP); +KS_DECLARE(ks_status_t) blade_event_create(blade_event_t **bevP, blade_handle_t *bh, const char *session_id, cJSON *json); +KS_DECLARE(ks_status_t) blade_event_destroy(blade_event_t **bevP); +KS_DECLARE(ks_status_t) blade_rpc_request_create(ks_pool_t *pool, cJSON **json, cJSON **params, const char **id, const char *method); +KS_DECLARE(ks_status_t) blade_rpc_response_create(ks_pool_t *pool, cJSON **json, cJSON **result, const char *id); +KS_DECLARE(ks_status_t) blade_rpc_error_create(ks_pool_t *pool, cJSON **json, cJSON **error, const char *id, int32_t code, const char *message); +KS_DECLARE(ks_status_t) blade_rpc_event_create(ks_pool_t *pool, cJSON **json, cJSON **result, const char *event); +KS_END_EXTERN_C + +#endif + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: + */ diff --git a/libs/libblade/src/include/blade_session.h b/libs/libblade/src/include/blade_session.h new file mode 100644 index 0000000000..25c26b01ac --- /dev/null +++ b/libs/libblade/src/include/blade_session.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2017, Shane Bryldt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * 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. + * + * * Neither the name of the original author; nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT OWNER + * OR CONTRIBUTORS 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. + */ + +#ifndef _BLADE_SESSION_H_ +#define _BLADE_SESSION_H_ +#include + +KS_BEGIN_EXTERN_C +KS_DECLARE(ks_status_t) blade_session_create(blade_session_t **bsP, blade_handle_t *bh); +KS_DECLARE(ks_status_t) blade_session_destroy(blade_session_t **bsP); +KS_DECLARE(ks_status_t) blade_session_startup(blade_session_t *bs); +KS_DECLARE(ks_status_t) blade_session_shutdown(blade_session_t *bs); +KS_DECLARE(blade_handle_t *) blade_session_handle_get(blade_session_t *bs); +KS_DECLARE(ks_pool_t *) blade_session_pool_get(blade_session_t *bs); +KS_DECLARE(const char *) blade_session_id_get(blade_session_t *bs); +KS_DECLARE(void) blade_session_id_set(blade_session_t *bs, const char *id); +KS_DECLARE(blade_session_state_t) blade_session_state_get(blade_session_t *bs); +KS_DECLARE(cJSON *) blade_session_properties_get(blade_session_t *bs); +KS_DECLARE(ks_status_t) blade_session_read_lock(blade_session_t *bs, ks_bool_t block); +KS_DECLARE(ks_status_t) blade_session_read_unlock(blade_session_t *bs); +KS_DECLARE(ks_status_t) blade_session_write_lock(blade_session_t *bs, ks_bool_t block); +KS_DECLARE(ks_status_t) blade_session_write_unlock(blade_session_t *bs); +KS_DECLARE(ks_status_t) blade_session_properties_read_lock(blade_session_t *bs, ks_bool_t block); +KS_DECLARE(ks_status_t) blade_session_properties_read_unlock(blade_session_t *bs); +KS_DECLARE(ks_status_t) blade_session_properties_write_lock(blade_session_t *bs, ks_bool_t block); +KS_DECLARE(ks_status_t) blade_session_properties_write_unlock(blade_session_t *bs); +KS_DECLARE(void) blade_session_state_set(blade_session_t *bs, blade_session_state_t state); +KS_DECLARE(void) blade_session_hangup(blade_session_t *bs); +KS_DECLARE(ks_bool_t) blade_session_terminating(blade_session_t *bs); +KS_DECLARE(ks_status_t) blade_session_connections_add(blade_session_t *bs, const char *id); +KS_DECLARE(ks_status_t) blade_session_connections_remove(blade_session_t *bs, const char *id); +KS_DECLARE(ks_status_t) blade_session_send(blade_session_t *bs, cJSON *json, blade_response_callback_t callback); +KS_DECLARE(ks_status_t) blade_session_sending_push(blade_session_t *bs, cJSON *json); +KS_DECLARE(ks_status_t) blade_session_sending_pop(blade_session_t *bs, cJSON **json); +KS_DECLARE(ks_status_t) blade_session_receiving_push(blade_session_t *bs, cJSON *json); +KS_DECLARE(ks_status_t) blade_session_receiving_pop(blade_session_t *bs, cJSON **json); +KS_END_EXTERN_C + +#endif + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: + */ diff --git a/libs/libblade/src/include/blade_space.h b/libs/libblade/src/include/blade_space.h new file mode 100644 index 0000000000..bf241e45c9 --- /dev/null +++ b/libs/libblade/src/include/blade_space.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2017, Shane Bryldt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * 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. + * + * * Neither the name of the original author; nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT OWNER + * OR CONTRIBUTORS 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. + */ + +#ifndef _BLADE_SPACE_H_ +#define _BLADE_SPACE_H_ +#include + +KS_BEGIN_EXTERN_C +KS_DECLARE(ks_status_t) blade_space_create(blade_space_t **bsP, blade_handle_t *bh, blade_module_t *bm, const char *path); +KS_DECLARE(ks_status_t) blade_space_destroy(blade_space_t **bsP); +KS_DECLARE(blade_handle_t *) blade_space_handle_get(blade_space_t *bs); +KS_DECLARE(blade_module_t *) blade_space_module_get(blade_space_t *bs); +KS_DECLARE(const char *) blade_space_path_get(blade_space_t *bs); +KS_DECLARE(ks_status_t) blade_space_methods_add(blade_space_t *bs, blade_method_t *bm); +KS_DECLARE(ks_status_t) blade_space_methods_remove(blade_space_t *bs, blade_method_t *bm); +KS_DECLARE(blade_method_t *) blade_space_methods_get(blade_space_t *bs, const char *name); +KS_END_EXTERN_C + +#endif + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: + */ diff --git a/libs/libblade/src/include/blade_stack.h b/libs/libblade/src/include/blade_stack.h index 81411fabdc..1e0bc134d9 100644 --- a/libs/libblade/src/include/blade_stack.h +++ b/libs/libblade/src/include/blade_stack.h @@ -1,23 +1,23 @@ /* * Copyright (c) 2007-2014, Anthony Minessale II * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: - * + * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * * 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. - * + * * * Neither the name of the original author; nor the names of any contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. - * - * + * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR @@ -42,12 +42,42 @@ KS_BEGIN_EXTERN_C KS_DECLARE(ks_status_t) blade_handle_destroy(blade_handle_t **bhP); -KS_DECLARE(ks_status_t) blade_handle_create(blade_handle_t **bhP, ks_pool_t *pool, ks_thread_pool_t *tpool, const char *nodeid); -KS_DECLARE(void) blade_handle_myid(blade_handle_t *bh, char *buffer); -KS_DECLARE(void) blade_handle_autoroute(blade_handle_t *bh, ks_bool_t autoroute, ks_port_t port); -KS_DECLARE(ks_status_t) blade_handle_bind(blade_handle_t *bh, const char *ip, ks_port_t port, ks_dht_endpoint_t **endpoint); -KS_DECLARE(void) blade_handle_pulse(blade_handle_t *bh, int32_t timeout); -KS_DECLARE(void) blade_handle_datastore_start(blade_handle_t *bh); +KS_DECLARE(ks_status_t) blade_handle_create(blade_handle_t **bhP, ks_pool_t *pool, ks_thread_pool_t *tpool); +KS_DECLARE(ks_status_t) blade_handle_startup(blade_handle_t *bh, config_setting_t *config); +KS_DECLARE(ks_status_t) blade_handle_shutdown(blade_handle_t *bh); +KS_DECLARE(ks_pool_t *) blade_handle_pool_get(blade_handle_t *bh); +KS_DECLARE(ks_thread_pool_t *) blade_handle_tpool_get(blade_handle_t *bh); + +KS_DECLARE(ks_status_t) blade_handle_transport_register(blade_handle_t *bh, blade_module_t *bm, const char *name, blade_transport_callbacks_t *callbacks); +KS_DECLARE(ks_status_t) blade_handle_transport_unregister(blade_handle_t *bh, const char *name); + +KS_DECLARE(ks_status_t) blade_handle_space_register(blade_space_t *bs); +KS_DECLARE(ks_status_t) blade_handle_space_unregister(blade_space_t *bs); +KS_DECLARE(blade_space_t *) blade_handle_space_lookup(blade_handle_t *bh, const char *path); + +KS_DECLARE(ks_status_t) blade_handle_event_register(blade_handle_t *bh, const char *event, blade_event_callback_t callback); +KS_DECLARE(ks_status_t) blade_handle_event_unregister(blade_handle_t *bh, const char *event); +KS_DECLARE(blade_event_callback_t) blade_handle_event_lookup(blade_handle_t *bh, const char *event); + +KS_DECLARE(ks_status_t) blade_handle_connect(blade_handle_t *bh, blade_connection_t **bcP, blade_identity_t *target, const char *session_id); + +KS_DECLARE(blade_connection_t *) blade_handle_connections_get(blade_handle_t *bh, const char *cid); +KS_DECLARE(ks_status_t) blade_handle_connections_add(blade_connection_t *bc); +KS_DECLARE(ks_status_t) blade_handle_connections_remove(blade_connection_t *bc); + +KS_DECLARE(blade_session_t *) blade_handle_sessions_get(blade_handle_t *bh, const char *sid); +KS_DECLARE(ks_status_t) blade_handle_sessions_add(blade_session_t *bs); +KS_DECLARE(ks_status_t) blade_handle_sessions_remove(blade_session_t *bs); +KS_DECLARE(void) blade_handle_sessions_send(blade_handle_t *bh, list_t *sessions, const char *exclude, cJSON *json); +KS_DECLARE(ks_status_t) blade_handle_session_state_callback_register(blade_handle_t *bh, void *data, blade_session_state_callback_t callback, const char **id); +KS_DECLARE(ks_status_t) blade_handle_session_state_callback_unregister(blade_handle_t *bh, const char *id); +KS_DECLARE(void) blade_handle_session_state_callbacks_execute(blade_session_t *bs, blade_session_state_condition_t condition); + +KS_DECLARE(blade_request_t *) blade_handle_requests_get(blade_handle_t *bh, const char *mid); +KS_DECLARE(ks_status_t) blade_handle_requests_add(blade_request_t *br); +KS_DECLARE(ks_status_t) blade_handle_requests_remove(blade_request_t *br); + +KS_DECLARE(ks_bool_t) blade_handle_datastore_available(blade_handle_t *bh); KS_DECLARE(ks_status_t) blade_handle_datastore_store(blade_handle_t *bh, const void *key, int32_t key_length, const void *data, int64_t data_length); KS_DECLARE(ks_status_t) blade_handle_datastore_fetch(blade_handle_t *bh, blade_datastore_fetch_callback_t callback, diff --git a/libs/libblade/src/include/blade_types.h b/libs/libblade/src/include/blade_types.h index 0bb43a4c07..e78c6dad0f 100644 --- a/libs/libblade/src/include/blade_types.h +++ b/libs/libblade/src/include/blade_types.h @@ -1,23 +1,23 @@ /* * Copyright (c) 2007-2014, Anthony Minessale II * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: - * + * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * * 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. - * + * * * Neither the name of the original author; nor the names of any contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. - * - * + * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR @@ -34,15 +34,157 @@ #ifndef _BLADE_TYPES_H_ #define _BLADE_TYPES_H_ #include +#include KS_BEGIN_EXTERN_C typedef struct blade_handle_s blade_handle_t; -typedef struct blade_peer_s blade_peer_t; +typedef struct blade_identity_s blade_identity_t; +typedef struct blade_module_s blade_module_t; +typedef struct blade_module_callbacks_s blade_module_callbacks_t; +typedef struct blade_transport_callbacks_s blade_transport_callbacks_t; +typedef struct blade_session_callbacks_s blade_session_callbacks_t; +typedef struct blade_connection_s blade_connection_t; +typedef struct blade_session_s blade_session_t; +typedef struct blade_request_s blade_request_t; +typedef struct blade_response_s blade_response_t; +typedef struct blade_event_s blade_event_t; +typedef struct blade_space_s blade_space_t; +typedef struct blade_method_s blade_method_t; + + typedef struct blade_datastore_s blade_datastore_t; + +typedef ks_bool_t (*blade_request_callback_t)(blade_module_t *bm, blade_request_t *breq); +typedef ks_bool_t (*blade_response_callback_t)(blade_response_t *bres); +typedef ks_bool_t (*blade_event_callback_t)(blade_event_t *bev); + typedef ks_bool_t (*blade_datastore_fetch_callback_t)(blade_datastore_t *bds, const void *data, uint32_t data_length, void *userdata); + +typedef enum { + BLADE_CONNECTION_STATE_NONE, + BLADE_CONNECTION_STATE_DISCONNECT, + BLADE_CONNECTION_STATE_NEW, + BLADE_CONNECTION_STATE_CONNECT, + BLADE_CONNECTION_STATE_ATTACH, + BLADE_CONNECTION_STATE_DETACH, + BLADE_CONNECTION_STATE_READY, +} blade_connection_state_t; + +typedef enum { + BLADE_CONNECTION_DIRECTION_INBOUND, + BLADE_CONNECTION_DIRECTION_OUTBOUND, +} blade_connection_direction_t; + +typedef enum { + BLADE_CONNECTION_STATE_CONDITION_PRE, + BLADE_CONNECTION_STATE_CONDITION_POST, +} blade_connection_state_condition_t; + +typedef enum { + BLADE_CONNECTION_STATE_HOOK_SUCCESS, + BLADE_CONNECTION_STATE_HOOK_DISCONNECT, + BLADE_CONNECTION_STATE_HOOK_BYPASS, +} blade_connection_state_hook_t; + +typedef enum { + BLADE_CONNECTION_RANK_POOR, + BLADE_CONNECTION_RANK_AVERAGE, + BLADE_CONNECTION_RANK_GOOD, + BLADE_CONNECTION_RANK_GREAT, +} blade_connection_rank_t; + + +typedef enum { + BLADE_SESSION_STATE_CONDITION_PRE, + BLADE_SESSION_STATE_CONDITION_POST, +} blade_session_state_condition_t; + +typedef enum { + BLADE_SESSION_STATE_NONE, + BLADE_SESSION_STATE_DESTROY, + BLADE_SESSION_STATE_HANGUP, + BLADE_SESSION_STATE_CONNECT, + BLADE_SESSION_STATE_ATTACH, + BLADE_SESSION_STATE_DETACH, + BLADE_SESSION_STATE_READY, +} blade_session_state_t; + + + +typedef ks_status_t (*blade_module_load_callback_t)(blade_module_t **bmP, blade_handle_t *bh); +typedef ks_status_t (*blade_module_unload_callback_t)(blade_module_t *bm); +typedef ks_status_t (*blade_module_startup_callback_t)(blade_module_t *bm, config_setting_t *config); +typedef ks_status_t (*blade_module_shutdown_callback_t)(blade_module_t *bm); + +struct blade_module_callbacks_s { + blade_module_load_callback_t onload; + blade_module_unload_callback_t onunload; + blade_module_startup_callback_t onstartup; + blade_module_shutdown_callback_t onshutdown; +}; + + +typedef ks_status_t (*blade_transport_connect_callback_t)(blade_connection_t **bcP, blade_module_t *bm, blade_identity_t *target, const char *session_id); +typedef blade_connection_rank_t (*blade_transport_rank_callback_t)(blade_connection_t *bc, blade_identity_t *target); +typedef ks_status_t (*blade_transport_send_callback_t)(blade_connection_t *bc, cJSON *json); +typedef ks_status_t (*blade_transport_receive_callback_t)(blade_connection_t *bc, cJSON **json); +typedef blade_connection_state_hook_t (*blade_transport_state_callback_t)(blade_connection_t *bc, blade_connection_state_condition_t condition); + +struct blade_transport_callbacks_s { + blade_transport_connect_callback_t onconnect; + blade_transport_rank_callback_t onrank; + blade_transport_send_callback_t onsend; + blade_transport_receive_callback_t onreceive; + + blade_transport_state_callback_t onstate_disconnect_inbound; + blade_transport_state_callback_t onstate_disconnect_outbound; + blade_transport_state_callback_t onstate_new_inbound; + blade_transport_state_callback_t onstate_new_outbound; + blade_transport_state_callback_t onstate_connect_inbound; + blade_transport_state_callback_t onstate_connect_outbound; + blade_transport_state_callback_t onstate_attach_inbound; + blade_transport_state_callback_t onstate_attach_outbound; + blade_transport_state_callback_t onstate_detach_inbound; + blade_transport_state_callback_t onstate_detach_outbound; + blade_transport_state_callback_t onstate_ready_inbound; + blade_transport_state_callback_t onstate_ready_outbound; +}; + +typedef void (*blade_session_state_callback_t)(blade_session_t *bs, blade_session_state_condition_t condition, void *data); + + +struct blade_request_s { + blade_handle_t *handle; + ks_pool_t *pool; + const char *session_id; + + cJSON *message; + const char *message_id; // pulled from message for easier keying + blade_response_callback_t callback; + // @todo ttl to wait for response before injecting an error response locally + // @todo rpc response callback +}; + +struct blade_response_s { + blade_handle_t *handle; + ks_pool_t *pool; + const char *session_id; + blade_request_t *request; + + cJSON *message; +}; + +struct blade_event_s { + blade_handle_t *handle; + ks_pool_t *pool; + const char *session_id; + + cJSON *message; +}; + KS_END_EXTERN_C #endif diff --git a/libs/libblade/test/Makefile.am b/libs/libblade/test/Makefile.am index d577b95507..514af0198c 100644 --- a/libs/libblade/test/Makefile.am +++ b/libs/libblade/test/Makefile.am @@ -1,7 +1,8 @@ -AM_CFLAGS += -I$(abs_top_srcdir)/src/include -g -ggdb -O0 -TEST_LDADD = $(abs_top_builddir)/libblade.la +AM_CFLAGS += -I$(abs_top_srcdir)/src/include -g -ggdb -O0 +TEST_LDADD = $(abs_top_builddir)/libblade.la -lconfig -lm -lpthread check_PROGRAMS = + check_PROGRAMS += testbuild testbuild_SOURCES = testbuild.c tap.c testbuild_CFLAGS = $(AM_CFLAGS) @@ -12,6 +13,11 @@ bladec_SOURCES = bladec.c tap.c bladec_CFLAGS = $(AM_CFLAGS) bladec_LDADD = $(TEST_LDADD) +check_PROGRAMS += blades +blades_SOURCES = blades.c tap.c +blades_CFLAGS = $(AM_CFLAGS) +blades_LDADD = $(TEST_LDADD) + TESTS=$(check_PROGRAMS) diff --git a/libs/libblade/test/bladec.c b/libs/libblade/test/bladec.c index 478b614bc0..ebc0384a21 100644 --- a/libs/libblade/test/bladec.c +++ b/libs/libblade/test/bladec.c @@ -26,44 +26,82 @@ struct command_def_s { command_callback callback; }; -void command_test(blade_handle_t *bh, char *args); void command_quit(blade_handle_t *bh, char *args); -void command_myid(blade_handle_t *bh, char *args); -void command_bind(blade_handle_t *bh, char *args); -void command_store(blade_handle_t *bh, char *args); -void command_fetch(blade_handle_t *bh, char *args); +void command_connect(blade_handle_t *bh, char *args); +void command_chat(blade_handle_t *bh, char *args); static const struct command_def_s command_defs[] = { - { "test", command_test }, { "quit", command_quit }, - { "myid", command_myid }, - { "bind", command_bind }, - { "store", command_store }, - { "fetch", command_fetch }, - + { "connect", command_connect }, + { "chat", command_chat }, + { NULL, NULL } }; +ks_bool_t on_blade_chat_join_response(blade_response_t *bres); +ks_bool_t on_blade_chat_message_event(blade_event_t *bev); +void on_blade_session_state_callback(blade_session_t *bs, blade_session_state_condition_t condition, void *data); int main(int argc, char **argv) { blade_handle_t *bh = NULL; - const char *nodeid; - - ks_assert(argc >= 2); - - nodeid = argv[1]; + config_t config; + config_setting_t *config_blade = NULL; + blade_module_t *mod_wss = NULL; + //blade_identity_t *id = NULL; + const char *cfgpath = "bladec.cfg"; + const char *session_state_callback_id = NULL; ks_global_set_default_logger(KS_LOG_LEVEL_DEBUG); - + blade_init(); - blade_handle_create(&bh, NULL, NULL, nodeid); + blade_handle_create(&bh, NULL, NULL); - blade_handle_autoroute(bh, KS_TRUE, KS_DHT_DEFAULT_PORT); + if (argc > 1) cfgpath = argv[1]; + + config_init(&config); + if (!config_read_file(&config, cfgpath)) { + ks_log(KS_LOG_ERROR, "%s:%d - %s\n", config_error_file(&config), config_error_line(&config), config_error_text(&config)); + config_destroy(&config); + return EXIT_FAILURE; + } + config_blade = config_lookup(&config, "blade"); + if (!config_blade) { + ks_log(KS_LOG_ERROR, "Missing 'blade' config group\n"); + config_destroy(&config); + return EXIT_FAILURE; + } + if (config_setting_type(config_blade) != CONFIG_TYPE_GROUP) { + ks_log(KS_LOG_ERROR, "The 'blade' config setting is not a group\n"); + return EXIT_FAILURE; + } + + if (blade_handle_startup(bh, config_blade) != KS_STATUS_SUCCESS) { + ks_log(KS_LOG_ERROR, "Blade startup failed\n"); + return EXIT_FAILURE; + } + + if (blade_module_wss_on_load(&mod_wss, bh) != KS_STATUS_SUCCESS) { + ks_log(KS_LOG_ERROR, "Blade WSS module load failed\n"); + return EXIT_FAILURE; + } + if (blade_module_wss_on_startup(mod_wss, config_blade) != KS_STATUS_SUCCESS) { + ks_log(KS_LOG_ERROR, "Blade WSS module startup failed\n"); + return EXIT_FAILURE; + } + + blade_handle_event_register(bh, "blade.chat.message", on_blade_chat_message_event); + blade_handle_session_state_callback_register(bh, NULL, on_blade_session_state_callback, &session_state_callback_id); loop(bh); - + + blade_handle_session_state_callback_unregister(bh, session_state_callback_id); + + blade_module_wss_on_shutdown(mod_wss); + + blade_module_wss_on_unload(mod_wss); + blade_handle_destroy(&bh); blade_shutdown(); @@ -71,6 +109,38 @@ int main(int argc, char **argv) return 0; } +ks_bool_t on_blade_chat_message_event(blade_event_t *bev) +{ + cJSON *res = NULL; + const char *from = NULL; + const char *message = NULL; + + ks_assert(bev); + + res = cJSON_GetObjectItem(bev->message, "result"); + from = cJSON_GetObjectCstr(res, "from"); + message = cJSON_GetObjectCstr(res, "message"); + + ks_log(KS_LOG_DEBUG, "Received Chat Message Event: (%s) %s\n", from, message); + + return KS_FALSE; +} + +void on_blade_session_state_callback(blade_session_t *bs, blade_session_state_condition_t condition, void *data) +{ + blade_session_state_t state = blade_session_state_get(bs); + + if (condition == BLADE_SESSION_STATE_CONDITION_PRE) { + ks_log(KS_LOG_DEBUG, "Blade Session State Changed: %s, %d\n", blade_session_id_get(bs), state); + if (state == BLADE_SESSION_STATE_READY) { + cJSON *req = NULL; + blade_rpc_request_create(blade_session_pool_get(bs), &req, NULL, NULL, "blade.chat.join"); + blade_session_send(bs, req, on_blade_chat_join_response); + cJSON_Delete(req); + } + } +} + void buffer_console_input(void) { ssize_t bytes = 0; @@ -121,14 +191,13 @@ void loop(blade_handle_t *bh) // @todo lines must not exceed 512 bytes, treat as error and ignore buffer until next new line? ks_assert(0); } - blade_handle_pulse(bh, 1); } } void parse_argument(char **input, char **arg, char terminator) { char *tmp; - + ks_assert(input); ks_assert(*input); ks_assert(arg); @@ -149,11 +218,11 @@ void process_console_input(blade_handle_t *bh, char *line) char *args = line; char *cmd = NULL; ks_bool_t found = KS_FALSE; - + ks_log(KS_LOG_DEBUG, "Output: %s\n", line); parse_argument(&args, &cmd, ' '); - + ks_log(KS_LOG_DEBUG, "Command: %s, Args: %s\n", cmd, args); for (int32_t index = 0; command_defs[index].cmd; ++index) { @@ -165,78 +234,78 @@ void process_console_input(blade_handle_t *bh, char *line) if (!found) ks_log(KS_LOG_INFO, "Command '%s' unknown.\n", cmd); } -void command_test(blade_handle_t *bh, char *args) -{ - ks_log(KS_LOG_DEBUG, "Hello World!\n"); -} - void command_quit(blade_handle_t *bh, char *args) { ks_assert(bh); ks_assert(args); - + ks_log(KS_LOG_DEBUG, "Shutting down\n"); g_shutdown = KS_TRUE; } -void command_myid(blade_handle_t *bh, char *args) +void command_connect(blade_handle_t *bh, char *args) { - char buf[KS_DHT_NODEID_SIZE * 2 + 1]; + blade_connection_t *bc = NULL; + blade_identity_t *target = NULL; ks_assert(bh); ks_assert(args); - blade_handle_myid(bh, buf); + blade_identity_create(&target, blade_handle_pool_get(bh)); - ks_log(KS_LOG_INFO, "%s\n", buf); + if (blade_identity_parse(target, args) == KS_STATUS_SUCCESS) blade_handle_connect(bh, &bc, target, NULL); + + blade_identity_destroy(&target); } -void command_bind(blade_handle_t *bh, char *args) +ks_bool_t on_blade_chat_join_response(blade_response_t *bres) // @todo this should get userdata passed in from when the callback is registered { - char *ip = NULL; - char *port = NULL; - ks_port_t p; + ks_log(KS_LOG_DEBUG, "Received Chat Join Response!\n"); + return KS_FALSE; +} +ks_bool_t on_blade_chat_send_response(blade_response_t *bres) // @todo this should get userdata passed in from when the callback is registered +{ + ks_log(KS_LOG_DEBUG, "Received Chat Send Response!\n"); + return KS_FALSE; +} + +void command_chat(blade_handle_t *bh, char *args) +{ + char *cmd = NULL; + + ks_assert(bh); ks_assert(args); - parse_argument(&args, &ip, ' '); - parse_argument(&args, &port, ' '); + parse_argument(&args, &cmd, ' '); + ks_log(KS_LOG_DEBUG, "Chat Command: %s, Args: %s\n", cmd, args); - p = atoi(port); // @todo use strtol for error handling + if (!strcmp(cmd, "leave")) { + } else if (!strcmp(cmd, "send")) { + char *sid = NULL; + blade_session_t *bs = NULL; + cJSON *req = NULL; + cJSON *params = NULL; - blade_handle_bind(bh, ip, p, NULL); -} - -void command_store(blade_handle_t *bh, char *args) -{ - char *key; - char *data; - - ks_assert(args); - - blade_handle_datastore_start(bh); - - parse_argument(&args, &key, ' '); - parse_argument(&args, &data, ' '); - - blade_handle_datastore_store(bh, key, strlen(key), data, strlen(data) + 1); -} - -ks_bool_t blade_datastore_fetch_callback(blade_datastore_t *bds, const void *data, uint32_t data_length, void *userdata) -{ - ks_log(KS_LOG_INFO, "%s\n", data); - return KS_TRUE; -} - -void command_fetch(blade_handle_t *bh, char *args) -{ - char *key; - - ks_assert(args); - - blade_handle_datastore_start(bh); - - parse_argument(&args, &key, ' '); - - blade_handle_datastore_fetch(bh, blade_datastore_fetch_callback, key, strlen(key), bh); + parse_argument(&args, &sid, ' '); + + bs = blade_handle_sessions_get(bh, sid); + if (!bs) { + ks_log(KS_LOG_DEBUG, "Unknown Session: %s\n", sid); + return; + } + blade_rpc_request_create(blade_handle_pool_get(bh), &req, ¶ms, NULL, "blade.chat.send"); + ks_assert(req); + ks_assert(params); + + cJSON_AddStringToObject(params, "message", args); + + blade_session_send(bs, req, on_blade_chat_send_response); + + blade_session_read_unlock(bs); + + cJSON_Delete(req); + } else { + ks_log(KS_LOG_DEBUG, "Unknown Chat Command: %s\n", cmd); + } } diff --git a/libs/libblade/test/bladec.cfg b/libs/libblade/test/bladec.cfg new file mode 100644 index 0000000000..14582ed3ab --- /dev/null +++ b/libs/libblade/test/bladec.cfg @@ -0,0 +1,11 @@ +blade: +{ + identity = "peer@domain"; + datastore: + { + database: + { + path = ":mem:"; + }; + }; +}; diff --git a/libs/libblade/test/blades.c b/libs/libblade/test/blades.c new file mode 100644 index 0000000000..10f3f5d97c --- /dev/null +++ b/libs/libblade/test/blades.c @@ -0,0 +1,207 @@ +#include "blade.h" +#include "tap.h" + +#ifdef _WIN32 +#define STDIO_FD(_fs) _fileno(_fs) +#define READ(_fd, _buffer, _count) _read(_fd, _buffer, _count) +#else +#define STDIO_FD(_fs) fileno(_fs) +#define READ(_fd, _buffer, _count) read(_fd, _buffer, _count) +#endif + +#define CONSOLE_INPUT_MAX 512 + +ks_bool_t g_shutdown = KS_FALSE; +char g_console_input[CONSOLE_INPUT_MAX]; +size_t g_console_input_length = 0; +size_t g_console_input_eol = 0; + +void loop(blade_handle_t *bh); +void process_console_input(blade_handle_t *bh, char *line); + +typedef void (*command_callback)(blade_handle_t *bh, char *args); + +struct command_def_s { + const char *cmd; + command_callback callback; +}; + +void command_quit(blade_handle_t *bh, char *args); + +static const struct command_def_s command_defs[] = { + { "quit", command_quit }, + + { NULL, NULL } +}; + +int main(int argc, char **argv) +{ + blade_handle_t *bh = NULL; + config_t config; + config_setting_t *config_blade = NULL; + blade_module_t *mod_wss = NULL; + blade_module_t *mod_chat = NULL; + //blade_identity_t *id = NULL; + const char *cfgpath = "blades.cfg"; + + ks_global_set_default_logger(KS_LOG_LEVEL_DEBUG); + + blade_init(); + + blade_handle_create(&bh, NULL, NULL); + + if (argc > 1) cfgpath = argv[1]; + + config_init(&config); + if (!config_read_file(&config, cfgpath)) { + ks_log(KS_LOG_ERROR, "%s:%d - %s\n", config_error_file(&config), config_error_line(&config), config_error_text(&config)); + config_destroy(&config); + return EXIT_FAILURE; + } + config_blade = config_lookup(&config, "blade"); + if (!config_blade) { + ks_log(KS_LOG_ERROR, "Missing 'blade' config group\n"); + config_destroy(&config); + return EXIT_FAILURE; + } + if (config_setting_type(config_blade) != CONFIG_TYPE_GROUP) { + ks_log(KS_LOG_ERROR, "The 'blade' config setting is not a group\n"); + return EXIT_FAILURE; + } + + if (blade_handle_startup(bh, config_blade) != KS_STATUS_SUCCESS) { + ks_log(KS_LOG_ERROR, "Blade startup failed\n"); + return EXIT_FAILURE; + } + + if (blade_module_wss_on_load(&mod_wss, bh) != KS_STATUS_SUCCESS) { + ks_log(KS_LOG_ERROR, "Blade WSS module load failed\n"); + return EXIT_FAILURE; + } + if (blade_module_wss_on_startup(mod_wss, config_blade) != KS_STATUS_SUCCESS) { + ks_log(KS_LOG_ERROR, "Blade WSS module startup failed\n"); + return EXIT_FAILURE; + } + + blade_module_chat_on_load(&mod_chat, bh); + blade_module_chat_on_startup(mod_chat, config_blade); + + loop(bh); + + blade_module_chat_on_shutdown(mod_chat); + blade_module_chat_on_unload(mod_chat); + + blade_module_wss_on_shutdown(mod_wss); + + blade_module_wss_on_unload(mod_wss); + + blade_handle_destroy(&bh); + + blade_shutdown(); + + return 0; +} + + + +void buffer_console_input(void) +{ + ssize_t bytes = 0; + struct pollfd poll[1]; + poll[0].fd = STDIO_FD(stdin); + poll[0].events = POLLIN | POLLERR; + + if (ks_poll(poll, 1, 1) > 0) { + if (poll[0].revents & POLLIN) { + if ((bytes = READ(poll[0].fd, g_console_input + g_console_input_length, CONSOLE_INPUT_MAX - g_console_input_length)) <= 0) { + // @todo error + return; + } + g_console_input_length += bytes; + } + } +} + +void loop(blade_handle_t *bh) +{ + while (!g_shutdown) { + ks_bool_t eol = KS_FALSE; + buffer_console_input(); + + for (; g_console_input_eol < g_console_input_length; ++g_console_input_eol) { + char c = g_console_input[g_console_input_eol]; + if (c == '\r' || c == '\n') { + eol = KS_TRUE; + break; + } + } + if (eol) { + g_console_input[g_console_input_eol] = '\0'; + process_console_input(bh, g_console_input); + g_console_input_eol++; + for (; g_console_input_eol < g_console_input_length; ++g_console_input_eol) { + char c = g_console_input[g_console_input_eol]; + if (c != '\r' && c != '\n') break; + } + if (g_console_input_eol == g_console_input_length) g_console_input_eol = g_console_input_length = 0; + else { + memcpy(g_console_input, g_console_input + g_console_input_eol, g_console_input_length - g_console_input_eol); + g_console_input_length -= g_console_input_eol; + g_console_input_eol = 0; + } + } + if (g_console_input_length == CONSOLE_INPUT_MAX) { + // @todo lines must not exceed 512 bytes, treat as error and ignore buffer until next new line? + ks_assert(0); + } + } +} + +void parse_argument(char **input, char **arg, char terminator) +{ + char *tmp; + + ks_assert(input); + ks_assert(*input); + ks_assert(arg); + + tmp = *input; + *arg = tmp; + + while (*tmp && *tmp != terminator) ++tmp; + if (*tmp == terminator) { + *tmp = '\0'; + ++tmp; + } + *input = tmp; +} + +void process_console_input(blade_handle_t *bh, char *line) +{ + char *args = line; + char *cmd = NULL; + ks_bool_t found = KS_FALSE; + + ks_log(KS_LOG_DEBUG, "Output: %s\n", line); + + parse_argument(&args, &cmd, ' '); + + ks_log(KS_LOG_DEBUG, "Command: %s, Args: %s\n", cmd, args); + + for (int32_t index = 0; command_defs[index].cmd; ++index) { + if (!strcmp(command_defs[index].cmd, cmd)) { + found = KS_TRUE; + command_defs[index].callback(bh, args); + } + } + if (!found) ks_log(KS_LOG_INFO, "Command '%s' unknown.\n", cmd); +} + +void command_quit(blade_handle_t *bh, char *args) +{ + ks_assert(bh); + ks_assert(args); + + ks_log(KS_LOG_DEBUG, "Shutting down\n"); + g_shutdown = KS_TRUE; +} diff --git a/libs/libblade/test/blades.cfg b/libs/libblade/test/blades.cfg new file mode 100644 index 0000000000..3420d74ff2 --- /dev/null +++ b/libs/libblade/test/blades.cfg @@ -0,0 +1,28 @@ +blade: +{ + identity = "service@domain"; + directory: + { + }; + datastore: + { + database: + { + path = ":mem:"; + }; + }; + wss: + { + endpoints: + { + ipv4 = ( { address = "0.0.0.0", port = 2100 } ); + ipv6 = ( { address = "::", port = 2100 } ); + backlog = 128; + }; + # SSL group is optional, disabled when absent + ssl: + { + # todo: server SSL stuffs here + }; + }; +}; diff --git a/libs/libks/Makefile.am b/libs/libks/Makefile.am index 48a66c5ff4..5a4bb8c850 100644 --- a/libs/libks/Makefile.am +++ b/libs/libks/Makefile.am @@ -10,13 +10,13 @@ lib_LTLIBRARIES = libks.la libks_la_SOURCES = src/ks.c src/ks_string.c src/ks_json.c src/cJSON.c src/cJSON_Utils.c src/ks_thread.c src/ks_thread_pool.c src/ks_mutex.c src/ks_config.c libks_la_SOURCES += src/ks_log.c src/ks_socket.c src/ks_buffer.c src/ks_pool.c src/simclist.c libks_la_SOURCES += src/ks_time.c src/ks_printf.c src/ks_hash.c src/ks_q.c src/ks_dso.c # src/ks_dht.c -libks_la_SOURCES += src/ks_ssl.c src/kws.c src/ks_rng.c +libks_la_SOURCES += src/ks_ssl.c src/kws.c src/ks_rng.c src/ks_base64.c libks_la_SOURCES += src/utp/utp_api.cpp src/utp/utp_callbacks.cpp src/utp/utp_hash.cpp src/utp/utp_internal.cpp libks_la_SOURCES += src/utp/utp_packedsockaddr.cpp src/utp/utp_utils.cpp src/ks_bencode.c libks_la_SOURCES += src/dht/ks_dht.c src/dht/ks_dht_datagram.c src/dht/ks_dht_endpoint.c src/dht/ks_dht_message.c src/dht/ks_dht_transaction.c libks_la_SOURCES += src/dht/ks_dht_job.c src/dht/ks_dht_search.c src/dht/ks_dht_publish.c src/dht/ks_dht_distribute.c src/dht/ks_dht_storageitem.c libks_la_SOURCES += src/dht/ks_dht_bucket.c -libks_la_SOURCES += crypt/aeskey.c crypt/aestab.c crypt/sha2.c crypt/twofish.c crypt/aes_modes.c crypt/aescrypt.c crypt/twofish_cfb.c +libks_la_SOURCES += crypt/aeskey.c crypt/aestab.c crypt/sha2.c crypt/twofish.c crypt/aes_modes.c crypt/aescrypt.c crypt/twofish_cfb.c #aes.h aescpp.h brg_endian.h aesopt.h aestab.h brg_types.h sha2.h twofish.h libks_la_SOURCES += src/ks_acl.c @@ -30,12 +30,9 @@ library_include_HEADERS += src/include/ks_thread_pool.h src/include/ks_cJSON.h s library_include_HEADERS += src/include/ks_pool.h src/include/simclist.h src/include/ks_time.h src/include/ks_q.h src/include/ks_socket.h library_include_HEADERS += src/include/ks_dso.h src/include/ks_platform.h src/include/ks_types.h # src/include/ks_rng.h src/include/ks_dht.h library_include_HEADERS += src/include/ks_printf.h src/include/ks_hash.h src/include/ks_ssl.h src/include/kws.h +library_include_HEADERS += src/include/ks_base64.h library_include_HEADERS += src/utp/utp_internal.h src/utp/utp.h src/utp/utp_types.h src/utp/utp_callbacks.h src/utp/utp_templates.h library_include_HEADERS += src/utp/utp_hash.h src/utp/utp_packedsockaddr.h src/utp/utp_utils.h src/include/ks_utp.h src/include/ks_acl.h tests: libks.la $(MAKE) -C test tests - - - - diff --git a/libs/libks/src/include/ks.h b/libs/libks/src/include/ks.h index b19f62e463..d5094f29a6 100644 --- a/libs/libks/src/include/ks.h +++ b/libs/libks/src/include/ks.h @@ -1,23 +1,23 @@ /* * Copyright (c) 2007-2014, Anthony Minessale II * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: - * + * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * * 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. - * + * * * Neither the name of the original author; nor the names of any contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. - * - * + * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR @@ -90,6 +90,8 @@ KS_DECLARE_DATA extern ks_logger_t ks_log; KS_DECLARE(void) ks_global_set_logger(ks_logger_t logger); /*! Sets the default log level for libks */ KS_DECLARE(void) ks_global_set_default_logger(int level); +/*! Sets the default log prefix for libks */ +KS_DECLARE(void) ks_global_set_default_logger_prefix(ks_log_prefix_t prefix); KS_DECLARE(size_t) ks_url_encode(const char *url, char *buf, size_t len); KS_DECLARE(char *) ks_url_decode(char *s); @@ -137,6 +139,7 @@ KS_DECLARE(void) ks_random_string(char *buf, uint16_t len, char *set); #include "ks_bencode.h" #include "ks_rng.h" #include "ks_acl.h" +#include "ks_base64.h" KS_END_EXTERN_C diff --git a/libs/libblade/src/include/bpcp.h b/libs/libks/src/include/ks_base64.h similarity index 84% rename from libs/libblade/src/include/bpcp.h rename to libs/libks/src/include/ks_base64.h index 46a648abbe..3c5eb81ace 100644 --- a/libs/libblade/src/include/bpcp.h +++ b/libs/libks/src/include/ks_base64.h @@ -1,23 +1,23 @@ /* - * Copyright (c) 2007-2014, Anthony Minessale II + * Copyright (c) 2017 FreeSWITCH Solutions LLC * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: - * + * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * * 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. - * + * * * Neither the name of the original author; nor the names of any contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. - * - * + * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR @@ -31,13 +31,14 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef _BPCP_H_ -#define _BPCP_H_ -#include -KS_BEGIN_EXTERN_C +#ifndef _KS_BASE64_H__ +#define _KS_BASE64_H__ -KS_END_EXTERN_C +#include + +KS_DECLARE(ks_status_t) ks_b64_encode(unsigned char *in, ks_size_t ilen, unsigned char *out, ks_size_t olen); +KS_DECLARE(ks_size_t) ks_b64_decode(char *in, char *out, ks_size_t olen); #endif diff --git a/libs/libks/src/include/ks_platform.h b/libs/libks/src/include/ks_platform.h index 741a904bd0..0b6a04c1af 100644 --- a/libs/libks/src/include/ks_platform.h +++ b/libs/libks/src/include/ks_platform.h @@ -1,23 +1,23 @@ /* * Copyright (c) 2007-2015, Anthony Minessale II * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: - * + * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * * 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. - * + * * * Neither the name of the original author; nor the names of any contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. - * - * + * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR @@ -103,9 +103,10 @@ KS_BEGIN_EXTERN_C #include #include #include -#include +#include +#include #endif - + #ifdef _MSC_VER #pragma comment(lib, "Ws2_32.lib") diff --git a/libs/libks/src/include/ks_threadmutex.h b/libs/libks/src/include/ks_threadmutex.h index ceac9e45e8..0739efe5db 100644 --- a/libs/libks/src/include/ks_threadmutex.h +++ b/libs/libks/src/include/ks_threadmutex.h @@ -45,6 +45,14 @@ KS_BEGIN_EXTERN_C #endif ks_thread_os_handle_t; +typedef +#ifdef WIN32 + DWORD +#else + pid_t +#endif +ks_pid_t; + struct ks_thread { ks_pool_t *pool; #ifdef WIN32 @@ -76,6 +84,7 @@ struct ks_thread { KS_DECLARE(int) ks_thread_set_priority(int nice_val); KS_DECLARE(ks_thread_os_handle_t) ks_thread_self(void); + KS_DECLARE(ks_pid_t) ks_thread_self_id(void); KS_DECLARE(ks_thread_os_handle_t) ks_thread_os_handle(ks_thread_t *thread); KS_DECLARE(ks_status_t) ks_thread_create_ex(ks_thread_t **thread, ks_thread_function_t func, void *data, uint32_t flags, size_t stack_size, ks_thread_priority_t priority, ks_pool_t *pool); diff --git a/libs/libks/src/include/ks_types.h b/libs/libks/src/include/ks_types.h index 74d59c03a0..f7e1aea07d 100644 --- a/libs/libks/src/include/ks_types.h +++ b/libs/libks/src/include/ks_types.h @@ -1,23 +1,23 @@ /* * Copyright (c) 2007-2015, Anthony Minessale II * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: - * + * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * * 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. - * + * * * Neither the name of the original author; nor the names of any contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. - * - * + * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR @@ -38,7 +38,7 @@ KS_BEGIN_EXTERN_C -#define KS_STR2ENUM_P(_FUNC1, _FUNC2, _TYPE) KS_DECLARE(_TYPE) _FUNC1 (const char *name); KS_DECLARE(const char *) _FUNC2 (_TYPE type); +#define KS_STR2ENUM_P(_FUNC1, _FUNC2, _TYPE) KS_DECLARE(_TYPE) _FUNC1 (const char *name); KS_DECLARE(const char *) _FUNC2 (_TYPE type); #define KS_STR2ENUM(_FUNC1, _FUNC2, _TYPE, _STRINGS, _MAX) \ KS_DECLARE(_TYPE) _FUNC1 (const char *name) \ @@ -63,7 +63,7 @@ KS_BEGIN_EXTERN_C return _STRINGS[(int)type]; \ } \ -#define KS_ENUM_NAMES(_NAME, _STRINGS) static const char * _NAME [] = { _STRINGS , NULL }; +#define KS_ENUM_NAMES(_NAME, _STRINGS) static const char * _NAME [] = { _STRINGS , NULL }; #define KS_VA_NONE "%s", "" @@ -142,7 +142,7 @@ KS_BEGIN_EXTERN_C /* insert new entries before this */\ "COUNT" - KS_STR2ENUM_P(ks_str2ks_status, ks_status2str, ks_status_t) + KS_STR2ENUM_P(ks_str2ks_status, ks_status2str, ks_status_t) /*! \brief Used internally for truth test */ typedef enum { @@ -173,6 +173,19 @@ KS_BEGIN_EXTERN_C #define KS_LOG_ALERT KS_PRE, KS_LOG_LEVEL_ALERT #define KS_LOG_EMERG KS_PRE, KS_LOG_LEVEL_EMERG +typedef enum { + KS_LOG_PREFIX_NONE = 0, + + KS_LOG_PREFIX_LEVEL = 1 << 0, + KS_LOG_PREFIX_FILE = 1 << 1, + KS_LOG_PREFIX_LINE = 1 << 2, + KS_LOG_PREFIX_FUNC = 1 << 3, + KS_LOG_PREFIX_THREAD = 1 << 4, + KS_LOG_PREFIX_TIME = 1 << 5, + + KS_LOG_PREFIX_ALL = KS_LOG_PREFIX_LEVEL | KS_LOG_PREFIX_FILE | KS_LOG_PREFIX_LINE | KS_LOG_PREFIX_FUNC | KS_LOG_PREFIX_THREAD | KS_LOG_PREFIX_TIME, +} ks_log_prefix_t; + struct ks_pool_s; typedef struct ks_pool_s ks_pool_t; diff --git a/libs/libks/src/include/simclist.h b/libs/libks/src/include/simclist.h index d22587df33..90cdd0cead 100755 --- a/libs/libks/src/include/simclist.h +++ b/libs/libks/src/include/simclist.h @@ -27,6 +27,7 @@ extern "C" { #endif +#include #include #include #include @@ -209,7 +210,7 @@ extern "C" { * @param l must point to a user-provided memory location * @return 0 for success. -1 for failure */ - int list_init(list_t *restrict l); + KS_DECLARE(int) list_init(list_t *restrict l); /** * completely remove the list from memory. @@ -220,7 +221,7 @@ extern "C" { * * @param l list to destroy */ - void list_destroy(list_t *restrict l); + KS_DECLARE(void) list_destroy(list_t *restrict l); /** * set the comparator function for list elements. @@ -234,7 +235,7 @@ extern "C" { * * @see element_comparator() */ - int list_attributes_comparator(list_t *restrict l, element_comparator comparator_fun); + KS_DECLARE(int) list_attributes_comparator(list_t *restrict l, element_comparator comparator_fun); /** * set a seeker function for list elements. @@ -248,7 +249,7 @@ extern "C" { * * @see element_seeker() */ - int list_attributes_seeker(list_t *restrict l, element_seeker seeker_fun); + KS_DECLARE(int) list_attributes_seeker(list_t *restrict l, element_seeker seeker_fun); /** * require to free element data when list entry is removed (default: don't free). @@ -280,7 +281,7 @@ extern "C" { * @see list_meter_double() * @see list_meter_string() */ - int list_attributes_copy(list_t *restrict l, element_meter metric_fun, int copy_data); + KS_DECLARE(int) list_attributes_copy(list_t *restrict l, element_meter metric_fun, int copy_data); /** * set the element hash computing function for the list elements. @@ -300,7 +301,7 @@ extern "C" { * * @see element_hash_computer() */ - int list_attributes_hash_computer(list_t *restrict l, element_hash_computer hash_computer_fun); + KS_DECLARE(int) list_attributes_hash_computer(list_t *restrict l, element_hash_computer hash_computer_fun); /** * set the element serializer function for the list elements. @@ -321,7 +322,7 @@ extern "C" { * @see list_dump_filedescriptor() * @see list_restore_filedescriptor() */ - int list_attributes_serializer(list_t *restrict l, element_serializer serializer_fun); + KS_DECLARE(int) list_attributes_serializer(list_t *restrict l, element_serializer serializer_fun); /** * set the element unserializer function for the list elements. @@ -343,7 +344,7 @@ extern "C" { * @see list_dump_filedescriptor() * @see list_restore_filedescriptor() */ - int list_attributes_unserializer(list_t *restrict l, element_unserializer unserializer_fun); + KS_DECLARE(int) list_attributes_unserializer(list_t *restrict l, element_unserializer unserializer_fun); /** * append data at the end of the list. @@ -355,7 +356,7 @@ extern "C" { * * @return 1 for success. < 0 for failure */ - int list_append(list_t *restrict l, const void *data); + KS_DECLARE(int) list_append(list_t *restrict l, const void *data); /** * insert data in the head of the list. @@ -367,7 +368,7 @@ extern "C" { * * @return 1 for success. < 0 for failure */ - int list_prepend(list_t *restrict l, const void *restrict data); + KS_DECLARE(int) list_prepend(list_t *restrict l, const void *restrict data); /** * extract the element in the top of the list. @@ -377,7 +378,7 @@ extern "C" { * @param l list to operate * @return reference to user datum, or NULL on errors */ - void *list_fetch(list_t *restrict l); + KS_DECLARE(void *) list_fetch(list_t *restrict l); /** * retrieve an element at a given position. @@ -386,7 +387,7 @@ extern "C" { * @param pos [0,size-1] position index of the element wanted * @return reference to user datum, or NULL on errors */ - void *list_get_at(const list_t *restrict l, unsigned int pos); + KS_DECLARE(void *) list_get_at(const list_t *restrict l, unsigned int pos); /** * return the maximum element of the list. @@ -400,7 +401,7 @@ extern "C" { * @param l list to operate * @return the reference to the element, or NULL */ - void *list_get_max(const list_t *restrict l); + KS_DECLARE(void *) list_get_max(const list_t *restrict l); /** * return the minimum element of the list. @@ -414,7 +415,7 @@ extern "C" { * @param l list to operate * @return the reference to the element, or NULL */ - void *list_get_min(const list_t *restrict l); + KS_DECLARE(void *) list_get_min(const list_t *restrict l); /** * retrieve and remove from list an element at a given position. @@ -423,7 +424,7 @@ extern "C" { * @param pos [0,size-1] position index of the element wanted * @return reference to user datum, or NULL on errors */ - void *list_extract_at(list_t *restrict l, unsigned int pos); + KS_DECLARE(void *) list_extract_at(list_t *restrict l, unsigned int pos); /** * insert an element at a given position. @@ -433,7 +434,7 @@ extern "C" { * @param pos [0,size-1] position index to insert the element at * @return positive value on success. Negative on failure */ - int list_insert_at(list_t *restrict l, const void *data, unsigned int pos); + KS_DECLARE(int) list_insert_at(list_t *restrict l, const void *data, unsigned int pos); /** * expunge the first found given element from the list. @@ -450,7 +451,7 @@ extern "C" { * @see list_attributes_comparator() * @see list_delete_at() */ - int list_delete(list_t *restrict l, const void *data); + KS_DECLARE(int) list_delete(list_t *restrict l, const void *data); /** * expunge an element at a given position from the list. @@ -459,7 +460,7 @@ extern "C" { * @param pos [0,size-1] position index of the element to be deleted * @return 0 on success. Negative value on failure */ - int list_delete_at(list_t *restrict l, unsigned int pos); + KS_DECLARE(int) list_delete_at(list_t *restrict l, unsigned int pos); /** * expunge an array of elements from the list, given their position range. @@ -469,7 +470,7 @@ extern "C" { * @param posend [posstart,size-1] position of the last element to be deleted * @return the number of elements successfully removed on success, <0 on error */ - int list_delete_range(list_t *restrict l, unsigned int posstart, unsigned int posend); + KS_DECLARE(int) list_delete_range(list_t *restrict l, unsigned int posstart, unsigned int posend); /** * clear all the elements off of the list. @@ -482,7 +483,7 @@ extern "C" { * @param l list to operate * @return the number of elements removed on success, <0 on error */ - int list_clear(list_t *restrict l); + KS_DECLARE(int) list_clear(list_t *restrict l); /** * inspect the number of elements in the list. @@ -490,7 +491,7 @@ extern "C" { * @param l list to operate * @return number of elements currently held by the list */ - unsigned int list_size(const list_t *restrict l); + KS_DECLARE(unsigned int) list_size(const list_t *restrict l); /** * inspect whether the list is empty. @@ -500,7 +501,7 @@ extern "C" { * * @see list_size() */ - int list_empty(const list_t *restrict l); + KS_DECLARE(int) list_empty(const list_t *restrict l); /** * find the position of an element in a list. @@ -519,7 +520,7 @@ extern "C" { * @see list_attributes_comparator() * @see list_get_at() */ - int list_locate(const list_t *restrict l, const void *data); + KS_DECLARE(int) list_locate(const list_t *restrict l, const void *data); /** * returns an element given an indicator. @@ -534,7 +535,7 @@ extern "C" { * @param indicator indicator data to pass to the seeker along with elements * @return reference to the element accepted by the seeker, or NULL if none found */ - void *list_seek(list_t *restrict l, const void *indicator); + KS_DECLARE(void *) list_seek(list_t *restrict l, const void *indicator); /** * inspect whether some data is member of the list. @@ -555,7 +556,7 @@ extern "C" { * * @see list_attributes_comparator() */ - int list_contains(const list_t *restrict l, const void *data); + KS_DECLARE(int) list_contains(const list_t *restrict l, const void *data); /** * concatenate two lists @@ -574,7 +575,7 @@ extern "C" { * @param dest reference to the destination list * @return 0 for success, -1 for errors */ - int list_concat(const list_t *l1, const list_t *l2, list_t *restrict dest); + KS_DECLARE(int) list_concat(const list_t *l1, const list_t *l2, list_t *restrict dest); /** * sort list elements. @@ -591,7 +592,7 @@ extern "C" { * * @see list_attributes_comparator() */ - int list_sort(list_t *restrict l, int versus); + KS_DECLARE(int) list_sort(list_t *restrict l, int versus); /** * start an iteration session. @@ -603,7 +604,7 @@ extern "C" { * * @see list_iterator_stop() */ - int list_iterator_start(list_t *restrict l); + KS_DECLARE(int) list_iterator_start(list_t *restrict l); /** * return the next element in the iteration session. @@ -611,7 +612,7 @@ extern "C" { * @param l list to operate * @return element datum, or NULL on errors */ - void *list_iterator_next(list_t *restrict l); + KS_DECLARE(void *) list_iterator_next(list_t *restrict l); /** * inspect whether more elements are available in the iteration session. @@ -619,7 +620,7 @@ extern "C" { * @param l list to operate * @return 0 iff no more elements are available. */ - int list_iterator_hasnext(const list_t *restrict l); + KS_DECLARE(int) list_iterator_hasnext(const list_t *restrict l); /** * end an iteration session. @@ -627,7 +628,7 @@ extern "C" { * @param l list to operate * @return 0 iff the iteration session cannot be stopped */ - int list_iterator_stop(list_t *restrict l); + KS_DECLARE(int) list_iterator_stop(list_t *restrict l); /** * return the hash of the current status of the list. @@ -637,7 +638,7 @@ extern "C" { * * @return 0 for success; <0 for failure */ - int list_hash(const list_t *restrict l, list_hash_t *restrict hash); + KS_DECLARE(int) list_hash(const list_t *restrict l, list_hash_t *restrict hash); #ifndef SIMCLIST_NO_DUMPRESTORE /** @@ -655,7 +656,7 @@ extern "C" { * * @see list_dump_filedescriptor() */ - int list_dump_getinfo_filedescriptor(int fd, list_dump_info_t *restrict info); + KS_DECLARE(int) list_dump_getinfo_filedescriptor(int fd, list_dump_info_t *restrict info); /** * get meta informations on a list dump on file. @@ -670,7 +671,7 @@ extern "C" { * * @see list_dump_filedescriptor() */ - int list_dump_getinfo_file(const char *restrict filename, list_dump_info_t *restrict info); + KS_DECLARE(int) list_dump_getinfo_file(const char *restrict filename, list_dump_info_t *restrict info); /** * dump the list into an open, writable file descriptor. @@ -706,7 +707,7 @@ extern "C" { * @see list_attributes_copy() * @see list_attributes_serializer() */ - int list_dump_filedescriptor(const list_t *restrict l, int fd, size_t *restrict len); + KS_DECLARE(int) list_dump_filedescriptor(const list_t *restrict l, int fd, size_t *restrict len); /** * dump the list to a file name. @@ -729,7 +730,7 @@ extern "C" { * * This function stores a representation of the list */ - int list_dump_file(const list_t *restrict l, const char *restrict filename, size_t *restrict len); + KS_DECLARE(int) list_dump_file(const list_t *restrict l, const char *restrict filename, size_t *restrict len); /** * restore the list from an open, readable file descriptor to memory. @@ -749,7 +750,7 @@ extern "C" { * @param len location to store the length of the dump read (bytes), or NULL * @return 0 if successful; -1 otherwise */ - int list_restore_filedescriptor(list_t *restrict l, int fd, size_t *restrict len); + KS_DECLARE(int) list_restore_filedescriptor(list_t *restrict l, int fd, size_t *restrict len); /** * restore the list from a file name. @@ -767,7 +768,7 @@ extern "C" { * @param len location to store the length of the dump read (bytes), or NULL * @return 0 if successful; -1 otherwise */ - int list_restore_file(list_t *restrict l, const char *restrict filename, size_t *len); + KS_DECLARE(int) list_restore_file(list_t *restrict l, const char *restrict filename, size_t *len); #endif /* ready-made comparators, meters and hash computers */ @@ -776,201 +777,201 @@ extern "C" { * ready-made comparator for int8_t elements. * @see list_attributes_comparator() */ - int list_comparator_int8_t (const void *a, const void *b); + KS_DECLARE(int) list_comparator_int8_t (const void *a, const void *b); /** * ready-made comparator for int16_t elements. * @see list_attributes_comparator() */ - int list_comparator_int16_t (const void *a, const void *b); + KS_DECLARE(int) list_comparator_int16_t (const void *a, const void *b); /** * ready-made comparator for int32_t elements. * @see list_attributes_comparator() */ - int list_comparator_int32_t (const void *a, const void *b); + KS_DECLARE(int) list_comparator_int32_t (const void *a, const void *b); /** * ready-made comparator for int64_t elements. * @see list_attributes_comparator() */ - int list_comparator_int64_t (const void *a, const void *b); + KS_DECLARE(int) list_comparator_int64_t (const void *a, const void *b); /** * ready-made comparator for uint8_t elements. * @see list_attributes_comparator() */ - int list_comparator_uint8_t (const void *a, const void *b); + KS_DECLARE(int) list_comparator_uint8_t (const void *a, const void *b); /** * ready-made comparator for uint16_t elements. * @see list_attributes_comparator() */ - int list_comparator_uint16_t (const void *a, const void *b); + KS_DECLARE(int) list_comparator_uint16_t (const void *a, const void *b); /** * ready-made comparator for uint32_t elements. * @see list_attributes_comparator() */ - int list_comparator_uint32_t (const void *a, const void *b); + KS_DECLARE(int) list_comparator_uint32_t (const void *a, const void *b); /** * ready-made comparator for uint64_t elements. * @see list_attributes_comparator() */ - int list_comparator_uint64_t (const void *a, const void *b); + KS_DECLARE(int) list_comparator_uint64_t (const void *a, const void *b); /** * ready-made comparator for float elements. * @see list_attributes_comparator() */ - int list_comparator_float(const void *a, const void *b); + KS_DECLARE(int) list_comparator_float(const void *a, const void *b); /** * ready-made comparator for double elements. * @see list_attributes_comparator() */ - int list_comparator_double(const void *a, const void *b); + KS_DECLARE(int) list_comparator_double(const void *a, const void *b); /** * ready-made comparator for string elements. * @see list_attributes_comparator() */ - int list_comparator_string(const void *a, const void *b); + KS_DECLARE(int) list_comparator_string(const void *a, const void *b); /* metric functions */ /** * ready-made metric function for int8_t elements. * @see list_attributes_copy() */ - size_t list_meter_int8_t (const void *el); + KS_DECLARE(size_t) list_meter_int8_t (const void *el); /** * ready-made metric function for int16_t elements. * @see list_attributes_copy() */ - size_t list_meter_int16_t (const void *el); + KS_DECLARE(size_t) list_meter_int16_t (const void *el); /** * ready-made metric function for int32_t elements. * @see list_attributes_copy() */ - size_t list_meter_int32_t (const void *el); + KS_DECLARE(size_t) list_meter_int32_t (const void *el); /** * ready-made metric function for int64_t elements. * @see list_attributes_copy() */ - size_t list_meter_int64_t (const void *el); + KS_DECLARE(size_t) list_meter_int64_t (const void *el); /** * ready-made metric function for uint8_t elements. * @see list_attributes_copy() */ - size_t list_meter_uint8_t (const void *el); + KS_DECLARE(size_t) list_meter_uint8_t (const void *el); /** * ready-made metric function for uint16_t elements. * @see list_attributes_copy() */ - size_t list_meter_uint16_t (const void *el); + KS_DECLARE(size_t) list_meter_uint16_t (const void *el); /** * ready-made metric function for uint32_t elements. * @see list_attributes_copy() */ - size_t list_meter_uint32_t (const void *el); + KS_DECLARE(size_t) list_meter_uint32_t (const void *el); /** * ready-made metric function for uint64_t elements. * @see list_attributes_copy() */ - size_t list_meter_uint64_t (const void *el); + KS_DECLARE(size_t) list_meter_uint64_t (const void *el); /** * ready-made metric function for float elements. * @see list_attributes_copy() */ - size_t list_meter_float(const void *el); + KS_DECLARE(size_t) list_meter_float(const void *el); /** * ready-made metric function for double elements. * @see list_attributes_copy() */ - size_t list_meter_double(const void *el); + KS_DECLARE(size_t) list_meter_double(const void *el); /** * ready-made metric function for string elements. * @see list_attributes_copy() */ - size_t list_meter_string(const void *el); + KS_DECLARE(size_t) list_meter_string(const void *el); /* hash functions */ /** * ready-made hash function for int8_t elements. * @see list_attributes_hash_computer() */ - list_hash_t list_hashcomputer_int8_t(const void *el); + KS_DECLARE(list_hash_t) list_hashcomputer_int8_t(const void *el); /** * ready-made hash function for int16_t elements. * @see list_attributes_hash_computer() */ - list_hash_t list_hashcomputer_int16_t(const void *el); + KS_DECLARE(list_hash_t) list_hashcomputer_int16_t(const void *el); /** * ready-made hash function for int32_t elements. * @see list_attributes_hash_computer() */ - list_hash_t list_hashcomputer_int32_t(const void *el); + KS_DECLARE(list_hash_t) list_hashcomputer_int32_t(const void *el); /** * ready-made hash function for int64_t elements. * @see list_attributes_hash_computer() */ - list_hash_t list_hashcomputer_int64_t(const void *el); + KS_DECLARE(list_hash_t) list_hashcomputer_int64_t(const void *el); /** * ready-made hash function for uint8_t elements. * @see list_attributes_hash_computer() */ - list_hash_t list_hashcomputer_uint8_t(const void *el); + KS_DECLARE(list_hash_t) list_hashcomputer_uint8_t(const void *el); /** * ready-made hash function for uint16_t elements. * @see list_attributes_hash_computer() */ - list_hash_t list_hashcomputer_uint16_t(const void *el); + KS_DECLARE(list_hash_t) list_hashcomputer_uint16_t(const void *el); /** * ready-made hash function for uint32_t elements. * @see list_attributes_hash_computer() */ - list_hash_t list_hashcomputer_uint32_t(const void *el); + KS_DECLARE(list_hash_t) list_hashcomputer_uint32_t(const void *el); /** * ready-made hash function for uint64_t elements. * @see list_attributes_hash_computer() */ - list_hash_t list_hashcomputer_uint64_t(const void *el); + KS_DECLARE(list_hash_t) list_hashcomputer_uint64_t(const void *el); /** * ready-made hash function for float elements. * @see list_attributes_hash_computer() */ - list_hash_t list_hashcomputer_float(const void *el); + KS_DECLARE(list_hash_t) list_hashcomputer_float(const void *el); /** * ready-made hash function for double elements. * @see list_attributes_hash_computer() */ - list_hash_t list_hashcomputer_double(const void *el); + KS_DECLARE(list_hash_t) list_hashcomputer_double(const void *el); /** * ready-made hash function for string elements. * @see list_attributes_hash_computer() */ - list_hash_t list_hashcomputer_string(const void *el); + KS_DECLARE(list_hash_t) list_hashcomputer_string(const void *el); #ifdef __cplusplus } diff --git a/libs/libks/src/ks_base64.c b/libs/libks/src/ks_base64.c new file mode 100644 index 0000000000..6e528f0fd2 --- /dev/null +++ b/libs/libks/src/ks_base64.c @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2017 FreeSWITCH Solutions LLC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * 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. + * + * * Neither the name of the original author; nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT OWNER + * OR CONTRIBUTORS 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 + + +static const char ks_b64_table[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + +KS_DECLARE(ks_status_t) ks_b64_encode(unsigned char *in, ks_size_t ilen, unsigned char *out, ks_size_t olen) +{ + int y = 0, bytes = 0; + size_t x = 0; + unsigned int b = 0, l = 0; + + for (x = 0; x < ilen; x++) { + b = (b << 8) + in[x]; + l += 8; + + while (l >= 6) { + out[bytes++] = ks_b64_table[(b >> (l -= 6)) % 64]; + if (bytes >= (int)olen - 1) { + goto end; + } + if (++y != 72) { + continue; + } + /* out[bytes++] = '\n'; */ + y = 0; + } + } + + if (l > 0) { + out[bytes++] = ks_b64_table[((b % 16) << (6 - l)) % 64]; + } + if (l != 0) { + while (l < 6 && bytes < (int)olen - 1) { + out[bytes++] = '=', l += 2; + } + } + + end: + + out[bytes] = '\0'; + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_size_t) ks_b64_decode(char *in, char *out, ks_size_t olen) +{ + + char l64[256]; + int b = 0, c, l = 0, i; + char *ip, *op = out; + size_t ol = 0; + + for (i = 0; i < 256; i++) { + l64[i] = -1; + } + + for (i = 0; i < 64; i++) { + l64[(int) ks_b64_table[i]] = (char) i; + } + + for (ip = in; ip && *ip; ip++) { + c = l64[(int) *ip]; + if (c == -1) { + continue; + } + + b = (b << 6) + c; + l += 6; + + while (l >= 8) { + op[ol++] = (char) ((b >> (l -= 8)) % 256); + if (ol >= olen - 2) { + goto end; + } + } + } + + end: + + op[ol++] = '\0'; + + return ol; +} + + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: + */ diff --git a/libs/libks/src/ks_log.c b/libs/libks/src/ks_log.c index a7c49f75ce..1d9f2a12eb 100644 --- a/libs/libks/src/ks_log.c +++ b/libs/libks/src/ks_log.c @@ -1,23 +1,23 @@ /* * Copyright (c) 2007-2014, Anthony Minessale II * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: - * + * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * * 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. - * + * * * Neither the name of the original author; nor the names of any contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. - * - * + * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR @@ -55,6 +55,7 @@ static const char *LEVEL_NAMES[] = { }; static int ks_log_level = 7; +static ks_log_prefix_t ks_log_prefix = KS_LOG_PREFIX_ALL; static const char *cut_path(const char *in) { @@ -78,6 +79,9 @@ static void default_logger(const char *file, const char *func, int line, int lev char *data; va_list ap; int ret; + char buf[1024]; + //int remaining = sizeof(buf) - 1; + int used = 0; if (level < 0 || level > 7) { level = 7; @@ -93,7 +97,33 @@ static void default_logger(const char *file, const char *func, int line, int lev ret = ks_vasprintf(&data, fmt, ap); if (ret != -1) { - fprintf(stderr, "[%s] %s:%d %s() %s", LEVEL_NAMES[level], fp, line, func, data); + buf[0] = '\0'; + used += 1; + + if (ks_log_prefix & KS_LOG_PREFIX_LEVEL) { + used += snprintf(buf + used - 1, sizeof(buf) - used, "[%s] ", LEVEL_NAMES[level]); + } + if (ks_log_prefix & KS_LOG_PREFIX_TIME) { + used += snprintf(buf + used - 1, sizeof(buf) - used, "@%lld ", (long long int)ks_time_now()); + } + if (ks_log_prefix & KS_LOG_PREFIX_THREAD) { + used += snprintf(buf + used - 1, sizeof(buf) - used, "#%d ", (int32_t)ks_thread_self_id()); + } + if (ks_log_prefix & KS_LOG_PREFIX_FILE) { + used += snprintf(buf + used - 1, sizeof(buf) - used, fp); + if (ks_log_prefix & KS_LOG_PREFIX_LINE) { + used += snprintf(buf + used - 1, sizeof(buf) - used, ":%d", line); + } + used += snprintf(buf + used - 1, sizeof(buf) - used, " "); + } + if (ks_log_prefix & KS_LOG_PREFIX_FUNC) { + used += snprintf(buf + used - 1, sizeof(buf) - used, "%s() ", func); + } + + used += snprintf(buf + used - 1, sizeof(buf) - used, data); + + //fprintf(stderr, "[%s] %s:%d %s() %s", LEVEL_NAMES[level], fp, line, func, data); + fprintf(stderr, buf); free(data); } @@ -121,3 +151,8 @@ KS_DECLARE(void) ks_global_set_default_logger(int level) ks_log = default_logger; ks_log_level = level; } + +KS_DECLARE(void) ks_global_set_default_logger_prefix(ks_log_prefix_t prefix) +{ + ks_log_prefix = prefix; +} diff --git a/libs/libks/src/ks_socket.c b/libs/libks/src/ks_socket.c index 1696437924..5c951ea956 100644 --- a/libs/libks/src/ks_socket.c +++ b/libs/libks/src/ks_socket.c @@ -121,6 +121,15 @@ KS_DECLARE(ks_status_t) ks_socket_option(ks_socket_t socket, int option_name, ks #endif } break; + case IPV6_V6ONLY: +#ifdef WIN32 +#warning make sure windows works like linux for IPV6 to IPV4 automapping stuff + result = setsockopt(socket, SOL_IPV6, IPV6_V6ONLY, (char *)&opt, sizeof(opt)); +#else + result = setsockopt(socket, SOL_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)); +#endif + if (!result) status = KS_STATUS_SUCCESS; + break; default: break; } diff --git a/libs/libks/src/ks_thread.c b/libs/libks/src/ks_thread.c index 890d22a02e..adb8b2163a 100644 --- a/libs/libks/src/ks_thread.c +++ b/libs/libks/src/ks_thread.c @@ -39,6 +39,17 @@ KS_DECLARE(ks_thread_os_handle_t) ks_thread_self(void) #endif } +KS_DECLARE(ks_pid_t) ks_thread_self_id(void) +{ +#ifdef WIN32 + return GetCurrentThreadId(); +#elseif gettid + return gettid(); +#else + return syscall(SYS_gettid); +#endif +} + static void ks_thread_init_priority(void) { #ifdef WIN32 diff --git a/libs/libks/src/simclist.c b/libs/libks/src/simclist.c index e132022260..0f9885af56 100755 --- a/libs/libks/src/simclist.c +++ b/libs/libks/src/simclist.c @@ -268,7 +268,7 @@ static inline long get_random(void) /* list initialization */ -int list_init(list_t *restrict l) +KS_DECLARE(int) list_init(list_t *restrict l) { if (l == NULL) return -1; @@ -306,7 +306,7 @@ int list_init(list_t *restrict l) return 0; } -void list_destroy(list_t *restrict l) +KS_DECLARE(void) list_destroy(list_t *restrict l) { unsigned int i; @@ -340,7 +340,7 @@ int list_attributes_setdefaults(list_t *restrict l) } /* setting list properties */ -int list_attributes_comparator(list_t *restrict l, element_comparator comparator_fun) +KS_DECLARE(int) list_attributes_comparator(list_t *restrict l, element_comparator comparator_fun) { if (l == NULL) return -1; @@ -352,7 +352,7 @@ int list_attributes_comparator(list_t *restrict l, element_comparator comparator return 0; } -int list_attributes_seeker(list_t *restrict l, element_seeker seeker_fun) +KS_DECLARE(int) list_attributes_seeker(list_t *restrict l, element_seeker seeker_fun) { if (l == NULL) return -1; @@ -363,7 +363,7 @@ int list_attributes_seeker(list_t *restrict l, element_seeker seeker_fun) return 0; } -int list_attributes_copy(list_t *restrict l, element_meter metric_fun, int copy_data) +KS_DECLARE(int) list_attributes_copy(list_t *restrict l, element_meter metric_fun, int copy_data) { if (l == NULL || (metric_fun == NULL && copy_data != 0)) return -1; @@ -376,7 +376,7 @@ int list_attributes_copy(list_t *restrict l, element_meter metric_fun, int copy_ return 0; } -int list_attributes_hash_computer(list_t *restrict l, element_hash_computer hash_computer_fun) +KS_DECLARE(int) list_attributes_hash_computer(list_t *restrict l, element_hash_computer hash_computer_fun) { if (l == NULL) return -1; @@ -386,7 +386,7 @@ int list_attributes_hash_computer(list_t *restrict l, element_hash_computer hash return 0; } -int list_attributes_serializer(list_t *restrict l, element_serializer serializer_fun) +KS_DECLARE(int) list_attributes_serializer(list_t *restrict l, element_serializer serializer_fun) { if (l == NULL) return -1; @@ -396,7 +396,7 @@ int list_attributes_serializer(list_t *restrict l, element_serializer serializer return 0; } -int list_attributes_unserializer(list_t *restrict l, element_unserializer unserializer_fun) +KS_DECLARE(int) list_attributes_unserializer(list_t *restrict l, element_unserializer unserializer_fun) { if (l == NULL) return -1; @@ -406,22 +406,22 @@ int list_attributes_unserializer(list_t *restrict l, element_unserializer unseri return 0; } -int list_append(list_t *restrict l, const void *data) +KS_DECLARE(int) list_append(list_t *restrict l, const void *data) { return list_insert_at(l, data, l->numels); } -int list_prepend(list_t *restrict l, const void *data) +KS_DECLARE(int) list_prepend(list_t *restrict l, const void *data) { return list_insert_at(l, data, 0); } -void *list_fetch(list_t *restrict l) +KS_DECLARE(void *) list_fetch(list_t *restrict l) { return list_extract_at(l, 0); } -void *list_get_at(const list_t *restrict l, unsigned int pos) +KS_DECLARE(void *) list_get_at(const list_t *restrict l, unsigned int pos) { struct list_entry_s *tmp; @@ -430,12 +430,12 @@ void *list_get_at(const list_t *restrict l, unsigned int pos) return (tmp != NULL ? tmp->data : NULL); } -void *list_get_max(const list_t *restrict l) +KS_DECLARE(void *) list_get_max(const list_t *restrict l) { return list_get_minmax(l, +1); } -void *list_get_min(const list_t *restrict l) +KS_DECLARE(void *) list_get_min(const list_t *restrict l) { return list_get_minmax(l, -1); } @@ -488,7 +488,7 @@ static inline struct list_entry_s *list_findpos(const list_t *restrict l, int po return ptr; } -void *list_extract_at(list_t *restrict l, unsigned int pos) +KS_DECLARE(void *) list_extract_at(list_t *restrict l, unsigned int pos) { struct list_entry_s *tmp; void *data; @@ -508,7 +508,7 @@ void *list_extract_at(list_t *restrict l, unsigned int pos) return data; } -int list_insert_at(list_t *restrict l, const void *data, unsigned int pos) +KS_DECLARE(int) list_insert_at(list_t *restrict l, const void *data, unsigned int pos) { struct list_entry_s *lent, *succ, *prec; @@ -561,7 +561,7 @@ int list_insert_at(list_t *restrict l, const void *data, unsigned int pos) return 1; } -int list_delete(list_t *restrict l, const void *data) +KS_DECLARE(int) list_delete(list_t *restrict l, const void *data) { int pos, r; @@ -578,7 +578,7 @@ int list_delete(list_t *restrict l, const void *data) return 0; } -int list_delete_at(list_t *restrict l, unsigned int pos) +KS_DECLARE(int) list_delete_at(list_t *restrict l, unsigned int pos) { struct list_entry_s *delendo; @@ -598,7 +598,7 @@ int list_delete_at(list_t *restrict l, unsigned int pos) return 0; } -int list_delete_range(list_t *restrict l, unsigned int posstart, unsigned int posend) +KS_DECLARE(int) list_delete_range(list_t *restrict l, unsigned int posstart, unsigned int posend) { struct list_entry_s *lastvalid, *tmp, *tmp2; unsigned int numdel, midposafter, i; @@ -665,7 +665,7 @@ int list_delete_range(list_t *restrict l, unsigned int posstart, unsigned int po return numdel; } -int list_clear(list_t *restrict l) +KS_DECLARE(int) list_clear(list_t *restrict l) { struct list_entry_s *s; unsigned int numels; @@ -715,17 +715,17 @@ int list_clear(list_t *restrict l) return numels; } -unsigned int list_size(const list_t *restrict l) +KS_DECLARE(unsigned int) list_size(const list_t *restrict l) { return l->numels; } -int list_empty(const list_t *restrict l) +KS_DECLARE(int) list_empty(const list_t *restrict l) { return (l->numels == 0); } -int list_locate(const list_t *restrict l, const void *data) +KS_DECLARE(int) list_locate(const list_t *restrict l, const void *data) { struct list_entry_s *el; int pos = 0; @@ -749,7 +749,7 @@ int list_locate(const list_t *restrict l, const void *data) return pos; } -void *list_seek(list_t *restrict l, const void *indicator) +KS_DECLARE(void *) list_seek(list_t *restrict l, const void *indicator) { const struct list_entry_s *iter; @@ -764,12 +764,12 @@ void *list_seek(list_t *restrict l, const void *indicator) return NULL; } -int list_contains(const list_t *restrict l, const void *data) +KS_DECLARE(int) list_contains(const list_t *restrict l, const void *data) { return (list_locate(l, data) >= 0); } -int list_concat(const list_t *l1, const list_t *l2, list_t *restrict dest) +KS_DECLARE(int) list_concat(const list_t *l1, const list_t *l2, list_t *restrict dest) { struct list_entry_s *el, *srcel; unsigned int cnt; @@ -825,7 +825,7 @@ int list_concat(const list_t *l1, const list_t *l2, list_t *restrict dest) return 0; } -int list_sort(list_t *restrict l, int versus) +KS_DECLARE(int) list_sort(list_t *restrict l, int versus) { if (l->iter_active || l->attrs.comparator == NULL) /* cannot modify list in the middle of an iteration */ return -1; @@ -1005,7 +1005,7 @@ static void list_sort_quicksort(list_t *restrict l, int versus, unsigned int fir #endif } -int list_iterator_start(list_t *restrict l) +KS_DECLARE(int) list_iterator_start(list_t *restrict l) { if (l->iter_active) return 0; @@ -1015,7 +1015,7 @@ int list_iterator_start(list_t *restrict l) return 1; } -void *list_iterator_next(list_t *restrict l) +KS_DECLARE(void *) list_iterator_next(list_t *restrict l) { void *toret; @@ -1029,14 +1029,14 @@ void *list_iterator_next(list_t *restrict l) return toret; } -int list_iterator_hasnext(const list_t *restrict l) +KS_DECLARE(int) list_iterator_hasnext(const list_t *restrict l) { if (!l->iter_active) return 0; return (l->iter_pos < l->numels); } -int list_iterator_stop(list_t *restrict l) +KS_DECLARE(int) list_iterator_stop(list_t *restrict l) { if (!l->iter_active) return 0; @@ -1045,7 +1045,7 @@ int list_iterator_stop(list_t *restrict l) return 1; } -int list_hash(const list_t *restrict l, list_hash_t *restrict hash) +KS_DECLARE(int) list_hash(const list_t *restrict l, list_hash_t *restrict hash) { struct list_entry_s *x; list_hash_t tmphash; @@ -1083,7 +1083,7 @@ int list_hash(const list_t *restrict l, list_hash_t *restrict hash) } #ifndef SIMCLIST_NO_DUMPRESTORE -int list_dump_getinfo_filedescriptor(int fd, list_dump_info_t *restrict info) +KS_DECLARE(int) list_dump_getinfo_filedescriptor(int fd, list_dump_info_t *restrict info) { int32_t terminator_head, terminator_tail; uint32_t elemlen; @@ -1148,7 +1148,7 @@ int list_dump_getinfo_filedescriptor(int fd, list_dump_info_t *restrict info) return 0; } -int list_dump_getinfo_file(const char *restrict filename, list_dump_info_t *restrict info) +KS_DECLARE(int) list_dump_getinfo_file(const char *restrict filename, list_dump_info_t *restrict info) { int fd, ret; @@ -1162,7 +1162,7 @@ int list_dump_getinfo_file(const char *restrict filename, list_dump_info_t *rest return ret; } -int list_dump_filedescriptor(const list_t *restrict l, int fd, size_t *restrict len) +KS_DECLARE(int) list_dump_filedescriptor(const list_t *restrict l, int fd, size_t *restrict len) { struct list_entry_s *x; void *ser_buf; @@ -1311,7 +1311,7 @@ int list_dump_filedescriptor(const list_t *restrict l, int fd, size_t *restrict return 0; } -int list_restore_filedescriptor(list_t *restrict l, int fd, size_t *restrict len) +KS_DECLARE(int) list_restore_filedescriptor(list_t *restrict l, int fd, size_t *restrict len) { struct list_dump_header_s header; unsigned long cnt; @@ -1436,7 +1436,7 @@ int list_restore_filedescriptor(list_t *restrict l, int fd, size_t *restrict len return 0; } -int list_dump_file(const list_t *restrict l, const char *restrict filename, size_t *restrict len) +KS_DECLARE(int) list_dump_file(const list_t *restrict l, const char *restrict filename, size_t *restrict len) { int fd, oflag, mode; @@ -1457,7 +1457,7 @@ int list_dump_file(const list_t *restrict l, const char *restrict filename, size return 0; } -int list_restore_file(list_t *restrict l, const char *restrict filename, size_t *restrict len) +KS_DECLARE(int) list_restore_file(list_t *restrict l, const char *restrict filename, size_t *restrict len) { int fd; @@ -1507,38 +1507,38 @@ static int list_drop_elem(list_t *restrict l, struct list_entry_s *tmp, unsigned } /* ready-made comparators and meters */ -#define SIMCLIST_NUMBER_COMPARATOR(type) int list_comparator_##type(const void *a, const void *b) { return( *(type *)a < *(type *)b) - (*(type *)a > *(type *)b); } +#define SIMCLIST_NUMBER_COMPARATOR(type) KS_DECLARE(int) list_comparator_##type(const void *a, const void *b) { return( *(type *)a < *(type *)b) - (*(type *)a > *(type *)b); } SIMCLIST_NUMBER_COMPARATOR(int8_t) SIMCLIST_NUMBER_COMPARATOR(int16_t) SIMCLIST_NUMBER_COMPARATOR(int32_t) SIMCLIST_NUMBER_COMPARATOR(int64_t) SIMCLIST_NUMBER_COMPARATOR(uint8_t) SIMCLIST_NUMBER_COMPARATOR(uint16_t) SIMCLIST_NUMBER_COMPARATOR(uint32_t) SIMCLIST_NUMBER_COMPARATOR(uint64_t) SIMCLIST_NUMBER_COMPARATOR(float) SIMCLIST_NUMBER_COMPARATOR(double) - int list_comparator_string(const void *a, const void *b) +KS_DECLARE(int) list_comparator_string(const void *a, const void *b) { return strcmp((const char *) b, (const char *) a); } /* ready-made metric functions */ -#define SIMCLIST_METER(type) size_t list_meter_##type(const void *el) { if (el) { /* kill compiler whinge */ } return sizeof(type); } +#define SIMCLIST_METER(type) KS_DECLARE(size_t) list_meter_##type(const void *el) { if (el) { /* kill compiler whinge */ } return sizeof(type); } SIMCLIST_METER(int8_t) SIMCLIST_METER(int16_t) SIMCLIST_METER(int32_t) SIMCLIST_METER(int64_t) SIMCLIST_METER(uint8_t) SIMCLIST_METER(uint16_t) SIMCLIST_METER(uint32_t) SIMCLIST_METER(uint64_t) SIMCLIST_METER(float) SIMCLIST_METER(double) - size_t list_meter_string(const void *el) +KS_DECLARE(size_t) list_meter_string(const void *el) { return strlen((const char *) el) + 1; } /* ready-made hashing functions */ -#define SIMCLIST_HASHCOMPUTER(type) list_hash_t list_hashcomputer_##type(const void *el) { return (list_hash_t)(*(type *)el); } +#define SIMCLIST_HASHCOMPUTER(type) KS_DECLARE(list_hash_t) list_hashcomputer_##type(const void *el) { return (list_hash_t)(*(type *)el); } SIMCLIST_HASHCOMPUTER(int8_t) SIMCLIST_HASHCOMPUTER(int16_t) SIMCLIST_HASHCOMPUTER(int32_t) SIMCLIST_HASHCOMPUTER(int64_t) SIMCLIST_HASHCOMPUTER(uint8_t) SIMCLIST_HASHCOMPUTER(uint16_t) SIMCLIST_HASHCOMPUTER(uint32_t) SIMCLIST_HASHCOMPUTER(uint64_t) SIMCLIST_HASHCOMPUTER(float) SIMCLIST_HASHCOMPUTER(double) - list_hash_t list_hashcomputer_string(const void *el) +KS_DECLARE(list_hash_t) list_hashcomputer_string(const void *el) { size_t l; list_hash_t hash = 123; diff --git a/libs/libks/test/Makefile.am b/libs/libks/test/Makefile.am index a974af0062..cffc18bdcd 100644 --- a/libs/libks/test/Makefile.am +++ b/libs/libks/test/Makefile.am @@ -1,6 +1,6 @@ AM_CFLAGS += -I$(abs_top_srcdir)/src/include -g -ggdb -O0 $(openssl_CFLAGS) TEST_LDADD = $(abs_top_builddir)/libks.la $(openssl_LIBS) -check_PROGRAMS = +check_PROGRAMS = EXTRA_DIST = tap.h @@ -91,7 +91,7 @@ nodeidgen_LDADD = $(TEST_LDADD) #check_PROGRAMS += libtorrent_example #libtorrent_example_SOURCES = libtorrent-example.c -#libtorrent_example_CFLAGS = $(AM_CFLAGS) +#libtorrent_example_CFLAGS = $(AM_CFLAGS) #libtorrent_example_LDADD = $(abs_top_builddir)/libks.la $(abs_top_builddir)/test/libtorrent.so /usr/lib/x86_64-linux-gnu/libboost_system.a $(openssl_LIBS) -ledit -lpthread -ltorrent-rasterbar -lstdc++ TESTS=$(check_PROGRAMS) @@ -103,4 +103,3 @@ $(abs_top_builddir)/test/libtorrent.so: $(abs_top_builddir)/test/libtorrent.o $(abs_top_builddir)/test/libtorrent.o: $(abs_top_builddir)/test/libtorrent.cpp g++ -c -fPIC -o $(abs_top_builddir)/test/libtorrent.o -I$(abs_top_builddir)/test/ $(abs_top_builddir)/test/libtorrent.cpp - diff --git a/libs/libks/test/testbuckets.c b/libs/libks/test/testbuckets.c index 5d1555a585..c9eca0cc2d 100644 --- a/libs/libks/test/testbuckets.c +++ b/libs/libks/test/testbuckets.c @@ -1140,11 +1140,9 @@ int main(int argc, char *argv[]) { ks_init(); ks_global_set_default_logger(7); - + ks_dht_nodeid_t homeid; memset(homeid.id, 0x01, KS_DHT_NODEID_SIZE); - - ks_dht_create(&dht, NULL, NULL, &homeid);