diff --git a/.drone.yml b/.drone.yml deleted file mode 100644 index 1eb86b4..0000000 --- a/.drone.yml +++ /dev/null @@ -1,47 +0,0 @@ ---- -kind: pipeline -name: default - -steps: - - name: scan-build - image: debian:buster - pull: always - commands: - - apt-get update && apt-get install -yq git build-essential autotools-dev lsb-release pkg-config automake autoconf libtool-bin clang-tools-7 - - apt-get install -yq cmake uuid-dev libssl-dev - - git clone https://github.com/signalwire/libks libks - - cd libks && cmake . -DCMAKE_BUILD_TYPE=Release && make && make install && cd .. - - sed -i '/cotire/d' ./CMakeLists.txt - - sed -i '/cotire/d' ./swclt_test/CMakeLists.txt - - mkdir -p scan-build - - scan-build-7 -o ./scan-build/ cmake . - - echo '#!/bin/bash\nscan-build-7 -o ./scan-build/ make -j`nproc --all` |& tee ./scan-build-result.txt\nexitstatus=$${PIPESTATUS[0]}\necho $$exitstatus > ./scan-build-status.txt\n' > scan.sh - - chmod +x scan.sh - - ./scan.sh - - exitstatus=`cat ./scan-build-status.txt` - - echo "*** Exit status is $exitstatus" - - - name: notify - image: signalwire/drone-notify - pull: always - environment: - SLACK_WEBHOOK_URL: - from_secret: slack_webhook_url - ENV_FILE: - from_secret: notify_env - commands: - - /root/scan-build-notify.sh - -trigger: - branch: - - master - -trigger: - event: - - pull_request - - push ---- -kind: signature -hmac: 4cda5542fa43796175f5abe9f8510deb23677fe8d4578e8c6096625d28705d49 - -... diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..027a4b2 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,77 @@ +name: CI + +on: + pull_request: + types: [synchronize, opened] + branches: + - "v2.0" + push: + branches: + - "v2.0" + +jobs: + scan_build: + runs-on: ubuntu-latest + container: + image: debian:buster + steps: + - name: install deps + run: | + apt-get update && apt-get install -yq build-essential autotools-dev lsb-release pkg-config automake autoconf libtool-bin clang-tools-7 + apt-get install -yq cmake uuid-dev libssl-dev + - name: Checkout + uses: actions/checkout@v3 + - run: pwd + - name: clone libks + uses: actions/checkout@v3 + with: + repository: signalwire/libks + ref: v2.0 + path: /__w/signalwire-c/signalwire-c/libks + - name: build libks + run: cd /__w/signalwire-c/signalwire-c/libks && cmake . -DCMAKE_BUILD_TYPE=Release && make && make install && cd .. + - id: scan_build + run: /__w/signalwire-c/signalwire-c/scan_build.sh + - name: Tar logs + id: tar + if: failure() + env: + COMPILATION_FAILED: ${{ steps.scan_build.outputs.COMPILATION_FAILED }} + BUGS_FOUND: ${{ steps.scan_build.outputs.BUGS_FOUND }} + run: | + cd /__w/signalwire-c/signalwire-c + ls -l + if [ "true" -eq $COMPILATION_FAILED ]; then + tar czvf scan-build-result.tar.gz ./scan-build-result.txt; + echo "ARTIFACT_PATH=scan-build-result.tar.gz" >> $GITHUB_OUTPUT; + echo "ARTIFACT=scan-build-result" >> $GITHUB_OUTPUT; + fi + if [ "true" -eq $BUGS_FOUND ]; then + tar czvf reports.tar.gz $REPORT; + echo "ARTIFACT_PATH=reports.tar.gz" >> $GITHUB_OUTPUT; + echo "ARTIFACT=reports" >> $GITHUB_OUTPUT; + fi + - name: Upload artifacts + if: failure() + uses: actions/upload-artifact@v3 + with: + name: ${{ steps.tar.outputs.ARTIFACT }}-${{ github.sha }}-${{ github.run_id }} + path: ${{ steps.tar.outputs.ARTIFACT_PATH }} + retention-days: 5 + - name: comment on PR + if: ${{ failure() && github.event_name == 'pull_request' }} + uses: thollander/actions-comment-pull-request@v2 + with: + message: | + scan_build _*failed*_. + Please checkout the results. + Action Run: [${{ github.run_id }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) + pr_number: 123 # This will comment on pull request #123 + - name: notify slack + if: ${{ failure() && github.event_name == 'push' }} + uses: signalwire/actions-template/.github/actions/slack@main + with: + CHANNEL: ${{ secrets.SLACK_CHANNEL_ID }} + MESSAGE: Scan-build ${{ github.repository }} > <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|${{ github.run_id }}>.\n ${{ steps.scan_build.outputs.MESSAGE }}}.\nPlease check the results. + env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} diff --git a/.gitignore b/.gitignore index 4af231e..9c6eb45 100644 --- a/.gitignore +++ b/.gitignore @@ -6,18 +6,17 @@ CTestTestfile.cmake Makefile *_cotire.cmake cmake_install.cmake -libsignalwire_client.so +libsignalwire_client2.so* testclient /cotire /build Testing/ core -swclt_bench/swclt_bench swclt_test/cotire/ swclt_test/swclt_test /install_manifest.txt /libsignalwire_client.dylib -/signalwire_client.pc +/signalwire_client2.pc /uninstall.cmake build-Win32/ build-x64/ @@ -25,3 +24,7 @@ CPackConfig.cmake CPackSourceConfig.cmake changelog.Debian.gz libsignalwire_client.so.1 +inc/signalwire-client-c/version.h +_CPack_Packages/ +install_manifest_runtime.txt +*.deb \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index aff8410..0000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "name": "(gdb) Debug testclient", - "type": "cppdbg", - "request": "launch", - "program": "${workspaceFolder}/build/tests/testclient", - "args": [], - "stopAtEntry": false, - "cwd": "${workspaceFolder}", - "environment": [], - "externalConsole": true, - "MIMode": "gdb", - "setupCommands": [ - { - "description": "Enable pretty-printing for gdb", - "text": "-enable-pretty-printing", - "ignoreFailures": true - } - ] - } - ] -} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 92b1fea..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "clang.cflags": [ - "-DHAVE_CLOCK_GETRES=1", - "-DHAVE_CLOCK_GETTIME=1", - "-DHAVE_CLOCK_NANOSLEEP=1", - "-DHAVE_LIBCRYPTO=1", - "-DHAVE_LIBSSL=1", - "-DHAVE_MALLOC=1", - "-DHAVE_MEMMEM=1", - "-DHAVE_PTHREAD_SETSCHEDPARAM=1", - "-DHAVE_SCHED_SETSCHEDULER=1", - "-DHAVE_STRFTIME=1", - "-DHAVE_USLEEP=1", - "-DKS_PLAT_LIN=1", - "-DRETSIGTYPE=void", - "-DSTDC_HEADERS=1", - "-DSWCLT_EXPORTS", - "-DSWCLT_VERSION_MAJOR=1", - "-DSWCLT_VERSION_MINOR=0", - "-DSWCLT_VERSION_REVISION=0", - "-DTIME_WITH_SYS_TIME=1", - "-D_REENTRANT=1", - "-D__BYTE_ORDER=__LITTLE_ENDIAN", - "-DKS_BUILD_DEBUG=1", - "-I/home/jason/signalwire/signalwire-client-c/inc", - "-std=c11", - "-I/home/jason/signalwire/signalwire-client-c/swclt_bench", - "-I/home/jason/signalwire/signalwire-client-c/swclt_test" - ], - "clang.cxxflags": [] -} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 1f80cda..ff5d46f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,15 +16,20 @@ if (WIN32) endif() endif() -# Declare our project, libks -project(SignalWire-Client-C VERSION 1.3.4 LANGUAGES C) -message("SignalWire-Client-C ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}") +# Declare our project, SignalWire-Client-C2 +project(SignalWire-Client-C2 VERSION 2.0.0 LANGUAGES C) +message("SignalWire-Client-C2 ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}") # Set package version set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR}) set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR}) set(CPACK_PACKAGE_VERSION_PATCH "0") +# Configure the version header file +configure_file ( + "${CMAKE_CURRENT_LIST_DIR}/inc/signalwire-client-c/version.h.in" + "${CMAKE_CURRENT_LIST_DIR}/inc/signalwire-client-c/version.h" ) + include(cmake/FindLibKS.cmake) include(${LIBKS_CMAKE_DIR}/ksutil.cmake) ksutil_setup_platform() @@ -42,7 +47,7 @@ if(KS_PLAT_LIN) endif() # Set package name -string(TOLOWER ${CMAKE_PROJECT_NAME} PACKAGE_NAME) +string(TOLOWER "${CMAKE_PROJECT_NAME}" PACKAGE_NAME) # Set package contact set(CPACK_PACKAGE_CONTACT "support@signalwire.com") @@ -108,7 +113,7 @@ if("${CMAKE_OS_NAME}" STREQUAL "Centos") set(CPACK_GENERATOR RPM) # Set RPM package name - set(CPACK_RPM_RUNTIME_PACKAGE_NAME "signalwire-client-c") + set(CPACK_RPM_RUNTIME_PACKAGE_NAME "signalwire-client-c2") # Set version release from environment variable if (NOT "$ENV{PACKAGE_RELEASE}" STREQUAL "") @@ -183,7 +188,7 @@ if("${CMAKE_OS_NAME}" STREQUAL "Debian") set(CPACK_DEB_COMPONENT_INSTALL "ON") # Set package name - set(CPACK_DEBIAN_PACKAGE_NAME "signalwire-client-c") + set(CPACK_DEBIAN_PACKAGE_NAME "signalwire-client-c2") # Abuse Cmake component name set(CPACK_DEBIAN_RUNTIME_PACKAGE_NAME ${CPACK_DEBIAN_PACKAGE_NAME}) @@ -248,7 +253,7 @@ if("${CMAKE_OS_NAME}" STREQUAL "Debian") set(CPACK_DEBIAN_FILE_NAME "${PACKAGE_NAME}_${PROJECT_VERSION}-${CPACK_DEBIAN_PACKAGE_RELEASE}_${DISTRO_CODENAME}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}.deb") # Set a Debian compliant changelog header - set(CHANGELOG_HEADER "${PACKAGE_NAME} (${CPACK_DEBIAN_PACKAGE_VERSION}) ${DISTRO_CODENAME}\; urgency=${CPACK_DEBIAN_PACKAGE_PRIORITY}") + set(CHANGELOG_HEADER "signalwire-client-c2 (${CPACK_DEBIAN_PACKAGE_VERSION}) ${DISTRO_CODENAME}\; urgency=${CPACK_DEBIAN_PACKAGE_PRIORITY}") # Generate a Debian compliant changelog if(GIT_FOUND AND GZIP_CMD AND DATE_CMD) @@ -288,7 +293,7 @@ if("${CMAKE_OS_NAME}" STREQUAL "Debian") execute_process(COMMAND ${GZIP_CMD} -f -9 -n changelog.Debian) # Install changelog - install(FILES "${CMAKE_BINARY_DIR}/changelog.Debian.gz" COMPONENT "runtime" DESTINATION "share/doc/${CPACK_DEBIAN_PACKAGE_NAME}") + install(FILES "${CMAKE_BINARY_DIR}/changelog.Debian.gz" COMPONENT "runtime" DESTINATION "share/doc/signalwire-client-c2") # Delete changelog related variables unset(CHANGELOG_HEADER) @@ -324,26 +329,26 @@ if (KS_PLAT_WIN) source_group(TREE ${CMAKE_CURRENT_LIST_DIR} FILES ${SWCltInc}) endif() -add_library(signalwire_client SHARED ${SWCltSrc} ${SWCltInc}) -set_target_properties(signalwire_client PROPERTIES SOVERSION ${PROJECT_VERSION_MAJOR}) -set_target_properties(signalwire_client PROPERTIES DEBUG_POSTFIX "") +add_library(signalwire_client2 SHARED ${SWCltSrc} ${SWCltInc}) +set_target_properties(signalwire_client2 PROPERTIES SOVERSION ${PROJECT_VERSION_MAJOR}) +set_target_properties(signalwire_client2 PROPERTIES DEBUG_POSTFIX "") # Define our exports symbol to key any definitions to toggle the visibility type -set_target_properties(signalwire_client PROPERTIES DEFINE_SYMBOL SWCLT_EXPORTS) +set_target_properties(signalwire_client2 PROPERTIES DEFINE_SYMBOL SWCLT_EXPORTS) # Setup blade core definnitions -target_compile_definitions(signalwire_client PUBLIC +target_compile_definitions(signalwire_client2 PUBLIC -DSWCLT_VERSION_MAJOR=${PROJECT_VERSION_MAJOR} -DSWCLT_VERSION_MINOR=${PROJECT_VERSION_MINOR} -DSWCLT_VERSION_REVISION=${PROJECT_VERSION_PATCH} ) if (KS_PLAT_WIN) - target_compile_definitions(signalwire_client PUBLIC) + target_compile_definitions(signalwire_client2 PUBLIC) endif() if (SWCLT_DEBUG_JSON) - target_compile_definitions(signalwire_client PUBLIC + target_compile_definitions(signalwire_client2 PUBLIC -DSWCLT_DEBUG_JSON=1 ) endif() @@ -355,11 +360,11 @@ endif() # Setup ks core dependent linkages if (NOT WIN32) - target_link_libraries(signalwire_client PUBLIC ks) + target_link_libraries(signalwire_client2 PUBLIC ks2) else() target_link_libraries( - signalwire_client PUBLIC - ${LIBKS_LIBRARY_PATH}/ks.lib + signalwire_client2 PUBLIC + ${LIBKS_LIBRARY_PATH}/ks2.lib OpenSSL::SSL OpenSSL::Crypto ) @@ -367,22 +372,22 @@ endif() # Include key paths target_include_directories( - signalwire_client PUBLIC - $/include/libks> + signalwire_client2 PUBLIC + $/include/libks2/libks> $ ) if (WIN32) # Include key paths target_include_directories( - signalwire_client PUBLIC + signalwire_client2 PUBLIC $ $ ) endif() target_include_directories( - signalwire_client PRIVATE + signalwire_client2 PRIVATE ) # Include the bin dir for config discovery @@ -390,22 +395,22 @@ set(CONF_INCLUDE_DIRS "${PROJECT_SOURCE_DIR}" "${PROJECT_BINARY_DIR}") if (NOT KS_PLAT_WIN) # Set install targets - install(TARGETS signalwire_client COMPONENT "runtime" EXPORT SignalWireClientConfig DESTINATION lib) - install(DIRECTORY inc/signalwire-client-c COMPONENT "runtime" DESTINATION include PATTERN internal EXCLUDE) + install(TARGETS signalwire_client2 COMPONENT "runtime" EXPORT SignalWireClient2Config DESTINATION lib) + install(DIRECTORY inc/signalwire-client-c COMPONENT "runtime" DESTINATION include/signalwire-client-c2 PATTERN internal EXCLUDE) # Set path for pkg-config based on ARCH and distro type if("${CMAKE_OS_NAME}" STREQUAL "Centos") # pkg-config --variable pc_path pkg-config if(${CPACK_SYSTEM_NAME} MATCHES "x86_64") - install(FILES ${PROJECT_BINARY_DIR}/signalwire_client.pc COMPONENT "runtime" DESTINATION lib64/pkgconfig) + install(FILES ${PROJECT_BINARY_DIR}/signalwire_client2.pc COMPONENT "runtime" DESTINATION lib64/pkgconfig) else() - install(FILES ${PROJECT_BINARY_DIR}/signalwire_client.pc COMPONENT "runtime" DESTINATION lib/pkgconfig) + install(FILES ${PROJECT_BINARY_DIR}/signalwire_client2.pc COMPONENT "runtime" DESTINATION lib/pkgconfig) endif() else() - install(FILES ${PROJECT_BINARY_DIR}/signalwire_client.pc COMPONENT "runtime" DESTINATION lib/pkgconfig) + install(FILES ${PROJECT_BINARY_DIR}/signalwire_client2.pc COMPONENT "runtime" DESTINATION lib/pkgconfig) endif() - install(EXPORT SignalWireClientConfig COMPONENT "runtime" DESTINATION include/signalwire-client-c/cmake) - install(FILES ${PROJECT_BINARY_DIR}/copyright COMPONENT "runtime" DESTINATION share/doc/signalwire-client-c) + install(EXPORT SignalWireClient2Config COMPONENT "runtime" DESTINATION include/signalwire-client-c2/signalwire-client-c/cmake) + install(FILES ${PROJECT_BINARY_DIR}/copyright COMPONENT "runtime" DESTINATION share/doc/signalwire-client-c2) # Set uninstall target if(NOT TARGET uninstall) @@ -428,16 +433,14 @@ if (NOT KS_PLAT_WIN) # Name: @PACKAGE_NAME@ # Version: @PACKAGE_VERSION@ set(PC_PREFIX ${CMAKE_INSTALL_PREFIX}) - get_property(PC_DEFINITIONS TARGET signalwire_client PROPERTY INTERFACE_COMPILE_DEFINITIONS) - get_property(PC_DEFINITIONS TARGET ks PROPERTY INTERFACE_COMPILE_DEFINITIONS) - set(PACKAGE_NAME signalwire_client) + get_property(PC_DEFINITIONS TARGET signalwire_client2 PROPERTY INTERFACE_COMPILE_DEFINITIONS) + get_property(PC_DEFINITIONS TARGET ks2 PROPERTY INTERFACE_COMPILE_DEFINITIONS) + set(PACKAGE_NAME signalwire_client2) set(PACKAGE_VERSION ${PROJECT_VERSION}) - configure_file("${CMAKE_CURRENT_LIST_DIR}/signalwire_client.pc.in" "${PROJECT_BINARY_DIR}/signalwire_client.pc" @ONLY) + configure_file("${CMAKE_CURRENT_LIST_DIR}/signalwire_client.pc.in" "${PROJECT_BINARY_DIR}/signalwire_client2.pc" @ONLY) endif() -cotire(signalwire_client) - -add_subdirectory(swclt_bench) +cotire(signalwire_client2) enable_testing() add_subdirectory(swclt_test) diff --git a/cmake/FindLibKS.cmake b/cmake/FindLibKS.cmake index 2ae8f72..8feda88 100644 --- a/cmake/FindLibKS.cmake +++ b/cmake/FindLibKS.cmake @@ -11,28 +11,31 @@ if (NOT TARGET ks) include(FindPkgConfig) # Use the pkg-config system to locate our libks configs - pkg_check_modules(LIBKS libks REQUIRED) + pkg_check_modules(LIBKS libks2 REQUIRED) + + # From here we can bootstrap into cmake stuff + set(LIBKS_CMAKE_DIR ${LIBKS_INCLUDE_DIRS}/libks/cmake) else() if ("${LIBKS_INCLUDE_DIRS}" STREQUAL "") message(FATAL_ERROR "Can't find Libks. Try setting LIBKS_INCLUDE_DIRS.") endif() - endif() - # From here we can bootstrap into cmake stuff - set(LIBKS_CMAKE_DIR ${LIBKS_INCLUDE_DIRS}/cmake) + # From here we can bootstrap into cmake stuff + set(LIBKS_CMAKE_DIR ${LIBKS_INCLUDE_DIRS}/cmake) + endif() # Load ks utils for our build macros include(${LIBKS_CMAKE_DIR}/ksutil.cmake) # Now load the package with a hint on where to look - set(LibKS_DIR ${LIBKS_CMAKE_DIR}) + set(LibKS2_DIR ${LIBKS_CMAKE_DIR}) if (NOT WIN32) - find_package(LibKS REQUIRED VERSION) + find_package(LibKS2 REQUIRED VERSION) else() if(LIBKS_INCLUDE_DIRS AND EXISTS "${LIBKS_INCLUDE_DIRS}/CMakeLists.txt") file(STRINGS "${LIBKS_INCLUDE_DIRS}/CMakeLists.txt" libks_version_str - REGEX "^[\t ]*project[\t (]*LibKS[\t ]+VERSION[\t ]+([0-9a-fA-F]\.[0-9a-fA-F]).*") + REGEX "^[\t ]*project[\t (]*LibKS2[\t ]+VERSION[\t ]+([0-9a-fA-F]\.[0-9a-fA-F]).*") string(COMPARE EQUAL "${libks_version_str}" "" _is_empty) if(_is_empty) @@ -41,18 +44,18 @@ if (NOT TARGET ks) "Incorrect LibKS VERSION in project command of LibKS's CMakeLists.txt" ": ${LIBKS_INCLUDE_DIRS}/CMakeLists.txt") endif() - string(REGEX REPLACE "^[\t ]*project[\t (]*LibKS[\t ]+VERSION[\t ]+([0-9a-fA-F]\.[0-9a-fA-F]).*$" + string(REGEX REPLACE "^[\t ]*project[\t (]*LibKS2[\t ]+VERSION[\t ]+([0-9a-fA-F]\.[0-9a-fA-F]).*$" "\\1;" LIBKS_VERSION_LIST "${libks_version_str}") list(GET LIBKS_VERSION_LIST 0 LIBKS_VERSION) endif() endif() - message("Found LibKS ${LIBKS_VERSION} package at path ${LIBKS_INCLUDE_DIRS}") - if (${LIBKS_VERSION} VERSION_LESS 1.1) - message(FATAL "Libks version 1.1 or greater is required") + message("Found LibKS2 ${LIBKS_VERSION} package at path ${LIBKS_INCLUDE_DIRS}") + if (${LIBKS_VERSION} VERSION_LESS 2.0) + message(FATAL "Libks version 2.0 or greater is required") endif() else() - get_target_property(KS_SOURCE_DIR ks SOURCE_DIR) + get_target_property(KS_SOURCE_DIR ks2 SOURCE_DIR) message("Switchblade ${KS_SOURCE_DIR}") set(LIBKS_CMAKE_DIR ${KS_SOURCE_DIR}/cmake) diff --git a/examples/client/CMakeLists.txt b/examples/client/CMakeLists.txt index 9083c3a..e1fb9ff 100644 --- a/examples/client/CMakeLists.txt +++ b/examples/client/CMakeLists.txt @@ -2,4 +2,4 @@ cmake_minimum_required(VERSION 3.7.2) # Right now we can setup the example add_executable(test main.c) -target_link_libraries(test signalwire_client) +target_link_libraries(test signalwire_client2) diff --git a/inc/signalwire-client-c/JSON/macros.h b/inc/signalwire-client-c/JSON/macros.h index 402bc02..c274b4e 100644 --- a/inc/signalwire-client-c/JSON/macros.h +++ b/inc/signalwire-client-c/JSON/macros.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 SignalWire, Inc + * Copyright (c) 2018-2019 SignalWire, Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -64,11 +64,13 @@ KS_BEGIN_EXTERN_C * to also exist. */ #define SWCLT_JSON_PARSE_BEG(function_name, target_type) \ static inline ks_status_t function_name##_PARSE(ks_pool_t *pool, \ - const ks_json_t * const object, target_type **result) \ + ks_json_t *object, target_type **result) \ { \ void (*release_cb)(target_type **) = (void(*)(target_type **))function_name##_DESTROY; \ target_type *target = (target_type *)ks_pool_alloc(pool, sizeof(target_type)); \ \ + (void)(object); /* suppress unused warnings */ \ + (void)(release_cb); /* suppress unused warnings */ \ if (!target) return KS_STATUS_NO_MEM; #define SWCLT_JSON_PARSE_CUSTOM_OPT(function_name, key) \ @@ -92,11 +94,13 @@ KS_BEGIN_EXTERN_C { \ ks_json_t *item = ks_json_get_object_item(object, #key); \ if (item) { \ + const char *str = ""; \ if (!ks_json_type_is_string(item)) { \ release_cb(&target); \ return KS_STATUS_INVALID_ARGUMENT; \ } \ - target->key = ks_json_value_uuid(item); \ + ks_json_value_string(item, &str); \ + target->key = ks_uuid_from_str(str); \ } \ } @@ -108,7 +112,7 @@ KS_BEGIN_EXTERN_C release_cb(&target); \ return KS_STATUS_INVALID_ARGUMENT; \ } \ - target->key = ks_json_value_bool(item); \ + ks_json_value_bool(item, &target->key); \ } \ } @@ -116,11 +120,11 @@ KS_BEGIN_EXTERN_C { \ ks_json_t *item = ks_json_get_object_item(object, #key); \ if (item) { \ - if (!ks_json_type_is_number(item)) { \ - release_cb(&target); \ - return KS_STATUS_INVALID_ARGUMENT; \ + if (!ks_json_type_is_number(item)) { \ + release_cb(&target); \ + return KS_STATUS_INVALID_ARGUMENT; \ } \ - *((int *)&target->key) = ks_json_value_number_int(item); \ + target->key = ks_json_get_number_int(item, 0); \ } \ } @@ -128,17 +132,41 @@ KS_BEGIN_EXTERN_C { \ ks_json_t *item = ks_json_get_object_item(object, #key); \ if (item) { \ - if (!ks_json_type_is_number(item)) { \ - release_cb(&target); \ - return KS_STATUS_INVALID_ARGUMENT; \ + if (!ks_json_type_is_number(item)) { \ + release_cb(&target); \ + return KS_STATUS_INVALID_ARGUMENT; \ } \ - *((int *)&target->key) = ks_json_value_number_int(item); \ - } else *((int *)&target->key) = def; \ + target->key = ks_json_get_number_int(item, 0); \ + } else target->key = def; \ + } + +#define SWCLT_JSON_PARSE_DOUBLE_OPT(key) \ + { \ + ks_json_t *item = ks_json_get_object_item(object, #key); \ + if (item) { \ + if (!ks_json_type_is_number(item)) { \ + release_cb(&target); \ + return KS_STATUS_INVALID_ARGUMENT; \ + } \ + target->key = ks_json_get_number_double(item, 0); \ + } \ + } + +#define SWCLT_JSON_PARSE_DOUBLE_OPT_DEF(key, def) \ + { \ + ks_json_t *item = ks_json_get_object_item(object, #key); \ + if (item) { \ + if (!ks_json_type_is_number(item)) { \ + release_cb(&target); \ + return KS_STATUS_INVALID_ARGUMENT; \ + } \ + target->key = ks_json_get_number_double(item, 0); \ + } else target->key = def; \ } #define SWCLT_JSON_PARSE_STRING_OPT(key) \ { \ - const char *str = ks_json_get_object_cstr(object, #key); \ + const char *str = ks_json_get_object_string(object, #key, NULL); \ if (str) { \ if (!(target->key = ks_pstrdup(pool, str))) { \ release_cb(&target); \ @@ -151,7 +179,7 @@ KS_BEGIN_EXTERN_C { \ ks_json_t *item = ks_json_get_object_item(object, #key); \ if (item) { \ - if (!(target->key = ks_json_pduplicate(pool, item, KS_TRUE))) { \ + if (!(target->key = ks_json_duplicate(item, KS_TRUE))) { \ release_cb(&target); \ return KS_STATUS_NO_MEM; \ } \ @@ -176,11 +204,13 @@ KS_BEGIN_EXTERN_C #define SWCLT_JSON_PARSE_UUID(key) \ { \ ks_json_t *item = ks_json_get_object_item(object, #key); \ + const char *str = ""; \ if (!item || !ks_json_type_is_string(item)) { \ release_cb(&target); \ return KS_STATUS_INVALID_ARGUMENT; \ } \ - target->key = ks_json_value_uuid(item); \ + ks_json_value_string(item, &str); \ + target->key = ks_uuid_from_str(str); \ } #define SWCLT_JSON_PARSE_BOOL(key) \ @@ -190,7 +220,7 @@ KS_BEGIN_EXTERN_C release_cb(&target); \ return KS_STATUS_INVALID_ARGUMENT; \ } \ - target->key = ks_json_value_bool(item); \ + ks_json_value_bool(item, &target->key); \ } #define SWCLT_JSON_PARSE_DOUBLE(key) \ @@ -200,7 +230,7 @@ KS_BEGIN_EXTERN_C release_cb(&target); \ return KS_STATUS_INVALID_ARGUMENT; \ } \ - *((double *)&target->key) = ks_json_value_number_double(item); \ + ks_json_value_number_double(item, (double *)&target->key); \ } #define SWCLT_JSON_PARSE_INT_EX(source_key, target_key) \ @@ -210,7 +240,7 @@ KS_BEGIN_EXTERN_C release_cb(&target); \ return KS_STATUS_INVALID_ARGUMENT; \ } \ - *((int *)&target->source_key) = ks_json_value_number_int(item); \ + ks_json_value_number_int(item, (int *)&target->source_key); \ } @@ -221,20 +251,20 @@ KS_BEGIN_EXTERN_C release_cb(&target); \ return KS_STATUS_INVALID_ARGUMENT; \ } \ - *((int *)&target->key) = ks_json_value_number_int(item); \ + ks_json_value_number_int(item, (int *)&target->key); \ } -#define SWCLT_JSON_PARSE_STRING(key) \ - { \ - const char *str = ks_json_get_object_cstr(object, #key); \ - if (!str) { \ - release_cb(&target); \ - return KS_STATUS_INVALID_ARGUMENT; \ - } \ - if (!(target->key = ks_pstrdup(pool, str))) { \ - release_cb(&target); \ - return KS_STATUS_NO_MEM; \ - } \ +#define SWCLT_JSON_PARSE_STRING(key) \ + { \ + const char *str = ks_json_get_object_string(object, #key, NULL); \ + if (!str) { \ + release_cb(&target); \ + return KS_STATUS_INVALID_ARGUMENT; \ + } \ + if (!(target->key = ks_pstrdup(pool, str))) { \ + release_cb(&target); \ + return KS_STATUS_NO_MEM; \ + } \ } #define SWCLT_JSON_PARSE_ITEM(key) \ @@ -244,14 +274,13 @@ KS_BEGIN_EXTERN_C release_cb(&target); \ return KS_STATUS_INVALID_ARGUMENT; \ } \ - if (!(target->key = ks_json_pduplicate(pool, item, KS_TRUE))) { \ + if (!(target->key = ks_json_duplicate(item, KS_TRUE))) { \ release_cb(&target); \ return KS_STATUS_NO_MEM; \ } \ } #define SWCLT_JSON_PARSE_END() \ - (void)release_cb; \ *result = (void *)target; \ return KS_STATUS_SUCCESS; \ } @@ -261,10 +290,11 @@ KS_BEGIN_EXTERN_C #define SWCLT_JSON_DESTROY_BEG(function_name, type) \ static inline void function_name##_DESTROY(type **__free_target) \ { \ - type *target; \ + type *target = NULL; \ if (!__free_target || !*__free_target) \ return; \ - target = *__free_target; + target = *__free_target; \ + (void)(target); /* suppress unused warnings */ #define SWCLT_JSON_DESTROY_STRING(key) ks_pool_free(&target->key); @@ -283,94 +313,67 @@ KS_BEGIN_EXTERN_C function_name##_DESTROY(&target->key); #define SWCLT_JSON_DESTROY_END() \ - (void)target; \ ks_pool_free(__free_target); \ } -/* MARHSAL - Converts the type to a json object, opposite of alloc/parse. +/* MARSHAL - Converts the type to a json object, opposite of alloc/parse. * Creates an inline function called function_name_MARSHAL. */ #define SWCLT_JSON_MARSHAL_BEG(function_name, target_type) \ - static inline ks_json_t * function_name##_MARSHAL(ks_pool_t *pool, target_type *source) \ + static inline ks_json_t * function_name##_MARSHAL(target_type *source) \ { \ - ks_json_t *target = NULL; \ + ks_json_t *target; \ if (!source) \ return NULL; \ - target = ks_json_pcreate_object(pool); \ + target = ks_json_create_object(); \ if (!target) \ return NULL; #define SWCLT_JSON_MARSHAL_UUID(key) \ - if (!(ks_json_padd_uuid_to_object(pool, target, #key, source->key))) { \ - ks_json_delete(&target); \ - return NULL; \ - } + { \ + char *str = ks_uuid_str(NULL, &source->key); \ + ks_json_add_string_to_object(target, #key, str); \ + ks_pool_free(&str); \ + } -#define SWCLT_JSON_MARSHAL_BOOL(key) \ - if (!(ks_json_padd_bool_to_object(pool, target, #key, source->key))) { \ - ks_json_delete(&target); \ - return NULL; \ - } +#define SWCLT_JSON_MARSHAL_BOOL(key) \ + ks_json_add_bool_to_object(target, #key, source->key); -#define SWCLT_JSON_MARSHAL_DOUBLE(key) \ - if (!(ks_json_padd_number_to_object(pool, target, #key, (double)source->key))) { \ - ks_json_delete(&target); \ - return NULL; \ - } +#define SWCLT_JSON_MARSHAL_DOUBLE(key) \ + ks_json_add_number_to_object(target, #key, (double)source->key); -#define SWCLT_JSON_MARSHAL_INT_EX(source_key, target_key) \ - if (!(ks_json_padd_number_to_object(pool, target, #target_key, (int)source->source_key))) { \ - ks_json_delete(&target); \ - return NULL; \ - } +#define SWCLT_JSON_MARSHAL_INT_EX(source_key, target_key) \ + ks_json_add_number_to_object(target, #target_key, (int)source->source_key); -#define SWCLT_JSON_MARSHAL_INT(key) \ - if (!(ks_json_padd_number_to_object(pool, target, #key, (int)source->key))) { \ - ks_json_delete(&target); \ - return NULL; \ - } +#define SWCLT_JSON_MARSHAL_INT(key) \ + ks_json_add_number_to_object(target, #key, (int)source->key); #define SWCLT_JSON_MARSHAL_CUSTOM(function_name, key) \ { \ ks_json_t *__custom_obj; \ - if (!(__custom_obj = function_name##_MARSHAL(pool, source->key))) { \ - ks_json_delete(&target); \ - return NULL; \ - } \ - if (!ks_json_add_item_to_object(target, #key, __custom_obj)) { \ + if (!(__custom_obj = function_name##_MARSHAL(source->key))) { \ ks_json_delete(&target); \ return NULL; \ } \ + ks_json_add_item_to_object(target, #key, __custom_obj); \ } #define SWCLT_JSON_MARSHAL_STRING_OPT(key) \ if (source->key) { \ - if (!(ks_json_padd_string_to_object(pool, target, #key, source->key))) { \ - ks_json_delete(&target); \ - return NULL; \ - } \ + ks_json_add_string_to_object(target, #key, source->key); \ source->key = NULL; \ } -#define SWCLT_JSON_MARSHAL_STRING(key) \ - if (!(ks_json_padd_string_to_object(pool, target, #key, source->key))) { \ - ks_json_delete(&target); \ - return NULL; \ - } +#define SWCLT_JSON_MARSHAL_STRING(key) \ + ks_json_add_string_to_object(target, #key, source->key); #define SWCLT_JSON_MARSHAL_ITEM_OPT(key) \ if (source->key) { \ - if (!(ks_json_add_item_to_object(target, #key, source->key))) { \ - ks_json_delete(&target); \ - return NULL; \ - } \ + ks_json_add_item_to_object(target, #key, source->key); \ source->key = NULL; \ } #define SWCLT_JSON_MARSHAL_ITEM(key) \ - if (!(ks_json_add_item_to_object(target, #key, source->key))){ \ - ks_json_delete(&target); \ - return NULL; \ - } \ + ks_json_add_item_to_object(target, #key, source->key); \ source->key = NULL; #define SWCLT_JSON_MARSHAL_END() \ diff --git a/inc/signalwire-client-c/blade/blade.h b/inc/signalwire-client-c/blade/blade.h index c5946bc..35da25b 100644 --- a/inc/signalwire-client-c/blade/blade.h +++ b/inc/signalwire-client-c/blade/blade.h @@ -22,11 +22,11 @@ #pragma once #define SWCLT_BLADE_VERSION_MAJOR 2 -#define SWCLT_BLADE_VERSION_MINOR 2 -#define SWCLT_BLADE_VERSION_REVISION 0 +#define SWCLT_BLADE_VERSION_MINOR 4 +#define SWCLT_BLADE_VERSION_REVISION 1 /* Define the default time to live amount for all blade commands */ -#define BLADE_DEFAULT_CMD_TTL_MS 5000 +#define BLADE_DEFAULT_CMD_TTL_MS 10000 #include "signalwire-client-c/blade/type.h" #include "signalwire-client-c/blade/connect.h" @@ -36,6 +36,7 @@ #include "signalwire-client-c/blade/broadcast.h" #include "signalwire-client-c/blade/execute.h" #include "signalwire-client-c/blade/identity.h" +#include "signalwire-client-c/blade/ping.h" #include "signalwire-client-c/blade/protocol.h" #include "signalwire-client-c/blade/register.h" #include "signalwire-client-c/blade/subscription.h" diff --git a/inc/signalwire-client-c/blade/broadcast.h b/inc/signalwire-client-c/blade/broadcast.h index af40087..bad5e5b 100644 --- a/inc/signalwire-client-c/blade/broadcast.h +++ b/inc/signalwire-client-c/blade/broadcast.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 SignalWire, Inc + * Copyright (c) 2018-2020 SignalWire, Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,7 +22,7 @@ #pragma once /* The method name for a broadcast request */ -static const char *BLADE_BROADCAST_METHOD = "blade.broadcast"; +#define BLADE_BROADCAST_METHOD "blade.broadcast" /* Flags for the command, in our case we don't get replies */ #define BLADE_BROADCAST_FLAGS SWCLT_CMD_FLAG_NOREPLY @@ -67,7 +67,7 @@ SWCLT_JSON_PARSE_END() * CREATE_BLADE_BROADCAST_CMD_ASYNC - Creates a command with a request * in it setup to submit a broadcast method to blade. */ -static inline swclt_cmd_t CREATE_BLADE_BROADCAST_CMD_ASYNC( +static inline swclt_cmd_t *CREATE_BLADE_BROADCAST_CMD_ASYNC( swclt_cmd_cb_t cb, void *cb_data, const char *protocol, @@ -78,11 +78,7 @@ static inline swclt_cmd_t CREATE_BLADE_BROADCAST_CMD_ASYNC( { ks_json_t *obj = NULL; blade_broadcast_rqu_t broadcast_rqu; - swclt_cmd_t cmd = KS_NULL_HANDLE; - ks_pool_t *pool; - - if (ks_pool_open(&pool)) - return cmd; + swclt_cmd_t *cmd = NULL; /* Fill in the broadcast request then marshal it, it will create copies * of all the fields so caller doesn't lose ownership here */ @@ -92,8 +88,7 @@ static inline swclt_cmd_t CREATE_BLADE_BROADCAST_CMD_ASYNC( broadcast_rqu.broadcaster_nodeid = broadcast_nodeid; broadcast_rqu.params = (!params || !*params) ? ks_json_create_object() : *params; - if (!(obj = BLADE_BROADCAST_RQU_MARSHAL(pool, &broadcast_rqu))) { - ks_pool_close(&pool); + if (!(obj = BLADE_BROADCAST_RQU_MARSHAL(&broadcast_rqu))) { /* Since params is last, on error here we can be sure params was * not freed so do not set the callers params to NULL */ @@ -107,7 +102,6 @@ static inline swclt_cmd_t CREATE_BLADE_BROADCAST_CMD_ASYNC( /* Now give it to the new command */ if (swclt_cmd_create_ex( &cmd, - &pool, cb, cb_data, BLADE_BROADCAST_METHOD, @@ -119,11 +113,10 @@ static inline swclt_cmd_t CREATE_BLADE_BROADCAST_CMD_ASYNC( done: ks_json_delete(&obj); - ks_pool_close(&pool); return cmd; } -static inline swclt_cmd_t CREATE_BLADE_BROADCAST_CMD( +static inline swclt_cmd_t *CREATE_BLADE_BROADCAST_CMD( const char *protocol, const char *channel, const char *event, diff --git a/inc/signalwire-client-c/blade/connect.h b/inc/signalwire-client-c/blade/connect.h index e369499..905cf8c 100644 --- a/inc/signalwire-client-c/blade/connect.h +++ b/inc/signalwire-client-c/blade/connect.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 SignalWire, Inc + * Copyright (c) 2018-2020 SignalWire, Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,7 +23,7 @@ #pragma once /* The method name for a connect request */ -static const char *BLADE_CONNECT_METHOD = "blade.connect"; +#define BLADE_CONNECT_METHOD "blade.connect" /* Flags for the command */ #define BLADE_CONNECT_FLAGS 0 @@ -41,17 +41,26 @@ typedef struct blade_connect_rqu_s { blade_version_t *version; ks_uuid_t sessionid; ks_json_t *authentication; + const char *agent; + const char *identity; + ks_json_t *network; } blade_connect_rqu_t; SWCLT_JSON_MARSHAL_BEG(BLADE_CONNECT_RQU, blade_connect_rqu_t) SWCLT_JSON_MARSHAL_CUSTOM(BLADE_VERSION, version) SWCLT_JSON_MARSHAL_UUID(sessionid) SWCLT_JSON_MARSHAL_ITEM_OPT(authentication) + SWCLT_JSON_MARSHAL_STRING_OPT(agent) + SWCLT_JSON_MARSHAL_STRING_OPT(identity) + SWCLT_JSON_MARSHAL_ITEM_OPT(network) SWCLT_JSON_MARSHAL_END() SWCLT_JSON_DESTROY_BEG(BLADE_CONNECT_RQU, blade_connect_rqu_t) SWCLT_JSON_DESTROY_CUSTOM(BLADE_VERSION, version) SWCLT_JSON_DESTROY_UUID(sessionid) + SWCLT_JSON_DESTROY_STRING(agent) + SWCLT_JSON_DESTROY_STRING(identity) + SWCLT_JSON_DESTROY_ITEM(network) SWCLT_JSON_DESTROY_END() SWCLT_JSON_ALLOC_BEG(BLADE_CONNECT_RQU, blade_connect_rqu_t) @@ -62,6 +71,9 @@ SWCLT_JSON_PARSE_BEG(BLADE_CONNECT_RQU, blade_connect_rqu_t) SWCLT_JSON_PARSE_CUSTOM(BLADE_VERSION, version) SWCLT_JSON_PARSE_UUID(sessionid) SWCLT_JSON_PARSE_ITEM_OPT(authentication) + SWCLT_JSON_PARSE_STRING_OPT(agent) + SWCLT_JSON_PARSE_STRING_OPT(identity) + SWCLT_JSON_PARSE_ITEM_OPT(network) SWCLT_JSON_PARSE_END() /* Define our reply structure */ @@ -121,35 +133,37 @@ SWCLT_JSON_PARSE_END() * CREATE_BLADE_CONNECT_CMD_ASYNC - Creates a command which holds * and owns the request json for a connect request. */ -static inline swclt_cmd_t CREATE_BLADE_CONNECT_CMD_ASYNC( +static inline swclt_cmd_t *CREATE_BLADE_CONNECT_CMD_ASYNC( + ks_pool_t *pool, ks_uuid_t previous_sessionid, ks_json_t **authentication, + const char *agent, + const char *identity, + ks_json_t *network, swclt_cmd_cb_t cb, void *cb_data) { blade_connect_rqu_t *connect_rqu = NULL; - swclt_cmd_t cmd = KS_NULL_HANDLE; + swclt_cmd_t *cmd = NULL; ks_json_t *obj = NULL; - ks_pool_t *pool; - - if (ks_pool_open(&pool)) - goto done; if (BLADE_CONNECT_RQU_ALLOC(pool, &connect_rqu)) goto done; connect_rqu->sessionid = previous_sessionid; + connect_rqu->agent = agent; + connect_rqu->identity = identity; + connect_rqu->network = ks_json_duplicate(network, KS_TRUE); if (authentication && *authentication) { connect_rqu->authentication = *authentication; *authentication = NULL; } - if (!(obj = BLADE_CONNECT_RQU_MARSHAL(pool, connect_rqu))) + if (!(obj = BLADE_CONNECT_RQU_MARSHAL(connect_rqu))) goto done; if (swclt_cmd_create_ex( &cmd, - &pool, cb, cb_data, BLADE_CONNECT_METHOD, @@ -160,15 +174,25 @@ static inline swclt_cmd_t CREATE_BLADE_CONNECT_CMD_ASYNC( goto done; done: - BLADE_CONNECT_RQU_DESTROY(&connect_rqu); + // These are not owned by the request, don't destroy them + if (connect_rqu) { + connect_rqu->agent = NULL; + connect_rqu->identity = NULL; + + BLADE_CONNECT_RQU_DESTROY(&connect_rqu); + } + ks_json_delete(&obj); - ks_pool_close(&pool); return cmd; } -static inline swclt_cmd_t CREATE_BLADE_CONNECT_CMD(ks_uuid_t previous_sessionid, - ks_json_t **authentication) +static inline swclt_cmd_t *CREATE_BLADE_CONNECT_CMD(ks_pool_t *pool, + ks_uuid_t previous_sessionid, + ks_json_t **authentication, + const char *agent, + const char *identity, + ks_json_t *network) { - return CREATE_BLADE_CONNECT_CMD_ASYNC(previous_sessionid, authentication, NULL, NULL); + return CREATE_BLADE_CONNECT_CMD_ASYNC(pool, previous_sessionid, authentication, agent, identity, network, NULL, NULL); } diff --git a/inc/signalwire-client-c/blade/disconnect.h b/inc/signalwire-client-c/blade/disconnect.h index 91d55a1..6da34ab 100644 --- a/inc/signalwire-client-c/blade/disconnect.h +++ b/inc/signalwire-client-c/blade/disconnect.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 SignalWire, Inc + * Copyright (c) 2018-2020 SignalWire, Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,7 +22,7 @@ #pragma once /* The method name for a disconnect request */ -static const char *BLADE_DISCONNECT_METHOD = "blade.disconnect"; +#define BLADE_DISCONNECT_METHOD "blade.disconnect" /* Flags for the command, in our case we don't get replies */ #define BLADE_DISCONNECT_FLAGS SWCLT_CMD_FLAG_NOREPLY @@ -48,23 +48,18 @@ SWCLT_JSON_PARSE_END() * CREATE_BLADE_DISCONNECT_CMD_ASYNC - Creates a command with a request * in it setup to submit a disconnect method to blade. */ -static inline swclt_cmd_t CREATE_BLADE_DISCONNECT_CMD_ASYNC( +static inline swclt_cmd_t *CREATE_BLADE_DISCONNECT_CMD_ASYNC( swclt_cmd_cb_t cb, void *cb_data) { ks_json_t *obj = NULL; blade_disconnect_rqu_t disconnect_rqu; - swclt_cmd_t cmd = KS_NULL_HANDLE; - ks_pool_t *pool; - - if (ks_pool_open(&pool)) - return cmd; + swclt_cmd_t *cmd = NULL; /* Fill in the disconnect request then marshal it, it will create copies * of all the fields so caller doesn't lose ownership here */ - if (!(obj = BLADE_DISCONNECT_RQU_MARSHAL(pool, &disconnect_rqu))) { - ks_pool_close(&pool); + if (!(obj = BLADE_DISCONNECT_RQU_MARSHAL(&disconnect_rqu))) { /* Since params is last, on error here we can be sure params was * not freed so do not set the callers params to NULL */ @@ -74,7 +69,6 @@ static inline swclt_cmd_t CREATE_BLADE_DISCONNECT_CMD_ASYNC( /* Now give it to the new command */ if (swclt_cmd_create_ex( &cmd, - &pool, cb, cb_data, BLADE_DISCONNECT_METHOD, @@ -86,11 +80,10 @@ static inline swclt_cmd_t CREATE_BLADE_DISCONNECT_CMD_ASYNC( done: ks_json_delete(&obj); - ks_pool_close(&pool); return cmd; } -static inline swclt_cmd_t CREATE_BLADE_DISCONNECT_CMD(void) +static inline swclt_cmd_t *CREATE_BLADE_DISCONNECT_CMD(void) { return CREATE_BLADE_DISCONNECT_CMD_ASYNC( NULL, diff --git a/inc/signalwire-client-c/blade/execute.h b/inc/signalwire-client-c/blade/execute.h index 0bb4a74..c06e7b6 100644 --- a/inc/signalwire-client-c/blade/execute.h +++ b/inc/signalwire-client-c/blade/execute.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 SignalWire, Inc + * Copyright (c) 2018-2019 SignalWire, Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,7 +23,7 @@ #pragma once /* The method name for a protocol request */ -static const char *BLADE_EXECUTE_METHOD = "blade.execute"; +#define BLADE_EXECUTE_METHOD "blade.execute" /* Flags for the command */ #define BLADE_EXECUTE_FLAGS 0 @@ -119,33 +119,25 @@ SWCLT_JSON_PARSE_END() * CREATE_BLADE_EXECUTE_CMD_ASYNC - Creates a command which holds * and owns the request json for an execute request. */ -#define CREATE_BLADE_EXECUTE_CMD_ASYNC(...) __CREATE_BLADE_EXECUTE_CMD_ASYNC( \ - __FILE__, __LINE__, __PRETTY_FUNCTION__, __VA_ARGS__) - -static inline swclt_cmd_t __CREATE_BLADE_EXECUTE_CMD_ASYNC( - const char *file, int line, const char *tag, +static inline swclt_cmd_t *CREATE_BLADE_EXECUTE_CMD_ASYNC( swclt_cmd_cb_t cb, void *cb_data, + const char *id, const char *responder, const char *protocol, const char *method, ks_json_t **params) { - swclt_cmd_t cmd; + swclt_cmd_t *cmd = NULL; ks_status_t status; ks_json_t *request; - ks_pool_t *pool; - - /* Forward declare the pool we're going to use for allocations relative to - * the command, we will hand this pool to the command and it will use it - * for its allocation */ - if ((status = ks_pool_open(&pool))) - return status; + ks_uuid_t msgid = ks_uuid_null(); + if (id) msgid = ks_uuid_from_str(id); request = ks_json_create_object(); - if (responder) ks_json_padd_string_to_object(pool, request, "responder_nodeid", responder); - ks_json_padd_string_to_object(pool, request, "protocol", protocol); - ks_json_padd_string_to_object(pool, request, "method", method); + if (responder) ks_json_add_string_to_object(request, "responder_nodeid", responder); + ks_json_add_string_to_object(request, "protocol", protocol); + ks_json_add_string_to_object(request, "method", method); ks_json_add_item_to_object(request, "params", *params); /* Clear callers ptr */ @@ -155,28 +147,27 @@ static inline swclt_cmd_t __CREATE_BLADE_EXECUTE_CMD_ASYNC( * and null out our ptr */ if ((status = swclt_cmd_create_ex( &cmd, - &pool, cb, cb_data, BLADE_EXECUTE_METHOD, &request, BLADE_EXECUTE_TTL_MS, BLADE_EXECUTE_FLAGS, - ks_uuid_null()))) { + msgid))) { ks_log(KS_LOG_WARNING, "Failed to allocate execute cmd: %lu", status); /* Safe to free this or at least attempt to, cmd will have set it to null if it * took ownership of it */ ks_json_delete(&request); - ks_pool_close(&pool); - return KS_NULL_HANDLE; + return NULL; } /* Phew, successfully allocated the command */ return cmd; } -static inline swclt_cmd_t CREATE_BLADE_EXECUTE_CMD( +static inline swclt_cmd_t *CREATE_BLADE_EXECUTE_CMD( + const char *id, const char *responder, const char *protocol, const char *method, @@ -185,6 +176,7 @@ static inline swclt_cmd_t CREATE_BLADE_EXECUTE_CMD( return CREATE_BLADE_EXECUTE_CMD_ASYNC( NULL, NULL, + id, responder, protocol, method, diff --git a/inc/signalwire-client-c/blade/identity.h b/inc/signalwire-client-c/blade/identity.h index de0a45a..2291122 100644 --- a/inc/signalwire-client-c/blade/identity.h +++ b/inc/signalwire-client-c/blade/identity.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 SignalWire, Inc + * Copyright (c) 2018-2020 SignalWire, Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,7 +23,7 @@ #pragma once /* The method name for an identity request */ -static const char *BLADE_IDENTITY_METHOD = "blade.identity"; +#define BLADE_IDENTITY_METHOD "blade.identity" /* Flags for the command */ #define BLADE_IDENTITY_FLAGS 0 @@ -58,43 +58,29 @@ SWCLT_JSON_PARSE_END() * CREATE_BLADE_IDENTITY_CMD_ASYNC - Creates a command which holds * and owns the request json for an identity request. */ -#define CREATE_BLADE_IDENTITY_CMD_ASYNC(...) __CREATE_BLADE_IDENTITY_CMD_ASYNC(__FILE__, __LINE__, __PRETTY_FUNCTION__, __VA_ARGS__) -static inline swclt_cmd_t __CREATE_BLADE_IDENTITY_CMD_ASYNC( - const char *file, int line, const char *tag, +static inline swclt_cmd_t *CREATE_BLADE_IDENTITY_CMD_ASYNC( swclt_cmd_cb_t cb, void *cb_data, const char *command, const char *identity) { - swclt_cmd_t cmd = KS_NULL_HANDLE; + swclt_cmd_t *cmd = NULL; ks_status_t status; ks_json_t *identities; ks_json_t *request; - ks_pool_t *pool; - - if (ks_pool_open(&pool)) - return cmd; /* Serialize the identity into an array */ - if (!(identities = __ks_json_create_array_inline( - pool, - file, - line, - tag, - 1, - ks_json_create_string(identity)))) { - ks_log(KS_LOG_WARNING, "Failed to marshal blade identity"); - ks_pool_close(&pool); + if (!(identities = ks_json_create_array())) { + ks_log(KS_LOG_WARNING, "Failed to allocate blade identity array"); return cmd; } + ks_json_add_string_to_array(identities, identity); if (!(request = BLADE_IDENTITY_RQU_MARSHAL( - pool, &(blade_identity_rqu_t){ - BLADE_IDENTITY_CMD_ADD, + command, identities}))) { ks_log(KS_LOG_WARNING, "Failed to allocate identity request"); - ks_pool_close(&pool); return cmd; } @@ -102,7 +88,6 @@ static inline swclt_cmd_t __CREATE_BLADE_IDENTITY_CMD_ASYNC( * and null out our ptr */ if ((status = swclt_cmd_create_ex( &cmd, - &pool, cb, cb_data, BLADE_IDENTITY_METHOD, @@ -115,15 +100,14 @@ static inline swclt_cmd_t __CREATE_BLADE_IDENTITY_CMD_ASYNC( /* Safe to free this or at least attempt to, cmd will have set it to null if it * took ownership of it */ ks_json_delete(&request); - ks_pool_close(&pool); - return KS_NULL_HANDLE; + return NULL; } /* Phew, successfully allocated the command */ return cmd; } -static inline swclt_cmd_t CREATE_BLADE_IDENTITY_CMD( +static inline swclt_cmd_t *CREATE_BLADE_IDENTITY_CMD( const char *command, const char *identity) { diff --git a/inc/signalwire-client-c/blade/netcast.h b/inc/signalwire-client-c/blade/netcast.h index ca0d31e..2ebe936 100644 --- a/inc/signalwire-client-c/blade/netcast.h +++ b/inc/signalwire-client-c/blade/netcast.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2019 SignalWire, Inc + * Copyright (c) 2018-2020 SignalWire, Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,31 +23,31 @@ #pragma once /* Define all our commands */ -static const char *BLADE_NETCAST_CMD_ROUTE_ADD = "route.add"; -static const char *BLADE_NETCAST_CMD_ROUTE_REMOVE = "route.remove"; -static const char *BLADE_NETCAST_CMD_IDENTITY_ADD = "identity.add"; -static const char *BLADE_NETCAST_CMD_IDENTITY_REMOVE = "identity.remove"; -static const char *BLADE_NETCAST_CMD_PROTOCOL_ADD = "protocol.add"; -static const char *BLADE_NETCAST_CMD_PROTOCOL_REMOVE = "protocol.remove"; -static const char *BLADE_NETCAST_CMD_PROTOCOL_UPDATE = "protocol.update"; -static const char *BLADE_NETCAST_CMD_PROTOCOL_PROVIDER_ADD = "protocol.provider.add"; -static const char *BLADE_NETCAST_CMD_PROTOCOL_PROVIDER_REMOVE = "protocol.provider.remove"; -static const char *BLADE_NETCAST_CMD_PROTOCOL_PROVIDER_RANK_UPDATE = "protocol.provider.rank.update"; -static const char *BLADE_NETCAST_CMD_PROTOCOL_PROVIDER_DATA_UPDATE = "protocol.provider.data.update"; -static const char *BLADE_NETCAST_CMD_PROTOCOL_CHANNEL_ADD = "protocol.channel.add"; -static const char *BLADE_NETCAST_CMD_PROTOCOL_CHANNEL_REMOVE = "protocol.channel.remove"; -static const char *BLADE_NETCAST_CMD_SUBSCRIPTION_ADD = "subscription.add"; -static const char *BLADE_NETCAST_CMD_SUBSCRIPTION_REMOVE = "subscription.remove"; -static const char *BLADE_NETCAST_CMD_AUTHORITY_ADD = "authority.add"; -static const char *BLADE_NETCAST_CMD_AUTHORITY_REMOVE = "authority.remove"; -static const char *BLADE_NETCAST_CMD_AUTHORIZATION_ADD = "authorization.add"; -static const char *BLADE_NETCAST_CMD_AUTHORIZATION_UPDATE = "authorization.update"; -static const char *BLADE_NETCAST_CMD_AUTHORIZATION_REMOVE = "authorization.remove"; -static const char *BLADE_NETCAST_CMD_ACCESS_ADD = "access.add"; -static const char *BLADE_NETCAST_CMD_ACCESS_REMOVE = "access.remove"; +#define BLADE_NETCAST_CMD_ROUTE_ADD "route.add" +#define BLADE_NETCAST_CMD_ROUTE_REMOVE "route.remove" +#define BLADE_NETCAST_CMD_IDENTITY_ADD "identity.add" +#define BLADE_NETCAST_CMD_IDENTITY_REMOVE "identity.remove" +#define BLADE_NETCAST_CMD_PROTOCOL_ADD "protocol.add" +#define BLADE_NETCAST_CMD_PROTOCOL_REMOVE "protocol.remove" +#define BLADE_NETCAST_CMD_PROTOCOL_UPDATE "protocol.update" +#define BLADE_NETCAST_CMD_PROTOCOL_PROVIDER_ADD "protocol.provider.add" +#define BLADE_NETCAST_CMD_PROTOCOL_PROVIDER_REMOVE "protocol.provider.remove" +#define BLADE_NETCAST_CMD_PROTOCOL_PROVIDER_RANK_UPDATE "protocol.provider.rank.update" +#define BLADE_NETCAST_CMD_PROTOCOL_PROVIDER_DATA_UPDATE "protocol.provider.data.update" +#define BLADE_NETCAST_CMD_PROTOCOL_CHANNEL_ADD "protocol.channel.add" +#define BLADE_NETCAST_CMD_PROTOCOL_CHANNEL_REMOVE "protocol.channel.remove" +#define BLADE_NETCAST_CMD_SUBSCRIPTION_ADD "subscription.add" +#define BLADE_NETCAST_CMD_SUBSCRIPTION_REMOVE "subscription.remove" +#define BLADE_NETCAST_CMD_AUTHORITY_ADD "authority.add" +#define BLADE_NETCAST_CMD_AUTHORITY_REMOVE "authority.remove" +#define BLADE_NETCAST_CMD_AUTHORIZATION_ADD "authorization.add" +#define BLADE_NETCAST_CMD_AUTHORIZATION_UPDATE "authorization.update" +#define BLADE_NETCAST_CMD_AUTHORIZATION_REMOVE "authorization.remove" +#define BLADE_NETCAST_CMD_ACCESS_ADD "access.add" +#define BLADE_NETCAST_CMD_ACCESS_REMOVE "access.remove" /* The method name for a netcast request */ -static const char *BLADE_NETCAST_METHOD = "blade.netcast"; +#define BLADE_NETCAST_METHOD "blade.netcast" /* Flags for the command, in our case we don't get replies */ #define BLADE_NETCAST_FLAGS SWCLT_CMD_FLAG_NOREPLY diff --git a/inc/signalwire-client-c/blade/ping.h b/inc/signalwire-client-c/blade/ping.h new file mode 100644 index 0000000..b7532bb --- /dev/null +++ b/inc/signalwire-client-c/blade/ping.h @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2018-2020 SignalWire, Inc + * + * 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. + */ +#pragma once + +/* The method name for a ping request */ +#define BLADE_PING_METHOD "blade.ping" + +/* Flags for the command, in our case we don't get replies */ +#define BLADE_PING_FLAGS SWCLT_CMD_FLAG_NOREPLY + +/* Default time to live for ping */ +#define BLADE_PING_TTL_MS BLADE_DEFAULT_CMD_TTL_MS + +/* Create our ping request template */ +typedef struct blade_ping_rqu_s { + double timestamp; + const char *payload; +} blade_ping_rqu_t; + +SWCLT_JSON_MARSHAL_BEG(BLADE_PING_RQU, blade_ping_rqu_t) + SWCLT_JSON_MARSHAL_DOUBLE(timestamp) + SWCLT_JSON_MARSHAL_STRING_OPT(payload) +SWCLT_JSON_MARSHAL_END() + +SWCLT_JSON_DESTROY_BEG(BLADE_PING_RQU, blade_ping_rqu_t) + SWCLT_JSON_DESTROY_DOUBLE(timestamp) + SWCLT_JSON_DESTROY_STRING(payload) +SWCLT_JSON_DESTROY_END() + +SWCLT_JSON_PARSE_BEG(BLADE_PING_RQU, blade_ping_rqu_t) + SWCLT_JSON_PARSE_DOUBLE_OPT(timestamp) + SWCLT_JSON_PARSE_STRING_OPT(payload) +SWCLT_JSON_PARSE_END() + +typedef struct blade_ping_rpl_s { + double timestamp; + const char *payload; +} blade_ping_rpl_t; + +SWCLT_JSON_MARSHAL_BEG(BLADE_PING_RPL, blade_ping_rpl_t) + SWCLT_JSON_MARSHAL_DOUBLE(timestamp) + SWCLT_JSON_MARSHAL_STRING_OPT(payload) +SWCLT_JSON_MARSHAL_END() + +SWCLT_JSON_DESTROY_BEG(BLADE_PING_RPL, blade_ping_rpl_t) + SWCLT_JSON_DESTROY_DOUBLE(timestamp) + SWCLT_JSON_DESTROY_STRING(payload) +SWCLT_JSON_DESTROY_END() + +SWCLT_JSON_PARSE_BEG(BLADE_PING_RPL, blade_ping_rpl_t) + SWCLT_JSON_PARSE_DOUBLE_OPT(timestamp) + SWCLT_JSON_PARSE_STRING_OPT(payload) +SWCLT_JSON_PARSE_END() + +/** + * CREATE_BLADE_PING_CMD_ASYNC - Creates a command with a request + * in it setup to submit a ping method to blade. + */ +static inline swclt_cmd_t *CREATE_BLADE_PING_CMD_ASYNC( + swclt_cmd_cb_t cb, + void *cb_data, + double timestamp, + const char *payload) +{ + ks_json_t *obj = NULL; + swclt_cmd_t *cmd = NULL; + + + /* Fill in the ping request then marshal it, it will create copies + * of all the fields so caller doesn't lose ownership here */ + + if (!(obj = BLADE_PING_RQU_MARSHAL(&(blade_ping_rqu_t){ + timestamp, + payload}))) { + + /* Since params is last, on error here we can be sure params was + * not freed so do not set the callers params to NULL */ + return cmd; + } + + /* Now give it to the new command */ + if (swclt_cmd_create_ex( + &cmd, + cb, + cb_data, + BLADE_PING_METHOD, + &obj, + BLADE_PING_TTL_MS, + BLADE_PING_FLAGS, + ks_uuid_null())) + goto done; + +done: + ks_json_delete(&obj); + return cmd; +} + +static inline swclt_cmd_t *CREATE_BLADE_PING_CMD(double timestamp, const char *payload) +{ + return CREATE_BLADE_PING_CMD_ASYNC( + NULL, + NULL, + timestamp, + payload); +} diff --git a/inc/signalwire-client-c/blade/protocol.h b/inc/signalwire-client-c/blade/protocol.h index 8b43ef1..233d64f 100644 --- a/inc/signalwire-client-c/blade/protocol.h +++ b/inc/signalwire-client-c/blade/protocol.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 SignalWire, Inc + * Copyright (c) 2018-2020 SignalWire, Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,7 +23,7 @@ #pragma once /* The method name for a protocol request */ -static const char *BLADE_PROTOCOL_METHOD = "blade.protocol"; +#define BLADE_PROTOCOL_METHOD "blade.protocol" /* Flags for the command */ #define BLADE_PROTOCOL_FLAGS 0 @@ -31,11 +31,11 @@ static const char *BLADE_PROTOCOL_METHOD = "blade.protocol"; /* Default time to live for protocol */ #define BLADE_PROTOCOL_TTL_MS BLADE_DEFAULT_CMD_TTL_MS -static const char *BLADE_PROTOCOL_CMD_PROVIDER_ADD = "provider.add"; -static const char *BLADE_PROTOCOL_CMD_PROVIDER_REMOVE = "provider.remove"; -static const char *BLADE_PROTOCOL_CMD_PROVIDER_RANK_UPDATE = "provider.rank.update"; -static const char *BLADE_PROTOCOL_CMD_CHANNEL_ADD = "channel.add"; -static const char *BLADE_PROTOCOL_CMD_CHANNEL_REMOVE = "channel.remove"; +#define BLADE_PROTOCOL_CMD_PROVIDER_ADD "provider.add" +#define BLADE_PROTOCOL_CMD_PROVIDER_REMOVE "provider.remove" +#define BLADE_PROTOCOL_CMD_PROVIDER_RANK_UPDATE "provider.rank.update" +#define BLADE_PROTOCOL_CMD_CHANNEL_ADD "channel.add" +#define BLADE_PROTOCOL_CMD_CHANNEL_REMOVE "channel.remove" typedef struct blade_protocol_rqu_s { const char *command; @@ -128,9 +128,7 @@ SWCLT_JSON_PARSE_END() * Creates a command which holds and owns the request json for a protocol provider add * request, also takes owner of the provider data and channels json passed in */ -#define CREATE_BLADE_PROTOCOL_PROVIDER_ADD_CMD_ASYNC(...) __CREATE_BLADE_PROTOCOL_PROVIDER_ADD_CMD_ASYNC(__FILE__, __LINE__, __PRETTY_FUNCTION__, __VA_ARGS__) -static inline swclt_cmd_t __CREATE_BLADE_PROTOCOL_PROVIDER_ADD_CMD_ASYNC( - const char *file, int line, const char *tag, +static inline swclt_cmd_t *CREATE_BLADE_PROTOCOL_PROVIDER_ADD_CMD_ASYNC( swclt_cmd_cb_t cb, void *cb_data, const char *protocol, @@ -142,17 +140,12 @@ static inline swclt_cmd_t __CREATE_BLADE_PROTOCOL_PROVIDER_ADD_CMD_ASYNC( int rank, ks_json_t **data) { - swclt_cmd_t cmd = KS_NULL_HANDLE; + swclt_cmd_t *cmd = NULL; ks_status_t status; ks_json_t *params; ks_json_t *request; - ks_pool_t *pool; - if (ks_pool_open(&pool)) - return cmd; - - if (!(params = BLADE_PROTOCOL_PROVIDER_ADD_PARAM_MARSHAL(pool, - &(blade_protocol_provider_add_param_t){ + if (!(params = BLADE_PROTOCOL_PROVIDER_ADD_PARAM_MARSHAL(&(blade_protocol_provider_add_param_t){ default_method_execute_access, default_channel_subscribe_access, default_channel_broadcast_access, @@ -164,7 +157,6 @@ static inline swclt_cmd_t __CREATE_BLADE_PROTOCOL_PROVIDER_ADD_CMD_ASYNC( /* Since provider_data and channels was at the end of the macro declartaion, it will have * not touched it and we will not indicate we've taken ownership of it */ - ks_pool_close(&pool); return cmd; } @@ -173,7 +165,6 @@ static inline swclt_cmd_t __CREATE_BLADE_PROTOCOL_PROVIDER_ADD_CMD_ASYNC( if (channels) *channels = NULL; if (!(request = BLADE_PROTOCOL_RQU_MARSHAL( - pool, &(blade_protocol_rqu_t){ BLADE_PROTOCOL_CMD_PROVIDER_ADD, protocol, @@ -182,7 +173,6 @@ static inline swclt_cmd_t __CREATE_BLADE_PROTOCOL_PROVIDER_ADD_CMD_ASYNC( /* Since params is allocated in the same pool, nothing to worry about with ownership */ ks_json_delete(¶ms); - ks_pool_close(&pool); return cmd; } @@ -190,7 +180,6 @@ static inline swclt_cmd_t __CREATE_BLADE_PROTOCOL_PROVIDER_ADD_CMD_ASYNC( * and null out our ptr */ if ((status = swclt_cmd_create_ex( &cmd, - &pool, cb, cb_data, BLADE_PROTOCOL_METHOD, @@ -203,7 +192,6 @@ static inline swclt_cmd_t __CREATE_BLADE_PROTOCOL_PROVIDER_ADD_CMD_ASYNC( /* Safe to free this or at least attempt to, cmd will have set it to null if it * took ownership of it */ ks_json_delete(&request); - ks_pool_close(&pool); return cmd; } @@ -211,7 +199,7 @@ static inline swclt_cmd_t __CREATE_BLADE_PROTOCOL_PROVIDER_ADD_CMD_ASYNC( return cmd; } -static inline swclt_cmd_t CREATE_BLADE_PROTOCOL_PROVIDER_ADD_CMD( +static inline swclt_cmd_t *CREATE_BLADE_PROTOCOL_PROVIDER_ADD_CMD( const char *protocol, blade_access_control_t default_method_execute_access, blade_access_control_t default_channel_subscribe_access, @@ -240,23 +228,16 @@ static inline swclt_cmd_t CREATE_BLADE_PROTOCOL_PROVIDER_ADD_CMD( * Creates a command which holds and owns the request json for a protocol provider add * request, also takes owner of the provider data and channels json passed in */ -#define CREATE_BLADE_PROTOCOL_PROVIDER_REMOVE_CMD_ASYNC(...) __CREATE_BLADE_PROTOCOL_PROVIDER_REMOVE_CMD_ASYNC(__FILE__, __LINE__, __PRETTY_FUNCTION__, __VA_ARGS__) -static inline swclt_cmd_t __CREATE_BLADE_PROTOCOL_PROVIDER_REMOVE_CMD_ASYNC( - const char *file, int line, const char *tag, +static inline swclt_cmd_t *CREATE_BLADE_PROTOCOL_PROVIDER_REMOVE_CMD_ASYNC( swclt_cmd_cb_t cb, void *cb_data, const char *protocol) { - swclt_cmd_t cmd = KS_NULL_HANDLE; + swclt_cmd_t *cmd = NULL; ks_status_t status; ks_json_t *request; - ks_pool_t *pool; - - if (ks_pool_open(&pool)) - return cmd; if (!(request = BLADE_PROTOCOL_RQU_MARSHAL( - pool, &(blade_protocol_rqu_t){ BLADE_PROTOCOL_CMD_PROVIDER_REMOVE, protocol, @@ -264,7 +245,6 @@ static inline swclt_cmd_t __CREATE_BLADE_PROTOCOL_PROVIDER_REMOVE_CMD_ASYNC( ks_log(KS_LOG_WARNING, "Failed to allocate protocol request"); /* Since params is allocated in the same pool, nothing to worry about with ownership */ - ks_pool_close(&pool); return cmd; } @@ -272,7 +252,6 @@ static inline swclt_cmd_t __CREATE_BLADE_PROTOCOL_PROVIDER_REMOVE_CMD_ASYNC( * and null out our ptr */ if ((status = swclt_cmd_create_ex( &cmd, - &pool, cb, cb_data, BLADE_PROTOCOL_METHOD, @@ -285,7 +264,6 @@ static inline swclt_cmd_t __CREATE_BLADE_PROTOCOL_PROVIDER_REMOVE_CMD_ASYNC( /* Safe to free this or at least attempt to, cmd will have set it to null if it * took ownership of it */ ks_json_delete(&request); - ks_pool_close(&pool); return cmd; } @@ -293,7 +271,7 @@ static inline swclt_cmd_t __CREATE_BLADE_PROTOCOL_PROVIDER_REMOVE_CMD_ASYNC( return cmd; } -static inline swclt_cmd_t CREATE_BLADE_PROTOCOL_PROVIDER_REMOVE_CMD(const char *protocol) +static inline swclt_cmd_t *CREATE_BLADE_PROTOCOL_PROVIDER_REMOVE_CMD(const char *protocol) { return CREATE_BLADE_PROTOCOL_PROVIDER_REMOVE_CMD_ASYNC( NULL, @@ -305,35 +283,27 @@ static inline swclt_cmd_t CREATE_BLADE_PROTOCOL_PROVIDER_REMOVE_CMD(const char * * CREATE_BLADE_PROTOCOL_PROVIDER_RANK_UPDATE_CMD_ASYNC * Creates a command which holds and owns the request json for a protocol provider rank update request */ -#define CREATE_BLADE_PROTOCOL_PROVIDER_RANK_UPDATE_CMD_ASYNC(...) __CREATE_BLADE_PROTOCOL_PROVIDER_RANK_UPDATE_CMD_ASYNC(__FILE__, __LINE__, __PRETTY_FUNCTION__, __VA_ARGS__) -static inline swclt_cmd_t __CREATE_BLADE_PROTOCOL_PROVIDER_RANK_UPDATE_CMD_ASYNC( - const char *file, int line, const char *tag, +static inline swclt_cmd_t *CREATE_BLADE_PROTOCOL_PROVIDER_RANK_UPDATE_CMD_ASYNC( swclt_cmd_cb_t cb, void *cb_data, const char *protocol, int rank) { - swclt_cmd_t cmd = KS_NULL_HANDLE; + swclt_cmd_t *cmd = NULL; ks_status_t status; ks_json_t *params; ks_json_t *request; - ks_pool_t *pool; - - if (ks_pool_open(&pool)) - return cmd; - if (!(params = BLADE_PROTOCOL_PROVIDER_RANK_UPDATE_PARAM_MARSHAL(pool, &(blade_protocol_provider_rank_update_param_t){ rank }))) { + if (!(params = BLADE_PROTOCOL_PROVIDER_RANK_UPDATE_PARAM_MARSHAL(&(blade_protocol_provider_rank_update_param_t){ rank }))) { ks_log(KS_LOG_WARNING, "Failed to allocate protocol request params"); /* Since provider_data and channels was at the end of the macro declartaion, it will have * not touched it and we will not indicate we've taken ownership of it */ - ks_pool_close(&pool); return cmd; } /* We have taken ownership of the provider_data and channels from here on out */ if (!(request = BLADE_PROTOCOL_RQU_MARSHAL( - pool, &(blade_protocol_rqu_t){ BLADE_PROTOCOL_CMD_PROVIDER_RANK_UPDATE, protocol, @@ -342,7 +312,6 @@ static inline swclt_cmd_t __CREATE_BLADE_PROTOCOL_PROVIDER_RANK_UPDATE_CMD_ASYNC /* Since params is allocated in the same pool, nothing to worry about with ownership */ ks_json_delete(¶ms); - ks_pool_close(&pool); return cmd; } @@ -350,7 +319,6 @@ static inline swclt_cmd_t __CREATE_BLADE_PROTOCOL_PROVIDER_RANK_UPDATE_CMD_ASYNC * and null out our ptr */ if ((status = swclt_cmd_create_ex( &cmd, - &pool, cb, cb_data, BLADE_PROTOCOL_METHOD, @@ -363,7 +331,6 @@ static inline swclt_cmd_t __CREATE_BLADE_PROTOCOL_PROVIDER_RANK_UPDATE_CMD_ASYNC /* Safe to free this or at least attempt to, cmd will have set it to null if it * took ownership of it */ ks_json_delete(&request); - ks_pool_close(&pool); return cmd; } @@ -371,7 +338,7 @@ static inline swclt_cmd_t __CREATE_BLADE_PROTOCOL_PROVIDER_RANK_UPDATE_CMD_ASYNC return cmd; } -static inline swclt_cmd_t CREATE_BLADE_PROTOCOL_PROVIDER_RANK_UPDATE_CMD( +static inline swclt_cmd_t *CREATE_BLADE_PROTOCOL_PROVIDER_RANK_UPDATE_CMD( const char *protocol, int rank) { diff --git a/inc/signalwire-client-c/blade/subscription.h b/inc/signalwire-client-c/blade/subscription.h index 7f2df4d..e2c885f 100644 --- a/inc/signalwire-client-c/blade/subscription.h +++ b/inc/signalwire-client-c/blade/subscription.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 SignalWire, Inc + * Copyright (c) 2018-2020 SignalWire, Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,11 +24,11 @@ KS_BEGIN_EXTERN_C -static const char *BLADE_SUBSCRIPTION_CMD_ADD = "add"; -static const char *BLADE_SUBSCRIPTION_CMD_REMOVE = "remove"; +#define BLADE_SUBSCRIPTION_CMD_ADD "add" +#define BLADE_SUBSCRIPTION_CMD_REMOVE "remove" /* The method name for a subscription request */ -static const char *BLADE_SUBSCRIPTION_METHOD = "blade.subscription"; +#define BLADE_SUBSCRIPTION_METHOD "blade.subscription" /* Flags for the command */ #define BLADE_SUBSCRIPTION_FLAGS 0 @@ -85,33 +85,33 @@ SWCLT_JSON_PARSE_END() * CREATE_BLADE_SUBSCRIPTION_CMD_ASYNC - Creates a command which holds * and owns the request json for a subscription request. */ -static inline swclt_cmd_t CREATE_BLADE_SUBSCRIPTION_CMD_ASYNC( +static inline swclt_cmd_t *CREATE_BLADE_SUBSCRIPTION_CMD_ASYNC( swclt_cmd_cb_t cb, void *cb_data, const char *command, const char *protocol, const char *channel) { - ks_pool_t *pool; ks_json_t *request_obj; ks_status_t status; - swclt_cmd_t cmd = KS_NULL_HANDLE; - blade_subscription_rqu_t request = { 0 }; + swclt_cmd_t *cmd = NULL; - if (ks_pool_open(&pool)) - return cmd; + ks_json_t *channels = ks_json_create_array(); /* Create a blade subscription request structure then marshal it */ - request.command = command; - request.protocol = protocol; - request.channels = ks_json_pcreate_array_inline(pool, 1, ks_json_pcreate_string(pool, channel)); + blade_subscription_rqu_t request = { + command, + protocol, + channels, + }; + + ks_json_add_string_to_array(channels, channel); - if (!(request_obj = BLADE_SUBSCRIPTION_RQU_MARSHAL(pool, &request))) { + if (!(request_obj = BLADE_SUBSCRIPTION_RQU_MARSHAL(&request))) { ks_log(KS_LOG_WARNING, "Failed to create subscription request"); /* Don't forget to free the channels we instantiated inline above */ ks_json_delete(&request.channels); - ks_pool_close(&pool); return cmd; } @@ -120,25 +120,23 @@ static inline swclt_cmd_t CREATE_BLADE_SUBSCRIPTION_CMD_ASYNC( /* Now wrap it in a command */ if ((status = swclt_cmd_create_ex( &cmd, - &pool, cb, cb_data, BLADE_SUBSCRIPTION_METHOD, &request_obj, - BLADE_SUBSCRIPTION_FLAGS, BLADE_SUBSCRIPTION_TTL_MS, + BLADE_SUBSCRIPTION_FLAGS, ks_uuid_null()))) { ks_log(KS_LOG_WARNING, "Failed to allocate subscription command: %lu", status); ks_json_delete(&request_obj); - ks_pool_close(&pool); - return status; + return cmd; } /* Success */ return cmd; } -static inline swclt_cmd_t CREATE_BLADE_SUBSCRIPTION_CMD( +static inline swclt_cmd_t *CREATE_BLADE_SUBSCRIPTION_CMD( const char *command, const char *protocol, const char *channel) @@ -153,20 +151,19 @@ static inline swclt_cmd_t CREATE_BLADE_SUBSCRIPTION_CMD( /* Creates a subscription request */ static inline ks_json_t *BLADE_SUBSCRIPTION_RQU( - ks_pool_t *pool, const char * const command, const char * const protocol, const char * const channel, blade_access_control_t broadcast_access, blade_access_control_t subscribe_access) { - ks_json_t *request = ks_json_pcreate_object(pool); - ks_json_t *channels = ks_json_pcreate_array(pool); - ks_json_padd_string_to_array(pool, channels, channel); - ks_json_padd_string_to_object(pool, request, "command", command); - ks_json_padd_string_to_object(pool, request, "protocol", protocol); + ks_json_t *request = ks_json_create_object(); + ks_json_t *channels = ks_json_add_array_to_object(request, "channels"); + ks_json_add_string_to_object(request, "command", command); + ks_json_add_string_to_object(request, "protocol", protocol); ks_json_add_string_to_array(channels, channel); - ks_json_add_item_to_object(request, "channels", channels); + (void)(broadcast_access); // unused + (void)(subscribe_access); // unused return request; } diff --git a/inc/signalwire-client-c/blade/util.h b/inc/signalwire-client-c/blade/util.h index 9430810..0774f76 100644 --- a/inc/signalwire-client-c/blade/util.h +++ b/inc/signalwire-client-c/blade/util.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 SignalWire, Inc + * Copyright (c) 2018-2020 SignalWire, Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -44,8 +44,12 @@ static inline uint32_t BLADE_METHOD_FLAGS(const char * const method) return BLADE_EXECUTE_FLAGS; else if (!strcmp(method, BLADE_SUBSCRIPTION_METHOD)) return BLADE_SUBSCRIPTION_FLAGS; - else - ks_abort_fmt("Unhandled method: %s", method); + else if (!strcmp(method, BLADE_PING_METHOD)) + return BLADE_PING_FLAGS; + else { + ks_log(KS_LOG_WARNING, "Unsupported blade method: %s", method); + return 0; + } } KS_END_EXTERN_C diff --git a/inc/signalwire-client-c/client.h b/inc/signalwire-client-c/client.h index 57aa2c4..8f3c488 100644 --- a/inc/signalwire-client-c/client.h +++ b/inc/signalwire-client-c/client.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 SignalWire, Inc + * Copyright (c) 2018-2020 SignalWire, Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -27,10 +27,6 @@ #include "signalwire-client-c/export.h" #include "signalwire-client-c/init.h" -#include "signalwire-client-c/handles.h" -#include "signalwire-client-c/handle_state.h" -#include "signalwire-client-c/handle_base.h" - #include "signalwire-client-c/transport/frame.h" #include "signalwire-client-c/command.h" #include "signalwire-client-c/transport/websocket.h" @@ -39,15 +35,12 @@ #include "signalwire-client-c/signalwire/signalwire.h" #include "signalwire-client-c/identity.h" #include "signalwire-client-c/config.h" -#include "signalwire-client-c/handle_type.h" #include "signalwire-client-c/connection.h" #include "signalwire-client-c/subscription.h" #include "signalwire-client-c/pmethod.h" #include "signalwire-client-c/nodestore.h" #include "signalwire-client-c/session.h" - -#include "signalwire-client-c/handle_manager.h" -#include "signalwire-client-c/handle_monitor.h" +#include "signalwire-client-c/version.h" /* Utility */ #include "signalwire-client-c/ssl.h" diff --git a/inc/signalwire-client-c/command.h b/inc/signalwire-client-c/command.h index 3f0569e..af105ac 100644 --- a/inc/signalwire-client-c/command.h +++ b/inc/signalwire-client-c/command.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 SignalWire, Inc + * Copyright (c) 2018-2022 SignalWire, Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,11 +22,6 @@ #pragma once -/* Obfuscate command internals */ -typedef struct swclt_cmd_ctx swclt_cmd_ctx_t; - -typedef ks_handle_t swclt_cmd_t; - /* A command is one of three types, request, result, error */ typedef enum { SWCLT_CMD_TYPE_INVALID, @@ -36,6 +31,55 @@ typedef enum { SWCLT_CMD_TYPE_FAILURE, /* A failure ocurred that prevented remote from responding */ } SWCLT_CMD_TYPE; +typedef struct swclt_cmd swclt_cmd_t; + +/* Flags control aspects of the command such as whether a reply is expected */ +#define SWCLT_CMD_FLAG_NOREPLY KS_BIT_FLAG(0) +#define SWCLT_CMD_FLAG_MAX 1 + +struct swclt_cmd_reply { + ks_pool_t *pool; + ks_json_t *json; + SWCLT_CMD_TYPE type; + ks_status_t failure_status; + char *failure_reason; +}; + +typedef struct swclt_cmd_reply swclt_cmd_reply_t; +typedef struct swclt_cmd_future swclt_cmd_future_t; + +typedef ks_status_t (*swclt_cmd_parse_cb_t)(ks_pool_t *pool, ks_json_t * const payload, void **structure); +typedef void (*swclt_cmd_cb_t)(swclt_cmd_reply_t *cmd_reply, void *cb_data); + +/* Ths client command context represents a commands state */ +struct swclt_cmd { + ks_pool_t *pool; + /* When a command completes it calls the callback */ + swclt_cmd_cb_t cb; + void *cb_data; + + /* The request ids, generated once on allocation */ + ks_uuid_t id; + char *id_str; + + /* CMD flags e.g. whether we expect a reply or not */ + uint32_t flags : SWCLT_CMD_FLAG_MAX; + + /* Our method, established on construction */ + char *method; + + /* The request, a command always has one, as it is born from a request */ + ks_json_t *json; + + /* The command type can be request (when constructed) result (when remote replied + * with no error) and error (when remove replies with an error */ + SWCLT_CMD_TYPE type; + + /* This is the time to live value, when non zero, the command will fail if the + * response is not received within the appropriate window. */ + uint32_t response_ttl_ms; +}; + static inline const char * swclt_cmd_type_str(SWCLT_CMD_TYPE type) { switch (type) { @@ -55,98 +99,49 @@ static inline const char * swclt_cmd_type_str(SWCLT_CMD_TYPE type) } } -/* Flags control aspects of the command such as whether a reply is expected */ -#define SWCLT_CMD_FLAG_NOREPLY KS_BIT_FLAG(0) -#define SWCLT_CMD_FLAG_MAX 1 - -typedef ks_status_t (*swclt_cmd_parse_cb_t)(ks_pool_t *pool, const ks_json_t * const payload, void **structure); -typedef void (*swclt_cmd_cb_t)(swclt_cmd_t cmd, void *cb_data); - KS_BEGIN_EXTERN_C -SWCLT_DECLARE(ks_status_t) __swclt_cmd_set_cb(swclt_cmd_t cmd, swclt_cmd_cb_t cb, void *cb_data, - const char *file, int line, const char *tag); -SWCLT_DECLARE(ks_status_t) __swclt_cmd_cb(swclt_cmd_t cmd, swclt_cmd_cb_t *cb, void **cb_data, - const char *file, int line, const char *tag); +SWCLT_DECLARE(ks_status_t) swclt_cmd_set_cb(swclt_cmd_t *cmd, swclt_cmd_cb_t cb, void *cb_data); -/* Create a command from a request, optional caller pool */ -SWCLT_DECLARE(ks_status_t) __swclt_cmd_create(swclt_cmd_t *cmd, const char * const method, ks_json_t **request, - uint32_t response_ttl_ms, uint32_t flags, const char *file, int line, const char *tag); +SWCLT_DECLARE(ks_status_t) swclt_cmd_future_create(swclt_cmd_future_t **future, swclt_cmd_t *cmd); +SWCLT_DECLARE(ks_status_t) swclt_cmd_future_get(swclt_cmd_future_t *future, swclt_cmd_reply_t **reply); +SWCLT_DECLARE(ks_uuid_t) swclt_cmd_future_get_id(swclt_cmd_future_t *future); +SWCLT_DECLARE(ks_status_t) swclt_cmd_future_destroy(swclt_cmd_future_t **future); -#define swclt_cmd_create(cmd, method, request, response_ttl_ms, flags) \ - __swclt_cmd_create(cmd, method, request, response_ttl_ms, flags, __FILE__, __LINE__, __PRETTY_FUNCTION__) +SWCLT_DECLARE(ks_status_t) swclt_cmd_reply_create(swclt_cmd_reply_t **reply); +SWCLT_DECLARE(ks_status_t) swclt_cmd_reply_ok(swclt_cmd_reply_t *reply); +SWCLT_DECLARE(ks_status_t) swclt_cmd_reply_parse(swclt_cmd_reply_t *reply, ks_pool_t *pool, swclt_cmd_parse_cb_t parse_cb, void **structure); +SWCLT_DECLARE(ks_status_t) swclt_cmd_reply_destroy(swclt_cmd_reply_t **reply); -/* Create a command from a request, and set a callback for async handling, optional caller pool */ -SWCLT_DECLARE(ks_status_t) __swclt_cmd_create_ex(swclt_cmd_t *cmd, ks_pool_t **pool, swclt_cmd_cb_t cb, void *cb_data, - const char * const method, ks_json_t **request, uint32_t response_ttl_ms, uint32_t flags, ks_uuid_t id, - const char *file, int line, const char *tag); +/* Create a command from a request, mandatory caller pool */ +SWCLT_DECLARE(ks_status_t) swclt_cmd_create(swclt_cmd_t **cmd, const char * const method, ks_json_t **request, + uint32_t response_ttl_ms, uint32_t flags); -#define swclt_cmd_create_ex(cmd, pool, cb, cb_data, method, request,response_ttl_ms, flags, id) \ - __swclt_cmd_create_ex(cmd, pool, cb, cb_data, method, request, response_ttl_ms, flags, id, __FILE__, __LINE__, __PRETTY_FUNCTION__) +/* Create a command from a request, and set a callback for async handling, mandatory caller pool */ +SWCLT_DECLARE(ks_status_t) swclt_cmd_create_ex(swclt_cmd_t **cmd, swclt_cmd_cb_t cb, void *cb_data, + const char * const method, ks_json_t **request, uint32_t response_ttl_ms, uint32_t flags, ks_uuid_t id); -/* Create a request from af rame w/optional callback */ -SWCLT_DECLARE(ks_status_t) __swclt_cmd_create_frame( - swclt_cmd_t *cmd, +/* Create a request from a frame w/optional callback */ +SWCLT_DECLARE(ks_status_t) swclt_cmd_create_frame( + swclt_cmd_t **cmd, swclt_cmd_cb_t cb, void *cb_data, - swclt_frame_t frame, + swclt_frame_t *frame, uint32_t response_ttl_ms, - uint32_t flags, - const char *file, - int line, - const char *tag); - -#define swclt_cmd_create_frame(cmd, cb, cb_data, frame, frame_response_ttl_ms, flags) \ - __swclt_cmd_create_frame(cmd, cb, cb_data, frame, frame_response_ttl_ms, flags, __FILE__, __LINE__, __PRETTY_FUNCTION__) - -SWCLT_DECLARE(ks_status_t) __swclt_cmd_print(swclt_cmd_t cmd, ks_pool_t *pool, char **string, const char *file, int line, const char *tag); -SWCLT_DECLARE(ks_status_t) __swclt_cmd_type(swclt_cmd_t cmd, SWCLT_CMD_TYPE *type, const char *file, int line, const char *tag); -SWCLT_DECLARE(ks_status_t) __swclt_cmd_set_result(swclt_cmd_t cmd, ks_json_t **result, const char *file, int line, const char *tag); -SWCLT_DECLARE(ks_status_t) __swclt_cmd_set_error(swclt_cmd_t cmd, ks_json_t **result, const char *file, int line, const char *tag); -SWCLT_DECLARE(ks_status_t) __swclt_cmd_lookup_parse(const char *file, int line, const char *tag, swclt_cmd_t cmd, ks_pool_t *pool, swclt_cmd_parse_cb_t parse_cb, void **structure, int components, ...); -SWCLT_DECLARE(ks_status_t) __swclt_cmd_error(swclt_cmd_t cmd, const ks_json_t **error, const char *file, int line, const char *tag); -SWCLT_DECLARE(ks_status_t) __swclt_cmd_result(swclt_cmd_t cmd, const ks_json_t **result, const char *file, int line, const char *tag); -SWCLT_DECLARE(ks_status_t) __swclt_cmd_request(swclt_cmd_t cmd, const ks_json_t **request, const char *file, int line, const char *tag); -SWCLT_DECLARE(ks_status_t) __swclt_cmd_id(swclt_cmd_t cmd, ks_uuid_t *id, const char *file, int line, const char *tag); -SWCLT_DECLARE(ks_status_t) __swclt_cmd_ttl(swclt_cmd_t cmd, uint32_t *response_ttl_ms, const char *file, int line, const char *tag); -SWCLT_DECLARE(ks_status_t) __swclt_cmd_set_ttl(swclt_cmd_t cmd, uint32_t response_ttl_ms, const char *file, int line, const char *tag); -SWCLT_DECLARE(ks_status_t) __swclt_cmd_submit_time(swclt_cmd_t cmd, ks_time_t *submit_time, const char *file, int line, const char *tag); -SWCLT_DECLARE(ks_status_t) __swclt_cmd_set_submit_time(swclt_cmd_t cmd, ks_time_t submit_time, const char *file, int line, const char *tag); -SWCLT_DECLARE(ks_status_t) __swclt_cmd_failure_info(swclt_cmd_t cmd, ks_status_t *failure_status, const char **failure_reason, const char *file, int line, const char *tag); -SWCLT_DECLARE(ks_status_t) __swclt_cmd_flags(swclt_cmd_t cmd, uint32_t *flags, const char *file, int line, const char *tag); -SWCLT_DECLARE(ks_status_t) __swclt_cmd_method(swclt_cmd_t cmd, const char **method, const char *file, int line, const char *tag); -SWCLT_DECLARE(ks_status_t) __swclt_cmd_parse_reply_frame(swclt_cmd_t cmd, swclt_frame_t frame, ks_bool_t *async, const char *file, int line, const char *tag); - -#define swclt_cmd_set_cb(cmd, cb, cb_data) __swclt_cmd_set_cb(cmd, cb, cb_data, __FILE__, __LINE__, __PRETTY_FUNCTION__) -#define swclt_cmd_cb(cmd, cb, cb_data) __swclt_cmd_cb(cmd, cb, cb_data, __FILE__, __LINE__, __PRETTY_FUNCTION__) -#define swclt_cmd_print(cmd, pool, string) __swclt_cmd_print(cmd, pool, string, __FILE__, __LINE__, __PRETTY_FUNCTION__) -#define swclt_cmd_type(cmd, type) __swclt_cmd_type(cmd, type, __FILE__, __LINE__, __PRETTY_FUNCTION__) -#define swclt_cmd_result(cmd, result) __swclt_cmd_result(cmd, result, __FILE__, __LINE__, __PRETTY_FUNCTION__) -#define swclt_cmd_set_result(cmd, result) __swclt_cmd_set_result(cmd, result, __FILE__, __LINE__, __PRETTY_FUNCTION__) -#define swclt_cmd_set_error(cmd, error) __swclt_cmd_set_error(cmd, error, __FILE__, __LINE__, __PRETTY_FUNCTION__) -#define swclt_cmd_lookup_parse_m(cmd, pool, parse_cb, structure, components, ...) __swclt_cmd_lookup_parse(__FILE__, __LINE__, __PRETTY_FUNCTION__, cmd, pool, parse_cb, structure, components, __VA_ARGS__) -#define swclt_cmd_lookup_parse_s(cmd, pool, parse_cb, structure) __swclt_cmd_lookup_parse(__FILE__, __LINE__, __PRETTY_FUNCTION__, cmd, pool, parse_cb, structure, 0) -#define swclt_cmd_error(cmd, error) __swclt_cmd_error(cmd, error, __FILE__, __LINE__, __PRETTY_FUNCTION__) -#define swclt_cmd_request(cmd, request) __swclt_cmd_request(cmd, request, __FILE__, __LINE__, __PRETTY_FUNCTION__) -#define swclt_cmd_id(cmd, id) __swclt_cmd_id(cmd, id, __FILE__, __LINE__, __PRETTY_FUNCTION__) -#define swclt_cmd_ttl(cmd, response_ttl_ms) __swclt_cmd_ttl(cmd, response_ttl_ms, __FILE__, __LINE__, __PRETTY_FUNCTION__) -#define swclt_cmd_set_ttl(cmd, response_ttl_ms) __swclt_cmd_set_ttl(cmd, response_ttl_ms, __FILE__, __LINE__, __PRETTY_FUNCTION__) -#define swclt_cmd_submit_time(cmd, submit_time) __swclt_cmd_submit_time(cmd, submit_time, __FILE__, __LINE__, __PRETTY_FUNCTION__) -#define swclt_cmd_set_submit_time(cmd, submit_time) __swclt_cmd_set_submit_time(cmd, submit_time, __FILE__, __LINE__, __PRETTY_FUNCTION__) -#define swclt_cmd_failure_info(cmd, failure_status, failure_reason) __swclt_cmd_failure_info(cmd, failure_status, failure_reason, __FILE__, __LINE__, __PRETTY_FUNCTION__) -#define swclt_cmd_flags(cmd, flags) __swclt_cmd_flags(cmd, flags, __FILE__, __LINE__, __PRETTY_FUNCTION__) -#define swclt_cmd_method(cmd, method) __swclt_cmd_method(cmd, method, __FILE__, __LINE__, __PRETTY_FUNCTION__) -#define swclt_cmd_parse_reply_frame(cmd, frame, async) __swclt_cmd_parse_reply_frame(cmd, frame, async, __FILE__, __LINE__, __PRETTY_FUNCTION__) - -SWCLT_DECLARE(ks_status_t) __swclt_cmd_report_failure_fmt(const char *file, int line, const char *tag, swclt_cmd_t cmd, ks_status_t failure_status, const char *failure_fmt, ...); -#define swclt_cmd_report_failure_fmt_s(cmd, status, error_fmt) __swclt_cmd_report_failure_fmt(__FILE__, __LINE__, __PRETTY_FUNCTION__, cmd, status, error_fmt) -#define swclt_cmd_report_failure_fmt_m(cmd, status, error_fmt, ...) __swclt_cmd_report_failure_fmt(__FILE__, __LINE__, __PRETTY_FUNCTION__, cmd, status, error_fmt, __VA_ARGS__) - -SWCLT_DECLARE(ks_status_t) __swclt_cmd_report_failure(swclt_cmd_t cmd, ks_status_t failure_status, const char *failure_description, const char *file, int line, const char *tag); -#define swclt_cmd_report_failure(cmd, status, description) __swclt_cmd_report_failure(cmd, status, description, __FILE__, __LINE__, __PRETTY_FUNCTION__) - -#define swclt_cmd_get(cmd, contextP) __ks_handle_get(SWCLT_HTYPE_CMD, cmd, (ks_handle_base_t **)contextP, __FILE__, __LINE__, __PRETTY_FUNCTION__) -#define swclt_cmd_put(contextP) __ks_handle_put(SWCLT_HTYPE_CMD, (ks_handle_base_t **)contextP, __FILE__, __LINE__, __PRETTY_FUNCTION__) + uint32_t flags); + +/* Duplicate a command, excluding its callback data */ +SWCLT_DECLARE(swclt_cmd_t *) swclt_cmd_duplicate(swclt_cmd_t *cmd); +SWCLT_DECLARE(ks_status_t) swclt_cmd_destroy(swclt_cmd_t **cmd); +SWCLT_DECLARE(char *) swclt_cmd_describe(swclt_cmd_t *cmd); +SWCLT_DECLARE(ks_status_t) swclt_cmd_print(swclt_cmd_t *cmd, ks_pool_t *pool, char **string); +SWCLT_DECLARE(ks_status_t) swclt_cmd_set_result(swclt_cmd_t *cmd, ks_json_t **result); +SWCLT_DECLARE(ks_status_t) swclt_cmd_set_error(swclt_cmd_t *cmd, ks_json_t **result); +SWCLT_DECLARE(ks_status_t) swclt_cmd_set_ttl(swclt_cmd_t *cmd, uint32_t response_ttl_ms); +SWCLT_DECLARE(ks_status_t) swclt_cmd_parse_reply_frame(swclt_cmd_t *cmd, swclt_frame_t *frame); + +SWCLT_DECLARE(ks_status_t) swclt_cmd_report_failure_fmt(swclt_cmd_t *cmd, ks_status_t failure_status, const char *failure_fmt, ...); +SWCLT_DECLARE(ks_status_t) swclt_cmd_report_failure(swclt_cmd_t *cmd, ks_status_t failure_status, const char *failure_description); KS_END_EXTERN_C diff --git a/inc/signalwire-client-c/config.h b/inc/signalwire-client-c/config.h index bbbbc86..e806a49 100644 --- a/inc/signalwire-client-c/config.h +++ b/inc/signalwire-client-c/config.h @@ -41,5 +41,27 @@ SWCLT_DECLARE(const char *) swclt_config_get_cert_chain_path(swclt_config_t *con SWCLT_DECLARE(ks_status_t) swclt_config_set_cert_chain_path(swclt_config_t *config, const char *value); SWCLT_DECLARE(const char *) swclt_config_get_authentication(swclt_config_t *config); SWCLT_DECLARE(ks_status_t) swclt_config_set_authentication(swclt_config_t *config, const char *value); +SWCLT_DECLARE(const char *) swclt_config_get_agent(swclt_config_t *config); +SWCLT_DECLARE(ks_status_t) swclt_config_set_agent(swclt_config_t *config, const char *value); +SWCLT_DECLARE(const char *) swclt_config_get_identity(swclt_config_t *config); +SWCLT_DECLARE(ks_status_t) swclt_config_set_identity(swclt_config_t *config, const char *value); + +SWCLT_DECLARE(ks_status_t) swclt_config_set_default_network(swclt_config_t *config, ks_bool_t allData); +SWCLT_DECLARE(ks_bool_t) swclt_config_get_network_route_data(swclt_config_t *config); +SWCLT_DECLARE(void) swclt_config_set_network_route_data(swclt_config_t *config, ks_bool_t value); +SWCLT_DECLARE(ks_bool_t) swclt_config_get_network_route_add(swclt_config_t *config); +SWCLT_DECLARE(void) swclt_config_set_network_route_add(swclt_config_t *config, ks_bool_t value); +SWCLT_DECLARE(ks_bool_t) swclt_config_get_network_route_remove(swclt_config_t *config); +SWCLT_DECLARE(void) swclt_config_set_network_route_remove(swclt_config_t *config, ks_bool_t value); +SWCLT_DECLARE(ks_bool_t) swclt_config_get_network_authority_data(swclt_config_t *config); +SWCLT_DECLARE(void) swclt_config_set_network_authority_data(swclt_config_t *config, ks_bool_t value); +SWCLT_DECLARE(ks_bool_t) swclt_config_get_network_authority_add(swclt_config_t *config); +SWCLT_DECLARE(void) swclt_config_set_network_authority_add(swclt_config_t *config, ks_bool_t value); +SWCLT_DECLARE(ks_bool_t) swclt_config_get_network_authority_remove(swclt_config_t *config); +SWCLT_DECLARE(void) swclt_config_set_network_authority_remove(swclt_config_t *config, ks_bool_t value); +SWCLT_DECLARE(ks_bool_t) swclt_config_get_network_filtered_protocols(swclt_config_t *config); +SWCLT_DECLARE(void) swclt_config_set_network_filtered_protocols(swclt_config_t *config, ks_bool_t value); +SWCLT_DECLARE(void) swclt_config_add_network_protocol(swclt_config_t *config, const char *value); +SWCLT_DECLARE(void) swclt_config_remove_network_protocol(swclt_config_t *config, const char *value); KS_END_EXTERN_C diff --git a/inc/signalwire-client-c/connection.h b/inc/signalwire-client-c/connection.h index 4e77ca0..86c48f7 100644 --- a/inc/signalwire-client-c/connection.h +++ b/inc/signalwire-client-c/connection.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 SignalWire, Inc + * Copyright (c) 2018-2022 SignalWire, Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,40 +24,109 @@ KS_BEGIN_EXTERN_C -/* All connections are handles, opaque numbers that manage ref counts */ -typedef ks_handle_t swclt_conn_t; +typedef struct swclt_ttl_tracker swclt_ttl_tracker_t; -/* Obfuscate our connection internals */ -typedef struct swclt_conn_ctx swclt_conn_ctx_t; +typedef struct swclt_conn swclt_conn_t; -typedef ks_status_t (*swclt_conn_incoming_cmd_cb_t)(swclt_conn_t conn, swclt_cmd_t cmd, void *cb_data); -typedef ks_status_t (*swclt_conn_connect_cb_t)(swclt_conn_t conn, ks_json_t *error, blade_connect_rpl_t *connect_rpl, void *cb_data); +typedef ks_status_t (*swclt_conn_incoming_cmd_cb_t)(swclt_conn_t *conn, swclt_cmd_t *cmd, void *cb_data); +typedef ks_status_t (*swclt_conn_connect_cb_t)(swclt_conn_t *conn, ks_json_t *error, blade_connect_rpl_t *connect_rpl, void *cb_data); +typedef ks_status_t (*swclt_conn_failed_cb_t)(swclt_conn_t *conn, void *cb_data); +/* Information about this connection */ +typedef struct swclt_conn_info_s { + /* We also store a copy of the wss's info structure */ + swclt_wss_info_t wss; + + /* Pulled from the blade connect result */ + ks_uuid_t sessionid; + const char *nodeid; + const char *master_nodeid; +} swclt_conn_info_t; + +/* Ths client connection context represents a connections state */ +struct swclt_conn { + + ks_pool_t *pool; + + /* When we receive an incoming request we call this callback with the prepared command */ + swclt_conn_incoming_cmd_cb_t incoming_cmd_cb; + void *incoming_cmd_cb_data; + + /* Optional callbacks for getting the initial connect result payload */ + swclt_conn_connect_cb_t connect_cb; + void *connect_cb_data; + + /* Optional callbacks for getting notified of connection failure */ + swclt_conn_failed_cb_t failed_cb; + void *failed_cb_data; + + /* Connection failed state */ + int failed; + + ks_mutex_t *failed_mutex; + + /* Our websocket transport, basically our connection to blade */ + swclt_wss_t *wss; + + /* Basic connection info that the caller can examine, contains + * our sessionid, nodeid, and master_nodeid variables returned from + * a connect result from blade, including our connected address and + * ssl context ptr (from websocket info) */ + swclt_conn_info_t info; + + /* The result of our last connect, kept around for reference */ + blade_connect_rpl_t *blade_connect_rpl; + + /* A hash of outstanding commands, keyed by their request ids. + * This is the outgoing queue for requests born from the client or + * requests which have been sent from blade. Since the uuids are + * globally unique we can just use one hash for both */ + ks_hash_t *outstanding_requests; + + /* TTLs to expire */ + swclt_ttl_tracker_t *ttl; + + /* pool to process incoming websocket frames */ + ks_thread_pool_t *incoming_frame_pool; + + /* when last stats were published */ + ks_time_t last_stats_update; + swclt_wss_stats_t last_stats; +}; + +SWCLT_DECLARE(void) swclt_conn_destroy(swclt_conn_t **conn); SWCLT_DECLARE(ks_status_t) swclt_conn_connect( - swclt_conn_t *conn, + swclt_conn_t **conn, swclt_conn_incoming_cmd_cb_t incoming_command_callback, void *incoming_command_cb_data, swclt_ident_t *ident, ks_json_t **authentication, + const char *agent, + const char *identity, + ks_json_t *network, const SSL_CTX *ssl); SWCLT_DECLARE(ks_status_t) swclt_conn_connect_ex( - swclt_conn_t *conn, + swclt_conn_t **conn, swclt_conn_incoming_cmd_cb_t incoming_command_callback, void *incoming_command_cb_data, swclt_conn_connect_cb_t connect_callback, void *connect_cb_data, + swclt_conn_failed_cb_t failed_callback, + void *failed_cb_data, swclt_ident_t *ident, ks_uuid_t previous_sessionid, ks_json_t **authentication, + const char *agent, + const char *identity, + ks_json_t *network, const SSL_CTX *ssl); -SWCLT_DECLARE(ks_status_t) swclt_conn_submit_request(swclt_conn_t conn, swclt_cmd_t cmd); -SWCLT_DECLARE(ks_status_t) swclt_conn_submit_result(swclt_conn_t conn, swclt_cmd_t cmd); -SWCLT_DECLARE(ks_status_t) swclt_conn_get_rates(swclt_conn_t conn, ks_throughput_t *recv, ks_throughput_t *send); - -#define swclt_conn_get(conn, contextP) __ks_handle_get(SWCLT_HTYPE_CONN, conn, (ks_handle_base_t**)contextP, __FILE__, __LINE__, __PRETTY_FUNCTION__) -#define swclt_conn_put(contextP) __ks_handle_put(SWCLT_HTYPE_CONN, (ks_handle_base_t**)contextP, __FILE__, __LINE__, __PRETTY_FUNCTION__) +SWCLT_DECLARE(ks_status_t) swclt_conn_submit_request(swclt_conn_t *conn, swclt_cmd_t **cmd, swclt_cmd_future_t **future); +SWCLT_DECLARE(ks_status_t) swclt_conn_submit_result(swclt_conn_t *conn, swclt_cmd_t *cmd); +SWCLT_DECLARE(ks_status_t) swclt_conn_cancel_request(swclt_conn_t *conn, swclt_cmd_future_t **future); +ks_status_t swclt_conn_info(swclt_conn_t *conn, swclt_conn_info_t *info); +SWCLT_DECLARE(char *) swclt_conn_describe(swclt_conn_t *conn); KS_END_EXTERN_C diff --git a/inc/signalwire-client-c/handle_base.h b/inc/signalwire-client-c/handle_base.h deleted file mode 100644 index f187199..0000000 --- a/inc/signalwire-client-c/handle_base.h +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright (c) 2018 SignalWire, Inc - * - * 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. - */ - -#pragma once - -KS_BEGIN_EXTERN_C - -/* Define our base handle structure, this is used to generically provide features - * across all client handle types */ -struct swclt_handle_base { - /* Just so we don't have to have another address on dereference - * manually define the same entries used in ks_handle_base_t */ - ks_handle_t handle; - ks_pool_t *pool; - - /* This is the current handle state */ - SWCLT_HSTATE state; - - /* The last state we transitioned from */ - SWCLT_HSTATE last_state; - - /* Set to the state the handle is requesting a service for, this is different - * then the swclt_hstate_change_t system, that system is for reporting, this one - * is used by the reporting handle itself when it wants to transition state */ - volatile SWCLT_HSTATE pending_state_change_service; - volatile swclt_hstate_state_change_handler_cb_t pending_state_change_handler_cb; - - /* Lightweight lock for managing the callback array */ - ks_spinlock_t lock; - - /* Service callback, if set makes the handle available for service event from - * the managers thread */ - swclt_hstate_service_cb_t service_cb; - - /* This is set by the handle when they want service, its used to contextualize - * the next requested service absolute timestamp, so that we do not service the handle - * sooner then it needs, nor do we service handles which have not requested - * a service time */ - ks_time_t next_service_time_stamp_ms; - - /* If we fail to service, this is the delay in ms for the next attempt */ - ks_time_t retry_service_delay_ms; - - /* When non null, this implies a state change is pending, and it contains - * the information needed to articulate why and what for the state change - * Note: You cannot layer state change requests, multiple layered requests to - * change state will be ignored. */ - swclt_hstate_change_t *pending_state_change_notification; - - /* State change gets raised by the handle anytime its state changes. The - * definition of a state change is relative to the type as the states are - * generic. These contextes allow for a one to many relationship between - * handles listening for a state change, and a handle reporting a - * state change. */ - swclt_hstate_listener_t *state_listeners; - uint32_t c_state_listeners; -}; - -#define SWCLT_HANDLE_ALLOC_TEMPLATE_S_TAG(pool, file, line, tag, handle_type, handle_ptr, context_type, initial_hstate, describe_method, deinit_method, init_method) \ - ks_status_t status; \ - context_type *context; \ - \ - if ((status = __ks_handle_alloc_ex(pool, handle_type, sizeof(context_type), \ - (ks_handle_base_t **)&context, handle_ptr, \ - (ks_handle_describe_cb_t)describe_method, \ - (ks_handle_deinit_cb_t)deinit_method, \ - file, line, tag))) { \ - if (status == KS_STATUS_HANDLE_NO_MORE_SLOTS) { \ - abort(); \ - } \ - return status; \ - } \ - \ - ((swclt_handle_base_t *)context)->state = initial_hstate; \ - ((swclt_handle_base_t *)context)->last_state = initial_hstate; \ - \ - if (status = init_method(context)) { \ - deinit_method(context); \ - ks_handle_destroy(handle_ptr); \ - return status; \ - } \ - \ - ks_handle_set_ready(*handle_ptr); \ - return status; - -#define SWCLT_HANDLE_ALLOC_TEMPLATE_M_TAG(pool, file, line, tag, handle_type, handle_ptr, context_type, initial_hstate, describe_method, deinit_method, init_method, ...) \ - ks_status_t status; \ - context_type *context; \ - \ - if ((status = __ks_handle_alloc_ex(pool, handle_type, sizeof(context_type), (ks_handle_base_t **)&context, handle_ptr, \ - (ks_handle_describe_cb_t)describe_method, (ks_handle_deinit_cb_t)deinit_method, \ - file, line, tag))) { \ - if (status == KS_STATUS_HANDLE_NO_MORE_SLOTS) { \ - abort(); \ - } \ - return status; \ - } \ - \ - ((swclt_handle_base_t *)context)->state = initial_hstate; \ - ((swclt_handle_base_t *)context)->last_state = initial_hstate; \ - \ - if (status = init_method(context, __VA_ARGS__)) { \ - deinit_method(context); \ - ks_handle_destroy(handle_ptr); \ - return status; \ - } \ - \ - ks_handle_set_ready(*handle_ptr); \ - return status; - -#define SWCLT_HANDLE_ALLOC_TEMPLATE_S(pool, handle_type, handle_ptr, context_type, initial_hstate, describe_method, deinit_method, init_method) \ - ks_status_t status; \ - context_type *context; \ - \ - if ((status = ks_handle_alloc_ex(pool, handle_type, sizeof(context_type), \ - &context, handle_ptr, \ - (ks_handle_describe_cb_t)describe_method, \ - (ks_handle_deinit_cb_t)deinit_method))) { \ - if (status == KS_STATUS_HANDLE_NO_MORE_SLOTS) { \ - abort(); \ - } \ - return status; \ - } \ - \ - ((swclt_handle_base_t *)context)->state = initial_hstate; \ - ((swclt_handle_base_t *)context)->last_state = initial_hstate; \ - \ - if (status = init_method(context)) { \ - deinit_method(context); \ - ks_handle_destroy(handle_ptr); \ - return status; \ - } \ - \ - ks_handle_set_ready(*handle_ptr); \ - return status; - -#define SWCLT_HANDLE_ALLOC_TEMPLATE_M(pool, handle_type, handle_ptr, context_type, initial_hstate, describe_method, deinit_method, init_method, ...) \ - ks_status_t status; \ - context_type *context; \ - \ - if ((status = ks_handle_alloc_ex(pool, handle_type, sizeof(context_type), &context, handle_ptr, \ - (ks_handle_describe_cb_t)describe_method, (ks_handle_deinit_cb_t)deinit_method))) { \ - if (status == KS_STATUS_HANDLE_NO_MORE_SLOTS) { \ - abort(); \ - } \ - return status; \ - } \ - \ - ((swclt_handle_base_t *)context)->state = initial_hstate; \ - ((swclt_handle_base_t *)context)->last_state = initial_hstate; \ - \ - if (status = init_method(context, __VA_ARGS__)) { \ - deinit_method(context); \ - ks_handle_destroy(handle_ptr); \ - return status; \ - } \ - \ - ks_handle_set_ready(*handle_ptr); \ - return status; - - -KS_END_EXTERN_C diff --git a/inc/signalwire-client-c/handle_monitor.h b/inc/signalwire-client-c/handle_monitor.h deleted file mode 100644 index 6622ab3..0000000 --- a/inc/signalwire-client-c/handle_monitor.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2018 SignalWire, Inc - * - * 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. - */ - -#pragma once - -/* Obfuscate our handle monitor internals */ -typedef struct swclt_hmon_ctx swclt_hmon_ctx_t; - -typedef ks_handle_t swclt_hmon_t; - -/* Define the callback signature for the monitor */ -typedef void(*swclt_hmon_state_change_cb_t)(ks_handle_t handle, const swclt_hstate_change_t *state_change_info, void *cb_data); - -KS_BEGIN_EXTERN_C - -SWCLT_DECLARE(ks_status_t) __swclt_hmon_register( - swclt_hmon_t *hmon, - ks_handle_t handle_to_monitor, - swclt_hmon_state_change_cb_t cb, - void *cb_data, - const char *file, - int line, - const char *tag); - -#define swclt_hmon_register(hmon, handle_to_monitor, cb, cb_data) __swclt_hmon_register( \ - hmon, handle_to_monitor, (swclt_hmon_state_change_cb_t)cb, cb_data, __FILE__, __LINE__, __PRETTY_FUNCTION__) - -SWCLT_DECLARE(ks_status_t) swclt_hmon_raise_hstate_change(swclt_hmon_t, const swclt_hstate_change_t *state_change_info); - -#define swclt_hmon_get(hmon, contextP) __ks_handle_get(SWCLT_HTYPE_HMON, hmon, (ks_handle_base_t**)contextP, __FILE__, __LINE__, __PRETTY_FUNCTION__) -#define swclt_hmon_put(contextP) __ks_handle_put(SWCLT_HTYPE_HMON, (ks_handle_base_t**)contextP, __FILE__, __LINE__, __PRETTY_FUNCTION__) - -KS_END_EXTERN_C diff --git a/inc/signalwire-client-c/handle_state.h b/inc/signalwire-client-c/handle_state.h deleted file mode 100644 index 749432b..0000000 --- a/inc/signalwire-client-c/handle_state.h +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright (c) 2018-2019 SignalWire, Inc - * - * 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. - */ - -#pragma once - -/* Handle states provide hooks to do things when they change, and they - * describe whether the handle is open for use or not */ -typedef enum { - SWCLT_HSTATE_INVALID, /* this is the no state, only used to distinguish between set and not set */ - SWCLT_HSTATE_NORMAL, /* the default state, we assume everything is normal post allocation. */ - SWCLT_HSTATE_ONLINE, /* the handle is now 'online' in whatever definition they want to consider it to be */ - SWCLT_HSTATE_OFFLINE, /* the handle is now 'offline' in whatever definition they want to consider it to be */ - SWCLT_HSTATE_DEGRADED, /* the service provided by the handle has halted due to some kind of failure. - * Note: The primary difference between DEGRADED and OFFLINE is that DEGRADED is - * caused by some kind of error, wherease OFFLINE may just be a request from the client. */ -} SWCLT_HSTATE; - -/* swclt_hstate_str - Returns a string version for the state enumeration value */ -static inline const char *swclt_hstate_str(SWCLT_HSTATE state) -{ - switch (state) { - case SWCLT_HSTATE_INVALID: - return "Invalid"; - case SWCLT_HSTATE_NORMAL: - return "Normal"; - case SWCLT_HSTATE_ONLINE: - return "Online"; - case SWCLT_HSTATE_OFFLINE: - return "Offline"; - case SWCLT_HSTATE_DEGRADED: - return "Degraded"; - default: - ks_abort_fmt("Invalid handle state: %d", state); - } -} - -/* Forward declare our handle type as it is composed of inner types that reference it */ -typedef struct swclt_hstate_change_request swclt_hstate_change_t; -typedef struct swclt_handle_base swclt_handle_base_t; - -/* A state change callback is available on any signalwire client handle */ -typedef void(*swclt_hstate_change_cb_t)(swclt_handle_base_t *ctx, swclt_hstate_change_t *state_change_request); - -/* State change handler for when a handle wants to transition state and execute code as part of the transition */ -typedef ks_status_t (*swclt_hstate_state_change_handler_cb_t)(swclt_handle_base_t *ctx, SWCLT_HSTATE requested_new_state); - -/* Service callback for all client handles */ -typedef void(*swclt_hstate_service_cb_t)(swclt_handle_base_t *ctx); - -/* Context structure used to describe a state change listener on a handle */ -typedef struct swclt_hstate_listener_s { - swclt_hstate_change_cb_t cb; /* Called anytime state changes on this handle, by the service manager thread */ - ks_handle_t handle; /* We check this ptr out before calling the callers callback */ -} swclt_hstate_listener_t; - -/* The state change request structure gets allocated by the handle anytime it wants to start a state change - * request. The manager will then take this state change and apply it in its thread */ -struct swclt_hstate_change_request { - /* the requested state to change to */ - SWCLT_HSTATE new_state; - - /* the original state snapshotted at allocation */ - SWCLT_HSTATE old_state; - - /* Reason and status for the state change (reason must be non zero when - * state is degraded, and must be zero when state is normal) */ - char *reason; - ks_status_t status; - -#if defined(KS_BUILD_DEBUG) - /* For debugging we stash the file/line/tag anytime state changes */ - const char *file; - const char *tag; - int line; -#endif -}; - -/* Describes in detail the pending state change, including the reason and status code. If debug - * is enabled, will include the file/line where the state change was initiated from. */ -static inline const char *swclt_hstate_describe_change(const swclt_hstate_change_t *pending_state_change) -{ - static KS_THREAD_LOCAL char buf[1024] = { 0 }; -#if defined(KS_BUILD_DEBUG) - snprintf(buf, sizeof(buf), "[%s=>%s] Status: %d Reason: %s\nLocation: %s:%d:%s", - swclt_hstate_str(pending_state_change->old_state), - swclt_hstate_str(pending_state_change->new_state), - pending_state_change->status, pending_state_change->reason, - pending_state_change->file, pending_state_change->line, pending_state_change->tag); -#else - snprintf(buf, sizeof(buf), "[%s=>%s] Status: %d Reason: %s", - swclt_hstate_str(pending_state_change->old_state), - swclt_hstate_str(pending_state_change->new_state), - pending_state_change->status, pending_state_change->reason); -#endif - return buf; -} - -/* Private handle state apis used internally, but still exposed for unit tests */ -SWCLT_DECLARE(ks_status_t) swclt_hstate_check_ctx(const swclt_handle_base_t *ctx, const char *log_message); -SWCLT_DECLARE(SWCLT_HSTATE) swclt_hstate_get_ctx(const swclt_handle_base_t *ctx); -SWCLT_DECLARE(SWCLT_HSTATE) swclt_hstate_get(ks_handle_t handle); -SWCLT_DECLARE(SWCLT_HSTATE) swclt_hstate_pending_get(ks_handle_t handle); -SWCLT_DECLARE(SWCLT_HSTATE) swclt_hstate_current_get(ks_handle_t handle); -SWCLT_DECLARE(ks_status_t) swclt_hstate_check(ks_handle_t handle, const char *log_message); - -SWCLT_DECLARE(void) __swclt_hstate_changed(swclt_handle_base_t *ctx,SWCLT_HSTATE new_state, ks_status_t status, const char *reason, const char *file, int line, const char *tag); - -SWCLT_DECLARE(void) __swclt_hstate_initiate_change_in( - swclt_handle_base_t *base, - SWCLT_HSTATE new_state, - swclt_hstate_state_change_handler_cb_t cb, - ks_time_t next_service_ms, - ks_time_t retry_delay_ms); -#define swclt_hstate_initiate_change_in(base, new_state, cb, next_service_ms, retry_delay_ms)\ - __swclt_hstate_initiate_change_in(base, new_state, (swclt_hstate_state_change_handler_cb_t)cb, next_service_ms, retry_delay_ms) - -SWCLT_DECLARE(void) __swclt_hstate_initiate_change_now( - swclt_handle_base_t *base, - SWCLT_HSTATE new_state, - swclt_hstate_state_change_handler_cb_t cb, - ks_time_t retry_delay_ms); - -#define swclt_hstate_initiate_change_now(base, new_state, cb, retry_delay_ms)\ - __swclt_hstate_initiate_change_now(base, new_state, (swclt_hstate_state_change_handler_cb_t)cb, retry_delay_ms) - -SWCLT_DECLARE(ks_status_t) __swclt_hstate_register_listener( - swclt_handle_base_t *listening_ctx, - swclt_hstate_change_cb_t state_change_cb, - ks_handle_t state_change_source_handle, - const char *file, - int line, - const char *tag); - -SWCLT_DECLARE(void) __swclt_hstate_register_service(swclt_handle_base_t *ctx, swclt_hstate_service_cb_t service_cb, const char *file, int line, const char *tag); - -/* Use these macros to forward the context to debug build structures while also providing a cast for - * the callbacks. */ -#define swclt_hstate_changed(ctx, new_state, status, reason) __swclt_hstate_changed(ctx, new_state, status, reason, __FILE__, __LINE__, __PRETTY_FUNCTION__) -#define swclt_hstate_register_listener(ctx, cb, handle) __swclt_hstate_register_listener((swclt_handle_base_t *)ctx, (swclt_hstate_change_cb_t)cb, handle, __FILE__, __LINE__, __PRETTY_FUNCTION__) -#define swclt_hstate_register_service(ctx, service_cb) __swclt_hstate_register_service(ctx, (swclt_hstate_service_cb_t)service_cb, __FILE__, __LINE__, __PRETTY_FUNCTION__) diff --git a/inc/signalwire-client-c/handle_type.h b/inc/signalwire-client-c/handle_type.h deleted file mode 100644 index 4680762..0000000 --- a/inc/signalwire-client-c/handle_type.h +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright (c) 2018 SignalWire, Inc - * - * 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. - */ - -#pragma once - -#define KS_BEGIN_EXTERN_C - -/* Define the handle types in swclient. We start at the user group start - * id, libks owns all groups prior. so we get 10 groups to play with - * but for now we only use one, KS_HANDLE_GROUP_SWCLT. */ - -#define KS_HANDLE_GROUP_SWCLT KS_HANDLE_USER_GROUP_START -#define KS_HANDLE_GROUP_SWCLT_SYS (KS_HANDLE_USER_GROUP_START + 1) - -typedef enum { - /* CONN - Connection which contains a transport - * to blade (e.g. websocket) handles reads and writes - * and knows how to do high level things like connect - * to blade with a connect request */ - SWCLT_HTYPE_CONN = KS_HANDLE_MAKE_TYPE(SWCLT_SYS, 1), - - /* CMD - Commands are request/reply wrappers for json rpc */ - SWCLT_HTYPE_CMD = KS_HANDLE_MAKE_TYPE(SWCLT_SYS, 2), - - /* FRAME - A frame is whats returned from a read on a transport, - * it provides transport specific attributes needed to handle - * a read or a write to a transport */ - SWCLT_HTYPE_FRAME = KS_HANDLE_MAKE_TYPE(SWCLT_SYS, 3), - - /* WSS - Web socket transport which abstracts the reading - * and writing semantics of a web socket, including ssl - * negotiation, and ping/pong handling */ - SWCLT_HTYPE_WSS = KS_HANDLE_MAKE_TYPE(SWCLT_SYS, 4), - - /* SESS - A session is the highest level construct, and is the - * primary means in which a client ineracts with this sdk. */ - SWCLT_HTYPE_SESS = KS_HANDLE_MAKE_TYPE(SWCLT_SYS, 5), - - /* SUB - A subscription holds the callback state for a subscription - * and is a child of a session. */ - SWCLT_HTYPE_SUB = KS_HANDLE_MAKE_TYPE(SWCLT, 1), - - /* STORE - A node store contains the up to date state of - * provider/protocols/channels and routes */ - SWCLT_HTYPE_STORE = KS_HANDLE_MAKE_TYPE(SWCLT, 2), - - /* HMON - A handle monitoring context, used for state change - * detection on any swclient handle */ - SWCLT_HTYPE_HMON = KS_HANDLE_MAKE_TYPE(SWCLT, 3), - - /* TEST - Used for unit testing */ - SWCLT_HTYPE_TEST = KS_HANDLE_MAKE_TYPE(SWCLT, 4), -} swclt_htype_t; - -static inline ks_bool_t swclt_htype_valid(swclt_htype_t type) -{ - /* The type is valid if its group is our group */ - return KS_HANDLE_GROUP_FROM_TYPE(type) == KS_HANDLE_GROUP_SWCLT || - KS_HANDLE_GROUP_FROM_TYPE(type) == KS_HANDLE_GROUP_SWCLT_SYS; -} - -static inline const char *const swclt_htype_str(swclt_htype_t type) -{ - switch(type) { - case SWCLT_HTYPE_CONN: - return "Connection"; - case SWCLT_HTYPE_CMD: - return "Command"; - case SWCLT_HTYPE_FRAME: - return "Frame"; - case SWCLT_HTYPE_WSS: - return "Websocket"; - case SWCLT_HTYPE_SESS: - return "Session"; - case SWCLT_HTYPE_SUB: - return "Subscription"; - case SWCLT_HTYPE_STORE: - return "NodeStore"; - case SWCLT_HTYPE_HMON: - return "HandleMonitor"; - case SWCLT_HTYPE_TEST: - return "Test"; - default: - ks_abort_fmt("Invalid handle type: %lu", type); - } -} - -#define KS_END_EXTERN_C - -/* For Emacs: - * Local Variables: - * mode:c - * indent-tabs-mode:t - * tab-width:4 - * c-basic-offset:4 - * End: - * For VIM: - * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: - */ diff --git a/inc/signalwire-client-c/handles.h b/inc/signalwire-client-c/handles.h deleted file mode 100644 index c3cc2d4..0000000 --- a/inc/signalwire-client-c/handles.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2018 SignalWire, Inc - * - * 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. - */ - -#pragma once - -KS_BEGIN_EXTERN_C - -typedef ks_handle_t swclt_sess_t; -typedef ks_handle_t swclt_cfg_t; - -KS_END_EXTERN_C diff --git a/inc/signalwire-client-c/internal/command.h b/inc/signalwire-client-c/internal/command.h deleted file mode 100644 index 2677803..0000000 --- a/inc/signalwire-client-c/internal/command.h +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (c) 2018 SignalWire, Inc - * - * 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. - */ - -#pragma once - -KS_BEGIN_EXTERN_C - -/* Ths client command context represents a commands state */ -struct swclt_cmd_ctx { - swclt_handle_base_t base; - - /* When a command completes it calls the callback */ - swclt_cmd_cb_t cb; - void *cb_data; - - /* Should we have a general failure processing a command, the - * type will go to SWCLT_CMD_TYPE_FAILURE and this status - * will be set along with a description buffer */ - ks_status_t failure_status; - char *failure_reason; - - /* A summarized allocation of the failure so we can re-use it */ - char *failure_summary; - -#if defined(KS_BUILD_DEBUG) - /* Stashed on debug builds */ - const char *failure_file; - int failure_line; - const char *failure_tag; -#endif - - /* Each command is thread safe in terms of access to its apis - * using a spinlock to avoid excessive resource allocations. */ - ks_spinlock_t lock; - - /* The request ids, generated once on allocation */ - ks_uuid_t id; - char *id_str; - - /* CMD flags e.g. whether we expect a reply or not */ - uint32_t flags : SWCLT_CMD_FLAG_MAX; - - /* Our method, established on construction */ - char *method; - - /* The request, a command always has one, as it is born from a request */ - ks_json_t *request; - - /* We unionize these since there can only be one kind of reply, an error - * or a result */ - union { - ks_json_t *error; - ks_json_t *result; - } reply; - - /* The command type can be request (when constructed) result (when remote replied - * with no error) and error (when remove replies with an error */ - SWCLT_CMD_TYPE type; - - /* This is the time to live value, when non zero, the command will fail if the - * response is not received within the appropriate window. */ - uint32_t response_ttl_ms; - - /* This is a timestamp for when we got submitted in the connectins outstanding - * request hash. This is used by the service manager to timeout commands - * which have exceeded their time to live amount */ - ks_time_t submit_time; -}; - -/* Internal api for providing read only access to the command */ -static inline ks_status_t __swclt_cmd_ctx_get( - swclt_cmd_t cmd, - const swclt_cmd_ctx_t **ctx, - const char *file, int line, const char *tag) -{ - return __ks_handle_get(SWCLT_HTYPE_CMD, cmd, (ks_handle_base_t **)ctx, file, line, tag); -} - -#define swclt_cmd_ctx_get(cmd, ctx)\ - __swclt_cmd_ctx_get(cmd, ctx, __FILE__, __LINE__, __FILE__) - -static inline void swclt_cmd_ctx_lock(const swclt_cmd_ctx_t *ctx) { ks_spinlock_acquire(&ctx->lock); } -static inline void swclt_cmd_ctx_unlock(const swclt_cmd_ctx_t *ctx) { ks_spinlock_release(&ctx->lock); } - -static inline void __swclt_cmd_ctx_put( - const swclt_cmd_ctx_t **ctx, - const char *file, int line, const char *tag) -{ - __ks_handle_put(SWCLT_HTYPE_CMD, (ks_handle_base_t **)ctx, file, line, tag); -} - -#define swclt_cmd_ctx_put(ctx)\ - __swclt_cmd_ctx_put(ctx, __FILE__, __LINE__, __FILE__) - -KS_END_EXTERN_C - -/* Define helper macros to eliminate boiler code in handle wrapped apis */ -#define SWCLT_CMD_SCOPE_BEG(cmd, ctx, status) \ - KS_HANDLE_SCOPE_BEG(SWCLT_HTYPE_CMD, cmd, swclt_cmd_ctx_t, ctx, status); - -#define SWCLT_CMD_SCOPE_END(cmd, ctx, status) \ - KS_HANDLE_SCOPE_END(SWCLT_HTYPE_CMD, cmd, swclt_cmd_ctx_t, ctx, status); - -#define SWCLT_CMD_SCOPE_BEG_TAG(cmd, ctx, status, file, line, tag) \ - KS_HANDLE_SCOPE_BEG_TAG(SWCLT_HTYPE_CMD, cmd, swclt_cmd_ctx_t, ctx, status, file, line, tag); - -#define SWCLT_CMD_SCOPE_END_TAG(cmd, ctx, status, file, line, tag) \ - KS_HANDLE_SCOPE_END_TAG(SWCLT_HTYPE_CMD, cmd, swclt_cmd_ctx_t, ctx, status, file, line, tag); - -/* For Emacs: - * Local Variables: - * mode:c - * indent-tabs-mode:t - * tab-width:4 - * c-basic-offset:4 - * End: - * For VIM: - * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: - */ - diff --git a/inc/signalwire-client-c/internal/config.h b/inc/signalwire-client-c/internal/config.h index 4728259..e5948e0 100644 --- a/inc/signalwire-client-c/internal/config.h +++ b/inc/signalwire-client-c/internal/config.h @@ -29,6 +29,9 @@ typedef struct swclt_config_s { const char *client_cert_path; const char *cert_chain_path; const char *authentication; + const char *agent; + const char *identity; + ks_json_t *network; } swclt_config_t; KS_END_EXTERN_C diff --git a/inc/signalwire-client-c/internal/connection.h b/inc/signalwire-client-c/internal/connection.h deleted file mode 100644 index a100a3e..0000000 --- a/inc/signalwire-client-c/internal/connection.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (c) 2018 SignalWire, Inc - * - * 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. - */ - -#pragma once - -KS_BEGIN_EXTERN_C - -/* Information about this connection */ -typedef struct swclt_conn_info_s { - /* We also store a copy of the wss's info structure */ - swclt_wss_info_t wss; - - /* Pulled from the blade connect result */ - ks_uuid_t sessionid; - const char *nodeid; - const char *master_nodeid; -} swclt_conn_info_t; - -/* Ths client connection context represents a connections state */ -struct swclt_conn_ctx { - swclt_handle_base_t base; - - /* When we receive an incoming request we call this callback with the prepared command */ - swclt_conn_incoming_cmd_cb_t incoming_cmd_cb; - void *incoming_cmd_cb_data; - - /* Optional callbacks for getting the initial connect result payload */ - swclt_conn_connect_cb_t connect_cb; - void *connect_cb_data; - - /* Our websocket transport, basically our connection to blade */ - swclt_wss_t wss; - - /* Basic connection info that the caller can examine, contains - * our sessionid, nodeid, and master_nodeid variables returned from - * a connect result from blade, including our connected address and - * ssl context ptr (from websocket info) */ - swclt_conn_info_t info; - - /* The result of our last connect, kept around for reference */ - blade_connect_rpl_t *blade_connect_rpl; - - /* A hash of oustanding commands, keyed by their request ids. - * This is the outgoing queue for requests born from the client or - * requests which have been sent from blade. Since the uuids are - * globally unique we can just use one hash for both */ - ks_hash_t *outstanding_requests; - - /* The outstanding condition is signalled anytime a command in the outstanding - * requests hash parses a result. Client readers wait on this condition until - * signalled */ - ks_cond_t *cmd_condition; -}; - -/* Private */ -ks_status_t swclt_conn_info(swclt_conn_t conn, swclt_conn_info_t *info); - -KS_END_EXTERN_C - -/* Define helper macros to eliminate boiler code in handle wrapped apis */ -#define SWCLT_CONN_SCOPE_BEG(conn, ctx, status) \ - KS_HANDLE_SCOPE_BEG(SWCLT_HTYPE_CONN, conn, swclt_conn_ctx_t, ctx, status); - -#define SWCLT_CONN_SCOPE_END(conn, ctx, status) \ - KS_HANDLE_SCOPE_END(SWCLT_HTYPE_CONN, conn, swclt_conn_ctx_t, ctx, status); - -/* For Emacs: - * Local Variables: - * mode:c - * indent-tabs-mode:t - * tab-width:4 - * c-basic-offset:4 - * End: - * For VIM: - * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: - */ diff --git a/inc/signalwire-client-c/internal/handle_monitor.h b/inc/signalwire-client-c/internal/handle_monitor.h deleted file mode 100644 index 53a9da9..0000000 --- a/inc/signalwire-client-c/internal/handle_monitor.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2018 SignalWire, Inc - * - * 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. - */ - -#pragma once - -KS_BEGIN_EXTERN_C - -struct swclt_hmon_ctx { - swclt_handle_base_t base; - - /* This is the handle the caller wants us to monitor */ - ks_handle_t handle_to_monitor; - - /* The callers callback and callback data */ - swclt_hmon_state_change_cb_t cb; - void *cb_data; -}; - -KS_END_EXTERN_C - -/* Define helper macros to eliminate boiler code in handle wrapped apis */ -#define SWCLT_HMON_SCOPE_BEG(hmon, ctx, status) \ - KS_HANDLE_SCOPE_BEG(SWCLT_HTYPE_HMON, hmon, swclt_hmon_ctx_t, ctx, status); - -#define SWCLT_HMON_SCOPE_END(hmon, ctx, status) \ - KS_HANDLE_SCOPE_END(SWCLT_HTYPE_HMON, hmon, swclt_hmon_ctx_t, ctx, status); - -/* For Emacs: - * Local Variables: - * mode:c - * indent-tabs-mode:t - * tab-width:4 - * c-basic-offset:4 - * End: - * For VIM: - * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: - */ diff --git a/inc/signalwire-client-c/internal/nodestore.h b/inc/signalwire-client-c/internal/nodestore.h deleted file mode 100644 index 9ecfccd..0000000 --- a/inc/signalwire-client-c/internal/nodestore.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2018-2019 SignalWire, Inc - * - * 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. - */ - -#pragma once - -KS_BEGIN_EXTERN_C - -struct swclt_store_ctx { - swclt_handle_base_t base; - - ks_pool_t *pool; - - // callbacks keyed by netcast command, pointing to value of callback to call - ks_hash_t *callbacks; - - /* Hash of nodes keyed by their nodeid. They point to a Node - * class which contains their certified status. */ - ks_hash_t *routes; - - /* Hash keyed by identity mapped to nodeid */ - ks_hash_t *identities; - - /* Last index position we selected for random protocol gathering */ - uint32_t last_random_protocol_idx; - - /* Hash of protocols, keyed by the protocol name, each protocol - * contains channels. */ - ks_hash_t *protocols; - - /* Hash of Subscription objects, keyed by their protocol name. */ - ks_hash_t *subscriptions; - - /* Hash of authorities, keyed by their node uuid. */ - ks_hash_t *authorities; - - /* Hash of protocols available to uncertified clients only, keyed by protocol name */ - ks_hash_t *protocols_uncertified; -}; - -/* Define helper macros to eliminate boiler code in handle wrapped apis */ -#define SWCLT_STORE_SCOPE_BEG(store, ctx, status) \ - KS_HANDLE_SCOPE_BEG(SWCLT_HTYPE_STORE, store, swclt_store_ctx_t, ctx, status); - -#define SWCLT_STORE_SCOPE_END(store, ctx, status) \ - KS_HANDLE_SCOPE_END(SWCLT_HTYPE_STORE, store, swclt_store_ctx_t, ctx, status); - -KS_END_EXTERN_C diff --git a/inc/signalwire-client-c/internal/session.h b/inc/signalwire-client-c/internal/session.h deleted file mode 100644 index 6237510..0000000 --- a/inc/signalwire-client-c/internal/session.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (c) 2018 SignalWire, Inc - * - * 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. - */ - -#pragma once - -#include "signalwire-client-c/internal/connection.h" - -KS_BEGIN_EXTERN_C - -/* Create a structure containing some handy peices of info */ -typedef struct swclt_sess_info_s { - /* The info structure from our connection (it itself then contains - * an info structure for the transport) */ - swclt_conn_info_t conn; - - /* Copied from the connect result in the connection info, used - * as a shortcut for convenience */ - ks_uuid_t sessionid; - const char *nodeid; - const char *master_nodeid; -} swclt_sess_info_t; - -/* Ths client session context represents a sessions state */ -struct swclt_sess_ctx { - swclt_handle_base_t base; - - /* The pool that manages all the allocations */ - ks_pool_t *pool; - - /* The node store, an api to keep the cache in sync with blade, contains - * a lotta info about stuff. */ - swclt_store_t store; - - /* Our connection */ - swclt_conn_t conn; - - /* The extracted identity info */ - swclt_ident_t ident; - - /* Our info structure */ - swclt_sess_info_t info; - - SSL_CTX *ssl; - - /* Our config handed to us by the client */ - swclt_config_t *config; - - /* Optional callback for authentication failure */ - swclt_sess_auth_failed_cb_t auth_failed_cb; - - /* We keep track of subscriptions in a hash here, each call to subscribe - * from the client will add an entry in this hash. The hash points to the - * subscription handle which will contain the users callback */ - ks_hash_t *subscriptions; - - /* Registry for Protocol RPC methods */ - ks_hash_t *methods; - - /* Setups completed for gandalf provider event channels */ - ks_hash_t *setups; - - /* Registry for metric rank updates for local protocols */ - ks_hash_t *metrics; -}; - -KS_END_EXTERN_C - -/* Define helper macros to eliminate boiler code in handle wrapped apis */ -#define SWCLT_SESS_SCOPE_BEG(sess, ctx, status) \ - KS_HANDLE_SCOPE_BEG(SWCLT_HTYPE_SESS, sess, swclt_sess_ctx_t, ctx, status); - -/* Semicolon at start of macro ensures it does not create a compound error - * if a goto label appears just before this macro */ -#define SWCLT_SESS_SCOPE_END(sess, ctx, status) \ - ;KS_HANDLE_SCOPE_END(SWCLT_HTYPE_SESS, sess, swclt_sess_ctx_t, ctx, status); - -/* For Emacs: - * Local Variables: - * mode:c - * indent-tabs-mode:t - * tab-width:4 - * c-basic-offset:4 - * End: - * For VIM: - * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: - */ diff --git a/inc/signalwire-client-c/internal/subscription.h b/inc/signalwire-client-c/internal/subscription.h deleted file mode 100644 index 2eb8f5c..0000000 --- a/inc/signalwire-client-c/internal/subscription.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2018 SignalWire, Inc - * - * 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. - */ - -#pragma once - -KS_BEGIN_EXTERN_C - -/* A subscription represents a logical registration of - * a listener on a channel */ -struct swclt_sub_ctx { - swclt_handle_base_t base; - - const char * protocol; - const char * channel; - - swclt_sub_cb_t cb; - - void *cb_data; -}; - -KS_END_EXTERN_C - -/* Define helper macros to eliminate boiler code in handle wrapped apis */ -#define SWCLT_SUB_SCOPE_BEG(sub, ctx, status) \ - KS_HANDLE_SCOPE_BEG(SWCLT_HTYPE_SUB, sub, swclt_sub_ctx_t, ctx, status); - -#define SWCLT_SUB_SCOPE_END(sub, ctx, status) \ - KS_HANDLE_SCOPE_END(SWCLT_HTYPE_SUB, sub, swclt_sub_ctx_t, ctx, status); - -/* For Emacs: - * Local Variables: - * mode:c - * indent-tabs-mode:t - * tab-width:4 - * c-basic-offset:4 - * End: - * For VIM: - * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: - */ diff --git a/inc/signalwire-client-c/nodestore.h b/inc/signalwire-client-c/nodestore.h index f378a24..21fcf34 100644 --- a/inc/signalwire-client-c/nodestore.h +++ b/inc/signalwire-client-c/nodestore.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2019 SignalWire, Inc + * Copyright (c) 2018-2020 SignalWire, Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,109 +24,133 @@ KS_BEGIN_EXTERN_C -/* All stores are handles, opaque numbers that manage ref counts */ -typedef ks_handle_t swclt_store_t; +struct swclt_store { + ks_pool_t *pool; -/* Obfuscate our connection internals */ -typedef struct swclt_store_ctx swclt_store_ctx_t; + // callbacks keyed by netcast command, pointing to value of callback to call + ks_hash_t *callbacks; -typedef void (*swclt_store_cb_protocol_add_t)(swclt_sess_t sess, + /* Hash of nodes keyed by their nodeid. They point to a Node + * class which contains their certified status. */ + ks_hash_t *routes; + + /* Hash keyed by identity mapped to nodeid */ + ks_hash_t *identities; + + /* Last index position we selected for random protocol gathering */ + uint32_t last_random_protocol_idx; + + /* Hash of protocols, keyed by the protocol name, each protocol + * contains channels. */ + ks_hash_t *protocols; + + /* Hash of Subscription objects, keyed by their protocol name. */ + ks_hash_t *subscriptions; + + /* Hash of authorities, keyed by their node uuid. */ + ks_hash_t *authorities; + + /* Hash of protocols available to uncertified clients only, keyed by protocol name */ + ks_hash_t *protocols_uncertified; + + swclt_sess_t *sess; +}; + +typedef struct swclt_store swclt_store_t; + +typedef void (*swclt_store_cb_protocol_add_t)(swclt_sess_t *sess, const char *protocol); -typedef void (*swclt_store_cb_protocol_remove_t)(swclt_sess_t sess, +typedef void (*swclt_store_cb_protocol_remove_t)(swclt_sess_t *sess, const char *protocol); -typedef void (*swclt_store_cb_protocol_provider_add_t)(swclt_sess_t sess, +typedef void (*swclt_store_cb_protocol_provider_add_t)(swclt_sess_t *sess, const blade_netcast_rqu_t *rqu, const blade_netcast_protocol_provider_add_param_t *params); typedef void (*swclt_store_cb_protocol_provider_remove_t)( - swclt_sess_t sess, + swclt_sess_t *sess, const blade_netcast_rqu_t* rqu, const blade_netcast_protocol_provider_remove_param_t *params); -typedef void (*swclt_store_cb_protocol_provider_rank_update_t)(swclt_sess_t sess, +typedef void (*swclt_store_cb_protocol_provider_rank_update_t)(swclt_sess_t *sess, const blade_netcast_rqu_t *rqu, const blade_netcast_protocol_provider_rank_update_param_t *params); -typedef void (*swclt_store_cb_protocol_provider_data_update_t)(swclt_sess_t sess, +typedef void (*swclt_store_cb_protocol_provider_data_update_t)(swclt_sess_t *sess, const blade_netcast_rqu_t *rqu, const blade_netcast_protocol_provider_data_update_param_t *params); -typedef void (*swclt_store_cb_route_add_t)(swclt_sess_t sess, +typedef void (*swclt_store_cb_route_add_t)(swclt_sess_t *sess, const blade_netcast_rqu_t *rqu, const blade_netcast_route_add_param_t *params); typedef void (*swclt_store_cb_route_remove_t)( - swclt_sess_t sess, + swclt_sess_t *sess, const blade_netcast_rqu_t* rqu, const blade_netcast_route_remove_param_t *params); -typedef void (*swclt_store_cb_authority_add_t)(swclt_sess_t sess, +typedef void (*swclt_store_cb_authority_add_t)(swclt_sess_t *sess, const blade_netcast_rqu_t *rqu, const blade_netcast_authority_add_param_t *params); typedef void (*swclt_store_cb_authority_remove_t)( - swclt_sess_t sess, + swclt_sess_t *sess, const blade_netcast_rqu_t* rqu, const blade_netcast_authority_remove_param_t *params); -typedef void (*swclt_store_cb_subscription_add_t)(swclt_sess_t sess, +typedef void (*swclt_store_cb_subscription_add_t)(swclt_sess_t *sess, const blade_netcast_rqu_t *rqu, const blade_netcast_subscription_add_param_t *params); typedef void (*swclt_store_cb_subscription_remove_t)( - swclt_sess_t sess, + swclt_sess_t *sess, const blade_netcast_rqu_t* rqu, const blade_netcast_subscription_remove_param_t *params); -typedef void (*swclt_store_cb_identity_add_t)(swclt_sess_t sess, +typedef void (*swclt_store_cb_identity_add_t)(swclt_sess_t *sess, const blade_netcast_rqu_t *rqu, const blade_netcast_identity_add_param_t *params); typedef void (*swclt_store_cb_identity_remove_t)( - swclt_sess_t sess, + swclt_sess_t *sess, const blade_netcast_rqu_t* rqu, const blade_netcast_identity_remove_param_t *params); +SWCLT_DECLARE(ks_status_t) swclt_store_create(swclt_store_t **store); +SWCLT_DECLARE(ks_status_t) swclt_store_destroy(swclt_store_t **store); +SWCLT_DECLARE(char *) swclt_store_describe(swclt_store_t *store); +SWCLT_DECLARE(ks_status_t) swclt_store_reset(swclt_store_t *store); +SWCLT_DECLARE(ks_status_t) swclt_store_populate(swclt_store_t *store, const blade_connect_rpl_t *connect_rpl); +SWCLT_DECLARE(ks_status_t) swclt_store_update(swclt_store_t *store, const blade_netcast_rqu_t *netcast_rqu); +SWCLT_DECLARE(ks_status_t) swclt_store_get_node_identities(swclt_store_t *store, const char *nodeid, ks_pool_t *pool, ks_hash_t **identities); +SWCLT_DECLARE(ks_status_t) swclt_store_get_protocols(swclt_store_t *store, ks_json_t **protocols); +SWCLT_DECLARE(ks_status_t) swclt_store_check_protocol(swclt_store_t *store, const char *name); -SWCLT_DECLARE(ks_status_t) swclt_store_create(swclt_store_t *store); -SWCLT_DECLARE(ks_status_t) swclt_store_reset(swclt_store_t store); -SWCLT_DECLARE(ks_status_t) swclt_store_populate(swclt_store_t store, const blade_connect_rpl_t *connect_rpl); -SWCLT_DECLARE(ks_status_t) swclt_store_update(swclt_store_t store, const blade_netcast_rqu_t *netcast_rqu); -SWCLT_DECLARE(ks_status_t) swclt_store_get_node_identities(swclt_store_t store, const char *nodeid, ks_pool_t *pool, ks_hash_t **identities); -SWCLT_DECLARE(ks_status_t) swclt_store_get_protocols(swclt_store_t store, ks_pool_t *pool, ks_json_t **protocols); -SWCLT_DECLARE(ks_status_t) swclt_store_check_protocol(swclt_store_t store, const char *name); - SWCLT_DECLARE(ks_status_t) swclt_store_select_random_protocol_provider( - swclt_store_t store, + swclt_store_t *store, const char *name, ks_pool_t *pool, char **providerid); SWCLT_DECLARE(ks_status_t) swclt_store_get_protocol_providers( - swclt_store_t store, + swclt_store_t *store, const char *name, - ks_pool_t *pool, ks_json_t **providers); -SWCLT_DECLARE(ks_status_t) swclt_store_cb_route_add(swclt_store_t store, swclt_store_cb_route_add_t cb); -SWCLT_DECLARE(ks_status_t) swclt_store_cb_route_remove(swclt_store_t store, swclt_store_cb_route_remove_t cb); -SWCLT_DECLARE(ks_status_t) swclt_store_cb_identity_add(swclt_store_t store, swclt_store_cb_identity_add_t cb); -SWCLT_DECLARE(ks_status_t) swclt_store_cb_identity_remove(swclt_store_t store, swclt_store_cb_identity_remove_t cb); -SWCLT_DECLARE(ks_status_t) swclt_store_cb_protocol_add(swclt_store_t store, swclt_store_cb_protocol_add_t cb); -SWCLT_DECLARE(ks_status_t) swclt_store_cb_protocol_remove(swclt_store_t store, swclt_store_cb_protocol_remove_t cb); -SWCLT_DECLARE(ks_status_t) swclt_store_cb_protocol_provider_add(swclt_store_t store, swclt_store_cb_protocol_provider_add_t cb); -SWCLT_DECLARE(ks_status_t) swclt_store_cb_protocol_provider_remove(swclt_store_t store, swclt_store_cb_protocol_provider_remove_t cb); -SWCLT_DECLARE(ks_status_t) swclt_store_cb_protocol_provider_rank_update(swclt_store_t store, swclt_store_cb_protocol_provider_rank_update_t cb); -SWCLT_DECLARE(ks_status_t) swclt_store_cb_protocol_provider_data_update(swclt_store_t store, swclt_store_cb_protocol_provider_data_update_t cb); -SWCLT_DECLARE(ks_status_t) swclt_store_cb_authority_add(swclt_store_t store, swclt_store_cb_authority_add_t cb); -SWCLT_DECLARE(ks_status_t) swclt_store_cb_authority_remove(swclt_store_t store, swclt_store_cb_authority_remove_t cb); -SWCLT_DECLARE(ks_status_t) swclt_store_cb_subscription_add(swclt_store_t store, swclt_store_cb_subscription_add_t cb); -SWCLT_DECLARE(ks_status_t) swclt_store_cb_subscription_remove(swclt_store_t store, swclt_store_cb_subscription_remove_t cb); - - -#define swclt_store_get(store, contextP) __ks_handle_get(SWCLT_HTYPE_STORE, store, (ks_handle_base_t**)contextP, __FILE__, __LINE__, __PRETTY_FUNCTION__) -#define swclt_store_put(contextP) __ks_handle_put(SWCLT_HTYPE_STORE, (ks_handle_base_t**)contextP, __FILE__, __LINE__, __PRETTY_FUNCTION__) +SWCLT_DECLARE(ks_status_t) swclt_store_cb_route_add(swclt_store_t *store, swclt_store_cb_route_add_t cb); +SWCLT_DECLARE(ks_status_t) swclt_store_cb_route_remove(swclt_store_t *store, swclt_store_cb_route_remove_t cb); +SWCLT_DECLARE(ks_status_t) swclt_store_cb_identity_add(swclt_store_t *store, swclt_store_cb_identity_add_t cb); +SWCLT_DECLARE(ks_status_t) swclt_store_cb_identity_remove(swclt_store_t *store, swclt_store_cb_identity_remove_t cb); +SWCLT_DECLARE(ks_status_t) swclt_store_cb_protocol_add(swclt_store_t *store, swclt_store_cb_protocol_add_t cb); +SWCLT_DECLARE(ks_status_t) swclt_store_cb_protocol_remove(swclt_store_t *store, swclt_store_cb_protocol_remove_t cb); +SWCLT_DECLARE(ks_status_t) swclt_store_cb_protocol_provider_add(swclt_store_t *store, swclt_store_cb_protocol_provider_add_t cb); +SWCLT_DECLARE(ks_status_t) swclt_store_cb_protocol_provider_remove(swclt_store_t *store, swclt_store_cb_protocol_provider_remove_t cb); +SWCLT_DECLARE(ks_status_t) swclt_store_cb_protocol_provider_rank_update(swclt_store_t *store, swclt_store_cb_protocol_provider_rank_update_t cb); +SWCLT_DECLARE(ks_status_t) swclt_store_cb_protocol_provider_data_update(swclt_store_t *store, swclt_store_cb_protocol_provider_data_update_t cb); +SWCLT_DECLARE(ks_status_t) swclt_store_cb_authority_add(swclt_store_t *store, swclt_store_cb_authority_add_t cb); +SWCLT_DECLARE(ks_status_t) swclt_store_cb_authority_remove(swclt_store_t *store, swclt_store_cb_authority_remove_t cb); +SWCLT_DECLARE(ks_status_t) swclt_store_cb_subscription_add(swclt_store_t *store, swclt_store_cb_subscription_add_t cb); +SWCLT_DECLARE(ks_status_t) swclt_store_cb_subscription_remove(swclt_store_t *store, swclt_store_cb_subscription_remove_t cb); KS_END_EXTERN_C diff --git a/inc/signalwire-client-c/pmethod.h b/inc/signalwire-client-c/pmethod.h index fb7db2e..f70d60c 100644 --- a/inc/signalwire-client-c/pmethod.h +++ b/inc/signalwire-client-c/pmethod.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 SignalWire, Inc + * Copyright (c) 2018-2020 SignalWire, Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -32,8 +32,8 @@ KS_BEGIN_EXTERN_C typedef ks_status_t (*swclt_pmethod_cb_t)( - ks_handle_t sess, - swclt_cmd_t cmd, + swclt_sess_t *sess, + swclt_cmd_t *cmd, const blade_execute_rqu_t *rqu, void *cb_data); /** diff --git a/inc/signalwire-client-c/session.h b/inc/signalwire-client-c/session.h index 5267414..4504fcf 100644 --- a/inc/signalwire-client-c/session.h +++ b/inc/signalwire-client-c/session.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2019 SignalWire, Inc + * Copyright (c) 2018-2022 SignalWire, Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,98 +24,182 @@ KS_BEGIN_EXTERN_C -/* Obfuscate our session internals */ -typedef struct swclt_sess_ctx swclt_sess_ctx_t; - -typedef void (*swclt_sess_auth_failed_cb_t)(swclt_sess_t sess); - -SWCLT_DECLARE(ks_status_t) __swclt_sess_create( - swclt_sess_t *sess, +typedef enum swclt_sess_state { + SWCLT_STATE_OFFLINE, + SWCLT_STATE_ONLINE, + SWCLT_STATE_RESTORED +} swclt_sess_state_t; + +static inline const char *swclt_sess_state_str(swclt_sess_state_t state) +{ + switch (state) { + case SWCLT_STATE_OFFLINE: + return "None"; + case SWCLT_STATE_ONLINE: + return "Online"; + case SWCLT_STATE_RESTORED: + return "Restored"; + default: + return ""; + } +} + +typedef void (*swclt_sess_auth_failed_cb_t)(swclt_sess_t *sess); +typedef void (*swclt_sess_state_change_cb_t)(swclt_sess_t *sess, void *cb_data); + +struct swclt_result_queue; +typedef struct swclt_result_queue swclt_result_queue_t; + +typedef struct swclt_sess_info_s { + /* The info structure from our connection (it itself then contains + * an info structure for the transport) */ + swclt_conn_info_t conn; + + /* Copied from the connect result in the connection info, used + * as a shortcut for convenience */ + ks_uuid_t sessionid; + const char *nodeid; + const char *master_nodeid; +} swclt_sess_info_t; + +/* Ths client session context represents a sessions state */ +struct swclt_sess { + /* The pool that manages all the allocations */ + ks_pool_t *pool; + + /* The node store, an api to keep the cache in sync with blade, contains + * a lotta info about stuff. */ + swclt_store_t *store; + + /* Our connection */ + swclt_conn_t *conn; + + /* The extracted identity info */ + swclt_ident_t ident; + + /* Our info structure */ + swclt_sess_info_t info; + + SSL_CTX *ssl; + + /* Our config handed to us by the client */ + swclt_config_t *config; + + /* Optional callback for authentication failure */ + swclt_sess_auth_failed_cb_t auth_failed_cb; + + /* We keep track of subscriptions in a hash here, each call to subscribe + * from the client will add an entry in this hash. The hash points to the + * subscription handle which will contain the users callback */ + ks_hash_t *subscriptions; + + /* Registry for Protocol RPC methods */ + ks_hash_t *methods; + + /* Setups completed for provider event channels */ + ks_hash_t *setups; + + /* Registry for metric rank updates for local protocols */ + ks_hash_t *metrics; + + /* Results that are pending delivery on re-connection */ + swclt_result_queue_t *result_first; + swclt_result_queue_t *result_last; + ks_mutex_t *result_mutex; + + /* Optional callback for state changes */ + swclt_sess_state_change_cb_t state_change_cb; + void *state_change_cb_data; + swclt_sess_state_t state; + ks_time_t disconnect_time; + ks_time_t connect_time; + ks_cond_t *monitor_cond; + ks_thread_t *monitor_thread; + + ks_rwl_t *rwlock; +}; + +SWCLT_DECLARE(ks_status_t) swclt_sess_create( + swclt_sess_t **sess, const char *identity_uri, - swclt_config_t *config, - const char *file, - int line, - const char *tag); - -#define swclt_sess_create(sess, ident, cfg) __swclt_sess_create(sess, ident, cfg, __FILE__, __LINE__, __PRETTY_FUNCTION__) - -SWCLT_DECLARE(ks_status_t) swclt_sess_set_auth_failed_cb(swclt_sess_t sess, swclt_sess_auth_failed_cb_t cb); -SWCLT_DECLARE(ks_status_t) swclt_sess_target_set(swclt_sess_t sess, const char *target); - -SWCLT_DECLARE(ks_status_t) swclt_sess_metric_register(swclt_sess_t sess, const char *protocol, int interval, int rank); -SWCLT_DECLARE(ks_status_t) swclt_sess_metric_update(swclt_sess_t sess, const char *protocol, int rank); -SWCLT_DECLARE(ks_status_t) swclt_sess_metric_current(swclt_sess_t sess, const char *protocol, int *rank); - -SWCLT_DECLARE(ks_bool_t) swclt_sess_has_authentication(swclt_sess_t sess); -SWCLT_DECLARE(ks_status_t) swclt_sess_rescan_env_config(swclt_sess_t sess); -SWCLT_DECLARE(ks_status_t) swclt_sess_connect(swclt_sess_t sess); -SWCLT_DECLARE(ks_status_t) swclt_sess_disconnect(swclt_sess_t sess); -SWCLT_DECLARE(ks_bool_t) swclt_sess_connected(swclt_sess_t sess); -SWCLT_DECLARE(ks_bool_t) swclt_sess_restored(swclt_sess_t sess); -SWCLT_DECLARE(ks_status_t) swclt_sess_info(swclt_sess_t sess, ks_pool_t *pool, ks_uuid_t *sessionid, char **nodeid, char **master_nodeid); -SWCLT_DECLARE(ks_status_t) swclt_sess_nodeid(swclt_sess_t sess, ks_pool_t *pool, char **nodeid); -SWCLT_DECLARE(ks_bool_t) swclt_sess_nodeid_local(swclt_sess_t sess, const char *nodeid); - -SWCLT_DECLARE(ks_status_t) __swclt_sess_register_protocol_method( - swclt_sess_t sess, + swclt_config_t *config); + +SWCLT_DECLARE(ks_status_t) swclt_sess_destroy(swclt_sess_t **sess); + +SWCLT_DECLARE(ks_status_t) swclt_sess_set_auth_failed_cb(swclt_sess_t *sess, swclt_sess_auth_failed_cb_t cb); +SWCLT_DECLARE(ks_status_t) swclt_sess_set_state_change_cb(swclt_sess_t *sess, swclt_sess_state_change_cb_t cb, void *cb_data); +SWCLT_DECLARE(ks_status_t) swclt_sess_target_set(swclt_sess_t *sess, const char *target); + +SWCLT_DECLARE(ks_status_t) swclt_sess_metric_register(swclt_sess_t *sess, const char *protocol, int interval, int rank); +SWCLT_DECLARE(ks_status_t) swclt_sess_metric_update(swclt_sess_t *sess, const char *protocol, int rank); +SWCLT_DECLARE(ks_status_t) swclt_sess_metric_current(swclt_sess_t *sess, const char *protocol, int *rank); + +SWCLT_DECLARE(ks_bool_t) swclt_sess_has_authentication(swclt_sess_t *sess); +SWCLT_DECLARE(ks_status_t) swclt_sess_rescan_env_config(swclt_sess_t *sess); +SWCLT_DECLARE(ks_status_t) swclt_sess_connect(swclt_sess_t *sess); +SWCLT_DECLARE(ks_status_t) swclt_sess_disconnect(swclt_sess_t *sess); +SWCLT_DECLARE(ks_bool_t) swclt_sess_connected(swclt_sess_t *sess); +SWCLT_DECLARE(ks_bool_t) swclt_sess_restored(swclt_sess_t *sess); +SWCLT_DECLARE(ks_status_t) swclt_sess_info(swclt_sess_t *sess, ks_pool_t *pool, ks_uuid_t *sessionid, char **nodeid, char **master_nodeid); +SWCLT_DECLARE(ks_status_t) swclt_sess_nodeid(swclt_sess_t *sess, ks_pool_t *pool, char **nodeid); +SWCLT_DECLARE(ks_bool_t) swclt_sess_nodeid_local(swclt_sess_t *sess, const char *nodeid); +SWCLT_DECLARE(ks_status_t) swclt_sess_wait_for_cmd_reply(swclt_sess_t *sess, swclt_cmd_future_t **future, swclt_cmd_reply_t **reply); + +SWCLT_DECLARE(ks_status_t) swclt_sess_register_protocol_method( + swclt_sess_t *sess, const char *protocol, const char *method, swclt_pmethod_cb_t pmethod, void *cb_data); -#define swclt_sess_register_protocol_method(sess, protocol, method, pmethod, cb_data) \ - __swclt_sess_register_protocol_method(sess, protocol, method, pmethod, cb_data) SWCLT_DECLARE(ks_status_t) swclt_sess_register_subscription_method( - swclt_sess_t sess, - swclt_sub_t *sub, + swclt_sess_t *sess, const char *protocol, const char *channel, swclt_sub_cb_t cb, void *cb_data); SWCLT_DECLARE(ks_status_t) swclt_sess_broadcast( - swclt_sess_t sess, + swclt_sess_t *sess, const char *protocol, const char *channel, const char *event, ks_json_t **params); SWCLT_DECLARE(ks_status_t) swclt_sess_subscription_add( - swclt_sess_t sess, + swclt_sess_t *sess, const char *protocol, const char *channel, swclt_sub_cb_t cb, void *cb_data, - swclt_sub_t *sub, - swclt_cmd_t *cmdP); + swclt_cmd_reply_t **reply); SWCLT_DECLARE(ks_status_t) swclt_sess_subscription_add_async( - swclt_sess_t sess, + swclt_sess_t *sess, const char *protocol, const char *channel, swclt_sub_cb_t cb, void *cb_data, - swclt_sub_t *sub, swclt_cmd_cb_t response_callback, void *response_callback_data, - swclt_cmd_t *cmdP); + swclt_cmd_future_t **future); SWCLT_DECLARE(ks_status_t) swclt_sess_subscription_remove( - swclt_sess_t sess, + swclt_sess_t *sess, const char *protocol, const char *channel, - swclt_cmd_t *cmdP); + swclt_cmd_reply_t **reply); SWCLT_DECLARE(ks_status_t) swclt_sess_subscription_remove_async( - swclt_sess_t sess, + swclt_sess_t *sess, const char *protocol, const char *channel, swclt_cmd_cb_t response_callback, void *response_callback_data, - swclt_cmd_t *cmd); + swclt_cmd_future_t **future); SWCLT_DECLARE(ks_status_t) swclt_sess_protocol_provider_add( - swclt_sess_t sess, + swclt_sess_t *sess, const char * protocol, blade_access_control_t default_method_execute_access, blade_access_control_t default_channel_subscribe_access, @@ -124,10 +208,10 @@ SWCLT_DECLARE(ks_status_t) swclt_sess_protocol_provider_add( ks_json_t **channels, int rank, ks_json_t **data, - swclt_cmd_t *cmdP); + swclt_cmd_reply_t **reply); SWCLT_DECLARE(ks_status_t) swclt_sess_protocol_provider_add_async( - swclt_sess_t sess, + swclt_sess_t *sess, const char * protocol, blade_access_control_t default_method_execute_access, blade_access_control_t default_channel_subscribe_access, @@ -138,84 +222,95 @@ SWCLT_DECLARE(ks_status_t) swclt_sess_protocol_provider_add_async( ks_json_t **data, swclt_cmd_cb_t response_callback, void *response_callback_data, - swclt_cmd_t *cmd); + swclt_cmd_future_t **future); -SWCLT_DECLARE(ks_status_t) swclt_sess_protocol_provider_remove(swclt_sess_t sess, const char * protocol, swclt_cmd_t *cmdP); +SWCLT_DECLARE(ks_status_t) swclt_sess_protocol_provider_remove(swclt_sess_t *sess, const char * protocol, swclt_cmd_reply_t **reply); SWCLT_DECLARE(ks_status_t) swclt_sess_protocol_provider_remove_async( - swclt_sess_t sess, + swclt_sess_t *sess, const char * protocol, swclt_cmd_cb_t response_callback, void *response_callback_data, - swclt_cmd_t *cmdP); + swclt_cmd_future_t **future); SWCLT_DECLARE(ks_status_t) swclt_sess_protocol_provider_rank_update( - swclt_sess_t sess, + swclt_sess_t *sess, const char * protocol, int rank, - swclt_cmd_t *cmdP); + swclt_cmd_reply_t **reply); SWCLT_DECLARE(ks_status_t) swclt_sess_protocol_provider_rank_update_async( - swclt_sess_t sess, + swclt_sess_t *sess, const char * protocol, int rank, swclt_cmd_cb_t response_callback, void *response_callback_data, - swclt_cmd_t *cmd); + swclt_cmd_future_t **future); -SWCLT_DECLARE(ks_status_t) swclt_sess_identity_add(swclt_sess_t sess, const char *identity, swclt_cmd_t *cmdP); +SWCLT_DECLARE(ks_status_t) swclt_sess_identity_add(swclt_sess_t *sess, const char *identity, swclt_cmd_reply_t **reply); SWCLT_DECLARE(ks_status_t) swclt_sess_identity_add_async( - swclt_sess_t sess, + swclt_sess_t *sess, const char *identity, swclt_cmd_cb_t response_callback, void *response_callback_data, - swclt_cmd_t *cmd); + swclt_cmd_future_t **future); SWCLT_DECLARE(ks_status_t) swclt_sess_execute( - swclt_sess_t sess, + swclt_sess_t *sess, const char *responder, const char *protocol, const char *method, ks_json_t **params, - swclt_cmd_t *cmdP); + swclt_cmd_reply_t **reply); -SWCLT_DECLARE(ks_status_t) swclt_sess_nodestore(swclt_sess_t sess, swclt_store_t *store); +SWCLT_DECLARE(ks_status_t) swclt_sess_execute_with_id( + swclt_sess_t *sess, + const char *id, + const char *responder, + const char *protocol, + const char *method, + ks_json_t **params, + swclt_cmd_reply_t **reply); SWCLT_DECLARE(ks_status_t) swclt_sess_execute_async( - swclt_sess_t sess, + swclt_sess_t *sess, const char *responder, const char *protocol, const char *method, ks_json_t **params, swclt_cmd_cb_t response_callback, void *response_callback_data, - swclt_cmd_t *cmdP); - -SWCLT_DECLARE(ks_status_t) swclt_sess_nodestore(swclt_sess_t sess, swclt_store_t *store); -SWCLT_DECLARE(ks_status_t) swclt_sess_get_rates(swclt_sess_t sess, ks_throughput_t *recv, ks_throughput_t *send); - -#define swclt_sess_get(sess, contextP) __ks_handle_get(SWCLT_HTYPE_SESS, sess, (ks_handle_base_t**)contextP, __FILE__, __LINE__, __PRETTY_FUNCTION__) -#define swclt_sess_put(contextP) __ks_handle_put(SWCLT_HTYPE_SESS, (ks_handle_base_t**)contextP, __FILE__, __LINE__, __PRETTY_FUNCTION__) + swclt_cmd_future_t **future); -SWCLT_DECLARE(ks_status_t) swclt_sess_signalwire_setup(swclt_sess_t sess, const char *service, swclt_sub_cb_t cb, void *cb_data); - -SWCLT_DECLARE(ks_status_t) swclt_sess_provisioning_setup(swclt_sess_t sess, swclt_sub_cb_t cb, void *cb_data); +SWCLT_DECLARE(ks_status_t) swclt_sess_execute_with_id_async( + swclt_sess_t *sess, + const char *id, + const char *responder, + const char *protocol, + const char *method, + ks_json_t **params, + swclt_cmd_cb_t response_callback, + void *response_callback_data, + swclt_cmd_future_t **future); + +SWCLT_DECLARE(ks_status_t) swclt_sess_signalwire_setup(swclt_sess_t *sess, const char *service, swclt_sub_cb_t cb, void *cb_data); +SWCLT_DECLARE(ks_status_t) swclt_sess_provisioning_setup(swclt_sess_t *sess, swclt_sub_cb_t cb, void *cb_data); -SWCLT_DECLARE(ks_status_t) swclt_sess_provisioning_configure(swclt_sess_t sess, +SWCLT_DECLARE(ks_status_t) swclt_sess_provisioning_configure(swclt_sess_t *sess, const char *target, const char *local_endpoint, const char *external_endpoint, const char *relay_connector_id, - swclt_cmd_t *cmdP); -SWCLT_DECLARE(ks_status_t) swclt_sess_provisioning_configure_async(swclt_sess_t sess, + swclt_cmd_reply_t **reply); +SWCLT_DECLARE(ks_status_t) swclt_sess_provisioning_configure_async(swclt_sess_t *sess, const char *target, const char *local_endpoint, const char *external_endpoint, const char *relay_connector_id, swclt_cmd_cb_t response_callback, void *response_callback_data, - swclt_cmd_t *cmdP); + swclt_cmd_future_t **future); KS_END_EXTERN_C diff --git a/inc/signalwire-client-c/subscription.h b/inc/signalwire-client-c/subscription.h index 827e897..3f42f0e 100644 --- a/inc/signalwire-client-c/subscription.h +++ b/inc/signalwire-client-c/subscription.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 SignalWire, Inc + * Copyright (c) 2018-2020 SignalWire, Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,28 +24,37 @@ KS_BEGIN_EXTERN_C -/* All subscriptions are handles, opaque numbers that manage ref counts */ -typedef ks_handle_t swclt_sub_t; +typedef struct swclt_sess swclt_sess_t; -/* Obfuscate our subscription internals */ -typedef struct swclt_sub_ctx swclt_sub_ctx_t; +typedef struct swclt_sub swclt_sub_t; typedef void (*swclt_sub_cb_t)( - ks_handle_t sess, + swclt_sess_t *sess, blade_broadcast_rqu_t *rqu, void *cb_data); +/* A subscription represents a logical registration of + * a listener on a channel */ +struct swclt_sub { + const char *protocol; + const char *channel; + + swclt_sub_cb_t cb; + + void *cb_data; +}; + + SWCLT_DECLARE(ks_status_t) swclt_sub_create( - swclt_sub_t *sub, + swclt_sub_t **sub, + ks_pool_t *pool, const char * const protocol, const char * const channel, swclt_sub_cb_t cb, void *data); -SWCLT_DECLARE(ks_status_t) swclt_sub_invoke(swclt_sub_t sub, ks_handle_t sess, blade_broadcast_rqu_t *broadcast_rqu); - -#define swclt_sub_get(sub, contextP) __ks_handle_get(SWCLT_HTYPE_SUB, sub, (ks_handle_base_t**)contextP, __FILE__, __LINE__, __PRETTY_FUNCTION__) -#define swclt_sub_put(contextP) __ks_handle_put(SWCLT_HTYPE_SUB, (ks_handle_base_t**)contextP, __FILE__, __LINE__, __PRETTY_FUNCTION__) - +SWCLT_DECLARE(ks_status_t) swclt_sub_invoke(swclt_sub_t *sub, swclt_sess_t *sess, blade_broadcast_rqu_t *broadcast_rqu); +SWCLT_DECLARE(char *) swclt_sub_describe(swclt_sub_t *sub); +SWCLT_DECLARE(ks_status_t) swclt_sub_destroy(swclt_sub_t **sub); KS_END_EXTERN_C diff --git a/inc/signalwire-client-c/transport/frame.h b/inc/signalwire-client-c/transport/frame.h index 6e3994c..ed9f57b 100644 --- a/inc/signalwire-client-c/transport/frame.h +++ b/inc/signalwire-client-c/transport/frame.h @@ -22,26 +22,23 @@ #pragma once -/* Obfuscate the frame context internals */ -typedef struct swclt_frame_ctx swclt_frame_ctx_t; +typedef struct swclt_frame { + /* Raw data read from the socket */ + ks_size_t len; + uint8_t *data; -typedef ks_handle_t swclt_frame_t; + /* The operation code for the socket */ + kws_opcode_t opcode; +} swclt_frame_t; KS_BEGIN_EXTERN_C -SWCLT_DECLARE(ks_status_t) __swclt_frame_alloc(swclt_frame_t *frame, const char *file, int line, const char *tag); -#define swclt_frame_alloc(frame) __swclt_frame_alloc(frame, __FILE__, __LINE__, __PRETTY_FUNCTION__) +SWCLT_DECLARE(ks_status_t) swclt_frame_alloc(swclt_frame_t **frame, ks_pool_t *pool); -SWCLT_DECLARE(ks_status_t) swclt_frame_get_json(swclt_frame_t frame, ks_json_t **json); +SWCLT_DECLARE(ks_status_t) swclt_frame_to_json(swclt_frame_t *frame, ks_json_t **json); -SWCLT_DECLARE(ks_status_t) swclt_frame_to_json(swclt_frame_t frame, ks_pool_t *pool, ks_json_t **json); -SWCLT_DECLARE(ks_status_t) swclt_frame_to_json_lookup(swclt_frame_t frame, ks_pool_t *pool, ks_json_t **json, int components, ...); - -SWCLT_DECLARE(ks_status_t) swclt_frame_copy_data(swclt_frame_t frame, void *data, ks_size_t len, kws_opcode_t opcode); -SWCLT_DECLARE(ks_status_t) swclt_frame_get_data(swclt_frame_t frame, void **data, ks_size_t *len, kws_opcode_t *opcode); - -#define swclt_frame_get(frame, contextP) __ks_handle_get(SWCLT_HTYPE_FRAME, frame, (ks_handle_base_t **)contextP, __FILE__, __LINE__, __PRETTY_FUNCTION__) -#define swclt_frame_put(contextP) ks_handle_put(SWCLT_HTYPE_FRAME, contextP) +SWCLT_DECLARE(ks_status_t) swclt_frame_copy_data(swclt_frame_t *frame, ks_pool_t *pool, void *data, ks_size_t len, kws_opcode_t opcode); +SWCLT_DECLARE(ks_status_t) swclt_frame_get_data(swclt_frame_t *frame, void **data, ks_size_t *len, kws_opcode_t *opcode); KS_END_EXTERN_C diff --git a/inc/signalwire-client-c/transport/internal/frame.h b/inc/signalwire-client-c/transport/internal/frame.h deleted file mode 100644 index a10f882..0000000 --- a/inc/signalwire-client-c/transport/internal/frame.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2018 SignalWire, Inc - * - * 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. - */ -#pragma once - -KS_BEGIN_EXTERN_C - -/* The internal context wrapped by the frame handle */ -struct swclt_frame_ctx { - swclt_handle_base_t base; - - /* Raw data read from the socket */ - ks_size_t len; - uint8_t *data; - - /* The frame may be manipulated by multiple threads from time to time, so - * a lightweight lock is a good idea */ - ks_spinlock_t lock; - - /* Cached description */ - const char *cached_description; - - /* Parsed data cached */ - ks_json_t *json; - - /* The operation code for the socket */ - kws_opcode_t opcode; -}; - -KS_END_EXTERN_C - -/* Define helper macros to eliminate boiler code in handle wrapped apis */ -#define SWCLT_FRAME_SCOPE_BEG(frame, ctx, status) \ - KS_HANDLE_SCOPE_BEG(SWCLT_HTYPE_FRAME, frame, swclt_frame_ctx_t, ctx, status); - -#define SWCLT_FRAME_SCOPE_END(frame, ctx, status) \ - KS_HANDLE_SCOPE_END(SWCLT_HTYPE_FRAME, frame, swclt_frame_ctx_t, ctx, status); - -/* For Emacs: - * Local Variables: - * mode:c - * indent-tabs-mode:t - * tab-width:4 - * c-basic-offset:4 - * End: - * For VIM: - * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: - */ - diff --git a/inc/signalwire-client-c/transport/internal/websocket.h b/inc/signalwire-client-c/transport/internal/websocket.h deleted file mode 100644 index bb18a57..0000000 --- a/inc/signalwire-client-c/transport/internal/websocket.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2018 SignalWire, Inc - * - * 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. - */ - -#pragma once - -KS_BEGIN_EXTERN_C - -struct swclt_wss_ctx { - swclt_handle_base_t base; - - /* Callback for when the reader reads a new frame */ - swclt_wss_incoming_frame_cb_t incoming_frame_cb; - void *incoming_frame_cb_data; - - /* Information concerning our connection */ - swclt_wss_info_t info; - - /* Raw socket from libks */ - ks_socket_t socket; - - /* Resolved address structure */ - ks_sockaddr_t addr; - - /* Web socket from libks */ - kws_t *wss; - - /* Ping management */ - ks_time_t ping_next_time_sec; - - /* We keep a read frame around and re-use its data buffer */ - swclt_frame_t read_frame; - - /* Our final status for our read thread, and its thread context - * for thread control (e.g. stop requests) */ - ks_status_t reader_status; - ks_thread_t *reader_thread; - - /* Build in rate calculators track throughput for send/recv all the time */ - ks_throughput_t rate_send, rate_recv; - - /* To maintain state, we lock with mutexes. We have two here - * the read_mutex is locked anytime someone is reading from - * the websocket, and the write_mutex is locked anytime - * someone is writing to the websocket */ - ks_mutex_t *read_mutex, *write_mutex; -}; - -KS_END_EXTERN_C - -/* Define helper macros to eliminate boiler code in handle wrapped apis */ -#define SWCLT_WSS_SCOPE_BEG(wss, ctx, status) \ - KS_HANDLE_SCOPE_BEG(SWCLT_HTYPE_WSS, wss, swclt_wss_ctx_t, ctx, status); - -#define SWCLT_WSS_SCOPE_END(wss, ctx, status) \ - KS_HANDLE_SCOPE_END(SWCLT_HTYPE_WSS, wss, swclt_wss_ctx_t, ctx, status); - -/* For Emacs: - * Local Variables: - * mode:c - * indent-tabs-mode:t - * tab-width:4 - * c-basic-offset:4 - * End: - * For VIM: - * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: - */ diff --git a/inc/signalwire-client-c/transport/websocket.h b/inc/signalwire-client-c/transport/websocket.h index 52302e5..f1ec555 100644 --- a/inc/signalwire-client-c/transport/websocket.h +++ b/inc/signalwire-client-c/transport/websocket.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 SignalWire, Inc + * Copyright (c) 2018-2020 SignalWire, Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,10 +24,10 @@ KS_BEGIN_EXTERN_C -/* Obfuscate our connection internals */ -typedef struct swclt_wss_ctx swclt_wss_ctx_t; +typedef struct swclt_wss swclt_wss_t; -typedef ks_handle_t swclt_wss_t; +typedef ks_status_t (*swclt_wss_incoming_frame_cb_t)(swclt_wss_t *wss, swclt_frame_t **frame, void *cb_data); +typedef ks_status_t (*swclt_wss_failed_cb_t)(swclt_wss_t *wss, void *cb_data); /* Define our info structure */ typedef struct swclt_wss_info_s { @@ -39,24 +39,70 @@ typedef struct swclt_wss_info_s { SSL_CTX *ssl; /* ssl context handed to us at first connect attempt */ } swclt_wss_info_t; -typedef ks_status_t (*swclt_wss_incoming_frame_cb_t)(swclt_wss_t wss, swclt_frame_t frame, void *cb_data); +typedef struct swclt_wss_stats { + int64_t read_frames; + int64_t write_frames; +} swclt_wss_stats_t; + +struct swclt_wss { + + ks_pool_t *pool; + + int failed; + + /* Callback for when the reader reads a new frame */ + swclt_wss_incoming_frame_cb_t incoming_frame_cb; + void *incoming_frame_cb_data; + + /* Callback for when the websocket fails */ + swclt_wss_failed_cb_t failed_cb; + void *failed_cb_data; + + /* Information concerning our connection */ + swclt_wss_info_t info; + + /* Raw socket from libks */ + ks_socket_t socket; + + /* Resolved address structure */ + ks_sockaddr_t addr; + + /* Web socket from libks */ + kws_t *wss; + + /* Ping management */ + ks_time_t ping_next_time_sec; + + /* We keep a read frame around and re-use its data buffer */ + swclt_frame_t *read_frame; + + /* Our final status for our read thread, and its thread context + * for thread control (e.g. stop requests) */ + ks_status_t reader_status; + ks_thread_t *reader_thread; + + ks_mutex_t *wss_mutex; + swclt_wss_stats_t stats; +}; SWCLT_DECLARE(ks_status_t) swclt_wss_connect( - swclt_wss_t *wss, + swclt_wss_t **wss, swclt_wss_incoming_frame_cb_t incoming_frame_cb, void *incoming_frame_cb_data, + swclt_wss_failed_cb_t failed_cb, + void *failed_cb_data, const char *address, short port, const char *path, uint32_t timeout_ms, const SSL_CTX *ssl); -SWCLT_DECLARE(ks_status_t) swclt_wss_write_cmd(swclt_wss_t wss, swclt_cmd_t cmd); -SWCLT_DECLARE(ks_status_t) swclt_wss_get_rates(swclt_wss_t wss, ks_throughput_t *send, ks_throughput_t *recv); -SWCLT_DECLARE(ks_status_t) swclt_wss_get_info(swclt_wss_t wss, swclt_wss_info_t *info); +SWCLT_DECLARE(void) swclt_wss_destroy(swclt_wss_t **wss); -#define swclt_wss_get(wss, contextP) ks_handle_get(SWCLT_HTYPE_WSS, wss, contextP) -#define swclt_wss_put(contextP) ks_handle_put(SWCLT_HTYPE_WSS, contextP) +SWCLT_DECLARE(ks_status_t) swclt_wss_write(swclt_wss_t *wss, char *data); +SWCLT_DECLARE(ks_status_t) swclt_wss_get_info(swclt_wss_t *wss, swclt_wss_info_t *info); +SWCLT_DECLARE(char *) swclt_wss_describe(swclt_wss_t *ctx); +SWCLT_DECLARE(void) swclt_wss_get_stats(swclt_wss_t *ctx, swclt_wss_stats_t *stats); KS_END_EXTERN_C diff --git a/inc/signalwire-client-c/handle_manager.h b/inc/signalwire-client-c/version.h.in similarity index 75% rename from inc/signalwire-client-c/handle_manager.h rename to inc/signalwire-client-c/version.h.in index 5f55192..6f55c73 100644 --- a/inc/signalwire-client-c/handle_manager.h +++ b/inc/signalwire-client-c/version.h.in @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 SignalWire, Inc + * Copyright (c) 2018-2020 SignalWire, Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,13 +22,17 @@ #pragma once -KS_BEGIN_EXTERN_C +#define SIGNALWIRE_CLIENT_C_VERSION_MAJOR @PROJECT_VERSION_MAJOR@ +#define SIGNALWIRE_CLIENT_C_VERSION_MINOR @PROJECT_VERSION_MINOR@ -SWCLT_DECLARE(ks_status_t) swclt_hmgr_init(void); -SWCLT_DECLARE(void) swclt_hmgr_shutdown(void); - -SWCLT_DECLARE(void) swclt_hmgr_request_service_in(swclt_handle_base_t *ctx, ks_time_t time_to_service); -SWCLT_DECLARE(void) swclt_hmgr_request_service_now(swclt_handle_base_t *ctx); - -KS_END_EXTERN_C +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: + */ diff --git a/scan_build.sh b/scan_build.sh new file mode 100755 index 0000000..30f5002 --- /dev/null +++ b/scan_build.sh @@ -0,0 +1,28 @@ +#!/bin/bash +cd /__w/signalwire-c/signalwire-c +sed -i '/cotire/d' ./CMakeLists.txt +sed -i '/cotire/d' ./swclt_test/CMakeLists.txt +mkdir -p scan-build +scan-build-7 -o ./scan-build/ cmake . +scan-build-7 -o ./scan-build/ make -j`nproc --all` |& tee ./scan-build-result.txt +exitstatus=${PIPESTATUS[0]} +echo "*** Exit status is $exitstatus"; +export SubString="scan-build: No bugs found"; +export COMPILATION_FAILED=false; +export BUGS_FOUND=false; +if [ "0" -ne $exitstatus ] ; then + export COMPILATION_FAILED=true; + echo MESSAGE="compilation failed" >> $GITHUB_OUTPUT; +fi +export RESULTFILE="/__w/signalwire-c/signalwire-c/scan-build-result.txt"; +cat $RESULTFILE; +if ! grep -sq "$SubString" $RESULTFILE; then + export BUGS_FOUND=true; + echo MESSAGE="found bugs" >> $GITHUB_OUTPUT; +fi +echo "COMPILATION_FAILED=$COMPILATION_FAILED" >> $GITHUB_OUTPUT; +echo "BUGS_FOUND=$BUGS_FOUND" >> $GITHUB_OUTPUT; +if [ "0" -ne $exitstatus ] || ! grep -sq "$SubString" $RESULTFILE; then + exit 1; +fi +exit 0; \ No newline at end of file diff --git a/signalwire_client.pc.in b/signalwire_client.pc.in index 10ed800..9b19bbd 100644 --- a/signalwire_client.pc.in +++ b/signalwire_client.pc.in @@ -1,7 +1,7 @@ prefix=@PC_PREFIX@ exec_prefix=${prefix} libdir=${prefix}/lib -includedir=${prefix}/include +includedir=${prefix}/include/signalwire-client-c2 definitions=@PC_DEFINITIONS@ Name: @PACKAGE_NAME@ @@ -9,4 +9,4 @@ Version: @PACKAGE_VERSION@ Description: SignalWire C Client SDK Cflags: -I${includedir} -Libs: -L${libdir} -lsignalwire_client +Libs: -L${libdir} -lsignalwire_client2 diff --git a/src/command.c b/src/command.c index bce3b48..a41f010 100644 --- a/src/command.c +++ b/src/command.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2019 SignalWire, Inc + * Copyright (c) 2018-2022 SignalWire, Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,401 +21,522 @@ */ #include "signalwire-client-c/client.h" -#include "signalwire-client-c/internal/command.h" #include "libks/ks_atomic.h" -static void __context_describe_locked(swclt_cmd_ctx_t *ctx, char *buffer, ks_size_t buffer_len) +#define ks_time_now_ms() ks_time_ms(ks_time_now()) + +typedef struct swclt_cmd_future { + ks_pool_t *pool; + ks_cond_t *cond; + swclt_cmd_reply_t *reply; + uint32_t response_ttl_ms; + ks_bool_t got_reply; + ks_uuid_t cmd_id; + ks_bool_t destroy; +} swclt_cmd_future_t; + +static void future_cmd_cb(swclt_cmd_reply_t *cmd_reply, void *cb_data) +{ + swclt_cmd_future_t *future = (swclt_cmd_future_t *)cb_data; + ks_bool_t destroy; + ks_cond_lock(future->cond); + future->got_reply = KS_TRUE; + future->reply = cmd_reply; + destroy = future->destroy; + if (destroy) { + // waiter has given up waiting for reply, clean things up + ks_cond_unlock(future->cond); + swclt_cmd_future_destroy(&future); + } else { + // notify waiter that the reply has arrived + ks_cond_broadcast(future->cond); + ks_cond_unlock(future->cond); + } +} + +SWCLT_DECLARE(ks_status_t) swclt_cmd_future_get(swclt_cmd_future_t *future, swclt_cmd_reply_t **reply) +{ + ks_status_t status; + if (!future->response_ttl_ms) { + return KS_STATUS_FAIL; + } + ks_time_t expiration_ms = ks_time_now_ms() + future->response_ttl_ms + 5000; + ks_cond_lock(future->cond); + while (!future->reply && expiration_ms > ks_time_now_ms()) { + ks_cond_timedwait(future->cond, future->response_ttl_ms); + } + status = swclt_cmd_reply_ok(future->reply); + if (reply && future->reply) { + *reply = future->reply; + future->reply = NULL; + } + ks_cond_unlock(future->cond); + return status; +} + +SWCLT_DECLARE(ks_uuid_t) swclt_cmd_future_get_id(swclt_cmd_future_t *future) +{ + return future->cmd_id; +} + +SWCLT_DECLARE(ks_status_t) swclt_cmd_future_destroy(swclt_cmd_future_t **futureP) +{ + if (futureP && *futureP) { + swclt_cmd_future_t *future = *futureP; + ks_cond_lock(future->cond); + if (future->got_reply) { + // destroy now + ks_cond_unlock(future->cond); + ks_pool_t *pool = future->pool; + ks_cond_destroy(&future->cond); + swclt_cmd_reply_destroy(&future->reply); + ks_pool_close(&pool); + } else { + // request destroy when reply arrives + future->destroy = KS_TRUE; + ks_cond_unlock(future->cond); + } + *futureP = NULL; + } + return KS_STATUS_SUCCESS; +} + +SWCLT_DECLARE(ks_status_t) swclt_cmd_future_create(swclt_cmd_future_t **futureP, swclt_cmd_t *cmd) +{ + ks_pool_t *pool = NULL; + ks_pool_open(&pool); + swclt_cmd_future_t *future = ks_pool_alloc(pool, sizeof(swclt_cmd_future_t)); + future->pool = pool; + future->response_ttl_ms = cmd->response_ttl_ms; + ks_cond_create(&future->cond, pool); + future->reply = NULL; + cmd->cb = future_cmd_cb; + cmd->cb_data = future; + *futureP = future; + return KS_STATUS_SUCCESS; +} + +SWCLT_DECLARE(ks_status_t) swclt_cmd_reply_create(swclt_cmd_reply_t **reply) +{ + if (reply) { + ks_pool_t *pool = NULL; + ks_pool_open(&pool); + *reply = ks_pool_alloc(pool, sizeof(swclt_cmd_reply_t)); + (*reply)->pool = pool; + return KS_STATUS_SUCCESS; + } + return KS_STATUS_ARG_INVALID; +} + +SWCLT_DECLARE(ks_status_t) swclt_cmd_reply_parse(swclt_cmd_reply_t *reply, ks_pool_t *pool, + swclt_cmd_parse_cb_t parse_cb, void **structure) { - switch (ctx->type) { + if (!pool) { + pool = reply->pool; + } + return parse_cb(pool, reply->json, structure); +} + +SWCLT_DECLARE(ks_status_t) swclt_cmd_reply_ok(swclt_cmd_reply_t *reply) +{ + if (reply && reply->type == SWCLT_CMD_TYPE_RESULT && reply->json) { + return KS_STATUS_SUCCESS; + } + return KS_STATUS_GENERR; +} + +SWCLT_DECLARE(ks_status_t) swclt_cmd_reply_destroy(swclt_cmd_reply_t **replyP) +{ + if (replyP && *replyP) { + swclt_cmd_reply_t *reply = *replyP; + ks_pool_t *pool = reply->pool; + *replyP = NULL; + ks_pool_free(&reply->failure_reason); + ks_json_delete(&reply->json); + ks_pool_close(&pool); + } +} + +SWCLT_DECLARE(char *) swclt_cmd_describe(swclt_cmd_t *cmd) +{ + char *str = NULL; + switch (cmd->type) { case SWCLT_CMD_TYPE_REQUEST: { - const char *json_str = ks_json_pprint(ctx->base.pool, ctx->request); - snprintf(buffer, buffer_len, "SWCLT CMD RQU: method: %s Id: %s TTL: %ums params: %s", ctx->method, ks_uuid_thr_str(&ctx->id), ctx->response_ttl_ms, json_str); - ks_pool_free(&json_str); + char *json_str = ks_json_print(cmd->json); + str = ks_psprintf(cmd->pool, "SWCLT CMD RQU: method: %s Id: %s TTL: %ums params: %s", cmd->method, ks_uuid_thr_str(&cmd->id), cmd->response_ttl_ms, json_str); + free(json_str); break; } case SWCLT_CMD_TYPE_RESULT: { - const char *json_str = ks_json_pprint(ctx->base.pool, ctx->reply.result); - snprintf(buffer, buffer_len, "SWCLT CMD RPL: method: %s Id: %s result: %s", ctx->method, ks_uuid_thr_str(&ctx->id), json_str); - ks_pool_free(&json_str); + char *json_str = ks_json_print(cmd->json); + str = ks_psprintf(cmd->pool, "SWCLT CMD RPL: method: %s Id: %s result: %s", cmd->method, ks_uuid_thr_str(&cmd->id), json_str); + free(json_str); break; } case SWCLT_CMD_TYPE_ERROR: { - const char *json_str = ks_json_pprint(ctx->base.pool, ctx->reply.error); - snprintf(buffer, buffer_len, "SWCLT CMD ERR: method: %s Id: %s error: %s", ctx->method, ks_uuid_thr_str(&ctx->id), json_str); - ks_pool_free(&json_str); + char *json_str = ks_json_print(cmd->json); + str = ks_psprintf(cmd->pool, "SWCLT CMD ERR: method: %s Id: %s error: %s", cmd->method, ks_uuid_thr_str(&cmd->id), json_str); + free(json_str); break; } case SWCLT_CMD_TYPE_FAILURE: { - snprintf(buffer, buffer_len, "SWCLT CMD FAIL: %s", ctx->failure_summary); + str = ks_psprintf(cmd->pool, "SWCLT CMD FAIL: method: %s Id: %s", cmd->method, ks_uuid_thr_str(&cmd->id)); break; } default: - ks_abort_fmt("Unexpected command type: %lu", ctx->type); + str = ks_pstrdup(cmd->pool, ""); + break; } + return str; } -static void __raise_callback(swclt_cmd_ctx_t *ctx) +static void __raise_callback(swclt_cmd_t *cmd, swclt_cmd_reply_t **cmd_reply) { - swclt_cmd_cb_t cb = ctx->cb; - void *cb_data = ctx->cb_data; - - /* Its a good idea not to hold on to this lock while we call back - * into other code so do that now */ - swclt_cmd_ctx_unlock(ctx); - if (cb) - cb(ctx->base.handle, cb_data); - swclt_cmd_ctx_lock(ctx); + if (cmd->cb) { + cmd->cb(*cmd_reply, cmd->cb_data); + *cmd_reply = NULL; + } else { + swclt_cmd_reply_destroy(cmd_reply); + } } - -static void __report_failure(const char *file, int line, const char *tag, swclt_cmd_ctx_t *ctx, ks_status_t failure_status, const char *failure_fmt, va_list *ap) +static void __report_failure(swclt_cmd_t *cmd, ks_status_t failure_status, const char *failure_fmt, va_list *ap) { - /* Reset previous allocation if set */ - ks_pool_free(&ctx->failure_reason); - ks_pool_free(&ctx->failure_summary); - - ctx->type = SWCLT_CMD_TYPE_FAILURE; - ctx->failure_status = failure_status; + swclt_cmd_reply_t *reply = NULL; + swclt_cmd_reply_create(&reply); + reply->type = SWCLT_CMD_TYPE_FAILURE; + reply->failure_status = failure_status; if (ap) { - ctx->failure_reason = ks_vpprintf(ctx->base.pool, failure_fmt, *ap); + reply->failure_reason = ks_vpprintf(reply->pool, failure_fmt, *ap); } else { - ctx->failure_reason = ks_pstrdup(ctx->base.pool, failure_fmt); + reply->failure_reason = ks_pstrdup(reply->pool, failure_fmt); } -#if defined(KS_BUILD_DEBUG) - ctx->failure_file = file; - ctx->failure_line = line; - ctx->failure_tag = tag; -#endif - -#if defined(KS_BUILD_DEBUG) - ctx->failure_summary = ks_psprintf(ctx->base.pool, "%s (%lu) [%s:%lu:%s]", ctx->failure_reason, ctx->failure_status, ctx->failure_file, ctx->failure_line, ctx->failure_tag); -#else - ctx->failure_summary = ks_psprintf(ctx->base.pool, "%s (%lu)", ctx->failure_reason, ctx->failure_status); -#endif - - ks_log(KS_LOG_WARNING, "Command was failed: %s", ctx->failure_summary); + ks_log(KS_LOG_WARNING, "Command was failed: %s (%lu)", reply->failure_reason, reply->failure_status); /* Raise the completion callback */ - __raise_callback(ctx); -} - -static void __context_describe(swclt_cmd_ctx_t *ctx, char *buffer, ks_size_t buffer_len) -{ - swclt_cmd_ctx_lock(ctx); - __context_describe_locked(ctx, buffer, buffer_len); - swclt_cmd_ctx_unlock(ctx); + __raise_callback(cmd, &reply); } -static ks_status_t __context_init_frame(swclt_cmd_ctx_t *ctx, swclt_frame_t frame) +static ks_status_t __init_frame(swclt_cmd_t *cmd, swclt_frame_t *frame) { const char *method, *jsonrpc; ks_json_t *original_json; ks_status_t status; + ks_json_t *params; /* Grab the json out of the frame, but don't copy it yet, we'll slice off just * the params portion */ - if (status = swclt_frame_get_json(frame, &original_json)) { - ks_log(KS_LOG_CRIT, "Received invalid frame: %s", ks_handle_describe(frame)); + if (status = swclt_frame_to_json(frame, &original_json)) { + ks_log(KS_LOG_CRIT, "Received invalid frame for command %s", ks_uuid_thr_str(&cmd->id)); return status; } #if defined(SWCLT_DEBUG_JSON) - KS_JSON_PPRINT(ctx->base.pool, "Parsing incoming frame:", original_json); + KS_JSON_PRINT(cmd->pool, "Parsing incoming frame:", original_json); #endif /* Now load the id, method, and verify it has at least a params structure in it as * well as the jsonrpc tag */ - if (!(method = ks_json_get_object_cstr_def(original_json, "method", NULL))) { - ks_log(KS_LOG_WARNING, "Invalid frame given to command construction, no method field present: %s", ks_handle_describe(frame)); - return KS_STATUS_INVALID_ARGUMENT; + if (!(method = ks_json_get_object_string(original_json, "method", NULL))) { + ks_log(KS_LOG_WARNING, "Invalid frame given to command %s construction, no method field present", ks_uuid_thr_str(&cmd->id)); + status = KS_STATUS_INVALID_ARGUMENT; + goto done; } - if (!(ctx->request = ks_json_get_object_item(original_json, "params"))) { - ks_log(KS_LOG_WARNING, "Invalid frame given to command construction, no params field present: %s", ks_handle_describe(frame)); - return KS_STATUS_INVALID_ARGUMENT; + if (!(params = ks_json_get_object_item(original_json, "params"))) { + ks_log(KS_LOG_WARNING, "Invalid frame given to command %s construction, no params field present", ks_uuid_thr_str(&cmd->id)); + status = KS_STATUS_INVALID_ARGUMENT; + goto done; } - if (!(jsonrpc = ks_json_get_object_cstr_def(original_json, "jsonrpc", NULL))) { - ks_log(KS_LOG_WARNING, "Invalid frame given to command construction, no jsonrpc field present: %s", ks_handle_describe(frame)); - return KS_STATUS_INVALID_ARGUMENT; + if (!(jsonrpc = ks_json_get_object_string(original_json, "jsonrpc", NULL))) { + ks_log(KS_LOG_WARNING, "Invalid frame given to command %s construction, no jsonrpc field present", ks_uuid_thr_str(&cmd->id)); + status = KS_STATUS_INVALID_ARGUMENT; + goto done; } - ctx->id = ks_json_get_object_uuid(original_json, "id"); - if (ks_uuid_is_null(&ctx->id)) { - ks_log(KS_LOG_WARNING, "Invalid frame given to command construction, no id (or null id) field: %s", ks_handle_describe(frame)); - return KS_STATUS_INVALID_ARGUMENT; + cmd->id = ks_uuid_from_str(ks_json_get_object_string(original_json, "id", "")); + if (ks_uuid_is_null(&cmd->id)) { + ks_log(KS_LOG_WARNING, "Invalid frame given to command %s construction, no id (or null id) field", ks_uuid_thr_str(&cmd->id)); + status = KS_STATUS_INVALID_ARGUMENT; + goto done; + } + if (!(cmd->id_str = ks_uuid_str(cmd->pool, &cmd->id))) { + status = KS_STATUS_NO_MEM; + goto done; } - if (!(ctx->id_str = ks_uuid_str(ctx->base.pool, &ctx->id))) - return KS_STATUS_NO_MEM; /* Right finally its valid, copy some things */ - if (!(ctx->method = ks_pstrdup(ctx->base.pool, method))) - return KS_STATUS_NO_MEM; + if (!(cmd->method = ks_pstrdup(cmd->pool, method))) { + status = KS_STATUS_NO_MEM; + goto done; + } /* We just need the request portion, dupe just that, leave the rest in ownership * of the frame */ - if (!(ctx->request = ks_json_pduplicate(ctx->base.pool, ks_json_lookup(original_json, 1, "params"), KS_TRUE))) { - ks_log(KS_LOG_CRIT, "Failed to allocate json request from frame: %s", ks_handle_describe(frame)); - return KS_STATUS_INVALID_ARGUMENT; + if (!(cmd->json = ks_json_duplicate(params, KS_TRUE))) { + ks_log(KS_LOG_CRIT, "Failed to allocate json request from command %s frame", ks_uuid_thr_str(&cmd->id)); + status = KS_STATUS_INVALID_ARGUMENT; + goto done; } - return KS_STATUS_SUCCESS; +done: + if (original_json) { + ks_json_delete(&original_json); + } + return status; } -static ks_status_t __context_init(swclt_cmd_ctx_t *ctx, swclt_cmd_cb_t cb, void *cb_data, const char * const method, - ks_json_t **request, uint32_t response_ttl_ms, uint32_t flags, ks_uuid_t uuid, swclt_frame_t frame) +static ks_status_t __init_cmd(swclt_cmd_t **cmdP, swclt_cmd_cb_t cb, void *cb_data, const char * const method, + ks_json_t **request, uint32_t response_ttl_ms, uint32_t flags, ks_uuid_t uuid, swclt_frame_t *frame) { + ks_pool_t *pool = NULL; + ks_pool_open(&pool); + swclt_cmd_t *cmd = ks_pool_alloc(pool, sizeof(swclt_cmd_t)); + *cmdP = cmd; + cmd->pool = pool; + /* Stash their callback if they're doing async (null would imply blocking)*/ - ctx->cb = cb; - ctx->cb_data = cb_data; + cmd->cb = cb; + cmd->cb_data = cb_data; - ctx->response_ttl_ms = response_ttl_ms; + cmd->response_ttl_ms = response_ttl_ms; /* We always start out as a request */ - ctx->type = SWCLT_CMD_TYPE_REQUEST; + cmd->type = SWCLT_CMD_TYPE_REQUEST; /* Now if they didn't specify anything its invalid, unless they're wanting to construct * from a frame */ if (!method || !request || !*request) { if (frame) { - return __context_init_frame(ctx, frame); + ks_status_t status = __init_frame(cmd, frame); + if (status != KS_STATUS_SUCCESS) { + swclt_cmd_destroy(cmdP); + } + return status; } - ks_log(KS_LOG_CRIT, "Context init failed invalid arguments: %s", ks_handle_describe(frame)); + ks_log(KS_LOG_WARNING, "Command %s init failed invalid arguments", ks_uuid_thr_str(&uuid)); + swclt_cmd_destroy(cmdP); return KS_STATUS_INVALID_ARGUMENT; } /* Take ownership of their request and null it out to indicate that */ - ctx->request = *request; + cmd->json = *request; *request = NULL; - /* Always dupe their string, their method string is their rsponsibility but, typically - * it will be a constant literal anyway */ - if (!(ctx->method = ks_pstrdup(ctx->base.pool, method))) { - /* Note we don't have to clean up here, context_deinit will get called by the - * handle template macro */ - return KS_STATUS_NO_MEM; - } + cmd->method = ks_pstrdup(cmd->pool, method); /* Generate a new uuid if the one they passed in is null */ if (ks_uuid_is_null(&uuid)) - ks_uuid(&ctx->id); + ks_uuid(&cmd->id); else - ctx->id = uuid; + cmd->id = uuid; - if (!(ctx->id_str = ks_uuid_str(ctx->base.pool, &ctx->id))) - return KS_STATUS_NO_MEM; + cmd->id_str = ks_uuid_str(cmd->pool, &cmd->id); + + cmd->flags = flags; - ctx->flags = flags; return KS_STATUS_SUCCESS; } -static void __context_deinit(swclt_cmd_ctx_t *ctx) +SWCLT_DECLARE(swclt_cmd_t *) swclt_cmd_duplicate(swclt_cmd_t *cmd) { - ks_pool_free(&ctx->method); - ks_pool_free(&ctx->id_str); - ks_json_delete((ks_json_t **)&ctx->request); - ks_json_delete(&ctx->reply.error); - ks_pool_free(&ctx->failure_summary); - ks_pool_free(&ctx->failure_reason); + ks_pool_t *pool = NULL; + ks_pool_open(&pool); + swclt_cmd_t *dup_cmd = ks_pool_alloc(pool, sizeof(*dup_cmd)); + dup_cmd->pool = pool; + if (cmd->json) { + dup_cmd->json = ks_json_duplicate(cmd->json, KS_TRUE); + } + dup_cmd->type = cmd->type; + dup_cmd->id = cmd->id; + dup_cmd->id_str = ks_uuid_str(dup_cmd->pool, &dup_cmd->id); + dup_cmd->response_ttl_ms = cmd->response_ttl_ms; + if (cmd->method) { + dup_cmd->method = ks_pstrdup(dup_cmd->pool, cmd->method); + } + dup_cmd->flags = cmd->flags; + return dup_cmd; } -static ks_json_t * __wrap_jsonrpc(swclt_cmd_ctx_t *ctx, const char *version, +SWCLT_DECLARE(ks_status_t) swclt_cmd_destroy(swclt_cmd_t **cmdP) +{ + if (cmdP && *cmdP) { + swclt_cmd_t *cmd = *cmdP; + *cmdP = NULL; + ks_pool_t *pool = cmd->pool; + ks_json_delete(&cmd->json); + ks_pool_close(&pool); + } +} + +static ks_json_t * __wrap_jsonrpc(const char *version, const char *method, const char *id, ks_json_t *params, ks_json_t *result, ks_json_t *error) { - ks_json_t *jsonrpc_object = ks_json_pcreate_object(ctx->base.pool); + ks_json_t *jsonrpc_object = ks_json_create_object(); ks_assertd(version); ks_assertd(id); - ks_assertd(ks_json_padd_string_to_object(ctx->base.pool, jsonrpc_object, "jsonrpc", version)); - ks_assertd(ks_json_padd_string_to_object(ctx->base.pool, jsonrpc_object, "id", id)); + ks_json_add_string_to_object(jsonrpc_object, "jsonrpc", version); + ks_json_add_string_to_object(jsonrpc_object, "id", id); if (method) { ks_assertd(!result); ks_assertd(!error); - ks_assertd(ks_json_padd_string_to_object(ctx->base.pool, jsonrpc_object, "method", method)); - ks_assertd(ks_json_add_item_to_object(jsonrpc_object, "params", params)); + ks_json_add_string_to_object(jsonrpc_object, "method", method); + ks_json_add_item_to_object(jsonrpc_object, "params", params); } else if (result) { ks_assertd(!params); ks_assertd(!method); ks_assertd(!error); - ks_assertd(ks_json_add_item_to_object(jsonrpc_object, "result", result)); + ks_json_add_item_to_object(jsonrpc_object, "result", result); } else { ks_assertd(error); ks_assertd(!params); ks_assertd(!method); ks_assertd(!result); - ks_assertd(ks_json_add_item_to_object(jsonrpc_object, "error", error)); + ks_json_add_item_to_object(jsonrpc_object, "error", error); } return jsonrpc_object; } -static ks_status_t __print_request(swclt_cmd_ctx_t *ctx, ks_pool_t *pool, char ** const string) +static ks_status_t __print_request(swclt_cmd_t *cmd, ks_pool_t *pool, char ** const string) { ks_json_t *jsonrpc_request; if (!pool) - pool = ctx->base.pool; + pool = cmd->pool; - jsonrpc_request = __wrap_jsonrpc(ctx, "2.0", ctx->method, - ctx->id_str, ks_json_pduplicate(pool, (ks_json_t *)ctx->request, KS_TRUE), NULL, NULL); + jsonrpc_request = __wrap_jsonrpc("2.0", cmd->method, + cmd->id_str, ks_json_duplicate(cmd->json, KS_TRUE), NULL, NULL); if (!jsonrpc_request) return KS_STATUS_NO_MEM; - *string = ks_json_pprint_unformatted(pool, jsonrpc_request); - if (!*string) { + char *json_string = ks_json_print_unformatted(jsonrpc_request); + if (!json_string) { ks_json_delete(&jsonrpc_request); return KS_STATUS_NO_MEM; } ks_json_delete(&jsonrpc_request); + *string = ks_pstrdup(pool, json_string); + free(json_string); return KS_STATUS_SUCCESS; } -static ks_status_t __print_result(swclt_cmd_ctx_t *ctx, ks_pool_t *pool, char **string) +static ks_status_t __print_result(swclt_cmd_t *cmd, ks_pool_t *pool, char **string) { ks_json_t *jsonrpc_result; - if (ctx->type != SWCLT_CMD_TYPE_RESULT) { - ks_log(KS_LOG_WARNING, "Attempt to print incorrect result type, command type is: %s", swclt_cmd_type_str(ctx->type)); + if (cmd->type != SWCLT_CMD_TYPE_RESULT) { + ks_log(KS_LOG_WARNING, "Attempt to print incorrect result type, command type is: %s", swclt_cmd_type_str(cmd->type)); return KS_STATUS_INVALID_ARGUMENT; } if (!pool) - pool = ctx->base.pool; + pool = cmd->pool; - jsonrpc_result = __wrap_jsonrpc(ctx, "2.0", NULL, - ctx->id_str, NULL, ks_json_pduplicate(pool, ctx->reply.result, KS_TRUE), NULL); + jsonrpc_result = __wrap_jsonrpc("2.0", NULL, + cmd->id_str, NULL, ks_json_duplicate(cmd->json, KS_TRUE), NULL); if (!jsonrpc_result) return KS_STATUS_NO_MEM; - *string = ks_json_pprint_unformatted(ctx->base.pool, jsonrpc_result); - if (!*string) { + char *json_string = ks_json_print_unformatted(jsonrpc_result); + if (!json_string) { ks_json_delete(&jsonrpc_result); return KS_STATUS_NO_MEM; } ks_json_delete(&jsonrpc_result); + *string = ks_pstrdup(pool, json_string); + free(json_string); return KS_STATUS_SUCCESS; } -static ks_status_t __print_error(swclt_cmd_ctx_t *ctx, ks_pool_t *pool, char **string) +static ks_status_t __print_error(swclt_cmd_t *cmd, ks_pool_t *pool, char **string) { ks_json_t *jsonrpc_error; - if (ctx->type != SWCLT_CMD_TYPE_ERROR) { - ks_log(KS_LOG_WARNING, "Attempt to print incorrect error type, command type is: %s", swclt_cmd_type_str(ctx->type)); + if (cmd->type != SWCLT_CMD_TYPE_ERROR) { + ks_log(KS_LOG_WARNING, "Attempt to print incorrect error type, command type is: %s", swclt_cmd_type_str(cmd->type)); return KS_STATUS_INVALID_ARGUMENT; } if (!pool) - pool = ctx->base.pool; + pool = cmd->pool; - jsonrpc_error = __wrap_jsonrpc(ctx, "2.0", NULL, - ctx->id_str, NULL, NULL, ks_json_pduplicate(pool, ctx->reply.error, KS_TRUE)); + jsonrpc_error = __wrap_jsonrpc("2.0", NULL, + cmd->id_str, NULL, NULL, ks_json_duplicate(cmd->json, KS_TRUE)); if (!jsonrpc_error) return KS_STATUS_NO_MEM; - *string = ks_json_pprint_unformatted(ctx->base.pool, jsonrpc_error); - if (!*string) { + char *json_string = ks_json_print_unformatted(jsonrpc_error); + if (!json_string) { ks_json_delete(&jsonrpc_error); return KS_STATUS_NO_MEM; } ks_json_delete(&jsonrpc_error); + *string = ks_pstrdup(pool, json_string); + free(json_string); return KS_STATUS_SUCCESS; } -static ks_status_t __set_result(swclt_cmd_ctx_t *ctx, ks_json_t **result) +static ks_status_t __set_result(swclt_cmd_t *cmd, ks_json_t **result) { /* In case someone calls this twice free the previous one */ - ks_json_delete(&ctx->reply.result); + ks_json_delete(&cmd->json); /* Take ownership of their result json blob and assign it */ - ctx->reply.result = *result; + cmd->json = *result; *result = NULL; /* Our command type is now result */ - ctx->type = SWCLT_CMD_TYPE_RESULT; + cmd->type = SWCLT_CMD_TYPE_RESULT; return KS_STATUS_SUCCESS; } -static ks_status_t __set_error(swclt_cmd_ctx_t *ctx, ks_json_t **error) +static ks_status_t __set_error(swclt_cmd_t *cmd, ks_json_t **error) { /* In case someone calls this twice free the previous one */ - ks_json_delete(&ctx->reply.error); + ks_json_delete(&cmd->json); /* Take ownership of their error json blob and assign it */ - ctx->reply.error = *error; + cmd->json = *error; *error = NULL; /* Our command type is now error */ - ctx->type = SWCLT_CMD_TYPE_ERROR; + cmd->type = SWCLT_CMD_TYPE_ERROR; return KS_STATUS_SUCCESS; } -SWCLT_DECLARE(ks_status_t) __swclt_cmd_create_frame( - swclt_cmd_t *cmd, - swclt_cmd_cb_t cb, - void *cb_data, - swclt_frame_t frame, - uint32_t response_ttl_ms, - uint32_t flags, - const char *file, - int line, - const char *tag) -{ - SWCLT_HANDLE_ALLOC_TEMPLATE_M_TAG( - NULL, - file, - line, - tag, - SWCLT_HTYPE_CMD, - cmd, - swclt_cmd_ctx_t, - SWCLT_HSTATE_NORMAL, - __context_describe, - __context_deinit, - __context_init, - cb, - cb_data, - NULL, - NULL, - response_ttl_ms, - flags, - ks_uuid_null(), - frame); +SWCLT_DECLARE(ks_status_t) swclt_cmd_create_frame( + swclt_cmd_t **cmd, + swclt_cmd_cb_t cb, + void *cb_data, + swclt_frame_t *frame, + uint32_t response_ttl_ms, + uint32_t flags) +{ + return __init_cmd(cmd, cb, cb_data, NULL, NULL, response_ttl_ms, flags, ks_uuid_null(), frame); } -SWCLT_DECLARE(ks_status_t) __swclt_cmd_create_ex( - swclt_cmd_t *cmd, - ks_pool_t **pool, +SWCLT_DECLARE(ks_status_t) swclt_cmd_create_ex( + swclt_cmd_t **cmd, swclt_cmd_cb_t cb, void *cb_data, const char * const method, ks_json_t **request, uint32_t response_ttl_ms, uint32_t flags, - ks_uuid_t id, - const char *file, - int line, - const char *tag) -{ - SWCLT_HANDLE_ALLOC_TEMPLATE_M_TAG( - pool, - file, - line, - tag, - SWCLT_HTYPE_CMD, + ks_uuid_t id) +{ + __init_cmd( cmd, - swclt_cmd_ctx_t, - SWCLT_HSTATE_NORMAL, - __context_describe, - __context_deinit, - __context_init, cb, cb_data, method, @@ -423,365 +544,142 @@ SWCLT_DECLARE(ks_status_t) __swclt_cmd_create_ex( response_ttl_ms, flags, id, - KS_NULL_HANDLE); + NULL); + return KS_STATUS_SUCCESS; } -SWCLT_DECLARE(ks_status_t) __swclt_cmd_create( - swclt_cmd_t *cmd, +SWCLT_DECLARE(ks_status_t) swclt_cmd_create( + swclt_cmd_t **cmd, const char * const method, ks_json_t **request, uint32_t response_ttl_ms, - uint32_t flags, - const char *file, - int line, - const char *tag) + uint32_t flags) { - return __swclt_cmd_create_ex(cmd, NULL, NULL, NULL, method, request, response_ttl_ms, flags, ks_uuid_null(), file, line, tag); + return swclt_cmd_create_ex(cmd, NULL, NULL, method, request, response_ttl_ms, flags, ks_uuid_null()); } -SWCLT_DECLARE(ks_status_t) __swclt_cmd_cb(swclt_cmd_t cmd, swclt_cmd_cb_t *cb, void **cb_data, const char *file, int line, const char *tag) +SWCLT_DECLARE(ks_status_t) swclt_cmd_print(swclt_cmd_t *cmd, ks_pool_t *pool, char **string) { - SWCLT_CMD_SCOPE_BEG_TAG(cmd, ctx, status, file, line, tag) - - swclt_cmd_ctx_lock(ctx); - *cb = ctx->cb; - *cb_data = ctx->cb_data; - swclt_cmd_ctx_unlock(ctx); - - SWCLT_CMD_SCOPE_END_TAG(cmd, ctx, status, file, line, tag) -} + ks_status_t status = KS_STATUS_SUCCESS; - -SWCLT_DECLARE(ks_status_t) __swclt_cmd_print(swclt_cmd_t cmd, ks_pool_t *pool, char **string, const char *file, int line, const char *tag) -{ - SWCLT_CMD_SCOPE_BEG_TAG(cmd, ctx, status, file, line, tag) - - swclt_cmd_ctx_lock(ctx); - - switch (ctx->type) { + switch (cmd->type) { case SWCLT_CMD_TYPE_REQUEST: - status = __print_request(ctx, pool, string); + status = __print_request(cmd, pool, string); break; case SWCLT_CMD_TYPE_RESULT: - status = __print_result(ctx, pool, string); + status = __print_result(cmd, pool, string); break; case SWCLT_CMD_TYPE_ERROR: - status = __print_error(ctx, pool, string); + status = __print_error(cmd, pool, string); break; default: - //status = KS_STATUS_INVALID_ARGUMENT; - ks_abort_fmt("Unexpected command context type: %lu", ctx->type); - break; - } - - swclt_cmd_ctx_unlock(ctx); - - SWCLT_CMD_SCOPE_END_TAG(cmd, ctx, status, file, line, tag); -} - -SWCLT_DECLARE(ks_status_t) __swclt_cmd_set_submit_time(swclt_cmd_t cmd, ks_time_t submit_time, const char *file, int line, const char *tag) -{ - SWCLT_CMD_SCOPE_BEG_TAG(cmd, ctx, status, file, line, tag); - - swclt_cmd_ctx_lock(ctx); - ctx->submit_time = submit_time; - swclt_cmd_ctx_unlock(ctx); - - SWCLT_CMD_SCOPE_END_TAG(cmd, ctx, status, file, line, tag); -} - -SWCLT_DECLARE(ks_status_t) __swclt_cmd_set_cb(swclt_cmd_t cmd, swclt_cmd_cb_t cb, void *cb_data, const char *file, int line, const char *tag) -{ - SWCLT_CMD_SCOPE_BEG_TAG(cmd, ctx, status, file, line, tag); - - swclt_cmd_ctx_lock(ctx); - ctx->cb = cb; - ctx->cb_data = cb_data; - swclt_cmd_ctx_unlock(ctx); - - SWCLT_CMD_SCOPE_END_TAG(cmd, ctx, status, file, line, tag); -} - -SWCLT_DECLARE(ks_status_t) __swclt_cmd_set_ttl(swclt_cmd_t cmd, uint32_t response_ttl_ms, const char *file, int line, const char *tag) -{ - SWCLT_CMD_SCOPE_BEG_TAG(cmd, ctx, status, file, line, tag); - - swclt_cmd_ctx_lock(ctx); - ctx->response_ttl_ms = response_ttl_ms; - swclt_cmd_ctx_unlock(ctx); - - SWCLT_CMD_SCOPE_END_TAG(cmd, ctx, status, file, line, tag); -} - -SWCLT_DECLARE(ks_status_t) __swclt_cmd_ttl(swclt_cmd_t cmd, uint32_t *response_ttl_ms, const char *file, int line, const char *tag) -{ - SWCLT_CMD_SCOPE_BEG_TAG(cmd, ctx, status, file, line, tag); - - swclt_cmd_ctx_lock(ctx); - *response_ttl_ms = ctx->response_ttl_ms; - swclt_cmd_ctx_unlock(ctx); - - SWCLT_CMD_SCOPE_END_TAG(cmd, ctx, status, file, line, tag); -} - -SWCLT_DECLARE(ks_status_t) __swclt_cmd_submit_time(swclt_cmd_t cmd, ks_time_t *submit_time, const char *file, int line, const char *tag) -{ - SWCLT_CMD_SCOPE_BEG_TAG(cmd, ctx, status, file, line, tag); - - swclt_cmd_ctx_lock(ctx); - *submit_time = ctx->submit_time; - swclt_cmd_ctx_unlock(ctx); - - SWCLT_CMD_SCOPE_END_TAG(cmd, ctx, status, file, line, tag); -} - -SWCLT_DECLARE(ks_status_t) __swclt_cmd_id(swclt_cmd_t cmd, ks_uuid_t *id, const char *file, int line, const char *tag) -{ - SWCLT_CMD_SCOPE_BEG_TAG(cmd, ctx, status, file, line, tag); - - swclt_cmd_ctx_lock(ctx); - *id = ctx->id; - swclt_cmd_ctx_unlock(ctx); - - SWCLT_CMD_SCOPE_END_TAG(cmd, ctx, status, file, line, tag); -} - -SWCLT_DECLARE(ks_status_t) __swclt_cmd_flags(swclt_cmd_t cmd, uint32_t *flags, const char *file, int line, const char *tag) -{ - SWCLT_CMD_SCOPE_BEG_TAG(cmd, ctx, status, file, line, tag); - - swclt_cmd_ctx_lock(ctx); - *flags = ctx->flags; - swclt_cmd_ctx_unlock(ctx); - - SWCLT_CMD_SCOPE_END_TAG(cmd, ctx, status, file, line, tag); -} - -SWCLT_DECLARE(ks_status_t) __swclt_cmd_lookup_parse(const char *file, int line, const char *tag, swclt_cmd_t cmd, ks_pool_t *pool, - swclt_cmd_parse_cb_t parse_cb, void **structure, int components, ...) -{ - SWCLT_CMD_SCOPE_BEG_TAG(cmd, ctx, status, file, line, tag); - va_list argptr; - ks_json_t *obj; - - swclt_cmd_ctx_lock(ctx); - - /* If no pool specified, assume our own */ - if (!pool) - pool = ctx->base.pool; - - va_start(argptr, components); - - if (ctx->type == SWCLT_CMD_TYPE_REQUEST) { - if (!(obj = ks_json_valookup(ctx->request, components, argptr))) { - status = KS_STATUS_NOT_FOUND; - goto done; - } - } else if (ctx->type == SWCLT_CMD_TYPE_RESULT) { - if (!(obj = ks_json_valookup(ctx->reply.result, components, argptr))) { - status = KS_STATUS_NOT_FOUND; - goto done; - } - } else { status = KS_STATUS_INVALID_ARGUMENT; - ks_log(KS_LOG_CRIT, "Failed to lookup and parse: %s:%lu:%s", file, line, tag); - goto done; - } - - status = parse_cb(pool, obj, structure); - -done: - swclt_cmd_ctx_unlock(ctx); - va_end(argptr); - SWCLT_CMD_SCOPE_END_TAG(cmd, ctx, status, file, line, tag); -} - -SWCLT_DECLARE(ks_status_t) __swclt_cmd_request(swclt_cmd_t cmd, const ks_json_t **request, const char *file, int line, const char *tag) -{ - SWCLT_CMD_SCOPE_BEG_TAG(cmd, ctx, status, file, line, tag); - - swclt_cmd_ctx_lock(ctx); - *request = (ks_json_t *)ctx->request; - swclt_cmd_ctx_unlock(ctx); - - SWCLT_CMD_SCOPE_END_TAG(cmd, ctx, status, file, line, tag); -} - -SWCLT_DECLARE(ks_status_t) __swclt_cmd_error(swclt_cmd_t cmd, const ks_json_t **error, const char *file, int line, const char *tag) -{ - SWCLT_CMD_SCOPE_BEG_TAG(cmd, ctx, status, file, line, tag); - - swclt_cmd_ctx_lock(ctx); - - if (ctx->type != SWCLT_CMD_TYPE_ERROR) { - status = KS_STATUS_INVALID_ARGUMENT; - ks_log(KS_LOG_CRIT, "Invalid type except SWCLT_CMD_TYPE_ERROR"); - goto done; + *string = NULL; + break; } - *error = ctx->reply.error; - -done: - swclt_cmd_ctx_unlock(ctx); - - SWCLT_CMD_SCOPE_END_TAG(cmd, ctx, status, file, line, tag); + return status; } -SWCLT_DECLARE(ks_status_t) __swclt_cmd_result(swclt_cmd_t cmd, const ks_json_t **result, const char *file, int line, const char *tag) +SWCLT_DECLARE(ks_status_t) swclt_cmd_set_cb(swclt_cmd_t *cmd, swclt_cmd_cb_t cb, void *cb_data) { - SWCLT_CMD_SCOPE_BEG_TAG(cmd, ctx, status, file, line, tag); - - swclt_cmd_ctx_lock(ctx); - - if (ctx->type != SWCLT_CMD_TYPE_RESULT) { - status = KS_STATUS_INVALID_ARGUMENT; - ks_log(KS_LOG_CRIT, "Invalid type expected result"); - goto done; + ks_status_t status = KS_STATUS_INVALID_ARGUMENT; + if (!(cmd->flags & SWCLT_CMD_FLAG_NOREPLY)) { + cmd->cb = cb; + cmd->cb_data = cb_data; + status = KS_STATUS_SUCCESS; } - - *result = ctx->reply.result; - -done: - swclt_cmd_ctx_unlock(ctx); - - SWCLT_CMD_SCOPE_END_TAG(cmd, ctx, status, file, line, tag); -} - -SWCLT_DECLARE(ks_status_t) __swclt_cmd_method(swclt_cmd_t cmd, const char **method, const char *file, int line, const char *tag) -{ - SWCLT_CMD_SCOPE_BEG_TAG(cmd, ctx, status, file, line, tag); - - swclt_cmd_ctx_lock(ctx); - *method = ctx->method; - swclt_cmd_ctx_unlock(ctx); - - SWCLT_CMD_SCOPE_END_TAG(cmd, ctx, status, file, line, tag); + return status; } -SWCLT_DECLARE(ks_status_t) __swclt_cmd_type(swclt_cmd_t cmd, SWCLT_CMD_TYPE *type, - const char *file, int line, const char *tag) +SWCLT_DECLARE(ks_status_t) swclt_cmd_set_ttl(swclt_cmd_t *cmd, uint32_t response_ttl_ms) { - SWCLT_CMD_SCOPE_BEG_TAG(cmd, ctx, status, file, line, tag); - - swclt_cmd_ctx_lock(ctx); - *type = ctx->type; - swclt_cmd_ctx_unlock(ctx); - - SWCLT_CMD_SCOPE_END_TAG(cmd, ctx, status, file, line, tag); -} - -SWCLT_DECLARE(ks_status_t) __swclt_cmd_failure_info(swclt_cmd_t cmd, ks_status_t *failure_status, - const char **failure_reason, const char *file, int line, const char *tag) -{ - SWCLT_CMD_SCOPE_BEG_TAG(cmd, ctx, status, file, line, tag); - swclt_cmd_ctx_lock(ctx); - - *failure_status = ctx->failure_status; - *failure_reason = ctx->failure_reason; - - swclt_cmd_ctx_unlock(ctx); - SWCLT_CMD_SCOPE_END_TAG(cmd, ctx, status, file, line, tag); + cmd->response_ttl_ms = response_ttl_ms; + return KS_STATUS_SUCCESS; } -SWCLT_DECLARE(ks_status_t) __swclt_cmd_report_failure(swclt_cmd_t cmd, ks_status_t failure_status, - const char *failure_description, const char *file, int line, const char* tag) +SWCLT_DECLARE(ks_status_t) swclt_cmd_report_failure(swclt_cmd_t *cmd, ks_status_t failure_status, + const char *failure_description) { - SWCLT_CMD_SCOPE_BEG_TAG(cmd, ctx, status, file, line, tag); - - swclt_cmd_ctx_lock(ctx); - __report_failure(file, line, tag, ctx, failure_status, failure_description, NULL); - swclt_cmd_ctx_unlock(ctx); - - SWCLT_CMD_SCOPE_END_TAG(cmd, ctx, status, file, line, tag); + __report_failure(cmd, failure_status, failure_description, NULL); + return KS_STATUS_SUCCESS; } -SWCLT_DECLARE(ks_status_t) __swclt_cmd_report_failure_fmt(const char *file, int line, const char* tag, swclt_cmd_t cmd, ks_status_t failure_status, const char *failure_fmt, ...) +SWCLT_DECLARE(ks_status_t) swclt_cmd_report_failure_fmt(swclt_cmd_t *cmd, ks_status_t failure_status, const char *failure_fmt, ...) { - SWCLT_CMD_SCOPE_BEG_TAG(cmd, ctx, status, file, line, tag); va_list ap; va_start(ap, failure_fmt); - swclt_cmd_ctx_lock(ctx); - __report_failure(file, line, tag, ctx, failure_status, failure_fmt, &ap); - swclt_cmd_ctx_unlock(ctx); + __report_failure(cmd, failure_status, failure_fmt, &ap); va_end(ap); - SWCLT_CMD_SCOPE_END_TAG(cmd, ctx, status, file, line, tag); + return KS_STATUS_SUCCESS; } -SWCLT_DECLARE(ks_status_t) __swclt_cmd_set_result(swclt_cmd_t cmd, ks_json_t **result, const char *file, int line, const char *tag) +SWCLT_DECLARE(ks_status_t) swclt_cmd_set_result(swclt_cmd_t *cmd, ks_json_t **result) { - SWCLT_CMD_SCOPE_BEG_TAG(cmd, ctx, status, file, line, tag); - - swclt_cmd_ctx_lock(ctx); - __set_result(ctx, result); - swclt_cmd_ctx_unlock(ctx); - - SWCLT_CMD_SCOPE_END_TAG(cmd, ctx, status, file, line, tag); + __set_result(cmd, result); + return KS_STATUS_SUCCESS; } -SWCLT_DECLARE(ks_status_t) __swclt_cmd_set_error(swclt_cmd_t cmd, ks_json_t **error, const char *file, int line, const char *tag) +SWCLT_DECLARE(ks_status_t) swclt_cmd_set_error(swclt_cmd_t *cmd, ks_json_t **error) { - SWCLT_CMD_SCOPE_BEG_TAG(cmd, ctx, status, file, line, tag); - - swclt_cmd_ctx_lock(ctx); - __set_error(ctx, error); - swclt_cmd_ctx_unlock(ctx); - - SWCLT_CMD_SCOPE_END_TAG(cmd, ctx, status, file, line, tag); + __set_error(cmd, error); + return KS_STATUS_SUCCESS; } -SWCLT_DECLARE(ks_status_t) __swclt_cmd_parse_reply_frame(swclt_cmd_t cmd, swclt_frame_t frame, ks_bool_t *async, const char *file, int line, const char *tag) +SWCLT_DECLARE(ks_status_t) swclt_cmd_parse_reply_frame(swclt_cmd_t *cmd, swclt_frame_t *frame) { - SWCLT_CMD_SCOPE_BEG_TAG(cmd, ctx, status, file, line, tag); + ks_status_t status = KS_STATUS_SUCCESS; + ks_json_t *json = NULL; + swclt_cmd_reply_t *reply = NULL; + swclt_cmd_reply_create(&reply); - swclt_cmd_ctx_lock(ctx); - - /* Convert the frame to json */ - ks_json_t *reply, *result, *error; + if (cmd->type != SWCLT_CMD_TYPE_REQUEST) { + ks_log(KS_LOG_INFO, "Discarding reply - command %s has already been finalized", ks_uuid_thr_str(&cmd->id)); + status = KS_STATUS_SUCCESS; // it is OK to continue + goto done; + } /* Get the json out of the frame */ - if (status = swclt_frame_to_json(frame, ctx->base.pool, &reply)) { - ctx->failure_status = status; - ctx->failure_reason = (char *)ks_pstrdup(ctx->base.pool, "Failed to parse the frame"); + if (status = swclt_frame_to_json(frame, &json)) { + reply->failure_status = status; + reply->failure_reason = (char *)ks_pstrdup(reply->pool, "Failed to parse the frame"); goto done; } /* Now lets see if it was an error or not */ - if (result = ks_json_get_object_item(reply, "result")) { + ks_json_t *result = NULL; + if (result = ks_json_get_object_item(json, "result")) { /* Hooray success */ - ctx->reply.result = result; - ctx->type = SWCLT_CMD_TYPE_RESULT; - } else if (error = ks_json_get_object_item(reply, "error")) { + reply->json = ks_json_duplicate(result, KS_TRUE); + reply->type = SWCLT_CMD_TYPE_RESULT; + } else if (result = ks_json_get_object_item(json, "error")) { /* Boo error */ - ctx->reply.error = error; - ctx->type = SWCLT_CMD_TYPE_ERROR; + reply->json = ks_json_duplicate(result, KS_TRUE); + reply->type = SWCLT_CMD_TYPE_ERROR; } else { /* uhh wut */ - status = ctx->failure_status = KS_STATUS_INVALID_ARGUMENT; - ks_log(KS_LOG_CRIT, "Failed to parse reply cmd"); - ctx->failure_reason = ks_pstrdup(ctx->base.pool, "The result did not contain an error or result key"); - } - - /* Command has completed, call the callback */ - if (ctx->cb) { - *async = KS_TRUE; - __raise_callback(ctx); + status = reply->failure_status = KS_STATUS_INVALID_ARGUMENT; + ks_log(KS_LOG_WARNING, "Failed to parse reply cmd"); + reply->failure_reason = ks_pstrdup(reply->pool, "The result did not contain an error or result key"); } done: if (status) { /* Got an invalid frame, set ourselves to failure with * the status built in */ - ctx->type = SWCLT_CMD_TYPE_FAILURE; - __raise_callback(ctx); + reply->type = SWCLT_CMD_TYPE_FAILURE; } + if (json) { + ks_json_delete(&json); + } + __raise_callback(cmd, &reply); - swclt_cmd_ctx_unlock(ctx); - - SWCLT_CMD_SCOPE_END_TAG(cmd, ctx, status, file, line, tag); + return KS_STATUS_SUCCESS; } + /* For Emacs: * Local Variables: * mode:c diff --git a/src/config.c b/src/config.c index e4afdec..2d2d0b3 100644 --- a/src/config.c +++ b/src/config.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 SignalWire, Inc + * Copyright (c) 2018-2019 SignalWire, Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -45,6 +45,8 @@ SWCLT_DECLARE(ks_status_t) swclt_config_destroy(swclt_config_t **config) ks_assert(config); ks_assert(*config); + if ((*config)->network) ks_json_delete(&(*config)->network); + pool = ks_pool_get(*config); ks_pool_close(&pool); @@ -53,26 +55,57 @@ SWCLT_DECLARE(ks_status_t) swclt_config_destroy(swclt_config_t **config) return ret; } +SWCLT_DECLARE(ks_status_t) swclt_config_set_default_network(swclt_config_t *config, ks_bool_t allData) +{ + if (config->network) ks_json_delete(&config->network); + config->network = ks_json_create_object(); + ks_json_add_bool_to_object(config->network, "route_data", allData); + ks_json_add_bool_to_object(config->network, "route_add", allData); + ks_json_add_bool_to_object(config->network, "route_remove", allData); + ks_json_add_bool_to_object(config->network, "authority_data", allData); + ks_json_add_bool_to_object(config->network, "authority_add", allData); + ks_json_add_bool_to_object(config->network, "authority_remove", allData); + ks_json_add_bool_to_object(config->network, "filtered_protocols", !allData); + ks_json_add_array_to_object(config->network, "protocols"); +} + SWCLT_DECLARE(ks_status_t) swclt_config_load_from_json(swclt_config_t *config, ks_json_t *json) { ks_status_t ret = KS_STATUS_SUCCESS; const char *val = NULL; + ks_json_t *network = NULL; - if ((val = ks_json_get_object_cstr_def(json, "private_key_path", NULL))) { + if ((val = ks_json_get_object_string(json, "private_key_path", NULL))) { swclt_config_set_private_key_path(config, val); } - if ((val = ks_json_get_object_cstr_def(json, "client_cert_path", NULL))) { + if ((val = ks_json_get_object_string(json, "client_cert_path", NULL))) { swclt_config_set_client_cert_path(config, val); } - if ((val = ks_json_get_object_cstr_def(json, "cert_chain_path", NULL))) { + if ((val = ks_json_get_object_string(json, "cert_chain_path", NULL))) { swclt_config_set_cert_chain_path(config, val); } - if ((val = ks_json_get_object_cstr_def(json, "authentication", NULL))) { + if ((val = ks_json_get_object_string(json, "authentication", NULL))) { swclt_config_set_authentication(config, val); } + + if ((val = ks_json_get_object_string(json, "agent", NULL))) { + swclt_config_set_agent(config, val); + } + + if ((val = ks_json_get_object_string(json, "identity", NULL))) { + swclt_config_set_identity(config, val); + } + + if ((network = ks_json_get_object_item(json, "network"))) { + if (config->network) ks_json_delete(&config->network); + config->network = ks_json_duplicate(network, KS_TRUE); + if (ks_json_get_object_item(config->network, "protocols") == NULL) ks_json_add_array_to_object(config->network, "protocols"); + } else { + swclt_config_set_default_network(config, KS_TRUE); + } return ret; } @@ -81,6 +114,8 @@ SWCLT_DECLARE(ks_status_t) swclt_config_load_from_env(swclt_config_t *config) { ks_status_t ret = KS_STATUS_SUCCESS; const char *val = NULL; + char protocolKey[256]; + int protocolCount = 0; if ((val = getenv("SW_PRIVATE_KEY_PATH"))) { swclt_config_set_private_key_path(config, val); @@ -98,6 +133,59 @@ SWCLT_DECLARE(ks_status_t) swclt_config_load_from_env(swclt_config_t *config) swclt_config_set_authentication(config, val); } + if ((val = getenv("SW_AGENT"))) { + swclt_config_set_agent(config, val); + } + + if ((val = getenv("SW_IDENTITY"))) { + swclt_config_set_identity(config, val); + } + + if ((val = getenv("SW_NETWORK_ROUTE_DATA"))) { + swclt_config_set_network_route_data(config, val[0] == '1' || val[0] == 't' || val[0] == 'T'); + } + + if ((val = getenv("SW_NETWORK_ROUTE_ADD"))) { + swclt_config_set_network_route_add(config, val[0] == '1' || val[0] == 't' || val[0] == 'T'); + } + + if ((val = getenv("SW_NETWORK_ROUTE_REMOVE"))) { + swclt_config_set_network_route_remove(config, val[0] == '1' || val[0] == 't' || val[0] == 'T'); + } + + if ((val = getenv("SW_NETWORK_AUTHORITY_DATA"))) { + swclt_config_set_network_authority_data(config, val[0] == '1' || val[0] == 't' || val[0] == 'T'); + } + + if ((val = getenv("SW_NETWORK_AUTHORITY_ADD"))) { + swclt_config_set_network_authority_add(config, val[0] == '1' || val[0] == 't' || val[0] == 'T'); + } + + if ((val = getenv("SW_NETWORK_AUTHORITY_REMOVE"))) { + swclt_config_set_network_authority_remove(config, val[0] == '1' || val[0] == 't' || val[0] == 'T'); + } + + if ((val = getenv("SW_NETWORK_FILTERED_PROTOCOLS"))) { + swclt_config_set_network_filtered_protocols(config, val[0] == '1' || val[0] == 't' || val[0] == 'T'); + } + + if (!config->network) { + swclt_config_set_default_network(config, KS_TRUE); + } else { + ks_json_t *protocols = NULL; + while (1) { + ks_snprintf(protocolKey, sizeof(protocolKey), "SW_NETWORK_PROTOCOL_%d", protocolCount); + ++protocolCount; + if (!(val = getenv(protocolKey)) || !val[0]) break; + if (!protocols) { + protocols = ks_json_get_object_item(config->network, "protocols"); + if (!protocols) protocols = ks_json_add_array_to_object(config->network, "protocols"); + } + ks_json_add_string_to_array(protocols, val); + } + } + + return ret; } @@ -170,3 +258,168 @@ SWCLT_DECLARE(ks_status_t) swclt_config_set_authentication(swclt_config_t *confi return KS_STATUS_SUCCESS; } +SWCLT_DECLARE(const char *) swclt_config_get_agent(swclt_config_t *config) +{ + ks_assert(config); + + return config->agent; +} + +SWCLT_DECLARE(ks_status_t) swclt_config_set_agent(swclt_config_t *config, const char *value) +{ + ks_assert(config); + + if (config->agent) ks_pool_free(&config->agent); + if (value) config->agent = ks_pstrdup(ks_pool_get(config), value); + + return KS_STATUS_SUCCESS; +} + +SWCLT_DECLARE(const char *) swclt_config_get_identity(swclt_config_t *config) +{ + ks_assert(config); + + return config->identity; +} + +SWCLT_DECLARE(ks_status_t) swclt_config_set_identity(swclt_config_t *config, const char *value) +{ + ks_assert(config); + + if (config->identity) ks_pool_free(&config->identity); + if (value) config->identity = ks_pstrdup(ks_pool_get(config), value); + + return KS_STATUS_SUCCESS; +} + +SWCLT_DECLARE(ks_bool_t) swclt_config_get_network_route_data(swclt_config_t *config) +{ + ks_assert(config); + + return ks_json_get_object_bool(config->network, "route_data", KS_FALSE); +} + +SWCLT_DECLARE(void) swclt_config_set_network_route_data(swclt_config_t *config, ks_bool_t value) +{ + ks_assert(config); + + ks_json_add_bool_to_object(config->network, "route_data", value); +} + +SWCLT_DECLARE(ks_bool_t) swclt_config_get_network_route_add(swclt_config_t *config) +{ + ks_assert(config); + + return ks_json_get_object_bool(config->network, "route_add", KS_FALSE); +} + +SWCLT_DECLARE(void) swclt_config_set_network_route_add(swclt_config_t *config, ks_bool_t value) +{ + ks_assert(config); + + ks_json_add_bool_to_object(config->network, "route_add", value); +} + +SWCLT_DECLARE(ks_bool_t) swclt_config_get_network_route_remove(swclt_config_t *config) +{ + ks_assert(config); + + return ks_json_get_object_bool(config->network, "route_remove", KS_FALSE); +} + +SWCLT_DECLARE(void) swclt_config_set_network_route_remove(swclt_config_t *config, ks_bool_t value) +{ + ks_assert(config); + + ks_json_add_bool_to_object(config->network, "route_remove", value); +} + +SWCLT_DECLARE(ks_bool_t) swclt_config_get_network_authority_data(swclt_config_t *config) +{ + ks_assert(config); + + return ks_json_get_object_bool(config->network, "authority_data", KS_FALSE); +} + +SWCLT_DECLARE(void) swclt_config_set_network_authority_data(swclt_config_t *config, ks_bool_t value) +{ + ks_assert(config); + + ks_json_add_bool_to_object(config->network, "authority_data", value); +} + +SWCLT_DECLARE(ks_bool_t) swclt_config_get_network_authority_add(swclt_config_t *config) +{ + ks_assert(config); + + return ks_json_get_object_bool(config->network, "authority_add", KS_FALSE); +} + +SWCLT_DECLARE(void) swclt_config_set_network_authority_add(swclt_config_t *config, ks_bool_t value) +{ + ks_assert(config); + + ks_json_add_bool_to_object(config->network, "authority_add", value); +} + +SWCLT_DECLARE(ks_bool_t) swclt_config_get_network_authority_remove(swclt_config_t *config) +{ + ks_assert(config); + + return ks_json_get_object_bool(config->network, "authority_remove", KS_FALSE); +} + +SWCLT_DECLARE(void) swclt_config_set_network_authority_remove(swclt_config_t *config, ks_bool_t value) +{ + ks_assert(config); + + ks_json_add_bool_to_object(config->network, "authority_remove", value); +} + +SWCLT_DECLARE(ks_bool_t) swclt_config_get_network_filtered_protocols(swclt_config_t *config) +{ + ks_assert(config); + + return ks_json_get_object_bool(config->network, "filtered_protocols", KS_FALSE); +} + +SWCLT_DECLARE(void) swclt_config_set_network_filtered_protocols(swclt_config_t *config, ks_bool_t value) +{ + ks_assert(config); + + ks_json_add_bool_to_object(config->network, "filtered_protocols", value); +} + +SWCLT_DECLARE(void) swclt_config_add_network_protocol(swclt_config_t *config, const char *value) +{ + ks_json_t *protocols = NULL; + ks_assert(config); + + if (!(protocols = ks_json_get_object_item(config->network, "protocols"))) { + protocols = ks_json_add_array_to_object(config->network, "protocols"); + } + + for (int index = 0; index < ks_json_get_array_size(protocols); ++index) { + if (!strcmp(ks_json_get_array_string(protocols, index, NULL), value)) return; + } + + ks_json_add_string_to_array(protocols, value); +} + +SWCLT_DECLARE(void) swclt_config_remove_network_protocol(swclt_config_t *config, const char *value) +{ + ks_json_t *protocols = NULL; + ks_assert(config); + + if (!(protocols = ks_json_get_object_item(config->network, "protocols"))) { + protocols = ks_json_add_array_to_object(config->network, "protocols"); + } + + for (int index = 0; index < ks_json_get_array_size(protocols); ++index) { + if (!strcmp(ks_json_get_array_string(protocols, index, NULL), value)) { + ks_json_delete_item_from_array(protocols, index); + return; + } + } +} + diff --git a/src/connection.c b/src/connection.c index 88c06cc..8065cc0 100644 --- a/src/connection.c +++ b/src/connection.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2019 SignalWire, Inc + * Copyright (c) 2018-2022 SignalWire, Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,540 +20,611 @@ * SOFTWARE. */ #include "signalwire-client-c/client.h" -#include "signalwire-client-c/internal/connection.h" #define ks_time_now_ms() ks_time_ms(ks_time_now()) -static ks_handle_t * __dupe_handle(swclt_conn_ctx_t *ctx, ks_handle_t handle) -{ - ks_handle_t *dup = ks_pool_alloc(ctx->base.pool, sizeof(handle)); - ks_assertd(dup); - memcpy(dup, &handle, sizeof(handle)); +static swclt_cmd_t *deregister_cmd(swclt_conn_t *ctx, ks_uuid_t id); - ks_log(KS_LOG_DEBUG, "Duplicated handle: %16.16llx", handle); - return dup; -} +/* 13107 commands per second over 5 second average TTL */ +#define TTL_HEAP_MAX_SIZE 65536 -static ks_status_t __register_cmd(swclt_conn_ctx_t *ctx, swclt_cmd_t cmd, ks_uuid_t *id, uint32_t *flags, uint32_t *ttl_ms) +typedef struct swclt_ttl_node { + ks_time_t expiry; + ks_uuid_t id; +} swclt_ttl_node_t; + +struct swclt_ttl_tracker { + int count; + ks_cond_t *cond; + ks_thread_t *thread; + swclt_conn_t *conn; + swclt_ttl_node_t heap[TTL_HEAP_MAX_SIZE]; // min heap of TTLs to expire +}; + +#define TTL_HEAP_ROOT 0 +#define TTL_HEAP_PARENT(pos) ((pos - 1) / 2) +#define TTL_HEAP_LEFT_CHILD(pos) ((pos * 2) + 1) +#define TTL_HEAP_RIGHT_CHILD(pos) ((pos * 2) + 2) + +inline static void ttl_heap_swap(swclt_ttl_tracker_t *ttl, int pos1, int pos2) { - ks_status_t status; + if (pos1 == pos2) { + return; + } + swclt_ttl_node_t tmp = ttl->heap[pos1]; + ttl->heap[pos1] = ttl->heap[pos2]; + ttl->heap[pos2] = tmp; +} - if (status = swclt_cmd_id(cmd, id)) - return status; - if (status = swclt_cmd_flags(cmd, flags)) - return status; - if (status = swclt_cmd_ttl(cmd, ttl_ms)) - return status; +static ks_status_t ttl_heap_remove(swclt_ttl_tracker_t *ttl) +{ + if (ttl->count <= 0) { + return KS_STATUS_FAIL; + } + // clear entry at root and swap with last entry + memset(&ttl->heap[TTL_HEAP_ROOT], 0, sizeof(ttl->heap[TTL_HEAP_ROOT])); + ttl_heap_swap(ttl, ttl->count - 1, TTL_HEAP_ROOT); + ttl->count--; + + // sift down the value... + int pos = TTL_HEAP_ROOT; + int swap; + while ((swap = TTL_HEAP_LEFT_CHILD(pos)) < ttl->count) { + int right = TTL_HEAP_RIGHT_CHILD(pos); + // if there is no left child or there is a right child and it is higher priority than left + if (!ttl->heap[swap].expiry || (ttl->heap[right].expiry && ttl->heap[right].expiry < ttl->heap[swap].expiry)) { + swap = right; + } + if (ttl->heap[swap].expiry && ttl->heap[pos].expiry > ttl->heap[swap].expiry) { + ttl_heap_swap(ttl, pos, swap); + pos = swap; + } else { + // done + break; + } + } + return KS_STATUS_SUCCESS; +} - /* Set this handle as a child of ours so we can free it if needed */ - ks_handle_set_parent(cmd, ctx->base.handle); +static ks_status_t ttl_heap_insert(swclt_ttl_tracker_t *ttl, ks_time_t expiry, ks_uuid_t id) +{ + if (ttl->count >= TTL_HEAP_MAX_SIZE) { + return KS_STATUS_FAIL; + } - ks_log(KS_LOG_DEBUG, "Inserting command handle: %16.16llx into hash for command key: %s", cmd, ks_uuid_thr_str(id)); + if (expiry == 0) { + return KS_STATUS_FAIL; + } - return ks_hash_insert(ctx->outstanding_requests, ks_uuid_dup(ctx->base.pool, id), __dupe_handle(ctx, cmd)); + // add to last position in the heap + int pos = ttl->count; + ttl->count++; + ttl->heap[pos].expiry = expiry; + ttl->heap[pos].id = id; + + // now sift up the value + while (pos > TTL_HEAP_ROOT) { + int parent = TTL_HEAP_PARENT(pos); + if (ttl->heap[parent].expiry > expiry) { + ttl_heap_swap(ttl, parent, pos); + pos = parent; + } else { + break; + } + } + return KS_STATUS_SUCCESS; } -static void __context_deinit( - swclt_conn_ctx_t *ctx) +static int ttl_tracker_size(swclt_ttl_tracker_t *ttl) { - ks_handle_destroy(&ctx->wss); - ks_hash_destroy(&ctx->outstanding_requests); + int count; + ks_cond_lock(ttl->cond); + count = ttl->count; + ks_cond_unlock(ttl->cond); + return count; } -static ks_status_t __wait_cmd_result(swclt_conn_ctx_t *ctx, swclt_cmd_t cmd, SWCLT_CMD_TYPE *type) +static ks_status_t ttl_tracker_watch(swclt_ttl_tracker_t *ttl, ks_time_t expiry, ks_uuid_t id) { - uint32_t ttl_ms, ttl_remaining_ms, duration_total_ms; - ks_time_t start_sec; - const char *method; - ks_status_t status; - - if (status = swclt_cmd_ttl(cmd, &ttl_ms)) - return status; - if (status = swclt_cmd_method(cmd, &method)) - return status; - ttl_remaining_ms = ttl_ms; - ks_assert(!(ttl_ms != 0 && ttl_ms < 1000)); - - /* Now wait for it to get completed */ - start_sec = ks_time_now_sec(); - ks_cond_lock(ctx->cmd_condition); - while (KS_TRUE) { - if (status = swclt_cmd_type(cmd, type)) - break; - - switch (*type) { - case SWCLT_CMD_TYPE_ERROR: - case SWCLT_CMD_TYPE_RESULT: - goto done; + ks_cond_lock(ttl->cond); + // need to wake thread if this TTL is before the next one + int wake_ttl_tracker_thread = !ttl->heap[TTL_HEAP_ROOT].expiry || ttl->heap[TTL_HEAP_ROOT].expiry > expiry; + if (ttl_heap_insert(ttl, expiry, id) != KS_STATUS_SUCCESS) { + ks_cond_unlock(ttl->cond); + ks_log(KS_LOG_ERROR, "Failed to track command %s TTL", ks_uuid_thr_str(&id)); + return KS_STATUS_FAIL; + } + if (wake_ttl_tracker_thread) { + // notify of new shortest TTL... + ks_cond_broadcast(ttl->cond); + } + ks_cond_unlock(ttl->cond); + return KS_STATUS_SUCCESS; +} - case SWCLT_CMD_TYPE_REQUEST: - break; +static ks_status_t ttl_tracker_next(swclt_ttl_tracker_t *ttl, ks_uuid_t *id) +{ + ks_cond_lock(ttl->cond); + ks_time_t wait_ms = 0; + ks_time_t now_ms = ks_time_now_ms(); + + // how long to wait for next TTL expiration? + if (!ttl->heap[TTL_HEAP_ROOT].expiry) { + // nothing to wait for + wait_ms = 5000; + } else if (ttl->heap[TTL_HEAP_ROOT].expiry > now_ms) { + wait_ms = ttl->heap[TTL_HEAP_ROOT].expiry - now_ms; + ks_log(KS_LOG_DEBUG, "Waiting %d for TTL expiration of %s", (uint32_t)wait_ms, ks_uuid_thr_str(&ttl->heap[TTL_HEAP_ROOT].id)); + } - case SWCLT_CMD_TYPE_FAILURE: - ks_log(KS_LOG_WARNING, "Command failure", status); - goto done; - default: - ks_abort_fmt("Invalid command type: %lu", *type); - } + // wait for TTL, up to 5 seconds + if (wait_ms > 5000) { + wait_ms = 5000; + } + if (wait_ms) { + ks_cond_timedwait(ttl->cond, wait_ms); + now_ms = ks_time_now_ms(); + } - if (ttl_ms) { - if (status = ks_cond_timedwait(ctx->cmd_condition, ttl_remaining_ms)) { - if (status != KS_STATUS_TIMEOUT) { - ks_log(KS_LOG_WARNING, "Condition wait failed: %lu", status); - break; - } - } + // check for TTL expiration + if (ttl->heap[TTL_HEAP_ROOT].expiry && ttl->heap[TTL_HEAP_ROOT].expiry <= now_ms) { + // TTL expired + *id = ttl->heap[TTL_HEAP_ROOT].id; + ttl_heap_remove(ttl); + ks_cond_unlock(ttl->cond); + return KS_STATUS_SUCCESS; + } - /* Update remaining and figure out if we've timed out */ - duration_total_ms = (uint32_t)((ks_time_now_sec() - start_sec) * 1000); - if (duration_total_ms > ttl_remaining_ms) { - swclt_cmd_report_failure_fmt_m(cmd, KS_STATUS_TIMEOUT, "TTL expired for command: %s (ttl_ms: %lu)", method, ttl_ms); + // Nothing expired + ks_cond_unlock(ttl->cond); + return KS_STATUS_TIMEOUT; +} - /* Return success, error is really within the cmd */ - status = KS_STATUS_SUCCESS; - break; +static void *ttl_tracker_thread(ks_thread_t *thread, void *data) +{ + swclt_ttl_tracker_t *ttl = (swclt_ttl_tracker_t *)data; + ks_log(KS_LOG_INFO, "TTL tracker thread running"); + while (ks_thread_stop_requested(thread) == KS_FALSE) { + ks_uuid_t id = { 0 }; + if (ttl_tracker_next(ttl, &id) == KS_STATUS_SUCCESS) { + swclt_cmd_t *cmd; + if ((cmd = deregister_cmd(ttl->conn, id))) { + swclt_cmd_report_failure_fmt(cmd, KS_STATUS_TIMEOUT, "TTL expired for command %s", ks_uuid_thr_str(&id)); + ks_log(KS_LOG_INFO, "TTL expired for command %s", ks_uuid_thr_str(&id)); + swclt_cmd_destroy(&cmd); } - - ttl_remaining_ms -= duration_total_ms; } - else - ks_cond_wait(ctx->cmd_condition); } - -done: - ks_cond_unlock(ctx->cmd_condition); - - return status; + ks_log(KS_LOG_INFO, "TTL tracker thread finished"); + return NULL; } -static void __deregister_cmd(swclt_conn_ctx_t *ctx, swclt_cmd_t cmd, ks_uuid_t id) +static void ttl_tracker_destroy(swclt_ttl_tracker_t **ttl) { - ks_hash_remove(ctx->outstanding_requests, &id); + if (ttl && *ttl) { + ks_log(KS_LOG_INFO, "Destroying TTL tracker"); + if ((*ttl)->thread && ks_thread_request_stop((*ttl)->thread) != KS_STATUS_SUCCESS) { + *ttl = NULL; + ks_log(KS_LOG_ERROR, "Failed to stop TTL thread. Leaking TTL data and moving on."); + return; + } + ks_cond_lock((*ttl)->cond); + ks_cond_broadcast((*ttl)->cond); + ks_cond_unlock((*ttl)->cond); + if ((*ttl)->thread) { + ks_thread_join((*ttl)->thread); + ks_thread_destroy(&(*ttl)->thread); + } + ks_cond_destroy(&(*ttl)->cond); + ks_pool_free(ttl); + } } -static ks_status_t __wait_outstanding_cmd_result(swclt_conn_ctx_t *ctx, swclt_cmd_t cmd, SWCLT_CMD_TYPE *type) +static ks_status_t ttl_tracker_create(ks_pool_t *pool, swclt_ttl_tracker_t **ttl, swclt_conn_t *ctx) { - ks_uuid_t id; ks_status_t status; - - if (status = swclt_cmd_id(cmd, &id)) - return status; - - ks_hash_read_lock(ctx->outstanding_requests); - if (!ks_hash_search(ctx->outstanding_requests, &id, KS_UNLOCKED)) { - ks_log(KS_LOG_WARNING, "Failed to lookup command: %16.16llx", cmd); - ks_hash_read_unlock(ctx->outstanding_requests); - return KS_STATUS_FAIL; + *ttl = ks_pool_alloc(pool, sizeof(swclt_ttl_tracker_t)); + ks_cond_create(&(*ttl)->cond, pool); + (*ttl)->conn = ctx; + if (status = ks_thread_create_tag(&(*ttl)->thread, ttl_tracker_thread, *ttl, NULL, "swclt-ttl-tracker")) { + ks_log(KS_LOG_CRIT, "Failed to allocate connection TTL thread: %lu", status); } - ks_hash_read_unlock(ctx->outstanding_requests); - - status = __wait_cmd_result(ctx, cmd, type); - - /* Great, remove it */ - __deregister_cmd(ctx, cmd, id); - return status; } -static ks_status_t __submit_result(swclt_conn_ctx_t *ctx, swclt_cmd_t cmd) +static void report_connection_failure(swclt_conn_t *conn) { - SWCLT_CMD_TYPE type; - ks_status_t status; + ks_mutex_lock(conn->failed_mutex); + if (!conn->failed) { + conn->failed = 1; + ks_mutex_unlock(conn->failed_mutex); + ks_log(KS_LOG_WARNING, "Reporting connection state failure"); + if (conn->failed_cb) { + conn->failed_cb(conn, conn->failed_cb_data); + } + } else { + ks_mutex_unlock(conn->failed_mutex); + } +} - if (status = swclt_hstate_check_ctx(&ctx->base, "Submit result denied due to state")) - return status; +static ks_status_t register_cmd(swclt_conn_t *ctx, swclt_cmd_t **cmdP) +{ + ks_status_t status = KS_STATUS_FAIL; + swclt_cmd_t *cmd = *cmdP; + + ks_log(KS_LOG_DEBUG, "Tracking command with id: %s and TTL: %d", ks_uuid_thr_str(&cmd->id), cmd->response_ttl_ms); - if (status = swclt_cmd_type(cmd, &type)) + if (!ctx->ttl || (status = ttl_tracker_watch(ctx->ttl, ks_time_now_ms() + (ks_time_t)cmd->response_ttl_ms, cmd->id))) { + ks_log(KS_LOG_ERROR, "Failed to track TTL for command with id: %s and TTL: %d", ks_uuid_thr_str(&cmd->id), cmd->response_ttl_ms); + report_connection_failure(ctx); return status; + } + ks_hash_insert(ctx->outstanding_requests, ks_uuid_dup(ctx->pool, &cmd->id), cmd); + *cmdP = NULL; - ks_assert(type == SWCLT_CMD_TYPE_RESULT || type == SWCLT_CMD_TYPE_ERROR); + return KS_STATUS_SUCCESS; +} - return swclt_wss_write_cmd(ctx->wss, cmd); +static swclt_cmd_t *deregister_cmd(swclt_conn_t *conn, ks_uuid_t id) +{ + return ks_hash_remove(conn->outstanding_requests, &id); } -static ks_status_t __submit_request(swclt_conn_ctx_t *ctx, swclt_cmd_t cmd) +SWCLT_DECLARE(ks_status_t) swclt_conn_cancel_request(swclt_conn_t *conn, swclt_cmd_future_t **future) { - uint32_t flags; - ks_uuid_t id; - SWCLT_CMD_TYPE type; - uint32_t ttl_ms; - ks_status_t status; + if (conn && future && *future) { + swclt_cmd_t *cmd = deregister_cmd(conn, swclt_cmd_future_get_id(*future)); + if (cmd) { + swclt_cmd_report_failure(cmd, KS_STATUS_TIMEOUT, "Canceled request"); + char *cmd_str = swclt_cmd_describe(cmd); + ks_log(KS_LOG_WARNING, "Canceled request and destroying command: %s", cmd_str); + ks_pool_free(&cmd_str); + swclt_cmd_destroy(&cmd); + swclt_cmd_future_destroy(future); + } + *future = NULL; + } + return KS_STATUS_SUCCESS; +} - /* Check state */ - if (status = swclt_hstate_check_ctx(&ctx->base, "Submit request denied due to state")) - return status; +static ks_status_t submit_result(swclt_conn_t *ctx, swclt_cmd_t *cmd) +{ + ks_status_t status; - ks_log(KS_LOG_DEBUG, "Submitting request: %s", ks_handle_describe(cmd)); + if (ctx->failed || !ctx->wss) { + return KS_STATUS_DISCONNECTED; + } - /* Register this cmd in our outstanding requests */ - if (status = __register_cmd(ctx, cmd, &id, &flags, &ttl_ms)) { - ks_log(KS_LOG_WARNING, "Failed to register cmd: %lu", status); - return status; + if (cmd->type != SWCLT_CMD_TYPE_RESULT && cmd->type != SWCLT_CMD_TYPE_ERROR) { + char *cmd_str = swclt_cmd_describe(cmd); + ks_log(KS_LOG_ERROR, "Invalid command type to send as result: %s", cmd_str); + ks_pool_free(&cmd_str); + return KS_STATUS_FAIL; } - /* And write it on the socket */ - if (status = swclt_wss_write_cmd(ctx->wss, cmd)) { - ks_log(KS_LOG_WARNING, "Failed to write to websocket: %lu", status); - return status; + /* convert command to JSON string */ + char *data = NULL; + if (status = swclt_cmd_print(cmd, cmd->pool, &data)) { + ks_log(KS_LOG_CRIT, "Invalid command, failed to render payload string: %lu", status); + return KS_STATUS_FAIL; } - /* Now mark this command as finally submitted, this will atomically now - * allow this command to be servied by the session thread */ - if (status = swclt_cmd_set_submit_time(cmd, ks_time_now())) { - ks_log(KS_LOG_CRIT, "Failed to update commands submit time: %lu", status); - return status; + /* Write the command data on the socket */ + if ((status = swclt_wss_write(ctx->wss, data))) { + ks_log(KS_LOG_WARNING, "Failed to write to websocket: %lu, %s", status, data); + status = KS_STATUS_DISCONNECTED; } + ks_pool_free(&data); - ks_log(KS_LOG_DEBUG, "Requesting service for command ttl of: %lums", ttl_ms); + return status; +} - /* Now ask to be serviced by its ttl time */ - swclt_hmgr_request_service_in(&ctx->base, ttl_ms); +static ks_status_t submit_request(swclt_conn_t *ctx, swclt_cmd_t **cmdP, swclt_cmd_future_t **cmd_future) +{ + ks_status_t status = KS_STATUS_SUCCESS; + uint32_t flags = 0; + swclt_cmd_t *cmd = *cmdP; + *cmdP = NULL; - /* If the command has a reply, wait for it (it will get - * de-registered by __wait_outstanding_cmd_result) */ - if (!(flags & SWCLT_CMD_FLAG_NOREPLY)) { - swclt_cmd_cb_t cb; - void *cb_data; + /* Check state of connection and websocket */ + if (ctx->failed || !ctx->wss) { + swclt_cmd_destroy(&cmd); + return KS_STATUS_FAIL; + } - /* Don't wait if a callback is set */ - if (status = swclt_cmd_cb(cmd, &cb, &cb_data)) - return status; + char *cmd_str = swclt_cmd_describe(cmd); + ks_log(KS_LOG_DEBUG, "Submitting request: %s", cmd_str); + ks_pool_free(&cmd_str); - if (!cb) { - if (status = __wait_outstanding_cmd_result(ctx, cmd, &type)) { - ks_log(KS_LOG_WARNING, "Failed to wait for cmd: %lu", status); + /* convert command to JSON string */ + char *data = NULL; + if (status = swclt_cmd_print(cmd, ctx->pool, &data)) { + ks_log(KS_LOG_CRIT, "Invalid command, failed to render payload string: %lu", status); + swclt_cmd_destroy(&cmd); + return KS_STATUS_FAIL; + } + + /* Register this cmd in our outstanding requests if there is a reply */ + if (!(cmd->flags & SWCLT_CMD_FLAG_NOREPLY)) { + if (!cmd->cb && cmd_future) { + /* set up our own callbacks to wait if none exist */ + if (status = swclt_cmd_future_create(cmd_future, cmd)) { + ks_log(KS_LOG_CRIT, "Failed to create command cmd_future"); + swclt_cmd_destroy(&cmd); + ks_pool_free(&data); + return KS_STATUS_FAIL; } } - return status; + + if ((cmd->cb || cmd_future) && (status = register_cmd(ctx, &cmd))) { + ks_log(KS_LOG_WARNING, "Failed to register cmd: %lu", status); + swclt_cmd_future_destroy(cmd_future); + swclt_cmd_destroy(&cmd); + ks_pool_free(&data); + return status; + } + } + + /* Write the command data on the socket */ + if ((status = swclt_wss_write(ctx->wss, data))) { + ks_log(KS_LOG_WARNING, "Failed to write to websocket: %lu, %s", status, data); } + ks_pool_free(&data); + + /* destroy command if we didn't register it (it is not NULL) */ + swclt_cmd_destroy(&cmd); - /* No reply so de-register it */ - __deregister_cmd(ctx, cmd, id); return status; } -static ks_status_t __on_incoming_request(swclt_conn_ctx_t *ctx, ks_json_t *payload, swclt_frame_t frame) +static ks_status_t on_incoming_request(swclt_conn_t *ctx, ks_json_t *payload, swclt_frame_t **frame) { const char *method; ks_uuid_t id; - swclt_cmd_t cmd = KS_NULL_HANDLE; + swclt_cmd_t *cmd = NULL; ks_status_t status; /* Check state */ - if (status = swclt_hstate_check_ctx(&ctx->base, "Ignoring incoming request due to state:")) - return status; + if (ctx->failed || !ctx->wss) { + return KS_STATUS_FAIL; + } - ks_log(KS_LOG_DEBUG, "Handling incoming request: %s", ks_handle_describe(frame)); + ks_log(KS_LOG_DEBUG, "Handling incoming request: %s", (*frame)->data); - if (!(method = ks_json_get_object_cstr_def(payload, "method", NULL))) { - ks_log(KS_LOG_WARNING, "Invalid response received: %s", ks_handle_describe(frame)); + if (!(method = ks_json_get_object_string(payload, "method", NULL))) { + ks_log(KS_LOG_WARNING, "Invalid response received: %s", (*frame)->data); return KS_STATUS_INVALID_ARGUMENT; } - id = ks_json_get_object_uuid(payload, "id"); + id = ks_uuid_from_str(ks_json_get_object_string(payload, "id", "")); if (ks_uuid_is_null(&id)) { - ks_log(KS_LOG_WARNING, "Response missing id: %s", ks_handle_describe(frame)); + ks_log(KS_LOG_WARNING, "Response missing id: %s", (*frame)->data); return KS_STATUS_INVALID_ARGUMENT; } + /* Create the command */ if (status = swclt_cmd_create_frame( &cmd, NULL, NULL, - frame, + *frame, 0, BLADE_METHOD_FLAGS(method))) { - ks_log(KS_LOG_WARNING, "Failed to create command (status: %lu) from frame: %s", status, ks_handle_describe(frame)); + ks_log(KS_LOG_WARNING, "Failed to create command (status: %lu) from frame: %s", status, (*frame)->data); return status; } ks_log(KS_LOG_DEBUG, "Dispatching incoming request method: %s id: %s", method, ks_uuid_thr_str(&id)); - /* Bind this cmd to our connection in case the callback does not free it */ - ks_handle_set_parent(cmd, ctx->base.handle); + /* And we're in charge of the frame now, we copied it, so free it */ + ks_pool_free(frame); + + /* Send command to client */ + ctx->incoming_cmd_cb(ctx, cmd, ctx->incoming_cmd_cb_data); - /* And we're in charge of the frame now, we copied it, so invalidae it */ - ks_handle_destroy(&frame); + /* Free the command */ + swclt_cmd_destroy(&cmd); - /* And raise the client */ - return ctx->incoming_cmd_cb(ctx->base.handle, cmd, ctx->incoming_cmd_cb_data); + return KS_STATUS_SUCCESS; } -static ks_status_t __on_incoming_frame(swclt_wss_t wss, swclt_frame_t frame, swclt_conn_ctx_t *ctx) +typedef struct incoming_frame_job { + swclt_conn_t *ctx; + swclt_frame_t *frame; +} incoming_frame_job_t; + +static void *on_incoming_frame_job(ks_thread_t *thread, void *data) { - ks_json_t *payload; - ks_status_t status; - const char *method; + incoming_frame_job_t *job = (incoming_frame_job_t *)data; + ks_json_t *payload = NULL; + ks_status_t status = KS_STATUS_SUCCESS; ks_uuid_t id; - swclt_cmd_t *outstanding_cmd = NULL; - swclt_cmd_t cmd; - ks_bool_t async = KS_FALSE; + swclt_cmd_t *cmd = NULL; + swclt_frame_t *frame = job->frame; + swclt_conn_t *ctx = job->ctx; - ks_log(KS_LOG_DEBUG, "Handling incoming frame: %s", ks_handle_describe(frame)); + free(job); - /* Lock to synchronize with the waiter thread */ - ks_cond_lock(ctx->cmd_condition); + ks_log(KS_LOG_DEBUG, "Handling incoming frame: %s", frame->data); /* Parse the json out of the frame to figure out what it is */ - if (status = swclt_frame_get_json(frame, &payload)) { + if (swclt_frame_to_json(frame, &payload) != KS_STATUS_SUCCESS) { ks_log(KS_LOG_ERROR, "Failed to get frame json: %lu", status); goto done; } - /* If its a request, we need to raise this directly with the callback */ + /* If it's a request, we need to raise this directly with the callback */ if (ks_json_get_object_item(payload, "params")) { - /* Ok we don't need this lock anymore sinc we're not a result */ - ks_cond_unlock(ctx->cmd_condition); - return __on_incoming_request(ctx, payload, frame); + on_incoming_request(ctx, payload, &frame); + goto done; } /* Must be a reply, look up our outstanding request */ - id = ks_json_get_object_uuid(payload, "id"); + id = ks_uuid_from_str(ks_json_get_object_string(payload, "id", "")); if (ks_uuid_is_null(&id)) { - ks_log(KS_LOG_WARNING, "Received invalid payload, missing id: %s", ks_handle_describe(frame)); - status = KS_STATUS_INVALID_ARGUMENT; - goto done; - } - - ks_hash_read_lock(ctx->outstanding_requests); - if (!(outstanding_cmd = ks_hash_search(ctx->outstanding_requests, &id, KS_UNLOCKED))) { - - /* Command probably timed out */ - ks_log(KS_LOG_DEBUG, "Could not locate cmd for frame: %s", ks_handle_describe(frame)); - - /* Unexpected, break in case it happens in a debugger */ - status = KS_STATUS_INVALID_ARGUMENT; - ks_hash_read_unlock(ctx->outstanding_requests); + ks_log(KS_LOG_WARNING, "Received invalid payload, missing id: %s", frame->data); goto done; } - ks_hash_read_unlock(ctx->outstanding_requests); - - /* Copy the handle out before we remove the memory storing it in the hash */ - cmd = *outstanding_cmd; - - /* Remove the command from outstanding requests */ - __deregister_cmd(ctx, *outstanding_cmd, id); - - /* Right away clear this commands ttl to prevent a timeout during dispatch */ - if (status = swclt_cmd_set_submit_time(cmd, 0)) { - ks_log(KS_LOG_ERROR, "Failed to set ttl to 0 on command while processing result: %s", ks_handle_describe(cmd)); - goto done; - } - - ks_log(KS_LOG_DEBUG, "Fetched cmd handle: %8.8llx", cmd); - if (status = swclt_cmd_method(cmd, &method)) { - ks_log(KS_LOG_ERROR, "Failed to get command method: %lu", status); + if (!(cmd = deregister_cmd(ctx, id))) { + /* Command probably timed out or we don't care about the reply, or a node sent bad data - + this is completely fine and should not cause us to tear down the connection. + */ + ks_log(KS_LOG_DEBUG, "Could not locate cmd for frame: %s", frame->data); goto done; } /* Great, feed it the reply */ - if (status = swclt_cmd_parse_reply_frame(cmd, frame, &async)) { + if (status = swclt_cmd_parse_reply_frame(cmd, frame)) { ks_log(KS_LOG_ERROR, "Failed to parse command reply: %lu", status); goto done; } - ks_log(KS_LOG_DEBUG, "Successfully read command result: %s", ks_handle_describe(cmd)); +done: - /* Now raise the signal */ - ks_cond_broadcast(ctx->cmd_condition); + ks_pool_free(&frame); -done: - ks_cond_unlock(ctx->cmd_condition); + if (payload) { + ks_json_delete(&payload); + } - ks_handle_destroy(&frame); + if (cmd) { + char *cmd_str = swclt_cmd_describe(cmd); + ks_log(KS_LOG_DEBUG, "Destroying command: %s", cmd_str); + ks_pool_free(&cmd_str); + swclt_cmd_destroy(&cmd); + } - if (async) { - ks_log(KS_LOG_DEBUG, "Destroying command: %s", ks_handle_describe(cmd)); - ks_handle_destroy(&cmd); + return NULL; +} + +static void check_connection_stats(swclt_wss_t *wss, swclt_conn_t *ctx) +{ + ks_time_t now = ks_time_now() / 1000; + if (now > ctx->last_stats_update + 10000) { + swclt_wss_stats_t wss_stats = { 0 }; + swclt_wss_get_stats(wss, &wss_stats); + if (ctx->last_stats_update > 0) { + int64_t read_frames_delta = wss_stats.read_frames - ctx->last_stats.read_frames; + int64_t write_frames_delta = wss_stats.write_frames - ctx->last_stats.write_frames; + double elapsed_sec = (double)(now - ctx->last_stats_update) / 1000.0; + double read_frames_per_sec = elapsed_sec > 0.0 ? (double)read_frames_delta / elapsed_sec : 0.0; + double write_frames_per_sec = elapsed_sec > 0.0 ? (double)write_frames_delta / elapsed_sec : 0.0; + ks_log(KS_LOG_INFO, + "LOG_FIELDS[" + "@#sw_conn_read_frames=%ld," + "@#sw_conn_read_frames_per_sec=%f," + "@#sw_conn_write_frames=%ld," + "@#sw_conn_write_frames_per_sec=%f," + "@#sw_conn_ttl_heap_size=%d," + "@#sw_conn_read_frames_queued=%d] SignalWire Client Connection Stats", + wss_stats.read_frames, + read_frames_per_sec, + wss_stats.write_frames, + write_frames_per_sec, + ttl_tracker_size(ctx->ttl), + (int)ks_thread_pool_backlog(ctx->incoming_frame_pool)); + } + ctx->last_stats = wss_stats; + ctx->last_stats_update = now; } +} - return status; +static ks_status_t on_incoming_frame(swclt_wss_t *wss, swclt_frame_t **frame, swclt_conn_t *ctx) +{ + if (frame && *frame) { + incoming_frame_job_t *job = malloc(sizeof(incoming_frame_job_t)); + job->ctx = ctx; + job->frame = *frame; + *frame = NULL; // take ownership of frame + ks_thread_pool_add_job(ctx->incoming_frame_pool, on_incoming_frame_job, job); + check_connection_stats(wss, ctx); + return KS_STATUS_SUCCESS; + } + return KS_STATUS_FAIL; } -static void __context_describe(swclt_conn_ctx_t *ctx, char *buffer, ks_size_t buffer_len) +SWCLT_DECLARE(char *) swclt_conn_describe(swclt_conn_t *ctx) { - /* We have to do all this garbage because of the poor decision to nest ks_handle_describe() calls that return a common thread local buffer */ - const char *desc = ks_handle_describe(ctx->wss); - ks_size_t desc_len = strlen(desc); - char preamble_buf[256] = { 0 }; - ks_size_t preamble_len = 0; - snprintf(preamble_buf, sizeof(preamble_buf), "SWCLT Connection to %s:%d - ", ctx->info.wss.address, ctx->info.wss.port); - preamble_len = strlen(preamble_buf); - if (desc_len + preamble_len + 1 > buffer_len) { - desc_len = buffer_len - preamble_len - 1; - } - memmove(buffer + preamble_len, desc, desc_len + 1); - memcpy(buffer, preamble_buf, preamble_len); - buffer[buffer_len - 1] = '\0'; + if (ctx) { + return ks_psprintf(ctx->pool, "SWCLT Connection to %s:%d - ", ctx->info.wss.address, ctx->info.wss.port); + } + return NULL; } -static ks_status_t __do_logical_connect(swclt_conn_ctx_t *ctx, ks_uuid_t previous_sessionid, ks_json_t **authentication) +static ks_status_t do_logical_connect(swclt_conn_t *ctx, + ks_uuid_t previous_sessionid, + ks_json_t **authentication, + const char *agent, + const char *identity, + ks_json_t *network) { - swclt_cmd_t cmd = CREATE_BLADE_CONNECT_CMD(previous_sessionid, authentication); - SWCLT_CMD_TYPE cmd_type; - ks_json_t *error = NULL; + swclt_cmd_t *cmd = CREATE_BLADE_CONNECT_CMD(ctx->pool, previous_sessionid, authentication, agent, identity, network); ks_status_t status = KS_STATUS_SUCCESS; + ks_json_t *error = NULL; + swclt_cmd_future_t *future = NULL; + swclt_cmd_reply_t *reply = NULL; if (!cmd) { status = KS_STATUS_NO_MEM; goto done; } - if (status = __submit_request(ctx, cmd)) + if (status = submit_request(ctx, &cmd, &future)) goto done; - if (status = swclt_cmd_type(cmd, &cmd_type)) { - ks_log(KS_LOG_ERROR, "Unable to get command type"); + if (!future) { + ks_log(KS_LOG_CRIT, "No blade.connect future received"); goto done; } - if (cmd_type == SWCLT_CMD_TYPE_ERROR) { - if (status = swclt_cmd_error(cmd, (const ks_json_t **)&error)) { - ks_log(KS_LOG_ERROR, "Unable to get command error"); + status = swclt_cmd_future_get(future, &reply); + if (status != KS_STATUS_SUCCESS) { + swclt_conn_cancel_request(ctx, &future); + } + swclt_cmd_future_destroy(&future); + if (swclt_cmd_reply_ok(reply) != KS_STATUS_SUCCESS) { + ks_log(KS_LOG_ERROR, "blade.connect failed"); + if (reply && reply->type == SWCLT_CMD_TYPE_ERROR) { + error = reply->json; } goto done; } - - if (status = swclt_cmd_lookup_parse_s(cmd, ctx->base.pool, - (swclt_cmd_parse_cb_t)BLADE_CONNECT_RPL_PARSE, (void **)&ctx->blade_connect_rpl)) { + + if (ctx->blade_connect_rpl) { + BLADE_CONNECT_RPL_DESTROY(&ctx->blade_connect_rpl); + } + if (status = swclt_cmd_reply_parse(reply, ctx->pool, + (swclt_cmd_parse_cb_t)BLADE_CONNECT_RPL_PARSE, (void **)&ctx->blade_connect_rpl)) { ks_log(KS_LOG_ERROR, "Unable to parse connect reply"); goto done; } /* Great snapshot our info types */ ctx->info.sessionid = ctx->blade_connect_rpl->sessionid; - ctx->info.nodeid = ks_pstrdup(ctx->base.pool, ctx->blade_connect_rpl->nodeid); + ctx->info.nodeid = ks_pstrdup(ctx->pool, ctx->blade_connect_rpl->nodeid); ctx->info.master_nodeid = ctx->blade_connect_rpl->master_nodeid; done: /* If the caller wants a call back for connect do that too */ if (ctx->connect_cb) { - if (status = ctx->connect_cb(ctx->base.handle, error, ctx->blade_connect_rpl, ctx->connect_cb_data)) { + if (status = ctx->connect_cb(ctx, error, ctx->blade_connect_rpl, ctx->connect_cb_data)) { ks_log(KS_LOG_WARNING, "Connect callback returned error: %lu", status); } } - ks_handle_destroy(&cmd); + swclt_cmd_destroy(&cmd); + swclt_cmd_reply_destroy(&reply); return status; } -static void __context_service(swclt_conn_ctx_t *ctx) +static void on_wss_failed(swclt_wss_t *wss, void *data) { - /* Iterate our outstanding commands, and time out any that haven't received - * a response by their ttl amount */ - ks_hash_iterator_t *itt; - - ks_log(KS_LOG_DEBUG, "Checking outstanding commands for ttl timeout: %s", ks_handle_describe_ctx(ctx)); - - ks_hash_write_lock(ctx->outstanding_requests); - for (itt = ks_hash_first(ctx->outstanding_requests, KS_UNLOCKED); itt; ) { - swclt_cmd_t *_cmd; - swclt_cmd_t cmd; - ks_uuid_t *id_key; - uint32_t ttl_ms; - ks_time_t cmd_submit_time = 0; - ks_bool_t remove = KS_FALSE; - SWCLT_CMD_TYPE type; - ks_time_t total_time_waited_ms = 0; - - ks_hash_this(itt, (const void **)&id_key, NULL, (void **)&_cmd); - - /* Copy it and let the hash delete it later */ - cmd = *_cmd; - - ks_log(KS_LOG_DEBUG, "Checking command: %s for ttl expiration", ks_handle_describe(cmd)); - - /* Now check its time */ - if (swclt_cmd_ttl(cmd, &ttl_ms) || swclt_cmd_submit_time(cmd, &cmd_submit_time) || swclt_cmd_type(cmd, &type)) { - ks_log(KS_LOG_WARNING, "Removing invalid cmd with id: %s from outstanding requests with handle value: %16.16llx", ks_uuid_thr_str(id_key), cmd); - remove = KS_TRUE; - } - - /* The command submit time is is left zero until the command actually is copletely written over the websocket - * so don't time these out */ - if (cmd_submit_time == 0) - goto next; - - /* Check for time travelers */ - if (ks_time_now() < cmd_submit_time) { - ks_debug_break(); - ks_log(KS_LOG_WARNING, "Invalid time detected in command, removing: %s", ks_handle_describe(cmd)); - remove = KS_TRUE; - } - - /* Leave commands with inifnite timeouts alone */ - if (ttl_ms == 0) - goto next; - - if (!remove && type == SWCLT_CMD_TYPE_REQUEST) { - total_time_waited_ms = ks_time_ms(ks_time_now() - cmd_submit_time); - - if (total_time_waited_ms > ttl_ms) { - ks_log(KS_LOG_WARNING, "Removing timed out cmd %s (current wait time is: %lums, ttl was: %lums)", ks_handle_describe(cmd), total_time_waited_ms, ttl_ms); - remove = KS_TRUE; - } else { - ks_log(KS_LOG_DEBUG, "Not removing cmd: %s, ttl has not crossed wait time of: %lums (current wait time is: %lums)", ks_handle_describe(cmd), ttl_ms, total_time_waited_ms); - - /* Enqueue a manager service for the remaining amount */ - swclt_hmgr_request_service_in(&ctx->base, ttl_ms - total_time_waited_ms); - } - } - - next: - - itt = ks_hash_next(&itt); - - if (remove) { - ks_log(KS_LOG_DEBUG, "Removing command registered for id: %s", ks_uuid_thr_str(id_key)); - - ks_hash_remove(ctx->outstanding_requests, id_key); - - /* Flag it failed with timeout */ - swclt_cmd_report_failure_fmt_m(cmd, KS_STATUS_TIMEOUT, "Command waited: %lums (ttl: %lums)", total_time_waited_ms, ttl_ms); - } - } - ks_hash_write_unlock(ctx->outstanding_requests); -} - -static void __on_wss_state_change(swclt_conn_ctx_t *ctx, swclt_hstate_change_t *state_change) -{ - /* When websocket dies, all commands get marked as failed and removed from the outstanding commands hash */ - if (state_change->new_state == SWCLT_HSTATE_DEGRADED) { - - /* Enqueue a state change on ourselves as well */ - swclt_hstate_changed(&ctx->base, SWCLT_HSTATE_DEGRADED, - KS_STATUS_FAIL, "Websocket failed"); - - ks_log(KS_LOG_WARNING, "Websocket got disconnected and has initiated a state change: %s", swclt_hstate_describe_change(state_change)); - ks_log(KS_LOG_WARNING, "Expiring all outstanding commands"); - - ks_hash_iterator_t *itt; - - ks_hash_write_lock(ctx->outstanding_requests); - for (itt = ks_hash_first(ctx->outstanding_requests, KS_UNLOCKED); itt; ) { - swclt_cmd_t *_cmd; - swclt_cmd_t cmd; - ks_uuid_t *id_key; - - ks_hash_this(itt, (const void **)&id_key, NULL, (void **)&_cmd); - - /* Copy it and let the hash delete it later */ - cmd = *_cmd; - - itt = ks_hash_next(&itt); - - ks_hash_remove(ctx->outstanding_requests, id_key); - - /* Flag it failed with timeout */ - swclt_cmd_report_failure_fmt_s(cmd, state_change->status, - swclt_hstate_describe_change(state_change)); - } - ks_hash_write_unlock(ctx->outstanding_requests); - } + swclt_conn_t *conn = (swclt_conn_t *)data; + report_connection_failure(conn); } -static ks_status_t __connect_wss(swclt_conn_ctx_t *ctx, ks_uuid_t previous_sessionid, ks_json_t **authentication) +static ks_status_t connect_wss(swclt_conn_t *ctx, ks_uuid_t previous_sessionid, ks_json_t **authentication, const char *agent, const char *identity, ks_json_t *network) { ks_status_t status; @@ -566,115 +637,125 @@ static ks_status_t __connect_wss(swclt_conn_ctx_t *ctx, ks_uuid_t previous_sessi else ks_log(KS_LOG_DEBUG, "Destroying previous web socket handle, re-connecting with new sessionid"); - ks_handle_destroy(&ctx->wss); + swclt_wss_destroy(&ctx->wss); } if (!ctx->info.wss.port) { ks_log(KS_LOG_INFO, "Port not specified, defaulting to 2100"); ctx->info.wss.port = 2100; } - + ks_log(KS_LOG_INFO, "Connecting to %s:%d/%s", ctx->info.wss.address, ctx->info.wss.port, ctx->info.wss.path); /* Create our websocket transport */ - if (status = swclt_wss_connect(&ctx->wss, (swclt_wss_incoming_frame_cb_t)__on_incoming_frame, - ctx, ctx->info.wss.address, ctx->info.wss.port, ctx->info.wss.path, ctx->info.wss.connect_timeout_ms, ctx->info.wss.ssl)) + if (status = swclt_wss_connect(&ctx->wss, + (swclt_wss_incoming_frame_cb_t)on_incoming_frame, ctx, + (swclt_wss_failed_cb_t)on_wss_failed, ctx, + ctx->info.wss.address, ctx->info.wss.port, ctx->info.wss.path, ctx->info.wss.connect_timeout_ms, ctx->info.wss.ssl)) return status; - /* Listen for state changes on the websocket */ - if (status = swclt_hstate_register_listener(&ctx->base, __on_wss_state_change, ctx->wss)) + /* Create TTL tracking thread */ + if (status = ttl_tracker_create(ctx->pool, &ctx->ttl, ctx)) return status; /* Now perform a logical connect to blade with the connect request */ - if (status = __do_logical_connect(ctx, previous_sessionid, authentication)) + if (status = do_logical_connect(ctx, previous_sessionid, authentication, agent, identity, network)) return status; return status; } -static ks_status_t __context_init( - swclt_conn_ctx_t *ctx, +SWCLT_DECLARE(void) swclt_conn_destroy(swclt_conn_t **conn) +{ + if (conn && *conn) { + ks_pool_t *pool = (*conn)->pool; + if ((*conn)->blade_connect_rpl) { + BLADE_CONNECT_RPL_DESTROY(&(*conn)->blade_connect_rpl); + } + ttl_tracker_destroy(&(*conn)->ttl); + swclt_wss_destroy(&(*conn)->wss); + if ((*conn)->incoming_frame_pool) { + ks_thread_pool_destroy(&(*conn)->incoming_frame_pool); + } + ks_hash_destroy(&(*conn)->outstanding_requests); + ks_mutex_destroy(&(*conn)->failed_mutex); + ks_pool_free(conn); + ks_pool_close(&pool); + } +} + +SWCLT_DECLARE(ks_status_t) swclt_conn_connect_ex( + swclt_conn_t **conn, swclt_conn_incoming_cmd_cb_t incoming_cmd_cb, void *incoming_cmd_cb_data, swclt_conn_connect_cb_t connect_cb, void *connect_cb_data, + swclt_conn_failed_cb_t failed_cb, + void *failed_cb_data, swclt_ident_t *ident, ks_uuid_t previous_sessionid, ks_json_t **authentication, + const char *agent, + const char *identity, + ks_json_t *network, const SSL_CTX *ssl) { - ks_status_t status; + ks_status_t status = KS_STATUS_SUCCESS; + ks_pool_t *pool = NULL; + ks_pool_open(&pool); + swclt_conn_t *new_conn = ks_pool_alloc(pool, sizeof(swclt_conn_t)); + new_conn->pool = pool; - ks_log(KS_LOG_INFO, "Initiating connection to: %s (parsed port: %u) at /%s", ident->host, (unsigned int)ident->portnum, ident->path); + ks_log(KS_LOG_INFO, "Initiating connection to: %s (parsed port: %u) at /%s", ident->host, (unsigned int)ident->portnum, ident->path ? ident->path : ""); - ctx->incoming_cmd_cb = incoming_cmd_cb; - ctx->incoming_cmd_cb_data = incoming_cmd_cb_data; - ctx->connect_cb = connect_cb; - ctx->connect_cb_data = connect_cb_data; + new_conn->incoming_cmd_cb = incoming_cmd_cb; + new_conn->incoming_cmd_cb_data = incoming_cmd_cb_data; + new_conn->connect_cb = connect_cb; + new_conn->connect_cb_data = connect_cb_data; + new_conn->failed_cb = failed_cb; + new_conn->failed_cb_data = failed_cb_data; /* Fill in the info on behalf of wss so we can re-use the same connect api * either during a reconnect or an initial connect */ - strncpy(ctx->info.wss.address, ident->host, sizeof(ctx->info.wss.address)); - ctx->info.wss.port = ident->portnum; - ctx->info.wss.ssl = (SSL_CTX *)ssl; - if (ident->path) strncpy(ctx->info.wss.path, ident->path, sizeof(ctx->info.wss.path)); - ctx->info.wss.connect_timeout_ms = 10000; - - if (status = ks_cond_create(&ctx->cmd_condition, ctx->base.pool)) - goto done; + strncpy(new_conn->info.wss.address, ident->host, sizeof(new_conn->info.wss.address)); + new_conn->info.wss.port = ident->portnum; + new_conn->info.wss.ssl = (SSL_CTX *)ssl; + if (ident->path) strncpy(new_conn->info.wss.path, ident->path, sizeof(new_conn->info.wss.path)); + new_conn->info.wss.connect_timeout_ms = 10000; /* Create our request hash */ - if (status = ks_hash_create(&ctx->outstanding_requests, KS_HASH_MODE_UUID, - KS_HASH_FLAG_DUP_CHECK | KS_HASH_FLAG_FREE_VALUE | KS_HASH_FLAG_FREE_KEY, ctx->base.pool)) + if (status = ks_hash_create(&new_conn->outstanding_requests, KS_HASH_MODE_UUID, + KS_HASH_FLAG_DUP_CHECK | KS_HASH_FLAG_FREE_KEY, new_conn->pool)) goto done; - /* Enable servicing */ - swclt_hstate_register_service(&ctx->base, __context_service); + ks_mutex_create(&new_conn->failed_mutex, KS_MUTEX_FLAG_DEFAULT, new_conn->pool); + + /* create thread pool to process incoming frames from websocket */ + if (status = ks_thread_pool_create(&new_conn->incoming_frame_pool, 1, 64, KS_THREAD_DEFAULT_STACK, + KS_PRI_DEFAULT, 30)) + goto done; /* Connect our websocket */ - if (status = __connect_wss(ctx, previous_sessionid, authentication)) + if (status = connect_wss(new_conn, previous_sessionid, authentication, agent, identity, network)) goto done; done: + if (status != KS_STATUS_SUCCESS) { + swclt_conn_destroy(&new_conn); + } + *conn = new_conn; return status; } -SWCLT_DECLARE(ks_status_t) swclt_conn_connect_ex( - swclt_conn_t *conn, - swclt_conn_incoming_cmd_cb_t incoming_cmd_cb, - void *incoming_cmd_cb_data, - swclt_conn_connect_cb_t connect_cb, - void *connect_cb_data, - swclt_ident_t *ident, - ks_uuid_t previous_sessionid, - ks_json_t **authentication, - const SSL_CTX *ssl) -{ - SWCLT_HANDLE_ALLOC_TEMPLATE_M( - NULL, - SWCLT_HTYPE_CONN, - conn, - swclt_conn_ctx_t, - SWCLT_HSTATE_NORMAL, - __context_describe, - __context_deinit, - __context_init, - incoming_cmd_cb, - incoming_cmd_cb_data, - connect_cb, - connect_cb_data, - ident, - previous_sessionid, - authentication, - ssl); -} - SWCLT_DECLARE(ks_status_t) swclt_conn_connect( - swclt_conn_t *conn, + swclt_conn_t **conn, swclt_conn_incoming_cmd_cb_t incoming_cmd_cb, void *incoming_cmd_cb_data, swclt_ident_t *ident, ks_json_t **authentication, + const char *agent, + const char *identity, + ks_json_t *network, const SSL_CTX *ssl) { return swclt_conn_connect_ex( @@ -683,43 +764,38 @@ SWCLT_DECLARE(ks_status_t) swclt_conn_connect( incoming_cmd_cb_data, NULL, NULL, + NULL, + NULL, ident, ks_uuid_null(), authentication, + agent, + identity, + network, ssl); } -SWCLT_DECLARE(ks_status_t) swclt_conn_submit_result(swclt_conn_t conn, swclt_cmd_t cmd) +SWCLT_DECLARE(ks_status_t) swclt_conn_submit_result(swclt_conn_t *conn, swclt_cmd_t *cmd) { - SWCLT_CONN_SCOPE_BEG(conn, ctx, status); - - status = __submit_result(ctx, cmd); - - SWCLT_CONN_SCOPE_END(conn, ctx, status); + if (conn) { + return submit_result(conn, cmd); + } + return KS_STATUS_DISCONNECTED; } -SWCLT_DECLARE(ks_status_t) swclt_conn_submit_request(swclt_conn_t conn, swclt_cmd_t cmd) +SWCLT_DECLARE(ks_status_t) swclt_conn_submit_request(swclt_conn_t *conn, swclt_cmd_t **cmd, swclt_cmd_future_t **future) { - SWCLT_CONN_SCOPE_BEG(conn, ctx, status); - - status = __submit_request(ctx, cmd); - - SWCLT_CONN_SCOPE_END(conn, ctx, status); + if (conn) { + return submit_request(conn, cmd, future); + } + return KS_STATUS_FAIL; } /* Private due to un-implemented caller ownership semantics, internal use only */ -ks_status_t swclt_conn_info(swclt_conn_t conn, swclt_conn_info_t *info) -{ - SWCLT_CONN_SCOPE_BEG(conn, ctx, status); - memcpy(info, &ctx->info, sizeof(ctx->info)); - SWCLT_CONN_SCOPE_END(conn, ctx, status); -} - -SWCLT_DECLARE(ks_status_t) swclt_conn_get_rates(swclt_conn_t conn, ks_throughput_t *recv, ks_throughput_t *send) +ks_status_t swclt_conn_info(swclt_conn_t *conn, swclt_conn_info_t *info) { - SWCLT_CONN_SCOPE_BEG(conn, ctx, status); - status = swclt_wss_get_rates(ctx->wss, recv, send); - SWCLT_CONN_SCOPE_END(conn, ctx, status); + memcpy(info, &conn->info, sizeof(conn->info)); + return KS_STATUS_SUCCESS; } /* For Emacs: diff --git a/src/handle_manager.c b/src/handle_manager.c deleted file mode 100644 index 3e14776..0000000 --- a/src/handle_manager.c +++ /dev/null @@ -1,336 +0,0 @@ -/* - * Copyright (c) 2018-2019 SignalWire, Inc - * - * 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. - */ - -#include "signalwire-client-c/client.h" - -/* The 'handle manager' in the signal wire client sdk, tracks all - * sessions and connections every so often to service them - * so that we can do things like re-establish dead sessions, - * process any expired commands on connections, etc. */ -static ks_thread_t *g_mgr_thread; /* the manager thread context itself */ -static ks_cond_t *g_mgr_condition; /* the manager sleeps on a condition for efficient wakeup */ -static ks_status_t g_mgr_final_status; /* We trap the last status here for some reason */ -static ks_time_t g_mgr_sleep_time_ms; /* Our current sleep ms count */ - -#define now() (ks_time_ms(ks_time_now())) - -static void __schedule_in_ms(ks_time_t schedule_in_ms) -{ - ks_cond_lock(g_mgr_condition); - - ks_assert(schedule_in_ms < 300000); - - if (!g_mgr_sleep_time_ms || g_mgr_sleep_time_ms > schedule_in_ms) - ks_log(KS_LOG_DEBUG, "Waking manager in: %lu", schedule_in_ms); - else { - ks_log(KS_LOG_DEBUG, "Not signalling service for next service time of: %lu", schedule_in_ms); - ks_log(KS_LOG_DEBUG, "Manager waking in: %lums", g_mgr_sleep_time_ms); - ks_cond_unlock(g_mgr_condition); - return; - } - - g_mgr_sleep_time_ms = schedule_in_ms; - - ks_log(KS_LOG_DEBUG, "Manager next sleep duration is: %lums", g_mgr_sleep_time_ms); - ks_cond_broadcast(g_mgr_condition); - ks_cond_unlock(g_mgr_condition); -} - -static void __report_state_change(swclt_hstate_listener_t *listener_ctx, swclt_handle_base_t *state_changed_ctx) -{ - swclt_handle_base_t *ctx; - - /* First we have to ensure the listener's handle is valid */ - if (ks_handle_get(0, listener_ctx->handle, &ctx)) - return; - - /* Finally, call the bastard */ - listener_ctx->cb(ctx, state_changed_ctx->pending_state_change_notification); - - ks_handle_put(0, &ctx); -} - -static void __notify_monitor_children(swclt_handle_base_t *ctx) -{ - ks_handle_t next = KS_NULL_HANDLE; - - while (KS_STATUS_SUCCESS == ks_handle_enum_type(SWCLT_HTYPE_HMON, &next)) { - ks_handle_t parent; - - if (ks_handle_parent(next, &parent)) - continue; - - /* If this monitor handle was watching the parent, raise its event */ - if (parent == ctx->handle) { - ks_log(KS_LOG_DEBUG, "Manager raising event on child monitor: %s for handle: %s", ks_handle_describe_ctx((ks_handle_base_t *)ctx), ks_handle_describe(parent)); - - swclt_hmon_raise_hstate_change(next, ctx->pending_state_change_notification); - } - } -} - -static ks_bool_t __service_handle(ks_handle_t handle) -{ - swclt_handle_base_t *ctx; - ks_bool_t serviced = KS_FALSE; - - if (ks_handle_get(0, handle, &ctx)) - return serviced; - - /* While we're here, handle state changes for the handle */ - ks_spinlock_acquire(&ctx->lock); - - ks_time_t now_time = now(); - - if (!ctx->next_service_time_stamp_ms) { - goto done; - } - - if (now_time < ctx->next_service_time_stamp_ms) { - /* Schedule it just to be sure */ - __schedule_in_ms(ctx->next_service_time_stamp_ms - now_time); - goto done; - } - - ks_log(KS_LOG_DEBUG, "Service begin: %s", ks_handle_describe_ctx(ctx)); - - serviced = KS_TRUE; - - if (ctx->service_cb) { - ks_spinlock_release(&ctx->lock); - ctx->service_cb(ctx); - ks_spinlock_acquire(&ctx->lock); - } - - if (ctx->pending_state_change_notification) { - ks_log(KS_LOG_DEBUG, "Handle: %s has pending state change notification", ks_handle_describe_ctx(ctx)); - - /* Update the state now, before we notify, don't want to confuse people - * when the state is reported but the handle doesn't have the new state in place */ - ctx->state = ctx->pending_state_change_notification->new_state; - - for (uint32_t cb_idx = 0; cb_idx < ctx->c_state_listeners; cb_idx++) { - swclt_hstate_listener_t state_listener; - - /* Grab the cb while under the lock then release it, we'll check if the - * array changed after we re-lock, this prevents some dicey scenarios where - * this lock gets held while a dependent thread is also locking on it */ - state_listener = ctx->state_listeners[cb_idx]; - - ks_spinlock_release(&ctx->lock); - - /* Call the listener */ - __report_state_change(&state_listener, ctx); - - /* Great now, if the array changed, thats ok, it can only grow, so we can proceed */ - ks_spinlock_acquire(&ctx->lock); - } - - /* Now iterate any monitors matching this parent and notify them too */ - ks_spinlock_release(&ctx->lock); - __notify_monitor_children(ctx); - ks_spinlock_acquire(&ctx->lock); - - /* State change is complete */ - ks_pool_free(&ctx->pending_state_change_notification); - } - - /* Now see if the handle had a pending state change request w/cb set */ - if (ctx->pending_state_change_handler_cb) { - ks_log(KS_LOG_DEBUG, "Handle: %s has pending state change", ks_handle_describe_ctx(ctx)); - - /* Grab the info while holding the lock as it may change during the cb or while we call */ - swclt_hstate_state_change_handler_cb_t cb = ctx->pending_state_change_handler_cb; - SWCLT_HSTATE new_state_request = ctx->pending_state_change_service; - ks_status_t state_change_status; - - ctx->pending_state_change_service = SWCLT_HSTATE_INVALID; - ctx->pending_state_change_handler_cb = NULL; - - /* Better be a non-invalid state */ - ks_assert(new_state_request != SWCLT_HSTATE_INVALID); - - /* Unlock and do the call */ - ks_spinlock_release(&ctx->lock); - - /* Call it, let it transition */ - if (state_change_status = cb(ctx, new_state_request)) { - /* Re-queue for them */ - ks_log(KS_LOG_DEBUG, "State change attempt (%s=>%s) failed (%lu) re-queueing in: %lums", - swclt_hstate_str(ctx->state), swclt_hstate_str(new_state_request), state_change_status, ctx->retry_service_delay_ms); - - swclt_hstate_initiate_change_in(ctx, new_state_request, - cb, ctx->retry_service_delay_ms, ctx->retry_service_delay_ms); - } - - ks_spinlock_acquire(&ctx->lock); - - /* If it was successful in its state change, go ahead and initiate a state change to the new - * state on behalf of the handle */ - if (state_change_status == KS_STATUS_SUCCESS) { - ks_pool_free(&ctx->pending_state_change_notification); - ks_spinlock_release(&ctx->lock); - swclt_hstate_changed(ctx, new_state_request, KS_STATUS_SUCCESS, "Manager completed state change request"); - ks_spinlock_acquire(&ctx->lock); - } - - } - - ks_log(KS_LOG_DEBUG, "Service end: %s", ks_handle_describe(handle)); - -done: - ks_spinlock_release(&ctx->lock); - ks_handle_put(0, &ctx); - return serviced; -} - -static void __service_handle_type(swclt_htype_t type) -{ - ks_handle_t next = KS_NULL_HANDLE; - uint32_t total = 0; - - ks_log(KS_LOG_DEBUG, "Service manager enumerating handles of type: %s", swclt_htype_str(type)); - - while (KS_STATUS_SUCCESS == ks_handle_enum_type(type, &next)) { - if (ks_thread_stop_requested(g_mgr_thread)) { - ks_log(KS_LOG_DEBUG, "Service manager stopping early due to stop request"); - break; - } - - if (__service_handle(next)) - total++; - } - - if (total) - ks_log(KS_LOG_DEBUG, "Service manager serviced: %lu handles of type: %s", total, swclt_htype_str(type)); -} - -static void __service_handles() -{ - /* Now loop through all handles and service them */ - __service_handle_type(SWCLT_HTYPE_CONN); - __service_handle_type(SWCLT_HTYPE_CMD); - __service_handle_type(SWCLT_HTYPE_FRAME); - __service_handle_type(SWCLT_HTYPE_WSS); - __service_handle_type(SWCLT_HTYPE_SESS); - __service_handle_type(SWCLT_HTYPE_SUB); - __service_handle_type(SWCLT_HTYPE_STORE); - __service_handle_type(SWCLT_HTYPE_TEST); -} - -static ks_status_t __manager_loop() -{ - ks_log(KS_LOG_INFO, "Manager loop starting"); - - /* Just hang out and wait to be woken up, with a hard wakeup at whatever - * the max is defined as no matter what */ - while (ks_thread_stop_requested(g_mgr_thread) == KS_FALSE) { - ks_cond_lock(g_mgr_condition); - - if (g_mgr_sleep_time_ms) { - ks_log(KS_LOG_DEBUG, "Manager sleeping for service in: %lums", g_mgr_sleep_time_ms); - ks_cond_timedwait(g_mgr_condition, g_mgr_sleep_time_ms); - } - else { - ks_log(KS_LOG_DEBUG, "Manager sleeping (not quite) indefinitely"); - ks_cond_timedwait(g_mgr_condition, 10000); - } - - ks_log(KS_LOG_DEBUG, "Manager woke up, checking handles"); - - /* Clear service time */ - g_mgr_sleep_time_ms = 0; - - ks_cond_unlock(g_mgr_condition); - - __service_handles(); - } - - ks_log(KS_LOG_INFO, "Manager loop stopping"); - - return KS_STATUS_SUCCESS; -} - -static void * __manager_thread_wrapper(ks_thread_t *thread, void *data) -{ - g_mgr_final_status = __manager_loop(); - return NULL; -} - -SWCLT_DECLARE(ks_status_t) swclt_hmgr_init(void) -{ - ks_status_t status; - - if (status = ks_cond_create(&g_mgr_condition, NULL)) { - ks_abort_fmt("Failed to allocate client manager condition: %lu", status); - } - - if (status = ks_thread_create(&g_mgr_thread, __manager_thread_wrapper, NULL, NULL)) - ks_abort_fmt("Failed to allocate client manager thread: %lu", status); - - return status; -} - -SWCLT_DECLARE(void) swclt_hmgr_shutdown(void) -{ - ks_log(KS_LOG_INFO, "Shutting down manager"); - - /* Do this in the right order, lock, - * flag thread request stopped, unlock - * then join/destroy. */ - ks_cond_lock(g_mgr_condition); - - ks_thread_request_stop(g_mgr_thread); - - ks_cond_broadcast(g_mgr_condition); - - ks_cond_unlock(g_mgr_condition); - - ks_thread_destroy(&g_mgr_thread); - ks_cond_destroy(&g_mgr_condition); -} - -SWCLT_DECLARE(void) swclt_hmgr_request_service_now(swclt_handle_base_t *ctx) -{ - swclt_hmgr_request_service_in(ctx, 1); -} - -SWCLT_DECLARE(void) swclt_hmgr_request_service_in( - swclt_handle_base_t *ctx, ks_time_t next_service_ms) -{ - ks_log(KS_LOG_DEBUG, "Handle: %s is requesting manager service in: %lums", - ks_handle_describe_ctx(ctx), next_service_ms); - - /* Wake up the manager at the given time by stashing this time then triggering it */ - ks_cond_lock(g_mgr_condition); - - /* Better be running */ - ks_assert(ks_thread_is_running(g_mgr_thread)); - - /* Update our pending service time */ - ctx->next_service_time_stamp_ms = now() + next_service_ms; - - /* Request service */ - __schedule_in_ms(next_service_ms); - - ks_cond_unlock(g_mgr_condition); -} diff --git a/src/handle_monitor.c b/src/handle_monitor.c deleted file mode 100644 index e372834..0000000 --- a/src/handle_monitor.c +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (c) 2018 SignalWire, Inc - * - * 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. - */ - -#include "signalwire-client-c/client.h" -#include "signalwire-client-c/internal/handle_monitor.h" - -static void __raise_hstate_change(swclt_hmon_ctx_t *ctx, const swclt_hstate_change_t *state_change_info) -{ - if (!ks_handle_valid(ctx->handle_to_monitor)) { - ks_log(KS_LOG_DEBUG, "Monitored handle event blocked, handle is now invalid"); - return; - } - - ks_log(KS_LOG_DEBUG, "Raising state change event: %s for monitor handle: %s", - swclt_hstate_describe_change(state_change_info), ks_handle_describe(ctx->handle_to_monitor)); - - ctx->cb(ctx->handle_to_monitor, state_change_info, ctx->cb_data); -} - -static void __context_describe(swclt_hmon_ctx_t *ctx, char *buffer, ks_size_t buffer_len) -{ - /* We have to do all this garbage because of the poor decision to nest ks_handle_describe() calls that return a common thread local buffer */ - const char *desc = ks_handle_describe(ctx->handle_to_monitor); - ks_size_t desc_len = strlen(desc); - const char *preamble = "SWCLT Handle Monitor for: "; - ks_size_t preamble_len = strlen(preamble); - if (desc_len + preamble_len + 1 > buffer_len) { - desc_len = buffer_len - preamble_len - 1; - } - memmove(buffer + preamble_len, desc, desc_len + 1); - memcpy(buffer, preamble, preamble_len); - buffer[buffer_len - 1] = '\0'; -} - -static ks_status_t __context_init(swclt_hmon_ctx_t *ctx, ks_handle_t handle_to_monitor, swclt_hmon_state_change_cb_t cb, void *cb_data) -{ - ctx->handle_to_monitor = handle_to_monitor; - ctx->cb = cb; - ctx->cb_data = cb_data; - - /* Associate ourselves a a child of the handle in question */ - return ks_handle_set_parent(ctx->base.handle, handle_to_monitor); -} - -static void __context_deinit(swclt_hmon_ctx_t *ctx) -{ -} - -SWCLT_DECLARE(ks_status_t) __swclt_hmon_register( - swclt_hmon_t *hmon, - ks_handle_t handle_to_monitor, - swclt_hmon_state_change_cb_t cb, - void *cb_data, - const char *file, - int line, - const char *tag) -{ - SWCLT_HANDLE_ALLOC_TEMPLATE_M_TAG( - NULL, - file, - line, - tag, - SWCLT_HTYPE_HMON, - hmon, - swclt_hmon_ctx_t, - SWCLT_HSTATE_NORMAL, - __context_describe, - __context_deinit, - __context_init, - handle_to_monitor, - cb, - cb_data); -} - -SWCLT_DECLARE(ks_status_t) swclt_hmon_raise_hstate_change(swclt_hmon_t hmon, const swclt_hstate_change_t *state_change_info) -{ - SWCLT_HMON_SCOPE_BEG(hmon, ctx, status) - __raise_hstate_change(ctx, state_change_info); - SWCLT_HMON_SCOPE_END(hmon, ctx, status) -} - diff --git a/src/handle_state.c b/src/handle_state.c deleted file mode 100644 index 765bf70..0000000 --- a/src/handle_state.c +++ /dev/null @@ -1,325 +0,0 @@ -/* - * Copyright (c) 2018 SignalWire, Inc - * - * 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. - */ - -#include "signalwire-client-c/client.h" - -#define now() ks_time_ms(ks_time_now()) - -/* swclt_hstate_check_ctx - If the current state is degraded will return - * the degraded reason code. It also takes an optional log message format - * string. Note: The description for the state will be appended to the input string */ -SWCLT_DECLARE(ks_status_t) swclt_hstate_check_ctx(const swclt_handle_base_t *ctx, const char *log_message) -{ - ks_status_t status = KS_STATUS_SUCCESS; - - ks_spinlock_acquire(&ctx->lock); - if (ctx->pending_state_change_notification) { - status = ctx->pending_state_change_notification->status; - - if (log_message) { - ks_log(KS_LOG_WARNING, "%s: %s", log_message, swclt_hstate_describe_change(ctx->pending_state_change_notification)); - } - } else { - switch (ctx->state) { - case SWCLT_HSTATE_ONLINE: - case SWCLT_HSTATE_NORMAL: - break; - - case SWCLT_HSTATE_OFFLINE: - case SWCLT_HSTATE_DEGRADED: - status = KS_STATUS_NOT_ALLOWED; - break; - } - } - ks_spinlock_release(&ctx->lock); - - return status; -} - -/* swclt_hstate_check - Given a handle, checks it out, then probes its pending state member - * if set, returns the state, otherwise returns the normal state. Note: If the handle is invalid - * we will assume a degraded state. */ -SWCLT_DECLARE(ks_status_t) swclt_hstate_check(ks_handle_t handle, const char *log_message) -{ - swclt_handle_base_t *ctx; - ks_status_t status; - - if (status = ks_handle_get(0, handle, &ctx)) { - ks_log(KS_LOG_WARNING, "Unexpected degraded handle state on state check: %d", status); - return status; - } - - status = swclt_hstate_check_ctx(ctx, log_message); - - ks_handle_put(0, &ctx); - - return status; -} - -/* swclt_handle_state - Returns the current state of the handles context, will - * not wait for the service manager to service, once swclt_hstate_changed is - * called, this api will immediatly return the new state. This is useful to - * handle i/o into and out of handles, as you can first check the current state - * before u do something. */ -SWCLT_DECLARE(SWCLT_HSTATE) swclt_hstate_ctx(swclt_handle_base_t *ctx) -{ - SWCLT_HSTATE state; - - ks_spinlock_acquire(&ctx->lock); - if (ctx->pending_state_change_service) - state = ctx->pending_state_change_service; - else - state = ctx->state; - ks_spinlock_release(&ctx->lock); - - return state; -} - -/* swclt_hstate_changed - Private function which can be called by any - * client handle when they want to announce some kind of state change. The - * state is set in the context and the manager is enqueued to service it. */ -SWCLT_DECLARE(void) __swclt_hstate_changed( - swclt_handle_base_t *ctx, - SWCLT_HSTATE new_state, - ks_status_t status, - const char *reason, - const char *file, - int line, - const char *tag) -{ - ks_spinlock_acquire(&ctx->lock); - - /* Layering is not allowed */ - if (ctx->pending_state_change_notification) { - ks_log(KS_LOG_WARNING, "State change [%s=>%s] denied, pending state change present [%s=>%s]", - ctx->state, new_state, ctx->pending_state_change_notification->new_state, ctx->pending_state_change_notification->new_state); - - ks_debug_break(); - ks_spinlock_release(&ctx->lock); - return; - } - - /* Set the state */ - ctx->last_state = ctx->state; - ctx->state = new_state; - - if (!(ctx->pending_state_change_notification = ks_pool_alloc(ctx->pool, sizeof(swclt_hstate_change_t)))) - ks_abort("Failed to allocate bytes for state change request"); - - ctx->pending_state_change_notification->status = status; - ctx->pending_state_change_notification->new_state = new_state; - ctx->pending_state_change_notification->old_state = ctx->last_state; - - if (!(ctx->pending_state_change_notification->reason = ks_pstrdup(ctx->pool, reason))) - ks_abort("Failed to allocate bytes for state change request"); - -#if defined(KS_BUILD_DEBUG) - ctx->pending_state_change_notification->file = file; - ctx->pending_state_change_notification->line = line; - ctx->pending_state_change_notification->tag = tag; -#endif - - if (ctx->pending_state_change_notification->new_state == SWCLT_HSTATE_DEGRADED || ctx->pending_state_change_notification->new_state == SWCLT_HSTATE_INVALID) { -#if defined(KS_BUILD_DEBUG) - /* FORCE logging on from here on out */ - //swclt_enable_log_output(KS_LOG_LEVEL_DEBUG); -#endif - ks_log(KS_LOG_WARNING, "Handle: %s state changed: %s for handle: %s", swclt_htype_str(KS_HANDLE_TYPE_FROM_HANDLE(ctx->handle)), - swclt_hstate_describe_change(ctx->pending_state_change_notification), ks_handle_describe(ctx->handle)); - } else { - ks_log(KS_LOG_INFO, "Handle: %s state changed: %s for handle: %s", - swclt_htype_str(KS_HANDLE_TYPE_FROM_HANDLE(ctx->handle)), swclt_hstate_describe_change(ctx->pending_state_change_notification), ks_handle_describe(ctx->handle)); - } - - ks_spinlock_release(&ctx->lock); - - /* Request service real soon, but not so soon we freak out */ - swclt_hmgr_request_service_in(ctx, 1000); -} - -/* swclt_hstate_register_listener - Adds a listener callback to a handles context. Listeners can be many to one, e.g. more then one - * consumer may want to be called when say a websocket gets diconnected, this includes the connection - * and/or the session, hence we have an allocated array of callback ptrs we register when this api - * is called. The caller can optionally specify a handle we should verify for their context */ -SWCLT_DECLARE(ks_status_t) __swclt_hstate_register_listener( - swclt_handle_base_t *listening_ctx, - swclt_hstate_change_cb_t state_cb, - ks_handle_t state_source_handle, - const char *file, - int line, - const char *tag) -{ - swclt_handle_base_t *state_source_ctx; - - /* We'll need to get to the context of the source the listener wants to watch */ - if (__ks_handle_get(0, state_source_handle, (ks_handle_base_t **)&state_source_ctx, file, line, tag)) { - ks_log(KS_LOG_WARNING, "Error attempting to register a state change listener on invalid handle: %16.16llx for listener: %s", - state_source_handle, ks_handle_describe(listening_ctx->handle)); - return KS_STATUS_HANDLE_INVALID; - } - - /* Lock and register another listener on the source context */ - ks_spinlock_acquire(&state_source_ctx->lock); - - /* Re-alloc and grow the array by one callback */ - if (state_source_ctx->state_listeners) { - if (!(state_source_ctx->state_listeners = ks_pool_resize( - state_source_ctx->state_listeners, sizeof(swclt_hstate_listener_t) * state_source_ctx->c_state_listeners + 1))) - ks_abort("Error re-allocating state change listener callback array"); - } else { - if (!(state_source_ctx->state_listeners = - ks_pool_alloc(state_source_ctx->pool, sizeof(swclt_hstate_listener_t)))) - ks_abort("Error allocating state change listener callback array"); - } - - /* And fill in the new entry */ - state_source_ctx->state_listeners[ - state_source_ctx->c_state_listeners].cb = state_cb; - state_source_ctx->state_listeners[ - state_source_ctx->c_state_listeners].handle = listening_ctx->handle; - state_source_ctx->c_state_listeners++; - - ks_spinlock_release(&state_source_ctx->lock); - - __ks_handle_put(0, (ks_handle_base_t **)&state_source_ctx, file, line, tag); - return KS_STATUS_SUCCESS; -} - -/* swclt_hstate_register_service - Binds a callback in the handles own context, that when set will - * allow the client service to call into the handle periodically, when requested, to service it. - * Servicing typically is how these handles do things like get re-connected when the socket dies. */ -SWCLT_DECLARE(void) __swclt_hstate_register_service( - swclt_handle_base_t *ctx, - swclt_hstate_service_cb_t service_cb, - const char *file, - int line, - const char *tag) -{ - ks_spinlock_acquire(&ctx->lock); - ctx->service_cb = service_cb; - ks_spinlock_release(&ctx->lock); -} - -SWCLT_DECLARE(SWCLT_HSTATE) swclt_hstate_get_ctx(const swclt_handle_base_t *ctx) -{ - SWCLT_HSTATE state; - - ks_spinlock_acquire(&ctx->lock); - if (ctx->pending_state_change_service != SWCLT_HSTATE_INVALID) - state = ctx->pending_state_change_service; - else - state = ctx->state; - ks_assert(state != SWCLT_HSTATE_INVALID); - ks_spinlock_release(&ctx->lock); - - return state; -} - -/** - * swclt_hstate_pending_get - Returns the pending state, useful for when you - * want control during a poll of a state change. - */ -SWCLT_DECLARE(SWCLT_HSTATE) swclt_hstate_pending_get(ks_handle_t handle) -{ - swclt_handle_base_t *ctx; - SWCLT_HSTATE state; - - ks_assert(swclt_htype_valid(KS_HANDLE_TYPE_FROM_HANDLE(handle))); - - if(ks_handle_get(0, handle, &ctx)) - return SWCLT_HSTATE_INVALID; - - ks_spinlock_acquire(&ctx->lock); - state = ctx->pending_state_change_service; - ks_spinlock_release(&ctx->lock); - - ks_handle_put(0, &ctx); - - return state; -} - -SWCLT_DECLARE(SWCLT_HSTATE) swclt_hstate_current_get(ks_handle_t handle) -{ - swclt_handle_base_t *ctx; - SWCLT_HSTATE state; - - ks_assert(swclt_htype_valid(KS_HANDLE_TYPE_FROM_HANDLE(handle))); - - if(ks_handle_get(0, handle, &ctx)) - return SWCLT_HSTATE_INVALID; - - ks_spinlock_acquire(&ctx->lock); - state = ctx->state; - ks_spinlock_release(&ctx->lock); - - ks_handle_put(0, &ctx); - - return state; -} - -SWCLT_DECLARE(SWCLT_HSTATE) swclt_hstate_get(ks_handle_t handle) -{ - swclt_handle_base_t *base; - SWCLT_HSTATE state; - - ks_assert(swclt_htype_valid(KS_HANDLE_TYPE_FROM_HANDLE(handle))); - - if(ks_handle_get(0, handle, &base)) - return SWCLT_HSTATE_INVALID; - - state = swclt_hstate_get_ctx(base); - - ks_handle_put(0, &base); - - return state; -} - -SWCLT_DECLARE(void) __swclt_hstate_initiate_change_now( - swclt_handle_base_t *base, - SWCLT_HSTATE new_state, - swclt_hstate_state_change_handler_cb_t cb, - ks_time_t retry_delay_ms) -{ - swclt_hstate_initiate_change_in(base, new_state, cb, 1, retry_delay_ms); -} - -SWCLT_DECLARE(void) __swclt_hstate_initiate_change_in( - swclt_handle_base_t *base, - SWCLT_HSTATE new_state, - swclt_hstate_state_change_handler_cb_t cb, - ks_time_t next_service_ms, - ks_time_t retry_delay_ms) -{ - ks_log(KS_LOG_INFO, "Handle: %s is initiating state change (state change: %s=>%s) in: %lums", - ks_handle_describe_ctx(base), swclt_hstate_str(base->state), swclt_hstate_str(new_state), next_service_ms); - - /* Update our pending service flag to true */ - ks_spinlock_acquire(&base->lock); - ks_assert(base->state != new_state); - base->pending_state_change_service = new_state; - base->pending_state_change_handler_cb = cb; - base->next_service_time_stamp_ms = now() + next_service_ms; - base->retry_service_delay_ms = retry_delay_ms; - ks_spinlock_release(&base->lock); - - swclt_hmgr_request_service_in(base, next_service_ms); -} diff --git a/src/init.c b/src/init.c index b69561f..6ce4427 100644 --- a/src/init.c +++ b/src/init.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 SignalWire, Inc + * Copyright (c) 2018-2020 SignalWire, Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -31,19 +31,11 @@ SWCLT_DECLARE(ks_status_t) swclt_init(int default_log_level) swclt_enable_log_output(default_log_level); - if (status = swclt_hmgr_init()) { - ks_log(KS_LOG_CRIT, "Failed to initialize client manager: %lu", status); - ks_shutdown(); - ks_debug_break(); - return status; - } - return status; } SWCLT_DECLARE(ks_status_t) swclt_shutdown(void) { - swclt_hmgr_shutdown(); return ks_shutdown(); } diff --git a/src/nodestore.c b/src/nodestore.c index 8deb549..c756eb0 100644 --- a/src/nodestore.c +++ b/src/nodestore.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2019 SignalWire, Inc + * Copyright (c) 2018-2020 SignalWire, Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,314 +21,304 @@ */ #include "signalwire-client-c/client.h" -#include "signalwire-client-c/internal/nodestore.h" -// @todo callbacks should be stored in internal lists of callbacks to support multiple consumers -// @todo if userdata is added requiring a structure of data, then maybe wrap within a handle as well? -static swclt_sess_t __get_sess_from_store_ctx(swclt_store_ctx_t *ctx) -{ - ks_handle_t parent; - ks_handle_parent(ctx->base.handle, &parent); - return parent; -} - -static ks_status_t __add_cb_route_add(swclt_store_ctx_t *ctx, swclt_store_cb_route_add_t cb) +static ks_status_t __add_cb_route_add(swclt_store_t *store, swclt_store_cb_route_add_t cb) { ks_log(KS_LOG_DEBUG, "Adding route add handler for method: %s", BLADE_NETCAST_CMD_ROUTE_ADD); - return ks_hash_insert(ctx->callbacks, (const void *)BLADE_NETCAST_CMD_ROUTE_ADD, (void *)cb); + return ks_hash_insert(store->callbacks, (const void *)BLADE_NETCAST_CMD_ROUTE_ADD, (void *)cb); } -static void __invoke_cb_route_add(swclt_store_ctx_t *ctx, const blade_netcast_rqu_t *rqu, const blade_netcast_route_add_param_t *params) +static void __invoke_cb_route_add(swclt_store_t *store, const blade_netcast_rqu_t *rqu, const blade_netcast_route_add_param_t *params) { swclt_store_cb_route_add_t cb; ks_log(KS_LOG_DEBUG, "Looking up route add handler for method: %s", BLADE_NETCAST_CMD_ROUTE_ADD); - ks_hash_read_lock(ctx->callbacks); - cb = (swclt_store_cb_route_add_t)ks_hash_search(ctx->callbacks, + ks_hash_read_lock(store->callbacks); + cb = (swclt_store_cb_route_add_t)ks_hash_search(store->callbacks, (const void *)BLADE_NETCAST_CMD_ROUTE_ADD, KS_UNLOCKED); - ks_hash_read_unlock(ctx->callbacks); + ks_hash_read_unlock(store->callbacks); if (cb) { ks_log(KS_LOG_DEBUG, "Invoking callback for node store add"); - cb(__get_sess_from_store_ctx(ctx), rqu, params); + cb(store->sess, rqu, params); } else { ks_log(KS_LOG_DEBUG, "No callback registered for route add method: %s", BLADE_NETCAST_CMD_ROUTE_ADD); } } -static ks_status_t __add_cb_route_remove(swclt_store_ctx_t *ctx, swclt_store_cb_route_remove_t cb) +static ks_status_t __add_cb_route_remove(swclt_store_t *store, swclt_store_cb_route_remove_t cb) { - return ks_hash_insert(ctx->callbacks, (const void *)BLADE_NETCAST_CMD_ROUTE_REMOVE, (void *)cb); + return ks_hash_insert(store->callbacks, (const void *)BLADE_NETCAST_CMD_ROUTE_REMOVE, (void *)cb); } -static void __invoke_cb_route_remove(swclt_store_ctx_t *ctx, const blade_netcast_rqu_t *rqu, const blade_netcast_route_remove_param_t *params) +static void __invoke_cb_route_remove(swclt_store_t *store, const blade_netcast_rqu_t *rqu, const blade_netcast_route_remove_param_t *params) { swclt_store_cb_route_remove_t cb; - ks_hash_read_lock(ctx->callbacks); - cb = (swclt_store_cb_route_remove_t)ks_hash_search(ctx->callbacks, + ks_hash_read_lock(store->callbacks); + cb = (swclt_store_cb_route_remove_t)ks_hash_search(store->callbacks, (const void *)BLADE_NETCAST_CMD_ROUTE_REMOVE, KS_UNLOCKED); - ks_hash_read_unlock(ctx->callbacks); + ks_hash_read_unlock(store->callbacks); - if (cb) cb(__get_sess_from_store_ctx(ctx), rqu, params); + if (cb) cb(store->sess, rqu, params); } -static ks_status_t __add_cb_identity_add(swclt_store_ctx_t *ctx, swclt_store_cb_identity_add_t cb) +static ks_status_t __add_cb_identity_add(swclt_store_t *store, swclt_store_cb_identity_add_t cb) { ks_log(KS_LOG_DEBUG, "Adding identity add handler for method: %s", BLADE_NETCAST_CMD_IDENTITY_ADD); - return ks_hash_insert(ctx->callbacks, (const void *)BLADE_NETCAST_CMD_IDENTITY_ADD, (void *)cb); + return ks_hash_insert(store->callbacks, (const void *)BLADE_NETCAST_CMD_IDENTITY_ADD, (void *)cb); } -static void __invoke_cb_identity_add(swclt_store_ctx_t *ctx, const blade_netcast_rqu_t *rqu, const blade_netcast_identity_add_param_t *params) +static void __invoke_cb_identity_add(swclt_store_t *store, const blade_netcast_rqu_t *rqu, const blade_netcast_identity_add_param_t *params) { swclt_store_cb_identity_add_t cb; ks_log(KS_LOG_DEBUG, "Looking up identity add handler for method: %s", BLADE_NETCAST_CMD_IDENTITY_ADD); - ks_hash_read_lock(ctx->callbacks); - cb = (swclt_store_cb_identity_add_t)ks_hash_search(ctx->callbacks, + ks_hash_read_lock(store->callbacks); + cb = (swclt_store_cb_identity_add_t)ks_hash_search(store->callbacks, (const void *)BLADE_NETCAST_CMD_IDENTITY_ADD, KS_UNLOCKED); - ks_hash_read_unlock(ctx->callbacks); + ks_hash_read_unlock(store->callbacks); if (cb) { ks_log(KS_LOG_DEBUG, "Invoking callback for node store identity add"); - cb(__get_sess_from_store_ctx(ctx), rqu, params); + cb(store->sess, rqu, params); } else { ks_log(KS_LOG_DEBUG, "No callback registered for identity add method: %s", BLADE_NETCAST_CMD_IDENTITY_ADD); } } -static ks_status_t __add_cb_identity_remove(swclt_store_ctx_t *ctx, swclt_store_cb_identity_remove_t cb) +static ks_status_t __add_cb_identity_remove(swclt_store_t *store, swclt_store_cb_identity_remove_t cb) { - return ks_hash_insert(ctx->callbacks, (const void *)BLADE_NETCAST_CMD_IDENTITY_REMOVE, (void *)cb); + return ks_hash_insert(store->callbacks, (const void *)BLADE_NETCAST_CMD_IDENTITY_REMOVE, (void *)cb); } -static void __invoke_cb_identity_remove(swclt_store_ctx_t *ctx, const blade_netcast_rqu_t *rqu, const blade_netcast_identity_remove_param_t *params) +static void __invoke_cb_identity_remove(swclt_store_t *store, const blade_netcast_rqu_t *rqu, const blade_netcast_identity_remove_param_t *params) { swclt_store_cb_identity_remove_t cb; - ks_hash_read_lock(ctx->callbacks); - cb = (swclt_store_cb_identity_remove_t)ks_hash_search(ctx->callbacks, + ks_hash_read_lock(store->callbacks); + cb = (swclt_store_cb_identity_remove_t)ks_hash_search(store->callbacks, (const void *)BLADE_NETCAST_CMD_IDENTITY_REMOVE, KS_UNLOCKED); - ks_hash_read_unlock(ctx->callbacks); + ks_hash_read_unlock(store->callbacks); - if (cb) cb(__get_sess_from_store_ctx(ctx), rqu, params); + if (cb) cb(store->sess, rqu, params); } -static ks_status_t __add_cb_protocol_add(swclt_store_ctx_t *ctx, swclt_store_cb_protocol_add_t cb) +static ks_status_t __add_cb_protocol_add(swclt_store_t *store, swclt_store_cb_protocol_add_t cb) { - return ks_hash_insert(ctx->callbacks, (const void *)BLADE_NETCAST_CMD_PROTOCOL_ADD, (void *)cb); + return ks_hash_insert(store->callbacks, (const void *)BLADE_NETCAST_CMD_PROTOCOL_ADD, (void *)cb); } -static void __invoke_cb_protocol_add(swclt_store_ctx_t *ctx, +static void __invoke_cb_protocol_add(swclt_store_t *store, const char *protocol) { swclt_store_cb_protocol_add_t cb; - ks_hash_read_lock(ctx->callbacks); - cb = (swclt_store_cb_protocol_add_t)ks_hash_search(ctx->callbacks, + ks_hash_read_lock(store->callbacks); + cb = (swclt_store_cb_protocol_add_t)ks_hash_search(store->callbacks, (const void *)BLADE_NETCAST_CMD_PROTOCOL_ADD, KS_UNLOCKED); - ks_hash_read_unlock(ctx->callbacks); + ks_hash_read_unlock(store->callbacks); - if (cb) cb(__get_sess_from_store_ctx(ctx), protocol); + if (cb) cb(store->sess, protocol); } -static ks_status_t __add_cb_protocol_remove(swclt_store_ctx_t *ctx, swclt_store_cb_protocol_remove_t cb) +static ks_status_t __add_cb_protocol_remove(swclt_store_t *store, swclt_store_cb_protocol_remove_t cb) { - return ks_hash_insert(ctx->callbacks, (const void *)BLADE_NETCAST_CMD_PROTOCOL_REMOVE, (void *)cb); + return ks_hash_insert(store->callbacks, (const void *)BLADE_NETCAST_CMD_PROTOCOL_REMOVE, (void *)cb); } -static void __invoke_cb_protocol_remove(swclt_store_ctx_t *ctx, +static void __invoke_cb_protocol_remove(swclt_store_t *store, const char *protocol) { swclt_store_cb_protocol_remove_t cb; - ks_hash_read_lock(ctx->callbacks); - cb = (swclt_store_cb_protocol_remove_t)ks_hash_search(ctx->callbacks, + ks_hash_read_lock(store->callbacks); + cb = (swclt_store_cb_protocol_remove_t)ks_hash_search(store->callbacks, (const void *)BLADE_NETCAST_CMD_PROTOCOL_REMOVE, KS_UNLOCKED); - ks_hash_read_unlock(ctx->callbacks); + ks_hash_read_unlock(store->callbacks); - if (cb) cb(__get_sess_from_store_ctx(ctx), protocol); + if (cb) cb(store->sess, protocol); } -static ks_status_t __add_cb_protocol_provider_add(swclt_store_ctx_t *ctx, swclt_store_cb_protocol_provider_add_t cb) +static ks_status_t __add_cb_protocol_provider_add(swclt_store_t *store, swclt_store_cb_protocol_provider_add_t cb) { - return ks_hash_insert(ctx->callbacks, (const void *)BLADE_NETCAST_CMD_PROTOCOL_PROVIDER_ADD, (void *)cb); + return ks_hash_insert(store->callbacks, (const void *)BLADE_NETCAST_CMD_PROTOCOL_PROVIDER_ADD, (void *)cb); } -static void __invoke_cb_protocol_provider_add(swclt_store_ctx_t *ctx, +static void __invoke_cb_protocol_provider_add(swclt_store_t *store, const blade_netcast_rqu_t *rqu, const blade_netcast_protocol_provider_add_param_t *params) { swclt_store_cb_protocol_provider_add_t cb; - ks_hash_read_lock(ctx->callbacks); - cb = (swclt_store_cb_protocol_provider_add_t)ks_hash_search(ctx->callbacks, + ks_hash_read_lock(store->callbacks); + cb = (swclt_store_cb_protocol_provider_add_t)ks_hash_search(store->callbacks, (const void *)BLADE_NETCAST_CMD_PROTOCOL_PROVIDER_ADD, KS_UNLOCKED); - ks_hash_read_unlock(ctx->callbacks); + ks_hash_read_unlock(store->callbacks); - if (cb) cb(__get_sess_from_store_ctx(ctx), rqu, params); + if (cb) cb(store->sess, rqu, params); } -static ks_status_t __add_cb_protocol_provider_remove(swclt_store_ctx_t *ctx, swclt_store_cb_protocol_provider_remove_t cb) +static ks_status_t __add_cb_protocol_provider_remove(swclt_store_t *store, swclt_store_cb_protocol_provider_remove_t cb) { - return ks_hash_insert(ctx->callbacks, (const void *)BLADE_NETCAST_CMD_PROTOCOL_PROVIDER_REMOVE, (void *)cb); + return ks_hash_insert(store->callbacks, (const void *)BLADE_NETCAST_CMD_PROTOCOL_PROVIDER_REMOVE, (void *)cb); } -static void __invoke_cb_protocol_provider_remove(swclt_store_ctx_t *ctx, +static void __invoke_cb_protocol_provider_remove(swclt_store_t *store, const blade_netcast_rqu_t *rqu, const blade_netcast_protocol_provider_remove_param_t *params) { swclt_store_cb_protocol_provider_remove_t cb; - ks_hash_read_lock(ctx->callbacks); - cb = (swclt_store_cb_protocol_provider_remove_t)ks_hash_search(ctx->callbacks, + ks_hash_read_lock(store->callbacks); + cb = (swclt_store_cb_protocol_provider_remove_t)ks_hash_search(store->callbacks, (const void *)BLADE_NETCAST_CMD_PROTOCOL_PROVIDER_REMOVE, KS_UNLOCKED); - ks_hash_read_unlock(ctx->callbacks); + ks_hash_read_unlock(store->callbacks); - if (cb) cb(__get_sess_from_store_ctx(ctx), rqu, params); + if (cb) cb(store->sess, rqu, params); } -static ks_status_t __add_cb_protocol_provider_rank_update(swclt_store_ctx_t *ctx, swclt_store_cb_protocol_provider_rank_update_t cb) +static ks_status_t __add_cb_protocol_provider_rank_update(swclt_store_t *store, swclt_store_cb_protocol_provider_rank_update_t cb) { - return ks_hash_insert(ctx->callbacks, (const void *)BLADE_NETCAST_CMD_PROTOCOL_PROVIDER_RANK_UPDATE, (void *)cb); + return ks_hash_insert(store->callbacks, (const void *)BLADE_NETCAST_CMD_PROTOCOL_PROVIDER_RANK_UPDATE, (void *)cb); } -static void __invoke_cb_protocol_provider_rank_update(swclt_store_ctx_t *ctx, +static void __invoke_cb_protocol_provider_rank_update(swclt_store_t *store, const blade_netcast_rqu_t *rqu, const blade_netcast_protocol_provider_rank_update_param_t *params) { swclt_store_cb_protocol_provider_rank_update_t cb; - ks_hash_read_lock(ctx->callbacks); - cb = (swclt_store_cb_protocol_provider_rank_update_t)ks_hash_search(ctx->callbacks, + ks_hash_read_lock(store->callbacks); + cb = (swclt_store_cb_protocol_provider_rank_update_t)ks_hash_search(store->callbacks, (const void *)BLADE_NETCAST_CMD_PROTOCOL_PROVIDER_RANK_UPDATE, KS_UNLOCKED); - ks_hash_read_unlock(ctx->callbacks); + ks_hash_read_unlock(store->callbacks); - if (cb) cb(__get_sess_from_store_ctx(ctx), rqu, params); + if (cb) cb(store->sess, rqu, params); } -static ks_status_t __add_cb_protocol_provider_data_update(swclt_store_ctx_t *ctx, swclt_store_cb_protocol_provider_data_update_t cb) +static ks_status_t __add_cb_protocol_provider_data_update(swclt_store_t *store, swclt_store_cb_protocol_provider_data_update_t cb) { - return ks_hash_insert(ctx->callbacks, (const void *)BLADE_NETCAST_CMD_PROTOCOL_PROVIDER_DATA_UPDATE, (void *)cb); + return ks_hash_insert(store->callbacks, (const void *)BLADE_NETCAST_CMD_PROTOCOL_PROVIDER_DATA_UPDATE, (void *)cb); } -static void __invoke_cb_protocol_provider_data_update(swclt_store_ctx_t *ctx, +static void __invoke_cb_protocol_provider_data_update(swclt_store_t *store, const blade_netcast_rqu_t *rqu, const blade_netcast_protocol_provider_data_update_param_t *params) { swclt_store_cb_protocol_provider_data_update_t cb; - ks_hash_read_lock(ctx->callbacks); - cb = (swclt_store_cb_protocol_provider_data_update_t)ks_hash_search(ctx->callbacks, + ks_hash_read_lock(store->callbacks); + cb = (swclt_store_cb_protocol_provider_data_update_t)ks_hash_search(store->callbacks, (const void *)BLADE_NETCAST_CMD_PROTOCOL_PROVIDER_DATA_UPDATE, KS_UNLOCKED); - ks_hash_read_unlock(ctx->callbacks); + ks_hash_read_unlock(store->callbacks); - if (cb) cb(__get_sess_from_store_ctx(ctx), rqu, params); + if (cb) cb(store->sess, rqu, params); } -static ks_status_t __add_cb_authority_add(swclt_store_ctx_t *ctx, swclt_store_cb_authority_add_t cb) +static ks_status_t __add_cb_authority_add(swclt_store_t *store, swclt_store_cb_authority_add_t cb) { ks_log(KS_LOG_DEBUG, "Adding authority add handler for method: %s", BLADE_NETCAST_CMD_AUTHORITY_ADD); - return ks_hash_insert(ctx->callbacks, (const void *)BLADE_NETCAST_CMD_AUTHORITY_ADD, (void *)cb); + return ks_hash_insert(store->callbacks, (const void *)BLADE_NETCAST_CMD_AUTHORITY_ADD, (void *)cb); } -static void __invoke_cb_authority_add(swclt_store_ctx_t *ctx, const blade_netcast_rqu_t *rqu, const blade_netcast_authority_add_param_t *params) +static void __invoke_cb_authority_add(swclt_store_t *store, const blade_netcast_rqu_t *rqu, const blade_netcast_authority_add_param_t *params) { swclt_store_cb_authority_add_t cb; ks_log(KS_LOG_DEBUG, "Looking up authority add handler for method: %s", BLADE_NETCAST_CMD_AUTHORITY_ADD); - ks_hash_read_lock(ctx->callbacks); - cb = (swclt_store_cb_authority_add_t)ks_hash_search(ctx->callbacks, + ks_hash_read_lock(store->callbacks); + cb = (swclt_store_cb_authority_add_t)ks_hash_search(store->callbacks, (const void *)BLADE_NETCAST_CMD_AUTHORITY_ADD, KS_UNLOCKED); - ks_hash_read_unlock(ctx->callbacks); + ks_hash_read_unlock(store->callbacks); if (cb) { ks_log(KS_LOG_DEBUG, "Invoking callback for node store add"); - cb(__get_sess_from_store_ctx(ctx), rqu, params); + cb(store->sess, rqu, params); } else { ks_log(KS_LOG_DEBUG, "No callback registered for authority add method: %s", BLADE_NETCAST_CMD_AUTHORITY_ADD); } } -static ks_status_t __add_cb_authority_remove(swclt_store_ctx_t *ctx, swclt_store_cb_authority_remove_t cb) +static ks_status_t __add_cb_authority_remove(swclt_store_t *store, swclt_store_cb_authority_remove_t cb) { - return ks_hash_insert(ctx->callbacks, (const void *)BLADE_NETCAST_CMD_AUTHORITY_REMOVE, (void *)cb); + return ks_hash_insert(store->callbacks, (const void *)BLADE_NETCAST_CMD_AUTHORITY_REMOVE, (void *)cb); } -static void __invoke_cb_authority_remove(swclt_store_ctx_t *ctx, const blade_netcast_rqu_t *rqu, const blade_netcast_authority_remove_param_t *params) +static void __invoke_cb_authority_remove(swclt_store_t *store, const blade_netcast_rqu_t *rqu, const blade_netcast_authority_remove_param_t *params) { swclt_store_cb_authority_remove_t cb; - ks_hash_read_lock(ctx->callbacks); - cb = (swclt_store_cb_authority_remove_t)ks_hash_search(ctx->callbacks, + ks_hash_read_lock(store->callbacks); + cb = (swclt_store_cb_authority_remove_t)ks_hash_search(store->callbacks, (const void *)BLADE_NETCAST_CMD_AUTHORITY_REMOVE, KS_UNLOCKED); - ks_hash_read_unlock(ctx->callbacks); + ks_hash_read_unlock(store->callbacks); - if (cb) cb(__get_sess_from_store_ctx(ctx), rqu, params); + if (cb) cb(store->sess, rqu, params); } -static ks_status_t __add_cb_subscription_add(swclt_store_ctx_t *ctx, swclt_store_cb_subscription_add_t cb) +static ks_status_t __add_cb_subscription_add(swclt_store_t *store, swclt_store_cb_subscription_add_t cb) { ks_log(KS_LOG_DEBUG, "Adding authority add handler for method: %s", BLADE_NETCAST_CMD_SUBSCRIPTION_ADD); - return ks_hash_insert(ctx->callbacks, (const void *)BLADE_NETCAST_CMD_SUBSCRIPTION_ADD, (void *)cb); + return ks_hash_insert(store->callbacks, (const void *)BLADE_NETCAST_CMD_SUBSCRIPTION_ADD, (void *)cb); } -static void __invoke_cb_subscription_add(swclt_store_ctx_t *ctx, const blade_netcast_rqu_t *rqu, const blade_netcast_subscription_add_param_t *params) +static void __invoke_cb_subscription_add(swclt_store_t *store, const blade_netcast_rqu_t *rqu, const blade_netcast_subscription_add_param_t *params) { swclt_store_cb_subscription_add_t cb; ks_log(KS_LOG_DEBUG, "Looking up subscription add handler for method: %s", BLADE_NETCAST_CMD_SUBSCRIPTION_ADD); - ks_hash_read_lock(ctx->callbacks); - cb = (swclt_store_cb_subscription_add_t)ks_hash_search(ctx->callbacks, + ks_hash_read_lock(store->callbacks); + cb = (swclt_store_cb_subscription_add_t)ks_hash_search(store->callbacks, (const void *)BLADE_NETCAST_CMD_SUBSCRIPTION_ADD, KS_UNLOCKED); - ks_hash_read_unlock(ctx->callbacks); + ks_hash_read_unlock(store->callbacks); if (cb) { ks_log(KS_LOG_DEBUG, "Invoking callback for node store add"); - cb(__get_sess_from_store_ctx(ctx), rqu, params); + cb(store->sess, rqu, params); } else { ks_log(KS_LOG_DEBUG, "No callback registered for subscription add method: %s", BLADE_NETCAST_CMD_SUBSCRIPTION_ADD); } } -static ks_status_t __add_cb_subscription_remove(swclt_store_ctx_t *ctx, swclt_store_cb_subscription_remove_t cb) +static ks_status_t __add_cb_subscription_remove(swclt_store_t *store, swclt_store_cb_subscription_remove_t cb) { - return ks_hash_insert(ctx->callbacks, (const void *)BLADE_NETCAST_CMD_SUBSCRIPTION_REMOVE, (void *)cb); + return ks_hash_insert(store->callbacks, (const void *)BLADE_NETCAST_CMD_SUBSCRIPTION_REMOVE, (void *)cb); } -static void __invoke_cb_subscription_remove(swclt_store_ctx_t *ctx, const blade_netcast_rqu_t *rqu, const blade_netcast_subscription_remove_param_t *params) +static void __invoke_cb_subscription_remove(swclt_store_t *store, const blade_netcast_rqu_t *rqu, const blade_netcast_subscription_remove_param_t *params) { swclt_store_cb_subscription_remove_t cb; - ks_hash_read_lock(ctx->callbacks); - cb = (swclt_store_cb_subscription_remove_t)ks_hash_search(ctx->callbacks, + ks_hash_read_lock(store->callbacks); + cb = (swclt_store_cb_subscription_remove_t)ks_hash_search(store->callbacks, (const void *)BLADE_NETCAST_CMD_SUBSCRIPTION_REMOVE, KS_UNLOCKED); - ks_hash_read_unlock(ctx->callbacks); + ks_hash_read_unlock(store->callbacks); - if (cb) cb(__get_sess_from_store_ctx(ctx), rqu, params); + if (cb) cb(store->sess, rqu, params); } static void __destroy_protocol(void *protocol) @@ -337,18 +327,18 @@ static void __destroy_protocol(void *protocol) BLADE_PROTOCOL_DESTROY(&proto); } -static ks_status_t __add_protocol_obj(swclt_store_ctx_t *ctx, ks_json_t *obj) +static ks_status_t __add_protocol_obj(swclt_store_t *store, ks_json_t *obj) { blade_protocol_t *protocol; ks_status_t status; - if (status = BLADE_PROTOCOL_PARSE(ctx->base.pool, obj, &protocol)) { + if (status = BLADE_PROTOCOL_PARSE(store->pool, obj, &protocol)) { ks_log(KS_LOG_ERROR, "Failed to parse protocol: %d", status); return status; } - if (status = ks_hash_insert(ctx->protocols, protocol->name, protocol)) { + if (status = ks_hash_insert(store->protocols, protocol->name, protocol)) { ks_log(KS_LOG_ERROR, "Failed to insert protocol: %d", status); BLADE_PROTOCOL_DESTROY(&protocol); return status; @@ -357,7 +347,7 @@ static ks_status_t __add_protocol_obj(swclt_store_ctx_t *ctx, ks_json_t *obj) return KS_STATUS_SUCCESS; } -static ks_status_t __add_protocol_uncertified_obj(swclt_store_ctx_t *ctx, ks_json_t *obj) +static ks_status_t __add_protocol_uncertified_obj(swclt_store_t *store, ks_json_t *obj) { char *key; ks_status_t status; @@ -367,10 +357,11 @@ static ks_status_t __add_protocol_uncertified_obj(swclt_store_ctx_t *ctx, ks_jso ks_log(KS_LOG_ERROR, "Failed to parse protocol uncertified: %d", status); return status; } + const char *str; + ks_json_value_string(obj, &str); + key = ks_pstrdup(ks_pool_get(store->protocols_uncertified), str); - key = ks_pstrdup(ks_pool_get(ctx->protocols_uncertified), ks_json_value_string(obj)); - - if (status = ks_hash_insert(ctx->protocols_uncertified, key, (void *)KS_TRUE)) { + if (status = ks_hash_insert(store->protocols_uncertified, key, (void *)KS_TRUE)) { ks_log(KS_LOG_ERROR, "Failed to insert protocol: %d", status); ks_pool_free(&key); return status; @@ -385,17 +376,17 @@ static void __destroy_node(void *node) BLADE_NODE_DESTROY(&n); } -static ks_status_t __add_route_obj(swclt_store_ctx_t *ctx, ks_json_t *obj) +static ks_status_t __add_route_obj(swclt_store_t *store, ks_json_t *obj) { blade_node_t *node; ks_status_t status; - if (status = BLADE_NODE_PARSE(ctx->base.pool, obj, &node)) { + if (status = BLADE_NODE_PARSE(store->pool, obj, &node)) { ks_log(KS_LOG_ERROR, "Failed to parse route: %d", status); return status; } - if (status = ks_hash_insert(ctx->routes, ks_pstrdup(ctx->base.pool, node->nodeid), node)) { + if (status = ks_hash_insert(store->routes, ks_pstrdup(store->pool, node->nodeid), node)) { ks_log(KS_LOG_ERROR, "Failed to insert route: %d", status); BLADE_NODE_DESTROY(&node); return status; @@ -404,10 +395,10 @@ static ks_status_t __add_route_obj(swclt_store_ctx_t *ctx, ks_json_t *obj) return KS_STATUS_SUCCESS; } -static ks_status_t __lookup_protocol(swclt_store_ctx_t *ctx, const char *name, blade_protocol_t **protocol) +static ks_status_t __lookup_protocol(swclt_store_t *store, const char *name, blade_protocol_t **protocol) { blade_protocol_t *found_protocol = (blade_protocol_t *) - ks_hash_search(ctx->protocols, name, KS_UNLOCKED); + ks_hash_search(store->protocols, name, KS_UNLOCKED); if (found_protocol) { if (protocol) @@ -418,20 +409,20 @@ static ks_status_t __lookup_protocol(swclt_store_ctx_t *ctx, const char *name, b return KS_STATUS_NOT_FOUND; } -static ks_status_t __lookup_protocol_uncertified(swclt_store_ctx_t *ctx, const char *name) +static ks_status_t __lookup_protocol_uncertified(swclt_store_t *store, const char *name) { - if (ks_hash_search(ctx->protocols_uncertified, name, KS_UNLOCKED) == NULL) return KS_STATUS_NOT_FOUND; + if (ks_hash_search(store->protocols_uncertified, name, KS_UNLOCKED) == NULL) return KS_STATUS_NOT_FOUND; return KS_STATUS_SUCCESS; } -static void __remove_identities_by_nodeid(swclt_store_ctx_t *ctx, const char *nodeid) +static void __remove_identities_by_nodeid(swclt_store_t *store, const char *nodeid) { ks_hash_iterator_t *itt; ks_hash_t *cleanup = NULL; - ks_hash_write_lock(ctx->identities); + ks_hash_write_lock(store->identities); // iterate all identities - for (itt = ks_hash_first(ctx->identities, KS_UNLOCKED); itt; itt = ks_hash_next(&itt)) { + for (itt = ks_hash_first(store->identities, KS_UNLOCKED); itt; itt = ks_hash_next(&itt)) { const char *val; const char *key; @@ -440,7 +431,7 @@ static void __remove_identities_by_nodeid(swclt_store_ctx_t *ctx, const char *no if (strcmp(nodeid, val)) continue; if (!cleanup) - ks_hash_create(&cleanup, KS_HASH_MODE_CASE_INSENSITIVE, KS_HASH_FLAG_NOLOCK, ctx->base.pool); + ks_hash_create(&cleanup, KS_HASH_MODE_CASE_INSENSITIVE, KS_HASH_FLAG_NOLOCK, store->pool); ks_log(KS_LOG_INFO, "Removing identity %s from node %s", key, val); @@ -454,15 +445,15 @@ static void __remove_identities_by_nodeid(swclt_store_ctx_t *ctx, const char *no ks_hash_this(itt, (const void **)&key, NULL, (void **)&val); - ks_hash_remove(ctx->identities, (const void *)key); + ks_hash_remove(store->identities, (const void *)key); } ks_hash_destroy(&cleanup); } - ks_hash_write_unlock(ctx->identities); + ks_hash_write_unlock(store->identities); } -static ks_status_t __get_node_identities(swclt_store_ctx_t *ctx, +static ks_status_t __get_node_identities(swclt_store_t *store, const char *nodeid, ks_pool_t *pool, ks_hash_t **identities) @@ -472,9 +463,9 @@ static ks_status_t __get_node_identities(swclt_store_ctx_t *ctx, ks_hash_create(identities, KS_HASH_MODE_CASE_INSENSITIVE, KS_HASH_FLAG_FREE_KEY, pool); - ks_hash_read_lock(ctx->identities); + ks_hash_read_lock(store->identities); // iterate all identities - for (itt = ks_hash_first(ctx->identities, KS_UNLOCKED); itt; itt = ks_hash_next(&itt)) { + for (itt = ks_hash_first(store->identities, KS_UNLOCKED); itt; itt = ks_hash_next(&itt)) { const char *val; const char *key; @@ -485,10 +476,10 @@ static ks_status_t __get_node_identities(swclt_store_ctx_t *ctx, ks_hash_insert(*identities, ks_pstrdup(pool, key), (void *)KS_TRUE); } - ks_hash_read_unlock(ctx->identities); + ks_hash_read_unlock(store->identities); } -static void __remove_provider_from_protocols(swclt_store_ctx_t *ctx, const char *nodeid) +static void __remove_provider_from_protocols(swclt_store_t *store, const char *nodeid) { ks_hash_iterator_t *itt; ks_hash_t *cleanup = NULL; @@ -496,9 +487,9 @@ static void __remove_provider_from_protocols(swclt_store_ctx_t *ctx, const char ks_log(KS_LOG_INFO, "Request to remove provider %s", nodeid); - ks_hash_write_lock(ctx->protocols); + ks_hash_write_lock(store->protocols); // iterate all protocols - for (itt = ks_hash_first(ctx->protocols, KS_UNLOCKED); itt; ) { + for (itt = ks_hash_first(store->protocols, KS_UNLOCKED); itt; ) { blade_protocol_t *protocol; const char *key; int32_t index = 0; @@ -516,7 +507,7 @@ static void __remove_provider_from_protocols(swclt_store_ctx_t *ctx, const char ks_bool_t match = KS_FALSE; // parse provider - ks_assertd(!BLADE_PROVIDER_PARSE(ctx->base.pool, entry, &provider)); + ks_assertd(!BLADE_PROVIDER_PARSE(store->pool, entry, &provider)); ks_log(KS_LOG_INFO, "Iterating provider %s", provider->nodeid); @@ -533,7 +524,7 @@ static void __remove_provider_from_protocols(swclt_store_ctx_t *ctx, const char // Lazily init the cleanup hash which we'll walk if we decide to remove this at the end if (!cleanup) - ks_hash_create(&cleanup, KS_HASH_MODE_CASE_INSENSITIVE, KS_HASH_FLAG_NOLOCK, ctx->base.pool); + ks_hash_create(&cleanup, KS_HASH_MODE_CASE_INSENSITIVE, KS_HASH_FLAG_NOLOCK, store->pool); // Just add the protocol no dupe her needed as the protocol and name are one item, we'll delete // the protocol which will free the key too when we iterate the cleanup hash below @@ -560,17 +551,18 @@ static void __remove_provider_from_protocols(swclt_store_ctx_t *ctx, const char ks_hash_this(itt, (const void **)&key, NULL, (void **)&protocol); itt = ks_hash_next(&itt); - - ks_hash_remove(ctx->protocols, (const void *)key); - __invoke_cb_protocol_remove(ctx, protocol->name); + char *protocol_name = ks_pstrdup(store->pool, protocol->name); + ks_hash_remove(store->protocols, (const void *)key); // now destroy it... + __invoke_cb_protocol_remove(store, protocol_name); + ks_pool_free(&protocol_name); } ks_hash_destroy(&cleanup); } - ks_hash_write_unlock(ctx->protocols); + ks_hash_write_unlock(store->protocols); } -static ks_status_t __select_random_protocol_provider(swclt_store_ctx_t *ctx, const char *name, ks_pool_t *pool, char **providerid) +static ks_status_t __select_random_protocol_provider(swclt_store_t *store, const char *name, ks_pool_t *pool, char **providerid) { blade_protocol_t *protocol = NULL; int32_t count = 0; @@ -581,10 +573,10 @@ static ks_status_t __select_random_protocol_provider(swclt_store_ctx_t *ctx, con *providerid = NULL; - ks_hash_write_lock(ctx->protocols); + ks_hash_write_lock(store->protocols); // find the protocol - if (status = __lookup_protocol(ctx, name, &protocol)) + if (status = __lookup_protocol(store, name, &protocol)) goto done; // get provider count @@ -597,7 +589,7 @@ static ks_status_t __select_random_protocol_provider(swclt_store_ctx_t *ctx, con index = (int32_t)(rand() % ks_json_get_array_size(protocol->providers)); // parse provider entry = ks_json_get_array_item(protocol->providers, index); - ks_assertd(!BLADE_PROVIDER_PARSE(ctx->base.pool, entry, &provider)); + ks_assertd(!BLADE_PROVIDER_PARSE(store->pool, entry, &provider)); // copy provider id *providerid = ks_pstrdup(pool, provider->nodeid); @@ -606,37 +598,35 @@ static ks_status_t __select_random_protocol_provider(swclt_store_ctx_t *ctx, con BLADE_PROVIDER_DESTROY(&provider); done: - ks_hash_write_unlock(ctx->protocols); + ks_hash_write_unlock(store->protocols); return status; } -static ks_status_t __get_protocols(swclt_store_ctx_t *ctx, - ks_pool_t *pool, +static ks_status_t __get_protocols(swclt_store_t *store, ks_json_t **protocols) { ks_status_t status = KS_STATUS_SUCCESS; - *protocols = ks_json_pcreate_array(pool); + *protocols = ks_json_create_array(); - ks_hash_read_lock(ctx->protocols); + ks_hash_read_lock(store->protocols); - for (ks_hash_iterator_t *it = ks_hash_first(ctx->protocols, KS_UNLOCKED); it; it = ks_hash_next(&it)) { + for (ks_hash_iterator_t *it = ks_hash_first(store->protocols, KS_UNLOCKED); it; it = ks_hash_next(&it)) { const char *key = NULL; blade_protocol_t *proto = NULL; ks_hash_this(it, (const void **)&key, NULL, (void **)&proto); - ks_json_padd_string_to_array(pool, *protocols, key); + ks_json_add_string_to_array(*protocols, key); } - ks_hash_read_unlock(ctx->protocols); + ks_hash_read_unlock(store->protocols); return status; } -static ks_status_t __get_protocol_providers(swclt_store_ctx_t *ctx, +static ks_status_t __get_protocol_providers(swclt_store_t *store, const char *name, - ks_pool_t *pool, ks_json_t **providers) { ks_status_t status = KS_STATUS_SUCCESS; @@ -644,9 +634,9 @@ static ks_status_t __get_protocol_providers(swclt_store_ctx_t *ctx, *providers = NULL; - ks_hash_read_lock(ctx->protocols); + ks_hash_read_lock(store->protocols); - if (status = __lookup_protocol(ctx, name, &protocol)) + if (status = __lookup_protocol(store, name, &protocol)) goto done; if (ks_json_get_array_size(protocol->providers) == 0) { @@ -654,24 +644,24 @@ static ks_status_t __get_protocol_providers(swclt_store_ctx_t *ctx, goto done; } - *providers = ks_json_pduplicate(pool, protocol->providers, KS_TRUE); + *providers = ks_json_duplicate(protocol->providers, KS_TRUE); done: - ks_hash_read_unlock(ctx->protocols); + ks_hash_read_unlock(store->protocols); return status; } -static ks_status_t __add_authority_obj(swclt_store_ctx_t *ctx, ks_json_t *obj) +static ks_status_t __add_authority_obj(swclt_store_t *store, ks_json_t *obj) { blade_netcast_authority_add_param_t *params; ks_status_t status; - if (status = BLADE_NETCAST_AUTHORITY_ADD_PARAM_PARSE(ctx->base.pool, obj, ¶ms)) { + if (status = BLADE_NETCAST_AUTHORITY_ADD_PARAM_PARSE(store->pool, obj, ¶ms)) { ks_log(KS_LOG_ERROR, "Failed to parse authority: %d", status); return status; } - if (status = ks_hash_insert(ctx->authorities, ks_pstrdup(ctx->base.pool, params->nodeid), (void *)KS_TRUE)) { + if (status = ks_hash_insert(store->authorities, ks_pstrdup(store->pool, params->nodeid), (void *)KS_TRUE)) { ks_log(KS_LOG_ERROR, "Failed to insert authority: %d", status); BLADE_NETCAST_AUTHORITY_ADD_PARAM_DESTROY(¶ms); return status; @@ -683,24 +673,24 @@ static ks_status_t __add_authority_obj(swclt_store_ctx_t *ctx, ks_json_t *obj) } // Protocol add/remove for uncertified clients only -static ks_status_t __update_protocol_add(swclt_store_ctx_t *ctx, const blade_netcast_rqu_t *netcast_rqu) +static ks_status_t __update_protocol_add(swclt_store_t *store, const blade_netcast_rqu_t *netcast_rqu) { blade_netcast_protocol_add_param_t *params = NULL; ks_status_t status = KS_STATUS_SUCCESS; - if (status = BLADE_NETCAST_PROTOCOL_ADD_PARAM_PARSE(ctx->base.pool, netcast_rqu->params, ¶ms)) + if (status = BLADE_NETCAST_PROTOCOL_ADD_PARAM_PARSE(store->pool, netcast_rqu->params, ¶ms)) return status; ks_log(KS_LOG_INFO, "Request to add new protocol %s", params->protocol); /* Lookup the protocol */ - if (status = __lookup_protocol_uncertified(ctx, params->protocol)) { - char *key = ks_pstrdup(ks_pool_get(ctx->protocols_uncertified), params->protocol); + if (status = __lookup_protocol_uncertified(store, params->protocol)) { + char *key = ks_pstrdup(ks_pool_get(store->protocols_uncertified), params->protocol); ks_log(KS_LOG_INFO, "Protocol %s does not exist yet, adding new entry", params->protocol); /* And add it */ - if (status = ks_hash_insert(ctx->protocols_uncertified, key, (void *)KS_TRUE)) { + if (status = ks_hash_insert(store->protocols_uncertified, key, (void *)KS_TRUE)) { ks_pool_free(&key); BLADE_NETCAST_PROTOCOL_ADD_PARAM_DESTROY(¶ms); return status; @@ -708,7 +698,7 @@ static ks_status_t __update_protocol_add(swclt_store_ctx_t *ctx, const blade_net ks_log(KS_LOG_INFO, "Protocol %s added", params->protocol); - __invoke_cb_protocol_add(ctx, params->protocol); + __invoke_cb_protocol_add(store, params->protocol); return status; @@ -721,20 +711,20 @@ static ks_status_t __update_protocol_add(swclt_store_ctx_t *ctx, const blade_net return status; } -static ks_status_t __update_protocol_remove(swclt_store_ctx_t *ctx, blade_netcast_rqu_t *netcast_rqu) +static ks_status_t __update_protocol_remove(swclt_store_t *store, blade_netcast_rqu_t *netcast_rqu) { blade_netcast_protocol_remove_param_t *params = NULL; ks_status_t status = KS_STATUS_SUCCESS; ks_bool_t match = KS_FALSE; - if (status = BLADE_NETCAST_PROTOCOL_REMOVE_PARAM_PARSE(ctx->base.pool, netcast_rqu->params, ¶ms)) + if (status = BLADE_NETCAST_PROTOCOL_REMOVE_PARAM_PARSE(store->pool, netcast_rqu->params, ¶ms)) return status; - match = ks_hash_remove(ctx->protocols_uncertified, (const void *)params->protocol) != NULL; + match = ks_hash_remove(store->protocols_uncertified, (const void *)params->protocol) != NULL; done: - if (match) __invoke_cb_protocol_remove(ctx, params->protocol); + if (match) __invoke_cb_protocol_remove(store, params->protocol); BLADE_NETCAST_PROTOCOL_REMOVE_PARAM_DESTROY(¶ms); @@ -742,56 +732,64 @@ static ks_status_t __update_protocol_remove(swclt_store_ctx_t *ctx, blade_netcas } // Provider add/remove -static ks_status_t __update_protocol_provider_add(swclt_store_ctx_t *ctx, const blade_netcast_rqu_t *netcast_rqu) +static ks_status_t __update_protocol_provider_add(swclt_store_t *store, const blade_netcast_rqu_t *netcast_rqu) { blade_netcast_protocol_provider_add_param_t *params = NULL; blade_protocol_t *protocol = NULL; ks_status_t status = KS_STATUS_SUCCESS; ks_json_t *provider_data = NULL; - if (status = BLADE_NETCAST_PROTOCOL_PROVIDER_ADD_PARAM_PARSE(ctx->base.pool, netcast_rqu->params, ¶ms)) + if (status = BLADE_NETCAST_PROTOCOL_PROVIDER_ADD_PARAM_PARSE(store->pool, netcast_rqu->params, ¶ms)) return status; ks_log(KS_LOG_INFO, "Request to add new provider %s for protocol %s", params->nodeid, params->protocol); - ks_hash_write_lock(ctx->protocols); + ks_hash_write_lock(store->protocols); /* Lookup the protocol */ - if (status = __lookup_protocol(ctx, params->protocol, &protocol)) { + if (status = __lookup_protocol(store, params->protocol, &protocol)) { ks_log(KS_LOG_INFO, "Protocol %s does not exist yet, adding new entry", params->protocol); /* Gotta add a new one */ - if (!(protocol = ks_pool_alloc(ctx->base.pool, sizeof(blade_protocol_t)))) { - ks_hash_write_unlock(ctx->protocols); + if (!(protocol = ks_pool_alloc(store->pool, sizeof(blade_protocol_t)))) { + ks_hash_write_unlock(store->protocols); + BLADE_NETCAST_PROTOCOL_PROVIDER_ADD_PARAM_DESTROY(¶ms); return KS_STATUS_NO_MEM; } - protocol->channels = ks_json_pduplicate(ctx->base.pool, params->channels, KS_TRUE); + protocol->channels = ks_json_duplicate(params->channels, KS_TRUE); protocol->default_channel_broadcast_access = params->default_channel_broadcast_access; protocol->default_channel_subscribe_access = params->default_channel_subscribe_access; protocol->default_method_execute_access = params->default_method_execute_access; - protocol->name = ks_pstrdup(ctx->base.pool, params->protocol); + protocol->name = ks_pstrdup(store->pool, params->protocol); if (params->data) { - provider_data = ks_json_pduplicate(ctx->base.pool, params->data, KS_TRUE); + provider_data = ks_json_duplicate(params->data, KS_TRUE); } - if (!(protocol->providers = ks_json_pcreate_array_inline(ctx->base.pool, 1, - BLADE_PROVIDER_MARSHAL(ctx->base.pool, - &(blade_provider_t){params->nodeid, NULL, params->rank, provider_data})))) { + + if (!(protocol->providers = ks_json_create_array())) { + ks_json_delete(&protocol->channels); ks_pool_free(&protocol); - ks_pool_free(&provider_data); + ks_json_delete(&provider_data); BLADE_NETCAST_PROTOCOL_PROVIDER_ADD_PARAM_DESTROY(¶ms); - ks_hash_write_unlock(ctx->protocols); + ks_hash_write_unlock(store->protocols); return KS_STATUS_NO_MEM; } + ks_json_add_item_to_array(protocol->providers, + BLADE_PROVIDER_MARSHAL( + &(blade_provider_t){params->nodeid, NULL, params->rank, provider_data} + ) + ); /* And add it */ - if (status = ks_hash_insert(ctx->protocols, protocol->name, protocol)) { + if (status = ks_hash_insert(store->protocols, protocol->name, protocol)) { + ks_json_delete(&protocol->channels); + ks_json_delete(&protocol->providers); ks_pool_free(&protocol); BLADE_NETCAST_PROTOCOL_PROVIDER_ADD_PARAM_DESTROY(¶ms); - ks_hash_write_unlock(ctx->protocols); + ks_hash_write_unlock(store->protocols); return status; } @@ -800,10 +798,12 @@ static ks_status_t __update_protocol_provider_add(swclt_store_ctx_t *ctx, const // @todo come back and fix this, params->protocol is going to be NULL, need to duplicate // the string so it remains valid in params - __invoke_cb_protocol_add(ctx, params->protocol); - __invoke_cb_protocol_provider_add(ctx, netcast_rqu, params); + __invoke_cb_protocol_add(store, params->protocol); + __invoke_cb_protocol_provider_add(store, netcast_rqu, params); - ks_hash_write_unlock(ctx->protocols); + ks_hash_write_unlock(store->protocols); + + BLADE_NETCAST_PROTOCOL_PROVIDER_ADD_PARAM_DESTROY(¶ms); return status; @@ -814,27 +814,23 @@ static ks_status_t __update_protocol_provider_add(swclt_store_ctx_t *ctx, const /* Now add any provider entries to the protocol */ if (params->data) { - provider_data = ks_json_pduplicate(ctx->base.pool, params->data, KS_TRUE); - } - if (!ks_json_add_item_to_array(protocol->providers, - BLADE_PROVIDER_MARSHAL(ctx->base.pool, &(blade_provider_t){params->nodeid, NULL, params->rank, provider_data}))) { - ks_pool_free(&provider_data); - ks_hash_write_unlock(ctx->protocols); - return KS_STATUS_NO_MEM; + provider_data = ks_json_duplicate(params->data, KS_TRUE); } + ks_json_add_item_to_array(protocol->providers, + BLADE_PROVIDER_MARSHAL(&(blade_provider_t){params->nodeid, NULL, params->rank, provider_data})); ks_log(KS_LOG_INFO, "Protocol %s add complete, provider count %lu", protocol->name, ks_json_get_array_size(protocol->providers)); - __invoke_cb_protocol_provider_add(ctx, netcast_rqu, params); + __invoke_cb_protocol_provider_add(store, netcast_rqu, params); BLADE_NETCAST_PROTOCOL_PROVIDER_ADD_PARAM_DESTROY(¶ms); - ks_hash_write_unlock(ctx->protocols); + ks_hash_write_unlock(store->protocols); return status; } -static ks_status_t __update_protocol_provider_remove(swclt_store_ctx_t *ctx, blade_netcast_rqu_t *netcast_rqu) +static ks_status_t __update_protocol_provider_remove(swclt_store_t *store, blade_netcast_rqu_t *netcast_rqu) { blade_netcast_protocol_provider_remove_param_t *params = NULL; blade_protocol_t *protocol = NULL; @@ -844,12 +840,12 @@ static ks_status_t __update_protocol_provider_remove(swclt_store_ctx_t *ctx, bla ks_bool_t match = KS_FALSE; ks_bool_t cleanup = KS_FALSE; - if (status = BLADE_NETCAST_PROTOCOL_PROVIDER_REMOVE_PARAM_PARSE(ctx->base.pool, netcast_rqu->params, ¶ms)) + if (status = BLADE_NETCAST_PROTOCOL_PROVIDER_REMOVE_PARAM_PARSE(store->pool, netcast_rqu->params, ¶ms)) return status; - ks_hash_write_lock(ctx->protocols); + ks_hash_write_lock(store->protocols); - if (status = __lookup_protocol(ctx, params->protocol, &protocol)) { + if (status = __lookup_protocol(store, params->protocol, &protocol)) { ks_log(KS_LOG_WARNING, "Received protocol provider remove for protocol '%s' which does not exist", params->protocol); status = KS_STATUS_SUCCESS; goto done; @@ -860,7 +856,7 @@ static ks_status_t __update_protocol_provider_remove(swclt_store_ctx_t *ctx, bla blade_provider_t *provider; // parse provider - ks_assertd(!BLADE_PROVIDER_PARSE(ctx->base.pool, entry, &provider)); + ks_assertd(!BLADE_PROVIDER_PARSE(store->pool, entry, &provider)); if (!strcmp(provider->nodeid, params->nodeid)) { match = KS_TRUE; @@ -880,14 +876,16 @@ static ks_status_t __update_protocol_provider_remove(swclt_store_ctx_t *ctx, bla if (cleanup) { // cleanup protocol if no providers left - ks_hash_remove(ctx->protocols, (const void *)protocol->name); - __invoke_cb_protocol_remove(ctx, protocol->name); + char *protocol_name = ks_pstrdup(store->pool, protocol->name); + ks_hash_remove(store->protocols, (const void *)protocol->name); // now destroy it... + __invoke_cb_protocol_remove(store, protocol_name); + ks_pool_free(&protocol_name); } done: - ks_hash_write_unlock(ctx->protocols); + ks_hash_write_unlock(store->protocols); - if (match) __invoke_cb_protocol_provider_remove(ctx, netcast_rqu, params); + if (match) __invoke_cb_protocol_provider_remove(store, netcast_rqu, params); BLADE_NETCAST_PROTOCOL_PROVIDER_REMOVE_PARAM_DESTROY(¶ms); @@ -895,7 +893,7 @@ static ks_status_t __update_protocol_provider_remove(swclt_store_ctx_t *ctx, bla } // Provider rank update -static ks_status_t __update_protocol_provider_rank_update(swclt_store_ctx_t *ctx, const blade_netcast_rqu_t *netcast_rqu) +static ks_status_t __update_protocol_provider_rank_update(swclt_store_t *store, const blade_netcast_rqu_t *netcast_rqu) { blade_netcast_protocol_provider_rank_update_param_t *params = NULL; blade_protocol_t *protocol = NULL; @@ -904,33 +902,33 @@ static ks_status_t __update_protocol_provider_rank_update(swclt_store_ctx_t *ctx ks_status_t status = KS_STATUS_SUCCESS; ks_bool_t found = KS_FALSE; - if (status = BLADE_NETCAST_PROTOCOL_PROVIDER_RANK_UPDATE_PARAM_PARSE(ctx->base.pool, netcast_rqu->params, ¶ms)) + if (status = BLADE_NETCAST_PROTOCOL_PROVIDER_RANK_UPDATE_PARAM_PARSE(store->pool, netcast_rqu->params, ¶ms)) return status; - ks_hash_write_lock(ctx->protocols); + ks_hash_write_lock(store->protocols); ks_log(KS_LOG_INFO, "Request to update rank for provider %s to %d for protocol %s", params->nodeid, params->rank, params->protocol); /* Lookup the protocol */ - if (status = __lookup_protocol(ctx, params->protocol, &protocol)) + if (status = __lookup_protocol(store, params->protocol, &protocol)) goto done; // find provider for (int32_t index = 0; index < ks_json_get_array_size(protocol->providers); ++index) { entry = ks_json_get_array_item(protocol->providers, index); - const char *provider_nodeid = ks_json_get_object_cstr_def(entry, "nodeid", ""); + const char *provider_nodeid = ks_json_get_object_string(entry, "nodeid", ""); if (!strcmp(provider_nodeid, params->nodeid)) { found = KS_TRUE; ks_json_delete_item_from_object(entry, "rank"); - ks_json_padd_number_to_object(ctx->base.pool, entry, "rank", params->rank); + ks_json_add_number_to_object(entry, "rank", params->rank); } } done: - ks_hash_write_unlock(ctx->protocols); + ks_hash_write_unlock(store->protocols); - if (found) __invoke_cb_protocol_provider_rank_update(ctx, netcast_rqu, params); + if (found) __invoke_cb_protocol_provider_rank_update(store, netcast_rqu, params); BLADE_NETCAST_PROTOCOL_PROVIDER_RANK_UPDATE_PARAM_DESTROY(¶ms); @@ -938,7 +936,7 @@ static ks_status_t __update_protocol_provider_rank_update(swclt_store_ctx_t *ctx } // Provider data update -static ks_status_t __update_protocol_provider_data_update(swclt_store_ctx_t *ctx, const blade_netcast_rqu_t *netcast_rqu) +static ks_status_t __update_protocol_provider_data_update(swclt_store_t *store, const blade_netcast_rqu_t *netcast_rqu) { blade_netcast_protocol_provider_data_update_param_t *params = NULL; blade_protocol_t *protocol = NULL; @@ -947,32 +945,32 @@ static ks_status_t __update_protocol_provider_data_update(swclt_store_ctx_t *ctx ks_status_t status = KS_STATUS_SUCCESS; ks_bool_t found = KS_FALSE; - if (status = BLADE_NETCAST_PROTOCOL_PROVIDER_DATA_UPDATE_PARAM_PARSE(ctx->base.pool, netcast_rqu->params, ¶ms)) + if (status = BLADE_NETCAST_PROTOCOL_PROVIDER_DATA_UPDATE_PARAM_PARSE(store->pool, netcast_rqu->params, ¶ms)) return status; - ks_hash_write_lock(ctx->protocols); + ks_hash_write_lock(store->protocols); ks_log(KS_LOG_INFO, "Request to update data for provider %s for protocol %s", params->nodeid, params->protocol); /* Lookup the protocol */ - if (status = __lookup_protocol(ctx, params->protocol, &protocol)) + if (status = __lookup_protocol(store, params->protocol, &protocol)) goto done; // find provider for (int32_t index = 0; index < ks_json_get_array_size(protocol->providers); ++index) { entry = ks_json_get_array_item(protocol->providers, index); - const char *provider_nodeid = ks_json_get_object_cstr_def(entry, "nodeid", ""); + const char *provider_nodeid = ks_json_get_object_string(entry, "nodeid", ""); if (!strcmp(provider_nodeid, params->nodeid)) { found = KS_TRUE; ks_json_delete_item_from_object(entry, "data"); - ks_json_add_item_to_object(entry, "data", ks_json_pduplicate(ctx->base.pool, params->data, KS_TRUE)); + ks_json_add_item_to_object(entry, "data", ks_json_duplicate(params->data, KS_TRUE)); } } done: - ks_hash_write_unlock(ctx->protocols); + ks_hash_write_unlock(store->protocols); - if (found) __invoke_cb_protocol_provider_data_update(ctx, netcast_rqu, params); + if (found) __invoke_cb_protocol_provider_data_update(store, netcast_rqu, params); BLADE_NETCAST_PROTOCOL_PROVIDER_DATA_UPDATE_PARAM_DESTROY(¶ms); @@ -981,46 +979,46 @@ static ks_status_t __update_protocol_provider_data_update(swclt_store_ctx_t *ctx // Route add/remove -static ks_status_t __update_route_add(swclt_store_ctx_t *ctx, blade_netcast_rqu_t *netcast_rqu) +static ks_status_t __update_route_add(swclt_store_t *store, blade_netcast_rqu_t *netcast_rqu) { blade_netcast_route_add_param_t *params; ks_status_t status; - if (status = BLADE_NETCAST_ROUTE_ADD_PARAM_PARSE(ctx->base.pool, netcast_rqu->params, ¶ms)) + if (status = BLADE_NETCAST_ROUTE_ADD_PARAM_PARSE(store->pool, netcast_rqu->params, ¶ms)) return status; - status = __add_route_obj(ctx, netcast_rqu->params); + status = __add_route_obj(store, netcast_rqu->params); - __invoke_cb_route_add(ctx, netcast_rqu, params); + __invoke_cb_route_add(store, netcast_rqu, params); BLADE_NETCAST_ROUTE_ADD_PARAM_DESTROY(¶ms); return status; } -static ks_status_t __update_route_remove(swclt_store_ctx_t *ctx, blade_netcast_rqu_t *netcast_rqu) +static ks_status_t __update_route_remove(swclt_store_t *store, blade_netcast_rqu_t *netcast_rqu) { blade_netcast_route_remove_param_t *params; ks_status_t status; - if (status = BLADE_NETCAST_ROUTE_REMOVE_PARAM_PARSE(ctx->base.pool, netcast_rqu->params, ¶ms)) + if (status = BLADE_NETCAST_ROUTE_REMOVE_PARAM_PARSE(store->pool, netcast_rqu->params, ¶ms)) return status; - __invoke_cb_route_remove(ctx, netcast_rqu, params); + __invoke_cb_route_remove(store, netcast_rqu, params); /* Remove the node from the hash */ - ks_hash_remove(ctx->routes, params->nodeid); + ks_hash_remove(store->routes, params->nodeid); /* Remove the identities that map to the nodeid */ - __remove_identities_by_nodeid(ctx, params->nodeid); + __remove_identities_by_nodeid(store, params->nodeid); /* Now we have to rummage through the protocols hash and remove any protocols hosted by this node */ - __remove_provider_from_protocols(ctx, params->nodeid); + __remove_provider_from_protocols(store, params->nodeid); // @todo purge subscribers with the given nodeid - ks_hash_remove(ctx->authorities, params->nodeid); - //ks_hash_remove(ctx->accesses, params->nodeid); + ks_hash_remove(store->authorities, params->nodeid); + //ks_hash_remove(store->accesses, params->nodeid); /* Done with the request */ BLADE_NETCAST_ROUTE_REMOVE_PARAM_DESTROY(¶ms); @@ -1029,20 +1027,20 @@ static ks_status_t __update_route_remove(swclt_store_ctx_t *ctx, blade_netcast_r } // Identity add/remove -static ks_status_t __update_identity_add(swclt_store_ctx_t *ctx, blade_netcast_rqu_t *netcast_rqu) +static ks_status_t __update_identity_add(swclt_store_t *store, blade_netcast_rqu_t *netcast_rqu) { blade_netcast_identity_add_param_t *params; ks_status_t status; - if (status = BLADE_NETCAST_IDENTITY_ADD_PARAM_PARSE(ctx->base.pool, netcast_rqu->params, ¶ms)) + if (status = BLADE_NETCAST_IDENTITY_ADD_PARAM_PARSE(store->pool, netcast_rqu->params, ¶ms)) return status; - if (status = ks_hash_insert(ctx->identities, ks_pstrdup(ctx->base.pool, params->identity), ks_pstrdup(ctx->base.pool, params->nodeid))) { + if (status = ks_hash_insert(store->identities, ks_pstrdup(store->pool, params->identity), ks_pstrdup(store->pool, params->nodeid))) { ks_log(KS_LOG_ERROR, "Failed to insert identity: %d", status); goto done; } - __invoke_cb_identity_add(ctx, netcast_rqu, params); + __invoke_cb_identity_add(store, netcast_rqu, params); done: BLADE_NETCAST_IDENTITY_ADD_PARAM_DESTROY(¶ms); @@ -1050,27 +1048,27 @@ static ks_status_t __update_identity_add(swclt_store_ctx_t *ctx, blade_netcast_r return status; } -static ks_status_t __update_identity_remove(swclt_store_ctx_t *ctx, blade_netcast_rqu_t *netcast_rqu) +static ks_status_t __update_identity_remove(swclt_store_t *store, blade_netcast_rqu_t *netcast_rqu) { blade_netcast_identity_remove_param_t *params; ks_status_t status; const char *nodeid = NULL; - if (status = BLADE_NETCAST_IDENTITY_REMOVE_PARAM_PARSE(ctx->base.pool, netcast_rqu->params, ¶ms)) + if (status = BLADE_NETCAST_IDENTITY_REMOVE_PARAM_PARSE(store->pool, netcast_rqu->params, ¶ms)) return status; - ks_hash_write_lock(ctx->identities); + ks_hash_write_lock(store->identities); /* Make sure the identity is owned by the right nodeid */ - nodeid = (const char *)ks_hash_search(ctx->identities, params->identity, KS_UNLOCKED); + nodeid = (const char *)ks_hash_search(store->identities, params->identity, KS_UNLOCKED); if (nodeid && !strcmp(nodeid, params->nodeid)) { /* Remove the identity from the hash */ - __invoke_cb_identity_remove(ctx, netcast_rqu, params); - ks_hash_remove(ctx->identities, params->identity); + __invoke_cb_identity_remove(store, netcast_rqu, params); + ks_hash_remove(store->identities, params->identity); } - ks_hash_write_unlock(ctx->identities); + ks_hash_write_unlock(store->identities); /* Done with the request */ BLADE_NETCAST_IDENTITY_REMOVE_PARAM_DESTROY(¶ms); @@ -1079,47 +1077,47 @@ static ks_status_t __update_identity_remove(swclt_store_ctx_t *ctx, blade_netcas } // Channel add/remove -static ks_status_t __update_channel_add(swclt_store_ctx_t *ctx, blade_netcast_rqu_t *netcast_rqu) +static ks_status_t __update_channel_add(swclt_store_t *store, blade_netcast_rqu_t *netcast_rqu) { /* @@ TODO */ return KS_STATUS_SUCCESS; } -static ks_status_t __update_channel_remove(swclt_store_ctx_t *ctx, blade_netcast_rqu_t *netcast_rqu) +static ks_status_t __update_channel_remove(swclt_store_t *store, blade_netcast_rqu_t *netcast_rqu) { /* @@ TODO */ return KS_STATUS_SUCCESS; } // Subscription add/remove -static ks_status_t __update_subscription_add(swclt_store_ctx_t *ctx, blade_netcast_rqu_t *netcast_rqu) +static ks_status_t __update_subscription_add(swclt_store_t *store, blade_netcast_rqu_t *netcast_rqu) { blade_netcast_subscription_add_param_t *params; ks_status_t status; - if (status = BLADE_NETCAST_SUBSCRIPTION_ADD_PARAM_PARSE(ctx->base.pool, netcast_rqu->params, ¶ms)) + if (status = BLADE_NETCAST_SUBSCRIPTION_ADD_PARAM_PARSE(store->pool, netcast_rqu->params, ¶ms)) return status; - /* @TODO add subscription to ctx->subscriptions */ + /* @TODO add subscription to store->subscriptions */ - __invoke_cb_subscription_add(ctx, netcast_rqu, params); + __invoke_cb_subscription_add(store, netcast_rqu, params); BLADE_NETCAST_SUBSCRIPTION_ADD_PARAM_DESTROY(¶ms); return status; } -static ks_status_t __update_subscription_remove(swclt_store_ctx_t *ctx, blade_netcast_rqu_t *netcast_rqu) +static ks_status_t __update_subscription_remove(swclt_store_t *store, blade_netcast_rqu_t *netcast_rqu) { blade_netcast_subscription_remove_param_t *params; ks_status_t status; - if (status = BLADE_NETCAST_SUBSCRIPTION_REMOVE_PARAM_PARSE(ctx->base.pool, netcast_rqu->params, ¶ms)) + if (status = BLADE_NETCAST_SUBSCRIPTION_REMOVE_PARAM_PARSE(store->pool, netcast_rqu->params, ¶ms)) return status; - /* @TODO Remove the subscription from ctx->subscriptions */ + /* @TODO Remove the subscription from store->subscriptions */ - __invoke_cb_subscription_remove(ctx, netcast_rqu, params); + __invoke_cb_subscription_remove(store, netcast_rqu, params); /* Done with the request */ BLADE_NETCAST_SUBSCRIPTION_REMOVE_PARAM_DESTROY(¶ms); @@ -1128,48 +1126,48 @@ static ks_status_t __update_subscription_remove(swclt_store_ctx_t *ctx, blade_ne } // Authorization add/remove -static ks_status_t __update_authorization_add(swclt_store_ctx_t *ctx, blade_netcast_rqu_t *netcast_rqu) +static ks_status_t __update_authorization_add(swclt_store_t *store, blade_netcast_rqu_t *netcast_rqu) { /* @@ TODO */ return KS_STATUS_SUCCESS; } -static ks_status_t __update_authorization_remove(swclt_store_ctx_t *ctx, blade_netcast_rqu_t *netcast_rqu) +static ks_status_t __update_authorization_remove(swclt_store_t *store, blade_netcast_rqu_t *netcast_rqu) { /* @@ TODO */ return KS_STATUS_SUCCESS; } // Authority add/remove -static ks_status_t __update_authority_add(swclt_store_ctx_t *ctx, blade_netcast_rqu_t *netcast_rqu) +static ks_status_t __update_authority_add(swclt_store_t *store, blade_netcast_rqu_t *netcast_rqu) { blade_netcast_authority_add_param_t *params; ks_status_t status; - if (status = BLADE_NETCAST_AUTHORITY_ADD_PARAM_PARSE(ctx->base.pool, netcast_rqu->params, ¶ms)) + if (status = BLADE_NETCAST_AUTHORITY_ADD_PARAM_PARSE(store->pool, netcast_rqu->params, ¶ms)) return status; - status = __add_authority_obj(ctx, netcast_rqu->params); + status = __add_authority_obj(store, netcast_rqu->params); - __invoke_cb_authority_add(ctx, netcast_rqu, params); + __invoke_cb_authority_add(store, netcast_rqu, params); BLADE_NETCAST_AUTHORITY_ADD_PARAM_DESTROY(¶ms); return status; } -static ks_status_t __update_authority_remove(swclt_store_ctx_t *ctx, blade_netcast_rqu_t *netcast_rqu) +static ks_status_t __update_authority_remove(swclt_store_t *store, blade_netcast_rqu_t *netcast_rqu) { blade_netcast_authority_remove_param_t *params; ks_status_t status; - if (status = BLADE_NETCAST_AUTHORITY_REMOVE_PARAM_PARSE(ctx->base.pool, netcast_rqu->params, ¶ms)) + if (status = BLADE_NETCAST_AUTHORITY_REMOVE_PARAM_PARSE(store->pool, netcast_rqu->params, ¶ms)) return status; /* Remove the node from the hash */ - ks_hash_remove(ctx->authorities, params->nodeid); + ks_hash_remove(store->authorities, params->nodeid); - __invoke_cb_authority_remove(ctx, netcast_rqu, params); + __invoke_cb_authority_remove(store, netcast_rqu, params); /* Done with the request */ BLADE_NETCAST_AUTHORITY_REMOVE_PARAM_DESTROY(¶ms); @@ -1178,64 +1176,64 @@ static ks_status_t __update_authority_remove(swclt_store_ctx_t *ctx, blade_netca } // Access add/remove -static ks_status_t __update_access_add(swclt_store_ctx_t *ctx, blade_netcast_rqu_t *netcast_rqu) +static ks_status_t __update_access_add(swclt_store_t *store, blade_netcast_rqu_t *netcast_rqu) { /* @@ TODO */ return KS_STATUS_SUCCESS; } -static ks_status_t __update_access_remove(swclt_store_ctx_t *ctx, blade_netcast_rqu_t *netcast_rqu) +static ks_status_t __update_access_remove(swclt_store_t *store, blade_netcast_rqu_t *netcast_rqu) { /* @@ TODO */ return KS_STATUS_SUCCESS; } -static ks_status_t __update(swclt_store_ctx_t *ctx, blade_netcast_rqu_t *netcast_rqu) +static ks_status_t __update(swclt_store_t *store, blade_netcast_rqu_t *netcast_rqu) { /* Based on the command, do the appropriate update call */ if (!strcmp(netcast_rqu->command, BLADE_NETCAST_CMD_PROTOCOL_ADD)) - return __update_protocol_add(ctx, netcast_rqu); + return __update_protocol_add(store, netcast_rqu); else if (!strcmp(netcast_rqu->command, BLADE_NETCAST_CMD_PROTOCOL_REMOVE)) - return __update_protocol_remove(ctx, netcast_rqu); + return __update_protocol_remove(store, netcast_rqu); else if (!strcmp(netcast_rqu->command, BLADE_NETCAST_CMD_PROTOCOL_PROVIDER_ADD)) - return __update_protocol_provider_add(ctx, netcast_rqu); + return __update_protocol_provider_add(store, netcast_rqu); else if (!strcmp(netcast_rqu->command, BLADE_NETCAST_CMD_PROTOCOL_PROVIDER_REMOVE)) - return __update_protocol_provider_remove(ctx, netcast_rqu); + return __update_protocol_provider_remove(store, netcast_rqu); else if (!strcmp(netcast_rqu->command, BLADE_NETCAST_CMD_PROTOCOL_PROVIDER_RANK_UPDATE)) - return __update_protocol_provider_rank_update(ctx, netcast_rqu); + return __update_protocol_provider_rank_update(store, netcast_rqu); else if (!strcmp(netcast_rqu->command, BLADE_NETCAST_CMD_PROTOCOL_PROVIDER_DATA_UPDATE)) - return __update_protocol_provider_data_update(ctx, netcast_rqu); + return __update_protocol_provider_data_update(store, netcast_rqu); else if (!strcmp(netcast_rqu->command, BLADE_NETCAST_CMD_ROUTE_ADD)) - return __update_route_add(ctx, netcast_rqu); + return __update_route_add(store, netcast_rqu); else if (!strcmp(netcast_rqu->command, BLADE_NETCAST_CMD_ROUTE_REMOVE)) - return __update_route_remove(ctx, netcast_rqu); + return __update_route_remove(store, netcast_rqu); else if (!strcmp(netcast_rqu->command, BLADE_NETCAST_CMD_IDENTITY_ADD)) - return __update_identity_add(ctx, netcast_rqu); + return __update_identity_add(store, netcast_rqu); else if (!strcmp(netcast_rqu->command, BLADE_NETCAST_CMD_IDENTITY_REMOVE)) - return __update_identity_remove(ctx, netcast_rqu); + return __update_identity_remove(store, netcast_rqu); else if (!strcmp(netcast_rqu->command, BLADE_NETCAST_CMD_SUBSCRIPTION_ADD)) - return __update_subscription_add(ctx, netcast_rqu); + return __update_subscription_add(store, netcast_rqu); else if (!strcmp(netcast_rqu->command, BLADE_NETCAST_CMD_SUBSCRIPTION_REMOVE)) - return __update_subscription_remove(ctx, netcast_rqu); + return __update_subscription_remove(store, netcast_rqu); else if (!strcmp(netcast_rqu->command, BLADE_NETCAST_CMD_AUTHORITY_ADD)) - return __update_authority_add(ctx, netcast_rqu); + return __update_authority_add(store, netcast_rqu); else if (!strcmp(netcast_rqu->command, BLADE_NETCAST_CMD_AUTHORITY_REMOVE)) - return __update_authority_remove(ctx, netcast_rqu); + return __update_authority_remove(store, netcast_rqu); else if (!strcmp(netcast_rqu->command, BLADE_NETCAST_CMD_AUTHORIZATION_ADD)) - return __update_authorization_add(ctx, netcast_rqu); + return __update_authorization_add(store, netcast_rqu); else if (!strcmp(netcast_rqu->command, BLADE_NETCAST_CMD_AUTHORIZATION_REMOVE)) - return __update_authorization_remove(ctx, netcast_rqu); + return __update_authorization_remove(store, netcast_rqu); else if (!strcmp(netcast_rqu->command, BLADE_NETCAST_CMD_ACCESS_ADD)) - return __update_access_add(ctx, netcast_rqu); + return __update_access_add(store, netcast_rqu); else if (!strcmp(netcast_rqu->command, BLADE_NETCAST_CMD_ACCESS_REMOVE)) - return __update_access_remove(ctx, netcast_rqu); + return __update_access_remove(store, netcast_rqu); else { ks_log(KS_LOG_WARNING, "Unknown netcast subcommand: %s", netcast_rqu->command); return KS_STATUS_SUCCESS; } } -static ks_status_t __populate_routes(swclt_store_ctx_t *ctx, blade_connect_rpl_t *connect_rpl) +static ks_status_t __populate_routes(swclt_store_t *store, blade_connect_rpl_t *connect_rpl) { ks_json_t *entry; ks_status_t status; @@ -1244,22 +1242,22 @@ static ks_status_t __populate_routes(swclt_store_ctx_t *ctx, blade_connect_rpl_t /* Walk the routes array and add them */ KS_JSON_ARRAY_FOREACH(entry, connect_rpl->routes) { - if (status = __add_route_obj(ctx, entry)) { + if (status = __add_route_obj(store, entry)) { ks_log(KS_LOG_ERROR, "Failed to populate route: %d", status); return status; } // Identities - nodeid = ks_json_get_object_cstr(entry, "nodeid"); + nodeid = ks_json_get_object_string(entry, "nodeid", NULL); identities = ks_json_get_object_item(entry, "identities"); if (nodeid && identities && ks_json_type_is_array(identities)) { int size = ks_json_get_array_size(identities); for (int index = 0; index < size; ++index) { - const char *identity = ks_json_get_array_cstr(identities, index); + const char *identity = ks_json_get_array_string(identities, index, NULL); if (!identity) continue; - ks_hash_insert(ctx->identities, ks_pstrdup(ctx->base.pool, identity), ks_pstrdup(ctx->base.pool, nodeid)); + ks_hash_insert(store->identities, ks_pstrdup(store->pool, identity), ks_pstrdup(store->pool, nodeid)); } } } @@ -1267,14 +1265,14 @@ static ks_status_t __populate_routes(swclt_store_ctx_t *ctx, blade_connect_rpl_t return KS_STATUS_SUCCESS; } -static ks_status_t __populate_protocols(swclt_store_ctx_t *ctx, blade_connect_rpl_t *connect_rpl) +static ks_status_t __populate_protocols(swclt_store_t *store, blade_connect_rpl_t *connect_rpl) { ks_json_t *entry; ks_status_t status; /* Walk the protocols and add them */ KS_JSON_ARRAY_FOREACH(entry, connect_rpl->protocols) { - if (status = __add_protocol_obj(ctx, entry)) { + if (status = __add_protocol_obj(store, entry)) { ks_log(KS_LOG_ERROR, "Failed to populate protocol: %d", status); return status; } @@ -1289,7 +1287,7 @@ static void __destroy_subscription(void *subscription) BLADE_SUBSCRIPTION_DESTROY(&sub); } -static ks_status_t __populate_subscriptions(swclt_store_ctx_t *ctx, blade_connect_rpl_t *connect_rpl) +static ks_status_t __populate_subscriptions(swclt_store_t *store, blade_connect_rpl_t *connect_rpl) { ks_json_t *entry; ks_status_t status; @@ -1298,13 +1296,13 @@ static ks_status_t __populate_subscriptions(swclt_store_ctx_t *ctx, blade_connec KS_JSON_ARRAY_FOREACH(entry, connect_rpl->subscriptions) { blade_subscription_t *subscription; - if (status = BLADE_SUBSCRIPTION_PARSE(ctx->base.pool, entry, &subscription)) { + if (status = BLADE_SUBSCRIPTION_PARSE(store->pool, entry, &subscription)) { ks_log(KS_LOG_ERROR, "Failed to parse subscription: %d", status); return status; } /* Subscriptions get keyed by a combo key with the channel/protocol */ - if (status = ks_hash_insert(ctx->subscriptions, ks_psprintf(ctx->base.pool, "%s:%s", subscription->protocol, subscription->channel), subscription)) { + if (status = ks_hash_insert(store->subscriptions, ks_psprintf(store->pool, "%s:%s", subscription->protocol, subscription->channel), subscription)) { ks_log(KS_LOG_ERROR, "Failed to insert subscription: %d", status); BLADE_SUBSCRIPTION_DESTROY(&subscription); return status; @@ -1314,34 +1312,36 @@ static ks_status_t __populate_subscriptions(swclt_store_ctx_t *ctx, blade_connec return KS_STATUS_SUCCESS; } -static ks_status_t __populate_authorities(swclt_store_ctx_t *ctx, blade_connect_rpl_t *connect_rpl) +static ks_status_t __populate_authorities(swclt_store_t *store, blade_connect_rpl_t *connect_rpl) { ks_json_t *entry; ks_status_t status; /* Walk the protocols and add them */ KS_JSON_ARRAY_FOREACH(entry, connect_rpl->authorities) { - const char *authority = ks_json_value_string(entry); - authority = ks_pstrdup(ks_pool_get(ctx->authorities), authority); - - if (status = ks_hash_insert(ctx->authorities, authority, (void *)KS_TRUE)) { - ks_log(KS_LOG_ERROR, "Failed to insert authority: %d", status); - ks_pool_free(&authority); - return status; + const char *authority; + if (!ks_json_value_string(entry, &authority)) { + authority = ks_pstrdup(ks_pool_get(store->authorities), authority); + + if (status = ks_hash_insert(store->authorities, authority, (void *)KS_TRUE)) { + ks_log(KS_LOG_ERROR, "Failed to insert authority: %d", status); + ks_pool_free(&authority); + return status; + } } } return KS_STATUS_SUCCESS; } -static ks_status_t __populate_protocols_uncertified(swclt_store_ctx_t *ctx, blade_connect_rpl_t *connect_rpl) +static ks_status_t __populate_protocols_uncertified(swclt_store_t *store, blade_connect_rpl_t *connect_rpl) { ks_json_t *entry; ks_status_t status; /* Walk the protocols and add them */ KS_JSON_ARRAY_FOREACH(entry, connect_rpl->protocols_uncertified) { - if (status = __add_protocol_uncertified_obj(ctx, entry)) { + if (status = __add_protocol_uncertified_obj(store, entry)) { ks_log(KS_LOG_ERROR, "Failed to populate protocol for uncertified client: %d", status); return status; } @@ -1350,340 +1350,321 @@ static ks_status_t __populate_protocols_uncertified(swclt_store_ctx_t *ctx, blad return KS_STATUS_SUCCESS; } - -static void __context_describe(swclt_store_ctx_t *ctx, char *buffer, ks_size_t buffer_len) +SWCLT_DECLARE(char *) swclt_store_describe(swclt_store_t *store) { - snprintf(buffer, buffer_len, "SWCLT Store: protocols %d routes %d authorities: %d subscriptions: %d", - ks_hash_count(ctx->protocols), ks_hash_count(ctx->routes), ks_hash_count(ctx->authorities), ks_hash_count(ctx->subscriptions)); + return ks_psprintf(store->pool, "SWCLT Store: protocols %d routes %d authorities: %d subscriptions: %d", + ks_hash_count(store->protocols), ks_hash_count(store->routes), ks_hash_count(store->authorities), ks_hash_count(store->subscriptions)); } -static void __context_deinit(swclt_store_ctx_t *ctx) +SWCLT_DECLARE(ks_status_t) swclt_store_destroy(swclt_store_t **storeP) { - ks_hash_destroy(&ctx->callbacks); - ks_hash_destroy(&ctx->subscriptions); - ks_hash_destroy(&ctx->protocols); - ks_hash_destroy(&ctx->identities); - ks_hash_destroy(&ctx->routes); - ks_hash_destroy(&ctx->authorities); - ks_hash_destroy(&ctx->protocols_uncertified); + if (storeP && *storeP) { + swclt_store_t *store = *storeP; + ks_pool_t *pool = store->pool; + *storeP = NULL; + ks_hash_destroy(&store->callbacks); + ks_hash_destroy(&store->subscriptions); + ks_hash_destroy(&store->protocols); + ks_hash_destroy(&store->identities); + ks_hash_destroy(&store->routes); + ks_hash_destroy(&store->authorities); + ks_hash_destroy(&store->protocols_uncertified); + ks_pool_close(&pool); + } + return KS_STATUS_SUCCESS; } -static ks_status_t __context_init(swclt_store_ctx_t *ctx) +SWCLT_DECLARE(ks_status_t) swclt_store_create(swclt_store_t **storeP) { ks_status_t status; + swclt_store_t *store = NULL; + ks_pool_t *pool = NULL; + + ks_pool_open(&pool); + store = ks_pool_alloc(pool, sizeof(swclt_store_t)); + store->pool = pool; + *storeP = store; if (status = ks_hash_create( - &ctx->callbacks, + &store->callbacks, KS_HASH_MODE_INT, KS_HASH_FLAG_DUP_CHECK | KS_HASH_FLAG_RWLOCK, - ctx->base.pool)) - return status; + store->pool)) + goto done; /* Create our authorities hash, keyed by the node id of the authority */ if (status = ks_hash_create( - &ctx->authorities, + &store->authorities, KS_HASH_MODE_CASE_INSENSITIVE, KS_HASH_FLAG_DUP_CHECK | KS_HASH_FLAG_FREE_KEY, - ctx->base.pool)) - return status; + store->pool)) + goto done; /* Create our subscriptions hash, keyed by the protocol + channel name */ if (status = ks_hash_create( - &ctx->subscriptions, + &store->subscriptions, KS_HASH_MODE_CASE_INSENSITIVE, KS_HASH_FLAG_DUP_CHECK | KS_HASH_FLAG_FREE_KEY, - ctx->base.pool)) - return status; - ks_hash_set_destructor(ctx->subscriptions, __destroy_subscription); + store->pool)) + goto done; + ks_hash_set_destructor(store->subscriptions, __destroy_subscription); /* Create our protocols hash, keyed by the protocol name */ if (status = ks_hash_create( - &ctx->protocols, + &store->protocols, KS_HASH_MODE_CASE_INSENSITIVE, KS_HASH_FLAG_DUP_CHECK, /* the key is inside the value struct - only need to destroy value */ - ctx->base.pool)) - return status; - ks_hash_set_destructor(ctx->protocols, __destroy_protocol); + store->pool)) + goto done; + ks_hash_set_destructor(store->protocols, __destroy_protocol); /* Create our routes hash, keyed by the nodeid */ if (status = ks_hash_create( - &ctx->routes, + &store->routes, KS_HASH_MODE_CASE_INSENSITIVE, KS_HASH_FLAG_DUP_CHECK | KS_HASH_FLAG_FREE_KEY, - ctx->base.pool)) - return status; - ks_hash_set_destructor(ctx->routes, __destroy_node); + store->pool)) + goto done; + ks_hash_set_destructor(store->routes, __destroy_node); /* Create our identities hash, keyed by the identity */ if (status = ks_hash_create( - &ctx->identities, + &store->identities, KS_HASH_MODE_CASE_INSENSITIVE, KS_HASH_FLAG_DUP_CHECK | KS_HASH_FLAG_FREE_KEY | KS_HASH_FLAG_FREE_VALUE, - ctx->base.pool)) - return status; + store->pool)) + goto done; /* Create our uncertified client protocols hash, keyed by the protocol name */ if (status = ks_hash_create( - &ctx->protocols_uncertified, + &store->protocols_uncertified, KS_HASH_MODE_CASE_INSENSITIVE, KS_HASH_FLAG_RWLOCK | KS_HASH_FLAG_DUP_CHECK | KS_HASH_FLAG_FREE_KEY, - ctx->base.pool)) - return status; + store->pool)) + goto done; + +done: + if (status != KS_STATUS_SUCCESS) { + swclt_store_destroy(storeP); + } return status; } -SWCLT_DECLARE(ks_status_t) swclt_store_reset(swclt_store_t store) +SWCLT_DECLARE(ks_status_t) swclt_store_reset(swclt_store_t *store) { ks_hash_iterator_t *itt; + ks_status_t status = KS_STATUS_SUCCESS; - SWCLT_STORE_SCOPE_BEG(store, ctx, status) - - while (itt = ks_hash_first(ctx->routes, KS_UNLOCKED)) { + ks_hash_write_lock(store->routes); + for (itt = ks_hash_first(store->routes, KS_UNLOCKED); itt; ) { const char *key = NULL; void *value = NULL; ks_hash_this(itt, (const void **)&key, NULL, (void **)&value); - ks_hash_remove(ctx->routes, (const void *)key); + itt = ks_hash_next(&itt); + ks_hash_remove(store->routes, (const void *)key); } + ks_hash_write_unlock(store->routes); - while (itt = ks_hash_first(ctx->identities, KS_UNLOCKED)) { + ks_hash_write_lock(store->identities); + for (itt = ks_hash_first(store->identities, KS_UNLOCKED); itt; ) { const char *key = NULL; void *value = NULL; ks_hash_this(itt, (const void **)&key, NULL, (void **)&value); - ks_hash_remove(ctx->identities, (const void *)key); + itt = ks_hash_next(&itt); + ks_hash_remove(store->identities, (const void *)key); } + ks_hash_write_unlock(store->identities); - while (itt = ks_hash_first(ctx->protocols, KS_UNLOCKED)) { + ks_hash_write_lock(store->protocols); + for (itt = ks_hash_first(store->protocols, KS_UNLOCKED); itt; ) { const char *key = NULL; blade_protocol_t *protocol = NULL; ks_hash_this(itt, (const void **)&key, NULL, (void **)&protocol); - ks_hash_remove(ctx->protocols, (const void *)key); + itt = ks_hash_next(&itt); + ks_hash_remove(store->protocols, (const void *)key); } + ks_hash_write_unlock(store->protocols); - while (itt = ks_hash_first(ctx->subscriptions, KS_UNLOCKED)) { + ks_hash_write_lock(store->subscriptions); + for (itt = ks_hash_first(store->subscriptions, KS_UNLOCKED); itt; ) { const char *key = NULL; void *value = NULL; ks_hash_this(itt, (const void **)&key, NULL, (void **)&value); - ks_hash_remove(ctx->subscriptions, (const void *)key); + itt = ks_hash_next(&itt); + ks_hash_remove(store->subscriptions, (const void *)key); } + ks_hash_write_unlock(store->subscriptions); - - while (itt = ks_hash_first(ctx->authorities, KS_UNLOCKED)) { + ks_hash_write_lock(store->authorities); + for (itt = ks_hash_first(store->authorities, KS_UNLOCKED); itt; ) { const char *key = NULL; void *value = NULL; ks_hash_this(itt, (const void **)&key, NULL, (void **)&value); - ks_hash_remove(ctx->authorities, (const void *)key); + itt = ks_hash_next(&itt); + ks_hash_remove(store->authorities, (const void *)key); } + ks_hash_write_unlock(store->authorities); - while (itt = ks_hash_first(ctx->protocols_uncertified, KS_UNLOCKED)) { + ks_hash_write_lock(store->protocols_uncertified); + for (itt = ks_hash_first(store->protocols_uncertified, KS_UNLOCKED); itt; ) { const char *key = NULL; void *value = NULL; ks_hash_this(itt, (const void **)&key, NULL, (void **)&value); - ks_hash_remove(ctx->protocols_uncertified, (const void *)key); + itt = ks_hash_next(&itt); + ks_hash_remove(store->protocols_uncertified, (const void *)key); } + ks_hash_write_unlock(store->protocols_uncertified); - - SWCLT_STORE_SCOPE_END(store, ctx, status) + return status; } -SWCLT_DECLARE(ks_status_t) swclt_store_populate(swclt_store_t store, const blade_connect_rpl_t *connect_rpl) +SWCLT_DECLARE(ks_status_t) swclt_store_populate(swclt_store_t *store, const blade_connect_rpl_t *connect_rpl) { blade_connect_rpl_t *rpl = (blade_connect_rpl_t *)connect_rpl; - SWCLT_STORE_SCOPE_BEG(store, ctx, status) + ks_status_t status = KS_STATUS_SUCCESS; /* Now popualte the fields from the connect result */ - if (status = __populate_routes(ctx, rpl)) + if (status = __populate_routes(store, rpl)) return status; - if (status = __populate_protocols(ctx, rpl)) + if (status = __populate_protocols(store, rpl)) return status; - if (status = __populate_subscriptions(ctx, rpl)) + if (status = __populate_subscriptions(store, rpl)) return status; - if (status = __populate_authorities(ctx, rpl)) + if (status = __populate_authorities(store, rpl)) return status; - if (status = __populate_protocols_uncertified(ctx, rpl)) + if (status = __populate_protocols_uncertified(store, rpl)) return status; - SWCLT_STORE_SCOPE_END(store, ctx, status) -} - -SWCLT_DECLARE(ks_status_t) swclt_store_create(swclt_store_t *store) -{ - SWCLT_HANDLE_ALLOC_TEMPLATE_S( - NULL, - SWCLT_HTYPE_STORE, - store, - swclt_store_ctx_t, - SWCLT_HSTATE_NORMAL, - __context_describe, - __context_deinit, - __context_init) + return status; } -SWCLT_DECLARE(ks_status_t) swclt_store_get_node_identities(swclt_store_t store, +SWCLT_DECLARE(ks_status_t) swclt_store_get_node_identities(swclt_store_t *store, const char *nodeid, ks_pool_t *pool, ks_hash_t **identities) { - SWCLT_STORE_SCOPE_BEG(store, ctx, status) - status = __get_node_identities(ctx, nodeid, pool, identities); - SWCLT_STORE_SCOPE_END(store, ctx, status) + return __get_node_identities(store, nodeid, pool, identities); } -SWCLT_DECLARE(ks_status_t) swclt_store_get_protocols(swclt_store_t store, ks_pool_t *pool, ks_json_t **protocols) +SWCLT_DECLARE(ks_status_t) swclt_store_get_protocols(swclt_store_t *store, ks_json_t **protocols) { - SWCLT_STORE_SCOPE_BEG(store, ctx, status) - status = __get_protocols(ctx, pool, protocols); - SWCLT_STORE_SCOPE_END(store, ctx, status) + return __get_protocols(store, protocols); } /* Returns success if the protocol exists and the store is valid, also works for uncertified client protocols check. */ -SWCLT_DECLARE(ks_status_t) swclt_store_check_protocol(swclt_store_t store, const char *name) +SWCLT_DECLARE(ks_status_t) swclt_store_check_protocol(swclt_store_t *store, const char *name) { - SWCLT_STORE_SCOPE_BEG(store, ctx, status) - if (status = __lookup_protocol(ctx, name, NULL)) { - status = __lookup_protocol_uncertified(ctx, name); + ks_status_t status = KS_STATUS_SUCCESS; + if (status = __lookup_protocol(store, name, NULL)) { + status = __lookup_protocol_uncertified(store, name); } - SWCLT_STORE_SCOPE_END(store, ctx, status) + return status; } SWCLT_DECLARE(ks_status_t) swclt_store_select_random_protocol_provider( - swclt_store_t store, + swclt_store_t *store, const char *name, ks_pool_t *pool, char **providerid) { - SWCLT_STORE_SCOPE_BEG(store, ctx, status) - status = __select_random_protocol_provider(ctx, name, pool, providerid); - SWCLT_STORE_SCOPE_END(store, ctx, status) + return __select_random_protocol_provider(store, name, pool, providerid); } -SWCLT_DECLARE(ks_status_t) swclt_store_get_protocol_providers(swclt_store_t store, +SWCLT_DECLARE(ks_status_t) swclt_store_get_protocol_providers(swclt_store_t *store, const char *name, - ks_pool_t *pool, ks_json_t **providers) { - SWCLT_STORE_SCOPE_BEG(store, ctx, status) - status = __get_protocol_providers(ctx, name, pool, providers); - SWCLT_STORE_SCOPE_END(store, ctx, status) + return __get_protocol_providers(store, name, providers); } -SWCLT_DECLARE(ks_status_t) swclt_store_update(swclt_store_t store, const blade_netcast_rqu_t *netcast_rqu) +SWCLT_DECLARE(ks_status_t) swclt_store_update(swclt_store_t *store, const blade_netcast_rqu_t *netcast_rqu) { - SWCLT_STORE_SCOPE_BEG(store, ctx, status) - status = __update(ctx, (blade_netcast_rqu_t *) netcast_rqu); - SWCLT_STORE_SCOPE_END(store, ctx, status) + return __update(store, (blade_netcast_rqu_t *) netcast_rqu); } -SWCLT_DECLARE(ks_status_t) swclt_store_cb_route_add(swclt_store_t store, swclt_store_cb_route_add_t cb) +SWCLT_DECLARE(ks_status_t) swclt_store_cb_route_add(swclt_store_t *store, swclt_store_cb_route_add_t cb) { - SWCLT_STORE_SCOPE_BEG(store, ctx, status) - status = __add_cb_route_add(ctx, cb); - SWCLT_STORE_SCOPE_END(store, ctx, status) + return __add_cb_route_add(store, cb); } -SWCLT_DECLARE(ks_status_t) swclt_store_cb_route_remove(swclt_store_t store, swclt_store_cb_route_remove_t cb) +SWCLT_DECLARE(ks_status_t) swclt_store_cb_route_remove(swclt_store_t *store, swclt_store_cb_route_remove_t cb) { - SWCLT_STORE_SCOPE_BEG(store, ctx, status) - status = __add_cb_route_remove(ctx, cb); - SWCLT_STORE_SCOPE_END(store, ctx, status) + return __add_cb_route_remove(store, cb); } -SWCLT_DECLARE(ks_status_t) swclt_store_cb_identity_add(swclt_store_t store, swclt_store_cb_identity_add_t cb) +SWCLT_DECLARE(ks_status_t) swclt_store_cb_identity_add(swclt_store_t *store, swclt_store_cb_identity_add_t cb) { - SWCLT_STORE_SCOPE_BEG(store, ctx, status) - status = __add_cb_identity_add(ctx, cb); - SWCLT_STORE_SCOPE_END(store, ctx, status) + return __add_cb_identity_add(store, cb); } -SWCLT_DECLARE(ks_status_t) swclt_store_cb_identity_remove(swclt_store_t store, swclt_store_cb_identity_remove_t cb) +SWCLT_DECLARE(ks_status_t) swclt_store_cb_identity_remove(swclt_store_t *store, swclt_store_cb_identity_remove_t cb) { - SWCLT_STORE_SCOPE_BEG(store, ctx, status) - status = __add_cb_identity_remove(ctx, cb); - SWCLT_STORE_SCOPE_END(store, ctx, status) + return __add_cb_identity_remove(store, cb); } -SWCLT_DECLARE(ks_status_t) swclt_store_cb_protocol_add(swclt_store_t store, swclt_store_cb_protocol_add_t cb) +SWCLT_DECLARE(ks_status_t) swclt_store_cb_protocol_add(swclt_store_t *store, swclt_store_cb_protocol_add_t cb) { - SWCLT_STORE_SCOPE_BEG(store, ctx, status) - status = __add_cb_protocol_add(ctx, cb); - SWCLT_STORE_SCOPE_END(store, ctx, status) + return __add_cb_protocol_add(store, cb); } -SWCLT_DECLARE(ks_status_t) swclt_store_cb_protocol_remove(swclt_store_t store, swclt_store_cb_protocol_remove_t cb) +SWCLT_DECLARE(ks_status_t) swclt_store_cb_protocol_remove(swclt_store_t *store, swclt_store_cb_protocol_remove_t cb) { - SWCLT_STORE_SCOPE_BEG(store, ctx, status) - status = __add_cb_protocol_remove(ctx, cb); - SWCLT_STORE_SCOPE_END(store, ctx, status) + return __add_cb_protocol_remove(store, cb); } -SWCLT_DECLARE(ks_status_t) swclt_store_cb_protocol_provider_add(swclt_store_t store, swclt_store_cb_protocol_provider_add_t cb) +SWCLT_DECLARE(ks_status_t) swclt_store_cb_protocol_provider_add(swclt_store_t *store, swclt_store_cb_protocol_provider_add_t cb) { - SWCLT_STORE_SCOPE_BEG(store, ctx, status) - status = __add_cb_protocol_provider_add(ctx, cb); - SWCLT_STORE_SCOPE_END(store, ctx, status) + return __add_cb_protocol_provider_add(store, cb); } -SWCLT_DECLARE(ks_status_t) swclt_store_cb_protocol_provider_remove(swclt_store_t store, swclt_store_cb_protocol_provider_remove_t cb) +SWCLT_DECLARE(ks_status_t) swclt_store_cb_protocol_provider_remove(swclt_store_t *store, swclt_store_cb_protocol_provider_remove_t cb) { - SWCLT_STORE_SCOPE_BEG(store, ctx, status) - status = __add_cb_protocol_provider_remove(ctx, cb); - SWCLT_STORE_SCOPE_END(store, ctx, status) + return __add_cb_protocol_provider_remove(store, cb); } -SWCLT_DECLARE(ks_status_t) swclt_store_cb_protocol_provider_rank_update(swclt_store_t store, swclt_store_cb_protocol_provider_rank_update_t cb) +SWCLT_DECLARE(ks_status_t) swclt_store_cb_protocol_provider_rank_update(swclt_store_t *store, swclt_store_cb_protocol_provider_rank_update_t cb) { - SWCLT_STORE_SCOPE_BEG(store, ctx, status) - status = __add_cb_protocol_provider_rank_update(ctx, cb); - SWCLT_STORE_SCOPE_END(store, ctx, status) + return __add_cb_protocol_provider_rank_update(store, cb); } -SWCLT_DECLARE(ks_status_t) swclt_store_cb_protocol_provider_data_update(swclt_store_t store, swclt_store_cb_protocol_provider_data_update_t cb) +SWCLT_DECLARE(ks_status_t) swclt_store_cb_protocol_provider_data_update(swclt_store_t *store, swclt_store_cb_protocol_provider_data_update_t cb) { - SWCLT_STORE_SCOPE_BEG(store, ctx, status) - status = __add_cb_protocol_provider_data_update(ctx, cb); - SWCLT_STORE_SCOPE_END(store, ctx, status) + return __add_cb_protocol_provider_data_update(store, cb); } -SWCLT_DECLARE(ks_status_t) swclt_store_cb_authority_add(swclt_store_t store, swclt_store_cb_authority_add_t cb) +SWCLT_DECLARE(ks_status_t) swclt_store_cb_authority_add(swclt_store_t *store, swclt_store_cb_authority_add_t cb) { - SWCLT_STORE_SCOPE_BEG(store, ctx, status) - status = __add_cb_authority_add(ctx, cb); - SWCLT_STORE_SCOPE_END(store, ctx, status) + return __add_cb_authority_add(store, cb); } -SWCLT_DECLARE(ks_status_t) swclt_store_cb_authority_remove(swclt_store_t store, swclt_store_cb_authority_remove_t cb) +SWCLT_DECLARE(ks_status_t) swclt_store_cb_authority_remove(swclt_store_t *store, swclt_store_cb_authority_remove_t cb) { - SWCLT_STORE_SCOPE_BEG(store, ctx, status) - status = __add_cb_authority_remove(ctx, cb); - SWCLT_STORE_SCOPE_END(store, ctx, status) + return __add_cb_authority_remove(store, cb); } -SWCLT_DECLARE(ks_status_t) swclt_store_cb_subscription_add(swclt_store_t store, swclt_store_cb_subscription_add_t cb) +SWCLT_DECLARE(ks_status_t) swclt_store_cb_subscription_add(swclt_store_t *store, swclt_store_cb_subscription_add_t cb) { - SWCLT_STORE_SCOPE_BEG(store, ctx, status) - status = __add_cb_subscription_add(ctx, cb); - SWCLT_STORE_SCOPE_END(store, ctx, status) + return __add_cb_subscription_add(store, cb); } -SWCLT_DECLARE(ks_status_t) swclt_store_cb_subscription_remove(swclt_store_t store, swclt_store_cb_subscription_remove_t cb) +SWCLT_DECLARE(ks_status_t) swclt_store_cb_subscription_remove(swclt_store_t *store, swclt_store_cb_subscription_remove_t cb) { - SWCLT_STORE_SCOPE_BEG(store, ctx, status) - status = __add_cb_subscription_remove(ctx, cb); - SWCLT_STORE_SCOPE_END(store, ctx, status) + return __add_cb_subscription_remove(store, cb); } diff --git a/src/session.c b/src/session.c index c77f001..4484a9b 100644 --- a/src/session.c +++ b/src/session.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2019 SignalWire, Inc + * Copyright (c) 2018-2022 SignalWire, Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,9 +22,6 @@ #include "signalwire-client-c/client.h" #include "signalwire-client-c/internal/config.h" -#include "signalwire-client-c/internal/session.h" -#include "signalwire-client-c/internal/command.h" -#include "signalwire-client-c/internal/subscription.h" typedef struct swclt_metric_reg_s { int interval; @@ -33,111 +30,269 @@ typedef struct swclt_metric_reg_s { ks_bool_t dirty; } swclt_metric_reg_t; +typedef struct swclt_result_queue { + swclt_cmd_t *result; + struct swclt_result_queue *next; + ks_time_t expire_time; +} swclt_result_queue_t; + +static ks_status_t __do_connect(swclt_sess_t *sess); +static ks_status_t __do_disconnect(swclt_sess_t *sess); + +static void enqueue_result(swclt_sess_t *sess, swclt_cmd_t *cmd) +{ + ks_mutex_lock(sess->result_mutex); + swclt_result_queue_t *node = ks_pool_alloc(sess->pool, sizeof(*node)); + node->result = swclt_cmd_duplicate(cmd); + node->expire_time = ks_time_now_sec() + 5; + if (sess->result_last) { + sess->result_last->next = node; + } + sess->result_last = node; + if (!sess->result_first) { + sess->result_first = node; + } + ks_mutex_unlock(sess->result_mutex); +} + +static void dequeue_result(swclt_sess_t *sess, swclt_cmd_t **cmd) +{ + int retry; + do { + retry = 0; + ks_mutex_lock(sess->result_mutex); + swclt_result_queue_t *first = sess->result_first; + *cmd = NULL; + if (first) { + if (first->expire_time >= ks_time_now_sec()) { + *cmd = first->result; + } else { + // too old... discard it and retry with next result + swclt_cmd_destroy(&first->result); + retry = 1; + } + sess->result_first = first->next; + ks_pool_free(&first); + } + if (!sess->result_first) { + sess->result_last = NULL; + } + ks_mutex_unlock(sess->result_mutex); + } while (retry); +} -static ks_status_t __context_state_transition(swclt_sess_ctx_t *ctx, SWCLT_HSTATE new_state); - -static void __context_deinit( - swclt_sess_ctx_t *ctx) +static void submit_results(swclt_sess_t *sess) { - ks_handle_destroy(&ctx->conn); - ks_hash_destroy(&ctx->subscriptions); - ks_hash_destroy(&ctx->methods); - ks_hash_destroy(&ctx->setups); - ks_hash_destroy(&ctx->metrics); - swclt_ssl_destroy_context(&ctx->ssl); - swclt_ident_destroy(&ctx->ident); + swclt_cmd_t *result = NULL; + dequeue_result(sess, &result); + while (result) { + ks_rwl_read_lock(sess->rwlock); + if (swclt_conn_submit_result(sess->conn, result) == KS_STATUS_DISCONNECTED) { + ks_rwl_read_unlock(sess->rwlock); + enqueue_result(sess, result); + break; + } + ks_rwl_read_unlock(sess->rwlock); + swclt_cmd_destroy(&result); + } } -static const char * __make_subscription_key(swclt_sess_ctx_t *ctx, const char * protocol, const char * channel) +static void flush_results(swclt_sess_t *sess) { - return ks_psprintf(ctx->base.pool, "%s:%s", protocol, channel); + swclt_cmd_t *result = NULL; + dequeue_result(sess, &result); + while (result) { + swclt_cmd_destroy(&result); + dequeue_result(sess, &result); + } } -static const char * __make_pmethod_key(swclt_sess_ctx_t *ctx, const char *protocol, const char *method) +static void check_session_state(swclt_sess_t *sess) { - return ks_psprintf(ctx->base.pool, "%s:%s", protocol, method); + ks_hash_read_lock(sess->metrics); + for (ks_hash_iterator_t *itt = ks_hash_first(sess->metrics, KS_UNLOCKED); itt; itt = ks_hash_next(&itt)) { + const char *key = NULL; + swclt_metric_reg_t *value = NULL; + + ks_hash_this(itt, (const void **)&key, NULL, (void **)&value); + + if (ks_time_now() >= value->timeout && value->dirty) { + value->timeout = ks_time_now() + (value->interval * KS_USEC_PER_SEC); + value->dirty = KS_FALSE; + + swclt_sess_protocol_provider_rank_update_async(sess, key, value->rank, NULL, NULL, NULL); + } + } + ks_hash_read_unlock(sess->metrics); + + if (sess->disconnect_time > 0 && ks_time_now_sec() >= sess->disconnect_time) { + sess->disconnect_time = 0; + if (sess->state == SWCLT_STATE_ONLINE) { + // Disconnect now and report state change + __do_disconnect(sess); + sess->state = SWCLT_STATE_OFFLINE; + if (sess->state_change_cb) { + sess->state_change_cb(sess, sess->state_change_cb_data); + } + } + } + if (sess->connect_time > 0 && ks_time_now_sec() >= sess->connect_time) { + // Connect and report state change on success + if (__do_connect(sess) == KS_STATUS_SUCCESS) { + sess->connect_time = 0; + if (sess->state_change_cb) { + sess->state_change_cb(sess, sess->state_change_cb_data); + } + } else { + // try again in 2 seconds + sess->connect_time = ks_time_now_sec() + 2; + } + } } -static swclt_pmethod_ctx_t * __make_pmethod_value(swclt_sess_ctx_t *ctx, swclt_pmethod_cb_t pmethod, void *cb_data) +static void *session_monitor_thread(ks_thread_t *thread, void *data) { - swclt_pmethod_ctx_t *pmethod_ctx = ks_pool_alloc(ctx->base.pool, sizeof(swclt_pmethod_ctx_t)); - pmethod_ctx->cb = pmethod; - pmethod_ctx->cb_data = cb_data; - return pmethod_ctx; + swclt_sess_t *sess = (swclt_sess_t *)data; + + ks_log(KS_LOG_DEBUG, "Session monitor starting"); + while (ks_thread_stop_requested(thread) == KS_FALSE) { + ks_cond_lock(sess->monitor_cond); + ks_cond_timedwait(sess->monitor_cond, 1000); + ks_cond_unlock(sess->monitor_cond); + if (ks_thread_stop_requested(thread) == KS_FALSE) { + check_session_state(sess); + } + } + + ks_log(KS_LOG_DEBUG, "Session monitor thread stopping"); + return NULL; } -static ks_status_t __setup_ssl(swclt_sess_ctx_t *ctx) +SWCLT_DECLARE(ks_status_t) swclt_sess_destroy(swclt_sess_t **sessP) { - swclt_ssl_destroy_context(&ctx->ssl); + if (sessP && *sessP) { + swclt_sess_t *sess = *sessP; + ks_pool_t *pool = sess->pool; + *sessP = NULL; + if (sess->monitor_thread) { + if (ks_thread_request_stop(sess->monitor_thread) != KS_STATUS_SUCCESS) { + ks_log(KS_LOG_ERROR, "Failed to stop session monitor thread. Leaking data and moving on."); + return KS_STATUS_FAIL; + } + ks_cond_lock(sess->monitor_cond); + ks_cond_broadcast(sess->monitor_cond); + ks_cond_unlock(sess->monitor_cond); + ks_thread_join(sess->monitor_thread); + ks_thread_destroy(&sess->monitor_thread); + } + sess->monitor_thread = NULL; + + swclt_conn_destroy(&sess->conn); - return swclt_ssl_create_context(ctx->config->private_key_path, ctx->config->client_cert_path, ctx->config->cert_chain_path, &ctx->ssl); + // wait for all readers to finish + ks_rwl_write_lock(sess->rwlock); + + // now destroy everything + ks_hash_destroy(&sess->subscriptions); + ks_hash_destroy(&sess->methods); + ks_hash_destroy(&sess->setups); + ks_hash_destroy(&sess->metrics); + if (sess->ssl) { + swclt_ssl_destroy_context(&sess->ssl); + } + swclt_ident_destroy(&sess->ident); + swclt_store_destroy(&sess->store); + ks_rwl_destroy(&sess->rwlock); + if(sess->result_mutex) { + flush_results(sess); + ks_mutex_destroy(&sess->result_mutex); + } + ks_pool_close(&pool); + } + return KS_STATUS_SUCCESS; } -static ks_bool_t __session_check_connected(swclt_sess_ctx_t *ctx, ks_bool_t leave_locked_on_connected) +static const char * __make_subscription_key(swclt_sess_t *sess, const char * protocol, const char * channel) { - ks_bool_t connected = KS_FALSE; - SWCLT_HSTATE state; + return ks_psprintf(sess->pool, "%s:%s", protocol, channel); +} - /* Lock while we touch the internals of the hstate system */ - ks_spinlock_acquire(&ctx->base.lock); +static const char * __make_pmethod_key(swclt_sess_t *sess, const char *protocol, const char *method) +{ + return ks_psprintf(sess->pool, "%s:%s", protocol, method); +} - state = ctx->base.state; - if (ctx->base.pending_state_change_service != SWCLT_HSTATE_INVALID) - state = SWCLT_HSTATE_INVALID; +static swclt_pmethod_ctx_t * __make_pmethod_value(swclt_sess_t *sess, swclt_pmethod_cb_t pmethod, void *cb_data) +{ + swclt_pmethod_ctx_t *pmethod_sess = ks_pool_alloc(sess->pool, sizeof(swclt_pmethod_ctx_t)); + pmethod_sess->cb = pmethod; + pmethod_sess->cb_data = cb_data; + return pmethod_sess; +} - connected = state == SWCLT_HSTATE_ONLINE; +static ks_status_t __setup_ssl(swclt_sess_t *sess) +{ + swclt_ssl_destroy_context(&sess->ssl); - if (!connected || !leave_locked_on_connected) - ks_spinlock_release(&ctx->base.lock); + return swclt_ssl_create_context(sess->config->private_key_path, sess->config->client_cert_path, sess->config->cert_chain_path, &sess->ssl); +} - return connected; +static ks_bool_t __session_check_connected(swclt_sess_t *sess) +{ + return sess->state == SWCLT_STATE_ONLINE || sess->state == SWCLT_STATE_RESTORED; +} + +static ks_bool_t __session_check_restored(swclt_sess_t *sess) +{ + return sess->state == SWCLT_STATE_RESTORED; } static ks_status_t __execute_pmethod_cb( - swclt_sess_ctx_t *ctx, - const swclt_cmd_ctx_t *cmd_ctx, - ks_pool_t *pool, - swclt_pmethod_ctx_t *pmethod_ctx, - const blade_execute_rqu_t *rqu) + swclt_sess_t *sess, + swclt_cmd_t *cmd, + ks_pool_t *pool, + swclt_pmethod_ctx_t *pmethod_sess, + const blade_execute_rqu_t *rqu) { const char *err_message = NULL; int err_code = 0; + char *cmd_str = swclt_cmd_describe(cmd); /* If the context could not be found, set the response appropriately */ - if (!pmethod_ctx) { + if (!pmethod_sess) { err_message = ks_psprintf( pool, - "Failed to lookup any registered protocol method handlers for protocol: %s command: %s", - rqu->protocol, - ks_handle_describe_ctx(cmd_ctx)); + "Failed to lookup any registered protocol method handlers for protocol: %s command: %s", + rqu->protocol, + cmd_str); err_code = -32601; } else { /* Raise the callback, if they respond with a general failure fail for them */ ks_status_t cb_result; - ks_log(KS_LOG_INFO, "Initiating execute for protocol: %s", rqu->protocol); + ks_log(KS_LOG_DEBUG, "Initiating execute for protocol: %s", rqu->protocol); - if (cb_result = pmethod_ctx->cb(ctx->base.handle, cmd_ctx->base.handle, rqu, pmethod_ctx->cb_data)) { + if (cb_result = pmethod_sess->cb(sess, cmd, rqu, pmethod_sess->cb_data)) { err_message = ks_psprintf( pool, - "Protocol method callback returned status: (%lu) command: %s", - cb_result, - ks_handle_describe_ctx(cmd_ctx)); + "Protocol method callback returned status: (%lu) command: %s", + cb_result, + cmd_str); err_code = -32603; } - ks_log(KS_LOG_INFO, "Completed execute for protocol: %s (%lu)", rqu->protocol, cb_result); + ks_log(KS_LOG_DEBUG, "Completed execute for protocol: %s (%lu)", rqu->protocol, cb_result); } /* Now verify the command was properly setup for result processing */ - if (cmd_ctx->type != SWCLT_CMD_TYPE_RESULT && cmd_ctx->type != SWCLT_CMD_TYPE_ERROR) { + if (cmd->type != SWCLT_CMD_TYPE_RESULT && cmd->type != SWCLT_CMD_TYPE_ERROR) { ks_debug_break(); /* Force one for them */ err_message = ks_psprintf( pool, "Protocol method failed to set result in command: %s", - ks_handle_describe_ctx(cmd_ctx)); + cmd_str); err_code = -32607; } @@ -145,22 +300,23 @@ static ks_status_t __execute_pmethod_cb( if (err_code) { ks_status_t status; - ks_log(KS_LOG_ERROR, err_message); + ks_log(KS_LOG_WARNING, err_message); ks_json_t *err = BLADE_EXECUTE_ERR_MARSHAL( - cmd_ctx->base.pool, &(blade_execute_err_t){ rqu->requester_nodeid, rqu->responder_nodeid, err_code, err_message}); - if (status = swclt_cmd_set_error(cmd_ctx->base.handle, &err)) { - ks_log(KS_LOG_ERROR, "Failed to set result in command: %lu", status); + if (status = swclt_cmd_set_error(cmd, &err)) { + ks_log(KS_LOG_WARNING, "Failed to set result in command: %s, status: %lu", cmd_str, status); ks_json_delete(&err); ks_pool_free(&err_message); + ks_pool_free(&cmd_str); } + ks_pool_free(&cmd_str); return status; } @@ -168,31 +324,24 @@ static ks_status_t __execute_pmethod_cb( return KS_STATUS_SUCCESS; } -static ks_status_t __on_incoming_cmd(swclt_conn_t conn, swclt_cmd_t cmd, swclt_sess_ctx_t *ctx) +static ks_status_t __on_incoming_cmd(swclt_conn_t *conn, swclt_cmd_t *cmd, swclt_sess_t *sess) { const char * method; - ks_status_t status; + ks_status_t status = KS_STATUS_FAIL; ks_json_t *request; ks_pool_t *cmd_pool; - const swclt_cmd_ctx_t *cmd_ctx; + char *cmd_str; - /* Reserve the cmd while we mess with it */ - if (status = swclt_cmd_ctx_get(cmd, &cmd_ctx)) { - ks_log(KS_LOG_ERROR, "Failed to reserve command for pmethod callback"); - return status; - } - - ks_log(KS_LOG_DEBUG, "Handling incoming command: %s from transport", ks_handle_describe_ctx(cmd_ctx)); + cmd_str = swclt_cmd_describe(cmd); - /* Retain thread saftey when messing with cmd internals */ - swclt_cmd_ctx_lock(cmd_ctx); + ks_log(KS_LOG_DEBUG, "Handling incoming command: %s from transport", cmd_str); /* Use the commands pool for all allocations */ - cmd_pool = (ks_pool_t *)cmd_ctx->base.pool; + cmd_pool = cmd->pool; /* Grab information about the method */ - method = cmd_ctx->method; - request = cmd_ctx->request; + method = cmd->method; + request = cmd->json; /* Keep it locked until we parse the request */ if (!strcmp(method, BLADE_BROADCAST_METHOD)) { @@ -202,66 +351,100 @@ static ks_status_t __on_incoming_cmd(swclt_conn_t conn, swclt_cmd_t cmd, swclt_s const char *key; status = BLADE_BROADCAST_RQU_PARSE(cmd_pool, request, &rqu); - swclt_cmd_ctx_unlock(cmd_ctx); if (status) { ks_log(KS_LOG_ERROR, "Failed to parse broadcast command: %s (%lu)", - ks_handle_describe_ctx(cmd_ctx), status); + cmd_str, status); goto done; } - key = __make_subscription_key(ctx, rqu->protocol, rqu->channel); - sub = ks_hash_search(ctx->subscriptions, key, KS_UNLOCKED); + key = __make_subscription_key(sess, rqu->protocol, rqu->channel); + sub = ks_hash_search(sess->subscriptions, key, KS_UNLOCKED); ks_pool_free(&key); if (!sub) { - ks_log(KS_LOG_ERROR, "Could not locate sub for protocol: %s channel: %s command: %s", - rqu->protocol, rqu->channel, ks_handle_describe_ctx(cmd_ctx)); + ks_log(KS_LOG_WARNING, "Could not locate sub for protocol: %s channel: %s command: %s", + rqu->protocol, rqu->channel, cmd_str); BLADE_BROADCAST_RQU_DESTROY(&rqu); status = KS_STATUS_NOT_FOUND; goto done; } - status = swclt_sub_invoke(*sub, ctx->base.handle, rqu); + status = swclt_sub_invoke(sub, sess, rqu); BLADE_BROADCAST_RQU_DESTROY(&rqu); goto done; } else if (!strcmp(method, BLADE_DISCONNECT_METHOD)) { - //blade_disconnect_rqu_t *rqu; + blade_disconnect_rqu_t *rqu; - //status = BLADE_DISCONNECT_RQU_PARSE(cmd_pool, request, &rqu); + status = BLADE_DISCONNECT_RQU_PARSE(cmd_pool, request, &rqu); - swclt_cmd_ctx_unlock(cmd_ctx); - - //if (status) { - // ks_log(KS_LOG_ERROR, "Failed to parse netcast command: %s (%lu)", ks_handle_describe_ctx(cmd_ctx), status); - // goto done; - //} + if (status) { + ks_log(KS_LOG_ERROR, "Failed to parse disconnect command: %s (%lu)", cmd_str, status); + goto done; + } // TODO: Handle disconnect properly, should halt sending more data until restored + ks_json_t *cmd_result = ks_json_create_object(); + status = swclt_cmd_set_result(cmd, &cmd_result); + + BLADE_DISCONNECT_RQU_DESTROY(&rqu); - //BLADE_DISCONNECT_RQU_DESTROY(&rqu); + if (!status) { + /* Now the command is ready to be sent back, send it */ + ks_rwl_read_lock(sess->rwlock); + status = swclt_conn_submit_result(sess->conn, cmd); + ks_rwl_read_unlock(sess->rwlock); + if (status) + ks_log(KS_LOG_ERROR, "Failed to submit reply from disconnect %s, status: %lu", cmd_str, status); + else + ks_log(KS_LOG_DEBUG, "Sent reply back from disconnect request: %s", cmd_str); + } + goto done; + } else if (!strcmp(method, BLADE_PING_METHOD)) { + blade_ping_rqu_t *rqu; + + status = BLADE_PING_RQU_PARSE(cmd_pool, request, &rqu); + + if (status) { + ks_log(KS_LOG_ERROR, "Failed to parse ping command: %s (%lu)", cmd_str, status); + goto done; + } + + ks_json_t *cmd_result = BLADE_PING_RPL_MARSHAL(&(blade_ping_rpl_t){ rqu->timestamp, rqu->payload }); + status = swclt_cmd_set_result(cmd, &cmd_result); + + BLADE_PING_RQU_DESTROY(&rqu); + + if (!status) { + /* Now the command is ready to be sent back, send it */ + ks_rwl_read_lock(sess->rwlock); + status = swclt_conn_submit_result(sess->conn, cmd); + ks_rwl_read_unlock(sess->rwlock); + if (status) + ks_log(KS_LOG_ERROR, "Failed to submit reply from ping: %s, status: %lu", cmd_str, status); + else + ks_log(KS_LOG_DEBUG, "Sent reply back from ping request: %s", cmd_str); + } goto done; } else if (!strcmp(method, BLADE_NETCAST_METHOD)) { blade_netcast_rqu_t *rqu; status = BLADE_NETCAST_RQU_PARSE(cmd_pool, request, &rqu); - swclt_cmd_ctx_unlock(cmd_ctx); - if (status) { - ks_log(KS_LOG_ERROR, "Failed to parse netcast command: %s (%lu)", ks_handle_describe_ctx(cmd_ctx), status); + ks_log(KS_LOG_ERROR, "Failed to parse netcast command: %s (%lu)", cmd_str, status); goto done; } - if (status = swclt_store_update(ctx->store, rqu)) { + if (status = swclt_store_update(sess->store, rqu)) { ks_log(KS_LOG_WARNING, "Failed to update nodestore from netcast command: %s (%lu)", - ks_handle_describe_ctx(cmd_ctx), status); + cmd_str, status); BLADE_NETCAST_RQU_DESTROY(&rqu); goto done; } - ks_log(KS_LOG_INFO, "Updated nodestore with netcast command: %s", ks_handle_describe_ctx(cmd_ctx)); + ks_log(KS_LOG_DEBUG, "Updated nodestore with netcast command: %s", cmd_str); BLADE_NETCAST_RQU_DESTROY(&rqu); goto done; } else if (!strcmp(method, BLADE_EXECUTE_METHOD)) { @@ -269,18 +452,17 @@ static ks_status_t __on_incoming_cmd(swclt_conn_t conn, swclt_cmd_t cmd, swclt_s const char *key; status = BLADE_EXECUTE_RQU_PARSE(cmd_pool, request, &rqu); - swclt_cmd_ctx_unlock(cmd_ctx); - ks_log(KS_LOG_DEBUG, "Dispatching incoming blade execute request: %s to callback", ks_handle_describe(cmd)); + ks_log(KS_LOG_INFO, "RX: %s", cmd_str); if (status) { - ks_log(KS_LOG_WARNING, "Failed to parse execute payload: %s (%lu)", ks_handle_describe_ctx(cmd_ctx), status); + ks_log(KS_LOG_WARNING, "Failed to parse execute payload: %s (%lu)", cmd_str, status); goto done; } /* Look up the pmethod, and execute it */ - key = __make_pmethod_key(ctx, rqu->protocol, rqu->method); - if (status = __execute_pmethod_cb(ctx, cmd_ctx, cmd_pool, ks_hash_search(ctx->methods, key, KS_UNLOCKED), rqu)) { + key = __make_pmethod_key(sess, rqu->protocol, rqu->method); + if (status = __execute_pmethod_cb(sess, cmd, cmd_pool, ks_hash_search(sess->methods, key, KS_UNLOCKED), rqu)) { ks_log(KS_LOG_ERROR, "Error executing pmethod: %lu", status); } ks_pool_free(&key); @@ -289,791 +471,688 @@ static ks_status_t __on_incoming_cmd(swclt_conn_t conn, swclt_cmd_t cmd, swclt_s BLADE_EXECUTE_RQU_DESTROY(&rqu); if (!status) { - ks_log(KS_LOG_INFO, "Sending reply back from execute request: %s", ks_handle_describe(cmd)); - - /* Now the command is ready to be sent back, enqueue it */ - if (status = swclt_conn_submit_result(ctx->conn, cmd)) - ks_log(KS_LOG_ERROR, "Failed to submit reply from execute: %lu", status); - else - ks_log(KS_LOG_INFO, "Sent reply back from execute request: %s", ks_handle_describe(cmd)); + char *reply_str = swclt_cmd_describe(cmd); + + /* Now the command is ready to be sent back, send it */ + ks_rwl_read_lock(sess->rwlock); + status = swclt_conn_submit_result(sess->conn, cmd); + ks_rwl_read_unlock(sess->rwlock); + if (status == KS_STATUS_DISCONNECTED) { + /* send after reconnection */ + ks_log(KS_LOG_INFO, "(Not connected) TX ENQUEUE: %s", reply_str); + enqueue_result(sess, cmd); + status = KS_STATUS_SUCCESS; + } else if (status) { + ks_log(KS_LOG_ERROR, "TX FAILED %s, status: %lu", reply_str, status); + } else { + ks_log(KS_LOG_INFO, "TX: %s", reply_str); + } + ks_pool_free(&reply_str); } goto done; } else { - swclt_cmd_ctx_unlock(cmd_ctx); - ks_log(KS_LOG_WARNING, "Not handling incoming command: %s", ks_handle_describe_ctx(cmd_ctx)); + ks_log(KS_LOG_WARNING, "Not handling incoming command: %s", cmd_str); } done: - /* Remove our reservation of the command */ - swclt_cmd_ctx_put(&cmd_ctx); - /* Done with the command now (NOTE: Commands are children of the connections so - * if we return here without destroying it it will eventually get destroyed when - * the connection dies or the session reconnects) */ - ks_handle_destroy(&cmd); + ks_pool_free(&cmd_str); return status; } -static ks_status_t __do_disconnect(swclt_sess_ctx_t *ctx) +static ks_status_t __do_disconnect(swclt_sess_t *sess) { - ks_handle_destroy(&ctx->conn); + ks_rwl_write_lock(sess->rwlock); + swclt_conn_t *conn = sess->conn; + sess->conn = NULL; + ks_rwl_write_unlock(sess->rwlock); + swclt_conn_destroy(&conn); return KS_STATUS_SUCCESS; } -static ks_status_t __on_connect_reply(swclt_conn_t conn, ks_json_t *error, const blade_connect_rpl_t *connect_rpl, swclt_sess_ctx_t *ctx) +static ks_status_t __on_connect_reply(swclt_conn_t *conn, ks_json_t *error, const blade_connect_rpl_t *connect_rpl, swclt_sess_t *sess) { ks_status_t status = KS_STATUS_FAIL; - if (error && ks_json_get_object_number_int_def(error, "code", 0) == -32002) { - if (ctx->auth_failed_cb) ctx->auth_failed_cb(ctx->base.handle); + if (error && ks_json_get_object_number_int(error, "code", 0) == -32002) { + if (sess->auth_failed_cb) sess->auth_failed_cb(sess); } - if (connect_rpl) { + if (connect_rpl) { status = KS_STATUS_SUCCESS; if (!connect_rpl->session_restored) { /* Great we got the reply populate the node store */ - swclt_store_reset(ctx->store); - if (status = swclt_store_populate(ctx->store, connect_rpl)) + swclt_store_reset(sess->store); + if (status = swclt_store_populate(sess->store, connect_rpl)) { ks_log(KS_LOG_WARNING, "Failed to populate node store from connect reply (%lu)", status); + } else { + ks_log(KS_LOG_DEBUG, "Populated node store from connect reply"); + } + } else { + ks_log(KS_LOG_DEBUG, "Restored session"); } } return status; } -static void __on_conn_state_change(swclt_sess_ctx_t *ctx, swclt_hstate_change_t *state_change) +static void __on_conn_failed(swclt_conn_t *conn, void *data) { - /* Enqueue a state change on ourselves as well */ - char *reason = ks_psprintf(ctx->base.pool, "Connection failed: %s", swclt_hstate_describe_change(state_change)); - swclt_hstate_changed(&ctx->base, SWCLT_HSTATE_DEGRADED, KS_STATUS_FAIL, reason); - ks_pool_free(&reason); - - /* Now we, as a session, will want to re-connect so, enqueue a request do to so */ - swclt_hstate_initiate_change_in(&ctx->base, SWCLT_HSTATE_ONLINE, __context_state_transition, 1000, 5000); + swclt_sess_t *sess = (swclt_sess_t *)data; + ks_cond_lock(sess->monitor_cond); + sess->disconnect_time = ks_time_now_sec(); + sess->connect_time = sess->disconnect_time + 5; + ks_cond_broadcast(sess->monitor_cond); + ks_cond_unlock(sess->monitor_cond); } -static ks_status_t __do_connect(swclt_sess_ctx_t *ctx) +static ks_status_t __do_connect(swclt_sess_t *sess) { ks_status_t status; ks_json_t *authentication = NULL; /* Defer this check here, so it can be rescanned from ENV at runtime after session creation */ - if (!ctx->config->private_key_path || !ctx->config->client_cert_path) { - if (!ctx->config->authentication) { + if (!sess->config->private_key_path || !sess->config->client_cert_path) { + if (!sess->config->authentication) { ks_log(KS_LOG_ERROR, "Cannot connect without certificates or authentication"); return KS_STATUS_FAIL; } } - ks_log(KS_LOG_INFO, "Session is performing connect"); + ks_log(KS_LOG_DEBUG, "Session is performing connect"); /* Delete the previous connection if present */ - ks_handle_destroy(&ctx->conn); + __do_disconnect(sess); /* Re-allocate a new ssl context */ - if (status = __setup_ssl(ctx)) { + if (status = __setup_ssl(sess)) { ks_log(KS_LOG_CRIT, "SSL Setup failed: %lu", status); return status; } - ks_log(KS_LOG_INFO, "Successfully setup ssl, initiating connection"); + ks_log(KS_LOG_DEBUG, "Successfully setup ssl, initiating connection"); - if (ctx->config->authentication) { - authentication = ks_json_parse(ctx->config->authentication); + if (sess->config->authentication) { + authentication = ks_json_parse(sess->config->authentication); } + swclt_conn_t *new_conn = NULL; + /* Create a connection and have it call us back anytime a new read is detected */ if (status = swclt_conn_connect_ex( - &ctx->conn, + &new_conn, (swclt_conn_incoming_cmd_cb_t)__on_incoming_cmd, - ctx, + sess, (swclt_conn_connect_cb_t)__on_connect_reply, - ctx, - &ctx->ident, - ctx->info.sessionid, /* Pass in our session id, if it was previous valid we'll try to re-use it */ + sess, + (swclt_conn_failed_cb_t)__on_conn_failed, + sess, + &sess->ident, + sess->info.sessionid, /* Pass in our session id, if it was previous valid we'll try to re-use it */ &authentication, - ctx->ssl)) { + sess->config->agent, + sess->config->identity, + sess->config->network, + sess->ssl)) { if (authentication) ks_json_delete(&authentication); return status; } - if (status = swclt_conn_info(ctx->conn, &ctx->info.conn)) { - ks_debug_break(); /* unexpected */ - ks_handle_destroy(&ctx->conn); - return status; - } + swclt_conn_info(new_conn, &sess->info.conn); + ks_rwl_write_lock(sess->rwlock); /* If we got a new session id, stash it */ - if (!ks_uuid_is_null(&ctx->info.sessionid)) { - if (ks_uuid_cmp(&ctx->info.sessionid, &ctx->info.conn.sessionid)) { + if (!ks_uuid_is_null(&sess->info.sessionid)) { + if (ks_uuid_cmp(&sess->info.sessionid, &sess->info.conn.sessionid)) { ks_log(KS_LOG_WARNING, "New session id created (old: %s, new: %s), all state invalidated", - ks_uuid_thr_str(&ctx->info.sessionid), ks_uuid_thr_str(&ctx->info.conn.sessionid)); - ctx->base.state = SWCLT_HSTATE_NORMAL; + ks_uuid_thr_str(&sess->info.sessionid), ks_uuid_thr_str(&sess->info.conn.sessionid)); + sess->state = SWCLT_STATE_ONLINE; + } else { + sess->state = SWCLT_STATE_RESTORED; } + } else { + sess->state = SWCLT_STATE_ONLINE; } - /* @@ TODO invalidate all state **/ - ctx->info.sessionid = ctx->info.conn.sessionid; - ctx->info.nodeid = ks_pstrdup(ctx->base.pool, ctx->info.conn.nodeid); - ctx->info.master_nodeid = ks_pstrdup(ctx->base.pool, ctx->info.conn.master_nodeid); + sess->info.sessionid = sess->info.conn.sessionid; + sess->info.nodeid = ks_pstrdup(sess->pool, sess->info.conn.nodeid); + sess->info.master_nodeid = ks_pstrdup(sess->pool, sess->info.conn.master_nodeid); + sess->conn = new_conn; + ks_rwl_write_unlock(sess->rwlock); - /* Monitor for state changed on the connection */ - swclt_hstate_register_listener(&ctx->base, __on_conn_state_change, ctx->conn); + ks_log(KS_LOG_INFO, "Successfully established sessionid: %s, nodeid: %s master_nodeid:%s", ks_uuid_thr_str(&sess->info.sessionid), sess->info.nodeid, sess->info.master_nodeid); - ks_log(KS_LOG_INFO, "Successfully established sessionid: %s", ks_uuid_thr_str(&ctx->info.sessionid)); - ks_log(KS_LOG_INFO, " nodeid: %s", ctx->info.nodeid); - ks_log(KS_LOG_INFO, " master_nodeid: %s", ctx->info.master_nodeid); + // send any results that were enqueued during disconnect + submit_results(sess); return status; } -static void __context_describe(swclt_sess_ctx_t *ctx, char *buffer, ks_size_t buffer_len) +static void __context_describe(swclt_sess_t *sess, char *buffer, ks_size_t buffer_len) { - const char *desc = NULL; - if (ctx->conn && (desc = ks_handle_describe(ctx->conn))) { - /* We have to do all this garbage because of the poor decision to nest ks_handle_describe() calls that return a common thread local buffer */ - ks_size_t desc_len = strlen(desc); - const char *preamble = "SWCLT Session - "; - ks_size_t preamble_len = strlen(preamble); - if (desc_len + preamble_len + 1 > buffer_len) { - desc_len = buffer_len - preamble_len - 1; - } - memmove(buffer + preamble_len, desc, desc_len + 1); - memcpy(buffer, preamble, preamble_len); - buffer[buffer_len - 1] = '\0'; + char *desc = NULL; + if (sess->conn && (desc = swclt_conn_describe(sess->conn))) { + snprintf(buffer, buffer_len, "SWCLT Session - %s", desc); + ks_pool_free(&desc); } else { snprintf(buffer, buffer_len, "SWCLT Session (not connected)"); } } -static ks_status_t __context_state_transition(swclt_sess_ctx_t *ctx, SWCLT_HSTATE new_state) +static ks_status_t __disconnect(swclt_sess_t *sess) { - switch (new_state) { - case SWCLT_HSTATE_ONLINE: - return __do_connect(ctx); - - case SWCLT_HSTATE_OFFLINE: - return __do_disconnect(ctx); - - default: - ks_debug_break(); - return KS_STATUS_SUCCESS; - } -} - -static ks_status_t __disconnect(swclt_sess_ctx_t *ctx) -{ - /* If we're already degraded null op */ - if (swclt_hstate_get_ctx(&ctx->base) == SWCLT_HSTATE_OFFLINE) - return KS_STATUS_SUCCESS; - - /* Request a state transition to offline */ - swclt_hstate_initiate_change_now(&ctx->base, SWCLT_HSTATE_OFFLINE, __context_state_transition, 1000); + ks_cond_lock(sess->monitor_cond); + sess->disconnect_time = ks_time_now_sec(); + ks_cond_broadcast(sess->monitor_cond); + ks_cond_unlock(sess->monitor_cond); return KS_STATUS_SUCCESS; } -static ks_status_t __connect(swclt_sess_ctx_t *ctx) +static ks_status_t __connect(swclt_sess_t *sess) { - /* If we're already ready null op */ - if (swclt_hstate_get_ctx(&ctx->base) == SWCLT_HSTATE_ONLINE) - return KS_STATUS_SUCCESS; - - /* Right so our state is still degraded, time to connect */ - swclt_hstate_initiate_change_now(&ctx->base, SWCLT_HSTATE_ONLINE, __context_state_transition, 5000); + ks_cond_lock(sess->monitor_cond); + sess->connect_time = ks_time_now_sec(); + ks_cond_broadcast(sess->monitor_cond); + ks_cond_unlock(sess->monitor_cond); return KS_STATUS_SUCCESS; } -static ks_status_t __swclt_sess_metric_register(swclt_sess_ctx_t *ctx, const char *protocol, int interval, int rank) +static ks_status_t __swclt_sess_metric_register(swclt_sess_t *sess, const char *protocol, int interval, int rank) { swclt_metric_reg_t *reg = NULL; - ks_hash_write_lock(ctx->metrics); + ks_hash_write_lock(sess->metrics); - reg = (swclt_metric_reg_t *)ks_hash_search(ctx->metrics, (const void *)protocol, KS_UNLOCKED); + reg = (swclt_metric_reg_t *)ks_hash_search(sess->metrics, (const void *)protocol, KS_UNLOCKED); if (reg) { - ks_log(KS_LOG_INFO, "Metric update for '%s'\n", protocol); + ks_log(KS_LOG_DEBUG, "Metric update for '%s'\n", protocol); reg->interval = interval; reg->rank = rank; } else { - ks_log(KS_LOG_INFO, "Metric added for '%s'\n", protocol); + ks_log(KS_LOG_DEBUG, "Metric added for '%s'\n", protocol); - reg = (swclt_metric_reg_t *)ks_pool_alloc(ks_pool_get(ctx->metrics), sizeof(swclt_metric_reg_t)); + reg = (swclt_metric_reg_t *)ks_pool_alloc(ks_pool_get(sess->metrics), sizeof(swclt_metric_reg_t)); reg->timeout = ks_time_now(); reg->interval = interval; reg->rank = rank; reg->dirty = KS_TRUE; - ks_hash_insert(ctx->metrics, (const void *)ks_pstrdup(ks_pool_get(ctx->metrics), protocol), (void *)reg); + ks_hash_insert(sess->metrics, (const void *)ks_pstrdup(ks_pool_get(sess->metrics), protocol), (void *)reg); } - ks_hash_write_unlock(ctx->metrics); + ks_hash_write_unlock(sess->metrics); return KS_STATUS_SUCCESS; } -static ks_status_t __swclt_sess_metric_update(swclt_sess_ctx_t *ctx, const char *protocol, int rank) +static ks_status_t __swclt_sess_metric_update(swclt_sess_t *sess, const char *protocol, int rank) { swclt_metric_reg_t *reg = NULL; - ks_hash_write_lock(ctx->metrics); + ks_hash_write_lock(sess->metrics); - reg = (swclt_metric_reg_t *)ks_hash_search(ctx->metrics, (const void *)protocol, KS_UNLOCKED); + reg = (swclt_metric_reg_t *)ks_hash_search(sess->metrics, (const void *)protocol, KS_UNLOCKED); if (reg && reg->rank != rank) { reg->rank = rank; reg->dirty = KS_TRUE; } - ks_hash_write_unlock(ctx->metrics); + ks_hash_write_unlock(sess->metrics); return KS_STATUS_SUCCESS; } -static ks_status_t __swclt_sess_metric_current(swclt_sess_ctx_t *ctx, const char *protocol, int *rank) +static ks_status_t __swclt_sess_metric_current(swclt_sess_t *sess, const char *protocol, int *rank) { ks_status_t ret = KS_STATUS_NOT_FOUND; swclt_metric_reg_t *reg = NULL; - ks_hash_read_lock(ctx->metrics); + ks_hash_read_lock(sess->metrics); - reg = (swclt_metric_reg_t *)ks_hash_search(ctx->metrics, (const void *)protocol, KS_UNLOCKED); + reg = (swclt_metric_reg_t *)ks_hash_search(sess->metrics, (const void *)protocol, KS_UNLOCKED); if (reg) { *rank = reg->rank; ret = KS_STATUS_SUCCESS; } - ks_hash_read_unlock(ctx->metrics); + ks_hash_read_unlock(sess->metrics); return ret; } -static void __context_service(swclt_sess_ctx_t *ctx) -{ - ks_hash_read_lock(ctx->metrics); - for (ks_hash_iterator_t *itt = ks_hash_first(ctx->metrics, KS_UNLOCKED); itt; itt = ks_hash_next(&itt)) { - const char *key = NULL; - swclt_metric_reg_t *value = NULL; - - ks_hash_this(itt, (const void **)&key, NULL, (void **)&value); - - if (ks_time_now() >= value->timeout && value->dirty) { - value->timeout = ks_time_now() + (value->interval * KS_USEC_PER_SEC); - value->dirty = KS_FALSE; - - swclt_sess_protocol_provider_rank_update_async(ctx->base.handle, key, value->rank, NULL, NULL, NULL); - } - } - ks_hash_read_unlock(ctx->metrics); - - /* Now ask to be serviced again in 1 second */ - swclt_hmgr_request_service_in(&ctx->base, 1000); -} - -static ks_status_t __context_init( - swclt_sess_ctx_t *ctx, +SWCLT_DECLARE(ks_status_t) swclt_sess_create( + swclt_sess_t **sessP, const char *identity_uri, swclt_config_t *config) { - ks_status_t status; + ks_status_t status = KS_STATUS_SUCCESS; + ks_pool_t *pool = NULL; + swclt_sess_t *sess = NULL; - ks_assert(ctx->base.state == SWCLT_HSTATE_NORMAL); + ks_pool_open(&pool); + sess = ks_pool_alloc(pool, sizeof(swclt_sess_t)); + sess->pool = pool; + *sessP = sess; ks_log(KS_LOG_INFO, "Session created with identity uri: %s", identity_uri); /* Allow the config to be shared, no ownership change */ - ctx->config = config; + sess->config = config; + + ks_rwl_create(&sess->rwlock, sess->pool); /* Parse the identity, it will contain the connection target address etc. */ - if (status = swclt_ident_from_str(&ctx->ident, ctx->base.pool, identity_uri)) { + if (status = swclt_ident_from_str(&sess->ident, sess->pool, identity_uri)) { ks_log(KS_LOG_ERROR, "Invalid identity uri: %s", identity_uri); goto done; } /* Allocate the subscriptions hash */ if (status = ks_hash_create( - &ctx->subscriptions, + &sess->subscriptions, KS_HASH_MODE_CASE_SENSITIVE, KS_HASH_FLAG_FREE_KEY | KS_HASH_FLAG_FREE_VALUE | KS_HASH_FLAG_MUTEX, - ctx->base.pool)) + sess->pool)) goto done; /* Allocate the methods hash */ if (status = ks_hash_create( - &ctx->methods, + &sess->methods, KS_HASH_MODE_CASE_SENSITIVE, KS_HASH_FLAG_FREE_KEY | KS_HASH_FLAG_FREE_VALUE | KS_HASH_FLAG_RWLOCK, - ctx->base.pool)) + sess->pool)) goto done; if (status = ks_hash_create( - &ctx->setups, + &sess->setups, KS_HASH_MODE_CASE_SENSITIVE, KS_HASH_FLAG_DUP_CHECK | KS_HASH_FLAG_FREE_KEY | KS_HASH_FLAG_FREE_VALUE | KS_HASH_FLAG_RWLOCK, - ctx->base.pool)) + sess->pool)) goto done; if (status = ks_hash_create( - &ctx->metrics, + &sess->metrics, KS_HASH_MODE_CASE_SENSITIVE, KS_HASH_FLAG_DUP_CHECK | KS_HASH_FLAG_FREE_KEY | KS_HASH_FLAG_FREE_VALUE | KS_HASH_FLAG_RWLOCK, - ctx->base.pool)) + sess->pool)) + goto done; + + if (status = ks_mutex_create( + &sess->result_mutex, KS_MUTEX_FLAG_DEFAULT, sess->pool)) goto done; /* Verify the config has what we need */ - if (!ctx->config->private_key_path || !ctx->config->client_cert_path) { - if (!ctx->config->authentication) { + if (!sess->config->private_key_path || !sess->config->client_cert_path) { + if (!sess->config->authentication) { ks_log(KS_LOG_WARNING, "No authentication configured"); } } - if (status = swclt_store_create(&ctx->store)) { + if (status = swclt_store_create(&sess->store)) { ks_log(KS_LOG_ERROR, "Failed to initialize node store (%lu)", status); goto done; } - ks_handle_set_parent(ctx->store, ctx->base.handle); + if (status = ks_cond_create(&sess->monitor_cond, NULL)) { + ks_log(KS_LOG_ERROR, "Failed to allocate session monitor condition: %lu", status); + goto done; + } - /* One second pulsing service */ - swclt_hstate_register_service(&ctx->base, __context_service); + if (status = ks_thread_create_tag(&sess->monitor_thread, session_monitor_thread, sess, NULL, "swclt-session-monitor")) { + ks_log(KS_LOG_CRIT, "Failed to allocate session monitor thread: %lu", status); + goto done; + } done: - if (status) - __context_deinit(ctx); + if (status) { + swclt_sess_destroy(sessP); + } return status; } -static ks_status_t __nodeid_local(swclt_sess_t sess, const char *nodeid) -{ - SWCLT_SESS_SCOPE_BEG(sess, ctx, status); - status = !strcmp(ctx->info.nodeid, nodeid) ? KS_STATUS_SUCCESS : KS_STATUS_FAIL; - SWCLT_SESS_SCOPE_END(sess, ctx, status); -} - -static ks_handle_t * __dupe_handle(swclt_sess_ctx_t *ctx, ks_handle_t handle) +static ks_status_t __nodeid_local(swclt_sess_t *sess, const char *nodeid) { - ks_handle_t *dup = ks_pool_alloc(ctx->base.pool, sizeof(handle)); - ks_assertd(dup); - memcpy(dup, &handle, sizeof(handle)); - return dup; + ks_status_t status = KS_STATUS_SUCCESS; + ks_rwl_read_lock(sess->rwlock); + status = !strcmp(sess->info.nodeid, nodeid) ? KS_STATUS_SUCCESS : KS_STATUS_FAIL; + ks_rwl_read_unlock(sess->rwlock); + return status; } static ks_status_t __unregister_subscription( - swclt_sess_ctx_t *ctx, + swclt_sess_t *sess, const char *protocol, const char *channel) { - const char *key = __make_subscription_key(ctx, protocol, channel); - ks_handle_t *sub = (ks_handle_t *)ks_hash_remove(ctx->subscriptions, key); + const char *key = __make_subscription_key(sess, protocol, channel); + swclt_sub_t *sub = ks_hash_remove(sess->subscriptions, key); ks_pool_free(&key); if (!sub) return KS_STATUS_NOT_FOUND; - ks_handle_destroy(sub); - ks_pool_free(&sub); + swclt_sub_destroy(&sub); return KS_STATUS_SUCCESS; } static ks_status_t __register_subscription( - swclt_sess_ctx_t *ctx, - const char * protocol, - const char * channel, - swclt_sub_t sub) + swclt_sess_t *sess, + const char *protocol, + const char *channel, + swclt_sub_t **sub) { - /* Mark this subscription as a dependent */ - ks_handle_set_parent(sub, ctx->base.handle); - + ks_rwl_read_lock(sess->rwlock); /* unregister if already registered so it does not leak anything, even the handle for the sub * should be cleaned up to avoid leaking for the duration of the session */ - __unregister_subscription(ctx, protocol, channel); - - /* @todo why duplicate the handle on the heap? can assign value of handle to the void* and not use FREE_VALUE hash flag - * answer: check out the sub, then put it in the hash */ + __unregister_subscription(sess, protocol, channel); /* And add it to the hash */ - return ks_hash_insert(ctx->subscriptions, __make_subscription_key(ctx, protocol, channel), __dupe_handle(ctx, sub)); + ks_status_t status = ks_hash_insert(sess->subscriptions, __make_subscription_key(sess, protocol, channel), *sub); + ks_rwl_read_unlock(sess->rwlock); + + if (status == KS_STATUS_SUCCESS) { + *sub = NULL; // take ownership + } + return status; } static ks_status_t __register_pmethod( - swclt_sess_ctx_t *ctx, + swclt_sess_t *sess, const char *protocol, const char *method, swclt_pmethod_cb_t pmethod, void *cb_data) { if (!pmethod) { - const char *key = __make_pmethod_key(ctx, protocol, method); - ks_hash_remove(ctx->methods, key); + const char *key = __make_pmethod_key(sess, protocol, method); + ks_hash_remove(sess->methods, key); ks_pool_free(&key); return KS_STATUS_SUCCESS; } - return ks_hash_insert(ctx->methods, __make_pmethod_key(ctx, protocol, method), __make_pmethod_value(ctx, pmethod, cb_data)); + return ks_hash_insert(sess->methods, __make_pmethod_key(sess, protocol, method), __make_pmethod_value(sess, pmethod, cb_data)); } -static ks_status_t __lookup_setup(swclt_sess_ctx_t *ctx, const char *service, ks_pool_t *pool, char **protocol) +static ks_status_t __lookup_setup(swclt_sess_t *sess, const char *service, ks_pool_t *pool, char **protocol) { ks_status_t status = KS_STATUS_NOT_FOUND; ks_bool_t exists = KS_FALSE; const char *proto = NULL; - ks_hash_read_lock(ctx->setups); - proto = ks_hash_search(ctx->setups, service, KS_UNLOCKED); + ks_hash_read_lock(sess->setups); + proto = ks_hash_search(sess->setups, service, KS_UNLOCKED); if (proto) { *protocol = ks_pstrdup(pool, proto); status = KS_STATUS_SUCCESS; } - ks_hash_read_unlock(ctx->setups); + ks_hash_read_unlock(sess->setups); return status; } -static ks_status_t __register_setup(swclt_sess_ctx_t *ctx, const char *service, const char *protocol) +static ks_status_t __register_setup(swclt_sess_t *sess, const char *service, const char *protocol) { ks_status_t status; - ks_hash_write_lock(ctx->setups); - status = ks_hash_insert(ctx->setups, ks_pstrdup(ks_pool_get(ctx->setups), service), ks_pstrdup(ks_pool_get(ctx->setups), protocol)); - ks_hash_write_unlock(ctx->setups); + ks_hash_write_lock(sess->setups); + status = ks_hash_insert(sess->setups, ks_pstrdup(ks_pool_get(sess->setups), service), ks_pstrdup(ks_pool_get(sess->setups), protocol)); + ks_hash_write_unlock(sess->setups); return status; } -SWCLT_DECLARE(ks_status_t) swclt_sess_set_auth_failed_cb(swclt_sess_t sess, swclt_sess_auth_failed_cb_t cb) +SWCLT_DECLARE(ks_status_t) swclt_sess_set_auth_failed_cb(swclt_sess_t *sess, swclt_sess_auth_failed_cb_t cb) { - swclt_sess_ctx_t *ctx; - ks_status_t result = KS_STATUS_SUCCESS; - - if(ks_handle_get(SWCLT_HTYPE_SESS, sess, &ctx)) - return result; - - ctx->auth_failed_cb = cb; - -done: - ks_handle_put(SWCLT_HTYPE_SESS, &ctx); + sess->auth_failed_cb = cb; + return KS_STATUS_SUCCESS; +} - return result; +SWCLT_DECLARE(ks_status_t) swclt_sess_set_state_change_cb(swclt_sess_t *sess, swclt_sess_state_change_cb_t cb, void *cb_data) +{ + sess->state_change_cb = cb; + sess->state_change_cb_data = cb_data; + return KS_STATUS_SUCCESS; } -SWCLT_DECLARE(ks_status_t) swclt_sess_target_set(swclt_sess_t sess, const char *target) +SWCLT_DECLARE(ks_status_t) swclt_sess_target_set(swclt_sess_t *sess, const char *target) { - swclt_sess_ctx_t *ctx; ks_status_t result = KS_STATUS_SUCCESS; - if(ks_handle_get(SWCLT_HTYPE_SESS, sess, &ctx)) + if (!sess) { return result; + } /* Parse the identity, it will contain the connection target address etc. */ - if (result = swclt_ident_from_str(&ctx->ident, ctx->base.pool, target)) { + if (result = swclt_ident_from_str(&sess->ident, sess->pool, target)) { ks_log(KS_LOG_ERROR, "Invalid identity uri: %s", target); goto done; } ks_log(KS_LOG_ERROR, "Updated session target to %s", target); done: - ks_handle_put(SWCLT_HTYPE_SESS, &ctx); return result; } -SWCLT_DECLARE(ks_status_t) swclt_sess_metric_register(swclt_sess_t sess, const char *protocol, int interval, int rank) -{ - ks_assert(protocol); - ks_assert(interval >= 1); - ks_assert(rank >= 0); - - SWCLT_SESS_SCOPE_BEG(sess, ctx, status); - status = __swclt_sess_metric_register(ctx, protocol, interval, rank); - SWCLT_SESS_SCOPE_END(sess, ctx, status); -} - -SWCLT_DECLARE(ks_status_t) swclt_sess_metric_update(swclt_sess_t sess, const char *protocol, int rank) +SWCLT_DECLARE(ks_status_t) swclt_sess_metric_register(swclt_sess_t *sess, const char *protocol, int interval, int rank) { - ks_assert(protocol); - ks_assert(rank >= 0); - - SWCLT_SESS_SCOPE_BEG(sess, ctx, status); - status = __swclt_sess_metric_update(ctx, protocol, rank); - SWCLT_SESS_SCOPE_END(sess, ctx, status); + if (!protocol) { + ks_log(KS_LOG_ERROR, "Missing protocol for rank register"); + return KS_STATUS_ARG_INVALID; + } + if (interval <= 0) { + ks_log(KS_LOG_ERROR, "Invalid rank interval %d for protocol %s", interval, protocol); + return KS_STATUS_ARG_INVALID; + } + if (rank < 0) { + ks_log(KS_LOG_ERROR, "Invalid rank %d for protocol %s", rank, protocol); + return KS_STATUS_ARG_INVALID; + } + return __swclt_sess_metric_register(sess, protocol, interval, rank); } -SWCLT_DECLARE(ks_status_t) swclt_sess_metric_current(swclt_sess_t sess, const char *protocol, int *rank) +SWCLT_DECLARE(ks_status_t) swclt_sess_metric_update(swclt_sess_t *sess, const char *protocol, int rank) { - ks_assert(protocol); - - SWCLT_SESS_SCOPE_BEG(sess, ctx, status); - status = __swclt_sess_metric_current(ctx, protocol, rank); - SWCLT_SESS_SCOPE_END(sess, ctx, status); + if (!protocol) { + ks_log(KS_LOG_ERROR, "Missing protocol for rank update"); + return KS_STATUS_ARG_INVALID; + } + if (rank < 0) { + ks_log(KS_LOG_ERROR, "Invalid rank %d for protocol %s", rank, protocol); + return KS_STATUS_ARG_INVALID; + } + return __swclt_sess_metric_update(sess, protocol, rank); } -SWCLT_DECLARE(ks_status_t) __swclt_sess_create( - swclt_sess_t *sess, - const char *identity_uri, - swclt_config_t *config, - const char *file, - int line, - const char *tag) +SWCLT_DECLARE(ks_status_t) swclt_sess_metric_current(swclt_sess_t *sess, const char *protocol, int *rank) { - SWCLT_HANDLE_ALLOC_TEMPLATE_M_TAG( - NULL, - file, - line, - tag, - SWCLT_HTYPE_SESS, - sess, - swclt_sess_ctx_t, - SWCLT_HSTATE_NORMAL, - __context_describe, - __context_deinit, - __context_init, - identity_uri, - config); + if (!protocol) { + ks_log(KS_LOG_ERROR, "Missing protocol for rank update"); + return KS_STATUS_ARG_INVALID; + } + return __swclt_sess_metric_current(sess, protocol, rank); } -SWCLT_DECLARE(ks_bool_t) swclt_sess_has_authentication(swclt_sess_t sess) +SWCLT_DECLARE(ks_bool_t) swclt_sess_has_authentication(swclt_sess_t *sess) { - swclt_sess_ctx_t *ctx; - ks_bool_t result = KS_FALSE; - - if(ks_handle_get(SWCLT_HTYPE_SESS, sess, &ctx)) - return result; - - result = (ctx->config->private_key_path && ctx->config->client_cert_path) || ctx->config->authentication; - - ks_handle_put(SWCLT_HTYPE_SESS, &ctx); - - return result; + return (sess->config->private_key_path && sess->config->client_cert_path) || sess->config->authentication; } -SWCLT_DECLARE(ks_status_t) swclt_sess_connect(swclt_sess_t sess) +SWCLT_DECLARE(ks_status_t) swclt_sess_connect(swclt_sess_t *sess) { - SWCLT_SESS_SCOPE_BEG(sess, ctx, status); - status = __connect(ctx); - SWCLT_SESS_SCOPE_END(sess, ctx, status); + return __connect(sess); } -SWCLT_DECLARE(ks_status_t) swclt_sess_disconnect(swclt_sess_t sess) +SWCLT_DECLARE(ks_status_t) swclt_sess_disconnect(swclt_sess_t *sess) { - ks_log(KS_LOG_DEBUG, "Disconnecting"); - SWCLT_SESS_SCOPE_BEG(sess, ctx, status); - status = __disconnect(ctx); - SWCLT_SESS_SCOPE_END(sess, ctx, status); - ks_log(KS_LOG_DEBUG, "Disconnected"); + return __disconnect(sess); } -SWCLT_DECLARE(ks_bool_t) swclt_sess_connected(swclt_sess_t sess) +SWCLT_DECLARE(ks_bool_t) swclt_sess_connected(swclt_sess_t *sess) { - swclt_sess_ctx_t *ctx; - ks_bool_t result = KS_FALSE; - - if(ks_handle_get(SWCLT_HTYPE_SESS, sess, &ctx)) - return result; - - result = __session_check_connected(ctx, KS_FALSE); - - ks_handle_put(SWCLT_HTYPE_SESS, &ctx); - - return result; + ks_bool_t connected; + ks_rwl_read_lock(sess->rwlock); + connected = __session_check_connected(sess); + ks_rwl_read_unlock(sess->rwlock); + return connected; } -SWCLT_DECLARE(ks_bool_t) swclt_sess_restored(swclt_sess_t sess) +SWCLT_DECLARE(ks_bool_t) swclt_sess_restored(swclt_sess_t *sess) { - swclt_handle_base_t *base; - SWCLT_HSTATE state; - SWCLT_HSTATE last_state; - - if (!swclt_htype_valid(KS_HANDLE_TYPE_FROM_HANDLE(sess))) - return KS_FALSE; - - if(ks_handle_get(0, sess, &base)) - return KS_FALSE; - - ks_spinlock_acquire(&base->lock); - state = base->state; - last_state = base->last_state; - if (base->pending_state_change_service != SWCLT_HSTATE_INVALID) - state = SWCLT_HSTATE_INVALID; - ks_spinlock_release(&base->lock); - - ks_handle_put(0, &base); - - return state == SWCLT_HSTATE_ONLINE && last_state == SWCLT_HSTATE_DEGRADED; + ks_bool_t connected; + ks_rwl_read_lock(sess->rwlock); + connected = __session_check_restored(sess); + ks_rwl_read_unlock(sess->rwlock); + return connected; } SWCLT_DECLARE(ks_status_t) swclt_sess_info( - swclt_sess_t sess, - ks_pool_t *pool, + swclt_sess_t *sess, + ks_pool_t *pool, ks_uuid_t *sessionid, char **nodeid, char **master_nodeid) { - SWCLT_SESS_SCOPE_BEG(sess, ctx, status); - - if (__session_check_connected(ctx, KS_TRUE)) { - + ks_rwl_read_lock(sess->rwlock); + if (__session_check_connected(sess)) { /* Default to our pool if no pool specified */ if (!pool) - pool = ctx->base.pool; + pool = sess->pool; /* Context will be locked now, safe the access info */ if (sessionid) - *sessionid = ctx->info.sessionid; + *sessionid = sess->info.sessionid; if (nodeid) - *nodeid = ks_pstrdup(pool, ctx->info.nodeid); + *nodeid = ks_pstrdup(pool, sess->info.nodeid); if (master_nodeid) - *master_nodeid = ks_pstrdup(pool, ctx->info.master_nodeid); - - /* Now unlock the context */ - ks_spinlock_release(&ctx->base.lock); + *master_nodeid = ks_pstrdup(pool, sess->info.master_nodeid); } - - SWCLT_SESS_SCOPE_END(sess, ctx, status); + ks_rwl_read_unlock(sess->rwlock); } -SWCLT_DECLARE(ks_status_t) swclt_sess_nodeid(swclt_sess_t sess, ks_pool_t *pool, char **nodeid) +SWCLT_DECLARE(ks_status_t) swclt_sess_nodeid(swclt_sess_t *sess, ks_pool_t *pool, char **nodeid) { - SWCLT_SESS_SCOPE_BEG(sess, ctx, status); - *nodeid = ks_pstrdup(pool, ctx->info.nodeid); - SWCLT_SESS_SCOPE_END(sess, ctx, status); + ks_rwl_read_lock(sess->rwlock); + *nodeid = ks_pstrdup(pool, sess->info.nodeid); + ks_rwl_read_unlock(sess->rwlock); + return KS_STATUS_SUCCESS; } -SWCLT_DECLARE(ks_bool_t) swclt_sess_nodeid_local(swclt_sess_t sess, const char *nodeid) +SWCLT_DECLARE(ks_bool_t) swclt_sess_nodeid_local(swclt_sess_t *sess, const char *nodeid) { return swclt_sess_connected(sess) && !__nodeid_local(sess, nodeid); } -SWCLT_DECLARE(ks_status_t) swclt_sess_nodestore( - swclt_sess_t sess, - swclt_store_t *store) -{ - SWCLT_SESS_SCOPE_BEG(sess, ctx, status); - *store = ctx->store; - SWCLT_SESS_SCOPE_END(sess, ctx, status); -} - -SWCLT_DECLARE(ks_status_t) __swclt_sess_register_protocol_method( - swclt_sess_t sess, +SWCLT_DECLARE(ks_status_t) swclt_sess_register_protocol_method( + swclt_sess_t *sess, const char *protocol, const char *method, swclt_pmethod_cb_t pmethod_cb, void *cb_data) { - /* @todo consider changing pmethod to a handle, and internally store both the callback and a user data pointer - * however, userdata associated at the time of pmethod registration does not make sense for conceivable use cases */ - SWCLT_SESS_SCOPE_BEG(sess, ctx, status); - status = __register_pmethod(ctx, protocol, method, pmethod_cb, cb_data); - SWCLT_SESS_SCOPE_END(sess, ctx, status); + return __register_pmethod(sess, protocol, method, pmethod_cb, cb_data); } SWCLT_DECLARE(ks_status_t) swclt_sess_register_subscription_method( - swclt_sess_t sess, - swclt_sub_t *sub, + swclt_sess_t *sess, const char *protocol, const char *channel, swclt_sub_cb_t cb, void *cb_data) { - SWCLT_SESS_SCOPE_BEG(sess, ctx, status); - - if (status = swclt_sub_create(sub, protocol, channel, cb, cb_data)) - goto ks_handle_scope_end; + ks_status_t status = KS_STATUS_SUCCESS; + swclt_sub_t *sub = NULL; + if (status = swclt_sub_create(&sub, sess->pool, protocol, channel, cb, cb_data)) + return status; /* Register this subscription, if request fails we just won't receive the events, * but registering the callback is fine and can be replaced if client tries again */ - if (status = __register_subscription(ctx, protocol, channel, *sub)) { - ks_handle_destroy(sub); - goto ks_handle_scope_end; - } - - SWCLT_SESS_SCOPE_END(sess, ctx, status); + status = __register_subscription(sess, protocol, channel, &sub); + swclt_sub_destroy(&sub); + return status; } SWCLT_DECLARE(ks_status_t) swclt_sess_broadcast( - swclt_sess_t sess, + swclt_sess_t *sess, const char *protocol, const char *channel, const char *event, ks_json_t **params) { - SWCLT_SESS_SCOPE_BEG(sess, ctx, status); - swclt_cmd_t cmd = KS_NULL_HANDLE; + ks_status_t status = KS_STATUS_SUCCESS; + swclt_cmd_t *cmd = NULL; /* Create the command */ if (!(cmd = CREATE_BLADE_BROADCAST_CMD( protocol, channel, event, - ctx->info.nodeid, + sess->info.nodeid, params))) { status = KS_STATUS_NO_MEM; goto done; } /* Now submit it */ - if (status = swclt_conn_submit_request(ctx->conn, cmd)) - goto done; + ks_rwl_read_lock(sess->rwlock); + status = swclt_conn_submit_request(sess->conn, &cmd, NULL); + ks_rwl_read_unlock(sess->rwlock); done: - ks_handle_destroy(&cmd); + swclt_cmd_destroy(&cmd); - SWCLT_SESS_SCOPE_END(sess, ctx, status); -} - -SWCLT_DECLARE(ks_status_t) swclt_sess_get_rates(swclt_sess_t sess, ks_throughput_t *recv, ks_throughput_t *send) -{ - SWCLT_SESS_SCOPE_BEG(sess, ctx, status); - status = swclt_conn_get_rates(ctx->conn, recv, send); - SWCLT_SESS_SCOPE_END(sess, ctx, status); + return status; } SWCLT_DECLARE(ks_status_t) swclt_sess_subscription_add( - swclt_sess_t sess, + swclt_sess_t *sess, const char *protocol, const char *channel, swclt_sub_cb_t cb, void *cb_data, - swclt_sub_t *sub, - swclt_cmd_t *cmdP) + swclt_cmd_reply_t **reply) { - return swclt_sess_subscription_add_async( + swclt_cmd_future_t *future = NULL; + swclt_sess_subscription_add_async( sess, protocol, channel, cb, cb_data, - sub, - NULL, /* By passing null here we make this synchronous by implication */ NULL, - cmdP); + NULL, + &future); + return swclt_sess_wait_for_cmd_reply(sess, &future, reply); } SWCLT_DECLARE(ks_status_t) swclt_sess_subscription_add_async( - swclt_sess_t sess, + swclt_sess_t *sess, const char *protocol, const char *channel, swclt_sub_cb_t cb, void *cb_data, - swclt_sub_t *sub, swclt_cmd_cb_t response_callback, void *response_callback_data, - swclt_cmd_t *cmdP) + swclt_cmd_future_t **future) { - SWCLT_SESS_SCOPE_BEG(sess, ctx, status); - swclt_cmd_t cmd = KS_NULL_HANDLE; + ks_status_t status = KS_STATUS_SUCCESS; + swclt_cmd_t *cmd = NULL; blade_subscription_rpl_t *subscription_rpl = NULL; + swclt_sub_t *sub = NULL; /* @todo remove the next 2 calls, and call swclt_sess_register_subscription_method externally, and * update to allow multiple channels to be subscribed via ks_json_t array of channel name strings * can also verify that the callbacks are already registered here as a sanity check */ /* We also will track this subscription with a handle */ - if (status = swclt_sub_create(sub, protocol, channel, cb, cb_data)) + if (status = swclt_sub_create(&sub, sess->pool, protocol, channel, cb, cb_data)) goto done; /* Register this subscription, if request fails we just won't receive the events, * but registering the callback is fine and can be replaced if client tries again */ - if (status = __register_subscription(ctx, protocol, channel, *sub)) + if (status = __register_subscription(sess, protocol, channel, &sub)) goto done; /* Allocate the request */ @@ -1091,58 +1170,48 @@ SWCLT_DECLARE(ks_status_t) swclt_sess_subscription_add_async( } /* Now submit the command */ - if (status = swclt_conn_submit_request(ctx->conn, cmd)) - goto done; - - /* If not async, and caller didnt pass in a spot for the command, destroy it */ - if (!cmdP && !response_callback) - ks_handle_destroy(&cmd); - else { - if (cmdP) *cmdP = cmd; - - /* Parent it to connection for safe guard */ - ks_handle_set_parent(cmd, ctx->conn); - } + ks_rwl_read_lock(sess->rwlock); + status = swclt_conn_submit_request(sess->conn, &cmd, future); + ks_rwl_read_unlock(sess->rwlock); done: - /* Only destroy the subscription if we failed */ - if (status) { - ks_handle_destroy(&cmd); - ks_handle_destroy(sub); - } + swclt_cmd_destroy(&cmd); + swclt_sub_destroy(&sub); - SWCLT_SESS_SCOPE_END(sess, ctx, status); + return status; } SWCLT_DECLARE(ks_status_t) swclt_sess_subscription_remove( - swclt_sess_t sess, + swclt_sess_t *sess, const char *protocol, const char *channel, - swclt_cmd_t *cmdP) + swclt_cmd_reply_t **reply) { - return swclt_sess_subscription_remove_async( + swclt_cmd_future_t *future = NULL; + swclt_sess_subscription_remove_async( sess, protocol, channel, - NULL, /* By passing null here we make this synchronous by implication */ NULL, - cmdP); + NULL, + &future); + return swclt_sess_wait_for_cmd_reply(sess, &future, reply); } SWCLT_DECLARE(ks_status_t) swclt_sess_subscription_remove_async( - swclt_sess_t sess, + swclt_sess_t *sess, const char *protocol, const char *channel, swclt_cmd_cb_t response_callback, void *response_callback_data, - swclt_cmd_t *cmdP) + swclt_cmd_future_t **future) { - SWCLT_SESS_SCOPE_BEG(sess, ctx, status); - swclt_cmd_t cmd = KS_NULL_HANDLE; + ks_status_t status = KS_STATUS_SUCCESS; + swclt_cmd_t *cmd = NULL; /* Unregister this subscription if it exists, if not then just continue with subscription removal * because the callback may have been removed manually */ - __unregister_subscription(ctx, protocol, channel); + __unregister_subscription(sess, protocol, channel); /* Allocate the request */ if (!(cmd = CREATE_BLADE_SUBSCRIPTION_CMD( @@ -1159,28 +1228,18 @@ SWCLT_DECLARE(ks_status_t) swclt_sess_subscription_remove_async( } /* Now submit the command */ - if (status = swclt_conn_submit_request(ctx->conn, cmd)) - goto done; - - /* If not async, and caller didnt pass in a spot for the command, destroy it */ - if (!cmdP && !response_callback) - ks_handle_destroy(&cmd); - else { - if (cmdP) *cmdP = cmd; - - /* Parent it to connection for safe guard */ - ks_handle_set_parent(cmd, ctx->conn); - } + ks_rwl_read_lock(sess->rwlock); + status = swclt_conn_submit_request(sess->conn, &cmd, future); + ks_rwl_read_unlock(sess->rwlock); done: - if (status) - ks_handle_destroy(&cmd); + swclt_cmd_destroy(&cmd); - SWCLT_SESS_SCOPE_END(sess, ctx, status); + return status; } SWCLT_DECLARE(ks_status_t) swclt_sess_protocol_provider_add( - swclt_sess_t sess, + swclt_sess_t *sess, const char * protocol, blade_access_control_t default_method_execute_access, blade_access_control_t default_channel_subscribe_access, @@ -1189,9 +1248,10 @@ SWCLT_DECLARE(ks_status_t) swclt_sess_protocol_provider_add( ks_json_t **channels, int rank, ks_json_t **data, - swclt_cmd_t *cmdP) + swclt_cmd_reply_t **reply) { - return swclt_sess_protocol_provider_add_async( + swclt_cmd_future_t *future = NULL; + swclt_sess_protocol_provider_add_async( sess, protocol, default_method_execute_access, @@ -1201,13 +1261,14 @@ SWCLT_DECLARE(ks_status_t) swclt_sess_protocol_provider_add( channels, rank, data, - NULL, /* By passing null here we make this synchronous by implication */ NULL, - cmdP); + NULL, + &future); + return swclt_sess_wait_for_cmd_reply(sess, &future, reply); } SWCLT_DECLARE(ks_status_t) swclt_sess_protocol_provider_add_async( - swclt_sess_t sess, + swclt_sess_t *sess, const char * protocol, blade_access_control_t default_method_execute_access, blade_access_control_t default_channel_subscribe_access, @@ -1218,10 +1279,10 @@ SWCLT_DECLARE(ks_status_t) swclt_sess_protocol_provider_add_async( ks_json_t **data, swclt_cmd_cb_t response_callback, void *response_callback_data, - swclt_cmd_t *cmdP) + swclt_cmd_future_t **future) { - SWCLT_SESS_SCOPE_BEG(sess, ctx, status); - swclt_cmd_t cmd = KS_NULL_HANDLE; + ks_status_t status = KS_STATUS_SUCCESS; + swclt_cmd_t *cmd = NULL; /* Create the command */ if (!(cmd = CREATE_BLADE_PROTOCOL_PROVIDER_ADD_CMD( @@ -1237,61 +1298,46 @@ SWCLT_DECLARE(ks_status_t) swclt_sess_protocol_provider_add_async( goto done; } - /* @todo verify that the subscription callbacks are already registered here as a sanity check - * for any channels that are auto_subscribed */ - /* If the caller wants to do async, set the callback in the cmd */ if (response_callback) { if (status = swclt_cmd_set_cb(cmd, response_callback, response_callback_data)) goto done; } - /* Now hand it to the connection to submit it, it will block until the - * reply is received */ - if (status = swclt_conn_submit_request(ctx->conn, cmd)) - goto done; - - /* If not async, and caller didnt pass in a spot for the command, destroy it */ - if (!cmdP && !response_callback) - ks_handle_destroy(&cmd); - else { - if (cmdP) *cmdP = cmd; - - /* Parent it to connection for safe guard */ - ks_handle_set_parent(cmd, ctx->conn); - } + ks_rwl_read_lock(sess->rwlock); + status = swclt_conn_submit_request(sess->conn, &cmd, future); + ks_rwl_read_unlock(sess->rwlock); done: - if (status) - ks_handle_destroy(&cmd); + swclt_cmd_destroy(&cmd); - SWCLT_SESS_SCOPE_END(sess, ctx, status); + return status; } SWCLT_DECLARE(ks_status_t) swclt_sess_protocol_provider_remove( - swclt_sess_t sess, - const char * protocol, - swclt_cmd_t *cmdP) + swclt_sess_t *sess, + const char * protocol, + swclt_cmd_reply_t **reply) { - return swclt_sess_protocol_provider_remove_async( + swclt_cmd_future_t *future = NULL; + swclt_sess_protocol_provider_remove_async( sess, protocol, - NULL, /* By passing null here we make this synchronous by implication */ NULL, - cmdP); + NULL, + &future); + return swclt_sess_wait_for_cmd_reply(sess, &future, reply); } SWCLT_DECLARE(ks_status_t) swclt_sess_protocol_provider_remove_async( - swclt_sess_t sess, + swclt_sess_t *sess, const char * protocol, swclt_cmd_cb_t response_callback, void *response_callback_data, - swclt_cmd_t *cmdP) + swclt_cmd_future_t **future) { - SWCLT_SESS_SCOPE_BEG(sess, ctx, status); - swclt_cmd_t cmd = KS_NULL_HANDLE; - - /* @todo separate protocol command field parsing into different types based on the command? */ + ks_status_t status = KS_STATUS_SUCCESS; + swclt_cmd_t *cmd = NULL; /* Create the command */ if (!(cmd = CREATE_BLADE_PROTOCOL_PROVIDER_REMOVE_CMD(protocol))) { @@ -1305,53 +1351,43 @@ SWCLT_DECLARE(ks_status_t) swclt_sess_protocol_provider_remove_async( goto done; } - /* Now hand it to the connection to submit it, it will block until the - * reply is received */ - if (status = swclt_conn_submit_request(ctx->conn, cmd)) - goto done; - - /* If not async, and caller didnt pass in a spot for the command, destroy it */ - if (!cmdP && !response_callback) - ks_handle_destroy(&cmd); - else { - if (cmdP) *cmdP = cmd; - - /* Parent it to connection for safe guard */ - ks_handle_set_parent(cmd, ctx->conn); - } + ks_rwl_read_lock(sess->rwlock); + status = swclt_conn_submit_request(sess->conn, &cmd, future); + ks_rwl_read_unlock(sess->rwlock); done: - if (status) - ks_handle_destroy(&cmd); + swclt_cmd_destroy(&cmd); - SWCLT_SESS_SCOPE_END(sess, ctx, status); + return status; } SWCLT_DECLARE(ks_status_t) swclt_sess_protocol_provider_rank_update( - swclt_sess_t sess, + swclt_sess_t *sess, const char * protocol, int rank, - swclt_cmd_t *cmdP) + swclt_cmd_reply_t **reply) { - return swclt_sess_protocol_provider_rank_update_async( + swclt_cmd_future_t *future = NULL; + swclt_sess_protocol_provider_rank_update_async( sess, protocol, rank, - NULL, /* By passing null here we make this synchronous by implication */ NULL, - cmdP); + NULL, + &future); + return swclt_sess_wait_for_cmd_reply(sess, &future, reply); } SWCLT_DECLARE(ks_status_t) swclt_sess_protocol_provider_rank_update_async( - swclt_sess_t sess, + swclt_sess_t *sess, const char * protocol, int rank, swclt_cmd_cb_t response_callback, void *response_callback_data, - swclt_cmd_t *cmdP) + swclt_cmd_future_t **future) { - SWCLT_SESS_SCOPE_BEG(sess, ctx, status); - swclt_cmd_t cmd = KS_NULL_HANDLE; + ks_status_t status = KS_STATUS_SUCCESS; + swclt_cmd_t *cmd = NULL; /* Create the command */ if (!(cmd = CREATE_BLADE_PROTOCOL_PROVIDER_RANK_UPDATE_CMD(protocol, rank))) { @@ -1365,51 +1401,41 @@ SWCLT_DECLARE(ks_status_t) swclt_sess_protocol_provider_rank_update_async( goto done; } - /* Now hand it to the connection to submit it, it will block until the - * reply is received */ - if (status = swclt_conn_submit_request(ctx->conn, cmd)) - goto done; - - /* If not async, and caller didnt pass in a spot for the command, destroy it */ - if (!cmdP && !response_callback) - ks_handle_destroy(&cmd); - else { - if (cmdP) *cmdP = cmd; - - /* Parent it to connection for safe guard */ - ks_handle_set_parent(cmd, ctx->conn); - } + ks_rwl_read_lock(sess->rwlock); + status = swclt_conn_submit_request(sess->conn, &cmd, future); + ks_rwl_read_unlock(sess->rwlock); done: - if (status) - ks_handle_destroy(&cmd); + swclt_cmd_destroy(&cmd); - SWCLT_SESS_SCOPE_END(sess, ctx, status); + return status; } SWCLT_DECLARE(ks_status_t) swclt_sess_identity_add( - swclt_sess_t sess, + swclt_sess_t *sess, const char *identity, - swclt_cmd_t *cmdP) + swclt_cmd_reply_t **reply) { - return swclt_sess_identity_add_async( + swclt_cmd_future_t *future = NULL; + swclt_sess_identity_add_async( sess, identity, - NULL, /* By passing null here we make this synchronous by implication */ NULL, - cmdP); + NULL, + &future); + return swclt_sess_wait_for_cmd_reply(sess, &future, reply); } SWCLT_DECLARE(ks_status_t) swclt_sess_identity_add_async( - swclt_sess_t sess, + swclt_sess_t *sess, const char *identity, swclt_cmd_cb_t response_callback, void *response_callback_data, - swclt_cmd_t *cmdP) + swclt_cmd_future_t **future) { - SWCLT_SESS_SCOPE_BEG(sess, ctx, status); - swclt_cmd_t cmd = KS_NULL_HANDLE; + ks_status_t status = KS_STATUS_SUCCESS; + swclt_cmd_t *cmd = NULL; /* Create the command */ if (!(cmd = CREATE_BLADE_IDENTITY_CMD( @@ -1425,60 +1451,96 @@ SWCLT_DECLARE(ks_status_t) swclt_sess_identity_add_async( goto done; } - /* Now hand it to the connection to submit it, it will block until the - * reply is received */ - if (status = swclt_conn_submit_request(ctx->conn, cmd)) - goto done; - - /* If not async, and caller didnt pass in a spot for the command, destroy it */ - if (!cmdP && !response_callback) - ks_handle_destroy(&cmd); - else { - if (cmdP) *cmdP = cmd; - ks_handle_set_parent(cmd, ctx->conn); - } + ks_rwl_read_lock(sess->rwlock); + status = swclt_conn_submit_request(sess->conn, &cmd, future); + ks_rwl_read_unlock(sess->rwlock); done: - if (status) - ks_handle_destroy(&cmd); + swclt_cmd_destroy(&cmd); + + return status; +} - SWCLT_SESS_SCOPE_END(sess, ctx, status); +SWCLT_DECLARE(ks_status_t) swclt_sess_wait_for_cmd_reply( + swclt_sess_t *sess, + swclt_cmd_future_t **future, + swclt_cmd_reply_t **reply) +{ + ks_status_t status = KS_STATUS_FAIL; + if (future && *future) { + status = swclt_cmd_future_get(*future, reply); + if (status != KS_STATUS_SUCCESS) { + ks_rwl_read_lock(sess->rwlock); + if (sess->conn) { + swclt_conn_cancel_request(sess->conn, future); + } + ks_rwl_read_unlock(sess->rwlock); + } + swclt_cmd_future_destroy(future); + } + return status; } SWCLT_DECLARE(ks_status_t) swclt_sess_execute( - swclt_sess_t sess, + swclt_sess_t *sess, + const char *responder, + const char *protocol, + const char *method, + ks_json_t **params, + swclt_cmd_reply_t **reply) +{ + swclt_cmd_future_t *future = NULL; + swclt_sess_execute_async( + sess, + responder, + protocol, + method, + params, + NULL, + NULL, + &future); + return swclt_sess_wait_for_cmd_reply(sess, &future, reply); +} + +SWCLT_DECLARE(ks_status_t) swclt_sess_execute_with_id( + swclt_sess_t *sess, + const char *id, const char *responder, const char *protocol, const char *method, ks_json_t **params, - swclt_cmd_t *cmdP) + swclt_cmd_reply_t **reply) { - return swclt_sess_execute_async( + swclt_cmd_future_t *future = NULL; + swclt_sess_execute_with_id_async( sess, + id, responder, protocol, method, params, - NULL, /* By passing null here we make this synchronous by implication */ NULL, - cmdP); + NULL, + &future); + return swclt_sess_wait_for_cmd_reply(sess, &future, reply); } SWCLT_DECLARE(ks_status_t) swclt_sess_execute_async( - swclt_sess_t sess, + swclt_sess_t *sess, const char *responder, const char *protocol, const char *method, ks_json_t **params, swclt_cmd_cb_t response_callback, void *response_callback_data, - swclt_cmd_t *cmdP) + swclt_cmd_future_t **future) { - SWCLT_SESS_SCOPE_BEG(sess, ctx, status); - swclt_cmd_t cmd = KS_NULL_HANDLE; + ks_status_t status = KS_STATUS_SUCCESS; + swclt_cmd_t *cmd = NULL; /* Create the command */ if (!(cmd = CREATE_BLADE_EXECUTE_CMD( + NULL, responder, protocol, method, @@ -1493,45 +1555,92 @@ SWCLT_DECLARE(ks_status_t) swclt_sess_execute_async( goto done; } - if (status = swclt_conn_submit_request(ctx->conn, cmd)) - goto done; + char *request_str = strdup(swclt_cmd_describe(cmd)); + + ks_rwl_read_lock(sess->rwlock); + status = swclt_conn_submit_request(sess->conn, &cmd, future); + ks_rwl_read_unlock(sess->rwlock); - /* If not async, and caller didnt pass in a spot for the command, destroy it */ - if (!cmdP && !response_callback) - ks_handle_destroy(&cmd); - else { - if (cmdP) *cmdP = cmd; - ks_handle_set_parent(cmd, ctx->conn); + if (status) { + ks_log(KS_LOG_WARNING, "FAILED TX: %s", request_str); + } else { + ks_log(KS_LOG_INFO, "TX: %s", request_str); } + free(request_str); done: - if (status) - ks_handle_destroy(&cmd); + swclt_cmd_destroy(&cmd); - SWCLT_SESS_SCOPE_END(sess, ctx, status); + return status; } -// @todo everything below here is high level supporting stuff related to future IDL templates -// and should probably be moved to it's own separate file for each protocol supported +SWCLT_DECLARE(ks_status_t) swclt_sess_execute_with_id_async( + swclt_sess_t *sess, + const char *id, + const char *responder, + const char *protocol, + const char *method, + ks_json_t **params, + swclt_cmd_cb_t response_callback, + void *response_callback_data, + swclt_cmd_future_t **future) +{ + ks_status_t status = KS_STATUS_SUCCESS; + swclt_cmd_t *cmd = NULL; + + /* Create the command */ + if (!(cmd = CREATE_BLADE_EXECUTE_CMD( + id, + responder, + protocol, + method, + params))) { + status = KS_STATUS_NO_MEM; + goto done; + } + + /* If the caller wants to do async, set the callback in the cmd */ + if (response_callback) { + if (status = swclt_cmd_set_cb(cmd, response_callback, response_callback_data)) + goto done; + } + + char *request_str = strdup(swclt_cmd_describe(cmd)); + + ks_rwl_read_lock(sess->rwlock); + status = swclt_conn_submit_request(sess->conn, &cmd, future); + ks_rwl_read_unlock(sess->rwlock); + + if (status) { + ks_log(KS_LOG_WARNING, "FAILED TX: %s", request_str); + } else { + ks_log(KS_LOG_INFO, "TX: %s", request_str); + } + free(request_str); + +done: + swclt_cmd_destroy(&cmd); + + return status; +} // signalwire consumer -SWCLT_DECLARE(ks_status_t) swclt_sess_signalwire_setup(swclt_sess_t sess, const char *service, swclt_sub_cb_t cb, void *cb_data) +SWCLT_DECLARE(ks_status_t) swclt_sess_signalwire_setup(swclt_sess_t *sess, const char *service, swclt_sub_cb_t cb, void *cb_data) { - SWCLT_SESS_SCOPE_BEG(sess, ctx, status); + ks_status_t status = KS_STATUS_SUCCESS; - swclt_store_t store; - ks_pool_t *pool = NULL; + swclt_store_t *store; ks_json_t *params = NULL; - swclt_cmd_t cmd = KS_NULL_HANDLE; - SWCLT_CMD_TYPE cmd_type; + swclt_cmd_reply_t *reply = NULL; ks_json_t *result = NULL; const char *protocol = NULL; ks_bool_t instance_found = KS_FALSE; - swclt_sub_t sub; - ks_assert(service); - ks_assert(cb); + if (!service) { + ks_log(KS_LOG_ERROR, "Missing service for signalwire.setup"); + return KS_STATUS_ARG_INVALID; + } // Make sure we are at least connected if (!swclt_sess_connected(sess)) { @@ -1540,16 +1649,8 @@ SWCLT_DECLARE(ks_status_t) swclt_sess_signalwire_setup(swclt_sess_t sess, const goto done; } - // And make sure the nodestore is available - if (status = swclt_sess_nodestore(sess, &store)) { - ks_log(KS_LOG_ERROR, "Setup for '%s' failed because session nodestore is unavailable: %d", service, status); - goto done; - } - - pool = ks_handle_pool(sess); - - params = ks_json_pcreate_object(pool); - ks_json_padd_string_to_object(pool, params, "service", service); + params = ks_json_create_object(); + ks_json_add_string_to_object(params, "service", service); // Send the setup request syncronously, if it fails bail out if (status = swclt_sess_execute(sess, @@ -1557,49 +1658,14 @@ SWCLT_DECLARE(ks_status_t) swclt_sess_signalwire_setup(swclt_sess_t sess, const "signalwire", "setup", ¶ms, - &cmd)) { + &reply)) { ks_log(KS_LOG_ERROR, "Setup for '%s' execute failed: %d", service, status); goto done; } - // Make sure the command is valid... - if (status = swclt_cmd_type(cmd, &cmd_type)) { - ks_log(KS_LOG_ERROR, "Setup for '%s' command invalid: %d", service, status); - goto done; - } - - // If we get an error back, bail out - if (cmd_type == SWCLT_CMD_TYPE_ERROR) { - ks_log(KS_LOG_ERROR, "Setup for '%s' response type error", service); - // @todo log more details from the error response - status = KS_STATUS_FAIL; - goto done; - } - - // If it's not an error or a result, what is it, did someone forget to set it from the request? - if (cmd_type != SWCLT_CMD_TYPE_RESULT) { - ks_log(KS_LOG_ERROR, "Setup for '%s' response type invalid", service); - status = KS_STATUS_FAIL; - goto done; - } - - // Make sure we actually get a result to look at - if (status = swclt_cmd_result(cmd, (const ks_json_t **)&result)) { - ks_log(KS_LOG_ERROR, "Setup for '%s' response has no result: %d", service, status); - goto done; - } - - // Get the inner result of the execute - result = ks_json_get_object_item(result, "result"); - if (!result) { - ks_log(KS_LOG_ERROR, "Setup for '%s' response has no result.result", service); - status = KS_STATUS_ARG_NULL; - goto done; - } - // Get protocol from result, duplicate it so we can destroy the command - protocol = ks_json_get_object_cstr_def(result, "protocol", NULL); - if (protocol) protocol = ks_pstrdup(ks_handle_pool(sess), protocol); + protocol = ks_json_get_object_string(ks_json_get_object_item(reply->json, "result"), "protocol", NULL); + if (protocol) protocol = ks_pstrdup(sess->pool, protocol); if (!protocol) { ks_log(KS_LOG_ERROR, "Setup for '%s' response has no result.result.protocol", service); @@ -1607,8 +1673,8 @@ SWCLT_DECLARE(ks_status_t) swclt_sess_signalwire_setup(swclt_sess_t sess, const goto done; } - // Destroy the execute command, taking the result data with it, protocol is duplicated in session pool - ks_handle_destroy(&cmd); + // Destroy the reply, taking the result data with it, protocol is duplicated in session pool + swclt_cmd_reply_destroy(&reply); ks_log(KS_LOG_DEBUG, "Setup for '%s' waiting for provider of protocol instance: %s", service, protocol); @@ -1618,7 +1684,7 @@ SWCLT_DECLARE(ks_status_t) swclt_sess_signalwire_setup(swclt_sess_t sess, const { int nodestore_attempts = 20; while (!instance_found && nodestore_attempts) { - if (!(instance_found = !swclt_store_check_protocol(store, protocol))) { + if (!(instance_found = !swclt_store_check_protocol(sess->store, protocol))) { ks_sleep_ms(100); --nodestore_attempts; } @@ -1633,116 +1699,85 @@ SWCLT_DECLARE(ks_status_t) swclt_sess_signalwire_setup(swclt_sess_t sess, const } // Now that protocol is available, sync subscribe to the notifications channel - if (status = swclt_sess_subscription_add(sess, protocol, "notifications", cb, cb_data, &sub, &cmd)) { + if (status = swclt_sess_subscription_add(sess, protocol, "notifications", cb, cb_data, NULL)) { ks_log(KS_LOG_ERROR, "Setup for '%s' subscription add failed: %d", service, status); goto done; } - - // Make sure the command is valid... - if (status = swclt_cmd_type(cmd, &cmd_type)) { - ks_log(KS_LOG_ERROR, "Setup for '%s' subscription add command invalid: %d", service, status); - goto done; - } - - // If we get an error back, bail out - if (cmd_type == SWCLT_CMD_TYPE_ERROR) { - ks_log(KS_LOG_ERROR, "Setup for '%s' subscription add response type error", service); - // @todo log more details from the error response - status = KS_STATUS_FAIL; - goto done; - } - - // If it's not an error or a result, what is it, did someone forget to set it from the request? - if (cmd_type != SWCLT_CMD_TYPE_RESULT) { - ks_log(KS_LOG_ERROR, "Setup for '%s' subscription add response type invalid", service); - status = KS_STATUS_FAIL; - goto done; - } - - // Make sure we actually get a result to look at - if (status = swclt_cmd_result(cmd, (const ks_json_t **)&result)) { - ks_log(KS_LOG_ERROR, "Setup for '%s', subscription add response has no result: %d", service, status); - goto done; - } - - // Destroy the subscription add command - ks_handle_destroy(&cmd); - - __register_setup(ctx, service, protocol); + __register_setup(sess, service, protocol); done: if (protocol) ks_pool_free(&protocol); - if (ks_handle_valid(cmd)) ks_handle_destroy(&cmd); + swclt_cmd_reply_destroy(&reply); + if (params) ks_json_delete(¶ms); - SWCLT_SESS_SCOPE_END(sess, ctx, status); + return status; } // signalwire provisioning consumer -SWCLT_DECLARE(ks_status_t) swclt_sess_provisioning_setup(swclt_sess_t sess, swclt_sub_cb_t cb, void *cb_data) +SWCLT_DECLARE(ks_status_t) swclt_sess_provisioning_setup(swclt_sess_t *sess, swclt_sub_cb_t cb, void *cb_data) { return swclt_sess_signalwire_setup(sess, "provisioning", cb, cb_data); } -SWCLT_DECLARE(ks_status_t) swclt_sess_provisioning_configure(swclt_sess_t sess, +SWCLT_DECLARE(ks_status_t) swclt_sess_provisioning_configure(swclt_sess_t *sess, const char *target, const char *local_endpoint, const char *external_endpoint, const char *relay_connector_id, - swclt_cmd_t *cmdP) + swclt_cmd_reply_t **reply) { - return swclt_sess_provisioning_configure_async(sess, + swclt_cmd_future_t *future = NULL; + swclt_sess_provisioning_configure_async(sess, target, local_endpoint, external_endpoint, relay_connector_id, - NULL, /* By passing null here we make this synchronous by implication */ NULL, - cmdP); + NULL, + &future); + return swclt_sess_wait_for_cmd_reply(sess, &future, reply); } -SWCLT_DECLARE(ks_status_t) swclt_sess_provisioning_configure_async(swclt_sess_t sess, +SWCLT_DECLARE(ks_status_t) swclt_sess_provisioning_configure_async(swclt_sess_t *sess, const char *target, const char *local_endpoint, const char *external_endpoint, const char *relay_connector_id, swclt_cmd_cb_t response_callback, void *response_callback_data, - swclt_cmd_t *cmdP) + swclt_cmd_future_t **future) { - SWCLT_SESS_SCOPE_BEG(sess, ctx, status); + ks_status_t status = KS_STATUS_SUCCESS; - swclt_store_t store; + swclt_store_t *store; ks_pool_t *pool = NULL; char *protocol = NULL; ks_json_t *params = NULL; - ks_assert(target); - ks_assert(local_endpoint); - ks_assert(external_endpoint); - ks_assert(relay_connector_id); + if (!target || !local_endpoint || !external_endpoint || !relay_connector_id) { + ks_log(KS_LOG_ERROR, "Missing required parameter"); + return KS_STATUS_ARG_INVALID; + } if (!swclt_sess_connected(sess)) { goto done; } - if (status = swclt_sess_nodestore(sess, &store)) - goto done; - - pool = ks_handle_pool(sess); + pool = sess->pool; - if (__lookup_setup(ctx, "provisioning", pool, &protocol)) { + if (__lookup_setup(sess, "provisioning", pool, &protocol)) { ks_log(KS_LOG_ERROR, "Provisioning setup has not been performed"); status = KS_STATUS_FAIL; goto done; } - params = ks_json_pcreate_object(pool); + params = ks_json_create_object(); - ks_json_padd_string_to_object(pool, params, "target", target); - ks_json_padd_string_to_object(pool, params, "local_endpoint", local_endpoint); - ks_json_padd_string_to_object(pool, params, "external_endpoint", external_endpoint); - ks_json_padd_string_to_object(pool, params, "relay_connector_id", relay_connector_id); + ks_json_add_string_to_object(params, "target", target); + ks_json_add_string_to_object(params, "local_endpoint", local_endpoint); + ks_json_add_string_to_object(params, "external_endpoint", external_endpoint); + ks_json_add_string_to_object(params, "relay_connector_id", relay_connector_id); status = swclt_sess_execute_async(sess, NULL, @@ -1751,11 +1786,12 @@ SWCLT_DECLARE(ks_status_t) swclt_sess_provisioning_configure_async(swclt_sess_t ¶ms, response_callback, response_callback_data, - cmdP); + future); done: if (protocol) ks_pool_free(&protocol); + if (params) ks_json_delete(¶ms); - SWCLT_SESS_SCOPE_END(sess, ctx, status); + return status; } @@ -1770,4 +1806,4 @@ SWCLT_DECLARE(ks_status_t) swclt_sess_provisioning_configure_async(swclt_sess_t * End: * For VIM: * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: - */ + */ \ No newline at end of file diff --git a/src/subscription.c b/src/subscription.c index 2a7a0df..2718c4d 100644 --- a/src/subscription.c +++ b/src/subscription.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 SignalWire, Inc + * Copyright (c) 2018-2020 SignalWire, Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,21 +21,26 @@ */ #include "signalwire-client-c/client.h" -#include "signalwire-client-c/internal/subscription.h" -static void __context_deinit(swclt_sub_ctx_t *ctx) +SWCLT_DECLARE(ks_status_t) swclt_sub_destroy(swclt_sub_t **subP) { - ks_pool_free(&ctx->protocol); - ks_pool_free(&ctx->channel); + if (subP && *subP) { + swclt_sub_t *sub = *subP; + *subP = NULL; + ks_pool_free(&sub->protocol); + ks_pool_free(&sub->channel); + } + return KS_STATUS_SUCCESS; } -static void __context_describe(swclt_sub_ctx_t *ctx, char *buffer, ks_size_t buffer_len) +SWCLT_DECLARE(char *) swclt_sub_describe(swclt_sub_t *sub) { - snprintf(buffer, buffer_len, "SWCLT Subscription to protocol: %s channel: %s", ctx->protocol, ctx->channel); + return ks_psprintf(NULL, "SWCLT Subscription to protocol: %s channel: %s", sub->protocol, sub->channel); } -static ks_status_t __context_init( - swclt_sub_ctx_t *ctx, +SWCLT_DECLARE(ks_status_t) swclt_sub_create( + swclt_sub_t **subP, + ks_pool_t *pool, const char * const protocol, const char * const channel, swclt_sub_cb_t cb, @@ -43,54 +48,35 @@ static ks_status_t __context_init( { ks_status_t status = KS_STATUS_SUCCESS; - if (!(ctx->protocol = ks_pstrdup(NULL, protocol))) { + swclt_sub_t *sub = ks_pool_alloc(pool, sizeof(swclt_sub_t)); + *subP = sub; + + if (!(sub->protocol = ks_pstrdup(NULL, protocol))) { status = KS_STATUS_NO_MEM; goto done; } - if (!(ctx->channel = ks_pstrdup(NULL, channel))) { + if (!(sub->channel = ks_pstrdup(NULL, channel))) { status = KS_STATUS_NO_MEM; goto done; } - ctx->cb = cb; - ctx->cb_data = cb_data; + sub->cb = cb; + sub->cb_data = cb_data; done: - if (status) - __context_deinit(ctx); + if (status) { + swclt_sub_destroy(subP); + } return status; } -SWCLT_DECLARE(ks_status_t) swclt_sub_create( - swclt_sub_t *sub, - const char * const protocol, - const char * const channel, - swclt_sub_cb_t callback, - void *cb_data) -{ - SWCLT_HANDLE_ALLOC_TEMPLATE_M( - NULL, - SWCLT_HTYPE_SUB, - sub, - swclt_sub_ctx_t, - SWCLT_HSTATE_NORMAL, - __context_describe, - __context_deinit, - __context_init, - protocol, - channel, - callback, - cb_data) -} - SWCLT_DECLARE(ks_status_t) swclt_sub_invoke( - swclt_sub_t sub, - swclt_sess_t sess, + swclt_sub_t *sub, + swclt_sess_t *sess, blade_broadcast_rqu_t *broadcast_rqu) { - SWCLT_SUB_SCOPE_BEG(sub, ctx, status) - ctx->cb(sess, broadcast_rqu, ctx->cb_data); - SWCLT_SUB_SCOPE_END(sub, ctx, status) + sub->cb(sess, broadcast_rqu, sub->cb_data); + return KS_STATUS_SUCCESS; } diff --git a/src/transport/frame.c b/src/transport/frame.c index 27f2697..088f2e1 100644 --- a/src/transport/frame.c +++ b/src/transport/frame.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 SignalWire, Inc + * Copyright (c) 2018-2019 SignalWire, Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,225 +21,86 @@ */ #include "signalwire-client-c/client.h" -#include "signalwire-client-c/transport/internal/frame.h" -static ks_status_t __get_json(swclt_frame_ctx_t *ctx, ks_json_t **json) -{ - if (!ctx->json) { /* Don't redundantly convert */ - if (!(ctx->json = ks_json_pparse(ctx->base.pool, ctx->data))) { - ks_log(KS_LOG_WARNING, "Failed to parse json"); - return KS_STATUS_INVALID_ARGUMENT; - } - } - - if (json) - *json = ctx->json; - - return KS_STATUS_SUCCESS; -} -static ks_status_t __to_json(swclt_frame_ctx_t *ctx, ks_pool_t *pool, ks_json_t **json) +static ks_status_t __to_json(swclt_frame_t *frame, ks_json_t **json) { - ks_status_t status; - - if (!ctx->json) { - if (status = __get_json(ctx, NULL)) - return status; - } - - if (!(*json = ks_json_pduplicate(pool, ctx->json, KS_TRUE))) { - return KS_STATUS_NO_MEM; - } - - return KS_STATUS_SUCCESS; -} - -static ks_status_t __to_json_lookup(swclt_frame_ctx_t *ctx, ks_pool_t *pool, ks_json_t **json, uint32_t components, va_list args) -{ - ks_json_t *item; - ks_status_t status; - - /* Make the json appear */ - if (!ctx->json) { - if (status = __get_json(ctx, NULL)) - return status; + if (!(*json = ks_json_parse(frame->data))) { + ks_log(KS_LOG_WARNING, "Failed to parse json"); + return KS_STATUS_INVALID_ARGUMENT; } - /* Do the lookup within our json object and duplicate it for them */ - if (!(item = ks_json_valookup(ctx->json, components, args))) - return KS_STATUS_NOT_FOUND; - - /* Found it so now dupe it for them */ - if (!(*json = ks_json_pduplicate(pool, item, KS_TRUE))) - return KS_STATUS_NO_MEM; - return KS_STATUS_SUCCESS; } -static ks_status_t __copy_data(swclt_frame_ctx_t *ctx, void *data, ks_size_t len, kws_opcode_t opcode) +static ks_status_t __copy_data(swclt_frame_t *frame, ks_pool_t *pool, void *data, ks_size_t len, kws_opcode_t opcode) { - /* Invalidate cachhed items */ - ks_json_delete(&ctx->json); - ks_pool_free(&ctx->cached_description); - ctx->len = 0; - ctx->opcode = -1; - if (!data || opcode == WSOC_INVALID) { ks_log(KS_LOG_ERROR, "Unable to copy data because: len = %lu, opcode = %d\n", len, opcode); return KS_STATUS_INVALID_ARGUMENT; } + frame->len = 0; + frame->opcode = -1; + // Note kws returns a size that does not include the final null so account for that here if (len > 0) { - if (ctx->data) { - if (!(ctx->data = ks_pool_resize(ctx->data, len + 1))) + if (frame->data) { + if (!(frame->data = ks_pool_resize(frame->data, len + 1))) return KS_STATUS_NO_MEM; } else { - if (!(ctx->data = ks_pool_alloc(ctx->base.pool, len + 1))) + if (!pool) pool = ks_pool_get(frame); + if (!(frame->data = ks_pool_alloc(pool, len + 1))) return KS_STATUS_NO_MEM; } - memcpy(ctx->data, data, len + 1); + memcpy(frame->data, data, len + 1); } - - ctx->opcode = opcode; - ctx->len = len; - return KS_STATUS_SUCCESS; -} - -static void __context_deinit(swclt_frame_ctx_t *ctx) -{ - ks_json_delete(&ctx->json); - ks_pool_free(&ctx->data); -} - -static ks_status_t __context_init(swclt_frame_ctx_t *ctx) -{ - ctx->opcode = WSOC_INVALID; + frame->opcode = opcode; + frame->len = len; return KS_STATUS_SUCCESS; } -static void __context_describe(swclt_frame_ctx_t *ctx, char *buffer, ks_size_t buffer_len) +static void swclt_frame_cleanup(void *ptr, void *arg, ks_pool_cleanup_action_t action, ks_pool_cleanup_type_t type) { - ks_status_t status; - - ks_spinlock_acquire(&ctx->lock); - - if (ctx->opcode == WSOC_TEXT && ctx->data) { - if (!ctx->cached_description) { - if (status = __get_json(ctx, NULL)) { - snprintf(buffer, buffer_len, "SWCLT Frame, failed to render json: %d", status); - ks_spinlock_release(&ctx->lock); - return; - } - else { - ctx->cached_description = ks_json_pprint(ctx->base.pool, ctx->json); - } - } - - if (ctx->cached_description) - snprintf(buffer, buffer_len, "SWCLT Frame, opcode: %u, length: %zu, json: %s", ctx->opcode, ctx->len, ctx->cached_description); -#if defined(KS_BUILD_DEBUG) - else { - /* Careful writing raw strings to logs heh, just do this for debug for now */ - snprintf(buffer, buffer_len, "SWCLT Frame, opcode: %u, length: %zu, (invalid json) text: %s", ctx->opcode, ctx->len, ctx->data); - } -#endif - } - else if (ctx->opcode != WSOC_INVALID) { - snprintf(buffer, buffer_len, "SWCLT Frame, opcode: %u, length: %zu", ctx->opcode, ctx->len); - } else { - snprintf(buffer, buffer_len, "SWCLT Frame (empy)"); - } - - ks_spinlock_release(&ctx->lock); + swclt_frame_t *frame = (swclt_frame_t *)ptr; + if (frame->data) ks_pool_free(&frame->data); } /** * Allocates a frame, wrapped in a handle. A frame is a context used * for reading and writing into a kws socket. */ -SWCLT_DECLARE(ks_status_t) __swclt_frame_alloc( - swclt_frame_t *frame, - const char *file, - int line, - const char *tag) +SWCLT_DECLARE(ks_status_t) swclt_frame_alloc(swclt_frame_t **frame, ks_pool_t *pool) { - SWCLT_HANDLE_ALLOC_TEMPLATE_S_TAG( - NULL, - file, - line, - tag, - SWCLT_HTYPE_FRAME, - frame, - swclt_frame_ctx_t, - SWCLT_HSTATE_NORMAL, - __context_describe, - __context_deinit, - __context_init); + if (frame) { + *frame = ks_pool_alloc(pool, sizeof(swclt_frame_t)); + ks_pool_set_cleanup(*frame, NULL, swclt_frame_cleanup); + return KS_STATUS_SUCCESS; + } + return KS_STATUS_INVALID_ARGUMENT; } /** * swclt_frame_to_json - Converts the json in the frame, and returns a copy which * the owner must release. */ -SWCLT_DECLARE(ks_status_t) swclt_frame_to_json(swclt_frame_t frame, ks_pool_t *pool, ks_json_t **json) +SWCLT_DECLARE(ks_status_t) swclt_frame_to_json(swclt_frame_t *frame, ks_json_t **json) { - SWCLT_FRAME_SCOPE_BEG(frame, ctx, status) - - ks_spinlock_acquire(&ctx->lock); - status = __to_json(ctx, pool, json); - ks_spinlock_release(&ctx->lock); - - SWCLT_FRAME_SCOPE_END(frame, ctx, status) + return __to_json(frame, json); } -SWCLT_DECLARE(ks_status_t) swclt_frame_to_json_lookup(swclt_frame_t frame, ks_pool_t *pool, ks_json_t **json, int components, ...) +SWCLT_DECLARE(ks_status_t) swclt_frame_copy_data(swclt_frame_t *frame, ks_pool_t *pool, void *data, ks_size_t len, kws_opcode_t opcode) { - SWCLT_FRAME_SCOPE_BEG(frame, ctx, status) - va_list ap; - va_start(ap, components); - - ks_spinlock_acquire(&ctx->lock); - status = __to_json_lookup(ctx, pool, json, components, ap); - ks_spinlock_release(&ctx->lock); - - SWCLT_FRAME_SCOPE_END(frame, ctx, status) + return __copy_data(frame, pool, data, len, opcode); } -/** - * swclt_frame_get_json - Converts the json in the frame, retains ownership, and - * returns a copy to the caller. - */ -SWCLT_DECLARE(ks_status_t) swclt_frame_get_json(swclt_frame_t frame, ks_json_t **json) +SWCLT_DECLARE(ks_status_t) swclt_frame_get_data(swclt_frame_t *frame, void **data, ks_size_t *len, kws_opcode_t *opcode) { - SWCLT_FRAME_SCOPE_BEG(frame, ctx, status) - - ks_spinlock_acquire(&ctx->lock); - status = __get_json(ctx, json); - ks_spinlock_release(&ctx->lock); - - SWCLT_FRAME_SCOPE_END(frame, ctx, status) -} - -SWCLT_DECLARE(ks_status_t) swclt_frame_copy_data(swclt_frame_t frame, void *data, ks_size_t len, kws_opcode_t opcode) -{ - SWCLT_FRAME_SCOPE_BEG(frame, ctx, status) - ks_spinlock_acquire(&ctx->lock); - status = __copy_data(ctx, data, len, opcode); - ks_spinlock_release(&ctx->lock); - SWCLT_FRAME_SCOPE_END(frame, ctx, status) -} - -SWCLT_DECLARE(ks_status_t) swclt_frame_get_data(swclt_frame_t frame, void **data, ks_size_t *len, kws_opcode_t *opcode) -{ - SWCLT_FRAME_SCOPE_BEG(frame, ctx, status) - ks_spinlock_acquire(&ctx->lock); - *data = ctx->data; - *len = ctx->len; - *opcode = ctx->opcode; - ks_spinlock_release(&ctx->lock); - SWCLT_FRAME_SCOPE_END(frame, ctx, status) + *data = frame->data; + *len = frame->len; + *opcode = frame->opcode; + return KS_STATUS_SUCCESS; } /* For Emacs: diff --git a/src/transport/websocket.c b/src/transport/websocket.c index cc060bc..dd6e7ad 100644 --- a/src/transport/websocket.c +++ b/src/transport/websocket.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 SignalWire, Inc + * Copyright (c) 2018-2020 SignalWire, Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,64 +21,56 @@ */ #include "signalwire-client-c/client.h" -#include "signalwire-client-c/transport/internal/websocket.h" -#include "signalwire-client-c/transport/internal/frame.h" // Write apis -static ks_status_t __write_raw(swclt_wss_ctx_t *ctx, kws_opcode_t opcode, const void *data, ks_size_t len) +static ks_status_t __write_raw(swclt_wss_t *ctx, kws_opcode_t opcode, const void *data, ks_size_t len) { ks_size_t wrote; ks_log(KS_LOG_DEBUG, "Writing frame of size: %lu opcode: %lu\n", len, opcode); - ks_mutex_lock(ctx->write_mutex); + ks_mutex_lock(ctx->wss_mutex); wrote = kws_write_frame(ctx->wss, opcode, data, len); - ks_mutex_unlock(ctx->write_mutex); + if (wrote > 0) { + ctx->stats.write_frames++; + } + ks_mutex_unlock(ctx->wss_mutex); if (wrote != len) { ks_log(KS_LOG_WARNING, "Failed to write frame\n"); return KS_STATUS_FAIL; - } else { - ks_throughput_report_ex(ctx->rate_send, len, KS_FALSE); } return KS_STATUS_SUCCESS; } -static ks_status_t __write_ping(swclt_wss_ctx_t *ctx) +static ks_status_t __write_ping(swclt_wss_t *ctx) { static uint64_t data = 0; return __write_raw(ctx, WSOC_PING, &data, sizeof(data)); } -static ks_status_t __write_pong(swclt_wss_ctx_t *ctx, swclt_frame_t frame) +static ks_status_t __write_pong(swclt_wss_t *ctx, swclt_frame_t *frame) { - swclt_frame_ctx_t *frame_ctx; ks_status_t status = KS_STATUS_SUCCESS; + ks_ssize_t wrote; - /* Reference the frame while we write it */ - if (status = swclt_frame_get(frame, &frame_ctx)) { - ks_log(KS_LOG_WARNING, "Invalid frame handed to write: %16.16llx (%lu)", frame, status); - return status; - } - - ks_mutex_lock(ctx->write_mutex); - if (kws_write_frame(ctx->wss, WSOC_PONG, frame_ctx->data, frame_ctx->len) != frame_ctx->len) { - ks_log(KS_LOG_WARNING, "Failed to write frame: %s", ks_handle_describe(frame)); + ks_mutex_lock(ctx->wss_mutex); + if ((wrote = kws_write_frame(ctx->wss, WSOC_PONG, frame->data, frame->len)) != frame->len) { + ks_log(KS_LOG_WARNING, "Failed to write pong"); status = KS_STATUS_FAIL; } - ks_mutex_unlock(ctx->write_mutex); - - /* Done with the reference */ - swclt_frame_put(&frame_ctx); - + if (wrote > 0) { + ctx->stats.write_frames++; + } + ks_mutex_unlock(ctx->wss_mutex); return status; } // Read apis -static ks_status_t __poll_read(swclt_wss_ctx_t *ctx, uint32_t *poll_flagsP) +static ks_status_t __poll_read(swclt_wss_t *ctx, uint32_t *poll_flagsP) { *poll_flagsP = ks_wait_sock(ctx->socket, 1000, KS_POLL_READ); if (*poll_flagsP & KS_POLL_ERROR) { @@ -89,29 +81,27 @@ static ks_status_t __poll_read(swclt_wss_ctx_t *ctx, uint32_t *poll_flagsP) } // Read thread -static ks_status_t __read_frame(swclt_wss_ctx_t *ctx, swclt_frame_t *frameP, kws_opcode_t *opcodeP) +static ks_status_t __read_frame(swclt_wss_t *ctx, swclt_frame_t **frameP, kws_opcode_t *opcodeP) { - swclt_frame_t frame = KS_NULL_HANDLE; + swclt_frame_t *frame = NULL; kws_opcode_t opcode = WSOC_INVALID; uint8_t *data; ks_ssize_t len; ks_status_t status; - /* Allocate a new frame if needed */ + /* Allocate a new frame off the global pool if needed */ if (!*frameP) { - if (status = swclt_frame_alloc(&frame)) + if (status = swclt_frame_alloc(&frame, NULL)) { return status; - - /* Associate the frame with our handle */ - ks_handle_set_parent(frame, ctx->base.handle); + } } else { frame = *frameP; /* Mark it null for now if we error its getting freed */ - *frameP = KS_NULL_HANDLE; + *frameP = NULL; } - ks_mutex_lock(ctx->write_mutex); + ks_mutex_lock(ctx->wss_mutex); /* kws will actually retain ownership of its buffer so, we'll want to make a copy * into our frame */ @@ -122,21 +112,20 @@ static ks_status_t __read_frame(swclt_wss_ctx_t *ctx, swclt_frame_t *frameP, kws status = KS_STATUS_NO_MEM; goto done; } - - /* Update rates */ - ks_throughput_report_ex(ctx->rate_recv, (size_t)len, KS_FALSE); + ctx->stats.read_frames++; ks_log(KS_LOG_DEBUG, "Copying frame of length: %lu of opcode: %lu", (size_t)len, opcode); /* Stash it in the frame */ - if (status = swclt_frame_copy_data(frame, data, (size_t)len, opcode)) + if (status = swclt_frame_copy_data(frame, NULL, data, (size_t)len, opcode)) { goto done; + } done: - ks_mutex_unlock(ctx->write_mutex); + ks_mutex_unlock(ctx->wss_mutex); if (status) { - ks_handle_destroy(&frame); + ks_pool_free(&frame); } else { *frameP = frame; *opcodeP = opcode; @@ -144,15 +133,10 @@ static ks_status_t __read_frame(swclt_wss_ctx_t *ctx, swclt_frame_t *frameP, kws return status; } -static ks_status_t __reader_loop(swclt_wss_ctx_t *ctx) +static ks_status_t __reader_loop(swclt_wss_t *ctx) { ks_status_t status; - /* Righto first things first, notify the throughputs that - * their clocks can officially start. */ - ks_throughput_start(ctx->rate_recv); - ks_throughput_start(ctx->rate_send); - ks_log(KS_LOG_DEBUG, "Websocket reader starting"); /* Loop until the caller requests a stop */ @@ -160,8 +144,9 @@ static ks_status_t __reader_loop(swclt_wss_ctx_t *ctx) uint32_t poll_flags; kws_opcode_t frame_opcode; - if (status = swclt_hstate_check_ctx(&ctx->base, "Reader stopping due to state change:")) - return status; + if (ctx->failed) { + return KS_STATUS_FAIL; + } ks_log(KS_LOG_DEBUG, "Waiting on read poll"); @@ -180,7 +165,11 @@ static ks_status_t __reader_loop(swclt_wss_ctx_t *ctx) return status; } - ks_log(KS_LOG_DEBUG, "Read frame: %s", ks_handle_describe(ctx->read_frame)); + if (ctx->read_frame->len > 0) { + ks_log(KS_LOG_DEBUG, "Read frame: opcode %u, length %zu, data: %s", ctx->read_frame->opcode, ctx->read_frame->len, ctx->read_frame->data); + } else { + ks_log(KS_LOG_DEBUG, "Read frame: opcode %u, length %zu, data: (null)", ctx->read_frame->opcode, ctx->read_frame->len); + } // Deal with pings switch(frame_opcode) { @@ -199,15 +188,12 @@ static ks_status_t __reader_loop(swclt_wss_ctx_t *ctx) case WSOC_TEXT: ks_log(KS_LOG_DEBUG, "Reading text "); // Notify the consumer there is a new frame - if (status = ctx->incoming_frame_cb(ctx->base.handle, ctx->read_frame, ctx->incoming_frame_cb_data)) { + if (status = ctx->incoming_frame_cb(ctx, &ctx->read_frame, ctx->incoming_frame_cb_data)) { ks_log(KS_LOG_WARNING, "Callback from incoming frame returned: %d, exiting", status); - // Done with the frame, callback is responsible for freeing it - ctx->read_frame = 0; + ks_pool_free(&ctx->read_frame); return status; } - - // Done with the frame, callback is responsible for freeing it - ctx->read_frame = 0; + ks_pool_free(&ctx->read_frame); break; } } else { @@ -233,66 +219,47 @@ static ks_status_t __reader_loop(swclt_wss_ctx_t *ctx) static void *__reader(ks_thread_t *thread, void *data) { - swclt_wss_ctx_t *ctx = (swclt_wss_ctx_t *)data; + swclt_wss_t *ctx = (swclt_wss_t *)data; ctx->reader_status = __reader_loop(ctx); if (ctx->reader_status && ctx->reader_status != KS_STATUS_THREAD_STOP_REQUESTED) { - /* Report a state change to degraded */ - swclt_hstate_changed(&ctx->base, SWCLT_HSTATE_DEGRADED, ctx->reader_status, "Reader failed"); + /* Report a failed state */ + ctx->failed = 1; + if (ctx->failed_cb) { + ctx->failed_cb(ctx, ctx->failed_cb_data); + } } return NULL; } -static ks_status_t __start_reader(swclt_wss_ctx_t *ctx) +static ks_status_t __start_reader(swclt_wss_t *ctx) { - return ks_thread_create_tag(&ctx->reader_thread, __reader, ctx, ctx->base.pool, "SWClient WSS Reader"); + return ks_thread_create_tag(&ctx->reader_thread, __reader, ctx, ctx->pool, "swclt-wss-reader"); } -// Context - -static void __context_deinit( - swclt_wss_ctx_t *ctx) -{ - ks_log(KS_LOG_INFO, "Shutting down websocket and stopping reader"); - - ks_thread_destroy(&ctx->reader_thread); - ks_handle_destroy(&ctx->rate_send); - ks_handle_destroy(&ctx->rate_recv); - ks_thread_destroy(&ctx->reader_thread); - ks_mutex_destroy(&ctx->write_mutex); - ks_mutex_destroy(&ctx->read_mutex); - ks_handle_destroy(&ctx->rate_recv); - ks_handle_destroy(&ctx->rate_send); - ks_handle_destroy(&ctx->read_frame); - kws_destroy(&ctx->wss); -} -static void __context_describe(swclt_wss_ctx_t *ctx, char *buffer, ks_size_t buffer_len) +SWCLT_DECLARE(char *) swclt_wss_describe(swclt_wss_t *ctx) { if (ctx->info.ssl) { - snprintf( - buffer, - buffer_len, + return ks_psprintf(ctx->pool, "SWCLT Websocket connection to: %s:%d/%s (Cipher: %s)", ctx->info.address, ctx->info.port, ctx->info.path, ctx->info.cipher ); - } else { - snprintf( - buffer, - buffer_len, - "SWCLT Websocket connection to: %s:%d/%s (No ssl)", - ctx->info.address, - ctx->info.port, - ctx->info.path - ); } + return ks_psprintf( + ctx->pool, + "SWCLT Websocket connection to: %s:%d/%s (No ssl)", + ctx->info.address, + ctx->info.port, + ctx->info.path + ); } -static ks_status_t __connect_socket(swclt_wss_ctx_t *ctx) +static ks_status_t __connect_socket(swclt_wss_t *ctx) { ks_status_t status; char buf[256]; @@ -308,7 +275,7 @@ static ks_status_t __connect_socket(swclt_wss_ctx_t *ctx) snprintf(buf, sizeof(buf), "/%s:%s:swclt", ctx->info.path, ctx->info.address); if (status = kws_init(&ctx->wss, ctx->socket, - ctx->info.ssl, buf, KWS_BLOCK | KWS_CLOSE_SOCK, ctx->base.pool)) + ctx->info.ssl, buf, KWS_BLOCK | KWS_CLOSE_SOCK, ctx->pool)) goto done; /* Load our negotiated cipher while we're here */ @@ -323,60 +290,89 @@ static ks_status_t __connect_socket(swclt_wss_ctx_t *ctx) done: if (status) { + if (ctx->reader_thread) { + ks_thread_request_stop(ctx->reader_thread); + } ks_socket_close(&ctx->socket); - ks_thread_destroy(&ctx->reader_thread); + if (ctx->reader_thread) { + ks_thread_join(ctx->reader_thread); + ks_thread_destroy(&ctx->reader_thread); + } kws_destroy(&ctx->wss); } return status; } -static ks_status_t __context_init( - swclt_wss_ctx_t *ctx, +SWCLT_DECLARE(void) swclt_wss_destroy(swclt_wss_t **wss) +{ + if (wss && *wss) { + ks_pool_t *pool = (*wss)->pool; + ks_log(KS_LOG_INFO, "Shutting down websocket"); + if ((*wss)->reader_thread) { + ks_thread_request_stop((*wss)->reader_thread); + } + ks_socket_close(&(*wss)->socket); + if ((*wss)->reader_thread) { + ks_thread_join((*wss)->reader_thread); + ks_thread_destroy(&(*wss)->reader_thread); + } + if ((*wss)->wss_mutex) { + ks_mutex_destroy(&(*wss)->wss_mutex); + } + ks_pool_free(&(*wss)->read_frame); + kws_destroy(&(*wss)->wss); + ks_pool_free(wss); + ks_pool_close(&pool); + } +} + +SWCLT_DECLARE(ks_status_t) swclt_wss_connect( + swclt_wss_t **wss, swclt_wss_incoming_frame_cb_t incoming_frame_cb, void *incoming_frame_cb_data, + swclt_wss_failed_cb_t failed_cb, + void *failed_cb_data, const char *address, short port, const char *path, uint32_t timeout_ms, const SSL_CTX *ssl) { - ks_status_t status; + ks_status_t status = KS_STATUS_SUCCESS; + ks_pool_t *pool = NULL; + ks_pool_open(&pool); + swclt_wss_t *new_wss = ks_pool_alloc(pool, sizeof(swclt_wss_t)); + new_wss->pool = pool; ks_log(KS_LOG_INFO, "Web socket initiating connection to: %s on port %u to /%s", address, (unsigned int)port, path); - ctx->incoming_frame_cb = incoming_frame_cb; - ctx->incoming_frame_cb_data = incoming_frame_cb_data; - ctx->info.ssl = (SSL_CTX *)ssl; - ctx->info.connect_timeout_ms = timeout_ms; - strncpy(ctx->info.address, address, sizeof(ctx->info.address) - 1); - strncpy(ctx->info.path, path, sizeof(ctx->info.path) - 1); - ctx->info.port = port; - - if (status = ks_throughput_create(&ctx->rate_send)) - goto done; - if (status = ks_throughput_create(&ctx->rate_recv)) - goto done; + new_wss->incoming_frame_cb = incoming_frame_cb; + new_wss->incoming_frame_cb_data = incoming_frame_cb_data; + new_wss->failed_cb = failed_cb; + new_wss->failed_cb_data = failed_cb_data; + new_wss->info.ssl = (SSL_CTX *)ssl; + new_wss->info.connect_timeout_ms = timeout_ms; + strncpy(new_wss->info.address, address, sizeof(new_wss->info.address) - 1); + strncpy(new_wss->info.path, path, sizeof(new_wss->info.path) - 1); + new_wss->info.port = port; ks_log(KS_LOG_DEBUG, "Resolving address: %s", address); - if (status = ks_addr_getbyname(address, port, AF_INET, &ctx->addr)) { + if (status = ks_addr_getbyname(address, port, AF_INET, &new_wss->addr)) { ks_log(KS_LOG_WARNING, "Failed to resolve: %s", address); goto done; } ks_log(KS_LOG_DEBUG, "Successfully resolved address"); - if (status = ks_mutex_create(&ctx->write_mutex, KS_MUTEX_FLAG_DEFAULT, ctx->base.pool)) - goto done; - - if (status = ks_mutex_create(&ctx->read_mutex, KS_MUTEX_FLAG_DEFAULT, ctx->base.pool)) + if (status = ks_mutex_create(&new_wss->wss_mutex, KS_MUTEX_FLAG_DEFAULT, new_wss->pool)) goto done; for (uint32_t tryCount = 0; tryCount < 2; tryCount++) { - ks_log(KS_LOG_INFO, "Performing connect try: %lu to: %s:%d/%s", tryCount, ctx->info.address, ctx->info.port, ctx->info.path); + ks_log(KS_LOG_INFO, "Performing connect try: %lu to: %s:%d/%s", tryCount, new_wss->info.address, new_wss->info.port, new_wss->info.path); - if (status = __connect_socket(ctx)) { + if (status = __connect_socket(new_wss)) { ks_sleep_ms(1000); continue; } @@ -384,86 +380,59 @@ static ks_status_t __context_init( } done: - if (status) - __context_deinit(ctx); - + if (status != KS_STATUS_SUCCESS) { + swclt_wss_destroy(&new_wss); + } + *wss = new_wss; return status; } -SWCLT_DECLARE(ks_status_t) swclt_wss_connect( - swclt_wss_t *wss, - swclt_wss_incoming_frame_cb_t incoming_frame_cb, - void *incoming_frame_cb_data, - const char *address, - short port, - const char *path, - uint32_t timeout_ms, - const SSL_CTX *ssl) -{ - SWCLT_HANDLE_ALLOC_TEMPLATE_M( - NULL, - SWCLT_HTYPE_WSS, - wss, - swclt_wss_ctx_t, - SWCLT_HSTATE_NORMAL, - __context_describe, - __context_deinit, - __context_init, - incoming_frame_cb, - incoming_frame_cb_data, - address, - port, - path, - timeout_ms, - ssl); -} - -SWCLT_DECLARE(ks_status_t) swclt_wss_get_info(swclt_wss_t wss, swclt_wss_info_t *info) +SWCLT_DECLARE(ks_status_t) swclt_wss_get_info(swclt_wss_t *wss, swclt_wss_info_t *info) { - SWCLT_WSS_SCOPE_BEG(wss, ctx, status) - memcpy(info, &ctx->info, sizeof(ctx->info)); - SWCLT_WSS_SCOPE_END(wss, ctx, status) + memcpy(info, &wss->info, sizeof(wss->info)); + return KS_STATUS_SUCCESS; } -SWCLT_DECLARE(ks_status_t) swclt_wss_get_rates(swclt_wss_t wss, ks_throughput_t *send, ks_throughput_t *recv) +SWCLT_DECLARE(void) swclt_wss_get_stats(swclt_wss_t *ctx, swclt_wss_stats_t *stats) { - SWCLT_WSS_SCOPE_BEG(wss, ctx, status) - *send = ctx->rate_send; - *recv = ctx->rate_recv; - SWCLT_WSS_SCOPE_END(wss, ctx, status) + ks_mutex_lock(ctx->wss_mutex); + memcpy(stats, &ctx->stats, sizeof(ctx->stats)); + ks_mutex_unlock(ctx->wss_mutex); } -SWCLT_DECLARE(ks_status_t) swclt_wss_write_cmd(swclt_wss_t wss, swclt_cmd_t cmd) +SWCLT_DECLARE(ks_status_t) swclt_wss_write(swclt_wss_t *wss, char *data) { - SWCLT_WSS_SCOPE_BEG(wss, ctx, status) - char *data; - ks_size_t len, wrote; + ks_size_t len; + ks_ssize_t wrote; + ks_status_t status; /* Ensure we have valid state */ - if (status = swclt_hstate_check_ctx(&ctx->base, "Write denied, invalid state:")) - goto ks_handle_scope_end; - - if (status = swclt_cmd_print(cmd, ctx->base.pool, &data)) { - ks_log(KS_LOG_CRIT, "Invalid command, faild to render payload string: %lu", status); - goto ks_handle_scope_end; + if (wss->failed) { + return KS_STATUS_FAIL; } len = strlen(data); - ks_mutex_lock(ctx->write_mutex); - wrote = kws_write_frame(ctx->wss, WSOC_TEXT, data, len); - ks_mutex_unlock(ctx->write_mutex); - - ks_log(KS_LOG_DEBUG, "Wrote frame: %s", ks_handle_describe(cmd)); + ks_mutex_lock(wss->wss_mutex); + wrote = kws_write_frame(wss->wss, WSOC_TEXT, data, len); + if (wrote > 0) { + wss->stats.write_frames++; + } + ks_mutex_unlock(wss->wss_mutex); - if (len != wrote) + if (wrote < 0 || len != (ks_size_t)wrote) { + ks_log(KS_LOG_WARNING, "Short write to websocket. wrote = %d, len = %u", wrote, len); status = KS_STATUS_FAIL; - else - ks_throughput_report_ex(ctx->rate_send, len, KS_FALSE); - - ks_pool_free(&data); + wss->failed = 1; + if (wss->failed_cb) { + wss->failed_cb(wss, wss->failed_cb_data); + } + } else { + ks_log(KS_LOG_DEBUG, "Wrote frame: %s", data); + status = KS_STATUS_SUCCESS; + } - SWCLT_WSS_SCOPE_END(wss, ctx, status) + return status; } /* For Emacs: diff --git a/swclt_bench.cfg b/swclt_bench.cfg deleted file mode 100644 index 46b0de6..0000000 --- a/swclt_bench.cfg +++ /dev/null @@ -1,20 +0,0 @@ -benchmark: -{ - target_identity = "blade://switchblade:2100"; -}; - -signalwire_client: -{ - transport: - { - wss: - { - ssl: - { - key = "./ca/intermediate/private/controller@freeswitch-upstream.key.pem"; - cert = "./ca/intermediate/certs/controller@freeswitch-upstream.cert.pem"; - chain = "./ca/intermediate/certs/ca-chain.cert.pem"; - }; - }; - }; -}; diff --git a/swclt_bench/CMakeLists.txt b/swclt_bench/CMakeLists.txt deleted file mode 100644 index 7c53f05..0000000 --- a/swclt_bench/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -add_executable(swclt_bench main.c) -target_link_libraries(swclt_bench signalwire_client) -target_include_directories(swclt_bench PUBLIC ${CMAKE_CURRENT_LIST_DIR}) - -set_target_properties(swclt_bench PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) - -configure_file(cfg/swclt_bench.cfg ${CMAKE_BINARY_DIR}/swclt_bench.cfg COPYONLY) diff --git a/swclt_bench/cfg/swclt_bench.cfg b/swclt_bench/cfg/swclt_bench.cfg deleted file mode 100644 index 46b0de6..0000000 --- a/swclt_bench/cfg/swclt_bench.cfg +++ /dev/null @@ -1,20 +0,0 @@ -benchmark: -{ - target_identity = "blade://switchblade:2100"; -}; - -signalwire_client: -{ - transport: - { - wss: - { - ssl: - { - key = "./ca/intermediate/private/controller@freeswitch-upstream.key.pem"; - cert = "./ca/intermediate/certs/controller@freeswitch-upstream.cert.pem"; - chain = "./ca/intermediate/certs/ca-chain.cert.pem"; - }; - }; - }; -}; diff --git a/swclt_bench/main.c b/swclt_bench/main.c deleted file mode 100644 index 590ba9f..0000000 --- a/swclt_bench/main.c +++ /dev/null @@ -1,275 +0,0 @@ -/* - * Copyright (c) 2018 SignalWire, Inc - * - * 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. - */ - -#include "signalwire-client-c/client.h" -#if 0 - -#define REQUIRE(x) ks_assertd(x) - -static swclt_sess_t g_session; -static const char *g_bench_protocol = "bench"; -static const char *g_bench_channel = "swbench"; -static const char *g_bench_event = "report"; -static ks_status_t g_bench_thread_status; -static ks_throughput_t g_throughput_send; -static ks_throughput_t g_throughput_recv; - -#define TEST_DURATION_SEC 50000000 - -/* Define our properties for this benchmark */ -static SSL_CTX * __create_ssl_ctx() -{ - static const char *key = "ca/intermediate/private/client@freeswitch-upstream.key.pem"; - static const char *cert = "ca/intermediate/certs/client@freeswitch-upstream.cert.pem"; - static const char *chain = "ca/intermediate/certs/ca-chain.cert.pem"; - SSL_CTX *ssl; - - REQUIRE(!swclt_ssl_create_context(key, cert, chain, &ssl)); - return ssl; -} - -void render_stats(const char *header) -{ - printf("%s\n", header); - printf("Send: %s\n", ks_handle_describe(g_throughput_send)); - printf("Recv: %s\n", ks_handle_describe(g_throughput_recv)); -} - -void on_bench_event( - swclt_sess_t sess, - blade_broadcast_rqu_t *rqu, - void *cb_data) -{ - ks_json_t *object = BLADE_BROADCAST_RQU_MARSHAL(NULL, *rqu); - - KS_JSON_PRINT("Got bench event:", object); - - ks_json_delete(&object); -} - -SWCLT_JSON_MARSHAL_BEG(THROUGHPUT_STATS, ks_throughput_stats_t) - SWCLT_JSON_MARSHAL_INT(size) - SWCLT_JSON_MARSHAL_INT(count) - SWCLT_JSON_MARSHAL_DOUBLE(rate_size) - SWCLT_JSON_MARSHAL_DOUBLE(rate_count) - SWCLT_JSON_MARSHAL_INT_EX(run_time_sec, run_time) -SWCLT_JSON_MARSHAL_END() - -SWCLT_JSON_DESTROY_BEG(THROUGHPUT_STATS, ks_throughput_stats_t) - SWCLT_JSON_DESTROY_INT(size) - SWCLT_JSON_DESTROY_INT(count) - SWCLT_JSON_DESTROY_DOUBLE(rate_size) - SWCLT_JSON_DESTROY_DOUBLE(rate_count) - SWCLT_JSON_DESTROY_INT_EX(run_time_sec, run_time) -SWCLT_JSON_DESTROY_END() - -SWCLT_JSON_PARSE_BEG(THROUGHPUT_STATS, ks_throughput_stats_t) - SWCLT_JSON_PARSE_INT(size) - SWCLT_JSON_PARSE_INT(count) - SWCLT_JSON_PARSE_DOUBLE(rate_size) - SWCLT_JSON_PARSE_DOUBLE(rate_count) - SWCLT_JSON_PARSE_INT_EX(run_time_sec, run_time) -SWCLT_JSON_PARSE_END() - -/* Define our benchmark payloads */ -typedef struct benchmark_payload_s { - ks_throughput_stats_t *conn_recv_stats; - ks_throughput_stats_t *conn_send_stats; -} benchmark_payload_t; - -SWCLT_JSON_MARSHAL_BEG(BENCHMARK_PAYLOAD, benchmark_payload_t) - SWCLT_JSON_MARSHAL_CUSTOM(THROUGHPUT_STATS, conn_recv_stats) - SWCLT_JSON_MARSHAL_CUSTOM(THROUGHPUT_STATS, conn_send_stats) -SWCLT_JSON_MARSHAL_END() - -SWCLT_JSON_DESTROY_BEG(BENCHMARK_PAYLOAD, benchmark_payload_t) - SWCLT_JSON_DESTROY_CUSTOM(THROUGHPUT_STATS, conn_recv_stats) - SWCLT_JSON_DESTROY_CUSTOM(THROUGHPUT_STATS, conn_send_stats) -SWCLT_JSON_DESTROY_END() - -SWCLT_JSON_PARSE_BEG(BENCHMARK_PAYLOAD, benchmark_payload_t) - SWCLT_JSON_PARSE_CUSTOM(THROUGHPUT_STATS, conn_recv_stats) - SWCLT_JSON_PARSE_CUSTOM(THROUGHPUT_STATS, conn_send_stats) -SWCLT_JSON_PARSE_END() - -ks_json_t * __create_bench_payload() -{ - ks_throughput_stats_t recv, send; - benchmark_payload_t payload = {&recv, &send}; - - REQUIRE(!ks_throughput_stats(g_throughput_recv, &recv)); - REQUIRE(!ks_throughput_stats(g_throughput_send, &send)); - - return BENCHMARK_PAYLOAD_MARSHAL(NULL, payload); -} - -void * bench_thread_run(ks_thread_t *thread, void *data) -{ - ks_time_t start = ks_time_now_sec(); - ks_time_t last_update = start; - - printf("Bench thread is active\n"); - - while (!ks_thread_stop_requested(thread)) { - ks_json_t *bench_payload = __create_bench_payload(); - - if (g_bench_thread_status = swclt_sess_broadcast( - g_session, - g_bench_protocol, - g_bench_channel, - g_bench_event, - bench_payload - )) { - ks_json_delete(&bench_payload); - printf("Bench errored on broadcast: %d\n", g_bench_thread_status); - break; - } - - ks_json_delete(&bench_payload); - - if (ks_time_now_sec() - last_update > 1) { - render_stats("Stats"); - last_update = ks_time_now_sec(); - } - - if ((ks_time_now_sec() - start) >= TEST_DURATION_SEC) - break; - } - - printf("Bench thread exiting with final status: %d\n", g_bench_thread_status); - - return NULL; -} - -ks_status_t do_bench() -{ - ks_thread_t *bench_thread = NULL; - ks_status_t status; - - /* Start a bench thread */ - if (status = ks_thread_create(&bench_thread, bench_thread_run, NULL, NULL)) - return status; - - /* And let it do its thing */ - ks_thread_join(bench_thread); - ks_thread_destroy(&bench_thread); - return g_bench_thread_status; -} - -int main(int argc, char **argv) -{ - static const char *key = "ca/intermediate/private/client@freeswitch-upstream.key.pem"; - static const char *cert = "ca/intermediate/certs/client@freeswitch-upstream.cert.pem"; - static const char *chain = "ca/intermediate/certs/ca-chain.cert.pem"; - swclt_sub_t sub; - ks_status_t status; - const char *identity; - swclt_cfg_t swclt_cfg, bench_cfg; - swclt_sess_info_t info; - - if (status = swclt_init(KS_LOG_LEVEL_DEBUG)) - return status; - - if (status = swclt_cfg_open_ex(&bench_cfg, "swclt_bench.cfg", "benchmark")) - goto done; - if (status = swclt_cfg_open_ex(&swclt_cfg, "swclt_bench.cfg", "signalwire_client")) - goto done; - - if (status = swclt_cfg_get_strval(bench_cfg, "target_identity", &identity)) - goto done; - - printf("swclt_bench starting, connecting to %s\n", identity); - - if (status = swclt_sess_create(&g_session, identity, swclt_cfg)) - goto done; - - /* Watch the session for state changes, anytime it goes normal we'll - * start pumping the benchmark payloads */ - if (status = swclt_handle_register_state_change_listener( - -#define swclt_handle_register_state_change_listener(ctx, cb, handle) __swclt_handle_register_state_change_listener((swclt_handle_base_t *)ctx, (swclt_hstate_change_cb_t)cb, handle) - - /* Initiate the connect phase */ - if (status = swclt_sess_connect(g_session)) - goto done; - - if (status = swclt_sess_info(g_session, &info)) { - printf("Failed to get session info: %d\n", status); - goto done; - } - - printf("Successfully allocated session: %s\n", ks_handle_describe(g_session)); - - /* Grab a copy of the rate reporters so we can report our rate */ - swclt_sess_get_rates(g_session, &g_throughput_recv, &g_throughput_send); - - printf("Publishing bench channel\n"); - - /* Publish a bench channel */ - if (status = swclt_sess_provider_add(g_session, g_bench_protocol, - (blade_channel_t){g_bench_channel, BLADE_ACL_PUBLIC}, - BLADE_ACL_PUBLIC, BLADE_ACL_PUBLIC, BLADE_ACL_PUBLIC)) { - printf("Failed to publish bench protocol: %d\n", status); - goto done; - } - - printf("Successfully published bench channel, subscribing next..."); - - /* And subscribe to it */ - if (status = swclt_sess_subscription_add( - g_session, - g_bench_protocol, - g_bench_channel, - &on_bench_event, - NULL, - &sub)) { - printf("Failed to subscribe to bench channel: %d", status); - goto done; - } - - printf("Successfully subscribed to bench channel, starting bench thread"); -; - /* Kick off the bench loop */ - if (status = do_bench()) { - printf("Bench failed: %d", status); - goto done; - } - - ks_throughput_stop(g_throughput_send); - ks_throughput_stop(g_throughput_recv); - - render_stats("Final stats"); - -done: - ks_handle_destroy(&g_session); - swclt_shutdown(); - - return status; -} - -#endif - -int main(int argc, char **argv) -{ - /** @@ TODo **/ - return -1; -} diff --git a/swclt_test/CMakeLists.txt b/swclt_test/CMakeLists.txt index 3f247a7..3e01745 100644 --- a/swclt_test/CMakeLists.txt +++ b/swclt_test/CMakeLists.txt @@ -17,7 +17,10 @@ add_executable( ) # Link to signal-wire-client/catch/LibPal -target_link_libraries(swclt_test signalwire_client) +target_link_libraries(swclt_test signalwire_client2) + +# Define our exports symbol to key any definitions to toggle the visibility type +set_target_properties(signalwire_client2 PROPERTIES DEFINE_SYMBOL SWCLT_EXPORTS) # Register our tests with cmake add_test(swclt_test swclt_test) diff --git a/swclt_test/cases/callback.c b/swclt_test/cases/callback.c deleted file mode 100644 index 92a9201..0000000 --- a/swclt_test/cases/callback.c +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2018 SignalWire, Inc - * - * 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. - */ -#include "swclt_test.h" - -static ks_bool_t g_called = KS_FALSE; - -void route_add_handler(swclt_sess_t sess, const blade_netcast_rqu_t *rqu, const blade_netcast_route_add_param_t *params) -{ - g_called = KS_TRUE; -} - -void test_callback(ks_pool_t *pool) -{ - swclt_sess_t sess; - swclt_sess_ctx_t *sess_ctx; - swclt_store_t store; - - /* Load the config we expect session to load */ - REQUIRE(swclt_config_get_private_key_path(g_certified_config)); - REQUIRE(swclt_config_get_client_cert_path(g_certified_config)); - REQUIRE(swclt_config_get_cert_chain_path(g_certified_config)); - - REQUIRE(!swclt_sess_create(&sess, g_target_ident_str, g_certified_config)); - - /* Get the node store */ - REQUIRE(!swclt_sess_nodestore(sess, &store)); - - /* Add a store callback */ - REQUIRE(!swclt_store_cb_route_add(store, route_add_handler)); - - /* Go online */ - REQUIRE(!swclt_sess_connect(sess)); - - /* Wait for its low level handle state to be ready */ - while (!swclt_sess_connected(sess)) - ks_sleep(1000); - - ks_log(KS_LOG_INFO, "*** Online!"); - - /* Wait for the netcast */ - ks_sleep_ms(5000); - - REQUIRE(g_called); - - /* Ok we're done */ - ks_handle_destroy(&sess); -} diff --git a/swclt_test/cases/command.c b/swclt_test/cases/command.c index 25bcf9b..71f909c 100644 --- a/swclt_test/cases/command.c +++ b/swclt_test/cases/command.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 SignalWire, Inc + * Copyright (c) 2018-2020 SignalWire, Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,7 +24,7 @@ void test_command_properties(ks_pool_t *pool) { - swclt_cmd_t cmd; + swclt_cmd_t *cmd; uint32_t flags; const char *method; SWCLT_CMD_TYPE type; @@ -35,16 +35,14 @@ void test_command_properties(ks_pool_t *pool) REQUIRE(request != NULL); REQUIRE(!swclt_cmd_create(&cmd, "bobo_method", &request, 5000, 5)); + REQUIRE(cmd); + REQUIRE(cmd->flags == 1); /* bitfields will imit it to known values */ - REQUIRE(!swclt_cmd_flags(cmd, &flags)); - REQUIRE(flags == 1); /* bitfields will imit it to known values */ + REQUIRE(cmd->method); + REQUIRE(!strcmp(cmd->method, "bobo_method")); - REQUIRE(!swclt_cmd_method(cmd, &method)); - REQUIRE(!strcmp(method, "bobo_method")); - - REQUIRE(!swclt_cmd_type(cmd, &type)); - REQUIRE(type == SWCLT_CMD_TYPE_REQUEST); - ks_handle_destroy(&cmd); + REQUIRE(cmd->type == SWCLT_CMD_TYPE_REQUEST); + swclt_cmd_destroy(&cmd); } void test_command(ks_pool_t *pool) diff --git a/swclt_test/cases/connection.c b/swclt_test/cases/connection.c index 1989017..a412b2e 100644 --- a/swclt_test/cases/connection.c +++ b/swclt_test/cases/connection.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2019 SignalWire, Inc + * Copyright (c) 2018-2020 SignalWire, Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,34 +22,50 @@ #include "swclt_test.h" +#include "../../src/connection.c" + static uint32_t g_protocol_response_cb_called; -static ks_status_t __on_incoming_cmd(swclt_conn_t conn, swclt_cmd_t cmd, void *cb_data) +static ks_status_t __on_incoming_cmd(swclt_conn_t *conn, swclt_cmd_t *cmd, void *cb_data) { + printf("ON INCOMING COMMAND\n"); return KS_STATUS_SUCCESS; } -static void __on_protocol_response(swclt_cmd_t cmd, void *cb_data) +static void __on_protocol_timeout_response(swclt_cmd_reply_t *reply, void *cb_data) +{ + REQUIRE(reply); + REQUIRE(reply->type == SWCLT_CMD_TYPE_FAILURE); + REQUIRE(reply->failure_reason); + REQUIRE(reply->failure_status == KS_STATUS_TIMEOUT); + printf("Validated failure code, message: %s\n", reply->failure_reason); + swclt_cmd_reply_destroy(&reply); + g_protocol_response_cb_called++; +} + +static void __on_protocol_result_response(swclt_cmd_reply_t *reply, void *cb_data) { + REQUIRE(swclt_cmd_reply_ok(reply) == KS_STATUS_SUCCESS); + swclt_cmd_reply_destroy(&reply); g_protocol_response_cb_called++; } void test_async(ks_pool_t *pool) { + swclt_cmd_t *cmd; SSL_CTX *ssl = create_ssl_context(); - swclt_conn_t conn; - swclt_cmd_t cmd; - SWCLT_CMD_TYPE cmd_type; - const ks_json_t *result; + swclt_conn_t *conn; ks_json_t *channels; + int i; - REQUIRE(!swclt_conn_connect(&conn, __on_incoming_cmd, NULL, &g_target_ident, NULL, ssl)); + REQUIRE(!swclt_conn_connect(&conn, __on_incoming_cmd, NULL, &g_target_ident, NULL, NULL, NULL, NULL, ssl)); - channels = ks_json_create_array_inline(1, BLADE_CHANNEL_MARSHAL(pool, &(blade_channel_t){"a_channel", 0, 0})); + channels = ks_json_create_array(); + ks_json_add_item_to_array(channels, BLADE_CHANNEL_MARSHAL(&(blade_channel_t){"a_channel", 0, 0})); /* Create an async command (bogus command but will generate a reply at least) */ REQUIRE(cmd = CREATE_BLADE_PROTOCOL_PROVIDER_ADD_CMD_ASYNC( - __on_protocol_response, + __on_protocol_result_response, NULL, "a_protocol", 0, @@ -61,51 +77,37 @@ void test_async(ks_pool_t *pool) NULL)); /* And submit it */ - REQUIRE(!swclt_conn_submit_request(conn, cmd)); + REQUIRE(!swclt_conn_submit_request(conn, &cmd, NULL)); /* Wait for it to respond */ - while (KS_TRUE) { - - REQUIRE(!swclt_cmd_type(cmd, &cmd_type)); - - if (cmd_type != SWCLT_CMD_TYPE_REQUEST) { - break; - } - + for (i = 0; i < 5 && g_protocol_response_cb_called == 0; i++) { ks_sleep_ms(1000); } - - REQUIRE(cmd_type == SWCLT_CMD_TYPE_RESULT); - REQUIRE(!swclt_cmd_result(cmd, &result)); REQUIRE(g_protocol_response_cb_called == 1); - - REQUIRE(ks_handle_valid(cmd)); - - ks_handle_destroy(&conn); - - /* Command should become invalid once we destroy the connection */ - REQUIRE(!ks_handle_valid(cmd)); + swclt_cmd_destroy(&cmd); + swclt_conn_destroy(&conn); swclt_ssl_destroy_context(&ssl); } void test_ttl(ks_pool_t *pool) { SSL_CTX *ssl = create_ssl_context(); - swclt_conn_t conn; - swclt_cmd_t cmd; + swclt_conn_t *conn; + swclt_cmd_t *cmd; SWCLT_CMD_TYPE cmd_type; - swclt_conn_ctx_t *conn_ctx; - swclt_wss_ctx_t *wss_ctx; - swclt_cmd_ctx_t *cmd_ctx; ks_json_t *channels; + int i; + + g_protocol_response_cb_called = 0; - REQUIRE(!swclt_conn_connect(&conn, __on_incoming_cmd, NULL, &g_target_ident, NULL, ssl)); + REQUIRE(!swclt_conn_connect(&conn, __on_incoming_cmd, NULL, &g_target_ident, NULL, NULL, NULL, NULL, ssl)); - channels = ks_json_create_array_inline(1, BLADE_CHANNEL_MARSHAL(pool, &(blade_channel_t){"a_channel", 0, 0})); + channels = ks_json_create_array(); + ks_json_add_item_to_array(channels, BLADE_CHANNEL_MARSHAL(&(blade_channel_t){"b_channel", 0, 0})); REQUIRE(cmd = CREATE_BLADE_PROTOCOL_PROVIDER_ADD_CMD_ASYNC( - __on_protocol_response, + __on_protocol_timeout_response, NULL, - "a_protocol", + "b_protocol", 0, 0, 0, @@ -115,54 +117,70 @@ void test_ttl(ks_pool_t *pool) NULL)); /* Lock the reader so we never get a response, forcing a timeout */ - conn_ctx = conn_get(conn); - wss_ctx = wss_get(conn_ctx->wss); - cmd_ctx = cmd_get(cmd); - REQUIRE(cmd_ctx->response_ttl_ms == BLADE_PROTOCOL_TTL_MS); - REQUIRE(cmd_ctx->flags == BLADE_PROTOCOL_FLAGS); - REQUIRE(!ks_mutex_lock(wss_ctx->write_mutex)); + REQUIRE(cmd->response_ttl_ms == BLADE_PROTOCOL_TTL_MS); + REQUIRE(cmd->flags == BLADE_PROTOCOL_FLAGS); + REQUIRE(!ks_mutex_lock(conn->wss->wss_mutex)); /* And submit it */ - REQUIRE(!swclt_conn_submit_request(conn, cmd)); - - /* Give it 4 seconds */ - ks_sleep_ms(4000); + REQUIRE(!swclt_conn_submit_request(conn, &cmd, NULL)); - { - ks_status_t failure_status; - const char *failure_message; + /* Wait for it to respond */ + for (i = 0; i < 7 && g_protocol_response_cb_called == 0; i++) { + ks_sleep_ms(1000); + } - /* Better have failed */ - REQUIRE(!swclt_cmd_type(cmd, &cmd_type)); + REQUIRE(g_protocol_response_cb_called == 1); - REQUIRE(cmd_type == SWCLT_CMD_TYPE_FAILURE); - REQUIRE(!swclt_cmd_failure_info(cmd, &failure_status, &failure_message)); + /* Don't forget to unlock the poor websocket reader */ + REQUIRE(!ks_mutex_unlock(conn->wss->wss_mutex)); - /* One for this test, and the one prior */ - REQUIRE(g_protocol_response_cb_called == 2); + swclt_cmd_destroy(&cmd); + swclt_conn_destroy(&conn); + swclt_ssl_destroy_context(&ssl); +} - REQUIRE(failure_status == KS_STATUS_TIMEOUT); - printf("Validated failure code, message: %s\n", failure_message); +void test_ttl_heap(ks_pool_t *pool) +{ + swclt_ttl_tracker_t *ttl = NULL; + ttl_tracker_create(pool, &ttl, NULL); + if (ttl->thread) { + ks_thread_request_stop(ttl->thread); + ks_thread_join(ttl->thread); + ks_thread_destroy(&ttl->thread); // don't want it } - /* Don't forget to unlock the poor websocket reader */ - REQUIRE(!ks_mutex_unlock(wss_ctx->write_mutex)); - - cmd_put(&cmd_ctx); - conn_put(&conn_ctx); - wss_put(&wss_ctx); - - REQUIRE(ks_handle_valid(cmd)); + int i; + int min = INT_MAX; + for (i = 0; i < 100; i++) { + ks_uuid_t uuid = { 0 }; + ks_uuid(&uuid); + int expiry = rand(); + if (expiry < min) { + min = expiry; + } + if (ttl_heap_insert(ttl, expiry, uuid) != KS_STATUS_SUCCESS) { + printf("Failed to insert UUID = %s, TTL = %d, min = %d, root = %d, count = %d\n", ks_uuid_thr_str(&uuid), expiry, min, ttl->heap[TTL_HEAP_ROOT].expiry, ttl->count); + } else { + printf("Insert UUID = %s, TTL = %d, min = %d, root = %d, count = %d\n", ks_uuid_thr_str(&uuid), expiry, min, ttl->heap[TTL_HEAP_ROOT].expiry, ttl->count); + } + REQUIRE(ttl->heap[TTL_HEAP_ROOT].expiry == min); + } - ks_handle_destroy(&conn); + min = 0; + while (ttl->count) { + REQUIRE(ttl->heap[TTL_HEAP_ROOT].expiry > 0); + printf("Remove UUID = %s, TTL = %d, count = %d\n", ks_uuid_thr_str(&ttl->heap[TTL_HEAP_ROOT].id), ttl->heap[TTL_HEAP_ROOT].expiry, ttl->count); + REQUIRE(ttl->heap[TTL_HEAP_ROOT].expiry >= min); + min = ttl->heap[TTL_HEAP_ROOT].expiry; + ttl_heap_remove(ttl); + } - /* Command should become invalid once we destroy the connection */ - REQUIRE(!ks_handle_valid(cmd)); - swclt_ssl_destroy_context(&ssl); + ttl_tracker_destroy(&ttl); } void test_connection(ks_pool_t *pool) { test_async(pool); test_ttl(pool); + test_ttl_heap(pool); } diff --git a/swclt_test/cases/execute.c b/swclt_test/cases/execute.c index b4eacdb..46bfa1c 100644 --- a/swclt_test/cases/execute.c +++ b/swclt_test/cases/execute.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 SignalWire, Inc + * Copyright (c) 2018-2022 SignalWire, Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,24 +22,50 @@ #include "swclt_test.h" -static void __on_session_state(swclt_sess_t sess, const swclt_hstate_change_t *state_change_info, ks_cond_t *cond) +static void __on_session_state_provider(swclt_sess_t *sess, void *condV) { + ks_cond_t *cond = (ks_cond_t *)condV; + if (sess->state == SWCLT_STATE_ONLINE) { + ks_json_t *channels = ks_json_create_array(); + swclt_sess_protocol_provider_add(sess, + "test", + BLADE_ACL_SYSTEM, // rpc execute + BLADE_ACL_SYSTEM, // channel subscribe + BLADE_ACL_SYSTEM, // channel broadcast + NULL, // method access overrides + &channels, + 3, + NULL, + NULL); + + swclt_sess_metric_register(sess, "test", 30, 9); + } else if (sess->state == SWCLT_STATE_OFFLINE) { + // Disconnected + } + /* Notify the waiting test of the state change */ ks_cond_lock(cond); ks_cond_broadcast(cond); ks_cond_unlock(cond); } -static ks_status_t __on_incoming_test_execute_rqu(swclt_sess_t sess, swclt_cmd_t cmd, const blade_execute_rqu_t *rqu, void *data) +static void __on_session_state(swclt_sess_t *sess, void *condV) +{ + /* Notify the waiting test of the state change */ + ks_cond_t *cond = (ks_cond_t *)condV; + ks_cond_lock(cond); + ks_cond_broadcast(cond); + ks_cond_unlock(cond); +} + +static ks_status_t __on_incoming_test_execute_rqu(swclt_sess_t *sess, swclt_cmd_t *cmd, const blade_execute_rqu_t *rqu, void *data) { /* Formulate a response */ ks_cond_t *cond = (ks_cond_t *)data; - ks_pool_t *cmd_pool = ks_handle_pool(cmd); - ks_json_t *result = ks_json_pcreate_object(cmd_pool); - ks_json_padd_string_to_object(cmd_pool, result, "reply", "i got it!"); + ks_json_t *result = ks_json_create_object(); + ks_json_add_string_to_object(result, "reply", "i got it!"); ks_json_t *cmd_result = BLADE_EXECUTE_RPL_MARSHAL( - cmd_pool, &(blade_execute_rpl_t){ rqu->requester_nodeid, rqu->responder_nodeid, @@ -47,26 +73,39 @@ static ks_status_t __on_incoming_test_execute_rqu(swclt_sess_t sess, swclt_cmd_t REQUIRE(!swclt_cmd_set_result(cmd, &cmd_result)); - /* Notify the waiting test of the state change */ - ks_cond_lock(cond); - ks_cond_broadcast(cond); - ks_cond_unlock(cond); return KS_STATUS_SUCCESS; } -static void __on_outgoing_test_execute_rpl(swclt_cmd_t cmd, ks_cond_t *cond) + +static ks_status_t __on_incoming_test_execute_rqu_slow(swclt_sess_t *sess, swclt_cmd_t *cmd, const blade_execute_rqu_t *rqu, void *data) { - ks_cond_lock(cond); - ks_cond_broadcast(cond); - ks_cond_unlock(cond); + /* Formulate a response */ + ks_cond_t *cond = (ks_cond_t *)data; + ks_json_t *result = ks_json_create_object(); + ks_json_add_string_to_object(result, "reply", "pong!"); + + ks_json_t *cmd_result = BLADE_EXECUTE_RPL_MARSHAL( + &(blade_execute_rpl_t){ + rqu->requester_nodeid, + rqu->responder_nodeid, + result}); + + ks_sleep_ms(1000); + + REQUIRE(!swclt_cmd_set_result(cmd, &cmd_result)); + + return KS_STATUS_SUCCESS; +} + +static void __on_outgoing_test_execute_rpl(swclt_cmd_reply_t *reply, void *cond) +{ + ks_cond_broadcast((ks_cond_t *)cond); } void test_execute(ks_pool_t *pool) { - swclt_sess_t sess1, sess2; - swclt_cfg_t cfg; + swclt_sess_t *sess1 = NULL, *sess2 = NULL; char *nodeid1, *nodeid2; - swclt_hmon_t sess1_mon, sess2_mon; ks_cond_t *cond; const char *ident; @@ -78,19 +117,25 @@ void test_execute(ks_pool_t *pool) REQUIRE(!swclt_sess_create(&sess2, g_target_ident_str, g_certified_config)); /* Get called back when they're connected */ - REQUIRE(!swclt_hmon_register(&sess1_mon, sess1, __on_session_state, cond)); - REQUIRE(!swclt_hmon_register(&sess2_mon, sess2, __on_session_state, cond)); + REQUIRE(!swclt_sess_set_state_change_cb(sess1, __on_session_state, cond)); + REQUIRE(!swclt_sess_set_state_change_cb(sess2, __on_session_state_provider, cond)); /* On the second session register a execute handler we'll communicate with */ REQUIRE(!swclt_sess_register_protocol_method(sess2, "test", "test.method", __on_incoming_test_execute_rqu, cond)); + REQUIRE(!swclt_sess_register_protocol_method(sess2, "test", "test.slow_method", __on_incoming_test_execute_rqu_slow, cond)); /* Initiate the connect */ REQUIRE(!swclt_sess_connect(sess1)); REQUIRE(!swclt_sess_connect(sess2)); - /* Wait for the state to change, twice */ - ks_cond_wait(cond); - ks_cond_wait(cond); + int i = 15; + while (i-- > 0 && (!swclt_sess_connected(sess1) || !swclt_sess_connected(sess2))) { + ks_cond_timedwait(cond, 1000); + } + REQUIRE(swclt_sess_connected(sess1)); + REQUIRE(swclt_sess_connected(sess2)); + + ks_cond_unlock(cond); /* Load our nodeids for both */ REQUIRE(!swclt_sess_info(sess1, pool, NULL, &nodeid1, NULL)); @@ -98,22 +143,46 @@ void test_execute(ks_pool_t *pool) /* Now execute from the first session to the second session */ ks_json_t *params = ks_json_create_object(); - swclt_cmd_t sess1_exec_cmd; REQUIRE(params); - REQUIRE(ks_json_add_string_to_object(params, "arg", "value")); - REQUIRE(!swclt_sess_execute_async(sess1, nodeid2, "test", "test.method", ¶ms, (swclt_cmd_cb_t)__on_outgoing_test_execute_rpl, cond, &sess1_exec_cmd)); - ks_cond_wait(cond); - ks_cond_wait(cond); - SWCLT_CMD_TYPE type; - REQUIRE(!swclt_cmd_type(sess1_exec_cmd, &type)); - ks_log(KS_LOG_INFO, "Final type is: %s", swclt_cmd_type_str(type)); - REQUIRE(type == SWCLT_CMD_TYPE_RESULT); - - ks_handle_destroy(&sess1_mon); - ks_handle_destroy(&sess2_mon); - - ks_handle_destroy(&sess1); - ks_handle_destroy(&sess2); - ks_handle_destroy(&cfg); + ks_json_add_string_to_object(params, "arg", "value"); + swclt_cmd_future_t *future = NULL; + REQUIRE(!swclt_sess_execute_async(sess1, nodeid2, "test", "test.method", ¶ms, NULL, NULL, &future)); + REQUIRE(future); + swclt_cmd_reply_t *reply = NULL; + REQUIRE(!swclt_sess_wait_for_cmd_reply(sess1, &future, &reply)); + swclt_cmd_reply_destroy(&reply); + + /* repeat, but this time don't wait for any response */ + params = ks_json_create_object(); + REQUIRE(params); + ks_json_add_string_to_object(params, "arg", "value"); + REQUIRE(!swclt_sess_execute_async(sess1, nodeid2, "test", "test.method", ¶ms, NULL, NULL, NULL)); + + ks_sleep_ms(5000); + + /* Now execute from the first session to the second session forcing a disconnect before the response is delivered */ + params = ks_json_create_object(); + REQUIRE(params); + ks_json_add_string_to_object(params, "arg", "value"); + future = NULL; + REQUIRE(!swclt_sess_execute_async(sess1, nodeid2, "test", "test.slow_method", ¶ms, NULL, NULL, &future)); + REQUIRE(future); + + ks_sleep_ms(200); + + /* Disconnect server */ + swclt_sess_disconnect(sess2); + + ks_sleep_ms(2000); + + /* Reconnect server */ + swclt_sess_connect(sess2); + + reply = NULL; + REQUIRE(!swclt_sess_wait_for_cmd_reply(sess1, &future, &reply)); + swclt_cmd_reply_destroy(&reply); + + swclt_sess_destroy(&sess1); + swclt_sess_destroy(&sess2); ks_cond_destroy(&cond); } diff --git a/swclt_test/cases/frame.c b/swclt_test/cases/frame.c deleted file mode 100644 index b1b8cda..0000000 --- a/swclt_test/cases/frame.c +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2018 SignalWire, Inc - * - * 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. - */ - -#include "swclt_test.h" - -void test_frame(ks_pool_t *pool) -{ - /** @@ TOD */ -} diff --git a/swclt_test/cases/hmanager.c b/swclt_test/cases/hmanager.c deleted file mode 100644 index e1ec97d..0000000 --- a/swclt_test/cases/hmanager.c +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (c) 2018 SignalWire, Inc - * - * 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. - */ -#include "swclt_test.h" - -#define now() (ks_time_ms(ks_time_now())) - -/* Define our context */ -typedef struct test_ctx_s { - swclt_handle_base_t base; - ks_time_t create_time; - ks_time_t service_time; - ks_time_t retry_time; - ks_time_t retry_try_1_time; - ks_cond_t *cond; -} test_ctx_t; - -static ks_status_t __context_state_transition(test_ctx_t *ctx, SWCLT_HSTATE new_state) -{ - REQUIRE(new_state == SWCLT_HSTATE_ONLINE); - - if (ctx->service_time == 0) { - /* Lets give it a max of 500ms for the assert */ - REQUIRE((now() - ctx->create_time) <= 500); - ctx->service_time = now(); - - /* Now fail the transition, should retry in ~1 seconds */ - return KS_STATUS_FAIL; - } else if (ctx->retry_time == 0) { - /* Should've servied us within 1.5 seconds */ - REQUIRE((now() - ctx->service_time) <= 1500); - - ctx->retry_time = now(); - - /* Fail it again */ - return KS_STATUS_FAIL; - } else if (ctx->retry_try_1_time == 0) { - - /* Same check, should've servied us within 1.5 seconds */ - REQUIRE((now() - ctx->retry_time) <= 1500); - - ctx->retry_try_1_time = now(); - - /* Ok test complete, signal */ - ks_cond_lock(ctx->cond); - ks_cond_broadcast(ctx->cond); - ks_cond_unlock(ctx->cond); - return KS_STATUS_SUCCESS; - } - - FAIL("Should not have gotten here"); -} - -static ks_status_t __context_init(test_ctx_t *ctx, ks_cond_t *cond) -{ - ctx->cond = cond; - - REQUIRE(ctx->base.state == SWCLT_HSTATE_NORMAL); - REQUIRE(ctx->base.last_state == SWCLT_HSTATE_NORMAL); - - ctx->create_time = now(); - - /* Alright requet service right away */ - swclt_hstate_initiate_change_now( - &ctx->base, - SWCLT_HSTATE_ONLINE, - __context_state_transition, - 1000); /* retry duration */ - - return KS_STATUS_SUCCESS; -} - -static void __context_describe(test_ctx_t *ctx, char *buffer, ks_size_t buffer_len) -{ - snprintf(buffer, buffer_len, "Unit test yo"); -} - -static void __context_deinit(test_ctx_t *ctx) -{ -} - -static ks_status_t allocate(ks_handle_t *handle, ks_cond_t *cond) -{ - SWCLT_HANDLE_ALLOC_TEMPLATE_M( - NULL, - SWCLT_HTYPE_TEST, - handle, - test_ctx_t, - SWCLT_HSTATE_NORMAL, - __context_describe, - __context_deinit, - __context_init, - cond); -} - -void test_hmanager(ks_pool_t *pool) -{ - ks_cond_t *cond; - REQUIRE(!ks_cond_create(&cond, NULL)); - REQUIRE(!ks_cond_lock(cond)); - - /* Create our own handle and test the signalwire client manager's */ - ks_handle_t handle; - REQUIRE(!allocate(&handle, cond)); - - /* Now wait for the test to end, should not take more hten 5 seconds, if so fail */ - if (ks_cond_timedwait(cond, 5000) == KS_STATUS_TIMEOUT) - ks_abort("Test took too long"); - - /* Success destroy it */ - ks_cond_unlock(cond); - ks_handle_destroy(&handle); - ks_cond_destroy(&cond); -} - diff --git a/swclt_test/cases/json.c b/swclt_test/cases/json.c index 55f2e4c..c577d1a 100644 --- a/swclt_test/cases/json.c +++ b/swclt_test/cases/json.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 SignalWire, Inc + * Copyright (c) 2018-2020 SignalWire, Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -33,11 +33,11 @@ static blade_netcast_rqu_t __netcast_protocol_provider_add_request(ks_pool_t *po blade_netcast_protocol_provider_add_param_t params = {0}; params.protocol = protocol; params.nodeid = ks_uuid_str(NULL, &nodeid); - params.channels = ks_json_create_array_inline(1, - BLADE_CHANNEL_MARSHAL(NULL, &(blade_channel_t){channel, BLADE_ACL_PUBLIC, BLADE_ACL_PUBLIC})); + params.channels = ks_json_create_array(); + ks_json_add_item_to_array(params.channels, BLADE_CHANNEL_MARSHAL(&(blade_channel_t){channel, BLADE_ACL_PUBLIC, BLADE_ACL_PUBLIC})); /* Marshal it into its parent request */ - request.params = BLADE_NETCAST_PROTOCOL_PROVIDER_ADD_PARAM_MARSHAL(NULL, ¶ms); + request.params = BLADE_NETCAST_PROTOCOL_PROVIDER_ADD_PARAM_MARSHAL(¶ms); return request; } @@ -45,35 +45,57 @@ static blade_netcast_rqu_t __netcast_protocol_provider_add_request(ks_pool_t *po static void test_blade_execute(ks_pool_t *pool) { ks_json_t *params = ks_json_create_object(); - ks_json_padd_string_to_object(NULL, params, "tag", "hi"); - swclt_cmd_t cmd = CREATE_BLADE_EXECUTE_CMD( + ks_json_add_string_to_object(params, "tag", "hi"); + swclt_cmd_t *cmd = CREATE_BLADE_EXECUTE_CMD( + NULL, "responder_a", "test.protocol", "test.method", ¶ms); REQUIRE(!params); - const ks_json_t *obj; - REQUIRE(swclt_cmd_error(cmd, &obj)); - REQUIRE(swclt_cmd_result(cmd, &obj)); - REQUIRE(!swclt_cmd_request(cmd, &obj)); + char *desc; + REQUIRE(!swclt_cmd_print(cmd, NULL, &desc)); + ks_log(KS_LOG_INFO, "Created command: %s", desc); + + REQUIRE(!strcmp(ks_json_get_object_string(cmd->json, "responder_nodeid", ""), "responder_a")); + REQUIRE(!strcmp(ks_json_get_object_string(cmd->json, "protocol", ""), "test.protocol")); + REQUIRE(!strcmp(ks_json_get_object_string(cmd->json, "method", ""), "test.method")); + REQUIRE(!strcmp(ks_json_get_object_string(ks_json_get_object_item(cmd->json, "params"), "tag", ""), "hi")); + + ks_pool_free(&desc); + swclt_cmd_destroy(&cmd); +} + +static void test_blade_execute_with_id(ks_pool_t *pool) +{ + ks_json_t *params = ks_json_create_object(); + ks_json_add_string_to_object(params, "tag", "hi"); + swclt_cmd_t *cmd = CREATE_BLADE_EXECUTE_CMD( + "a6786101-4c6e-4d1a-ac22-1c5dcbbd3c48", + "responder_a", + "test.protocol", + "test.method", + ¶ms); + REQUIRE(!params); char *desc; REQUIRE(!swclt_cmd_print(cmd, NULL, &desc)); ks_log(KS_LOG_INFO, "Created command: %s", desc); - REQUIRE(!strcmp(ks_json_get_object_cstr(obj, "responder_nodeid"), "responder_a")); - REQUIRE(!strcmp(ks_json_get_object_cstr(obj, "protocol"), "test.protocol")); - REQUIRE(!strcmp(ks_json_get_object_cstr(obj, "method"), "test.method")); - REQUIRE(!strcmp(ks_json_lookup_cstr(obj, 2, "params", "tag"), "hi")); + REQUIRE(!strcmp(cmd->id_str, "a6786101-4c6e-4d1a-ac22-1c5dcbbd3c48")); + REQUIRE(!strcmp(ks_json_get_object_string(cmd->json, "responder_nodeid", ""), "responder_a")); + REQUIRE(!strcmp(ks_json_get_object_string(cmd->json, "protocol", ""), "test.protocol")); + REQUIRE(!strcmp(ks_json_get_object_string(cmd->json, "method", ""), "test.method")); + REQUIRE(!strcmp(ks_json_get_object_string(ks_json_get_object_item(cmd->json, "params"), "tag", ""), "hi")); ks_pool_free(&desc); - ks_handle_destroy(&cmd); + swclt_cmd_destroy(&cmd); } static void test_blade_channel(ks_pool_t *pool) { - ks_json_t *obj = BLADE_CHANNEL_MARSHAL(NULL, &(blade_channel_t){"bobo", BLADE_ACL_PUBLIC, BLADE_ACL_PUBLIC}); + ks_json_t *obj = BLADE_CHANNEL_MARSHAL(&(blade_channel_t){"bobo", BLADE_ACL_PUBLIC, BLADE_ACL_PUBLIC}); ks_json_delete(&obj); } @@ -81,4 +103,5 @@ void test_json(ks_pool_t *pool) { test_blade_channel(pool); test_blade_execute(pool); + test_blade_execute_with_id(pool); } diff --git a/swclt_test/cases/nodestore.c b/swclt_test/cases/nodestore.c index f90fa95..5a2d960 100644 --- a/swclt_test/cases/nodestore.c +++ b/swclt_test/cases/nodestore.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2019 SignalWire, Inc + * Copyright (c) 2018-2020 SignalWire, Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,102 +21,110 @@ */ #include "swclt_test.h" -#include "signalwire-client-c/internal/nodestore.h" ks_uuid_t g_route_nodeid_1, g_route_nodeid_2, g_sessionid; blade_netcast_rqu_t __netcast_protocol_provider_add_request(ks_pool_t *pool, const char *protocol, ks_uuid_t nodeid, const char *channel) { - blade_netcast_rqu_t request; + blade_netcast_rqu_t request = { 0 }; request.command = BLADE_NETCAST_CMD_PROTOCOL_PROVIDER_ADD; request.certified_only = KS_TRUE; request.netcaster_nodeid = ks_uuid_null_str(pool); /* Fill in the params too */ - blade_netcast_protocol_provider_add_param_t params = {0}; + blade_netcast_protocol_provider_add_param_t params = { 0 }; params.protocol = protocol; params.nodeid = ks_uuid_str(pool, &nodeid); - params.channels = ks_json_pcreate_array_inline(pool, 1, - BLADE_CHANNEL_MARSHAL(pool, &(blade_channel_t){channel, BLADE_ACL_PUBLIC, BLADE_ACL_PUBLIC})); + params.channels = ks_json_create_array(); + ks_json_add_item_to_array(params.channels, BLADE_CHANNEL_MARSHAL(&(blade_channel_t){channel, BLADE_ACL_PUBLIC, BLADE_ACL_PUBLIC})); /* Marshal it into its parent request */ - request.params = BLADE_NETCAST_PROTOCOL_PROVIDER_ADD_PARAM_MARSHAL(pool, ¶ms); + request.params = BLADE_NETCAST_PROTOCOL_PROVIDER_ADD_PARAM_MARSHAL(¶ms); return request; } blade_netcast_rqu_t __netcast_route_add_request(ks_pool_t *pool, const char *nodeid, ks_bool_t certified) { - blade_netcast_rqu_t request; + blade_netcast_rqu_t request = { 0 }; request.command = BLADE_NETCAST_CMD_ROUTE_ADD; request.certified_only = KS_TRUE; request.netcaster_nodeid = ks_uuid_null_str(pool); /* Fill in the params too */ - blade_netcast_route_add_param_t params; + blade_netcast_route_add_param_t params = { 0 }; params.certified = certified; params.nodeid = nodeid; /* Marshal it into its parent request */ - request.params = BLADE_NETCAST_ROUTE_ADD_PARAM_MARSHAL(pool, ¶ms); + request.params = BLADE_NETCAST_ROUTE_ADD_PARAM_MARSHAL(¶ms); return request; } blade_netcast_rqu_t __netcast_route_remove_request(ks_pool_t *pool, const char *nodeid) { - blade_netcast_rqu_t request; + blade_netcast_rqu_t request = { 0 }; request.command = BLADE_NETCAST_CMD_ROUTE_REMOVE; request.certified_only = KS_TRUE; request.netcaster_nodeid = ks_uuid_null_str(pool); /* Fill in the params too */ - blade_netcast_route_remove_param_t params; + blade_netcast_route_remove_param_t params = { 0 }; params.nodeid = nodeid; params.certified = KS_TRUE; /* Marshal it into its parent request */ - request.params = BLADE_NETCAST_ROUTE_REMOVE_PARAM_MARSHAL(pool, ¶ms); + request.params = BLADE_NETCAST_ROUTE_REMOVE_PARAM_MARSHAL(¶ms); return request; } -blade_connect_rpl_t __connect_reply(ks_pool_t *pool) +blade_connect_rpl_t *__connect_reply(ks_pool_t *pool) { - ks_json_t *routes = ks_json_pcreate_array(pool); - ks_json_t *protocols = ks_json_pcreate_array(pool); - ks_json_t *subscriptions = ks_json_pcreate_array(pool); - + ks_json_t *routes = ks_json_create_array(); + ks_json_t *protocols = ks_json_create_array(); + ks_json_t *subscriptions = ks_json_create_array(); + /* Add a couple routes */ - ks_json_add_item_to_array(routes, BLADE_NODE_MARSHAL(pool, &(blade_node_t){ks_uuid_str(pool, &g_route_nodeid_1)})); - ks_json_add_item_to_array(routes, BLADE_NODE_MARSHAL(pool, &(blade_node_t){ks_uuid_str(pool, &g_route_nodeid_2)})); + ks_json_add_item_to_array(routes, BLADE_NODE_MARSHAL(&(blade_node_t){ks_uuid_str(pool, &g_route_nodeid_1)})); + ks_json_add_item_to_array(routes, BLADE_NODE_MARSHAL(&(blade_node_t){ks_uuid_str(pool, &g_route_nodeid_2)})); /* Add a couple protocols each with one provider and one channel */ + ks_json_t *channels = ks_json_create_array(); + ks_json_add_item_to_array(channels, BLADE_CHANNEL_MARSHAL(&(blade_channel_t){"bobo_channel_1", BLADE_ACL_PUBLIC, BLADE_ACL_PUBLIC})); + ks_json_t *providers = ks_json_create_array(); + ks_json_add_item_to_array(providers, BLADE_PROVIDER_MARSHAL(&(blade_provider_t){ks_uuid_str(pool, &g_route_nodeid_1), ks_json_create_array()})); ks_json_add_item_to_array(protocols, - BLADE_PROTOCOL_MARSHAL(pool, &(blade_protocol_t){ + BLADE_PROTOCOL_MARSHAL(&(blade_protocol_t){ "bobo_protocol_1", BLADE_ACL_PUBLIC, BLADE_ACL_PUBLIC, BLADE_ACL_PUBLIC, - ks_json_pcreate_array_inline(pool, 1, BLADE_PROVIDER_MARSHAL(pool, &(blade_provider_t){ks_uuid_str(pool, &g_route_nodeid_1), ks_json_pcreate_array(pool)})), - ks_json_pcreate_array_inline(pool, 1, BLADE_CHANNEL_MARSHAL(pool, &(blade_channel_t){"bobo_channel_1", BLADE_ACL_PUBLIC, BLADE_ACL_PUBLIC})) + providers, + channels, })); + channels = ks_json_create_array(); + ks_json_add_item_to_array(channels, BLADE_CHANNEL_MARSHAL(&(blade_channel_t){"bobo_channel_2", BLADE_ACL_PUBLIC, BLADE_ACL_PUBLIC})); + providers = ks_json_create_array(); + ks_json_add_item_to_array(providers, BLADE_PROVIDER_MARSHAL(&(blade_provider_t){ks_uuid_str(pool, &g_route_nodeid_2), ks_json_create_array()})); ks_json_add_item_to_array(protocols, - BLADE_PROTOCOL_MARSHAL(pool, &(blade_protocol_t){ + BLADE_PROTOCOL_MARSHAL(&(blade_protocol_t){ "bobo_protocol_2", BLADE_ACL_PUBLIC, BLADE_ACL_PUBLIC, BLADE_ACL_PUBLIC, - ks_json_pcreate_array_inline(pool, 1, BLADE_PROVIDER_MARSHAL(pool, &(blade_provider_t){ks_uuid_str(pool, &g_route_nodeid_2), ks_json_pcreate_array(pool)})), - ks_json_pcreate_array_inline(pool, 1, BLADE_CHANNEL_MARSHAL(pool, &(blade_channel_t){"bobo_channel_2", BLADE_ACL_PUBLIC, BLADE_ACL_PUBLIC})) + providers, + channels, })); /* Have the second node be subscribed to the first */ + ks_json_t *nodeids = ks_json_create_array(); + ks_json_add_string_to_array(nodeids, ks_uuid_str(pool, &g_route_nodeid_2)); ks_json_add_item_to_array(subscriptions, - BLADE_SUBSCRIPTION_MARSHAL(pool, &(blade_subscription_t){ + BLADE_SUBSCRIPTION_MARSHAL(&(blade_subscription_t){ "bobo_protocol_1", "bobo_channel_1", - ks_json_pcreate_array_inline(pool, 1, - ks_json_pcreate_uuid(pool, g_route_nodeid_2)) + nodeids, })); /* Now compose it altogether in a connect result */ - blade_connect_rpl_t reply = (blade_connect_rpl_t){ + blade_connect_rpl_t reply_s = (blade_connect_rpl_t){ KS_FALSE, *ks_uuid(&g_sessionid), ks_uuid_str(pool, &g_route_nodeid_1), @@ -125,45 +133,45 @@ blade_connect_rpl_t __connect_reply(ks_pool_t *pool) routes, protocols, subscriptions, - ks_json_pcreate_array(pool) + ks_json_create_array() }; + blade_connect_rpl_t *reply = ks_pool_alloc(pool, sizeof (*reply)); + memcpy(reply, &reply_s, sizeof(blade_connect_rpl_t)); // copy to struct allocated off of pool so it can be destroyed return reply; } void test_nodestore_update(ks_pool_t *pool) { - blade_connect_rpl_t connect_rpl = __connect_reply(pool); - swclt_store_t store; - swclt_store_ctx_t *store_ctx; - ks_uuid_t new_route_nodeid; + blade_connect_rpl_t *connect_rpl = __connect_reply(pool); + swclt_store_t *store = NULL; + ks_uuid_t new_route_nodeid = { 0 }; const char *new_route_nodeid_str; ks_uuid(&new_route_nodeid); new_route_nodeid_str = ks_uuid_str(pool, &new_route_nodeid); REQUIRE(!swclt_store_create(&store)); - REQUIRE(!swclt_store_populate(store, &connect_rpl)); - REQUIRE(!swclt_store_get(store, &store_ctx)); + REQUIRE(!swclt_store_populate(store, connect_rpl)); /* The store should properly render the types */ - REQUIRE(ks_hash_count(store_ctx->protocols) == 2); - REQUIRE(ks_hash_count(store_ctx->subscriptions) == 1); - REQUIRE(ks_hash_count(store_ctx->routes) == 2); - REQUIRE(ks_hash_count(store_ctx->authorities) == 0); + REQUIRE(ks_hash_count(store->protocols) == 2); + REQUIRE(ks_hash_count(store->subscriptions) == 1); + REQUIRE(ks_hash_count(store->routes) == 2); + REQUIRE(ks_hash_count(store->authorities) == 0); /* Now update with a new node */ blade_netcast_rqu_t netcast_rqu = __netcast_route_add_request(pool, new_route_nodeid_str, KS_TRUE); REQUIRE(!swclt_store_update(store, &netcast_rqu)); - REQUIRE(ks_hash_count(store_ctx->routes) == 3); - REQUIRE(((blade_node_t *)ks_hash_search(store_ctx->routes, new_route_nodeid_str, KS_UNLOCKED))->certified == KS_TRUE); + REQUIRE(ks_hash_count(store->routes) == 3); + REQUIRE(((blade_node_t *)ks_hash_search(store->routes, new_route_nodeid_str, KS_UNLOCKED))->certified == KS_TRUE); ks_json_delete(&netcast_rqu.params); /* And a new provider with a new provider + channel */ netcast_rqu = __netcast_protocol_provider_add_request(pool, "bobo_protocol_new", new_route_nodeid, "bobo_channel_new"); REQUIRE(!swclt_store_update(store, &netcast_rqu)); - REQUIRE(ks_hash_count(store_ctx->protocols) == 3); + REQUIRE(ks_hash_count(store->protocols) == 3); { - blade_protocol_t *protocol = (blade_protocol_t *)ks_hash_search(store_ctx->protocols, "bobo_protocol_new", KS_UNLOCKED); + blade_protocol_t *protocol = (blade_protocol_t *)ks_hash_search(store->protocols, "bobo_protocol_new", KS_UNLOCKED); REQUIRE(!strcmp(protocol->name, "bobo_protocol_new")); /* Should have one channel in it called something silly */ @@ -181,12 +189,12 @@ void test_nodestore_update(ks_pool_t *pool) /* And remove it, should also remove the protocol */ netcast_rqu = __netcast_route_remove_request(pool, new_route_nodeid_str); REQUIRE(!swclt_store_update(store, &netcast_rqu)); - REQUIRE(ks_hash_count(store_ctx->routes) == 2); - REQUIRE(ks_hash_count(store_ctx->protocols) == 2); + REQUIRE(ks_hash_count(store->routes) == 2); + REQUIRE(ks_hash_count(store->protocols) == 2); ks_json_delete(&netcast_rqu.params); - swclt_store_put(&store_ctx); - ks_handle_destroy(&store); + swclt_store_destroy(&store); + BLADE_CONNECT_RPL_DESTROY(&connect_rpl); } void test_nodestore_protocol_select(ks_pool_t *pool) @@ -196,6 +204,10 @@ void test_nodestore_protocol_select(ks_pool_t *pool) void test_nodestore(ks_pool_t *pool) { + ks_uuid(&g_route_nodeid_1); + ks_uuid(&g_route_nodeid_2); + ks_uuid(&g_sessionid); + test_nodestore_update(pool); test_nodestore_protocol_select(pool); } diff --git a/swclt_test/cases/session.c b/swclt_test/cases/session.c index c6b2d62..8dce496 100644 --- a/swclt_test/cases/session.c +++ b/swclt_test/cases/session.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 SignalWire, Inc + * Copyright (c) 2018-2022 SignalWire, Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,25 +21,19 @@ */ #include "swclt_test.h" -static SWCLT_HSTATE g_last_state_change; static ks_cond_t *g_cond; -static void __on_sess_hmon_event(swclt_sess_t sess, swclt_hstate_change_t *state_change_info, const char *cb_data) +static void __on_sess_state_event(swclt_sess_t *sess, void *cb_data) { - REQUIRE(!strcmp(cb_data, "bobo")); + REQUIRE(!strcmp((char *)cb_data, "foo")); ks_cond_lock(g_cond); - g_last_state_change = state_change_info->new_state; ks_cond_broadcast(g_cond); ks_cond_unlock(g_cond); } -typedef void(*swclt_hstate_change_cb_t)(swclt_handle_base_t *ctx, swclt_hstate_change_t *state_change_request); - void test_session(ks_pool_t *pool) { - swclt_sess_t sess; - swclt_sess_ctx_t *sess_ctx; - swclt_hmon_t hmon; + swclt_sess_t *sess = NULL; REQUIRE(!ks_cond_create(&g_cond, NULL)); @@ -51,49 +45,38 @@ void test_session(ks_pool_t *pool) REQUIRE(!swclt_sess_create(&sess, g_target_ident_str, g_certified_config)); /* Register a monitor to get to know when session comes online successfully */ - REQUIRE(!swclt_hmon_register(&hmon, sess, __on_sess_hmon_event, "bobo")); - - { - ks_handle_t next = 0; - uint32_t count = 0; - - while (KS_STATUS_SUCCESS == ks_handle_enum_type(SWCLT_HTYPE_HMON, &next)) - count++; - - REQUIRE(count == 1); - } + REQUIRE(!swclt_sess_set_state_change_cb(sess, __on_sess_state_event, "foo")); ks_cond_lock(g_cond); /* Now take the session onine */ REQUIRE(!swclt_sess_connect(sess)); - - ks_cond_wait(g_cond); - - /* Should be online */ - REQUIRE(g_last_state_change == SWCLT_HSTATE_ONLINE); + int i = 5; + while (i-- > 0 && !swclt_sess_connected(sess)) { + ks_cond_timedwait(g_cond, 1000); + } + REQUIRE(swclt_sess_connected(sess)); /* Now disconnect the session */ REQUIRE(!swclt_sess_disconnect(sess)); - - /* Should be offline now */ - ks_cond_wait(g_cond); - - REQUIRE(g_last_state_change == SWCLT_HSTATE_OFFLINE); - - /* Now delete the monitor */ - ks_handle_destroy(&hmon); - - ks_cond_unlock(g_cond); + i = 5; + while (i-- > 0 && swclt_sess_connected(sess)) { + ks_cond_timedwait(g_cond, 1000); + } + REQUIRE(!swclt_sess_connected(sess)); /* Go online again */ REQUIRE(!swclt_sess_connect(sess)); + i = 5; + while (i-- > 0 && !swclt_sess_connected(sess)) { + ks_cond_timedwait(g_cond, 1000); + } + REQUIRE(swclt_sess_connected(sess)); + + ks_cond_unlock(g_cond); - /* Wait for its low level handle state to be back to OK */ - while (swclt_hstate_check(sess, NULL)) - ks_sleep(1000); + swclt_sess_destroy(&sess); /* Ok we're done */ ks_cond_destroy(&g_cond); - ks_handle_destroy(&sess); } diff --git a/swclt_test/cases/uncert.c b/swclt_test/cases/uncert.c index 5be511f..93f1290 100644 --- a/swclt_test/cases/uncert.c +++ b/swclt_test/cases/uncert.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 SignalWire, Inc + * Copyright (c) 2018-2020 SignalWire, Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,25 +22,19 @@ #include "swclt_test.h" -static SWCLT_HSTATE g_last_state_change; static ks_cond_t *g_cond; -static void __on_sess_hmon_event(swclt_sess_t sess, swclt_hstate_change_t *state_change_info, const char *cb_data) +static void __on_sess_state_event(swclt_sess_t *sess, void *cb_data) { - REQUIRE(!strcmp(cb_data, "bobo")); + REQUIRE(!strcmp((char *)cb_data, "bobo")); ks_cond_lock(g_cond); - g_last_state_change = state_change_info->new_state; ks_cond_broadcast(g_cond); ks_cond_unlock(g_cond); } -typedef void(*swclt_hstate_change_cb_t)(swclt_handle_base_t *ctx, swclt_hstate_change_t *state_change_request); - void test_uncert_exp(ks_pool_t *pool) { - swclt_sess_t sess; - swclt_sess_ctx_t *sess_ctx; - swclt_hmon_t hmon; + swclt_sess_t *sess; REQUIRE(!ks_cond_create(&g_cond, NULL)); @@ -50,49 +44,38 @@ void test_uncert_exp(ks_pool_t *pool) REQUIRE(!swclt_sess_create(&sess, g_target_ident_str, g_uncertified_config)); /* Register a monitor to get to know when session comes online successfully */ - REQUIRE(!swclt_hmon_register(&hmon, sess, __on_sess_hmon_event, "bobo")); - - { - ks_handle_t next = 0; - uint32_t count = 0; - - while (KS_STATUS_SUCCESS == ks_handle_enum_type(SWCLT_HTYPE_HMON, &next)) - count++; - - REQUIRE(count == 1); - } + REQUIRE(!swclt_sess_set_state_change_cb(sess, __on_sess_state_event, "bobo")); ks_cond_lock(g_cond); /* Now take the session onine */ REQUIRE(!swclt_sess_connect(sess)); - - ks_cond_wait(g_cond); - - /* Should be online */ - REQUIRE(g_last_state_change == SWCLT_HSTATE_ONLINE); + int i = 5; + while (i-- > 0 && !swclt_sess_connected(sess)) { + ks_cond_timedwait(g_cond, 1000); + } + REQUIRE(swclt_sess_connected(sess)); /* Now disconnect the session */ REQUIRE(!swclt_sess_disconnect(sess)); - - /* Should be offline now */ - ks_cond_wait(g_cond); - - REQUIRE(g_last_state_change == SWCLT_HSTATE_OFFLINE); - - /* Now delete the monitor */ - ks_handle_destroy(&hmon); - - ks_cond_unlock(g_cond); + i = 5; + while (i-- > 0 && swclt_sess_connected(sess)) { + ks_cond_timedwait(g_cond, 1000); + } + REQUIRE(!swclt_sess_connected(sess)); /* Go online again */ REQUIRE(!swclt_sess_connect(sess)); + i = 5; + while (i-- > 0 && !swclt_sess_connected(sess)) { + ks_cond_timedwait(g_cond, 1000); + } + REQUIRE(swclt_sess_connected(sess)); + + ks_cond_unlock(g_cond); - /* Wait for its low level handle state to be back to OK */ - while (swclt_hstate_check(sess, NULL)) - ks_sleep(1000); + swclt_sess_destroy(&sess); /* Ok we're done */ ks_cond_destroy(&g_cond); - ks_handle_destroy(&sess); } diff --git a/swclt_test/cases/websocket.c b/swclt_test/cases/websocket.c deleted file mode 100644 index 59b389f..0000000 --- a/swclt_test/cases/websocket.c +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2018 SignalWire, Inc - * - * 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. - */ - -#include "swclt_test.h" - -void test_websocket(ks_pool_t *pool) -{ - /** @@ TODO */ -} diff --git a/swclt_test/cfg/swclt_bench.cfg b/swclt_test/cfg/swclt_bench.cfg deleted file mode 100644 index fefaa53..0000000 --- a/swclt_test/cfg/swclt_bench.cfg +++ /dev/null @@ -1,20 +0,0 @@ -test: -{ - target_identity = "blade://switchblade:2100"; -} - -signalwire_client: -{ - transport: - { - wss: - { - ssl: - { - key = "./ca/intermediate/private/controller@freeswitch-upstream.key.pem"; - cert = "./ca/intermediate/certs/controller@freeswitch-upstream.cert.pem"; - chain = "./ca/intermediate/certs/ca-chain.cert.pem"; - }; - }; - }; -}; diff --git a/swclt_test/main.c b/swclt_test/main.c index 317003d..3fb3d02 100644 --- a/swclt_test/main.c +++ b/swclt_test/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 SignalWire, Inc + * Copyright (c) 2018-2020 SignalWire, Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,29 +21,21 @@ */ #include "swclt_test.h" -DECLARE_TEST(hmanager); DECLARE_TEST(json); -DECLARE_TEST(frame); -DECLARE_TEST(websocket); DECLARE_TEST(command); DECLARE_TEST(execute); DECLARE_TEST(connection); DECLARE_TEST(session); DECLARE_TEST(nodestore); -DECLARE_TEST(callback); DECLARE_TEST(uncert_exp); test_entry_t g_test_methods[] = { - TEST_ENTRY(hmanager), TEST_ENTRY(json), - TEST_ENTRY(frame), - TEST_ENTRY(websocket), TEST_ENTRY(command), TEST_ENTRY(execute), TEST_ENTRY(connection), TEST_ENTRY(session), TEST_ENTRY(nodestore), - TEST_ENTRY(callback), TEST_ENTRY(uncert_exp), }; @@ -145,17 +137,17 @@ int main(int argc, char **argv) swclt_init(KS_LOG_LEVEL_DEBUG); ks_global_set_logger(__test_logger); - certified_config = ks_json_pcreate_object(NULL); - uncertified_config = ks_json_pcreate_object(NULL); + certified_config = ks_json_create_object(); + uncertified_config = ks_json_create_object(); - ks_json_padd_string_to_object(NULL, certified_config, "private_key_path", "./ca/intermediate/private/controller@freeswitch-upstream.key.pem"); - ks_json_padd_string_to_object(NULL, certified_config, "client_cert_path", "./ca/intermediate/certs/controller@freeswitch-upstream.cert.pem"); - ks_json_padd_string_to_object(NULL, certified_config, "cert_chain_path", "./ca/intermediate/certs/ca-chain.cert.pem"); + ks_json_add_string_to_object(certified_config, "private_key_path", "./ca/intermediate/private/controller@freeswitch-upstream.key.pem"); + ks_json_add_string_to_object(certified_config, "client_cert_path", "./ca/intermediate/certs/controller@freeswitch-upstream.cert.pem"); + ks_json_add_string_to_object(certified_config, "cert_chain_path", "./ca/intermediate/certs/ca-chain.cert.pem"); swclt_config_create(&g_certified_config); swclt_config_load_from_json(g_certified_config, certified_config); - ks_json_padd_string_to_object(NULL, uncertified_config, "authentication", "{ \"project\": \"06f784c6-6bd5-47fb-9897-407d66551333\", \"token\": \"PT2eddbccd77832e761d191513df8945d4e1bf70e8f3f74aaa\" }"); + ks_json_add_string_to_object(uncertified_config, "authentication", "{ \"project\": \"06f784c6-6bd5-47fb-9897-407d66551333\", \"token\": \"PT2eddbccd77832e761d191513df8945d4e1bf70e8f3f74aaa\" }"); swclt_config_create(&g_uncertified_config); swclt_config_load_from_json(g_uncertified_config, uncertified_config); diff --git a/swclt_test/swclt_test.h b/swclt_test/swclt_test.h index bf8e201..109cbf6 100644 --- a/swclt_test/swclt_test.h +++ b/swclt_test/swclt_test.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 SignalWire, Inc + * Copyright (c) 2018-2020 SignalWire, Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -57,4 +57,3 @@ typedef struct test_entry_s { /* Load some utils */ #include "util/ssl.h" -#include "util/handle.h" diff --git a/swclt_test/util/handle.h b/swclt_test/util/handle.h deleted file mode 100644 index de08f03..0000000 --- a/swclt_test/util/handle.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2018 SignalWire, Inc - * - * 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. - */ - -#pragma once - -/* Load all internals for unit testing */ -#include "signalwire-client-c/internal/command.h" -#include "signalwire-client-c/internal/connection.h" -#include "signalwire-client-c/internal/session.h" -#include "signalwire-client-c/internal/subscription.h" -#include "signalwire-client-c/transport/internal/frame.h" -#include "signalwire-client-c/transport/internal/websocket.h" - -/* Create uber handle shortcuts for testing */ -#define SWCLT_TEST_HANDLE_GET_DEF(name) \ -static inline swclt_##name##_ctx_t * name##_get(ks_handle_t handle) \ -{ \ - swclt_##name##_ctx_t *ctx; \ - REQUIRE(!swclt_##name##_get(handle, &ctx)); \ - return ctx; \ -} \ - \ -static inline void name##_put(swclt_##name##_ctx_t **ctx) \ -{ \ - REQUIRE(!swclt_##name##_put(ctx)); \ -} - -SWCLT_TEST_HANDLE_GET_DEF(sess) -SWCLT_TEST_HANDLE_GET_DEF(conn) -SWCLT_TEST_HANDLE_GET_DEF(cmd) -SWCLT_TEST_HANDLE_GET_DEF(frame) -SWCLT_TEST_HANDLE_GET_DEF(wss) -SWCLT_TEST_HANDLE_GET_DEF(sub) - diff --git a/win/buildpackages.task b/win/buildpackages.task index 924112a..524c93b 100644 --- a/win/buildpackages.task +++ b/win/buildpackages.task @@ -64,13 +64,13 @@ using System.Diagnostics; if (!FileOrDirectoryExists(package_root)) { Directory.CreateDirectory(package_root + @"\binaries\$(Platform)\$(Configuration)"); - string[] files = {"signalwire_client.lib", @"signalwire_client.dll"}; + string[] files = {"signalwire_client2.lib", @"signalwire_client2.dll"}; foreach (string f in files) { File.Copy(@"..\Build-$(Platform)\$(Configuration)\" + f, package_root + @"\binaries\$(Platform)\$(Configuration)\" + f); } if ("$(Configuration.ToLower())" == "debug" ) { - string[] debug_files = {"signalwire_client.pdb"}; + string[] debug_files = {"signalwire_client2.pdb"}; foreach (string f in debug_files) { File.Copy(@"..\Build-$(Platform)\$(Configuration)\" + f, package_root + @"\binaries\$(Platform)\$(Configuration)\" + f); } diff --git a/win/libks-version.props b/win/libks-version.props index c1ecaef..9e013ef 100644 --- a/win/libks-version.props +++ b/win/libks-version.props @@ -4,8 +4,8 @@ - 1.8.3 - 0 + 2.0.0 + 1 true diff --git a/win/libks.props b/win/libks.props index 45b1658..c2bf690 100644 --- a/win/libks.props +++ b/win/libks.props @@ -46,7 +46,7 @@ - $(libksDir)\src\include;%(AdditionalIncludeDirectories) - __PRETTY_FUNCTION__=__FUNCSIG__;WIN32;_WINDOWS;SWCLT_VERSION_MAJOR=1;SWCLT_VERSION_MINOR=0;SWCLT_VERSION_REVISION=0;_WIN32_WINNT=0x0600;_WINSOCK_DEPRECATED_NO_WARNINGS=1;WIN32_LEAN_AND_MEAN=1;KS_PLAT_WIN=1;NOMAXMIN=1;_CRT_SECURE_NO_WARNINGS=1;SWCLT_EXPORTS;%(PreprocessorDefinitions) + $(libksDir)\libks\src\include;%(AdditionalIncludeDirectories) + __KS_FUNC__=__FUNCSIG__;WIN32;_WINDOWS;SWCLT_VERSION_MAJOR=1;SWCLT_VERSION_MINOR=0;SWCLT_VERSION_REVISION=0;_WIN32_WINNT=0x0600;_WINSOCK_DEPRECATED_NO_WARNINGS=1;WIN32_LEAN_AND_MEAN=1;KS_PLAT_WIN=1;NOMAXMIN=1;_CRT_SECURE_NO_WARNINGS=1;SWCLT_EXPORTS;%(PreprocessorDefinitions) $(libksDir)\binaries\$(Platform)\$(Configuration)\;%(AdditionalLibraryDirectories) diff --git a/win/signalwire-client-c-version.props b/win/signalwire-client-c-version.props index c5ccd8c..b7062e6 100644 --- a/win/signalwire-client-c-version.props +++ b/win/signalwire-client-c-version.props @@ -4,8 +4,8 @@ - 1.3.4 - 0 + 2.0.0 + 1 $(BaseDir)../ diff --git a/win/signalwire-client-c.vcxproj b/win/signalwire-client-c.vcxproj index 9454202..b2f00ed 100644 --- a/win/signalwire-client-c.vcxproj +++ b/win/signalwire-client-c.vcxproj @@ -50,7 +50,7 @@ - - + + \ No newline at end of file