diff --git a/.ci/after_success.sh b/.ci/after_success.sh
index cb41432..e256689 100755
--- a/.ci/after_success.sh
+++ b/.ci/after_success.sh
@@ -4,5 +4,5 @@ set -e
set -o pipefail
if [[ -n "${GCOV}" ]]; then
- coveralls -e test -e out/CMakeFiles/CompilerIdC/CMakeCCompilerId.c -e out/CMakeFiles/CompilerIdCXX/CMakeCXXCompilerId.cpp --gcov "$(which "${GCOV}")" --encoding iso-8859-1 -b out || echo 'coveralls upload failed.'
+ coveralls -e test -e build/CMakeFiles/CompilerIdC/CMakeCCompilerId.c -e build/CMakeFiles/CompilerIdCXX/CMakeCXXCompilerId.cpp --gcov "$(which "${GCOV}")" --encoding iso-8859-1 -b build || echo 'coveralls upload failed.'
fi
diff --git a/.ci/before_install.sh b/.ci/before_install.sh
new file mode 100755
index 0000000..1bdd89d
--- /dev/null
+++ b/.ci/before_install.sh
@@ -0,0 +1,28 @@
+#!/usr/bin/env bash
+
+set -e
+set -o pipefail
+
+if [[ -n "${CI_TARGET}" ]]; then
+ exit
+fi
+
+if [[ "${TRAVIS_OS_NAME}" == osx ]]; then
+ brew update
+fi
+
+echo "Upgrade Python 2's pip."
+pip2.7 -q install --user --upgrade pip
+
+if [[ "${TRAVIS_OS_NAME}" == osx ]]; then
+ echo "Install Python 3."
+ brew install python3
+ echo "Upgrade Python 3's pip."
+ pip3 -q install --user --upgrade pip
+else
+ # TODO: Replace with upgrade when Travis gets python3-pip package.
+ echo "Install pip for Python 3."
+ curl -sSL https://bootstrap.pypa.io/get-pip.py -o "${HOME}/get-pip.py"
+ # After this, pip in PATH will refer to Python 3's pip.
+ python3.3 "${HOME}/get-pip.py" --user --upgrade
+fi
diff --git a/.ci/install.sh b/.ci/install.sh
new file mode 100755
index 0000000..7e1cb0a
--- /dev/null
+++ b/.ci/install.sh
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+
+set -e
+set -o pipefail
+
+if [[ -n "${CI_TARGET}" ]]; then
+ exit
+fi
+
+if [[ "${TRAVIS_OS_NAME}" == osx ]]; then
+ brew install gettext
+ brew reinstall -s libtool
+fi
+
+# Use default CC to avoid compilation problems when installing Python modules.
+echo "Install coveralls for Python 2."
+CC=cc pip2.7 -q install --user --upgrade cpp-coveralls
diff --git a/.travis.yml b/.travis.yml
index beec4b0..f4cc41a 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -7,7 +7,7 @@ matrix:
env: EXTRA_FLAGS="-DCLANG_ADDRESS_SANITIZER=ON"
- os: linux
compiler: gcc-5
- env: GCOV=gcov-5 EXTRA_FLAGS="-DUSE_COVERAGE=ON"
+ env: GCOV=gcov-5 CMAKE_FLAGS="-DUSE_COVERAGE=ON"
before_install:
# cmocka
@@ -17,16 +17,9 @@ before_install:
- cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr .. && make -j2 && sudo make install
- cd ../..
- rm -rf cmocka-1.0.1
- # libuv
- - curl -L https://github.com/libuv/libuv/archive/v1.8.0.tar.gz | tar xzf -
- - (cd libuv-1.8.0 && ./autogen.sh && ./configure --prefix=/usr && make && sudo make install)
- - rm -rf libuv-1.8.0
- # msgpack-c
- - curl -L https://github.com/msgpack/msgpack-c/archive/cpp-1.4.1.tar.gz | tar xzf -
- - (cd msgpack-c-cpp-1.4.1 && ./bootstrap && ./configure --prefix=/usr && make && sudo make install)
- - rm -rf msgpack-c-cpp-1.4.1
- # coveralls
- - pip install --user cpp-coveralls
+ - .ci/before_install.sh
+
+install: .ci/install.sh
addons:
apt:
@@ -49,9 +42,10 @@ addons:
- llvm-3.4-dev
- ninja-build
- pkg-config
+ - python3.3-dev
- libbsd-dev
- libhiredis-dev
before_script: redis-server redis-test.conf
-script: make sb && make test && make sb-makekey && ./out/bin/sb-makekey && ./out/bin/sb-test
+script: make sb && make test && make sb-makekey && ./build/bin/sb-makekey && ./build/bin/sb-test
after_success: .ci/after_success.sh
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 75973e6..a0c15b6 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -7,6 +7,16 @@ option(CLANG_MEMORY_SANITIZER "Enable clang memory sanitizer." OFF)
option(CLANG_THREAD_SANITIZER "Enable clang thread sanitizer." OFF)
option(CLANG_ANALYZER "Enable clang static analyzer." OFF)
+# Prefer our bundled versions of dependencies.
+set(DEPS_PREFIX "${CMAKE_CURRENT_SOURCE_DIR}/.deps/usr" CACHE PATH "Path prefix for finding dependencies")
+if(CMAKE_CROSSCOMPILING AND NOT UNIX)
+ list(INSERT CMAKE_FIND_ROOT_PATH 0 ${DEPS_PREFIX})
+ list(INSERT CMAKE_PREFIX_PATH 0 ${DEPS_PREFIX}/../host/bin)
+else()
+ list(INSERT CMAKE_PREFIX_PATH 0 ${DEPS_PREFIX})
+ set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:${DEPS_PREFIX}/lib/pkgconfig")
+endif()
+
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
if(NOT CMAKE_BUILD_TYPE)
@@ -97,8 +107,10 @@ file(GLOB MANPAGES
# splonebox target sources
set(SPLONEBOX-SOURCES
src/main.c
+ src/main.h
src/sb-common.h
src/khash.h
+ src/klist.h
src/kvec.h
src/queue.h
src/string.c
@@ -123,13 +135,21 @@ set(SPLONEBOX-SOURCES
src/address.c
src/address.h
src/sbmemzero.c
+ src/mem.c
+ src/mem.h
src/api/sb-api.h
src/api/register.c
src/api/result.c
src/api/run.c
+ src/api/broadcast.c
+ src/api/subscribe.c
+ src/api/unsubscribe.c
+ src/api/helpers.c
+ src/api/helpers.h
src/rpc/sb-rpc.h
src/rpc/connection/event.c
src/rpc/connection/event.h
+ src/rpc/connection/event-defs.h
src/rpc/connection/streamhandle.c
src/rpc/connection/streamhandle.h
src/rpc/connection/inputstream.c
@@ -144,10 +164,9 @@ set(SPLONEBOX-SOURCES
src/rpc/connection/crypto.c
src/rpc/connection/crypto.h
src/rpc/connection/loop.c
- src/rpc/msgpack/sb-msgpack-rpc.h
- src/rpc/msgpack/message.c
- src/rpc/msgpack/pack.c
- src/rpc/msgpack/unpack.c
+ src/rpc/connection/loop.h
+ src/rpc/msgpack/helpers.c
+ src/rpc/msgpack/helpers.h
src/rpc/db/sb-db.h
src/rpc/db/connect.c
src/rpc/db/plugin.c
@@ -159,6 +178,8 @@ set(SPLONEBOX-SOURCES
set(SB-PLUGINKEY-SOURCES
src/sb-pluginkey.c
src/sb-common.h
+ src/mem.c
+ src/mem.h
src/rpc/sb-rpc.h
src/util.c
src/confparse.c
@@ -170,6 +191,8 @@ set(SB-PLUGINKEY-SOURCES
# sb-makekey target sources
set(SB-MAKEKEY-SOURCES
src/sb-common.h
+ src/mem.c
+ src/mem.h
src/reallocarray.c
src/tweetnacl.c
src/tweetnacl.h
@@ -180,8 +203,10 @@ set(SB-MAKEKEY-SOURCES
# splonebox test(s) sources
set(TEST-SOURCES
+ src/main.h
src/sb-common.h
src/khash.h
+ src/klist.h
src/queue.h
src/string.c
src/reallocarray.c
@@ -202,6 +227,8 @@ set(TEST-SOURCES
src/parse.h
src/util.c
src/util.h
+ src/mem.c
+ src/mem.h
src/address.c
src/address.h
src/sbmemzero.c
@@ -209,9 +236,15 @@ set(TEST-SOURCES
src/api/register.c
src/api/run.c
src/api/result.c
+ src/api/broadcast.c
+ src/api/subscribe.c
+ src/api/unsubscribe.c
+ src/api/helpers.c
+ src/api/helpers.h
src/rpc/sb-rpc.h
src/rpc/connection/event.c
src/rpc/connection/event.h
+ src/rpc/connection/event-defs.h
src/rpc/connection/streamhandle.c
src/rpc/connection/streamhandle.h
src/rpc/connection/inputstream.c
@@ -226,10 +259,9 @@ set(TEST-SOURCES
src/rpc/connection/crypto.c
src/rpc/connection/crypto.h
src/rpc/connection/loop.c
- src/rpc/msgpack/sb-msgpack-rpc.h
- src/rpc/msgpack/message.c
- src/rpc/msgpack/pack.c
- src/rpc/msgpack/unpack.c
+ src/rpc/connection/loop.h
+ src/rpc/msgpack/helpers.c
+ src/rpc/msgpack/helpers.h
src/rpc/db/sb-db.h
src/rpc/db/connect.c
src/rpc/db/plugin.c
@@ -245,28 +277,7 @@ set(TEST-SOURCES
test/helper-validate.h
test/unit/server-start.c
test/unit/server-stop.c
- test/unit/pack-string.c
- test/unit/pack-int.c
- test/unit/pack-uint.c
- test/unit/pack-array.c
- test/unit/pack-nil.c
- test/unit/pack-float.c
- test/unit/pack-bool.c
- test/unit/regression-issue-60.c
- test/unit/unpack-string.c
- test/unit/unpack-uint.c
- test/unit/unpack-array.c
test/unit/dispatch-table-get.c
- test/unit/event-queue-put.c
- test/unit/event-queue-get.c
- test/unit/message-deserialize-request.c
- test/unit/message-deserialize-response.c
- test/unit/message-deserialize-error-response.c
- test/unit/message-serialize-request.c
- test/unit/message-serialize-response.c
- test/unit/message-serialize-error-response.c
- test/unit/message-is-request.c
- test/unit/message-is-response.c
test/functional/db-connect.c
test/functional/db-plugin-add.c
test/functional/db-pluginkey-verify.c
@@ -278,9 +289,12 @@ set(TEST-SOURCES
test/functional/dispatch-handle-register.c
test/functional/dispatch-handle-run.c
test/functional/dispatch-handle-result.c
+ test/functional/dispatch-handle-subscribe.c
+ test/functional/dispatch-handle-broadcast.c
test/functional/crypto.c
test/functional/confparse.c
test/functional/db-whitelist.c
+ test/functional/msgpack-rpc-helper.c
)
if(CLANG_ADDRESS_SANITIZER OR CLANG_MEMORY_SANITIZER OR CLANG_TSAN)
@@ -289,7 +303,11 @@ endif()
include_directories(${PROJECT_SOURCE_DIR}/src)
include_directories(${PROJECT_SOURCE_DIR}/test)
-include_directories(${BSD_INCLUDE_DIRS} ${LIBUV_INCLUDE_DIRS} ${MSGPACK_INCLUDE_DIRS} ${HIREDIS_INCLUDE_DIRS} ${CMOCKA_INCLUDE_DIRS})
+include_directories(SYSTEM ${BSD_INCLUDE_DIRS})
+include_directories(SYSTEM ${LIBUV_INCLUDE_DIRS})
+include_directories(SYSTEM ${MSGPACK_INCLUDE_DIRS})
+include_directories(SYSTEM ${HIREDIS_INCLUDE_DIRS})
+include_directories(SYSTEM ${CMOCKA_INCLUDE_DIRS})
# sb target
add_executable(sb ${SPLONEBOX-SOURCES})
@@ -311,7 +329,7 @@ add_executable(sb-test ${TEST-SOURCES})
add_executable(sb-pluginkey ${SB-PLUGINKEY-SOURCES})
# wrap some functions for testing
-set_property(TARGET sb-test APPEND_STRING PROPERTY LINK_FLAGS "-Wl,--wrap=outputstream_write,--wrap=loop_wait_for_response,--wrap=crypto_write ")
+set_property(TARGET sb-test APPEND_STRING PROPERTY LINK_FLAGS "-Wl,--wrap=outputstream_write,--wrap=loop_process_events_until,--wrap=crypto_write ")
set_property(TARGET sb-test APPEND_STRING PROPERTY COMPILE_FLAGS "-DBOX_UNIT_TESTS ")
target_link_libraries(sb-test
diff --git a/Makefile b/Makefile
index a19af4e..0f8ab3e 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
BUILD_DIR := out
CMAKE_BUILD_TYPE ?= Debug
-FLAGS := -DCMAKE_BUILD_TYPE=$(CMAKE_BUILD_TYPE)
+CMAKE_FLAGS := -DCMAKE_BUILD_TYPE=$(CMAKE_BUILD_TYPE)
EXTRA_FLAGS ?=
BUILD_TYPE ?= $(shell (type ninja > /dev/null 2>&1 && echo "Ninja") || echo "Unix Makefiles")
@@ -26,35 +26,59 @@ endif
BUILD_CMD = $(BUILD_TOOL) $(VERBOSE_FLAG)
+# Extra CMake flags which extend the default set
+CMAKE_EXTRA_FLAGS ?=
+USE_BUNDLED_DEPS ?=
+DEPS_CMAKE_FLAGS ?=
+
+ifneq (,$(USE_BUNDLED_DEPS))
+ BUNDLED_CMAKE_FLAG := -DUSE_BUNDLED=$(USE_BUNDLED_DEPS)
+endif
+
all: sb test sb-makekey sb-pluginkey
-sb:
- test -d $(BUILD_DIR) || mkdir $(BUILD_DIR)
- cd out && cmake -G '$(BUILD_TYPE)' $(FLAGS) $(EXTRA_FLAGS) ..
- $(BUILD_CMD) -C out sb
+sb: build/.ran-cmake deps
+ +$(BUILD_CMD) -C build sb
+
+cmake:
+ touch CMakeLists.txt
+ $(MAKE) build/.ran-cmake
+
+build/.ran-cmake: | deps
+ cd build && cmake -G '$(BUILD_TYPE)' $(CMAKE_FLAGS) $(CMAKE_EXTRA_FLAGS) ..
+ touch $@
+
+deps: | build/.ran-third-party-cmake
+ifeq ($(call filter-true,$(USE_BUNDLED_DEPS)),)
+ +$(BUILD_CMD) -C .deps
+endif
+
+build/.ran-third-party-cmake:
+ifeq ($(call filter-true,$(USE_BUNDLED_DEPS)),)
+ mkdir -p .deps
+ cd .deps && \
+ cmake -G '$(BUILD_TYPE)' $(BUNDLED_CMAKE_FLAG) $(BUNDLED_LUA_CMAKE_FLAG) \
+ $(DEPS_CMAKE_FLAGS) ../third-party
+endif
+ mkdir -p build
+ touch $@
-test:
- test -d $(BUILD_DIR) || mkdir $(BUILD_DIR)
- cd out && cmake -G '$(BUILD_TYPE)' $(FLAGS) $(EXTRA_FLAGS) ..
- $(BUILD_CMD) -C out sb-test
+test: build/.ran-cmake deps
+ +$(BUILD_CMD) -C build sb-test
-sb-makekey:
- test -d $(BUILD_DIR) || mkdir $(BUILD_DIR)
- cd out && cmake -G '$(BUILD_TYPE)' $(FLAGS) $(EXTRA_FLAGS) ..
- $(BUILD_CMD) -C out sb-makekey
+sb-makekey: build/.ran-cmake deps
+ +$(BUILD_CMD) -C build sb-makekey
-sb-pluginkey:
- test -d $(BUILD_DIR) || mkdir $(BUILD_DIR)
- cd out && cmake -G '$(BUILD_TYPE)' $(FLAGS) $(EXTRA_FLAGS) ..
- $(BUILD_CMD) -C out sb-pluginkey
+sb-pluginkey: build/.ran-cmake deps
+ +$(BUILD_CMD) -C build sb-pluginkey
clean:
- +test -d out && $(BUILD_CMD) -C out clean || true
+ +test -d build && $(BUILD_CMD) -C build clean || true
distclean: clean
- rm -rf out
+ rm -rf .deps build
install: | sb
+$(BUILD_CMD) -C out install
-.PHONY: test clean distclean sb install sb-makekey
+.PHONY: test clean distclean sb install sb-makekey deps cmake
diff --git a/cmake/FindLIBUV.cmake b/cmake/FindLIBUV.cmake
index 5affbc2..dcdd5e4 100644
--- a/cmake/FindLIBUV.cmake
+++ b/cmake/FindLIBUV.cmake
@@ -1,17 +1,108 @@
-find_package(PkgConfig)
-if (PKG_CONFIG_FOUND)
- pkg_check_modules(SHARED_LIBUV REQUIRED libuv)
+# - Try to find libuv
+# Once done, this will define
+#
+# LIBUV_FOUND - system has libuv
+# LIBUV_INCLUDE_DIRS - the libuv include directories
+# LIBUV_LIBRARIES - link these to use libuv
+#
+# Set the LIBUV_USE_STATIC variable to specify if static libraries should
+# be preferred to shared ones.
+
+if(NOT LIBUV_USE_BUNDLED)
+ find_package(PkgConfig)
+ if (PKG_CONFIG_FOUND)
+ pkg_check_modules(PC_LIBUV QUIET libuv)
+ endif()
+else()
+ set(PC_LIBUV_INCLUDEDIR)
+ set(PC_LIBUV_INCLUDE_DIRS)
+ set(PC_LIBUV_LIBDIR)
+ set(PC_LIBUV_LIBRARY_DIRS)
+ set(LIMIT_SEARCH NO_DEFAULT_PATH)
endif()
find_path(LIBUV_INCLUDE_DIR uv.h
- HINTS ${SHARED_LIBUV_INCLUDEDIR} ${SHARED_LIBUV_INCLUDE_DIRS}
+ HINTS ${PC_LIBUV_INCLUDEDIR} ${PC_LIBUV_INCLUDE_DIRS}
${LIMIT_SEARCH})
-find_library(LIBUV_LIBRARY NAMES uv
- HINTS ${SHARED_LIBUV_LIBDIR} ${SHARED_LIBUV_LIBRARY_DIRS}
+# If we're asked to use static linkage, add libuv.a as a preferred library name.
+if(LIBUV_USE_STATIC)
+ list(APPEND LIBUV_NAMES
+ "${CMAKE_STATIC_LIBRARY_PREFIX}uv${CMAKE_STATIC_LIBRARY_SUFFIX}")
+endif(LIBUV_USE_STATIC)
+
+if(MSVC)
+ list(APPEND LIBUV_NAMES libuv)
+else()
+ list(APPEND LIBUV_NAMES uv)
+endif()
+
+find_library(LIBUV_LIBRARY NAMES ${LIBUV_NAMES}
+ HINTS ${PC_LIBUV_LIBDIR} ${PC_LIBUV_LIBRARY_DIRS}
${LIMIT_SEARCH})
mark_as_advanced(LIBUV_INCLUDE_DIR LIBUV_LIBRARY)
-set(LIBUV_LIBRARIES ${LIBUV_LIBRARY} ${SHARED_LIBUV_LIBRARIES})
+if(PC_LIBUV_LIBRARIES)
+ list(REMOVE_ITEM PC_LIBUV_LIBRARIES uv)
+endif()
+
+set(LIBUV_LIBRARIES ${LIBUV_LIBRARY} ${PC_LIBUV_LIBRARIES})
set(LIBUV_INCLUDE_DIRS ${LIBUV_INCLUDE_DIR})
+
+# Deal with the fact that libuv.pc is missing important dependency information.
+
+include(CheckLibraryExists)
+
+check_library_exists(dl dlopen "dlfcn.h" HAVE_LIBDL)
+if(HAVE_LIBDL)
+ list(APPEND LIBUV_LIBRARIES dl)
+endif()
+
+check_library_exists(kstat kstat_lookup "kstat.h" HAVE_LIBKSTAT)
+if(HAVE_LIBKSTAT)
+ list(APPEND LIBUV_LIBRARIES kstat)
+endif()
+
+check_library_exists(kvm kvm_open "kvm.h" HAVE_LIBKVM)
+if(HAVE_LIBKVM)
+ list(APPEND LIBUV_LIBRARIES kvm)
+endif()
+
+check_library_exists(nsl gethostbyname "nsl.h" HAVE_LIBNSL)
+if(HAVE_LIBNSL)
+ list(APPEND LIBUV_LIBRARIES nsl)
+endif()
+
+check_library_exists(perfstat perfstat_cpu "libperfstat.h" HAVE_LIBPERFSTAT)
+if(HAVE_LIBPERFSTAT)
+ list(APPEND LIBUV_LIBRARIES perfstat)
+endif()
+
+check_library_exists(rt clock_gettime "time.h" HAVE_LIBRT)
+if(HAVE_LIBRT)
+ list(APPEND LIBUV_LIBRARIES rt)
+endif()
+
+check_library_exists(sendfile sendfile "" HAVE_LIBSENDFILE)
+if(HAVE_LIBSENDFILE)
+ list(APPEND LIBUV_LIBRARIES sendfile)
+endif()
+
+if(WIN32)
+ # check_library_exists() does not work for Win32 API calls in X86 due to name
+ # mangling calling conventions
+ list(APPEND LIBUV_LIBRARIES iphlpapi)
+ list(APPEND LIBUV_LIBRARIES psapi)
+ list(APPEND LIBUV_LIBRARIES userenv)
+ list(APPEND LIBUV_LIBRARIES ws2_32)
+endif()
+
+include(FindPackageHandleStandardArgs)
+
+# handle the QUIETLY and REQUIRED arguments and set LIBUV_FOUND to TRUE
+# if all listed variables are TRUE
+find_package_handle_standard_args(LibUV DEFAULT_MSG
+ LIBUV_LIBRARY LIBUV_INCLUDE_DIR)
+
+mark_as_advanced(LIBUV_INCLUDE_DIR LIBUV_LIBRARY)
diff --git a/cmake/FindMSGPACK.cmake b/cmake/FindMSGPACK.cmake
index ac4006e..1774f59 100644
--- a/cmake/FindMSGPACK.cmake
+++ b/cmake/FindMSGPACK.cmake
@@ -1,21 +1,63 @@
-find_package(PkgConfig)
-if (PKG_CONFIG_FOUND)
- pkg_search_module(SHARED_MSGPACK QUIET
+if(NOT MSGPACK_USE_BUNDLED)
+ find_package(PkgConfig)
+ if (PKG_CONFIG_FOUND)
+ pkg_search_module(PC_MSGPACK QUIET
msgpackc>=${Msgpack_FIND_VERSION}
msgpack>=${Msgpack_FIND_VERSION})
+ endif()
+else()
+ set(PC_MSGPACK_INCLUDEDIR)
+ set(PC_MSGPACK_INCLUDE_DIRS)
+ set(PC_MSGPACK_LIBDIR)
+ set(PC_MSGPACK_LIBRARY_DIRS)
+ set(LIMIT_SEARCH NO_DEFAULT_PATH)
endif()
-find_path(MSGPACK_INCLUDE_DIR msgpack.h
- HINTS ${SHARED_MSGPACK_INCLUDEDIR} ${SHARED_MSGPACK_INCLUDE_DIRS}
+set(MSGPACK_DEFINITIONS ${PC_MSGPACK_CFLAGS_OTHER})
+
+find_path(MSGPACK_INCLUDE_DIR msgpack/version_master.h
+ HINTS ${PC_MSGPACK_INCLUDEDIR} ${PC_MSGPACK_INCLUDE_DIRS}
${LIMIT_SEARCH})
-
-list(APPEND MSGPACK_NAMES msgpackc msgpack)
+
+if(MSGPACK_INCLUDE_DIR)
+ file(READ ${MSGPACK_INCLUDE_DIR}/msgpack/version_master.h msgpack_version_h)
+ string(REGEX REPLACE ".*MSGPACK_VERSION_MAJOR +([0-9]+).*" "\\1" MSGPACK_VERSION_MAJOR "${msgpack_version_h}")
+ string(REGEX REPLACE ".*MSGPACK_VERSION_MINOR +([0-9]+).*" "\\1" MSGPACK_VERSION_MINOR "${msgpack_version_h}")
+ string(REGEX REPLACE ".*MSGPACK_VERSION_REVISION +([0-9]+).*" "\\1" MSGPACK_VERSION_REVISION "${msgpack_version_h}")
+ set(MSGPACK_VERSION_STRING "${MSGPACK_VERSION_MAJOR}.${MSGPACK_VERSION_MINOR}.${MSGPACK_VERSION_REVISION}")
+else()
+ set(MSGPACK_VERSION_STRING)
+endif()
+
+# If we're asked to use static linkage, add libmsgpack{,c}.a as a preferred library name.
+if(MSGPACK_USE_STATIC)
+ list(APPEND MSGPACK_NAMES
+ "${CMAKE_STATIC_LIBRARY_PREFIX}msgpackc${CMAKE_STATIC_LIBRARY_SUFFIX}"
+ "${CMAKE_STATIC_LIBRARY_PREFIX}msgpack${CMAKE_STATIC_LIBRARY_SUFFIX}")
+endif()
+
+if(MSVC)
+ # The import library for the msgpack DLL has a different name
+ list(APPEND MSGPACK_NAMES msgpack_import)
+else()
+ list(APPEND MSGPACK_NAMES msgpackc msgpack)
+endif()
find_library(MSGPACK_LIBRARY NAMES ${MSGPACK_NAMES}
- HINTS ${SHARED_MSGPACK_LIBDIR} ${SHARED_MSGPACK_LIBRARY_DIRS}
+ # Check each directory for all names to avoid using headers/libraries from
+ # different places.
+ NAMES_PER_DIR
+ HINTS ${PC_MSGPACK_LIBDIR} ${PC_MSGPACK_LIBRARY_DIRS}
${LIMIT_SEARCH})
mark_as_advanced(MSGPACK_INCLUDE_DIR MSGPACK_LIBRARY)
set(MSGPACK_LIBRARIES ${MSGPACK_LIBRARY})
set(MSGPACK_INCLUDE_DIRS ${MSGPACK_INCLUDE_DIR})
+
+include(FindPackageHandleStandardArgs)
+# handle the QUIETLY and REQUIRED arguments and set MSGPACK_FOUND to TRUE
+# if all listed variables are TRUE
+find_package_handle_standard_args(Msgpack
+ REQUIRED_VARS MSGPACK_LIBRARY MSGPACK_INCLUDE_DIR
+ VERSION_VAR MSGPACK_VERSION_STRING)
diff --git a/test/unit/pack-bool.c b/src/api/broadcast.c
similarity index 59%
rename from test/unit/pack-bool.c
rename to src/api/broadcast.c
index 4b31bca..22cf640 100644
--- a/test/unit/pack-bool.c
+++ b/src/api/broadcast.c
@@ -14,24 +14,25 @@
* along with this program. If not, see .
*/
-#include
+#include
+#include
-#include "sb-common.h"
-#include "rpc/msgpack/sb-msgpack-rpc.h"
-#include "helper-unix.h"
+#include "rpc/db/sb-db.h"
+#include "api/sb-api.h"
+#include "api/helpers.h"
-void unit_pack_bool(UNUSED(void **state))
+int api_broadcast(string event, array args, struct api_error *api_error)
{
- msgpack_sbuffer sbuf;
- msgpack_packer pk;
+ object o = copy_object(ARRAY_OBJ(args));
- msgpack_sbuffer_init(&sbuf);
- msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
+ sbassert(api_error);
- assert_int_equal(0, pack_bool(&pk, true));
- assert_int_equal(0, pack_bool(&pk, false));
- assert_int_not_equal(0, pack_bool(NULL, true));
+ if (!connection_send_event(0, event.str, o.data.array)) {
+ error_set(api_error, API_ERROR_TYPE_VALIDATION,
+ "Broadcasting event failed");
+ return -1;
+ }
- msgpack_sbuffer_destroy(&sbuf);
+ return 0;
}
diff --git a/src/api/helpers.c b/src/api/helpers.c
new file mode 100644
index 0000000..96d75ab
--- /dev/null
+++ b/src/api/helpers.c
@@ -0,0 +1,127 @@
+/**
+ * Copyright (C) 2015 splone UG
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ * This file incorporates code covered by the following terms:
+ *
+ * Copyright Neovim contributors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ #include
+ #include
+
+ #include "api/sb-api.h"
+ #include "api/helpers.h"
+ #include "sb-common.h"
+
+void api_free_string(string value)
+{
+ if (!value.str) {
+ return;
+ }
+
+ FREE(value.str);
+}
+
+void api_free_object(object value)
+{
+ switch (value.type) {
+ case OBJECT_TYPE_NIL:
+ case OBJECT_TYPE_BOOL:
+ case OBJECT_TYPE_INT:
+ case OBJECT_TYPE_UINT:
+ case OBJECT_TYPE_FLOAT:
+ break;
+
+ case OBJECT_TYPE_STR:
+ api_free_string(value.data.string);
+ break;
+
+ case OBJECT_TYPE_ARRAY:
+ api_free_array(value.data.array);
+ break;
+
+ case OBJECT_TYPE_DICTIONARY:
+ api_free_dictionary(value.data.dictionary);
+ break;
+
+ default:
+ abort();
+ }
+}
+
+void api_free_array(array value)
+{
+ for (size_t i = 0; i < value.size; i++) {
+ api_free_object(value.items[i]);
+ }
+
+ FREE(value.items);
+}
+
+void api_free_dictionary(dictionary value)
+{
+ for (size_t i = 0; i < value.size; i++) {
+ api_free_string(value.items[i].key);
+ api_free_object(value.items[i].value);
+ }
+
+ FREE(value.items);
+}
+
+object copy_object(object obj)
+{
+ switch (obj.type) {
+ case OBJECT_TYPE_NIL:
+ case OBJECT_TYPE_BOOL:
+ case OBJECT_TYPE_INT:
+ case OBJECT_TYPE_UINT:
+ case OBJECT_TYPE_FLOAT:
+ return obj;
+
+ case OBJECT_TYPE_STR:
+ return STRING_OBJ(cstring_copy_string(obj.data.string.str));
+
+ case OBJECT_TYPE_ARRAY: {
+ array rv = ARRAY_DICT_INIT;
+ for (size_t i = 0; i < obj.data.array.size; i++) {
+ ADD(rv, copy_object(obj.data.array.items[i]));
+ }
+ return ARRAY_OBJ(rv);
+ }
+
+ case OBJECT_TYPE_DICTIONARY: {
+ dictionary rv = ARRAY_DICT_INIT;
+ for (size_t i = 0; i < obj.data.dictionary.size; i++) {
+ key_value_pair item = obj.data.dictionary.items[i];
+ PUT(rv, item.key.str, copy_object(item.value));
+ }
+ return DICTIONARY_OBJ(rv);
+ }
+ default:
+ abort();
+ }
+}
diff --git a/src/api/helpers.h b/src/api/helpers.h
new file mode 100644
index 0000000..e312a3b
--- /dev/null
+++ b/src/api/helpers.h
@@ -0,0 +1,45 @@
+#pragma once
+
+#include "sb-common.h"
+
+#define OBJECT_INIT { .type = OBJECT_TYPE_NIL }
+
+#define OBJECT_OBJ(o) o
+
+#define BOOLEAN_OBJ(b) ((object) { \
+ .type = OBJECT_TYPE_BOOL, \
+ .data.boolean = b })
+
+#define INTEGER_OBJ(i) ((object) { \
+ .type = OBJECT_TYPE_INT, \
+ .data.integer = i })
+
+#define UINTEGER_OBJ(i) ((object) { \
+ .type = OBJECT_TYPE_UINT, \
+ .data.uinteger = i })
+
+#define FLOATING_OBJ(f) ((object) { \
+ .type = OBJECT_TYPE_FLOAT, \
+ .data.floating = f })
+
+#define STRING_OBJ(s) ((object) { \
+ .type = OBJECT_TYPE_STR, \
+ .data.string = s })
+
+#define ARRAY_OBJ(a) ((object) { \
+ .type = OBJECT_TYPE_ARRAY, \
+ .data.array = a })
+
+#define DICTIONARY_OBJ(d) ((object) { \
+ .type = OBJECT_TYPE_DICTIONARY, \
+ .data.dictionary = d })
+
+#define NIL ((object) {.type = OBJECT_TYPE_NIL})
+
+#define PUT(dict, k, v) \
+ kv_push(dict, ((key_value_pair) { .key = cstring_copy_string(k), .value = v }))
+
+#define ADD(array, item) \
+ kv_push(array, item)
+
+#define STATIC_CSTR_AS_STRING(s) ((string) {.str = s, .length = sizeof(s) - 1})
diff --git a/src/api/register.c b/src/api/register.c
index ae2e91a..86613b1 100644
--- a/src/api/register.c
+++ b/src/api/register.c
@@ -19,13 +19,15 @@
#include "rpc/db/sb-db.h"
#include "api/sb-api.h"
+#include "api/helpers.h"
-int api_register(string name, string desc,
- string author, string license, array functions, uint64_t con_id,
- uint32_t msgid, char *pluginkey, struct api_error *api_error)
+int api_register(string name, string desc, string author, string license,
+ array functions, char *pluginkey, struct api_error *api_error)
{
- struct message_object *func;
- array params = ARRAY_INIT;
+ object *func;
+
+ sbassert(pluginkey);
+ sbassert(api_error);
if (functions.size == 0) {
error_set(api_error, API_ERROR_TYPE_VALIDATION,
@@ -40,7 +42,7 @@ int api_register(string name, string desc,
}
for (size_t i = 0; i < functions.size; i++) {
- func = &functions.obj[i];
+ func = &functions.items[i];
if (func->type != OBJECT_TYPE_ARRAY) {
error_set(api_error, API_ERROR_TYPE_VALIDATION,
@@ -48,7 +50,7 @@ int api_register(string name, string desc,
continue;
}
- if (db_function_add(pluginkey, &func->data.params) == -1) {
+ if (db_function_add(pluginkey, &func->data.array) == -1) {
error_set(api_error, API_ERROR_TYPE_VALIDATION,
"Failed to register function in database.");
continue;
@@ -58,9 +60,5 @@ int api_register(string name, string desc,
if (api_error->isset)
return (-1);
- if (connection_send_response(con_id, msgid, params, api_error) < 0) {
- return (-1);
- };
-
return (0);
}
diff --git a/src/api/result.c b/src/api/result.c
index 965e583..cd52dc2 100644
--- a/src/api/result.c
+++ b/src/api/result.c
@@ -18,83 +18,48 @@
#include
#include "api/sb-api.h"
+#include "api/helpers.h"
#include "sb-common.h"
-int api_result(char *targetpluginkey, uint64_t callid,
- struct message_object args, uint64_t con_id, uint32_t msgid,
+int api_result(char *targetpluginkey, uint64_t callid, array args,
struct api_error *api_error)
{
- struct message_object *data;
- struct message_object *meta;
- array result_params;
- array result_response_params;
string result;
- struct callinfo cinfo;
+ object res;
- if (!api_error)
- return (-1);
-
- result_params.size = 2;
- result_params.obj = CALLOC(2, struct message_object);
-
- if (!result_params.obj)
- return (-1);
+ sbassert(targetpluginkey);
+ sbassert(api_error);
- /* data refs to first result_params parameter */
- meta = &result_params.obj[0];
+ array meta = ARRAY_DICT_INIT;
+ ADD(meta, UINTEGER_OBJ(callid));
- meta->type = OBJECT_TYPE_ARRAY;
-
- /* meta = [nil, callid] */
- meta->data.params.size = 1;
- meta->data.params.obj = CALLOC(1, struct message_object);
-
- if (!meta->data.params.obj)
- return (-1);
-
- /* add callid */
- data = &meta->data.params.obj[0];
- data->type = OBJECT_TYPE_UINT;
- data->data.uinteger = callid;
-
- /* add function parameters, data refs to third result_params parameter */
- data = &result_params.obj[1];
-
- data->type = OBJECT_TYPE_ARRAY;
- data->data.params = message_object_copy(args).data.params;
+ array request = ARRAY_DICT_INIT;
+ ADD(request, ARRAY_OBJ(meta));
+ ADD(request, copy_object(ARRAY_OBJ(args)));
/* send request */
result = (string) {.str = "result", .length = sizeof("result") - 1};
- cinfo = connection_send_request(targetpluginkey, result, result_params,
+ res = connection_send_request(targetpluginkey, result, request,
api_error);
if (api_error->isset)
return (-1);
- if (cinfo.response.params.size != 1) {
+ if (res.data.array.size != 1) {
error_set(api_error, API_ERROR_TYPE_VALIDATION,
"Error dispatching result API response. Either response is broken "
"or it just has wrong params size.");
return (-1);
}
- if (!(cinfo.response.params.obj[0].type == OBJECT_TYPE_UINT &&
- callid == cinfo.response.params.obj[0].data.uinteger)) {
+ if (!(res.data.array.items[0].type == OBJECT_TYPE_UINT &&
+ callid == res.data.array.items[0].data.uinteger)) {
error_set(api_error, API_ERROR_TYPE_VALIDATION,
"Error dispatching run API response. Invalid callid");
return (-1);
}
- result_response_params.size = 1;
- result_response_params.obj = CALLOC(1, struct message_object);
- result_response_params.obj[0].type = OBJECT_TYPE_UINT;
- result_response_params.obj[0].data.uinteger = callid;
-
- if (connection_send_response(con_id, msgid, result_response_params, api_error) < 0) {
- return (-1);
- };
-
- free_params(cinfo.response.params);
+ api_free_array(res.data.array);
return (0);
}
diff --git a/src/api/run.c b/src/api/run.c
index 9f9b6d9..a3e7884 100644
--- a/src/api/run.c
+++ b/src/api/run.c
@@ -19,102 +19,60 @@
#include "rpc/db/sb-db.h"
#include "api/sb-api.h"
+#include "api/helpers.h"
+
int api_run(char *targetpluginkey, string function_name, uint64_t callid,
- struct message_object args, uint64_t con_id,
- uint32_t msgid, struct api_error *api_error)
+ array args, struct api_error *api_error)
{
- struct message_object *data;
- struct message_object *meta;
- array run_params;
- array run_response_params;
string run;
- struct callinfo cinfo;
+ object result;
+ array meta = ARRAY_DICT_INIT;
+ array request = ARRAY_DICT_INIT;
- if (!api_error)
- return (-1);
+ sbassert(api_error);
if (db_plugin_verify(targetpluginkey) == -1) {
error_set(api_error, API_ERROR_TYPE_VALIDATION, "API key is invalid.");
return (-1);
}
- if (db_function_verify(targetpluginkey, function_name, &args.data.params) == -1) {
+ if (db_function_verify(targetpluginkey, function_name, &args) == -1) {
error_set(api_error, API_ERROR_TYPE_VALIDATION,
"run() verification failed.");
return (-1);
}
- run_params.size = 3;
- run_params.obj = CALLOC(3, struct message_object);
-
- if (!run_params.obj)
- return (-1);
-
- /* data refs to first run_params parameter */
- meta = &run_params.obj[0];
-
- meta->type = OBJECT_TYPE_ARRAY;
-
- /* meta = [nil, callid] */
- meta->data.params.size = 2;
- meta->data.params.obj = CALLOC(2, struct message_object);
-
- if (!meta->data.params.obj)
- return (-1);
-
- /* add nil */
- data = &meta->data.params.obj[0];
- data->type = OBJECT_TYPE_NIL;
-
- /* add callid */
- data = &meta->data.params.obj[1];
- data->type = OBJECT_TYPE_UINT;
- data->data.uinteger = callid;
-
- /* add function name, data refs to second run_params parameter */
- data = &run_params.obj[1];
- data->type = OBJECT_TYPE_STR;
- data->data.string = cstring_copy_string(function_name.str);
+ ADD(meta, OBJECT_OBJ((object) OBJECT_INIT));
+ ADD(meta, UINTEGER_OBJ(callid));
- /* add function parameters, data refs to third run_params parameter */
- data = &run_params.obj[2];
-
- data->type = OBJECT_TYPE_ARRAY;
- data->data.params = message_object_copy(args).data.params;
+ ADD(request, ARRAY_OBJ(meta));
+ ADD(request, STRING_OBJ(cstring_copy_string(function_name.str)));
+ ADD(request, copy_object(ARRAY_OBJ(args)));
/* send request */
run = (string) {.str = "run", .length = sizeof("run") - 1};
- cinfo = connection_send_request(targetpluginkey, run, run_params,
+ result = connection_send_request(targetpluginkey, run, request,
api_error);
if (api_error->isset)
return (-1);
- if (cinfo.response.params.size != 1) {
+ if (result.data.array.size != 1) {
error_set(api_error, API_ERROR_TYPE_VALIDATION,
"Error dispatching run API response. Either response is broken "
"or it just has wrong params size.");
return (-1);
}
- if (!(cinfo.response.params.obj[0].type == OBJECT_TYPE_UINT &&
- callid == cinfo.response.params.obj[0].data.uinteger)) {
+ if (!(result.data.array.items[0].type == OBJECT_TYPE_UINT &&
+ callid == result.data.array.items[0].data.uinteger)) {
error_set(api_error, API_ERROR_TYPE_VALIDATION,
"Error dispatching run API response. Invalid callid");
return (-1);
}
- run_response_params.size = 1;
- run_response_params.obj = CALLOC(1, struct message_object);
- run_response_params.obj[0].type = OBJECT_TYPE_UINT;
- run_response_params.obj[0].data.uinteger = callid;
-
- if (connection_send_response(con_id, msgid, run_response_params, api_error) < 0) {
- return (-1);
- };
-
- free_params(cinfo.response.params);
+ api_free_array(result.data.array);
return (0);
}
diff --git a/src/api/sb-api.h b/src/api/sb-api.h
index 0bc16e1..0b464a9 100644
--- a/src/api/sb-api.h
+++ b/src/api/sb-api.h
@@ -34,8 +34,8 @@
* @return 0 in case of success otherwise 1
*/
int api_register(string name, string desc,
- string author, string license, array functions, uint64_t con_id,
- uint32_t msgid, char *pluginkey, struct api_error *api_error);
+ string author, string license, array functions, char *pluginkey,
+ struct api_error *api_error);
/**
* Run a plugin function
@@ -47,8 +47,7 @@
* @return 0 in case of success otherwise -1
*/
int api_run(char *targetpluginkey, string function_name, uint64_t callid,
- struct message_object args, uint64_t con_id, uint32_t msgid,
- struct api_error *api_error);
+ array args, struct api_error *api_error);
/**
* Generates an API key using /dev/urandom. The length of the key
@@ -56,8 +55,17 @@ int api_run(char *targetpluginkey, string function_name, uint64_t callid,
* @param[in] key string to store the API key.
* @return 0 in case of success otherwise -1
*/
+
+int api_broadcast(string eventname, array args, struct api_error *api_error);
+int api_subscribe(uint64_t id, string event, struct api_error *api_error);
+int api_unsubscribe(uint64_t id, string event, struct api_error *api_error);
int api_get_key(string key);
-int api_result(char *targetpluginkey, uint64_t callid,
- struct message_object args, uint64_t con_id, uint32_t msgid,
+int api_result(char *targetpluginkey, uint64_t callid, array args,
struct api_error *api_error);
+
+void api_free_string(string value);
+void api_free_object(object value);
+void api_free_array(array value);
+void api_free_dictionary(dictionary value);
+object copy_object(object obj);
diff --git a/test/unit/pack-float.c b/src/api/subscribe.c
similarity index 61%
rename from test/unit/pack-float.c
rename to src/api/subscribe.c
index f590c6d..0fe02a7 100644
--- a/test/unit/pack-float.c
+++ b/src/api/subscribe.c
@@ -14,23 +14,25 @@
* along with this program. If not, see .
*/
-#include
+#include
+#include
-#include "sb-common.h"
-#include "rpc/msgpack/sb-msgpack-rpc.h"
-#include "helper-unix.h"
+#include "rpc/db/sb-db.h"
+#include "api/sb-api.h"
+#include "api/helpers.h"
-void unit_pack_float(UNUSED(void **state))
+int api_subscribe(uint64_t con_id, string event, struct api_error *api_error)
{
- msgpack_sbuffer sbuf;
- msgpack_packer pk;
+ size_t length = (event.length < METHOD_MAXLEN ? event.length : METHOD_MAXLEN);
+ char e[METHOD_MAXLEN + 1];
- msgpack_sbuffer_init(&sbuf);
- msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
+ sbassert(api_error);
- assert_int_equal(0, pack_float(&pk, 1.234));
- assert_int_not_equal(0, pack_float(NULL, 1.234));
+ memcpy(e, event.str, length);
+ e[length] = '\000';
- msgpack_sbuffer_destroy(&sbuf);
+ connection_subscribe(con_id, e);
+
+ return 0;
}
diff --git a/test/unit/pack-string.c b/src/api/unsubscribe.c
similarity index 56%
rename from test/unit/pack-string.c
rename to src/api/unsubscribe.c
index 7079a6e..de758dc 100644
--- a/test/unit/pack-string.c
+++ b/src/api/unsubscribe.c
@@ -14,25 +14,25 @@
* along with this program. If not, see .
*/
-#include
+#include
+#include
-#include "sb-common.h"
-#include "rpc/msgpack/sb-msgpack-rpc.h"
-#include "helper-unix.h"
+#include "rpc/db/sb-db.h"
+#include "api/sb-api.h"
+#include "api/helpers.h"
-void unit_pack_string(UNUSED(void **state))
+int api_unsubscribe(uint64_t con_id, string event, struct api_error *api_error)
{
- string key = cstring_copy_string("TeiwieDoowuiMeix6SooxieFievee2io3ohhu5uo5ughu8cieja4iu6chuirija");
- msgpack_sbuffer sbuf;
- msgpack_packer pk;
+ size_t length = (event.length < METHOD_MAXLEN ? event.length : METHOD_MAXLEN);
+ char e[METHOD_MAXLEN + 1];
- msgpack_sbuffer_init(&sbuf);
- msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
+ sbassert(api_error);
- assert_int_equal(0, pack_string(&pk, key));
- assert_int_not_equal(0, pack_string(NULL, key));
+ memcpy(e, event.str, length);
+ e[length] = '\000';
- msgpack_sbuffer_destroy(&sbuf);
- free_string(key);
+ connection_unsubscribe(con_id, e);
+
+ return 0;
}
diff --git a/src/confparse.c b/src/confparse.c
index 58939fc..c3b19d4 100644
--- a/src/confparse.c
+++ b/src/confparse.c
@@ -82,7 +82,7 @@ bitarray_init_zero(unsigned int n_bits)
{
/* round up to the next int. */
size_t sz = (n_bits+BITARRAY_MASK) >> BITARRAY_SHIFT;
- return (CALLOC(sz, unsigned int));
+ return calloc_or_die(sz, sizeof(unsigned int));
}
/** Free the bit array ba. */
@@ -243,7 +243,7 @@ STATIC void reset(const configformat *fmt, void *options, const configvar *var,
if (!use_defaults)
return; /* all done */
if (var->initvalue) {
- c = CALLOC(1, configline);
+ c = calloc_or_die(1, sizeof(configline));
c->key = box_strdup(var->name);
c->value = box_strdup(var->initvalue);
if (assign_value(fmt, options, c) < 0) {
@@ -338,7 +338,7 @@ STATIC const char * unescape_string(const char *s, char **result,
}
}
end_of_loop:
- out = *result = MALLOC_ARRAY((size_t)(cp-s + 1), char);
+ out = *result = malloc_array_or_die((size_t)(cp-s + 1), sizeof(char));
cp = s+1;
while (1) {
switch (*cp)
@@ -405,7 +405,7 @@ STATIC void line_append(configline **lst, const char *key, const char *val)
{
configline *newline;
- newline = CALLOC(1, configline);
+ newline = calloc_or_die(1, sizeof(configline));
newline->key = box_strdup(key);
newline->value = box_strdup(val);
newline->next = NULL;
@@ -668,7 +668,7 @@ int confparse_get_lines(const char *string, configline **result, int extended)
/* This list can get long, so we keep a pointer to the end of it
* rather than using line_append over and over and getting
* n^2 performance. */
- *next = CALLOC(1, configline);
+ *next = calloc_or_die(1, sizeof(configline));
(*next)->key = k;
(*next)->value = v;
(*next)->next = NULL;
diff --git a/src/filesystem.c b/src/filesystem.c
index eb15693..ab21939 100644
--- a/src/filesystem.c
+++ b/src/filesystem.c
@@ -153,6 +153,9 @@ int filesystem_save_sync(const char *fn, const void *x, size_t xlen)
int fd;
int r;
+ if (!x)
+ return -1;
+
fd = filesystem_open_write(fn);
if (fd == -1)
@@ -174,6 +177,9 @@ int filesystem_load(const char *fn, void *x, size_t xlen)
int fd;
int r;
+ if (!x)
+ return -1;
+
fd = filesystem_open_read(fn);
if (fd == -1)
diff --git a/src/hashmap.c b/src/hashmap.c
index 5b22b32..017c68e 100644
--- a/src/hashmap.c
+++ b/src/hashmap.c
@@ -59,7 +59,7 @@
\
hashmap(T, U) *hashmap_##T##_##U##_new() \
{ \
- hashmap(T, U) *rv = MALLOC(hashmap(T, U)); \
+ hashmap(T, U) *rv = malloc_or_die(sizeof(hashmap(T, U))); \
rv->table = kh_init(T##_##U##_map); \
return rv; \
} \
diff --git a/src/klist.h b/src/klist.h
new file mode 100644
index 0000000..4da2cab
--- /dev/null
+++ b/src/klist.h
@@ -0,0 +1,134 @@
+/* The MIT License
+ Copyright (c) 2008-2009, by Attractive Chaos
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+*/
+
+#ifndef _AC_KLIST_H
+#define _AC_KLIST_H
+
+#include
+#include
+#include "sb-common.h"
+
+#define KMEMPOOL_INIT(name, kmptype_t, kmpfree_f) \
+ typedef struct { \
+ size_t cnt, n, max; \
+ kmptype_t **buf; \
+ } kmp_##name##_t; \
+ static inline kmp_##name##_t *kmp_init_##name(void) { \
+ return calloc_or_die(1, sizeof(kmp_##name##_t)); \
+ } \
+ static inline void kmp_destroy_##name(kmp_##name##_t *mp) { \
+ size_t k; \
+ for (k = 0; k < mp->n; k++) { \
+ kmpfree_f(mp->buf[k]); FREE(mp->buf[k]); \
+ } \
+ FREE(mp->buf); FREE(mp); \
+ } \
+ static inline kmptype_t *kmp_alloc_##name(kmp_##name##_t *mp) { \
+ mp->cnt++; \
+ if (mp->n == 0) { \
+ return calloc_or_die(1, sizeof(kmptype_t)); \
+ } \
+ return mp->buf[--mp->n]; \
+ } \
+ static inline void kmp_free_##name(kmp_##name##_t *mp, kmptype_t *p) { \
+ mp->cnt--; \
+ if (mp->n == mp->max) { \
+ mp->max = mp->max ? (mp->max << 1) : 16; \
+ mp->buf = realloc_array_or_die(mp->buf, mp->max, sizeof(kmptype_t *)); \
+ } \
+ mp->buf[mp->n++] = p; \
+ }
+
+#define kmempool_t(name) kmp_##name##_t
+#define kmp_init(name) kmp_init_##name()
+#define kmp_destroy(name, mp) kmp_destroy_##name(mp)
+#define kmp_alloc(name, mp) kmp_alloc_##name(mp)
+#define kmp_free(name, mp, p) kmp_free_##name(mp, p)
+
+#define KLIST_INIT(name, kltype_t, kmpfree_t) \
+ struct __kl1_##name { \
+ kltype_t data; \
+ struct __kl1_##name *next; \
+ }; \
+ typedef struct __kl1_##name kl1_##name; \
+ KMEMPOOL_INIT(name, kl1_##name, kmpfree_t) \
+ typedef struct { \
+ kl1_##name *head, *tail; \
+ kmp_##name##_t *mp; \
+ size_t size; \
+ } kl_##name##_t; \
+ static inline kl_##name##_t *kl_init_##name(void) { \
+ kl_##name##_t *kl = calloc_or_die(1, sizeof(kl_##name##_t)); \
+ kl->mp = kmp_init(name); \
+ kl->head = kl->tail = kmp_alloc(name, kl->mp); \
+ kl->head->next = 0; \
+ return kl; \
+ } \
+ static inline void kl_destroy_##name(kl_##name##_t *kl) { \
+ kl1_##name *p; \
+ for (p = kl->head; p != kl->tail; p = p->next) { \
+ kmp_free(name, kl->mp, p); \
+ } \
+ kmp_free(name, kl->mp, p); \
+ kmp_destroy(name, kl->mp); \
+ FREE(kl); \
+ } \
+ static inline void kl_push_##name(kl_##name##_t *kl, kltype_t d) { \
+ kl1_##name *q, *p = kmp_alloc(name, kl->mp); \
+ q = kl->tail; p->next = 0; kl->tail->next = p; kl->tail = p; \
+ kl->size++; \
+ q->data = d; \
+ } \
+ static inline kltype_t kl_shift_at_##name(kl_##name##_t *kl, \
+ kl1_##name **n) { \
+ assert((*n)->next); \
+ kl1_##name *p; \
+ kl->size--; \
+ p = *n; \
+ *n = (*n)->next; \
+ if (p == kl->head) { \
+ kl->head = *n; \
+ } \
+ kltype_t d = p->data; \
+ kmp_free(name, kl->mp, p); \
+ return d; \
+ }
+
+#define kliter_t(name) kl1_##name
+#define klist_t(name) kl_##name##_t
+#define kl_val(iter) ((iter)->data)
+#define kl_next(iter) ((iter)->next)
+#define kl_begin(kl) ((kl)->head)
+#define kl_end(kl) ((kl)->tail)
+
+#define kl_init(name) kl_init_##name()
+#define kl_destroy(name, kl) kl_destroy_##name(kl)
+#define kl_push(name, kl, d) kl_push_##name(kl, d)
+#define kl_shift_at(name, kl, node) kl_shift_at_##name(kl, node)
+#define kl_shift(name, kl) kl_shift_at(name, kl, &kl->head)
+#define kl_empty(kl) ((kl)->size == 0)
+// Iteration macros. It's ok to modify the list while iterating as long as a
+// `break` statement is executed before the next iteration.
+#define kl_iter(name, kl, p) kl_iter_at(name, kl, p, NULL)
+#define kl_iter_at(name, kl, p, h) \
+ for (kl1_##name **p = h ? h : &kl->head; *p != kl->tail; p = &(*p)->next)
+
+#endif
diff --git a/src/kvec.h b/src/kvec.h
index 31d2e14..2d087d0 100644
--- a/src/kvec.h
+++ b/src/kvec.h
@@ -49,42 +49,129 @@ int main() {
#define AC_KVEC_H
#include
+#include
-#define kv_roundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x))
+#include "sb-common.h"
-#define kvec_t(type) struct { size_t size, capacity; type *obj; }
-#define kv_init(v) ((v).size = (v).capacity = 0, (v).obj = 0)
-#define kv_destroy(v) free((v).obj)
-#define kv_A(v, i) ((v).obj[(i)])
-#define kv_pop(v) ((v).obj[--(v).size])
+#define kv_roundup32(x) \
+ ((--(x)), \
+ ((x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16), \
+ (++(x)))
+
+#define KV_INITIAL_VALUE { .size = 0, .capacity = 0, .items = NULL }
+
+#define kvec_t(type) \
+ struct { \
+ size_t size; \
+ size_t capacity; \
+ type *items; \
+ }
+
+#define kv_init(v) ((v).size = (v).capacity = 0, (v).items = 0)
+#define kv_destroy(v) free((v).items)
+#define kv_A(v, i) ((v).items[(i)])
+#define kv_pop(v) ((v).items[--(v).size])
#define kv_size(v) ((v).size)
#define kv_max(v) ((v).capacity)
+#define kv_last(v) kv_A(v, kv_size(v) - 1)
+
+#define kv_resize(v, s) \
+ ((v).capacity = (s), \
+ (v).items = realloc((v).items, sizeof((v).items[0]) * (v).capacity))
+
+#define kv_resize_full(v) \
+ kv_resize(v, (v).capacity ? (v).capacity << 1 : 8)
+
+#define kv_copy(v1, v0) \
+ do { \
+ if ((v1).capacity < (v0).size) { \
+ kv_resize(v1, (v0).size); \
+ } \
+ (v1).size = (v0).size; \
+ memcpy((v1).items, (v0).items, sizeof((v1).items[0]) * (v0).size); \
+ } while (0) \
+
+#define kv_pushp(v) \
+ ((((v).size == (v).capacity) ? (kv_resize_full(v), 0) : 0), \
+ ((v).items + ((v).size++)))
+
+#define kv_push(v, x) \
+ (*kv_pushp(v) = (x))
+
+#define kv_a(v, i) \
+ (((v).capacity <= (size_t) (i) \
+ ? ((v).capacity = (v).size = (i) + 1, \
+ kv_roundup32((v).capacity), \
+ kv_resize((v), (v).capacity), 0) \
+ : ((v).size <= (size_t) (i) \
+ ? (v).size = (i) + 1 \
+ : 0)), \
+ (v).items[(i)])
+
+#define kvec_withinit_t(type, INIT_SIZE) \
+ struct { \
+ size_t size; \
+ size_t capacity; \
+ type *items; \
+ type init_array[INIT_SIZE]; \
+ }
+
+#define kvi_init(v) \
+ ((v).capacity = ARRAY_SIZE((v).init_array), \
+ (v).size = 0, \
+ (v).items = (v).init_array)
+
+/// Move data to a new destination and free source
+static inline void *_memcpy_free(void *const restrict dest,
+ void *const restrict src,
+ const size_t size)
+{
+ memcpy(dest, src, size);
+ free(src);
+ return dest;
+}
+
-#define kv_resize(type, v, s) ((v).capacity = (s), (v).obj = (type*)realloc((v).obj, sizeof(type) * (v).capacity))
-
-#define kv_copy(type, v1, v0) do { \
- if ((v1).capacity < (v0).size) kv_resize(type, v1, (v0).size); \
- (v1).size = (v0).size; \
- memcpy((v1).obj, (v0).obj, sizeof(type) * (v0).size); \
- } while (0) \
-
-#define kv_push(type, v, x) do { \
- if ((v).size == (v).capacity) { \
- (v).capacity = (v).capacity? (v).capacity<<1 : 8; \
- (v).obj = (type*)realloc((v).obj, sizeof(type) * (v).capacity); \
- } \
- (v).obj[(v).size++] = (x); \
- } while (0)
-
-#define kv_pushp(type, v) (((v).size == (v).capacity)? \
- ((v).capacity = ((v).capacity? (v).capacity<<1 : 8), \
- (v).obj = (type*)realloc((v).obj, sizeof(type) * (v).capacity), 0) \
- : 0), ((v).obj + ((v).size++))
-
-#define kv_a(type, v, i) (((v).capacity <= (size_t)(i)? \
- ((v).capacity = (v).size = (i) + 1, kv_roundup32((v).capacity), \
- (v).obj = (type*)realloc((v).obj, sizeof(type) * (v).capacity), 0) \
- : (v).size <= (size_t)(i)? (v).size = (i) + 1 \
- : 0), (v).obj[(i)])
+#define kvi_resize(v, s) \
+ ((v).capacity = ((s) > ARRAY_SIZE((v).init_array) \
+ ? (s) \
+ : ARRAY_SIZE((v).init_array)), \
+ (v).items = ((v).capacity == ARRAY_SIZE((v).init_array) \
+ ? ((v).items == (v).init_array \
+ ? (v).items \
+ : _memcpy_free((v).init_array, (v).items, \
+ (v).size * sizeof((v).items[0]))) \
+ : ((v).items == (v).init_array \
+ ? memcpy(xmalloc((v).capacity * sizeof((v).items[0])), \
+ (v).items, \
+ (v).size * sizeof((v).items[0])) \
+ : realloc((v).items, \
+ (v).capacity * sizeof((v).items[0])))))
+
+/// Resize vector with preallocated array when it is full
+///
+/// @param[out] v Vector to resize.
+#define kvi_resize_full(v) \
+ /* ARRAY_SIZE((v).init_array) is the minimal capacity of this vector. */ \
+ /* Thus when vector is full capacity may not be zero and it is safe */ \
+ /* not to bother with checking whether (v).capacity is 0. But now */ \
+ /* capacity is not guaranteed to have size that is a power of 2, it is */ \
+ /* hard to fix this here and is not very necessary if users will use */ \
+ /* 2^x initial array size. */ \
+ kvi_resize(v, (v).capacity << 1)
+
+#define kvi_pushp(v) \
+ ((((v).size == (v).capacity) ? (kvi_resize_full(v), 0) : 0), \
+ ((v).items + ((v).size++)))
+
+#define kvi_push(v, x) \
+ (*kvi_pushp(v) = (x))
+
+#define kvi_destroy(v) \
+ do { \
+ if ((v).items != (v).init_array) { \
+ FREE((v).items); \
+ } \
+ } while (0)
#endif
diff --git a/src/main.c b/src/main.c
index 86eaa07..7919003 100644
--- a/src/main.c
+++ b/src/main.c
@@ -22,9 +22,10 @@
#include "tweetnacl.h"
#include "rpc/sb-rpc.h"
#include "rpc/db/sb-db.h"
+#include "main.h"
int8_t verbose_level;
-uv_loop_t loop;
+loop main_loop;
int main(int argc, char **argv)
{
@@ -38,7 +39,7 @@ int main(int argc, char **argv)
abort();
}
- uv_loop_init(&loop);
+ loop_init(&main_loop, NULL);
crypto_init();
@@ -60,12 +61,6 @@ int main(int argc, char **argv)
abort();
}
- /* initialize event queue */
- if (event_initialize() == -1) {
- LOG_ERROR("Failed to initialize event queue.");
- abort();
- }
-
/* initialize connections */
if (connection_init() == -1) {
LOG_ERROR("Failed to initialise connections.");
@@ -85,9 +80,9 @@ int main(int argc, char **argv)
server_start_pipe(globaloptions->ApiNamedPipeListen);
}
- uv_run(&loop, UV_RUN_DEFAULT);
-
- options_free(globaloptions);
+ for (;;) {
+ LOOP_PROCESS_EVENTS_UNTIL(&main_loop, main_loop.events, 2000, false);
+ }
return (0);
}
diff --git a/test/unit/pack-nil.c b/src/main.h
similarity index 62%
rename from test/unit/pack-nil.c
rename to src/main.h
index b027c88..eacfddb 100644
--- a/test/unit/pack-nil.c
+++ b/src/main.h
@@ -14,23 +14,8 @@
* along with this program. If not, see .
*/
-#include
+ #pragma once
-#include "sb-common.h"
-#include "rpc/msgpack/sb-msgpack-rpc.h"
-#include "helper-unix.h"
+#include "rpc/connection/loop.h"
-
-void unit_pack_nil(UNUSED(void **state))
-{
- msgpack_sbuffer sbuf;
- msgpack_packer pk;
-
- msgpack_sbuffer_init(&sbuf);
- msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
-
- assert_int_equal(0, pack_nil(&pk));
- assert_int_not_equal(0, pack_nil(NULL));
-
- msgpack_sbuffer_destroy(&sbuf);
-}
+extern loop main_loop;
diff --git a/src/mem.c b/src/mem.c
new file mode 100644
index 0000000..b838869
--- /dev/null
+++ b/src/mem.c
@@ -0,0 +1,47 @@
+#include
+
+#include "sb-common.h"
+
+void *malloc_or_die(size_t n)
+{
+ void *p = malloc(n);
+ if (!p && 0 < n)
+ LOG_MEMERROR();
+ return p;
+}
+
+
+void *malloc_array_or_die(size_t nmemb, size_t n)
+{
+ void *p = reallocarray(NULL, nmemb, n);
+ if (!p && 0 < n)
+ LOG_MEMERROR();
+ return p;
+}
+
+
+void *calloc_or_die(size_t nmemb, size_t n)
+{
+ void *p = calloc(nmemb, n);
+ if (!p && 0 < n)
+ LOG_MEMERROR();
+ return p;
+}
+
+
+void *realloc_array_or_die(void *ptr, size_t nmemb, size_t n)
+{
+ void *p = reallocarray(ptr, nmemb, n);
+ if (!p && 0 < n)
+ LOG_MEMERROR();
+ return p;
+}
+
+
+void *realloc_or_die(void *ptr, size_t n)
+{
+ void *p = realloc(ptr, n);
+ if (!p && 0 < n)
+ LOG_MEMERROR();
+ return p;
+}
diff --git a/src/mem.h b/src/mem.h
new file mode 100644
index 0000000..fd5ee39
--- /dev/null
+++ b/src/mem.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include
+
+#define FREE(p) do { \
+ if (PREDICT_LIKELY((p)!=NULL)) \
+ free(p); \
+ p = NULL; \
+ } while (0)
+
+#define MALLOC_TYPE(t) ((t)malloc_or_die(sizeof(t)))
+
+void *reallocarray(void *optr, size_t nmemb, size_t size);
+
+void *realloc_array_or_die(void *ptr, size_t nmemb, size_t n);
+void *malloc_array_or_die(size_t nmemb, size_t n);
+void *malloc_or_die(size_t n);
+void *calloc_or_die(size_t nmemb, size_t n);
+void *realloc_or_die(void *ptr, size_t n);
+
diff --git a/src/options.c b/src/options.c
index a1eca04..6bc35a8 100644
--- a/src/options.c
+++ b/src/options.c
@@ -152,7 +152,7 @@ STATIC setoptionerror options_init_from_string(const char *cf)
int retval;
setoptionerror err = SETOPT_ERR_MISC;
- newoptions = CALLOC(1, options);
+ newoptions = calloc_or_die(1, sizeof(options));
newoptions->magic_ = OR_OPTIONS_MAGIC;
options_init(newoptions);
@@ -212,7 +212,7 @@ STATIC char *load_boxrc_from_disk(void)
return (NULL);
}
- string = MALLOC_ARRAY((size_t)(statbuf.st_size+1), char);
+ string = malloc_array_or_die((size_t)(statbuf.st_size+1), sizeof(char));
ssize_t r = filesystem_read_all(fd, string, (size_t)statbuf.st_size);
if (r < 0) {
diff --git a/src/queue.h b/src/queue.h
index 1be9e9c..1c52ad8 100644
--- a/src/queue.h
+++ b/src/queue.h
@@ -1,753 +1,85 @@
-/*-
- * Copyright (c) 1991, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 4. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
- *
- * @(#)queue.h 8.5 (Berkeley) 8/20/94
- * $FreeBSD$
- */
-#ifndef _SYS_QUEUE_H_
-#define _SYS_QUEUE_H_
+// Copyright (c) 2013, Ben Noordhuis
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-#include
+#pragma once
-/*
- * This file defines four types of data structures: singly-linked lists,
- * singly-linked tail queues, lists and tail queues.
- *
- * A singly-linked list is headed by a single forward pointer. The elements
- * are singly linked for minimum space and pointer manipulation overhead at
- * the expense of O(n) removal for arbitrary elements. New elements can be
- * added to the list after an existing element or at the head of the list.
- * Elements being removed from the head of the list should use the explicit
- * macro for this purpose for optimum efficiency. A singly-linked list may
- * only be traversed in the forward direction. Singly-linked lists are ideal
- * for applications with large datasets and few or no removals or for
- * implementing a LIFO queue.
- *
- * A singly-linked tail queue is headed by a pair of pointers, one to the
- * head of the list and the other to the tail of the list. The elements are
- * singly linked for minimum space and pointer manipulation overhead at the
- * expense of O(n) removal for arbitrary elements. New elements can be added
- * to the list after an existing element, at the head of the list, or at the
- * end of the list. Elements being removed from the head of the tail queue
- * should use the explicit macro for this purpose for optimum efficiency.
- * A singly-linked tail queue may only be traversed in the forward direction.
- * Singly-linked tail queues are ideal for applications with large datasets
- * and few or no removals or for implementing a FIFO queue.
- *
- * A list is headed by a single forward pointer (or an array of forward
- * pointers for a hash table header). The elements are doubly linked
- * so that an arbitrary element can be removed without a need to
- * traverse the list. New elements can be added to the list before
- * or after an existing element or at the head of the list. A list
- * may be traversed in either direction.
- *
- * A tail queue is headed by a pair of pointers, one to the head of the
- * list and the other to the tail of the list. The elements are doubly
- * linked so that an arbitrary element can be removed without a need to
- * traverse the list. New elements can be added to the list before or
- * after an existing element, at the head of the list, or at the end of
- * the list. A tail queue may be traversed in either direction.
- *
- * For details on the use of these macros, see the queue(3) manual page.
- *
- *
- * SLIST LIST STAILQ TAILQ
- * _HEAD + + + +
- * _CLASS_HEAD + + + +
- * _HEAD_INITIALIZER + + + +
- * _ENTRY + + + +
- * _CLASS_ENTRY + + + +
- * _INIT + + + +
- * _EMPTY + + + +
- * _FIRST + + + +
- * _NEXT + + + +
- * _PREV - + - +
- * _LAST - - + +
- * _FOREACH + + + +
- * _FOREACH_FROM + + + +
- * _FOREACH_SAFE + + + +
- * _FOREACH_FROM_SAFE + + + +
- * _FOREACH_REVERSE - - - +
- * _FOREACH_REVERSE_FROM - - - +
- * _FOREACH_REVERSE_SAFE - - - +
- * _FOREACH_REVERSE_FROM_SAFE - - - +
- * _INSERT_HEAD + + + +
- * _INSERT_BEFORE - + - +
- * _INSERT_AFTER + + + +
- * _INSERT_TAIL - - + +
- * _CONCAT - - + +
- * _REMOVE_AFTER + - + -
- * _REMOVE_HEAD + - + -
- * _REMOVE + + + +
- * _SWAP + + + +
- *
- */
-#ifdef QUEUE_MACRO_DEBUG
-/* Store the last 2 places the queue element or head was altered */
-struct qm_trace {
- unsigned long lastline;
- unsigned long prevline;
- const char *lastfile;
- const char *prevfile;
-};
+#include
-#define TRACEBUF struct qm_trace trace;
-#define TRACEBUF_INITIALIZER { __LINE__, 0, __FILE__, NULL } ,
-#define TRASHIT(x) do {(x) = (void *)-1;} while (0)
-#define QMD_SAVELINK(name, link) void **name = (void *)&(link)
+typedef struct _queue {
+ struct _queue *next;
+ struct _queue *prev;
+} QUEUE;
-#define QMD_TRACE_HEAD(head) do { \
- (head)->trace.prevline = (head)->trace.lastline; \
- (head)->trace.prevfile = (head)->trace.lastfile; \
- (head)->trace.lastline = __LINE__; \
- (head)->trace.lastfile = __FILE__; \
-} while (0)
+// Public macros.
+#define QUEUE_DATA(ptr, type, field) \
+ ((type *)(void *)((char *)(ptr) - offsetof(type, field)))
-#define QMD_TRACE_ELEM(elem) do { \
- (elem)->trace.prevline = (elem)->trace.lastline; \
- (elem)->trace.prevfile = (elem)->trace.lastfile; \
- (elem)->trace.lastline = __LINE__; \
- (elem)->trace.lastfile = __FILE__; \
-} while (0)
+#define QUEUE_FOREACH(q, h) \
+ for ( /* NOLINT(readability/braces) */ \
+ (q) = (h)->next; (q) != (h); (q) = (q)->next)
-#else
-#define QMD_TRACE_ELEM(elem)
-#define QMD_TRACE_HEAD(head)
-#define QMD_SAVELINK(name, link)
-#define TRACEBUF
-#define TRACEBUF_INITIALIZER
-#define TRASHIT(x)
-#endif /* QUEUE_MACRO_DEBUG */
-
-#ifdef __cplusplus
-/*
- * In C++ there can be structure lists and class lists:
- */
-#define QUEUE_TYPEOF(type) type
-#else
-#define QUEUE_TYPEOF(type) struct type
-#endif
-
-/*
- * Singly-linked List declarations.
- */
-#define SLIST_HEAD(name, type) \
-struct name { \
- struct type *slh_first; /* first element */ \
-}
-
-#define SLIST_CLASS_HEAD(name, type) \
-struct name { \
- class type *slh_first; /* first element */ \
-}
-
-#define SLIST_HEAD_INITIALIZER(head) \
- { NULL }
-
-#define SLIST_ENTRY(type) \
-struct { \
- struct type *sle_next; /* next element */ \
-}
-
-#define SLIST_CLASS_ENTRY(type) \
-struct { \
- class type *sle_next; /* next element */ \
-}
-
-/*
- * Singly-linked List functions.
- */
-#define SLIST_EMPTY(head) ((head)->slh_first == NULL)
-
-#define SLIST_FIRST(head) ((head)->slh_first)
-
-#define SLIST_FOREACH(var, head, field) \
- for ((var) = SLIST_FIRST((head)); \
- (var); \
- (var) = SLIST_NEXT((var), field))
-
-#define SLIST_FOREACH_FROM(var, head, field) \
- for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \
- (var); \
- (var) = SLIST_NEXT((var), field))
-
-#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
- for ((var) = SLIST_FIRST((head)); \
- (var) && ((tvar) = SLIST_NEXT((var), field), 1); \
- (var) = (tvar))
-
-#define SLIST_FOREACH_FROM_SAFE(var, head, field, tvar) \
- for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \
- (var) && ((tvar) = SLIST_NEXT((var), field), 1); \
- (var) = (tvar))
-
-#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \
- for ((varp) = &SLIST_FIRST((head)); \
- ((var) = *(varp)) != NULL; \
- (varp) = &SLIST_NEXT((var), field))
-
-#define SLIST_INIT(head) do { \
- SLIST_FIRST((head)) = NULL; \
-} while (0)
-
-#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
- SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \
- SLIST_NEXT((slistelm), field) = (elm); \
-} while (0)
-
-#define SLIST_INSERT_HEAD(head, elm, field) do { \
- SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \
- SLIST_FIRST((head)) = (elm); \
-} while (0)
-
-#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
-
-#define SLIST_REMOVE(head, elm, type, field) do { \
- QMD_SAVELINK(oldnext, (elm)->field.sle_next); \
- if (SLIST_FIRST((head)) == (elm)) { \
- SLIST_REMOVE_HEAD((head), field); \
- } \
- else { \
- QUEUE_TYPEOF(type) *curelm = SLIST_FIRST(head); \
- while (SLIST_NEXT(curelm, field) != (elm)) \
- curelm = SLIST_NEXT(curelm, field); \
- SLIST_REMOVE_AFTER(curelm, field); \
- } \
- TRASHIT(*oldnext); \
-} while (0)
-
-#define SLIST_REMOVE_AFTER(elm, field) do { \
- SLIST_NEXT(elm, field) = \
- SLIST_NEXT(SLIST_NEXT(elm, field), field); \
-} while (0)
-
-#define SLIST_REMOVE_HEAD(head, field) do { \
- SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \
-} while (0)
-
-#define SLIST_SWAP(head1, head2, type) do { \
- QUEUE_TYPEOF(type) *swap_first = SLIST_FIRST(head1); \
- SLIST_FIRST(head1) = SLIST_FIRST(head2); \
- SLIST_FIRST(head2) = swap_first; \
-} while (0)
-
-/*
- * Singly-linked Tail queue declarations.
- */
-#define STAILQ_HEAD(name, type) \
-struct name { \
- struct type *stqh_first;/* first element */ \
- struct type **stqh_last;/* addr of last next element */ \
-}
-
-#define STAILQ_CLASS_HEAD(name, type) \
-struct name { \
- class type *stqh_first; /* first element */ \
- class type **stqh_last; /* addr of last next element */ \
-}
-
-#define STAILQ_HEAD_INITIALIZER(head) \
- { NULL, &(head).stqh_first }
-
-#define STAILQ_ENTRY(type) \
-struct { \
- struct type *stqe_next; /* next element */ \
-}
-
-#define STAILQ_CLASS_ENTRY(type) \
-struct { \
- class type *stqe_next; /* next element */ \
-}
-
-/*
- * Singly-linked Tail queue functions.
- */
-#define STAILQ_CONCAT(head1, head2) do { \
- if (!STAILQ_EMPTY((head2))) { \
- *(head1)->stqh_last = (head2)->stqh_first; \
- (head1)->stqh_last = (head2)->stqh_last; \
- STAILQ_INIT((head2)); \
- } \
-} while (0)
-
-#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL)
-
-#define STAILQ_FIRST(head) ((head)->stqh_first)
-
-#define STAILQ_FOREACH(var, head, field) \
- for((var) = STAILQ_FIRST((head)); \
- (var); \
- (var) = STAILQ_NEXT((var), field))
-
-#define STAILQ_FOREACH_FROM(var, head, field) \
- for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \
- (var); \
- (var) = STAILQ_NEXT((var), field))
-
-#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \
- for ((var) = STAILQ_FIRST((head)); \
- (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
- (var) = (tvar))
-
-#define STAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \
- for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \
- (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
- (var) = (tvar))
-
-#define STAILQ_INIT(head) do { \
- STAILQ_FIRST((head)) = NULL; \
- (head)->stqh_last = &STAILQ_FIRST((head)); \
-} while (0)
-
-#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \
- if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\
- (head)->stqh_last = &STAILQ_NEXT((elm), field); \
- STAILQ_NEXT((tqelm), field) = (elm); \
-} while (0)
-
-#define STAILQ_INSERT_HEAD(head, elm, field) do { \
- if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \
- (head)->stqh_last = &STAILQ_NEXT((elm), field); \
- STAILQ_FIRST((head)) = (elm); \
-} while (0)
-
-#define STAILQ_INSERT_TAIL(head, elm, field) do { \
- STAILQ_NEXT((elm), field) = NULL; \
- *(head)->stqh_last = (elm); \
- (head)->stqh_last = &STAILQ_NEXT((elm), field); \
-} while (0)
-
-#define STAILQ_LAST(head, type, field) \
- (STAILQ_EMPTY((head)) ? NULL : \
- __containerof((head)->stqh_last, \
- QUEUE_TYPEOF(type), field.stqe_next))
-
-#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
-
-#define STAILQ_REMOVE(head, elm, type, field) do { \
- QMD_SAVELINK(oldnext, (elm)->field.stqe_next); \
- if (STAILQ_FIRST((head)) == (elm)) { \
- STAILQ_REMOVE_HEAD((head), field); \
- } \
- else { \
- QUEUE_TYPEOF(type) *curelm = STAILQ_FIRST(head); \
- while (STAILQ_NEXT(curelm, field) != (elm)) \
- curelm = STAILQ_NEXT(curelm, field); \
- STAILQ_REMOVE_AFTER(head, curelm, field); \
- } \
- TRASHIT(*oldnext); \
-} while (0)
-
-#define STAILQ_REMOVE_AFTER(head, elm, field) do { \
- if ((STAILQ_NEXT(elm, field) = \
- STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \
- (head)->stqh_last = &STAILQ_NEXT((elm), field); \
-} while (0)
-
-#define STAILQ_REMOVE_HEAD(head, field) do { \
- if ((STAILQ_FIRST((head)) = \
- STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \
- (head)->stqh_last = &STAILQ_FIRST((head)); \
-} while (0)
-
-#define STAILQ_SWAP(head1, head2, type) do { \
- QUEUE_TYPEOF(type) *swap_first = STAILQ_FIRST(head1); \
- QUEUE_TYPEOF(type) **swap_last = (head1)->stqh_last; \
- STAILQ_FIRST(head1) = STAILQ_FIRST(head2); \
- (head1)->stqh_last = (head2)->stqh_last; \
- STAILQ_FIRST(head2) = swap_first; \
- (head2)->stqh_last = swap_last; \
- if (STAILQ_EMPTY(head1)) \
- (head1)->stqh_last = &STAILQ_FIRST(head1); \
- if (STAILQ_EMPTY(head2)) \
- (head2)->stqh_last = &STAILQ_FIRST(head2); \
-} while (0)
-
-
-/*
- * List declarations.
- */
-#define LIST_HEAD(name, type) \
-struct name { \
- struct type *lh_first; /* first element */ \
+// ffi.cdef is unable to swallow `bool` in place of `int` here.
+static inline int QUEUE_EMPTY(const QUEUE *const q)
+{
+ return q == q->next;
}
-#define LIST_CLASS_HEAD(name, type) \
-struct name { \
- class type *lh_first; /* first element */ \
-}
+#define QUEUE_HEAD(q) (q)->next
-#define LIST_HEAD_INITIALIZER(head) \
- { NULL }
-
-#define LIST_ENTRY(type) \
-struct { \
- struct type *le_next; /* next element */ \
- struct type **le_prev; /* address of previous next element */ \
+static inline void QUEUE_INIT(QUEUE *const q)
+{
+ q->next = q;
+ q->prev = q;
}
-#define LIST_CLASS_ENTRY(type) \
-struct { \
- class type *le_next; /* next element */ \
- class type **le_prev; /* address of previous next element */ \
+static inline void QUEUE_ADD(QUEUE *const h, QUEUE *const n)
+{
+ h->prev->next = n->next;
+ n->next->prev = h->prev;
+ h->prev = n->prev;
+ h->prev->next = h;
}
-/*
- * List functions.
- */
-
-#if (defined(_KERNEL) && defined(INVARIANTS))
-#define QMD_LIST_CHECK_HEAD(head, field) do { \
- if (LIST_FIRST((head)) != NULL && \
- LIST_FIRST((head))->field.le_prev != \
- &LIST_FIRST((head))) \
- panic("Bad list head %p first->prev != head", (head)); \
-} while (0)
-
-#define QMD_LIST_CHECK_NEXT(elm, field) do { \
- if (LIST_NEXT((elm), field) != NULL && \
- LIST_NEXT((elm), field)->field.le_prev != \
- &((elm)->field.le_next)) \
- panic("Bad link elm %p next->prev != elm", (elm)); \
-} while (0)
-
-#define QMD_LIST_CHECK_PREV(elm, field) do { \
- if (*(elm)->field.le_prev != (elm)) \
- panic("Bad link elm %p prev->next != elm", (elm)); \
-} while (0)
-#else
-#define QMD_LIST_CHECK_HEAD(head, field)
-#define QMD_LIST_CHECK_NEXT(elm, field)
-#define QMD_LIST_CHECK_PREV(elm, field)
-#endif /* (_KERNEL && INVARIANTS) */
-
-#define LIST_EMPTY(head) ((head)->lh_first == NULL)
-
-#define LIST_FIRST(head) ((head)->lh_first)
-
-#define LIST_FOREACH(var, head, field) \
- for ((var) = LIST_FIRST((head)); \
- (var); \
- (var) = LIST_NEXT((var), field))
-
-#define LIST_FOREACH_FROM(var, head, field) \
- for ((var) = ((var) ? (var) : LIST_FIRST((head))); \
- (var); \
- (var) = LIST_NEXT((var), field))
-
-#define LIST_FOREACH_SAFE(var, head, field, tvar) \
- for ((var) = LIST_FIRST((head)); \
- (var) && ((tvar) = LIST_NEXT((var), field), 1); \
- (var) = (tvar))
-
-#define LIST_FOREACH_FROM_SAFE(var, head, field, tvar) \
- for ((var) = ((var) ? (var) : LIST_FIRST((head))); \
- (var) && ((tvar) = LIST_NEXT((var), field), 1); \
- (var) = (tvar))
-
-#define LIST_INIT(head) do { \
- LIST_FIRST((head)) = NULL; \
-} while (0)
-
-#define LIST_INSERT_AFTER(listelm, elm, field) do { \
- QMD_LIST_CHECK_NEXT(listelm, field); \
- if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\
- LIST_NEXT((listelm), field)->field.le_prev = \
- &LIST_NEXT((elm), field); \
- LIST_NEXT((listelm), field) = (elm); \
- (elm)->field.le_prev = &LIST_NEXT((listelm), field); \
-} while (0)
-
-#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
- QMD_LIST_CHECK_PREV(listelm, field); \
- (elm)->field.le_prev = (listelm)->field.le_prev; \
- LIST_NEXT((elm), field) = (listelm); \
- *(listelm)->field.le_prev = (elm); \
- (listelm)->field.le_prev = &LIST_NEXT((elm), field); \
-} while (0)
-
-#define LIST_INSERT_HEAD(head, elm, field) do { \
- QMD_LIST_CHECK_HEAD((head), field); \
- if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \
- LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\
- LIST_FIRST((head)) = (elm); \
- (elm)->field.le_prev = &LIST_FIRST((head)); \
-} while (0)
-
-#define LIST_NEXT(elm, field) ((elm)->field.le_next)
-
-#define LIST_PREV(elm, head, type, field) \
- ((elm)->field.le_prev == &LIST_FIRST((head)) ? NULL : \
- __containerof((elm)->field.le_prev, \
- QUEUE_TYPEOF(type), field.le_next))
-
-#define LIST_REMOVE(elm, field) do { \
- QMD_SAVELINK(oldnext, (elm)->field.le_next); \
- QMD_SAVELINK(oldprev, (elm)->field.le_prev); \
- QMD_LIST_CHECK_NEXT(elm, field); \
- QMD_LIST_CHECK_PREV(elm, field); \
- if (LIST_NEXT((elm), field) != NULL) \
- LIST_NEXT((elm), field)->field.le_prev = \
- (elm)->field.le_prev; \
- *(elm)->field.le_prev = LIST_NEXT((elm), field); \
- TRASHIT(*oldnext); \
- TRASHIT(*oldprev); \
-} while (0)
-
-#define LIST_SWAP(head1, head2, type, field) do { \
- QUEUE_TYPEOF(type) *swap_tmp = LIST_FIRST(head1); \
- LIST_FIRST((head1)) = LIST_FIRST((head2)); \
- LIST_FIRST((head2)) = swap_tmp; \
- if ((swap_tmp = LIST_FIRST((head1))) != NULL) \
- swap_tmp->field.le_prev = &LIST_FIRST((head1)); \
- if ((swap_tmp = LIST_FIRST((head2))) != NULL) \
- swap_tmp->field.le_prev = &LIST_FIRST((head2)); \
-} while (0)
-
-/*
- * Tail queue declarations.
- */
-#define TAILQ_HEAD(name, type) \
-struct name { \
- struct type *tqh_first; /* first element */ \
- struct type **tqh_last; /* addr of last next element */ \
- TRACEBUF \
+static inline void QUEUE_SPLIT(QUEUE *const h, QUEUE *const q, QUEUE *const n)
+{
+ n->prev = h->prev;
+ n->prev->next = n;
+ n->next = q;
+ h->prev = q->prev;
+ h->prev->next = h;
+ q->prev = n;
}
-#define TAILQ_CLASS_HEAD(name, type) \
-struct name { \
- class type *tqh_first; /* first element */ \
- class type **tqh_last; /* addr of last next element */ \
- TRACEBUF \
+static inline void QUEUE_INSERT_HEAD(QUEUE *const h, QUEUE *const q)
+{
+ q->next = h->next;
+ q->prev = h;
+ q->next->prev = q;
+ h->next = q;
}
-#define TAILQ_HEAD_INITIALIZER(head) \
- { NULL, &(head).tqh_first, TRACEBUF_INITIALIZER }
-
-#define TAILQ_ENTRY(type) \
-struct { \
- struct type *tqe_next; /* next element */ \
- struct type **tqe_prev; /* address of previous next element */ \
- TRACEBUF \
+static inline void QUEUE_INSERT_TAIL(QUEUE *const h, QUEUE *const q)
+{
+ q->next = h;
+ q->prev = h->prev;
+ q->prev->next = q;
+ h->prev = q;
}
-#define TAILQ_CLASS_ENTRY(type) \
-struct { \
- class type *tqe_next; /* next element */ \
- class type **tqe_prev; /* address of previous next element */ \
- TRACEBUF \
+static inline void QUEUE_REMOVE(QUEUE *const q)
+{
+ q->prev->next = q->next;
+ q->next->prev = q->prev;
}
-
-/*
- * Tail queue functions.
- */
-#if (defined(_KERNEL) && defined(INVARIANTS))
-#define QMD_TAILQ_CHECK_HEAD(head, field) do { \
- if (!TAILQ_EMPTY(head) && \
- TAILQ_FIRST((head))->field.tqe_prev != \
- &TAILQ_FIRST((head))) \
- panic("Bad tailq head %p first->prev != head", (head)); \
-} while (0)
-
-#define QMD_TAILQ_CHECK_TAIL(head, field) do { \
- if (*(head)->tqh_last != NULL) \
- panic("Bad tailq NEXT(%p->tqh_last) != NULL", (head)); \
-} while (0)
-
-#define QMD_TAILQ_CHECK_NEXT(elm, field) do { \
- if (TAILQ_NEXT((elm), field) != NULL && \
- TAILQ_NEXT((elm), field)->field.tqe_prev != \
- &((elm)->field.tqe_next)) \
- panic("Bad link elm %p next->prev != elm", (elm)); \
-} while (0)
-
-#define QMD_TAILQ_CHECK_PREV(elm, field) do { \
- if (*(elm)->field.tqe_prev != (elm)) \
- panic("Bad link elm %p prev->next != elm", (elm)); \
-} while (0)
-#else
-#define QMD_TAILQ_CHECK_HEAD(head, field)
-#define QMD_TAILQ_CHECK_TAIL(head, headname)
-#define QMD_TAILQ_CHECK_NEXT(elm, field)
-#define QMD_TAILQ_CHECK_PREV(elm, field)
-#endif /* (_KERNEL && INVARIANTS) */
-
-#define TAILQ_CONCAT(head1, head2, field) do { \
- if (!TAILQ_EMPTY(head2)) { \
- *(head1)->tqh_last = (head2)->tqh_first; \
- (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
- (head1)->tqh_last = (head2)->tqh_last; \
- TAILQ_INIT((head2)); \
- QMD_TRACE_HEAD(head1); \
- QMD_TRACE_HEAD(head2); \
- } \
-} while (0)
-
-#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
-
-#define TAILQ_FIRST(head) ((head)->tqh_first)
-
-#define TAILQ_FOREACH(var, head, field) \
- for ((var) = TAILQ_FIRST((head)); \
- (var); \
- (var) = TAILQ_NEXT((var), field))
-
-#define TAILQ_FOREACH_FROM(var, head, field) \
- for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \
- (var); \
- (var) = TAILQ_NEXT((var), field))
-
-#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
- for ((var) = TAILQ_FIRST((head)); \
- (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \
- (var) = (tvar))
-
-#define TAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \
- for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \
- (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \
- (var) = (tvar))
-
-#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
- for ((var) = TAILQ_LAST((head), headname); \
- (var); \
- (var) = TAILQ_PREV((var), headname, field))
-
-#define TAILQ_FOREACH_REVERSE_FROM(var, head, headname, field) \
- for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \
- (var); \
- (var) = TAILQ_PREV((var), headname, field))
-
-#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
- for ((var) = TAILQ_LAST((head), headname); \
- (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \
- (var) = (tvar))
-
-#define TAILQ_FOREACH_REVERSE_FROM_SAFE(var, head, headname, field, tvar) \
- for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \
- (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \
- (var) = (tvar))
-
-#define TAILQ_INIT(head) do { \
- TAILQ_FIRST((head)) = NULL; \
- (head)->tqh_last = &TAILQ_FIRST((head)); \
- QMD_TRACE_HEAD(head); \
-} while (0)
-
-#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
- QMD_TAILQ_CHECK_NEXT(listelm, field); \
- if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\
- TAILQ_NEXT((elm), field)->field.tqe_prev = \
- &TAILQ_NEXT((elm), field); \
- else { \
- (head)->tqh_last = &TAILQ_NEXT((elm), field); \
- QMD_TRACE_HEAD(head); \
- } \
- TAILQ_NEXT((listelm), field) = (elm); \
- (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \
- QMD_TRACE_ELEM(&(elm)->field); \
- QMD_TRACE_ELEM(&(listelm)->field); \
-} while (0)
-
-#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
- QMD_TAILQ_CHECK_PREV(listelm, field); \
- (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
- TAILQ_NEXT((elm), field) = (listelm); \
- *(listelm)->field.tqe_prev = (elm); \
- (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \
- QMD_TRACE_ELEM(&(elm)->field); \
- QMD_TRACE_ELEM(&(listelm)->field); \
-} while (0)
-
-#define TAILQ_INSERT_HEAD(head, elm, field) do { \
- QMD_TAILQ_CHECK_HEAD(head, field); \
- if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \
- TAILQ_FIRST((head))->field.tqe_prev = \
- &TAILQ_NEXT((elm), field); \
- else \
- (head)->tqh_last = &TAILQ_NEXT((elm), field); \
- TAILQ_FIRST((head)) = (elm); \
- (elm)->field.tqe_prev = &TAILQ_FIRST((head)); \
- QMD_TRACE_HEAD(head); \
- QMD_TRACE_ELEM(&(elm)->field); \
-} while (0)
-
-#define TAILQ_INSERT_TAIL(head, elm, field) do { \
- QMD_TAILQ_CHECK_TAIL(head, field); \
- TAILQ_NEXT((elm), field) = NULL; \
- (elm)->field.tqe_prev = (head)->tqh_last; \
- *(head)->tqh_last = (elm); \
- (head)->tqh_last = &TAILQ_NEXT((elm), field); \
- QMD_TRACE_HEAD(head); \
- QMD_TRACE_ELEM(&(elm)->field); \
-} while (0)
-
-#define TAILQ_LAST(head, headname) \
- (*(((struct headname *)((head)->tqh_last))->tqh_last))
-
-#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
-
-#define TAILQ_PREV(elm, headname, field) \
- (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
-
-#define TAILQ_REMOVE(head, elm, field) do { \
- QMD_SAVELINK(oldnext, (elm)->field.tqe_next); \
- QMD_SAVELINK(oldprev, (elm)->field.tqe_prev); \
- QMD_TAILQ_CHECK_NEXT(elm, field); \
- QMD_TAILQ_CHECK_PREV(elm, field); \
- if ((TAILQ_NEXT((elm), field)) != NULL) \
- TAILQ_NEXT((elm), field)->field.tqe_prev = \
- (elm)->field.tqe_prev; \
- else { \
- (head)->tqh_last = (elm)->field.tqe_prev; \
- QMD_TRACE_HEAD(head); \
- } \
- *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \
- TRASHIT(*oldnext); \
- TRASHIT(*oldprev); \
- QMD_TRACE_ELEM(&(elm)->field); \
-} while (0)
-
-#define TAILQ_SWAP(head1, head2, type, field) do { \
- QUEUE_TYPEOF(type) *swap_first = (head1)->tqh_first; \
- QUEUE_TYPEOF(type) **swap_last = (head1)->tqh_last; \
- (head1)->tqh_first = (head2)->tqh_first; \
- (head1)->tqh_last = (head2)->tqh_last; \
- (head2)->tqh_first = swap_first; \
- (head2)->tqh_last = swap_last; \
- if ((swap_first = (head1)->tqh_first) != NULL) \
- swap_first->field.tqe_prev = &(head1)->tqh_first; \
- else \
- (head1)->tqh_last = &(head1)->tqh_first; \
- if ((swap_first = (head2)->tqh_first) != NULL) \
- swap_first->field.tqe_prev = &(head2)->tqh_first; \
- else \
- (head2)->tqh_last = &(head2)->tqh_first; \
-} while (0)
-
-#endif /* !_SYS_QUEUE_H_ */
diff --git a/src/rpc/connection/connection.c b/src/rpc/connection/connection.c
index 40267bd..8645c43 100644
--- a/src/rpc/connection/connection.c
+++ b/src/rpc/connection/connection.c
@@ -30,53 +30,64 @@
* limitations under the License.
*/
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include "tweetnacl.h"
-#include "rpc/sb-rpc.h"
#include "rpc/connection/connection.h"
-#include "api/sb-api.h"
-
-STATIC int parse_cb(inputstream *istream, void *data, bool eof);
+#ifdef __linux__
+#include // for strlcpy
+#endif
+#include // for msgpack_object, msgpack_object_union
+#include // for msgpack_packer_init, msgpack_packer
+#include // for msgpack_sbuffer, msgpack_sbuffer_c...
+#include // for msgpack_unpacked, msgpack_unpacked...
+#include // for true, bool, false
+#include // for NULL, size_t
+#include // for uint64_t, UINT64_MAX, uint32_t
+#include // for abort, exit, realloc
+#include // for strlen
+#include // for uv_handle_t, uv_close, uv_timer_t
+#include "api/helpers.h" // for NIL
+#include "api/sb-api.h" // for api_free_array, api_free_object
+#include "khash.h" // for __i, khint32_t
+#include "main.h" // for main_loop
+#include "rpc/connection/event.h" // for multiqueue_free, multiqueue_new_child
+#include "rpc/connection/loop.h" // for loop, LOOP_PROCESS_EVENTS_UNTIL
+#include "rpc/msgpack/helpers.h" // for msgpack_rpc_serialize_request, msg...
+#include "rpc/sb-rpc.h" // for callinfo, crypto_context, object
+#include "tweetnacl.h" // for randombytes
+
+
+STATIC void parse_cb(inputstream *istream, void *data, bool eof);
STATIC void close_cb(uv_handle_t *handle);
STATIC void timer_cb(uv_timer_t *timer);
-STATIC int connection_handle_request(struct connection *con,
+STATIC void connection_handle_request(struct connection *con,
msgpack_object *obj);
STATIC void connection_handle_response(struct connection *con,
msgpack_object *obj);
-STATIC void connection_request_event(connection_request_event_info *info);
+STATIC void connection_request_event(void **argv);
STATIC void connection_close(struct connection *con);
STATIC void call_set_error(struct connection *con, char *msg);
STATIC int is_valid_rpc_response(msgpack_object *obj, struct connection *con);
STATIC void free_connection(struct connection *con);
STATIC void incref(struct connection *con);
STATIC void decref(struct connection *con);
+STATIC void unsubscribe(struct connection *con, char *event);
+STATIC void send_delayed_notifications(struct connection *con);
static uint64_t next_con_id = 1;
static hashmap(uint64_t, ptr_t) *connections = NULL;
static hashmap(cstr_t, uint64_t) *pluginkeys = NULL;
+static hashmap(cstr_t, ptr_t) *event_strings = NULL;
static msgpack_sbuffer sbuf;
-equeue *equeue_root;
-uv_loop_t loop;
int connection_init(void)
{
connections = hashmap_new(uint64_t, ptr_t)();
pluginkeys = hashmap_new(cstr_t, uint64_t)();
+ event_strings = hashmap_new(cstr_t, ptr_t)();
if (dispatch_table_init() == -1)
return (-1);
- if (!connections || !pluginkeys)
+ if (!connections || !pluginkeys || !event_strings)
return (-1);
msgpack_sbuffer_init(&sbuf);
@@ -95,8 +106,10 @@ int connection_teardown(void)
connection_close(con);
});
+
hashmap_free(uint64_t, ptr_t)(connections);
hashmap_free(cstr_t, uint64_t)(pluginkeys);
+ hashmap_free(cstr_t, ptr_t)(event_strings);
dispatch_teardown();
msgpack_sbuffer_destroy(&sbuf);
@@ -110,21 +123,20 @@ int connection_create(uv_stream_t *stream)
stream->data = NULL;
- struct connection *con = MALLOC(struct connection);
-
- if (con == NULL)
- return (-1);
+ struct connection *con = malloc_or_die(sizeof(struct connection));
con->id = next_con_id++;
con->msgid = 1;
con->refcount = 1;
con->mpac = msgpack_unpacker_new(MSGPACK_UNPACKER_INIT_BUFFER_SIZE);
con->closed = false;
- con->queue = equeue_new(equeue_root);
+ con->events = multiqueue_new_child(main_loop.events);
con->streams.read = inputstream_new(parse_cb, STREAM_BUFFER_SIZE, con);
con->streams.write = outputstream_new(1024 * 1024);
con->streams.uv = stream;
con->cc.nonce = (uint64_t) randommod(281474976710656LL);
+ con->subscribed_events = hashmap_new(cstr_t, ptr_t)();
+ con->pending_requests = 0;
if (ISODD(con->cc.nonce)) {
con->cc.nonce++;
@@ -137,7 +149,7 @@ int connection_create(uv_stream_t *stream)
randombytes(con->cc.minutekey, sizeof con->cc.minutekey);
randombytes(con->cc.lastminutekey, sizeof con->cc.lastminutekey);
con->minutekey_timer.data = &con->cc;
- r = uv_timer_init(&loop, &con->minutekey_timer);
+ r = uv_timer_init(&main_loop.uv, &con->minutekey_timer);
sbassert(r == 0);
r = uv_timer_start(&con->minutekey_timer, timer_cb, 60000, 60000);
sbassert(r == 0);
@@ -148,6 +160,7 @@ int connection_create(uv_stream_t *stream)
con->packet.pos = 0;
kv_init(con->callvector);
+ kv_init(con->delayed_notifications);
inputstream_set(con->streams.read, stream);
inputstream_start(con->streams.read);
@@ -158,6 +171,90 @@ int connection_create(uv_stream_t *stream)
return (0);
}
+void connection_subscribe(uint64_t id, char *event)
+{
+ struct connection *con;
+
+ if (!(con = hashmap_get(uint64_t, ptr_t)(connections, id)) || con->closed)
+ abort();
+
+ char *event_string = hashmap_get(cstr_t, ptr_t)(event_strings, event);
+
+ if (!event_string) {
+ event_string = box_strdup(event);
+ hashmap_put(cstr_t, ptr_t)(event_strings, event_string, event_string);
+ }
+
+ hashmap_put(cstr_t, ptr_t)(con->subscribed_events, event_string, event_string);
+}
+
+void connection_unsubscribe(uint64_t id, char *event)
+{
+ struct connection *con;
+
+ if (!(con = hashmap_get(uint64_t, ptr_t)(connections, id)) || con->closed)
+ abort();
+
+ unsubscribe(con, event);
+}
+
+STATIC void broadcast_event(char *name, array args)
+{
+ kvec_t(struct connection *) subscribed = KV_INITIAL_VALUE;
+ struct connection *con;
+ msgpack_packer packer;
+
+ hashmap_foreach_value(connections, con, {
+ if (hashmap_has(cstr_t, ptr_t)(con->subscribed_events, name)) {
+ kv_push(subscribed, con);
+ }
+ });
+
+ if (!kv_size(subscribed)) {
+ api_free_array(args);
+ goto end;
+ }
+
+ string method = {.length = strlen(name), .str = name};
+
+ msgpack_packer_init(&packer, &sbuf, msgpack_sbuffer_write);
+ msgpack_rpc_serialize_request(0, method, args, &packer);
+ api_free_array(args);
+
+ for (size_t i = 0; i < kv_size(subscribed); i++) {
+ con = kv_A(subscribed, i);
+
+ if (con->pending_requests) {
+ wbuffer *rv = malloc_or_die(sizeof(wbuffer));
+ rv->size = sbuf.size;
+ rv->data = sb_memdup_nulterm(sbuf.data, sbuf.size);
+ kv_push(con->delayed_notifications, rv);
+ } else {
+ crypto_write(&con->cc, sbuf.data, sbuf.size, con->streams.write);
+ }
+ }
+
+ msgpack_sbuffer_clear(&sbuf);
+
+end:
+ kv_destroy(subscribed);
+}
+
+STATIC void unsubscribe(struct connection *con, char *event)
+{
+ char *event_string = hashmap_get(cstr_t, ptr_t)(event_strings, event);
+ hashmap_del(cstr_t, ptr_t)(con->subscribed_events, event_string);
+
+ hashmap_foreach_value(connections, con, {
+ if (hashmap_has(cstr_t, ptr_t)(con->subscribed_events, event_string)) {
+ return;
+ }
+ });
+
+ hashmap_del(cstr_t, ptr_t)(event_strings, event_string);
+ FREE(event_string);
+}
+
STATIC void incref(struct connection *con)
{
con->refcount++;
@@ -171,12 +268,12 @@ STATIC void decref(struct connection *con)
}
-int connection_hashmap_put(uint64_t id, struct connection *con)
+void connection_hashmap_put(uint64_t id, struct connection *con)
{
hashmap_put(uint64_t, ptr_t)(connections, id, con);
}
-int pluginkeys_hashmap_put(char *pluginkey, uint64_t id)
+void pluginkeys_hashmap_put(char *pluginkey, uint64_t id)
{
hashmap_put(cstr_t, uint64_t)(pluginkeys, pluginkey, id);
}
@@ -186,8 +283,16 @@ STATIC void free_connection(struct connection *con)
hashmap_del(uint64_t, ptr_t)(connections, con->id);
hashmap_del(cstr_t, uint64_t)(pluginkeys, con->cc.pluginkeystring);
msgpack_unpacker_free(con->mpac);
+
+ char *event_string;
+ hashmap_foreach_value(con->subscribed_events, event_string, {
+ unsubscribe(con, event_string);
+ });
+
+ hashmap_free(cstr_t, ptr_t)(con->subscribed_events);
kv_destroy(con->callvector);
- equeue_free(con->queue);
+ kv_destroy(con->delayed_notifications);
+ multiqueue_free(con->events);
if (con->packet.data)
FREE(con->packet.data);
@@ -203,17 +308,18 @@ STATIC void timer_cb(uv_timer_t *timer)
STATIC void connection_close(struct connection *con)
{
- int is_closing;
uv_handle_t *handle;
uv_handle_t *timer_handle;
if (con->closed)
return;
+ con->closed = true;
+
timer_handle = (uv_handle_t*) &con->minutekey_timer;
if (timer_handle) {
uv_close(timer_handle, NULL);
- uv_run(&loop, UV_RUN_ONCE);
+ uv_run(&main_loop.uv, UV_RUN_ONCE);
}
inputstream_free(con->streams.read);
@@ -223,10 +329,6 @@ STATIC void connection_close(struct connection *con)
if (handle)
uv_close(handle, close_cb);
- kv_destroy(con->callvector);
-
- con->closed = 0;
-
decref(con);
}
@@ -250,7 +352,31 @@ STATIC void reset_parser(struct connection *con)
reset_packet(con);
}
-STATIC int parse_cb(inputstream *istream, void *data, bool eof)
+STATIC bool is_rpc_response(msgpack_object *obj)
+{
+ return obj->type == MSGPACK_OBJECT_ARRAY
+ && obj->via.array.size == 4
+ && obj->via.array.ptr[0].type == MSGPACK_OBJECT_POSITIVE_INTEGER
+ && obj->via.array.ptr[0].via.u64 == 1
+ && obj->via.array.ptr[1].type == MSGPACK_OBJECT_POSITIVE_INTEGER;
+}
+
+STATIC void send_error(struct connection *con, uint64_t id, char *err)
+{
+ struct api_error e = ERROR_INIT;
+
+ error_set(&e, API_ERROR_TYPE_VALIDATION, "%s", err);
+
+ msgpack_packer pac;
+ msgpack_packer_init(&pac, &sbuf, msgpack_sbuffer_write);
+ msgpack_rpc_serialize_response(id, &e, NIL, &pac);
+
+ crypto_write(&con->cc, sbuf.data, sbuf.size, con->streams.write);
+
+ msgpack_sbuffer_clear(&sbuf);
+}
+
+STATIC void parse_cb(inputstream *istream, void *data, bool eof)
{
unsigned char *packet;
unsigned char hellopacket[192];
@@ -269,7 +395,7 @@ STATIC int parse_cb(inputstream *istream, void *data, bool eof)
if (eof) {
connection_close(con);
- goto fail;
+ goto end;
}
if (con->cc.state == TUNNEL_INITIAL) {
@@ -278,7 +404,7 @@ STATIC int parse_cb(inputstream *istream, void *data, bool eof)
con->streams.write) != 0)
LOG_WARNING("establishing crypto tunnel failed at hello-cookie packet");
- goto fail;
+ goto end;
} else if (con->cc.state == TUNNEL_COOKIE_SENT) {
size = inputstream_read(istream, initiatepacket, 256);
if (crypto_recv_initiate(&con->cc, initiatepacket) != 0) {
@@ -286,6 +412,15 @@ STATIC int parse_cb(inputstream *istream, void *data, bool eof)
con->cc.state = TUNNEL_INITIAL;
}
+ if (hashmap_has(cstr_t, uint64_t)(pluginkeys,
+ con->cc.pluginkeystring)) {
+ LOG_WARNING("pluginkey already registered, closing connection");
+ sbmemzero(con->cc.pluginkeystring,
+ sizeof con->cc.pluginkeystring);
+ connection_close(con);
+ goto end;
+ }
+
hashmap_put(cstr_t, uint64_t)(pluginkeys, con->cc.pluginkeystring,
con->id);
}
@@ -293,7 +428,7 @@ STATIC int parse_cb(inputstream *istream, void *data, bool eof)
pending = inputstream_pending(istream);
if (pending <= 0 || con->cc.state != TUNNEL_ESTABLISHED)
- goto fail;
+ goto end;
if (con->packet.end <= 0) {
packet = inputstream_get_read(istream, &read);
@@ -301,21 +436,16 @@ STATIC int parse_cb(inputstream *istream, void *data, bool eof)
/* read the packet length */
if (crypto_verify_header(&con->cc, packet, &con->packet.length)) {
reset_packet(con);
- goto fail;
+ goto end;
}
con->packet.end = con->packet.length;
- con->packet.data = MALLOC_ARRAY(MAX(con->packet.end, read), unsigned char);
-
- if (!con->packet.data) {
- LOG_ERROR("Failed to alloc mem for con packet.");
- goto fail;
- }
+ con->packet.data = malloc_array_or_die(MAX(con->packet.end, read), sizeof(unsigned char));
if (msgpack_unpacker_reserve_buffer(con->mpac,
MAX(read, con->packet.end)) == false) {
LOG_ERROR("Failed to reserve mem msgpack buffer.");
- goto fail;
+ goto end;
};
/* get decrypted message start position */
@@ -333,7 +463,7 @@ STATIC int parse_cb(inputstream *istream, void *data, bool eof)
if (crypto_read(&con->cc, con->packet.data, con->unpackbuf +
consumedlen, con->packet.length, &plaintextlen) != 0) {
reset_parser(con);
- goto fail;
+ goto end;
}
consumedlen += plaintextlen;
@@ -341,12 +471,12 @@ STATIC int parse_cb(inputstream *istream, void *data, bool eof)
if (packet == NULL) {
reset_parser(con);
- goto fail;
+ goto end;
}
if (crypto_verify_header(&con->cc, packet, &con->packet.length)) {
reset_parser(con);
- goto fail;
+ goto end;
}
con->packet.end = con->packet.length;
@@ -355,19 +485,17 @@ STATIC int parse_cb(inputstream *istream, void *data, bool eof)
}
if (con->packet.end > 0 && read == 0) {
- decref(con);
- return (0);
+ goto end;
}
if (crypto_read(&con->cc, con->packet.data, con->unpackbuf +
consumedlen, con->packet.length, &plaintextlen) != 0) {
reset_parser(con);
- goto fail;
+ goto end;
}
consumedlen += plaintextlen;
- reset_packet(con);
- FREE(con->packet.data);
+ reset_parser(con);
}
msgpack_unpacker_buffer_consumed(con->mpac, consumedlen);
@@ -377,9 +505,9 @@ STATIC int parse_cb(inputstream *istream, void *data, bool eof)
/* deserialize objects, one by one */
while ((ret =
msgpack_unpacker_next(con->mpac, &result)) == MSGPACK_UNPACK_SUCCESS) {
- if (message_is_request(&result.data))
- connection_handle_request(con, &result.data);
- else if (message_is_response(&result.data)) {
+ bool is_response = is_rpc_response(&result.data);
+
+ if (is_response) {
if (is_valid_rpc_response(&result.data, con)) {
connection_handle_response(con, &result.data);
} else {
@@ -387,35 +515,77 @@ STATIC int parse_cb(inputstream *istream, void *data, bool eof)
"request id. Ensure the client is properly "
"synchronized");
}
- } else {
- LOG_WARNING("invalid msgpack object");
- msgpack_object_print(stdout, result.data);
+
+ msgpack_unpacked_destroy(&result);
+ goto end;
}
+
+ connection_handle_request(con, &result.data);
}
- decref(con);
+ if (ret == MSGPACK_UNPACK_NOMEM_ERROR) {
+ decref(con);
+ exit(2);
+ }
- return (0);
+ if (ret == MSGPACK_UNPACK_PARSE_ERROR) {
+ send_error(con, 0, "Invalid msgpack payload. "
+ "This error can also happen when deserializing "
+ "an object with high level of nesting");
+ }
-fail:
+end:
decref(con);
- return (-1);
}
-struct callinfo connection_send_request(char *pluginkey, string method,
- array params, struct api_error *api_error)
+bool connection_send_event(uint64_t id, char *name, array args)
+{
+ msgpack_packer packer;
+ struct connection *con = NULL;
+
+ if (id && (!(con = hashmap_get(uint64_t, ptr_t)(connections, id))
+ || con->closed)) {
+ api_free_array(args);
+ return false;
+ }
+
+ if (con) {
+ string method = cstring_to_string(name);
+ msgpack_packer_init(&packer, &sbuf, msgpack_sbuffer_write);
+ msgpack_rpc_serialize_request(0, method, args, &packer);
+ api_free_array(args);
+
+ if (con->pending_requests) {
+ wbuffer *rv = malloc_or_die(sizeof(wbuffer));
+ rv->size = sbuf.size;
+ rv->data = sb_memdup_nulterm(sbuf.data, sbuf.size);
+ kv_push(con->delayed_notifications, rv);
+ } else {
+ crypto_write(&con->cc, sbuf.data, sbuf.size, con->streams.write);
+ }
+
+ msgpack_sbuffer_clear(&sbuf);
+ } else {
+ broadcast_event(name, args);
+ }
+
+ return true;
+}
+
+
+object connection_send_request(char *pluginkey, string method,
+ array args, struct api_error *err)
{
uint64_t id;
struct connection *con;
msgpack_packer packer;
- struct message_request request;
id = hashmap_get(cstr_t, uint64_t)(pluginkeys, pluginkey);
if (id == 0) {
- free_params(params);
- error_set(api_error, API_ERROR_TYPE_VALIDATION, "plugin not registered");
- return CALLINFO_INIT;
+ api_free_array(args);
+ error_set(err, API_ERROR_TYPE_VALIDATION, "plugin not registered");
+ return NIL;
}
con = hashmap_get(uint64_t, ptr_t)(connections, id);
@@ -425,44 +595,80 @@ struct callinfo connection_send_request(char *pluginkey, string method,
* the initial connection from the sender.
*/
if (!con) {
- free_params(params);
- error_set(api_error, API_ERROR_TYPE_VALIDATION, "plugin not registered");
- return CALLINFO_INIT;
+ api_free_array(args);
+ error_set(err, API_ERROR_TYPE_VALIDATION, "plugin not registered");
+ return NIL;
}
incref(con);
- request.msgid = con->msgid++;
- request.method = method;
- request.params = params;
+
+ uint64_t msgid = con->msgid++;
msgpack_packer_init(&packer, &sbuf, msgpack_sbuffer_write);
- message_serialize_request(&request, &packer);
- free_params(params);
+ msgpack_rpc_serialize_request(msgid, method, args, &packer);
+
+ api_free_array(args);
LOG_VERBOSE(VERBOSE_LEVEL_0, "sending request: method = %s, callinfo id = %u\n",
- method, request.msgid);
+ method.str, con->msgid);
if (crypto_write(&con->cc, sbuf.data, sbuf.size, con->streams.write) != 0)
- return CALLINFO_INIT;
+ return NIL;
+
+ msgpack_sbuffer_clear(&sbuf);
- struct callinfo cinfo = (struct callinfo) {request.msgid, false, false,((struct message_response) {0, ARRAY_INIT})};
+ struct callinfo cinfo = (struct callinfo) { msgid, false, false, NIL };
- loop_wait_for_response(con, &cinfo);
+ loop_process_events_until(&main_loop, con, &cinfo);
- msgpack_sbuffer_clear(&sbuf);
+ if (cinfo.errored) {
+ if (cinfo.result.type == OBJECT_TYPE_STR) {
+ error_set(err, API_ERROR_TYPE_EXCEPTION, "%s",
+ cinfo.result.data.string.str);
+ } else if (cinfo.result.type == OBJECT_TYPE_ARRAY) {
+ array array = cinfo.result.data.array;
+
+ if (array.size == 2 && array.items[0].type == OBJECT_TYPE_INT
+ && (array.items[0].data.integer == API_ERROR_TYPE_EXCEPTION
+ || array.items[0].data.integer == API_ERROR_TYPE_VALIDATION)
+ && array.items[1].type == OBJECT_TYPE_STR) {
+ err->type = (api_error_type) array.items[0].data.integer;
+ strlcpy(err->msg, array.items[1].data.string.str, sizeof(err->msg));
+ err->isset = true;
+ } else {
+ error_set(err, API_ERROR_TYPE_EXCEPTION, "%s", "unknown error");
+ }
+ } else {
+ error_set(err, API_ERROR_TYPE_EXCEPTION, "%s", "unknown error");
+ }
+
+ api_free_object(cinfo.result);
+ }
+
+ if (!con->pending_requests) {
+ send_delayed_notifications(con);
+ }
decref(con);
- if (cinfo.errorresponse)
- return CALLINFO_INIT;
+ return cinfo.errored ? NIL : cinfo.result;
+}
+
+STATIC void send_delayed_notifications(struct connection *con)
+{
+ for (size_t i = 0; i < kv_size(con->delayed_notifications); i++) {
+ wbuffer *buffer = kv_A(con->delayed_notifications, i);
+ crypto_write(&con->cc, buffer->data, buffer->size, con->streams.write);
+ FREE(buffer->data);
+ FREE(buffer);
+ }
- return cinfo;
+ kv_size(con->delayed_notifications) = 0;
}
int connection_send_response(uint64_t con_id, uint32_t msgid,
- array params, struct api_error *api_error)
+ object arg, struct api_error *api_error)
{
msgpack_packer packer;
- struct message_response response;
struct connection *con;
con = hashmap_get(uint64_t, ptr_t)(connections, con_id);
@@ -476,11 +682,8 @@ int connection_send_response(uint64_t con_id, uint32_t msgid,
return (-1);
}
- response.msgid = msgid;
- response.params = params;
-
msgpack_packer_init(&packer, &sbuf, msgpack_sbuffer_write);
- message_serialize_response(&response, &packer);
+ msgpack_rpc_serialize_response(msgid, api_error, arg, &packer);
if (api_error->isset) {
return (-1);
@@ -491,91 +694,92 @@ int connection_send_response(uint64_t con_id, uint32_t msgid,
}
msgpack_sbuffer_clear(&sbuf);
- free_params(params);
+ api_free_object(arg);
return 0;
}
-STATIC int connection_handle_request(struct connection *con,
+STATIC void connection_handle_request(struct connection *con,
msgpack_object *obj)
{
+ array args = ARRAY_DICT_INIT;
+ uint64_t msgid;
dispatch_info dispatcher;
+ msgpack_object *method;
struct api_error api_error = ERROR_INIT;
- connection_request_event_info eventinfo;
- api_event event;
- if (!obj || !con)
- return (-1);
+ msgpack_rpc_validate(&msgid, obj, &api_error);
- if (message_deserialize_request(&eventinfo.request, obj, &api_error) != 0) {
- /* request wasn't parsed correctly, send error with pseudo RESPONSE ID*/
- eventinfo.request.msgid = MESSAGE_RESPONSE_UNKNOWN;
- eventinfo.request.method = cstring_copy_string("error");
+ if (api_error.isset) {
+ send_error(con, msgid, "Invalid message from connection, closed");
}
- LOG_VERBOSE(VERBOSE_LEVEL_0, "received request: method = %s\n",
- eventinfo.request.method.str);
+ method = msgpack_rpc_method(obj);
- dispatcher = dispatch_table_get(eventinfo.request.method);
+ if (method) {
+ dispatcher = msgpack_rpc_get_handler_for(method->via.bin.ptr,
+ method->via.bin.size);
+ } else {
+ dispatcher.func = msgpack_rpc_handle_missing_method;
+ dispatcher.async = true;
+ }
- if (dispatcher.func == NULL) {
- LOG_VERBOSE(VERBOSE_LEVEL_0, "could not dispatch method\n");
- error_set(&api_error, API_ERROR_TYPE_VALIDATION, "could not dispatch method");
- dispatcher.func = handle_error;
+ if (!msgpack_rpc_to_array(msgpack_rpc_args(obj), &args)) {
+ dispatcher.func = msgpack_rpc_handle_invalid_arguments;
dispatcher.async = true;
}
- eventinfo.con = con;
- eventinfo.api_error = api_error;
- eventinfo.dispatcher = dispatcher;
+ connection_request_event_info *eventinfo = malloc_or_die(sizeof(connection_request_event_info));
+ eventinfo->con = con;
+ eventinfo->dispatcher = dispatcher;
+ eventinfo->args = args;
+ eventinfo->msgid = msgid;
incref(con);
if (dispatcher.async)
- connection_request_event(&eventinfo);
+ connection_request_event((void**)&eventinfo);
else {
- event.handler = connection_request_event;
- event.info = eventinfo;
- equeue_put(con->queue, event);
- /* TODO: move this call to a suitable place (main?) */
- equeue_run_events(equeue_root);
+ multiqueue_put(con->events, connection_request_event, 1, eventinfo);
}
-
- return (0);
}
-STATIC void connection_request_event(connection_request_event_info *eventinfo)
+STATIC void connection_request_event(void **argv)
{
+ connection_request_event_info *eventinfo = argv[0];
+ object result;
msgpack_packer packer;
struct connection *con;
+ struct api_error error = ERROR_INIT;
con = eventinfo->con;
+ array args = eventinfo->args;
+ uint64_t msgid = eventinfo->msgid;
+ dispatch_info handler = eventinfo->dispatcher;
- eventinfo->dispatcher.func(con->id, &eventinfo->request,
- con->cc.pluginkeystring, &eventinfo->api_error);
+ result = handler.func(con->id, msgid, con->cc.pluginkeystring, args, &error);
- if (eventinfo->api_error.isset) {
+ if (eventinfo->msgid != UINT64_MAX) {
msgpack_packer_init(&packer, &sbuf, msgpack_sbuffer_write);
- message_serialize_error_response(&packer, &eventinfo->api_error,
- eventinfo->request.msgid);
-
+ msgpack_rpc_serialize_response(msgid, &error, result, &packer);
crypto_write(&eventinfo->con->cc, sbuf.data, sbuf.size,
eventinfo->con->streams.write);
-
msgpack_sbuffer_clear(&sbuf);
+ } else {
+ api_free_object(result);
}
- free_params(eventinfo->request.params);
- free_string(eventinfo->request.method);
+ api_free_array(args);
decref(con);
+ FREE(eventinfo);
}
STATIC int is_valid_rpc_response(msgpack_object *obj, struct connection *con)
{
- uint64_t msg_id = message_get_id(obj);
+ uint64_t msg_id = obj->via.array.ptr[1].via.u64;
return kv_size(con->callvector) && msg_id
== kv_A(con->callvector, kv_size(con->callvector) - 1)->msgid;
@@ -586,20 +790,19 @@ STATIC void connection_handle_response(struct connection *con,
msgpack_object *obj)
{
struct callinfo *cinfo;
- struct api_error api_error = { .isset = false };
cinfo = kv_A(con->callvector, kv_size(con->callvector) - 1);
- LOG_VERBOSE(VERBOSE_LEVEL_0, "received response: callinfo id = %u\n",
+ LOG_VERBOSE(VERBOSE_LEVEL_0, "received response: callinfo id = %lu\n",
cinfo->msgid);
- cinfo->hasresponse = true;
- cinfo->errorresponse = message_is_error_response(obj);
+ cinfo->returned = true;
+ cinfo->errored = (obj->via.array.ptr[2].type != MSGPACK_OBJECT_NIL);
- if (cinfo->errorresponse) {
- message_deserialize_error_response(&cinfo->response, obj, &api_error);
+ if (cinfo->errored) {
+ msgpack_rpc_to_object(&obj->via.array.ptr[2], &cinfo->result);
} else {
- message_deserialize_response(&cinfo->response, obj, &api_error);
+ msgpack_rpc_to_object(&obj->via.array.ptr[3], &cinfo->result);
}
}
@@ -609,8 +812,8 @@ STATIC void call_set_error(struct connection *con, UNUSED(char *msg))
for (size_t i = 0; i < kv_size(con->callvector); i++) {
cinfo = kv_A(con->callvector, i);
- cinfo->errorresponse = true;
- cinfo->hasresponse = true;
+ cinfo->errored = true;
+ cinfo->returned = true;
}
connection_close(con);
diff --git a/src/rpc/connection/connection.h b/src/rpc/connection/connection.h
index 9d0a414..4a8b485 100644
--- a/src/rpc/connection/connection.h
+++ b/src/rpc/connection/connection.h
@@ -16,15 +16,42 @@
#pragma once
-#include "rpc/sb-rpc.h"
+#include // for msgpack_sbuffer
+#include // for msgpack_unpacker
+#include // for bool
+#include // for size_t
+#include // for uint64_t, uint32_t
+#include // for uv_stream_t, uv_timer_t
+#include "kvec.h" // for kvec_t
+#include "rpc/connection/event.h" // for multiqueue
+#include "rpc/sb-rpc.h" // for crypto_context, hashmap_cstr_t_ptr_t
+#include "sb-common.h" // for hashmap
-STATIC void close_cb(uv_handle_t *handle);
-STATIC int connection_handle_request(struct connection *con,
- msgpack_object *obj);
-STATIC void connection_handle_response(struct connection *con,
- msgpack_object *obj);
-STATIC void connection_request_event(connection_request_event_info *info);
-STATIC void connection_close(struct connection *con);
-STATIC int parse_cb(inputstream *istream, void *data, bool eof);
-STATIC int is_valid_rpc_response(msgpack_object *obj, struct connection *con);
-STATIC void call_set_error(struct connection *con, char *msg);
+struct connection {
+ uint64_t id;
+ uint32_t msgid;
+ size_t pending_requests;
+ size_t refcount;
+ msgpack_unpacker *mpac;
+ msgpack_sbuffer *sbuf;
+ char *unpackbuf;
+ bool closed;
+ multiqueue *events;
+ struct {
+ inputstream *read;
+ outputstream *write;
+ uv_stream_t *uv;
+ } streams;
+ kvec_t(struct callinfo *) callvector;
+ kvec_t(wbuffer *) delayed_notifications;
+ struct crypto_context cc;
+ struct {
+ uint64_t start;
+ uint64_t end;
+ uint64_t pos;
+ uint64_t length;
+ unsigned char *data;
+ } packet;
+ uv_timer_t minutekey_timer;
+ hashmap(cstr_t, ptr_t) *subscribed_events;
+};
diff --git a/src/rpc/connection/crypto.c b/src/rpc/connection/crypto.c
index f45c1b5..8562496 100644
--- a/src/rpc/connection/crypto.c
+++ b/src/rpc/connection/crypto.c
@@ -1,10 +1,12 @@
-#include
-#include
-#include "sb-common.h"
-#include "rpc/sb-rpc.h"
-#include "rpc/db/sb-db.h"
#include "rpc/connection/crypto.h"
-#include "tweetnacl.h"
+#include // for uint64_t
+#include // for exit
+#include // for memcpy, NULL, size_t
+#include // for close
+#include "rpc/db/sb-db.h" // for db_authorized_verify, db_authorized_whitel...
+#include "rpc/sb-rpc.h" // for crypto_context, outputstream_write, output...
+#include "sb-common.h" // for sbmemzero, sbassert, FREE, ISODD, STATIC
+#include "tweetnacl.h" // for crypto_box_NONCEBYTES, crypto_box_open_aft...
#define CRYPTO_PREFIX_SPLONEBOXCLIENT "splonebox-client"
#define CRYPTO_PREFIX_SPLONEBOXSERVER "splonebox-server"
@@ -498,10 +500,7 @@ int crypto_write(struct crypto_context *cc, char *data,
* (crypto_box_ZEROBYTES)
*/
packetlen = length + 56;
- packet = MALLOC_ARRAY(packetlen, unsigned char);
-
- if (packet == NULL)
- return -1;
+ packet = malloc_array_or_die(packetlen, sizeof(unsigned char));
/* update nonce */
nonce_update(cc);
@@ -525,11 +524,8 @@ int crypto_write(struct crypto_context *cc, char *data,
memcpy(packet + 16, lengthbox + 16, 24);
blocklen = length + 32;
- block = CALLOC(blocklen, unsigned char);
- ciphertext = MALLOC_ARRAY(blocklen, unsigned char);
-
- if ((block == NULL) || (ciphertext == NULL))
- return -1;
+ block = calloc_or_die(blocklen, sizeof(unsigned char));
+ ciphertext = malloc_array_or_die(blocklen, sizeof(unsigned char));
memcpy(block + 32, data, length);
@@ -591,11 +587,8 @@ int crypto_read(struct crypto_context *cc, unsigned char *in, char *out,
/* ciphertextlen = length - 8 (id) - 72 (length) - 8 (nonce) + 16 (padding) */
ciphertextlen = length - 24;
- block = MALLOC_ARRAY(ciphertextlen, unsigned char);
- ciphertextpadded = CALLOC(ciphertextlen, unsigned char);
-
- if ((block == NULL) || (ciphertextpadded == NULL))
- return -1;
+ block = malloc_array_or_die(ciphertextlen, sizeof(unsigned char));
+ ciphertextpadded = calloc_or_die(ciphertextlen, sizeof(unsigned char));
memcpy(ciphertextpadded + 16, in + 40, blocklen);
diff --git a/src/rpc/connection/dispatch.c b/src/rpc/connection/dispatch.c
index ce6bdd1..65b90df 100644
--- a/src/rpc/connection/dispatch.c
+++ b/src/rpc/connection/dispatch.c
@@ -30,20 +30,36 @@
* limitations under the License.
*/
-#include
-
-#include "rpc/sb-rpc.h"
-#include "api/sb-api.h"
-#include "sb-common.h"
+#include // for msgpack_sbuffer_init, msgpack_sbuffer
+#include // for false, true
+#include // for uint64_t
+#include // for snprintf
+#include // for NULL, size_t
+#include "api/helpers.h" // for ARRAY_OBJ, ADD, NIL, UINTEGER_OBJ
+#include "api/sb-api.h" // for api_broadcast, api_register, api_result
+#include "rpc/sb-rpc.h" // for object, array, object::(anonymous), dis...
+#include "sb-common.h" // for ::API_ERROR_TYPE_VALIDATION, error_set
static msgpack_sbuffer sbuf;
static hashmap(string, dispatch_info) *dispatch_table = NULL;
static hashmap(uint64_t, ptr_t) *callids = NULL;
-int handle_error(uint64_t con_id, struct message_request *request,
- char *pluginkey, struct api_error *error)
+object msgpack_rpc_handle_missing_method(UNUSED(uint64_t channel_id),
+ UNUSED(uint64_t msgid), UNUSED(char *pluginkey), UNUSED(array args),
+ struct api_error *error)
{
- return (0);
+ snprintf(error->msg, sizeof(error->msg), "Invalid method name");
+ error->isset = true;
+ return NIL;
+}
+
+object msgpack_rpc_handle_invalid_arguments(UNUSED(uint64_t channel_id),
+ UNUSED(uint64_t msgid), UNUSED(char *pluginkey), UNUSED(array args),
+ struct api_error *error)
+{
+ snprintf(error->msg, sizeof(error->msg), "Invalid method arguments");
+ error->isset = true;
+ return NIL;
}
/*
@@ -53,29 +69,31 @@ int handle_error(uint64_t con_id, struct message_request *request,
* @param api_error `struct api_error` error object-instance
* @return 0 if success, -1 otherwise
*/
-int handle_register(uint64_t con_id, struct message_request *request,
- char *pluginkey, struct api_error *error)
+object handle_register(UNUSED(uint64_t con_id), UNUSED(uint64_t msgid),
+ char *pluginkey, array args, struct api_error *error)
{
- array *meta = NULL;
- array functions;
+ array rv = ARRAY_DICT_INIT;
+ object ret = ARRAY_OBJ(rv);
+ array meta = ARRAY_DICT_INIT;
+ array functions = ARRAY_DICT_INIT;
string name, description, author, license;
- if (!error || !request)
- return (-1);
+ if (!error)
+ goto end;
/* check params size */
- if (request->params.size != 2) {
+ if (args.size != 2) {
error_set(error, API_ERROR_TYPE_VALIDATION,
"Error dispatching register API request. Invalid params size");
- return (-1);
+ goto end;
}
- if (request->params.obj[0].type == OBJECT_TYPE_ARRAY)
- meta = &request->params.obj[0].data.params;
+ if (args.items[0].type == OBJECT_TYPE_ARRAY)
+ meta = args.items[0].data.array;
else {
error_set(error, API_ERROR_TYPE_VALIDATION,
"Error dispatching register API request. meta params has wrong type");
- return (-1);
+ goto end;
}
/*
@@ -83,240 +101,347 @@ int handle_register(uint64_t con_id, struct message_request *request,
* [name, description, author, license]
*/
- if (!meta) {
- error_set(error, API_ERROR_TYPE_VALIDATION,
- "Error dispatching register API request. meta params is NULL");
- return (-1);
- }
-
- if (meta->size != 4) {
+ if (meta.size != 4) {
error_set(error, API_ERROR_TYPE_VALIDATION,
"Error dispatching register API request. Invalid meta params size");
- return (-1);
+ goto end;
}
/* extract meta information */
- if ((meta->obj[0].type != OBJECT_TYPE_STR) ||
- (meta->obj[1].type != OBJECT_TYPE_STR) ||
- (meta->obj[2].type != OBJECT_TYPE_STR) ||
- (meta->obj[3].type != OBJECT_TYPE_STR)) {
+ if ((meta.items[0].type != OBJECT_TYPE_STR) ||
+ (meta.items[1].type != OBJECT_TYPE_STR) ||
+ (meta.items[2].type != OBJECT_TYPE_STR) ||
+ (meta.items[3].type != OBJECT_TYPE_STR)) {
error_set(error, API_ERROR_TYPE_VALIDATION,
"Error dispatching register API request. meta element has wrong type");
- return (-1);
+ goto end;
}
- if (!meta->obj[0].data.string.str || !meta->obj[1].data.string.str ||
- !meta->obj[2].data.string.str || !meta->obj[3].data.string.str) {
+ if (!meta.items[0].data.string.str || !meta.items[1].data.string.str ||
+ !meta.items[2].data.string.str || !meta.items[3].data.string.str) {
error_set(error, API_ERROR_TYPE_VALIDATION,
"Error dispatching register API request. Invalid meta params size");
- return (-1);
+ goto end;
}
- name = meta->obj[0].data.string;
- description = meta->obj[1].data.string;
- author = meta->obj[2].data.string;
- license = meta->obj[3].data.string;
+ name = meta.items[0].data.string;
+ description = meta.items[1].data.string;
+ author = meta.items[2].data.string;
+ license = meta.items[3].data.string;
- if (request->params.obj[1].type != OBJECT_TYPE_ARRAY) {
+ if (args.items[1].type == OBJECT_TYPE_ARRAY) {
+ functions = args.items[1].data.array;
+ } else {
error_set(error, API_ERROR_TYPE_VALIDATION,
"Error dispatching register API request. functions has wrong type");
- return (-1);
+ goto end;
}
- functions = request->params.obj[1].data.params;
-
- if (api_register(name, description, author, license,
- functions, con_id, request->msgid, pluginkey, error) == -1) {
+ if (api_register(name, description, author, license, functions, pluginkey,
+ error) == -1) {
if (!error->isset)
error_set(error, API_ERROR_TYPE_VALIDATION,
"Error running register API request.");
- return (-1);
+ goto end;
}
- return (0);
+end:
+ return ret;
}
-int handle_run(uint64_t con_id, struct message_request *request,
- char *pluginkey, struct api_error *error)
+object handle_run(UNUSED(uint64_t con_id), UNUSED(uint64_t msgid),
+ char *pluginkey, array args, struct api_error *error)
{
+ array rv = ARRAY_DICT_INIT;
+ array meta = ARRAY_DICT_INIT;
+ array runargs = ARRAY_DICT_INIT;
+ string function_name = STRING_INIT;
+ object ret = ARRAY_OBJ(rv);
uint64_t callid;
- array *meta = NULL;
- string function_name;
char *targetpluginkey;
- struct message_object args_object;
-
- if (!error || !request)
- return (-1);
+ if (!error)
+ goto end;
/* check params size */
- if (request->params.size != 3) {
+ if (args.size != 3) {
error_set(error, API_ERROR_TYPE_VALIDATION,
"Error dispatching run API request. Invalid params size");
- return (-1);
+ goto end;
}
- if (request->params.obj[0].type == OBJECT_TYPE_ARRAY)
- meta = &request->params.obj[0].data.params;
+ if (args.items[0].type == OBJECT_TYPE_ARRAY)
+ meta = args.items[0].data.array;
else {
error_set(error, API_ERROR_TYPE_VALIDATION,
"Error dispatching run API request. meta params has wrong type");
- return (-1);
- }
-
- if (!meta) {
- error_set(error, API_ERROR_TYPE_VALIDATION,
- "Error dispatching run API request. meta params is NULL");
- return (-1);
+ goto end;
}
/* meta = [targetpluginkey, nil]*/
- if (meta->size != 2) {
+ if (meta.size != 2) {
error_set(error, API_ERROR_TYPE_VALIDATION,
"Error dispatching run API request. Invalid meta params size");
- return (-1);
+ goto end;
}
/* extract meta information */
- if (meta->obj[0].type != OBJECT_TYPE_STR) {
+ if (meta.items[0].type != OBJECT_TYPE_STR) {
error_set(error, API_ERROR_TYPE_VALIDATION,
"Error dispatching run API request. meta elements have wrong type");
- return (-1);
+ goto end;
}
- if (!meta->obj[0].data.string.str ||
- meta->obj[0].data.string.length+1 != PLUGINKEY_STRING_SIZE) {
+ if (!meta.items[0].data.string.str ||
+ meta.items[0].data.string.length + 1 != PLUGINKEY_STRING_SIZE) {
error_set(error, API_ERROR_TYPE_VALIDATION,
"Error dispatching run API request. Invalid meta params size");
- return (-1);
+ goto end;
}
- targetpluginkey = meta->obj[0].data.string.str;
+ targetpluginkey = meta.items[0].data.string.str;
to_upper(targetpluginkey);
- if (meta->obj[1].type != OBJECT_TYPE_NIL) {
+ if (meta.items[1].type != OBJECT_TYPE_NIL) {
error_set(error, API_ERROR_TYPE_VALIDATION,
"Error dispatching run API request. meta elements have wrong type");
- return (-1);
+ goto end;
}
- if (request->params.obj[1].type != OBJECT_TYPE_STR) {
+ if (args.items[1].type == OBJECT_TYPE_STR) {
+ function_name = args.items[1].data.string;
+ } else {
error_set(error, API_ERROR_TYPE_VALIDATION,
"Error dispatching run API request. function string has wrong type");
- return (-1);
- }
-
- if (!request->params.obj[1].data.string.str) {
- error_set(error, API_ERROR_TYPE_VALIDATION,
- "Error dispatching register API request. Invalid meta params size");
- return (-1);
+ goto end;
}
- function_name = request->params.obj[1].data.string;
-
- if (request->params.obj[2].type != OBJECT_TYPE_ARRAY) {
+ if (args.items[2].type == OBJECT_TYPE_ARRAY) {
+ runargs = args.items[2].data.array;
+ } else {
error_set(error, API_ERROR_TYPE_VALIDATION,
"Error dispatching run API request. function string has wrong type");
- return (-1);
+ goto end;
}
- args_object = request->params.obj[2];
callid = (uint64_t) randommod(281474976710656LL);
LOG_VERBOSE(VERBOSE_LEVEL_1, "generated callid %lu\n", callid);
hashmap_put(uint64_t, ptr_t)(callids, callid, pluginkey);
- if (api_run(targetpluginkey, function_name, callid, args_object, con_id,
- request->msgid, error) == -1) {
+ if (api_run(targetpluginkey, function_name, callid, runargs, error) == -1) {
if (false == error->isset)
error_set(error, API_ERROR_TYPE_VALIDATION,
"Error executing run API request.");
- return (-1);
+ goto end;
}
- return (0);
+ ADD(rv, UINTEGER_OBJ(callid));
+ ret = ARRAY_OBJ(rv);
+
+end:
+ return ret;
}
-int handle_result(uint64_t con_id, struct message_request *request,
- char *pluginkey, struct api_error *error)
+object handle_result(UNUSED(uint64_t con_id), UNUSED(uint64_t msgid),
+ UNUSED(char *pluginkey), array args, struct api_error *error)
{
+ array rv = ARRAY_DICT_INIT;
+ array meta = ARRAY_DICT_INIT;
+ array resultargs = ARRAY_DICT_INIT;
+ object ret = ARRAY_OBJ(rv);
uint64_t callid;
- array *meta = NULL;
- struct message_object args_object;
char * targetpluginkey;
- if (!error || !request)
- return (-1);
+ if (!error)
+ goto end;
/* check params size */
- if (request->params.size != 2) {
+ if (args.size != 2) {
error_set(error, API_ERROR_TYPE_VALIDATION,
"Error dispatching result API request. Invalid params size");
- return (-1);
+ goto end;
}
- if (request->params.obj[0].type == OBJECT_TYPE_ARRAY)
- meta = &request->params.obj[0].data.params;
+ if (args.items[0].type == OBJECT_TYPE_ARRAY)
+ meta = args.items[0].data.array;
else {
error_set(error, API_ERROR_TYPE_VALIDATION,
"Error dispatching result API request. meta params has wrong type");
- return (-1);
- }
-
- if (!meta) {
- error_set(error, API_ERROR_TYPE_VALIDATION,
- "Error dispatching result API request. meta params is NULL");
- return (-1);
+ goto end;
}
/* meta = [callid]*/
- if (meta->size != 1) {
+ if (meta.size != 1) {
error_set(error, API_ERROR_TYPE_VALIDATION,
"Error dispatching result API request. Invalid meta params size");
- return (-1);
+ goto end;
}
/* extract meta information */
- if (meta->obj[0].type != OBJECT_TYPE_UINT) {
+ if (meta.items[0].type == OBJECT_TYPE_UINT) {
+ callid = meta.items[0].data.uinteger;
+ } else {
error_set(error, API_ERROR_TYPE_VALIDATION,
"Error dispatching result API request. meta elements have wrong type");
- return (-1);
+ goto end;
}
- callid = meta->obj[0].data.uinteger;
-
- if (request->params.obj[1].type != OBJECT_TYPE_ARRAY) {
+ if (args.items[1].type == OBJECT_TYPE_ARRAY) {
+ resultargs = args.items[1].data.array;
+ } else {
error_set(error, API_ERROR_TYPE_VALIDATION,
"Error dispatching result API request. function string has wrong type");
- return (-1);
+ goto end;
}
- args_object = request->params.obj[1];
-
targetpluginkey = hashmap_get(uint64_t, ptr_t)(callids, callid);
if (!targetpluginkey) {
error_set(error, API_ERROR_TYPE_VALIDATION,
"Failed to find target's key associated with given callid.");
- return (-1);
+ goto end;
}
- if (api_result(targetpluginkey, callid, args_object, con_id, request->msgid,
- error) == -1) {
+ if (api_result(targetpluginkey, callid, resultargs, error) == -1) {
if (false == error->isset)
error_set(error, API_ERROR_TYPE_VALIDATION,
"Error executing result API request.");
- return (-1);
+ goto end;
}
hashmap_del(uint64_t, ptr_t)(callids, callid);
- return (0);
+ ADD(rv, UINTEGER_OBJ(callid));
+ ret = ARRAY_OBJ(rv);
+
+end:
+ return ret;
+}
+
+
+object handle_broadcast(UNUSED(uint64_t con_id), UNUSED(uint64_t msgid),
+ UNUSED(char *pluginkey), array args, struct api_error *error)
+{
+ array rv = ARRAY_DICT_INIT;
+ array eventargs = ARRAY_DICT_INIT;
+ object ret = ARRAY_OBJ(rv);
+ string eventname = STRING_INIT;
+
+ if (!error)
+ goto end;
+
+ /* check params size */
+ if (args.size != 2) {
+ error_set(error, API_ERROR_TYPE_VALIDATION,
+ "Error dispatching broadcast API request. Invalid params size");
+ goto end;
+ }
+
+ if (args.items[0].type == OBJECT_TYPE_STR)
+ eventname = args.items[0].data.string;
+ else {
+ error_set(error, API_ERROR_TYPE_VALIDATION,
+ "Error dispatching broadcast API request. event name has wrong type");
+ goto end;
+ }
+
+ if (args.items[1].type == OBJECT_TYPE_ARRAY)
+ eventargs = args.items[1].data.array;
+ else {
+ error_set(error, API_ERROR_TYPE_VALIDATION,
+ "Error dispatching broadcast API request. event args has wrong type");
+ goto end;
+ }
+
+ if (api_broadcast(eventname, eventargs, error) == -1) {
+ if (false == error->isset)
+ error_set(error, API_ERROR_TYPE_VALIDATION,
+ "Error executing broadcast API request.");
+ goto end;
+ }
+
+end:
+ return ret;
}
+
+object handle_subscribe(uint64_t con_id, UNUSED(uint64_t msgid),
+ UNUSED(char *pluginkey), array args, struct api_error *error)
+{
+ array rv = ARRAY_DICT_INIT;
+ object ret = ARRAY_OBJ(rv);
+ string eventname = STRING_INIT;
+
+ if (!error)
+ goto end;
+
+ /* check params size */
+ if (args.size != 1) {
+ error_set(error, API_ERROR_TYPE_VALIDATION,
+ "Error dispatching broadcast API request. Invalid params size");
+ goto end;
+ }
+
+ if (args.items[0].type == OBJECT_TYPE_STR)
+ eventname = args.items[0].data.string;
+ else {
+ error_set(error, API_ERROR_TYPE_VALIDATION,
+ "Error dispatching broadcast API request. event name has wrong type");
+ goto end;
+ }
+
+ if (api_subscribe(con_id, eventname, error) == -1) {
+ if (false == error->isset)
+ error_set(error, API_ERROR_TYPE_VALIDATION,
+ "Error executing broadcast API request.");
+ goto end;
+ }
+
+end:
+ return ret;
+}
+
+object handle_unsubscribe(uint64_t con_id, UNUSED(uint64_t msgid),
+ UNUSED(char *pluginkey), array args, struct api_error *error)
+{
+ array rv = ARRAY_DICT_INIT;
+ object ret = ARRAY_OBJ(rv);
+ string eventname = STRING_INIT;
+
+ if (!error)
+ goto end;
+
+ /* check params size */
+ if (args.size != 1) {
+ error_set(error, API_ERROR_TYPE_VALIDATION,
+ "Error dispatching broadcast API request. Invalid params size");
+ goto end;
+ }
+
+ if (args.items[0].type == OBJECT_TYPE_STR)
+ eventname = args.items[0].data.string;
+ else {
+ error_set(error, API_ERROR_TYPE_VALIDATION,
+ "Error dispatching broadcast API request. event name has wrong type");
+ goto end;
+ }
+
+ if (api_unsubscribe(con_id, eventname, error) == -1) {
+ if (false == error->isset)
+ error_set(error, API_ERROR_TYPE_VALIDATION,
+ "Error executing broadcast API request.");
+ goto end;
+ }
+
+end:
+ return ret;
+}
+
+
void dispatch_table_put(string method, dispatch_info info)
{
hashmap_put(string, dispatch_info)(dispatch_table, method, info);
@@ -337,17 +462,35 @@ int dispatch_teardown(void)
return (0);
}
+dispatch_info msgpack_rpc_get_handler_for(const char *name, size_t name_len)
+{
+ string m = { .str = (char *)name, .length = name_len };
+ dispatch_info rv =
+ hashmap_get(string, dispatch_info)(dispatch_table, m);
+
+ if (!rv.func) {
+ rv.func = msgpack_rpc_handle_missing_method;
+ }
+
+ return rv;
+}
+
int dispatch_table_init(void)
{
- dispatch_info register_info = {.func = handle_register, .async = true,
+ dispatch_info register_info = {.func = handle_register, .async = false,
.name = (string) {.str = "register", .length = sizeof("register") - 1}};
- dispatch_info run_info = {.func = handle_run, .async = true,
+ dispatch_info run_info = {.func = handle_run, .async = false,
.name = (string) {.str = "run", .length = sizeof("run") - 1}};
- dispatch_info error_info = {.func = handle_error, .async = true,
- .name = (string) {.str = "error", .length = sizeof("error") - 1}};
- dispatch_info result_info = {.func = handle_result, .async = true,
+ dispatch_info result_info = {.func = handle_result, .async = false,
.name = (string) {.str = "result", .length = sizeof("result") - 1,}};
+ dispatch_info broadcast_info = {.func = handle_broadcast, .async = true,
+ .name = (string) {.str = "broadcast", .length = sizeof("broadcast") - 1,}};
+ dispatch_info subscribe_info = {.func = handle_subscribe, .async = false,
+ .name = (string) {.str = "subscribe", .length = sizeof("subscribe") - 1,}};
+ dispatch_info unsubscribe_info = {.func = handle_unsubscribe, .async = false,
+ .name = (string) {.str = "unsubscribe", .length = sizeof("unsubscribe") - 1,}};
+
msgpack_sbuffer_init(&sbuf);
@@ -359,9 +502,10 @@ int dispatch_table_init(void)
dispatch_table_put(register_info.name, register_info);
dispatch_table_put(run_info.name, run_info);
- dispatch_table_put(error_info.name, error_info);
dispatch_table_put(result_info.name, result_info);
-
+ dispatch_table_put(broadcast_info.name, broadcast_info);
+ dispatch_table_put(subscribe_info.name, subscribe_info);
+ dispatch_table_put(unsubscribe_info.name, unsubscribe_info);
return (0);
}
diff --git a/src/rpc/connection/event-defs.h b/src/rpc/connection/event-defs.h
new file mode 100644
index 0000000..1e5b0de
--- /dev/null
+++ b/src/rpc/connection/event-defs.h
@@ -0,0 +1,70 @@
+/**
+ * Copyright (C) 2015 splone UG
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ * This file incorporates code covered by the following terms:
+ *
+ * Copyright Neovim contributors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include
+#include "sb-common.h"
+
+#define EVENT_HANDLER_MAX_ARGC 6
+
+typedef void (*argv_callback)(void **argv);
+typedef struct message {
+ int priority;
+ argv_callback handler;
+ void *argv[EVENT_HANDLER_MAX_ARGC];
+} event;
+
+typedef void(*event_scheduler)(event e, void *data);
+
+#define VA_EVENT_INIT(event, p, h, a) \
+ do { \
+ sbassert(a <= EVENT_HANDLER_MAX_ARGC); \
+ (event)->priority = p; \
+ (event)->handler = h; \
+ if (a) { \
+ va_list args; \
+ va_start(args, a); \
+ for (int i = 0; i < a; i++) { \
+ (event)->argv[i] = va_arg(args, void *); \
+ } \
+ va_end(args); \
+ } \
+ } while (0)
+
+static inline event event_create(int priority, argv_callback cb, int argc, ...)
+{
+ sbassert(argc <= EVENT_HANDLER_MAX_ARGC);
+ event e;
+ VA_EVENT_INIT(&e, priority, cb, argc);
+ return e;
+}
diff --git a/src/rpc/connection/event.c b/src/rpc/connection/event.c
index 6d997d3..8a4fa33 100644
--- a/src/rpc/connection/event.c
+++ b/src/rpc/connection/event.c
@@ -29,156 +29,164 @@
* limitations under the License.
*/
-#include
-
-#include "sb-common.h"
-#include "rpc/sb-rpc.h"
#include "rpc/connection/event.h"
-
-equeue *equeue_root;
-
-int event_initialize(void)
+#include // for bool, false, true
+#include // for NULL
+#include "queue.h" // for QUEUE_REMOVE, QUEUE, QUEUE_EMPTY, QUEUE_INSER...
+#include "sb-common.h" // for sbassert, FREE, MALLOC
+
+typedef struct multiqueue_item multiqueueitem;
+
+struct multiqueue_item {
+ union {
+ multiqueue *queue;
+ struct {
+ event event;
+ multiqueueitem *parent;
+ } item;
+ } data;
+ bool link; // true: current item is just a link to a node in a child queue
+ QUEUE node;
+};
+
+struct multiqueue {
+ multiqueue *parent;
+ QUEUE headtail;
+ put_callback put_cb;
+ void *data;
+};
+
+static multiqueue *multiqueue_new(multiqueue *parent, put_callback put_cb, void *data);
+static event multiqueue_remove(multiqueue *this);
+static void multiqueue_push(multiqueue *this, event e);
+static multiqueueitem *multiqueue_node_data(QUEUE *q);
+
+static event NILEVENT = { .handler = NULL, .argv = {NULL} };
+
+multiqueue *multiqueue_new_parent(put_callback put_cb, void *data)
{
- /* initialize event root queue */
- equeue_root = equeue_new(NULL);
-
- if (!equeue_root)
- return (-1);
-
- return 0;
+ return multiqueue_new(NULL, put_cb, data);
}
-
-equeue *equeue_new(equeue *root)
+multiqueue *multiqueue_new_child(multiqueue *parent)
{
- equeue *queue = MALLOC(equeue);
-
- if (!queue)
- return (NULL);
-
- queue->root = root;
-
- TAILQ_INIT(&queue->head);
-
- return (queue);
+ sbassert(!parent->parent);
+ return multiqueue_new(parent, NULL, NULL);
}
-
-/**
- * check if queue is empty
- *
- * @params queue queue instance
- */
-bool equeue_empty(equeue *queue)
+static multiqueue *multiqueue_new(multiqueue *parent, put_callback put_cb,
+ void *data)
{
- return (TAILQ_EMPTY(&queue->head));
+ multiqueue *rv = malloc_or_die(sizeof(multiqueue));
+ QUEUE_INIT(&rv->headtail);
+ rv->parent = parent;
+ rv->put_cb = put_cb;
+ rv->data = data;
+ return rv;
}
-
-int equeue_put(equeue *queue, api_event event)
+void multiqueue_free(multiqueue *this)
{
- if (!queue)
- return (-1);
-
- /* disallow `put` to root queue */
- if (!queue->root)
- return (-1);
-
- queue_entry *entry = MALLOC(queue_entry);
- entry->data.entry.event = event;
- TAILQ_INSERT_TAIL(&queue->head, entry, node);
-
- entry->data.entry.root = MALLOC(queue_entry);
- entry->data.entry.root->data.queue = queue;
- TAILQ_INSERT_TAIL(&queue->root->head, entry->data.entry.root, node);
+ sbassert(this);
+ while (!QUEUE_EMPTY(&this->headtail)) {
+ QUEUE *q = QUEUE_HEAD(&this->headtail);
+ multiqueueitem *item = multiqueue_node_data(q);
+ if (this->parent) {
+ QUEUE_REMOVE(&item->data.item.parent->node);
+ FREE(item->data.item.parent);
+ }
+ QUEUE_REMOVE(q);
+ FREE(item);
+ }
- return (0);
+ FREE(this);
}
-
-api_event equeue_get(equeue *queue)
+event multiqueue_get(multiqueue *this)
{
- api_event event;
- queue_entry *entry;
+ return multiqueue_empty(this) ? NILEVENT : multiqueue_remove(this);
+}
- if (equeue_empty(queue)) {
- event.handler = NULL;
- return (event);
+void multiqueue_put_event(multiqueue *this, event e)
+{
+ sbassert(this);
+ multiqueue_push(this, e);
+ if (this->parent && this->parent->put_cb) {
+ this->parent->put_cb(this->parent, this->parent->data);
}
+}
- if (queue->root) {
- entry = dequeue_child(queue);
- } else {
- entry = dequeue_child_from_root(queue);
+void multiqueue_process_events(multiqueue *this)
+{
+ sbassert(this);
+ while (!multiqueue_empty(this)) {
+ event e = multiqueue_get(this);
+ if (e.handler) {
+ e.handler(e.argv);
+ }
}
-
- event = entry->data.entry.event;
-
- FREE(entry);
-
- return (event);
}
-
-STATIC queue_entry *dequeue_child(equeue *queue)
+bool multiqueue_empty(multiqueue *this)
{
- queue_entry *entry;
-
- entry = TAILQ_FIRST(&queue->head);
- TAILQ_REMOVE(&queue->head, entry, node);
- TAILQ_REMOVE(&queue->root->head, entry->data.entry.root, node);
-
- FREE(entry->data.entry.root);
-
- return (entry);
+ sbassert(this);
+ return QUEUE_EMPTY(&this->headtail);
}
-
-STATIC queue_entry *dequeue_child_from_root(equeue *queue)
+void multiqueue_replace_parent(multiqueue *this, multiqueue *new_parent)
{
- queue_entry *entry, *centry;
- equeue *cqueue;
-
- entry = TAILQ_FIRST(&queue->head);
- TAILQ_REMOVE(&queue->head, entry, node);
- cqueue = entry->data.queue;
- centry = TAILQ_FIRST(&cqueue->head);
- TAILQ_REMOVE(&cqueue->head, centry, node);
-
- FREE(entry);
-
- return (centry);
+ sbassert(multiqueue_empty(this));
+ this->parent = new_parent;
}
-
-void equeue_free(equeue *queue)
+static event multiqueue_remove(multiqueue *this)
{
- queue_entry *entry;
-
- if (queue->root) {
- while (!equeue_empty(queue)) {
- entry = dequeue_child(queue);
- FREE(entry);
- }
+ sbassert(!multiqueue_empty(this));
+ QUEUE *h = QUEUE_HEAD(&this->headtail);
+ QUEUE_REMOVE(h);
+ multiqueueitem *item = multiqueue_node_data(h);
+ event rv;
+
+ if (item->link) {
+ sbassert(!this->parent);
+ // remove the next node in the linked queue
+ multiqueue *linked = item->data.queue;
+ sbassert(!multiqueue_empty(linked));
+ multiqueueitem *child =
+ multiqueue_node_data(QUEUE_HEAD(&linked->headtail));
+ QUEUE_REMOVE(&child->node);
+ rv = child->data.item.event;
+ FREE(child);
} else {
- while (!equeue_empty(queue)) {
- entry = dequeue_child_from_root(queue);
- FREE(entry);
+ if (this->parent) {
+ // remove the corresponding link node in the parent queue
+ QUEUE_REMOVE(&item->data.item.parent->node);
+ FREE(item->data.item.parent);
}
+ rv = item->data.item.event;
}
- FREE(queue);
+ FREE(item);
+ return rv;
}
-
-void equeue_run_events(equeue *queue)
+static void multiqueue_push(multiqueue *this, event e)
{
- api_event event;
-
- while (!equeue_empty(queue)) {
- event = equeue_get(queue);
-
- if (event.handler)
- event.handler(&event.info);
+ multiqueueitem *item = malloc_or_die(sizeof(multiqueueitem));
+ item->link = false;
+ item->data.item.event = e;
+ QUEUE_INSERT_TAIL(&this->headtail, &item->node);
+
+ if (this->parent) {
+ // push link node to the parent queue
+ item->data.item.parent = malloc_or_die(sizeof(multiqueueitem));
+ item->data.item.parent->link = true;
+ item->data.item.parent->data.queue = this;
+ QUEUE_INSERT_TAIL(&this->parent->headtail, &item->data.item.parent->node);
}
}
+
+static multiqueueitem *multiqueue_node_data(QUEUE *q)
+{
+ return QUEUE_DATA(q, multiqueueitem, node);
+}
diff --git a/src/rpc/connection/event.h b/src/rpc/connection/event.h
index 9b09f3a..a09351d 100644
--- a/src/rpc/connection/event.h
+++ b/src/rpc/connection/event.h
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2016 splone UG
+ * Copyright (C) 2015 splone UG
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
@@ -12,11 +12,41 @@
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
+ *
+ * This file incorporates code covered by the following terms:
+ *
+ * Copyright Neovim contributors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
#pragma once
-#include "rpc/sb-rpc.h"
+#include // for bool
+#include "rpc/connection/event-defs.h"
+
+
+typedef struct multiqueue multiqueue;
+typedef void (*put_callback)(multiqueue *multiq, void *data);
+
+multiqueue *multiqueue_new_parent(put_callback put_cb, void *data);
+multiqueue *multiqueue_new_child(multiqueue *parent);
+void multiqueue_free(multiqueue *this);
+event multiqueue_get(multiqueue *this);
+void multiqueue_put_event(multiqueue *this, event e);
+void multiqueue_process_events(multiqueue *this);
+bool multiqueue_empty(multiqueue *this);
+void multiqueue_replace_parent(multiqueue *this, multiqueue *new_parent);
-STATIC queue_entry *dequeue_child(equeue *queue);
-STATIC queue_entry *dequeue_child_from_root(equeue *queue);
+#define multiqueue_put(q, h, ...) \
+ multiqueue_put_event(q, event_create(1, h, __VA_ARGS__));
diff --git a/src/rpc/connection/inputstream.c b/src/rpc/connection/inputstream.c
index 43df7e7..f05c443 100644
--- a/src/rpc/connection/inputstream.c
+++ b/src/rpc/connection/inputstream.c
@@ -42,10 +42,7 @@
inputstream *inputstream_new(inputstream_cb cb, uint32_t buffer_size,
void *data)
{
- inputstream *rs = MALLOC(inputstream);
-
- if (rs == NULL)
- return (NULL);
+ inputstream *rs = malloc_or_die(sizeof(inputstream));
rs->data = data;
rs->size = 0;
@@ -54,7 +51,7 @@ inputstream *inputstream_new(inputstream_cb cb, uint32_t buffer_size,
rs->free_handle = false;
/* initialize circular buffer */
- rs->circbuf_start = MALLOC_ARRAY(buffer_size, unsigned char);
+ rs->circbuf_start = malloc_array_or_die(buffer_size, sizeof(unsigned char));
rs->circbuf_read_pos = rs->circbuf_start;
rs->circbuf_write_pos = rs->circbuf_start;
rs->circbuf_end = rs->circbuf_start + buffer_size;
diff --git a/src/rpc/connection/loop.c b/src/rpc/connection/loop.c
index cd008fa..e5ca1e9 100644
--- a/src/rpc/connection/loop.c
+++ b/src/rpc/connection/loop.c
@@ -30,42 +30,117 @@
* limitations under the License.
*/
-#include
+#include "rpc/connection/loop.h"
+#include // for bool
+#include // for NULL, abort
+#include "rpc/sb-rpc.h" // for event
-#include "rpc/sb-rpc.h"
-#include "sb-common.h"
+static void async_cb(uv_async_t *handle);
+static void timer_cb(uv_timer_t *handle);
-static inline void loop_poll_events_until(struct connection *con,
- bool *condition);
-equeue *equeue_root;
-uv_loop_t loop;
+void loop_init(UNUSED(loop *loop), UNUSED(void *data))
+{
+ uv_loop_init(&loop->uv);
+ loop->recursive = 0;
+ loop->uv.data = loop;
+ loop->children = kl_init(WatcherPtr);
+ loop->children_stop_requests = 0;
+ loop->events = multiqueue_new_parent(loop_on_put, loop);
+ loop->fast_events = multiqueue_new_child(loop->events);
+ loop->thread_events = multiqueue_new_parent(NULL, NULL);
+ uv_mutex_init(&loop->mutex);
+ uv_async_init(&loop->uv, &loop->async, async_cb);
+ uv_signal_init(&loop->uv, &loop->children_watcher);
+ uv_timer_init(&loop->uv, &loop->children_kill_timer);
+ uv_timer_init(&loop->uv, &loop->poll_timer);
+}
-static inline void loop_poll_events_until(struct connection *con,
- bool *condition)
+void loop_poll_events(loop *loop, int ms)
{
- while (!*condition) {
- if (con->queue && !equeue_empty(con->queue)) {
- equeue_run_events(con->queue);
- } else {
- uv_run(&loop, UV_RUN_ONCE);
- equeue_run_events(equeue_root);
- }
+ if (loop->recursive++) {
+ abort();
}
+
+ uv_run_mode mode = UV_RUN_ONCE;
+
+ if (ms > 0) {
+ uv_timer_start(&loop->poll_timer, timer_cb, (uint64_t)ms, (uint64_t)ms);
+ } else if (ms == 0) {
+ mode = UV_RUN_NOWAIT;
+ }
+
+ uv_run(&loop->uv, mode);
+
+ if (ms > 0) {
+ uv_timer_stop(&loop->poll_timer);
+ }
+
+ loop->recursive--; // Can re-enter uv_run now
+ multiqueue_process_events(loop->fast_events);
}
+// Schedule an event from another thread
+void loop_schedule(loop *loop, event e)
+{
+ uv_mutex_lock(&loop->mutex);
+ multiqueue_put_event(loop->thread_events, e);
+ uv_async_send(&loop->async);
+ uv_mutex_unlock(&loop->mutex);
+}
-void loop_wait_for_response(struct connection *con,
- struct callinfo *cinfo)
+void loop_on_put(UNUSED(multiqueue *queue), void *data)
{
+ loop *loop = data;
+ // Sometimes libuv will run pending callbacks(timer for example) before
+ // blocking for a poll. If this happens and the callback pushes a event to one
+ // of the queues, the event would only be processed after the poll
+ // returns(user hits a key for example). To avoid this scenario, we call
+ // uv_stop when a event is enqueued.
+ uv_stop(&loop->uv);
+}
+void loop_close(loop *loop, bool wait)
+{
+ uv_mutex_destroy(&loop->mutex);
+ uv_close((uv_handle_t *)&loop->children_watcher, NULL);
+ uv_close((uv_handle_t *)&loop->children_kill_timer, NULL);
+ uv_close((uv_handle_t *)&loop->poll_timer, NULL);
+ uv_close((uv_handle_t *)&loop->async, NULL);
+ do {
+ uv_run(&loop->uv, wait ? UV_RUN_DEFAULT : UV_RUN_NOWAIT);
+ } while (uv_loop_close(&loop->uv) && wait);
+ multiqueue_free(loop->fast_events);
+ multiqueue_free(loop->thread_events);
+ multiqueue_free(loop->events);
+ kl_destroy(WatcherPtr, loop->children);
+}
+
+static void async_cb(uv_async_t *handle)
+{
+ loop *l = handle->loop->data;
+ uv_mutex_lock(&l->mutex);
+ while (!multiqueue_empty(l->thread_events)) {
+ event ev = multiqueue_get(l->thread_events);
+ multiqueue_put_event(l->fast_events, ev);
+ }
+ uv_mutex_unlock(&l->mutex);
+}
+
+static void timer_cb(UNUSED(uv_timer_t *handle))
+{
+}
+
+void loop_process_events_until(loop *loop, struct connection *con,
+ struct callinfo *cinfo)
+{
/* push callinfo to connection callinfo vector */
- kv_push(struct callinfo *, con->callvector, cinfo);
- con->pendingcalls++;
+ kv_push(con->callvector, cinfo);
+ con->pending_requests++;
/* wait until requestinfo returned, in time process events */
- loop_poll_events_until(con, &cinfo->hasresponse);
+ LOOP_PROCESS_EVENTS_UNTIL(loop, con->events, -1, cinfo->returned);
/* delete last from callinfo vector */
- kv_pop(con->callvector);
- con->pendingcalls--;
+ (void)kv_pop(con->callvector);
+ con->pending_requests--;
}
diff --git a/src/rpc/connection/loop.h b/src/rpc/connection/loop.h
new file mode 100644
index 0000000..997f357
--- /dev/null
+++ b/src/rpc/connection/loop.h
@@ -0,0 +1,110 @@
+/**
+ * Copyright (C) 2015 splone UG
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ * This file incorporates code covered by the following terms:
+ *
+ * Copyright Neovim contributors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ #pragma once
+
+#include // for bool
+#include // for size_t
+#include // for uint64_t
+#include // for uv_mutex_t
+#include // for uv_hrtime, uv_timer_t, uv_asy...
+#include "klist.h" // for mp, kl, k, p, KLIST_INIT, kli...
+#include "kvec.h" // for connection::(anonymous), kv_pop
+#include "rpc/connection/connection.h" // for connection
+#include "rpc/connection/event.h" // for multiqueue, multiqueue_empty
+#include "rpc/connection/event-defs.h"
+#include "rpc/sb-rpc.h" // for callinfo, event
+
+
+typedef void * WatcherPtr;
+
+#define _noop(x)
+KLIST_INIT(WatcherPtr, WatcherPtr, _noop)
+
+typedef struct loop {
+ uv_loop_t uv;
+ multiqueue *events, *fast_events, *thread_events;
+ klist_t(WatcherPtr) *children;
+ uv_signal_t children_watcher;
+ uv_timer_t children_kill_timer, poll_timer;
+ size_t children_stop_requests;
+ uv_async_t async;
+ uv_mutex_t mutex;
+ int recursive;
+} loop;
+
+#define CREATE_EVENT(multiqueue, handler, argc, ...) \
+ do { \
+ if (multiqueue) { \
+ multiqueue_put((multiqueue), (handler), argc, __VA_ARGS__); \
+ } else { \
+ void *argv[argc] = { __VA_ARGS__ }; \
+ (handler)(argv); \
+ } \
+ } while (0)
+
+// Poll for events until a condition or timeout
+#define LOOP_PROCESS_EVENTS_UNTIL(loop, multiqueue, timeout, condition) \
+ do { \
+ int remaining = timeout; \
+ uint64_t before = (remaining > 0) ? uv_hrtime() : 0; \
+ while (!(condition)) { \
+ LOOP_PROCESS_EVENTS(loop, multiqueue, remaining); \
+ if (remaining == 0) { \
+ break; \
+ } else if (remaining > 0) { \
+ uint64_t now = uv_hrtime(); \
+ remaining -= (int) ((now - before) / 1000000); \
+ before = now; \
+ if (remaining <= 0) { \
+ break; \
+ } \
+ } \
+ } \
+ } while (0)
+
+#define LOOP_PROCESS_EVENTS(loop, multiqueue, timeout) \
+ do { \
+ if (multiqueue && !multiqueue_empty(multiqueue)) { \
+ multiqueue_process_events(multiqueue); \
+ } else { \
+ loop_poll_events(loop, timeout); \
+ } \
+ } while (0)
+
+void loop_init(loop *loop, void *data);
+void loop_poll_events(loop *loop, int ms);
+void loop_schedule(loop *loop, event e);
+void loop_on_put(multiqueue *queue, void *data);
+void loop_close(loop *loop, bool wait);
+void loop_process_events_until(loop *loop, struct connection *con,
+ struct callinfo *cinfo);
diff --git a/src/rpc/connection/outputstream.c b/src/rpc/connection/outputstream.c
index a4a0b2e..03078aa 100644
--- a/src/rpc/connection/outputstream.c
+++ b/src/rpc/connection/outputstream.c
@@ -40,10 +40,7 @@
outputstream *outputstream_new(uint32_t maxmem)
{
- outputstream *ws = MALLOC(outputstream);
-
- if (ws == NULL)
- return (NULL);
+ outputstream *ws = malloc_or_die(sizeof(outputstream));
ws->maxmem = maxmem;
ws->stream = NULL;
@@ -78,18 +75,12 @@ int outputstream_write(outputstream *ostream, char *buffer, size_t len)
buf.base = buffer;
buf.len = len;
- data = MALLOC(struct write_request_data);
-
- if (data == NULL)
- return (-1);
+ data = malloc_or_die(sizeof(struct write_request_data));
data->ostream = ostream;
data->buffer = buffer;
data->len = len;
- req = MALLOC(uv_write_t);
-
- if (req == NULL)
- return (-1);
+ req = malloc_or_die(sizeof(uv_write_t));
req->data = data;
ostream->curmem += len;
diff --git a/src/rpc/connection/server.c b/src/rpc/connection/server.c
index 8d53cf8..f44956f 100644
--- a/src/rpc/connection/server.c
+++ b/src/rpc/connection/server.c
@@ -14,18 +14,21 @@
* along with this program. If not, see .
*/
-#include
-#include
+#include "rpc/connection/server.h"
#ifdef __linux__
-#include
+#include // for strlcpy
#endif
-#include
-
-#include "sb-common.h"
-#include "rpc/sb-rpc.h"
-#include "rpc/db/sb-db.h"
-#include "rpc/connection/server.h"
-
+#include // for getnameinfo, NI_MAXHOST, NI_MAXSERV
+#include // for sockaddr_in
+#include // for NULL, size_t
+#include // for uint16_t
+#include // for sockaddr
+#include "khash.h" // for __i, khint32_t
+#include "main.h" // for main_loop
+#include "rpc/connection/loop.h" // for loop
+#include "rpc/db/sb-db.h" // for db_authorized_set_whitelist_all
+#include "rpc/sb-rpc.h" // for hashmap_cstr_t_ptr_t, hashmap_cstr_...
+#include "sb-common.h" // for fmt_addr, ::SERVER_TYPE_TCP, LOG_ERROR
#define ADDRESS_MAX_SIZE 256
#define MAX_CONNECTIONS 16
@@ -48,8 +51,6 @@ struct server {
static hashmap(cstr_t, ptr_t) *servers = NULL;
-uv_loop_t loop;
-
int server_init(void)
{
servers = hashmap_new(cstr_t, ptr_t)();
@@ -71,22 +72,19 @@ int server_start_tcp(boxaddr *addr, uint16_t port)
sbassert(addr);
- server = MALLOC(struct server);
+ server = malloc_or_die(sizeof(struct server));
if (hashmap_has(cstr_t, ptr_t)(servers, fmt_addr(addr))) {
LOG("Already listening on %s", fmt_addr(addr));
return (-1);
}
- if (server == NULL)
- return (-1);
-
uv_stream_t *stream = NULL;
box_addr_to_sockaddr(addr, port, &server->socket.tcp.addr,
sizeof(struct sockaddr_in));
- uv_tcp_init(&loop, &server->socket.tcp.handle);
+ uv_tcp_init(&main_loop.uv, &server->socket.tcp.handle);
result = uv_tcp_bind(&server->socket.tcp.handle,
(const struct sockaddr *)&server->socket.tcp.addr, 0);
@@ -120,16 +118,13 @@ int server_start_pipe(char *name)
sbassert(name);
- server = MALLOC(struct server);
+ server = malloc_or_die(sizeof(struct server));
if (hashmap_has(cstr_t, ptr_t)(servers, name)) {
LOG("Already listening on %s", name);
return (-1);
}
- if (server == NULL)
- return (-1);
-
uv_stream_t *stream = NULL;
if (strlcpy(server->socket.pipe.addr, name, sizeof(server->socket.pipe.addr))
@@ -138,7 +133,7 @@ int server_start_pipe(char *name)
return (-1);
}
- uv_pipe_init(&loop, &server->socket.pipe.handle, 0);
+ uv_pipe_init(&main_loop.uv, &server->socket.pipe.handle, 0);
result = uv_pipe_bind(&server->socket.pipe.handle, server->socket.pipe.addr);
if (result) {
@@ -214,15 +209,12 @@ STATIC void connection_cb(uv_stream_t *server_stream, int status)
hbuflen = sizeof(hbuf);
server = server_stream->data;
- client = MALLOC(uv_stream_t);
-
- if (client == NULL)
- return;
+ client = malloc_or_die(sizeof(uv_stream_t));
if (server->type == SERVER_TYPE_TCP)
- uv_tcp_init(&loop, (uv_tcp_t *)client);
+ uv_tcp_init(&main_loop.uv, (uv_tcp_t *)client);
else
- uv_pipe_init(&loop, (uv_pipe_t *)client, 0);
+ uv_pipe_init(&main_loop.uv, (uv_pipe_t *)client, 0);
result = uv_accept(server_stream, client);
diff --git a/src/rpc/connection/server.h b/src/rpc/connection/server.h
index e79eaa6..77c8590 100644
--- a/src/rpc/connection/server.h
+++ b/src/rpc/connection/server.h
@@ -16,7 +16,8 @@
#pragma once
-#include "rpc/sb-rpc.h"
+#include // for uv_handle_t, uv_stream_t
+#include "sb-common.h" // for STATIC
STATIC void connection_cb(uv_stream_t *server_stream, int status);
STATIC void client_free_cb(uv_handle_t *handle);
diff --git a/src/rpc/connection/streamhandle.c b/src/rpc/connection/streamhandle.c
index b087adb..bb3478a 100644
--- a/src/rpc/connection/streamhandle.c
+++ b/src/rpc/connection/streamhandle.c
@@ -74,10 +74,7 @@ STATIC struct streamhandle *init_streamhandle(uv_handle_t *handle)
struct streamhandle *hd;
if (handle->data == NULL) {
- hd = MALLOC(struct streamhandle);
-
- if (hd == NULL)
- return (NULL);
+ hd = malloc_or_die(sizeof(struct streamhandle));
hd->istream = NULL;
hd->ostream = NULL;
diff --git a/src/rpc/db/function.c b/src/rpc/db/function.c
index d5e5714..264ccfd 100644
--- a/src/rpc/db/function.c
+++ b/src/rpc/db/function.c
@@ -52,7 +52,7 @@ static int db_function_add_meta(char *pluginkey, string name, string desc)
{
redisReply *reply;
- if (!rc)
+ if (!rc || !name.str || !desc.str)
return (-1);
reply = redisCommand(rc, "HSET %s:func:%s:meta desc %s", pluginkey,
@@ -71,7 +71,7 @@ static int db_function_add_meta(char *pluginkey, string name, string desc)
static int db_function_add_args(char *pluginkey, string name,
- message_object_type type)
+ object_type type)
{
redisReply *reply;
@@ -103,6 +103,9 @@ static int db_function_flush_args(char *pluginkey, string name)
/* if start > end, the result will be an empty list (which causes
* key to be removed) */
+ printf("name: %s\n", name.str);
+ printf("pluginkey: %s\n", pluginkey);
+
reply = redisCommand(rc, "LTRIM %s:func:%s:args %i %i", pluginkey,
name.str, start, end);
@@ -122,13 +125,13 @@ static int db_function_flush_args(char *pluginkey, string name)
int db_function_add(char *pluginkey, array *func)
{
- struct message_object *name_elem, *desc_elem, *args, *arg;
+ object *name_elem, *desc_elem, *args, *arg;
string name, desc;
if (!func || !rc || (func->size <= 2))
return (-1);
- name_elem = &func->obj[0];
+ name_elem = &func->items[0];
if (!name_elem) {
LOG_WARNING("Illegal function name pointer.");
@@ -144,7 +147,7 @@ int db_function_add(char *pluginkey, array *func)
return (-1);
}
- desc_elem = &func->obj[1];
+ desc_elem = &func->items[1];
if (!desc_elem) {
LOG_WARNING("Illegal function description pointer.");
@@ -164,12 +167,12 @@ int db_function_add(char *pluginkey, array *func)
if (db_function_add_meta(pluginkey, name, desc) == -1)
return (-1);
- args = &func->obj[2];
+ args = &func->items[2];
db_function_flush_args(pluginkey, name);
- for (size_t i = 0; i < args->data.params.size; i++) {
- arg = &args->data.params.obj[i];
+ for (size_t i = 0; i < args->data.array.size; i++) {
+ arg = &args->data.array.items[i];
if (db_function_add_args(pluginkey, name, arg->type) == -1) {
LOG_WARNING("Failed to add function arguments!");
@@ -284,15 +287,15 @@ static int db_function_typecheck(char *pluginkey, string name,
return (-1);
}
- if(val == OBJECT_TYPE_INT && args->obj[k].type == OBJECT_TYPE_UINT){
+ if(val == OBJECT_TYPE_INT && args->items[k].type == OBJECT_TYPE_UINT){
/* Any positive integer will be treated as an unsigned int
* (see unpack/pack.c) and might be a valid signed integer */
- if(args->obj[k].data.uinteger > INT64_MAX){
+ if(args->items[k].data.uinteger > INT64_MAX){
LOG_WARNING("run() function argument has wrong type.");
freeReplyObject(reply);
return (-1);
}
- } else if (val != args->obj[k].type){
+ } else if (val != args->items[k].type){
LOG_WARNING("run() function argument has wrong type.");
freeReplyObject(reply);
return (-1);
diff --git a/src/rpc/msgpack/helpers.c b/src/rpc/msgpack/helpers.c
new file mode 100644
index 0000000..99c775c
--- /dev/null
+++ b/src/rpc/msgpack/helpers.c
@@ -0,0 +1,496 @@
+#include "rpc/sb-rpc.h"
+#include "api/helpers.h"
+#include "rpc/msgpack/helpers.h"
+#include "sb-common.h"
+
+STATIC bool msgpack_rpc_to_string(const msgpack_object *const obj,
+ string *const arg);
+STATIC bool msgpack_rpc_is_notification(msgpack_object *req);
+STATIC msgpack_object *msgpack_rpc_msg_id(msgpack_object *req);
+
+typedef struct {
+ const msgpack_object *mobj;
+ object *aobj;
+ bool container;
+ size_t idx;
+} msgpack_to_api_object_stack_item;
+
+bool msgpack_rpc_to_object(const msgpack_object *const obj, object *const arg)
+{
+ bool ret = true;
+ kvec_t(msgpack_to_api_object_stack_item) stack = KV_INITIAL_VALUE;
+ kv_push(stack, ((msgpack_to_api_object_stack_item) { obj, arg, false, 0 }));
+
+ while (ret && kv_size(stack)) {
+ msgpack_to_api_object_stack_item cur = kv_last(stack);
+
+ if (!cur.container) {
+ *cur.aobj = NIL;
+ }
+
+ switch (cur.mobj->type) {
+ case MSGPACK_OBJECT_NIL: {
+ break;
+ }
+ case MSGPACK_OBJECT_BOOLEAN: {
+ *cur.aobj = BOOLEAN_OBJ(cur.mobj->via.boolean);
+ break;
+ }
+ case MSGPACK_OBJECT_NEGATIVE_INTEGER: {
+ sbassert(sizeof(int64_t) == sizeof(cur.mobj->via.i64));
+ *cur.aobj = INTEGER_OBJ(cur.mobj->via.i64);
+ break;
+ }
+ case MSGPACK_OBJECT_POSITIVE_INTEGER: {
+ sbassert(sizeof(uint64_t) == sizeof(cur.mobj->via.u64));
+ *cur.aobj = UINTEGER_OBJ(cur.mobj->via.u64);
+ break;
+ }
+ case MSGPACK_OBJECT_FLOAT: {
+ sbassert(sizeof(double) == sizeof(cur.mobj->via.f64));
+ *cur.aobj = FLOATING_OBJ(cur.mobj->via.f64);
+ break;
+ }
+ case MSGPACK_OBJECT_STR: {
+ *cur.aobj = STRING_OBJ(((string) {
+ .length = cur.mobj->via.str.size,
+ .str = (cur.mobj->via.str.ptr == NULL || cur.mobj->via.str.size == 0
+ ? NULL : box_strndup(cur.mobj->via.str.ptr,
+ cur.mobj->via.str.size)),
+ }));
+ break;
+ }
+ case MSGPACK_OBJECT_BIN: {
+ *cur.aobj = STRING_OBJ(((string) {
+ .length = cur.mobj->via.bin.size,
+ .str = (cur.mobj->via.bin.ptr == NULL || cur.mobj->via.bin.size == 0
+ ? NULL : box_strndup(cur.mobj->via.bin.ptr, cur.mobj->via.bin.size)),
+ }));
+ break;
+ }
+
+ case MSGPACK_OBJECT_ARRAY: {
+ const size_t size = cur.mobj->via.array.size;
+
+ if (cur.container) {
+ if (cur.idx >= size) {
+ (void)kv_pop(stack);
+ } else {
+ const size_t idx = cur.idx;
+ cur.idx++;
+ kv_last(stack) = cur;
+ kv_push(stack, ((msgpack_to_api_object_stack_item) {
+ .mobj = &cur.mobj->via.array.ptr[idx],
+ .aobj = &cur.aobj->data.array.items[idx],
+ .container = false,
+ }));
+ }
+ } else {
+ *cur.aobj = ARRAY_OBJ(((array) {
+ .size = size,
+ .capacity = size,
+ .items = (size > 0
+ ? calloc(size, sizeof(*cur.aobj->data.array.items)) : NULL),
+ }));
+ cur.container = true;
+ kv_last(stack) = cur;
+ }
+ break;
+ }
+ case MSGPACK_OBJECT_MAP: {
+ const size_t size = cur.mobj->via.map.size;
+
+ if (cur.container) {
+ if (cur.idx >= size) {
+ (void)kv_pop(stack);
+ } else {
+ const size_t idx = cur.idx;
+ cur.idx++;
+ kv_last(stack) = cur;
+ const msgpack_object *const key = &cur.mobj->via.map.ptr[idx].key;
+ switch (key->type) {
+ case MSGPACK_OBJECT_STR: {
+ cur.aobj->data.dictionary.items[idx].key = ((string) {
+ .length = key->via.str.size,
+ .str = (key->via.str.ptr == NULL || key->via.str.size == 0
+ ? NULL : box_strndup(key->via.str.ptr, key->via.str.size)),
+ });
+ break;
+ }
+ case MSGPACK_OBJECT_BIN: {
+ cur.aobj->data.dictionary.items[idx].key = ((string) {
+ .length = key->via.bin.size,
+ .str = (key->via.bin.ptr == NULL || key->via.bin.size == 0
+ ? NULL : box_strndup(key->via.bin.ptr, key->via.bin.size)),
+ });
+ break;
+ }
+ case MSGPACK_OBJECT_NIL:
+ case MSGPACK_OBJECT_BOOLEAN:
+ case MSGPACK_OBJECT_POSITIVE_INTEGER:
+ case MSGPACK_OBJECT_NEGATIVE_INTEGER:
+ case MSGPACK_OBJECT_FLOAT:
+ case MSGPACK_OBJECT_EXT:
+ case MSGPACK_OBJECT_MAP:
+ case MSGPACK_OBJECT_ARRAY: {
+ ret = false;
+ break;
+ }
+ }
+ if (ret) {
+ kv_push(stack, ((msgpack_to_api_object_stack_item) {
+ .mobj = &cur.mobj->via.map.ptr[idx].val,
+ .aobj = &cur.aobj->data.dictionary.items[idx].value,
+ .container = false,
+ }));
+ }
+ }
+ } else {
+ *cur.aobj = DICTIONARY_OBJ(((dictionary) {
+ .size = size,
+ .capacity = size,
+ .items = (size > 0
+ ? calloc(size, sizeof(*cur.aobj->data.dictionary.items))
+ : NULL),
+ }));
+ cur.container = true;
+ kv_last(stack) = cur;
+ }
+ break;
+ }
+ case MSGPACK_OBJECT_EXT: {
+ break;
+ }
+ }
+ if (!cur.container) {
+ (void)kv_pop(stack);
+ }
+ }
+
+ kv_destroy(stack);
+
+ return ret;
+}
+
+STATIC bool msgpack_rpc_to_string(const msgpack_object *const obj,
+ string *const arg)
+{
+ if (obj->type == MSGPACK_OBJECT_BIN || obj->type == MSGPACK_OBJECT_STR) {
+ arg->str = obj->via.bin.ptr != NULL
+ ? box_strndup(obj->via.bin.ptr, obj->via.bin.size)
+ : NULL;
+ arg->length = obj->via.bin.size;
+ return true;
+ }
+ return false;
+}
+
+bool msgpack_rpc_to_array(const msgpack_object *const obj, array *const arg)
+{
+ if (obj->type != MSGPACK_OBJECT_ARRAY) {
+ return false;
+ }
+
+ arg->size = obj->via.array.size;
+ arg->items = calloc_or_die(obj->via.array.size, sizeof(object));
+
+ for (uint32_t i = 0; i < obj->via.array.size; i++) {
+ if (!msgpack_rpc_to_object(obj->via.array.ptr + i, &arg->items[i])) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool msgpack_rpc_to_dictionary(const msgpack_object *const obj,
+ dictionary *const arg)
+{
+ if (obj->type != MSGPACK_OBJECT_MAP) {
+ return false;
+ }
+
+ arg->size = obj->via.array.size;
+ arg->items = calloc_or_die(obj->via.map.size, sizeof(key_value_pair));
+
+
+ for (uint32_t i = 0; i < obj->via.map.size; i++) {
+ if (!msgpack_rpc_to_string(&obj->via.map.ptr[i].key,
+ &arg->items[i].key)) {
+ return false;
+ }
+
+ if (!msgpack_rpc_to_object(&obj->via.map.ptr[i].val,
+ &arg->items[i].value)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void msgpack_rpc_from_boolean(bool result, msgpack_packer *res)
+{
+ if (result) {
+ msgpack_pack_true(res);
+ } else {
+ msgpack_pack_false(res);
+ }
+}
+
+void msgpack_rpc_from_integer(int64_t result, msgpack_packer *res)
+{
+ msgpack_pack_int64(res, result);
+}
+
+void msgpack_rpc_from_uinteger(uint64_t result, msgpack_packer *res)
+{
+ msgpack_pack_uint64(res, result);
+}
+
+void msgpack_rpc_from_float(double result, msgpack_packer *res)
+{
+ msgpack_pack_double(res, result);
+}
+
+void msgpack_rpc_from_string(string result, msgpack_packer *res)
+{
+ msgpack_pack_str(res, result.length);
+ msgpack_pack_str_body(res, result.str, result.length);
+}
+
+typedef struct {
+ const object *obj;
+ bool container;
+ size_t idx;
+} api_to_msgpack_object_stack_item;
+
+void msgpack_rpc_from_object(const object result, msgpack_packer *const res)
+{
+ kvec_t(api_to_msgpack_object_stack_item) stack = KV_INITIAL_VALUE;
+ kv_push(stack, ((api_to_msgpack_object_stack_item) {&result, false, 0}));
+
+ while (kv_size(stack)) {
+ api_to_msgpack_object_stack_item cur = kv_last(stack);
+
+ switch (cur.obj->type) {
+ case OBJECT_TYPE_NIL: {
+ msgpack_pack_nil(res);
+ break;
+ }
+ case OBJECT_TYPE_BOOL: {
+ msgpack_rpc_from_boolean(cur.obj->data.boolean, res);
+ break;
+ }
+ case OBJECT_TYPE_INT: {
+ msgpack_rpc_from_integer(cur.obj->data.integer, res);
+ break;
+ }
+ case OBJECT_TYPE_UINT: {
+ msgpack_rpc_from_uinteger(cur.obj->data.uinteger, res);
+ break;
+ }
+ case OBJECT_TYPE_FLOAT: {
+ msgpack_rpc_from_float(cur.obj->data.floating, res);
+ break;
+ }
+ case OBJECT_TYPE_STR: {
+ msgpack_rpc_from_string(cur.obj->data.string, res);
+ break;
+ }
+ case OBJECT_TYPE_ARRAY: {
+ const size_t size = cur.obj->data.array.size;
+
+ if (cur.container) {
+ if (cur.idx >= size) {
+ (void)kv_pop(stack);
+ } else {
+ const size_t idx = cur.idx;
+ cur.idx++;
+ kv_last(stack) = cur;
+ kv_push(stack, ((api_to_msgpack_object_stack_item) {
+ .obj = &cur.obj->data.array.items[idx],
+ .container = false,
+ }));
+ }
+ } else {
+ msgpack_pack_array(res, size);
+ cur.container = true;
+ kv_last(stack) = cur;
+ }
+ break;
+ }
+ case OBJECT_TYPE_DICTIONARY: {
+ const size_t size = cur.obj->data.dictionary.size;
+ if (cur.container) {
+ if (cur.idx >= size) {
+ (void)kv_pop(stack);
+ } else {
+ const size_t idx = cur.idx;
+ cur.idx++;
+ kv_last(stack) = cur;
+ msgpack_rpc_from_string(cur.obj->data.dictionary.items[idx].key,
+ res);
+ kv_push(stack, ((api_to_msgpack_object_stack_item) {
+ .obj = &cur.obj->data.dictionary.items[idx].value,
+ .container = false,
+ }));
+ }
+ } else {
+ msgpack_pack_map(res, size);
+ cur.container = true;
+ kv_last(stack) = cur;
+ }
+ break;
+ }
+ }
+ if (!cur.container) {
+ (void)kv_pop(stack);
+ }
+ }
+ kv_destroy(stack);
+}
+
+void msgpack_rpc_from_array(array result, msgpack_packer *res)
+{
+ msgpack_pack_array(res, result.size);
+
+ for (size_t i = 0; i < result.size; i++) {
+ msgpack_rpc_from_object(result.items[i], res);
+ }
+}
+
+void msgpack_rpc_from_dictionary(dictionary result, msgpack_packer *res)
+{
+ msgpack_pack_map(res, result.size);
+
+ for (size_t i = 0; i < result.size; i++) {
+ msgpack_rpc_from_string(result.items[i].key, res);
+ msgpack_rpc_from_object(result.items[i].value, res);
+ }
+}
+
+void msgpack_rpc_serialize_request(uint64_t request_id,
+ string method,
+ array args,
+ msgpack_packer *pac)
+{
+ msgpack_pack_array(pac, request_id ? 4 : 3);
+ msgpack_pack_int(pac, request_id ? 0 : 2);
+
+ if (request_id) {
+ msgpack_pack_uint64(pac, request_id);
+ }
+
+ msgpack_rpc_from_string(method, pac);
+ msgpack_rpc_from_array(args, pac);
+}
+
+void msgpack_rpc_serialize_response(uint64_t response_id,
+ struct api_error *err,
+ object arg,
+ msgpack_packer *pac)
+{
+ msgpack_pack_array(pac, 4);
+ msgpack_pack_int(pac, 1);
+ msgpack_pack_uint64(pac, response_id);
+
+ if (err->isset) {
+ // error represented by a [type, message] array
+ msgpack_pack_array(pac, 2);
+ msgpack_rpc_from_integer(err->type, pac);
+ msgpack_rpc_from_string(cstring_to_string(err->msg), pac);
+ // Nil result
+ msgpack_pack_nil(pac);
+ } else {
+ // Nil error
+ msgpack_pack_nil(pac);
+ // Return value
+ msgpack_rpc_from_object(arg, pac);
+ }
+}
+
+STATIC bool msgpack_rpc_is_notification(msgpack_object *req)
+{
+ return req->via.array.ptr[0].via.u64 == 2;
+}
+
+msgpack_object *msgpack_rpc_method(msgpack_object *req)
+{
+ msgpack_object *obj = req->via.array.ptr
+ + (msgpack_rpc_is_notification(req) ? 1 : 2);
+ return obj->type == MSGPACK_OBJECT_STR || obj->type == MSGPACK_OBJECT_BIN ?
+ obj : NULL;
+}
+
+msgpack_object *msgpack_rpc_args(msgpack_object *req)
+{
+ msgpack_object *obj = req->via.array.ptr
+ + (msgpack_rpc_is_notification(req) ? 2 : 3);
+ return obj->type == MSGPACK_OBJECT_ARRAY ? obj : NULL;
+}
+
+STATIC msgpack_object *msgpack_rpc_msg_id(msgpack_object *req)
+{
+ if (msgpack_rpc_is_notification(req)) {
+ return NULL;
+ }
+ msgpack_object *obj = &req->via.array.ptr[1];
+ return obj->type == MSGPACK_OBJECT_POSITIVE_INTEGER ? obj : NULL;
+}
+
+void msgpack_rpc_validate(uint64_t *response_id,
+ msgpack_object *req,
+ struct api_error *err)
+{
+ // response id not known yet
+
+ *response_id = UINT64_MAX;
+ // Validate the basic structure of the msgpack-rpc payload
+ if (req->type != MSGPACK_OBJECT_ARRAY) {
+ error_set(err, API_ERROR_TYPE_VALIDATION, "Message is not an array");
+ return;
+ }
+
+ if (req->via.array.size == 0) {
+ error_set(err, API_ERROR_TYPE_VALIDATION, "Message is empty");
+ return;
+ }
+
+ if (req->via.array.ptr[0].type != MSGPACK_OBJECT_POSITIVE_INTEGER) {
+ error_set(err, API_ERROR_TYPE_VALIDATION, "Message type must be an integer");
+ return;
+ }
+
+ uint64_t type = req->via.array.ptr[0].via.u64;
+ if (type != MESSAGE_TYPE_REQUEST && type != MESSAGE_TYPE_NOTIFICATION) {
+ error_set(err, API_ERROR_TYPE_VALIDATION, "Unknown message type");
+ return;
+ }
+
+ if ((type == MESSAGE_TYPE_REQUEST && req->via.array.size != 4)
+ || (type == MESSAGE_TYPE_NOTIFICATION && req->via.array.size != 3)) {
+ error_set(err, API_ERROR_TYPE_VALIDATION,
+ "Request array size should be 4 (request) or 3 (notification)");
+ return;
+ }
+
+ if (type == MESSAGE_TYPE_REQUEST) {
+ msgpack_object *id_obj = msgpack_rpc_msg_id(req);
+ if (!id_obj) {
+ error_set(err, API_ERROR_TYPE_VALIDATION, "ID must be a positive integer");
+ return;
+ }
+ *response_id = id_obj->via.u64;
+ }
+
+ if (!msgpack_rpc_method(req)) {
+ error_set(err, API_ERROR_TYPE_VALIDATION, "Method must be a string");
+ return;
+ }
+
+ if (!msgpack_rpc_args(req)) {
+ error_set(err, API_ERROR_TYPE_VALIDATION, "Parameters must be an array");
+ return;
+ }
+}
diff --git a/src/rpc/msgpack/helpers.h b/src/rpc/msgpack/helpers.h
new file mode 100644
index 0000000..7cb7d7d
--- /dev/null
+++ b/src/rpc/msgpack/helpers.h
@@ -0,0 +1,45 @@
+#pragma once
+
+#include "rpc/sb-rpc.h"
+
+bool msgpack_rpc_to_object(const msgpack_object *const obj, object *const arg);
+
+//STATIC bool msgpack_rpc_to_string(const msgpack_object *const obj,
+// string *const arg);
+
+bool msgpack_rpc_to_array(const msgpack_object *const obj, array *const arg);
+
+bool msgpack_rpc_to_dictionary(const msgpack_object *const obj,
+ dictionary *const arg);
+
+void msgpack_rpc_from_boolean(bool result, msgpack_packer *res);
+
+void msgpack_rpc_from_integer(int64_t result, msgpack_packer *res);
+void msgpack_rpc_from_uinteger(uint64_t result, msgpack_packer *res);
+
+void msgpack_rpc_from_float(double result, msgpack_packer *res);
+
+void msgpack_rpc_from_string(string result, msgpack_packer *res);
+
+void msgpack_rpc_from_object(const object result, msgpack_packer *const res);
+
+void msgpack_rpc_from_array(array result, msgpack_packer *res);
+
+void msgpack_rpc_from_dictionary(dictionary result, msgpack_packer *res);
+
+void msgpack_rpc_serialize_request(uint64_t request_id, string method,
+ array args, msgpack_packer *pac);
+
+void msgpack_rpc_serialize_response(uint64_t response_id, struct api_error *err,
+ object arg, msgpack_packer *pac);
+
+//STATIC bool msgpack_rpc_is_notification(msgpack_object *req);
+
+msgpack_object *msgpack_rpc_method(msgpack_object *req);
+
+msgpack_object *msgpack_rpc_args(msgpack_object *req);
+
+//STATIC msgpack_object *msgpack_rpc_msg_id(msgpack_object *req);
+
+void msgpack_rpc_validate(uint64_t *response_id, msgpack_object *req,
+ struct api_error *err);
diff --git a/src/rpc/msgpack/message.c b/src/rpc/msgpack/message.c
deleted file mode 100644
index cfadb58..0000000
--- a/src/rpc/msgpack/message.c
+++ /dev/null
@@ -1,469 +0,0 @@
-/**
- * Copyright (C) 2015 splone UG
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-#include
-#include
-
-#include "rpc/msgpack/sb-msgpack-rpc.h"
-#include "sb-common.h"
-
-bool message_is_request(msgpack_object *obj)
-{
- if (!obj)
- return (false);
-
- /* check if type is mspack_object_params */
- if (obj->type != MSGPACK_OBJECT_ARRAY)
- return (false);
-
- msgpack_object_array *params = &obj->via.array;
-
- return (params->size == MESSAGE_REQUEST_ARRAY_SIZE &&
- params->ptr[0].type == MSGPACK_OBJECT_POSITIVE_INTEGER &&
- params->ptr[0].via.u64 == MESSAGE_TYPE_REQUEST);
-}
-
-
-bool message_is_response(msgpack_object *obj)
-{
- if (!obj)
- return (false);
-
- /* check if type is mspack_object_array */
- if (obj->type != MSGPACK_OBJECT_ARRAY)
- return (false);
-
- msgpack_object_array *array = &obj->via.array;
-
- /* check if message is a response */
- return (array->size == MESSAGE_RESPONSE_ARRAY_SIZE &&
- array->ptr[0].type == MSGPACK_OBJECT_POSITIVE_INTEGER &&
- array->ptr[0].via.u64 == MESSAGE_TYPE_RESPONSE);
-}
-
-
-int message_serialize_error_response(msgpack_packer *pk,
- struct api_error *api_error, uint32_t msgid)
-{
- struct message_object *err_data;
- array err_array;
-
- if (!pk || !api_error || !api_error->isset)
- return (-1);
-
- msgpack_pack_array(pk, 4);
-
- pack_uint8(pk, MESSAGE_TYPE_RESPONSE);
- pack_uint32(pk, msgid);
-
- err_array.size = 2;
- err_array.obj = CALLOC(2, struct message_object);
-
- if (!err_array.obj) {
- LOG_WARNING("Couldn't allocate memory for err_array object");
- return (-1);
- }
-
- err_data = &err_array.obj[0];
- err_data->type = OBJECT_TYPE_INT;
- err_data->data.integer = api_error->type;
-
- err_data = &err_array.obj[1];
- err_data->type = OBJECT_TYPE_STR;
- err_data->data.string = cstring_to_string(api_error->msg);
-
- if (!err_data->data.string.str) {
- LOG_WARNING("Couldn't allocate memory for string in err_array object");
- return (-1);
- }
-
- if (pack_params(pk, err_array) == -1)
- return (-1);
-
- pack_nil(pk);
-
- FREE(err_array.obj);
-
- return (0);
-}
-
-
-int message_serialize_response(struct message_response *res,
- msgpack_packer *pk)
-{
- if (!pk || !res)
- return (-1);
-
- msgpack_pack_array(pk, 4);
- pack_uint8(pk, MESSAGE_TYPE_RESPONSE);
- pack_uint32(pk, res->msgid);
- pack_nil(pk);
-
- if (pack_params(pk, res->params) == -1)
- return (-1);
-
- return (0);
-}
-
-
-int message_serialize_request(struct message_request *req,
- msgpack_packer *pk)
-{
- if (!pk || !req)
- return (-1);
-
- msgpack_pack_array(pk, 4);
- pack_uint8(pk, MESSAGE_TYPE_REQUEST);
- pack_uint32(pk, req->msgid);
-
- if (req->method.str == NULL || (pack_string(pk, req->method) == -1))
- return (-1);
-
- if (pack_params(pk, req->params) == -1)
- return (-1);
-
- return (0);
-}
-
-
-int message_deserialize_request(struct message_request *req,
- msgpack_object *obj, struct api_error *api_error)
-{
- msgpack_object *type, *msgid, *method, *params;
- uint64_t tmp_type;
- uint64_t tmp_msgid;
-
- if (!api_error)
- return (-1);
-
- if (!req || !obj) {
- error_set(api_error, API_ERROR_TYPE_VALIDATION, "Error");
- return (-1);
- }
-
- /* type */
- if (obj->via.array.ptr[0].type != MSGPACK_OBJECT_POSITIVE_INTEGER) {
- error_set(api_error, API_ERROR_TYPE_VALIDATION, "type field has wrong type");
- return (-1);
- }
-
- type = &obj->via.array.ptr[0];
- if (!type) {
- error_set(api_error, API_ERROR_TYPE_VALIDATION, "unpack type failed");
- return (-1);
- }
-
- tmp_type = unpack_uint(type);
-
- if (tmp_type != MESSAGE_TYPE_REQUEST) {
- error_set(api_error, API_ERROR_TYPE_VALIDATION, "type must be 0 or 1");
- return (-1);
- }
-
- /* message id */
- msgid = &obj->via.array.ptr[1];
-
- if (!msgid || msgid->type != MSGPACK_OBJECT_POSITIVE_INTEGER) {
- error_set(api_error, API_ERROR_TYPE_VALIDATION, "illegal msgid");
- return (-1);
- }
-
- tmp_msgid = unpack_uint(msgid);
-
- if (tmp_msgid >= UINT32_MAX) {
- error_set(api_error, API_ERROR_TYPE_VALIDATION, "invalid msgid");
- return (-1);
- }
-
- req->msgid = (uint32_t)tmp_msgid;
-
- /* method */
- if (obj->via.array.ptr[2].type != MSGPACK_OBJECT_STR) {
- error_set(api_error, API_ERROR_TYPE_VALIDATION, "method field has wrong type");
- return (-1);
- }
-
- method = &obj->via.array.ptr[2];
-
- if (!method) {
- error_set(api_error, API_ERROR_TYPE_VALIDATION, "unpack method failed");
- return (-1);
- }
-
- req->method = unpack_string(method);
-
- if (!req->method.str) {
- error_set(api_error, API_ERROR_TYPE_VALIDATION, "Error unpacking method");
- return (-1);
- }
-
- /* params */
- if (obj->via.array.ptr[3].type != MSGPACK_OBJECT_ARRAY) {
- free_string(req->method);
- error_set(api_error, API_ERROR_TYPE_VALIDATION, "params field has wrong type");
- return (-1);
- }
-
- params = &obj->via.array.ptr[3];
-
- if (!params) {
- free_string(req->method);
- error_set(api_error, API_ERROR_TYPE_VALIDATION, "unpack params failed");
- return (-1);
- }
-
- if (unpack_params(params, &req->params) == -1) {
- free_string(req->method);
- error_set(api_error, API_ERROR_TYPE_VALIDATION, "Error unpacking params");
- return (-1);
- }
-
- return (0);
-}
-
-bool message_is_error_response(msgpack_object *obj)
-{
- return (obj->via.array.ptr[2].type != MSGPACK_OBJECT_NIL);
-}
-
-uint64_t message_get_id(msgpack_object *obj)
-{
- return (obj->via.array.ptr[1].via.u64);
-}
-
-int message_deserialize_response(struct message_response *res,
- msgpack_object *obj, struct api_error *api_error)
-{
- msgpack_object *type, *msgid, *params;
- uint64_t tmp_msgid;
-
- if (!api_error)
- return (-1);
-
- if (!res || !obj) {
- error_set(api_error, API_ERROR_TYPE_VALIDATION, "Error");
- return (-1);
- }
-
- /* type */
- if (obj->via.array.ptr[0].type != MSGPACK_OBJECT_POSITIVE_INTEGER) {
- error_set(api_error, API_ERROR_TYPE_VALIDATION, "type field has wrong type");
- return (-1);
- }
-
- type = &obj->via.array.ptr[0];
-
- if (!type) {
- error_set(api_error, API_ERROR_TYPE_VALIDATION, "unpack type failed");
- return (-1);
- }
-
- if (unpack_uint(type) != MESSAGE_TYPE_RESPONSE) {
- error_set(api_error, API_ERROR_TYPE_VALIDATION, "type must be 1");
- return (-1);
- }
-
- /* message id */
- msgid = &obj->via.array.ptr[1];
-
- if (!msgid || msgid->type != MSGPACK_OBJECT_POSITIVE_INTEGER) {
- error_set(api_error, API_ERROR_TYPE_VALIDATION, "illegal msgid");
- return (-1);
- }
-
- tmp_msgid = unpack_uint(msgid);
-
- if (tmp_msgid >= UINT32_MAX) {
- error_set(api_error, API_ERROR_TYPE_VALIDATION, "invalid msgid");
- return (-1);
- }
-
- res->msgid = (uint32_t)tmp_msgid;
-
- /* nil */
- if (obj->via.array.ptr[2].type != MSGPACK_OBJECT_NIL) {
- error_set(api_error, API_ERROR_TYPE_VALIDATION, "nil field has wrong type");
- return (-1);
- }
-
- /* params */
- if (obj->via.array.ptr[3].type != MSGPACK_OBJECT_ARRAY) {
- error_set(api_error, API_ERROR_TYPE_VALIDATION, "params field has wrong type");
- return (-1);
- }
-
- params = &obj->via.array.ptr[3];
-
- if (!params) {
- error_set(api_error, API_ERROR_TYPE_VALIDATION, "unpack params failed");
- return (-1);
- }
-
- if (unpack_params(params, &res->params) == -1) {
- error_set(api_error, API_ERROR_TYPE_VALIDATION, "Error unpacking params");
- return (-1);
- }
-
- return (0);
-}
-
-int message_deserialize_error_response(struct message_response *res,
- msgpack_object *obj, struct api_error *api_error)
-{
- msgpack_object *type, *msgid, *params;
- uint64_t tmp_msgid;
-
- if (!api_error)
- return (-1);
-
- if (!res || !obj) {
- error_set(api_error, API_ERROR_TYPE_VALIDATION, "Error");
- return (-1);
- }
-
- /* type */
- if (obj->via.array.ptr[0].type != MSGPACK_OBJECT_POSITIVE_INTEGER) {
- error_set(api_error, API_ERROR_TYPE_VALIDATION, "type field has wrong type");
- return (-1);
- }
-
- type = &obj->via.array.ptr[0];
-
- if (!type) {
- error_set(api_error, API_ERROR_TYPE_VALIDATION, "unpack type failed");
- return (-1);
- }
-
- if (unpack_uint(type) != MESSAGE_TYPE_RESPONSE) {
- error_set(api_error, API_ERROR_TYPE_VALIDATION, "type must be 1");
- return (-1);
- }
-
- /* message id */
- msgid = &obj->via.array.ptr[1];
-
- if (!msgid || msgid->type != MSGPACK_OBJECT_POSITIVE_INTEGER) {
- error_set(api_error, API_ERROR_TYPE_VALIDATION, "illegal msgid");
- return (-1);
- }
-
- tmp_msgid = unpack_uint(msgid);
-
- if (tmp_msgid >= UINT32_MAX) {
- error_set(api_error, API_ERROR_TYPE_VALIDATION, "invalid msgid");
- return (-1);
- }
-
- res->msgid = (uint32_t)tmp_msgid;
-
- /* params */
- if (obj->via.array.ptr[2].type != MSGPACK_OBJECT_ARRAY) {
- error_set(api_error, API_ERROR_TYPE_VALIDATION, "params field has wrong type");
- return (-1);
- }
-
- params = &obj->via.array.ptr[2];
-
- if (!params) {
- error_set(api_error, API_ERROR_TYPE_VALIDATION, "unpack params failed");
- return (-1);
- }
-
- if (unpack_params(params, &res->params) == -1) {
- error_set(api_error, API_ERROR_TYPE_VALIDATION, "Error unpacking params");
- return (-1);
- }
-
- /* nil */
- if (obj->via.array.ptr[3].type != MSGPACK_OBJECT_NIL) {
- free_params(res->params);
- error_set(api_error, API_ERROR_TYPE_VALIDATION, "nil field has wrong type");
- return (-1);
- }
-
- return (0);
-}
-
-
-static void free_message_object(message_object obj)
-{
- switch (obj.type) {
- case OBJECT_TYPE_NIL:
- break;
- case OBJECT_TYPE_INT:
- break;
- case OBJECT_TYPE_UINT:
- break;
- case OBJECT_TYPE_BIN:
- /* FALLTHROUGH */
- case OBJECT_TYPE_STR:
- free_string(obj.data.string);
- break;
- case OBJECT_TYPE_BOOL:
- break;
- case OBJECT_TYPE_FLOAT:
- break;
- case OBJECT_TYPE_ARRAY:
- free_params(obj.data.params);
- break;
- default:
- return;
- }
-}
-
-struct message_object message_object_copy(struct message_object obj)
-{
- switch (obj.type) {
- case OBJECT_TYPE_NIL:
- /* FALLTHROUGH */
- case OBJECT_TYPE_BOOL:
- /* FALLTHROUGH */
- case OBJECT_TYPE_INT:
- /* FALLTHROUGH */
- case OBJECT_TYPE_UINT:
- /* FALLTHROUGH */
- case OBJECT_TYPE_FLOAT:
- return obj;
- case OBJECT_TYPE_BIN:
- /* FALLTHROUGH */
- case OBJECT_TYPE_STR:
- return (struct message_object) {.type = OBJECT_TYPE_STR, .data.string =
- cstring_copy_string(obj.data.string.str) };
- case OBJECT_TYPE_ARRAY: {
- array array = ARRAY_INIT;
-
- for (size_t i = 0; i < obj.data.params.size; i++) {
- kv_push(struct message_object, array,
- message_object_copy(obj.data.params.obj[i]));
- }
-
- return (struct message_object) {.type = OBJECT_TYPE_ARRAY,
- .data.params = array};
- }
- default:
- abort();
- }
-}
-
-
-void free_params(array params)
-{
- for (size_t i = 0; i < params.size; i++)
- free_message_object(params.obj[i]);
-
- if (params.obj)
- FREE(params.obj);
-}
diff --git a/src/rpc/msgpack/pack.c b/src/rpc/msgpack/pack.c
deleted file mode 100644
index a967fe5..0000000
--- a/src/rpc/msgpack/pack.c
+++ /dev/null
@@ -1,208 +0,0 @@
-/**
- * Copyright (C) 2015 splone UG
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include "rpc/msgpack/sb-msgpack-rpc.h"
-#include "sb-common.h"
-
-
-int pack_string(msgpack_packer *pk, string str)
-{
- if (!pk)
- return (-1);
-
- msgpack_pack_bin(pk, str.length);
- msgpack_pack_bin_body(pk, str.str, str.length);
-
- return (0);
-}
-
-
-int pack_uint8(msgpack_packer *pk, uint8_t uinteger)
-{
- if (!pk)
- return (-1);
-
- msgpack_pack_uint8(pk, uinteger);
-
- return (0);
-}
-
-
-int pack_uint16(msgpack_packer *pk, uint16_t uinteger)
-{
- if (!pk)
- return (-1);
-
- msgpack_pack_uint16(pk, uinteger);
-
- return (0);
-}
-
-
-int pack_uint32(msgpack_packer *pk, uint32_t uinteger)
-{
- if (!pk)
- return (-1);
-
- msgpack_pack_uint32(pk, uinteger);
-
- return (0);
-}
-
-
-int pack_uint64(msgpack_packer *pk, uint64_t uinteger)
-{
- if (!pk)
- return (-1);
-
- msgpack_pack_uint64(pk, uinteger);
-
- return (0);
-}
-
-
-int pack_int8(msgpack_packer *pk, int8_t integer)
-{
- if (!pk)
- return (-1);
-
- msgpack_pack_int8(pk, integer);
-
- return (0);
-}
-
-
-int pack_int16(msgpack_packer *pk, int16_t integer)
-{
- if (!pk)
- return (-1);
-
- msgpack_pack_int16(pk, integer);
-
- return (0);
-}
-
-
-int pack_int32(msgpack_packer *pk, int32_t integer)
-{
- if (!pk)
- return (-1);
-
- msgpack_pack_int32(pk, integer);
-
- return (0);
-}
-
-
-int pack_int64(msgpack_packer *pk, int64_t integer)
-{
- if (!pk)
- return (-1);
-
- msgpack_pack_int64(pk, integer);
-
- return (0);
-}
-
-
-int pack_bool(msgpack_packer *pk, bool boolean)
-{
- if (!pk)
- return (-1);
-
- if (boolean)
- msgpack_pack_true(pk);
- else
- msgpack_pack_false(pk);
-
- return (0);
-}
-
-
-int pack_float(msgpack_packer *pk, double floating)
-{
- if (!pk)
- return (-1);
-
- msgpack_pack_double(pk, floating);
-
- return (0);
-}
-
-
-int pack_nil(msgpack_packer *pk)
-{
- if (!pk)
- return (-1);
-
- msgpack_pack_nil(pk);
-
- return (0);
-}
-
-
-int pack_params(msgpack_packer *pk, array params)
-{
- size_t i;
-
- if (!pk)
- return (-1);
-
- msgpack_pack_array(pk, params.size);
-
- for (i = 0; i < params.size; i++) {
- message_object *object = ¶ms.obj[i];
- message_object_type type = object->type;
-
- switch (type) {
- case (OBJECT_TYPE_NIL):
- pack_nil(pk);
- continue;
- case (OBJECT_TYPE_INT):
- pack_int64(pk, object->data.integer);
- continue;
- case (OBJECT_TYPE_UINT):
- pack_uint64(pk, object->data.uinteger);
- continue;
- case (OBJECT_TYPE_BOOL):
- pack_bool(pk, object->data.boolean);
- continue;
- case (OBJECT_TYPE_FLOAT):
- pack_float(pk, object->data.floating);
- continue;
- case (OBJECT_TYPE_ARRAY):
- pack_params(pk, object->data.params);
- continue;
- case (OBJECT_TYPE_STR):
- /* FALLTHROUGH */
- case (OBJECT_TYPE_BIN):
- pack_string(pk, object->data.string);
- continue;
- default:
- return (-1);
- }
- }
-
- return (0);
-}
diff --git a/src/rpc/msgpack/sb-msgpack-rpc.h b/src/rpc/msgpack/sb-msgpack-rpc.h
deleted file mode 100644
index ea776b0..0000000
--- a/src/rpc/msgpack/sb-msgpack-rpc.h
+++ /dev/null
@@ -1,104 +0,0 @@
-/**
- * Copyright (C) 2015 splone UG
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-#pragma once
-
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include "rpc/sb-rpc.h"
-#include "sb-common.h"
-
-
-/*
- * Message Format:
- *
- * - Request -
- *
- * msgpack - Array
- * ----------------------------------------------------
- * | 0 (type) | id (request_id) | method | arguments |
- * ----------------------------------------------------
- *
- * - Response -
- *
- * msgpack - Array
- * --------------------------------------------------------------------
- * | 1 (type) | id (request_id) | Error Code or Null | result or Null |
- * --------------------------------------------------------------------
- */
-
-/* Functions */
-
-int message_unpack_type(msgpack_object *obj, struct message_request *req,
- struct api_error *api_error);
-int message_unpack_validate_type(msgpack_object *ptr,
- struct api_error *api_error);
-int message_unpack_validate_msgid(msgpack_object *ptr,
- struct api_error *api_error);
-int message_unpack_validate_status(msgpack_object *ptr,
- struct api_error *api_error);
-int message_unpack_validate_result(msgpack_object *ptr,
- struct api_error *api_error);
-int message_unpack_msgid(msgpack_object *obj, struct message_request *req,
- struct api_error *api_error);
-int message_unpack_status(msgpack_object *obj, struct message_response *res,
- struct api_error *api_error);
-int message_unpack_result(msgpack_object *obj, struct message_request *req,
- struct api_error *api_error);
-int message_unpack_method(msgpack_object *obj, struct message_request *req,
- struct api_error *api_error);
-int message_unpack_validate_method(msgpack_object *ptr,
- struct api_error *api_error);
-int message_unpack_validate_args(msgpack_object *ptr,
- struct api_error *api_error);
-int message_unpack_args(msgpack_object *obj, struct message_request *req,
- array *params, struct api_error *api_error);
-int message_pack_type(msgpack_packer *pk, uint8_t type);
-int message_pack_method(msgpack_packer *pk, char *method);
-int message_pack_params(msgpack_packer *pk,
- array *params);
-int message_pack_msgid(msgpack_packer *pk, uint32_t msgid);
-
-
-
-string unpack_string(msgpack_object *obj);
-char * unpack_bin(msgpack_object *obj);
-int64_t unpack_int(msgpack_object *obj);
-uint64_t unpack_uint(msgpack_object *obj);
-bool unpack_boolean(msgpack_object *obj);
-double unpack_float(msgpack_object *obj);
-int unpack_params(msgpack_object *obj, array *params);
-
-
-
-int pack_string(msgpack_packer *pk, string str);
-int pack_uint8(msgpack_packer *pk, uint8_t uinteger);
-int pack_uint16(msgpack_packer *pk, uint16_t uinteger);
-int pack_uint32(msgpack_packer *pk, uint32_t uinteger);
-int pack_uint64(msgpack_packer *pk, uint64_t uinteger);
-int pack_int8(msgpack_packer *pk, int8_t integer);
-int pack_int16(msgpack_packer *pk, int16_t integer);
-int pack_int32(msgpack_packer *pk, int32_t integer);
-int pack_int64(msgpack_packer *pk, int64_t integer);
-int pack_nil(msgpack_packer *pk);
-int pack_bool(msgpack_packer *pk, bool boolean);
-int pack_float(msgpack_packer *pk, double floating);
-int pack_params(msgpack_packer *pk, array params);
diff --git a/src/rpc/msgpack/unpack.c b/src/rpc/msgpack/unpack.c
deleted file mode 100644
index 98e0082..0000000
--- a/src/rpc/msgpack/unpack.c
+++ /dev/null
@@ -1,140 +0,0 @@
-/**
- * Copyright (C) 2015 splone UG
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include "rpc/msgpack/sb-msgpack-rpc.h"
-#include "sb-common.h"
-
-string unpack_string(msgpack_object *obj)
-{
- string str;
- char *ret = MALLOC_ARRAY(obj->via.bin.size + 1, char);
-
- if (!ret || !obj->via.bin.ptr) {
- str = (string) {
- .str = NULL, .length = 0
- };
- return (str);
- }
-
- ret[obj->via.bin.size] = '\0';
- memcpy(ret, obj->via.bin.ptr, obj->via.bin.size);
-
- str = (string) {
- .str = ret, .length = obj->via.bin.size
- };
-
- return (str);
-}
-
-
-int64_t unpack_int(msgpack_object *obj)
-{
- return (obj->via.i64);
-}
-
-
-uint64_t unpack_uint(msgpack_object *obj)
-{
- return (obj->via.u64);
-}
-
-
-bool unpack_boolean(msgpack_object *obj)
-{
- return (obj->via.boolean);
-}
-
-
-double unpack_float(msgpack_object *obj)
-{
- return (obj->via.f64);
-}
-
-
-int unpack_params(msgpack_object *obj, array *params)
-{
- struct message_object *elem;
- msgpack_object *tmp;
-
- if (!params)
- return (-1);
-
- /* if array is empty return success */
- if (obj->via.array.size <= 0) {
- params->obj = NULL;
- params->size = 0;
- return (0);
- }
-
- params->obj = CALLOC(obj->via.array.size, struct message_object);
- params->size = obj->via.array.size;
-
- if (!params->obj)
- return (-1);
-
- for (size_t i = 0; i < params->size; i++) {
- tmp = &obj->via.array.ptr[i];
- elem = ¶ms->obj[i];
-
- switch (tmp->type) {
- case MSGPACK_OBJECT_POSITIVE_INTEGER:
- elem->type = OBJECT_TYPE_UINT;
- elem->data.uinteger = unpack_uint(tmp);
- continue;
- case MSGPACK_OBJECT_NEGATIVE_INTEGER:
- elem->type = OBJECT_TYPE_INT;
- elem->data.integer = unpack_int(tmp);
- continue;
- case MSGPACK_OBJECT_STR:
- /* FALLTHROUGH */
- case MSGPACK_OBJECT_BIN:
- elem->type = OBJECT_TYPE_STR;
- elem->data.string = unpack_string(tmp);
- continue;
- case MSGPACK_OBJECT_BOOLEAN:
- elem->type = OBJECT_TYPE_BOOL;
- elem->data.boolean = unpack_boolean(tmp);
- continue;
- case MSGPACK_OBJECT_NIL:
- elem->type = OBJECT_TYPE_NIL;
- continue;
- case MSGPACK_OBJECT_FLOAT:
- elem->type = OBJECT_TYPE_FLOAT;
- elem->data.floating = unpack_float(tmp);
- continue;
- case MSGPACK_OBJECT_ARRAY:
- elem->type = OBJECT_TYPE_ARRAY;
- if (unpack_params(tmp, &elem->data.params) == -1)
- return (-1);
- continue;
- case MSGPACK_OBJECT_MAP:
- /* FALLTHROUGH */
- case MSGPACK_OBJECT_EXT:
- return (-1);
- default:
- return (-1);
- }
- }
-
- return (0);
-}
diff --git a/src/rpc/sb-rpc.h b/src/rpc/sb-rpc.h
index 8e1c9fc..e9cd89d 100644
--- a/src/rpc/sb-rpc.h
+++ b/src/rpc/sb-rpc.h
@@ -43,23 +43,21 @@
/* Typedefs */
typedef struct outputstream outputstream;
typedef struct inputstream inputstream;
-typedef int (*inputstream_cb)(inputstream *inputstream, void *data, bool eof);
+typedef void (*inputstream_cb)(inputstream *inputstream, void *data, bool eof);
typedef struct api_event api_event;
-typedef struct equeue equeue;
-typedef struct queue_entry queue_entry;
typedef struct message_object message_object;
typedef struct connection_request_event_info connection_request_event_info;
#define MESSAGE_REQUEST_ARRAY_SIZE 4
#define MESSAGE_RESPONSE_ARRAY_SIZE 4
-#define MESSAGE_TYPE_REQUEST 0
-#define MESSAGE_TYPE_RESPONSE 1
#define MESSAGE_RESPONSE_UNKNOWN UINT32_MAX
+#define METHOD_MAXLEN 512
+
#define STREAM_BUFFER_SIZE 0xffff
-#define CALLINFO_INIT (struct callinfo) {0, false, false,((struct message_response) {0, ARRAY_INIT})}
+#define CALLINFO_INIT (struct callinfo) {0, false, false, NIL}
/*
@@ -73,6 +71,14 @@ typedef struct connection_request_event_info connection_request_event_info;
/* Enums */
+typedef enum {
+ MESSAGE_TYPE_REQUEST,
+ MESSAGE_TYPE_RESPONSE,
+ MESSAGE_TYPE_NOTIFICATION
+} message_type;
+
+typedef struct object object;
+
typedef enum {
OBJECT_TYPE_NIL,
OBJECT_TYPE_INT,
@@ -80,9 +86,9 @@ typedef enum {
OBJECT_TYPE_BOOL,
OBJECT_TYPE_FLOAT,
OBJECT_TYPE_STR,
- OBJECT_TYPE_BIN,
OBJECT_TYPE_ARRAY,
-} message_object_type;
+ OBJECT_TYPE_DICTIONARY
+} object_type;
typedef enum {
TUNNEL_INITIAL,
@@ -92,14 +98,22 @@ typedef enum {
/* Structs */
+typedef struct key_value_pair key_value_pair;
+
typedef struct {
- message_object *obj;
+ object *items;
size_t size;
size_t capacity;
} array;
-struct message_object {
- message_object_type type;
+typedef struct {
+ key_value_pair *items;
+ size_t size;
+ size_t capacity;
+} dictionary;
+
+struct object {
+ object_type type;
union {
int64_t integer;
uint64_t uinteger;
@@ -107,7 +121,8 @@ struct message_object {
char *bin;
bool boolean;
double floating;
- array params;
+ array array;
+ dictionary dictionary;
} data;
};
@@ -122,6 +137,12 @@ struct message_response {
array params;
};
+struct key_value_pair {
+ string key;
+ object value;
+};
+
+
/*****************************************************************************
* The following crypto_context structure should be considered PRIVATE to *
* the rpc connection layer. No non-rpc connection layer code should be *
@@ -131,6 +152,23 @@ struct message_response {
#define PLUGINKEY_STRING_SIZE ((PLUGINKEY_SIZE * 2) + 1)
#define CLIENTLONGTERMPK_ARRAY_SIZE 32
+typedef object (*apidispatchwrapper)(uint64_t con_id, uint64_t msgid,
+ char *pluginkey, array args, struct api_error *error);
+
+typedef struct {
+ apidispatchwrapper func;
+ bool async;
+ string name;
+} dispatch_info;
+
+/* hashmap declarations */
+MAP_DECLS(uint64_t, string)
+MAP_DECLS(string, ptr_t)
+MAP_DECLS(uint64_t, ptr_t) /* maps callid <> pluginkey */
+MAP_DECLS(cstr_t, ptr_t) /* maps pluginkey <> connection */
+MAP_DECLS(string, dispatch_info)
+MAP_DECLS(cstr_t, uint64_t)
+
struct crypto_context {
crypto_state state;
uint64_t nonce;
@@ -144,50 +182,21 @@ struct crypto_context {
char pluginkeystring[PLUGINKEY_STRING_SIZE];
};
-struct connection {
- uint64_t id;
- uint32_t msgid;
- uint32_t pendingcalls;
+typedef struct wbuffer wbuffer;
+
+struct wbuffer {
+ size_t size;
size_t refcount;
- msgpack_unpacker *mpac;
- msgpack_sbuffer *sbuf;
- char *unpackbuf;
- bool closed;
- equeue *queue;
- struct {
- inputstream *read;
- outputstream *write;
- uv_stream_t *uv;
- } streams;
- kvec_t(struct callinfo *) callvector;
- struct crypto_context cc;
- struct {
- uint64_t start;
- uint64_t end;
- uint64_t pos;
- uint64_t length;
- unsigned char *data;
- } packet;
- uv_timer_t minutekey_timer;
+ char *data;
};
struct callinfo {
- uint32_t msgid;
- bool hasresponse;
- bool errorresponse;
- struct message_response response;
+ uint64_t msgid;
+ bool returned;
+ bool errored;
+ object result;
};
-typedef int (*apidispatchwrapper)(uint64_t con_id,
- struct message_request *request, char *pluginkey,
- struct api_error *error);
-
-typedef struct {
- apidispatchwrapper func;
- bool async;
- string name;
-} dispatch_info;
-
struct outputstream {
uv_stream_t *stream;
size_t curmem;
@@ -216,48 +225,11 @@ struct inputstream {
/* this structure holds a request and all information to send a response */
struct connection_request_event_info {
struct connection *con;
- struct message_request request;
dispatch_info dispatcher;
- struct api_error api_error;
-};
-
-/* this structure holds the request event information and a callback to a
- handler function */
-struct api_event {
- connection_request_event_info info;
- void (*handler)(connection_request_event_info *info);
-};
-
-TAILQ_HEAD(queue, queue_entry);
-
-struct equeue {
- struct queue head;
- equeue *root;
-};
-
-struct queue_entry {
- union {
- equeue *queue;
- struct {
- queue_entry *root;
- api_event event;
- } entry;
- } data;
- TAILQ_ENTRY(queue_entry) node;
+ array args;
+ uint64_t msgid;
};
-/* hashmap declarations */
-MAP_DECLS(uint64_t, string)
-MAP_DECLS(string, ptr_t)
-MAP_DECLS(uint64_t, ptr_t) /* maps callid <> pluginkey */
-MAP_DECLS(cstr_t, ptr_t) /* maps pluginkey <> connection */
-MAP_DECLS(string, dispatch_info)
-MAP_DECLS(cstr_t, uint64_t)
-
-/* define global root event queue */
-extern equeue *equeue_root;
-
-
/* Functions */
/**
@@ -276,15 +248,18 @@ int connection_init(void);
*/
int connection_create(uv_stream_t *stream);
-struct callinfo connection_send_request(char *pluginkey, string method,
- array params, struct api_error *api_error);
+object connection_send_request(char *pluginkey, string method,
+ array args, struct api_error *err);
int connection_send_response(uint64_t con_id, uint32_t msgid,
- array params, struct api_error *api_error);
-int connection_hashmap_put(uint64_t id, struct connection *con);
-int pluginkeys_hashmap_put(char *pluginkey, uint64_t id);
+ object arg, struct api_error *api_error);
+void connection_hashmap_put(uint64_t id, struct connection *con);
+void pluginkeys_hashmap_put(char *pluginkey, uint64_t id);
void loop_wait_for_response(struct connection *con,
struct callinfo *cinfo);
int connection_teardown(void);
+void connection_subscribe(uint64_t id, char *event);
+void connection_unsubscribe(uint64_t id, char *event);
+bool connection_send_event(uint64_t id, char *name, array args);
/**
* Create a new `outputstream` instance. A `outputstream` instance contains the
@@ -413,61 +388,6 @@ int server_start_pipe(char *name);
int server_stop(char * endpoint);
int server_close(void);
-int event_initialize(void);
-
-/**
- * create a new queue or child queue
- *
- * @params root if NULL a root queue is created, if not NULL a child queue for
- * `root` is created
- * @returns queue instance
- */
-equeue * equeue_new(equeue *root);
-
-/**
- * get a queue element
- *
- * this function first checks if the passed queue is empty, if yes it return
- * an empty event object. if it's not empty the first queue entry will be
- * returned and removed from the queue.
- *
- * if the queue entry is a child, the first child queue entry is returned and
- * removed from the child queue. after that, the child queue entry associated
- * event is returned.
- *
- * if the queue entry is no child. the queue entry associated event is returned
- *
- * @param queue queue instance
- */
-api_event equeue_get(equeue *queue);
-
-/**
- * put an event in a queue
- *
- * @param queue queue instance
- * @param event event to enqueue
- */
-int equeue_put(equeue *queue, api_event event);
-
-/**
- * run all events of a queue
- *
- * @params queue queue instance
- */
-void equeue_run_events(equeue *queue);
-
-/**
- * free queue
- *
- * @params queue queue instance
- */
-void equeue_free(equeue *queue);
-
-bool equeue_empty(equeue *queue);
-
-
-
-
void streamhandle_set_inputstream(uv_handle_t *handle,
inputstream *inputstream);
void streamhandle_set_outputstream(uv_handle_t *handle,
@@ -478,16 +398,24 @@ inputstream * streamhandle_get_inputstream(uv_handle_t *handle);
int dispatch_table_init(void);
int dispatch_teardown(void);
dispatch_info dispatch_table_get(string method);
+dispatch_info msgpack_rpc_get_handler_for(const char *name, size_t name_len);
void dispatch_table_put(string method, dispatch_info info);
-int handle_run(uint64_t con_id, struct message_request *request,
- char *pluginkey, struct api_error *error);
-int handle_result(uint64_t con_id, struct message_request *request,
- char *pluginkey, struct api_error *error);
-int handle_register(uint64_t con_id, struct message_request *request,
- char *pluginkey, struct api_error *error);
-int handle_error(uint64_t con_id, struct message_request *request,
- char *pluginkey, struct api_error *error);
-
+object handle_run(uint64_t con_id, uint64_t msgid, char *pluginkey,
+ array args, struct api_error *error);
+object handle_result(uint64_t con_id, uint64_t msgid, char *pluginkey,
+ array args, struct api_error *error);
+object handle_register(uint64_t con_id, uint64_t msgid, char *pluginkey,
+ array args, struct api_error *error);
+object handle_subscribe(uint64_t con_id, uint64_t msgid, char *pluginkey,
+ array args, struct api_error *error);
+object handle_unsubscribe(uint64_t con_id, uint64_t msgid, char *pluginkey,
+ array args, struct api_error *error);
+object handle_broadcast(uint64_t con_id, uint64_t msgid, char *pluginkey,
+ array args, struct api_error *error);
+object msgpack_rpc_handle_invalid_arguments(uint64_t channel_id,
+ uint64_t msgid, char *pluginkey, array args, struct api_error *error);
+object msgpack_rpc_handle_missing_method(uint64_t channel_id,
+ uint64_t msgid, char *pluginkey, array args, struct api_error *error);
/* Message Functions */
void free_params(array params);
@@ -508,6 +436,7 @@ void message_dispatch(msgpack_object *req, msgpack_packer *res);
uint64_t message_get_id(msgpack_object *obj);
bool message_is_error_response(msgpack_object *obj);
struct message_object message_object_copy(struct message_object obj);
+object copy_object(object obj);
@@ -601,3 +530,9 @@ uint64_t uint64_unpack(const unsigned char *x);
* returns -1 in case of error otherwise 0
*/
int byte_isequal(const void *yv, long long ylen, const void *xv);
+
+void msgpack_rpc_serialize_response(uint64_t response_id, struct api_error *err,
+ object arg, msgpack_packer *pac);
+
+void msgpack_rpc_serialize_request(uint64_t request_id, string method,
+ array args, msgpack_packer *pac);
diff --git a/src/sb-common.h b/src/sb-common.h
index a2284cd..95c186c 100644
--- a/src/sb-common.h
+++ b/src/sb-common.h
@@ -78,12 +78,14 @@
#include "queue.h"
#include "khash.h"
#include "kvec.h"
+#include "mem.h"
/* Structs */
#define API_ERROR_MESSAGE_LEN 512
typedef enum {
- API_ERROR_TYPE_VALIDATION,
+ API_ERROR_TYPE_EXCEPTION,
+ API_ERROR_TYPE_VALIDATION
} api_error_type;
typedef struct {
@@ -235,9 +237,6 @@ typedef const char * cstr_t;
/* verbosity global */
extern int8_t verbose_level;
-/* uv loop global */
-extern uv_loop_t loop;
-
/* address parsing helper inline functions */
/** Helper: given a hex digit, return its value, or -1 if it isn't hex. */
static inline int hex_decode_digit(char c)
@@ -385,22 +384,6 @@ define UNUSED(x) x
#define PREDICT_UNLIKELY(exp) (exp)
#endif
-#define MALLOC(type) ((type *)reallocarray(NULL, 1, sizeof(type)))
-
-#define MALLOC_ARRAY(number, type) \
- ((type *)reallocarray(NULL, number, sizeof(type)))
-
-#define CALLOC(number, type) \
- ((type *)calloc(number, sizeof(type)))
-
-#define REALLOC_ARRAY(pointer, number, type) \
- ((type *)reallocarray(pointer, number, sizeof(type)))
-
-#define FREE(pointer) do { \
- free(pointer); \
- pointer = NULL; \
- } while (0)
-
#define LOG(...) \
do { \
fprintf(stdout, __VA_ARGS__); \
@@ -424,6 +407,12 @@ define UNUSED(x) x
} \
} while (0)
+#define LOG_MEMERROR() \
+ do { \
+ LOG_ERROR("[%s] %s (%d):\tFailed to alloc memory.\n", \
+ __FILE__, __func__, __LINE__); \
+ } while (0) \
+
#if 8 == 8
#define ARCH_64
#elif 8 == 4
@@ -442,7 +431,8 @@ define UNUSED(x) x
#define BOX_ADDR_BUF_LEN 48
-#define ARRAY_INIT {.size = 0, .capacity = 0, .obj = NULL}
+#define ARRAY_INIT {.size = 0, .capacity = 0, .items = NULL}
+#define ARRAY_DICT_INIT {.size = 0, .capacity = 0, .items = NULL}
#define STRING_INIT {.str = NULL, .length = 0}
#define ERROR_INIT {.isset = false}
@@ -526,6 +516,8 @@ uint64_t parse_units(const char *val, struct unit_table_t *u, int *ok);
const char * eat_whitespace(const char *s);
char * box_strdup(const char *s);
char * box_strndup(const char *s, size_t n);
+void * sb_memdup(const void *mem, size_t len);
+void * sb_memdup_nulterm(const void *mem, size_t len);
int box_sscanf(const char *buf, const char *pattern, ...);
int box_vsscanf(const char *buf, const char *pattern, va_list ap);
diff --git a/src/sb-pluginkey.c b/src/sb-pluginkey.c
index 62b984d..e3aeedb 100644
--- a/src/sb-pluginkey.c
+++ b/src/sb-pluginkey.c
@@ -48,7 +48,7 @@ int main(int argc, char **argv)
goto fail;
}
- pluginkey = MALLOC_ARRAY((PLUGINKEY_SIZE*2) + 1, char);
+ pluginkey = malloc_array_or_die((PLUGINKEY_SIZE*2) + 1, sizeof(char));
if (!pluginkey) {
LOG("Failed to alloc mem for pluginkey_base16_encoded.\n");
goto fail;
diff --git a/src/signal.c b/src/signal.c
index b362072..1268c04 100644
--- a/src/signal.c
+++ b/src/signal.c
@@ -15,6 +15,7 @@
*/
#include "sb-common.h"
+#include "main.h"
static void signal_sigint_cb(uv_signal_t *uvhandle, int signum);
@@ -23,7 +24,7 @@ uv_signal_t sigint;
int signal_init(void)
{
/* SIGINT */
- if (uv_signal_init(&loop, &sigint) != 0) {
+ if (uv_signal_init(&main_loop.uv, &sigint) != 0) {
return (-1);
}
diff --git a/src/string.c b/src/string.c
index bfa27c5..e1a5d41 100644
--- a/src/string.c
+++ b/src/string.c
@@ -39,7 +39,7 @@ string cstring_copy_string(const char *str)
return (string) STRING_INIT;
length = strlen(str);
- ret = MALLOC_ARRAY(length + 1, char);
+ ret = malloc_array_or_die(length + 1, sizeof(char));
strlcpy(ret, str, length + 1);
diff --git a/src/util.c b/src/util.c
index f8a383f..b4fc31a 100644
--- a/src/util.c
+++ b/src/util.c
@@ -242,7 +242,7 @@ char * box_strndup(const char *s, size_t n)
sbassert(s);
- dup = MALLOC_ARRAY((n+1), char);
+ dup = malloc_array_or_die((n+1), sizeof(char));
strncpy(dup, s, n);
dup[n]='\0';
@@ -250,6 +250,33 @@ char * box_strndup(const char *s, size_t n)
return (dup);
}
+/** Allocate a chunk of len bytes, with the same contents as the
+ * len bytes starting at mem. */
+void * sb_memdup(const void *mem, size_t len)
+{
+ char *duplicate;
+ sbassert(mem);
+
+ duplicate = malloc_array_or_die(len, sizeof(char));
+ memcpy(duplicate, mem, len);
+
+ return duplicate;
+}
+
+/** As sb_memdup(), but add an extra 0 byte at the end of the resulting
+ * memory. */
+void * sb_memdup_nulterm(const void *mem, size_t len)
+{
+ char *duplicate;
+
+ sbassert(mem);
+ duplicate = malloc_array_or_die((len + 1), sizeof(char));
+ memcpy(duplicate, mem, len);
+ duplicate[len] = '\0';
+
+ return duplicate;
+}
+
/** Minimal sscanf replacement: parse buf according to pattern
* and store the results in the corresponding argument fields. Differs from
* sscanf in that:
diff --git a/src/version.h b/src/version.h
index 124db07..3ca89b3 100644
--- a/src/version.h
+++ b/src/version.h
@@ -21,7 +21,7 @@
#define VERSION_MAJOR 0
#define VERSION_MINOR 1
-#define VERSION_REVISION 5
+#define VERSION_REVISION 7
#define VERSION STRINGIFY(VERSION_MAJOR) "." \
STRINGIFY(VERSION_MINOR) "." \
diff --git a/test/functional/crypto.c b/test/functional/crypto.c
index e5d1c43..0d527ea 100644
--- a/test/functional/crypto.c
+++ b/test/functional/crypto.c
@@ -46,11 +46,8 @@ int validate_crypto_cookie_packet(unsigned char *buffer,
ciphertextlen = 160;
blocklen = 144;
- block = MALLOC_ARRAY(ciphertextlen, unsigned char);
- ciphertextpadded = CALLOC(ciphertextlen, unsigned char);
-
- if (block == NULL || ciphertextpadded == NULL)
- return (-1);
+ block = malloc_array_or_die(ciphertextlen, sizeof(unsigned char));
+ ciphertextpadded = calloc_or_die(ciphertextlen, sizeof(unsigned char));
memcpy(ciphertextpadded + 16, buffer + 24, blocklen);
@@ -100,11 +97,8 @@ int validate_crypto_write(unsigned char *buffer, UNUSED(uint64_t length))
ciphertextlen = unpackedlen - 24;
blocklen = unpackedlen - 40;
- block = MALLOC_ARRAY(ciphertextlen, unsigned char);
- ciphertextpadded = CALLOC(ciphertextlen, unsigned char);
-
- if (block == NULL || ciphertextpadded == NULL)
- return (-1);
+ block = malloc_array_or_die(ciphertextlen, sizeof(unsigned char));
+ ciphertextpadded = calloc_or_die(ciphertextlen, sizeof(unsigned char));
memcpy(ciphertextpadded + 16, buffer + 40, blocklen);
diff --git a/test/functional/db-function-flush-args.c b/test/functional/db-function-flush-args.c
index 298e7c4..4ec3a1c 100644
--- a/test/functional/db-function-flush-args.c
+++ b/test/functional/db-function-flush-args.c
@@ -20,6 +20,7 @@
#include "helper-all.h"
#include "rpc/db/sb-db.h"
#include "sb-common.h"
+#include "api/sb-api.h"
#include "helper-unix.h"
@@ -59,23 +60,23 @@ void functional_db_function_flush_args(UNUSED(void **state))
ssize_t argc = 0;
params.size = 4;
- params.obj = CALLOC(params.size, struct message_object);
+ params.items = calloc_or_die(params.size, sizeof(object));
- params.obj[0].type = OBJECT_TYPE_STR;
- params.obj[0].data.string = name;
+ params.items[0].type = OBJECT_TYPE_STR;
+ params.items[0].data.string = name;
- params.obj[1].type = OBJECT_TYPE_STR;
- params.obj[1].data.string = desc;
+ params.items[1].type = OBJECT_TYPE_STR;
+ params.items[1].data.string = desc;
- params.obj[2].type = OBJECT_TYPE_ARRAY;
- params.obj[2].data.params.size = 1;
- params.obj[2].data.params.obj = CALLOC(params.obj[2].data.params.size,
- struct message_object);
- params.obj[2].data.params.obj[0].type = OBJECT_TYPE_INT;
- params.obj[2].data.params.obj[0].data.uinteger = 6;
+ params.items[2].type = OBJECT_TYPE_ARRAY;
+ params.items[2].data.array.size = 1;
+ params.items[2].data.array.items = calloc_or_die(
+ params.items[2].data.array.size, sizeof(object));
+ params.items[2].data.array.items[0].type = OBJECT_TYPE_INT;
+ params.items[2].data.array.items[0].data.uinteger = 6;
connect_and_create(pluginkey);
- args = ¶ms.obj[2].data.params;
+ args = ¶ms.items[2].data.array;
/* register function */
assert_int_equal(0, db_function_add(pluginkey, ¶ms));
@@ -96,14 +97,15 @@ void functional_db_function_flush_args(UNUSED(void **state))
/* now test with two arguments */
- free_params(params.obj[2].data.params);
- params.obj[2].data.params.size = 2;
- params.obj[2].data.params.obj = CALLOC(params.obj[2].data.params.size,
- struct message_object);
- params.obj[2].data.params.obj[0].type = OBJECT_TYPE_INT;
- params.obj[2].data.params.obj[0].data.uinteger = 6;
- params.obj[2].data.params.obj[0].type = OBJECT_TYPE_INT;
- params.obj[2].data.params.obj[0].data.uinteger = 12;
+ api_free_array(params.items[2].data.array);
+
+ params.items[2].data.array.size = 2;
+ params.items[2].data.array.items = calloc_or_die(
+ params.items[2].data.array.size, sizeof(object));
+ params.items[2].data.array.items[0].type = OBJECT_TYPE_INT;
+ params.items[2].data.array.items[0].data.uinteger = 6;
+ params.items[2].data.array.items[0].type = OBJECT_TYPE_INT;
+ params.items[2].data.array.items[0].data.uinteger = 12;
assert_int_equal(0, db_function_add(pluginkey, ¶ms));
argc = db_function_get_argc(pluginkey, name);
@@ -117,5 +119,5 @@ void functional_db_function_flush_args(UNUSED(void **state))
db_close();
- free_params(params);
+ api_free_array(params);
}
diff --git a/test/functional/db-function-register.c b/test/functional/db-function-register.c
index 0c2a522..d551319 100644
--- a/test/functional/db-function-register.c
+++ b/test/functional/db-function-register.c
@@ -19,6 +19,7 @@
#include "helper-all.h"
#include "rpc/sb-rpc.h"
+#include "api/sb-api.h"
#include "sb-common.h"
#include "helper-unix.h"
@@ -33,20 +34,20 @@ void functional_db_function_add(UNUSED(void **state))
array params;
params.size = 4;
- params.obj = CALLOC(params.size, struct message_object);
+ params.items = calloc_or_die(params.size, sizeof(object));
- params.obj[0].type = OBJECT_TYPE_STR;
- params.obj[0].data.string = name;
+ params.items[0].type = OBJECT_TYPE_STR;
+ params.items[0].data.string = name;
- params.obj[1].type = OBJECT_TYPE_STR;
- params.obj[1].data.string = desc;
+ params.items[1].type = OBJECT_TYPE_STR;
+ params.items[1].data.string = desc;
- params.obj[2].type = OBJECT_TYPE_ARRAY;
- params.obj[2].data.params.size = 1;
- params.obj[2].data.params.obj = CALLOC(params.obj[2].data.params.size,
- struct message_object);
- params.obj[2].data.params.obj[0].type = OBJECT_TYPE_INT;
- params.obj[2].data.params.obj[0].data.uinteger = 6;
+ params.items[2].type = OBJECT_TYPE_ARRAY;
+ params.items[2].data.array.size = 1;
+ params.items[2].data.array.items = calloc_or_die(
+ params.items[2].data.array.size, sizeof(object));
+ params.items[2].data.array.items[0].type = OBJECT_TYPE_INT;
+ params.items[2].data.array.items[0].data.uinteger = 6;
connect_and_create(pluginkey);
@@ -59,23 +60,23 @@ void functional_db_function_add(UNUSED(void **state))
/* function without arguments should work */
params.size = 4;
- params.obj[2].data.params.size = 0;
+ params.items[2].data.array.size = 0;
assert_int_equal(0, db_function_add(pluginkey, ¶ms));
/* function without name should not work */
params.size = 4;
- params.obj[2].data.params.size = 1;
- params.obj[0].data.string = cstring_copy_string("");
+ params.items[2].data.array.size = 1;
+ params.items[0].data.string = cstring_copy_string("");
assert_int_not_equal(0, db_function_add(pluginkey, ¶ms));
- free_string(params.obj[0].data.string);
+ free_string(params.items[0].data.string);
/* function without description should work */
- params.obj[0].data.string = name;
- params.obj[1].data.string = cstring_copy_string("");
+ params.items[0].data.string = name;
+ params.items[1].data.string = cstring_copy_string("");
assert_int_equal(0, db_function_add(pluginkey, ¶ms));
db_close();
- free_params(params);
- free_string(desc);
+ api_free_array(params);
+ api_free_string(desc);
}
diff --git a/test/functional/db-function-verify.c b/test/functional/db-function-verify.c
index 280bd6c..9eba55d 100644
--- a/test/functional/db-function-verify.c
+++ b/test/functional/db-function-verify.c
@@ -20,6 +20,7 @@
#include "helper-all.h"
#include "rpc/sb-rpc.h"
+#include "api/sb-api.h"
#include "sb-common.h"
#include "helper-unix.h"
@@ -33,22 +34,22 @@ void functional_db_function_verify(UNUSED(void **state))
array params, *args;
params.size = 4;
- params.obj = CALLOC(params.size, struct message_object);
+ params.items = calloc_or_die(params.size, sizeof(object));
- params.obj[0].type = OBJECT_TYPE_STR;
- params.obj[0].data.string = name;
+ params.items[0].type = OBJECT_TYPE_STR;
+ params.items[0].data.string = name;
- params.obj[1].type = OBJECT_TYPE_STR;
- params.obj[1].data.string = desc;
+ params.items[1].type = OBJECT_TYPE_STR;
+ params.items[1].data.string = desc;
- params.obj[2].type = OBJECT_TYPE_ARRAY;
- params.obj[2].data.params.size = 1;
- params.obj[2].data.params.obj = CALLOC(params.obj[2].data.params.size,
- struct message_object);
- params.obj[2].data.params.obj[0].type = OBJECT_TYPE_INT;
- params.obj[2].data.params.obj[0].data.uinteger = 6;
+ params.items[2].type = OBJECT_TYPE_ARRAY;
+ params.items[2].data.array.size = 1;
+ params.items[2].data.array.items = calloc_or_die(
+ params.items[2].data.array.size, sizeof(object));
+ params.items[2].data.array.items[0].type = OBJECT_TYPE_INT;
+ params.items[2].data.array.items[0].data.uinteger = 6;
- args = ¶ms.obj[2].data.params;
+ args = ¶ms.items[2].data.array;
connect_and_create(pluginkey);
@@ -73,11 +74,11 @@ void functional_db_function_verify(UNUSED(void **state))
/* verifying an existing function with wrong arguments' type */
args->size = 1;
- args->obj[0].type = OBJECT_TYPE_UINT;
- args->obj[0].data.integer = -1;
+ args->items[0].type = OBJECT_TYPE_UINT;
+ args->items[0].data.integer = -1;
assert_int_not_equal(0, db_function_verify(pluginkey, name, args));
- free_params(params);
- free_string(invalid_name);
+ api_free_array(params);
+ api_free_string(invalid_name);
db_close();
}
diff --git a/test/functional/dispatch-handle-broadcast.c b/test/functional/dispatch-handle-broadcast.c
new file mode 100644
index 0000000..2970a52
--- /dev/null
+++ b/test/functional/dispatch-handle-broadcast.c
@@ -0,0 +1,198 @@
+/**
+ * Copyright (C) 2015 splone UG
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+
+#include "sb-common.h"
+#include "rpc/sb-rpc.h"
+#include "api/helpers.h"
+#include "rpc/msgpack/helpers.h"
+#include "rpc/connection/connection.h"
+#include "api/sb-api.h"
+#ifdef __linux__
+#include
+#endif
+
+#include "helper-unix.h"
+#include "helper-all.h"
+#include "helper-validate.h"
+
+static array api_broadcast_valid(void)
+{
+ array args = ARRAY_DICT_INIT;
+ ADD(args, STRING_OBJ(cstring_copy_string("abc")));
+ ADD(args, STRING_OBJ(cstring_copy_string("def")));
+
+ array request = ARRAY_DICT_INIT;
+ ADD(request, STRING_OBJ(cstring_copy_string("testname")));
+ ADD(request, ARRAY_OBJ(args));
+
+ return request;
+}
+
+static array api_broadcast_wrong_args_size(void)
+{
+ array args = ARRAY_DICT_INIT;
+ ADD(args, STRING_OBJ(cstring_copy_string("abc")));
+ ADD(args, STRING_OBJ(cstring_copy_string("def")));
+
+ array request = ARRAY_DICT_INIT;
+ ADD(request, STRING_OBJ(cstring_copy_string("testname")));
+ ADD(request, ARRAY_OBJ(args));
+ ADD(request, STRING_OBJ(cstring_copy_string("wrong")));
+
+ return request;
+}
+
+static array api_broadcast_wrong_args_type(void)
+{
+ array args = ARRAY_DICT_INIT;
+ ADD(args, STRING_OBJ(cstring_copy_string("abc")));
+ ADD(args, STRING_OBJ(cstring_copy_string("def")));
+
+ array request = ARRAY_DICT_INIT;
+ ADD(request, OBJECT_OBJ((object) OBJECT_INIT));
+ ADD(request, ARRAY_OBJ(args));
+
+ return request;
+}
+
+static array api_subscribe_valid(void)
+{
+ array request = ARRAY_DICT_INIT;
+ ADD(request, STRING_OBJ(cstring_copy_string("testname")));
+
+ return request;
+}
+
+int validate_broadcast(const unsigned long data1,
+ UNUSED(const unsigned long data2))
+{
+ struct msgpack_object *deserialized = (struct msgpack_object *) data1;
+ array message;
+
+ msgpack_rpc_to_array(deserialized, &message);
+
+ assert_true(message.items[0].type == OBJECT_TYPE_UINT);
+ assert_int_equal(2, message.items[0].data.uinteger);
+
+ assert_true(message.items[1].type == OBJECT_TYPE_STR);
+ assert_string_equal(message.items[1].data.string.str, "testname");
+
+ assert_true(message.items[2].type == OBJECT_TYPE_ARRAY);
+ assert_int_equal(2, message.items[2].data.array.size);
+
+ assert_true(message.items[2].data.array.items[0].type == OBJECT_TYPE_STR);
+ assert_string_equal(message.items[2].data.array.items[0].data.string.str,
+ "abc");
+
+ assert_true(message.items[2].data.array.items[1].type == OBJECT_TYPE_STR);
+ assert_string_equal(message.items[2].data.array.items[1].data.string.str,
+ "def");
+
+ api_free_array(message);
+
+ return 1;
+}
+
+
+void functional_dispatch_handle_broadcast(UNUSED(void **state))
+{
+ struct plugin *plugin = helper_get_example_plugin();
+ struct connection *con1;
+ struct connection *con2;
+ struct api_error error = ERROR_INIT;
+ array request;
+
+ con1 = calloc_or_die(1, sizeof(struct connection));
+ con1->id = (uint64_t) randommod(281474976710656LL);
+ con1->msgid = 4321;
+ con1->subscribed_events = hashmap_new(cstr_t, ptr_t)();
+
+ con2 = calloc_or_die(1, sizeof(struct connection));
+ con2->id = (uint64_t) randommod(281474976710656LL);
+ con2->msgid = 4321;
+ con2->subscribed_events = hashmap_new(cstr_t, ptr_t)();
+
+ assert_non_null(con1);
+ assert_non_null(con2);
+
+ strlcpy(con1->cc.pluginkeystring, "ABCD", sizeof("ABCD") - 1);
+ strlcpy(con2->cc.pluginkeystring, "EFGH", sizeof("EFGH") - 1);
+
+ connect_to_db();
+ assert_int_equal(0, connection_init());
+
+ con1->refcount++;
+ con2->refcount++;
+
+ connection_hashmap_put(con1->id, con1);
+ connection_hashmap_put(con2->id, con2);
+ pluginkeys_hashmap_put(con1->cc.pluginkeystring, con1->id);
+ pluginkeys_hashmap_put(con2->cc.pluginkeystring, con2->id);
+
+ expect_check(__wrap_crypto_write, &deserialized, validate_broadcast, NULL);
+ expect_check(__wrap_crypto_write, &deserialized, validate_broadcast, NULL);
+
+ request = api_subscribe_valid();
+ handle_subscribe(con1->id, 123, con1->cc.pluginkeystring, request, &error);
+ assert_false(error.isset);
+ api_free_array(request);
+
+ request = api_subscribe_valid();
+ handle_subscribe(con2->id, 123, con2->cc.pluginkeystring, request, &error);
+ assert_false(error.isset);
+ api_free_array(request);
+
+ request = api_broadcast_valid();
+ handle_broadcast(con2->id, 123, con2->cc.pluginkeystring, request, &error);
+ assert_false(error.isset);
+ api_free_array(request);
+
+ request = api_broadcast_wrong_args_size();
+ handle_broadcast(con2->id, 123, con2->cc.pluginkeystring, request, &error);
+ assert_true(error.isset);
+ error.isset = false;
+ api_free_array(request);
+
+ request = api_broadcast_wrong_args_type();
+ handle_broadcast(con2->id, 123, con2->cc.pluginkeystring, request, &error);
+ assert_true(error.isset);
+ error.isset = false;
+ api_free_array(request);
+
+ request = api_subscribe_valid();
+ handle_unsubscribe(con1->id, 123, con1->cc.pluginkeystring, request, &error);
+ assert_false(error.isset);
+ api_free_array(request);
+
+ request = api_subscribe_valid();
+ handle_unsubscribe(con2->id, 123, con2->cc.pluginkeystring, request, &error);
+ assert_false(error.isset);
+ api_free_array(request);
+
+ con1->closed = true;
+ con2->closed = true;
+
+ hashmap_free(cstr_t, ptr_t)(con1->subscribed_events);
+ hashmap_free(cstr_t, ptr_t)(con2->subscribed_events);
+
+ helper_free_plugin(plugin);
+ connection_teardown();
+ FREE(con1);
+ FREE(con2);
+ db_close();
+}
diff --git a/test/functional/dispatch-handle-register.c b/test/functional/dispatch-handle-register.c
index 89faeb5..dc6912e 100644
--- a/test/functional/dispatch-handle-register.c
+++ b/test/functional/dispatch-handle-register.c
@@ -19,209 +19,233 @@
#include "sb-common.h"
#include "tweetnacl.h"
#include "rpc/sb-rpc.h"
-#include "rpc/msgpack/sb-msgpack-rpc.h"
+#include "rpc/connection/connection.h"
+#include "api/helpers.h"
+#include "api/sb-api.h"
+#ifdef __linux__
+#include
+#endif
#include "helper-unix.h"
#include "helper-all.h"
#include "helper-validate.h"
-void functional_dispatch_handle_register(UNUSED(void **state))
+static array api_register_valid(void)
{
- connection_request_event_info info;
- struct message_request *request;
- array *meta, *functions, *func1, *func2, *args;
- struct api_error *err = MALLOC(struct api_error);
- char pluginkey[PLUGINKEY_STRING_SIZE] = "012345789ABCDEFH";
+ array registermeta = ARRAY_DICT_INIT;
+ ADD(registermeta, STRING_OBJ(cstring_copy_string("register")));
+ ADD(registermeta, STRING_OBJ(cstring_copy_string("register a plugin")));
+ ADD(registermeta, STRING_OBJ(cstring_copy_string("stze")));
+ ADD(registermeta, STRING_OBJ(cstring_copy_string("none")));
+
+ array registerfunc1args = ARRAY_DICT_INIT;
+ ADD(registerfunc1args, INTEGER_OBJ(1234));
+ ADD(registerfunc1args, STRING_OBJ(cstring_copy_string("func1 arg")));
+
+ array registerfunc1 = ARRAY_DICT_INIT;
+ ADD(registerfunc1, STRING_OBJ(cstring_copy_string("my function name")));
+ ADD(registerfunc1, STRING_OBJ(cstring_copy_string("my function description")));
+ ADD(registerfunc1, ARRAY_OBJ(registerfunc1args));
+
+ array registerfunc2 = ARRAY_DICT_INIT;
+ ADD(registerfunc2, STRING_OBJ(cstring_copy_string("my sec function name")));
+ ADD(registerfunc2, STRING_OBJ(cstring_copy_string("my sec function description")));
+ ADD(registerfunc2, ARRAY_OBJ(ARRAY_DICT_INIT));
+
+ array registerfunctions = ARRAY_DICT_INIT;
+ ADD(registerfunctions, ARRAY_OBJ(registerfunc1));
+ ADD(registerfunctions, ARRAY_OBJ(registerfunc2));
+
+ array request = ARRAY_DICT_INIT;
+ ADD(request, ARRAY_OBJ(registermeta));
+ ADD(request, ARRAY_OBJ(registerfunctions));
+
+ return request;
+}
+
+static array api_register_wrong_args_size(void)
+{
+ array registermeta = ARRAY_DICT_INIT;
+ ADD(registermeta, STRING_OBJ(cstring_copy_string("register")));
+ ADD(registermeta, STRING_OBJ(cstring_copy_string("register a plugin")));
+ ADD(registermeta, STRING_OBJ(cstring_copy_string("stze")));
+ ADD(registermeta, STRING_OBJ(cstring_copy_string("none")));
+
+ array request = ARRAY_DICT_INIT;
+ ADD(request, ARRAY_OBJ(registermeta));
+
+ return request;
+}
- string name = cstring_copy_string("register");
- string description = cstring_copy_string("register a plugin");
- string author = cstring_copy_string("test");
- string license = cstring_copy_string("none");
+static array api_register_wrong_args_type(void)
+{
+ array registerfunc1 = ARRAY_DICT_INIT;
+ ADD(registerfunc1, STRING_OBJ(cstring_copy_string("my function name")));
+ ADD(registerfunc1, STRING_OBJ(cstring_copy_string("my function description")));
+ ADD(registerfunc1, ARRAY_OBJ(ARRAY_DICT_INIT));
- info.request.msgid = 1;
+ array registerfunc2 = ARRAY_DICT_INIT;
+ ADD(registerfunc2, STRING_OBJ(cstring_copy_string("my sec function name")));
+ ADD(registerfunc2, STRING_OBJ(cstring_copy_string("my sec function description")));
+ ADD(registerfunc2, ARRAY_OBJ(ARRAY_DICT_INIT));
- request = &info.request;
- assert_non_null(request);
+ array registerfunctions = ARRAY_DICT_INIT;
+ ADD(registerfunctions, ARRAY_OBJ(registerfunc1));
+ ADD(registerfunctions, ARRAY_OBJ(registerfunc2));
- info.api_error = *err;
- assert_non_null(err);
+ array request = ARRAY_DICT_INIT;
+ ADD(request, STRING_OBJ(cstring_copy_string("wrong")));
+ ADD(request, ARRAY_OBJ(registerfunctions));
- info.api_error.isset = false;
+ return request;
+}
- info.con = CALLOC(1, struct connection);
- info.con->closed = true;
- info.con->id = 1234;
- strlcpy(info.con->cc.pluginkeystring, pluginkey, PLUGINKEY_STRING_SIZE+1);
- assert_non_null(info.con);
+static array api_register_wrong_meta_size(void)
+{
+ array registermeta = ARRAY_DICT_INIT;
+ ADD(registermeta, STRING_OBJ(cstring_copy_string("register")));
+ ADD(registermeta, STRING_OBJ(cstring_copy_string("register a plugin")));
+ ADD(registermeta, STRING_OBJ(cstring_copy_string("stze")));
+
+ array registerfunc1 = ARRAY_DICT_INIT;
+ ADD(registerfunc1, STRING_OBJ(cstring_copy_string("my function name")));
+ ADD(registerfunc1, STRING_OBJ(cstring_copy_string("my function description")));
+ ADD(registerfunc1, ARRAY_OBJ(ARRAY_DICT_INIT));
+
+ array registerfunc2 = ARRAY_DICT_INIT;
+ ADD(registerfunc2, STRING_OBJ(cstring_copy_string("my sec function name")));
+ ADD(registerfunc2, STRING_OBJ(cstring_copy_string("my sec function description")));
+ ADD(registerfunc2, ARRAY_OBJ(ARRAY_DICT_INIT));
+
+ array registerfunctions = ARRAY_DICT_INIT;
+ ADD(registerfunctions, ARRAY_OBJ(registerfunc1));
+ ADD(registerfunctions, ARRAY_OBJ(registerfunc2));
+
+ array request = ARRAY_DICT_INIT;
+ ADD(request, ARRAY_OBJ(registermeta));
+ ADD(request, ARRAY_OBJ(registerfunctions));
+
+ return request;
+}
- connect_and_create(info.con->cc.pluginkeystring);
+static array api_register_missing_function_description(void)
+{
+ array registermeta = ARRAY_DICT_INIT;
+ ADD(registermeta, STRING_OBJ(cstring_copy_string("register")));
+ ADD(registermeta, STRING_OBJ(cstring_copy_string("register a plugin")));
+ ADD(registermeta, STRING_OBJ(cstring_copy_string("stze")));
+ ADD(registermeta, STRING_OBJ(cstring_copy_string("none")));
+
+ array registerfunc1 = ARRAY_DICT_INIT;
+ ADD(registerfunc1, STRING_OBJ(cstring_copy_string("my function name")));
+ ADD(registerfunc1, OBJECT_OBJ((object) OBJECT_INIT));
+ ADD(registerfunc1, ARRAY_OBJ(ARRAY_DICT_INIT));
+
+ array registerfunc2 = ARRAY_DICT_INIT;
+ ADD(registerfunc2, STRING_OBJ(cstring_copy_string("my sec function name")));
+ ADD(registerfunc2, STRING_OBJ(cstring_copy_string("my sec function description")));
+ ADD(registerfunc2, ARRAY_OBJ(ARRAY_DICT_INIT));
+
+ array registerfunctions = ARRAY_DICT_INIT;
+ ADD(registerfunctions, ARRAY_OBJ(registerfunc1));
+ ADD(registerfunctions, ARRAY_OBJ(registerfunc2));
+
+ array request = ARRAY_DICT_INIT;
+ ADD(request, ARRAY_OBJ(registermeta));
+ ADD(request, ARRAY_OBJ(registerfunctions));
+
+ return request;
+}
+
+static array api_register_empty_functions(void)
+{
+ array registermeta = ARRAY_DICT_INIT;
+ ADD(registermeta, STRING_OBJ(cstring_copy_string("register")));
+ ADD(registermeta, STRING_OBJ(cstring_copy_string("register a plugin")));
+ ADD(registermeta, STRING_OBJ(cstring_copy_string("stze")));
+ ADD(registermeta, STRING_OBJ(cstring_copy_string("none")));
+
+ array registerfunctions = ARRAY_DICT_INIT;
+
+ array request = ARRAY_DICT_INIT;
+ ADD(request, ARRAY_OBJ(registermeta));
+ ADD(request, ARRAY_OBJ(registerfunctions));
+
+ return request;
+}
+
+void functional_dispatch_handle_register(UNUSED(void **state))
+{
+ struct connection *con;
+ array request;
+ struct api_error error = ERROR_INIT;
+ char pluginkey[PLUGINKEY_STRING_SIZE] = "012345789ABCDEFH";
+
+ con = calloc_or_die(1, sizeof(struct connection));
+ con->closed = true;
+ con->id = 1234;
+ con->msgid = 4321;
+ strlcpy(con->cc.pluginkeystring, pluginkey, PLUGINKEY_STRING_SIZE+1);
+ assert_non_null(con);
+
+ connect_and_create(con->cc.pluginkeystring);
assert_int_equal(0, connection_init());
- connection_hashmap_put(info.con->id, info.con);
-
- /* first level arrays:
- *
- * [meta] args->obj[0]
- * [functions] args->obj[1]
- */
- request->params.size = 2;
- request->params.obj = CALLOC(2, struct message_object);
- request->params.obj[0].type = OBJECT_TYPE_ARRAY;
- request->params.obj[1].type = OBJECT_TYPE_ARRAY;
-
- /* meta array:
- *
- * [name]
- * [desciption]
- * [author]
- * [license]
- */
- meta = &request->params.obj[0].data.params;
- meta->size = 4;
- meta->obj = CALLOC(meta->size, struct message_object);
- meta->obj[0].type = OBJECT_TYPE_STR;
- meta->obj[0].data.string = name;
- meta->obj[1].type = OBJECT_TYPE_STR;
- meta->obj[1].data.string = description;
- meta->obj[2].type = OBJECT_TYPE_STR;
- meta->obj[2].data.string = author;
- meta->obj[3].type = OBJECT_TYPE_STR;
- meta->obj[3].data.string = license;
-
- functions = &request->params.obj[1].data.params;
- functions->size = 2;
- functions->obj = CALLOC(functions->size, struct message_object);
- functions->obj[0].type = OBJECT_TYPE_ARRAY;
- functions->obj[1].type = OBJECT_TYPE_ARRAY;
-
- func1 = &functions->obj[0].data.params;
- func1->size = 3;
- func1->obj = CALLOC(3, struct message_object);
- func1->obj[0].type = OBJECT_TYPE_STR;
- func1->obj[0].data.string = cstring_copy_string("my function name");
- func1->obj[1].type = OBJECT_TYPE_STR;
- func1->obj[1].data.string = cstring_copy_string("my function description");
- func1->obj[2].type = OBJECT_TYPE_ARRAY;
- func1->obj[2].data.params.size = 0;
-
- func2 = &functions->obj[1].data.params;
- func2->size = 3;
- func2->obj = CALLOC(3, struct message_object);
- func2->obj[0].type = OBJECT_TYPE_STR;
- func2->obj[0].data.string = cstring_copy_string("my snd function name");
- func2->obj[1].type = OBJECT_TYPE_STR;
- func2->obj[1].data.string = cstring_copy_string("my snd function description");
- func2->obj[2].type = OBJECT_TYPE_ARRAY;
- func2->obj[2].data.params.size = 0;
+ connection_hashmap_put(con->id, con);
/* a valid request has to trigger the corresponding response */
- assert_false(info.api_error.isset);
- expect_check(__wrap_crypto_write, &deserialized, validate_register_response, NULL);
- assert_int_equal(0, handle_register(info.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error));
+ request = api_register_valid();
+ assert_false(error.isset);
+ handle_register(con->id, 123, con->cc.pluginkeystring, request, &error);
+ assert_false(error.isset);
+ api_free_array(request);
/* payload does not consist of two arrays, meta and func */
- request->params.size = 1;
- assert_false(info.api_error.isset);
- assert_int_not_equal(0, handle_register(info.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error));
- assert_true(info.api_error.isset);
- assert_true(info.api_error.type == API_ERROR_TYPE_VALIDATION);
- info.api_error.isset = false;
- request->params.size = 2;
+ request = api_register_wrong_args_size();
+ assert_false(error.isset);
+ handle_register(con->id, 123, con->cc.pluginkeystring, request, &error);
+ assert_true(error.isset);
+ assert_true(error.type == API_ERROR_TYPE_VALIDATION);
+ error.isset = false;
+ api_free_array(request);
/* object has wrong type */
- request->params.obj[0].type = OBJECT_TYPE_STR;
- assert_false(info.api_error.isset);
- assert_int_not_equal(0, handle_register(info.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error));
- assert_true(info.api_error.isset);
- assert_true(info.api_error.type == API_ERROR_TYPE_VALIDATION);
- info.api_error.isset = false;
- request->params.obj[0].type = OBJECT_TYPE_ARRAY;
+ request = api_register_wrong_args_type();
+ assert_false(error.isset);
+ handle_register(con->id, 123, con->cc.pluginkeystring, request, &error);
+ assert_true(error.isset);
+ assert_true(error.type == API_ERROR_TYPE_VALIDATION);
+ error.isset = false;
+ api_free_array(request);
/* meta has wrong size */
- meta->size = 3;
- assert_false(info.api_error.isset);
- assert_int_not_equal(0, handle_register(info.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error));
- assert_true(info.api_error.isset);
- assert_true(info.api_error.type == API_ERROR_TYPE_VALIDATION);
- info.api_error.isset = false;
- meta->size = 4;
-
- /* plugin name is very long and contains same random bytes. since
- * there are no real constrains for the plugin name, this should work.
- */
- size_t len = 8096;
- unsigned char *buf = MALLOC_ARRAY(len, unsigned char);
- assert_non_null(buf);
- randombytes(buf, len);
- meta->obj[0].data.string.str = (char*) buf;
- meta->obj[0].data.string.length = len;
- assert_false(info.api_error.isset);
- expect_check(__wrap_crypto_write, &deserialized, validate_register_response, NULL);
- assert_int_equal(0, handle_register(info.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error));
- assert_false(info.api_error.isset);
- meta->obj[0].data.string = name;
- FREE(buf);
+ request = api_register_wrong_meta_size();
+ assert_false(error.isset);
+ handle_register(con->id, 123, con->cc.pluginkeystring, request, &error);
+ assert_true(error.isset);
+ assert_true(error.type == API_ERROR_TYPE_VALIDATION);
+ error.isset = false;
+ api_free_array(request);
/* no function description field should fail */
- func1->obj[1].type = OBJECT_TYPE_NIL;
- assert_false(info.api_error.isset);
- assert_int_not_equal(0, handle_register(info.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error));
- assert_true(info.api_error.isset);
- assert_true(info.api_error.type == API_ERROR_TYPE_VALIDATION);
- func1->obj[1].type = OBJECT_TYPE_STR;
- info.api_error.isset = false;
-
- /* very long function description should work so far */
- string func_desc = func1->obj[1].data.string;
- len = 8096;
- buf = MALLOC_ARRAY(len, unsigned char);
- assert_non_null(buf);
- randombytes(buf, len);
- func1->obj[1].data.string.str = (char*) buf;
- func1->obj[1].data.string.length = len;
- assert_false(info.api_error.isset);
- expect_check(__wrap_crypto_write, &deserialized, validate_register_response, NULL);
- assert_int_equal(0, handle_register(info.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error));
- assert_false(info.api_error.isset);
- func1->obj[1].data.string = func_desc;
- FREE(buf);
+ request = api_register_missing_function_description();
+ assert_false(error.isset);
+ handle_register(con->id, 123, con->cc.pluginkeystring, request, &error);
+ assert_true(error.isset);
+ assert_true(error.type == API_ERROR_TYPE_VALIDATION);
+ error.isset = false;
+ api_free_array(request);
/* registering w/o function, should not work */
- functions->size = 0;
- assert_false(info.api_error.isset);
- assert_int_not_equal(0, handle_register(info.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error));
- assert_true(info.api_error.isset);
- assert_true(info.api_error.type == API_ERROR_TYPE_VALIDATION);
- info.api_error.isset = false;
- functions->size = 1;
-
- /* registering function with arguments, should work. the arguments
- * remain for the following tests. */
- args = &func1->obj[2].data.params;
- args->size = 2;
- args->obj = CALLOC(args->size, struct message_object);
- args->obj[0].type = OBJECT_TYPE_INT;
- args->obj[0].data.integer = 5;
- args->obj[1].type = OBJECT_TYPE_STR;
- args->obj[1].data.string = cstring_copy_string("foobar");
- assert_false(info.api_error.isset);
- expect_check(__wrap_crypto_write, &deserialized, validate_register_response, NULL);
- assert_int_equal(0, handle_register(info.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error));
- assert_false(info.api_error.isset);
- info.api_error.isset = false;
-
- /* registering only one function must work */
- functions->size = 1;
- assert_false(info.api_error.isset);
- expect_check(__wrap_crypto_write, &deserialized, validate_register_response, NULL);
- assert_int_equal(0, handle_register(info.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error));
- assert_false(info.api_error.isset);
- info.api_error.isset = false;
- functions->size = 2;
-
- free_params(request->params);
+ request = api_register_empty_functions();
+ assert_false(error.isset);
+ handle_register(con->id, 123, con->cc.pluginkeystring, request, &error);
+ assert_true(error.isset);
+ assert_true(error.type == API_ERROR_TYPE_VALIDATION);
+ error.isset = false;
+ api_free_array(request);
+
connection_teardown();
- FREE(err);
- FREE(info.con);
+ FREE(con);
db_close();
}
diff --git a/test/functional/dispatch-handle-result.c b/test/functional/dispatch-handle-result.c
index 3918a9b..105ce31 100644
--- a/test/functional/dispatch-handle-result.c
+++ b/test/functional/dispatch-handle-result.c
@@ -18,155 +18,239 @@
#include "helper-all.h"
#include "sb-common.h"
-#include "rpc/msgpack/sb-msgpack-rpc.h"
#include "rpc/sb-rpc.h"
+#include "rpc/connection/connection.h"
+#include "api/helpers.h"
+#include "api/sb-api.h"
+#ifdef __linux__
+#include
+#endif
#include "helper-all.h"
#include "helper-unix.h"
#include "helper-validate.h"
-static struct plugin *prepare_test(connection_request_event_info *info)
+static array api_result_valid(struct plugin * plugin)
{
- struct api_error err = ERROR_INIT;
- info->api_error = err;
+ array meta = ARRAY_DICT_INIT;
+ ADD(meta, UINTEGER_OBJ(plugin->callid));
- /* create test plugin that is used for the tests */
- struct plugin *plugin = helper_get_example_plugin();
- helper_register_plugin(plugin);
+ array args = ARRAY_DICT_INIT;
+ ADD(args, STRING_OBJ(cstring_copy_string(plugin->function->args[0].str)));
+ ADD(args, STRING_OBJ(cstring_copy_string(plugin->function->args[1].str)));
- /* establish fake connection to plugin */
- info->con = CALLOC(1, struct connection);
- info->con->closed = true;
- connection_hashmap_put(info->con->id, info->con);
- strlcpy(info->con->cc.pluginkeystring, plugin->key.str, plugin->key.length+1);
- assert_non_null(info->con);
+ array request = ARRAY_DICT_INIT;
+ ADD(request, ARRAY_OBJ(meta));
+ ADD(request, ARRAY_OBJ(args));
- expect_check(__wrap_crypto_write, &deserialized, validate_run_request, plugin);
- expect_check(__wrap_crypto_write, &deserialized, validate_run_response, plugin);
+ return request;
+}
+
+static array api_result_wrong_args_size_small(struct plugin * plugin)
+{
+ array meta = ARRAY_DICT_INIT;
+ ADD(meta, UINTEGER_OBJ(plugin->callid));
+
+ array request = ARRAY_DICT_INIT;
+ ADD(request, ARRAY_OBJ(meta));
+
+ return request;
+}
+
+static array api_result_wrong_args_size_big(struct plugin * plugin)
+{
+ array meta = ARRAY_DICT_INIT;
+ ADD(meta, UINTEGER_OBJ(plugin->callid));
+
+ array args = ARRAY_DICT_INIT;
+ ADD(args, STRING_OBJ(cstring_copy_string(plugin->function->args[0].str)));
+ ADD(args, STRING_OBJ(cstring_copy_string(plugin->function->args[1].str)));
+
+ array wrong = ARRAY_DICT_INIT;
+
+ array request = ARRAY_DICT_INIT;
+ ADD(request, ARRAY_OBJ(meta));
+ ADD(request, ARRAY_OBJ(args));
+ ADD(request, ARRAY_OBJ(wrong));
+
+ return request;
+}
+
+static array api_result_wrong_meta_size(struct plugin * plugin)
+{
+ array meta = ARRAY_DICT_INIT;
+ ADD(meta, UINTEGER_OBJ(plugin->callid));
+ ADD(meta, STRING_OBJ(cstring_copy_string("wrong")));
+
+ array args = ARRAY_DICT_INIT;
+ ADD(args, STRING_OBJ(cstring_copy_string(plugin->function->args[0].str)));
+ ADD(args, STRING_OBJ(cstring_copy_string(plugin->function->args[1].str)));
+
+ array request = ARRAY_DICT_INIT;
+ ADD(request, ARRAY_OBJ(meta));
+ ADD(request, ARRAY_OBJ(args));
+
+ return request;
+}
+
+static array api_result_wrong_meta_type(struct plugin * plugin)
+{
+ array args = ARRAY_DICT_INIT;
+ ADD(args, STRING_OBJ(cstring_copy_string(plugin->function->args[0].str)));
+ ADD(args, STRING_OBJ(cstring_copy_string(plugin->function->args[1].str)));
+
+ array request = ARRAY_DICT_INIT;
+ ADD(request, STRING_OBJ(cstring_copy_string("wrong")));
+ ADD(request, ARRAY_OBJ(args));
+
+ return request;
+}
+
+static array api_result_wrong_meta_callid_type(struct plugin * plugin)
+{
+ array meta = ARRAY_DICT_INIT;
+ ADD(meta, STRING_OBJ(cstring_copy_string("wrong")));
- helper_build_run_request(&info->request, plugin
- ,OBJECT_TYPE_ARRAY /* meta array type */
- ,2 /* meta size */
- ,OBJECT_TYPE_STR /* target plugin key */
- ,OBJECT_TYPE_NIL /* call id type */
- ,OBJECT_TYPE_STR /* function name */
- ,OBJECT_TYPE_ARRAY /* arguments */
- );
+ array args = ARRAY_DICT_INIT;
+ ADD(args, STRING_OBJ(cstring_copy_string(plugin->function->args[0].str)));
+ ADD(args, STRING_OBJ(cstring_copy_string(plugin->function->args[1].str)));
- assert_int_equal(0, handle_run(info->con->id, &info->request, info->con->cc.pluginkeystring, &info->api_error));
- free_params(info->request.params);
+ array request = ARRAY_DICT_INIT;
+ ADD(request, ARRAY_OBJ(meta));
+ ADD(request, ARRAY_OBJ(args));
- return plugin;
+ return request;
}
+
void functional_dispatch_handle_result(UNUSED(void **state))
{
- connection_request_event_info info;
- struct plugin *plugin;
+ struct api_error error = ERROR_INIT;
+ array request = ARRAY_DICT_INIT;
+ struct connection *con;
- plugin = prepare_test(&info);
+ /* create test plugin that is used for the tests */
+ struct plugin *plugin = helper_get_example_plugin();
- expect_check(__wrap_crypto_write, &deserialized, validate_result_request, NULL);
- expect_check(__wrap_crypto_write, &deserialized, validate_result_response, NULL);
+ con = calloc_or_die(1, sizeof(struct connection));
+ con->closed = true;
+ con->id = (uint64_t) randommod(281474976710656LL);
+ con->msgid = 4321;
- /*
- * The following asserts verifies a legitim run call. In detail, it
- * verifies, that the forwarded run request by the server is of proper
- * format.
- * */
- helper_build_result_request(&info.request, plugin
- ,OBJECT_TYPE_ARRAY /* meta array type */
- ,1 /* meta array size */
- ,OBJECT_TYPE_UINT /* call id type */
- ,OBJECT_TYPE_ARRAY /* arguments */
- );
-
- assert_int_equal(0, handle_result(info.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error));
+ assert_non_null(con);
- /*
- * The following asserts verify, that the handle_result method cancels
- * as soon as illegitim result calls are processed. A API_ERROR must be
- * set in order inform the caller later on.
- */
+ strlcpy(con->cc.pluginkeystring, plugin->key.str, plugin->key.length+1);
- /* size of payload too small */
- info.request.params.size = 1;
+ array registermeta = ARRAY_DICT_INIT;
+ ADD(registermeta, STRING_OBJ(cstring_copy_string(plugin->name.str)));
+ ADD(registermeta, STRING_OBJ(cstring_copy_string(plugin->description.str)));
+ ADD(registermeta, STRING_OBJ(cstring_copy_string(plugin->author.str)));
+ ADD(registermeta, STRING_OBJ(cstring_copy_string(plugin->license.str)));
- assert_int_not_equal(0, handle_result(info.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error));
- assert_true(info.api_error.isset);
- assert_true(info.api_error.type == API_ERROR_TYPE_VALIDATION);
- info.api_error.isset = false;
+ array registerarguments = ARRAY_DICT_INIT;
+ ADD(registerarguments, STRING_OBJ(cstring_copy_string(plugin->function->args[0].str)));
+ ADD(registerarguments, STRING_OBJ(cstring_copy_string(plugin->function->args[1].str)));
- /* size of payload too big */
- info.request.params.size = 3;
+ array registerfunc1 = ARRAY_DICT_INIT;
+ ADD(registerfunc1, STRING_OBJ(cstring_copy_string(plugin->function->name.str)));
+ ADD(registerfunc1, STRING_OBJ(cstring_copy_string(plugin->function->description.str)));
+ ADD(registerfunc1, ARRAY_OBJ(registerarguments));
- assert_int_not_equal(0, handle_result(info.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error));
- assert_true(info.api_error.isset);
- assert_true(info.api_error.type == API_ERROR_TYPE_VALIDATION);
- info.api_error.isset = false;
+ array registerfunctions = ARRAY_DICT_INIT;
+ ADD(registerfunctions, ARRAY_OBJ(registerfunc1));
- info.request.params.size = 2;
+ array registerrequest = ARRAY_DICT_INIT;
+ ADD(registerrequest, ARRAY_OBJ(registermeta));
+ ADD(registerrequest, ARRAY_OBJ(registerfunctions));
- /* wrong meta size */
- helper_request_set_meta_size(&info.request,
- OBJECT_TYPE_ARRAY, 2);
+ connect_to_db();
+ assert_int_equal(0, connection_init());
- assert_int_not_equal(0, handle_result(info.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error));
- assert_true(info.api_error.isset);
- assert_true(info.api_error.type == API_ERROR_TYPE_VALIDATION);
- info.api_error.isset = false;
+ error.isset = false;
- helper_request_set_meta_size(&info.request,
- OBJECT_TYPE_ARRAY, 1);
+ con->refcount++;
- /* wrong meta type */
- helper_request_set_meta_size(&info.request,
- OBJECT_TYPE_STR, 1);
+ connection_hashmap_put(con->id, con);
+ pluginkeys_hashmap_put(con->cc.pluginkeystring, con->id);
- assert_int_not_equal(0, handle_result(info.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error));
- assert_true(info.api_error.isset);
- assert_true(info.api_error.type == API_ERROR_TYPE_VALIDATION);
- info.api_error.isset = false;
+ handle_register(con->id, 123, con->cc.pluginkeystring, registerrequest,
+ &error);
+ assert_false(error.isset);
+ api_free_array(registerrequest);
- helper_request_set_meta_size(&info.request,
- OBJECT_TYPE_ARRAY, 1);
+ expect_check(__wrap_crypto_write, &deserialized, validate_run_request, plugin);
- /* wrong callid type */
- helper_request_set_callid(&info.request, OBJECT_TYPE_STR);
+ // RUN API CALL
+ array runmeta = ARRAY_DICT_INIT;
+ ADD(runmeta, STRING_OBJ(cstring_copy_string(plugin->key.str)));
+ ADD(runmeta, OBJECT_OBJ((object) OBJECT_INIT));
- assert_int_not_equal(0, handle_result(info.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error));
- assert_true(info.api_error.isset);
- assert_true(info.api_error.type == API_ERROR_TYPE_VALIDATION);
- info.api_error.isset = false;
+ array runargs = ARRAY_DICT_INIT;
+ ADD(runargs, STRING_OBJ(cstring_copy_string(plugin->function->args[0].str)));
+ ADD(runargs, STRING_OBJ(cstring_copy_string(plugin->function->args[1].str)));
- helper_request_set_callid(&info.request, OBJECT_TYPE_UINT);
+ array runrequest = ARRAY_DICT_INIT;
+ ADD(runrequest, ARRAY_OBJ(runmeta));
+ ADD(runrequest, STRING_OBJ(cstring_copy_string(plugin->function->name.str)));
+ ADD(runrequest, ARRAY_OBJ(runargs));
- /* wrong callid */
- info.request.params.obj[0].data.params.obj[0].data.uinteger = 0;
+ handle_run(con->id, 1234, con->cc.pluginkeystring, runrequest, &error);
+ api_free_array(runrequest);
+
+ expect_check(__wrap_crypto_write, &deserialized, validate_result_request, NULL);
- assert_int_not_equal(0, handle_result(info.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error));
- assert_true(info.api_error.isset);
- assert_true(info.api_error.type == API_ERROR_TYPE_VALIDATION);
- info.api_error.isset = false;
+ request = api_result_valid(plugin);
+ handle_result(con->id, 1234, con->cc.pluginkeystring, request, &error);
+ assert_false(error.isset);
+ api_free_array(request);
+ /*
+ * The following asserts verify, that the handle_result method cancels
+ * as soon as illegitim result calls are processed. A API_ERROR must be
+ * set in order inform the caller later on.
+ */
- info.request.params.obj[0].data.params.obj[0].data.uinteger = plugin->callid;
+ /* size of payload too small */
+ request = api_result_wrong_args_size_small(plugin);
+ handle_result(con->id, 1234, con->cc.pluginkeystring, request, &error);
+ assert_true(error.isset);
+ assert_true(error.type == API_ERROR_TYPE_VALIDATION);
+ error.isset = false;
+ api_free_array(request);
- /* wrong argument type */
- helper_request_set_args_size(&info.request,
- OBJECT_TYPE_STR, 2);
+ /* size of payload too big */
+ request = api_result_wrong_args_size_big(plugin);
+ handle_result(con->id, 1234, con->cc.pluginkeystring, request, &error);
+ assert_true(error.isset);
+ assert_true(error.type == API_ERROR_TYPE_VALIDATION);
+ error.isset = false;
+ api_free_array(request);
- assert_int_not_equal(0, handle_result(info.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error));
- assert_true(info.api_error.isset);
- assert_true(info.api_error.type == API_ERROR_TYPE_VALIDATION);
- info.api_error.isset = false;
+ /* wrong meta size */
+ request = api_result_wrong_meta_size(plugin);
+ handle_result(con->id, 1234, con->cc.pluginkeystring, request, &error);
+ assert_true(error.isset);
+ assert_true(error.type == API_ERROR_TYPE_VALIDATION);
+ error.isset = false;
+ api_free_array(request);
- helper_request_set_args_size(&info.request,
- OBJECT_TYPE_ARRAY, 2);
+ /* wrong meta type */
+ request = api_result_wrong_meta_type(plugin);
+ handle_result(con->id, 1234, con->cc.pluginkeystring, request, &error);
+ assert_true(error.isset);
+ assert_true(error.type == API_ERROR_TYPE_VALIDATION);
+ error.isset = false;
+ api_free_array(request);
+ /* wrong callid type */
+ request = api_result_wrong_meta_callid_type(plugin);
+ handle_result(con->id, 1234, con->cc.pluginkeystring, request, &error);
+ assert_true(error.isset);
+ assert_true(error.type == API_ERROR_TYPE_VALIDATION);
+ error.isset = false;
+ api_free_array(request);
- free_params(info.request.params);
helper_free_plugin(plugin);
connection_teardown();
- FREE(info.con);
+ FREE(con);
db_close();
}
diff --git a/test/functional/dispatch-handle-run.c b/test/functional/dispatch-handle-run.c
index 48f1d11..27ba977 100644
--- a/test/functional/dispatch-handle-run.c
+++ b/test/functional/dispatch-handle-run.c
@@ -17,132 +17,206 @@
#include
#include "sb-common.h"
-#include "rpc/msgpack/sb-msgpack-rpc.h"
#include "rpc/sb-rpc.h"
+#include "rpc/connection/connection.h"
+#include "api/helpers.h"
+#include "api/sb-api.h"
+#ifdef __linux__
+#include
+#endif
#include "helper-unix.h"
#include "helper-all.h"
#include "helper-validate.h"
-static struct plugin *prepare_test(connection_request_event_info *info)
+static array api_run_valid(struct plugin *plugin)
{
- struct api_error err = ERROR_INIT;
- info->api_error = err;
+ array meta = ARRAY_DICT_INIT;
+ ADD(meta, STRING_OBJ(cstring_copy_string(plugin->key.str)));
+ ADD(meta, OBJECT_OBJ((object) OBJECT_INIT));
- /* create test plugin that is used for the tests */
- struct plugin *plugin = helper_get_example_plugin();
- helper_register_plugin(plugin);
+ array args = ARRAY_DICT_INIT;
+ ADD(args, STRING_OBJ(cstring_copy_string(plugin->function->args[0].str)));
+ ADD(args, STRING_OBJ(cstring_copy_string(plugin->function->args[1].str)));
+
+ array request = ARRAY_DICT_INIT;
+ ADD(request, ARRAY_OBJ(meta));
+ ADD(request, STRING_OBJ(cstring_copy_string(plugin->function->name.str)));
+ ADD(request, ARRAY_OBJ(args));
+
+ return request;
+}
+
+static array api_run_call_not_registered_function(struct plugin *plugin)
+{
+ array meta = ARRAY_DICT_INIT;
+ ADD(meta, STRING_OBJ(cstring_copy_string(plugin->key.str)));
+ ADD(meta, OBJECT_OBJ((object) OBJECT_INIT));
+
+ array args = ARRAY_DICT_INIT;
+ ADD(args, STRING_OBJ(cstring_copy_string(plugin->function->args[0].str)));
+ ADD(args, STRING_OBJ(cstring_copy_string(plugin->function->args[1].str)));
+
+ array request = ARRAY_DICT_INIT;
+ ADD(request, ARRAY_OBJ(meta));
+ ADD(request, STRING_OBJ(cstring_copy_string("wrong")));
+ ADD(request, ARRAY_OBJ(args));
+
+ return request;
+}
+
+static array api_run_wrong_meta_type(struct plugin *plugin)
+{
+ array args = ARRAY_DICT_INIT;
+ ADD(args, STRING_OBJ(cstring_copy_string(plugin->function->args[0].str)));
+ ADD(args, STRING_OBJ(cstring_copy_string(plugin->function->args[1].str)));
+
+ array request = ARRAY_DICT_INIT;
+ ADD(request, STRING_OBJ(cstring_copy_string("wrong")));
+ ADD(request, STRING_OBJ(cstring_copy_string(plugin->function->name.str)));
+ ADD(request, ARRAY_OBJ(args));
+
+ return request;
+}
+
+static array api_run_wrong_meta_size(struct plugin *plugin)
+{
+ array meta = ARRAY_DICT_INIT;
+ ADD(meta, STRING_OBJ(cstring_copy_string(plugin->key.str)));
+
+ array args = ARRAY_DICT_INIT;
+ ADD(args, STRING_OBJ(cstring_copy_string(plugin->function->args[0].str)));
+ ADD(args, STRING_OBJ(cstring_copy_string(plugin->function->args[1].str)));
+
+ array request = ARRAY_DICT_INIT;
+ ADD(request, ARRAY_OBJ(meta));
+ ADD(request, STRING_OBJ(cstring_copy_string(plugin->function->name.str)));
+ ADD(request, ARRAY_OBJ(args));
+
+ return request;
+}
+
+static array api_run_wrong_pluginkey_type(struct plugin *plugin)
+{
+ array meta = ARRAY_DICT_INIT;
+ ADD(meta, OBJECT_OBJ((object) OBJECT_INIT));
+ ADD(meta, OBJECT_OBJ((object) OBJECT_INIT));
+
+ array args = ARRAY_DICT_INIT;
+ ADD(args, STRING_OBJ(cstring_copy_string(plugin->function->args[0].str)));
+ ADD(args, STRING_OBJ(cstring_copy_string(plugin->function->args[1].str)));
- /* establish fake connection to plugin */
- info->con = CALLOC(1, struct connection);
- info->con->closed = true;
- info->con->id = 12345;
- connection_hashmap_put(info->con->id, info->con);
- strlcpy(info->con->cc.pluginkeystring, plugin->key.str, plugin->key.length+1);
- assert_non_null(info->con);
+ array request = ARRAY_DICT_INIT;
+ ADD(request, ARRAY_OBJ(meta));
+ ADD(request, STRING_OBJ(cstring_copy_string(plugin->function->name.str)));
+ ADD(request, ARRAY_OBJ(args));
- return plugin;
+ return request;
}
void functional_dispatch_handle_run(UNUSED(void **state))
{
- connection_request_event_info info;
- struct plugin *plugin;
+ struct plugin *plugin = helper_get_example_plugin();
+ struct connection *con;
+ struct api_error error = ERROR_INIT;
+ array request;
- plugin = prepare_test(&info);
+ con = calloc_or_die(1, sizeof(struct connection));
+ con->closed = true;
+ con->id = (uint64_t) randommod(281474976710656LL);
+ con->msgid = 4321;
- expect_check(__wrap_crypto_write, &deserialized, validate_run_request, plugin);
- expect_check(__wrap_crypto_write, &deserialized, validate_run_response, plugin);
+ assert_non_null(con);
- /*
- * The following asserts verifies a legitim run call. In detail, it
- * verifies, that the forwarded run request by the server is of proper
- * format.
- * */
- helper_build_run_request(&info.request, plugin
- ,OBJECT_TYPE_ARRAY /* meta array type */
- ,2 /* meta size */
- ,OBJECT_TYPE_STR /* target plugin key */
- ,OBJECT_TYPE_NIL /* call id type */
- ,OBJECT_TYPE_STR /* function name */
- ,OBJECT_TYPE_ARRAY /* arguments */
- );
-
- //nfo.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error
-
- assert_int_equal(0, handle_run(info.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error));
- assert_false(info.api_error.isset);
+ strlcpy(con->cc.pluginkeystring, plugin->key.str, plugin->key.length+1);
- /*
- * The following asserts verify, that the handle_run method cancels
- * as soon as illegitim run calls are processed. A API_ERROR must be
- * set in order inform the caller later on.
- */
+ array registermeta = ARRAY_DICT_INIT;
+ ADD(registermeta, STRING_OBJ(cstring_copy_string(plugin->name.str)));
+ ADD(registermeta, STRING_OBJ(cstring_copy_string(plugin->description.str)));
+ ADD(registermeta, STRING_OBJ(cstring_copy_string(plugin->author.str)));
+ ADD(registermeta, STRING_OBJ(cstring_copy_string(plugin->license.str)));
- /* calling not registered function should fail */
- helper_request_set_function_name(&info.request, OBJECT_TYPE_STR,
- "invalid funcion name");
+ array registerarguments = ARRAY_DICT_INIT;
+ ADD(registerarguments, STRING_OBJ(cstring_copy_string(plugin->function->args[0].str)));
+ ADD(registerarguments, STRING_OBJ(cstring_copy_string(plugin->function->args[1].str)));
- assert_int_not_equal(0, handle_run(info.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error));
- assert_true(info.api_error.isset);
- assert_true(info.api_error.type == API_ERROR_TYPE_VALIDATION);
- info.api_error.isset = false;
+ array registerfunc1 = ARRAY_DICT_INIT;
+ ADD(registerfunc1, STRING_OBJ(cstring_copy_string(plugin->function->name.str)));
+ ADD(registerfunc1, STRING_OBJ(cstring_copy_string(plugin->function->description.str)));
+ ADD(registerfunc1, ARRAY_OBJ(registerarguments));
- /* reset to correct request */
- helper_request_set_function_name(&info.request, OBJECT_TYPE_STR,
- plugin->function->name.str);
+ array registerfunctions = ARRAY_DICT_INIT;
+ ADD(registerfunctions, ARRAY_OBJ(registerfunc1));
- /* meta object has wrong type */
- helper_request_set_meta_size(&info.request, OBJECT_TYPE_STR, 2);
+ array registerrequest = ARRAY_DICT_INIT;
+ ADD(registerrequest, ARRAY_OBJ(registermeta));
+ ADD(registerrequest, ARRAY_OBJ(registerfunctions));
- assert_false(info.api_error.isset);
- assert_int_not_equal(0, handle_run(info.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error));
- assert_true(info.api_error.isset);
- assert_true(info.api_error.type == API_ERROR_TYPE_VALIDATION);
- info.api_error.isset = false;
+ connect_to_db();
+ assert_int_equal(0, connection_init());
- /* reset to correct request */
- helper_request_set_meta_size(&info.request, OBJECT_TYPE_ARRAY, 2);
+ error.isset = false;
- /* meta has wrong size */
- helper_request_set_meta_size(&info.request, OBJECT_TYPE_ARRAY, 3);
+ con->refcount++;
- assert_false(info.api_error.isset);
- assert_int_not_equal(0, handle_run(info.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error));
- assert_true(info.api_error.isset);
- assert_true(info.api_error.type == API_ERROR_TYPE_VALIDATION);
- info.api_error.isset = false;
+ connection_hashmap_put(con->id, con);
+ pluginkeys_hashmap_put(con->cc.pluginkeystring, con->id);
- /* reset to correct request */
- helper_request_set_meta_size(&info.request, OBJECT_TYPE_ARRAY, 2);
+ handle_register(con->id, 123, con->cc.pluginkeystring, registerrequest,
+ &error);
+ assert_false(error.isset);
+ api_free_array(registerrequest);
- /* target plugin key has wrong type */
- helper_request_set_pluginkey_type(&info.request, OBJECT_TYPE_ARRAY, plugin->key.str);
+ expect_check(__wrap_crypto_write, &deserialized, validate_run_request, plugin);
- assert_false(info.api_error.isset);
- assert_int_not_equal(0, handle_run(info.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error));
- assert_true(info.api_error.isset);
- assert_true(info.api_error.type == API_ERROR_TYPE_VALIDATION);
- info.api_error.isset = false;
+ request = api_run_valid(plugin);
+ handle_run(con->id, 123, con->cc.pluginkeystring, request, &error);
+ assert_false(error.isset);
+ api_free_array(request);
- /* reset to correct request */
- helper_request_set_pluginkey_type(&info.request, OBJECT_TYPE_STR, plugin->key.str);
- array *meta = &info.request.params.obj[0].data.params;
- free_string(meta->obj[0].data.string);
+ /*
+ * The following asserts verify, that the handle_run method cancels
+ * as soon as illegitim run calls are processed. A API_ERROR must be
+ * set in order inform the caller later on.
+ */
- /* call_id has wrong type */
- helper_request_set_callid(&info.request, OBJECT_TYPE_BIN);
+ /* calling not registered function should fail */
+ request = api_run_call_not_registered_function(plugin);
+ handle_run(con->id, 123, con->cc.pluginkeystring, request, &error);
+ assert_true(error.isset);
+ assert_true(error.type == API_ERROR_TYPE_VALIDATION);
+ error.isset = false;
+ api_free_array(request);
- assert_false(info.api_error.isset);
- assert_int_not_equal(0, handle_run(info.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error));
- assert_true(info.api_error.isset);
- assert_true(info.api_error.type == API_ERROR_TYPE_VALIDATION);
+ /* meta object has wrong type */
+ request = api_run_wrong_meta_type(plugin);
+ assert_false(error.isset);
+ handle_run(con->id, 123, con->cc.pluginkeystring, request, &error);
+ assert_true(error.isset);
+ assert_true(error.type == API_ERROR_TYPE_VALIDATION);
+ error.isset = false;
+ api_free_array(request);
- /* reset to correct request */
- helper_request_set_callid(&info.request, OBJECT_TYPE_NIL);
+ /* meta has wrong size */
+ request = api_run_wrong_meta_size(plugin);
+ assert_false(error.isset);
+ handle_run(con->id, 123, con->cc.pluginkeystring, request, &error);
+ assert_true(error.isset);
+ assert_true(error.type == API_ERROR_TYPE_VALIDATION);
+ error.isset = false;
+ api_free_array(request);
+
+ /* target plugin key has wrong type */
+ request = api_run_wrong_pluginkey_type(plugin);
+ assert_false(error.isset);
+ handle_run(con->id, 123, con->cc.pluginkeystring, request, &error);
+ assert_true(error.isset);
+ assert_true(error.type == API_ERROR_TYPE_VALIDATION);
+ error.isset = false;
+ api_free_array(request);
- free_params(info.request.params);
helper_free_plugin(plugin);
connection_teardown();
+ FREE(con);
db_close();
}
diff --git a/test/functional/dispatch-handle-subscribe.c b/test/functional/dispatch-handle-subscribe.c
new file mode 100644
index 0000000..254dfa3
--- /dev/null
+++ b/test/functional/dispatch-handle-subscribe.c
@@ -0,0 +1,151 @@
+/**
+ * Copyright (C) 2015 splone UG
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+
+#include "sb-common.h"
+#include "rpc/sb-rpc.h"
+#include "rpc/connection/connection.h"
+#include "api/helpers.h"
+#include "api/sb-api.h"
+#ifdef __linux__
+#include
+#endif
+
+#include "helper-unix.h"
+#include "helper-all.h"
+#include "helper-validate.h"
+
+static array api_subscribe_valid(void)
+{
+ array request = ARRAY_DICT_INIT;
+ ADD(request, STRING_OBJ(cstring_copy_string("testname")));
+
+ return request;
+}
+
+static array api_subscribe_wrong_args_size(void)
+{
+ array request = ARRAY_DICT_INIT;
+ ADD(request, STRING_OBJ(cstring_copy_string("testname")));
+ ADD(request, STRING_OBJ(cstring_copy_string("wrong")));
+
+ return request;
+}
+
+static array api_subscribe_wrong_args_type(void)
+{
+ array request = ARRAY_DICT_INIT;
+ ADD(request, OBJECT_OBJ((object) OBJECT_INIT));
+
+ return request;
+}
+
+void functional_dispatch_handle_subscribe(UNUSED(void **state))
+{
+ struct plugin *plugin = helper_get_example_plugin();
+ struct connection *con1;
+ struct connection *con2;
+ struct api_error error = ERROR_INIT;
+ array request;
+
+ con1 = calloc_or_die(1, sizeof(struct connection));
+ con1->id = (uint64_t) randommod(281474976710656LL);
+ con1->msgid = 4321;
+ con1->subscribed_events = hashmap_new(cstr_t, ptr_t)();
+
+ con2 = calloc_or_die(1, sizeof(struct connection));
+ con2->id = (uint64_t) randommod(281474976710656LL);
+ con2->msgid = 4321;
+ con2->subscribed_events = hashmap_new(cstr_t, ptr_t)();
+
+ assert_non_null(con1);
+ assert_non_null(con2);
+
+ strlcpy(con1->cc.pluginkeystring, "ABCD", sizeof("ABCD") - 1);
+ strlcpy(con2->cc.pluginkeystring, "EFGH", sizeof("EFGH") - 1);
+
+ connect_to_db();
+ assert_int_equal(0, connection_init());
+
+ con1->refcount++;
+ con2->refcount++;
+
+ connection_hashmap_put(con1->id, con1);
+ connection_hashmap_put(con2->id, con2);
+ pluginkeys_hashmap_put(con1->cc.pluginkeystring, con1->id);
+ pluginkeys_hashmap_put(con2->cc.pluginkeystring, con2->id);
+
+
+
+ //expect_check(__wrap_crypto_write, &deserialized, validate_run_request, plugin);
+
+ request = api_subscribe_valid();
+ handle_subscribe(con1->id, 123, con1->cc.pluginkeystring, request, &error);
+ assert_false(error.isset);
+ api_free_array(request);
+
+ request = api_subscribe_valid();
+ handle_subscribe(con2->id, 123, con2->cc.pluginkeystring, request, &error);
+ assert_false(error.isset);
+ api_free_array(request);
+
+ request = api_subscribe_valid();
+ handle_unsubscribe(con1->id, 123, con1->cc.pluginkeystring, request, &error);
+ assert_false(error.isset);
+ api_free_array(request);
+
+ request = api_subscribe_valid();
+ handle_unsubscribe(con2->id, 123, con2->cc.pluginkeystring, request, &error);
+ assert_false(error.isset);
+ api_free_array(request);
+
+ request = api_subscribe_wrong_args_size();
+ handle_subscribe(con1->id, 123, con1->cc.pluginkeystring, request, &error);
+ assert_true(error.isset);
+ error.isset = false;
+ api_free_array(request);
+
+ request = api_subscribe_wrong_args_type();
+ handle_subscribe(con1->id, 123, con1->cc.pluginkeystring, request, &error);
+ assert_true(error.isset);
+ error.isset = false;
+ api_free_array(request);
+
+ request = api_subscribe_wrong_args_size();
+ handle_unsubscribe(con1->id, 123, con1->cc.pluginkeystring, request, &error);
+ assert_true(error.isset);
+ error.isset = false;
+ api_free_array(request);
+
+ request = api_subscribe_wrong_args_type();
+ handle_unsubscribe(con1->id, 123, con1->cc.pluginkeystring, request, &error);
+ assert_true(error.isset);
+ error.isset = false;
+ api_free_array(request);
+
+ con1->closed = true;
+ con2->closed = true;
+
+ hashmap_free(cstr_t, ptr_t)(con1->subscribed_events);
+ hashmap_free(cstr_t, ptr_t)(con2->subscribed_events);
+
+ helper_free_plugin(plugin);
+ connection_teardown();
+ FREE(con1);
+ FREE(con2);
+ db_close();
+}
diff --git a/test/functional/msgpack-rpc-helper.c b/test/functional/msgpack-rpc-helper.c
new file mode 100644
index 0000000..7ef67e3
--- /dev/null
+++ b/test/functional/msgpack-rpc-helper.c
@@ -0,0 +1,174 @@
+/**
+ * Copyright (C) 2015 splone UG
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+#include "helper-unix.h"
+#include "sb-common.h"
+#include "api/helpers.h"
+#include "rpc/msgpack/helpers.h"
+
+static object helper_valid_object_all_type(void)
+{
+ array test = ARRAY_DICT_INIT;
+
+ dictionary dict_test = ARRAY_DICT_INIT;
+
+ dictionary dict_abc = ARRAY_DICT_INIT;
+ PUT(dict_abc, "123", INTEGER_OBJ(123));
+ PUT(dict_abc, "456", STRING_OBJ(cstring_copy_string("456")));
+
+ dictionary dict_def = ARRAY_DICT_INIT;
+ PUT(dict_def, "789", INTEGER_OBJ(789));
+ PUT(dict_def, "987", STRING_OBJ(cstring_copy_string("987")));
+
+ dictionary dict_ghi = ARRAY_DICT_INIT;
+ PUT(dict_ghi, "654", INTEGER_OBJ(654));
+ PUT(dict_ghi, "321", STRING_OBJ(cstring_copy_string("321")));
+
+ PUT(dict_test, "abc", DICTIONARY_OBJ(dict_abc));
+ PUT(dict_test, "def", DICTIONARY_OBJ(dict_def));
+ PUT(dict_test, "ghi", DICTIONARY_OBJ(dict_ghi));
+
+ array array_abc = ARRAY_DICT_INIT;
+ ADD(array_abc, OBJECT_OBJ((object) OBJECT_INIT));
+ ADD(array_abc, FLOATING_OBJ(1.2345));
+ ADD(array_abc, UINTEGER_OBJ(1234));
+ ADD(array_abc, INTEGER_OBJ(-1234));
+
+ array array_def = ARRAY_DICT_INIT;
+ ADD(array_def, STRING_OBJ(cstring_copy_string("test")));
+ ADD(array_def, BOOLEAN_OBJ(true));
+ ADD(array_def, BOOLEAN_OBJ(false));
+
+ ADD(test, ARRAY_OBJ(array_abc));
+ ADD(test, ARRAY_OBJ(array_def));
+ ADD(test, DICTIONARY_OBJ(dict_test));
+
+ return ARRAY_OBJ(test);
+}
+
+static object helper_valid_notification_object(void)
+{
+ array notification = ARRAY_DICT_INIT;
+
+ array args = ARRAY_DICT_INIT;
+ ADD(args, STRING_OBJ(cstring_copy_string("test")));
+ ADD(args, BOOLEAN_OBJ(true));
+ ADD(args, BOOLEAN_OBJ(false));
+
+ ADD(notification, UINTEGER_OBJ(2));
+ ADD(notification, STRING_OBJ(cstring_copy_string("test")));
+ ADD(notification, ARRAY_OBJ(args));
+
+ return ARRAY_OBJ(notification);
+}
+
+static object helper_valid_request_object(void)
+{
+ array request = ARRAY_DICT_INIT;
+
+ array args = ARRAY_DICT_INIT;
+ ADD(args, STRING_OBJ(cstring_copy_string("test")));
+ ADD(args, BOOLEAN_OBJ(true));
+ ADD(args, BOOLEAN_OBJ(false));
+
+ ADD(request, UINTEGER_OBJ(0));
+ ADD(request, UINTEGER_OBJ(1234));
+ ADD(request, STRING_OBJ(cstring_copy_string("test")));
+ ADD(request, ARRAY_OBJ(args));
+
+ return ARRAY_OBJ(request);
+}
+
+
+void functional_msgpack_rpc_helper(UNUSED(void **state))
+{
+ struct api_error err = ERROR_INIT;
+ msgpack_sbuffer sbuf;
+ msgpack_packer pk;
+
+ msgpack_sbuffer_init(&sbuf);
+ msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
+
+ // declare test object with all available types
+
+ object serialize_object = helper_valid_object_all_type();
+ msgpack_rpc_from_object(serialize_object, &pk);
+
+ msgpack_zone mempool;
+ msgpack_zone_init(&mempool, 2048);
+
+ msgpack_object deserialized;
+ msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized);
+ msgpack_sbuffer_clear(&sbuf);
+
+ array deserialized_array;
+ assert_true(msgpack_rpc_to_array(&deserialized, &deserialized_array));
+
+ object deserialized_object;
+ assert_true(msgpack_rpc_to_object(&deserialized, &deserialized_object));
+
+ // test response serialize without error set
+ msgpack_rpc_serialize_response(1234, &err, deserialized_object, &pk);
+ msgpack_sbuffer_clear(&sbuf);
+
+ // test response serialize with error set
+ error_set(&err, API_ERROR_TYPE_EXCEPTION, "Error Error Error");
+ msgpack_rpc_serialize_response(1234, &err, deserialized_object, &pk);
+ msgpack_sbuffer_clear(&sbuf);
+
+ // notification helper tests
+ err.isset = false;
+ object notification_object = helper_valid_notification_object();
+ msgpack_rpc_from_object(notification_object, &pk);
+ msgpack_object deserialized_not;
+ msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized_not);
+ assert_true(msgpack_rpc_is_notification(&deserialized_not));
+ assert_null(msgpack_rpc_msg_id(&deserialized_not));
+ uint64_t msgid_not = 1234;
+ msgpack_rpc_validate(&msgid_not, &deserialized_not, &err);
+ assert_false(err.isset);
+ msgpack_object *deserialized_not_args = msgpack_rpc_args(&deserialized_not);
+ assert_non_null(deserialized_not_args);
+ msgpack_object *deserialized_not_method = msgpack_rpc_method(&deserialized_not);
+ assert_non_null(deserialized_not_method);
+ msgpack_sbuffer_clear(&sbuf);
+
+ // notification helper tests
+ err.isset = false;
+ object request_object = helper_valid_request_object();
+ msgpack_rpc_from_object(request_object, &pk);
+ msgpack_object deserialized_req;
+ msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized_req);
+ assert_false(msgpack_rpc_is_notification(&deserialized_req));
+ assert_non_null(msgpack_rpc_msg_id(&deserialized_req));
+ uint64_t msgid_req = 1234;
+ msgpack_rpc_validate(&msgid_req, &deserialized_req, &err);
+ assert_false(err.isset);
+ msgpack_object *deserialized_req_args = msgpack_rpc_args(&deserialized_req);
+ assert_non_null(deserialized_req_args);
+ msgpack_object *deserialized_req_method = msgpack_rpc_method(&deserialized_req);
+ assert_non_null(deserialized_req_method);
+
+ msgpack_sbuffer_clear(&sbuf);
+
+ api_free_object(serialize_object);
+ api_free_array(deserialized_array);
+ api_free_object(deserialized_object);
+ api_free_object(notification_object);
+ api_free_object(request_object);
+ msgpack_zone_destroy(&mempool);
+ msgpack_sbuffer_destroy(&sbuf);
+}
diff --git a/test/helper-all.c b/test/helper-all.c
index ac3152c..a2dcd65 100644
--- a/test/helper-all.c
+++ b/test/helper-all.c
@@ -18,6 +18,8 @@
#include "sb-common.h"
#include "rpc/db/sb-db.h"
+#include "api/helpers.h"
+#include "api/sb-api.h"
#include "helper-unix.h"
#include "helper-all.h"
@@ -69,33 +71,30 @@ void connect_and_create(char *pluginkey)
struct plugin *helper_get_example_plugin(void)
{
- struct plugin *p = MALLOC(struct plugin);
- struct function *f = MALLOC(struct function);
-
- if (!p || !f)
- LOG_ERROR("[test] Failed to alloc mem for example plugin.\n");
+ struct plugin *p = malloc_or_die(sizeof(struct plugin));
+ struct function *f = malloc_or_die(sizeof(struct function));
p->key = (string) {.str = "0123456789ABCDEF",
- .length = sizeof("0123456789ABCDEF") - 1};
+ .length = sizeof("0123456789ABCDEF") - 1};
p->name = (string) {.str = "plugin name",
- .length = sizeof("plugin name") - 1};
+ .length = sizeof("plugin name") - 1};
p->description = (string) {.str = "plugin desc",
- .length = sizeof("plugin desc") - 1};
+ .length = sizeof("plugin desc") - 1};
p->license = (string) {.str = "plugin license",
- .length = sizeof("plugin license") - 1};
+ .length = sizeof("plugin license") - 1};
p->author = (string) {.str = "plugin author",
- .length = sizeof("plugin author") - 1};
+ .length = sizeof("plugin author") - 1};
p->callid = 0;
f->name = (string) {.str = "function name",
- .length = sizeof("function name") - 1};
+ .length = sizeof("function name") - 1};
f->description = (string) {.str = "function desc",
- .length = sizeof("function desc") - 1};
+ .length = sizeof("function desc") - 1};
f->args[0] = (string) {.str = "arg 1",
- .length = sizeof("arg 1") - 1};
+ .length = sizeof("arg 1") - 1};
f->args[1] = (string) {.str = "arg 2",
- .length = sizeof("arg 2") - 1};
+ .length = sizeof("arg 2") - 1};
p->function = f;
return p;
@@ -106,221 +105,3 @@ void helper_free_plugin(struct plugin *p)
FREE(p->function);
FREE(p);
}
-
-
-/* Builds a register call and executes handle_register in order
- * to register the plugin. */
-void helper_register_plugin(struct plugin *p)
-{
- connection_request_event_info info;
- struct message_request *register_request;
- array *meta, *functions, *func1, *args;
- struct api_error err = ERROR_INIT;
-
- info.request.msgid = 1;
- register_request = &info.request;
- assert_non_null(register_request);
-
- info.api_error = err;
-
- info.con = CALLOC(1, struct connection);
- info.con->closed = true;
- info.con->msgid = 1;
- info.con->id = (uint64_t) randommod(281474976710656LL);
-
- assert_non_null(info.con);
-
- strlcpy(info.con->cc.pluginkeystring, p->key.str, p->key.length+1);
-
- connect_to_db();
- assert_int_equal(0, connection_init());
-
- /* first level arrays:
- *
- * [meta] args->obj[0]
- * [functions] args->obj[1]
- */
- register_request->params.size = 2;
- register_request->params.obj = CALLOC(2, struct message_object);
- register_request->params.obj[0].type = OBJECT_TYPE_ARRAY;
- register_request->params.obj[1].type = OBJECT_TYPE_ARRAY;
-
- /* meta array:
- *
- * [name]
- * [desciption]
- * [author]
- * [license]
- */
- meta = ®ister_request->params.obj[0].data.params;
- meta->size = 4;
- meta->obj = CALLOC(meta->size, struct message_object);
- meta->obj[0].type = OBJECT_TYPE_STR;
- meta->obj[0].data.string = cstring_copy_string(p->name.str);
- meta->obj[1].type = OBJECT_TYPE_STR;
- meta->obj[1].data.string = cstring_copy_string(p->description.str);
- meta->obj[2].type = OBJECT_TYPE_STR;
- meta->obj[2].data.string = cstring_copy_string(p->author.str);
- meta->obj[3].type = OBJECT_TYPE_STR;
- meta->obj[3].data.string = cstring_copy_string(p->license.str);
-
- functions = ®ister_request->params.obj[1].data.params;
- functions->size = 1;
- functions->obj = CALLOC(functions->size, struct message_object);
- functions->obj[0].type = OBJECT_TYPE_ARRAY;
-
- func1 = &functions->obj[0].data.params;
- func1->size = 3;
- func1->obj = CALLOC(3, struct message_object);
- func1->obj[0].type = OBJECT_TYPE_STR;
- func1->obj[0].data.string = cstring_copy_string(p->function->name.str);
- func1->obj[1].type = OBJECT_TYPE_STR;
- func1->obj[1].data.string = cstring_copy_string(p->function->description.str);
- func1->obj[2].type = OBJECT_TYPE_ARRAY;
-
- /* function arguments */
- args = &func1->obj[2].data.params;
- args->size = 2;
- args->obj = CALLOC(2, struct message_object);
- args->obj[0].type = OBJECT_TYPE_STR;
- args->obj[0].data.string = cstring_copy_string(p->function->args[0].str);
- args->obj[1].type = OBJECT_TYPE_STR;
- args->obj[1].data.string = cstring_copy_string(p->function->args[1].str);
-
- /* before running function, it must be registered successfully */
- info.api_error.isset = false;
- expect_check(__wrap_crypto_write, &deserialized,
- validate_register_response,
- NULL);
-
- info.con->refcount++;
-
- connection_hashmap_put(info.con->id, info.con);
- pluginkeys_hashmap_put(info.con->cc.pluginkeystring, info.con->id);
-
- assert_int_equal(0, handle_register(info.con->id, &info.request,
- info.con->cc.pluginkeystring, &info.api_error));
- assert_false(info.api_error.isset);
-
- //hashmap_put(uint64_t, ptr_t)(connections, info.con->id, info.con);
- free_params(register_request->params);
- //FREE(args->obj);
- //FREE(func1->obj);
- //FREE(functions->obj);
- //FREE(meta->obj);
- //FREE(register_request->params.obj);
-}
-
-/* Builds a run request */
-void helper_build_run_request(struct message_request *rr,
- struct plugin *plugin,
- message_object_type metaarraytype,
- size_t metasize,
- message_object_type metaclientidtype,
- message_object_type metacallidtype,
- message_object_type functionnametype,
- message_object_type argstype
-)
-{
- array argsarray = ARRAY_INIT;
- array *meta;
-
- rr->msgid = 1;
-
- rr->params.size = 3;
- rr->params.obj = CALLOC(rr->params.size, struct message_object);
- rr->params.obj[0].type = metaarraytype;
-
- meta = &rr->params.obj[0].data.params;
- meta->size = metasize;
- meta->obj = CALLOC(meta->size, struct message_object);
- meta->obj[0].type = metaclientidtype;
- meta->obj[0].data.string = cstring_copy_string(plugin->key.str);
- meta->obj[1].type = metacallidtype;
-
- rr->params.obj[1].type = functionnametype;
- rr->params.obj[1].data.string = cstring_copy_string(plugin->function->name.str);
-
- argsarray.size = 2;
- argsarray.obj = CALLOC(argsarray.size, struct message_object);
- argsarray.obj[0].type = OBJECT_TYPE_STR; /* first argument */
- argsarray.obj[0].data.string = cstring_copy_string(plugin->function->args[0].str);
- argsarray.obj[1].type = OBJECT_TYPE_STR; /* second argument */
- argsarray.obj[1].data.string = cstring_copy_string(plugin->function->args[1].str);
-
- rr->params.obj[2].type = argstype;
- rr->params.obj[2].data.params = argsarray;
-}
-
-
-void helper_build_result_request(struct message_request *rr,
- struct plugin *plugin,
- message_object_type metaarraytype,
- size_t metasize,
- message_object_type metacallidtype,
- message_object_type argstype)
-{
- array argsarray = ARRAY_INIT;
- array *meta;
-
- rr->msgid = 1;
-
- rr->params.size = 2;
- rr->params.obj = CALLOC(rr->params.size, struct message_object);
- rr->params.obj[0].type = metaarraytype;
- meta = &rr->params.obj[0].data.params;
- meta->size = metasize;
- meta->obj = CALLOC(meta->size, struct message_object);
- meta->obj[0].type = metacallidtype;
- meta->obj[0].data.uinteger = plugin->callid;
-
- argsarray.size = 2;
- argsarray.obj = CALLOC(argsarray.size, struct message_object);
- argsarray.obj[0].type = OBJECT_TYPE_STR; /* first argument */
- argsarray.obj[0].data.string = cstring_copy_string(plugin->function->args[0].str);
- argsarray.obj[1].type = OBJECT_TYPE_STR; /* second argument */
- argsarray.obj[1].data.string = cstring_copy_string(plugin->function->args[1].str);
-
- rr->params.obj[1].type = argstype;
- rr->params.obj[1].data.params = argsarray;
-}
-
-void helper_request_set_callid(struct message_request *rr,
- message_object_type callidtype)
-{
- array *meta = &rr->params.obj[0].data.params;
-
- meta->obj[0].type = callidtype;
-}
-
-void helper_request_set_meta_size(struct message_request *rr,
- message_object_type metatype, uint64_t metasize)
-{
- rr->params.obj[0].type = metatype;
- rr->params.obj[0].data.params.size = metasize;
-}
-
-void helper_request_set_args_size(struct message_request *rr,
- message_object_type argstype, uint64_t argssize)
-{
- rr->params.obj[1].type = argstype;
- rr->params.obj[1].data.params.size = argssize;
-}
-
-void helper_request_set_function_name(struct message_request *rr,
- message_object_type nametype, char *name)
-{
- free_string(rr->params.obj[1].data.string);
- rr->params.obj[1].type = nametype;
- rr->params.obj[1].data.string = cstring_copy_string(name);
-}
-
-void helper_request_set_pluginkey_type(struct message_request *rr,
- message_object_type pluginkeytype, char *pluginkey)
-{
- array *meta = &rr->params.obj[0].data.params;
-
- free_string(meta->obj[0].data.string);
- meta->obj[0].data.string = cstring_copy_string(pluginkey);
- meta->obj[0].type = pluginkeytype;
-}
diff --git a/test/helper-all.h b/test/helper-all.h
index 9974492..1931b9f 100644
--- a/test/helper-all.h
+++ b/test/helper-all.h
@@ -44,33 +44,3 @@ void register_test_function(void);
struct plugin *helper_get_example_plugin(void);
void helper_free_plugin(struct plugin *p);
void helper_register_plugin(struct plugin *p);
-
-/* some helper functions to build and manipulate requests */
-void helper_request_set_args_size(struct message_request *rr,
- message_object_type argstype, uint64_t argssize);
-void helper_request_set_meta_size(struct message_request *rr,
- message_object_type metatype, uint64_t metasize);
-void helper_request_set_callid(struct message_request *rr,
- message_object_type callidtype);
-void helper_request_set_pluginkey_type(struct message_request *rr,
- message_object_type pluginkeytype, char *pluginkey);
-void helper_request_set_function_name(struct message_request *rr,
- message_object_type nametype, char *name);
-
-
-void helper_build_result_request(struct message_request *rr,
- struct plugin *plugin,
- message_object_type metaarraytype,
- size_t metasize,
- message_object_type metacallidtype,
- message_object_type argstype);
-void helper_build_run_request(struct message_request *rr,
- struct plugin *plugin,
- message_object_type metaarraytype,
- size_t metasize,
- message_object_type metaclientidtype,
- message_object_type metacallidtype,
- message_object_type functionnametype,
- message_object_type argstype);
-
-
diff --git a/test/helper-validate.c b/test/helper-validate.c
index aac08e5..9fc7ce8 100644
--- a/test/helper-validate.c
+++ b/test/helper-validate.c
@@ -15,244 +15,130 @@
*/
#include "rpc/db/sb-db.h"
-#include "rpc/msgpack/sb-msgpack-rpc.h"
+#include "api/helpers.h"
+#include "rpc/msgpack/helpers.h"
#include "helper-unix.h"
#include "helper-all.h"
-int validate_register_response(const unsigned long data1,
- UNUSED(const unsigned long data2))
-{
- struct msgpack_object *deserialized = (struct msgpack_object *) data1;
- struct message_object request;
- array params;
-
- wrap_crypto_write = true;
-
- assert_int_equal(0, unpack_params(deserialized, ¶ms));
-
- /* msgpack request needs to be 1 */
- assert_true(params.obj[0].type == OBJECT_TYPE_UINT);
- assert_int_equal(1, params.obj[0].data.uinteger);
-
- /* msg id which is random */
- assert_true(params.obj[1].type == OBJECT_TYPE_UINT);
-
- /* next field is NIL */
- assert_true(params.obj[2].type == OBJECT_TYPE_NIL);
-
- /* followed by an empty array */
- request = params.obj[3];
- assert_true(request.type == OBJECT_TYPE_ARRAY);
- assert_int_equal(0, request.data.params.size);
-
- free_params(params);
-
- return (1);
-}
int validate_run_request(const unsigned long data1,
UNUSED(const unsigned long data2))
{
struct msgpack_object *deserialized = (struct msgpack_object *) data1;
struct plugin *p = (struct plugin *) data2;
- struct message_object meta, request, func, args;
- array params;
- assert_int_equal(0, unpack_params(deserialized, ¶ms));
+ array message;
+ object request, meta, func, args;
+
+ msgpack_rpc_to_array(deserialized, &message);
/* msgpack request needs to be 0 */
- assert_true(params.obj[0].type == OBJECT_TYPE_UINT);
- assert_int_equal(0, params.obj[0].data.uinteger);
+ assert_true(message.items[0].type == OBJECT_TYPE_UINT);
+ assert_int_equal(0, message.items[0].data.uinteger);
/* msg id which is random */
- assert_true(params.obj[1].type == OBJECT_TYPE_UINT);
+ assert_true(message.items[1].type == OBJECT_TYPE_UINT);
/* run call */
- assert_true(params.obj[2].type == OBJECT_TYPE_STR);
- assert_string_equal(params.obj[2].data.string.str, "run");
+ assert_true(message.items[2].type == OBJECT_TYPE_STR);
+ assert_string_equal(message.items[2].data.string.str, "run");
/* first level arrays:
*
* [meta] args->obj[0]
* [functions] args->obj[1]
*/
- request = params.obj[3];
+ request = message.items[3];
assert_true(request.type == OBJECT_TYPE_ARRAY);
- assert_int_equal(3, request.data.params.size);
+ assert_int_equal(3, request.data.array.size);
- meta = request.data.params.obj[0];
+ meta = request.data.array.items[0];
assert_true(meta.type == OBJECT_TYPE_ARRAY);
- assert_int_equal(2, meta.data.params.size);
+ assert_int_equal(2, meta.data.array.size);
/* client2 id should be nil */
- assert_true(meta.data.params.obj[0].type == OBJECT_TYPE_NIL);
+ assert_true(meta.data.array.items[0].type == OBJECT_TYPE_NIL);
/* verify that the server sent a proper callid */
- assert_true(meta.data.params.obj[1].type == OBJECT_TYPE_UINT);
+ assert_true(meta.data.array.items[1].type == OBJECT_TYPE_UINT);
/* since the callid must be forwarded to the client1, the original
* sender of the rpc call, we need to push the callid on the cmocka test
* stack. this allows verifying of it later on */
- uint64_t callid = meta.data.params.obj[1].data.uinteger;
- will_return(__wrap_loop_wait_for_response, OBJECT_TYPE_UINT);
- will_return(__wrap_loop_wait_for_response, callid);
- expect_value(validate_run_response, response.data.params.obj[0].data.uinteger, callid);
+ uint64_t callid = meta.data.array.items[1].data.uinteger;
+ will_return(__wrap_loop_process_events_until, OBJECT_TYPE_UINT);
+ will_return(__wrap_loop_process_events_until, callid);
+
p->callid = callid;
/* the function to call on client2 side */
- func = request.data.params.obj[1];
+ func = request.data.array.items[1];
assert_true(func.type == OBJECT_TYPE_STR);
assert_string_equal(func.data.string.str, p->function->name.str);
/* the function arguments to pass to client2 side */
- assert_true(request.data.params.obj[2].type == OBJECT_TYPE_ARRAY);
- assert_int_equal(2, request.data.params.obj[2].data.params.size);
+ assert_true(request.data.array.items[2].type == OBJECT_TYPE_ARRAY);
+ assert_int_equal(2, request.data.array.items[2].data.array.size);
- args = request.data.params.obj[2];
- assert_true(args.data.params.obj[0].type == OBJECT_TYPE_STR);
- assert_string_equal(args.data.params.obj[0].data.string.str, p->function->args[0].str);
+ args = request.data.array.items[2];
+ assert_true(args.data.array.items[0].type == OBJECT_TYPE_STR);
+ assert_string_equal(args.data.array.items[0].data.string.str, p->function->args[0].str);
- assert_true(args.data.params.obj[1].type == OBJECT_TYPE_STR);
- assert_string_equal(args.data.params.obj[1].data.string.str, p->function->args[1].str);
+ assert_true(args.data.array.items[1].type == OBJECT_TYPE_STR);
+ assert_string_equal(args.data.array.items[1].data.string.str, p->function->args[1].str);
- free_params(params);
+ api_free_array(message);
return (1);
}
-int validate_run_response(const unsigned long data1,
- UNUSED(const unsigned long data2))
-{
- struct msgpack_object *deserialized = (struct msgpack_object *) data1;
- struct message_object response;
- struct plugin *p = (struct plugin *) data2;
- array params;
-
- wrap_crypto_write = true;
-
- assert_int_equal(0, unpack_params(deserialized, ¶ms));
-
- /* msgpack response needs to be 1 */
- assert_true(params.obj[0].type == OBJECT_TYPE_UINT);
- assert_int_equal(1, params.obj[0].data.uinteger);
-
- /* msg id which is random */
- assert_true(params.obj[1].type == OBJECT_TYPE_UINT);
-
- /* method should be NIL */
- assert_true(params.obj[2].type == OBJECT_TYPE_NIL);
-
- /* first level arrays:
- *
- * [meta] args->obj[0]
- */
- response = params.obj[3];
- assert_true(response.type == OBJECT_TYPE_ARRAY);
- assert_int_equal(1, response.data.params.size);
-
- /* the server must send a call id, store the callid for further
- * validation */
- assert_true(response.data.params.obj[0].type == OBJECT_TYPE_UINT);
- check_expected(response.data.params.obj[0].data.uinteger);
- //TODO validate callid against plugin->callid
- if (p) {
- assert_true(response.data.params.obj[0].data.uinteger == p->callid);
- }
-
- free_params(params);
-
- return (1);
-}
-
int validate_result_request(const unsigned long data1,
UNUSED(const unsigned long data2))
{
struct msgpack_object *deserialized = (struct msgpack_object *) data1;
- struct message_object meta, request;
- array params;
+ array message;
+ object meta, request;
- assert_int_equal(0, unpack_params(deserialized, ¶ms));
+ msgpack_rpc_to_array(deserialized, &message);
/* msgpack request needs to be 0 */
- assert_true(params.obj[0].type == OBJECT_TYPE_UINT);
- assert_int_equal(0, params.obj[0].data.uinteger);
+ assert_true(message.items[0].type == OBJECT_TYPE_UINT);
+ assert_int_equal(0, message.items[0].data.uinteger);
/* msg id which is random */
- assert_true(params.obj[1].type == OBJECT_TYPE_UINT);
+ assert_true(message.items[1].type == OBJECT_TYPE_UINT);
/* run call */
- assert_true(params.obj[2].type == OBJECT_TYPE_STR);
- assert_string_equal(params.obj[2].data.string.str, "result");
+ assert_true(message.items[2].type == OBJECT_TYPE_STR);
+ assert_string_equal(message.items[2].data.string.str, "result");
/* first level arrays:
*
* [meta] args->obj[0]
* [functions] args->obj[1]
*/
- request = params.obj[3];
+ request = message.items[3];
assert_true(request.type == OBJECT_TYPE_ARRAY);
- assert_int_equal(2, request.data.params.size);
+ assert_int_equal(2, request.data.array.size);
- meta = request.data.params.obj[0];
+ meta = request.data.array.items[0];
assert_true(meta.type == OBJECT_TYPE_ARRAY);
- assert_int_equal(1, meta.data.params.size);
+ assert_int_equal(1, meta.data.array.size);
/* client2 id should be nil */
- assert_true(meta.data.params.obj[0].type == OBJECT_TYPE_UINT);
+ assert_true(meta.data.array.items[0].type == OBJECT_TYPE_UINT);
/* since the callid must be forwarded to the client1, the original
* sender of the rpc call, we need to push the callid on the cmocka test
* stack. this allows verifying of it later on */
- uint64_t callid = meta.data.params.obj[0].data.uinteger;
+ uint64_t callid = meta.data.array.items[0].data.uinteger;
- will_return(__wrap_loop_wait_for_response, OBJECT_TYPE_UINT);
- will_return(__wrap_loop_wait_for_response, callid);
- expect_value(validate_result_response, response.data.params.obj[0].data.uinteger, callid);
+ will_return(__wrap_loop_process_events_until, OBJECT_TYPE_UINT);
+ will_return(__wrap_loop_process_events_until, callid);
- free_params(params);
+ api_free_array(message);
return (1);
}
-
-int validate_result_response(const unsigned long data1,
- UNUSED(const unsigned long data2))
-{
- struct msgpack_object *deserialized = (struct msgpack_object *) data1;
- struct message_object response;
- array params;
-
- wrap_crypto_write = true;
-
- assert_int_equal(0, unpack_params(deserialized, ¶ms));
-
- /* msgpack response needs to be 1 */
- assert_true(params.obj[0].type == OBJECT_TYPE_UINT);
- assert_int_equal(1, params.obj[0].data.uinteger);
-
- /* msg id which is random */
- assert_true(params.obj[1].type == OBJECT_TYPE_UINT);
-
- /* method should be NIL */
- assert_true(params.obj[2].type == OBJECT_TYPE_NIL);
-
- /* first level arrays:
- *
- * [meta] args->obj[0]
- */
- response = params.obj[3];
- assert_true(response.type == OBJECT_TYPE_ARRAY);
- assert_int_equal(1, response.data.params.size);
-
- /* the server must send a call id, store the callid for further
- * validation */
- assert_true(response.data.params.obj[0].type == OBJECT_TYPE_UINT);
- check_expected(response.data.params.obj[0].data.uinteger);
-
- free_params(params);
-
- return (1);
-}
-
-
diff --git a/test/main.c b/test/main.c
index 75895c6..c406bf0 100644
--- a/test/main.c
+++ b/test/main.c
@@ -17,8 +17,10 @@
#include "test-list.h"
#include "helper-unix.h"
#include "sb-common.h"
+#include "main.h"
int8_t verbose_level;
+loop main_loop;
int main(UNUSED(int argc), UNUSED(char **argv))
{
diff --git a/test/test-list.h b/test/test-list.h
index 28c9532..a8408f4 100644
--- a/test/test-list.h
+++ b/test/test-list.h
@@ -20,28 +20,7 @@
void unit_server_start(void **state);
void unit_server_stop(void **state);
-void unit_pack_string(void **state);
-void unit_pack_int(void **state);
-void unit_pack_uint(void **state);
-void unit_pack_array(void **state);
-void unit_pack_nil(void **state);
-void unit_pack_float(void **state);
-void unit_pack_bool(void **state);
-void unit_unpack_string(void **state);
-void unit_unpack_uint(void **state);
-void unit_unpack_array(void **state);
void unit_dispatch_table_get(void **state);
-void unit_event_queue_put(void **state);
-void unit_event_queue_get(void **state);
-void unit_regression_issue_60(void **state);
-void unit_message_deserialize_request(void **state);
-void unit_message_deserialize_response(void **state);
-void unit_message_deserialize_error_response(void **state);
-void unit_message_serialize_request(void **state);
-void unit_message_serialize_response(void **state);
-void unit_message_serialize_error_response(void **state);
-void unit_message_is_request(void **state);
-void unit_message_is_response(void **state);
void functional_client_connect(void **state);
void functional_db_connect(void **state);
@@ -55,6 +34,9 @@ void functional_filesystem_save_sync(void **state);
void functional_dispatch_handle_register(void **state);
void functional_dispatch_handle_run(void **state);
void functional_dispatch_handle_result(void **state);
+void functional_dispatch_handle_subscribe(void **state);
+void functional_dispatch_handle_broadcast(void **state);
+void functional_msgpack_rpc_helper(void **state);
void functional_crypto(void **state);
void functional_confparse(void **state);
void functional_db_whitelist(void **state);
@@ -63,23 +45,6 @@ const struct CMUnitTest tests[] = {
cmocka_unit_test(unit_dispatch_table_get),
cmocka_unit_test(unit_server_start),
cmocka_unit_test(unit_server_stop),
- cmocka_unit_test(unit_pack_string),
- cmocka_unit_test(unit_pack_int),
- cmocka_unit_test(unit_pack_uint),
- cmocka_unit_test(unit_pack_nil),
- cmocka_unit_test(unit_pack_float),
- cmocka_unit_test(unit_pack_bool),
- cmocka_unit_test(unit_pack_array),
- cmocka_unit_test(unit_regression_issue_60),
- cmocka_unit_test(unit_event_queue_put),
- cmocka_unit_test(unit_message_deserialize_request),
- cmocka_unit_test(unit_message_deserialize_response),
- cmocka_unit_test(unit_message_deserialize_error_response),
- cmocka_unit_test(unit_message_serialize_request),
- cmocka_unit_test(unit_message_serialize_response),
- cmocka_unit_test(unit_message_serialize_error_response),
- cmocka_unit_test(unit_message_is_request),
- cmocka_unit_test(unit_message_is_response),
cmocka_unit_test(functional_db_connect),
cmocka_unit_test(functional_db_plugin_add),
cmocka_unit_test(functional_db_pluginkey_verify),
@@ -91,6 +56,9 @@ const struct CMUnitTest tests[] = {
cmocka_unit_test(functional_dispatch_handle_register),
cmocka_unit_test(functional_dispatch_handle_run),
cmocka_unit_test(functional_dispatch_handle_result),
+ cmocka_unit_test(functional_dispatch_handle_subscribe),
+ cmocka_unit_test(functional_dispatch_handle_broadcast),
+ cmocka_unit_test(functional_msgpack_rpc_helper),
cmocka_unit_test(functional_crypto),
cmocka_unit_test(functional_confparse),
cmocka_unit_test(functional_db_whitelist),
diff --git a/test/unit/event-queue-get.c b/test/unit/event-queue-get.c
deleted file mode 100644
index f697075..0000000
--- a/test/unit/event-queue-get.c
+++ /dev/null
@@ -1,39 +0,0 @@
-/**
- * Copyright (C) 2015 splone UG
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-#include "sb-common.h"
-#include "rpc/sb-rpc.h"
-#include "helper-unix.h"
-
-equeue *equeue_root;
-equeue *equeue_child_one;
-static api_event events;
-
-void unit_event_queue_get(UNUSED(void **state))
-{
- equeue_root = equeue_new(NULL);
- assert_non_null(equeue_root);
- equeue_child_one = equeue_new(equeue_root);
- assert_non_null(equeue_child_one);
-
- assert_int_not_equal(0, equeue_put(equeue_child_one, events));
- assert_false(TAILQ_EMPTY(&equeue_root->head));
- equeue_get(equeue_child_one);
- assert_true(TAILQ_EMPTY(&equeue_root->head));
-
- equeue_free(equeue_root);
- equeue_free(equeue_child_one);
-}
diff --git a/test/unit/event-queue-put.c b/test/unit/event-queue-put.c
deleted file mode 100644
index 5218f70..0000000
--- a/test/unit/event-queue-put.c
+++ /dev/null
@@ -1,43 +0,0 @@
-/**
- * Copyright (C) 2015 splone UG
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-#include "sb-common.h"
-#include "rpc/sb-rpc.h"
-#include "helper-unix.h"
-
-
-equeue *equeue_root;
-equeue *equeue_child_one;
-equeue *equeue_child_two;
-static api_event events;
-
-void unit_event_queue_put(UNUSED(void **state))
-{
- equeue_root = equeue_new(NULL);
- assert_non_null(equeue_root);
- equeue_child_one = equeue_new(equeue_root);
- assert_non_null(equeue_child_one);
- equeue_child_two = equeue_new(equeue_root);
- assert_non_null(equeue_child_two);
-
- assert_int_not_equal(0, equeue_put(equeue_root, events));
- assert_int_not_equal(0, equeue_put(NULL, events));
- assert_int_equal(0, equeue_put(equeue_child_one, events));
-
- equeue_free(equeue_root);
- equeue_free(equeue_child_one);
- equeue_free(equeue_child_two);
-}
diff --git a/test/unit/message-deserialize-error-response.c b/test/unit/message-deserialize-error-response.c
deleted file mode 100644
index c86929e..0000000
--- a/test/unit/message-deserialize-error-response.c
+++ /dev/null
@@ -1,156 +0,0 @@
-/**
- * Copyright (C) 2016 splone UG
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-#include
-
-#include "sb-common.h"
-#include "rpc/msgpack/sb-msgpack-rpc.h"
-#include "helper-unix.h"
-
-
-void unit_message_deserialize_error_response(UNUSED(void **state))
-{
- struct api_error error = ERROR_INIT;
- msgpack_sbuffer sbuf;
- msgpack_packer pk;
- msgpack_zone mempool;
- msgpack_object deserialized;
- struct message_response response;
-
- msgpack_sbuffer_init(&sbuf);
- msgpack_zone_init(&mempool, 2048);
-
- /* positiv test */
- msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
- msgpack_pack_array(&pk, 4);
- msgpack_pack_uint8(&pk, 1);
- msgpack_pack_uint32(&pk, 1234);
- msgpack_pack_array(&pk, 1);
- msgpack_pack_uint8(&pk, 0);
- msgpack_pack_nil(&pk);
- msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized);
-
- assert_int_equal(0, message_deserialize_error_response(&response,
- &deserialized, &error));
-
- assert_true(message_is_error_response(&deserialized));
-
- msgpack_sbuffer_clear(&sbuf);
-
- free_params(response.params);
-
- /* wrong type type */
- msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
- msgpack_pack_array(&pk, 4);
- msgpack_pack_nil(&pk);
- msgpack_pack_uint32(&pk, 1234);
- msgpack_pack_array(&pk, 1);
- msgpack_pack_uint8(&pk, 0);
- msgpack_pack_nil(&pk);
- msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized);
-
- assert_int_not_equal(0, message_deserialize_error_response(&response,
- &deserialized, &error));
-
- msgpack_sbuffer_clear(&sbuf);
-
- /* wrong type */
- msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
- msgpack_pack_array(&pk, 4);
- msgpack_pack_uint8(&pk, 0);
- msgpack_pack_uint32(&pk, 1234);
- msgpack_pack_array(&pk, 1);
- msgpack_pack_uint8(&pk, 0);
- msgpack_pack_nil(&pk);
- msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized);
-
- assert_int_not_equal(0, message_deserialize_error_response(&response,
- &deserialized, &error));
-
- msgpack_sbuffer_clear(&sbuf);
-
- /* wrong msgid */
- msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
- msgpack_pack_array(&pk, 4);
- msgpack_pack_uint8(&pk, 1);
- msgpack_pack_int(&pk, -1234);
- msgpack_pack_array(&pk, 1);
- msgpack_pack_uint8(&pk, 0);
- msgpack_pack_nil(&pk);
- msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized);
-
- assert_int_not_equal(0, message_deserialize_error_response(&response,
- &deserialized, &error));
-
- msgpack_sbuffer_clear(&sbuf);
-
- /* wrong msgid value*/
- msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
- msgpack_pack_array(&pk, 4);
- msgpack_pack_uint8(&pk, 1);
- msgpack_pack_uint32(&pk, UINT32_MAX);
- msgpack_pack_array(&pk, 1);
- msgpack_pack_uint8(&pk, 0);
- msgpack_pack_nil(&pk);
- msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized);
-
- assert_int_not_equal(0, message_deserialize_error_response(&response,
- &deserialized, &error));
-
- msgpack_sbuffer_clear(&sbuf);
-
- /* wrong params */
- msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
- msgpack_pack_array(&pk, 4);
- msgpack_pack_uint8(&pk, 1);
- msgpack_pack_uint32(&pk, 1234);
- msgpack_pack_nil(&pk);
- msgpack_pack_nil(&pk);
- msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized);
-
- assert_int_not_equal(0, message_deserialize_error_response(&response,
- &deserialized, &error));
-
- msgpack_sbuffer_clear(&sbuf);
-
- /* wrong params */
- msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
- msgpack_pack_array(&pk, 4);
- msgpack_pack_uint8(&pk, 1);
- msgpack_pack_uint32(&pk, 1234);
- msgpack_pack_array(&pk, 1);
- msgpack_pack_uint8(&pk, 0);
- msgpack_pack_array(&pk, 1);
- msgpack_pack_uint8(&pk, 0);
- msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized);
-
- assert_int_not_equal(0, message_deserialize_error_response(&response,
- &deserialized, &error));
-
- msgpack_sbuffer_clear(&sbuf);
-
- /* null input params */
- /* null input params */
- assert_int_not_equal(0, message_deserialize_error_response(&response,
- &deserialized, NULL));
- assert_int_not_equal(0, message_deserialize_error_response(&response, NULL,
- &error));
- assert_int_not_equal(0, message_deserialize_error_response(NULL,
- &deserialized, &error));
-
- msgpack_zone_destroy(&mempool);
- msgpack_sbuffer_destroy(&sbuf);
-}
diff --git a/test/unit/message-deserialize-request.c b/test/unit/message-deserialize-request.c
deleted file mode 100644
index abe134c..0000000
--- a/test/unit/message-deserialize-request.c
+++ /dev/null
@@ -1,157 +0,0 @@
-/**
- * Copyright (C) 2016 splone UG
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-#include
-
-#include "sb-common.h"
-#include "rpc/msgpack/sb-msgpack-rpc.h"
-#include "helper-unix.h"
-
-
-void unit_message_deserialize_request(UNUSED(void **state))
-{
- struct api_error error = ERROR_INIT;
- msgpack_sbuffer sbuf;
- msgpack_packer pk;
- msgpack_zone mempool;
- msgpack_object deserialized;
- struct message_request request;
-
- msgpack_sbuffer_init(&sbuf);
- msgpack_zone_init(&mempool, 2048);
-
- /* positiv test */
- msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
- msgpack_pack_array(&pk, 4);
- msgpack_pack_uint8(&pk, 0);
- msgpack_pack_uint32(&pk, 1234);
- msgpack_pack_str(&pk, 4);
- msgpack_pack_str_body(&pk, "test", 4);
- msgpack_pack_array(&pk, 1);
- msgpack_pack_uint8(&pk, 0);
- msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized);
-
- assert_int_equal(0, message_deserialize_request(&request, &deserialized,
- &error));
-
- msgpack_sbuffer_clear(&sbuf);
-
- free_string(request.method);
- free_params(request.params);
-
- /* wrong type type*/
- msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
- msgpack_pack_array(&pk, 4);
- msgpack_pack_nil(&pk);
- msgpack_pack_uint32(&pk, 1234);
- msgpack_pack_str(&pk, 4);
- msgpack_pack_str_body(&pk, "test", 4);
- msgpack_pack_array(&pk, 1);
- msgpack_pack_uint8(&pk, 0);
- msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized);
-
- assert_int_not_equal(0, message_deserialize_request(&request, &deserialized,
- &error));
-
- /* wrong type */
- msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
- msgpack_pack_array(&pk, 4);
- msgpack_pack_uint8(&pk, 1);
- msgpack_pack_uint32(&pk, 1234);
- msgpack_pack_str(&pk, 4);
- msgpack_pack_str_body(&pk, "test", 4);
- msgpack_pack_array(&pk, 1);
- msgpack_pack_uint8(&pk, 0);
- msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized);
-
- assert_int_not_equal(0, message_deserialize_request(&request, &deserialized,
- &error));
-
- msgpack_sbuffer_clear(&sbuf);
-
- /* wrong msgid */
- msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
- msgpack_pack_array(&pk, 4);
- msgpack_pack_uint8(&pk, 0);
- msgpack_pack_int(&pk, -1234);
- msgpack_pack_str(&pk, 4);
- msgpack_pack_str_body(&pk, "test", 4);
- msgpack_pack_array(&pk, 1);
- msgpack_pack_uint8(&pk, 0);
- msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized);
-
- assert_int_not_equal(0, message_deserialize_request(&request, &deserialized,
- &error));
-
- msgpack_sbuffer_clear(&sbuf);
-
- /* wrong msgid value*/
- msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
- msgpack_pack_array(&pk, 4);
- msgpack_pack_uint8(&pk, 0);
- msgpack_pack_uint32(&pk, UINT32_MAX);
- msgpack_pack_str(&pk, 4);
- msgpack_pack_str_body(&pk, "test", 4);
- msgpack_pack_array(&pk, 1);
- msgpack_pack_uint8(&pk, 0);
- msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized);
-
- assert_int_not_equal(0, message_deserialize_request(&request, &deserialized,
- &error));
-
- msgpack_sbuffer_clear(&sbuf);
-
- /* wrong method */
- msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
- msgpack_pack_array(&pk, 4);
- msgpack_pack_uint8(&pk, 0);
- msgpack_pack_int(&pk, 1234);
- msgpack_pack_nil(&pk);
- msgpack_pack_array(&pk, 1);
- msgpack_pack_uint8(&pk, 0);
- msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized);
-
- assert_int_not_equal(0, message_deserialize_request(&request, &deserialized,
- &error));
-
- msgpack_sbuffer_clear(&sbuf);
-
- /* wrong params */
- msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
- msgpack_pack_array(&pk, 4);
- msgpack_pack_uint8(&pk, 0);
- msgpack_pack_int(&pk, 1234);
- msgpack_pack_str(&pk, 4);
- msgpack_pack_str_body(&pk, "test", 4);
- msgpack_pack_nil(&pk);
- msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized);
-
- assert_int_not_equal(0, message_deserialize_request(&request, &deserialized,
- &error));
-
- msgpack_sbuffer_clear(&sbuf);
-
- /* null input params */
- assert_int_not_equal(0, message_deserialize_request(&request, &deserialized,
- NULL));
- assert_int_not_equal(0, message_deserialize_request(&request, NULL,
- &error));
- assert_int_not_equal(0, message_deserialize_request(NULL, &deserialized,
- &error));
-
- msgpack_zone_destroy(&mempool);
- msgpack_sbuffer_destroy(&sbuf);
-}
diff --git a/test/unit/message-deserialize-response.c b/test/unit/message-deserialize-response.c
deleted file mode 100644
index 6dde481..0000000
--- a/test/unit/message-deserialize-response.c
+++ /dev/null
@@ -1,152 +0,0 @@
-/**
- * Copyright (C) 2016 splone UG
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-#include
-
-#include "sb-common.h"
-#include "rpc/msgpack/sb-msgpack-rpc.h"
-#include "helper-unix.h"
-
-
-void unit_message_deserialize_response(UNUSED(void **state))
-{
- struct api_error error = ERROR_INIT;
- msgpack_sbuffer sbuf;
- msgpack_packer pk;
- msgpack_zone mempool;
- msgpack_object deserialized;
- struct message_response response;
-
- msgpack_sbuffer_init(&sbuf);
- msgpack_zone_init(&mempool, 2048);
-
- /* positiv test */
- msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
- msgpack_pack_array(&pk, 4);
- msgpack_pack_uint8(&pk, 1);
- msgpack_pack_uint32(&pk, 1234);
- msgpack_pack_nil(&pk);
- msgpack_pack_array(&pk, 1);
- msgpack_pack_uint8(&pk, 0);
- msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized);
-
- assert_int_equal(0, message_deserialize_response(&response, &deserialized,
- &error));
-
- msgpack_sbuffer_clear(&sbuf);
-
- free_params(response.params);
-
- /* wrong type type */
- msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
- msgpack_pack_array(&pk, 4);
- msgpack_pack_nil(&pk);
- msgpack_pack_uint32(&pk, 1234);
- msgpack_pack_nil(&pk);
- msgpack_pack_array(&pk, 1);
- msgpack_pack_uint8(&pk, 0);
- msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized);
-
- assert_int_not_equal(0, message_deserialize_response(&response, &deserialized,
- &error));
-
- msgpack_sbuffer_clear(&sbuf);
-
- /* wrong type */
- msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
- msgpack_pack_array(&pk, 4);
- msgpack_pack_uint8(&pk, 0);
- msgpack_pack_uint32(&pk, 1234);
- msgpack_pack_nil(&pk);
- msgpack_pack_array(&pk, 1);
- msgpack_pack_uint8(&pk, 0);
- msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized);
-
- assert_int_not_equal(0, message_deserialize_response(&response, &deserialized,
- &error));
-
- msgpack_sbuffer_clear(&sbuf);
-
- /* wrong msgid */
- msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
- msgpack_pack_array(&pk, 4);
- msgpack_pack_uint8(&pk, 1);
- msgpack_pack_int(&pk, -1234);
- msgpack_pack_nil(&pk);
- msgpack_pack_array(&pk, 1);
- msgpack_pack_uint8(&pk, 0);
- msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized);
-
- assert_int_not_equal(0, message_deserialize_response(&response, &deserialized,
- &error));
-
- msgpack_sbuffer_clear(&sbuf);
-
- /* wrong msgid value*/
- msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
- msgpack_pack_array(&pk, 4);
- msgpack_pack_uint8(&pk, 1);
- msgpack_pack_uint32(&pk, UINT32_MAX);
- msgpack_pack_nil(&pk);
- msgpack_pack_array(&pk, 1);
- msgpack_pack_uint8(&pk, 0);
- msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized);
-
- assert_int_not_equal(0, message_deserialize_response(&response, &deserialized,
- &error));
-
- msgpack_sbuffer_clear(&sbuf);
-
- /* wrong nil */
- msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
- msgpack_pack_array(&pk, 4);
- msgpack_pack_uint8(&pk, 1);
- msgpack_pack_uint32(&pk, 1234);
- msgpack_pack_uint8(&pk, 1);
- msgpack_pack_array(&pk, 1);
- msgpack_pack_uint8(&pk, 0);
- msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized);
-
- assert_int_not_equal(0, message_deserialize_response(&response, &deserialized,
- &error));
-
- msgpack_sbuffer_clear(&sbuf);
-
- /* wrong params */
- msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
- msgpack_pack_array(&pk, 4);
- msgpack_pack_uint8(&pk, 1);
- msgpack_pack_uint32(&pk, 1234);
- msgpack_pack_nil(&pk);
- msgpack_pack_nil(&pk);
- msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized);
-
- assert_int_not_equal(0, message_deserialize_response(&response, &deserialized,
- &error));
-
- msgpack_sbuffer_clear(&sbuf);
-
- /* null input params */
- assert_int_not_equal(0, message_deserialize_response(&response, &deserialized,
- NULL));
- assert_int_not_equal(0, message_deserialize_response(&response, NULL,
- &error));
- assert_int_not_equal(0, message_deserialize_response(NULL, &deserialized,
- &error));
-
- msgpack_zone_destroy(&mempool);
- msgpack_sbuffer_destroy(&sbuf);
-}
diff --git a/test/unit/message-is-request.c b/test/unit/message-is-request.c
deleted file mode 100644
index 8760e24..0000000
--- a/test/unit/message-is-request.c
+++ /dev/null
@@ -1,76 +0,0 @@
-/**
- * Copyright (C) 2016 splone UG
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-#include
-
-#include "sb-common.h"
-#include "rpc/msgpack/sb-msgpack-rpc.h"
-#include "helper-unix.h"
-
-
-void unit_message_is_request(UNUSED(void **state))
-{
- msgpack_sbuffer sbuf;
- msgpack_packer pk;
- msgpack_zone mempool;
- msgpack_object deserialized;
-
- msgpack_sbuffer_init(&sbuf);
- msgpack_zone_init(&mempool, 2048);
-
- /* positiv test */
- msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
- msgpack_pack_array(&pk, 4);
- msgpack_pack_uint8(&pk, 0);
- msgpack_pack_uint8(&pk, 0);
- msgpack_pack_uint8(&pk, 0);
- msgpack_pack_uint8(&pk, 0);
- msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized);
-
- assert_true(message_is_request(&deserialized));
-
- msgpack_sbuffer_clear(&sbuf);
-
- /* wrong type test */
- msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
- msgpack_pack_array(&pk, 4);
- msgpack_pack_uint8(&pk, 1);
- msgpack_pack_uint8(&pk, 1);
- msgpack_pack_uint8(&pk, 1);
- msgpack_pack_uint8(&pk, 1);
- msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized);
-
- assert_false(message_is_request(&deserialized));
-
- msgpack_sbuffer_clear(&sbuf);
-
- /* no array test */
- msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
- msgpack_pack_uint8(&pk, 1);
- msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized);
-
- assert_false(message_is_request(&deserialized));
-
- msgpack_sbuffer_clear(&sbuf);
-
- /* NULL test */
- assert_false(message_is_request(NULL));
-
- msgpack_sbuffer_clear(&sbuf);
-
- msgpack_zone_destroy(&mempool);
- msgpack_sbuffer_destroy(&sbuf);
-}
diff --git a/test/unit/message-is-response.c b/test/unit/message-is-response.c
deleted file mode 100644
index 5ad9402..0000000
--- a/test/unit/message-is-response.c
+++ /dev/null
@@ -1,76 +0,0 @@
-/**
- * Copyright (C) 2016 splone UG
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-#include
-
-#include "sb-common.h"
-#include "rpc/msgpack/sb-msgpack-rpc.h"
-#include "helper-unix.h"
-
-
-void unit_message_is_response(UNUSED(void **state))
-{
- msgpack_sbuffer sbuf;
- msgpack_packer pk;
- msgpack_zone mempool;
- msgpack_object deserialized;
-
- msgpack_sbuffer_init(&sbuf);
- msgpack_zone_init(&mempool, 2048);
-
- /* positiv test */
- msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
- msgpack_pack_array(&pk, 4);
- msgpack_pack_uint8(&pk, 1);
- msgpack_pack_uint8(&pk, 0);
- msgpack_pack_uint8(&pk, 0);
- msgpack_pack_uint8(&pk, 0);
- msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized);
-
- assert_true(message_is_response(&deserialized));
-
- msgpack_sbuffer_clear(&sbuf);
-
- /* wrong type test */
- msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
- msgpack_pack_array(&pk, 4);
- msgpack_pack_uint8(&pk, 0);
- msgpack_pack_uint8(&pk, 1);
- msgpack_pack_uint8(&pk, 1);
- msgpack_pack_uint8(&pk, 1);
- msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized);
-
- assert_false(message_is_response(&deserialized));
-
- msgpack_sbuffer_clear(&sbuf);
-
- /* no array test */
- msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
- msgpack_pack_uint8(&pk, 1);
- msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized);
-
- assert_false(message_is_response(&deserialized));
-
- msgpack_sbuffer_clear(&sbuf);
-
- /* NULL test */
- assert_false(message_is_response(NULL));
-
- msgpack_sbuffer_clear(&sbuf);
-
- msgpack_zone_destroy(&mempool);
- msgpack_sbuffer_destroy(&sbuf);
-}
diff --git a/test/unit/message-serialize-error-response.c b/test/unit/message-serialize-error-response.c
deleted file mode 100644
index 6dace64..0000000
--- a/test/unit/message-serialize-error-response.c
+++ /dev/null
@@ -1,43 +0,0 @@
-/**
- * Copyright (C) 2016 splone UG
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-#include
-
-#include "sb-common.h"
-#include "rpc/msgpack/sb-msgpack-rpc.h"
-#include "helper-unix.h"
-
-
-void unit_message_serialize_error_response(UNUSED(void **state))
-{
- msgpack_sbuffer sbuf;
- struct api_error error = ERROR_INIT;
- msgpack_packer pk;
-
- error_set(&error, API_ERROR_TYPE_VALIDATION, "test error");
-
- msgpack_sbuffer_init(&sbuf);
-
- /* positiv test */
- msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
- assert_int_equal(0, message_serialize_error_response(&pk, &error, 1234));
- msgpack_sbuffer_clear(&sbuf);
-
- /* null check */
- assert_int_not_equal(0, message_serialize_error_response(NULL, NULL, 1234));
-
- msgpack_sbuffer_destroy(&sbuf);
-}
diff --git a/test/unit/message-serialize-request.c b/test/unit/message-serialize-request.c
deleted file mode 100644
index 371e32a..0000000
--- a/test/unit/message-serialize-request.c
+++ /dev/null
@@ -1,77 +0,0 @@
-/**
- * Copyright (C) 2016 splone UG
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-#include
-
-#include "sb-common.h"
-#include "rpc/msgpack/sb-msgpack-rpc.h"
-#include "helper-unix.h"
-
-
-void unit_message_serialize_request(UNUSED(void **state))
-{
- msgpack_sbuffer sbuf;
- msgpack_packer pk;
- struct message_request request;
- array params;
-
- params.size = 1;
- params.obj = CALLOC(1, struct message_object);
- params.obj[0].type = OBJECT_TYPE_UINT;
- params.obj[0].data.uinteger = 1234;
-
- msgpack_sbuffer_init(&sbuf);
-
- /* positiv test */
- msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
- request.msgid = 1234;
- request.method = (string) {.str = "test method",
- .length = sizeof("test method") - 1};
- request.params = params;
- assert_int_equal(0, message_serialize_request(&request, &pk));
- msgpack_sbuffer_clear(&sbuf);
-
- /* no valid string */
- msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
- request.msgid = 1234;
- request.method = (string) STRING_INIT;
- request.params = params;
- assert_int_not_equal(0, message_serialize_request(&request, &pk));
- msgpack_sbuffer_clear(&sbuf);
-
- free_params(request.params);
-
- params.size = 1;
- params.obj = CALLOC(1, struct message_object);
- params.obj[0].type = 1000;
- params.obj[0].data.uinteger = 1234;
-
- /* no valid params */
- msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
- request.msgid = 1234;
- request.method = (string) {.str = "test method",
- .length = sizeof("test method") - 1};
- request.params = params;
- assert_int_not_equal(0, message_serialize_request(&request, &pk));
- msgpack_sbuffer_clear(&sbuf);
-
- free_params(request.params);
-
- /* null check */
- assert_int_not_equal(0, message_serialize_request(NULL, NULL));
-
- msgpack_sbuffer_destroy(&sbuf);
-}
diff --git a/test/unit/message-serialize-response.c b/test/unit/message-serialize-response.c
deleted file mode 100644
index 8809e86..0000000
--- a/test/unit/message-serialize-response.c
+++ /dev/null
@@ -1,65 +0,0 @@
-/**
- * Copyright (C) 2016 splone UG
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-#include
-
-#include "sb-common.h"
-#include "rpc/msgpack/sb-msgpack-rpc.h"
-#include "helper-unix.h"
-
-
-void unit_message_serialize_response(UNUSED(void **state))
-{
- msgpack_sbuffer sbuf;
- msgpack_packer pk;
- struct message_response response;
- array params;
-
- params.size = 1;
- params.obj = CALLOC(1, struct message_object);
- params.obj[0].type = OBJECT_TYPE_UINT;
- params.obj[0].data.uinteger = 1234;
-
- msgpack_sbuffer_init(&sbuf);
-
- /* positiv test */
- msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
- response.msgid = 1234;
- response.params = params;
- assert_int_equal(0, message_serialize_response(&response, &pk));
- msgpack_sbuffer_clear(&sbuf);
-
- free_params(response.params);
-
- params.size = 1;
- params.obj = CALLOC(1, struct message_object);
- params.obj[0].type = 1000;
- params.obj[0].data.uinteger = 1234;
-
- /* no valid params */
- msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
- response.msgid = 1234;
- response.params = params;
- assert_int_not_equal(0, message_serialize_response(&response, &pk));
- msgpack_sbuffer_clear(&sbuf);
-
- free_params(response.params);
-
- /* null check */
- assert_int_not_equal(0, message_serialize_response(NULL, NULL));
-
- msgpack_sbuffer_destroy(&sbuf);
-}
diff --git a/test/unit/pack-array.c b/test/unit/pack-array.c
deleted file mode 100644
index 2bf5767..0000000
--- a/test/unit/pack-array.c
+++ /dev/null
@@ -1,61 +0,0 @@
-/**
- * Copyright (C) 2015 splone UG
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-#include
-
-#include "sb-common.h"
-#include "rpc/msgpack/sb-msgpack-rpc.h"
-#include "helper-unix.h"
-
-
-void unit_pack_array(UNUSED(void **state))
-{
- array params = ARRAY_INIT;
- msgpack_sbuffer sbuf;
- msgpack_packer pk;
-
- params.size = 5;
- params.obj = CALLOC(params.size, struct message_object);
-
- params.obj[0].type = OBJECT_TYPE_UINT;
- params.obj[0].data.uinteger = 5;
-
- params.obj[1].type = OBJECT_TYPE_INT;
- params.obj[1].data.uinteger = 5;
-
- params.obj[2].type = OBJECT_TYPE_ARRAY;
- params.obj[2].data.params.size = 1;
- params.obj[2].data.params.obj = CALLOC(params.obj[2].data.params.size,
- struct message_object);
- params.obj[2].data.params.obj[0].type = OBJECT_TYPE_INT;
- params.obj[2].data.params.obj[0].data.uinteger = 6;
-
- params.obj[3].type = OBJECT_TYPE_BOOL;
- params.obj[3].data.boolean = true;
-
- params.obj[4].type = OBJECT_TYPE_FLOAT;
- params.obj[4].data.floating = 1.2345;
-
- msgpack_sbuffer_init(&sbuf);
- msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
-
- assert_int_equal(0, pack_params(&pk, params));
- assert_int_not_equal(0, pack_params(NULL, params));
-
- msgpack_sbuffer_destroy(&sbuf);
-
- free_params(params);
-}
diff --git a/test/unit/pack-int.c b/test/unit/pack-int.c
deleted file mode 100644
index 86e98b2..0000000
--- a/test/unit/pack-int.c
+++ /dev/null
@@ -1,53 +0,0 @@
-/**
- * Copyright (C) 2015 splone UG
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-#include
-
-#include "sb-common.h"
-#include "rpc/msgpack/sb-msgpack-rpc.h"
-#include "helper-unix.h"
-
-
-void unit_pack_int(UNUSED(void **state))
-{
- msgpack_sbuffer sbuf;
- msgpack_packer pk;
-
- msgpack_sbuffer_init(&sbuf);
- msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
-
- assert_int_equal(0, pack_int8(&pk, 0));
- assert_int_equal(0, pack_int8(&pk, 1));
- assert_int_equal(0, pack_int8(&pk, 11));
- assert_int_not_equal(0, pack_int8(NULL, 11));
-
- assert_int_equal(0, pack_int16(&pk, 0));
- assert_int_equal(0, pack_int16(&pk, 1));
- assert_int_equal(0, pack_int16(&pk, 11));
- assert_int_not_equal(0, pack_int16(NULL, 11));
-
- assert_int_equal(0, pack_int32(&pk, 0));
- assert_int_equal(0, pack_int32(&pk, 1));
- assert_int_equal(0, pack_int32(&pk, 11));
- assert_int_not_equal(0, pack_int32(NULL, 11));
-
- assert_int_equal(0, pack_int64(&pk, 0));
- assert_int_equal(0, pack_int64(&pk, 1));
- assert_int_equal(0, pack_int64(&pk, 11));
- assert_int_not_equal(0, pack_int64(NULL, 11));
-
- msgpack_sbuffer_destroy(&sbuf);
-}
diff --git a/test/unit/pack-uint.c b/test/unit/pack-uint.c
deleted file mode 100644
index 2253f87..0000000
--- a/test/unit/pack-uint.c
+++ /dev/null
@@ -1,53 +0,0 @@
-/**
- * Copyright (C) 2015 splone UG
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-#include
-
-#include "sb-common.h"
-#include "rpc/msgpack/sb-msgpack-rpc.h"
-#include "helper-unix.h"
-
-
-void unit_pack_uint(UNUSED(void **state))
-{
- msgpack_sbuffer sbuf;
- msgpack_packer pk;
-
- msgpack_sbuffer_init(&sbuf);
- msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
-
- assert_int_equal(0, pack_uint8(&pk, 0));
- assert_int_equal(0, pack_uint8(&pk, 1));
- assert_int_equal(0, pack_uint8(&pk, 11));
- assert_int_not_equal(0, pack_uint8(NULL, 11));
-
- assert_int_equal(0, pack_uint16(&pk, 0));
- assert_int_equal(0, pack_uint16(&pk, 1));
- assert_int_equal(0, pack_uint16(&pk, 11));
- assert_int_not_equal(0, pack_uint16(NULL, 11));
-
- assert_int_equal(0, pack_uint32(&pk, 0));
- assert_int_equal(0, pack_uint32(&pk, 1));
- assert_int_equal(0, pack_uint32(&pk, 11));
- assert_int_not_equal(0, pack_uint32(NULL, 11));
-
- assert_int_equal(0, pack_uint64(&pk, 0));
- assert_int_equal(0, pack_uint64(&pk, 1));
- assert_int_equal(0, pack_uint64(&pk, 11));
- assert_int_not_equal(0, pack_uint64(NULL, 11));
-
- msgpack_sbuffer_destroy(&sbuf);
-}
diff --git a/test/unit/regression-issue-60.c b/test/unit/regression-issue-60.c
deleted file mode 100644
index a3ec4cd..0000000
--- a/test/unit/regression-issue-60.c
+++ /dev/null
@@ -1,47 +0,0 @@
-/**
- * Copyright (C) 2015 splone UG
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-#include
-
-#include "sb-common.h"
-#include "rpc/msgpack/sb-msgpack-rpc.h"
-#include "helper-unix.h"
-
-void unit_regression_issue_60(UNUSED(void **state))
-{
- array params;
- struct msgpack_object deserialized;
- msgpack_unpacked result;
- msgpack_sbuffer sbuf;
- msgpack_packer pk;
-
- size_t off = 0;
-
- msgpack_sbuffer_init(&sbuf);
- msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
-
- msgpack_pack_array(&pk, 0);
-
- msgpack_unpacked_init(&result);
- msgpack_unpack_next(&result, sbuf.data, sbuf.size, &off);
- msgpack_sbuffer_destroy(&sbuf);
-
- deserialized = result.data;
-
- assert_int_equal(0, unpack_params(&deserialized, ¶ms));
- free_params(params);
- msgpack_unpacked_destroy(&result);
-}
diff --git a/test/unit/server-start.c b/test/unit/server-start.c
index abbb227..544d279 100644
--- a/test/unit/server-start.c
+++ b/test/unit/server-start.c
@@ -17,12 +17,12 @@
#include
#include
+#include "helper-all.h"
#include "sb-common.h"
#include "rpc/sb-rpc.h"
#include "helper-unix.h"
#include "rpc/db/sb-db.h"
-
-uv_loop_t loop;
+#include "main.h"
void unit_server_start(UNUSED(void **state))
{
@@ -39,7 +39,7 @@ void unit_server_start(UNUSED(void **state))
srand((unsigned)time(NULL));
assert_int_equal(0, server_init());
- uv_loop_init(&loop);
+ loop_init(&main_loop, NULL);
snprintf(buf1, 19, "/tmp/splnbx-%d", rand() % (999999 + 1 - 100000) + 100000);
assert_int_equal(0, server_start_pipe(buf1));
@@ -58,8 +58,8 @@ void unit_server_start(UNUSED(void **state))
server_close();
- uv_run(&loop, UV_RUN_ONCE);
- uv_loop_close(&loop);
+ uv_run(&main_loop.uv, UV_RUN_ONCE);
+ uv_loop_close(&main_loop.uv);
db_close();
}
diff --git a/test/unit/server-stop.c b/test/unit/server-stop.c
index 53dbf6c..4dc5632 100644
--- a/test/unit/server-stop.c
+++ b/test/unit/server-stop.c
@@ -15,18 +15,18 @@
*/
#include "sb-common.h"
+#include "helper-all.h"
#include "rpc/sb-rpc.h"
#include "helper-unix.h"
#include "rpc/db/sb-db.h"
-
-uv_loop_t loop;
+#include "main.h"
void unit_server_stop(UNUSED(void **state))
{
boxaddr addr;
uint16_t port;
- uv_loop_init(&loop);
+ loop_init(&main_loop, NULL);
connect_to_db();
box_addr_port_lookup("127.0.0.1:11111", &addr, &port);
@@ -38,8 +38,8 @@ void unit_server_stop(UNUSED(void **state))
server_close();
- uv_run(&loop, UV_RUN_ONCE);
- uv_loop_close(&loop);
+ uv_run(&main_loop.uv, UV_RUN_ONCE);
+ uv_loop_close(&main_loop.uv);
db_close();
}
diff --git a/test/unit/unpack-array.c b/test/unit/unpack-array.c
deleted file mode 100644
index da2e2da..0000000
--- a/test/unit/unpack-array.c
+++ /dev/null
@@ -1,78 +0,0 @@
-/**
- * Copyright (C) 2015 splone UG
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-#include
-
-#include "sb-common.h"
-#include "rpc/msgpack/sb-msgpack-rpc.h"
-#include "helper-unix.h"
-
-
-static msgpack_object deserialized;
-
-void init_unpack_array(msgpack_object_type type)
-{
- size_t off = 0;
-
- msgpack_sbuffer sbuf;
- msgpack_sbuffer_init(&sbuf);
- msgpack_packer pk;
- msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
-
- msgpack_pack_array(&pk, 3);
- if (type == MSGPACK_OBJECT_NIL) {
- msgpack_pack_nil(&pk);
- } else if (type == MSGPACK_OBJECT_POSITIVE_INTEGER) {
- msgpack_pack_int(&pk, 1);
- } else if (type == MSGPACK_OBJECT_BOOLEAN) {
- msgpack_pack_true(&pk);
- } else if (type == MSGPACK_OBJECT_MAP) {
- msgpack_pack_map(&pk, 1);
- } else if (type == MSGPACK_OBJECT_ARRAY) {
- msgpack_pack_array(&pk, 3);
- msgpack_pack_int(&pk, 1);
- msgpack_pack_int(&pk, 1);
- msgpack_pack_int(&pk, 1);
- } else if (type == MSGPACK_OBJECT_FLOAT) {
- msgpack_pack_double(&pk, 1.2);
- }
- msgpack_pack_true(&pk);
- msgpack_pack_str(&pk, 7);
- msgpack_pack_str_body(&pk, "example", 7);
-
- msgpack_unpacked result;
-
- msgpack_unpacked_init(&result);
- msgpack_unpack_next(&result, sbuf.data, sbuf.size, &off);
- msgpack_sbuffer_destroy(&sbuf);
- deserialized = result.data;
-}
-
-void unit_unpack_array(UNUSED(void **state))
-{
- array params;
-
- init_unpack_array(MSGPACK_OBJECT_NIL);
- assert_int_equal(0, unpack_params(&deserialized, ¶ms));
- init_unpack_array(MSGPACK_OBJECT_ARRAY);
- assert_int_equal(0, unpack_params(&deserialized, ¶ms));
- init_unpack_array(MSGPACK_OBJECT_FLOAT);
- assert_int_equal(0, unpack_params(&deserialized, ¶ms));
- init_unpack_array(MSGPACK_OBJECT_MAP);
- assert_int_equal(0, unpack_params(&deserialized, ¶ms));
- init_unpack_array(MSGPACK_OBJECT_POSITIVE_INTEGER);
- assert_int_equal(0, unpack_params(&deserialized, ¶ms));
-}
diff --git a/test/unit/unpack-string.c b/test/unit/unpack-string.c
deleted file mode 100644
index ffe2c34..0000000
--- a/test/unit/unpack-string.c
+++ /dev/null
@@ -1,42 +0,0 @@
-/**
- * Copyright (C) 2015 splone UG
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-#include
-
-#include "sb-common.h"
-#include "rpc/msgpack/sb-msgpack-rpc.h"
-#include "helper-unix.h"
-
-
-void unit_unpack_string(UNUSED(void **state))
-{
- msgpack_sbuffer sbuf;
- msgpack_sbuffer_init(&sbuf);
- msgpack_packer pk;
- msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
-
- msgpack_pack_str(&pk, 64);
- msgpack_pack_str_body(
- &pk,
- "TeiwieDoowuiMeix6SooxieFievee2io3ohhu5uo5ughu8cieja4iu6chuirijae",
- 64);
-
- msgpack_object deserialized;
- msgpack_unpack(sbuf.data, sbuf.size, NULL, NULL, &deserialized);
- msgpack_sbuffer_destroy(&sbuf);
-
- assert_non_null(unpack_string(&deserialized).str);
-}
diff --git a/test/unit/unpack-uint.c b/test/unit/unpack-uint.c
deleted file mode 100644
index f9463b2..0000000
--- a/test/unit/unpack-uint.c
+++ /dev/null
@@ -1,45 +0,0 @@
-/**
- * Copyright (C) 2015 splone UG
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-#include
-
-#include "sb-common.h"
-#include "rpc/msgpack/sb-msgpack-rpc.h"
-#include "helper-unix.h"
-
-
-static msgpack_object deserialized;
-
-void init_unpack_uint(uint64_t type)
-{
- msgpack_sbuffer sbuf;
- msgpack_sbuffer_init(&sbuf);
- msgpack_packer pk;
- msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
-
- msgpack_pack_uint64(&pk, type);
-
- msgpack_unpack(sbuf.data, sbuf.size, NULL, NULL, &deserialized);
- msgpack_sbuffer_destroy(&sbuf);
-}
-
-void unit_unpack_uint(UNUSED(void **state))
-{
- init_unpack_uint(0);
- assert_int_equal(0, unpack_uint(&deserialized));
- init_unpack_uint(1);
- assert_int_equal(1, unpack_uint(&deserialized));
-}
diff --git a/test/wrapper-functions.c b/test/wrapper-functions.c
index 585f976..0d09863 100644
--- a/test/wrapper-functions.c
+++ b/test/wrapper-functions.c
@@ -2,6 +2,9 @@
#include
#include
#include "rpc/sb-rpc.h"
+#include "rpc/msgpack/helpers.h"
+#include "rpc/connection/loop.h"
+#include "api/helpers.h"
#include "helper-unix.h"
#include "helper-all.h"
@@ -42,7 +45,7 @@ int __wrap_outputstream_write(UNUSED(outputstream *ostream), char *buffer, size_
break;
case 'M':
/* message packet */
- assert_int_equal(0, validate_crypto_write(buffer, len));
+ assert_int_equal(0, validate_crypto_write((unsigned char*)buffer, len));
break;
default:
LOG_WARNING("Illegal identifier suffix.");
@@ -52,15 +55,19 @@ int __wrap_outputstream_write(UNUSED(outputstream *ostream), char *buffer, size_
return (0);
}
-void __wrap_loop_wait_for_response(UNUSED(struct connection *con),
- struct callinfo *cinfo)
+void __wrap_loop_process_events_until(UNUSED(loop *loop),
+ UNUSED(struct connection *con), struct callinfo *cinfo)
{
assert_non_null(cinfo);
/* The callid and the message_params type are determined by the unit test. */
- cinfo->response.params.size = 1;
- cinfo->response.params.obj = CALLOC(cinfo->response.params.size,
- struct message_object);
- cinfo->response.params.obj[0].type = (message_object_type)mock();
- cinfo->response.params.obj[0].data.uinteger = (uint64_t)mock();
+
+ cinfo->result = ARRAY_OBJ(((array) {
+ .size = 1,
+ .capacity = 1,
+ .items = calloc(1, sizeof(object)),
+ }));
+
+ cinfo->result.data.array.items[0].type = (object_type)mock();
+ cinfo->result.data.array.items[0].data.uinteger = (uint64_t)mock();
}
diff --git a/third-party/CMakeLists.txt b/third-party/CMakeLists.txt
new file mode 100644
index 0000000..3321498
--- /dev/null
+++ b/third-party/CMakeLists.txt
@@ -0,0 +1,108 @@
+# This is not meant to be included by the top-level.
+cmake_minimum_required (VERSION 2.8.7)
+project(SPLONEBOX_DEPS)
+
+# Point CMake at any custom modules we may ship
+list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
+
+# In Windows/MSVC CMAKE_BUILD_TYPE changes the paths/linking of the build
+# recipes (libuv, msgpack), make sure it is set
+if(NOT CMAKE_BUILD_TYPE)
+ set(CMAKE_BUILD_TYPE Release)
+endif()
+
+set(DEPS_INSTALL_DIR "${CMAKE_BINARY_DIR}/usr" CACHE PATH "Dependencies install directory.")
+set(DEPS_BIN_DIR "${DEPS_INSTALL_DIR}/bin" CACHE PATH "Dependencies binary install directory.")
+set(DEPS_LIB_DIR "${DEPS_INSTALL_DIR}/lib" CACHE PATH "Dependencies library install directory.")
+set(DEPS_BUILD_DIR "${CMAKE_BINARY_DIR}/build" CACHE PATH "Dependencies build directory.")
+set(DEPS_DOWNLOAD_DIR "${DEPS_BUILD_DIR}/downloads" CACHE PATH "Dependencies download directory.")
+
+option(USE_BUNDLED "Use bundled dependencies." ON)
+
+option(USE_BUNDLED_LIBUV "Use the bundled libuv." ${USE_BUNDLED})
+option(USE_BUNDLED_MSGPACK "Use the bundled msgpack." ${USE_BUNDLED})
+
+#XXX(tarruda): Lua is only used for debugging the functional test client, no
+# build it unless explicitly requested
+option(USE_BUNDLED_LUA "Use the bundled version of lua." OFF)
+
+if(USE_BUNDLED AND (NOT WIN32))
+ option(USE_BUNDLED_GPERF "Use the bundled version of gperf." ON)
+else()
+ option(USE_BUNDLED_GPERF "Use the bundled version of gperf." OFF)
+endif()
+
+option(USE_EXISTING_SRC_DIR "Skip download of deps sources in case of existing source directory." OFF)
+
+if(UNIX)
+ find_program(MAKE_PRG NAMES gmake make)
+ if(MAKE_PRG)
+ execute_process(
+ COMMAND "${MAKE_PRG}" --version
+ OUTPUT_VARIABLE MAKE_VERSION_INFO)
+ if(NOT "${OUTPUT_VARIABLE}" MATCHES ".*GNU.*")
+ unset(MAKE_PRG)
+ endif()
+ endif()
+ if(NOT MAKE_PRG)
+ message(FATAL_ERROR "GNU Make is required to build the dependencies.")
+ else()
+ message(STATUS "Found GNU Make at ${MAKE_PRG}")
+ endif()
+endif()
+
+# When using make, use the $(MAKE) variable to avoid warning about the job
+# server.
+if(CMAKE_GENERATOR MATCHES "Makefiles")
+ set(MAKE_PRG "$(MAKE)")
+endif()
+
+if(CMAKE_C_COMPILER_ARG1)
+ set(DEPS_C_COMPILER "${CMAKE_C_COMPILER} ${CMAKE_C_COMPILER_ARG1}")
+else()
+ set(DEPS_C_COMPILER "${CMAKE_C_COMPILER}")
+endif()
+
+# Cross compiling: use these for dependencies built for the
+# HOST system, when not crosscompiling these should be the
+# same as DEPS_*. Except when targeting Unix in which case
+# want all the dependencies to use the same compiler.
+if(CMAKE_CROSSCOMPILING AND NOT UNIX)
+ set(HOSTDEPS_INSTALL_DIR "${CMAKE_BINARY_DIR}/host")
+ set(HOSTDEPS_BIN_DIR "${HOSTDEPS_INSTALL_DIR}/bin")
+ set(HOSTDEPS_LIB_DIR "${HOSTDEPS_INSTALL_DIR}/lib")
+ set(HOSTDEPS_C_COMPILER "${HOST_C_COMPILER}")
+else()
+ set(HOSTDEPS_INSTALL_DIR "${DEPS_INSTALL_DIR}")
+ set(HOSTDEPS_BIN_DIR "${DEPS_BIN_DIR}")
+ set(HOSTDEPS_LIB_DIR "${DEPS_LIB_DIR}")
+ set(HOSTDEPS_C_COMPILER "${DEPS_C_COMPILER}")
+endif()
+
+include(ExternalProject)
+
+set(LIBUV_URL https://github.com/libuv/libuv/archive/v1.10.0.tar.gz)
+set(LIBUV_SHA256 50f4ed57d65af4ab634e2cbdd90c49213020e15b4d77d3631feb633cbba9239f)
+
+set(MSGPACK_URL https://github.com/msgpack/msgpack-c/archive/cpp-1.0.0.tar.gz)
+set(MSGPACK_SHA256 afda64ca445203bb7092372b822bae8b2539fdcebbfc3f753f393628c2bcfe7d)
+
+if(USE_BUNDLED_LIBUV)
+ include(BuildLibuv)
+endif()
+
+if(USE_BUNDLED_MSGPACK)
+ include(BuildMsgpack)
+endif()
+
+
+add_custom_target(clean-shared-libraries
+ COMMAND ${CMAKE_COMMAND}
+ -DREMOVE_FILE_GLOB=${DEPS_INSTALL_DIR}/lib/${CMAKE_SHARED_LIBRARY_PREFIX}*${CMAKE_SHARED_LIBRARY_SUFFIX}*
+ -P ${PROJECT_SOURCE_DIR}/cmake/RemoveFiles.cmake
+ DEPENDS ${THIRD_PARTY_DEPS}
+)
+
+add_custom_target(third-party ALL
+ COMMAND ${CMAKE_COMMAND} -E touch .third-party
+ DEPENDS clean-shared-libraries)
diff --git a/third-party/cmake/BuildLibuv.cmake b/third-party/cmake/BuildLibuv.cmake
new file mode 100644
index 0000000..5482f28
--- /dev/null
+++ b/third-party/cmake/BuildLibuv.cmake
@@ -0,0 +1,102 @@
+include(CMakeParseArguments)
+
+# BuildLibuv(TARGET targetname CONFIGURE_COMMAND ... BUILD_COMMAND ... INSTALL_COMMAND ...)
+# Reusable function to build libuv, wraps ExternalProject_Add.
+# Failing to pass a command argument will result in no command being run
+function(BuildLibuv)
+ cmake_parse_arguments(_libuv
+ "BUILD_IN_SOURCE"
+ "TARGET"
+ "CONFIGURE_COMMAND;BUILD_COMMAND;INSTALL_COMMAND"
+ ${ARGN})
+
+ if(NOT _libuv_CONFIGURE_COMMAND AND NOT _libuv_BUILD_COMMAND
+ AND NOT _libuv_INSTALL_COMMAND)
+ message(FATAL_ERROR "Must pass at least one of CONFIGURE_COMMAND, BUILD_COMMAND, INSTALL_COMMAND")
+ endif()
+ if(NOT _libuv_TARGET)
+ set(_libuv_TARGET "libuv")
+ endif()
+
+ ExternalProject_Add(${_libuv_TARGET}
+ PREFIX ${DEPS_BUILD_DIR}
+ URL ${LIBUV_URL}
+ DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/libuv
+ DOWNLOAD_COMMAND ${CMAKE_COMMAND}
+ -DPREFIX=${DEPS_BUILD_DIR}
+ -DDOWNLOAD_DIR=${DEPS_DOWNLOAD_DIR}/libuv
+ -DURL=${LIBUV_URL}
+ -DEXPECTED_SHA256=${LIBUV_SHA256}
+ -DTARGET=${_libuv_TARGET}
+ -DUSE_EXISTING_SRC_DIR=${USE_EXISTING_SRC_DIR}
+ -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/DownloadAndExtractFile.cmake
+ BUILD_IN_SOURCE ${_libuv_BUILD_IN_SOURCE}
+ CONFIGURE_COMMAND "${_libuv_CONFIGURE_COMMAND}"
+ BUILD_COMMAND "${_libuv_BUILD_COMMAND}"
+ INSTALL_COMMAND "${_libuv_INSTALL_COMMAND}")
+endfunction()
+
+set(UNIX_CFGCMD sh ${DEPS_BUILD_DIR}/src/libuv/autogen.sh &&
+ ${DEPS_BUILD_DIR}/src/libuv/configure --with-pic --disable-shared
+ --prefix=${DEPS_INSTALL_DIR} --libdir=${DEPS_INSTALL_DIR}/lib
+ CC=${DEPS_C_COMPILER})
+
+if(UNIX)
+ BuildLibuv(
+ CONFIGURE_COMMAND ${UNIX_CFGCMD}
+ INSTALL_COMMAND ${MAKE_PRG} V=1 install)
+
+elseif(MINGW AND CMAKE_CROSSCOMPILING)
+ # Build libuv for the host
+ BuildLibuv(TARGET libuv_host
+ CONFIGURE_COMMAND sh ${DEPS_BUILD_DIR}/src/libuv_host/autogen.sh && ${DEPS_BUILD_DIR}/src/libuv_host/configure --with-pic --disable-shared --prefix=${HOSTDEPS_INSTALL_DIR} CC=${HOST_C_COMPILER}
+ INSTALL_COMMAND ${MAKE_PRG} V=1 install)
+
+ # Build libuv for the target
+ BuildLibuv(
+ CONFIGURE_COMMAND ${UNIX_CFGCMD} --host=${CROSS_TARGET}
+ INSTALL_COMMAND ${MAKE_PRG} V=1 install)
+
+elseif(MINGW)
+
+ # Native MinGW
+ BuildLibUv(BUILD_IN_SOURCE
+ BUILD_COMMAND ${CMAKE_MAKE_PROGRAM} -f Makefile.mingw
+ INSTALL_COMMAND ${CMAKE_COMMAND} -E make_directory ${DEPS_INSTALL_DIR}/lib
+ COMMAND ${CMAKE_COMMAND} -E copy ${DEPS_BUILD_DIR}/src/libuv/libuv.a ${DEPS_INSTALL_DIR}/lib
+ COMMAND ${CMAKE_COMMAND} -E make_directory ${DEPS_INSTALL_DIR}/include
+ COMMAND ${CMAKE_COMMAND} -E copy_directory ${DEPS_BUILD_DIR}/src/libuv/include ${DEPS_INSTALL_DIR}/include
+ )
+
+elseif(WIN32 AND MSVC)
+
+ find_package(PythonInterp 2.6 REQUIRED)
+ if(NOT PYTHONINTERP_FOUND OR PYTHON_VERSION_MAJOR GREATER 2)
+ message(FATAL_ERROR "Python2 is required to build libuv on windows, use -DPYTHON_EXECUTABLE to set a python interpreter")
+ endif()
+
+ string(FIND ${CMAKE_GENERATOR} Win64 VS_WIN64)
+ if(VS_WIN64 EQUAL -1)
+ set(VS_ARCH x86)
+ else()
+ set(VS_ARCH x64)
+ endif()
+ string(TOLOWER ${CMAKE_BUILD_TYPE} LOWERCASE_BUILD_TYPE)
+ set(UV_OUTPUT_DIR ${DEPS_BUILD_DIR}/src/libuv/${CMAKE_BUILD_TYPE})
+ BuildLibUv(
+ BUILD_COMMAND set PYTHON=${PYTHON_EXECUTABLE} COMMAND ${DEPS_BUILD_DIR}/src/libuv/vcbuild.bat shared ${LOWERCASE_BUILD_TYPE} ${VS_ARCH}
+ INSTALL_COMMAND ${CMAKE_COMMAND} -E make_directory ${DEPS_INSTALL_DIR}/lib
+ COMMAND ${CMAKE_COMMAND} -E make_directory ${DEPS_INSTALL_DIR}/bin
+ COMMAND ${CMAKE_COMMAND} -E copy ${UV_OUTPUT_DIR}/libuv.lib ${DEPS_INSTALL_DIR}/lib
+ # Some applications (lua-client/luarocks) look for uv.lib instead of libuv.lib
+ COMMAND ${CMAKE_COMMAND} -E copy ${UV_OUTPUT_DIR}/libuv.lib ${DEPS_INSTALL_DIR}/lib/uv.lib
+ COMMAND ${CMAKE_COMMAND} -E copy ${UV_OUTPUT_DIR}/libuv.dll ${DEPS_INSTALL_DIR}/bin/
+ COMMAND ${CMAKE_COMMAND} -E copy ${UV_OUTPUT_DIR}/libuv.dll ${DEPS_INSTALL_DIR}/bin/uv.dll
+ COMMAND ${CMAKE_COMMAND} -E make_directory ${DEPS_INSTALL_DIR}/include
+ COMMAND ${CMAKE_COMMAND} -E copy_directory ${DEPS_BUILD_DIR}/src/libuv/include ${DEPS_INSTALL_DIR}/include)
+
+else()
+ message(FATAL_ERROR "Trying to build libuv in an unsupported system ${CMAKE_SYSTEM_NAME}/${CMAKE_C_COMPILER_ID}")
+endif()
+
+list(APPEND THIRD_PARTY_DEPS libuv)
diff --git a/third-party/cmake/BuildMsgpack.cmake b/third-party/cmake/BuildMsgpack.cmake
new file mode 100644
index 0000000..6b38508
--- /dev/null
+++ b/third-party/cmake/BuildMsgpack.cmake
@@ -0,0 +1,79 @@
+include(CMakeParseArguments)
+
+# BuildMsgpack(CONFIGURE_COMMAND ... BUILD_COMMAND ... INSTALL_COMMAND ...)
+# Reusable function to build msgpack, wraps ExternalProject_Add.
+# Failing to pass a command argument will result in no command being run
+function(BuildMsgpack)
+ cmake_parse_arguments(_msgpack
+ ""
+ ""
+ "CONFIGURE_COMMAND;BUILD_COMMAND;INSTALL_COMMAND"
+ ${ARGN})
+
+ if(NOT _msgpack_CONFIGURE_COMMAND AND NOT _msgpack_BUILD_COMMAND
+ AND NOT _msgpack_INSTALL_COMMAND)
+ message(FATAL_ERROR "Must pass at least one of CONFIGURE_COMMAND, BUILD_COMMAND, INSTALL_COMMAND")
+ endif()
+
+ ExternalProject_Add(msgpack
+ PREFIX ${DEPS_BUILD_DIR}
+ URL ${MSGPACK_URL}
+ DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/msgpack
+ DOWNLOAD_COMMAND ${CMAKE_COMMAND}
+ -DPREFIX=${DEPS_BUILD_DIR}
+ -DDOWNLOAD_DIR=${DEPS_DOWNLOAD_DIR}/msgpack
+ -DURL=${MSGPACK_URL}
+ -DEXPECTED_SHA256=${MSGPACK_SHA256}
+ -DTARGET=msgpack
+ -DUSE_EXISTING_SRC_DIR=${USE_EXISTING_SRC_DIR}
+ -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/DownloadAndExtractFile.cmake
+ CONFIGURE_COMMAND "${_msgpack_CONFIGURE_COMMAND}"
+ BUILD_COMMAND "${_msgpack_BUILD_COMMAND}"
+ INSTALL_COMMAND "${_msgpack_INSTALL_COMMAND}")
+endfunction()
+
+set(MSGPACK_CONFIGURE_COMMAND ${CMAKE_COMMAND} ${DEPS_BUILD_DIR}/src/msgpack
+ -DMSGPACK_ENABLE_CXX=OFF
+ -DMSGPACK_BUILD_TESTS=OFF
+ -DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR}
+ -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
+ -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
+ "-DCMAKE_C_FLAGS:STRING=${CMAKE_C_COMPILER_ARG1} -fPIC"
+ -DCMAKE_GENERATOR=${CMAKE_GENERATOR})
+
+set(MSGPACK_BUILD_COMMAND ${CMAKE_COMMAND} --build . --config ${CMAKE_BUILD_TYPE})
+set(MSGPACK_INSTALL_COMMAND ${CMAKE_COMMAND} --build . --target install --config ${CMAKE_BUILD_TYPE})
+
+if(MINGW AND CMAKE_CROSSCOMPILING)
+ get_filename_component(TOOLCHAIN ${CMAKE_TOOLCHAIN_FILE} REALPATH)
+ set(MSGPACK_CONFIGURE_COMMAND ${CMAKE_COMMAND} ${DEPS_BUILD_DIR}/src/msgpack
+ -DMSGPACK_ENABLE_CXX=OFF
+ -DMSGPACK_BUILD_TESTS=OFF
+ -DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR}
+ # Pass toolchain
+ -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN}
+ -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
+ # Hack to avoid -rdynamic in Mingw
+ -DCMAKE_SHARED_LIBRARY_LINK_C_FLAGS="")
+elseif(MSVC)
+ # Same as Unix without fPIC
+ set(MSGPACK_CONFIGURE_COMMAND ${CMAKE_COMMAND} ${DEPS_BUILD_DIR}/src/msgpack
+ -DMSGPACK_ENABLE_CXX=OFF
+ -DMSGPACK_BUILD_TESTS=OFF
+ -DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR}
+ -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
+ "-DCMAKE_C_FLAGS:STRING=${CMAKE_C_COMPILER_ARG1}"
+ -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
+ # Make sure we use the same generator, otherwise we may
+ # accidentaly end up using different MSVC runtimes
+ -DCMAKE_GENERATOR=${CMAKE_GENERATOR})
+ # Place the DLL in the bin folder
+ set(MSGPACK_INSTALL_COMMAND ${MSGPACK_INSTALL_COMMAND}
+ COMMAND ${CMAKE_COMMAND} -E copy ${DEPS_INSTALL_DIR}/lib/msgpack.dll ${DEPS_INSTALL_DIR}/bin)
+endif()
+
+BuildMsgpack(CONFIGURE_COMMAND ${MSGPACK_CONFIGURE_COMMAND}
+ BUILD_COMMAND ${MSGPACK_BUILD_COMMAND}
+ INSTALL_COMMAND ${MSGPACK_INSTALL_COMMAND})
+
+list(APPEND THIRD_PARTY_DEPS msgpack)
diff --git a/third-party/cmake/DownloadAndExtractFile.cmake b/third-party/cmake/DownloadAndExtractFile.cmake
new file mode 100644
index 0000000..24e431b
--- /dev/null
+++ b/third-party/cmake/DownloadAndExtractFile.cmake
@@ -0,0 +1,162 @@
+if(NOT DEFINED PREFIX)
+ message(FATAL_ERROR "PREFIX must be defined.")
+endif()
+
+if(NOT DEFINED URL)
+ message(FATAL_ERROR "URL must be defined.")
+endif()
+
+if(NOT DEFINED DOWNLOAD_DIR)
+ message(FATAL_ERROR "DOWNLOAD_DIR must be defined.")
+endif()
+
+if(NOT DEFINED EXPECTED_SHA256)
+ message(FATAL_ERROR "EXPECTED_SHA256 must be defined.")
+endif()
+
+if(NOT DEFINED TARGET)
+ message(FATAL_ERROR "TARGET must be defined.")
+endif()
+
+set(SRC_DIR ${PREFIX}/src/${TARGET})
+
+# Check whether the source has been downloaded. If true, skip it.
+# Useful for external downloads like homebrew.
+if(USE_EXISTING_SRC_DIR)
+ if(EXISTS "${SRC_DIR}" AND IS_DIRECTORY "${SRC_DIR}")
+ file(GLOB EXISTED_FILES "${SRC_DIR}/*")
+ if(EXISTED_FILES)
+ message(STATUS "${SRC_DIR} is found and not empty, skipping download and extraction. ")
+ return()
+ endif()
+ endif()
+ message(FATAL_ERROR "USE_EXISTING_SRC_DIR set to ON, but '${SRC_DIR}' does not exist or is empty.")
+endif()
+
+# Taken from ExternalProject_Add. Let's hope we can drop this one day when
+# ExternalProject_Add allows you to disable SHOW_PROGRESS on the file download.
+if(TIMEOUT)
+ set(timeout_args TIMEOUT ${timeout})
+ set(timeout_msg "${timeout} seconds")
+else()
+ set(timeout_args "# no TIMEOUT")
+ set(timeout_msg "none")
+endif()
+
+string(REGEX MATCH "[^/\\?]*$" fname "${URL}")
+if(NOT "${fname}" MATCHES "(\\.|=)(bz2|tar|tgz|tar\\.gz|zip)$")
+ string(REGEX MATCH "([^/\\?]+(\\.|=)(bz2|tar|tgz|tar\\.gz|zip))/.*$" match_result "${URL}")
+ set(fname "${CMAKE_MATCH_1}")
+endif()
+if(NOT "${fname}" MATCHES "(\\.|=)(bz2|tar|tgz|tar\\.gz|zip)$")
+ message(FATAL_ERROR "Could not extract tarball filename from url:\n ${url}")
+endif()
+string(REPLACE ";" "-" fname "${fname}")
+
+set(file ${DOWNLOAD_DIR}/${fname})
+message(STATUS "file: ${file}")
+
+message(STATUS "downloading...
+ src='${URL}'
+ dst='${file}'
+ timeout='${timeout_msg}'")
+
+file(DOWNLOAD ${URL} ${file}
+ ${timeout_args}
+ ${hash_args}
+ STATUS status
+ LOG log)
+
+list(GET status 0 status_code)
+list(GET status 1 status_string)
+
+if(NOT status_code EQUAL 0)
+ message(FATAL_ERROR "error: downloading '${URL}' failed
+ status_code: ${status_code}
+ status_string: ${status_string}
+ log: ${log}
+")
+endif()
+
+set(NULL_SHA256 "0000000000000000000000000000000000000000000000000000000000000000")
+
+# Allow users to use "SKIP" or "skip" as the sha256 to skip checking the hash.
+# You can still use the all zeros hash too.
+if((EXPECTED_SHA256 STREQUAL "SKIP") OR (EXPECTED_SHA256 STREQUAL "skip"))
+ set(EXPECTED_SHA256 ${NULL_SHA256})
+endif()
+
+# We could avoid computing the SHA256 entirely if a NULL_SHA256 was given,
+# but we want to warn users of an empty file.
+file(SHA256 ${file} ACTUAL_SHA256)
+if(ACTUAL_SHA256 STREQUAL "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
+ # File was empty. It's likely due to lack of SSL support.
+ message(FATAL_ERROR
+ "Failed to download ${URL}. The file is empty and likely means CMake "
+ "was built without SSL support. Please use a version of CMake with "
+ "proper SSL support. See "
+ "https://github.com/neovim/neovim/wiki/Building-Neovim#build-prerequisites "
+ "for more information.")
+elseif((NOT EXPECTED_SHA256 STREQUAL NULL_SHA256) AND
+ (NOT EXPECTED_SHA256 STREQUAL ACTUAL_SHA256))
+ # Wasn't a NULL SHA256 and we didn't match, so we fail.
+ message(FATAL_ERROR
+ "Failed to download ${URL}. Expected a SHA256 of "
+ "${EXPECTED_SHA256} but got ${ACTUAL_SHA256} instead.")
+endif()
+
+message(STATUS "downloading... done")
+
+# Slurped from a generated extract-TARGET.cmake file.
+message(STATUS "extracting...
+ src='${file}'
+ dst='${SRC_DIR}'")
+
+if(NOT EXISTS "${file}")
+ message(FATAL_ERROR "error: file to extract does not exist: '${file}'")
+endif()
+
+# Prepare a space for extracting:
+#
+set(i 1234)
+while(EXISTS "${SRC_DIR}/../ex-${TARGET}${i}")
+ math(EXPR i "${i} + 1")
+endwhile()
+set(ut_dir "${SRC_DIR}/../ex-${TARGET}${i}")
+file(MAKE_DIRECTORY "${ut_dir}")
+
+# Extract it:
+#
+message(STATUS "extracting... [tar xfz]")
+execute_process(COMMAND ${CMAKE_COMMAND} -E tar xfz ${file}
+ WORKING_DIRECTORY ${ut_dir}
+ RESULT_VARIABLE rv)
+
+if(NOT rv EQUAL 0)
+ message(STATUS "extracting... [error clean up]")
+ file(REMOVE_RECURSE "${ut_dir}")
+ message(FATAL_ERROR "error: extract of '${file}' failed")
+endif()
+
+# Analyze what came out of the tar file:
+#
+message(STATUS "extracting... [analysis]")
+file(GLOB contents "${ut_dir}/*")
+list(LENGTH contents n)
+if(NOT n EQUAL 1 OR NOT IS_DIRECTORY "${contents}")
+ set(contents "${ut_dir}")
+endif()
+
+# Move "the one" directory to the final directory:
+#
+message(STATUS "extracting... [rename]")
+file(REMOVE_RECURSE ${SRC_DIR})
+get_filename_component(contents ${contents} ABSOLUTE)
+file(RENAME ${contents} ${SRC_DIR})
+
+# Clean up:
+#
+message(STATUS "extracting... [clean up]")
+file(REMOVE_RECURSE "${ut_dir}")
+
+message(STATUS "extracting... done")
diff --git a/third-party/cmake/RemoveFiles.cmake b/third-party/cmake/RemoveFiles.cmake
new file mode 100644
index 0000000..88e2bc7
--- /dev/null
+++ b/third-party/cmake/RemoveFiles.cmake
@@ -0,0 +1,5 @@
+file(GLOB_RECURSE FILES_TO_REMOVE ${REMOVE_FILE_GLOB})
+
+if(FILES_TO_REMOVE)
+ file(REMOVE ${FILES_TO_REMOVE})
+endif()