From c60a42009d200d910c9bd6616e2c34da6757eced Mon Sep 17 00:00:00 2001 From: colm Date: Mon, 23 Jan 2017 14:30:19 -0500 Subject: [PATCH 01/34] FS-9952: Initial json rpc messaging --- libs/libks/Makefile.am | 5 +- libs/libks/src/include/ks_rpcmessage.h | 91 ++++++++ libs/libks/src/ks_rpcmessage.c | 306 +++++++++++++++++++++++++ libs/libks/test/Makefile.am | 5 + 4 files changed, 405 insertions(+), 2 deletions(-) create mode 100644 libs/libks/src/include/ks_rpcmessage.h create mode 100644 libs/libks/src/ks_rpcmessage.c diff --git a/libs/libks/Makefile.am b/libs/libks/Makefile.am index 48a66c5ff4..8007ce6feb 100644 --- a/libs/libks/Makefile.am +++ b/libs/libks/Makefile.am @@ -10,7 +10,7 @@ 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_rpcmessage.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 @@ -29,7 +29,8 @@ library_include_HEADERS = src/include/ks_config.h src/include/ks.h src/include/k library_include_HEADERS += src/include/ks_thread_pool.h src/include/ks_cJSON.h src/include/ks_cJSON_Utils.h 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_printf.h src/include/ks_hash.h src/include/ks_ssl.h src/include/kws.h +library_include_HEADERS += src/include/ks_rpcmessage.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 diff --git a/libs/libks/src/include/ks_rpcmessage.h b/libs/libks/src/include/ks_rpcmessage.h new file mode 100644 index 0000000000..45c3def89c --- /dev/null +++ b/libs/libks/src/include/ks_rpcmessage.h @@ -0,0 +1,91 @@ +/* + * 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. + */ + +#ifndef _KS_RPCMESSAGE_H_ +#define _KS_RPCMESSAGE_H_ + +#include "ks.h" + +KS_BEGIN_EXTERN_C + + + + + +typedef struct ks_rpcmessaging_handle_s ks_rpcmessaging_handle_t; + + + +typedef ks_status_t (*jrpc_func_t)(ks_rpcmessaging_handle_t* handle, cJSON *params, cJSON **responseP); + + + + +KS_DECLARE(ks_rpcmessaging_handle_t *) ks_rpcmessage_init(ks_pool_t* pool, ks_rpcmessaging_handle_t** handleP); +KS_DECLARE(void) ks_rpcmessage_deinit(ks_rpcmessaging_handle_t** handleP); + +KS_DECLARE(cJSON *)ks_rpcmessage_new_request(ks_rpcmessaging_handle_t* handle, + const char *method, + cJSON **parmsP, + cJSON **requestP); +KS_DECLARE(cJSON *)ks_rpcmessage_new_response(ks_rpcmessaging_handle_t* handle, + const cJSON *request, + cJSON *result, + cJSON **responseP); + +KS_DECLARE(ks_status_t)ks_rpcmessage_namespace(ks_rpcmessaging_handle_t* handle, const char* namespace); + +KS_DECLARE(ks_status_t)ks_rpcmessage_register_function(ks_rpcmessaging_handle_t* handle, const char *command, jrpc_func_t func); +KS_DECLARE(jrpc_func_t) ks_rpcmessage_find_function(ks_rpcmessaging_handle_t* handle, const char *command); + +KS_DECLARE(ks_status_t) ks_rpcmessage_process_request(ks_rpcmessaging_handle_t* handle, + uint8_t *data, + ks_size_t size, + cJSON **responseP); +KS_DECLARE(ks_status_t) ks_rpcmessage_process_jsonrequest(ks_rpcmessaging_handle_t* handle, cJSON *request, cJSON **responseP); + + +KS_END_EXTERN_C + +#endif /* defined(_KS_RPCMESSAGE_H_) */ + +/* 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_rpcmessage.c b/libs/libks/src/ks_rpcmessage.c new file mode 100644 index 0000000000..a28747ef72 --- /dev/null +++ b/libs/libks/src/ks_rpcmessage.c @@ -0,0 +1,306 @@ +/* + * 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. + */ + +#pragma GCC optimize ("O0") + + +#include +#include + +#define KS_RPCMESSAGE_NAMESPACE_LENGTH 16 + + +struct ks_rpcmessaging_handle_s +{ + ks_hash_t *method_hash; + + ks_mutex_t *id_mutex; + uint32_t message_id; + + ks_pool_t *pool; + + char namespace[KS_RPCMESSAGE_NAMESPACE_LENGTH+2]; +}; + + +static uint32_t ks_rpcmessage_next_id(ks_rpcmessaging_handle_t* handle) +{ + uint32_t message_id; + + ks_mutex_lock(handle->id_mutex); + + ++handle->message_id; + + if (!handle->message_id) { + ++handle->message_id; + } + + message_id = handle->message_id; + + ks_mutex_unlock(handle->id_mutex); + + return message_id; +} + + +KS_DECLARE(ks_rpcmessaging_handle_t*) ks_rpcmessage_init(ks_pool_t* pool, ks_rpcmessaging_handle_t** handleP) +{ + ks_rpcmessaging_handle_t *handle = (ks_rpcmessaging_handle_t *)ks_pool_alloc(pool, sizeof(ks_rpcmessaging_handle_t)); + *handleP = handle; + + ks_hash_create(&handle->method_hash, + KS_HASH_MODE_CASE_SENSITIVE, + KS_HASH_FLAG_RWLOCK + KS_HASH_FLAG_DUP_CHECK + KS_HASH_FLAG_FREE_KEY, + pool); + + ks_mutex_create(&handle->id_mutex, KS_MUTEX_FLAG_DEFAULT, pool); + + strcpy(handle->namespace, "global."); + + handle->pool = pool; + return handle; +} + + +KS_DECLARE(void) ks_rpcmessage_deinit(ks_rpcmessaging_handle_t** handleP) +{ + ks_rpcmessaging_handle_t *handle = *handleP; + ks_hash_destroy(&handle->method_hash); + ks_pool_free(handle->pool, handleP); + return; +} + +static cJSON *ks_rpcmessage_new(uint32_t id) +{ + cJSON *obj = cJSON_CreateObject(); + cJSON_AddItemToObject(obj, "jsonrpc", cJSON_CreateString("2.0")); + + if (id) { + cJSON_AddItemToObject(obj, "id", cJSON_CreateNumber(id)); + } + + return obj; +} + +static cJSON *ks_rpcmessage_dup(cJSON *msgid) +{ + cJSON *obj = cJSON_CreateObject(); + cJSON_AddItemToObject(obj, "jsonrpc", cJSON_CreateString("2.0")); + + if (msgid) { + cJSON_AddItemToObject(obj, "id", cJSON_Duplicate(msgid, 0)); + } + + return obj; +} + + +KS_DECLARE(cJSON *) ks_rpcmessage_new_request(ks_rpcmessaging_handle_t* handle, + const char *command, + cJSON **paramsP, + cJSON **request) +{ + cJSON *msg, *params = NULL; + *request = NULL; + + if (!ks_rpcmessage_find_function(handle, command)) { + ks_log(KS_LOG_ERROR, "Attempt to create unknown message type : %s, namespace %s\n", command, handle->namespace); + return NULL; + } + + msg = ks_rpcmessage_new(ks_rpcmessage_next_id(handle)); + + if (paramsP && *paramsP) { + params = *paramsP; + } + + if (!params) { + params = cJSON_CreateObject(); + } + + cJSON_AddItemToObject(msg, "method", cJSON_CreateString(command)); + cJSON_AddItemToObject(msg, "params", params); + + if (paramsP) { + *paramsP = params; + } + + return msg; +} + +KS_DECLARE(cJSON *) ks_rpcmessage_new_response(ks_rpcmessaging_handle_t* handle, + const cJSON *request, + cJSON *result, + cJSON **pmsg) +{ + cJSON *respmsg = NULL; + cJSON *msgid = cJSON_GetObjectItem(request, "id"); + cJSON *command = cJSON_GetObjectItem(request, "method"); + + if (!msgid || !command) { + return NULL; + } + + *pmsg = respmsg = ks_rpcmessage_dup(msgid); + + cJSON_AddItemToObject(respmsg, "method", cJSON_Duplicate(command, 0)); + + if (result) { + cJSON_AddItemToObject(respmsg, "result", result); + } + + return respmsg; +} + +KS_DECLARE(ks_status_t) ks_rpcmessage_namespace(ks_rpcmessaging_handle_t* handle, const char* namespace) +{ + + strncpy(handle->namespace, namespace, sizeof(KS_RPCMESSAGE_NAMESPACE_LENGTH)); + handle->namespace[KS_RPCMESSAGE_NAMESPACE_LENGTH] = 0; + ks_log(KS_LOG_DEBUG, "Setting message namespace value %s", handle->namespace); + strcat( handle->namespace, "."); + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) ks_rpcmessage_register_function(ks_rpcmessaging_handle_t* handle, const char *command, jrpc_func_t func) +{ + char fqcommand[256]; + memset(fqcommand, 0, sizeof(fqcommand)); + + if (handle->namespace[0] != 0) { + strcpy(fqcommand, handle->namespace); + } + strcat(fqcommand, command); + + int lkey = strlen(fqcommand)+1; + + if (lkey < 16) { + lkey = 16; + } + + char *key = (char*)ks_pool_alloc(handle->pool, lkey); + strcpy(key, fqcommand); + + ks_hash_write_lock(handle->method_hash); + ks_hash_insert(handle->method_hash, key, (void *) (intptr_t)func); + ks_hash_write_unlock(handle->method_hash); + + ks_log(KS_LOG_DEBUG, "Message %s registered (%s)\n", command, fqcommand); + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(jrpc_func_t) ks_rpcmessage_find_function(ks_rpcmessaging_handle_t* handle, const char *command) +{ + char fqcommand[256]; + memset(fqcommand, 0, sizeof(fqcommand)); + + if (handle->namespace[0] != 0) { + strcpy(fqcommand, handle->namespace); + } + + strcat(fqcommand, command); + + ks_hash_read_lock(handle->method_hash); + + jrpc_func_t func = (jrpc_func_t) (intptr_t) ks_hash_search(handle->method_hash, fqcommand, KS_UNLOCKED); + + ks_hash_read_unlock(handle->method_hash); + + return func; +} + +KS_DECLARE(ks_status_t) ks_rpcmessage_process_jsonrequest(ks_rpcmessaging_handle_t* handle, cJSON *request, cJSON **responseP) +{ + const char *command = cJSON_GetObjectCstr(request, "method"); + cJSON *error = NULL; + cJSON *response = NULL; + *responseP = NULL; + + if (!command) { + error = cJSON_CreateString("Command not found"); + } + + //todo - add more checks ? + + if (error) { + *responseP = response = ks_rpcmessage_new_request(handle, 0, &error, &response); + return KS_STATUS_FAIL; + } + + jrpc_func_t func = ks_rpcmessage_find_function(handle, command); + if (!func) { + error = cJSON_CreateString("Command not supported"); + } + + return func(handle, request, responseP); +} + + + +KS_DECLARE(ks_status_t) ks_rpcmessage_process_request(ks_rpcmessaging_handle_t* handle, + uint8_t *data, + ks_size_t size, + cJSON **responseP) +{ + cJSON *response = NULL; + cJSON *error = NULL; + + cJSON *request = cJSON_Parse((char*)data); + + if (!request) { + error = cJSON_CreateString("Invalid json format"); + *responseP = response = ks_rpcmessage_new_request(handle, 0, &error, &response); + return KS_STATUS_FAIL; + } + + ks_status_t status = ks_rpcmessage_process_jsonrequest(handle, request, responseP); + + cJSON_Delete(request); + + return status; +} + + + +/* 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/test/Makefile.am b/libs/libks/test/Makefile.am index a974af0062..0b85a468c4 100644 --- a/libs/libks/test/Makefile.am +++ b/libs/libks/test/Makefile.am @@ -4,6 +4,11 @@ check_PROGRAMS = EXTRA_DIST = tap.h +check_PROGRAMS += testmessages +testmessages_SOURCES = testmessages.c tap.c +testmessages_CFLAGS = $(AM_CFLAGS) +testmessages_LDADD = $(TEST_LDADD) + check_PROGRAMS += testbuckets testbuckets_SOURCES = testbuckets.c tap.c testbuckets_CFLAGS = $(AM_CFLAGS) From 8d029ae06b8fa70a4e0513edfe69559eb48b00a6 Mon Sep 17 00:00:00 2001 From: colm Date: Mon, 23 Jan 2017 14:31:48 -0500 Subject: [PATCH 02/34] FS-9952: Update testbuckets to current api --- libs/libks/test/testbuckets.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) 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); From 59b1ab829ec8ad29d2bf80607ffa4b97d51b34fe Mon Sep 17 00:00:00 2001 From: colm Date: Wed, 25 Jan 2017 14:52:22 -0500 Subject: [PATCH 03/34] FS-9952: Initial json rpc messaging --- libs/libks/src/include/ks_rpcmessage.h | 76 ++++++- libs/libks/src/ks_rpcmessage.c | 274 ++++++++++++++++++++----- libs/libks/test/testmessages.c | 273 ++++++++++++++++++++++++ 3 files changed, 558 insertions(+), 65 deletions(-) create mode 100644 libs/libks/test/testmessages.c diff --git a/libs/libks/src/include/ks_rpcmessage.h b/libs/libks/src/include/ks_rpcmessage.h index 45c3def89c..c897f06a82 100644 --- a/libs/libks/src/include/ks_rpcmessage.h +++ b/libs/libks/src/include/ks_rpcmessage.h @@ -31,6 +31,53 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +/* + * Usage notes + * + * ks_rpcmessaging_handle_t *handle; + * + * ks_rpcmessage_init(pool, &handle); + * + * ks_rpcmessage_version(handle, version); + * ks_rpcmessage_namespace(handle, application_namespace); + * ks_rpcmessage_register_function(handle, "invite", handle_invite_message); + * ks_rpcmessage_register_function(handle, "media", handle_media_message); + * + * ... + * cJSON* request = NULL; + * cJSON* parms = NULL; + * cJSON* response = NULL; + * + * request = ks_rpcmessage_create_request(h, "invite", &parms, &request); + * cJSON_AddStringToObject(parms, "hello", "cruel world"); + * ... and send + * + * + * static ks_status_t handle_..._message(ks_rpcmessaging_handle_t* handle, cJSON *msg, cJSON **response) + * { + * cJSON *respvalue = cJSON_CreateNumber(1); + * cJSON *x = *response = ks_rpcmessage_create_response(h, msg, &respvalue, response); + * if ( x == NULL) { + * return KS_STATUS_FAIL; + * } + * ... + * return KS_STATUS_SUCCESS; + * } + + * + * + * + * + * + * + * ... + * ks_rpcmessage_deinit(&handle); + * + */ + + + + #ifndef _KS_RPCMESSAGE_H_ #define _KS_RPCMESSAGE_H_ @@ -43,36 +90,45 @@ KS_BEGIN_EXTERN_C typedef struct ks_rpcmessaging_handle_s ks_rpcmessaging_handle_t; +typedef uint32_t ks_rpcmessage_id; - -typedef ks_status_t (*jrpc_func_t)(ks_rpcmessaging_handle_t* handle, cJSON *params, cJSON **responseP); - +typedef ks_status_t (*jrpc_func_t)(ks_rpcmessaging_handle_t* handle, cJSON *request, cJSON **responseP); +typedef ks_status_t (*jrpc_resp_func_t)(ks_rpcmessaging_handle_t* handle, cJSON *response); KS_DECLARE(ks_rpcmessaging_handle_t *) ks_rpcmessage_init(ks_pool_t* pool, ks_rpcmessaging_handle_t** handleP); KS_DECLARE(void) ks_rpcmessage_deinit(ks_rpcmessaging_handle_t** handleP); -KS_DECLARE(cJSON *)ks_rpcmessage_new_request(ks_rpcmessaging_handle_t* handle, +KS_DECLARE(ks_status_t)ks_rpcmessage_namespace(ks_rpcmessaging_handle_t* handle, const char* namespace, const char* version); + +KS_DECLARE(ks_rpcmessage_id)ks_rpcmessage_create_request(ks_rpcmessaging_handle_t* handle, const char *method, cJSON **parmsP, cJSON **requestP); -KS_DECLARE(cJSON *)ks_rpcmessage_new_response(ks_rpcmessaging_handle_t* handle, +KS_DECLARE(ks_rpcmessage_id)ks_rpcmessage_create_response(ks_rpcmessaging_handle_t* handle, const cJSON *request, - cJSON *result, + cJSON **resultP, cJSON **responseP); +KS_DECLARE(ks_rpcmessage_id)ks_rpcmessage_create_errorresponse(ks_rpcmessaging_handle_t* handle, + const cJSON *request, + cJSON **errorP, + cJSON **responseP); -KS_DECLARE(ks_status_t)ks_rpcmessage_namespace(ks_rpcmessaging_handle_t* handle, const char* namespace); +KS_DECLARE(ks_status_t)ks_rpcmessage_register_function(ks_rpcmessaging_handle_t* handle, + const char *command, + jrpc_func_t func, + jrpc_resp_func_t respfunc); -KS_DECLARE(ks_status_t)ks_rpcmessage_register_function(ks_rpcmessaging_handle_t* handle, const char *command, jrpc_func_t func); KS_DECLARE(jrpc_func_t) ks_rpcmessage_find_function(ks_rpcmessaging_handle_t* handle, const char *command); +KS_DECLARE(jrpc_resp_func_t) ks_rpcmessage_find_response_function(ks_rpcmessaging_handle_t* handle, const char *command); -KS_DECLARE(ks_status_t) ks_rpcmessage_process_request(ks_rpcmessaging_handle_t* handle, +KS_DECLARE(ks_status_t) ks_rpcmessage_process_message(ks_rpcmessaging_handle_t* handle, uint8_t *data, ks_size_t size, cJSON **responseP); -KS_DECLARE(ks_status_t) ks_rpcmessage_process_jsonrequest(ks_rpcmessaging_handle_t* handle, cJSON *request, cJSON **responseP); +KS_DECLARE(ks_status_t) ks_rpcmessage_process_jsonmessage(ks_rpcmessaging_handle_t* handle, cJSON *request, cJSON **responseP); KS_END_EXTERN_C diff --git a/libs/libks/src/ks_rpcmessage.c b/libs/libks/src/ks_rpcmessage.c index a28747ef72..4712c7bd13 100644 --- a/libs/libks/src/ks_rpcmessage.c +++ b/libs/libks/src/ks_rpcmessage.c @@ -38,7 +38,9 @@ #include #define KS_RPCMESSAGE_NAMESPACE_LENGTH 16 - +#define KS_PRCMESSAGE_COMMAND_LENGTH 238 +#define KS_PRCMESSAGE_FQCOMMAND_LENGTH KS_RPCMESSAGE_NAMESPACE_LENGTH + 1 + KS_PRCMESSAGE_COMMAND_LENGTH +#define KS_RPCMESSAGE_VERSION_LENGTH 9 struct ks_rpcmessaging_handle_s { @@ -50,8 +52,17 @@ struct ks_rpcmessaging_handle_s ks_pool_t *pool; char namespace[KS_RPCMESSAGE_NAMESPACE_LENGTH+2]; + char version[KS_RPCMESSAGE_VERSION_LENGTH+1]; /* nnn.nn.nn */ }; +typedef struct ks_rpcmessage_callbackpair_s +{ + jrpc_func_t request_func; + jrpc_resp_func_t response_func; + uint16_t command_length; + char command[1]; +} ks_rpcmessage_callbackpair_t; + static uint32_t ks_rpcmessage_next_id(ks_rpcmessaging_handle_t* handle) { @@ -80,7 +91,7 @@ KS_DECLARE(ks_rpcmessaging_handle_t*) ks_rpcmessage_init(ks_pool_t* pool, ks_rpc ks_hash_create(&handle->method_hash, KS_HASH_MODE_CASE_SENSITIVE, - KS_HASH_FLAG_RWLOCK + KS_HASH_FLAG_DUP_CHECK + KS_HASH_FLAG_FREE_KEY, + KS_HASH_FLAG_RWLOCK + KS_HASH_FLAG_DUP_CHECK + KS_HASH_FLAG_FREE_VALUE, pool); ks_mutex_create(&handle->id_mutex, KS_MUTEX_FLAG_DEFAULT, pool); @@ -124,21 +135,36 @@ static cJSON *ks_rpcmessage_dup(cJSON *msgid) return obj; } +static ks_bool_t ks_rpcmessage_isrequest(cJSON *msg) +{ + //cJSON *params = cJSON_GetObjectItem(msg, "param"); + cJSON *result = cJSON_GetObjectItem(msg, "result"); + cJSON *error = cJSON_GetObjectItem(msg, "error"); -KS_DECLARE(cJSON *) ks_rpcmessage_new_request(ks_rpcmessaging_handle_t* handle, + if (result || error) { + return KS_FALSE; + } + + return KS_TRUE; +} + + + +KS_DECLARE(ks_rpcmessage_id) ks_rpcmessage_create_request(ks_rpcmessaging_handle_t* handle, const char *command, cJSON **paramsP, - cJSON **request) + cJSON **requestP) { cJSON *msg, *params = NULL; - *request = NULL; + *requestP = NULL; if (!ks_rpcmessage_find_function(handle, command)) { ks_log(KS_LOG_ERROR, "Attempt to create unknown message type : %s, namespace %s\n", command, handle->namespace); - return NULL; + return 0; } - msg = ks_rpcmessage_new(ks_rpcmessage_next_id(handle)); + ks_rpcmessage_id msgid = ks_rpcmessage_next_id(handle); + msg = ks_rpcmessage_new(msgid); if (paramsP && *paramsP) { params = *paramsP; @@ -148,54 +174,141 @@ KS_DECLARE(cJSON *) ks_rpcmessage_new_request(ks_rpcmessaging_handle_t* handle, params = cJSON_CreateObject(); } - cJSON_AddItemToObject(msg, "method", cJSON_CreateString(command)); + char fqcommand[KS_PRCMESSAGE_FQCOMMAND_LENGTH]; + memset(fqcommand, 0, sizeof(fqcommand)); + + if (handle->namespace[0] != 0) { + strcpy(fqcommand, handle->namespace); + } + + strcat(fqcommand, command); + + cJSON_AddItemToObject(msg, "method", cJSON_CreateString(fqcommand)); cJSON_AddItemToObject(msg, "params", params); + if (handle->version[0] != 0) { + cJSON_AddItemToObject(msg, "version", cJSON_CreateString(handle->version)); + } + if (paramsP) { *paramsP = params; } - return msg; + *requestP = msg; + return msgid; } -KS_DECLARE(cJSON *) ks_rpcmessage_new_response(ks_rpcmessaging_handle_t* handle, - const cJSON *request, - cJSON *result, - cJSON **pmsg) +static ks_rpcmessage_id ks_rpcmessage_get_messageid(const cJSON *msg, cJSON **cmsgidP) +{ + uint32_t msgid = 0; + + cJSON *cmsgid = cJSON_GetObjectItem(msg, "id"); + + if (cmsgid->type == cJSON_Number) { + msgid = (uint32_t) cmsgid->valueint; + } + + *cmsgidP = cmsgid; + + return msgid; +} + + +static ks_rpcmessage_id ks_rpcmessage_new_response(ks_rpcmessaging_handle_t* handle, + const cJSON *request, + cJSON *result, + cJSON **pmsg) { cJSON *respmsg = NULL; - cJSON *msgid = cJSON_GetObjectItem(request, "id"); - cJSON *command = cJSON_GetObjectItem(request, "method"); - - if (!msgid || !command) { - return NULL; - } - - *pmsg = respmsg = ks_rpcmessage_dup(msgid); + cJSON *cmsgid = NULL; + cJSON *command = cJSON_GetObjectItem(request, "method"); + + ks_rpcmessage_id msgid = ks_rpcmessage_get_messageid(request, &cmsgid ); + + if (!msgid || !command) { + return 0; + } + + *pmsg = respmsg = ks_rpcmessage_dup(cmsgid); cJSON_AddItemToObject(respmsg, "method", cJSON_Duplicate(command, 0)); - if (result) { - cJSON_AddItemToObject(respmsg, "result", result); - } + if (handle->version[0] != 0) { + cJSON_AddItemToObject(respmsg, "version", cJSON_CreateString(handle->version)); + } - return respmsg; + if (result) { + cJSON_AddItemToObject(respmsg, "result", result); + } + + return msgid; } -KS_DECLARE(ks_status_t) ks_rpcmessage_namespace(ks_rpcmessaging_handle_t* handle, const char* namespace) -{ - strncpy(handle->namespace, namespace, sizeof(KS_RPCMESSAGE_NAMESPACE_LENGTH)); - handle->namespace[KS_RPCMESSAGE_NAMESPACE_LENGTH] = 0; - ks_log(KS_LOG_DEBUG, "Setting message namespace value %s", handle->namespace); +KS_DECLARE(ks_rpcmessage_id) ks_rpcmessage_create_response(ks_rpcmessaging_handle_t* handle, + const cJSON *request, + cJSON **resultP, + cJSON **responseP) +{ + ks_rpcmessage_id msgid = ks_rpcmessage_new_response(handle, request, *resultP, responseP); + cJSON *respmsg = *responseP; + + if (msgid) { + + if (*resultP == NULL) { + *resultP = cJSON_CreateObject(); + cJSON_AddItemToObject(respmsg, "result", *resultP); + } + } + + return msgid; +} + +KS_DECLARE(ks_rpcmessage_id) ks_rpcmessage_create_errorresponse(ks_rpcmessaging_handle_t* handle, + const cJSON *request, + cJSON **errorP, + cJSON **responseP) +{ + ks_rpcmessage_id msgid = ks_rpcmessage_new_response(handle, request, *errorP, responseP); + cJSON *respmsg = *responseP; + + if (msgid) { + + if (*errorP == NULL) { + *errorP = cJSON_CreateObject(); + cJSON_AddItemToObject(respmsg, "error", *errorP); + } + } + + return msgid; +} + +KS_DECLARE(ks_status_t) ks_rpcmessage_namespace(ks_rpcmessaging_handle_t* handle, const char* namespace, const char* version) +{ + memset(handle->namespace, 0, sizeof(handle->namespace)); + memset(handle->version, 0, sizeof(handle->version)); + + strncpy(handle->namespace, namespace, KS_RPCMESSAGE_NAMESPACE_LENGTH); + strncpy(handle->version, version, KS_RPCMESSAGE_VERSION_LENGTH); + handle->namespace[sizeof(handle->namespace) - 1] = 0; + handle->version[sizeof(handle->version) -1] = 0; + + ks_log(KS_LOG_DEBUG, "Setting message namespace value %s, version %s", handle->namespace, handle->version); strcat( handle->namespace, "."); return KS_STATUS_SUCCESS; } -KS_DECLARE(ks_status_t) ks_rpcmessage_register_function(ks_rpcmessaging_handle_t* handle, const char *command, jrpc_func_t func) +KS_DECLARE(ks_status_t) ks_rpcmessage_register_function(ks_rpcmessaging_handle_t* handle, + const char *command, + jrpc_func_t func, + jrpc_resp_func_t respfunc) { - char fqcommand[256]; + if (!func && !respfunc) { + return KS_STATUS_FAIL; + } + + char fqcommand[KS_PRCMESSAGE_FQCOMMAND_LENGTH]; memset(fqcommand, 0, sizeof(fqcommand)); if (handle->namespace[0] != 0) { @@ -209,11 +322,17 @@ KS_DECLARE(ks_status_t) ks_rpcmessage_register_function(ks_rpcmessaging_handle_t lkey = 16; } - char *key = (char*)ks_pool_alloc(handle->pool, lkey); - strcpy(key, fqcommand); + ks_rpcmessage_callbackpair_t* callbacks = + (ks_rpcmessage_callbackpair_t*)ks_pool_alloc(handle->pool, lkey + sizeof(ks_rpcmessage_callbackpair_t)); + + strcpy(callbacks->command, fqcommand); + callbacks->command_length = lkey; + callbacks->request_func = func; + callbacks->response_func = respfunc; ks_hash_write_lock(handle->method_hash); - ks_hash_insert(handle->method_hash, key, (void *) (intptr_t)func); + ks_hash_insert(handle->method_hash, callbacks->command, (void *) callbacks); + ks_hash_write_unlock(handle->method_hash); ks_log(KS_LOG_DEBUG, "Message %s registered (%s)\n", command, fqcommand); @@ -221,27 +340,60 @@ KS_DECLARE(ks_status_t) ks_rpcmessage_register_function(ks_rpcmessaging_handle_t return KS_STATUS_SUCCESS; } -KS_DECLARE(jrpc_func_t) ks_rpcmessage_find_function(ks_rpcmessaging_handle_t* handle, const char *command) +static ks_rpcmessage_callbackpair_t* ks_rpcmessage_find_function_ex(ks_rpcmessaging_handle_t* handle, char *command) { - char fqcommand[256]; - memset(fqcommand, 0, sizeof(fqcommand)); - - if (handle->namespace[0] != 0) { - strcpy(fqcommand, handle->namespace); - } - - strcat(fqcommand, command); - ks_hash_read_lock(handle->method_hash); - jrpc_func_t func = (jrpc_func_t) (intptr_t) ks_hash_search(handle->method_hash, fqcommand, KS_UNLOCKED); + ks_rpcmessage_callbackpair_t* callbacks = ks_hash_search(handle->method_hash, command, KS_UNLOCKED); ks_hash_read_unlock(handle->method_hash); - return func; + return callbacks; } -KS_DECLARE(ks_status_t) ks_rpcmessage_process_jsonrequest(ks_rpcmessaging_handle_t* handle, cJSON *request, cJSON **responseP) +KS_DECLARE(jrpc_func_t) ks_rpcmessage_find_function(ks_rpcmessaging_handle_t* handle, const char *command) +{ + char fqcommand[KS_PRCMESSAGE_FQCOMMAND_LENGTH]; + memset(fqcommand, 0, sizeof(fqcommand)); + + if (handle->namespace[0] != 0) { + strcpy(fqcommand, handle->namespace); + strcat(fqcommand, command); + } + else { + strcpy(fqcommand, command); + } + + + ks_rpcmessage_callbackpair_t* callbacks = ks_rpcmessage_find_function_ex(handle, (char *)fqcommand); + + if (!callbacks) { + return NULL; + } + + return callbacks->request_func; +} + +KS_DECLARE(jrpc_resp_func_t) ks_rpcmessage_find_response_function(ks_rpcmessaging_handle_t* handle, const char *command) +{ + char fqcommand[KS_PRCMESSAGE_FQCOMMAND_LENGTH]; + memset(fqcommand, 0, sizeof(fqcommand)); + + if (handle->namespace[0] != 0) { + strcpy(fqcommand, handle->namespace); + strcat(fqcommand, command); + } + else { + strcpy(fqcommand, command); + } + + ks_rpcmessage_callbackpair_t* callbacks = ks_rpcmessage_find_function_ex(handle, (char *)fqcommand); + + return callbacks->response_func; +} + + +KS_DECLARE(ks_status_t) ks_rpcmessage_process_jsonmessage(ks_rpcmessaging_handle_t* handle, cJSON *request, cJSON **responseP) { const char *command = cJSON_GetObjectCstr(request, "method"); cJSON *error = NULL; @@ -255,21 +407,33 @@ KS_DECLARE(ks_status_t) ks_rpcmessage_process_jsonrequest(ks_rpcmessaging_handle //todo - add more checks ? if (error) { - *responseP = response = ks_rpcmessage_new_request(handle, 0, &error, &response); + ks_rpcmessage_create_request(handle, 0, &error, &response); return KS_STATUS_FAIL; } - jrpc_func_t func = ks_rpcmessage_find_function(handle, command); - if (!func) { + + ks_rpcmessage_callbackpair_t* callbacks = ks_rpcmessage_find_function_ex(handle, (char *)command); + + if (!callbacks) { error = cJSON_CreateString("Command not supported"); + return KS_STATUS_FAIL; } - return func(handle, request, responseP); + ks_bool_t isrequest = ks_rpcmessage_isrequest(request); + + if (isrequest && callbacks->request_func) { + return callbacks->request_func(handle, request, responseP); + } + else if (!isrequest && callbacks->response_func) { + return callbacks->response_func(handle, request); + } + + return KS_STATUS_FAIL; } -KS_DECLARE(ks_status_t) ks_rpcmessage_process_request(ks_rpcmessaging_handle_t* handle, +KS_DECLARE(ks_status_t) ks_rpcmessage_process_message(ks_rpcmessaging_handle_t* handle, uint8_t *data, ks_size_t size, cJSON **responseP) @@ -281,11 +445,11 @@ KS_DECLARE(ks_status_t) ks_rpcmessage_process_request(ks_rpcmessaging_handle_t* if (!request) { error = cJSON_CreateString("Invalid json format"); - *responseP = response = ks_rpcmessage_new_request(handle, 0, &error, &response); + ks_rpcmessage_create_request(handle, 0, &error, &response); return KS_STATUS_FAIL; } - ks_status_t status = ks_rpcmessage_process_jsonrequest(handle, request, responseP); + ks_status_t status = ks_rpcmessage_process_jsonmessage(handle, request, responseP); cJSON_Delete(request); diff --git a/libs/libks/test/testmessages.c b/libs/libks/test/testmessages.c new file mode 100644 index 0000000000..689173f44d --- /dev/null +++ b/libs/libks/test/testmessages.c @@ -0,0 +1,273 @@ +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" +#pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-function" + +#include "../src/include/ks_rpcmessage.h" + +#pragma GCC optimize ("O0") + + +ks_pool_t *pool; +ks_thread_pool_t *tpool; + +ks_rpcmessaging_handle_t *h; + +static ks_thread_t *threads[10]; + +static char idbuffer[51]; + + +static ks_status_t process_wombat_response(ks_rpcmessaging_handle_t* handle, cJSON *msg) +{ + printf("entering process_wombat_response\n"); + printf("exiting process_wombat_response\n"); + return KS_STATUS_FAIL; +} + +static ks_status_t process_wombat(ks_rpcmessaging_handle_t* handle, cJSON *msg, cJSON **response) +{ + printf("entering process_wombat\n"); + + char *b0 = cJSON_Print(msg); + printf("Request: %s\n", b0); + free(b0); + + cJSON *msg_id = cJSON_GetObjectItem(msg, "id"); + if (msg_id) { + if (msg_id->type == cJSON_Number) { + printf("Number int %d double %f\n", msg_id->valueint, msg_id->valuedouble); + } + } + + cJSON *respvalue = cJSON_CreateNumber(1); + + ks_rpcmessage_id msgid = ks_rpcmessage_create_response(h, msg, &respvalue, response); + + char *b1 = cJSON_Print(*response); //(*response); + printf("Response: msgid %d\n%s\n", msgid, b1); + free(b1); + + printf("exiting process_wombat\n"); + + return KS_STATUS_SUCCESS; +} + +static ks_status_t process_badbunny(ks_rpcmessaging_handle_t* handle, cJSON *msg, cJSON **response) +{ + printf("entering process_badbunny\n"); + + char *b0 = cJSON_Print(msg); + printf("Request: %s\n", b0); + free(b0); + + cJSON *msg_id = cJSON_GetObjectItem(msg, "id"); + if (msg_id) { + if (msg_id->type == cJSON_Number) { + printf("Number int %d double %f\n", msg_id->valueint, msg_id->valuedouble); + } + } + + cJSON *respvalue; + + ks_rpcmessage_id msgid = ks_rpcmessage_create_errorresponse(h, msg, &respvalue, response); + + char *b2 = cJSON_Print(*response); + printf("Request: msgid %d\n%s\n", msgid, b2); + free(b2); + + //cJSON *respvalue = cJSON_CreateNumber(1); + + + char *b1 = cJSON_Print(*response); //(*response); + printf("Response: %s\n", b1); + free(b1); + + printf("exiting process_badbunny\n"); + + + return KS_STATUS_SUCCESS; +} + + +void test01() +{ + printf("**** testrpcmessages - test01 start\n"); fflush(stdout); + + ks_rpcmessage_register_function(h, "wombat", process_wombat, process_wombat_response); + ks_rpcmessage_register_function(h, "badbunny", process_badbunny, NULL); + ks_rpcmessage_register_function(h, "onewaywombat", NULL, process_wombat_response); + + cJSON* request = NULL; + cJSON* parms = NULL; + cJSON* response = NULL; + + /* try an invalid message */ + + ks_rpcmessage_id msgid = ks_rpcmessage_create_request(h, "colm", &parms, &request); + if (msgid != 0) { + printf("invalid message created %d\n", msgid); + printf("request:\n%s\n", cJSON_Print(request)); + } + + /* try a basic message */ + + msgid = ks_rpcmessage_create_request(h, "wombat", &parms, &request); + if (msgid == 0) { + printf("failed to create a wombat\n"); + return; + } + + cJSON_AddStringToObject(parms, "hello", "cruel world"); + char* data = cJSON_PrintUnformatted(request); + + printf("\ntest01 request: %d\n%s\n\n", msgid, data); + + /* process message */ + + ks_size_t size = strlen(data); + ks_status_t status = ks_rpcmessage_process_message(h, (uint8_t*)data, size, &response); + + char* data1 = cJSON_Print(response); + ks_size_t size1 = strlen(data1); + printf("\ntest01i response: %d\n%s\n\n", msgid, data1); + + /* process response */ + + ks_status_t status1 = ks_rpcmessage_process_message(h, (uint8_t*)data1, size1, &response); + + free(data); + free(data1); + cJSON_Delete(request); + + /* create message 2 */ + + cJSON *parms1 = cJSON_CreateNumber(1); + cJSON *request1 = NULL; + + msgid = ks_rpcmessage_create_request(h, "badbunny", &parms1, &request1); + + data = cJSON_PrintUnformatted(request1); + printf("\ntest01i request: %d\n%s\n\n", msgid, data); + + /* process message 2 */ + + size = strlen(data); + status = ks_rpcmessage_process_message(h, (uint8_t*)data, size, &response); + + data1 = cJSON_PrintUnformatted(response); + printf("\ntest01 response: %d\n%s\n\n", msgid, data1); + + /* process response 2 - no handler so this should fail */ + + size1 = strlen(data1); + + status = ks_rpcmessage_process_message(h, (uint8_t*)data1, size1, &response); + + if (status != KS_STATUS_FAIL) { + printf("badbunny found a response handler ?\n"); + } + + free(data); + free(data1); + cJSON_Delete(request1); + + + printf("**** testrpcmessages - test01 complete\n\n\n"); fflush(stdout); +} + +void test02() +{ + printf("**** testmessages - test02 start\n"); fflush(stdout); + + printf("**** testmessages - test02 finished\n"); fflush(stdout); + + return; +} + + + + +/* test06 */ +/* ------ */ + +static void *testnodelocking_ex1(ks_thread_t *thread, void *data) +{ + return NULL; +} + +static void *testnodelocking_ex2(ks_thread_t *thread, void *data) +{ + return NULL; +} + + +void test06() +{ + printf("**** testmessages - test06 start\n"); fflush(stdout); + + ks_thread_t *t0; + ks_thread_create(&t0, testnodelocking_ex1, NULL, pool); + + ks_thread_t *t1; + ks_thread_create(&t1, testnodelocking_ex2, NULL, pool); + + ks_thread_join(t1); + ks_thread_join(t0); + + printf("\n\n* **testmessages - test06 -- threads complete\n\n"); fflush(stdout); + + printf("**** testmessages - test06 start\n"); fflush(stdout); + + return; +} + + + +int main(int argc, char *argv[]) { + + printf("testmessages - start\n"); + + int tests[100]; + if (argc == 0) { + tests[0] = 1; + tests[1] = 2; + tests[2] = 3; + tests[3] = 4; + tests[4] = 5; + } + else { + for(int tix=1; tix<100 && tix Date: Thu, 26 Jan 2017 12:45:40 -0500 Subject: [PATCH 04/34] FS-9952: Add base64 code (from verto) --- libs/libks/Makefile.am | 4 +- libs/libks/src/include/ks_base64.h | 54 ++++++++++++ libs/libks/src/ks_base64.c | 130 +++++++++++++++++++++++++++++ 3 files changed, 186 insertions(+), 2 deletions(-) create mode 100644 libs/libks/src/include/ks_base64.h create mode 100644 libs/libks/src/ks_base64.c diff --git a/libs/libks/Makefile.am b/libs/libks/Makefile.am index 8007ce6feb..57538f236b 100644 --- a/libs/libks/Makefile.am +++ b/libs/libks/Makefile.am @@ -10,7 +10,7 @@ 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 src/ks_rpcmessage.c +libks_la_SOURCES += src/ks_ssl.c src/kws.c src/ks_rng.c src/ks_rpcmessage.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 @@ -30,7 +30,7 @@ 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_rpcmessage.h +library_include_HEADERS += src/include/ks_rpcmessage.h 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 diff --git a/libs/libks/src/include/ks_base64.h b/libs/libks/src/include/ks_base64.h new file mode 100644 index 0000000000..3c5eb81ace --- /dev/null +++ b/libs/libks/src/include/ks_base64.h @@ -0,0 +1,54 @@ +/* + * 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. + */ + + +#ifndef _KS_BASE64_H__ +#define _KS_BASE64_H__ + +#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 + +/* 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_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: + */ From eb57b7910e15d84eb22cfdd70b7abd10f8b50094 Mon Sep 17 00:00:00 2001 From: Shane Bryldt Date: Fri, 27 Jan 2017 19:49:02 +0000 Subject: [PATCH 05/34] FS-9952: Temporary commit for some peer review --- libs/libblade/Makefile.am | 8 +- libs/libblade/src/blade_datastore.c | 98 ++++- libs/libblade/src/blade_directory.c | 165 ++++++++ libs/libblade/src/blade_peer.c | 133 +++++-- libs/libblade/src/blade_service.c | 401 ++++++++++++++++++++ libs/libblade/src/blade_stack.c | 121 +++--- libs/libblade/src/include/blade.h | 3 + libs/libblade/src/include/blade_datastore.h | 11 +- libs/libblade/src/include/blade_directory.h | 61 +++ libs/libblade/src/include/blade_peer.h | 8 +- libs/libblade/src/include/blade_service.h | 61 +++ libs/libblade/src/include/blade_stack.h | 13 +- libs/libblade/src/include/blade_types.h | 2 + libs/libblade/test/bladec.c | 47 +-- 14 files changed, 978 insertions(+), 154 deletions(-) create mode 100644 libs/libblade/src/blade_directory.c create mode 100644 libs/libblade/src/blade_service.c create mode 100644 libs/libblade/src/include/blade_directory.h create mode 100644 libs/libblade/src/include/blade_service.h diff --git a/libs/libblade/Makefile.am b/libs/libblade/Makefile.am index fe4c96b108..f55b1266bb 100644 --- a/libs/libblade/Makefile.am +++ b/libs/libblade/Makefile.am @@ -11,13 +11,13 @@ 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 src/blade_peer.c src/blade_service.c src/bpcp.c src/blade_datastore.c src/blade_directory.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_datastore.h +library_include_HEADERS = src/include/blade.h src/include/blade_types.h src/include/blade_stack.h src/include/blade_peer.h src/include/blade_service.h +library_include_HEADERS += src/include/bpcp.h src/include/blade_datastore.h src/include/blade_directory.h library_include_HEADERS += src/include/unqlite.h test/tap.h tests: libblade.la diff --git a/libs/libblade/src/blade_datastore.c b/libs/libblade/src/blade_datastore.c index d97cd9840d..49c3cbddb6 100644 --- a/libs/libblade/src/blade_datastore.c +++ b/libs/libblade/src/blade_datastore.c @@ -37,12 +37,19 @@ typedef enum { BDS_NONE = 0, BDS_MYPOOL = (1 << 0), + BDS_MYTPOOL = (1 << 1), } 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; + //config_setting_t *config_service; + unqlite *db; + //blade_service_t *service; }; struct blade_datastore_fetch_userdata_s @@ -71,11 +78,10 @@ KS_DECLARE(ks_status_t) blade_datastore_destroy(blade_datastore_t **bdsP) flags = bds->flags; pool = bds->pool; - if (bds->db) { - unqlite_close(bds->db); - bds->db = NULL; - } + blade_datastore_shutdown(bds); + if (bds->tpool && (flags & BDS_MYTPOOL)) ks_thread_pool_destroy(&bds->tpool); + ks_pool_free(bds->pool, &bds); if (pool && (flags & BDS_MYPOOL)) ks_pool_close(&pool); @@ -83,7 +89,7 @@ KS_DECLARE(ks_status_t) blade_datastore_destroy(blade_datastore_t **bdsP) 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; @@ -93,16 +99,67 @@ KS_DECLARE(ks_status_t) blade_datastore_create(blade_datastore_t **bdsP, ks_pool ks_pool_open(&pool); ks_assert(pool); } - + // @todo: move thread pool creation to startup which allows thread pool to be configurable + if (!tpool) { + newflags |= BDS_MYTPOOL; + ks_thread_pool_create(&tpool, + BLADE_DATASTORE_TPOOL_MIN, + BLADE_DATASTORE_TPOOL_MAX, + BLADE_DATASTORE_TPOOL_STACK, + KS_PRI_NORMAL, + BLADE_DATASTORE_TPOOL_IDLE); + 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; + //config_setting_t *service = 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); + //service = config_setting_get_member(config, "service"); + + 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); + //bds->config_service = service; + + 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, NULL, UNQLITE_OPEN_IN_MEMORY) != UNQLITE_OK) { + 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; } @@ -110,15 +167,31 @@ KS_DECLARE(ks_status_t) blade_datastore_create(blade_datastore_t **bdsP, ks_pool // @todo VM init if document store is used (and output consumer callback) + //blade_service_create(&bds->service, bds->pool, bds->tpool); + //ks_assert(bds->service); + //blade_service_startup(bds->service, bds->config_service); + 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->service) blade_service_destroy(&bds->service); + + if (bds->db) { + unqlite_close(bds->db); + bds->db = NULL; + } + + if (bds->config_database_path) ks_pool_free(bds->pool, &bds->config_database_path); + //bds->config_service = NULL; + + 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 +220,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 +269,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_directory.c b/libs/libblade/src/blade_directory.c new file mode 100644 index 0000000000..ab24942dfe --- /dev/null +++ b/libs/libblade/src/blade_directory.c @@ -0,0 +1,165 @@ +/* + * 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" + + +typedef enum { + BD_NONE = 0, + BD_MYPOOL = (1 << 0), + BD_MYTPOOL = (1 << 1), +} bdpvt_flag_t; + +struct blade_directory_s { + bdpvt_flag_t flags; + ks_pool_t *pool; + ks_thread_pool_t *tpool; + + config_setting_t *config_service; + + blade_service_t *service; +}; + + + + +KS_DECLARE(ks_status_t) blade_directory_destroy(blade_directory_t **bdP) +{ + blade_directory_t *bd = NULL; + bdpvt_flag_t flags; + ks_pool_t *pool; + + ks_assert(bdP); + + bd = *bdP; + *bdP = NULL; + + ks_assert(bd); + + flags = bd->flags; + pool = bd->pool; + + blade_directory_shutdown(bd); + + if (bd->tpool && (flags & BD_MYTPOOL)) ks_thread_pool_destroy(&bd->tpool); + + ks_pool_free(bd->pool, &bd); + + if (pool && (flags & BD_MYPOOL)) ks_pool_close(&pool); + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_directory_create(blade_directory_t **bdP, ks_pool_t *pool, ks_thread_pool_t *tpool) +{ + bdpvt_flag_t newflags = BD_NONE; + blade_directory_t *bd = NULL; + + if (!pool) { + newflags |= BD_MYPOOL; + ks_pool_open(&pool); + ks_assert(pool); + } + // @todo: move thread pool creation to startup which allows thread pool to be configurable + if (!tpool) { + newflags |= BD_MYTPOOL; + ks_thread_pool_create(&tpool, + BLADE_DIRECTORY_TPOOL_MIN, + BLADE_DIRECTORY_TPOOL_MAX, + BLADE_DIRECTORY_TPOOL_STACK, + KS_PRI_NORMAL, + BLADE_DIRECTORY_TPOOL_IDLE); + ks_assert(tpool); + } + + bd = ks_pool_alloc(pool, sizeof(*bd)); + bd->flags = newflags; + bd->pool = pool; + bd->tpool = tpool; + *bdP = bd; + + return KS_STATUS_SUCCESS; +} + +ks_status_t blade_directory_config(blade_directory_t *bd, config_setting_t *config) +{ + config_setting_t *service = NULL; + + ks_assert(bd); + + if (!config) return KS_STATUS_FAIL; + if (!config_setting_is_group(config)) return KS_STATUS_FAIL; + + service = config_setting_get_member(config, "service"); + if (!service) return KS_STATUS_FAIL; + + bd->config_service = service; + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_directory_startup(blade_directory_t *bd, config_setting_t *config) +{ + ks_assert(bd); + + blade_directory_shutdown(bd); + + if (blade_directory_config(bd, config) != KS_STATUS_SUCCESS) return KS_STATUS_FAIL; + + blade_service_create(&bd->service, bd->pool, bd->tpool); + ks_assert(bd->service); + if (blade_service_startup(bd->service, bd->config_service) != KS_STATUS_SUCCESS) return KS_STATUS_FAIL; + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_directory_shutdown(blade_directory_t *bd) +{ + ks_assert(bd); + + if (bd->service) blade_service_destroy(&bd->service); + + 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_peer.c b/libs/libblade/src/blade_peer.c index 8bb96ae676..2e794a629b 100644 --- a/libs/libblade/src/blade_peer.c +++ b/libs/libblade/src/blade_peer.c @@ -33,11 +33,6 @@ #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), @@ -48,10 +43,20 @@ struct blade_peer_s { bppvt_flag_t flags; ks_pool_t *pool; ks_thread_pool_t *tpool; - ks_dht_t *dht; + blade_service_t *service; + + ks_bool_t shutdown; + kws_t *kws; + ks_thread_t *kws_thread; + + ks_q_t *messages_sending; + ks_q_t *messages_receiving; }; +void *blade_peer_kws_thread(ks_thread_t *thread, void *data); + + KS_DECLARE(ks_status_t) blade_peer_destroy(blade_peer_t **bpP) { blade_peer_t *bp = NULL; @@ -68,7 +73,11 @@ KS_DECLARE(ks_status_t) blade_peer_destroy(blade_peer_t **bpP) flags = bp->flags; pool = bp->pool; - if (bp->dht) ks_dht_destroy(&bp->dht); + blade_peer_shutdown(bp); + + ks_q_destroy(&bp->messages_sending); + ks_q_destroy(&bp->messages_receiving); + if (bp->tpool && (flags & BP_MYTPOOL)) ks_thread_pool_destroy(&bp->tpool); ks_pool_free(bp->pool, &bp); @@ -78,11 +87,13 @@ KS_DECLARE(ks_status_t) blade_peer_destroy(blade_peer_t **bpP) 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) +KS_DECLARE(ks_status_t) blade_peer_create(blade_peer_t **bpP, ks_pool_t *pool, ks_thread_pool_t *tpool, blade_service_t *service) { bppvt_flag_t newflags = BP_NONE; blade_peer_t *bp = NULL; - ks_dht_t *dht = NULL; + + ks_assert(bpP); + ks_assert(service); if (!pool) { newflags |= BP_MYPOOL; @@ -94,50 +105,112 @@ KS_DECLARE(ks_status_t) blade_peer_create(blade_peer_t **bpP, ks_pool_t *pool, k 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; + bp->service = service; + ks_q_create(&bp->messages_sending, pool, 0); + ks_q_create(&bp->messages_receiving, pool, 0); *bpP = bp; return KS_STATUS_SUCCESS; } -KS_DECLARE(ks_dht_nodeid_t *) blade_peer_myid(blade_peer_t *bp) +KS_DECLARE(ks_status_t) blade_peer_startup(blade_peer_t *bp, kws_t *kws) { ks_assert(bp); - ks_assert(bp->dht); + ks_assert(kws); - return &bp->dht->nodeid; + // @todo: consider using a recycle queue for blade_peer_t in blade_service_t, just need to call startup then + + blade_peer_shutdown(bp); + + bp->kws = kws; + + if (ks_thread_create_ex(&bp->kws_thread, + blade_peer_kws_thread, + bp, + KS_THREAD_FLAG_DEFAULT, + KS_THREAD_DEFAULT_STACK, + KS_PRI_NORMAL, + bp->pool) != KS_STATUS_SUCCESS) return KS_STATUS_FAIL; + + return KS_STATUS_SUCCESS; } -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_shutdown(blade_peer_t *bp) { ks_assert(bp); - ks_dht_autoroute(bp->dht, autoroute, port); + bp->shutdown = KS_TRUE; + + if (bp->kws_thread) { + ks_thread_join(bp->kws_thread); + ks_pool_free(bp->pool, &bp->kws_thread); + } + + if (bp->kws) kws_destroy(&bp->kws); + + bp->shutdown = KS_FALSE; + return KS_STATUS_SUCCESS; } -KS_DECLARE(ks_status_t) blade_peer_bind(blade_peer_t *bp, const ks_sockaddr_t *addr, ks_dht_endpoint_t **endpoint) +void *blade_peer_kws_thread(ks_thread_t *thread, void *data) { - ks_assert(bp); - ks_assert(addr); + blade_peer_t *peer; + kws_opcode_t opcode; + uint8_t *data; + ks_size_t data_len; + blade_message_t *message; - return ks_dht_bind(bp->dht, addr, endpoint); + ks_assert(thread); + ks_assert(data); + + peer = (blade_peer_t *)data; + + while (!peer->shutdown) { + // @todo use nonblocking kws mode so that if no data at all is available yet we can still do other things such as sending messages before trying again + // or easier alternative, just use ks_poll (or select) to check if there is a POLLIN event pending, but this requires direct access to the socket, or + // kws can be updated to add a function to poll the inner socket for events (IE, kws_poll(kws, &inbool, NULL, &errbool, timeout)) + data_len = kws_read_frame(peer->kws, &opcode, &data); + + if (data_len <= 0) { + // @todo error handling, 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 + + // @todo: this way of disconnecting would have the service periodically check the list of connected peers for those that are disconnecting, + // remove them from the connected peer list, and then call peer destroy which will wait for this thread to rejoin which it already will have, + // and then destroy the inner kws and finish any cleanup of the actual socket if neccessary, and can still call an ondisconnected callback + // at the service level + peer->disconnecting = KS_TRUE; + break; + } + + // @todo this will check the discarded queue first and realloc if there is not enough space, otherwise allocate a message, and finally copy the data + if (blade_handle_message_claim(peer->service->handle, &message, data, data_len) != KS_STATUS_SUCCESS || !message) { + // @todo error handling + // just drop the peer for now, the only failure scenarios are asserted OOM, or if the discard queue pop fails + peer->disconnecting = KS_TRUE; + break; + } + + ks_q_push(peer->messages_receiving, message); + // @todo callback up the stack to indicate a message has been received and can be popped (more efficient than constantly polling by popping)? + + + if (ks_q_trypop(peer->messages_sending, &message) == KS_STATUS_SUCCESS) { + } + } + + return NULL; } - -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 diff --git a/libs/libblade/src/blade_service.c b/libs/libblade/src/blade_service.c new file mode 100644 index 0000000000..e1361bf9df --- /dev/null +++ b/libs/libblade/src/blade_service.c @@ -0,0 +1,401 @@ +/* + * 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" + +typedef enum { + BS_NONE = 0, + BS_MYPOOL = (1 << 0), + BS_MYTPOOL = (1 << 1) +} bspvt_flag_t; + +#define BLADE_SERVICE_WEBSOCKETS_ENDPOINTS_MULTIHOME_MAX 16 + +struct blade_service_s { + bspvt_flag_t flags; + ks_pool_t *pool; + ks_thread_pool_t *tpool; + + ks_sockaddr_t config_websockets_endpoints_ipv4[BLADE_SERVICE_WEBSOCKETS_ENDPOINTS_MULTIHOME_MAX]; + ks_sockaddr_t config_websockets_endpoints_ipv6[BLADE_SERVICE_WEBSOCKETS_ENDPOINTS_MULTIHOME_MAX]; + int32_t config_websockets_endpoints_ipv4_length; + int32_t config_websockets_endpoints_ipv6_length; + int32_t config_websockets_endpoints_backlog; + + ks_bool_t shutdown; + + struct pollfd *listeners_poll; + int32_t *listeners_families; + int32_t listeners_size; + int32_t listeners_length; + ks_thread_t *listeners_thread; + + list_t connected; +}; + + +void *blade_service_listeners_thread(ks_thread_t *thread, void *data); +ks_status_t blade_service_listen(blade_service_t *bs, ks_sockaddr_t *addr); + + +KS_DECLARE(ks_status_t) blade_service_destroy(blade_service_t **bsP) +{ + blade_service_t *bs = NULL; + bspvt_flag_t flags; + ks_pool_t *pool; + + ks_assert(bsP); + + bs = *bsP; + *bsP = NULL; + + ks_assert(bs); + + flags = bs->flags; + pool = bs->pool; + + blade_service_shutdown(bs); + + list_destroy(&bs->connected); + + if (bs->tpool && (flags & BS_MYTPOOL)) ks_thread_pool_destroy(&bs->tpool); + + ks_pool_free(bs->pool, &bs); + + if (pool && (flags & BS_MYPOOL)) ks_pool_close(&pool); + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_service_create(blade_service_t **bsP, ks_pool_t *pool, ks_thread_pool_t *tpool) +{ + bspvt_flag_t newflags = BS_NONE; + blade_service_t *bs = NULL; + + if (!pool) { + newflags |= BS_MYPOOL; + ks_pool_open(&pool); + ks_assert(pool); + } + if (!tpool) { + newflags |= BS_MYTPOOL; + ks_thread_pool_create(&tpool, BLADE_SERVICE_TPOOL_MIN, BLADE_SERVICE_TPOOL_MAX, BLADE_SERVICE_TPOOL_STACK, KS_PRI_NORMAL, BLADE_SERVICE_TPOOL_IDLE); + ks_assert(tpool); + } + + bs = ks_pool_alloc(pool, sizeof(*bs)); + bs->flags = newflags; + bs->pool = pool; + bs->tpool = tpool; + list_init(&bs->connected); + *bsP = bs; + + return KS_STATUS_SUCCESS; +} + +ks_status_t blade_service_config(blade_service_t *bs, config_setting_t *config) +{ + config_setting_t *websockets = NULL; + config_setting_t *websockets_endpoints = NULL; + config_setting_t *websockets_endpoints_ipv4 = NULL; + config_setting_t *websockets_endpoints_ipv6 = NULL; + config_setting_t *websockets_ssl = NULL; + config_setting_t *element; + config_setting_t *tmp1; + config_setting_t *tmp2; + ks_sockaddr_t config_websockets_endpoints_ipv4[BLADE_SERVICE_WEBSOCKETS_ENDPOINTS_MULTIHOME_MAX]; + ks_sockaddr_t config_websockets_endpoints_ipv6[BLADE_SERVICE_WEBSOCKETS_ENDPOINTS_MULTIHOME_MAX]; + int32_t config_websockets_endpoints_ipv4_length = 0; + int32_t config_websockets_endpoints_ipv6_length = 0; + int32_t config_websockets_endpoints_backlog = 8; + + ks_assert(bs); + + if (!config) return KS_STATUS_FAIL; + if (!config_setting_is_group(config)) return KS_STATUS_FAIL; + + websockets = config_setting_get_member(config, "websockets"); + if (!websockets) return KS_STATUS_FAIL; + websockets_endpoints = config_setting_get_member(config, "endpoints"); + if (!websockets_endpoints) return KS_STATUS_FAIL; + websockets_endpoints_ipv4 = config_lookup_from(websockets_endpoints, "ipv4"); + websockets_endpoints_ipv6 = config_lookup_from(websockets_endpoints, "ipv6"); + if (websockets_endpoints_ipv4) { + if (config_setting_type(websockets_endpoints_ipv4) != CONFIG_TYPE_LIST) return KS_STATUS_FAIL; + if ((config_websockets_endpoints_ipv4_length = config_setting_length(websockets_endpoints_ipv4)) > BLADE_SERVICE_WEBSOCKETS_ENDPOINTS_MULTIHOME_MAX) + return KS_STATUS_FAIL; + + for (int32_t index = 0; index < config_websockets_endpoints_ipv4_length; ++index) { + element = config_setting_get_elem(websockets_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_websockets_endpoints_ipv4[index], + config_setting_get_string(tmp1), + config_setting_get_int(tmp2), + AF_INET) != KS_STATUS_SUCCESS) return KS_STATUS_FAIL; + } + } + if (websockets_endpoints_ipv6) { + if (config_setting_type(websockets_endpoints_ipv6) != CONFIG_TYPE_LIST) return KS_STATUS_FAIL; + if ((config_websockets_endpoints_ipv6_length = config_setting_length(websockets_endpoints_ipv6)) > BLADE_SERVICE_WEBSOCKETS_ENDPOINTS_MULTIHOME_MAX) + return KS_STATUS_FAIL; + + for (int32_t index = 0; index < config_websockets_endpoints_ipv6_length; ++index) { + element = config_setting_get_elem(websockets_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_websockets_endpoints_ipv6[index], + config_setting_get_string(tmp1), + config_setting_get_int(tmp2), + AF_INET6) != KS_STATUS_SUCCESS) return KS_STATUS_FAIL; + } + } + if (config_websockets_endpoints_ipv4_length + config_websockets_endpoints_ipv6_length <= 0) return KS_STATUS_FAIL; + tmp1 = config_lookup_from(websockets_endpoints, "backlog"); + if (tmp1) { + if (config_setting_type(tmp1) != CONFIG_TYPE_INT) return KS_STATUS_FAIL; + config_websockets_endpoints_backlog = config_setting_get_int(tmp1); + } + websockets_ssl = config_setting_get_member(websockets, "ssl"); + if (websockets_ssl) { + // @todo: SSL stuffs from websockets_ssl into config_websockets_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 from the current config when reloading a new config + for (int32_t index = 0; index < config_websockets_endpoints_ipv4_length; ++index) + bs->config_websockets_endpoints_ipv4[index] = config_websockets_endpoints_ipv4[index]; + for (int32_t index = 0; index < config_websockets_endpoints_ipv6_length; ++index) + bs->config_websockets_endpoints_ipv6[index] = config_websockets_endpoints_ipv6[index]; + bs->config_websockets_endpoints_ipv4_length = config_websockets_endpoints_ipv4_length; + bs->config_websockets_endpoints_ipv6_length = config_websockets_endpoints_ipv6_length; + bs->config_websockets_endpoints_backlog = config_websockets_endpoints_backlog; + //bs->config_websockets_ssl = config_websockets_ssl; + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_service_startup(blade_service_t *bs, config_setting_t *config) +{ + ks_assert(bs); + + blade_service_shutdown(bs); + + // @todo: If the configuration is invalid, and this is a case of reloading a new config, then the service shutdown shouldn't occur + // but the service may use configuration that changes before we shutdown if it is read successfully, may require a config reader/writer mutex? + + if (blade_service_config(bs, config) != KS_STATUS_SUCCESS) return KS_STATUS_FAIL; + + for (int32_t index = 0; index < bs->config_websockets_endpoints_ipv4_length; ++index) { + if (blade_service_listen(bs, &bs->config_websockets_endpoints_ipv4[index]) != KS_STATUS_SUCCESS) return KS_STATUS_FAIL; + } + for (int32_t index = 0; index < bs->config_websockets_endpoints_ipv6_length; ++index) { + if (blade_service_listen(bs, &bs->config_websockets_endpoints_ipv6[index]) != KS_STATUS_SUCCESS) return KS_STATUS_FAIL; + } + + if (ks_thread_create_ex(&bs->listeners_thread, + blade_service_listeners_thread, + bs, + KS_THREAD_FLAG_DEFAULT, + KS_THREAD_DEFAULT_STACK, + KS_PRI_NORMAL, + bs->pool) != KS_STATUS_SUCCESS) return KS_STATUS_FAIL; + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_service_shutdown(blade_service_t *bs) +{ + ks_assert(bs); + + bs->shutdown = KS_TRUE; + + if (bs->listeners_thread) { + ks_thread_join(bs->listeners_thread); + ks_pool_free(bs->pool, &bs->listeners_thread); + } + + for (int32_t index = 0; index < bs->listeners_length; ++index) { + ks_socket_t sock = bs->listeners_poll[index].fd; + ks_socket_shutdown(sock, SHUT_RDWR); + ks_socket_close(&sock); + } + bs->listeners_length = 0; + + list_iterator_start(&bs->connected); + while (list_iterator_hasnext(&bs->connected)) { + blade_peer_t *peer = (blade_peer_t *)list_iterator_next(&bs->connected); + blade_peer_destroy(&peer); + } + list_iterator_stop(&bs->connected); + list_clear(&bs->connected); + + bs->shutdown = KS_FALSE; + return KS_STATUS_SUCCESS; +} + +ks_status_t blade_service_listen(blade_service_t *bs, 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(bs); + ks_assert(addr); + + if ((listener = socket(addr->family, SOCK_STREAM, IPPROTO_TCP)) == KS_SOCK_INVALID) { + ret = KS_STATUS_FAIL; + goto done; + } + + ks_socket_option(listener, SO_REUSEADDR, KS_TRUE); + ks_socket_option(listener, TCP_NODELAY, KS_TRUE); + // @todo make sure v6 does not automatically map to a v4 using socket option IPV6_V6ONLY? + + if (ks_addr_bind(listener, addr) != KS_STATUS_SUCCESS) { + ret = KS_STATUS_FAIL; + goto done; + } + + if (listen(listener, bs->config_websockets_endpoints_backlog) != 0) { + ret = KS_STATUS_FAIL; + goto done; + } + + listener_index = bs->listeners_length++; + if (bs->listeners_length > bs->listeners_size) { + bs->listeners_size = bs->listeners_length; + bs->listeners_poll = (struct pollfd *)ks_pool_resize(bs->pool, bs->listeners_poll, sizeof(struct pollfd) * bs->listeners_size); + ks_assert(bs->listeners_poll); + bs->listeners_families = (int32_t *)ks_pool_resize(bs->pool, bs->listeners_families, sizeof(int32_t) * bs->listeners_size); + ks_assert(bs->listeners_families); + } + bs->listeners_poll[listener_index].fd = listener; + bs->listeners_poll[listener_index].events = POLLIN | POLLERR; + bs->listeners_families[listener_index] = addr->family; + + 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_service_listeners_thread(ks_thread_t *thread, void *data) +{ + blade_service_t *service; + + ks_assert(thread); + ks_assert(data); + + service = (blade_service_t *)data; + + while (!service->shutdown) { + if (ks_poll(service->listeners_poll, service->listeners_length, 100) > 0) { + for (int32_t index = 0; index < service->listeners_length; ++index) { + ks_socket_t sock; + ks_sockaddr_t raddr; + socklen_t slen = 0; + kws_t *kws = NULL; + blade_peer_t *peer = NULL; + + if (!(service->listeners_poll[index].revents & POLLIN)) continue; + if (service->listeners_poll[index].revents & POLLERR) { + // @todo: error handling, just skip the listener for now + continue; + } + + if (service->listeners_families[index] == AF_INET) { + slen = sizeof(raddr.v.v4); + if ((sock = accept(service->listeners_poll[index].fd, (struct sockaddr *)&raddr.v.v4, &slen)) == KS_SOCK_INVALID) { + // @todo: error handling, just skip the socket for now + continue; + } + raddr.family = AF_INET; + } else { + slen = sizeof(raddr.v.v6); + if ((sock = accept(service->listeners_poll[index].fd, (struct sockaddr *)&raddr.v.v6, &slen)) == KS_SOCK_INVALID) { + // @todo: error handling, just skip the socket for now + continue; + } + raddr.family = AF_INET6; + } + + ks_addr_get_host(&raddr); + ks_addr_get_port(&raddr); + + // @todo: SSL init stuffs based on data from service->config_websockets_ssl + + if (kws_init(&kws, sock, NULL, NULL, KWS_BLOCK, service->pool) != KS_STATUS_SUCCESS) { + // @todo: error handling, just close and skip the socket for now + ks_socket_close(&sock); + continue; + } + + blade_peer_create(&peer, service->pool, service->tpool); + ks_assert(peer); + + // @todo: should probably assign kws before adding to list, in a separate call from startup because it starts the internal worker thread + + list_append(&service->connected, peer); + + blade_peer_startup(peer, kws); + } + } + } + + return NULL; +} + +/* 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..5f7c8a7d73 100644 --- a/libs/libblade/src/blade_stack.c +++ b/libs/libblade/src/blade_stack.c @@ -43,7 +43,12 @@ 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_datastore; + config_setting_t *config_directory; + + //blade_peer_t *peer; + blade_directory_t *directory; blade_datastore_t *datastore; }; @@ -64,9 +69,9 @@ 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_peer_destroy(&bh->peer); + blade_handle_shutdown(bh); + + //blade_peer_destroy(&bh->peer); if (bh->tpool && (flags & BH_MYTPOOL)) ks_thread_pool_destroy(&bh->tpool); ks_pool_free(bh->pool, &bh); @@ -78,14 +83,12 @@ 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_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_dht_nodeid_t nid; - ks_assert(nodeid); - ks_assert(strlen(nodeid) == (KS_DHT_NODEID_SIZE * 2)); + ks_assert(bhP); if (!pool) { newflags |= BH_MYPOOL; @@ -101,75 +104,88 @@ KS_DECLARE(ks_status_t) blade_handle_create(blade_handle_t **bhP, ks_pool_t *poo 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); + //blade_peer_create(&bh->peer, bh->pool, bh->tpool); *bhP = bh; return KS_STATUS_SUCCESS; } -KS_DECLARE(void) blade_handle_myid(blade_handle_t *bh, char *buffer) +ks_status_t blade_handle_config(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); -} - -KS_DECLARE(void) blade_handle_autoroute(blade_handle_t *bh, ks_bool_t autoroute, ks_port_t port) -{ - ks_assert(bh); - ks_assert(bh->peer); - - blade_peer_autoroute(bh->peer, autoroute, 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_sockaddr_t addr; - int family = AF_INET; + config_setting_t *datastore = NULL; + config_setting_t *directory = NULL; ks_assert(bh); - ks_assert(ip); - ks_assert(port); - if (ip[1] != '.' && ip[2] != '.' && ip[3] != '.') family = AF_INET6; + if (!config) return KS_STATUS_FAIL; + if (!config_setting_is_group(config)) return KS_STATUS_FAIL; + + datastore = config_setting_get_member(config, "datastore"); + //if (datastore && !config_setting_is_group(datastore)) return KS_STATUS_FAIL; - ks_addr_set(&addr, ip, port, family); - return blade_peer_bind(bh->peer, &addr, endpoint); + directory = config_setting_get_member(config, "directory"); + //if (directory && !config_setting_is_group(directory)) return KS_STATUS_FAIL; + + bh->config_datastore = datastore; + bh->config_directory = directory; + + return KS_STATUS_SUCCESS; } -KS_DECLARE(void) blade_handle_pulse(blade_handle_t *bh, int32_t timeout) -{ - ks_assert(bh); - ks_assert(timeout >= 0); - - blade_peer_pulse(bh->peer, timeout); - if (bh->datastore) blade_datastore_pulse(bh->datastore, timeout); -} - - -KS_DECLARE(void) blade_handle_datastore_start(blade_handle_t *bh) +KS_DECLARE(ks_status_t) blade_handle_startup(blade_handle_t *bh, config_setting_t *config) { ks_assert(bh); - if (bh->datastore) return; + if (blade_handle_config(bh, config) != KS_STATUS_SUCCESS) return KS_STATUS_FAIL; + + if (bh->config_datastore && !blade_handle_datastore_available(bh)) { + blade_datastore_create(&bh->datastore, bh->pool, bh->tpool); + blade_datastore_startup(bh->datastore, bh->config_datastore); + } + + if (bh->config_directory && !blade_handle_directory_available(bh)) { + blade_directory_create(&bh->directory, bh->pool, bh->tpool); + blade_directory_startup(bh->directory, config); + } + + return KS_STATUS_SUCCESS; +} - blade_datastore_create(&bh->datastore, bh->pool); +KS_DECLARE(ks_status_t) blade_handle_shutdown(blade_handle_t *bh) +{ + ks_assert(bh); + + if (blade_handle_directory_available(bh)) blade_directory_destroy(&bh->directory); + + if (blade_handle_datastore_available(bh)) blade_datastore_destroy(&bh->datastore); + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_bool_t) blade_handle_datastore_available(blade_handle_t *bh) +{ + ks_assert(bh); + + return bh->datastore != NULL; +} + +KS_DECLARE(ks_bool_t) blade_handle_directory_available(blade_handle_t *bh) +{ + ks_assert(bh); + + return bh->directory != 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 +197,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/include/blade.h b/libs/libblade/src/include/blade.h index 4d44ea07fb..578d41fee0 100644 --- a/libs/libblade/src/include/blade.h +++ b/libs/libblade/src/include/blade.h @@ -36,11 +36,14 @@ #include #include #include +#include #include "unqlite.h" #include "blade_types.h" #include "blade_stack.h" #include "blade_peer.h" +#include "blade_service.h" #include "blade_datastore.h" +#include "blade_directory.h" #include "bpcp.h" KS_BEGIN_EXTERN_C diff --git a/libs/libblade/src/include/blade_datastore.h b/libs/libblade/src/include/blade_datastore.h index 62918a3a41..641eccbd55 100644 --- a/libs/libblade/src/include/blade_datastore.h +++ b/libs/libblade/src/include/blade_datastore.h @@ -35,10 +35,17 @@ #define _BLADE_DATASTORE_H_ #include +#define BLADE_DATASTORE_TPOOL_MIN 2 +#define BLADE_DATASTORE_TPOOL_MAX 8 +#define BLADE_DATASTORE_TPOOL_STACK (1024 * 256) +#define BLADE_DATASTORE_TPOOL_IDLE 10 + 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_directory.h b/libs/libblade/src/include/blade_directory.h new file mode 100644 index 0000000000..e0a66980f3 --- /dev/null +++ b/libs/libblade/src/include/blade_directory.h @@ -0,0 +1,61 @@ +/* + * 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. + */ + +#ifndef _BLADE_DIRECTORY_H_ +#define _BLADE_DIRECTORY_H_ +#include + +#define BLADE_DIRECTORY_TPOOL_MIN 2 +#define BLADE_DIRECTORY_TPOOL_MAX 8 +#define BLADE_DIRECTORY_TPOOL_STACK (1024 * 256) +#define BLADE_DIRECTORY_TPOOL_IDLE 10 + +KS_BEGIN_EXTERN_C +KS_DECLARE(ks_status_t) blade_directory_create(blade_directory_t **bdP, ks_pool_t *pool, ks_thread_pool_t *tpool); +KS_DECLARE(ks_status_t) blade_directory_destroy(blade_directory_t **bdP); +KS_DECLARE(ks_status_t) blade_directory_startup(blade_directory_t *bd, config_setting_t *config); +KS_DECLARE(ks_status_t) blade_directory_shutdown(blade_directory_t *bd); +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_peer.h b/libs/libblade/src/include/blade_peer.h index 3c0bb5925d..8a15044ac5 100644 --- a/libs/libblade/src/include/blade_peer.h +++ b/libs/libblade/src/include/blade_peer.h @@ -41,12 +41,10 @@ #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_create(blade_peer_t **bpP, ks_pool_t *pool, ks_thread_pool_t *tpool); 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_peer_startup(blade_peer_t *bp, kws_t *kws); +KS_DECLARE(ks_status_t) blade_peer_shutdown(blade_peer_t *bp); KS_END_EXTERN_C #endif diff --git a/libs/libblade/src/include/blade_service.h b/libs/libblade/src/include/blade_service.h new file mode 100644 index 0000000000..9ec014f277 --- /dev/null +++ b/libs/libblade/src/include/blade_service.h @@ -0,0 +1,61 @@ +/* + * 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. + */ + +#ifndef _BLADE_SERVICE_H_ +#define _BLADE_SERVICE_H_ +#include + +#define BLADE_SERVICE_TPOOL_MIN 2 +#define BLADE_SERVICE_TPOOL_MAX 8 +#define BLADE_SERVICE_TPOOL_STACK (1024 * 256) +#define BLADE_SERVICE_TPOOL_IDLE 10 + +KS_BEGIN_EXTERN_C +KS_DECLARE(ks_status_t) blade_service_create(blade_service_t **bsP, ks_pool_t *pool, ks_thread_pool_t *tpool); +KS_DECLARE(ks_status_t) blade_service_destroy(blade_service_t **bsP); +KS_DECLARE(ks_status_t) blade_service_startup(blade_service_t *bs, config_setting_t *config); +KS_DECLARE(ks_status_t) blade_service_shutdown(blade_service_t *bs); +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..1eb4229d72 100644 --- a/libs/libblade/src/include/blade_stack.h +++ b/libs/libblade/src/include/blade_stack.h @@ -42,12 +42,13 @@ 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_bool_t) blade_handle_datastore_available(blade_handle_t *bh); +KS_DECLARE(ks_bool_t) blade_handle_directory_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..0cb2ec8e3e 100644 --- a/libs/libblade/src/include/blade_types.h +++ b/libs/libblade/src/include/blade_types.h @@ -39,7 +39,9 @@ KS_BEGIN_EXTERN_C typedef struct blade_handle_s blade_handle_t; typedef struct blade_peer_s blade_peer_t; +typedef struct blade_service_s blade_service_t; typedef struct blade_datastore_s blade_datastore_t; +typedef struct blade_directory_s blade_directory_t; typedef ks_bool_t (*blade_datastore_fetch_callback_t)(blade_datastore_t *bds, const void *data, uint32_t data_length, void *userdata); diff --git a/libs/libblade/test/bladec.c b/libs/libblade/test/bladec.c index 478b614bc0..09e821edc2 100644 --- a/libs/libblade/test/bladec.c +++ b/libs/libblade/test/bladec.c @@ -28,16 +28,12 @@ struct command_def_s { 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); 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 }, @@ -48,19 +44,12 @@ static const struct command_def_s command_defs[] = { int main(int argc, char **argv) { blade_handle_t *bh = NULL; - const char *nodeid; - - ks_assert(argc >= 2); - - nodeid = argv[1]; ks_global_set_default_logger(KS_LOG_LEVEL_DEBUG); blade_init(); - blade_handle_create(&bh, NULL, NULL, nodeid); - - blade_handle_autoroute(bh, KS_TRUE, KS_DHT_DEFAULT_PORT); + blade_handle_create(&bh, NULL, NULL); loop(bh); @@ -121,7 +110,7 @@ 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); + blade_handle_pulse(bh); } } @@ -179,34 +168,6 @@ void command_quit(blade_handle_t *bh, char *args) g_shutdown = KS_TRUE; } -void command_myid(blade_handle_t *bh, char *args) -{ - char buf[KS_DHT_NODEID_SIZE * 2 + 1]; - - ks_assert(bh); - ks_assert(args); - - blade_handle_myid(bh, buf); - - ks_log(KS_LOG_INFO, "%s\n", buf); -} - -void command_bind(blade_handle_t *bh, char *args) -{ - char *ip = NULL; - char *port = NULL; - ks_port_t p; - - ks_assert(args); - - parse_argument(&args, &ip, ' '); - parse_argument(&args, &port, ' '); - - p = atoi(port); // @todo use strtol for error handling - - blade_handle_bind(bh, ip, p, NULL); -} - void command_store(blade_handle_t *bh, char *args) { char *key; @@ -214,7 +175,7 @@ void command_store(blade_handle_t *bh, char *args) ks_assert(args); - blade_handle_datastore_start(bh); + blade_handle_datastore_startup(bh, NULL); parse_argument(&args, &key, ' '); parse_argument(&args, &data, ' '); @@ -234,7 +195,7 @@ void command_fetch(blade_handle_t *bh, char *args) ks_assert(args); - blade_handle_datastore_start(bh); + blade_handle_datastore_startup(bh, NULL); parse_argument(&args, &key, ' '); From 80179e7bd02edebb3750b2b2d38cd0b83b6e293d Mon Sep 17 00:00:00 2001 From: Shane Bryldt Date: Mon, 30 Jan 2017 05:02:58 +0000 Subject: [PATCH 06/34] FS-9952: More work on the blade service transport layer, now compiles but is untested, and still missing a few pieces to be functional --- libs/libblade/Makefile.am | 5 +- libs/libblade/src/blade_directory.c | 165 ------------------ libs/libblade/src/blade_message.c | 129 ++++++++++++++ libs/libblade/src/blade_peer.c | 140 +++++++++++---- libs/libblade/src/blade_service.c | 74 ++++---- libs/libblade/src/blade_stack.c | 100 ++++++++--- libs/libblade/src/include/blade.h | 2 +- .../{blade_directory.h => blade_message.h} | 18 +- libs/libblade/src/include/blade_peer.h | 12 +- libs/libblade/src/include/blade_service.h | 3 +- libs/libblade/src/include/blade_stack.h | 7 +- libs/libblade/src/include/blade_types.h | 2 +- 12 files changed, 373 insertions(+), 284 deletions(-) delete mode 100644 libs/libblade/src/blade_directory.c create mode 100644 libs/libblade/src/blade_message.c rename libs/libblade/src/include/{blade_directory.h => blade_message.h} (76%) diff --git a/libs/libblade/Makefile.am b/libs/libblade/Makefile.am index f55b1266bb..ee5e5677d0 100644 --- a/libs/libblade/Makefile.am +++ b/libs/libblade/Makefile.am @@ -11,13 +11,14 @@ 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/blade_service.c src/bpcp.c src/blade_datastore.c src/blade_directory.c +libblade_la_SOURCES = src/blade.c src/blade_stack.c src/blade_peer.c src/blade_service.c src/bpcp.c src/blade_datastore.c +libblade_la_SOURCES += src/blade_message.c libblade_la_CFLAGS = $(AM_CFLAGS) $(AM_CPPFLAGS) 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/blade_service.h -library_include_HEADERS += src/include/bpcp.h src/include/blade_datastore.h src/include/blade_directory.h +library_include_HEADERS += src/include/bpcp.h src/include/blade_datastore.h src/include/blade_message.h library_include_HEADERS += src/include/unqlite.h test/tap.h tests: libblade.la diff --git a/libs/libblade/src/blade_directory.c b/libs/libblade/src/blade_directory.c deleted file mode 100644 index ab24942dfe..0000000000 --- a/libs/libblade/src/blade_directory.c +++ /dev/null @@ -1,165 +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" - - -typedef enum { - BD_NONE = 0, - BD_MYPOOL = (1 << 0), - BD_MYTPOOL = (1 << 1), -} bdpvt_flag_t; - -struct blade_directory_s { - bdpvt_flag_t flags; - ks_pool_t *pool; - ks_thread_pool_t *tpool; - - config_setting_t *config_service; - - blade_service_t *service; -}; - - - - -KS_DECLARE(ks_status_t) blade_directory_destroy(blade_directory_t **bdP) -{ - blade_directory_t *bd = NULL; - bdpvt_flag_t flags; - ks_pool_t *pool; - - ks_assert(bdP); - - bd = *bdP; - *bdP = NULL; - - ks_assert(bd); - - flags = bd->flags; - pool = bd->pool; - - blade_directory_shutdown(bd); - - if (bd->tpool && (flags & BD_MYTPOOL)) ks_thread_pool_destroy(&bd->tpool); - - ks_pool_free(bd->pool, &bd); - - if (pool && (flags & BD_MYPOOL)) ks_pool_close(&pool); - - return KS_STATUS_SUCCESS; -} - -KS_DECLARE(ks_status_t) blade_directory_create(blade_directory_t **bdP, ks_pool_t *pool, ks_thread_pool_t *tpool) -{ - bdpvt_flag_t newflags = BD_NONE; - blade_directory_t *bd = NULL; - - if (!pool) { - newflags |= BD_MYPOOL; - ks_pool_open(&pool); - ks_assert(pool); - } - // @todo: move thread pool creation to startup which allows thread pool to be configurable - if (!tpool) { - newflags |= BD_MYTPOOL; - ks_thread_pool_create(&tpool, - BLADE_DIRECTORY_TPOOL_MIN, - BLADE_DIRECTORY_TPOOL_MAX, - BLADE_DIRECTORY_TPOOL_STACK, - KS_PRI_NORMAL, - BLADE_DIRECTORY_TPOOL_IDLE); - ks_assert(tpool); - } - - bd = ks_pool_alloc(pool, sizeof(*bd)); - bd->flags = newflags; - bd->pool = pool; - bd->tpool = tpool; - *bdP = bd; - - return KS_STATUS_SUCCESS; -} - -ks_status_t blade_directory_config(blade_directory_t *bd, config_setting_t *config) -{ - config_setting_t *service = NULL; - - ks_assert(bd); - - if (!config) return KS_STATUS_FAIL; - if (!config_setting_is_group(config)) return KS_STATUS_FAIL; - - service = config_setting_get_member(config, "service"); - if (!service) return KS_STATUS_FAIL; - - bd->config_service = service; - - return KS_STATUS_SUCCESS; -} - -KS_DECLARE(ks_status_t) blade_directory_startup(blade_directory_t *bd, config_setting_t *config) -{ - ks_assert(bd); - - blade_directory_shutdown(bd); - - if (blade_directory_config(bd, config) != KS_STATUS_SUCCESS) return KS_STATUS_FAIL; - - blade_service_create(&bd->service, bd->pool, bd->tpool); - ks_assert(bd->service); - if (blade_service_startup(bd->service, bd->config_service) != KS_STATUS_SUCCESS) return KS_STATUS_FAIL; - - return KS_STATUS_SUCCESS; -} - -KS_DECLARE(ks_status_t) blade_directory_shutdown(blade_directory_t *bd) -{ - ks_assert(bd); - - if (bd->service) blade_service_destroy(&bd->service); - - 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_message.c b/libs/libblade/src/blade_message.c new file mode 100644 index 0000000000..0bf401302f --- /dev/null +++ b/libs/libblade/src/blade_message.c @@ -0,0 +1,129 @@ +/* + * 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" + +struct blade_message_s { + ks_pool_t *pool; + blade_handle_t *handle; + + void *data; + ks_size_t data_length; + ks_size_t data_size; +}; + + +KS_DECLARE(ks_status_t) blade_message_destroy(blade_message_t **bmP) +{ + blade_message_t *bm = NULL; + + ks_assert(bmP); + + bm = *bmP; + *bmP = NULL; + + ks_assert(bm); + + if (bm->data) ks_pool_free(bm->pool, &bm->data); + + ks_pool_free(bm->pool, &bm); + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_message_create(blade_message_t **bmP, ks_pool_t *pool, blade_handle_t *handle) +{ + blade_message_t *bm = NULL; + + ks_assert(bmP); + ks_assert(pool); + ks_assert(handle); + + bm = ks_pool_alloc(pool, sizeof(*bm)); + bm->pool = pool; + bm->handle = handle; + *bmP = bm; + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_message_discard(blade_message_t **bm) +{ + ks_assert(bm); + ks_assert(*bm); + + return blade_handle_message_discard((*bm)->handle, bm); +} + +KS_DECLARE(ks_status_t) blade_message_set(blade_message_t *bm, void *data, ks_size_t data_length) +{ + ks_assert(bm); + ks_assert(data); + ks_assert(data_length > 0); + + // @todo fail on a max message size? + + if (data_length > bm->data_size) { + // @todo talk to tony about adding flags to ks_pool_resize_ex to prevent the memcpy, don't need to copy old memory here + // otherwise switch to a new allocation instead of resizing + bm->data = ks_pool_resize(bm->pool, bm->data, data_length); + ks_assert(bm->data); + bm->data_size = data_length; + } + memcpy(bm->data, data, data_length); + bm->data_length = data_length; + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_message_get(blade_message_t *bm, void **data, ks_size_t *data_length) +{ + ks_assert(bm); + + *data = bm->data; + *data_length = bm->data_length; + + 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_peer.c b/libs/libblade/src/blade_peer.c index 2e794a629b..942841ce64 100644 --- a/libs/libblade/src/blade_peer.c +++ b/libs/libblade/src/blade_peer.c @@ -45,7 +45,9 @@ struct blade_peer_s { ks_thread_pool_t *tpool; blade_service_t *service; + ks_socket_t sock; ks_bool_t shutdown; + ks_bool_t disconnecting; kws_t *kws; ks_thread_t *kws_thread; @@ -118,8 +120,10 @@ KS_DECLARE(ks_status_t) blade_peer_create(blade_peer_t **bpP, ks_pool_t *pool, k return KS_STATUS_SUCCESS; } -KS_DECLARE(ks_status_t) blade_peer_startup(blade_peer_t *bp, kws_t *kws) +KS_DECLARE(ks_status_t) blade_peer_startup(blade_peer_t *bp, ks_socket_t sock) { + kws_t *kws = NULL; + ks_assert(bp); ks_assert(kws); @@ -127,7 +131,7 @@ KS_DECLARE(ks_status_t) blade_peer_startup(blade_peer_t *bp, kws_t *kws) blade_peer_shutdown(bp); - bp->kws = kws; + bp->sock = sock; if (ks_thread_create_ex(&bp->kws_thread, blade_peer_kws_thread, @@ -142,6 +146,8 @@ KS_DECLARE(ks_status_t) blade_peer_startup(blade_peer_t *bp, kws_t *kws) KS_DECLARE(ks_status_t) blade_peer_shutdown(blade_peer_t *bp) { + blade_message_t *message = NULL; + ks_assert(bp); bp->shutdown = KS_TRUE; @@ -150,61 +156,125 @@ KS_DECLARE(ks_status_t) blade_peer_shutdown(blade_peer_t *bp) ks_thread_join(bp->kws_thread); ks_pool_free(bp->pool, &bp->kws_thread); } + + while (ks_q_trypop(bp->messages_sending, (void **)&message) == KS_STATUS_SUCCESS && message) blade_message_discard(&message); + while (ks_q_trypop(bp->messages_receiving, (void **)&message) == KS_STATUS_SUCCESS && message) blade_message_discard(&message); if (bp->kws) kws_destroy(&bp->kws); + else if (bp->sock != KS_SOCK_INVALID) ks_socket_close(&bp->sock); + bp->sock = KS_SOCK_INVALID; bp->shutdown = KS_FALSE; return KS_STATUS_SUCCESS; } +KS_DECLARE(void) blade_peer_disconnect(blade_peer_t *bp) +{ + ks_assert(bp); + + bp->disconnecting = KS_TRUE; +} + +KS_DECLARE(ks_bool_t) blade_peer_disconnecting(blade_peer_t *bp) +{ + ks_assert(bp); + return bp->disconnecting; +} + +KS_DECLARE(ks_status_t) blade_peer_message_pop(blade_peer_t *peer, blade_message_t **message) +{ + ks_assert(peer); + ks_assert(message); + + *message = NULL; + return ks_q_trypop(peer->messages_receiving, (void **)message); +} + +KS_DECLARE(ks_status_t) blade_peer_message_push(blade_peer_t *peer, void *data, ks_size_t data_length) +{ + blade_message_t *message = NULL; + + ks_assert(peer); + ks_assert(data); + ks_assert(data_length > 0); + + if (blade_handle_message_claim(blade_service_handle(peer->service), &message, data, data_length) != KS_STATUS_SUCCESS || !message) { + // @todo error handling + // just drop the peer for now, the only failure scenarios are asserted OOM, or if the discard queue pop fails + peer->disconnecting = KS_TRUE; + return KS_STATUS_FAIL; + } + ks_q_push(peer->messages_sending, message); + return KS_STATUS_SUCCESS; +} + void *blade_peer_kws_thread(ks_thread_t *thread, void *data) { - blade_peer_t *peer; + blade_peer_t *peer = NULL; kws_opcode_t opcode; - uint8_t *data; - ks_size_t data_len; - blade_message_t *message; + uint8_t *frame_data = NULL; + ks_size_t frame_data_len = 0; + blade_message_t *message = NULL; + int32_t poll_flags = 0; ks_assert(thread); ks_assert(data); peer = (blade_peer_t *)data; + // @todo: SSL init stuffs based on data from peer->service->config_websockets_ssl to pass into kws_init + + if (kws_init(&peer->kws, peer->sock, NULL, NULL, KWS_BLOCK, peer->pool) != KS_STATUS_SUCCESS) { + peer->disconnecting = KS_TRUE; + return NULL; + } + while (!peer->shutdown) { - // @todo use nonblocking kws mode so that if no data at all is available yet we can still do other things such as sending messages before trying again - // or easier alternative, just use ks_poll (or select) to check if there is a POLLIN event pending, but this requires direct access to the socket, or - // kws can be updated to add a function to poll the inner socket for events (IE, kws_poll(kws, &inbool, NULL, &errbool, timeout)) - data_len = kws_read_frame(peer->kws, &opcode, &data); + // @todo get exact timeout from service config? + poll_flags = ks_wait_sock(peer->sock, 100, KS_POLL_READ | KS_POLL_ERROR); - if (data_len <= 0) { - // @todo error handling, 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 + if (poll_flags & KS_POLL_ERROR) { + // @todo switch this (and others below) to the enum for the state callback, called during the service connected peer cleanup + peer->disconnecting = KS_TRUE; + break; + } + + if (poll_flags & KS_POLL_READ) { + frame_data_len = kws_read_frame(peer->kws, &opcode, &frame_data); - // @todo: this way of disconnecting would have the service periodically check the list of connected peers for those that are disconnecting, - // remove them from the connected peer list, and then call peer destroy which will wait for this thread to rejoin which it already will have, - // and then destroy the inner kws and finish any cleanup of the actual socket if neccessary, and can still call an ondisconnected callback - // at the service level - peer->disconnecting = KS_TRUE; - break; - } + if (frame_data_len <= 0) { + // @todo error handling, 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 + + // @todo: this way of disconnecting would have the service periodically check the list of connected peers for those that are disconnecting, + // remove them from the connected peer list, and then call peer destroy which will wait for this thread to rejoin which it already will have, + // and then destroy the inner kws and finish any cleanup of the actual socket if neccessary, and can still call an ondisconnected callback + // at the service level + peer->disconnecting = KS_TRUE; + break; + } - // @todo this will check the discarded queue first and realloc if there is not enough space, otherwise allocate a message, and finally copy the data - if (blade_handle_message_claim(peer->service->handle, &message, data, data_len) != KS_STATUS_SUCCESS || !message) { - // @todo error handling - // just drop the peer for now, the only failure scenarios are asserted OOM, or if the discard queue pop fails - peer->disconnecting = KS_TRUE; - break; - } + if (blade_handle_message_claim(blade_service_handle(peer->service), &message, frame_data, frame_data_len) != KS_STATUS_SUCCESS || !message) { + // @todo error handling + // just drop the peer for now, the only failure scenarios are asserted OOM, or if the discard queue pop fails + peer->disconnecting = KS_TRUE; + break; + } - ks_q_push(peer->messages_receiving, message); - // @todo callback up the stack to indicate a message has been received and can be popped (more efficient than constantly polling by popping)? + ks_q_push(peer->messages_receiving, message); + // @todo callback up the stack to indicate a message has been received and can be popped (more efficient than constantly polling by popping)? + // might not perfectly fit the traditional state callback, but it could work if it only sends the state temporarily and does NOT actually change + // the internal state to receiving + } - - if (ks_q_trypop(peer->messages_sending, &message) == KS_STATUS_SUCCESS) { + // @todo consider only sending one message at a time and use shorter polling timeout to prevent any considerable blocking if send buffers get full + while (ks_q_trypop(peer->messages_sending, (void **)&message) == KS_STATUS_SUCCESS && message) { + blade_message_get(message, (void **)&frame_data, &frame_data_len); + kws_write_frame(peer->kws, WSOC_TEXT, frame_data, frame_data_len); } } diff --git a/libs/libblade/src/blade_service.c b/libs/libblade/src/blade_service.c index e1361bf9df..dc7dcadbf6 100644 --- a/libs/libblade/src/blade_service.c +++ b/libs/libblade/src/blade_service.c @@ -45,6 +45,7 @@ struct blade_service_s { bspvt_flag_t flags; ks_pool_t *pool; ks_thread_pool_t *tpool; + blade_handle_t *handle; ks_sockaddr_t config_websockets_endpoints_ipv4[BLADE_SERVICE_WEBSOCKETS_ENDPOINTS_MULTIHOME_MAX]; ks_sockaddr_t config_websockets_endpoints_ipv6[BLADE_SERVICE_WEBSOCKETS_ENDPOINTS_MULTIHOME_MAX]; @@ -97,11 +98,14 @@ KS_DECLARE(ks_status_t) blade_service_destroy(blade_service_t **bsP) return KS_STATUS_SUCCESS; } -KS_DECLARE(ks_status_t) blade_service_create(blade_service_t **bsP, ks_pool_t *pool, ks_thread_pool_t *tpool) +KS_DECLARE(ks_status_t) blade_service_create(blade_service_t **bsP, ks_pool_t *pool, ks_thread_pool_t *tpool, blade_handle_t *handle) { bspvt_flag_t newflags = BS_NONE; blade_service_t *bs = NULL; + ks_assert(bsP); + ks_assert(handle); + if (!pool) { newflags |= BS_MYPOOL; ks_pool_open(&pool); @@ -117,12 +121,19 @@ KS_DECLARE(ks_status_t) blade_service_create(blade_service_t **bsP, ks_pool_t *p bs->flags = newflags; bs->pool = pool; bs->tpool = tpool; + bs->handle = handle; list_init(&bs->connected); *bsP = bs; return KS_STATUS_SUCCESS; } +KS_DECLARE(blade_handle_t *) blade_service_handle(blade_service_t *bs) +{ + ks_assert(bs); + return bs->handle; +} + ks_status_t blade_service_config(blade_service_t *bs, config_setting_t *config) { config_setting_t *websockets = NULL; @@ -325,7 +336,7 @@ ks_status_t blade_service_listen(blade_service_t *bs, ks_sockaddr_t *addr) void *blade_service_listeners_thread(ks_thread_t *thread, void *data) { - blade_service_t *service; + blade_service_t *service = NULL; ks_assert(thread); ks_assert(data); @@ -333,57 +344,50 @@ void *blade_service_listeners_thread(ks_thread_t *thread, void *data) service = (blade_service_t *)data; while (!service->shutdown) { + // @todo take exact timeout from a setting in config_service_endpoints if (ks_poll(service->listeners_poll, service->listeners_length, 100) > 0) { for (int32_t index = 0; index < service->listeners_length; ++index) { - ks_socket_t sock; - ks_sockaddr_t raddr; - socklen_t slen = 0; - kws_t *kws = NULL; + ks_socket_t sock = KS_SOCK_INVALID; blade_peer_t *peer = NULL; if (!(service->listeners_poll[index].revents & POLLIN)) continue; if (service->listeners_poll[index].revents & POLLERR) { - // @todo: error handling, just skip the listener for now + // @todo: error handling, just skip the listener for now, it might recover, could skip X sanity times before closing? continue; } - if (service->listeners_families[index] == AF_INET) { - slen = sizeof(raddr.v.v4); - if ((sock = accept(service->listeners_poll[index].fd, (struct sockaddr *)&raddr.v.v4, &slen)) == KS_SOCK_INVALID) { - // @todo: error handling, just skip the socket for now - continue; - } - raddr.family = AF_INET; - } else { - slen = sizeof(raddr.v.v6); - if ((sock = accept(service->listeners_poll[index].fd, (struct sockaddr *)&raddr.v.v6, &slen)) == KS_SOCK_INVALID) { - // @todo: error handling, just skip the socket for now - continue; - } - raddr.family = AF_INET6; - } - - ks_addr_get_host(&raddr); - ks_addr_get_port(&raddr); - - // @todo: SSL init stuffs based on data from service->config_websockets_ssl - - if (kws_init(&kws, sock, NULL, NULL, KWS_BLOCK, service->pool) != KS_STATUS_SUCCESS) { - // @todo: error handling, just close and skip the socket for now - ks_socket_close(&sock); + if ((sock = accept(service->listeners_poll[index].fd, NULL, NULL)) == KS_SOCK_INVALID) { + // @todo: error handling, just skip the socket for now as most causes are because the remote side suddenly became unreachable continue; } - blade_peer_create(&peer, service->pool, service->tpool); + // @todo consider a recycle queue of peers per service, and only have to call startup when one is already available + // blade_service_peer_claim(service, &peer); + blade_peer_create(&peer, service->pool, service->tpool, service); ks_assert(peer); - // @todo: should probably assign kws before adding to list, in a separate call from startup because it starts the internal worker thread + // @todo call state callback with connecting enum state + + blade_peer_startup(peer, sock); list_append(&service->connected, peer); - - blade_peer_startup(peer, kws); } } + + list_iterator_start(&service->connected); + while (list_iterator_hasnext(&service->connected)) { + blade_peer_t *peer = (blade_peer_t *)list_iterator_next(&service->connected); + // @todo expose accessor for disconnecting, after changing it into the state callback enum + // ensure that every way kws_close might occur leads back to disconnecting = KS_TRUE for this to universally process disconnects + if (blade_peer_disconnecting(peer)) { + // @todo check if there is an iterator based remove function, or indexed iteration to use list_delete_at() + list_delete(&service->connected, peer); + // @todo call state callback with internal disconnecting enum state (stored in peer to hold specific reason for disconnecting) + // @todo switch to blade_peer_shutdown(&peer) and blade_peer_discard(&peer) after introducing recycling of peers + blade_peer_destroy(&peer); + } + } + list_iterator_stop(&service->connected); } return NULL; diff --git a/libs/libblade/src/blade_stack.c b/libs/libblade/src/blade_stack.c index 5f7c8a7d73..b262336bf9 100644 --- a/libs/libblade/src/blade_stack.c +++ b/libs/libblade/src/blade_stack.c @@ -44,11 +44,12 @@ struct blade_handle_s { ks_pool_t *pool; ks_thread_pool_t *tpool; + config_setting_t *config_service; config_setting_t *config_datastore; - config_setting_t *config_directory; - - //blade_peer_t *peer; - blade_directory_t *directory; + + ks_q_t *messages_discarded; + blade_service_t *service; + blade_datastore_t *datastore; }; @@ -70,8 +71,12 @@ KS_DECLARE(ks_status_t) blade_handle_destroy(blade_handle_t **bhP) pool = bh->pool; blade_handle_shutdown(bh); - - //blade_peer_destroy(&bh->peer); + + if (bh->messages_discarded) { + // @todo make sure messages are cleaned up + ks_q_destroy(&bh->messages_discarded); + } + if (bh->tpool && (flags & BH_MYTPOOL)) ks_thread_pool_destroy(&bh->tpool); ks_pool_free(bh->pool, &bh); @@ -104,7 +109,10 @@ KS_DECLARE(ks_status_t) blade_handle_create(blade_handle_t **bhP, ks_pool_t *poo bh->flags = newflags; bh->pool = pool; bh->tpool = tpool; - //blade_peer_create(&bh->peer, bh->pool, bh->tpool); + + // @todo check thresholds from config, for now just ensure it doesn't grow out of control, allow 100 discarded messages + ks_q_create(&bh->messages_discarded, bh->pool, 100); + ks_assert(bh->messages_discarded); *bhP = bh; @@ -113,22 +121,24 @@ KS_DECLARE(ks_status_t) blade_handle_create(blade_handle_t **bhP, ks_pool_t *poo ks_status_t blade_handle_config(blade_handle_t *bh, config_setting_t *config) { + config_setting_t *service = NULL; config_setting_t *datastore = NULL; - config_setting_t *directory = NULL; ks_assert(bh); if (!config) return KS_STATUS_FAIL; if (!config_setting_is_group(config)) return KS_STATUS_FAIL; + // @todo config for messages_discarded threshold (ie, message count, message memory, etc) + + service = config_setting_get_member(config, "service"); + datastore = config_setting_get_member(config, "datastore"); //if (datastore && !config_setting_is_group(datastore)) return KS_STATUS_FAIL; - - directory = config_setting_get_member(config, "directory"); - //if (directory && !config_setting_is_group(directory)) return KS_STATUS_FAIL; + + bh->config_service = service; bh->config_datastore = datastore; - bh->config_directory = directory; return KS_STATUS_SUCCESS; } @@ -138,15 +148,17 @@ KS_DECLARE(ks_status_t) blade_handle_startup(blade_handle_t *bh, config_setting_ ks_assert(bh); if (blade_handle_config(bh, config) != KS_STATUS_SUCCESS) return KS_STATUS_FAIL; + + if (bh->config_service && !blade_handle_service_available(bh)) { + blade_service_create(&bh->service, bh->pool, bh->tpool, bh); + ks_assert(bh->service); + if (blade_service_startup(bh->service, bh->config_service) != KS_STATUS_SUCCESS) return KS_STATUS_FAIL; + } if (bh->config_datastore && !blade_handle_datastore_available(bh)) { blade_datastore_create(&bh->datastore, bh->pool, bh->tpool); - blade_datastore_startup(bh->datastore, bh->config_datastore); - } - - if (bh->config_directory && !blade_handle_directory_available(bh)) { - blade_directory_create(&bh->directory, bh->pool, bh->tpool); - blade_directory_startup(bh->directory, config); + ks_assert(bh->datastore); + if (blade_datastore_startup(bh->datastore, bh->config_datastore) != KS_STATUS_SUCCESS) return KS_STATUS_FAIL; } return KS_STATUS_SUCCESS; @@ -155,14 +167,55 @@ KS_DECLARE(ks_status_t) blade_handle_startup(blade_handle_t *bh, config_setting_ KS_DECLARE(ks_status_t) blade_handle_shutdown(blade_handle_t *bh) { ks_assert(bh); - - if (blade_handle_directory_available(bh)) blade_directory_destroy(&bh->directory); + + if (blade_handle_service_available(bh)) blade_service_destroy(&bh->service); if (blade_handle_datastore_available(bh)) blade_datastore_destroy(&bh->datastore); return KS_STATUS_SUCCESS; } +KS_DECLARE(ks_status_t) blade_handle_message_claim(blade_handle_t *bh, blade_message_t **message, void *data, ks_size_t data_length) +{ + blade_message_t *msg = NULL; + + ks_assert(bh); + ks_assert(message); + ks_assert(data); + + *message = NULL; + + if (ks_q_trypop(bh->messages_discarded, (void **)&msg) != KS_STATUS_SUCCESS || !msg) blade_message_create(&msg, bh->pool, bh); + ks_assert(msg); + + if (blade_message_set(msg, data, data_length) != KS_STATUS_SUCCESS) return KS_STATUS_FAIL; + + *message = msg; + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_handle_message_discard(blade_handle_t *bh, blade_message_t **message) +{ + ks_assert(bh); + ks_assert(message); + ks_assert(*message); + + // @todo check thresholds for discarded messages, if the queue is full just destroy the message for now (currently 100 messages) + if (ks_q_push(bh->messages_discarded, *message) != KS_STATUS_SUCCESS) blade_message_destroy(message); + + *message = NULL; + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_bool_t) blade_handle_service_available(blade_handle_t *bh) +{ + ks_assert(bh); + + return bh->service != NULL; +} + KS_DECLARE(ks_bool_t) blade_handle_datastore_available(blade_handle_t *bh) { ks_assert(bh); @@ -170,13 +223,6 @@ KS_DECLARE(ks_bool_t) blade_handle_datastore_available(blade_handle_t *bh) return bh->datastore != NULL; } -KS_DECLARE(ks_bool_t) blade_handle_directory_available(blade_handle_t *bh) -{ - ks_assert(bh); - - return bh->directory != 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); diff --git a/libs/libblade/src/include/blade.h b/libs/libblade/src/include/blade.h index 578d41fee0..f8a3a4459f 100644 --- a/libs/libblade/src/include/blade.h +++ b/libs/libblade/src/include/blade.h @@ -42,8 +42,8 @@ #include "blade_stack.h" #include "blade_peer.h" #include "blade_service.h" +#include "blade_message.h" #include "blade_datastore.h" -#include "blade_directory.h" #include "bpcp.h" KS_BEGIN_EXTERN_C diff --git a/libs/libblade/src/include/blade_directory.h b/libs/libblade/src/include/blade_message.h similarity index 76% rename from libs/libblade/src/include/blade_directory.h rename to libs/libblade/src/include/blade_message.h index e0a66980f3..5db9c60c22 100644 --- a/libs/libblade/src/include/blade_directory.h +++ b/libs/libblade/src/include/blade_message.h @@ -31,20 +31,16 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef _BLADE_DIRECTORY_H_ -#define _BLADE_DIRECTORY_H_ +#ifndef _BLADE_MESSAGE_H_ +#define _BLADE_MESSAGE_H_ #include -#define BLADE_DIRECTORY_TPOOL_MIN 2 -#define BLADE_DIRECTORY_TPOOL_MAX 8 -#define BLADE_DIRECTORY_TPOOL_STACK (1024 * 256) -#define BLADE_DIRECTORY_TPOOL_IDLE 10 - KS_BEGIN_EXTERN_C -KS_DECLARE(ks_status_t) blade_directory_create(blade_directory_t **bdP, ks_pool_t *pool, ks_thread_pool_t *tpool); -KS_DECLARE(ks_status_t) blade_directory_destroy(blade_directory_t **bdP); -KS_DECLARE(ks_status_t) blade_directory_startup(blade_directory_t *bd, config_setting_t *config); -KS_DECLARE(ks_status_t) blade_directory_shutdown(blade_directory_t *bd); +KS_DECLARE(ks_status_t) blade_message_create(blade_message_t **bmP, ks_pool_t *pool, blade_handle_t *handle); +KS_DECLARE(ks_status_t) blade_message_destroy(blade_message_t **bmP); +KS_DECLARE(ks_status_t) blade_message_set(blade_message_t *bm, void *data, ks_size_t data_length); +KS_DECLARE(ks_status_t) blade_message_get(blade_message_t *bm, void **data, ks_size_t *data_length); +KS_DECLARE(ks_status_t) blade_message_discard(blade_message_t **bm); KS_END_EXTERN_C #endif diff --git a/libs/libblade/src/include/blade_peer.h b/libs/libblade/src/include/blade_peer.h index 8a15044ac5..acffc37998 100644 --- a/libs/libblade/src/include/blade_peer.h +++ b/libs/libblade/src/include/blade_peer.h @@ -31,8 +31,8 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef _BPCP_H_ -#define _BPCP_H_ +#ifndef _BLADE_PEER_H_ +#define _BLADE_PEER_H_ #include #define BLADE_PEER_TPOOL_MIN 2 @@ -41,10 +41,14 @@ #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_DECLARE(ks_status_t) blade_peer_create(blade_peer_t **bpP, ks_pool_t *pool, ks_thread_pool_t *tpool, blade_service_t *service); KS_DECLARE(ks_status_t) blade_peer_destroy(blade_peer_t **bpP); -KS_DECLARE(ks_status_t) blade_peer_startup(blade_peer_t *bp, kws_t *kws); +KS_DECLARE(ks_status_t) blade_peer_startup(blade_peer_t *bp, ks_socket_t sock); KS_DECLARE(ks_status_t) blade_peer_shutdown(blade_peer_t *bp); +KS_DECLARE(void) blade_peer_disconnect(blade_peer_t *bp); +KS_DECLARE(ks_bool_t) blade_peer_disconnecting(blade_peer_t *bp); +KS_DECLARE(ks_status_t) blade_peer_message_pop(blade_peer_t *peer, blade_message_t **message); +KS_DECLARE(ks_status_t) blade_peer_message_push(blade_peer_t *peer, void *data, ks_size_t data_length); KS_END_EXTERN_C #endif diff --git a/libs/libblade/src/include/blade_service.h b/libs/libblade/src/include/blade_service.h index 9ec014f277..d6f1de40b5 100644 --- a/libs/libblade/src/include/blade_service.h +++ b/libs/libblade/src/include/blade_service.h @@ -41,8 +41,9 @@ #define BLADE_SERVICE_TPOOL_IDLE 10 KS_BEGIN_EXTERN_C -KS_DECLARE(ks_status_t) blade_service_create(blade_service_t **bsP, ks_pool_t *pool, ks_thread_pool_t *tpool); +KS_DECLARE(ks_status_t) blade_service_create(blade_service_t **bsP, ks_pool_t *pool, ks_thread_pool_t *tpool, blade_handle_t *handle); KS_DECLARE(ks_status_t) blade_service_destroy(blade_service_t **bsP); +KS_DECLARE(blade_handle_t *) blade_service_handle(blade_service_t *bs); KS_DECLARE(ks_status_t) blade_service_startup(blade_service_t *bs, config_setting_t *config); KS_DECLARE(ks_status_t) blade_service_shutdown(blade_service_t *bs); KS_END_EXTERN_C diff --git a/libs/libblade/src/include/blade_stack.h b/libs/libblade/src/include/blade_stack.h index 1eb4229d72..507d8a12f7 100644 --- a/libs/libblade/src/include/blade_stack.h +++ b/libs/libblade/src/include/blade_stack.h @@ -46,9 +46,12 @@ KS_DECLARE(ks_status_t) blade_handle_create(blade_handle_t **bhP, ks_pool_t *poo 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_bool_t) blade_handle_datastore_available(blade_handle_t *bh); -KS_DECLARE(ks_bool_t) blade_handle_directory_available(blade_handle_t *bh); +KS_DECLARE(ks_status_t) blade_handle_message_claim(blade_handle_t *bh, blade_message_t **message, void *data, ks_size_t data_length); +KS_DECLARE(ks_status_t) blade_handle_message_discard(blade_handle_t *bh, blade_message_t **message); +KS_DECLARE(ks_bool_t) blade_handle_service_available(blade_handle_t *bh); + +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 0cb2ec8e3e..e7484d0f38 100644 --- a/libs/libblade/src/include/blade_types.h +++ b/libs/libblade/src/include/blade_types.h @@ -40,8 +40,8 @@ KS_BEGIN_EXTERN_C typedef struct blade_handle_s blade_handle_t; typedef struct blade_peer_s blade_peer_t; typedef struct blade_service_s blade_service_t; +typedef struct blade_message_s blade_message_t; typedef struct blade_datastore_s blade_datastore_t; -typedef struct blade_directory_s blade_directory_t; typedef ks_bool_t (*blade_datastore_fetch_callback_t)(blade_datastore_t *bds, const void *data, uint32_t data_length, void *userdata); From 4ec0fbc58170a0906c7c9b844331a45564bcbe71 Mon Sep 17 00:00:00 2001 From: Shane Bryldt Date: Mon, 30 Jan 2017 22:51:09 +0000 Subject: [PATCH 07/34] FS-9952: A few changes and implemented the initial service peer state callbacks --- libs/libblade/src/blade_datastore.c | 50 +---------- libs/libblade/src/blade_peer.c | 93 +++++++++------------ libs/libblade/src/blade_service.c | 63 ++++++-------- libs/libblade/src/blade_stack.c | 4 +- libs/libblade/src/include/blade_datastore.h | 5 -- libs/libblade/src/include/blade_peer.h | 10 +-- libs/libblade/src/include/blade_service.h | 12 +-- libs/libblade/src/include/blade_stack.h | 2 +- libs/libblade/src/include/blade_types.h | 14 ++++ 9 files changed, 94 insertions(+), 159 deletions(-) diff --git a/libs/libblade/src/blade_datastore.c b/libs/libblade/src/blade_datastore.c index 49c3cbddb6..9401a6e577 100644 --- a/libs/libblade/src/blade_datastore.c +++ b/libs/libblade/src/blade_datastore.c @@ -34,22 +34,13 @@ #include "blade.h" -typedef enum { - BDS_NONE = 0, - BDS_MYPOOL = (1 << 0), - BDS_MYTPOOL = (1 << 1), -} 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; - //config_setting_t *config_service; unqlite *db; - //blade_service_t *service; }; struct blade_datastore_fetch_userdata_s @@ -65,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); @@ -75,44 +64,22 @@ KS_DECLARE(ks_status_t) blade_datastore_destroy(blade_datastore_t **bdsP) ks_assert(bds); - flags = bds->flags; - pool = bds->pool; - blade_datastore_shutdown(bds); - if (bds->tpool && (flags & BDS_MYTPOOL)) ks_thread_pool_destroy(&bds->tpool); - 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_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); - } - // @todo: move thread pool creation to startup which allows thread pool to be configurable - if (!tpool) { - newflags |= BDS_MYTPOOL; - ks_thread_pool_create(&tpool, - BLADE_DATASTORE_TPOOL_MIN, - BLADE_DATASTORE_TPOOL_MAX, - BLADE_DATASTORE_TPOOL_STACK, - KS_PRI_NORMAL, - BLADE_DATASTORE_TPOOL_IDLE); - ks_assert(tpool); - } + 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; @@ -124,7 +91,6 @@ ks_status_t blade_datastore_config(blade_datastore_t *bds, config_setting_t *con { config_setting_t *tmp; config_setting_t *database = NULL; - //config_setting_t *service = NULL; const char *config_database_path = NULL; ks_assert(bds); @@ -138,11 +104,9 @@ ks_status_t blade_datastore_config(blade_datastore_t *bds, config_setting_t *con 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); - //service = config_setting_get_member(config, "service"); 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); - //bds->config_service = service; return KS_STATUS_SUCCESS; } @@ -155,7 +119,6 @@ KS_DECLARE(ks_status_t) blade_datastore_startup(blade_datastore_t *bds, config_s if (blade_datastore_config(bds, config) != KS_STATUS_SUCCESS) return KS_STATUS_FAIL; - //if (unqlite_open(&bds->db, NULL, UNQLITE_OPEN_IN_MEMORY) != UNQLITE_OK) { if (unqlite_open(&bds->db, bds->config_database_path, UNQLITE_OPEN_CREATE) != UNQLITE_OK) { const char *errbuf = NULL; blade_datastore_error(bds, &errbuf, NULL); @@ -167,10 +130,6 @@ KS_DECLARE(ks_status_t) blade_datastore_startup(blade_datastore_t *bds, config_s // @todo VM init if document store is used (and output consumer callback) - //blade_service_create(&bds->service, bds->pool, bds->tpool); - //ks_assert(bds->service); - //blade_service_startup(bds->service, bds->config_service); - return KS_STATUS_SUCCESS; } @@ -178,15 +137,12 @@ KS_DECLARE(ks_status_t) blade_datastore_shutdown(blade_datastore_t *bds) { ks_assert(bds); - //if (bds->service) blade_service_destroy(&bds->service); - if (bds->db) { unqlite_close(bds->db); bds->db = NULL; } if (bds->config_database_path) ks_pool_free(bds->pool, &bds->config_database_path); - //bds->config_service = NULL; return KS_STATUS_SUCCESS; } diff --git a/libs/libblade/src/blade_peer.c b/libs/libblade/src/blade_peer.c index 942841ce64..5ec3d6ba0d 100644 --- a/libs/libblade/src/blade_peer.c +++ b/libs/libblade/src/blade_peer.c @@ -33,21 +33,16 @@ #include "blade.h" -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; blade_service_t *service; ks_socket_t sock; ks_bool_t shutdown; - ks_bool_t disconnecting; + blade_peerstate_t state; + blade_peerreason_t reason; + kws_t *kws; ks_thread_t *kws_thread; @@ -62,8 +57,6 @@ void *blade_peer_kws_thread(ks_thread_t *thread, void *data); 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); @@ -72,47 +65,31 @@ KS_DECLARE(ks_status_t) blade_peer_destroy(blade_peer_t **bpP) ks_assert(bp); - flags = bp->flags; - pool = bp->pool; - blade_peer_shutdown(bp); ks_q_destroy(&bp->messages_sending); ks_q_destroy(&bp->messages_receiving); - 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, blade_service_t *service) { - bppvt_flag_t newflags = BP_NONE; blade_peer_t *bp = NULL; ks_assert(bpP); + ks_assert(pool); + ks_assert(tpool); ks_assert(service); - 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); - } - bp = ks_pool_alloc(pool, sizeof(*bp)); - bp->flags = newflags; bp->pool = pool; bp->tpool = tpool; bp->service = service; + bp->state = BLADE_PEERSTATE_CONNECTING; + bp->reason = BLADE_PEERREASON_NORMAL; ks_q_create(&bp->messages_sending, pool, 0); ks_q_create(&bp->messages_receiving, pool, 0); *bpP = bp; @@ -132,6 +109,8 @@ KS_DECLARE(ks_status_t) blade_peer_startup(blade_peer_t *bp, ks_socket_t sock) blade_peer_shutdown(bp); bp->sock = sock; + bp->state = BLADE_PEERSTATE_CONNECTING; + bp->reason = BLADE_PEERREASON_NORMAL; if (ks_thread_create_ex(&bp->kws_thread, blade_peer_kws_thread, @@ -139,7 +118,11 @@ KS_DECLARE(ks_status_t) blade_peer_startup(blade_peer_t *bp, ks_socket_t sock) KS_THREAD_FLAG_DEFAULT, KS_THREAD_DEFAULT_STACK, KS_PRI_NORMAL, - bp->pool) != KS_STATUS_SUCCESS) return KS_STATUS_FAIL; + bp->pool) != KS_STATUS_SUCCESS) { + // @todo error logging + blade_peer_disconnect(bp, BLADE_PEERREASON_ERROR); + return KS_STATUS_FAIL; + } return KS_STATUS_SUCCESS; } @@ -159,7 +142,7 @@ KS_DECLARE(ks_status_t) blade_peer_shutdown(blade_peer_t *bp) while (ks_q_trypop(bp->messages_sending, (void **)&message) == KS_STATUS_SUCCESS && message) blade_message_discard(&message); while (ks_q_trypop(bp->messages_receiving, (void **)&message) == KS_STATUS_SUCCESS && message) blade_message_discard(&message); - + if (bp->kws) kws_destroy(&bp->kws); else if (bp->sock != KS_SOCK_INVALID) ks_socket_close(&bp->sock); bp->sock = KS_SOCK_INVALID; @@ -168,17 +151,19 @@ KS_DECLARE(ks_status_t) blade_peer_shutdown(blade_peer_t *bp) return KS_STATUS_SUCCESS; } -KS_DECLARE(void) blade_peer_disconnect(blade_peer_t *bp) +KS_DECLARE(void) blade_peer_disconnect(blade_peer_t *bp, blade_peerreason_t reason) { ks_assert(bp); - bp->disconnecting = KS_TRUE; + // @todo check if already disconnecting for another reason, avoid resetting to get initial reason for disconnect? + bp->reason = reason; + bp->state = BLADE_PEERSTATE_DISCONNECTING; } -KS_DECLARE(ks_bool_t) blade_peer_disconnecting(blade_peer_t *bp) +KS_DECLARE(blade_peerstate_t) blade_peer_state(blade_peer_t *bp) { ks_assert(bp); - return bp->disconnecting; + return bp->state; } KS_DECLARE(ks_status_t) blade_peer_message_pop(blade_peer_t *peer, blade_message_t **message) @@ -199,9 +184,8 @@ KS_DECLARE(ks_status_t) blade_peer_message_push(blade_peer_t *peer, void *data, ks_assert(data_length > 0); if (blade_handle_message_claim(blade_service_handle(peer->service), &message, data, data_length) != KS_STATUS_SUCCESS || !message) { - // @todo error handling - // just drop the peer for now, the only failure scenarios are asserted OOM, or if the discard queue pop fails - peer->disconnecting = KS_TRUE; + // @todo error logging + blade_peer_disconnect(peer, BLADE_PEERREASON_ERROR); return KS_STATUS_FAIL; } ks_q_push(peer->messages_sending, message); @@ -222,20 +206,27 @@ void *blade_peer_kws_thread(ks_thread_t *thread, void *data) peer = (blade_peer_t *)data; + // @todo consider using an INITIALIZING state to track when there is problems during initialization (specifically SSL negotiations)? + // @todo should stack be notified with an internal event callback here before logic layer initialization starts (IE, before SSL negotiations)? + peer->state = BLADE_PEERSTATE_RUNNING; + // @todo: SSL init stuffs based on data from peer->service->config_websockets_ssl to pass into kws_init if (kws_init(&peer->kws, peer->sock, NULL, NULL, KWS_BLOCK, peer->pool) != KS_STATUS_SUCCESS) { - peer->disconnecting = KS_TRUE; + // @todo error logging + blade_peer_disconnect(peer, BLADE_PEERREASON_ERROR); return NULL; } + + blade_service_peer_state_callback(peer->service, peer, BLADE_PEERSTATE_RUNNING); while (!peer->shutdown) { // @todo get exact timeout from service config? poll_flags = ks_wait_sock(peer->sock, 100, KS_POLL_READ | KS_POLL_ERROR); if (poll_flags & KS_POLL_ERROR) { - // @todo switch this (and others below) to the enum for the state callback, called during the service connected peer cleanup - peer->disconnecting = KS_TRUE; + // @todo error logging + blade_peer_disconnect(peer, BLADE_PEERREASON_ERROR); break; } @@ -243,37 +234,31 @@ void *blade_peer_kws_thread(ks_thread_t *thread, void *data) frame_data_len = kws_read_frame(peer->kws, &opcode, &frame_data); if (frame_data_len <= 0) { - // @todo error handling, strerror(ks_errno()) + // @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 - // @todo: this way of disconnecting would have the service periodically check the list of connected peers for those that are disconnecting, - // remove them from the connected peer list, and then call peer destroy which will wait for this thread to rejoin which it already will have, - // and then destroy the inner kws and finish any cleanup of the actual socket if neccessary, and can still call an ondisconnected callback - // at the service level - peer->disconnecting = KS_TRUE; + blade_peer_disconnect(peer, BLADE_PEERREASON_ERROR); break; } if (blade_handle_message_claim(blade_service_handle(peer->service), &message, frame_data, frame_data_len) != KS_STATUS_SUCCESS || !message) { - // @todo error handling - // just drop the peer for now, the only failure scenarios are asserted OOM, or if the discard queue pop fails - peer->disconnecting = KS_TRUE; + // @todo error logging + blade_peer_disconnect(peer, BLADE_PEERREASON_ERROR); break; } ks_q_push(peer->messages_receiving, message); - // @todo callback up the stack to indicate a message has been received and can be popped (more efficient than constantly polling by popping)? - // might not perfectly fit the traditional state callback, but it could work if it only sends the state temporarily and does NOT actually change - // the internal state to receiving + blade_service_peer_state_callback(peer->service, peer, BLADE_PEERSTATE_RECEIVING); } // @todo consider only sending one message at a time and use shorter polling timeout to prevent any considerable blocking if send buffers get full while (ks_q_trypop(peer->messages_sending, (void **)&message) == KS_STATUS_SUCCESS && message) { blade_message_get(message, (void **)&frame_data, &frame_data_len); + // @todo may need to get the WSOC_TEXT from the message if using WSOC_BINARY is desired later kws_write_frame(peer->kws, WSOC_TEXT, frame_data, frame_data_len); } } diff --git a/libs/libblade/src/blade_service.c b/libs/libblade/src/blade_service.c index dc7dcadbf6..b52e7d06a9 100644 --- a/libs/libblade/src/blade_service.c +++ b/libs/libblade/src/blade_service.c @@ -33,19 +33,13 @@ #include "blade.h" -typedef enum { - BS_NONE = 0, - BS_MYPOOL = (1 << 0), - BS_MYTPOOL = (1 << 1) -} bspvt_flag_t; - #define BLADE_SERVICE_WEBSOCKETS_ENDPOINTS_MULTIHOME_MAX 16 struct blade_service_s { - bspvt_flag_t flags; ks_pool_t *pool; ks_thread_pool_t *tpool; blade_handle_t *handle; + blade_service_peer_state_callback_t peer_state_callback; ks_sockaddr_t config_websockets_endpoints_ipv4[BLADE_SERVICE_WEBSOCKETS_ENDPOINTS_MULTIHOME_MAX]; ks_sockaddr_t config_websockets_endpoints_ipv6[BLADE_SERVICE_WEBSOCKETS_ENDPOINTS_MULTIHOME_MAX]; @@ -56,7 +50,6 @@ struct blade_service_s { ks_bool_t shutdown; struct pollfd *listeners_poll; - int32_t *listeners_families; int32_t listeners_size; int32_t listeners_length; ks_thread_t *listeners_thread; @@ -72,8 +65,6 @@ ks_status_t blade_service_listen(blade_service_t *bs, ks_sockaddr_t *addr); KS_DECLARE(ks_status_t) blade_service_destroy(blade_service_t **bsP) { blade_service_t *bs = NULL; - bspvt_flag_t flags; - ks_pool_t *pool; ks_assert(bsP); @@ -82,46 +73,33 @@ KS_DECLARE(ks_status_t) blade_service_destroy(blade_service_t **bsP) ks_assert(bs); - flags = bs->flags; - pool = bs->pool; - blade_service_shutdown(bs); list_destroy(&bs->connected); - if (bs->tpool && (flags & BS_MYTPOOL)) ks_thread_pool_destroy(&bs->tpool); - ks_pool_free(bs->pool, &bs); - if (pool && (flags & BS_MYPOOL)) ks_pool_close(&pool); - return KS_STATUS_SUCCESS; } -KS_DECLARE(ks_status_t) blade_service_create(blade_service_t **bsP, ks_pool_t *pool, ks_thread_pool_t *tpool, blade_handle_t *handle) +KS_DECLARE(ks_status_t) blade_service_create(blade_service_t **bsP, + ks_pool_t *pool, + ks_thread_pool_t *tpool, + blade_handle_t *handle, + blade_service_peer_state_callback_t peer_state_callback) { - bspvt_flag_t newflags = BS_NONE; blade_service_t *bs = NULL; ks_assert(bsP); + ks_assert(pool); + ks_assert(tpool); ks_assert(handle); - if (!pool) { - newflags |= BS_MYPOOL; - ks_pool_open(&pool); - ks_assert(pool); - } - if (!tpool) { - newflags |= BS_MYTPOOL; - ks_thread_pool_create(&tpool, BLADE_SERVICE_TPOOL_MIN, BLADE_SERVICE_TPOOL_MAX, BLADE_SERVICE_TPOOL_STACK, KS_PRI_NORMAL, BLADE_SERVICE_TPOOL_IDLE); - ks_assert(tpool); - } - bs = ks_pool_alloc(pool, sizeof(*bs)); - bs->flags = newflags; bs->pool = pool; bs->tpool = tpool; bs->handle = handle; + bs->peer_state_callback = peer_state_callback; list_init(&bs->connected); *bsP = bs; @@ -258,6 +236,8 @@ KS_DECLARE(ks_status_t) blade_service_shutdown(blade_service_t *bs) { ks_assert(bs); + // @todo 1 more callback for blade_service_state_callback_t? providing event up the stack on service startup, shutdown, and service errors? + bs->shutdown = KS_TRUE; if (bs->listeners_thread) { @@ -275,7 +255,7 @@ KS_DECLARE(ks_status_t) blade_service_shutdown(blade_service_t *bs) list_iterator_start(&bs->connected); while (list_iterator_hasnext(&bs->connected)) { blade_peer_t *peer = (blade_peer_t *)list_iterator_next(&bs->connected); - blade_peer_destroy(&peer); + blade_peer_destroy(&peer); // @todo determine if NOT receiving the DISCONNECTING event callback for these will matter, as service is being shutdown } list_iterator_stop(&bs->connected); list_clear(&bs->connected); @@ -284,6 +264,14 @@ KS_DECLARE(ks_status_t) blade_service_shutdown(blade_service_t *bs) return KS_STATUS_SUCCESS; } +KS_DECLARE(void) blade_service_peer_state_callback(blade_service_t *bs, blade_peer_t *bp, blade_peerstate_t state) +{ + ks_assert(bs); + ks_assert(bp); + + if (bs->peer_state_callback) bs->peer_state_callback(bs, bp, state); +} + ks_status_t blade_service_listen(blade_service_t *bs, ks_sockaddr_t *addr) { ks_socket_t listener = KS_SOCK_INVALID; @@ -317,12 +305,9 @@ ks_status_t blade_service_listen(blade_service_t *bs, ks_sockaddr_t *addr) bs->listeners_size = bs->listeners_length; bs->listeners_poll = (struct pollfd *)ks_pool_resize(bs->pool, bs->listeners_poll, sizeof(struct pollfd) * bs->listeners_size); ks_assert(bs->listeners_poll); - bs->listeners_families = (int32_t *)ks_pool_resize(bs->pool, bs->listeners_families, sizeof(int32_t) * bs->listeners_size); - ks_assert(bs->listeners_families); } bs->listeners_poll[listener_index].fd = listener; bs->listeners_poll[listener_index].events = POLLIN | POLLERR; - bs->listeners_families[listener_index] = addr->family; done: if (ret != KS_STATUS_SUCCESS) { @@ -343,6 +328,8 @@ void *blade_service_listeners_thread(ks_thread_t *thread, void *data) service = (blade_service_t *)data; + // @todo 1 more callback for blade_service_state_callback_t? providing event up the stack on service startup, shutdown, and service errors? + while (!service->shutdown) { // @todo take exact timeout from a setting in config_service_endpoints if (ks_poll(service->listeners_poll, service->listeners_length, 100) > 0) { @@ -367,6 +354,7 @@ void *blade_service_listeners_thread(ks_thread_t *thread, void *data) ks_assert(peer); // @todo call state callback with connecting enum state + blade_service_peer_state_callback(service, peer, BLADE_PEERSTATE_CONNECTING); blade_peer_startup(peer, sock); @@ -379,10 +367,11 @@ void *blade_service_listeners_thread(ks_thread_t *thread, void *data) blade_peer_t *peer = (blade_peer_t *)list_iterator_next(&service->connected); // @todo expose accessor for disconnecting, after changing it into the state callback enum // ensure that every way kws_close might occur leads back to disconnecting = KS_TRUE for this to universally process disconnects - if (blade_peer_disconnecting(peer)) { + if (blade_peer_state(peer) == BLADE_PEERSTATE_DISCONNECTING) { // @todo check if there is an iterator based remove function, or indexed iteration to use list_delete_at() list_delete(&service->connected, peer); - // @todo call state callback with internal disconnecting enum state (stored in peer to hold specific reason for disconnecting) + blade_service_peer_state_callback(service, peer, BLADE_PEERSTATE_DISCONNECTING); + // @todo switch to blade_peer_shutdown(&peer) and blade_peer_discard(&peer) after introducing recycling of peers blade_peer_destroy(&peer); } diff --git a/libs/libblade/src/blade_stack.c b/libs/libblade/src/blade_stack.c index b262336bf9..48a89afe0d 100644 --- a/libs/libblade/src/blade_stack.c +++ b/libs/libblade/src/blade_stack.c @@ -143,14 +143,14 @@ ks_status_t blade_handle_config(blade_handle_t *bh, config_setting_t *config) return KS_STATUS_SUCCESS; } -KS_DECLARE(ks_status_t) blade_handle_startup(blade_handle_t *bh, config_setting_t *config) +KS_DECLARE(ks_status_t) blade_handle_startup(blade_handle_t *bh, config_setting_t *config, blade_service_peer_state_callback_t service_peer_state_callback) { ks_assert(bh); if (blade_handle_config(bh, config) != KS_STATUS_SUCCESS) return KS_STATUS_FAIL; if (bh->config_service && !blade_handle_service_available(bh)) { - blade_service_create(&bh->service, bh->pool, bh->tpool, bh); + blade_service_create(&bh->service, bh->pool, bh->tpool, bh, service_peer_state_callback); ks_assert(bh->service); if (blade_service_startup(bh->service, bh->config_service) != KS_STATUS_SUCCESS) return KS_STATUS_FAIL; } diff --git a/libs/libblade/src/include/blade_datastore.h b/libs/libblade/src/include/blade_datastore.h index 641eccbd55..b42ce736ee 100644 --- a/libs/libblade/src/include/blade_datastore.h +++ b/libs/libblade/src/include/blade_datastore.h @@ -35,11 +35,6 @@ #define _BLADE_DATASTORE_H_ #include -#define BLADE_DATASTORE_TPOOL_MIN 2 -#define BLADE_DATASTORE_TPOOL_MAX 8 -#define BLADE_DATASTORE_TPOOL_STACK (1024 * 256) -#define BLADE_DATASTORE_TPOOL_IDLE 10 - KS_BEGIN_EXTERN_C 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); diff --git a/libs/libblade/src/include/blade_peer.h b/libs/libblade/src/include/blade_peer.h index acffc37998..a5cf304b46 100644 --- a/libs/libblade/src/include/blade_peer.h +++ b/libs/libblade/src/include/blade_peer.h @@ -35,18 +35,14 @@ #define _BLADE_PEER_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, blade_service_t *service); KS_DECLARE(ks_status_t) blade_peer_destroy(blade_peer_t **bpP); KS_DECLARE(ks_status_t) blade_peer_startup(blade_peer_t *bp, ks_socket_t sock); KS_DECLARE(ks_status_t) blade_peer_shutdown(blade_peer_t *bp); -KS_DECLARE(void) blade_peer_disconnect(blade_peer_t *bp); -KS_DECLARE(ks_bool_t) blade_peer_disconnecting(blade_peer_t *bp); +KS_DECLARE(void) blade_peer_disconnect(blade_peer_t *bp, blade_peerreason_t reason); +KS_DECLARE(blade_peerstate_t) blade_peer_state(blade_peer_t *bp); +KS_DECLARE(blade_peerreason_t) blade_peer_reason(blade_peer_t *bp); KS_DECLARE(ks_status_t) blade_peer_message_pop(blade_peer_t *peer, blade_message_t **message); KS_DECLARE(ks_status_t) blade_peer_message_push(blade_peer_t *peer, void *data, ks_size_t data_length); KS_END_EXTERN_C diff --git a/libs/libblade/src/include/blade_service.h b/libs/libblade/src/include/blade_service.h index d6f1de40b5..a5fa890b48 100644 --- a/libs/libblade/src/include/blade_service.h +++ b/libs/libblade/src/include/blade_service.h @@ -35,17 +35,17 @@ #define _BLADE_SERVICE_H_ #include -#define BLADE_SERVICE_TPOOL_MIN 2 -#define BLADE_SERVICE_TPOOL_MAX 8 -#define BLADE_SERVICE_TPOOL_STACK (1024 * 256) -#define BLADE_SERVICE_TPOOL_IDLE 10 - KS_BEGIN_EXTERN_C -KS_DECLARE(ks_status_t) blade_service_create(blade_service_t **bsP, ks_pool_t *pool, ks_thread_pool_t *tpool, blade_handle_t *handle); +KS_DECLARE(ks_status_t) blade_service_create(blade_service_t **bsP, + ks_pool_t *pool, + ks_thread_pool_t *tpool, + blade_handle_t *handle, + blade_service_peer_state_callback_t peer_state_callback); KS_DECLARE(ks_status_t) blade_service_destroy(blade_service_t **bsP); KS_DECLARE(blade_handle_t *) blade_service_handle(blade_service_t *bs); KS_DECLARE(ks_status_t) blade_service_startup(blade_service_t *bs, config_setting_t *config); KS_DECLARE(ks_status_t) blade_service_shutdown(blade_service_t *bs); +KS_DECLARE(void) blade_service_peer_state_callback(blade_service_t *bs, blade_peer_t *bp, blade_peerstate_t state); KS_END_EXTERN_C #endif diff --git a/libs/libblade/src/include/blade_stack.h b/libs/libblade/src/include/blade_stack.h index 507d8a12f7..ff44f230e9 100644 --- a/libs/libblade/src/include/blade_stack.h +++ b/libs/libblade/src/include/blade_stack.h @@ -43,7 +43,7 @@ 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); -KS_DECLARE(ks_status_t) blade_handle_startup(blade_handle_t *bh, config_setting_t *config); +KS_DECLARE(ks_status_t) blade_handle_startup(blade_handle_t *bh, config_setting_t *config, blade_service_peer_state_callback_t service_peer_state_callback); KS_DECLARE(ks_status_t) blade_handle_shutdown(blade_handle_t *bh); KS_DECLARE(ks_status_t) blade_handle_message_claim(blade_handle_t *bh, blade_message_t **message, void *data, ks_size_t data_length); diff --git a/libs/libblade/src/include/blade_types.h b/libs/libblade/src/include/blade_types.h index e7484d0f38..b534fde6fd 100644 --- a/libs/libblade/src/include/blade_types.h +++ b/libs/libblade/src/include/blade_types.h @@ -37,12 +37,26 @@ KS_BEGIN_EXTERN_C +typedef enum { + BLADE_PEERSTATE_CONNECTING, + BLADE_PEERSTATE_DISCONNECTING, + BLADE_PEERSTATE_RUNNING, + BLADE_PEERSTATE_RECEIVING, +} blade_peerstate_t; + +typedef enum { + BLADE_PEERREASON_NORMAL, + BLADE_PEERREASON_ERROR, + // @todo populate more reasons for disconnecting as neccessary +} blade_peerreason_t; + typedef struct blade_handle_s blade_handle_t; typedef struct blade_peer_s blade_peer_t; typedef struct blade_service_s blade_service_t; typedef struct blade_message_s blade_message_t; typedef struct blade_datastore_s blade_datastore_t; +typedef void (*blade_service_peer_state_callback_t)(blade_service_t *bs, blade_peer_t *bp, blade_peerstate_t state); typedef ks_bool_t (*blade_datastore_fetch_callback_t)(blade_datastore_t *bds, const void *data, uint32_t data_length, void *userdata); KS_END_EXTERN_C From 89940dfc601d5689aef54d5dfc6ca734406c0e0d Mon Sep 17 00:00:00 2001 From: Shane Bryldt Date: Tue, 31 Jan 2017 19:28:21 +0000 Subject: [PATCH 08/34] FS-9952: Fixed some stuff to get the blade service tested upto the point of processing config and listening on the same port across multiple interfaces --- libs/libblade/src/blade_service.c | 52 +++++-- libs/libblade/src/blade_stack.c | 15 +- libs/libblade/test/Makefile.am | 2 +- libs/libblade/test/bladec | 228 ++++++++++++++++++++++++++++++ libs/libblade/test/bladec.c | 40 +++++- libs/libblade/test/bladec.cfg | 50 +++++++ libs/libks/src/include/simclist.h | 143 +++++++++---------- libs/libks/src/ks_socket.c | 9 ++ libs/libks/src/simclist.c | 88 ++++++------ 9 files changed, 492 insertions(+), 135 deletions(-) create mode 100755 libs/libblade/test/bladec create mode 100644 libs/libblade/test/bladec.cfg diff --git a/libs/libblade/src/blade_service.c b/libs/libblade/src/blade_service.c index b52e7d06a9..4eabd669af 100644 --- a/libs/libblade/src/blade_service.c +++ b/libs/libblade/src/blade_service.c @@ -130,13 +130,25 @@ ks_status_t blade_service_config(blade_service_t *bs, config_setting_t *config) ks_assert(bs); - if (!config) return KS_STATUS_FAIL; - if (!config_setting_is_group(config)) return KS_STATUS_FAIL; + if (!config) { + ks_log(KS_LOG_DEBUG, "!config\n"); + return KS_STATUS_FAIL; + } + if (!config_setting_is_group(config)) { + ks_log(KS_LOG_DEBUG, "!config_setting_is_group(config)\n"); + return KS_STATUS_FAIL; + } websockets = config_setting_get_member(config, "websockets"); - if (!websockets) return KS_STATUS_FAIL; - websockets_endpoints = config_setting_get_member(config, "endpoints"); - if (!websockets_endpoints) return KS_STATUS_FAIL; + if (!websockets) { + ks_log(KS_LOG_DEBUG, "!websockets\n"); + return KS_STATUS_FAIL; + } + websockets_endpoints = config_setting_get_member(websockets, "endpoints"); + if (!websockets_endpoints) { + ks_log(KS_LOG_DEBUG, "!websockets_endpoints\n"); + return KS_STATUS_FAIL; + } websockets_endpoints_ipv4 = config_lookup_from(websockets_endpoints, "ipv4"); websockets_endpoints_ipv6 = config_lookup_from(websockets_endpoints, "ipv6"); if (websockets_endpoints_ipv4) { @@ -156,6 +168,10 @@ ks_status_t blade_service_config(blade_service_t *bs, config_setting_t *config) 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_websockets_endpoints_ipv4[index]), + ks_addr_get_port(&config_websockets_endpoints_ipv4[index])); } } if (websockets_endpoints_ipv6) { @@ -175,6 +191,10 @@ ks_status_t blade_service_config(blade_service_t *bs, config_setting_t *config) 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_websockets_endpoints_ipv6[index]), + ks_addr_get_port(&config_websockets_endpoints_ipv6[index])); } } if (config_websockets_endpoints_ipv4_length + config_websockets_endpoints_ipv6_length <= 0) return KS_STATUS_FAIL; @@ -212,13 +232,22 @@ KS_DECLARE(ks_status_t) blade_service_startup(blade_service_t *bs, config_settin // @todo: If the configuration is invalid, and this is a case of reloading a new config, then the service shutdown shouldn't occur // but the service may use configuration that changes before we shutdown if it is read successfully, may require a config reader/writer mutex? - if (blade_service_config(bs, config) != KS_STATUS_SUCCESS) return KS_STATUS_FAIL; + if (blade_service_config(bs, config) != KS_STATUS_SUCCESS) { + ks_log(KS_LOG_DEBUG, "blade_service_config failed\n"); + return KS_STATUS_FAIL; + } for (int32_t index = 0; index < bs->config_websockets_endpoints_ipv4_length; ++index) { - if (blade_service_listen(bs, &bs->config_websockets_endpoints_ipv4[index]) != KS_STATUS_SUCCESS) return KS_STATUS_FAIL; + if (blade_service_listen(bs, &bs->config_websockets_endpoints_ipv4[index]) != KS_STATUS_SUCCESS) { + ks_log(KS_LOG_DEBUG, "blade_service_listen (v4) failed\n"); + return KS_STATUS_FAIL; + } } for (int32_t index = 0; index < bs->config_websockets_endpoints_ipv6_length; ++index) { - if (blade_service_listen(bs, &bs->config_websockets_endpoints_ipv6[index]) != KS_STATUS_SUCCESS) return KS_STATUS_FAIL; + if (blade_service_listen(bs, &bs->config_websockets_endpoints_ipv6[index]) != KS_STATUS_SUCCESS) { + ks_log(KS_LOG_DEBUG, "blade_service_listen (v6) failed\n"); + return KS_STATUS_FAIL; + } } if (ks_thread_create_ex(&bs->listeners_thread, @@ -282,20 +311,23 @@ ks_status_t blade_service_listen(blade_service_t *bs, ks_sockaddr_t *addr) 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); - // @todo make sure v6 does not automatically map to a v4 using socket option IPV6_V6ONLY? + 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, bs->config_websockets_endpoints_backlog) != 0) { + ks_log(KS_LOG_DEBUG, "listen(listener, backlog) != 0\n"); ret = KS_STATUS_FAIL; goto done; } @@ -327,6 +359,8 @@ void *blade_service_listeners_thread(ks_thread_t *thread, void *data) ks_assert(data); service = (blade_service_t *)data; + + ks_log(KS_LOG_DEBUG, "Service running\n"); // @todo 1 more callback for blade_service_state_callback_t? providing event up the stack on service startup, shutdown, and service errors? diff --git a/libs/libblade/src/blade_stack.c b/libs/libblade/src/blade_stack.c index 48a89afe0d..8504af02c4 100644 --- a/libs/libblade/src/blade_stack.c +++ b/libs/libblade/src/blade_stack.c @@ -147,18 +147,27 @@ KS_DECLARE(ks_status_t) blade_handle_startup(blade_handle_t *bh, config_setting_ { ks_assert(bh); - if (blade_handle_config(bh, config) != KS_STATUS_SUCCESS) return KS_STATUS_FAIL; + 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_service && !blade_handle_service_available(bh)) { blade_service_create(&bh->service, bh->pool, bh->tpool, bh, service_peer_state_callback); ks_assert(bh->service); - if (blade_service_startup(bh->service, bh->config_service) != KS_STATUS_SUCCESS) return KS_STATUS_FAIL; + if (blade_service_startup(bh->service, bh->config_service) != KS_STATUS_SUCCESS) { + ks_log(KS_LOG_DEBUG, "blade_service_startup 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) return KS_STATUS_FAIL; + 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; + } } return KS_STATUS_SUCCESS; diff --git a/libs/libblade/test/Makefile.am b/libs/libblade/test/Makefile.am index d577b95507..9bc1a688df 100644 --- a/libs/libblade/test/Makefile.am +++ b/libs/libblade/test/Makefile.am @@ -1,5 +1,5 @@ AM_CFLAGS += -I$(abs_top_srcdir)/src/include -g -ggdb -O0 -TEST_LDADD = $(abs_top_builddir)/libblade.la +TEST_LDADD = $(abs_top_builddir)/libblade.la -lconfig -lm -lpthread check_PROGRAMS = check_PROGRAMS += testbuild diff --git a/libs/libblade/test/bladec b/libs/libblade/test/bladec new file mode 100755 index 0000000000..805d6501b3 --- /dev/null +++ b/libs/libblade/test/bladec @@ -0,0 +1,228 @@ +#! /bin/bash + +# bladec - temporary wrapper script for .libs/bladec +# Generated by libtool (GNU libtool) 2.4.2 Debian-2.4.2-1.11 +# +# The bladec program cannot be directly executed until all the libtool +# libraries that it depends on are installed. +# +# This wrapper script should never be moved out of the build directory. +# If it is, it will not operate correctly. + +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +sed_quote_subst='s/\([`"$\\]\)/\\\1/g' + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac +fi +BIN_SH=xpg4; export BIN_SH # for Tru64 +DUALCASE=1; export DUALCASE # for MKS sh + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +relink_command="(cd /usr/src/freeswitch/libs/libblade/test; { test -z \"\${LIBRARY_PATH+set}\" || unset LIBRARY_PATH || { LIBRARY_PATH=; export LIBRARY_PATH; }; }; { test -z \"\${COMPILER_PATH+set}\" || unset COMPILER_PATH || { COMPILER_PATH=; export COMPILER_PATH; }; }; { test -z \"\${GCC_EXEC_PREFIX+set}\" || unset GCC_EXEC_PREFIX || { GCC_EXEC_PREFIX=; export GCC_EXEC_PREFIX; }; }; { test -z \"\${LD_RUN_PATH+set}\" || unset LD_RUN_PATH || { LD_RUN_PATH=; export LD_RUN_PATH; }; }; { test -z \"\${LD_LIBRARY_PATH+set}\" || unset LD_LIBRARY_PATH || { LD_LIBRARY_PATH=; export LD_LIBRARY_PATH; }; }; PATH=/usr/lib/ccache:/usr/lib/ccache:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/bin:/usr/local/bin:/usr/local/sbin:/usr/local/freeswitch/bin:/opt/bin:/usr/local/bin:/usr/local/sbin:/usr/local/freeswitch/bin; export PATH; gcc -fPIC -Wall -std=c99 -pedantic -DUSE_SCHED_SETSCHEDULER=1 -DKS_API_VISIBILITY=1 -fvisibility=hidden -Werror -DHAVE_PTHREAD_SETSCHEDPARAM=1 -DHAVE_OPENSSL -fsanitize=address -fno-omit-frame-pointer -I/usr/src/freeswitch/libs/libblade/src/include -g -ggdb -O0 -g -O2 -fsanitize=address -o \$progdir/\$file bladec-bladec.o bladec-tap.o -L/usr/src/freeswitch/libs/libblade/../libks/.libs/ /usr/src/freeswitch/libs/libblade/../libks/.libs//libks.so -lsodium /usr/src/freeswitch/libs/libblade/.libs/libblade.so -lconfig -lm -lpthread -lssl -lcrypto -Wl,-rpath -Wl,/usr/src/freeswitch/libs/libblade/../libks/.libs/ -Wl,-rpath -Wl,/usr/src/freeswitch/libs/libblade/.libs)" + +# This environment variable determines our operation mode. +if test "$libtool_install_magic" = "%%%MAGIC variable%%%"; then + # install mode needs the following variables: + generated_by_libtool_version='2.4.2' + notinst_deplibs=' /usr/src/freeswitch/libs/libblade/../libks/.libs//libks.la /usr/src/freeswitch/libs/libblade/libblade.la' +else + # When we are sourced in execute mode, $file and $ECHO are already set. + if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then + file="$0" + +# A function that is used when there is no print builtin or printf. +func_fallback_echo () +{ + eval 'cat <<_LTECHO_EOF +$1 +_LTECHO_EOF' +} + ECHO="printf %s\\n" + fi + +# Very basic option parsing. These options are (a) specific to +# the libtool wrapper, (b) are identical between the wrapper +# /script/ and the wrapper /executable/ which is used only on +# windows platforms, and (c) all begin with the string --lt- +# (application programs are unlikely to have options which match +# this pattern). +# +# There are only two supported options: --lt-debug and +# --lt-dump-script. There is, deliberately, no --lt-help. +# +# The first argument to this parsing function should be the +# script's ../libtool value, followed by no. +lt_option_debug= +func_parse_lt_options () +{ + lt_script_arg0=$0 + shift + for lt_opt + do + case "$lt_opt" in + --lt-debug) lt_option_debug=1 ;; + --lt-dump-script) + lt_dump_D=`$ECHO "X$lt_script_arg0" | /bin/sed -e 's/^X//' -e 's%/[^/]*$%%'` + test "X$lt_dump_D" = "X$lt_script_arg0" && lt_dump_D=. + lt_dump_F=`$ECHO "X$lt_script_arg0" | /bin/sed -e 's/^X//' -e 's%^.*/%%'` + cat "$lt_dump_D/$lt_dump_F" + exit 0 + ;; + --lt-*) + $ECHO "Unrecognized --lt- option: '$lt_opt'" 1>&2 + exit 1 + ;; + esac + done + + # Print the debug banner immediately: + if test -n "$lt_option_debug"; then + echo "bladec:bladec:${LINENO}: libtool wrapper (GNU libtool) 2.4.2 Debian-2.4.2-1.11" 1>&2 + fi +} + +# Used when --lt-debug. Prints its arguments to stdout +# (redirection is the responsibility of the caller) +func_lt_dump_args () +{ + lt_dump_args_N=1; + for lt_arg + do + $ECHO "bladec:bladec:${LINENO}: newargv[$lt_dump_args_N]: $lt_arg" + lt_dump_args_N=`expr $lt_dump_args_N + 1` + done +} + +# Core function for launching the target application +func_exec_program_core () +{ + + if test -n "$lt_option_debug"; then + $ECHO "bladec:bladec:${LINENO}: newargv[0]: $progdir/$program" 1>&2 + func_lt_dump_args ${1+"$@"} 1>&2 + fi + exec "$progdir/$program" ${1+"$@"} + + $ECHO "$0: cannot exec $program $*" 1>&2 + exit 1 +} + +# A function to encapsulate launching the target application +# Strips options in the --lt-* namespace from $@ and +# launches target application with the remaining arguments. +func_exec_program () +{ + case " $* " in + *\ --lt-*) + for lt_wr_arg + do + case $lt_wr_arg in + --lt-*) ;; + *) set x "$@" "$lt_wr_arg"; shift;; + esac + shift + done ;; + esac + func_exec_program_core ${1+"$@"} +} + + # Parse options + func_parse_lt_options "$0" ${1+"$@"} + + # Find the directory that this script lives in. + thisdir=`$ECHO "$file" | /bin/sed 's%/[^/]*$%%'` + test "x$thisdir" = "x$file" && thisdir=. + + # Follow symbolic links until we get to the real thisdir. + file=`ls -ld "$file" | /bin/sed -n 's/.*-> //p'` + while test -n "$file"; do + destdir=`$ECHO "$file" | /bin/sed 's%/[^/]*$%%'` + + # If there was a directory component, then change thisdir. + if test "x$destdir" != "x$file"; then + case "$destdir" in + [\\/]* | [A-Za-z]:[\\/]*) thisdir="$destdir" ;; + *) thisdir="$thisdir/$destdir" ;; + esac + fi + + file=`$ECHO "$file" | /bin/sed 's%^.*/%%'` + file=`ls -ld "$thisdir/$file" | /bin/sed -n 's/.*-> //p'` + done + + # Usually 'no', except on cygwin/mingw when embedded into + # the cwrapper. + WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=no + if test "$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR" = "yes"; then + # special case for '.' + if test "$thisdir" = "."; then + thisdir=`pwd` + fi + # remove .libs from thisdir + case "$thisdir" in + *[\\/].libs ) thisdir=`$ECHO "$thisdir" | /bin/sed 's%[\\/][^\\/]*$%%'` ;; + .libs ) thisdir=. ;; + esac + fi + + # Try to get the absolute directory name. + absdir=`cd "$thisdir" && pwd` + test -n "$absdir" && thisdir="$absdir" + + program=lt-'bladec' + progdir="$thisdir/.libs" + + if test ! -f "$progdir/$program" || + { file=`ls -1dt "$progdir/$program" "$progdir/../$program" 2>/dev/null | /bin/sed 1q`; \ + test "X$file" != "X$progdir/$program"; }; then + + file="$$-$program" + + if test ! -d "$progdir"; then + mkdir "$progdir" + else + rm -f "$progdir/$file" + fi + + # relink executable if necessary + if test -n "$relink_command"; then + if relink_command_output=`eval $relink_command 2>&1`; then : + else + printf %s\n "$relink_command_output" >&2 + rm -f "$progdir/$file" + exit 1 + fi + fi + + mv -f "$progdir/$file" "$progdir/$program" 2>/dev/null || + { rm -f "$progdir/$program"; + mv -f "$progdir/$file" "$progdir/$program"; } + rm -f "$progdir/$file" + fi + + if test -f "$progdir/$program"; then + if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then + # Run the actual program with our arguments. + func_exec_program ${1+"$@"} + fi + else + # The program doesn't exist. + $ECHO "$0: error: \`$progdir/$program' does not exist" 1>&2 + $ECHO "This script is just a wrapper for $program." 1>&2 + $ECHO "See the libtool documentation for more information." 1>&2 + exit 1 + fi +fi diff --git a/libs/libblade/test/bladec.c b/libs/libblade/test/bladec.c index 09e821edc2..aadfed612e 100644 --- a/libs/libblade/test/bladec.c +++ b/libs/libblade/test/bladec.c @@ -16,6 +16,7 @@ char g_console_input[CONSOLE_INPUT_MAX]; size_t g_console_input_length = 0; size_t g_console_input_eol = 0; +void service_peer_state_callback(blade_service_t *service, blade_peer_t *peer, blade_peerstate_t state); void loop(blade_handle_t *bh); void process_console_input(blade_handle_t *bh, char *line); @@ -40,10 +41,11 @@ static const struct command_def_s command_defs[] = { { NULL, NULL } }; - int main(int argc, char **argv) { blade_handle_t *bh = NULL; + config_t config; + config_setting_t *config_blade = NULL; ks_global_set_default_logger(KS_LOG_LEVEL_DEBUG); @@ -51,8 +53,31 @@ int main(int argc, char **argv) blade_handle_create(&bh, NULL, NULL); - loop(bh); + // @todo load config file, and lookup "blade" setting to put into config_blade + config_init(&config); + if (!config_read_file(&config, "bladec.cfg")) { + 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, service_peer_state_callback) != KS_STATUS_SUCCESS) { + ks_log(KS_LOG_ERROR, "Blade startup failed\n"); + return EXIT_FAILURE; + } + + loop(bh); + blade_handle_destroy(&bh); blade_shutdown(); @@ -60,6 +85,12 @@ int main(int argc, char **argv) return 0; } +void service_peer_state_callback(blade_service_t *service, blade_peer_t *peer, blade_peerstate_t state) +{ + // @todo log output and pop peer messages if state == BLADE_PEERSTATE_RECEIVING + ks_log(KS_LOG_INFO, "service peer state callback: %d\n", (int)state); +} + void buffer_console_input(void) { ssize_t bytes = 0; @@ -110,7 +141,6 @@ 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); } } @@ -175,8 +205,6 @@ void command_store(blade_handle_t *bh, char *args) ks_assert(args); - blade_handle_datastore_startup(bh, NULL); - parse_argument(&args, &key, ' '); parse_argument(&args, &data, ' '); @@ -195,8 +223,6 @@ void command_fetch(blade_handle_t *bh, char *args) ks_assert(args); - blade_handle_datastore_startup(bh, NULL); - parse_argument(&args, &key, ' '); blade_handle_datastore_fetch(bh, blade_datastore_fetch_callback, key, strlen(key), bh); diff --git a/libs/libblade/test/bladec.cfg b/libs/libblade/test/bladec.cfg new file mode 100644 index 0000000000..a14a4e7f7a --- /dev/null +++ b/libs/libblade/test/bladec.cfg @@ -0,0 +1,50 @@ +blade: +{ + # client stuff, for peers who connect out to services + client: + { + directory: + { + # todo: hints for ways to find a directory service, at least kws client_data for now + # add DNS SRV in the future + uri = "???:127.0.0.1+2100:???"; # todo: confirm expected format, "uri:host:proto" + + websocket: + { + # SSL group is optional, disabled when absent + ssl: + { + # todo: client SSL stuffs here + }; + }; + }; + }; + + + # server stuff, for services that peers connect to + # todo: consider encapsulating in a "server" group for organizational structure + datastore: + { + database: + { + path = ":mem:"; + }; + }; + service: + { + websockets: + { + endpoints: + { + ipv4 = ( { address = "0.0.0.0", port = 2100 } ); + ipv6 = ( { address = "::", port = 2100 } ); + backlog = 128; + }; + # SSL group is optional, disabled when absent + ssl: + { + # todo: service SSL stuffs here + }; + }; + }; +}; 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_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/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; From 87cf88083381fb7e018adcffd8b27197bb739457 Mon Sep 17 00:00:00 2001 From: colm Date: Mon, 6 Feb 2017 19:23:09 -0500 Subject: [PATCH 09/34] FS-9952: Update ks json-rpc api --- libs/libks/src/include/ks_rpcmessage.h | 99 ++---- libs/libks/src/ks_rpcmessage.c | 431 ++++++++++--------------- libs/libks/test/testmessages.c | 232 +++---------- 3 files changed, 238 insertions(+), 524 deletions(-) diff --git a/libs/libks/src/include/ks_rpcmessage.h b/libs/libks/src/include/ks_rpcmessage.h index c897f06a82..832a6f3217 100644 --- a/libs/libks/src/include/ks_rpcmessage.h +++ b/libs/libks/src/include/ks_rpcmessage.h @@ -31,52 +31,6 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -/* - * Usage notes - * - * ks_rpcmessaging_handle_t *handle; - * - * ks_rpcmessage_init(pool, &handle); - * - * ks_rpcmessage_version(handle, version); - * ks_rpcmessage_namespace(handle, application_namespace); - * ks_rpcmessage_register_function(handle, "invite", handle_invite_message); - * ks_rpcmessage_register_function(handle, "media", handle_media_message); - * - * ... - * cJSON* request = NULL; - * cJSON* parms = NULL; - * cJSON* response = NULL; - * - * request = ks_rpcmessage_create_request(h, "invite", &parms, &request); - * cJSON_AddStringToObject(parms, "hello", "cruel world"); - * ... and send - * - * - * static ks_status_t handle_..._message(ks_rpcmessaging_handle_t* handle, cJSON *msg, cJSON **response) - * { - * cJSON *respvalue = cJSON_CreateNumber(1); - * cJSON *x = *response = ks_rpcmessage_create_response(h, msg, &respvalue, response); - * if ( x == NULL) { - * return KS_STATUS_FAIL; - * } - * ... - * return KS_STATUS_SUCCESS; - * } - - * - * - * - * - * - * - * ... - * ks_rpcmessage_deinit(&handle); - * - */ - - - #ifndef _KS_RPCMESSAGE_H_ #define _KS_RPCMESSAGE_H_ @@ -85,51 +39,48 @@ KS_BEGIN_EXTERN_C +#define KS_RPCMESSAGE_NAMESPACE_LENGTH 16 +#define KS_RPCMESSAGE_COMMAND_LENGTH 238 +#define KS_RPCMESSAGE_FQCOMMAND_LENGTH (KS_RPCMESSAGE_NAMESPACE_LENGTH+KS_RPCMESSAGE_COMMAND_LENGTH+1) +#define KS_RPCMESSAGE_VERSION_LENGTH 9 - - -typedef struct ks_rpcmessaging_handle_s ks_rpcmessaging_handle_t; typedef uint32_t ks_rpcmessage_id; -typedef ks_status_t (*jrpc_func_t)(ks_rpcmessaging_handle_t* handle, cJSON *request, cJSON **responseP); -typedef ks_status_t (*jrpc_resp_func_t)(ks_rpcmessaging_handle_t* handle, cJSON *response); +KS_DECLARE(void) ks_rpcmessage_init(ks_pool_t *pool); + +KS_DECLARE(void*) ks_json_pool_alloc(ks_size_t size); +KS_DECLARE(void) ks_json_pool_free(void *ptr); - -KS_DECLARE(ks_rpcmessaging_handle_t *) ks_rpcmessage_init(ks_pool_t* pool, ks_rpcmessaging_handle_t** handleP); -KS_DECLARE(void) ks_rpcmessage_deinit(ks_rpcmessaging_handle_t** handleP); - -KS_DECLARE(ks_status_t)ks_rpcmessage_namespace(ks_rpcmessaging_handle_t* handle, const char* namespace, const char* version); - -KS_DECLARE(ks_rpcmessage_id)ks_rpcmessage_create_request(ks_rpcmessaging_handle_t* handle, - const char *method, +KS_DECLARE(ks_rpcmessage_id) ks_rpcmessage_create_request(char *namespace, + char *method, + char *sessionid, + char *version, cJSON **parmsP, cJSON **requestP); -KS_DECLARE(ks_rpcmessage_id)ks_rpcmessage_create_response(ks_rpcmessaging_handle_t* handle, + +KS_DECLARE(ks_size_t) ks_rpc_create_buffer(char *namespace, + char *method, + char *sessionid, + char *version, + cJSON **parmsP, + ks_buffer_t *buffer); + +KS_DECLARE(ks_rpcmessage_id) ks_rpcmessage_create_response( const cJSON *request, cJSON **resultP, cJSON **responseP); -KS_DECLARE(ks_rpcmessage_id)ks_rpcmessage_create_errorresponse(ks_rpcmessaging_handle_t* handle, + +KS_DECLARE(ks_rpcmessage_id) ks_rpcmessage_create_errorresponse( const cJSON *request, cJSON **errorP, cJSON **responseP); -KS_DECLARE(ks_status_t)ks_rpcmessage_register_function(ks_rpcmessaging_handle_t* handle, - const char *command, - jrpc_func_t func, - jrpc_resp_func_t respfunc); - -KS_DECLARE(jrpc_func_t) ks_rpcmessage_find_function(ks_rpcmessaging_handle_t* handle, const char *command); -KS_DECLARE(jrpc_resp_func_t) ks_rpcmessage_find_response_function(ks_rpcmessaging_handle_t* handle, const char *command); - -KS_DECLARE(ks_status_t) ks_rpcmessage_process_message(ks_rpcmessaging_handle_t* handle, - uint8_t *data, - ks_size_t size, - cJSON **responseP); -KS_DECLARE(ks_status_t) ks_rpcmessage_process_jsonmessage(ks_rpcmessaging_handle_t* handle, cJSON *request, cJSON **responseP); +KS_DECLARE(ks_bool_t) ks_rpcmessage_isrequest(cJSON *msg); +KS_DECLARE(ks_bool_t) ks_rpcmessage_isrpc(cJSON *msg); KS_END_EXTERN_C diff --git a/libs/libks/src/ks_rpcmessage.c b/libs/libks/src/ks_rpcmessage.c index 4712c7bd13..41e327bcc8 100644 --- a/libs/libks/src/ks_rpcmessage.c +++ b/libs/libks/src/ks_rpcmessage.c @@ -36,81 +36,63 @@ #include #include +#include -#define KS_RPCMESSAGE_NAMESPACE_LENGTH 16 -#define KS_PRCMESSAGE_COMMAND_LENGTH 238 -#define KS_PRCMESSAGE_FQCOMMAND_LENGTH KS_RPCMESSAGE_NAMESPACE_LENGTH + 1 + KS_PRCMESSAGE_COMMAND_LENGTH -#define KS_RPCMESSAGE_VERSION_LENGTH 9 - -struct ks_rpcmessaging_handle_s +struct { - ks_hash_t *method_hash; - ks_mutex_t *id_mutex; - uint32_t message_id; - - ks_pool_t *pool; + ks_mutex_t *id_mutex; + uint32_t message_id; - char namespace[KS_RPCMESSAGE_NAMESPACE_LENGTH+2]; - char version[KS_RPCMESSAGE_VERSION_LENGTH+1]; /* nnn.nn.nn */ -}; + ks_pool_t *pool; -typedef struct ks_rpcmessage_callbackpair_s +} handle = {NULL, 0, NULL}; + +KS_DECLARE(void*) ks_json_pool_alloc(ks_size_t size) { - jrpc_func_t request_func; - jrpc_resp_func_t response_func; - uint16_t command_length; - char command[1]; -} ks_rpcmessage_callbackpair_t; + return ks_pool_alloc(handle.pool, size); +} + +KS_DECLARE(void) ks_json_pool_free(void *ptr) +{ + ks_pool_free(handle.pool, &ptr); +} -static uint32_t ks_rpcmessage_next_id(ks_rpcmessaging_handle_t* handle) +KS_DECLARE(void) ks_rpcmessage_init(ks_pool_t* pool) +{ + if (!handle.id_mutex) { + ks_mutex_create(&handle.id_mutex, KS_MUTEX_FLAG_DEFAULT, pool); + handle.pool = pool; + + cJSON_Hooks hooks; + hooks.malloc_fn = ks_json_pool_alloc; + hooks.free_fn = ks_json_pool_free; + cJSON_InitHooks(&hooks); + } + return; +} + +static uint32_t ks_rpcmessage_next_id() { uint32_t message_id; - ks_mutex_lock(handle->id_mutex); + ks_mutex_lock(handle.id_mutex); - ++handle->message_id; + ++handle.message_id; - if (!handle->message_id) { - ++handle->message_id; + if (!handle.message_id) { + ++handle.message_id; } - message_id = handle->message_id; + message_id = handle.message_id; - ks_mutex_unlock(handle->id_mutex); + ks_mutex_unlock(handle.id_mutex); return message_id; } -KS_DECLARE(ks_rpcmessaging_handle_t*) ks_rpcmessage_init(ks_pool_t* pool, ks_rpcmessaging_handle_t** handleP) -{ - ks_rpcmessaging_handle_t *handle = (ks_rpcmessaging_handle_t *)ks_pool_alloc(pool, sizeof(ks_rpcmessaging_handle_t)); - *handleP = handle; - - ks_hash_create(&handle->method_hash, - KS_HASH_MODE_CASE_SENSITIVE, - KS_HASH_FLAG_RWLOCK + KS_HASH_FLAG_DUP_CHECK + KS_HASH_FLAG_FREE_VALUE, - pool); - - ks_mutex_create(&handle->id_mutex, KS_MUTEX_FLAG_DEFAULT, pool); - - strcpy(handle->namespace, "global."); - - handle->pool = pool; - return handle; -} - - -KS_DECLARE(void) ks_rpcmessage_deinit(ks_rpcmessaging_handle_t** handleP) -{ - ks_rpcmessaging_handle_t *handle = *handleP; - ks_hash_destroy(&handle->method_hash); - ks_pool_free(handle->pool, handleP); - return; -} - static cJSON *ks_rpcmessage_new(uint32_t id) { cJSON *obj = cJSON_CreateObject(); @@ -135,9 +117,8 @@ static cJSON *ks_rpcmessage_dup(cJSON *msgid) return obj; } -static ks_bool_t ks_rpcmessage_isrequest(cJSON *msg) +KS_DECLARE(ks_bool_t) ks_rpcmessage_isrequest(cJSON *msg) { - //cJSON *params = cJSON_GetObjectItem(msg, "param"); cJSON *result = cJSON_GetObjectItem(msg, "result"); cJSON *error = cJSON_GetObjectItem(msg, "error"); @@ -148,47 +129,79 @@ static ks_bool_t ks_rpcmessage_isrequest(cJSON *msg) return KS_TRUE; } +KS_DECLARE(ks_bool_t) ks_rpcmessage_isrpc(cJSON *msg) +{ + cJSON *rpc = cJSON_GetObjectItem(msg, "json-rpc"); + + if (rpc) { + return KS_FALSE; + } + + return KS_TRUE; +} -KS_DECLARE(ks_rpcmessage_id) ks_rpcmessage_create_request(ks_rpcmessaging_handle_t* handle, - const char *command, + + +KS_DECLARE(ks_rpcmessage_id) ks_rpcmessage_create_request(char *namespace, + char *command, + char *sessionid, + char *version, cJSON **paramsP, cJSON **requestP) { cJSON *msg, *params = NULL; *requestP = NULL; - if (!ks_rpcmessage_find_function(handle, command)) { - ks_log(KS_LOG_ERROR, "Attempt to create unknown message type : %s, namespace %s\n", command, handle->namespace); - return 0; - } - - ks_rpcmessage_id msgid = ks_rpcmessage_next_id(handle); + ks_rpcmessage_id msgid = ks_rpcmessage_next_id(); msg = ks_rpcmessage_new(msgid); - if (paramsP && *paramsP) { - params = *paramsP; + if (paramsP && *paramsP) { /* parameters have been passed */ + + cJSON *p = *paramsP; + + if (p->type != cJSON_Object) { /* need to wrap this in a param field */ + params = cJSON_CreateObject(); + cJSON_AddItemToObject(params, "param", p); + } + else { + params = *paramsP; + } + + cJSON *v = cJSON_GetObjectItem(params, "version"); + + if (!v) { /* add version if needed */ + cJSON_AddStringToObject(params, "version", version); + } + else { + cJSON_AddStringToObject(params, "version", "0"); + } } if (!params) { params = cJSON_CreateObject(); + + if (version && version[0] != 0) { + cJSON_AddStringToObject(params, "version", version); + } + else { + cJSON_AddStringToObject(params, "version", "0"); + } + } - char fqcommand[KS_PRCMESSAGE_FQCOMMAND_LENGTH]; + char fqcommand[KS_RPCMESSAGE_FQCOMMAND_LENGTH]; memset(fqcommand, 0, sizeof(fqcommand)); - if (handle->namespace[0] != 0) { - strcpy(fqcommand, handle->namespace); - } - - strcat(fqcommand, command); + sprintf(fqcommand, "%s.%s", namespace, command); cJSON_AddItemToObject(msg, "method", cJSON_CreateString(fqcommand)); - cJSON_AddItemToObject(msg, "params", params); - if (handle->version[0] != 0) { - cJSON_AddItemToObject(msg, "version", cJSON_CreateString(handle->version)); - } + if (sessionid && sessionid[0] != 0) { + cJSON_AddStringToObject(params, "sessionid", sessionid); + } + + cJSON_AddItemToObject(msg, "params", params); if (paramsP) { *paramsP = params; @@ -198,6 +211,31 @@ KS_DECLARE(ks_rpcmessage_id) ks_rpcmessage_create_request(ks_rpcmessaging_handle return msgid; } +KS_DECLARE(ks_size_t) ks_rpc_create_buffer(char *namespace, + char *method, + char *sessionid, + char *version, + cJSON **parms, + ks_buffer_t *buffer) +{ + cJSON *message; + + ks_rpcmessage_id msgid = ks_rpcmessage_create_request(namespace, method, sessionid, version, parms, &message); + + if (!msgid) { + return 0; + } + + const char* b = cJSON_PrintUnformatted(message); + ks_size_t size = strlen(b); + + ks_buffer_write(buffer, b, size); + cJSON_Delete(message); + + return size; +} + + static ks_rpcmessage_id ks_rpcmessage_get_messageid(const cJSON *msg, cJSON **cmsgidP) { uint32_t msgid = 0; @@ -214,14 +252,22 @@ static ks_rpcmessage_id ks_rpcmessage_get_messageid(const cJSON *msg, cJSON **cm } -static ks_rpcmessage_id ks_rpcmessage_new_response(ks_rpcmessaging_handle_t* handle, +static ks_rpcmessage_id ks_rpcmessage_new_response( const cJSON *request, cJSON *result, cJSON **pmsg) { cJSON *respmsg = NULL; cJSON *cmsgid = NULL; + cJSON *version = NULL; + cJSON *sessionid = NULL; + cJSON *command = cJSON_GetObjectItem(request, "method"); + cJSON *params = cJSON_GetObjectItem(request, "params"); + + if (params) { + version = cJSON_GetObjectItem(request, "version"); + } ks_rpcmessage_id msgid = ks_rpcmessage_get_messageid(request, &cmsgid ); @@ -233,11 +279,25 @@ static ks_rpcmessage_id ks_rpcmessage_new_response(ks_rpcmessaging_handle_t* han cJSON_AddItemToObject(respmsg, "method", cJSON_Duplicate(command, 0)); - if (handle->version[0] != 0) { - cJSON_AddItemToObject(respmsg, "version", cJSON_CreateString(handle->version)); - } - if (result) { + + cJSON *params = cJSON_GetObjectItem(request, "params"); + + if (params) { + version = cJSON_GetObjectItem(params, "version"); + + if (version) { + cJSON_AddItemToObject(result, "version", cJSON_Duplicate(version, 0)); + } + + sessionid = cJSON_GetObjectItem(params, "sessionid"); + + if (sessionid) { + cJSON_AddItemToObject(result, "sessionid", cJSON_Duplicate(sessionid, 0)); + } + + } + cJSON_AddItemToObject(respmsg, "result", result); } @@ -245,31 +305,56 @@ static ks_rpcmessage_id ks_rpcmessage_new_response(ks_rpcmessaging_handle_t* han } -KS_DECLARE(ks_rpcmessage_id) ks_rpcmessage_create_response(ks_rpcmessaging_handle_t* handle, +KS_DECLARE(ks_rpcmessage_id) ks_rpcmessage_create_response( const cJSON *request, cJSON **resultP, cJSON **responseP) { - ks_rpcmessage_id msgid = ks_rpcmessage_new_response(handle, request, *resultP, responseP); + ks_rpcmessage_id msgid = ks_rpcmessage_new_response(request, *resultP, responseP); + cJSON *respmsg = *responseP; if (msgid) { if (*resultP == NULL) { *resultP = cJSON_CreateObject(); - cJSON_AddItemToObject(respmsg, "result", *resultP); + cJSON *result = *resultP; + + cJSON *params = cJSON_GetObjectItem(request, "params"); + + if (params) { + cJSON *version = cJSON_GetObjectItem(request, "version"); + cJSON *sessionid = cJSON_GetObjectItem(request, "sessionid"); + + if (version) { + cJSON_AddItemToObject(result, "version", cJSON_Duplicate(version, 0)); + } + else { + cJSON_AddStringToObject(result, "version", "0"); + } + + if (sessionid) { + cJSON_AddItemToObject(result, "sessionid", cJSON_Duplicate(sessionid, 0)); + } + + } + else { + cJSON_AddStringToObject(result, "version", "0"); + } + + cJSON_AddItemToObject(respmsg, "result", result); } } return msgid; } -KS_DECLARE(ks_rpcmessage_id) ks_rpcmessage_create_errorresponse(ks_rpcmessaging_handle_t* handle, +KS_DECLARE(ks_rpcmessage_id) ks_rpcmessage_create_errorresponse( const cJSON *request, cJSON **errorP, cJSON **responseP) { - ks_rpcmessage_id msgid = ks_rpcmessage_new_response(handle, request, *errorP, responseP); + ks_rpcmessage_id msgid = ks_rpcmessage_new_response(request, *errorP, responseP); cJSON *respmsg = *responseP; if (msgid) { @@ -283,180 +368,6 @@ KS_DECLARE(ks_rpcmessage_id) ks_rpcmessage_create_errorresponse(ks_rpcmessaging_ return msgid; } -KS_DECLARE(ks_status_t) ks_rpcmessage_namespace(ks_rpcmessaging_handle_t* handle, const char* namespace, const char* version) -{ - memset(handle->namespace, 0, sizeof(handle->namespace)); - memset(handle->version, 0, sizeof(handle->version)); - - strncpy(handle->namespace, namespace, KS_RPCMESSAGE_NAMESPACE_LENGTH); - strncpy(handle->version, version, KS_RPCMESSAGE_VERSION_LENGTH); - handle->namespace[sizeof(handle->namespace) - 1] = 0; - handle->version[sizeof(handle->version) -1] = 0; - - ks_log(KS_LOG_DEBUG, "Setting message namespace value %s, version %s", handle->namespace, handle->version); - strcat( handle->namespace, "."); - - return KS_STATUS_SUCCESS; -} - -KS_DECLARE(ks_status_t) ks_rpcmessage_register_function(ks_rpcmessaging_handle_t* handle, - const char *command, - jrpc_func_t func, - jrpc_resp_func_t respfunc) -{ - if (!func && !respfunc) { - return KS_STATUS_FAIL; - } - - char fqcommand[KS_PRCMESSAGE_FQCOMMAND_LENGTH]; - memset(fqcommand, 0, sizeof(fqcommand)); - - if (handle->namespace[0] != 0) { - strcpy(fqcommand, handle->namespace); - } - strcat(fqcommand, command); - - int lkey = strlen(fqcommand)+1; - - if (lkey < 16) { - lkey = 16; - } - - ks_rpcmessage_callbackpair_t* callbacks = - (ks_rpcmessage_callbackpair_t*)ks_pool_alloc(handle->pool, lkey + sizeof(ks_rpcmessage_callbackpair_t)); - - strcpy(callbacks->command, fqcommand); - callbacks->command_length = lkey; - callbacks->request_func = func; - callbacks->response_func = respfunc; - - ks_hash_write_lock(handle->method_hash); - ks_hash_insert(handle->method_hash, callbacks->command, (void *) callbacks); - - ks_hash_write_unlock(handle->method_hash); - - ks_log(KS_LOG_DEBUG, "Message %s registered (%s)\n", command, fqcommand); - - return KS_STATUS_SUCCESS; -} - -static ks_rpcmessage_callbackpair_t* ks_rpcmessage_find_function_ex(ks_rpcmessaging_handle_t* handle, char *command) -{ - ks_hash_read_lock(handle->method_hash); - - ks_rpcmessage_callbackpair_t* callbacks = ks_hash_search(handle->method_hash, command, KS_UNLOCKED); - - ks_hash_read_unlock(handle->method_hash); - - return callbacks; -} - -KS_DECLARE(jrpc_func_t) ks_rpcmessage_find_function(ks_rpcmessaging_handle_t* handle, const char *command) -{ - char fqcommand[KS_PRCMESSAGE_FQCOMMAND_LENGTH]; - memset(fqcommand, 0, sizeof(fqcommand)); - - if (handle->namespace[0] != 0) { - strcpy(fqcommand, handle->namespace); - strcat(fqcommand, command); - } - else { - strcpy(fqcommand, command); - } - - - ks_rpcmessage_callbackpair_t* callbacks = ks_rpcmessage_find_function_ex(handle, (char *)fqcommand); - - if (!callbacks) { - return NULL; - } - - return callbacks->request_func; -} - -KS_DECLARE(jrpc_resp_func_t) ks_rpcmessage_find_response_function(ks_rpcmessaging_handle_t* handle, const char *command) -{ - char fqcommand[KS_PRCMESSAGE_FQCOMMAND_LENGTH]; - memset(fqcommand, 0, sizeof(fqcommand)); - - if (handle->namespace[0] != 0) { - strcpy(fqcommand, handle->namespace); - strcat(fqcommand, command); - } - else { - strcpy(fqcommand, command); - } - - ks_rpcmessage_callbackpair_t* callbacks = ks_rpcmessage_find_function_ex(handle, (char *)fqcommand); - - return callbacks->response_func; -} - - -KS_DECLARE(ks_status_t) ks_rpcmessage_process_jsonmessage(ks_rpcmessaging_handle_t* handle, cJSON *request, cJSON **responseP) -{ - const char *command = cJSON_GetObjectCstr(request, "method"); - cJSON *error = NULL; - cJSON *response = NULL; - *responseP = NULL; - - if (!command) { - error = cJSON_CreateString("Command not found"); - } - - //todo - add more checks ? - - if (error) { - ks_rpcmessage_create_request(handle, 0, &error, &response); - return KS_STATUS_FAIL; - } - - - ks_rpcmessage_callbackpair_t* callbacks = ks_rpcmessage_find_function_ex(handle, (char *)command); - - if (!callbacks) { - error = cJSON_CreateString("Command not supported"); - return KS_STATUS_FAIL; - } - - ks_bool_t isrequest = ks_rpcmessage_isrequest(request); - - if (isrequest && callbacks->request_func) { - return callbacks->request_func(handle, request, responseP); - } - else if (!isrequest && callbacks->response_func) { - return callbacks->response_func(handle, request); - } - - return KS_STATUS_FAIL; -} - - - -KS_DECLARE(ks_status_t) ks_rpcmessage_process_message(ks_rpcmessaging_handle_t* handle, - uint8_t *data, - ks_size_t size, - cJSON **responseP) -{ - cJSON *response = NULL; - cJSON *error = NULL; - - cJSON *request = cJSON_Parse((char*)data); - - if (!request) { - error = cJSON_CreateString("Invalid json format"); - ks_rpcmessage_create_request(handle, 0, &error, &response); - return KS_STATUS_FAIL; - } - - ks_status_t status = ks_rpcmessage_process_jsonmessage(handle, request, responseP); - - cJSON_Delete(request); - - return status; -} - - /* For Emacs: * Local Variables: diff --git a/libs/libks/test/testmessages.c b/libs/libks/test/testmessages.c index 689173f44d..1849a58494 100644 --- a/libs/libks/test/testmessages.c +++ b/libs/libks/test/testmessages.c @@ -8,170 +8,65 @@ ks_pool_t *pool; -ks_thread_pool_t *tpool; - -ks_rpcmessaging_handle_t *h; - -static ks_thread_t *threads[10]; - -static char idbuffer[51]; - - -static ks_status_t process_wombat_response(ks_rpcmessaging_handle_t* handle, cJSON *msg) -{ - printf("entering process_wombat_response\n"); - printf("exiting process_wombat_response\n"); - return KS_STATUS_FAIL; -} - -static ks_status_t process_wombat(ks_rpcmessaging_handle_t* handle, cJSON *msg, cJSON **response) -{ - printf("entering process_wombat\n"); - - char *b0 = cJSON_Print(msg); - printf("Request: %s\n", b0); - free(b0); - - cJSON *msg_id = cJSON_GetObjectItem(msg, "id"); - if (msg_id) { - if (msg_id->type == cJSON_Number) { - printf("Number int %d double %f\n", msg_id->valueint, msg_id->valuedouble); - } - } - - cJSON *respvalue = cJSON_CreateNumber(1); - - ks_rpcmessage_id msgid = ks_rpcmessage_create_response(h, msg, &respvalue, response); - - char *b1 = cJSON_Print(*response); //(*response); - printf("Response: msgid %d\n%s\n", msgid, b1); - free(b1); - - printf("exiting process_wombat\n"); - - return KS_STATUS_SUCCESS; -} - -static ks_status_t process_badbunny(ks_rpcmessaging_handle_t* handle, cJSON *msg, cJSON **response) -{ - printf("entering process_badbunny\n"); - - char *b0 = cJSON_Print(msg); - printf("Request: %s\n", b0); - free(b0); - - cJSON *msg_id = cJSON_GetObjectItem(msg, "id"); - if (msg_id) { - if (msg_id->type == cJSON_Number) { - printf("Number int %d double %f\n", msg_id->valueint, msg_id->valuedouble); - } - } - - cJSON *respvalue; - - ks_rpcmessage_id msgid = ks_rpcmessage_create_errorresponse(h, msg, &respvalue, response); - - char *b2 = cJSON_Print(*response); - printf("Request: msgid %d\n%s\n", msgid, b2); - free(b2); - - //cJSON *respvalue = cJSON_CreateNumber(1); - - - char *b1 = cJSON_Print(*response); //(*response); - printf("Response: %s\n", b1); - free(b1); - - printf("exiting process_badbunny\n"); - - - return KS_STATUS_SUCCESS; -} void test01() { - printf("**** testrpcmessages - test01 start\n"); fflush(stdout); + printf("**** testrpcmessages - test01 start\n\n"); fflush(stdout); - ks_rpcmessage_register_function(h, "wombat", process_wombat, process_wombat_response); - ks_rpcmessage_register_function(h, "badbunny", process_badbunny, NULL); - ks_rpcmessage_register_function(h, "onewaywombat", NULL, process_wombat_response); + cJSON* request1 = NULL; + cJSON* parms1 = NULL; + cJSON* response1 = NULL; - cJSON* request = NULL; - cJSON* parms = NULL; - cJSON* response = NULL; - - /* try an invalid message */ - - ks_rpcmessage_id msgid = ks_rpcmessage_create_request(h, "colm", &parms, &request); - if (msgid != 0) { - printf("invalid message created %d\n", msgid); - printf("request:\n%s\n", cJSON_Print(request)); + /*namespace, method, sessionid, version, params, **request */ + ks_rpcmessage_id msgid = ks_rpcmessage_create_request("app1", "func1", "s001", "1.0", &parms1, &request1); + if (msgid == 0) { + printf("message create failed %d\n", msgid); } - /* try a basic message */ - - msgid = ks_rpcmessage_create_request(h, "wombat", &parms, &request); - if (msgid == 0) { - printf("failed to create a wombat\n"); - return; - } - - cJSON_AddStringToObject(parms, "hello", "cruel world"); - char* data = cJSON_PrintUnformatted(request); + cJSON_AddStringToObject(parms1, "hello", "cruel world"); + char* data = cJSON_PrintUnformatted(request1); - printf("\ntest01 request: %d\n%s\n\n", msgid, data); + printf("test01 request1: %d\n%s\n\n", msgid, data); + ks_json_pool_free(data); - /* process message */ + + /* convert to buffer */ + cJSON* parms2 = NULL; + ks_buffer_t *buffer; + + ks_buffer_create(&buffer, 256, 256, 1024); + + ks_size_t n = ks_rpc_create_buffer("app2", "func2", "s002", "1.1", &parms2, buffer); + + ks_size_t size = ks_buffer_len(buffer); + char *b = (char *)ks_pool_alloc(pool, size+1); + ks_buffer_read(buffer, b, size); - ks_size_t size = strlen(data); - ks_status_t status = ks_rpcmessage_process_message(h, (uint8_t*)data, size, &response); - - char* data1 = cJSON_Print(response); - ks_size_t size1 = strlen(data1); - printf("\ntest01i response: %d\n%s\n\n", msgid, data1); + printf("test01 request2: %d %d from ks_buffer\n%s\n\n\n", n, size, b); - /* process response */ - ks_status_t status1 = ks_rpcmessage_process_message(h, (uint8_t*)data1, size1, &response); - - free(data); - free(data1); - cJSON_Delete(request); - - /* create message 2 */ + /* create message 3 */ - cJSON *parms1 = cJSON_CreateNumber(1); - cJSON *request1 = NULL; + cJSON *parms3 = cJSON_CreateNumber(1); + cJSON *request3 = NULL; - msgid = ks_rpcmessage_create_request(h, "badbunny", &parms1, &request1); - - data = cJSON_PrintUnformatted(request1); + msgid = ks_rpcmessage_create_request("app1", "badbunny", "s002", "1.1", &parms3, &request3); + data = cJSON_PrintUnformatted(request3); printf("\ntest01i request: %d\n%s\n\n", msgid, data); - /* process message 2 */ + cJSON *response3 = NULL; + cJSON *reply3 = NULL; - size = strlen(data); - status = ks_rpcmessage_process_message(h, (uint8_t*)data, size, &response); + ks_rpcmessage_create_response(request3, &reply3, &response3); + + data = cJSON_PrintUnformatted(response3); + printf("\ntest01 response3: %d\n%s\n\n", msgid, data); - data1 = cJSON_PrintUnformatted(response); - printf("\ntest01 response: %d\n%s\n\n", msgid, data1); - - /* process response 2 - no handler so this should fail */ - - size1 = strlen(data1); - - status = ks_rpcmessage_process_message(h, (uint8_t*)data1, size1, &response); + ks_json_pool_free(data); + cJSON_Delete(request3); + cJSON_Delete(response3); - if (status != KS_STATUS_FAIL) { - printf("badbunny found a response handler ?\n"); - } - - free(data); - free(data1); - cJSON_Delete(request1); - - printf("**** testrpcmessages - test01 complete\n\n\n"); fflush(stdout); } @@ -186,54 +81,13 @@ void test02() - -/* test06 */ -/* ------ */ - -static void *testnodelocking_ex1(ks_thread_t *thread, void *data) -{ - return NULL; -} - -static void *testnodelocking_ex2(ks_thread_t *thread, void *data) -{ - return NULL; -} - - -void test06() -{ - printf("**** testmessages - test06 start\n"); fflush(stdout); - - ks_thread_t *t0; - ks_thread_create(&t0, testnodelocking_ex1, NULL, pool); - - ks_thread_t *t1; - ks_thread_create(&t1, testnodelocking_ex2, NULL, pool); - - ks_thread_join(t1); - ks_thread_join(t0); - - printf("\n\n* **testmessages - test06 -- threads complete\n\n"); fflush(stdout); - - printf("**** testmessages - test06 start\n"); fflush(stdout); - - return; -} - - - int main(int argc, char *argv[]) { printf("testmessages - start\n"); int tests[100]; - if (argc == 0) { + if (argc == 1) { tests[0] = 1; - tests[1] = 2; - tests[2] = 3; - tests[3] = 4; - tests[4] = 5; } else { for(int tix=1; tix<100 && tix Date: Tue, 7 Feb 2017 20:18:45 -0500 Subject: [PATCH 10/34] FS-9952: Initial rpc application implementation --- libs/libblade/Makefile.am | 4 +- libs/libblade/src/blade_rpcproto.c | 897 +++++++++++++++++++++ libs/libblade/src/include/blade_rpcproto.h | 115 +++ 3 files changed, 1014 insertions(+), 2 deletions(-) create mode 100644 libs/libblade/src/blade_rpcproto.c create mode 100644 libs/libblade/src/include/blade_rpcproto.h diff --git a/libs/libblade/Makefile.am b/libs/libblade/Makefile.am index ee5e5677d0..12ef174b65 100644 --- a/libs/libblade/Makefile.am +++ b/libs/libblade/Makefile.am @@ -12,13 +12,13 @@ libunqlite_la_LIBADD = -lpthread lib_LTLIBRARIES = libblade.la libblade_la_SOURCES = src/blade.c src/blade_stack.c src/blade_peer.c src/blade_service.c src/bpcp.c src/blade_datastore.c -libblade_la_SOURCES += src/blade_message.c +libblade_la_SOURCES += src/blade_message.c src/blade_rpcproto.c libblade_la_CFLAGS = $(AM_CFLAGS) $(AM_CPPFLAGS) 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/blade_service.h -library_include_HEADERS += src/include/bpcp.h src/include/blade_datastore.h src/include/blade_message.h +library_include_HEADERS += src/include/bpcp.h src/include/blade_datastore.h src/include/blade_message.h src/include/blade_rpcproto.h library_include_HEADERS += src/include/unqlite.h test/tap.h tests: libblade.la diff --git a/libs/libblade/src/blade_rpcproto.c b/libs/libblade/src/blade_rpcproto.c new file mode 100644 index 0000000000..823cf1f246 --- /dev/null +++ b/libs/libblade/src/blade_rpcproto.c @@ -0,0 +1,897 @@ +/* + * 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. + */ + +#pragma GCC optimize ("O0") + +#include +#include + +/* + * internal shared structure grounded in global + */ +typedef struct blade_rpc_handle_ex { + + ks_hash_t *namespace_hash; /* hash to namespace methods */ + ks_hash_t *template_hash; /* hash to template methods */ + + ks_hash_t *peer_hash; /* hash to peer structure */ + + ks_q_t *event_queue; + ks_bool_t isactive; + ks_pool_t *pool; + +} blade_rpc_handle_ex_t; + + +typedef struct blade_rpc_namespace_s { + + char name[KS_RPCMESSAGE_NAMESPACE_LENGTH+1]; + char version[KS_RPCMESSAGE_VERSION_LENGTH+1]; /* nnn.nn.nn */ + ks_hash_t *method_hash; /* hash to namespace methods */ + +} blade_rpc_namespace_t; + + + + +blade_rpc_handle_ex_t *g_handle = NULL; + + +/* + * callbacks - from blade_rpc_handle_ex->method_hash + */ + +typedef struct blade_rpc_custom_callbackpair_s +{ + jrpc_prefix_func_t prefix_request_func; + jrpc_postfix_func_t postfix_request_func; + + jrpc_prefix_resp_func_t prefix_response_func; + jrpc_postfix_resp_func_t postfix_response_func; + +} blade_rpc_custom_callbackpair_t; + + + +typedef struct blade_rpc_callbackpair_s +{ + jrpc_func_t request_func; + + jrpc_resp_func_t response_func; + + blade_rpc_custom_callbackpair_t* custom; + + ks_mutex_t *lock; + + uint16_t command_length; + char command[1]; + +} blade_rpc_callbackpair_t; + + + + + +static void blade_rpc_make_fqcommand(const char* namespace, const char *command, char *fscommand) +{ + memset(fscommand, 0, KS_RPCMESSAGE_FQCOMMAND_LENGTH); + sprintf(fscommand, "%s.%s", namespace, command); + + return; +} + +static void blade_rpc_parse_fqcommand(const char* fscommand, char *namespace, char *command) +{ + memset(command, 0, KS_RPCMESSAGE_COMMAND_LENGTH); + memset(namespace, 0, KS_RPCMESSAGE_NAMESPACE_LENGTH); + + uint32_t len = strlen(fscommand); + + assert(len <= KS_RPCMESSAGE_FQCOMMAND_LENGTH); + ks_bool_t dotfound = KS_FALSE; + + for(int i=0, x=0; inamespace_hash, + KS_HASH_MODE_CASE_SENSITIVE, + KS_HASH_FLAG_RWLOCK + KS_HASH_FLAG_DUP_CHECK, // + KS_HASH_FLAG_FREE_VALUE, + pool); + + ks_hash_create(&g_handle->template_hash, + KS_HASH_MODE_CASE_SENSITIVE, + KS_HASH_FLAG_RWLOCK + KS_HASH_FLAG_DUP_CHECK, // + KS_HASH_FLAG_FREE_VALUE, + pool); + + ks_q_create(&g_handle->event_queue, pool, 1024); + + g_handle->pool = pool; + + /* initialize rpc messaging mechanism */ + ks_rpcmessage_init(pool); + + g_handle->isactive = KS_TRUE; + } + return KS_STATUS_SUCCESS; +} + + +KS_DECLARE(ks_status_t) blade_rpc_onconnect(ks_pool_t *pool, blade_peer_t* peer) +{ + + + return KS_STATUS_FAIL; +} + +KS_DECLARE(ks_status_t) blade_rpc_disconnect(blade_peer_t* peer) +{ + + return KS_STATUS_FAIL; +} + + + +/* + * namespace setup + */ + +/* + * function/callback functions + */ +static blade_rpc_callbackpair_t *blade_rpc_find_callbacks_locked(char *namespace, char *command) +{ + blade_rpc_callbackpair_t *callbacks = NULL; + + blade_rpc_namespace_t *n = ks_hash_search(g_handle->namespace_hash, namespace, KS_UNLOCKED); + if (n) { + char fqcommand[KS_RPCMESSAGE_FQCOMMAND_LENGTH+1]; + blade_rpc_make_fqcommand(namespace, command, fqcommand); + + ks_hash_read_lock(n->method_hash); + + callbacks = ks_hash_search(n->method_hash, fqcommand, KS_UNLOCKED); + ks_mutex_lock(callbacks->lock); + + ks_hash_read_lock(n->method_hash); + } + + return callbacks; +} + +static blade_rpc_callbackpair_t *blade_rpc_find_template_locked(char *name, char *command) +{ + blade_rpc_callbackpair_t *callbacks = NULL; + + blade_rpc_namespace_t *n = ks_hash_search(g_handle->template_hash, name, KS_UNLOCKED); + if (n) { + + ks_hash_read_lock(n->method_hash); + callbacks = ks_hash_search(n->method_hash, command, KS_UNLOCKED); + ks_mutex_lock(callbacks->lock); + + ks_hash_read_lock(n->method_hash); + } + + return callbacks; +} + + + + +static blade_rpc_callbackpair_t *blade_rpc_find_callbacks_locked_fq(char *fqcommand) +{ + blade_rpc_callbackpair_t *callbacks = NULL; + + char command[KS_RPCMESSAGE_COMMAND_LENGTH+1]; + char namespace[KS_RPCMESSAGE_NAMESPACE_LENGTH+1]; + + blade_rpc_parse_fqcommand(fqcommand, namespace, command); + + blade_rpc_namespace_t *n = ks_hash_search(g_handle->namespace_hash, namespace, KS_UNLOCKED); + if (n) { + blade_rpc_make_fqcommand(namespace, command, fqcommand); + ks_hash_read_lock(n->method_hash); + + callbacks = ks_hash_search(n->method_hash, fqcommand, KS_UNLOCKED); + ks_mutex_lock(callbacks->lock); + + ks_hash_read_unlock(n->method_hash); + } + + return callbacks; +} + + +KS_DECLARE(jrpc_func_t) blade_rpc_find_request_function(char *fqcommand) +{ + + blade_rpc_callbackpair_t* callbacks = blade_rpc_find_callbacks_locked_fq(fqcommand); + + if (!callbacks) { + return NULL; + } + + jrpc_func_t f = callbacks->request_func; + + ks_mutex_unlock(callbacks->lock); + + return f; +} + +KS_DECLARE(jrpc_resp_func_t) blade_rpc_find_requestprefix_function(char *fqcommand) +{ + + blade_rpc_callbackpair_t* callbacks = blade_rpc_find_callbacks_locked_fq(fqcommand); + + if (!callbacks || !callbacks->custom) { + return NULL; + } + + jrpc_resp_func_t f = callbacks->custom->prefix_request_func; + + ks_mutex_unlock(callbacks->lock); + + return f; +} + +KS_DECLARE(jrpc_resp_func_t) blade_rpc_find_response_function(char *fqcommand) +{ + + blade_rpc_callbackpair_t* callbacks = blade_rpc_find_callbacks_locked_fq(fqcommand); + + if (!callbacks) { + return NULL; + } + + jrpc_resp_func_t f = callbacks->response_func; + + ks_mutex_unlock(callbacks->lock); + + return f; +} + +KS_DECLARE(jrpc_resp_func_t) blade_rpc_find_responseprefix_function(char *fqcommand) +{ + + blade_rpc_callbackpair_t* callbacks = blade_rpc_find_callbacks_locked_fq(fqcommand); + + if (!callbacks || !callbacks->custom) { + return NULL; + } + + jrpc_resp_func_t f = callbacks->custom->prefix_response_func; + + ks_mutex_unlock(callbacks->lock); + + return f; +} + + +KS_DECLARE(ks_status_t) blade_rpc_declare_namespace(char* namespace, const char* version) +{ + + /* find/insert to namespace hash as needed */ + ks_hash_write_lock(g_handle->namespace_hash); + blade_rpc_namespace_t *n = ks_hash_search(g_handle->namespace_hash, namespace, KS_UNLOCKED); + if (n == NULL) { + n = ks_pool_alloc(g_handle->pool, sizeof (blade_rpc_namespace_t) + strlen(namespace) + 1); + strncpy(n->name, namespace, KS_RPCMESSAGE_NAMESPACE_LENGTH); + strncpy(n->version, version, KS_RPCMESSAGE_VERSION_LENGTH); + ks_hash_insert(g_handle->namespace_hash, n->name, n); + } + ks_hash_write_unlock(g_handle->namespace_hash); + + ks_log(KS_LOG_DEBUG, "Setting message namespace value %s, version %s", namespace, version); + + return KS_STATUS_SUCCESS; +} + + +KS_DECLARE(ks_status_t)blade_rpc_register_namespace_function(char *namespace, + char *command, + jrpc_func_t func, + jrpc_resp_func_t respfunc) +{ + if (!func && !respfunc) { + return KS_STATUS_FAIL; + } + + char nskey[KS_RPCMESSAGE_NAMESPACE_LENGTH+1]; + memset(nskey, 0, sizeof(nskey)); + strcpy(nskey, namespace); + + char fqcommand[KS_RPCMESSAGE_FQCOMMAND_LENGTH]; + memset(fqcommand, 0, sizeof(fqcommand)); + + strcpy(fqcommand, namespace); + strcpy(fqcommand, "."); + strcat(fqcommand, command); + + int lkey = strlen(fqcommand)+1; + + if (lkey < 16) { + lkey = 16; + } + + ks_hash_read_lock(g_handle->namespace_hash); /* lock namespace hash */ + + blade_rpc_namespace_t *n = ks_hash_search(g_handle->namespace_hash, nskey, KS_UNLOCKED); + + if (n == NULL) { + ks_hash_read_unlock(g_handle->namespace_hash); + ks_log(KS_LOG_ERROR, "Unable to find %s namespace\n", namespace); + return KS_STATUS_FAIL; + } + + blade_rpc_callbackpair_t* callbacks = blade_rpc_find_callbacks_locked(nskey, command); + + /* just ignore attempt to re register callbacks */ + /* @todo : should this be smarter, allow override ? */ + if (callbacks != NULL) { + ks_mutex_unlock(callbacks->lock); + ks_hash_read_unlock(g_handle->namespace_hash); + ks_log(KS_LOG_ERROR, "Callbacks already registered for %s namespace\n", namespace); + return KS_STATUS_FAIL; + } + + callbacks = + (blade_rpc_callbackpair_t*)ks_pool_alloc(g_handle->pool, lkey + sizeof(blade_rpc_callbackpair_t)); + + strcpy(callbacks->command, command); + callbacks->command_length = lkey; + callbacks->request_func = func; + callbacks->response_func = respfunc; + ks_mutex_create(&callbacks->lock, KS_MUTEX_FLAG_DEFAULT, g_handle->pool); + + ks_hash_write_lock(n->method_hash); /* lock method hash */ + + ks_hash_insert(n->method_hash, callbacks->command, (void *) callbacks); + + ks_hash_write_unlock(n->method_hash); /* unlock method hash */ + ks_hash_read_unlock(g_handle->namespace_hash); /* unlock namespace hash */ + + ks_log(KS_LOG_DEBUG, "Message %s %s registered\n", namespace, command); + + return KS_STATUS_SUCCESS; + +} + + +KS_DECLARE(ks_status_t)blade_rpc_register_prefix_request_function(char *namespace, + char *command, + jrpc_prefix_func_t prefix_func, + jrpc_postfix_func_t postfix_func) +{ + ks_status_t s = KS_STATUS_FAIL; + + ks_hash_write_lock(g_handle->namespace_hash); + blade_rpc_callbackpair_t* callbacks = blade_rpc_find_callbacks_locked(namespace, command); + + if (callbacks) { + + if (!callbacks->custom) { + callbacks->custom = + (blade_rpc_custom_callbackpair_t *)ks_pool_alloc(g_handle->pool, sizeof(blade_rpc_custom_callbackpair_t)); + } + + callbacks->custom->prefix_request_func = prefix_func; + callbacks->custom->postfix_request_func = postfix_func; + ks_mutex_unlock(callbacks->lock); + s = KS_STATUS_SUCCESS; + } + + ks_hash_write_unlock(g_handle->namespace_hash); + return s; +} + +KS_DECLARE(ks_status_t)blade_rpc_register_prefix_response_function(char* namespace, + char *command, + jrpc_prefix_resp_func_t prefix_func, + jrpc_postfix_resp_func_t postfix_func) +{ + ks_status_t s = KS_STATUS_FAIL; + + ks_hash_write_lock(g_handle->namespace_hash); + blade_rpc_callbackpair_t *callbacks = blade_rpc_find_callbacks_locked(namespace, command); + + if (callbacks) { + + if (!callbacks->custom) { + callbacks->custom = + (blade_rpc_custom_callbackpair_t *)ks_pool_alloc(g_handle->pool, sizeof(blade_rpc_custom_callbackpair_t)); + } + + callbacks->custom->prefix_response_func = prefix_func; + callbacks->custom->postfix_response_func = postfix_func; + ks_mutex_unlock(callbacks->lock); + s = KS_STATUS_SUCCESS; + } + + ks_hash_write_unlock(g_handle->namespace_hash); + return s; +} + +KS_DECLARE(void) blade_rpc_remove_namespace(char* namespace) +{ + + ks_hash_write_lock(g_handle->namespace_hash); + + blade_rpc_namespace_t *n = ks_hash_search(g_handle->namespace_hash, namespace, KS_UNLOCKED); + + ks_hash_iterator_t* it = ks_hash_first(n->method_hash, KS_HASH_FLAG_RWLOCK); + + while (it) { + + const void *key; + void *value; + ks_ssize_t len = strlen(key); + + ks_hash_this(it, &key, &len, &value); + blade_rpc_callbackpair_t *callbacks = (blade_rpc_callbackpair_t *)value; + + ks_mutex_lock(callbacks->lock); + + if (callbacks->custom) { + ks_pool_free(g_handle->pool, callbacks->custom); + } + + it = ks_hash_next(&it); + ks_hash_remove(n->method_hash, (void *)key); + } + + ks_hash_write_unlock(g_handle->namespace_hash); + + return; +} + + +/* + * template functions + * + */ + +KS_DECLARE(ks_status_t) blade_rpc_declare_template(char* templatename, const char* version) +{ + + /* find/insert to namespace hash as needed */ + ks_hash_write_lock(g_handle->template_hash); + blade_rpc_namespace_t *n = ks_hash_search(g_handle->template_hash, templatename, KS_UNLOCKED); + if (n == NULL) { + n = ks_pool_alloc(g_handle->pool, sizeof (blade_rpc_namespace_t) + strlen(templatename) + 1); + strncpy(n->name, templatename, KS_RPCMESSAGE_NAMESPACE_LENGTH); + strncpy(n->version, version, KS_RPCMESSAGE_VERSION_LENGTH); + ks_hash_insert(g_handle->template_hash, n->name, n); + } + ks_hash_write_unlock(g_handle->template_hash); + + ks_log(KS_LOG_DEBUG, "Declaring application template namespace %s, version %s", templatename, version); + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t)blade_rpc_register_template_function(char *name, + char *command, + jrpc_func_t func, + jrpc_resp_func_t respfunc) +{ + if (!func && !respfunc) { + return KS_STATUS_FAIL; + } + + int lkey = strlen(command)+1; + + if (lkey < 16) { + lkey = 16; + } + + ks_hash_read_lock(g_handle->template_hash); /* lock template hash */ + + blade_rpc_namespace_t *n = ks_hash_search(g_handle->template_hash, name, KS_UNLOCKED); + + if (n == NULL) { + ks_hash_read_unlock(g_handle->template_hash); + ks_log(KS_LOG_ERROR, "Unable to find %s template\n", name); + return KS_STATUS_FAIL; + } + + blade_rpc_callbackpair_t* callbacks = blade_rpc_find_template_locked(name, command); + + /* just ignore attempt to re register callbacks */ + /* as the template may already be in use leading to confusion */ + + if (callbacks != NULL) { + ks_mutex_unlock(callbacks->lock); + ks_hash_read_unlock(g_handle->template_hash); + ks_log(KS_LOG_ERROR, "Callbacks already registered for %s template\n", name); + return KS_STATUS_FAIL; + } + + callbacks = + (blade_rpc_callbackpair_t*)ks_pool_alloc(g_handle->pool, lkey + sizeof(blade_rpc_callbackpair_t)); + + strcpy(callbacks->command, command); + callbacks->command_length = lkey; + callbacks->request_func = func; + callbacks->response_func = respfunc; + + ks_mutex_create(&callbacks->lock, KS_MUTEX_FLAG_DEFAULT, g_handle->pool); + + ks_hash_write_lock(n->method_hash); /* lock method hash */ + + ks_hash_insert(n->method_hash, callbacks->command, (void *) callbacks); + + ks_hash_write_unlock(n->method_hash); /* unlock method hash */ + ks_hash_read_unlock(g_handle->template_hash); /* unlock namespace hash */ + + ks_log(KS_LOG_DEBUG, "Template message %s %s registered\n", name, command); + + return KS_STATUS_SUCCESS; + +} + +KS_DECLARE(ks_status_t)blade_rpc_inherit_template(char *namespace, char* template) +{ + ks_hash_read_lock(g_handle->template_hash); + + char tkey[KS_RPCMESSAGE_NAMESPACE_LENGTH+1]; + memset(tkey, 0, sizeof(tkey)); + strcpy(tkey, template); + + char nskey[KS_RPCMESSAGE_NAMESPACE_LENGTH+1]; + memset(nskey, 0, sizeof(tkey)); + strcpy(nskey, namespace); + + blade_rpc_namespace_t *n = ks_hash_search(g_handle->template_hash, tkey, KS_UNLOCKED); + + if (!n) { + ks_hash_read_unlock(g_handle->template_hash); + ks_log(KS_LOG_ERROR, "Unable to locate template %s\n", template); + return KS_STATUS_FAIL; + } + + ks_hash_read_lock(g_handle->namespace_hash); + + blade_rpc_namespace_t *ns = ks_hash_search(g_handle->namespace_hash, nskey, KS_UNLOCKED); + + if (!ns) { + ks_hash_read_unlock(g_handle->template_hash); + ks_hash_read_unlock(g_handle->namespace_hash); + ks_log(KS_LOG_ERROR, "Unable to locate namespace %s\n", namespace); + return KS_STATUS_FAIL; + } + + ks_hash_write_lock(ns->method_hash); + + ks_hash_iterator_t* it = ks_hash_first(n->method_hash, KS_HASH_FLAG_RWLOCK); + + ks_hash_iterator_t* itfirst = it; + + /* first check that there are no name conflicts */ + while (it) { + + const void *key; + void *value; + ks_ssize_t len = strlen(key); + + ks_hash_this(it, &key, &len, &value); + blade_rpc_callbackpair_t *t_callback = (blade_rpc_callbackpair_t *)value; + + ks_mutex_lock(t_callback->lock); + + char fqcommand[KS_RPCMESSAGE_FQCOMMAND_LENGTH+1]; + blade_rpc_make_fqcommand(namespace, t_callback->command, fqcommand); + blade_rpc_callbackpair_t *ns_callbacks = ks_hash_search(ns->method_hash, fqcommand, KS_UNLOCKED); + + if (ns_callbacks) { /* if something already registered for this function kick the entire inherit */ + ks_hash_read_unlock(g_handle->template_hash); + ks_hash_read_unlock(g_handle->namespace_hash); + ks_hash_read_unlock(ns->method_hash); + ks_mutex_unlock(t_callback->lock); + ks_log(KS_LOG_ERROR, "Implementing template %s in namespace %s rejected. Command %s is ambiguous\n", + template, namespace, t_callback->command); + return KS_STATUS_FAIL; + } + + ks_mutex_unlock(t_callback->lock); + + it = ks_hash_next(&it); + } + + /* ok - if we have got this far then the inherit is problem free */ + + it = itfirst; + + while (it) { + + const void *key; + void *value; + ks_ssize_t len = strlen(key); + + ks_hash_this(it, &key, &len, &value); + blade_rpc_callbackpair_t *t_callback = (blade_rpc_callbackpair_t *)value; + + ks_mutex_lock(t_callback->lock); + + int lkey = t_callback->command_length; + + blade_rpc_callbackpair_t *callbacks = + (blade_rpc_callbackpair_t*)ks_pool_alloc(g_handle->pool, lkey + sizeof(blade_rpc_callbackpair_t)); + + strcpy(callbacks->command, t_callback->command); + callbacks->command_length = lkey; + callbacks->request_func = t_callback->request_func; + callbacks->response_func = t_callback->response_func; + ks_mutex_create(&callbacks->lock, KS_MUTEX_FLAG_DEFAULT, g_handle->pool); + + ks_hash_insert(ns->method_hash, callbacks->command, (void *) callbacks); + + ks_mutex_unlock(t_callback->lock); + + it = ks_hash_next(&it); + } + + + ks_hash_write_lock(ns->method_hash); + + ks_hash_read_unlock(g_handle->namespace_hash); + ks_hash_read_unlock(g_handle->template_hash); + + return KS_STATUS_SUCCESS; +} + + + +/* + * send message + * ------------ + */ + +KS_DECLARE(ks_status_t) blade_rpc_write_data(char *sessionid, char* data, uint32_t size) +{ + + ks_status_t s = KS_STATUS_FAIL; + + // convert to json + cJSON *msg = cJSON_Parse(data); + + if (msg) { + + // ks_status_t blade_peer_message_push(blade_peer_t *peer, void *data, int32_t data_length); + + s = KS_STATUS_SUCCESS; + } + else { + ks_log(KS_LOG_ERROR, "Unable to format outbound message\n"); + } + + + // ks_rpc_write_json + // ks_status_t blade_peer_message_push(blade_peer_t *peer, void *data, int32_t data_length); + return s; +} + + +KS_DECLARE(ks_status_t) blade_rpc_write_json(cJSON* json) +{ + // just push the messages onto the communication manager + // synchronization etc, taken care of by the transport api' + char *data = cJSON_PrintUnformatted(json); + if (data) { + ks_log(KS_LOG_DEBUG, "%s\n", data); + //return blade_rpc_write_data(sessionid, data, strlen(data)); + } + ks_log(KS_LOG_ERROR, "Unable to parse json\n"); + return KS_STATUS_FAIL; +} + + + + +/* + * Transport layer callbacks follow below + * +*/ + + + + +/* + * rpc message processing + */ +static ks_status_t blade_rpc_process_jsonmessage_all(cJSON *request, cJSON **responseP) +{ + const char *fqcommand = cJSON_GetObjectCstr(request, "method"); + cJSON *error = NULL; + cJSON *response = NULL; + *responseP = NULL; + + if (!fqcommand) { + error = cJSON_CreateObject(); + cJSON_AddStringToObject(error, "errormessage", "Command not specified"); + ks_rpcmessage_create_request("rpcprotocol", "unknowncommand", NULL, NULL, &error, responseP); + return KS_STATUS_FAIL; + } + + + char namespace[KS_RPCMESSAGE_NAMESPACE_LENGTH]; + char command[KS_RPCMESSAGE_COMMAND_LENGTH]; + + blade_rpc_parse_fqcommand(fqcommand, namespace, command); + blade_rpc_callbackpair_t* callbacks = blade_rpc_find_callbacks_locked(namespace, command); + + if (!callbacks) { + error = cJSON_CreateObject(); + cJSON_AddStringToObject(error, "errormessage", "Command not supported"); + ks_rpcmessage_create_response(request, &error, responseP); + return KS_STATUS_FAIL; + } + + //todo - add more checks ? + + ks_bool_t isrequest = ks_rpcmessage_isrequest(request); + + ks_status_t s = KS_STATUS_SUCCESS; + + if (isrequest && callbacks->request_func) { + + if (callbacks->custom && callbacks->custom->prefix_request_func) { + s = callbacks->custom->prefix_request_func(request); + } + + if (s == KS_STATUS_SUCCESS) { + s = callbacks->request_func(request, responseP); + } + + if (s == KS_STATUS_SUCCESS && callbacks->custom && callbacks->custom->postfix_request_func) { + s = callbacks->custom->postfix_request_func(request, responseP); + } + + ks_mutex_unlock(callbacks->lock); + + return s; + } + else if (!isrequest && callbacks->response_func) { + + if (callbacks->custom && callbacks->custom->prefix_response_func) { + s = callbacks->custom->prefix_response_func(response); + } + + if (s == KS_STATUS_SUCCESS) { + s = callbacks->response_func(response); + } + + if (s == KS_STATUS_SUCCESS && callbacks->custom && callbacks->custom->postfix_response_func) { + s = callbacks->custom->postfix_response_func(response); + } + + ks_mutex_unlock(callbacks->lock); + + return s; + } + + ks_log(KS_LOG_ERROR, "Unable to find message handler for %s\n", command); + + return KS_STATUS_FAIL; +} + +/* + * +*/ +KS_DECLARE(ks_status_t) blade_rpc_process_jsonmessage(cJSON *request, cJSON **responseP) +{ + ks_status_t respstatus = blade_rpc_process_jsonmessage_all(request, responseP); + cJSON *response = *responseP; + if (respstatus == KS_STATUS_SUCCESS && response != NULL) { + blade_rpc_write_json(response); + } + return respstatus; +} + +KS_DECLARE(ks_status_t) blade_rpc_process_data(const uint8_t *data, + ks_size_t size) +{ + + cJSON *json = cJSON_Parse((const char*)data); + if (json != NULL) { + ks_log( KS_LOG_ERROR, "Unable to parse message\n"); + return KS_STATUS_FAIL; + } + + /* deal with rpc message */ + if (ks_rpcmessage_isrpc(json)) { + cJSON *response = NULL; + ks_status_t respstatus = blade_rpc_process_jsonmessage_all(json, &response); + if (respstatus == KS_STATUS_SUCCESS && response != NULL) { + blade_rpc_write_json(response); + cJSON_Delete(response); + } + return respstatus; + } + + ks_log(KS_LOG_ERROR, "Unable to identify message type\n"); + + return KS_STATUS_FAIL; +} + +KS_DECLARE(ks_status_t) blade_rpc_process_blademessage(blade_message_t *message) +{ + uint8_t* data = NULL; + ks_size_t size = 0; + + blade_message_get(message, (void **)&data, &size); + + if (data && size>0) { + ks_status_t s = blade_rpc_process_data(data, size); + blade_message_discard(&message); + return s; + } + + ks_log(KS_LOG_ERROR, "Message read failed\n"); + return KS_STATUS_FAIL; + +} + + +/* 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_rpcproto.h b/libs/libblade/src/include/blade_rpcproto.h new file mode 100644 index 0000000000..d6a37f397a --- /dev/null +++ b/libs/libblade/src/include/blade_rpcproto.h @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2017, FreeSWITCH 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. + */ + +#ifndef _BLADE_RPCPROTO_H_ +#define _BLADE_RPCPROTO_H_ + +#include +#include + +// temp typedefs to get compile going +//typedef struct blade_peer_s blade_peer_t; +//typedef struct blade_message_s blade_message_t; +//typedef struct blade_event_s blade_event_t; + +#define KS_RPCMESSAGE_NAMESPACE_LENGTH 16 +#define KS_RPCMESSAGE_COMMAND_LENGTH 238 +#define KS_RPCMESSAGE_FQCOMMAND_LENGTH (KS_RPCMESSAGE_NAMESPACE_LENGTH+KS_RPCMESSAGE_COMMAND_LENGTH+1) +#define KS_RPCMESSAGE_VERSION_LENGTH 9 + + +typedef ks_status_t (*jrpc_func_t) (cJSON *request, cJSON **responseP); +typedef ks_status_t (*jrpc_prefix_func_t) (cJSON *request); +typedef ks_status_t (*jrpc_postfix_func_t) (cJSON *response, cJSON **responseP); + +typedef ks_status_t (*jrpc_resp_func_t) (cJSON *response); +typedef ks_status_t (*jrpc_prefix_resp_func_t) (cJSON *response); +typedef ks_status_t (*jrpc_postfix_resp_func_t) (cJSON *response); + + +/* + * setup + * ----- + */ + +KS_DECLARE(ks_status_t) blade_rpc_init(ks_pool_t *pool); +KS_DECLARE(ks_status_t) blade_rpc_runprocess(); + + +/* + * namespace and call back registration + * ------------------------------------ + */ +KS_DECLARE(ks_status_t) blade_rpc_declare_namespace(char* namespace, const char* version); +KS_DECLARE(ks_status_t) blade_rpc_register_function(char* namespace, + char *command, + jrpc_func_t func, + jrpc_resp_func_t respfunc); +KS_DECLARE(ks_status_t) blade_rpc_register_prefix_request_function(char* namespace, + char *command, + jrpc_prefix_func_t prefix_func, + jrpc_postfix_func_t postfix_func); +KS_DECLARE(ks_status_t) blade_rpc_register_prefix_response_function(char *namespace, + char *command, + jrpc_prefix_resp_func_t prefix_func, + jrpc_postfix_resp_func_t postfix_func); +KS_DECLARE(void) blade_rpc_remove_namespace(char* namespace); + + +/* + * peer create/destroy + * ------------------- + */ +KS_DECLARE(ks_status_t) blade_rpc_onconnect(ks_pool_t *pool, blade_peer_t* peer); +KS_DECLARE(ks_status_t) blade_rpc_disconnect(blade_peer_t* peer); + +/* + * send message + * ------------ + */ +KS_DECLARE(ks_status_t) blade_rpc_write(char *sessionid, char* data, uint32_t size); +KS_DECLARE(ks_status_t) blade_rpc_write_json(cJSON* json); + + +/* + * process inbound message + * ----------------------- + */ +KS_DECLARE(ks_status_t) blade_rpc_process_blademessage(blade_message_t *message); +KS_DECLARE(ks_status_t) blade_rpc_process_data(const uint8_t *data, ks_size_t size); + +KS_DECLARE(ks_status_t) blade_rpc_process_jsonmessage(cJSON *request, cJSON **responseP); + + +#endif + From a9945a682b89fb3a9ce2c3b3f9a2bf0dabecd333 Mon Sep 17 00:00:00 2001 From: colm Date: Wed, 8 Feb 2017 15:52:36 -0500 Subject: [PATCH 11/34] FS-9952: Initial rpc application implementation --- libs/libblade/src/blade_rpcproto.c | 128 +++++++++++++-------- libs/libblade/src/include/blade_rpcproto.h | 41 ++++--- 2 files changed, 109 insertions(+), 60 deletions(-) diff --git a/libs/libblade/src/blade_rpcproto.c b/libs/libblade/src/blade_rpcproto.c index 823cf1f246..8f8ac0aedb 100644 --- a/libs/libblade/src/blade_rpcproto.c +++ b/libs/libblade/src/blade_rpcproto.c @@ -73,11 +73,11 @@ blade_rpc_handle_ex_t *g_handle = NULL; typedef struct blade_rpc_custom_callbackpair_s { - jrpc_prefix_func_t prefix_request_func; - jrpc_postfix_func_t postfix_request_func; + jrpc_func_t prefix_request_func; + jrpc_func_t postfix_request_func; - jrpc_prefix_resp_func_t prefix_response_func; - jrpc_postfix_resp_func_t postfix_response_func; + jrpc_func_t prefix_response_func; + jrpc_func_t postfix_response_func; } blade_rpc_custom_callbackpair_t; @@ -85,9 +85,9 @@ typedef struct blade_rpc_custom_callbackpair_s typedef struct blade_rpc_callbackpair_s { - jrpc_func_t request_func; + jrpc_func_t request_func; - jrpc_resp_func_t response_func; + jrpc_func_t response_func; blade_rpc_custom_callbackpair_t* custom; @@ -270,7 +270,7 @@ KS_DECLARE(jrpc_func_t) blade_rpc_find_request_function(char *fqcommand) return f; } -KS_DECLARE(jrpc_resp_func_t) blade_rpc_find_requestprefix_function(char *fqcommand) +KS_DECLARE(jrpc_func_t) blade_rpc_find_requestprefix_function(char *fqcommand) { blade_rpc_callbackpair_t* callbacks = blade_rpc_find_callbacks_locked_fq(fqcommand); @@ -279,14 +279,14 @@ KS_DECLARE(jrpc_resp_func_t) blade_rpc_find_requestprefix_function(char *fqcomma return NULL; } - jrpc_resp_func_t f = callbacks->custom->prefix_request_func; + jrpc_func_t f = callbacks->custom->prefix_request_func; ks_mutex_unlock(callbacks->lock); return f; } -KS_DECLARE(jrpc_resp_func_t) blade_rpc_find_response_function(char *fqcommand) +KS_DECLARE(jrpc_func_t) blade_rpc_find_response_function(char *fqcommand) { blade_rpc_callbackpair_t* callbacks = blade_rpc_find_callbacks_locked_fq(fqcommand); @@ -295,14 +295,14 @@ KS_DECLARE(jrpc_resp_func_t) blade_rpc_find_response_function(char *fqcommand) return NULL; } - jrpc_resp_func_t f = callbacks->response_func; + jrpc_func_t f = callbacks->response_func; ks_mutex_unlock(callbacks->lock); return f; } -KS_DECLARE(jrpc_resp_func_t) blade_rpc_find_responseprefix_function(char *fqcommand) +KS_DECLARE(jrpc_func_t) blade_rpc_find_responseprefix_function(char *fqcommand) { blade_rpc_callbackpair_t* callbacks = blade_rpc_find_callbacks_locked_fq(fqcommand); @@ -311,7 +311,7 @@ KS_DECLARE(jrpc_resp_func_t) blade_rpc_find_responseprefix_function(char *fqcomm return NULL; } - jrpc_resp_func_t f = callbacks->custom->prefix_response_func; + jrpc_func_t f = callbacks->custom->prefix_response_func; ks_mutex_unlock(callbacks->lock); @@ -342,7 +342,7 @@ KS_DECLARE(ks_status_t) blade_rpc_declare_namespace(char* namespace, const char* KS_DECLARE(ks_status_t)blade_rpc_register_namespace_function(char *namespace, char *command, jrpc_func_t func, - jrpc_resp_func_t respfunc) + jrpc_func_t respfunc) { if (!func && !respfunc) { return KS_STATUS_FAIL; @@ -411,8 +411,8 @@ KS_DECLARE(ks_status_t)blade_rpc_register_namespace_function(char *namespace, KS_DECLARE(ks_status_t)blade_rpc_register_prefix_request_function(char *namespace, char *command, - jrpc_prefix_func_t prefix_func, - jrpc_postfix_func_t postfix_func) + jrpc_func_t prefix_func, + jrpc_func_t postfix_func) { ks_status_t s = KS_STATUS_FAIL; @@ -438,8 +438,8 @@ KS_DECLARE(ks_status_t)blade_rpc_register_prefix_request_function(char *namespac KS_DECLARE(ks_status_t)blade_rpc_register_prefix_response_function(char* namespace, char *command, - jrpc_prefix_resp_func_t prefix_func, - jrpc_postfix_resp_func_t postfix_func) + jrpc_func_t prefix_func, + jrpc_func_t postfix_func) { ks_status_t s = KS_STATUS_FAIL; @@ -524,7 +524,7 @@ KS_DECLARE(ks_status_t) blade_rpc_declare_template(char* templatename, const cha KS_DECLARE(ks_status_t)blade_rpc_register_template_function(char *name, char *command, jrpc_func_t func, - jrpc_resp_func_t respfunc) + jrpc_func_t respfunc) { if (!func && !respfunc) { return KS_STATUS_FAIL; @@ -750,17 +750,18 @@ KS_DECLARE(ks_status_t) blade_rpc_write_json(cJSON* json) /* * rpc message processing */ -static ks_status_t blade_rpc_process_jsonmessage_all(cJSON *request, cJSON **responseP) +static ks_status_t blade_rpc_process_jsonmessage_all(cJSON *request) { const char *fqcommand = cJSON_GetObjectCstr(request, "method"); cJSON *error = NULL; cJSON *response = NULL; - *responseP = NULL; + cJSON *responseP = NULL; if (!fqcommand) { error = cJSON_CreateObject(); cJSON_AddStringToObject(error, "errormessage", "Command not specified"); - ks_rpcmessage_create_request("rpcprotocol", "unknowncommand", NULL, NULL, &error, responseP); + ks_rpcmessage_create_request("rpcprotocol", "unknowncommand", NULL, NULL, &error, &responseP); + blade_rpc_write_json(responseP); return KS_STATUS_FAIL; } @@ -774,7 +775,8 @@ static ks_status_t blade_rpc_process_jsonmessage_all(cJSON *request, cJSON **res if (!callbacks) { error = cJSON_CreateObject(); cJSON_AddStringToObject(error, "errormessage", "Command not supported"); - ks_rpcmessage_create_response(request, &error, responseP); + ks_rpcmessage_create_response(request, &error, &responseP); + blade_rpc_write_json(responseP); return KS_STATUS_FAIL; } @@ -782,43 +784,84 @@ static ks_status_t blade_rpc_process_jsonmessage_all(cJSON *request, cJSON **res ks_bool_t isrequest = ks_rpcmessage_isrequest(request); - ks_status_t s = KS_STATUS_SUCCESS; + enum jrpc_status_t jrcs = JRPC_ERROR; if (isrequest && callbacks->request_func) { + cJSON *responseP = NULL; + if (callbacks->custom && callbacks->custom->prefix_request_func) { - s = callbacks->custom->prefix_request_func(request); + jrcs = callbacks->custom->prefix_request_func(request, &responseP); + if (jrcs == JRPC_SEND && responseP) { + blade_rpc_write_json(responseP); + cJSON_Delete(responseP); + responseP = NULL; + } } - if (s == KS_STATUS_SUCCESS) { - s = callbacks->request_func(request, responseP); + if (jrcs != JRPC_ERROR) { + jrcs = callbacks->request_func(request, &responseP); + if (jrcs == JRPC_SEND && responseP) { + blade_rpc_write_json(responseP); + cJSON_Delete(responseP); + responseP = NULL; + } } - if (s == KS_STATUS_SUCCESS && callbacks->custom && callbacks->custom->postfix_request_func) { - s = callbacks->custom->postfix_request_func(request, responseP); + if (jrcs != JRPC_ERROR && callbacks->custom && callbacks->custom->postfix_request_func) { + jrcs = callbacks->custom->postfix_request_func(request, &responseP); + if (jrcs == JRPC_SEND && responseP) { + blade_rpc_write_json(responseP); + cJSON_Delete(responseP); + responseP = NULL; + } } ks_mutex_unlock(callbacks->lock); - return s; + if (jrcs == JRPC_ERROR) { + return KS_STATUS_FAIL; + } + + return KS_STATUS_SUCCESS; } else if (!isrequest && callbacks->response_func) { if (callbacks->custom && callbacks->custom->prefix_response_func) { - s = callbacks->custom->prefix_response_func(response); + jrcs = callbacks->custom->prefix_response_func(response, &responseP); + if (jrcs == JRPC_SEND && responseP) { + blade_rpc_write_json(responseP); + cJSON_Delete(responseP); + responseP = NULL; + } } - if (s == KS_STATUS_SUCCESS) { - s = callbacks->response_func(response); + if (jrcs != JRPC_ERROR) { + jrcs = callbacks->response_func(response, &responseP); + if (jrcs == JRPC_SEND && responseP) { + blade_rpc_write_json(responseP); + cJSON_Delete(responseP); + responseP = NULL; + } } - if (s == KS_STATUS_SUCCESS && callbacks->custom && callbacks->custom->postfix_response_func) { - s = callbacks->custom->postfix_response_func(response); + if (jrcs != JRPC_ERROR && callbacks->custom && callbacks->custom->postfix_response_func) { + jrcs = callbacks->custom->postfix_response_func(response, &responseP); + if (jrcs == JRPC_SEND && responseP) { + blade_rpc_write_json(responseP); + cJSON_Delete(responseP); + responseP = NULL; + } } ks_mutex_unlock(callbacks->lock); - return s; + + if (jrcs == JRPC_ERROR) { + return KS_STATUS_FAIL; + } + + return KS_STATUS_SUCCESS; } ks_log(KS_LOG_ERROR, "Unable to find message handler for %s\n", command); @@ -829,13 +872,9 @@ static ks_status_t blade_rpc_process_jsonmessage_all(cJSON *request, cJSON **res /* * */ -KS_DECLARE(ks_status_t) blade_rpc_process_jsonmessage(cJSON *request, cJSON **responseP) +KS_DECLARE(ks_status_t) blade_rpc_process_jsonmessage(cJSON *request) { - ks_status_t respstatus = blade_rpc_process_jsonmessage_all(request, responseP); - cJSON *response = *responseP; - if (respstatus == KS_STATUS_SUCCESS && response != NULL) { - blade_rpc_write_json(response); - } + ks_status_t respstatus = blade_rpc_process_jsonmessage_all(request); return respstatus; } @@ -851,12 +890,7 @@ KS_DECLARE(ks_status_t) blade_rpc_process_data(const uint8_t *data, /* deal with rpc message */ if (ks_rpcmessage_isrpc(json)) { - cJSON *response = NULL; - ks_status_t respstatus = blade_rpc_process_jsonmessage_all(json, &response); - if (respstatus == KS_STATUS_SUCCESS && response != NULL) { - blade_rpc_write_json(response); - cJSON_Delete(response); - } + ks_status_t respstatus = blade_rpc_process_jsonmessage_all(json); return respstatus; } diff --git a/libs/libblade/src/include/blade_rpcproto.h b/libs/libblade/src/include/blade_rpcproto.h index d6a37f397a..c8bfa2b775 100644 --- a/libs/libblade/src/include/blade_rpcproto.h +++ b/libs/libblade/src/include/blade_rpcproto.h @@ -48,13 +48,14 @@ #define KS_RPCMESSAGE_VERSION_LENGTH 9 -typedef ks_status_t (*jrpc_func_t) (cJSON *request, cJSON **responseP); -typedef ks_status_t (*jrpc_prefix_func_t) (cJSON *request); -typedef ks_status_t (*jrpc_postfix_func_t) (cJSON *response, cJSON **responseP); +enum jrpc_status_t { + JRPC_PASS = 0, + JRPC_SEND, + JRPC_ERROR +}; -typedef ks_status_t (*jrpc_resp_func_t) (cJSON *response); -typedef ks_status_t (*jrpc_prefix_resp_func_t) (cJSON *response); -typedef ks_status_t (*jrpc_postfix_resp_func_t) (cJSON *response); + +typedef enum jrpc_status_t (*jrpc_func_t) (cJSON *request, cJSON **responseP); /* @@ -74,17 +75,31 @@ KS_DECLARE(ks_status_t) blade_rpc_declare_namespace(char* namespace, const char* KS_DECLARE(ks_status_t) blade_rpc_register_function(char* namespace, char *command, jrpc_func_t func, - jrpc_resp_func_t respfunc); + jrpc_func_t respfunc); KS_DECLARE(ks_status_t) blade_rpc_register_prefix_request_function(char* namespace, char *command, - jrpc_prefix_func_t prefix_func, - jrpc_postfix_func_t postfix_func); + jrpc_func_t prefix_func, + jrpc_func_t postfix_func); KS_DECLARE(ks_status_t) blade_rpc_register_prefix_response_function(char *namespace, char *command, - jrpc_prefix_resp_func_t prefix_func, - jrpc_postfix_resp_func_t postfix_func); + jrpc_func_t prefix_func, + jrpc_func_t postfix_func); KS_DECLARE(void) blade_rpc_remove_namespace(char* namespace); +/* + * template registration and inheritance + * ------------------------------------- + */ +KS_DECLARE(ks_status_t) blade_rpc_declare_template(char* templatename, const char* version); + +KS_DECLARE(ks_status_t)blade_rpc_register_template_function(char *name, + char *command, + jrpc_func_t func, + jrpc_func_t respfunc); + +KS_DECLARE(ks_status_t)blade_rpc_inherit_template(char *namespace, char* template); + + /* * peer create/destroy @@ -97,7 +112,7 @@ KS_DECLARE(ks_status_t) blade_rpc_disconnect(blade_peer_t* peer); * send message * ------------ */ -KS_DECLARE(ks_status_t) blade_rpc_write(char *sessionid, char* data, uint32_t size); +KS_DECLARE(ks_status_t) blade_rpc_write(char *sessionid, char* data, uint32_t size); //uuid_t ? KS_DECLARE(ks_status_t) blade_rpc_write_json(cJSON* json); @@ -108,7 +123,7 @@ KS_DECLARE(ks_status_t) blade_rpc_write_json(cJSON* json); KS_DECLARE(ks_status_t) blade_rpc_process_blademessage(blade_message_t *message); KS_DECLARE(ks_status_t) blade_rpc_process_data(const uint8_t *data, ks_size_t size); -KS_DECLARE(ks_status_t) blade_rpc_process_jsonmessage(cJSON *request, cJSON **responseP); +KS_DECLARE(ks_status_t) blade_rpc_process_jsonmessage(cJSON *request); #endif From b24eb4d7073b69002f5ddbcf9ced4ad6c06e3ef8 Mon Sep 17 00:00:00 2001 From: colm Date: Wed, 8 Feb 2017 21:49:09 -0500 Subject: [PATCH 12/34] FS-9952: Initial rpc application implementation --- libs/libblade/src/blade_rpcproto.c | 135 +++++++++++---------- libs/libblade/src/include/blade_rpcproto.h | 4 +- 2 files changed, 74 insertions(+), 65 deletions(-) diff --git a/libs/libblade/src/blade_rpcproto.c b/libs/libblade/src/blade_rpcproto.c index 8f8ac0aedb..c2b811939c 100644 --- a/libs/libblade/src/blade_rpcproto.c +++ b/libs/libblade/src/blade_rpcproto.c @@ -124,7 +124,7 @@ static void blade_rpc_parse_fqcommand(const char* fscommand, char *namespace, ch if (fscommand[i] == '.' && dotfound == KS_FALSE) { dotfound = KS_TRUE; - x = 0; + x = -1; } else if (dotfound == KS_FALSE) { namespace[x] = fscommand[i]; @@ -142,7 +142,6 @@ static void blade_rpc_parse_fqcommand(const char* fscommand, char *namespace, ch KS_DECLARE(ks_status_t) blade_rpc_init(ks_pool_t *pool) { - if (g_handle == NULL) { g_handle = ks_pool_alloc(pool, sizeof(blade_rpc_handle_ex_t)); ks_hash_create(&g_handle->namespace_hash, @@ -196,15 +195,16 @@ static blade_rpc_callbackpair_t *blade_rpc_find_callbacks_locked(char *namespace blade_rpc_namespace_t *n = ks_hash_search(g_handle->namespace_hash, namespace, KS_UNLOCKED); if (n) { - char fqcommand[KS_RPCMESSAGE_FQCOMMAND_LENGTH+1]; - blade_rpc_make_fqcommand(namespace, command, fqcommand); ks_hash_read_lock(n->method_hash); - callbacks = ks_hash_search(n->method_hash, fqcommand, KS_UNLOCKED); - ks_mutex_lock(callbacks->lock); + callbacks = ks_hash_search(n->method_hash, command, KS_UNLOCKED); - ks_hash_read_lock(n->method_hash); + if (callbacks) { + ks_mutex_lock(callbacks->lock); + } + + ks_hash_read_unlock(n->method_hash); } return callbacks; @@ -219,9 +219,12 @@ static blade_rpc_callbackpair_t *blade_rpc_find_template_locked(char *name, char ks_hash_read_lock(n->method_hash); callbacks = ks_hash_search(n->method_hash, command, KS_UNLOCKED); - ks_mutex_lock(callbacks->lock); - ks_hash_read_lock(n->method_hash); + if (callbacks) { + ks_mutex_lock(callbacks->lock); + } + + ks_hash_read_unlock(n->method_hash); } return callbacks; @@ -245,7 +248,10 @@ static blade_rpc_callbackpair_t *blade_rpc_find_callbacks_locked_fq(char *fqcomm ks_hash_read_lock(n->method_hash); callbacks = ks_hash_search(n->method_hash, fqcommand, KS_UNLOCKED); - ks_mutex_lock(callbacks->lock); + + if (callbacks) { + ks_mutex_lock(callbacks->lock); + } ks_hash_read_unlock(n->method_hash); } @@ -329,17 +335,21 @@ KS_DECLARE(ks_status_t) blade_rpc_declare_namespace(char* namespace, const char* n = ks_pool_alloc(g_handle->pool, sizeof (blade_rpc_namespace_t) + strlen(namespace) + 1); strncpy(n->name, namespace, KS_RPCMESSAGE_NAMESPACE_LENGTH); strncpy(n->version, version, KS_RPCMESSAGE_VERSION_LENGTH); + ks_hash_create(&n->method_hash, + KS_HASH_MODE_CASE_SENSITIVE, + KS_HASH_FLAG_RWLOCK + KS_HASH_FLAG_DUP_CHECK + KS_HASH_FLAG_FREE_VALUE, + g_handle->pool); ks_hash_insert(g_handle->namespace_hash, n->name, n); } ks_hash_write_unlock(g_handle->namespace_hash); - ks_log(KS_LOG_DEBUG, "Setting message namespace value %s, version %s", namespace, version); + ks_log(KS_LOG_DEBUG, "Setting message namespace value %s, version %s\n", namespace, version); return KS_STATUS_SUCCESS; } -KS_DECLARE(ks_status_t)blade_rpc_register_namespace_function(char *namespace, +KS_DECLARE(ks_status_t)blade_rpc_register_function(char *namespace, char *command, jrpc_func_t func, jrpc_func_t respfunc) @@ -355,11 +365,11 @@ KS_DECLARE(ks_status_t)blade_rpc_register_namespace_function(char *namespace, char fqcommand[KS_RPCMESSAGE_FQCOMMAND_LENGTH]; memset(fqcommand, 0, sizeof(fqcommand)); - strcpy(fqcommand, namespace); - strcpy(fqcommand, "."); +// strcpy(fqcommand, namespace); +// strcpy(fqcommand, "."); strcat(fqcommand, command); - int lkey = strlen(fqcommand)+1; + int lkey = strlen(command)+1; if (lkey < 16) { lkey = 16; @@ -409,15 +419,19 @@ KS_DECLARE(ks_status_t)blade_rpc_register_namespace_function(char *namespace, } -KS_DECLARE(ks_status_t)blade_rpc_register_prefix_request_function(char *namespace, +KS_DECLARE(ks_status_t)blade_rpc_register_custom_request_function(char *namespace, char *command, jrpc_func_t prefix_func, jrpc_func_t postfix_func) { ks_status_t s = KS_STATUS_FAIL; + char fqcommand[KS_RPCMESSAGE_FQCOMMAND_LENGTH]; + memset(fqcommand, 0, sizeof(fqcommand)); + strcat(fqcommand, command); + ks_hash_write_lock(g_handle->namespace_hash); - blade_rpc_callbackpair_t* callbacks = blade_rpc_find_callbacks_locked(namespace, command); + blade_rpc_callbackpair_t* callbacks = blade_rpc_find_callbacks_locked(namespace, fqcommand); if (callbacks) { @@ -436,15 +450,19 @@ KS_DECLARE(ks_status_t)blade_rpc_register_prefix_request_function(char *namespac return s; } -KS_DECLARE(ks_status_t)blade_rpc_register_prefix_response_function(char* namespace, +KS_DECLARE(ks_status_t)blade_rpc_register_custom_response_function(char* namespace, char *command, jrpc_func_t prefix_func, jrpc_func_t postfix_func) { ks_status_t s = KS_STATUS_FAIL; + + char fqcommand[KS_RPCMESSAGE_FQCOMMAND_LENGTH]; + memset(fqcommand, 0, sizeof(fqcommand)); + strcat(fqcommand, command); ks_hash_write_lock(g_handle->namespace_hash); - blade_rpc_callbackpair_t *callbacks = blade_rpc_find_callbacks_locked(namespace, command); + blade_rpc_callbackpair_t *callbacks = blade_rpc_find_callbacks_locked(namespace, fqcommand); if (callbacks) { @@ -465,10 +483,13 @@ KS_DECLARE(ks_status_t)blade_rpc_register_prefix_response_function(char* namespa KS_DECLARE(void) blade_rpc_remove_namespace(char* namespace) { + char nskey[KS_RPCMESSAGE_NAMESPACE_LENGTH]; + memset(nskey, 0, sizeof(nskey)); + strcat(nskey, namespace); ks_hash_write_lock(g_handle->namespace_hash); - blade_rpc_namespace_t *n = ks_hash_search(g_handle->namespace_hash, namespace, KS_UNLOCKED); + blade_rpc_namespace_t *n = ks_hash_search(g_handle->namespace_hash, nskey, KS_UNLOCKED); ks_hash_iterator_t* it = ks_hash_first(n->method_hash, KS_HASH_FLAG_RWLOCK); @@ -504,19 +525,28 @@ KS_DECLARE(void) blade_rpc_remove_namespace(char* namespace) KS_DECLARE(ks_status_t) blade_rpc_declare_template(char* templatename, const char* version) { + char nskey[KS_RPCMESSAGE_NAMESPACE_LENGTH]; + memset(nskey, 0, sizeof(nskey)); + strcat(nskey, templatename); + /* find/insert to namespace hash as needed */ ks_hash_write_lock(g_handle->template_hash); - blade_rpc_namespace_t *n = ks_hash_search(g_handle->template_hash, templatename, KS_UNLOCKED); + blade_rpc_namespace_t *n = ks_hash_search(g_handle->template_hash, nskey, KS_UNLOCKED); + if (n == NULL) { n = ks_pool_alloc(g_handle->pool, sizeof (blade_rpc_namespace_t) + strlen(templatename) + 1); strncpy(n->name, templatename, KS_RPCMESSAGE_NAMESPACE_LENGTH); strncpy(n->version, version, KS_RPCMESSAGE_VERSION_LENGTH); + ks_hash_create(&n->method_hash, + KS_HASH_MODE_CASE_SENSITIVE, + KS_HASH_FLAG_RWLOCK + KS_HASH_FLAG_DUP_CHECK + KS_HASH_FLAG_FREE_VALUE, + g_handle->pool); ks_hash_insert(g_handle->template_hash, n->name, n); } ks_hash_write_unlock(g_handle->template_hash); - ks_log(KS_LOG_DEBUG, "Declaring application template namespace %s, version %s", templatename, version); + ks_log(KS_LOG_DEBUG, "Declaring application template namespace %s, version %s\n", templatename, version); return KS_STATUS_SUCCESS; } @@ -526,11 +556,21 @@ KS_DECLARE(ks_status_t)blade_rpc_register_template_function(char *name, jrpc_func_t func, jrpc_func_t respfunc) { + (void)blade_rpc_find_template_locked; //remove + if (!func && !respfunc) { return KS_STATUS_FAIL; } - int lkey = strlen(command)+1; + char nskey[KS_RPCMESSAGE_NAMESPACE_LENGTH]; + memset(nskey, 0, sizeof(nskey)); + strcat(nskey, name); + + char fqcommand[KS_RPCMESSAGE_FQCOMMAND_LENGTH]; + memset(fqcommand, 0, sizeof(fqcommand)); + strcat(fqcommand, command); + + int lkey = strlen(fqcommand)+1; if (lkey < 16) { lkey = 16; @@ -538,7 +578,7 @@ KS_DECLARE(ks_status_t)blade_rpc_register_template_function(char *name, ks_hash_read_lock(g_handle->template_hash); /* lock template hash */ - blade_rpc_namespace_t *n = ks_hash_search(g_handle->template_hash, name, KS_UNLOCKED); + blade_rpc_namespace_t *n = ks_hash_search(g_handle->template_hash, nskey, KS_UNLOCKED); if (n == NULL) { ks_hash_read_unlock(g_handle->template_hash); @@ -546,7 +586,9 @@ KS_DECLARE(ks_status_t)blade_rpc_register_template_function(char *name, return KS_STATUS_FAIL; } - blade_rpc_callbackpair_t* callbacks = blade_rpc_find_template_locked(name, command); + ks_hash_read_lock(n->method_hash); + blade_rpc_callbackpair_t* callbacks = ks_hash_search(n->method_hash, fqcommand, KS_UNLOCKED); + ks_hash_read_unlock(n->method_hash); /* just ignore attempt to re register callbacks */ /* as the template may already be in use leading to confusion */ @@ -616,43 +658,6 @@ KS_DECLARE(ks_status_t)blade_rpc_inherit_template(char *namespace, char* templat ks_hash_iterator_t* it = ks_hash_first(n->method_hash, KS_HASH_FLAG_RWLOCK); - ks_hash_iterator_t* itfirst = it; - - /* first check that there are no name conflicts */ - while (it) { - - const void *key; - void *value; - ks_ssize_t len = strlen(key); - - ks_hash_this(it, &key, &len, &value); - blade_rpc_callbackpair_t *t_callback = (blade_rpc_callbackpair_t *)value; - - ks_mutex_lock(t_callback->lock); - - char fqcommand[KS_RPCMESSAGE_FQCOMMAND_LENGTH+1]; - blade_rpc_make_fqcommand(namespace, t_callback->command, fqcommand); - blade_rpc_callbackpair_t *ns_callbacks = ks_hash_search(ns->method_hash, fqcommand, KS_UNLOCKED); - - if (ns_callbacks) { /* if something already registered for this function kick the entire inherit */ - ks_hash_read_unlock(g_handle->template_hash); - ks_hash_read_unlock(g_handle->namespace_hash); - ks_hash_read_unlock(ns->method_hash); - ks_mutex_unlock(t_callback->lock); - ks_log(KS_LOG_ERROR, "Implementing template %s in namespace %s rejected. Command %s is ambiguous\n", - template, namespace, t_callback->command); - return KS_STATUS_FAIL; - } - - ks_mutex_unlock(t_callback->lock); - - it = ks_hash_next(&it); - } - - /* ok - if we have got this far then the inherit is problem free */ - - it = itfirst; - while (it) { const void *key; @@ -669,7 +674,10 @@ KS_DECLARE(ks_status_t)blade_rpc_inherit_template(char *namespace, char* templat blade_rpc_callbackpair_t *callbacks = (blade_rpc_callbackpair_t*)ks_pool_alloc(g_handle->pool, lkey + sizeof(blade_rpc_callbackpair_t)); - strcpy(callbacks->command, t_callback->command); + strcat(callbacks->command, template); + strcat(callbacks->command, "."); + strcat(callbacks->command, t_callback->command); + callbacks->command_length = lkey; callbacks->request_func = t_callback->request_func; callbacks->response_func = t_callback->response_func; @@ -731,6 +739,7 @@ KS_DECLARE(ks_status_t) blade_rpc_write_json(cJSON* json) if (data) { ks_log(KS_LOG_DEBUG, "%s\n", data); //return blade_rpc_write_data(sessionid, data, strlen(data)); + return KS_STATUS_SUCCESS; } ks_log(KS_LOG_ERROR, "Unable to parse json\n"); return KS_STATUS_FAIL; diff --git a/libs/libblade/src/include/blade_rpcproto.h b/libs/libblade/src/include/blade_rpcproto.h index c8bfa2b775..c93c3805f5 100644 --- a/libs/libblade/src/include/blade_rpcproto.h +++ b/libs/libblade/src/include/blade_rpcproto.h @@ -76,11 +76,11 @@ KS_DECLARE(ks_status_t) blade_rpc_register_function(char* namespace, char *command, jrpc_func_t func, jrpc_func_t respfunc); -KS_DECLARE(ks_status_t) blade_rpc_register_prefix_request_function(char* namespace, +KS_DECLARE(ks_status_t) blade_rpc_register_custom_request_function(char* namespace, char *command, jrpc_func_t prefix_func, jrpc_func_t postfix_func); -KS_DECLARE(ks_status_t) blade_rpc_register_prefix_response_function(char *namespace, +KS_DECLARE(ks_status_t) blade_rpc_register_custom_response_function(char *namespace, char *command, jrpc_func_t prefix_func, jrpc_func_t postfix_func); From c3b7bb583f497157b31ac46294dbdc6d30ddd3a5 Mon Sep 17 00:00:00 2001 From: Shane Bryldt Date: Thu, 9 Feb 2017 17:08:07 +0000 Subject: [PATCH 13/34] FS-9952: Rewrote core code to utilize state machine driven system based on discussions, code compiles but completely untested currently --- libs/libblade/Makefile.am | 6 +- libs/libblade/src/blade_connection.c | 251 +++++++ libs/libblade/src/blade_identity.c | 100 +++ libs/libblade/src/blade_module.c | 98 +++ libs/libblade/src/blade_module_wss.c | 655 ++++++++++++++++++ libs/libblade/src/blade_peer.c | 278 -------- libs/libblade/src/blade_service.c | 428 ------------ libs/libblade/src/blade_stack.c | 31 +- libs/libblade/src/include/blade.h | 6 +- libs/libblade/src/include/blade_connection.h | 67 ++ .../{blade_service.h => blade_identity.h} | 20 +- .../include/{blade_peer.h => blade_module.h} | 18 +- libs/libblade/src/include/blade_stack.h | 6 +- libs/libblade/src/include/blade_types.h | 83 ++- libs/libblade/test/bladec | 228 ------ 15 files changed, 1275 insertions(+), 1000 deletions(-) create mode 100644 libs/libblade/src/blade_connection.c create mode 100644 libs/libblade/src/blade_identity.c create mode 100644 libs/libblade/src/blade_module.c create mode 100644 libs/libblade/src/blade_module_wss.c delete mode 100644 libs/libblade/src/blade_peer.c delete mode 100644 libs/libblade/src/blade_service.c create mode 100644 libs/libblade/src/include/blade_connection.h rename libs/libblade/src/include/{blade_service.h => blade_identity.h} (70%) rename libs/libblade/src/include/{blade_peer.h => blade_module.h} (67%) delete mode 100755 libs/libblade/test/bladec diff --git a/libs/libblade/Makefile.am b/libs/libblade/Makefile.am index 12ef174b65..c9343ff9fc 100644 --- a/libs/libblade/Makefile.am +++ b/libs/libblade/Makefile.am @@ -11,14 +11,16 @@ 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/blade_service.c src/bpcp.c src/blade_datastore.c +libblade_la_SOURCES = src/blade.c src/blade_stack.c src/bpcp.c src/blade_datastore.c libblade_la_SOURCES += src/blade_message.c src/blade_rpcproto.c +libblade_la_SOURCES += src/blade_identity.c src/blade_module.c src/blade_connection.c src/blade_module_wss.c libblade_la_CFLAGS = $(AM_CFLAGS) $(AM_CPPFLAGS) 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/blade_service.h +library_include_HEADERS = src/include/blade.h src/include/blade_types.h src/include/blade_stack.h library_include_HEADERS += src/include/bpcp.h src/include/blade_datastore.h src/include/blade_message.h src/include/blade_rpcproto.h +library_include_HEADERS += src/include/blade_identity.h src/include/blade_module.h src/include/blade_connection.h library_include_HEADERS += src/include/unqlite.h test/tap.h tests: libblade.la diff --git a/libs/libblade/src/blade_connection.c b/libs/libblade/src/blade_connection.c new file mode 100644 index 0000000000..3ac336697f --- /dev/null +++ b/libs/libblade/src/blade_connection.c @@ -0,0 +1,251 @@ +/* + * 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_data; + blade_transport_callbacks_t *transport_callbacks; + + ks_bool_t shutdown; + // @todo add auto generated UUID + ks_thread_t *state_thread; + blade_connection_state_t state; + + ks_q_t *sending; + ks_q_t *receiving; +}; + +void *blade_connection_state_thread(ks_thread_t *thread, void *data); + + +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) +{ + blade_connection_t *bc = NULL; + ks_pool_t *pool = NULL; + + ks_assert(bcP); + ks_assert(bh); + ks_assert(transport_data); + 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_data = transport_data; + bc->transport_callbacks = transport_callbacks; + ks_q_create(&bc->sending, pool, 0); + ks_q_create(&bc->receiving, pool, 0); + *bcP = bc; + + 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_q_destroy(&bc->receiving); + + ks_pool_free(bc->pool, bcP); + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_connection_startup(blade_connection_t *bc) +{ + ks_assert(bc); + + 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 + blade_connection_disconnect(bc); + return KS_STATUS_FAIL; + } + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_connection_shutdown(blade_connection_t *bc) +{ + 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; + } + + //while (ks_q_trypop(bc->sending, (void **)&message) == KS_STATUS_SUCCESS && message) blade_message_discard(&message); + //while (ks_q_trypop(bc->receiving, (void **)&message) == KS_STATUS_SUCCESS && message) blade_message_discard(&message); + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(void *) blade_connection_transport_get(blade_connection_t *bc) +{ + ks_assert(bc); + + return bc->transport_data; +} + +KS_DECLARE(void) blade_connection_state_set(blade_connection_t *bc, blade_connection_state_t state) +{ + ks_assert(bc); + + bc->transport_callbacks->onstate(bc, state, BLADE_CONNECTION_STATE_CONDITION_PRE); + bc->state = state; +} + +KS_DECLARE(void) blade_connection_disconnect(blade_connection_t *bc) +{ + ks_assert(bc); + + blade_connection_state_set(bc, BLADE_CONNECTION_STATE_DISCONNECT); +} + +KS_DECLARE(ks_status_t) blade_connection_sending_push(blade_connection_t *bc, blade_identity_t *target, cJSON *json) +{ + ks_assert(bc); + ks_assert(json); + + // @todo need internal envelope to wrap an identity object and a json object just for the queue + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_connection_sending_pop(blade_connection_t *bc, blade_identity_t **target, cJSON **json) +{ + ks_assert(bc); + ks_assert(json); + + // @todo need internal envelope to wrap an identity object and a json object just for the queue + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_connection_receiving_push(blade_connection_t *bc, cJSON *json) +{ + ks_assert(bc); + ks_assert(json); + + return ks_q_push(bc->receiving, json); +} + +KS_DECLARE(ks_status_t) blade_connection_receiving_pop(blade_connection_t *bc, cJSON **json) +{ + ks_assert(bc); + ks_assert(json); + + return ks_q_trypop(bc->receiving, (void **)json); +} + +void *blade_connection_state_thread(ks_thread_t *thread, void *data) +{ + blade_connection_t *bc = NULL; + blade_connection_state_hook_t hook; + + ks_assert(thread); + ks_assert(data); + + bc = (blade_connection_t *)data; + + while (!bc->shutdown) { + // @todo need to get messages from the transport into receiving queue, and pop messages from sending queue to write out using transport + // sending is relatively easy, but receiving cannot occur universally due to cases like kws_init() blocking and expecting data to be on the wire + // and other transports may have similar behaviours, but CONNECTIN, ATTACH, and READY require async message passing into application layer + // and sending whenever the response hits the queue + + // @todo it's possible that onstate could handle receiving and sending messages during the appropriate states, but this means some states + // like CONNECTIN which may send and receive multiple messages require BYPASSing until the application layer updates the state or disconnects + + hook = bc->transport_callbacks->onstate(bc, bc->state, 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 pop from sending queue, and pass to transport callback to send out + switch (bc->state) { + case BLADE_CONNECTION_STATE_NEW: + blade_connection_state_set(bc, BLADE_CONNECTION_STATE_CONNECT); + break; + case BLADE_CONNECTION_STATE_CONNECT: + blade_connection_state_set(bc, BLADE_CONNECTION_STATE_ATTACH); + break; + case BLADE_CONNECTION_STATE_ATTACH: + blade_connection_state_set(bc, BLADE_CONNECTION_STATE_READY); + break; + case BLADE_CONNECTION_STATE_DETACH: + blade_connection_disconnect(bc); + break; + default: break; + } + } + } + + return NULL; +} + +/* 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_identity.c b/libs/libblade/src/blade_identity.c new file mode 100644 index 0000000000..3e814e80af --- /dev/null +++ b/libs/libblade/src/blade_identity.c @@ -0,0 +1,100 @@ +/* + * 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; + // @todo breakdown of uri into constituent parts +}; + + +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; + + 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) +{ + ks_assert(bi); + ks_assert(uri); + + if (bi->uri) ks_pool_free(bi->pool, &bi->uri); + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_identity_uri(blade_identity_t *bi, const char **uri) +{ + ks_assert(bi); + ks_assert(uri); + + *uri = bi->uri; + 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_module.c b/libs/libblade/src/blade_module.c new file mode 100644 index 0000000000..a874e45ffc --- /dev/null +++ b/libs/libblade/src/blade_module.c @@ -0,0 +1,98 @@ +/* + * 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(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_wss.c b/libs/libblade/src/blade_module_wss.c new file mode 100644 index 0000000000..3efe4307af --- /dev/null +++ b/libs/libblade/src/blade_module_wss.c @@ -0,0 +1,655 @@ +/* + * 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_ENDPOINTS_MULTIHOME_MAX 16 + +typedef struct blade_module_wss_s blade_module_wss_t; +typedef struct blade_transport_wss_s blade_transport_wss_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; + ks_q_t *disconnected; +}; + +struct blade_transport_wss_s { + blade_module_wss_t *module; + ks_pool_t *pool; + + ks_socket_t sock; + kws_t *kws; +}; + + + +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); + +ks_status_t blade_module_wss_onload(blade_module_t **bmP, blade_handle_t *bh); +ks_status_t blade_module_wss_onunload(blade_module_t *bm); +ks_status_t blade_module_wss_onstartup(blade_module_t *bm, config_setting_t *config); +ks_status_t blade_module_wss_onshutdown(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_onconnect(blade_connection_t **bcP, blade_module_t *bm, blade_identity_t *target); +blade_connection_rank_t blade_transport_wss_onrank(blade_connection_t *bc, blade_identity_t *target); +blade_connection_state_hook_t blade_transport_wss_onstate(blade_connection_t *bc, blade_connection_state_t state, blade_connection_state_condition_t condition); + + + +static blade_module_callbacks_t g_module_wss_callbacks = +{ + blade_module_wss_onload, + blade_module_wss_onunload, + blade_module_wss_onstartup, + blade_module_wss_onshutdown, +}; + +static blade_transport_callbacks_t g_transport_wss_callbacks = +{ + blade_transport_wss_onconnect, + blade_transport_wss_onrank, + blade_transport_wss_onstate, +}; + + + +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_assert(bm_wssP); + ks_assert(bh); + + bm_wss = ks_pool_alloc(bm_wss->pool, sizeof(blade_module_wss_t)); + bm_wss->handle = bh; + bm_wss->pool = blade_handle_pool_get(bh); + 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); + ks_q_create(&bm_wss->disconnected, bm_wss->pool, 0); + ks_assert(bm_wss->disconnected); + + *bm_wssP = bm_wss; + + 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_onshutdown(bm_wss->module); + + blade_module_destroy(&bm_wss->module); + + list_destroy(&bm_wss->connected); + ks_q_destroy(&bm_wss->disconnected); + + ks_pool_free(bm_wss->pool, bm_wssP); + + return KS_STATUS_SUCCESS; +} + +ks_status_t blade_module_wss_onload(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; + + return KS_STATUS_SUCCESS; +} + +ks_status_t blade_module_wss_onunload(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); + + 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) { + ks_log(KS_LOG_DEBUG, "!wss\n"); + return KS_STATUS_FAIL; + } + 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; + + return KS_STATUS_SUCCESS; +} + +ks_status_t blade_module_wss_onstartup(blade_module_t *bm, config_setting_t *config) +{ + blade_module_wss_t *bm_wss = 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; + + return KS_STATUS_SUCCESS; +} + +ks_status_t blade_module_wss_onshutdown(blade_module_t *bm) +{ + blade_module_wss_t *bm_wss = NULL; + blade_transport_wss_t *bt_wss = NULL; + blade_connection_t *bc = NULL; + + ks_assert(bm); + + bm_wss = (blade_module_wss_t *)blade_module_data_get(bm); + + 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; + } + + 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); + + while (ks_q_trypop(bm_wss->disconnected, (void **)&bc) == KS_STATUS_SUCCESS) ; + list_iterator_start(&bm_wss->connected); + while (list_iterator_hasnext(&bm_wss->connected)) { + bc = (blade_connection_t *)list_iterator_next(&bm_wss->connected); + bt_wss = (blade_transport_wss_t *)blade_connection_transport_get(bc); + + blade_connection_destroy(&bc); + blade_transport_wss_destroy(&bt_wss); + } + list_iterator_stop(&bm_wss->connected); + list_clear(&bm_wss->connected); + + 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; + + 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_t *bt_wss = NULL; + blade_connection_t *bc = NULL; + + ks_assert(thread); + ks_assert(data); + + bm_wss = (blade_module_wss_t *)data; + + 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 & POLLIN)) continue; + 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? + 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 + continue; + } + + blade_transport_wss_create(&bt_wss, bm_wss, sock); + ks_assert(bt_wss); + + blade_connection_create(&bc, bm_wss->handle, bt_wss, bm_wss->transport_callbacks); + ks_assert(bc); + + blade_connection_startup(bc); + + list_append(&bm_wss->connected, bc); + + blade_connection_state_set(bc, BLADE_CONNECTION_STATE_NEW); + } + } + + while (ks_q_trypop(bm_wss->disconnected, (void **)&bc) == KS_STATUS_SUCCESS) { + bt_wss = (blade_transport_wss_t *)blade_connection_transport_get(bc); + + list_delete(&bm_wss->connected, bc); + + blade_connection_destroy(&bc); + blade_transport_wss_destroy(&bt_wss); + } + } + + 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; + + 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); + + return KS_STATUS_SUCCESS; +} + +ks_status_t blade_transport_wss_onconnect(blade_connection_t **bcP, blade_module_t *bm, blade_identity_t *target) +{ + ks_assert(bcP); + ks_assert(bm); + ks_assert(target); + + *bcP = NULL; + + // @todo connect-out equivilent of accept + + return KS_STATUS_SUCCESS; +} + +blade_connection_rank_t blade_transport_wss_onrank(blade_connection_t *bc, blade_identity_t *target) +{ + ks_assert(bc); + ks_assert(target); + + return KS_STATUS_SUCCESS; +} + +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) { + // @todo error logging + return KS_STATUS_FAIL; + } + if (poll_flags & KS_POLL_READ) { + kws_opcode_t opcode; + uint8_t *frame_data = NULL; + ks_size_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 + return KS_STATUS_FAIL; + } + + //if (blade_handle_message_claim(blade_service_handle(peer->service), &message, frame_data, frame_data_len) != KS_STATUS_SUCCESS || !message) { + // @todo error logging + // return KS_STATUS_FAIL; + //} + + // @todo convert frame_data to cJSON safely, make sure data is null-terminated at frame_data_len + if (!(*json = cJSON_Parse((char *)frame_data))) { + return KS_STATUS_FAIL; + } + } + return KS_STATUS_SUCCESS; +} + +ks_status_t blade_transport_wss_write(blade_transport_wss_t *bt_wss, cJSON *json) +{ + //blade_message_get(message, &target, &json); + char *json_str = cJSON_PrintUnformatted(json); + ks_size_t json_str_len = 0; + if (!json_str) { + // @todo error logging + return KS_STATUS_FAIL; + } + json_str_len = strlen(json_str) + 1; + kws_write_frame(bt_wss->kws, WSOC_TEXT, json_str, json_str_len); + + return KS_STATUS_SUCCESS; +} + +blade_connection_state_hook_t blade_transport_wss_onstate(blade_connection_t *bc, blade_connection_state_t state, blade_connection_state_condition_t condition) +{ + blade_transport_wss_t *bt_wss = NULL; + //cJSON *json = NULL; + + ks_assert(bc); + + bt_wss = (blade_transport_wss_t *)blade_connection_transport_get(bc); + + switch (state) { + case BLADE_CONNECTION_STATE_DISCONNECT: + { + if (condition == BLADE_CONNECTION_STATE_CONDITION_POST) { + ks_q_push(bt_wss->module->disconnected, bc); + blade_connection_state_set(bc, BLADE_CONNECTION_STATE_NONE); + } + break; + } + case BLADE_CONNECTION_STATE_NEW: + { + if (condition == BLADE_CONNECTION_STATE_CONDITION_POST) { + // @todo: SSL init stuffs based on data from peer->service->config_websockets_ssl to pass into kws_init + if (kws_init(&bt_wss->kws, bt_wss->sock, NULL, NULL, KWS_BLOCK, bt_wss->pool) != KS_STATUS_SUCCESS) { + // @todo error logging + return BLADE_CONNECTION_STATE_HOOK_DISCONNECT; + } + } + break; + } + case BLADE_CONNECTION_STATE_CONNECT: + { + // @todo abstract read message and write message, so these can be called from connection and processed from there + + //if (blade_transport_wss_read(bt_wss, &json) != KS_STATUS_SUCCESS) return BLADE_CONNECTION_STATEHOOK_DISCONNECT; + + //if (json) { + // @todo processing connectin messages for identity registration + // cJSON_Delete(json); + //blade_connection_receiving_push(conn, json); + //} + + // @todo wrap identity + json into an envelope for queueing through the connection + //while (blade_connection_sending_pop(bc, (void **)&json) == KS_STATUS_SUCCESS && json) { + // ks_status_t ret = blade_transport_wss_write(bt_wss, json); + // cJSON_Delete(json); + // if (ret != KS_STATUS_SUCCESS) return BLADE_CONNECTION_STATE_HOOK_DISCONNECT; + //} + return BLADE_CONNECTION_STATE_HOOK_SUCCESS; + //break; + } + default: break; + } + + return BLADE_CONNECTION_STATE_HOOK_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_peer.c b/libs/libblade/src/blade_peer.c deleted file mode 100644 index 5ec3d6ba0d..0000000000 --- a/libs/libblade/src/blade_peer.c +++ /dev/null @@ -1,278 +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" - -struct blade_peer_s { - ks_pool_t *pool; - ks_thread_pool_t *tpool; - blade_service_t *service; - - ks_socket_t sock; - ks_bool_t shutdown; - blade_peerstate_t state; - blade_peerreason_t reason; - - kws_t *kws; - ks_thread_t *kws_thread; - - ks_q_t *messages_sending; - ks_q_t *messages_receiving; -}; - - -void *blade_peer_kws_thread(ks_thread_t *thread, void *data); - - -KS_DECLARE(ks_status_t) blade_peer_destroy(blade_peer_t **bpP) -{ - blade_peer_t *bp = NULL; - - ks_assert(bpP); - - bp = *bpP; - *bpP = NULL; - - ks_assert(bp); - - blade_peer_shutdown(bp); - - ks_q_destroy(&bp->messages_sending); - ks_q_destroy(&bp->messages_receiving); - - ks_pool_free(bp->pool, &bp); - - 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, blade_service_t *service) -{ - blade_peer_t *bp = NULL; - - ks_assert(bpP); - ks_assert(pool); - ks_assert(tpool); - ks_assert(service); - - bp = ks_pool_alloc(pool, sizeof(*bp)); - bp->pool = pool; - bp->tpool = tpool; - bp->service = service; - bp->state = BLADE_PEERSTATE_CONNECTING; - bp->reason = BLADE_PEERREASON_NORMAL; - ks_q_create(&bp->messages_sending, pool, 0); - ks_q_create(&bp->messages_receiving, pool, 0); - *bpP = bp; - - return KS_STATUS_SUCCESS; -} - -KS_DECLARE(ks_status_t) blade_peer_startup(blade_peer_t *bp, ks_socket_t sock) -{ - kws_t *kws = NULL; - - ks_assert(bp); - ks_assert(kws); - - // @todo: consider using a recycle queue for blade_peer_t in blade_service_t, just need to call startup then - - blade_peer_shutdown(bp); - - bp->sock = sock; - bp->state = BLADE_PEERSTATE_CONNECTING; - bp->reason = BLADE_PEERREASON_NORMAL; - - if (ks_thread_create_ex(&bp->kws_thread, - blade_peer_kws_thread, - bp, - KS_THREAD_FLAG_DEFAULT, - KS_THREAD_DEFAULT_STACK, - KS_PRI_NORMAL, - bp->pool) != KS_STATUS_SUCCESS) { - // @todo error logging - blade_peer_disconnect(bp, BLADE_PEERREASON_ERROR); - return KS_STATUS_FAIL; - } - - return KS_STATUS_SUCCESS; -} - -KS_DECLARE(ks_status_t) blade_peer_shutdown(blade_peer_t *bp) -{ - blade_message_t *message = NULL; - - ks_assert(bp); - - bp->shutdown = KS_TRUE; - - if (bp->kws_thread) { - ks_thread_join(bp->kws_thread); - ks_pool_free(bp->pool, &bp->kws_thread); - } - - while (ks_q_trypop(bp->messages_sending, (void **)&message) == KS_STATUS_SUCCESS && message) blade_message_discard(&message); - while (ks_q_trypop(bp->messages_receiving, (void **)&message) == KS_STATUS_SUCCESS && message) blade_message_discard(&message); - - if (bp->kws) kws_destroy(&bp->kws); - else if (bp->sock != KS_SOCK_INVALID) ks_socket_close(&bp->sock); - bp->sock = KS_SOCK_INVALID; - - bp->shutdown = KS_FALSE; - return KS_STATUS_SUCCESS; -} - -KS_DECLARE(void) blade_peer_disconnect(blade_peer_t *bp, blade_peerreason_t reason) -{ - ks_assert(bp); - - // @todo check if already disconnecting for another reason, avoid resetting to get initial reason for disconnect? - bp->reason = reason; - bp->state = BLADE_PEERSTATE_DISCONNECTING; -} - -KS_DECLARE(blade_peerstate_t) blade_peer_state(blade_peer_t *bp) -{ - ks_assert(bp); - return bp->state; -} - -KS_DECLARE(ks_status_t) blade_peer_message_pop(blade_peer_t *peer, blade_message_t **message) -{ - ks_assert(peer); - ks_assert(message); - - *message = NULL; - return ks_q_trypop(peer->messages_receiving, (void **)message); -} - -KS_DECLARE(ks_status_t) blade_peer_message_push(blade_peer_t *peer, void *data, ks_size_t data_length) -{ - blade_message_t *message = NULL; - - ks_assert(peer); - ks_assert(data); - ks_assert(data_length > 0); - - if (blade_handle_message_claim(blade_service_handle(peer->service), &message, data, data_length) != KS_STATUS_SUCCESS || !message) { - // @todo error logging - blade_peer_disconnect(peer, BLADE_PEERREASON_ERROR); - return KS_STATUS_FAIL; - } - ks_q_push(peer->messages_sending, message); - return KS_STATUS_SUCCESS; -} - -void *blade_peer_kws_thread(ks_thread_t *thread, void *data) -{ - blade_peer_t *peer = NULL; - kws_opcode_t opcode; - uint8_t *frame_data = NULL; - ks_size_t frame_data_len = 0; - blade_message_t *message = NULL; - int32_t poll_flags = 0; - - ks_assert(thread); - ks_assert(data); - - peer = (blade_peer_t *)data; - - // @todo consider using an INITIALIZING state to track when there is problems during initialization (specifically SSL negotiations)? - // @todo should stack be notified with an internal event callback here before logic layer initialization starts (IE, before SSL negotiations)? - peer->state = BLADE_PEERSTATE_RUNNING; - - // @todo: SSL init stuffs based on data from peer->service->config_websockets_ssl to pass into kws_init - - if (kws_init(&peer->kws, peer->sock, NULL, NULL, KWS_BLOCK, peer->pool) != KS_STATUS_SUCCESS) { - // @todo error logging - blade_peer_disconnect(peer, BLADE_PEERREASON_ERROR); - return NULL; - } - - blade_service_peer_state_callback(peer->service, peer, BLADE_PEERSTATE_RUNNING); - - while (!peer->shutdown) { - // @todo get exact timeout from service config? - poll_flags = ks_wait_sock(peer->sock, 100, KS_POLL_READ | KS_POLL_ERROR); - - if (poll_flags & KS_POLL_ERROR) { - // @todo error logging - blade_peer_disconnect(peer, BLADE_PEERREASON_ERROR); - break; - } - - if (poll_flags & KS_POLL_READ) { - frame_data_len = kws_read_frame(peer->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 - - blade_peer_disconnect(peer, BLADE_PEERREASON_ERROR); - break; - } - - if (blade_handle_message_claim(blade_service_handle(peer->service), &message, frame_data, frame_data_len) != KS_STATUS_SUCCESS || !message) { - // @todo error logging - blade_peer_disconnect(peer, BLADE_PEERREASON_ERROR); - break; - } - - ks_q_push(peer->messages_receiving, message); - blade_service_peer_state_callback(peer->service, peer, BLADE_PEERSTATE_RECEIVING); - } - - // @todo consider only sending one message at a time and use shorter polling timeout to prevent any considerable blocking if send buffers get full - while (ks_q_trypop(peer->messages_sending, (void **)&message) == KS_STATUS_SUCCESS && message) { - blade_message_get(message, (void **)&frame_data, &frame_data_len); - // @todo may need to get the WSOC_TEXT from the message if using WSOC_BINARY is desired later - kws_write_frame(peer->kws, WSOC_TEXT, frame_data, frame_data_len); - } - } - - return NULL; -} - -/* 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_service.c b/libs/libblade/src/blade_service.c deleted file mode 100644 index 4eabd669af..0000000000 --- a/libs/libblade/src/blade_service.c +++ /dev/null @@ -1,428 +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 BLADE_SERVICE_WEBSOCKETS_ENDPOINTS_MULTIHOME_MAX 16 - -struct blade_service_s { - ks_pool_t *pool; - ks_thread_pool_t *tpool; - blade_handle_t *handle; - blade_service_peer_state_callback_t peer_state_callback; - - ks_sockaddr_t config_websockets_endpoints_ipv4[BLADE_SERVICE_WEBSOCKETS_ENDPOINTS_MULTIHOME_MAX]; - ks_sockaddr_t config_websockets_endpoints_ipv6[BLADE_SERVICE_WEBSOCKETS_ENDPOINTS_MULTIHOME_MAX]; - int32_t config_websockets_endpoints_ipv4_length; - int32_t config_websockets_endpoints_ipv6_length; - int32_t config_websockets_endpoints_backlog; - - ks_bool_t shutdown; - - struct pollfd *listeners_poll; - int32_t listeners_size; - int32_t listeners_length; - ks_thread_t *listeners_thread; - - list_t connected; -}; - - -void *blade_service_listeners_thread(ks_thread_t *thread, void *data); -ks_status_t blade_service_listen(blade_service_t *bs, ks_sockaddr_t *addr); - - -KS_DECLARE(ks_status_t) blade_service_destroy(blade_service_t **bsP) -{ - blade_service_t *bs = NULL; - - ks_assert(bsP); - - bs = *bsP; - *bsP = NULL; - - ks_assert(bs); - - blade_service_shutdown(bs); - - list_destroy(&bs->connected); - - ks_pool_free(bs->pool, &bs); - - return KS_STATUS_SUCCESS; -} - -KS_DECLARE(ks_status_t) blade_service_create(blade_service_t **bsP, - ks_pool_t *pool, - ks_thread_pool_t *tpool, - blade_handle_t *handle, - blade_service_peer_state_callback_t peer_state_callback) -{ - blade_service_t *bs = NULL; - - ks_assert(bsP); - ks_assert(pool); - ks_assert(tpool); - ks_assert(handle); - - bs = ks_pool_alloc(pool, sizeof(*bs)); - bs->pool = pool; - bs->tpool = tpool; - bs->handle = handle; - bs->peer_state_callback = peer_state_callback; - list_init(&bs->connected); - *bsP = bs; - - return KS_STATUS_SUCCESS; -} - -KS_DECLARE(blade_handle_t *) blade_service_handle(blade_service_t *bs) -{ - ks_assert(bs); - return bs->handle; -} - -ks_status_t blade_service_config(blade_service_t *bs, config_setting_t *config) -{ - config_setting_t *websockets = NULL; - config_setting_t *websockets_endpoints = NULL; - config_setting_t *websockets_endpoints_ipv4 = NULL; - config_setting_t *websockets_endpoints_ipv6 = NULL; - config_setting_t *websockets_ssl = NULL; - config_setting_t *element; - config_setting_t *tmp1; - config_setting_t *tmp2; - ks_sockaddr_t config_websockets_endpoints_ipv4[BLADE_SERVICE_WEBSOCKETS_ENDPOINTS_MULTIHOME_MAX]; - ks_sockaddr_t config_websockets_endpoints_ipv6[BLADE_SERVICE_WEBSOCKETS_ENDPOINTS_MULTIHOME_MAX]; - int32_t config_websockets_endpoints_ipv4_length = 0; - int32_t config_websockets_endpoints_ipv6_length = 0; - int32_t config_websockets_endpoints_backlog = 8; - - ks_assert(bs); - - if (!config) { - ks_log(KS_LOG_DEBUG, "!config\n"); - return KS_STATUS_FAIL; - } - if (!config_setting_is_group(config)) { - ks_log(KS_LOG_DEBUG, "!config_setting_is_group(config)\n"); - return KS_STATUS_FAIL; - } - - websockets = config_setting_get_member(config, "websockets"); - if (!websockets) { - ks_log(KS_LOG_DEBUG, "!websockets\n"); - return KS_STATUS_FAIL; - } - websockets_endpoints = config_setting_get_member(websockets, "endpoints"); - if (!websockets_endpoints) { - ks_log(KS_LOG_DEBUG, "!websockets_endpoints\n"); - return KS_STATUS_FAIL; - } - websockets_endpoints_ipv4 = config_lookup_from(websockets_endpoints, "ipv4"); - websockets_endpoints_ipv6 = config_lookup_from(websockets_endpoints, "ipv6"); - if (websockets_endpoints_ipv4) { - if (config_setting_type(websockets_endpoints_ipv4) != CONFIG_TYPE_LIST) return KS_STATUS_FAIL; - if ((config_websockets_endpoints_ipv4_length = config_setting_length(websockets_endpoints_ipv4)) > BLADE_SERVICE_WEBSOCKETS_ENDPOINTS_MULTIHOME_MAX) - return KS_STATUS_FAIL; - - for (int32_t index = 0; index < config_websockets_endpoints_ipv4_length; ++index) { - element = config_setting_get_elem(websockets_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_websockets_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_websockets_endpoints_ipv4[index]), - ks_addr_get_port(&config_websockets_endpoints_ipv4[index])); - } - } - if (websockets_endpoints_ipv6) { - if (config_setting_type(websockets_endpoints_ipv6) != CONFIG_TYPE_LIST) return KS_STATUS_FAIL; - if ((config_websockets_endpoints_ipv6_length = config_setting_length(websockets_endpoints_ipv6)) > BLADE_SERVICE_WEBSOCKETS_ENDPOINTS_MULTIHOME_MAX) - return KS_STATUS_FAIL; - - for (int32_t index = 0; index < config_websockets_endpoints_ipv6_length; ++index) { - element = config_setting_get_elem(websockets_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_websockets_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_websockets_endpoints_ipv6[index]), - ks_addr_get_port(&config_websockets_endpoints_ipv6[index])); - } - } - if (config_websockets_endpoints_ipv4_length + config_websockets_endpoints_ipv6_length <= 0) return KS_STATUS_FAIL; - tmp1 = config_lookup_from(websockets_endpoints, "backlog"); - if (tmp1) { - if (config_setting_type(tmp1) != CONFIG_TYPE_INT) return KS_STATUS_FAIL; - config_websockets_endpoints_backlog = config_setting_get_int(tmp1); - } - websockets_ssl = config_setting_get_member(websockets, "ssl"); - if (websockets_ssl) { - // @todo: SSL stuffs from websockets_ssl into config_websockets_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 from the current config when reloading a new config - for (int32_t index = 0; index < config_websockets_endpoints_ipv4_length; ++index) - bs->config_websockets_endpoints_ipv4[index] = config_websockets_endpoints_ipv4[index]; - for (int32_t index = 0; index < config_websockets_endpoints_ipv6_length; ++index) - bs->config_websockets_endpoints_ipv6[index] = config_websockets_endpoints_ipv6[index]; - bs->config_websockets_endpoints_ipv4_length = config_websockets_endpoints_ipv4_length; - bs->config_websockets_endpoints_ipv6_length = config_websockets_endpoints_ipv6_length; - bs->config_websockets_endpoints_backlog = config_websockets_endpoints_backlog; - //bs->config_websockets_ssl = config_websockets_ssl; - - return KS_STATUS_SUCCESS; -} - -KS_DECLARE(ks_status_t) blade_service_startup(blade_service_t *bs, config_setting_t *config) -{ - ks_assert(bs); - - blade_service_shutdown(bs); - - // @todo: If the configuration is invalid, and this is a case of reloading a new config, then the service shutdown shouldn't occur - // but the service may use configuration that changes before we shutdown if it is read successfully, may require a config reader/writer mutex? - - if (blade_service_config(bs, config) != KS_STATUS_SUCCESS) { - ks_log(KS_LOG_DEBUG, "blade_service_config failed\n"); - return KS_STATUS_FAIL; - } - - for (int32_t index = 0; index < bs->config_websockets_endpoints_ipv4_length; ++index) { - if (blade_service_listen(bs, &bs->config_websockets_endpoints_ipv4[index]) != KS_STATUS_SUCCESS) { - ks_log(KS_LOG_DEBUG, "blade_service_listen (v4) failed\n"); - return KS_STATUS_FAIL; - } - } - for (int32_t index = 0; index < bs->config_websockets_endpoints_ipv6_length; ++index) { - if (blade_service_listen(bs, &bs->config_websockets_endpoints_ipv6[index]) != KS_STATUS_SUCCESS) { - ks_log(KS_LOG_DEBUG, "blade_service_listen (v6) failed\n"); - return KS_STATUS_FAIL; - } - } - - if (ks_thread_create_ex(&bs->listeners_thread, - blade_service_listeners_thread, - bs, - KS_THREAD_FLAG_DEFAULT, - KS_THREAD_DEFAULT_STACK, - KS_PRI_NORMAL, - bs->pool) != KS_STATUS_SUCCESS) return KS_STATUS_FAIL; - - return KS_STATUS_SUCCESS; -} - -KS_DECLARE(ks_status_t) blade_service_shutdown(blade_service_t *bs) -{ - ks_assert(bs); - - // @todo 1 more callback for blade_service_state_callback_t? providing event up the stack on service startup, shutdown, and service errors? - - bs->shutdown = KS_TRUE; - - if (bs->listeners_thread) { - ks_thread_join(bs->listeners_thread); - ks_pool_free(bs->pool, &bs->listeners_thread); - } - - for (int32_t index = 0; index < bs->listeners_length; ++index) { - ks_socket_t sock = bs->listeners_poll[index].fd; - ks_socket_shutdown(sock, SHUT_RDWR); - ks_socket_close(&sock); - } - bs->listeners_length = 0; - - list_iterator_start(&bs->connected); - while (list_iterator_hasnext(&bs->connected)) { - blade_peer_t *peer = (blade_peer_t *)list_iterator_next(&bs->connected); - blade_peer_destroy(&peer); // @todo determine if NOT receiving the DISCONNECTING event callback for these will matter, as service is being shutdown - } - list_iterator_stop(&bs->connected); - list_clear(&bs->connected); - - bs->shutdown = KS_FALSE; - return KS_STATUS_SUCCESS; -} - -KS_DECLARE(void) blade_service_peer_state_callback(blade_service_t *bs, blade_peer_t *bp, blade_peerstate_t state) -{ - ks_assert(bs); - ks_assert(bp); - - if (bs->peer_state_callback) bs->peer_state_callback(bs, bp, state); -} - -ks_status_t blade_service_listen(blade_service_t *bs, 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(bs); - 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, bs->config_websockets_endpoints_backlog) != 0) { - ks_log(KS_LOG_DEBUG, "listen(listener, backlog) != 0\n"); - ret = KS_STATUS_FAIL; - goto done; - } - - listener_index = bs->listeners_length++; - if (bs->listeners_length > bs->listeners_size) { - bs->listeners_size = bs->listeners_length; - bs->listeners_poll = (struct pollfd *)ks_pool_resize(bs->pool, bs->listeners_poll, sizeof(struct pollfd) * bs->listeners_size); - ks_assert(bs->listeners_poll); - } - bs->listeners_poll[listener_index].fd = listener; - bs->listeners_poll[listener_index].events = POLLIN | POLLERR; - - 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_service_listeners_thread(ks_thread_t *thread, void *data) -{ - blade_service_t *service = NULL; - - ks_assert(thread); - ks_assert(data); - - service = (blade_service_t *)data; - - ks_log(KS_LOG_DEBUG, "Service running\n"); - - // @todo 1 more callback for blade_service_state_callback_t? providing event up the stack on service startup, shutdown, and service errors? - - while (!service->shutdown) { - // @todo take exact timeout from a setting in config_service_endpoints - if (ks_poll(service->listeners_poll, service->listeners_length, 100) > 0) { - for (int32_t index = 0; index < service->listeners_length; ++index) { - ks_socket_t sock = KS_SOCK_INVALID; - blade_peer_t *peer = NULL; - - if (!(service->listeners_poll[index].revents & POLLIN)) continue; - if (service->listeners_poll[index].revents & POLLERR) { - // @todo: error handling, just skip the listener for now, it might recover, could skip X sanity times before closing? - continue; - } - - if ((sock = accept(service->listeners_poll[index].fd, NULL, NULL)) == KS_SOCK_INVALID) { - // @todo: error handling, just skip the socket for now as most causes are because the remote side suddenly became unreachable - continue; - } - - // @todo consider a recycle queue of peers per service, and only have to call startup when one is already available - // blade_service_peer_claim(service, &peer); - blade_peer_create(&peer, service->pool, service->tpool, service); - ks_assert(peer); - - // @todo call state callback with connecting enum state - blade_service_peer_state_callback(service, peer, BLADE_PEERSTATE_CONNECTING); - - blade_peer_startup(peer, sock); - - list_append(&service->connected, peer); - } - } - - list_iterator_start(&service->connected); - while (list_iterator_hasnext(&service->connected)) { - blade_peer_t *peer = (blade_peer_t *)list_iterator_next(&service->connected); - // @todo expose accessor for disconnecting, after changing it into the state callback enum - // ensure that every way kws_close might occur leads back to disconnecting = KS_TRUE for this to universally process disconnects - if (blade_peer_state(peer) == BLADE_PEERSTATE_DISCONNECTING) { - // @todo check if there is an iterator based remove function, or indexed iteration to use list_delete_at() - list_delete(&service->connected, peer); - blade_service_peer_state_callback(service, peer, BLADE_PEERSTATE_DISCONNECTING); - - // @todo switch to blade_peer_shutdown(&peer) and blade_peer_discard(&peer) after introducing recycling of peers - blade_peer_destroy(&peer); - } - } - list_iterator_stop(&service->connected); - } - - return NULL; -} - -/* 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 8504af02c4..aeac8682db 100644 --- a/libs/libblade/src/blade_stack.c +++ b/libs/libblade/src/blade_stack.c @@ -48,7 +48,6 @@ struct blade_handle_s { config_setting_t *config_datastore; ks_q_t *messages_discarded; - blade_service_t *service; blade_datastore_t *datastore; }; @@ -143,7 +142,7 @@ ks_status_t blade_handle_config(blade_handle_t *bh, config_setting_t *config) return KS_STATUS_SUCCESS; } -KS_DECLARE(ks_status_t) blade_handle_startup(blade_handle_t *bh, config_setting_t *config, blade_service_peer_state_callback_t service_peer_state_callback) +KS_DECLARE(ks_status_t) blade_handle_startup(blade_handle_t *bh, config_setting_t *config) { ks_assert(bh); @@ -152,15 +151,6 @@ KS_DECLARE(ks_status_t) blade_handle_startup(blade_handle_t *bh, config_setting_ return KS_STATUS_FAIL; } - if (bh->config_service && !blade_handle_service_available(bh)) { - blade_service_create(&bh->service, bh->pool, bh->tpool, bh, service_peer_state_callback); - ks_assert(bh->service); - if (blade_service_startup(bh->service, bh->config_service) != KS_STATUS_SUCCESS) { - ks_log(KS_LOG_DEBUG, "blade_service_startup 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); @@ -177,13 +167,23 @@ KS_DECLARE(ks_status_t) blade_handle_shutdown(blade_handle_t *bh) { ks_assert(bh); - if (blade_handle_service_available(bh)) blade_service_destroy(&bh->service); - if (blade_handle_datastore_available(bh)) blade_datastore_destroy(&bh->datastore); return KS_STATUS_SUCCESS; } +KS_DECLARE(ks_pool_t *) blade_handle_pool_get(blade_handle_t *bh) +{ + ks_assert(bh); + return bh->pool; +} + +KS_DECLARE(ks_thread_pool_t *) blade_handle_tpool_get(blade_handle_t *bh) +{ + ks_assert(bh); + return bh->tpool; +} + KS_DECLARE(ks_status_t) blade_handle_message_claim(blade_handle_t *bh, blade_message_t **message, void *data, ks_size_t data_length) { blade_message_t *msg = NULL; @@ -218,12 +218,7 @@ KS_DECLARE(ks_status_t) blade_handle_message_discard(blade_handle_t *bh, blade_m return KS_STATUS_SUCCESS; } -KS_DECLARE(ks_bool_t) blade_handle_service_available(blade_handle_t *bh) -{ - ks_assert(bh); - return bh->service != NULL; -} KS_DECLARE(ks_bool_t) blade_handle_datastore_available(blade_handle_t *bh) { diff --git a/libs/libblade/src/include/blade.h b/libs/libblade/src/include/blade.h index f8a3a4459f..d7a6a7cac1 100644 --- a/libs/libblade/src/include/blade.h +++ b/libs/libblade/src/include/blade.h @@ -40,12 +40,14 @@ #include "unqlite.h" #include "blade_types.h" #include "blade_stack.h" -#include "blade_peer.h" -#include "blade_service.h" #include "blade_message.h" #include "blade_datastore.h" #include "bpcp.h" +#include "blade_identity.h" +#include "blade_module.h" +#include "blade_connection.h" + KS_BEGIN_EXTERN_C KS_DECLARE(ks_status_t) blade_init(void); diff --git a/libs/libblade/src/include/blade_connection.h b/libs/libblade/src/include/blade_connection.h new file mode 100644 index 0000000000..d48ec8c221 --- /dev/null +++ b/libs/libblade/src/include/blade_connection.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_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); +KS_DECLARE(ks_status_t) blade_connection_shutdown(blade_connection_t *bc); +KS_DECLARE(void *) blade_connection_transport_get(blade_connection_t *bc); +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, blade_identity_t *target, cJSON *json); +KS_DECLARE(ks_status_t) blade_connection_sending_pop(blade_connection_t *bc, blade_identity_t **target, cJSON **json); +KS_DECLARE(ks_status_t) blade_connection_receiving_push(blade_connection_t *bc, cJSON *json); +KS_DECLARE(ks_status_t) blade_connection_receiving_pop(blade_connection_t *bc, 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_service.h b/libs/libblade/src/include/blade_identity.h similarity index 70% rename from libs/libblade/src/include/blade_service.h rename to libs/libblade/src/include/blade_identity.h index a5fa890b48..41d0341b06 100644 --- a/libs/libblade/src/include/blade_service.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,21 +31,15 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef _BLADE_SERVICE_H_ -#define _BLADE_SERVICE_H_ +#ifndef _BLADE_IDENTITY_H_ +#define _BLADE_IDENTITY_H_ #include KS_BEGIN_EXTERN_C -KS_DECLARE(ks_status_t) blade_service_create(blade_service_t **bsP, - ks_pool_t *pool, - ks_thread_pool_t *tpool, - blade_handle_t *handle, - blade_service_peer_state_callback_t peer_state_callback); -KS_DECLARE(ks_status_t) blade_service_destroy(blade_service_t **bsP); -KS_DECLARE(blade_handle_t *) blade_service_handle(blade_service_t *bs); -KS_DECLARE(ks_status_t) blade_service_startup(blade_service_t *bs, config_setting_t *config); -KS_DECLARE(ks_status_t) blade_service_shutdown(blade_service_t *bs); -KS_DECLARE(void) blade_service_peer_state_callback(blade_service_t *bs, blade_peer_t *bp, blade_peerstate_t state); +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(ks_status_t) blade_identity_uri(blade_identity_t *bi, const char **uri); KS_END_EXTERN_C #endif diff --git a/libs/libblade/src/include/blade_peer.h b/libs/libblade/src/include/blade_module.h similarity index 67% rename from libs/libblade/src/include/blade_peer.h rename to libs/libblade/src/include/blade_module.h index a5cf304b46..ef2ba88de7 100644 --- a/libs/libblade/src/include/blade_peer.h +++ b/libs/libblade/src/include/blade_module.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,20 +31,14 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef _BLADE_PEER_H_ -#define _BLADE_PEER_H_ +#ifndef _BLADE_MODULE_H_ +#define _BLADE_MODULE_H_ #include 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, blade_service_t *service); -KS_DECLARE(ks_status_t) blade_peer_destroy(blade_peer_t **bpP); -KS_DECLARE(ks_status_t) blade_peer_startup(blade_peer_t *bp, ks_socket_t sock); -KS_DECLARE(ks_status_t) blade_peer_shutdown(blade_peer_t *bp); -KS_DECLARE(void) blade_peer_disconnect(blade_peer_t *bp, blade_peerreason_t reason); -KS_DECLARE(blade_peerstate_t) blade_peer_state(blade_peer_t *bp); -KS_DECLARE(blade_peerreason_t) blade_peer_reason(blade_peer_t *bp); -KS_DECLARE(ks_status_t) blade_peer_message_pop(blade_peer_t *peer, blade_message_t **message); -KS_DECLARE(ks_status_t) blade_peer_message_push(blade_peer_t *peer, void *data, ks_size_t data_length); +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(void *) blade_module_data_get(blade_module_t *bm); KS_END_EXTERN_C #endif diff --git a/libs/libblade/src/include/blade_stack.h b/libs/libblade/src/include/blade_stack.h index ff44f230e9..04daa932d6 100644 --- a/libs/libblade/src/include/blade_stack.h +++ b/libs/libblade/src/include/blade_stack.h @@ -43,14 +43,14 @@ 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); -KS_DECLARE(ks_status_t) blade_handle_startup(blade_handle_t *bh, config_setting_t *config, blade_service_peer_state_callback_t service_peer_state_callback); +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_message_claim(blade_handle_t *bh, blade_message_t **message, void *data, ks_size_t data_length); KS_DECLARE(ks_status_t) blade_handle_message_discard(blade_handle_t *bh, blade_message_t **message); -KS_DECLARE(ks_bool_t) blade_handle_service_available(blade_handle_t *bh); - 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, diff --git a/libs/libblade/src/include/blade_types.h b/libs/libblade/src/include/blade_types.h index b534fde6fd..cc2c054197 100644 --- a/libs/libblade/src/include/blade_types.h +++ b/libs/libblade/src/include/blade_types.h @@ -37,28 +37,79 @@ KS_BEGIN_EXTERN_C -typedef enum { - BLADE_PEERSTATE_CONNECTING, - BLADE_PEERSTATE_DISCONNECTING, - BLADE_PEERSTATE_RUNNING, - BLADE_PEERSTATE_RECEIVING, -} blade_peerstate_t; - -typedef enum { - BLADE_PEERREASON_NORMAL, - BLADE_PEERREASON_ERROR, - // @todo populate more reasons for disconnecting as neccessary -} blade_peerreason_t; - typedef struct blade_handle_s blade_handle_t; -typedef struct blade_peer_s blade_peer_t; -typedef struct blade_service_s blade_service_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_connection_s blade_connection_t; + typedef struct blade_message_s blade_message_t; typedef struct blade_datastore_s blade_datastore_t; -typedef void (*blade_service_peer_state_callback_t)(blade_service_t *bs, blade_peer_t *bp, blade_peerstate_t state); 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_IN, + BLADE_CONNECTION_DIRECTION_OUT, +} 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 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); +typedef blade_connection_rank_t (*blade_transport_rank_callback_t)(blade_connection_t *bc, blade_identity_t *target); +typedef blade_connection_state_hook_t (*blade_transport_state_callback_t)(blade_connection_t *bc, + blade_connection_state_t state, + 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_state_callback_t onstate; +}; + + KS_END_EXTERN_C #endif diff --git a/libs/libblade/test/bladec b/libs/libblade/test/bladec deleted file mode 100755 index 805d6501b3..0000000000 --- a/libs/libblade/test/bladec +++ /dev/null @@ -1,228 +0,0 @@ -#! /bin/bash - -# bladec - temporary wrapper script for .libs/bladec -# Generated by libtool (GNU libtool) 2.4.2 Debian-2.4.2-1.11 -# -# The bladec program cannot be directly executed until all the libtool -# libraries that it depends on are installed. -# -# This wrapper script should never be moved out of the build directory. -# If it is, it will not operate correctly. - -# Sed substitution that helps us do robust quoting. It backslashifies -# metacharacters that are still active within double-quoted strings. -sed_quote_subst='s/\([`"$\\]\)/\\\1/g' - -# Be Bourne compatible -if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then - emulate sh - NULLCMD=: - # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which - # is contrary to our usage. Disable this feature. - alias -g '${1+"$@"}'='"$@"' - setopt NO_GLOB_SUBST -else - case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac -fi -BIN_SH=xpg4; export BIN_SH # for Tru64 -DUALCASE=1; export DUALCASE # for MKS sh - -# The HP-UX ksh and POSIX shell print the target directory to stdout -# if CDPATH is set. -(unset CDPATH) >/dev/null 2>&1 && unset CDPATH - -relink_command="(cd /usr/src/freeswitch/libs/libblade/test; { test -z \"\${LIBRARY_PATH+set}\" || unset LIBRARY_PATH || { LIBRARY_PATH=; export LIBRARY_PATH; }; }; { test -z \"\${COMPILER_PATH+set}\" || unset COMPILER_PATH || { COMPILER_PATH=; export COMPILER_PATH; }; }; { test -z \"\${GCC_EXEC_PREFIX+set}\" || unset GCC_EXEC_PREFIX || { GCC_EXEC_PREFIX=; export GCC_EXEC_PREFIX; }; }; { test -z \"\${LD_RUN_PATH+set}\" || unset LD_RUN_PATH || { LD_RUN_PATH=; export LD_RUN_PATH; }; }; { test -z \"\${LD_LIBRARY_PATH+set}\" || unset LD_LIBRARY_PATH || { LD_LIBRARY_PATH=; export LD_LIBRARY_PATH; }; }; PATH=/usr/lib/ccache:/usr/lib/ccache:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/bin:/usr/local/bin:/usr/local/sbin:/usr/local/freeswitch/bin:/opt/bin:/usr/local/bin:/usr/local/sbin:/usr/local/freeswitch/bin; export PATH; gcc -fPIC -Wall -std=c99 -pedantic -DUSE_SCHED_SETSCHEDULER=1 -DKS_API_VISIBILITY=1 -fvisibility=hidden -Werror -DHAVE_PTHREAD_SETSCHEDPARAM=1 -DHAVE_OPENSSL -fsanitize=address -fno-omit-frame-pointer -I/usr/src/freeswitch/libs/libblade/src/include -g -ggdb -O0 -g -O2 -fsanitize=address -o \$progdir/\$file bladec-bladec.o bladec-tap.o -L/usr/src/freeswitch/libs/libblade/../libks/.libs/ /usr/src/freeswitch/libs/libblade/../libks/.libs//libks.so -lsodium /usr/src/freeswitch/libs/libblade/.libs/libblade.so -lconfig -lm -lpthread -lssl -lcrypto -Wl,-rpath -Wl,/usr/src/freeswitch/libs/libblade/../libks/.libs/ -Wl,-rpath -Wl,/usr/src/freeswitch/libs/libblade/.libs)" - -# This environment variable determines our operation mode. -if test "$libtool_install_magic" = "%%%MAGIC variable%%%"; then - # install mode needs the following variables: - generated_by_libtool_version='2.4.2' - notinst_deplibs=' /usr/src/freeswitch/libs/libblade/../libks/.libs//libks.la /usr/src/freeswitch/libs/libblade/libblade.la' -else - # When we are sourced in execute mode, $file and $ECHO are already set. - if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then - file="$0" - -# A function that is used when there is no print builtin or printf. -func_fallback_echo () -{ - eval 'cat <<_LTECHO_EOF -$1 -_LTECHO_EOF' -} - ECHO="printf %s\\n" - fi - -# Very basic option parsing. These options are (a) specific to -# the libtool wrapper, (b) are identical between the wrapper -# /script/ and the wrapper /executable/ which is used only on -# windows platforms, and (c) all begin with the string --lt- -# (application programs are unlikely to have options which match -# this pattern). -# -# There are only two supported options: --lt-debug and -# --lt-dump-script. There is, deliberately, no --lt-help. -# -# The first argument to this parsing function should be the -# script's ../libtool value, followed by no. -lt_option_debug= -func_parse_lt_options () -{ - lt_script_arg0=$0 - shift - for lt_opt - do - case "$lt_opt" in - --lt-debug) lt_option_debug=1 ;; - --lt-dump-script) - lt_dump_D=`$ECHO "X$lt_script_arg0" | /bin/sed -e 's/^X//' -e 's%/[^/]*$%%'` - test "X$lt_dump_D" = "X$lt_script_arg0" && lt_dump_D=. - lt_dump_F=`$ECHO "X$lt_script_arg0" | /bin/sed -e 's/^X//' -e 's%^.*/%%'` - cat "$lt_dump_D/$lt_dump_F" - exit 0 - ;; - --lt-*) - $ECHO "Unrecognized --lt- option: '$lt_opt'" 1>&2 - exit 1 - ;; - esac - done - - # Print the debug banner immediately: - if test -n "$lt_option_debug"; then - echo "bladec:bladec:${LINENO}: libtool wrapper (GNU libtool) 2.4.2 Debian-2.4.2-1.11" 1>&2 - fi -} - -# Used when --lt-debug. Prints its arguments to stdout -# (redirection is the responsibility of the caller) -func_lt_dump_args () -{ - lt_dump_args_N=1; - for lt_arg - do - $ECHO "bladec:bladec:${LINENO}: newargv[$lt_dump_args_N]: $lt_arg" - lt_dump_args_N=`expr $lt_dump_args_N + 1` - done -} - -# Core function for launching the target application -func_exec_program_core () -{ - - if test -n "$lt_option_debug"; then - $ECHO "bladec:bladec:${LINENO}: newargv[0]: $progdir/$program" 1>&2 - func_lt_dump_args ${1+"$@"} 1>&2 - fi - exec "$progdir/$program" ${1+"$@"} - - $ECHO "$0: cannot exec $program $*" 1>&2 - exit 1 -} - -# A function to encapsulate launching the target application -# Strips options in the --lt-* namespace from $@ and -# launches target application with the remaining arguments. -func_exec_program () -{ - case " $* " in - *\ --lt-*) - for lt_wr_arg - do - case $lt_wr_arg in - --lt-*) ;; - *) set x "$@" "$lt_wr_arg"; shift;; - esac - shift - done ;; - esac - func_exec_program_core ${1+"$@"} -} - - # Parse options - func_parse_lt_options "$0" ${1+"$@"} - - # Find the directory that this script lives in. - thisdir=`$ECHO "$file" | /bin/sed 's%/[^/]*$%%'` - test "x$thisdir" = "x$file" && thisdir=. - - # Follow symbolic links until we get to the real thisdir. - file=`ls -ld "$file" | /bin/sed -n 's/.*-> //p'` - while test -n "$file"; do - destdir=`$ECHO "$file" | /bin/sed 's%/[^/]*$%%'` - - # If there was a directory component, then change thisdir. - if test "x$destdir" != "x$file"; then - case "$destdir" in - [\\/]* | [A-Za-z]:[\\/]*) thisdir="$destdir" ;; - *) thisdir="$thisdir/$destdir" ;; - esac - fi - - file=`$ECHO "$file" | /bin/sed 's%^.*/%%'` - file=`ls -ld "$thisdir/$file" | /bin/sed -n 's/.*-> //p'` - done - - # Usually 'no', except on cygwin/mingw when embedded into - # the cwrapper. - WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=no - if test "$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR" = "yes"; then - # special case for '.' - if test "$thisdir" = "."; then - thisdir=`pwd` - fi - # remove .libs from thisdir - case "$thisdir" in - *[\\/].libs ) thisdir=`$ECHO "$thisdir" | /bin/sed 's%[\\/][^\\/]*$%%'` ;; - .libs ) thisdir=. ;; - esac - fi - - # Try to get the absolute directory name. - absdir=`cd "$thisdir" && pwd` - test -n "$absdir" && thisdir="$absdir" - - program=lt-'bladec' - progdir="$thisdir/.libs" - - if test ! -f "$progdir/$program" || - { file=`ls -1dt "$progdir/$program" "$progdir/../$program" 2>/dev/null | /bin/sed 1q`; \ - test "X$file" != "X$progdir/$program"; }; then - - file="$$-$program" - - if test ! -d "$progdir"; then - mkdir "$progdir" - else - rm -f "$progdir/$file" - fi - - # relink executable if necessary - if test -n "$relink_command"; then - if relink_command_output=`eval $relink_command 2>&1`; then : - else - printf %s\n "$relink_command_output" >&2 - rm -f "$progdir/$file" - exit 1 - fi - fi - - mv -f "$progdir/$file" "$progdir/$program" 2>/dev/null || - { rm -f "$progdir/$program"; - mv -f "$progdir/$file" "$progdir/$program"; } - rm -f "$progdir/$file" - fi - - if test -f "$progdir/$program"; then - if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then - # Run the actual program with our arguments. - func_exec_program ${1+"$@"} - fi - else - # The program doesn't exist. - $ECHO "$0: error: \`$progdir/$program' does not exist" 1>&2 - $ECHO "This script is just a wrapper for $program." 1>&2 - $ECHO "See the libtool documentation for more information." 1>&2 - exit 1 - fi -fi From eb4ece83b998a8f5eb4917f4066e291a5309235c Mon Sep 17 00:00:00 2001 From: colm Date: Thu, 9 Feb 2017 17:56:10 -0500 Subject: [PATCH 14/34] FS-9952: Add testrpcproto, Update api --- libs/libblade/src/blade_rpcproto.c | 23 +- libs/libblade/src/include/blade_rpcproto.h | 11 +- libs/libblade/test/Makefile.am | 6 + libs/libblade/test/testrpcproto.c | 359 +++++++++++++++++++++ 4 files changed, 382 insertions(+), 17 deletions(-) create mode 100644 libs/libblade/test/testrpcproto.c diff --git a/libs/libblade/src/blade_rpcproto.c b/libs/libblade/src/blade_rpcproto.c index c2b811939c..c00d354e53 100644 --- a/libs/libblade/src/blade_rpcproto.c +++ b/libs/libblade/src/blade_rpcproto.c @@ -793,7 +793,7 @@ static ks_status_t blade_rpc_process_jsonmessage_all(cJSON *request) ks_bool_t isrequest = ks_rpcmessage_isrequest(request); - enum jrpc_status_t jrcs = JRPC_ERROR; + enum jrpc_status_t jrcs = 0; if (isrequest && callbacks->request_func) { @@ -801,25 +801,25 @@ static ks_status_t blade_rpc_process_jsonmessage_all(cJSON *request) if (callbacks->custom && callbacks->custom->prefix_request_func) { jrcs = callbacks->custom->prefix_request_func(request, &responseP); - if (jrcs == JRPC_SEND && responseP) { + if ( (jrcs & JRPC_SEND) && responseP) { blade_rpc_write_json(responseP); cJSON_Delete(responseP); responseP = NULL; } } - if (jrcs != JRPC_ERROR) { + if ( !(jrcs & JRPC_EXIT) && jrcs != JRPC_ERROR) { jrcs = callbacks->request_func(request, &responseP); - if (jrcs == JRPC_SEND && responseP) { + if ((jrcs & JRPC_SEND) && responseP) { blade_rpc_write_json(responseP); cJSON_Delete(responseP); responseP = NULL; } } - if (jrcs != JRPC_ERROR && callbacks->custom && callbacks->custom->postfix_request_func) { + if ( !(jrcs & JRPC_EXIT) && jrcs != JRPC_ERROR && callbacks->custom && callbacks->custom->postfix_request_func) { jrcs = callbacks->custom->postfix_request_func(request, &responseP); - if (jrcs == JRPC_SEND && responseP) { + if ( (jrcs & JRPC_SEND) && responseP) { blade_rpc_write_json(responseP); cJSON_Delete(responseP); responseP = NULL; @@ -838,25 +838,25 @@ static ks_status_t blade_rpc_process_jsonmessage_all(cJSON *request) if (callbacks->custom && callbacks->custom->prefix_response_func) { jrcs = callbacks->custom->prefix_response_func(response, &responseP); - if (jrcs == JRPC_SEND && responseP) { + if ( (jrcs & JRPC_SEND) && responseP) { blade_rpc_write_json(responseP); cJSON_Delete(responseP); responseP = NULL; } } - if (jrcs != JRPC_ERROR) { + if ( !(jrcs & JRPC_EXIT) && jrcs != JRPC_ERROR) { jrcs = callbacks->response_func(response, &responseP); - if (jrcs == JRPC_SEND && responseP) { + if ( (jrcs & JRPC_SEND) && responseP) { blade_rpc_write_json(responseP); cJSON_Delete(responseP); responseP = NULL; } } - if (jrcs != JRPC_ERROR && callbacks->custom && callbacks->custom->postfix_response_func) { + if ( !(jrcs & JRPC_EXIT) && jrcs != JRPC_ERROR && callbacks->custom && callbacks->custom->postfix_response_func) { jrcs = callbacks->custom->postfix_response_func(response, &responseP); - if (jrcs == JRPC_SEND && responseP) { + if ( (jrcs & JRPC_SEND) && responseP) { blade_rpc_write_json(responseP); cJSON_Delete(responseP); responseP = NULL; @@ -865,7 +865,6 @@ static ks_status_t blade_rpc_process_jsonmessage_all(cJSON *request) ks_mutex_unlock(callbacks->lock); - if (jrcs == JRPC_ERROR) { return KS_STATUS_FAIL; } diff --git a/libs/libblade/src/include/blade_rpcproto.h b/libs/libblade/src/include/blade_rpcproto.h index c93c3805f5..621c10a48a 100644 --- a/libs/libblade/src/include/blade_rpcproto.h +++ b/libs/libblade/src/include/blade_rpcproto.h @@ -49,13 +49,15 @@ enum jrpc_status_t { - JRPC_PASS = 0, - JRPC_SEND, - JRPC_ERROR + JRPC_PASS = (1 << 0), + JRPC_SEND = (1 << 1), + JRPC_EXIT = (1 << 2), + JRPC_SEND_EXIT = JRPC_SEND + JRPC_EXIT, + JRPC_ERROR = (1 << 3) }; -typedef enum jrpc_status_t (*jrpc_func_t) (cJSON *request, cJSON **responseP); +typedef enum jrpc_status_t (*jrpc_func_t) (cJSON *request, cJSON **replyP); /* @@ -64,7 +66,6 @@ typedef enum jrpc_status_t (*jrpc_func_t) (cJSON *request, cJSON **responseP); */ KS_DECLARE(ks_status_t) blade_rpc_init(ks_pool_t *pool); -KS_DECLARE(ks_status_t) blade_rpc_runprocess(); /* diff --git a/libs/libblade/test/Makefile.am b/libs/libblade/test/Makefile.am index 9bc1a688df..e6c8c3902f 100644 --- a/libs/libblade/test/Makefile.am +++ b/libs/libblade/test/Makefile.am @@ -2,6 +2,12 @@ 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 += testrpcproto +testrpcproto_SOURCES = testrpcproto.c tap.c +testrpcproto_CFLAGS = $(AM_CFLAGS) +testrpcproto_LDADD = $(TEST_LDADD) + check_PROGRAMS += testbuild testbuild_SOURCES = testbuild.c tap.c testbuild_CFLAGS = $(AM_CFLAGS) diff --git a/libs/libblade/test/testrpcproto.c b/libs/libblade/test/testrpcproto.c new file mode 100644 index 0000000000..b479d07033 --- /dev/null +++ b/libs/libblade/test/testrpcproto.c @@ -0,0 +1,359 @@ +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" +#pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-function" + + +#include + +#pragma GCC optimize ("O0") + + +ks_pool_t *pool; +ks_thread_pool_t *tpool; + + +static ks_thread_t *threads[10]; + +static char idbuffer[51]; + + + +static enum jrpc_status_t process_widget(cJSON *msg, cJSON **response) +{ + printf("entering process_widget\n"); + + char *b0 = cJSON_PrintUnformatted(msg); + printf("Request: %s\n", b0); + ks_pool_free(pool, &b0); + + cJSON *resp = cJSON_CreateObject(); + cJSON_AddNumberToObject(resp, "code", 199); + + ks_rpcmessage_id msgid = ks_rpcmessage_create_response(msg, &resp, response); + + char *b1 = cJSON_PrintUnformatted(*response); //(*response); + printf("Response: msgid %d\n%s\n", msgid, b1); + ks_pool_free(pool, &b1); + + printf("exiting process_wombat\n"); + + return JRPC_SEND; +} + + +static enum jrpc_status_t process_widget_response(cJSON *request, cJSON **msg) +{ + printf("entering process_widget_response\n"); + printf("exiting process_widget_response\n"); + return JRPC_PASS; +} + + + +static enum jrpc_status_t process_wombat(cJSON *msg, cJSON **replyP) +{ + printf("entering process_wombat\n"); + + char *b0 = cJSON_PrintUnformatted(msg); + printf("\nRequest: %s\n\n", b0); + ks_pool_free(pool, &b0); + + cJSON *result = cJSON_CreateObject(); + cJSON_AddNumberToObject(result, "code", 99); + cJSON *response; + + ks_rpcmessage_id msgid = ks_rpcmessage_create_response(msg, &result, &response); + + cJSON *response_copy = cJSON_Duplicate(response, 1); + blade_rpc_process_jsonmessage(response_copy); + + if (msgid != 0) { + char *b1 = cJSON_PrintUnformatted(response); //(*response); + printf("\nResponse: msgid %d\n%s\n\n", msgid, b1); + blade_rpc_write_json(response); + ks_pool_free(pool, &b1); + } + else { + printf("process_wombat_preresponse: unable to create response \n"); + return JRPC_ERROR; + } + + + cJSON *parms2 = NULL; + msgid = ks_rpcmessage_create_request("app1", "widget", "99", "1.0", &parms2, replyP); + + printf("\n\nexiting process_wombat with a reply to send\n"); + + return JRPC_SEND; +} + +static enum jrpc_status_t process_wombat_prerequest(cJSON *request, cJSON **msg) +{ + printf("entering process_wombat_prerequest\n"); + printf("exiting process_wombat_prerequest\n"); + return JRPC_SEND; +} + +static enum jrpc_status_t process_wombat_postrequest(cJSON *request, cJSON **msg) +{ + printf("entering process_wombat_postrequest\n"); + printf("exiting process_wombat_postrequest\n"); + return JRPC_PASS; +} + + + +static enum jrpc_status_t process_wombat_response(cJSON *request, cJSON **msg) +{ + printf("entering process_wombat_response\n"); + printf("exiting process_wombat_response\n"); + return JRPC_PASS; +} + +static enum jrpc_status_t process_wombat_preresponse(cJSON *request, cJSON **msg) +{ + + printf("entering process_wombat_preresponse\n"); + + cJSON *response = NULL; + cJSON *result = NULL; + + cJSON *parms2 = NULL; + + //ks_rpcmessage_id msgid = ks_rpcmessage_create_request("app1", "widget", "99", "1.0", &parms2, msg); + + printf("exiting process_wombat_preresponse\n"); + return JRPC_SEND; +} + +static enum jrpc_status_t process_wombat_postresponse(cJSON *request, cJSON **msg) +{ + printf("entering process_postwombat_response\n"); + printf("exiting process_postwombat_response\n"); + return JRPC_PASS; +} + + + + +static enum jrpc_status_t process_badbunny( cJSON *msg, cJSON **response) +{ + printf("entering process_badbunny\n"); + + char *b0 = cJSON_PrintUnformatted(msg); + printf("\nRequest: %s\n\n", b0); + ks_pool_free(pool, &b0); + + cJSON *respvalue; + + ks_rpcmessage_id msgid = ks_rpcmessage_create_errorresponse(msg, &respvalue, response); + + char *b2 = cJSON_PrintUnformatted(*response); + printf("\nRequest: msgid %d\n%s\n\n", msgid, b2); + ks_pool_free(pool, &b2); + + //cJSON *respvalue = cJSON_CreateNumber(1); + + + char *b1 = cJSON_PrintUnformatted(*response); //(*response); + printf("\nResponse: %s\n\n", b1); + ks_pool_free(pool, &b1); + + printf("exiting process_badbunny\n"); + + + return JRPC_SEND; +} + + +void test01() +{ + printf("**** testrpcmessages - test01 start\n"); fflush(stdout); + + blade_rpc_declare_template("temp1", "1.0"); + + blade_rpc_register_template_function("temp1", "widget", process_widget, process_widget_response); + + blade_rpc_declare_namespace("app1", "1.0"); + + blade_rpc_register_function("app1", "wombat", process_wombat, process_wombat_response); + + blade_rpc_register_custom_request_function("app1", "wombat", process_wombat_prerequest, process_wombat_postresponse); + blade_rpc_register_custom_response_function("app1", "wombat", process_wombat_preresponse, process_wombat_postresponse); + + + /* message 1 */ + /* --------- */ + cJSON* request1 = NULL; + cJSON* parms1 = NULL; + + printf("\n\n\n - message1 - basic message\n\n\n"); + + ks_rpcmessage_id msgid = ks_rpcmessage_create_request("app1", "wombat", "99", "1.0", &parms1, &request1); + if (msgid == 0) { + printf("test01.1: unable to create message 1\n"); + return; + } + + if (!request1) { + printf("test01.1: No json returned from create request 1\n"); + return; + } + + char *pdata = cJSON_PrintUnformatted(request1); + + if (!pdata) { + printf("test01.1: unable to parse cJSON object\n"); + return; + } + + printf("request:\n%s\n", pdata); + + cJSON_AddStringToObject(parms1, "hello", "cruel world"); + + blade_rpc_process_jsonmessage(request1); + + cJSON_Delete(request1); + + ks_pool_free(pool, &pdata); + + printf("\ntest01.1 complete\n"); + + + /* message 2 */ + /* --------- */ + + printf("\n\n\n - message2 - test inherit\n\n\n"); + + blade_rpc_inherit_template("app1", "temp1"); + + cJSON* request2 = NULL; + cJSON* parms2 = NULL; + + msgid = ks_rpcmessage_create_request("app1", "temp1.widget", "99", "1.0", &parms2, &request2); + if (msgid == 0) { + printf("test01.2: failed to create a wombat\n"); + return; + } + + if (!request2) { + printf("test01.2: No json returned from create request 1\n"); + return; + } + + pdata = cJSON_PrintUnformatted(request2); + + if (!pdata) { + printf("test01.2: unable to parse cJSON object\n"); + return; + } + + printf("request:\n%s\n", pdata); + + cJSON_AddStringToObject(parms2, "hello2", "cruel world2"); + + blade_rpc_process_jsonmessage(request2); + + cJSON_Delete(request2); + ks_pool_free(pool, &pdata); + + printf("\ntest01.2 complete\n"); + + return; + +} + +void test02() +{ + printf("**** testmessages - test02 start\n"); fflush(stdout); + + printf("**** testmessages - test02 finished\n"); fflush(stdout); + + return; +} + + + + +/* test06 */ +/* ------ */ + +static void *testnodelocking_ex1(ks_thread_t *thread, void *data) +{ + return NULL; +} + +static void *testnodelocking_ex2(ks_thread_t *thread, void *data) +{ + return NULL; +} + + +void test06() +{ + printf("**** testmessages - test06 start\n"); fflush(stdout); + + ks_thread_t *t0; + ks_thread_create(&t0, testnodelocking_ex1, NULL, pool); + + ks_thread_t *t1; + ks_thread_create(&t1, testnodelocking_ex2, NULL, pool); + + ks_thread_join(t1); + ks_thread_join(t0); + + printf("\n\n* **testmessages - test06 -- threads complete\n\n"); fflush(stdout); + + printf("**** testmessages - test06 start\n"); fflush(stdout); + + return; +} + + + +int main(int argc, char *argv[]) { + + printf("testmessages - start\n"); + + int tests[100]; + if (argc == 0) { + tests[0] = 1; + tests[1] = 2; + tests[2] = 3; + tests[3] = 4; + tests[4] = 5; + } + else { + for(int tix=1; tix<100 && tix Date: Fri, 10 Feb 2017 02:17:20 +0000 Subject: [PATCH 15/34] FS-9952: More work on the connection and transport code, couple things left to do but nearly ready for testing upto starting session negotiations --- libs/libblade/src/blade_connection.c | 155 ++++++-- libs/libblade/src/blade_module_wss.c | 349 ++++++++++++++----- libs/libblade/src/include/blade_connection.h | 4 +- libs/libblade/src/include/blade_types.h | 26 +- 4 files changed, 398 insertions(+), 136 deletions(-) diff --git a/libs/libblade/src/blade_connection.c b/libs/libblade/src/blade_connection.c index 3ac336697f..69f1a39366 100644 --- a/libs/libblade/src/blade_connection.c +++ b/libs/libblade/src/blade_connection.c @@ -37,16 +37,18 @@ 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; // @todo add auto generated UUID + blade_connection_direction_t direction; ks_thread_t *state_thread; blade_connection_state_t state; ks_q_t *sending; - ks_q_t *receiving; + //ks_q_t *receiving; }; void *blade_connection_state_thread(ks_thread_t *thread, void *data); @@ -54,7 +56,7 @@ void *blade_connection_state_thread(ks_thread_t *thread, void *data); KS_DECLARE(ks_status_t) blade_connection_create(blade_connection_t **bcP, blade_handle_t *bh, - void *transport_data, + void *transport_init_data, blade_transport_callbacks_t *transport_callbacks) { blade_connection_t *bc = NULL; @@ -62,7 +64,6 @@ KS_DECLARE(ks_status_t) blade_connection_create(blade_connection_t **bcP, ks_assert(bcP); ks_assert(bh); - ks_assert(transport_data); ks_assert(transport_callbacks); pool = blade_handle_pool_get(bh); @@ -70,10 +71,10 @@ KS_DECLARE(ks_status_t) blade_connection_create(blade_connection_t **bcP, bc = ks_pool_alloc(pool, sizeof(blade_connection_t)); bc->handle = bh; bc->pool = pool; - bc->transport_data = transport_data; + bc->transport_init_data = transport_init_data; bc->transport_callbacks = transport_callbacks; ks_q_create(&bc->sending, pool, 0); - ks_q_create(&bc->receiving, pool, 0); + //ks_q_create(&bc->receiving, pool, 0); *bcP = bc; return KS_STATUS_SUCCESS; @@ -91,17 +92,18 @@ KS_DECLARE(ks_status_t) blade_connection_destroy(blade_connection_t **bcP) blade_connection_shutdown(bc); ks_q_destroy(&bc->sending); - ks_q_destroy(&bc->receiving); + //ks_q_destroy(&bc->receiving); ks_pool_free(bc->pool, bcP); return KS_STATUS_SUCCESS; } -KS_DECLARE(ks_status_t) blade_connection_startup(blade_connection_t *bc) +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, @@ -112,7 +114,6 @@ KS_DECLARE(ks_status_t) blade_connection_startup(blade_connection_t *bc) KS_PRI_NORMAL, bc->pool) != KS_STATUS_SUCCESS) { // @todo error logging - blade_connection_disconnect(bc); return KS_STATUS_FAIL; } @@ -136,6 +137,13 @@ KS_DECLARE(ks_status_t) blade_connection_shutdown(blade_connection_t *bc) return KS_STATUS_SUCCESS; } +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); @@ -143,19 +151,72 @@ KS_DECLARE(void *) blade_connection_transport_get(blade_connection_t *bc) return bc->transport_data; } -KS_DECLARE(void) blade_connection_state_set(blade_connection_t *bc, blade_connection_state_t state) +KS_DECLARE(void) blade_connection_transport_set(blade_connection_t *bc, void *transport_data) { ks_assert(bc); - bc->transport_callbacks->onstate(bc, state, BLADE_CONNECTION_STATE_CONDITION_PRE); + 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); - blade_connection_state_set(bc, BLADE_CONNECTION_STATE_DISCONNECT); + if (bc->state != BLADE_CONNECTION_STATE_DETACH && bc->state != BLADE_CONNECTION_STATE_DISCONNECT) + blade_connection_state_set(bc, BLADE_CONNECTION_STATE_DETACH); } KS_DECLARE(ks_status_t) blade_connection_sending_push(blade_connection_t *bc, blade_identity_t *target, cJSON *json) @@ -178,26 +239,30 @@ KS_DECLARE(ks_status_t) blade_connection_sending_pop(blade_connection_t *bc, bla return KS_STATUS_SUCCESS; } -KS_DECLARE(ks_status_t) blade_connection_receiving_push(blade_connection_t *bc, cJSON *json) -{ - ks_assert(bc); - ks_assert(json); +// @todo may not need receiving queue on connection, by the time we are queueing we should have a session to receive into +//KS_DECLARE(ks_status_t) blade_connection_receiving_push(blade_connection_t *bc, cJSON *json) +//{ +// ks_assert(bc); +// ks_assert(json); - return ks_q_push(bc->receiving, json); -} +// return ks_q_push(bc->receiving, json); +//} -KS_DECLARE(ks_status_t) blade_connection_receiving_pop(blade_connection_t *bc, cJSON **json) -{ - ks_assert(bc); - ks_assert(json); +//KS_DECLARE(ks_status_t) blade_connection_receiving_pop(blade_connection_t *bc, cJSON **json) +//{ +// ks_assert(bc); +// ks_assert(json); - return ks_q_trypop(bc->receiving, (void **)json); -} +// return ks_q_trypop(bc->receiving, (void **)json); +//} void *blade_connection_state_thread(ks_thread_t *thread, void *data) { blade_connection_t *bc = NULL; - blade_connection_state_hook_t hook; + blade_connection_state_t state; + blade_transport_state_callback_t callback = NULL; + blade_connection_state_hook_t hook = BLADE_CONNECTION_STATE_HOOK_SUCCESS; + cJSON *json = NULL; ks_assert(thread); ks_assert(data); @@ -205,20 +270,34 @@ void *blade_connection_state_thread(ks_thread_t *thread, void *data) bc = (blade_connection_t *)data; while (!bc->shutdown) { - // @todo need to get messages from the transport into receiving queue, and pop messages from sending queue to write out using transport - // sending is relatively easy, but receiving cannot occur universally due to cases like kws_init() blocking and expecting data to be on the wire - // and other transports may have similar behaviours, but CONNECTIN, ATTACH, and READY require async message passing into application layer - // and sending whenever the response hits the queue + + // @todo pop from connection sending queue and call transport callback to write one message (passing target identity too) + // and delete the cJSON object here after returning from callback + - // @todo it's possible that onstate could handle receiving and sending messages during the appropriate states, but this means some states - // like CONNECTIN which may send and receive multiple messages require BYPASSing until the application layer updates the state or disconnects + // @todo seems like connection will not need a receiving queue as the session will exist prior to async transmissions + + state = bc->state; + hook = BLADE_CONNECTION_STATE_HOOK_SUCCESS; + callback = blade_connection_state_callback_lookup(bc, state); + + // @todo should this just go in the ready state callback? it's generalized here, so the callback for READY doesn't really + // need to do anything + if (state == BLADE_CONNECTION_STATE_READY && bc->transport_callbacks->onreceive(bc, &json) == KS_STATUS_SUCCESS && json) { + // @todo push json to session receiving queue + + } - hook = bc->transport_callbacks->onstate(bc, bc->state, BLADE_CONNECTION_STATE_CONDITION_POST); - if (hook == BLADE_CONNECTION_STATE_HOOK_DISCONNECT) - blade_connection_disconnect(bc); + if (callback) hook = callback(bc, BLADE_CONNECTION_STATE_CONDITION_POST); + + if (hook == BLADE_CONNECTION_STATE_HOOK_DISCONNECT && (state == BLADE_CONNECTION_STATE_DETACH || state == BLADE_CONNECTION_STATE_DISCONNECT)) + hook = BLADE_CONNECTION_STATE_HOOK_SUCCESS; + + if (hook == BLADE_CONNECTION_STATE_HOOK_DISCONNECT) blade_connection_disconnect(bc); else if (hook == BLADE_CONNECTION_STATE_HOOK_SUCCESS) { - // @todo pop from sending queue, and pass to transport callback to send out - switch (bc->state) { + switch (state) { + case BLADE_CONNECTION_STATE_DISCONNECT: + return NULL; case BLADE_CONNECTION_STATE_NEW: blade_connection_state_set(bc, BLADE_CONNECTION_STATE_CONNECT); break; @@ -226,10 +305,14 @@ void *blade_connection_state_thread(ks_thread_t *thread, void *data) blade_connection_state_set(bc, BLADE_CONNECTION_STATE_ATTACH); break; case BLADE_CONNECTION_STATE_ATTACH: + // @todo receive message with nullable session id for reconnect and some sort of secure token for a reconnect challenge? + // determine how much of session management is handled here... do we process these session negotiation messages without + // passing it up to the application layer? or does the application layer give back a session and build the response? blade_connection_state_set(bc, BLADE_CONNECTION_STATE_READY); break; case BLADE_CONNECTION_STATE_DETACH: - blade_connection_disconnect(bc); + // @todo detach from session if this connection is attached + blade_connection_state_set(bc, BLADE_CONNECTION_STATE_DISCONNECT); break; default: break; } diff --git a/libs/libblade/src/blade_module_wss.c b/libs/libblade/src/blade_module_wss.c index 3efe4307af..75f2993a51 100644 --- a/libs/libblade/src/blade_module_wss.c +++ b/libs/libblade/src/blade_module_wss.c @@ -37,6 +37,7 @@ 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; @@ -70,42 +71,81 @@ struct blade_transport_wss_s { kws_t *kws; }; +struct blade_transport_wss_init_s { + blade_module_wss_t *module; + ks_pool_t *pool; + + ks_socket_t sock; +}; + 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); -ks_status_t blade_module_wss_onload(blade_module_t **bmP, blade_handle_t *bh); -ks_status_t blade_module_wss_onunload(blade_module_t *bm); -ks_status_t blade_module_wss_onstartup(blade_module_t *bm, config_setting_t *config); -ks_status_t blade_module_wss_onshutdown(blade_module_t *bm); +ks_status_t blade_module_wss_on_load(blade_module_t **bmP, blade_handle_t *bh); +ks_status_t blade_module_wss_on_unload(blade_module_t *bm); +ks_status_t blade_module_wss_on_startup(blade_module_t *bm, config_setting_t *config); +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_onconnect(blade_connection_t **bcP, blade_module_t *bm, blade_identity_t *target); -blade_connection_rank_t blade_transport_wss_onrank(blade_connection_t *bc, blade_identity_t *target); -blade_connection_state_hook_t blade_transport_wss_onstate(blade_connection_t *bc, blade_connection_state_t state, blade_connection_state_condition_t condition); +ks_status_t blade_transport_wss_on_connect(blade_connection_t **bcP, blade_module_t *bm, blade_identity_t *target); +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, blade_identity_t *target, 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(blade_connection_t *bc, blade_connection_state_condition_t condition); +blade_connection_state_hook_t blade_transport_wss_on_state_ready(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); +ks_status_t blade_transport_wss_init_destroy(blade_transport_wss_init_t **bt_wssiP); static blade_module_callbacks_t g_module_wss_callbacks = { - blade_module_wss_onload, - blade_module_wss_onunload, - blade_module_wss_onstartup, - blade_module_wss_onshutdown, + 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_onconnect, - blade_transport_wss_onrank, - blade_transport_wss_onstate, + 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, + blade_transport_wss_on_state_detach, + blade_transport_wss_on_state_ready, + blade_transport_wss_on_state_ready, }; @@ -144,7 +184,7 @@ ks_status_t blade_module_wss_destroy(blade_module_wss_t **bm_wssP) bm_wss = *bm_wssP; - blade_module_wss_onshutdown(bm_wss->module); + blade_module_wss_on_shutdown(bm_wss->module); blade_module_destroy(&bm_wss->module); @@ -156,7 +196,7 @@ ks_status_t blade_module_wss_destroy(blade_module_wss_t **bm_wssP) return KS_STATUS_SUCCESS; } -ks_status_t blade_module_wss_onload(blade_module_t **bmP, blade_handle_t *bh) +ks_status_t blade_module_wss_on_load(blade_module_t **bmP, blade_handle_t *bh) { blade_module_wss_t *bm_wss = NULL; @@ -171,7 +211,7 @@ ks_status_t blade_module_wss_onload(blade_module_t **bmP, blade_handle_t *bh) return KS_STATUS_SUCCESS; } -ks_status_t blade_module_wss_onunload(blade_module_t *bm) +ks_status_t blade_module_wss_on_unload(blade_module_t *bm) { blade_module_wss_t *bm_wss = NULL; @@ -293,7 +333,7 @@ ks_status_t blade_module_wss_config(blade_module_wss_t *bm_wss, config_setting_t return KS_STATUS_SUCCESS; } -ks_status_t blade_module_wss_onstartup(blade_module_t *bm, config_setting_t *config) +ks_status_t blade_module_wss_on_startup(blade_module_t *bm, config_setting_t *config) { blade_module_wss_t *bm_wss = NULL; @@ -331,7 +371,7 @@ ks_status_t blade_module_wss_onstartup(blade_module_t *bm, config_setting_t *con return KS_STATUS_SUCCESS; } -ks_status_t blade_module_wss_onshutdown(blade_module_t *bm) +ks_status_t blade_module_wss_on_shutdown(blade_module_t *bm) { blade_module_wss_t *bm_wss = NULL; blade_transport_wss_t *bt_wss = NULL; @@ -423,6 +463,7 @@ ks_status_t blade_module_wss_listen(blade_module_wss_t *bm_wss, ks_sockaddr_t *a 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_transport_wss_t *bt_wss = NULL; blade_connection_t *bc = NULL; @@ -448,27 +489,32 @@ void *blade_module_wss_listeners_thread(ks_thread_t *thread, void *data) continue; } - blade_transport_wss_create(&bt_wss, bm_wss, sock); - ks_assert(bt_wss); + blade_transport_wss_init_create(&bt_wss_init, bm_wss, sock); + ks_assert(bt_wss_init); - blade_connection_create(&bc, bm_wss->handle, bt_wss, bm_wss->transport_callbacks); + blade_connection_create(&bc, bm_wss->handle, bt_wss_init, bm_wss->transport_callbacks); ks_assert(bc); - blade_connection_startup(bc); - + if (blade_connection_startup(bc, BLADE_CONNECTION_DIRECTION_INBOUND) != KS_STATUS_SUCCESS) { + blade_connection_destroy(&bc); + blade_transport_wss_init_destroy(&bt_wss_init); + ks_socket_close(&sock); + continue; + } list_append(&bm_wss->connected, bc); - blade_connection_state_set(bc, BLADE_CONNECTION_STATE_NEW); } } while (ks_q_trypop(bm_wss->disconnected, (void **)&bc) == KS_STATUS_SUCCESS) { + bt_wss_init = (blade_transport_wss_init_t *)blade_connection_transport_init_get(bc); bt_wss = (blade_transport_wss_t *)blade_connection_transport_get(bc); list_delete(&bm_wss->connected, bc); + if (bt_wss_init) blade_transport_wss_init_destroy(&bt_wss_init); blade_connection_destroy(&bc); - blade_transport_wss_destroy(&bt_wss); + if (bt_wss) blade_transport_wss_destroy(&bt_wss); } } @@ -512,7 +558,7 @@ ks_status_t blade_transport_wss_destroy(blade_transport_wss_t **bt_wssP) return KS_STATUS_SUCCESS; } -ks_status_t blade_transport_wss_onconnect(blade_connection_t **bcP, blade_module_t *bm, blade_identity_t *target) +ks_status_t blade_transport_wss_on_connect(blade_connection_t **bcP, blade_module_t *bm, blade_identity_t *target) { ks_assert(bcP); ks_assert(bm); @@ -525,14 +571,44 @@ ks_status_t blade_transport_wss_onconnect(blade_connection_t **bcP, blade_module return KS_STATUS_SUCCESS; } -blade_connection_rank_t blade_transport_wss_onrank(blade_connection_t *bc, blade_identity_t *target) +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) +{ + char *json_str = cJSON_PrintUnformatted(json); + ks_size_t json_str_len = 0; + if (!json_str) { + // @todo error logging + return KS_STATUS_FAIL; + } + json_str_len = strlen(json_str) + 1; // @todo determine if WSOC_TEXT null terminates when read_frame is called, or if it's safe to include like this + kws_write_frame(bt_wss->kws, WSOC_TEXT, json_str, json_str_len); + + free(json_str); + return KS_STATUS_SUCCESS; } +ks_status_t blade_transport_wss_on_send(blade_connection_t *bc, blade_identity_t *target, cJSON *json) +{ + blade_transport_wss_t *bt_wss = NULL; + + ks_assert(bc); + ks_assert(json); + + ks_log(KS_LOG_DEBUG, "Send Callback\n"); + + 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? @@ -559,12 +635,6 @@ ks_status_t blade_transport_wss_read(blade_transport_wss_t *bt_wss, cJSON **json return KS_STATUS_FAIL; } - //if (blade_handle_message_claim(blade_service_handle(peer->service), &message, frame_data, frame_data_len) != KS_STATUS_SUCCESS || !message) { - // @todo error logging - // return KS_STATUS_FAIL; - //} - - // @todo convert frame_data to cJSON safely, make sure data is null-terminated at frame_data_len if (!(*json = cJSON_Parse((char *)frame_data))) { return KS_STATUS_FAIL; } @@ -572,77 +642,170 @@ ks_status_t blade_transport_wss_read(blade_transport_wss_t *bt_wss, cJSON **json return KS_STATUS_SUCCESS; } -ks_status_t blade_transport_wss_write(blade_transport_wss_t *bt_wss, cJSON *json) -{ - //blade_message_get(message, &target, &json); - char *json_str = cJSON_PrintUnformatted(json); - ks_size_t json_str_len = 0; - if (!json_str) { - // @todo error logging - return KS_STATUS_FAIL; - } - json_str_len = strlen(json_str) + 1; - kws_write_frame(bt_wss->kws, WSOC_TEXT, json_str, json_str_len); - - return KS_STATUS_SUCCESS; -} - -blade_connection_state_hook_t blade_transport_wss_onstate(blade_connection_t *bc, blade_connection_state_t state, blade_connection_state_condition_t condition) +ks_status_t blade_transport_wss_on_receive(blade_connection_t *bc, cJSON **json) { blade_transport_wss_t *bt_wss = NULL; - //cJSON *json = NULL; ks_assert(bc); + ks_assert(json); + + ks_log(KS_LOG_DEBUG, "Receive Callback\n"); bt_wss = (blade_transport_wss_t *)blade_connection_transport_get(bc); - switch (state) { - case BLADE_CONNECTION_STATE_DISCONNECT: - { - if (condition == BLADE_CONNECTION_STATE_CONDITION_POST) { - ks_q_push(bt_wss->module->disconnected, bc); - blade_connection_state_set(bc, BLADE_CONNECTION_STATE_NONE); - } - break; - } - case BLADE_CONNECTION_STATE_NEW: - { - if (condition == BLADE_CONNECTION_STATE_CONDITION_POST) { - // @todo: SSL init stuffs based on data from peer->service->config_websockets_ssl to pass into kws_init - if (kws_init(&bt_wss->kws, bt_wss->sock, NULL, NULL, KWS_BLOCK, bt_wss->pool) != KS_STATUS_SUCCESS) { - // @todo error logging - return BLADE_CONNECTION_STATE_HOOK_DISCONNECT; - } - } - break; - } - case BLADE_CONNECTION_STATE_CONNECT: - { - // @todo abstract read message and write message, so these can be called from connection and processed from there - - //if (blade_transport_wss_read(bt_wss, &json) != KS_STATUS_SUCCESS) return BLADE_CONNECTION_STATEHOOK_DISCONNECT; + return blade_transport_wss_read(bt_wss, json); +} - //if (json) { - // @todo processing connectin messages for identity registration - // cJSON_Delete(json); - //blade_connection_receiving_push(conn, json); - //} +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; + + 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); + + ks_q_push(bt_wss->module->disconnected, bc); - // @todo wrap identity + json into an envelope for queueing through the connection - //while (blade_connection_sending_pop(bc, (void **)&json) == KS_STATUS_SUCCESS && json) { - // ks_status_t ret = blade_transport_wss_write(bt_wss, json); - // cJSON_Delete(json); - // if (ret != KS_STATUS_SUCCESS) return BLADE_CONNECTION_STATE_HOOK_DISCONNECT; - //} - return BLADE_CONNECTION_STATE_HOOK_SUCCESS; - //break; - } - default: break; - } - 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) +{ + 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; + + 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) { + // @todo error logging + 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) +{ + 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_attach_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); + // @todo Establish sessid and discover existing session or create and register new session through BLADE commands + // Set session state to CONNECT if its new or RECONNECT if existing + // start session and its thread if its new + + return BLADE_CONNECTION_STATE_HOOK_BYPASS; +} + +blade_connection_state_hook_t blade_transport_wss_on_state_attach_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_detach(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(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; +} + +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) +{ + 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; + + *bt_wssiP = bt_wssi; + + 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; + + ks_pool_free(bt_wssi->pool, bt_wssiP); + + return KS_STATUS_SUCCESS; +} + /* For Emacs: * Local Variables: * mode:c diff --git a/libs/libblade/src/include/blade_connection.h b/libs/libblade/src/include/blade_connection.h index d48ec8c221..70ee105b7d 100644 --- a/libs/libblade/src/include/blade_connection.h +++ b/libs/libblade/src/include/blade_connection.h @@ -41,9 +41,11 @@ KS_DECLARE(ks_status_t) blade_connection_create(blade_connection_t **bcP, 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); +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(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); diff --git a/libs/libblade/src/include/blade_types.h b/libs/libblade/src/include/blade_types.h index cc2c054197..c2c961266b 100644 --- a/libs/libblade/src/include/blade_types.h +++ b/libs/libblade/src/include/blade_types.h @@ -62,8 +62,8 @@ typedef enum { } blade_connection_state_t; typedef enum { - BLADE_CONNECTION_DIRECTION_IN, - BLADE_CONNECTION_DIRECTION_OUT, + BLADE_CONNECTION_DIRECTION_INBOUND, + BLADE_CONNECTION_DIRECTION_OUTBOUND, } blade_connection_direction_t; typedef enum { @@ -99,14 +99,28 @@ struct blade_module_callbacks_s { typedef ks_status_t (*blade_transport_connect_callback_t)(blade_connection_t **bcP, blade_module_t *bm, blade_identity_t *target); typedef blade_connection_rank_t (*blade_transport_rank_callback_t)(blade_connection_t *bc, blade_identity_t *target); -typedef blade_connection_state_hook_t (*blade_transport_state_callback_t)(blade_connection_t *bc, - blade_connection_state_t state, - blade_connection_state_condition_t condition); +typedef ks_status_t (*blade_transport_send_callback_t)(blade_connection_t *bc, blade_identity_t *target, 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_state_callback_t onstate; + 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; }; From 5eb61cf53563bec4aa263bdd8aa92c02fddde73c Mon Sep 17 00:00:00 2001 From: colm Date: Fri, 10 Feb 2017 09:13:22 -0500 Subject: [PATCH 16/34] FS-9952: Update for compile issues --- libs/libblade/src/blade_rpcproto.c | 24 +++++++++++----------- libs/libblade/src/include/blade_rpcproto.h | 4 ++-- libs/libblade/src/include/blade_types.h | 1 + 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/libs/libblade/src/blade_rpcproto.c b/libs/libblade/src/blade_rpcproto.c index c00d354e53..53b546663c 100644 --- a/libs/libblade/src/blade_rpcproto.c +++ b/libs/libblade/src/blade_rpcproto.c @@ -167,18 +167,18 @@ KS_DECLARE(ks_status_t) blade_rpc_init(ks_pool_t *pool) } -KS_DECLARE(ks_status_t) blade_rpc_onconnect(ks_pool_t *pool, blade_peer_t* peer) -{ - - - return KS_STATUS_FAIL; -} - -KS_DECLARE(ks_status_t) blade_rpc_disconnect(blade_peer_t* peer) -{ - - return KS_STATUS_FAIL; -} +//KS_DECLARE(ks_status_t) blade_rpc_onconnect(ks_pool_t *pool, blade_peer_t* peer) +//{ +// +// +// return KS_STATUS_FAIL; +//} +// +//KS_DECLARE(ks_status_t) blade_rpc_disconnect(blade_peer_t* peer) +//{ +// +// return KS_STATUS_FAIL; +//} diff --git a/libs/libblade/src/include/blade_rpcproto.h b/libs/libblade/src/include/blade_rpcproto.h index 621c10a48a..862ccd9f00 100644 --- a/libs/libblade/src/include/blade_rpcproto.h +++ b/libs/libblade/src/include/blade_rpcproto.h @@ -106,8 +106,8 @@ KS_DECLARE(ks_status_t)blade_rpc_inherit_template(char *namespace, char* templat * peer create/destroy * ------------------- */ -KS_DECLARE(ks_status_t) blade_rpc_onconnect(ks_pool_t *pool, blade_peer_t* peer); -KS_DECLARE(ks_status_t) blade_rpc_disconnect(blade_peer_t* peer); +//KS_DECLARE(ks_status_t) blade_rpc_onconnect(ks_pool_t *pool, blade_peer_t* peer); +//KS_DECLARE(ks_status_t) blade_rpc_disconnect(blade_peer_t* peer); /* * send message diff --git a/libs/libblade/src/include/blade_types.h b/libs/libblade/src/include/blade_types.h index c2c961266b..6df447ec60 100644 --- a/libs/libblade/src/include/blade_types.h +++ b/libs/libblade/src/include/blade_types.h @@ -34,6 +34,7 @@ #ifndef _BLADE_TYPES_H_ #define _BLADE_TYPES_H_ #include +#include KS_BEGIN_EXTERN_C From fa6a4e768672b37d56319f2f8e36722353bae258 Mon Sep 17 00:00:00 2001 From: colm Date: Fri, 10 Feb 2017 13:22:05 -0500 Subject: [PATCH 17/34] FS-9952: Add rpc h files to ks --- libs/libks/src/include/ks.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libs/libks/src/include/ks.h b/libs/libks/src/include/ks.h index b19f62e463..9f0e511ba4 100644 --- a/libs/libks/src/include/ks.h +++ b/libs/libks/src/include/ks.h @@ -137,6 +137,8 @@ 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_rpcmessage.h" +#include "ks_base64.h" KS_END_EXTERN_C From 7e2d375d76e5edc642cd7d185b6609861643500b Mon Sep 17 00:00:00 2001 From: Shane Bryldt Date: Fri, 10 Feb 2017 18:14:48 +0000 Subject: [PATCH 18/34] FS-9952: Added envelope to queue sending messages, prepping for initial server transport testing --- libs/libblade/src/blade_connection.c | 123 +++++++++++++++++++-------- libs/libblade/src/blade_identity.c | 3 + libs/libblade/src/blade_module_wss.c | 34 ++++++-- libs/libblade/test/bladec.c | 13 ++- libs/libblade/test/bladec.cfg | 46 +++------- 5 files changed, 133 insertions(+), 86 deletions(-) diff --git a/libs/libblade/src/blade_connection.c b/libs/libblade/src/blade_connection.c index 69f1a39366..e18f2c247e 100644 --- a/libs/libblade/src/blade_connection.c +++ b/libs/libblade/src/blade_connection.c @@ -51,6 +51,49 @@ struct blade_connection_s { //ks_q_t *receiving; }; +// @todo may want to make this reusable for session as it'll need to queue the same details during temporary connection loss +typedef struct blade_connection_sending_s blade_connection_sending_t; +struct blade_connection_sending_s { + ks_pool_t *pool; + blade_identity_t *target; + cJSON *json; +}; + +ks_status_t blade_connection_sending_create(blade_connection_sending_t **bcsP, ks_pool_t *pool, blade_identity_t *target, cJSON *json) +{ + blade_connection_sending_t *bcs = NULL; + + ks_assert(bcsP); + ks_assert(pool); + ks_assert(json); + + bcs = ks_pool_alloc(pool, sizeof(blade_connection_sending_t)); + bcs->pool = pool; + bcs->target = target; + bcs->json = json; + *bcsP = bcs; + + return KS_STATUS_SUCCESS; +} + +ks_status_t blade_connection_sending_destroy(blade_connection_sending_t **bcsP) +{ + blade_connection_sending_t *bcs = NULL; + + ks_assert(bcsP); + ks_assert(*bcsP); + + bcs = *bcsP; + + if (bcs->target) blade_identity_destroy(&bcs->target); + if (bcs->json) cJSON_Delete(bcs->json); + + ks_pool_free(bcs->pool, bcsP); + + return KS_STATUS_SUCCESS; +} + + void *blade_connection_state_thread(ks_thread_t *thread, void *data); @@ -74,7 +117,8 @@ KS_DECLARE(ks_status_t) blade_connection_create(blade_connection_t **bcP, bc->transport_init_data = transport_init_data; bc->transport_callbacks = transport_callbacks; ks_q_create(&bc->sending, pool, 0); - //ks_q_create(&bc->receiving, pool, 0); + ks_assert(bc->sending); + *bcP = bc; return KS_STATUS_SUCCESS; @@ -92,7 +136,6 @@ KS_DECLARE(ks_status_t) blade_connection_destroy(blade_connection_t **bcP) blade_connection_shutdown(bc); ks_q_destroy(&bc->sending); - //ks_q_destroy(&bc->receiving); ks_pool_free(bc->pool, bcP); @@ -122,6 +165,8 @@ KS_DECLARE(ks_status_t) blade_connection_startup(blade_connection_t *bc, blade_c KS_DECLARE(ks_status_t) blade_connection_shutdown(blade_connection_t *bc) { + blade_connection_sending_t *bcs = NULL; + ks_assert(bc); if (bc->state_thread) { @@ -131,8 +176,7 @@ KS_DECLARE(ks_status_t) blade_connection_shutdown(blade_connection_t *bc) bc->shutdown = KS_FALSE; } - //while (ks_q_trypop(bc->sending, (void **)&message) == KS_STATUS_SUCCESS && message) blade_message_discard(&message); - //while (ks_q_trypop(bc->receiving, (void **)&message) == KS_STATUS_SUCCESS && message) blade_message_discard(&message); + while (ks_q_trypop(bc->sending, (void **)&bcs) == KS_STATUS_SUCCESS && bcs) blade_connection_sending_destroy(&bcs); return KS_STATUS_SUCCESS; } @@ -221,40 +265,40 @@ KS_DECLARE(void) blade_connection_disconnect(blade_connection_t *bc) KS_DECLARE(ks_status_t) blade_connection_sending_push(blade_connection_t *bc, blade_identity_t *target, cJSON *json) { + blade_connection_sending_t *bcs = NULL; + ks_assert(bc); ks_assert(json); - // @todo need internal envelope to wrap an identity object and a json object just for the queue + blade_connection_sending_create(&bcs, bc->pool, target, json); + ks_assert(bcs); - return KS_STATUS_SUCCESS; + return ks_q_push(bc->sending, bcs); } KS_DECLARE(ks_status_t) blade_connection_sending_pop(blade_connection_t *bc, blade_identity_t **target, cJSON **json) { + ks_status_t ret = KS_STATUS_SUCCESS; + blade_connection_sending_t *bcs = NULL; + ks_assert(bc); ks_assert(json); - // @todo need internal envelope to wrap an identity object and a json object just for the queue - - return KS_STATUS_SUCCESS; + ret = ks_q_trypop(bc->sending, (void **)&bcs); + + if (bcs) { + if (target) *target = bcs->target; + *json = bcs->json; + + bcs->target = NULL; + bcs->json = NULL; + + blade_connection_sending_destroy(&bcs); + } + + return ret; } -// @todo may not need receiving queue on connection, by the time we are queueing we should have a session to receive into -//KS_DECLARE(ks_status_t) blade_connection_receiving_push(blade_connection_t *bc, cJSON *json) -//{ -// ks_assert(bc); -// ks_assert(json); - -// return ks_q_push(bc->receiving, json); -//} - -//KS_DECLARE(ks_status_t) blade_connection_receiving_pop(blade_connection_t *bc, cJSON **json) -//{ -// ks_assert(bc); -// ks_assert(json); - -// return ks_q_trypop(bc->receiving, (void **)json); -//} void *blade_connection_state_thread(ks_thread_t *thread, void *data) { @@ -262,6 +306,7 @@ void *blade_connection_state_thread(ks_thread_t *thread, void *data) blade_connection_state_t state; blade_transport_state_callback_t callback = NULL; blade_connection_state_hook_t hook = BLADE_CONNECTION_STATE_HOOK_SUCCESS; + blade_identity_t *target = NULL; cJSON *json = NULL; ks_assert(thread); @@ -270,22 +315,28 @@ void *blade_connection_state_thread(ks_thread_t *thread, void *data) bc = (blade_connection_t *)data; while (!bc->shutdown) { - - // @todo pop from connection sending queue and call transport callback to write one message (passing target identity too) - // and delete the cJSON object here after returning from callback - - // @todo seems like connection will not need a receiving queue as the session will exist prior to async transmissions - state = bc->state; hook = BLADE_CONNECTION_STATE_HOOK_SUCCESS; callback = blade_connection_state_callback_lookup(bc, state); - // @todo should this just go in the ready state callback? it's generalized here, so the callback for READY doesn't really - // need to do anything - if (state == BLADE_CONNECTION_STATE_READY && bc->transport_callbacks->onreceive(bc, &json) == KS_STATUS_SUCCESS && json) { - // @todo push json to session receiving queue - + while (blade_connection_sending_pop(bc, &target, &json) == KS_STATUS_SUCCESS && json) { + if (bc->transport_callbacks->onsend(bc, target, json) != KS_STATUS_SUCCESS) { + blade_connection_disconnect(bc); + break; + } + } + + if (state == BLADE_CONNECTION_STATE_READY) { + do { + if (bc->transport_callbacks->onreceive(bc, &json) != KS_STATUS_SUCCESS) { + blade_connection_disconnect(bc); + break; + } + if (json) { + // @todo push json to session receiving queue + } + } while (json) ; } if (callback) hook = callback(bc, BLADE_CONNECTION_STATE_CONDITION_POST); diff --git a/libs/libblade/src/blade_identity.c b/libs/libblade/src/blade_identity.c index 3e814e80af..034923c7de 100644 --- a/libs/libblade/src/blade_identity.c +++ b/libs/libblade/src/blade_identity.c @@ -75,6 +75,9 @@ KS_DECLARE(ks_status_t) blade_identity_parse(blade_identity_t *bi, const char *u ks_assert(uri); if (bi->uri) ks_pool_free(bi->pool, &bi->uri); + bi->uri = ks_pstrdup(bi->pool, uri); + + // @todo parse into components return KS_STATUS_SUCCESS; } diff --git a/libs/libblade/src/blade_module_wss.c b/libs/libblade/src/blade_module_wss.c index 75f2993a51..f7c485b2ff 100644 --- a/libs/libblade/src/blade_module_wss.c +++ b/libs/libblade/src/blade_module_wss.c @@ -342,6 +342,8 @@ ks_status_t blade_module_wss_on_startup(blade_module_t *bm, config_setting_t *co bm_wss = (blade_module_wss_t *)blade_module_data_get(bm); + // @todo register wss transport to the blade_handle_t + 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; @@ -381,6 +383,8 @@ ks_status_t blade_module_wss_on_shutdown(blade_module_t *bm) bm_wss = (blade_module_wss_t *)blade_module_data_get(bm); + // @todo unregister wss transport from the blade_handle_t + if (bm_wss->listeners_thread) { bm_wss->shutdown = KS_TRUE; ks_thread_join(bm_wss->listeners_thread); @@ -396,6 +400,9 @@ ks_status_t blade_module_wss_on_shutdown(blade_module_t *bm) bm_wss->listeners_count = 0; if (bm_wss->listeners_poll) ks_pool_free(bm_wss->pool, &bm_wss->listeners_poll); + // @todo connections should be gracefully disconnected so that they detach from sessions properly + // which means this should occur before the listeners thread is terminated, which requires that + // the listener sockets be made inactive (or closed) to stop accepting while shutting down while (ks_q_trypop(bm_wss->disconnected, (void **)&bc) == KS_STATUS_SUCCESS) ; list_iterator_start(&bm_wss->connected); while (list_iterator_hasnext(&bm_wss->connected)) { @@ -581,22 +588,30 @@ blade_connection_rank_t blade_transport_wss_on_rank(blade_connection_t *bc, blad 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) { // @todo error logging - return KS_STATUS_FAIL; + ret = KS_STATUS_FAIL; + goto done; } json_str_len = strlen(json_str) + 1; // @todo determine if WSOC_TEXT null terminates when read_frame is called, or if it's safe to include like this - kws_write_frame(bt_wss->kws, WSOC_TEXT, json_str, json_str_len); + if (kws_write_frame(bt_wss->kws, WSOC_TEXT, json_str, json_str_len) != json_str_len) { + // @todo error logging + ret = KS_STATUS_FAIL; + goto done; + } - free(json_str); + done: + if (json_str) free(json_str); - return KS_STATUS_SUCCESS; + return ret; } ks_status_t blade_transport_wss_on_send(blade_connection_t *bc, blade_identity_t *target, cJSON *json) { + ks_status_t ret = KS_STATUS_SUCCESS; blade_transport_wss_t *bt_wss = NULL; ks_assert(bc); @@ -606,7 +621,13 @@ ks_status_t blade_transport_wss_on_send(blade_connection_t *bc, blade_identity_t bt_wss = (blade_transport_wss_t *)blade_connection_transport_get(bc); - return blade_transport_wss_write(bt_wss, json); + ret = blade_transport_wss_write(bt_wss, json); + + // @todo use reference counting on blade_identity_t and cJSON objects + if (target) blade_identity_destroy(&target); + cJSON_Delete(json); + + return ret; } ks_status_t blade_transport_wss_read(blade_transport_wss_t *bt_wss, cJSON **json) @@ -743,7 +764,8 @@ blade_connection_state_hook_t blade_transport_wss_on_state_attach_inbound(blade_ // @todo Establish sessid and discover existing session or create and register new session through BLADE commands // Set session state to CONNECT if its new or RECONNECT if existing // start session and its thread if its new - + + ks_sleep_ms(1000); // @todo temporary testing, remove this and return success once negotiations are done return BLADE_CONNECTION_STATE_HOOK_BYPASS; } diff --git a/libs/libblade/test/bladec.c b/libs/libblade/test/bladec.c index aadfed612e..1b7243bca8 100644 --- a/libs/libblade/test/bladec.c +++ b/libs/libblade/test/bladec.c @@ -16,7 +16,6 @@ char g_console_input[CONSOLE_INPUT_MAX]; size_t g_console_input_length = 0; size_t g_console_input_eol = 0; -void service_peer_state_callback(blade_service_t *service, blade_peer_t *peer, blade_peerstate_t state); void loop(blade_handle_t *bh); void process_console_input(blade_handle_t *bh, char *line); @@ -71,11 +70,13 @@ int main(int argc, char **argv) return EXIT_FAILURE; } - if (blade_handle_startup(bh, config_blade, service_peer_state_callback) != KS_STATUS_SUCCESS) { + if (blade_handle_startup(bh, config_blade) != KS_STATUS_SUCCESS) { ks_log(KS_LOG_ERROR, "Blade startup failed\n"); return EXIT_FAILURE; } + // @todo get to wss module callbacks, call onload to kick off registration + loop(bh); blade_handle_destroy(&bh); @@ -85,12 +86,8 @@ int main(int argc, char **argv) return 0; } -void service_peer_state_callback(blade_service_t *service, blade_peer_t *peer, blade_peerstate_t state) -{ - // @todo log output and pop peer messages if state == BLADE_PEERSTATE_RECEIVING - ks_log(KS_LOG_INFO, "service peer state callback: %d\n", (int)state); -} - + + void buffer_console_input(void) { ssize_t bytes = 0; diff --git a/libs/libblade/test/bladec.cfg b/libs/libblade/test/bladec.cfg index a14a4e7f7a..95a7f24397 100644 --- a/libs/libblade/test/bladec.cfg +++ b/libs/libblade/test/bladec.cfg @@ -1,28 +1,5 @@ blade: { - # client stuff, for peers who connect out to services - client: - { - directory: - { - # todo: hints for ways to find a directory service, at least kws client_data for now - # add DNS SRV in the future - uri = "???:127.0.0.1+2100:???"; # todo: confirm expected format, "uri:host:proto" - - websocket: - { - # SSL group is optional, disabled when absent - ssl: - { - # todo: client SSL stuffs here - }; - }; - }; - }; - - - # server stuff, for services that peers connect to - # todo: consider encapsulating in a "server" group for organizational structure datastore: { database: @@ -30,21 +7,18 @@ blade: path = ":mem:"; }; }; - service: + wss: { - websockets: + endpoints: { - endpoints: - { - ipv4 = ( { address = "0.0.0.0", port = 2100 } ); - ipv6 = ( { address = "::", port = 2100 } ); - backlog = 128; - }; - # SSL group is optional, disabled when absent - ssl: - { - # todo: service SSL stuffs here - }; + 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 }; }; }; From 2b3e2646e3e26676da740b7770182f1e4fb474b7 Mon Sep 17 00:00:00 2001 From: Shane Bryldt Date: Fri, 10 Feb 2017 18:31:38 +0000 Subject: [PATCH 19/34] FS-9952: Updated bladec test, it successfully listens for connections, further testing required --- libs/libblade/src/blade_module_wss.c | 26 ++++++++++++++---------- libs/libblade/src/include/blade_module.h | 6 ++++++ libs/libblade/test/bladec.c | 10 ++++++++- 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/libs/libblade/src/blade_module_wss.c b/libs/libblade/src/blade_module_wss.c index f7c485b2ff..109082b232 100644 --- a/libs/libblade/src/blade_module_wss.c +++ b/libs/libblade/src/blade_module_wss.c @@ -83,10 +83,11 @@ struct blade_transport_wss_init_s { 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); -ks_status_t blade_module_wss_on_load(blade_module_t **bmP, blade_handle_t *bh); -ks_status_t blade_module_wss_on_unload(blade_module_t *bm); -ks_status_t blade_module_wss_on_startup(blade_module_t *bm, config_setting_t *config); -ks_status_t blade_module_wss_on_shutdown(blade_module_t *bm); +// @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); @@ -153,19 +154,22 @@ static blade_transport_callbacks_t g_transport_wss_callbacks = 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); - bm_wss = ks_pool_alloc(bm_wss->pool, sizeof(blade_module_wss_t)); + pool = blade_handle_pool_get(bh); + + bm_wss = ks_pool_alloc(pool, sizeof(blade_module_wss_t)); bm_wss->handle = bh; - bm_wss->pool = blade_handle_pool_get(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); ks_q_create(&bm_wss->disconnected, bm_wss->pool, 0); ks_assert(bm_wss->disconnected); @@ -196,7 +200,7 @@ ks_status_t blade_module_wss_destroy(blade_module_wss_t **bm_wssP) return KS_STATUS_SUCCESS; } -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_load(blade_module_t **bmP, blade_handle_t *bh) { blade_module_wss_t *bm_wss = NULL; @@ -211,7 +215,7 @@ ks_status_t blade_module_wss_on_load(blade_module_t **bmP, blade_handle_t *bh) return KS_STATUS_SUCCESS; } -ks_status_t blade_module_wss_on_unload(blade_module_t *bm) +KS_DECLARE(ks_status_t) blade_module_wss_on_unload(blade_module_t *bm) { blade_module_wss_t *bm_wss = NULL; @@ -333,7 +337,7 @@ ks_status_t blade_module_wss_config(blade_module_wss_t *bm_wss, config_setting_t return KS_STATUS_SUCCESS; } -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_startup(blade_module_t *bm, config_setting_t *config) { blade_module_wss_t *bm_wss = NULL; @@ -373,7 +377,7 @@ ks_status_t blade_module_wss_on_startup(blade_module_t *bm, config_setting_t *co return KS_STATUS_SUCCESS; } -ks_status_t blade_module_wss_on_shutdown(blade_module_t *bm) +KS_DECLARE(ks_status_t) blade_module_wss_on_shutdown(blade_module_t *bm) { blade_module_wss_t *bm_wss = NULL; blade_transport_wss_t *bt_wss = NULL; diff --git a/libs/libblade/src/include/blade_module.h b/libs/libblade/src/include/blade_module.h index ef2ba88de7..706a8e892c 100644 --- a/libs/libblade/src/include/blade_module.h +++ b/libs/libblade/src/include/blade_module.h @@ -39,6 +39,12 @@ 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(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_END_EXTERN_C #endif diff --git a/libs/libblade/test/bladec.c b/libs/libblade/test/bladec.c index 1b7243bca8..b2abc8b11b 100644 --- a/libs/libblade/test/bladec.c +++ b/libs/libblade/test/bladec.c @@ -45,6 +45,7 @@ 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; ks_global_set_default_logger(KS_LOG_LEVEL_DEBUG); @@ -75,7 +76,14 @@ int main(int argc, char **argv) return EXIT_FAILURE; } - // @todo get to wss module callbacks, call onload to kick off registration + 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; + } loop(bh); From 942ae77bde1c6d1d5a77fbb5729d8b333fba4909 Mon Sep 17 00:00:00 2001 From: Shane Bryldt Date: Mon, 13 Feb 2017 19:07:33 +0000 Subject: [PATCH 20/34] FS-9952: Added initial support for registering transports, and initial untested code for parsing identities --- libs/libblade/src/blade_identity.c | 52 +++++++++++++++++++++++-- libs/libblade/src/blade_module_wss.c | 8 ++-- libs/libblade/src/blade_stack.c | 46 ++++++++++++++++++++++ libs/libblade/src/include/blade_stack.h | 3 ++ libs/libblade/src/include/blade_types.h | 2 + libs/libblade/test/bladec.cfg | 4 ++ libs/libblade/test/bladec2.cfg | 29 ++++++++++++++ 7 files changed, 138 insertions(+), 6 deletions(-) create mode 100644 libs/libblade/test/bladec2.cfg diff --git a/libs/libblade/src/blade_identity.c b/libs/libblade/src/blade_identity.c index 034923c7de..fafbfb5ae8 100644 --- a/libs/libblade/src/blade_identity.c +++ b/libs/libblade/src/blade_identity.c @@ -37,7 +37,12 @@ struct blade_identity_s { ks_pool_t *pool; const char *uri; - // @todo breakdown of uri into constituent parts + + const char *components; + const char *name; + const char *domain; + const char *resource; + ks_hash_t *parameters; }; @@ -63,6 +68,11 @@ KS_DECLARE(ks_status_t) blade_identity_destroy(blade_identity_t **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); @@ -71,14 +81,50 @@ 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) { + char *tmp = NULL; + char *tmp2 = NULL; + ks_assert(bi); ks_assert(uri); - if (bi->uri) ks_pool_free(bi->pool, &bi->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); - // @todo parse into components + 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; } diff --git a/libs/libblade/src/blade_module_wss.c b/libs/libblade/src/blade_module_wss.c index 109082b232..b98bbb0814 100644 --- a/libs/libblade/src/blade_module_wss.c +++ b/libs/libblade/src/blade_module_wss.c @@ -130,6 +130,8 @@ static blade_module_callbacks_t g_module_wss_callbacks = static blade_transport_callbacks_t g_transport_wss_callbacks = { + "wss", + blade_transport_wss_on_connect, blade_transport_wss_on_rank, blade_transport_wss_on_send, @@ -346,8 +348,6 @@ KS_DECLARE(ks_status_t) blade_module_wss_on_startup(blade_module_t *bm, config_s bm_wss = (blade_module_wss_t *)blade_module_data_get(bm); - // @todo register wss transport to the blade_handle_t - 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; @@ -374,6 +374,8 @@ KS_DECLARE(ks_status_t) blade_module_wss_on_startup(blade_module_t *bm, config_s KS_PRI_NORMAL, bm_wss->pool) != KS_STATUS_SUCCESS) return KS_STATUS_FAIL; + blade_handle_transport_register(bm_wss->handle, bm_wss->transport_callbacks); + return KS_STATUS_SUCCESS; } @@ -387,7 +389,7 @@ KS_DECLARE(ks_status_t) blade_module_wss_on_shutdown(blade_module_t *bm) bm_wss = (blade_module_wss_t *)blade_module_data_get(bm); - // @todo unregister wss transport from the blade_handle_t + blade_handle_transport_unregister(bm_wss->handle, bm_wss->transport_callbacks); if (bm_wss->listeners_thread) { bm_wss->shutdown = KS_TRUE; diff --git a/libs/libblade/src/blade_stack.c b/libs/libblade/src/blade_stack.c index aeac8682db..02f2dc6df3 100644 --- a/libs/libblade/src/blade_stack.c +++ b/libs/libblade/src/blade_stack.c @@ -47,6 +47,7 @@ struct blade_handle_s { config_setting_t *config_service; config_setting_t *config_datastore; + ks_hash_t *transports; ks_q_t *messages_discarded; blade_datastore_t *datastore; @@ -76,6 +77,8 @@ KS_DECLARE(ks_status_t) blade_handle_destroy(blade_handle_t **bhP) ks_q_destroy(&bh->messages_discarded); } + ks_hash_destroy(&bh->transports); + if (bh->tpool && (flags & BH_MYTPOOL)) ks_thread_pool_destroy(&bh->tpool); ks_pool_free(bh->pool, &bh); @@ -109,6 +112,9 @@ KS_DECLARE(ks_status_t) blade_handle_create(blade_handle_t **bhP, ks_pool_t *poo 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); + // @todo check thresholds from config, for now just ensure it doesn't grow out of control, allow 100 discarded messages ks_q_create(&bh->messages_discarded, bh->pool, 100); ks_assert(bh->messages_discarded); @@ -184,6 +190,46 @@ KS_DECLARE(ks_thread_pool_t *) blade_handle_tpool_get(blade_handle_t *bh) return bh->tpool; } +KS_DECLARE(ks_status_t) blade_handle_transport_register(blade_handle_t *bh, blade_transport_callbacks_t *callbacks) +{ + ks_assert(bh); + ks_assert(callbacks); + + ks_hash_write_lock(bh->transports); + ks_hash_insert(bh->transports, (void *)callbacks->name, callbacks); + ks_hash_write_unlock(bh->transports); + + ks_log(KS_LOG_DEBUG, "Transport Registered: %s\n", callbacks->name); + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_handle_transport_unregister(blade_handle_t *bh, blade_transport_callbacks_t *callbacks) +{ + ks_assert(bh); + ks_assert(callbacks); + + ks_hash_write_lock(bh->transports); + ks_hash_remove(bh->transports, (void *)callbacks->name); + ks_hash_write_unlock(bh->transports); + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_handle_connect(blade_handle_t *bh, blade_connection_t **bcP, blade_identity_t *target) +{ + ks_assert(bh); + ks_assert(target); + + ks_hash_read_lock(bh->transports); + // @todo find transport for target, check if target specifies explicit transport parameter first, otherwise use onrank and keep highest ranked callbacks + ks_hash_read_unlock(bh->transports); + + // transport_callbacks->onconnect(bcP, target); + + return KS_STATUS_SUCCESS; +} + KS_DECLARE(ks_status_t) blade_handle_message_claim(blade_handle_t *bh, blade_message_t **message, void *data, ks_size_t data_length) { blade_message_t *msg = NULL; diff --git a/libs/libblade/src/include/blade_stack.h b/libs/libblade/src/include/blade_stack.h index 04daa932d6..78b4429376 100644 --- a/libs/libblade/src/include/blade_stack.h +++ b/libs/libblade/src/include/blade_stack.h @@ -48,6 +48,9 @@ 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_transport_callbacks_t *callbacks); +KS_DECLARE(ks_status_t) blade_handle_transport_unregister(blade_handle_t *bh, blade_transport_callbacks_t *callbacks); + KS_DECLARE(ks_status_t) blade_handle_message_claim(blade_handle_t *bh, blade_message_t **message, void *data, ks_size_t data_length); KS_DECLARE(ks_status_t) blade_handle_message_discard(blade_handle_t *bh, blade_message_t **message); diff --git a/libs/libblade/src/include/blade_types.h b/libs/libblade/src/include/blade_types.h index 6df447ec60..aad7e4aaa8 100644 --- a/libs/libblade/src/include/blade_types.h +++ b/libs/libblade/src/include/blade_types.h @@ -105,6 +105,8 @@ typedef ks_status_t (*blade_transport_receive_callback_t)(blade_connection_t *bc 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 { + const char *name; + blade_transport_connect_callback_t onconnect; blade_transport_rank_callback_t onrank; blade_transport_send_callback_t onsend; diff --git a/libs/libblade/test/bladec.cfg b/libs/libblade/test/bladec.cfg index 95a7f24397..2233b34608 100644 --- a/libs/libblade/test/bladec.cfg +++ b/libs/libblade/test/bladec.cfg @@ -1,5 +1,9 @@ blade: { + identity = "directory@domain"; + directory: + { + }; datastore: { database: diff --git a/libs/libblade/test/bladec2.cfg b/libs/libblade/test/bladec2.cfg new file mode 100644 index 0000000000..1167439083 --- /dev/null +++ b/libs/libblade/test/bladec2.cfg @@ -0,0 +1,29 @@ +blade: +{ + identity = "peer@domain"; + directory: + { + uris = ( "directory@domain?transport=wss&host=127.0.0.1&port=2100" ); + }; + datastore: + { + database: + { + path = ":mem:"; + }; + }; + wss: + { + endpoints: + { + ipv4 = ( { address = "0.0.0.0", port = 2101 } ); + ipv6 = ( { address = "::", port = 2101 } ); + backlog = 128; + }; + # SSL group is optional, disabled when absent + ssl: + { + # todo: server SSL stuffs here + }; + }; +}; From 418092e1eebd18c8c4071b82259ba40ea3fa7ccd Mon Sep 17 00:00:00 2001 From: Shane Bryldt Date: Tue, 14 Feb 2017 19:04:48 +0000 Subject: [PATCH 21/34] FS-9952: Some work towards client connectivity support, commit is to remove blade_message_t and get RPC stuff updated, code does not compile currently --- libs/libblade/Makefile.am | 4 +- libs/libblade/src/blade_identity.c | 37 +++- libs/libblade/src/blade_message.c | 129 ------------- libs/libblade/src/blade_module_wss.c | 8 +- libs/libblade/src/blade_stack.c | 208 ++++++++++++--------- libs/libblade/src/include/blade.h | 6 +- libs/libblade/src/include/blade_identity.h | 3 +- libs/libblade/src/include/blade_message.h | 57 ------ libs/libblade/src/include/blade_stack.h | 6 +- libs/libblade/src/include/blade_types.h | 3 - libs/libblade/test/bladec.c | 4 + 11 files changed, 171 insertions(+), 294 deletions(-) delete mode 100644 libs/libblade/src/blade_message.c delete mode 100644 libs/libblade/src/include/blade_message.h diff --git a/libs/libblade/Makefile.am b/libs/libblade/Makefile.am index c9343ff9fc..c4e1872375 100644 --- a/libs/libblade/Makefile.am +++ b/libs/libblade/Makefile.am @@ -12,14 +12,14 @@ libunqlite_la_LIBADD = -lpthread lib_LTLIBRARIES = libblade.la libblade_la_SOURCES = src/blade.c src/blade_stack.c src/bpcp.c src/blade_datastore.c -libblade_la_SOURCES += src/blade_message.c src/blade_rpcproto.c +libblade_la_SOURCES += src/blade_rpcproto.c libblade_la_SOURCES += src/blade_identity.c src/blade_module.c src/blade_connection.c src/blade_module_wss.c libblade_la_CFLAGS = $(AM_CFLAGS) $(AM_CPPFLAGS) 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 -library_include_HEADERS += src/include/bpcp.h src/include/blade_datastore.h src/include/blade_message.h src/include/blade_rpcproto.h +library_include_HEADERS += src/include/bpcp.h src/include/blade_datastore.h src/include/blade_rpcproto.h library_include_HEADERS += src/include/blade_identity.h src/include/blade_module.h src/include/blade_connection.h library_include_HEADERS += src/include/unqlite.h test/tap.h diff --git a/libs/libblade/src/blade_identity.c b/libs/libblade/src/blade_identity.c index fafbfb5ae8..030d544de6 100644 --- a/libs/libblade/src/blade_identity.c +++ b/libs/libblade/src/blade_identity.c @@ -87,6 +87,8 @@ KS_DECLARE(ks_status_t) blade_identity_parse(blade_identity_t *bi, const char *u 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); @@ -97,13 +99,13 @@ KS_DECLARE(ks_status_t) blade_identity_parse(blade_identity_t *bi, const char *u 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'; @@ -125,18 +127,41 @@ KS_DECLARE(ks_status_t) blade_identity_parse(blade_identity_t *bi, const char *u } } + // @todo remove this, temporary for testing + ks_log(KS_LOG_DEBUG, " name: %s\n", bi->name); + ks_log(KS_LOG_DEBUG, " domain: %s\n", bi->domain); + ks_log(KS_LOG_DEBUG, " resource: %s\n", bi->resource); + for (ks_hash_iterator_t *it = ks_hash_first(bi->parameters, KS_UNLOCKED); it; it = ks_hash_next(&it)) { + const char *key = NULL; + const char *val = NULL; + + ks_hash_this(it, (const void **)&key, NULL, (void **)&val); + + ks_log(KS_LOG_DEBUG, " key: %s = %s\n", key, val); + } + return KS_STATUS_SUCCESS; } -KS_DECLARE(ks_status_t) blade_identity_uri(blade_identity_t *bi, const char **uri) +KS_DECLARE(const char *) blade_identity_uri(blade_identity_t *bi) { ks_assert(bi); - ks_assert(uri); - *uri = bi->uri; + return bi->uri; +} + +KS_DECLARE(ks_status_t) blade_identity_parameter_get(blade_identity_t *bi, const char *key, const char **value) +{ + ks_assert(bi); + ks_assert(key); + ks_assert(value); + + *value = (const char *)ks_hash_search(bi->parameters, (void *)key, KS_UNLOCKED); + return KS_STATUS_SUCCESS; } - + + /* For Emacs: * Local Variables: * mode:c diff --git a/libs/libblade/src/blade_message.c b/libs/libblade/src/blade_message.c deleted file mode 100644 index 0bf401302f..0000000000 --- a/libs/libblade/src/blade_message.c +++ /dev/null @@ -1,129 +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" - -struct blade_message_s { - ks_pool_t *pool; - blade_handle_t *handle; - - void *data; - ks_size_t data_length; - ks_size_t data_size; -}; - - -KS_DECLARE(ks_status_t) blade_message_destroy(blade_message_t **bmP) -{ - blade_message_t *bm = NULL; - - ks_assert(bmP); - - bm = *bmP; - *bmP = NULL; - - ks_assert(bm); - - if (bm->data) ks_pool_free(bm->pool, &bm->data); - - ks_pool_free(bm->pool, &bm); - - return KS_STATUS_SUCCESS; -} - -KS_DECLARE(ks_status_t) blade_message_create(blade_message_t **bmP, ks_pool_t *pool, blade_handle_t *handle) -{ - blade_message_t *bm = NULL; - - ks_assert(bmP); - ks_assert(pool); - ks_assert(handle); - - bm = ks_pool_alloc(pool, sizeof(*bm)); - bm->pool = pool; - bm->handle = handle; - *bmP = bm; - - return KS_STATUS_SUCCESS; -} - -KS_DECLARE(ks_status_t) blade_message_discard(blade_message_t **bm) -{ - ks_assert(bm); - ks_assert(*bm); - - return blade_handle_message_discard((*bm)->handle, bm); -} - -KS_DECLARE(ks_status_t) blade_message_set(blade_message_t *bm, void *data, ks_size_t data_length) -{ - ks_assert(bm); - ks_assert(data); - ks_assert(data_length > 0); - - // @todo fail on a max message size? - - if (data_length > bm->data_size) { - // @todo talk to tony about adding flags to ks_pool_resize_ex to prevent the memcpy, don't need to copy old memory here - // otherwise switch to a new allocation instead of resizing - bm->data = ks_pool_resize(bm->pool, bm->data, data_length); - ks_assert(bm->data); - bm->data_size = data_length; - } - memcpy(bm->data, data, data_length); - bm->data_length = data_length; - - return KS_STATUS_SUCCESS; -} - -KS_DECLARE(ks_status_t) blade_message_get(blade_message_t *bm, void **data, ks_size_t *data_length) -{ - ks_assert(bm); - - *data = bm->data; - *data_length = bm->data_length; - - 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_module_wss.c b/libs/libblade/src/blade_module_wss.c index b98bbb0814..0ad2d9bfd2 100644 --- a/libs/libblade/src/blade_module_wss.c +++ b/libs/libblade/src/blade_module_wss.c @@ -33,6 +33,7 @@ #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; @@ -130,8 +131,6 @@ static blade_module_callbacks_t g_module_wss_callbacks = static blade_transport_callbacks_t g_transport_wss_callbacks = { - "wss", - blade_transport_wss_on_connect, blade_transport_wss_on_rank, blade_transport_wss_on_send, @@ -374,7 +373,7 @@ KS_DECLARE(ks_status_t) blade_module_wss_on_startup(blade_module_t *bm, config_s KS_PRI_NORMAL, bm_wss->pool) != KS_STATUS_SUCCESS) return KS_STATUS_FAIL; - blade_handle_transport_register(bm_wss->handle, bm_wss->transport_callbacks); + blade_handle_transport_register(bm_wss->handle, bm, BLADE_MODULE_WSS_TRANSPORT_NAME, bm_wss->transport_callbacks); return KS_STATUS_SUCCESS; } @@ -389,7 +388,7 @@ KS_DECLARE(ks_status_t) blade_module_wss_on_shutdown(blade_module_t *bm) bm_wss = (blade_module_wss_t *)blade_module_data_get(bm); - blade_handle_transport_unregister(bm_wss->handle, bm_wss->transport_callbacks); + blade_handle_transport_unregister(bm_wss->handle, BLADE_MODULE_WSS_TRANSPORT_NAME); if (bm_wss->listeners_thread) { bm_wss->shutdown = KS_TRUE; @@ -580,6 +579,7 @@ ks_status_t blade_transport_wss_on_connect(blade_connection_t **bcP, blade_modul *bcP = NULL; // @todo connect-out equivilent of accept + ks_log(KS_LOG_DEBUG, "Connect Callback: %s\n", blade_identity_uri(target)); return KS_STATUS_SUCCESS; } diff --git a/libs/libblade/src/blade_stack.c b/libs/libblade/src/blade_stack.c index 02f2dc6df3..3b012f8e38 100644 --- a/libs/libblade/src/blade_stack.c +++ b/libs/libblade/src/blade_stack.c @@ -48,12 +48,89 @@ struct blade_handle_s { config_setting_t *config_datastore; ks_hash_t *transports; - ks_q_t *messages_discarded; blade_datastore_t *datastore; }; +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; +} + + + +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); + + *bhP = bh; + + return KS_STATUS_SUCCESS; +} + KS_DECLARE(ks_status_t) blade_handle_destroy(blade_handle_t **bhP) { blade_handle_t *bh = NULL; @@ -72,11 +149,6 @@ KS_DECLARE(ks_status_t) blade_handle_destroy(blade_handle_t **bhP) blade_handle_shutdown(bh); - if (bh->messages_discarded) { - // @todo make sure messages are cleaned up - ks_q_destroy(&bh->messages_discarded); - } - ks_hash_destroy(&bh->transports); if (bh->tpool && (flags & BH_MYTPOOL)) ks_thread_pool_destroy(&bh->tpool); @@ -90,40 +162,6 @@ 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) -{ - 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(*bh)); - 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); - - // @todo check thresholds from config, for now just ensure it doesn't grow out of control, allow 100 discarded messages - ks_q_create(&bh->messages_discarded, bh->pool, 100); - ks_assert(bh->messages_discarded); - - *bhP = bh; - - return KS_STATUS_SUCCESS; -} - ks_status_t blade_handle_config(blade_handle_t *bh, config_setting_t *config) { config_setting_t *service = NULL; @@ -134,8 +172,6 @@ ks_status_t blade_handle_config(blade_handle_t *bh, config_setting_t *config) if (!config) return KS_STATUS_FAIL; if (!config_setting_is_group(config)) return KS_STATUS_FAIL; - // @todo config for messages_discarded threshold (ie, message count, message memory, etc) - service = config_setting_get_member(config, "service"); datastore = config_setting_get_member(config, "datastore"); @@ -173,6 +209,7 @@ KS_DECLARE(ks_status_t) blade_handle_shutdown(blade_handle_t *bh) { ks_assert(bh); + // @todo cleanup registered transports if (blade_handle_datastore_available(bh)) blade_datastore_destroy(&bh->datastore); return KS_STATUS_SUCCESS; @@ -190,79 +227,82 @@ KS_DECLARE(ks_thread_pool_t *) blade_handle_tpool_get(blade_handle_t *bh) return bh->tpool; } -KS_DECLARE(ks_status_t) blade_handle_transport_register(blade_handle_t *bh, blade_transport_callbacks_t *callbacks) +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); + blade_handle_transport_registration_create(&bhtr, bh->pool, bm, callbacks); + ks_assert(bhtr); + ks_hash_write_lock(bh->transports); - ks_hash_insert(bh->transports, (void *)callbacks->name, callbacks); + 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); - ks_log(KS_LOG_DEBUG, "Transport Registered: %s\n", callbacks->name); + 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, blade_transport_callbacks_t *callbacks) +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(callbacks); + ks_assert(name); ks_hash_write_lock(bh->transports); - ks_hash_remove(bh->transports, (void *)callbacks->name); + 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); + return KS_STATUS_SUCCESS; } KS_DECLARE(ks_status_t) blade_handle_connect(blade_handle_t *bh, blade_connection_t **bcP, blade_identity_t *target) { + ks_status_t ret = KS_STATUS_SUCCESS; + blade_handle_transport_registration_t *bhtr = NULL; + const char *tname = NULL; + ks_assert(bh); ks_assert(target); ks_hash_read_lock(bh->transports); - // @todo find transport for target, check if target specifies explicit transport parameter first, otherwise use onrank and keep highest ranked callbacks + + blade_identity_parameter_get(target, "transport", &tname); + 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 + } + } 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); - // transport_callbacks->onconnect(bcP, target); + // @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); + else ret = KS_STATUS_FAIL; - return KS_STATUS_SUCCESS; + return ret; } -KS_DECLARE(ks_status_t) blade_handle_message_claim(blade_handle_t *bh, blade_message_t **message, void *data, ks_size_t data_length) -{ - blade_message_t *msg = NULL; - - ks_assert(bh); - ks_assert(message); - ks_assert(data); - - *message = NULL; - - if (ks_q_trypop(bh->messages_discarded, (void **)&msg) != KS_STATUS_SUCCESS || !msg) blade_message_create(&msg, bh->pool, bh); - ks_assert(msg); - - if (blade_message_set(msg, data, data_length) != KS_STATUS_SUCCESS) return KS_STATUS_FAIL; - - *message = msg; - - return KS_STATUS_SUCCESS; -} - -KS_DECLARE(ks_status_t) blade_handle_message_discard(blade_handle_t *bh, blade_message_t **message) -{ - ks_assert(bh); - ks_assert(message); - ks_assert(*message); - - // @todo check thresholds for discarded messages, if the queue is full just destroy the message for now (currently 100 messages) - if (ks_q_push(bh->messages_discarded, *message) != KS_STATUS_SUCCESS) blade_message_destroy(message); - - *message = NULL; - - return KS_STATUS_SUCCESS; -} diff --git a/libs/libblade/src/include/blade.h b/libs/libblade/src/include/blade.h index d7a6a7cac1..d558fa5f4c 100644 --- a/libs/libblade/src/include/blade.h +++ b/libs/libblade/src/include/blade.h @@ -40,13 +40,11 @@ #include "unqlite.h" #include "blade_types.h" #include "blade_stack.h" -#include "blade_message.h" -#include "blade_datastore.h" -#include "bpcp.h" - #include "blade_identity.h" #include "blade_module.h" #include "blade_connection.h" +#include "blade_datastore.h" +#include "bpcp.h" KS_BEGIN_EXTERN_C diff --git a/libs/libblade/src/include/blade_identity.h b/libs/libblade/src/include/blade_identity.h index 41d0341b06..4b9a29c6f7 100644 --- a/libs/libblade/src/include/blade_identity.h +++ b/libs/libblade/src/include/blade_identity.h @@ -39,7 +39,8 @@ KS_BEGIN_EXTERN_C 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(ks_status_t) blade_identity_uri(blade_identity_t *bi, const char **uri); +KS_DECLARE(const char *) blade_identity_uri(blade_identity_t *bi); +KS_DECLARE(ks_status_t) blade_identity_parameter_get(blade_identity_t *bi, const char *key, const char **value); KS_END_EXTERN_C #endif diff --git a/libs/libblade/src/include/blade_message.h b/libs/libblade/src/include/blade_message.h deleted file mode 100644 index 5db9c60c22..0000000000 --- a/libs/libblade/src/include/blade_message.h +++ /dev/null @@ -1,57 +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. - */ - -#ifndef _BLADE_MESSAGE_H_ -#define _BLADE_MESSAGE_H_ -#include - -KS_BEGIN_EXTERN_C -KS_DECLARE(ks_status_t) blade_message_create(blade_message_t **bmP, ks_pool_t *pool, blade_handle_t *handle); -KS_DECLARE(ks_status_t) blade_message_destroy(blade_message_t **bmP); -KS_DECLARE(ks_status_t) blade_message_set(blade_message_t *bm, void *data, ks_size_t data_length); -KS_DECLARE(ks_status_t) blade_message_get(blade_message_t *bm, void **data, ks_size_t *data_length); -KS_DECLARE(ks_status_t) blade_message_discard(blade_message_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_stack.h b/libs/libblade/src/include/blade_stack.h index 78b4429376..9a3551c565 100644 --- a/libs/libblade/src/include/blade_stack.h +++ b/libs/libblade/src/include/blade_stack.h @@ -48,11 +48,9 @@ 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_transport_callbacks_t *callbacks); -KS_DECLARE(ks_status_t) blade_handle_transport_unregister(blade_handle_t *bh, blade_transport_callbacks_t *callbacks); +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_message_claim(blade_handle_t *bh, blade_message_t **message, void *data, ks_size_t data_length); -KS_DECLARE(ks_status_t) blade_handle_message_discard(blade_handle_t *bh, blade_message_t **message); 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); diff --git a/libs/libblade/src/include/blade_types.h b/libs/libblade/src/include/blade_types.h index aad7e4aaa8..249dd9b31f 100644 --- a/libs/libblade/src/include/blade_types.h +++ b/libs/libblade/src/include/blade_types.h @@ -45,7 +45,6 @@ typedef struct blade_module_callbacks_s blade_module_callbacks_t; typedef struct blade_transport_callbacks_s blade_transport_callbacks_t; typedef struct blade_connection_s blade_connection_t; -typedef struct blade_message_s blade_message_t; typedef struct blade_datastore_s blade_datastore_t; typedef ks_bool_t (*blade_datastore_fetch_callback_t)(blade_datastore_t *bds, const void *data, uint32_t data_length, void *userdata); @@ -105,8 +104,6 @@ typedef ks_status_t (*blade_transport_receive_callback_t)(blade_connection_t *bc 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 { - const char *name; - blade_transport_connect_callback_t onconnect; blade_transport_rank_callback_t onrank; blade_transport_send_callback_t onsend; diff --git a/libs/libblade/test/bladec.c b/libs/libblade/test/bladec.c index b2abc8b11b..796f439253 100644 --- a/libs/libblade/test/bladec.c +++ b/libs/libblade/test/bladec.c @@ -46,6 +46,7 @@ int main(int argc, char **argv) config_t config; config_setting_t *config_blade = NULL; blade_module_t *mod_wss = NULL; + //blade_identity_t *id = NULL; ks_global_set_default_logger(KS_LOG_LEVEL_DEBUG); @@ -53,6 +54,9 @@ int main(int argc, char **argv) blade_handle_create(&bh, NULL, NULL); + //blade_identity_create(&id, blade_handle_pool_get(bh)); + //blade_identity_parse(id, "test@domain.com/laptop?transport=wss&host=127.0.0.1&port=1234"); + // @todo load config file, and lookup "blade" setting to put into config_blade config_init(&config); if (!config_read_file(&config, "bladec.cfg")) { From 06e38ba1ffbcedd140345f93aa419ce37fb7b64e Mon Sep 17 00:00:00 2001 From: colm Date: Tue, 14 Feb 2017 15:40:00 -0500 Subject: [PATCH 22/34] FS-9952: Add blade extention to rpc messages --- libs/libblade/src/blade_rpcproto.c | 203 ++++++++++-- libs/libblade/src/include/blade_rpcproto.h | 34 +- libs/libblade/test/testrpcproto.c | 365 ++++++++++++++++++++- libs/libks/src/include/ks_rpcmessage.h | 16 +- libs/libks/src/ks_rpcmessage.c | 179 ++++------ libs/libks/test/testmessages.c | 8 +- 6 files changed, 630 insertions(+), 175 deletions(-) diff --git a/libs/libblade/src/blade_rpcproto.c b/libs/libblade/src/blade_rpcproto.c index 53b546663c..e955fffb56 100644 --- a/libs/libblade/src/blade_rpcproto.c +++ b/libs/libblade/src/blade_rpcproto.c @@ -34,7 +34,6 @@ #pragma GCC optimize ("O0") #include -#include /* * internal shared structure grounded in global @@ -700,6 +699,172 @@ KS_DECLARE(ks_status_t)blade_rpc_inherit_template(char *namespace, char* templat } +/* + * create a request message + */ +KS_DECLARE(ks_rpcmessageid_t) blade_rpc_create_request(char *namespace, + char *method, + blade_rpc_fields_t* fields, + cJSON **paramsP, + cJSON **requestP) +{ + cJSON *jversion = NULL; + blade_rpc_callbackpair_t* callbacks = NULL; + + *requestP = NULL; + + ks_hash_read_lock(g_handle->namespace_hash); + blade_rpc_namespace_t *n = ks_hash_search(g_handle->namespace_hash, namespace, KS_UNLOCKED); + + if (n) { + ks_hash_read_lock(n->method_hash); + callbacks = ks_hash_search(n->method_hash, method, KS_UNLOCKED); + if (callbacks) { + jversion = cJSON_CreateString(n->version); + } + ks_hash_read_unlock(n->method_hash); + } + + ks_hash_read_unlock(g_handle->namespace_hash); + + if (!n) { + ks_log(KS_LOG_ERROR, "No namespace %s found\n", namespace); + return 0; + } + + if (!callbacks) { + ks_log(KS_LOG_ERROR, "No method %s.%s found\n", namespace, method); + return 0; + } + + ks_rpcmessageid_t msgid = ks_rpcmessage_create_request(namespace, method, paramsP, requestP); + + if (!msgid || *requestP == NULL) { + ks_log(KS_LOG_ERROR, "Unable to create rpc message for method %s.%s\n", namespace, method); + return 0; + } + + cJSON *jfields = cJSON_CreateObject(); + + cJSON_AddItemToObject(jfields, "version", jversion); + + if (fields->to) { + cJSON_AddStringToObject(jfields, "to", fields->to); + } + + if (fields->from) { + cJSON_AddStringToObject(jfields, "from", fields->from); + } + + if (fields->token) { + cJSON_AddStringToObject(jfields, "token", fields->token); + } + + cJSON_AddItemToObject(*requestP, "blade", jfields); + + return msgid; +} + +KS_DECLARE(ks_rpcmessageid_t) blade_rpc_create_response(cJSON *request, + cJSON **replyP, + cJSON **responseP) +{ + cJSON *jfields = cJSON_GetObjectItem(request, "blade"); + + if (!jfields) { + ks_log(KS_LOG_ERROR, "No blade routing info found. Unable to create response\n"); + return 0; + } + + ks_rpcmessageid_t msgid = ks_rpcmessage_create_response(request, replyP, responseP); + + if (!msgid || *responseP == NULL) { + ks_log(KS_LOG_ERROR, "Unable to create rpc response message\n"); //TODO : Add namespace, method from request + return 0; + } + + const char *to = cJSON_GetObjectCstr(jfields, "to"); + const char *from = cJSON_GetObjectCstr(jfields, "from"); + const char *token = cJSON_GetObjectCstr(jfields, "token"); + const char *version = cJSON_GetObjectCstr(jfields, "version"); + + cJSON *blade = cJSON_CreateObject(); + + if (to) { + cJSON_AddStringToObject(blade, "to", from); + } + + if (from) { + cJSON_AddStringToObject(blade, "from", to); + } + + if (token) { + cJSON_AddStringToObject(blade, "token", token); + } + + if (version) { + cJSON_AddStringToObject(blade, "version", version); + } + + cJSON_AddItemToObject(*responseP, "blade", blade); + + return msgid; +} + +const char BLADE_JRPC_METHOD[] = "method"; +const char BLADE_JRPC_FIELDS[] = "blade"; +const char BLADE_JRPC_TO[] = "to"; +const char BLADE_JRPC_FROM[] = "from"; +const char BLADE_JRPC_TOKEN[] = "token"; +const char BLADE_JRPC_VERSION[] = "version"; + +KS_DECLARE(ks_status_t) blade_rpc_parse_message(cJSON *message, + char **namespaceP, + char **methodP, + char **versionP, + blade_rpc_fields_t **fieldsP) +{ + const char *m = cJSON_GetObjectCstr(message, BLADE_JRPC_METHOD); + cJSON *blade = cJSON_GetObjectItem(message, BLADE_JRPC_FIELDS); + + *fieldsP = NULL; + *namespaceP = NULL; + *versionP = NULL; + *methodP = NULL; + + if (!m || !blade) { + const char *buffer = cJSON_PrintUnformatted(message); + ks_log(KS_LOG_ERROR, "Unable to locate necessary fields in message:\n%s\n", buffer); + ks_pool_free(g_handle->pool, buffer); + return KS_STATUS_FAIL; + } + + ks_size_t len = KS_RPCMESSAGE_COMMAND_LENGTH + 1 + + KS_RPCMESSAGE_NAMESPACE_LENGTH + 1 + + KS_RPCMESSAGE_VERSION_LENGTH + 1 + + sizeof(blade_rpc_fields_t) + 1; + + blade_rpc_fields_t *fields = (blade_rpc_fields_t *)ks_pool_alloc(g_handle->pool, len); + + fields->to = cJSON_GetObjectCstr(blade, BLADE_JRPC_TO); + fields->from = cJSON_GetObjectCstr(blade, BLADE_JRPC_FROM); + fields->from = cJSON_GetObjectCstr(blade, BLADE_JRPC_TOKEN); + + char *namespace = (char*)fields + sizeof(blade_rpc_fields_t); + char *command = namespace + KS_RPCMESSAGE_NAMESPACE_LENGTH + 1; + char *version = command + KS_RPCMESSAGE_COMMAND_LENGTH + 1; + + blade_rpc_parse_fqcommand(m, namespace, command); + + strcpy(version, cJSON_GetObjectCstr(blade, BLADE_JRPC_VERSION)); + + *fieldsP = fields; + *namespaceP = namespace; + *methodP = command; + + return KS_STATUS_SUCCESS; +} + /* * send message @@ -769,7 +934,7 @@ static ks_status_t blade_rpc_process_jsonmessage_all(cJSON *request) if (!fqcommand) { error = cJSON_CreateObject(); cJSON_AddStringToObject(error, "errormessage", "Command not specified"); - ks_rpcmessage_create_request("rpcprotocol", "unknowncommand", NULL, NULL, &error, &responseP); + ks_rpcmessage_create_request("rpcprotocol", "unknowncommand", &error, &responseP); blade_rpc_write_json(responseP); return KS_STATUS_FAIL; } @@ -907,23 +1072,23 @@ KS_DECLARE(ks_status_t) blade_rpc_process_data(const uint8_t *data, return KS_STATUS_FAIL; } -KS_DECLARE(ks_status_t) blade_rpc_process_blademessage(blade_message_t *message) -{ - uint8_t* data = NULL; - ks_size_t size = 0; - - blade_message_get(message, (void **)&data, &size); - - if (data && size>0) { - ks_status_t s = blade_rpc_process_data(data, size); - blade_message_discard(&message); - return s; - } - - ks_log(KS_LOG_ERROR, "Message read failed\n"); - return KS_STATUS_FAIL; - -} +//KS_DECLARE(ks_status_t) blade_rpc_process_blademessage(blade_message_t *message) +//{ +// uint8_t* data = NULL; +// ks_size_t size = 0; +// +// blade_message_get(message, (void **)&data, &size); +// +// if (data && size>0) { +// ks_status_t s = blade_rpc_process_data(data, size); +// blade_message_discard(&message); +// return s; +// } +// +// ks_log(KS_LOG_ERROR, "Message read failed\n"); +// return KS_STATUS_FAIL; +// +//} /* For Emacs: diff --git a/libs/libblade/src/include/blade_rpcproto.h b/libs/libblade/src/include/blade_rpcproto.h index 862ccd9f00..193c8604ab 100644 --- a/libs/libblade/src/include/blade_rpcproto.h +++ b/libs/libblade/src/include/blade_rpcproto.h @@ -39,7 +39,6 @@ // temp typedefs to get compile going //typedef struct blade_peer_s blade_peer_t; -//typedef struct blade_message_s blade_message_t; //typedef struct blade_event_s blade_event_t; #define KS_RPCMESSAGE_NAMESPACE_LENGTH 16 @@ -48,6 +47,20 @@ #define KS_RPCMESSAGE_VERSION_LENGTH 9 +/* + * contents to add to the "blade" field in rpc + */ + +typedef struct blade_rpc_fields_s { + + const char *to; + const char *from; + const char *token; + +} blade_rpc_fields_t; + + + enum jrpc_status_t { JRPC_PASS = (1 << 0), JRPC_SEND = (1 << 1), @@ -101,6 +114,24 @@ KS_DECLARE(ks_status_t)blade_rpc_register_template_function(char *name, KS_DECLARE(ks_status_t)blade_rpc_inherit_template(char *namespace, char* template); +/* + * create a request message + */ +KS_DECLARE(ks_rpcmessageid_t) blade_rpc_create_request(char *namespace, + char *method, + blade_rpc_fields_t* fields, + cJSON **paramsP, + cJSON **requestP); + +KS_DECLARE(ks_rpcmessageid_t) blade_rpc_create_response(cJSON *request, + cJSON **reply, + cJSON **response); + +KS_DECLARE(ks_status_t) blade_rpc_parse_message(cJSON *message, + char **namespace, + char **method, + char **version, + blade_rpc_fields_t **fieldsP); /* * peer create/destroy @@ -121,7 +152,6 @@ KS_DECLARE(ks_status_t) blade_rpc_write_json(cJSON* json); * process inbound message * ----------------------- */ -KS_DECLARE(ks_status_t) blade_rpc_process_blademessage(blade_message_t *message); KS_DECLARE(ks_status_t) blade_rpc_process_data(const uint8_t *data, ks_size_t size); KS_DECLARE(ks_status_t) blade_rpc_process_jsonmessage(cJSON *request); diff --git a/libs/libblade/test/testrpcproto.c b/libs/libblade/test/testrpcproto.c index b479d07033..4679a7f277 100644 --- a/libs/libblade/test/testrpcproto.c +++ b/libs/libblade/test/testrpcproto.c @@ -29,7 +29,7 @@ static enum jrpc_status_t process_widget(cJSON *msg, cJSON **response) cJSON *resp = cJSON_CreateObject(); cJSON_AddNumberToObject(resp, "code", 199); - ks_rpcmessage_id msgid = ks_rpcmessage_create_response(msg, &resp, response); + ks_rpcmessageid_t msgid = ks_rpcmessage_create_response(msg, &resp, response); char *b1 = cJSON_PrintUnformatted(*response); //(*response); printf("Response: msgid %d\n%s\n", msgid, b1); @@ -62,7 +62,8 @@ static enum jrpc_status_t process_wombat(cJSON *msg, cJSON **replyP) cJSON_AddNumberToObject(result, "code", 99); cJSON *response; - ks_rpcmessage_id msgid = ks_rpcmessage_create_response(msg, &result, &response); +// ks_rpcmessageid_t msgid = ks_rpcmessage_create_response(msg, &result, &response); + ks_rpcmessageid_t msgid = blade_rpc_create_response(msg, &result, &response); cJSON *response_copy = cJSON_Duplicate(response, 1); blade_rpc_process_jsonmessage(response_copy); @@ -80,7 +81,18 @@ static enum jrpc_status_t process_wombat(cJSON *msg, cJSON **replyP) cJSON *parms2 = NULL; - msgid = ks_rpcmessage_create_request("app1", "widget", "99", "1.0", &parms2, replyP); + + char to[] = "tony@freeswitch.com/laptop?transport=wss&host=server1.freeswitch.com&port=1234"; + char from[] = "colm@freeswitch.com/laptop?transport=wss&host=server2.freeswitch.com&port=4321"; + char token[] = "abcdefhgjojklmnopqrst"; + + blade_rpc_fields_t fields; + fields.to = to; + fields.from = from; + fields.token = token; + +// msgid = ks_rpcmessage_create_request("app1", "widget", &parms2, replyP); + msgid = blade_rpc_create_request("app1", "widget", &fields, &parms2, replyP); printf("\n\nexiting process_wombat with a reply to send\n"); @@ -120,7 +132,7 @@ static enum jrpc_status_t process_wombat_preresponse(cJSON *request, cJSON **ms cJSON *parms2 = NULL; - //ks_rpcmessage_id msgid = ks_rpcmessage_create_request("app1", "widget", "99", "1.0", &parms2, msg); + //ks_rpcmessageid_t msgid = ks_rpcmessage_create_request("app1", "widget", &parms2, msg); printf("exiting process_wombat_preresponse\n"); return JRPC_SEND; @@ -146,7 +158,7 @@ static enum jrpc_status_t process_badbunny( cJSON *msg, cJSON **response) cJSON *respvalue; - ks_rpcmessage_id msgid = ks_rpcmessage_create_errorresponse(msg, &respvalue, response); + ks_rpcmessageid_t msgid = ks_rpcmessage_create_errorresponse(msg, &respvalue, response); char *b2 = cJSON_PrintUnformatted(*response); printf("\nRequest: msgid %d\n%s\n\n", msgid, b2); @@ -187,9 +199,9 @@ void test01() cJSON* request1 = NULL; cJSON* parms1 = NULL; - printf("\n\n\n - message1 - basic message\n\n\n"); + printf("\n\n\n - test01 message1 - basic message\n\n\n"); - ks_rpcmessage_id msgid = ks_rpcmessage_create_request("app1", "wombat", "99", "1.0", &parms1, &request1); + ks_rpcmessageid_t msgid = ks_rpcmessage_create_request("app1", "wombat", &parms1, &request1); if (msgid == 0) { printf("test01.1: unable to create message 1\n"); return; @@ -200,6 +212,8 @@ void test01() return; } + cJSON_AddStringToObject(parms1, "hello", "cruel world"); + char *pdata = cJSON_PrintUnformatted(request1); if (!pdata) { @@ -207,9 +221,8 @@ void test01() return; } - printf("request:\n%s\n", pdata); + printf("test01 request:\n%s\n", pdata); - cJSON_AddStringToObject(parms1, "hello", "cruel world"); blade_rpc_process_jsonmessage(request1); @@ -223,14 +236,16 @@ void test01() /* message 2 */ /* --------- */ - printf("\n\n\n - message2 - test inherit\n\n\n"); + printf("\n\n\n test01 - message2 - test inherit\n\n\n"); blade_rpc_inherit_template("app1", "temp1"); cJSON* request2 = NULL; - cJSON* parms2 = NULL; + cJSON* parms2 = cJSON_CreateObject(); - msgid = ks_rpcmessage_create_request("app1", "temp1.widget", "99", "1.0", &parms2, &request2); + cJSON_AddStringToObject(parms2, "hello2", "cruel world once again"); + + msgid = ks_rpcmessage_create_request("app1", "temp1.widget", &parms2, &request2); if (msgid == 0) { printf("test01.2: failed to create a wombat\n"); return; @@ -248,9 +263,7 @@ void test01() return; } - printf("request:\n%s\n", pdata); - - cJSON_AddStringToObject(parms2, "hello2", "cruel world2"); + printf("\ntest01 request:\n%s\n\n\n", pdata); blade_rpc_process_jsonmessage(request2); @@ -265,14 +278,318 @@ void test01() void test02() { - printf("**** testmessages - test02 start\n"); fflush(stdout); + printf("**** testrpcmessages - test02 start\n"); fflush(stdout); - printf("**** testmessages - test02 finished\n"); fflush(stdout); + blade_rpc_declare_namespace("app2", "1.0"); + + blade_rpc_register_function("app2", "wombat", process_wombat, process_wombat_response); + + blade_rpc_inherit_template("app2", "temp1"); + + blade_rpc_register_custom_request_function("app2", "wombat", process_wombat_prerequest, process_wombat_postresponse); + blade_rpc_register_custom_response_function("app2", "wombat", process_wombat_preresponse, process_wombat_postresponse); + + blade_rpc_register_function("app2", "bunny", process_badbunny, NULL); + + char to[] = "tony@freeswitch.com/laptop?transport=wss&host=server1.freeswitch.com&port=1234"; + char from[] = "colm@freeswitch.com/laptop?transport=wss&host=server2.freeswitch.com&port=4321"; + char token[] = "abcdefhgjojklmnopqrst"; + + blade_rpc_fields_t fields; + fields.to = to; + fields.from = from; + fields.token = token; + + + /* test the 4 different ways to handle param messages */ + + cJSON *params1 = NULL; + cJSON *request1; + + ks_rpcmessageid_t msgid = blade_rpc_create_request("app2", "temp1.widget2", &fields, ¶ms1, &request1); + + if (!msgid) { + printf("test02.1: create_request failed\n"); + return; + } + + cJSON_AddStringToObject(params1, "hello", "cruel world"); + + char *pdata = cJSON_PrintUnformatted(request1); + + if (!pdata) { + printf("test02.1: unable to parse cJSON object\n"); + return; + } + + printf("\ntest02.1 request:\n\n%s\n\n\n", pdata); + + printf("\n\n -----------------------------------------\n\n"); + + ks_status_t s1 = blade_rpc_process_jsonmessage(request1); + if (s1 == KS_STATUS_FAIL) { + printf("test02.1: process request1 failed\n"); + return; + } + + printf(" -----------------------------------------\n\n\n\n"); + + ks_pool_free(pool, &pdata); + + cJSON *reply1 = NULL; + cJSON *response1 = NULL; + + ks_rpcmessageid_t msgid2 = blade_rpc_create_response(request1, &reply1, &response1); + + if (!msgid2) { + printf("test02.1: create_response failed\n"); + return; + } + + cJSON_AddNumberToObject(reply1, "code", 10); + cJSON_AddStringToObject(reply1, "farewell", "cruel server"); + + pdata = cJSON_PrintUnformatted(response1); + + if (!pdata) { + printf("test02.1: unable to parse cJSON response object\n"); + return; + } + + printf("\ntest02.1 response:\n\n%s\n\n\n", pdata); + + printf("\n\n -----------------------------------------\n\n"); + + s1 = blade_rpc_process_jsonmessage(response1); + if (s1 == KS_STATUS_FAIL) { + printf("test02.1: process request1 failed\n"); + return; + } + + printf(" -----------------------------------------\n\n\n\n"); + + + ks_pool_free(pool, &pdata); + + printf("**** testrpcmessages - test02 finished\n"); fflush(stdout); return; } +void test02a() +{ + printf("**** testrpcmessages - test02a start\n"); fflush(stdout); + + char to[] = "tony@freeswitch.com/laptop?transport=wss&host=server1.freeswitch.com&port=1234"; + char from[] = "colm@freeswitch.com/laptop?transport=wss&host=server2.freeswitch.com&port=4321"; + char token[] = "abcdefhgjojklmnopqrst"; + + blade_rpc_fields_t fields; + fields.to = to; + fields.from = from; + fields.token = token; + + + /* test the 4 different ways to handle param messages */ + + cJSON *request1; + + ks_rpcmessageid_t msgid = blade_rpc_create_request("app2", "wombat", &fields, NULL, &request1); + + if (!msgid) { + printf("test02.1: create_request failed\n"); + return; + } + + char *pdata = cJSON_PrintUnformatted(request1); + + if (!pdata) { + printf("test02.1: unable to parse cJSON object\n"); + return; + } + + printf("\ntest02.1 request:\n\n%s\n\n\n", pdata); + + printf("\n\n -----------------------------------------\n\n"); + + ks_status_t s1 = blade_rpc_process_jsonmessage(request1); + if (s1 == KS_STATUS_FAIL) { + printf("test02.1: process request1 failed\n"); + return; + } + + printf(" -----------------------------------------\n\n\n\n"); + + + + + ks_pool_free(pool, &pdata); + + cJSON *response1 = NULL; + + ks_rpcmessageid_t msgid2 = blade_rpc_create_response(request1, NULL, &response1); + + if (!msgid2) { + printf("test02.1: create_response failed\n"); + return; + } + + pdata = cJSON_PrintUnformatted(response1); + + printf("\ntest02.1 response:\n\n%s\n\n\n", pdata); + + ks_pool_free(pool, &pdata); + + printf("**** testrpcmessages - test02a finished\n\n\n"); fflush(stdout); + + return; +} + + +void test02b() +{ + printf("**** testrpcmessages - test02b start\n"); fflush(stdout); + + char to[] = "tony@freeswitch.com/laptop?transport=wss&host=server1.freeswitch.com&port=1234"; + char from[] = "colm@freeswitch.com/laptop?transport=wss&host=server2.freeswitch.com&port=4321"; + char token[] = "abcdefhgjojklmnopqrst"; + + blade_rpc_fields_t fields; + fields.to = to; + fields.from = from; + fields.token = token; + + + /* test the 4 different ways to handle param messages */ + + cJSON *params1 = cJSON_CreateNumber(4321); + cJSON *request1; + + ks_rpcmessageid_t msgid = blade_rpc_create_request("app2", "temp1.widget", &fields, ¶ms1, &request1); + + if (!msgid) { + printf("test02.1: create_request failed\n"); + return; + } + + char *pdata = cJSON_PrintUnformatted(request1); + + if (!pdata) { + printf("test02.1: unable to parse cJSON object\n"); + return; + } + + printf("\ntest02.1 request:\n\n%s\n\n\n", pdata); + + ks_pool_free(pool, &pdata); + + cJSON *reply1 = cJSON_CreateString("successful"); + cJSON *response1 = NULL; + + ks_rpcmessageid_t msgid2 = blade_rpc_create_response(request1, &reply1, &response1); + + if (!msgid2) { + printf("test02.1: create_response failed\n"); + return; + } + + pdata = cJSON_PrintUnformatted(response1); + + printf("\ntest02.1 response:\n\n%s\n\n\n", pdata); + + ks_pool_free(pool, &pdata); + + printf("**** testrpcmessages - test02b finished\n"); fflush(stdout); + + return; +} + + +void test02c() +{ + + printf("**** testrpcmessages - test02c start\n"); fflush(stdout); + + char to[] = "tony@freeswitch.com/laptop?transport=wss&host=server1.freeswitch.com&port=1234"; + char from[] = "colm@freeswitch.com/laptop?transport=wss&host=server2.freeswitch.com&port=4321"; + char token[] = "abcdefhgjojklmnopqrst"; + + blade_rpc_fields_t fields; + fields.to = to; + fields.from = from; + fields.token = token; + + + /* test the 4 different ways to handle param messages */ + + cJSON *params1 = cJSON_CreateObject(); + + cJSON_AddStringToObject(params1, "string1", "here is a string"); + cJSON_AddNumberToObject(params1, "number1", 4242); + + cJSON *request1; + + ks_rpcmessageid_t msgid = blade_rpc_create_request("app2", "bunny", &fields, ¶ms1, &request1); + + if (!msgid) { + printf("test02.1: create_request failed\n"); + return; + } + + cJSON_AddStringToObject(params1, "hello", "cruel world"); + + char *pdata = cJSON_PrintUnformatted(request1); + + if (!pdata) { + printf("test02.1: unable to parse cJSON object\n"); + return; + } + + printf("\ntest02.1 request:\n\n%s\n\n\n", pdata); + + ks_pool_free(pool, &pdata); + + + cJSON *reply1 = cJSON_CreateObject(); + cJSON_AddNumberToObject(reply1, "code", 10); + cJSON_AddStringToObject(reply1, "farewell", "cruel server"); + + + cJSON *response1 = NULL; + + ks_rpcmessageid_t msgid2 = blade_rpc_create_response(request1, &reply1, &response1); + + if (!msgid2) { + printf("test02.1: create_response failed\n"); + return; + } + + + pdata = cJSON_PrintUnformatted(response1); + + printf("\ntest02.1 response:\n\n%s\n\n\n", pdata); + + ks_pool_free(pool, &pdata); + + printf("**** testrpcmessages - test02c finished\n"); fflush(stdout); + + return; +} + + + + + + + + + + + + + + /* test06 */ @@ -340,6 +657,13 @@ int main(int argc, char *argv[]) { blade_rpc_init(pool); + blade_rpc_declare_template("temp1", "1.0"); + + blade_rpc_register_template_function("temp1", "widget", process_widget, process_widget_response); + blade_rpc_register_template_function("temp1", "widget2", process_widget, process_widget_response); + blade_rpc_register_template_function("temp1", "widget3", process_widget, process_widget_response); + + for (int tix=0; tixtype != cJSON_Object) { /* need to wrap this in a param field */ - params = cJSON_CreateObject(); - cJSON_AddItemToObject(params, "param", p); - } - else { + if (*paramsP) { /* parameters have been passed */ params = *paramsP; } - - cJSON *v = cJSON_GetObjectItem(params, "version"); - - if (!v) { /* add version if needed */ - cJSON_AddStringToObject(params, "version", version); - } else { - cJSON_AddStringToObject(params, "version", "0"); - } - } - - if (!params) { - params = cJSON_CreateObject(); - - if (version && version[0] != 0) { - cJSON_AddStringToObject(params, "version", version); - } - else { - cJSON_AddStringToObject(params, "version", "0"); + params = cJSON_CreateObject(); + *paramsP = params; } + cJSON_AddItemToObject(msg, PARAMS, params); } char fqcommand[KS_RPCMESSAGE_FQCOMMAND_LENGTH]; @@ -195,17 +182,7 @@ KS_DECLARE(ks_rpcmessage_id) ks_rpcmessage_create_request(char *namespace, sprintf(fqcommand, "%s.%s", namespace, command); - cJSON_AddItemToObject(msg, "method", cJSON_CreateString(fqcommand)); - - if (sessionid && sessionid[0] != 0) { - cJSON_AddStringToObject(params, "sessionid", sessionid); - } - - cJSON_AddItemToObject(msg, "params", params); - - if (paramsP) { - *paramsP = params; - } + cJSON_AddItemToObject(msg, METHOD, cJSON_CreateString(fqcommand)); *requestP = msg; return msgid; @@ -213,19 +190,22 @@ KS_DECLARE(ks_rpcmessage_id) ks_rpcmessage_create_request(char *namespace, KS_DECLARE(ks_size_t) ks_rpc_create_buffer(char *namespace, char *method, - char *sessionid, - char *version, - cJSON **parms, + cJSON **params, ks_buffer_t *buffer) { + cJSON *message; - ks_rpcmessage_id msgid = ks_rpcmessage_create_request(namespace, method, sessionid, version, parms, &message); + ks_rpcmessageid_t msgid = ks_rpcmessage_create_request(namespace, method, params, &message); if (!msgid) { return 0; } + if ( (*params)->child == NULL) { + cJSON_AddNullToObject(*params, "bladenull"); + } + const char* b = cJSON_PrintUnformatted(message); ks_size_t size = strlen(b); @@ -236,14 +216,14 @@ KS_DECLARE(ks_size_t) ks_rpc_create_buffer(char *namespace, } -static ks_rpcmessage_id ks_rpcmessage_get_messageid(const cJSON *msg, cJSON **cmsgidP) +static ks_rpcmessageid_t ks_rpcmessage_get_messageid(const cJSON *msg, cJSON **cmsgidP) { - uint32_t msgid = 0; + ks_rpcmessageid_t msgid = 0; - cJSON *cmsgid = cJSON_GetObjectItem(msg, "id"); + cJSON *cmsgid = cJSON_GetObjectItem(msg, ID); if (cmsgid->type == cJSON_Number) { - msgid = (uint32_t) cmsgid->valueint; + msgid = (ks_rpcmessageid_t) cmsgid->valueint; } *cmsgidP = cmsgid; @@ -252,24 +232,17 @@ static ks_rpcmessage_id ks_rpcmessage_get_messageid(const cJSON *msg, cJSON **cm } -static ks_rpcmessage_id ks_rpcmessage_new_response( +static ks_rpcmessageid_t ks_rpcmessage_new_response( const cJSON *request, - cJSON *result, + cJSON **result, cJSON **pmsg) { cJSON *respmsg = NULL; cJSON *cmsgid = NULL; - cJSON *version = NULL; - cJSON *sessionid = NULL; - cJSON *command = cJSON_GetObjectItem(request, "method"); - cJSON *params = cJSON_GetObjectItem(request, "params"); + cJSON *command = cJSON_GetObjectItem(request, METHOD); - if (params) { - version = cJSON_GetObjectItem(request, "version"); - } - - ks_rpcmessage_id msgid = ks_rpcmessage_get_messageid(request, &cmsgid ); + ks_rpcmessageid_t msgid = ks_rpcmessage_get_messageid(request, &cmsgid ); if (!msgid || !command) { return 0; @@ -277,91 +250,51 @@ static ks_rpcmessage_id ks_rpcmessage_new_response( *pmsg = respmsg = ks_rpcmessage_dup(cmsgid); - cJSON_AddItemToObject(respmsg, "method", cJSON_Duplicate(command, 0)); + cJSON_AddItemToObject(respmsg, METHOD, cJSON_Duplicate(command, 0)); - if (result) { - - cJSON *params = cJSON_GetObjectItem(request, "params"); - - if (params) { - version = cJSON_GetObjectItem(params, "version"); - - if (version) { - cJSON_AddItemToObject(result, "version", cJSON_Duplicate(version, 0)); - } - - sessionid = cJSON_GetObjectItem(params, "sessionid"); - - if (sessionid) { - cJSON_AddItemToObject(result, "sessionid", cJSON_Duplicate(sessionid, 0)); - } - - } - - cJSON_AddItemToObject(respmsg, "result", result); + if (result && *result) { + cJSON_AddItemToObject(respmsg, RESULT, *result); } return msgid; } -KS_DECLARE(ks_rpcmessage_id) ks_rpcmessage_create_response( +KS_DECLARE(ks_rpcmessageid_t) ks_rpcmessage_create_response( const cJSON *request, cJSON **resultP, cJSON **responseP) { - ks_rpcmessage_id msgid = ks_rpcmessage_new_response(request, *resultP, responseP); + ks_rpcmessageid_t msgid = ks_rpcmessage_new_response(request, resultP, responseP); cJSON *respmsg = *responseP; if (msgid) { - if (*resultP == NULL) { - *resultP = cJSON_CreateObject(); - cJSON *result = *resultP; - - cJSON *params = cJSON_GetObjectItem(request, "params"); - - if (params) { - cJSON *version = cJSON_GetObjectItem(request, "version"); - cJSON *sessionid = cJSON_GetObjectItem(request, "sessionid"); - - if (version) { - cJSON_AddItemToObject(result, "version", cJSON_Duplicate(version, 0)); - } - else { - cJSON_AddStringToObject(result, "version", "0"); - } - - if (sessionid) { - cJSON_AddItemToObject(result, "sessionid", cJSON_Duplicate(sessionid, 0)); - } - - } - else { - cJSON_AddStringToObject(result, "version", "0"); - } - - cJSON_AddItemToObject(respmsg, "result", result); + if (resultP && *resultP == NULL) { + cJSON *result = cJSON_CreateObject(); + *resultP = result; + cJSON_AddItemToObject(respmsg, RESULT, result); } } return msgid; } -KS_DECLARE(ks_rpcmessage_id) ks_rpcmessage_create_errorresponse( +KS_DECLARE(ks_rpcmessageid_t) ks_rpcmessage_create_errorresponse( const cJSON *request, cJSON **errorP, cJSON **responseP) { - ks_rpcmessage_id msgid = ks_rpcmessage_new_response(request, *errorP, responseP); + ks_rpcmessageid_t msgid = ks_rpcmessage_new_response(request, errorP, responseP); cJSON *respmsg = *responseP; if (msgid) { - - if (*errorP == NULL) { - *errorP = cJSON_CreateObject(); - cJSON_AddItemToObject(respmsg, "error", *errorP); + + if (errorP && *errorP == NULL) { + cJSON *error = cJSON_CreateObject(); + *errorP = error; + cJSON_AddItemToObject(respmsg, ERROR, error); } } diff --git a/libs/libks/test/testmessages.c b/libs/libks/test/testmessages.c index 1849a58494..22dd092aa2 100644 --- a/libs/libks/test/testmessages.c +++ b/libs/libks/test/testmessages.c @@ -18,8 +18,8 @@ void test01() cJSON* parms1 = NULL; cJSON* response1 = NULL; - /*namespace, method, sessionid, version, params, **request */ - ks_rpcmessage_id msgid = ks_rpcmessage_create_request("app1", "func1", "s001", "1.0", &parms1, &request1); + /*namespace, method, params, **request */ + ks_rpcmessageid_t msgid = ks_rpcmessage_create_request("app1", "func1", &parms1, &request1); if (msgid == 0) { printf("message create failed %d\n", msgid); } @@ -37,7 +37,7 @@ void test01() ks_buffer_create(&buffer, 256, 256, 1024); - ks_size_t n = ks_rpc_create_buffer("app2", "func2", "s002", "1.1", &parms2, buffer); + ks_size_t n = ks_rpc_create_buffer("app2", "func2", &parms2, buffer); ks_size_t size = ks_buffer_len(buffer); char *b = (char *)ks_pool_alloc(pool, size+1); @@ -51,7 +51,7 @@ void test01() cJSON *parms3 = cJSON_CreateNumber(1); cJSON *request3 = NULL; - msgid = ks_rpcmessage_create_request("app1", "badbunny", "s002", "1.1", &parms3, &request3); + msgid = ks_rpcmessage_create_request("app1", "badbunny", &parms3, &request3); data = cJSON_PrintUnformatted(request3); printf("\ntest01i request: %d\n%s\n\n", msgid, data); From d6d8ede6b9806540ddc15fd17f57c391321c7961 Mon Sep 17 00:00:00 2001 From: colm Date: Tue, 14 Feb 2017 19:47:00 -0500 Subject: [PATCH 23/34] FS-9952: Add query & tests for blade extention to rpc messages --- libs/libblade/src/blade_rpcproto.c | 63 +++++++++++++++++++--- libs/libblade/src/include/blade_rpcproto.h | 3 +- libs/libblade/test/testrpcproto.c | 40 ++++++++++++-- 3 files changed, 95 insertions(+), 11 deletions(-) diff --git a/libs/libblade/src/blade_rpcproto.c b/libs/libblade/src/blade_rpcproto.c index e955fffb56..3123a77118 100644 --- a/libs/libblade/src/blade_rpcproto.c +++ b/libs/libblade/src/blade_rpcproto.c @@ -812,6 +812,7 @@ KS_DECLARE(ks_rpcmessageid_t) blade_rpc_create_response(cJSON *request, } const char BLADE_JRPC_METHOD[] = "method"; +const char BLADE_JRPC_ID[] = "id"; const char BLADE_JRPC_FIELDS[] = "blade"; const char BLADE_JRPC_TO[] = "to"; const char BLADE_JRPC_FROM[] = "from"; @@ -822,15 +823,22 @@ KS_DECLARE(ks_status_t) blade_rpc_parse_message(cJSON *message, char **namespaceP, char **methodP, char **versionP, + uint32_t *idP, blade_rpc_fields_t **fieldsP) { const char *m = cJSON_GetObjectCstr(message, BLADE_JRPC_METHOD); cJSON *blade = cJSON_GetObjectItem(message, BLADE_JRPC_FIELDS); + cJSON *jid = cJSON_GetObjectItem(message, BLADE_JRPC_ID); *fieldsP = NULL; *namespaceP = NULL; *versionP = NULL; *methodP = NULL; + *idP = 0; + + if (jid) { + *idP = jid->valueint; + } if (!m || !blade) { const char *buffer = cJSON_PrintUnformatted(message); @@ -839,20 +847,60 @@ KS_DECLARE(ks_status_t) blade_rpc_parse_message(cJSON *message, return KS_STATUS_FAIL; } + cJSON *jto = cJSON_GetObjectItem(blade, BLADE_JRPC_TO); + cJSON *jfrom = cJSON_GetObjectItem(blade, BLADE_JRPC_FROM); + cJSON *jtoken = cJSON_GetObjectItem(blade, BLADE_JRPC_TOKEN); + + ks_size_t len = KS_RPCMESSAGE_COMMAND_LENGTH + 1 + KS_RPCMESSAGE_NAMESPACE_LENGTH + 1 + KS_RPCMESSAGE_VERSION_LENGTH + 1 + sizeof(blade_rpc_fields_t) + 1; + uint32_t lento = 0; + uint32_t lenfrom = 0; + uint32_t lentoken = 0; + + if (jto) { + lento = strlen(jto->valuestring) + 1; + len += lento; + } + + if (jfrom) { + lenfrom += strlen(jfrom->valuestring) + 1; + len += lenfrom; + } + + if (jtoken) { + lentoken += strlen(jtoken->valuestring) + 1; + len += lentoken; + } + blade_rpc_fields_t *fields = (blade_rpc_fields_t *)ks_pool_alloc(g_handle->pool, len); + + char *namespace = (char*)fields + sizeof(blade_rpc_fields_t); + char *command = namespace + KS_RPCMESSAGE_NAMESPACE_LENGTH + 1; + char *version = command + KS_RPCMESSAGE_COMMAND_LENGTH + 1; - fields->to = cJSON_GetObjectCstr(blade, BLADE_JRPC_TO); - fields->from = cJSON_GetObjectCstr(blade, BLADE_JRPC_FROM); - fields->from = cJSON_GetObjectCstr(blade, BLADE_JRPC_TOKEN); - - char *namespace = (char*)fields + sizeof(blade_rpc_fields_t); - char *command = namespace + KS_RPCMESSAGE_NAMESPACE_LENGTH + 1; - char *version = command + KS_RPCMESSAGE_COMMAND_LENGTH + 1; + char *ptr = version + KS_RPCMESSAGE_VERSION_LENGTH + 1; + + if (jto) { + strcpy(ptr, jto->valuestring); + fields->to = ptr; + ptr += strlen(jto->valuestring) + 1; + } + + if (jfrom) { + strcpy(ptr, jfrom->valuestring); + fields->from = ptr; + ptr += strlen(jfrom->valuestring) + 1; + } + + if (jtoken) { + strcpy(ptr, jtoken->valuestring); + fields->token = ptr; + ptr += strlen(jtoken->valuestring) + 1; + } blade_rpc_parse_fqcommand(m, namespace, command); @@ -861,6 +909,7 @@ KS_DECLARE(ks_status_t) blade_rpc_parse_message(cJSON *message, *fieldsP = fields; *namespaceP = namespace; *methodP = command; + *versionP = version; return KS_STATUS_SUCCESS; } diff --git a/libs/libblade/src/include/blade_rpcproto.h b/libs/libblade/src/include/blade_rpcproto.h index 193c8604ab..3a8421c6da 100644 --- a/libs/libblade/src/include/blade_rpcproto.h +++ b/libs/libblade/src/include/blade_rpcproto.h @@ -130,7 +130,8 @@ KS_DECLARE(ks_rpcmessageid_t) blade_rpc_create_response(cJSON *request, KS_DECLARE(ks_status_t) blade_rpc_parse_message(cJSON *message, char **namespace, char **method, - char **version, + char **version, + uint32_t *idP, blade_rpc_fields_t **fieldsP); /* diff --git a/libs/libblade/test/testrpcproto.c b/libs/libblade/test/testrpcproto.c index 4679a7f277..2b5f28252f 100644 --- a/libs/libblade/test/testrpcproto.c +++ b/libs/libblade/test/testrpcproto.c @@ -29,7 +29,8 @@ static enum jrpc_status_t process_widget(cJSON *msg, cJSON **response) cJSON *resp = cJSON_CreateObject(); cJSON_AddNumberToObject(resp, "code", 199); - ks_rpcmessageid_t msgid = ks_rpcmessage_create_response(msg, &resp, response); + //ks_rpcmessageid_t msgid = ks_rpcmessage_create_response(msg, &resp, response); + ks_rpcmessageid_t msgid = blade_rpc_create_response(msg, &resp, response); char *b1 = cJSON_PrintUnformatted(*response); //(*response); printf("Response: msgid %d\n%s\n", msgid, b1); @@ -75,10 +76,27 @@ static enum jrpc_status_t process_wombat(cJSON *msg, cJSON **replyP) ks_pool_free(pool, &b1); } else { - printf("process_wombat_preresponse: unable to create response \n"); + printf("process_wombat: unable to create response \n"); return JRPC_ERROR; } + blade_rpc_fields_t *r_fields; + + char *r_method; + char *r_namespace; + char *r_version; + uint32_t r_id; + + ks_status_t s1 = blade_rpc_parse_message(msg, &r_namespace, &r_method, &r_version, &r_id, &r_fields); + + if (s1 == KS_STATUS_FAIL) { + printf("process_wombat: blade_rpc_parse_message failed\n"); + return JRPC_ERROR; + } + + printf("\nprocess_wombat: blade_rpc_parse_message namespace %s, method %s, id %d, version %s, to %s, from %s, token %s\n\n", + r_namespace, r_method, r_id, r_version, + r_fields->to, r_fields->from, r_fields->token); cJSON *parms2 = NULL; @@ -92,7 +110,23 @@ static enum jrpc_status_t process_wombat(cJSON *msg, cJSON **replyP) fields.token = token; // msgid = ks_rpcmessage_create_request("app1", "widget", &parms2, replyP); - msgid = blade_rpc_create_request("app1", "widget", &fields, &parms2, replyP); + msgid = blade_rpc_create_request(r_namespace, r_method, &fields, NULL, replyP); + + if (!msgid) { + printf("process wombat: create of next request failed\n"); + return JRPC_ERROR; + } + + b0 = cJSON_PrintUnformatted(*replyP); + + if (!b0) { + printf("process wombat: create of next request cannot be formatted\n"); + return JRPC_ERROR; + } + + + printf("\nprocess wombat: next request\n%s\n\n", b0); + printf("\n\nexiting process_wombat with a reply to send\n"); From a7add33519df40a6736cef18ce3872a6d5b7803a Mon Sep 17 00:00:00 2001 From: Shane Bryldt Date: Wed, 15 Feb 2017 00:56:29 +0000 Subject: [PATCH 24/34] FS-9952: Committing to show problem with ks_pool_resize --- libs/libblade/src/blade_identity.c | 7 +- libs/libblade/src/blade_module_wss.c | 306 ++++++++++++++------- libs/libblade/src/blade_stack.c | 41 ++- libs/libblade/src/include/blade_identity.h | 2 +- libs/libblade/src/include/blade_stack.h | 2 +- libs/libblade/test/bladec.c | 29 +- libs/libblade/test/bladec2.cfg | 18 -- 7 files changed, 269 insertions(+), 136 deletions(-) diff --git a/libs/libblade/src/blade_identity.c b/libs/libblade/src/blade_identity.c index 030d544de6..040bc9727d 100644 --- a/libs/libblade/src/blade_identity.c +++ b/libs/libblade/src/blade_identity.c @@ -150,15 +150,12 @@ KS_DECLARE(const char *) blade_identity_uri(blade_identity_t *bi) return bi->uri; } -KS_DECLARE(ks_status_t) blade_identity_parameter_get(blade_identity_t *bi, const char *key, const char **value) +KS_DECLARE(const char *) blade_identity_parameter_get(blade_identity_t *bi, const char *key) { ks_assert(bi); ks_assert(key); - ks_assert(value); - *value = (const char *)ks_hash_search(bi->parameters, (void *)key, KS_UNLOCKED); - - return KS_STATUS_SUCCESS; + return (const char *)ks_hash_search(bi->parameters, (void *)key, KS_UNLOCKED); } diff --git a/libs/libblade/src/blade_module_wss.c b/libs/libblade/src/blade_module_wss.c index 0ad2d9bfd2..4478a788a1 100644 --- a/libs/libblade/src/blade_module_wss.c +++ b/libs/libblade/src/blade_module_wss.c @@ -229,6 +229,38 @@ KS_DECLARE(ks_status_t) blade_module_wss_on_unload(blade_module_t *bm) 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) +{ + 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; + + *bt_wssiP = bt_wssi; + + 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; + + ks_pool_free(bt_wssi->pool, bt_wssiP); + + 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; @@ -254,73 +286,71 @@ ks_status_t blade_module_wss_config(blade_module_wss_t *bm_wss, config_setting_t } wss = config_setting_get_member(config, "wss"); - if (!wss) { - ks_log(KS_LOG_DEBUG, "!wss\n"); - return KS_STATUS_FAIL; - } - 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) + 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; - - 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; + 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_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])); + 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 } - } - 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 } @@ -454,6 +484,11 @@ ks_status_t blade_module_wss_listen(blade_module_wss_t *bm_wss, ks_sockaddr_t *a goto done; } + ks_log(KS_LOG_DEBUG, "Listeners Before\n"); + for (int index = 0; index < bm_wss->listeners_count; ++index) { + ks_log(KS_LOG_DEBUG, " Listener %d = %d\n", index, bm_wss->listeners_poll[index].fd); + } + listener_index = bm_wss->listeners_count++; bm_wss->listeners_poll = (struct pollfd *)ks_pool_resize(bm_wss->pool, bm_wss->listeners_poll, @@ -462,6 +497,11 @@ ks_status_t blade_module_wss_listen(blade_module_wss_t *bm_wss, ks_sockaddr_t *a bm_wss->listeners_poll[listener_index].fd = listener; bm_wss->listeners_poll[listener_index].events = POLLIN | POLLERR; + ks_log(KS_LOG_DEBUG, "Listeners After\n"); + for (int index = 0; index < bm_wss->listeners_count; ++index) { + ks_log(KS_LOG_DEBUG, " Listener %d = %d\n", index, bm_wss->listeners_poll[index].fd); + } + done: if (ret != KS_STATUS_SUCCESS) { if (listener != KS_SOCK_INVALID) { @@ -484,26 +524,30 @@ void *blade_module_wss_listeners_thread(ks_thread_t *thread, void *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 & POLLIN)) continue; 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, "Listener POLLERR\n"); 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 continue; } + ks_log(KS_LOG_DEBUG, "Socket Accepted\n"); + blade_transport_wss_init_create(&bt_wss_init, bm_wss, sock); ks_assert(bt_wss_init); - + blade_connection_create(&bc, bm_wss->handle, bt_wss_init, bm_wss->transport_callbacks); ks_assert(bc); @@ -529,6 +573,7 @@ void *blade_module_wss_listeners_thread(ks_thread_t *thread, void *data) if (bt_wss) blade_transport_wss_destroy(&bt_wss); } } + ks_log(KS_LOG_DEBUG, "Stopped\n"); return NULL; } @@ -572,16 +617,86 @@ 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) { + 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; - // @todo connect-out equivilent of accept ks_log(KS_LOG_DEBUG, "Connect Callback: %s\n", blade_identity_uri(target)); - return KS_STATUS_SUCCESS; + // @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 + 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) { + 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_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 + 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); + 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) { + blade_connection_destroy(&bc); + blade_transport_wss_init_destroy(&bt_wss_init); + ks_socket_close(&sock); + ret = KS_STATUS_FAIL; + goto done; + } + // @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 + list_append(&bm_wss->connected, bc); + *bcP = bc; + + blade_connection_state_set(bc, BLADE_CONNECTION_STATE_NEW); + + done: + return ret; } blade_connection_rank_t blade_transport_wss_on_rank(blade_connection_t *bc, blade_identity_t *target) @@ -723,12 +838,22 @@ blade_connection_state_hook_t blade_transport_wss_on_state_new_inbound(blade_con 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; } @@ -755,10 +880,22 @@ blade_connection_state_hook_t blade_transport_wss_on_state_connect_inbound(blade 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) { + // @todo error logging + return BLADE_CONNECTION_STATE_HOOK_DISCONNECT; + } + return BLADE_CONNECTION_STATE_HOOK_SUCCESS; } @@ -781,7 +918,8 @@ blade_connection_state_hook_t blade_transport_wss_on_state_attach_outbound(blade ks_log(KS_LOG_DEBUG, "State Callback: %d\n", (int32_t)condition); - return BLADE_CONNECTION_STATE_HOOK_SUCCESS; + ks_sleep_ms(1000); // @todo temporary testing, remove this and return success once negotiations are done + return BLADE_CONNECTION_STATE_HOOK_BYPASS; } blade_connection_state_hook_t blade_transport_wss_on_state_detach(blade_connection_t *bc, blade_connection_state_condition_t condition) @@ -802,38 +940,6 @@ blade_connection_state_hook_t blade_transport_wss_on_state_ready(blade_connectio return BLADE_CONNECTION_STATE_HOOK_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) -{ - 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; - - *bt_wssiP = bt_wssi; - - 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; - - ks_pool_free(bt_wssi->pool, bt_wssiP); - - return KS_STATUS_SUCCESS; -} - /* For Emacs: * Local Variables: * mode:c diff --git a/libs/libblade/src/blade_stack.c b/libs/libblade/src/blade_stack.c index 3b012f8e38..e522f2c617 100644 --- a/libs/libblade/src/blade_stack.c +++ b/libs/libblade/src/blade_stack.c @@ -44,11 +44,12 @@ struct blade_handle_s { ks_pool_t *pool; ks_thread_pool_t *tpool; - config_setting_t *config_service; + config_setting_t *config_directory; config_setting_t *config_datastore; ks_hash_t *transports; + blade_identity_t *identity; blade_datastore_t *datastore; }; @@ -164,7 +165,7 @@ KS_DECLARE(ks_status_t) blade_handle_destroy(blade_handle_t **bhP) ks_status_t blade_handle_config(blade_handle_t *bh, config_setting_t *config) { - config_setting_t *service = NULL; + config_setting_t *directory = NULL; config_setting_t *datastore = NULL; ks_assert(bh); @@ -172,13 +173,13 @@ ks_status_t blade_handle_config(blade_handle_t *bh, config_setting_t *config) if (!config) return KS_STATUS_FAIL; if (!config_setting_is_group(config)) return KS_STATUS_FAIL; - service = config_setting_get_member(config, "service"); + 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_service = service; + bh->config_directory = directory; bh->config_datastore = datastore; return KS_STATUS_SUCCESS; @@ -201,7 +202,11 @@ KS_DECLARE(ks_status_t) blade_handle_startup(blade_handle_t *bh, config_setting_ return KS_STATUS_FAIL; } } + + // @todo load DSOs + // @todo call onload and onstartup callbacks for modules from DSOs + return KS_STATUS_SUCCESS; } @@ -209,9 +214,12 @@ KS_DECLARE(ks_status_t) blade_handle_shutdown(blade_handle_t *bh) { ks_assert(bh); - // @todo cleanup registered transports + // @todo call onshutdown and onunload callbacks for modules from DSOs + + // @todo unload DSOs + if (blade_handle_datastore_available(bh)) blade_datastore_destroy(&bh->datastore); - + return KS_STATUS_SUCCESS; } @@ -279,14 +287,33 @@ KS_DECLARE(ks_status_t) blade_handle_connect(blade_handle_t *bh, blade_connectio 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); - blade_identity_parameter_get(target, "transport", &tname); + 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)) { diff --git a/libs/libblade/src/include/blade_identity.h b/libs/libblade/src/include/blade_identity.h index 4b9a29c6f7..8d8329459e 100644 --- a/libs/libblade/src/include/blade_identity.h +++ b/libs/libblade/src/include/blade_identity.h @@ -40,7 +40,7 @@ KS_DECLARE(ks_status_t) blade_identity_create(blade_identity_t **biP, ks_pool_t 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(ks_status_t) blade_identity_parameter_get(blade_identity_t *bi, const char *key, const char **value); +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_stack.h b/libs/libblade/src/include/blade_stack.h index 9a3551c565..40c355a8df 100644 --- a/libs/libblade/src/include/blade_stack.h +++ b/libs/libblade/src/include/blade_stack.h @@ -50,7 +50,7 @@ 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_connect(blade_handle_t *bh, blade_connection_t **bcP, blade_identity_t *target); 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); diff --git a/libs/libblade/test/bladec.c b/libs/libblade/test/bladec.c index 796f439253..510897971d 100644 --- a/libs/libblade/test/bladec.c +++ b/libs/libblade/test/bladec.c @@ -30,12 +30,14 @@ void command_test(blade_handle_t *bh, char *args); void command_quit(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); static const struct command_def_s command_defs[] = { { "test", command_test }, { "quit", command_quit }, { "store", command_store }, { "fetch", command_fetch }, + { "connect", command_connect }, { NULL, NULL } }; @@ -47,6 +49,8 @@ int main(int argc, char **argv) config_setting_t *config_blade = NULL; blade_module_t *mod_wss = NULL; //blade_identity_t *id = NULL; + const char *cfgpath = "bladec.cfg"; + ks_global_set_default_logger(KS_LOG_LEVEL_DEBUG); @@ -54,12 +58,10 @@ int main(int argc, char **argv) blade_handle_create(&bh, NULL, NULL); - //blade_identity_create(&id, blade_handle_pool_get(bh)); - //blade_identity_parse(id, "test@domain.com/laptop?transport=wss&host=127.0.0.1&port=1234"); + if (argc > 1) cfgpath = argv[1]; - // @todo load config file, and lookup "blade" setting to put into config_blade config_init(&config); - if (!config_read_file(&config, "bladec.cfg")) { + 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; @@ -91,6 +93,10 @@ int main(int argc, char **argv) loop(bh); + blade_module_wss_on_shutdown(mod_wss); + + blade_module_wss_on_unload(mod_wss); + blade_handle_destroy(&bh); blade_shutdown(); @@ -236,3 +242,18 @@ void command_fetch(blade_handle_t *bh, char *args) blade_handle_datastore_fetch(bh, blade_datastore_fetch_callback, key, strlen(key), bh); } + +void command_connect(blade_handle_t *bh, char *args) +{ + blade_connection_t *bc = NULL; + blade_identity_t *target = NULL; + + ks_assert(bh); + ks_assert(args); + + blade_identity_create(&target, blade_handle_pool_get(bh)); + + if (blade_identity_parse(target, args) == KS_STATUS_SUCCESS) blade_handle_connect(bh, &bc, target); + + blade_identity_destroy(&target); +} diff --git a/libs/libblade/test/bladec2.cfg b/libs/libblade/test/bladec2.cfg index 1167439083..14582ed3ab 100644 --- a/libs/libblade/test/bladec2.cfg +++ b/libs/libblade/test/bladec2.cfg @@ -1,10 +1,6 @@ blade: { identity = "peer@domain"; - directory: - { - uris = ( "directory@domain?transport=wss&host=127.0.0.1&port=2100" ); - }; datastore: { database: @@ -12,18 +8,4 @@ blade: path = ":mem:"; }; }; - wss: - { - endpoints: - { - ipv4 = ( { address = "0.0.0.0", port = 2101 } ); - ipv6 = ( { address = "::", port = 2101 } ); - backlog = 128; - }; - # SSL group is optional, disabled when absent - ssl: - { - # todo: server SSL stuffs here - }; - }; }; From aeff38c16513afe6c6e8cf92f1bd85e115b3ce98 Mon Sep 17 00:00:00 2001 From: colm Date: Tue, 14 Feb 2017 22:45:49 -0500 Subject: [PATCH 25/34] FS-9952: Fix compile errors in test 64bit build --- libs/libks/test/testmessages.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/libs/libks/test/testmessages.c b/libs/libks/test/testmessages.c index 22dd092aa2..7609cf69ac 100644 --- a/libs/libks/test/testmessages.c +++ b/libs/libks/test/testmessages.c @@ -43,7 +43,7 @@ void test01() char *b = (char *)ks_pool_alloc(pool, size+1); ks_buffer_read(buffer, b, size); - printf("test01 request2: %d %d from ks_buffer\n%s\n\n\n", n, size, b); + printf("test01 request2: %zd %zd from ks_buffer\n%s\n\n\n", n, size, b); /* create message 3 */ @@ -56,9 +56,8 @@ void test01() printf("\ntest01i request: %d\n%s\n\n", msgid, data); cJSON *response3 = NULL; - cJSON *reply3 = NULL; - ks_rpcmessage_create_response(request3, &reply3, &response3); + ks_rpcmessage_create_response(request3, NULL, &response3); data = cJSON_PrintUnformatted(response3); printf("\ntest01 response3: %d\n%s\n\n", msgid, data); From 8e417220d3329226d07bcf6fcbe5b2e3999e7bb0 Mon Sep 17 00:00:00 2001 From: colm Date: Wed, 15 Feb 2017 21:17:13 -0500 Subject: [PATCH 26/34] FS-9952: Add blade rpc error response creation --- libs/libblade/src/blade_rpcproto.c | 25 ++++++++++++++++++++++ libs/libblade/src/include/blade_rpcproto.h | 4 ++++ libs/libks/src/ks_rpcmessage.c | 5 ++++- 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/libs/libblade/src/blade_rpcproto.c b/libs/libblade/src/blade_rpcproto.c index 3123a77118..bedfd1c505 100644 --- a/libs/libblade/src/blade_rpcproto.c +++ b/libs/libblade/src/blade_rpcproto.c @@ -811,6 +811,31 @@ KS_DECLARE(ks_rpcmessageid_t) blade_rpc_create_response(cJSON *request, return msgid; } +KS_DECLARE(ks_rpcmessageid_t) blade_rpc_create_errorresponse(cJSON *request, + cJSON **errorP, + cJSON **responseP) +{ + ks_rpcmessageid_t msgid = blade_rpc_create_response(request, NULL, responseP); + + if (msgid) { + + if (errorP) { + + if (*errorP) { + cJSON_AddItemToObject(*responseP, "error", *errorP); + } + else { + cJSON *error = cJSON_CreateObject(); + cJSON_AddItemToObject(*responseP, "error", error); + *errorP = error; + } + } + } + + return msgid; +} + + const char BLADE_JRPC_METHOD[] = "method"; const char BLADE_JRPC_ID[] = "id"; const char BLADE_JRPC_FIELDS[] = "blade"; diff --git a/libs/libblade/src/include/blade_rpcproto.h b/libs/libblade/src/include/blade_rpcproto.h index 3a8421c6da..f108c981e6 100644 --- a/libs/libblade/src/include/blade_rpcproto.h +++ b/libs/libblade/src/include/blade_rpcproto.h @@ -127,6 +127,10 @@ KS_DECLARE(ks_rpcmessageid_t) blade_rpc_create_response(cJSON *request, cJSON **reply, cJSON **response); +KS_DECLARE(ks_rpcmessageid_t) blade_rpc_create_errorresponse(cJSON *request, + cJSON **reply, + cJSON **response); + KS_DECLARE(ks_status_t) blade_rpc_parse_message(cJSON *message, char **namespace, char **method, diff --git a/libs/libks/src/ks_rpcmessage.c b/libs/libks/src/ks_rpcmessage.c index 1b5e48754e..e950c693e8 100644 --- a/libs/libks/src/ks_rpcmessage.c +++ b/libs/libks/src/ks_rpcmessage.c @@ -286,7 +286,7 @@ KS_DECLARE(ks_rpcmessageid_t) ks_rpcmessage_create_errorresponse( cJSON **errorP, cJSON **responseP) { - ks_rpcmessageid_t msgid = ks_rpcmessage_new_response(request, errorP, responseP); + ks_rpcmessageid_t msgid = ks_rpcmessage_new_response(request, NULL, responseP); cJSON *respmsg = *responseP; if (msgid) { @@ -296,6 +296,9 @@ KS_DECLARE(ks_rpcmessageid_t) ks_rpcmessage_create_errorresponse( *errorP = error; cJSON_AddItemToObject(respmsg, ERROR, error); } + else if (errorP && *errorP) { + cJSON_AddItemToObject(*responseP, ERROR, *errorP); + } } return msgid; From cb7e95fd9a0cce2ec97ea79b81b69fdbc11013ea Mon Sep 17 00:00:00 2001 From: Shane Bryldt Date: Tue, 21 Feb 2017 16:37:50 +0000 Subject: [PATCH 27/34] FS-9952: A bunch of cleanup and shifting connections towards ID based passing instead of pointers, will replicate and adjust for session system next --- libs/libblade/Makefile.am | 4 +- libs/libblade/src/blade_connection.c | 179 +++++------ libs/libblade/src/blade_module_wss.c | 67 +++-- libs/libblade/src/blade_protocol.c | 84 ++++++ libs/libblade/src/blade_session.c | 294 +++++++++++++++++++ libs/libblade/src/blade_stack.c | 93 +++++- libs/libblade/src/include/blade.h | 2 + libs/libblade/src/include/blade_connection.h | 12 +- libs/libblade/src/include/blade_protocol.h | 54 ++++ libs/libblade/src/include/blade_session.h | 63 ++++ libs/libblade/src/include/blade_stack.h | 5 +- libs/libblade/src/include/blade_types.h | 37 ++- 12 files changed, 784 insertions(+), 110 deletions(-) create mode 100644 libs/libblade/src/blade_protocol.c create mode 100644 libs/libblade/src/blade_session.c create mode 100644 libs/libblade/src/include/blade_protocol.h create mode 100644 libs/libblade/src/include/blade_session.h diff --git a/libs/libblade/Makefile.am b/libs/libblade/Makefile.am index c4e1872375..46e751405f 100644 --- a/libs/libblade/Makefile.am +++ b/libs/libblade/Makefile.am @@ -14,13 +14,15 @@ lib_LTLIBRARIES = libblade.la libblade_la_SOURCES = src/blade.c src/blade_stack.c src/bpcp.c src/blade_datastore.c libblade_la_SOURCES += src/blade_rpcproto.c libblade_la_SOURCES += src/blade_identity.c src/blade_module.c src/blade_connection.c src/blade_module_wss.c +libblade_la_SOURCES += src/blade_session.c src/blade_protocol.c libblade_la_CFLAGS = $(AM_CFLAGS) $(AM_CPPFLAGS) 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 library_include_HEADERS += src/include/bpcp.h src/include/blade_datastore.h src/include/blade_rpcproto.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_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 library_include_HEADERS += src/include/unqlite.h test/tap.h tests: libblade.la diff --git a/libs/libblade/src/blade_connection.c b/libs/libblade/src/blade_connection.c index e18f2c247e..d548c477de 100644 --- a/libs/libblade/src/blade_connection.c +++ b/libs/libblade/src/blade_connection.c @@ -42,58 +42,16 @@ struct blade_connection_s { blade_transport_callbacks_t *transport_callbacks; ks_bool_t shutdown; - // @todo add auto generated UUID 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; - //ks_q_t *receiving; }; -// @todo may want to make this reusable for session as it'll need to queue the same details during temporary connection loss -typedef struct blade_connection_sending_s blade_connection_sending_t; -struct blade_connection_sending_s { - ks_pool_t *pool; - blade_identity_t *target; - cJSON *json; -}; - -ks_status_t blade_connection_sending_create(blade_connection_sending_t **bcsP, ks_pool_t *pool, blade_identity_t *target, cJSON *json) -{ - blade_connection_sending_t *bcs = NULL; - - ks_assert(bcsP); - ks_assert(pool); - ks_assert(json); - - bcs = ks_pool_alloc(pool, sizeof(blade_connection_sending_t)); - bcs->pool = pool; - bcs->target = target; - bcs->json = json; - *bcsP = bcs; - - return KS_STATUS_SUCCESS; -} - -ks_status_t blade_connection_sending_destroy(blade_connection_sending_t **bcsP) -{ - blade_connection_sending_t *bcs = NULL; - - ks_assert(bcsP); - ks_assert(*bcsP); - - bcs = *bcsP; - - if (bcs->target) blade_identity_destroy(&bcs->target); - if (bcs->json) cJSON_Delete(bcs->json); - - ks_pool_free(bcs->pool, bcsP); - - return KS_STATUS_SUCCESS; -} - - void *blade_connection_state_thread(ks_thread_t *thread, void *data); @@ -104,6 +62,7 @@ KS_DECLARE(ks_status_t) blade_connection_create(blade_connection_t **bcP, { blade_connection_t *bc = NULL; ks_pool_t *pool = NULL; + uuid_t id; ks_assert(bcP); ks_assert(bh); @@ -116,6 +75,14 @@ KS_DECLARE(ks_status_t) blade_connection_create(blade_connection_t **bcP, 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); @@ -127,7 +94,7 @@ KS_DECLARE(ks_status_t) blade_connection_create(blade_connection_t **bcP, KS_DECLARE(ks_status_t) blade_connection_destroy(blade_connection_t **bcP) { blade_connection_t *bc = NULL; - + ks_assert(bcP); ks_assert(*bcP); @@ -137,6 +104,10 @@ KS_DECLARE(ks_status_t) blade_connection_destroy(blade_connection_t **bcP) ks_q_destroy(&bc->sending); + ks_rwl_destroy(&bc->lock); + + ks_pool_free(bc->pool, &bc->id); + ks_pool_free(bc->pool, bcP); return KS_STATUS_SUCCESS; @@ -165,7 +136,7 @@ KS_DECLARE(ks_status_t) blade_connection_startup(blade_connection_t *bc, blade_c KS_DECLARE(ks_status_t) blade_connection_shutdown(blade_connection_t *bc) { - blade_connection_sending_t *bcs = NULL; + cJSON *json = NULL; ks_assert(bc); @@ -176,11 +147,62 @@ KS_DECLARE(ks_status_t) blade_connection_shutdown(blade_connection_t *bc) bc->shutdown = KS_FALSE; } - while (ks_q_trypop(bc->sending, (void **)&bcs) == KS_STATUS_SUCCESS && bcs) blade_connection_sending_destroy(&bcs); + while (ks_q_trypop(bc->sending, (void **)&json) == KS_STATUS_SUCCESS && json) cJSON_Delete(json); 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(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); @@ -263,40 +285,23 @@ KS_DECLARE(void) blade_connection_disconnect(blade_connection_t *bc) blade_connection_state_set(bc, BLADE_CONNECTION_STATE_DETACH); } -KS_DECLARE(ks_status_t) blade_connection_sending_push(blade_connection_t *bc, blade_identity_t *target, cJSON *json) +KS_DECLARE(ks_status_t) blade_connection_sending_push(blade_connection_t *bc, cJSON *json) { - blade_connection_sending_t *bcs = NULL; - - ks_assert(bc); - ks_assert(json); - - blade_connection_sending_create(&bcs, bc->pool, target, json); - ks_assert(bcs); - - return ks_q_push(bc->sending, bcs); -} - -KS_DECLARE(ks_status_t) blade_connection_sending_pop(blade_connection_t *bc, blade_identity_t **target, cJSON **json) -{ - ks_status_t ret = KS_STATUS_SUCCESS; - blade_connection_sending_t *bcs = NULL; + cJSON *json_copy = NULL; ks_assert(bc); ks_assert(json); - ret = ks_q_trypop(bc->sending, (void **)&bcs); + json_copy = cJSON_Duplicate(json, 1); + return ks_q_push(bc->sending, json_copy); +} - if (bcs) { - if (target) *target = bcs->target; - *json = bcs->json; +KS_DECLARE(ks_status_t) blade_connection_sending_pop(blade_connection_t *bc, cJSON **json) +{ + ks_assert(bc); + ks_assert(json); - bcs->target = NULL; - bcs->json = NULL; - - blade_connection_sending_destroy(&bcs); - } - - return ret; + return ks_q_trypop(bc->sending, (void **)json); } @@ -306,7 +311,6 @@ void *blade_connection_state_thread(ks_thread_t *thread, void *data) blade_connection_state_t state; blade_transport_state_callback_t callback = NULL; blade_connection_state_hook_t hook = BLADE_CONNECTION_STATE_HOOK_SUCCESS; - blade_identity_t *target = NULL; cJSON *json = NULL; ks_assert(thread); @@ -320,23 +324,32 @@ void *blade_connection_state_thread(ks_thread_t *thread, void *data) hook = BLADE_CONNECTION_STATE_HOOK_SUCCESS; callback = blade_connection_state_callback_lookup(bc, state); - while (blade_connection_sending_pop(bc, &target, &json) == KS_STATUS_SUCCESS && json) { - if (bc->transport_callbacks->onsend(bc, target, json) != KS_STATUS_SUCCESS) { - blade_connection_disconnect(bc); - break; + // @todo only READY state? + if (state != BLADE_CONNECTION_STATE_DETACH && state != BLADE_CONNECTION_STATE_DISCONNECT) { + 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; + } } } if (state == BLADE_CONNECTION_STATE_READY) { - do { + ks_bool_t done = KS_FALSE; + while (!done) { if (bc->transport_callbacks->onreceive(bc, &json) != KS_STATUS_SUCCESS) { blade_connection_disconnect(bc); break; } - if (json) { + if (!(done = (json == NULL))) { // @todo push json to session receiving queue + cJSON_Delete(json); + json = NULL; } - } while (json) ; + } } if (callback) hook = callback(bc, BLADE_CONNECTION_STATE_CONDITION_POST); diff --git a/libs/libblade/src/blade_module_wss.c b/libs/libblade/src/blade_module_wss.c index 4478a788a1..8ca224cf9f 100644 --- a/libs/libblade/src/blade_module_wss.c +++ b/libs/libblade/src/blade_module_wss.c @@ -101,7 +101,7 @@ 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); 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, blade_identity_t *target, cJSON *json); +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); @@ -484,11 +484,6 @@ ks_status_t blade_module_wss_listen(blade_module_wss_t *bm_wss, ks_sockaddr_t *a goto done; } - ks_log(KS_LOG_DEBUG, "Listeners Before\n"); - for (int index = 0; index < bm_wss->listeners_count; ++index) { - ks_log(KS_LOG_DEBUG, " Listener %d = %d\n", index, bm_wss->listeners_poll[index].fd); - } - listener_index = bm_wss->listeners_count++; bm_wss->listeners_poll = (struct pollfd *)ks_pool_resize(bm_wss->pool, bm_wss->listeners_poll, @@ -497,11 +492,6 @@ ks_status_t blade_module_wss_listen(blade_module_wss_t *bm_wss, ks_sockaddr_t *a bm_wss->listeners_poll[listener_index].fd = listener; bm_wss->listeners_poll[listener_index].events = POLLIN | POLLERR; - ks_log(KS_LOG_DEBUG, "Listeners After\n"); - for (int index = 0; index < bm_wss->listeners_count; ++index) { - ks_log(KS_LOG_DEBUG, " Listener %d = %d\n", index, bm_wss->listeners_poll[index].fd); - } - done: if (ret != KS_STATUS_SUCCESS) { if (listener != KS_SOCK_INVALID) { @@ -533,7 +523,6 @@ void *blade_module_wss_listeners_thread(ks_thread_t *thread, void *data) 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, "Listener POLLERR\n"); continue; } if (!(bm_wss->listeners_poll[index].revents & POLLIN)) continue; @@ -550,6 +539,8 @@ void *blade_module_wss_listeners_thread(ks_thread_t *thread, void *data) 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) { blade_connection_destroy(&bc); @@ -557,8 +548,11 @@ void *blade_module_wss_listeners_thread(ks_thread_t *thread, void *data) ks_socket_close(&sock); continue; } + 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); } } @@ -566,6 +560,7 @@ void *blade_module_wss_listeners_thread(ks_thread_t *thread, void *data) bt_wss_init = (blade_transport_wss_init_t *)blade_connection_transport_init_get(bc); bt_wss = (blade_transport_wss_t *)blade_connection_transport_get(bc); + blade_handle_connections_remove(bc); list_delete(&bm_wss->connected, bc); if (bt_wss_init) blade_transport_wss_init_destroy(&bt_wss_init); @@ -730,7 +725,7 @@ ks_status_t blade_transport_wss_write(blade_transport_wss_t *bt_wss, cJSON *json return ret; } -ks_status_t blade_transport_wss_on_send(blade_connection_t *bc, blade_identity_t *target, cJSON *json) +ks_status_t blade_transport_wss_on_send(blade_connection_t *bc, cJSON *json) { ks_status_t ret = KS_STATUS_SUCCESS; blade_transport_wss_t *bt_wss = NULL; @@ -745,7 +740,6 @@ ks_status_t blade_transport_wss_on_send(blade_connection_t *bc, blade_identity_t ret = blade_transport_wss_write(bt_wss, json); // @todo use reference counting on blade_identity_t and cJSON objects - if (target) blade_identity_destroy(&target); cJSON_Delete(json); return ret; @@ -904,10 +898,30 @@ blade_connection_state_hook_t blade_transport_wss_on_state_attach_inbound(blade_ ks_assert(bc); ks_log(KS_LOG_DEBUG, "State Callback: %d\n", (int32_t)condition); - // @todo Establish sessid and discover existing session or create and register new session through BLADE commands - // Set session state to CONNECT if its new or RECONNECT if existing - // start session and its thread if its new + // @todo block while reading expected message with blade_transport_wss_read(bt_wss, json) + + // @todo check if expected message is a request by confirming it has a method field (along with json field validation, stay compliant with jsonrpc) + + // @todo validate method is "blade.session.attach" + + // @todo validate parameters "session-id" and "session-token" must both be present or omitted, validate both are strings and valid uuid format + // if both are omitted, params may be omitted entirely by jsonrpc spec + + // @todo if session-id is provided, lookup existing session within the blade_handle_t + + // @todo if the session exists, verify the session-token, if it matches then use this session + + // @todo if the session-token does not match, or the session does not exist, or the session-id and session-token are not provided then create a new session + + // @todo once session is established, associate it to the connection + + // @todo if anything fails, return HOOK_DISCONNECT, otherwise return HOOK_SUCCESS which will continue the rest of the session attaching process + // which is to grab the expected session off the connection and attach the connection to the connection list on the session, start the session thread if + // it hasn't already been started, and set the session state to CONNECT or ATTACH... discuss with tony, finalize session state machine regarding multiple + // connections attempting to attach at the same time to the session and changing the session state, may need to queue pending connections to the session + // and process them from within the session state machine thread + ks_sleep_ms(1000); // @todo temporary testing, remove this and return success once negotiations are done return BLADE_CONNECTION_STATE_HOOK_BYPASS; } @@ -918,6 +932,25 @@ blade_connection_state_hook_t blade_transport_wss_on_state_attach_outbound(blade ks_log(KS_LOG_DEBUG, "State Callback: %d\n", (int32_t)condition); + // @todo produce jsonrpc compliant message to call method "blade.session.attach" + + // @todo add params with nested session-id and session-token if attempting to reconnect as a client, this should probably be passed in from + // the blade_handle_connect() call and then through the init parameters for the transport (do not directly use the old session, but copy the id and token) + + // @todo block while sending message with blade_transport_wss_write(bt_wss, json) + + // @todo block while receiving expected response with blade_transport_wss_read(bt_wss, json) + + // @todo check for error field, log and return HOOK_DISCONNECT if any errors occur + + // @todo check for result field, and nested session-id and session-token + + // @todo lookup the old session from the blade_handle_t, if it still exists then use this session + + // @todo if the old session does not exist, then create a new session and populate with the parameters from the results + + // @todo once session is established, associate it to the connection, see attach_inbound for notes regarding universal actions after returning SUCCESS + ks_sleep_ms(1000); // @todo temporary testing, remove this and return success once negotiations are done return BLADE_CONNECTION_STATE_HOOK_BYPASS; } diff --git a/libs/libblade/src/blade_protocol.c b/libs/libblade/src/blade_protocol.c new file mode 100644 index 0000000000..4a3c071594 --- /dev/null +++ b/libs/libblade/src/blade_protocol.c @@ -0,0 +1,84 @@ +/* + * 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, ks_pool_t *pool, const char *session_id, cJSON *json /*, response_callback*/) +{ + blade_request_t *breq = NULL; + + ks_assert(breqP); + ks_assert(pool); + ks_assert(session_id); + ks_assert(json); + + breq = ks_pool_alloc(pool, sizeof(blade_request_t)); + breq->pool = pool; + breq->refs = 1; + breq->session_id = ks_pstrdup(pool, session_id); + breq->message = json; + breq->message_id = cJSON_GetObjectCstr(json, "id"); + //breq->response_callback = response_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; +} + + +/* 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..950a510838 --- /dev/null +++ b/libs/libblade/src/blade_session.c @@ -0,0 +1,294 @@ +/* + * 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; + list_t connections; + + ks_q_t *sending; + ks_q_t *receiving; +}; + +void *blade_session_state_thread(ks_thread_t *thread, void *data); + + +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); + + 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); + + *bsP = bs; + + 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); + + list_destroy(&bs->connections); + ks_q_destroy(&bs->receiving); + ks_q_destroy(&bs->sending); + + ks_pool_free(bs->pool, &bs->id); + + ks_pool_free(bs->pool, bsP); + + 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; + } + + 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); + + return KS_STATUS_SUCCESS; +} + +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(void) blade_session_state_set(blade_session_t *bs, blade_session_state_t state) +{ + ks_assert(bs); + + bs->state = state; +} + +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) + blade_session_state_set(bs, BLADE_SESSION_STATE_HANGUP); +} + +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) { + // @todo error logging... this shouldn't happen + 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; + } + + *bcP = bc; + + return KS_STATUS_SUCCESS; +} + +KS_DECLARE(ks_status_t) blade_session_send(blade_session_t *bs, cJSON *json) +{ + ks_assert(bs); + ks_assert(json); + + // @todo check json for "method", if this is an outgoing request then build up the data for a response to lookup the message id and get back to the request + // this can reuse blade_request_t so that when the blade_response_t is passed up the blade_request_t within it is familiar from inbound requests + + 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); + } + + 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); +} + +// @todo receive queue push and pop + +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); + cJSON_Delete(json); + } + } + + switch (state) { + case BLADE_SESSION_STATE_DESTROY: + return NULL; + case BLADE_SESSION_STATE_HANGUP: + // @todo detach from session if this connection is attached + blade_session_state_set(bs, BLADE_SESSION_STATE_DESTROY); + break; + case BLADE_SESSION_STATE_READY: + // @todo pop from session receiving queue and pass to blade_protocol_process() + break; + default: break; + } + } + + return NULL; +} + +/* 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 e522f2c617..6ec4af3a29 100644 --- a/libs/libblade/src/blade_stack.c +++ b/libs/libblade/src/blade_stack.c @@ -47,10 +47,17 @@ struct blade_handle_s { config_setting_t *config_directory; config_setting_t *config_datastore; - ks_hash_t *transports; + ks_hash_t *transports; // registered transports exposed by modules, NOT active connections - blade_identity_t *identity; + //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 + // @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 }; @@ -127,6 +134,14 @@ KS_DECLARE(ks_status_t) blade_handle_create(blade_handle_t **bhP, ks_pool_t *poo 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->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); + // @todo decide if this is uint32_t or uuid string, prefer uuid string to avoid needing another lock and variable for next id + 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; return KS_STATUS_SUCCESS; @@ -150,6 +165,9 @@ KS_DECLARE(ks_status_t) blade_handle_destroy(blade_handle_t **bhP) blade_handle_shutdown(bh); + ks_hash_destroy(&bh->requests); + ks_hash_destroy(&bh->sessions); + ks_hash_destroy(&bh->connections); ks_hash_destroy(&bh->transports); if (bh->tpool && (flags & BH_MYTPOOL)) ks_thread_pool_destroy(&bh->tpool); @@ -212,9 +230,23 @@ KS_DECLARE(ks_status_t) blade_handle_startup(blade_handle_t *bh, config_setting_ KS_DECLARE(ks_status_t) blade_handle_shutdown(blade_handle_t *bh) { + ks_hash_iterator_t *it = NULL; + ks_assert(bh); - // @todo call onshutdown and onunload callbacks for modules from DSOs + 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); + } + + // @todo terminate all sessions, which will disconnect all attached connections + + // @todo call onshutdown and onunload callbacks for modules from DSOs, which will unregister transports and disconnect remaining unattached connections // @todo unload DSOs @@ -245,6 +277,8 @@ KS_DECLARE(ks_status_t) blade_handle_transport_register(blade_handle_t *bh, blad 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); @@ -331,6 +365,59 @@ KS_DECLARE(ks_status_t) blade_handle_connect(blade_handle_t *bh, blade_connectio } +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); + + return ret; +} + KS_DECLARE(ks_bool_t) blade_handle_datastore_available(blade_handle_t *bh) diff --git a/libs/libblade/src/include/blade.h b/libs/libblade/src/include/blade.h index d558fa5f4c..54d744657a 100644 --- a/libs/libblade/src/include/blade.h +++ b/libs/libblade/src/include/blade.h @@ -43,6 +43,8 @@ #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" diff --git a/libs/libblade/src/include/blade_connection.h b/libs/libblade/src/include/blade_connection.h index 70ee105b7d..5d149bc445 100644 --- a/libs/libblade/src/include/blade_connection.h +++ b/libs/libblade/src/include/blade_connection.h @@ -43,16 +43,20 @@ KS_DECLARE(ks_status_t) blade_connection_create(blade_connection_t **bcP, 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(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, blade_identity_t *target, cJSON *json); -KS_DECLARE(ks_status_t) blade_connection_sending_pop(blade_connection_t *bc, blade_identity_t **target, cJSON **json); -KS_DECLARE(ks_status_t) blade_connection_receiving_push(blade_connection_t *bc, cJSON *json); -KS_DECLARE(ks_status_t) blade_connection_receiving_pop(blade_connection_t *bc, cJSON **json); +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_END_EXTERN_C #endif diff --git a/libs/libblade/src/include/blade_protocol.h b/libs/libblade/src/include/blade_protocol.h new file mode 100644 index 0000000000..fcd89cc01a --- /dev/null +++ b/libs/libblade/src/include/blade_protocol.h @@ -0,0 +1,54 @@ +/* + * 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, ks_pool_t *pool, const char *session_id, cJSON *json /*, response_callback*/); +KS_DECLARE(ks_status_t) blade_request_destroy(blade_request_t **breqP); +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..975d10bbfb --- /dev/null +++ b/libs/libblade/src/include/blade_session.h @@ -0,0 +1,63 @@ +/* + * 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_sesssion_startup(blade_session_t *bs); +KS_DECLARE(ks_status_t) blade_session_shutdown(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(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_status_t) blade_session_send(blade_session_t *bs, cJSON *json); +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_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 40c355a8df..f6de9665ba 100644 --- a/libs/libblade/src/include/blade_stack.h +++ b/libs/libblade/src/include/blade_stack.h @@ -51,7 +51,10 @@ 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_connect(blade_handle_t *bh, blade_connection_t **bcP, blade_identity_t *target); - +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(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, diff --git a/libs/libblade/src/include/blade_types.h b/libs/libblade/src/include/blade_types.h index 249dd9b31f..5531557dd6 100644 --- a/libs/libblade/src/include/blade_types.h +++ b/libs/libblade/src/include/blade_types.h @@ -44,6 +44,9 @@ 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_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_datastore_s blade_datastore_t; @@ -84,6 +87,18 @@ typedef enum { BLADE_CONNECTION_RANK_GREAT, } blade_connection_rank_t; + +typedef enum { + BLADE_SESSION_STATE_NONE, + BLADE_SESSION_STATE_DESTROY, + BLADE_SESSION_STATE_HANGUP, + 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); @@ -99,7 +114,7 @@ struct blade_module_callbacks_s { typedef ks_status_t (*blade_transport_connect_callback_t)(blade_connection_t **bcP, blade_module_t *bm, blade_identity_t *target); 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, blade_identity_t *target, cJSON *json); +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); @@ -124,6 +139,26 @@ struct blade_transport_callbacks_s { }; +struct blade_request_s { + ks_pool_t *pool; + uint32_t refs; + const char *session_id; + + cJSON *message; + const char *message_id; // pulled from message for easier keying + // @todo ttl to wait for response before injecting an error response locally + // @todo rpc response callback +}; + +struct blade_response_s { + ks_pool_t *pool; + uint32_t refs; + const char *session_id; + blade_request_t *request; + + cJSON *message; +}; + KS_END_EXTERN_C #endif From 3d8fd5dcafdcebf7f570e4b7586d350eec2cd410 Mon Sep 17 00:00:00 2001 From: Shane Bryldt Date: Tue, 21 Feb 2017 21:20:44 +0000 Subject: [PATCH 28/34] FS-9952: Added the first half of the session negotations for the server side, untested as it requires the second half coming soon for client side --- libs/libblade/src/blade_connection.c | 35 ++++++- libs/libblade/src/blade_module_wss.c | 105 ++++++++++++++++--- libs/libblade/src/blade_session.c | 100 ++++++++++++++++++ libs/libblade/src/blade_stack.c | 54 ++++++++++ libs/libblade/src/include/blade_connection.h | 2 + libs/libblade/src/include/blade_session.h | 9 +- libs/libblade/src/include/blade_stack.h | 5 + libs/libblade/src/include/blade_types.h | 1 + 8 files changed, 288 insertions(+), 23 deletions(-) diff --git a/libs/libblade/src/blade_connection.c b/libs/libblade/src/blade_connection.c index d548c477de..eadbea4adb 100644 --- a/libs/libblade/src/blade_connection.c +++ b/libs/libblade/src/blade_connection.c @@ -50,6 +50,8 @@ struct blade_connection_s { ks_rwl_t *lock; ks_q_t *sending; + + const char *session; }; void *blade_connection_state_thread(ks_thread_t *thread, void *data); @@ -147,6 +149,8 @@ KS_DECLARE(ks_status_t) blade_connection_shutdown(blade_connection_t *bc) 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); return KS_STATUS_SUCCESS; @@ -304,6 +308,20 @@ KS_DECLARE(ks_status_t) blade_connection_sending_pop(blade_connection_t *bc, cJS 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) { @@ -369,11 +387,18 @@ void *blade_connection_state_thread(ks_thread_t *thread, void *data) blade_connection_state_set(bc, BLADE_CONNECTION_STATE_ATTACH); break; case BLADE_CONNECTION_STATE_ATTACH: - // @todo receive message with nullable session id for reconnect and some sort of secure token for a reconnect challenge? - // determine how much of session management is handled here... do we process these session negotiation messages without - // passing it up to the application layer? or does the application layer give back a session and build the response? - blade_connection_state_set(bc, BLADE_CONNECTION_STATE_READY); - break; + { + 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); + + blade_session_read_unlock(bs); // unlock the session we expect to be locked during the callback to ensure we can finish attaching + break; + } case BLADE_CONNECTION_STATE_DETACH: // @todo detach from session if this connection is attached blade_connection_state_set(bc, BLADE_CONNECTION_STATE_DISCONNECT); diff --git a/libs/libblade/src/blade_module_wss.c b/libs/libblade/src/blade_module_wss.c index 8ca224cf9f..429fdba344 100644 --- a/libs/libblade/src/blade_module_wss.c +++ b/libs/libblade/src/blade_module_wss.c @@ -895,35 +895,104 @@ blade_connection_state_hook_t blade_transport_wss_on_state_connect_outbound(blad 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; + cJSON *json = NULL; + cJSON *params = NULL; + blade_session_t *bs = NULL; + blade_handle_t *bh = NULL; + const char *jsonrpc = NULL; + const char *method = NULL; + const char *id = 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); - // @todo block while reading expected message with blade_transport_wss_read(bt_wss, json) + bt_wss = (blade_transport_wss_t *)blade_connection_transport_get(bc); - // @todo check if expected message is a request by confirming it has a method field (along with json field validation, stay compliant with jsonrpc) + // @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) == KS_STATUS_SUCCESS) { + if (json) break; + ks_sleep(250); + if (ks_time_now() >= timeout) break; + } - // @todo validate method is "blade.session.attach" + if (!json) { + // @todo error logging + ret = BLADE_CONNECTION_STATE_HOOK_DISCONNECT; + goto done; + } - // @todo validate parameters "session-id" and "session-token" must both be present or omitted, validate both are strings and valid uuid format - // if both are omitted, params may be omitted entirely by jsonrpc spec + // @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, "jsonrpc"); // @todo check for definitions of these keys and fixed values + if (!jsonrpc || strcmp(jsonrpc, "2.0")) { + // @todo error logging + ret = BLADE_CONNECTION_STATE_HOOK_DISCONNECT; + goto done; + } - // @todo if session-id is provided, lookup existing session within the blade_handle_t + id = cJSON_GetObjectCstr(json, "id"); // @todo switch to number if we are not using a uuid for message id + if (!id) { + // @todo error logging + ret = BLADE_CONNECTION_STATE_HOOK_DISCONNECT; + goto done; + } - // @todo if the session exists, verify the session-token, if it matches then use this session + method = cJSON_GetObjectCstr(json, "method"); + if (!method || strcasecmp(method, "blade.session.attach")) { + // @todo error logging + ret = BLADE_CONNECTION_STATE_HOOK_DISCONNECT; + goto done; + } - // @todo if the session-token does not match, or the session does not exist, or the session-id and session-token are not provided then create a new session + params = cJSON_GetObjectItem(json, "params"); + if (params) { + sid = cJSON_GetObjectCstr(params, "session-id"); + if (sid) { + // @todo validate uuid format by parsing, not currently available in uuid functions + ks_log(KS_LOG_DEBUG, "Session Requested: %s\n", sid); + } + } - // @todo once session is established, associate it to the connection - - // @todo if anything fails, return HOOK_DISCONNECT, otherwise return HOOK_SUCCESS which will continue the rest of the session attaching process - // which is to grab the expected session off the connection and attach the connection to the connection list on the session, start the session thread if - // it hasn't already been started, and set the session state to CONNECT or ATTACH... discuss with tony, finalize session state machine regarding multiple - // connections attempting to attach at the same time to the session and changing the session state, may need to queue pending connections to the session - // and process them from within the session state machine thread + 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) { + ks_log(KS_LOG_DEBUG, "Session Located: %s\n", blade_session_id_get(bs)); + } + } - ks_sleep_ms(1000); // @todo temporary testing, remove this and return success once negotiations are done - return BLADE_CONNECTION_STATE_HOOK_BYPASS; + if (!bs) { + blade_session_create(&bs, bh); + ks_assert(bs); + + ks_log(KS_LOG_DEBUG, "Session Created: %s\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) { + blade_session_read_unlock(bs); + blade_session_destroy(&bs); + ret = BLADE_CONNECTION_STATE_HOOK_DISCONNECT; + goto done; + } + blade_handle_sessions_add(bs); + } + + 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) cJSON_Delete(json); + return ret; } blade_connection_state_hook_t blade_transport_wss_on_state_attach_outbound(blade_connection_t *bc, blade_connection_state_condition_t condition) @@ -961,6 +1030,7 @@ blade_connection_state_hook_t blade_transport_wss_on_state_detach(blade_connecti ks_log(KS_LOG_DEBUG, "State Callback: %d\n", (int32_t)condition); + ks_sleep(1000); return BLADE_CONNECTION_STATE_HOOK_SUCCESS; } @@ -970,6 +1040,7 @@ blade_connection_state_hook_t blade_transport_wss_on_state_ready(blade_connectio ks_log(KS_LOG_DEBUG, "State Callback: %d\n", (int32_t)condition); + ks_sleep(1000); return BLADE_CONNECTION_STATE_HOOK_SUCCESS; } diff --git a/libs/libblade/src/blade_session.c b/libs/libblade/src/blade_session.c index 950a510838..fbfd227254 100644 --- a/libs/libblade/src/blade_session.c +++ b/libs/libblade/src/blade_session.c @@ -42,6 +42,7 @@ struct blade_session_s { blade_session_state_t state; const char *id; + ks_rwl_t *lock; list_t connections; ks_q_t *sending; @@ -69,6 +70,9 @@ KS_DECLARE(ks_status_t) blade_session_create(blade_session_t **bsP, blade_handle 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); @@ -95,6 +99,8 @@ KS_DECLARE(ks_status_t) blade_session_destroy(blade_session_t **bsP) 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); @@ -138,9 +144,24 @@ KS_DECLARE(ks_status_t) blade_session_shutdown(blade_session_t *bs) 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); + 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(const char *) blade_session_id_get(blade_session_t *bs) { ks_assert(bs); @@ -157,6 +178,43 @@ KS_DECLARE(void) blade_session_id_set(blade_session_t *bs, const char *id) bs->id = ks_pstrdup(bs->pool, id); } +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(void) blade_session_state_set(blade_session_t *bs, blade_session_state_t state) { ks_assert(bs); @@ -172,6 +230,41 @@ KS_DECLARE(void) blade_session_hangup(blade_session_t *bs) blade_session_state_set(bs, BLADE_SESSION_STATE_HANGUP); } +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); + + 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)) { + list_delete_at(&bs->connections, i); + ks_pool_free(bs->pool, &cid); + break; + } + } + + return ret; +} + ks_status_t blade_session_connections_choose(blade_session_t *bs, cJSON *json, blade_connection_t **bcP) { blade_connection_t *bc = NULL; @@ -194,6 +287,7 @@ ks_status_t blade_session_connections_choose(blade_session_t *bs, cJSON *json, b // @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; @@ -272,6 +366,12 @@ void *blade_session_state_thread(ks_thread_t *thread, void *data) // @todo detach from session if this connection is attached blade_session_state_set(bs, BLADE_SESSION_STATE_DESTROY); break; + case BLADE_SESSION_STATE_CONNECT: + break; + case BLADE_SESSION_STATE_ATTACH: + break; + case BLADE_SESSION_STATE_DETACH: + break; case BLADE_SESSION_STATE_READY: // @todo pop from session receiving queue and pass to blade_protocol_process() break; diff --git a/libs/libblade/src/blade_stack.c b/libs/libblade/src/blade_stack.c index 6ec4af3a29..5977c091d7 100644 --- a/libs/libblade/src/blade_stack.c +++ b/libs/libblade/src/blade_stack.c @@ -419,6 +419,60 @@ 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) +{ + blade_session_t *bs = NULL; + + ks_assert(bs); + 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(ks_bool_t) blade_handle_datastore_available(blade_handle_t *bh) { diff --git a/libs/libblade/src/include/blade_connection.h b/libs/libblade/src/include/blade_connection.h index 5d149bc445..2242197861 100644 --- a/libs/libblade/src/include/blade_connection.h +++ b/libs/libblade/src/include/blade_connection.h @@ -57,6 +57,8 @@ 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 diff --git a/libs/libblade/src/include/blade_session.h b/libs/libblade/src/include/blade_session.h index 975d10bbfb..c6d376acd9 100644 --- a/libs/libblade/src/include/blade_session.h +++ b/libs/libblade/src/include/blade_session.h @@ -38,12 +38,19 @@ 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_sesssion_startup(blade_session_t *bs); +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(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(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(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_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); 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); diff --git a/libs/libblade/src/include/blade_stack.h b/libs/libblade/src/include/blade_stack.h index f6de9665ba..7b87fa84f3 100644 --- a/libs/libblade/src/include/blade_stack.h +++ b/libs/libblade/src/include/blade_stack.h @@ -51,10 +51,15 @@ 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_connect(blade_handle_t *bh, blade_connection_t **bcP, blade_identity_t *target); + 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(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, diff --git a/libs/libblade/src/include/blade_types.h b/libs/libblade/src/include/blade_types.h index 5531557dd6..2442d88a06 100644 --- a/libs/libblade/src/include/blade_types.h +++ b/libs/libblade/src/include/blade_types.h @@ -92,6 +92,7 @@ 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, From 14a99987bb0b9f41e363e7716facf4a278e1e330 Mon Sep 17 00:00:00 2001 From: Shane Bryldt Date: Thu, 23 Feb 2017 23:01:22 +0000 Subject: [PATCH 29/34] FS-9952: Preliminary session negotiations done, added a bunch of logging, fixed up cleanup code, needs more testing and more error handling --- libs/libblade/src/blade_connection.c | 51 ++- libs/libblade/src/blade_identity.c | 13 - libs/libblade/src/blade_module_wss.c | 355 ++++++++++++++----- libs/libblade/src/blade_session.c | 85 ++++- libs/libblade/src/blade_stack.c | 19 +- libs/libblade/src/include/blade_connection.h | 1 + libs/libblade/src/include/blade_session.h | 3 + libs/libblade/src/include/blade_stack.h | 2 +- libs/libblade/src/include/blade_types.h | 2 +- libs/libblade/test/bladec.c | 2 +- 10 files changed, 406 insertions(+), 127 deletions(-) diff --git a/libs/libblade/src/blade_connection.c b/libs/libblade/src/blade_connection.c index eadbea4adb..7d5cfadfe0 100644 --- a/libs/libblade/src/blade_connection.c +++ b/libs/libblade/src/blade_connection.c @@ -90,6 +90,8 @@ KS_DECLARE(ks_status_t) blade_connection_create(blade_connection_t **bcP, *bcP = bc; + ks_log(KS_LOG_DEBUG, "Created\n"); + return KS_STATUS_SUCCESS; } @@ -112,6 +114,8 @@ KS_DECLARE(ks_status_t) blade_connection_destroy(blade_connection_t **bcP) ks_pool_free(bc->pool, bcP); + ks_log(KS_LOG_DEBUG, "Destroyed\n"); + return KS_STATUS_SUCCESS; } @@ -133,6 +137,8 @@ KS_DECLARE(ks_status_t) blade_connection_startup(blade_connection_t *bc, blade_c return KS_STATUS_FAIL; } + ks_log(KS_LOG_DEBUG, "Started\n"); + return KS_STATUS_SUCCESS; } @@ -153,6 +159,8 @@ KS_DECLARE(ks_status_t) blade_connection_shutdown(blade_connection_t *bc) 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; } @@ -163,6 +171,13 @@ KS_DECLARE(blade_handle_t *) blade_connection_handle_get(blade_connection_t *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); @@ -285,8 +300,10 @@ 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) + 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) @@ -342,6 +359,9 @@ void *blade_connection_state_thread(ks_thread_t *thread, void *data) hook = BLADE_CONNECTION_STATE_HOOK_SUCCESS; callback = blade_connection_state_callback_lookup(bc, state); + if (state == BLADE_CONNECTION_STATE_DISCONNECT) { + blade_handle_connections_remove(bc); + } // @todo only READY state? if (state != BLADE_CONNECTION_STATE_DETACH && state != BLADE_CONNECTION_STATE_DISCONNECT) { while (blade_connection_sending_pop(bc, &json) == KS_STATUS_SUCCESS && json) { @@ -363,7 +383,9 @@ void *blade_connection_state_thread(ks_thread_t *thread, void *data) break; } if (!(done = (json == NULL))) { - // @todo push json to session receiving queue + blade_session_t *bs = blade_handle_sessions_get(bc->handle, bc->session); + ks_assert(bs); + blade_session_receiving_push(bs, json); cJSON_Delete(json); json = NULL; } @@ -379,7 +401,8 @@ void *blade_connection_state_thread(ks_thread_t *thread, void *data) else if (hook == BLADE_CONNECTION_STATE_HOOK_SUCCESS) { switch (state) { case BLADE_CONNECTION_STATE_DISCONNECT: - return NULL; + blade_connection_destroy(&bc); + break; case BLADE_CONNECTION_STATE_NEW: blade_connection_state_set(bc, BLADE_CONNECTION_STATE_CONNECT); break; @@ -388,24 +411,38 @@ void *blade_connection_state_thread(ks_thread_t *thread, void *data) break; case BLADE_CONNECTION_STATE_ATTACH: { + // @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); + 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 break; } case BLADE_CONNECTION_STATE_DETACH: - // @todo detach from session if this connection is attached - blade_connection_state_set(bc, BLADE_CONNECTION_STATE_DISCONNECT); - break; + { + 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); + break; + } default: break; } } + + if (state == BLADE_CONNECTION_STATE_DISCONNECT) break; } return NULL; diff --git a/libs/libblade/src/blade_identity.c b/libs/libblade/src/blade_identity.c index 040bc9727d..5c2cecafad 100644 --- a/libs/libblade/src/blade_identity.c +++ b/libs/libblade/src/blade_identity.c @@ -127,19 +127,6 @@ KS_DECLARE(ks_status_t) blade_identity_parse(blade_identity_t *bi, const char *u } } - // @todo remove this, temporary for testing - ks_log(KS_LOG_DEBUG, " name: %s\n", bi->name); - ks_log(KS_LOG_DEBUG, " domain: %s\n", bi->domain); - ks_log(KS_LOG_DEBUG, " resource: %s\n", bi->resource); - for (ks_hash_iterator_t *it = ks_hash_first(bi->parameters, KS_UNLOCKED); it; it = ks_hash_next(&it)) { - const char *key = NULL; - const char *val = NULL; - - ks_hash_this(it, (const void **)&key, NULL, (void **)&val); - - ks_log(KS_LOG_DEBUG, " key: %s = %s\n", key, val); - } - return KS_STATUS_SUCCESS; } diff --git a/libs/libblade/src/blade_module_wss.c b/libs/libblade/src/blade_module_wss.c index 429fdba344..31d2235e30 100644 --- a/libs/libblade/src/blade_module_wss.c +++ b/libs/libblade/src/blade_module_wss.c @@ -60,8 +60,7 @@ struct blade_module_wss_s { struct pollfd *listeners_poll; int32_t listeners_count; - list_t connected; - ks_q_t *disconnected; + 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 { @@ -77,6 +76,7 @@ struct blade_transport_wss_init_s { ks_pool_t *pool; ks_socket_t sock; + const char *session_id; }; @@ -98,7 +98,7 @@ 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); +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); @@ -116,7 +116,7 @@ blade_connection_state_hook_t blade_transport_wss_on_state_ready(blade_connectio -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); +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); @@ -172,11 +172,11 @@ ks_status_t blade_module_wss_create(blade_module_wss_t **bm_wssP, blade_handle_t bm_wss->transport_callbacks = &g_transport_wss_callbacks; list_init(&bm_wss->connected); - ks_q_create(&bm_wss->disconnected, bm_wss->pool, 0); - ks_assert(bm_wss->disconnected); *bm_wssP = bm_wss; + ks_log(KS_LOG_DEBUG, "Created\n"); + return KS_STATUS_SUCCESS; } @@ -194,10 +194,11 @@ ks_status_t blade_module_wss_destroy(blade_module_wss_t **bm_wssP) blade_module_destroy(&bm_wss->module); list_destroy(&bm_wss->connected); - ks_q_destroy(&bm_wss->disconnected); ks_pool_free(bm_wss->pool, bm_wssP); + ks_log(KS_LOG_DEBUG, "Destroyed\n"); + return KS_STATUS_SUCCESS; } @@ -213,6 +214,8 @@ KS_DECLARE(ks_status_t) blade_module_wss_on_load(blade_module_t **bmP, blade_han *bmP = bm_wss->module; + ks_log(KS_LOG_DEBUG, "Loaded\n"); + return KS_STATUS_SUCCESS; } @@ -226,10 +229,12 @@ KS_DECLARE(ks_status_t) blade_module_wss_on_unload(blade_module_t *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) +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; @@ -241,9 +246,12 @@ ks_status_t blade_transport_wss_init_create(blade_transport_wss_init_t **bt_wssi 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; } @@ -256,8 +264,12 @@ ks_status_t blade_transport_wss_init_destroy(blade_transport_wss_init_t **bt_wss 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; } @@ -365,6 +377,8 @@ ks_status_t blade_module_wss_config(blade_module_wss_t *bm_wss, config_setting_t 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; } @@ -405,13 +419,14 @@ KS_DECLARE(ks_status_t) blade_module_wss_on_startup(blade_module_t *bm, config_s blade_handle_transport_register(bm_wss->handle, bm, BLADE_MODULE_WSS_TRANSPORT_NAME, bm_wss->transport_callbacks); + 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_transport_wss_t *bt_wss = NULL; blade_connection_t *bc = NULL; ks_assert(bm); @@ -435,20 +450,18 @@ KS_DECLARE(ks_status_t) blade_module_wss_on_shutdown(blade_module_t *bm) bm_wss->listeners_count = 0; if (bm_wss->listeners_poll) ks_pool_free(bm_wss->pool, &bm_wss->listeners_poll); - // @todo connections should be gracefully disconnected so that they detach from sessions properly - // which means this should occur before the listeners thread is terminated, which requires that - // the listener sockets be made inactive (or closed) to stop accepting while shutting down - while (ks_q_trypop(bm_wss->disconnected, (void **)&bc) == KS_STATUS_SUCCESS) ; - list_iterator_start(&bm_wss->connected); - while (list_iterator_hasnext(&bm_wss->connected)) { - bc = (blade_connection_t *)list_iterator_next(&bm_wss->connected); - bt_wss = (blade_transport_wss_t *)blade_connection_transport_get(bc); - - blade_connection_destroy(&bc); - blade_transport_wss_destroy(&bt_wss); + 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); } - list_iterator_stop(&bm_wss->connected); - list_clear(&bm_wss->connected); + + ks_log(KS_LOG_DEBUG, "Stopped\n"); return KS_STATUS_SUCCESS; } @@ -492,6 +505,8 @@ ks_status_t blade_module_wss_listen(blade_module_wss_t *bm_wss, ks_sockaddr_t *a 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) { @@ -506,7 +521,6 @@ 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_transport_wss_t *bt_wss = NULL; blade_connection_t *bc = NULL; ks_assert(thread); @@ -523,18 +537,22 @@ void *blade_module_wss_listeners_thread(ks_thread_t *thread, void *data) 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; } - ks_log(KS_LOG_DEBUG, "Socket Accepted\n"); + // @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); + 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); @@ -543,11 +561,14 @@ void *blade_module_wss_listeners_thread(ks_thread_t *thread, void *data) 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); @@ -555,18 +576,6 @@ void *blade_module_wss_listeners_thread(ks_thread_t *thread, void *data) blade_connection_read_unlock(bc); } } - - while (ks_q_trypop(bm_wss->disconnected, (void **)&bc) == KS_STATUS_SUCCESS) { - bt_wss_init = (blade_transport_wss_init_t *)blade_connection_transport_init_get(bc); - bt_wss = (blade_transport_wss_t *)blade_connection_transport_get(bc); - - blade_handle_connections_remove(bc); - list_delete(&bm_wss->connected, bc); - - if (bt_wss_init) blade_transport_wss_init_destroy(&bt_wss_init); - blade_connection_destroy(&bc); - if (bt_wss) blade_transport_wss_destroy(&bt_wss); - } } ks_log(KS_LOG_DEBUG, "Stopped\n"); @@ -590,6 +599,8 @@ ks_status_t blade_transport_wss_create(blade_transport_wss_t **bt_wssP, blade_mo *bt_wssP = bt_wss; + ks_log(KS_LOG_DEBUG, "Created\n"); + return KS_STATUS_SUCCESS; } @@ -607,10 +618,12 @@ ks_status_t blade_transport_wss_destroy(blade_transport_wss_t **bt_wssP) 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) +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; @@ -640,6 +653,7 @@ ks_status_t blade_transport_wss_on_connect(blade_connection_t **bcP, blade_modul 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; } @@ -649,6 +663,7 @@ ks_status_t blade_transport_wss_on_connect(blade_connection_t **bcP, blade_modul ks_size_t len = strlen(ip); if (len <= 3) { + ks_log(KS_LOG_DEBUG, "Invalid host provided\n"); ret = KS_STATUS_FAIL; goto done; } @@ -660,36 +675,43 @@ ks_status_t blade_transport_wss_on_connect(blade_connection_t **bcP, blade_modul 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"); + ks_log(KS_LOG_DEBUG, "Socket connected\n"); - blade_transport_wss_init_create(&bt_wss_init, bm_wss, sock); + 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); - *bcP = bc; - + blade_connection_state_set(bc, BLADE_CONNECTION_STATE_NEW); + *bcP = bc; + done: return ret; } @@ -708,16 +730,18 @@ ks_status_t blade_transport_wss_write(blade_transport_wss_t *bt_wss, cJSON *json char *json_str = cJSON_PrintUnformatted(json); ks_size_t json_str_len = 0; if (!json_str) { - // @todo error logging + ks_log(KS_LOG_DEBUG, "Failed to generate json string\n"); ret = KS_STATUS_FAIL; goto done; } - json_str_len = strlen(json_str) + 1; // @todo determine if WSOC_TEXT null terminates when read_frame is called, or if it's safe to include like this + // @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) { - // @todo error logging + 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); @@ -733,13 +757,10 @@ ks_status_t blade_transport_wss_on_send(blade_connection_t *bc, cJSON *json) ks_assert(bc); ks_assert(json); - ks_log(KS_LOG_DEBUG, "Send Callback\n"); - bt_wss = (blade_transport_wss_t *)blade_connection_transport_get(bc); ret = blade_transport_wss_write(bt_wss, json); - // @todo use reference counting on blade_identity_t and cJSON objects cJSON_Delete(json); return ret; @@ -753,7 +774,7 @@ ks_status_t blade_transport_wss_read(blade_transport_wss_t *bt_wss, cJSON **json *json = NULL; if (poll_flags & KS_POLL_ERROR) { - // @todo error logging + ks_log(KS_LOG_DEBUG, "POLLERR\n"); return KS_STATUS_FAIL; } if (poll_flags & KS_POLL_READ) { @@ -768,10 +789,13 @@ ks_status_t blade_transport_wss_read(blade_transport_wss_t *bt_wss, cJSON **json // -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; } } @@ -785,8 +809,6 @@ ks_status_t blade_transport_wss_on_receive(blade_connection_t *bc, cJSON **json) ks_assert(bc); ks_assert(json); - ks_log(KS_LOG_DEBUG, "Receive Callback\n"); - bt_wss = (blade_transport_wss_t *)blade_connection_transport_get(bc); return blade_transport_wss_read(bt_wss, json); @@ -795,6 +817,7 @@ 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_transport_wss_t *bt_wss = NULL; + blade_transport_wss_init_t *bt_wss_init = NULL; ks_assert(bc); @@ -803,8 +826,12 @@ blade_connection_state_hook_t blade_transport_wss_on_state_disconnect(blade_conn 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); - ks_q_push(bt_wss->module->disconnected, 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; } @@ -865,7 +892,7 @@ blade_connection_state_hook_t blade_transport_wss_on_state_connect_inbound(blade // @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) { - // @todo error logging + ks_log(KS_LOG_DEBUG, "Failed websocket init\n"); return BLADE_CONNECTION_STATE_HOOK_DISCONNECT; } @@ -886,7 +913,7 @@ blade_connection_state_hook_t blade_transport_wss_on_state_connect_outbound(blad // @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) { - // @todo error logging + ks_log(KS_LOG_DEBUG, "Failed websocket init\n"); return BLADE_CONNECTION_STATE_HOOK_DISCONNECT; } @@ -897,13 +924,16 @@ blade_connection_state_hook_t blade_transport_wss_on_state_attach_inbound(blade_ { blade_connection_state_hook_t ret = BLADE_CONNECTION_STATE_HOOK_SUCCESS; blade_transport_wss_t *bt_wss = NULL; - cJSON *json = NULL; + cJSON *json_req = NULL; + cJSON *json_res = NULL; cJSON *params = NULL; + cJSON *result = NULL; + //cJSON *error = NULL; blade_session_t *bs = NULL; blade_handle_t *bh = NULL; const char *jsonrpc = NULL; - const char *method = NULL; const char *id = NULL; + const char *method = NULL; const char *sid = NULL; ks_time_t timeout; @@ -914,114 +944,258 @@ blade_connection_state_hook_t blade_transport_wss_on_state_attach_inbound(blade_ 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 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) == KS_STATUS_SUCCESS) { - if (json) break; - ks_sleep(250); + 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) { - // @todo error logging + if (!json_req) { + 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, "jsonrpc"); // @todo check for definitions of these keys and fixed values + jsonrpc = cJSON_GetObjectCstr(json_req, "jsonrpc"); // @todo check for definitions of these keys and fixed values if (!jsonrpc || strcmp(jsonrpc, "2.0")) { - // @todo error logging + ks_log(KS_LOG_DEBUG, "Received message is not the expected protocol\n"); + // @todo send error response before disconnecting, code = -32600 (invalid request) ret = BLADE_CONNECTION_STATE_HOOK_DISCONNECT; goto done; } - id = cJSON_GetObjectCstr(json, "id"); // @todo switch to number if we are not using a uuid for message id + id = cJSON_GetObjectCstr(json_req, "id"); // @todo switch to number if we are not using a uuid for message id if (!id) { - // @todo error logging + ks_log(KS_LOG_DEBUG, "Received message is missing 'id'\n"); + // @todo send error response before disconnecting, code = -32600 (invalid request) ret = BLADE_CONNECTION_STATE_HOOK_DISCONNECT; goto done; } - method = cJSON_GetObjectCstr(json, "method"); + method = cJSON_GetObjectCstr(json_req, "method"); if (!method || strcasecmp(method, "blade.session.attach")) { - // @todo error logging + ks_log(KS_LOG_DEBUG, "Received message is missing 'method' or is an unexpected method\n"); + // @todo send error response before disconnecting, code = -32601 (method not found) ret = BLADE_CONNECTION_STATE_HOOK_DISCONNECT; goto done; } - params = cJSON_GetObjectItem(json, "params"); + params = cJSON_GetObjectItem(json_req, "params"); if (params) { sid = cJSON_GetObjectCstr(params, "session-id"); if (sid) { - // @todo validate uuid format by parsing, not currently available in uuid functions - ks_log(KS_LOG_DEBUG, "Session Requested: %s\n", 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) { - ks_log(KS_LOG_DEBUG, "Session Located: %s\n", blade_session_id_get(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 Created: %s\n", blade_session_id_get(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)); + // @todo send error response before disconnecting, code = -32603 (internal error) 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 + json_res = cJSON_CreateObject(); + cJSON_AddStringToObject(json_res, "jsonrpc", "2.0"); + cJSON_AddStringToObject(json_res, "id", id); + + result = cJSON_CreateObject(); + cJSON_AddStringToObject(result, "session-id", blade_session_id_get(bs)); + cJSON_AddItemToObject(json_res, "result", result); + + // @todo send response + if (blade_transport_wss_write(bt_wss, json_res) != KS_STATUS_SUCCESS) { + ks_log(KS_LOG_DEBUG, "Failed to write 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) cJSON_Delete(json); + 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_res = NULL; + uuid_t msgid; + const char *mid = NULL; + ks_time_t timeout; + const char *jsonrpc = NULL; + const char *id = NULL; + cJSON *error = NULL; + cJSON *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); - // @todo produce jsonrpc compliant message to call method "blade.session.attach" + if (condition == BLADE_CONNECTION_STATE_CONDITION_PRE) return BLADE_CONNECTION_STATE_HOOK_SUCCESS; - // @todo add params with nested session-id and session-token if attempting to reconnect as a client, this should probably be passed in from - // the blade_handle_connect() call and then through the init parameters for the transport (do not directly use the old session, but copy the id and token) + 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); - // @todo block while sending message with blade_transport_wss_write(bt_wss, json) - // @todo block while receiving expected response with blade_transport_wss_read(bt_wss, json) + // @todo wrapper to build a request and response/error + json_req = cJSON_CreateObject(); + cJSON_AddStringToObject(json_req, "jsonrpc", "2.0"); + cJSON_AddStringToObject(json_req, "method", "blade.session.attach"); - // @todo check for error field, log and return HOOK_DISCONNECT if any errors occur + ks_uuid(&msgid); + mid = ks_uuid_str(pool, &msgid); + cJSON_AddStringToObject(json_req, "id", mid); - // @todo check for result field, and nested session-id and session-token + if (bt_wss_init->session_id) { + cJSON *params = cJSON_CreateObject(); + cJSON_AddStringToObject(params, "session-id", bt_wss_init->session_id); + cJSON_AddItemToObject(json_req, "params", params); + } - // @todo lookup the old session from the blade_handle_t, if it still exists then use this session - - // @todo if the old session does not exist, then create a new session and populate with the parameters from the results + ks_log(KS_LOG_DEBUG, "Session (%s) requested\n", (bt_wss_init->session_id ? bt_wss_init->session_id : "none")); - // @todo once session is established, associate it to the connection, see attach_inbound for notes regarding universal actions after returning SUCCESS + if (blade_transport_wss_write(bt_wss, json_req) != KS_STATUS_SUCCESS) { + ks_log(KS_LOG_DEBUG, "Failed to write message\n"); + ret = BLADE_CONNECTION_STATE_HOOK_DISCONNECT; + goto done; + } - ks_sleep_ms(1000); // @todo temporary testing, remove this and return success once negotiations are done - return BLADE_CONNECTION_STATE_HOOK_BYPASS; + + 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 is missing 'id'\n"); + ret = BLADE_CONNECTION_STATE_HOOK_DISCONNECT; + goto done; + } + + error = cJSON_GetObjectItem(json_res, "error"); + if (error) { + ks_log(KS_LOG_DEBUG, "Error message ... add the details\n"); + ret = BLADE_CONNECTION_STATE_HOOK_DISCONNECT; + goto done; + } + + result = cJSON_GetObjectItem(json_res, "result"); + if (!result) { + ks_log(KS_LOG_DEBUG, "Received message is missing 'result'\n"); + ret = BLADE_CONNECTION_STATE_HOOK_DISCONNECT; + goto done; + } + + sid = cJSON_GetObjectCstr(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 (mid) ks_pool_free(pool, &mid); + 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(blade_connection_t *bc, blade_connection_state_condition_t condition) @@ -1029,8 +1203,7 @@ blade_connection_state_hook_t blade_transport_wss_on_state_detach(blade_connecti ks_assert(bc); ks_log(KS_LOG_DEBUG, "State Callback: %d\n", (int32_t)condition); - - ks_sleep(1000); + return BLADE_CONNECTION_STATE_HOOK_SUCCESS; } @@ -1040,7 +1213,7 @@ blade_connection_state_hook_t blade_transport_wss_on_state_ready(blade_connectio ks_log(KS_LOG_DEBUG, "State Callback: %d\n", (int32_t)condition); - ks_sleep(1000); + ks_sleep_ms(1000); return BLADE_CONNECTION_STATE_HOOK_SUCCESS; } diff --git a/libs/libblade/src/blade_session.c b/libs/libblade/src/blade_session.c index fbfd227254..06f500a9d7 100644 --- a/libs/libblade/src/blade_session.c +++ b/libs/libblade/src/blade_session.c @@ -81,6 +81,8 @@ KS_DECLARE(ks_status_t) blade_session_create(blade_session_t **bsP, blade_handle *bsP = bs; + ks_log(KS_LOG_DEBUG, "Created\n"); + return KS_STATUS_SUCCESS; } @@ -105,6 +107,8 @@ KS_DECLARE(ks_status_t) blade_session_destroy(blade_session_t **bsP) ks_pool_free(bs->pool, bsP); + ks_log(KS_LOG_DEBUG, "Destroyed\n"); + return KS_STATUS_SUCCESS; } @@ -125,6 +129,8 @@ KS_DECLARE(ks_status_t) blade_session_startup(blade_session_t *bs) return KS_STATUS_FAIL; } + ks_log(KS_LOG_DEBUG, "Started\n"); + return KS_STATUS_SUCCESS; } @@ -152,6 +158,8 @@ KS_DECLARE(ks_status_t) blade_session_shutdown(blade_session_t *bs) list_iterator_stop(&bs->connections); list_clear(&bs->connections); + ks_log(KS_LOG_DEBUG, "Stopped\n"); + return KS_STATUS_SUCCESS; } @@ -226,8 +234,17 @@ 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) + 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) @@ -242,6 +259,8 @@ KS_DECLARE(ks_status_t) blade_session_connections_add(blade_session_t *bs, const list_append(&bs->connections, cid); + ks_log(KS_LOG_DEBUG, "Session (%s) connection added (%s)\n", bs->id, id); + return ret; } @@ -256,6 +275,7 @@ KS_DECLARE(ks_status_t) blade_session_connections_remove(blade_session_t *bs, co 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; @@ -278,7 +298,7 @@ ks_status_t blade_session_connections_choose(blade_session_t *bs, cJSON *json, b // later there will need to be a way to pick which connection to use cid = list_get_at(&bs->connections, 0); if (!cid) { - // @todo error logging... this shouldn't happen + // no connections available return KS_STATUS_FAIL; } @@ -310,6 +330,7 @@ KS_DECLARE(ks_status_t) blade_session_send(blade_session_t *bs, cJSON *json) 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; @@ -334,7 +355,25 @@ KS_DECLARE(ks_status_t) blade_session_sending_pop(blade_session_t *bs, cJSON **j return ks_q_trypop(bs->sending, (void **)json); } -// @todo receive queue push and pop +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) { @@ -354,26 +393,56 @@ void *blade_session_state_thread(ks_thread_t *thread, void *data) 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); + 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); } } switch (state) { case BLADE_SESSION_STATE_DESTROY: + ks_log(KS_LOG_DEBUG, "Session (%s) state destroy\n", bs->id); + blade_handle_sessions_remove(bs); + blade_session_destroy(&bs); return NULL; case BLADE_SESSION_STATE_HANGUP: - // @todo detach from session if this connection is attached - blade_session_state_set(bs, BLADE_SESSION_STATE_DESTROY); - break; + { + 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); + 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: - // @todo pop from session receiving queue and pass to blade_protocol_process() + ks_log(KS_LOG_DEBUG, "Session (%s) state ready\n", bs->id); + // @todo pop from session receiving queue and pass into protocol layer through something like blade_protocol_process() + ks_sleep_ms(1000); break; default: break; } diff --git a/libs/libblade/src/blade_stack.c b/libs/libblade/src/blade_stack.c index 5977c091d7..1a680a0ff5 100644 --- a/libs/libblade/src/blade_stack.c +++ b/libs/libblade/src/blade_stack.c @@ -244,8 +244,17 @@ KS_DECLARE(ks_status_t) blade_handle_shutdown(blade_handle_t *bh) blade_request_destroy(&value); } - // @todo terminate all sessions, which will disconnect all attached connections - + 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->requests, 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 disconnect remaining unattached connections // @todo unload DSOs @@ -312,7 +321,7 @@ KS_DECLARE(ks_status_t) blade_handle_transport_unregister(blade_handle_t *bh, co return KS_STATUS_SUCCESS; } -KS_DECLARE(ks_status_t) blade_handle_connect(blade_handle_t *bh, blade_connection_t **bcP, blade_identity_t *target) +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; @@ -358,7 +367,7 @@ KS_DECLARE(ks_status_t) blade_handle_connect(blade_handle_t *bh, blade_connectio // @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); + if (bhtr) ret = bhtr->callbacks->onconnect(bcP, bhtr->module, target, session_id); else ret = KS_STATUS_FAIL; return ret; @@ -423,7 +432,7 @@ KS_DECLARE(blade_session_t *) blade_handle_sessions_get(blade_handle_t *bh, cons { blade_session_t *bs = NULL; - ks_assert(bs); + ks_assert(bh); ks_assert(sid); ks_hash_read_lock(bh->sessions); diff --git a/libs/libblade/src/include/blade_connection.h b/libs/libblade/src/include/blade_connection.h index 2242197861..8b8660f9cb 100644 --- a/libs/libblade/src/include/blade_connection.h +++ b/libs/libblade/src/include/blade_connection.h @@ -44,6 +44,7 @@ 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); diff --git a/libs/libblade/src/include/blade_session.h b/libs/libblade/src/include/blade_session.h index c6d376acd9..f4e92775b3 100644 --- a/libs/libblade/src/include/blade_session.h +++ b/libs/libblade/src/include/blade_session.h @@ -49,11 +49,14 @@ KS_DECLARE(ks_status_t) blade_session_write_lock(blade_session_t *bs, ks_bool_t KS_DECLARE(ks_status_t) blade_session_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); 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 diff --git a/libs/libblade/src/include/blade_stack.h b/libs/libblade/src/include/blade_stack.h index 7b87fa84f3..dd1affb71d 100644 --- a/libs/libblade/src/include/blade_stack.h +++ b/libs/libblade/src/include/blade_stack.h @@ -50,7 +50,7 @@ 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_connect(blade_handle_t *bh, blade_connection_t **bcP, blade_identity_t *target); +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); diff --git a/libs/libblade/src/include/blade_types.h b/libs/libblade/src/include/blade_types.h index 2442d88a06..66e8ea0d01 100644 --- a/libs/libblade/src/include/blade_types.h +++ b/libs/libblade/src/include/blade_types.h @@ -113,7 +113,7 @@ struct blade_module_callbacks_s { }; -typedef ks_status_t (*blade_transport_connect_callback_t)(blade_connection_t **bcP, blade_module_t *bm, blade_identity_t *target); +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); diff --git a/libs/libblade/test/bladec.c b/libs/libblade/test/bladec.c index 510897971d..46bfda9969 100644 --- a/libs/libblade/test/bladec.c +++ b/libs/libblade/test/bladec.c @@ -253,7 +253,7 @@ void command_connect(blade_handle_t *bh, char *args) blade_identity_create(&target, blade_handle_pool_get(bh)); - if (blade_identity_parse(target, args) == KS_STATUS_SUCCESS) blade_handle_connect(bh, &bc, target); + if (blade_identity_parse(target, args) == KS_STATUS_SUCCESS) blade_handle_connect(bh, &bc, target, NULL); blade_identity_destroy(&target); } From 5d7e40c811e7c7899da9d6c3be60dda5fc104669 Mon Sep 17 00:00:00 2001 From: Shane Bryldt Date: Mon, 27 Feb 2017 18:39:40 +0000 Subject: [PATCH 30/34] FS-9952: Some code refactoring and added TTL for sessions, currently harcoded at 5 seconds for testing purposes only and should come from config --- libs/libblade/src/blade_connection.c | 284 +++++++++++++++++---------- libs/libblade/src/blade_module_wss.c | 82 ++++---- libs/libblade/src/blade_session.c | 194 ++++++++++++------ 3 files changed, 356 insertions(+), 204 deletions(-) diff --git a/libs/libblade/src/blade_connection.c b/libs/libblade/src/blade_connection.c index 7d5cfadfe0..6816d61db9 100644 --- a/libs/libblade/src/blade_connection.c +++ b/libs/libblade/src/blade_connection.c @@ -1,23 +1,23 @@ /* * 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 @@ -48,13 +48,19 @@ struct blade_connection_s { 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, @@ -84,7 +90,7 @@ KS_DECLARE(ks_status_t) blade_connection_create(blade_connection_t **bcP, ks_rwl_create(&bc->lock, pool); ks_assert(bc->lock); - + ks_q_create(&bc->sending, pool, 0); ks_assert(bc->sending); @@ -136,7 +142,7 @@ KS_DECLARE(ks_status_t) blade_connection_startup(blade_connection_t *bc, blade_c // @todo error logging return KS_STATUS_FAIL; } - + ks_log(KS_LOG_DEBUG, "Started\n"); return KS_STATUS_SUCCESS; @@ -246,7 +252,7 @@ KS_DECLARE(void) blade_connection_transport_set(blade_connection_t *bc, void *tr 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) { @@ -292,7 +298,7 @@ KS_DECLARE(void) blade_connection_state_set(blade_connection_t *bc, blade_connec if (callback) hook = callback(bc, BLADE_CONNECTION_STATE_CONDITION_PRE); bc->state = state; - + if (hook == BLADE_CONNECTION_STATE_HOOK_DISCONNECT) blade_connection_disconnect(bc); } @@ -309,7 +315,7 @@ KS_DECLARE(void) blade_connection_disconnect(blade_connection_t *bc) 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); @@ -344,9 +350,6 @@ void *blade_connection_state_thread(ks_thread_t *thread, void *data) { blade_connection_t *bc = NULL; blade_connection_state_t state; - blade_transport_state_callback_t callback = NULL; - blade_connection_state_hook_t hook = BLADE_CONNECTION_STATE_HOOK_SUCCESS; - cJSON *json = NULL; ks_assert(thread); ks_assert(data); @@ -354,92 +357,28 @@ void *blade_connection_state_thread(ks_thread_t *thread, void *data) bc = (blade_connection_t *)data; while (!bc->shutdown) { - state = bc->state; - hook = BLADE_CONNECTION_STATE_HOOK_SUCCESS; - callback = blade_connection_state_callback_lookup(bc, state); - if (state == BLADE_CONNECTION_STATE_DISCONNECT) { - blade_handle_connections_remove(bc); - } - // @todo only READY state? - if (state != BLADE_CONNECTION_STATE_DETACH && state != BLADE_CONNECTION_STATE_DISCONNECT) { - 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; - } - } - } - - if (state == BLADE_CONNECTION_STATE_READY) { - ks_bool_t done = KS_FALSE; - while (!done) { - if (bc->transport_callbacks->onreceive(bc, &json) != KS_STATUS_SUCCESS) { - blade_connection_disconnect(bc); - break; - } - if (!(done = (json == NULL))) { - blade_session_t *bs = blade_handle_sessions_get(bc->handle, bc->session); - ks_assert(bs); - blade_session_receiving_push(bs, json); - cJSON_Delete(json); - json = NULL; - } - } - } - - if (callback) hook = callback(bc, BLADE_CONNECTION_STATE_CONDITION_POST); - - if (hook == BLADE_CONNECTION_STATE_HOOK_DISCONNECT && (state == BLADE_CONNECTION_STATE_DETACH || state == BLADE_CONNECTION_STATE_DISCONNECT)) - hook = BLADE_CONNECTION_STATE_HOOK_SUCCESS; - - if (hook == BLADE_CONNECTION_STATE_HOOK_DISCONNECT) blade_connection_disconnect(bc); - else if (hook == BLADE_CONNECTION_STATE_HOOK_SUCCESS) { - switch (state) { - case BLADE_CONNECTION_STATE_DISCONNECT: - blade_connection_destroy(&bc); - break; - case BLADE_CONNECTION_STATE_NEW: - blade_connection_state_set(bc, BLADE_CONNECTION_STATE_CONNECT); - break; - case BLADE_CONNECTION_STATE_CONNECT: - blade_connection_state_set(bc, BLADE_CONNECTION_STATE_ATTACH); - break; - case BLADE_CONNECTION_STATE_ATTACH: - { - // @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 - break; - } - case BLADE_CONNECTION_STATE_DETACH: - { - 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); - break; - } - default: break; - } + 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; @@ -447,7 +386,152 @@ void *blade_connection_state_thread(ks_thread_t *thread, void *data) 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; + 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))) { + blade_session_t *bs = blade_handle_sessions_get(bc->handle, bc->session); + ks_assert(bs); + blade_session_receiving_push(bs, json); + cJSON_Delete(json); + json = NULL; + } + } + + 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); + + return KS_STATUS_SUCCESS; +} + + /* For Emacs: * Local Variables: * mode:c diff --git a/libs/libblade/src/blade_module_wss.c b/libs/libblade/src/blade_module_wss.c index 31d2235e30..ae5c5e1f96 100644 --- a/libs/libblade/src/blade_module_wss.c +++ b/libs/libblade/src/blade_module_wss.c @@ -1,23 +1,23 @@ /* * 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 @@ -135,7 +135,7 @@ static blade_transport_callbacks_t g_transport_wss_callbacks = 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, @@ -156,7 +156,7 @@ ks_status_t blade_module_wss_create(blade_module_wss_t **bm_wssP, blade_handle_t { blade_module_wss_t *bm_wss = NULL; ks_pool_t *pool = NULL; - + ks_assert(bm_wssP); ks_assert(bh); @@ -176,21 +176,21 @@ ks_status_t blade_module_wss_create(blade_module_wss_t **bm_wssP, blade_handle_t *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); @@ -213,7 +213,7 @@ KS_DECLARE(ks_status_t) blade_module_wss_on_load(blade_module_t **bmP, blade_han ks_assert(bm_wss); *bmP = bm_wss->module; - + ks_log(KS_LOG_DEBUG, "Loaded\n"); return KS_STATUS_SUCCESS; @@ -226,9 +226,9 @@ KS_DECLARE(ks_status_t) blade_module_wss_on_unload(blade_module_t *bm) 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; @@ -249,7 +249,7 @@ ks_status_t blade_transport_wss_init_create(blade_transport_wss_init_t **bt_wssi 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; @@ -258,7 +258,7 @@ ks_status_t blade_transport_wss_init_create(blade_transport_wss_init_t **bt_wssi 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); @@ -267,7 +267,7 @@ ks_status_t blade_transport_wss_init_destroy(blade_transport_wss_init_t **bt_wss 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; @@ -310,7 +310,7 @@ ks_status_t blade_module_wss_config(blade_module_wss_t *bm_wss, config_setting_t 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"); @@ -385,7 +385,7 @@ ks_status_t blade_module_wss_config(blade_module_wss_t *bm_wss, config_setting_t 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; - + ks_assert(bm); ks_assert(config); @@ -416,9 +416,9 @@ KS_DECLARE(ks_status_t) blade_module_wss_on_startup(blade_module_t *bm, config_s 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); - + ks_log(KS_LOG_DEBUG, "Started\n"); return KS_STATUS_SUCCESS; @@ -428,7 +428,7 @@ 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_assert(bm); bm_wss = (blade_module_wss_t *)blade_module_data_get(bm); @@ -460,7 +460,7 @@ KS_DECLARE(ks_status_t) blade_module_wss_on_shutdown(blade_module_t *bm) list_iterator_stop(&bm_wss->connected); while (list_size(&bm_wss->connected) > 0) ks_sleep_ms(100); } - + ks_log(KS_LOG_DEBUG, "Stopped\n"); return KS_STATUS_SUCCESS; @@ -471,7 +471,7 @@ ks_status_t blade_module_wss_listen(blade_module_wss_t *bm_wss, ks_sockaddr_t *a 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); @@ -549,7 +549,7 @@ void *blade_module_wss_listeners_thread(ks_thread_t *thread, void *data) } // @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); @@ -557,7 +557,7 @@ void *blade_module_wss_listeners_thread(ks_thread_t *thread, void *data) 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) { @@ -568,7 +568,7 @@ void *blade_module_wss_listeners_thread(ks_thread_t *thread, void *data) 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); @@ -598,7 +598,7 @@ ks_status_t blade_transport_wss_create(blade_transport_wss_t **bt_wssP, blade_mo bt_wss->sock = sock; *bt_wssP = bt_wss; - + ks_log(KS_LOG_DEBUG, "Created\n"); return KS_STATUS_SUCCESS; @@ -607,7 +607,7 @@ ks_status_t blade_transport_wss_create(blade_transport_wss_t **bt_wssP, blade_mo 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); @@ -615,9 +615,9 @@ ks_status_t blade_transport_wss_destroy(blade_transport_wss_t **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; @@ -641,7 +641,7 @@ ks_status_t blade_transport_wss_on_connect(blade_connection_t **bcP, blade_modul 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)); @@ -670,7 +670,7 @@ ks_status_t blade_transport_wss_on_connect(blade_connection_t **bcP, blade_modul 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; @@ -720,7 +720,7 @@ blade_connection_rank_t blade_transport_wss_on_rank(blade_connection_t *bc, blad { ks_assert(bc); ks_assert(target); - + return BLADE_CONNECTION_RANK_POOR; } @@ -780,7 +780,7 @@ ks_status_t blade_transport_wss_read(blade_transport_wss_t *bt_wss, cJSON **json if (poll_flags & KS_POLL_READ) { kws_opcode_t opcode; uint8_t *frame_data = NULL; - ks_size_t frame_data_len = kws_read_frame(bt_wss->kws, &opcode, &frame_data); + 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()) @@ -1033,7 +1033,7 @@ blade_connection_state_hook_t blade_transport_wss_on_state_attach_inbound(blade_ json_res = cJSON_CreateObject(); cJSON_AddStringToObject(json_res, "jsonrpc", "2.0"); cJSON_AddStringToObject(json_res, "id", id); - + result = cJSON_CreateObject(); cJSON_AddStringToObject(result, "session-id", blade_session_id_get(bs)); cJSON_AddItemToObject(json_res, "result", result); @@ -1046,7 +1046,7 @@ blade_connection_state_hook_t blade_transport_wss_on_state_attach_inbound(blade_ } 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 @@ -1103,7 +1103,7 @@ blade_connection_state_hook_t blade_transport_wss_on_state_attach_outbound(blade } 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 message\n"); ret = BLADE_CONNECTION_STATE_HOOK_DISCONNECT; @@ -1123,7 +1123,7 @@ blade_connection_state_hook_t blade_transport_wss_on_state_attach_outbound(blade 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")) { @@ -1159,7 +1159,7 @@ blade_connection_state_hook_t blade_transport_wss_on_state_attach_outbound(blade 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 @@ -1203,7 +1203,7 @@ blade_connection_state_hook_t blade_transport_wss_on_state_detach(blade_connecti ks_assert(bc); ks_log(KS_LOG_DEBUG, "State Callback: %d\n", (int32_t)condition); - + return BLADE_CONNECTION_STATE_HOOK_SUCCESS; } diff --git a/libs/libblade/src/blade_session.c b/libs/libblade/src/blade_session.c index 06f500a9d7..d5037bfcad 100644 --- a/libs/libblade/src/blade_session.c +++ b/libs/libblade/src/blade_session.c @@ -1,23 +1,23 @@ /* * 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 @@ -44,20 +44,24 @@ struct blade_session_s { const char *id; ks_rwl_t *lock; list_t connections; - + ks_time_t ttl; + ks_q_t *sending; ks_q_t *receiving; }; 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); @@ -72,7 +76,7 @@ KS_DECLARE(ks_status_t) blade_session_create(blade_session_t **bsP, blade_handle 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); @@ -128,7 +132,7 @@ KS_DECLARE(ks_status_t) blade_session_startup(blade_session_t *bs) // @todo error logging return KS_STATUS_FAIL; } - + ks_log(KS_LOG_DEBUG, "Started\n"); return KS_STATUS_SUCCESS; @@ -157,7 +161,7 @@ KS_DECLARE(ks_status_t) blade_session_shutdown(blade_session_t *bs) } list_iterator_stop(&bs->connections); list_clear(&bs->connections); - + ks_log(KS_LOG_DEBUG, "Stopped\n"); return KS_STATUS_SUCCESS; @@ -256,11 +260,13 @@ KS_DECLARE(ks_status_t) blade_session_connections_add(blade_session_t *bs, const 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; } @@ -282,6 +288,8 @@ KS_DECLARE(ks_status_t) blade_session_connections_remove(blade_session_t *bs, co } } + if (list_size(&bs->connections) == 0) bs->ttl = ks_time_now() + (5 * KS_USEC_PER_SEC); + return ret; } @@ -301,7 +309,7 @@ ks_status_t blade_session_connections_choose(blade_session_t *bs, cJSON *json, b // no connections available return KS_STATUS_FAIL; } - + bc = blade_handle_connections_get(bs->handle, cid); if (!bc) { // @todo error logging... this shouldn't happen @@ -314,28 +322,6 @@ ks_status_t blade_session_connections_choose(blade_session_t *bs, cJSON *json, b return KS_STATUS_SUCCESS; } -KS_DECLARE(ks_status_t) blade_session_send(blade_session_t *bs, cJSON *json) -{ - ks_assert(bs); - ks_assert(json); - - // @todo check json for "method", if this is an outgoing request then build up the data for a response to lookup the message id and get back to the request - // this can reuse blade_request_t so that when the blade_response_t is passed up the blade_request_t within it is familiar from inbound requests - - 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_DECLARE(ks_status_t) blade_session_sending_push(blade_session_t *bs, cJSON *json) { cJSON *json_copy = NULL; @@ -387,7 +373,7 @@ void *blade_session_state_thread(ks_thread_t *thread, void *data) bs = (blade_session_t *)data; while (!bs->shutdown) { - + state = bs->state; if (!list_empty(&bs->connections)) { @@ -403,30 +389,11 @@ void *blade_session_state_thread(ks_thread_t *thread, void *data) switch (state) { case BLADE_SESSION_STATE_DESTROY: - ks_log(KS_LOG_DEBUG, "Session (%s) state destroy\n", bs->id); - blade_handle_sessions_remove(bs); - blade_session_destroy(&bs); + blade_session_state_on_destroy(bs); return NULL; case BLADE_SESSION_STATE_HANGUP: - { - 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); - break; - } + 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); @@ -440,17 +407,118 @@ void *blade_session_state_thread(ks_thread_t *thread, void *data) ks_sleep_ms(1000); break; case BLADE_SESSION_STATE_READY: - ks_log(KS_LOG_DEBUG, "Session (%s) state ready\n", bs->id); - // @todo pop from session receiving queue and pass into protocol layer through something like blade_protocol_process() - ks_sleep_ms(1000); + 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(1000); + return KS_STATUS_SUCCESS; +} + + +KS_DECLARE(ks_status_t) blade_session_send(blade_session_t *bs, cJSON *json) +{ + ks_assert(bs); + ks_assert(json); + + // @todo check json for "method", if this is an outgoing request then build up the data for a response to lookup the message id and get back to the request + // this can reuse blade_request_t so that when the blade_response_t is passed up the blade_request_t within it is familiar from inbound requests + + 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) +{ + ks_status_t ret = KS_STATUS_SUCCESS; + + ks_assert(bs); + ks_assert(json); + + ks_log(KS_LOG_DEBUG, "Session (%s) processing\n", bs->id); + + // @todo teardown the message, convert into a blade_request_t or blade_response_t + + return ret; +} + /* For Emacs: * Local Variables: * mode:c From fd49aebb1d13054400a9c465b598b256bfb60744 Mon Sep 17 00:00:00 2001 From: Shane Bryldt Date: Tue, 28 Feb 2017 23:37:19 +0000 Subject: [PATCH 31/34] FS-9952: Intermediate commit for a fresh point to start retrofitting the jsonrpc code that is incomplete --- libs/libblade/src/blade_protocol.c | 69 ++++++++++++--- libs/libblade/src/blade_session.c | 62 +++++++++++++- libs/libblade/src/blade_stack.c | 97 ++++++++++++++++------ libs/libblade/src/include/blade_protocol.h | 16 ++-- libs/libblade/src/include/blade_stack.h | 20 +++-- libs/libblade/src/include/blade_types.h | 16 ++-- 6 files changed, 221 insertions(+), 59 deletions(-) diff --git a/libs/libblade/src/blade_protocol.c b/libs/libblade/src/blade_protocol.c index 4a3c071594..830be262f0 100644 --- a/libs/libblade/src/blade_protocol.c +++ b/libs/libblade/src/blade_protocol.c @@ -1,23 +1,23 @@ /* * 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 @@ -33,22 +33,26 @@ #include "blade.h" -KS_DECLARE(ks_status_t) blade_request_create(blade_request_t **breqP, ks_pool_t *pool, const char *session_id, cJSON *json /*, response_callback*/) +KS_DECLARE(ks_status_t) blade_request_create(blade_request_t **breqP, blade_handle_t *bh, const char *session_id, cJSON *json) { blade_request_t *breq = NULL; + ks_pool_t *pool = NULL; ks_assert(breqP); - ks_assert(pool); + 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->refs = 1; breq->session_id = ks_pstrdup(pool, session_id); breq->message = json; breq->message_id = cJSON_GetObjectCstr(json, "id"); - //breq->response_callback = response_callback; + *breqP = breq; return KS_STATUS_SUCCESS; @@ -72,6 +76,51 @@ 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) +{ + 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 = json; + + *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; +} + + /* For Emacs: * Local Variables: * mode:c diff --git a/libs/libblade/src/blade_session.c b/libs/libblade/src/blade_session.c index d5037bfcad..58c9b323d1 100644 --- a/libs/libblade/src/blade_session.c +++ b/libs/libblade/src/blade_session.c @@ -485,11 +485,20 @@ ks_status_t blade_session_state_on_ready(blade_session_t *bs) KS_DECLARE(ks_status_t) blade_session_send(blade_session_t *bs, cJSON *json) { + blade_request_t *request = NULL; + const char *method = NULL; + ks_assert(bs); ks_assert(json); - // @todo check json for "method", if this is an outgoing request then build up the data for a response to lookup the message id and get back to the request - // this can reuse blade_request_t so that when the blade_response_t is passed up the blade_request_t within it is familiar from inbound requests + method = cJSON_GetObjectCstr(json, "method"); + if (method) { + blade_request_create(&request, bs->handle, bs->id, json); + 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); + } 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 @@ -508,6 +517,11 @@ KS_DECLARE(ks_status_t) blade_session_send(blade_session_t *bs, cJSON *json) ks_status_t blade_session_process(blade_session_t *bs, cJSON *json) { ks_status_t ret = KS_STATUS_SUCCESS; + blade_request_t *breq = NULL; + blade_response_t *bres = NULL; + const char *jsonrpc = NULL; + const char *id = NULL; + const char *method = NULL; ks_assert(bs); ks_assert(json); @@ -515,6 +529,50 @@ ks_status_t blade_session_process(blade_session_t *bs, cJSON *json) ks_log(KS_LOG_DEBUG, "Session (%s) processing\n", bs->id); // @todo teardown the message, convert into a blade_request_t or blade_response_t + // @todo validate the jsonrpc fields + + 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; + } + + id = cJSON_GetObjectCstr(json, "id"); + if (!id) { + ks_log(KS_LOG_DEBUG, "Received 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) { + // @todo use method to find RPC callbacks + + blade_request_create(&breq, bs->handle, bs->id, json); + ks_assert(breq); + + // @todo call request callback handler + } else { + breq = blade_handle_requests_get(bs->handle, id); + if (!breq) { + // @todo hangup session entirely? + return KS_STATUS_FAIL; + } + blade_handle_requests_remove(breq); + + method = cJSON_GetObjectCstr(breq->message, "method"); + ks_assert(method); + + // @todo use method to find RPC callbacks + + blade_response_create(&bres, bs->handle, bs->id, breq, json); + ks_assert(bres); + + // @todo call response callback handler + } return ret; } diff --git a/libs/libblade/src/blade_stack.c b/libs/libblade/src/blade_stack.c index 1a680a0ff5..56357344ce 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,7 +43,7 @@ struct blade_handle_s { bhpvt_flag_t flags; ks_pool_t *pool; ks_thread_pool_t *tpool; - + config_setting_t *config_directory; config_setting_t *config_datastore; @@ -125,7 +125,7 @@ KS_DECLARE(ks_status_t) blade_handle_create(blade_handle_t **bhP, ks_pool_t *poo 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; @@ -133,7 +133,7 @@ KS_DECLARE(ks_status_t) blade_handle_create(blade_handle_t **bhP, ks_pool_t *poo 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->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); @@ -141,7 +141,7 @@ KS_DECLARE(ks_status_t) blade_handle_create(blade_handle_t **bhP, ks_pool_t *poo // @todo decide if this is uint32_t or uuid string, prefer uuid string to avoid needing another lock and variable for next id 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; return KS_STATUS_SUCCESS; @@ -185,21 +185,21 @@ ks_status_t blade_handle_config(blade_handle_t *bh, config_setting_t *config) { config_setting_t *directory = NULL; config_setting_t *datastore = NULL; - + ks_assert(bh); if (!config) return KS_STATUS_FAIL; if (!config_setting_is_group(config)) return KS_STATUS_FAIL; 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; } @@ -222,7 +222,7 @@ KS_DECLARE(ks_status_t) blade_handle_startup(blade_handle_t *bh, config_setting_ } // @todo load DSOs - + // @todo call onload and onstartup callbacks for modules from DSOs return KS_STATUS_SUCCESS; @@ -237,20 +237,20 @@ KS_DECLARE(ks_status_t) blade_handle_shutdown(blade_handle_t *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->requests, key); - + blade_session_hangup(value); } while (ks_hash_count(bh->sessions) > 0) ks_sleep_ms(100); @@ -280,7 +280,7 @@ KS_DECLARE(ks_status_t) blade_handle_transport_register(blade_handle_t *bh, blad { blade_handle_transport_registration_t *bhtr = NULL; blade_handle_transport_registration_t *bhtr_old = NULL; - + ks_assert(bh); ks_assert(bm); ks_assert(name); @@ -326,7 +326,7 @@ KS_DECLARE(ks_status_t) blade_handle_connect(blade_handle_t *bh, blade_connectio ks_status_t ret = KS_STATUS_SUCCESS; blade_handle_transport_registration_t *bhtr = NULL; const char *tname = NULL; - + ks_assert(bh); ks_assert(target); @@ -482,6 +482,55 @@ KS_DECLARE(ks_status_t) blade_handle_sessions_remove(blade_session_t *bs) } +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) { @@ -499,7 +548,7 @@ KS_DECLARE(ks_status_t) blade_handle_datastore_store(blade_handle_t *bh, const v 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); } @@ -513,9 +562,9 @@ KS_DECLARE(ks_status_t) blade_handle_datastore_fetch(blade_handle_t *bh, 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/include/blade_protocol.h b/libs/libblade/src/include/blade_protocol.h index fcd89cc01a..ddc4a3cdee 100644 --- a/libs/libblade/src/include/blade_protocol.h +++ b/libs/libblade/src/include/blade_protocol.h @@ -1,23 +1,23 @@ /* * 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 @@ -36,8 +36,10 @@ #include KS_BEGIN_EXTERN_C -KS_DECLARE(ks_status_t) blade_request_create(blade_request_t **breqP, ks_pool_t *pool, const char *session_id, cJSON *json /*, response_callback*/); +KS_DECLARE(ks_status_t) blade_request_create(blade_request_t **breqP, blade_handle_t *bh, const char *session_id, cJSON *json); 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_END_EXTERN_C #endif diff --git a/libs/libblade/src/include/blade_stack.h b/libs/libblade/src/include/blade_stack.h index dd1affb71d..ff86af388e 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 @@ -55,11 +55,15 @@ KS_DECLARE(ks_status_t) blade_handle_connect(blade_handle_t *bh, blade_connectio 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(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, diff --git a/libs/libblade/src/include/blade_types.h b/libs/libblade/src/include/blade_types.h index 66e8ea0d01..295217979d 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 @@ -141,8 +141,8 @@ struct blade_transport_callbacks_s { struct blade_request_s { + blade_handle_t *handle; ks_pool_t *pool; - uint32_t refs; const char *session_id; cJSON *message; @@ -152,8 +152,8 @@ struct blade_request_s { }; struct blade_response_s { + blade_handle_t *handle; ks_pool_t *pool; - uint32_t refs; const char *session_id; blade_request_t *request; From 8f569f715bf2ef957b336a26b633ba76fcb6998f Mon Sep 17 00:00:00 2001 From: Shane Bryldt Date: Tue, 7 Mar 2017 18:45:52 +0000 Subject: [PATCH 32/34] FS-9952: Big commit, first registered jsonrpc echo call successful, lots of cleanup remaining --- libs/libblade/Makefile.am | 19 +- libs/libblade/src/blade_connection.c | 9 +- libs/libblade/src/blade_method.c | 118 ++ libs/libblade/src/blade_module_wss.c | 201 ++- libs/libblade/src/blade_protocol.c | 93 +- libs/libblade/src/blade_rpcproto.c | 1178 ----------------- libs/libblade/src/blade_session.c | 78 +- libs/libblade/src/blade_space.c | 176 +++ libs/libblade/src/blade_stack.c | 94 +- libs/libblade/src/bpcp.c | 108 -- libs/libblade/src/include/blade.h | 15 +- .../src/include/{bpcp.h => blade_method.h} | 23 +- libs/libblade/src/include/blade_protocol.h | 9 +- libs/libblade/src/include/blade_rpcproto.h | 166 --- libs/libblade/src/include/blade_session.h | 14 +- .../src/include/blade_space.h} | 71 +- libs/libblade/src/include/blade_stack.h | 5 + libs/libblade/src/include/blade_types.h | 7 + libs/libblade/test/Makefile.am | 7 +- libs/libblade/test/testrpcproto.c | 724 ---------- libs/libks/Makefile.am | 12 +- libs/libks/src/include/ks.h | 13 +- libs/libks/src/ks_rpcmessage.c | 317 ----- libs/libks/test/Makefile.am | 10 +- libs/libks/test/testmessages.c | 124 -- 25 files changed, 778 insertions(+), 2813 deletions(-) create mode 100644 libs/libblade/src/blade_method.c delete mode 100644 libs/libblade/src/blade_rpcproto.c create mode 100644 libs/libblade/src/blade_space.c delete mode 100644 libs/libblade/src/bpcp.c rename libs/libblade/src/include/{bpcp.h => blade_method.h} (79%) delete mode 100644 libs/libblade/src/include/blade_rpcproto.h rename libs/{libks/src/include/ks_rpcmessage.h => libblade/src/include/blade_space.h} (52%) delete mode 100644 libs/libblade/test/testrpcproto.c delete mode 100644 libs/libks/src/ks_rpcmessage.c delete mode 100644 libs/libks/test/testmessages.c diff --git a/libs/libblade/Makefile.am b/libs/libblade/Makefile.am index 46e751405f..9346c3249a 100644 --- a/libs/libblade/Makefile.am +++ b/libs/libblade/Makefile.am @@ -11,23 +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/bpcp.c src/blade_datastore.c -libblade_la_SOURCES += src/blade_rpcproto.c -libblade_la_SOURCES += src/blade_identity.c src/blade_module.c src/blade_connection.c src/blade_module_wss.c -libblade_la_SOURCES += src/blade_session.c src/blade_protocol.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 libblade_la_CFLAGS = $(AM_CFLAGS) $(AM_CPPFLAGS) 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 -library_include_HEADERS += src/include/bpcp.h src/include/blade_datastore.h src/include/blade_rpcproto.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 +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 index 6816d61db9..f8cfaead54 100644 --- a/libs/libblade/src/blade_connection.c +++ b/libs/libblade/src/blade_connection.c @@ -495,6 +495,7 @@ 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); @@ -514,14 +515,18 @@ ks_status_t blade_connection_state_on_ready(blade_connection_t *bc) blade_connection_disconnect(bc); break; } + if (!(done = (json == NULL))) { - blade_session_t *bs = blade_handle_sessions_get(bc->handle, bc->session); - ks_assert(bs); + 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); 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_wss.c b/libs/libblade/src/blade_module_wss.c index ae5c5e1f96..dc1118cb95 100644 --- a/libs/libblade/src/blade_module_wss.c +++ b/libs/libblade/src/blade_module_wss.c @@ -111,8 +111,10 @@ blade_connection_state_hook_t blade_transport_wss_on_state_connect_inbound(blade 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(blade_connection_t *bc, blade_connection_state_condition_t condition); -blade_connection_state_hook_t blade_transport_wss_on_state_ready(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); @@ -120,6 +122,9 @@ ks_status_t blade_transport_wss_init_create(blade_transport_wss_init_t **bt_wssi ks_status_t blade_transport_wss_init_destroy(blade_transport_wss_init_t **bt_wssiP); +ks_bool_t blade_test_echo_request_handler(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 = { @@ -144,10 +149,10 @@ static blade_transport_callbacks_t g_transport_wss_callbacks = 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, - blade_transport_wss_on_state_detach, - blade_transport_wss_on_state_ready, - blade_transport_wss_on_state_ready, + 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, }; @@ -385,6 +390,8 @@ ks_status_t blade_module_wss_config(blade_module_wss_t *bm_wss, config_setting_t 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); @@ -419,6 +426,17 @@ KS_DECLARE(ks_status_t) blade_module_wss_on_startup(blade_module_t *bm, config_s blade_handle_transport_register(bm_wss->handle, bm, BLADE_MODULE_WSS_TRANSPORT_NAME, bm_wss->transport_callbacks); + + blade_space_create(&space, bm_wss->handle, "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; @@ -428,6 +446,7 @@ 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); @@ -440,6 +459,7 @@ KS_DECLARE(ks_status_t) blade_module_wss_on_shutdown(blade_module_t *bm) 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) { @@ -461,7 +481,7 @@ KS_DECLARE(ks_status_t) blade_module_wss_on_shutdown(blade_module_t *bm) while (list_size(&bm_wss->connected) > 0) ks_sleep_ms(100); } - ks_log(KS_LOG_DEBUG, "Stopped\n"); + if (stopped) ks_log(KS_LOG_DEBUG, "Stopped\n"); return KS_STATUS_SUCCESS; } @@ -751,7 +771,6 @@ ks_status_t blade_transport_wss_write(blade_transport_wss_t *bt_wss, cJSON *json ks_status_t blade_transport_wss_on_send(blade_connection_t *bc, cJSON *json) { - ks_status_t ret = KS_STATUS_SUCCESS; blade_transport_wss_t *bt_wss = NULL; ks_assert(bc); @@ -759,11 +778,7 @@ ks_status_t blade_transport_wss_on_send(blade_connection_t *bc, cJSON *json) bt_wss = (blade_transport_wss_t *)blade_connection_transport_get(bc); - ret = blade_transport_wss_write(bt_wss, json); - - cJSON_Delete(json); - - return ret; + return blade_transport_wss_write(bt_wss, json); } ks_status_t blade_transport_wss_read(blade_transport_wss_t *bt_wss, cJSON **json) @@ -814,6 +829,29 @@ ks_status_t blade_transport_wss_on_receive(blade_connection_t *bc, cJSON **json) 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; @@ -924,10 +962,11 @@ blade_connection_state_hook_t blade_transport_wss_on_state_attach_inbound(blade_ { 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 *params = NULL; - cJSON *result = NULL; + cJSON *json_params = NULL; + cJSON *json_result = NULL; //cJSON *error = NULL; blade_session_t *bs = NULL; blade_handle_t *bh = NULL; @@ -948,6 +987,8 @@ blade_connection_state_hook_t blade_transport_wss_on_state_attach_inbound(blade_ 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) { @@ -958,6 +999,7 @@ blade_connection_state_hook_t blade_transport_wss_on_state_attach_inbound(blade_ 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; } @@ -966,7 +1008,7 @@ blade_connection_state_hook_t blade_transport_wss_on_state_attach_inbound(blade_ 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"); - // @todo send error response before disconnecting, code = -32600 (invalid request) + blade_transport_wss_rpc_error_send(bc, NULL, -32600, "Invalid request, missing 'jsonrpc' field"); ret = BLADE_CONNECTION_STATE_HOOK_DISCONNECT; goto done; } @@ -974,7 +1016,7 @@ blade_connection_state_hook_t blade_transport_wss_on_state_attach_inbound(blade_ 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"); - // @todo send error response before disconnecting, code = -32600 (invalid request) + blade_transport_wss_rpc_error_send(bc, NULL, -32600, "Invalid request, missing 'id' field"); ret = BLADE_CONNECTION_STATE_HOOK_DISCONNECT; goto done; } @@ -982,14 +1024,14 @@ blade_connection_state_hook_t blade_transport_wss_on_state_attach_inbound(blade_ 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"); - // @todo send error response before disconnecting, code = -32601 (method not found) + blade_transport_wss_rpc_error_send(bc, id, -32601, "Missing or unexpected 'method' field"); ret = BLADE_CONNECTION_STATE_HOOK_DISCONNECT; goto done; } - params = cJSON_GetObjectItem(json_req, "params"); - if (params) { - sid = cJSON_GetObjectCstr(params, "session-id"); + 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); @@ -1019,7 +1061,7 @@ blade_connection_state_hook_t blade_transport_wss_on_state_attach_inbound(blade_ if (blade_session_startup(bs) != KS_STATUS_SUCCESS) { ks_log(KS_LOG_DEBUG, "Session (%s) startup failed\n", blade_session_id_get(bs)); - // @todo send error response before disconnecting, code = -32603 (internal error) + 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; @@ -1030,17 +1072,14 @@ blade_connection_state_hook_t blade_transport_wss_on_state_attach_inbound(blade_ } // @todo wrapper to generate request and response - json_res = cJSON_CreateObject(); - cJSON_AddStringToObject(json_res, "jsonrpc", "2.0"); - cJSON_AddStringToObject(json_res, "id", id); + blade_rpc_response_create(pool, &json_res, &json_result, id); + ks_assert(json_res); - result = cJSON_CreateObject(); - cJSON_AddStringToObject(result, "session-id", blade_session_id_get(bs)); - cJSON_AddItemToObject(json_res, "result", result); + 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 message\n"); + ks_log(KS_LOG_DEBUG, "Failed to write response message\n"); ret = BLADE_CONNECTION_STATE_HOOK_DISCONNECT; goto done; } @@ -1064,14 +1103,14 @@ blade_connection_state_hook_t blade_transport_wss_on_state_attach_outbound(blade 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; - uuid_t msgid; const char *mid = NULL; ks_time_t timeout; const char *jsonrpc = NULL; const char *id = NULL; - cJSON *error = NULL; - cJSON *result = NULL; + cJSON *json_error = NULL; + cJSON *json_result = NULL; const char *sid = NULL; blade_session_t *bs = NULL; @@ -1087,25 +1126,15 @@ blade_connection_state_hook_t blade_transport_wss_on_state_attach_outbound(blade pool = blade_connection_pool_get(bc); - // @todo wrapper to build a request and response/error - json_req = cJSON_CreateObject(); - cJSON_AddStringToObject(json_req, "jsonrpc", "2.0"); - cJSON_AddStringToObject(json_req, "method", "blade.session.attach"); + blade_rpc_request_create(pool, &json_req, &json_params, &mid, "blade.session.attach"); + ks_assert(json_req); - ks_uuid(&msgid); - mid = ks_uuid_str(pool, &msgid); - cJSON_AddStringToObject(json_req, "id", mid); - - if (bt_wss_init->session_id) { - cJSON *params = cJSON_CreateObject(); - cJSON_AddStringToObject(params, "session-id", bt_wss_init->session_id); - cJSON_AddItemToObject(json_req, "params", params); - } + 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 message\n"); + ks_log(KS_LOG_DEBUG, "Failed to write request message\n"); ret = BLADE_CONNECTION_STATE_HOOK_DISCONNECT; goto done; } @@ -1134,26 +1163,26 @@ blade_connection_state_hook_t blade_transport_wss_on_state_attach_outbound(blade 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 is missing 'id'\n"); + ks_log(KS_LOG_DEBUG, "Received message has missing or unexpected 'id'\n"); ret = BLADE_CONNECTION_STATE_HOOK_DISCONNECT; goto done; } - error = cJSON_GetObjectItem(json_res, "error"); - if (error) { + 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; } - result = cJSON_GetObjectItem(json_res, "result"); - if (!result) { + 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(result, "session-id"); + 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; @@ -1192,13 +1221,12 @@ blade_connection_state_hook_t blade_transport_wss_on_state_attach_outbound(blade blade_connection_session_set(bc, blade_session_id_get(bs)); done: - if (mid) ks_pool_free(pool, &mid); 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(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) { ks_assert(bc); @@ -1207,7 +1235,16 @@ blade_connection_state_hook_t blade_transport_wss_on_state_detach(blade_connecti return BLADE_CONNECTION_STATE_HOOK_SUCCESS; } -blade_connection_state_hook_t blade_transport_wss_on_state_ready(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) +{ + 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); @@ -1217,6 +1254,60 @@ blade_connection_state_hook_t blade_transport_wss_on_state_ready(blade_connectio 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); + + ks_log(KS_LOG_DEBUG, "State Callback: %d\n", (int32_t)condition); + + if (condition == BLADE_CONNECTION_STATE_CONDITION_PRE) { + blade_session_t *bs = NULL; + cJSON *req = NULL; + + 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); + } + + ks_sleep_ms(1000); + return BLADE_CONNECTION_STATE_HOOK_SUCCESS; +} + + + +ks_bool_t blade_test_echo_request_handler(blade_request_t *breq) +{ + blade_session_t *bs = NULL; + cJSON *res = NULL; + + 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 diff --git a/libs/libblade/src/blade_protocol.c b/libs/libblade/src/blade_protocol.c index 830be262f0..092c31f237 100644 --- a/libs/libblade/src/blade_protocol.c +++ b/libs/libblade/src/blade_protocol.c @@ -33,7 +33,11 @@ #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) +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; @@ -50,8 +54,9 @@ KS_DECLARE(ks_status_t) blade_request_create(blade_request_t **breqP, blade_hand breq->handle = bh; breq->pool = pool; breq->session_id = ks_pstrdup(pool, session_id); - breq->message = json; + breq->message = cJSON_Duplicate(json, 1); breq->message_id = cJSON_GetObjectCstr(json, "id"); + breq->callback = callback; *breqP = breq; @@ -95,7 +100,7 @@ KS_DECLARE(ks_status_t) blade_response_create(blade_response_t **bresP, blade_ha bres->pool = pool; bres->session_id = ks_pstrdup(pool, session_id); bres->request = breq; - bres->message = json; + bres->message = cJSON_Duplicate(json, 1); *bresP = bres; @@ -120,6 +125,88 @@ KS_DECLARE(ks_status_t) blade_response_destroy(blade_response_t **bresP) 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; +} /* For Emacs: * Local Variables: diff --git a/libs/libblade/src/blade_rpcproto.c b/libs/libblade/src/blade_rpcproto.c deleted file mode 100644 index bedfd1c505..0000000000 --- a/libs/libblade/src/blade_rpcproto.c +++ /dev/null @@ -1,1178 +0,0 @@ -/* - * 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. - */ - -#pragma GCC optimize ("O0") - -#include - -/* - * internal shared structure grounded in global - */ -typedef struct blade_rpc_handle_ex { - - ks_hash_t *namespace_hash; /* hash to namespace methods */ - ks_hash_t *template_hash; /* hash to template methods */ - - ks_hash_t *peer_hash; /* hash to peer structure */ - - ks_q_t *event_queue; - ks_bool_t isactive; - ks_pool_t *pool; - -} blade_rpc_handle_ex_t; - - -typedef struct blade_rpc_namespace_s { - - char name[KS_RPCMESSAGE_NAMESPACE_LENGTH+1]; - char version[KS_RPCMESSAGE_VERSION_LENGTH+1]; /* nnn.nn.nn */ - ks_hash_t *method_hash; /* hash to namespace methods */ - -} blade_rpc_namespace_t; - - - - -blade_rpc_handle_ex_t *g_handle = NULL; - - -/* - * callbacks - from blade_rpc_handle_ex->method_hash - */ - -typedef struct blade_rpc_custom_callbackpair_s -{ - jrpc_func_t prefix_request_func; - jrpc_func_t postfix_request_func; - - jrpc_func_t prefix_response_func; - jrpc_func_t postfix_response_func; - -} blade_rpc_custom_callbackpair_t; - - - -typedef struct blade_rpc_callbackpair_s -{ - jrpc_func_t request_func; - - jrpc_func_t response_func; - - blade_rpc_custom_callbackpair_t* custom; - - ks_mutex_t *lock; - - uint16_t command_length; - char command[1]; - -} blade_rpc_callbackpair_t; - - - - - -static void blade_rpc_make_fqcommand(const char* namespace, const char *command, char *fscommand) -{ - memset(fscommand, 0, KS_RPCMESSAGE_FQCOMMAND_LENGTH); - sprintf(fscommand, "%s.%s", namespace, command); - - return; -} - -static void blade_rpc_parse_fqcommand(const char* fscommand, char *namespace, char *command) -{ - memset(command, 0, KS_RPCMESSAGE_COMMAND_LENGTH); - memset(namespace, 0, KS_RPCMESSAGE_NAMESPACE_LENGTH); - - uint32_t len = strlen(fscommand); - - assert(len <= KS_RPCMESSAGE_FQCOMMAND_LENGTH); - ks_bool_t dotfound = KS_FALSE; - - for(int i=0, x=0; inamespace_hash, - KS_HASH_MODE_CASE_SENSITIVE, - KS_HASH_FLAG_RWLOCK + KS_HASH_FLAG_DUP_CHECK, // + KS_HASH_FLAG_FREE_VALUE, - pool); - - ks_hash_create(&g_handle->template_hash, - KS_HASH_MODE_CASE_SENSITIVE, - KS_HASH_FLAG_RWLOCK + KS_HASH_FLAG_DUP_CHECK, // + KS_HASH_FLAG_FREE_VALUE, - pool); - - ks_q_create(&g_handle->event_queue, pool, 1024); - - g_handle->pool = pool; - - /* initialize rpc messaging mechanism */ - ks_rpcmessage_init(pool); - - g_handle->isactive = KS_TRUE; - } - return KS_STATUS_SUCCESS; -} - - -//KS_DECLARE(ks_status_t) blade_rpc_onconnect(ks_pool_t *pool, blade_peer_t* peer) -//{ -// -// -// return KS_STATUS_FAIL; -//} -// -//KS_DECLARE(ks_status_t) blade_rpc_disconnect(blade_peer_t* peer) -//{ -// -// return KS_STATUS_FAIL; -//} - - - -/* - * namespace setup - */ - -/* - * function/callback functions - */ -static blade_rpc_callbackpair_t *blade_rpc_find_callbacks_locked(char *namespace, char *command) -{ - blade_rpc_callbackpair_t *callbacks = NULL; - - blade_rpc_namespace_t *n = ks_hash_search(g_handle->namespace_hash, namespace, KS_UNLOCKED); - if (n) { - - ks_hash_read_lock(n->method_hash); - - callbacks = ks_hash_search(n->method_hash, command, KS_UNLOCKED); - - if (callbacks) { - ks_mutex_lock(callbacks->lock); - } - - ks_hash_read_unlock(n->method_hash); - } - - return callbacks; -} - -static blade_rpc_callbackpair_t *blade_rpc_find_template_locked(char *name, char *command) -{ - blade_rpc_callbackpair_t *callbacks = NULL; - - blade_rpc_namespace_t *n = ks_hash_search(g_handle->template_hash, name, KS_UNLOCKED); - if (n) { - - ks_hash_read_lock(n->method_hash); - callbacks = ks_hash_search(n->method_hash, command, KS_UNLOCKED); - - if (callbacks) { - ks_mutex_lock(callbacks->lock); - } - - ks_hash_read_unlock(n->method_hash); - } - - return callbacks; -} - - - - -static blade_rpc_callbackpair_t *blade_rpc_find_callbacks_locked_fq(char *fqcommand) -{ - blade_rpc_callbackpair_t *callbacks = NULL; - - char command[KS_RPCMESSAGE_COMMAND_LENGTH+1]; - char namespace[KS_RPCMESSAGE_NAMESPACE_LENGTH+1]; - - blade_rpc_parse_fqcommand(fqcommand, namespace, command); - - blade_rpc_namespace_t *n = ks_hash_search(g_handle->namespace_hash, namespace, KS_UNLOCKED); - if (n) { - blade_rpc_make_fqcommand(namespace, command, fqcommand); - ks_hash_read_lock(n->method_hash); - - callbacks = ks_hash_search(n->method_hash, fqcommand, KS_UNLOCKED); - - if (callbacks) { - ks_mutex_lock(callbacks->lock); - } - - ks_hash_read_unlock(n->method_hash); - } - - return callbacks; -} - - -KS_DECLARE(jrpc_func_t) blade_rpc_find_request_function(char *fqcommand) -{ - - blade_rpc_callbackpair_t* callbacks = blade_rpc_find_callbacks_locked_fq(fqcommand); - - if (!callbacks) { - return NULL; - } - - jrpc_func_t f = callbacks->request_func; - - ks_mutex_unlock(callbacks->lock); - - return f; -} - -KS_DECLARE(jrpc_func_t) blade_rpc_find_requestprefix_function(char *fqcommand) -{ - - blade_rpc_callbackpair_t* callbacks = blade_rpc_find_callbacks_locked_fq(fqcommand); - - if (!callbacks || !callbacks->custom) { - return NULL; - } - - jrpc_func_t f = callbacks->custom->prefix_request_func; - - ks_mutex_unlock(callbacks->lock); - - return f; -} - -KS_DECLARE(jrpc_func_t) blade_rpc_find_response_function(char *fqcommand) -{ - - blade_rpc_callbackpair_t* callbacks = blade_rpc_find_callbacks_locked_fq(fqcommand); - - if (!callbacks) { - return NULL; - } - - jrpc_func_t f = callbacks->response_func; - - ks_mutex_unlock(callbacks->lock); - - return f; -} - -KS_DECLARE(jrpc_func_t) blade_rpc_find_responseprefix_function(char *fqcommand) -{ - - blade_rpc_callbackpair_t* callbacks = blade_rpc_find_callbacks_locked_fq(fqcommand); - - if (!callbacks || !callbacks->custom) { - return NULL; - } - - jrpc_func_t f = callbacks->custom->prefix_response_func; - - ks_mutex_unlock(callbacks->lock); - - return f; -} - - -KS_DECLARE(ks_status_t) blade_rpc_declare_namespace(char* namespace, const char* version) -{ - - /* find/insert to namespace hash as needed */ - ks_hash_write_lock(g_handle->namespace_hash); - blade_rpc_namespace_t *n = ks_hash_search(g_handle->namespace_hash, namespace, KS_UNLOCKED); - if (n == NULL) { - n = ks_pool_alloc(g_handle->pool, sizeof (blade_rpc_namespace_t) + strlen(namespace) + 1); - strncpy(n->name, namespace, KS_RPCMESSAGE_NAMESPACE_LENGTH); - strncpy(n->version, version, KS_RPCMESSAGE_VERSION_LENGTH); - ks_hash_create(&n->method_hash, - KS_HASH_MODE_CASE_SENSITIVE, - KS_HASH_FLAG_RWLOCK + KS_HASH_FLAG_DUP_CHECK + KS_HASH_FLAG_FREE_VALUE, - g_handle->pool); - ks_hash_insert(g_handle->namespace_hash, n->name, n); - } - ks_hash_write_unlock(g_handle->namespace_hash); - - ks_log(KS_LOG_DEBUG, "Setting message namespace value %s, version %s\n", namespace, version); - - return KS_STATUS_SUCCESS; -} - - -KS_DECLARE(ks_status_t)blade_rpc_register_function(char *namespace, - char *command, - jrpc_func_t func, - jrpc_func_t respfunc) -{ - if (!func && !respfunc) { - return KS_STATUS_FAIL; - } - - char nskey[KS_RPCMESSAGE_NAMESPACE_LENGTH+1]; - memset(nskey, 0, sizeof(nskey)); - strcpy(nskey, namespace); - - char fqcommand[KS_RPCMESSAGE_FQCOMMAND_LENGTH]; - memset(fqcommand, 0, sizeof(fqcommand)); - -// strcpy(fqcommand, namespace); -// strcpy(fqcommand, "."); - strcat(fqcommand, command); - - int lkey = strlen(command)+1; - - if (lkey < 16) { - lkey = 16; - } - - ks_hash_read_lock(g_handle->namespace_hash); /* lock namespace hash */ - - blade_rpc_namespace_t *n = ks_hash_search(g_handle->namespace_hash, nskey, KS_UNLOCKED); - - if (n == NULL) { - ks_hash_read_unlock(g_handle->namespace_hash); - ks_log(KS_LOG_ERROR, "Unable to find %s namespace\n", namespace); - return KS_STATUS_FAIL; - } - - blade_rpc_callbackpair_t* callbacks = blade_rpc_find_callbacks_locked(nskey, command); - - /* just ignore attempt to re register callbacks */ - /* @todo : should this be smarter, allow override ? */ - if (callbacks != NULL) { - ks_mutex_unlock(callbacks->lock); - ks_hash_read_unlock(g_handle->namespace_hash); - ks_log(KS_LOG_ERROR, "Callbacks already registered for %s namespace\n", namespace); - return KS_STATUS_FAIL; - } - - callbacks = - (blade_rpc_callbackpair_t*)ks_pool_alloc(g_handle->pool, lkey + sizeof(blade_rpc_callbackpair_t)); - - strcpy(callbacks->command, command); - callbacks->command_length = lkey; - callbacks->request_func = func; - callbacks->response_func = respfunc; - ks_mutex_create(&callbacks->lock, KS_MUTEX_FLAG_DEFAULT, g_handle->pool); - - ks_hash_write_lock(n->method_hash); /* lock method hash */ - - ks_hash_insert(n->method_hash, callbacks->command, (void *) callbacks); - - ks_hash_write_unlock(n->method_hash); /* unlock method hash */ - ks_hash_read_unlock(g_handle->namespace_hash); /* unlock namespace hash */ - - ks_log(KS_LOG_DEBUG, "Message %s %s registered\n", namespace, command); - - return KS_STATUS_SUCCESS; - -} - - -KS_DECLARE(ks_status_t)blade_rpc_register_custom_request_function(char *namespace, - char *command, - jrpc_func_t prefix_func, - jrpc_func_t postfix_func) -{ - ks_status_t s = KS_STATUS_FAIL; - - char fqcommand[KS_RPCMESSAGE_FQCOMMAND_LENGTH]; - memset(fqcommand, 0, sizeof(fqcommand)); - strcat(fqcommand, command); - - ks_hash_write_lock(g_handle->namespace_hash); - blade_rpc_callbackpair_t* callbacks = blade_rpc_find_callbacks_locked(namespace, fqcommand); - - if (callbacks) { - - if (!callbacks->custom) { - callbacks->custom = - (blade_rpc_custom_callbackpair_t *)ks_pool_alloc(g_handle->pool, sizeof(blade_rpc_custom_callbackpair_t)); - } - - callbacks->custom->prefix_request_func = prefix_func; - callbacks->custom->postfix_request_func = postfix_func; - ks_mutex_unlock(callbacks->lock); - s = KS_STATUS_SUCCESS; - } - - ks_hash_write_unlock(g_handle->namespace_hash); - return s; -} - -KS_DECLARE(ks_status_t)blade_rpc_register_custom_response_function(char* namespace, - char *command, - jrpc_func_t prefix_func, - jrpc_func_t postfix_func) -{ - ks_status_t s = KS_STATUS_FAIL; - - char fqcommand[KS_RPCMESSAGE_FQCOMMAND_LENGTH]; - memset(fqcommand, 0, sizeof(fqcommand)); - strcat(fqcommand, command); - - ks_hash_write_lock(g_handle->namespace_hash); - blade_rpc_callbackpair_t *callbacks = blade_rpc_find_callbacks_locked(namespace, fqcommand); - - if (callbacks) { - - if (!callbacks->custom) { - callbacks->custom = - (blade_rpc_custom_callbackpair_t *)ks_pool_alloc(g_handle->pool, sizeof(blade_rpc_custom_callbackpair_t)); - } - - callbacks->custom->prefix_response_func = prefix_func; - callbacks->custom->postfix_response_func = postfix_func; - ks_mutex_unlock(callbacks->lock); - s = KS_STATUS_SUCCESS; - } - - ks_hash_write_unlock(g_handle->namespace_hash); - return s; -} - -KS_DECLARE(void) blade_rpc_remove_namespace(char* namespace) -{ - char nskey[KS_RPCMESSAGE_NAMESPACE_LENGTH]; - memset(nskey, 0, sizeof(nskey)); - strcat(nskey, namespace); - - ks_hash_write_lock(g_handle->namespace_hash); - - blade_rpc_namespace_t *n = ks_hash_search(g_handle->namespace_hash, nskey, KS_UNLOCKED); - - ks_hash_iterator_t* it = ks_hash_first(n->method_hash, KS_HASH_FLAG_RWLOCK); - - while (it) { - - const void *key; - void *value; - ks_ssize_t len = strlen(key); - - ks_hash_this(it, &key, &len, &value); - blade_rpc_callbackpair_t *callbacks = (blade_rpc_callbackpair_t *)value; - - ks_mutex_lock(callbacks->lock); - - if (callbacks->custom) { - ks_pool_free(g_handle->pool, callbacks->custom); - } - - it = ks_hash_next(&it); - ks_hash_remove(n->method_hash, (void *)key); - } - - ks_hash_write_unlock(g_handle->namespace_hash); - - return; -} - - -/* - * template functions - * - */ - -KS_DECLARE(ks_status_t) blade_rpc_declare_template(char* templatename, const char* version) -{ - char nskey[KS_RPCMESSAGE_NAMESPACE_LENGTH]; - memset(nskey, 0, sizeof(nskey)); - strcat(nskey, templatename); - - - /* find/insert to namespace hash as needed */ - ks_hash_write_lock(g_handle->template_hash); - blade_rpc_namespace_t *n = ks_hash_search(g_handle->template_hash, nskey, KS_UNLOCKED); - - if (n == NULL) { - n = ks_pool_alloc(g_handle->pool, sizeof (blade_rpc_namespace_t) + strlen(templatename) + 1); - strncpy(n->name, templatename, KS_RPCMESSAGE_NAMESPACE_LENGTH); - strncpy(n->version, version, KS_RPCMESSAGE_VERSION_LENGTH); - ks_hash_create(&n->method_hash, - KS_HASH_MODE_CASE_SENSITIVE, - KS_HASH_FLAG_RWLOCK + KS_HASH_FLAG_DUP_CHECK + KS_HASH_FLAG_FREE_VALUE, - g_handle->pool); - ks_hash_insert(g_handle->template_hash, n->name, n); - } - ks_hash_write_unlock(g_handle->template_hash); - - ks_log(KS_LOG_DEBUG, "Declaring application template namespace %s, version %s\n", templatename, version); - - return KS_STATUS_SUCCESS; -} - -KS_DECLARE(ks_status_t)blade_rpc_register_template_function(char *name, - char *command, - jrpc_func_t func, - jrpc_func_t respfunc) -{ - (void)blade_rpc_find_template_locked; //remove - - if (!func && !respfunc) { - return KS_STATUS_FAIL; - } - - char nskey[KS_RPCMESSAGE_NAMESPACE_LENGTH]; - memset(nskey, 0, sizeof(nskey)); - strcat(nskey, name); - - char fqcommand[KS_RPCMESSAGE_FQCOMMAND_LENGTH]; - memset(fqcommand, 0, sizeof(fqcommand)); - strcat(fqcommand, command); - - int lkey = strlen(fqcommand)+1; - - if (lkey < 16) { - lkey = 16; - } - - ks_hash_read_lock(g_handle->template_hash); /* lock template hash */ - - blade_rpc_namespace_t *n = ks_hash_search(g_handle->template_hash, nskey, KS_UNLOCKED); - - if (n == NULL) { - ks_hash_read_unlock(g_handle->template_hash); - ks_log(KS_LOG_ERROR, "Unable to find %s template\n", name); - return KS_STATUS_FAIL; - } - - ks_hash_read_lock(n->method_hash); - blade_rpc_callbackpair_t* callbacks = ks_hash_search(n->method_hash, fqcommand, KS_UNLOCKED); - ks_hash_read_unlock(n->method_hash); - - /* just ignore attempt to re register callbacks */ - /* as the template may already be in use leading to confusion */ - - if (callbacks != NULL) { - ks_mutex_unlock(callbacks->lock); - ks_hash_read_unlock(g_handle->template_hash); - ks_log(KS_LOG_ERROR, "Callbacks already registered for %s template\n", name); - return KS_STATUS_FAIL; - } - - callbacks = - (blade_rpc_callbackpair_t*)ks_pool_alloc(g_handle->pool, lkey + sizeof(blade_rpc_callbackpair_t)); - - strcpy(callbacks->command, command); - callbacks->command_length = lkey; - callbacks->request_func = func; - callbacks->response_func = respfunc; - - ks_mutex_create(&callbacks->lock, KS_MUTEX_FLAG_DEFAULT, g_handle->pool); - - ks_hash_write_lock(n->method_hash); /* lock method hash */ - - ks_hash_insert(n->method_hash, callbacks->command, (void *) callbacks); - - ks_hash_write_unlock(n->method_hash); /* unlock method hash */ - ks_hash_read_unlock(g_handle->template_hash); /* unlock namespace hash */ - - ks_log(KS_LOG_DEBUG, "Template message %s %s registered\n", name, command); - - return KS_STATUS_SUCCESS; - -} - -KS_DECLARE(ks_status_t)blade_rpc_inherit_template(char *namespace, char* template) -{ - ks_hash_read_lock(g_handle->template_hash); - - char tkey[KS_RPCMESSAGE_NAMESPACE_LENGTH+1]; - memset(tkey, 0, sizeof(tkey)); - strcpy(tkey, template); - - char nskey[KS_RPCMESSAGE_NAMESPACE_LENGTH+1]; - memset(nskey, 0, sizeof(tkey)); - strcpy(nskey, namespace); - - blade_rpc_namespace_t *n = ks_hash_search(g_handle->template_hash, tkey, KS_UNLOCKED); - - if (!n) { - ks_hash_read_unlock(g_handle->template_hash); - ks_log(KS_LOG_ERROR, "Unable to locate template %s\n", template); - return KS_STATUS_FAIL; - } - - ks_hash_read_lock(g_handle->namespace_hash); - - blade_rpc_namespace_t *ns = ks_hash_search(g_handle->namespace_hash, nskey, KS_UNLOCKED); - - if (!ns) { - ks_hash_read_unlock(g_handle->template_hash); - ks_hash_read_unlock(g_handle->namespace_hash); - ks_log(KS_LOG_ERROR, "Unable to locate namespace %s\n", namespace); - return KS_STATUS_FAIL; - } - - ks_hash_write_lock(ns->method_hash); - - ks_hash_iterator_t* it = ks_hash_first(n->method_hash, KS_HASH_FLAG_RWLOCK); - - while (it) { - - const void *key; - void *value; - ks_ssize_t len = strlen(key); - - ks_hash_this(it, &key, &len, &value); - blade_rpc_callbackpair_t *t_callback = (blade_rpc_callbackpair_t *)value; - - ks_mutex_lock(t_callback->lock); - - int lkey = t_callback->command_length; - - blade_rpc_callbackpair_t *callbacks = - (blade_rpc_callbackpair_t*)ks_pool_alloc(g_handle->pool, lkey + sizeof(blade_rpc_callbackpair_t)); - - strcat(callbacks->command, template); - strcat(callbacks->command, "."); - strcat(callbacks->command, t_callback->command); - - callbacks->command_length = lkey; - callbacks->request_func = t_callback->request_func; - callbacks->response_func = t_callback->response_func; - ks_mutex_create(&callbacks->lock, KS_MUTEX_FLAG_DEFAULT, g_handle->pool); - - ks_hash_insert(ns->method_hash, callbacks->command, (void *) callbacks); - - ks_mutex_unlock(t_callback->lock); - - it = ks_hash_next(&it); - } - - - ks_hash_write_lock(ns->method_hash); - - ks_hash_read_unlock(g_handle->namespace_hash); - ks_hash_read_unlock(g_handle->template_hash); - - return KS_STATUS_SUCCESS; -} - - -/* - * create a request message - */ -KS_DECLARE(ks_rpcmessageid_t) blade_rpc_create_request(char *namespace, - char *method, - blade_rpc_fields_t* fields, - cJSON **paramsP, - cJSON **requestP) -{ - cJSON *jversion = NULL; - blade_rpc_callbackpair_t* callbacks = NULL; - - *requestP = NULL; - - ks_hash_read_lock(g_handle->namespace_hash); - blade_rpc_namespace_t *n = ks_hash_search(g_handle->namespace_hash, namespace, KS_UNLOCKED); - - if (n) { - ks_hash_read_lock(n->method_hash); - callbacks = ks_hash_search(n->method_hash, method, KS_UNLOCKED); - if (callbacks) { - jversion = cJSON_CreateString(n->version); - } - ks_hash_read_unlock(n->method_hash); - } - - ks_hash_read_unlock(g_handle->namespace_hash); - - if (!n) { - ks_log(KS_LOG_ERROR, "No namespace %s found\n", namespace); - return 0; - } - - if (!callbacks) { - ks_log(KS_LOG_ERROR, "No method %s.%s found\n", namespace, method); - return 0; - } - - ks_rpcmessageid_t msgid = ks_rpcmessage_create_request(namespace, method, paramsP, requestP); - - if (!msgid || *requestP == NULL) { - ks_log(KS_LOG_ERROR, "Unable to create rpc message for method %s.%s\n", namespace, method); - return 0; - } - - cJSON *jfields = cJSON_CreateObject(); - - cJSON_AddItemToObject(jfields, "version", jversion); - - if (fields->to) { - cJSON_AddStringToObject(jfields, "to", fields->to); - } - - if (fields->from) { - cJSON_AddStringToObject(jfields, "from", fields->from); - } - - if (fields->token) { - cJSON_AddStringToObject(jfields, "token", fields->token); - } - - cJSON_AddItemToObject(*requestP, "blade", jfields); - - return msgid; -} - -KS_DECLARE(ks_rpcmessageid_t) blade_rpc_create_response(cJSON *request, - cJSON **replyP, - cJSON **responseP) -{ - cJSON *jfields = cJSON_GetObjectItem(request, "blade"); - - if (!jfields) { - ks_log(KS_LOG_ERROR, "No blade routing info found. Unable to create response\n"); - return 0; - } - - ks_rpcmessageid_t msgid = ks_rpcmessage_create_response(request, replyP, responseP); - - if (!msgid || *responseP == NULL) { - ks_log(KS_LOG_ERROR, "Unable to create rpc response message\n"); //TODO : Add namespace, method from request - return 0; - } - - const char *to = cJSON_GetObjectCstr(jfields, "to"); - const char *from = cJSON_GetObjectCstr(jfields, "from"); - const char *token = cJSON_GetObjectCstr(jfields, "token"); - const char *version = cJSON_GetObjectCstr(jfields, "version"); - - cJSON *blade = cJSON_CreateObject(); - - if (to) { - cJSON_AddStringToObject(blade, "to", from); - } - - if (from) { - cJSON_AddStringToObject(blade, "from", to); - } - - if (token) { - cJSON_AddStringToObject(blade, "token", token); - } - - if (version) { - cJSON_AddStringToObject(blade, "version", version); - } - - cJSON_AddItemToObject(*responseP, "blade", blade); - - return msgid; -} - -KS_DECLARE(ks_rpcmessageid_t) blade_rpc_create_errorresponse(cJSON *request, - cJSON **errorP, - cJSON **responseP) -{ - ks_rpcmessageid_t msgid = blade_rpc_create_response(request, NULL, responseP); - - if (msgid) { - - if (errorP) { - - if (*errorP) { - cJSON_AddItemToObject(*responseP, "error", *errorP); - } - else { - cJSON *error = cJSON_CreateObject(); - cJSON_AddItemToObject(*responseP, "error", error); - *errorP = error; - } - } - } - - return msgid; -} - - -const char BLADE_JRPC_METHOD[] = "method"; -const char BLADE_JRPC_ID[] = "id"; -const char BLADE_JRPC_FIELDS[] = "blade"; -const char BLADE_JRPC_TO[] = "to"; -const char BLADE_JRPC_FROM[] = "from"; -const char BLADE_JRPC_TOKEN[] = "token"; -const char BLADE_JRPC_VERSION[] = "version"; - -KS_DECLARE(ks_status_t) blade_rpc_parse_message(cJSON *message, - char **namespaceP, - char **methodP, - char **versionP, - uint32_t *idP, - blade_rpc_fields_t **fieldsP) -{ - const char *m = cJSON_GetObjectCstr(message, BLADE_JRPC_METHOD); - cJSON *blade = cJSON_GetObjectItem(message, BLADE_JRPC_FIELDS); - cJSON *jid = cJSON_GetObjectItem(message, BLADE_JRPC_ID); - - *fieldsP = NULL; - *namespaceP = NULL; - *versionP = NULL; - *methodP = NULL; - *idP = 0; - - if (jid) { - *idP = jid->valueint; - } - - if (!m || !blade) { - const char *buffer = cJSON_PrintUnformatted(message); - ks_log(KS_LOG_ERROR, "Unable to locate necessary fields in message:\n%s\n", buffer); - ks_pool_free(g_handle->pool, buffer); - return KS_STATUS_FAIL; - } - - cJSON *jto = cJSON_GetObjectItem(blade, BLADE_JRPC_TO); - cJSON *jfrom = cJSON_GetObjectItem(blade, BLADE_JRPC_FROM); - cJSON *jtoken = cJSON_GetObjectItem(blade, BLADE_JRPC_TOKEN); - - - ks_size_t len = KS_RPCMESSAGE_COMMAND_LENGTH + 1 + - KS_RPCMESSAGE_NAMESPACE_LENGTH + 1 + - KS_RPCMESSAGE_VERSION_LENGTH + 1 + - sizeof(blade_rpc_fields_t) + 1; - - uint32_t lento = 0; - uint32_t lenfrom = 0; - uint32_t lentoken = 0; - - if (jto) { - lento = strlen(jto->valuestring) + 1; - len += lento; - } - - if (jfrom) { - lenfrom += strlen(jfrom->valuestring) + 1; - len += lenfrom; - } - - if (jtoken) { - lentoken += strlen(jtoken->valuestring) + 1; - len += lentoken; - } - - blade_rpc_fields_t *fields = (blade_rpc_fields_t *)ks_pool_alloc(g_handle->pool, len); - - char *namespace = (char*)fields + sizeof(blade_rpc_fields_t); - char *command = namespace + KS_RPCMESSAGE_NAMESPACE_LENGTH + 1; - char *version = command + KS_RPCMESSAGE_COMMAND_LENGTH + 1; - - char *ptr = version + KS_RPCMESSAGE_VERSION_LENGTH + 1; - - if (jto) { - strcpy(ptr, jto->valuestring); - fields->to = ptr; - ptr += strlen(jto->valuestring) + 1; - } - - if (jfrom) { - strcpy(ptr, jfrom->valuestring); - fields->from = ptr; - ptr += strlen(jfrom->valuestring) + 1; - } - - if (jtoken) { - strcpy(ptr, jtoken->valuestring); - fields->token = ptr; - ptr += strlen(jtoken->valuestring) + 1; - } - - blade_rpc_parse_fqcommand(m, namespace, command); - - strcpy(version, cJSON_GetObjectCstr(blade, BLADE_JRPC_VERSION)); - - *fieldsP = fields; - *namespaceP = namespace; - *methodP = command; - *versionP = version; - - return KS_STATUS_SUCCESS; -} - - -/* - * send message - * ------------ - */ - -KS_DECLARE(ks_status_t) blade_rpc_write_data(char *sessionid, char* data, uint32_t size) -{ - - ks_status_t s = KS_STATUS_FAIL; - - // convert to json - cJSON *msg = cJSON_Parse(data); - - if (msg) { - - // ks_status_t blade_peer_message_push(blade_peer_t *peer, void *data, int32_t data_length); - - s = KS_STATUS_SUCCESS; - } - else { - ks_log(KS_LOG_ERROR, "Unable to format outbound message\n"); - } - - - // ks_rpc_write_json - // ks_status_t blade_peer_message_push(blade_peer_t *peer, void *data, int32_t data_length); - return s; -} - - -KS_DECLARE(ks_status_t) blade_rpc_write_json(cJSON* json) -{ - // just push the messages onto the communication manager - // synchronization etc, taken care of by the transport api' - char *data = cJSON_PrintUnformatted(json); - if (data) { - ks_log(KS_LOG_DEBUG, "%s\n", data); - //return blade_rpc_write_data(sessionid, data, strlen(data)); - return KS_STATUS_SUCCESS; - } - ks_log(KS_LOG_ERROR, "Unable to parse json\n"); - return KS_STATUS_FAIL; -} - - - - -/* - * Transport layer callbacks follow below - * -*/ - - - - -/* - * rpc message processing - */ -static ks_status_t blade_rpc_process_jsonmessage_all(cJSON *request) -{ - const char *fqcommand = cJSON_GetObjectCstr(request, "method"); - cJSON *error = NULL; - cJSON *response = NULL; - cJSON *responseP = NULL; - - if (!fqcommand) { - error = cJSON_CreateObject(); - cJSON_AddStringToObject(error, "errormessage", "Command not specified"); - ks_rpcmessage_create_request("rpcprotocol", "unknowncommand", &error, &responseP); - blade_rpc_write_json(responseP); - return KS_STATUS_FAIL; - } - - - char namespace[KS_RPCMESSAGE_NAMESPACE_LENGTH]; - char command[KS_RPCMESSAGE_COMMAND_LENGTH]; - - blade_rpc_parse_fqcommand(fqcommand, namespace, command); - blade_rpc_callbackpair_t* callbacks = blade_rpc_find_callbacks_locked(namespace, command); - - if (!callbacks) { - error = cJSON_CreateObject(); - cJSON_AddStringToObject(error, "errormessage", "Command not supported"); - ks_rpcmessage_create_response(request, &error, &responseP); - blade_rpc_write_json(responseP); - return KS_STATUS_FAIL; - } - - //todo - add more checks ? - - ks_bool_t isrequest = ks_rpcmessage_isrequest(request); - - enum jrpc_status_t jrcs = 0; - - if (isrequest && callbacks->request_func) { - - cJSON *responseP = NULL; - - if (callbacks->custom && callbacks->custom->prefix_request_func) { - jrcs = callbacks->custom->prefix_request_func(request, &responseP); - if ( (jrcs & JRPC_SEND) && responseP) { - blade_rpc_write_json(responseP); - cJSON_Delete(responseP); - responseP = NULL; - } - } - - if ( !(jrcs & JRPC_EXIT) && jrcs != JRPC_ERROR) { - jrcs = callbacks->request_func(request, &responseP); - if ((jrcs & JRPC_SEND) && responseP) { - blade_rpc_write_json(responseP); - cJSON_Delete(responseP); - responseP = NULL; - } - } - - if ( !(jrcs & JRPC_EXIT) && jrcs != JRPC_ERROR && callbacks->custom && callbacks->custom->postfix_request_func) { - jrcs = callbacks->custom->postfix_request_func(request, &responseP); - if ( (jrcs & JRPC_SEND) && responseP) { - blade_rpc_write_json(responseP); - cJSON_Delete(responseP); - responseP = NULL; - } - } - - ks_mutex_unlock(callbacks->lock); - - if (jrcs == JRPC_ERROR) { - return KS_STATUS_FAIL; - } - - return KS_STATUS_SUCCESS; - } - else if (!isrequest && callbacks->response_func) { - - if (callbacks->custom && callbacks->custom->prefix_response_func) { - jrcs = callbacks->custom->prefix_response_func(response, &responseP); - if ( (jrcs & JRPC_SEND) && responseP) { - blade_rpc_write_json(responseP); - cJSON_Delete(responseP); - responseP = NULL; - } - } - - if ( !(jrcs & JRPC_EXIT) && jrcs != JRPC_ERROR) { - jrcs = callbacks->response_func(response, &responseP); - if ( (jrcs & JRPC_SEND) && responseP) { - blade_rpc_write_json(responseP); - cJSON_Delete(responseP); - responseP = NULL; - } - } - - if ( !(jrcs & JRPC_EXIT) && jrcs != JRPC_ERROR && callbacks->custom && callbacks->custom->postfix_response_func) { - jrcs = callbacks->custom->postfix_response_func(response, &responseP); - if ( (jrcs & JRPC_SEND) && responseP) { - blade_rpc_write_json(responseP); - cJSON_Delete(responseP); - responseP = NULL; - } - } - - ks_mutex_unlock(callbacks->lock); - - if (jrcs == JRPC_ERROR) { - return KS_STATUS_FAIL; - } - - return KS_STATUS_SUCCESS; - } - - ks_log(KS_LOG_ERROR, "Unable to find message handler for %s\n", command); - - return KS_STATUS_FAIL; -} - -/* - * -*/ -KS_DECLARE(ks_status_t) blade_rpc_process_jsonmessage(cJSON *request) -{ - ks_status_t respstatus = blade_rpc_process_jsonmessage_all(request); - return respstatus; -} - -KS_DECLARE(ks_status_t) blade_rpc_process_data(const uint8_t *data, - ks_size_t size) -{ - - cJSON *json = cJSON_Parse((const char*)data); - if (json != NULL) { - ks_log( KS_LOG_ERROR, "Unable to parse message\n"); - return KS_STATUS_FAIL; - } - - /* deal with rpc message */ - if (ks_rpcmessage_isrpc(json)) { - ks_status_t respstatus = blade_rpc_process_jsonmessage_all(json); - return respstatus; - } - - ks_log(KS_LOG_ERROR, "Unable to identify message type\n"); - - return KS_STATUS_FAIL; -} - -//KS_DECLARE(ks_status_t) blade_rpc_process_blademessage(blade_message_t *message) -//{ -// uint8_t* data = NULL; -// ks_size_t size = 0; -// -// blade_message_get(message, (void **)&data, &size); -// -// if (data && size>0) { -// ks_status_t s = blade_rpc_process_data(data, size); -// blade_message_discard(&message); -// return s; -// } -// -// ks_log(KS_LOG_ERROR, "Message read failed\n"); -// return KS_STATUS_FAIL; -// -//} - - -/* 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 index 58c9b323d1..048baec183 100644 --- a/libs/libblade/src/blade_session.c +++ b/libs/libblade/src/blade_session.c @@ -430,6 +430,7 @@ 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); @@ -483,21 +484,31 @@ ks_status_t blade_session_state_on_ready(blade_session_t *bs) } -KS_DECLARE(ks_status_t) blade_session_send(blade_session_t *bs, cJSON *json) +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 (method) { - blade_request_create(&request, bs->handle, bs->id, json); + // @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)) { @@ -516,20 +527,18 @@ KS_DECLARE(ks_status_t) blade_session_send(blade_session_t *bs, cJSON *json) ks_status_t blade_session_process(blade_session_t *bs, cJSON *json) { - ks_status_t ret = KS_STATUS_SUCCESS; blade_request_t *breq = NULL; blade_response_t *bres = NULL; const char *jsonrpc = 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); - // @todo teardown the message, convert into a blade_request_t or blade_response_t - // @todo validate the jsonrpc fields jsonrpc = cJSON_GetObjectCstr(json, "jsonrpc"); if (!jsonrpc || strcmp(jsonrpc, "2.0")) { @@ -549,13 +558,50 @@ ks_status_t blade_session_process(blade_session_t *bs, cJSON *json) method = cJSON_GetObjectCstr(json, "method"); if (method) { - // @todo use method to find RPC callbacks + // @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, '.'); - blade_request_create(&breq, bs->handle, bs->id, json); + 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_pool_free(bs->pool, (void **)&space_name); + // @todo send error response, code = -32601 (method not found) + // @todo hangup session entirely? + return KS_STATUS_FAIL; + } + *method_name = '\0'; + method_name++; // @todo check if can be postfixed safely on previous assignment, can't recall + + tmp_space = blade_handle_space_lookup(bs->handle, space_name); + if (tmp_space) tmp_method = blade_space_methods_get(tmp_space, method_name); + + ks_pool_free(bs->pool, (void **)&space_name); + + if (!tmp_method) { + // @todo send error response, code = -32601 (method not found) + // @todo hangup session entirely? + 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); - // @todo call request callback handler + disconnect = callback(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? @@ -563,18 +609,20 @@ ks_status_t blade_session_process(blade_session_t *bs, cJSON *json) } blade_handle_requests_remove(breq); - method = cJSON_GetObjectCstr(breq->message, "method"); - ks_assert(method); - - // @todo use method to find RPC callbacks - blade_response_create(&bres, bs->handle, bs->id, breq, json); ks_assert(bres); - // @todo call response callback handler + disconnect = breq->callback(bres); + + blade_response_destroy(&bres); } - return ret; + if (disconnect) { + // @todo hangup session entirely? + return KS_STATUS_FAIL; + } + + return KS_STATUS_SUCCESS; } /* For Emacs: diff --git a/libs/libblade/src/blade_space.c b/libs/libblade/src/blade_space.c new file mode 100644 index 0000000000..09a8574c4c --- /dev/null +++ b/libs/libblade/src/blade_space.c @@ -0,0 +1,176 @@ +/* + * 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; + + const char *path; + ks_hash_t *methods; +}; + + +KS_DECLARE(ks_status_t) blade_space_create(blade_space_t **bsP, blade_handle_t *bh, 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->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(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 56357344ce..697a8d9feb 100644 --- a/libs/libblade/src/blade_stack.c +++ b/libs/libblade/src/blade_stack.c @@ -48,6 +48,7 @@ struct blade_handle_s { 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 //blade_identity_t *identity; blade_datastore_t *datastore; @@ -69,6 +70,7 @@ struct blade_handle_transport_registration_s { 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, @@ -133,6 +135,8 @@ KS_DECLARE(ks_status_t) blade_handle_create(blade_handle_t **bhP, ks_pool_t *poo 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->connections, KS_HASH_MODE_CASE_INSENSITIVE, KS_HASH_FLAG_NOLOCK | KS_HASH_FLAG_DUP_CHECK, bh->pool); ks_assert(bh->connections); @@ -144,6 +148,8 @@ KS_DECLARE(ks_status_t) blade_handle_create(blade_handle_t **bhP, ks_pool_t *poo *bhP = bh; + ks_log(KS_LOG_DEBUG, "Created\n"); + return KS_STATUS_SUCCESS; } @@ -168,10 +174,13 @@ KS_DECLARE(ks_status_t) blade_handle_destroy(blade_handle_t **bhP) ks_hash_destroy(&bh->requests); ks_hash_destroy(&bh->sessions); ks_hash_destroy(&bh->connections); + ks_hash_destroy(&bh->spaces); ks_hash_destroy(&bh->transports); 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)) { @@ -249,13 +258,22 @@ KS_DECLARE(ks_status_t) blade_handle_shutdown(blade_handle_t *bh) blade_session_t *value = NULL; ks_hash_this(it, (const void **)&key, NULL, (void **)&value); - ks_hash_remove(bh->requests, key); + //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 disconnect remaining unattached connections + // @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->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 @@ -316,11 +334,81 @@ KS_DECLARE(ks_status_t) blade_handle_transport_unregister(blade_handle_t *bh, co if (bhtr) ks_hash_remove(bh->transports, (void *)name); ks_hash_write_unlock(bh->transports); - if (bhtr) blade_handle_transport_registration_destroy(&bhtr); + 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_connect(blade_handle_t *bh, blade_connection_t **bcP, blade_identity_t *target, const char *session_id) { ks_status_t ret = KS_STATUS_SUCCESS; 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 54d744657a..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 @@ -46,7 +46,8 @@ #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/bpcp.h b/libs/libblade/src/include/blade_method.h similarity index 79% rename from libs/libblade/src/include/bpcp.h rename to libs/libblade/src/include/blade_method.h index 46a648abbe..74bf87d13f 100644 --- a/libs/libblade/src/include/bpcp.h +++ b/libs/libblade/src/include/blade_method.h @@ -1,23 +1,23 @@ /* - * 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 * 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,12 +31,15 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef _BPCP_H_ -#define _BPCP_H_ +#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 diff --git a/libs/libblade/src/include/blade_protocol.h b/libs/libblade/src/include/blade_protocol.h index ddc4a3cdee..0d3b4c772f 100644 --- a/libs/libblade/src/include/blade_protocol.h +++ b/libs/libblade/src/include/blade_protocol.h @@ -36,10 +36,17 @@ #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); +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_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_END_EXTERN_C #endif diff --git a/libs/libblade/src/include/blade_rpcproto.h b/libs/libblade/src/include/blade_rpcproto.h deleted file mode 100644 index f108c981e6..0000000000 --- a/libs/libblade/src/include/blade_rpcproto.h +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright (c) 2017, FreeSWITCH 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. - */ - -#ifndef _BLADE_RPCPROTO_H_ -#define _BLADE_RPCPROTO_H_ - -#include -#include - -// temp typedefs to get compile going -//typedef struct blade_peer_s blade_peer_t; -//typedef struct blade_event_s blade_event_t; - -#define KS_RPCMESSAGE_NAMESPACE_LENGTH 16 -#define KS_RPCMESSAGE_COMMAND_LENGTH 238 -#define KS_RPCMESSAGE_FQCOMMAND_LENGTH (KS_RPCMESSAGE_NAMESPACE_LENGTH+KS_RPCMESSAGE_COMMAND_LENGTH+1) -#define KS_RPCMESSAGE_VERSION_LENGTH 9 - - -/* - * contents to add to the "blade" field in rpc - */ - -typedef struct blade_rpc_fields_s { - - const char *to; - const char *from; - const char *token; - -} blade_rpc_fields_t; - - - -enum jrpc_status_t { - JRPC_PASS = (1 << 0), - JRPC_SEND = (1 << 1), - JRPC_EXIT = (1 << 2), - JRPC_SEND_EXIT = JRPC_SEND + JRPC_EXIT, - JRPC_ERROR = (1 << 3) -}; - - -typedef enum jrpc_status_t (*jrpc_func_t) (cJSON *request, cJSON **replyP); - - -/* - * setup - * ----- - */ - -KS_DECLARE(ks_status_t) blade_rpc_init(ks_pool_t *pool); - - -/* - * namespace and call back registration - * ------------------------------------ - */ -KS_DECLARE(ks_status_t) blade_rpc_declare_namespace(char* namespace, const char* version); -KS_DECLARE(ks_status_t) blade_rpc_register_function(char* namespace, - char *command, - jrpc_func_t func, - jrpc_func_t respfunc); -KS_DECLARE(ks_status_t) blade_rpc_register_custom_request_function(char* namespace, - char *command, - jrpc_func_t prefix_func, - jrpc_func_t postfix_func); -KS_DECLARE(ks_status_t) blade_rpc_register_custom_response_function(char *namespace, - char *command, - jrpc_func_t prefix_func, - jrpc_func_t postfix_func); -KS_DECLARE(void) blade_rpc_remove_namespace(char* namespace); - -/* - * template registration and inheritance - * ------------------------------------- - */ -KS_DECLARE(ks_status_t) blade_rpc_declare_template(char* templatename, const char* version); - -KS_DECLARE(ks_status_t)blade_rpc_register_template_function(char *name, - char *command, - jrpc_func_t func, - jrpc_func_t respfunc); - -KS_DECLARE(ks_status_t)blade_rpc_inherit_template(char *namespace, char* template); - - -/* - * create a request message - */ -KS_DECLARE(ks_rpcmessageid_t) blade_rpc_create_request(char *namespace, - char *method, - blade_rpc_fields_t* fields, - cJSON **paramsP, - cJSON **requestP); - -KS_DECLARE(ks_rpcmessageid_t) blade_rpc_create_response(cJSON *request, - cJSON **reply, - cJSON **response); - -KS_DECLARE(ks_rpcmessageid_t) blade_rpc_create_errorresponse(cJSON *request, - cJSON **reply, - cJSON **response); - -KS_DECLARE(ks_status_t) blade_rpc_parse_message(cJSON *message, - char **namespace, - char **method, - char **version, - uint32_t *idP, - blade_rpc_fields_t **fieldsP); - -/* - * peer create/destroy - * ------------------- - */ -//KS_DECLARE(ks_status_t) blade_rpc_onconnect(ks_pool_t *pool, blade_peer_t* peer); -//KS_DECLARE(ks_status_t) blade_rpc_disconnect(blade_peer_t* peer); - -/* - * send message - * ------------ - */ -KS_DECLARE(ks_status_t) blade_rpc_write(char *sessionid, char* data, uint32_t size); //uuid_t ? -KS_DECLARE(ks_status_t) blade_rpc_write_json(cJSON* json); - - -/* - * process inbound message - * ----------------------- - */ -KS_DECLARE(ks_status_t) blade_rpc_process_data(const uint8_t *data, ks_size_t size); - -KS_DECLARE(ks_status_t) blade_rpc_process_jsonmessage(cJSON *request); - - -#endif - diff --git a/libs/libblade/src/include/blade_session.h b/libs/libblade/src/include/blade_session.h index f4e92775b3..e79fbf34b0 100644 --- a/libs/libblade/src/include/blade_session.h +++ b/libs/libblade/src/include/blade_session.h @@ -1,23 +1,23 @@ /* * 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 @@ -52,7 +52,7 @@ 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); +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); diff --git a/libs/libks/src/include/ks_rpcmessage.h b/libs/libblade/src/include/blade_space.h similarity index 52% rename from libs/libks/src/include/ks_rpcmessage.h rename to libs/libblade/src/include/blade_space.h index da2157e9d0..65664fdba5 100644 --- a/libs/libks/src/include/ks_rpcmessage.h +++ b/libs/libblade/src/include/blade_space.h @@ -1,23 +1,23 @@ /* - * Copyright (c) 2017, FreeSWITCH Solutions LLC + * 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 @@ -31,56 +31,21 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - -#ifndef _KS_RPCMESSAGE_H_ -#define _KS_RPCMESSAGE_H_ - -#include "ks.h" +#ifndef _BLADE_SPACE_H_ +#define _BLADE_SPACE_H_ +#include KS_BEGIN_EXTERN_C - -#define KS_RPCMESSAGE_NAMESPACE_LENGTH 16 -#define KS_RPCMESSAGE_COMMAND_LENGTH 238 -#define KS_RPCMESSAGE_FQCOMMAND_LENGTH (KS_RPCMESSAGE_NAMESPACE_LENGTH+KS_RPCMESSAGE_COMMAND_LENGTH+1) -#define KS_RPCMESSAGE_VERSION_LENGTH 9 - - -typedef uint32_t ks_rpcmessageid_t; - - -KS_DECLARE(void) ks_rpcmessage_init(ks_pool_t *pool); - -KS_DECLARE(void*) ks_json_pool_alloc(ks_size_t size); -KS_DECLARE(void) ks_json_pool_free(void *ptr); - - -KS_DECLARE(ks_rpcmessageid_t) ks_rpcmessage_create_request(char *namespace, - char *method, - cJSON **paramsP, - cJSON **requestP); - -KS_DECLARE(ks_size_t) ks_rpc_create_buffer(char *namespace, - char *method, - cJSON **paramsP, - ks_buffer_t *buffer); - -KS_DECLARE(ks_rpcmessageid_t) ks_rpcmessage_create_response( - const cJSON *request, - cJSON **resultP, - cJSON **responseP); - -KS_DECLARE(ks_rpcmessageid_t) ks_rpcmessage_create_errorresponse( - const cJSON *request, - cJSON **errorP, - cJSON **responseP); - -KS_DECLARE(ks_bool_t) ks_rpcmessage_isrequest(cJSON *msg); - -KS_DECLARE(ks_bool_t) ks_rpcmessage_isrpc(cJSON *msg); - +KS_DECLARE(ks_status_t) blade_space_create(blade_space_t **bsP, blade_handle_t *bh, 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(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 /* defined(_KS_RPCMESSAGE_H_) */ +#endif /* For Emacs: * Local Variables: diff --git a/libs/libblade/src/include/blade_stack.h b/libs/libblade/src/include/blade_stack.h index ff86af388e..27b45bf93f 100644 --- a/libs/libblade/src/include/blade_stack.h +++ b/libs/libblade/src/include/blade_stack.h @@ -50,6 +50,11 @@ 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_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); diff --git a/libs/libblade/src/include/blade_types.h b/libs/libblade/src/include/blade_types.h index 295217979d..14c908f857 100644 --- a/libs/libblade/src/include/blade_types.h +++ b/libs/libblade/src/include/blade_types.h @@ -47,6 +47,12 @@ 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_space_s blade_space_t; +typedef struct blade_method_s blade_method_t; + + +typedef ks_bool_t (*blade_request_callback_t)(blade_request_t *breq); +typedef ks_bool_t (*blade_response_callback_t)(blade_response_t *bres); typedef struct blade_datastore_s blade_datastore_t; @@ -147,6 +153,7 @@ struct blade_request_s { 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 }; diff --git a/libs/libblade/test/Makefile.am b/libs/libblade/test/Makefile.am index e6c8c3902f..328a71ef25 100644 --- a/libs/libblade/test/Makefile.am +++ b/libs/libblade/test/Makefile.am @@ -1,13 +1,8 @@ -AM_CFLAGS += -I$(abs_top_srcdir)/src/include -g -ggdb -O0 +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 += testrpcproto -testrpcproto_SOURCES = testrpcproto.c tap.c -testrpcproto_CFLAGS = $(AM_CFLAGS) -testrpcproto_LDADD = $(TEST_LDADD) - check_PROGRAMS += testbuild testbuild_SOURCES = testbuild.c tap.c testbuild_CFLAGS = $(AM_CFLAGS) diff --git a/libs/libblade/test/testrpcproto.c b/libs/libblade/test/testrpcproto.c deleted file mode 100644 index 2b5f28252f..0000000000 --- a/libs/libblade/test/testrpcproto.c +++ /dev/null @@ -1,724 +0,0 @@ -#pragma GCC diagnostic ignored "-Wunused-but-set-variable" -#pragma GCC diagnostic ignored "-Wunused-variable" -#pragma GCC diagnostic ignored "-Wunused-function" - - -#include - -#pragma GCC optimize ("O0") - - -ks_pool_t *pool; -ks_thread_pool_t *tpool; - - -static ks_thread_t *threads[10]; - -static char idbuffer[51]; - - - -static enum jrpc_status_t process_widget(cJSON *msg, cJSON **response) -{ - printf("entering process_widget\n"); - - char *b0 = cJSON_PrintUnformatted(msg); - printf("Request: %s\n", b0); - ks_pool_free(pool, &b0); - - cJSON *resp = cJSON_CreateObject(); - cJSON_AddNumberToObject(resp, "code", 199); - - //ks_rpcmessageid_t msgid = ks_rpcmessage_create_response(msg, &resp, response); - ks_rpcmessageid_t msgid = blade_rpc_create_response(msg, &resp, response); - - char *b1 = cJSON_PrintUnformatted(*response); //(*response); - printf("Response: msgid %d\n%s\n", msgid, b1); - ks_pool_free(pool, &b1); - - printf("exiting process_wombat\n"); - - return JRPC_SEND; -} - - -static enum jrpc_status_t process_widget_response(cJSON *request, cJSON **msg) -{ - printf("entering process_widget_response\n"); - printf("exiting process_widget_response\n"); - return JRPC_PASS; -} - - - -static enum jrpc_status_t process_wombat(cJSON *msg, cJSON **replyP) -{ - printf("entering process_wombat\n"); - - char *b0 = cJSON_PrintUnformatted(msg); - printf("\nRequest: %s\n\n", b0); - ks_pool_free(pool, &b0); - - cJSON *result = cJSON_CreateObject(); - cJSON_AddNumberToObject(result, "code", 99); - cJSON *response; - -// ks_rpcmessageid_t msgid = ks_rpcmessage_create_response(msg, &result, &response); - ks_rpcmessageid_t msgid = blade_rpc_create_response(msg, &result, &response); - - cJSON *response_copy = cJSON_Duplicate(response, 1); - blade_rpc_process_jsonmessage(response_copy); - - if (msgid != 0) { - char *b1 = cJSON_PrintUnformatted(response); //(*response); - printf("\nResponse: msgid %d\n%s\n\n", msgid, b1); - blade_rpc_write_json(response); - ks_pool_free(pool, &b1); - } - else { - printf("process_wombat: unable to create response \n"); - return JRPC_ERROR; - } - - blade_rpc_fields_t *r_fields; - - char *r_method; - char *r_namespace; - char *r_version; - uint32_t r_id; - - ks_status_t s1 = blade_rpc_parse_message(msg, &r_namespace, &r_method, &r_version, &r_id, &r_fields); - - if (s1 == KS_STATUS_FAIL) { - printf("process_wombat: blade_rpc_parse_message failed\n"); - return JRPC_ERROR; - } - - printf("\nprocess_wombat: blade_rpc_parse_message namespace %s, method %s, id %d, version %s, to %s, from %s, token %s\n\n", - r_namespace, r_method, r_id, r_version, - r_fields->to, r_fields->from, r_fields->token); - - cJSON *parms2 = NULL; - - char to[] = "tony@freeswitch.com/laptop?transport=wss&host=server1.freeswitch.com&port=1234"; - char from[] = "colm@freeswitch.com/laptop?transport=wss&host=server2.freeswitch.com&port=4321"; - char token[] = "abcdefhgjojklmnopqrst"; - - blade_rpc_fields_t fields; - fields.to = to; - fields.from = from; - fields.token = token; - -// msgid = ks_rpcmessage_create_request("app1", "widget", &parms2, replyP); - msgid = blade_rpc_create_request(r_namespace, r_method, &fields, NULL, replyP); - - if (!msgid) { - printf("process wombat: create of next request failed\n"); - return JRPC_ERROR; - } - - b0 = cJSON_PrintUnformatted(*replyP); - - if (!b0) { - printf("process wombat: create of next request cannot be formatted\n"); - return JRPC_ERROR; - } - - - printf("\nprocess wombat: next request\n%s\n\n", b0); - - - printf("\n\nexiting process_wombat with a reply to send\n"); - - return JRPC_SEND; -} - -static enum jrpc_status_t process_wombat_prerequest(cJSON *request, cJSON **msg) -{ - printf("entering process_wombat_prerequest\n"); - printf("exiting process_wombat_prerequest\n"); - return JRPC_SEND; -} - -static enum jrpc_status_t process_wombat_postrequest(cJSON *request, cJSON **msg) -{ - printf("entering process_wombat_postrequest\n"); - printf("exiting process_wombat_postrequest\n"); - return JRPC_PASS; -} - - - -static enum jrpc_status_t process_wombat_response(cJSON *request, cJSON **msg) -{ - printf("entering process_wombat_response\n"); - printf("exiting process_wombat_response\n"); - return JRPC_PASS; -} - -static enum jrpc_status_t process_wombat_preresponse(cJSON *request, cJSON **msg) -{ - - printf("entering process_wombat_preresponse\n"); - - cJSON *response = NULL; - cJSON *result = NULL; - - cJSON *parms2 = NULL; - - //ks_rpcmessageid_t msgid = ks_rpcmessage_create_request("app1", "widget", &parms2, msg); - - printf("exiting process_wombat_preresponse\n"); - return JRPC_SEND; -} - -static enum jrpc_status_t process_wombat_postresponse(cJSON *request, cJSON **msg) -{ - printf("entering process_postwombat_response\n"); - printf("exiting process_postwombat_response\n"); - return JRPC_PASS; -} - - - - -static enum jrpc_status_t process_badbunny( cJSON *msg, cJSON **response) -{ - printf("entering process_badbunny\n"); - - char *b0 = cJSON_PrintUnformatted(msg); - printf("\nRequest: %s\n\n", b0); - ks_pool_free(pool, &b0); - - cJSON *respvalue; - - ks_rpcmessageid_t msgid = ks_rpcmessage_create_errorresponse(msg, &respvalue, response); - - char *b2 = cJSON_PrintUnformatted(*response); - printf("\nRequest: msgid %d\n%s\n\n", msgid, b2); - ks_pool_free(pool, &b2); - - //cJSON *respvalue = cJSON_CreateNumber(1); - - - char *b1 = cJSON_PrintUnformatted(*response); //(*response); - printf("\nResponse: %s\n\n", b1); - ks_pool_free(pool, &b1); - - printf("exiting process_badbunny\n"); - - - return JRPC_SEND; -} - - -void test01() -{ - printf("**** testrpcmessages - test01 start\n"); fflush(stdout); - - blade_rpc_declare_template("temp1", "1.0"); - - blade_rpc_register_template_function("temp1", "widget", process_widget, process_widget_response); - - blade_rpc_declare_namespace("app1", "1.0"); - - blade_rpc_register_function("app1", "wombat", process_wombat, process_wombat_response); - - blade_rpc_register_custom_request_function("app1", "wombat", process_wombat_prerequest, process_wombat_postresponse); - blade_rpc_register_custom_response_function("app1", "wombat", process_wombat_preresponse, process_wombat_postresponse); - - - /* message 1 */ - /* --------- */ - cJSON* request1 = NULL; - cJSON* parms1 = NULL; - - printf("\n\n\n - test01 message1 - basic message\n\n\n"); - - ks_rpcmessageid_t msgid = ks_rpcmessage_create_request("app1", "wombat", &parms1, &request1); - if (msgid == 0) { - printf("test01.1: unable to create message 1\n"); - return; - } - - if (!request1) { - printf("test01.1: No json returned from create request 1\n"); - return; - } - - cJSON_AddStringToObject(parms1, "hello", "cruel world"); - - char *pdata = cJSON_PrintUnformatted(request1); - - if (!pdata) { - printf("test01.1: unable to parse cJSON object\n"); - return; - } - - printf("test01 request:\n%s\n", pdata); - - - blade_rpc_process_jsonmessage(request1); - - cJSON_Delete(request1); - - ks_pool_free(pool, &pdata); - - printf("\ntest01.1 complete\n"); - - - /* message 2 */ - /* --------- */ - - printf("\n\n\n test01 - message2 - test inherit\n\n\n"); - - blade_rpc_inherit_template("app1", "temp1"); - - cJSON* request2 = NULL; - cJSON* parms2 = cJSON_CreateObject(); - - cJSON_AddStringToObject(parms2, "hello2", "cruel world once again"); - - msgid = ks_rpcmessage_create_request("app1", "temp1.widget", &parms2, &request2); - if (msgid == 0) { - printf("test01.2: failed to create a wombat\n"); - return; - } - - if (!request2) { - printf("test01.2: No json returned from create request 1\n"); - return; - } - - pdata = cJSON_PrintUnformatted(request2); - - if (!pdata) { - printf("test01.2: unable to parse cJSON object\n"); - return; - } - - printf("\ntest01 request:\n%s\n\n\n", pdata); - - blade_rpc_process_jsonmessage(request2); - - cJSON_Delete(request2); - ks_pool_free(pool, &pdata); - - printf("\ntest01.2 complete\n"); - - return; - -} - -void test02() -{ - printf("**** testrpcmessages - test02 start\n"); fflush(stdout); - - blade_rpc_declare_namespace("app2", "1.0"); - - blade_rpc_register_function("app2", "wombat", process_wombat, process_wombat_response); - - blade_rpc_inherit_template("app2", "temp1"); - - blade_rpc_register_custom_request_function("app2", "wombat", process_wombat_prerequest, process_wombat_postresponse); - blade_rpc_register_custom_response_function("app2", "wombat", process_wombat_preresponse, process_wombat_postresponse); - - blade_rpc_register_function("app2", "bunny", process_badbunny, NULL); - - char to[] = "tony@freeswitch.com/laptop?transport=wss&host=server1.freeswitch.com&port=1234"; - char from[] = "colm@freeswitch.com/laptop?transport=wss&host=server2.freeswitch.com&port=4321"; - char token[] = "abcdefhgjojklmnopqrst"; - - blade_rpc_fields_t fields; - fields.to = to; - fields.from = from; - fields.token = token; - - - /* test the 4 different ways to handle param messages */ - - cJSON *params1 = NULL; - cJSON *request1; - - ks_rpcmessageid_t msgid = blade_rpc_create_request("app2", "temp1.widget2", &fields, ¶ms1, &request1); - - if (!msgid) { - printf("test02.1: create_request failed\n"); - return; - } - - cJSON_AddStringToObject(params1, "hello", "cruel world"); - - char *pdata = cJSON_PrintUnformatted(request1); - - if (!pdata) { - printf("test02.1: unable to parse cJSON object\n"); - return; - } - - printf("\ntest02.1 request:\n\n%s\n\n\n", pdata); - - printf("\n\n -----------------------------------------\n\n"); - - ks_status_t s1 = blade_rpc_process_jsonmessage(request1); - if (s1 == KS_STATUS_FAIL) { - printf("test02.1: process request1 failed\n"); - return; - } - - printf(" -----------------------------------------\n\n\n\n"); - - ks_pool_free(pool, &pdata); - - cJSON *reply1 = NULL; - cJSON *response1 = NULL; - - ks_rpcmessageid_t msgid2 = blade_rpc_create_response(request1, &reply1, &response1); - - if (!msgid2) { - printf("test02.1: create_response failed\n"); - return; - } - - cJSON_AddNumberToObject(reply1, "code", 10); - cJSON_AddStringToObject(reply1, "farewell", "cruel server"); - - pdata = cJSON_PrintUnformatted(response1); - - if (!pdata) { - printf("test02.1: unable to parse cJSON response object\n"); - return; - } - - printf("\ntest02.1 response:\n\n%s\n\n\n", pdata); - - printf("\n\n -----------------------------------------\n\n"); - - s1 = blade_rpc_process_jsonmessage(response1); - if (s1 == KS_STATUS_FAIL) { - printf("test02.1: process request1 failed\n"); - return; - } - - printf(" -----------------------------------------\n\n\n\n"); - - - ks_pool_free(pool, &pdata); - - printf("**** testrpcmessages - test02 finished\n"); fflush(stdout); - - return; -} - - -void test02a() -{ - printf("**** testrpcmessages - test02a start\n"); fflush(stdout); - - char to[] = "tony@freeswitch.com/laptop?transport=wss&host=server1.freeswitch.com&port=1234"; - char from[] = "colm@freeswitch.com/laptop?transport=wss&host=server2.freeswitch.com&port=4321"; - char token[] = "abcdefhgjojklmnopqrst"; - - blade_rpc_fields_t fields; - fields.to = to; - fields.from = from; - fields.token = token; - - - /* test the 4 different ways to handle param messages */ - - cJSON *request1; - - ks_rpcmessageid_t msgid = blade_rpc_create_request("app2", "wombat", &fields, NULL, &request1); - - if (!msgid) { - printf("test02.1: create_request failed\n"); - return; - } - - char *pdata = cJSON_PrintUnformatted(request1); - - if (!pdata) { - printf("test02.1: unable to parse cJSON object\n"); - return; - } - - printf("\ntest02.1 request:\n\n%s\n\n\n", pdata); - - printf("\n\n -----------------------------------------\n\n"); - - ks_status_t s1 = blade_rpc_process_jsonmessage(request1); - if (s1 == KS_STATUS_FAIL) { - printf("test02.1: process request1 failed\n"); - return; - } - - printf(" -----------------------------------------\n\n\n\n"); - - - - - ks_pool_free(pool, &pdata); - - cJSON *response1 = NULL; - - ks_rpcmessageid_t msgid2 = blade_rpc_create_response(request1, NULL, &response1); - - if (!msgid2) { - printf("test02.1: create_response failed\n"); - return; - } - - pdata = cJSON_PrintUnformatted(response1); - - printf("\ntest02.1 response:\n\n%s\n\n\n", pdata); - - ks_pool_free(pool, &pdata); - - printf("**** testrpcmessages - test02a finished\n\n\n"); fflush(stdout); - - return; -} - - -void test02b() -{ - printf("**** testrpcmessages - test02b start\n"); fflush(stdout); - - char to[] = "tony@freeswitch.com/laptop?transport=wss&host=server1.freeswitch.com&port=1234"; - char from[] = "colm@freeswitch.com/laptop?transport=wss&host=server2.freeswitch.com&port=4321"; - char token[] = "abcdefhgjojklmnopqrst"; - - blade_rpc_fields_t fields; - fields.to = to; - fields.from = from; - fields.token = token; - - - /* test the 4 different ways to handle param messages */ - - cJSON *params1 = cJSON_CreateNumber(4321); - cJSON *request1; - - ks_rpcmessageid_t msgid = blade_rpc_create_request("app2", "temp1.widget", &fields, ¶ms1, &request1); - - if (!msgid) { - printf("test02.1: create_request failed\n"); - return; - } - - char *pdata = cJSON_PrintUnformatted(request1); - - if (!pdata) { - printf("test02.1: unable to parse cJSON object\n"); - return; - } - - printf("\ntest02.1 request:\n\n%s\n\n\n", pdata); - - ks_pool_free(pool, &pdata); - - cJSON *reply1 = cJSON_CreateString("successful"); - cJSON *response1 = NULL; - - ks_rpcmessageid_t msgid2 = blade_rpc_create_response(request1, &reply1, &response1); - - if (!msgid2) { - printf("test02.1: create_response failed\n"); - return; - } - - pdata = cJSON_PrintUnformatted(response1); - - printf("\ntest02.1 response:\n\n%s\n\n\n", pdata); - - ks_pool_free(pool, &pdata); - - printf("**** testrpcmessages - test02b finished\n"); fflush(stdout); - - return; -} - - -void test02c() -{ - - printf("**** testrpcmessages - test02c start\n"); fflush(stdout); - - char to[] = "tony@freeswitch.com/laptop?transport=wss&host=server1.freeswitch.com&port=1234"; - char from[] = "colm@freeswitch.com/laptop?transport=wss&host=server2.freeswitch.com&port=4321"; - char token[] = "abcdefhgjojklmnopqrst"; - - blade_rpc_fields_t fields; - fields.to = to; - fields.from = from; - fields.token = token; - - - /* test the 4 different ways to handle param messages */ - - cJSON *params1 = cJSON_CreateObject(); - - cJSON_AddStringToObject(params1, "string1", "here is a string"); - cJSON_AddNumberToObject(params1, "number1", 4242); - - cJSON *request1; - - ks_rpcmessageid_t msgid = blade_rpc_create_request("app2", "bunny", &fields, ¶ms1, &request1); - - if (!msgid) { - printf("test02.1: create_request failed\n"); - return; - } - - cJSON_AddStringToObject(params1, "hello", "cruel world"); - - char *pdata = cJSON_PrintUnformatted(request1); - - if (!pdata) { - printf("test02.1: unable to parse cJSON object\n"); - return; - } - - printf("\ntest02.1 request:\n\n%s\n\n\n", pdata); - - ks_pool_free(pool, &pdata); - - - cJSON *reply1 = cJSON_CreateObject(); - cJSON_AddNumberToObject(reply1, "code", 10); - cJSON_AddStringToObject(reply1, "farewell", "cruel server"); - - - cJSON *response1 = NULL; - - ks_rpcmessageid_t msgid2 = blade_rpc_create_response(request1, &reply1, &response1); - - if (!msgid2) { - printf("test02.1: create_response failed\n"); - return; - } - - - pdata = cJSON_PrintUnformatted(response1); - - printf("\ntest02.1 response:\n\n%s\n\n\n", pdata); - - ks_pool_free(pool, &pdata); - - printf("**** testrpcmessages - test02c finished\n"); fflush(stdout); - - return; -} - - - - - - - - - - - - - - - - -/* test06 */ -/* ------ */ - -static void *testnodelocking_ex1(ks_thread_t *thread, void *data) -{ - return NULL; -} - -static void *testnodelocking_ex2(ks_thread_t *thread, void *data) -{ - return NULL; -} - - -void test06() -{ - printf("**** testmessages - test06 start\n"); fflush(stdout); - - ks_thread_t *t0; - ks_thread_create(&t0, testnodelocking_ex1, NULL, pool); - - ks_thread_t *t1; - ks_thread_create(&t1, testnodelocking_ex2, NULL, pool); - - ks_thread_join(t1); - ks_thread_join(t0); - - printf("\n\n* **testmessages - test06 -- threads complete\n\n"); fflush(stdout); - - printf("**** testmessages - test06 start\n"); fflush(stdout); - - return; -} - - - -int main(int argc, char *argv[]) { - - printf("testmessages - start\n"); - - int tests[100]; - if (argc == 0) { - tests[0] = 1; - tests[1] = 2; - tests[2] = 3; - tests[3] = 4; - tests[4] = 5; - } - else { - for(int tix=1; tix<100 && tix -#include -#include - -struct -{ - - ks_mutex_t *id_mutex; - uint32_t message_id; - - ks_pool_t *pool; - -} handle = {NULL, 0, NULL}; - -const char PROTOCOL[] = "jsonrpc"; -const char PROTOCOL_VERSION[] = "2.0"; -const char ID[] = "id"; -const char METHOD[] = "method"; -const char PARAMS[] = "params"; -const char ERROR[] = "error"; -const char RESULT[] = "result"; - - - -KS_DECLARE(void*) ks_json_pool_alloc(ks_size_t size) -{ - return ks_pool_alloc(handle.pool, size); -} - -KS_DECLARE(void) ks_json_pool_free(void *ptr) -{ - ks_pool_free(handle.pool, &ptr); -} - - -KS_DECLARE(void) ks_rpcmessage_init(ks_pool_t* pool) -{ - if (!handle.id_mutex) { - ks_mutex_create(&handle.id_mutex, KS_MUTEX_FLAG_DEFAULT, pool); - handle.pool = pool; - - cJSON_Hooks hooks; - hooks.malloc_fn = ks_json_pool_alloc; - hooks.free_fn = ks_json_pool_free; - cJSON_InitHooks(&hooks); - } - return; -} - -static uint32_t ks_rpcmessage_next_id() -{ - uint32_t message_id; - - ks_mutex_lock(handle.id_mutex); - - ++handle.message_id; - - if (!handle.message_id) { - ++handle.message_id; - } - - message_id = handle.message_id; - - ks_mutex_unlock(handle.id_mutex); - - return message_id; -} - - -static cJSON *ks_rpcmessage_new(uint32_t id) -{ - cJSON *obj = cJSON_CreateObject(); - cJSON_AddItemToObject(obj, PROTOCOL, cJSON_CreateString(PROTOCOL_VERSION)); - - if (id) { - cJSON_AddItemToObject(obj, ID, cJSON_CreateNumber(id)); - } - - return obj; -} - -static cJSON *ks_rpcmessage_dup(cJSON *msgid) -{ - cJSON *obj = cJSON_CreateObject(); - cJSON_AddItemToObject(obj, PROTOCOL, cJSON_CreateString(PROTOCOL_VERSION)); - - if (msgid) { - cJSON_AddItemToObject(obj, ID, cJSON_Duplicate(msgid, 0)); - } - - return obj; -} - -KS_DECLARE(ks_bool_t) ks_rpcmessage_isrequest(cJSON *msg) -{ - cJSON *result = cJSON_GetObjectItem(msg, RESULT); - cJSON *error = cJSON_GetObjectItem(msg, ERROR); - - if (result || error) { - return KS_FALSE; - } - - return KS_TRUE; -} - -KS_DECLARE(ks_bool_t) ks_rpcmessage_isrpc(cJSON *msg) -{ - cJSON *rpc = cJSON_GetObjectItem(msg, PROTOCOL); - - if (rpc) { - return KS_FALSE; - } - - return KS_TRUE; -} - - - - -KS_DECLARE(ks_rpcmessageid_t) ks_rpcmessage_create_request(char *namespace, - char *command, - cJSON **paramsP, - cJSON **requestP) -{ - cJSON *msg, *params = NULL; - *requestP = NULL; - - ks_rpcmessageid_t msgid = ks_rpcmessage_next_id(); - msg = ks_rpcmessage_new(msgid); - - if (paramsP) { - - if (*paramsP) { /* parameters have been passed */ - params = *paramsP; - } - else { - params = cJSON_CreateObject(); - *paramsP = params; - } - - cJSON_AddItemToObject(msg, PARAMS, params); - } - - char fqcommand[KS_RPCMESSAGE_FQCOMMAND_LENGTH]; - memset(fqcommand, 0, sizeof(fqcommand)); - - sprintf(fqcommand, "%s.%s", namespace, command); - - cJSON_AddItemToObject(msg, METHOD, cJSON_CreateString(fqcommand)); - - *requestP = msg; - return msgid; -} - -KS_DECLARE(ks_size_t) ks_rpc_create_buffer(char *namespace, - char *method, - cJSON **params, - ks_buffer_t *buffer) -{ - - cJSON *message; - - ks_rpcmessageid_t msgid = ks_rpcmessage_create_request(namespace, method, params, &message); - - if (!msgid) { - return 0; - } - - if ( (*params)->child == NULL) { - cJSON_AddNullToObject(*params, "bladenull"); - } - - const char* b = cJSON_PrintUnformatted(message); - ks_size_t size = strlen(b); - - ks_buffer_write(buffer, b, size); - cJSON_Delete(message); - - return size; -} - - -static ks_rpcmessageid_t ks_rpcmessage_get_messageid(const cJSON *msg, cJSON **cmsgidP) -{ - ks_rpcmessageid_t msgid = 0; - - cJSON *cmsgid = cJSON_GetObjectItem(msg, ID); - - if (cmsgid->type == cJSON_Number) { - msgid = (ks_rpcmessageid_t) cmsgid->valueint; - } - - *cmsgidP = cmsgid; - - return msgid; -} - - -static ks_rpcmessageid_t ks_rpcmessage_new_response( - const cJSON *request, - cJSON **result, - cJSON **pmsg) -{ - cJSON *respmsg = NULL; - cJSON *cmsgid = NULL; - - cJSON *command = cJSON_GetObjectItem(request, METHOD); - - ks_rpcmessageid_t msgid = ks_rpcmessage_get_messageid(request, &cmsgid ); - - if (!msgid || !command) { - return 0; - } - - *pmsg = respmsg = ks_rpcmessage_dup(cmsgid); - - cJSON_AddItemToObject(respmsg, METHOD, cJSON_Duplicate(command, 0)); - - if (result && *result) { - cJSON_AddItemToObject(respmsg, RESULT, *result); - } - - return msgid; -} - - -KS_DECLARE(ks_rpcmessageid_t) ks_rpcmessage_create_response( - const cJSON *request, - cJSON **resultP, - cJSON **responseP) -{ - ks_rpcmessageid_t msgid = ks_rpcmessage_new_response(request, resultP, responseP); - - cJSON *respmsg = *responseP; - - if (msgid) { - - if (resultP && *resultP == NULL) { - cJSON *result = cJSON_CreateObject(); - *resultP = result; - cJSON_AddItemToObject(respmsg, RESULT, result); - } - } - - return msgid; -} - -KS_DECLARE(ks_rpcmessageid_t) ks_rpcmessage_create_errorresponse( - const cJSON *request, - cJSON **errorP, - cJSON **responseP) -{ - ks_rpcmessageid_t msgid = ks_rpcmessage_new_response(request, NULL, responseP); - cJSON *respmsg = *responseP; - - if (msgid) { - - if (errorP && *errorP == NULL) { - cJSON *error = cJSON_CreateObject(); - *errorP = error; - cJSON_AddItemToObject(respmsg, ERROR, error); - } - else if (errorP && *errorP) { - cJSON_AddItemToObject(*responseP, ERROR, *errorP); - } - } - - return msgid; -} - - -/* 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/test/Makefile.am b/libs/libks/test/Makefile.am index 0b85a468c4..cffc18bdcd 100644 --- a/libs/libks/test/Makefile.am +++ b/libs/libks/test/Makefile.am @@ -1,14 +1,9 @@ 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 -check_PROGRAMS += testmessages -testmessages_SOURCES = testmessages.c tap.c -testmessages_CFLAGS = $(AM_CFLAGS) -testmessages_LDADD = $(TEST_LDADD) - check_PROGRAMS += testbuckets testbuckets_SOURCES = testbuckets.c tap.c testbuckets_CFLAGS = $(AM_CFLAGS) @@ -96,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) @@ -108,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/testmessages.c b/libs/libks/test/testmessages.c deleted file mode 100644 index 7609cf69ac..0000000000 --- a/libs/libks/test/testmessages.c +++ /dev/null @@ -1,124 +0,0 @@ -#pragma GCC diagnostic ignored "-Wunused-but-set-variable" -#pragma GCC diagnostic ignored "-Wunused-variable" -#pragma GCC diagnostic ignored "-Wunused-function" - -#include "../src/include/ks_rpcmessage.h" - -#pragma GCC optimize ("O0") - - -ks_pool_t *pool; - - -void test01() -{ - printf("**** testrpcmessages - test01 start\n\n"); fflush(stdout); - - cJSON* request1 = NULL; - cJSON* parms1 = NULL; - cJSON* response1 = NULL; - - /*namespace, method, params, **request */ - ks_rpcmessageid_t msgid = ks_rpcmessage_create_request("app1", "func1", &parms1, &request1); - if (msgid == 0) { - printf("message create failed %d\n", msgid); - } - - cJSON_AddStringToObject(parms1, "hello", "cruel world"); - char* data = cJSON_PrintUnformatted(request1); - - printf("test01 request1: %d\n%s\n\n", msgid, data); - ks_json_pool_free(data); - - - /* convert to buffer */ - cJSON* parms2 = NULL; - ks_buffer_t *buffer; - - ks_buffer_create(&buffer, 256, 256, 1024); - - ks_size_t n = ks_rpc_create_buffer("app2", "func2", &parms2, buffer); - - ks_size_t size = ks_buffer_len(buffer); - char *b = (char *)ks_pool_alloc(pool, size+1); - ks_buffer_read(buffer, b, size); - - printf("test01 request2: %zd %zd from ks_buffer\n%s\n\n\n", n, size, b); - - - /* create message 3 */ - - cJSON *parms3 = cJSON_CreateNumber(1); - cJSON *request3 = NULL; - - msgid = ks_rpcmessage_create_request("app1", "badbunny", &parms3, &request3); - data = cJSON_PrintUnformatted(request3); - printf("\ntest01i request: %d\n%s\n\n", msgid, data); - - cJSON *response3 = NULL; - - ks_rpcmessage_create_response(request3, NULL, &response3); - - data = cJSON_PrintUnformatted(response3); - printf("\ntest01 response3: %d\n%s\n\n", msgid, data); - - ks_json_pool_free(data); - cJSON_Delete(request3); - cJSON_Delete(response3); - - printf("**** testrpcmessages - test01 complete\n\n\n"); fflush(stdout); -} - -void test02() -{ - printf("**** testmessages - test02 start\n"); fflush(stdout); - - printf("**** testmessages - test02 finished\n"); fflush(stdout); - - return; -} - - - -int main(int argc, char *argv[]) { - - printf("testmessages - start\n"); - - int tests[100]; - if (argc == 1) { - tests[0] = 1; - } - else { - for(int tix=1; tix<100 && tix Date: Wed, 8 Mar 2017 19:51:54 +0000 Subject: [PATCH 33/34] FS-9952: Added support to default ks_log system for including optional prefixes as desired, also added thread and time prefix options --- libs/libks/src/include/ks.h | 2 + libs/libks/src/include/ks_platform.h | 17 +++++---- libs/libks/src/include/ks_threadmutex.h | 9 +++++ libs/libks/src/include/ks_types.h | 31 +++++++++++----- libs/libks/src/ks_log.c | 49 +++++++++++++++++++++---- libs/libks/src/ks_thread.c | 11 ++++++ 6 files changed, 95 insertions(+), 24 deletions(-) diff --git a/libs/libks/src/include/ks.h b/libs/libks/src/include/ks.h index f7b88d002d..d5094f29a6 100644 --- a/libs/libks/src/include/ks.h +++ b/libs/libks/src/include/ks.h @@ -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); 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/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_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 From 7742dffae4192e73c258493a7320eb11b6437b8a Mon Sep 17 00:00:00 2001 From: Shane Bryldt Date: Thu, 16 Mar 2017 13:13:26 -0500 Subject: [PATCH 34/34] FS-9952: Initial implementation of a very basic text chat system which introduced a number of supporting subsystems --- libs/libblade/Makefile.am | 2 +- libs/libblade/src/blade_connection.c | 1 + libs/libblade/src/blade_module.c | 21 +- libs/libblade/src/blade_module_chat.c | 432 +++++++++++++++++++++ libs/libblade/src/blade_module_wss.c | 17 +- libs/libblade/src/blade_protocol.c | 81 +++- libs/libblade/src/blade_session.c | 227 ++++++++--- libs/libblade/src/blade_space.c | 11 +- libs/libblade/src/blade_stack.c | 214 +++++++++- libs/libblade/src/include/blade_module.h | 18 +- libs/libblade/src/include/blade_protocol.h | 3 + libs/libblade/src/include/blade_session.h | 7 + libs/libblade/src/include/blade_space.h | 3 +- libs/libblade/src/include/blade_stack.h | 8 + libs/libblade/src/include/blade_types.h | 26 +- libs/libblade/test/Makefile.am | 5 + libs/libblade/test/bladec.c | 156 +++++--- libs/libblade/test/bladec.cfg | 19 +- libs/libblade/test/bladec2.cfg | 11 - libs/libblade/test/blades.c | 207 ++++++++++ libs/libblade/test/blades.cfg | 28 ++ 21 files changed, 1325 insertions(+), 172 deletions(-) create mode 100644 libs/libblade/src/blade_module_chat.c delete mode 100644 libs/libblade/test/bladec2.cfg create mode 100644 libs/libblade/test/blades.c create mode 100644 libs/libblade/test/blades.cfg diff --git a/libs/libblade/Makefile.am b/libs/libblade/Makefile.am index 9346c3249a..f3ad18d3e0 100644 --- a/libs/libblade/Makefile.am +++ b/libs/libblade/Makefile.am @@ -15,7 +15,7 @@ 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 +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 -lconfig $(AM_LDFLAGS) libblade_la_LIBADD = libunqlite.la diff --git a/libs/libblade/src/blade_connection.c b/libs/libblade/src/blade_connection.c index f8cfaead54..d757845b6b 100644 --- a/libs/libblade/src/blade_connection.c +++ b/libs/libblade/src/blade_connection.c @@ -532,6 +532,7 @@ ks_status_t blade_connection_state_on_ready(blade_connection_t *bc) 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; } diff --git a/libs/libblade/src/blade_module.c b/libs/libblade/src/blade_module.c index a874e45ffc..403748b0fc 100644 --- a/libs/libblade/src/blade_module.c +++ b/libs/libblade/src/blade_module.c @@ -1,23 +1,23 @@ /* * 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 @@ -78,6 +78,13 @@ KS_DECLARE(ks_status_t) blade_module_destroy(blade_module_t **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); @@ -85,7 +92,7 @@ KS_DECLARE(void *) blade_module_data_get(blade_module_t *bm) return bm->module_data; } - + /* For Emacs: * Local Variables: * mode:c 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 index dc1118cb95..107a8420d3 100644 --- a/libs/libblade/src/blade_module_wss.c +++ b/libs/libblade/src/blade_module_wss.c @@ -122,7 +122,7 @@ ks_status_t blade_transport_wss_init_create(blade_transport_wss_init_t **bt_wssi ks_status_t blade_transport_wss_init_destroy(blade_transport_wss_init_t **bt_wssiP); -ks_bool_t blade_test_echo_request_handler(blade_request_t *breq); +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); @@ -427,7 +427,7 @@ KS_DECLARE(ks_status_t) blade_module_wss_on_startup(blade_module_t *bm, config_s blade_handle_transport_register(bm_wss->handle, bm, BLADE_MODULE_WSS_TRANSPORT_NAME, bm_wss->transport_callbacks); - blade_space_create(&space, bm_wss->handle, "blade.test"); + blade_space_create(&space, bm_wss->handle, bm, "blade.test"); ks_assert(space); blade_method_create(&method, space, "echo", blade_test_echo_request_handler); @@ -1248,9 +1248,10 @@ blade_connection_state_hook_t blade_transport_wss_on_state_ready_inbound(blade_c { ks_assert(bc); - ks_log(KS_LOG_DEBUG, "State Callback: %d\n", (int32_t)condition); + if (condition == BLADE_CONNECTION_STATE_CONDITION_PRE) { + ks_log(KS_LOG_DEBUG, "State Callback: %d\n", (int32_t)condition); + } - ks_sleep_ms(1000); return BLADE_CONNECTION_STATE_HOOK_SUCCESS; } @@ -1258,12 +1259,12 @@ blade_connection_state_hook_t blade_transport_wss_on_state_ready_outbound(blade_ { ks_assert(bc); - ks_log(KS_LOG_DEBUG, "State Callback: %d\n", (int32_t)condition); - 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); @@ -1273,17 +1274,17 @@ blade_connection_state_hook_t blade_transport_wss_on_state_ready_outbound(blade_ blade_session_read_unlock(bs); } - ks_sleep_ms(1000); return BLADE_CONNECTION_STATE_HOOK_SUCCESS; } -ks_bool_t blade_test_echo_request_handler(blade_request_t *breq) +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"); diff --git a/libs/libblade/src/blade_protocol.c b/libs/libblade/src/blade_protocol.c index 092c31f237..2752ce9fff 100644 --- a/libs/libblade/src/blade_protocol.c +++ b/libs/libblade/src/blade_protocol.c @@ -55,7 +55,7 @@ KS_DECLARE(ks_status_t) blade_request_create(blade_request_t **breqP, breq->pool = pool; breq->session_id = ks_pstrdup(pool, session_id); breq->message = cJSON_Duplicate(json, 1); - breq->message_id = cJSON_GetObjectCstr(json, "id"); + breq->message_id = cJSON_GetObjectCstr(breq->message, "id"); breq->callback = callback; *breqP = breq; @@ -81,7 +81,11 @@ 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_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; @@ -125,6 +129,50 @@ KS_DECLARE(ks_status_t) blade_response_destroy(blade_response_t **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; @@ -208,6 +256,35 @@ KS_DECLARE(ks_status_t) blade_rpc_error_create(ks_pool_t *pool, cJSON **json, cJ 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 diff --git a/libs/libblade/src/blade_session.c b/libs/libblade/src/blade_session.c index 048baec183..f284d90045 100644 --- a/libs/libblade/src/blade_session.c +++ b/libs/libblade/src/blade_session.c @@ -48,6 +48,9 @@ struct blade_session_s { 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); @@ -83,6 +86,11 @@ KS_DECLARE(ks_status_t) blade_session_create(blade_session_t **bsP, blade_handle 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"); @@ -101,6 +109,10 @@ KS_DECLARE(ks_status_t) blade_session_destroy(blade_session_t **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); @@ -174,6 +186,13 @@ KS_DECLARE(blade_handle_t *) blade_session_handle_get(blade_session_t *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); @@ -190,6 +209,20 @@ KS_DECLARE(void) blade_session_id_set(blade_session_t *bs, const char *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; @@ -227,11 +260,50 @@ 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_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) @@ -387,6 +459,8 @@ void *blade_session_state_thread(ks_thread_t *thread, void *data) } } + 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); @@ -468,7 +542,7 @@ ks_status_t blade_session_state_on_ready(blade_session_t *bs) ks_assert(bs); - ks_log(KS_LOG_DEBUG, "Session (%s) state ready\n", bs->id); + //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) { @@ -479,7 +553,7 @@ ks_status_t blade_session_state_on_ready(blade_session_t *bs) } } - ks_sleep_ms(1000); + ks_sleep_ms(1); return KS_STATUS_SUCCESS; } @@ -495,7 +569,15 @@ KS_DECLARE(ks_status_t) blade_session_send(blade_session_t *bs, cJSON *json, bla method = cJSON_GetObjectCstr(json, "method"); id = cJSON_GetObjectCstr(json, "id"); - if (method) { + 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); @@ -529,7 +611,10 @@ 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; @@ -548,73 +633,99 @@ ks_status_t blade_session_process(blade_session_t *bs, cJSON *json) return KS_STATUS_FAIL; } - id = cJSON_GetObjectCstr(json, "id"); - if (!id) { - ks_log(KS_LOG_DEBUG, "Received message is missing 'id'\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"); } - 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, '.'); + 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); - ks_log(KS_LOG_DEBUG, "Session (%s) receiving request (%s) for %s\n", bs->id, id, method); + blade_event_create(&bev, bs->handle, bs->id, json); + ks_assert(bev); - if (!method_name || method_name == space_name) { - ks_pool_free(bs->pool, (void **)&space_name); - // @todo send error response, code = -32601 (method not found) - // @todo hangup session entirely? - return KS_STATUS_FAIL; + disconnect = callback(bev); + + blade_event_destroy(&bev); } - *method_name = '\0'; - method_name++; // @todo check if can be postfixed safely on previous assignment, can't recall - - tmp_space = blade_handle_space_lookup(bs->handle, space_name); - if (tmp_space) tmp_method = blade_space_methods_get(tmp_space, method_name); - - ks_pool_free(bs->pool, (void **)&space_name); - - if (!tmp_method) { - // @todo send error response, code = -32601 (method not found) - // @todo hangup session entirely? - 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(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) { + 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; } - blade_handle_requests_remove(breq); - blade_response_create(&bres, bs->handle, bs->id, breq, json); - ks_assert(bres); + 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, '.'); - disconnect = breq->callback(bres); + ks_log(KS_LOG_DEBUG, "Session (%s) receiving request (%s) for %s\n", bs->id, id, method); - blade_response_destroy(&bres); + 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) { diff --git a/libs/libblade/src/blade_space.c b/libs/libblade/src/blade_space.c index 09a8574c4c..33931c2453 100644 --- a/libs/libblade/src/blade_space.c +++ b/libs/libblade/src/blade_space.c @@ -36,13 +36,14 @@ 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, const char *path) +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; @@ -56,6 +57,7 @@ KS_DECLARE(ks_status_t) blade_space_create(blade_space_t **bsP, blade_handle_t * 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); @@ -101,6 +103,13 @@ KS_DECLARE(blade_handle_t *) blade_space_handle_get(blade_space_t *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); diff --git a/libs/libblade/src/blade_stack.c b/libs/libblade/src/blade_stack.c index 697a8d9feb..3ffb6ed65f 100644 --- a/libs/libblade/src/blade_stack.c +++ b/libs/libblade/src/blade_stack.c @@ -49,14 +49,21 @@ struct blade_handle_s { 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 }; @@ -70,7 +77,6 @@ struct blade_handle_transport_registration_s { 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, @@ -110,6 +116,59 @@ KS_DECLARE(ks_status_t) blade_handle_transport_registration_destroy(blade_handle } +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) { @@ -137,12 +196,17 @@ KS_DECLARE(ks_status_t) blade_handle_create(blade_handle_t **bhP, ks_pool_t *poo 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); - // @todo decide if this is uint32_t or uuid string, prefer uuid string to avoid needing another lock and variable for next id + 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); @@ -172,8 +236,10 @@ KS_DECLARE(ks_status_t) blade_handle_destroy(blade_handle_t **bhP) 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); @@ -267,6 +333,14 @@ KS_DECLARE(ks_status_t) blade_handle_shutdown(blade_handle_t *bh) // @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; @@ -409,6 +483,53 @@ KS_DECLARE(blade_space_t *) blade_handle_space_lookup(blade_handle_t *bh, const 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; @@ -512,10 +633,11 @@ KS_DECLARE(ks_status_t) blade_handle_connections_remove(blade_connection_t *bc) 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; @@ -569,6 +691,92 @@ KS_DECLARE(ks_status_t) blade_handle_sessions_remove(blade_session_t *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) { diff --git a/libs/libblade/src/include/blade_module.h b/libs/libblade/src/include/blade_module.h index 706a8e892c..e5fc558f16 100644 --- a/libs/libblade/src/include/blade_module.h +++ b/libs/libblade/src/include/blade_module.h @@ -1,23 +1,23 @@ /* * 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 @@ -38,6 +38,7 @@ 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 @@ -45,6 +46,11 @@ KS_DECLARE(ks_status_t) blade_module_wss_on_load(blade_module_t **bmP, blade_han 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 diff --git a/libs/libblade/src/include/blade_protocol.h b/libs/libblade/src/include/blade_protocol.h index 0d3b4c772f..cb4c6dff9b 100644 --- a/libs/libblade/src/include/blade_protocol.h +++ b/libs/libblade/src/include/blade_protocol.h @@ -44,9 +44,12 @@ KS_DECLARE(ks_status_t) blade_request_create(blade_request_t **breqP, 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 diff --git a/libs/libblade/src/include/blade_session.h b/libs/libblade/src/include/blade_session.h index e79fbf34b0..25c26b01ac 100644 --- a/libs/libblade/src/include/blade_session.h +++ b/libs/libblade/src/include/blade_session.h @@ -41,12 +41,19 @@ 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); diff --git a/libs/libblade/src/include/blade_space.h b/libs/libblade/src/include/blade_space.h index 65664fdba5..bf241e45c9 100644 --- a/libs/libblade/src/include/blade_space.h +++ b/libs/libblade/src/include/blade_space.h @@ -36,9 +36,10 @@ #include KS_BEGIN_EXTERN_C -KS_DECLARE(ks_status_t) blade_space_create(blade_space_t **bsP, blade_handle_t *bh, const char *path); +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); diff --git a/libs/libblade/src/include/blade_stack.h b/libs/libblade/src/include/blade_stack.h index 27b45bf93f..1e0bc134d9 100644 --- a/libs/libblade/src/include/blade_stack.h +++ b/libs/libblade/src/include/blade_stack.h @@ -55,6 +55,10 @@ 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); @@ -64,6 +68,10 @@ 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); diff --git a/libs/libblade/src/include/blade_types.h b/libs/libblade/src/include/blade_types.h index 14c908f857..e78c6dad0f 100644 --- a/libs/libblade/src/include/blade_types.h +++ b/libs/libblade/src/include/blade_types.h @@ -43,21 +43,24 @@ 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 ks_bool_t (*blade_request_callback_t)(blade_request_t *breq); -typedef ks_bool_t (*blade_response_callback_t)(blade_response_t *bres); - typedef struct blade_datastore_s blade_datastore_t; -typedef ks_bool_t (*blade_datastore_fetch_callback_t)(blade_datastore_t *bds, const void *data, uint32_t data_length, void *userdata); +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 { @@ -94,6 +97,11 @@ typedef enum { } 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, @@ -145,6 +153,8 @@ struct blade_transport_callbacks_s { 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; @@ -167,6 +177,14 @@ struct blade_response_s { 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 328a71ef25..514af0198c 100644 --- a/libs/libblade/test/Makefile.am +++ b/libs/libblade/test/Makefile.am @@ -13,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 46bfda9969..ebc0384a21 100644 --- a/libs/libblade/test/bladec.c +++ b/libs/libblade/test/bladec.c @@ -26,22 +26,22 @@ 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_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 }, - { "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; @@ -50,16 +50,16 @@ int main(int argc, char **argv) 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); 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)); @@ -76,7 +76,7 @@ int main(int argc, char **argv) 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; @@ -91,8 +91,13 @@ int main(int argc, char **argv) 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); @@ -104,7 +109,37 @@ 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) { @@ -162,7 +197,7 @@ void loop(blade_handle_t *bh) void parse_argument(char **input, char **arg, char terminator) { char *tmp; - + ks_assert(input); ks_assert(*input); ks_assert(arg); @@ -183,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) { @@ -199,61 +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_store(blade_handle_t *bh, char *args) -{ - char *key; - char *data; - - ks_assert(args); - - 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); - - parse_argument(&args, &key, ' '); - - blade_handle_datastore_fetch(bh, blade_datastore_fetch_callback, key, strlen(key), bh); -} - void command_connect(blade_handle_t *bh, char *args) { blade_connection_t *bc = NULL; blade_identity_t *target = NULL; - + ks_assert(bh); ks_assert(args); blade_identity_create(&target, blade_handle_pool_get(bh)); - + if (blade_identity_parse(target, args) == KS_STATUS_SUCCESS) blade_handle_connect(bh, &bc, target, NULL); blade_identity_destroy(&target); } + +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 +{ + 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, &cmd, ' '); + ks_log(KS_LOG_DEBUG, "Chat Command: %s, Args: %s\n", cmd, args); + + if (!strcmp(cmd, "leave")) { + } else if (!strcmp(cmd, "send")) { + char *sid = NULL; + blade_session_t *bs = NULL; + cJSON *req = NULL; + cJSON *params = NULL; + + 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 index 2233b34608..14582ed3ab 100644 --- a/libs/libblade/test/bladec.cfg +++ b/libs/libblade/test/bladec.cfg @@ -1,9 +1,6 @@ blade: { - identity = "directory@domain"; - directory: - { - }; + identity = "peer@domain"; datastore: { database: @@ -11,18 +8,4 @@ blade: 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/libblade/test/bladec2.cfg b/libs/libblade/test/bladec2.cfg deleted file mode 100644 index 14582ed3ab..0000000000 --- a/libs/libblade/test/bladec2.cfg +++ /dev/null @@ -1,11 +0,0 @@ -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 + }; + }; +};