├── .clang-format ├── .github └── workflows │ ├── cpp.yml │ └── rust.yml ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── include ├── rpc_core.hpp └── rpc_core │ ├── config.hpp │ ├── connection.hpp │ ├── detail │ ├── callable │ │ ├── callable.hpp │ │ ├── function.hpp │ │ ├── functor.hpp │ │ ├── helpers.hpp │ │ └── member_function.hpp │ ├── coder.hpp │ ├── coder_varint.hpp │ ├── copyable.hpp │ ├── data_packer.hpp │ ├── log.h │ ├── msg_dispatcher.hpp │ ├── msg_wrapper.hpp │ ├── noncopyable.hpp │ ├── string_view.hpp │ ├── type_traits.hpp │ └── varint.hpp │ ├── dispose.hpp │ ├── plugin │ ├── flatbuffers.hpp │ ├── json.hpp │ └── json_msg.hpp │ ├── request.hpp │ ├── request.ipp │ ├── request_response.hpp │ ├── result.hpp │ ├── rpc.hpp │ ├── rpc.ipp │ ├── serialize.hpp │ ├── serialize │ ├── binary_wrap.hpp │ ├── detail │ │ └── auto_size.hpp │ ├── std_array.hpp │ ├── std_basic_string.hpp │ ├── std_bitset.hpp │ ├── std_chrono.hpp │ ├── std_complex.hpp │ ├── std_container_adaptors.hpp │ ├── std_forward_list.hpp │ ├── std_list_like.hpp │ ├── std_map.hpp │ ├── std_pair.hpp │ ├── std_set.hpp │ ├── std_shared_ptr.hpp │ ├── std_tuple.hpp │ ├── std_unique_ptr.hpp │ ├── type_enum.hpp │ ├── type_ptr.hpp │ ├── type_raw.hpp │ ├── type_struct.hpp │ └── type_void.hpp │ ├── serialize_api.hpp │ ├── serialize_nlohmann_json.hpp │ ├── serialize_type.hpp │ ├── type.hpp │ └── version.hpp ├── library.json ├── rust ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md └── src │ ├── connection.rs │ ├── detail │ ├── coder.rs │ ├── mod.rs │ ├── msg_dispatcher.rs │ └── msg_wrapper.rs │ ├── dispose.rs │ ├── examples │ ├── rpc_client.rs │ ├── rpc_server.rs │ ├── tcp_client.rs │ └── tcp_server.rs │ ├── lib.rs │ ├── net │ ├── config.rs │ ├── config_builder.rs │ ├── detail │ │ ├── mod.rs │ │ └── tcp_channel.rs │ ├── mod.rs │ ├── rpc_client.rs │ ├── rpc_server.rs │ ├── tcp_client.rs │ └── tcp_server.rs │ ├── request.rs │ ├── rpc.rs │ ├── tests │ ├── net_rpc.rs │ ├── net_tcp.rs │ └── rpc.rs │ └── type_def.rs └── test ├── assert_def.h ├── main.cpp ├── plugin ├── JsonType.h ├── RawType.h └── fb │ ├── .clang-format │ ├── FbMsg.fbs │ └── FbMsg_generated.h ├── serialize ├── CustomType.h └── TestStruct.h ├── test.h ├── test_data_packer.cpp ├── test_plugin.cpp ├── test_rpc.cpp └── test_serialize.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | Language: Cpp 2 | BasedOnStyle: Google 3 | ColumnLimit: 150 4 | AllowShortFunctionsOnASingleLine: Empty 5 | AllowShortLambdasOnASingleLine: Empty 6 | -------------------------------------------------------------------------------- /.github/workflows/cpp.yml: -------------------------------------------------------------------------------- 1 | name: cpp 2 | 3 | on: 4 | push: 5 | paths-ignore: 6 | - '**.md' 7 | - 'rust/**' 8 | pull_request: 9 | paths-ignore: 10 | - '**.md' 11 | - 'rust/**' 12 | 13 | jobs: 14 | build-and-test: 15 | 16 | name: ${{ matrix.toolchain }}-${{ matrix.configuration }} 17 | runs-on: ${{ matrix.os }} 18 | 19 | strategy: 20 | fail-fast: false 21 | 22 | matrix: 23 | toolchain: 24 | - linux-gcc 25 | - macos-clang 26 | - windows-msvc 27 | - windows-mingw 28 | 29 | configuration: 30 | - Debug 31 | - Release 32 | 33 | include: 34 | - toolchain: linux-gcc 35 | os: ubuntu-latest 36 | compiler: gcc 37 | env: 38 | CMAKE_OPTIONS: "-DRPC_CORE_TEST_PLUGIN=ON" 39 | 40 | - toolchain: macos-clang 41 | os: macos-latest 42 | compiler: clang 43 | env: 44 | CMAKE_OPTIONS: "-DRPC_CORE_TEST_PLUGIN=ON" 45 | 46 | - toolchain: windows-msvc 47 | os: windows-latest 48 | compiler: msvc 49 | env: 50 | BIN_SUFFIX: ".exe" 51 | 52 | - toolchain: windows-mingw 53 | os: windows-latest 54 | compiler: mingw 55 | env: 56 | BIN_SUFFIX: ".exe" 57 | CMAKE_OPTIONS: "-G \"MinGW Makefiles\"" 58 | 59 | steps: 60 | - uses: actions/checkout@v4 61 | 62 | - name: Configure (${{ matrix.configuration }}) 63 | run: cmake -S . -B build -DCMAKE_BUILD_TYPE=${{ matrix.configuration }} ${{ matrix.env.CMAKE_OPTIONS }} 64 | 65 | - name: Init thirdparty 66 | if: matrix.os != 'windows-latest' 67 | run: cmake --build build --target rpc_core_test_init 68 | 69 | - name: Build with ${{ matrix.compiler }} 70 | run: cmake --build build --config ${{ matrix.configuration }} -j 71 | 72 | - name: Windows-MSVC Compatible 73 | if: matrix.os == 'windows-latest' && matrix.compiler == 'msvc' 74 | working-directory: build 75 | run: Move-Item -Path .\${{ matrix.configuration }}\* -Destination .\ 76 | 77 | - name: Test 78 | working-directory: build 79 | run: ./rpc_core_test${{ matrix.env.BIN_SUFFIX }} 80 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: rust 2 | 3 | on: 4 | push: 5 | paths-ignore: 6 | - '**.md' 7 | - 'include/**' 8 | - 'test/**' 9 | pull_request: 10 | paths-ignore: 11 | - '**.md' 12 | - 'include/**' 13 | - 'test/**' 14 | 15 | env: 16 | RUST_BACKTRACE: 1 17 | 18 | jobs: 19 | Linux: 20 | runs-on: ubuntu-latest 21 | steps: 22 | - uses: actions/checkout@v3 23 | 24 | - name: cargo fmt -- --check 25 | working-directory: rust 26 | run: cargo fmt -- --check 27 | 28 | - name: build 29 | working-directory: rust 30 | run: cargo build 31 | 32 | - name: test build 33 | working-directory: rust 34 | run: cargo test build 35 | 36 | - name: rpc 37 | working-directory: rust 38 | run: cargo test --test rpc 39 | 40 | - name: net_tcp 41 | working-directory: rust 42 | run: cargo test --test net_tcp --features="net" 43 | 44 | - name: net_rpc 45 | working-directory: rust 46 | run: cargo test --test net_rpc --features="net" 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | 3 | # clion 4 | .idea/ 5 | cmake-build-*/ 6 | 7 | # qt creator 8 | CMakeLists.txt.user 9 | 10 | # vscode 11 | .vscode/ 12 | 13 | # thirdparty 14 | /thirdparty/ 15 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | project(rpc_core CXX) 4 | 5 | # config 6 | option(RPC_CORE_SERIALIZE_USE_CUSTOM "" "") 7 | option(RPC_CORE_SERIALIZE_USE_NLOHMANN_JSON "" OFF) 8 | option(RPC_CORE_FEATURE_FUTURE "" OFF) 9 | option(RPC_CORE_FEATURE_CO_ASIO "" OFF) 10 | option(RPC_CORE_FEATURE_CODER_VARINT "" OFF) 11 | 12 | # test 13 | option(RPC_CORE_BUILD_TEST "" OFF) 14 | option(RPC_CORE_TEST_PLUGIN "" OFF) 15 | option(RPC_CORE_TEST_LINK_PTHREAD "" OFF) 16 | 17 | if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) 18 | set(RPC_CORE_BUILD_TEST ON) 19 | set(RPC_CORE_FEATURE_FUTURE ON) 20 | endif () 21 | 22 | set(CMAKE_CXX_STANDARD 14) 23 | if (MSVC) 24 | add_compile_options(/Zc:preprocessor) 25 | add_compile_options(/utf-8) 26 | add_compile_options(-DNOMINMAX) 27 | else () 28 | add_compile_options(-Wall -Wunused-parameter) 29 | endif () 30 | 31 | add_library(${PROJECT_NAME} INTERFACE) 32 | target_include_directories(${PROJECT_NAME} INTERFACE include) 33 | 34 | if (RPC_CORE_SERIALIZE_USE_CUSTOM) 35 | target_compile_definitions(${PROJECT_NAME} INTERFACE -DRPC_CORE_SERIALIZE_USE_CUSTOM="${RPC_CORE_SERIALIZE_USE_CUSTOM}") 36 | endif () 37 | 38 | if (RPC_CORE_SERIALIZE_USE_NLOHMANN_JSON) 39 | target_compile_definitions(${PROJECT_NAME} INTERFACE -DRPC_CORE_SERIALIZE_USE_NLOHMANN_JSON) 40 | endif () 41 | 42 | if (RPC_CORE_FEATURE_FUTURE) 43 | target_compile_definitions(${PROJECT_NAME} INTERFACE -DRPC_CORE_FEATURE_FUTURE) 44 | endif () 45 | 46 | if (RPC_CORE_FEATURE_CO_ASIO) 47 | target_compile_definitions(${PROJECT_NAME} INTERFACE -DRPC_CORE_FEATURE_CO_ASIO) 48 | endif () 49 | 50 | if (RPC_CORE_FEATURE_CODER_VARINT) 51 | target_compile_definitions(${PROJECT_NAME} INTERFACE -DRPC_CORE_FEATURE_CODER_VARINT) 52 | endif () 53 | 54 | if (RPC_CORE_BUILD_TEST) 55 | set(EXAMPLE_COMPILE_DEFINE 56 | ANDROID_STANDALONE 57 | RPC_CORE_LOG_SHOW_DEBUG 58 | # RPC_CORE_LOG_SHOW_VERBOSE 59 | ) 60 | 61 | set(TARGET_NAME ${PROJECT_NAME}_test) 62 | file(GLOB SRCS test/main.cpp test/test_rpc.cpp test/test_serialize.cpp test/test_data_packer.cpp) 63 | add_executable(${TARGET_NAME}) 64 | if (RPC_CORE_TEST_LINK_PTHREAD) 65 | # some linux platform need link -pthread for std::future api 66 | set(LIBRARIES -pthread) 67 | endif () 68 | target_link_libraries(${TARGET_NAME} ${PROJECT_NAME} ${LIBRARIES}) 69 | target_compile_definitions(${TARGET_NAME} PRIVATE ${EXAMPLE_COMPILE_DEFINE}) 70 | 71 | if (RPC_CORE_TEST_PLUGIN) 72 | list(APPEND SRCS test/test_plugin.cpp) 73 | target_compile_definitions(${TARGET_NAME} PRIVATE "RPC_CORE_TEST_PLUGIN") 74 | add_custom_target(${TARGET_NAME}_init 75 | # clear dir 76 | COMMAND rm -rf ${CMAKE_CURRENT_LIST_DIR}/thirdparty 77 | # json 78 | COMMAND mkdir -p ${CMAKE_CURRENT_LIST_DIR}/thirdparty/nlohmann 79 | COMMAND curl -L -o ${CMAKE_CURRENT_LIST_DIR}/thirdparty/nlohmann/json.hpp https://github.com/nlohmann/json/releases/download/v3.11.2/json.hpp 80 | # flatbuffers 81 | COMMAND git clone https://github.com/google/flatbuffers.git --depth=1 --branch=v23.1.21 ${CMAKE_CURRENT_LIST_DIR}/thirdparty/flatbuffers 82 | WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} 83 | ) 84 | 85 | target_include_directories(${TARGET_NAME} PRIVATE thirdparty) 86 | target_include_directories(${TARGET_NAME} PRIVATE thirdparty/flatbuffers/include) 87 | endif () 88 | target_sources(${TARGET_NAME} PRIVATE ${SRCS}) 89 | endif () 90 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023-2025 liushuai 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /include/rpc_core.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // first include 4 | #include "rpc_core/serialize.hpp" 5 | 6 | // other include 7 | #include "rpc_core/connection.hpp" 8 | #include "rpc_core/dispose.hpp" 9 | #include "rpc_core/request.hpp" 10 | #include "rpc_core/rpc.hpp" 11 | 12 | // impl 13 | #include "rpc_core/request.ipp" 14 | #include "rpc_core/rpc.ipp" 15 | -------------------------------------------------------------------------------- /include/rpc_core/config.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "version.hpp" 4 | -------------------------------------------------------------------------------- /include/rpc_core/connection.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | // config 8 | #include "config.hpp" 9 | 10 | // include 11 | #include "detail/data_packer.hpp" 12 | #include "detail/noncopyable.hpp" 13 | 14 | namespace rpc_core { 15 | 16 | /** 17 | * Defines interfaces for sending and receiving messages 18 | * Usage: 19 | * 1. Both sending and receiving should ensure that a complete package of data is sent/received. 20 | * 2. Call on_recv_package when a package of data is actually received. 21 | * 3. Provide the implementation of sending data, send_package_impl. 22 | */ 23 | struct connection : detail::noncopyable { 24 | std::function send_package_impl; 25 | std::function on_recv_package; 26 | }; 27 | 28 | /** 29 | * Default connection avoid crash 30 | */ 31 | struct default_connection : connection { 32 | default_connection() { 33 | send_package_impl = [](const std::string &payload) { 34 | RPC_CORE_LOGE("need send_package_impl: %zu", payload.size()); 35 | }; 36 | on_recv_package = [](const std::string &payload) { 37 | RPC_CORE_LOGE("need on_recv_package: %zu", payload.size()); 38 | }; 39 | } 40 | }; 41 | 42 | /** 43 | * Loopback connection for testing 44 | */ 45 | struct loopback_connection : public connection { 46 | static std::pair, std::shared_ptr> create() { 47 | auto c1 = std::make_shared(); 48 | auto c1_weak = std::weak_ptr(c1); 49 | auto c2 = std::make_shared(); 50 | auto c2_weak = std::weak_ptr(c2); 51 | c1->send_package_impl = [c2_weak](std::string package) { 52 | c2_weak.lock()->on_recv_package(std::move(package)); 53 | }; 54 | c2->send_package_impl = [c1_weak](std::string package) { 55 | c1_weak.lock()->on_recv_package(std::move(package)); 56 | }; 57 | return std::make_pair(c1, c2); 58 | } 59 | }; 60 | 61 | /** 62 | * Stream connection 63 | * for bytes stream: tcp socket, serial port, etc. 64 | */ 65 | struct stream_connection : public connection { 66 | explicit stream_connection(uint32_t max_body_size = UINT32_MAX) : data_packer_(max_body_size) { 67 | send_package_impl = [this](const std::string &package) { 68 | auto payload = data_packer_.pack(package); 69 | send_bytes_impl(std::move(payload)); 70 | }; 71 | data_packer_.on_data = [this](std::string payload) { 72 | on_recv_package(std::move(payload)); 73 | }; 74 | on_recv_bytes = [this](const void *data, size_t size) { 75 | data_packer_.feed(data, size); 76 | }; 77 | } 78 | 79 | /** 80 | * should call on connected or disconnected 81 | */ 82 | void reset() { 83 | data_packer_.reset(); 84 | } 85 | 86 | public: 87 | std::function send_bytes_impl; 88 | std::function on_recv_bytes; 89 | 90 | private: 91 | detail::data_packer data_packer_; 92 | }; 93 | 94 | } // namespace rpc_core 95 | -------------------------------------------------------------------------------- /include/rpc_core/detail/callable/callable.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "function.hpp" 7 | #include "functor.hpp" 8 | #include "helpers.hpp" 9 | #include "member_function.hpp" 10 | 11 | namespace rpc_core { 12 | 13 | // There are three basic kinds of callable types 14 | // function types 15 | struct function_tag {}; 16 | // function pointer types 17 | struct function_ptr_tag {}; 18 | // classes with operator() 19 | struct functor_tag {}; 20 | 21 | namespace detail { 22 | 23 | /** Define traits for a operator() member function pointer type */ 24 | 25 | // classes with operator() 26 | template 27 | struct callable_traits : functor_traits { 28 | typedef functor_tag callable_category; 29 | }; 30 | 31 | // functions 32 | template 33 | struct callable_traits : function_traits { 34 | typedef function_tag callable_category; 35 | }; 36 | 37 | // function pointers 38 | template 39 | struct callable_traits : function_traits { 40 | typedef function_ptr_tag callable_category; 41 | }; 42 | 43 | } // namespace detail 44 | 45 | // Main template 46 | 47 | /** Traits for a callable (function/functor/lambda/...) */ 48 | template 49 | struct callable_traits : detail::callable_traits> {}; 50 | 51 | /** Convert a callable to a std::function<> */ 52 | template 53 | std::function::function_type> to_stdfunction(Callable fun) { 54 | std::function::function_type> stdfun(std::forward(fun)); 55 | return stdfun; 56 | } 57 | 58 | } // namespace rpc_core 59 | -------------------------------------------------------------------------------- /include/rpc_core/detail/callable/function.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "helpers.hpp" 6 | 7 | namespace rpc_core { 8 | 9 | namespace detail { 10 | 11 | namespace { 12 | 13 | /** Define traits for a function type */ 14 | template 15 | struct function_traits; 16 | 17 | template 18 | struct function_traits { 19 | typedef Ret function_type(Args...); 20 | typedef Ret return_type; 21 | static constexpr std::size_t argc = types_count::value; 22 | 23 | template 24 | using argument_type = typename types_n::type; 25 | }; 26 | 27 | template 28 | const std::size_t function_traits::argc; 29 | 30 | } // namespace 31 | 32 | } // namespace detail 33 | 34 | template 35 | struct function_traits : detail::function_traits> {}; 36 | 37 | template 38 | struct function_traits : detail::function_traits> {}; 39 | 40 | } // namespace rpc_core 41 | -------------------------------------------------------------------------------- /include/rpc_core/detail/callable/functor.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "function.hpp" 4 | #include "helpers.hpp" 5 | #include "member_function.hpp" 6 | 7 | namespace rpc_core { 8 | 9 | namespace detail { 10 | 11 | template 12 | using call_operator_traits = member_function_traits; 13 | 14 | // classes with operator() 15 | template 16 | struct functor_traits : function_traits::function_type> { 17 | typedef call_operator_traits call_operator; 18 | }; 19 | 20 | } // namespace detail 21 | 22 | template 23 | struct functor_traits : detail::functor_traits> {}; 24 | 25 | } // namespace rpc_core 26 | -------------------------------------------------------------------------------- /include/rpc_core/detail/callable/helpers.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace rpc_core { 6 | 7 | namespace detail { 8 | 9 | /** Remove reference and cv qualification */ 10 | template 11 | using remove_cvref_t = typename std::remove_cv::type>::type; 12 | 13 | /** Count the number of types given to the template */ 14 | template 15 | struct types_count; 16 | 17 | template <> 18 | struct types_count<> { 19 | static constexpr std::size_t value = 0; 20 | }; 21 | 22 | template 23 | struct types_count { 24 | static constexpr std::size_t value = types_count::value + 1; 25 | }; 26 | 27 | /** Get the nth type given to the template */ 28 | template 29 | struct types_n; 30 | 31 | template 32 | struct types_n : types_n {}; 33 | 34 | template 35 | struct types_n<0, Type, Types...> { 36 | typedef Type type; 37 | }; 38 | 39 | /** Test if a type is in a list given types */ 40 | template 41 | struct types_has; 42 | 43 | template 44 | struct types_has { 45 | static constexpr bool value = false; 46 | }; 47 | 48 | template 49 | struct types_has { 50 | static constexpr bool value = true; 51 | }; 52 | 53 | template 54 | struct types_has : types_has {}; 55 | 56 | } // namespace detail 57 | 58 | } // namespace rpc_core 59 | -------------------------------------------------------------------------------- /include/rpc_core/detail/callable/member_function.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "helpers.hpp" 4 | 5 | namespace rpc_core { 6 | 7 | namespace detail { 8 | 9 | // Tags for member function qualifiers 10 | struct const_tag {}; 11 | struct volatile_tag {}; 12 | struct lref_tag {}; 13 | struct rref_tag {}; 14 | struct noexcept_tag {}; 15 | 16 | namespace { 17 | 18 | template 19 | struct member_function_traits_q : function_traits { 20 | typedef Class class_type; 21 | static constexpr bool is_const = types_has::value; 22 | static constexpr bool is_volatile = types_has::value; 23 | static constexpr bool is_lref = types_has::value; 24 | static constexpr bool is_rref = types_has::value; 25 | #if __cpp_noexcept_function_type 26 | static constexpr bool is_noexcept = types_has::value; 27 | #endif 28 | }; 29 | 30 | // We need these until C++17 in case someone takes the address of one 31 | // of those static variables or passses it by reference to a function 32 | template 33 | const bool member_function_traits_q::is_const; 34 | template 35 | const bool member_function_traits_q::is_volatile; 36 | template 37 | const bool member_function_traits_q::is_lref; 38 | template 39 | const bool member_function_traits_q::is_rref; 40 | #if __cpp_noexcept_function_type 41 | template 42 | const bool member_function_traits_q::is_noexcept; 43 | #endif 44 | 45 | } // namespace 46 | 47 | template 48 | struct member_function_traits; 49 | 50 | template 51 | struct member_function_traits : member_function_traits_q {}; 52 | 53 | template 54 | struct member_function_traits : member_function_traits_q {}; 55 | 56 | template 57 | struct member_function_traits : member_function_traits_q {}; 58 | 59 | template 60 | struct member_function_traits : member_function_traits_q {}; 61 | 62 | template 63 | struct member_function_traits : member_function_traits_q {}; 64 | 65 | template 66 | struct member_function_traits : member_function_traits_q {}; 67 | 68 | template 69 | struct member_function_traits : member_function_traits_q {}; 70 | 71 | template 72 | struct member_function_traits 73 | : member_function_traits_q {}; 74 | 75 | template 76 | struct member_function_traits : member_function_traits_q {}; 77 | 78 | template 79 | struct member_function_traits : member_function_traits_q {}; 80 | 81 | template 82 | struct member_function_traits : member_function_traits_q {}; 83 | 84 | template 85 | struct member_function_traits 86 | : member_function_traits_q {}; 87 | 88 | #if __cpp_noexcept_function_type 89 | template 90 | struct member_function_traits : member_function_traits_q {}; 91 | 92 | template 93 | struct member_function_traits : member_function_traits_q {}; 94 | 95 | template 96 | struct member_function_traits : member_function_traits_q { 97 | }; 98 | 99 | template 100 | struct member_function_traits 101 | : member_function_traits_q {}; 102 | 103 | template 104 | struct member_function_traits : member_function_traits_q {}; 105 | 106 | template 107 | struct member_function_traits 108 | : member_function_traits_q {}; 109 | 110 | template 111 | struct member_function_traits 112 | : member_function_traits_q {}; 113 | 114 | template 115 | struct member_function_traits 116 | : member_function_traits_q {}; 117 | 118 | template 119 | struct member_function_traits : member_function_traits_q {}; 120 | 121 | template 122 | struct member_function_traits 123 | : member_function_traits_q {}; 124 | 125 | template 126 | struct member_function_traits 127 | : member_function_traits_q {}; 128 | 129 | template 130 | struct member_function_traits 131 | : member_function_traits_q {}; 132 | #endif // __cpp_noexcept_function_type 133 | 134 | } // namespace detail 135 | 136 | template 137 | struct member_function_traits : detail::member_function_traits> {}; 138 | 139 | } // namespace rpc_core 140 | -------------------------------------------------------------------------------- /include/rpc_core/detail/coder.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef RPC_CORE_FEATURE_CODER_VARINT 4 | #include "coder_varint.hpp" 5 | #else 6 | #include "msg_wrapper.hpp" 7 | 8 | namespace rpc_core { 9 | namespace detail { 10 | 11 | class coder { 12 | public: 13 | static std::string serialize(const msg_wrapper& msg) { 14 | std::string payload; 15 | payload.reserve(PayloadMinLen + msg.cmd.size() + msg.data.size()); 16 | payload.append((char*)&msg.seq, 4); 17 | auto cmd_len = (uint16_t)msg.cmd.length(); 18 | payload.append((char*)&cmd_len, 2); 19 | payload.append((char*)msg.cmd.data(), cmd_len); 20 | payload.append((char*)&msg.type, 1); 21 | if (msg.request_payload) { 22 | payload.append(*msg.request_payload); 23 | } else { 24 | payload.append(msg.data); 25 | } 26 | return payload; 27 | } 28 | 29 | static msg_wrapper deserialize(const std::string& payload, bool& ok) { 30 | msg_wrapper msg; 31 | if (payload.size() < PayloadMinLen) { 32 | ok = false; 33 | return msg; 34 | } 35 | char* p = (char*)payload.data(); 36 | const char* pend = payload.data() + payload.size(); 37 | msg.seq = *(seq_type*)p; 38 | p += 4; 39 | uint16_t cmd_len = *(uint16_t*)p; 40 | p += 2; 41 | if (p + cmd_len + 1 > pend) { 42 | ok = false; 43 | return msg; 44 | } 45 | msg.cmd.assign(p, cmd_len); 46 | p += cmd_len; 47 | msg.type = *(msg_wrapper::msg_type*)(p); 48 | p += 1; 49 | msg.data.assign(p, pend - p); 50 | ok = true; 51 | return msg; 52 | } 53 | 54 | private: 55 | static const uint8_t PayloadMinLen = 4 /*seq*/ + 2 /*cmd_len*/ + 1 /*type*/; 56 | }; 57 | 58 | } // namespace detail 59 | } // namespace rpc_core 60 | #endif 61 | -------------------------------------------------------------------------------- /include/rpc_core/detail/coder_varint.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "msg_wrapper.hpp" 4 | #include "varint.hpp" 5 | 6 | namespace rpc_core { 7 | namespace detail { 8 | 9 | class coder { 10 | public: 11 | static std::string serialize(const msg_wrapper& msg) { 12 | std::string payload; 13 | std::string v_seq = to_varint(msg.seq); 14 | std::string v_cmd_len = to_varint(msg.cmd.length()); 15 | payload.reserve(v_seq.size() + v_cmd_len.size() + sizeof(msg.type) + msg.cmd.size() + msg.data.size()); 16 | payload.append(v_seq); 17 | payload.append(v_cmd_len); 18 | payload.append(msg.cmd); 19 | payload.append((char*)&msg.type, sizeof(msg.type)); 20 | if (msg.request_payload) { 21 | payload.append(*msg.request_payload); 22 | } else { 23 | payload.append(msg.data); 24 | } 25 | return payload; 26 | } 27 | 28 | static msg_wrapper deserialize(const std::string& payload, bool& ok) { 29 | msg_wrapper msg; 30 | char* p = (char*)payload.data(); 31 | const char* pend = payload.data() + payload.size(); 32 | uint8_t bytes; 33 | msg.seq = from_varint(p, &bytes); 34 | p += bytes; 35 | uint16_t cmd_len = from_varint(p, &bytes); 36 | p += bytes; 37 | if (p + cmd_len + sizeof(msg.type) > pend) { 38 | ok = false; 39 | return msg; 40 | } 41 | msg.cmd.assign(p, cmd_len); 42 | p += cmd_len; 43 | msg.type = *(msg_wrapper::msg_type*)(p); 44 | p += sizeof(msg.type); 45 | msg.data.assign(p, pend - p); 46 | ok = true; 47 | return msg; 48 | } 49 | }; 50 | 51 | } // namespace detail 52 | } // namespace rpc_core 53 | -------------------------------------------------------------------------------- /include/rpc_core/detail/copyable.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace rpc_core { 4 | namespace detail { 5 | 6 | class copyable { 7 | public: 8 | copyable(const copyable&) = default; 9 | copyable& operator=(const copyable&) = default; 10 | 11 | protected: 12 | copyable() = default; 13 | ~copyable() = default; 14 | }; 15 | 16 | } // namespace detail 17 | } // namespace rpc_core 18 | -------------------------------------------------------------------------------- /include/rpc_core/detail/data_packer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | // #define RPC_CORE_LOG_SHOW_VERBOSE 8 | #include "log.h" 9 | #include "noncopyable.hpp" 10 | 11 | namespace rpc_core { 12 | namespace detail { 13 | 14 | class data_packer : detail::noncopyable { 15 | public: 16 | explicit data_packer(uint32_t max_body_size = UINT32_MAX) : max_body_size_(max_body_size) {} 17 | 18 | public: 19 | bool pack(const void *data, size_t size, const std::function &cb) const { 20 | if (size > max_body_size_) { 21 | return false; 22 | } 23 | auto ret = cb(&size, 4); 24 | if (!ret) return false; 25 | ret = cb(data, size); 26 | if (!ret) return false; 27 | 28 | return true; 29 | } 30 | 31 | std::string pack(const void *data, size_t size) const { 32 | std::string payload; 33 | if (size > max_body_size_) { 34 | RPC_CORE_LOGW("size > max_body_size: %zu > %u", size, max_body_size_); 35 | return payload; 36 | } 37 | payload.insert(0, (char *)&size, 4); 38 | payload.insert(payload.size(), (char *)data, size); 39 | return payload; 40 | } 41 | 42 | std::string pack(const std::string &data) const { 43 | return pack(data.data(), data.size()); 44 | } 45 | 46 | public: 47 | bool feed(const void *data, size_t size) { // NOLINT(misc-no-recursion) 48 | if (body_size_ != 0) { 49 | feed_body(data, size); 50 | return true; 51 | } 52 | 53 | /// wait header(4 bytes) 54 | if (header_len_now_ + size < 4) { 55 | buffer_.insert(buffer_.size(), (char *)data, size); 56 | header_len_now_ += (uint32_t)size; 57 | return true; 58 | } 59 | 60 | /// herder data ready, start read body 61 | // 1. read header, aka: body size 62 | uint8_t header_len = 4 - header_len_now_; 63 | size_t body_len = size - header_len; 64 | for (int i = 0; i < header_len; ++i) { 65 | buffer_.push_back(((char *)data)[i]); 66 | } 67 | body_size_ = *(uint32_t *)(buffer_.data()); 68 | buffer_.clear(); 69 | RPC_CORE_LOGV("feed: wait body_size: %u", body_size_); 70 | if (body_size_ > max_body_size_) { 71 | RPC_CORE_LOGW("body_size > max_body_size: %u > %u", body_size_, max_body_size_); 72 | reset(); 73 | return false; 74 | } 75 | 76 | // 2. feed body 77 | if (body_len != 0) { 78 | feed_body((char *)data + header_len, body_len); 79 | } 80 | return true; 81 | } 82 | 83 | void reset() { 84 | buffer_.clear(); 85 | buffer_.shrink_to_fit(); 86 | header_len_now_ = 0; 87 | body_size_ = 0; 88 | } 89 | 90 | private: 91 | void feed_body(const void *data, size_t size) { // NOLINT(misc-no-recursion) 92 | if (buffer_.size() + size < body_size_) { 93 | buffer_.insert(buffer_.size(), (char *)data, size); 94 | } else { 95 | size_t body_need = body_size_ - buffer_.size(); 96 | size_t body_left = size - body_need; 97 | buffer_.insert(buffer_.size(), (char *)data, body_need); 98 | if (on_data) on_data(std::move(buffer_)); 99 | 100 | reset(); 101 | 102 | if (body_left != 0) { 103 | feed((char *)data + body_need, body_left); 104 | } 105 | } 106 | } 107 | 108 | public: 109 | std::function on_data; 110 | 111 | private: 112 | uint32_t max_body_size_; 113 | std::string buffer_; 114 | 115 | uint32_t header_len_now_ = 0; 116 | uint32_t body_size_ = 0; 117 | }; 118 | 119 | } // namespace detail 120 | } // namespace rpc_core 121 | -------------------------------------------------------------------------------- /include/rpc_core/detail/msg_dispatcher.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "../connection.hpp" 8 | #include "coder.hpp" 9 | #include "log.h" 10 | #include "noncopyable.hpp" 11 | 12 | namespace rpc_core { 13 | namespace detail { 14 | 15 | class msg_dispatcher : public std::enable_shared_from_this, noncopyable { 16 | public: 17 | using cmd_handle = std::function(msg_wrapper)>; 18 | using rsp_handle = std::function; 19 | 20 | using timeout_cb = std::function; 21 | using timer_impl = std::function; 22 | 23 | public: 24 | explicit msg_dispatcher(std::shared_ptr conn) : conn_(std::move(conn)) {} 25 | 26 | void init() { 27 | conn_->on_recv_package = ([self = std::weak_ptr(shared_from_this())](const std::string& payload) { 28 | auto self_lock = self.lock(); 29 | if (!self_lock) { 30 | RPC_CORE_LOGD("msg_dispatcher expired"); 31 | return; 32 | } 33 | bool success; 34 | auto msg = coder::deserialize(payload, success); 35 | if (success) { 36 | self_lock->dispatch(std::move(msg)); 37 | } else { 38 | RPC_CORE_LOGE("payload deserialize error"); 39 | } 40 | }); 41 | } 42 | 43 | private: 44 | void dispatch(msg_wrapper msg) { 45 | switch (msg.type & (msg_wrapper::command | msg_wrapper::response)) { 46 | case msg_wrapper::command: { 47 | // ping 48 | const bool is_ping = msg.type & msg_wrapper::ping; 49 | if (is_ping) { 50 | RPC_CORE_LOGD("<= seq:%u type:ping", msg.seq); 51 | msg.type = static_cast(msg_wrapper::response | msg_wrapper::pong); 52 | RPC_CORE_LOGD("=> seq:%u type:pong", msg.seq); 53 | conn_->send_package_impl(coder::serialize(msg)); 54 | return; 55 | } 56 | 57 | // command 58 | RPC_CORE_LOGD("<= seq:%u cmd:%s", msg.seq, msg.cmd.c_str()); 59 | const auto& cmd = msg.cmd; 60 | auto it = cmd_handle_map_.find(cmd); 61 | if (it == cmd_handle_map_.cend()) { 62 | RPC_CORE_LOGD("not subscribe cmd for: %s", cmd.c_str()); 63 | const bool need_rsp = msg.type & msg_wrapper::need_rsp; 64 | if (need_rsp) { 65 | RPC_CORE_LOGD("=> seq:%u type:rsp", msg.seq); 66 | msg_wrapper rsp; 67 | rsp.seq = msg.seq; 68 | rsp.type = static_cast(msg_wrapper::msg_type::response | msg_wrapper::msg_type::no_such_cmd); 69 | conn_->send_package_impl(coder::serialize(rsp)); 70 | } 71 | return; 72 | } 73 | const auto& fn = it->second; 74 | const bool need_rsp = msg.type & msg_wrapper::need_rsp; 75 | auto resp = fn(std::move(msg)); 76 | if (need_rsp) { 77 | auto state = resp.second.response_state; 78 | switch (state) { 79 | case msg_wrapper::response_state::serialize_error: { 80 | RPC_CORE_LOGW("=> seq:%u serialize_error", resp.second.seq); 81 | } break; 82 | case msg_wrapper::response_state::response_sync: { 83 | RPC_CORE_LOGD("=> seq:%u type:rsp", resp.second.seq); 84 | conn_->send_package_impl(coder::serialize(resp.second)); 85 | } break; 86 | case msg_wrapper::response_state::response_async: { 87 | RPC_CORE_LOGD("=> seq:%u type:rsp_async", resp.second.seq); 88 | if (resp.second.async_helper->is_ready()) { 89 | resp.second.data = resp.second.async_helper->get_data(); 90 | resp.second.async_helper->is_ready = nullptr; 91 | resp.second.async_helper->get_data = nullptr; 92 | conn_->send_package_impl(coder::serialize(resp.second)); 93 | } else { 94 | auto helper = resp.second.async_helper.get(); 95 | helper->send_async_response = [c = std::weak_ptr(conn_), mw = std::move(resp.second)](std::string data) mutable { 96 | mw.data = std::move(data); 97 | auto conn = c.lock(); 98 | if (conn) { 99 | conn->send_package_impl(coder::serialize(mw)); 100 | } 101 | }; 102 | } 103 | } break; 104 | } 105 | } 106 | } break; 107 | 108 | case msg_wrapper::response: { 109 | // pong or response 110 | RPC_CORE_LOGD("<= seq:%u type:%s", msg.seq, (msg.type & detail::msg_wrapper::msg_type::pong) ? "pong" : "rsp"); 111 | auto it = rsp_handle_map_.find(msg.seq); 112 | if (it == rsp_handle_map_.cend()) { 113 | RPC_CORE_LOGD("no rsp for seq:%u", msg.seq); 114 | break; 115 | } 116 | const auto& cb = it->second; 117 | if (!cb) { 118 | RPC_CORE_LOGE("rsp can not be null"); 119 | return; 120 | } 121 | if (cb(std::move(msg))) { 122 | RPC_CORE_LOGV("rsp_handle_map_.size=%zu", rsp_handle_map_.size()); 123 | } else { 124 | RPC_CORE_LOGE("may deserialize error"); 125 | } 126 | rsp_handle_map_.erase(it); 127 | } break; 128 | 129 | default: 130 | RPC_CORE_LOGE("unknown type"); 131 | } 132 | } 133 | 134 | public: 135 | inline void subscribe_cmd(const cmd_type& cmd, cmd_handle handle) { 136 | RPC_CORE_LOGD("subscribe cmd:%s", cmd.c_str()); 137 | cmd_handle_map_[cmd] = std::move(handle); 138 | } 139 | 140 | void unsubscribe_cmd(const cmd_type& cmd) { 141 | auto it = cmd_handle_map_.find(cmd); 142 | if (it != cmd_handle_map_.cend()) { 143 | RPC_CORE_LOGD("erase cmd:%s", cmd.c_str()); 144 | cmd_handle_map_.erase(it); 145 | } else { 146 | RPC_CORE_LOGD("not subscribe cmd for: %s", cmd.c_str()); 147 | } 148 | } 149 | 150 | void subscribe_rsp(seq_type seq, rsp_handle handle, timeout_cb timeout_cb, uint32_t timeout_ms) { 151 | RPC_CORE_LOGD("subscribe_rsp seq:%u", seq); 152 | if (handle == nullptr) return; 153 | 154 | if (timer_impl_ == nullptr) { 155 | RPC_CORE_LOGW("no timeout will cause memory leak!"); 156 | return; 157 | } 158 | 159 | rsp_handle_map_[seq] = std::move(handle); 160 | timer_impl_(timeout_ms, [self = std::weak_ptr(shared_from_this()), seq, timeout_cb = std::move(timeout_cb)] { 161 | auto self_lock = self.lock(); 162 | if (!self_lock) { 163 | RPC_CORE_LOGD("seq:%u timeout after destroy", seq); 164 | return; 165 | } 166 | auto it = self_lock->rsp_handle_map_.find(seq); 167 | if (it != self_lock->rsp_handle_map_.cend()) { 168 | if (timeout_cb) { 169 | timeout_cb(); 170 | } 171 | self_lock->rsp_handle_map_.erase(seq); 172 | RPC_CORE_LOGV("Timeout seq=%d, rsp_handle_map_.size=%zu", seq, this->rsp_handle_map_.size()); 173 | } 174 | }); 175 | } 176 | 177 | inline void set_timer_impl(timer_impl timer_impl) { 178 | timer_impl_ = std::move(timer_impl); 179 | } 180 | 181 | private: 182 | std::shared_ptr conn_; 183 | std::map cmd_handle_map_; 184 | std::map rsp_handle_map_; 185 | timer_impl timer_impl_; 186 | }; 187 | 188 | } // namespace detail 189 | } // namespace rpc_core 190 | -------------------------------------------------------------------------------- /include/rpc_core/detail/msg_wrapper.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "../type.hpp" 7 | #include "copyable.hpp" 8 | #include "log.h" 9 | #include "msg_wrapper.hpp" 10 | 11 | namespace rpc_core { 12 | namespace detail { 13 | 14 | struct async_helper : noncopyable { 15 | std::function is_ready; 16 | std::function get_data; 17 | std::function send_async_response; 18 | }; 19 | using async_helper_s = std::shared_ptr; 20 | 21 | struct msg_wrapper : copyable { // NOLINT 22 | enum msg_type : uint8_t { 23 | command = 1 << 0, 24 | response = 1 << 1, 25 | need_rsp = 1 << 2, 26 | ping = 1 << 3, 27 | pong = 1 << 4, 28 | no_such_cmd = 1 << 5, 29 | }; 30 | 31 | enum class response_state : uint8_t { 32 | response_sync = 1 << 0, 33 | response_async = 1 << 1, 34 | serialize_error = 1 << 2, 35 | }; 36 | 37 | seq_type seq; 38 | cmd_type cmd; 39 | msg_type type; 40 | std::string data; 41 | std::string const* request_payload = nullptr; 42 | 43 | response_state response_state; 44 | async_helper_s async_helper; 45 | 46 | std::string dump() const { 47 | char tmp[100]; 48 | snprintf(tmp, 100, "seq:%u, type:%u, cmd:%s", seq, type, cmd.c_str()); 49 | return tmp; 50 | } 51 | 52 | template 53 | std::pair unpack_as() const { 54 | T message; 55 | bool ok = deserialize(data, message); 56 | if (!ok) { 57 | RPC_CORE_LOGE("deserialize error, msg info:%s", dump().c_str()); 58 | } 59 | return std::make_pair(ok, std::move(message)); 60 | } 61 | 62 | template 63 | static std::pair make_rsp(seq_type seq, T* t = nullptr, bool success = true) { 64 | msg_wrapper msg; 65 | msg.type = msg_wrapper::response; 66 | msg.seq = seq; 67 | if (success && t != nullptr) { 68 | msg.data = serialize(*t); 69 | } 70 | msg.response_state = success ? response_state::response_sync : response_state::serialize_error; 71 | return std::make_pair(success, std::move(msg)); 72 | } 73 | 74 | static std::pair make_rsp_async(seq_type seq, detail::async_helper_s async_helper, bool success = true) { 75 | msg_wrapper msg; 76 | msg.type = msg_wrapper::response; 77 | msg.seq = seq; 78 | msg.async_helper = std::move(async_helper); 79 | msg.response_state = success ? response_state::response_async : response_state::serialize_error; 80 | return std::make_pair(success, std::move(msg)); 81 | } 82 | }; 83 | 84 | } // namespace detail 85 | } // namespace rpc_core 86 | -------------------------------------------------------------------------------- /include/rpc_core/detail/noncopyable.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace rpc_core { 4 | namespace detail { 5 | 6 | class noncopyable { 7 | public: 8 | noncopyable(const noncopyable&) = delete; 9 | noncopyable& operator=(const noncopyable&) = delete; 10 | 11 | protected: 12 | noncopyable() = default; 13 | ~noncopyable() = default; 14 | }; 15 | 16 | } // namespace detail 17 | } // namespace rpc_core 18 | -------------------------------------------------------------------------------- /include/rpc_core/detail/string_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace rpc_core { 7 | namespace detail { 8 | 9 | class string_view { 10 | public: 11 | string_view(const char* data, size_t size) : data_(data), size_(size) {} 12 | string_view(const std::string& data) : data_(data.data()), size_(data.size()) {} // NOLINT 13 | const char* data() const { 14 | return data_; 15 | } 16 | size_t size() const { 17 | return size_; 18 | } 19 | 20 | private: 21 | const char* data_; 22 | size_t size_; 23 | }; 24 | 25 | } // namespace detail 26 | } // namespace rpc_core 27 | -------------------------------------------------------------------------------- /include/rpc_core/detail/type_traits.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace rpc_core { 6 | namespace detail { 7 | 8 | template 9 | struct all_base_of { 10 | static constexpr bool value = std::is_base_of::value && all_base_of::value; 11 | }; 12 | 13 | template 14 | struct all_base_of { 15 | static constexpr bool value = std::is_base_of::value; 16 | }; 17 | 18 | template 19 | constexpr const T& min(const T& a, const T& b) { 20 | return a < b ? a : b; 21 | } 22 | 23 | } // namespace detail 24 | } // namespace rpc_core 25 | -------------------------------------------------------------------------------- /include/rpc_core/detail/varint.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace rpc_core { 8 | namespace detail { 9 | 10 | static const uint8_t MSB = 0x80; 11 | static const uint8_t MSB_ALL = ~0x7F; 12 | 13 | inline uint8_t* varint_encode(unsigned long long n, uint8_t* buf, uint8_t* bytes) { 14 | uint8_t* ptr = buf; 15 | while (n & MSB_ALL) { 16 | *(ptr++) = (n & 0xFF) | MSB; 17 | n = n >> 7; 18 | } 19 | *ptr = n; 20 | *bytes = ptr - buf + 1; 21 | return buf; 22 | } 23 | 24 | inline unsigned long long varint_decode(uint8_t* buf, uint8_t* bytes) { 25 | unsigned long long result = 0; 26 | int bits = 0; 27 | uint8_t* ptr = buf; 28 | unsigned long long ll; 29 | while (*ptr & MSB) { 30 | ll = *ptr; 31 | result += ((ll & 0x7F) << bits); 32 | ptr++; 33 | bits += 7; 34 | } 35 | ll = *ptr; 36 | result += ((ll & 0x7F) << bits); 37 | *bytes = ptr - buf + 1; 38 | return result; 39 | } 40 | 41 | inline std::string to_varint(uint32_t var) { 42 | uint8_t buf[sizeof(uint32_t) + 1]; // enough 43 | std::string ret; 44 | uint8_t bytes; 45 | varint_encode(var, buf, &bytes); 46 | return {(char*)buf, bytes}; 47 | } 48 | 49 | inline uint32_t from_varint(void* data, uint8_t* bytes) { 50 | return varint_decode((uint8_t*)data, bytes); 51 | ; 52 | } 53 | 54 | } // namespace detail 55 | } // namespace rpc_core 56 | -------------------------------------------------------------------------------- /include/rpc_core/dispose.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | // #define RPC_CORE_LOG_SHOW_VERBOSE 8 | 9 | // config 10 | #include "config.hpp" 11 | 12 | // include 13 | #include "detail/noncopyable.hpp" 14 | #include "request.hpp" 15 | 16 | namespace rpc_core { 17 | 18 | class dispose : detail::noncopyable { 19 | public: 20 | static std::shared_ptr create() { 21 | return std::make_shared(); 22 | } 23 | 24 | public: 25 | void add(const request_s& request) { 26 | RPC_CORE_LOGV("add: ptr:%p", request.get()); 27 | requests_.push_back(request_w{request}); 28 | } 29 | 30 | void remove(const request_s& request) { 31 | RPC_CORE_LOGV("remove: ptr:%p", request.get()); 32 | auto iter = std::remove_if(requests_.begin(), requests_.end(), [&](request_w& param) { 33 | auto r = param.lock(); 34 | if (!r) return true; 35 | return r == request; 36 | }); 37 | requests_.erase(iter, requests_.end()); 38 | } 39 | 40 | void dismiss() { 41 | for (const auto& item : requests_) { 42 | auto r = item.lock(); 43 | if (r) { 44 | r->cancel(); 45 | } 46 | } 47 | requests_.clear(); 48 | } 49 | 50 | ~dispose() { 51 | RPC_CORE_LOGD("~dispose: size:%zu", requests_.size()); 52 | dismiss(); 53 | } 54 | 55 | private: 56 | std::vector requests_; 57 | }; 58 | 59 | using dispose_s = std::shared_ptr; 60 | 61 | } // namespace rpc_core 62 | -------------------------------------------------------------------------------- /include/rpc_core/plugin/flatbuffers.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "flatbuffers/flatbuffers.h" 4 | #include "rpc_core/serialize.hpp" 5 | 6 | namespace rpc_core { 7 | 8 | template ::value, int>::type = 0> 9 | serialize_oarchive& operator>>(const T& t, serialize_oarchive& oa) { 10 | using TableType = typename T::TableType; 11 | 12 | flatbuffers::FlatBufferBuilder fbb(1024); 13 | auto offset = TableType::Pack(fbb, &t); 14 | fbb.Finish(offset); 15 | auto data = fbb.GetBufferPointer(); 16 | auto size = fbb.GetSize(); 17 | oa.data.append((char*)data, size); 18 | return oa; 19 | } 20 | 21 | template ::value, int>::type = 0> 22 | serialize_iarchive& operator<<(T& t, serialize_iarchive& ia) { 23 | using TableType = typename T::TableType; 24 | 25 | flatbuffers::Verifier verifier((uint8_t*)ia.data, ia.size); 26 | bool ok = verifier.VerifyBuffer(); 27 | if (!ok) { 28 | ia.error = true; 29 | return ia; 30 | } 31 | flatbuffers::GetRoot(ia.data)->UnPackTo(&t); 32 | return ia; 33 | } 34 | 35 | } // namespace rpc_core 36 | -------------------------------------------------------------------------------- /include/rpc_core/plugin/json.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "nlohmann/json.hpp" 4 | #include "rpc_core/detail/log.h" 5 | #include "rpc_core/serialize.hpp" 6 | 7 | namespace rpc_core { 8 | 9 | template ::value, int>::type = 0> 10 | serialize_oarchive& operator>>(const T& t, serialize_oarchive& oa) { 11 | t.dump() >> oa; 12 | return oa; 13 | } 14 | 15 | template ::value, int>::type = 0> 16 | serialize_iarchive& operator<<(T& t, serialize_iarchive& ia) { 17 | try { 18 | nlohmann::json::parse(ia.data, ia.data + ia.size).swap(t); 19 | } catch (std::exception& e) { 20 | RPC_CORE_LOGE("deserialize: %s", e.what()); 21 | ia.error = true; 22 | } 23 | return ia; 24 | } 25 | 26 | } // namespace rpc_core 27 | -------------------------------------------------------------------------------- /include/rpc_core/plugin/json_msg.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "nlohmann/json.hpp" 4 | #include "rpc_core/detail/log.h" 5 | #include "rpc_core/serialize.hpp" 6 | 7 | #define RPC_CORE_DEFINE_TYPE_NLOHMANN_JSON(CLASS) \ 8 | template ::value, int>::type = 0> \ 9 | ::rpc_core::serialize_oarchive& operator>>(const T& t, ::rpc_core::serialize_oarchive& oa) { \ 10 | oa.data.append(nlohmann::json(t).dump(-1)); \ 11 | return oa; \ 12 | } \ 13 | template ::value, int>::type = 0> \ 14 | ::rpc_core::serialize_iarchive& operator<<(T& t, ::rpc_core::serialize_iarchive& ia) { \ 15 | try { \ 16 | t = nlohmann::json::parse(ia.data, ia.data + ia.size).get(); \ 17 | } catch (std::exception & e) { \ 18 | RPC_CORE_LOGE("deserialize: %s", e.what()); \ 19 | ia.error = true; \ 20 | } \ 21 | return ia; \ 22 | } 23 | 24 | #define RPC_CORE_DEFINE_TYPE_JSON(Type, ...) \ 25 | NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, __VA_ARGS__) \ 26 | RPC_CORE_DEFINE_TYPE_NLOHMANN_JSON(Type); 27 | -------------------------------------------------------------------------------- /include/rpc_core/request.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #ifdef RPC_CORE_FEATURE_FUTURE 7 | #include 8 | #endif 9 | 10 | // config 11 | #include "config.hpp" 12 | 13 | // include 14 | #include "detail/callable/callable.hpp" 15 | #include "detail/msg_wrapper.hpp" 16 | #include "detail/noncopyable.hpp" 17 | #include "result.hpp" 18 | #include "serialize.hpp" 19 | 20 | namespace rpc_core { 21 | 22 | class rpc; 23 | using rpc_s = std::shared_ptr; 24 | using rpc_w = std::weak_ptr; 25 | class dispose; 26 | 27 | class request : detail::noncopyable, public std::enable_shared_from_this { 28 | friend class rpc; 29 | 30 | public: 31 | using request_s = std::shared_ptr; 32 | using request_w = std::weak_ptr; 33 | 34 | public: 35 | template 36 | static request_s create(Args&&... args) { 37 | struct helper : public request { 38 | explicit helper(Args&&... a) : request(std::forward(a)...) {} 39 | }; 40 | auto r = std::make_shared(std::forward(args)...); 41 | r->timeout(nullptr); 42 | return r; 43 | } 44 | 45 | public: 46 | request_s cmd(cmd_type cmd) { 47 | cmd_ = std::move(cmd); 48 | return shared_from_this(); 49 | } 50 | 51 | template 52 | request_s msg(T&& message) { 53 | this->payload_ = serialize(std::forward(message)); 54 | return shared_from_this(); 55 | } 56 | 57 | template ::argc == 2, int>::type = 0> 58 | request_s rsp(F cb) { 59 | using T = detail::remove_cvref_t::template argument_type<0>>; 60 | 61 | need_rsp_ = true; 62 | auto self = shared_from_this(); 63 | this->rsp_handle_ = [this, cb = std::move(cb)](detail::msg_wrapper msg) mutable { 64 | if (canceled_) { 65 | on_finish(finally_t::canceled); 66 | return true; 67 | } 68 | 69 | if (msg.type & detail::msg_wrapper::msg_type::no_such_cmd) { 70 | on_finish(finally_t::no_such_cmd); 71 | return true; 72 | } 73 | 74 | auto rsp = msg.unpack_as(); 75 | if (rsp.first) { 76 | cb(std::move(rsp.second), finally_t::normal); 77 | on_finish(finally_t::normal); 78 | return true; 79 | } else { 80 | cb({}, finally_t::rsp_serialize_error); 81 | on_finish(finally_t::rsp_serialize_error); 82 | return false; 83 | } 84 | }; 85 | return self; 86 | } 87 | 88 | template ::argc == 1, int>::type = 0> 89 | request_s rsp(F cb) { 90 | using T = detail::remove_cvref_t::template argument_type<0>>; 91 | 92 | need_rsp_ = true; 93 | auto self = shared_from_this(); 94 | this->rsp_handle_ = [this, cb = std::move(cb)](detail::msg_wrapper msg) mutable { 95 | if (canceled_) { 96 | on_finish(finally_t::canceled); 97 | return true; 98 | } 99 | 100 | if (msg.type & detail::msg_wrapper::msg_type::no_such_cmd) { 101 | on_finish(finally_t::no_such_cmd); 102 | return true; 103 | } 104 | 105 | auto rsp = msg.unpack_as(); 106 | if (rsp.first) { 107 | cb(std::move(rsp.second)); 108 | on_finish(finally_t::normal); 109 | return true; 110 | } else { 111 | on_finish(finally_t::rsp_serialize_error); 112 | return false; 113 | } 114 | }; 115 | return self; 116 | } 117 | 118 | template ::argc == 0, int>::type = 0> 119 | request_s rsp(F cb) { 120 | need_rsp_ = true; 121 | auto self = shared_from_this(); 122 | this->rsp_handle_ = [this, cb = std::move(cb)](const detail::msg_wrapper& msg) mutable { 123 | RPC_CORE_UNUSED(msg); 124 | if (canceled_) { 125 | on_finish(finally_t::canceled); 126 | return true; 127 | } 128 | 129 | if (msg.type & detail::msg_wrapper::msg_type::no_such_cmd) { 130 | on_finish(finally_t::no_such_cmd); 131 | return true; 132 | } 133 | 134 | cb(); 135 | on_finish(finally_t::normal); 136 | return true; 137 | }; 138 | return self; 139 | } 140 | 141 | /** 142 | * one call, one finally 143 | * @param finally 144 | * @return 145 | */ 146 | request_s finally(std::function finally) { 147 | finally_ = std::move(finally); 148 | return shared_from_this(); 149 | } 150 | 151 | request_s finally(std::function finally) { 152 | finally_ = [finally = std::move(finally)](finally_t t) mutable { 153 | RPC_CORE_UNUSED(t); 154 | finally(); 155 | }; 156 | return shared_from_this(); 157 | } 158 | 159 | inline void call(const rpc_s& rpc = nullptr); 160 | 161 | request_s ping() { 162 | is_ping_ = true; 163 | return shared_from_this(); 164 | } 165 | 166 | request_s timeout_ms(uint32_t timeout_ms) { 167 | timeout_ms_ = timeout_ms; 168 | return shared_from_this(); 169 | } 170 | 171 | /** 172 | * timeout callback for wait `rsp` 173 | */ 174 | request_s timeout(std::function timeout_cb) { 175 | timeout_cb_ = [this, timeout_cb = std::move(timeout_cb)]() mutable { 176 | if (timeout_cb) { 177 | timeout_cb(); 178 | } 179 | if (retry_count_ == -1) { 180 | call(); 181 | } else if (retry_count_ > 0) { 182 | retry_count_--; 183 | call(); 184 | } else { 185 | on_finish(finally_t::timeout); 186 | } 187 | }; 188 | return shared_from_this(); 189 | } 190 | 191 | inline request_s add_to(dispose& dispose); 192 | 193 | request_s cancel() { 194 | canceled(true); 195 | on_finish(finally_t::canceled); 196 | return shared_from_this(); 197 | } 198 | 199 | request_s reset_cancel() { 200 | canceled(false); 201 | return shared_from_this(); 202 | } 203 | 204 | /** 205 | * Automatic retry times after timeout 206 | * -1 means retry indefinitely, 0 means no retry, >0 means the number of retries. 207 | */ 208 | request_s retry(int count) { 209 | retry_count_ = count; 210 | return shared_from_this(); 211 | } 212 | 213 | /** 214 | * Force ignoring `rsp` callback. 215 | */ 216 | request_s disable_rsp() { 217 | need_rsp_ = false; 218 | return shared_from_this(); 219 | } 220 | 221 | request_s enable_rsp() { 222 | need_rsp_ = true; 223 | return shared_from_this(); 224 | } 225 | 226 | /** 227 | * Mark wait peer's response for finally 228 | * if no rsp handle, rpc call will finish immediately with FinallyType::NoNeedRsp 229 | * template is used for suppress warnings on some old compilers(rsp callable_traits) 230 | */ 231 | template 232 | request_s mark_need_rsp() { 233 | rsp([] {}); 234 | return shared_from_this(); 235 | } 236 | 237 | request_s rpc(rpc_w rpc) { 238 | rpc_ = std::move(rpc); 239 | return shared_from_this(); 240 | } 241 | 242 | rpc_w rpc() { 243 | return rpc_; 244 | } 245 | 246 | bool is_canceled() const { 247 | return canceled_; 248 | } 249 | 250 | request_s canceled(bool canceled) { 251 | canceled_ = canceled; 252 | return shared_from_this(); 253 | } 254 | 255 | #ifdef RPC_CORE_FEATURE_FUTURE 256 | /** 257 | * Future pattern 258 | * It is not recommended to use blocking interfaces unless you are very clear about what you are doing, as it is easy to cause deadlock. 259 | */ 260 | template ::value, int>::type = 0> 261 | std::future> future(const rpc_s& rpc = nullptr); 262 | 263 | template ::value, int>::type = 0> 264 | std::future> future(const rpc_s& rpc = nullptr); 265 | #endif 266 | 267 | #ifdef RPC_CORE_FEATURE_CO_ASIO 268 | template ::value, int>::type = 0> 269 | asio::awaitable> co_call(); 270 | 271 | template ::value, int>::type = 0> 272 | asio::awaitable> co_call(); 273 | #endif 274 | 275 | #ifdef RPC_CORE_FEATURE_CO_CUSTOM 276 | template ::value, int>::type = 0> 277 | RPC_CORE_FEATURE_CO_CUSTOM_R RPC_CORE_FEATURE_CO_CUSTOM(); 278 | 279 | template ::value, int>::type = 0> 280 | RPC_CORE_FEATURE_CO_CUSTOM_R RPC_CORE_FEATURE_CO_CUSTOM(); 281 | #endif 282 | 283 | private: 284 | explicit request(const rpc_s& rpc = nullptr) : rpc_(rpc) { 285 | RPC_CORE_LOGD("request: %p", this); 286 | } 287 | ~request() { 288 | RPC_CORE_LOGD("~request: %p", this); 289 | } 290 | 291 | private: 292 | void on_finish(finally_t type) { 293 | if (!waiting_rsp_) return; 294 | waiting_rsp_ = false; 295 | RPC_CORE_LOGD("on_finish: cmd:%s type:%s", cmd_.c_str(), finally_t_str(type)); 296 | finally_type_ = type; 297 | if (finally_) { 298 | finally_(finally_type_); 299 | } 300 | self_keeper_ = nullptr; 301 | } 302 | 303 | private: 304 | rpc_w rpc_; 305 | request_s self_keeper_; 306 | seq_type seq_{}; 307 | cmd_type cmd_; 308 | std::string payload_; 309 | bool need_rsp_ = false; 310 | bool canceled_ = false; 311 | std::function rsp_handle_; 312 | uint32_t timeout_ms_ = 3000; 313 | std::function timeout_cb_; 314 | finally_t finally_type_ = finally_t::no_need_rsp; 315 | std::function finally_; 316 | int retry_count_ = 0; 317 | bool waiting_rsp_ = false; 318 | bool is_ping_ = false; 319 | }; 320 | 321 | using request_s = request::request_s; 322 | using request_w = request::request_w; 323 | 324 | } // namespace rpc_core 325 | -------------------------------------------------------------------------------- /include/rpc_core/request.ipp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "request.hpp" 4 | #include "rpc.hpp" 5 | 6 | namespace rpc_core { 7 | 8 | void request::call(const rpc_s& rpc) { 9 | waiting_rsp_ = true; 10 | 11 | if (canceled_) { 12 | on_finish(finally_t::canceled); 13 | return; 14 | } 15 | 16 | self_keeper_ = shared_from_this(); 17 | if (rpc) { 18 | rpc_ = rpc; 19 | } else if (rpc_.expired()) { 20 | on_finish(finally_t::rpc_expired); 21 | return; 22 | } 23 | 24 | auto r = rpc_.lock(); 25 | if (!r->is_ready()) { 26 | on_finish(finally_t::rpc_not_ready); 27 | return; 28 | } 29 | seq_ = r->make_seq(); 30 | r->send_request(this); 31 | if (!need_rsp_) { 32 | on_finish(finally_t::no_need_rsp); 33 | } 34 | } 35 | 36 | request_s request::add_to(dispose& dispose) { 37 | auto self = shared_from_this(); 38 | dispose.add(self); 39 | return self; 40 | } 41 | 42 | #ifdef RPC_CORE_FEATURE_FUTURE 43 | template ::value, int>::type> 44 | std::future> request::future(const rpc_s& rpc) { 45 | auto promise = std::make_shared>>(); 46 | rsp([promise](R r, finally_t type) { 47 | promise->set_value({type, std::move(r)}); 48 | }); 49 | call(rpc); 50 | return promise->get_future(); 51 | } 52 | 53 | template ::value, int>::type> 54 | std::future> request::future(const rpc_s& rpc) { 55 | auto promise = std::make_shared>>(); 56 | mark_need_rsp(); 57 | finally([promise](finally_t type) { 58 | promise->set_value({type}); 59 | }); 60 | call(rpc); 61 | return promise->get_future(); 62 | } 63 | #endif 64 | 65 | #ifdef RPC_CORE_FEATURE_CO_ASIO 66 | template ::value, int>::type> 67 | asio::awaitable> request::co_call() { 68 | auto executor = co_await asio::this_coro::executor; 69 | co_return co_await asio::async_compose)>( 70 | [this, &executor](auto& self) mutable { 71 | using ST = std::remove_reference::type; 72 | auto self_sp = std::make_shared(std::forward(self)); 73 | rsp([&executor, self = std::move(self_sp)](R data, finally_t type) mutable { 74 | asio::dispatch(executor, [self = std::move(self), data = std::move(data), type]() { 75 | self->complete({type, data}); 76 | }); 77 | }); 78 | call(); 79 | }, 80 | asio::use_awaitable); 81 | } 82 | 83 | template ::value, int>::type> 84 | asio::awaitable> request::co_call() { 85 | auto executor = co_await asio::this_coro::executor; 86 | co_return co_await asio::async_compose)>( 87 | [this, &executor](auto& self) mutable { 88 | using ST = std::remove_reference::type; 89 | auto self_sp = std::make_shared(std::forward(self)); 90 | mark_need_rsp(); 91 | finally([&executor, self = std::move(self_sp)](finally_t type) mutable { 92 | asio::dispatch(executor, [self = std::move(self), type] { 93 | self->complete({type}); 94 | }); 95 | }); 96 | call(); 97 | }, 98 | asio::use_awaitable); 99 | } 100 | #endif 101 | 102 | } // namespace rpc_core 103 | -------------------------------------------------------------------------------- /include/rpc_core/request_response.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | // config 7 | #include "config.hpp" 8 | 9 | // include 10 | #include "detail/callable/callable.hpp" 11 | #include "detail/noncopyable.hpp" 12 | 13 | namespace rpc_core { 14 | 15 | template 16 | struct request_response_impl : public std::enable_shared_from_this>, private detail::noncopyable { 17 | using request_response_t = request_response_impl; 18 | 19 | public: 20 | static std::shared_ptr create() { 21 | struct helper : public request_response_t { 22 | explicit helper() : request_response_t() {} 23 | }; 24 | return std::make_shared(); 25 | } 26 | 27 | std::weak_ptr weak_ptr() { 28 | return request_response_t::shared_from_this(); 29 | } 30 | 31 | private: 32 | request_response_impl() = default; 33 | 34 | public: 35 | Req req; 36 | using RspType = Rsp; 37 | std::string rsp_data; // serialized data 38 | 39 | bool rsp_ready{false}; 40 | std::function rsp; 41 | }; 42 | 43 | template 44 | using request_response = std::shared_ptr>; 45 | 46 | namespace detail { 47 | 48 | template 49 | struct is_request_response : std::false_type {}; 50 | 51 | template 52 | struct is_request_response>> : std::true_type {}; 53 | 54 | template 55 | struct fp_is_request_response_helper { 56 | static constexpr bool value = false; 57 | }; 58 | 59 | template 60 | struct fp_is_request_response_helper { 61 | using request_response = detail::remove_cvref_t::template argument_type<0>>; 62 | static constexpr bool value = detail::is_request_response::value; 63 | }; 64 | 65 | template 66 | struct fp_is_request_response { 67 | static constexpr bool ONE_PARAM = detail::callable_traits::argc == 1; 68 | static constexpr bool value = fp_is_request_response_helper::value; 69 | }; 70 | 71 | } // namespace detail 72 | 73 | } // namespace rpc_core 74 | -------------------------------------------------------------------------------- /include/rpc_core/result.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | // config 7 | #include "config.hpp" 8 | 9 | namespace rpc_core { 10 | 11 | enum class finally_t : int { 12 | normal = 0, 13 | no_need_rsp = 1, 14 | timeout = 2, 15 | canceled = 3, 16 | rpc_expired = 4, 17 | rpc_not_ready = 5, 18 | rsp_serialize_error = 6, 19 | no_such_cmd = 7, 20 | }; 21 | 22 | inline const char* finally_t_str(finally_t t) { 23 | switch (t) { 24 | case finally_t::normal: 25 | return "normal"; 26 | case finally_t::no_need_rsp: 27 | return "no_need_rsp"; 28 | case finally_t::timeout: 29 | return "timeout"; 30 | case finally_t::canceled: 31 | return "canceled"; 32 | case finally_t::rpc_expired: 33 | return "rpc_expired"; 34 | case finally_t::rpc_not_ready: 35 | return "rpc_not_ready"; 36 | case finally_t::rsp_serialize_error: 37 | return "rsp_serialize_error"; 38 | case finally_t::no_such_cmd: 39 | return "no_such_cmd"; 40 | default: 41 | return "unknown"; 42 | } 43 | } 44 | 45 | template 46 | struct result; 47 | 48 | template 49 | struct result { 50 | finally_t type; 51 | T data; 52 | 53 | explicit operator bool() const { 54 | return type == finally_t::normal; 55 | } 56 | 57 | T& operator*() { 58 | return data; 59 | } 60 | 61 | T&& take() { 62 | return std::move(data); 63 | } 64 | }; 65 | 66 | template <> 67 | struct result { 68 | finally_t type; 69 | 70 | explicit operator bool() const { 71 | return type == finally_t::normal; 72 | } 73 | }; 74 | 75 | } // namespace rpc_core 76 | -------------------------------------------------------------------------------- /include/rpc_core/rpc.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | // config 7 | #include "config.hpp" 8 | 9 | // include 10 | #include "connection.hpp" 11 | #include "detail/callable/callable.hpp" 12 | #include "detail/msg_dispatcher.hpp" 13 | #include "detail/noncopyable.hpp" 14 | #include "request_response.hpp" 15 | 16 | namespace rpc_core { 17 | 18 | class request; 19 | using request_s = std::shared_ptr; 20 | 21 | class rpc : detail::noncopyable, public std::enable_shared_from_this { 22 | public: 23 | using timeout_cb = detail::msg_dispatcher::timeout_cb; 24 | 25 | public: 26 | template 27 | static std::shared_ptr create(Args&&... args) { 28 | struct helper : public rpc { 29 | explicit helper(Args&&... a) : rpc(std::forward(a)...) {} 30 | }; 31 | return std::make_shared(std::forward(args)...); 32 | } 33 | 34 | private: 35 | explicit rpc(std::shared_ptr conn = std::make_shared()) 36 | : conn_(conn), dispatcher_(std::make_shared(std::move(conn))) { 37 | dispatcher_->init(); 38 | RPC_CORE_LOGD("rpc: %p", this); 39 | } 40 | 41 | ~rpc() { 42 | RPC_CORE_LOGD("~rpc: %p", this); 43 | }; 44 | 45 | public: 46 | inline std::shared_ptr get_connection() const { 47 | return conn_; 48 | } 49 | 50 | inline void set_timer(detail::msg_dispatcher::timer_impl timer_impl) { 51 | dispatcher_->set_timer_impl(std::move(timer_impl)); 52 | } 53 | 54 | inline void set_ready(bool ready) { 55 | is_ready_ = ready; 56 | } 57 | 58 | public: 59 | template ::value, int>::type = 0> 60 | void subscribe(const cmd_type& cmd, F handle) { 61 | constexpr bool F_ReturnIsEmpty = std::is_void::return_type>::value; 62 | constexpr bool F_ParamIsEmpty = detail::callable_traits::argc == 0; 63 | subscribe_helper()(cmd, std::move(handle), dispatcher_.get()); 64 | } 65 | 66 | template 67 | using Scheduler = std::function::return_type()>)>; 68 | 69 | template ::value, int>::type = 0> 70 | void subscribe(const cmd_type& cmd, F handle) { 71 | static_assert(std::is_void::return_type>::value, "should return void"); 72 | subscribe(cmd, std::move(handle), nullptr); 73 | } 74 | 75 | template ::value, int>::type = 0> 76 | void subscribe(const cmd_type& cmd, F handle, Scheduler scheduler) { 77 | static_assert(detail::callable_traits::argc == 1, "should be request_response<>"); 78 | dispatcher_->subscribe_cmd(cmd, [handle = std::move(handle), scheduler = std::move(scheduler)](const detail::msg_wrapper& msg) mutable { 79 | using request_response = detail::remove_cvref_t::template argument_type<0>>; 80 | using request_response_impl = typename request_response::element_type; 81 | static_assert(detail::is_request_response::value, "should be request_response<>"); 82 | using Req = decltype(request_response_impl::req); 83 | using Rsp = typename request_response_impl::RspType; 84 | request_response rr = request_response_impl::create(); 85 | auto r = msg.unpack_as(); 86 | // notice lifecycle: request_response hold async_helper 87 | // but async_helper->is_ready will hold rr lifetime for check if it is ready and get data 88 | // it will be release in msg_dispatcher after check 89 | auto async_helper = std::make_shared(); 90 | async_helper->is_ready = [rr] { 91 | return rr->rsp_ready; 92 | }; 93 | async_helper->get_data = [rr = rr.get()] { 94 | return std::move(rr->rsp_data); 95 | }; 96 | if (r.first) { 97 | rr->req = std::move(r.second); 98 | rr->rsp = [rr = rr.get(), hp = async_helper](Rsp rsp) mutable { 99 | if (rr->rsp_ready) { 100 | RPC_CORE_LOGD("rsp should only call once"); 101 | return; 102 | } 103 | rr->rsp_ready = true; 104 | rr->rsp_data = serialize(std::move(rsp)); 105 | if (hp->send_async_response) { // means after handle() 106 | hp->send_async_response(std::move(rr->rsp_data)); 107 | } 108 | }; 109 | if (scheduler) { 110 | scheduler(std::bind(handle, std::move(rr))); 111 | } else { 112 | (void)handle(std::move(rr)); 113 | } 114 | } 115 | return detail::msg_wrapper::make_rsp_async(msg.seq, std::move(async_helper), r.first); 116 | }); 117 | } 118 | 119 | inline void unsubscribe(const cmd_type& cmd) { 120 | dispatcher_->unsubscribe_cmd(cmd); 121 | } 122 | 123 | public: 124 | inline request_s create_request(); 125 | 126 | inline request_s cmd(cmd_type cmd); 127 | 128 | inline request_s ping(std::string payload = {}); 129 | 130 | template 131 | inline void call(cmd_type cmd, Msg&& message); 132 | 133 | template 134 | inline void call(cmd_type cmd, Msg&& message, Rsp&& rsp); 135 | 136 | #ifdef RPC_CORE_FEATURE_CO_ASIO 137 | template 138 | inline asio::awaitable> co_call(cmd_type cmd); 139 | 140 | template 141 | inline asio::awaitable> co_call(cmd_type cmd, Msg&& message); 142 | #endif 143 | 144 | #ifdef RPC_CORE_FEATURE_CO_CUSTOM 145 | template 146 | inline RPC_CORE_FEATURE_CO_CUSTOM_R RPC_CORE_FEATURE_CO_CUSTOM(cmd_type cmd); 147 | 148 | template 149 | inline RPC_CORE_FEATURE_CO_CUSTOM_R RPC_CORE_FEATURE_CO_CUSTOM(cmd_type cmd, Msg&& message); 150 | #endif 151 | 152 | public: 153 | inline seq_type make_seq() { 154 | return seq_++; 155 | } 156 | 157 | inline void send_request(request const* request); 158 | 159 | inline bool is_ready() const { 160 | return is_ready_; 161 | } 162 | 163 | private: 164 | template 165 | struct subscribe_helper; 166 | 167 | template 168 | struct subscribe_helper { 169 | void operator()(const cmd_type& cmd, F handle, detail::msg_dispatcher* dispatcher) { 170 | dispatcher->subscribe_cmd(cmd, [handle = std::move(handle)](const detail::msg_wrapper& msg) mutable { 171 | using F_Param = detail::remove_cvref_t::template argument_type<0>>; 172 | using F_Return = detail::remove_cvref_t::return_type>; 173 | 174 | auto r = msg.unpack_as(); 175 | F_Return ret; 176 | if (r.first) { 177 | ret = handle(std::move(r.second)); 178 | } 179 | return detail::msg_wrapper::make_rsp(msg.seq, &ret, r.first); 180 | }); 181 | } 182 | }; 183 | 184 | template 185 | struct subscribe_helper { 186 | void operator()(const cmd_type& cmd, F handle, detail::msg_dispatcher* dispatcher) { 187 | dispatcher->subscribe_cmd(cmd, [handle = std::move(handle)](const detail::msg_wrapper& msg) mutable { 188 | using F_Param = detail::remove_cvref_t::template argument_type<0>>; 189 | 190 | auto r = msg.unpack_as(); 191 | if (r.first) { 192 | handle(std::move(r.second)); 193 | } 194 | return detail::msg_wrapper::make_rsp(msg.seq, nullptr, r.first); 195 | }); 196 | } 197 | }; 198 | 199 | template 200 | struct subscribe_helper { 201 | void operator()(const cmd_type& cmd, F handle, detail::msg_dispatcher* dispatcher) { 202 | dispatcher->subscribe_cmd(cmd, [handle = std::move(handle)](const detail::msg_wrapper& msg) mutable { 203 | using F_Return = typename detail::callable_traits::return_type; 204 | 205 | F_Return ret = handle(); 206 | return detail::msg_wrapper::make_rsp(msg.seq, &ret, true); 207 | }); 208 | } 209 | }; 210 | 211 | template 212 | struct subscribe_helper { 213 | void operator()(const cmd_type& cmd, F handle, detail::msg_dispatcher* dispatcher) { 214 | dispatcher->subscribe_cmd(cmd, [handle = std::move(handle)](const detail::msg_wrapper& msg) mutable { 215 | handle(); 216 | return detail::msg_wrapper::make_rsp(msg.seq, nullptr, true); 217 | }); 218 | } 219 | }; 220 | 221 | private: 222 | std::shared_ptr conn_; 223 | std::shared_ptr dispatcher_; 224 | seq_type seq_{0}; 225 | bool is_ready_ = false; 226 | }; 227 | 228 | using rpc_s = std::shared_ptr; 229 | using rpc_w = std::weak_ptr; 230 | 231 | } // namespace rpc_core 232 | -------------------------------------------------------------------------------- /include/rpc_core/rpc.ipp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "request.hpp" 4 | #include "rpc.hpp" 5 | 6 | namespace rpc_core { 7 | 8 | request_s rpc::create_request() { 9 | return request::create(shared_from_this()); 10 | } 11 | 12 | request_s rpc::cmd(cmd_type cmd) { 13 | return create_request()->cmd(std::move(cmd)); 14 | } 15 | 16 | request_s rpc::ping(std::string payload) { 17 | return create_request()->ping()->msg(std::move(payload)); 18 | } 19 | 20 | template 21 | inline void rpc::call(cmd_type cmd, Msg&& message) { 22 | this->cmd(std::move(cmd))->msg(std::forward(message))->call(); 23 | } 24 | 25 | template 26 | inline void rpc::call(cmd_type cmd, Msg&& message, Rsp&& rsp) { 27 | this->cmd(std::move(cmd))->msg(std::forward(message))->rsp(std::forward(rsp))->call(); 28 | } 29 | 30 | #ifdef RPC_CORE_FEATURE_CO_ASIO 31 | template 32 | inline asio::awaitable> rpc::co_call(cmd_type cmd) { 33 | co_return co_await this->cmd(std::move(cmd))->co_call(); 34 | } 35 | 36 | template 37 | inline asio::awaitable> rpc::co_call(cmd_type cmd, Msg&& message) { 38 | co_return co_await this->cmd(std::move(cmd))->msg(std::forward(message))->template co_call(); 39 | } 40 | #endif 41 | 42 | void rpc::send_request(request const* request) { 43 | if (request->need_rsp_) { 44 | dispatcher_->subscribe_rsp(request->seq_, request->rsp_handle_, request->timeout_cb_, request->timeout_ms_); 45 | } 46 | detail::msg_wrapper msg; 47 | msg.type = static_cast(detail::msg_wrapper::command | (request->is_ping_ ? detail::msg_wrapper::ping : 0) | 48 | (request->need_rsp_ ? detail::msg_wrapper::need_rsp : 0)); 49 | msg.cmd = request->cmd_; 50 | msg.seq = request->seq_; 51 | msg.request_payload = &request->payload_; 52 | RPC_CORE_LOGD("=> seq:%u type:%s %s", msg.seq, (msg.type & detail::msg_wrapper::msg_type::ping) ? "ping" : "cmd", msg.cmd.c_str()); 53 | conn_->send_package_impl(detail::coder::serialize(msg)); 54 | } 55 | 56 | } // namespace rpc_core 57 | -------------------------------------------------------------------------------- /include/rpc_core/serialize.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // config 4 | #include "config.hpp" 5 | 6 | #if defined(RPC_CORE_SERIALIZE_USE_CUSTOM) 7 | 8 | #include RPC_CORE_SERIALIZE_USE_CUSTOM 9 | 10 | #elif defined(RPC_CORE_SERIALIZE_USE_NLOHMANN_JSON) 11 | 12 | #include "serialize_nlohmann_json.hpp" 13 | 14 | #else 15 | 16 | // type traits 17 | #include "detail/type_traits.hpp" 18 | 19 | // serialize api 20 | #include "serialize_api.hpp" 21 | 22 | // raw type 23 | #include "serialize/type_raw.hpp" 24 | 25 | // other types 26 | #include "serialize/binary_wrap.hpp" 27 | #include "serialize/std_array.hpp" 28 | #include "serialize/std_basic_string.hpp" 29 | #include "serialize/std_bitset.hpp" 30 | #include "serialize/std_chrono.hpp" 31 | #include "serialize/std_complex.hpp" 32 | #include "serialize/std_container_adaptors.hpp" 33 | #include "serialize/std_forward_list.hpp" 34 | #include "serialize/std_list_like.hpp" 35 | #include "serialize/std_map.hpp" 36 | #include "serialize/std_pair.hpp" 37 | #include "serialize/std_set.hpp" 38 | #include "serialize/std_shared_ptr.hpp" 39 | #include "serialize/std_tuple.hpp" 40 | #include "serialize/std_unique_ptr.hpp" 41 | #include "serialize/type_enum.hpp" 42 | #include "serialize/type_ptr.hpp" 43 | #include "serialize/type_struct.hpp" 44 | #include "serialize/type_void.hpp" 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /include/rpc_core/serialize/binary_wrap.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace rpc_core { 7 | 8 | struct binary_wrap { 9 | binary_wrap() = default; 10 | binary_wrap(void* data, size_t size) : data(data), size(size) {} 11 | void* data = nullptr; 12 | size_t size = 0; 13 | 14 | // private: 15 | std::shared_ptr _data_; 16 | }; 17 | 18 | template ::value, int>::type = 0> 19 | inline serialize_oarchive& operator>>(const T& t, serialize_oarchive& oa) { 20 | t.size >> oa; 21 | oa.data.append((char*)t.data, t.size); 22 | return oa; 23 | } 24 | 25 | template ::value, int>::type = 0> 26 | inline serialize_iarchive& operator<<(T& t, serialize_iarchive& ia) { 27 | t.size << ia; 28 | t._data_ = std::shared_ptr(new uint8_t[t.size], [](const uint8_t* p) { 29 | delete[] p; 30 | }); 31 | t.data = t._data_.get(); 32 | memcpy(t.data, ia.data, t.size); 33 | ia.data += t.size; 34 | ia.size -= t.size; 35 | return ia; 36 | } 37 | 38 | } // namespace rpc_core 39 | -------------------------------------------------------------------------------- /include/rpc_core/serialize/detail/auto_size.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace rpc_core { 9 | namespace detail { 10 | 11 | template 12 | struct auto_size_type { 13 | explicit auto_size_type(int_impl_t value = 0) : value(value) {} 14 | 15 | std::string serialize() const { 16 | if (value == 0) { 17 | return {(char*)&value, 1}; 18 | } 19 | 20 | uint8_t effective_bytes = sizeof(int_impl_t); 21 | auto value_tmp = value; 22 | if (value_tmp < 0) { 23 | value_tmp = ~value_tmp + 1; 24 | } 25 | for (int i = sizeof(int_impl_t) - 1; i >= 0; --i) { 26 | if ((value_tmp >> (i * 8)) & 0xff) { 27 | break; 28 | } else { 29 | --effective_bytes; 30 | } 31 | } 32 | 33 | std::string ret; 34 | ret.resize(1 + effective_bytes); 35 | auto data = (uint8_t*)ret.data(); 36 | data[0] = effective_bytes; 37 | memcpy(data + 1, &value_tmp, effective_bytes); 38 | if (value < 0) { 39 | data[0] |= 0x80; 40 | } 41 | return ret; 42 | } 43 | 44 | int deserialize(const void* data) { 45 | auto p = (uint8_t*)data; 46 | uint8_t size_bytes = p[0]; 47 | bool negative = false; 48 | if (size_bytes & 0x80) { 49 | negative = true; 50 | size_bytes &= 0x7f; 51 | } 52 | memcpy(&value, p + 1, size_bytes); 53 | if (negative) { 54 | value = ~value + 1; 55 | } 56 | return size_bytes + 1; 57 | } 58 | 59 | int_impl_t value; 60 | }; 61 | 62 | using auto_size = auto_size_type; 63 | using auto_intmax = auto_size_type; 64 | using auto_uintmax = auto_size_type; 65 | 66 | template 67 | struct is_auto_size_type : std::false_type {}; 68 | 69 | template 70 | struct is_auto_size_type> : std::true_type {}; 71 | 72 | } // namespace detail 73 | } // namespace rpc_core 74 | -------------------------------------------------------------------------------- /include/rpc_core/serialize/std_array.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "../detail/callable/helpers.hpp" 6 | 7 | namespace rpc_core { 8 | 9 | namespace detail { 10 | 11 | template 12 | struct is_std_array : std::false_type {}; 13 | 14 | template 15 | struct is_std_array> : std::true_type {}; 16 | 17 | } // namespace detail 18 | 19 | template ::value, int>::type = 0> 20 | serialize_oarchive& operator>>(const T& t, serialize_oarchive& oa) { 21 | detail::auto_size size(t.size()); 22 | size >> oa; 23 | for (auto& item : t) { 24 | if (std::is_fundamental>::value) { 25 | item >> oa; 26 | } else { 27 | serialize_oarchive tmp; 28 | item >> tmp; 29 | tmp >> oa; 30 | } 31 | } 32 | return oa; 33 | } 34 | 35 | template ::value, int>::type = 0> 36 | serialize_iarchive& operator<<(T& t, serialize_iarchive& ia) { 37 | detail::auto_size size; 38 | size << ia; 39 | for (size_t i = 0; i < size.value; ++i) { 40 | typename T::value_type item; 41 | if (std::is_fundamental>::value) { 42 | item << ia; 43 | } else { 44 | serialize_iarchive tmp; 45 | tmp << ia; 46 | item << tmp; 47 | if (tmp.error) { 48 | ia.error = true; 49 | break; 50 | } 51 | } 52 | t[i] = std::move(item); 53 | } 54 | return ia; 55 | } 56 | 57 | } // namespace rpc_core 58 | -------------------------------------------------------------------------------- /include/rpc_core/serialize/std_basic_string.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace rpc_core { 6 | 7 | namespace detail { 8 | 9 | template 10 | struct is_std_basic_string : std::false_type {}; 11 | 12 | template 13 | struct is_std_basic_string> : std::true_type {}; 14 | 15 | } // namespace detail 16 | 17 | template ::value, int>::type = 0> 18 | inline serialize_oarchive& operator>>(const T& t, serialize_oarchive& oa) { 19 | using VT = typename T::value_type; 20 | oa.data.append((char*)t.data(), t.size() * sizeof(VT)); 21 | return oa; 22 | } 23 | 24 | template ::value, int>::type = 0> 25 | inline serialize_oarchive& operator>>(T&& t, serialize_oarchive& oa) { 26 | using VT = typename T::value_type; 27 | if (oa.data.empty()) { 28 | oa.data = std::forward(t); 29 | } else { 30 | oa.data.append((char*)t.data(), t.size() * sizeof(VT)); 31 | } 32 | return oa; 33 | } 34 | 35 | template ::value, int>::type = 0> 36 | inline serialize_iarchive& operator<<(T& t, serialize_iarchive& ia) { 37 | using VT = typename T::value_type; 38 | t = T((VT*)(ia.data), ia.size / sizeof(VT)); 39 | return ia; 40 | } 41 | 42 | } // namespace rpc_core 43 | -------------------------------------------------------------------------------- /include/rpc_core/serialize/std_bitset.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace rpc_core { 8 | 9 | namespace detail { 10 | 11 | template 12 | struct is_std_bitset : std::false_type {}; 13 | 14 | template 15 | struct is_std_bitset> : std::true_type {}; 16 | 17 | } // namespace detail 18 | 19 | template ::value, int>::type = 0> 20 | serialize_oarchive& operator>>(const T& t, serialize_oarchive& oa) { 21 | oa.data.append(t.to_string()); 22 | return oa; 23 | } 24 | 25 | template ::value, int>::type = 0> 26 | serialize_iarchive& operator<<(T& t, serialize_iarchive& ia) { 27 | std::string tmp; 28 | tmp << ia; 29 | t = T(std::move(tmp)); 30 | return ia; 31 | } 32 | 33 | } // namespace rpc_core 34 | -------------------------------------------------------------------------------- /include/rpc_core/serialize/std_chrono.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace rpc_core { 7 | 8 | namespace detail { 9 | 10 | template 11 | struct is_std_chrono_duration : std::false_type {}; 12 | 13 | template 14 | struct is_std_chrono_duration> : std::true_type {}; 15 | 16 | template 17 | struct is_std_chrono_time_point : std::false_type {}; 18 | 19 | template 20 | struct is_std_chrono_time_point> : std::true_type {}; 21 | 22 | } // namespace detail 23 | 24 | template ::value, int>::type = 0> 25 | serialize_oarchive& operator>>(const T& t, serialize_oarchive& oa) { 26 | detail::auto_intmax count(t.count()); 27 | count >> oa; 28 | return oa; 29 | } 30 | 31 | template ::value, int>::type = 0> 32 | serialize_iarchive& operator<<(T& t, serialize_iarchive& ia) { 33 | detail::auto_intmax rep; 34 | rep << ia; 35 | t = T(rep.value); 36 | return ia; 37 | } 38 | 39 | template ::value, int>::type = 0> 40 | serialize_oarchive& operator>>(const T& t, serialize_oarchive& oa) { 41 | t.time_since_epoch() >> oa; 42 | return oa; 43 | } 44 | 45 | template ::value, int>::type = 0> 46 | serialize_iarchive& operator<<(T& t, serialize_iarchive& ia) { 47 | typename T::duration duration; 48 | duration << ia; 49 | t = T(duration); 50 | return ia; 51 | } 52 | 53 | } // namespace rpc_core 54 | -------------------------------------------------------------------------------- /include/rpc_core/serialize/std_complex.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace rpc_core { 7 | 8 | namespace detail { 9 | 10 | template 11 | struct is_std_complex : std::false_type {}; 12 | 13 | template 14 | struct is_std_complex> : std::true_type {}; 15 | 16 | } // namespace detail 17 | 18 | template ::value, int>::type = 0> 19 | serialize_oarchive& operator>>(const T& t, serialize_oarchive& oa) { 20 | using VT = typename T::value_type; 21 | if (std::is_fundamental::value) { 22 | t.real() >> oa; 23 | t.imag() >> oa; 24 | } else { 25 | oa & t.real(); 26 | oa & t.imag(); 27 | } 28 | return oa; 29 | } 30 | 31 | template ::value, int>::type = 0> 32 | serialize_iarchive& operator<<(T& t, serialize_iarchive& ia) { 33 | using VT = typename T::value_type; 34 | if (std::is_fundamental::value) { 35 | VT real; 36 | real << ia; 37 | t.real(real); 38 | VT imag; 39 | imag << ia; 40 | t.imag(imag); 41 | } else { 42 | VT real; 43 | ia & real; 44 | t.real(std::move(real)); 45 | VT imag; 46 | ia & imag; 47 | t.imag(std::move(imag)); 48 | } 49 | return ia; 50 | } 51 | 52 | } // namespace rpc_core 53 | -------------------------------------------------------------------------------- /include/rpc_core/serialize/std_container_adaptors.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace rpc_core { 7 | 8 | namespace detail { 9 | 10 | template 11 | struct is_std_stack : std::false_type {}; 12 | 13 | template 14 | struct is_std_stack> : std::true_type {}; 15 | 16 | template 17 | struct is_std_stack> : std::true_type {}; 18 | 19 | template 20 | struct is_std_stack> : std::true_type {}; 21 | 22 | } // namespace detail 23 | 24 | template ::value, int>::type = 0> 25 | serialize_oarchive& operator>>(const T& t, serialize_oarchive& oa) { 26 | (typename T::container_type&)t >> oa; 27 | return oa; 28 | } 29 | 30 | template ::value, int>::type = 0> 31 | serialize_iarchive& operator<<(T& t, serialize_iarchive& ia) { 32 | (typename T::container_type&)t << ia; 33 | return ia; 34 | } 35 | 36 | } // namespace rpc_core 37 | -------------------------------------------------------------------------------- /include/rpc_core/serialize/std_forward_list.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "../detail/callable/helpers.hpp" 6 | 7 | namespace rpc_core { 8 | 9 | namespace detail { 10 | 11 | template 12 | struct is_std_forward_list : std::false_type {}; 13 | 14 | template 15 | struct is_std_forward_list> : std::true_type {}; 16 | 17 | } // namespace detail 18 | 19 | template ::value, int>::type = 0> 20 | serialize_oarchive& operator>>(const T& t, serialize_oarchive& oa) { 21 | detail::auto_size size(std::distance(t.cbegin(), t.cend())); 22 | size >> oa; 23 | for (auto& item : t) { 24 | if (std::is_fundamental>::value) { 25 | item >> oa; 26 | } else { 27 | serialize_oarchive tmp; 28 | item >> tmp; 29 | tmp >> oa; 30 | } 31 | } 32 | return oa; 33 | } 34 | 35 | template ::value, int>::type = 0> 36 | serialize_iarchive& operator<<(T& t, serialize_iarchive& ia) { 37 | detail::auto_size size; 38 | size << ia; 39 | for (size_t i = 0; i < size.value; ++i) { 40 | typename T::value_type item; 41 | if (std::is_fundamental>::value) { 42 | item << ia; 43 | } else { 44 | serialize_iarchive tmp; 45 | tmp << ia; 46 | item << tmp; 47 | if (tmp.error) { 48 | ia.error = true; 49 | break; 50 | } 51 | } 52 | t.emplace_front(std::move(item)); 53 | } 54 | t.reverse(); 55 | return ia; 56 | } 57 | 58 | } // namespace rpc_core 59 | -------------------------------------------------------------------------------- /include/rpc_core/serialize/std_list_like.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace rpc_core { 8 | 9 | namespace detail { 10 | 11 | template 12 | struct is_std_list_like : std::false_type {}; 13 | 14 | template 15 | struct is_std_list_like> : std::true_type {}; 16 | 17 | template 18 | struct is_std_list_like> : std::true_type {}; 19 | 20 | template 21 | struct is_std_list_like> : std::true_type {}; 22 | 23 | } // namespace detail 24 | 25 | template ::value, int>::type = 0> 26 | serialize_oarchive& operator>>(const T& t, serialize_oarchive& oa) { 27 | detail::auto_size size(t.size()); 28 | size >> oa; 29 | for (auto& item : t) { 30 | if (std::is_fundamental>::value) { 31 | item >> oa; 32 | } else { 33 | serialize_oarchive tmp; 34 | item >> tmp; 35 | tmp >> oa; 36 | } 37 | } 38 | return oa; 39 | } 40 | 41 | template ::value, int>::type = 0> 42 | serialize_iarchive& operator<<(T& t, serialize_iarchive& ia) { 43 | detail::auto_size size; 44 | size << ia; 45 | for (size_t i = 0; i < size.value; ++i) { 46 | typename T::value_type item; 47 | if (std::is_fundamental>::value) { 48 | item << ia; 49 | } else { 50 | serialize_iarchive tmp; 51 | tmp << ia; 52 | item << tmp; 53 | if (tmp.error) { 54 | ia.error = true; 55 | break; 56 | } 57 | } 58 | t.emplace_back(std::move(item)); 59 | } 60 | return ia; 61 | } 62 | 63 | } // namespace rpc_core 64 | -------------------------------------------------------------------------------- /include/rpc_core/serialize/std_map.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace rpc_core { 7 | 8 | namespace detail { 9 | 10 | template 11 | struct is_std_map_like : std::false_type {}; 12 | 13 | template 14 | struct is_std_map_like> : std::true_type {}; 15 | 16 | template 17 | struct is_std_map_like> : std::true_type {}; 18 | 19 | template 20 | struct is_std_map_like> : std::true_type {}; 21 | 22 | template 23 | struct is_std_map_like> : std::true_type {}; 24 | 25 | } // namespace detail 26 | 27 | template ::value, int>::type = 0> 28 | serialize_oarchive& operator>>(const T& t, serialize_oarchive& oa) { 29 | detail::auto_size size(t.size()); 30 | size >> oa; 31 | for (auto& item : t) { 32 | serialize_oarchive tmp; 33 | item >> tmp; 34 | tmp >> oa; 35 | } 36 | return oa; 37 | } 38 | 39 | template ::value, int>::type = 0> 40 | serialize_iarchive& operator<<(T& t, serialize_iarchive& ia) { 41 | detail::auto_size size; 42 | size << ia; 43 | for (size_t i = 0; i < size.value; ++i) { 44 | typename T::value_type item; 45 | serialize_iarchive tmp; 46 | tmp << ia; 47 | item << tmp; 48 | if (tmp.error) { 49 | ia.error = true; 50 | break; 51 | } 52 | t.emplace(std::move(item)); 53 | } 54 | return ia; 55 | } 56 | 57 | } // namespace rpc_core 58 | -------------------------------------------------------------------------------- /include/rpc_core/serialize/std_pair.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace rpc_core { 6 | 7 | namespace detail { 8 | 9 | template 10 | struct is_std_pair : std::false_type {}; 11 | 12 | template 13 | struct is_std_pair> : std::true_type {}; 14 | 15 | } // namespace detail 16 | 17 | template ::value, int>::type = 0> 18 | inline serialize_oarchive& operator>>(const T& t, serialize_oarchive& oa) { 19 | std::tie(t.first, t.second) >> oa; 20 | return oa; 21 | } 22 | 23 | template ::value, int>::type = 0> 24 | serialize_iarchive& operator<<(T& t, serialize_iarchive& ia) { 25 | using first_type = detail::remove_cvref_t; 26 | using second_type = detail::remove_cvref_t; 27 | auto& tt = (std::pair&)t; 28 | std::tuple tup; 29 | tup << ia; 30 | std::tie(tt.first, tt.second) = std::move(tup); 31 | return ia; 32 | } 33 | 34 | } // namespace rpc_core 35 | -------------------------------------------------------------------------------- /include/rpc_core/serialize/std_set.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace rpc_core { 7 | 8 | namespace detail { 9 | 10 | template 11 | struct is_std_set_like : std::false_type {}; 12 | 13 | template 14 | struct is_std_set_like> : std::true_type {}; 15 | 16 | template 17 | struct is_std_set_like> : std::true_type {}; 18 | 19 | template 20 | struct is_std_set_like> : std::true_type {}; 21 | 22 | template 23 | struct is_std_set_like> : std::true_type {}; 24 | 25 | } // namespace detail 26 | 27 | template ::value, int>::type = 0> 28 | serialize_oarchive& operator>>(const T& t, serialize_oarchive& oa) { 29 | detail::auto_size size(t.size()); 30 | size >> oa; 31 | for (auto& item : t) { 32 | if (std::is_fundamental>::value) { 33 | item >> oa; 34 | } else { 35 | serialize_oarchive tmp; 36 | item >> tmp; 37 | tmp >> oa; 38 | } 39 | } 40 | return oa; 41 | } 42 | 43 | template ::value, int>::type = 0> 44 | serialize_iarchive& operator<<(T& t, serialize_iarchive& ia) { 45 | detail::auto_size size; 46 | size << ia; 47 | for (size_t i = 0; i < size.value; ++i) { 48 | typename T::value_type item; 49 | if (std::is_fundamental>::value) { 50 | item << ia; 51 | } else { 52 | serialize_iarchive tmp; 53 | tmp << ia; 54 | item << tmp; 55 | if (tmp.error) { 56 | ia.error = true; 57 | break; 58 | } 59 | } 60 | t.emplace(std::move(item)); 61 | } 62 | return ia; 63 | } 64 | 65 | } // namespace rpc_core 66 | -------------------------------------------------------------------------------- /include/rpc_core/serialize/std_shared_ptr.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace rpc_core { 6 | 7 | namespace detail { 8 | 9 | template 10 | struct is_std_shared_ptr : std::false_type {}; 11 | 12 | template 13 | struct is_std_shared_ptr> : std::true_type {}; 14 | 15 | } // namespace detail 16 | 17 | template ::value, int>::type = 0> 18 | inline serialize_oarchive& operator>>(const T& t, serialize_oarchive& oa) { 19 | if (t != nullptr) { 20 | true >> oa; 21 | *t >> oa; 22 | } else { 23 | false >> oa; 24 | } 25 | return oa; 26 | } 27 | 28 | template ::value, int>::type = 0> 29 | inline serialize_iarchive& operator<<(T& t, serialize_iarchive& ia) { 30 | bool notnull; 31 | notnull << ia; 32 | if (notnull) { 33 | using Type = typename T::element_type; 34 | t = std::make_shared(); 35 | *t << ia; 36 | } 37 | return ia; 38 | } 39 | 40 | } // namespace rpc_core 41 | -------------------------------------------------------------------------------- /include/rpc_core/serialize/std_tuple.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "../detail/callable/helpers.hpp" 6 | 7 | namespace rpc_core { 8 | 9 | namespace detail { 10 | 11 | template 12 | using tuple_element_t = detail::remove_cvref_t>::type>; 13 | 14 | template 15 | struct is_tuple : std::false_type {}; 16 | 17 | template 18 | struct is_tuple> : std::true_type {}; 19 | 20 | template 21 | struct is_std_ignore { 22 | static const bool value = std::is_same, T>::value; 23 | }; 24 | 25 | enum class tuple_serialize_type { 26 | Ignore, 27 | RawType, 28 | Normal, 29 | }; 30 | 31 | template 32 | struct tuple_serialize_type_check { 33 | static constexpr tuple_serialize_type value = std::is_fundamental::value ? tuple_serialize_type::RawType 34 | : is_std_ignore::value ? tuple_serialize_type::Ignore 35 | : tuple_serialize_type::Normal; 36 | }; 37 | 38 | template 39 | struct tuple_serialize_helper_impl; 40 | 41 | template 42 | struct tuple_serialize_helper_impl { 43 | static void serialize(const Tuple& t, serialize_oarchive& oa) { 44 | serialize_oarchive tmp; 45 | std::get(t) >> tmp; 46 | tmp >> oa; 47 | } 48 | }; 49 | 50 | template 51 | struct tuple_serialize_helper_impl { 52 | static void serialize(const Tuple& t, serialize_oarchive& oa) { 53 | std::get(t) >> oa; 54 | } 55 | }; 56 | 57 | template 58 | struct tuple_serialize_helper_impl { 59 | static void serialize(const Tuple& t, serialize_oarchive& oa) { 60 | RPC_CORE_UNUSED(t); 61 | RPC_CORE_UNUSED(oa); 62 | } 63 | }; 64 | 65 | template 66 | struct tuple_serialize_helper { 67 | static void serialize(const Tuple& t, serialize_oarchive& oa) { 68 | tuple_serialize_helper::serialize(t, oa); 69 | tuple_serialize_helper_impl>::value>::serialize(t, oa); 70 | } 71 | }; 72 | 73 | template 74 | struct tuple_serialize_helper { 75 | static void serialize(const Tuple& t, serialize_oarchive& oa) { 76 | tuple_serialize_helper_impl>::value>::serialize(t, oa); 77 | } 78 | }; 79 | 80 | template 81 | void tuple_serialize(const std::tuple& t, serialize_oarchive& oa) { 82 | tuple_serialize_helper::serialize(t, oa); 83 | } 84 | 85 | template 86 | struct tuple_de_serialize_helper_impl; 87 | 88 | template 89 | struct tuple_de_serialize_helper_impl { 90 | static void de_serialize(Tuple& t, serialize_iarchive& ia) { 91 | serialize_iarchive tmp; 92 | tmp << ia; 93 | std::get(t) << tmp; 94 | } 95 | }; 96 | 97 | template 98 | struct tuple_de_serialize_helper_impl { 99 | static void de_serialize(Tuple& t, serialize_iarchive& ia) { 100 | std::get(t) << ia; 101 | } 102 | }; 103 | 104 | template 105 | struct tuple_de_serialize_helper_impl { 106 | static void de_serialize(Tuple& t, serialize_iarchive& ia) { 107 | RPC_CORE_UNUSED(t); 108 | RPC_CORE_UNUSED(ia); 109 | } 110 | }; 111 | 112 | template 113 | struct tuple_de_serialize_helper { 114 | static void de_serialize(Tuple& t, serialize_iarchive& ia) { 115 | tuple_de_serialize_helper::de_serialize(t, ia); 116 | if (ia.error) return; 117 | tuple_de_serialize_helper_impl>::value>::de_serialize(t, ia); 118 | } 119 | }; 120 | 121 | template 122 | struct tuple_de_serialize_helper { 123 | static void de_serialize(Tuple& t, serialize_iarchive& ia) { 124 | tuple_de_serialize_helper_impl>::value>::de_serialize(t, ia); 125 | } 126 | }; 127 | 128 | template 129 | void tuple_de_serialize(std::tuple& t, serialize_iarchive& ia) { 130 | tuple_de_serialize_helper::de_serialize(t, ia); 131 | } 132 | 133 | } // namespace detail 134 | 135 | template ::value, int>::type = 0> 136 | serialize_oarchive& operator>>(const T& t, serialize_oarchive& oa) { 137 | detail::tuple_serialize(t, oa); 138 | return oa; 139 | } 140 | 141 | template ::value, int>::type = 0> 142 | serialize_iarchive& operator<<(T& t, serialize_iarchive& ia) { 143 | detail::tuple_de_serialize(t, ia); 144 | return ia; 145 | } 146 | 147 | } // namespace rpc_core 148 | -------------------------------------------------------------------------------- /include/rpc_core/serialize/std_unique_ptr.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace rpc_core { 6 | 7 | namespace detail { 8 | 9 | template 10 | struct is_std_unique_ptr : std::false_type {}; 11 | 12 | template 13 | struct is_std_unique_ptr> : std::true_type {}; 14 | 15 | } // namespace detail 16 | 17 | template ::value, int>::type = 0> 18 | inline serialize_oarchive& operator>>(const T& t, serialize_oarchive& oa) { 19 | if (t != nullptr) { 20 | true >> oa; 21 | *t >> oa; 22 | } else { 23 | false >> oa; 24 | } 25 | return oa; 26 | } 27 | 28 | template ::value, int>::type = 0> 29 | inline serialize_iarchive& operator<<(T& t, serialize_iarchive& ia) { 30 | bool notnull; 31 | notnull << ia; 32 | if (notnull) { 33 | using Type = typename T::element_type; 34 | t = std::unique_ptr(new Type); 35 | *t << ia; 36 | } 37 | return ia; 38 | } 39 | 40 | } // namespace rpc_core 41 | -------------------------------------------------------------------------------- /include/rpc_core/serialize/type_enum.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace rpc_core { 4 | 5 | template ::value, int>::type = 0> 6 | inline serialize_oarchive& operator>>(const T& t, serialize_oarchive& oa) { 7 | detail::auto_uintmax impl((uintmax_t)t); 8 | impl >> oa; 9 | return oa; 10 | } 11 | 12 | template ::value, int>::type = 0> 13 | inline serialize_iarchive& operator<<(T& t, serialize_iarchive& ia) { 14 | detail::auto_uintmax impl; 15 | impl << ia; 16 | t = (T)impl.value; 17 | return ia; 18 | } 19 | 20 | } // namespace rpc_core 21 | -------------------------------------------------------------------------------- /include/rpc_core/serialize/type_ptr.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace rpc_core { 6 | 7 | template ::value, int>::type = 0> 8 | inline serialize_oarchive& operator>>(const T& t, serialize_oarchive& oa) { 9 | auto ptr = (intptr_t)t; 10 | ptr >> oa; 11 | return oa; 12 | } 13 | 14 | template ::value, int>::type = 0> 15 | inline serialize_iarchive& operator<<(T& t, serialize_iarchive& ia) { 16 | intptr_t ptr; 17 | ptr << ia; 18 | t = (T)ptr; 19 | return ia; 20 | } 21 | 22 | } // namespace rpc_core 23 | -------------------------------------------------------------------------------- /include/rpc_core/serialize/type_raw.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "detail/auto_size.hpp" 7 | 8 | #define RPC_CORE_DETAIL_DEFINE_RAW_TYPE(type_raw, type_size) \ 9 | static_assert(sizeof(type_raw) <= type_size, ""); \ 10 | inline serialize_oarchive& operator>>(const type_raw& t, serialize_oarchive& oa) { \ 11 | oa.data.append((char*)&t, type_size); \ 12 | return oa; \ 13 | } \ 14 | inline serialize_iarchive& operator<<(type_raw& t, serialize_iarchive& ia) { \ 15 | t = {}; \ 16 | memcpy(&t, ia.data, detail::min(sizeof(t), type_size)); \ 17 | ia.data += type_size; \ 18 | ia.size -= type_size; \ 19 | return ia; \ 20 | } 21 | 22 | #define RPC_CORE_DETAIL_DEFINE_RAW_TYPE_AUTO_SIZE(type_raw, type_auto) \ 23 | inline serialize_oarchive& operator>>(const type_raw& t, serialize_oarchive& oa) { \ 24 | type_auto impl(t); \ 25 | impl >> oa; \ 26 | return oa; \ 27 | } \ 28 | inline serialize_iarchive& operator<<(type_raw& t, serialize_iarchive& ia) { \ 29 | type_auto impl; \ 30 | impl << ia; \ 31 | t = (type_raw)impl.value; \ 32 | return ia; \ 33 | } 34 | 35 | namespace rpc_core { 36 | 37 | RPC_CORE_DETAIL_DEFINE_RAW_TYPE(bool, 1); 38 | RPC_CORE_DETAIL_DEFINE_RAW_TYPE(char, 1); 39 | RPC_CORE_DETAIL_DEFINE_RAW_TYPE(signed char, 1); 40 | RPC_CORE_DETAIL_DEFINE_RAW_TYPE(unsigned char, 1); 41 | RPC_CORE_DETAIL_DEFINE_RAW_TYPE(short, 2); 42 | RPC_CORE_DETAIL_DEFINE_RAW_TYPE(unsigned short, 2); 43 | RPC_CORE_DETAIL_DEFINE_RAW_TYPE_AUTO_SIZE(int, detail::auto_intmax); 44 | RPC_CORE_DETAIL_DEFINE_RAW_TYPE_AUTO_SIZE(unsigned int, detail::auto_uintmax); 45 | RPC_CORE_DETAIL_DEFINE_RAW_TYPE_AUTO_SIZE(long, detail::auto_intmax); 46 | RPC_CORE_DETAIL_DEFINE_RAW_TYPE_AUTO_SIZE(unsigned long, detail::auto_uintmax); 47 | RPC_CORE_DETAIL_DEFINE_RAW_TYPE_AUTO_SIZE(long long, detail::auto_intmax); 48 | RPC_CORE_DETAIL_DEFINE_RAW_TYPE_AUTO_SIZE(unsigned long long, detail::auto_uintmax); 49 | 50 | RPC_CORE_DETAIL_DEFINE_RAW_TYPE(float, 4); 51 | RPC_CORE_DETAIL_DEFINE_RAW_TYPE(double, 8); 52 | RPC_CORE_DETAIL_DEFINE_RAW_TYPE(long double, 16); 53 | 54 | } // namespace rpc_core 55 | -------------------------------------------------------------------------------- /include/rpc_core/serialize/type_void.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace rpc_core { 4 | 5 | template ::value, int>::type = 0> 6 | inline serialize_oarchive& operator>>(const T& t, serialize_oarchive& oa) { 7 | RPC_CORE_UNUSED(t); 8 | return oa; 9 | } 10 | 11 | template ::value, int>::type = 0> 12 | inline serialize_iarchive& operator<<(T& t, serialize_iarchive& ia) { 13 | RPC_CORE_UNUSED(t); 14 | return ia; 15 | } 16 | 17 | } // namespace rpc_core 18 | -------------------------------------------------------------------------------- /include/rpc_core/serialize_api.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // config 4 | #include "config.hpp" 5 | 6 | // include 7 | #include "serialize_type.hpp" 8 | 9 | namespace rpc_core { 10 | 11 | template 12 | inline std::string serialize(T&& t) { 13 | serialize_oarchive ar; 14 | std::forward(t) >> ar; 15 | return std::move(ar.data); 16 | } 17 | 18 | template 19 | inline bool deserialize(const detail::string_view& data, T& t) { 20 | serialize_iarchive ar(data); 21 | t << ar; 22 | return !ar.error; 23 | } 24 | 25 | inline bool deserialize(std::string&& data, std::string& t) { 26 | std::swap(data, t); 27 | return true; 28 | } 29 | 30 | } // namespace rpc_core 31 | -------------------------------------------------------------------------------- /include/rpc_core/serialize_nlohmann_json.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "detail/log.h" 4 | #include "detail/string_view.hpp" 5 | #include "nlohmann/json.hpp" 6 | 7 | namespace rpc_core { 8 | 9 | template 10 | inline std::string serialize(T&& t) { 11 | return nlohmann::json(t).dump(-1); 12 | } 13 | 14 | template 15 | inline bool deserialize(const detail::string_view& data, T& t) { 16 | try { 17 | t = nlohmann::json::parse(data.data(), data.data() + data.size()).get(); 18 | return true; 19 | } catch (std::exception& e) { 20 | RPC_CORE_LOGE("deserialize: %s", e.what()); 21 | return false; 22 | } 23 | } 24 | 25 | } // namespace rpc_core 26 | -------------------------------------------------------------------------------- /include/rpc_core/serialize_type.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // config 4 | #include "config.hpp" 5 | 6 | // include 7 | #include "detail/noncopyable.hpp" 8 | #include "detail/string_view.hpp" 9 | #include "serialize/detail/auto_size.hpp" 10 | #include "type.hpp" 11 | 12 | namespace rpc_core { 13 | 14 | struct serialize_oarchive : detail::noncopyable { 15 | std::string data; 16 | }; 17 | 18 | inline serialize_oarchive& operator>>(const serialize_oarchive& t, serialize_oarchive& oa) { 19 | oa.data.append(detail::auto_size(t.data.size()).serialize()); 20 | oa.data.append(t.data); 21 | return oa; 22 | } 23 | 24 | inline serialize_oarchive& operator>>(serialize_oarchive&& t, serialize_oarchive& oa) { 25 | if (oa.data.empty()) { 26 | oa.data = std::move(t.data); 27 | return oa; 28 | } 29 | oa.data.append(detail::auto_size(t.data.size()).serialize()); 30 | oa.data.append(t.data); 31 | return oa; 32 | } 33 | 34 | struct serialize_iarchive : detail::noncopyable { 35 | serialize_iarchive() = default; 36 | explicit serialize_iarchive(const detail::string_view& sv) : data(sv.data()), size(sv.size()) {} 37 | serialize_iarchive(const char* data, size_t size) : data(data), size(size) {} 38 | const char* data = nullptr; 39 | size_t size = 0; 40 | bool error = false; 41 | }; 42 | 43 | inline serialize_iarchive& operator<<(serialize_iarchive& t, serialize_iarchive& ia) { 44 | detail::auto_size size; 45 | int cost = size.deserialize(ia.data); 46 | ia.data += cost; 47 | t.data = ia.data; 48 | t.size = size.value; 49 | ia.data += size.value; 50 | ia.size -= cost + size.value; 51 | return ia; 52 | } 53 | 54 | template ::value, int>::type = 0> 55 | inline serialize_oarchive& operator>>(const T& t, serialize_oarchive& oa) { 56 | oa.data.append(t.serialize()); 57 | return oa; 58 | } 59 | 60 | template ::value, int>::type = 0> 61 | inline serialize_iarchive& operator<<(T& t, serialize_iarchive& ia) { 62 | int cost = t.deserialize((uint8_t*)ia.data); 63 | ia.data += cost; 64 | ia.size -= cost; 65 | return ia; 66 | } 67 | 68 | } // namespace rpc_core 69 | -------------------------------------------------------------------------------- /include/rpc_core/type.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | // config 7 | #include "config.hpp" 8 | 9 | namespace rpc_core { 10 | 11 | #define RPC_CORE_UNUSED(x) (void)x 12 | 13 | using cmd_type = std::string; 14 | 15 | using seq_type = uint32_t; 16 | 17 | } // namespace rpc_core 18 | -------------------------------------------------------------------------------- /include/rpc_core/version.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define RPC_CORE_VER_MAJOR 2 4 | #define RPC_CORE_VER_MINOR 1 5 | #define RPC_CORE_VER_PATCH 0 6 | 7 | #define RPC_CORE_TO_VERSION(major, minor, patch) (major * 10000 + minor * 100 + patch) 8 | #define RPC_CORE_VERSION RPC_CORE_TO_VERSION(RPC_CORE_VER_MAJOR, RPC_CORE_VER_MINOR, RPC_CORE_VER_PATCH) 9 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rpc_core", 3 | "description": "a tiny c++14 rpc library, supports all platforms (macOS, Linux, Windows, iOS, Android, etc.) and most microchips (Arduino, STM32, ESP32/ESP8266, etc.)", 4 | "keywords": "rpc", 5 | "authors": { 6 | "name": "shuai132", 7 | "maintainer": true 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/shuai132/rpc_core.git" 12 | }, 13 | "version": "2.1.0", 14 | "build": { 15 | "srcDir": "_", 16 | "flags": [ 17 | "-I include" 18 | ] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /rust/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | /.idea/ 4 | -------------------------------------------------------------------------------- /rust/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rpc-core" 3 | version = "0.3.2" 4 | description = "a tiny rpc library for rust" 5 | categories = ["network-programming", "embedded"] 6 | homepage = "https://github.com/shuai132/rpc_core/tree/master/rust" 7 | repository = "https://github.com/shuai132/rpc_core" 8 | authors = ["liushuai <770722922@qq.com>"] 9 | readme = "README.md" 10 | license = "MIT" 11 | edition = "2021" 12 | rust-version = "1.81" 13 | autotests = true 14 | 15 | [dependencies] 16 | bitflags = "2.4" 17 | log = "0.4" 18 | serde = "1.0" 19 | serde_json = "1.0" 20 | 21 | tokio = { version = "1", features = ["rt", "net", "io-util", "macros"], optional = true } 22 | tokio-stream = { version = "0.1", optional = true } 23 | 24 | [features] 25 | default = [] 26 | full = ["net"] 27 | net = ["tokio", "tokio-stream"] 28 | 29 | [package.metadata.docs.rs] 30 | features = ["full"] 31 | 32 | [dev-dependencies] 33 | env_logger = "0.11.5" 34 | 35 | [[test]] 36 | name = "rpc" 37 | path = "src/tests/rpc.rs" 38 | 39 | [[test]] 40 | name = "net_rpc" 41 | path = "src/tests/net_rpc.rs" 42 | required-features = ["net"] 43 | 44 | [[test]] 45 | name = "net_tcp" 46 | path = "src/tests/net_tcp.rs" 47 | required-features = ["net"] 48 | 49 | [[example]] 50 | name = "tcp_client" 51 | path = "src/examples/tcp_client.rs" 52 | required-features = ["net"] 53 | 54 | [[example]] 55 | name = "tcp_server" 56 | path = "src/examples/tcp_server.rs" 57 | required-features = ["net"] 58 | 59 | [[example]] 60 | name = "rpc_client" 61 | path = "src/examples/rpc_client.rs" 62 | required-features = ["net"] 63 | 64 | [[example]] 65 | name = "rpc_server" 66 | path = "src/examples/rpc_server.rs" 67 | required-features = ["net"] 68 | -------------------------------------------------------------------------------- /rust/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023-2024 liushuai 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /rust/README.md: -------------------------------------------------------------------------------- 1 | # rpc-core 2 | 3 | [![Build Status](https://github.com/shuai132/rpc_core/workflows/rust/badge.svg)](https://github.com/shuai132/rpc_core/actions?workflow=rust) 4 | [![Latest version](https://img.shields.io/crates/v/rpc-core.svg)](https://crates.io/crates/rpc-core) 5 | [![Documentation](https://docs.rs/rpc-core/badge.svg)](https://docs.rs/rpc-core) 6 | ![License](https://img.shields.io/crates/l/rpc-core.svg) 7 | 8 | # Usage 9 | 10 | Run the following Cargo command in your project directory: 11 | 12 | ```shell 13 | cargo add rpc-core 14 | ``` 15 | 16 | Or add the following line to your Cargo.toml: 17 | 18 | ```toml 19 | [dependencies] 20 | rpc-core = { version = "0.3.2", features = ["net"] } 21 | ``` 22 | 23 | # Example 24 | 25 | See [src/tests](src/tests) for details: 26 | 27 | * receiver 28 | ```rust 29 | fn subscribe() { 30 | rpc_s.subscribe("cmd", |msg: String| -> String { 31 | assert_eq!(msg, "hello"); 32 | "world".to_string() 33 | }); 34 | } 35 | 36 | ``` 37 | 38 | * sender (callback) 39 | ```rust 40 | fn call() { 41 | rpc_c.cmd("cmd") 42 | .msg("hello") 43 | .rsp(|msg: String| { 44 | assert_eq!(msg, "world"); 45 | }) 46 | .call(); 47 | } 48 | ``` 49 | 50 | * sender (future) 51 | ```rust 52 | async fn call() { 53 | let result = rpc_c.cmd("cmd").msg("hello").future::().await; 54 | assert_eq!(result.result.unwrap(), "world"); 55 | } 56 | ``` 57 | 58 | # Features 59 | 60 | ## net 61 | 62 | See `examples` for details: [src/examples](src/examples) 63 | 64 | * server 65 | ```rust 66 | fn server() { 67 | let rpc = Rpc::new(None); 68 | rpc.subscribe("cmd", |msg: String| -> String { 69 | assert_eq!(msg, "hello"); 70 | "world".to_string() 71 | }); 72 | 73 | let server = rpc_server::RpcServer::new(6666, RpcConfigBuilder::new().rpc(Some(rpc.clone())).build()); 74 | server.start(); 75 | } 76 | ``` 77 | 78 | * client 79 | ```rust 80 | async fn client() { 81 | let rpc = Rpc::new(None); 82 | let client = rpc_client::RpcClient::new(RpcConfigBuilder::new().rpc(Some(rpc.clone())).build()); 83 | client.set_reconnect(1000); 84 | client.open("localhost", 6666); 85 | 86 | let result = rpc.cmd("cmd").msg("hello").future::().await; 87 | assert_eq!(result.result.unwrap(), "world"); 88 | } 89 | ``` 90 | 91 | # License 92 | 93 | This project is licensed under the [MIT license](LICENSE). 94 | -------------------------------------------------------------------------------- /rust/src/connection.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | use std::rc::Rc; 3 | 4 | pub trait Connection { 5 | fn set_send_package_impl(&mut self, handle: Box)>); 6 | fn send_package(&self, package: Vec); 7 | fn set_recv_package_impl(&mut self, handle: Box)>); 8 | fn on_recv_package(&self, package: Vec); 9 | } 10 | 11 | #[derive(Default)] 12 | pub struct DefaultConnection { 13 | send_package_impl: Option)>>, 14 | recv_package_impl: Option)>>, 15 | } 16 | 17 | impl DefaultConnection { 18 | pub fn new() -> Rc> { 19 | Rc::new(RefCell::from(DefaultConnection::default())) 20 | } 21 | } 22 | 23 | impl Connection for DefaultConnection { 24 | fn set_send_package_impl(&mut self, handle: Box)>) { 25 | self.send_package_impl = Some(handle); 26 | } 27 | 28 | fn send_package(&self, package: Vec) { 29 | if let Some(handle) = self.send_package_impl.as_ref() { 30 | handle(package); 31 | } 32 | } 33 | 34 | fn set_recv_package_impl(&mut self, handle: Box)>) { 35 | self.recv_package_impl = Some(handle); 36 | } 37 | 38 | fn on_recv_package(&self, package: Vec) { 39 | if let Some(handle) = self.recv_package_impl.as_ref() { 40 | handle(package); 41 | } 42 | } 43 | } 44 | 45 | pub struct LoopbackConnection; 46 | 47 | impl LoopbackConnection { 48 | #[allow(clippy::new_ret_no_self)] 49 | pub fn new() -> ( 50 | Rc>, 51 | Rc>, 52 | ) { 53 | let c1 = Rc::new(RefCell::new(DefaultConnection::default())); 54 | let c1_weak = Rc::downgrade(&c1); 55 | let c2 = Rc::new(RefCell::new(DefaultConnection::default())); 56 | let c2_weak = Rc::downgrade(&c2); 57 | 58 | c1.borrow_mut().send_package_impl = Some(Box::new(move |package: Vec| { 59 | c2_weak.upgrade().unwrap().borrow().on_recv_package(package); 60 | })); 61 | c2.borrow_mut().send_package_impl = Some(Box::new(move |package: Vec| { 62 | c1_weak.upgrade().unwrap().borrow().on_recv_package(package); 63 | })); 64 | (c1, c2) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /rust/src/detail/coder.rs: -------------------------------------------------------------------------------- 1 | use crate::detail::msg_wrapper::{MsgType, MsgWrapper}; 2 | use crate::type_def::SeqType; 3 | 4 | const PAYLOAD_MIN_LEN: usize = 4 /*seq*/ + 2 /*cmdLen*/ + 1 /*type*/; 5 | 6 | pub fn serialize(msg: &MsgWrapper) -> Vec { 7 | let mut payload: Vec = Vec::with_capacity(PAYLOAD_MIN_LEN); 8 | payload.extend_from_slice(&msg.seq.to_le_bytes()); 9 | let cmd_len: u16 = msg.cmd.len() as u16; 10 | payload.extend_from_slice(&cmd_len.to_le_bytes()); 11 | payload.extend(msg.cmd.bytes()); 12 | let type_: u8 = msg.type_.bits(); 13 | payload.extend_from_slice(&type_.to_le_bytes()); 14 | if let Some(request_payload) = &msg.request_payload { 15 | payload.extend(request_payload); 16 | } else { 17 | payload.extend(&msg.data); 18 | } 19 | payload 20 | } 21 | 22 | pub fn deserialize(payload: &[u8]) -> Option { 23 | if payload.len() < PAYLOAD_MIN_LEN { 24 | return None; 25 | } 26 | 27 | let mut msg = MsgWrapper::new(); 28 | unsafe { 29 | let mut p = payload.as_ptr(); 30 | let pend = p.add(payload.len()); 31 | 32 | msg.seq = *(p as *const SeqType); 33 | p = p.add(4); 34 | 35 | let cmd_len: u16 = *(p as *const u16); 36 | p = p.add(2); 37 | 38 | msg.cmd = String::from_utf8_unchecked(Vec::from(std::slice::from_raw_parts( 39 | p as *mut u8, 40 | cmd_len as usize, 41 | ))); 42 | p = p.add(cmd_len as usize); 43 | 44 | msg.type_ = MsgType::from_bits(*p)?; 45 | p = p.add(1); 46 | 47 | msg.data = Vec::from(std::slice::from_raw_parts(p, pend.offset_from(p) as usize)); 48 | } 49 | 50 | Some(msg) 51 | } 52 | -------------------------------------------------------------------------------- /rust/src/detail/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod coder; 2 | pub(crate) mod msg_dispatcher; 3 | pub(crate) mod msg_wrapper; 4 | -------------------------------------------------------------------------------- /rust/src/detail/msg_dispatcher.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | use std::collections::HashMap; 3 | use std::rc::{Rc, Weak}; 4 | 5 | use log::{debug, error, trace, warn}; 6 | 7 | use crate::connection::Connection; 8 | use crate::detail::coder; 9 | use crate::detail::msg_wrapper::{MsgType, MsgWrapper}; 10 | use crate::type_def::{CmdType, SeqType}; 11 | 12 | pub type TimeoutCb = dyn Fn(); 13 | pub type TimerImpl = dyn Fn(u32, Box); 14 | 15 | type CmdHandle = Box Option>; 16 | pub type RspHandle = dyn Fn(MsgWrapper) -> bool; 17 | 18 | pub struct MsgDispatcher { 19 | conn: Weak>, 20 | cmd_handle_map: HashMap, 21 | rsp_handle_map: HashMap>, 22 | timer_impl: Option>, 23 | this: Weak>, 24 | } 25 | 26 | impl MsgDispatcher { 27 | pub fn new(conn: Rc>) -> Rc> { 28 | Rc::>::new_cyclic(|this_weak| { 29 | let dispatcher = RefCell::new(Self { 30 | conn: Rc::downgrade(&conn), 31 | cmd_handle_map: HashMap::new(), 32 | rsp_handle_map: HashMap::new(), 33 | timer_impl: None, 34 | this: this_weak.clone(), 35 | }); 36 | 37 | let this_weak = this_weak.clone(); 38 | conn.borrow_mut() 39 | .set_recv_package_impl(Box::new(move |payload| { 40 | let Some(this) = this_weak.upgrade() else { 41 | return; 42 | }; 43 | if let Some(msg) = coder::deserialize(&payload) { 44 | this.borrow_mut().dispatch(msg); 45 | } else { 46 | error!("deserialize error"); 47 | } 48 | })); 49 | dispatcher 50 | }) 51 | } 52 | } 53 | 54 | impl MsgDispatcher { 55 | pub fn subscribe_cmd(&mut self, cmd: String, handle: CmdHandle) { 56 | self.cmd_handle_map.insert(cmd, handle); 57 | } 58 | 59 | pub fn unsubscribe_cmd(&mut self, cmd: String) { 60 | if self.cmd_handle_map.remove(&cmd).is_some() { 61 | debug!("erase cmd: {}", cmd); 62 | } else { 63 | debug!("not subscribe cmd for: {}", cmd); 64 | } 65 | } 66 | 67 | pub fn subscribe_rsp( 68 | &mut self, 69 | seq: SeqType, 70 | rsp_handle: Rc, 71 | timeout_cb: Option>, 72 | timeout_ms: u32, 73 | ) { 74 | self.rsp_handle_map.insert(seq, rsp_handle); 75 | if let Some(timer_impl) = &self.timer_impl { 76 | let this_weak = self.this.clone(); 77 | timer_impl( 78 | timeout_ms, 79 | Box::new(move || { 80 | let Some(this) = this_weak.upgrade() else { 81 | debug!("seq:{} timeout after destroy", seq); 82 | return; 83 | }; 84 | 85 | let mut this = this.borrow_mut(); 86 | if this.rsp_handle_map.remove(&seq).is_some() { 87 | if let Some(timeout_cb) = &timeout_cb { 88 | timeout_cb(); 89 | } 90 | trace!( 91 | "Timeout seq={}, rsp_handle_map.size={}", 92 | seq, 93 | this.rsp_handle_map.len() 94 | ); 95 | } 96 | }), 97 | ); 98 | } else { 99 | warn!("no timeout will cause memory leak!"); 100 | } 101 | } 102 | 103 | pub fn dispatch(&mut self, mut msg: MsgWrapper) { 104 | if msg.type_.contains(MsgType::Command) { 105 | // ping 106 | let is_ping = msg.type_.contains(MsgType::Ping); 107 | if is_ping { 108 | debug!("<= seq:{} type:ping", &msg.seq); 109 | msg.type_ = MsgType::Response | MsgType::Pong; 110 | debug!("=> seq:{} type:pong", &msg.seq); 111 | if let Some(conn) = self.conn.upgrade() { 112 | conn.borrow().send_package(coder::serialize(&msg)); 113 | } 114 | return; 115 | } 116 | 117 | // command 118 | debug!("<= seq:{} cmd:{}", &msg.seq, &msg.cmd); 119 | let cmd = &msg.cmd; 120 | if let Some(handle) = self.cmd_handle_map.get(cmd) { 121 | let need_rsp = msg.type_.contains(MsgType::NeedRsp); 122 | let resp = handle(msg); 123 | if need_rsp && resp.is_some() { 124 | let rsp = resp.unwrap(); 125 | debug!("=> seq:{} type:rsp", &rsp.seq); 126 | if let Some(conn) = self.conn.upgrade() { 127 | conn.borrow().send_package(coder::serialize(&rsp)); 128 | } 129 | } 130 | } else { 131 | debug!("not subscribe cmd for: {}", cmd); 132 | let need_rsp = msg.type_.contains(MsgType::NeedRsp); 133 | if need_rsp { 134 | debug!("=> seq:{} type:rsp", msg.seq); 135 | let mut rsp = MsgWrapper::new(); 136 | rsp.seq = msg.seq; 137 | rsp.type_ = MsgType::Response | MsgType::NoSuchCmd; 138 | if let Some(conn) = self.conn.upgrade() { 139 | conn.borrow().send_package(coder::serialize(&rsp)); 140 | } 141 | } 142 | } 143 | } else if msg.type_.contains(MsgType::Response) { 144 | // pong or response 145 | debug!( 146 | "<= seq:{} type:{}", 147 | msg.seq, 148 | if msg.type_.contains(MsgType::Pong) { 149 | "pong" 150 | } else { 151 | "rsp" 152 | } 153 | ); 154 | if let Some(handle) = self.rsp_handle_map.remove(&msg.seq) { 155 | if handle(msg) { 156 | trace!("rsp_handle_map.size={}", self.rsp_handle_map.len()); 157 | } else { 158 | error!("may deserialize error"); 159 | } 160 | } else { 161 | debug!("no rsp for seq:{}", msg.seq); 162 | return; 163 | } 164 | } else { 165 | error!("unknown type"); 166 | } 167 | } 168 | 169 | pub fn set_timer_impl(&mut self, timer_impl: F) 170 | where 171 | F: Fn(u32, Box) + 'static, 172 | { 173 | self.timer_impl = Some(Rc::new(timer_impl)); 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /rust/src/detail/msg_wrapper.rs: -------------------------------------------------------------------------------- 1 | use bitflags::bitflags; 2 | use log::error; 3 | use serde_json::Error; 4 | 5 | use crate::type_def::{CmdType, SeqType}; 6 | 7 | bitflags! { 8 | pub struct MsgType: u8 { 9 | const Command = 1 << 0; 10 | const Response = 1 << 1; 11 | const NeedRsp = 1 << 2; 12 | const Ping = 1 << 3; 13 | const Pong = 1 << 4; 14 | const NoSuchCmd = 1 << 5; 15 | } 16 | } 17 | 18 | pub struct MsgWrapper { 19 | pub seq: SeqType, 20 | pub type_: MsgType, 21 | pub cmd: CmdType, 22 | pub data: Vec, 23 | pub request_payload: Option>, 24 | } 25 | 26 | impl MsgWrapper { 27 | pub fn new() -> Self { 28 | Self { 29 | seq: 0, 30 | type_: MsgType::Command, 31 | cmd: "".to_string(), 32 | data: vec![], 33 | request_payload: None, 34 | } 35 | } 36 | 37 | pub fn dump(&self) -> String { 38 | format!( 39 | "seq:{}, type:{}, cmd:{}", 40 | self.seq, 41 | self.type_.bits(), 42 | self.cmd 43 | ) 44 | } 45 | 46 | pub fn unpack_as<'a, T>(&'a self) -> Result 47 | where 48 | T: serde::Deserialize<'a>, 49 | { 50 | let r = serde_json::from_slice::(self.data.as_slice()); 51 | if r.is_err() { 52 | error!("deserialize error, msg info:{}", self.dump()); 53 | } 54 | r 55 | } 56 | 57 | pub fn make_rsp(seq: SeqType, rsp: R) -> MsgWrapper 58 | where 59 | R: serde::Serialize, 60 | { 61 | let mut msg = MsgWrapper::new(); 62 | msg.type_ = MsgType::Response; 63 | msg.seq = seq; 64 | msg.data = serde_json::to_string(&rsp).unwrap().into_bytes(); 65 | msg 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /rust/src/dispose.rs: -------------------------------------------------------------------------------- 1 | use std::rc::{Rc, Weak}; 2 | 3 | use crate::request::Request; 4 | 5 | #[derive(Default)] 6 | pub struct Dispose { 7 | requests: Vec>, 8 | } 9 | 10 | impl Dispose { 11 | pub fn new() -> Dispose { 12 | Dispose::default() 13 | } 14 | 15 | pub fn dismiss(&mut self) { 16 | for item in &self.requests { 17 | if let Some(request) = item.upgrade() { 18 | request.cancel(); 19 | } 20 | } 21 | self.requests.clear(); 22 | } 23 | } 24 | 25 | impl Drop for Dispose { 26 | fn drop(&mut self) { 27 | self.dismiss(); 28 | } 29 | } 30 | 31 | impl Dispose { 32 | pub fn add(&mut self, request: &Rc) { 33 | self.requests.push(Rc::downgrade(request)); 34 | } 35 | 36 | pub fn remove(&mut self, request: &Rc) { 37 | self.requests.retain(|r| { 38 | !if let Some(r) = r.upgrade() { 39 | Rc::ptr_eq(&r, request) 40 | } else { 41 | true 42 | } 43 | }); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /rust/src/examples/rpc_client.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | 3 | use log::info; 4 | 5 | use rpc_core::net::config_builder::RpcConfigBuilder; 6 | use rpc_core::net::rpc_client; 7 | use rpc_core::rpc::Rpc; 8 | 9 | fn main() { 10 | std::env::set_var("RUST_LOG", "trace"); 11 | env_logger::init(); 12 | 13 | let runtime = tokio::runtime::Builder::new_current_thread() 14 | .enable_all() 15 | .build() 16 | .unwrap(); 17 | 18 | runtime.block_on(async { 19 | let local = tokio::task::LocalSet::new(); 20 | local 21 | .run_until(async move { 22 | let rpc = Rpc::new(None); 23 | let client = rpc_client::RpcClient::new( 24 | RpcConfigBuilder::new().rpc(Some(rpc.clone())).build(), 25 | ); 26 | client.on_open(|_: Rc| { 27 | info!("on_open"); 28 | }); 29 | client.on_open_failed(|e| { 30 | info!("on_open_failed: {:?}", e); 31 | }); 32 | client.on_close(|| { 33 | info!("on_close"); 34 | }); 35 | client.set_reconnect(1000); 36 | client.open("localhost", 6666); 37 | info!("start..."); 38 | 39 | loop { 40 | tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; 41 | 42 | info!("usage: callback..."); 43 | rpc.cmd("cmd") 44 | .msg("hello") 45 | .rsp(|msg: String| { 46 | info!("### rsp: {msg}"); 47 | }) 48 | .call(); 49 | 50 | info!("usage: future..."); 51 | let result = rpc.cmd("cmd").msg("hello").future::().await; 52 | info!("### rsp: {result:?}"); 53 | } 54 | }) 55 | .await; 56 | }); 57 | } 58 | -------------------------------------------------------------------------------- /rust/src/examples/rpc_server.rs: -------------------------------------------------------------------------------- 1 | use log::info; 2 | 3 | use rpc_core::net::config_builder::RpcConfigBuilder; 4 | use rpc_core::net::rpc_server; 5 | use rpc_core::rpc::Rpc; 6 | 7 | fn main() { 8 | std::env::set_var("RUST_LOG", "trace"); 9 | env_logger::init(); 10 | 11 | let runtime = tokio::runtime::Builder::new_current_thread() 12 | .enable_all() 13 | .build() 14 | .unwrap(); 15 | 16 | runtime.block_on(async { 17 | let local = tokio::task::LocalSet::new(); 18 | local 19 | .run_until(async move { 20 | let rpc = Rpc::new(None); 21 | rpc.subscribe("cmd", |msg: String| -> String { 22 | info!("cmd: {msg}"); 23 | "world".to_string() 24 | }); 25 | 26 | let server = rpc_server::RpcServer::new( 27 | 6666, 28 | RpcConfigBuilder::new().rpc(Some(rpc.clone())).build(), 29 | ); 30 | server.on_session(move |session| { 31 | info!("on_open"); 32 | let session = session.upgrade().unwrap(); 33 | session.on_close(|| { 34 | info!("on_close"); 35 | }); 36 | }); 37 | 38 | info!("start..."); 39 | server.start(); 40 | loop { 41 | tokio::time::sleep(tokio::time::Duration::from_secs(1000)).await; 42 | } 43 | }) 44 | .await; 45 | }); 46 | } 47 | -------------------------------------------------------------------------------- /rust/src/examples/tcp_client.rs: -------------------------------------------------------------------------------- 1 | use log::info; 2 | 3 | use rpc_core::net::config_builder::TcpConfigBuilder; 4 | use rpc_core::net::tcp_client; 5 | 6 | fn main() { 7 | std::env::set_var("RUST_LOG", "trace"); 8 | env_logger::init(); 9 | 10 | let runtime = tokio::runtime::Builder::new_current_thread() 11 | .enable_all() 12 | .build() 13 | .unwrap(); 14 | 15 | runtime.block_on(async { 16 | let local = tokio::task::LocalSet::new(); 17 | local 18 | .run_until(async move { 19 | let client = 20 | tcp_client::TcpClient::new(TcpConfigBuilder::new().auto_pack(false).build()); 21 | let client_weak = client.downgrade(); 22 | client.on_open(move || { 23 | info!("on_open"); 24 | client_weak.upgrade().unwrap().send_str("hello from client"); 25 | }); 26 | client.on_open_failed(|e| { 27 | info!("on_open_failed: {:?}", e); 28 | }); 29 | client.on_data(|data| { 30 | info!("on_data: {}", String::from_utf8_lossy(data.as_slice())); 31 | }); 32 | client.on_close(|| { 33 | info!("on_close"); 34 | }); 35 | client.set_reconnect(1000); 36 | client.open("localhost", 6666); 37 | info!("start..."); 38 | 39 | loop { 40 | tokio::time::sleep(tokio::time::Duration::from_secs(1000)).await; 41 | } 42 | }) 43 | .await; 44 | }); 45 | } 46 | -------------------------------------------------------------------------------- /rust/src/examples/tcp_server.rs: -------------------------------------------------------------------------------- 1 | use log::info; 2 | 3 | use rpc_core::net::config_builder::TcpConfigBuilder; 4 | use rpc_core::net::tcp_server; 5 | 6 | fn main() { 7 | std::env::set_var("RUST_LOG", "trace"); 8 | env_logger::init(); 9 | 10 | let runtime = tokio::runtime::Builder::new_current_thread() 11 | .enable_all() 12 | .build() 13 | .unwrap(); 14 | 15 | runtime.block_on(async { 16 | let local = tokio::task::LocalSet::new(); 17 | local 18 | .run_until(async move { 19 | let server = tcp_server::TcpServer::new( 20 | 6666, 21 | TcpConfigBuilder::new().auto_pack(false).build(), 22 | ); 23 | server.on_session(move |session| { 24 | info!("on_open"); 25 | let session = session.upgrade().unwrap(); 26 | session.on_data(|data| { 27 | info!("on_data: {}", String::from_utf8_lossy(data.as_slice())); 28 | }); 29 | session.on_close(|| { 30 | info!("on_close"); 31 | }); 32 | }); 33 | 34 | info!("start..."); 35 | server.start(); 36 | loop { 37 | tokio::time::sleep(tokio::time::Duration::from_secs(1000)).await; 38 | } 39 | }) 40 | .await; 41 | }); 42 | } 43 | -------------------------------------------------------------------------------- /rust/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod connection; 2 | pub mod dispose; 3 | pub mod request; 4 | pub mod rpc; 5 | pub mod type_def; 6 | 7 | mod detail; 8 | 9 | #[cfg(feature = "net")] 10 | pub mod net; 11 | -------------------------------------------------------------------------------- /rust/src/net/config.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | 3 | pub struct TcpConfig { 4 | pub auto_pack: bool, 5 | pub enable_ipv6: bool, 6 | pub max_body_size: u32, 7 | pub max_send_buffer_size: u32, 8 | pub socket_send_buffer_size: u32, 9 | pub socket_recv_buffer_size: u32, 10 | } 11 | 12 | impl TcpConfig { 13 | pub fn new() -> Self { 14 | Self { 15 | auto_pack: false, 16 | enable_ipv6: false, 17 | max_body_size: 0, 18 | max_send_buffer_size: 0, 19 | socket_send_buffer_size: 0, 20 | socket_recv_buffer_size: 0, 21 | } 22 | } 23 | } 24 | 25 | impl TcpConfig { 26 | pub fn init(&mut self) { 27 | if !self.auto_pack && self.max_body_size == u32::MAX { 28 | self.max_body_size = 1024; 29 | } 30 | } 31 | } 32 | 33 | pub struct RpcConfig { 34 | pub rpc: Option>, 35 | pub ping_interval_ms: u32, 36 | pub pong_timeout_ms: u32, 37 | pub enable_ipv6: bool, 38 | pub max_body_size: u32, 39 | pub max_send_buffer_size: u32, 40 | pub socket_send_buffer_size: u32, 41 | pub socket_recv_buffer_size: u32, 42 | } 43 | 44 | impl RpcConfig { 45 | pub fn to_tcp_config(&self) -> TcpConfig { 46 | TcpConfig { 47 | auto_pack: true, 48 | enable_ipv6: self.enable_ipv6, 49 | max_body_size: self.max_body_size, 50 | max_send_buffer_size: self.max_send_buffer_size, 51 | socket_send_buffer_size: self.socket_send_buffer_size, 52 | socket_recv_buffer_size: self.socket_recv_buffer_size, 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /rust/src/net/config_builder.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | 3 | use crate::net::config::{RpcConfig, TcpConfig}; 4 | 5 | pub struct TcpConfigBuilder { 6 | auto_pack: bool, 7 | enable_ipv6: bool, 8 | max_body_size: u32, 9 | max_send_buffer_size: u32, 10 | socket_send_buffer_size: u32, 11 | socket_recv_buffer_size: u32, 12 | } 13 | 14 | impl TcpConfigBuilder { 15 | pub fn new() -> Self { 16 | Self { 17 | auto_pack: false, 18 | enable_ipv6: false, 19 | max_body_size: 0, 20 | max_send_buffer_size: 0, 21 | socket_send_buffer_size: 0, 22 | socket_recv_buffer_size: 0, 23 | } 24 | } 25 | 26 | pub fn auto_pack(&mut self, auto_pack: bool) -> &mut Self { 27 | self.auto_pack = auto_pack; 28 | self 29 | } 30 | 31 | pub fn enable_ipv6(&mut self, enable_ipv6: bool) -> &mut Self { 32 | self.enable_ipv6 = enable_ipv6; 33 | self 34 | } 35 | 36 | pub fn max_body_size(&mut self, max_body_size: u32) -> &mut Self { 37 | self.max_body_size = max_body_size; 38 | self 39 | } 40 | 41 | pub fn max_send_buffer_size(&mut self, max_send_buffer_size: u32) -> &mut Self { 42 | self.max_send_buffer_size = max_send_buffer_size; 43 | self 44 | } 45 | 46 | pub fn socket_send_buffer_size(&mut self, socket_send_buffer_size: u32) -> &mut Self { 47 | self.socket_send_buffer_size = socket_send_buffer_size; 48 | self 49 | } 50 | 51 | pub fn socket_recv_buffer_size(&mut self, socket_recv_buffer_size: u32) -> &mut Self { 52 | self.socket_recv_buffer_size = socket_recv_buffer_size; 53 | self 54 | } 55 | 56 | pub fn build(&self) -> TcpConfig { 57 | TcpConfig { 58 | auto_pack: self.auto_pack, 59 | enable_ipv6: self.enable_ipv6, 60 | max_body_size: self.max_body_size, 61 | max_send_buffer_size: self.max_send_buffer_size, 62 | socket_send_buffer_size: self.socket_send_buffer_size, 63 | socket_recv_buffer_size: self.socket_recv_buffer_size, 64 | } 65 | } 66 | } 67 | 68 | pub struct RpcConfigBuilder { 69 | rpc: Option>, 70 | ping_interval_ms: u32, 71 | pong_timeout_ms: u32, 72 | enable_ipv6: bool, 73 | max_body_size: u32, 74 | max_send_buffer_size: u32, 75 | socket_send_buffer_size: u32, 76 | socket_recv_buffer_size: u32, 77 | } 78 | 79 | impl RpcConfigBuilder { 80 | pub fn new() -> Self { 81 | RpcConfigBuilder { 82 | rpc: None, 83 | ping_interval_ms: 0, 84 | pong_timeout_ms: 0, 85 | enable_ipv6: false, 86 | max_body_size: 0, 87 | max_send_buffer_size: 0, 88 | socket_send_buffer_size: 0, 89 | socket_recv_buffer_size: 0, 90 | } 91 | } 92 | 93 | pub fn rpc(mut self, rpc: Option>) -> Self { 94 | self.rpc = rpc; 95 | self 96 | } 97 | 98 | pub fn ping_interval_ms(mut self, ping_interval_ms: u32) -> Self { 99 | self.ping_interval_ms = ping_interval_ms; 100 | self 101 | } 102 | 103 | pub fn pong_timeout_ms(mut self, pong_timeout_ms: u32) -> Self { 104 | self.pong_timeout_ms = pong_timeout_ms; 105 | self 106 | } 107 | 108 | pub fn enable_ipv6(mut self, enable_ipv6: bool) -> Self { 109 | self.enable_ipv6 = enable_ipv6; 110 | self 111 | } 112 | 113 | pub fn max_body_size(mut self, max_body_size: u32) -> Self { 114 | self.max_body_size = max_body_size; 115 | self 116 | } 117 | 118 | pub fn max_send_buffer_size(mut self, max_send_buffer_size: u32) -> Self { 119 | self.max_send_buffer_size = max_send_buffer_size; 120 | self 121 | } 122 | 123 | pub fn socket_send_buffer_size(mut self, socket_send_buffer_size: u32) -> Self { 124 | self.socket_send_buffer_size = socket_send_buffer_size; 125 | self 126 | } 127 | 128 | pub fn socket_recv_buffer_size(mut self, socket_recv_buffer_size: u32) -> Self { 129 | self.socket_recv_buffer_size = socket_recv_buffer_size; 130 | self 131 | } 132 | 133 | pub fn build(self) -> RpcConfig { 134 | RpcConfig { 135 | rpc: self.rpc, 136 | ping_interval_ms: self.ping_interval_ms, 137 | pong_timeout_ms: self.pong_timeout_ms, 138 | enable_ipv6: self.enable_ipv6, 139 | max_body_size: self.max_body_size, 140 | max_send_buffer_size: self.max_send_buffer_size, 141 | socket_send_buffer_size: self.socket_send_buffer_size, 142 | socket_recv_buffer_size: self.socket_recv_buffer_size, 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /rust/src/net/detail/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod tcp_channel; 2 | -------------------------------------------------------------------------------- /rust/src/net/detail/tcp_channel.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | use std::collections::VecDeque; 3 | use std::rc::Rc; 4 | 5 | use log::{debug, trace}; 6 | use tokio::io::{AsyncReadExt, AsyncWriteExt, ReadHalf}; 7 | use tokio::net::TcpStream; 8 | use tokio::select; 9 | use tokio::sync::Notify; 10 | 11 | use crate::net::config::TcpConfig; 12 | 13 | pub struct TcpChannel { 14 | config: Rc>, 15 | on_close: RefCell>>, 16 | on_data: RefCell)>>>, 17 | send_queue: RefCell>>, 18 | send_queue_notify: Notify, 19 | quit_notify: Notify, 20 | read_quit_finish_notify: Notify, 21 | write_quit_finish_notify: Notify, 22 | is_open: RefCell, 23 | } 24 | 25 | impl TcpChannel { 26 | pub fn new(config: Rc>) -> Rc { 27 | Rc::new(Self { 28 | config, 29 | on_close: None.into(), 30 | on_data: None.into(), 31 | send_queue: VecDeque::new().into(), 32 | send_queue_notify: Notify::new(), 33 | quit_notify: Notify::new(), 34 | read_quit_finish_notify: Notify::new(), 35 | write_quit_finish_notify: Notify::new(), 36 | is_open: false.into(), 37 | }) 38 | } 39 | 40 | pub fn is_open(&self) -> bool { 41 | *self.is_open.borrow() 42 | } 43 | 44 | pub fn on_data(&self, callback: F) 45 | where 46 | F: Fn(Vec) + 'static, 47 | { 48 | *self.on_data.borrow_mut() = Some(Box::new(callback)); 49 | } 50 | 51 | pub fn on_close(&self, callback: F) 52 | where 53 | F: Fn() + 'static, 54 | { 55 | *self.on_close.borrow_mut() = Some(Box::new(callback)); 56 | } 57 | 58 | pub fn send(&self, data: Vec) { 59 | if !*self.is_open.borrow() { 60 | debug!("channel not connected"); 61 | return; 62 | } 63 | if data.is_empty() { 64 | debug!("send empty ignore"); 65 | return; 66 | } 67 | { 68 | let mut send_queue = self.send_queue.borrow_mut(); 69 | if self.config.borrow().auto_pack { 70 | send_queue.push_back((data.len() as u32).to_le_bytes().to_vec()); 71 | } 72 | send_queue.push_back(data); 73 | } 74 | self.send_queue_notify.notify_one(); 75 | } 76 | 77 | pub fn send_str(&self, data: impl ToString) { 78 | self.send(data.to_string().as_bytes().to_vec()); 79 | } 80 | 81 | pub fn close(&self) { 82 | self.do_close(); 83 | } 84 | 85 | pub async fn wait_close_finish(&self) { 86 | tokio::join!( 87 | self.read_quit_finish_notify.notified(), 88 | self.write_quit_finish_notify.notified(), 89 | ); 90 | } 91 | 92 | pub fn do_open(self: &Rc, stream: TcpStream) { 93 | if *self.is_open.borrow() { 94 | self.do_close(); 95 | } 96 | *self.is_open.borrow_mut() = true; 97 | let this = self.clone(); 98 | tokio::task::spawn_local(async move { 99 | let (read_half, write_half) = tokio::io::split(stream); 100 | 101 | // read loop task 102 | if this.config.borrow().auto_pack { 103 | let this = this.clone(); 104 | tokio::task::spawn_local(async move { 105 | let mut read_half = read_half; 106 | loop { 107 | if !*this.is_open.borrow() { 108 | break; 109 | } 110 | select! { 111 | goon = this.do_read_header(&mut read_half) => { 112 | if !goon { 113 | break; 114 | } 115 | }, 116 | _ = this.quit_notify.notified() => { 117 | break; 118 | }, 119 | } 120 | } 121 | trace!("loop exit: read"); 122 | this.read_quit_finish_notify.notify_one(); 123 | }); 124 | } else { 125 | let this = this.clone(); 126 | tokio::task::spawn_local(async move { 127 | let mut read_half = read_half; 128 | loop { 129 | select! { 130 | goon = this.do_read_data(&mut read_half) => { 131 | if !goon { 132 | break; 133 | } 134 | }, 135 | _ = this.quit_notify.notified() => { 136 | break; 137 | }, 138 | } 139 | } 140 | trace!("loop exit: read"); 141 | this.read_quit_finish_notify.notify_one(); 142 | }); 143 | } 144 | 145 | // write loop task 146 | tokio::task::spawn_local(async move { 147 | let mut write_half = write_half; 148 | loop { 149 | if !*this.is_open.borrow() { 150 | break; 151 | } 152 | 153 | let send_data = this.send_queue.borrow_mut().pop_front(); 154 | if let Some(data) = send_data { 155 | let mut result = None; 156 | select! { 157 | write_ret = write_half.write_all(&data) => { 158 | result = write_ret.ok(); 159 | }, 160 | _ = this.quit_notify.notified() => {}, 161 | } 162 | if result.is_none() { 163 | this.do_close(); 164 | break; 165 | } 166 | } else { 167 | this.send_queue_notify.notified().await; 168 | } 169 | } 170 | trace!("loop exit: write"); 171 | 172 | if let Some(on_close) = this.on_close.borrow().as_ref() { 173 | on_close(); 174 | } 175 | this.write_quit_finish_notify.notify_one(); 176 | }); 177 | }); 178 | } 179 | 180 | fn do_close(&self) { 181 | if !*self.is_open.borrow() { 182 | return; 183 | } 184 | *self.is_open.borrow_mut() = false; 185 | self.quit_notify.notify_waiters(); 186 | self.send_queue_notify.notify_one(); 187 | } 188 | 189 | async fn do_read_data(&self, read_half: &mut ReadHalf) -> bool { 190 | let mut buffer = vec![]; 191 | let read_result = read_half.read_buf(&mut buffer).await.ok(); 192 | 193 | if read_result.is_some() && !buffer.is_empty() { 194 | if let Some(on_data) = self.on_data.borrow().as_ref() { 195 | on_data(buffer); 196 | } 197 | true 198 | } else { 199 | self.do_close(); 200 | false 201 | } 202 | } 203 | 204 | async fn do_read_header(&self, read_half: &mut ReadHalf) -> bool { 205 | let mut buffer = [0u8; 4]; 206 | let read_result = read_half.read_exact(&mut buffer).await.ok(); 207 | 208 | if read_result.is_some() { 209 | let body_size = u32::from_le_bytes(buffer); 210 | self.do_read_body(read_half, body_size).await 211 | } else { 212 | self.do_close(); 213 | false 214 | } 215 | } 216 | 217 | async fn do_read_body(&self, read_half: &mut ReadHalf, body_size: u32) -> bool { 218 | let mut buffer: Vec = vec![0; body_size as usize]; 219 | let read_result = read_half.read_exact(&mut buffer).await.ok(); 220 | 221 | if read_result.is_some() { 222 | if let Some(on_data) = self.on_data.borrow().as_ref() { 223 | on_data(buffer); 224 | } 225 | true 226 | } else { 227 | self.do_close(); 228 | false 229 | } 230 | } 231 | } 232 | 233 | impl Drop for TcpChannel { 234 | fn drop(&mut self) { 235 | trace!("~TcpChannel"); 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /rust/src/net/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod config; 2 | pub mod config_builder; 3 | pub mod rpc_client; 4 | pub mod rpc_server; 5 | pub mod tcp_client; 6 | pub mod tcp_server; 7 | 8 | mod detail; 9 | -------------------------------------------------------------------------------- /rust/src/net/rpc_client.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | use std::error::Error; 3 | use std::rc::Rc; 4 | 5 | use crate::connection::Connection; 6 | use crate::net::config::RpcConfig; 7 | use crate::net::tcp_client::TcpClient; 8 | use crate::rpc::Rpc; 9 | 10 | pub struct RpcClientImpl { 11 | tcp_client: Rc, 12 | config: RpcConfig, 13 | on_open: Option)>>, 14 | on_open_failed: Option>, 15 | on_close: Option>, 16 | connection: Rc>, 17 | rpc: Option>, 18 | } 19 | 20 | pub struct RpcClient { 21 | inner: RefCell, 22 | } 23 | 24 | impl RpcClient { 25 | pub fn new(config: RpcConfig) -> Rc { 26 | let r = Rc::new(Self { 27 | inner: RefCell::new(RpcClientImpl { 28 | tcp_client: TcpClient::new(config.to_tcp_config()), 29 | config, 30 | on_open: None, 31 | on_open_failed: None, 32 | on_close: None, 33 | connection: crate::connection::DefaultConnection::new(), 34 | rpc: None, 35 | }), 36 | }); 37 | 38 | let this_weak = Rc::downgrade(&r); 39 | r.inner.borrow_mut().tcp_client.on_open(move || { 40 | let this = this_weak.upgrade().unwrap(); 41 | { 42 | let mut this = this.inner.borrow_mut(); 43 | if let Some(rpc) = this.config.rpc.clone() { 44 | this.connection = rpc.get_connection(); 45 | this.rpc = Some(rpc); 46 | } else { 47 | this.rpc = Some(Rpc::new(Some(this.connection.clone()))); 48 | } 49 | } 50 | 51 | { 52 | let this_weak = this_weak.clone(); 53 | this.inner 54 | .borrow() 55 | .connection 56 | .borrow_mut() 57 | .set_send_package_impl(Box::new(move |package: Vec| { 58 | if let Some(this) = this_weak.upgrade() { 59 | this.inner.borrow().tcp_client.send(package); 60 | } 61 | })); 62 | } 63 | { 64 | let this_weak = this_weak.clone(); 65 | this.inner.borrow().tcp_client.on_data(move |package| { 66 | if let Some(this) = this_weak.upgrade() { 67 | this.inner 68 | .borrow() 69 | .connection 70 | .borrow_mut() 71 | .on_recv_package(package); 72 | } 73 | }); 74 | } 75 | 76 | this.inner.borrow_mut().rpc.as_ref().unwrap().set_timer( 77 | |ms: u32, handle: Box| { 78 | tokio::task::spawn_local(async move { 79 | tokio::time::sleep(tokio::time::Duration::from_millis(ms as u64)).await; 80 | handle(); 81 | }); 82 | }, 83 | ); 84 | { 85 | let this_weak = this_weak.clone(); 86 | this.inner.borrow().tcp_client.on_close(move || { 87 | let this = this_weak.upgrade().unwrap(); 88 | this.inner.borrow().rpc.as_ref().unwrap().set_ready(false); 89 | { 90 | let inner = this.inner.borrow(); 91 | if let Some(on_close) = inner.on_close.as_ref() { 92 | on_close(); 93 | } 94 | } 95 | }); 96 | } 97 | this.inner 98 | .borrow_mut() 99 | .rpc 100 | .as_ref() 101 | .unwrap() 102 | .set_ready(true); 103 | 104 | { 105 | let this = this.inner.borrow(); 106 | if let Some(on_open) = &this.on_open { 107 | on_open(this.rpc.clone().unwrap()); 108 | } 109 | } 110 | }); 111 | 112 | let this_weak = Rc::downgrade(&r); 113 | r.inner.borrow_mut().tcp_client.on_open_failed(move |e| { 114 | let this = this_weak.upgrade().unwrap(); 115 | { 116 | let inner = this.inner.borrow(); 117 | if let Some(on_open_failed) = inner.on_open_failed.as_ref() { 118 | on_open_failed(e); 119 | } 120 | } 121 | }); 122 | 123 | r 124 | } 125 | 126 | pub fn open(&self, host: impl ToString, port: u16) { 127 | self.inner.borrow_mut().tcp_client.open(host, port); 128 | } 129 | 130 | pub fn close(&self) { 131 | self.inner.borrow().tcp_client.close(); 132 | } 133 | 134 | pub fn set_reconnect(&self, ms: u32) { 135 | self.inner.borrow_mut().tcp_client.set_reconnect(ms); 136 | } 137 | 138 | pub fn cancel_reconnect(&self) { 139 | self.inner.borrow_mut().tcp_client.cancel_reconnect(); 140 | } 141 | 142 | pub fn stop(&mut self) { 143 | self.close(); 144 | } 145 | 146 | pub fn on_open(&self, callback: F) 147 | where 148 | F: Fn(Rc) + 'static, 149 | { 150 | self.inner.borrow_mut().on_open = Some(Box::new(callback)); 151 | } 152 | 153 | pub fn on_open_failed(&self, callback: F) 154 | where 155 | F: Fn(&dyn Error) + 'static, 156 | { 157 | self.inner.borrow_mut().on_open_failed = Some(Box::new(callback)); 158 | } 159 | 160 | pub fn on_close(&self, callback: F) 161 | where 162 | F: Fn() + 'static, 163 | { 164 | self.inner.borrow_mut().on_close = Some(Box::new(callback)); 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /rust/src/net/rpc_server.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | use std::rc::{Rc, Weak}; 3 | 4 | use log::{debug, trace}; 5 | 6 | use crate::net::config::RpcConfig; 7 | use crate::net::detail::tcp_channel::TcpChannel; 8 | use crate::net::tcp_server::TcpServer; 9 | use crate::rpc::Rpc; 10 | 11 | pub struct RpcSession { 12 | pub rpc: RefCell>, 13 | on_close: RefCell>>, 14 | channel: Weak, 15 | } 16 | 17 | impl RpcSession { 18 | pub fn new(rpc: Rc, channel: Weak) -> Rc { 19 | Rc::new(Self { 20 | rpc: rpc.into(), 21 | on_close: None.into(), 22 | channel, 23 | }) 24 | } 25 | 26 | pub fn on_close(&self, callback: F) 27 | where 28 | F: Fn() + 'static, 29 | { 30 | *self.on_close.borrow_mut() = Some(Box::new(callback)); 31 | } 32 | } 33 | 34 | impl Drop for RpcSession { 35 | fn drop(&mut self) { 36 | trace!("~RpcSession"); 37 | } 38 | } 39 | 40 | pub struct RpcServer { 41 | config: Rc>, 42 | server: Rc, 43 | on_session: RefCell)>>>, 44 | this: RefCell>, 45 | } 46 | 47 | impl RpcServer { 48 | pub fn new(port: u16, config: RpcConfig) -> Rc { 49 | Rc::new_cyclic(|this_weak| { 50 | let tcp_config = config.to_tcp_config(); 51 | let config = Rc::new(RefCell::new(config)); 52 | 53 | let r = Self { 54 | config, 55 | server: TcpServer::new(port, tcp_config), 56 | on_session: None.into(), 57 | this: this_weak.clone().into(), 58 | }; 59 | 60 | let this_weak = this_weak.clone(); 61 | r.server.on_session(move |session| { 62 | let this = this_weak.upgrade().unwrap(); 63 | let tcp_channel = session.upgrade().unwrap(); 64 | let rpc = if let Some(rpc) = this.config.borrow().rpc.clone() { 65 | if rpc.is_ready() { 66 | debug!("rpc already connected"); 67 | tcp_channel.close(); 68 | return; 69 | } 70 | rpc 71 | } else { 72 | Rpc::new(None) 73 | }; 74 | 75 | let rpc_session = RpcSession::new(rpc.clone(), Rc::downgrade(&tcp_channel)); 76 | let rs_weak = Rc::downgrade(&rpc_session); 77 | 78 | { 79 | let rs_weak = rs_weak.clone(); 80 | rpc.get_connection() 81 | .borrow_mut() 82 | .set_send_package_impl(Box::new(move |package: Vec| { 83 | if let Some(rs) = rs_weak.upgrade() { 84 | rs.channel.upgrade().unwrap().send(package); 85 | } 86 | })); 87 | } 88 | { 89 | let rs_weak = rs_weak.clone(); 90 | tcp_channel.on_data(move |package| { 91 | if let Some(rs) = rs_weak.upgrade() { 92 | rs.rpc 93 | .borrow() 94 | .get_connection() 95 | .borrow() 96 | .on_recv_package(package); 97 | } 98 | }); 99 | } 100 | 101 | rpc.set_timer(|ms: u32, handle: Box| { 102 | tokio::task::spawn_local(async move { 103 | tokio::time::sleep(tokio::time::Duration::from_millis(ms as u64)).await; 104 | handle(); 105 | }); 106 | }); 107 | { 108 | // bind rpc_session lifecycle to tcp_session and end with on_close 109 | let rs = rpc_session.clone(); 110 | // let tc_weak = Rc::downgrade(&tcp_channel); 111 | tcp_channel.on_close(move || { 112 | rs.rpc.borrow_mut().set_ready(false); 113 | // *tc_weak.upgrade().unwrap().on_close.borrow_mut() = None; 114 | }); 115 | } 116 | rpc_session.rpc.borrow_mut().set_ready(true); 117 | 118 | { 119 | let on_session = this.on_session.borrow(); 120 | if let Some(on_session) = on_session.as_ref() { 121 | on_session(rs_weak); 122 | } 123 | } 124 | }); 125 | r 126 | }) 127 | } 128 | 129 | pub fn downgrade(&self) -> Weak { 130 | self.this.borrow().clone() 131 | } 132 | 133 | pub fn start(&self) { 134 | self.server.start(); 135 | } 136 | 137 | pub fn stop(&self) { 138 | self.server.stop(); 139 | } 140 | 141 | pub fn on_session(&self, callback: F) 142 | where 143 | F: Fn(Weak) + 'static, 144 | { 145 | *self.on_session.borrow_mut() = Some(Box::new(callback)); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /rust/src/net/tcp_client.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | use std::error::Error; 3 | use std::net::ToSocketAddrs; 4 | use std::rc::{Rc, Weak}; 5 | 6 | use log::debug; 7 | use tokio::net::TcpStream; 8 | 9 | use crate::net::config::TcpConfig; 10 | use crate::net::detail::tcp_channel::TcpChannel; 11 | 12 | pub struct TcpClient { 13 | host: RefCell, 14 | port: RefCell, 15 | config: Rc>, 16 | on_open: RefCell>>, 17 | on_open_failed: RefCell>>, 18 | on_close: RefCell>>, 19 | reconnect_ms: RefCell, 20 | reconnect_timer_running: RefCell, 21 | channel: Rc, 22 | this: RefCell>, 23 | } 24 | 25 | // public 26 | impl TcpClient { 27 | pub fn new(config: TcpConfig) -> Rc { 28 | Rc::::new_cyclic(|this_weak| { 29 | let config = Rc::new(RefCell::new(config)); 30 | let r = Self { 31 | host: "".to_string().into(), 32 | port: 0.into(), 33 | config: config.clone(), 34 | on_open: None.into(), 35 | on_open_failed: None.into(), 36 | on_close: None.into(), 37 | reconnect_ms: 0.into(), 38 | reconnect_timer_running: false.into(), 39 | channel: TcpChannel::new(config), 40 | this: this_weak.clone().into(), 41 | }; 42 | 43 | let this_weak = this_weak.clone(); 44 | r.channel.on_close(move || { 45 | let Some(this) = this_weak.upgrade() else { 46 | return; 47 | }; 48 | if let Some(on_close) = this.on_close.borrow().as_ref() { 49 | on_close(); 50 | } 51 | tokio::task::spawn_local(async move { 52 | this.check_reconnect().await; 53 | }); 54 | }); 55 | r 56 | }) 57 | } 58 | 59 | pub fn downgrade(&self) -> Weak { 60 | self.this.borrow().clone() 61 | } 62 | 63 | pub fn open(&self, host: impl ToString, port: u16) { 64 | *self.host.borrow_mut() = host.to_string(); 65 | *self.port.borrow_mut() = port; 66 | self.do_open(); 67 | } 68 | 69 | pub fn close(&self) { 70 | self.cancel_reconnect(); 71 | self.channel.close(); 72 | } 73 | 74 | pub fn set_reconnect(&self, ms: u32) { 75 | *self.reconnect_ms.borrow_mut() = ms; 76 | } 77 | 78 | pub fn cancel_reconnect(&self) { 79 | *self.reconnect_timer_running.borrow_mut() = false; 80 | } 81 | 82 | pub fn stop(&self) { 83 | self.close(); 84 | } 85 | 86 | pub fn on_open(&self, callback: F) 87 | where 88 | F: Fn() + 'static, 89 | { 90 | *self.on_open.borrow_mut() = Some(Box::new(callback)); 91 | } 92 | 93 | pub fn on_open_failed(&self, callback: F) 94 | where 95 | F: Fn(&dyn Error) + 'static, 96 | { 97 | *self.on_open_failed.borrow_mut() = Some(Box::new(callback)); 98 | } 99 | 100 | pub fn on_data(&self, callback: F) 101 | where 102 | F: Fn(Vec) + 'static, 103 | { 104 | self.channel.on_data(callback); 105 | } 106 | 107 | pub fn on_close(&self, callback: F) 108 | where 109 | F: Fn() + 'static, 110 | { 111 | *self.on_close.borrow_mut() = Some(Box::new(callback)); 112 | } 113 | 114 | pub fn send(&self, data: Vec) { 115 | self.channel.send(data); 116 | } 117 | 118 | pub fn send_str(&self, data: impl ToString) { 119 | self.channel.send_str(data); 120 | } 121 | } 122 | 123 | // private 124 | impl TcpClient { 125 | fn do_open(&self) { 126 | self.config.borrow_mut().init(); 127 | let host = self.host.borrow().clone(); 128 | let port = *self.port.borrow(); 129 | 130 | let this_weak = self.this.borrow().clone(); 131 | tokio::task::spawn_local(async move { 132 | let this = this_weak.upgrade().unwrap(); 133 | if this.channel.is_open() { 134 | this.channel.close(); 135 | this.channel.wait_close_finish().await; 136 | } 137 | debug!("connect_tcp: {host} {port}"); 138 | let result = TcpClient::connect_tcp(host, port).await; 139 | debug!("connect_tcp: {result:?}"); 140 | 141 | match result { 142 | Ok(stream) => { 143 | this.channel.do_open(stream); 144 | if let Some(on_open) = this.on_open.borrow_mut().as_ref() { 145 | on_open(); 146 | } 147 | } 148 | Err(err) => { 149 | if let Some(on_open_failed) = this.on_open_failed.borrow().as_ref() { 150 | on_open_failed(&*err); 151 | } 152 | this.check_reconnect().await; 153 | } 154 | }; 155 | }); 156 | } 157 | 158 | async fn connect_tcp( 159 | host: String, 160 | port: u16, 161 | ) -> Result> { 162 | let mut host = host; 163 | if host == "localhost" { 164 | host = "127.0.0.1".parse().unwrap(); 165 | } 166 | let addr = (host, port).to_socket_addrs()?.next().unwrap(); 167 | let stream = TcpStream::connect(addr).await?; 168 | Ok(stream) 169 | } 170 | 171 | async fn check_reconnect(&self) { 172 | if !self.channel.is_open() 173 | && !*self.reconnect_timer_running.borrow() 174 | && *self.reconnect_ms.borrow() > 0 175 | { 176 | *self.reconnect_timer_running.borrow_mut() = true; 177 | tokio::time::sleep(tokio::time::Duration::from_millis( 178 | (*self.reconnect_ms.borrow()).into(), 179 | )) 180 | .await; 181 | if *self.reconnect_timer_running.borrow() { 182 | *self.reconnect_timer_running.borrow_mut() = false; 183 | } else { 184 | return; 185 | } 186 | if !self.channel.is_open() { 187 | self.do_open(); 188 | } 189 | } 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /rust/src/net/tcp_server.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | use std::net::SocketAddr; 3 | use std::rc::{Rc, Weak}; 4 | 5 | use log::{debug, trace}; 6 | use tokio::net::TcpListener; 7 | use tokio::select; 8 | use tokio::sync::Notify; 9 | 10 | use crate::net::config::TcpConfig; 11 | use crate::net::detail::tcp_channel::TcpChannel; 12 | 13 | pub struct TcpServer { 14 | port: RefCell, 15 | config: Rc>, 16 | on_session: RefCell)>>>, 17 | quit_notify: Notify, 18 | this: RefCell>, 19 | } 20 | 21 | // public 22 | impl TcpServer { 23 | pub fn new(port: u16, config: TcpConfig) -> Rc { 24 | Rc::new_cyclic(|this_weak| Self { 25 | port: port.into(), 26 | config: Rc::new(RefCell::new(config)), 27 | on_session: None.into(), 28 | quit_notify: Notify::new(), 29 | this: this_weak.clone().into(), 30 | }) 31 | } 32 | 33 | pub fn downgrade(&self) -> Weak { 34 | self.this.borrow().clone() 35 | } 36 | 37 | pub fn start(&self) { 38 | self.config.borrow_mut().init(); 39 | let port = *self.port.borrow(); 40 | 41 | let this_weak = self.this.borrow().clone(); 42 | 43 | tokio::task::spawn_local(async move { 44 | debug!("listen: {port}"); 45 | let listener = TcpListener::bind(SocketAddr::new("0.0.0.0".parse().unwrap(), port)) 46 | .await 47 | .unwrap(); 48 | loop { 49 | let this = this_weak.upgrade().unwrap(); 50 | select! { 51 | res = listener.accept() => { 52 | match res { 53 | Ok((stream, addr))=> { 54 | debug!("accept addr: {addr}"); 55 | tokio::task::spawn_local(async move { 56 | let session = TcpChannel::new(this.config.clone()); 57 | if let Some(on_session) = this.on_session.borrow_mut().as_ref() { 58 | session.do_open(stream); 59 | on_session(Rc::downgrade(&session)); 60 | } 61 | }); 62 | }, 63 | Err(e) => { 64 | println!("Error accepting connection: {}", e); 65 | } 66 | } 67 | } 68 | _ = this.quit_notify.notified() => { 69 | trace!("server: stop"); 70 | break; 71 | } 72 | } 73 | } 74 | }); 75 | } 76 | 77 | pub fn stop(&self) { 78 | self.quit_notify.notify_one(); 79 | } 80 | 81 | pub fn on_session(&self, callback: F) 82 | where 83 | F: Fn(Weak) + 'static, 84 | { 85 | *self.on_session.borrow_mut() = Some(Box::new(callback)); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /rust/src/rpc.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | use std::rc::{Rc, Weak}; 3 | 4 | use log::debug; 5 | 6 | use crate::connection::{Connection, DefaultConnection}; 7 | use crate::detail::coder; 8 | use crate::detail::msg_dispatcher::{MsgDispatcher, TimeoutCb}; 9 | use crate::detail::msg_wrapper::{MsgType, MsgWrapper}; 10 | use crate::request::Request; 11 | use crate::type_def::SeqType; 12 | 13 | pub struct RpcImpl { 14 | weak: Weak, 15 | connection: Rc>, 16 | dispatcher: Rc>, 17 | seq: SeqType, 18 | is_ready: bool, 19 | } 20 | 21 | pub struct Rpc { 22 | inner: RefCell, 23 | } 24 | 25 | impl Rpc { 26 | #[allow(clippy::unwrap_or_default)] 27 | pub fn new(connection: Option>>) -> Rc { 28 | let connection = connection.unwrap_or(DefaultConnection::new()); 29 | let rpc = Rc::new(Rpc { 30 | inner: RefCell::new(RpcImpl { 31 | weak: Weak::new(), 32 | connection: connection.clone(), 33 | dispatcher: MsgDispatcher::new(connection), 34 | seq: 0, 35 | is_ready: false, 36 | }), 37 | }); 38 | rpc.inner.borrow_mut().weak = Rc::downgrade(&rpc); 39 | rpc 40 | } 41 | 42 | pub fn subscribe(&self, cmd: C, handle: F) 43 | where 44 | C: ToString, 45 | P: for<'de> serde::Deserialize<'de>, 46 | R: serde::Serialize, 47 | F: Fn(P) -> R + 'static, 48 | { 49 | self.inner.borrow().dispatcher.borrow_mut().subscribe_cmd( 50 | cmd.to_string(), 51 | Box::new(move |msg| -> Option { 52 | if let Ok(value) = msg.unpack_as::

() { 53 | let rsp: R = handle(value); 54 | Some(MsgWrapper::make_rsp(msg.seq, rsp)) 55 | } else { 56 | None 57 | } 58 | }), 59 | ); 60 | } 61 | 62 | pub fn unsubscribe(&self, cmd: C) 63 | where 64 | C: ToString, 65 | { 66 | self.inner 67 | .borrow() 68 | .dispatcher 69 | .borrow_mut() 70 | .unsubscribe_cmd(cmd.to_string()); 71 | } 72 | 73 | pub fn create_request(&self) -> Rc { 74 | Request::create_with_rpc(self.inner.borrow().weak.clone()) 75 | } 76 | 77 | pub fn cmd(&self, cmd: T) -> Rc 78 | where 79 | T: ToString, 80 | { 81 | let r = self.create_request(); 82 | r.cmd(cmd.to_string()); 83 | r 84 | } 85 | 86 | pub fn ping(&self) -> Rc { 87 | let r = self.create_request(); 88 | r.ping(); 89 | r 90 | } 91 | 92 | pub fn ping_msg(&self, payload: impl ToString) -> Rc { 93 | let r = self.create_request(); 94 | r.ping().msg(payload.to_string()); 95 | r 96 | } 97 | 98 | pub fn set_timer(&self, timer_impl: F) 99 | where 100 | F: Fn(u32, Box) + 'static, 101 | { 102 | self.inner 103 | .borrow() 104 | .dispatcher 105 | .borrow_mut() 106 | .set_timer_impl(timer_impl); 107 | } 108 | 109 | pub fn set_ready(&self, ready: bool) { 110 | self.inner.borrow_mut().is_ready = ready; 111 | } 112 | 113 | pub fn get_connection(&self) -> Rc> { 114 | self.inner.borrow().connection.clone() 115 | } 116 | } 117 | 118 | impl Rpc { 119 | pub fn make_seq(&self) -> SeqType { 120 | let mut inner = self.inner.borrow_mut(); 121 | let seq = inner.seq; 122 | inner.seq += 1; 123 | seq 124 | } 125 | 126 | pub fn send_request(&self, request: &Request) { 127 | let msg; 128 | let payload; 129 | let connection; 130 | { 131 | let inner = self.inner.borrow(); 132 | let request = request.inner.borrow(); 133 | if request.need_rsp { 134 | inner.dispatcher.borrow_mut().subscribe_rsp( 135 | request.seq, 136 | request.rsp_handle.as_ref().unwrap().clone(), 137 | request.timeout_cb.clone(), 138 | request.timeout_ms, 139 | ); 140 | } 141 | let mut type_ = MsgType::Command; 142 | if request.is_ping { 143 | type_ |= MsgType::Ping; 144 | } 145 | if request.need_rsp { 146 | type_ |= MsgType::NeedRsp; 147 | } 148 | msg = MsgWrapper { 149 | seq: request.seq, 150 | type_, 151 | cmd: request.cmd.clone(), 152 | data: request.payload.clone().unwrap_or_default(), 153 | request_payload: None, 154 | }; 155 | 156 | payload = coder::serialize(&msg); 157 | connection = inner.connection.clone(); 158 | } 159 | debug!( 160 | "=> seq:{} type:{} {}", 161 | msg.seq, 162 | if msg.type_.contains(MsgType::Ping) { 163 | "ping" 164 | } else { 165 | "cmd" 166 | }, 167 | msg.cmd 168 | ); 169 | connection.borrow().send_package(payload); 170 | } 171 | 172 | pub fn is_ready(&self) -> bool { 173 | self.inner.borrow().is_ready 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /rust/src/tests/net_rpc.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | 3 | use log::info; 4 | 5 | use rpc_core::net::config_builder::RpcConfigBuilder; 6 | use rpc_core::net::{rpc_client, rpc_server}; 7 | use rpc_core::rpc::Rpc; 8 | 9 | #[test] 10 | fn net_rpc() { 11 | std::env::set_var("RUST_LOG", "trace"); 12 | env_logger::init(); 13 | 14 | let thread_server = std::thread::spawn(|| { 15 | let runtime = tokio::runtime::Builder::new_current_thread() 16 | .enable_all() 17 | .build() 18 | .unwrap(); 19 | 20 | runtime.block_on(async { 21 | let local = tokio::task::LocalSet::new(); 22 | local 23 | .run_until(async move { 24 | let rpc = Rpc::new(None); 25 | rpc.subscribe("cmd", |msg: String| -> String { 26 | info!("cmd: {msg}"); 27 | assert_eq!(msg, "hello"); 28 | "world".to_string() 29 | }); 30 | 31 | let server = rpc_server::RpcServer::new( 32 | 6666, 33 | RpcConfigBuilder::new().rpc(Some(rpc.clone())).build(), 34 | ); 35 | server.on_session(move |session| { 36 | info!("on_open"); 37 | let session = session.upgrade().unwrap(); 38 | session.on_close(|| { 39 | info!("on_close"); 40 | }); 41 | }); 42 | 43 | info!("server: start..."); 44 | server.start(); 45 | 46 | tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; 47 | }) 48 | .await; 49 | }); 50 | }); 51 | 52 | let thread_client = std::thread::spawn(|| { 53 | let runtime = tokio::runtime::Builder::new_current_thread() 54 | .enable_all() 55 | .build() 56 | .unwrap(); 57 | 58 | runtime.block_on(async { 59 | let local = tokio::task::LocalSet::new(); 60 | local 61 | .run_until(async move { 62 | let rpc = Rpc::new(None); 63 | let client = rpc_client::RpcClient::new( 64 | RpcConfigBuilder::new().rpc(Some(rpc.clone())).build(), 65 | ); 66 | client.on_open(|_: Rc| { 67 | info!("on_open"); 68 | }); 69 | client.on_open_failed(|e| { 70 | info!("on_open_failed: {:?}", e); 71 | }); 72 | client.on_close(|| { 73 | info!("on_close"); 74 | }); 75 | client.set_reconnect(1000); 76 | 77 | // wait server ready 78 | tokio::time::sleep(tokio::time::Duration::from_millis(200)).await; 79 | client.open("localhost", 6666); 80 | info!("client: start..."); 81 | 82 | tokio::time::sleep(tokio::time::Duration::from_millis(200)).await; 83 | 84 | info!("usage: callback..."); 85 | rpc.cmd("cmd") 86 | .msg("hello") 87 | .rsp(|msg: String| { 88 | info!("### rsp: {msg}"); 89 | assert_eq!(msg, "world"); 90 | }) 91 | .call(); 92 | 93 | info!("usage: future..."); 94 | let result = rpc.cmd("cmd").msg("hello").future::().await; 95 | info!("### rsp: {result:?}"); 96 | assert_eq!(result.result.unwrap(), "world"); 97 | }) 98 | .await; 99 | }); 100 | }); 101 | 102 | thread_client.join().expect("thread_client: panic"); 103 | thread_server.join().expect("thread_server: panic"); 104 | } 105 | -------------------------------------------------------------------------------- /rust/src/tests/net_tcp.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | 3 | use log::info; 4 | 5 | use rpc_core::net::config_builder::TcpConfigBuilder; 6 | use rpc_core::net::{tcp_client, tcp_server}; 7 | 8 | #[test] 9 | fn net_tcp() { 10 | std::env::set_var("RUST_LOG", "trace"); 11 | env_logger::init(); 12 | 13 | let thread_server = std::thread::spawn(|| { 14 | let runtime = tokio::runtime::Builder::new_current_thread() 15 | .enable_all() 16 | .build() 17 | .unwrap(); 18 | 19 | runtime.block_on(async { 20 | let local = tokio::task::LocalSet::new(); 21 | local 22 | .run_until(async move { 23 | let server = tcp_server::TcpServer::new( 24 | 6666, 25 | TcpConfigBuilder::new().auto_pack(true).build(), 26 | ); 27 | server.on_session(move |session| { 28 | info!("session: on_open"); 29 | let session = session.upgrade().unwrap(); 30 | let sw = Rc::downgrade(&session); 31 | session.on_data(move |data| { 32 | let msg = String::from_utf8_lossy(data.as_slice()); 33 | info!("session: on_data: {}", msg); 34 | assert_eq!(msg, "hello"); 35 | sw.upgrade().unwrap().send_str("world"); 36 | }); 37 | session.on_close(|| { 38 | info!("session: on_close"); 39 | }); 40 | }); 41 | info!("server: start..."); 42 | server.start(); 43 | tokio::time::sleep(tokio::time::Duration::from_millis(200)).await; 44 | }) 45 | .await; 46 | }); 47 | }); 48 | 49 | let thread_client = std::thread::spawn(|| { 50 | let runtime = tokio::runtime::Builder::new_current_thread() 51 | .enable_all() 52 | .build() 53 | .unwrap(); 54 | 55 | runtime.block_on(async { 56 | let local = tokio::task::LocalSet::new(); 57 | local 58 | .run_until(async move { 59 | let client = 60 | tcp_client::TcpClient::new(TcpConfigBuilder::new().auto_pack(true).build()); 61 | let client_weak = client.downgrade(); 62 | client.on_open(move || { 63 | info!("client: on_open"); 64 | client_weak.upgrade().unwrap().send_str("hello"); 65 | }); 66 | client.on_open_failed(|e| { 67 | info!("client: on_open_failed: {:?}", e); 68 | }); 69 | client.on_data(|data| { 70 | let msg = String::from_utf8_lossy(data.as_slice()); 71 | info!("client: on_data: {}", msg); 72 | assert_eq!(msg, "world"); 73 | }); 74 | client.on_close(|| { 75 | info!("client: on_close"); 76 | }); 77 | client.set_reconnect(1000); 78 | client.open("localhost", 6666); 79 | info!("client: start..."); 80 | tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; 81 | }) 82 | .await; 83 | }); 84 | }); 85 | 86 | thread_client.join().expect("thread_client: panic"); 87 | thread_server.join().expect("thread_server: panic"); 88 | } 89 | -------------------------------------------------------------------------------- /rust/src/tests/rpc.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | use std::rc::Rc; 3 | 4 | use log::info; 5 | 6 | use rpc_core::request::FinallyType; 7 | 8 | #[test] 9 | fn rpc() { 10 | std::env::set_var("RUST_LOG", "trace"); 11 | env_logger::init(); 12 | 13 | // loopback connection 14 | let (connection_s, connection_c) = rpc_core::connection::LoopbackConnection::new(); 15 | 16 | // rpc server 17 | let rpc_s = rpc_core::rpc::Rpc::new(Some(connection_s)); 18 | rpc_s.set_timer(|ms: u32, _: Box| { 19 | info!("set_timer: {ms}"); 20 | }); 21 | rpc_s.set_ready(true); 22 | 23 | rpc_s.subscribe("cmd", |msg: String| -> String { 24 | assert_eq!(msg, "hello"); 25 | "world".to_string() 26 | }); 27 | 28 | // rpc client 29 | let rpc_c = rpc_core::rpc::Rpc::new(Some(connection_c)); 30 | rpc_c.set_timer(|ms: u32, _: Box| { 31 | info!("set_timer: {ms}"); 32 | }); 33 | rpc_c.set_ready(true); 34 | 35 | // test code 36 | let pass = Rc::new(RefCell::new(false)); 37 | let pass_clone = pass.clone(); 38 | rpc_c 39 | .cmd("cmd") 40 | .msg("hello") 41 | .rsp(move |msg: String| { 42 | assert_eq!(msg, "world"); 43 | *pass_clone.borrow_mut() = true; 44 | }) 45 | .call(); 46 | assert!(*pass.borrow()); 47 | 48 | info!("--- test unsubscribe ---"); 49 | rpc_s.subscribe("x", |_: ()| {}); 50 | rpc_s.unsubscribe("x"); 51 | 52 | info!("--- test ping ---"); 53 | *pass.borrow_mut() = false; 54 | let pass_clone = pass.clone(); 55 | rpc_c.ping(); 56 | rpc_c 57 | .ping_msg("hello") 58 | .rsp(move |msg: String| { 59 | info!("rsp: {}", msg); 60 | *pass_clone.borrow_mut() = true; 61 | }) 62 | .call(); 63 | assert!(*pass.borrow()); 64 | 65 | info!("--- test request ---"); 66 | { 67 | let request = rpc_core::request::Request::new(); 68 | let pass = Rc::new(RefCell::new(false)); 69 | let pass_clone = pass.clone(); 70 | request 71 | .cmd("cmd") 72 | .msg("hello") 73 | .rsp(move |msg: String| { 74 | assert_eq!(msg, "world"); 75 | *pass_clone.borrow_mut() = true; 76 | }) 77 | .call_with_rpc(rpc_c.clone()); 78 | assert!(*pass.borrow()); 79 | } 80 | 81 | info!("--- test dispose ---"); 82 | { 83 | info!("--- dispose test RAII ---"); 84 | rpc_s.subscribe("cmd", |_: String| -> String { 85 | assert!(false); 86 | "".to_string() 87 | }); 88 | 89 | let pass = Rc::new(RefCell::new(false)); 90 | let pass_clone = pass.clone(); 91 | let request = rpc_c.cmd("cmd"); 92 | request 93 | .msg("hello") 94 | .rsp(|_: String| { 95 | assert!(false); 96 | }) 97 | .finally(move |t| { 98 | assert_eq!(t, FinallyType::Canceled); 99 | *pass_clone.borrow_mut() = true; 100 | }); 101 | { 102 | let mut dispose = rpc_core::dispose::Dispose::new(); 103 | request.add_to(&mut dispose); 104 | } 105 | request.call(); 106 | assert!(*pass.borrow()); 107 | } 108 | { 109 | info!("--- dispose test remove ---"); 110 | rpc_s.subscribe("cmd", |_: String| -> String { "".to_string() }); 111 | 112 | let pass = Rc::new(RefCell::new(false)); 113 | let pass_clone = pass.clone(); 114 | let request = rpc_c.cmd("cmd"); 115 | request 116 | .msg("hello") 117 | .rsp(|_: String| { 118 | assert!(true); 119 | }) 120 | .finally(move |t| { 121 | assert_eq!(t, FinallyType::Normal); 122 | *pass_clone.borrow_mut() = true; 123 | }); 124 | { 125 | let mut dispose = rpc_core::dispose::Dispose::new(); 126 | request.add_to(&mut dispose); 127 | dispose.remove(&request); 128 | } 129 | request.call(); 130 | assert!(*pass.borrow()); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /rust/src/type_def.rs: -------------------------------------------------------------------------------- 1 | pub type SeqType = u32; 2 | pub type CmdType = String; 3 | -------------------------------------------------------------------------------- /test/assert_def.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | // clang-format off 7 | 8 | #define __ASSERT(e, file, line) \ 9 | ((void)printf ("%s:%d: failed assertion `%s'\n", file, line, e), abort()) 10 | 11 | #define ASSERT(e) \ 12 | ((void) ((e) ? ((void)0) : __ASSERT (#e, __FILE__, __LINE__))) 13 | -------------------------------------------------------------------------------- /test/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "rpc_core.hpp" 4 | #include "rpc_core/detail/log.h" 5 | #include "test.h" 6 | 7 | using namespace rpc_core_test; 8 | 9 | int main() { 10 | RPC_CORE_LOG("version: %d", RPC_CORE_VERSION); 11 | 12 | printf("\n"); 13 | RPC_CORE_LOG("test_serialize..."); 14 | test_serialize(); 15 | 16 | printf("\n"); 17 | RPC_CORE_LOG("test_data_packer..."); 18 | test_data_packer(); 19 | 20 | #ifdef RPC_CORE_TEST_PLUGIN 21 | printf("\n"); 22 | RPC_CORE_LOG("test_plugin..."); 23 | test_plugin(); 24 | #endif 25 | 26 | printf("\n"); 27 | RPC_CORE_LOG("test_rpc..."); 28 | test_rpc(); 29 | 30 | return 0; 31 | } 32 | -------------------------------------------------------------------------------- /test/plugin/JsonType.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "nlohmann/json.hpp" 4 | #include "rpc_core/plugin/json_msg.hpp" 5 | 6 | struct JsonType { 7 | int id = 0; 8 | std::string name; 9 | uint8_t age = 0; 10 | }; 11 | RPC_CORE_DEFINE_TYPE_JSON(JsonType, id, name, age); 12 | -------------------------------------------------------------------------------- /test/plugin/RawType.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "rpc_core/serialize.hpp" 4 | 5 | struct RawType { 6 | uint8_t id = 0; 7 | std::string name; 8 | uint8_t age = 0; 9 | }; 10 | RPC_CORE_DEFINE_TYPE(RawType, id, name, age); 11 | -------------------------------------------------------------------------------- /test/plugin/fb/.clang-format: -------------------------------------------------------------------------------- 1 | DisableFormat: true 2 | -------------------------------------------------------------------------------- /test/plugin/fb/FbMsg.fbs: -------------------------------------------------------------------------------- 1 | namespace msg; 2 | 3 | table FbMsg { 4 | id: int; 5 | age: uint8; 6 | name: string; 7 | } 8 | 9 | root_type FbMsg; 10 | -------------------------------------------------------------------------------- /test/plugin/fb/FbMsg_generated.h: -------------------------------------------------------------------------------- 1 | // automatically generated by the FlatBuffers compiler, do not modify 2 | 3 | 4 | #ifndef FLATBUFFERS_GENERATED_FBMSG_MSG_H_ 5 | #define FLATBUFFERS_GENERATED_FBMSG_MSG_H_ 6 | 7 | #include "flatbuffers/flatbuffers.h" 8 | 9 | // Ensure the included flatbuffers.h is the same version as when this file was 10 | // generated, otherwise it may not be compatible. 11 | static_assert(FLATBUFFERS_VERSION_MAJOR == 23 && 12 | FLATBUFFERS_VERSION_MINOR == 1 && 13 | FLATBUFFERS_VERSION_REVISION == 21, 14 | "Non-compatible flatbuffers version included"); 15 | 16 | namespace msg { 17 | 18 | struct FbMsg; 19 | struct FbMsgBuilder; 20 | struct FbMsgT; 21 | 22 | struct FbMsgT : public ::flatbuffers::NativeTable { 23 | typedef FbMsg TableType; 24 | int32_t id = 0; 25 | uint8_t age = 0; 26 | std::string name{}; 27 | }; 28 | 29 | struct FbMsg FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { 30 | typedef FbMsgT NativeTableType; 31 | typedef FbMsgBuilder Builder; 32 | enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { 33 | VT_ID = 4, 34 | VT_AGE = 6, 35 | VT_NAME = 8 36 | }; 37 | int32_t id() const { 38 | return GetField(VT_ID, 0); 39 | } 40 | uint8_t age() const { 41 | return GetField(VT_AGE, 0); 42 | } 43 | const ::flatbuffers::String *name() const { 44 | return GetPointer(VT_NAME); 45 | } 46 | bool Verify(::flatbuffers::Verifier &verifier) const { 47 | return VerifyTableStart(verifier) && 48 | VerifyField(verifier, VT_ID, 4) && 49 | VerifyField(verifier, VT_AGE, 1) && 50 | VerifyOffset(verifier, VT_NAME) && 51 | verifier.VerifyString(name()) && 52 | verifier.EndTable(); 53 | } 54 | FbMsgT *UnPack(const ::flatbuffers::resolver_function_t *_resolver = nullptr) const; 55 | void UnPackTo(FbMsgT *_o, const ::flatbuffers::resolver_function_t *_resolver = nullptr) const; 56 | static ::flatbuffers::Offset Pack(::flatbuffers::FlatBufferBuilder &_fbb, const FbMsgT* _o, const ::flatbuffers::rehasher_function_t *_rehasher = nullptr); 57 | }; 58 | 59 | struct FbMsgBuilder { 60 | typedef FbMsg Table; 61 | ::flatbuffers::FlatBufferBuilder &fbb_; 62 | ::flatbuffers::uoffset_t start_; 63 | void add_id(int32_t id) { 64 | fbb_.AddElement(FbMsg::VT_ID, id, 0); 65 | } 66 | void add_age(uint8_t age) { 67 | fbb_.AddElement(FbMsg::VT_AGE, age, 0); 68 | } 69 | void add_name(::flatbuffers::Offset<::flatbuffers::String> name) { 70 | fbb_.AddOffset(FbMsg::VT_NAME, name); 71 | } 72 | explicit FbMsgBuilder(::flatbuffers::FlatBufferBuilder &_fbb) 73 | : fbb_(_fbb) { 74 | start_ = fbb_.StartTable(); 75 | } 76 | ::flatbuffers::Offset Finish() { 77 | const auto end = fbb_.EndTable(start_); 78 | auto o = ::flatbuffers::Offset(end); 79 | return o; 80 | } 81 | }; 82 | 83 | inline ::flatbuffers::Offset CreateFbMsg( 84 | ::flatbuffers::FlatBufferBuilder &_fbb, 85 | int32_t id = 0, 86 | uint8_t age = 0, 87 | ::flatbuffers::Offset<::flatbuffers::String> name = 0) { 88 | FbMsgBuilder builder_(_fbb); 89 | builder_.add_name(name); 90 | builder_.add_id(id); 91 | builder_.add_age(age); 92 | return builder_.Finish(); 93 | } 94 | 95 | inline ::flatbuffers::Offset CreateFbMsgDirect( 96 | ::flatbuffers::FlatBufferBuilder &_fbb, 97 | int32_t id = 0, 98 | uint8_t age = 0, 99 | const char *name = nullptr) { 100 | auto name__ = name ? _fbb.CreateString(name) : 0; 101 | return msg::CreateFbMsg( 102 | _fbb, 103 | id, 104 | age, 105 | name__); 106 | } 107 | 108 | ::flatbuffers::Offset CreateFbMsg(::flatbuffers::FlatBufferBuilder &_fbb, const FbMsgT *_o, const ::flatbuffers::rehasher_function_t *_rehasher = nullptr); 109 | 110 | inline FbMsgT *FbMsg::UnPack(const ::flatbuffers::resolver_function_t *_resolver) const { 111 | auto _o = std::unique_ptr(new FbMsgT()); 112 | UnPackTo(_o.get(), _resolver); 113 | return _o.release(); 114 | } 115 | 116 | inline void FbMsg::UnPackTo(FbMsgT *_o, const ::flatbuffers::resolver_function_t *_resolver) const { 117 | (void)_o; 118 | (void)_resolver; 119 | { auto _e = id(); _o->id = _e; } 120 | { auto _e = age(); _o->age = _e; } 121 | { auto _e = name(); if (_e) _o->name = _e->str(); } 122 | } 123 | 124 | inline ::flatbuffers::Offset FbMsg::Pack(::flatbuffers::FlatBufferBuilder &_fbb, const FbMsgT* _o, const ::flatbuffers::rehasher_function_t *_rehasher) { 125 | return CreateFbMsg(_fbb, _o, _rehasher); 126 | } 127 | 128 | inline ::flatbuffers::Offset CreateFbMsg(::flatbuffers::FlatBufferBuilder &_fbb, const FbMsgT *_o, const ::flatbuffers::rehasher_function_t *_rehasher) { 129 | (void)_rehasher; 130 | (void)_o; 131 | struct _VectorArgs { ::flatbuffers::FlatBufferBuilder *__fbb; const FbMsgT* __o; const ::flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va; 132 | auto _id = _o->id; 133 | auto _age = _o->age; 134 | auto _name = _o->name.empty() ? 0 : _fbb.CreateString(_o->name); 135 | return msg::CreateFbMsg( 136 | _fbb, 137 | _id, 138 | _age, 139 | _name); 140 | } 141 | 142 | inline const msg::FbMsg *GetFbMsg(const void *buf) { 143 | return ::flatbuffers::GetRoot(buf); 144 | } 145 | 146 | inline const msg::FbMsg *GetSizePrefixedFbMsg(const void *buf) { 147 | return ::flatbuffers::GetSizePrefixedRoot(buf); 148 | } 149 | 150 | inline bool VerifyFbMsgBuffer( 151 | ::flatbuffers::Verifier &verifier) { 152 | return verifier.VerifyBuffer(nullptr); 153 | } 154 | 155 | inline bool VerifySizePrefixedFbMsgBuffer( 156 | ::flatbuffers::Verifier &verifier) { 157 | return verifier.VerifySizePrefixedBuffer(nullptr); 158 | } 159 | 160 | inline void FinishFbMsgBuffer( 161 | ::flatbuffers::FlatBufferBuilder &fbb, 162 | ::flatbuffers::Offset root) { 163 | fbb.Finish(root); 164 | } 165 | 166 | inline void FinishSizePrefixedFbMsgBuffer( 167 | ::flatbuffers::FlatBufferBuilder &fbb, 168 | ::flatbuffers::Offset root) { 169 | fbb.FinishSizePrefixed(root); 170 | } 171 | 172 | inline std::unique_ptr UnPackFbMsg( 173 | const void *buf, 174 | const ::flatbuffers::resolver_function_t *res = nullptr) { 175 | return std::unique_ptr(GetFbMsg(buf)->UnPack(res)); 176 | } 177 | 178 | inline std::unique_ptr UnPackSizePrefixedFbMsg( 179 | const void *buf, 180 | const ::flatbuffers::resolver_function_t *res = nullptr) { 181 | return std::unique_ptr(GetSizePrefixedFbMsg(buf)->UnPack(res)); 182 | } 183 | 184 | } // namespace msg 185 | 186 | #endif // FLATBUFFERS_GENERATED_FBMSG_MSG_H_ 187 | -------------------------------------------------------------------------------- /test/serialize/CustomType.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "rpc_core/serialize.hpp" 4 | 5 | struct CustomType { 6 | uint32_t id = 0; 7 | std::vector ids; 8 | std::string name; 9 | bool operator==(const CustomType& t) const { 10 | return std::tie(id, ids, name) == std::tie(t.id, t.ids, t.name); 11 | } 12 | bool operator<(const CustomType& t) const { 13 | return std::tie(id, ids, name) < std::tie(t.id, t.ids, t.name); 14 | } 15 | }; 16 | RPC_CORE_DEFINE_TYPE(CustomType, id, ids, name); 17 | 18 | // 指针 19 | struct CustomTypePtr { 20 | int32_t* int_n; 21 | int32_t* int_v; 22 | std::unique_ptr unique_ptr_n; 23 | std::unique_ptr unique_ptr_v; 24 | std::shared_ptr shared_ptr_n; 25 | std::shared_ptr shared_ptr_v; 26 | }; 27 | RPC_CORE_DEFINE_TYPE(CustomTypePtr, int_n, int_v, unique_ptr_n, unique_ptr_v, shared_ptr_n, shared_ptr_v); 28 | 29 | #pragma pack(push, 1) 30 | struct CustomType2 { 31 | uint8_t id1{}; 32 | uint8_t id2{}; 33 | uint32_t id3{}; 34 | }; 35 | #pragma pack(pop) 36 | RPC_CORE_DEFINE_TYPE(CustomType2, id1, id2, id3); 37 | 38 | #pragma pack(push, 4) 39 | struct CustomType3 { 40 | uint8_t id1{}; 41 | uint8_t id2{}; 42 | uint32_t id3{}; 43 | }; 44 | #pragma pack(pop) 45 | RPC_CORE_DEFINE_TYPE(CustomType3, id1, id2, id3); 46 | 47 | namespace test { 48 | // 嵌套定义 49 | struct CustomTypeNest { 50 | CustomType2 c2{}; 51 | CustomType3 c3{}; 52 | CustomTypeNest* ptr{}; 53 | }; 54 | RPC_CORE_DEFINE_TYPE(test::CustomTypeNest, c2, c3, ptr); 55 | 56 | // 内部定义 57 | struct CustomTypeNest2 { 58 | // can be private 59 | CustomType2 c2{}; 60 | CustomType3 c3{}; 61 | CustomTypeNest* ptr{}; 62 | RPC_CORE_DEFINE_TYPE_INNER(c2, c3, ptr); 63 | }; 64 | 65 | } // namespace test 66 | -------------------------------------------------------------------------------- /test/serialize/TestStruct.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "rpc_core/serialize.hpp" 4 | 5 | struct TestStruct { 6 | uint8_t a; 7 | uint16_t b; 8 | uint32_t c; 9 | }; 10 | RPC_CORE_DEFINE_TYPE(TestStruct, a, b, c); 11 | -------------------------------------------------------------------------------- /test/test.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace rpc_core_test { 4 | 5 | void test_serialize(); 6 | 7 | void test_data_packer(); 8 | 9 | void test_rpc(); 10 | 11 | void test_plugin(); 12 | 13 | } // namespace rpc_core_test 14 | -------------------------------------------------------------------------------- /test/test_data_packer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "assert_def.h" 5 | #include "rpc_core.hpp" 6 | #include "test.h" 7 | 8 | static void test_simple() { 9 | RPC_CORE_LOGI(); 10 | RPC_CORE_LOGI("test_simple..."); 11 | rpc_core::detail::data_packer packer; 12 | std::string testData = "hello world"; 13 | std::string packedData; 14 | 15 | packer.pack(testData.data(), testData.size(), [&](const void *data, size_t size) { 16 | packedData.insert(packedData.size(), (char *)data, size); 17 | return true; 18 | }); 19 | ASSERT(packedData.size() == testData.size() + 4); 20 | 21 | std::string feedRecData; 22 | packer.on_data = [&](std::string data) { 23 | feedRecData = std::move(data); 24 | }; 25 | packer.feed(packedData.data(), packedData.size()); 26 | ASSERT(testData == feedRecData); 27 | RPC_CORE_LOGI("packedData PASS"); 28 | 29 | std::string packedData2 = packer.pack(testData); 30 | ASSERT(packedData2 == packedData); 31 | RPC_CORE_LOGI("packedData2 PASS"); 32 | 33 | RPC_CORE_LOGI("feed again..."); 34 | feedRecData.clear(); 35 | ASSERT(testData != feedRecData); 36 | packer.feed(packedData.data(), packedData.size()); 37 | ASSERT(testData == feedRecData); 38 | } 39 | 40 | static void test_random() { 41 | RPC_CORE_LOGI(); 42 | RPC_CORE_LOGI("test_random..."); 43 | RPC_CORE_LOGI("generate big data..."); 44 | bool pass = false; 45 | std::string TEST_PAYLOAD; 46 | size_t TestAddCount = 1000; 47 | for (size_t i = 0; i < TestAddCount; i++) { 48 | TEST_PAYLOAD += "helloworld"; // 10bytes 49 | } 50 | RPC_CORE_LOGI("data generated, size:%zu", TEST_PAYLOAD.size()); 51 | ASSERT(TEST_PAYLOAD.size() == TestAddCount * 10); 52 | 53 | rpc_core::detail::data_packer packer; 54 | packer.on_data = [&](const std::string &data) { 55 | size_t size = data.size(); 56 | RPC_CORE_LOGI("get payload size:%zu", size); 57 | if (data == TEST_PAYLOAD) { 58 | pass = true; 59 | } 60 | }; 61 | 62 | RPC_CORE_LOGI("packing..."); 63 | auto payload = packer.pack(TEST_PAYLOAD); 64 | const size_t payloadSize = payload.size(); 65 | RPC_CORE_LOGI("payloadSize:%zu", payloadSize); 66 | ASSERT(payloadSize == TestAddCount * 10 + 4); 67 | 68 | RPC_CORE_LOGI("******test normal******"); 69 | packer.feed(payload.data(), payloadSize); 70 | ASSERT(pass); 71 | pass = false; 72 | 73 | RPC_CORE_LOGI("******test random******"); 74 | size_t sendLeft = payloadSize; 75 | std::default_random_engine generator((unsigned)std::chrono::system_clock::now().time_since_epoch().count()); 76 | std::uniform_int_distribution dis(1, 10); 77 | auto random = std::bind(dis, generator); // NOLINT 78 | while (sendLeft > 0) { 79 | size_t randomSize = random(); 80 | // RPC_CORE_LOGI("random: %u, %u", randomSize, sendLeft); 81 | size_t needSend = std::min(randomSize, sendLeft); 82 | packer.feed(payload.data() + (payloadSize - sendLeft), needSend); 83 | sendLeft -= needSend; 84 | } 85 | ASSERT(pass); 86 | } 87 | 88 | namespace rpc_core_test { 89 | 90 | void test_data_packer() { 91 | test_simple(); 92 | test_random(); 93 | } 94 | 95 | } // namespace rpc_core_test 96 | -------------------------------------------------------------------------------- /test/test_plugin.cpp: -------------------------------------------------------------------------------- 1 | #include "assert_def.h" 2 | #include "plugin/JsonType.h" 3 | #include "plugin/RawType.h" 4 | #include "plugin/fb/FbMsg_generated.h" 5 | #include "rpc_core/plugin/flatbuffers.hpp" 6 | #include "rpc_core/plugin/json.hpp" 7 | #include "test.h" 8 | 9 | namespace rpc_core_test { 10 | 11 | void test_plugin() { 12 | using namespace rpc_core; 13 | { 14 | RPC_CORE_LOGI("RawType..."); 15 | RawType a; 16 | a.id = 1; 17 | a.name = "test"; 18 | a.age = 18; 19 | 20 | auto payload = serialize(a); 21 | // payload is not readable 22 | RPC_CORE_LOGI("RawType: size: %zu", payload.size()); 23 | 24 | RawType b; 25 | deserialize(payload, b); 26 | ASSERT(a.id == b.id); 27 | ASSERT(a.name == b.name); 28 | ASSERT(a.age == b.age); 29 | } 30 | 31 | { 32 | RPC_CORE_LOGI("json..."); 33 | nlohmann::json a; 34 | a["id"] = 1; 35 | a["name"] = "test"; 36 | a["age"] = 18; 37 | 38 | auto payload = serialize(a); 39 | RPC_CORE_LOGI("json: %s", payload.c_str()); 40 | RPC_CORE_LOGI("json: size: %zu", payload.size()); 41 | 42 | nlohmann::json b; 43 | deserialize(payload, b); 44 | ASSERT(b["id"] == a["id"]); 45 | ASSERT(b["name"] == a["name"]); 46 | ASSERT(b["age"] == a["age"]); 47 | } 48 | 49 | { 50 | RPC_CORE_LOGI("JsonType..."); 51 | JsonType a; 52 | a.id = 1; 53 | a.name = "test"; 54 | a.age = 18; 55 | 56 | auto payload = serialize(a); 57 | RPC_CORE_LOGI("JsonType: %s", payload.c_str()); 58 | RPC_CORE_LOGI("JsonType: size: %zu", payload.size()); 59 | 60 | JsonType b; 61 | deserialize(payload, b); 62 | ASSERT(b.id == a.id); 63 | ASSERT(b.name == a.name); 64 | ASSERT(b.age == a.age); 65 | } 66 | 67 | { 68 | RPC_CORE_LOGI("flatbuffers..."); 69 | msg::FbMsgT a; 70 | a.id = 1; 71 | a.name = "test"; 72 | a.age = 18; 73 | 74 | auto payload = serialize(a); 75 | // flatbuffers payload is not readable 76 | RPC_CORE_LOGI("flatbuffers: size: %zu", payload.size()); 77 | 78 | msg::FbMsgT b; 79 | deserialize(payload, b); 80 | ASSERT(b.id == a.id); 81 | ASSERT(b.name == a.name); 82 | ASSERT(b.age == a.age); 83 | } 84 | } 85 | 86 | } // namespace rpc_core_test 87 | --------------------------------------------------------------------------------