]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
FS-9952: Initial json rpc messaging
authorcolm <colm@freeswitch1>
Mon, 23 Jan 2017 19:30:19 +0000 (14:30 -0500)
committerMike Jerris <mike@jerris.com>
Wed, 22 Mar 2017 21:42:48 +0000 (17:42 -0400)
libs/libks/Makefile.am
libs/libks/src/include/ks_rpcmessage.h [new file with mode: 0644]
libs/libks/src/ks_rpcmessage.c [new file with mode: 0644]
libs/libks/test/Makefile.am

index 48a66c5ff4bb814d08b088c0aa85429e204e9139..8007ce6feb4c6bbfcc2fa52d35022cd0f98ccbf9 100644 (file)
@@ -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 (file)
index 0000000..45c3def
--- /dev/null
@@ -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 (file)
index 0000000..a28747e
--- /dev/null
@@ -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 <ks.h>
+#include <ks_rpcmessage.h>
+
+#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:
+ */
index a974af0062649a1120808d0b681bf9d048e9d570..0b85a468c4fa375f8036472bf26ba29340b878ec 100644 (file)
@@ -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)