diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..c8e65c60 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,26 @@ +name: CI + +on: + push: + pull_request: + schedule: + - cron: '0 0 * * 0' + +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + fail-fast: false + steps: + - name: Checkout + uses: actions/checkout@v2.0.0 + + - name: Build project + uses: nicledomaS/cmake_build_action@v1.3 + with: + submodule_update: ON + run_tests: ON + unit_test_build: -DBUILD_UNIT_TESTS=ON + cmake_args: -DCMAKE_CXX_FLAGS=-std=c++11 diff --git a/.gitignore b/.gitignore index 989bd14a..376798db 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,12 @@ build/ -**/*.user \ No newline at end of file +**/*.user + +CMakeCache.txt +CMakeFiles/ +Makefile +cmake_install.cmake +install_manifest.txt +libsioclient.a +sio_test +.DS_Store +.cache/ \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index 2644de04..6c672230 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "lib/rapidjson"] path = lib/rapidjson url = https://github.com/miloyip/rapidjson.git +[submodule "lib/asio"] + path = lib/asio + url = https://github.com/chriskohlhoff/asio.git diff --git a/API.md b/API.md new file mode 100644 index 00000000..4ad0e376 --- /dev/null +++ b/API.md @@ -0,0 +1,209 @@ +## API +### *Overview* +There're just 3 roles in this library - `socket`, `client` and `message`. + +`client` is for physical connection while `socket` is for "namespace" (which is like a logical channel), which means one `socket` paired with one namespace, and one `client` paired with one physical connection. + +Since a physical connection can have multiple namespaces (which is called multiplex), a `client` object may have multiple `socket` objects, each of which is bound to a distinct `namespace`. + +Use `client` to setup the connection to the server, manange the connection status, also session id for the connection. + +Use `socket` to send messages under namespace and receives messages in the namespace, also handle special types of message. + +The `message` is just about the content you want to send, with text, binary or structured combinations. + +### *Socket* +#### Constructors +Sockets are all managed by `client`, no public constructors. + +You can get it's pointer by `client.socket(namespace)`. + +#### Event Emitter +`void emit(std::string const& name, message::list const& msglist, std::function const& ack)` + +Universal event emission interface, by applying implicit conversion magic, it is backward compatible with all previous `emit` interfaces. + +#### Event Bindings +`void on(std::string const& event_name,event_listener const& func)` + +`void on(std::string const& event_name,event_listener_aux const& func)` + +Bind a callback to specified event name. Same as `socket.on()` function in JS, `event_listener` is for full content event object, `event_listener_aux` is for convenience. + +`void off(std::string const& event_name)` + +Unbind the event callback with specified name. + +`void off_all()` + +Clear all event bindings (not including the error listener). + +`void on_error(error_listener const& l)` + +Bind the error handler for socket.io error messages. + +`void off_error()` + +Unbind the error handler. + +```C++ +//event object: +class event +{ +public: + const std::string& get_nsp() const; + + const std::string& get_name() const; + + const message::ptr& get_message() const; + + bool need_ack() const; + + void put_ack_message(message::ptr const& ack_message); + + message::ptr const& get_ack_message() const; + ... +}; +//event listener declare: +typedef std::function event_listener_aux; + +typedef std::function event_listener; + +typedef std::function error_listener; + +``` + +#### Connect and close socket +`connect` will happen for existing `socket`s automatically when `client` have opened up the physical connection. + +`socket` opened with connected `client` will connect to its namespace immediately. + +`void close()` + +Positively disconnect from namespace. + +#### Get name of namespace +`std::string const& get_namespace() const` + +Get current namespace name which the client is inside. + +### *Client* +#### Constructors +`client()` default constructor. + +#### Connection Listeners +`void set_open_listener(con_listener const& l)` + +Call when websocket is open, especially means good connectivity. + +`void set_fail_listener(con_listener const& l)` + +Call when failed in connecting. + +`void set_close_listener(close_listener const& l)` + +Call when closed or drop. See `client::close_reason` + +```C++ +//connection listener declare: +enum close_reason +{ + close_reason_normal, + close_reason_drop +}; +typedef std::function con_listener; + +typedef std::function close_listener; +``` +#### Socket listeners +`void set_socket_open_listener(socket_listener const& l)` + +Set listener for socket connect event, called when any sockets being ready to send message. + +`void set_socket_close_listener(socket_listener const& l)` + +Set listener for socket close event, called when any sockets being closed, afterward, corresponding `socket` object will be cleared from client. + +```C++ + //socket_listener declare: + typedef std::function socket_listener; +``` + +#### Connect and Close +`void connect(const std::string& uri)` + +Connect to socket.io server, e.g., `client.connect("ws://localhost:3000");` + +`void close()` + +Close the client, return immediately. + +`void sync_close()` + +Close the client, don't return until it is really closed. + +`bool opened() const` + +Check if client's connection is opened. + +#### Transparent reconnecting +`void set_reconnect_attempts(int attempts)` + +Set max reconnect attempts, set to 0 to disable transparent reconnecting. + +`void set_reconnect_delay(unsigned millis)` + +Set minimum delay for reconnecting, this is the delay for 1st reconnecting attempt, +then the delay duration grows by attempts made. + +`void set_reconnect_delay_max(unsigned millis)` + +Set maximum delay for reconnecting. + +`void set_reconnecting_listener(con_listener const& l)` + +Set listener for reconnecting is in process. + +`void set_reconnect_listener(reconnect_listener const& l)` + +Set listener for reconnecting event, called once a delayed connecting is scheduled. + +#### Logs +`void set_logs_default()` + +Configure logs to the default level (connect, disconnect, app) + +`void set_logs_quiet()` + +Configure logs to the quiet level + +`void set_logs_verbose()` + +Configure logs to the verbose level + +#### Namespace +`socket::ptr socket(std::string const& nsp)` + +Get a pointer to a socket which is paired with the specified namespace. + +#### Session ID +`std::string const& get_sessionid() const` + +Get socket.io session id. + +### *Message* +`message` Base class of all message object. + +`int_message` message contains a 64-bit integer. + +`double_message` message contains a double. + +`string_message` message contains a string. + +`array_message` message contains a `vector`. + +`object_message` message contains a `map`. + +`message::ptr` pointer to `message` object, it will be one of its derived classes, judge by `message.get_flag()`. + +All designated constructor of `message` objects is hidden, you need to create message and get the `message::ptr` by `[derived]_message:create()`. diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..d1691679 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,40 @@ +# [2.1.0](https://github.com/socketio/socket.io-client-cpp/compare/2.0.0...2.1.0) (2021-10-12) + + +### Bug Fixes + +* fix ASIO_STANDALONE release build trying to use boost::random ([#301](https://github.com/socketio/socket.io-client-cpp/issues/301)) ([168ce9d](https://github.com/socketio/socket.io-client-cpp/commit/168ce9d10b4ac667c43fe16b4cf530f6a3749235)) +* fix LOG call syntax ([#301](https://github.com/socketio/socket.io-client-cpp/issues/301)) ([c09221f](https://github.com/socketio/socket.io-client-cpp/commit/c09221f357effe1a5a0fc0e7d7902eba1ab0484d)) + + +### Features + +* support TLSv1.2 and newer ([#321](https://github.com/socketio/socket.io-client-cpp/issues/321)) ([7c60ba9](https://github.com/socketio/socket.io-client-cpp/commit/7c60ba9d1e5e58de57f127025bcf69f4baecd2b4)) + + + +# [3.1.0](https://github.com/socketio/socket.io-client-cpp/compare/3.0.0...3.1.0) (2021-10-12) + + +### Bug Fixes + +* lower the minimum CMake supported version ([b196fa7](https://github.com/socketio/socket.io-client-cpp/commit/b196fa7537cd3f7bed626ead873a7b71d1293c0d)) +* handle closing sockets upon on_fail events ([d1c73b7](https://github.com/socketio/socket.io-client-cpp/commit/d1c73b73a8f536da3d353eac2a560af9791b13e3)) +* resolve client_impl::ping LOG call syntax in debug builds ([e7de4eb](https://github.com/socketio/socket.io-client-cpp/commit/e7de4ebf64f4f49e18594a2c093c07beb963579a)) + + +### Features + +* allow resource path to be set in connection URI ([#134](https://github.com/socketio/socket.io-client-cpp/issues/134)) ([36a8cd4](https://github.com/socketio/socket.io-client-cpp/commit/36a8cd45272aa51f0f6ef27aa4744dbc6e8421f7)) +* add support for logging configuration ([1b42ce7](https://github.com/socketio/socket.io-client-cpp/commit/1b42ce738f4c3e260f79bcb143bfe6efcdce5709)) +* support TLSv1.2 and newer ([#321](https://github.com/socketio/socket.io-client-cpp/issues/321)) ([82d39a9](https://github.com/socketio/socket.io-client-cpp/commit/82d39a90ef118500a0329d214eec331db983bd74)) + + + +# [3.0.0](https://github.com/socketio/socket.io-client-cpp/compare/2.0.0...3.0.0) (2021-01-09) + + +### Features + +* add support for Socket.IO v3 ([ec4d540](https://github.com/socketio/socket.io-client-cpp/commit/ec4d540ad54593604ac2091e67ffc2a6d9a00db6)) + diff --git a/CMakeLists.txt b/CMakeLists.txt index c9ad9cee..150cc331 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,62 +1,177 @@ -cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR) -PROJECT(sioclient) - -if(NOT CMAKE_BUILD_TYPE ) -MESSAGE(STATUS "not define build type, set to release" ) -set(CMAKE_BUILD_TYPE Release ) -elseif(NOT (${CMAKE_BUILD_TYPE} STREQUAL "Release" OR ${CMAKE_BUILD_TYPE} STREQUAL "Debug" )) -MESSAGE(SEND_ERROR "CMAKE_BUILD_TYPE must be either Release or Debug") -return() +cmake_minimum_required(VERSION 3.12...3.27) + +PROJECT(sioclient + VERSION 3.1.0 +) + +option(BUILD_SHARED_LIBS "Build the shared library" OFF) +option(BUILD_UNIT_TESTS "Builds unit tests target" OFF) +option(USE_SUBMODULES "Use source in local submodules instead of system libraries" ON) +option(DISABLE_LOGGING "Do not print logging messages" OFF) + +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(DEFAULT_BUILD_TYPE "Release") + message(STATUS "Setting build type to '${DEFAULT_BUILD_TYPE}' as none was specified.") + set(CMAKE_BUILD_TYPE "${DEFAULT_BUILD_TYPE}" CACHE STRING "Choose the type of build." FORCE) + + # Set the possible values of build type for cmake-gui + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") endif() -set(BOOST_VER "1.55.0" CACHE STRING "boost version" ) +# Only do these if this is the main project, and not if it is included through add_subdirectory +if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) + # Testing only available if this is the main app + # Note this needs to be done in the main CMakeLists + # since it calls enable_testing, which must be in the + # main CMakeLists. + include(CTest) +endif() + +add_definitions( + # These will force ASIO to compile without Boost + -DBOOST_DATE_TIME_NO_LIB + -DBOOST_REGEX_NO_LIB + -DASIO_STANDALONE -set(Boost_USE_STATIC_LIBS ON) -set(Boost_USE_MULTITHREADED ON) -set(Boost_USE_STATIC_RUNTIME OFF) -find_package(Boost ${BOOST_VER} REQUIRED COMPONENTS system date_time random) + # These will force sioclient to compile with C++11 + -D_WEBSOCKETPP_CPP11_STL_ + -D_WEBSOCKETPP_CPP11_FUNCTIONAL_ + -D_WEBSOCKETPP_CPP11_TYPE_TRAITS_ + -D_WEBSOCKETPP_CPP11_CHRONO_ +) -aux_source_directory(${CMAKE_CURRENT_LIST_DIR}/src ALL_SRC) -aux_source_directory(${CMAKE_CURRENT_LIST_DIR}/src/internal ALL_SRC) -file(GLOB ALL_HEADERS ${CMAKE_CURRENT_LIST_DIR}/src/*.h ) -set(SIO_INCLUDEDIR ${CMAKE_CURRENT_LIST_DIR}) +if (DISABLE_LOGGING) + add_definitions(-DSIO_DISABLE_LOGGING) +endif() -add_library(sioclient STATIC ${ALL_SRC}) -target_include_directories(sioclient PRIVATE ${Boost_INCLUDE_DIRS} - ${CMAKE_CURRENT_LIST_DIR}/src - ${CMAKE_CURRENT_LIST_DIR}/lib/websocketpp - ${CMAKE_CURRENT_LIST_DIR}/lib/rapidjson/include +set(ALL_SRC + "src/sio_client.cpp" + "src/sio_socket.cpp" + "src/internal/sio_client_impl.cpp" + "src/internal/sio_packet.cpp" ) +add_library(sioclient ${ALL_SRC}) -set_property(TARGET sioclient PROPERTY CXX_STANDARD 11) -set_property(TARGET sioclient PROPERTY CXX_STANDARD_REQUIRED ON) -target_link_libraries(sioclient PRIVATE ${Boost_LIBRARIES}) +if(USE_SUBMODULES) + set(MODULE_INCLUDE_DIRS + ${CMAKE_CURRENT_LIST_DIR}/lib/websocketpp + ${CMAKE_CURRENT_LIST_DIR}/lib/rapidjson/include + ${CMAKE_CURRENT_LIST_DIR}/lib/asio/asio/include + ) +else() + find_package(websocketpp CONFIG REQUIRED) + find_package(asio CONFIG REQUIRED) + find_package(RapidJSON CONFIG REQUIRED) + target_link_libraries(sioclient PRIVATE websocketpp::websocketpp asio::asio rapidjson) +endif() + +include(GNUInstallDirs) + +target_include_directories(sioclient + PUBLIC + $ + $ + PRIVATE + ${MODULE_INCLUDE_DIRS} +) + +target_compile_features(sioclient PUBLIC cxx_std_11) + +find_package(Threads REQUIRED) +target_link_libraries(sioclient PUBLIC Threads::Threads) + +if(BUILD_SHARED_LIBS) + set_target_properties(sioclient + PROPERTIES + SOVERSION ${PROJECT_VERSION_MAJOR} + VERSION ${PROJECT_VERSION} + ) +endif() + +list(APPEND TARGET_LIBRARIES sioclient) find_package(OpenSSL) + if(OPENSSL_FOUND) -add_library(sioclient_tls STATIC ${ALL_SRC}) -target_include_directories(sioclient_tls PRIVATE ${Boost_INCLUDE_DIRS} - ${CMAKE_CURRENT_LIST_DIR}/src - ${CMAKE_CURRENT_LIST_DIR}/lib/websocketpp - ${CMAKE_CURRENT_LIST_DIR}/lib/rapidjson/include - ${OPENSSL_INCLUDE_DIR} -) + add_library(sioclient_tls ${ALL_SRC}) + target_include_directories(sioclient_tls PUBLIC + $ + $ + PRIVATE + ${MODULE_INCLUDE_DIRS} + ${OPENSSL_INCLUDE_DIR} + ) + + target_compile_features(sioclient_tls PUBLIC cxx_std_11) + target_link_libraries(sioclient_tls PRIVATE OpenSSL::SSL OpenSSL::Crypto) + if (NOT USE_SUBMODULES) + target_link_libraries(sioclient_tls PRIVATE websocketpp::websocketpp asio asio::asio rapidjson) + endif() -set_property(TARGET sioclient_tls PROPERTY CXX_STANDARD 11) -set_property(TARGET sioclient_tls PROPERTY CXX_STANDARD_REQUIRED ON) -target_link_libraries(sioclient_tls PRIVATE ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} ) -target_compile_definitions(sioclient_tls PRIVATE -DSIO_TLS) + target_compile_definitions(sioclient_tls PRIVATE -DSIO_TLS) + target_link_libraries(sioclient_tls PUBLIC Threads::Threads) + if(BUILD_SHARED_LIBS) + set_target_properties(sioclient_tls + PROPERTIES + SOVERSION ${PROJECT_VERSION_MAJOR} + VERSION ${PROJECT_VERSION} + ) + endif() + + list(APPEND TARGET_LIBRARIES sioclient_tls) endif() -install(FILES ${ALL_HEADERS} - DESTINATION "${CMAKE_CURRENT_LIST_DIR}/build/include" +export(PACKAGE sioclient) + +file(GLOB ALL_HEADERS ${CMAKE_CURRENT_LIST_DIR}/src/*.h) +install(FILES ${ALL_HEADERS} + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} +) + +install(TARGETS ${TARGET_LIBRARIES} EXPORT sioclientTargets + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ) -install(TARGETS sioclient - DESTINATION "${CMAKE_CURRENT_LIST_DIR}/build/lib/${CMAKE_BUILD_TYPE}" +# === generate a CMake Config File === +include(CMakePackageConfigHelpers) +set(ConfigPackageLocation ${CMAKE_INSTALL_LIBDIR}/cmake/sioclient) +string(REGEX REPLACE "([^;]+)" "find_dependency(\\1)" _find_dependency_calls "${_package_dependencies}") +string(REPLACE ";" "\n" _find_dependency_calls "${_find_dependency_calls}") + +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/sioclient/sioclientConfigVersion.cmake" + VERSION ${sioclient_VERSION} + COMPATIBILITY AnyNewerVersion +) + +export(EXPORT sioclientTargets + FILE "${CMAKE_CURRENT_BINARY_DIR}/sioclient/sioclientTargets.cmake" + NAMESPACE sioclient:: +) + +configure_package_config_file(sioclientConfig.cmake.in + "${CMAKE_CURRENT_BINARY_DIR}/sioclient/sioclientConfig.cmake" + INSTALL_DESTINATION "${ConfigPackageLocation}" ) -install(FILES ${Boost_LIBRARIES} - DESTINATION "${CMAKE_CURRENT_LIST_DIR}/build/lib/${CMAKE_BUILD_TYPE}" +install(EXPORT sioclientTargets + NAMESPACE + sioclient:: + DESTINATION + ${ConfigPackageLocation} +) +install( + FILES + "${CMAKE_CURRENT_BINARY_DIR}/sioclient/sioclientConfig.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/sioclient/sioclientConfigVersion.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/sioclient/sioclientTargets.cmake" + DESTINATION + ${ConfigPackageLocation} ) + +if((CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND BUILD_TESTING) OR BUILD_UNIT_TESTS) + add_subdirectory(test) +endif() diff --git a/INSTALL.md b/INSTALL.md new file mode 100644 index 00000000..480ed047 --- /dev/null +++ b/INSTALL.md @@ -0,0 +1,39 @@ +## Install + +### With CMake +1. Use `git clone --recurse-submodules https://github.com/socketio/socket.io-client-cpp.git` to clone your local repo. +2. Run `cmake ./` +3. Run `make install`(if makefile generated) or open generated project (if project file generated) to build. +4. Outputs is under `./build`, link with the all static libs under `./build/lib` and include headers under `./build/include` in your client code where you want to use it. + +### Without CMake +1. Use `git clone --recurse-submodules https://github.com/socketio/socket.io-client-cpp.git` to clone your local repo. +2. Add `./lib/asio/asio/include`, `./lib/websocketpp` and `./lib/rapidjson/include` to headers search path. +3. Include all files under `./src` in your project, add `sio_client.cpp`,`sio_socket.cpp`,`internal/sio_client_impl.cpp`, `internal/sio_packet.cpp` to source list. +4. Add `BOOST_DATE_TIME_NO_LIB`, `BOOST_REGEX_NO_LIB`, `ASIO_STANDALONE`, `_WEBSOCKETPP_CPP11_STL_` and `_WEBSOCKETPP_CPP11_FUNCTIONAL_` to the preprocessor definitions +5. Include `sio_client.h` in your client code where you want to use it. + +### With vcpkg + +You can download and install the Socket.IO C++ client using the [vcpkg](https://github.com/Microsoft/vcpkg) dependency manager: + +```bash +git clone https://github.com/Microsoft/vcpkg.git +cd vcpkg +./bootstrap-vcpkg.sh +./vcpkg integrate install +./vcpkg install socket-io-client +``` + +The Socket.IO client port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository. + +### With Conan + +You can install pre-built binaries for Socket.IO C++ client or build it from source using [Conan](https://conan.io/). Use the following command: + +``` +conan install --requires="sioclient/[*]" --build=missing +``` + +The Socket.IO client Conan recipe is kept up to date by Conan maintainers and community contributors. +If the version is out of date, please [create an issue or pull request](https://github.com/conan-io/conan-center-index) on the ConanCenterIndex repository. diff --git a/INSTALL_IOS.md b/INSTALL_IOS.md new file mode 100644 index 00000000..e154cf85 --- /dev/null +++ b/INSTALL_IOS.md @@ -0,0 +1,27 @@ +## iOS + +### Option 1: Create a static library + +1. Create a static library +1. Copy the header files into xcode + +Use the static libraries generated by the example project [iOS example project](examples/iOS) + +Create one for +- release iphoneos +- release simulator +- debug iphoneos +- debug simulator + +Join the debug libraries and the release libraries with e.g. +``` +libtool -static -o libUniversalRelease.a Release-iphoneos/libsioclient.a Release-iphonesimulator/libsioclient.a +libtool -static -o libUniversalDebug.a Debug-iphoneos/libsioclient.a Debug-iphonesimulator/libsioclient.a +``` + + +### Option 2: Manual integration + +Use this [shell](https://gist.github.com/melode11/a90114a2abf009ca22ea) to download and build boost completely automattically. It installs boost to `/prefix`. + +See the [iOS example project](examples/iOS) for how to integrate the rest. diff --git a/LICENSE b/LICENSE old mode 100755 new mode 100644 diff --git a/README.md b/README.md old mode 100755 new mode 100644 index 08252483..3b093973 --- a/README.md +++ b/README.md @@ -1,92 +1,116 @@ # Socket.IO C++ Client -This repository contains the Socket.IO C++ client. It depends on [websocket++](https://github.com/zaphoyd/websocketpp) and is inspired by [socket.io-clientpp](https://github.com/ebshimizu/socket.io-clientpp). +[![Build Status](https://github.com/socketio/socket.io-client-cpp/workflows/CI/badge.svg)](https://github.com/socketio/socket.io-client-cpp/actions) -By virtue of being written in C++, this client works in several different platforms. The [examples](https://github.com/socketio/socket.io-client-cpp/tree/master/examples) folder contains an iPhone, QT and Console example chat client! +By virtue of being written in C++, this client works in several different platforms. The [examples](https://github.com/socketio/socket.io-client-cpp/tree/master/examples) folder contains an iPhone, QT and Console example chat client! It depends on [websocket++](https://github.com/zaphoyd/websocketpp) and is inspired by [socket.io-clientpp](https://github.com/ebshimizu/socket.io-clientpp). [![Clients with iPhone, QT, Console and web](https://cldup.com/ukvVVZmvYV.png)](https://github.com/socketio/socket.io-client-cpp/tree/master/examples) +## Compatibility table + + + + + + + + + + + + + + + + + + + + +
C++ Client versionSocket.IO server version
1.x / 2.x3.x / 4.x
2.x (2.x branch)YESYES, with allowEIO3: true
3.x (master branch)NOYES
+ ## Features - 100% written in modern C++11 -- Compatible with 1.0+ protocol - Binary support - Automatic JSON encoding - Multiplex support - Similar API to the Socket.IO JS client +- Cross platform -## How to use -### With CMake -1. Install boost, see [Boost setup](#boost_setup) section. -2. Use `git clone --recurse-submodules https://github.com/socketio/socket.io-client-cpp.git` to clone your local repo. -3. Run `cmake -DBOOST_ROOT:STRING= -DBOOST_VER:STRING= ./` -4. Run `make install`(if makefile generated) or open generated project (if project file generated) to build. -5. Outputs is under `./build`, link with the all static libs under `./build/lib` and include headers under `./build/include` in your client code where you want to use it. +Note: Only the WebSocket transport is currently implemented (no fallback to HTTP long-polling) + +## Installation alternatives + +* [With CMAKE](./INSTALL.md#with-cmake) +* [Without CMAKE](./INSTALL.md#without-cmake) +* [With VCPKG](./INSTALL.md#with-vcpkg) +* [With Conan](./INSTALL.md#with-conan) +* [iOS and OS X](./INSTALL_IOS.md) + * Option 1: Cocoapods + * Option 2: Create a static library + * Option 3: Manual integration -* If you're using boost without install,you can specify `boost include dir` and `boost lib dir` separately by: -```bash -cmake --DBOOST_INCLUDEDIR= --DBOOST_LIBRARYDIR= --DBOOST_VER:STRING= -./ -``` -* CMake didn't allow merging static libraries,but they're all copied to `./build/lib`, you can DIY if you like. -### Without CMake -1. Install boost, see [Boost setup](#boost_setup) section. -2. Use `git clone --recurse-submodules https://github.com/socketio/socket.io-client-cpp.git` to clone your local repo. -3. Add `/include`,`./lib/websocketpp` and `./lib/rapidjson/include` to headers search path. -4. Include all files under `./src` in your project, add `sio_packet.cpp`,`sio_socket.cpp`,`internal/sio_client_impl.cpp` to source list. -5. Add `/lib` to library search path, add `boost.lib`(Win32) or `-lboost`(Other) link option. -6. Include `sio_client.h` in your client code where you want to use it. +## Quickstart -## Quick start -The APIs are similar with JS client. +** [Full overview of API can be seen here](./API.md) ** -Connect to a server + +The APIs are similar to the JS client. + +#### Connect to a server ```C++ sio::client h; h.connect("http://127.0.0.1:3000"); ``` -Emit a event +#### Emit an event + ```C++ -//emit event name only: -socket->emit("login"); -//emit text +// emit event name only: +h.socket()->emit("login"); + +// emit text h.socket()->emit("add user", username); -//emit binary + +// emit binary char buf[100]; -h.socket()->emit("add user", std::make_shared(&buf,100)); -//emit message object with lambda ack handler -h.socket()->emit("add user", string_message::create(username), [&](message::ptr const& msg) -{ +h.socket()->emit("add user", std::make_shared(buf,100)); + +// emit message object with lambda ack handler +h.socket()->emit("add user", string_message::create(username), [&](message::list const& msg) { }); -//emit with `message::list` -message::list li("arg1"); -li.push(string_message::create("arg2")); -socket->emit("new va",li);// support io.on("new va",function(arg1,arg2){}); style in server side. + +// emit multiple arguments +message::list li("sports"); +li.push(string_message::create("economics")); +socket->emit("categories", li); ``` -* Items in `message::list` will be expanded in server side event callback function as function arguments. +Items in `message::list` will be expanded in server side event callback function as function arguments. -Bind a event +#### Bind an event + +##### Bind with function pointer ```C++ -/**************** bind with function pointer ***************/ void OnMessage(sio::event &) { - + } h.socket()->on("new message", &OnMessage); +``` -/********************* bind with lambda ********************/ +##### Bind with lambda +```C++ h.socket()->on("login", [&](sio::event& ev) { //handle login message //post to UI thread if any UI updating. }); +``` -/**************** bind with member function *****************/ +##### Bind with member function +```C++ class MessageHandler { public: @@ -95,235 +119,13 @@ public: MessageHandler mh; h.socket()->on("new message",std::bind( &MessageHandler::OnMessage,&mh,std::placeholders::_1)); ``` -Send to other namespace -```C++ -h.socket("/chat")->emit("add user", username); -``` - -## API -### *Overview* -There're just 3 roles in this library - `socket`,`client` and `message`. - -`client` is for physical connection while `socket` is for "namespace"(which is like a logical channel), which means one `socket` paired with one namespace, and one `client` paired with one physical connection. - -Since a physical connection can have multiple namespaces (which is called multiplex), a `client` object may have multiple `socket` objects, each is bind to a distinct `namespace`. - -Use `client` to setup the connection to the server, manange the connection status, also session id for the connection. - -Use `socket` to send messages under namespace and receives messages in the namespace, also handle special types of message. - -The `message` is just about the content you want to send, with text,binary or structured combinations. - -### *Socket* -#### Constructors -Sockets are all managed by `client`, no public constructors. - -You can get it's pointer by `client.socket(namespace)`. - -#### Event Emitter -`void emit(std::string const& name, message::list const& msglist, std::function const& ack)` - -Universal event emition interface, by applying implicit conversion magic, it is backward compatible with all previous `emit` interfaces. - -#### Event Bindings -`void on(std::string const& event_name,event_listener const& func)` - -`void on(std::string const& event_name,event_listener_aux const& func)` - -Bind a callback to specified event name. Same as `socket.on()` function in JS, `event_listener` is for full content event object,`event_listener_aux` is for convinience. - -`void off(std::string const& event_name)` - -Unbind the event callback with specified name. - -`void off_all()` - -Clear all event bindings (not including the error listener). - -`void on_error(error_listener const& l)` - -Bind the error handler for socket.io error messages. - -`void off_error()` - -Unbind the error handler. +#### Using namespace ```C++ -//event object: -class event -{ -public: - const std::string& get_nsp() const; - - const std::string& get_name() const; - - const message::ptr& get_message() const; - - bool need_ack() const; - - void put_ack_message(message::ptr const& ack_message); - - message::ptr const& get_ack_message() const; - ... -}; -//event listener declare: -typedef std::function event_listener_aux; - -typedef std::function event_listener; - -typedef std::function error_listener; - -``` - -#### Connect and close socket -`connect` will happen for existing `socket`s automatically when `client` have opened up the physical connection. - -`socket` opened with connected `client` will connect to its namespace immediately. - -`void close()` - -Positively disconnect from namespace. - -#### Get name of namespace -`std::string const& get_namespace() const` - -Get current namespace name which the client is inside. - -### *Client* -#### Constructors -`client()` default constructor. - -#### Connection Listeners -`void set_open_listener(con_listener const& l)` - -Call when websocket is open, especially means good connectivity. - -`void set_fail_listener(con_listener const& l)` - -Call when failed in connecting. - -`void set_close_listener(close_listener const& l)` - -Call when closed or drop. See `client::close_reason` - -```C++ -//connection listener declare: -enum close_reason -{ - close_reason_normal, - close_reason_drop -}; -typedef std::function con_listener; - -typedef std::function close_listener; -``` -#### Socket listeners -`void set_socket_open_listener(socket_listener const& l)` - -Set listener for socket connect event, called when any sockets being ready to send message. - -`void set_socket_close_listener(socket_listener const& l)` - -Set listener for socket close event, called when any sockets being closed, afterward, corresponding `socket` object will be cleared from client. - -```C++ - //socket_listener declare: - typedef std::function socket_listener; -``` - -#### Connect and Close -`void connect(const std::string& uri)` - -Connect to socket.io server, eg. `client.connect("ws://localhost:3000");` - -`void close()` - -Close the client, return immediately. - -`void sync_close()` - -Close the client, return until it is really closed. - -`bool opened() const` - -Check if client's connection is opened. - -#### Transparent reconnecting -`void set_reconnect_attempts(int attempts)` - -Set max reconnect attempts, set to 0 to disable transparent reconnecting. - -`void set_reconnect_delay(unsigned millis)` - -Set minimum delay for reconnecting, this is the delay for 1st reconnecting attempt, -then the delay duration grows by attempts made. - -`void set_reconnect_delay_max(unsigned millis)` - -Set maximum delay for reconnecting. - -`void set_reconnecting_listener(con_listener const& l)` - -Set listener for reconnecting is in process. - -`void set_reconnect_listener(reconnect_listener const& l)` - -Set listener for reconnecting event, called once a delayed connecting is scheduled. - -#### Namespace -`socket::ptr socket(std::string const& nsp)` - -Get a pointer to a socket which is paired with the specified namespace. - -#### Session ID -`std::string const& get_sessionid() const` - -Get socket.io session id. - -### *Message* -`message` Base class of all message object. - -`int_message` message contains a 64-bit integer. - -`double_message` message contains a double. - -`string_message` message contains a string. - -`array_message` message contains a `vector`. - -`object_message` message contains a `map`. - -`message::ptr` pointer to `message` object, it will be one of its derived classes, judge by `message.get_flag()`. - -All designated constructor of `message` objects is hidden, you need to create message and get the `message::ptr` by `[derived]_message:create()`. - -## Boost setup -* Download boost from [boost.org](http://www.boost.org/). - -* Unpack boost to some place. - -* Run either .\bootstrap.bat (on Windows), or ./bootstrap.sh (on other operating systems) under boost folder. - -## Boost build (Build the necessary subset only) - -#### Windows (or other mainstream desktop platforms shall work too): -Run with following script will build the necessary subset: - -```bash -bjam install --prefix="" --with-system --with-date_time --with-random link=static runtime-link=shared threading=multi -``` -Optionally You can merge all output .lib files into a fat one,especially if you're not using cmake. - -In output folder, run: - -```bash -lib.exe /OUT:boost.lib * +h.socket("/chat")->emit("add user", username); ``` +** [Full overview of API can be seen here](./API.md) ** -#### iOS -Use this [shell](https://gist.github.com/melode11/a90114a2abf009ca22ea) to download and build boost completely automattically. - -It installs boost to `/prefix`. +## License -##License MIT diff --git a/examples/Console/CMakeLists.txt b/examples/Console/CMakeLists.txt index 907c5d18..0ca9213b 100644 --- a/examples/Console/CMakeLists.txt +++ b/examples/Console/CMakeLists.txt @@ -1,10 +1,9 @@ cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR) +find_package(Threads REQUIRED) include(${CMAKE_CURRENT_SOURCE_DIR}/../../CMakeLists.txt) add_executable(sio_console_demo main.cpp) -set_property(TARGET sio_console_demo PROPERTY CXX_STANDARD 11) -set_property(TARGET sio_console_demo PROPERTY CXX_STANDARD_REQUIRED ON) target_link_libraries(sio_console_demo sioclient) -target_link_libraries(sio_console_demo pthread ) +target_link_libraries(sio_console_demo Threads::Threads) +target_compile_features(sio_console_demo PRIVATE cxx_std_11) message(STATUS ${Boost_INCLUDE_DIRS} ) #target_include_directories(sio_console_demo PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../src" ${Boost_INCLUDE_DIRS} ) - diff --git a/examples/Console/main.cpp b/examples/Console/main.cpp old mode 100755 new mode 100644 index 1dc4e685..9a95dfb2 --- a/examples/Console/main.cpp +++ b/examples/Console/main.cpp @@ -69,9 +69,9 @@ int participants = -1; socket::ptr current_socket; -void bind_events(socket::ptr &socket) +void bind_events() { - current_socket->on("new message", sio::socket::event_listener_aux([&](string const& name, message::ptr const& data, bool isAck,message::ptr &ack_resp) + current_socket->on("new message", sio::socket::event_listener_aux([&](string const& name, message::ptr const& data, bool isAck,message::list &ack_resp) { _lock.lock(); string user = data->get_map()["username"]->get_string(); @@ -80,7 +80,7 @@ void bind_events(socket::ptr &socket) _lock.unlock(); })); - current_socket->on("user joined",sio::socket::event_listener_aux([&](string const& name, message::ptr const& data, bool isAck,message::ptr &ack_resp) + current_socket->on("user joined",sio::socket::event_listener_aux([&](string const& name, message::ptr const& data, bool isAck,message::list &ack_resp) { _lock.lock(); string user = data->get_map()["username"]->get_string(); @@ -91,7 +91,7 @@ void bind_events(socket::ptr &socket) HIGHLIGHT(user<<" joined"<<"\nthere"<<(plural?" are ":"'s ")<< participants<<(plural?" participants":" participant")); _lock.unlock(); })); - current_socket->on("user left", sio::socket::event_listener_aux([&](string const& name, message::ptr const& data, bool isAck,message::ptr &ack_resp) + current_socket->on("user left", sio::socket::event_listener_aux([&](string const& name, message::ptr const& data, bool isAck,message::list &ack_resp) { _lock.lock(); string user = data->get_map()["username"]->get_string(); @@ -126,7 +126,7 @@ MAIN_FUNC getline(cin, nickname); } - current_socket->on("login", sio::socket::event_listener_aux([&](string const& name, message::ptr const& data, bool isAck,message::ptr &ack_resp){ + current_socket->on("login", sio::socket::event_listener_aux([&](string const& name, message::ptr const& data, bool isAck,message::list &ack_resp){ _lock.lock(); participants = data->get_map()["numUsers"]->get_int(); bool plural = participants !=1; @@ -141,7 +141,7 @@ MAIN_FUNC _cond.wait(_lock); } _lock.unlock(); - bind_events(current_socket); + bind_events(); HIGHLIGHT("Start to chat,commands:\n'$exit' : exit chat\n'$nsp ' : change namespace"); for (std::string line; std::getline(std::cin, line);) { @@ -166,7 +166,7 @@ MAIN_FUNC current_socket->close(); } current_socket = h.socket(new_nsp); - bind_events(current_socket); + bind_events(); //if change to default nsp, we do not need to login again (since it is not closed). if(current_socket->get_namespace() == "/") { diff --git a/examples/QT/README.md b/examples/QT/README.md index 03ccb8ec..a26610d1 100644 --- a/examples/QT/README.md +++ b/examples/QT/README.md @@ -2,7 +2,7 @@ In this tutorial we’ll learn how to create a QT chat application that communicates with a [Socket.IO Node.JS chat server](https://github.com/Automattic/socket.io/tree/master/examples/chat). -###Introduction +### Introduction To follow along, start by cloning the repository: [socket.io-client-cpp](https://github.com/socketio/socket.io-client-cpp). Using: @@ -39,7 +39,7 @@ SioChatDemo |__mainwindow.ui ``` -###Import SioClient and config compile options. +### Import SioClient and config compile options. Let's copy the SioClient into the QT project as a subfolder `sioclient`. Edit `SioChatDemo.pro` to config paths and compile options, simply add: @@ -65,24 +65,7 @@ CONFIG+=c++11 `no_keywords` is for preventing qmake treat some function's name `emit` as the keyword of signal-slot mechanism. `c++11` ask for C++11 support. -##Import boost -Suppose we now have our boost `headers` and a fat boost `static lib` named `libboost.a`(non-win32) or `boost.lib`(win32) ready. - -To import them we need to edit `SioChatDemo.pro` again,add header include: - -```bash -INCLUDEPATH += `our boost headers folder` -``` - -also linker options: - -```bash -win32:CONFIG(release, debug|release): LIBS += -L`our Win32 boost static lib folder` -lboost -else:win32:CONFIG(debug, debug|release): LIBS += -L`our Win32 boost static lib folder` -lboost -else:unix: LIBS += -L`our osx boost static lib folder` -lboost -``` - -###Make up mainwindow ui. +### Make up mainwindow ui. Make up a simple ui by drag and drop widget from `Widget box` in left side. We finally end up with this: @@ -101,7 +84,7 @@ It contains: * a `QPushButton` at the bottomright for sending message, named `sendBtn` -###Add Slots in mainwindow +### Add Slots in mainwindow Slots need to be added in `mainwindow` class to handle UI events.They are * click login button @@ -122,7 +105,7 @@ public Q_SLOTS: void OnMessageReturn(); ``` -###Connect UI event signal and slots together +### Connect UI event signal and slots together Open `mainwindow.ui` in Design mode. switch to `signals/slots` mode by check `Menu->Edit->Edit Signals/Slots` By press left mouse on widget and drag on to the window (cursor will become a sign of electrical ground), to open the connection editor. @@ -135,7 +118,7 @@ We finally end up with this: ![QT signals&slots](https://cldup.com/Vsb-UXG3FC.jpg) -###Adding UI refresh Signals/Slots +### Adding UI refresh Signals/Slots `sio::client`'s callbacks are not in UI thread. However, UI is required to be updated by those callbacks, so we need some `Signal` for non-UI thread to "request" `Slots` functions been called in UI thread. Say if we want to signal `QListWidgetItem` being added, add: ```C++ @@ -160,7 +143,7 @@ Then connect them in `MainWindow` constructor. connect(this,SIGNAL(RequestAddListItem(QListWidgetItem*)),this,SLOT(AddListItem(QListWidgetItem*))); ``` -###Init sio::client in MainWindow +### Init sio::client in MainWindow For single window applications, simply let `MainWindow` class holding the `sio::client` object: declare a `unique_ptr` member of `sio::client` and Several event handling functions in `mainwindow.h` @@ -214,7 +197,7 @@ MainWindow::MainWindow(QWidget *parent) : } ``` -###Managing connection state +### Managing connection state We have several connection listeners for connection events. First we want to send login message once we're connected, get the default `socket` from `client` to do that. @@ -256,7 +239,7 @@ MainWindow::~MainWindow() } ``` -###Handle socket.io events +### Handle socket.io events We'll need to handle socket.io events in our functions bind to socket.io events. For example, we need to show received messages to the `listView` @@ -278,7 +261,7 @@ void MainWindow::OnNewMessage(std::string const& name,message::ptr const& data,b } ``` -###Sending chat message +### Sending chat message When `sendBtn` is clicked, we need to send the text in `messageEdit` to chatroom. Add code to `SendBtnClicked()`: @@ -301,7 +284,7 @@ void MainWindow::SendBtnClicked() } ``` -###Further reading +### Further reading You can run [Demo project](https://github.com/socketio/socket.io-client-cpp/tree/master/examples/QT/SioChatDemo) to have a closer look. Before running, please follow the [instructions](../../README.md#with_cmake) to make the sioclient library. diff --git a/examples/QT/SioChatDemo/mainwindow.cpp b/examples/QT/SioChatDemo/mainwindow.cpp index fe7532b4..83a41652 100644 --- a/examples/QT/SioChatDemo/mainwindow.cpp +++ b/examples/QT/SioChatDemo/mainwindow.cpp @@ -137,7 +137,7 @@ void MainWindow::RemoveListItem(QListWidgetItem* item) } -void MainWindow::OnNewMessage(std::string const& name,message::ptr const& data,bool hasAck,message::ptr &ack_resp) +void MainWindow::OnNewMessage(std::string const& name,message::ptr const& data,bool hasAck,message::list &ack_resp) { if(data->get_flag() == message::flag_object) @@ -152,7 +152,7 @@ void MainWindow::OnNewMessage(std::string const& name,message::ptr const& data,b } } -void MainWindow::OnUserJoined(std::string const& name,message::ptr const& data,bool hasAck,message::ptr &ack_resp) +void MainWindow::OnUserJoined(std::string const& name,message::ptr const& data,bool hasAck,message::list &ack_resp) { if(data->get_flag() == message::flag_object) { @@ -181,7 +181,7 @@ void MainWindow::OnUserJoined(std::string const& name,message::ptr const& data,b } -void MainWindow::OnUserLeft(std::string const& name,message::ptr const& data,bool hasAck,message::ptr &ack_resp) +void MainWindow::OnUserLeft(std::string const& name,message::ptr const& data,bool hasAck,message::list &ack_resp) { if(data->get_flag() == message::flag_object) { @@ -209,7 +209,7 @@ void MainWindow::OnUserLeft(std::string const& name,message::ptr const& data,boo } } -void MainWindow::OnTyping(std::string const& name,message::ptr const& data,bool hasAck,message::ptr &ack_resp) +void MainWindow::OnTyping(std::string const& name,message::ptr const& data,bool hasAck,message::list &ack_resp) { if(m_typingItem == NULL) { @@ -223,7 +223,7 @@ void MainWindow::OnTyping(std::string const& name,message::ptr const& data,bool } } -void MainWindow::OnStopTyping(std::string const& name,message::ptr const& data,bool hasAck,message::ptr &ack_resp) +void MainWindow::OnStopTyping(std::string const& name,message::ptr const& data,bool hasAck,message::list &ack_resp) { if(m_typingItem != NULL) { @@ -232,7 +232,7 @@ void MainWindow::OnStopTyping(std::string const& name,message::ptr const& data,b } } -void MainWindow::OnLogin(std::string const& name,message::ptr const& data,bool hasAck,message::ptr &ack_resp) +void MainWindow::OnLogin(std::string const& name,message::ptr const& data,bool hasAck,message::list &ack_resp) { Q_EMIT RequestToggleInputs(true); int numUser = data->get_map()["numUsers"]->get_int(); diff --git a/examples/QT/SioChatDemo/mainwindow.h b/examples/QT/SioChatDemo/mainwindow.h index 49147add..df32afe8 100644 --- a/examples/QT/SioChatDemo/mainwindow.h +++ b/examples/QT/SioChatDemo/mainwindow.h @@ -41,12 +41,12 @@ private Q_SLOTS: void NicknameAccept(); void NicknameCancelled(); private: - void OnNewMessage(std::string const& name,message::ptr const& data,bool hasAck,message::ptr &ack_resp); - void OnUserJoined(std::string const& name,message::ptr const& data,bool hasAck,message::ptr &ack_resp); - void OnUserLeft(std::string const& name,message::ptr const& data,bool hasAck,message::ptr &ack_resp); - void OnTyping(std::string const& name,message::ptr const& data,bool hasAck,message::ptr &ack_resp); - void OnStopTyping(std::string const& name,message::ptr const& data,bool hasAck,message::ptr &ack_resp); - void OnLogin(std::string const& name,message::ptr const& data,bool hasAck,message::ptr &ack_resp); + void OnNewMessage(std::string const& name,message::ptr const& data,bool hasAck,message::list &ack_resp); + void OnUserJoined(std::string const& name,message::ptr const& data,bool hasAck,message::list &ack_resp); + void OnUserLeft(std::string const& name,message::ptr const& data,bool hasAck,message::list &ack_resp); + void OnTyping(std::string const& name,message::ptr const& data,bool hasAck,message::list &ack_resp); + void OnStopTyping(std::string const& name,message::ptr const& data,bool hasAck,message::list &ack_resp); + void OnLogin(std::string const& name,message::ptr const& data,bool hasAck,message::list &ack_resp); void OnConnected(std::string const& nsp); void OnClosed(client::close_reason const& reason); void OnFailed(); diff --git a/examples/iOS/SioChatDemo/SioChatDemo/CRViewController.mm b/examples/iOS/SioChatDemo/SioChatDemo/CRViewController.mm index 3b45442e..6bac43da 100644 --- a/examples/iOS/SioChatDemo/SioChatDemo/CRViewController.mm +++ b/examples/iOS/SioChatDemo/SioChatDemo/CRViewController.mm @@ -69,7 +69,7 @@ -(void) updateUser:(NSString*)user count:(NSInteger) num joinOrLeft:(BOOL) isJoi using namespace sio; -void OnNewMessage(CFTypeRef ctrl,string const& name,sio::message::ptr const& data,bool needACK,sio::message::ptr ackResp) +void OnNewMessage(CFTypeRef ctrl,string const& name,sio::message::ptr const& data,bool needACK,sio::message::list ackResp) { if(data->get_flag() == message::flag_object) { @@ -82,7 +82,7 @@ void OnNewMessage(CFTypeRef ctrl,string const& name,sio::message::ptr const& dat } -void OnTyping(CFTypeRef ctrl,string const& name,sio::message::ptr const& data,bool needACK,sio::message::ptr ackResp) +void OnTyping(CFTypeRef ctrl,string const& name,sio::message::ptr const& data,bool needACK,sio::message::list ackResp) { if(data->get_flag() == message::flag_object) { @@ -93,7 +93,7 @@ void OnTyping(CFTypeRef ctrl,string const& name,sio::message::ptr const& data,bo } } -void OnStopTyping(CFTypeRef ctrl,string const& name,sio::message::ptr const& data,bool needACK,sio::message::ptr ackResp) +void OnStopTyping(CFTypeRef ctrl,string const& name,sio::message::ptr const& data,bool needACK,sio::message::list ackResp) { if(data->get_flag() == message::flag_object) { @@ -104,7 +104,7 @@ void OnStopTyping(CFTypeRef ctrl,string const& name,sio::message::ptr const& dat } } -void OnUserJoined(CFTypeRef ctrl, string const& name, sio::message::ptr const& data, bool needACK, sio::message::ptr ackResp) +void OnUserJoined(CFTypeRef ctrl, string const& name, sio::message::ptr const& data, bool needACK, sio::message::list ackResp) { if(data->get_flag() == message::flag_object) { @@ -116,7 +116,7 @@ void OnUserJoined(CFTypeRef ctrl, string const& name, sio::message::ptr const& d } } -void OnUserLeft(CFTypeRef ctrl, string const& name, sio::message::ptr const& data, bool needACK, sio::message::ptr ackResp) +void OnUserLeft(CFTypeRef ctrl, string const& name, sio::message::ptr const& data, bool needACK, sio::message::list ackResp) { if(data->get_flag() == message::flag_object) { @@ -129,7 +129,7 @@ void OnUserLeft(CFTypeRef ctrl, string const& name, sio::message::ptr const& dat } -void OnLogin(CFTypeRef ctrl, string const& name, sio::message::ptr const& data, bool needACK, sio::message::ptr ackResp) +void OnLogin(CFTypeRef ctrl, string const& name, sio::message::ptr const& data, bool needACK, sio::message::list ackResp) { if(data->get_flag() == message::flag_object) { diff --git a/lib/asio b/lib/asio new file mode 160000 index 00000000..230c0d2a --- /dev/null +++ b/lib/asio @@ -0,0 +1 @@ +Subproject commit 230c0d2ae035c5ce1292233fcab03cea0d341264 diff --git a/lib/websocketpp b/lib/websocketpp index ac5d7ea5..56123c87 160000 --- a/lib/websocketpp +++ b/lib/websocketpp @@ -1 +1 @@ -Subproject commit ac5d7ea5af9734de965688b54a7860259887b537 +Subproject commit 56123c87598f8b1dd471be83ca841ceae07f95ba diff --git a/sioclientConfig.cmake.in b/sioclientConfig.cmake.in new file mode 100644 index 00000000..a28fad84 --- /dev/null +++ b/sioclientConfig.cmake.in @@ -0,0 +1,7 @@ + +@PACKAGE_INIT@ + +include(CMakeFindDependencyMacro) +@_find_dependency_calls@ + +include("${CMAKE_CURRENT_LIST_DIR}/sioclientTargets.cmake") \ No newline at end of file diff --git a/src/internal/sio_client_impl.cpp b/src/internal/sio_client_impl.cpp index 12d4c78a..02cceada 100644 --- a/src/internal/sio_client_impl.cpp +++ b/src/internal/sio_client_impl.cpp @@ -7,32 +7,39 @@ // #include "sio_client_impl.h" +#include #include -#include +#include #include #include // Comment this out to disable handshake logging to stdout -#if DEBUG || _DEBUG +#if (DEBUG || _DEBUG) && !defined(SIO_DISABLE_LOGGING) #define LOG(x) std::cout << x #else #define LOG(x) #endif -using boost::posix_time::milliseconds; +#if SIO_TLS +// If using Asio's SSL support, you will also need to add this #include. +// Source: http://think-async.com/Asio/asio-1.10.6/doc/asio/using.html +// #include +#endif + +using std::chrono::milliseconds; using namespace std; namespace sio { /*************************public:*************************/ - client_impl::client_impl() : - m_con_state(con_closed), + client_impl::client_impl(client_options const& options) : m_ping_interval(0), m_ping_timeout(0), m_network_thread(), - m_reconn_attempts(0xFFFFFFFF), - m_reconn_made(0), + m_con_state(con_closed), m_reconn_delay(5000), - m_reconn_delay_max(25000) + m_reconn_delay_max(25000), + m_reconn_attempts(0xFFFFFFFF), + m_reconn_made(0) { using websocketpp::log::alevel; #ifndef DEBUG @@ -40,21 +47,25 @@ namespace sio m_client.set_access_channels(alevel::connect|alevel::disconnect|alevel::app); #endif // Initialize the Asio transport policy - m_client.init_asio(); + if (options.io_context != nullptr) { + m_client.init_asio(options.io_context); + } else { + m_client.init_asio(); + } // Bind the clients we are using - using websocketpp::lib::placeholders::_1; - using websocketpp::lib::placeholders::_2; - m_client.set_open_handler(lib::bind(&client_impl::on_open,this,_1)); - m_client.set_close_handler(lib::bind(&client_impl::on_close,this,_1)); - m_client.set_fail_handler(lib::bind(&client_impl::on_fail,this,_1)); - m_client.set_message_handler(lib::bind(&client_impl::on_message,this,_1,_2)); + using std::placeholders::_1; + using std::placeholders::_2; + m_client.set_open_handler(std::bind(&client_impl::on_open,this,_1)); + m_client.set_close_handler(std::bind(&client_impl::on_close,this,_1)); + m_client.set_fail_handler(std::bind(&client_impl::on_fail,this,_1)); + m_client.set_message_handler(std::bind(&client_impl::on_message,this,_1,_2)); #if SIO_TLS - m_client.set_tls_init_handler(lib::bind(&client_impl::on_tls_init,this,_1)); + m_client.set_tls_init_handler(std::bind(&client_impl::on_tls_init,this,_1)); #endif - m_packet_mgr.set_decode_callback(lib::bind(&client_impl::on_decode,this,_1)); + m_packet_mgr.set_decode_callback(std::bind(&client_impl::on_decode,this,_1)); - m_packet_mgr.set_encode_callback(lib::bind(&client_impl::on_encode,this,_1,_2)); + m_packet_mgr.set_encode_callback(std::bind(&client_impl::on_encode,this,_1,_2)); } client_impl::~client_impl() @@ -62,8 +73,15 @@ namespace sio this->sockets_invoke_void(&sio::socket::on_close); sync_close(); } + + void client_impl::set_proxy_basic_auth(const std::string& uri, const std::string& username, const std::string& password) + { + m_proxy_base_url = uri; + m_proxy_basic_username = username; + m_proxy_basic_password = password; + } - void client_impl::connect(const string& uri, const map& query) + void client_impl::connect(const string& uri, const map& query, const map& headers, const message::ptr& auth) { if(m_reconn_timer) { @@ -95,13 +113,18 @@ namespace sio query_str.append("&"); query_str.append(it->first); query_str.append("="); - query_str.append(it->second); + string query_str_value=encode_query_string(it->second); + query_str.append(query_str_value); } m_query_string=move(query_str); + m_http_headers = headers; + m_auth = auth; + this->reset_states(); - m_client.get_io_service().dispatch(lib::bind(&client_impl::connect_impl,this,uri,m_query_string)); - m_network_thread.reset(new thread(lib::bind(&client_impl::run_loop,this)));//uri lifecycle? + m_abort_retries = false; + m_client.get_io_service().dispatch(std::bind(&client_impl::connect_impl,this,uri,m_query_string)); + m_network_thread.reset(new thread(std::bind(&client_impl::run_loop,this)));//uri lifecycle? } @@ -130,7 +153,7 @@ namespace sio } else { - pair p(aux,shared_ptr(new sio::socket(this,aux))); + pair p(aux,shared_ptr(new sio::socket(this,aux,m_auth))); return (m_sockets.insert(p).first)->second; } } @@ -138,15 +161,17 @@ namespace sio void client_impl::close() { m_con_state = con_closing; + m_abort_retries = true; this->sockets_invoke_void(&sio::socket::close); - m_client.get_io_service().dispatch(lib::bind(&client_impl::close_impl, this,close::status::normal,"End by user")); + m_client.get_io_service().dispatch(std::bind(&client_impl::close_impl, this,close::status::normal,"End by user")); } void client_impl::sync_close() { m_con_state = con_closing; + m_abort_retries = true; this->sockets_invoke_void(&sio::socket::close); - m_client.get_io_service().dispatch(lib::bind(&client_impl::close_impl, this,close::status::normal,"End by user")); + m_client.get_io_service().dispatch(std::bind(&client_impl::close_impl, this,close::status::normal,"End by user")); if(m_network_thread) { m_network_thread->join(); @@ -154,6 +179,22 @@ namespace sio } } + void client_impl::set_logs_default() + { + m_client.clear_access_channels(websocketpp::log::alevel::all); + m_client.set_access_channels(websocketpp::log::alevel::connect | websocketpp::log::alevel::disconnect | websocketpp::log::alevel::app); + } + + void client_impl::set_logs_quiet() + { + m_client.clear_access_channels(websocketpp::log::alevel::all); + } + + void client_impl::set_logs_verbose() + { + m_client.set_access_channels(websocketpp::log::alevel::all); + } + /*************************protected:*************************/ void client_impl::send(packet& p) { @@ -170,7 +211,7 @@ namespace sio } } - boost::asio::io_service& client_impl::get_io_service() + asio::io_service& client_impl::get_io_service() { return m_client.get_io_service(); } @@ -205,13 +246,23 @@ namespace sio #else ss<<"ws://"; #endif - if (m_sid.size()==0) { - ss<0){ + ss<<"&sid="<set_proxy_basic_auth(m_proxy_basic_username, m_proxy_basic_password, ec); + if (ec) { + m_client.get_alog().write(websocketpp::log::alevel::app, + "Set Proxy Basic Auth Error: " + ec.message()); + break; + } + } + } + m_client.connect(con); return; } @@ -253,13 +325,6 @@ namespace sio { if(m_con_state == con_opened) { - //delay the ping, since we already have message to send. - boost::system::error_code timeout_ec; - if(m_ping_timer) - { - m_ping_timer->expires_from_now(milliseconds(m_ping_interval),timeout_ec); - m_ping_timer->async_wait(lib::bind(&client_impl::ping,this,lib::placeholders::_1)); - } lib::error_code ec; m_client.send(m_con,*payload_ptr,opcode,ec); if(ec) @@ -269,46 +334,17 @@ namespace sio } } - void client_impl::ping(const boost::system::error_code& ec) - { - if(ec || m_con.expired()) - { - LOG("ping exit,con is expired?"< payload) - { - lib::error_code ec; - this->m_client.send(this->m_con, *payload, frame::opcode::text, ec); - }); - if(m_ping_timer) - { - boost::system::error_code e_code; - m_ping_timer->expires_from_now(milliseconds(m_ping_interval), e_code); - m_ping_timer->async_wait(lib::bind(&client_impl::ping,this,lib::placeholders::_1)); - } - if(!m_ping_timeout_timer) - { - m_ping_timeout_timer.reset(new boost::asio::deadline_timer(m_client.get_io_service())); - boost::system::error_code timeout_ec; - m_ping_timeout_timer->expires_from_now(milliseconds(m_ping_timeout), timeout_ec); - m_ping_timeout_timer->async_wait(lib::bind(&client_impl::timeout_pong, this,lib::placeholders::_1)); - } - } - - void client_impl::timeout_pong(const boost::system::error_code &ec) + void client_impl::timeout_ping(const asio::error_code &ec) { if(ec) { return; } - LOG("Pong timeout"<reset_states(); LOG("Reconnecting..."<(static_cast(m_reconn_delay * pow(1.5,m_reconn_made)),m_reconn_delay_max); + unsigned reconn_made = min(m_reconn_made,32);//protect the pow result to be too big. + return static_cast(min(m_reconn_delay * pow(1.5,reconn_made),m_reconn_delay_max)); } socket::ptr client_impl::get_socket_locked(string const& nsp) @@ -357,21 +394,27 @@ namespace sio } } - void client_impl::on_fail(connection_hdl con) + void client_impl::on_fail(connection_hdl) { + if (m_con_state == con_closing) { + LOG("Connection failed while closing." << endl); + this->close(); + return; + } + m_con.reset(); m_con_state = con_closed; this->sockets_invoke_void(&sio::socket::on_disconnect); LOG("Connection failed." << endl); - if(m_reconn_madenext_delay(); if(m_reconnect_listener) m_reconnect_listener(m_reconn_made,delay); - m_reconn_timer.reset(new boost::asio::deadline_timer(m_client.get_io_service())); - boost::system::error_code ec; + m_reconn_timer.reset(new asio::steady_timer(m_client.get_io_service())); + asio::error_code ec; m_reconn_timer->expires_from_now(milliseconds(delay), ec); - m_reconn_timer->async_wait(lib::bind(&client_impl::timeout_reconnect,this,lib::placeholders::_1)); + m_reconn_timer->async_wait(std::bind(&client_impl::timeout_reconnect,this, std::placeholders::_1)); } else { @@ -381,6 +424,12 @@ namespace sio void client_impl::on_open(connection_hdl con) { + if (m_con_state == con_closing) { + LOG("Connection opened while closing." << endl); + this->close(); + return; + } + LOG("Connected." << endl); m_con_state = con_opened; m_con = con; @@ -393,6 +442,7 @@ namespace sio void client_impl::on_close(connection_hdl con) { LOG("Client Disconnected." << endl); + con_state m_con_state_was = m_con_state; m_con_state = con_closed; lib::error_code ec; close::status::value code = close::status::normal; @@ -408,7 +458,11 @@ namespace sio m_con.reset(); this->clear_timers(); client::close_reason reason; - if(code == close::status::normal) + + // If we initiated the close, no matter what the close status was, + // we'll consider it a normal close. (When using TLS, we can + // sometimes get a TLS Short Read error when closing.) + if(code == close::status::normal || m_con_state_was == con_closing) { this->sockets_invoke_void(&sio::socket::on_disconnect); reason = client::close_reason_normal; @@ -416,15 +470,15 @@ namespace sio else { this->sockets_invoke_void(&sio::socket::on_disconnect); - if(m_reconn_madenext_delay(); if(m_reconnect_listener) m_reconnect_listener(m_reconn_made,delay); - m_reconn_timer.reset(new boost::asio::deadline_timer(m_client.get_io_service())); - boost::system::error_code ec; + m_reconn_timer.reset(new asio::steady_timer(m_client.get_io_service())); + asio::error_code ec; m_reconn_timer->expires_from_now(milliseconds(delay), ec); - m_reconn_timer->async_wait(lib::bind(&client_impl::timeout_reconnect,this,lib::placeholders::_1)); + m_reconn_timer->async_wait(std::bind(&client_impl::timeout_reconnect,this, std::placeholders::_1)); return; } reason = client::close_reason_drop; @@ -436,13 +490,8 @@ namespace sio } } - void client_impl::on_message(connection_hdl con, client_type::message_ptr msg) + void client_impl::on_message(connection_hdl, client_type::message_ptr msg) { - if (m_ping_timeout_timer) { - boost::system::error_code ec; - m_ping_timeout_timer->expires_from_now(milliseconds(m_ping_timeout),ec); - m_ping_timeout_timer->async_wait(lib::bind(&client_impl::timeout_pong, this,lib::placeholders::_1)); - } // Parse the incoming message according to socket.IO rules m_packet_mgr.put_payload(msg->get_payload()); } @@ -479,26 +528,27 @@ namespace sio m_ping_timeout = 60000; } - m_ping_timer.reset(new boost::asio::deadline_timer(m_client.get_io_service())); - boost::system::error_code ec; - m_ping_timer->expires_from_now(milliseconds(m_ping_interval), ec); - if(ec)LOG("ec:"<async_wait(lib::bind(&client_impl::ping,this,lib::placeholders::_1)); - LOG("On handshake,sid:"< payload) { - m_ping_timeout_timer->cancel(); - m_ping_timeout_timer.reset(); - } + this->m_client.send(this->m_con, *payload, frame::opcode::text); + }); + + // Reset the ping timeout. + update_ping_timeout_timer(); } void client_impl::on_decode(packet const& p) @@ -507,6 +557,19 @@ namespace sio { case packet::frame_message: { + // Special event for sid sync + if (p.get_type() == packet::type_connect) { + auto message = p.get_message(); + if (message && message->get_flag() == message::flag_object) + { + const object_message* obj_ptr = static_cast(message.get()); + const std::map* values = &(obj_ptr->get_map()); + auto it = values->find("sid"); + if (it != values->end()) { + m_sid = std::static_pointer_cast(it->second)->get_string(); + } + } + } socket::ptr so_ptr = get_socket_locked(p.get_nsp()); if(so_ptr)so_ptr->on_message_packet(p); break; @@ -518,8 +581,8 @@ namespace sio //FIXME how to deal? this->close_impl(close::status::abnormal_close, "End by server"); break; - case packet::frame_pong: - this->on_pong(); + case packet::frame_ping: + this->on_ping(); break; default: @@ -530,23 +593,28 @@ namespace sio void client_impl::on_encode(bool isBinary,shared_ptr const& payload) { LOG("encoded payload length:"<length()<cancel(ec); m_ping_timeout_timer.reset(); } - if(m_ping_timer) - { - m_ping_timer->cancel(ec); - m_ping_timer.reset(); + } + + void client_impl::update_ping_timeout_timer() { + if (!m_ping_timeout_timer) { + m_ping_timeout_timer = std::unique_ptr(new asio::steady_timer(get_io_service())); } + + asio::error_code ec; + m_ping_timeout_timer->expires_from_now(milliseconds(m_ping_interval + m_ping_timeout), ec); + m_ping_timeout_timer->async_wait(std::bind(&client_impl::timeout_ping, this, std::placeholders::_1)); } void client_impl::reset_states() @@ -559,11 +627,12 @@ namespace sio #if SIO_TLS client_impl::context_ptr client_impl::on_tls_init(connection_hdl conn) { - context_ptr ctx = context_ptr(new boost::asio::ssl::context(boost::asio::ssl::context::tlsv1)); - boost::system::error_code ec; - ctx->set_options(boost::asio::ssl::context::default_workarounds | - boost::asio::ssl::context::no_sslv2 | - boost::asio::ssl::context::single_dh_use,ec); + context_ptr ctx = context_ptr(new asio::ssl::context(asio::ssl::context::tls)); + asio::error_code ec; + ctx->set_options(asio::ssl::context::default_workarounds | + asio::ssl::context::no_tlsv1 | + asio::ssl::context::no_tlsv1_1 | + asio::ssl::context::single_dh_use,ec); if(ec) { cerr<<"Init tls failed,reason:"<< ec.message()<= 'a' && c <= 'z') || (c>= 'A' && c<= 'Z') || (c >= '0' && c<= '9')){ + ss << c; + } else { + ss << '%' << std::uppercase << std::setw(2) << int((unsigned char) c) << std::nouppercase; + } + } + ss << std::dec; + return ss.str(); + } } diff --git a/src/internal/sio_client_impl.h b/src/internal/sio_client_impl.h index fb5910cf..f4f02aa5 100644 --- a/src/internal/sio_client_impl.h +++ b/src/internal/sio_client_impl.h @@ -4,7 +4,6 @@ #include #ifdef _WIN32 #define _WEBSOCKETPP_CPP11_THREAD_ -#define BOOST_ALL_NO_LIB //#define _WEBSOCKETPP_CPP11_RANDOM_DEVICE_ #define _WEBSOCKETPP_NO_CPP11_FUNCTIONAL_ #define INTIALIZER(__TYPE__) @@ -30,8 +29,16 @@ typedef websocketpp::config::asio_tls_client client_config; typedef websocketpp::config::asio_client client_config; #endif //SIO_TLS #endif //DEBUG -#include +#if SIO_TLS +#include +#endif + +#include +#include +#include + +#include #include #include #include @@ -55,7 +62,7 @@ namespace sio con_closed }; - client_impl(); + client_impl(client_options const& options); ~client_impl(); @@ -97,7 +104,8 @@ namespace sio } // Client Functions - such as send, etc. - void connect(const std::string& uri, const std::map& queryString); + void connect(const std::string& uri, const std::map& queryString, + const std::map& httpExtraHeaders, const message::ptr& auth); sio::socket::ptr const& socket(const std::string& nsp); @@ -115,13 +123,21 @@ namespace sio void set_reconnect_delay(unsigned millis) {m_reconn_delay = millis;if(m_reconn_delay_maxmillis) m_reconn_delay = millis;} - + + void set_logs_default(); + + void set_logs_quiet(); + + void set_logs_verbose(); + + void set_proxy_basic_auth(const std::string& uri, const std::string& username, const std::string& password); + protected: void send(packet& p); void remove_socket(std::string const& nsp); - boost::asio::io_service& get_io_service(); + asio::io_service& get_io_service(); void on_socket_closed(std::string const& nsp); @@ -136,11 +152,11 @@ namespace sio void send_impl(std::shared_ptr const& payload_ptr,frame::opcode::value opcode); - void ping(const boost::system::error_code& ec); + void ping(const asio::error_code& ec); - void timeout_pong(const boost::system::error_code& ec); + void timeout_ping(const asio::error_code& ec); - void timeout_reconnect(boost::system::error_code const& ec); + void timeout_reconnect(asio::error_code const& ec); unsigned next_delay() const; @@ -163,18 +179,23 @@ namespace sio //socketio callbacks void on_handshake(message::ptr const& message); - void on_pong(); + void on_ping(); void reset_states(); void clear_timers(); + + void update_ping_timeout_timer(); #if SIO_TLS - typedef websocketpp::lib::shared_ptr context_ptr; + typedef websocketpp::lib::shared_ptr context_ptr; context_ptr on_tls_init(connection_hdl con); #endif + // Percent encode query string + std::string encode_query_string(const std::string &query); + // Connection pointer for client functions. connection_hdl m_con; client_type m_client; @@ -182,6 +203,11 @@ namespace sio std::string m_sid; std::string m_base_url; std::string m_query_string; + std::map m_http_headers; + message::ptr m_auth; + std::string m_proxy_base_url; + std::string m_proxy_basic_username; + std::string m_proxy_basic_password; unsigned int m_ping_interval; unsigned int m_ping_timeout; @@ -190,11 +216,9 @@ namespace sio packet_manager m_packet_mgr; - std::unique_ptr m_ping_timer; - - std::unique_ptr m_ping_timeout_timer; + std::unique_ptr m_ping_timeout_timer; - std::unique_ptr m_reconn_timer; + std::unique_ptr m_reconn_timer; con_state m_con_state; @@ -218,7 +242,9 @@ namespace sio unsigned m_reconn_attempts; unsigned m_reconn_made; - + + std::atomic m_abort_retries { false }; + friend class sio::client; friend class sio::socket; }; diff --git a/src/internal/sio_packet.cpp b/src/internal/sio_packet.cpp old mode 100755 new mode 100644 index 0f7f16d5..da73edb4 --- a/src/internal/sio_packet.cpp +++ b/src/internal/sio_packet.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #define kBIN_PLACE_HOLDER "_placeholder" @@ -18,6 +19,16 @@ namespace sio using namespace std; void accept_message(message const& msg,Value& val, Document& doc,vector >& buffers); + void accept_bool_message(bool_message const& msg, Value& val) + { + val.SetBool(msg.get_bool()); + } + + void accept_null_message(Value& val) + { + val.SetNull(); + } + void accept_int_message(int_message const& msg, Value& val) { val.SetInt64(msg.get_int()); @@ -43,13 +54,7 @@ namespace sio Value numVal; numVal.SetInt((int)buffers.size()); val.AddMember("num", numVal, doc.GetAllocator()); - //FIXME can not avoid binary copy here. - shared_ptr write_buffer = make_shared(); - write_buffer->reserve(msg.get_binary()->size()+1); - char frame_char = packet::frame_message; - write_buffer->append(&frame_char,1); - write_buffer->append(*(msg.get_binary())); - buffers.push_back(write_buffer); + buffers.push_back(msg.get_binary()); } void accept_array_message(array_message const& msg,Value& val,Document& doc,vector >& buffers) @@ -94,6 +99,16 @@ namespace sio accept_string_message(*(static_cast(msg_ptr)), val); break; } + case message::flag_boolean: + { + accept_bool_message(*(static_cast(msg_ptr)), val); + break; + } + case message::flag_null: + { + accept_null_message(val); + break; + } case message::flag_binary: { accept_binary_message(*(static_cast(msg_ptr)), val,doc,buffers); @@ -162,6 +177,14 @@ namespace sio } return ptr; } + else if(value.IsBool()) + { + return bool_message::create(value.GetBool()); + } + else if(value.IsNull()) + { + return null_message::create(); + } return message::ptr(); } @@ -169,8 +192,8 @@ namespace sio _frame(frame_message), _type((isAck?type_ack : type_event) | type_undetermined), _nsp(nsp), - _message(msg), _pack_id(pack_id), + _message(msg), _pending_buffers(0) { assert((!isAck @@ -181,8 +204,8 @@ namespace sio _frame(frame_message), _type(type), _nsp(nsp), - _message(msg), _pack_id(-1), + _message(msg), _pending_buffers(0) { @@ -225,7 +248,7 @@ namespace sio { if (_pending_buffers > 0) { assert(is_binary_message(buf_payload));//this is ensured by outside. - _buffers.push_back(std::make_shared(buf_payload.data()+1,buf_payload.size()-1)); + _buffers.push_back(std::make_shared(buf_payload.data(),buf_payload.size())); _pending_buffers--; if (_pending_buffers == 0) { @@ -259,7 +282,7 @@ namespace sio pos++; if (_type == type_binary_event || _type == type_binary_ack) { size_t score_pos = payload_ptr.find('-'); - _pending_buffers = stoi(payload_ptr.substr(pos,score_pos)); + _pending_buffers = static_cast(std::stoul(payload_ptr.substr(pos, score_pos - pos))); pos = score_pos+1; } } @@ -299,11 +322,18 @@ namespace sio if(pos(payload_ptr.data() + json_pos, payload_ptr.length() - json_pos)); + _buffers.push_back(make_shared(payload_ptr.data() + json_pos, payload_ptr.length() - json_pos)); return true; } else diff --git a/src/internal/sio_packet.h b/src/internal/sio_packet.h old mode 100755 new mode 100644 diff --git a/src/sio_client.cpp b/src/sio_client.cpp old mode 100755 new mode 100644 index 1c7636a7..1cdb7f7c --- a/src/sio_client.cpp +++ b/src/sio_client.cpp @@ -8,13 +8,14 @@ #include "internal/sio_client_impl.h" using namespace websocketpp; -using boost::posix_time::milliseconds; using std::stringstream; namespace sio { - client::client(): - m_impl(new client_impl()) + client::client() : m_impl(new client_impl({})) {} + + client::client(client_options const& options): + m_impl(new client_impl(options)) { } @@ -67,16 +68,42 @@ namespace sio { m_impl->clear_socket_listeners(); } + + void client::set_proxy_basic_auth(const std::string& uri, const std::string& username, const std::string& password) + { + m_impl->set_proxy_basic_auth(uri, username, password); + } void client::connect(const std::string& uri) { - const std::map query; - m_impl->connect(uri, query); + m_impl->connect(uri, {}, {}, {}); + } + + void client::connect(const std::string& uri, const message::ptr& auth) + { + m_impl->connect(uri, {}, {}, auth); } void client::connect(const std::string& uri, const std::map& query) { - m_impl->connect(uri, query); + m_impl->connect(uri, query, {}, {}); + } + + void client::connect(const std::string& uri, const std::map& query, const message::ptr& auth) + { + m_impl->connect(uri, query, {}, auth); + } + + void client::connect(const std::string& uri, const std::map& query, + const std::map& http_extra_headers) + { + m_impl->connect(uri, query, http_extra_headers, {}); + } + + void client::connect(const std::string& uri, const std::map& query, + const std::map& http_extra_headers, const message::ptr& auth) + { + m_impl->connect(uri, query, http_extra_headers, auth); } socket::ptr const& client::socket(const std::string& nsp) @@ -119,5 +146,20 @@ namespace sio { m_impl->set_reconnect_delay_max(millis); } - + + void client::set_logs_default() + { + m_impl->set_logs_default(); + } + + void client::set_logs_quiet() + { + m_impl->set_logs_quiet(); + } + + void client::set_logs_verbose() + { + m_impl->set_logs_verbose(); + } + } diff --git a/src/sio_client.h b/src/sio_client.h old mode 100755 new mode 100644 index 1cf33ff7..a8cbb9fe --- a/src/sio_client.h +++ b/src/sio_client.h @@ -11,9 +11,17 @@ #include "sio_message.h" #include "sio_socket.h" +namespace asio { + class io_context; +} + namespace sio { class client_impl; + + struct client_options { + asio::io_context* io_context = nullptr; + }; class client { public: @@ -32,6 +40,7 @@ namespace sio typedef std::function socket_listener; client(); + client(client_options const& options); ~client(); //set listeners and event bindings. @@ -56,14 +65,30 @@ namespace sio // Client Functions - such as send, etc. void connect(const std::string& uri); + void connect(const std::string& uri, const message::ptr& auth); + void connect(const std::string& uri, const std::map& query); + void connect(const std::string& uri, const std::map& query, const message::ptr& auth); + + void connect(const std::string& uri, const std::map& query, + const std::map& http_extra_headers); + + void connect(const std::string& uri, const std::map& query, + const std::map& http_extra_headers, const message::ptr& auth); + void set_reconnect_attempts(int attempts); void set_reconnect_delay(unsigned millis); void set_reconnect_delay_max(unsigned millis); - + + void set_logs_default(); + + void set_logs_quiet(); + + void set_logs_verbose(); + sio::socket::ptr const& socket(const std::string& nsp = ""); // Closes the connection @@ -71,14 +96,16 @@ namespace sio void sync_close(); + void set_proxy_basic_auth(const std::string& uri, const std::string& username, const std::string& password); + bool opened() const; std::string const& get_sessionid() const; private: //disable copy constructor and assign operator. - client(client const& cl){} - void operator=(client const& cl){} + client(client const&){} + void operator=(client const&){} client_impl* m_impl; }; diff --git a/src/sio_message.h b/src/sio_message.h old mode 100755 new mode 100644 index 8d829e15..26e7115c --- a/src/sio_message.h +++ b/src/sio_message.h @@ -11,10 +11,9 @@ #include #include #include +#include namespace sio { - using namespace std; - class message { public: @@ -25,84 +24,131 @@ namespace sio flag_string, flag_binary, flag_array, - flag_object + flag_object, + flag_boolean, + flag_null }; - + + virtual ~message(){}; + class list; flag get_flag() const { return _flag; } - - typedef shared_ptr ptr; - + + typedef std::shared_ptr ptr; + + virtual bool get_bool() const + { + assert(false); + return false; + } + virtual int64_t get_int() const { assert(false); return 0; } - + virtual double get_double() const { assert(false); return 0; } - - virtual string const& get_string() const + + virtual std::string const& get_string() const { assert(false); - static string s_empty_string; + static std::string s_empty_string; s_empty_string.clear(); return s_empty_string; } - - virtual shared_ptr const& get_binary() const + + virtual std::shared_ptr const& get_binary() const { assert(false); - static shared_ptr s_empty_binary; + static std::shared_ptr s_empty_binary; s_empty_binary = nullptr; return s_empty_binary; } - - virtual const vector& get_vector() const + + virtual const std::vector& get_vector() const { assert(false); - static vector s_empty_vector; + static std::vector s_empty_vector; s_empty_vector.clear(); return s_empty_vector; } - virtual vector& get_vector() + virtual std::vector& get_vector() { assert(false); - static vector s_empty_vector; + static std::vector s_empty_vector; s_empty_vector.clear(); return s_empty_vector; } - - virtual const map& get_map() const + + virtual const std::map& get_map() const { assert(false); - static map s_empty_map; + static std::map s_empty_map; s_empty_map.clear(); return s_empty_map; } - - virtual map& get_map() + + virtual std::map& get_map() { assert(false); - static map s_empty_map; + static std::map s_empty_map; s_empty_map.clear(); return s_empty_map; } private: flag _flag; - + protected: message(flag f):_flag(f){} }; - + + class null_message : public message + { + protected: + null_message() + :message(flag_null) + { + } + + public: + static message::ptr create() + { + return ptr(new null_message()); + } + }; + + class bool_message : public message + { + bool _v; + + protected: + bool_message(bool v) + :message(flag_boolean),_v(v) + { + } + + public: + static message::ptr create(bool v) + { + return ptr(new bool_message(v)); + } + + bool get_bool() const override + { + return _v; + } + }; + class int_message : public message { int64_t _v; @@ -111,19 +157,24 @@ namespace sio :message(flag_integer),_v(v) { } - + public: static message::ptr create(int64_t v) { return ptr(new int_message(v)); } - - int64_t get_int() const + + int64_t get_int() const override { return _v; } + + double get_double() const override + { + return static_cast(_v);//add double accessor for integer. + } }; - + class double_message : public message { double _v; @@ -131,94 +182,164 @@ namespace sio :message(flag_double),_v(v) { } - + public: static message::ptr create(double v) { return ptr(new double_message(v)); } - - double get_double() const + + double get_double() const override { return _v; } }; - + class string_message : public message { - string _v; - string_message(string const& v) + std::string _v; + string_message(std::string const& v) :message(flag_string),_v(v) { } - string_message(string&& v) - :message(flag_string),_v(move(v)) + string_message(std::string&& v) + :message(flag_string),_v(std::move(v)) { } public: - static message::ptr create(string const& v) + static message::ptr create(std::string const& v) { return ptr(new string_message(v)); } - static message::ptr create(string&& v) + static message::ptr create(std::string&& v) { - return ptr(new string_message(move(v))); + return ptr(new string_message(std::move(v))); } - - string const& get_string() const + + std::string const& get_string() const override { return _v; } }; - + class binary_message : public message { - shared_ptr _v; - binary_message(shared_ptr const& v) + std::shared_ptr _v; + binary_message(std::shared_ptr const& v) :message(flag_binary),_v(v) { } public: - static message::ptr create(shared_ptr const& v) + static message::ptr create(std::shared_ptr const& v) { return ptr(new binary_message(v)); } - - shared_ptr const& get_binary() const + + std::shared_ptr const& get_binary() const override { return _v; } }; - + class array_message : public message { - vector _v; + std::vector _v; array_message():message(flag_array) { } - + public: static message::ptr create() { return ptr(new array_message()); } - - vector& get_vector() + + void push(message::ptr const& msg) + { + if(msg) + _v.push_back(msg); + } + + void push(const std::string& text) + { + _v.push_back(string_message::create(text)); + } + + void push(std::string&& text) + { + _v.push_back(string_message::create(std::move(text))); + } + + void push(std::shared_ptr const& binary) + { + if(binary) + _v.push_back(binary_message::create(binary)); + } + + void push(std::shared_ptr const& binary) + { + if(binary) + _v.push_back(binary_message::create(binary)); + } + + void insert(size_t pos,message::ptr const& msg) + { + _v.insert(_v.begin()+pos, msg); + } + + void insert(size_t pos,const std::string& text) + { + _v.insert(_v.begin()+pos, string_message::create(text)); + } + + void insert(size_t pos,std::string&& text) + { + _v.insert(_v.begin()+pos, string_message::create(std::move(text))); + } + + void insert(size_t pos,std::shared_ptr const& binary) + { + if(binary) + _v.insert(_v.begin()+pos, binary_message::create(binary)); + } + + void insert(size_t pos,std::shared_ptr const& binary) + { + if(binary) + _v.insert(_v.begin()+pos, binary_message::create(binary)); + } + + size_t size() const + { + return _v.size(); + } + + const message::ptr& at(size_t i) const + { + return _v[i]; + } + + const message::ptr& operator[] (size_t i) const + { + return _v[i]; + } + + std::vector& get_vector() override { return _v; } - - const vector& get_vector() const + + const std::vector& get_vector() const override { return _v; } }; - + class object_message : public message { - map _v; + std::map _v; object_message() : message(flag_object) { } @@ -227,13 +348,64 @@ namespace sio { return ptr(new object_message()); } - - map& get_map() + + void insert(const std::string & key,message::ptr const& msg) + { + _v[key] = msg; + } + + void insert(const std::string & key,const std::string& text) + { + _v[key] = string_message::create(text); + } + + void insert(const std::string & key,std::string&& text) + { + _v[key] = string_message::create(std::move(text)); + } + + void insert(const std::string & key,std::shared_ptr const& binary) + { + if(binary) + _v[key] = binary_message::create(binary); + } + + void insert(const std::string & key,std::shared_ptr const& binary) + { + if(binary) + _v[key] = binary_message::create(binary); + } + + bool has(const std::string & key) + { + return _v.find(key) != _v.end(); + } + + const message::ptr& at(const std::string & key) const + { + static std::shared_ptr not_found; + + std::map::const_iterator it = _v.find(key); + if (it != _v.cend()) return it->second; + return not_found; + } + + const message::ptr& operator[] (const std::string & key) const + { + return at(key); + } + + bool has(const std::string & key) const + { + return _v.find(key) != _v.end(); + } + + std::map& get_map() override { return _v; } - - const map& get_map() const + + const std::map& get_map() const override { return _v; } @@ -246,7 +418,7 @@ namespace sio { } - list(nullptr_t) + list(std::nullptr_t) { } @@ -256,50 +428,106 @@ namespace sio } + list & operator= (const message::list && rhs) + { + m_vector = std::move(rhs.m_vector); + return *this; + } + + template + list(T&& content, + typename std::enable_if,typename std::remove_reference::type>::value>::type* = 0): + m_vector(std::forward(content)) + { + } + list(message::list const& rhs): m_vector(rhs.m_vector) { } - list(message::ptr const& message) + list(message::ptr const& msg) + { + if(msg) + m_vector.push_back(msg); + } + + list(const std::string& text) + { + m_vector.push_back(string_message::create(text)); + } + + list(std::string&& text) + { + m_vector.push_back(string_message::create(std::move(text))); + } + + list(std::shared_ptr const& binary) + { + if(binary) + m_vector.push_back(binary_message::create(binary)); + } + + list(std::shared_ptr const& binary) + { + if(binary) + m_vector.push_back(binary_message::create(binary)); + } + + void push(message::ptr const& msg) { - if(message) - m_vector.push_back(message); + if(msg) + m_vector.push_back(msg); } - list(const string& text) + void push(const std::string& text) { m_vector.push_back(string_message::create(text)); } - list(string&& text) + void push(std::string&& text) { - m_vector.push_back(string_message::create(move(text))); + m_vector.push_back(string_message::create(std::move(text))); } - list(shared_ptr const& binary) + void push(std::shared_ptr const& binary) { if(binary) m_vector.push_back(binary_message::create(binary)); } - list(shared_ptr const& binary) + void push(std::shared_ptr const& binary) { if(binary) m_vector.push_back(binary_message::create(binary)); } - void push(message::ptr const& message) + void insert(size_t pos,message::ptr const& msg) + { + m_vector.insert(m_vector.begin()+pos, msg); + } + + void insert(size_t pos,const std::string& text) + { + m_vector.insert(m_vector.begin()+pos, string_message::create(text)); + } + + void insert(size_t pos,std::string&& text) { - if(message) - m_vector.push_back(message); + m_vector.insert(m_vector.begin()+pos, string_message::create(std::move(text))); + } + void insert(size_t pos,std::shared_ptr const& binary) + { + if(binary) + m_vector.insert(m_vector.begin()+pos, binary_message::create(binary)); } - void insert(size_t pos,message::ptr const& message) + void insert(size_t pos,std::shared_ptr const& binary) { - m_vector.insert(m_vector.begin()+pos, message); + if(binary) + m_vector.insert(m_vector.begin()+pos, binary_message::create(binary)); } size_t size() const @@ -317,7 +545,7 @@ namespace sio return m_vector[i]; } - message::ptr to_array_message(string const& event_name) const + message::ptr to_array_message(std::string const& event_name) const { message::ptr arr = array_message::create(); arr->get_vector().push_back(string_message::create(event_name)); @@ -325,8 +553,15 @@ namespace sio return arr; } + message::ptr to_array_message() const + { + message::ptr arr = array_message::create(); + arr->get_vector().insert(arr->get_vector().end(),m_vector.begin(),m_vector.end()); + return arr; + } + private: - vector m_vector; + std::vector m_vector; }; } diff --git a/src/sio_socket.cpp b/src/sio_socket.cpp index 754299ec..f89429f7 100644 --- a/src/sio_socket.cpp +++ b/src/sio_socket.cpp @@ -1,12 +1,14 @@ #include "sio_socket.h" #include "internal/sio_packet.h" #include "internal/sio_client_impl.h" -#include -#include +#include +#include #include +#include #include +#include -#if DEBUG || _DEBUG +#if (DEBUG || _DEBUG) && !defined(SIO_DISABLE_LOGGING) #define LOG(x) std::cout << x #else #define LOG(x) @@ -67,10 +69,10 @@ namespace sio return m_need_ack; } - void event::put_ack_message(message::ptr const& ack_message) + void event::put_ack_message(message::list const& ack_message) { if(m_need_ack) - m_ack_message = ack_message; + m_ack_message = std::move(ack_message); } inline @@ -91,13 +93,13 @@ namespace sio { } - message::ptr const& event::get_ack_message() const + message::list const& event::get_ack_message() const { return m_ack_message; } inline - message::ptr& event::get_ack_message_impl() + message::list& event::get_ack_message_impl() { return m_ack_message; } @@ -106,13 +108,17 @@ namespace sio { public: - impl(client_impl *,std::string const&); + impl(client_impl *, std::string const&, message::ptr const&); ~impl(); void on(std::string const& event_name,event_listener_aux const& func); void on(std::string const& event_name,event_listener const& func); + void on_any(event_listener_aux const& func); + + void on_any(event_listener const& func); + void off(std::string const& event_name); void off_all(); @@ -131,7 +137,7 @@ namespace sio void close(); - void emit(std::string const& name, message::list const& msglist, std::function const& ack); + void emit(std::string const& name, message::list const& msglist, std::function const& ack); std::string const& get_namespace() const {return m_nsp;} @@ -150,14 +156,14 @@ namespace sio // Message Parsing callbacks. void on_socketio_event(const std::string& nsp, int msgId,const std::string& name, message::list&& message); - void on_socketio_ack(int msgId, message::ptr const& message); + void on_socketio_ack(int msgId, message::list const& message); void on_socketio_error(message::ptr const& err_message); event_listener get_bind_listener_locked(string const& event); - void ack(int msgId,string const& name,message::ptr const& ack_message); + void ack(int msgId,string const& name,message::list const& ack_message); - void timeout_connection(const boost::system::error_code &ec); + void timeout_connection(const asio::error_code &ec); void send_connect(); @@ -171,18 +177,23 @@ namespace sio bool m_connected; std::string m_nsp; + message::ptr m_auth; - std::map > m_acks; + std::map > m_acks; std::map m_event_binding; + event_listener m_event_listener; + error_listener m_error_listener; - std::unique_ptr m_connection_timer; + std::unique_ptr m_connection_timer; std::queue m_packet_queue; std::mutex m_event_mutex; + + std::mutex m_packet_mutex; friend class socket; }; @@ -198,6 +209,16 @@ namespace sio m_event_binding[event_name] = func; } + void socket::impl::on_any(event_listener_aux const& func) + { + m_event_listener = event_adapter::do_adapt(func); + } + + void socket::impl::on_any(event_listener const& func) + { + m_event_listener = func; + } + void socket::impl::off(std::string const& event_name) { std::lock_guard guard(m_event_mutex); @@ -224,10 +245,11 @@ namespace sio m_error_listener = nullptr; } - socket::impl::impl(client_impl *client,std::string const& nsp): + socket::impl::impl(client_impl *client, std::string const& nsp, message::ptr const& auth): m_client(client), + m_connected(false), m_nsp(nsp), - m_connected(false) + m_auth(auth) { NULL_GUARD(client); if(m_client->opened()) @@ -243,7 +265,7 @@ namespace sio unsigned int socket::impl::s_global_event_id = 1; - void socket::impl::emit(std::string const& name, message::list const& msglist, std::function const& ack) + void socket::impl::emit(std::string const& name, message::list const& msglist, std::function const& ack) { NULL_GUARD(m_client); message::ptr msg_ptr = msglist.to_array_message(name); @@ -265,15 +287,11 @@ namespace sio void socket::impl::send_connect() { NULL_GUARD(m_client); - if(m_nsp == "/") - { - return; - } - packet p(packet::type_connect,m_nsp); + packet p(packet::type_connect, m_nsp, m_auth); m_client->send(p); - m_connection_timer.reset(new boost::asio::deadline_timer(m_client->get_io_service())); - boost::system::error_code ec; - m_connection_timer->expires_from_now(boost::posix_time::milliseconds(20000), ec); + m_connection_timer.reset(new asio::steady_timer(m_client->get_io_service())); + asio::error_code ec; + m_connection_timer->expires_from_now(std::chrono::milliseconds(20000), ec); m_connection_timer->async_wait(std::bind(&socket::impl::timeout_connection,this, std::placeholders::_1)); } @@ -287,11 +305,11 @@ namespace sio if(!m_connection_timer) { - m_connection_timer.reset(new boost::asio::deadline_timer(m_client->get_io_service())); + m_connection_timer.reset(new asio::steady_timer(m_client->get_io_service())); } - boost::system::error_code ec; - m_connection_timer->expires_from_now(boost::posix_time::milliseconds(3000), ec); - m_connection_timer->async_wait(lib::bind(&socket::impl::on_close, this)); + asio::error_code ec; + m_connection_timer->expires_from_now(std::chrono::milliseconds(3000), ec); + m_connection_timer->async_wait(std::bind(&socket::impl::on_close, this)); } } @@ -306,9 +324,18 @@ namespace sio { m_connected = true; m_client->on_socket_opened(m_nsp); - while (!m_packet_queue.empty()) { - m_client->send(m_packet_queue.front()); + + while (true) { + m_packet_mutex.lock(); + if(m_packet_queue.empty()) + { + m_packet_mutex.unlock(); + return; + } + sio::packet front_pack = std::move(m_packet_queue.front()); m_packet_queue.pop(); + m_packet_mutex.unlock(); + m_client->send(front_pack); } } } @@ -316,18 +343,23 @@ namespace sio void socket::impl::on_close() { NULL_GUARD(m_client); + sio::client_impl *client = m_client; + m_client = NULL; + if(m_connection_timer) { m_connection_timer->cancel(); m_connection_timer.reset(); } m_connected = false; - while (!m_packet_queue.empty()) { - m_packet_queue.pop(); - } - m_client->on_socket_closed(m_nsp); - m_client->remove_socket(m_nsp); - m_client = NULL; + { + std::lock_guard guard(m_packet_mutex); + while (!m_packet_queue.empty()) { + m_packet_queue.pop(); + } + } + client->on_socket_closed(m_nsp); + client->remove_socket(m_nsp); } void socket::impl::on_open() @@ -341,6 +373,7 @@ namespace sio if(m_connected) { m_connected = false; + std::lock_guard guard(m_packet_mutex); while (!m_packet_queue.empty()) { m_packet_queue.pop(); } @@ -398,19 +431,13 @@ namespace sio const message::ptr ptr = p.get_message(); if(ptr->get_flag() == message::flag_array) { - const array_message* array_ptr = static_cast(ptr.get()); - if(array_ptr->get_vector().size() >= 1&&array_ptr->get_vector()[0]->get_flag() == message::flag_string) - { - message::ptr value_ptr; - if(array_ptr->get_vector().size()>1) - { - value_ptr = array_ptr->get_vector()[1]; - } - this->on_socketio_ack(p.get_pack_id(), value_ptr); - break; - } + message::list msglist(ptr->get_vector()); + this->on_socketio_ack(p.get_pack_id(),msglist); } - this->on_socketio_ack(p.get_pack_id(),ptr); + else + { + this->on_socketio_ack(p.get_pack_id(),message::list(ptr)); + } break; } // Error @@ -432,22 +459,22 @@ namespace sio event ev = event_adapter::create_event(nsp,name, std::move(message),needAck); event_listener func = this->get_bind_listener_locked(name); if(func)func(ev); + if (m_event_listener) m_event_listener(ev); if(needAck) { this->ack(msgId, name, ev.get_ack_message()); } } - void socket::impl::ack(int msgId, const string &name, const message::ptr &ack_message) + void socket::impl::ack(int msgId, const string &, const message::list &ack_message) { - message::list li(ack_message); - packet p(m_nsp, li.to_array_message(name),msgId,true); + packet p(m_nsp, ack_message.to_array_message(),msgId,true); send_packet(p); } - void socket::impl::on_socketio_ack(int msgId, message::ptr const& message) + void socket::impl::on_socketio_ack(int msgId, message::list const& message) { - std::function l; + std::function l; { std::lock_guard guard(m_event_mutex); auto it = m_acks.find(msgId); @@ -465,7 +492,7 @@ namespace sio if(m_error_listener)m_error_listener(err_message); } - void socket::impl::timeout_connection(const boost::system::error_code &ec) + void socket::impl::timeout_connection(const asio::error_code &ec) { NULL_GUARD(m_client); if(ec) @@ -483,14 +510,23 @@ namespace sio NULL_GUARD(m_client); if(m_connected) { - while (!m_packet_queue.empty()) { - m_client->send(m_packet_queue.front()); + while (true) { + m_packet_mutex.lock(); + if(m_packet_queue.empty()) + { + m_packet_mutex.unlock(); + break; + } + sio::packet front_pack = std::move(m_packet_queue.front()); m_packet_queue.pop(); + m_packet_mutex.unlock(); + m_client->send(front_pack); } m_client->send(p); } else { + std::lock_guard guard(m_packet_mutex); m_packet_queue.push(p); } } @@ -506,8 +542,8 @@ namespace sio return socket::event_listener(); } - socket::socket(client_impl* client,std::string const& nsp): - m_impl(new impl(client,nsp)) + socket::socket(client_impl* client,std::string const& nsp,message::ptr const& auth): + m_impl(new impl(client,nsp,auth)) { } @@ -526,6 +562,16 @@ namespace sio m_impl->on(event_name, func); } + void socket::on_any(event_listener_aux const& func) + { + m_impl->on_any(func); + } + + void socket::on_any(event_listener const& func) + { + m_impl->on_any(func); + } + void socket::off(std::string const& event_name) { m_impl->off(event_name); @@ -551,7 +597,7 @@ namespace sio m_impl->off_error(); } - void socket::emit(std::string const& name, message::list const& msglist, std::function const& ack) + void socket::emit(std::string const& name, message::list const& msglist, std::function const& ack) { m_impl->emit(name, msglist,ack); } diff --git a/src/sio_socket.h b/src/sio_socket.h index a509c957..9e2e5294 100644 --- a/src/sio_socket.h +++ b/src/sio_socket.h @@ -19,22 +19,22 @@ namespace sio bool need_ack() const; - void put_ack_message(message::ptr const& ack_message); + void put_ack_message(message::list const& ack_message); - message::ptr const& get_ack_message() const; + message::list const& get_ack_message() const; protected: event(std::string const& nsp,std::string const& name,message::list const& messages,bool need_ack); event(std::string const& nsp,std::string const& name,message::list&& messages,bool need_ack); - message::ptr& get_ack_message_impl(); + message::list& get_ack_message_impl(); private: const std::string m_nsp; const std::string m_name; const message::list m_messages; const bool m_need_ack; - message::ptr m_ack_message; + message::list m_ack_message; friend class event_adapter; }; @@ -46,7 +46,7 @@ namespace sio class socket { public: - typedef std::function event_listener_aux; + typedef std::function event_listener_aux; typedef std::function event_listener; @@ -62,6 +62,10 @@ namespace sio void off(std::string const& event_name); + void on_any(event_listener const& func); + + void on_any(event_listener_aux const& func); + void off_all(); void close(); @@ -70,12 +74,12 @@ namespace sio void off_error(); - void emit(std::string const& name, message::list const& msglist = nullptr, std::function const& ack = nullptr); + void emit(std::string const& name, message::list const& msglist = nullptr, std::function const& ack = nullptr); std::string const& get_namespace() const; protected: - socket(client_impl*,std::string const&); + socket(client_impl*,std::string const&,message::ptr const&); void on_connected(); @@ -91,8 +95,8 @@ namespace sio private: //disable copy constructor and assign operator. - socket(socket const& sock){} - void operator=(socket const& sock){} + socket(socket const&){} + void operator=(socket const&){} class impl; impl *m_impl; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c9c78d81..f3c66957 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,9 +1,14 @@ -cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR) -include(${CMAKE_CURRENT_SOURCE_DIR}/../CMakeLists.txt) -find_package(Boost ${BOOST_VER} REQUIRED COMPONENTS unit_test_framework) +include(FetchContent) + +FetchContent_Declare( + Catch2 + GIT_REPOSITORY https://github.com/catchorg/Catch2.git + GIT_TAG v3.3.2 +) +FetchContent_MakeAvailable(Catch2) + +find_package(Threads REQUIRED) + add_executable(sio_test sio_test.cpp) -set_property(TARGET sio_test PROPERTY CXX_STANDARD 11) -set_property(TARGET sio_test PROPERTY CXX_STANDARD_REQUIRED ON) -target_link_libraries(sioclient PRIVATE ${Boost_LIBRARIES}) -target_link_libraries(sio_test sioclient) -target_include_directories(sio_test PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../src" ${Boost_INCLUDE_DIRS} ) +target_link_libraries(sio_test PRIVATE Catch2::Catch2WithMain sioclient Threads::Threads) +add_test(sioclient_test sio_test) diff --git a/test/README.md b/test/README.md deleted file mode 100644 index ee7b663f..00000000 --- a/test/README.md +++ /dev/null @@ -1,18 +0,0 @@ -You need have your boost unpacked on your disk, at least staged following modules: - -* system -* date_time -* random -* unit_test_framework - -Then use following instruction to gen makefile or VS project. -```bash -cmake -DBOOST_LIBRARYDIR=`` -DBOOST_INCLUDEDIR=`` -DBOOST_VER:STRING=`` -DCMAKE_BUILD_TYPE=Debug ./ -``` -Then run `make` or open by VS. - -For example I've installed boost 1.57.0 at `D:\boost_1_57_0` and staged the static lib at `D\boost_1_57_0\build\lib` then the command should be: -```bash -cmake -DBOOST_LIBRARYDIR=D:\boost_1_57_0\build\lib -DBOOST_INCLUDEDIR=D:\boost_1_57_0 -DBOOST_VER:STRING=1.57.0 -DCMAKE_BUILD_TYPE=Debug ./ -``` -In this case(Windows) CMake will create a VS project under `./test` folder. Open in VS and run it. \ No newline at end of file diff --git a/test/echo_server/index.js b/test/echo_server/index.js index 2c59ab1c..049066be 100644 --- a/test/echo_server/index.js +++ b/test/echo_server/index.js @@ -1,19 +1,17 @@ var port = 3000; -var io = require('socket.io').listen(port); +var io = require('socket.io')().listen(port); console.log("Listening on port " + port); /* Socket.IO events */ io.on("connection", function(socket){ console.log("new connection"); - socket.on('test_text',function() - { - console.log("test text event received."); + socket.on('test_text', (...args) => { + console.log("test text event received.", args); }); - socket.on('test_binary',function() - { - var args =Array.prototype.slice.call(arguments); + socket.on('test_binary', (...args) => { + console.log("test binary event received", args); if(args[0] instanceof Buffer) { console.log("test binary event received,binary length:"+ args[0].length); diff --git a/test/echo_server/package-lock.json b/test/echo_server/package-lock.json new file mode 100644 index 00000000..15ee0cb0 --- /dev/null +++ b/test/echo_server/package-lock.json @@ -0,0 +1,165 @@ +{ + "name": "echo_server", + "version": "0.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@types/component-emitter": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.10.tgz", + "integrity": "sha512-bsjleuRKWmGqajMerkzox19aGbscQX5rmmvvXl3wlIp5gMG1HgkiwPxsN5p070fBDKTNSPgojVbuY1+HWMbFhg==" + }, + "@types/cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-y7mImlc/rNkvCRmg8gC3/lj87S7pTUIJ6QGjwHR9WQJcFs+ZMTOaoPrkdFA/YdbuqVEmEbb5RdhVxMkAcgOnpg==" + }, + "@types/cors": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.9.tgz", + "integrity": "sha512-zurD1ibz21BRlAOIKP8yhrxlqKx6L9VCwkB5kMiP6nZAhoF5MvC7qS1qPA7nRcr1GJolfkQC7/EAL4hdYejLtg==" + }, + "@types/node": { + "version": "14.14.20", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.20.tgz", + "integrity": "sha512-Y93R97Ouif9JEOWPIUyU+eyIdyRqQR0I8Ez1dzku4hDx34NWh4HbtIc3WNzwB1Y9ULvNGeu5B8h8bVL5cAk4/A==" + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "base64-arraybuffer": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz", + "integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI=" + }, + "base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==" + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" + }, + "cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" + }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "requires": { + "ms": "2.1.2" + } + }, + "engine.io": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-4.0.6.tgz", + "integrity": "sha512-rf7HAVZpcRrcKEKddgIzYUnwg0g5HE1RvJaTLwkcfJmce4g+po8aMuE6vxzp6JwlK8FEq/vi0KWN6tA585DjaA==", + "requires": { + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~4.0.0", + "ws": "~7.4.2" + } + }, + "engine.io-parser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-4.0.2.tgz", + "integrity": "sha512-sHfEQv6nmtJrq6TKuIz5kyEKH/qSdK56H/A+7DnAuUPWosnIZAS2NHNcPLmyjtY3cGS/MqJdZbUjW97JU72iYg==", + "requires": { + "base64-arraybuffer": "0.1.4" + } + }, + "mime-db": { + "version": "1.45.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.45.0.tgz", + "integrity": "sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w==" + }, + "mime-types": { + "version": "2.1.28", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.28.tgz", + "integrity": "sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ==", + "requires": { + "mime-db": "1.45.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "socket.io": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-3.0.5.tgz", + "integrity": "sha512-5yWQ43P/4IttmPCGKDQ3CVocBiJWGpibyhYJxgUhf69EHMzmK8XW0DkmHIoYdLmZaVZJyiEkUqpeC7rSCIqekw==", + "requires": { + "@types/cookie": "^0.4.0", + "@types/cors": "^2.8.8", + "@types/node": "^14.14.10", + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "debug": "~4.3.1", + "engine.io": "~4.0.6", + "socket.io-adapter": "~2.0.3", + "socket.io-parser": "~4.0.3" + } + }, + "socket.io-adapter": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.0.3.tgz", + "integrity": "sha512-2wo4EXgxOGSFueqvHAdnmi5JLZzWqMArjuP4nqC26AtLh5PoCPsaRbRdah2xhcwTAMooZfjYiNVNkkmmSMaxOQ==" + }, + "socket.io-parser": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.5.tgz", + "integrity": "sha512-sNjbT9dX63nqUFIOv95tTVm6elyIU4RvB1m8dOeZt+IgWwcWklFDOdmGcfo3zSiRsnR/3pJkjY5lfoGqEe4Eig==", + "requires": { + "@types/component-emitter": "^1.2.10", + "component-emitter": "~1.3.0", + "debug": "~4.3.1" + } + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "ws": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==" + } + } +} diff --git a/test/echo_server/package.json b/test/echo_server/package.json index e4a4727e..b980f3c8 100644 --- a/test/echo_server/package.json +++ b/test/echo_server/package.json @@ -1,8 +1,8 @@ { "name": "echo_server", - "author":"Melo Yao", - "version":"0.0.0", + "author": "Melo Yao", + "version": "0.0.0", "dependencies": { - "socket.io": "1.*" - } + "socket.io": "^3.0.5" + } } diff --git a/test/sio_test.cpp b/test/sio_test.cpp index fb6f3e50..3901f05e 100644 --- a/test/sio_test.cpp +++ b/test/sio_test.cpp @@ -10,65 +10,65 @@ #include #include -#define BOOST_TEST_MODULE sio_test +#include -#include #ifndef _WIN32 #include "json.hpp" //nlohmann::json cannot build in MSVC #endif using namespace sio; -BOOST_AUTO_TEST_SUITE(test_packet) -BOOST_AUTO_TEST_CASE( test_packet_construct_1 ) +TEST_CASE( "test_packet_construct_1" ) { packet p("/nsp",nullptr,1001,true); - BOOST_CHECK(p.get_frame() == packet::frame_message); - BOOST_CHECK(p.get_message() == nullptr); - BOOST_CHECK(p.get_nsp() == std::string("/nsp")); - BOOST_CHECK(p.get_pack_id() == 1001); + CHECK(p.get_frame() == packet::frame_message); + CHECK(p.get_message() == nullptr); + CHECK(p.get_nsp() == std::string("/nsp")); + CHECK(p.get_pack_id() == 1001); } -BOOST_AUTO_TEST_CASE( test_packet_construct_2 ) +TEST_CASE( "test_packet_construct_2" ) { packet p(packet::frame_ping); - BOOST_CHECK(p.get_frame() == packet::frame_ping); - BOOST_CHECK(p.get_message() == nullptr); - BOOST_CHECK(p.get_nsp() == std::string("")); - BOOST_CHECK(p.get_pack_id() == 0xFFFFFFFF); + CHECK(p.get_frame() == packet::frame_ping); + CHECK(p.get_message() == nullptr); + CHECK(p.get_nsp() == std::string("")); + CHECK(p.get_pack_id() == 0xFFFFFFFF); } -BOOST_AUTO_TEST_CASE( test_packet_construct_3 ) +TEST_CASE( "test_packet_construct_3" ) { packet p(packet::type_connect,"/nsp",nullptr); - BOOST_CHECK(p.get_frame() == packet::frame_message); - BOOST_CHECK(p.get_type() == packet::type_connect); - BOOST_CHECK(p.get_message() == nullptr); - BOOST_CHECK(p.get_nsp() == std::string("/nsp")); - BOOST_CHECK(p.get_pack_id() == 0xFFFFFFFF); + CHECK(p.get_frame() == packet::frame_message); + CHECK(p.get_type() == packet::type_connect); + CHECK(p.get_message() == nullptr); + CHECK(p.get_nsp() == std::string("/nsp")); + CHECK(p.get_pack_id() == 0xFFFFFFFF); } -BOOST_AUTO_TEST_CASE( test_packet_accept_1 ) +TEST_CASE( "test_packet_accept_1" ) { packet p(packet::type_connect,"/nsp",nullptr); std::string payload; std::vector > buffers; p.accept(payload,buffers); - BOOST_CHECK(buffers.size() == 0); - BOOST_CHECK_MESSAGE(payload == "40/nsp",std::string("outputing payload:")+payload); + CHECK(buffers.size() == 0); + CHECK(payload == "40/nsp"); + INFO("outputing payload:" << payload); } -BOOST_AUTO_TEST_CASE( test_packet_accept_2 ) +TEST_CASE( "test_packet_accept_2" ) { packet p(packet::frame_ping); std::string payload; std::vector > buffers; p.accept(payload,buffers); - BOOST_CHECK(buffers.size() == 0); - BOOST_CHECK_MESSAGE(payload == "2",std::string("outputing payload:")+payload); + CHECK(buffers.size() == 0); + CHECK(payload == "2"); + INFO("outputing payload:" << payload); } -BOOST_AUTO_TEST_CASE( test_packet_accept_3 ) +TEST_CASE( "test_packet_accept_3" ) { message::ptr array = array_message::create(); array->get_vector().push_back(string_message::create("event")); @@ -77,13 +77,14 @@ BOOST_AUTO_TEST_CASE( test_packet_accept_3 ) std::string payload; std::vector > buffers; p.accept(payload,buffers); - BOOST_CHECK(p.get_type() == packet::type_ack); - BOOST_CHECK(buffers.size() == 0); - BOOST_CHECK_MESSAGE(payload == "43/nsp,1001[\"event\",\"text\"]",std::string("outputing payload:")+payload); + CHECK(p.get_type() == packet::type_ack); + CHECK(buffers.size() == 0); + CHECK(payload == "43/nsp,1001[\"event\",\"text\"]"); + INFO("outputing payload:" << payload); } #ifndef _WIN32 -BOOST_AUTO_TEST_CASE( test_packet_accept_4 ) +TEST_CASE( "test_packet_accept_4" ) { message::ptr binObj = object_message::create(); binObj->get_map()["desc"] = string_message::create("Bin of 100 bytes"); @@ -98,132 +99,138 @@ BOOST_AUTO_TEST_CASE( test_packet_accept_4 ) std::string payload; std::vector > buffers; p.accept(payload,buffers); - BOOST_CHECK(p.get_type() == packet::type_binary_event); - BOOST_REQUIRE(buffers.size() == 2); + CHECK(p.get_type() == packet::type_binary_event); + REQUIRE(buffers.size() == 2); size_t json_start = payload.find("{"); - BOOST_REQUIRE(json_start!=std::string::npos); + REQUIRE(json_start!=std::string::npos); std::string header = payload.substr(0,json_start); - BOOST_CHECK_MESSAGE(header=="452-/nsp,1001",std::string("outputing payload header:")+header); + CHECK(header=="452-/nsp,1001"); + INFO("outputing payload:" << payload); std::string json = payload.substr(json_start); nlohmann::json j = nlohmann::json::parse(json); - BOOST_CHECK_MESSAGE(j["desc"].get() == "Bin of 100 bytes", std::string("outputing payload desc:") + j["desc"].get()); - BOOST_CHECK_MESSAGE((bool)j["bin1"]["_placeholder"] , std::string("outputing payload bin1:") + j["bin1"].dump()); - BOOST_CHECK_MESSAGE((bool)j["bin2"]["_placeholder"] , std::string("outputing payload bin2:") + j["bin2"].dump()); + CHECK(j["desc"].get() == "Bin of 100 bytes"); + INFO("outputing payload desc::" << j["desc"].get()); + CHECK((bool)j["bin1"]["_placeholder"]); + INFO("outputing payload bin1:" << j["bin1"].dump()); + CHECK((bool)j["bin2"]["_placeholder"]); + INFO("outputing payload bin2:" << j["bin2"].dump()); int bin1Num = j["bin1"]["num"].get(); char numchar[] = {0,0}; numchar[0] = bin1Num+'0'; - BOOST_CHECK_MESSAGE(buffers[bin1Num]->length()==101 , std::string("outputing payload bin1 num:")+numchar); - BOOST_CHECK(buffers[bin1Num]->at(50)==0 && buffers[bin1Num]->at(0) == packet::frame_message); + CHECK(buffers[bin1Num]->length()==100); + INFO("outputing payload bin1 num:" << numchar); + CHECK(buffers[bin1Num]->at(50)==0); + CHECK(buffers[bin1Num]->at(0) == 0); int bin2Num = j["bin2"]["num"].get(); numchar[0] = bin2Num+'0'; - BOOST_CHECK_MESSAGE(buffers[bin2Num]->length()==51 , std::string("outputing payload bin2 num:") + numchar); - BOOST_CHECK(buffers[bin2Num]->at(25)==1 && buffers[bin2Num]->at(0) == packet::frame_message); + CHECK(buffers[bin2Num]->length()==50); + INFO("outputing payload bin2 num:" << numchar); + CHECK(buffers[bin2Num]->at(25)==1); + CHECK(buffers[bin2Num]->at(0) == 1); } #endif -BOOST_AUTO_TEST_CASE( test_packet_parse_1 ) +TEST_CASE( "test_packet_parse_1" ) { packet p; bool hasbin = p.parse("42/nsp,1001[\"event\",\"text\"]"); - BOOST_CHECK(!hasbin); - BOOST_CHECK(p.get_frame() == packet::frame_message); - BOOST_CHECK(p.get_type() == packet::type_event); - BOOST_CHECK(p.get_nsp() == "/nsp"); - BOOST_CHECK(p.get_pack_id() == 1001); - BOOST_CHECK(p.get_message()->get_flag() == message::flag_array); - BOOST_REQUIRE(p.get_message()->get_vector()[0]->get_flag() == message::flag_string); - BOOST_CHECK(p.get_message()->get_vector()[0]->get_string() == "event"); - BOOST_REQUIRE(p.get_message()->get_vector()[1]->get_flag() == message::flag_string); - BOOST_CHECK(p.get_message()->get_vector()[1]->get_string() == "text"); + CHECK(!hasbin); + CHECK(p.get_frame() == packet::frame_message); + CHECK(p.get_type() == packet::type_event); + CHECK(p.get_nsp() == "/nsp"); + CHECK(p.get_pack_id() == 1001); + CHECK(p.get_message()->get_flag() == message::flag_array); + REQUIRE(p.get_message()->get_vector()[0]->get_flag() == message::flag_string); + CHECK(p.get_message()->get_vector()[0]->get_string() == "event"); + REQUIRE(p.get_message()->get_vector()[1]->get_flag() == message::flag_string); + CHECK(p.get_message()->get_vector()[1]->get_string() == "text"); hasbin = p.parse("431111[\"ack\",{\"count\":5}]"); - BOOST_CHECK(!hasbin); - BOOST_CHECK(p.get_frame() == packet::frame_message); - BOOST_CHECK(p.get_type() == packet::type_ack); - BOOST_CHECK(p.get_pack_id() == 1111); - BOOST_CHECK(p.get_nsp() == "/"); - BOOST_CHECK(p.get_message()->get_flag() == message::flag_array); - BOOST_REQUIRE(p.get_message()->get_vector()[0]->get_flag() == message::flag_string); - BOOST_CHECK(p.get_message()->get_vector()[0]->get_string() == "ack"); - BOOST_REQUIRE(p.get_message()->get_vector()[1]->get_flag() == message::flag_object); - BOOST_CHECK(p.get_message()->get_vector()[1]->get_map()["count"]->get_int() == 5); + CHECK(!hasbin); + CHECK(p.get_frame() == packet::frame_message); + CHECK(p.get_type() == packet::type_ack); + CHECK(p.get_pack_id() == 1111); + CHECK(p.get_nsp() == "/"); + CHECK(p.get_message()->get_flag() == message::flag_array); + REQUIRE(p.get_message()->get_vector()[0]->get_flag() == message::flag_string); + CHECK(p.get_message()->get_vector()[0]->get_string() == "ack"); + REQUIRE(p.get_message()->get_vector()[1]->get_flag() == message::flag_object); + CHECK(p.get_message()->get_vector()[1]->get_map()["count"]->get_int() == 5); } -BOOST_AUTO_TEST_CASE( test_packet_parse_2 ) +TEST_CASE( "test_packet_parse_2" ) { packet p; bool hasbin = p.parse("3"); - BOOST_CHECK(!hasbin); - BOOST_CHECK(p.get_frame() == packet::frame_pong); - BOOST_CHECK(!p.get_message()); - BOOST_CHECK(p.get_nsp() == "/"); - BOOST_CHECK(p.get_pack_id() == -1); + CHECK(!hasbin); + CHECK(p.get_frame() == packet::frame_pong); + CHECK(!p.get_message()); + CHECK(p.get_nsp() == "/"); + CHECK(p.get_pack_id() == -1); hasbin = p.parse("2"); - BOOST_CHECK(!hasbin); - BOOST_CHECK(p.get_frame() == packet::frame_ping); - BOOST_CHECK(!p.get_message()); - BOOST_CHECK(p.get_nsp() == "/"); - BOOST_CHECK(p.get_pack_id() == -1); + CHECK(!hasbin); + CHECK(p.get_frame() == packet::frame_ping); + CHECK(!p.get_message()); + CHECK(p.get_nsp() == "/"); + CHECK(p.get_pack_id() == -1); } -BOOST_AUTO_TEST_CASE( test_packet_parse_3 ) +TEST_CASE( "test_packet_parse_3" ) { packet p; bool hasbin = p.parse("40/nsp"); - BOOST_CHECK(!hasbin); - BOOST_CHECK(p.get_type() == packet::type_connect); - BOOST_CHECK(p.get_frame() == packet::frame_message); - BOOST_CHECK(p.get_nsp() == "/nsp"); - BOOST_CHECK(p.get_pack_id() == -1); - BOOST_CHECK(!p.get_message()); + CHECK(!hasbin); + CHECK(p.get_type() == packet::type_connect); + CHECK(p.get_frame() == packet::frame_message); + CHECK(p.get_nsp() == "/nsp"); + CHECK(p.get_pack_id() == -1); + CHECK(!p.get_message()); p.parse("40"); - BOOST_CHECK(p.get_type() == packet::type_connect); - BOOST_CHECK(p.get_nsp() == "/"); - BOOST_CHECK(p.get_pack_id() == -1); - BOOST_CHECK(!p.get_message()); + CHECK(p.get_type() == packet::type_connect); + CHECK(p.get_nsp() == "/"); + CHECK(p.get_pack_id() == -1); + CHECK(!p.get_message()); p.parse("44\"error\""); - BOOST_CHECK(p.get_type() == packet::type_error); - BOOST_CHECK(p.get_nsp() == "/"); - BOOST_CHECK(p.get_pack_id() == -1); - BOOST_CHECK(p.get_message()->get_flag() == message::flag_string); + CHECK(p.get_type() == packet::type_error); + CHECK(p.get_nsp() == "/"); + CHECK(p.get_pack_id() == -1); + CHECK(p.get_message()->get_flag() == message::flag_string); p.parse("44/nsp,\"error\""); - BOOST_CHECK(p.get_type() == packet::type_error); - BOOST_CHECK(p.get_nsp() == "/nsp"); - BOOST_CHECK(p.get_pack_id() == -1); - BOOST_CHECK(p.get_message()->get_flag() == message::flag_string); + CHECK(p.get_type() == packet::type_error); + CHECK(p.get_nsp() == "/nsp"); + CHECK(p.get_pack_id() == -1); + CHECK(p.get_message()->get_flag() == message::flag_string); } -BOOST_AUTO_TEST_CASE( test_packet_parse_4 ) +TEST_CASE( "test_packet_parse_4" ) { packet p; bool hasbin = p.parse("452-/nsp,101[\"bin_event\",[{\"_placeholder\":true,\"num\":1},{\"_placeholder\":true,\"num\":0},\"text\"]]"); - BOOST_CHECK(hasbin); - char buf[101]; + CHECK(hasbin); + char buf[100]; buf[0] = packet::frame_message; - memset(buf+1,0,100); + memset(buf + 1,0,99); - std::string bufstr(buf,101); - std::string bufstr2(buf,51); - BOOST_CHECK(p.parse_buffer(bufstr)); - BOOST_CHECK(!p.parse_buffer(bufstr2)); + std::string bufstr(buf,100); + std::string bufstr2(buf,50); + CHECK(p.parse_buffer(bufstr)); + CHECK(!p.parse_buffer(bufstr2)); - BOOST_CHECK(p.get_frame() == packet::frame_message); - BOOST_CHECK(p.get_nsp() == "/nsp"); - BOOST_CHECK(p.get_pack_id() == 101); + CHECK(p.get_frame() == packet::frame_message); + CHECK(p.get_nsp() == "/nsp"); + CHECK(p.get_pack_id() == 101); message::ptr msg = p.get_message(); - BOOST_REQUIRE(msg&&msg->get_flag() == message::flag_array); - BOOST_CHECK(msg->get_vector()[0]->get_string() == "bin_event"); + REQUIRE(msg); + REQUIRE(msg->get_flag() == message::flag_array); + CHECK(msg->get_vector()[0]->get_string() == "bin_event"); message::ptr array = msg->get_vector()[1]; - BOOST_REQUIRE(array->get_flag() == message::flag_array); - BOOST_REQUIRE(array->get_vector()[0]->get_flag() == message::flag_binary); - BOOST_REQUIRE(array->get_vector()[1]->get_flag() == message::flag_binary); - BOOST_REQUIRE(array->get_vector()[2]->get_flag() == message::flag_string); - BOOST_CHECK(array->get_vector()[0]->get_binary()->size() == 50); - BOOST_CHECK(array->get_vector()[1]->get_binary()->size() == 100); - BOOST_CHECK(array->get_vector()[2]->get_string() == "text"); + REQUIRE(array->get_flag() == message::flag_array); + REQUIRE(array->get_vector()[0]->get_flag() == message::flag_binary); + REQUIRE(array->get_vector()[1]->get_flag() == message::flag_binary); + REQUIRE(array->get_vector()[2]->get_flag() == message::flag_string); + CHECK(array->get_vector()[0]->get_binary()->size() == 50); + CHECK(array->get_vector()[1]->get_binary()->size() == 100); + CHECK(array->get_vector()[2]->get_string() == "text"); } - -BOOST_AUTO_TEST_SUITE_END() -