├── .gitignore ├── .gitmodules ├── An easy to use RPC framework implemented by c++11 and c++14.pptx ├── CMakeLists.txt ├── README.md ├── bench.png ├── code_check.png ├── example ├── async_client_example │ ├── CMakeLists.txt │ └── async_client_example.cpp ├── bench_client │ ├── CMakeLists.txt │ └── bench_client.cpp ├── bench_server │ ├── CMakeLists.txt │ └── bench_server.cpp ├── codec_policy │ └── codec_policy.cpp ├── key_value_client │ └── key_value_client.cpp ├── key_value_server │ └── key_value_server.cpp ├── pub_sub_example │ ├── CMakeLists.txt │ ├── pub_client_example.cpp │ └── sub_client_example.cpp ├── server_example │ ├── CMakeLists.txt │ └── server_example.cpp ├── sync_client_example │ ├── CMakeLists.txt │ ├── client.cfg │ └── sync_client_example.cpp ├── test_timeout_and_switch_endoint │ └── test_timeout_and_switch_endpoint.cpp └── timax_bind │ ├── CMakeLists.txt │ └── timax_bind.cpp ├── msvc ├── async_client_example │ ├── async_client_example.vcxproj │ └── async_client_example.vcxproj.filters ├── bench_client │ ├── bench_client.vcxproj │ └── bench_client.vcxproj.filters ├── bench_server │ ├── bench_server.vcxproj │ └── bench_server.vcxproj.filters ├── codec_policy │ ├── codec_policy.vcxproj │ └── codec_policy.vcxproj.filters ├── key_value_server │ ├── key_value_server.vcxproj │ └── key_value_server.vcxproj.filters ├── kv_store_client │ ├── kv_store_client.vcxproj │ └── kv_store_client.vcxproj.filters ├── pub_client_example │ ├── pub_client_example.vcxproj │ └── pub_client_example.vcxproj.filters ├── rest_examples.props ├── rest_rpc.sln ├── rest_rpc │ ├── ReadMe.txt │ ├── rest_rpc.vcxproj │ └── rest_rpc.vcxproj.filters ├── server_example │ ├── server.cfg │ ├── server_example.vcxproj │ ├── server_example.vcxproj.filters │ └── tempfile ├── sub_client_example │ ├── sub_client_example.vcxproj │ └── sub_client_example.vcxproj.filters ├── sync_client_example │ ├── client.cfg │ ├── sync_client_example.vcxproj │ └── sync_client_example.vcxproj.filters ├── test_timeout_and_switch_endpoint │ ├── test_timeout_and_switch_endpoint.vcxproj │ └── test_timeout_and_switch_endpoint.vcxproj.filters └── timax_bind │ ├── timax_bind.vcxproj │ └── timax_bind.vcxproj.filters └── rest_rpc ├── base ├── codec.hpp ├── common.h ├── consts.h ├── excetion.hpp ├── function_traits.hpp ├── hash.hpp ├── io_service_pool.hpp ├── log.hpp └── utils.hpp ├── client.hpp ├── client ├── async_client.hpp ├── detail │ ├── async_client_private.hpp │ ├── async_connection.hpp │ ├── async_rpc_channel.hpp │ ├── async_rpc_channel_impl.hpp │ ├── async_rpc_context.hpp │ ├── async_sub_channel.hpp │ └── wait_barrier.hpp ├── protocol.hpp └── sync_client.hpp ├── codec ├── codec.hpp ├── codec_boost.hpp ├── codec_json.hpp ├── codec_msgpack.hpp └── codec_xml.hpp ├── forward.hpp ├── rpc.hpp ├── server.hpp ├── server ├── connection.hpp ├── connection_impl.hpp ├── context.hpp ├── invoker_traits.hpp ├── ios_wrapper.hpp ├── ios_wrapper_impl.hpp ├── router.hpp └── server.hpp └── test ├── test_router.hpp └── unit_test.hpp /.gitignore: -------------------------------------------------------------------------------- 1 | *.suo 2 | .vs 3 | *.VC.db 4 | *.VC.opendb 5 | *.sdf 6 | *.lg.txt 7 | x64 8 | Debug 9 | Release 10 | *.user 11 | /build 12 | /client_proxy/build -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "spdlog"] 2 | path = spdlog 3 | url = https://github.com/gabime/spdlog.git 4 | [submodule "iguana"] 5 | path = iguana 6 | url = https://github.com/qicosmos/iguana.git 7 | -------------------------------------------------------------------------------- /An easy to use RPC framework implemented by c++11 and c++14.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/purecpp-org/rest_rpc_old-out-of-date-now-/3f562a1388ca83c1832132ed2fb66d9a70f57142/An easy to use RPC framework implemented by c++11 and c++14.pptx -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | project(rest_rpc) 4 | 5 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/") 6 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread -std=c++14") 7 | 8 | if (CMAKE_BUILD_TYPE STREQUAL Debug) 9 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_DEBUG") 10 | #add_definitions( 11 | # -D_DEBUG 12 | #) 13 | endif () 14 | 15 | add_definitions(-DFMT_HEADER_ONLY) 16 | 17 | find_package(Boost COMPONENTS coroutine context system thread chrono REQUIRED) 18 | 19 | set(REST_RPC_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) 20 | 21 | include_directories( 22 | ${Boost_INCLUDE_DIRS} 23 | ${REST_RPC_DIRECTORY} 24 | ${REST_RPC_DIRECTORY}/iguana 25 | ${REST_RPC_DIRECTORY}/iguana/third_party/msgpack/include 26 | ${REST_RPC_DIRECTORY}/spdlog/include 27 | ${REST_RPC_DIRECTORY}/msgpack/include 28 | ) 29 | SET(EXTRA_LIBS ${EXTRA_LIBS} ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) 30 | 31 | 32 | set(ASYNC_CLIENT_SOURCE 33 | ${REST_RPC_DIRECTORY}/example/async_client_example/async_client_example.cpp 34 | ) 35 | set(BENCH_CLIENT_SOURCE 36 | ${REST_RPC_DIRECTORY}/example/bench_client/bench_client.cpp 37 | ) 38 | set(BENCH_SERVER_SOURCE 39 | ${REST_RPC_DIRECTORY}/example/bench_server/bench_server.cpp 40 | ) 41 | set(KEY_VALUE_CLIENT_SOURCE 42 | ${REST_RPC_DIRECTORY}/example/key_value_client/key_value_client.cpp 43 | ) 44 | set(KEY_VALUE_SERVER_SOURCE 45 | ${REST_RPC_DIRECTORY}/example/key_value_server/key_value_server.cpp 46 | ) 47 | set(SERVER_EXAMPLE_SOURCE 48 | ${REST_RPC_DIRECTORY}/example/server_example/server_example.cpp 49 | ) 50 | set(TIMAX_BIND_SOURCE 51 | ${REST_RPC_DIRECTORY}/example/timax_bind/timax_bind.cpp 52 | ) 53 | 54 | 55 | add_executable(async_client ${ASYNC_CLIENT_SOURCE}) 56 | target_link_libraries(async_client ${EXTRA_LIBS}) 57 | 58 | add_executable(bench_client ${BENCH_CLIENT_SOURCE}) 59 | target_link_libraries(bench_client ${EXTRA_LIBS}) 60 | 61 | add_executable(bench_server ${BENCH_SERVER_SOURCE}) 62 | target_link_libraries(bench_server ${EXTRA_LIBS}) 63 | 64 | add_executable(key_value_client ${KEY_VALUE_CLIENT_SOURCE}) 65 | target_link_libraries(key_value_client ${EXTRA_LIBS}) 66 | 67 | add_executable(key_value_server ${KEY_VALUE_SERVER_SOURCE}) 68 | target_link_libraries(key_value_server ${EXTRA_LIBS}) 69 | 70 | add_executable(server_example ${SERVER_EXAMPLE_SOURCE}) 71 | target_link_libraries(server_example ${EXTRA_LIBS}) 72 | 73 | add_executable(tiamx_bind ${TIMAX_BIND_SOURCE}) 74 | target_link_libraries(tiamx_bind ${EXTRA_LIBS}) 75 | 76 | include (InstallRequiredSystemLibraries) 77 | set (CPACK_PACKAGE_VERSION_MAJOR "1") 78 | set (CPACK_PACKAGE_VERSION_MINOR "0") 79 | SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "David Doria") 80 | include (CPack) 81 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [english wiki](https://github.com/topcpporg/rest_rpc/wiki/English) 2 | 在此特别感谢社区的朋友刘丹将rest_rpc的中文说明翻译为英文。 3 | 4 | #[chinese wiki](https://github.com/topcpporg/rest_rpc/wiki/Chinese) 5 | 6 | ##Contributer 7 | 8 | [江南(qicosmos)](https://github.com/qicosmos),[IndignantAngel](https://github.com/IndignantAngel) 9 | 10 | ##Contact us 11 | qicosmos@163.com 12 | 13 | -------------------------- 14 | 15 | ## rest_rpc v0.91 release note 16 | #### 新增特性 17 | 1.业务函数的参数可以有connection_ptr,也可以没有,取决于你的需要,使用更灵活。 18 | ```cpp 19 | server.register_handler("add_with_conn", [] 20 | (timax::rpc::connection_ptr conn, int a, int b) 21 | { 22 | auto result = a + b; 23 | if (result < 1) 24 | conn->close(); 25 | return result; 26 | }); 27 | ``` 28 | 2.客户端添加private接口,拥有更高的权限和更多的流程控制 29 | 30 | 3.server端的pub提供了一个纯转发的重载实现 31 | 32 | 4.提供管理多个endpoint的工具 33 | ```cpp 34 | auto endpoints = timax::rpc::get_tcp_endpoints("127.0.0.1:5001|127.0.0.1:5002"); 35 | for(auto const& endpoint : endpoints) 36 | { 37 | std::cout << endpoint << std::endl; 38 | } 39 | ``` 40 | 5.客户端pub接口的,将会把转发协议的name当做topic,广播给所有监听这个topic的客户端,而不需要再服务器上注册handler; 41 | 42 | 6.服务器注册handler,将使用hash值代替字符串 43 | 44 | #### Bug修复 45 | 1. rpc超时后异步调用链断开 46 | 2. 客户端和服务器read大块消息时,因使用boost::bind,而发生了意外地拷贝,招致读取到不正确的地址 47 | 3. 支持更低版本的编译器 48 | -------------------------------------------------------------------------------- /bench.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/purecpp-org/rest_rpc_old-out-of-date-now-/3f562a1388ca83c1832132ed2fb66d9a70f57142/bench.png -------------------------------------------------------------------------------- /code_check.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/purecpp-org/rest_rpc_old-out-of-date-now-/3f562a1388ca83c1832132ed2fb66d9a70f57142/code_check.png -------------------------------------------------------------------------------- /example/async_client_example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | project(async_client) 4 | 5 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/") 6 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread -std=c++14") 7 | 8 | if (CMAKE_BUILD_TYPE STREQUAL Debug) 9 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_DEBUG") 10 | #add_definitions( 11 | # -D_DEBUG 12 | #) 13 | endif () 14 | 15 | add_definitions(-DFMT_HEADER_ONLY) 16 | 17 | find_package(Boost COMPONENTS coroutine context system thread chrono REQUIRED) 18 | 19 | set(REST_RPC_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../../) 20 | 21 | include_directories( 22 | ${Boost_INCLUDE_DIRS} 23 | ${REST_RPC_DIRECTORY} 24 | ${REST_RPC_DIRECTORY}/iguana 25 | ${REST_RPC_DIRECTORY}/iguana/third_party/msgpack/include 26 | ${REST_RPC_DIRECTORY}/spdlog/include 27 | ${REST_RPC_DIRECTORY}/msgpack/include 28 | ) 29 | SET(EXTRA_LIBS ${EXTRA_LIBS} ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) 30 | 31 | 32 | set(SOURCE_FILES 33 | async_client_example.cpp 34 | ) 35 | 36 | 37 | add_executable(async_client ${SOURCE_FILES}) 38 | target_link_libraries(async_client ${EXTRA_LIBS}) 39 | 40 | include (InstallRequiredSystemLibraries) 41 | set (CPACK_PACKAGE_VERSION_MAJOR "1") 42 | set (CPACK_PACKAGE_VERSION_MINOR "0") 43 | SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "David Doria") 44 | include (CPack) 45 | -------------------------------------------------------------------------------- /example/async_client_example/async_client_example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace client 4 | { 5 | TIMAX_DEFINE_PROTOCOL(add, int(int, int)); 6 | TIMAX_DEFINE_PROTOCOL(compose, void(int, const std::string&, timax::rpc::blob_t, double)); 7 | TIMAX_DEFINE_FORWARD(sub_add, int); 8 | TIMAX_DEFINE_PROTOCOL(sub_not_exist, double(int, std::string const&)); 9 | } 10 | 11 | 12 | using tcp = boost::asio::ip::tcp; 13 | using async_client_t = timax::rpc::async_client; 14 | 15 | // create the client 16 | async_client_t asycn_client; 17 | 18 | void async_client_rpc_example(tcp::endpoint const& endpoint) 19 | { 20 | using namespace std::chrono_literals; 21 | 22 | // the interface is type safe and non-connect oriented designed 23 | asycn_client.call(endpoint, client::add, 1.0, 200.0f); 24 | 25 | // we can set some callbacks to process some specific eventsS 26 | asycn_client.call(endpoint, client::add, 1, 2).on_ok([](auto r) 27 | { 28 | std::cout << r << std::endl; 29 | }).on_error([](auto const& error) 30 | { 31 | std::cout << error.get_error_message() << std::endl; 32 | }).timeout(1min); 33 | 34 | // we can also use the asynchronized client in a synchronized way 35 | try 36 | { 37 | auto task = asycn_client.call(endpoint, client::add, 3, 5); 38 | auto const& result = task.get(); 39 | std::cout << result << std::endl; 40 | } 41 | catch (timax::rpc::exception const& e) 42 | { 43 | std::cout << e.get_error_message() << std::endl; 44 | } 45 | 46 | auto backup_endpoint = timax::rpc::get_tcp_endpoint("127.0.0.1", 8999); 47 | asycn_client.call(endpoint, client::add, 1, 2).on_error([&backup_endpoint](auto const& error) 48 | { 49 | if (error.get_error_code() == timax::rpc::error_code::BADCONNECTION) 50 | asycn_client.call(backup_endpoint, client::add, 1, 2); 51 | }); 52 | } 53 | 54 | void async_client_sub_example(tcp::endpoint const& endpoint) 55 | { 56 | // we can use the sub interface to keep track of some topics we are interested in 57 | asycn_client.sub(endpoint, client::sub_add, [](auto r) 58 | { 59 | std::cout << r << std::endl; 60 | }, // interface of dealing with error is also supplied; 61 | [](auto const& error) 62 | { 63 | std::cout << error.get_error_message() << std::endl; 64 | }); 65 | } 66 | 67 | void async_compose_example(tcp::endpoint const& endpoint) 68 | { 69 | timax::rpc::blob_t p = { "it is a test", 13 }; 70 | auto task = asycn_client.call(endpoint, client::compose, 1, "test", p, 2.5); 71 | 72 | task.cancel(); 73 | 74 | try 75 | { 76 | task.wait(); 77 | } 78 | catch (timax::rpc::exception const& exception) 79 | { 80 | std::cout << exception.get_error_message() << std::endl; 81 | } 82 | } 83 | 84 | void test_timeout(tcp::endpoint const& endpoint) 85 | { 86 | using namespace std::chrono_literals; 87 | for (auto loop = 0; loop < 10000; ++loop) 88 | { 89 | asycn_client.call(endpoint, client::add, 1, loop). 90 | on_error([loop](auto const& error) 91 | { 92 | std::cout << "Error: " << error.get_error_message() << " loop" << loop << std::endl; 93 | }).on_ok([loop](auto r) 94 | { 95 | std::cout << "OK loop" << loop << std::endl; 96 | }); 97 | std::this_thread::sleep_for(10s); 98 | } 99 | } 100 | 101 | int main() 102 | { 103 | timax::log::get().init("async_client_example.lg"); 104 | 105 | auto endpoint = timax::rpc::get_tcp_endpoint("127.0.0.1", 9000); 106 | 107 | async_client_rpc_example(endpoint); 108 | async_client_sub_example(endpoint); 109 | async_compose_example(endpoint); 110 | test_timeout(endpoint); 111 | std::getchar(); 112 | return 0; 113 | } -------------------------------------------------------------------------------- /example/bench_client/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | project(bench_client) 4 | 5 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/") 6 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread -std=c++14") 7 | 8 | if (CMAKE_BUILD_TYPE STREQUAL Debug) 9 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_DEBUG") 10 | #add_definitions( 11 | # -D_DEBUG 12 | #) 13 | endif () 14 | 15 | add_definitions(-DFMT_HEADER_ONLY) 16 | 17 | find_package(Boost 1.55 COMPONENTS coroutine context system thread chrono serialization REQUIRED) 18 | 19 | set(REST_RPC_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../../) 20 | 21 | include_directories( 22 | ${Boost_INCLUDE_DIRS} 23 | ${REST_RPC_DIRECTORY} 24 | ${REST_RPC_DIRECTORY}/iguana 25 | ${REST_RPC_DIRECTORY}/iguana/third_party/msgpack/include 26 | ${REST_RPC_DIRECTORY}/spdlog/include 27 | ${REST_RPC_DIRECTORY}/msgpack/include 28 | ) 29 | SET(EXTRA_LIBS ${EXTRA_LIBS} ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) 30 | 31 | 32 | set(SOURCE_FILES 33 | bench_client.cpp 34 | ) 35 | 36 | 37 | add_executable(bench_client ${SOURCE_FILES}) 38 | target_link_libraries(bench_client ${EXTRA_LIBS}) 39 | 40 | include (InstallRequiredSystemLibraries) 41 | set (CPACK_PACKAGE_VERSION_MAJOR "1") 42 | set (CPACK_PACKAGE_VERSION_MINOR "0") 43 | SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "David Doria") 44 | include (CPack) 45 | -------------------------------------------------------------------------------- /example/bench_client/bench_client.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace bench 4 | { 5 | struct configure 6 | { 7 | std::string hostname; 8 | std::string port; 9 | }; 10 | REFLECTION(configure, hostname, port); 11 | 12 | configure get_config() 13 | { 14 | std::ifstream in("client.cfg"); 15 | std::stringstream ss; 16 | ss << in.rdbuf(); 17 | 18 | configure cfg = { "127.0.0.1", "9000" }; 19 | try 20 | { 21 | auto file_content = ss.str(); 22 | iguana::json::from_json(cfg, file_content.data(), file_content.size()); 23 | } 24 | catch (const std::exception& e) 25 | { 26 | timax::SPD_LOG_ERROR(e.what()); 27 | } 28 | 29 | return cfg; 30 | } 31 | 32 | TIMAX_DEFINE_PROTOCOL(add, int(int, int)); 33 | TIMAX_DEFINE_PROTOCOL(bench_connection, void(void)); 34 | 35 | std::atomic count{ 0 }; 36 | 37 | void bench_async(boost::asio::ip::tcp::endpoint const& endpoint) 38 | { 39 | using client_t = timax::rpc::async_client; 40 | 41 | auto client = std::make_shared(); 42 | 43 | std::thread{ [] 44 | { 45 | while (true) 46 | { 47 | using namespace std::chrono_literals; 48 | std::this_thread::sleep_for(1s); 49 | std::cout << count.load() << std::endl; 50 | count.store(0); 51 | } 52 | } }.detach(); 53 | 54 | 55 | std::thread{ 56 | [client, &endpoint] 57 | { 58 | int a = 0, b = 0; 59 | while (true) 60 | { 61 | client->call(endpoint, bench::add, a, b++).on_ok([](auto) 62 | { 63 | ++count; 64 | }); 65 | } 66 | 67 | } }.detach(); 68 | } 69 | 70 | void bench_sync(boost::asio::ip::tcp::endpoint const& endpoint) 71 | { 72 | using client_t = timax::rpc::sync_client; 73 | 74 | std::thread{ [] 75 | { 76 | while (true) 77 | { 78 | using namespace std::chrono_literals; 79 | std::this_thread::sleep_for(1s); 80 | std::cout << count.load() << std::endl; 81 | count.store(0); 82 | } 83 | } }.detach(); 84 | 85 | std::thread{ 86 | [endpoint] 87 | { 88 | client_t client; 89 | 90 | int a = 0, b = 0; 91 | while (true) 92 | { 93 | try 94 | { 95 | client.call(endpoint, bench::add, a, b++); 96 | ++count; 97 | } 98 | catch (...) 99 | { 100 | std::cout << "Exception: " << std::endl; 101 | break; 102 | } 103 | } 104 | 105 | } }.detach(); 106 | } 107 | 108 | void bench_conn(boost::asio::ip::tcp::endpoint const& endpoint, int connection_count) 109 | { 110 | using namespace std::chrono_literals; 111 | using client_private_t = timax::rpc::async_client_private; 112 | 113 | auto pool = std::make_shared(std::thread::hardware_concurrency()); 114 | pool->start(); 115 | 116 | std::thread 117 | { 118 | [pool, &endpoint, connection_count] 119 | { 120 | std::list client; 121 | auto const push_in_one_turn = 2048; 122 | auto already_pushed = 0; 123 | auto push_left = connection_count; 124 | while (true) 125 | { 126 | while (already_pushed < push_in_one_turn && push_left > 0) 127 | { 128 | client.emplace_back(pool->get_io_service()); 129 | auto ctx = client.back().make_rpc_context(endpoint, bench_connection); 130 | client.back().call(ctx); 131 | ++already_pushed; 132 | --push_left; 133 | } 134 | already_pushed = 0; 135 | std::this_thread::sleep_for(20ms); 136 | } 137 | } 138 | 139 | }.detach(); 140 | 141 | } 142 | } 143 | 144 | int main(int argc, char *argv[]) 145 | { 146 | // first of all, initialize log module 147 | timax::log::get().init("rest_rpc_client.lg"); 148 | 149 | auto config = bench::get_config(); 150 | auto endpoint = timax::rpc::get_tcp_endpoint( 151 | config.hostname, boost::lexical_cast(config.port)); 152 | int connection_count; 153 | 154 | enum class client_style_t 155 | { 156 | UNKNOWN, 157 | SYCN, 158 | ASYCN, 159 | CONN, 160 | }; 161 | 162 | std::string client_style; 163 | client_style_t style = client_style_t::UNKNOWN; 164 | 165 | if (2 > argc) 166 | { 167 | std::cout << "Usage: " << "$ ./bench_client %s(sync, async or conn)" << std::endl; 168 | return -1; 169 | } 170 | else 171 | { 172 | client_style = argv[1]; 173 | if ("sync" == client_style) 174 | { 175 | style = client_style_t::SYCN; 176 | } 177 | else if ("async" == client_style) 178 | { 179 | style = client_style_t::ASYCN; 180 | } 181 | else if ("conn" == client_style) 182 | { 183 | style = client_style_t::CONN; 184 | } 185 | if (client_style_t::UNKNOWN == style) 186 | { 187 | std::cout << "Usage: " << "$ ./bench_client %s(sync, async or conn)" << std::endl; 188 | return -1; 189 | } 190 | } 191 | 192 | switch (style) 193 | { 194 | case client_style_t::SYCN: 195 | bench::bench_sync(endpoint); 196 | break; 197 | case client_style_t::ASYCN: 198 | bench::bench_async(endpoint); 199 | break; 200 | case client_style_t::CONN: 201 | if (3 != argc) 202 | { 203 | std::cout << "Usage: " << "$ ./bench_client conn %d(connection count)" << std::endl; 204 | return -1; 205 | } 206 | connection_count = boost::lexical_cast(argv[2]); 207 | bench::bench_conn(endpoint, connection_count); 208 | break; 209 | default: 210 | return -1; 211 | } 212 | 213 | std::getchar(); 214 | return 0; 215 | } 216 | -------------------------------------------------------------------------------- /example/bench_server/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | project(bench_server) 4 | 5 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/") 6 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread -std=c++14") 7 | 8 | if (CMAKE_BUILD_TYPE STREQUAL Debug) 9 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_DEBUG") 10 | #add_definitions( 11 | # -D_DEBUG 12 | #) 13 | endif () 14 | 15 | add_definitions(-DFMT_HEADER_ONLY) 16 | 17 | find_package(Boost 1.55 COMPONENTS coroutine context system thread chrono serialization REQUIRED) 18 | 19 | set(REST_RPC_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../../) 20 | 21 | include_directories( 22 | ${Boost_INCLUDE_DIRS} 23 | ${REST_RPC_DIRECTORY} 24 | ${REST_RPC_DIRECTORY}/iguana 25 | ${REST_RPC_DIRECTORY}/iguana/third_party/msgpack/include 26 | ${REST_RPC_DIRECTORY}/spdlog/include 27 | ${REST_RPC_DIRECTORY}/msgpack/include 28 | ) 29 | SET(EXTRA_LIBS ${EXTRA_LIBS} ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) 30 | 31 | 32 | set(SOURCE_FILES 33 | bench_server.cpp 34 | ) 35 | 36 | 37 | add_executable(bench_server ${SOURCE_FILES}) 38 | target_link_libraries(bench_server ${EXTRA_LIBS}) 39 | 40 | include (InstallRequiredSystemLibraries) 41 | set (CPACK_PACKAGE_VERSION_MAJOR "1") 42 | set (CPACK_PACKAGE_VERSION_MINOR "0") 43 | SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "David Doria") 44 | include (CPack) 45 | -------------------------------------------------------------------------------- /example/bench_server/bench_server.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace bench 4 | { 5 | int add(int a, int b) 6 | { 7 | return a + b; 8 | } 9 | 10 | void some_task_takes_a_lot_of_time(double, int) 11 | { 12 | using namespace std::chrono_literals; 13 | std::this_thread::sleep_for(5s); 14 | } 15 | 16 | enum class type_t 17 | { 18 | connection, 19 | operation, 20 | }; 21 | 22 | template 23 | void count_qps(Server& server, std::atomic& qps) 24 | { 25 | server.register_handler("add", bench::add, 26 | [&qps](auto, auto) 27 | { 28 | ++qps; 29 | }); 30 | 31 | server.register_handler("sub_add", bench::add, [&server, &qps](auto, auto r) 32 | { 33 | ++qps; 34 | server.pub("sub_add", r, [&qps] 35 | { 36 | ++qps; 37 | }); 38 | }); 39 | 40 | std::thread{ [&qps] 41 | { 42 | while (true) 43 | { 44 | using namespace std::chrono_literals; 45 | 46 | std::cout << "QPS: " << qps.load() << ".\n"; 47 | qps.store(0); 48 | std::this_thread::sleep_for(1s); 49 | } 50 | 51 | } }.detach(); 52 | } 53 | 54 | template 55 | void count_connection(Server& server, std::atomic& conn_count) 56 | { 57 | server.register_handler("bench_connection", [&conn_count]() 58 | { 59 | ++conn_count; 60 | }); 61 | 62 | std::thread{ [&conn_count] 63 | { 64 | while (true) 65 | { 66 | using namespace std::chrono_literals; 67 | 68 | std::cout << "Connection: " << conn_count.load() << ".\n"; 69 | std::this_thread::sleep_for(1s); 70 | } 71 | 72 | } }.detach(); 73 | } 74 | } 75 | 76 | int main(int argc, char *argv[]) 77 | { 78 | using namespace std::chrono_literals; 79 | using server_t = timax::rpc::server; 80 | 81 | if (3 != argc) 82 | { 83 | std::cout << "Usage: " << "$ ./bench_server %d(0 or 1) %d(your port number)" << std::endl; 84 | return -1; 85 | } 86 | 87 | timax::log::get().init("bench_server.lg"); 88 | 89 | std::atomic work_count{ 0 }; 90 | auto bench_type = static_cast(boost::lexical_cast(argv[1])); 91 | auto port = boost::lexical_cast(argv[2]); 92 | auto pool_size = static_cast(std::thread::hardware_concurrency()); 93 | 94 | server_t server{ port, pool_size }; 95 | 96 | switch (bench_type) 97 | { 98 | case bench::type_t::connection: 99 | bench::count_connection(server, work_count); 100 | break; 101 | case bench::type_t::operation: 102 | bench::count_qps(server, work_count); 103 | break; 104 | } 105 | 106 | server.start(); 107 | std::getchar(); 108 | server.stop(); 109 | return 0; 110 | } 111 | -------------------------------------------------------------------------------- /example/codec_policy/codec_policy.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | struct foo_t 4 | { 5 | int a; 6 | double b; 7 | std::string c; 8 | 9 | using meta_tag = void; 10 | 11 | //template 12 | //void msgpack_pack(Packer& pk) const 13 | //{ 14 | // msgpack::type::make_define_array(a, b, c).msgpack_pack(pk); 15 | //} 16 | // 17 | //void (msgpack::object const& o) 18 | //{ 19 | // msgpack::type::make_define_array(a, b, c).msgpack_unpack(o); 20 | //} 21 | // 22 | //template 23 | //void msgpack_object(MSGPACK_OBJECT* o, msgpack::zone& z) const 24 | //{ 25 | // msgpack::type::make_define_array(a, b, c).msgpack_object(o, z); 26 | //} 27 | 28 | //MSGPACK_DEFINE(a, b, c); 29 | }; 30 | REFLECTION(foo_t, a, b, c); 31 | 32 | void meta_with_msgpack() 33 | { 34 | foo_t foo = { 1, 2.0, "ryuga waga teki wo kuraou!" }; 35 | msgpack::sbuffer buffer; 36 | msgpack::pack(buffer, foo); 37 | 38 | msgpack::unpacked msg; 39 | msgpack::unpack(&msg, buffer.data(), buffer.size()); 40 | auto foo1 = msg.get().as(); 41 | } 42 | 43 | void meta_with_boost() 44 | { 45 | foo_t foo = { 1, 2.0, "ryuga waga teki wo kuraou!" }; 46 | std::ostringstream os; 47 | boost::archive::text_oarchive oa(os); 48 | //oa << foo; 49 | } 50 | 51 | // msgpack adapt META macro 52 | int main() 53 | { 54 | meta_with_msgpack(); 55 | meta_with_boost(); 56 | return 0; 57 | } 58 | -------------------------------------------------------------------------------- /example/key_value_client/key_value_client.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | namespace kv 5 | { 6 | using namespace std::string_literals; 7 | 8 | TIMAX_DEFINE_PROTOCOL(get, std::vector(std::string const&)); 9 | TIMAX_DEFINE_PROTOCOL(put, bool(std::string const&, std::vector const&)); 10 | 11 | enum class operation_t 12 | { 13 | put, 14 | get, 15 | unknown, 16 | }; 17 | 18 | operation_t get_operation(char const* operation) 19 | { 20 | if ("put"s == operation) 21 | return operation_t::put; 22 | else if("get"s == operation) 23 | return operation_t::get; 24 | else 25 | return operation_t::unknown; 26 | } 27 | 28 | using client_t = timax::rpc::sync_client; 29 | 30 | int put_operation(int argc, char* argv[]) 31 | { 32 | if (5 != argc) 33 | { 34 | std::cout << "Args not match!" << std::endl; 35 | return -1; 36 | } 37 | 38 | auto const operation = argv[2]; 39 | std::string key(argv[3]); 40 | std::vector value; 41 | if ("string"s == operation) 42 | { 43 | auto len = std::strlen(argv[4]); 44 | if (0 == len) 45 | { 46 | std::cout << "Cannot send a null value." << std::endl; 47 | return -1; 48 | } 49 | value.resize(len + 1); 50 | std::copy(argv[4], argv[4] + value.size(), value.data()); 51 | } 52 | else if ("file"s == operation) 53 | { 54 | std::fstream file; 55 | file.open(argv[4], std::ios::in | std::ios::binary); 56 | if (!file) 57 | { 58 | std::cout << "File not exists!" << std::endl; 59 | return -1; 60 | } 61 | file.seekg(std::ios::end); 62 | auto size = file.tellg(); 63 | if (size > 102400) 64 | { 65 | std::cout << "Too big for rpc to send." << std::endl; 66 | return -1; 67 | } 68 | if (size <= 0) 69 | { 70 | std::cout << "Cannot send a null value." << std::endl; 71 | return -1; 72 | } 73 | file.seekg(std::ios::beg); 74 | value.resize(size); 75 | file.read(value.data(), value.size()); 76 | } 77 | else 78 | { 79 | std::cout << "Unknown operation!" << std::endl; 80 | return -1; 81 | } 82 | 83 | 84 | client_t client; 85 | auto endpoint = timax::rpc::get_tcp_endpoint("127.0.0.1", 9000); 86 | 87 | try 88 | { 89 | auto result = client.call(endpoint, put, key, value); 90 | if (!result) 91 | { 92 | std::cout << "Failed to store object." << std::endl; 93 | return -1; 94 | } 95 | 96 | } 97 | catch (timax::rpc::exception const& exception) 98 | { 99 | std::cout << "Failed to store object, exception:" << exception.get_error_message() << std::endl; 100 | return -1; 101 | } 102 | 103 | return 0; 104 | } 105 | 106 | int get_operation(int argc, char* argv[]) 107 | { 108 | if (4 > argc) 109 | return -1; 110 | 111 | auto const operation = argv[2]; 112 | std::string key = argv[3]; 113 | 114 | client_t client; 115 | auto endpoint = timax::rpc::get_tcp_endpoint("127.0.0.1", 9000); 116 | 117 | if ("string"s == operation) 118 | { 119 | auto buffer = client.call(endpoint, get, key); 120 | std::cout << buffer.data() << std::endl; 121 | } 122 | else if ("file"s == operation) 123 | { 124 | if (argc != 5) 125 | { 126 | std::cout << "Args not match." << std::endl; 127 | return -1; 128 | } 129 | 130 | auto buffer = client.call(endpoint, get, key); 131 | auto filepath = argv[4]; 132 | 133 | std::fstream file; 134 | file.open(filepath, std::ios::out | std::ios::binary); 135 | file.write(buffer.data(), buffer.size()); 136 | file.close(); 137 | } 138 | else 139 | { 140 | std::cout << "Unknown operation!" << std::endl; 141 | return -1; 142 | } 143 | 144 | return 0; 145 | } 146 | } 147 | 148 | int main(int argc, char* argv[]) 149 | { 150 | if (2 > argc) 151 | return -1; 152 | 153 | auto operation = kv::get_operation(argv[1]); 154 | if (kv::operation_t::unknown == operation) 155 | return -1; 156 | 157 | switch (operation) 158 | { 159 | case kv::operation_t::put: 160 | return kv::put_operation(argc, argv); 161 | case kv::operation_t::get: 162 | return kv::get_operation(argc, argv); 163 | default: 164 | std::cout << "Unknown operation!" << std::endl; 165 | return -1; 166 | } 167 | 168 | 169 | return 0; 170 | } -------------------------------------------------------------------------------- /example/key_value_server/key_value_server.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | class kv_store 4 | { 5 | public: 6 | using server_t = timax::rpc::server; 7 | using kv_container_t = std::map>; 8 | using lock_t = std::unique_lock; 9 | 10 | public: 11 | explicit kv_store() 12 | : server_(9000, std::thread::hardware_concurrency()) 13 | { 14 | 15 | } 16 | 17 | void start() 18 | { 19 | server_.register_handler("put", timax::bind(&kv_store::put, this)); 20 | server_.register_handler("get", timax::bind(&kv_store::get, this)); 21 | 22 | server_.start(); 23 | } 24 | 25 | void stop() 26 | { 27 | server_.stop(); 28 | } 29 | 30 | bool put(std::string&& key, std::vector&& value) 31 | { 32 | lock_t lock{ mutex_ }; 33 | auto itr = storage_.find(key); 34 | if (itr == storage_.end()) 35 | { 36 | storage_.emplace(std::move(key), std::move(value)); 37 | return true; 38 | } 39 | itr->second = std::move(value); 40 | return false; 41 | } 42 | 43 | std::vector get(std::string const& key) 44 | { 45 | lock_t lock{ mutex_ }; 46 | auto itr = storage_.find(key); 47 | if (itr == storage_.end()) 48 | return{}; 49 | std::vector to_return = std::move(itr->second); 50 | storage_.erase(itr); 51 | return to_return; 52 | } 53 | 54 | void remove(std::string const& key) 55 | { 56 | lock_t lock{ mutex_ }; 57 | auto itr = storage_.find(key); 58 | if (itr != storage_.end()) 59 | storage_.erase(itr); 60 | } 61 | 62 | private: 63 | server_t server_; 64 | kv_container_t storage_; 65 | std::mutex mutex_; 66 | }; 67 | 68 | int main(void) 69 | { 70 | kv_store kv_store_service; 71 | 72 | kv_store_service.start(); 73 | std::getchar(); 74 | kv_store_service.stop(); 75 | } 76 | -------------------------------------------------------------------------------- /example/pub_sub_example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | project(pub_sub_client) 4 | 5 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/") 6 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread -std=c++14") 7 | 8 | if (CMAKE_BUILD_TYPE STREQUAL Debug) 9 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_DEBUG") 10 | #add_definitions( 11 | # -D_DEBUG 12 | #) 13 | endif () 14 | 15 | add_definitions(-DFMT_HEADER_ONLY) 16 | 17 | find_package(Boost 1.55 COMPONENTS coroutine context system thread chrono serialization REQUIRED) 18 | 19 | set(REST_RPC_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../../) 20 | 21 | include_directories( 22 | ${Boost_INCLUDE_DIRS} 23 | ${REST_RPC_DIRECTORY} 24 | ${REST_RPC_DIRECTORY}/iguana 25 | ${REST_RPC_DIRECTORY}/iguana/third_party/msgpack/include 26 | ${REST_RPC_DIRECTORY}/spdlog/include 27 | ${REST_RPC_DIRECTORY}/msgpack/include 28 | ) 29 | SET(EXTRA_LIBS ${EXTRA_LIBS} ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) 30 | 31 | 32 | set(PUB_SOURCES 33 | pub_client_example.cpp 34 | ) 35 | 36 | set(SUB_SOURCES 37 | sub_client_example.cpp 38 | ) 39 | 40 | 41 | add_executable(pub_client ${PUB_SOURCES}) 42 | target_link_libraries(pub_client ${EXTRA_LIBS}) 43 | 44 | add_executable(sub_client ${SUB_SOURCES}) 45 | target_link_libraries(sub_client ${EXTRA_LIBS}) 46 | 47 | include (InstallRequiredSystemLibraries) 48 | set (CPACK_PACKAGE_VERSION_MAJOR "1") 49 | set (CPACK_PACKAGE_VERSION_MINOR "0") 50 | SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "David Doria") 51 | include (CPack) 52 | -------------------------------------------------------------------------------- /example/pub_sub_example/pub_client_example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace client 4 | { 5 | struct configure 6 | { 7 | std::string hostname; 8 | std::string port; 9 | }; 10 | REFLECTION(configure, hostname, port); 11 | 12 | configure get_config() 13 | { 14 | std::ifstream in("client.cfg"); 15 | std::stringstream ss; 16 | ss << in.rdbuf(); 17 | 18 | configure cfg = { "127.0.0.1", "9000" }; 19 | try 20 | { 21 | auto file_content = ss.str(); 22 | iguana::json::from_json(cfg, file_content.data(), file_content.size()); 23 | } 24 | catch (const std::exception& e) 25 | { 26 | timax::SPD_LOG_ERROR(e.what()); 27 | } 28 | 29 | return cfg; 30 | } 31 | 32 | TIMAX_DEFINE_PROTOCOL(add_pub, int(int, int)); 33 | TIMAX_DEFINE_FORWARD(sub_add, int); 34 | } 35 | 36 | using async_client_t = timax::rpc::async_client; 37 | 38 | int main(void) 39 | { 40 | timax::log::get().init("pub_client.lg"); 41 | 42 | auto config = client::get_config(); 43 | 44 | auto endpoint = timax::rpc::get_tcp_endpoint(config.hostname, 45 | boost::lexical_cast(config.port)); 46 | 47 | auto async_client = std::make_shared(); 48 | 49 | try 50 | { 51 | int lhs = 1, rhs = 2; 52 | 53 | while (true) 54 | { 55 | using namespace std::chrono_literals; 56 | async_client->call(endpoint, client::add_pub, lhs, rhs++); 57 | std::this_thread::sleep_for(200ms); 58 | async_client->pub(endpoint, client::sub_add, rhs); 59 | std::this_thread::sleep_for(200ms); 60 | } 61 | } 62 | catch (timax::rpc::exception const& e) 63 | { 64 | std::cout << e.get_error_message() << std::endl; 65 | } 66 | 67 | return 0; 68 | } -------------------------------------------------------------------------------- /example/pub_sub_example/sub_client_example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace client 4 | { 5 | struct configure 6 | { 7 | std::string hostname; 8 | std::string port; 9 | }; 10 | REFLECTION(configure, hostname, port); 11 | 12 | configure get_config() 13 | { 14 | std::ifstream in("client.cfg"); 15 | std::stringstream ss; 16 | ss << in.rdbuf(); 17 | 18 | configure cfg = { "127.0.0.1", "9000" }; 19 | try 20 | { 21 | auto file_content = ss.str(); 22 | iguana::json::from_json(cfg, file_content.data(), file_content.size()); 23 | } 24 | catch (const std::exception& e) 25 | { 26 | timax::SPD_LOG_ERROR(e.what()); 27 | } 28 | 29 | return cfg; 30 | } 31 | 32 | TIMAX_DEFINE_FORWARD(sub_add, int); 33 | } 34 | 35 | using async_client_t = timax::rpc::async_client; 36 | 37 | std::atomic g_flag(false); 38 | int main(void) 39 | { 40 | timax::log::get().init("sub_client.lg"); 41 | 42 | auto config = client::get_config(); 43 | 44 | auto endpoint = timax::rpc::get_tcp_endpoint(config.hostname, 45 | boost::lexical_cast(config.port)); 46 | 47 | auto async_client = std::make_shared(); 48 | 49 | try 50 | { 51 | async_client->sub(endpoint, client::sub_add, 52 | [](int r) { std::cout << r << std::endl; }, 53 | [](auto const& e) { std::cout << e.get_error_message() << std::endl; } 54 | ); 55 | } 56 | catch (timax::rpc::exception const& e) 57 | { 58 | std::cout << e.get_error_message() << std::endl; 59 | } 60 | 61 | getchar(); 62 | return 0; 63 | } -------------------------------------------------------------------------------- /example/server_example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | project(server_example) 4 | 5 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/") 6 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread -std=c++14") 7 | 8 | if (CMAKE_BUILD_TYPE STREQUAL Debug) 9 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_DEBUG") 10 | #add_definitions( 11 | # -D_DEBUG 12 | #) 13 | endif () 14 | 15 | add_definitions(-DFMT_HEADER_ONLY) 16 | 17 | find_package(Boost 1.55 COMPONENTS coroutine context system thread chrono serialization REQUIRED) 18 | 19 | set(REST_RPC_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../../) 20 | 21 | include_directories( 22 | ${Boost_INCLUDE_DIRS} 23 | ${REST_RPC_DIRECTORY} 24 | ${REST_RPC_DIRECTORY}/iguana 25 | ${REST_RPC_DIRECTORY}/iguana/third_party/msgpack/include 26 | ${REST_RPC_DIRECTORY}/spdlog/include 27 | ${REST_RPC_DIRECTORY}/msgpack/include 28 | ) 29 | SET(EXTRA_LIBS ${EXTRA_LIBS} ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) 30 | 31 | 32 | set(SOURCE_FILES 33 | server_example.cpp 34 | ) 35 | 36 | 37 | add_executable(server_example ${SOURCE_FILES}) 38 | target_link_libraries(server_example ${EXTRA_LIBS}) 39 | 40 | include (InstallRequiredSystemLibraries) 41 | set (CPACK_PACKAGE_VERSION_MAJOR "1") 42 | set (CPACK_PACKAGE_VERSION_MINOR "0") 43 | SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "David Doria") 44 | include (CPack) 45 | -------------------------------------------------------------------------------- /example/server_example/server_example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | uint16_t port = 9000; 4 | size_t pool_size = std::thread::hardware_concurrency(); 5 | 6 | struct person 7 | { 8 | int age; 9 | std::string name; 10 | }; 11 | 12 | REFLECTION(person, age, name); 13 | 14 | namespace client 15 | { 16 | int test(person const& p) 17 | { 18 | return p.age; 19 | } 20 | 21 | int add(int a, int b) 22 | { 23 | return a + b; 24 | } 25 | 26 | void dummy() 27 | { 28 | std::cout << "dummy" << std::endl; 29 | } 30 | 31 | void some_task_takes_a_lot_of_time(double, int) 32 | { 33 | using namespace std::chrono_literals; 34 | std::this_thread::sleep_for(5s); 35 | } 36 | 37 | struct foo 38 | { 39 | template 40 | T add_impl(T a, T b) 41 | { 42 | return a + b; 43 | } 44 | 45 | int add(int a, int b) 46 | { 47 | return add_impl(a, b); 48 | } 49 | }; 50 | } 51 | 52 | struct test 53 | { 54 | void compose(int i, const std::string& str, const timax::rpc::blob_t& bl, double d) 55 | { 56 | std::cout << i << " " << str << " " << bl.data() << " " << bl.size() <<" "< 62 | void print(std::index_sequence) 63 | { 64 | bool swallow[] = { (printf("%d\n", Is), true)... }; 65 | } 66 | 67 | int main() 68 | { 69 | timax::log::get().init("rest_rpc_server.lg"); 70 | using server_t = timax::rpc::server; 71 | server_t server{ port, pool_size, std::chrono::seconds{ 2 } }; 72 | client::foo foo{}; 73 | 74 | server.register_handler("add", client::add); 75 | server.register_handler("test", client::test); 76 | server.register_handler("add_pub", client::add, [&server](auto conn, int r) { server.pub("sub_add", r); }); 77 | server.register_handler("foo_add", timax::bind(&client::foo::add, &foo)); 78 | server.register_handler("dummy", client::dummy); 79 | server.register_handler("add_with_conn", [] 80 | (timax::rpc::connection_ptr conn, int a, int b) 81 | { 82 | auto result = a + b; 83 | if (result < 1) 84 | conn->close(); 85 | return result; 86 | }); 87 | 88 | server.async_register_handler("time_consuming", client::some_task_takes_a_lot_of_time, [](auto conn) { std::cout << "acomplished!" << std::endl; }); 89 | 90 | test t; 91 | server.register_handler("compose", timax::bind(&test::compose, &t)); 92 | 93 | server.start(); 94 | std::getchar(); 95 | server.stop(); 96 | return 0; 97 | } -------------------------------------------------------------------------------- /example/sync_client_example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | project(sync_client) 4 | 5 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/") 6 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread -std=c++14") 7 | 8 | if (CMAKE_BUILD_TYPE STREQUAL Debug) 9 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_DEBUG") 10 | #add_definitions( 11 | # -D_DEBUG 12 | #) 13 | endif () 14 | 15 | add_definitions(-DFMT_HEADER_ONLY) 16 | 17 | find_package(Boost 1.55 COMPONENTS coroutine context system thread chrono REQUIRED) 18 | 19 | set(REST_RPC_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../..) 20 | 21 | include_directories( 22 | ${Boost_INCLUDE_DIRS} 23 | ${REST_RPC_DIRECTORY} 24 | ${REST_RPC_DIRECTORY}/iguana 25 | ${REST_RPC_DIRECTORY}/iguana/third_party/msgpack/include 26 | ${REST_RPC_DIRECTORY}/spdlog/include 27 | ${REST_RPC_DIRECTORY}/msgpack/include 28 | ) 29 | SET(EXTRA_LIBS ${EXTRA_LIBS} ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) 30 | 31 | include_directories(${PROJECT_SOURCE_DIR}) 32 | 33 | set(SOURCE_FILES 34 | sync_client_example.cpp 35 | ) 36 | 37 | 38 | add_executable(sync_client ${SOURCE_FILES}) 39 | target_link_libraries(sync_client ${EXTRA_LIBS}) 40 | 41 | include (InstallRequiredSystemLibraries) 42 | set (CPACK_PACKAGE_VERSION_MAJOR "1") 43 | set (CPACK_PACKAGE_VERSION_MINOR "0") 44 | SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "David Doria") 45 | include (CPack) 46 | -------------------------------------------------------------------------------- /example/sync_client_example/client.cfg: -------------------------------------------------------------------------------- 1 | { 2 | "hostname":"127.0.0.1", 3 | "port":"9000" 4 | } -------------------------------------------------------------------------------- /example/sync_client_example/sync_client_example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace client 4 | { 5 | struct person 6 | { 7 | int age; 8 | std::string name; 9 | }; 10 | REFLECTION(person, age, name); 11 | 12 | struct configure 13 | { 14 | std::string hostname; 15 | std::string port; 16 | }; 17 | REFLECTION(configure, hostname, port); 18 | 19 | configure get_config() 20 | { 21 | std::ifstream in("client.cfg"); 22 | std::stringstream ss; 23 | ss << in.rdbuf(); 24 | 25 | configure cfg = { "127.0.0.1", "9000" }; 26 | try 27 | { 28 | auto file_content = ss.str(); 29 | iguana::json::from_json(cfg, file_content.data(), file_content.size()); 30 | } 31 | catch (const std::exception& e) 32 | { 33 | timax::SPD_LOG_ERROR(e.what()); 34 | } 35 | 36 | return cfg; 37 | } 38 | } 39 | 40 | using sync_client = timax::rpc::sync_client; 41 | 42 | namespace client 43 | { 44 | TIMAX_DEFINE_PROTOCOL(add, int(int, int)); 45 | TIMAX_DEFINE_PROTOCOL(test, int(person)); 46 | } 47 | 48 | int main(void) 49 | { 50 | timax::log::get().init("rest_rpc_client.lg"); 51 | //auto cfg = client::get_config(); 52 | 53 | auto endpoint = timax::rpc::get_tcp_endpoint("127.0.0.1", 9000); 54 | 55 | sync_client client; 56 | 57 | try 58 | { 59 | auto result = client.call(endpoint, client::add, 1, 2); 60 | assert(result == 3); 61 | 62 | //client.call(endpoint, client::madoka, 2.0, 8); 63 | } 64 | catch (timax::rpc::exception const& e) 65 | { 66 | std::cout << e.get_error_message() << std::endl; 67 | } 68 | 69 | return 0; 70 | } -------------------------------------------------------------------------------- /example/test_timeout_and_switch_endoint/test_timeout_and_switch_endpoint.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace client 4 | { 5 | TIMAX_DEFINE_PROTOCOL(add, int(int, int)); 6 | } 7 | 8 | int main(void) 9 | { 10 | using namespace std::chrono_literals; 11 | using client_t = timax::rpc::async_client; 12 | 13 | auto endpoints = timax::rpc::get_tcp_endpoints("192.168.2.204:9000|192.168.2.237:9000"); 14 | auto itr = endpoints.begin(); 15 | 16 | client_t async_client; 17 | 18 | while (true) 19 | { 20 | try 21 | { 22 | auto task = async_client.call(*itr, client::add, 1, 2); 23 | auto r = task.get(20s); 24 | assert(3 == r); 25 | } 26 | catch (timax::rpc::exception const& error) 27 | { 28 | auto ec = error.get_error_code(); 29 | if (ec == timax::rpc::error_code::BADCONNECTION || 30 | ec == timax::rpc::error_code::TIMEOUT) 31 | { 32 | ++itr; 33 | if (itr == endpoints.end()) 34 | itr = endpoints.begin(); 35 | } 36 | else 37 | { 38 | break; 39 | } 40 | } 41 | 42 | std::this_thread::sleep_for(1s); 43 | } 44 | 45 | return 0; 46 | } -------------------------------------------------------------------------------- /example/timax_bind/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | project(timax_bind) 4 | 5 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/") 6 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread -std=c++14") 7 | 8 | if (CMAKE_BUILD_TYPE STREQUAL Debug) 9 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_DEBUG") 10 | #add_definitions( 11 | # -D_DEBUG 12 | #) 13 | endif () 14 | 15 | add_definitions(-DFMT_HEADER_ONLY) 16 | 17 | find_package(Boost COMPONENTS coroutine context system thread chrono REQUIRED) 18 | 19 | set(REST_RPC_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../../) 20 | 21 | include_directories( 22 | ${Boost_INCLUDE_DIRS} 23 | ${REST_RPC_DIRECTORY} 24 | ${REST_RPC_DIRECTORY}/iguana 25 | ${REST_RPC_DIRECTORY}/iguana/third_party/msgpack/include 26 | ${REST_RPC_DIRECTORY}/spdlog/include 27 | ${REST_RPC_DIRECTORY}/msgpack/include 28 | ) 29 | SET(EXTRA_LIBS ${EXTRA_LIBS} ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) 30 | 31 | 32 | set(SOURCE_FILES 33 | timax_bind.cpp 34 | ) 35 | 36 | 37 | add_executable(timax_bind ${SOURCE_FILES}) 38 | target_link_libraries(timax_bind ${EXTRA_LIBS}) 39 | 40 | include (InstallRequiredSystemLibraries) 41 | set (CPACK_PACKAGE_VERSION_MAJOR "1") 42 | set (CPACK_PACKAGE_VERSION_MINOR "0") 43 | SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "David Doria") 44 | include (CPack) 45 | -------------------------------------------------------------------------------- /example/timax_bind/timax_bind.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace client 4 | { 5 | int add(int a, int b) 6 | { 7 | return a + b; 8 | } 9 | 10 | int apply_add(std::function add_result, int rhs) 11 | { 12 | return add_result() + rhs; 13 | } 14 | 15 | struct foo 16 | { 17 | double wtf(int a, std::string const& b) const 18 | { 19 | return a * static_cast(b.size()); 20 | } 21 | }; 22 | } 23 | 24 | int main() 25 | { 26 | using namespace std::string_literals; 27 | auto string = "127.0.0.1:9000, 127.0.0.1:4000"s; 28 | auto endpoints = timax::rpc::get_tcp_endpoints(string); 29 | for (auto const& endpoint : endpoints) 30 | { 31 | std::cout << endpoint << std::endl; 32 | } 33 | 34 | //using namespace std::placeholders; 35 | client::foo foo; 36 | auto bind1_with_boost_placeholders = timax::bind(&client::foo::wtf, foo, 1, _1); 37 | auto bind1_with_std_placeholders = timax::bind(&client::foo::wtf, foo, 38 | std::placeholders::_1, std::placeholders::_2); 39 | auto bind2 = timax::bind(client::add, 1, _1); 40 | auto bind3 = timax::bind(&client::foo::wtf, &foo); 41 | 42 | auto foo_ptr = std::make_shared(); 43 | auto bind4 = timax::bind(&client::foo::wtf, foo_ptr); 44 | auto bind_test = timax::bind(client::apply_add, timax::bind(client::add, 1, 1), _1); 45 | 46 | bind1_with_boost_placeholders("boost::placeholders"); 47 | bind1_with_std_placeholders(2, "std::placehodlers"); 48 | bind2(2); 49 | bind3(3, "WTF"); 50 | bind4(4, "shared_ptr"); 51 | bind_test(1); 52 | 53 | return 0; 54 | } -------------------------------------------------------------------------------- /msvc/async_client_example/async_client_example.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | x64 7 | 8 | 9 | Release 10 | x64 11 | 12 | 13 | 14 | {290C2B17-D183-479E-82B1-AE60C973FBC3} 15 | Win32Proj 16 | async_client_example 17 | 8.1 18 | 19 | 20 | 21 | Application 22 | true 23 | v140 24 | Unicode 25 | 26 | 27 | Application 28 | false 29 | v140 30 | true 31 | Unicode 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | true 49 | ..\..\Kapok\fmt;..\..\thread-pool-cpp\thread_pool;$(IncludePath) 50 | 51 | 52 | false 53 | ..\..\Kapok\fmt;..\..\thread-pool-cpp\thread_pool;$(IncludePath) 54 | 55 | 56 | 57 | 58 | 59 | Level3 60 | Disabled 61 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions);FMT_HEADER_ONLY 62 | true 63 | 4146 64 | 65 | 66 | Console 67 | true 68 | 69 | 70 | 71 | 72 | Level3 73 | 74 | 75 | MaxSpeed 76 | true 77 | true 78 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions);FMT_HEADER_ONLY 79 | true 80 | 4146 81 | 82 | 83 | Console 84 | true 85 | true 86 | true 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /msvc/async_client_example/async_client_example.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /msvc/bench_client/bench_client.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | x64 7 | 8 | 9 | Release 10 | x64 11 | 12 | 13 | 14 | 15 | 16 | 17 | {62CF66E7-E088-4883-8DA9-99DEF93581F3} 18 | Win32Proj 19 | bench_client 20 | 8.1 21 | 22 | 23 | 24 | Application 25 | true 26 | v140 27 | Unicode 28 | 29 | 30 | Application 31 | false 32 | v140 33 | true 34 | Unicode 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | true 52 | 53 | 54 | false 55 | 56 | 57 | 58 | 59 | 60 | Level3 61 | Disabled 62 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 63 | 64 | 65 | Console 66 | true 67 | 68 | 69 | 70 | 71 | Level3 72 | 73 | 74 | MaxSpeed 75 | true 76 | true 77 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 78 | 79 | 80 | Console 81 | true 82 | true 83 | true 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /msvc/bench_client/bench_client.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /msvc/bench_server/bench_server.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | x64 7 | 8 | 9 | Release 10 | x64 11 | 12 | 13 | 14 | 15 | 16 | 17 | {3E4AD516-30BC-406F-BFB0-2181BE508229} 18 | Win32Proj 19 | bench_server 20 | 8.1 21 | 22 | 23 | 24 | Application 25 | true 26 | v140 27 | Unicode 28 | 29 | 30 | Application 31 | false 32 | v140 33 | true 34 | Unicode 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | true 52 | 53 | 54 | false 55 | 56 | 57 | 58 | 59 | 60 | Level3 61 | Disabled 62 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 63 | 64 | 65 | Console 66 | true 67 | 68 | 69 | 70 | 71 | Level3 72 | 73 | 74 | MaxSpeed 75 | true 76 | true 77 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 78 | 79 | 80 | Console 81 | true 82 | true 83 | true 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /msvc/bench_server/bench_server.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /msvc/codec_policy/codec_policy.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | x64 7 | 8 | 9 | Release 10 | x64 11 | 12 | 13 | 14 | {ECFC6AFC-1C5A-4109-898C-82D426620DC1} 15 | Win32Proj 16 | codec_policy 17 | 8.1 18 | 19 | 20 | 21 | Application 22 | true 23 | v140 24 | Unicode 25 | 26 | 27 | Application 28 | false 29 | v140 30 | true 31 | Unicode 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | true 49 | 50 | 51 | false 52 | 53 | 54 | 55 | 56 | 57 | Level3 58 | Disabled 59 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 60 | 61 | 62 | Console 63 | true 64 | 65 | 66 | 67 | 68 | Level3 69 | 70 | 71 | MaxSpeed 72 | true 73 | true 74 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 75 | 76 | 77 | Console 78 | true 79 | true 80 | true 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /msvc/codec_policy/codec_policy.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /msvc/key_value_server/key_value_server.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | x64 7 | 8 | 9 | Release 10 | x64 11 | 12 | 13 | 14 | {453872A1-A3DD-4AC7-8917-DC3F6291DE14} 15 | Win32Proj 16 | key_value_server 17 | 8.1 18 | 19 | 20 | 21 | Application 22 | true 23 | v140 24 | Unicode 25 | 26 | 27 | Application 28 | false 29 | v140 30 | true 31 | Unicode 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | true 49 | 50 | 51 | false 52 | 53 | 54 | 55 | 56 | 57 | Level3 58 | Disabled 59 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 60 | 61 | 62 | Console 63 | true 64 | 65 | 66 | 67 | 68 | Level3 69 | 70 | 71 | MaxSpeed 72 | true 73 | true 74 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 75 | 76 | 77 | Console 78 | true 79 | true 80 | true 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /msvc/key_value_server/key_value_server.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /msvc/kv_store_client/kv_store_client.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | x64 7 | 8 | 9 | Release 10 | x64 11 | 12 | 13 | 14 | {7217D288-20EC-4AA3-97BB-AB5CD1027312} 15 | Win32Proj 16 | kv_store_client 17 | 8.1 18 | 19 | 20 | 21 | Application 22 | true 23 | v140 24 | Unicode 25 | 26 | 27 | Application 28 | false 29 | v140 30 | true 31 | Unicode 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | true 49 | 50 | 51 | false 52 | 53 | 54 | 55 | 56 | 57 | Level3 58 | Disabled 59 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 60 | 61 | 62 | Console 63 | true 64 | 65 | 66 | 67 | 68 | Level3 69 | 70 | 71 | MaxSpeed 72 | true 73 | true 74 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 75 | 76 | 77 | Console 78 | true 79 | true 80 | true 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /msvc/kv_store_client/kv_store_client.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /msvc/pub_client_example/pub_client_example.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | x64 7 | 8 | 9 | Release 10 | x64 11 | 12 | 13 | 14 | {0B1704CD-DDAF-4A2A-918F-E79D1CE5C8F0} 15 | Win32Proj 16 | pub_client_example 17 | 8.1 18 | 19 | 20 | 21 | Application 22 | true 23 | v140 24 | Unicode 25 | 26 | 27 | Application 28 | false 29 | v140 30 | true 31 | Unicode 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | true 49 | ..\..\msgpack\include;..\..\Kapok\fmt;$(IncludePath) 50 | 51 | 52 | false 53 | ..\..\msgpack\include;..\..\Kapok\fmt;$(IncludePath) 54 | 55 | 56 | 57 | 58 | 59 | Level3 60 | Disabled 61 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 62 | 63 | 64 | Console 65 | true 66 | 67 | 68 | 69 | 70 | Level3 71 | 72 | 73 | MaxSpeed 74 | true 75 | true 76 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 77 | 78 | 79 | Console 80 | true 81 | true 82 | true 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /msvc/pub_client_example/pub_client_example.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /msvc/rest_examples.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | D:\coding\dev_env\boost\include\boost-1_62\ 6 | D:\coding\dev_env\boost\lib\ 7 | 8 | 9 | 10 | 11 | $(SolutionDir)../;$(SolutionDir)../spdlog/include;$(SolutionDir)../iguana;$(SolutionDir)../iguana/third_party/msgpack/include 12 | FMT_HEADER_ONLY;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) 13 | true 14 | 15 | 16 | $(BOOST_LIB_PATH);%(AdditionalLibraryDirectories) 17 | 18 | 19 | 20 | 21 | $(BOOST_INCLUDE_PATH) 22 | 23 | 24 | $(BOOST_LIB_PATH) 25 | 26 | 27 | -------------------------------------------------------------------------------- /msvc/rest_rpc.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "server_example", "server_example\server_example.vcxproj", "{76C1387E-37D9-413E-A506-FADE500F9617}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sync_client_example", "sync_client_example\sync_client_example.vcxproj", "{FB0953C5-2EB7-469E-A3E0-B6A3F05E13EC}" 9 | EndProject 10 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "async_client_example", "async_client_example\async_client_example.vcxproj", "{290C2B17-D183-479E-82B1-AE60C973FBC3}" 11 | EndProject 12 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pub_client_example", "pub_client_example\pub_client_example.vcxproj", "{0B1704CD-DDAF-4A2A-918F-E79D1CE5C8F0}" 13 | EndProject 14 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sub_client_example", "sub_client_example\sub_client_example.vcxproj", "{3A941BE6-D082-47B4-A6D6-81D693173FF2}" 15 | EndProject 16 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rest_rpc", "rest_rpc\rest_rpc.vcxproj", "{BB7587EB-C565-4B77-82B9-0F4D8C30E3CC}" 17 | EndProject 18 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bench_client", "bench_client\bench_client.vcxproj", "{62CF66E7-E088-4883-8DA9-99DEF93581F3}" 19 | EndProject 20 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bench_server", "bench_server\bench_server.vcxproj", "{3E4AD516-30BC-406F-BFB0-2181BE508229}" 21 | EndProject 22 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "timax_bind", "timax_bind\timax_bind.vcxproj", "{AD860AD3-BEAB-4B28-B789-65BFBB877C22}" 23 | EndProject 24 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "key_value_server", "key_value_server\key_value_server.vcxproj", "{453872A1-A3DD-4AC7-8917-DC3F6291DE14}" 25 | EndProject 26 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "kv_store_client", "kv_store_client\kv_store_client.vcxproj", "{7217D288-20EC-4AA3-97BB-AB5CD1027312}" 27 | EndProject 28 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_timeout_and_switch_endpoint", "test_timeout_and_switch_endpoint\test_timeout_and_switch_endpoint.vcxproj", "{1E568365-1958-4BEE-AF5D-BD2CB9F3608D}" 29 | EndProject 30 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "codec_policy", "codec_policy\codec_policy.vcxproj", "{ECFC6AFC-1C5A-4109-898C-82D426620DC1}" 31 | EndProject 32 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "lowlevel_test", "lowlevel_test", "{FAA777D7-F56B-475E-9496-F036356A8A61}" 33 | EndProject 34 | Global 35 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 36 | Debug|x64 = Debug|x64 37 | Release|x64 = Release|x64 38 | EndGlobalSection 39 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 40 | {76C1387E-37D9-413E-A506-FADE500F9617}.Debug|x64.ActiveCfg = Debug|x64 41 | {76C1387E-37D9-413E-A506-FADE500F9617}.Debug|x64.Build.0 = Debug|x64 42 | {76C1387E-37D9-413E-A506-FADE500F9617}.Release|x64.ActiveCfg = Release|x64 43 | {76C1387E-37D9-413E-A506-FADE500F9617}.Release|x64.Build.0 = Release|x64 44 | {FB0953C5-2EB7-469E-A3E0-B6A3F05E13EC}.Debug|x64.ActiveCfg = Debug|x64 45 | {FB0953C5-2EB7-469E-A3E0-B6A3F05E13EC}.Debug|x64.Build.0 = Debug|x64 46 | {FB0953C5-2EB7-469E-A3E0-B6A3F05E13EC}.Release|x64.ActiveCfg = Release|x64 47 | {FB0953C5-2EB7-469E-A3E0-B6A3F05E13EC}.Release|x64.Build.0 = Release|x64 48 | {290C2B17-D183-479E-82B1-AE60C973FBC3}.Debug|x64.ActiveCfg = Debug|x64 49 | {290C2B17-D183-479E-82B1-AE60C973FBC3}.Debug|x64.Build.0 = Debug|x64 50 | {290C2B17-D183-479E-82B1-AE60C973FBC3}.Release|x64.ActiveCfg = Release|x64 51 | {290C2B17-D183-479E-82B1-AE60C973FBC3}.Release|x64.Build.0 = Release|x64 52 | {0B1704CD-DDAF-4A2A-918F-E79D1CE5C8F0}.Debug|x64.ActiveCfg = Debug|x64 53 | {0B1704CD-DDAF-4A2A-918F-E79D1CE5C8F0}.Debug|x64.Build.0 = Debug|x64 54 | {0B1704CD-DDAF-4A2A-918F-E79D1CE5C8F0}.Release|x64.ActiveCfg = Release|x64 55 | {0B1704CD-DDAF-4A2A-918F-E79D1CE5C8F0}.Release|x64.Build.0 = Release|x64 56 | {3A941BE6-D082-47B4-A6D6-81D693173FF2}.Debug|x64.ActiveCfg = Debug|x64 57 | {3A941BE6-D082-47B4-A6D6-81D693173FF2}.Debug|x64.Build.0 = Debug|x64 58 | {3A941BE6-D082-47B4-A6D6-81D693173FF2}.Release|x64.ActiveCfg = Release|x64 59 | {3A941BE6-D082-47B4-A6D6-81D693173FF2}.Release|x64.Build.0 = Release|x64 60 | {BB7587EB-C565-4B77-82B9-0F4D8C30E3CC}.Debug|x64.ActiveCfg = Debug|x64 61 | {BB7587EB-C565-4B77-82B9-0F4D8C30E3CC}.Debug|x64.Build.0 = Debug|x64 62 | {BB7587EB-C565-4B77-82B9-0F4D8C30E3CC}.Release|x64.ActiveCfg = Release|x64 63 | {BB7587EB-C565-4B77-82B9-0F4D8C30E3CC}.Release|x64.Build.0 = Release|x64 64 | {62CF66E7-E088-4883-8DA9-99DEF93581F3}.Debug|x64.ActiveCfg = Debug|x64 65 | {62CF66E7-E088-4883-8DA9-99DEF93581F3}.Debug|x64.Build.0 = Debug|x64 66 | {62CF66E7-E088-4883-8DA9-99DEF93581F3}.Release|x64.ActiveCfg = Release|x64 67 | {62CF66E7-E088-4883-8DA9-99DEF93581F3}.Release|x64.Build.0 = Release|x64 68 | {3E4AD516-30BC-406F-BFB0-2181BE508229}.Debug|x64.ActiveCfg = Debug|x64 69 | {3E4AD516-30BC-406F-BFB0-2181BE508229}.Debug|x64.Build.0 = Debug|x64 70 | {3E4AD516-30BC-406F-BFB0-2181BE508229}.Release|x64.ActiveCfg = Release|x64 71 | {3E4AD516-30BC-406F-BFB0-2181BE508229}.Release|x64.Build.0 = Release|x64 72 | {AD860AD3-BEAB-4B28-B789-65BFBB877C22}.Debug|x64.ActiveCfg = Debug|x64 73 | {AD860AD3-BEAB-4B28-B789-65BFBB877C22}.Debug|x64.Build.0 = Debug|x64 74 | {AD860AD3-BEAB-4B28-B789-65BFBB877C22}.Release|x64.ActiveCfg = Release|x64 75 | {AD860AD3-BEAB-4B28-B789-65BFBB877C22}.Release|x64.Build.0 = Release|x64 76 | {453872A1-A3DD-4AC7-8917-DC3F6291DE14}.Debug|x64.ActiveCfg = Debug|x64 77 | {453872A1-A3DD-4AC7-8917-DC3F6291DE14}.Debug|x64.Build.0 = Debug|x64 78 | {453872A1-A3DD-4AC7-8917-DC3F6291DE14}.Release|x64.ActiveCfg = Release|x64 79 | {453872A1-A3DD-4AC7-8917-DC3F6291DE14}.Release|x64.Build.0 = Release|x64 80 | {7217D288-20EC-4AA3-97BB-AB5CD1027312}.Debug|x64.ActiveCfg = Debug|x64 81 | {7217D288-20EC-4AA3-97BB-AB5CD1027312}.Debug|x64.Build.0 = Debug|x64 82 | {7217D288-20EC-4AA3-97BB-AB5CD1027312}.Release|x64.ActiveCfg = Release|x64 83 | {7217D288-20EC-4AA3-97BB-AB5CD1027312}.Release|x64.Build.0 = Release|x64 84 | {1E568365-1958-4BEE-AF5D-BD2CB9F3608D}.Debug|x64.ActiveCfg = Debug|x64 85 | {1E568365-1958-4BEE-AF5D-BD2CB9F3608D}.Debug|x64.Build.0 = Debug|x64 86 | {1E568365-1958-4BEE-AF5D-BD2CB9F3608D}.Release|x64.ActiveCfg = Release|x64 87 | {1E568365-1958-4BEE-AF5D-BD2CB9F3608D}.Release|x64.Build.0 = Release|x64 88 | {ECFC6AFC-1C5A-4109-898C-82D426620DC1}.Debug|x64.ActiveCfg = Debug|x64 89 | {ECFC6AFC-1C5A-4109-898C-82D426620DC1}.Debug|x64.Build.0 = Debug|x64 90 | {ECFC6AFC-1C5A-4109-898C-82D426620DC1}.Release|x64.ActiveCfg = Release|x64 91 | {ECFC6AFC-1C5A-4109-898C-82D426620DC1}.Release|x64.Build.0 = Release|x64 92 | EndGlobalSection 93 | GlobalSection(SolutionProperties) = preSolution 94 | HideSolutionNode = FALSE 95 | EndGlobalSection 96 | GlobalSection(NestedProjects) = preSolution 97 | {AD860AD3-BEAB-4B28-B789-65BFBB877C22} = {FAA777D7-F56B-475E-9496-F036356A8A61} 98 | {ECFC6AFC-1C5A-4109-898C-82D426620DC1} = {FAA777D7-F56B-475E-9496-F036356A8A61} 99 | EndGlobalSection 100 | EndGlobal 101 | -------------------------------------------------------------------------------- /msvc/rest_rpc/ReadMe.txt: -------------------------------------------------------------------------------- 1 | ======================================================================== 2 | STATIC LIBRARY : rest_rpc Project Overview 3 | ======================================================================== 4 | 5 | AppWizard has created this rest_rpc library project for you. 6 | 7 | No source files were created as part of your project. 8 | 9 | 10 | rest_rpc.vcxproj 11 | This is the main project file for VC++ projects generated using an Application Wizard. 12 | It contains information about the version of Visual C++ that generated the file, and 13 | information about the platforms, configurations, and project features selected with the 14 | Application Wizard. 15 | 16 | rest_rpc.vcxproj.filters 17 | This is the filters file for VC++ projects generated using an Application Wizard. 18 | It contains information about the association between the files in your project 19 | and the filters. This association is used in the IDE to show grouping of files with 20 | similar extensions under a specific node (for e.g. ".cpp" files are associated with the 21 | "Source Files" filter). 22 | 23 | ///////////////////////////////////////////////////////////////////////////// 24 | Other notes: 25 | 26 | AppWizard uses "TODO:" comments to indicate parts of the source code you 27 | should add to or customize. 28 | 29 | ///////////////////////////////////////////////////////////////////////////// 30 | -------------------------------------------------------------------------------- /msvc/rest_rpc/rest_rpc.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | x64 7 | 8 | 9 | Release 10 | x64 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | {BB7587EB-C565-4B77-82B9-0F4D8C30E3CC} 52 | Win32Proj 53 | rest_rpc 54 | 8.1 55 | 56 | 57 | 58 | StaticLibrary 59 | true 60 | v140 61 | Unicode 62 | 63 | 64 | StaticLibrary 65 | false 66 | v140 67 | true 68 | Unicode 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | Level3 90 | Disabled 91 | _DEBUG;_LIB;%(PreprocessorDefinitions) 92 | 93 | 94 | Windows 95 | 96 | 97 | 98 | 99 | Level3 100 | 101 | 102 | MaxSpeed 103 | true 104 | true 105 | NDEBUG;_LIB;%(PreprocessorDefinitions) 106 | 107 | 108 | Windows 109 | true 110 | true 111 | 112 | 113 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /msvc/rest_rpc/rest_rpc.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {24e37928-1991-4dc3-ba22-e1520441ec16} 6 | 7 | 8 | {d4ce1e0f-96bd-4ce4-ab64-1698ae255233} 9 | 10 | 11 | {fd5429ba-5c93-48cd-ab60-b1f749ce23c1} 12 | 13 | 14 | {bed6d52d-9edd-4bbb-a143-6923fd90b181} 15 | 16 | 17 | {8a789685-3d7d-4e78-86e1-4eba49ee3e65} 18 | 19 | 20 | 21 | 22 | base 23 | 24 | 25 | base 26 | 27 | 28 | base 29 | 30 | 31 | base 32 | 33 | 34 | base 35 | 36 | 37 | base 38 | 39 | 40 | client 41 | 42 | 43 | client 44 | 45 | 46 | server 47 | 48 | 49 | server 50 | 51 | 52 | server 53 | 54 | 55 | server 56 | 57 | 58 | 59 | 60 | 61 | 62 | client\detail 63 | 64 | 65 | client\detail 66 | 67 | 68 | client\detail 69 | 70 | 71 | server 72 | 73 | 74 | base 75 | 76 | 77 | client 78 | 79 | 80 | server 81 | 82 | 83 | client\detail 84 | 85 | 86 | client\detail 87 | 88 | 89 | client\detail 90 | 91 | 92 | client\detail 93 | 94 | 95 | server 96 | 97 | 98 | base 99 | 100 | 101 | server 102 | 103 | 104 | codec 105 | 106 | 107 | codec 108 | 109 | 110 | base 111 | 112 | 113 | codec 114 | 115 | 116 | codec 117 | 118 | 119 | -------------------------------------------------------------------------------- /msvc/server_example/server.cfg: -------------------------------------------------------------------------------- 1 | { 2 | "port":9000, 3 | "thread_num":4, 4 | "nodelay":false 5 | } -------------------------------------------------------------------------------- /msvc/server_example/server_example.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | x64 7 | 8 | 9 | Release 10 | x64 11 | 12 | 13 | 14 | {76C1387E-37D9-413E-A506-FADE500F9617} 15 | Win32Proj 16 | server_example 17 | 8.1 18 | 19 | 20 | 21 | Application 22 | true 23 | v140 24 | Unicode 25 | 26 | 27 | Application 28 | false 29 | v140 30 | true 31 | Unicode 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | true 49 | ..\..\Kapok\fmt;..\..\thread-pool-cpp\thread_pool;..\..\msgpack\include;$(IncludePath) 50 | 51 | 52 | false 53 | ..\..\Kapok\fmt;..\..\thread-pool-cpp\thread_pool;..\..\msgpack\include;$(IncludePath) 54 | 55 | 56 | 57 | 58 | 59 | Level3 60 | Disabled 61 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions);FMT_HEADER_ONLY 62 | true 63 | 4146 64 | 65 | 66 | Console 67 | true 68 | 69 | 70 | 71 | 72 | Level3 73 | 74 | 75 | MaxSpeed 76 | true 77 | true 78 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions);FMT_HEADER_ONLY 79 | true 80 | 4146 81 | 82 | 83 | Console 84 | true 85 | true 86 | true 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /msvc/server_example/server_example.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /msvc/server_example/tempfile: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/purecpp-org/rest_rpc_old-out-of-date-now-/3f562a1388ca83c1832132ed2fb66d9a70f57142/msvc/server_example/tempfile -------------------------------------------------------------------------------- /msvc/sub_client_example/sub_client_example.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | x64 7 | 8 | 9 | Release 10 | x64 11 | 12 | 13 | 14 | {3A941BE6-D082-47B4-A6D6-81D693173FF2} 15 | Win32Proj 16 | sub_client_example 17 | 8.1 18 | 19 | 20 | 21 | Application 22 | true 23 | v140 24 | Unicode 25 | 26 | 27 | Application 28 | false 29 | v140 30 | true 31 | Unicode 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | true 49 | ..\..\msgpack\include;..\..\Kapok\fmt;$(IncludePath) 50 | 51 | 52 | false 53 | ..\..\msgpack\include;..\..\Kapok\fmt;$(IncludePath) 54 | 55 | 56 | 57 | 58 | 59 | Level3 60 | Disabled 61 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 62 | 63 | 64 | Console 65 | true 66 | 67 | 68 | 69 | 70 | Level3 71 | 72 | 73 | MaxSpeed 74 | true 75 | true 76 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 77 | 78 | 79 | Console 80 | true 81 | true 82 | true 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /msvc/sub_client_example/sub_client_example.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /msvc/sync_client_example/client.cfg: -------------------------------------------------------------------------------- 1 | { 2 | "hostname":"127.0.0.1", 3 | "port":"9000" 4 | } -------------------------------------------------------------------------------- /msvc/sync_client_example/sync_client_example.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | x64 7 | 8 | 9 | Release 10 | x64 11 | 12 | 13 | 14 | {FB0953C5-2EB7-469E-A3E0-B6A3F05E13EC} 15 | Win32Proj 16 | sync_client_example 17 | 8.1 18 | 19 | 20 | 21 | Application 22 | true 23 | v140 24 | Unicode 25 | 26 | 27 | Application 28 | false 29 | v140 30 | true 31 | Unicode 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | true 49 | ..\..\msgpack\include;..\..\Kapok\fmt;$(IncludePath) 50 | 51 | 52 | false 53 | ..\..\msgpack\include;..\..\Kapok\fmt;$(IncludePath) 54 | 55 | 56 | 57 | 58 | 59 | Level3 60 | Disabled 61 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions);FMT_HEADER_ONLY 62 | true 63 | 4146 64 | 65 | 66 | Console 67 | true 68 | 69 | 70 | 71 | 72 | Level3 73 | 74 | 75 | MaxSpeed 76 | true 77 | true 78 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions);FMT_HEADER_ONLY 79 | true 80 | 4146 81 | 82 | 83 | Console 84 | true 85 | true 86 | true 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /msvc/sync_client_example/sync_client_example.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /msvc/test_timeout_and_switch_endpoint/test_timeout_and_switch_endpoint.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | x64 7 | 8 | 9 | Release 10 | x64 11 | 12 | 13 | 14 | {1E568365-1958-4BEE-AF5D-BD2CB9F3608D} 15 | Win32Proj 16 | test_timeout_and_switch_endpoint 17 | 8.1 18 | 19 | 20 | 21 | Application 22 | true 23 | v140 24 | Unicode 25 | 26 | 27 | Application 28 | false 29 | v140 30 | true 31 | Unicode 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | true 49 | 50 | 51 | false 52 | 53 | 54 | 55 | 56 | 57 | Level3 58 | Disabled 59 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 60 | 61 | 62 | Console 63 | true 64 | 65 | 66 | 67 | 68 | Level3 69 | 70 | 71 | MaxSpeed 72 | true 73 | true 74 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 75 | 76 | 77 | Console 78 | true 79 | true 80 | true 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /msvc/test_timeout_and_switch_endpoint/test_timeout_and_switch_endpoint.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /msvc/timax_bind/timax_bind.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | x64 7 | 8 | 9 | Release 10 | x64 11 | 12 | 13 | 14 | {AD860AD3-BEAB-4B28-B789-65BFBB877C22} 15 | Win32Proj 16 | timax_bind 17 | 8.1 18 | 19 | 20 | 21 | Application 22 | true 23 | v140 24 | Unicode 25 | 26 | 27 | Application 28 | false 29 | v140 30 | true 31 | Unicode 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | true 49 | 50 | 51 | false 52 | 53 | 54 | 55 | 56 | 57 | Level3 58 | Disabled 59 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 60 | 61 | 62 | Console 63 | true 64 | 65 | 66 | 67 | 68 | Level3 69 | 70 | 71 | MaxSpeed 72 | true 73 | true 74 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 75 | 76 | 77 | Console 78 | true 79 | true 80 | true 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /msvc/timax_bind/timax_bind.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /rest_rpc/base/codec.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | namespace boost { namespace serialization { 7 | /** 8 | * serialization for tuples 9 | */ 10 | template 11 | void serialize(Archive & ar, const std::index_sequence&, std::tuple & t, unsigned int version) 12 | { 13 | bool arr[] = { (ar & std::get(t), false)... }; 14 | (void*)arr; 15 | } 16 | 17 | template 18 | void serialize(Archive & ar, std::tuple & t, unsigned int version) 19 | { 20 | serialize(ar, std::make_index_sequence{}, t, version); 21 | } 22 | 23 | } // end serialization namespace 24 | } // end boost namespace 25 | 26 | namespace timax { namespace rpc 27 | { 28 | struct blob_t 29 | { 30 | blob_t() : raw_ref_() {} 31 | blob_t(char const* data, size_t size) 32 | : raw_ref_(data, static_cast(size)) 33 | { 34 | } 35 | 36 | template 37 | void msgpack_pack(Packer& pk) const 38 | { 39 | pk.pack_bin(raw_ref_.size); 40 | pk.pack_bin_body(raw_ref_.ptr, raw_ref_.size); 41 | } 42 | 43 | void msgpack_unpack(msgpack::object const& o) 44 | { 45 | msgpack::operator>>(o, raw_ref_); 46 | } 47 | 48 | auto data() const 49 | { 50 | return raw_ref_.ptr; 51 | } 52 | 53 | size_t size() const 54 | { 55 | return raw_ref_.size; 56 | } 57 | 58 | msgpack::type::raw_ref raw_ref_; 59 | }; 60 | 61 | struct msgpack_codec 62 | { 63 | //using buffer_type = msgpack::sbuffer; 64 | using buffer_type = std::vector; 65 | class buffer_t 66 | { 67 | public: 68 | buffer_t() 69 | : buffer_t(0) 70 | { } 71 | 72 | explicit buffer_t(size_t len) 73 | : buffer_(len, 0) 74 | , offset_(0) 75 | { } 76 | 77 | buffer_t(buffer_t const&) = default; 78 | buffer_t(buffer_t &&) = default; 79 | buffer_t& operator= (buffer_t const&) = default; 80 | buffer_t& operator= (buffer_t &&) = default; 81 | 82 | void write(char const* data, size_t length) 83 | { 84 | if (buffer_.size() - offset_ < length) 85 | buffer_.resize(length + offset_); 86 | 87 | std::memcpy(buffer_.data() + offset_, data, length); 88 | offset_ += length; 89 | } 90 | 91 | std::vector release() const noexcept 92 | { 93 | return std::move(buffer_); 94 | } 95 | 96 | private: 97 | std::vector buffer_; 98 | size_t offset_; 99 | }; 100 | 101 | template 102 | buffer_type pack_args(Args&& ... args) const 103 | { 104 | buffer_t buffer; 105 | auto args_tuple = std::make_tuple(std::forward(args)...); 106 | msgpack::pack(buffer, args_tuple); 107 | return buffer.release(); 108 | } 109 | 110 | template 111 | buffer_type pack(T&& t) const 112 | { 113 | buffer_t buffer; 114 | msgpack::pack(buffer, std::forward(t)); 115 | return buffer.release(); 116 | } 117 | 118 | template 119 | T unpack(char const* data, size_t length) 120 | { 121 | try 122 | { 123 | msgpack::unpack(&msg_, data, length); 124 | return msg_.get().as(); 125 | } 126 | catch (...) 127 | { 128 | using namespace std::string_literals; 129 | exception error{ error_code::FAIL, "Args not match!"s }; 130 | throw error; 131 | } 132 | } 133 | 134 | private: 135 | msgpack::unpacked msg_; 136 | }; 137 | 138 | 139 | 140 | struct boost_codec 141 | { 142 | template 143 | T unpack(char const* data, size_t length) 144 | { 145 | std::stringstream ss; 146 | ss.write(data, length); 147 | boost::archive::text_iarchive ia(ss); 148 | T t; 149 | ia >> t; 150 | return t; 151 | } 152 | 153 | using buffer_type = std::vector; 154 | 155 | template 156 | buffer_type pack_args(Args&& ... args) const 157 | { 158 | auto args_tuple = std::make_tuple(std::forward(args)...); 159 | std::stringstream ss; 160 | boost::archive::text_oarchive oa(ss); 161 | oa << args_tuple; 162 | 163 | return assign(ss); 164 | } 165 | 166 | template 167 | buffer_type pack(T&& t) 168 | { 169 | std::stringstream ss; 170 | boost::archive::text_oarchive oa(ss); 171 | oa << std::forward(t); 172 | 173 | return assign(ss); 174 | } 175 | 176 | std::vector assign(std::stringstream& ss) const 177 | { 178 | std::vector vec; 179 | std::streampos beg = ss.tellg(); 180 | ss.seekg(0, std::ios_base::end); 181 | std::streampos end = ss.tellg(); 182 | ss.seekg(0, std::ios_base::beg); 183 | vec.reserve(end - beg); 184 | 185 | vec.assign(std::istreambuf_iterator(ss), std::istreambuf_iterator()); 186 | return vec; 187 | } 188 | }; 189 | } } 190 | 191 | namespace timax { namespace rpc 192 | { 193 | template 194 | struct is_std_tuple : std::false_type {}; 195 | 196 | template 197 | struct is_std_tuple> : std::true_type {}; 198 | 199 | template 200 | auto pack_as_tuple_if_not_impl(std::true_type, CodecPolicy const& cp, Arg&& arg) 201 | { 202 | return cp.pack(std::forward(arg)); 203 | } 204 | 205 | template 206 | auto pack_as_tuple_if_not_impl(std::false_type, CodecPolicy const& cp, Arg&& arg) 207 | { 208 | return cp.pack_args(std::forward(arg)); 209 | } 210 | 211 | template 212 | auto pack_as_tuple_if_not(CodecPolicy const& cp, Arg&& arg) 213 | { 214 | return pack_as_tuple_if_not_impl(is_std_tuple>>{}, 215 | cp, std::forward(arg)); 216 | } 217 | } } 218 | -------------------------------------------------------------------------------- /rest_rpc/base/common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace timax{ namespace rpc 4 | { 5 | struct req_header 6 | { 7 | req_header() = default; 8 | 9 | req_header(int16_t code, uint32_t id, uint32_t len, uint64_t hash) 10 | : code(code) 11 | , id(id) 12 | , len(len) 13 | , hash(hash) 14 | { 15 | } 16 | 17 | int16_t code = 0; 18 | uint32_t id = 0; 19 | uint32_t len = 0; 20 | uint64_t hash = 0; 21 | }; 22 | 23 | struct rep_header 24 | { 25 | rep_header() = default; 26 | 27 | rep_header(int16_t code, uint32_t id, uint32_t len) 28 | : code(code) 29 | , id(id) 30 | , len(len) 31 | { 32 | } 33 | 34 | rep_header(req_header const& header) 35 | : code(header.code) 36 | , id(header.id) 37 | , len(header.len) 38 | { 39 | } 40 | 41 | int16_t code = 0; 42 | uint32_t id = 0; 43 | uint32_t len = 0; 44 | }; 45 | 46 | enum class result_code : int16_t 47 | { 48 | OK = 0, 49 | FAIL = 1, 50 | }; 51 | 52 | enum class error_code 53 | { 54 | OK, 55 | UNKNOWN, 56 | FAIL, 57 | TIMEOUT, 58 | CANCEL, 59 | BADCONNECTION, 60 | }; 61 | } } 62 | 63 | -------------------------------------------------------------------------------- /rest_rpc/base/consts.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace timax { namespace rpc 4 | { 5 | static const char* SUB_TOPIC = "sub_topic"; 6 | static const char* PUB = "pub"; 7 | static const char* SUB_CONFIRM = "sub_confirm"; 8 | static const char* HEART_BEAT = "heart_beat"; 9 | static const char* RESULT = "result"; 10 | static const char* CODE = "code"; 11 | 12 | static const size_t MAX_BUF_LEN = 1048576*10; 13 | static const size_t HEAD_LEN = 12; 14 | static const size_t PAGE_SIZE = 1024; 15 | static const size_t MAX_QUEUE_SIZE = 10240; 16 | } } -------------------------------------------------------------------------------- /rest_rpc/base/excetion.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace timax { namespace rpc 4 | { 5 | class exception 6 | { 7 | public: 8 | exception() noexcept 9 | : error_code_(static_cast(error_code::OK)) 10 | , error_message_() 11 | { 12 | } 13 | 14 | exception(error_code ec, std::string em) 15 | : error_code_(static_cast(ec)) 16 | , error_message_(std::move(em)) 17 | { 18 | 19 | } 20 | 21 | error_code get_error_code() const noexcept 22 | { 23 | return static_cast(error_code_); 24 | } 25 | 26 | std::string const& get_error_message() const noexcept 27 | { 28 | return error_message_; 29 | } 30 | 31 | void set_message(std::string message) 32 | { 33 | error_message_ = std::move(message); 34 | } 35 | 36 | void set_code(error_code ec) noexcept 37 | { 38 | error_code_ = static_cast(ec); 39 | } 40 | 41 | operator bool() const noexcept 42 | { 43 | return error_code_ != static_cast(error_code::OK); 44 | } 45 | 46 | public: 47 | int16_t error_code_; 48 | std::string error_message_; 49 | }; 50 | 51 | REFLECTION(exception, error_code_, error_message_); 52 | } } -------------------------------------------------------------------------------- /rest_rpc/base/hash.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace timax 6 | { 7 | template 8 | struct hash_tmpl 9 | { 10 | using hash_policy = HashPolicy; 11 | using result_type = T; 12 | using argument_type = typename hash_policy::argument_type; 13 | 14 | result_type operator() (argument_type const& arg) const 15 | { 16 | return hash_policy::template hash(arg); 17 | } 18 | }; 19 | 20 | template 21 | struct bkdr_hash 22 | { 23 | using argument_type = T; 24 | 25 | template 26 | static auto hash(argument_type const& arg) 27 | { 28 | return hash_impl(reinterpret_cast(arg.c_str()), 29 | arg.size() * sizeof(typename argument_type::value_type)); 30 | } 31 | 32 | private: 33 | 34 | template 35 | static auto hash_impl(uint8_t const* first, size_t count) 36 | { 37 | ResultT seed = 131; 38 | ResultT val = 0; 39 | 40 | for (size_t loop = 0; loop < count; ++loop) 41 | { 42 | val = val * seed + first[loop]; 43 | } 44 | 45 | return val; 46 | } 47 | }; 48 | } -------------------------------------------------------------------------------- /rest_rpc/base/io_service_pool.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace timax { namespace rpc 4 | { 5 | class io_service_pool : boost::noncopyable 6 | { 7 | public: 8 | using ios_work_ptr = std::unique_ptr; 9 | 10 | class ios_worker 11 | { 12 | public: 13 | ios_worker() 14 | : ios_() 15 | , work_(std::make_unique(ios_)) 16 | {} 17 | 18 | void start() 19 | { 20 | worker_ = std::move(std::thread{ boost::bind(&io_service_t::run, &ios_) }); 21 | } 22 | 23 | void stop() 24 | { 25 | work_.reset(); 26 | if (!ios_.stopped()) 27 | ios_.stop(); 28 | } 29 | 30 | void wait() 31 | { 32 | if (worker_.joinable()) 33 | worker_.join(); 34 | } 35 | 36 | auto& get_io_service() 37 | { 38 | return ios_; 39 | } 40 | 41 | private: 42 | io_service_t ios_; 43 | ios_work_ptr work_; 44 | std::thread worker_; 45 | }; 46 | 47 | using iterator = std::list::iterator; 48 | 49 | public: 50 | explicit io_service_pool(size_t pool_size) 51 | : ios_workers_(pool_size) 52 | , next_io_service_(ios_workers_.begin()) 53 | { 54 | } 55 | 56 | ~io_service_pool() 57 | { 58 | stop(); 59 | } 60 | 61 | void start() 62 | { 63 | for (auto& ios_worker : ios_workers_) 64 | ios_worker.start(); 65 | } 66 | 67 | void stop() 68 | { 69 | for (auto& ios : ios_workers_) 70 | ios.stop(); 71 | 72 | for (auto& ios : ios_workers_) 73 | ios.wait(); 74 | } 75 | 76 | auto& get_io_service() 77 | { 78 | auto current = next_io_service_++; 79 | if (ios_workers_.end() == next_io_service_) 80 | { 81 | next_io_service_ = ios_workers_.begin(); 82 | } 83 | 84 | return current->get_io_service(); 85 | } 86 | 87 | private: 88 | std::list ios_workers_; 89 | iterator next_io_service_; 90 | }; 91 | } } -------------------------------------------------------------------------------- /rest_rpc/base/log.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace timax 5 | { 6 | class log 7 | { 8 | public: 9 | static log& get() 10 | { 11 | static log _log; 12 | return _log; 13 | } 14 | 15 | bool init(const std::string& file_name) 16 | { 17 | try 18 | { 19 | log_ = spdlog::rotating_logger_mt("logger", file_name, 1024 * 1024 * 50, 2); 20 | console_log_ = spdlog::stdout_logger_mt("console"); 21 | } 22 | catch (std::exception&) 23 | { 24 | return false; 25 | } 26 | catch (...) 27 | { 28 | return false; 29 | } 30 | 31 | return true; 32 | } 33 | 34 | std::shared_ptr get_log() 35 | { 36 | return log_; 37 | } 38 | 39 | std::shared_ptr get_console_log() 40 | { 41 | return console_log_; 42 | } 43 | private: 44 | log() = default; 45 | log(const log&) = delete; 46 | log(log&&) = delete; 47 | 48 | std::shared_ptr log_; 49 | std::shared_ptr console_log_; 50 | }; 51 | 52 | 53 | template 54 | static inline void SPD_LOG_TRACE(const char* fmt, const Args&... args) 55 | { 56 | log::get().get_log()->trace(fmt, args...); 57 | } 58 | 59 | template 60 | static inline void SPD_LOG_INFO(const char* fmt, const Args&... args) 61 | { 62 | log::get().get_log()->info(fmt, args...); 63 | } 64 | 65 | //template 66 | //static inline void SPD_LOG_NOTICE(const char* fmt, const Args&... args) 67 | //{ 68 | // log::get().get_log()->notice(fmt, args...); 69 | //} 70 | 71 | template 72 | static inline void SPD_LOG_WARN(const char* fmt, const Args&... args) 73 | { 74 | log::get().get_log()->warn(fmt, args...); 75 | } 76 | 77 | template 78 | static inline void SPD_LOG_ERROR(const char* fmt, const Args&... args) 79 | { 80 | log::get().get_log()->error(fmt, args...); 81 | } 82 | 83 | template 84 | static inline void SPD_LOG_CRITICAL(const char* fmt, const Args&... args) 85 | { 86 | log::get().get_log()->critical(fmt, args...); 87 | } 88 | 89 | //template 90 | //static inline void SPD_LOG_ALERT(const char* fmt, const Args&... args) 91 | //{ 92 | // log::get().get_log()->alert(fmt, args...); 93 | //} 94 | 95 | //template 96 | //static inline void SPD_LOG_EMERG(const char* fmt, const Args&... args) 97 | //{ 98 | // log::get().get_log()->emerg(fmt, args...); 99 | //} 100 | 101 | template 102 | static inline void SPD_LOG_DEBUG(const char* fmt, const Args&... args) 103 | { 104 | #ifdef NDEBUG 105 | 106 | #else 107 | log::get().get_log()->set_level(spdlog::level::debug); 108 | log::get().get_log()->debug(fmt, args...); 109 | 110 | log::get().get_console_log()->set_level(spdlog::level::debug); 111 | log::get().get_console_log()->debug(fmt, args...); 112 | #endif 113 | } 114 | } -------------------------------------------------------------------------------- /rest_rpc/base/utils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace timax{ namespace rpc 4 | { 5 | inline bool retry(const std::function& func, size_t max_attempts, size_t retry_interval = 0) 6 | { 7 | for (size_t i = 0; i < max_attempts; i++) 8 | { 9 | if (func()) 10 | return true; 11 | 12 | if (retry_interval > 0) 13 | std::this_thread::sleep_for(std::chrono::milliseconds(retry_interval)); 14 | } 15 | 16 | return false; 17 | } 18 | 19 | inline std::vector get_tcp_endpoints(std::string const& address_port_string_list) 20 | { 21 | std::vector address_port_list; 22 | boost::split(address_port_list, address_port_string_list, boost::is_any_of(" ,|")); 23 | std::vector tcp_endpoints; 24 | for (auto const& address_port : address_port_list) 25 | { 26 | auto pos = address_port.rfind(':'); 27 | if (std::string::npos != pos) 28 | { 29 | tcp_endpoints.emplace_back( 30 | boost::asio::ip::address::from_string(address_port.substr(0, pos)), 31 | boost::lexical_cast(address_port.substr(pos + 1)) 32 | ); 33 | } 34 | } 35 | 36 | return tcp_endpoints; 37 | } 38 | 39 | inline static tcp::endpoint get_tcp_endpoint(std::string const& address, uint16_t port) 40 | { 41 | return{ boost::asio::ip::address::from_string(address), port }; 42 | } 43 | 44 | inline auto get_topic_and_data(char const* data, size_t size) -> std::tuple 45 | { 46 | std::string topic = data; 47 | if (topic.empty()) 48 | throw exception{ error_code::FAIL, "Miss topic!" }; 49 | 50 | auto topic_length = topic.size() + 1; 51 | if(size <= topic_length) 52 | throw exception{ error_code::FAIL, "Pub protocol error!" }; 53 | 54 | data += topic_length; 55 | size -= topic_length; 56 | return std::make_tuple(std::move(topic), data, size); 57 | } 58 | } } 59 | -------------------------------------------------------------------------------- /rest_rpc/client.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "forward.hpp" 4 | #include "client/protocol.hpp" 5 | #include "client/async_client.hpp" 6 | #include "client/sync_client.hpp" 7 | -------------------------------------------------------------------------------- /rest_rpc/client/async_client.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "detail/async_connection.hpp" 3 | #include "detail/wait_barrier.hpp" 4 | // for rpc in async client 5 | #include "detail/async_rpc_context.hpp" 6 | #include "detail/async_rpc_channel.hpp" 7 | #include "detail/async_rpc_channel_impl.hpp" 8 | // for rpc in async client 9 | #include "detail/async_sub_channel.hpp" 10 | #include "detail/async_client_private.hpp" 11 | 12 | namespace timax { namespace rpc 13 | { 14 | template 15 | class async_client 16 | { 17 | public: 18 | using codec_policy = CodecPolicy; 19 | using async_client_private_t = async_client_private; 20 | using work_ptr = std::unique_ptr; 21 | 22 | template 23 | using rpc_task_alias = typed_rpc_task; 24 | 25 | public: 26 | async_client() 27 | : ios_() 28 | , ios_work_(std::make_unique(ios_)) 29 | , ios_run_thread_(boost::bind(&io_service_t::run, &ios_)) 30 | , client_private_(ios_) 31 | { 32 | } 33 | 34 | ~async_client() 35 | { 36 | ios_work_.reset(); 37 | if (!ios_.stopped()) 38 | ios_.stop(); 39 | if (ios_run_thread_.joinable()) 40 | ios_run_thread_.join(); 41 | } 42 | 43 | template 44 | auto call(tcp::endpoint endpoint, Protocol const& protocol, Args&& ... args) 45 | { 46 | static_assert(is_rpc_protocol::value, "Illegal protocol for rpc call!"); 47 | static_assert(is_argument_match::value, "Arguments` types don`t match the protocol!"); 48 | using result_type = typename Protocol::result_type; 49 | using rpc_task_t = rpc_task_alias; 50 | auto ctx = client_private_.make_rpc_context(endpoint, protocol, std::forward(args)...); 51 | return rpc_task_t{ client_private_, ctx }; 52 | } 53 | 54 | template 55 | auto pub(tcp::endpoint endpoint, Protocol const& protocol, Args&& ... args) 56 | { 57 | static_assert(is_forward_protocol::value, "Illegal protocol for publication!"); 58 | using rpc_task_t = rpc_task_alias; 59 | auto ctx = client_private_.make_pub_context(endpoint, protocol, std::forward(args)...); 60 | return rpc_task_t{ client_private_, ctx }; 61 | } 62 | 63 | template 64 | void sub(tcp::endpoint const& endpoint, Protocol const& protocol, Handlers&& ... handlers) 65 | { 66 | static_assert(is_forward_protocol::value, "Illegal protocol for subscription!"); 67 | client_private_.sub(endpoint, protocol, std::forward(handlers)...); 68 | } 69 | 70 | private: 71 | io_service_t ios_; 72 | work_ptr ios_work_; 73 | std::thread ios_run_thread_; 74 | async_client_private_t client_private_; 75 | }; 76 | } } 77 | -------------------------------------------------------------------------------- /rest_rpc/client/detail/async_client_private.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace timax { namespace rpc 4 | { 5 | template 6 | inline auto make_rpc_context(io_service_t& ios, tcp::endpoint const& endpoint, 7 | uint64_t name, typename CodecPolicy::buffer_type&& buffer) 8 | { 9 | using context_t = rpc_context; 10 | return std::make_shared(ios, endpoint, name, std::move(buffer)); 11 | } 12 | 13 | template 14 | inline auto make_rpc_context(io_service_t& ios, tcp::endpoint const& endpoint, 15 | uint64_t name, std::string const& topic, typename CodecPolicy::buffer_type&& buffer) 16 | { 17 | using context_t = rpc_context; 18 | return std::make_shared(ios, endpoint, name, topic, std::move(buffer)); 19 | } 20 | 21 | template 22 | inline auto make_rpc_context(io_service_t& ios, tcp::endpoint const& endpoint, 23 | CodecPolicy const& cp, Protocol const& protocol, Args&& ... args) 24 | { 25 | auto buffer = protocol.pack_args(cp, std::forward(args)...); 26 | return make_rpc_context(ios, endpoint, protocol.name(), std::move(buffer)); 27 | } 28 | 29 | template 30 | inline auto make_rpc_context(io_service_t& ios, tcp::endpoint const& endpoint, CodecPolicy const& cp, 31 | uint64_t hash, Protocol const& protocol, Args&& ... args) 32 | { 33 | auto buffer = protocol.pack_args(cp, std::forward(args)...); 34 | return make_rpc_context(ios, endpoint, hash, protocol.topic(), std::move(buffer)); 35 | } 36 | 37 | template 38 | class async_client_private 39 | { 40 | public: 41 | using codec_policy = CodecPolicy; 42 | using context_t = rpc_context; 43 | using context_ptr = std::shared_ptr; 44 | using rpc_manager_t = rpc_manager; 45 | using sub_manager_t = sub_manager; 46 | using hash_engine = hash_tmpl, uint64_t>; 47 | 48 | public: 49 | async_client_private(io_service_t& ios) 50 | : ios_(ios) 51 | , rpc_manager_(ios) 52 | , sub_manager_(ios) 53 | , pub_hash_(hash_(PUB)) 54 | { 55 | } 56 | 57 | io_service_t& get_io_service() 58 | { 59 | return ios_; 60 | } 61 | 62 | rpc_manager_t& get_rpc_manager() 63 | { 64 | return rpc_manager_; 65 | } 66 | 67 | sub_manager_t& get_sub_manager() 68 | { 69 | return sub_manager_; 70 | } 71 | 72 | codec_policy get_codec_policy() const 73 | { 74 | return {}; 75 | } 76 | 77 | void call(context_ptr& context) 78 | { 79 | rpc_manager_.call(context); 80 | } 81 | 82 | void cancel(context_ptr const& context) 83 | { 84 | rpc_manager_.cancel(context); 85 | } 86 | 87 | template 88 | auto make_rpc_context(tcp::endpoint const& endpoint, Protocol const& protocol, Args&& ... args) 89 | { 90 | codec_policy cp{}; 91 | return rpc::make_rpc_context(ios_, endpoint, cp, protocol, std::forward(args)...); 92 | } 93 | 94 | template 95 | auto make_pub_context(tcp::endpoint const& endpoint, Protocol const& protocol, Args&& ... args) 96 | { 97 | codec_policy cp{}; 98 | return rpc::make_rpc_context(ios_, endpoint, cp, pub_hash_, protocol, std::forward(args)...); 99 | } 100 | 101 | template 102 | void sub(tcp::endpoint const& endpoint, Protocol const& protocol, Func&& func) 103 | { 104 | sub_manager_.sub(endpoint, protocol, std::forward(func)); 105 | } 106 | 107 | template 108 | void sub(tcp::endpoint const& endpoint, Protocol const& protocol, Func&& func, EFunc&& efunc) 109 | { 110 | sub_manager_.sub(endpoint, protocol, std::forward(func), std::forward(efunc)); 111 | } 112 | 113 | uint64_t hash(std::string const& topic) const 114 | { 115 | return hash_(topic); 116 | } 117 | 118 | private: 119 | io_service_t& ios_; 120 | rpc_manager_t rpc_manager_; 121 | sub_manager_t sub_manager_; 122 | hash_engine hash_; 123 | uint64_t pub_hash_; 124 | }; 125 | 126 | template 127 | class rpc_task 128 | { 129 | public: 130 | using codec_policy = CodecPolicy; 131 | using client_private_t = async_client_private; 132 | using context_ptr = typename client_private_t::context_ptr; 133 | 134 | protected: 135 | rpc_task(client_private_t& client, context_ptr& ctx) 136 | : client_(client) 137 | , ctx_(ctx) 138 | , dismiss_(false) 139 | {} 140 | 141 | ~rpc_task() 142 | { 143 | do_call_managed(); 144 | } 145 | 146 | rpc_task(rpc_task&& other) 147 | : client_(other.client_) 148 | , ctx_(std::move(other.ctx_)) 149 | , dismiss_(other.dismiss_) 150 | { 151 | other.dismiss_ = true; 152 | } 153 | 154 | rpc_task(rpc_task const& other) = default; 155 | 156 | void do_call_managed() 157 | { 158 | if (!dismiss_) 159 | { 160 | client_.call(ctx_); 161 | } 162 | } 163 | 164 | void do_call_and_wait() 165 | { 166 | if (!dismiss_) 167 | { 168 | dismiss_ = true; 169 | ctx_->create_barrier(); 170 | client_.call(ctx_); 171 | ctx_->wait(); 172 | } 173 | } 174 | 175 | public: 176 | void cancel() 177 | { 178 | this->client_.cancel(ctx_); 179 | } 180 | 181 | protected: 182 | client_private_t& client_; 183 | context_ptr ctx_; 184 | bool dismiss_; 185 | }; 186 | 187 | template 188 | class typed_rpc_task : public rpc_task 189 | { 190 | public: 191 | using codec_policy = CodecPolicy; 192 | using base_type = rpc_task; 193 | using result_type = Ret; 194 | using client_private_t = typename base_type::client_private_t; 195 | using context_ptr = typename base_type::context_ptr; 196 | 197 | public: 198 | typed_rpc_task(client_private_t& client, context_ptr& ctx) 199 | : base_type(client, ctx) 200 | { 201 | } 202 | 203 | typed_rpc_task(typed_rpc_task const&) = default; 204 | 205 | template 206 | typed_rpc_task&& on_ok(F&& f) && 207 | { 208 | if (nullptr == result_) 209 | { 210 | result_ = std::make_shared(); 211 | } 212 | 213 | this->ctx_->on_ok = [func = std::forward(f), r = result_](char const* data, size_t size) 214 | { 215 | codec_policy codec{}; 216 | *r = codec.template unpack(data, size); 217 | func(*r); 218 | }; 219 | 220 | return std::move(*this); 221 | } 222 | 223 | template 224 | typed_rpc_task&& on_error(F&& f) && 225 | { 226 | this->ctx_->on_error = std::forward(f); 227 | return std::move(*this); 228 | } 229 | 230 | typed_rpc_task&& timeout(duration_t const& t) && 231 | { 232 | this->ctx_->timeout = t; 233 | return std::move(*this); 234 | } 235 | 236 | void wait(duration_t const& duration = duration_t::max()) & 237 | { 238 | if (!this->dismiss_) 239 | { 240 | if (nullptr == result_) 241 | { 242 | result_ = std::make_shared(); 243 | } 244 | 245 | this->ctx_->on_ok = [r = result_](char const* data, size_t size) 246 | { 247 | codec_policy codec{}; 248 | *r = codec.template unpack(data, size); 249 | }; 250 | this->ctx_->on_error = nullptr; 251 | this->ctx_->timeout = duration; 252 | } 253 | this->do_call_and_wait(); 254 | } 255 | 256 | result_type const& get(duration_t const& duration = duration_t::max()) & 257 | { 258 | wait(duration); 259 | return *result_; 260 | } 261 | 262 | private: 263 | std::shared_ptr result_; 264 | }; 265 | 266 | template 267 | class typed_rpc_task : public rpc_task 268 | { 269 | public: 270 | using codec_policy = CodecPolicy; 271 | using base_type = rpc_task; 272 | using result_type = void; 273 | using client_private_t = typename base_type::client_private_t; 274 | using context_ptr = typename base_type::context_ptr; 275 | 276 | public: 277 | typed_rpc_task(client_private_t& client, context_ptr& ctx) 278 | : base_type(client, ctx) 279 | { 280 | } 281 | 282 | typed_rpc_task(typed_rpc_task const&) = default; 283 | 284 | template 285 | typed_rpc_task&& on_ok(F&& f) && 286 | { 287 | this->ctx_->on_ok = [func = std::forward(f)](char const* data, size_t size) { func(); }; 288 | return std::move(*this); 289 | } 290 | 291 | template 292 | typed_rpc_task&& on_error(F&& f) && 293 | { 294 | this->ctx_->on_error = std::forward(f); 295 | return std::move(*this); 296 | } 297 | 298 | typed_rpc_task&& timeout(duration_t const& t) && 299 | { 300 | this->ctx_->timeout = t; 301 | return std::move(*this); 302 | } 303 | 304 | void wait(duration_t const& duration = duration_t::max()) & 305 | { 306 | if (!this->dismiss_) 307 | { 308 | this->ctx_->timeout = duration; 309 | this->ctx_->on_ok = nullptr; 310 | this->ctx_->on_error = nullptr; 311 | } 312 | this->do_call_and_wait(); 313 | } 314 | }; 315 | } } -------------------------------------------------------------------------------- /rest_rpc/client/detail/async_connection.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace timax{ namespace rpc 4 | { 5 | class async_connection 6 | { 7 | public: 8 | async_connection( 9 | io_service_t& ios, 10 | tcp::endpoint const& endpoint, 11 | size_t max_retry_count = 0) 12 | : socket_(ios) 13 | , endpoint_(endpoint) 14 | , max_retry_count_(max_retry_count) 15 | , retry_count_(0) 16 | { 17 | } 18 | 19 | void start( 20 | std::function&& on_success, 21 | std::function&& on_error) 22 | { 23 | on_success_ = std::move(on_success); 24 | on_error_ = std::move(on_error); 25 | start_connect(); 26 | } 27 | 28 | tcp::socket& socket() 29 | { 30 | return socket_; 31 | } 32 | 33 | tcp::endpoint const& endpoint() const 34 | { 35 | return endpoint_; 36 | } 37 | 38 | private: 39 | void start_connect() 40 | { 41 | socket_.async_connect(endpoint_, boost::bind(&async_connection::handle_connection, this, asio_error)); 42 | } 43 | 44 | void handle_connection(const boost::system::error_code& error) 45 | { 46 | if (!error) 47 | { 48 | if (on_success_) 49 | on_success_(); 50 | } 51 | else if (++retry_count_ < max_retry_count_ || 0 == max_retry_count_) 52 | { 53 | start_connect(); 54 | } 55 | else 56 | { 57 | if (on_error_) 58 | on_error_(); 59 | } 60 | } 61 | 62 | private: 63 | tcp::socket socket_; 64 | tcp::endpoint endpoint_; 65 | size_t const max_retry_count_; 66 | size_t retry_count_; 67 | std::function on_success_; 68 | std::function on_error_; 69 | }; 70 | } } -------------------------------------------------------------------------------- /rest_rpc/client/detail/async_rpc_channel.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace timax { namespace rpc 4 | { 5 | template 6 | class rpc_manager; 7 | 8 | template 9 | class rpc_channel : public std::enable_shared_from_this> 10 | { 11 | enum class status_t 12 | { 13 | stopped, 14 | ready, 15 | running, 16 | }; 17 | 18 | public: 19 | using codec_policy = CodecPolicy; 20 | using rpc_manager_t = rpc_manager; 21 | using rpc_call_container_t = rpc_call_container; 22 | using context_t = typename rpc_call_container_t::context_t; 23 | using context_ptr = typename rpc_call_container_t::context_ptr; 24 | using call_list_t = typename rpc_call_container_t::call_list_t; 25 | using call_map_t = typename rpc_call_container_t::call_map_t; 26 | 27 | public: 28 | inline rpc_channel(rpc_manager_t& mgr, io_service_t& ios, tcp::endpoint const& endpoint); 29 | inline ~rpc_channel(); 30 | inline void start(); 31 | inline void call(context_ptr& ctx); 32 | inline void cancel(context_ptr const& ctx); 33 | 34 | private: 35 | inline void start_rpc_service(); 36 | inline void stop_rpc_service(error_code error); 37 | inline void call_impl(); 38 | inline void call_impl1(); 39 | inline void recv_head(); 40 | inline void recv_body(); 41 | inline void call_complete(context_ptr& ctx); 42 | inline void setup_heartbeat_timer(); 43 | inline void stop_rpc_calls(error_code error); 44 | inline void set_timeout(context_ptr& ctx); 45 | 46 | private: // handlers 47 | inline void handle_send(boost::system::error_code const& error); 48 | inline void handle_recv_head(boost::system::error_code const& error); 49 | inline void handle_recv_body(context_ptr ctx, boost::system::error_code const& error); 50 | inline void handle_recv_body_discard(boost::system::error_code const& error); 51 | inline void handle_heartbeat(boost::system::error_code const& error); 52 | inline void handle_timeout(context_ptr ctx, boost::system::error_code const& error); 53 | 54 | private: 55 | rpc_manager_t& rpc_mgr_; 56 | steady_timer_t hb_timer_; 57 | async_connection connection_; 58 | rpc_call_container_t calls_; 59 | std::atomic status_; 60 | bool is_write_in_progress_; 61 | rep_header head_; 62 | mutable std::mutex mutex_; 63 | call_list_t to_calls_; 64 | std::vector to_discard_message_; 65 | }; 66 | 67 | template 68 | class rpc_manager 69 | { 70 | template 71 | friend class rpc_channel; 72 | 73 | public: 74 | using codec_policy = CodecPolicy; 75 | using rpc_channel_t = rpc_channel; 76 | using session_map_t = std::map>; 77 | using session_ptr = std::shared_ptr; 78 | using context_ptr = typename rpc_channel_t::context_ptr; 79 | using context_t = typename rpc_channel_t::context_t; 80 | 81 | public: 82 | inline explicit rpc_manager(io_service_t& ios); 83 | inline void call(context_ptr& ctx); 84 | inline void cancel(context_ptr const& ctx); 85 | inline io_service_t& get_io_service(); 86 | 87 | private: 88 | inline session_ptr get_session(tcp::endpoint const& endpoint); 89 | inline void remove_session(tcp::endpoint const& endpoint); 90 | 91 | private: 92 | io_service_t& ios_; 93 | session_map_t sessions_; 94 | mutable std::mutex mutex_; 95 | }; 96 | } } -------------------------------------------------------------------------------- /rest_rpc/client/detail/async_rpc_channel_impl.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace timax { namespace rpc 4 | { 5 | template 6 | rpc_channel::rpc_channel(rpc_manager_t& mgr, io_service_t& ios, tcp::endpoint const& endpoint) 7 | : rpc_mgr_(mgr) 8 | , hb_timer_(ios) 9 | , connection_(ios, endpoint) 10 | , status_(status_t::ready) 11 | , is_write_in_progress_(false) 12 | { 13 | } 14 | 15 | template 16 | rpc_channel::~rpc_channel() 17 | { 18 | stop_rpc_service(error_code::CANCEL); 19 | } 20 | 21 | template 22 | void rpc_channel::start() 23 | { 24 | auto self = this->shared_from_this(); 25 | connection_.start( 26 | [this, self] // when successfully connected 27 | { 28 | start_rpc_service(); 29 | }, 30 | [this, self] // when failed to connect 31 | { 32 | stop_rpc_calls(error_code::BADCONNECTION); 33 | }); 34 | } 35 | 36 | template 37 | void rpc_channel::call(context_ptr& ctx) 38 | { 39 | auto status = status_.load(); 40 | if (status_t::stopped == status) 41 | { 42 | ctx->error(error_code::BADCONNECTION, "rpc session already stoppet"); 43 | return; 44 | } 45 | 46 | bool empty = false; 47 | { 48 | lock_t lock{ mutex_ }; 49 | empty = calls_.call_list_empty(); 50 | auto push_success = calls_.push_call(ctx); 51 | lock.unlock(); 52 | if (!push_success) 53 | { 54 | ctx->error(error_code::UNKNOWN); 55 | } 56 | else 57 | { 58 | set_timeout(ctx); 59 | } 60 | } 61 | 62 | if (empty && status_t::running == status) 63 | { 64 | auto self = this->shared_from_this(); 65 | rpc_mgr_.get_io_service().post([self, this] 66 | { 67 | if (to_calls_.empty()) 68 | { 69 | call_impl(); 70 | } 71 | }); 72 | } 73 | } 74 | 75 | template 76 | void rpc_channel::cancel(context_ptr const& ctx) 77 | { 78 | auto self = this->shared_from_this(); 79 | rpc_mgr_.get_io_service().post([self, this, ctx] 80 | { 81 | calls_.get_call_from_map(ctx->head.id); 82 | ctx->error(error_code::CANCEL, "Rpc cancled by client"); 83 | }); 84 | } 85 | 86 | template 87 | void rpc_channel::start_rpc_service() 88 | { 89 | status_ = status_t::running; 90 | call_impl(); 91 | recv_head(); 92 | setup_heartbeat_timer(); 93 | } 94 | 95 | template 96 | void rpc_channel::stop_rpc_service(error_code error) 97 | { 98 | status_ = status_t::stopped; 99 | stop_rpc_calls(error); 100 | } 101 | 102 | template 103 | void rpc_channel::call_impl() 104 | { 105 | lock_t lock{ mutex_ }; 106 | if (!calls_.call_list_empty()) 107 | { 108 | calls_.task_calls_from_list(to_calls_); 109 | lock.unlock(); 110 | call_impl1(); 111 | } 112 | } 113 | 114 | template 115 | void rpc_channel::call_impl1() 116 | { 117 | auto& to_call = to_calls_.front(); 118 | 119 | async_write(connection_.socket(), to_call->get_send_message(), 120 | boost::bind(&rpc_channel::handle_send, this->shared_from_this(), asio_error)); 121 | } 122 | 123 | template 124 | void rpc_channel::recv_head() 125 | { 126 | async_read(connection_.socket(), boost::asio::buffer(&head_, sizeof(head_)), 127 | boost::bind(&rpc_channel::handle_recv_head, this->shared_from_this(), asio_error)); 128 | } 129 | 130 | template 131 | void rpc_channel::recv_body() 132 | { 133 | auto call_id = head_.id; 134 | lock_t locker{ mutex_ }; 135 | auto call_ctx = calls_.get_call_from_map(call_id); 136 | locker.unlock(); 137 | 138 | if (0 == head_.len) 139 | { 140 | call_complete(call_ctx); 141 | } 142 | else if (head_.len > MAX_BUF_LEN) 143 | { 144 | stop_rpc_service(error_code::UNKNOWN); 145 | connection_.socket().close(); 146 | } 147 | else 148 | { 149 | if (nullptr == call_ctx) 150 | { 151 | to_discard_message_.resize(head_.len); 152 | async_read(connection_.socket(), boost::asio::buffer(to_discard_message_), 153 | boost::bind(&rpc_channel::handle_recv_body_discard, this->shared_from_this(), asio_error)); 154 | } 155 | else 156 | { 157 | async_read(connection_.socket(), call_ctx->get_recv_message(head_.len), boost::bind(&rpc_channel::handle_recv_body, 158 | this->shared_from_this(), call_ctx, asio_error)); 159 | } 160 | } 161 | } 162 | 163 | template 164 | void rpc_channel::call_complete(context_ptr& ctx) 165 | { 166 | if (nullptr != ctx) 167 | { 168 | auto rcode = static_cast(head_.code); 169 | if (result_code::OK == rcode) 170 | { 171 | ctx->ok(); 172 | } 173 | else 174 | { 175 | ctx->error(); 176 | } 177 | } 178 | 179 | recv_head(); 180 | } 181 | 182 | template 183 | void rpc_channel::setup_heartbeat_timer() 184 | { 185 | using namespace std::chrono_literals; 186 | 187 | hb_timer_.expires_from_now(15s); 188 | hb_timer_.async_wait(boost::bind(&rpc_channel::handle_heartbeat, this->shared_from_this(), asio_error)); 189 | } 190 | 191 | template 192 | void rpc_channel::stop_rpc_calls(error_code error) 193 | { 194 | call_map_t to_responses; 195 | { 196 | lock_t locker{ mutex_ }; 197 | calls_.task_calls_from_map(to_responses); 198 | } 199 | for (auto& elem : to_responses) 200 | { 201 | auto ctx = elem.second; 202 | ctx->error(error); 203 | } 204 | 205 | rpc_mgr_.remove_session(connection_.endpoint()); 206 | } 207 | 208 | template 209 | void rpc_channel::set_timeout(context_ptr& ctx) 210 | { 211 | if (duration_t::max() != ctx->timeout) 212 | { 213 | ctx->timer.expires_from_now(ctx->timeout); 214 | ctx->timer.async_wait(boost::bind(&rpc_channel::handle_timeout, 215 | this->shared_from_this(), ctx, asio_error)); 216 | } 217 | } 218 | 219 | template 220 | void rpc_channel::handle_send(boost::system::error_code const& error) 221 | { 222 | if (!connection_.socket().is_open()) 223 | return; 224 | 225 | to_calls_.pop_front(); 226 | 227 | if (error) 228 | { 229 | stop_rpc_service(error_code::BADCONNECTION); 230 | } 231 | else if(to_calls_.empty()) 232 | { 233 | call_impl(); 234 | } 235 | else 236 | { 237 | call_impl1(); 238 | } 239 | } 240 | 241 | template 242 | void rpc_channel::handle_recv_head(boost::system::error_code const& error) 243 | { 244 | if (!connection_.socket().is_open()) 245 | return; 246 | 247 | if (!error) 248 | { 249 | recv_body(); 250 | } 251 | else 252 | { 253 | // TODO log 254 | stop_rpc_service(error_code::BADCONNECTION); 255 | } 256 | } 257 | 258 | template 259 | void rpc_channel::handle_recv_body(context_ptr ctx, boost::system::error_code const& error) 260 | { 261 | if (!connection_.socket().is_open()) 262 | return; 263 | 264 | if (!error) 265 | { 266 | call_complete(ctx); 267 | } 268 | else 269 | { 270 | stop_rpc_service(error_code::BADCONNECTION); 271 | } 272 | } 273 | 274 | template 275 | void rpc_channel::handle_recv_body_discard(boost::system::error_code const& error) 276 | { 277 | if (!connection_.socket().is_open()) 278 | return; 279 | 280 | if (!error) 281 | { 282 | recv_head(); 283 | } 284 | else 285 | { 286 | stop_rpc_service(error_code::BADCONNECTION); 287 | } 288 | } 289 | 290 | template 291 | void rpc_channel::handle_heartbeat(boost::system::error_code const& error) 292 | { 293 | if (!connection_.socket().is_open()) 294 | return; 295 | 296 | if (!error) 297 | { 298 | auto ctx = std::make_shared(rpc_mgr_.get_io_service()); 299 | call(ctx); 300 | setup_heartbeat_timer(); 301 | } 302 | } 303 | 304 | template 305 | void rpc_channel::handle_timeout(context_ptr ctx, boost::system::error_code const& error) 306 | { 307 | if (!error && !ctx->is_over) 308 | { 309 | ctx->error(error_code::TIMEOUT, std::move(error.message())); 310 | lock_t lock{ mutex_ }; 311 | calls_.remove_call_from_map(ctx->head.id); 312 | } 313 | } 314 | 315 | template 316 | rpc_manager::rpc_manager(io_service_t& ios) 317 | : ios_(ios) 318 | {} 319 | 320 | template 321 | void rpc_manager::call(context_ptr& ctx) 322 | { 323 | get_session(ctx->endpoint)->call(ctx); 324 | } 325 | 326 | template 327 | void rpc_manager::cancel(context_ptr const& ctx) 328 | { 329 | get_session(ctx->endpoint)->cancel(ctx); 330 | } 331 | 332 | template 333 | io_service_t& rpc_manager::get_io_service() 334 | { 335 | return ios_; 336 | } 337 | 338 | template 339 | typename rpc_manager::session_ptr rpc_manager::get_session(tcp::endpoint const& endpoint) 340 | { 341 | lock_t locker{ mutex_ }; 342 | auto itr = sessions_.find(endpoint); 343 | if (itr == sessions_.end()) 344 | { 345 | auto session = std::make_shared(*this, ios_, endpoint); 346 | session->start(); 347 | sessions_.emplace(endpoint, session); 348 | return session; 349 | } 350 | else 351 | { 352 | return itr->second; 353 | } 354 | } 355 | 356 | template 357 | void rpc_manager::remove_session(tcp::endpoint const& endpoint) 358 | { 359 | lock_t locker{ mutex_ }; 360 | auto itr = sessions_.find(endpoint); 361 | if (itr != sessions_.end()) 362 | sessions_.erase(itr); 363 | } 364 | } } -------------------------------------------------------------------------------- /rest_rpc/client/detail/async_rpc_context.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace timax { namespace rpc 4 | { 5 | template 6 | struct rpc_context 7 | { 8 | using codec_policy = CodecPolicy; 9 | using buffer_t = typename codec_policy::buffer_type; 10 | using success_function_t = std::function; 11 | using on_error_function_t = std::function; 12 | using asio_buffers = std::vector; 13 | 14 | rpc_context( 15 | io_service_t& ios, 16 | tcp::endpoint const& endpoint, 17 | uint64_t hash, 18 | buffer_t&& request) 19 | : timer(ios) 20 | , timeout(duration_t::max()) 21 | , endpoint(endpoint) 22 | , req(std::move(request)) 23 | , head(0, 0, static_cast(req.size()), hash) 24 | , buffer({ 25 | boost::asio::buffer(&head, sizeof(head)), 26 | boost::asio::buffer(req) }) 27 | , is_over(false) 28 | { 29 | } 30 | 31 | rpc_context( 32 | io_service_t& ios, 33 | tcp::endpoint const& endpoint, 34 | uint64_t hash, 35 | std::string const& t, 36 | buffer_t&& request) 37 | : timer(ios) 38 | , timeout(duration_t::max()) 39 | , endpoint(endpoint) 40 | , req(std::move(request)) 41 | , topic(t) 42 | , head(0, 0, static_cast(req.size() + topic.length() + 1), hash) 43 | , buffer({ 44 | boost::asio::buffer(&head, sizeof(head)), 45 | boost::asio::buffer(topic.c_str(), topic.length() + 1), 46 | boost::asio::buffer(req) }) 47 | , is_over(false) 48 | { 49 | } 50 | 51 | explicit rpc_context(io_service_t& ios) 52 | : timer(ios) 53 | , timeout(duration_t::max()) 54 | , buffer({ boost::asio::buffer(&head, sizeof(head)) }) 55 | , is_over(false) 56 | { 57 | } 58 | 59 | req_header& get_head() 60 | { 61 | return head; 62 | } 63 | 64 | decltype(auto) get_send_message() const 65 | { 66 | return buffer; 67 | } 68 | 69 | auto get_recv_message(size_t size) 70 | { 71 | rep.resize(size); 72 | return boost::asio::buffer(rep); 73 | } 74 | 75 | void ok() 76 | { 77 | if (!is_over) 78 | { 79 | is_over = true; 80 | 81 | if (on_ok) 82 | on_ok(rep.data(), rep.size()); 83 | 84 | if (nullptr != barrier) 85 | barrier->notify(); 86 | } 87 | 88 | } 89 | 90 | void error() 91 | { 92 | codec_policy cp{}; 93 | auto recv_error = cp.template unpack(rep.data(), rep.size()); 94 | err = std::move(recv_error); 95 | 96 | post_error(); 97 | } 98 | 99 | void error(error_code errcode, std::string const& message = "") 100 | { 101 | err.set_code(errcode); 102 | if (!message.empty()) 103 | { 104 | err.set_message(message); 105 | } 106 | 107 | post_error(); 108 | } 109 | 110 | void post_error() 111 | { 112 | if (!is_over) 113 | { 114 | is_over = true; 115 | 116 | if (on_error) 117 | on_error(err); 118 | 119 | if (nullptr != barrier) 120 | barrier->notify(); 121 | } 122 | 123 | } 124 | 125 | void create_barrier() 126 | { 127 | if (nullptr == barrier) 128 | barrier.reset(new result_barrier{}); 129 | } 130 | 131 | void wait() 132 | { 133 | if(nullptr != barrier) 134 | barrier->wait(); 135 | 136 | if (err) 137 | throw err; 138 | } 139 | 140 | steady_timer_t timer; 141 | steady_timer_t::duration timeout; 142 | tcp::endpoint endpoint; 143 | buffer_t req; // request buffer 144 | std::vector rep; // response buffer 145 | std::string topic; 146 | req_header head; 147 | asio_buffers buffer; 148 | exception err; 149 | success_function_t on_ok; 150 | on_error_function_t on_error; 151 | bool is_over; 152 | std::unique_ptr barrier; 153 | }; 154 | 155 | template 156 | class rpc_call_container 157 | { 158 | public: 159 | using codec_policy = CodecPolicy; 160 | using context_t = rpc_context; 161 | using context_ptr = std::shared_ptr; 162 | using call_map_t = std::map; 163 | using call_list_t = std::list; 164 | 165 | public: 166 | explicit rpc_call_container(size_t max_size = MAX_QUEUE_SIZE) 167 | : call_id_(0) 168 | , max_size_(max_size) 169 | { 170 | } 171 | 172 | bool push_call(context_ptr& ctx) 173 | { 174 | if (call_map_.size() >= max_size_) 175 | return false; 176 | 177 | push_call_response(ctx); 178 | call_list_.push_back(ctx); 179 | return true; 180 | } 181 | 182 | void push_call_response(context_ptr& ctx) 183 | { 184 | if (ctx->req.size() > 0) 185 | { 186 | auto call_id = ++call_id_; 187 | ctx->get_head().id = call_id; 188 | call_map_.emplace(call_id, ctx); 189 | } 190 | } 191 | 192 | void task_calls_from_list(call_list_t& to_calls) 193 | { 194 | to_calls = std::move(call_list_); 195 | } 196 | 197 | bool call_list_empty() const 198 | { 199 | return call_list_.empty(); 200 | } 201 | 202 | context_ptr get_call_from_map(uint32_t call_id) 203 | { 204 | auto itr = call_map_.find(call_id); 205 | if (call_map_.end() != itr) 206 | { 207 | context_ptr ctx = itr->second; 208 | call_map_.erase(itr); 209 | return ctx; 210 | } 211 | return nullptr; 212 | } 213 | 214 | void remove_call_from_map(uint32_t call_id) 215 | { 216 | auto itr = call_map_.find(call_id); 217 | if(call_map_.end() != itr) 218 | call_map_.erase(itr); 219 | } 220 | 221 | void task_calls_from_map(call_map_t& call_map) 222 | { 223 | call_map = std::move(call_map_); 224 | } 225 | 226 | size_t get_call_list_size() const 227 | { 228 | return call_list_.size(); 229 | } 230 | 231 | size_t get_call_map_size() const 232 | { 233 | return call_map_.size(); 234 | } 235 | 236 | private: 237 | call_map_t call_map_; 238 | call_list_t call_list_; 239 | uint32_t call_id_; 240 | size_t max_size_; 241 | }; 242 | } } -------------------------------------------------------------------------------- /rest_rpc/client/detail/wait_barrier.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace timax { namespace rpc 4 | { 5 | class result_barrier 6 | { 7 | public: 8 | void wait() 9 | { 10 | using namespace std::chrono_literals; 11 | lock_t locker{ mutex_ }; 12 | cond_var_.wait(locker, [this] { return is_over_; }); 13 | } 14 | 15 | void notify() 16 | { 17 | lock_t lock{ mutex_ }; 18 | is_over_ = true; 19 | cond_var_.notify_one(); 20 | } 21 | 22 | protected: 23 | bool is_over_ = false; 24 | std::mutex mutex_; 25 | std::condition_variable cond_var_; 26 | }; 27 | } } -------------------------------------------------------------------------------- /rest_rpc/client/protocol.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define TIMAX_DEFINE_PROTOCOL(handler, ...) static const ::timax::rpc::rpc_protocol<__VA_ARGS__> handler{ #handler } 6 | #define TIMAX_DEFINE_FORWARD(handler, ...) static const ::timax::rpc::forward_protocol<__VA_ARGS__> handler{ #handler } 7 | 8 | namespace timax { namespace rpc 9 | { 10 | template 11 | struct is_argument_match 12 | { 13 | private: 14 | template 15 | static std::false_type test(...); 16 | 17 | template ()(std::declval()...))> 19 | static std::true_type test(int); 20 | 21 | using result_type = decltype(test(0)); 22 | public: 23 | static constexpr bool value = result_type::value; 24 | }; 25 | 26 | template 27 | struct rpc_protocol; 28 | 29 | template 30 | struct rpc_protocol_base; 31 | 32 | template 33 | struct rpc_protocol_base 34 | { 35 | using result_type = typename boost::function_traits::result_type; 36 | using signature_type = Ret(Args...); 37 | using hash_engine = hash_tmpl, uint64_t>; 38 | 39 | explicit rpc_protocol_base(std::string const& name) 40 | : name_(static_cast(hash_engine{}(name))) 41 | { 42 | } 43 | 44 | uint64_t name() const noexcept 45 | { 46 | return name_; 47 | } 48 | 49 | template 50 | auto pack_args(CodecPolicy const& cp, TArgs&& ... args) const 51 | { 52 | static_assert(is_argument_match::value, "Arguments` types don`t match the protocol!"); 53 | return cp.pack_args(std::forward(static_cast(args))...); 54 | } 55 | 56 | template 57 | auto pack_topic(CodecPolicy const& cp) const 58 | { 59 | return cp.pack_args(name_); 60 | } 61 | 62 | private: 63 | uint64_t name_; 64 | }; 65 | 66 | template 67 | struct rpc_protocol : rpc_protocol_base 68 | { 69 | using base_type = rpc_protocol_base; 70 | using result_type = typename base_type::result_type; 71 | using signature_type = typename base_type::signature_type; 72 | 73 | explicit rpc_protocol(std::string const& name) 74 | : base_type(name) 75 | { 76 | } 77 | 78 | template ::value>> 79 | auto pack_result(CodecPolicy const& cp, result_type&& ret) const 80 | { 81 | return cp.pack(std::forward(ret)); 82 | } 83 | 84 | template ::value>> 85 | result_type unpack(CodecPolicy& cp, char const* data, size_t length) const 86 | { 87 | return cp.template unpack(data, length); 88 | } 89 | }; 90 | 91 | template 92 | struct rpc_protocol : rpc_protocol_base 93 | { 94 | using base_type = rpc_protocol_base; 95 | using result_type = typename base_type::result_type; 96 | using signature_type = typename base_type::signature_type; 97 | 98 | explicit rpc_protocol(std::string name) 99 | : base_type(std::move(name)) 100 | { 101 | } 102 | }; 103 | 104 | template 105 | struct check_forward_protocol 106 | { 107 | static constexpr bool value = false; 108 | }; 109 | 110 | template <> 111 | struct check_forward_protocol<> 112 | { 113 | static constexpr bool value = true; 114 | }; 115 | 116 | template 117 | struct check_forward_protocol 118 | { 119 | static constexpr bool value = !std::is_void::value 120 | && check_forward_protocol::value; 121 | }; 122 | 123 | template 124 | struct forward_protocol 125 | { 126 | static_assert(check_forward_protocol::value, "illegal protocol parameters!"); 127 | using tuple_type = std::tuple>...>; 128 | using hash_engine = hash_tmpl, uint64_t>; 129 | 130 | forward_protocol(std::string const& name) 131 | : topic_name_(name) 132 | , name_(static_cast(hash_engine{}(name))) 133 | { 134 | } 135 | 136 | template 137 | auto pack(CodecPolicy const& cp, TArgs&& ... args) const 138 | { 139 | static_assert(is_argument_match::value, "Arguments` types don`t match the protocol!"); 140 | return cp.pack_args(std::forward(static_cast(args))...); 141 | } 142 | 143 | template 144 | auto pack_args(CodecPolicy const& cp, TArgs&& ... args) const 145 | { 146 | return pack(cp, std::forward(args)...); 147 | } 148 | 149 | template 150 | auto unpack(CodecPolicy& cp, char const* data, size_t size) const 151 | { 152 | return cp.template unpack(data, size); 153 | } 154 | 155 | uint64_t name() const 156 | { 157 | return name_; 158 | } 159 | 160 | std::string const& topic() const noexcept 161 | { 162 | return topic_name_; 163 | } 164 | 165 | template 166 | auto pack_topic(CodecPolicy const& cp) const 167 | { 168 | return cp.pack_args(topic_name_); 169 | } 170 | 171 | private: 172 | std::string topic_name_; 173 | uint64_t name_; 174 | }; 175 | 176 | template <> 177 | struct forward_protocol<> 178 | { 179 | }; 180 | 181 | template 182 | struct is_forward_protocol : std::false_type {}; 183 | 184 | template 185 | struct is_forward_protocol> : std::true_type {}; 186 | 187 | template 188 | struct is_rpc_protocol : std::false_type {}; 189 | 190 | template 191 | struct is_rpc_protocol> : std::true_type {}; 192 | 193 | TIMAX_DEFINE_PROTOCOL(sub_topic, std::string(std::string const&)); 194 | } } 195 | -------------------------------------------------------------------------------- /rest_rpc/client/sync_client.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace timax { namespace rpc 4 | { 5 | template 6 | class sync_client 7 | { 8 | using codec_policy = CodecPolicy; 9 | using async_client_t = async_client; 10 | using async_client_ptr = std::shared_ptr; 11 | 12 | public: 13 | sync_client() 14 | : client_(std::make_shared()) 15 | { 16 | } 17 | 18 | template 19 | auto call(tcp::endpoint const& endpoint, Protocol const& protocol, Args&& ... args) 20 | { 21 | using result_type = typename Protocol::result_type; 22 | return call_impl(std::is_void{}, endpoint, protocol, std::forward(args)...); 23 | } 24 | 25 | private: 26 | template 27 | auto call_impl(std::false_type, tcp::endpoint const& endpoint, Protocol const& protocol, Args&& ... args) 28 | { 29 | using result_type = typename Protocol::result_type; 30 | auto task = client_->call(endpoint, protocol, std::forward(args)...); 31 | return task.get(); 32 | } 33 | 34 | template 35 | auto call_impl(std::true_type, tcp::endpoint const& endpoint, Protocol const& protocol, Args&& ... args) 36 | { 37 | auto task = client_->call(endpoint, protocol, std::forward(args)...); 38 | task.wait(); 39 | } 40 | 41 | private: 42 | async_client_ptr client_; 43 | }; 44 | } } -------------------------------------------------------------------------------- /rest_rpc/codec/codec.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "codec_msgpack.hpp" 4 | #include "codec_json.hpp" 5 | #include "codec_xml.hpp" 6 | 7 | namespace timax { namespace rpc 8 | { 9 | template 10 | struct is_std_tuple : std::false_type {}; 11 | 12 | template 13 | struct is_std_tuple> : std::true_type {}; 14 | 15 | template 16 | auto pack_as_tuple_if_not_impl(std::true_type, CodecPolicy const& cp, Arg&& arg) 17 | { 18 | return cp.pack(std::forward(arg)); 19 | } 20 | 21 | template 22 | auto pack_as_tuple_if_not_impl(std::false_type, CodecPolicy const& cp, Arg&& arg) 23 | { 24 | return cp.pack_args(std::forward(arg)); 25 | } 26 | 27 | template 28 | auto pack_as_tuple_if_not(CodecPolicy const& cp, Arg&& arg) 29 | { 30 | return pack_as_tuple_if_not_impl(is_std_tuple>>{}, 31 | cp, std::forward(arg)); 32 | } 33 | } } -------------------------------------------------------------------------------- /rest_rpc/codec/codec_boost.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace boost { namespace serialization { 4 | /** 5 | * serialization for tuples 6 | */ 7 | template 8 | void serialize(Archive & ar, const std::index_sequence&, std::tuple & t, unsigned int version) 9 | { 10 | bool swallow[] = { (ar & std::get(t), false)... }; 11 | (void*)swallow; 12 | } 13 | 14 | template 15 | void serialize(Archive & ar, std::tuple & t, unsigned int version) 16 | { 17 | serialize(ar, std::make_index_sequence{}, t, version); 18 | } 19 | 20 | template 21 | void serialize_meta(Archive& ar, Tuple& tuple, std::index_sequence) 22 | { 23 | bool swallow[] = { (ar & std::get(tuple).second, false)... }; 24 | (void*)swallow; 25 | } 26 | 27 | template 28 | void serialize_meta(Archive & ar, Tuple& tuple) 29 | { 30 | serialize_meta(ar, tuple, std::make_index_sequence::value>{}); 31 | } 32 | 33 | template 34 | auto serialize(Archive& ar, T& t, unsigned int version) -> std::enable_if_t::value> 35 | { 36 | serialize_meta(ar, t.Meta()); 37 | } 38 | 39 | } // end serialization namespace 40 | } // end boost namespace 41 | 42 | namespace timax { namespace rpc 43 | { 44 | struct boost_codec 45 | { 46 | template 47 | T unpack(char const* data, size_t length) 48 | { 49 | std::stringstream ss; 50 | ss.write(data, length); 51 | boost::archive::text_iarchive ia(ss); 52 | T t; 53 | ia >> t; 54 | return t; 55 | } 56 | 57 | using buffer_type = std::vector; 58 | 59 | template 60 | buffer_type pack_args(Args&& ... args) const 61 | { 62 | auto args_tuple = std::make_tuple(std::forward(args)...); 63 | std::stringstream ss; 64 | boost::archive::text_oarchive oa(ss); 65 | oa << args_tuple; 66 | 67 | return assign(ss); 68 | } 69 | 70 | template 71 | buffer_type pack(T&& t) 72 | { 73 | std::stringstream ss; 74 | boost::archive::text_oarchive oa(ss); 75 | oa << std::forward(t); 76 | 77 | return assign(ss); 78 | } 79 | 80 | std::vector assign(std::stringstream& ss) const 81 | { 82 | std::vector vec; 83 | std::streampos beg = ss.tellg(); 84 | ss.seekg(0, std::ios_base::end); 85 | std::streampos end = ss.tellg(); 86 | ss.seekg(0, std::ios_base::beg); 87 | vec.reserve(end - beg); 88 | 89 | vec.assign(std::istreambuf_iterator(ss), std::istreambuf_iterator()); 90 | return vec; 91 | } 92 | }; 93 | } } 94 | -------------------------------------------------------------------------------- /rest_rpc/codec/codec_json.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace timax { namespace rpc 6 | { 7 | struct json_codec 8 | { 9 | using buffer_type = std::string; 10 | 11 | template 12 | buffer_type pack_args(Args&& ... args) const 13 | { 14 | iguana::string_stream ss; 15 | auto args_tuple = std::make_tuple(std::forward(args)...); 16 | iguana::json::to_json(ss, args_tuple); 17 | return ss.str(); 18 | } 19 | 20 | template 21 | buffer_type pack(T&& t) const 22 | { 23 | iguana::string_stream ss; 24 | iguana::json::to_json(ss, std::forward(t)); 25 | return ss.str(); 26 | } 27 | 28 | template 29 | T unpack(char const* data, size_t length) 30 | { 31 | try 32 | { 33 | T t; 34 | iguana::json::from_json(t, data, length); 35 | return t; 36 | } 37 | catch (...) 38 | { 39 | using namespace std::string_literals; 40 | exception error{ error_code::FAIL, "Args not match!"s }; 41 | throw error; 42 | } 43 | } 44 | }; 45 | } } -------------------------------------------------------------------------------- /rest_rpc/codec/codec_msgpack.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace timax { namespace rpc 6 | { 7 | struct msgpack_codec 8 | { 9 | using buffer_type = std::vector; 10 | 11 | template 12 | buffer_type pack_args(Args&& ... args) const 13 | { 14 | iguana::memory_buffer buffer; 15 | auto args_tuple = std::make_tuple(std::forward(args)...); 16 | iguana::msgpack::to_msgpack(buffer, args_tuple); 17 | return buffer.release(); 18 | } 19 | 20 | template 21 | buffer_type pack(T&& t) const 22 | { 23 | iguana::memory_buffer buffer; 24 | iguana::msgpack::to_msgpack(buffer, std::forward(t)); 25 | return buffer.release(); 26 | } 27 | 28 | template 29 | T unpack(char const* data, size_t length) 30 | { 31 | try 32 | { 33 | T t; 34 | iguana::msgpack::from_msgpack(t, msg_, data, length); 35 | return t; 36 | } 37 | catch (...) 38 | { 39 | using namespace std::string_literals; 40 | exception error{ error_code::FAIL, "Args not match!"s }; 41 | throw error; 42 | } 43 | } 44 | 45 | private: 46 | msgpack::unpacked msg_; 47 | }; 48 | } } 49 | -------------------------------------------------------------------------------- /rest_rpc/codec/codec_xml.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace timax { namespace rpc 6 | { 7 | struct xml_codec 8 | { 9 | using buffer_type = std::string; 10 | 11 | template 12 | buffer_type pack_args(Args&& ... args) const 13 | { 14 | iguana::string_stream ss; 15 | auto args_tuple = std::make_tuple(std::forward(args)...); 16 | iguana::xml::to_xml(ss, args_tuple); 17 | return ss.str(); 18 | } 19 | 20 | template 21 | buffer_type pack(T&& t) const 22 | { 23 | iguana::string_stream ss; 24 | iguana::xml::to_xml(ss, std::forward(t)); 25 | return ss.str(); 26 | } 27 | 28 | template 29 | T unpack(char const* data, size_t length) 30 | { 31 | try 32 | { 33 | T t; 34 | iguana::xml::from_xml(t, data, length); 35 | return t; 36 | } 37 | catch (...) 38 | { 39 | using namespace std::string_literals; 40 | exception error{ error_code::FAIL, "Args not match!"s }; 41 | throw error; 42 | } 43 | } 44 | }; 45 | } } 46 | -------------------------------------------------------------------------------- /rest_rpc/forward.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | // boost libraries 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | // standard libraries 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | namespace timax { namespace rpc 39 | { 40 | using tcp = boost::asio::ip::tcp; 41 | using io_service_t = boost::asio::io_service; 42 | using lock_t = std::unique_lock; 43 | using deadline_timer_t = boost::asio::deadline_timer; 44 | using steady_timer_t = boost::asio::steady_timer; 45 | using duration_t = steady_timer_t::duration; 46 | 47 | class router_base; 48 | class connection; 49 | class exception; 50 | using connection_ptr = std::shared_ptr; 51 | 52 | template 53 | class server; 54 | 55 | static const auto asio_error = boost::asio::placeholders::error; 56 | } } 57 | 58 | // common headers 59 | #include 60 | #include 61 | #include 62 | #include 63 | #include 64 | #include 65 | #include 66 | #include 67 | #include 68 | -------------------------------------------------------------------------------- /rest_rpc/rpc.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "server.hpp" 4 | #include "client.hpp" -------------------------------------------------------------------------------- /rest_rpc/server.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "forward.hpp" 4 | #include "server/context.hpp" 5 | #include "server/ios_wrapper.hpp" 6 | #include "server/connection.hpp" 7 | #include "server/invoker_traits.hpp" 8 | #include "server/router.hpp" 9 | #include "server/connection_impl.hpp" 10 | #include "server/ios_wrapper_impl.hpp" 11 | #include "server/server.hpp" -------------------------------------------------------------------------------- /rest_rpc/server/connection.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace timax { namespace rpc 4 | { 5 | class ios_wrapper; 6 | using blob_t = iguana::blob_t; 7 | 8 | class connection : public std::enable_shared_from_this 9 | { 10 | public: 11 | template friend class server; 12 | template friend class router; 13 | template friend struct invoker_traits; 14 | friend class ios_wrapper; 15 | 16 | using connection_ptr = std::shared_ptr; 17 | using context_ptr = std::shared_ptr; 18 | 19 | public: 20 | connection(io_service_t& ios, router_base& router, duration_t time_out); 21 | void close(); 22 | io_service_t& get_io_service(); 23 | 24 | protected: 25 | tcp::socket& socket(); 26 | void start(); 27 | void response(context_ptr& ctx); 28 | void on_error(boost::system::error_code const& error); 29 | blob_t get_read_buffer() const; 30 | req_header const& get_read_header() const; 31 | 32 | private: 33 | void set_no_delay(); 34 | void expires_timer(); 35 | void cancel_timer(); 36 | void read_head(); 37 | void read_body(); 38 | 39 | private: 40 | void handle_read_head(boost::system::error_code const& error); 41 | void handle_read_body(boost::system::error_code const& error); 42 | void handle_time_out(boost::system::error_code const& error); 43 | 44 | private: 45 | ios_wrapper ios_wrapper_; 46 | router_base& router_; 47 | tcp::socket socket_; 48 | req_header head_; 49 | std::vector read_buffer_; 50 | steady_timer_t timer_; 51 | duration_t time_out_; 52 | }; 53 | } } -------------------------------------------------------------------------------- /rest_rpc/server/connection_impl.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace timax { namespace rpc 4 | { 5 | connection::connection(io_service_t& ios, router_base& router, duration_t time_out) 6 | : ios_wrapper_(ios) 7 | , router_(router) 8 | , socket_(ios) 9 | , read_buffer_(PAGE_SIZE) 10 | , timer_(ios) 11 | , time_out_(time_out) 12 | { 13 | } 14 | 15 | void connection::response(context_ptr& ctx) 16 | { 17 | auto self = this->shared_from_this(); 18 | ios_wrapper_.write(self, ctx); 19 | } 20 | 21 | void connection::close() 22 | { 23 | boost::system::error_code ignored_ec; 24 | socket_.close(ignored_ec); 25 | } 26 | 27 | io_service_t& connection::get_io_service() 28 | { 29 | return ios_wrapper_.get_io_service(); 30 | } 31 | 32 | tcp::socket& connection::socket() 33 | { 34 | return socket_; 35 | } 36 | 37 | void connection::start() 38 | { 39 | set_no_delay(); 40 | read_head(); 41 | } 42 | 43 | void connection::on_error(boost::system::error_code const& error) 44 | { 45 | close(); 46 | router_.on_error(this->shared_from_this(), error); 47 | } 48 | 49 | blob_t connection::get_read_buffer() const 50 | { 51 | return{ read_buffer_.data(), head_.len }; 52 | } 53 | 54 | req_header const& connection::get_read_header() const 55 | { 56 | return head_; 57 | } 58 | 59 | void connection::set_no_delay() 60 | { 61 | boost::asio::ip::tcp::no_delay option(true); 62 | boost::system::error_code ec; 63 | socket_.set_option(option, ec); 64 | } 65 | 66 | void connection::expires_timer() 67 | { 68 | if (time_out_.count() == 0) 69 | return; 70 | 71 | timer_.expires_from_now(time_out_); 72 | timer_.async_wait(boost::bind(&connection::handle_time_out, this->shared_from_this(), asio_error)); 73 | } 74 | 75 | void connection::cancel_timer() 76 | { 77 | if (time_out_.count() == 0) 78 | return; 79 | 80 | timer_.cancel(); 81 | } 82 | 83 | void connection::read_head() 84 | { 85 | async_read(socket_, boost::asio::buffer(&head_, sizeof(head_)), 86 | boost::bind(&connection::handle_read_head, this->shared_from_this(), asio_error)); 87 | } 88 | 89 | void connection::read_body() 90 | { 91 | if (head_.len > MAX_BUF_LEN) 92 | { 93 | socket_.close(); 94 | return; 95 | } 96 | 97 | if (head_.len > PAGE_SIZE) 98 | { 99 | read_buffer_.resize(head_.len); 100 | } 101 | 102 | expires_timer(); 103 | async_read(socket_, boost::asio::buffer(read_buffer_.data(), head_.len), 104 | boost::bind(&connection::handle_read_body, this->shared_from_this(), asio_error)); 105 | } 106 | 107 | void connection::handle_read_head(boost::system::error_code const& error) 108 | { 109 | if (!socket_.is_open()) 110 | return; 111 | 112 | if (!error) 113 | { 114 | if (head_.len == 0) 115 | { 116 | read_head(); 117 | } 118 | else 119 | { 120 | read_body(); 121 | } 122 | } 123 | else 124 | { 125 | on_error(error); 126 | } 127 | } 128 | 129 | void connection::handle_read_body(boost::system::error_code const& error) 130 | { 131 | cancel_timer(); 132 | if (!socket_.is_open()) 133 | return; 134 | 135 | if (!error) 136 | { 137 | router_.on_read(this->shared_from_this()); 138 | read_head(); 139 | } 140 | else 141 | { 142 | on_error(error); 143 | } 144 | } 145 | 146 | void connection::handle_time_out(boost::system::error_code const& error) 147 | { 148 | if (!socket_.is_open()) 149 | return; 150 | 151 | if (!error) 152 | { 153 | boost::system::error_code e = boost::asio::error::timed_out; 154 | on_error(e); 155 | } 156 | } 157 | } } -------------------------------------------------------------------------------- /rest_rpc/server/context.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace timax { namespace rpc 4 | { 5 | struct context_t 6 | { 7 | using post_func_t = std::function; 8 | using message_t = std::vector; 9 | 10 | context_t() = default; 11 | 12 | template 13 | context_t(rep_header const& h, Message&& msg, post_func_t postf) 14 | : context_t(h, std::move(message_t{ msg.begin(), msg.end() }), std::move(postf)) 15 | { 16 | } 17 | 18 | template 19 | context_t(Message&& msg, post_func_t postf) 20 | : context_t(std::move(message_t{ msg.begin(), msg.end() }), std::move(postf)) 21 | { 22 | } 23 | 24 | context_t(rep_header const& h, message_t msg, post_func_t postf) 25 | : head(h) 26 | , message(std::move(msg)) 27 | , post_func(std::move(postf)) 28 | { 29 | head.len = static_cast(message.size()); 30 | } 31 | 32 | context_t(message_t msg, post_func_t postf) 33 | : message(std::move(msg)) 34 | , post_func(std::move(postf)) 35 | { 36 | head.len = static_cast(message.size()); 37 | } 38 | 39 | void apply_post_func() const 40 | { 41 | if (post_func) 42 | post_func(); 43 | } 44 | 45 | auto get_message() const 46 | -> std::vector 47 | { 48 | if (message.empty()) 49 | return{ boost::asio::buffer(&head, sizeof(head)) }; 50 | 51 | return{ boost::asio::buffer(&head, sizeof(head)), boost::asio::buffer(message) }; 52 | } 53 | 54 | template 55 | static auto make_error_message(req_header const& h, Message&& msg, post_func_t postf = nullptr) 56 | { 57 | auto ctx = make_message_with_head(h, std::forward(msg), std::move(postf)); 58 | ctx->head.code = static_cast(result_code::FAIL); 59 | return ctx; 60 | } 61 | 62 | template 63 | static auto make_message_with_head(req_header const& h, Message&& msg, post_func_t postf = nullptr) 64 | { 65 | return std::make_shared(h, std::forward(msg), std::move(postf)); 66 | } 67 | 68 | template 69 | static auto make_message_without_head(Message&& msg, post_func_t postf = nullptr) 70 | { 71 | return std::make_shared(std::forward(msg), std::move(postf)); 72 | } 73 | 74 | rep_header head{ 0, 0, 0 }; 75 | message_t message; 76 | post_func_t post_func; 77 | }; 78 | } } -------------------------------------------------------------------------------- /rest_rpc/server/ios_wrapper.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace timax { namespace rpc 4 | { 5 | class ios_wrapper 6 | { 7 | public: 8 | using post_func_t = std::function; 9 | using context_ptr = std::shared_ptr; 10 | using context_container_t = std::list>; 11 | 12 | public: 13 | explicit ios_wrapper(io_service_t& ios); 14 | void write(connection_ptr& conn_ptr, context_ptr& context); 15 | io_service_t& get_io_service() noexcept; 16 | 17 | private: 18 | void write_progress_entry(connection_ptr& conn_ptr, context_ptr& context); 19 | void write_progress(); 20 | void write_progress(context_container_t&& delay_messages); 21 | 22 | private: 23 | void handle_write_entry(connection_ptr conn_ptr, context_ptr context, boost::system::error_code const& error); 24 | void handle_write(context_container_t& delay_messages, boost::system::error_code const& error); 25 | 26 | private: 27 | io_service_t& ios_; 28 | context_container_t delay_messages_; 29 | bool write_in_progress_; 30 | mutable std::mutex mutex_; 31 | }; 32 | } } -------------------------------------------------------------------------------- /rest_rpc/server/ios_wrapper_impl.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace timax { namespace rpc 4 | { 5 | ios_wrapper::ios_wrapper(io_service_t& ios) 6 | : ios_(ios) 7 | , write_in_progress_(false) 8 | { 9 | } 10 | 11 | void ios_wrapper::write(connection_ptr& conn_ptr, context_ptr& context) 12 | { 13 | assert(nullptr != context); 14 | 15 | lock_t lock{ mutex_ }; 16 | if (!write_in_progress_) 17 | { 18 | write_in_progress_ = true; 19 | lock.unlock(); 20 | write_progress_entry(conn_ptr, context); 21 | } 22 | else 23 | { 24 | delay_messages_.emplace_back(conn_ptr, context); 25 | } 26 | } 27 | 28 | io_service_t& ios_wrapper::get_io_service() noexcept 29 | { 30 | return ios_; 31 | } 32 | 33 | void ios_wrapper::write_progress_entry(connection_ptr& conn_ptr, context_ptr& context) 34 | { 35 | assert(nullptr != context); 36 | async_write(conn_ptr->socket(), context->get_message(), boost::bind( 37 | &ios_wrapper::handle_write_entry, this, conn_ptr, context, asio_error)); 38 | } 39 | 40 | void ios_wrapper::write_progress() 41 | { 42 | lock_t lock{ mutex_ }; 43 | if (delay_messages_.empty()) 44 | { 45 | write_in_progress_ = false; 46 | return; 47 | } 48 | else 49 | { 50 | context_container_t delay_messages = std::move(delay_messages_); 51 | lock.unlock(); 52 | write_progress(std::move(delay_messages)); 53 | } 54 | } 55 | 56 | void ios_wrapper::write_progress(context_container_t&& delay_messages) 57 | { 58 | auto& conn_ptr = delay_messages.front().first; 59 | auto& ctx_ptr = delay_messages.front().second; 60 | 61 | async_write(conn_ptr->socket(), ctx_ptr->get_message(), std::bind( 62 | &ios_wrapper::handle_write, this, std::move(delay_messages), std::placeholders::_1)); 63 | } 64 | 65 | void ios_wrapper::handle_write_entry(connection_ptr conn_ptr, context_ptr context, boost::system::error_code const& error) 66 | { 67 | assert(nullptr != context); 68 | if (!conn_ptr->socket().is_open()) 69 | return; 70 | 71 | if (error) 72 | { 73 | conn_ptr->on_error(error); 74 | } 75 | else 76 | { 77 | // call the post function 78 | context->apply_post_func(); 79 | 80 | // release the memory as soon as possible 81 | context.reset(); 82 | 83 | // continue 84 | write_progress(); 85 | } 86 | } 87 | 88 | void ios_wrapper::handle_write(context_container_t& delay_messages, boost::system::error_code const& error) 89 | { 90 | connection_ptr conn_ptr; 91 | context_ptr ctx_ptr; 92 | std::tie(conn_ptr, ctx_ptr) = std::move(delay_messages.front()); 93 | delay_messages.pop_front(); 94 | 95 | if (!conn_ptr->socket().is_open()) 96 | return; 97 | 98 | if (error) 99 | { 100 | conn_ptr->on_error(error); 101 | } 102 | else 103 | { 104 | // call the post function 105 | ctx_ptr->apply_post_func(); 106 | 107 | // release the memory as soon as possible 108 | ctx_ptr.reset(); 109 | conn_ptr.reset(); 110 | 111 | // continue 112 | if (!delay_messages.empty()) 113 | { 114 | write_progress(std::move(delay_messages)); 115 | } 116 | else 117 | { 118 | write_progress(); 119 | } 120 | } 121 | } 122 | } } -------------------------------------------------------------------------------- /rest_rpc/server/router.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace timax { namespace rpc 4 | { 5 | class router_base : boost::noncopyable 6 | { 7 | public: 8 | using connection_ptr = std::shared_ptr; 9 | using invoker_t = std::function; 10 | using invoker_container = std::unordered_map; 11 | using on_read_func = std::function; 12 | using on_error_func = std::function; 13 | 14 | public: 15 | bool register_invoker(uint64_t name, invoker_t&& invoker) 16 | { 17 | return invokers_.emplace(name, std::move(invoker)).second; 18 | } 19 | 20 | bool has_invoker(uint64_t name) noexcept 21 | { 22 | invokers_.find(name) != invokers_.end(); 23 | } 24 | 25 | void on_read(connection_ptr const& conn_ptr) 26 | { 27 | if(on_read_) 28 | on_read_(conn_ptr); 29 | } 30 | 31 | void on_error(connection_ptr const& conn_ptr, boost::system::error_code const& error) 32 | { 33 | if (on_error_) 34 | on_error_(conn_ptr, error); 35 | } 36 | 37 | void set_on_read(on_read_func&& on_read) 38 | { 39 | on_read_ = std::move(on_read); 40 | } 41 | 42 | void set_on_error(on_error_func&& on_error) 43 | { 44 | on_error_ = std::move(on_error); 45 | } 46 | 47 | protected: 48 | invoker_container invokers_; 49 | on_read_func on_read_; 50 | on_error_func on_error_; 51 | }; 52 | 53 | template 54 | class router : public router_base 55 | { 56 | public: 57 | using codec_policy = CodecPolicy; 58 | using message_t = typename codec_policy::buffer_type; 59 | using connection_ptr = typename router_base::connection_ptr; 60 | using invoker_t = typename router_base::invoker_t; 61 | using invoker_container = typename router_base::invoker_container; 62 | using on_read_func = typename router_base::on_read_func; 63 | using on_error_func = typename router_base::on_error_func; 64 | using hash_engine = hash_tmpl, uint64_t>; 65 | 66 | public: 67 | template 68 | bool register_invoker(std::string const& name, Handler&& handler, PostFunc&& post_func) 69 | { 70 | using handler_traits_type = handler_traits; 71 | using invoker_traits_type = invoker_traits< 72 | typename handler_traits_type::return_tag, handler_exec_sync>; 73 | 74 | return register_invoker_impl(name, 75 | std::forward(handler), std::forward(post_func)); 76 | } 77 | 78 | template 79 | bool register_invoker(std::string const& name, Handler&& handler) 80 | { 81 | using handler_traits_type = handler_traits; 82 | using invoker_traits_type = invoker_traits< 83 | typename handler_traits_type::return_tag, handler_exec_sync>; 84 | 85 | return register_invoker_impl(name, std::forward(handler)); 86 | } 87 | 88 | template 89 | bool async_register_invoker(std::string const& name, Handler&& handler, PostFunc&& post_func) 90 | { 91 | using handler_traits_type = handler_traits; 92 | using invoker_traits_type = invoker_traits< 93 | typename handler_traits_type::return_tag, handler_exec_async>; 94 | 95 | return register_invoker_impl(name, 96 | std::forward(handler), std::forward(post_func)); 97 | } 98 | 99 | template 100 | bool async_register_invoker(std::string const& name, Handler&& handler) 101 | { 102 | using handler_traits_type = handler_traits; 103 | using invoker_traits_type = invoker_traits< 104 | typename handler_traits_type::return_tag, handler_exec_async>; 105 | 106 | return register_invoker_impl(name, std::forward(handler)); 107 | } 108 | 109 | template 110 | bool register_raw_invoker(std::string const& name, Handler&& handler) 111 | { 112 | return register_invoker_impl(name, std::forward(handler)); 113 | } 114 | 115 | bool has_invoker(std::string const& name) const 116 | { 117 | auto name_hash = hash_(name); 118 | return router_base::has_invoker(name_hash); 119 | } 120 | 121 | void apply_invoker(connection_ptr conn, char const* data, size_t size) const 122 | { 123 | static auto cannot_find_invoker_error = codec_policy{}.pack(exception{ error_code::FAIL, "Cannot find handler!" }); 124 | 125 | auto& header = conn->get_read_header(); 126 | auto itr = this->invokers_.find(header.hash); 127 | if (this->invokers_.end() == itr) 128 | { 129 | auto ctx = context_t::make_error_message(header, cannot_find_invoker_error); 130 | conn->response(ctx); 131 | } 132 | else 133 | { 134 | auto& invoker = itr->second; 135 | if (!invoker) 136 | { 137 | auto ctx = context_t::make_error_message(header, cannot_find_invoker_error); 138 | conn->response(ctx); 139 | } 140 | 141 | try 142 | { 143 | invoker(conn, data, size); 144 | } 145 | catch (exception const& error) 146 | { 147 | // response serialized exception to client 148 | auto args_not_match_error = codec_policy{}.pack(error); 149 | auto args_not_match_error_message = context_t::make_error_message(header, 150 | std::move(args_not_match_error)); 151 | conn->response(args_not_match_error_message); 152 | } 153 | } 154 | } 155 | 156 | private: 157 | template 158 | bool register_invoker_impl(std::string const& name, Handlers&& ... handlers) 159 | { 160 | auto hash = hash_(name); 161 | auto itr = this->invokers_.find(hash); 162 | if (this->invokers_.end() != itr) 163 | return false; 164 | 165 | auto invoker = InvokerTraits::template get(std::forward(handlers)...); 166 | this->invokers_.emplace(hash, std::move(invoker)); 167 | return true; 168 | } 169 | 170 | template 171 | bool register_invoker_impl(std::string const& name, Handler&& handler) 172 | { 173 | auto hash = hash_(name); 174 | auto itr = this->invokers_.find(hash); 175 | if (this->invokers_.end() != itr) 176 | return false; 177 | 178 | invoker_t invoker = std::forward(handler); 179 | this->invokers_.emplace(hash, std::move(invoker)); 180 | return true; 181 | } 182 | 183 | private: 184 | // mutable std::mutex mutex_; 185 | hash_engine hash_; 186 | }; 187 | } } 188 | -------------------------------------------------------------------------------- /rest_rpc/server/server.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace timax { namespace rpc 4 | { 5 | template 6 | class server 7 | { 8 | public: 9 | using codec_policy = CodecPolicy; 10 | using connection_weak = std::weak_ptr; 11 | using router_t = router; 12 | using invoker_t = typename router_t::invoker_t; 13 | using sub_container = std::multimap; 14 | 15 | public: 16 | server(uint16_t port, size_t pool_size, duration_t time_out = duration_t::max()) 17 | : ios_pool_(pool_size) 18 | , acceptor_(ios_pool_.get_io_service(), tcp::endpoint{tcp::v4(), port}) 19 | , time_out_(time_out) 20 | { 21 | init_callback_functions(); 22 | 23 | register_handler(SUB_TOPIC, [this](std::string const& topic) { return topic; }, [this](auto conn, auto const& topic) 24 | { 25 | if (!topic.empty()) 26 | { 27 | lock_t lock{ mutex_ }; 28 | subscribers_.emplace(topic, conn); 29 | } 30 | }); 31 | 32 | router_.register_raw_invoker(PUB, [this](connection_ptr conn, char const* data, size_t size) 33 | { 34 | std::string topic; 35 | size_t length = 0; 36 | std::tie(topic, data, length) = 37 | std::move(get_topic_and_data(data, size)); 38 | pub(topic, data, length); 39 | auto ctx = context_t::make_message_with_head(conn->head_, context_t::message_t{}); 40 | conn->response(ctx); 41 | }); 42 | } 43 | 44 | ~server() 45 | { 46 | stop(); 47 | } 48 | 49 | void start() 50 | { 51 | ios_pool_.start(); 52 | do_accept(); 53 | } 54 | 55 | void stop() 56 | { 57 | ios_pool_.stop(); 58 | } 59 | 60 | template 61 | bool register_handler(std::string const& name, Handler&& handler, PostFunc&& post_func) 62 | { 63 | return router_.register_invoker(name, std::forward(handler), std::forward(post_func)); 64 | } 65 | 66 | template 67 | bool async_register_handler(std::string const& name, Handler&& handler, PostFunc&& post_func) 68 | { 69 | return router_.async_register_invoker(name, std::forward(handler), std::forward(post_func)); 70 | } 71 | 72 | template 73 | bool register_handler(std::string const& name, Handler&& handler) 74 | { 75 | return router_.register_invoker(name, std::forward(handler)); 76 | } 77 | 78 | template 79 | bool async_register_handler(std::string const& name, Handler&& handler) 80 | { 81 | return router_.async_register_invoker(name, std::forward(handler)); 82 | } 83 | 84 | template 85 | void pub(std::string const& topic, Result const& result, std::function&& postf = nullptr) 86 | { 87 | auto buffer = pack_as_tuple_if_not(codec_policy{}, result); 88 | auto ctx = context_t::make_message_without_head(std::move(buffer), std::move(postf)); 89 | public_to_subscriber(topic, ctx); 90 | } 91 | 92 | void pub(std::string const& topic, char const* data, size_t size, std::function&& posf = nullptr) 93 | { 94 | context_t::message_t buffer{ data, data + size }; 95 | auto ctx = context_t::make_message_without_head(std::move(buffer), std::move(posf)); 96 | public_to_subscriber(topic, ctx); 97 | } 98 | 99 | void remove_sub_conn(connection_ptr conn) 100 | { 101 | lock_t lock{ mutex_ }; 102 | for (auto itr = subscribers_.begin(); itr != subscribers_.end(); ) 103 | { 104 | if (itr->second.lock() == conn) 105 | itr = subscribers_.erase(itr); 106 | else 107 | ++itr; 108 | } 109 | } 110 | 111 | private: 112 | void init_callback_functions() 113 | { 114 | router_.set_on_read([this](connection_ptr conn_ptr) 115 | { 116 | //auto& header = conn_ptr->get_read_header(); 117 | auto read_buffer = conn_ptr->get_read_buffer(); 118 | router_.apply_invoker(conn_ptr, read_buffer.data(), read_buffer.size()); 119 | }); 120 | 121 | router_.set_on_error([this](connection_ptr conn_ptr, boost::system::error_code const& /*error*/) 122 | { 123 | // TODO ... 124 | remove_sub_conn(conn_ptr); 125 | }); 126 | } 127 | 128 | void do_accept() 129 | { 130 | auto new_connection = std::make_shared( 131 | ios_pool_.get_io_service(), router_, time_out_); 132 | 133 | acceptor_.async_accept(new_connection->socket(), 134 | [this, new_connection](boost::system::error_code const& error) 135 | { 136 | if (!error) 137 | { 138 | new_connection->start(); 139 | } 140 | else 141 | { 142 | // TODO log error 143 | } 144 | 145 | do_accept(); 146 | }); 147 | } 148 | 149 | void public_to_subscriber(std::string const& topic, std::shared_ptr& ctx) 150 | { 151 | lock_t lock{ mutex_ }; 152 | auto range = subscribers_.equal_range(topic); 153 | if (range.first == range.second) 154 | return; 155 | 156 | std::list alives; 157 | std::for_each(range.first, range.second, [&alives](auto& elem) 158 | { 159 | auto conn = elem.second.lock(); 160 | if (conn) 161 | { 162 | alives.push_back(conn); 163 | } 164 | }); 165 | 166 | lock.unlock(); 167 | 168 | for (auto& alive_conn : alives) 169 | { 170 | alive_conn->response(ctx); 171 | } 172 | } 173 | 174 | private: 175 | router_t router_; 176 | io_service_pool ios_pool_; 177 | tcp::acceptor acceptor_; 178 | duration_t time_out_; 179 | mutable std::mutex mutex_; 180 | sub_container subscribers_; 181 | }; 182 | } } -------------------------------------------------------------------------------- /rest_rpc/test/test_router.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "function_traits.hpp" 3 | #define TEST_MAIN 4 | #include "unit_test.hpp" 5 | 6 | void test_func(int , int ) 7 | { 8 | 9 | } 10 | 11 | TEST_CASE(function_traits_on_normal_function) 12 | { 13 | TEST_CHECK(function_traits::arity == 2); 14 | } 15 | 16 | -------------------------------------------------------------------------------- /rest_rpc/test/unit_test.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | struct BaseCase 9 | { 10 | virtual void run() = 0; 11 | virtual void abort() = 0; 12 | virtual bool isAborted() = 0; 13 | virtual ~BaseCase() = default; 14 | }; 15 | 16 | struct AbortThisCase {}; 17 | 18 | class UnitTest 19 | { 20 | private: 21 | UnitTest() : last_checked_line_{ 0 }, failure_num_{ 0 }, current_case_{ nullptr } {} 22 | std::vector test_cases_; 23 | std::string last_checked_file_; 24 | size_t last_checked_line_; 25 | size_t failure_num_; 26 | BaseCase* current_case_; 27 | 28 | public: 29 | static UnitTest& getInstance() 30 | { 31 | static UnitTest instance; 32 | return instance; 33 | } 34 | 35 | void runAll() 36 | { 37 | std::cout << ">>> running " << test_cases_.size() << " tests..." << std::endl; 38 | for (BaseCase* test : test_cases_) 39 | { 40 | current_case_ = test; 41 | current_case_->run(); 42 | } 43 | } 44 | 45 | BaseCase* currentCase() 46 | { 47 | return current_case_; 48 | } 49 | 50 | void registerTestCase(BaseCase *test) 51 | { 52 | test_cases_.push_back(test); 53 | } 54 | 55 | void printLastCheckedPoint() 56 | { 57 | std::cout << ">>> "; 58 | std::cout << last_checked_file_ << "(" << last_checked_line_ << ")" << ": last checkpoint" << std::endl; 59 | } 60 | 61 | void checkFile(const std::string& file) 62 | { 63 | last_checked_file_ = file; 64 | } 65 | 66 | void checkLine(size_t line) 67 | { 68 | last_checked_line_ = line; 69 | } 70 | 71 | void incFailure() 72 | { 73 | ++failure_num_; 74 | } 75 | 76 | size_t getFailureNum() 77 | { 78 | return failure_num_; 79 | } 80 | }; 81 | 82 | template 83 | struct TestCase : BaseCase 84 | { 85 | public: 86 | TestCase(std::function method, const std::string& name, const std::string& file, size_t line) 87 | : method_{ method }, case_name_{ name }, defined_file_{ file }, defined_line_{ line }, is_aborted_{ false } 88 | { 89 | UnitTest::getInstance().registerTestCase(this); 90 | } 91 | 92 | void run() override 93 | { 94 | try 95 | { 96 | UnitTest::getInstance().checkFile(defined_file_); 97 | UnitTest::getInstance().checkLine(defined_line_); 98 | size_t old_failure_num = UnitTest::getInstance().getFailureNum(); 99 | method_(); 100 | int failures = UnitTest::getInstance().getFailureNum() - old_failure_num; 101 | if (failures) 102 | { 103 | std::cout << ">>> "; 104 | std::cout << failures << " failures are detected in the test case \"" << case_name_ << "\"" << std::endl; 105 | } 106 | } 107 | catch (AbortThisCase&) 108 | { 109 | std::cout << ">>> " << case_name_ << " aborted." << std::endl; 110 | UnitTest::getInstance().printLastCheckedPoint(); 111 | } 112 | catch (std::exception& e) 113 | { 114 | UnitTest::getInstance().incFailure(); 115 | std::cout << ">>> fatal error: in \"" << case_name_ << "\": " << typeid(e).name() << ": " << e.what() << std::endl; 116 | UnitTest::getInstance().printLastCheckedPoint(); 117 | } 118 | catch (...) 119 | { 120 | UnitTest::getInstance().incFailure(); 121 | std::cout << ">>> fatal error: in \"" << case_name_ << "\": unknown type exception" << std::endl; 122 | UnitTest::getInstance().printLastCheckedPoint(); 123 | } 124 | } 125 | 126 | void abort() override 127 | { 128 | is_aborted_ = true; 129 | } 130 | 131 | bool isAborted() override 132 | { 133 | return is_aborted_; 134 | } 135 | 136 | ~TestCase() override = default; 137 | 138 | private: 139 | std::function method_; 140 | std::string case_name_; 141 | std::string defined_file_; 142 | size_t defined_line_; 143 | bool is_aborted_; 144 | }; 145 | 146 | template <> 147 | struct TestCase 148 | { 149 | TestCase(std::function, const std::string&, const std::string&, size_t) 150 | { 151 | } 152 | }; 153 | 154 | #ifdef TEST_MAIN 155 | [[noreturn]] 156 | static void report_and_exit() 157 | { 158 | std::cout << "\n**** "; 159 | std::cout << UnitTest::getInstance().getFailureNum() << " failures are detected." << std::endl; 160 | exit(UnitTest::getInstance().getFailureNum()); 161 | } 162 | 163 | int main() 164 | { 165 | signal(SIGSEGV, [](int) 166 | { 167 | UnitTest::getInstance().incFailure(); 168 | std::cout << ">>> fatal error: received SIGSEGV." << std::endl; 169 | UnitTest::getInstance().printLastCheckedPoint(); 170 | report_and_exit(); 171 | }); 172 | UnitTest::getInstance().runAll(); 173 | report_and_exit(); 174 | } 175 | #endif 176 | 177 | template ()(std::declval()...))> 178 | void do_check_failed(F&& f, Args&&... args) 179 | { 180 | f(std::forward(args)...); 181 | } 182 | 183 | template 184 | void do_check_failed(Msgs&&... msgs) 185 | { 186 | (void)std::initializer_list{(std::cout << ">>> " << msgs << std::endl, 0)...}; 187 | } 188 | 189 | #define TEST_CASE(test_name, ...) \ 190 | static void test_name(); \ 191 | static TestCase<__VA_ARGS__> test_name##_case{test_name, #test_name, __FILE__, __LINE__};\ 192 | static void test_name() 193 | 194 | #define G_CHECK(cond, strict, ...) \ 195 | do { \ 196 | BaseCase* cur_case = UnitTest::getInstance().currentCase(); \ 197 | if(cur_case->isAborted()) \ 198 | throw AbortThisCase{}; \ 199 | UnitTest::getInstance().checkFile(__FILE__); \ 200 | UnitTest::getInstance().checkLine(__LINE__); \ 201 | if(!(cond)) { \ 202 | UnitTest::getInstance().incFailure(); \ 203 | if(strict) { \ 204 | std::cout << ">>> check \"" << #cond << "\" failed." << std::endl; \ 205 | std::cout << ">>> critical error at " __FILE__ "(" << __LINE__ << ")." << std::endl;\ 206 | do_check_failed(__VA_ARGS__); \ 207 | cur_case->abort(); \ 208 | throw AbortThisCase{}; \ 209 | } else { \ 210 | std::cout << ">>> check \"" << #cond << "\" failed." << "at " \ 211 | << __FILE__ << "(" << __LINE__ << ")" << std::endl; \ 212 | do_check_failed(__VA_ARGS__); \ 213 | } \ 214 | } \ 215 | } while(0) 216 | 217 | #define TEST_CHECK(cond, ...) \ 218 | G_CHECK(cond, false, __VA_ARGS__) 219 | 220 | #define TEST_REQUIRE(cond, ...) \ 221 | G_CHECK(cond, true, __VA_ARGS__) 222 | 223 | #define TEST_REQUIRE_GUARD(...) \ 224 | TEST_CHECK(true, __VA_ARGS__) 225 | --------------------------------------------------------------------------------