diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..1c86739e --- /dev/null +++ b/.dockerignore @@ -0,0 +1,29 @@ +# osx noise +.DS_Store +profile + +# xcode noise +build/* +*.mode1 +*.mode1v3 +*.mode2v3 +*.perspective +*.perspectivev3 +*.pbxuser +*.xcworkspace +xcuserdata + +# svn & cvs +.svn +CVS +reports/* +doc/html +CMakeLists.txt.user* +src/examples/gen/* +*~ + +# IntelliJ +.idea + +*.tar.gz +*.asc diff --git a/.gitignore b/.gitignore index e789f3e9..1c86739e 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,6 @@ src/examples/gen/* # IntelliJ .idea + +*.tar.gz +*.asc diff --git a/.travis.yml b/.travis.yml index 42e04745..2ddde3c7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,41 +1,63 @@ language: cpp dist: trusty - +sudo: required +services: + - docker before_install: - - sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y - - sudo apt-get update -qq - - sudo apt-get install -qq libcurl4-openssl-dev libjsoncpp-dev libargtable2-dev libgnutls-dev libgcrypt11-dev valgrind wget gcc-5 g++-5 - - wget http://ftp.gnu.org/gnu/libmicrohttpd/libmicrohttpd-0.9.52.tar.gz - - tar -xvf libmicrohttpd-0.9.52.tar.gz - - cd libmicrohttpd-0.9.52 - - ./configure && make - - sudo make install && sudo ldconfig - - cd .. && sudo rm -rf libmicrohttpd-0.9.52 - - sudo pip install codecov - - sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y - - sudo apt-get update -qq - - if [ "$CXX" = "g++" ]; then export CXX="g++-5" CC="gcc-5"; fi - -install: - - if [ "$CXX" = "g++" ]; then export CXX="g++-5" CC="gcc-5"; fi - - sudo update-alternatives --install /usr/bin/gcov gcov /usr/bin/gcov-5 90 + - | + if [[ "$OS" == "native" && "$TRAVIS_OS_NAME" == "linux" ]] + then + sudo apt-get install -qq wget + sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y + sudo add-apt-repository 'deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-5.0 main' -y + wget -O - http://llvm.org/apt/llvm-snapshot.gpg.key | sudo apt-key add - + sudo apt-get update -qq + sudo apt-get install -qq libcurl4-openssl-dev libhiredis-dev redis-server libjsoncpp-dev libargtable2-dev libgnutls-dev libgcrypt11-dev valgrind wget gcc-5 g++-5 clang-5.0 + wget http://ftp.gnu.org/gnu/libmicrohttpd/libmicrohttpd-0.9.52.tar.gz + tar -xvf libmicrohttpd-0.9.52.tar.gz + cd libmicrohttpd-0.9.52 + ./configure && make + sudo make install && sudo ldconfig + cd .. && sudo rm -rf libmicrohttpd-0.9.52 + sudo pip install codecov + sudo update-alternatives --install /usr/bin/gcov gcov /usr/bin/gcov-5 90 + elif [[ "$TRAVIS_OS_NAME" == "osx" ]] + then + brew update + brew install jsoncpp argtable curl hiredis redis + brew install libmicrohttpd --with-ssl + fi -env: - - HTTP_SERVER=YES HTTP_CLIENT=YES COMPILE_STUBGEN=YES +matrix: + include: + - env: OS=fedora + os: linux + - env: OS=arch + os: linux + - env: OS=debian8 + os: linux + - env: OS=debian9 + os: linux + - env: OS=debian10 + os: linux + - env: OS=ubuntu1604 + os: linux + - env: OS=ubuntu1704 + os: linux + - env: OS=native + os: linux + compiler: clang + - env: OS=native + os: linux + compiler: gcc + - env: OS=native + os: osx + compiler: clang compiler: - - gcc + - gcc script: - - mkdir -p build && cd build - - cmake -DCMAKE_BUILD_TYPE=Debug -DHTTP_CLIENT=${HTTP_CLIENT} -DHTTP_SERVER=${HTTP_SERVER} -DCOMPILE_STUBGEN=${COMPILE_STUBGEN} .. - - make - - make test - - sudo make install && sudo ldconfig - - g++ ../src/examples/simpleclient.cpp -ljsonrpccpp-client -ljsoncpp -ljsonrpccpp-common -lcurl -o sampleclient - - g++ ../src/examples/simpleserver.cpp -ljsonrpccpp-server -ljsoncpp -ljsonrpccpp-common -lmicrohttpd -o sampleserver - - sudo make uninstall - - cd .. - - ./dev/codecov.sh -after_success: - - bash <(curl -s https://codecov.io/bash) + - if [[ "$OS" == "native" ]]; then ./docker/build_test_install.sh; fi + - if [[ "$OS" == "native" && "$TRAVIS_OS_NAME" == "linux" && "$COMPILER" == "gcc" ]]; then ./dev/codecov.sh; fi + - if [[ "$OS" != "native" ]]; then cd docker; make ${OS}; fi diff --git a/AUTHORS.md b/AUTHORS.md index 9bffd37a..3ec4c667 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -33,9 +33,15 @@ Alexandre Poirot + adapted build file to generate pkg-config file for this lib + added client and server connectors that use Tcp Sockets on Linux and Windows (uses native socket and thread API on each OS) +Trevor Vannoy ++ python client stub generator + Jean-Daniel Michaud + added server/client file descriptor support +Jacques Software ++ added support for redis connector + Bugfixes (chronological order) ============================== @@ -66,3 +72,6 @@ Erik Lundin Michał Górny + bugfixes in the build system + +Trevor Vannoy ++ fixed tcp socket client compile issues diff --git a/CHANGELOG.md b/CHANGELOG.md index d3204857..afec8808 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,111 +1,186 @@ -Changes in v0.7.0 ------------------ -- Change: Requiring C++11 support (gcc >= 4.8) -- Fix: armhf compatibility -- Fix: Invalid client id field handling (removed int only check) -- Fix: Security issues in unixdomainsocket connectors -- Fix: Missing CURL include directive -- Fix: Parallel build which failed due to failing CATCH dependency -- Fix: Handling 64-bit ids -- Fix: Invalid parameter check -- Fix: Invalid pointer handling in HTTP-Server -- NEW: HttpServer can now be configured to listen localhost only -- NEW: TCP Server + Client connectors - -Changes in v0.6.0 ------------------ -- NEW: pkg-config files for all shared libraries -- NEW: UNIX Socket client + server connector -- NEW: multiarch support -- Change: unit testing framework to catch -- Change: allow disabling shared library build -- Change: split out shared/static library for stubgenerator - -Changes in v0.5.0 ------------------ -- Added `--version` option to jsonrpcstub. -- Added msvc support. -- Added data field support for JsonRpcException. -- Added contributions guide: https://github.com/cinemast/libjson-rpc-cpp#contributions +# Change Log +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/) +and this project adheres to [Semantic Versioning](http://semver.org/). + +## [v1.0.0] - 2017-08-27 +### Fixed +- Typo in ERROR_CLIENT_CONNECTOR exception +- Integration testsuite when run without HTTP +- dev/testcoverage.sh script which did not create the build directory +- Indentation in CMakeLists.txt files +- Positional parameters with more than 10 items +- C++11 deprecated dynamic excpetion specifiers have been removed +- libmicrohttpd legacy detection for `EPOLL` + +### Added +- File descriptor client and server connector +- Redis client and server connector +- Docker based build system for testing on multiple distributions +- Python client stubgenerator +- CI Integration for OSX build +- `StreamReader` and `StreamWriter` classes to handle the buffering +- [Makefile](Makefile) for developer/contributor related functions + +### Removed +- Method `BatchResponse::getResult(Json::Value& id)` +- Method `AbstractServerConnector::SendResponse()` +- Scripts dev/ci.sh, dev/createpackage.sh, dev/installdeps.sh +- `dev/coverage.sh` in favor of `make coverage` +- Windows support, which will hopefully come back soon + +### Changed +- Migrated from coveralls.io to codecov.io +- Changed maintainer e-mail address +- Use libmicrohttpd's EPOLL where possible (lmhd >= 0.9.52) +- Added `set -e` to testcoverage.sh script +- Changelog format to [keepachangelog.com](http://keepachangelog.com/en/0.3.0/) +- Refactored all socket-based client and server connectors to reduce code duplication +- Changed interfaces for `AbstractServerConnector` to avoid the ugly `void *` backpointer + +## [v0.7.0] - 2016-08-10 +### Fixed +- armhf compatibility +- Invalid client id field handling (removed int only check) +- Security issues in unixdomainsocket connectors +- Missing CURL include directive +- Parallel build which failed due to failing CATCH dependency +- Handling 64-bit ids +- Invalid parameter check +- Invalid pointer handling in HTTP-Server + +### Added +- HttpServer can now be configured to listen localhost only +- TCP Server + Client connectors + +## Changed +- Requiring C++11 support (gcc >= 4.8) + +## [v0.6.0] - 2015-06-27 +### Added +- pkg-config files for all shared libraries +- UNIX Socket client + server connector +- multiarch support + +### Changed +- unit testing framework to catch +- allow disabling shared library build +- split out shared/static library for stubgenerator + +## [v0.5.0] - 2015-04-07 +### Fixed +- building tests with examples disabled. +- unnecessary rebuilds of stubs on each `make` call. + +### Added +- `--version` option to jsonrpcstub. +- msvc support. +- data field support for JsonRpcException. +- contributions guide: https://github.com/cinemast/libjson-rpc-cpp#contributions - HttpClient uses Http Keep-Alive, which improves performance drastically. -- Added multiarch support. -- Fixed building tests with examples disabled. +- multiarch support. + +### Changed - Made static library build optional (via `BUILD_STATIC_LIBS`). -- Fixed unnecessary rebuilds of stubs on each `make` call. -Changes in v0.4.2 ------------------ -- Fix of spelling mistakes. +## [v0.4.2] - 2015-01-21 +### Fixed +- Some spelling mistakes. +- HttpServer with Threading option in SSL startup. + +### Changed - Use CMAKE versioning in manpage. -- Improving include scheme of jsoncpp. -- Bugfix in HttpServer with Threading option in SSL startup. +- Improvied include scheme of jsoncpp. + +## [v0.4.1] - 2014-12-01 +### Added +- coverity scan support +- [API compatibility report](http://upstream.rosalinux.ru/versions/libjson-rpc-cpp.html) +- Stubgenerator option for protocol switches (JSON-RPC 1.0 & 2.0) -Changes in v0.4.1 ------------------ -- Added coverity scan support -- [Added API compatibility report](http://upstream.rosalinux.ru/versions/libjson-rpc-cpp.html) +### Changed - Improved manpage -- Extended Stubgenerator for protocol switches (JSON-RPC 1.0 & 2.0) -Changes in v0.4 ---------------- -- Memory leak fixes -- Switched Http Server to libmicrohttpd -- Added full WIN32 build support +## [v0.4] - 2014-11-21 +### Fixed +- Memory leaks + +### Added +- Full WIN32 build support +- JavaScript client stub support - Improved test coverage (100% line coverage) -- Added JavaScript client stub support + +### Changed +- Switched Http Server to libmicrohttpd - Removed TCP Client/Server implementation due to security and codestyle problems. -- Finally removed dirty pointer stuff in bindAndAddX() methods. +- Removed dirty pointer stuff in bindAndAddX() methods. - Using call by value in generated stubs for primitive data types. -Changes in v0.3.2 ------------------ -- Bugfixes -- Additional testcases for client + server -> higher testcoverage +## [v0.3.2] - 2014-10-26 +### Fixed +- Minor bugs + +### Added +- Testcases for client + server -> higher testcoverage - JSON-RPC 1 Client + Server support + +### Changed - Refactorings in server for JSON-RPC 1 support - Hiding irrelevant API headers from installation - Renamed AbstractClientConnector to IClientConnector (please regenearte your client stubs after upgrading) - Reactivated dev/testcoverage.sh to measure testcoverage. -Changes in v0.3.1 ------------------ -- Bugfixes +## [v0.3.1] - 2014-10-22 +### Fixed +- Minor bugs + +### Added +- Experimental Javascript client to stubgenerator + +### Changed - Changed SOVERSION -- Added experimental Javascript client to stubgenerator - Adapted HTTP Server to enable CORS. -Changes in v0.3 ---------------- +## [v0.3] - 2014-10-19 +### Fixed +- Renamed .so files to avoid collisions with makerbot's libjsonrpc. +- Invalid Batchcalls in Client and Server caused runtime exceptions. + +### Added +- Namespace/package support for generated stub classes. +- CMake options to enable/disable Stubgenerator, Examples, Connectors and Testsuite. +- Boost-test based unit testing suite, which makes testing more flexible. + +### Changed - Split up server and client into separate libraries - Lot's of refactorings in the build system and stubgenerator. -- Added namespace/package support for generated stub classes. -- libjson-cpp is no longer directly embedded. -- Simplified spec format: a procedure specification without `return` field is a notification. -- Introduced a boost-test based unit testing suite, which makes testing more flexible. -- Added CMake options to enable/disable Stubgenerator, Examples, Connectors and Testsuite. - Removed Autotools support (because of all the changes in this release). -- Bugfix: renamed .so files to avoid collisions with makerbot's libjsonrpc. -- Bugfix: Invalid Batchcalls in Client and Server caused runtime exceptions. +- Removed embedded libjson-cpp. +- Simplified spec format: a procedure specification without `return` field is a notification. + +# [v0.2.1] - 2013-07-27 +### Added +- Support for positional parameters. (see at [example specification](https://github.com/cinemast/libjson-rpc-cpp/blob/master/src/example/spec.json) how to declare them) + +## [0.2] - 2013-05-29 +### Fixed +- Minor bugs -Changes in v0.2.1 ------------------ -- Added support for positional parameters. (see at [example specification](https://github.com/cinemast/libjson-rpc-cpp/blob/master/src/example/spec.json) how to declare them) +### Added +- Stub generator for client and server. +- SpecificationWriter to generate Specifications from RPC-Server definitions. +- SpecificationParser to parse a Specification file and generate Methods for the RPC-Server. +- Automated testing after build phase (using `make test`) -Changes in v0.2 ---------------- -- Minor bugfixes. +### Changed - Refactored architecture. -- stub generator for client and server. -- removed mandatory configuration files (making it more compatible for embedded use cases). -- Introduced SpecificationWriter to generate Specifications from RPC-Server definitions. -- Introduced SpecificationParser to parse a Specification file and generate Methods for the RPC-Server. +- Removed mandatory configuration files (making it more compatible for embedded use cases). - Updated JsonCPP library - Update Mongoose library - Enable SSL Support (provided by mongoose) -- Introduced automated testing after build phase (using `make test`) - Embedding dependent libraries (to avoid naming conflicts) -Known issues -------------- -- Under Ubuntu 12.04 SSL support is not enabled in the libmicrohttpd-dev package that is provided by ubuntu. Look at the [.travis.yml](https://github.com/cinemast/libjson-rpc-cpp/blob/develop/.travis.yml) file to see how I cope with this. +## [0.1] - 2013-02-07 +### Added +- Initial release diff --git a/CMakeLists.txt b/CMakeLists.txt index ca36fa78..9fdc7a48 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,32 +17,35 @@ cmake_policy(SET CMP0007 NEW) cmake_policy(SET CMP0012 NEW) if (${CMAKE_MAJOR_VERSION} GREATER 2) - # old policy do not use MACOSX_RPATH - cmake_policy(SET CMP0042 OLD) + # old policy do not use MACOSX_RPATH + cmake_policy(SET CMP0042 OLD) endif() -set(MAJOR_VERSION 0) -set(MINOR_VERSION 7) +set(MAJOR_VERSION 1) +set(MINOR_VERSION 0) set(PATCH_VERSION 0) -set(SO_VERSION 0) +set(SO_VERSION 1) set(BUILD_SHARED_LIBS YES CACHE BOOL "Build shared libraries") set(BUILD_STATIC_LIBS NO CACHE BOOL "Build static libraries") set(LIB_SUFFIX "" CACHE STRING "Suffix for library directory (32/64)") if (NOT BUILD_SHARED_LIBS AND NOT BUILD_STATIC_LIBS) - message(FATAL_ERROR "Both BUILD_SHARED_LIBS and BUILD_STATIC_LIBS are disabled") + message(FATAL_ERROR "Both BUILD_SHARED_LIBS and BUILD_STATIC_LIBS are disabled") endif() # defaults for modules that can be enabled/disabled if(UNIX) - set(UNIX_DOMAIN_SOCKET_SERVER YES CACHE BOOL "Include Unix Domain Socket server") - set(UNIX_DOMAIN_SOCKET_CLIENT YES CACHE BOOL "Include Unix Domain Socket client") - set(FILE_DESCRIPTOR_SERVER YES CACHE BOOL "Include File Descriptor server") - set(FILE_DESCRIPTOR_CLIENT YES CACHE BOOL "Include File Descriptor client") + set(UNIX_DOMAIN_SOCKET_SERVER YES CACHE BOOL "Include Unix Domain Socket server") + set(UNIX_DOMAIN_SOCKET_CLIENT YES CACHE BOOL "Include Unix Domain Socket client") + set(FILE_DESCRIPTOR_SERVER YES CACHE BOOL "Include File Descriptor server") + set(FILE_DESCRIPTOR_CLIENT YES CACHE BOOL "Include File Descriptor client") endif(UNIX) + set(TCP_SOCKET_SERVER NO CACHE BOOL "Include Tcp Socket server") set(TCP_SOCKET_CLIENT NO CACHE BOOL "Include Tcp Socket client") +set(REDIS_SERVER YES CACHE BOOL "Include Redis server using hiredis") +set(REDIS_CLIENT YES CACHE BOOL "Include Redis client using hiredis") set(HTTP_SERVER YES CACHE BOOL "Include HTTP server using libmicrohttpd") set(HTTP_CLIENT YES CACHE BOOL "Include HTTP client support using curl") set(COMPILE_TESTS YES CACHE BOOL "Compile test framework") @@ -53,13 +56,15 @@ option(WITH_COVERAGE "Build with code coverage flags" ON) # print actual settings if(UNIX) - message(STATUS "UNIX_DOMAIN_SOCKET_SERVER: ${UNIX_DOMAIN_SOCKET_SERVER}") - message(STATUS "UNIX_DOMAIN_SOCKET_CLIENT: ${UNIX_DOMAIN_SOCKET_CLIENT}") + message(STATUS "UNIX_DOMAIN_SOCKET_SERVER: ${UNIX_DOMAIN_SOCKET_SERVER}") + message(STATUS "UNIX_DOMAIN_SOCKET_CLIENT: ${UNIX_DOMAIN_SOCKET_CLIENT}") endif(UNIX) message(STATUS "TCP_SOCKET_SERVER: ${TCP_SOCKET_SERVER}") message(STATUS "TCP_SOCKET_CLIENT: ${TCP_SOCKET_CLIENT}") message(STATUS "HTTP_SERVER: ${HTTP_SERVER}") message(STATUS "HTTP_CLIENT: ${HTTP_CLIENT}") +message(STATUS "REDIS_SERVER: ${REDIS_SERVER}") +message(STATUS "REDIS_CLIENT: ${REDIS_CLIENT}") if(UNIX) message(STATUS "UNIXDOMAINSOCKET_SERVER: ${UNIX_DOMAIN_SOCKET_SERVER}") message(STATUS "UNIXDOMAINSOCKET_CLIENT: ${UNIX_DOMAIN_SOCKET_CLIENT}") @@ -86,12 +91,12 @@ add_subdirectory(src/jsonrpccpp) #the stubgenerator if (COMPILE_STUBGEN) - add_subdirectory(src/stubgenerator) -endif () + add_subdirectory(src/stubgenerator) +endif() # setup examples if (COMPILE_EXAMPLES) - add_subdirectory(src/examples) + add_subdirectory(src/examples) endif() # setup test suite @@ -102,22 +107,22 @@ endif() # create documentation if (DOXYGEN_FOUND) - file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/doc) - message(STATUS "Found doxygen: ${DOXYGEN_EXECUTABLE}") - configure_file("${PROJECT_SOURCE_DIR}/doc/doxyfile.in" "${CMAKE_BINARY_DIR}/Doxyfile" @ONLY) - add_custom_target(doc ${DOXYGEN_EXECUTABLE} ${CMAKE_BINARY_DIR}/Doxyfile WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/doc COMMENT "Generating API documentation") + file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/doc) + message(STATUS "Found doxygen: ${DOXYGEN_EXECUTABLE}") + configure_file("${PROJECT_SOURCE_DIR}/doc/doxyfile.in" "${CMAKE_BINARY_DIR}/Doxyfile" @ONLY) + add_custom_target(doc ${DOXYGEN_EXECUTABLE} ${CMAKE_BINARY_DIR}/Doxyfile WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/doc COMMENT "Generating API documentation") endif(DOXYGEN_FOUND) # setup uninstall target configure_file( - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in" - "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" - IMMEDIATE @ONLY + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" + IMMEDIATE @ONLY ) add_custom_target( - uninstall - COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake + uninstall + COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake ) # CMake package diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..c13bfe99 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,52 @@ +# Developer Information + +If you plan to contribute to libjson-rpc-cpp, please skim the following information. + +## Developer Makefile + +The [Makefile](Makefile) contains useful commands that makes development for libjson-rpc-cpp easier. + +* `make build`: builds the framework in the default configuration +* `make build-docker`: builds the framework for all distributions in docker +* `make test`: runs all tests +* `make format`: formats all cpp and header files with `clang-format` +* `make check-format`: checks if the sources have been formatted +* `make coverage`: generates a coverage report to `reports/coverage.html` +* `make clean`: remove all build artifacts + +## Contributions + +Contributions of any kind are always very welcome. +Here are some suggestions: + +- Bugreports +- Bugfixes +- Extending documentation (especially doxygen) +- Extending the test coverage +- Simplifying the build system +- Suggestion of new features +- New features: + - Adding new connectors. + - Adding new languages to the stubgenerator. + +Additionally you can find a wishlist and planned features [here](https://github.com/cinemast/libjson-rpc-cpp/projects/1) + +### Guidelines / Conventions + +We do not want to prevent you from contributing by having too strict guidelines. +If you have ideas for improvement, just do it your way, rather than doing it not at all. + +Anyway here is a list of how we would prefer your contributions: +#### Issues: + - Use the issue tracker on github to report bugs or improvements. + - Please avoid sending me mails directly, as this is not visible to others. + - Please close issues on yourself if you think a problem has been dealt with. + +#### Code contributions: + - Please raise a pull-request against the **develop** branch. + - If you add features, please keep the test-coverage at 100% line coverage and document them accordingly (doxygen, manpage, etc.). + - If you fix a bug, please refer the issue in the [commit message](https://help.github.com/articles/closing-issues-via-commit-messages/). + - Please make sure that the travis-ci build passes (you will get notified if you raise a pull-request). + - Add yourself to the AUTHORS.md. + - Document your changes in the CHANGELOG.md + - Format the code using `make format` diff --git a/LICENSE.txt b/LICENSE.txt index f2b18c3b..d96f002c 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (C) 2011-2016 Peter Spiess-Knafl +Copyright (C) 2011-2017 Peter Spiess-Knafl Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..9665d751 --- /dev/null +++ b/Makefile @@ -0,0 +1,35 @@ +# This file is only used for development for convinience functions as +# quick builds and tests + +GIT_VERSION := $(shell git describe --abbrev=4 --dirty --always --tags) +.PHONY: build + +build: + mkdir -p build + cd build && cmake -DCMAKE_BUILD_TYPE=Debug -DWITH_COVERAGE=Yes .. && make -j$(nproc) + +build-docker: + cd docker && make all + +coverage: test + mkdir -p reports + gcovr -r . -d -e "build" -e "src/test" -e "src/examples" -e "src/stubgenerator/main.cpp" --html --html-details -o reports/coverage.html + +format: + find . -name "*.h" -o -name "*.cpp" -exec clang-format -style=LLVM -i {} \; + +check-format: format + git diff --exit-code + +tarball: + git archive --format=tar.gz --prefix=libjson-rpc-cpp-$(GIT_VERSION)/ -o libjson-rpc-cpp-$(GIT_VERSION).tar.gz HEAD + +signed-taball: tarball + gpg --armor --detach-sign libjson-rpc-cpp-$(GIT_VERSION).tar.gz + +test: build + cd build && ./bin/unit_testsuite + +clean: + rm -rf build + rm -rf reports diff --git a/README.md b/README.md index 62866c7b..908616e1 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -[![Build Status](https://travis-ci.org/cinemast/libjson-rpc-cpp.png?branch=master)](https://travis-ci.org/cinemast/libjson-rpc-cpp) [![codecov](https://codecov.io/gh/cinemast/libjson-rpc-cpp/branch/master/graph/badge.svg)](https://codecov.io/gh/cinemast/libjson-rpc-cpp) [![Coverity Status](https://scan.coverity.com/projects/3169/badge.svg?flat=1)](https://scan.coverity.com/projects/3169) +Master [![Build Status](https://travis-ci.org/cinemast/libjson-rpc-cpp.png?branch=master)](https://travis-ci.org/cinemast/libjson-rpc-cpp) [![codecov](https://codecov.io/gh/cinemast/libjson-rpc-cpp/branch/master/graph/badge.svg)](https://codecov.io/gh/cinemast/libjson-rpc-cpp) +Develop [![Build Status](https://travis-ci.org/cinemast/libjson-rpc-cpp.png?branch=develop)](https://travis-ci.org/cinemast/libjson-rpc-cpp) [![codecov](https://codecov.io/gh/cinemast/libjson-rpc-cpp/branch/develop/graph/badge.svg)](https://codecov.io/gh/cinemast/libjson-rpc-cpp) | +[![Coverity Status](https://scan.coverity.com/projects/3169/badge.svg?flat=1)](https://scan.coverity.com/projects/3169) libjson-rpc-cpp =============== @@ -9,16 +11,16 @@ It is fully JSON-RPC [2.0 & 1.0 compatible](http://www.jsonrpc.org/specification ![libjson-rpc-cpp logo](https://github.com/cinemast/libjson-rpc-cpp/blob/master/dev/artwork/logo.png?raw=true) **5 good reasons for using libjson-rpc-cpp in your next RPC project** -- Full JSON-RPC 2.0 & 1.0 Client and Server Support. -- jsonrpcstub - a tool that generates stub-classes for your JSON-RPC client AND server applications. +- Full JSON-RPC 2.0 & partial JSON-RPC 1.0 client and server Support. +- jsonrpcstub - a tool that generates stub-classes for your JSON-RPC client and server applications. - Ready to use HTTP + TCP server and client to provide simple interfaces for your JSON-RPC application. -- Cross platform build support and [precompiled binaries for WIN32](http://spiessknafl.at/libjson-rpc-cpp). -- Super liberal [MIT-License](http://en.wikipedia.org/wiki/MIT_License). +- Cross platform build support for Linux and OS X. +- Super liberal [MIT-License](http://en.wikipedia.org/wiki/MIT_License). **Other good reasons to use libjson-rpc-cpp** - Easy to use [cmake](http://www.cmake.org) cross platform build system. - Clean and simple architecture, which makes it easy to extend. -- Tested under MacOS X (10.9), GNU/Linux (Debian 8 64-bit), Windows 7 (MinGW32) and Raspbian Wheezy (armhf). +- Continuously tested under MacOS, and [various linux distributions](https://travis-ci.org/cinemast/libjson-rpc-cpp). - Automated testing using `make test`. - Useful Examples provided. e.g. XBMC Remote using json-rpc client part and stub generator. - The stubgenerator currently supports C++ and JavaScript. @@ -71,17 +73,13 @@ Install the dependencies - [libjsoncpp](https://github.com/open-source-parsers/jsoncpp) - [libargtable](http://argtable.sourceforge.net/) - [cmake](http://www.cmake.org/) +- [libhiredis](https://redislabs.com/lp/hiredis/) **UNIX** For Debian and Arch GNU/Linux based systems, all dependencies are available via the package manager. For OS X all dependencies are available in [Brew](http://brew.sh) -**Windows** - -- Download the precompiled dependencies form [here](https://spiessknafl.at/libjson-rpc-cpp/libjson-rpc-cpp_win32-deps.zip). -- Extract it into the cloned repository, so that there is a `win32-deps` folder in the root project directory. - Build ----- @@ -109,6 +107,8 @@ Default configuration should be fine for most systems, but here are available co - `-DCOMPILE_EXAMPLES=NO` disables examples. - `-DHTTP_SERVER=NO` disable the libmicrohttpd webserver. - `-DHTTP_CLIENT=NO` disable the curl client. +- `-DREDIS_SERVER=NO` disable the redis server connector. +- `-DREDIS_CLIENT=NO` disable the redis client connector. - `-DUNIX_DOMAIN_SOCKET_SERVER=NO` disable the unix domain socket server connector. - `-DUNIX_DOMAIN_SOCKET_CLIENT=NO` disable the unix domain socket client connector. - `-DFILE_DESCRIPTOR_SERVER=NO` disable the file descriptor server connector. @@ -126,7 +126,7 @@ This example will show the most simple way to create a rpc server and client. If [ { "name": "sayHello", - "params": { + "params": { "name": "Peter" }, "returns" : "Hello Peter" @@ -233,93 +233,32 @@ Compile the client with: g++ main.cpp -ljsoncpp -lcurl -ljsonrpccpp-common -ljsonrpccpp-client -o sampleclient ``` -References -========== -- [NASA Ames Research Center](http://www.nasa.gov/centers/ames/home/): use it to obtain aircraft state information from an aircraft simulator. -- [LaseShark 3D Printer](https://github.com/macpod/lasershark_3dp): used to control the firmware of the 3D printer. -- [cpp-ethereum](https://github.com/ethereum/cpp-ethereum): a distributed computing framework. -- [mage-sdk-cpp](https://github.com/mage/mage-sdk-cpp): a game engine. -- [bitcodin](http://www.bitmovin.net): a scalable cloud based video transcoding platform. -- [wgslib](http://wgslib.com/): A web geostatistics library. -- [bitcoin-api-cpp](https://github.com/minium/bitcoin-api-cpp): a C++ interface to bitcoin. -- [NIT DASH Content Server](http://www.nit.eu/offer/research-projects-products/334-http2dash): Dynamic Adaptive Streaming over HTTP server. +## Contributions -If you use this library and find it useful, I would be very pleased if you let me know about it. +Please take a look at [CONTRIBUTING.md](CONTRIBUTING.md) -Developer Information -===================== -Contributions -------------- -Contributions of any kind are always very welcome. -Here are some suggestions: -- Bugreports -- Bugfixes -- Extending documentation (especially doxygen) -- Suggestion of new features -- New features: - - Adding new connectors. - - Adding new languages to the stubgenerator. - -**Guidelines / Conventions** - -We do not want to prevent you from contributing by having too strict guidelines. -If you have ideas for improvement, just do it your way, rather than doing it not at all. - -Anyway here is a list of how we would prefer your contributions: -- Issues: - - Use the issue tracker on github to report bugs or improvements. - - Please avoid sending me mails directly, as this is not visible to others. - - Please close issues on your own if you think a problem has been dealt with. -- Code contributions: - - Please raise a pull-request against the develop branch. - - If you add features, please keep the test-coverage at 100% and document them (doxygen, manpage, etc.). - - If you fix a bug, please refer the issue in the [commit message](https://help.github.com/articles/closing-issues-via-commit-messages/). - - Please make sure that the travis-ci build passes (you will get notified if you raise a pull-request). - - Add yourself to the AUTHORS.md. - - Use 4 spaces instead of tabs. - -Mailing list ------------- -[libjsonrpccpp-devel@lists.sourceforge.net](https://lists.sourceforge.net/lists/listinfo/libjsonrpccpp-devel) - -Roadmap for next release ------------------------- -- Generate client stubs for other languages. -- Extend doxygen documentation. +## Changelogs -Changelogs ----------- -Changelogs can be found [here](https://github.com/cinemast/libjson-rpc-cpp/blob/master/CHANGELOG.md). +Changelogs can be found [here](CHANGELOG.md). -API compatibility ------------------ +## API compatibility We do our best to keep the API/ABI stable, to prevent problems when updating this framework. A compatiblity report can be found [here](http://upstream.rosalinux.ru/versions/libjson-rpc-cpp.html). -License -------- -This framework is licensed under [MIT](http://en.wikipedia.org/wiki/MIT_License). +## License +This framework is licensed under [MIT](http://en.wikipedia.org/wiki/MIT_License). All of this libraries dependencies are licensed under MIT compatible licenses. -Documentation -------------- -The documentation for this library can be generated using doxygen. -If it is installed on your system, you can simply type: - -```sh -cd build -make doc -``` - -This generates the Latex and HTML documentation into `build/doc` - -Run the tests -------------- -Simply run: - -```sh -make test -``` +References +========== +- [NASA Ames Research Center](http://www.nasa.gov/centers/ames/home/): use it to obtain aircraft state information from an aircraft simulator. +- [LaseShark 3D Printer](https://github.com/macpod/lasershark_3dp): used to control the firmware of the 3D printer. +- [cpp-ethereum](https://github.com/ethereum/cpp-ethereum): C++ implementations for the ethereum crypto currency. +- [mage-sdk-cpp](https://github.com/mage/mage-sdk-cpp): a game engine. +- [bitcodin](http://www.bitmovin.net): a scalable cloud based video transcoding platform. +- [wgslib](http://wgslib.com/): a web geostatistics library. +- [bitcoin-api-cpp](https://github.com/minium/bitcoin-api-cpp): a C++ interface to bitcoin. +- [NIT DASH Content Server](http://www.nit.eu/offer/research-projects-products/334-http2dash): Dynamic Adaptive Streaming over HTTP server. -Testcoverage can be retrieved by invoking the [dev/testcoverage.sh script](https://github.com/cinemast/libjson-rpc-cpp/blob/master/dev/testcoverage.sh) inside the `dev` folder. +If you use this library and find it useful, I would be very pleased if you let me know about it. diff --git a/cmake/CMakeCompilerSettings.cmake b/cmake/CMakeCompilerSettings.cmake index f42f9fec..ba9380e1 100644 --- a/cmake/CMakeCompilerSettings.cmake +++ b/cmake/CMakeCompilerSettings.cmake @@ -1,17 +1,18 @@ # Set necessary compile and link flags - - if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall -Wformat -Wno-format-extra-args -Wformat-security -Wformat-nonliteral -Wformat=2 -Wextra -Wnon-virtual-dtor -fPIC -O0") if(WITH_COVERAGE) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fprofile-arcs -ftest-coverage") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") endif() elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") -# TODO figure clang stuff to enable test-coverage -# Instrument Program flow should be set to Yes -# http://stackoverflow.com/questions/7949781/undefined-symbols-for-architecture-i386-upgrading-project-to-ios-5 - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall -Wextra -Wnon-virtual-dtor -fPIC -O0") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++11 -Wall -Wextra -Wnon-virtual-dtor -fPIC -O0") + if(WITH_COVERAGE) + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fprofile-arcs -ftest-coverage") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") + endif() elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") -# no msvc flags for now -endif() \ No newline at end of file + # no msvc flags for now +endif() diff --git a/cmake/CMakeDependencies.cmake b/cmake/CMakeDependencies.cmake index 0044c847..bb1ebaae 100644 --- a/cmake/CMakeDependencies.cmake +++ b/cmake/CMakeDependencies.cmake @@ -27,9 +27,9 @@ message(STATUS "Threads: ${CMAKE_THREADS_LIBS_INIT}") # find Argtable if(${COMPILE_STUBGEN}) - find_package(Argtable REQUIRED) - message(STATUS "Argtable header: ${ARGTABLE_INCLUDE_DIRS}") - message(STATUS "Argtable lib : ${ARGTABLE_LIBRARIES}") + find_package(Argtable REQUIRED) + message(STATUS "Argtable header: ${ARGTABLE_INCLUDE_DIRS}") + message(STATUS "Argtable lib : ${ARGTABLE_LIBRARIES}") endif() if(${HTTP_CLIENT}) @@ -44,25 +44,33 @@ if(${HTTP_CLIENT}) endif() endif() -if (${HTTP_SERVER}) # find libmicrohttpd - find_package(MHD REQUIRED) - message(STATUS "MHD header: ${MHD_INCLUDE_DIRS}") - message(STATUS "MHD lib : ${MHD_LIBRARIES}") +if (${HTTP_SERVER}) + + find_package(MHD REQUIRED) + message(STATUS "MHD header: ${MHD_INCLUDE_DIRS}") + message(STATUS "MHD lib : ${MHD_LIBRARIES}") +endif() + +# find hiredis +if (${REDIS_SERVER}) + + find_package(Hiredis REQUIRED) + message(STATUS "Hiredis header: ${HIREDIS_INCLUDE_DIRS}") + message(STATUS "Hiredis lib : ${HIREDIS_LIBRARIES}") endif() # find doxygen find_package(Doxygen) if (${COMPILE_TESTS}) - - find_package(Catch) - if(NOT CATCH_FOUND) - message("Could not find catch, downloading it now") + find_package(Catch) + if(NOT CATCH_FOUND) + message("Could not find catch, downloading it now") # Includes Catch in the project: - add_subdirectory(${CMAKE_SOURCE_DIR}/src/catch) - include_directories(${CATCH_INCLUDE_DIR} ${COMMON_INCLUDES}) - else() - INCLUDE_DIRECTORIES(${CATCH_INCLUDE_DIRS}) - endif() + add_subdirectory(${CMAKE_SOURCE_DIR}/src/catch) + include_directories(${CATCH_INCLUDE_DIR} ${COMMON_INCLUDES}) + else() + include_directories(${CATCH_INCLUDE_DIRS}) + endif() endif() diff --git a/cmake/CMakePackaging.cmake b/cmake/CMakePackaging.cmake index 81aa402a..e8c06a48 100644 --- a/cmake/CMakePackaging.cmake +++ b/cmake/CMakePackaging.cmake @@ -8,11 +8,12 @@ set(CPACK_PACKAGE_VERSION_MINOR "${MINOR_VERSION}") set(CPACK_PACKAGE_VERSION_PATCH "${PATCH_VERSION}") if (WIN32 AND NOT UNIX) - set(CPACK_GENERATOR "NSIS") - set(CPACK_NSIS_DISPLAY_NAME "${CPACK_PACKAGE_INSTALL_DIRECTORY} libjson-rpc-cpp") - set(CPACK_NSIS_HELP_LINK "http://github.com/cinemast/libjson-rpc-cpp") - set(CPACK_NSIS_URL_INFO_ABOUT "http://github.com/cinemast/libjson-rpc-cpp") - set(CPACK_NSIS_CONTACT "Peter Spiess-Knafl ") - set(CPACK_NSIS_MODIFY_PATH ON) + set(CPACK_GENERATOR "NSIS") + set(CPACK_NSIS_DISPLAY_NAME "${CPACK_PACKAGE_INSTALL_DIRECTORY} libjson-rpc-cpp") + set(CPACK_NSIS_HELP_LINK "http://github.com/cinemast/libjson-rpc-cpp") + set(CPACK_NSIS_URL_INFO_ABOUT "http://github.com/cinemast/libjson-rpc-cpp") + set(CPACK_NSIS_CONTACT "Peter Spiess-Knafl ") + set(CPACK_NSIS_MODIFY_PATH ON) endif(WIN32 AND NOT UNIX) + include(CPack) diff --git a/cmake/FindArgtable.cmake b/cmake/FindArgtable.cmake index da2daae4..9728dbd4 100644 --- a/cmake/FindArgtable.cmake +++ b/cmake/FindArgtable.cmake @@ -6,15 +6,15 @@ # ARGTABLE_LIBRARIES - Link these to use ARGTABLE find_path ( - ARGTABLE_INCLUDE_DIR - NAMES argtable2.h - DOC "argtable include dir" + ARGTABLE_INCLUDE_DIR + NAMES argtable2.h + DOC "argtable include dir" ) find_library ( - ARGTABLE_LIBRARY - NAMES argtable2 - DOC "argtable library" + ARGTABLE_LIBRARY + NAMES argtable2 + DOC "argtable library" ) set(ARGTABLE_INCLUDE_DIRS ${ARGTABLE_INCLUDE_DIR}) @@ -24,20 +24,17 @@ set(ARGTABLE_LIBRARIES ${ARGTABLE_LIBRARY}) # same naming convention as in qt (appending debug library with d) # boost is using the same "hack" as us with "optimized" and "debug" if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") - find_library( - ARGTABLE_LIBRARY_DEBUG - NAMES argtable2d - DOC "argtable debug library" - ) - - set(ARGTABLE_LIBRARIES optimized ${ARGTABLE_LIBRARIES} debug ${ARGTABLE_LIBRARY_DEBUG}) - + find_library( + ARGTABLE_LIBRARY_DEBUG + NAMES argtable2d + DOC "argtable debug library" + ) + set(ARGTABLE_LIBRARIES optimized ${ARGTABLE_LIBRARIES} debug ${ARGTABLE_LIBRARY_DEBUG}) endif() # handle the QUIETLY and REQUIRED arguments and set JSONCPP_FOUND to TRUE # if all listed variables are TRUE, hide their existence from configuration view include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(argtable DEFAULT_MSG - ARGTABLE_INCLUDE_DIR ARGTABLE_LIBRARY) +find_package_handle_standard_args(argtable DEFAULT_MSG ARGTABLE_INCLUDE_DIR ARGTABLE_LIBRARY) mark_as_advanced (ARGTABLE_INCLUDE_DIR ARGTABLE_LIBRARY) diff --git a/cmake/FindCURL.cmake b/cmake/FindCURL.cmake index 4add8937..1112ac0b 100644 --- a/cmake/FindCURL.cmake +++ b/cmake/FindCURL.cmake @@ -17,10 +17,10 @@ find_path( ) find_library( - CURL_LIBRARY - # names from cmake's FindCURL - NAMES curl curllib libcurl_imp curllib_static libcurl - DOC "curl library" + CURL_LIBRARY + # names from cmake's FindCURL + NAMES curl curllib libcurl_imp curllib_static libcurl + DOC "curl library" ) add_library(CURL::libcurl UNKNOWN IMPORTED) @@ -52,6 +52,5 @@ endif() # handle the QUIETLY and REQUIRED arguments and set CURL_FOUND to TRUE # if all listed variables are TRUE, hide their existence from configuration view include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(CURL DEFAULT_MSG - CURL_INCLUDE_DIR CURL_LIBRARY) +find_package_handle_standard_args(CURL DEFAULT_MSG CURL_INCLUDE_DIR CURL_LIBRARY) mark_as_advanced (CURL_INCLUDE_DIR CURL_LIBRARY) diff --git a/cmake/FindCatch.cmake b/cmake/FindCatch.cmake index f9deaa8d..472ee6d7 100644 --- a/cmake/FindCatch.cmake +++ b/cmake/FindCatch.cmake @@ -4,12 +4,9 @@ find_path( DOC "catch include dir" ) - set(CATCH_INCLUDE_DIRS ${CATCH_INCLUDE_DIR}) - include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(catch DEFAULT_MSG - CATCH_INCLUDE_DIR) +find_package_handle_standard_args(catch DEFAULT_MSG CATCH_INCLUDE_DIR) mark_as_advanced (CATCH_INCLUDE_DIR) diff --git a/cmake/FindHiredis.cmake b/cmake/FindHiredis.cmake new file mode 100755 index 00000000..79a28a98 --- /dev/null +++ b/cmake/FindHiredis.cmake @@ -0,0 +1,26 @@ +# - Try to find Hiredis +# Once done this will define +# +# HIREDIS_FOUND - system has HIREDIS +# HIREDIS_INCLUDE_DIRS - the HIREDIS include directory +# HIREDIS_LIBRARY - Link these to use HIREDIS + +find_path( + HIREDIS_INCLUDE_DIR + NAMES hiredis hiredis.h hiredis/hiredis.h + DOC "hiredis include dir" +) + +find_library( + HIREDIS_LIBRARY + NAMES hiredis libhiredis + DOC "hiredis library" +) + +set(HIREDIS_INCLUDE_DIRS ${HIREDIS_INCLUDE_DIR}) +set(HIREDIS_LIBRARIES ${HIREDIS_LIBRARY}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(hiredis DEFAULT_MSG HIREDIS_INCLUDE_DIR HIREDIS_LIBRARY) +mark_as_advanced(HIREDIS_INCLUDE_DIR HIREDIS_LIBRARY) + diff --git a/cmake/FindJsoncpp.cmake b/cmake/FindJsoncpp.cmake index cf700924..7b2bf1ba 100644 --- a/cmake/FindJsoncpp.cmake +++ b/cmake/FindJsoncpp.cmake @@ -19,9 +19,9 @@ find_path( ) find_library( - JSONCPP_LIBRARY - NAMES jsoncpp - DOC "jsoncpp library" + JSONCPP_LIBRARY + NAMES jsoncpp + DOC "jsoncpp library" ) add_library(jsoncpp_lib_static UNKNOWN IMPORTED) @@ -53,15 +53,15 @@ endif() # find JSONCPP_INCLUDE_PREFIX find_path( - JSONCPP_INCLUDE_PREFIX - NAMES json.h - PATH_SUFFIXES jsoncpp/json json + JSONCPP_INCLUDE_PREFIX + NAMES json.h + PATH_SUFFIXES jsoncpp/json json ) if (${JSONCPP_INCLUDE_PREFIX} MATCHES "jsoncpp") - set(JSONCPP_INCLUDE_PREFIX "jsoncpp/json") + set(JSONCPP_INCLUDE_PREFIX "jsoncpp/json") else() - set(JSONCPP_INCLUDE_PREFIX "json") + set(JSONCPP_INCLUDE_PREFIX "json") endif() @@ -69,6 +69,5 @@ endif() # handle the QUIETLY and REQUIRED arguments and set JSONCPP_FOUND to TRUE # if all listed variables are TRUE, hide their existence from configuration view include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(jsoncpp DEFAULT_MSG - JSONCPP_INCLUDE_DIR JSONCPP_LIBRARY) +find_package_handle_standard_args(jsoncpp DEFAULT_MSG JSONCPP_INCLUDE_DIR JSONCPP_LIBRARY) mark_as_advanced (JSONCPP_INCLUDE_DIR JSONCPP_LIBRARY) diff --git a/cmake/FindMHD.cmake b/cmake/FindMHD.cmake index 279a2e7b..23e31c44 100755 --- a/cmake/FindMHD.cmake +++ b/cmake/FindMHD.cmake @@ -6,15 +6,15 @@ # MHD_LIBRARY - Link these to use MHD find_path( - MHD_INCLUDE_DIR - NAMES microhttpd.h - DOC "microhttpd include dir" + MHD_INCLUDE_DIR + NAMES microhttpd.h + DOC "microhttpd include dir" ) find_library( - MHD_LIBRARY - NAMES microhttpd microhttpd-10 libmicrohttpd libmicrohttpd-dll - DOC "microhttpd library" + MHD_LIBRARY + NAMES microhttpd microhttpd-10 libmicrohttpd libmicrohttpd-dll + DOC "microhttpd library" ) set(MHD_INCLUDE_DIRS ${MHD_INCLUDE_DIR}) @@ -25,19 +25,15 @@ set(MHD_LIBRARIES ${MHD_LIBRARY}) # boost is using the same "hack" as us with "optimized" and "debug" # official MHD project actually uses _d suffix if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") - find_library( - MHD_LIBRARY_DEBUG - NAMES microhttpd_d microhttpd-10_d libmicrohttpd_d libmicrohttpd-dll_d - DOC "mhd debug library" - ) - - set(MHD_LIBRARIES optimized ${MHD_LIBRARIES} debug ${MHD_LIBRARY_DEBUG}) - + find_library( + MHD_LIBRARY_DEBUG + NAMES microhttpd_d microhttpd-10_d libmicrohttpd_d libmicrohttpd-dll_d + DOC "mhd debug library" + ) + set(MHD_LIBRARIES optimized ${MHD_LIBRARIES} debug ${MHD_LIBRARY_DEBUG}) endif() include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(mhd DEFAULT_MSG - MHD_INCLUDE_DIR MHD_LIBRARY) - +find_package_handle_standard_args(mhd DEFAULT_MSG MHD_INCLUDE_DIR MHD_LIBRARY) mark_as_advanced(MHD_INCLUDE_DIR MHD_LIBRARY) diff --git a/cmake/cmake_uninstall.cmake.in b/cmake/cmake_uninstall.cmake.in index 49183b66..6a597a39 100644 --- a/cmake/cmake_uninstall.cmake.in +++ b/cmake/cmake_uninstall.cmake.in @@ -2,27 +2,24 @@ cmake_policy(SET CMP0007 NEW) if (NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") - message(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"") + message(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"") endif(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) string(REGEX REPLACE "\n" ";" files "${files}") list(REVERSE files) foreach (file ${files}) - message(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"") - if (EXISTS "$ENV{DESTDIR}${file}") - execute_process( - COMMAND @CMAKE_COMMAND@ -E remove "$ENV{DESTDIR}${file}" - OUTPUT_VARIABLE rm_out - RESULT_VARIABLE rm_retval - ) - - if (NOT ${rm_retval} EQUAL 0) - message(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"") - endif (NOT ${rm_retval} EQUAL 0) - - else (EXISTS "$ENV{DESTDIR}${file}") - message(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.") - endif (EXISTS "$ENV{DESTDIR}${file}") - + message(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"") + if (EXISTS "$ENV{DESTDIR}${file}") + execute_process( + COMMAND @CMAKE_COMMAND@ -E remove "$ENV{DESTDIR}${file}" + OUTPUT_VARIABLE rm_out + RESULT_VARIABLE rm_retval + ) + if (NOT ${rm_retval} EQUAL 0) + message(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"") + endif (NOT ${rm_retval} EQUAL 0) + else (EXISTS "$ENV{DESTDIR}${file}") + message(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.") + endif (EXISTS "$ENV{DESTDIR}${file}") endforeach(file) diff --git a/cmake/libjsonrpccpp-client.pc.cmake b/cmake/libjsonrpccpp-client.pc.cmake index 2d84a124..295e8456 100644 --- a/cmake/libjsonrpccpp-client.pc.cmake +++ b/cmake/libjsonrpccpp-client.pc.cmake @@ -1,5 +1,5 @@ Name: libjsonrpccpp-client Description: A C++ client implementation of json-rpc. Version: ${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_VERSION} -Libs: -L${FULL_PATH_LIBDIR} -ljsoncpp -ljsonrpccpp-common -ljsonrpccpp-client -lcurl +Libs: -L${FULL_PATH_LIBDIR} -ljsoncpp -ljsonrpccpp-common -ljsonrpccpp-client -lcurl -lhiredis Cflags: -I${FULL_PATH_INCLUDEDIR} diff --git a/cmake/libjsonrpccpp-server.pc.cmake b/cmake/libjsonrpccpp-server.pc.cmake index 580a9422..10688ad4 100644 --- a/cmake/libjsonrpccpp-server.pc.cmake +++ b/cmake/libjsonrpccpp-server.pc.cmake @@ -1,5 +1,5 @@ Name: libjsonrpccpp-server Description: A C++ server implementation of json-rpc. Version: ${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_VERSION} -Libs: -L${FULL_PATH_LIBDIR} -ljsoncpp -ljsonrpccpp-common -ljsonrpccpp-server -lmicrohttpd +Libs: -L${FULL_PATH_LIBDIR} -ljsoncpp -ljsonrpccpp-common -ljsonrpccpp-server -lmicrohttpd -lhiredis Cflags: -I${FULL_PATH_INCLUDEDIR} diff --git a/codecov.yml b/codecov.yml index 9c8d6593..fcd19039 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,3 +1,4 @@ coverage: ignore: - src/test/.* + - src/examples/.* diff --git a/dev/artwork/overview.dia b/dev/artwork/overview.dia index 99cf60bd..6b7cb391 100644 --- a/dev/artwork/overview.dia +++ b/dev/artwork/overview.dia @@ -143,16 +143,16 @@ - + - + - + @@ -167,7 +167,7 @@ - + @@ -184,7 +184,7 @@ - + @@ -197,16 +197,16 @@ - + - + - - - - + + + + @@ -232,16 +232,16 @@ - + - + - + @@ -276,7 +276,7 @@ - + @@ -289,16 +289,16 @@ - + - + - - - - + + + + @@ -324,16 +324,16 @@ - + - + - - - - + + + + @@ -359,16 +359,16 @@ - + - + - + @@ -400,7 +400,7 @@ - + @@ -450,16 +450,16 @@ - + - + - + @@ -491,7 +491,7 @@ - + @@ -541,14 +541,14 @@ - + - + - - + + @@ -578,13 +578,13 @@ - + - + - + @@ -638,7 +638,7 @@ - + @@ -698,10 +698,10 @@ ## - + - + @@ -719,13 +719,13 @@ - + - + - + @@ -779,7 +779,7 @@ - + @@ -860,13 +860,13 @@ - + - + - + @@ -920,7 +920,7 @@ - + @@ -1001,16 +1001,16 @@ - + - + - + @@ -1025,7 +1025,7 @@ - + @@ -1045,7 +1045,7 @@ - + @@ -1058,20 +1058,20 @@ - + - + - - - - - + + + + + @@ -1104,19 +1104,19 @@ - + - + - - - - + + + + @@ -1145,18 +1145,18 @@ - + - + - - - + + + @@ -1187,18 +1187,18 @@ - + - + - - - + + + @@ -1229,19 +1229,19 @@ - + - + - - - - + + + + @@ -1271,113 +1271,12 @@ - - - - - - - - - - - - #Things you need to write# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #Things that are generated# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #Things provided by this framework# - - - - - - - - - - - - - - - - - - - - - - - - + - + - + @@ -1391,7 +1290,40 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + #Implement abstract methods# + + + + + + + + + @@ -1405,5 +1337,137 @@ + + + + + + + + + + + #generates Stubs# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Your application# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Generated code# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #libsjson-rpc-cpp# + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/artwork/overview.png b/dev/artwork/overview.png index 8f9bdd32..7ba1c9c1 100644 Binary files a/dev/artwork/overview.png and b/dev/artwork/overview.png differ diff --git a/dev/ci.sh b/dev/ci.sh deleted file mode 100755 index 5b6b1f44..00000000 --- a/dev/ci.sh +++ /dev/null @@ -1,52 +0,0 @@ -#!/bin/bash -set -e - -build_configuration() { - echo "Building configuration $1" - mkdir -p build - cd build - cmake $1 .. - make - mkdir -p root - make DESTDIR=root install - cd .. -} - -cleanup() { - cd build - make DESTDIR=root uninstall - make clean - rm -f sampleclient - rm -f sampleserver - cd .. - rm -rf build -} -rm -rf reports -mkdir -p reports -build_configuration "-DCMAKE_BUILD_TYPE=Debug -DHTTP_SERVER=YES -DHTTP_CLIENT=YES -DCOMPILE_STUBGEN=YES -DCOMPILE_EXAMPLES=YES -DCOMPILE_TESTS=YES -DUNIX_DOMAIN_SOCKET_SERVER=YES -DUNIX_DOMAIN_SOCKET_CLIENT=YES" -echo "Compiling examples" -cd build -g++ ../src/examples/simpleclient.cpp -Iroot/usr/local/include -Lroot/usr/local/lib -ljsonrpccpp-client -ljsoncpp -ljsonrpccpp-common -lcurl -o sampleclient -g++ ../src/examples/simpleserver.cpp -Iroot/usr/local/include -Lroot/usr/local/lib -ljsonrpccpp-server -ljsoncpp -ljsonrpccpp-common -lmicrohttpd -o sampleserver -test -f sampleclient -test -f sampleserver - -echo "Generating valgrind report" -valgrind --leak-check=full --xml=yes --xml-file=../reports/valgrind.xml ./bin/unit_testsuite --reporter=junit --out=../reports/tests.xml - - -echo "Generating coverage report" -gcovr -e "build" -e "src/test" -x -r .. > ../reports/coverage.xml -gcovr -e "build" -e "src/test" -r .. --html --html-details -o ../reports/coverage.html - -echo "Generating cppcheck report" -cppcheck -I ../src --enable=all --xml ../src --xml-version=2 2> ../reports/cppcheck.xml - -cd .. -echo "Cleanup that mess" -cleanup - -build_configuration "-DCMAKE_BUILD_TYPE=Debug -DHTTP_SERVER=NO -DHTTP_CLIENT=NO -DCOMPILE_STUBGEN=YES -DCOMPILE_EXAMPLES=YES -DCOMPILE_TESTS=YES -DUNIX_DOMAIN_SOCKET_SERVER=NO -DUNIX_DOMAIN_SOCKET_CLIENT=NO" -cleanup - -echo "Integration successful" diff --git a/dev/codecov.sh b/dev/codecov.sh index c69e260e..b0cbeef1 100755 --- a/dev/codecov.sh +++ b/dev/codecov.sh @@ -1,6 +1,8 @@ #!/bin/bash -for filename in `find . | egrep '\.o'`; -do - gcov-5 $filename > /dev/null; +for filename in `find . | egrep '\.o'`; +do + gcov-5 $filename > /dev/null; done + +bash <(curl -s https://codecov.io/bash); diff --git a/dev/codestyle/eclipse_codetemplates.xml b/dev/codestyle/eclipse_codetemplates.xml deleted file mode 100644 index b0a7a385..00000000 --- a/dev/codestyle/eclipse_codetemplates.xml +++ /dev/null @@ -1,2 +0,0 @@ - \ No newline at end of file diff --git a/dev/codestyle/eclipse_formatter.xml b/dev/codestyle/eclipse_formatter.xml deleted file mode 100644 index 62e10b52..00000000 --- a/dev/codestyle/eclipse_formatter.xml +++ /dev/null @@ -1,167 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/dev/codestyle/license.txt b/dev/codestyle/license.txt deleted file mode 100644 index 6eec411c..00000000 --- a/dev/codestyle/license.txt +++ /dev/null @@ -1,8 +0,0 @@ -/************************************************************************* - * libjson-rpc-cpp - ************************************************************************* - * @file %FILENAME% - * @date %DATE% - * @author Peter Spiess-Knafl - * @license See attached LICENSE.txt - ************************************************************************/ diff --git a/dev/codestyle/qtcreator.xml b/dev/codestyle/qtcreator.xml deleted file mode 100644 index cad26781..00000000 --- a/dev/codestyle/qtcreator.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - CodeStyleData - - false - false - true - true - true - false - true - false - true - true - false - true - false - true - false - 4 - true - true - 2 - true - 4 - - - - DisplayName - bitmovin - - diff --git a/dev/createpackage.sh b/dev/createpackage.sh deleted file mode 100755 index f95709d3..00000000 --- a/dev/createpackage.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash - - echo "Bundling for $1" - mkdir -p ../dist/libjson-rpc-cpp - mkdir -p ../dist/libjson-rpc-cpp/doc - cd ../doc - doxygen libjson-rpc-cpp.doxyfile - mv html/* ../dist/libjson-rpc-cpp/doc - #cmake -DCMAKE_INSTALL_PREFIX=../dist/libjson-rpc-cpp - #make install - #cd ../dist - zip -r libjson-rpc-cpp-$1.zip libjson-rpc-cpp - rm -r libjson-rpc-cpp - cd ../dev diff --git a/dev/installdeps.sh b/dev/installdeps.sh deleted file mode 100644 index cf6083db..00000000 --- a/dev/installdeps.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -sudo apt-get install libmicrohttpd-dev libjsoncpp-dev libcurl4-openssl-dev cmake cppcheck valgrind gcovr diff --git a/dev/testcoverage.sh b/dev/testcoverage.sh deleted file mode 100755 index bec01900..00000000 --- a/dev/testcoverage.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash -cd ../build - -echo "Building debug build" -cmake -DCMAKE_BUILD_TYPE=Debug .. -make -j4 - -echo "Executing Test Suite" -bin/unit_testsuite - -echo "Generate HTML report" -mkdir -p reports -gcovr -r .. -d -e "build" -e "src/test" --html --html-details -o reports/coverage.html -xdg-open reports/coverage.html -cd ../dev - diff --git a/doc/manpage.in b/doc/manpage.in index 92b54fa5..f1d5e231 100644 --- a/doc/manpage.in +++ b/doc/manpage.in @@ -1,19 +1,20 @@ .\" Manpage for jsonrpcstub. .\" Contact psk@autistici.org to correct errors or typos. -.TH man 1 "7 April 2015" "@MAJOR_VERSION@.@MINOR_VERSION@.@PATCH_VERSION@" "jsonrpcstub man page" +.TH man 1 "22 April 2017" "@MAJOR_VERSION@.@MINOR_VERSION@.@PATCH_VERSION@" "jsonrpcstub man page" .SH NAME jsonrpcstub \- genearate stubs for the libjson\-rpc\-cpp framework. .SH SYNOPSIS .B -jsonrpcstub specfile.json [\-\-cpp\-server=namespace::ClassName] - [\-\-cpp\-server\-file=classqname.h] [\-\-cpp\-client=namespace::ClassName] -[\-\-cpp\-client-file=classname.h] [\-\-js\-client=ClassName] -[\-\-js-client-file=classname.js] [\-h] [\-v] [\-\-version] +jsonrpcstub specfile.json [\-\-cpp\-server=namespace::ClassName] + [\-\-cpp\-server\-file=classqname.h] [\-\-cpp\-client=namespace::ClassName] +[\-\-cpp\-client-file=classname.h] [\-\-js\-client=ClassName] +[\-\-js-client-file=classname.js] [\-\-py\-client=ClassName] +[\-\-py\-client\-file=classname.py] [\-h] [\-v] [\-\-version] .PP .SH DESCRIPTION .PP -jsonrpcstub is a tool to generate C++ and JavaScript classes from a procedure specification file. +jsonrpcstub is a tool to generate C++, JavaScript, and Python classes from a procedure specification file. .SS SPECIFICATION SYNTAX .PP The specifictaion file is a JSON file containing all available JSON\-RPC methods and notifications @@ -38,9 +39,10 @@ with their corresponding parameters and return values contained in a top\-level .fi .PP -The literal in each \fB"params"\fP and \fB"returns"\fP section defines the corresponding type. -If the \fb"params"\fP contains an array, the parameters are accepted by position, -if it contains an object, they are accepted by name. +The literal in each \fB"params"\fP and \fB"returns"\fP section defines the corresponding type. +If the \fb"params"\fP contains an array, the parameters are accepted by position, +if it contains an object, they are accepted by name. + .SH OPTIONS .IP \-h Print usage information. @@ -49,34 +51,48 @@ Print verbose information during generation. .IP \-\-version Print version info and exit. .IP \-\-cpp\-server=ClassName -Creates a Abstract Server class. Namespaces can be provided using the :: notation +Creates a Abstract Server class. Namespaces can be provided using the :: notation (e.g. ns1::ns2::Classname). .IP \-\-cpp\-server\-file=filename.h Defines the filename to use when generating the C++ Abstract Server class. If this is not provided, the lowercase classname is used. .IP \-\-cpp\-client=ClassName -Creates a C++ client class. Namespaces can be provided using the :: notation +Creates a C++ client class. Namespaces can be provided using the :: notation (e.g. ns1::ns2::Classname). .IP \-\-cpp\-client\-file=filename.h Defines the filename to use when generating the C++ client class. If this is not provided, the lowercase classname is used. .IP \-\-js\-client=ClassName -Creates a JavaScript client class. No namespaces are supported in this option. +Creates a JavaScript client class. No namespaces are supported in this option. .IP \-\-js\-client-file=filename.js -Defines the filename to use when generating the JavaScrip client class. +Defines the filename to use when generating the JavaScript client class. +.IP \-\-py\-client=ClassName +Creates a Python client class. No namespaces are supported in this option. +.IP \-\-py\-client\-file=filename.py +Defines the filename to use when generating the Python client class. +If this is not provided, the lowercase classname is used. + .SH EXAMPLES .PP Generate C++ Stubs for Server and Client, the classes will be named AbstractStubServer and StubClient: -.P P +.IP .B -\& jsonrpcstub spec.json \-\-cpp\-server=AbstractStubServer \-\-cpp\-client=StubClient +\&jsonrpcstub spec.json \-\-cpp\-server=AbstractStubServer \-\-cpp\-client=StubClient .B .PP Generate JavaScript Client class MyRpcClient into file someclient.js: +.IP +.B +\&jsonrpcstub spec.json \-\-js\-client=MyRpcClient \-\-js\-client\-file=someclient.js +.B .PP +Generate Python client class StubClient, which will be saved into stubclient.py +.IP .B -\& jsonrpcstub spec.json \-\-js\-client=MyRpcClient \-\-js\-client\-file=someclient.js +\&jsonrpcstub spec.json \-\-py\-client=StubClient .B +.PP + .SH EXIT STATUS This command returns 0 if no error occurred. In any other case, it returns 1. .SH SEE ALSO @@ -86,23 +102,23 @@ No known bugs. Please report found bugs as an issue on github or send me an emai .SH COPYRIGHT -Copyright (C) 2011\-2015 Peter Spiess\-Knafl +Copyright (C) 2011\-2017 Peter Spiess\-Knafl -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in the -Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in the +Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. .SH AUTHOR diff --git a/docker/ArchLinux.Dockerfile b/docker/ArchLinux.Dockerfile new file mode 100644 index 00000000..f478a2b7 --- /dev/null +++ b/docker/ArchLinux.Dockerfile @@ -0,0 +1,27 @@ +FROM base/archlinux:latest +MAINTAINER Peter Spiess-Knafl + +RUN pacman -Sy --noconfirm \ + sudo \ + sed \ + grep \ + awk \ + fakeroot \ + wget \ + cmake \ + make \ + gcc \ + git \ + jsoncpp \ + libmicrohttpd \ + curl \ + hiredis \ + redis + +RUN sudo -u nobody mkdir /tmp/argtable && cd /tmp && git clone https://aur.archlinux.org/argtable.git && cd argtable && sudo -u nobody makepkg +RUN pacman -U --noconfirm /tmp/argtable/*.pkg.tar.xz +RUN mkdir /app +COPY docker/build_test_install.sh /app +COPY . /app +RUN chmod a+x /app/build_test_install.sh +RUN cd /app && ./build_test_install.sh diff --git a/docker/Debian10.Dockerfile b/docker/Debian10.Dockerfile new file mode 100644 index 00000000..b92f0a2c --- /dev/null +++ b/docker/Debian10.Dockerfile @@ -0,0 +1,20 @@ +FROM debian:buster +MAINTAINER Peter Spiess-Knafl + +RUN apt-get update && apt-get install -y \ + wget \ + build-essential \ + cmake \ + libjsoncpp-dev \ + libargtable2-dev \ + libcurl4-openssl-dev \ + libmicrohttpd-dev \ + git \ + libhiredis-dev \ + redis-server + +RUN mkdir /app +COPY docker/build_test_install.sh /app +COPY . /app +RUN chmod a+x /app/build_test_install.sh +RUN cd /app && ./build_test_install.sh diff --git a/docker/Debian8.Dockerfile b/docker/Debian8.Dockerfile new file mode 100644 index 00000000..f2b1ab71 --- /dev/null +++ b/docker/Debian8.Dockerfile @@ -0,0 +1,20 @@ +FROM debian:8 +MAINTAINER Peter Spiess-Knafl + +RUN apt-get update && apt-get install -y \ + wget \ + build-essential \ + cmake \ + libjsoncpp-dev \ + libargtable2-dev \ + libcurl4-openssl-dev \ + libmicrohttpd-dev \ + git \ + libhiredis-dev \ + redis-server + +RUN mkdir /app +COPY docker/build_test_install.sh /app +COPY . /app +RUN chmod a+x /app/build_test_install.sh +RUN cd /app && ./build_test_install.sh diff --git a/docker/Debian9.Dockerfile b/docker/Debian9.Dockerfile new file mode 100644 index 00000000..cf860e1a --- /dev/null +++ b/docker/Debian9.Dockerfile @@ -0,0 +1,20 @@ +FROM debian:9 +MAINTAINER Peter Spiess-Knafl + +RUN apt-get update && apt-get install -y \ + wget \ + build-essential \ + cmake \ + libjsoncpp-dev \ + libargtable2-dev \ + libcurl4-openssl-dev \ + libmicrohttpd-dev \ + git \ + libhiredis-dev \ + redis-server + +RUN mkdir /app +COPY docker/build_test_install.sh /app +COPY . /app +RUN chmod a+x /app/build_test_install.sh +RUN cd /app && ./build_test_install.sh diff --git a/docker/Dockerfile b/docker/Dockerfile deleted file mode 100644 index 74113a80..00000000 --- a/docker/Dockerfile +++ /dev/null @@ -1,55 +0,0 @@ -# THIS DOCKERFILE DOWNLOADS AND COMPILES CURL, LIBMICROHTTPD, JSONCPP, ARGTABLE AND LIBJSON-RPC-CPP FOR LINUX/DEBIAN - -# 2015, author: Péricles Lopes Machado (gogo40) -# Based on Victor Laskin Dockerfile (http://vitiy.info/dockerfile-example-to-compile-libcurl-for-android-inside-docker-container/) - -FROM debian:sid - -MAINTAINER Péricles Lopes Machado - -# Create output directories - -# Directory to export generated files -RUN mkdir /output - -# Directory with generated files -RUN mkdir /build && mkdir /build/include - -# Install compilation tools - -RUN apt-get update - -RUN apt-get install -y \ -wget \ -build-essential \ -cmake \ -libjsoncpp-dev \ -libargtable2-dev \ -libcurl4-openssl-dev \ -libmicrohttpd-dev \ -gcovr \ -git - -# Clone and build libjson-rpc-cpp - -RUN git clone https://github.com/cinemast/libjson-rpc-cpp.git - -RUN cd /libjson-rpc-cpp && \ -cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_STATIC_LIBS=ON -DBUILD_SHARED_LIBS=ON \ -/libjson-rpc-cpp - -RUN cd /libjson-rpc-cpp && \ -make -j $(nproc) && \ -make install - -# Copy to output generated files - -RUN cp -r /libjson-rpc-cpp/lib /build -RUN cp -r /usr/local/include/jsonrpccpp /build/include/jsonrpccpp - -# To get the results run container with output folder -# Example: docker run -v HOSTFOLDER:/output --rm=true IMAGENAME - -ENTRYPOINT cp -r /build/* /output - - diff --git a/docker/Fedora.Dockerfile b/docker/Fedora.Dockerfile new file mode 100644 index 00000000..95aaeea1 --- /dev/null +++ b/docker/Fedora.Dockerfile @@ -0,0 +1,21 @@ +FROM fedora:latest +MAINTAINER Peter Spiess-Knafl + +RUN dnf -y install \ + gcc-c++ \ + jsoncpp-devel \ + libcurl-devel \ + libmicrohttpd-devel \ + catch-devel \ + git \ + cmake \ + make \ + argtable-devel \ + hiredis-devel \ + redis + +RUN mkdir /app +COPY docker/build_test_install.sh /app +COPY . /app +RUN chmod a+x /app/build_test_install.sh +RUN cd /app && ./build_test_install.sh diff --git a/docker/Makefile b/docker/Makefile new file mode 100644 index 00000000..8c49eb70 --- /dev/null +++ b/docker/Makefile @@ -0,0 +1,23 @@ + +all: debian8 debian9 debian10 ubuntu1604 ubuntu1704 arch fedora + +debian8: Debian8.Dockerfile + docker build --rm -f $< .. + +debian9: Debian9.Dockerfile + docker build --rm -f $< .. + +debian10: Debian10.Dockerfile + docker build --rm -f $< .. + +ubuntu1604: Ubuntu1604.Dockerfile + docker build --rm -f $< .. + +ubuntu1704: Ubuntu1704.Dockerfile + docker build --rm -f $< .. + +arch: ArchLinux.Dockerfile + docker build --rm -f $< .. + +fedora: Fedora.Dockerfile + docker build --rm -f $< .. diff --git a/docker/Ubuntu1604.Dockerfile b/docker/Ubuntu1604.Dockerfile new file mode 100644 index 00000000..7af8ec01 --- /dev/null +++ b/docker/Ubuntu1604.Dockerfile @@ -0,0 +1,20 @@ +FROM ubuntu:16.04 +MAINTAINER Peter Spiess-Knafl + +RUN apt-get update && apt-get install -y \ + wget \ + build-essential \ + cmake \ + libjsoncpp-dev \ + libargtable2-dev \ + libcurl4-openssl-dev \ + libmicrohttpd-dev \ + git \ + libhiredis-dev \ + redis-server + +RUN mkdir /app +COPY docker/build_test_install.sh /app +COPY . /app +RUN chmod a+x /app/build_test_install.sh +RUN cd /app && ./build_test_install.sh diff --git a/docker/Ubuntu1704.Dockerfile b/docker/Ubuntu1704.Dockerfile new file mode 100644 index 00000000..3ab1c95a --- /dev/null +++ b/docker/Ubuntu1704.Dockerfile @@ -0,0 +1,20 @@ +FROM ubuntu:17.04 +MAINTAINER Peter Spiess-Knafl + +RUN apt-get update && apt-get install -y \ + wget \ + build-essential \ + cmake \ + libjsoncpp-dev \ + libargtable2-dev \ + libcurl4-openssl-dev \ + libmicrohttpd-dev \ + git \ + libhiredis-dev \ + redis-server + +RUN mkdir /app +COPY docker/build_test_install.sh /app +COPY . /app +RUN chmod a+x /app/build_test_install.sh +RUN cd /app && ./build_test_install.sh diff --git a/docker/build_linux_debian_libs.sh b/docker/build_linux_debian_libs.sh deleted file mode 100755 index a0b56b5a..00000000 --- a/docker/build_linux_debian_libs.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -docker build -t debian/libjson-rpc-cpp . -docker run -v $PWD/output:/output --rm=true debian/libjson-rpc-cpp - diff --git a/docker/build_test_install.sh b/docker/build_test_install.sh new file mode 100755 index 00000000..ffe5ac65 --- /dev/null +++ b/docker/build_test_install.sh @@ -0,0 +1,45 @@ +#!/bin/bash +set -v +set -e + +if [ "$TRAVIS_OS_NAME" == "osx" ] +then + PREFIX=/usr/local +else + PREFIX=/usr +fi + +CLIENT_LIBS="-ljsoncpp -lcurl -ljsonrpccpp-common -ljsonrpccpp-client -lhiredis" +SERVER_LIBS="-ljsoncpp -lmicrohttpd -ljsonrpccpp-common -ljsonrpccpp-server -lhiredis" +mkdir -p build && cd build +cmake -DCMAKE_INSTALL_PREFIX="$PREFIX" -DWITH_COVERAGE=YES -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_STATIC_LIBS=ON -DTCP_SOCKET_SERVER=YES -DTCP_SOCKET_CLIENT=YES \ + -DBUILD_SHARED_LIBS=ON .. +make -j$(nproc) +./bin/unit_testsuite + +if [ "$OS" == "native" ] +then + sudo make install + if [ "$TRAVIS_OS_NAME" != "osx" ] + then + sudo ldconfig + fi +else + make install + ldconfig +fi +cd ../src/examples +g++ -std=c++11 simpleclient.cpp $CLIENT_LIBS -o simpleclient +g++ -std=c++11 simpleserver.cpp $SERVER_LIBS -o simpleserver + +mkdir -p gen && cd gen +jsonrpcstub ../spec.json --cpp-server=AbstractStubServer --cpp-client=StubClient +cd .. +g++ -std=c++11 stubclient.cpp $CLIENT_LIBS -o stubclient +g++ -std=c++11 stubserver.cpp $SERVER_LIBS -o stubserver + +test -f simpleclient +test -f simpleserver +test -f stubserver +test -f stubclient diff --git a/src/examples/CMakeLists.txt b/src/examples/CMakeLists.txt index a2efb16f..d84bf57f 100644 --- a/src/examples/CMakeLists.txt +++ b/src/examples/CMakeLists.txt @@ -1,34 +1,34 @@ file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/gen) add_custom_command( - OUTPUT ${CMAKE_BINARY_DIR}/gen/abstractstubserver.h - COMMAND jsonrpcstub ARGS ${CMAKE_CURRENT_SOURCE_DIR}/spec.json --cpp-server=AbstractStubServer --cpp-server-file=${CMAKE_BINARY_DIR}/gen/abstractstubserver.h - MAIN_DEPENDENCY spec.json - DEPENDS jsonrpcstub - COMMENT "Generating Server Stubfiles" - VERBATIM + OUTPUT ${CMAKE_BINARY_DIR}/gen/abstractstubserver.h + COMMAND jsonrpcstub ARGS ${CMAKE_CURRENT_SOURCE_DIR}/spec.json --cpp-server=AbstractStubServer --cpp-server-file=${CMAKE_BINARY_DIR}/gen/abstractstubserver.h + MAIN_DEPENDENCY spec.json + DEPENDS jsonrpcstub + COMMENT "Generating Server Stubfiles" + VERBATIM ) add_custom_command( - OUTPUT ${CMAKE_BINARY_DIR}/gen/stubclient.h - COMMAND jsonrpcstub ARGS ${CMAKE_CURRENT_SOURCE_DIR}/spec.json --cpp-client=StubClient --cpp-client-file=${CMAKE_BINARY_DIR}/gen/stubclient.h - MAIN_DEPENDENCY spec.json - DEPENDS jsonrpcstub - COMMENT "Generating Client Stubfile" - VERBATIM + OUTPUT ${CMAKE_BINARY_DIR}/gen/stubclient.h + COMMAND jsonrpcstub ARGS ${CMAKE_CURRENT_SOURCE_DIR}/spec.json --cpp-client=StubClient --cpp-client-file=${CMAKE_BINARY_DIR}/gen/stubclient.h + MAIN_DEPENDENCY spec.json + DEPENDS jsonrpcstub + COMMENT "Generating Client Stubfile" + VERBATIM ) add_custom_target(common_stubs - DEPENDS ${CMAKE_BINARY_DIR}/gen/abstractstubserver.h ${CMAKE_BINARY_DIR}/gen/stubclient.h + DEPENDS ${CMAKE_BINARY_DIR}/gen/abstractstubserver.h ${CMAKE_BINARY_DIR}/gen/stubclient.h ) add_custom_command( - OUTPUT ${CMAKE_BINARY_DIR}/gen/xbmcremote.h - COMMAND jsonrpcstub ARGS ${CMAKE_CURRENT_SOURCE_DIR}/xbmc_remote.json --cpp-client=XbmcRemoteClient --cpp-client-file=${CMAKE_BINARY_DIR}/gen/xbmcremote.h - MAIN_DEPENDENCY xbmc_remote.json - DEPENDS jsonrpcstub - COMMENT "Generating Client XBMC Stubfile" - VERBATIM + OUTPUT ${CMAKE_BINARY_DIR}/gen/xbmcremote.h + COMMAND jsonrpcstub ARGS ${CMAKE_CURRENT_SOURCE_DIR}/xbmc_remote.json --cpp-client=XbmcRemoteClient --cpp-client-file=${CMAKE_BINARY_DIR}/gen/xbmcremote.h + MAIN_DEPENDENCY xbmc_remote.json + DEPENDS jsonrpcstub + COMMENT "Generating Client XBMC Stubfile" + VERBATIM ) include_directories(..) @@ -36,30 +36,27 @@ include_directories(${CMAKE_BINARY_DIR}) include_directories(${MHD_INCLUDE_DIRS}) if(UNIX) - if (UNIX_DOMAIN_SOCKET_SERVER AND UNIX_DOMAIN_SOCKET_CLIENT) - add_executable(unixdomainsocketserversample unixdomainsocketserver.cpp) - target_link_libraries(unixdomainsocketserversample jsonrpcserver) - - add_executable(unixdomainsocketclientsample unixdomainsocketclient.cpp) - target_link_libraries(unixdomainsocketclientsample jsonrpcclient) - endif (UNIX_DOMAIN_SOCKET_SERVER AND UNIX_DOMAIN_SOCKET_CLIENT) + if(UNIX_DOMAIN_SOCKET_SERVER AND UNIX_DOMAIN_SOCKET_CLIENT) + add_executable(unixdomainsocketserversample unixdomainsocketserver.cpp) + target_link_libraries(unixdomainsocketserversample jsonrpcserver) + add_executable(unixdomainsocketclientsample unixdomainsocketclient.cpp) + target_link_libraries(unixdomainsocketclientsample jsonrpcclient) + endif() - if (FILE_DESCRIPTOR_SERVER AND FILE_DESCRIPTOR_CLIENT) + if(FILE_DESCRIPTOR_SERVER AND FILE_DESCRIPTOR_CLIENT) add_executable(filedescriptorserversample filedescriptorserver.cpp) target_link_libraries(filedescriptorserversample jsonrpcserver) - add_executable(filedescriptorclientsample filedescriptorclient.cpp) target_link_libraries(filedescriptorclientsample jsonrpcclient) - endif (FILE_DESCRIPTOR_SERVER AND FILE_DESCRIPTOR_CLIENT) + endif() endif(UNIX) if (TCP_SOCKET_SERVER AND TCP_SOCKET_CLIENT) - add_executable(tcpsocketclient tcpsocketclient.cpp) - target_link_libraries(tcpsocketclient jsonrpcclient) - - add_executable(tcpsocketserver tcpsocketserver.cpp) - target_link_libraries(tcpsocketserver jsonrpcserver) -endif (TCP_SOCKET_SERVER AND TCP_SOCKET_CLIENT) + add_executable(tcpsocketclient tcpsocketclient.cpp) + target_link_libraries(tcpsocketclient jsonrpcclient) + add_executable(tcpsocketserver tcpsocketserver.cpp) + target_link_libraries(tcpsocketserver jsonrpcserver) +endif() if(HTTP_SERVER) add_executable(simpleserversample simpleserver.cpp) @@ -71,10 +68,20 @@ if(HTTP_CLIENT) target_link_libraries(simpleclientsample jsonrpcclient) endif() +if(REDIS_SERVER) + add_executable(redisserversample redisserver.cpp) + target_link_libraries(redisserversample jsonrpcserver) +endif() + +if(REDIS_CLIENT) + add_executable(redisclientsample redisclient.cpp) + target_link_libraries(redisclientsample jsonrpcclient) +endif() + if (COMPILE_STUBGEN) if(HTTP_CLIENT) - add_executable(stubclientsample stubclient.cpp ${CMAKE_BINARY_DIR}/gen/stubclient.h) - target_link_libraries(stubclientsample jsonrpcclient) + add_executable(stubclientsample stubclient.cpp ${CMAKE_BINARY_DIR}/gen/stubclient.h) + target_link_libraries(stubclientsample jsonrpcclient) add_executable(xbmcremote xbmcremote.cpp ${CMAKE_BINARY_DIR}/gen/xbmcremote.h) target_link_libraries(xbmcremote jsonrpcclient) endif() diff --git a/src/examples/filedescriptorclient.cpp b/src/examples/filedescriptorclient.cpp index 927f7984..6150ce59 100644 --- a/src/examples/filedescriptorclient.cpp +++ b/src/examples/filedescriptorclient.cpp @@ -2,7 +2,8 @@ * @file filedescriptorclient.cpp * @date 27.10.2016 * @author Jean-Daniel Michaud - * @brief An example implementation of a client based on plain old file descriptors + * @brief An example implementation of a client based on plain old file + * descriptors */ /* @@ -28,28 +29,24 @@ * compliant json string in the standard input to test it. */ +#include #include #include -#include #include using namespace jsonrpc; using namespace std; -int main() -{ +int main() { FileDescriptorClient client(STDIN_FILENO, STDOUT_FILENO); Client c(client); Json::Value params; params["name"] = "Peter"; - try - { + try { cerr << "client:" << c.CallMethod("sayHello", params) << endl; - } - catch (JsonRpcException& e) - { + } catch (JsonRpcException &e) { cerr << e.what() << endl; } } diff --git a/src/examples/filedescriptorserver.cpp b/src/examples/filedescriptorserver.cpp index bde4dfd2..a897063d 100644 --- a/src/examples/filedescriptorserver.cpp +++ b/src/examples/filedescriptorserver.cpp @@ -2,7 +2,8 @@ * @file filedescriptorserver.cpp * @date 11.05.2015 * @author Alexandre Poirot - * @brief An example implementation of a server based on plain old file descriptors + * @brief An example implementation of a server based on plain old file + * descriptors */ /* @@ -28,62 +29,54 @@ * compliant json string in the standard input to test it. */ -#include -#include #include #include #include +#include +#include #include - #define DELIMITER_CHAR char(0x0A) using namespace jsonrpc; using namespace std; -class SampleServer : public AbstractServer -{ +class SampleServer : public AbstractServer { public: - SampleServer(FileDescriptorServer &server) : - AbstractServer(server) - { - this->bindAndAddMethod(Procedure("sayHello", PARAMS_BY_NAME, JSON_STRING, "name", JSON_STRING, NULL), &SampleServer::sayHello); - this->bindAndAddNotification(Procedure("notifyServer", PARAMS_BY_NAME, NULL), &SampleServer::notifyServer); + SampleServer(FileDescriptorServer &server) + : AbstractServer(server) { + this->bindAndAddMethod(Procedure("sayHello", PARAMS_BY_NAME, JSON_STRING, + "name", JSON_STRING, NULL), + &SampleServer::sayHello); + this->bindAndAddNotification( + Procedure("notifyServer", PARAMS_BY_NAME, NULL), + &SampleServer::notifyServer); } - //method - void sayHello(const Json::Value& request, Json::Value& response) - { + // method + void sayHello(const Json::Value &request, Json::Value &response) { response = "Hello: " + request["name"].asString(); } - //notification - void notifyServer(const Json::Value& request) - { + // notification + void notifyServer(const Json::Value &request) { (void)request; exit(0); } }; -int main() -{ - try - { +int main() { + try { FileDescriptorServer server(STDIN_FILENO, STDOUT_FILENO); SampleServer serv(server); - if (serv.StartListening()) - { + if (serv.StartListening()) { cerr << "Server started successfully" << endl; - server.Wait(); - } - else - { + getchar(); + } else { cerr << "Error starting Server" << endl; } - } - catch (jsonrpc::JsonRpcException& e) - { + } catch (jsonrpc::JsonRpcException &e) { cerr << e.what() << endl; } } diff --git a/src/examples/redisclient.cpp b/src/examples/redisclient.cpp new file mode 100644 index 00000000..4fec4993 --- /dev/null +++ b/src/examples/redisclient.cpp @@ -0,0 +1,34 @@ +/** + * @file redisclient.cpp + * @date 18.08.2017 + * @author Jacques Software + * @brief This is a simple redis client example. + */ + +#include +#include +#include +#include + +using namespace jsonrpc; +using namespace std; + +int main() { + + execlp("redis-server", "redis-server", "-v", NULL); + + RedisClient client("127.0.0.1", 6379, "example"); + client.SetTimeout(1); + Client c(client); + + Json::Value params; + params["name"] = "Peter"; + Json::Value params2; + + try { + cout << c.CallMethod("sayHello", params) << endl; + c.CallNotification("notifyServer", params2); + } catch (JsonRpcException &e) { + cerr << e.what() << endl; + } +} diff --git a/src/examples/redisserver.cpp b/src/examples/redisserver.cpp new file mode 100644 index 00000000..9fc7f8c3 --- /dev/null +++ b/src/examples/redisserver.cpp @@ -0,0 +1,54 @@ +/** + * @file redisserver.cpp + * @date 18.08.2017 + * @author Jacques Software + * @brief This is a simple redis server example. + */ + +#include +#include +#include +#include +#include + +using namespace jsonrpc; +using namespace std; + +class SampleServer : public AbstractServer { +public: + SampleServer(RedisServer &server) : AbstractServer(server) { + this->bindAndAddMethod(Procedure("sayHello", PARAMS_BY_NAME, JSON_STRING, + "name", JSON_STRING, NULL), + &SampleServer::sayHello); + this->bindAndAddNotification( + Procedure("notifyServer", PARAMS_BY_NAME, NULL), + &SampleServer::notifyServer); + } + + // method + void sayHello(const Json::Value &request, Json::Value &response) { + response = "Hello: " + request["name"].asString(); + } + + // notification + void notifyServer(const Json::Value &request) { + (void)request; + cout << "server received some Notification" << endl; + } +}; + +int main() { + try { + RedisServer server("127.0.0.1", 6379, "example"); + SampleServer serv(server); + if (serv.StartListening()) { + cout << "Server started successfully" << endl; + getchar(); + serv.StopListening(); + } else { + cout << "Error starting Server" << endl; + } + } catch (jsonrpc::JsonRpcException &e) { + cerr << e.what() << endl; + } +} diff --git a/src/examples/simpleclient.cpp b/src/examples/simpleclient.cpp index a54d0dcb..806f0091 100644 --- a/src/examples/simpleclient.cpp +++ b/src/examples/simpleclient.cpp @@ -5,29 +5,23 @@ * @brief This is a simple client example. */ +#include #include #include -#include using namespace jsonrpc; using namespace std; -int main() -{ - HttpClient client("http://localhost:8383"); - Client c(client); - - Json::Value params; - params["name"] = "Peter"; - - try - { - cout << c.CallMethod("sayHello", params) << endl; - } - catch (JsonRpcException& e) - { - cerr << e.what() << endl; - } +int main() { + HttpClient client("http://localhost:8383"); + Client c(client); + Json::Value params; + params["name"] = "Peter"; + try { + cout << c.CallMethod("sayHello", params) << endl; + } catch (JsonRpcException &e) { + cerr << e.what() << endl; + } } diff --git a/src/examples/simpleserver.cpp b/src/examples/simpleserver.cpp index ab758f13..50efd57c 100644 --- a/src/examples/simpleserver.cpp +++ b/src/examples/simpleserver.cpp @@ -5,63 +5,56 @@ * @brief main.cpp */ -#include -#include #include #include #include #include - - +#include +#include using namespace jsonrpc; using namespace std; -class SampleServer : public AbstractServer -{ - public: - SampleServer(HttpServer &server) : - AbstractServer(server) - { - this->bindAndAddMethod(Procedure("sayHello", PARAMS_BY_NAME, JSON_STRING, "name", JSON_STRING, NULL), &SampleServer::sayHello); - this->bindAndAddNotification(Procedure("notifyServer", PARAMS_BY_NAME, NULL), &SampleServer::notifyServer); - } - - //method - void sayHello(const Json::Value& request, Json::Value& response) - { - response = "Hello: " + request["name"].asString(); - } - - //notification - void notifyServer(const Json::Value& request) - { - (void)request; - cout << "server received some Notification" << endl; - } +class SampleServer : public AbstractServer { +public: + SampleServer(HttpServer &server) : AbstractServer(server) { + this->bindAndAddMethod(Procedure("sayHello", PARAMS_BY_NAME, JSON_STRING, + "name", JSON_STRING, NULL), + &SampleServer::sayHello); + this->bindAndAddNotification( + Procedure("notifyServer", PARAMS_BY_NAME, NULL), + &SampleServer::notifyServer); + } + + // method + void sayHello(const Json::Value &request, Json::Value &response) { + response = "Hello: " + request["name"].asString(); + } + + // notification + void notifyServer(const Json::Value &request) { + (void)request; + cout << "server received some Notification" << endl; + } }; -int main() -{ - try - { - HttpServer server(8383); - SampleServer serv(server); - if (serv.StartListening()) - { - cout << "Server started successfully" << endl; - getchar(); - serv.StopListening(); - } - else - { - cout << "Error starting Server" << endl; - } - } - catch (jsonrpc::JsonRpcException& e) - { - cerr << e.what() << endl; +int main() { + try { + HttpServer server(8383); + SampleServer serv(server); + if (serv.StartListening()) { + cout << "Server started successfully" << endl; + getchar(); + serv.StopListening(); + } else { + cout << "Error starting Server" << endl; } - //curl --data "{\"jsonrpc\":\"2.0\",\"method\":\"sayHello\",\"id\":1,\"params\":{\"name\":\"peter\"}}" localhost:8383 - //curl --data "{\"jsonrpc\":\"2.0\",\"method\":\"notifyServer\", \"params\": null}" localhost:8383 + } catch (jsonrpc::JsonRpcException &e) { + cerr << e.what() << endl; + } + // curl --data + // "{\"jsonrpc\":\"2.0\",\"method\":\"sayHello\",\"id\":1,\"params\":{\"name\":\"peter\"}}" + // localhost:8383 curl --data + // "{\"jsonrpc\":\"2.0\",\"method\":\"notifyServer\", \"params\": null}" + // localhost:8383 } diff --git a/src/examples/stubclient.cpp b/src/examples/stubclient.cpp index 0a980da7..957d92b1 100644 --- a/src/examples/stubclient.cpp +++ b/src/examples/stubclient.cpp @@ -15,47 +15,38 @@ using namespace jsonrpc; using namespace std; -int main() -{ +int main() { - Json::Value a = 3; - Json::Value b = "3"; + Json::Value a = 3; + Json::Value b = "3"; - std::map responses; + std::map responses; - responses[a] = b; - responses[b] = "asölfj"; + responses[a] = b; + responses[b] = "asölfj"; - cout << responses[b] << endl; + cout << responses[b] << endl; - if (a == b) - { - cout << a.toStyledString() << " == " << b.toStyledString() << endl; - } - else - { - cout << a.toStyledString() << " != " << b.toStyledString() << endl; - } + if (a == b) { + cout << a.toStyledString() << " == " << b.toStyledString() << endl; + } else { + cout << a.toStyledString() << " != " << b.toStyledString() << endl; + } - HttpClient httpclient("http://localhost:8383"); - //StubClient c(httpclient, JSONRPC_CLIENT_V1); //json-rpc 1.0 - StubClient c(httpclient, JSONRPC_CLIENT_V2); //json-rpc 2.0 + HttpClient httpclient("http://localhost:8383"); + // StubClient c(httpclient, JSONRPC_CLIENT_V1); //json-rpc 1.0 + StubClient c(httpclient, JSONRPC_CLIENT_V2); // json-rpc 2.0 + try { + cout << c.sayHello("Peter Knafl") << endl; + c.notifyServer(); - try - { - cout << c.sayHello("Peter Knafl") << endl; - c.notifyServer(); + cout << " 3 + 5 = " << c.addNumbers(3, 5) << endl; + cout << " 3.2 + 5.3 = " << c.addNumbers2(3.2, 5.3) << endl; + cout << "Compare: " << c.isEqual("Peter", "peter") << endl; + cout << "Build object: " << c.buildObject("Peter", 1990) << endl; - cout << " 3 + 5 = " << c.addNumbers(3,5) << endl; - cout << " 3.2 + 5.3 = " << c.addNumbers2(3.2,5.3) << endl; - cout << "Compare: " << c.isEqual("Peter", "peter") << endl; - cout << "Build object: " << c.buildObject("Peter", 1990) << endl; - - } - catch (JsonRpcException& e) - { - cerr << e.what() << endl; - } + } catch (JsonRpcException &e) { + cerr << e.what() << endl; + } } - diff --git a/src/examples/stubserver.cpp b/src/examples/stubserver.cpp index bb1a567a..fde057dc 100644 --- a/src/examples/stubserver.cpp +++ b/src/examples/stubserver.cpp @@ -15,73 +15,55 @@ using namespace jsonrpc; using namespace std; -class MyStubServer : public AbstractStubServer -{ - public: - MyStubServer(AbstractServerConnector &connector, serverVersion_t type); - - virtual void notifyServer(); - virtual std::string sayHello(const std::string& name); - virtual int addNumbers(int param1, int param2); - virtual double addNumbers2(double param1, double param2); - virtual bool isEqual(const std::string& str1, const std::string &str2); - virtual Json::Value buildObject(const std::string &name, int age); - virtual std::string methodWithoutParameters(); - +class MyStubServer : public AbstractStubServer { +public: + MyStubServer(AbstractServerConnector &connector, serverVersion_t type); + + virtual void notifyServer(); + virtual std::string sayHello(const std::string &name); + virtual int addNumbers(int param1, int param2); + virtual double addNumbers2(double param1, double param2); + virtual bool isEqual(const std::string &str1, const std::string &str2); + virtual Json::Value buildObject(const std::string &name, int age); + virtual std::string methodWithoutParameters(); }; -MyStubServer::MyStubServer(AbstractServerConnector &connector, serverVersion_t type) : - AbstractStubServer(connector, type) -{ -} +MyStubServer::MyStubServer(AbstractServerConnector &connector, + serverVersion_t type) + : AbstractStubServer(connector, type) {} -void MyStubServer::notifyServer() -{ - cout << "Server got notified" << endl; -} +void MyStubServer::notifyServer() { cout << "Server got notified" << endl; } -string MyStubServer::sayHello(const string &name) -{ - return "Hello " + name; -} +string MyStubServer::sayHello(const string &name) { return "Hello " + name; } -int MyStubServer::addNumbers(int param1, int param2) -{ - return param1 + param2; -} +int MyStubServer::addNumbers(int param1, int param2) { return param1 + param2; } -double MyStubServer::addNumbers2(double param1, double param2) -{ - return param1 + param2; +double MyStubServer::addNumbers2(double param1, double param2) { + return param1 + param2; } -bool MyStubServer::isEqual(const string &str1, const string &str2) -{ - return str1 == str2; +bool MyStubServer::isEqual(const string &str1, const string &str2) { + return str1 == str2; } -Json::Value MyStubServer::buildObject(const string &name, int age) -{ - Json::Value result; - result["name"] = name; - result["year"] = age; - return result; +Json::Value MyStubServer::buildObject(const string &name, int age) { + Json::Value result; + result["name"] = name; + result["year"] = age; + return result; } -string MyStubServer::methodWithoutParameters() -{ - return "Test"; -} +string MyStubServer::methodWithoutParameters() { return "Test"; } -int main() -{ - HttpServer httpserver(8383); - MyStubServer s(httpserver, JSONRPC_SERVER_V1V2); //hybrid server (json-rpc 1.0 & 2.0) - s.StartListening(); - cout << "Hit enter to stop the server" << endl; - getchar(); +int main() { + HttpServer httpserver(8383); + MyStubServer s(httpserver, + JSONRPC_SERVER_V1V2); // hybrid server (json-rpc 1.0 & 2.0) + s.StartListening(); + cout << "Hit enter to stop the server" << endl; + getchar(); - s.StopListening(); + s.StopListening(); - return 0; + return 0; } diff --git a/src/examples/tcpsocketclient.cpp b/src/examples/tcpsocketclient.cpp index 98a4a3e0..85818e6c 100644 --- a/src/examples/tcpsocketclient.cpp +++ b/src/examples/tcpsocketclient.cpp @@ -5,47 +5,39 @@ * @brief tcpsocketclient.cpp */ +#include +#include #include #include -#include -#include using namespace jsonrpc; using namespace std; -int main(int argc, char** argv) -{ - string host; - unsigned int port; - - if(argc == 3) { - host = string(argv[1]); - port = atoi(argv[2]); - } - else { - host = "127.0.0.1"; - port = 6543; - } - - - cout << "Params are :" << endl; - cout << "\t host: " << host << endl; - cout << "\t port: " << port << endl; - - TcpSocketClient client(host, port); - Client c(client); - - Json::Value params; - params["name"] = "Peter"; - - try - { - cout << c.CallMethod("sayHello", params) << endl; - } - catch (JsonRpcException& e) - { - cerr << e.what() << endl; - } - - +int main(int argc, char **argv) { + string host; + unsigned int port; + + if (argc == 3) { + host = string(argv[1]); + port = atoi(argv[2]); + } else { + host = "127.0.0.1"; + port = 6543; + } + + cout << "Params are :" << endl; + cout << "\t host: " << host << endl; + cout << "\t port: " << port << endl; + + TcpSocketClient client(host, port); + Client c(client); + + Json::Value params; + params["name"] = "Peter"; + + try { + cout << c.CallMethod("sayHello", params) << endl; + } catch (JsonRpcException &e) { + cerr << e.what() << endl; + } } diff --git a/src/examples/tcpsocketserver.cpp b/src/examples/tcpsocketserver.cpp index 25d1d54c..70f0ce07 100644 --- a/src/examples/tcpsocketserver.cpp +++ b/src/examples/tcpsocketserver.cpp @@ -5,78 +5,66 @@ * @brief unixdomainsocketserver.cpp */ -#include -#include +#include #include #include #include -#include - +#include +#include using namespace jsonrpc; using namespace std; -class SampleServer : public AbstractServer -{ - public: - SampleServer(TcpSocketServer &server) : - AbstractServer(server) - { - this->bindAndAddMethod(Procedure("sayHello", PARAMS_BY_NAME, JSON_STRING, "name", JSON_STRING, NULL), &SampleServer::sayHello); - this->bindAndAddNotification(Procedure("notifyServer", PARAMS_BY_NAME, NULL), &SampleServer::notifyServer); - } +class SampleServer : public AbstractServer { +public: + SampleServer(TcpSocketServer &server) : AbstractServer(server) { + this->bindAndAddMethod(Procedure("sayHello", PARAMS_BY_NAME, JSON_STRING, + "name", JSON_STRING, NULL), + &SampleServer::sayHello); + this->bindAndAddNotification( + Procedure("notifyServer", PARAMS_BY_NAME, NULL), + &SampleServer::notifyServer); + } - //method - void sayHello(const Json::Value& request, Json::Value& response) - { - response = "Hello: " + request["name"].asString(); - } + // method + void sayHello(const Json::Value &request, Json::Value &response) { + response = "Hello: " + request["name"].asString(); + } - //notification - void notifyServer(const Json::Value& request) - { - (void)request; - cout << "server received some Notification" << endl; - } + // notification + void notifyServer(const Json::Value &request) { + (void)request; + cout << "server received some Notification" << endl; + } }; -int main(int argc, char** argv) -{ - try - { - string ip; - unsigned int port; - - if(argc == 3) - { - ip = string(argv[1]); - port = atoi(argv[2]); - } - else - { - ip = "127.0.0.1"; - port = 6543; - } - - cout << "Params are :" << endl; - cout << "\t ip: " << ip << endl; - cout << "\t port: " << port << endl; - - TcpSocketServer server(ip, port); - SampleServer serv(server); - if (serv.StartListening()) - { - cout << "Server started successfully" << endl; - getchar(); - serv.StopListening(); - } - else - { - cout << "Error starting Server" << endl; - } - } - catch (jsonrpc::JsonRpcException& e) - { - cerr << e.what() << endl; - } +int main(int argc, char **argv) { + try { + string ip; + unsigned int port; + + if (argc == 3) { + ip = string(argv[1]); + port = atoi(argv[2]); + } else { + ip = "127.0.0.1"; + port = 6543; + } + + cout << "Params are :" << endl; + cout << "\t ip: " << ip << endl; + cout << "\t port: " << port << endl; + + TcpSocketServer server(ip, port); + SampleServer serv(server); + if (serv.StartListening()) { + cout << "Server started successfully" << endl; + getchar(); + serv.StopListening(); + } else { + cout << "Error starting Server" << endl; + } + } catch (jsonrpc::JsonRpcException &e) { + cerr << e.what() << endl; + } } diff --git a/src/examples/unixdomainsocketclient.cpp b/src/examples/unixdomainsocketclient.cpp index 80ac0f40..14b7881e 100644 --- a/src/examples/unixdomainsocketclient.cpp +++ b/src/examples/unixdomainsocketclient.cpp @@ -5,29 +5,23 @@ * @brief unixdomainsocketclient.cpp */ +#include #include #include -#include using namespace jsonrpc; using namespace std; -int main() -{ - UnixDomainSocketClient client("/tmp/unixdomainsocketexample"); - Client c(client); - - Json::Value params; - params["name"] = "Peter"; - - try - { - cout << c.CallMethod("sayHello", params) << endl; - } - catch (JsonRpcException& e) - { - cerr << e.what() << endl; - } +int main() { + UnixDomainSocketClient client("./unixdomainsocketexample"); + Client c(client); + Json::Value params; + params["name"] = "Peter"; + try { + cout << c.CallMethod("sayHello", params) << endl; + } catch (JsonRpcException &e) { + cerr << e.what() << endl; + } } diff --git a/src/examples/unixdomainsocketserver.cpp b/src/examples/unixdomainsocketserver.cpp index 04d694ab..94a3cfe2 100644 --- a/src/examples/unixdomainsocketserver.cpp +++ b/src/examples/unixdomainsocketserver.cpp @@ -5,59 +5,51 @@ * @brief unixdomainsocketserver.cpp */ -#include -#include #include #include #include - +#include +#include using namespace jsonrpc; using namespace std; -class SampleServer : public AbstractServer -{ - public: - SampleServer(UnixDomainSocketServer &server) : - AbstractServer(server) - { - this->bindAndAddMethod(Procedure("sayHello", PARAMS_BY_NAME, JSON_STRING, "name", JSON_STRING, NULL), &SampleServer::sayHello); - this->bindAndAddNotification(Procedure("notifyServer", PARAMS_BY_NAME, NULL), &SampleServer::notifyServer); - } +class SampleServer : public AbstractServer { +public: + SampleServer(UnixDomainSocketServer &server) + : AbstractServer(server) { + this->bindAndAddMethod(Procedure("sayHello", PARAMS_BY_NAME, JSON_STRING, + "name", JSON_STRING, NULL), + &SampleServer::sayHello); + this->bindAndAddNotification( + Procedure("notifyServer", PARAMS_BY_NAME, NULL), + &SampleServer::notifyServer); + } - //method - void sayHello(const Json::Value& request, Json::Value& response) - { - response = "Hello: " + request["name"].asString(); - } + // method + void sayHello(const Json::Value &request, Json::Value &response) { + response = "Hello: " + request["name"].asString(); + } - //notification - void notifyServer(const Json::Value& request) - { - (void)request; - cout << "server received some Notification" << endl; - } + // notification + void notifyServer(const Json::Value &request) { + (void)request; + cout << "server received some Notification" << endl; + } }; -int main() -{ - try - { - UnixDomainSocketServer server("/tmp/unixdomainsocketexample"); - SampleServer serv(server); - if (serv.StartListening()) - { - cout << "Server started successfully" << endl; - getchar(); - serv.StopListening(); - } - else - { - cout << "Error starting Server" << endl; - } - } - catch (jsonrpc::JsonRpcException& e) - { - cerr << e.what() << endl; - } +int main() { + try { + UnixDomainSocketServer server("./unixdomainsocketexample"); + SampleServer serv(server); + if (serv.StartListening()) { + cout << "Server started successfully" << endl; + getchar(); + serv.StopListening(); + } else { + cout << "Error starting Server" << endl; + } + } catch (jsonrpc::JsonRpcException &e) { + cerr << e.what() << endl; + } } diff --git a/src/examples/xbmcremote.cpp b/src/examples/xbmcremote.cpp index 93c1ee5d..8a7df755 100644 --- a/src/examples/xbmcremote.cpp +++ b/src/examples/xbmcremote.cpp @@ -15,87 +15,92 @@ #include #include #ifndef WIN32 - #include +#include #else - #include +#include #endif -#include #include +#include #include using namespace jsonrpc; using namespace std; -//Taken from: http://stackoverflow.com/questions/2984307/c-key-pressed-in-linux-console +// Taken from: +// http://stackoverflow.com/questions/2984307/c-key-pressed-in-linux-console int kbhit() { - int ch; + int ch; #ifndef WIN32 - struct termios neu, alt; - int fd = fileno(stdin); - tcgetattr(fd, &alt); - neu = alt; - neu.c_lflag &= ~(ICANON|ECHO); - tcsetattr(fd, TCSANOW, &neu); - ch = getchar(); - tcsetattr(fd, TCSANOW, &alt); + struct termios neu, alt; + int fd = fileno(stdin); + tcgetattr(fd, &alt); + neu = alt; + neu.c_lflag &= ~(ICANON | ECHO); + tcsetattr(fd, TCSANOW, &neu); + ch = getchar(); + tcsetattr(fd, TCSANOW, &alt); #else - while (!_kbhit()) { - usleep(100000); - } - ch = _getch(); + while (!_kbhit()) { + usleep(100000); + } + ch = _getch(); #endif - return ch; + return ch; } +int main(int argc, char **argv) { -int main(int argc, char** argv) { - - if(argc < 2) - { - cerr << "Provide XBMC API URL as argument! e.g.: " << argv[0] << " http://127.0.0.1:8080/jsonrpc" << endl; - return -1; - } - else - { - cout << "XBMC Remote control" << endl; - cout << "\ta -> left" << endl; - cout << "\td -> right" << endl; - cout << "\tw -> up" << endl; - cout << "\td -> down" << endl; - cout << "\tEsc -> back" << endl; - cout << "\tEnter -> select" << endl; - cout << "\tx -> exit application" << endl; + if (argc < 2) { + cerr << "Provide XBMC API URL as argument! e.g.: " << argv[0] + << " http://127.0.0.1:8080/jsonrpc" << endl; + return -1; + } else { + cout << "XBMC Remote control" << endl; + cout << "\ta -> left" << endl; + cout << "\td -> right" << endl; + cout << "\tw -> up" << endl; + cout << "\td -> down" << endl; + cout << "\tEsc -> back" << endl; + cout << "\tEnter -> select" << endl; + cout << "\tx -> exit application" << endl; - try { - HttpClient httpclient(argv[1]); - XbmcRemoteClient stub(httpclient); - bool run = true; - while (run) { + try { + HttpClient httpclient(argv[1]); + XbmcRemoteClient stub(httpclient); + bool run = true; + while (run) { - int key = kbhit(); - switch(key) { - case 97: stub.Input_Left(); - break; - case 115: stub.Input_Down(); - break; - case 100: stub.Input_Right(); - break; - case 119: stub.Input_Up(); - break; - case 10: - case 13: stub.Input_Select(); - break; - case 127: - case 27: stub.Input_Back(); - break; - case 120: run = false; - break; - } - } - } catch(JsonRpcException& e) { - cerr << e.what() << endl; + int key = kbhit(); + switch (key) { + case 97: + stub.Input_Left(); + break; + case 115: + stub.Input_Down(); + break; + case 100: + stub.Input_Right(); + break; + case 119: + stub.Input_Up(); + break; + case 10: + case 13: + stub.Input_Select(); + break; + case 127: + case 27: + stub.Input_Back(); + break; + case 120: + run = false; + break; } + } + } catch (JsonRpcException &e) { + cerr << e.what() << endl; } - return 0; + } + return 0; } diff --git a/src/jsonrpccpp/CMakeLists.txt b/src/jsonrpccpp/CMakeLists.txt index f9ec86d4..f9a152a4 100644 --- a/src/jsonrpccpp/CMakeLists.txt +++ b/src/jsonrpccpp/CMakeLists.txt @@ -8,18 +8,20 @@ file(GLOB jsonrpc_install_header_server server/requesthandlerfactory.h server/abstractserver.h server/abstractserverconnector.h + server/abstractthreadedserver.h server/iprocedureinvokationhandler.h server/iclientconnectionhandler.h + server/threadpool.h ) file(GLOB jsonrpc_header_server server/*.h) file(GLOB jsonrpc_source_server server/*.c*) # setup client headers and sources file(GLOB jsonrpc_install_header_client - client/batchcall.h - client/batchresponse.h - client/client.h - client/iclientconnector.h + client/batchcall.h + client/batchresponse.h + client/client.h + client/iclientconnector.h ) file(GLOB jsonrpc_header_client client/*.h) file(GLOB jsonrpc_source_client client/*.c*) @@ -45,63 +47,76 @@ if (HTTP_SERVER) list(APPEND server_connector_libs ${CMAKE_THREAD_LIBS_INIT} ${MHD_LIBRARIES}) endif() +# setup sources for redis connectors +if (REDIS_CLIENT) + list(APPEND client_connector_header "client/connectors/redisclient.h") + list(APPEND client_connector_source "client/connectors/redisclient.cpp") + list(APPEND client_connector_libs ${HIREDIS_LIBRARIES}) + include_directories(${HIREDIS_INCLUDE_DIRS}) +endif() + +if (REDIS_SERVER) + list(APPEND server_connector_header "server/connectors/redisserver.h") + list(APPEND server_connector_source "server/connectors/redisserver.cpp") + list(APPEND server_connector_libs ${CMAKE_THREAD_LIBS_INIT} ${HIREDIS_LIBRARIES}) +endif() + # setup sources for unix domain socket connectors if (UNIX_DOMAIN_SOCKET_SERVER) - list(APPEND server_connector_header "server/connectors/unixdomainsocketserver.h") - list(APPEND server_connector_source "server/connectors/unixdomainsocketserver.cpp") - list(APPEND server_connector_libs ${CMAKE_THREAD_LIBS_INIT}) + list(APPEND server_connector_header "server/connectors/unixdomainsocketserver.h") + list(APPEND server_connector_source "server/connectors/unixdomainsocketserver.cpp") + list(APPEND server_connector_libs ${CMAKE_THREAD_LIBS_INIT}) endif() if (UNIX_DOMAIN_SOCKET_CLIENT) - list(APPEND client_connector_header "client/connectors/unixdomainsocketclient.h") - list(APPEND client_connector_source "client/connectors/unixdomainsocketclient.cpp") + list(APPEND client_connector_header "client/connectors/unixdomainsocketclient.h") + list(APPEND client_connector_source "client/connectors/unixdomainsocketclient.cpp") endif() # setup sources for tcp socket connectors if (TCP_SOCKET_SERVER) - list(APPEND server_connector_header "server/connectors/tcpsocketserver.h") - list(APPEND server_connector_source "server/connectors/tcpsocketserver.cpp") - if (WIN32) - list(APPEND server_connector_header "server/connectors/windowstcpsocketserver.h") - list(APPEND server_connector_source "server/connectors/windowstcpsocketserver.cpp") - list(APPEND server_connector_libs ws2_32) - endif() - if(UNIX) - list(APPEND server_connector_header "server/connectors/linuxtcpsocketserver.h") - list(APPEND server_connector_source "server/connectors/linuxtcpsocketserver.cpp") - endif() - list(APPEND server_connector_libs ${CMAKE_THREAD_LIBS_INIT}) + list(APPEND server_connector_header "server/connectors/tcpsocketserver.h") + list(APPEND server_connector_source "server/connectors/tcpsocketserver.cpp") + if (WIN32) + list(APPEND server_connector_header "server/connectors/windowstcpsocketserver.h") + list(APPEND server_connector_source "server/connectors/windowstcpsocketserver.cpp") + list(APPEND server_connector_libs ws2_32) + endif() + if(UNIX) + list(APPEND server_connector_header "server/connectors/linuxtcpsocketserver.h") + list(APPEND server_connector_source "server/connectors/linuxtcpsocketserver.cpp") + endif() + list(APPEND server_connector_libs ${CMAKE_THREAD_LIBS_INIT}) endif() if (TCP_SOCKET_CLIENT) - list(APPEND client_connector_header "client/connectors/tcpsocketclient.h") - list(APPEND client_connector_source "client/connectors/tcpsocketclient.cpp") - if (WIN32) - list(APPEND client_connector_header "client/connectors/windowstcpsocketclient.h") - list(APPEND client_connector_source "client/connectors/windowstcpsocketclient.cpp") - list(APPEND client_connector_libs ws2_32) - endif() - if(UNIX) - list(APPEND client_connector_header "client/connectors/linuxtcpsocketclient.h") - list(APPEND client_connector_source "client/connectors/linuxtcpsocketclient.cpp") - endif() - list(APPEND client_connector_libs ${CMAKE_THREAD_LIBS_INIT}) + list(APPEND client_connector_header "client/connectors/tcpsocketclient.h") + list(APPEND client_connector_source "client/connectors/tcpsocketclient.cpp") + if (WIN32) + list(APPEND client_connector_header "client/connectors/windowstcpsocketclient.h") + list(APPEND client_connector_source "client/connectors/windowstcpsocketclient.cpp") + list(APPEND client_connector_libs ws2_32) + endif() + if(UNIX) + list(APPEND client_connector_header "client/connectors/linuxtcpsocketclient.h") + list(APPEND client_connector_source "client/connectors/linuxtcpsocketclient.cpp") + endif() + list(APPEND client_connector_libs ${CMAKE_THREAD_LIBS_INIT}) endif() if (FILE_DESCRIPTOR_SERVER) - list(APPEND server_connector_header "server/connectors/filedescriptorserver.h") - list(APPEND server_connector_source "server/connectors/filedescriptorserver.cpp") - list(APPEND server_connector_libs ${CMAKE_THREAD_LIBS_INIT}) + list(APPEND server_connector_header "server/connectors/filedescriptorserver.h") + list(APPEND server_connector_source "server/connectors/filedescriptorserver.cpp") + list(APPEND server_connector_libs ${CMAKE_THREAD_LIBS_INIT}) endif() if (FILE_DESCRIPTOR_CLIENT) - list(APPEND client_connector_header "client/connectors/filedescriptorclient.h") - list(APPEND client_connector_source "client/connectors/filedescriptorclient.cpp") - list(APPEND client_connector_libs ${CMAKE_THREAD_LIBS_INIT}) + list(APPEND client_connector_header "client/connectors/filedescriptorclient.h") + list(APPEND client_connector_source "client/connectors/filedescriptorclient.cpp") + list(APPEND client_connector_libs ${CMAKE_THREAD_LIBS_INIT}) endif() # configure a header file to pass some of the CMake settings to the source code -# TODO: move it to custom build step? file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/gen/jsonrpccpp/common") configure_file("${PROJECT_SOURCE_DIR}/src/jsonrpccpp/version.h.in" "${CMAKE_BINARY_DIR}/gen/jsonrpccpp/version.h") configure_file("${PROJECT_SOURCE_DIR}/src/jsonrpccpp/common/jsonparser.h.in" "${CMAKE_BINARY_DIR}/gen/jsonrpccpp/common/jsonparser.h") @@ -133,10 +148,10 @@ endif() # setup shared client library if (BUILD_SHARED_LIBS) - add_library(jsonrpcclient SHARED ${jsonrpc_source_client} ${jsonrpc_header} ${jsonrpc_header_client} ${client_connector_source}) - add_dependencies(jsonrpcclient jsonrpccommon) - target_link_libraries(jsonrpcclient jsonrpccommon ${client_connector_libs}) - set_target_properties(jsonrpcclient PROPERTIES OUTPUT_NAME jsonrpccpp-client) + add_library(jsonrpcclient SHARED ${jsonrpc_source_client} ${jsonrpc_header} ${jsonrpc_header_client} ${client_connector_source}) + add_dependencies(jsonrpcclient jsonrpccommon) + target_link_libraries(jsonrpcclient jsonrpccommon ${client_connector_libs}) + set_target_properties(jsonrpcclient PROPERTIES OUTPUT_NAME jsonrpccpp-client) endif() # setup static client library @@ -152,10 +167,10 @@ endif() # setup shared server library if (BUILD_SHARED_LIBS) - add_library(jsonrpcserver SHARED ${jsonrpc_source_server} ${jsonrpc_header} ${jsonrpc_header_server} ${server_connector_source}) - add_dependencies(jsonrpcserver jsonrpccommon) - target_link_libraries(jsonrpcserver jsonrpccommon ${server_connector_libs}) - set_target_properties(jsonrpcserver PROPERTIES OUTPUT_NAME jsonrpccpp-server) + add_library(jsonrpcserver SHARED ${jsonrpc_source_server} ${jsonrpc_header} ${jsonrpc_header_server} ${server_connector_source}) + add_dependencies(jsonrpcserver jsonrpccommon) + target_link_libraries(jsonrpcserver jsonrpccommon ${server_connector_libs}) + set_target_properties(jsonrpcserver PROPERTIES OUTPUT_NAME jsonrpccpp-server) endif() # setup static server library @@ -172,7 +187,7 @@ endif() set(ALL_LIBS) if (BUILD_SHARED_LIBS OR NOT BUILD_STATIC_LIBS) - list(APPEND ALL_LIBS jsonrpccommon jsonrpcclient jsonrpcserver) + list(APPEND ALL_LIBS jsonrpccommon jsonrpcclient jsonrpcserver) endif() if (BUILD_STATIC_LIBS OR MSVC) @@ -214,9 +229,9 @@ get_filename_component(FULL_PATH_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX} ABSOLUTE set(FULL_PATH_INCLUDEDIR "${FULL_PATH_INSTALL_PREFIX}/include") set(FULL_PATH_LIBDIR "${FULL_PATH_INSTALL_PREFIX}/lib/${CMAKE_LIBRARY_PATH}") -CONFIGURE_FILE(${PROJECT_SOURCE_DIR}/cmake/libjsonrpccpp-client.pc.cmake ${CMAKE_BINARY_DIR}/libjsonrpccpp-client.pc) -CONFIGURE_FILE(${PROJECT_SOURCE_DIR}/cmake/libjsonrpccpp-server.pc.cmake ${CMAKE_BINARY_DIR}/libjsonrpccpp-server.pc) -CONFIGURE_FILE(${PROJECT_SOURCE_DIR}/cmake/libjsonrpccpp-common.pc.cmake ${CMAKE_BINARY_DIR}/libjsonrpccpp-common.pc) +configure_file(${PROJECT_SOURCE_DIR}/cmake/libjsonrpccpp-client.pc.cmake ${CMAKE_BINARY_DIR}/libjsonrpccpp-client.pc) +configure_file(${PROJECT_SOURCE_DIR}/cmake/libjsonrpccpp-server.pc.cmake ${CMAKE_BINARY_DIR}/libjsonrpccpp-server.pc) +configure_file(${PROJECT_SOURCE_DIR}/cmake/libjsonrpccpp-common.pc.cmake ${CMAKE_BINARY_DIR}/libjsonrpccpp-common.pc) INSTALL(FILES "${CMAKE_BINARY_DIR}/libjsonrpccpp-server.pc" diff --git a/src/jsonrpccpp/client/batchcall.cpp b/src/jsonrpccpp/client/batchcall.cpp index 13212449..82635222 100644 --- a/src/jsonrpccpp/client/batchcall.cpp +++ b/src/jsonrpccpp/client/batchcall.cpp @@ -13,41 +13,32 @@ using namespace jsonrpc; using namespace std; +BatchCall::BatchCall() : id(1) {} -BatchCall::BatchCall() : - id(1) -{ -} - -int BatchCall::addCall (const string &methodname, const Json::Value ¶ms, bool isNotification) -{ - Json::Value call; - call[RpcProtocolClient::KEY_PROTOCOL_VERSION] = "2.0"; - call[RpcProtocolClient::KEY_PROCEDURE_NAME] = methodname; - call[RpcProtocolClient::KEY_PARAMETER] = params; - if(!isNotification) - { - call[RpcProtocolClient::KEY_ID] = this->id++; - } - result.append(call); +int BatchCall::addCall(const string &methodname, const Json::Value ¶ms, + bool isNotification) { + Json::Value call; + call[RpcProtocolClient::KEY_PROTOCOL_VERSION] = "2.0"; + call[RpcProtocolClient::KEY_PROCEDURE_NAME] = methodname; + call[RpcProtocolClient::KEY_PARAMETER] = params; + if (!isNotification) { + call[RpcProtocolClient::KEY_ID] = this->id++; + } + result.append(call); - if (isNotification) - return -1; - return call[RpcProtocolClient::KEY_ID].asInt(); + if (isNotification) + return -1; + return call[RpcProtocolClient::KEY_ID].asInt(); } -string BatchCall::toString (bool fast) const -{ - string result; - if (fast) - { - Json::FastWriter writer; - result = writer.write(this->result); - } - else - { - Json::StyledWriter writer; - result = writer.write(this->result); - } - return result; +string BatchCall::toString(bool fast) const { + string result; + if (fast) { + Json::FastWriter writer; + result = writer.write(this->result); + } else { + Json::StyledWriter writer; + result = writer.write(this->result); + } + return result; } diff --git a/src/jsonrpccpp/client/batchresponse.cpp b/src/jsonrpccpp/client/batchresponse.cpp index 7e22e75e..f68f01b7 100644 --- a/src/jsonrpccpp/client/batchresponse.cpp +++ b/src/jsonrpccpp/client/batchresponse.cpp @@ -13,67 +13,49 @@ using namespace jsonrpc; using namespace std; +BatchResponse::BatchResponse() {} -BatchResponse::BatchResponse() -{ +void BatchResponse::addResponse(Json::Value &id, Json::Value response, + bool isError) { + if (isError) { + errorResponses.push_back(id); + } + responses[id] = response; } -void BatchResponse::addResponse(Json::Value &id, Json::Value response, bool isError) -{ - if (isError) { - errorResponses.push_back(id); - } - responses[id] = response; +Json::Value BatchResponse::getResult(int id) { + Json::Value result; + Json::Value i = id; + getResult(i, result); + return result; } -Json::Value BatchResponse::getResult(Json::Value& id) -{ - Json::Value result; - getResult(id, result); - return result; +void BatchResponse::getResult(Json::Value &id, Json::Value &result) { + if (getErrorCode(id) == 0) + result = responses[id]; + else + result = Json::nullValue; } -Json::Value BatchResponse::getResult(int id) -{ - Json::Value result; - Json::Value i = id; - getResult(i, result); - return result; +int BatchResponse::getErrorCode(Json::Value &id) { + if (std::find(errorResponses.begin(), errorResponses.end(), id) != + errorResponses.end()) { + return responses[id]["code"].asInt(); + } + return 0; } -void BatchResponse::getResult(Json::Value& id, Json::Value &result) -{ - if (getErrorCode(id) == 0) - result = responses[id]; - else - result = Json::nullValue; +string BatchResponse::getErrorMessage(Json::Value &id) { + if (std::find(errorResponses.begin(), errorResponses.end(), id) != + errorResponses.end()) { + return responses[id]["message"].asString(); + } + return ""; } -int BatchResponse::getErrorCode(Json::Value& id) -{ - if(std::find(errorResponses.begin(), errorResponses.end(), id) != errorResponses.end()) - { - return responses[id]["code"].asInt(); - } - return 0; +string BatchResponse::getErrorMessage(int id) { + Json::Value i = id; + return getErrorMessage(i); } -string BatchResponse::getErrorMessage(Json::Value& id) -{ - if(std::find(errorResponses.begin(), errorResponses.end(), id) != errorResponses.end()) - { - return responses[id]["message"].asString(); - } - return ""; -} - -string BatchResponse::getErrorMessage(int id) -{ - Json::Value i = id; - return getErrorMessage(i); -} - -bool BatchResponse::hasErrors() -{ - return !errorResponses.empty(); -} +bool BatchResponse::hasErrors() { return !errorResponses.empty(); } diff --git a/src/jsonrpccpp/client/batchresponse.h b/src/jsonrpccpp/client/batchresponse.h index 1f14bb4f..928218c1 100644 --- a/src/jsonrpccpp/client/batchresponse.h +++ b/src/jsonrpccpp/client/batchresponse.h @@ -37,8 +37,6 @@ namespace jsonrpc { * @param id * @return */ - Json::Value getResult(Json::Value& id); - Json::Value getResult(int id); diff --git a/src/jsonrpccpp/client/client.cpp b/src/jsonrpccpp/client/client.cpp index 8345979c..59770cdc 100644 --- a/src/jsonrpccpp/client/client.cpp +++ b/src/jsonrpccpp/client/client.cpp @@ -12,75 +12,68 @@ using namespace jsonrpc; -Client::Client(IClientConnector &connector, clientVersion_t version) : - connector(connector) -{ - this->protocol = new RpcProtocolClient(version); +Client::Client(IClientConnector &connector, clientVersion_t version) + : connector(connector) { + this->protocol = new RpcProtocolClient(version); } -Client::~Client() -{ - delete this->protocol; -} +Client::~Client() { delete this->protocol; } -void Client::CallMethod(const std::string &name, const Json::Value ¶meter, Json::Value& result) throw(JsonRpcException) -{ - std::string request, response; - protocol->BuildRequest(name, parameter, request, false); - connector.SendRPCMessage(request, response); - protocol->HandleResponse(response, result); +void Client::CallMethod(const std::string &name, const Json::Value ¶meter, + Json::Value &result) { + std::string request, response; + protocol->BuildRequest(name, parameter, request, false); + connector.SendRPCMessage(request, response); + protocol->HandleResponse(response, result); } -void Client::CallProcedures(const BatchCall &calls, BatchResponse &result) throw(JsonRpcException) -{ - std::string request, response; - request = calls.toString(); - connector.SendRPCMessage(request, response); - Json::Reader reader; - Json::Value tmpresult; +void Client::CallProcedures(const BatchCall &calls, BatchResponse &result) { + std::string request, response; + request = calls.toString(); + connector.SendRPCMessage(request, response); + Json::Reader reader; + Json::Value tmpresult; - if (!reader.parse(response, tmpresult) || !tmpresult.isArray()) - { - throw JsonRpcException(Errors::ERROR_CLIENT_INVALID_RESPONSE, "Array expected."); - } + if (!reader.parse(response, tmpresult) || !tmpresult.isArray()) { + throw JsonRpcException(Errors::ERROR_CLIENT_INVALID_RESPONSE, + "Array expected."); + } - for (unsigned int i=0; i < tmpresult.size(); i++) - { - if (tmpresult[i].isObject()) { - Json::Value singleResult; - try { - Json::Value id = this->protocol->HandleResponse(tmpresult[i], singleResult); - result.addResponse(id, singleResult, false); - } - catch (JsonRpcException& ex) { - Json::Value id = -1; - if(tmpresult[i].isMember("id")) - id = tmpresult[i]["id"]; - result.addResponse(id, tmpresult[i]["error"], true); - } - } - else - throw JsonRpcException(Errors::ERROR_CLIENT_INVALID_RESPONSE, "Object in Array expected."); - } + for (unsigned int i = 0; i < tmpresult.size(); i++) { + if (tmpresult[i].isObject()) { + Json::Value singleResult; + try { + Json::Value id = + this->protocol->HandleResponse(tmpresult[i], singleResult); + result.addResponse(id, singleResult, false); + } catch (JsonRpcException &ex) { + Json::Value id = -1; + if (tmpresult[i].isMember("id")) + id = tmpresult[i]["id"]; + result.addResponse(id, tmpresult[i]["error"], true); + } + } else + throw JsonRpcException(Errors::ERROR_CLIENT_INVALID_RESPONSE, + "Object in Array expected."); + } } -BatchResponse Client::CallProcedures(const BatchCall &calls) throw(JsonRpcException) -{ - BatchResponse result; - this->CallProcedures(calls, result); - return result; +BatchResponse Client::CallProcedures(const BatchCall &calls) { + BatchResponse result; + this->CallProcedures(calls, result); + return result; } -Json::Value Client::CallMethod(const std::string& name, const Json::Value& parameter) throw(JsonRpcException) -{ - Json::Value result; - this->CallMethod(name, parameter, result); - return result; +Json::Value Client::CallMethod(const std::string &name, + const Json::Value ¶meter) { + Json::Value result; + this->CallMethod(name, parameter, result); + return result; } -void Client::CallNotification(const std::string& name, const Json::Value& parameter) throw(JsonRpcException) -{ - std::string request, response; - protocol->BuildRequest(name, parameter, request, true); - connector.SendRPCMessage(request, response); +void Client::CallNotification(const std::string &name, + const Json::Value ¶meter) { + std::string request, response; + protocol->BuildRequest(name, parameter, request, true); + connector.SendRPCMessage(request, response); } diff --git a/src/jsonrpccpp/client/client.h b/src/jsonrpccpp/client/client.h index 7aefe6c5..15ad8e15 100644 --- a/src/jsonrpccpp/client/client.h +++ b/src/jsonrpccpp/client/client.h @@ -30,13 +30,13 @@ namespace jsonrpc Client(IClientConnector &connector, clientVersion_t version = JSONRPC_CLIENT_V2); virtual ~Client(); - void CallMethod (const std::string &name, const Json::Value ¶meter, Json::Value& result) throw (JsonRpcException); - Json::Value CallMethod (const std::string &name, const Json::Value ¶meter) throw (JsonRpcException); + void CallMethod (const std::string &name, const Json::Value ¶meter, Json::Value& result) ; + Json::Value CallMethod (const std::string &name, const Json::Value ¶meter) ; - void CallProcedures (const BatchCall &calls, BatchResponse &response) throw (JsonRpcException); - BatchResponse CallProcedures (const BatchCall &calls) throw (JsonRpcException); + void CallProcedures (const BatchCall &calls, BatchResponse &response) ; + BatchResponse CallProcedures (const BatchCall &calls) ; - void CallNotification (const std::string& name, const Json::Value& parameter) throw (JsonRpcException); + void CallNotification (const std::string& name, const Json::Value& parameter) ; private: IClientConnector &connector; diff --git a/src/jsonrpccpp/client/connectors/filedescriptorclient.cpp b/src/jsonrpccpp/client/connectors/filedescriptorclient.cpp index 77aac7ea..75cdf0b4 100644 --- a/src/jsonrpccpp/client/connectors/filedescriptorclient.cpp +++ b/src/jsonrpccpp/client/connectors/filedescriptorclient.cpp @@ -8,72 +8,57 @@ ************************************************************************/ #include "filedescriptorclient.h" -#include +#include "../../common/sharedconstants.h" +#include "../../common/streamreader.h" +#include "../../common/streamwriter.h" +#include +#include +#include #include -#include #include +#include #include -#include -#include -#include +#include -#define BUFFER_SIZE 64 -#define MAX_WRITE_SIZE (long unsigned int) 1024 -#ifndef DELIMITER_CHAR -#define DELIMITER_CHAR char(0x0A) -#endif //DELIMITER_CHAR #define MEX_ERR_MSG 255 using namespace jsonrpc; using namespace std; -FileDescriptorClient::FileDescriptorClient(int inputfd, int outputfd) : - inputfd(inputfd), outputfd(outputfd) -{ -} +FileDescriptorClient::FileDescriptorClient(int inputfd, int outputfd) + : inputfd(inputfd), outputfd(outputfd) {} -FileDescriptorClient::~FileDescriptorClient() -{ -} +FileDescriptorClient::~FileDescriptorClient() {} -void FileDescriptorClient::SendRPCMessage(const std::string& message, - std::string& result) throw (JsonRpcException) -{ - bool fullyWritten = false; - string toSend = message; - do - { - ssize_t byteWritten = write(outputfd, toSend.c_str(), min(toSend.size(), MAX_WRITE_SIZE)); - if (byteWritten < 1) - throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, - "Unknown error occured while writing to the output file descriptor"); +void FileDescriptorClient::SendRPCMessage(const std::string &message, + std::string &result) { + + string toSend = message + DEFAULT_DELIMITER_CHAR; + StreamWriter writer; - unsigned long len = toSend.size() - byteWritten; - toSend = toSend.substr(byteWritten, len); - if (toSend.size() == 0) - fullyWritten = true; - } while(!fullyWritten); + if (!writer.Write(toSend, outputfd)) { + throw JsonRpcException( + Errors::ERROR_CLIENT_CONNECTOR, + "Unknown error occured while writing to the output file descriptor"); + } if (!IsReadable(inputfd)) throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, - "The input file descriptor is not readable"); + "The input file descriptor is not readable"); - ssize_t nbytes = 0; - char buffer[BUFFER_SIZE]; - do - { - nbytes = read(inputfd, buffer, BUFFER_SIZE); - result.append(buffer, nbytes); - } while(result.find(DELIMITER_CHAR) == string::npos); + StreamReader reader(DEFAULT_BUFFER_SIZE); + if (!reader.Read(result, inputfd, DEFAULT_DELIMITER_CHAR)) { + throw JsonRpcException( + Errors::ERROR_CLIENT_CONNECTOR, + "Unknown error occured while reading from input file descriptor"); + } } -bool FileDescriptorClient::IsReadable(int fd) -{ +bool FileDescriptorClient::IsReadable(int fd) { int o_accmode = 0; int ret = fcntl(fd, F_GETFL, &o_accmode); if (ret == -1) return false; return ((o_accmode & O_ACCMODE) == O_RDONLY || - (o_accmode & O_ACCMODE) == O_RDWR); + (o_accmode & O_ACCMODE) == O_RDWR); } - diff --git a/src/jsonrpccpp/client/connectors/filedescriptorclient.h b/src/jsonrpccpp/client/connectors/filedescriptorclient.h index fe509042..22f43692 100644 --- a/src/jsonrpccpp/client/connectors/filedescriptorclient.h +++ b/src/jsonrpccpp/client/connectors/filedescriptorclient.h @@ -20,7 +20,7 @@ namespace jsonrpc public: FileDescriptorClient(int inputfd, int outputfd); virtual ~FileDescriptorClient(); - virtual void SendRPCMessage(const std::string& message, std::string& result) throw (JsonRpcException); + virtual void SendRPCMessage(const std::string& message, std::string& result) ; private: int inputfd; diff --git a/src/jsonrpccpp/client/connectors/httpclient.cpp b/src/jsonrpccpp/client/connectors/httpclient.cpp index 1d45eba2..099aef9b 100644 --- a/src/jsonrpccpp/client/connectors/httpclient.cpp +++ b/src/jsonrpccpp/client/connectors/httpclient.cpp @@ -8,127 +8,115 @@ ************************************************************************/ #include "httpclient.h" +#include #include -#include #include -#include +#include #include using namespace jsonrpc; class curl_initializer { - public: - curl_initializer() {curl_global_init(CURL_GLOBAL_ALL);} - ~curl_initializer() {curl_global_cleanup();} +public: + curl_initializer() { curl_global_init(CURL_GLOBAL_ALL); } + ~curl_initializer() { curl_global_cleanup(); } }; // See here: http://curl.haxx.se/libcurl/c/curl_global_init.html static curl_initializer _curl_init = curl_initializer(); /** - * taken from http://stackoverflow.com/questions/2329571/c-libcurl-get-output-into-a-string + * taken from + * http://stackoverflow.com/questions/2329571/c-libcurl-get-output-into-a-string */ -struct string -{ - char *ptr; - size_t len; +struct string { + char *ptr; + size_t len; }; static size_t writefunc(void *ptr, size_t size, size_t nmemb, - struct string *s) -{ - size_t new_len = s->len + size * nmemb; - s->ptr = (char*) realloc(s->ptr, new_len + 1); - memcpy(s->ptr + s->len, ptr, size * nmemb); - s->ptr[new_len] = '\0'; - s->len = new_len; - return size * nmemb; + struct string *s) { + size_t new_len = s->len + size * nmemb; + s->ptr = (char *)realloc(s->ptr, new_len + 1); + memcpy(s->ptr + s->len, ptr, size * nmemb); + s->ptr[new_len] = '\0'; + s->len = new_len; + return size * nmemb; } -void init_string(struct string *s) -{ - s->len = 0; - s->ptr = (char*) malloc(s->len + 1); - s->ptr[0] = '\0'; +void init_string(struct string *s) { + s->len = 0; + s->ptr = static_cast(malloc(s->len + 1)); + s->ptr[0] = '\0'; } -HttpClient::HttpClient(const std::string& url) throw(JsonRpcException) - : url(url) -{ - this->timeout = 10000; - curl = curl_easy_init(); +HttpClient::HttpClient(const std::string &url) : url(url) { + this->timeout = 10000; + curl = curl_easy_init(); } -HttpClient::~HttpClient() -{ - curl_easy_cleanup(curl); -} +HttpClient::~HttpClient() { curl_easy_cleanup(curl); } -void HttpClient::SendRPCMessage(const std::string& message, std::string& result) throw (JsonRpcException) -{ - curl_easy_setopt(curl, CURLOPT_URL, this->url.c_str()); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); +void HttpClient::SendRPCMessage(const std::string &message, + std::string &result) { + curl_easy_setopt(curl, CURLOPT_URL, this->url.c_str()); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); - CURLcode res; + CURLcode res; - struct string s; - init_string(&s); + struct string s; + init_string(&s); - struct curl_slist* headers = NULL; + struct curl_slist *headers = NULL; - for (std::map::iterator header = this->headers.begin(); header != this->headers.end(); ++header) { - headers = curl_slist_append(headers, (header->first + ": " + header->second).c_str()); - } + for (std::map::iterator header = + this->headers.begin(); + header != this->headers.end(); ++header) { + headers = curl_slist_append( + headers, (header->first + ": " + header->second).c_str()); + } - headers = curl_slist_append(headers, "Content-Type: application/json"); - headers = curl_slist_append(headers, "charsets: utf-8"); + headers = curl_slist_append(headers, "Content-Type: application/json"); + headers = curl_slist_append(headers, "charsets: utf-8"); - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, message.c_str()); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); - curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); - curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, timeout); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, message.c_str()); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, timeout); - res = curl_easy_perform(curl); + res = curl_easy_perform(curl); - result = s.ptr; - free(s.ptr); - curl_slist_free_all(headers); - if (res != CURLE_OK) - { - std::stringstream str; - str << "libcurl error: " << res; + result = s.ptr; + free(s.ptr); + curl_slist_free_all(headers); + if (res != CURLE_OK) { + std::stringstream str; + str << "libcurl error: " << res; - if (res == 7) - str << " -> Could not connect to " << this->url; - else if(res == 28) - str << " -> Operation timed out"; - throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, str.str()); - } + if (res == 7) + str << " -> Could not connect to " << this->url; + else if (res == 28) + str << " -> Operation timed out"; + throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, str.str()); + } - long http_code = 0; - curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code); + long http_code = 0; + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); - if (http_code != 200) - { - throw JsonRpcException(Errors::ERROR_RPC_INTERNAL_ERROR, result); - } + if (http_code != 200) { + throw JsonRpcException(Errors::ERROR_RPC_INTERNAL_ERROR, result); + } } -void HttpClient::SetUrl(const std::string& url) -{ - this->url = url; -} +void HttpClient::SetUrl(const std::string &url) { this->url = url; } -void HttpClient::SetTimeout(long timeout) -{ - this->timeout = timeout; -} +void HttpClient::SetTimeout(long timeout) { this->timeout = timeout; } -void HttpClient::AddHeader(const std::string& attr, const std::string& val) { - this->headers[attr] = val; +void HttpClient::AddHeader(const std::string &attr, const std::string &val) { + this->headers[attr] = val; } -void HttpClient::RemoveHeader(const std::string& attr) { - this->headers.erase(attr); +void HttpClient::RemoveHeader(const std::string &attr) { + this->headers.erase(attr); } diff --git a/src/jsonrpccpp/client/connectors/httpclient.h b/src/jsonrpccpp/client/connectors/httpclient.h index ee1e52cb..66859f13 100644 --- a/src/jsonrpccpp/client/connectors/httpclient.h +++ b/src/jsonrpccpp/client/connectors/httpclient.h @@ -11,35 +11,33 @@ #define JSONRPC_CPP_HTTPCLIENT_H_ #include "../iclientconnector.h" -#include #include +#include #include -namespace jsonrpc -{ - class HttpClient : public IClientConnector - { - public: - HttpClient(const std::string& url) throw (JsonRpcException); - virtual ~HttpClient(); - virtual void SendRPCMessage(const std::string& message, std::string& result) throw (JsonRpcException); - - void SetUrl(const std::string& url); - void SetTimeout(long timeout); - - void AddHeader(const std::string& attr, const std::string& val); - void RemoveHeader(const std::string& attr); - - private: - std::map headers; - std::string url; - - /** - * @brief timeout for http request in milliseconds - */ - long timeout; - CURL* curl; - }; +namespace jsonrpc { +class HttpClient : public IClientConnector { +public: + HttpClient(const std::string &url); + virtual ~HttpClient(); + virtual void SendRPCMessage(const std::string &message, std::string &result); + + void SetUrl(const std::string &url); + void SetTimeout(long timeout); + + void AddHeader(const std::string &attr, const std::string &val); + void RemoveHeader(const std::string &attr); + +private: + std::map headers; + std::string url; + + /** + * @brief timeout for http request in milliseconds + */ + long timeout; + CURL *curl; +}; } /* namespace jsonrpc */ #endif /* JSONRPC_CPP_HTTPCLIENT_H_ */ diff --git a/src/jsonrpccpp/client/connectors/linuxtcpsocketclient.cpp b/src/jsonrpccpp/client/connectors/linuxtcpsocketclient.cpp index 631998dd..23b0c317 100644 --- a/src/jsonrpccpp/client/connectors/linuxtcpsocketclient.cpp +++ b/src/jsonrpccpp/client/connectors/linuxtcpsocketclient.cpp @@ -2,236 +2,157 @@ * libjson-rpc-cpp ************************************************************************* * @file linuxtcpsocketclient.cpp - * @date 17.07.2015 + * @date 17.10.2016 * @author Alexandre Poirot * @license See attached LICENSE.txt ************************************************************************/ #include "linuxtcpsocketclient.h" -#include +#include "../../common/sharedconstants.h" +#include "../../common/streamreader.h" +#include "../../common/streamwriter.h" +#include #include #include -#include -#include -#include -#include -#include -#include -#include #include - -#define BUFFER_SIZE 64 -#ifndef DELIMITER_CHAR -#define DELIMITER_CHAR char(0x0A) -#endif //DELIMITER_CHAR +#include +#include +#include +#include +#include +#include +#include using namespace jsonrpc; using namespace std; -LinuxTcpSocketClient::LinuxTcpSocketClient(const std::string& hostToConnect, const unsigned int &port) : - TcpSocketClientPrivate(), - hostToConnect(hostToConnect), - port(port) -{ -} - -LinuxTcpSocketClient::~LinuxTcpSocketClient() -{ -} - -void LinuxTcpSocketClient::SendRPCMessage(const std::string& message, std::string& result) throw (JsonRpcException) -{ - int socket_fd = this->Connect(); - char buffer[BUFFER_SIZE]; - - bool fullyWritten = false; - string toSend = message; - do - { - ssize_t byteWritten = send(socket_fd, toSend.c_str(), toSend.size(), 0); - if(byteWritten == -1) - { - string message = "send() failed"; - int err = errno; - switch(err) - { - case EACCES: - case EWOULDBLOCK: - case EBADF: - case ECONNRESET: - case EDESTADDRREQ: - case EFAULT: - case EINTR: - case EINVAL: - case EISCONN: - case EMSGSIZE: - case ENOBUFS: - case ENOMEM: - case ENOTCONN: - case ENOTSOCK: - case EOPNOTSUPP: - case EPIPE: - message = strerror(err); - break; - } - close(socket_fd); - throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, message); - } - else if(static_cast(byteWritten) < toSend.size()) - { - int len = toSend.size() - byteWritten; - toSend = toSend.substr(byteWritten + sizeof(char), len); - } - else - fullyWritten = true; - } while(!fullyWritten); - - do - { - int nbytes = recv(socket_fd, buffer, BUFFER_SIZE, 0); - if(nbytes == -1) - { - string message = "recv() failed"; - int err = errno; - switch(err) - { - case EWOULDBLOCK: - case EBADF: - case ECONNRESET: - case EFAULT: - case EINTR: - case EINVAL: - case ENOMEM: - case ENOTCONN: - case ENOTSOCK: - message = strerror(err); - break; - } - close(socket_fd); - throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, message); - } - else - { - string tmp; - tmp.append(buffer, nbytes); - result.append(buffer,nbytes); - } - } while(result.find(DELIMITER_CHAR) == string::npos); - - close(socket_fd); +LinuxTcpSocketClient::LinuxTcpSocketClient(const std::string &hostToConnect, + const unsigned int &port) + : hostToConnect(hostToConnect), port(port) {} + +LinuxTcpSocketClient::~LinuxTcpSocketClient() {} + +void LinuxTcpSocketClient::SendRPCMessage(const std::string &message, + std::string &result) { + int socket_fd = this->Connect(); + + StreamWriter writer; + string toSend = message + DEFAULT_DELIMITER_CHAR; + if (!writer.Write(toSend, socket_fd)) { + throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, + "Could not write request"); + } + + StreamReader reader(DEFAULT_BUFFER_SIZE); + if (!reader.Read(result, socket_fd, DEFAULT_DELIMITER_CHAR)) { + throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, + "Could not read response"); + } + close(socket_fd); } -int LinuxTcpSocketClient::Connect() throw (JsonRpcException) -{ - if(this->IsIpv4Address(this->hostToConnect)) - { - return this->Connect(this->hostToConnect, this->port); - } - else //We were given a hostname - { - struct addrinfo *result = NULL; - struct addrinfo hints; - memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_family = AF_INET; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = IPPROTO_TCP; - char port[6]; - snprintf(port, 6, "%d", this->port); - int retval = getaddrinfo(this->hostToConnect.c_str(), port, &hints, &result); - if(retval != 0) - throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, "Could not resolve hostname."); - bool foundValidIp = false; - int socket_fd; - for(struct addrinfo *temp = result; (temp != NULL) && !foundValidIp; temp = temp->ai_next) - { - if(temp->ai_family == AF_INET) - { - try - { - sockaddr_in* sock = reinterpret_cast(temp->ai_addr); - socket_fd = this->Connect(inet_ntoa(sock->sin_addr), ntohs(sock->sin_port)); - foundValidIp = true; - } - catch(const JsonRpcException& e) - { - foundValidIp = false; - socket_fd = -1; - } - catch(void* p) - { - foundValidIp = false; - socket_fd = -1; - } - } - } - - if(!foundValidIp) - throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, "Hostname resolved but connection was refused on the given port."); - - return socket_fd; - } +int LinuxTcpSocketClient::Connect() { + if (this->IsIpv4Address(this->hostToConnect)) { + return this->Connect(this->hostToConnect, this->port); + } else // We were given a hostname + { + struct addrinfo *result = NULL; + struct addrinfo hints; + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + char port[6]; + snprintf(port, 6, "%d", this->port); + int retval = + getaddrinfo(this->hostToConnect.c_str(), port, &hints, &result); + if (retval != 0) + throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, + "Could not resolve hostname."); + bool foundValidIp = false; + int socket_fd; + for (struct addrinfo *temp = result; (temp != NULL) && !foundValidIp; + temp = temp->ai_next) { + if (temp->ai_family == AF_INET) { + try { + sockaddr_in *sock = reinterpret_cast(temp->ai_addr); + socket_fd = + this->Connect(inet_ntoa(sock->sin_addr), ntohs(sock->sin_port)); + foundValidIp = true; + } catch (const JsonRpcException &e) { + foundValidIp = false; + socket_fd = -1; + } catch (void *p) { + foundValidIp = false; + socket_fd = -1; + } + } + } + + if (!foundValidIp) + throw JsonRpcException( + Errors::ERROR_CLIENT_CONNECTOR, + "Hostname resolved but connection was refused on the given port."); + + return socket_fd; + } } -int LinuxTcpSocketClient::Connect(const string& ip, const int& port) throw (JsonRpcException) -{ - sockaddr_in address; - int socket_fd; - socket_fd = socket(AF_INET, SOCK_STREAM, 0); - if (socket_fd < 0) - { - string message = "socket() failed"; - int err = errno; - switch(err) - { - case EACCES: - case EAFNOSUPPORT: - case EINVAL: - case EMFILE: - case ENOBUFS: - case ENOMEM: - case EPROTONOSUPPORT: - message = strerror(err); - break; - } - throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, message); - } - memset(&address, 0, sizeof(sockaddr_in)); - - address.sin_family = AF_INET; - inet_aton(ip.c_str(), &(address.sin_addr)); - address.sin_port = htons(port); - - if(connect(socket_fd, (struct sockaddr *) &address, sizeof(sockaddr_in)) != 0) - { - string message = "connect() failed"; - int err = errno; - switch(err) - { - case EACCES: - case EPERM: - case EADDRINUSE: - case EAFNOSUPPORT: - case EAGAIN: - case EALREADY: - case EBADF: - case ECONNREFUSED: - case EFAULT: - case EINPROGRESS: - case EINTR: - case EISCONN: - case ENETUNREACH: - case ENOTSOCK: - case ETIMEDOUT: - message = strerror(err); - break; - } - throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, message); - } - return socket_fd; +int LinuxTcpSocketClient::Connect(const string &ip, const int &port) { + sockaddr_in address; + int socket_fd; + socket_fd = socket(AF_INET, SOCK_STREAM, 0); + if (socket_fd < 0) { + string message = "socket() failed"; + int err = errno; + switch (err) { + case EACCES: + case EAFNOSUPPORT: + case EINVAL: + case EMFILE: + case ENOBUFS: + case ENOMEM: + case EPROTONOSUPPORT: + message = strerror(err); + break; + } + throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, message); + } + memset(&address, 0, sizeof(sockaddr_in)); + + address.sin_family = AF_INET; + inet_aton(ip.c_str(), &(address.sin_addr)); + address.sin_port = htons(port); + + if (connect(socket_fd, (struct sockaddr *)&address, sizeof(sockaddr_in)) != + 0) { + string message = "connect() failed"; + int err = errno; + switch (err) { + case EACCES: + case EPERM: + case EADDRINUSE: + case EAFNOSUPPORT: + case EAGAIN: + case EALREADY: + case EBADF: + case ECONNREFUSED: + case EFAULT: + case EINPROGRESS: + case EINTR: + case EISCONN: + case ENETUNREACH: + case ENOTSOCK: + case ETIMEDOUT: + message = strerror(err); + break; + } + throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, message); + } + return socket_fd; } -bool LinuxTcpSocketClient::IsIpv4Address(const std::string& ip) -{ - struct in_addr addr; - return (inet_aton(ip.c_str(), &addr) != 0); +bool LinuxTcpSocketClient::IsIpv4Address(const std::string &ip) { + struct in_addr addr; + return (inet_aton(ip.c_str(), &addr) != 0); } diff --git a/src/jsonrpccpp/client/connectors/linuxtcpsocketclient.h b/src/jsonrpccpp/client/connectors/linuxtcpsocketclient.h index 5d05315f..cd337c28 100644 --- a/src/jsonrpccpp/client/connectors/linuxtcpsocketclient.h +++ b/src/jsonrpccpp/client/connectors/linuxtcpsocketclient.h @@ -2,7 +2,7 @@ * libjson-rpc-cpp ************************************************************************* * @file linuxtcpsocketclient.h - * @date 17.07.2015 + * @date 17.10.2016 * @author Alexandre Poirot * @license See attached LICENSE.txt ************************************************************************/ @@ -10,70 +10,68 @@ #ifndef JSONRPC_CPP_LINUXTCPSOCKETCLIENT_H_ #define JSONRPC_CPP_LINUXTCPSOCKETCLIENT_H_ -#include -#include -#include "tcpsocketclientprivate.h" +#include namespace jsonrpc { - /** - * This class is the Linux/UNIX implementation of TCPSocketClient. - * It uses the POSIX socket API to performs its job. - */ - class LinuxTcpSocketClient : public TcpSocketClientPrivate - { - public: - /** - * @brief LinuxTcpSocketClient, constructor of the Linux/UNIX implementation of class TcpSocketClient - * @param hostToConnect The hostname or the ipv4 address on which the client should try to connect - * @param port The port on which the client should try to connect - */ - LinuxTcpSocketClient(const std::string& hostToConnect, const unsigned int &port); - /** - * @brief ~LinuxTcpSocketClient, the destructor of LinuxTcpSocketClient - */ - virtual ~LinuxTcpSocketClient(); - /** - * @brief The real implementation of TcpSocketClient::SendRPCMessage method. - * @param message The message to send - * @param result The result of the call returned by the server - * @throw JsonRpcException Thrown when an issue is encountered with socket manipulation (see message of exception for more information about what happened). - */ - virtual void SendRPCMessage(const std::string& message, std::string& result) throw (JsonRpcException); + /** + * This class is the Linux/UNIX implementation of TCPSocketClient. + * It uses the POSIX socket API to performs its job. + */ + class LinuxTcpSocketClient : public IClientConnector + { + public: + /** + * @brief LinuxTcpSocketClient, constructor of the Linux/UNIX implementation of class TcpSocketClient + * @param hostToConnect The hostname or the ipv4 address on which the client should try to connect + * @param port The port on which the client should try to connect + */ + LinuxTcpSocketClient(const std::string& hostToConnect, const unsigned int &port); + /** + * @brief ~LinuxTcpSocketClient, the destructor of LinuxTcpSocketClient + */ + virtual ~LinuxTcpSocketClient(); + /** + * @brief The real implementation of TcpSocketClient::SendRPCMessage method. + * @param message The message to send + * @param result The result of the call returned by the server + * @throw JsonRpcException Thrown when an issue is encountered with socket manipulation (see message of exception for more information about what happened). + */ + virtual void SendRPCMessage(const std::string& message, std::string& result) ; - private: - std::string hostToConnect; /*!< The hostname or the ipv4 address on which the client should try to connect*/ - unsigned int port; /*!< The port on which the client should try to connect*/ - /** - * @brief Connects to the host and port provided by constructor parameters. - * - * This method detects if the hostToConnect attribute is either an IPv4 or a hostname. - * On first case it tries to connect to the ip. - * On second case it tries to resolve hostname to an ip and tries to connect to it if resolve was successful. - * - * @returns A file descriptor to the successfully connected socket - * @throw JsonRpcException Thrown when an issue is encountered while trying to connect (see message of exception for more information about what happened). - */ - int Connect() throw (JsonRpcException); - /** - * @brief Connects to provided ip and port. - * - * This method tries to connect to the provided ip and port. - * - * @param ip The ipv4 address to connect to - * @param port The port to connect to - * @returns A file descriptor to the successfully connected socket - * @throw JsonRpcException Thrown when an issue is encountered while trying to connect (see message of exception for more information about what happened). - */ - int Connect(const std::string& ip, const int& port) throw (JsonRpcException); - /** - * @brief Check if provided ip is an ipv4 address. - * - * @param ip The ipv4 address to check - * @returns A boolean indicating if the provided ip is or is not an ipv4 address - */ - bool IsIpv4Address(const std::string& ip); - }; + private: + std::string hostToConnect; /*!< The hostname or the ipv4 address on which the client should try to connect*/ + unsigned int port; /*!< The port on which the client should try to connect*/ + /** + * @brief Connects to the host and port provided by constructor parameters. + * + * This method detects if the hostToConnect attribute is either an IPv4 or a hostname. + * On first case it tries to connect to the ip. + * On second case it tries to resolve hostname to an ip and tries to connect to it if resolve was successful. + * + * @returns A file descriptor to the successfully connected socket + * @throw JsonRpcException Thrown when an issue is encountered while trying to connect (see message of exception for more information about what happened). + */ + int Connect() ; + /** + * @brief Connects to provided ip and port. + * + * This method tries to connect to the provided ip and port. + * + * @param ip The ipv4 address to connect to + * @param port The port to connect to + * @returns A file descriptor to the successfully connected socket + * @throw JsonRpcException Thrown when an issue is encountered while trying to connect (see message of exception for more information about what happened). + */ + int Connect(const std::string& ip, const int& port) ; + /** + * @brief Check if provided ip is an ipv4 address. + * + * @param ip The ipv4 address to check + * @returns A boolean indicating if the provided ip is or is not an ipv4 address + */ + bool IsIpv4Address(const std::string& ip); + }; } /* namespace jsonrpc */ #endif /* JSONRPC_CPP_LINUXTCPSOCKETCLIENT_H_ */ diff --git a/src/jsonrpccpp/client/connectors/redisclient.cpp b/src/jsonrpccpp/client/connectors/redisclient.cpp new file mode 100644 index 00000000..3745f7ea --- /dev/null +++ b/src/jsonrpccpp/client/connectors/redisclient.cpp @@ -0,0 +1,176 @@ +/************************************************************************* + * libjson-rpc-cpp + ************************************************************************* + * @file redisclient.cpp + * @date 12.08.2017 + * @author Jacques Software + * @license See attached LICENSE.txt + ************************************************************************/ + +#include "redisclient.h" + +#include +#include +#include +#include +#include + +using namespace jsonrpc; + +/** + * Generate a random string. + * @param s Buffer for the random string. + * @param len The length of the random string. + */ +void genRandom(char *s, int len) { + static const char alphanum[] = "0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz"; + + for (int i = 0; i < len; ++i) { + s[i] = alphanum[rand() % (sizeof(alphanum) - 1)]; + } + + s[len] = 0; +} + +/** + * Helper function for SendRpcMessage which processes the hiredis reply + * object and determines if it's valid and extracts the result. + * @param reply The reply object. + * @param result The result is returned here. + */ +void ProcessReply(redisReply *reply, std::string &result) { + + // The return from hiredis is strange in that it's always an array of + // length 2, with the first element the name of the key as a string, + // and the second element the actual element that we popped. + if (reply->type != REDIS_REPLY_ARRAY) { + throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, "Item not an array"); + } + + if (reply->elements != 2) { + throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, + "Item needs two elements"); + } + + // It's the second element that we care about + redisReply *data = reply->element[1]; + + // It should be a json string + if (data->type != REDIS_REPLY_STRING) { + throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, "Item not a string"); + } + + std::string json(data->str, data->str + data->len * sizeof data->str[0]); + result = json; +} + +/** + * Generates a unique random name for the return queue. + * @param con Our redis context. + * @param prefix Prefix for the queue name. + * @param ret_queue The name is returned here. + */ +void jsonrpc::GetReturnQueue(redisContext *con, const std::string &prefix, + std::string &ret_queue) { + char id[17]; + std::stringstream str; + genRandom(id, 16); + str << prefix << "_" << id; + ret_queue = str.str(); + + redisReply *reply = + (redisReply *)redisCommand(con, "EXISTS %s", ret_queue.c_str()); + if (reply == NULL) { + throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, + "redis error: Failed to run queue check"); + } + + if (reply->type != REDIS_REPLY_INTEGER) { + freeReplyObject(reply); + throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, + "redis error: Failed to run queue check"); + } + + if (reply->integer != 0) { + freeReplyObject(reply); + // If we are really unlucky and the queue already exists then we try again. + GetReturnQueue(con, prefix, ret_queue); + return; + } + freeReplyObject(reply); +} + +RedisClient::RedisClient(const std::string &host, int port, + const std::string &queue) + : queue(queue), con(NULL) { + this->timeout = 10; + + con = redisConnect(host.c_str(), port); + if (con == NULL) { + throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, + "redis error: Failed to connect"); + } + + if (con->err) { + std::stringstream err; + err << "redis error: " << con->err; + redisFree(con); + con = NULL; + throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, err.str()); + } + + struct timeval tv; + gettimeofday(&tv, NULL); + srand(time(NULL) + tv.tv_usec); +} + +RedisClient::~RedisClient() { + if (con != NULL) { + redisFree(con); + } +} + +void RedisClient::SendRPCMessage(const std::string &message, + std::string &result) { + std::string ret_queue; + GetReturnQueue(con, queue, ret_queue); + + redisReply *ret; + std::string data = ret_queue + "!" + message; + ret = (redisReply *)redisCommand(con, "LPUSH %s %s", queue.c_str(), + data.c_str()); + + if (ret == NULL) { + throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, + "Unknown error while sending request"); + } + if (ret->type != REDIS_REPLY_INTEGER || ret->integer <= 0) { + freeReplyObject(ret); + throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, + "Error while sending request, queue not updated"); + } + + freeReplyObject(ret); + + redisReply *reply = NULL; + reply = (redisReply *)redisCommand(con, "BRPOP %s %d", ret_queue.c_str(), + this->timeout); + if (reply == NULL) { + throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, + "Unknown error while getting response"); + } + if (reply->type == REDIS_REPLY_NIL) { + freeReplyObject(reply); + throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, + "Operation timed out"); + } + + ProcessReply(reply, result); + freeReplyObject(reply); +} + +void RedisClient::SetQueue(const std::string &queue) { this->queue = queue; } + +void RedisClient::SetTimeout(long timeout) { this->timeout = timeout; } diff --git a/src/jsonrpccpp/client/connectors/redisclient.h b/src/jsonrpccpp/client/connectors/redisclient.h new file mode 100644 index 00000000..620ae0aa --- /dev/null +++ b/src/jsonrpccpp/client/connectors/redisclient.h @@ -0,0 +1,92 @@ +/************************************************************************* + * libjson-rpc-cpp + ************************************************************************* + * @file redisclient.h + * @date 12.08.2017 + * @author Jacques Software + * @license See attached LICENSE.txt + ************************************************************************/ + +#ifndef JSONRPC_CPP_REDISCLIENT_H_ +#define JSONRPC_CPP_REDISCLIENT_H_ + +#include "../iclientconnector.h" +#include +#include + +namespace jsonrpc +{ + /** + * This class is the redis implementation of an AbstractClientConnector. + * It uses hiredis to connect to a designated redis server. + * + * The RedisClient sends the json string to the RedisServer by pushing + * it to the RedisServer's queue using LPUSH. The RedisServer will pop + * the item off the queue, handle it, then send its response back via + * another queue using LPUSH. The client will wait for this response for + * `timeout` seconds using BRPOP. + * + * The queue used to handle the responses must be unique, therefore the + * RedisClient generates a random queue name with the prefix of the + * RedisServer's queue, and ensures that it is unique using EXISTS. + * It then prepends the response/return queue name to the json string + * with an exlimation mark used as a seperator. + */ + class RedisClient : public IClientConnector + { + public: + + /** + * RedisClient + * @param host The ip address of the redis server. + * @param port The port of the redis server. + * @param queue The queue to send to. + */ + RedisClient(const std::string& host, int port, const std::string& queue); + virtual ~RedisClient(); + + + /** + * This method will send an rpc message to the RedisServer and return the result. + * @param message The message to send. + * @param result The returned message from the server. + */ + virtual void SendRPCMessage(const std::string& message, std::string& result); + + + /** + * Set the queue that we are messaging with. + * @param queue The queue to send to. + */ + void SetQueue(const std::string& queue); + + + /** + * Set how long we should wait for a response. + * @param timeout The length of time to wait in seconds + */ + void SetTimeout(long timeout); + + private: + + /** + * @brief Queue that we are messaging + */ + std::string queue; + + /** + * @brief Timeout for http request in milliseconds + */ + long timeout; + + /** + * @brief Our connection to the redis server + */ + redisContext * con; + }; + + // Exported here for unit testing purposes. + void GetReturnQueue(redisContext * con, const std::string& prefix, std::string& ret_queue); + +} /* namespace jsonrpc */ +#endif /* JSONRPC_CPP_REDISCLIENT_H_ */ diff --git a/src/jsonrpccpp/client/connectors/streamreader.cpp b/src/jsonrpccpp/client/connectors/streamreader.cpp new file mode 100644 index 00000000..3782579e --- /dev/null +++ b/src/jsonrpccpp/client/connectors/streamreader.cpp @@ -0,0 +1,3 @@ +#include "streamreader.h" + +StreamReader::StreamReader() {} diff --git a/src/jsonrpccpp/client/connectors/streamreader.h b/src/jsonrpccpp/client/connectors/streamreader.h new file mode 100644 index 00000000..f4d631be --- /dev/null +++ b/src/jsonrpccpp/client/connectors/streamreader.h @@ -0,0 +1,11 @@ +#ifndef STREAMREADER_H +#define STREAMREADER_H + + +class StreamReader +{ +public: + StreamReader(); +}; + +#endif // STREAMREADER_H \ No newline at end of file diff --git a/src/jsonrpccpp/client/connectors/tcpsocketclient.cpp b/src/jsonrpccpp/client/connectors/tcpsocketclient.cpp index 874e0faa..ccfe2dc7 100644 --- a/src/jsonrpccpp/client/connectors/tcpsocketclient.cpp +++ b/src/jsonrpccpp/client/connectors/tcpsocketclient.cpp @@ -11,37 +11,32 @@ #ifdef __WIN32__ #include "windowstcpsocketclient.h" -#elif __unix__ +#else #include "linuxtcpsocketclient.h" #endif using namespace jsonrpc; using namespace std; -TcpSocketClient::TcpSocketClient(const std::string& ipToConnect, const unsigned int &port) -{ +TcpSocketClient::TcpSocketClient(const std::string &ipToConnect, + const unsigned int &port) { #ifdef __WIN32__ - this->realSocket = new WindowsTcpSocketClient(ipToConnect, port); -#elif __unix__ - this->realSocket = new LinuxTcpSocketClient(ipToConnect, port); + this->realSocket = new WindowsTcpSocketClient(ipToConnect, port); #else - this->realSocket = NULL; + this->realSocket = new LinuxTcpSocketClient(ipToConnect, port); #endif } -TcpSocketClient::~TcpSocketClient() -{ - if(this->realSocket != NULL) - { - delete this->realSocket; - this->realSocket = NULL; - } +TcpSocketClient::~TcpSocketClient() { + if (this->realSocket != NULL) { + delete this->realSocket; + this->realSocket = NULL; + } } -void TcpSocketClient::SendRPCMessage(const std::string& message, std::string& result) throw (JsonRpcException) -{ - if(this->realSocket != NULL) - { - this->realSocket->SendRPCMessage(message, result); - } +void TcpSocketClient::SendRPCMessage(const std::string &message, + std::string &result) { + if (this->realSocket != NULL) { + this->realSocket->SendRPCMessage(message, result); + } } diff --git a/src/jsonrpccpp/client/connectors/tcpsocketclient.h b/src/jsonrpccpp/client/connectors/tcpsocketclient.h index 66e80054..9002e448 100644 --- a/src/jsonrpccpp/client/connectors/tcpsocketclient.h +++ b/src/jsonrpccpp/client/connectors/tcpsocketclient.h @@ -2,7 +2,7 @@ * libjson-rpc-cpp ************************************************************************* * @file unixdomainsocketclient.h - * @date 11.05.2015 + * @date 17.10.2016 * @author Alexandre Poirot * @license See attached LICENSE.txt ************************************************************************/ @@ -10,10 +10,9 @@ #ifndef JSONRPC_CPP_TCPSOCKETCLIENT_H_ #define JSONRPC_CPP_TCPSOCKETCLIENT_H_ -#include "../iclientconnector.h" +#include #include #include -#include "tcpsocketclientprivate.h" namespace jsonrpc { @@ -24,18 +23,18 @@ namespace jsonrpc * This class hides OS specific features in real implementation of this client. Currently it has implementation for * both Linux and Windows. */ - class TcpSocketClient : public IClientConnector - { - public: + class TcpSocketClient : public IClientConnector + { + public: /** * @brief TcpSocketClient, constructor for the included TcpSocketClient - * + * * Instanciates the real implementation of TcpSocketClientPrivate depending on running OS. - * + * * @param ipToConnect The ipv4 address on which the client should try to connect * @param port The port on which the client should try to connect */ - TcpSocketClient(const std::string& ipToConnect, const unsigned int &port); + TcpSocketClient(const std::string& ipToConnect, const unsigned int &port); /** * @brief ~TcpSocketClient, the destructor of TcpSocketClient */ @@ -46,11 +45,11 @@ namespace jsonrpc * @param result The result of the call returned by the servsr * @throw JsonRpcException Thrown when an issue is encounter with socket manipulation (see message of exception for more information about what happened). */ - virtual void SendRPCMessage(const std::string& message, std::string& result) throw (JsonRpcException); + virtual void SendRPCMessage(const std::string& message, std::string& result) ; - private: - TcpSocketClientPrivate *realSocket; /*!< A pointer to the real implementation of this class depending of running OS*/ - }; + private: + IClientConnector *realSocket; /*!< A pointer to the real implementation of this class depending of running OS*/ + }; } /* namespace jsonrpc */ #endif /* JSONRPC_CPP_TCPSOCKETCLIENT_H_ */ diff --git a/src/jsonrpccpp/client/connectors/tcpsocketclientprivate.h b/src/jsonrpccpp/client/connectors/tcpsocketclientprivate.h deleted file mode 100644 index 767eecfe..00000000 --- a/src/jsonrpccpp/client/connectors/tcpsocketclientprivate.h +++ /dev/null @@ -1,26 +0,0 @@ -/************************************************************************* - * libjson-rpc-cpp - ************************************************************************* - * @file tcpsocketclientprivate.h - * @date 17.07.2015 - * @author Alexandre Poirot - * @license See attached LICENSE.txt - ************************************************************************/ - -#ifndef JSONRPC_CPP_TCPSOCKETCLIENTPRIVATE_H_ -#define JSONRPC_CPP_TCPSOCKETCLIENTPRIVATE_H_ - -#include "../iclientconnector.h" -#include - -namespace jsonrpc -{ - /** - * This class is an interface to the real implementation of TcpSocketClient. Kind of a strategy design pattern. - */ - class TcpSocketClientPrivate : public IClientConnector - { - }; - -} /* namespace jsonrpc */ -#endif /* JSONRPC_CPP_TCPSOCKETCLIENTPRIVATE_H_ */ diff --git a/src/jsonrpccpp/client/connectors/unixdomainsocketclient.cpp b/src/jsonrpccpp/client/connectors/unixdomainsocketclient.cpp index 160ef1be..5a583ecb 100644 --- a/src/jsonrpccpp/client/connectors/unixdomainsocketclient.cpp +++ b/src/jsonrpccpp/client/connectors/unixdomainsocketclient.cpp @@ -8,77 +8,57 @@ ************************************************************************/ #include "unixdomainsocketclient.h" -#include -#include -#include +#include "../../common/sharedconstants.h" +#include "../../common/streamreader.h" +#include "../../common/streamwriter.h" #include -#include +#include +#include +#include +#include #include #include - -#include - -#define BUFFER_SIZE 64 -#define PATH_MAX 108 -#ifndef DELIMITER_CHAR -#define DELIMITER_CHAR char(0x0A) -#endif //DELIMITER_CHAR +#include using namespace jsonrpc; using namespace std; - UnixDomainSocketClient::UnixDomainSocketClient(const std::string& path) -: path(path) -{ -} - -UnixDomainSocketClient::~UnixDomainSocketClient() -{ -} - -void UnixDomainSocketClient::SendRPCMessage(const std::string& message, std::string& result) throw (JsonRpcException) -{ - sockaddr_un address; - int socket_fd, nbytes; - char buffer[BUFFER_SIZE]; - socket_fd = socket(AF_UNIX, SOCK_STREAM, 0); - if (socket_fd < 0) - { - throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, "Could not create unix domain socket"); - } - - memset(&address, 0, sizeof(sockaddr_un)); +UnixDomainSocketClient::UnixDomainSocketClient(const std::string &path) + : path(path) {} - address.sun_family = AF_UNIX; - snprintf(address.sun_path, PATH_MAX, "%s", this->path.c_str()); +UnixDomainSocketClient::~UnixDomainSocketClient() {} - if(connect(socket_fd, (struct sockaddr *) &address, sizeof(sockaddr_un)) != 0) - { - throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, "Could not connect to: " + this->path); - } +void UnixDomainSocketClient::SendRPCMessage(const std::string &message, + std::string &result) { + sockaddr_un address; + int socket_fd; + socket_fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (socket_fd < 0) { + throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, + "Could not create unix domain socket"); + } + memset(&address, 0, sizeof(sockaddr_un)); - bool fullyWritten = false; - string toSend = message; - do - { - ssize_t byteWritten = write(socket_fd, toSend.c_str(), toSend.size()); - if(static_cast(byteWritten) < toSend.size()) - { - int len = toSend.size() - byteWritten; - toSend = toSend.substr(byteWritten + sizeof(char), len); - } - else - fullyWritten = true; - } while(!fullyWritten); + address.sun_family = AF_UNIX; + strncpy(address.sun_path, this->path.c_str(), 107); - do - { - nbytes = read(socket_fd, buffer, BUFFER_SIZE); - string tmp; - tmp.append(buffer, nbytes); - result.append(buffer,nbytes); + if (connect(socket_fd, (struct sockaddr *)&address, sizeof(sockaddr_un)) != + 0) { + throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, + "Could not connect to: " + this->path); + } - } while(result.find(DELIMITER_CHAR) == string::npos); + StreamWriter writer; + string toSend = message + DEFAULT_DELIMITER_CHAR; + if (!writer.Write(toSend, socket_fd)) { + throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, + "Could not write request"); + } - close(socket_fd); + StreamReader reader(DEFAULT_BUFFER_SIZE); + if (!reader.Read(result, socket_fd, DEFAULT_DELIMITER_CHAR)) { + throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, + "Could not read response"); + } + close(socket_fd); } diff --git a/src/jsonrpccpp/client/connectors/unixdomainsocketclient.h b/src/jsonrpccpp/client/connectors/unixdomainsocketclient.h index 1906c296..2e2ccc09 100644 --- a/src/jsonrpccpp/client/connectors/unixdomainsocketclient.h +++ b/src/jsonrpccpp/client/connectors/unixdomainsocketclient.h @@ -15,16 +15,16 @@ namespace jsonrpc { - class UnixDomainSocketClient : public IClientConnector - { - public: - UnixDomainSocketClient(const std::string& path); - virtual ~UnixDomainSocketClient(); - virtual void SendRPCMessage(const std::string& message, std::string& result) throw (JsonRpcException); + class UnixDomainSocketClient : public IClientConnector + { + public: + UnixDomainSocketClient(const std::string& path); + virtual ~UnixDomainSocketClient(); + virtual void SendRPCMessage(const std::string& message, std::string& result) ; - private: - std::string path; - }; + private: + std::string path; + }; } /* namespace jsonrpc */ #endif /* JSONRPC_CPP_UNIXDOMAINSOCKETCLIENT_H_ */ diff --git a/src/jsonrpccpp/client/connectors/windowstcpsocketclient.cpp b/src/jsonrpccpp/client/connectors/windowstcpsocketclient.cpp index 4ebdf6f3..064861d0 100644 --- a/src/jsonrpccpp/client/connectors/windowstcpsocketclient.cpp +++ b/src/jsonrpccpp/client/connectors/windowstcpsocketclient.cpp @@ -8,9 +8,9 @@ ************************************************************************/ #include "windowstcpsocketclient.h" -#include #include #include +#include #undef _WIN32_WINNT #define _WIN32_WINNT 0x501 #include @@ -18,273 +18,246 @@ #define BUFFER_SIZE 64 #ifndef DELIMITER_CHAR #define DELIMITER_CHAR char(0x0A) -#endif //DELIMITER_CHAR +#endif // DELIMITER_CHAR using namespace jsonrpc; using namespace std; -WindowsTcpSocketClient::WindowsTcpSocketClient(const std::string& hostToConnect, const unsigned int &port) : - hostToConnect(hostToConnect), - port(port) -{ -} +WindowsTcpSocketClient::WindowsTcpSocketClient(const std::string &hostToConnect, + const unsigned int &port) + : hostToConnect(hostToConnect), port(port) {} -WindowsTcpSocketClient::~WindowsTcpSocketClient() -{ -} +WindowsTcpSocketClient::~WindowsTcpSocketClient() {} -void WindowsTcpSocketClient::SendRPCMessage(const std::string& message, std::string& result) throw (JsonRpcException) -{ - SOCKET socket_fd = this->Connect(); - char buffer[BUFFER_SIZE]; - bool fullyWritten = false; - string toSend = message; - do - { - int byteWritten = send(socket_fd, toSend.c_str(), toSend.size(), 0); - if(byteWritten == -1) - { - string message = "send() failed"; - int err = WSAGetLastError(); - switch(err) - { - case WSANOTINITIALISED: - case WSAENETDOWN: - case WSAEACCES: - case WSAEINTR: - case WSAEINPROGRESS: - case WSAEFAULT: - case WSAENETRESET: - case WSAENOBUFS: - case WSAENOTCONN: - case WSAENOTSOCK: - case WSAEOPNOTSUPP: - case WSAESHUTDOWN: - case WSAEWOULDBLOCK: - case WSAEMSGSIZE: - case WSAEHOSTUNREACH: - case WSAEINVAL: - case WSAECONNABORTED: - case WSAECONNRESET: - case WSAETIMEDOUT: - message = GetErrorMessage(err); - break; - } - closesocket(socket_fd); - throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, message); - } - else if(static_cast(byteWritten) < toSend.size()) - { - int len = toSend.size() - byteWritten; - toSend = toSend.substr(byteWritten + sizeof(char), len); - } - else - fullyWritten = true; - } while(!fullyWritten); +void WindowsTcpSocketClient::SendRPCMessage( + const std::string &message, std::string &result) throw(JsonRpcException) { + SOCKET socket_fd = this->Connect(); + char buffer[BUFFER_SIZE]; + bool fullyWritten = false; + string toSend = message; + do { + int byteWritten = send(socket_fd, toSend.c_str(), toSend.size(), 0); + if (byteWritten == -1) { + string message = "send() failed"; + int err = WSAGetLastError(); + switch (err) { + case WSANOTINITIALISED: + case WSAENETDOWN: + case WSAEACCES: + case WSAEINTR: + case WSAEINPROGRESS: + case WSAEFAULT: + case WSAENETRESET: + case WSAENOBUFS: + case WSAENOTCONN: + case WSAENOTSOCK: + case WSAEOPNOTSUPP: + case WSAESHUTDOWN: + case WSAEWOULDBLOCK: + case WSAEMSGSIZE: + case WSAEHOSTUNREACH: + case WSAEINVAL: + case WSAECONNABORTED: + case WSAECONNRESET: + case WSAETIMEDOUT: + message = GetErrorMessage(err); + break; + } + closesocket(socket_fd); + throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, message); + } else if (static_cast(byteWritten) < toSend.size()) { + int len = toSend.size() - byteWritten; + toSend = toSend.substr(byteWritten + sizeof(char), len); + } else + fullyWritten = true; + } while (!fullyWritten); - do - { - int nbytes = recv(socket_fd, buffer, BUFFER_SIZE, 0); - if(nbytes == -1) - { - string message = "recv() failed"; - int err = WSAGetLastError(); - switch(err) - { - case WSANOTINITIALISED: - case WSAENETDOWN: - case WSAEFAULT: - case WSAENOTCONN: - case WSAEINTR: - case WSAEINPROGRESS: - case WSAENETRESET: - case WSAENOTSOCK: - case WSAEOPNOTSUPP: - case WSAESHUTDOWN: - case WSAEWOULDBLOCK: - case WSAEMSGSIZE: - case WSAEINVAL: - case WSAECONNABORTED: - case WSAETIMEDOUT: - case WSAECONNRESET: - message = GetErrorMessage(err); - break; - } - closesocket(socket_fd); - throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, message); - } - else - { - string tmp; - tmp.append(buffer, nbytes); - result.append(buffer,nbytes); - } + do { + int nbytes = recv(socket_fd, buffer, BUFFER_SIZE, 0); + if (nbytes == -1) { + string message = "recv() failed"; + int err = WSAGetLastError(); + switch (err) { + case WSANOTINITIALISED: + case WSAENETDOWN: + case WSAEFAULT: + case WSAENOTCONN: + case WSAEINTR: + case WSAEINPROGRESS: + case WSAENETRESET: + case WSAENOTSOCK: + case WSAEOPNOTSUPP: + case WSAESHUTDOWN: + case WSAEWOULDBLOCK: + case WSAEMSGSIZE: + case WSAEINVAL: + case WSAECONNABORTED: + case WSAETIMEDOUT: + case WSAECONNRESET: + message = GetErrorMessage(err); + break; + } + closesocket(socket_fd); + throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, message); + } else { + string tmp; + tmp.append(buffer, nbytes); + result.append(buffer, nbytes); + } - } while(result.find(DELIMITER_CHAR) == string::npos); + } while (result.find(DELIMITER_CHAR) == string::npos); - closesocket(socket_fd); + closesocket(socket_fd); } -string WindowsTcpSocketClient::GetErrorMessage(const int& e) -{ - LPVOID lpMsgBuf; - lpMsgBuf = (LPVOID)"Unknown error"; - FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, e, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPTSTR)&lpMsgBuf, 0, NULL); - string message(static_cast(lpMsgBuf)); - LocalFree(lpMsgBuf); - return message; +string WindowsTcpSocketClient::GetErrorMessage(const int &e) { + LPVOID lpMsgBuf; + lpMsgBuf = (LPVOID) "Unknown error"; + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, e, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&lpMsgBuf, 0, NULL); + string message(static_cast(lpMsgBuf)); + LocalFree(lpMsgBuf); + return message; } -SOCKET WindowsTcpSocketClient::Connect() throw (JsonRpcException) -{ - if(this->IsIpv4Address(this->hostToConnect)) - { - return this->Connect(this->hostToConnect, this->port); - } - else //We were given a hostname - { - struct addrinfo *result = NULL; - struct addrinfo hints; - memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_family = AF_INET; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = IPPROTO_TCP; - char port[6]; - port = itoa(this->port, port, 10); - DWORD retval = getaddrinfo(this->hostToConnect.c_str(), port, &hints, &result); - if(retval != 0) - throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, "Could not resolve hostname."); +SOCKET WindowsTcpSocketClient::Connect() throw(JsonRpcException) { + if (this->IsIpv4Address(this->hostToConnect)) { + return this->Connect(this->hostToConnect, this->port); + } else // We were given a hostname + { + struct addrinfo *result = NULL; + struct addrinfo hints; + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + char port[6]; + port = itoa(this->port, port, 10); + DWORD retval = + getaddrinfo(this->hostToConnect.c_str(), port, &hints, &result); + if (retval != 0) + throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, + "Could not resolve hostname."); - bool foundValidIp = false; - SOCKET socket_fd = INVALID_SOCKET; - for(struct addrinfo *temp = result; (temp != NULL) && !foundValidIp; temp = temp->ai_next) - { - if(temp->ai_family == AF_INET) - { - try - { - SOCKADDR_IN* sock = reinterpret_cast(temp->ai_addr); - socket_fd = this->Connect(inet_ntoa(sock->sin_addr), ntohs(sock->sin_port)); - foundValidIp = true; - } - catch(const JsonRpcException& e) - { - foundValidIp = false; - socket_fd = INVALID_SOCKET; - } - catch(void* p) - { - foundValidIp = false; - socket_fd = INVALID_SOCKET; - } - } - } + bool foundValidIp = false; + SOCKET socket_fd = INVALID_SOCKET; + for (struct addrinfo *temp = result; (temp != NULL) && !foundValidIp; + temp = temp->ai_next) { + if (temp->ai_family == AF_INET) { + try { + SOCKADDR_IN *sock = reinterpret_cast(temp->ai_addr); + socket_fd = + this->Connect(inet_ntoa(sock->sin_addr), ntohs(sock->sin_port)); + foundValidIp = true; + } catch (const JsonRpcException &e) { + foundValidIp = false; + socket_fd = INVALID_SOCKET; + } catch (void *p) { + foundValidIp = false; + socket_fd = INVALID_SOCKET; + } + } + } - if(!foundValidIp) - throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, "Hostname resolved but connection was refused on the given port."); + if (!foundValidIp) + throw JsonRpcException( + Errors::ERROR_CLIENT_CONNECTOR, + "Hostname resolved but connection was refused on the given port."); - return socket_fd; - } + return socket_fd; + } } -SOCKET WindowsTcpSocketClient::Connect(const string& ip, const int& port) throw (JsonRpcException) -{ - SOCKADDR_IN address; - SOCKET socket_fd = socket(AF_INET, SOCK_STREAM, 0); - if (socket_fd == INVALID_SOCKET) - { - string message = "socket() failed"; - int err = WSAGetLastError(); - switch(err) - { - case WSANOTINITIALISED: - case WSAENETDOWN: - case WSAEAFNOSUPPORT: - case WSAEINPROGRESS: - case WSAEMFILE: - case WSAEINVAL: - case WSAEINVALIDPROVIDER: - case WSAEINVALIDPROCTABLE: - case WSAENOBUFS: - case WSAEPROTONOSUPPORT: - case WSAEPROTOTYPE: - case WSAEPROVIDERFAILEDINIT: - case WSAESOCKTNOSUPPORT: - message = GetErrorMessage(err); - break; - } - throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, message); - } - memset(&address, 0, sizeof(SOCKADDR_IN)); +SOCKET +WindowsTcpSocketClient::Connect(const string &ip, + const int &port) throw(JsonRpcException) { + SOCKADDR_IN address; + SOCKET socket_fd = socket(AF_INET, SOCK_STREAM, 0); + if (socket_fd == INVALID_SOCKET) { + string message = "socket() failed"; + int err = WSAGetLastError(); + switch (err) { + case WSANOTINITIALISED: + case WSAENETDOWN: + case WSAEAFNOSUPPORT: + case WSAEINPROGRESS: + case WSAEMFILE: + case WSAEINVAL: + case WSAEINVALIDPROVIDER: + case WSAEINVALIDPROCTABLE: + case WSAENOBUFS: + case WSAEPROTONOSUPPORT: + case WSAEPROTOTYPE: + case WSAEPROVIDERFAILEDINIT: + case WSAESOCKTNOSUPPORT: + message = GetErrorMessage(err); + break; + } + throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, message); + } + memset(&address, 0, sizeof(SOCKADDR_IN)); - address.sin_family = AF_INET; - address.sin_addr.s_addr = inet_addr(ip.c_str()); - address.sin_port = htons(port); - if(connect(socket_fd, reinterpret_cast(&address), sizeof(SOCKADDR_IN)) != 0) - { - string message = "connect() failed"; - int err = WSAGetLastError(); - switch(err) - { - case WSANOTINITIALISED: - case WSAENETDOWN: - case WSAEADDRINUSE: - case WSAEINTR: - case WSAEINPROGRESS: - case WSAEALREADY: - case WSAEADDRNOTAVAIL: - case WSAEAFNOSUPPORT: - case WSAECONNREFUSED: - case WSAEFAULT: - case WSAEINVAL: - case WSAEISCONN: - case WSAENETUNREACH: - case WSAEHOSTUNREACH: - case WSAENOBUFS: - case WSAENOTSOCK: - case WSAETIMEDOUT: - case WSAEWOULDBLOCK: - case WSAEACCES: - message = GetErrorMessage(err); - break; - } - throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, message); - } - return socket_fd; + address.sin_family = AF_INET; + address.sin_addr.s_addr = inet_addr(ip.c_str()); + address.sin_port = htons(port); + if (connect(socket_fd, reinterpret_cast(&address), + sizeof(SOCKADDR_IN)) != 0) { + string message = "connect() failed"; + int err = WSAGetLastError(); + switch (err) { + case WSANOTINITIALISED: + case WSAENETDOWN: + case WSAEADDRINUSE: + case WSAEINTR: + case WSAEINPROGRESS: + case WSAEALREADY: + case WSAEADDRNOTAVAIL: + case WSAEAFNOSUPPORT: + case WSAECONNREFUSED: + case WSAEFAULT: + case WSAEINVAL: + case WSAEISCONN: + case WSAENETUNREACH: + case WSAEHOSTUNREACH: + case WSAENOBUFS: + case WSAENOTSOCK: + case WSAETIMEDOUT: + case WSAEWOULDBLOCK: + case WSAEACCES: + message = GetErrorMessage(err); + break; + } + throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, message); + } + return socket_fd; } -bool WindowsTcpSocketClient::IsIpv4Address(const std::string& ip) -{ - return (inet_addr(ip.c_str()) != INADDR_NONE); +bool WindowsTcpSocketClient::IsIpv4Address(const std::string &ip) { + return (inet_addr(ip.c_str()) != INADDR_NONE); } -//This is inspired from SFML to manage Winsock initialization. Thanks to them! ( http://www.sfml-dev.org/ ). -struct ClientSocketInitializer -{ - ClientSocketInitializer() +// This is inspired from SFML to manage Winsock initialization. Thanks to them! +// ( http://www.sfml-dev.org/ ). +struct ClientSocketInitializer { + ClientSocketInitializer() - { - WSADATA init; - if(WSAStartup(MAKEWORD(2, 2), &init) != 0) - { - throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, "An issue occured while WSAStartup executed."); - } - } + { + WSADATA init; + if (WSAStartup(MAKEWORD(2, 2), &init) != 0) { + throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, + "An issue occured while WSAStartup executed."); + } + } - ~ClientSocketInitializer() + ~ClientSocketInitializer() - { - if(WSACleanup() != 0) - { - cerr << "An issue occured while WSAClean executed." << endl; - } - } + { + if (WSACleanup() != 0) { + cerr << "An issue occured while WSAClean executed." << endl; + } + } }; struct ClientSocketInitializer clientGlobalInitializer; diff --git a/src/jsonrpccpp/client/connectors/windowstcpsocketclient.h b/src/jsonrpccpp/client/connectors/windowstcpsocketclient.h index a1da2e96..2a7e0031 100644 --- a/src/jsonrpccpp/client/connectors/windowstcpsocketclient.h +++ b/src/jsonrpccpp/client/connectors/windowstcpsocketclient.h @@ -2,7 +2,7 @@ * libjson-rpc-cpp ************************************************************************* * @file windowstcpsocketclient.h - * @date 17.07.2015 + * @date 17.10.2016 * @author Alexandre Poirot * @license See attached LICENSE.txt ************************************************************************/ @@ -12,75 +12,75 @@ #include #include -#include "tcpsocketclientprivate.h" +#include #include namespace jsonrpc { - /** - * This class is the windows implementation of TCPSocketClient. - * It uses the Winsock2 API to performs its job. - */ - class WindowsTcpSocketClient : public TcpSocketClientPrivate - { - public: - /** - * @brief WindowsTcpSocketClient, constructor of the Windows implementation of class TcpSocketClient - * @param hostToConnect The hostname or the ipv4 address on which the client should try to connect - * @param port The port on which the client should try to connect - */ - WindowsTcpSocketClient(const std::string& hostToConnect, const unsigned int &port); - /** - * @brief ~WindowsTcpSocketClient, the destructor of WindowsTcpSocketClient - */ - virtual ~WindowsTcpSocketClient(); - /** - * @brief The real implementation of TcpSocketClient::SendRPCMessage method. - * @param message The message to send - * @param result The result of the call returned by the server - * @throw JsonRpcException Thrown when an issue is encounter with socket manipulation (see message of exception for more information about what happened). - */ - virtual void SendRPCMessage(const std::string& message, std::string& result) throw (JsonRpcException); + /** + * This class is the windows implementation of TCPSocketClient. + * It uses the Winsock2 API to performs its job. + */ + class WindowsTcpSocketClient : public IClientConnector + { + public: + /** + * @brief WindowsTcpSocketClient, constructor of the Windows implementation of class TcpSocketClient + * @param hostToConnect The hostname or the ipv4 address on which the client should try to connect + * @param port The port on which the client should try to connect + */ + WindowsTcpSocketClient(const std::string& hostToConnect, const unsigned int &port); + /** + * @brief ~WindowsTcpSocketClient, the destructor of WindowsTcpSocketClient + */ + virtual ~WindowsTcpSocketClient(); + /** + * @brief The real implementation of TcpSocketClient::SendRPCMessage method. + * @param message The message to send + * @param result The result of the call returned by the server + * @throw JsonRpcException Thrown when an issue is encounter with socket manipulation (see message of exception for more information about what happened). + */ + virtual void SendRPCMessage(const std::string& message, std::string& result) ; - private: - std::string hostToConnect; /*!< The hostname or the ipv4 address on which the client should try to connect*/ - unsigned int port; /*!< The port on which the client should try to connect*/ - /** - * @brief A method to produce human readable messages from Winsock2 error values. - * @param e A Winsock2 error value - * @return The message matching the error value - */ - static std::string GetErrorMessage(const int &e); - /** - * @brief Connects to the host and port provided by constructor parameters. - * - * This method detects if the hostToConnect attribute is either an IPv4 or a hostname. - * On first case it tries to connect to the ip. - * On second case it tries to resolve hostname to an ip and tries to connect to it if resolve was successful. - * - * @returns A file descriptor to the successfully connected socket - * @throw JsonRpcException Thrown when an issue is encountered while trying to connect (see message of exception for more information about what happened). - */ - SOCKET Connect() throw (JsonRpcException); - /** - * @brief Connects to provided ip and port. - * - * This method tries to connect to the provided ip and port. - * - * @param ip The ipv4 address to connect to - * @param port The port to connect to - * @returns A file descriptor to the successfully connected socket - * @throw JsonRpcException Thrown when an issue is encountered while trying to connect (see message of exception for more information about what happened). - */ - SOCKET Connect(const std::string& ip, const int& port) throw (JsonRpcException); - /** - * @brief Check if provided ip is an ipv4 address. - * - * @param ip The ipv4 address to check - * @returns A boolean indicating if the provided ip is or is not an ipv4 address - */ - bool IsIpv4Address(const std::string& ip); - }; + private: + std::string hostToConnect; /*!< The hostname or the ipv4 address on which the client should try to connect*/ + unsigned int port; /*!< The port on which the client should try to connect*/ + /** + * @brief A method to produce human readable messages from Winsock2 error values. + * @param e A Winsock2 error value + * @return The message matching the error value + */ + static std::string GetErrorMessage(const int &e); + /** + * @brief Connects to the host and port provided by constructor parameters. + * + * This method detects if the hostToConnect attribute is either an IPv4 or a hostname. + * On first case it tries to connect to the ip. + * On second case it tries to resolve hostname to an ip and tries to connect to it if resolve was successful. + * + * @returns A file descriptor to the successfully connected socket + * @throw JsonRpcException Thrown when an issue is encountered while trying to connect (see message of exception for more information about what happened). + */ + SOCKET Connect() ; + /** + * @brief Connects to provided ip and port. + * + * This method tries to connect to the provided ip and port. + * + * @param ip The ipv4 address to connect to + * @param port The port to connect to + * @returns A file descriptor to the successfully connected socket + * @throw JsonRpcException Thrown when an issue is encountered while trying to connect (see message of exception for more information about what happened). + */ + SOCKET Connect(const std::string& ip, const int& port) ; + /** + * @brief Check if provided ip is an ipv4 address. + * + * @param ip The ipv4 address to check + * @returns A boolean indicating if the provided ip is or is not an ipv4 address + */ + bool IsIpv4Address(const std::string& ip); + }; } /* namespace jsonrpc */ #endif /* JSONRPC_CPP_WINDOWSTCPSOCKETCLIENT_H_ */ diff --git a/src/jsonrpccpp/client/iclientconnector.h b/src/jsonrpccpp/client/iclientconnector.h index ff399f18..a95696ae 100644 --- a/src/jsonrpccpp/client/iclientconnector.h +++ b/src/jsonrpccpp/client/iclientconnector.h @@ -20,7 +20,7 @@ namespace jsonrpc public: virtual ~IClientConnector(){} - virtual void SendRPCMessage(const std::string& message, std::string& result) throw(JsonRpcException) = 0; + virtual void SendRPCMessage(const std::string& message, std::string& result) = 0; }; } /* namespace jsonrpc */ #endif /* JSONRPC_CPP_CLIENTCONNECTOR_H_ */ diff --git a/src/jsonrpccpp/client/rpcprotocolclient.cpp b/src/jsonrpccpp/client/rpcprotocolclient.cpp index 69a9ede0..d74d0663 100644 --- a/src/jsonrpccpp/client/rpcprotocolclient.cpp +++ b/src/jsonrpccpp/client/rpcprotocolclient.cpp @@ -13,129 +13,120 @@ using namespace jsonrpc; const std::string RpcProtocolClient::KEY_PROTOCOL_VERSION = "jsonrpc"; -const std::string RpcProtocolClient::KEY_PROCEDURE_NAME = "method"; -const std::string RpcProtocolClient::KEY_ID = "id"; -const std::string RpcProtocolClient::KEY_PARAMETER = "params"; -const std::string RpcProtocolClient::KEY_AUTH = "auth"; -const std::string RpcProtocolClient::KEY_RESULT = "result"; -const std::string RpcProtocolClient::KEY_ERROR = "error"; -const std::string RpcProtocolClient::KEY_ERROR_CODE = "code"; -const std::string RpcProtocolClient::KEY_ERROR_MESSAGE = "message"; -const std::string RpcProtocolClient::KEY_ERROR_DATA = "data"; +const std::string RpcProtocolClient::KEY_PROCEDURE_NAME = "method"; +const std::string RpcProtocolClient::KEY_ID = "id"; +const std::string RpcProtocolClient::KEY_PARAMETER = "params"; +const std::string RpcProtocolClient::KEY_AUTH = "auth"; +const std::string RpcProtocolClient::KEY_RESULT = "result"; +const std::string RpcProtocolClient::KEY_ERROR = "error"; +const std::string RpcProtocolClient::KEY_ERROR_CODE = "code"; +const std::string RpcProtocolClient::KEY_ERROR_MESSAGE = "message"; +const std::string RpcProtocolClient::KEY_ERROR_DATA = "data"; -RpcProtocolClient::RpcProtocolClient(clientVersion_t version) : - version(version) -{ -} +RpcProtocolClient::RpcProtocolClient(clientVersion_t version) + : version(version) {} -void RpcProtocolClient::BuildRequest(const std::string &method, const Json::Value ¶meter, std::string &result, bool isNotification) -{ - Json::Value request; - Json::FastWriter writer; - this->BuildRequest(1, method,parameter,request, isNotification); - result = writer.write(request); +void RpcProtocolClient::BuildRequest(const std::string &method, + const Json::Value ¶meter, + std::string &result, bool isNotification) { + Json::Value request; + Json::FastWriter writer; + this->BuildRequest(1, method, parameter, request, isNotification); + result = writer.write(request); } -void RpcProtocolClient::HandleResponse(const std::string &response, Json::Value& result) throw(JsonRpcException) -{ - Json::Reader reader; - Json::Value value; - if(reader.parse(response, value)) - { - this->HandleResponse(value, result); - } - else - { - throw JsonRpcException(Errors::ERROR_RPC_JSON_PARSE_ERROR, " " + response); - } +void RpcProtocolClient::HandleResponse(const std::string &response, + Json::Value &result) { + Json::Reader reader; + Json::Value value; + if (reader.parse(response, value)) { + this->HandleResponse(value, result); + } else { + throw JsonRpcException(Errors::ERROR_RPC_JSON_PARSE_ERROR, " " + response); + } } -Json::Value RpcProtocolClient::HandleResponse(const Json::Value &value, Json::Value &result) throw(JsonRpcException) -{ - if(this->ValidateResponse(value)) - { - if (this->HasError(value)) - { - this->throwErrorException(value); - } - else - { - result = value[KEY_RESULT]; - } +Json::Value RpcProtocolClient::HandleResponse(const Json::Value &value, + Json::Value &result) { + if (this->ValidateResponse(value)) { + if (this->HasError(value)) { + this->throwErrorException(value); + } else { + result = value[KEY_RESULT]; } - else - { - throw JsonRpcException(Errors::ERROR_CLIENT_INVALID_RESPONSE, " " + value.toStyledString()); - } - return value[KEY_ID]; + } else { + throw JsonRpcException(Errors::ERROR_CLIENT_INVALID_RESPONSE, + " " + value.toStyledString()); + } + return value[KEY_ID]; } -void RpcProtocolClient::BuildRequest(int id, const std::string &method, const Json::Value ¶meter, Json::Value &result, bool isNotification) -{ - if (this->version == JSONRPC_CLIENT_V2) - result[KEY_PROTOCOL_VERSION] = "2.0"; - result[KEY_PROCEDURE_NAME] = method; - if (parameter != Json::nullValue) - result[KEY_PARAMETER] = parameter; - if (!isNotification) - result[KEY_ID] = id; - else if (this->version == JSONRPC_CLIENT_V1) - result[KEY_ID] = Json::nullValue; +void RpcProtocolClient::BuildRequest(int id, const std::string &method, + const Json::Value ¶meter, + Json::Value &result, bool isNotification) { + if (this->version == JSONRPC_CLIENT_V2) + result[KEY_PROTOCOL_VERSION] = "2.0"; + result[KEY_PROCEDURE_NAME] = method; + if (parameter != Json::nullValue) + result[KEY_PARAMETER] = parameter; + if (!isNotification) + result[KEY_ID] = id; + else if (this->version == JSONRPC_CLIENT_V1) + result[KEY_ID] = Json::nullValue; } -void RpcProtocolClient::throwErrorException(const Json::Value &response) -{ - if (response[KEY_ERROR].isMember(KEY_ERROR_MESSAGE) && response[KEY_ERROR][KEY_ERROR_MESSAGE].isString()) - { - if (response[KEY_ERROR].isMember(KEY_ERROR_DATA)) - { - throw JsonRpcException(response[KEY_ERROR][KEY_ERROR_CODE].asInt(), response[KEY_ERROR][KEY_ERROR_MESSAGE].asString(), response[KEY_ERROR][KEY_ERROR_DATA]); - } - else - { - throw JsonRpcException(response[KEY_ERROR][KEY_ERROR_CODE].asInt(), response[KEY_ERROR][KEY_ERROR_MESSAGE].asString()); - } - } - else - { - throw JsonRpcException(response[KEY_ERROR][KEY_ERROR_CODE].asInt()); +void RpcProtocolClient::throwErrorException(const Json::Value &response) { + if (response[KEY_ERROR].isMember(KEY_ERROR_MESSAGE) && + response[KEY_ERROR][KEY_ERROR_MESSAGE].isString()) { + if (response[KEY_ERROR].isMember(KEY_ERROR_DATA)) { + throw JsonRpcException(response[KEY_ERROR][KEY_ERROR_CODE].asInt(), + response[KEY_ERROR][KEY_ERROR_MESSAGE].asString(), + response[KEY_ERROR][KEY_ERROR_DATA]); + } else { + throw JsonRpcException(response[KEY_ERROR][KEY_ERROR_CODE].asInt(), + response[KEY_ERROR][KEY_ERROR_MESSAGE].asString()); } + } else { + throw JsonRpcException(response[KEY_ERROR][KEY_ERROR_CODE].asInt()); + } } -bool RpcProtocolClient::ValidateResponse(const Json::Value& response) -{ - if (!response.isObject() || !response.isMember(KEY_ID)) - return false; +bool RpcProtocolClient::ValidateResponse(const Json::Value &response) { + if (!response.isObject() || !response.isMember(KEY_ID)) + return false; - if (this->version == JSONRPC_CLIENT_V1) - { - if (!response.isMember(KEY_RESULT) || !response.isMember(KEY_ERROR)) - return false; - if(!response[KEY_RESULT].isNull() && !response[KEY_ERROR].isNull()) - return false; - if (!response[KEY_ERROR].isNull() && !(response[KEY_ERROR].isObject() && response[KEY_ERROR].isMember(KEY_ERROR_CODE) && response[KEY_ERROR][KEY_ERROR_CODE].isIntegral())) - return false; - } - else if (this->version == JSONRPC_CLIENT_V2) - { - if (!response.isMember(KEY_PROTOCOL_VERSION) || response[KEY_PROTOCOL_VERSION] != "2.0") - return false; - if (response.isMember(KEY_RESULT) && response.isMember(KEY_ERROR)) - return false; - if (!response.isMember(KEY_RESULT) && !response.isMember(KEY_ERROR)) - return false; - if (response.isMember(KEY_ERROR) && !(response[KEY_ERROR].isObject() && response[KEY_ERROR].isMember(KEY_ERROR_CODE) && response[KEY_ERROR][KEY_ERROR_CODE].isIntegral())) - return false; - } + if (this->version == JSONRPC_CLIENT_V1) { + if (!response.isMember(KEY_RESULT) || !response.isMember(KEY_ERROR)) + return false; + if (!response[KEY_RESULT].isNull() && !response[KEY_ERROR].isNull()) + return false; + if (!response[KEY_ERROR].isNull() && + !(response[KEY_ERROR].isObject() && + response[KEY_ERROR].isMember(KEY_ERROR_CODE) && + response[KEY_ERROR][KEY_ERROR_CODE].isIntegral())) + return false; + } else if (this->version == JSONRPC_CLIENT_V2) { + if (!response.isMember(KEY_PROTOCOL_VERSION) || + response[KEY_PROTOCOL_VERSION] != "2.0") + return false; + if (response.isMember(KEY_RESULT) && response.isMember(KEY_ERROR)) + return false; + if (!response.isMember(KEY_RESULT) && !response.isMember(KEY_ERROR)) + return false; + if (response.isMember(KEY_ERROR) && + !(response[KEY_ERROR].isObject() && + response[KEY_ERROR].isMember(KEY_ERROR_CODE) && + response[KEY_ERROR][KEY_ERROR_CODE].isIntegral())) + return false; + } - return true; + return true; } -bool RpcProtocolClient::HasError(const Json::Value& response) -{ - if (this->version == JSONRPC_CLIENT_V1 && !response[KEY_ERROR].isNull()) - return true; - else if (this->version == JSONRPC_CLIENT_V2 && response.isMember(KEY_ERROR)) - return true; - return false; +bool RpcProtocolClient::HasError(const Json::Value &response) { + if (this->version == JSONRPC_CLIENT_V1 && !response[KEY_ERROR].isNull()) + return true; + else if (this->version == JSONRPC_CLIENT_V2 && response.isMember(KEY_ERROR)) + return true; + return false; } diff --git a/src/jsonrpccpp/client/rpcprotocolclient.h b/src/jsonrpccpp/client/rpcprotocolclient.h index 802b6a54..335b9aa3 100644 --- a/src/jsonrpccpp/client/rpcprotocolclient.h +++ b/src/jsonrpccpp/client/rpcprotocolclient.h @@ -50,7 +50,7 @@ namespace jsonrpc { * @brief Does the same as Json::Value RpcProtocolClient::HandleResponse(const std::string& response) throw(Exception) * but returns result as reference for performance speed up. */ - void HandleResponse(const std::string &response, Json::Value &result) throw (JsonRpcException); + void HandleResponse(const std::string &response, Json::Value &result) ; /** * @brief HandleResponse @@ -58,7 +58,7 @@ namespace jsonrpc { * @param result * @return response id */ - Json::Value HandleResponse(const Json::Value &response, Json::Value &result) throw (JsonRpcException); + Json::Value HandleResponse(const Json::Value &response, Json::Value &result) ; static const std::string KEY_PROTOCOL_VERSION; static const std::string KEY_PROCEDURE_NAME; diff --git a/src/jsonrpccpp/common/errors.cpp b/src/jsonrpccpp/common/errors.cpp index 127f02ff..550f00ab 100644 --- a/src/jsonrpccpp/common/errors.cpp +++ b/src/jsonrpccpp/common/errors.cpp @@ -15,54 +15,58 @@ using namespace jsonrpc; std::map Errors::possibleErrors; Errors::_init Errors::_initializer; -const int Errors::ERROR_RPC_JSON_PARSE_ERROR = -32700; -const int Errors::ERROR_RPC_METHOD_NOT_FOUND = -32601; -const int Errors::ERROR_RPC_INVALID_REQUEST = -32600; -const int Errors::ERROR_RPC_INVALID_PARAMS = -32602; -const int Errors::ERROR_RPC_INTERNAL_ERROR = -32603; +const int Errors::ERROR_RPC_JSON_PARSE_ERROR = -32700; +const int Errors::ERROR_RPC_METHOD_NOT_FOUND = -32601; +const int Errors::ERROR_RPC_INVALID_REQUEST = -32600; +const int Errors::ERROR_RPC_INVALID_PARAMS = -32602; +const int Errors::ERROR_RPC_INTERNAL_ERROR = -32603; -const int Errors::ERROR_SERVER_PROCEDURE_IS_METHOD = -32604; -const int Errors::ERROR_SERVER_PROCEDURE_IS_NOTIFICATION = -32605; -const int Errors::ERROR_SERVER_PROCEDURE_POINTER_IS_NULL = -32606; -const int Errors::ERROR_SERVER_PROCEDURE_SPECIFICATION_NOT_FOUND = -32000; -const int Errors::ERROR_SERVER_CONNECTOR = -32002; -const int Errors::ERROR_SERVER_PROCEDURE_SPECIFICATION_SYNTAX = -32007; +const int Errors::ERROR_SERVER_PROCEDURE_IS_METHOD = -32604; +const int Errors::ERROR_SERVER_PROCEDURE_IS_NOTIFICATION = -32605; +const int Errors::ERROR_SERVER_PROCEDURE_POINTER_IS_NULL = -32606; +const int Errors::ERROR_SERVER_PROCEDURE_SPECIFICATION_NOT_FOUND = -32000; +const int Errors::ERROR_SERVER_CONNECTOR = -32002; +const int Errors::ERROR_SERVER_PROCEDURE_SPECIFICATION_SYNTAX = -32007; -const int Errors::ERROR_CLIENT_CONNECTOR = -32003; -const int Errors::ERROR_CLIENT_INVALID_RESPONSE = -32001; +const int Errors::ERROR_CLIENT_CONNECTOR = -32003; +const int Errors::ERROR_CLIENT_INVALID_RESPONSE = -32001; Errors::_init::_init() { - //Official Errors - possibleErrors[ERROR_RPC_INVALID_REQUEST] = - "INVALID_JSON_REQUEST: The JSON sent is not a valid JSON-RPC Request object"; - possibleErrors[ERROR_RPC_METHOD_NOT_FOUND] = - "METHOD_NOT_FOUND: The method being requested is not available on this server"; - possibleErrors[ERROR_RPC_INVALID_PARAMS] = - "INVALID_PARAMS: Invalid method parameters (invalid name and/or type) recognised"; - possibleErrors[ERROR_RPC_JSON_PARSE_ERROR] = - "JSON_PARSE_ERROR: The JSON-Object is not JSON-Valid"; - possibleErrors[ERROR_RPC_INTERNAL_ERROR] = "INTERNAL_ERROR: "; + // Official Errors + possibleErrors[ERROR_RPC_INVALID_REQUEST] = "INVALID_JSON_REQUEST: The JSON " + "sent is not a valid JSON-RPC " + "Request object"; + possibleErrors[ERROR_RPC_METHOD_NOT_FOUND] = "METHOD_NOT_FOUND: The method " + "being requested is not " + "available on this server"; + possibleErrors[ERROR_RPC_INVALID_PARAMS] = "INVALID_PARAMS: Invalid method " + "parameters (invalid name and/or " + "type) recognised"; + possibleErrors[ERROR_RPC_JSON_PARSE_ERROR] = + "JSON_PARSE_ERROR: The JSON-Object is not JSON-Valid"; + possibleErrors[ERROR_RPC_INTERNAL_ERROR] = "INTERNAL_ERROR: "; - possibleErrors[ERROR_SERVER_PROCEDURE_IS_METHOD] = - "PROCEDURE_IS_METHOD: The requested notification is declared as a method"; - possibleErrors[ERROR_SERVER_PROCEDURE_IS_NOTIFICATION] = - "PROCEDURE_IS_NOTIFICATION: The requested method is declared as notification"; - possibleErrors[ERROR_SERVER_PROCEDURE_POINTER_IS_NULL] = - "PROCEDURE_POINTER_IS_NULL: Server has no function Reference registered"; - possibleErrors[ERROR_SERVER_PROCEDURE_SPECIFICATION_NOT_FOUND] = "Configuration file was not found"; + possibleErrors[ERROR_SERVER_PROCEDURE_IS_METHOD] = + "PROCEDURE_IS_METHOD: The requested notification is declared as a method"; + possibleErrors[ERROR_SERVER_PROCEDURE_IS_NOTIFICATION] = + "PROCEDURE_IS_NOTIFICATION: The requested method is declared as " + "notification"; + possibleErrors[ERROR_SERVER_PROCEDURE_POINTER_IS_NULL] = + "PROCEDURE_POINTER_IS_NULL: Server has no function Reference registered"; + possibleErrors[ERROR_SERVER_PROCEDURE_SPECIFICATION_NOT_FOUND] = + "Configuration file was not found"; - possibleErrors[ERROR_SERVER_PROCEDURE_SPECIFICATION_SYNTAX] = "Procedure specification mallformed"; + possibleErrors[ERROR_SERVER_PROCEDURE_SPECIFICATION_SYNTAX] = + "Procedure specification mallformed"; - possibleErrors[ERROR_CLIENT_INVALID_RESPONSE] = "The response is invalid"; - possibleErrors[ERROR_CLIENT_CONNECTOR] = "Client connector error"; - possibleErrors[ERROR_SERVER_CONNECTOR] = "Server connector error"; + possibleErrors[ERROR_CLIENT_INVALID_RESPONSE] = "The response is invalid"; + possibleErrors[ERROR_CLIENT_CONNECTOR] = "Client connector error"; + possibleErrors[ERROR_SERVER_CONNECTOR] = "Server connector error"; } -std::string Errors::GetErrorMessage(int errorCode) -{ - if(possibleErrors.find(errorCode) == possibleErrors.end()) - { - return ""; - } - return possibleErrors[errorCode]; +std::string Errors::GetErrorMessage(int errorCode) { + if (possibleErrors.find(errorCode) == possibleErrors.end()) { + return ""; + } + return possibleErrors[errorCode]; } diff --git a/src/jsonrpccpp/common/exception.cpp b/src/jsonrpccpp/common/exception.cpp index 98037f24..822d197e 100644 --- a/src/jsonrpccpp/common/exception.cpp +++ b/src/jsonrpccpp/common/exception.cpp @@ -11,77 +11,53 @@ using namespace jsonrpc; -JsonRpcException::JsonRpcException(int code) : - code(code), - message(Errors::GetErrorMessage(code)) -{ - this->setWhatMessage(); +JsonRpcException::JsonRpcException(int code) + : code(code), message(Errors::GetErrorMessage(code)) { + this->setWhatMessage(); } -JsonRpcException::JsonRpcException(int code, const std::string& message) : - code(code), - message(Errors::GetErrorMessage(code)) -{ - if (this->message != "") - this->message = this->message + ": "; - this->message = this->message + message; - this->setWhatMessage(); +JsonRpcException::JsonRpcException(int code, const std::string &message) + : code(code), message(Errors::GetErrorMessage(code)) { + if (this->message != "") + this->message = this->message + ": "; + this->message = this->message + message; + this->setWhatMessage(); } -JsonRpcException::JsonRpcException(int code, const std::string &message, const Json::Value &data) : - code(code), - message(Errors::GetErrorMessage(code)), - data(data) -{ - if (this->message != "") - this->message = this->message + ": "; - this->message = this->message + message; - this->setWhatMessage(); +JsonRpcException::JsonRpcException(int code, const std::string &message, + const Json::Value &data) + : code(code), message(Errors::GetErrorMessage(code)), data(data) { + if (this->message != "") + this->message = this->message + ": "; + this->message = this->message + message; + this->setWhatMessage(); } -JsonRpcException::JsonRpcException(const std::string& message) : - code(0), - message(message) -{ - this->setWhatMessage(); +JsonRpcException::JsonRpcException(const std::string &message) + : code(0), message(message) { + this->setWhatMessage(); } -JsonRpcException::~JsonRpcException() throw () -{ -} +JsonRpcException::~JsonRpcException() throw() {} -int JsonRpcException::GetCode() const -{ - return code; -} +int JsonRpcException::GetCode() const { return code; } -const std::string& JsonRpcException::GetMessage() const -{ - return message; -} +const std::string &JsonRpcException::GetMessage() const { return message; } -const Json::Value& JsonRpcException::GetData() const -{ - return data; -} +const Json::Value &JsonRpcException::GetData() const { return data; } -const char* JsonRpcException::what() const throw () -{ - return this->whatString.c_str(); +const char *JsonRpcException::what() const throw() { + return this->whatString.c_str(); } -void JsonRpcException::setWhatMessage() -{ - if (this->code != 0) - { - std::stringstream ss; - ss << "Exception " << this->code << " : " << this->message; - if (data != Json::nullValue) - ss << ", data: " << data.toStyledString(); - this->whatString = ss.str(); - } - else - { - this->whatString = this->message; - } +void JsonRpcException::setWhatMessage() { + if (this->code != 0) { + std::stringstream ss; + ss << "Exception " << this->code << " : " << this->message; + if (data != Json::nullValue) + ss << ", data: " << data.toStyledString(); + this->whatString = ss.str(); + } else { + this->whatString = this->message; + } } diff --git a/src/jsonrpccpp/common/jsonparser.h.in b/src/jsonrpccpp/common/jsonparser.h.in index 2d964289..3363ccfd 100644 --- a/src/jsonrpccpp/common/jsonparser.h.in +++ b/src/jsonrpccpp/common/jsonparser.h.in @@ -3,7 +3,7 @@ ************************************************************************* * @file jsonparser.h * @date 1/21/2015 - * @author Peter Spiess-Knafl + * @author Peter Spiess-Knafl * @license See attached LICENSE.txt ************************************************************************/ diff --git a/src/jsonrpccpp/common/procedure.cpp b/src/jsonrpccpp/common/procedure.cpp index 5a8f0795..2cffcf32 100644 --- a/src/jsonrpccpp/common/procedure.cpp +++ b/src/jsonrpccpp/common/procedure.cpp @@ -8,179 +8,148 @@ ************************************************************************/ #include "procedure.h" -#include "exception.h" #include "errors.h" -#include +#include "exception.h" #include +#include using namespace std; using namespace jsonrpc; -Procedure::Procedure() : - procedureName(""), - procedureType(RPC_METHOD), - returntype(JSON_BOOLEAN), - paramDeclaration(PARAMS_BY_NAME) -{ -} +Procedure::Procedure() + : procedureName(""), procedureType(RPC_METHOD), returntype(JSON_BOOLEAN), + paramDeclaration(PARAMS_BY_NAME) {} -Procedure::Procedure(const string &name, parameterDeclaration_t paramType, jsontype_t returntype, ...) -{ - va_list parameters; - va_start(parameters, returntype); - const char* paramname = va_arg(parameters, const char*); - jsontype_t type; - while(paramname != NULL) { - type = (jsontype_t)va_arg(parameters, int); - this->AddParameter(paramname, type); - paramname = va_arg(parameters, const char*); - } - va_end(parameters); - this->procedureName = name; - this->returntype = returntype; - this->procedureType = RPC_METHOD; - this->paramDeclaration = paramType; -} -Procedure::Procedure(const string &name, parameterDeclaration_t paramType, ...) -{ - va_list parameters; - va_start(parameters, paramType); - const char* paramname = va_arg(parameters, const char*); - jsontype_t type; - while(paramname != NULL) { - type = (jsontype_t)va_arg(parameters, int); - this->AddParameter(paramname, type); - paramname = va_arg(parameters, const char*); - } - va_end(parameters); - this->procedureName = name; - this->procedureType = RPC_NOTIFICATION; - this->paramDeclaration = paramType; - this->returntype = JSON_BOOLEAN; +Procedure::Procedure(const string &name, parameterDeclaration_t paramType, + jsontype_t returntype, ...) { + va_list parameters; + va_start(parameters, returntype); + const char *paramname = va_arg(parameters, const char *); + jsontype_t type; + while (paramname != NULL) { + type = (jsontype_t)va_arg(parameters, int); + this->AddParameter(paramname, type); + paramname = va_arg(parameters, const char *); + } + va_end(parameters); + this->procedureName = name; + this->returntype = returntype; + this->procedureType = RPC_METHOD; + this->paramDeclaration = paramType; +} +Procedure::Procedure(const string &name, parameterDeclaration_t paramType, + ...) { + va_list parameters; + va_start(parameters, paramType); + const char *paramname = va_arg(parameters, const char *); + jsontype_t type; + while (paramname != NULL) { + type = (jsontype_t)va_arg(parameters, int); + this->AddParameter(paramname, type); + paramname = va_arg(parameters, const char *); + } + va_end(parameters); + this->procedureName = name; + this->procedureType = RPC_NOTIFICATION; + this->paramDeclaration = paramType; + this->returntype = JSON_BOOLEAN; } -bool Procedure::ValdiateParameters (const Json::Value& parameters) const -{ - if (this->parametersName.empty()) - { - return true; - } - if(parameters.isArray() && this->paramDeclaration == PARAMS_BY_POSITION) - { - return this->ValidatePositionalParameters(parameters); - } - else if(parameters.isObject() && this->paramDeclaration == PARAMS_BY_NAME) - { - return this->ValidateNamedParameters(parameters); - } - else - { - return false; - } -} -const parameterNameList_t& Procedure::GetParameters () const -{ - return this->parametersName; -} -procedure_t Procedure::GetProcedureType () const -{ - return this->procedureType; -} -const std::string& Procedure::GetProcedureName () const -{ - return this->procedureName; -} -parameterDeclaration_t Procedure::GetParameterDeclarationType () const -{ - return this->paramDeclaration; -} -jsontype_t Procedure::GetReturnType () const -{ - return this->returntype; -} +bool Procedure::ValdiateParameters(const Json::Value ¶meters) const { + if (this->parametersName.empty()) { + return true; + } + if (parameters.isArray() && this->paramDeclaration == PARAMS_BY_POSITION) { + return this->ValidatePositionalParameters(parameters); + } else if (parameters.isObject() && + this->paramDeclaration == PARAMS_BY_NAME) { + return this->ValidateNamedParameters(parameters); + } else { + return false; + } +} +const parameterNameList_t &Procedure::GetParameters() const { + return this->parametersName; +} +procedure_t Procedure::GetProcedureType() const { return this->procedureType; } +const std::string &Procedure::GetProcedureName() const { + return this->procedureName; +} +parameterDeclaration_t Procedure::GetParameterDeclarationType() const { + return this->paramDeclaration; +} +jsontype_t Procedure::GetReturnType() const { return this->returntype; } -void Procedure::SetProcedureName (const string &name) -{ - this->procedureName = name; +void Procedure::SetProcedureName(const string &name) { + this->procedureName = name; } -void Procedure::SetProcedureType (procedure_t type) -{ - this->procedureType = type; +void Procedure::SetProcedureType(procedure_t type) { + this->procedureType = type; } -void Procedure::SetReturnType (jsontype_t type) -{ - this->returntype = type; -} -void Procedure::SetParameterDeclarationType (parameterDeclaration_t type) -{ - this->paramDeclaration = type; +void Procedure::SetReturnType(jsontype_t type) { this->returntype = type; } +void Procedure::SetParameterDeclarationType(parameterDeclaration_t type) { + this->paramDeclaration = type; } -void Procedure::AddParameter (const string& name, jsontype_t type) -{ - this->parametersName[name] = type; - this->parametersPosition.push_back(type); -} -bool Procedure::ValidateNamedParameters (const Json::Value ¶meters) const -{ - bool ok = parameters.isObject() || parameters.isNull(); - for (map::const_iterator it = this->parametersName.begin(); ok == true && it != this->parametersName.end(); ++it) - { - if (!parameters.isMember(it->first)) - { - ok = false; - } - else - { - ok = this->ValidateSingleParameter(it->second, parameters[it->first]); - } +void Procedure::AddParameter(const string &name, jsontype_t type) { + this->parametersName[name] = type; + this->parametersPosition.push_back(type); +} +bool Procedure::ValidateNamedParameters(const Json::Value ¶meters) const { + bool ok = parameters.isObject() || parameters.isNull(); + for (map::const_iterator it = + this->parametersName.begin(); + ok == true && it != this->parametersName.end(); ++it) { + if (!parameters.isMember(it->first)) { + ok = false; + } else { + ok = this->ValidateSingleParameter(it->second, parameters[it->first]); } - return ok; + } + return ok; } -bool Procedure::ValidatePositionalParameters (const Json::Value ¶meters) const -{ - bool ok = true; +bool Procedure::ValidatePositionalParameters( + const Json::Value ¶meters) const { + bool ok = true; - if(parameters.size() != this->parametersPosition.size()) - { - return false; - } + if (parameters.size() != this->parametersPosition.size()) { + return false; + } - for(unsigned int i=0; ok && i < this->parametersPosition.size(); i++) - { - ok = this->ValidateSingleParameter(this->parametersPosition.at(i), parameters[i]); - } - return ok; -} -bool Procedure::ValidateSingleParameter (jsontype_t expectedType, const Json::Value &value) const -{ - bool ok = true; - switch (expectedType) - { - case JSON_STRING: - if (!value.isString()) - ok = false; - break; - case JSON_BOOLEAN: - if (!value.isBool()) - ok = false; - break; - case JSON_INTEGER: - if (!value.isIntegral()) - ok = false; - break; - case JSON_REAL: - if (!value.isDouble()) - ok = false; - break; - case JSON_OBJECT: - if (!value.isObject()) - ok = false; - break; - case JSON_ARRAY: - if (!value.isArray()) - ok = false; - break; - } - return ok; + for (unsigned int i = 0; ok && i < this->parametersPosition.size(); i++) { + ok = this->ValidateSingleParameter(this->parametersPosition.at(i), + parameters[i]); + } + return ok; +} +bool Procedure::ValidateSingleParameter(jsontype_t expectedType, + const Json::Value &value) const { + bool ok = true; + switch (expectedType) { + case JSON_STRING: + if (!value.isString()) + ok = false; + break; + case JSON_BOOLEAN: + if (!value.isBool()) + ok = false; + break; + case JSON_INTEGER: + if (!value.isIntegral()) + ok = false; + break; + case JSON_REAL: + if (!value.isDouble()) + ok = false; + break; + case JSON_OBJECT: + if (!value.isObject()) + ok = false; + break; + case JSON_ARRAY: + if (!value.isArray()) + ok = false; + break; + } + return ok; } diff --git a/src/jsonrpccpp/common/sharedconstants.h b/src/jsonrpccpp/common/sharedconstants.h new file mode 100644 index 00000000..1cfca7cc --- /dev/null +++ b/src/jsonrpccpp/common/sharedconstants.h @@ -0,0 +1,17 @@ +#ifndef SHAREDCONSTANTS_H +#define SHAREDCONSTANTS_H + +#ifdef __APPLE__ + #include +#endif + +#ifdef __linux__ + #include +#endif + + +#define DEFAULT_DELIMITER_CHAR char(0x0A) +#define DEFAULT_BUFFER_SIZE 1024 + + +#endif // SHAREDCONSTANTS_H diff --git a/src/jsonrpccpp/common/specificationparser.cpp b/src/jsonrpccpp/common/specificationparser.cpp index fff3aaea..900a49e6 100644 --- a/src/jsonrpccpp/common/specificationparser.cpp +++ b/src/jsonrpccpp/common/specificationparser.cpp @@ -8,166 +8,152 @@ ************************************************************************/ #include "specificationparser.h" -#include #include +#include +#include using namespace std; using namespace jsonrpc; - -vector SpecificationParser::GetProceduresFromFile (const string &filename) throw(JsonRpcException) -{ - string content; - GetFileContent(filename, content); - return GetProceduresFromString(content); +vector +SpecificationParser::GetProceduresFromFile(const string &filename) { + string content; + GetFileContent(filename, content); + return GetProceduresFromString(content); } -vector SpecificationParser::GetProceduresFromString(const string &content) throw(JsonRpcException) -{ +vector +SpecificationParser::GetProceduresFromString(const string &content) { - Json::Reader reader; - Json::Value val; - if(!reader.parse(content,val)) - { - throw JsonRpcException(Errors::ERROR_RPC_JSON_PARSE_ERROR, " specification file contains syntax errors"); - } + Json::Reader reader; + Json::Value val; + if (!reader.parse(content, val)) { + throw JsonRpcException(Errors::ERROR_RPC_JSON_PARSE_ERROR, + " specification file contains syntax errors"); + } - if (!val.isArray()) - { - throw JsonRpcException(Errors::ERROR_SERVER_PROCEDURE_SPECIFICATION_SYNTAX, " top level json value is not an array"); - } + if (!val.isArray()) { + throw JsonRpcException(Errors::ERROR_SERVER_PROCEDURE_SPECIFICATION_SYNTAX, + " top level json value is not an array"); + } - vector result; - map procnames; - for (unsigned int i = 0; i < val.size(); i++) - { - Procedure proc; - GetProcedure(val[i], proc); - if (procnames.find(proc.GetProcedureName()) != procnames.end()) - { - throw JsonRpcException(Errors::ERROR_SERVER_PROCEDURE_SPECIFICATION_SYNTAX, "Procedurename not unique: " + proc.GetProcedureName()); - } - procnames[proc.GetProcedureName()] = proc; - result.push_back(proc); + vector result; + map procnames; + for (unsigned int i = 0; i < val.size(); i++) { + Procedure proc; + GetProcedure(val[i], proc); + if (procnames.find(proc.GetProcedureName()) != procnames.end()) { + throw JsonRpcException( + Errors::ERROR_SERVER_PROCEDURE_SPECIFICATION_SYNTAX, + "Procedurename not unique: " + proc.GetProcedureName()); } - return result; + procnames[proc.GetProcedureName()] = proc; + result.push_back(proc); + } + return result; } -void SpecificationParser::GetProcedure (Json::Value &signature, Procedure &result) -{ - if (signature.isObject() && GetProcedureName(signature) != "") - { - result.SetProcedureName(GetProcedureName(signature)); - if (signature.isMember(KEY_SPEC_RETURN_TYPE)) - { - result.SetProcedureType(RPC_METHOD); - result.SetReturnType(toJsonType(signature[KEY_SPEC_RETURN_TYPE])); - } - else - { - result.SetProcedureType(RPC_NOTIFICATION); - } - if (signature.isMember(KEY_SPEC_PROCEDURE_PARAMETERS)) - { - if (signature[KEY_SPEC_PROCEDURE_PARAMETERS].isObject() || signature[KEY_SPEC_PROCEDURE_PARAMETERS].isArray()) - { - if (signature[KEY_SPEC_PROCEDURE_PARAMETERS].isArray()) - { - result.SetParameterDeclarationType(PARAMS_BY_POSITION); - GetPositionalParameters(signature, result); - } - else if (signature[KEY_SPEC_PROCEDURE_PARAMETERS].isObject()) - { - result.SetParameterDeclarationType(PARAMS_BY_NAME); - GetNamedParameters(signature, result); - } - } - else - { - throw JsonRpcException(Errors::ERROR_SERVER_PROCEDURE_SPECIFICATION_SYNTAX, - "Invalid signature types in fileds: " - + signature.toStyledString()); - } - } +void SpecificationParser::GetProcedure(Json::Value &signature, + Procedure &result) { + if (signature.isObject() && GetProcedureName(signature) != "") { + result.SetProcedureName(GetProcedureName(signature)); + if (signature.isMember(KEY_SPEC_RETURN_TYPE)) { + result.SetProcedureType(RPC_METHOD); + result.SetReturnType(toJsonType(signature[KEY_SPEC_RETURN_TYPE])); + } else { + result.SetProcedureType(RPC_NOTIFICATION); } - else - { - throw JsonRpcException(Errors::ERROR_SERVER_PROCEDURE_SPECIFICATION_SYNTAX, - "procedure declaration does not contain name or parameters: " - + signature.toStyledString()); + if (signature.isMember(KEY_SPEC_PROCEDURE_PARAMETERS)) { + if (signature[KEY_SPEC_PROCEDURE_PARAMETERS].isObject() || + signature[KEY_SPEC_PROCEDURE_PARAMETERS].isArray()) { + if (signature[KEY_SPEC_PROCEDURE_PARAMETERS].isArray()) { + result.SetParameterDeclarationType(PARAMS_BY_POSITION); + GetPositionalParameters(signature, result); + } else if (signature[KEY_SPEC_PROCEDURE_PARAMETERS].isObject()) { + result.SetParameterDeclarationType(PARAMS_BY_NAME); + GetNamedParameters(signature, result); + } + } else { + throw JsonRpcException( + Errors::ERROR_SERVER_PROCEDURE_SPECIFICATION_SYNTAX, + "Invalid signature types in fileds: " + signature.toStyledString()); + } } + } else { + throw JsonRpcException( + Errors::ERROR_SERVER_PROCEDURE_SPECIFICATION_SYNTAX, + "procedure declaration does not contain name or parameters: " + + signature.toStyledString()); + } } -void SpecificationParser::GetFileContent (const std::string &filename, std::string& target) -{ - ifstream config(filename.c_str()); +void SpecificationParser::GetFileContent(const std::string &filename, + std::string &target) { + ifstream config(filename.c_str()); - if (config) - { - config.open(filename.c_str(), ios::in); - target.assign((std::istreambuf_iterator(config)), - (std::istreambuf_iterator())); - } - else - { - throw JsonRpcException(Errors::ERROR_SERVER_PROCEDURE_SPECIFICATION_NOT_FOUND, filename); - } + if (config) { + config.open(filename.c_str(), ios::in); + target.assign((std::istreambuf_iterator(config)), + (std::istreambuf_iterator())); + } else { + throw JsonRpcException( + Errors::ERROR_SERVER_PROCEDURE_SPECIFICATION_NOT_FOUND, filename); + } } -jsontype_t SpecificationParser::toJsonType (Json::Value &val) -{ - jsontype_t result; - switch(val.type()) - { - case Json::uintValue: - case Json::intValue: - result = JSON_INTEGER; - break; - case Json::realValue: - result = JSON_REAL; - break; - case Json::stringValue: - result = JSON_STRING; - break; - case Json::booleanValue: - result = JSON_BOOLEAN; - break; - case Json::arrayValue: - result = JSON_ARRAY; - break; - case Json::objectValue: - result = JSON_OBJECT; - break; - default: - throw JsonRpcException(Errors::ERROR_SERVER_PROCEDURE_SPECIFICATION_SYNTAX,"Unknown parameter type: " - + val.toStyledString()); - } - return result; +jsontype_t SpecificationParser::toJsonType(Json::Value &val) { + jsontype_t result; + switch (val.type()) { + case Json::uintValue: + case Json::intValue: + result = JSON_INTEGER; + break; + case Json::realValue: + result = JSON_REAL; + break; + case Json::stringValue: + result = JSON_STRING; + break; + case Json::booleanValue: + result = JSON_BOOLEAN; + break; + case Json::arrayValue: + result = JSON_ARRAY; + break; + case Json::objectValue: + result = JSON_OBJECT; + break; + default: + throw JsonRpcException(Errors::ERROR_SERVER_PROCEDURE_SPECIFICATION_SYNTAX, + "Unknown parameter type: " + val.toStyledString()); + } + return result; } -void SpecificationParser::GetPositionalParameters(Json::Value &val, Procedure &result) -{ - //Positional parameters - for (unsigned int i=0; i < val[KEY_SPEC_PROCEDURE_PARAMETERS].size(); i++) - { - stringstream paramname; - paramname << "param" << (i+1); - result.AddParameter(paramname.str(), toJsonType(val[KEY_SPEC_PROCEDURE_PARAMETERS][i])); - } +void SpecificationParser::GetPositionalParameters(Json::Value &val, + Procedure &result) { + // Positional parameters + for (unsigned int i = 0; i < val[KEY_SPEC_PROCEDURE_PARAMETERS].size(); i++) { + stringstream paramname; + paramname << "param" << std::setfill('0') << std::setw(2) << (i + 1); + result.AddParameter(paramname.str(), + toJsonType(val[KEY_SPEC_PROCEDURE_PARAMETERS][i])); + } } -void SpecificationParser::GetNamedParameters(Json::Value &val, Procedure &result) -{ - vector parameters = val[KEY_SPEC_PROCEDURE_PARAMETERS].getMemberNames(); - for (unsigned int i=0; i < parameters.size(); ++i) - { - result.AddParameter(parameters.at(i), toJsonType(val[KEY_SPEC_PROCEDURE_PARAMETERS][parameters.at(i)])); - } +void SpecificationParser::GetNamedParameters(Json::Value &val, + Procedure &result) { + vector parameters = + val[KEY_SPEC_PROCEDURE_PARAMETERS].getMemberNames(); + for (unsigned int i = 0; i < parameters.size(); ++i) { + result.AddParameter( + parameters.at(i), + toJsonType(val[KEY_SPEC_PROCEDURE_PARAMETERS][parameters.at(i)])); + } } -string SpecificationParser::GetProcedureName(Json::Value &signature) -{ - if(signature[KEY_SPEC_PROCEDURE_NAME].isString()) - return signature[KEY_SPEC_PROCEDURE_NAME].asString(); +string SpecificationParser::GetProcedureName(Json::Value &signature) { + if (signature[KEY_SPEC_PROCEDURE_NAME].isString()) + return signature[KEY_SPEC_PROCEDURE_NAME].asString(); - if (signature[KEY_SPEC_PROCEDURE_METHOD].isString()) - return signature[KEY_SPEC_PROCEDURE_METHOD].asString(); + if (signature[KEY_SPEC_PROCEDURE_METHOD].isString()) + return signature[KEY_SPEC_PROCEDURE_METHOD].asString(); - if (signature[KEY_SPEC_PROCEDURE_NOTIFICATION].isString()) - return signature[KEY_SPEC_PROCEDURE_NOTIFICATION].asString(); - return ""; + if (signature[KEY_SPEC_PROCEDURE_NOTIFICATION].isString()) + return signature[KEY_SPEC_PROCEDURE_NOTIFICATION].asString(); + return ""; } diff --git a/src/jsonrpccpp/common/specificationparser.h b/src/jsonrpccpp/common/specificationparser.h index 22c3626f..92df0142 100644 --- a/src/jsonrpccpp/common/specificationparser.h +++ b/src/jsonrpccpp/common/specificationparser.h @@ -18,8 +18,8 @@ namespace jsonrpc { class SpecificationParser { public: - static std::vector GetProceduresFromFile(const std::string& filename) throw (JsonRpcException); - static std::vector GetProceduresFromString(const std::string& spec) throw (JsonRpcException); + static std::vector GetProceduresFromFile(const std::string& filename) ; + static std::vector GetProceduresFromString(const std::string& spec) ; static void GetFileContent (const std::string& filename, std::string& target); diff --git a/src/jsonrpccpp/common/specificationwriter.cpp b/src/jsonrpccpp/common/specificationwriter.cpp index 50129619..04aa4f9f 100644 --- a/src/jsonrpccpp/common/specificationwriter.cpp +++ b/src/jsonrpccpp/common/specificationwriter.cpp @@ -10,82 +10,75 @@ #include "specificationwriter.h" #include "jsonparser.h" #include -#include #include +#include using namespace std; using namespace jsonrpc; -Json::Value SpecificationWriter::toJsonValue (const vector &procedures) -{ - Json::Value result; - Json::Value row; - for(unsigned int i=0; i < procedures.size(); i++) - { - procedureToJsonValue(procedures.at(i), row); - result[i] = row; - row.clear(); - } - return result; +Json::Value +SpecificationWriter::toJsonValue(const vector &procedures) { + Json::Value result; + Json::Value row; + for (unsigned int i = 0; i < procedures.size(); i++) { + procedureToJsonValue(procedures.at(i), row); + result[i] = row; + row.clear(); + } + return result; } -std::string SpecificationWriter::toString (const vector &procedures) -{ - Json::StyledWriter wr; - return wr.write(toJsonValue(procedures)); +std::string SpecificationWriter::toString(const vector &procedures) { + Json::StyledWriter wr; + return wr.write(toJsonValue(procedures)); } -bool SpecificationWriter::toFile (const std::string &filename, const vector &procedures) -{ - ofstream file; - file.open(filename.c_str(), ios_base::out); - if (!file.is_open()) - return false; - file << toString(procedures); - file.close(); - return true; +bool SpecificationWriter::toFile(const std::string &filename, + const vector &procedures) { + ofstream file; + file.open(filename.c_str(), ios_base::out); + if (!file.is_open()) + return false; + file << toString(procedures); + file.close(); + return true; } -Json::Value SpecificationWriter::toJsonLiteral (jsontype_t type) -{ - Json::Value literal; - switch(type) - { - case JSON_BOOLEAN: - literal = true; - break; - case JSON_STRING: - literal = "somestring"; - break; - case JSON_REAL: - literal = 1.0; - break; - case JSON_ARRAY: - literal = Json::arrayValue; - break; - case JSON_OBJECT: - literal["objectkey"] = "objectvalue"; - break; - case JSON_INTEGER: - literal = 1; - break; - } - return literal; +Json::Value SpecificationWriter::toJsonLiteral(jsontype_t type) { + Json::Value literal; + switch (type) { + case JSON_BOOLEAN: + literal = true; + break; + case JSON_STRING: + literal = "somestring"; + break; + case JSON_REAL: + literal = 1.0; + break; + case JSON_ARRAY: + literal = Json::arrayValue; + break; + case JSON_OBJECT: + literal["objectkey"] = "objectvalue"; + break; + case JSON_INTEGER: + literal = 1; + break; + } + return literal; } -void SpecificationWriter::procedureToJsonValue (const Procedure &procedure, Json::Value &target) -{ - target[KEY_SPEC_PROCEDURE_NAME] = procedure.GetProcedureName(); - if(procedure.GetProcedureType() == RPC_METHOD) - { - target[KEY_SPEC_RETURN_TYPE] = toJsonLiteral(procedure.GetReturnType()); - } - for(parameterNameList_t::const_iterator it = procedure.GetParameters().begin(); it != procedure.GetParameters().end(); ++it) - { - if(procedure.GetParameterDeclarationType() == PARAMS_BY_NAME) - { - target[KEY_SPEC_PROCEDURE_PARAMETERS][it->first] = toJsonLiteral(it->second); - } - else - { - target[KEY_SPEC_PROCEDURE_PARAMETERS].append(toJsonLiteral(it->second)); - } +void SpecificationWriter::procedureToJsonValue(const Procedure &procedure, + Json::Value &target) { + target[KEY_SPEC_PROCEDURE_NAME] = procedure.GetProcedureName(); + if (procedure.GetProcedureType() == RPC_METHOD) { + target[KEY_SPEC_RETURN_TYPE] = toJsonLiteral(procedure.GetReturnType()); + } + for (parameterNameList_t::const_iterator it = + procedure.GetParameters().begin(); + it != procedure.GetParameters().end(); ++it) { + if (procedure.GetParameterDeclarationType() == PARAMS_BY_NAME) { + target[KEY_SPEC_PROCEDURE_PARAMETERS][it->first] = + toJsonLiteral(it->second); + } else { + target[KEY_SPEC_PROCEDURE_PARAMETERS].append(toJsonLiteral(it->second)); } + } } - diff --git a/src/jsonrpccpp/common/streamreader.cpp b/src/jsonrpccpp/common/streamreader.cpp new file mode 100644 index 00000000..a608858b --- /dev/null +++ b/src/jsonrpccpp/common/streamreader.cpp @@ -0,0 +1,26 @@ +#include "streamreader.h" +#include +#include + +using namespace jsonrpc; +using namespace std; + +StreamReader::StreamReader(size_t buffersize) + : buffersize(buffersize), buffer(static_cast(malloc(buffersize))) {} + +StreamReader::~StreamReader() { free(buffer); } + +bool StreamReader::Read(std::string &target, int fd, char delimiter) { + ssize_t bytesRead; + do { + bytesRead = read(fd, this->buffer, buffersize); + if (bytesRead < 0) { + return false; + } else { + target.append(buffer, static_cast(bytesRead)); + } + } while (target.find(delimiter) == string::npos && bytesRead > 0); + + target.pop_back(); + return true; +} diff --git a/src/jsonrpccpp/common/streamreader.h b/src/jsonrpccpp/common/streamreader.h new file mode 100644 index 00000000..e971a7e9 --- /dev/null +++ b/src/jsonrpccpp/common/streamreader.h @@ -0,0 +1,21 @@ +#ifndef STREAMREADER_H +#define STREAMREADER_H + +#include +#include + +namespace jsonrpc { +class StreamReader +{ +public: + StreamReader(size_t buffersize); + virtual ~StreamReader(); + + bool Read(std::string &target, int fd, char delimiter); + +private: + size_t buffersize; + char* buffer; +}; +} +#endif // STREAMREADER_H diff --git a/src/jsonrpccpp/common/streamwriter.cpp b/src/jsonrpccpp/common/streamwriter.cpp new file mode 100644 index 00000000..5f71cfb1 --- /dev/null +++ b/src/jsonrpccpp/common/streamwriter.cpp @@ -0,0 +1,22 @@ +#include "streamwriter.h" +#include + +using namespace jsonrpc; +using namespace std; + +bool StreamWriter::Write(const string &source, int fd) { + ssize_t bytesWritten; + size_t totalSize = source.size(); + size_t remainingSize = totalSize; + + do { + bytesWritten = + write(fd, source.c_str() + (totalSize - remainingSize), remainingSize); + if (bytesWritten < 0) { + return false; + } else { + remainingSize -= static_cast(bytesWritten); + } + } while (remainingSize > 0); + return true; +} diff --git a/src/jsonrpccpp/common/streamwriter.h b/src/jsonrpccpp/common/streamwriter.h new file mode 100644 index 00000000..bdb50cd5 --- /dev/null +++ b/src/jsonrpccpp/common/streamwriter.h @@ -0,0 +1,17 @@ +#ifndef STREAMWRITER_H +#define STREAMWRITER_H + +#include +#include + +namespace jsonrpc { +class StreamWriter +{ +public: + bool Write(const std::string &source, int fd); +}; + +} + + +#endif // STREAMWRITER_H diff --git a/src/jsonrpccpp/server/abstractprotocolhandler.cpp b/src/jsonrpccpp/server/abstractprotocolhandler.cpp index c9ea96ae..580090eb 100644 --- a/src/jsonrpccpp/server/abstractprotocolhandler.cpp +++ b/src/jsonrpccpp/server/abstractprotocolhandler.cpp @@ -16,88 +16,72 @@ using namespace jsonrpc; using namespace std; -AbstractProtocolHandler::AbstractProtocolHandler(IProcedureInvokationHandler &handler) : - handler(handler) -{ -} +AbstractProtocolHandler::AbstractProtocolHandler( + IProcedureInvokationHandler &handler) + : handler(handler) {} -AbstractProtocolHandler::~AbstractProtocolHandler() -{ -} +AbstractProtocolHandler::~AbstractProtocolHandler() {} -void AbstractProtocolHandler::AddProcedure(const Procedure &procedure) -{ - this->procedures[procedure.GetProcedureName()] = procedure; +void AbstractProtocolHandler::AddProcedure(const Procedure &procedure) { + this->procedures[procedure.GetProcedureName()] = procedure; } -void AbstractProtocolHandler::HandleRequest(const std::string &request, std::string &retValue) -{ - Json::Reader reader; - Json::Value req; - Json::Value resp; - Json::FastWriter w; +void AbstractProtocolHandler::HandleRequest(const std::string &request, + std::string &retValue) { + Json::Reader reader; + Json::Value req; + Json::Value resp; + Json::FastWriter w; - if (reader.parse(request, req, false)) - { - this->HandleJsonRequest(req, resp); - } - else - { - this->WrapError(Json::nullValue, Errors::ERROR_RPC_JSON_PARSE_ERROR, Errors::GetErrorMessage(Errors::ERROR_RPC_JSON_PARSE_ERROR), resp); - } + if (reader.parse(request, req, false)) { + this->HandleJsonRequest(req, resp); + } else { + this->WrapError(Json::nullValue, Errors::ERROR_RPC_JSON_PARSE_ERROR, + Errors::GetErrorMessage(Errors::ERROR_RPC_JSON_PARSE_ERROR), + resp); + } - if (resp != Json::nullValue) - retValue = w.write(resp); + if (resp != Json::nullValue) + retValue = w.write(resp); } -void AbstractProtocolHandler::ProcessRequest(const Json::Value &request, Json::Value &response) -{ - Procedure& method = this->procedures[request[KEY_REQUEST_METHODNAME].asString()]; - Json::Value result; +void AbstractProtocolHandler::ProcessRequest(const Json::Value &request, + Json::Value &response) { + Procedure &method = + this->procedures[request[KEY_REQUEST_METHODNAME].asString()]; + Json::Value result; - if (method.GetProcedureType() == RPC_METHOD) - { - handler.HandleMethodCall(method, request[KEY_REQUEST_PARAMETERS], result); - this->WrapResult(request, response, result); - } - else - { - handler.HandleNotificationCall(method, request[KEY_REQUEST_PARAMETERS]); - response = Json::nullValue; - } + if (method.GetProcedureType() == RPC_METHOD) { + handler.HandleMethodCall(method, request[KEY_REQUEST_PARAMETERS], result); + this->WrapResult(request, response, result); + } else { + handler.HandleNotificationCall(method, request[KEY_REQUEST_PARAMETERS]); + response = Json::nullValue; + } } -int AbstractProtocolHandler::ValidateRequest(const Json::Value &request) -{ - int error = 0; - Procedure proc; - if (!this->ValidateRequestFields(request)) - { - error = Errors::ERROR_RPC_INVALID_REQUEST; - } - else - { - map::iterator it = this->procedures.find(request[KEY_REQUEST_METHODNAME].asString()); - if (it != this->procedures.end()) - { - proc = it->second; - if(this->GetRequestType(request) == RPC_METHOD && proc.GetProcedureType() == RPC_NOTIFICATION) - { - error = Errors::ERROR_SERVER_PROCEDURE_IS_NOTIFICATION; - } - else if(this->GetRequestType(request) == RPC_NOTIFICATION && proc.GetProcedureType() == RPC_METHOD) - { - error = Errors::ERROR_SERVER_PROCEDURE_IS_METHOD; - } - else if (!proc.ValdiateParameters(request[KEY_REQUEST_PARAMETERS])) - { - error = Errors::ERROR_RPC_INVALID_PARAMS; - } - } - else - { - error = Errors::ERROR_RPC_METHOD_NOT_FOUND; - } +int AbstractProtocolHandler::ValidateRequest(const Json::Value &request) { + int error = 0; + Procedure proc; + if (!this->ValidateRequestFields(request)) { + error = Errors::ERROR_RPC_INVALID_REQUEST; + } else { + map::iterator it = + this->procedures.find(request[KEY_REQUEST_METHODNAME].asString()); + if (it != this->procedures.end()) { + proc = it->second; + if (this->GetRequestType(request) == RPC_METHOD && + proc.GetProcedureType() == RPC_NOTIFICATION) { + error = Errors::ERROR_SERVER_PROCEDURE_IS_NOTIFICATION; + } else if (this->GetRequestType(request) == RPC_NOTIFICATION && + proc.GetProcedureType() == RPC_METHOD) { + error = Errors::ERROR_SERVER_PROCEDURE_IS_METHOD; + } else if (!proc.ValdiateParameters(request[KEY_REQUEST_PARAMETERS])) { + error = Errors::ERROR_RPC_INVALID_PARAMS; + } + } else { + error = Errors::ERROR_RPC_METHOD_NOT_FOUND; } - return error; + } + return error; } diff --git a/src/jsonrpccpp/server/abstractserverconnector.cpp b/src/jsonrpccpp/server/abstractserverconnector.cpp index 1ba82186..b8d37ae4 100644 --- a/src/jsonrpccpp/server/abstractserverconnector.cpp +++ b/src/jsonrpccpp/server/abstractserverconnector.cpp @@ -8,42 +8,27 @@ ************************************************************************/ #include "abstractserverconnector.h" -#include #include +#include using namespace std; using namespace jsonrpc; -AbstractServerConnector::AbstractServerConnector() -{ - this->handler = NULL; -} +AbstractServerConnector::AbstractServerConnector() { this->handler = NULL; } -AbstractServerConnector::~AbstractServerConnector() -{ -} +AbstractServerConnector::~AbstractServerConnector() {} -bool AbstractServerConnector::OnRequest(const std::string& request, void* addInfo) -{ - string response; - if (this->handler != NULL) - { - this->handler->HandleRequest(request, response); - this->SendResponse(response, addInfo); - return true; - } - else - { - return false; - } +void AbstractServerConnector::ProcessRequest(const string &request, + string &response) { + if (this->handler != NULL) { + this->handler->HandleRequest(request, response); + } } -void AbstractServerConnector::SetHandler(IClientConnectionHandler* handler) -{ - this->handler = handler; +void AbstractServerConnector::SetHandler(IClientConnectionHandler *handler) { + this->handler = handler; } -IClientConnectionHandler *AbstractServerConnector::GetHandler() -{ - return this->handler; +IClientConnectionHandler *AbstractServerConnector::GetHandler() { + return this->handler; } diff --git a/src/jsonrpccpp/server/abstractserverconnector.h b/src/jsonrpccpp/server/abstractserverconnector.h index e655b7d2..58507829 100644 --- a/src/jsonrpccpp/server/abstractserverconnector.h +++ b/src/jsonrpccpp/server/abstractserverconnector.h @@ -10,50 +10,37 @@ #ifndef JSONRPC_CPP_SERVERCONNECTOR_H_ #define JSONRPC_CPP_SERVERCONNECTOR_H_ -#include #include "iclientconnectionhandler.h" +#include -namespace jsonrpc -{ - - class AbstractServerConnector - { - public: - AbstractServerConnector(); - virtual ~AbstractServerConnector(); - - /** - * This method should signal the Connector to start waiting for requests, in any way that is appropriate for the derived connector class. - * If something went wrong, this method should return false, otherwise true. - */ - virtual bool StartListening() = 0; - /** - * This method should signal the Connector to stop waiting for requests, in any way that is appropriate for the derived connector class. - * If something went wrong, this method should return false, otherwise true. - */ - virtual bool StopListening() = 0; - - /** - * This method should send a response to the client in any way that is appropriate for the derived connector class. - * @param response - the response that should be send to the client - * @param addInfo - additional Info, that the Connector might need for responding. - * @return returns true on success, false otherwise - */ - bool virtual SendResponse(const std::string& response, void* addInfo = NULL) = 0; - - /** - * This method must be called, when a request is recognised. It will do everything else for you (including sending the response). - * @param request - the request that has been recognised. - * @param addInfo - additional Info, that the Connector might need for responding. - */ - bool OnRequest(const std::string& request, void* addInfo = NULL); - - void SetHandler(IClientConnectionHandler* handler); - IClientConnectionHandler* GetHandler(); - - private: - IClientConnectionHandler *handler; - }; +namespace jsonrpc { + +class AbstractServerConnector { +public: + AbstractServerConnector(); + virtual ~AbstractServerConnector(); + + /** + * This method should signal the Connector to start waiting for requests, in + * any way that is appropriate for the derived connector class. + * If something went wrong, this method should return false, otherwise true. + */ + virtual bool StartListening() = 0; + /** + * This method should signal the Connector to stop waiting for requests, in + * any way that is appropriate for the derived connector class. + * If something went wrong, this method should return false, otherwise true. + */ + virtual bool StopListening() = 0; + + void ProcessRequest(const std::string &request, std::string &response); + + void SetHandler(IClientConnectionHandler *handler); + IClientConnectionHandler *GetHandler(); + +private: + IClientConnectionHandler *handler; +}; } /* namespace jsonrpc */ #endif /* JSONRPC_CPP_ERVERCONNECTOR_H_ */ diff --git a/src/jsonrpccpp/server/abstractthreadedserver.cpp b/src/jsonrpccpp/server/abstractthreadedserver.cpp new file mode 100644 index 00000000..693bc811 --- /dev/null +++ b/src/jsonrpccpp/server/abstractthreadedserver.cpp @@ -0,0 +1,51 @@ +#include "abstractthreadedserver.h" + +using namespace jsonrpc; +using namespace std; + +AbstractThreadedServer::AbstractThreadedServer(size_t threads) + : running(false), threadPool(threads), threads(threads) {} + +AbstractThreadedServer::~AbstractThreadedServer() { this->StopListening(); } + +bool AbstractThreadedServer::StartListening() { + if (this->running) + return false; + + if (!this->InitializeListener()) + return false; + + this->running = true; + + this->listenerThread = + unique_ptr(new thread(&AbstractThreadedServer::ListenLoop, this)); + + return true; +} + +bool AbstractThreadedServer::StopListening() { + if (!this->running) + return false; + + this->running = false; + + this->listenerThread->join(); + return true; +} + +void AbstractThreadedServer::ListenLoop() { + while (this->running) { + int conn = this->CheckForConnection(); + + if (conn > 0) { + if (this->threads > 0) { + this->threadPool.enqueue(&AbstractThreadedServer::HandleConnection, + this, conn); + } else { + this->HandleConnection(conn); + } + } else { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + } +} diff --git a/src/jsonrpccpp/server/abstractthreadedserver.h b/src/jsonrpccpp/server/abstractthreadedserver.h new file mode 100644 index 00000000..4fa3f3c7 --- /dev/null +++ b/src/jsonrpccpp/server/abstractthreadedserver.h @@ -0,0 +1,49 @@ +#ifndef ABSTRACTTHREADEDSERVER_H +#define ABSTRACTTHREADEDSERVER_H + +#include "abstractserverconnector.h" +#include "threadpool.h" +#include +#include + +namespace jsonrpc { +class AbstractThreadedServer : public AbstractServerConnector { +public: + AbstractThreadedServer(size_t threads); + virtual ~AbstractThreadedServer(); + + virtual bool StartListening(); + virtual bool StopListening(); + +protected: + /** + * @brief InitializeListener should initialize sockets, file descriptors etc. + * @return + */ + virtual bool InitializeListener() = 0; + + /** + * @brief CheckForConnection should poll for a new connection. This must be + * a non-blocking call. + * @return a handle which is passed on to HandleConnection() + */ + virtual int CheckForConnection() = 0; + + /** + * @brief HandleConnection must handle connection information for a given + * handle that has been returned by CheckForConnection() + * @param connection + */ + virtual void HandleConnection(int connection) = 0; + +private: + bool running; + std::unique_ptr listenerThread; + ThreadPool threadPool; + size_t threads; + + void ListenLoop(); +}; +} + +#endif // ABSTRACTTHREADEDSERVER_H diff --git a/src/jsonrpccpp/server/connectors/filedescriptorserver.cpp b/src/jsonrpccpp/server/connectors/filedescriptorserver.cpp index 8effaa97..6eb51b4d 100644 --- a/src/jsonrpccpp/server/connectors/filedescriptorserver.cpp +++ b/src/jsonrpccpp/server/connectors/filedescriptorserver.cpp @@ -8,12 +8,14 @@ ************************************************************************/ #include "filedescriptorserver.h" +#include "../../common/sharedconstants.h" + +#include +#include #include -#include #include +#include #include -#include -#include using namespace jsonrpc; using namespace std; @@ -22,126 +24,50 @@ using namespace std; #ifndef DELIMITER_CHAR #define DELIMITER_CHAR char(0x0A) #endif -#define READ_TIMEOUT 0.2 // Set timeout in seconds +#define READ_TIMEOUT 0.001 // Set timeout in seconds -FileDescriptorServer::FileDescriptorServer(int inputfd, int outputfd) : - running(false), inputfd(inputfd), outputfd(outputfd) -{ -} - -bool FileDescriptorServer::StartListening() -{ - if(this->running) - return false; +FileDescriptorServer::FileDescriptorServer(int inputfd, int outputfd) + : AbstractThreadedServer(0), inputfd(inputfd), outputfd(outputfd), + reader(DEFAULT_BUFFER_SIZE) {} +bool FileDescriptorServer::InitializeListener() { if (!IsReadable(inputfd) || !IsWritable(outputfd)) return false; - - this->running = true; - int ret = pthread_create(&(this->listenning_thread), NULL, FileDescriptorServer::LaunchLoop, this); - this->running = static_cast(ret == 0); - - return this->running; -} - -bool FileDescriptorServer::StopListening() -{ - if (!this->running) - return false; - this->running = false; - pthread_join(this->listenning_thread, NULL); - return !(this->running); -} - -void FileDescriptorServer::Wait() const { - pthread_join(this->listenning_thread, NULL); -} - -bool FileDescriptorServer::SendResponse(const string& response, void* addInfo) -{ - (void)addInfo; // Suppress warning - string toSend = response; - // If the DELIMITER_CHAR was not append, do it now - if (toSend.find(DELIMITER_CHAR) == string::npos) - toSend.append(1, DELIMITER_CHAR); - - if (DELIMITER_CHAR != '\n') - toSend = toSend.substr(0, toSend.find_last_of('\n')) + DELIMITER_CHAR; - - ssize_t result = 0; - ssize_t nbytes = toSend.size(); - do - { - result = write(outputfd, &(toSend.c_str()[toSend.size() - nbytes]), toSend.size() - result); - nbytes -= result; - } while (result && nbytes); // While we are still writing and there is still to write - return result != 0; + return true; } -void* FileDescriptorServer::LaunchLoop(void *p_data) -{ - FileDescriptorServer *instance = reinterpret_cast(p_data);; - instance->ListenLoop(); - return NULL; -} - -void FileDescriptorServer::ListenLoop() -{ - while (this->running) - { - char buffer[BUFFER_SIZE]; - ssize_t nbytes; - string request; - do - { - // Wait for something to be read. - // Interrupt after a timeout to check it we should still wait or just stop. - int wait_ret = 0; - if ((wait_ret = this->WaitForRead()) > 0 && FD_ISSET(inputfd, &read_fds)) - { - this->running = (nbytes = read(inputfd, buffer, BUFFER_SIZE)) != 0; - request.append(buffer, nbytes); - } else { - if (wait_ret < 0) { - // File closed - this->running = false; - break; - } - } - } while (this->running && request.find(DELIMITER_CHAR) == string::npos); - if (this->running) { // False if either the input fd was closed or someone interrupted us with ::StopListening. - this->OnRequest(request, NULL); - } - } -} - -int FileDescriptorServer::WaitForRead() { - // Has to be reset after every call, as POSIX allow the value to be modifiable by the system. +int FileDescriptorServer::CheckForConnection() { FD_ZERO(&read_fds); FD_ZERO(&write_fds); FD_ZERO(&except_fds); FD_SET(inputfd, &read_fds); timeout.tv_sec = 0; - timeout.tv_usec = (suseconds_t) (READ_TIMEOUT * 1000000); + timeout.tv_usec = (suseconds_t)(READ_TIMEOUT * 1000000); // Wait for something to read return select(inputfd + 1, &read_fds, &write_fds, &except_fds, &timeout); } -bool FileDescriptorServer::IsReadable(int fd) -{ +void FileDescriptorServer::HandleConnection(int connection) { + (void)(connection); + string request, response; + reader.Read(request, inputfd, DEFAULT_DELIMITER_CHAR); + this->ProcessRequest(request, response); + response.append(1, DEFAULT_DELIMITER_CHAR); + writer.Write(response, outputfd); +} + +bool FileDescriptorServer::IsReadable(int fd) { int o_accmode = 0; int ret = fcntl(fd, F_GETFL, &o_accmode); if (ret == -1) return false; return ((o_accmode & O_ACCMODE) == O_RDONLY || - (o_accmode & O_ACCMODE) == O_RDWR); + (o_accmode & O_ACCMODE) == O_RDWR); } -bool FileDescriptorServer::IsWritable(int fd) -{ +bool FileDescriptorServer::IsWritable(int fd) { int ret = fcntl(fd, F_GETFL); if (ret == -1) return false; return ((ret & O_WRONLY) || (ret & O_RDWR)); } - diff --git a/src/jsonrpccpp/server/connectors/filedescriptorserver.h b/src/jsonrpccpp/server/connectors/filedescriptorserver.h index 2489512c..a01f3c1d 100644 --- a/src/jsonrpccpp/server/connectors/filedescriptorserver.h +++ b/src/jsonrpccpp/server/connectors/filedescriptorserver.h @@ -10,72 +10,49 @@ #ifndef JSONRPC_CPP_FILEDESCRIPTORSERVERCONNECTOR_H_ #define JSONRPC_CPP_FILEDESCRIPTORSERVERCONNECTOR_H_ -#include +#include "../../common/streamreader.h" +#include "../../common/streamwriter.h" +#include "../abstractthreadedserver.h" #include +#include #include - -#include "../abstractserverconnector.h" - -namespace jsonrpc -{ +#include + +namespace jsonrpc { +/** + * This class is the file descriptor implementation of an + * AbstractServerConnector. + * It uses the POSIX file API and POSIX thread API to performs its job. + * Each client request is handled in a new thread. + */ +class FileDescriptorServer : public AbstractThreadedServer { +public: /** - * This class is the file descriptor implementation of an AbstractServerConnector. - * It uses the POSIX file API and POSIX thread API to performs its job. - * Each client request is handled in a new thread. + * AbstractServerConnector + * @param inputfd The file descriptor already open for us to read + * @param outputfd The file descriptor already open for us to write */ - class FileDescriptorServer: public AbstractServerConnector - { - public: - /** - * AbstractServerConnector - * @param inputfd The file descriptor already open for us to read - * @param outputfd The file descriptor already open for us to write - */ - FileDescriptorServer(int inputfd, int outputfd); - /** - * This method launches the listening loop that will handle client connections. - * @return true if the file is readable, false otherwise. - */ - bool StartListening(); - /** - * This method stops the listening loop that will handle client connections. - * @return True if successful, false otherwise of if not listening. - */ - bool StopListening(); - /** - * This method sends the result of the RPC Call over the output file - * @param response The response to send to the client - * @param addInfo Additionnal parameters - * @return A boolean that indicates the success or the failure of the operation. - */ - bool SendResponse(const std::string& response, void* addInfo = NULL); - - /** - * This method blocks the caller as long as the server is listening to its input. - */ - void Wait() const; - - private: - bool running; - int inputfd; - int outputfd; - - // For select operation - fd_set read_fds; - fd_set write_fds; - fd_set except_fds; - struct timeval timeout; - - pthread_t listenning_thread; - - static void* LaunchLoop(void *p_data); - void ListenLoop(); - bool IsReadable(int fd); - bool IsWritable(int fd); - - int WaitForRead(); - }; + FileDescriptorServer(int inputfd, int outputfd); + + virtual bool InitializeListener(); + virtual int CheckForConnection(); + virtual void HandleConnection(int connection); + +private: + int inputfd; + int outputfd; + StreamReader reader; + StreamWriter writer; + + bool IsReadable(int fd); + bool IsWritable(int fd); + + // For select operation + fd_set read_fds; + fd_set write_fds; + fd_set except_fds; + struct timeval timeout; +}; } #endif /* JSONRPC_CPP_FILEDESCRIPTORSERVERCONNECTOR_H_ */ - diff --git a/src/jsonrpccpp/server/connectors/httpserver.cpp b/src/jsonrpccpp/server/connectors/httpserver.cpp index 0c23d229..5a4471e9 100644 --- a/src/jsonrpccpp/server/connectors/httpserver.cpp +++ b/src/jsonrpccpp/server/connectors/httpserver.cpp @@ -9,9 +9,9 @@ #include "httpserver.h" #include -#include #include #include +#include using namespace jsonrpc; using namespace std; @@ -19,171 +19,162 @@ using namespace std; #define BUFFERSIZE 65536 struct mhd_coninfo { - struct MHD_PostProcessor *postprocessor; - MHD_Connection* connection; - stringstream request; - HttpServer* server; - int code; + struct MHD_PostProcessor *postprocessor; + MHD_Connection *connection; + stringstream request; + HttpServer *server; + int code; }; -HttpServer::HttpServer(int port, const std::string &sslcert, const std::string &sslkey, int threads) : - AbstractServerConnector(), - port(port), - threads(threads), - running(false), - path_sslcert(sslcert), - path_sslkey(sslkey), - daemon(NULL) -{ -} - -IClientConnectionHandler *HttpServer::GetHandler(const std::string &url) -{ - if (AbstractServerConnector::GetHandler() != NULL) - return AbstractServerConnector::GetHandler(); - map::iterator it = this->urlhandler.find(url); - if (it != this->urlhandler.end()) - return it->second; - return NULL; +HttpServer::HttpServer(int port, const std::string &sslcert, + const std::string &sslkey, int threads) + : AbstractServerConnector(), port(port), threads(threads), running(false), + path_sslcert(sslcert), path_sslkey(sslkey), daemon(NULL) {} + +IClientConnectionHandler *HttpServer::GetHandler(const std::string &url) { + if (AbstractServerConnector::GetHandler() != NULL) + return AbstractServerConnector::GetHandler(); + map::iterator it = + this->urlhandler.find(url); + if (it != this->urlhandler.end()) + return it->second; + return NULL; } -bool HttpServer::StartListening() -{ - if(!this->running) - { - const bool has_epoll = (MHD_is_feature_supported(MHD_FEATURE_EPOLL) == MHD_YES); - const bool has_poll = (MHD_is_feature_supported(MHD_FEATURE_POLL) == MHD_YES); - unsigned int mhd_flags; - if (has_epoll) - // In MHD version 0.9.44 the flag is renamed to - // MHD_USE_EPOLL_INTERNALLY_LINUX_ONLY. In later versions both - // are deprecated. - #if defined(MHD_USE_EPOLL_INTERNALLY) - mhd_flags = MHD_USE_EPOLL_INTERNALLY; - #else - mhd_flags = MHD_USE_EPOLL_INTERNALLY_LINUX_ONLY; - #endif - else if (has_poll) - mhd_flags = MHD_USE_POLL_INTERNALLY; - else - mhd_flags = MHD_USE_SELECT_INTERNALLY; - if (this->path_sslcert != "" && this->path_sslkey != "") - { - try { - SpecificationParser::GetFileContent(this->path_sslcert, this->sslcert); - SpecificationParser::GetFileContent(this->path_sslkey, this->sslkey); - - this->daemon = MHD_start_daemon(MHD_USE_SSL | mhd_flags, this->port, NULL, NULL, HttpServer::callback, this, MHD_OPTION_HTTPS_MEM_KEY, this->sslkey.c_str(), MHD_OPTION_HTTPS_MEM_CERT, this->sslcert.c_str(), MHD_OPTION_THREAD_POOL_SIZE, this->threads, MHD_OPTION_END); - } - catch (JsonRpcException& ex) - { - return false; - } - } - else - { - this->daemon = MHD_start_daemon(mhd_flags, this->port, NULL, NULL, HttpServer::callback, this, MHD_OPTION_THREAD_POOL_SIZE, this->threads, MHD_OPTION_END); - } - if (this->daemon != NULL) - this->running = true; - +bool HttpServer::StartListening() { + if (!this->running) { + const bool has_epoll = + (MHD_is_feature_supported(MHD_FEATURE_EPOLL) == MHD_YES); + const bool has_poll = + (MHD_is_feature_supported(MHD_FEATURE_POLL) == MHD_YES); + unsigned int mhd_flags; + if (has_epoll) +// In MHD version 0.9.44 the flag is renamed to +// MHD_USE_EPOLL_INTERNALLY_LINUX_ONLY. In later versions both +// are deprecated. +#if defined(MHD_USE_EPOLL_INTERNALLY) + mhd_flags = MHD_USE_EPOLL_INTERNALLY; +#else + mhd_flags = MHD_USE_EPOLL_INTERNALLY_LINUX_ONLY; +#endif + else if (has_poll) + mhd_flags = MHD_USE_POLL_INTERNALLY; + if (this->path_sslcert != "" && this->path_sslkey != "") { + try { + SpecificationParser::GetFileContent(this->path_sslcert, this->sslcert); + SpecificationParser::GetFileContent(this->path_sslkey, this->sslkey); + + this->daemon = MHD_start_daemon( + MHD_USE_SSL | mhd_flags, this->port, NULL, NULL, + HttpServer::callback, this, MHD_OPTION_HTTPS_MEM_KEY, + this->sslkey.c_str(), MHD_OPTION_HTTPS_MEM_CERT, + this->sslcert.c_str(), MHD_OPTION_THREAD_POOL_SIZE, this->threads, + MHD_OPTION_END); + } catch (JsonRpcException &ex) { + return false; + } + } else { + this->daemon = MHD_start_daemon( + mhd_flags, this->port, NULL, NULL, HttpServer::callback, this, + MHD_OPTION_THREAD_POOL_SIZE, this->threads, MHD_OPTION_END); } - return this->running; + if (this->daemon != NULL) + this->running = true; + } + return this->running; } -bool HttpServer::StopListening() -{ - if(this->running) - { - MHD_stop_daemon(this->daemon); - this->running = false; - } - return true; +bool HttpServer::StopListening() { + if (this->running) { + MHD_stop_daemon(this->daemon); + this->running = false; + } + return true; } -bool HttpServer::SendResponse(const string& response, void* addInfo) -{ - struct mhd_coninfo* client_connection = static_cast(addInfo); - struct MHD_Response *result = MHD_create_response_from_buffer(response.size(),(void *) response.c_str(), MHD_RESPMEM_MUST_COPY); +bool HttpServer::SendResponse(const string &response, void *addInfo) { + struct mhd_coninfo *client_connection = + static_cast(addInfo); + struct MHD_Response *result = MHD_create_response_from_buffer( + response.size(), (void *)response.c_str(), MHD_RESPMEM_MUST_COPY); - MHD_add_response_header(result, "Content-Type", "application/json"); - MHD_add_response_header(result, "Access-Control-Allow-Origin", "*"); + MHD_add_response_header(result, "Content-Type", "application/json"); + MHD_add_response_header(result, "Access-Control-Allow-Origin", "*"); - int ret = MHD_queue_response(client_connection->connection, client_connection->code, result); - MHD_destroy_response(result); - return ret == MHD_YES; + int ret = MHD_queue_response(client_connection->connection, + client_connection->code, result); + MHD_destroy_response(result); + return ret == MHD_YES; } -bool HttpServer::SendOptionsResponse(void* addInfo) -{ - struct mhd_coninfo* client_connection = static_cast(addInfo); - struct MHD_Response *result = MHD_create_response_from_buffer(0, NULL, MHD_RESPMEM_MUST_COPY); - - MHD_add_response_header(result, "Allow", "POST, OPTIONS"); - MHD_add_response_header(result, "Access-Control-Allow-Origin", "*"); - MHD_add_response_header(result, "Access-Control-Allow-Headers", "origin, content-type, accept"); - MHD_add_response_header(result, "DAV", "1"); - - int ret = MHD_queue_response(client_connection->connection, client_connection->code, result); - MHD_destroy_response(result); - return ret == MHD_YES; +bool HttpServer::SendOptionsResponse(void *addInfo) { + struct mhd_coninfo *client_connection = + static_cast(addInfo); + struct MHD_Response *result = + MHD_create_response_from_buffer(0, NULL, MHD_RESPMEM_MUST_COPY); + + MHD_add_response_header(result, "Allow", "POST, OPTIONS"); + MHD_add_response_header(result, "Access-Control-Allow-Origin", "*"); + MHD_add_response_header(result, "Access-Control-Allow-Headers", + "origin, content-type, accept"); + MHD_add_response_header(result, "DAV", "1"); + + int ret = MHD_queue_response(client_connection->connection, + client_connection->code, result); + MHD_destroy_response(result); + return ret == MHD_YES; } -void HttpServer::SetUrlHandler(const string &url, IClientConnectionHandler *handler) -{ - this->urlhandler[url] = handler; - this->SetHandler(NULL); +void HttpServer::SetUrlHandler(const string &url, + IClientConnectionHandler *handler) { + this->urlhandler[url] = handler; + this->SetHandler(NULL); } -int HttpServer::callback(void *cls, MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **con_cls) -{ - (void)version; - if (*con_cls == NULL) - { - struct mhd_coninfo* client_connection = new mhd_coninfo; - client_connection->connection = connection; - client_connection->server = static_cast(cls); - *con_cls = client_connection; - return MHD_YES; - } - struct mhd_coninfo* client_connection = static_cast(*con_cls); - - if (string("POST") == method) - { - if (*upload_data_size != 0) - { - client_connection->request.write(upload_data, *upload_data_size); - *upload_data_size = 0; - return MHD_YES; - } - else - { - string response; - IClientConnectionHandler* handler = client_connection->server->GetHandler(string(url)); - if (handler == NULL) - { - client_connection->code = MHD_HTTP_INTERNAL_SERVER_ERROR; - client_connection->server->SendResponse("No client connection handler found", client_connection); - } - else - { - client_connection->code = MHD_HTTP_OK; - handler->HandleRequest(client_connection->request.str(), response); - client_connection->server->SendResponse(response, client_connection); - } - } - } - else if (string("OPTIONS") == method) { +int HttpServer::callback(void *cls, MHD_Connection *connection, const char *url, + const char *method, const char *version, + const char *upload_data, size_t *upload_data_size, + void **con_cls) { + (void)version; + if (*con_cls == NULL) { + struct mhd_coninfo *client_connection = new mhd_coninfo; + client_connection->connection = connection; + client_connection->server = static_cast(cls); + *con_cls = client_connection; + return MHD_YES; + } + struct mhd_coninfo *client_connection = + static_cast(*con_cls); + + if (string("POST") == method) { + if (*upload_data_size != 0) { + client_connection->request.write(upload_data, *upload_data_size); + *upload_data_size = 0; + return MHD_YES; + } else { + string response; + IClientConnectionHandler *handler = + client_connection->server->GetHandler(string(url)); + if (handler == NULL) { + client_connection->code = MHD_HTTP_INTERNAL_SERVER_ERROR; + client_connection->server->SendResponse( + "No client connection handler found", client_connection); + } else { client_connection->code = MHD_HTTP_OK; - client_connection->server->SendOptionsResponse(client_connection); - } - else - { - client_connection->code = MHD_HTTP_METHOD_NOT_ALLOWED; - client_connection->server->SendResponse("Not allowed HTTP Method", client_connection); + handler->HandleRequest(client_connection->request.str(), response); + client_connection->server->SendResponse(response, client_connection); + } } - delete client_connection; - *con_cls = NULL; - - return MHD_YES; + } else if (string("OPTIONS") == method) { + client_connection->code = MHD_HTTP_OK; + client_connection->server->SendOptionsResponse(client_connection); + } else { + client_connection->code = MHD_HTTP_METHOD_NOT_ALLOWED; + client_connection->server->SendResponse("Not allowed HTTP Method", + client_connection); + } + delete client_connection; + *con_cls = NULL; + + return MHD_YES; } diff --git a/src/jsonrpccpp/server/connectors/linuxtcpsocketserver.cpp b/src/jsonrpccpp/server/connectors/linuxtcpsocketserver.cpp index df680c70..a82ad84f 100644 --- a/src/jsonrpccpp/server/connectors/linuxtcpsocketserver.cpp +++ b/src/jsonrpccpp/server/connectors/linuxtcpsocketserver.cpp @@ -8,256 +8,116 @@ ************************************************************************/ #include "linuxtcpsocketserver.h" - -#include +#include "../../common/sharedconstants.h" +#include "../../common/streamreader.h" +#include "../../common/streamwriter.h" #include +#include #include -#include -#include #include +#include +#include -#include +#include #include +#include #include -#include - -#include - using namespace jsonrpc; using namespace std; -#define BUFFER_SIZE 64 -#ifndef DELIMITER_CHAR -#define DELIMITER_CHAR char(0x0A) -#endif //DELIMITER_CHAR +LinuxTcpSocketServer::LinuxTcpSocketServer(const std::string &ipToBind, + const unsigned int &port, + size_t threads) + : AbstractThreadedServer(threads), ipToBind(ipToBind), port(port) {} -LinuxTcpSocketServer::LinuxTcpSocketServer(const std::string& ipToBind, const unsigned int &port) : - AbstractServerConnector(), - running(false), - ipToBind(ipToBind), - port(port) -{ +LinuxTcpSocketServer::~LinuxTcpSocketServer() { + shutdown(this->socket_fd, 2); + close(this->socket_fd); } -bool LinuxTcpSocketServer::StartListening() -{ - if(!this->running) - { - //Create and bind socket here. - //Then launch the listenning loop. - this->socket_fd = socket(AF_INET, SOCK_STREAM, 0); - if(this->socket_fd < 0) - { - return false; - } - - fcntl(this->socket_fd, F_SETFL, FNDELAY); - int reuseaddr = 1; - setsockopt(this->socket_fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr)); - - /* start with a clean address structure */ - memset(&(this->address), 0, sizeof(struct sockaddr_in)); - - this->address.sin_family = AF_INET; - inet_aton(this->ipToBind.c_str(), &(this->address.sin_addr)); - this->address.sin_port = htons(this->port); - - if(::bind(this->socket_fd, reinterpret_cast(&(this->address)), sizeof(struct sockaddr_in)) != 0) - { - return false; - } - - if(listen(this->socket_fd, 5) != 0) - { - return false; - } - //Launch listening loop there - this->running = true; - int ret = pthread_create(&(this->listenning_thread), NULL, LinuxTcpSocketServer::LaunchLoop, this); - if(ret != 0) - { - pthread_detach(this->listenning_thread); - shutdown(this->socket_fd, 2); - close(this->socket_fd); - } - this->running = static_cast(ret==0); - return this->running; - } - else - { - return false; - } +bool LinuxTcpSocketServer::InitializeListener() { + this->socket_fd = socket(AF_INET, SOCK_STREAM, 0); + if (this->socket_fd < 0) { + return false; + } + + fcntl(this->socket_fd, F_SETFL, FNDELAY); + int reuseaddr = 1; + setsockopt(this->socket_fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, + sizeof(reuseaddr)); + + /* start with a clean address structure */ + memset(&(this->address), 0, sizeof(struct sockaddr_in)); + + this->address.sin_family = AF_INET; + inet_aton(this->ipToBind.c_str(), &(this->address.sin_addr)); + this->address.sin_port = htons(this->port); + + if (::bind(this->socket_fd, + reinterpret_cast(&(this->address)), + sizeof(struct sockaddr_in)) != 0) { + return false; + } + + if (listen(this->socket_fd, 5) != 0) { + return false; + } + return true; } -bool LinuxTcpSocketServer::StopListening() -{ - if(this->running) - { - this->running = false; - pthread_join(this->listenning_thread, NULL); - shutdown(this->socket_fd, 2); - close(this->socket_fd); - return !(this->running); - } - else - { - return false; - } +int LinuxTcpSocketServer::CheckForConnection() { + struct sockaddr_in connection_address; + memset(&connection_address, 0, sizeof(struct sockaddr_in)); + socklen_t address_length = sizeof(connection_address); + return accept(this->socket_fd, + reinterpret_cast(&(connection_address)), + &address_length); } -bool LinuxTcpSocketServer::SendResponse(const string& response, void* addInfo) -{ - bool result = false; - int connection_fd = reinterpret_cast(addInfo); - - string temp = response; - if(temp.find(DELIMITER_CHAR) == string::npos) - { - temp.append(1, DELIMITER_CHAR); - } - if(DELIMITER_CHAR != '\n') - { - char eot = DELIMITER_CHAR; - string toSend = temp.substr(0, toSend.find_last_of('\n')); - toSend += eot; - result = this->WriteToSocket(connection_fd, toSend); - } - else - { - result = this->WriteToSocket(connection_fd, temp); - } - CleanClose(connection_fd); - return result; -} - -void* LinuxTcpSocketServer::LaunchLoop(void *p_data) -{ - pthread_detach(pthread_self()); - LinuxTcpSocketServer *instance = reinterpret_cast(p_data);; - instance->ListenLoop(); - return NULL; -} - -void LinuxTcpSocketServer::ListenLoop() -{ - int connection_fd = 0; - struct sockaddr_in connection_address; - memset(&connection_address, 0, sizeof(struct sockaddr_in)); - socklen_t address_length = sizeof(connection_address); - while(this->running) - { - if((connection_fd = accept(this->socket_fd, reinterpret_cast(&(connection_address)), &address_length)) > 0) - { - pthread_t client_thread; - struct GenerateResponseParameters *params = new struct GenerateResponseParameters(); - params->instance = this; - params->connection_fd = connection_fd; - int ret = pthread_create(&client_thread, NULL, LinuxTcpSocketServer::GenerateResponse, params); - if(ret != 0) - { - pthread_detach(client_thread); - delete params; - params = NULL; - CleanClose(connection_fd); - } - } - else - { - usleep(2500); - } - } -} - -void* LinuxTcpSocketServer::GenerateResponse(void *p_data) -{ - pthread_detach(pthread_self()); - struct GenerateResponseParameters* params = reinterpret_cast(p_data); - LinuxTcpSocketServer *instance = params->instance; - int connection_fd = params->connection_fd; - delete params; - params = NULL; - int nbytes; - char buffer[BUFFER_SIZE]; - string request; - do - { //The client sends its json formatted request and a delimiter request. - nbytes = recv(connection_fd, buffer, BUFFER_SIZE, 0); - if(nbytes == -1) - { - instance->CleanClose(connection_fd); - return NULL; - } - else - { - request.append(buffer,nbytes); - } - } while(request.find(DELIMITER_CHAR) == string::npos); - instance->OnRequest(request, reinterpret_cast(connection_fd)); - return NULL; -} +void LinuxTcpSocketServer::HandleConnection(int connection) { + StreamReader reader(DEFAULT_BUFFER_SIZE); + string request, response; + reader.Read(request, connection, DEFAULT_DELIMITER_CHAR); -bool LinuxTcpSocketServer::WriteToSocket(const int& fd, const string& toWrite) -{ - bool fullyWritten = false; - bool errorOccured = false; - string toSend = toWrite; - do - { - ssize_t byteWritten = send(fd, toSend.c_str(), toSend.size(), 0); - if(byteWritten < 0) - { - errorOccured = true; - CleanClose(fd); - } - else if(static_cast(byteWritten) < toSend.size()) - { - int len = toSend.size() - byteWritten; - toSend = toSend.substr(byteWritten + sizeof(char), len); - } - else - fullyWritten = true; - } while(!fullyWritten && !errorOccured); + this->ProcessRequest(request, response); - return fullyWritten && !errorOccured; + response.append(1, DEFAULT_DELIMITER_CHAR); + StreamWriter writer; + writer.Write(response, connection); + CleanClose(connection); } -bool LinuxTcpSocketServer::WaitClientClose(const int& fd, const int &timeout) -{ - bool ret = false; - int i = 0; - while((recv(fd, NULL, 0, 0) != 0) && i < timeout) - { - usleep(1); - ++i; - ret = true; - } +bool LinuxTcpSocketServer::WaitClientClose(const int &fd, const int &timeout) { + bool ret = false; + int i = 0; + while ((recv(fd, NULL, 0, 0) != 0) && i < timeout) { + usleep(1); + ++i; + ret = true; + } - return ret; + return ret; } -int LinuxTcpSocketServer::CloseByReset(const int& fd) -{ - struct linger so_linger; - so_linger.l_onoff = 1; - so_linger.l_linger = 0; +int LinuxTcpSocketServer::CloseByReset(const int &fd) { + struct linger so_linger; + so_linger.l_onoff = 1; + so_linger.l_linger = 0; - int ret = setsockopt(fd, SOL_SOCKET, SO_LINGER, &so_linger, sizeof(so_linger)); - if(ret != 0) - return ret; + int ret = + setsockopt(fd, SOL_SOCKET, SO_LINGER, &so_linger, sizeof(so_linger)); + if (ret != 0) + return ret; - return close(fd); + return close(fd); } -int LinuxTcpSocketServer::CleanClose(const int& fd) -{ - if(WaitClientClose(fd)) - { - return close(fd); - } - else - { - return CloseByReset(fd); - } +int LinuxTcpSocketServer::CleanClose(const int &fd) { + if (WaitClientClose(fd)) { + return close(fd); + } else { + return CloseByReset(fd); + } } diff --git a/src/jsonrpccpp/server/connectors/linuxtcpsocketserver.h b/src/jsonrpccpp/server/connectors/linuxtcpsocketserver.h index d9461d0b..9a03f34b 100644 --- a/src/jsonrpccpp/server/connectors/linuxtcpsocketserver.h +++ b/src/jsonrpccpp/server/connectors/linuxtcpsocketserver.h @@ -10,135 +10,84 @@ #ifndef JSONRPC_CPP_LINUXTCPSOCKETSERVERCONNECTOR_H_ #define JSONRPC_CPP_LINUXTCPSOCKETSERVERCONNECTOR_H_ +#include +#include +#include #include #include +#include +#include #include #include -#include -#include -#include -#include -#include -#include "../abstractserverconnector.h" +#include "../abstractthreadedserver.h" -namespace jsonrpc -{ - /** - * This class is the Linux/UNIX implementation of TCPSocketServer. - * It uses the POSIX socket API and POSIX thread API to performs its job. - * Each client request is handled in a new thread. - */ - class LinuxTcpSocketServer: public AbstractServerConnector - { - public: - /** - * @brief LinuxTcpSocketServer, constructor of the Linux/UNIX implementation of class TcpSocketServer - * @param ipToBind The ipv4 address on which the server should bind and listen - * @param port The port on which the server should bind and listen - */ - LinuxTcpSocketServer(const std::string& ipToBind, const unsigned int &port); - /** - * @brief The real implementation TcpSocketServer::StartListening method. - * - * This method launches the listening loop that will handle client connections. - * The return value depends on the current listening states : - * - not listening and no error come up while bind and listen returns true - * - not listening but error happen on bind or listen returns false - * - is called while listening returns false - * - * @return A boolean that indicates the success or the failure of the operation. - */ - bool StartListening(); - /** - * @brief The real implementation TcpSocketServer::StopListening method. - * - * This method stops the listening loop that will handle client connections. - * The return value depends on the current listening states : - * - listening and successfuly stops the listen loop returns true - * - is called while not listening returns false - * - * @return A boolean that indicates the success or the failure of the operation. - */ - bool StopListening(); +namespace jsonrpc { +/** + * This class is the Linux/UNIX implementation of TCPSocketServer. + * It uses the POSIX socket API and POSIX thread API to performs its job. + * Each client request is handled in a new thread. + */ +class LinuxTcpSocketServer : public AbstractThreadedServer { +public: + /** + * @brief LinuxTcpSocketServer, constructor of the Linux/UNIX + * implementation of class TcpSocketServer + * @param ipToBind The ipv4 address on which the server should + * bind and listen + * @param port The port on which the server should bind and listen + */ + LinuxTcpSocketServer(const std::string &ipToBind, const unsigned int &port, + size_t threads = 1); - /** - * @brief The real implementation TcpSocketServer::SendResponse method. - * - * This method sends the result of the RPC Call over the tcp socket that the client has used to perform its request. - * @param response The response to send to the client - * @param addInfo Additionnal parameters (mainly client socket file descriptor) - * @return A boolean that indicates the success or the failure of the operation. - */ - bool SendResponse(const std::string& response, void* addInfo = NULL); + virtual ~LinuxTcpSocketServer(); - private: - bool running; /*!< A boolean that is used to know the listening state*/ - std::string ipToBind; /*!< The ipv4 address on which the server should bind and listen*/ - unsigned int port; /*!< The port on which the server should bind and listen*/ - int socket_fd; /*!< The file descriptior of the listening socket*/ - struct sockaddr_in address; /*!< The listening socket*/ + virtual bool InitializeListener(); + virtual int CheckForConnection(); + virtual void HandleConnection(int connection); - pthread_t listenning_thread; /*!< The identifier of the listen loop thread*/ +private: + std::string ipToBind; + unsigned int port; + int socket_fd; + struct sockaddr_in address; - /** - * @brief The static method that is used as listening thread entry point - * @param p_data The parameters for the thread entry point method - */ - static void* LaunchLoop(void *p_data); - /** - * @brief The method that launches the listenning loop - */ - void ListenLoop(); - struct GenerateResponseParameters - { - LinuxTcpSocketServer *instance; - int connection_fd; - }; /*!< The structure used to give parameters to the Response generating method*/ - /** - * @brief The static method that is used as client request handling entry point - * @param p_data The parameters for the thread entry point method - */ - static void* GenerateResponse(void *p_data); - /** - * @brief A method that write a message to socket - * - * Tries to send the full message. - * @param fd The file descriptor of the socket message should be sent - * @param toSend The message to send over socket - * @returns A boolean indicating the success or the failure of the operation - */ - bool WriteToSocket(const int& fd, const std::string& toSend); - /** - * @brief A method that wait for the client to close the tcp session - * - * This method wait for the client to close the tcp session in order to avoid the server to enter in TIME_WAIT status. - * Entering in TIME_WAIT status with too many clients may occur in a DOS attack - * since server will not be able to use a new socket when a new client connects. - * @param fd The file descriptor of the socket that should be closed by the client - * @param timeout The maximum time the server will wait for the client to close the tcp session in microseconds. - * @returns A boolean indicating the success or the failure of the operation - */ - bool WaitClientClose(const int& fd, const int &timeout = 100000); - /** - * @brief A method that close a socket by reseting it - * - * This method reset the tcp session in order to avoid enter in TIME_WAIT state. - * @param fd The file descriptor of the socket that should be reset - * @returns The return value of POSIX close() method - */ - int CloseByReset(const int& fd); - /** - * @brief A method that cleanly close a socket by avoid TIME_WAIT state - * - * This method uses WaitClientClose and ClodeByReset to clenly close a tcp session with a client - * (avoiding TIME_WAIT to avoid DOS attacks). - * @param fd The file descriptor of the socket that should be cleanly closed - * @returns The return value of POSIX close() method - */ - int CleanClose(const int& fd); - }; + /** + * @brief A method that wait for the client to close the tcp session + * + * This method wait for the client to close the tcp session in order to avoid + * the server to enter in TIME_WAIT status. + * Entering in TIME_WAIT status with too many clients may occur in a DOS + * attack + * since server will not be able to use a new socket when a new client + * connects. + * @param fd The file descriptor of the socket that should be closed by the + * client + * @param timeout The maximum time the server will wait for the client to + * close the tcp session in microseconds. + * @returns A boolean indicating the success or the failure of the operation + */ + bool WaitClientClose(const int &fd, const int &timeout = 100000); + /** + * @brief A method that close a socket by reseting it + * + * This method reset the tcp session in order to avoid enter in TIME_WAIT + * state. + * @param fd The file descriptor of the socket that should be reset + * @returns The return value of POSIX close() method + */ + int CloseByReset(const int &fd); + /** + * @brief A method that cleanly close a socket by avoid TIME_WAIT state + * + * This method uses WaitClientClose and ClodeByReset to clenly close a tcp + * session with a client + * (avoiding TIME_WAIT to avoid DOS attacks). + * @param fd The file descriptor of the socket that should be cleanly closed + * @returns The return value of POSIX close() method + */ + int CleanClose(const int &fd); +}; } /* namespace jsonrpc */ #endif /* JSONRPC_CPP_LINUXTCPSOCKETSERVERCONNECTOR_H_ */ - diff --git a/src/jsonrpccpp/server/connectors/redisserver.cpp b/src/jsonrpccpp/server/connectors/redisserver.cpp new file mode 100644 index 00000000..9ebed215 --- /dev/null +++ b/src/jsonrpccpp/server/connectors/redisserver.cpp @@ -0,0 +1,149 @@ +/************************************************************************* + * libjson-rpc-cpp + ************************************************************************* + * @file redisserver.cpp + * @date 15.08.2017 + * @author Jacques Software + * @license See attached LICENSE.txt + ************************************************************************/ + +#include "redisserver.h" + +using namespace jsonrpc; + +/** + * This is a helper method for the ListenLoop. Checks that the request is + * valid and then retusn the request string and the queue to return the + * response to. + * @param req Redis request that has been received. + * @param ret_queue The return queue is returned here. + * @param request The request is returned here. + * @return Returns true on success, false otherwise. + */ +bool ProcessRedisReply(redisReply *req, std::string &ret_queue, + std::string &request) { + + // The return from hiredis is strange in that it's always an array of + // length 2, with the first element the name of the key as a string, + // and the second element the actual element that we popped. + if (req->type != REDIS_REPLY_ARRAY) { + return false; + } + + if (req->elements != 2) { + return false; + } + + // It's the second element that we care about + redisReply *data = req->element[1]; + + // It should be a json string + if (data->type != REDIS_REPLY_STRING) { + return false; + } + + std::string json(data->str, data->str + data->len * sizeof data->str[0]); + + size_t pos = json.find("!"); + if (pos == std::string::npos) { + return false; + } + + ret_queue = json.substr(0, pos); + request = json.substr(pos + 1); + + return true; +} + +RedisServer::RedisServer(std::string host, int port, std::string queue) + : running(false), host(host), port(port), queue(queue), con(NULL) {} + +bool RedisServer::StartListening() { + if (this->running) { + return this->running; + } + + con = redisConnect(host.c_str(), port); + if (con == NULL) { + return false; + } + if (con->err != 0) { + redisFree(con); + con = NULL; + return false; + } + + this->running = true; + int ret = pthread_create(&(this->listenning_thread), NULL, + RedisServer::LaunchLoop, this); + this->running = static_cast(ret == 0); + + return this->running; +} + +bool RedisServer::StopListening() { + if (!this->running) { + return true; + } + this->running = false; + pthread_join(this->listenning_thread, NULL); + if (con != NULL) { + redisFree(con); + } + return !(this->running); +} + +bool RedisServer::SendResponse(const std::string &response, + const std::string &ret_queue) { + redisReply *ret; + ret = (redisReply *)redisCommand(con, "LPUSH %s %s", ret_queue.c_str(), + response.c_str()); + + if (ret == NULL) { + return false; + } + + if (ret->type != REDIS_REPLY_INTEGER || ret->integer <= 0) { + freeReplyObject(ret); + return false; + } + + freeReplyObject(ret); + return true; +} + +void *RedisServer::LaunchLoop(void *p_data) { + RedisServer *instance = reinterpret_cast(p_data); + ; + instance->ListenLoop(); + return NULL; +} + +void RedisServer::ListenLoop() { + while (this->running) { + redisReply *req = NULL; + std::string request; + req = (redisReply *)redisCommand(con, "BRPOP %s 1", queue.c_str()); + if (req == NULL) { + continue; + } + + if (req->type == REDIS_REPLY_NIL) { + freeReplyObject(req); + continue; + } + + std::string ret_queue; + bool ret = ProcessRedisReply(req, ret_queue, request); + freeReplyObject(req); + if (ret == false) { + continue; + } + + if (this->running) { + std::string response; + this->ProcessRequest(request, response); + this->SendResponse(response, ret_queue); + } + } +} diff --git a/src/jsonrpccpp/server/connectors/redisserver.h b/src/jsonrpccpp/server/connectors/redisserver.h new file mode 100644 index 00000000..61984946 --- /dev/null +++ b/src/jsonrpccpp/server/connectors/redisserver.h @@ -0,0 +1,117 @@ +/************************************************************************* + * libjson-rpc-cpp + ************************************************************************* + * @file redisserver.h + * @date 15.08.2017 + * @author Jacques Software + * @license See attached LICENSE.txt + ************************************************************************/ + +#ifndef JSONRPC_CPP_REDISSERVERCONNECTOR_H_ +#define JSONRPC_CPP_REDISSERVERCONNECTOR_H_ + +#include +#include +#include + +#include "../abstractserverconnector.h" + +namespace jsonrpc +{ + /** + * This class is the redis implementation of an AbstractServerConnector. + * It uses hiredis to connect to a designated redis server. + * + * The RedisServer watches a designated queue for incoming requests in + * ListenLoop by trying BRPOP. When it does receive a request it grabs the + * response/return queue name, processes the request and then uses LPUSH to + * send the result back to the client. + */ + class RedisServer: public AbstractServerConnector + { + public: + /** + * RedisServer + * @param host The ip address of the redis server. + * @param port The port of the redis server. + * @param queue The queue to listen on. + */ + RedisServer(std::string host, int port, std::string queue); + + + /** + * This method launches the listening loop that will handle client connections. + * @return true for success, false otherwise. + */ + bool StartListening(); + + + /** + * This method stops the listening loop that will handle client connections. + * @return True if successful, false otherwise or if not listening. + */ + bool StopListening(); + + + /** + * This method sends the result of the RPC Call back to the client + * @param response The response to send to the client. + * @param ret_queue The queue to send the response to. + * @return A boolean that indicates the success or the failure of the operation. + */ + bool SendResponse(const std::string& response, const std::string& ret_queue); + + private: + + /** + * Callback for listening thread to start ListenLoop. + * @param p_data A pointer to this object. + * @return Nothing. + */ + static void* LaunchLoop(void *p_data); + + + /** + * Main loop listening for connections. The loop pops requests off the + * servers' queue and processes them. It then calls this class's + * OnRequest method which handles the request and sends the response + * using this classes' SendResponse method. + */ + void ListenLoop(); + + private: + + /** + * @brief Keeps track of whether the server is running. + */ + bool running; + + /** + * @brief Our listening thread + */ + pthread_t listenning_thread; + + /** + * @brief Ip address of the redis server + */ + std::string host; + + /** + * @brief port of the redis server + */ + int port; + + /** + * @brief Queue that we are messaging. + */ + std::string queue; + + /** + * @brief Our connection to the redis server + */ + redisContext * con; + + }; +} + +#endif /* JSONRPC_CPP_REDISSERVERCONNECTOR_H_ */ diff --git a/src/jsonrpccpp/server/connectors/tcpsocketserver.cpp b/src/jsonrpccpp/server/connectors/tcpsocketserver.cpp index fbdf6fa6..d4853182 100644 --- a/src/jsonrpccpp/server/connectors/tcpsocketserver.cpp +++ b/src/jsonrpccpp/server/connectors/tcpsocketserver.cpp @@ -10,7 +10,7 @@ #include "tcpsocketserver.h" #ifdef _WIN32 #include "windowstcpsocketserver.h" -#elif __unix__ +#else #include "linuxtcpsocketserver.h" #endif #include @@ -18,50 +18,34 @@ using namespace jsonrpc; using namespace std; -TcpSocketServer::TcpSocketServer(const std::string& ipToBind, const unsigned int &port) : - AbstractServerConnector() -{ +TcpSocketServer::TcpSocketServer(const std::string &ipToBind, + const unsigned int &port) + : AbstractServerConnector() { #ifdef _WIN32 - this->realSocket = new WindowsTcpSocketServer(ipToBind, port); -#elif __unix__ - this->realSocket = new LinuxTcpSocketServer(ipToBind, port); + this->realSocket = new WindowsTcpSocketServer(ipToBind, port); #else - this->realSocket = NULL; + this->realSocket = new LinuxTcpSocketServer(ipToBind, port); #endif } -TcpSocketServer::~TcpSocketServer() -{ - if(this->realSocket != NULL) - { - delete this->realSocket; - this->realSocket = NULL; - } -} - -bool TcpSocketServer::StartListening() -{ - if(this->realSocket != NULL) - { - this->realSocket->SetHandler(this->GetHandler()); - return this->realSocket->StartListening(); - } - else - return false; +TcpSocketServer::~TcpSocketServer() { + if (this->realSocket != NULL) { + delete this->realSocket; + this->realSocket = NULL; + } } -bool TcpSocketServer::StopListening() -{ - if(this->realSocket != NULL) - return this->realSocket->StopListening(); - else - return false; +bool TcpSocketServer::StartListening() { + if (this->realSocket != NULL) { + this->realSocket->SetHandler(this->GetHandler()); + return this->realSocket->StartListening(); + } else + return false; } -bool TcpSocketServer::SendResponse(const string& response, void* addInfo) -{ - if(this->realSocket != NULL) - return this->realSocket->SendResponse(response, addInfo); - else - return false; +bool TcpSocketServer::StopListening() { + if (this->realSocket != NULL) + return this->realSocket->StopListening(); + else + return false; } diff --git a/src/jsonrpccpp/server/connectors/tcpsocketserver.h b/src/jsonrpccpp/server/connectors/tcpsocketserver.h index 2c9c69f7..5b9205d1 100644 --- a/src/jsonrpccpp/server/connectors/tcpsocketserver.h +++ b/src/jsonrpccpp/server/connectors/tcpsocketserver.h @@ -12,69 +12,64 @@ #include "../abstractserverconnector.h" -namespace jsonrpc -{ - /** - * This class provides an embedded TCP Socket Server that handle incoming Requests and send result over same socket. - * It uses the delimiter character to distinct a full RPC Message over the tcp flow. This character is parametered on - * compilation time in implementation files. The default value for this delimiter is 0x0A a.k.a. "new line". - * This class hides OS specific features in real implementation of this server. Currently it has implementation for - * both Linux and Windows. - */ - class TcpSocketServer: public AbstractServerConnector - { - public: - /** - * @brief TcpSocketServer, constructor for the included TcpSocketServer - * - * Instanciates the real implementation of TcpSocketServerPrivate depending on running OS. - * - * @param ipToBind The ipv4 address on which the server should bind and listen - * @param port The port on which the server should bind and listen - */ - TcpSocketServer(const std::string& ipToBind, const unsigned int &port); - /** - * @brief ~TcpSocketServer, the destructor of TcpSocketServer - */ - ~TcpSocketServer(); - /** - * @brief The AbstractServerConnector::StartListening method overload. - * - * This method launches the listening loop that will handle client connections. - * The return value depends on the current listening states : - * - not listening and no error come up while bind and listen returns true - * - not listening but error happen on bind or listen returns false - * - is called while listening returns false - * - * @return A boolean that indicates the success or the failure of the operation. - */ - bool StartListening(); - /** - * @brief The AbstractServerConnector::StopListening method overload. - * - * This method stops the listening loop that will handle client connections. - * The return value depends on the current listening states : - * - listening and successfuly stops the listen loop returns true - * - is called while not listening returns false - * - * @return A boolean that indicates the success or the failure of the operation. - */ - bool StopListening(); - - /** - * @brief The AbstractServerConnector::SendResponse method overload. - * - * This method sends the result of the RPC Call over the tcp socket that the client has used to perform its request. - * @param response The response to send to the client - * @param addInfo Additionnal parameters (mainly client socket file descriptor) - * @return A boolean that indicates the success or the failure of the operation. - */ - bool SendResponse(const std::string& response, void* addInfo = NULL); +namespace jsonrpc { +/** + * This class provides an embedded TCP Socket Server that handle incoming + * Requests and send result over same socket. + * It uses the delimiter character to distinct a full RPC Message over the + * tcp flow. This character is parametered on + * compilation time in implementation files. The default value for this + * delimiter is 0x0A a.k.a. "new line". + * This class hides OS specific features in real implementation of this + * server. Currently it has implementation for + * both Linux and Windows. + */ +class TcpSocketServer : public AbstractServerConnector { +public: + /** + * @brief TcpSocketServer, constructor for the included + * TcpSocketServer + * + * Instanciates the real implementation of TcpSocketServerPrivate + * depending on running OS. + * + * @param ipToBind The ipv4 address on which the server should + * bind and listen + * @param port The port on which the server should bind and listen + */ + TcpSocketServer(const std::string &ipToBind, const unsigned int &port); - private: - AbstractServerConnector *realSocket; /*!< A pointer to the real implementation of this class depending of running OS*/ - }; + ~TcpSocketServer(); + /** + * @brief The AbstractServerConnector::StartListening method overload. + * + * This method launches the listening loop that will handle client + * connections. + * The return value depends on the current listening states : + * - not listening and no error come up while bind and listen returns true + * - not listening but error happen on bind or listen returns false + * - is called while listening returns false + * + * @return A boolean that indicates the success or the failure of the + * operation. + */ + bool StartListening(); + /** + * @brief The AbstractServerConnector::StopListening method overload. + * + * This method stops the listening loop that will handle client connections. + * The return value depends on the current listening states : + * - listening and successfuly stops the listen loop returns true + * - is called while not listening returns false + * + * @return A boolean that indicates the success or the failure of the + * operation. + */ + bool StopListening(); + +private: + AbstractServerConnector *realSocket; +}; } /* namespace jsonrpc */ #endif /* JSONRPC_CPP_TCPSOCKETSERVERCONNECTOR_H_ */ - diff --git a/src/jsonrpccpp/server/connectors/unixdomainsocketserver.cpp b/src/jsonrpccpp/server/connectors/unixdomainsocketserver.cpp index e63c49e8..dcbf34bf 100644 --- a/src/jsonrpccpp/server/connectors/unixdomainsocketserver.cpp +++ b/src/jsonrpccpp/server/connectors/unixdomainsocketserver.cpp @@ -8,200 +8,79 @@ ************************************************************************/ #include "unixdomainsocketserver.h" +#include "../../common/sharedconstants.h" +#include +#include #include -#include +#include +#include #include +#include #include -#include -#include -#include #include -#include using namespace jsonrpc; using namespace std; -#define BUFFER_SIZE 1024 -#define PATH_MAX 108 -#ifndef DELIMITER_CHAR -#define DELIMITER_CHAR char(0x0A) -#endif - -UnixDomainSocketServer::UnixDomainSocketServer(const string &socket_path) : - running(false), - socket_path(socket_path.substr(0, PATH_MAX)) -{ +UnixDomainSocketServer::UnixDomainSocketServer(const string &socket_path, + size_t threads) + : AbstractThreadedServer(threads), socket_path(socket_path), socket_fd(-1) { } -bool UnixDomainSocketServer::StartListening() -{ - if(!this->running) - { - //Create and bind socket here. - //Then launch the listenning loop. - if (access(this->socket_path.c_str(), F_OK) != -1) - return false; - - this->socket_fd = socket(AF_UNIX, SOCK_STREAM, 0); - - if(this->socket_fd < 0) - { - return false; - } - - unlink(this->socket_path.c_str()); - - fcntl(this->socket_fd, F_SETFL, FNDELAY); - - /* start with a clean address structure */ - memset(&(this->address), 0, sizeof(struct sockaddr_un)); - - this->address.sun_family = AF_UNIX; - snprintf(this->address.sun_path, PATH_MAX, "%s", this->socket_path.c_str()); - - if(bind(this->socket_fd, reinterpret_cast(&(this->address)), sizeof(struct sockaddr_un)) != 0) - { - return false; - } - - if(listen(this->socket_fd, 5) != 0) - { - return false; - } - - //Launch listening loop there - this->running = true; - int ret = pthread_create(&(this->listenning_thread), NULL, UnixDomainSocketServer::LaunchLoop, this); - if(ret != 0) - { - pthread_detach(this->listenning_thread); - } - this->running = static_cast(ret==0); - - return this->running; - } - else - { - return false; - } +UnixDomainSocketServer::~UnixDomainSocketServer() { + if (this->socket_fd != -1) + close(this->socket_fd); + unlink(this->socket_path.c_str()); } -bool UnixDomainSocketServer::StopListening() -{ - if(this->running) - { - this->running = false; - pthread_join(this->listenning_thread, NULL); - close(this->socket_fd); - unlink(this->socket_path.c_str()); - return !(this->running); - } - else - { - return false; - } -} +bool UnixDomainSocketServer::InitializeListener() { + if (access(this->socket_path.c_str(), F_OK) != -1) + return false; -bool UnixDomainSocketServer::SendResponse(const string& response, void* addInfo) -{ - bool result = false; - int connection_fd = reinterpret_cast(addInfo); - - string temp = response; - if(temp.find(DELIMITER_CHAR) == string::npos) - { - temp.append(1, DELIMITER_CHAR); - } - if(DELIMITER_CHAR != '\n') - { - char eot = DELIMITER_CHAR; - string toSend = temp.substr(0, toSend.find_last_of('\n')); - toSend += eot; - result = this->WriteToSocket(connection_fd, toSend); - } - else - { - result = this->WriteToSocket(connection_fd, temp); - } - close(connection_fd); - return result; -} + this->socket_fd = socket(AF_UNIX, SOCK_STREAM, 0); -void* UnixDomainSocketServer::LaunchLoop(void *p_data) -{ - pthread_detach(pthread_self()); - UnixDomainSocketServer *instance = reinterpret_cast(p_data);; - instance->ListenLoop(); - return NULL; -} + if (this->socket_fd < 0) { + return false; + } + + // Set to non-blocking mode + fcntl(this->socket_fd, F_SETFL, FNDELAY); -void UnixDomainSocketServer::ListenLoop() -{ - int connection_fd; - socklen_t address_length = sizeof(this->address); - while(this->running) - { - if((connection_fd = accept(this->socket_fd, reinterpret_cast(&(this->address)), &address_length)) > 0) - { - pthread_t client_thread; - struct ClientConnection *params = new struct ClientConnection(); - params->instance = this; - params->connection_fd = connection_fd; - int ret = pthread_create(&client_thread, NULL, UnixDomainSocketServer::GenerateResponse, params); - if(ret != 0) - { - pthread_detach(client_thread); - delete params; - params = NULL; - } - } - else - { - usleep(25000); - } - } + memset(&(this->address), 0, sizeof(struct sockaddr_un)); + this->address.sun_family = AF_UNIX; + strncpy(this->address.sun_path, this->socket_path.c_str(), 107); + + if (::bind(this->socket_fd, + reinterpret_cast(&(this->address)), + sizeof(struct sockaddr_un)) != 0) { + return false; + } + + if (listen(this->socket_fd, 5) != 0) { + return false; + } + return true; } -void* UnixDomainSocketServer::GenerateResponse(void *p_data) -{ - pthread_detach(pthread_self()); - struct ClientConnection* params = reinterpret_cast(p_data); - UnixDomainSocketServer *instance = params->instance; - int connection_fd = params->connection_fd; - delete params; - params = NULL; - int nbytes; - char buffer[BUFFER_SIZE]; - string request; - do - { //The client sends its json formatted request and a delimiter request. - nbytes = read(connection_fd, buffer, BUFFER_SIZE); - request.append(buffer,nbytes); - } while(request.find(DELIMITER_CHAR) == string::npos); - - instance->OnRequest(request, reinterpret_cast(connection_fd)); - return NULL; +int UnixDomainSocketServer::CheckForConnection() { + socklen_t address_length = sizeof(this->address); + + int fd; + fd = accept(this->socket_fd, + reinterpret_cast(&(this->address)), + &address_length); + return fd; } +void UnixDomainSocketServer::HandleConnection(int connection) { + string request, response; + StreamReader reader(DEFAULT_BUFFER_SIZE); + reader.Read(request, connection, DEFAULT_DELIMITER_CHAR); + this->ProcessRequest(request, response); + + response.append(1, DEFAULT_DELIMITER_CHAR); + StreamWriter writer; + writer.Write(response, connection); -bool UnixDomainSocketServer::WriteToSocket(int fd, const string& toWrite) -{ - bool fullyWritten = false; - bool errorOccured = false; - string toSend = toWrite; - do - { - ssize_t byteWritten = write(fd, toSend.c_str(), toSend.size()); - if(byteWritten < 0) - errorOccured = true; - else if(byteWritten < static_cast(toSend.size())) - { - int len = toSend.size() - byteWritten; - toSend = toSend.substr(byteWritten + sizeof(char), len); - } - else - fullyWritten = true; - } while(!fullyWritten && !errorOccured); - - return fullyWritten && !errorOccured; + close(connection); } diff --git a/src/jsonrpccpp/server/connectors/unixdomainsocketserver.h b/src/jsonrpccpp/server/connectors/unixdomainsocketserver.h index 2d2072b0..2ae9afb8 100644 --- a/src/jsonrpccpp/server/connectors/unixdomainsocketserver.h +++ b/src/jsonrpccpp/server/connectors/unixdomainsocketserver.h @@ -10,55 +10,43 @@ #ifndef JSONRPC_CPP_UNIXDOMAINSOCKETSERVERCONNECTOR_H_ #define JSONRPC_CPP_UNIXDOMAINSOCKETSERVERCONNECTOR_H_ +#include #include #include -#include -#include -#include #include +#include +#include #include -#include - -#include "../abstractserverconnector.h" - -namespace jsonrpc -{ - /** - * This class provides an embedded Unix Domain Socket Server,to handle incoming Requests. - */ - class UnixDomainSocketServer: public AbstractServerConnector - { - public: - /** - * @brief UnixDomainSocketServer, constructor for the included UnixDomainSocketServer - * @param socket_path, a string containing the path to the unix socket - */ - UnixDomainSocketServer(const std::string& socket_path); - - virtual bool StartListening(); - virtual bool StopListening(); - - bool virtual SendResponse(const std::string& response, void* addInfo = NULL); - - private: - bool running; - std::string socket_path; - int socket_fd; - struct sockaddr_un address; - - pthread_t listenning_thread; +#include - static void* LaunchLoop(void *p_data); - void ListenLoop(); - struct ClientConnection - { - UnixDomainSocketServer *instance; - int connection_fd; - }; - static void* GenerateResponse(void *p_data); - bool WriteToSocket(int fd, const std::string& toSend); - }; +#include "../../common/streamreader.h" +#include "../../common/streamwriter.h" +#include "../abstractthreadedserver.h" + +namespace jsonrpc { +/** + * This class provides an embedded Unix Domain Socket Server,to handle incoming + * Requests. + */ +class UnixDomainSocketServer : public AbstractThreadedServer { +public: + /** + * @brief UnixDomainSocketServer, constructor for the included + * UnixDomainSocketServer + * @param socket_path, a string containing the path to the unix socket + */ + UnixDomainSocketServer(const std::string &socket_path, size_t threads = 1); + virtual ~UnixDomainSocketServer(); + + virtual bool InitializeListener(); + virtual int CheckForConnection(); + virtual void HandleConnection(int connection); + +private: + std::string socket_path; + int socket_fd; + struct sockaddr_un address; +}; } /* namespace jsonrpc */ #endif /* JSONRPC_CPP_HTTPSERVERCONNECTOR_H_ */ - diff --git a/src/jsonrpccpp/server/connectors/windowstcpsocketserver.cpp b/src/jsonrpccpp/server/connectors/windowstcpsocketserver.cpp index 70e48714..47543055 100644 --- a/src/jsonrpccpp/server/connectors/windowstcpsocketserver.cpp +++ b/src/jsonrpccpp/server/connectors/windowstcpsocketserver.cpp @@ -9,13 +9,13 @@ #include "windowstcpsocketserver.h" -#include #include +#include #include #include -#include #include +#include #include #include @@ -26,267 +26,237 @@ using namespace std; #define BUFFER_SIZE 64 #ifndef DELIMITER_CHAR #define DELIMITER_CHAR char(0x0A) -#endif //DELIMITER_CHAR +#endif // DELIMITER_CHAR -WindowsTcpSocketServer::WindowsTcpSocketServer(const std::string& ipToBind, const unsigned int &port) : - AbstractServerConnector(), - ipToBind(ipToBind), - port(port), - running(false) -{ -} - -WindowsTcpSocketServer::~WindowsTcpSocketServer() -{ -} +WindowsTcpSocketServer::WindowsTcpSocketServer(const std::string &ipToBind, + const unsigned int &port) + : AbstractServerConnector(), ipToBind(ipToBind), port(port), + running(false) {} -bool WindowsTcpSocketServer::StartListening() -{ - if(!this->running) - { - //Create and bind socket here. - //Then launch the listenning loop. - this->socket_fd = socket(AF_INET, SOCK_STREAM, 0); - if(this->socket_fd < 0) - { - return false; - } - unsigned long nonBlocking = 1; - ioctlsocket(this->socket_fd, FIONBIO, &nonBlocking); //Set non blocking - int reuseaddr = 1; - setsockopt(this->socket_fd, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&reuseaddr), sizeof(reuseaddr)); +WindowsTcpSocketServer::~WindowsTcpSocketServer() {} - /* start with a clean address structure */ - memset(&(this->address), 0, sizeof(SOCKADDR_IN)); +bool WindowsTcpSocketServer::StartListening() { + if (!this->running) { + // Create and bind socket here. + // Then launch the listenning loop. + this->socket_fd = socket(AF_INET, SOCK_STREAM, 0); + if (this->socket_fd < 0) { + return false; + } + unsigned long nonBlocking = 1; + ioctlsocket(this->socket_fd, FIONBIO, &nonBlocking); // Set non blocking + int reuseaddr = 1; + setsockopt(this->socket_fd, SOL_SOCKET, SO_REUSEADDR, + reinterpret_cast(&reuseaddr), sizeof(reuseaddr)); - this->address.sin_family = AF_INET; - this->address.sin_addr.s_addr = inet_addr(this->ipToBind.c_str()); - this->address.sin_port = htons(this->port); + /* start with a clean address structure */ + memset(&(this->address), 0, sizeof(SOCKADDR_IN)); - if(::bind(this->socket_fd, reinterpret_cast(&(this->address)), sizeof(SOCKADDR_IN)) != 0) - { - return false; - } + this->address.sin_family = AF_INET; + this->address.sin_addr.s_addr = inet_addr(this->ipToBind.c_str()); + this->address.sin_port = htons(this->port); - if(listen(this->socket_fd, 5) != 0) - { - return false; - } - //Launch listening loop there - this->running = true; - HANDLE ret = CreateThread(NULL, 0, reinterpret_cast(&(WindowsTcpSocketServer::LaunchLoop)), reinterpret_cast(this), 0, &(this->listenning_thread)); - if(ret == NULL) - { - ExitProcess(3); - } - else - { - CloseHandle(ret); - } - this->running = static_cast(ret!=NULL); - return this->running; - } - else - { - return false; - } + if (::bind(this->socket_fd, reinterpret_cast(&(this->address)), + sizeof(SOCKADDR_IN)) != 0) { + return false; + } + if (listen(this->socket_fd, 5) != 0) { + return false; + } + // Launch listening loop there + this->running = true; + HANDLE ret = CreateThread(NULL, 0, + reinterpret_cast( + &(WindowsTcpSocketServer::LaunchLoop)), + reinterpret_cast(this), 0, + &(this->listenning_thread)); + if (ret == NULL) { + ExitProcess(3); + } else { + CloseHandle(ret); + } + this->running = static_cast(ret != NULL); + return this->running; + } else { + return false; + } } -bool WindowsTcpSocketServer::StopListening() -{ - if(this->running) - { - this->running = false; - WaitForSingleObject(OpenThread(THREAD_ALL_ACCESS, FALSE,this->listenning_thread), INFINITE); - closesocket(this->socket_fd); - return !(this->running); - } - else - { - return false; - } +bool WindowsTcpSocketServer::StopListening() { + if (this->running) { + this->running = false; + WaitForSingleObject( + OpenThread(THREAD_ALL_ACCESS, FALSE, this->listenning_thread), + INFINITE); + closesocket(this->socket_fd); + return !(this->running); + } else { + return false; + } } -bool WindowsTcpSocketServer::SendResponse(const string& response, void* addInfo) -{ - bool result = false; - int connection_fd = reinterpret_cast(addInfo); +bool WindowsTcpSocketServer::SendResponse(const string &response, + void *addInfo) { + bool result = false; + int connection_fd = reinterpret_cast(addInfo); - string temp = response; - if(temp.find(DELIMITER_CHAR) == string::npos) - { - temp.append(1, DELIMITER_CHAR); - } - if(DELIMITER_CHAR != '\n') - { - char eot = DELIMITER_CHAR; - string toSend = temp.substr(0, toSend.find_last_of('\n')); - toSend += eot; - result = this->WriteToSocket(connection_fd, toSend); - } - else - { - result = this->WriteToSocket(connection_fd, temp); - } - CleanClose(connection_fd); - return result; + string temp = response; + if (temp.find(DELIMITER_CHAR) == string::npos) { + temp.append(1, DELIMITER_CHAR); + } + if (DELIMITER_CHAR != '\n') { + char eot = DELIMITER_CHAR; + string toSend = temp.substr(0, toSend.find_last_of('\n')); + toSend += eot; + result = this->WriteToSocket(connection_fd, toSend); + } else { + result = this->WriteToSocket(connection_fd, temp); + } + CleanClose(connection_fd); + return result; } -DWORD WINAPI WindowsTcpSocketServer::LaunchLoop(LPVOID lp_data) -{ - WindowsTcpSocketServer *instance = reinterpret_cast(lp_data);; - instance->ListenLoop(); - CloseHandle(GetCurrentThread()); - return 0; //DO NOT USE ExitThread function here! ExitThread does not call destructors for allocated objects and therefore it would lead to a memory leak. +DWORD WINAPI WindowsTcpSocketServer::LaunchLoop(LPVOID lp_data) { + WindowsTcpSocketServer *instance = + reinterpret_cast(lp_data); + ; + instance->ListenLoop(); + CloseHandle(GetCurrentThread()); + return 0; // DO NOT USE ExitThread function here! ExitThread does not call + // destructors for allocated objects and therefore it would lead to + // a memory leak. } -void WindowsTcpSocketServer::ListenLoop() -{ - while(this->running) - { - SOCKET connection_fd = INVALID_SOCKET; - SOCKADDR_IN connection_address; - memset(&connection_address, 0, sizeof(SOCKADDR_IN)); - int address_length = sizeof(connection_address); - if((connection_fd = accept(this->socket_fd, reinterpret_cast(&connection_address), &address_length)) != INVALID_SOCKET) - { - unsigned long nonBlocking = 0; - ioctlsocket(connection_fd, FIONBIO, &nonBlocking); //Set blocking - DWORD client_thread; - struct GenerateResponseParameters *params = new struct GenerateResponseParameters(); - params->instance = this; - params->connection_fd = connection_fd; - HANDLE ret = CreateThread(NULL, 0, reinterpret_cast(&(WindowsTcpSocketServer::GenerateResponse)), reinterpret_cast(params), 0, &client_thread); - if(ret == NULL) - { - delete params; - params = NULL; - CleanClose(connection_fd); - } - else - { - CloseHandle(ret); - } - } - else - { - Sleep(2.5); - } - } +void WindowsTcpSocketServer::ListenLoop() { + while (this->running) { + SOCKET connection_fd = INVALID_SOCKET; + SOCKADDR_IN connection_address; + memset(&connection_address, 0, sizeof(SOCKADDR_IN)); + int address_length = sizeof(connection_address); + if ((connection_fd = accept( + this->socket_fd, reinterpret_cast(&connection_address), + &address_length)) != INVALID_SOCKET) { + unsigned long nonBlocking = 0; + ioctlsocket(connection_fd, FIONBIO, &nonBlocking); // Set blocking + DWORD client_thread; + struct GenerateResponseParameters *params = + new struct GenerateResponseParameters(); + params->instance = this; + params->connection_fd = connection_fd; + HANDLE ret = + CreateThread(NULL, 0, + reinterpret_cast( + &(WindowsTcpSocketServer::GenerateResponse)), + reinterpret_cast(params), 0, &client_thread); + if (ret == NULL) { + delete params; + params = NULL; + CleanClose(connection_fd); + } else { + CloseHandle(ret); + } + } else { + Sleep(2.5); + } + } } -DWORD WINAPI WindowsTcpSocketServer::GenerateResponse(LPVOID lp_data) -{ - struct GenerateResponseParameters* params = reinterpret_cast(lp_data); - WindowsTcpSocketServer *instance = params->instance; - int connection_fd = params->connection_fd; - delete params; - params = NULL; - int nbytes = 0; - char buffer[BUFFER_SIZE]; - memset(&buffer, 0, BUFFER_SIZE); - string request = ""; - do - { //The client sends its json formatted request and a delimiter request. - nbytes = recv(connection_fd, buffer, BUFFER_SIZE, 0); - if(nbytes == -1) - { - instance->CleanClose(connection_fd); - } - else - { - request.append(buffer,nbytes); - } - } while(request.find(DELIMITER_CHAR) == string::npos); - instance->OnRequest(request, reinterpret_cast(connection_fd)); - CloseHandle(GetCurrentThread()); - return 0; //DO NOT USE ExitThread function here! ExitThread does not call destructors for allocated objects and therefore it would lead to a memory leak. +DWORD WINAPI WindowsTcpSocketServer::GenerateResponse(LPVOID lp_data) { + struct GenerateResponseParameters *params = + reinterpret_cast(lp_data); + WindowsTcpSocketServer *instance = params->instance; + int connection_fd = params->connection_fd; + delete params; + params = NULL; + int nbytes = 0; + char buffer[BUFFER_SIZE]; + memset(&buffer, 0, BUFFER_SIZE); + string request = ""; + do { // The client sends its json formatted request and a delimiter request. + nbytes = recv(connection_fd, buffer, BUFFER_SIZE, 0); + if (nbytes == -1) { + instance->CleanClose(connection_fd); + } else { + request.append(buffer, nbytes); + } + } while (request.find(DELIMITER_CHAR) == string::npos); + instance->OnRequest(request, reinterpret_cast(connection_fd)); + CloseHandle(GetCurrentThread()); + return 0; // DO NOT USE ExitThread function here! ExitThread does not call + // destructors for allocated objects and therefore it would lead to + // a memory leak. } -bool WindowsTcpSocketServer::WriteToSocket(const SOCKET& fd, const string& toWrite) -{ - bool fullyWritten = false; - bool errorOccured = false; - string toSend = toWrite; - do - { - unsigned long byteWritten = send(fd, toSend.c_str(), toSend.size(), 0); - if(byteWritten < 0) - { - errorOccured = true; - CleanClose(fd); - } - else if(byteWritten < toSend.size()) - { - int len = toSend.size() - byteWritten; - toSend = toSend.substr(byteWritten + sizeof(char), len); - } - else - fullyWritten = true; - } while(!fullyWritten && !errorOccured); - - return fullyWritten && !errorOccured; +bool WindowsTcpSocketServer::WriteToSocket(const SOCKET &fd, + const string &toWrite) { + bool fullyWritten = false; + bool errorOccured = false; + string toSend = toWrite; + do { + unsigned long byteWritten = send(fd, toSend.c_str(), toSend.size(), 0); + if (byteWritten < 0) { + errorOccured = true; + CleanClose(fd); + } else if (byteWritten < toSend.size()) { + int len = toSend.size() - byteWritten; + toSend = toSend.substr(byteWritten + sizeof(char), len); + } else + fullyWritten = true; + } while (!fullyWritten && !errorOccured); + return fullyWritten && !errorOccured; } -bool WindowsTcpSocketServer::WaitClientClose(const SOCKET& fd, const int &timeout) -{ - bool ret = false; - int i = 0; - while((recv(fd, NULL, 0, 0) != 0) && i < timeout) - { - Sleep(1); - ++i; - ret = true; - } +bool WindowsTcpSocketServer::WaitClientClose(const SOCKET &fd, + const int &timeout) { + bool ret = false; + int i = 0; + while ((recv(fd, NULL, 0, 0) != 0) && i < timeout) { + Sleep(1); + ++i; + ret = true; + } - return ret; + return ret; } -int WindowsTcpSocketServer::CloseByReset(const SOCKET& fd) -{ - struct linger so_linger; - so_linger.l_onoff = 1; - so_linger.l_linger = 0; +int WindowsTcpSocketServer::CloseByReset(const SOCKET &fd) { + struct linger so_linger; + so_linger.l_onoff = 1; + so_linger.l_linger = 0; - int ret = setsockopt(fd, SOL_SOCKET, SO_LINGER, reinterpret_cast(&so_linger), sizeof(so_linger)); - if(ret != 0) - return ret; + int ret = setsockopt(fd, SOL_SOCKET, SO_LINGER, + reinterpret_cast(&so_linger), sizeof(so_linger)); + if (ret != 0) + return ret; - return closesocket(fd); + return closesocket(fd); } -int WindowsTcpSocketServer::CleanClose(const SOCKET& fd) -{ - if(WaitClientClose(fd)) - { - return closesocket(fd); - } - else - { - return CloseByReset(fd); - } +int WindowsTcpSocketServer::CleanClose(const SOCKET &fd) { + if (WaitClientClose(fd)) { + return closesocket(fd); + } else { + return CloseByReset(fd); + } } -//This is inspired from SFML to manage Winsock initialization. Thanks to them! ( http://www.sfml-dev.org/ ). -struct ServerSocketInitializer -{ - ServerSocketInitializer() - { - WSADATA init; - if(WSAStartup(MAKEWORD(2, 2), &init) != 0) - { - JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, "An issue occured while WSAStartup executed."); - } - } +// This is inspired from SFML to manage Winsock initialization. Thanks to them! +// ( http://www.sfml-dev.org/ ). +struct ServerSocketInitializer { + ServerSocketInitializer() { + WSADATA init; + if (WSAStartup(MAKEWORD(2, 2), &init) != 0) { + JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, + "An issue occured while WSAStartup executed."); + } + } - ~ServerSocketInitializer() - { - if(WSACleanup() != 0) - { - cerr << "An issue occured while WSAClean executed." << endl; - } - } + ~ServerSocketInitializer() { + if (WSACleanup() != 0) { + cerr << "An issue occured while WSAClean executed." << endl; + } + } }; struct ServerSocketInitializer serverGlobalInitializer; diff --git a/src/jsonrpccpp/server/connectors/windowstcpsocketserver.h b/src/jsonrpccpp/server/connectors/windowstcpsocketserver.h index 88f0e80e..6bb8c75c 100644 --- a/src/jsonrpccpp/server/connectors/windowstcpsocketserver.h +++ b/src/jsonrpccpp/server/connectors/windowstcpsocketserver.h @@ -18,125 +18,124 @@ namespace jsonrpc { - /** + /** * This class is the Windows implementation of TCPSocketServer. * It uses the Winsock2 socket API and Windows thread API to performs its job. * Each client request is handled in a new thread. */ class WindowsTcpSocketServer: public AbstractServerConnector - { - public: - /** + { + public: + /** * @brief WindowsTcpSocketServer, constructor of the Windows implementation of class TcpSocketServer * @param ipToBind The ipv4 address on which the server should bind and listen * @param port The port on which the server should bind and listen */ - WindowsTcpSocketServer(const std::string& ipToBind, const unsigned int &port); + WindowsTcpSocketServer(const std::string& ipToBind, const unsigned int &port); /** * @brief ~WindowsTcpSocketServer, the destructor of WindowsTcpSocketServer */ - ~WindowsTcpSocketServer(); + ~WindowsTcpSocketServer(); /** * @brief The real implementation TcpSocketServer::StartListening method. - * - * This method launches the listening loop that will handle client connections. + * + * This method launches the listening loop that will handle client connections. * The return value depends on the current listening states : * - not listening and no error come up while bind and listen returns true * - not listening but error happen on bind or listen returns false * - is called while listening returns false - * + * * @return A boolean that indicates the success or the failure of the operation. */ - bool StartListening(); + bool StartListening(); /** * @brief The real implementation TcpSocketServer::StopListening method. - * - * This method stops the listening loop that will handle client connections. + * + * This method stops the listening loop that will handle client connections. * The return value depends on the current listening states : * - listening and successfuly stops the listen loop returns true * - is called while not listening returns false - * + * * @return A boolean that indicates the success or the failure of the operation. */ - bool StopListening(); + bool StopListening(); /** * @brief The real implementation TcpSocketServer::SendResponse method. - * + * * This method sends the result of the RPC Call over the tcp socket that the client has used to perform its request. * @param response The response to send to the client * @param addInfo Additionnal parameters (mainly client socket file descriptor) * @return A boolean that indicates the success or the failure of the operation. */ - bool SendResponse(const std::string& response, void* addInfo = NULL); + bool SendResponse(const std::string& response, void* addInfo = NULL); - private: - bool running; /*!< A boolean that is used to know the listening state*/ - std::string ipToBind; /*!< The ipv4 address on which the server should bind and listen*/ - unsigned int port; /*!< The port on which the server should bind and listen*/ - SOCKET socket_fd; /*!< The file descriptior of the listening socket*/ - SOCKADDR_IN address; /*!< The listening socket*/ + private: + bool running; /*!< A boolean that is used to know the listening state*/ + std::string ipToBind; /*!< The ipv4 address on which the server should bind and listen*/ + unsigned int port; /*!< The port on which the server should bind and listen*/ + SOCKET socket_fd; /*!< The file descriptior of the listening socket*/ + SOCKADDR_IN address; /*!< The listening socket*/ - DWORD listenning_thread; /*!< The identifier of the listen loop thread*/ + DWORD listenning_thread; /*!< The identifier of the listen loop thread*/ /** * @brief The static method that is used as listening thread entry point * @param lp_data The parameters for the thread entry point method */ - static DWORD WINAPI LaunchLoop(LPVOID lp_data); + static DWORD WINAPI LaunchLoop(LPVOID lp_data); /** * @brief The method that launches the listenning loop */ - void ListenLoop(); - struct GenerateResponseParameters - { - WindowsTcpSocketServer *instance; - SOCKET connection_fd; - }; /*!< The structure used to give parameters to the Response generating method*/ - /** + void ListenLoop(); + struct GenerateResponseParameters + { + WindowsTcpSocketServer *instance; + SOCKET connection_fd; + }; /*!< The structure used to give parameters to the Response generating method*/ + /** * @brief The static method that is used as client request handling entry point * @param lp_data The parameters for the thread entry point method */ static DWORD WINAPI GenerateResponse(LPVOID lp_data); /** * @brief A method that write a message to socket - * + * * Tries to send the full message. * @param fd The file descriptor of the socket message should be sent * @param toSend The message to send over socket * @returns A boolean indicating the success or the failure of the operation */ - bool WriteToSocket(const SOCKET& fd, const std::string& toSend); + bool WriteToSocket(const SOCKET& fd, const std::string& toSend); /** * @brief A method that wait for the client to close the tcp session - * + * * This method wait for the client to close the tcp session in order to avoid the server to enter in TIME_WAIT status. - * Entering in TIME_WAIT status with too many clients may occur in a DOS attack + * Entering in TIME_WAIT status with too many clients may occur in a DOS attack * since server will not be able to use a new socket when a new client connects. * @param fd The file descriptor of the socket that should be closed by the client * @param timeout The maximum time the server will wait for the client to close the tcp session in milliseconds. * @returns A boolean indicating the success or the failure of the operation */ - bool WaitClientClose(const SOCKET& fd, const int &timeout = 100); + bool WaitClientClose(const SOCKET& fd, const int &timeout = 100); /** * @brief A method that close a socket by reseting it - * + * * This method reset the tcp session in order to avoid enter in TIME_WAIT state. * @param fd The file descriptor of the socket that should be reset * @returns The return value of POSIX close() method */ - int CloseByReset(const SOCKET& fd); + int CloseByReset(const SOCKET& fd); /** * @brief A method that cleanly close a socket by avoid TIME_WAIT state - * + * * This method uses WaitClientClose and ClodeByReset to clenly close a tcp session with a client * (avoiding TIME_WAIT to avoid DOS attacks). * @param fd The file descriptor of the socket that should be cleanly closed * @returns The return value of POSIX close() method */ - int CleanClose(const SOCKET& fd); - }; + int CleanClose(const SOCKET& fd); + }; } /* namespace jsonrpc */ #endif /* JSONRPC_CPP_WINDOWSTCPSOCKETSERVERCONNECTOR_H_ */ - diff --git a/src/jsonrpccpp/server/requesthandlerfactory.cpp b/src/jsonrpccpp/server/requesthandlerfactory.cpp index dddee863..16c1b5b8 100644 --- a/src/jsonrpccpp/server/requesthandlerfactory.cpp +++ b/src/jsonrpccpp/server/requesthandlerfactory.cpp @@ -8,25 +8,25 @@ ************************************************************************/ #include "requesthandlerfactory.h" +#include "rpcprotocolserver12.h" #include "rpcprotocolserverv1.h" #include "rpcprotocolserverv2.h" -#include "rpcprotocolserver12.h" using namespace jsonrpc; -IProtocolHandler *RequestHandlerFactory::createProtocolHandler(serverVersion_t type, IProcedureInvokationHandler &handler) -{ - IProtocolHandler* result = NULL; - switch (type) { - case JSONRPC_SERVER_V1: - result = new RpcProtocolServerV1(handler); - break; - case JSONRPC_SERVER_V2: - result = new RpcProtocolServerV2(handler); - break; - case JSONRPC_SERVER_V1V2: - result = new RpcProtocolServer12(handler); - break; - } - return result; +IProtocolHandler *RequestHandlerFactory::createProtocolHandler( + serverVersion_t type, IProcedureInvokationHandler &handler) { + IProtocolHandler *result = NULL; + switch (type) { + case JSONRPC_SERVER_V1: + result = new RpcProtocolServerV1(handler); + break; + case JSONRPC_SERVER_V2: + result = new RpcProtocolServerV2(handler); + break; + case JSONRPC_SERVER_V1V2: + result = new RpcProtocolServer12(handler); + break; + } + return result; } diff --git a/src/jsonrpccpp/server/rpcprotocolserver12.cpp b/src/jsonrpccpp/server/rpcprotocolserver12.cpp index 04eb8cb9..4bf9e7a5 100644 --- a/src/jsonrpccpp/server/rpcprotocolserver12.cpp +++ b/src/jsonrpccpp/server/rpcprotocolserver12.cpp @@ -12,40 +12,36 @@ using namespace jsonrpc; -RpcProtocolServer12::RpcProtocolServer12(IProcedureInvokationHandler &handler) : - rpc1(handler), - rpc2(handler) -{ -} +RpcProtocolServer12::RpcProtocolServer12(IProcedureInvokationHandler &handler) + : rpc1(handler), rpc2(handler) {} -void RpcProtocolServer12::AddProcedure(const Procedure &procedure) -{ - this->rpc1.AddProcedure(procedure); - this->rpc2.AddProcedure(procedure); +void RpcProtocolServer12::AddProcedure(const Procedure &procedure) { + this->rpc1.AddProcedure(procedure); + this->rpc2.AddProcedure(procedure); } -void RpcProtocolServer12::HandleRequest(const std::string &request, std::string &retValue) -{ - Json::Reader reader; - Json::Value req; - Json::Value resp; - Json::FastWriter w; +void RpcProtocolServer12::HandleRequest(const std::string &request, + std::string &retValue) { + Json::Reader reader; + Json::Value req; + Json::Value resp; + Json::FastWriter w; - if (reader.parse(request, req, false)) - { - this->GetHandler(req).HandleJsonRequest(req, resp); - } - else - { - this->GetHandler(req).WrapError(Json::nullValue, Errors::ERROR_RPC_JSON_PARSE_ERROR, Errors::GetErrorMessage(Errors::ERROR_RPC_JSON_PARSE_ERROR), resp); - } - if (resp != Json::nullValue) - retValue = w.write(resp); + if (reader.parse(request, req, false)) { + this->GetHandler(req).HandleJsonRequest(req, resp); + } else { + this->GetHandler(req).WrapError( + Json::nullValue, Errors::ERROR_RPC_JSON_PARSE_ERROR, + Errors::GetErrorMessage(Errors::ERROR_RPC_JSON_PARSE_ERROR), resp); + } + if (resp != Json::nullValue) + retValue = w.write(resp); } -AbstractProtocolHandler &RpcProtocolServer12::GetHandler(const Json::Value &request) -{ - if (request.isArray() || (request.isObject() && request.isMember("jsonrpc") && request["jsonrpc"].asString() == "2.0")) - return rpc2; - return rpc1; +AbstractProtocolHandler & +RpcProtocolServer12::GetHandler(const Json::Value &request) { + if (request.isArray() || (request.isObject() && request.isMember("jsonrpc") && + request["jsonrpc"].asString() == "2.0")) + return rpc2; + return rpc1; } diff --git a/src/jsonrpccpp/server/rpcprotocolserverv1.cpp b/src/jsonrpccpp/server/rpcprotocolserverv1.cpp index 5ed0c66c..4ffa633d 100644 --- a/src/jsonrpccpp/server/rpcprotocolserverv1.cpp +++ b/src/jsonrpccpp/server/rpcprotocolserverv1.cpp @@ -8,87 +8,78 @@ ************************************************************************/ #include "rpcprotocolserverv1.h" -#include #include +#include using namespace jsonrpc; -RpcProtocolServerV1::RpcProtocolServerV1(IProcedureInvokationHandler &handler) : - AbstractProtocolHandler(handler) -{ -} +RpcProtocolServerV1::RpcProtocolServerV1(IProcedureInvokationHandler &handler) + : AbstractProtocolHandler(handler) {} -void RpcProtocolServerV1::HandleJsonRequest(const Json::Value &req, Json::Value &response) -{ - if (req.isObject()) - { - int error = this->ValidateRequest(req); - if (error == 0) - { - try - { - this->ProcessRequest(req, response); - } - catch (const JsonRpcException & exc) - { - this->WrapException(req, exc, response); - } - } - else - { - this->WrapError(req, error, Errors::GetErrorMessage(error), response); - } - } - else - { - this->WrapError(Json::nullValue, Errors::ERROR_RPC_INVALID_REQUEST, Errors::GetErrorMessage(Errors::ERROR_RPC_INVALID_REQUEST), response); +void RpcProtocolServerV1::HandleJsonRequest(const Json::Value &req, + Json::Value &response) { + if (req.isObject()) { + int error = this->ValidateRequest(req); + if (error == 0) { + try { + this->ProcessRequest(req, response); + } catch (const JsonRpcException &exc) { + this->WrapException(req, exc, response); + } + } else { + this->WrapError(req, error, Errors::GetErrorMessage(error), response); } + } else { + this->WrapError(Json::nullValue, Errors::ERROR_RPC_INVALID_REQUEST, + Errors::GetErrorMessage(Errors::ERROR_RPC_INVALID_REQUEST), + response); + } } -bool RpcProtocolServerV1::ValidateRequestFields(const Json::Value &request) -{ - if (!(request.isMember(KEY_REQUEST_METHODNAME) && request[KEY_REQUEST_METHODNAME].isString())) - return false; - if (!request.isMember(KEY_REQUEST_ID)) - return false; - if (!request.isMember(KEY_REQUEST_PARAMETERS)) - return false; - if (!( request[KEY_REQUEST_PARAMETERS].isArray() || request[KEY_REQUEST_PARAMETERS].isNull())) - return false; - return true; +bool RpcProtocolServerV1::ValidateRequestFields(const Json::Value &request) { + if (!(request.isMember(KEY_REQUEST_METHODNAME) && + request[KEY_REQUEST_METHODNAME].isString())) + return false; + if (!request.isMember(KEY_REQUEST_ID)) + return false; + if (!request.isMember(KEY_REQUEST_PARAMETERS)) + return false; + if (!(request[KEY_REQUEST_PARAMETERS].isArray() || + request[KEY_REQUEST_PARAMETERS].isNull())) + return false; + return true; } -void RpcProtocolServerV1::WrapResult(const Json::Value &request, Json::Value &response, Json::Value &retValue) -{ - response[KEY_RESPONSE_RESULT] = retValue; - response[KEY_RESPONSE_ERROR] = Json::nullValue; - response[KEY_REQUEST_ID] = request[KEY_REQUEST_ID]; +void RpcProtocolServerV1::WrapResult(const Json::Value &request, + Json::Value &response, + Json::Value &retValue) { + response[KEY_RESPONSE_RESULT] = retValue; + response[KEY_RESPONSE_ERROR] = Json::nullValue; + response[KEY_REQUEST_ID] = request[KEY_REQUEST_ID]; } -void RpcProtocolServerV1::WrapError(const Json::Value &request, int code, const std::string &message, Json::Value &result) -{ - result["error"]["code"] = code; - result["error"]["message"] = message; - result["result"] = Json::nullValue; - if(request.isObject() && request.isMember("id")) - { - result["id"] = request["id"]; - } - else - { - result["id"] = Json::nullValue; - } +void RpcProtocolServerV1::WrapError(const Json::Value &request, int code, + const std::string &message, + Json::Value &result) { + result["error"]["code"] = code; + result["error"]["message"] = message; + result["result"] = Json::nullValue; + if (request.isObject() && request.isMember("id")) { + result["id"] = request["id"]; + } else { + result["id"] = Json::nullValue; + } } -void RpcProtocolServerV1::WrapException(const Json::Value &request, const JsonRpcException &exception, Json::Value &result) -{ - this->WrapError(request, exception.GetCode(), exception.GetMessage(), result); - result["error"]["data"] = exception.GetData(); +void RpcProtocolServerV1::WrapException(const Json::Value &request, + const JsonRpcException &exception, + Json::Value &result) { + this->WrapError(request, exception.GetCode(), exception.GetMessage(), result); + result["error"]["data"] = exception.GetData(); } -procedure_t RpcProtocolServerV1::GetRequestType(const Json::Value &request) -{ - if (request[KEY_REQUEST_ID] == Json::nullValue) - return RPC_NOTIFICATION; - return RPC_METHOD; +procedure_t RpcProtocolServerV1::GetRequestType(const Json::Value &request) { + if (request[KEY_REQUEST_ID] == Json::nullValue) + return RPC_NOTIFICATION; + return RPC_METHOD; } diff --git a/src/jsonrpccpp/server/rpcprotocolserverv2.cpp b/src/jsonrpccpp/server/rpcprotocolserverv2.cpp index 91b16b5d..32765b59 100644 --- a/src/jsonrpccpp/server/rpcprotocolserverv2.cpp +++ b/src/jsonrpccpp/server/rpcprotocolserverv2.cpp @@ -8,114 +8,112 @@ ************************************************************************/ #include "rpcprotocolserverv2.h" -#include #include +#include using namespace std; using namespace jsonrpc; -RpcProtocolServerV2::RpcProtocolServerV2(IProcedureInvokationHandler &handler) : - AbstractProtocolHandler(handler) -{ -} +RpcProtocolServerV2::RpcProtocolServerV2(IProcedureInvokationHandler &handler) + : AbstractProtocolHandler(handler) {} -void RpcProtocolServerV2::HandleJsonRequest (const Json::Value &req, Json::Value &response) -{ - //It could be a Batch Request - if (req.isArray()) - { - this->HandleBatchRequest(req, response); - } //It could be a simple Request - else if (req.isObject()) - { - this->HandleSingleRequest(req, response); - } - else - { - this->WrapError(Json::nullValue, Errors::ERROR_RPC_INVALID_REQUEST, Errors::GetErrorMessage(Errors::ERROR_RPC_INVALID_REQUEST), response); - } +void RpcProtocolServerV2::HandleJsonRequest(const Json::Value &req, + Json::Value &response) { + // It could be a Batch Request + if (req.isArray()) { + this->HandleBatchRequest(req, response); + } // It could be a simple Request + else if (req.isObject()) { + this->HandleSingleRequest(req, response); + } else { + this->WrapError(Json::nullValue, Errors::ERROR_RPC_INVALID_REQUEST, + Errors::GetErrorMessage(Errors::ERROR_RPC_INVALID_REQUEST), + response); + } } -void RpcProtocolServerV2::HandleSingleRequest (const Json::Value &req, Json::Value& response) -{ - int error = this->ValidateRequest(req); - if (error == 0) - { - try - { - this->ProcessRequest(req, response); - } - catch (const JsonRpcException & exc) - { - this->WrapException(req, exc, response); - } - } - else - { - this->WrapError(req, error, Errors::GetErrorMessage(error),response); +void RpcProtocolServerV2::HandleSingleRequest(const Json::Value &req, + Json::Value &response) { + int error = this->ValidateRequest(req); + if (error == 0) { + try { + this->ProcessRequest(req, response); + } catch (const JsonRpcException &exc) { + this->WrapException(req, exc, response); } + } else { + this->WrapError(req, error, Errors::GetErrorMessage(error), response); + } } -void RpcProtocolServerV2::HandleBatchRequest (const Json::Value &req, Json::Value& response) -{ - if (req.size() == 0) - this->WrapError(Json::nullValue, Errors::ERROR_RPC_INVALID_REQUEST, Errors::GetErrorMessage(Errors::ERROR_RPC_INVALID_REQUEST), response); - else - { - for (unsigned int i = 0; i < req.size(); i++) - { - Json::Value result; - this->HandleSingleRequest(req[i], result); - if (result != Json::nullValue) - response.append(result); - } +void RpcProtocolServerV2::HandleBatchRequest(const Json::Value &req, + Json::Value &response) { + if (req.size() == 0) + this->WrapError(Json::nullValue, Errors::ERROR_RPC_INVALID_REQUEST, + Errors::GetErrorMessage(Errors::ERROR_RPC_INVALID_REQUEST), + response); + else { + for (unsigned int i = 0; i < req.size(); i++) { + Json::Value result; + this->HandleSingleRequest(req[i], result); + if (result != Json::nullValue) + response.append(result); } + } } -bool RpcProtocolServerV2::ValidateRequestFields(const Json::Value &request) -{ - if (!request.isObject()) - return false; - if (!(request.isMember(KEY_REQUEST_METHODNAME) && request[KEY_REQUEST_METHODNAME].isString())) - return false; - if (!(request.isMember(KEY_REQUEST_VERSION) && request[KEY_REQUEST_VERSION].isString() && request[KEY_REQUEST_VERSION].asString() == JSON_RPC_VERSION2)) - return false; - if (request.isMember(KEY_REQUEST_ID) && !(request[KEY_REQUEST_ID].isIntegral() || request[KEY_REQUEST_ID].isString() || request[KEY_REQUEST_ID].isNull())) - return false; - if (request.isMember(KEY_REQUEST_PARAMETERS) && !(request[KEY_REQUEST_PARAMETERS].isObject() || request[KEY_REQUEST_PARAMETERS].isArray() || request[KEY_REQUEST_PARAMETERS].isNull())) - return false; - return true; +bool RpcProtocolServerV2::ValidateRequestFields(const Json::Value &request) { + if (!request.isObject()) + return false; + if (!(request.isMember(KEY_REQUEST_METHODNAME) && + request[KEY_REQUEST_METHODNAME].isString())) + return false; + if (!(request.isMember(KEY_REQUEST_VERSION) && + request[KEY_REQUEST_VERSION].isString() && + request[KEY_REQUEST_VERSION].asString() == JSON_RPC_VERSION2)) + return false; + if (request.isMember(KEY_REQUEST_ID) && + !(request[KEY_REQUEST_ID].isIntegral() || + request[KEY_REQUEST_ID].isString() || request[KEY_REQUEST_ID].isNull())) + return false; + if (request.isMember(KEY_REQUEST_PARAMETERS) && + !(request[KEY_REQUEST_PARAMETERS].isObject() || + request[KEY_REQUEST_PARAMETERS].isArray() || + request[KEY_REQUEST_PARAMETERS].isNull())) + return false; + return true; } -void RpcProtocolServerV2::WrapResult(const Json::Value &request, Json::Value &response, Json::Value &result) -{ - response[KEY_REQUEST_VERSION] = JSON_RPC_VERSION2; - response[KEY_RESPONSE_RESULT] = result; - response[KEY_REQUEST_ID] = request[KEY_REQUEST_ID]; +void RpcProtocolServerV2::WrapResult(const Json::Value &request, + Json::Value &response, + Json::Value &result) { + response[KEY_REQUEST_VERSION] = JSON_RPC_VERSION2; + response[KEY_RESPONSE_RESULT] = result; + response[KEY_REQUEST_ID] = request[KEY_REQUEST_ID]; } -void RpcProtocolServerV2::WrapError(const Json::Value &request, int code, const string &message, Json::Value &result) -{ - result["jsonrpc"] = "2.0"; - result["error"]["code"] = code; - result["error"]["message"] = message; +void RpcProtocolServerV2::WrapError(const Json::Value &request, int code, + const string &message, + Json::Value &result) { + result["jsonrpc"] = "2.0"; + result["error"]["code"] = code; + result["error"]["message"] = message; - if(request.isObject() && request.isMember("id") && (request["id"].isNull() || request["id"].isIntegral() || request["id"].isString())) - { - result["id"] = request["id"]; - } - else - { - result["id"] = Json::nullValue; - } + if (request.isObject() && request.isMember("id") && + (request["id"].isNull() || request["id"].isIntegral() || + request["id"].isString())) { + result["id"] = request["id"]; + } else { + result["id"] = Json::nullValue; + } } -void RpcProtocolServerV2::WrapException(const Json::Value &request, const JsonRpcException &exception, Json::Value &result) -{ - this->WrapError(request, exception.GetCode(), exception.GetMessage(), result); - result["error"]["data"] = exception.GetData(); +void RpcProtocolServerV2::WrapException(const Json::Value &request, + const JsonRpcException &exception, + Json::Value &result) { + this->WrapError(request, exception.GetCode(), exception.GetMessage(), result); + result["error"]["data"] = exception.GetData(); } -procedure_t RpcProtocolServerV2::GetRequestType(const Json::Value &request) -{ - if (request.isMember(KEY_REQUEST_ID)) - return RPC_METHOD; - return RPC_NOTIFICATION; +procedure_t RpcProtocolServerV2::GetRequestType(const Json::Value &request) { + if (request.isMember(KEY_REQUEST_ID)) + return RPC_METHOD; + return RPC_NOTIFICATION; } diff --git a/src/jsonrpccpp/server/threadpool.h b/src/jsonrpccpp/server/threadpool.h new file mode 100644 index 00000000..09dc0362 --- /dev/null +++ b/src/jsonrpccpp/server/threadpool.h @@ -0,0 +1,124 @@ +/* +Copyright (c) 2012 Jakob Progsch, Václav Zeman + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source +distribution. +*/ + +#ifndef THREAD_POOL_H +#define THREAD_POOL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace jsonrpc { +class ThreadPool { +public: + ThreadPool(size_t); + template + auto enqueue(F&& f, Args&&... args) + -> std::future::type>; + ~ThreadPool(); +private: + // need to keep track of threads so we can join them + std::vector< std::thread > workers; + // the task queue + std::queue< std::function > tasks; + + // synchronization + std::mutex queue_mutex; + std::condition_variable condition; + bool stop; +}; + +// the constructor just launches some amount of workers +inline ThreadPool::ThreadPool(size_t threads) + : stop(false) +{ + for(size_t i = 0;i task; + + { + std::unique_lock lock(this->queue_mutex); + this->condition.wait(lock, + [this]{ return this->stop || !this->tasks.empty(); }); + if(this->stop && this->tasks.empty()) + return; + task = std::move(this->tasks.front()); + this->tasks.pop(); + } + + task(); + } + } + ); +} + +// add new work item to the pool +template +auto ThreadPool::enqueue(F&& f, Args&&... args) + -> std::future::type> +{ + using return_type = typename std::result_of::type; + + auto task = std::make_shared< std::packaged_task >( + std::bind(std::forward(f), std::forward(args)...) + ); + + std::future res = task->get_future(); + { + std::unique_lock lock(queue_mutex); + + // don't allow enqueueing after stopping the pool + if(stop) + throw std::runtime_error("enqueue on stopped ThreadPool"); + + tasks.emplace([task](){ (*task)(); }); + } + condition.notify_one(); + return res; +} + +// the destructor joins all threads +inline ThreadPool::~ThreadPool() +{ + { + std::unique_lock lock(queue_mutex); + stop = true; + } + condition.notify_all(); + for(std::thread &worker: workers) + worker.join(); +} +} + + +#endif diff --git a/src/stubgenerator/CMakeLists.txt b/src/stubgenerator/CMakeLists.txt index a3558280..5ae34aa6 100644 --- a/src/stubgenerator/CMakeLists.txt +++ b/src/stubgenerator/CMakeLists.txt @@ -21,51 +21,44 @@ if (BUILD_STATIC_LIBS) target_link_libraries(libjsonrpcstubStatic common ${ARGTABLE_LIBRARIES}) set_target_properties(libjsonrpcstubStatic PROPERTIES OUTPUT_NAME jsonrpccpp-stub) - if (NOT BUILD_SHARED_LIBS) - add_library(libjsonrpcstub ALIAS libjsonrpcstubStatic) - endif() + if (NOT BUILD_SHARED_LIBS) + add_library(libjsonrpcstub ALIAS libjsonrpcstubStatic) + endif() - list(APPEND ALL_LIBS libjsonrpcstubStatic) + list(APPEND ALL_LIBS libjsonrpcstubStatic) endif() set(VERSION_STRING ${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_VERSION}) -set_target_properties( - ${ALL_LIBS} - PROPERTIES VERSION "${VERSION_STRING}" SOVERSION "${SO_VERSION}" -) - +set_target_properties(${ALL_LIBS} PROPERTIES VERSION "${VERSION_STRING}" SOVERSION "${SO_VERSION}") add_executable(jsonrpcstub main.cpp) target_link_libraries(jsonrpcstub jsonrpccommon libjsonrpcstub ) #Generate manpage if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Windows") - configure_file("${CMAKE_SOURCE_DIR}/doc/manpage.in" "${CMAKE_BINARY_DIR}/manpage" @ONLY) - add_custom_command(OUTPUT jsonrpcstub.1.gz - COMMAND gzip -c "${CMAKE_BINARY_DIR}/manpage" > ${CMAKE_BINARY_DIR}/jsonrpcstub.1.gz - COMMENT "Generating man pages" - VERBATIM - ) - - add_custom_target(manpage ALL DEPENDS jsonrpcstub.1.gz) - install(FILES ${CMAKE_BINARY_DIR}/jsonrpcstub.1.gz DESTINATION share/man/man1/) - add_dependencies(jsonrpcstub manpage) - + configure_file("${CMAKE_SOURCE_DIR}/doc/manpage.in" "${CMAKE_BINARY_DIR}/manpage" @ONLY) + add_custom_command(OUTPUT jsonrpcstub.1.gz + COMMAND gzip -c "${CMAKE_BINARY_DIR}/manpage" > ${CMAKE_BINARY_DIR}/jsonrpcstub.1.gz + COMMENT "Generating man pages" + VERBATIM + ) + add_custom_target(manpage ALL DEPENDS jsonrpcstub.1.gz) + install(FILES ${CMAKE_BINARY_DIR}/jsonrpcstub.1.gz DESTINATION share/man/man1/) + add_dependencies(jsonrpcstub manpage) endif() -CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/cmake/libjsonrpccpp-stub.pc.cmake ${CMAKE_BINARY_DIR}/libjsonrpccpp-stub.pc) +configure_file(${CMAKE_SOURCE_DIR}/cmake/libjsonrpccpp-stub.pc.cmake ${CMAKE_BINARY_DIR}/libjsonrpccpp-stub.pc) -INSTALL(FILES +install(FILES "${CMAKE_BINARY_DIR}/libjsonrpccpp-stub.pc" DESTINATION "lib/${CMAKE_LIBRARY_PATH}/pkgconfig") install(DIRECTORY ${CMAKE_SOURCE_DIR}/src/stubgenerator/ - DESTINATION include/jsonrpccpp/stubgen - FILES_MATCHING PATTERN "*.h") + DESTINATION include/jsonrpccpp/stubgen + FILES_MATCHING PATTERN "*.h") install(TARGETS ${ALL_LIBS} jsonrpcstub - LIBRARY DESTINATION lib${LIB_SUFFIX}/${CMAKE_LIBRARY_PATH} - ARCHIVE DESTINATION lib${LIB_SUFFIX}/${CMAKE_LIBRARY_PATH} - RUNTIME DESTINATION bin + LIBRARY DESTINATION lib${LIB_SUFFIX}/${CMAKE_LIBRARY_PATH} + ARCHIVE DESTINATION lib${LIB_SUFFIX}/${CMAKE_LIBRARY_PATH} + RUNTIME DESTINATION bin ) - diff --git a/src/stubgenerator/client/cppclientstubgenerator.cpp b/src/stubgenerator/client/cppclientstubgenerator.cpp index ebb2d77f..15f79617 100644 --- a/src/stubgenerator/client/cppclientstubgenerator.cpp +++ b/src/stubgenerator/client/cppclientstubgenerator.cpp @@ -12,14 +12,17 @@ #define TEMPLATE_CPPCLIENT_SIGCLASS "class : public jsonrpc::Client" -#define TEMPLATE_CPPCLIENT_SIGCONSTRUCTOR "(jsonrpc::IClientConnector &conn, jsonrpc::clientVersion_t type = jsonrpc::JSONRPC_CLIENT_V2) : jsonrpc::Client(conn, type) {}" +#define TEMPLATE_CPPCLIENT_SIGCONSTRUCTOR \ + "(jsonrpc::IClientConnector &conn, jsonrpc::clientVersion_t type " \ + "= jsonrpc::JSONRPC_CLIENT_V2) : jsonrpc::Client(conn, type) {}" -#define TEMPLATE_CPPCLIENT_SIGMETHOD " () throw (jsonrpc::JsonRpcException)" +#define TEMPLATE_CPPCLIENT_SIGMETHOD " () " #define TEMPLATE_NAMED_ASSIGNMENT "p[\"\"] = ;" #define TEMPLATE_POSITION_ASSIGNMENT "p.append();" -#define TEMPLATE_METHODCALL "Json::Value result = this->CallMethod(\"\",p);" +#define TEMPLATE_METHODCALL \ + "Json::Value result = this->CallMethod(\"\",p);" #define TEMPLATE_NOTIFICATIONCALL "this->CallNotification(\"\",p);" #define TEMPLATE_RETURNCHECK "if (result)" @@ -28,128 +31,117 @@ using namespace std; using namespace jsonrpc; - -CPPClientStubGenerator::CPPClientStubGenerator(const string &stubname, std::vector &procedures, std::ostream& outputstream) : - StubGenerator(stubname, procedures, outputstream) -{ -} - -CPPClientStubGenerator::CPPClientStubGenerator(const string &stubname, std::vector &procedures, const string filename) : - StubGenerator(stubname, procedures, filename) -{ +CPPClientStubGenerator::CPPClientStubGenerator( + const string &stubname, std::vector &procedures, + std::ostream &outputstream) + : StubGenerator(stubname, procedures, outputstream) {} + +CPPClientStubGenerator::CPPClientStubGenerator( + const string &stubname, std::vector &procedures, + const string filename) + : StubGenerator(stubname, procedures, filename) {} + +void CPPClientStubGenerator::generateStub() { + vector classname = CPPHelper::splitPackages(this->stubname); + CPPHelper::prolog(*this, this->stubname); + this->writeLine("#include "); + this->writeNewLine(); + + int depth = CPPHelper::namespaceOpen(*this, stubname); + + this->writeLine(replaceAll(TEMPLATE_CPPCLIENT_SIGCLASS, "", + classname.at(classname.size() - 1))); + this->writeLine("{"); + this->increaseIndentation(); + this->writeLine("public:"); + this->increaseIndentation(); + + this->writeLine(replaceAll(TEMPLATE_CPPCLIENT_SIGCONSTRUCTOR, "", + classname.at(classname.size() - 1))); + this->writeNewLine(); + + for (unsigned int i = 0; i < procedures.size(); i++) { + this->generateMethod(procedures[i]); + } + + this->decreaseIndentation(); + this->decreaseIndentation(); + this->writeLine("};"); + this->writeNewLine(); + + CPPHelper::namespaceClose(*this, depth); + CPPHelper::epilog(*this, this->stubname); } -void CPPClientStubGenerator::generateStub() -{ - vector classname = CPPHelper::splitPackages(this->stubname); - CPPHelper::prolog(*this, this->stubname); - this->writeLine("#include "); - this->writeNewLine(); +void CPPClientStubGenerator::generateMethod(Procedure &proc) { + string procsignature = TEMPLATE_CPPCLIENT_SIGMETHOD; + string returntype = CPPHelper::toCppReturntype(proc.GetReturnType()); + if (proc.GetProcedureType() == RPC_NOTIFICATION) + returntype = "void"; - int depth = CPPHelper::namespaceOpen(*this, stubname); + replaceAll2(procsignature, "", returntype); + replaceAll2(procsignature, "", + CPPHelper::normalizeString(proc.GetProcedureName())); + replaceAll2(procsignature, "", + CPPHelper::generateParameterDeclarationList(proc)); - this->writeLine(replaceAll(TEMPLATE_CPPCLIENT_SIGCLASS, "", classname.at(classname.size()-1))); - this->writeLine("{"); - this->increaseIndentation(); - this->writeLine("public:"); - this->increaseIndentation(); + this->writeLine(procsignature); + this->writeLine("{"); + this->increaseIndentation(); - this->writeLine(replaceAll(TEMPLATE_CPPCLIENT_SIGCONSTRUCTOR, "", classname.at(classname.size()-1))); - this->writeNewLine(); + this->writeLine("Json::Value p;"); - for (unsigned int i=0; i < procedures.size(); i++) - { - this->generateMethod(procedures[i]); - } + generateAssignments(proc); + generateProcCall(proc); - this->decreaseIndentation(); - this->decreaseIndentation(); - this->writeLine("};"); - this->writeNewLine(); - - CPPHelper::namespaceClose(*this, depth); - CPPHelper::epilog(*this, this->stubname); + this->decreaseIndentation(); + this->writeLine("}"); } -void CPPClientStubGenerator::generateMethod(Procedure &proc) -{ - string procsignature = TEMPLATE_CPPCLIENT_SIGMETHOD; - string returntype = CPPHelper::toCppReturntype(proc.GetReturnType()); - if (proc.GetProcedureType() == RPC_NOTIFICATION) - returntype = "void"; - - replaceAll2(procsignature, "", returntype); - replaceAll2(procsignature, "", CPPHelper::normalizeString(proc.GetProcedureName())); - replaceAll2(procsignature, "", CPPHelper::generateParameterDeclarationList(proc)); - - this->writeLine(procsignature); - this->writeLine("{"); - this->increaseIndentation(); - - this->writeLine("Json::Value p;"); - - generateAssignments(proc); - generateProcCall(proc); - - this->decreaseIndentation(); - this->writeLine("}"); - - -} - -void CPPClientStubGenerator::generateAssignments(Procedure &proc) -{ - string assignment; - parameterNameList_t list = proc.GetParameters(); - if(list.size() > 0) - { - for (parameterNameList_t::iterator it = list.begin(); it != list.end(); ++it) - { - - if(proc.GetParameterDeclarationType() == PARAMS_BY_NAME) - { - assignment = TEMPLATE_NAMED_ASSIGNMENT; - } - else - { - assignment = TEMPLATE_POSITION_ASSIGNMENT; - } - replaceAll2(assignment, "", it->first); - this->writeLine(assignment); - } - } - else - { - this->writeLine("p = Json::nullValue;"); +void CPPClientStubGenerator::generateAssignments(Procedure &proc) { + string assignment; + parameterNameList_t list = proc.GetParameters(); + if (list.size() > 0) { + for (parameterNameList_t::iterator it = list.begin(); it != list.end(); + ++it) { + + if (proc.GetParameterDeclarationType() == PARAMS_BY_NAME) { + assignment = TEMPLATE_NAMED_ASSIGNMENT; + } else { + assignment = TEMPLATE_POSITION_ASSIGNMENT; + } + replaceAll2(assignment, "", it->first); + this->writeLine(assignment); } - + } else { + this->writeLine("p = Json::nullValue;"); + } } -void CPPClientStubGenerator::generateProcCall(Procedure &proc) -{ - string call; - if (proc.GetProcedureType() == RPC_METHOD) - { - call = TEMPLATE_METHODCALL; - this->writeLine(replaceAll(call, "", proc.GetProcedureName())); - call = TEMPLATE_RETURNCHECK; - replaceAll2(call,"", CPPHelper::isCppConversion(proc.GetReturnType())); - this->writeLine(call); - this->increaseIndentation(); - call = TEMPLATE_RETURN; - replaceAll2(call,"", CPPHelper::toCppConversion(proc.GetReturnType())); - this->writeLine(call); - this->decreaseIndentation(); - this->writeLine("else"); - this->increaseIndentation(); - this->writeLine("throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());"); - this->decreaseIndentation(); - } - else - { - call = TEMPLATE_NOTIFICATIONCALL; - replaceAll2(call, "", proc.GetProcedureName()); - this->writeLine(call); - } - +void CPPClientStubGenerator::generateProcCall(Procedure &proc) { + string call; + if (proc.GetProcedureType() == RPC_METHOD) { + call = TEMPLATE_METHODCALL; + this->writeLine(replaceAll(call, "", proc.GetProcedureName())); + call = TEMPLATE_RETURNCHECK; + replaceAll2(call, "", + CPPHelper::isCppConversion(proc.GetReturnType())); + this->writeLine(call); + this->increaseIndentation(); + call = TEMPLATE_RETURN; + replaceAll2(call, "", + CPPHelper::toCppConversion(proc.GetReturnType())); + this->writeLine(call); + this->decreaseIndentation(); + this->writeLine("else"); + this->increaseIndentation(); + this->writeLine("throw " + "jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_" + "INVALID_RESPONSE, result.toStyledString());"); + this->decreaseIndentation(); + } else { + call = TEMPLATE_NOTIFICATIONCALL; + replaceAll2(call, "", proc.GetProcedureName()); + this->writeLine(call); + } } diff --git a/src/stubgenerator/client/jsclientstubgenerator.cpp b/src/stubgenerator/client/jsclientstubgenerator.cpp index 26b74703..f038caf3 100644 --- a/src/stubgenerator/client/jsclientstubgenerator.cpp +++ b/src/stubgenerator/client/jsclientstubgenerator.cpp @@ -13,7 +13,8 @@ using namespace jsonrpc; using namespace std; -#define TEMPLATE_JS_PROLOG "function (url) {\n\ +#define TEMPLATE_JS_PROLOG \ + "function (url) {\n\ this.url = url;\n\ var id = 1;\n\ \n\ @@ -58,119 +59,112 @@ using namespace std; }\n\ }\n" -#define TEMPLATE_JS_METHOD ".prototype. = function(callbackSuccess, callbackError) {" +#define TEMPLATE_JS_METHOD \ + ".prototype. = function(callbackSuccess, " \ + "callbackError) {" #define TEMPLATE_JS_PARAM_NAMED "var params = {};" #define TEMPLATE_JS_PARAM_POSITIONAL "var params = [];" #define TEMPLATE_JS_PARAM_EMPTY "var params = null;" -#define TEMPLATE_JS_CALL_METHOD "return this.doRPC(\"\", params, true, callbackSuccess, callbackError);" -#define TEMPLATE_JS_CALL_NOTIFICATION "this.doRPC(\"\", params, false, callbackSuccess, callbackError);" - - - -JSClientStubGenerator::JSClientStubGenerator(const string &stubname, std::vector &procedures, ostream &outputstream) : - StubGenerator(stubname, procedures, outputstream) -{ +#define TEMPLATE_JS_CALL_METHOD \ + "return this.doRPC(\"\", params, true, callbackSuccess, " \ + "callbackError);" +#define TEMPLATE_JS_CALL_NOTIFICATION \ + "this.doRPC(\"\", params, false, callbackSuccess, " \ + "callbackError);" + +JSClientStubGenerator::JSClientStubGenerator(const string &stubname, + std::vector &procedures, + ostream &outputstream) + : StubGenerator(stubname, procedures, outputstream) {} + +JSClientStubGenerator::JSClientStubGenerator(const string &stubname, + std::vector &procedures, + const string &filename) + : StubGenerator(stubname, procedures, filename) {} + +string JSClientStubGenerator::class2Filename(const string &classname) { + string result = classname; + std::transform(result.begin(), result.end(), result.begin(), ::tolower); + return result + ".js"; } -JSClientStubGenerator::JSClientStubGenerator(const string &stubname, std::vector &procedures, const string &filename) : - StubGenerator(stubname, procedures, filename) -{ +void JSClientStubGenerator::generateStub() { + this->writeLine("/**"); + this->writeLine( + " * This file is generated by jsonrpcstub, DO NOT CHANGE IT MANUALLY!"); + this->writeLine(" */"); + this->write(replaceAll(TEMPLATE_JS_PROLOG, "", stubname)); + this->writeNewLine(); + + for (unsigned int i = 0; i < procedures.size(); i++) { + this->generateMethod(procedures[i]); + } } -string JSClientStubGenerator::class2Filename(const string &classname) -{ - string result = classname; - std::transform(result.begin(), result.end(), result.begin(), ::tolower); - return result + ".js"; -} +void JSClientStubGenerator::generateMethod(Procedure &proc) { + string method = TEMPLATE_JS_METHOD; + replaceAll2(method, "", stubname); + replaceAll2(method, "", + noramlizeJsLiteral(proc.GetProcedureName())); -void JSClientStubGenerator::generateStub() -{ - this->writeLine("/**"); - this->writeLine(" * This file is generated by jsonrpcstub, DO NOT CHANGE IT MANUALLY!"); - this->writeLine(" */"); - this->write(replaceAll(TEMPLATE_JS_PROLOG, "", stubname)); - this->writeNewLine(); - - for (unsigned int i=0; i < procedures.size(); i++) - { - this->generateMethod(procedures[i]); - } + stringstream param_string; + stringstream params_assignment; -} + parameterNameList_t list = proc.GetParameters(); + for (parameterNameList_t::iterator it = list.begin(); it != list.end();) { + param_string << it->first; -void JSClientStubGenerator::generateMethod(Procedure &proc) -{ - string method = TEMPLATE_JS_METHOD; - replaceAll2(method, "", stubname); - replaceAll2(method, "", noramlizeJsLiteral(proc.GetProcedureName())); - - stringstream param_string; - stringstream params_assignment; - - parameterNameList_t list = proc.GetParameters(); - for (parameterNameList_t::iterator it = list.begin(); it != list.end();) - { - param_string << it->first; - - if (proc.GetParameterDeclarationType() == PARAMS_BY_NAME) - params_assignment << it->first << " : " << it->first; - else - params_assignment << it->first; - - if (++it != list.end()) - { - params_assignment << ", "; - } - param_string << ", "; - } + if (proc.GetParameterDeclarationType() == PARAMS_BY_NAME) + params_assignment << it->first << " : " << it->first; + else + params_assignment << it->first; - replaceAll2(method, "", param_string.str()); + if (++it != list.end()) { + params_assignment << ", "; + } + param_string << ", "; + } - this->writeLine(method); - this->increaseIndentation(); + replaceAll2(method, "", param_string.str()); - string params; + this->writeLine(method); + this->increaseIndentation(); - if (proc.GetParameters().size() > 0) - { - if (proc.GetParameterDeclarationType() == PARAMS_BY_NAME) - params = TEMPLATE_JS_PARAM_NAMED; - else - params = TEMPLATE_JS_PARAM_POSITIONAL; + string params; - replaceAll2(params, "", params_assignment.str()); - this->writeLine(params); - } + if (proc.GetParameters().size() > 0) { + if (proc.GetParameterDeclarationType() == PARAMS_BY_NAME) + params = TEMPLATE_JS_PARAM_NAMED; else - { - this->writeLine(TEMPLATE_JS_PARAM_EMPTY); - } - + params = TEMPLATE_JS_PARAM_POSITIONAL; - if (proc.GetProcedureType() == RPC_METHOD) - method = TEMPLATE_JS_CALL_METHOD; - else - method = TEMPLATE_JS_CALL_NOTIFICATION; + replaceAll2(params, "", params_assignment.str()); + this->writeLine(params); + } else { + this->writeLine(TEMPLATE_JS_PARAM_EMPTY); + } - replaceAll2(method, "", proc.GetProcedureName()); + if (proc.GetProcedureType() == RPC_METHOD) + method = TEMPLATE_JS_CALL_METHOD; + else + method = TEMPLATE_JS_CALL_NOTIFICATION; - this->writeLine(method); + replaceAll2(method, "", proc.GetProcedureName()); - this->decreaseIndentation(); - this->writeLine("};"); + this->writeLine(method); + this->decreaseIndentation(); + this->writeLine("};"); } -string JSClientStubGenerator::noramlizeJsLiteral(const string &literal) -{ - string result = literal; - for(unsigned int i=0; i < literal.length(); i++) - { - if (!((literal[i] >= 'a' && literal[i] <= 'z') || (literal[i] >= 'A' && literal[i] <= 'Z') || (literal[i] >= '0' && literal[i] <= '9') || literal[i] == '_')) - { - result[i] = '_'; - } +string JSClientStubGenerator::noramlizeJsLiteral(const string &literal) { + string result = literal; + for (unsigned int i = 0; i < literal.length(); i++) { + if (!((literal[i] >= 'a' && literal[i] <= 'z') || + (literal[i] >= 'A' && literal[i] <= 'Z') || + (literal[i] >= '0' && literal[i] <= '9') || literal[i] == '_')) { + result[i] = '_'; } - return result; + } + return result; } diff --git a/src/stubgenerator/client/pyclientstubgenerator.cpp b/src/stubgenerator/client/pyclientstubgenerator.cpp new file mode 100644 index 00000000..9a825aee --- /dev/null +++ b/src/stubgenerator/client/pyclientstubgenerator.cpp @@ -0,0 +1,152 @@ +#include "pyclientstubgenerator.h" +#include + +#define TEMPLATE_PYTHON_CLIENT_SIGCLASS "class (client.Client):" + +#define TEMPLATE_PYTHON_CLIENT_SIGCONSTRUCTOR \ + "def __init__(self, connector, version='2.0'):\n super(, " \ + "self).__init__(connector, version)" + +#define TEMPLATE_PYTHON_CLIENT_SIGMETHOD "def (self):" + +#define TEMPLATE_NAMED_ASSIGNMENT "parameters[\'\'] = " +#define TEMPLATE_POSITION_ASSIGNMENT "parameters.append()" + +#define TEMPLATE_METHODCALL "result = self.call_method(\'\', parameters)" +#define TEMPLATE_NOTIFICATIONCALL \ + "self.call_notification(\'\', parameters)" + +using namespace std; +using namespace jsonrpc; + +PythonClientStubGenerator::PythonClientStubGenerator( + const string &stubname, std::vector &procedures, + std::ostream &outputstream) + : StubGenerator(stubname, procedures, outputstream) {} + +PythonClientStubGenerator::PythonClientStubGenerator( + const string &stubname, std::vector &procedures, + const string filename) + : StubGenerator(stubname, procedures, filename) {} + +void PythonClientStubGenerator::generateStub() { + this->writeLine("#"); + this->writeLine( + "# This file is generated by jsonrpcstub, DO NOT CHANGE IT MANUALLY!"); + this->writeLine("#"); + this->writeNewLine(); + this->writeLine("#"); + this->writeLine("# To use this client, jsonrpc_pyclient must be installed:"); + this->writeLine("# pip install jsonrpc_pyclient"); + this->writeLine("#"); + this->writeNewLine(); + this->writeLine("from jsonrpc_pyclient import client"); + this->writeNewLine(); + + this->writeLine(replaceAll(TEMPLATE_PYTHON_CLIENT_SIGCLASS, "", + this->stubname)); + this->increaseIndentation(); + + this->writeLine(replaceAll(TEMPLATE_PYTHON_CLIENT_SIGCONSTRUCTOR, + "", this->stubname)); + this->writeNewLine(); + + for (unsigned int i = 0; i < procedures.size(); i++) { + this->generateMethod(procedures[i]); + } + + this->decreaseIndentation(); + this->writeNewLine(); +} + +void PythonClientStubGenerator::generateMethod(Procedure &proc) { + string procsignature = TEMPLATE_PYTHON_CLIENT_SIGMETHOD; + replaceAll2(procsignature, "", + normalizeString(proc.GetProcedureName())); + + // generate parameters string + string params = generateParameterDeclarationList(proc); + replaceAll2(procsignature, "", params); + + this->writeLine(procsignature); + this->increaseIndentation(); + + generateAssignments(proc); + this->writeNewLine(); + generateProcCall(proc); + this->writeNewLine(); + + this->decreaseIndentation(); +} + +string +PythonClientStubGenerator::generateParameterDeclarationList(Procedure &proc) { + stringstream param_string; + parameterNameList_t list = proc.GetParameters(); + + for (parameterNameList_t::iterator it = list.begin(); it != list.end(); + ++it) { + param_string << ", "; + param_string << it->first; + } + + return param_string.str(); +} + +void PythonClientStubGenerator::generateAssignments(Procedure &proc) { + string assignment; + parameterNameList_t list = proc.GetParameters(); + if (list.size() > 0) { + parameterDeclaration_t declType = proc.GetParameterDeclarationType(); + if (proc.GetParameterDeclarationType() == PARAMS_BY_NAME) { + this->writeLine("parameters = {}"); + } else if (proc.GetParameterDeclarationType() == PARAMS_BY_POSITION) { + this->writeLine("parameters = []"); + } + + for (parameterNameList_t::iterator it = list.begin(); it != list.end(); + ++it) { + + if (declType == PARAMS_BY_NAME) { + assignment = TEMPLATE_NAMED_ASSIGNMENT; + } else { + assignment = TEMPLATE_POSITION_ASSIGNMENT; + } + replaceAll2(assignment, "", it->first); + this->writeLine(assignment); + } + } else { + this->writeLine("parameters = None"); + } +} + +void PythonClientStubGenerator::generateProcCall(Procedure &proc) { + string call; + if (proc.GetProcedureType() == RPC_METHOD) { + call = TEMPLATE_METHODCALL; + this->writeLine(replaceAll(call, "", proc.GetProcedureName())); + this->writeLine("return result"); + } else { + call = TEMPLATE_NOTIFICATIONCALL; + replaceAll2(call, "", proc.GetProcedureName()); + this->writeLine(call); + } +} + +string PythonClientStubGenerator::class2Filename(const string &classname) { + string result = classname; + std::transform(result.begin(), result.end(), result.begin(), ::tolower); + return result + ".py"; +} + +string PythonClientStubGenerator::normalizeString(const string &text) { + string result = text; + for (unsigned int i = 0; i < text.length(); i++) { + if (!((text[i] >= 'a' && text[i] <= 'z') || + (text[i] >= 'A' && text[i] <= 'Z') || + (text[i] >= '0' && text[i] <= '9') || text[i] == '_')) { + result[i] = '_'; + } + } + return result; +} diff --git a/src/stubgenerator/client/pyclientstubgenerator.h b/src/stubgenerator/client/pyclientstubgenerator.h new file mode 100644 index 00000000..e3a365ce --- /dev/null +++ b/src/stubgenerator/client/pyclientstubgenerator.h @@ -0,0 +1,31 @@ +#ifndef PYTHON_CLIENT_STUB_GENERATOR_H +#define PYTHON_CLIENT_STUB_GENERATOR_H + +#include "../stubgenerator.h" + +namespace jsonrpc +{ + /** + * The stub client this class generates requires jsonrpc_pyclient + * to be installed from pypi. + * https://github.com/tvannoy/jsonrpc_pyclient + */ + class PythonClientStubGenerator : public StubGenerator + { + public: + + + PythonClientStubGenerator(const std::string& stubname, std::vector &procedures, std::ostream& outputstream); + PythonClientStubGenerator(const std::string& stubname, std::vector &procedures, const std::string filename); + + virtual void generateStub(); + + void generateMethod(Procedure& proc); + void generateAssignments(Procedure& proc); + void generateProcCall(Procedure &proc); + std::string generateParameterDeclarationList(Procedure &proc); + static std::string class2Filename(const std::string &classname); + static std::string normalizeString(const std::string &text); + }; +} +#endif // PYTHON_CLIENT_STUB_GENERATOR_H diff --git a/src/stubgenerator/codegenerator.cpp b/src/stubgenerator/codegenerator.cpp index c316ec60..106b69a3 100644 --- a/src/stubgenerator/codegenerator.cpp +++ b/src/stubgenerator/codegenerator.cpp @@ -12,66 +12,47 @@ using namespace jsonrpc; using namespace std; -CodeGenerator::CodeGenerator(const ::string &filename) : - indentation(0), - atBeginning(true) -{ - this->file.open(filename.c_str()); - this->output = &this->file; - this->indentSymbol = " "; +CodeGenerator::CodeGenerator(const ::string &filename) + : indentation(0), atBeginning(true) { + this->file.open(filename.c_str()); + this->output = &this->file; + this->indentSymbol = " "; } -CodeGenerator::CodeGenerator(::ostream &outputstream) : - output(&outputstream), - indentSymbol(" "), - indentation(0), - atBeginning(true) -{ -} +CodeGenerator::CodeGenerator(::ostream &outputstream) + : output(&outputstream), indentSymbol(" "), indentation(0), + atBeginning(true) {} -CodeGenerator::~CodeGenerator() -{ - this->output->flush(); - if (this->file.is_open()) - { - this->file.close(); - } +CodeGenerator::~CodeGenerator() { + this->output->flush(); + if (this->file.is_open()) { + this->file.close(); + } } -void CodeGenerator::write(const ::string &line) -{ - if (this->atBeginning) - { - this->atBeginning = false; - for(int i=0; i < this->indentation; i++) - *this->output << this->indentSymbol; - } - *this->output << line; +void CodeGenerator::write(const ::string &line) { + if (this->atBeginning) { + this->atBeginning = false; + for (int i = 0; i < this->indentation; i++) + *this->output << this->indentSymbol; + } + *this->output << line; } -void CodeGenerator::writeLine(const ::string &line) -{ - this->write(line); - this->writeNewLine(); +void CodeGenerator::writeLine(const ::string &line) { + this->write(line); + this->writeNewLine(); } -void CodeGenerator::writeNewLine() -{ - *this->output << endl; - this->atBeginning = true; +void CodeGenerator::writeNewLine() { + *this->output << endl; + this->atBeginning = true; } -void CodeGenerator::increaseIndentation() -{ - this->indentation++; -} +void CodeGenerator::increaseIndentation() { this->indentation++; } -void CodeGenerator::decreaseIndentation() -{ - this->indentation--; -} +void CodeGenerator::decreaseIndentation() { this->indentation--; } -void CodeGenerator::setIndentSymbol(const ::string &symbol) -{ - this->indentSymbol = symbol; +void CodeGenerator::setIndentSymbol(const ::string &symbol) { + this->indentSymbol = symbol; } diff --git a/src/stubgenerator/helper/cpphelper.cpp b/src/stubgenerator/helper/cpphelper.cpp index c627a1d1..575fd86a 100644 --- a/src/stubgenerator/helper/cpphelper.cpp +++ b/src/stubgenerator/helper/cpphelper.cpp @@ -9,8 +9,8 @@ #include "cpphelper.h" #include "../stubgenerator.h" -#include #include +#include using namespace std; using namespace jsonrpc; @@ -20,219 +20,200 @@ using namespace jsonrpc; #define TEMPLATE_EPILOG "#endif //JSONRPC_CPP_STUB__H_" -string CPPHelper::toCppType (jsontype_t type, bool isConst, bool isReference) -{ - string result; - switch(type) - { - case JSON_BOOLEAN: - result = "bool"; - break; - case JSON_INTEGER: - result = "int"; - break; - case JSON_REAL: - result = "double"; - break; - case JSON_STRING: - result = "std::string"; - break; - default: - result = "Json::Value"; - break; - } - if(isConst) - { - result = "const " + result; - } - if(isReference) - { - result = result + "&"; - } - return result; +string CPPHelper::toCppType(jsontype_t type, bool isConst, bool isReference) { + string result; + switch (type) { + case JSON_BOOLEAN: + result = "bool"; + break; + case JSON_INTEGER: + result = "int"; + break; + case JSON_REAL: + result = "double"; + break; + case JSON_STRING: + result = "std::string"; + break; + default: + result = "Json::Value"; + break; + } + if (isConst) { + result = "const " + result; + } + if (isReference) { + result = result + "&"; + } + return result; } -string CPPHelper::toCppConversion (jsontype_t type) -{ - string result; - switch(type) - { - case JSON_BOOLEAN: - result = ".asBool()"; - break; - case JSON_INTEGER: - result = ".asInt()"; - break; - case JSON_REAL: - result = ".asDouble()"; - break; - case JSON_STRING: - result = ".asString()"; - break; - default: - result = ""; - break; - } - return result; +string CPPHelper::toCppConversion(jsontype_t type) { + string result; + switch (type) { + case JSON_BOOLEAN: + result = ".asBool()"; + break; + case JSON_INTEGER: + result = ".asInt()"; + break; + case JSON_REAL: + result = ".asDouble()"; + break; + case JSON_STRING: + result = ".asString()"; + break; + default: + result = ""; + break; + } + return result; } -string CPPHelper::toString (jsontype_t type) -{ - string result; - switch(type) - { - case JSON_BOOLEAN: - result = "jsonrpc::JSON_BOOLEAN"; - break; - case JSON_INTEGER: - result = "jsonrpc::JSON_INTEGER"; - break; - case JSON_REAL: - result = "jsonrpc::JSON_REAL"; - break; - case JSON_STRING: - result = "jsonrpc::JSON_STRING"; - break; - case JSON_OBJECT: - result = "jsonrpc::JSON_OBJECT"; - break; - case JSON_ARRAY: - result = "jsonrpc::JSON_ARRAY"; - break; - } - return result; +string CPPHelper::toString(jsontype_t type) { + string result; + switch (type) { + case JSON_BOOLEAN: + result = "jsonrpc::JSON_BOOLEAN"; + break; + case JSON_INTEGER: + result = "jsonrpc::JSON_INTEGER"; + break; + case JSON_REAL: + result = "jsonrpc::JSON_REAL"; + break; + case JSON_STRING: + result = "jsonrpc::JSON_STRING"; + break; + case JSON_OBJECT: + result = "jsonrpc::JSON_OBJECT"; + break; + case JSON_ARRAY: + result = "jsonrpc::JSON_ARRAY"; + break; + } + return result; } -string CPPHelper::generateParameterDeclarationList (Procedure &proc) -{ - stringstream param_string; - parameterNameList_t list = proc.GetParameters(); - for (parameterNameList_t::iterator it = list.begin(); it != list.end();) - { - param_string << toCppParamType(it->second) << " " << it->first; - if (++it != list.end()) - { - param_string << ", "; - } +string CPPHelper::generateParameterDeclarationList(Procedure &proc) { + stringstream param_string; + parameterNameList_t list = proc.GetParameters(); + for (parameterNameList_t::iterator it = list.begin(); it != list.end();) { + param_string << toCppParamType(it->second) << " " << it->first; + if (++it != list.end()) { + param_string << ", "; } - return param_string.str(); + } + return param_string.str(); } -string CPPHelper::toCppReturntype(jsontype_t type) -{ - return toCppType(type, false, false); +string CPPHelper::toCppReturntype(jsontype_t type) { + return toCppType(type, false, false); } -string CPPHelper::toCppParamType(jsontype_t type) -{ - if (type == JSON_ARRAY || type == JSON_OBJECT || type == JSON_STRING) - return toCppType(type, true, true); - else - return toCppType(type, false, false); +string CPPHelper::toCppParamType(jsontype_t type) { + if (type == JSON_ARRAY || type == JSON_OBJECT || type == JSON_STRING) + return toCppType(type, true, true); + else + return toCppType(type, false, false); } -string CPPHelper::class2Filename(const string &classname) -{ - vector packages = splitPackages(classname); - string data = packages.at(packages.size()-1); - std::transform(data.begin(), data.end(), data.begin(), ::tolower); - return data+".h"; +string CPPHelper::class2Filename(const string &classname) { + vector packages = splitPackages(classname); + string data = packages.at(packages.size() - 1); + std::transform(data.begin(), data.end(), data.begin(), ::tolower); + return data + ".h"; } -std::vector CPPHelper::splitPackages(const string &classname) -{ - string s = classname; - string delimiter = "::"; - size_t pos = 0; - vector tokens; - while ((pos = s.find(delimiter)) != string::npos) { - tokens.push_back(s.substr(0, pos)); - s.erase(0, pos + delimiter.length()); - } - tokens.push_back(s); - return tokens; +std::vector CPPHelper::splitPackages(const string &classname) { + string s = classname; + string delimiter = "::"; + size_t pos = 0; + vector tokens; + while ((pos = s.find(delimiter)) != string::npos) { + tokens.push_back(s.substr(0, pos)); + s.erase(0, pos + delimiter.length()); + } + tokens.push_back(s); + return tokens; } -void CPPHelper::prolog(CodeGenerator &cg, const string &stubname) -{ - cg.writeLine("/**"); - cg.writeLine(" * This file is generated by jsonrpcstub, DO NOT CHANGE IT MANUALLY!"); - cg.writeLine(" */"); - cg.writeNewLine(); - - string stub_upper = stubname; - std::transform(stub_upper.begin(), stub_upper.end(), stub_upper.begin(), - ::toupper); - StubGenerator::replaceAll2(stub_upper, "::", "_"); - - cg.writeLine(StubGenerator::replaceAll(TEMPLATE_CPPSERVER_GUARD1, "", stub_upper)); - cg.writeLine(StubGenerator::replaceAll(TEMPLATE_CPPSERVER_GUARD2, "", stub_upper)); - cg.writeNewLine(); +void CPPHelper::prolog(CodeGenerator &cg, const string &stubname) { + cg.writeLine("/**"); + cg.writeLine( + " * This file is generated by jsonrpcstub, DO NOT CHANGE IT MANUALLY!"); + cg.writeLine(" */"); + cg.writeNewLine(); + + string stub_upper = stubname; + std::transform(stub_upper.begin(), stub_upper.end(), stub_upper.begin(), + ::toupper); + StubGenerator::replaceAll2(stub_upper, "::", "_"); + + cg.writeLine(StubGenerator::replaceAll(TEMPLATE_CPPSERVER_GUARD1, + "", stub_upper)); + cg.writeLine(StubGenerator::replaceAll(TEMPLATE_CPPSERVER_GUARD2, + "", stub_upper)); + cg.writeNewLine(); } -void CPPHelper::epilog(CodeGenerator &cg, const string &stubname) -{ - string stub_upper = stubname; - std::transform(stub_upper.begin(), stub_upper.end(), stub_upper.begin(),::toupper); - StubGenerator::replaceAll2(stub_upper, "::", "_"); - cg.writeLine(StubGenerator::replaceAll(TEMPLATE_EPILOG, "", stub_upper)); +void CPPHelper::epilog(CodeGenerator &cg, const string &stubname) { + string stub_upper = stubname; + std::transform(stub_upper.begin(), stub_upper.end(), stub_upper.begin(), + ::toupper); + StubGenerator::replaceAll2(stub_upper, "::", "_"); + cg.writeLine( + StubGenerator::replaceAll(TEMPLATE_EPILOG, "", stub_upper)); } -int CPPHelper::namespaceOpen(CodeGenerator &cg, const string &classname) -{ - vector namespaces = splitPackages(classname); +int CPPHelper::namespaceOpen(CodeGenerator &cg, const string &classname) { + vector namespaces = splitPackages(classname); - for (unsigned int i=0; i < namespaces.size() -1; i++) - { - cg.write("namespace "); - cg.write(namespaces.at(i)); - cg.writeLine(" {"); - cg.increaseIndentation(); - } - return namespaces.size()-1; + for (unsigned int i = 0; i < namespaces.size() - 1; i++) { + cg.write("namespace "); + cg.write(namespaces.at(i)); + cg.writeLine(" {"); + cg.increaseIndentation(); + } + return namespaces.size() - 1; } -void CPPHelper::namespaceClose(CodeGenerator &cg, int depth) -{ - for (int i=0; i < depth; i++) - { - cg.decreaseIndentation(); - cg.writeLine("}"); - } +void CPPHelper::namespaceClose(CodeGenerator &cg, int depth) { + for (int i = 0; i < depth; i++) { + cg.decreaseIndentation(); + cg.writeLine("}"); + } } -string CPPHelper::normalizeString (const string &text) -{ - string result = text; - for(unsigned int i=0; i < text.length(); i++) - { - if (!((text[i] >= 'a' && text[i] <= 'z') || (text[i] >= 'A' && text[i] <= 'Z') || (text[i] >= '0' && text[i] <= '9') || text[i] == '_')) - { - result[i] = '_'; - } +string CPPHelper::normalizeString(const string &text) { + string result = text; + for (unsigned int i = 0; i < text.length(); i++) { + if (!((text[i] >= 'a' && text[i] <= 'z') || + (text[i] >= 'A' && text[i] <= 'Z') || + (text[i] >= '0' && text[i] <= '9') || text[i] == '_')) { + result[i] = '_'; } - return result; + } + return result; } -string CPPHelper::isCppConversion (jsontype_t type) -{ - string result; - switch(type) - { - case JSON_BOOLEAN: - result = ".isBool()"; - break; - case JSON_INTEGER: - result = ".isIntegral()"; - break; - case JSON_REAL: - result = ".isDouble()"; - break; - case JSON_STRING: - result = ".isString()"; - break; - case JSON_OBJECT: - result = ".isObject()"; - break; - case JSON_ARRAY: - result = ".isArray()"; - break; - } - return result; +string CPPHelper::isCppConversion(jsontype_t type) { + string result; + switch (type) { + case JSON_BOOLEAN: + result = ".isBool()"; + break; + case JSON_INTEGER: + result = ".isIntegral()"; + break; + case JSON_REAL: + result = ".isDouble()"; + break; + case JSON_STRING: + result = ".isString()"; + break; + case JSON_OBJECT: + result = ".isObject()"; + break; + case JSON_ARRAY: + result = ".isArray()"; + break; + } + return result; } diff --git a/src/stubgenerator/main.cpp b/src/stubgenerator/main.cpp index 097599b6..7acc2092 100644 --- a/src/stubgenerator/main.cpp +++ b/src/stubgenerator/main.cpp @@ -7,25 +7,24 @@ * @license See attached LICENSE.txt ************************************************************************/ -#include #include +#include #include "stubgeneratorfactory.h" using namespace std; using namespace jsonrpc; -int main(int argc, char** argv) -{ - vector stubgens; - vector procedures; +int main(int argc, char **argv) { + vector stubgens; + vector procedures; - bool result = StubGeneratorFactory::createStubGenerators(argc, argv, procedures, stubgens, stdout, stderr); + bool result = StubGeneratorFactory::createStubGenerators( + argc, argv, procedures, stubgens, stdout, stderr); - for (unsigned int i=0; i < stubgens.size(); ++i) - { - stubgens[i]->generateStub(); - } - StubGeneratorFactory::deleteStubGenerators(stubgens); - return !result; + for (unsigned int i = 0; i < stubgens.size(); ++i) { + stubgens[i]->generateStub(); + } + StubGeneratorFactory::deleteStubGenerators(stubgens); + return !result; } diff --git a/src/stubgenerator/server/cppserverstubgenerator.cpp b/src/stubgenerator/server/cppserverstubgenerator.cpp index 179a9114..2cb4a7d3 100644 --- a/src/stubgenerator/server/cppserverstubgenerator.cpp +++ b/src/stubgenerator/server/cppserverstubgenerator.cpp @@ -10,188 +10,193 @@ #include "cppserverstubgenerator.h" #include "../helper/cpphelper.h" +#include #include #include -#include - -#define TEMPLATE_CPPSERVER_METHODBINDING "this->bindAndAddMethod(jsonrpc::Procedure(\"\", , , NULL), &::I);" -#define TEMPLATE_CPPSERVER_NOTIFICATIONBINDING "this->bindAndAddNotification(jsonrpc::Procedure(\"\", , NULL), &::I);" - -#define TEMPLATE_CPPSERVER_SIGCLASS "class : public jsonrpc::AbstractServer<>" -#define TEMPLATE_CPPSERVER_SIGCONSTRUCTOR "(jsonrpc::AbstractServerConnector &conn, jsonrpc::serverVersion_t type = jsonrpc::JSONRPC_SERVER_V2) : jsonrpc::AbstractServer<>(conn, type)" -#define TEMPLATE_CPPSERVER_SIGMETHOD "inline virtual void I(const Json::Value &request, Json::Value &response)" -#define TEMPLATE_CPPSERVER_SIGNOTIFICATION "inline virtual void I(const Json::Value &request)" - -#define TEMPLATE_SERVER_ABSTRACTDEFINITION "virtual () = 0;" +#define TEMPLATE_CPPSERVER_METHODBINDING \ + "this->bindAndAddMethod(jsonrpc::Procedure(\"\", " \ + ", , NULL), " \ + "&::I);" +#define TEMPLATE_CPPSERVER_NOTIFICATIONBINDING \ + "this->bindAndAddNotification(jsonrpc::Procedure(\"\", " \ + ", NULL), &::I);" + +#define TEMPLATE_CPPSERVER_SIGCLASS \ + "class : public jsonrpc::AbstractServer<>" +#define TEMPLATE_CPPSERVER_SIGCONSTRUCTOR \ + "(jsonrpc::AbstractServerConnector &conn, " \ + "jsonrpc::serverVersion_t type = jsonrpc::JSONRPC_SERVER_V2) : " \ + "jsonrpc::AbstractServer<>(conn, type)" + +#define TEMPLATE_CPPSERVER_SIGMETHOD \ + "inline virtual void I(const Json::Value &request, " \ + "Json::Value &response)" +#define TEMPLATE_CPPSERVER_SIGNOTIFICATION \ + "inline virtual void I(const Json::Value &request)" + +#define TEMPLATE_SERVER_ABSTRACTDEFINITION \ + "virtual () = 0;" using namespace std; using namespace jsonrpc; +CPPServerStubGenerator::CPPServerStubGenerator(const std::string &stubname, + vector &procedures, + ostream &outputstream) + : StubGenerator(stubname, procedures, outputstream) {} -CPPServerStubGenerator::CPPServerStubGenerator(const std::string &stubname, vector &procedures, ostream &outputstream) : - StubGenerator(stubname, procedures, outputstream) -{ -} +CPPServerStubGenerator::CPPServerStubGenerator( + const string &stubname, std::vector &procedures, + const string &filename) + : StubGenerator(stubname, procedures, filename) {} -CPPServerStubGenerator::CPPServerStubGenerator(const string &stubname, std::vector &procedures, const string &filename) : - StubGenerator(stubname, procedures, filename) -{ -} +void CPPServerStubGenerator::generateStub() { + vector classname = CPPHelper::splitPackages(this->stubname); + CPPHelper::prolog(*this, this->stubname); -void CPPServerStubGenerator::generateStub() -{ - vector classname = CPPHelper::splitPackages(this->stubname); - CPPHelper::prolog(*this, this->stubname); + this->writeLine("#include "); + this->writeNewLine(); - this->writeLine("#include "); - this->writeNewLine(); + int depth = CPPHelper::namespaceOpen(*this, stubname); - int depth = CPPHelper::namespaceOpen(*this, stubname); + this->writeLine(replaceAll(TEMPLATE_CPPSERVER_SIGCLASS, "", + classname.at(classname.size() - 1))); + this->writeLine("{"); + this->increaseIndentation(); + this->writeLine("public:"); + this->increaseIndentation(); - this->writeLine(replaceAll(TEMPLATE_CPPSERVER_SIGCLASS, "", classname.at(classname.size()-1))); - this->writeLine("{"); - this->increaseIndentation(); - this->writeLine("public:"); - this->increaseIndentation(); + this->writeLine(replaceAll(TEMPLATE_CPPSERVER_SIGCONSTRUCTOR, "", + classname.at(classname.size() - 1))); + this->writeLine("{"); + this->generateBindings(); + this->writeLine("}"); - this->writeLine(replaceAll(TEMPLATE_CPPSERVER_SIGCONSTRUCTOR, "", classname.at(classname.size()-1))); - this->writeLine("{"); - this->generateBindings(); - this->writeLine("}"); + this->writeNewLine(); - this->writeNewLine(); + this->generateProcedureDefinitions(); - this->generateProcedureDefinitions(); + this->generateAbstractDefinitions(); - this->generateAbstractDefinitions(); + this->decreaseIndentation(); + this->decreaseIndentation(); + this->writeLine("};"); + this->writeNewLine(); - this->decreaseIndentation(); - this->decreaseIndentation(); - this->writeLine("};"); - this->writeNewLine(); + CPPHelper::namespaceClose(*this, depth); + CPPHelper::epilog(*this, this->stubname); +} - CPPHelper::namespaceClose(*this, depth); - CPPHelper::epilog(*this,this->stubname); +void CPPServerStubGenerator::generateBindings() { + string tmp; + this->increaseIndentation(); + for (vector::iterator it = this->procedures.begin(); + it != this->procedures.end(); ++it) { + Procedure &proc = *it; + if (proc.GetProcedureType() == RPC_METHOD) { + tmp = TEMPLATE_CPPSERVER_METHODBINDING; + } else { + tmp = TEMPLATE_CPPSERVER_NOTIFICATIONBINDING; + } + replaceAll2(tmp, "", proc.GetProcedureName()); + replaceAll2(tmp, "", + CPPHelper::normalizeString(proc.GetProcedureName())); + replaceAll2(tmp, "", CPPHelper::toString(proc.GetReturnType())); + replaceAll2(tmp, "", generateBindingParameterlist(proc)); + replaceAll2(tmp, "", this->stubname); + + if (proc.GetParameterDeclarationType() == PARAMS_BY_NAME) { + replaceAll2(tmp, "", "jsonrpc::PARAMS_BY_NAME"); + } else { + replaceAll2(tmp, "", "jsonrpc::PARAMS_BY_POSITION"); + } + + this->writeLine(tmp); + } + this->decreaseIndentation(); } +void CPPServerStubGenerator::generateProcedureDefinitions() { + for (vector::iterator it = this->procedures.begin(); + it != this->procedures.end(); ++it) { + Procedure &proc = *it; + if (proc.GetProcedureType() == RPC_METHOD) + this->writeLine( + replaceAll(TEMPLATE_CPPSERVER_SIGMETHOD, "", + CPPHelper::normalizeString(proc.GetProcedureName()))); + else + this->writeLine( + replaceAll(TEMPLATE_CPPSERVER_SIGNOTIFICATION, "", + CPPHelper::normalizeString(proc.GetProcedureName()))); -void CPPServerStubGenerator::generateBindings() -{ - string tmp; + this->writeLine("{"); this->increaseIndentation(); - for(vector::iterator it = this->procedures.begin(); it != this->procedures.end(); ++it) - { - Procedure &proc = *it; - if(proc.GetProcedureType() == RPC_METHOD) - { - tmp = TEMPLATE_CPPSERVER_METHODBINDING; - } - else - { - tmp = TEMPLATE_CPPSERVER_NOTIFICATIONBINDING; - } - replaceAll2(tmp, "", proc.GetProcedureName()); - replaceAll2(tmp, "", CPPHelper::normalizeString(proc.GetProcedureName())); - replaceAll2(tmp, "", CPPHelper::toString(proc.GetReturnType())); - replaceAll2(tmp, "", generateBindingParameterlist(proc)); - replaceAll2(tmp, "", this->stubname); - - if(proc.GetParameterDeclarationType() == PARAMS_BY_NAME) - { - replaceAll2(tmp, "", "jsonrpc::PARAMS_BY_NAME"); - } - else - { - replaceAll2(tmp, "", "jsonrpc::PARAMS_BY_POSITION"); - } - - this->writeLine(tmp); - } - this->decreaseIndentation(); -} + if (proc.GetParameters().empty()) + this->writeLine("(void)request;"); -void CPPServerStubGenerator::generateProcedureDefinitions() -{ - for(vector::iterator it = this->procedures.begin(); it != this->procedures.end(); ++it) - { - Procedure &proc = *it; - if(proc.GetProcedureType() == RPC_METHOD) - this->writeLine(replaceAll(TEMPLATE_CPPSERVER_SIGMETHOD, "", CPPHelper::normalizeString(proc.GetProcedureName()))); - else - this->writeLine(replaceAll(TEMPLATE_CPPSERVER_SIGNOTIFICATION, "", CPPHelper::normalizeString(proc.GetProcedureName()))); - - this->writeLine("{"); - this->increaseIndentation(); - if (proc.GetParameters().empty()) - this->writeLine("(void)request;"); - - if (proc.GetProcedureType() == RPC_METHOD) - this->write("response = "); - this->write("this->"); - this->write(CPPHelper::normalizeString(proc.GetProcedureName())+"("); - this->generateParameterMapping(proc); - this->writeLine(");"); - - this->decreaseIndentation(); - this->writeLine("}"); - } + if (proc.GetProcedureType() == RPC_METHOD) + this->write("response = "); + this->write("this->"); + this->write(CPPHelper::normalizeString(proc.GetProcedureName()) + "("); + this->generateParameterMapping(proc); + this->writeLine(");"); + + this->decreaseIndentation(); + this->writeLine("}"); + } } -void CPPServerStubGenerator::generateAbstractDefinitions() -{ - string tmp; - for(vector::iterator it = this->procedures.begin(); it != this->procedures.end(); ++it) - { - Procedure& proc = *it; - tmp = TEMPLATE_SERVER_ABSTRACTDEFINITION; - string returntype ="void"; - if(proc.GetProcedureType() == RPC_METHOD) - { - returntype = CPPHelper::toCppReturntype(proc.GetReturnType()); - } - replaceAll2(tmp, "", returntype); - replaceAll2(tmp, "", CPPHelper::normalizeString(proc.GetProcedureName())); - replaceAll2(tmp, "", CPPHelper::generateParameterDeclarationList(proc)); - this->writeLine(tmp); +void CPPServerStubGenerator::generateAbstractDefinitions() { + string tmp; + for (vector::iterator it = this->procedures.begin(); + it != this->procedures.end(); ++it) { + Procedure &proc = *it; + tmp = TEMPLATE_SERVER_ABSTRACTDEFINITION; + string returntype = "void"; + if (proc.GetProcedureType() == RPC_METHOD) { + returntype = CPPHelper::toCppReturntype(proc.GetReturnType()); } + replaceAll2(tmp, "", returntype); + replaceAll2(tmp, "", + CPPHelper::normalizeString(proc.GetProcedureName())); + replaceAll2(tmp, "", + CPPHelper::generateParameterDeclarationList(proc)); + this->writeLine(tmp); + } } -string CPPServerStubGenerator::generateBindingParameterlist(Procedure &proc) -{ - stringstream parameter; - const parameterNameList_t& list = proc.GetParameters(); +string CPPServerStubGenerator::generateBindingParameterlist(Procedure &proc) { + stringstream parameter; + const parameterNameList_t &list = proc.GetParameters(); - for(parameterNameList_t::const_iterator it2 = list.begin(); it2 != list.end(); ++it2) - { - parameter << "\"" << it2->first << "\"," << CPPHelper::toString(it2->second) << ","; - } - return parameter.str(); + for (parameterNameList_t::const_iterator it2 = list.begin(); + it2 != list.end(); ++it2) { + parameter << "\"" << it2->first << "\"," << CPPHelper::toString(it2->second) + << ","; + } + return parameter.str(); } -void CPPServerStubGenerator::generateParameterMapping(Procedure &proc) -{ - string tmp; - const parameterNameList_t& params = proc.GetParameters(); - int i=0; - for(parameterNameList_t::const_iterator it2 = params.begin(); it2 != params.end(); ++it2) - { - if(proc.GetParameterDeclarationType() == PARAMS_BY_NAME) - { - tmp = "request[\"" + it2->first + "\"]" + CPPHelper::toCppConversion(it2->second); - } - else - { - stringstream tmp2; - tmp2 << "request["<< i << "u]" << CPPHelper::toCppConversion(it2->second); - tmp = tmp2.str(); - } - this->write(tmp); - if(it2 != --params.end()) - { - this->write(", "); - } - i++; +void CPPServerStubGenerator::generateParameterMapping(Procedure &proc) { + string tmp; + const parameterNameList_t ¶ms = proc.GetParameters(); + int i = 0; + for (parameterNameList_t::const_iterator it2 = params.begin(); + it2 != params.end(); ++it2) { + if (proc.GetParameterDeclarationType() == PARAMS_BY_NAME) { + tmp = "request[\"" + it2->first + "\"]" + + CPPHelper::toCppConversion(it2->second); + } else { + stringstream tmp2; + tmp2 << "request[" << i << "u]" + << CPPHelper::toCppConversion(it2->second); + tmp = tmp2.str(); + } + this->write(tmp); + if (it2 != --params.end()) { + this->write(", "); } + i++; + } } - diff --git a/src/stubgenerator/stubgenerator.cpp b/src/stubgenerator/stubgenerator.cpp index f9b2df4a..532769b5 100644 --- a/src/stubgenerator/stubgenerator.cpp +++ b/src/stubgenerator/stubgenerator.cpp @@ -7,55 +7,51 @@ * @license See attached LICENSE.txt ************************************************************************/ -#include #include +#include #include #include -#include "stubgenerator.h" -#include "helper/cpphelper.h" -#include "server/cppserverstubgenerator.h" #include "client/cppclientstubgenerator.h" #include "client/jsclientstubgenerator.h" +#include "client/pyclientstubgenerator.h" +#include "helper/cpphelper.h" +#include "server/cppserverstubgenerator.h" +#include "stubgenerator.h" using namespace std; using namespace jsonrpc; -#define EXIT_ERROR(X) cerr << X << endl;arg_freetable(argtable,sizeof(argtable)/sizeof(argtable[0]));return 1; - +#define EXIT_ERROR(X) \ + cerr << X << endl; \ + arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); \ + return 1; -StubGenerator::StubGenerator (const string &stubname, std::vector &procedures, ostream &outputstream) : - CodeGenerator (outputstream), - stubname (stubname), - procedures (procedures) -{ -} +StubGenerator::StubGenerator(const string &stubname, + std::vector &procedures, + ostream &outputstream) + : CodeGenerator(outputstream), stubname(stubname), procedures(procedures) {} -StubGenerator::StubGenerator (const string &stubname, std::vector &procedures, const std::string &filename) : - CodeGenerator (filename), - stubname (stubname), - procedures (procedures) -{ -} +StubGenerator::StubGenerator(const string &stubname, + std::vector &procedures, + const std::string &filename) + : CodeGenerator(filename), stubname(stubname), procedures(procedures) {} -StubGenerator::~StubGenerator () -{ -} +StubGenerator::~StubGenerator() {} -string StubGenerator::replaceAll (const string &text, const string &fnd, const string &rep) -{ - string result = text; - replaceAll2(result, fnd, rep); - return result; +string StubGenerator::replaceAll(const string &text, const string &fnd, + const string &rep) { + string result = text; + replaceAll2(result, fnd, rep); + return result; } -void StubGenerator::replaceAll2(string &result, const string &find, const string &replace) -{ - size_t pos = result.find(find); - while (pos != string::npos) - { - result.replace(pos, find.length(), replace); - pos = result.find(find, pos + replace.length()); - } +void StubGenerator::replaceAll2(string &result, const string &find, + const string &replace) { + size_t pos = result.find(find); + while (pos != string::npos) { + result.replace(pos, find.length(), replace); + pos = result.find(find, pos + replace.length()); + } } diff --git a/src/stubgenerator/stubgeneratorfactory.cpp b/src/stubgenerator/stubgeneratorfactory.cpp index 34536557..68037afd 100644 --- a/src/stubgenerator/stubgeneratorfactory.cpp +++ b/src/stubgenerator/stubgeneratorfactory.cpp @@ -8,134 +8,166 @@ ************************************************************************/ #include "stubgeneratorfactory.h" -#include -#include -#include -#include -#include "helper/cpphelper.h" #include "client/cppclientstubgenerator.h" #include "client/jsclientstubgenerator.h" +#include "client/pyclientstubgenerator.h" +#include "helper/cpphelper.h" #include "server/cppserverstubgenerator.h" +#include +#include +#include +#include using namespace jsonrpc; using namespace std; -bool StubGeneratorFactory::createStubGenerators(int argc, char **argv, vector &procedures, vector &stubgenerators, FILE *stdout, FILE *stderr) -{ - struct arg_file *inputfile = arg_file0(NULL, NULL, "", "path of input specification file"); - struct arg_lit *help = arg_lit0("h","help", "print this help and exit"); - struct arg_lit *version = arg_lit0(NULL,"version", "print version and exit"); - struct arg_lit *verbose = arg_lit0("v","verbose", "print more information about what is happening"); - struct arg_str *cppserver = arg_str0(NULL, "cpp-server", "", "name of the C++ server stub class"); - struct arg_str *cppserverfile = arg_str0(NULL, "cpp-server-file", "", "name of the C++ server stub file"); - struct arg_str *cppclient = arg_str0(NULL, "cpp-client", "", "name of the C++ client stub class"); - struct arg_str *cppclientfile = arg_str0(NULL, "cpp-client-file", "", "name of the C++ client stub file"); - struct arg_str *jsclient = arg_str0(NULL, "js-client", "", "name of the JavaScript client stub class"); - struct arg_str *jsclientfile = arg_str0(NULL, "js-client-file", "", "name of the JavaScript client stub file"); - - - struct arg_end *end = arg_end(20); - void* argtable[] = {inputfile, help, version, verbose, cppserver, cppserverfile, cppclient, cppclientfile, jsclient, jsclientfile,end}; - - if (arg_parse(argc,argv,argtable) > 0) - { - arg_print_errors(stderr, end, argv[0]); - arg_freetable(argtable,sizeof(argtable)/sizeof(argtable[0])); - return false; - } +bool StubGeneratorFactory::createStubGenerators( + int argc, char **argv, vector &procedures, + vector &stubgenerators, FILE *stdout, FILE *stderr) { + struct arg_file *inputfile = + arg_file0(NULL, NULL, "", "path of input specification file"); + struct arg_lit *help = arg_lit0("h", "help", "print this help and exit"); + struct arg_lit *version = arg_lit0(NULL, "version", "print version and exit"); + struct arg_lit *verbose = arg_lit0( + "v", "verbose", "print more information about what is happening"); + struct arg_str *cppserver = + arg_str0(NULL, "cpp-server", "", + "name of the C++ server stub class"); + struct arg_str *cppserverfile = + arg_str0(NULL, "cpp-server-file", "", + "name of the C++ server stub file"); + struct arg_str *cppclient = + arg_str0(NULL, "cpp-client", "", + "name of the C++ client stub class"); + struct arg_str *cppclientfile = + arg_str0(NULL, "cpp-client-file", "", + "name of the C++ client stub file"); + struct arg_str *jsclient = + arg_str0(NULL, "js-client", "", + "name of the JavaScript client stub class"); + struct arg_str *jsclientfile = + arg_str0(NULL, "js-client-file", "", + "name of the JavaScript client stub file"); + struct arg_str *pyclient = arg_str0(NULL, "py-client", "", + "name of the Python client stub class"); + struct arg_str *pyclientfile = + arg_str0(NULL, "py-client-file", "", + "name of the Python client stub file"); - if (help->count > 0) - { - fprintf(stdout, "Usage: %s ", argv[0]); - arg_print_syntax(stdout,argtable,"\n"); cout << endl; - arg_print_glossary_gnu(stdout, argtable); - arg_freetable(argtable,sizeof(argtable)/sizeof(argtable[0])); - return true; + struct arg_end *end = arg_end(20); + void *argtable[] = {inputfile, help, version, verbose, + cppserver, cppserverfile, cppclient, cppclientfile, + jsclient, jsclientfile, pyclient, pyclientfile, + end}; + + if (arg_parse(argc, argv, argtable) > 0) { + arg_print_errors(stderr, end, argv[0]); + arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); + return false; + } + + if (help->count > 0) { + fprintf(stdout, "Usage: %s ", argv[0]); + arg_print_syntax(stdout, argtable, "\n"); + cout << endl; + arg_print_glossary_gnu(stdout, argtable); + arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); + return true; + } + + if (version->count > 0) { + fprintf(stdout, "jsonrpcstub version %d.%d.%d\n", JSONRPC_CPP_MAJOR_VERSION, + JSONRPC_CPP_MINOR_VERSION, JSONRPC_CPP_PATCH_VERSION); + arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); + return true; + } + + if (inputfile->count == 0) { + fprintf(stderr, "Invalid arguments: specfile must be provided.\n"); + arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); + return false; + } + + try { + procedures = + SpecificationParser::GetProceduresFromFile(inputfile->filename[0]); + if (verbose->count > 0) { + fprintf(stdout, "Found %zu procedures in %s\n", procedures.size(), + inputfile->filename[0]); + for (unsigned int i = 0; i < procedures.size(); ++i) { + if (procedures.at(i).GetProcedureType() == RPC_METHOD) { + fprintf(stdout, "\t[Method] "); + } else { + fprintf(stdout, "\t[Notification] "); + } + fprintf(stdout, "%s\n", procedures.at(i).GetProcedureName().c_str()); + } + fprintf(stdout, "\n"); } - if (version->count > 0) - { - fprintf(stdout, "jsonrpcstub version %d.%d.%d\n", JSONRPC_CPP_MAJOR_VERSION, JSONRPC_CPP_MINOR_VERSION, JSONRPC_CPP_PATCH_VERSION); - arg_freetable(argtable,sizeof(argtable)/sizeof(argtable[0])); - return true; + if (cppserver->count > 0) { + string filename; + if (cppserverfile->count > 0) + filename = cppserverfile->sval[0]; + else + filename = CPPHelper::class2Filename(cppserver->sval[0]); + if (verbose->count > 0) + fprintf(stdout, "Generating C++ Serverstub to: %s\n", filename.c_str()); + stubgenerators.push_back( + new CPPServerStubGenerator(cppserver->sval[0], procedures, filename)); } - if (inputfile->count == 0) { - fprintf(stderr, "Invalid arguments: specfile must be provided.\n"); - arg_freetable(argtable,sizeof(argtable)/sizeof(argtable[0])); - return false; + if (cppclient->count > 0) { + string filename; + if (cppclientfile->count > 0) + filename = cppclientfile->sval[0]; + else + filename = CPPHelper::class2Filename(cppclient->sval[0]); + if (verbose->count > 0) + fprintf(stdout, "Generating C++ Clientstub to: %s\n", filename.c_str()); + stubgenerators.push_back( + new CPPClientStubGenerator(cppclient->sval[0], procedures, filename)); } - try { - procedures = SpecificationParser::GetProceduresFromFile(inputfile->filename[0]); - if (verbose->count > 0) - { - fprintf(stdout, "Found %zu procedures in %s\n", procedures.size(), inputfile->filename[0]); - for (unsigned int i = 0; i < procedures.size(); ++i) { - if (procedures.at(i).GetProcedureType() == RPC_METHOD) - { - fprintf(stdout, "\t[Method] "); - } - else - { - fprintf(stdout, "\t[Notification] "); - } - fprintf(stdout, "%s\n", procedures.at(i).GetProcedureName().c_str()); - } - fprintf(stdout, "\n"); - } + if (jsclient->count > 0) { + string filename; + if (jsclientfile->count > 0) + filename = jsclientfile->sval[0]; + else + filename = JSClientStubGenerator::class2Filename(jsclient->sval[0]); - if (cppserver->count > 0) - { - string filename; - if (cppserverfile->count > 0) - filename = cppserverfile->sval[0]; - else - filename = CPPHelper::class2Filename(cppserver->sval[0]); - if (verbose->count > 0) - fprintf(stdout, "Generating C++ Serverstub to: %s\n", filename.c_str()); - stubgenerators.push_back(new CPPServerStubGenerator(cppserver->sval[0], procedures, filename)); - } + if (verbose->count > 0) + fprintf(stdout, "Generating JavaScript Clientstub to: %s\n", + filename.c_str()); + stubgenerators.push_back( + new JSClientStubGenerator(jsclient->sval[0], procedures, filename)); + } - if (cppclient->count > 0) - { - string filename; - if (cppclientfile->count > 0) - filename = cppclientfile->sval[0]; - else - filename = CPPHelper::class2Filename(cppclient->sval[0]); - if (verbose->count > 0) - fprintf(stdout, "Generating C++ Clientstub to: %s\n", filename.c_str()); - stubgenerators.push_back(new CPPClientStubGenerator(cppclient->sval[0], procedures, filename)); - } + if (pyclient->count > 0) { + string filename; + if (pyclientfile->count > 0) + filename = pyclientfile->sval[0]; + else + filename = PythonClientStubGenerator::class2Filename(pyclient->sval[0]); - if (jsclient->count > 0) - { - string filename; - if (jsclientfile->count > 0) - filename = jsclientfile->sval[0]; - else - filename = JSClientStubGenerator::class2Filename(jsclient->sval[0]); - - if (verbose->count > 0) - fprintf(stdout, "Generating JavaScript Clientstub to: %s\n", filename.c_str()); - stubgenerators.push_back(new JSClientStubGenerator(jsclient->sval[0], procedures, filename)); - } + if (verbose->count > 0) + fprintf(stdout, "Generating Python Clientstub to: %s\n", + filename.c_str()); + stubgenerators.push_back(new PythonClientStubGenerator( + pyclient->sval[0], procedures, filename)); } - catch (const JsonRpcException &ex) - { - fprintf(stderr, "%s\n", ex.what()); - arg_freetable(argtable,sizeof(argtable)/sizeof(argtable[0])); - return false; - } - arg_freetable(argtable,sizeof(argtable)/sizeof(argtable[0])); - return true; + } catch (const JsonRpcException &ex) { + fprintf(stderr, "%s\n", ex.what()); + arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); + return false; + } + arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); + return true; } -void StubGeneratorFactory::deleteStubGenerators(std::vector &stubgenerators) -{ - for (unsigned int i=0; i < stubgenerators.size(); ++i) - { - delete stubgenerators[i]; - } +void StubGeneratorFactory::deleteStubGenerators( + std::vector &stubgenerators) { + for (unsigned int i = 0; i < stubgenerators.size(); ++i) { + delete stubgenerators[i]; + } } diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 2ebea78f..6be58848 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -6,30 +6,38 @@ include_directories(..) include_directories(${CMAKE_BINARY_DIR}) add_custom_command( - OUTPUT ${CMAKE_BINARY_DIR}/gen/abstractstubserver.h - COMMAND jsonrpcstub ARGS ${CMAKE_CURRENT_SOURCE_DIR}/spec.json --cpp-server=AbstractStubServer --cpp-server-file=${CMAKE_BINARY_DIR}/gen/abstractstubserver.h - MAIN_DEPENDENCY spec.json - DEPENDS jsonrpcstub - COMMENT "Generating Server Stubfiles" - VERBATIM + OUTPUT ${CMAKE_BINARY_DIR}/gen/abstractstubserver.h + COMMAND jsonrpcstub ARGS ${CMAKE_CURRENT_SOURCE_DIR}/spec.json --cpp-server=AbstractStubServer --cpp-server-file=${CMAKE_BINARY_DIR}/gen/abstractstubserver.h + MAIN_DEPENDENCY spec.json + DEPENDS jsonrpcstub + COMMENT "Generating Server Stubfiles" + VERBATIM ) add_custom_command( - OUTPUT ${CMAKE_BINARY_DIR}/gen/stubclient.h - COMMAND jsonrpcstub ARGS ${CMAKE_CURRENT_SOURCE_DIR}/spec.json --cpp-client=StubClient --cpp-client-file=${CMAKE_BINARY_DIR}/gen/stubclient.h - MAIN_DEPENDENCY spec.json - DEPENDS jsonrpcstub - COMMENT "Generating Client Stubfile" - VERBATIM + OUTPUT ${CMAKE_BINARY_DIR}/gen/stubclient.h + COMMAND jsonrpcstub ARGS ${CMAKE_CURRENT_SOURCE_DIR}/spec.json --cpp-client=StubClient --cpp-client-file=${CMAKE_BINARY_DIR}/gen/stubclient.h + MAIN_DEPENDENCY spec.json + DEPENDS jsonrpcstub + COMMENT "Generating Client Stubfile" + VERBATIM ) if(HTTP_CLIENT AND HTTP_SERVER) - add_definitions(-DHTTP_TESTING) - file(COPY ${CMAKE_SOURCE_DIR}/src/examples/server.key ${CMAKE_SOURCE_DIR}/src/examples/server.pem DESTINATION ${CMAKE_BINARY_DIR}) - file(COPY ${CMAKE_SOURCE_DIR}/src/examples/server.key ${CMAKE_SOURCE_DIR}/src/examples/server.pem DESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) + add_definitions(-DHTTP_TESTING) + file(COPY ${CMAKE_SOURCE_DIR}/src/examples/server.key ${CMAKE_SOURCE_DIR}/src/examples/server.pem DESTINATION ${CMAKE_BINARY_DIR}) + file(COPY ${CMAKE_SOURCE_DIR}/src/examples/server.key ${CMAKE_SOURCE_DIR}/src/examples/server.pem DESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) else() - list(REMOVE_ITEM test_source "${CMAKE_CURRENT_SOURCE_DIR}/testhttpserver.cpp") + list(REMOVE_ITEM test_source "${CMAKE_CURRENT_SOURCE_DIR}/testhttpserver.cpp") +endif() + +if(REDIS_CLIENT AND REDIS_SERVER) + file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/redis.conf DESTINATION ${CMAKE_BINARY_DIR}) + file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/redis.conf DESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) + add_definitions(-DREDIS_TESTING) +else() + list(REMOVE_ITEM test_source "${CMAKE_CURRENT_SOURCE_DIR}/testredisserver.cpp") endif() if(UNIX_DOMAIN_SOCKET_SERVER AND UNIX_DOMAIN_SOCKET_CLIENT) @@ -45,12 +53,12 @@ if(TCP_SOCKET_SERVER AND TCP_SOCKET_CLIENT) endif() if(COMPILE_STUBGEN) - add_definitions(-DSTUBGEN_TESTING) - file(GLOB test_specs *.json) - file(COPY ${test_specs} DESTINATION ${CMAKE_BINARY_DIR}) - file(COPY ${test_specs} DESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) - list(APPEND test_source "${CMAKE_BINARY_DIR}/gen/abstractstubserver.h") - list(APPEND test_source "${CMAKE_BINARY_DIR}/gen/stubclient.h") + add_definitions(-DSTUBGEN_TESTING) + file(GLOB test_specs *.json) + file(COPY ${test_specs} DESTINATION ${CMAKE_BINARY_DIR}) + file(COPY ${test_specs} DESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) + list(APPEND test_source "${CMAKE_BINARY_DIR}/gen/abstractstubserver.h") + list(APPEND test_source "${CMAKE_BINARY_DIR}/gen/stubclient.h") endif() add_executable(unit_testsuite ${test_source}) @@ -59,13 +67,13 @@ target_link_libraries(unit_testsuite jsonrpcserver) target_link_libraries(unit_testsuite jsonrpcclient) if (NOT CATCH_FOUND) - # let's wait for catch files to be downloaded - add_dependencies(unit_testsuite catch) + # let's wait for catch files to be downloaded + add_dependencies(unit_testsuite catch) endif() if(COMPILE_STUBGEN) - target_link_libraries(unit_testsuite libjsonrpcstub) - add_dependencies(unit_testsuite libjsonrpcstub) + target_link_libraries(unit_testsuite libjsonrpcstub) + add_dependencies(unit_testsuite libjsonrpcstub) endif() add_test(client ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unit_testsuite "[client]") @@ -73,27 +81,31 @@ add_test(server ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unit_testsuite "[server]") add_test(NAME common WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unit_testsuite "[common]") if(HTTP_CLIENT AND HTTP_SERVER) - add_test(NAME connector_http WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unit_testsuite "[connector_http]") + add_test(NAME connector_http WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unit_testsuite "[connector_http]") +endif() + +if(REDIS_CLIENT AND REDIS_SERVER) + add_test(NAME connector_redis WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unit_testsuite "[connector_redis]") endif() if (UNIX_DOMAIN_SOCKET_CLIENT AND UNIX_DOMAIN_SOCKET_SERVER) - add_test(NAME connector_unixdomainsocket WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unit_testsuite "[connector_unixdomainsocket]") + add_test(NAME connector_unixdomainsocket WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unit_testsuite "[connector_unixdomainsocket]") endif() if (FILE_DESCRIPTOR_CLIENT AND FILE_DESCRIPTOR_SERVER) - add_test(NAME connector_filedescriptor WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unit_testsuite "[connector_filedescriptor]") + add_test(NAME connector_filedescriptor WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unit_testsuite "[connector_filedescriptor]") endif() if (TCP_SOCKET_CLIENT AND TCP_SOCKET_SERVER) - add_test(NAME connector_tcpsocket WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unit_testsuite "[connector_tcpsocket]") + add_test(NAME connector_tcpsocket WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unit_testsuite "[connector_tcpsocket]") endif() if(COMPILE_STUBGEN) - add_test(NAME stubgen WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unit_testsuite "[stubgenerator]") + add_test(NAME stubgen WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unit_testsuite "[stubgenerator]") endif() if(HTTP_CLIENT AND HTTP_SERVER AND COMPILE_STUBGEN) - add_test(NAME integration WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unit_testsuite "[integration]") + add_test(NAME integration WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unit_testsuite "[integration]") endif() add_test(NAME all WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unit_testsuite) diff --git a/src/test/mockclientconnectionhandler.cpp b/src/test/mockclientconnectionhandler.cpp index d7438ebb..13b20df7 100644 --- a/src/test/mockclientconnectionhandler.cpp +++ b/src/test/mockclientconnectionhandler.cpp @@ -8,31 +8,19 @@ ************************************************************************/ #include "mockclientconnectionhandler.h" -#if _MSC_VER #include #include -#else -#include -#endif using namespace std; using namespace jsonrpc; -MockClientConnectionHandler::MockClientConnectionHandler() : - response(""), - request(""), - timeout(0) -{ +MockClientConnectionHandler::MockClientConnectionHandler() + : response(""), request(""), timeout(0) {} -} +void MockClientConnectionHandler::HandleRequest(const std::string &request, + std::string &retValue) { + std::this_thread::sleep_for(std::chrono::microseconds(timeout * 1000)); -void MockClientConnectionHandler::HandleRequest(const std::string &request, std::string &retValue) -{ -#if _MSC_VER - std::this_thread::sleep_for(std::chrono::microseconds(timeout*1000)); -#else - usleep(timeout*1000); -#endif - this->request = request; - retValue = response; + this->request = request; + retValue = response; } diff --git a/src/test/mockclientconnector.cpp b/src/test/mockclientconnector.cpp index 3b3264d6..a037056c 100644 --- a/src/test/mockclientconnector.cpp +++ b/src/test/mockclientconnector.cpp @@ -12,33 +12,25 @@ using namespace jsonrpc; using namespace std; -MockClientConnector::MockClientConnector() -{ -} - -string MockClientConnector::GetRequest() -{ - return request; -} +MockClientConnector::MockClientConnector() {} -Json::Value MockClientConnector::GetJsonRequest() -{ - Json::Reader reader; - Json::Value result; - if (reader.parse(request, result)) - return result; - else - return Json::nullValue; +string MockClientConnector::GetRequest() { return request; } +Json::Value MockClientConnector::GetJsonRequest() { + Json::Reader reader; + Json::Value result; + if (reader.parse(request, result)) + return result; + else + return Json::nullValue; } -void MockClientConnector::SetResponse(const std::string &response) -{ - this->response = response; +void MockClientConnector::SetResponse(const std::string &response) { + this->response = response; } -void MockClientConnector::SendRPCMessage(const string &message, string &result) throw(JsonRpcException) -{ - request = message; - result = this->response; +void MockClientConnector::SendRPCMessage(const string &message, + string &result) { + request = message; + result = this->response; } diff --git a/src/test/mockclientconnector.h b/src/test/mockclientconnector.h index 4d2176fe..a706d7ef 100644 --- a/src/test/mockclientconnector.h +++ b/src/test/mockclientconnector.h @@ -23,7 +23,7 @@ namespace jsonrpc { std::string GetRequest(); Json::Value GetJsonRequest(); - virtual void SendRPCMessage(const std::string& message, std::string& result) throw(JsonRpcException); + virtual void SendRPCMessage(const std::string& message, std::string& result) ; private: std::string response; diff --git a/src/test/mockserverconnector.cpp b/src/test/mockserverconnector.cpp index 65340ada..9433929d 100644 --- a/src/test/mockserverconnector.cpp +++ b/src/test/mockserverconnector.cpp @@ -12,55 +12,35 @@ using namespace jsonrpc; using namespace std; +MockServerConnector::MockServerConnector() {} -MockServerConnector::MockServerConnector() -{ -} - -bool MockServerConnector::StartListening() -{ - return true; -} +bool MockServerConnector::StartListening() { return true; } -bool MockServerConnector::StopListening() -{ - return true; -} +bool MockServerConnector::StopListening() { return true; } -bool MockServerConnector::SendResponse(const string &response, void *addInfo) -{ - (void)addInfo; - this->response = response; - return true; +bool MockServerConnector::SetRequest(const string &request) { + this->request = request; + this->response = ""; + this->ProcessRequest(request, this->response); + return true; } -bool MockServerConnector::SetRequest(const string &request) -{ - this->request = request; - return this->OnRequest(this->request, NULL); +Json::Value MockServerConnector::GetJsonRequest() { + Json::Reader reader; + Json::Value result; + if (reader.parse(request, result)) + return result; + else + return Json::nullValue; } -Json::Value MockServerConnector::GetJsonRequest() -{ - Json::Reader reader; - Json::Value result; - if (reader.parse(request, result)) - return result; - else - return Json::nullValue; -} - -string MockServerConnector::GetResponse() -{ - return this->response; -} +string MockServerConnector::GetResponse() { return this->response; } -Json::Value MockServerConnector::GetJsonResponse() -{ - Json::Reader reader; - Json::Value result; - if (reader.parse(response, result)) - return result; - else - return Json::nullValue; +Json::Value MockServerConnector::GetJsonResponse() { + Json::Reader reader; + Json::Value result; + if (reader.parse(response, result)) + return result; + else + return Json::nullValue; } diff --git a/src/test/mockserverconnector.h b/src/test/mockserverconnector.h index 7b31e36c..18f8f44a 100644 --- a/src/test/mockserverconnector.h +++ b/src/test/mockserverconnector.h @@ -10,31 +10,28 @@ #ifndef JSONRPC_MOCKSERVERCONNECTOR_H #define JSONRPC_MOCKSERVERCONNECTOR_H -#include #include +#include namespace jsonrpc { - class MockServerConnector : public AbstractServerConnector - { - public: - MockServerConnector(); - - virtual bool StartListening(); - virtual bool StopListening(); +class MockServerConnector : public AbstractServerConnector { +public: + MockServerConnector(); - bool virtual SendResponse(const std::string& response, void* addInfo = NULL); + virtual bool StartListening(); + virtual bool StopListening(); - bool SetRequest(const std::string &request); - Json::Value GetJsonRequest(); + bool SetRequest(const std::string &request); + Json::Value GetJsonRequest(); - std::string GetResponse(); - Json::Value GetJsonResponse(); + std::string GetResponse(); + Json::Value GetJsonResponse(); - private: - std::string request; - std::string response; - }; +private: + std::string request; + std::string response; +}; } // namespace jsonrpc diff --git a/src/test/redis.conf b/src/test/redis.conf new file mode 100644 index 00000000..4c2f7a6c --- /dev/null +++ b/src/test/redis.conf @@ -0,0 +1,943 @@ +# Redis configuration file example. +# +# Note that in order to read the configuration file, Redis must be +# started with the file path as first argument: +# +# ./redis-server /path/to/redis.conf + +# Note on units: when memory size is needed, it is possible to specify +# it in the usual form of 1k 5GB 4M and so forth: +# +# 1k => 1000 bytes +# 1kb => 1024 bytes +# 1m => 1000000 bytes +# 1mb => 1024*1024 bytes +# 1g => 1000000000 bytes +# 1gb => 1024*1024*1024 bytes +# +# units are case insensitive so 1GB 1Gb 1gB are all the same. + +################################## INCLUDES ################################### + +# Include one or more other config files here. This is useful if you +# have a standard template that goes to all Redis servers but also need +# to customize a few per-server settings. Include files can include +# other files, so use this wisely. +# +# Notice option "include" won't be rewritten by command "CONFIG REWRITE" +# from admin or Redis Sentinel. Since Redis always uses the last processed +# line as value of a configuration directive, you'd better put includes +# at the beginning of this file to avoid overwriting config change at runtime. +# +# If instead you are interested in using includes to override configuration +# options, it is better to use include as the last line. +# +# include /path/to/local.conf +# include /path/to/other.conf + +################################ GENERAL ##################################### + +# By default Redis does not run as a daemon. Use 'yes' if you need it. +# Note that Redis will write a pid file in /var/run/redis.pid when daemonized. +# daemonize yes + +# When running daemonized, Redis writes a pid file in /var/run/redis.pid by +# default. You can specify a custom pid file location here. +# pidfile /var/run/redis/redis-server.pid + +# Accept connections on the specified port, default is 6379. +# If port 0 is specified Redis will not listen on a TCP socket. +port 6380 + +# TCP listen() backlog. +# +# In high requests-per-second environments you need an high backlog in order +# to avoid slow clients connections issues. Note that the Linux kernel +# will silently truncate it to the value of /proc/sys/net/core/somaxconn so +# make sure to raise both the value of somaxconn and tcp_max_syn_backlog +# in order to get the desired effect. +tcp-backlog 511 + +# By default Redis listens for connections from all the network interfaces +# available on the server. It is possible to listen to just one or multiple +# interfaces using the "bind" configuration directive, followed by one or +# more IP addresses. +# +# Examples: +# +# bind 192.168.1.100 10.0.0.1 +bind 127.0.0.1 + +# Specify the path for the Unix socket that will be used to listen for +# incoming connections. There is no default, so Redis will not listen +# on a unix socket when not specified. +# +# unixsocket /var/run/redis/redis.sock +# unixsocketperm 700 + +# Close the connection after a client is idle for N seconds (0 to disable) +timeout 0 + +# TCP keepalive. +# +# If non-zero, use SO_KEEPALIVE to send TCP ACKs to clients in absence +# of communication. This is useful for two reasons: +# +# 1) Detect dead peers. +# 2) Take the connection alive from the point of view of network +# equipment in the middle. +# +# On Linux, the specified value (in seconds) is the period used to send ACKs. +# Note that to close the connection the double of the time is needed. +# On other kernels the period depends on the kernel configuration. +# +# A reasonable value for this option is 60 seconds. +tcp-keepalive 0 + +# Specify the server verbosity level. +# This can be one of: +# debug (a lot of information, useful for development/testing) +# verbose (many rarely useful info, but not a mess like the debug level) +# notice (moderately verbose, what you want in production probably) +# warning (only very important / critical messages are logged) +loglevel verbose + +# Specify the log file name. Also the empty string can be used to force +# Redis to log on the standard output. Note that if you use standard +# output for logging but daemonize, logs will be sent to /dev/null +# logfile /tmp/redis-server.log + +# To enable logging to the system logger, just set 'syslog-enabled' to yes, +# and optionally update the other syslog parameters to suit your needs. +# syslog-enabled no + +# Specify the syslog identity. +# syslog-ident redis + +# Specify the syslog facility. Must be USER or between LOCAL0-LOCAL7. +# syslog-facility local0 + +# Set the number of databases. The default database is DB 0, you can select +# a different one on a per-connection basis using SELECT where +# dbid is a number between 0 and 'databases'-1 +databases 1 + +################################ SNAPSHOTTING ################################ +# +# Save the DB on disk: +# +# save +# +# Will save the DB if both the given number of seconds and the given +# number of write operations against the DB occurred. +# +# In the example below the behaviour will be to save: +# after 900 sec (15 min) if at least 1 key changed +# after 300 sec (5 min) if at least 10 keys changed +# after 60 sec if at least 10000 keys changed +# +# Note: you can disable saving completely by commenting out all "save" lines. +# +# It is also possible to remove all the previously configured save +# points by adding a save directive with a single empty string argument +# like in the following example: +# +# save "" + +# save 900 1 +# save 300 10 +# save 60 10000 + +# By default Redis will stop accepting writes if RDB snapshots are enabled +# (at least one save point) and the latest background save failed. +# This will make the user aware (in a hard way) that data is not persisting +# on disk properly, otherwise chances are that no one will notice and some +# disaster will happen. +# +# If the background saving process will start working again Redis will +# automatically allow writes again. +# +# However if you have setup your proper monitoring of the Redis server +# and persistence, you may want to disable this feature so that Redis will +# continue to work as usual even if there are problems with disk, +# permissions, and so forth. +stop-writes-on-bgsave-error yes + +# Compress string objects using LZF when dump .rdb databases? +# For default that's set to 'yes' as it's almost always a win. +# If you want to save some CPU in the saving child set it to 'no' but +# the dataset will likely be bigger if you have compressible values or keys. +rdbcompression yes + +# Since version 5 of RDB a CRC64 checksum is placed at the end of the file. +# This makes the format more resistant to corruption but there is a performance +# hit to pay (around 10%) when saving and loading RDB files, so you can disable it +# for maximum performances. +# +# RDB files created with checksum disabled have a checksum of zero that will +# tell the loading code to skip the check. +rdbchecksum yes + +# The filename where to dump the DB +dbfilename dump.rdb + +# The working directory. +# +# The DB will be written inside this directory, with the filename specified +# above using the 'dbfilename' configuration directive. +# +# The Append Only File will also be created inside this directory. +# +# Note that you must specify a directory here, not a file name. +dir /tmp + +################################# REPLICATION ################################# + +# Master-Slave replication. Use slaveof to make a Redis instance a copy of +# another Redis server. A few things to understand ASAP about Redis replication. +# +# 1) Redis replication is asynchronous, but you can configure a master to +# stop accepting writes if it appears to be not connected with at least +# a given number of slaves. +# 2) Redis slaves are able to perform a partial resynchronization with the +# master if the replication link is lost for a relatively small amount of +# time. You may want to configure the replication backlog size (see the next +# sections of this file) with a sensible value depending on your needs. +# 3) Replication is automatic and does not need user intervention. After a +# network partition slaves automatically try to reconnect to masters +# and resynchronize with them. +# +# slaveof + +# If the master is password protected (using the "requirepass" configuration +# directive below) it is possible to tell the slave to authenticate before +# starting the replication synchronization process, otherwise the master will +# refuse the slave request. +# +# masterauth + +# When a slave loses its connection with the master, or when the replication +# is still in progress, the slave can act in two different ways: +# +# 1) if slave-serve-stale-data is set to 'yes' (the default) the slave will +# still reply to client requests, possibly with out of date data, or the +# data set may just be empty if this is the first synchronization. +# +# 2) if slave-serve-stale-data is set to 'no' the slave will reply with +# an error "SYNC with master in progress" to all the kind of commands +# but to INFO and SLAVEOF. +# +slave-serve-stale-data yes + +# You can configure a slave instance to accept writes or not. Writing against +# a slave instance may be useful to store some ephemeral data (because data +# written on a slave will be easily deleted after resync with the master) but +# may also cause problems if clients are writing to it because of a +# misconfiguration. +# +# Since Redis 2.6 by default slaves are read-only. +# +# Note: read only slaves are not designed to be exposed to untrusted clients +# on the internet. It's just a protection layer against misuse of the instance. +# Still a read only slave exports by default all the administrative commands +# such as CONFIG, DEBUG, and so forth. To a limited extent you can improve +# security of read only slaves using 'rename-command' to shadow all the +# administrative / dangerous commands. +slave-read-only yes + +# Replication SYNC strategy: disk or socket. +# +# ------------------------------------------------------- +# WARNING: DISKLESS REPLICATION IS EXPERIMENTAL CURRENTLY +# ------------------------------------------------------- +# +# New slaves and reconnecting slaves that are not able to continue the replication +# process just receiving differences, need to do what is called a "full +# synchronization". An RDB file is transmitted from the master to the slaves. +# The transmission can happen in two different ways: +# +# 1) Disk-backed: The Redis master creates a new process that writes the RDB +# file on disk. Later the file is transferred by the parent +# process to the slaves incrementally. +# 2) Diskless: The Redis master creates a new process that directly writes the +# RDB file to slave sockets, without touching the disk at all. +# +# With disk-backed replication, while the RDB file is generated, more slaves +# can be queued and served with the RDB file as soon as the current child producing +# the RDB file finishes its work. With diskless replication instead once +# the transfer starts, new slaves arriving will be queued and a new transfer +# will start when the current one terminates. +# +# When diskless replication is used, the master waits a configurable amount of +# time (in seconds) before starting the transfer in the hope that multiple slaves +# will arrive and the transfer can be parallelized. +# +# With slow disks and fast (large bandwidth) networks, diskless replication +# works better. +# repl-diskless-sync no + +# When diskless replication is enabled, it is possible to configure the delay +# the server waits in order to spawn the child that transfers the RDB via socket +# to the slaves. +# +# This is important since once the transfer starts, it is not possible to serve +# new slaves arriving, that will be queued for the next RDB transfer, so the server +# waits a delay in order to let more slaves arrive. +# +# The delay is specified in seconds, and by default is 5 seconds. To disable +# it entirely just set it to 0 seconds and the transfer will start ASAP. +# repl-diskless-sync-delay 5 + +# Slaves send PINGs to server in a predefined interval. It's possible to change +# this interval with the repl_ping_slave_period option. The default value is 10 +# seconds. +# +# repl-ping-slave-period 10 + +# The following option sets the replication timeout for: +# +# 1) Bulk transfer I/O during SYNC, from the point of view of slave. +# 2) Master timeout from the point of view of slaves (data, pings). +# 3) Slave timeout from the point of view of masters (REPLCONF ACK pings). +# +# It is important to make sure that this value is greater than the value +# specified for repl-ping-slave-period otherwise a timeout will be detected +# every time there is low traffic between the master and the slave. +# +# repl-timeout 60 + +# Disable TCP_NODELAY on the slave socket after SYNC? +# +# If you select "yes" Redis will use a smaller number of TCP packets and +# less bandwidth to send data to slaves. But this can add a delay for +# the data to appear on the slave side, up to 40 milliseconds with +# Linux kernels using a default configuration. +# +# If you select "no" the delay for data to appear on the slave side will +# be reduced but more bandwidth will be used for replication. +# +# By default we optimize for low latency, but in very high traffic conditions +# or when the master and slaves are many hops away, turning this to "yes" may +# be a good idea. +# repl-disable-tcp-nodelay no + +# Set the replication backlog size. The backlog is a buffer that accumulates +# slave data when slaves are disconnected for some time, so that when a slave +# wants to reconnect again, often a full resync is not needed, but a partial +# resync is enough, just passing the portion of data the slave missed while +# disconnected. +# +# The bigger the replication backlog, the longer the time the slave can be +# disconnected and later be able to perform a partial resynchronization. +# +# The backlog is only allocated once there is at least a slave connected. +# +# repl-backlog-size 1mb + +# After a master has no longer connected slaves for some time, the backlog +# will be freed. The following option configures the amount of seconds that +# need to elapse, starting from the time the last slave disconnected, for +# the backlog buffer to be freed. +# +# A value of 0 means to never release the backlog. +# +# repl-backlog-ttl 3600 + +# The slave priority is an integer number published by Redis in the INFO output. +# It is used by Redis Sentinel in order to select a slave to promote into a +# master if the master is no longer working correctly. +# +# A slave with a low priority number is considered better for promotion, so +# for instance if there are three slaves with priority 10, 100, 25 Sentinel will +# pick the one with priority 10, that is the lowest. +# +# However a special priority of 0 marks the slave as not able to perform the +# role of master, so a slave with priority of 0 will never be selected by +# Redis Sentinel for promotion. +# +# By default the priority is 100. +# slave-priority 100 + +# It is possible for a master to stop accepting writes if there are less than +# N slaves connected, having a lag less or equal than M seconds. +# +# The N slaves need to be in "online" state. +# +# The lag in seconds, that must be <= the specified value, is calculated from +# the last ping received from the slave, that is usually sent every second. +# +# This option does not GUARANTEE that N replicas will accept the write, but +# will limit the window of exposure for lost writes in case not enough slaves +# are available, to the specified number of seconds. +# +# For example to require at least 3 slaves with a lag <= 10 seconds use: +# +# min-slaves-to-write 3 +# min-slaves-max-lag 10 +# +# Setting one or the other to 0 disables the feature. +# +# By default min-slaves-to-write is set to 0 (feature disabled) and +# min-slaves-max-lag is set to 10. + +################################## SECURITY ################################### + +# Require clients to issue AUTH before processing any other +# commands. This might be useful in environments in which you do not trust +# others with access to the host running redis-server. +# +# This should stay commented out for backward compatibility and because most +# people do not need auth (e.g. they run their own servers). +# +# Warning: since Redis is pretty fast an outside user can try up to +# 150k passwords per second against a good box. This means that you should +# use a very strong password otherwise it will be very easy to break. +# +# requirepass foobared + +# Command renaming. +# +# It is possible to change the name of dangerous commands in a shared +# environment. For instance the CONFIG command may be renamed into something +# hard to guess so that it will still be available for internal-use tools +# but not available for general clients. +# +# Example: +# +# rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52 +# +# It is also possible to completely kill a command by renaming it into +# an empty string: +# +# rename-command CONFIG "" +# +# Please note that changing the name of commands that are logged into the +# AOF file or transmitted to slaves may cause problems. + +################################### LIMITS #################################### + +# Set the max number of connected clients at the same time. By default +# this limit is set to 10000 clients, however if the Redis server is not +# able to configure the process file limit to allow for the specified limit +# the max number of allowed clients is set to the current file limit +# minus 32 (as Redis reserves a few file descriptors for internal uses). +# +# Once the limit is reached Redis will close all the new connections sending +# an error 'max number of clients reached'. +# +# maxclients 10000 + +# Don't use more memory than the specified amount of bytes. +# When the memory limit is reached Redis will try to remove keys +# according to the eviction policy selected (see maxmemory-policy). +# +# If Redis can't remove keys according to the policy, or if the policy is +# set to 'noeviction', Redis will start to reply with errors to commands +# that would use more memory, like SET, LPUSH, and so on, and will continue +# to reply to read-only commands like GET. +# +# This option is usually useful when using Redis as an LRU cache, or to set +# a hard memory limit for an instance (using the 'noeviction' policy). +# +# WARNING: If you have slaves attached to an instance with maxmemory on, +# the size of the output buffers needed to feed the slaves are subtracted +# from the used memory count, so that network problems / resyncs will +# not trigger a loop where keys are evicted, and in turn the output +# buffer of slaves is full with DELs of keys evicted triggering the deletion +# of more keys, and so forth until the database is completely emptied. +# +# In short... if you have slaves attached it is suggested that you set a lower +# limit for maxmemory so that there is some free RAM on the system for slave +# output buffers (but this is not needed if the policy is 'noeviction'). +# +# maxmemory + +# MAXMEMORY POLICY: how Redis will select what to remove when maxmemory +# is reached. You can select among five behaviors: +# +# volatile-lru -> remove the key with an expire set using an LRU algorithm +# allkeys-lru -> remove any key according to the LRU algorithm +# volatile-random -> remove a random key with an expire set +# allkeys-random -> remove a random key, any key +# volatile-ttl -> remove the key with the nearest expire time (minor TTL) +# noeviction -> don't expire at all, just return an error on write operations +# +# Note: with any of the above policies, Redis will return an error on write +# operations, when there are no suitable keys for eviction. +# +# At the date of writing these commands are: set setnx setex append +# incr decr rpush lpush rpushx lpushx linsert lset rpoplpush sadd +# sinter sinterstore sunion sunionstore sdiff sdiffstore zadd zincrby +# zunionstore zinterstore hset hsetnx hmset hincrby incrby decrby +# getset mset msetnx exec sort +# +# The default is: +# +# maxmemory-policy noeviction + +# LRU and minimal TTL algorithms are not precise algorithms but approximated +# algorithms (in order to save memory), so you can tune it for speed or +# accuracy. For default Redis will check five keys and pick the one that was +# used less recently, you can change the sample size using the following +# configuration directive. +# +# The default of 5 produces good enough results. 10 Approximates very closely +# true LRU but costs a bit more CPU. 3 is very fast but not very accurate. +# +# maxmemory-samples 5 + +############################## APPEND ONLY MODE ############################### + +# By default Redis asynchronously dumps the dataset on disk. This mode is +# good enough in many applications, but an issue with the Redis process or +# a power outage may result into a few minutes of writes lost (depending on +# the configured save points). +# +# The Append Only File is an alternative persistence mode that provides +# much better durability. For instance using the default data fsync policy +# (see later in the config file) Redis can lose just one second of writes in a +# dramatic event like a server power outage, or a single write if something +# wrong with the Redis process itself happens, but the operating system is +# still running correctly. +# +# AOF and RDB persistence can be enabled at the same time without problems. +# If the AOF is enabled on startup Redis will load the AOF, that is the file +# with the better durability guarantees. +# +# Please check http://redis.io/topics/persistence for more information. + +appendonly no + +# The name of the append only file (default: "appendonly.aof") + +appendfilename "appendonly.aof" + +# The fsync() call tells the Operating System to actually write data on disk +# instead of waiting for more data in the output buffer. Some OS will really flush +# data on disk, some other OS will just try to do it ASAP. +# +# Redis supports three different modes: +# +# no: don't fsync, just let the OS flush the data when it wants. Faster. +# always: fsync after every write to the append only log. Slow, Safest. +# everysec: fsync only one time every second. Compromise. +# +# The default is "everysec", as that's usually the right compromise between +# speed and data safety. It's up to you to understand if you can relax this to +# "no" that will let the operating system flush the output buffer when +# it wants, for better performances (but if you can live with the idea of +# some data loss consider the default persistence mode that's snapshotting), +# or on the contrary, use "always" that's very slow but a bit safer than +# everysec. +# +# More details please check the following article: +# http://antirez.com/post/redis-persistence-demystified.html +# +# If unsure, use "everysec". + +# appendfsync always +appendfsync everysec +# appendfsync no + +# When the AOF fsync policy is set to always or everysec, and a background +# saving process (a background save or AOF log background rewriting) is +# performing a lot of I/O against the disk, in some Linux configurations +# Redis may block too long on the fsync() call. Note that there is no fix for +# this currently, as even performing fsync in a different thread will block +# our synchronous write(2) call. +# +# In order to mitigate this problem it's possible to use the following option +# that will prevent fsync() from being called in the main process while a +# BGSAVE or BGREWRITEAOF is in progress. +# +# This means that while another child is saving, the durability of Redis is +# the same as "appendfsync none". In practical terms, this means that it is +# possible to lose up to 30 seconds of log in the worst scenario (with the +# default Linux settings). +# +# If you have latency problems turn this to "yes". Otherwise leave it as +# "no" that is the safest pick from the point of view of durability. + +no-appendfsync-on-rewrite no + +# Automatic rewrite of the append only file. +# Redis is able to automatically rewrite the log file implicitly calling +# BGREWRITEAOF when the AOF log size grows by the specified percentage. +# +# This is how it works: Redis remembers the size of the AOF file after the +# latest rewrite (if no rewrite has happened since the restart, the size of +# the AOF at startup is used). +# +# This base size is compared to the current size. If the current size is +# bigger than the specified percentage, the rewrite is triggered. Also +# you need to specify a minimal size for the AOF file to be rewritten, this +# is useful to avoid rewriting the AOF file even if the percentage increase +# is reached but it is still pretty small. +# +# Specify a percentage of zero in order to disable the automatic AOF +# rewrite feature. + +auto-aof-rewrite-percentage 100 +auto-aof-rewrite-min-size 64mb + +# An AOF file may be found to be truncated at the end during the Redis +# startup process, when the AOF data gets loaded back into memory. +# This may happen when the system where Redis is running +# crashes, especially when an ext4 filesystem is mounted without the +# data=ordered option (however this can't happen when Redis itself +# crashes or aborts but the operating system still works correctly). +# +# Redis can either exit with an error when this happens, or load as much +# data as possible (the default now) and start if the AOF file is found +# to be truncated at the end. The following option controls this behavior. +# +# If aof-load-truncated is set to yes, a truncated AOF file is loaded and +# the Redis server starts emitting a log to inform the user of the event. +# Otherwise if the option is set to no, the server aborts with an error +# and refuses to start. When the option is set to no, the user requires +# to fix the AOF file using the "redis-check-aof" utility before to restart +# the server. +# +# Note that if the AOF file will be found to be corrupted in the middle +# the server will still exit with an error. This option only applies when +# Redis will try to read more data from the AOF file but not enough bytes +# will be found. +aof-load-truncated yes + +################################ LUA SCRIPTING ############################### + +# Max execution time of a Lua script in milliseconds. +# +# If the maximum execution time is reached Redis will log that a script is +# still in execution after the maximum allowed time and will start to +# reply to queries with an error. +# +# When a long running script exceeds the maximum execution time only the +# SCRIPT KILL and SHUTDOWN NOSAVE commands are available. The first can be +# used to stop a script that did not yet called write commands. The second +# is the only way to shut down the server in the case a write command was +# already issued by the script but the user doesn't want to wait for the natural +# termination of the script. +# +# Set it to 0 or a negative value for unlimited execution without warnings. +lua-time-limit 5000 + +################################ REDIS CLUSTER ############################### +# +# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +# WARNING EXPERIMENTAL: Redis Cluster is considered to be stable code, however +# in order to mark it as "mature" we need to wait for a non trivial percentage +# of users to deploy it in production. +# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +# +# Normal Redis instances can't be part of a Redis Cluster; only nodes that are +# started as cluster nodes can. In order to start a Redis instance as a +# cluster node enable the cluster support uncommenting the following: +# +# cluster-enabled yes + +# Every cluster node has a cluster configuration file. This file is not +# intended to be edited by hand. It is created and updated by Redis nodes. +# Every Redis Cluster node requires a different cluster configuration file. +# Make sure that instances running in the same system do not have +# overlapping cluster configuration file names. +# +# cluster-config-file nodes-6379.conf + +# Cluster node timeout is the amount of milliseconds a node must be unreachable +# for it to be considered in failure state. +# Most other internal time limits are multiple of the node timeout. +# +# cluster-node-timeout 15000 + +# A slave of a failing master will avoid to start a failover if its data +# looks too old. +# +# There is no simple way for a slave to actually have a exact measure of +# its "data age", so the following two checks are performed: +# +# 1) If there are multiple slaves able to failover, they exchange messages +# in order to try to give an advantage to the slave with the best +# replication offset (more data from the master processed). +# Slaves will try to get their rank by offset, and apply to the start +# of the failover a delay proportional to their rank. +# +# 2) Every single slave computes the time of the last interaction with +# its master. This can be the last ping or command received (if the master +# is still in the "connected" state), or the time that elapsed since the +# disconnection with the master (if the replication link is currently down). +# If the last interaction is too old, the slave will not try to failover +# at all. +# +# The point "2" can be tuned by user. Specifically a slave will not perform +# the failover if, since the last interaction with the master, the time +# elapsed is greater than: +# +# (node-timeout * slave-validity-factor) + repl-ping-slave-period +# +# So for example if node-timeout is 30 seconds, and the slave-validity-factor +# is 10, and assuming a default repl-ping-slave-period of 10 seconds, the +# slave will not try to failover if it was not able to talk with the master +# for longer than 310 seconds. +# +# A large slave-validity-factor may allow slaves with too old data to failover +# a master, while a too small value may prevent the cluster from being able to +# elect a slave at all. +# +# For maximum availability, it is possible to set the slave-validity-factor +# to a value of 0, which means, that slaves will always try to failover the +# master regardless of the last time they interacted with the master. +# (However they'll always try to apply a delay proportional to their +# offset rank). +# +# Zero is the only value able to guarantee that when all the partitions heal +# the cluster will always be able to continue. +# +# cluster-slave-validity-factor 10 + +# Cluster slaves are able to migrate to orphaned masters, that are masters +# that are left without working slaves. This improves the cluster ability +# to resist to failures as otherwise an orphaned master can't be failed over +# in case of failure if it has no working slaves. +# +# Slaves migrate to orphaned masters only if there are still at least a +# given number of other working slaves for their old master. This number +# is the "migration barrier". A migration barrier of 1 means that a slave +# will migrate only if there is at least 1 other working slave for its master +# and so forth. It usually reflects the number of slaves you want for every +# master in your cluster. +# +# Default is 1 (slaves migrate only if their masters remain with at least +# one slave). To disable migration just set it to a very large value. +# A value of 0 can be set but is useful only for debugging and dangerous +# in production. +# +# cluster-migration-barrier 1 + +# By default Redis Cluster nodes stop accepting queries if they detect there +# is at least an hash slot uncovered (no available node is serving it). +# This way if the cluster is partially down (for example a range of hash slots +# are no longer covered) all the cluster becomes, eventually, unavailable. +# It automatically returns available as soon as all the slots are covered again. +# +# However sometimes you want the subset of the cluster which is working, +# to continue to accept queries for the part of the key space that is still +# covered. In order to do so, just set the cluster-require-full-coverage +# option to no. +# +# cluster-require-full-coverage yes + +# In order to setup your cluster make sure to read the documentation +# available at http://redis.io web site. + +################################## SLOW LOG ################################### + +# The Redis Slow Log is a system to log queries that exceeded a specified +# execution time. The execution time does not include the I/O operations +# like talking with the client, sending the reply and so forth, +# but just the time needed to actually execute the command (this is the only +# stage of command execution where the thread is blocked and can not serve +# other requests in the meantime). +# +# You can configure the slow log with two parameters: one tells Redis +# what is the execution time, in microseconds, to exceed in order for the +# command to get logged, and the other parameter is the length of the +# slow log. When a new command is logged the oldest one is removed from the +# queue of logged commands. + +# The following time is expressed in microseconds, so 1000000 is equivalent +# to one second. Note that a negative number disables the slow log, while +# a value of zero forces the logging of every command. +slowlog-log-slower-than 10000 + +# There is no limit to this length. Just be aware that it will consume memory. +# You can reclaim memory used by the slow log with SLOWLOG RESET. +slowlog-max-len 128 + +################################ LATENCY MONITOR ############################## + +# The Redis latency monitoring subsystem samples different operations +# at runtime in order to collect data related to possible sources of +# latency of a Redis instance. +# +# Via the LATENCY command this information is available to the user that can +# print graphs and obtain reports. +# +# The system only logs operations that were performed in a time equal or +# greater than the amount of milliseconds specified via the +# latency-monitor-threshold configuration directive. When its value is set +# to zero, the latency monitor is turned off. +# +# By default latency monitoring is disabled since it is mostly not needed +# if you don't have latency issues, and collecting data has a performance +# impact, that while very small, can be measured under big load. Latency +# monitoring can easily be enabled at runtime using the command +# "CONFIG SET latency-monitor-threshold " if needed. +latency-monitor-threshold 0 + +############################# EVENT NOTIFICATION ############################## + +# Redis can notify Pub/Sub clients about events happening in the key space. +# This feature is documented at http://redis.io/topics/notifications +# +# For instance if keyspace events notification is enabled, and a client +# performs a DEL operation on key "foo" stored in the Database 0, two +# messages will be published via Pub/Sub: +# +# PUBLISH __keyspace@0__:foo del +# PUBLISH __keyevent@0__:del foo +# +# It is possible to select the events that Redis will notify among a set +# of classes. Every class is identified by a single character: +# +# K Keyspace events, published with __keyspace@__ prefix. +# E Keyevent events, published with __keyevent@__ prefix. +# g Generic commands (non-type specific) like DEL, EXPIRE, RENAME, ... +# $ String commands +# l List commands +# s Set commands +# h Hash commands +# z Sorted set commands +# x Expired events (events generated every time a key expires) +# e Evicted events (events generated when a key is evicted for maxmemory) +# A Alias for g$lshzxe, so that the "AKE" string means all the events. +# +# The "notify-keyspace-events" takes as argument a string that is composed +# of zero or multiple characters. The empty string means that notifications +# are disabled. +# +# Example: to enable list and generic events, from the point of view of the +# event name, use: +# +# notify-keyspace-events Elg +# +# Example 2: to get the stream of the expired keys subscribing to channel +# name __keyevent@0__:expired use: +# +# notify-keyspace-events Ex +# +# By default all notifications are disabled because most users don't need +# this feature and the feature has some overhead. Note that if you don't +# specify at least one of K or E, no events will be delivered. +notify-keyspace-events "" + +############################### ADVANCED CONFIG ############################### + +# Hashes are encoded using a memory efficient data structure when they have a +# small number of entries, and the biggest entry does not exceed a given +# threshold. These thresholds can be configured using the following directives. +hash-max-ziplist-entries 512 +hash-max-ziplist-value 64 + +# Similarly to hashes, small lists are also encoded in a special way in order +# to save a lot of space. The special representation is only used when +# you are under the following limits: +list-max-ziplist-entries 512 +list-max-ziplist-value 64 + +# Sets have a special encoding in just one case: when a set is composed +# of just strings that happen to be integers in radix 10 in the range +# of 64 bit signed integers. +# The following configuration setting sets the limit in the size of the +# set in order to use this special memory saving encoding. +set-max-intset-entries 512 + +# Similarly to hashes and lists, sorted sets are also specially encoded in +# order to save a lot of space. This encoding is only used when the length and +# elements of a sorted set are below the following limits: +zset-max-ziplist-entries 128 +zset-max-ziplist-value 64 + +# HyperLogLog sparse representation bytes limit. The limit includes the +# 16 bytes header. When an HyperLogLog using the sparse representation crosses +# this limit, it is converted into the dense representation. +# +# A value greater than 16000 is totally useless, since at that point the +# dense representation is more memory efficient. +# +# The suggested value is ~ 3000 in order to have the benefits of +# the space efficient encoding without slowing down too much PFADD, +# which is O(N) with the sparse encoding. The value can be raised to +# ~ 10000 when CPU is not a concern, but space is, and the data set is +# composed of many HyperLogLogs with cardinality in the 0 - 15000 range. +hll-sparse-max-bytes 3000 + +# Active rehashing uses 1 millisecond every 100 milliseconds of CPU time in +# order to help rehashing the main Redis hash table (the one mapping top-level +# keys to values). The hash table implementation Redis uses (see dict.c) +# performs a lazy rehashing: the more operation you run into a hash table +# that is rehashing, the more rehashing "steps" are performed, so if the +# server is idle the rehashing is never complete and some more memory is used +# by the hash table. +# +# The default is to use this millisecond 10 times every second in order to +# actively rehash the main dictionaries, freeing memory when possible. +# +# If unsure: +# use "activerehashing no" if you have hard latency requirements and it is +# not a good thing in your environment that Redis can reply from time to time +# to queries with 2 milliseconds delay. +# +# use "activerehashing yes" if you don't have such hard requirements but +# want to free memory asap when possible. +activerehashing yes + +# The client output buffer limits can be used to force disconnection of clients +# that are not reading data from the server fast enough for some reason (a +# common reason is that a Pub/Sub client can't consume messages as fast as the +# publisher can produce them). +# +# The limit can be set differently for the three different classes of clients: +# +# normal -> normal clients including MONITOR clients +# slave -> slave clients +# pubsub -> clients subscribed to at least one pubsub channel or pattern +# +# The syntax of every client-output-buffer-limit directive is the following: +# +# client-output-buffer-limit +# +# A client is immediately disconnected once the hard limit is reached, or if +# the soft limit is reached and remains reached for the specified number of +# seconds (continuously). +# So for instance if the hard limit is 32 megabytes and the soft limit is +# 16 megabytes / 10 seconds, the client will get disconnected immediately +# if the size of the output buffers reach 32 megabytes, but will also get +# disconnected if the client reaches 16 megabytes and continuously overcomes +# the limit for 10 seconds. +# +# By default normal clients are not limited because they don't receive data +# without asking (in a push way), but just after a request, so only +# asynchronous clients may create a scenario where data is requested faster +# than it can read. +# +# Instead there is a default limit for pubsub and slave clients, since +# subscribers and slaves receive data in a push fashion. +# +# Both the hard or the soft limit can be disabled by setting them to zero. +client-output-buffer-limit normal 0 0 0 +client-output-buffer-limit slave 256mb 64mb 60 +client-output-buffer-limit pubsub 32mb 8mb 60 + +# Redis calls an internal function to perform many background tasks, like +# closing connections of clients in timeout, purging expired keys that are +# never requested, and so forth. +# +# Not all tasks are performed with the same frequency, but Redis checks for +# tasks to perform according to the specified "hz" value. +# +# By default "hz" is set to 10. Raising the value will use more CPU when +# Redis is idle, but at the same time will make Redis more responsive when +# there are many keys expiring at the same time, and timeouts may be +# handled with more precision. +# +# The range is between 1 and 500, however a value over 100 is usually not +# a good idea. Most users should use the default of 10 and raise this up to +# 100 only in environments where very low latency is required. +hz 10 + +# When a child rewrites the AOF file, if the following option is enabled +# the file will be fsync-ed every 32 MB of data generated. This is useful +# in order to commit the file to the disk more incrementally and avoid +# big latency spikes. +aof-rewrite-incremental-fsync yes diff --git a/src/test/test_client.cpp b/src/test/test_client.cpp index 0e0c054a..4cee3f3d 100644 --- a/src/test/test_client.cpp +++ b/src/test/test_client.cpp @@ -7,10 +7,10 @@ * @license See attached LICENSE.txt ************************************************************************/ +#include "checkexception.h" +#include "mockclientconnector.h" #include #include -#include "mockclientconnector.h" -#include "checkexception.h" #define TEST_MODULE "[client]" @@ -18,228 +18,246 @@ using namespace jsonrpc; using namespace std; namespace testclient { - bool check_exception1(JsonRpcException const&ex) - { - return ex.GetCode() == Errors::ERROR_RPC_JSON_PARSE_ERROR; - } - - bool check_exception2(JsonRpcException const&ex) - { - return ex.GetCode() == Errors::ERROR_CLIENT_INVALID_RESPONSE; - } - - bool check_exception3(JsonRpcException const&ex) - { - return ex.GetCode() == Errors::ERROR_RPC_INVALID_REQUEST && ex.GetData().size() == 2; - } - - struct F { - MockClientConnector c; - Client client; - Json::Value params; - - F() : client(c, JSONRPC_CLIENT_V2) - { - } - }; - - struct F1 { - MockClientConnector c; - Client client; - Json::Value params; - - F1() : client(c, JSONRPC_CLIENT_V1) - { - } - }; +bool check_exception1(JsonRpcException const &ex) { + return ex.GetCode() == Errors::ERROR_RPC_JSON_PARSE_ERROR; } -using namespace testclient; -TEST_CASE_METHOD(F, "test_client_id", TEST_MODULE) -{ - params.append(33); - c.SetResponse("{\"jsonrpc\":\"2.0\", \"id\": \"1\", \"result\": 23}"); - Json::Value response = client.CallMethod("abcd", params); - CHECK(response.asInt() == 23); - - c.SetResponse("{\"jsonrpc\":\"2.0\", \"id\": 1, \"result\": 24}"); - response = client.CallMethod("abcd", params); - CHECK(response.asInt() == 24); +bool check_exception2(JsonRpcException const &ex) { + return ex.GetCode() == Errors::ERROR_CLIENT_INVALID_RESPONSE; } -TEST_CASE_METHOD(F, "test_client_v2_method_success", TEST_MODULE) -{ - params.append(23); - c.SetResponse("{\"jsonrpc\":\"2.0\", \"id\": 1, \"result\": 23}"); - Json::Value response = client.CallMethod("abcd", params); - Json::Value v = c.GetJsonRequest(); - - CHECK(v["method"].asString() == "abcd"); - CHECK(v["params"][0].asInt() == 23); - CHECK(v["jsonrpc"].asString() == "2.0"); - CHECK(v["id"].asInt() == 1); +bool check_exception3(JsonRpcException const &ex) { + return ex.GetCode() == Errors::ERROR_RPC_INVALID_REQUEST && + ex.GetData().size() == 2; } -TEST_CASE_METHOD(F, "test_client_v2_notification_success", TEST_MODULE) -{ - params.append(23); - client.CallNotification("abcd", params); - Json::Value v = c.GetJsonRequest(); +struct F { + MockClientConnector c; + Client client; + Json::Value params; - CHECK(v["method"].asString() == "abcd"); - CHECK(v["params"][0].asInt() == 23); - CHECK(v["jsonrpc"].asString() == "2.0"); - CHECK(v.isMember("id") == false); -} + F() : client(c, JSONRPC_CLIENT_V2) {} +}; -TEST_CASE_METHOD(F, "test_client_v2_errorresponse", TEST_MODULE) -{ - c.SetResponse("{\"jsonrpc\":\"2.0\", \"error\": {\"code\": -32600, \"message\": \"Invalid Request\", \"data\": [1,2]}, \"id\": null}"); - CHECK_EXCEPTION_TYPE(client.CallMethod("abcd", Json::nullValue), JsonRpcException, check_exception3); -} +struct F1 { + MockClientConnector c; + Client client; + Json::Value params; -TEST_CASE_METHOD(F, "test_client_v2_invalidjson", TEST_MODULE) -{ - c.SetResponse("{\"method\":234"); - CHECK_EXCEPTION_TYPE(client.CallMethod("abcd", Json::nullValue), JsonRpcException, check_exception1); -} + F1() : client(c, JSONRPC_CLIENT_V1) {} +}; +} // namespace testclient +using namespace testclient; + +TEST_CASE_METHOD(F, "test_client_id", TEST_MODULE) { + params.append(33); + c.SetResponse("{\"jsonrpc\":\"2.0\", \"id\": \"1\", \"result\": 23}"); + Json::Value response = client.CallMethod("abcd", params); + CHECK(response.asInt() == 23); -TEST_CASE_METHOD(F, "test_client_v2_invalidresponse", TEST_MODULE) -{ - c.SetResponse("{\"jsonrpc\":\"2.0\", \"id\": 1, \"resulto\": 23}"); - CHECK_EXCEPTION_TYPE(client.CallMethod("abcd", Json::nullValue), JsonRpcException, check_exception2); - c.SetResponse("{\"jsonrpc\":\"2.0\", \"id2\": 1, \"result\": 23}"); - CHECK_EXCEPTION_TYPE(client.CallMethod("abcd", Json::nullValue), JsonRpcException, check_exception2); - c.SetResponse("{\"jsonrpc\":\"1.0\", \"id\": 1, \"result\": 23}"); - CHECK_EXCEPTION_TYPE(client.CallMethod("abcd", Json::nullValue), JsonRpcException, check_exception2); - c.SetResponse("{\"id\": 1, \"result\": 23}"); - CHECK_EXCEPTION_TYPE(client.CallMethod("abcd", Json::nullValue), JsonRpcException, check_exception2); - c.SetResponse("{\"jsonrpc\":\"2.0\", \"id\": 1, \"result\": 23, \"error\": {}}"); - CHECK_EXCEPTION_TYPE(client.CallMethod("abcd", Json::nullValue), JsonRpcException, check_exception2); - c.SetResponse("{\"jsonrpc\":\"2.0\", \"id\": 1, \"error\": {}}"); - CHECK_EXCEPTION_TYPE(client.CallMethod("abcd", Json::nullValue), JsonRpcException, check_exception2); - c.SetResponse("{\"jsonrpc\":\"2.0\", \"result\": 23}"); - CHECK_EXCEPTION_TYPE(client.CallMethod("abcd", Json::nullValue), JsonRpcException, check_exception2); - c.SetResponse("{}"); - CHECK_EXCEPTION_TYPE(client.CallMethod("abcd", Json::nullValue), JsonRpcException, check_exception2); - c.SetResponse("[]"); - CHECK_EXCEPTION_TYPE(client.CallMethod("abcd", Json::nullValue), JsonRpcException, check_exception2); - c.SetResponse("23"); - CHECK_EXCEPTION_TYPE(client.CallMethod("abcd", Json::nullValue), JsonRpcException, check_exception2); + c.SetResponse("{\"jsonrpc\":\"2.0\", \"id\": 1, \"result\": 24}"); + response = client.CallMethod("abcd", params); + CHECK(response.asInt() == 24); } -TEST_CASE_METHOD(F, "test_client_v2_batchcall_success", TEST_MODULE) -{ - BatchCall bc; - CHECK(bc.addCall("abc", Json::nullValue, false) == 1); - CHECK(bc.addCall("def", Json::nullValue, true) == -1); - CHECK(bc.addCall("abc", Json::nullValue, false) == 2); +TEST_CASE_METHOD(F, "test_client_v2_method_success", TEST_MODULE) { + params.append(23); + c.SetResponse("{\"jsonrpc\":\"2.0\", \"id\": 1, \"result\": 23}"); + Json::Value response = client.CallMethod("abcd", params); + Json::Value v = c.GetJsonRequest(); + + CHECK(v["method"].asString() == "abcd"); + CHECK(v["params"][0].asInt() == 23); + CHECK(v["jsonrpc"].asString() == "2.0"); + CHECK(v["id"].asInt() == 1); +} - c.SetResponse("[{\"jsonrpc\":\"2.0\", \"id\": 1, \"result\": 23},{\"jsonrpc\":\"2.0\", \"id\": 2, \"result\": 24}]"); +TEST_CASE_METHOD(F, "test_client_v2_notification_success", TEST_MODULE) { + params.append(23); + client.CallNotification("abcd", params); + Json::Value v = c.GetJsonRequest(); - BatchResponse response = client.CallProcedures(bc); + CHECK(v["method"].asString() == "abcd"); + CHECK(v["params"][0].asInt() == 23); + CHECK(v["jsonrpc"].asString() == "2.0"); + CHECK(v.isMember("id") == false); +} - CHECK(response.hasErrors() == false); - CHECK(response.getResult(1).asInt() == 23); - CHECK(response.getResult(2).asInt() == 24); - CHECK(response.getResult(3).isNull() == true); +TEST_CASE_METHOD(F, "test_client_v2_errorresponse", TEST_MODULE) { + c.SetResponse("{\"jsonrpc\":\"2.0\", \"error\": {\"code\": -32600, " + "\"message\": \"Invalid Request\", \"data\": [1,2]}, \"id\": " + "null}"); + CHECK_EXCEPTION_TYPE(client.CallMethod("abcd", Json::nullValue), + JsonRpcException, check_exception3); +} - Json::Value request = c.GetJsonRequest(); - CHECK(request.size() == 3); - CHECK(request[0]["method"].asString() == "abc"); - CHECK(request[0]["id"].asInt() == 1); - CHECK(request[1]["method"].asString() == "def"); - CHECK(request[1]["id"].isNull() == true); - CHECK(request[2]["id"].asInt() == 2); +TEST_CASE_METHOD(F, "test_client_v2_invalidjson", TEST_MODULE) { + c.SetResponse("{\"method\":234"); + CHECK_EXCEPTION_TYPE(client.CallMethod("abcd", Json::nullValue), + JsonRpcException, check_exception1); +} - bc.toString(false); +TEST_CASE_METHOD(F, "test_client_v2_invalidresponse", TEST_MODULE) { + c.SetResponse("{\"jsonrpc\":\"2.0\", \"id\": 1, \"resulto\": 23}"); + CHECK_EXCEPTION_TYPE(client.CallMethod("abcd", Json::nullValue), + JsonRpcException, check_exception2); + c.SetResponse("{\"jsonrpc\":\"2.0\", \"id2\": 1, \"result\": 23}"); + CHECK_EXCEPTION_TYPE(client.CallMethod("abcd", Json::nullValue), + JsonRpcException, check_exception2); + c.SetResponse("{\"jsonrpc\":\"1.0\", \"id\": 1, \"result\": 23}"); + CHECK_EXCEPTION_TYPE(client.CallMethod("abcd", Json::nullValue), + JsonRpcException, check_exception2); + c.SetResponse("{\"id\": 1, \"result\": 23}"); + CHECK_EXCEPTION_TYPE(client.CallMethod("abcd", Json::nullValue), + JsonRpcException, check_exception2); + c.SetResponse( + "{\"jsonrpc\":\"2.0\", \"id\": 1, \"result\": 23, \"error\": {}}"); + CHECK_EXCEPTION_TYPE(client.CallMethod("abcd", Json::nullValue), + JsonRpcException, check_exception2); + c.SetResponse("{\"jsonrpc\":\"2.0\", \"id\": 1, \"error\": {}}"); + CHECK_EXCEPTION_TYPE(client.CallMethod("abcd", Json::nullValue), + JsonRpcException, check_exception2); + c.SetResponse("{\"jsonrpc\":\"2.0\", \"result\": 23}"); + CHECK_EXCEPTION_TYPE(client.CallMethod("abcd", Json::nullValue), + JsonRpcException, check_exception2); + c.SetResponse("{}"); + CHECK_EXCEPTION_TYPE(client.CallMethod("abcd", Json::nullValue), + JsonRpcException, check_exception2); + c.SetResponse("[]"); + CHECK_EXCEPTION_TYPE(client.CallMethod("abcd", Json::nullValue), + JsonRpcException, check_exception2); + c.SetResponse("23"); + CHECK_EXCEPTION_TYPE(client.CallMethod("abcd", Json::nullValue), + JsonRpcException, check_exception2); } -TEST_CASE_METHOD(F, "test_client_v2_batchcall_error", TEST_MODULE) -{ - BatchCall bc; - CHECK(bc.addCall("abc", Json::nullValue, false) == 1); - CHECK(bc.addCall("def", Json::nullValue, false) == 2); - CHECK(bc.addCall("abc", Json::nullValue, false) == 3); +TEST_CASE_METHOD(F, "test_client_v2_batchcall_success", TEST_MODULE) { + BatchCall bc; + CHECK(bc.addCall("abc", Json::nullValue, false) == 1); + CHECK(bc.addCall("def", Json::nullValue, true) == -1); + CHECK(bc.addCall("abc", Json::nullValue, false) == 2); - c.SetResponse("[{\"jsonrpc\":\"2.0\", \"id\": 1, \"result\": 23},{\"jsonrpc\":\"2.0\", \"id\": 2, \"error\": {\"code\": -32001, \"message\": \"error1\"}},{\"jsonrpc\":\"2.0\", \"id\": null, \"error\": {\"code\": -32002, \"message\": \"error2\"}}]"); + c.SetResponse("[{\"jsonrpc\":\"2.0\", \"id\": 1, \"result\": " + "23},{\"jsonrpc\":\"2.0\", \"id\": 2, \"result\": 24}]"); - BatchResponse response = client.CallProcedures(bc); + BatchResponse response = client.CallProcedures(bc); - CHECK(response.hasErrors() == true); - CHECK(response.getResult(1).asInt() == 23); - CHECK(response.getResult(2).isNull() == true); - CHECK(response.getResult(3).isNull() == true); - CHECK(response.getErrorMessage(2) == "error1"); - CHECK(response.getErrorMessage(3) == ""); + CHECK(response.hasErrors() == false); + CHECK(response.getResult(1).asInt() == 23); + CHECK(response.getResult(2).asInt() == 24); + CHECK(response.getResult(3).isNull() == true); - c.SetResponse("{}"); - CHECK_EXCEPTION_TYPE(client.CallProcedures(bc), JsonRpcException, check_exception2); + Json::Value request = c.GetJsonRequest(); + CHECK(request.size() == 3); + CHECK(request[0]["method"].asString() == "abc"); + CHECK(request[0]["id"].asInt() == 1); + CHECK(request[1]["method"].asString() == "def"); + CHECK(request[1]["id"].isNull() == true); + CHECK(request[2]["id"].asInt() == 2); - c.SetResponse("[1,2,3]"); - CHECK_EXCEPTION_TYPE(client.CallProcedures(bc), JsonRpcException, check_exception2); + bc.toString(false); +} - c.SetResponse("[[],[],[]]"); - CHECK_EXCEPTION_TYPE(client.CallProcedures(bc), JsonRpcException, check_exception2); +TEST_CASE_METHOD(F, "test_client_v2_batchcall_error", TEST_MODULE) { + BatchCall bc; + CHECK(bc.addCall("abc", Json::nullValue, false) == 1); + CHECK(bc.addCall("def", Json::nullValue, false) == 2); + CHECK(bc.addCall("abc", Json::nullValue, false) == 3); + + c.SetResponse("[{\"jsonrpc\":\"2.0\", \"id\": 1, \"result\": " + "23},{\"jsonrpc\":\"2.0\", \"id\": 2, \"error\": {\"code\": " + "-32001, \"message\": \"error1\"}},{\"jsonrpc\":\"2.0\", " + "\"id\": null, \"error\": {\"code\": -32002, \"message\": " + "\"error2\"}}]"); + + BatchResponse response = client.CallProcedures(bc); + + CHECK(response.hasErrors() == true); + CHECK(response.getResult(1).asInt() == 23); + CHECK(response.getResult(2).isNull() == true); + CHECK(response.getResult(3).isNull() == true); + CHECK(response.getErrorMessage(2) == "error1"); + CHECK(response.getErrorMessage(3) == ""); + + c.SetResponse("{}"); + CHECK_EXCEPTION_TYPE(client.CallProcedures(bc), JsonRpcException, + check_exception2); + + c.SetResponse("[1,2,3]"); + CHECK_EXCEPTION_TYPE(client.CallProcedures(bc), JsonRpcException, + check_exception2); + + c.SetResponse("[[],[],[]]"); + CHECK_EXCEPTION_TYPE(client.CallProcedures(bc), JsonRpcException, + check_exception2); } -TEST_CASE_METHOD(F1, "test_client_v1_method_success", TEST_MODULE) -{ - params.append(23); - c.SetResponse("{\"id\": 1, \"result\": 23, \"error\": null}"); +TEST_CASE_METHOD(F1, "test_client_v1_method_success", TEST_MODULE) { + params.append(23); + c.SetResponse("{\"id\": 1, \"result\": 23, \"error\": null}"); - Json::Value response = client.CallMethod("abcd", params); - Json::Value v = c.GetJsonRequest(); + Json::Value response = client.CallMethod("abcd", params); + Json::Value v = c.GetJsonRequest(); - CHECK(v["method"].asString() == "abcd"); - CHECK(v["params"][0].asInt() == 23); - CHECK(v.isMember("jsonrpc") == false); - CHECK(v["id"].asInt() == 1); + CHECK(v["method"].asString() == "abcd"); + CHECK(v["params"][0].asInt() == 23); + CHECK(v.isMember("jsonrpc") == false); + CHECK(v["id"].asInt() == 1); - CHECK(response.asInt() == 23); + CHECK(response.asInt() == 23); } -TEST_CASE_METHOD(F1, "test_client_v1_notification_success", TEST_MODULE) -{ - params.append(23); +TEST_CASE_METHOD(F1, "test_client_v1_notification_success", TEST_MODULE) { + params.append(23); - client.CallNotification("abcd", params); + client.CallNotification("abcd", params); - Json::Value v = c.GetJsonRequest(); + Json::Value v = c.GetJsonRequest(); - CHECK(v["method"].asString() == "abcd"); - CHECK(v["params"][0].asInt() == 23); - CHECK(v.isMember("id") == true); - CHECK(v["id"] == Json::nullValue); + CHECK(v["method"].asString() == "abcd"); + CHECK(v["params"][0].asInt() == 23); + CHECK(v.isMember("id") == true); + CHECK(v["id"] == Json::nullValue); } -TEST_CASE_METHOD(F1, "test_client_v1_errorresponse", TEST_MODULE) -{ - c.SetResponse("{\"result\": null, \"error\": {\"code\": -32600, \"message\": \"Invalid Request\", \"data\": [1,2]}, \"id\": null}"); - CHECK_EXCEPTION_TYPE(client.CallMethod("abcd", Json::nullValue), JsonRpcException, check_exception3); +TEST_CASE_METHOD(F1, "test_client_v1_errorresponse", TEST_MODULE) { + c.SetResponse("{\"result\": null, \"error\": {\"code\": -32600, \"message\": " + "\"Invalid Request\", \"data\": [1,2]}, \"id\": null}"); + CHECK_EXCEPTION_TYPE(client.CallMethod("abcd", Json::nullValue), + JsonRpcException, check_exception3); - c.SetResponse("{\"result\": null, \"error\": {\"code\": -32600, \"message\": \"Invalid Request\", \"data\": [1,2]}, \"id\": null}"); - CHECK_EXCEPTION_TYPE(client.CallMethod("abcd", Json::nullValue), JsonRpcException, check_exception3); + c.SetResponse("{\"result\": null, \"error\": {\"code\": -32600, \"message\": " + "\"Invalid Request\", \"data\": [1,2]}, \"id\": null}"); + CHECK_EXCEPTION_TYPE(client.CallMethod("abcd", Json::nullValue), + JsonRpcException, check_exception3); } -TEST_CASE_METHOD(F1, "test_client_v1_invalidresponse", TEST_MODULE) -{ - c.SetResponse("{\"id\": 1, \"resulto\": 23, \"error\": null}"); - CHECK_EXCEPTION_TYPE(client.CallMethod("abcd", Json::nullValue), JsonRpcException, check_exception2); - c.SetResponse("{\"id\": 1, \"result\": 23}"); - CHECK_EXCEPTION_TYPE(client.CallMethod("abcd", Json::nullValue), JsonRpcException, check_exception2); - c.SetResponse("{\"id\": 1, \"error\": null}"); - CHECK_EXCEPTION_TYPE(client.CallMethod("abcd", Json::nullValue), JsonRpcException, check_exception2); - c.SetResponse("{\"id\": 1}"); - CHECK_EXCEPTION_TYPE(client.CallMethod("abcd", Json::nullValue), JsonRpcException, check_exception2); - c.SetResponse("{\"id\": 1, \"result\": 23, \"error\": {}}"); - CHECK_EXCEPTION_TYPE(client.CallMethod("abcd", Json::nullValue), JsonRpcException, check_exception2); - c.SetResponse("{\"id\": 1, \"result\": null, \"error\": {}}"); - CHECK_EXCEPTION_TYPE(client.CallMethod("abcd", Json::nullValue), JsonRpcException, check_exception2); - c.SetResponse("{}"); - CHECK_EXCEPTION_TYPE(client.CallMethod("abcd", Json::nullValue), JsonRpcException, check_exception2); - c.SetResponse("[]"); - CHECK_EXCEPTION_TYPE(client.CallMethod("abcd", Json::nullValue), JsonRpcException, check_exception2); - c.SetResponse("23"); - CHECK_EXCEPTION_TYPE(client.CallMethod("abcd", Json::nullValue), JsonRpcException, check_exception2); +TEST_CASE_METHOD(F1, "test_client_v1_invalidresponse", TEST_MODULE) { + c.SetResponse("{\"id\": 1, \"resulto\": 23, \"error\": null}"); + CHECK_EXCEPTION_TYPE(client.CallMethod("abcd", Json::nullValue), + JsonRpcException, check_exception2); + c.SetResponse("{\"id\": 1, \"result\": 23}"); + CHECK_EXCEPTION_TYPE(client.CallMethod("abcd", Json::nullValue), + JsonRpcException, check_exception2); + c.SetResponse("{\"id\": 1, \"error\": null}"); + CHECK_EXCEPTION_TYPE(client.CallMethod("abcd", Json::nullValue), + JsonRpcException, check_exception2); + c.SetResponse("{\"id\": 1}"); + CHECK_EXCEPTION_TYPE(client.CallMethod("abcd", Json::nullValue), + JsonRpcException, check_exception2); + c.SetResponse("{\"id\": 1, \"result\": 23, \"error\": {}}"); + CHECK_EXCEPTION_TYPE(client.CallMethod("abcd", Json::nullValue), + JsonRpcException, check_exception2); + c.SetResponse("{\"id\": 1, \"result\": null, \"error\": {}}"); + CHECK_EXCEPTION_TYPE(client.CallMethod("abcd", Json::nullValue), + JsonRpcException, check_exception2); + c.SetResponse("{}"); + CHECK_EXCEPTION_TYPE(client.CallMethod("abcd", Json::nullValue), + JsonRpcException, check_exception2); + c.SetResponse("[]"); + CHECK_EXCEPTION_TYPE(client.CallMethod("abcd", Json::nullValue), + JsonRpcException, check_exception2); + c.SetResponse("23"); + CHECK_EXCEPTION_TYPE(client.CallMethod("abcd", Json::nullValue), + JsonRpcException, check_exception2); } diff --git a/src/test/test_common.cpp b/src/test/test_common.cpp index 858f8efd..76de0481 100644 --- a/src/test/test_common.cpp +++ b/src/test/test_common.cpp @@ -7,12 +7,12 @@ * @license See attached LICENSE.txt ************************************************************************/ +#include "checkexception.h" #include -#include #include +#include #include #include -#include "checkexception.h" #define TEST_MODULE "[common]" @@ -20,180 +20,202 @@ using namespace jsonrpc; using namespace std; namespace testcommon { - bool check_exception1(JsonRpcException const & ex) - { - return ex.GetCode() == Errors::ERROR_RPC_JSON_PARSE_ERROR; - } - - bool check_exception2(JsonRpcException const & ex) - { - return ex.GetCode() == Errors::ERROR_SERVER_PROCEDURE_SPECIFICATION_SYNTAX; - } +bool check_exception1(JsonRpcException const &ex) { + return ex.GetCode() == Errors::ERROR_RPC_JSON_PARSE_ERROR; } +bool check_exception2(JsonRpcException const &ex) { + return ex.GetCode() == Errors::ERROR_SERVER_PROCEDURE_SPECIFICATION_SYNTAX; +} +} // namespace testcommon using namespace testcommon; -TEST_CASE("test_procedure_parametervalidation", TEST_MODULE) -{ - Procedure proc1("someprocedure", PARAMS_BY_NAME, JSON_BOOLEAN, "name", JSON_STRING, "ssnr", JSON_INTEGER, NULL); - - //Expected to pass validation - Json::Value param1; - param1["name"] = "Peter"; - param1["ssnr"] = 4711; - CHECK(proc1.ValidateNamedParameters(param1) == true); - - //Expected to fail validation - Json::Value param2; - param2.append("Peter"); - param2.append(4711); - CHECK(proc1.ValidateNamedParameters(param2) == false); - - //Expected to fail validation - Json::Value param3; - param3.append(4711); - param3.append("Peter"); - CHECK(proc1.ValidateNamedParameters(param3) == false); - - Procedure proc2("someprocedure", PARAMS_BY_NAME, JSON_BOOLEAN, "bool", JSON_BOOLEAN, "object", JSON_OBJECT, "array", JSON_ARRAY, "real", JSON_REAL, "int", JSON_INTEGER, NULL); - Json::Value param4; - Json::Value array; - array.append(0); - param4["bool"] = true; - param4["object"] = param1; - param4["array"] = array; - param4["real"] = 0.332; - param4["int"] = 3; - - CHECK(proc2.ValidateNamedParameters(param4) == true); - - param4["bool"] = "String"; - CHECK(proc2.ValidateNamedParameters(param4) == false); - param4["bool"] = true; - - param4["object"] = "String"; - CHECK(proc2.ValidateNamedParameters(param4) == false); - param4["object"] = param1; - - param4["real"] = "String"; - CHECK(proc2.ValidateNamedParameters(param4) == false); - param4["real"] = 0.322; - - param4["array"] = "String"; - CHECK(proc2.ValidateNamedParameters(param4) == false); - param4["array"] = array; - - param4["int"] = "String"; - CHECK(proc2.ValidateNamedParameters(param4) == false); +TEST_CASE("test_procedure_parametervalidation", TEST_MODULE) { + Procedure proc1("someprocedure", PARAMS_BY_NAME, JSON_BOOLEAN, "name", + JSON_STRING, "ssnr", JSON_INTEGER, NULL); + + // Expected to pass validation + Json::Value param1; + param1["name"] = "Peter"; + param1["ssnr"] = 4711; + CHECK(proc1.ValidateNamedParameters(param1) == true); + + // Expected to fail validation + Json::Value param2; + param2.append("Peter"); + param2.append(4711); + CHECK(proc1.ValidateNamedParameters(param2) == false); + + // Expected to fail validation + Json::Value param3; + param3.append(4711); + param3.append("Peter"); + CHECK(proc1.ValidateNamedParameters(param3) == false); + + Procedure proc2("someprocedure", PARAMS_BY_NAME, JSON_BOOLEAN, "bool", + JSON_BOOLEAN, "object", JSON_OBJECT, "array", JSON_ARRAY, + "real", JSON_REAL, "int", JSON_INTEGER, NULL); + Json::Value param4; + Json::Value array; + array.append(0); + param4["bool"] = true; + param4["object"] = param1; + param4["array"] = array; + param4["real"] = 0.332; + param4["int"] = 3; + + CHECK(proc2.ValidateNamedParameters(param4) == true); + + param4["bool"] = "String"; + CHECK(proc2.ValidateNamedParameters(param4) == false); + param4["bool"] = true; + + param4["object"] = "String"; + CHECK(proc2.ValidateNamedParameters(param4) == false); + param4["object"] = param1; + + param4["real"] = "String"; + CHECK(proc2.ValidateNamedParameters(param4) == false); + param4["real"] = 0.322; + + param4["array"] = "String"; + CHECK(proc2.ValidateNamedParameters(param4) == false); + param4["array"] = array; + + param4["int"] = "String"; + CHECK(proc2.ValidateNamedParameters(param4) == false); } -TEST_CASE("test_exception", TEST_MODULE) -{ - JsonRpcException ex(Errors::ERROR_CLIENT_CONNECTOR); - CHECK(string(ex.what()) == "Exception -32003 : Client connector error"); - CHECK(string(ex.GetMessage()) == "Client connector error"); - CHECK(ex.GetCode() == -32003); - - JsonRpcException ex2(Errors::ERROR_CLIENT_CONNECTOR, "addInfo"); - CHECK(string(ex2.what()) == "Exception -32003 : Client connector error: addInfo"); - - JsonRpcException ex3("addInfo"); - CHECK(string(ex3.what()) == "addInfo"); - CHECK(ex3.GetMessage() == "addInfo"); - CHECK(ex3.GetCode() == 0); - - Json::Value data; - data.append(13); - data.append(41); - JsonRpcException ex4(Errors::ERROR_RPC_INTERNAL_ERROR, "internal error", data); - CHECK(ex4.GetData().size() == 2); - CHECK(ex4.GetData()[0].asInt() == 13); - CHECK(ex4.GetData()[1].asInt() == 41); - +TEST_CASE("test_exception", TEST_MODULE) { + JsonRpcException ex(Errors::ERROR_CLIENT_CONNECTOR); + CHECK(string(ex.what()) == "Exception -32003 : Client connector error"); + CHECK(string(ex.GetMessage()) == "Client connector error"); + CHECK(ex.GetCode() == -32003); + + JsonRpcException ex2(Errors::ERROR_CLIENT_CONNECTOR, "addInfo"); + CHECK(string(ex2.what()) == + "Exception -32003 : Client connector error: addInfo"); + + JsonRpcException ex3("addInfo"); + CHECK(string(ex3.what()) == "addInfo"); + CHECK(ex3.GetMessage() == "addInfo"); + CHECK(ex3.GetCode() == 0); + + Json::Value data; + data.append(13); + data.append(41); + JsonRpcException ex4(Errors::ERROR_RPC_INTERNAL_ERROR, "internal error", + data); + CHECK(ex4.GetData().size() == 2); + CHECK(ex4.GetData()[0].asInt() == 13); + CHECK(ex4.GetData()[1].asInt() == 41); } -TEST_CASE("test_specificationparser_errors", TEST_MODULE) -{ - CHECK_EXCEPTION_TYPE(SpecificationParser::GetProceduresFromFile("testspec1.json"), JsonRpcException, check_exception1); - CHECK_EXCEPTION_TYPE(SpecificationParser::GetProceduresFromFile("testspec2.json"), JsonRpcException, check_exception2); - CHECK_EXCEPTION_TYPE(SpecificationParser::GetProceduresFromFile("testspec3.json"), JsonRpcException, check_exception2); - CHECK_EXCEPTION_TYPE(SpecificationParser::GetProceduresFromFile("testspec4.json"), JsonRpcException, check_exception2); - CHECK_EXCEPTION_TYPE(SpecificationParser::GetProceduresFromString("{}"), JsonRpcException, check_exception2); - - CHECK_EXCEPTION_TYPE(SpecificationParser::GetProceduresFromString("[{\"name\":\"proc1\"},{\"name\":\"proc1\"}]"), JsonRpcException, check_exception2); - CHECK_EXCEPTION_TYPE(SpecificationParser::GetProceduresFromString("[{\"name\":\"proc1\", \"params\": {\"param1\": null}}]"), JsonRpcException, check_exception2); - CHECK_EXCEPTION_TYPE(SpecificationParser::GetProceduresFromString("[{\"name\":\"proc1\", \"params\": 23}]"), JsonRpcException, check_exception2); +TEST_CASE("test_specificationparser_errors", TEST_MODULE) { + CHECK_EXCEPTION_TYPE( + SpecificationParser::GetProceduresFromFile("testspec1.json"), + JsonRpcException, check_exception1); + CHECK_EXCEPTION_TYPE( + SpecificationParser::GetProceduresFromFile("testspec2.json"), + JsonRpcException, check_exception2); + CHECK_EXCEPTION_TYPE( + SpecificationParser::GetProceduresFromFile("testspec3.json"), + JsonRpcException, check_exception2); + CHECK_EXCEPTION_TYPE( + SpecificationParser::GetProceduresFromFile("testspec4.json"), + JsonRpcException, check_exception2); + CHECK_EXCEPTION_TYPE(SpecificationParser::GetProceduresFromString("{}"), + JsonRpcException, check_exception2); + + CHECK_EXCEPTION_TYPE(SpecificationParser::GetProceduresFromString( + "[{\"name\":\"proc1\"},{\"name\":\"proc1\"}]"), + JsonRpcException, check_exception2); + CHECK_EXCEPTION_TYPE( + SpecificationParser::GetProceduresFromString( + "[{\"name\":\"proc1\", \"params\": {\"param1\": null}}]"), + JsonRpcException, check_exception2); + CHECK_EXCEPTION_TYPE(SpecificationParser::GetProceduresFromString( + "[{\"name\":\"proc1\", \"params\": 23}]"), + JsonRpcException, check_exception2); } -TEST_CASE("test_specificationparser_success", TEST_MODULE) -{ - std::vector procs = SpecificationParser::GetProceduresFromFile("testspec5.json"); - REQUIRE(procs.size() == 4); +TEST_CASE("test_specificationparser_success", TEST_MODULE) { + std::vector procs = + SpecificationParser::GetProceduresFromFile("testspec5.json"); + REQUIRE(procs.size() == 4); - CHECK(procs[0].GetProcedureName() == "testmethod"); - CHECK(procs[0].GetReturnType() == JSON_STRING); - CHECK(procs[0].GetProcedureType() == RPC_METHOD); - CHECK(procs[0].GetParameterDeclarationType() == PARAMS_BY_NAME); + CHECK(procs[0].GetProcedureName() == "testmethod"); + CHECK(procs[0].GetReturnType() == JSON_STRING); + CHECK(procs[0].GetProcedureType() == RPC_METHOD); + CHECK(procs[0].GetParameterDeclarationType() == PARAMS_BY_NAME); - CHECK(procs[2].GetProcedureName() == "testmethod2"); - CHECK(procs[2].GetReturnType() == JSON_REAL); - CHECK(procs[2].GetProcedureType() == RPC_METHOD); - CHECK(procs[2].GetParameterDeclarationType() == PARAMS_BY_NAME); + CHECK(procs[2].GetProcedureName() == "testmethod2"); + CHECK(procs[2].GetReturnType() == JSON_REAL); + CHECK(procs[2].GetProcedureType() == RPC_METHOD); + CHECK(procs[2].GetParameterDeclarationType() == PARAMS_BY_NAME); - CHECK(procs[1].GetProcedureName() == "testnotification"); - CHECK(procs[1].GetProcedureType() == RPC_NOTIFICATION); - CHECK(procs[1].GetParameterDeclarationType() == PARAMS_BY_NAME); + CHECK(procs[1].GetProcedureName() == "testnotification"); + CHECK(procs[1].GetProcedureType() == RPC_NOTIFICATION); + CHECK(procs[1].GetParameterDeclarationType() == PARAMS_BY_NAME); - CHECK(procs[3].GetProcedureName() == "testnotification2"); - CHECK(procs[3].GetProcedureType() == RPC_NOTIFICATION); - CHECK(procs[3].GetParameterDeclarationType() == PARAMS_BY_NAME); + CHECK(procs[3].GetProcedureName() == "testnotification2"); + CHECK(procs[3].GetProcedureType() == RPC_NOTIFICATION); + CHECK(procs[3].GetParameterDeclarationType() == PARAMS_BY_NAME); } -TEST_CASE("test_specificationwriter", TEST_MODULE) -{ - vector procedures; +TEST_CASE("test_specificationwriter", TEST_MODULE) { + vector procedures; - procedures.push_back(Procedure("testmethod1", PARAMS_BY_NAME, JSON_INTEGER, "param1", JSON_INTEGER, "param2", JSON_REAL, NULL)); - procedures.push_back(Procedure("testmethod2", PARAMS_BY_POSITION, JSON_INTEGER, "param1", JSON_OBJECT, "param2", JSON_ARRAY, NULL)); + procedures.push_back(Procedure("testmethod1", PARAMS_BY_NAME, JSON_INTEGER, + "param1", JSON_INTEGER, "param2", JSON_REAL, + NULL)); + procedures.push_back(Procedure("testmethod2", PARAMS_BY_POSITION, + JSON_INTEGER, "param1", JSON_OBJECT, "param2", + JSON_ARRAY, NULL)); - procedures.push_back(Procedure("testnotification1", PARAMS_BY_NAME, "param1", JSON_BOOLEAN, "param2", JSON_STRING, NULL)); - procedures.push_back(Procedure("testnotification2", PARAMS_BY_POSITION, "param1", JSON_INTEGER, "param2", JSON_STRING, NULL)); + procedures.push_back(Procedure("testnotification1", PARAMS_BY_NAME, "param1", + JSON_BOOLEAN, "param2", JSON_STRING, NULL)); + procedures.push_back(Procedure("testnotification2", PARAMS_BY_POSITION, + "param1", JSON_INTEGER, "param2", JSON_STRING, + NULL)); - procedures.push_back(Procedure("testnotification3", PARAMS_BY_POSITION, NULL)); + procedures.push_back( + Procedure("testnotification3", PARAMS_BY_POSITION, NULL)); - Json::Value result = SpecificationWriter::toJsonValue(procedures); + Json::Value result = SpecificationWriter::toJsonValue(procedures); - REQUIRE(result.isArray() == true); - REQUIRE(result.size() == procedures.size()); + REQUIRE(result.isArray() == true); + REQUIRE(result.size() == procedures.size()); - CHECK(result[0]["name"].asString() == "testmethod1"); - CHECK(result[1]["name"].asString() == "testmethod2"); - CHECK(result[2]["name"].asString() == "testnotification1"); - CHECK(result[3]["name"].asString() == "testnotification2"); - CHECK(result[4]["name"].asString() == "testnotification3"); + CHECK(result[0]["name"].asString() == "testmethod1"); + CHECK(result[1]["name"].asString() == "testmethod2"); + CHECK(result[2]["name"].asString() == "testnotification1"); + CHECK(result[3]["name"].asString() == "testnotification2"); + CHECK(result[4]["name"].asString() == "testnotification3"); - REQUIRE(result[0]["params"].isObject() == true); - CHECK(result[0]["params"]["param1"].isIntegral() == true); - CHECK(result[0]["params"]["param2"].isDouble() == true); + REQUIRE(result[0]["params"].isObject() == true); + CHECK(result[0]["params"]["param1"].isIntegral() == true); + CHECK(result[0]["params"]["param2"].isDouble() == true); - REQUIRE(result[1]["params"].isArray() == true); - CHECK(result[1]["params"][0].isObject() == true); - CHECK(result[1]["params"][1].isArray() == true); + REQUIRE(result[1]["params"].isArray() == true); + CHECK(result[1]["params"][0].isObject() == true); + CHECK(result[1]["params"][1].isArray() == true); - REQUIRE(result[2]["params"].isObject() == true); - CHECK(result[2]["params"]["param1"].isBool() == true); - CHECK(result[2]["params"]["param2"].isString() == true); + REQUIRE(result[2]["params"].isObject() == true); + CHECK(result[2]["params"]["param1"].isBool() == true); + CHECK(result[2]["params"]["param2"].isString() == true); - REQUIRE(result[3]["params"].isArray() == true); - CHECK(result[3]["params"][0].isIntegral() == true); - CHECK(result[3]["params"][1].isString() == true); + REQUIRE(result[3]["params"].isArray() == true); + CHECK(result[3]["params"][0].isIntegral() == true); + CHECK(result[3]["params"][1].isString() == true); - CHECK(result[4].isMember("params") == false); + CHECK(result[4].isMember("params") == false); - CHECK(result[0]["returns"].isIntegral() == true); - CHECK(result[1]["returns"].isIntegral() == true); + CHECK(result[0]["returns"].isIntegral() == true); + CHECK(result[1]["returns"].isIntegral() == true); - CHECK(SpecificationWriter::toFile("testspec.json", procedures) == true); - CHECK(SpecificationWriter::toFile("/a/b/c/testspec.json", procedures) == false); + CHECK(SpecificationWriter::toFile("testspec.json", procedures) == true); + CHECK(SpecificationWriter::toFile("/a/b/c/testspec.json", procedures) == + false); } diff --git a/src/test/test_connector_filedescriptor.cpp b/src/test/test_connector_filedescriptor.cpp index 9959b7c5..9c05a6f8 100644 --- a/src/test/test_connector_filedescriptor.cpp +++ b/src/test/test_connector_filedescriptor.cpp @@ -8,213 +8,196 @@ ************************************************************************/ #ifdef FILEDESCRIPTOR_TESTING -#include +#include "mockclientconnectionhandler.h" #include -#include -#include #include -#include "mockclientconnectionhandler.h" +#include +#include +#include +#include #include "checkexception.h" using namespace jsonrpc; using namespace std; -#ifndef DELIMITER_CHAR - #define DELIMITER_CHAR char(0x0A) -#endif - #define TEST_MODULE "[connector_filedescriptor]" -namespace testfiledescriptorserver -{ - struct F { - FileDescriptorServer *server; - FileDescriptorClient *client; - MockClientConnectionHandler handler; - - F() - { - pipe(c2sfd); - pipe(s2cfd); - server = new FileDescriptorServer(c2sfd[0], s2cfd[1]); - client = new FileDescriptorClient(s2cfd[0], c2sfd[1]); - server->SetHandler(&handler); - REQUIRE(server->StartListening()); - } - ~F() - { - server->StopListening(); - delete server; - delete client; - close(c2sfd[0]); - close(c2sfd[1]); - close(s2cfd[0]); - close(s2cfd[1]); - } - - int c2sfd[2]; // Client to server fd - int s2cfd[2]; // Server to client fd - }; - - bool check_exception1(JsonRpcException const&ex) - { - return ex.GetCode() == Errors::ERROR_CLIENT_CONNECTOR; - } -} -using namespace testfiledescriptorserver; +namespace testfiledescriptorserver { +struct F { + FileDescriptorServer *server; + FileDescriptorClient *client; + MockClientConnectionHandler handler; -TEST_CASE_METHOD(F, "test_filedescriptor_success", TEST_MODULE) -{ - handler.response = "exampleresponse"; - handler.timeout = 100; - string result; - string request = "examplerequest"; - request.push_back(DELIMITER_CHAR); - string expectedResult = "exampleresponse"; - expectedResult.push_back(DELIMITER_CHAR); + F() { + pipe(c2sfd); + pipe(s2cfd); + server = new FileDescriptorServer(c2sfd[0], s2cfd[1]); + client = new FileDescriptorClient(s2cfd[0], c2sfd[1]); + server->SetHandler(&handler); + REQUIRE(server->StartListening()); + } + ~F() { + server->StopListening(); + delete server; + delete client; + close(c2sfd[0]); + close(c2sfd[1]); + close(s2cfd[0]); + close(s2cfd[1]); + } - client->SendRPCMessage(request, result); + int c2sfd[2]; // Client to server fd + int s2cfd[2]; // Server to client fd +}; - CHECK(handler.request == request); - CHECK(result == expectedResult); +bool check_exception1(JsonRpcException const &ex) { + return ex.GetCode() == Errors::ERROR_CLIENT_CONNECTOR; } +} // namespace testfiledescriptorserver +using namespace testfiledescriptorserver; -TEST_CASE_METHOD(F, "test_filedescriptor_longpost", TEST_MODULE) -{ - int mb = 2; - unsigned long size = mb * 1024*1024; - char* str = (char*) malloc(size * sizeof(char)); - if (str == NULL) - { - FAIL("Could not allocate enough memory for test"); - } - for (unsigned long i=0; i < size; i++) - { - str[i] = (char)('a'+(i%26)); - } - str[size-1] = '\n'; - - handler.response = str; - string response; - client->SendRPCMessage(str, response); - - CHECK(handler.request == str); - CHECK(response == handler.response); - CHECK(response.size() == size); - - free(str); +TEST_CASE_METHOD(F, "test_filedescriptor_success", TEST_MODULE) { + handler.response = "exampleresponse"; + handler.timeout = 1; + string result; + string request = "examplerequest"; + string expectedResult = "exampleresponse"; - server->StopListening(); -} + client->SendRPCMessage(request, result); -TEST_CASE_METHOD(F, "test_filedescriptor_success_with_delimiter", TEST_MODULE) -{ - handler.response = "exampleresponse"; - handler.response.push_back(DELIMITER_CHAR); - handler.timeout = 100; - string result; - string request = "examplerequest"; - request.push_back(DELIMITER_CHAR); - string expectedResult = "exampleresponse"; - expectedResult.push_back(DELIMITER_CHAR); - - client->SendRPCMessage(request, result); - - CHECK(handler.request == request); - CHECK(result == expectedResult); + CHECK(handler.request == request); + CHECK(result == expectedResult); } -TEST_CASE("test_filedescriptor_server_multiplestart", TEST_MODULE) -{ - int fds[2]; - pipe(fds); - FileDescriptorServer server(fds[0], fds[1]); - CHECK(server.StartListening() == true); - CHECK(server.StartListening() == false); - - CHECK(server.StopListening() == true); - CHECK(server.StopListening() == false); - - server.Wait(); - - close(fds[0]); - close(fds[1]); +TEST_CASE_METHOD(F, "test_filedescriptor_longpost", TEST_MODULE) { + int mb = 2; + unsigned long size = mb * 1024 * 1024; + char *str = (char *)malloc(size * sizeof(char)); + if (str == NULL) { + FAIL("Could not allocate enough memory for test"); + } + for (unsigned long i = 0; i < size; i++) { + str[i] = (char)('a' + (i % 26)); + } + str[size - 1] = '\0'; + handler.response = str; + string response; + client->SendRPCMessage(str, response); + + CHECK(handler.request == str); + CHECK(response.size() == size - 1); + CHECK(response == handler.response); + + free(str); } -TEST_CASE("test_filedescriptor_server_input_fd_invalid_while_listening", TEST_MODULE) -{ - int c2sfd[2]; - pipe(c2sfd); - int s2cfd[2]; - pipe(s2cfd); +TEST_CASE_METHOD(F, "test_filedescriptor_success_with_delimiter", TEST_MODULE) { + handler.response = "exampleresponse"; + handler.response.push_back(DEFAULT_DELIMITER_CHAR); + handler.timeout = 100; + string result; + string request = "examplerequest"; + request.push_back(DEFAULT_DELIMITER_CHAR); + string expectedResult = "exampleresponse"; + expectedResult.push_back(DEFAULT_DELIMITER_CHAR); - FileDescriptorServer server(c2sfd[0], s2cfd[1]); - // Close the writing side of the client to server pipe, as if the client crashed. - CHECK(server.StartListening() == true); - close(c2sfd[0]); - server.Wait(); + client->SendRPCMessage(request, result); - close(c2sfd[1]); - close(s2cfd[1]); - close(s2cfd[0]); + CHECK(handler.request == request); + CHECK(result == expectedResult); } +TEST_CASE("test_filedescriptor_server_multiplestart", TEST_MODULE) { + int fds[2]; + pipe(fds); + FileDescriptorServer server(fds[0], fds[1]); + CHECK(server.StartListening() == true); + CHECK(server.StartListening() == false); -TEST_CASE("test_filedescriptor_server_input_fd_invalid", TEST_MODULE) -{ - int c2sfd[2]; - pipe(c2sfd); - int s2cfd[2]; - pipe(s2cfd); + CHECK(server.StopListening() == true); + CHECK(server.StopListening() == false); - FileDescriptorServer server(c2sfd[0], s2cfd[1]); - // Close the writing side of the client to server pipe, as if the client crashed. - close(c2sfd[0]); - CHECK(server.StartListening() == false); - close(c2sfd[1]); - close(s2cfd[1]); - close(s2cfd[0]); + // server.Wait(); + + close(fds[0]); + close(fds[1]); } -TEST_CASE("test_filedescriptor_server_output_fd_invalid", TEST_MODULE) -{ - int c2sfd[2]; - pipe(c2sfd); - int s2cfd[2]; - pipe(s2cfd); +TEST_CASE("test_filedescriptor_server_input_fd_invalid_while_listening", + TEST_MODULE) { + int c2sfd[2]; + pipe(c2sfd); + int s2cfd[2]; + pipe(s2cfd); + + FileDescriptorServer server(c2sfd[0], s2cfd[1]); + // Close the writing side of the client to server pipe, as if the client + // crashed. + CHECK(server.StartListening() == true); + close(c2sfd[0]); + // server.Wait(); + + close(c2sfd[1]); + close(s2cfd[1]); + close(s2cfd[0]); +} - FileDescriptorServer server(c2sfd[0], s2cfd[1]); - // Close the writing side of the server to client pipe, as if the client crashed. - close(s2cfd[1]); - CHECK(server.StartListening() == false); - close(c2sfd[0]); - close(c2sfd[1]); - close(s2cfd[0]); +TEST_CASE("test_filedescriptor_server_input_fd_invalid", TEST_MODULE) { + int c2sfd[2]; + pipe(c2sfd); + int s2cfd[2]; + pipe(s2cfd); + + FileDescriptorServer server(c2sfd[0], s2cfd[1]); + // Close the writing side of the client to server pipe, as if the client + // crashed. + close(c2sfd[0]); + CHECK(server.StartListening() == false); + close(c2sfd[1]); + close(s2cfd[1]); + close(s2cfd[0]); } -TEST_CASE("test_filedescriptor_client_output_fd_invalid", TEST_MODULE) -{ - FileDescriptorClient client(2, 6); - string result; - CHECK_EXCEPTION_TYPE(client.SendRPCMessage("foobar", result), JsonRpcException, check_exception1); +TEST_CASE("test_filedescriptor_server_output_fd_invalid", TEST_MODULE) { + int c2sfd[2]; + pipe(c2sfd); + int s2cfd[2]; + pipe(s2cfd); + + FileDescriptorServer server(c2sfd[0], s2cfd[1]); + // Close the writing side of the server to client pipe, as if the client + // crashed. + close(s2cfd[1]); + CHECK(server.StartListening() == false); + close(c2sfd[0]); + close(c2sfd[1]); + close(s2cfd[0]); } -TEST_CASE("test_filedescriptor_client_input_fd_invalid", TEST_MODULE) -{ - int c2sfd[2]; - pipe(c2sfd); - int s2cfd[2]; - pipe(s2cfd); +TEST_CASE("test_filedescriptor_client_output_fd_invalid", TEST_MODULE) { + FileDescriptorClient client(2, 6); + string result; + CHECK_EXCEPTION_TYPE(client.SendRPCMessage("foobar", result), + JsonRpcException, check_exception1); +} - FileDescriptorClient client(s2cfd[0], c2sfd[1]); - // Close the reading side of the server to client pipe, as if the server crashed. - close(s2cfd[0]); - string result; - CHECK_EXCEPTION_TYPE(client.SendRPCMessage("foobar", result), JsonRpcException, check_exception1); - close(c2sfd[1]); - close(s2cfd[1]); - close(c2sfd[0]); +TEST_CASE("test_filedescriptor_client_input_fd_invalid", TEST_MODULE) { + int c2sfd[2]; + pipe(c2sfd); + int s2cfd[2]; + pipe(s2cfd); + + FileDescriptorClient client(s2cfd[0], c2sfd[1]); + // Close the reading side of the server to client pipe, as if the server + // crashed. + close(s2cfd[0]); + string result; + CHECK_EXCEPTION_TYPE(client.SendRPCMessage("foobar", result), + JsonRpcException, check_exception1); + close(c2sfd[1]); + close(s2cfd[1]); + close(c2sfd[0]); } #endif // FILEDESCRIPTOR_TESTING diff --git a/src/test/test_connector_http.cpp b/src/test/test_connector_http.cpp index 5157edcc..54083c55 100644 --- a/src/test/test_connector_http.cpp +++ b/src/test/test_connector_http.cpp @@ -9,13 +9,13 @@ #ifdef HTTP_TESTING #include -#include -#include #include +#include +#include +#include "checkexception.h" #include "mockclientconnectionhandler.h" #include "testhttpserver.h" -#include "checkexception.h" using namespace jsonrpc; using namespace std; @@ -25,203 +25,185 @@ using namespace std; #define TEST_MODULE "[connector_http]" -namespace testhttpserver -{ - struct F { - HttpServer server; - HttpClient client; - MockClientConnectionHandler handler; - - F() : - server(TEST_PORT), - client(CLIENT_URL) - { - server.SetHandler(&handler); - server.StartListening(); - } - ~F() - { - server.StopListening(); - } - }; - - bool check_exception1(JsonRpcException const&ex) - { - return ex.GetCode() == Errors::ERROR_CLIENT_CONNECTOR; - } - - bool check_exception2(JsonRpcException const&ex) - { - return ex.GetCode() == Errors::ERROR_RPC_INTERNAL_ERROR; - } +namespace testhttpserver { +struct F { + HttpServer server; + HttpClient client; + MockClientConnectionHandler handler; + + F() : server(TEST_PORT), client(CLIENT_URL) { + server.SetHandler(&handler); + server.StartListening(); + } + ~F() { server.StopListening(); } +}; + +bool check_exception1(JsonRpcException const &ex) { + return ex.GetCode() == Errors::ERROR_CLIENT_CONNECTOR; +} + +bool check_exception2(JsonRpcException const &ex) { + return ex.GetCode() == Errors::ERROR_RPC_INTERNAL_ERROR; } +} // namespace testhttpserver using namespace testhttpserver; -TEST_CASE_METHOD(F, "test_http_success", TEST_MODULE) -{ - handler.response = "exampleresponse"; - string result; - client.SendRPCMessage("examplerequest", result); +TEST_CASE_METHOD(F, "test_http_success", TEST_MODULE) { + handler.response = "exampleresponse"; + string result; + client.SendRPCMessage("examplerequest", result); - CHECK(handler.request == "examplerequest"); - CHECK(result == "exampleresponse"); + CHECK(handler.request == "examplerequest"); + CHECK(result == "exampleresponse"); } -TEST_CASE("test_http_client_error", TEST_MODULE) -{ - HttpClient client("http://someinvalidurl/asdf"); - string result; - CHECK_EXCEPTION_TYPE(client.SendRPCMessage("asdfasfwer", result), JsonRpcException, check_exception1); +TEST_CASE("test_http_client_error", TEST_MODULE) { + HttpClient client("http://someinvalidurl/asdf"); + string result; + CHECK_EXCEPTION_TYPE(client.SendRPCMessage("asdfasfwer", result), + JsonRpcException, check_exception1); } #ifndef WIN32 -TEST_CASE("test_http_server_multiplestart", TEST_MODULE) -{ - HttpServer server(TEST_PORT); - CHECK(server.StartListening() == true); +TEST_CASE("test_http_server_multiplestart", TEST_MODULE) { + HttpServer server(TEST_PORT); + CHECK(server.StartListening() == true); - HttpServer server2(TEST_PORT); - CHECK(server2.StartListening() == false); + HttpServer server2(TEST_PORT); + CHECK(server2.StartListening() == false); - CHECK(server.StopListening() == true); + CHECK(server.StopListening() == true); } #endif -TEST_CASE_METHOD(F, "test_http_client_timeout", TEST_MODULE) -{ - handler.timeout = 20; - client.SetTimeout(10); - string result; - CHECK_EXCEPTION_TYPE(client.SendRPCMessage("Test", result), JsonRpcException, check_exception1); - handler.timeout = 0; - client.SetTimeout(10000); - handler.response = "asdf"; - client.SendRPCMessage("", result); - CHECK(result == "asdf"); - server.StopListening(); - CHECK_EXCEPTION_TYPE(client.SendRPCMessage("Test", result), JsonRpcException, check_exception1); +TEST_CASE_METHOD(F, "test_http_client_timeout", TEST_MODULE) { + handler.timeout = 20; + client.SetTimeout(10); + string result; + CHECK_EXCEPTION_TYPE(client.SendRPCMessage("Test", result), JsonRpcException, + check_exception1); + handler.timeout = 0; + client.SetTimeout(10000); + handler.response = "asdf"; + client.SendRPCMessage("", result); + CHECK(result == "asdf"); + server.StopListening(); + CHECK_EXCEPTION_TYPE(client.SendRPCMessage("Test", result), JsonRpcException, + check_exception1); } -TEST_CASE("test_http_client_headers", TEST_MODULE) -{ - TestHttpServer server(TEST_PORT); - HttpClient client(CLIENT_URL); +TEST_CASE("test_http_client_headers", TEST_MODULE) { + TestHttpServer server(TEST_PORT); + HttpClient client(CLIENT_URL); - REQUIRE(server.StartListening() == true); - client.AddHeader("X-Auth", "1234"); - server.SetResponse("asdf"); - string result; - client.SendRPCMessage("", result); - CHECK(result == "asdf"); - CHECK(server.GetHeader("X-Auth") == "1234"); + REQUIRE(server.StartListening() == true); + client.AddHeader("X-Auth", "1234"); + server.SetResponse("asdf"); + string result; + client.SendRPCMessage("", result); + CHECK(result == "asdf"); + CHECK(server.GetHeader("X-Auth") == "1234"); - client.RemoveHeader("X-Auth"); - client.SendRPCMessage("", result); - CHECK(server.GetHeader("X-Auth") == ""); + client.RemoveHeader("X-Auth"); + client.SendRPCMessage("", result); + CHECK(server.GetHeader("X-Auth") == ""); - server.StopListening(); + server.StopListening(); } -TEST_CASE_METHOD(F, "test_http_get", TEST_MODULE) -{ - CURL* curl = curl_easy_init(); +TEST_CASE_METHOD(F, "test_http_get", TEST_MODULE) { + CURL *curl = curl_easy_init(); - curl_easy_setopt(curl, CURLOPT_URL, CLIENT_URL); - curl_easy_setopt(curl, CURLOPT_NOBODY, 1); - CURLcode code = curl_easy_perform(curl); - REQUIRE(code == CURLE_OK); + curl_easy_setopt(curl, CURLOPT_URL, CLIENT_URL); + curl_easy_setopt(curl, CURLOPT_NOBODY, 1); + CURLcode code = curl_easy_perform(curl); + REQUIRE(code == CURLE_OK); - long http_code = 0; - curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code); - CHECK(http_code == 405); + long http_code = 0; + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); + CHECK(http_code == 405); - curl_easy_cleanup(curl); + curl_easy_cleanup(curl); } -TEST_CASE_METHOD(F, "test_http_get_options", TEST_MODULE) -{ - CURL* curl = curl_easy_init(); +TEST_CASE_METHOD(F, "test_http_get_options", TEST_MODULE) { + CURL *curl = curl_easy_init(); - curl_easy_setopt(curl, CURLOPT_URL, CLIENT_URL); - curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "OPTIONS"); - CURLcode code = curl_easy_perform(curl); - REQUIRE(code == CURLE_OK); + curl_easy_setopt(curl, CURLOPT_URL, CLIENT_URL); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "OPTIONS"); + CURLcode code = curl_easy_perform(curl); + REQUIRE(code == CURLE_OK); - long http_code = 0; - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); - CHECK(http_code == 200); // No error when server asked for OPTIONS. + long http_code = 0; + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); + CHECK(http_code == 200); // No error when server asked for OPTIONS. - curl_easy_cleanup(curl); + curl_easy_cleanup(curl); } -TEST_CASE("test_http_server_endpoints", TEST_MODULE) -{ - MockClientConnectionHandler handler1; - MockClientConnectionHandler handler2; +TEST_CASE("test_http_server_endpoints", TEST_MODULE) { + MockClientConnectionHandler handler1; + MockClientConnectionHandler handler2; - handler1.response = "response1"; - handler2.response = "response2"; + handler1.response = "response1"; + handler2.response = "response2"; - HttpServer server(TEST_PORT); - server.SetUrlHandler("/handler1", &handler1); - server.SetUrlHandler("/handler2", &handler2); + HttpServer server(TEST_PORT); + server.SetUrlHandler("/handler1", &handler1); + server.SetUrlHandler("/handler2", &handler2); - REQUIRE(server.StartListening() == true); - HttpClient client1("http://localhost:8383/handler1"); - HttpClient client2("http://localhost:8383/handler2"); - HttpClient client3("http://localhost:8383/handler3"); + REQUIRE(server.StartListening() == true); + HttpClient client1("http://localhost:8383/handler1"); + HttpClient client2("http://localhost:8383/handler2"); + HttpClient client3("http://localhost:8383/handler3"); - string response; - client1.SendRPCMessage("test", response); - CHECK(response == "response1"); - client2.SendRPCMessage("test", response); - CHECK(response == "response2"); + string response; + client1.SendRPCMessage("test", response); + CHECK(response == "response1"); + client2.SendRPCMessage("test", response); + CHECK(response == "response2"); - CHECK_EXCEPTION_TYPE(client3.SendRPCMessage("test", response), JsonRpcException, check_exception2); + CHECK_EXCEPTION_TYPE(client3.SendRPCMessage("test", response), + JsonRpcException, check_exception2); - client3.SetUrl("http://localhost:8383/handler2"); - client3.SendRPCMessage("test", response); - CHECK(response == "response2"); + client3.SetUrl("http://localhost:8383/handler2"); + client3.SendRPCMessage("test", response); + CHECK(response == "response2"); - server.StopListening(); + server.StopListening(); } - -TEST_CASE_METHOD(F, "test_http_server_longpost", TEST_MODULE) -{ - int mb = 5; - unsigned long size = mb * 1024*1024; - char* str = (char*) malloc(size * sizeof(char)); - if (str == NULL) - { - FAIL("Could not allocate enough memory for test"); - } - for (unsigned long i=0; i < size; i++) - { - str[i] = (char)('a'+(i%26)); - } - str[size-1] = '\0'; - - handler.response = str; - string response; - client.SetTimeout(-1); - client.SendRPCMessage(str, response); - - CHECK(handler.request == str); - CHECK(response == handler.response); - CHECK(response.size() == size-1); - - free(str); +TEST_CASE_METHOD(F, "test_http_server_longpost", TEST_MODULE) { + int mb = 5; + unsigned long size = mb * 1024 * 1024; + char *str = (char *)malloc(size * sizeof(char)); + if (str == NULL) { + FAIL("Could not allocate enough memory for test"); + } + for (unsigned long i = 0; i < size; i++) { + str[i] = (char)('a' + (i % 26)); + } + str[size - 1] = '\0'; + + handler.response = str; + string response; + client.SetTimeout(-1); + client.SendRPCMessage(str, response); + + CHECK(handler.request == str); + CHECK(response == handler.response); + CHECK(response.size() == size - 1); + + free(str); } -TEST_CASE("test_http_server_ssl", TEST_MODULE) -{ - HttpServer server(TEST_PORT, "/a/b/c", "/d/e/f"); - CHECK(server.StartListening() == false); +TEST_CASE("test_http_server_ssl", TEST_MODULE) { + HttpServer server(TEST_PORT, "/a/b/c", "/d/e/f"); + CHECK(server.StartListening() == false); - HttpServer server2(TEST_PORT, "server.pem", "server.key"); - CHECK(server2.StartListening() == true); - server2.StopListening(); + HttpServer server2(TEST_PORT, "server.pem", "server.key"); + CHECK(server2.StartListening() == true); + server2.StopListening(); } #endif diff --git a/src/test/test_connector_redis.cpp b/src/test/test_connector_redis.cpp new file mode 100644 index 00000000..80bd1e03 --- /dev/null +++ b/src/test/test_connector_redis.cpp @@ -0,0 +1,219 @@ +/************************************************************************* + * libjson-rpc-cpp + ************************************************************************* + * @file test_connector_redis.cpp + * @date 13.08.2017 + * @author Jacques Software + * @license See attached LICENSE.txt + ************************************************************************/ + +#ifdef REDIS_TESTING +#include +#include +#include + +#include "checkexception.h" +#include "mockclientconnectionhandler.h" +#include "testredisserver.h" + +#include +#include + +using namespace jsonrpc; +using namespace std; + +#define TEST_PORT 6380 +#define TEST_HOST "127.0.0.1" +#define TEST_QUEUE "mytest" + +#define TEST_MODULE "[connector_redis]" + +namespace testredisserver { +/////////////////////////////////////////////////////////////////////////// +// Prepare redis fixture +// +// This fixture shall: +// 1. Start up a redis-server in the backgroun +// 2. Create a standard RedisServer connection which can be used +// 3. Create a standard RedisClient connection which can be used +// 4. Create a hiredis connection which can also be used +// +struct F { + TestRedisServer redis_server; + redisContext *con; + RedisServer server; + RedisClient client; + MockClientConnectionHandler handler; + + F() + : redis_server(), server(TEST_HOST, TEST_PORT, TEST_QUEUE), + client(TEST_HOST, TEST_PORT, TEST_QUEUE) { + con = redisConnect(TEST_HOST, TEST_PORT); + server.SetHandler(&handler); + server.StartListening(); + client.SetTimeout(1); + } + ~F() { + redisFree(con); + server.StopListening(); + redis_server.Stop(); + } +}; + +bool check_exception1(JsonRpcException const &ex) { + return ex.GetCode() == Errors::ERROR_CLIENT_CONNECTOR; +} + +bool check_exception2(JsonRpcException const &ex) { + return ex.GetCode() == Errors::ERROR_RPC_INTERNAL_ERROR; +} +} // namespace testredisserver + +using namespace testredisserver; + +TEST_CASE_METHOD(F, "test_redis_success", TEST_MODULE) { + handler.response = "exampleresponse"; + string result; + client.SendRPCMessage("examplerequest", result); + + CHECK(handler.request == "examplerequest"); + CHECK(result == "exampleresponse"); +} + +TEST_CASE_METHOD(F, "test_redis_multiple", TEST_MODULE) { + for (int i = 0; i < 50; i++) { + stringstream request; + stringstream response; + request << "examplerequest" << i; + response << "exampleresponse" << i; + handler.response = response.str(); + string result; + client.SendRPCMessage(request.str(), result); + CHECK(handler.request == request.str()); + CHECK(result == response.str()); + } +} + +TEST_CASE("test_redis_client_error", TEST_MODULE) { + CHECK_EXCEPTION_TYPE(RedisClient client(TEST_HOST, TEST_PORT + 1, TEST_QUEUE), + JsonRpcException, check_exception1); +} + +#ifdef __APPLE__ +#define RAND_FIRST "2PN0bdwPY7CA8M06" +#define RAND_SECOND "zVKEkhHgZVgtV1iT" +#else +#define RAND_FIRST "fa37JncCHryDsbza" +#define RAND_SECOND "yy4cBWDxS22JjzhM" +#endif + +TEST_CASE_METHOD(F, "test_redis_client_ret_queue", TEST_MODULE) { + redisReply *reply; + string retqueue; + + // Check correct behaviour from a known point + srand(0); + GetReturnQueue(con, TEST_QUEUE, retqueue); + CHECK(retqueue == TEST_QUEUE "_" RAND_FIRST); + + GetReturnQueue(con, TEST_QUEUE, retqueue); + CHECK(retqueue == TEST_QUEUE "_" RAND_SECOND); + + // Check behaviour when queue already exists + reply = (redisReply *)redisCommand(con, "LPUSH %s %s", + TEST_QUEUE "_" RAND_FIRST, "test"); + freeReplyObject(reply); + + srand(0); + GetReturnQueue(con, TEST_QUEUE, retqueue); + CHECK(retqueue == TEST_QUEUE "_" RAND_SECOND); + + // Check for a failure with the redis context + redis_server.Stop(); + CHECK_EXCEPTION_TYPE(GetReturnQueue(con, TEST_QUEUE, retqueue), + JsonRpcException, check_exception1); +} + +TEST_CASE_METHOD(F, "test_redis_client_set_queue", TEST_MODULE) { + RedisServer server2(TEST_HOST, TEST_PORT, TEST_QUEUE "_other"); + MockClientConnectionHandler handler2; + server2.SetHandler(&handler2); + server2.StartListening(); + + client.SetQueue(TEST_QUEUE "_other"); + + handler2.response = "exampleresponse"; + string result; + client.SendRPCMessage("examplerequest", result); + + CHECK(handler2.request == "examplerequest"); + CHECK(result == "exampleresponse"); + + server2.StopListening(); +} + +TEST_CASE_METHOD(F, "test_redis_server_error", TEST_MODULE) { + RedisServer server2(TEST_HOST, TEST_PORT + 1, TEST_QUEUE); + + CHECK(server2.StartListening() == false); + CHECK(server.StartListening() == true); + CHECK(server2.StopListening() == true); + CHECK(server.StopListening() == true); + CHECK(server.StopListening() == true); +} + +TEST_CASE_METHOD(F, "test_redis_server_bad_request", TEST_MODULE) { + string queue = TEST_QUEUE; + string req1 = "test"; + + redisReply *reply; + + reply = (redisReply *)redisCommand(con, "LPUSH %s %s", queue.c_str(), + req1.c_str()); + CHECK(handler.request == ""); + freeReplyObject(reply); + reply = (redisReply *)redisCommand(con, "DEL %s", queue.c_str()); + freeReplyObject(reply); + + reply = + (redisReply *)redisCommand(con, "SET %s %s", queue.c_str(), req1.c_str()); + CHECK(handler.request == ""); + freeReplyObject(reply); + reply = (redisReply *)redisCommand(con, "DEL %s", queue.c_str()); + freeReplyObject(reply); +} + +TEST_CASE_METHOD(F, "test_redis_client_timeout", TEST_MODULE) { + RedisClient client2(TEST_HOST, TEST_PORT, "invalid_queue"); + client2.SetTimeout(1); + + string result; + CHECK_EXCEPTION_TYPE(client2.SendRPCMessage("examplerequest", result), + JsonRpcException, check_exception1); +} + +TEST_CASE_METHOD(F, "test_redis_server_longrequest", TEST_MODULE) { + int mb = 5; + unsigned long size = mb * 1024 * 1024; + char *str = (char *)malloc(size * sizeof(char)); + if (str == NULL) { + FAIL("Could not allocate enough memory for test"); + } + for (unsigned long i = 0; i < size; i++) { + str[i] = (char)('a' + (i % 26)); + } + str[size - 1] = '\0'; + + handler.response = str; + string response; + client.SetTimeout(10); + client.SendRPCMessage(str, response); + + CHECK(handler.request == str); + CHECK(response == handler.response); + CHECK(response.size() == size - 1); + + free(str); +} + +#endif diff --git a/src/test/test_connector_tcpsocket.cpp b/src/test/test_connector_tcpsocket.cpp index 920077bf..adf624a1 100644 --- a/src/test/test_connector_tcpsocket.cpp +++ b/src/test/test_connector_tcpsocket.cpp @@ -8,10 +8,10 @@ ************************************************************************/ #ifdef TCPSOCKET_TESTING +#include "mockclientconnectionhandler.h" #include -#include #include -#include "mockclientconnectionhandler.h" +#include #include "checkexception.h" @@ -19,7 +19,7 @@ using namespace jsonrpc; using namespace std; #ifndef DELIMITER_CHAR - #define DELIMITER_CHAR char(0x0A) +#define DELIMITER_CHAR char(0x0A) #endif #define TEST_MODULE "[connector_tcpsocket]" @@ -27,68 +27,60 @@ using namespace std; #define IP "127.0.0.1" #define PORT 50000 -namespace testtcpsocketserver -{ - struct F { - TcpSocketServer server; - TcpSocketClient client; - MockClientConnectionHandler handler; - - F() : - server(IP, PORT), - client(IP, PORT) - { - server.SetHandler(&handler); - REQUIRE(server.StartListening()); - } - ~F() - { - server.StopListening(); - } - }; - - bool check_exception1(JsonRpcException const&ex) - { - return ex.GetCode() == Errors::ERROR_CLIENT_CONNECTOR; - } +namespace testtcpsocketserver { +struct F { + TcpSocketServer server; + TcpSocketClient client; + MockClientConnectionHandler handler; + + F() : server(IP, PORT), client(IP, PORT) { + server.SetHandler(&handler); + REQUIRE(server.StartListening()); + } + ~F() { server.StopListening(); } +}; + +bool check_exception1(JsonRpcException const &ex) { + return ex.GetCode() == Errors::ERROR_CLIENT_CONNECTOR; } +} // namespace testtcpsocketserver using namespace testtcpsocketserver; -TEST_CASE_METHOD(F, "test_tcpsocket_success", TEST_MODULE) -{ - handler.response = "exampleresponse"; - handler.timeout = 100; - string result; - string request = "examplerequest"; - request.push_back(DELIMITER_CHAR); - string expectedResult = "exampleresponse"; - expectedResult.push_back(DELIMITER_CHAR); +TEST_CASE_METHOD(F, "test_tcpsocket_success", TEST_MODULE) { + handler.response = "exampleresponse"; + handler.timeout = 100; + string result; + string request = "examplerequest"; + string expectedResult = "exampleresponse"; - client.SendRPCMessage(request, result); + client.SendRPCMessage(request, result); - CHECK(handler.request == request); - CHECK(result == expectedResult); + CHECK(handler.request == request); + CHECK(result == expectedResult); } -TEST_CASE("test_tcpsocket_server_multiplestart", TEST_MODULE) -{ - TcpSocketServer server(IP, PORT); - CHECK(server.StartListening() == true); - CHECK(server.StartListening() == false); +TEST_CASE("test_tcpsocket_server_multiplestart", TEST_MODULE) { + TcpSocketServer server(IP, PORT); + CHECK(server.StartListening() == true); + CHECK(server.StartListening() == false); - TcpSocketServer server2(IP, PORT); - CHECK(server2.StartListening() == false); - CHECK(server2.StopListening() == false); + TcpSocketServer server2(IP, PORT); + CHECK(server2.StartListening() == false); + CHECK(server2.StopListening() == false); - CHECK(server.StopListening() == true); + CHECK(server.StopListening() == true); } - -TEST_CASE("test_tcpsocket_client_invalid", TEST_MODULE) -{ - TcpSocketClient client("127.0.0.1", 40000); //If this test fails, check that port 40000 is really unused. If it is used, change this port value to an unused port, recompile tests and run tests again. - string result; - CHECK_EXCEPTION_TYPE(client.SendRPCMessage("foobar", result), JsonRpcException, check_exception1); +TEST_CASE("test_tcpsocket_client_invalid", TEST_MODULE) { + TcpSocketClient client("127.0.0.1", 40000); // If this test fails, check that + // port 40000 is really unused. If + // it is used, change this port + // value to an unused port, + // recompile tests and run tests + // again. + string result; + CHECK_EXCEPTION_TYPE(client.SendRPCMessage("foobar", result), + JsonRpcException, check_exception1); } #endif diff --git a/src/test/test_connector_unixdomainsocket.cpp b/src/test/test_connector_unixdomainsocket.cpp index bcd27108..a3d237ab 100644 --- a/src/test/test_connector_unixdomainsocket.cpp +++ b/src/test/test_connector_unixdomainsocket.cpp @@ -8,89 +8,73 @@ ************************************************************************/ #ifdef UNIXDOMAINSOCKET_TESTING +#include "mockclientconnectionhandler.h" #include -#include #include -#include "mockclientconnectionhandler.h" +#include #include "checkexception.h" +#include using namespace jsonrpc; using namespace std; -#ifndef DELIMITER_CHAR - #define DELIMITER_CHAR char(0x0A) -#endif - #define TEST_MODULE "[connector_unixdomainsocket]" -#define SOCKET_PATH "/tmp/jsonrpccpp-socket" - -namespace testunixdomainsocketserver -{ - struct F { - UnixDomainSocketServer server; - UnixDomainSocketClient client; - MockClientConnectionHandler handler; - - F() : - server(SOCKET_PATH), - client(SOCKET_PATH) - { - server.SetHandler(&handler); - REQUIRE(server.StartListening()); - } - ~F() - { - server.StopListening(); - unlink(SOCKET_PATH); - } - }; - - bool check_exception1(JsonRpcException const&ex) - { - return ex.GetCode() == Errors::ERROR_CLIENT_CONNECTOR; - } +namespace testunixdomainsocketserver { +struct F { + string filename; + UnixDomainSocketServer server; + UnixDomainSocketClient client; + MockClientConnectionHandler handler; + + F() : filename("/tmp/somedomainsocket"), server(filename), client(filename) { + remove(filename.c_str()); + server.SetHandler(&handler); + REQUIRE(server.StartListening()); + } + ~F() { server.StopListening(); } +}; + +bool check_exception1(JsonRpcException const &ex) { + return ex.GetCode() == Errors::ERROR_CLIENT_CONNECTOR; } +} // namespace testunixdomainsocketserver using namespace testunixdomainsocketserver; -TEST_CASE_METHOD(F, "test_unixdomainsocket_success", TEST_MODULE) -{ - handler.response = "exampleresponse"; - handler.timeout = 100; - string result; - string request = "examplerequest"; - request.push_back(DELIMITER_CHAR); - string expectedResult = "exampleresponse"; - expectedResult.push_back(DELIMITER_CHAR); +TEST_CASE_METHOD(F, "test_unixdomainsocket_success", TEST_MODULE) { + handler.response = "exampleresponse"; + handler.timeout = 100; + string result; + string request = "examplerequest"; + string expectedResult = "exampleresponse"; - client.SendRPCMessage(request, result); + client.SendRPCMessage(request, result); - CHECK(handler.request == request); - CHECK(result == expectedResult); + CHECK(handler.request == request); + CHECK(result == expectedResult); } +TEST_CASE("test_unixdomainsocket_server_multiplestart", TEST_MODULE) { + string filename = "/tmp/somedomainsocket"; -TEST_CASE("test_unixdomainsocket_server_multiplestart", TEST_MODULE) -{ - UnixDomainSocketServer server(SOCKET_PATH); - CHECK(server.StartListening() == true); - CHECK(server.StartListening() == false); - - UnixDomainSocketServer server2(SOCKET_PATH); - CHECK(server2.StartListening() == false); - CHECK(server2.StopListening() == false); + UnixDomainSocketServer *server = new UnixDomainSocketServer(filename); + CHECK(server->StartListening() == true); + CHECK(server->StartListening() == false); - CHECK(server.StopListening() == true); + UnixDomainSocketServer server2(filename); + CHECK(server2.StartListening() == false); + CHECK(server2.StopListening() == false); - unlink(SOCKET_PATH); + CHECK(server->StopListening() == true); + delete server; } -TEST_CASE("test_unixdomainsocket_client_invalid", TEST_MODULE) -{ - UnixDomainSocketClient client("tmp/someinvalidpath"); - string result; - CHECK_EXCEPTION_TYPE(client.SendRPCMessage("foobar", result), JsonRpcException, check_exception1); +TEST_CASE("test_unixdomainsocket_client_invalid", TEST_MODULE) { + UnixDomainSocketClient client("tmp/someinvalidpath"); + string result; + CHECK_EXCEPTION_TYPE(client.SendRPCMessage("foobar", result), + JsonRpcException, check_exception1); } #endif diff --git a/src/test/test_integration.cpp b/src/test/test_integration.cpp index bc6eefe2..8c487e14 100644 --- a/src/test/test_integration.cpp +++ b/src/test/test_integration.cpp @@ -7,13 +7,12 @@ * @license See attached LICENSE.txt ************************************************************************/ - #ifdef STUBGEN_TESTING #include -#include #ifdef HTTP_TESTING #include +#include #endif #include "gen/abstractstubserver.h" @@ -24,85 +23,73 @@ using namespace std; #define TEST_PORT 8383 #define CLIENT_URL "http://localhost:8383" -#define TEST_PATH "/tmp/jsonrpccppintegrationtest" #define TEST_MODULE "[integration]" class StubServer : public AbstractStubServer { - public: - StubServer(AbstractServerConnector &connector) : AbstractStubServer(connector) {} - virtual void notifyServer() { - - } - - virtual std::string sayHello(const std::string& name) - { - return string("Hello ") + name; - } - - virtual int addNumbers(int param1, int param2) - { - return param1+param2; - } - - virtual double addNumbers2(double param1, double param2) - { - return param1 + param2; - } - - virtual bool isEqual(const std::string& str1, const std::string &str2) - { - return str1 == str2; - } - - virtual Json::Value buildObject(const std::string &name, int age) - { - Json::Value result; - result["name"] = name; - result["age"] = age; - return result; - } - - virtual std::string methodWithoutParameters() - { - return "foo"; - } -}; +public: + StubServer(AbstractServerConnector &connector) + : AbstractStubServer(connector) {} + virtual void notifyServer() {} -#ifdef HTTP_TESTING + virtual std::string sayHello(const std::string &name) { + return string("Hello ") + name; + } -TEST_CASE("test_integration_http", TEST_MODULE) -{ - HttpServer sconn(TEST_PORT); - HttpClient cconn(CLIENT_URL); - StubServer server(sconn); - server.StartListening(); - StubClient client(cconn); + virtual int addNumbers(int param1, int param2) { return param1 + param2; } - CHECK(client.addNumbers(3,4) == 7); - CHECK(client.addNumbers2(3.2,4.2) == 7.4); - CHECK(client.sayHello("Test") == "Hello Test"); - CHECK(client.methodWithoutParameters() == "foo"); - CHECK(client.isEqual("str1", "str1") == true); - CHECK(client.isEqual("str1", "str2") == false); + virtual double addNumbers2(double param1, double param2) { + return param1 + param2; + } - Json::Value result = client.buildObject("Test", 33); - CHECK(result["name"].asString() == "Test"); - CHECK(result["age"].asInt() == 33); + virtual bool isEqual(const std::string &str1, const std::string &str2) { + return str1 == str2; + } - server.StopListening(); + virtual Json::Value buildObject(const std::string &name, int age) { + Json::Value result; + result["name"] = name; + result["age"] = age; + return result; + } + + virtual std::string methodWithoutParameters() { return "foo"; } +}; + +#ifdef HTTP_TESTING + +TEST_CASE("test_integration_http", TEST_MODULE) { + HttpServer sconn(TEST_PORT); + HttpClient cconn(CLIENT_URL); + StubServer server(sconn); + server.StartListening(); + StubClient client(cconn); + + CHECK(client.addNumbers(3, 4) == 7); + CHECK(client.addNumbers2(3.2, 4.2) == 7.4); + CHECK(client.sayHello("Test") == "Hello Test"); + CHECK(client.methodWithoutParameters() == "foo"); + CHECK(client.isEqual("str1", "str1") == true); + CHECK(client.isEqual("str1", "str2") == false); + + Json::Value result = client.buildObject("Test", 33); + CHECK(result["name"].asString() == "Test"); + CHECK(result["age"].asInt() == 33); + + server.StopListening(); } #endif #ifdef UNIXDOMAINSOCKET_TESTING -#include #include - +#include +/* TEST_CASE("test_integration_unixdomain", TEST_MODULE) { - UnixDomainSocketServer sconn(TEST_PATH); - UnixDomainSocketClient cconn(TEST_PATH); + string filename = "/tmp/jsonrpcunixdomain"; + UnixDomainSocketServer sconn(filename); + UnixDomainSocketClient cconn(filename); StubServer server(sconn); server.StartListening(); @@ -120,47 +107,45 @@ TEST_CASE("test_integration_unixdomain", TEST_MODULE) CHECK(result["age"].asInt() == 33); server.StopListening(); -} + remove(filename.c_str()); +}*/ #endif #ifdef FILEDESCRIPTOR_TESTING -#include #include +#include +TEST_CASE("test_integration_filedescriptor", TEST_MODULE) { + int c2sfd[2]; // Client to server fd + int s2cfd[2]; // Server to client fd + pipe(c2sfd); + pipe(s2cfd); -TEST_CASE("test_integration_filedescriptor", TEST_MODULE) -{ - int c2sfd[2]; // Client to server fd - int s2cfd[2]; // Server to client fd - pipe(c2sfd); - pipe(s2cfd); + FileDescriptorServer sconn(c2sfd[0], s2cfd[1]); + FileDescriptorClient cconn(s2cfd[0], c2sfd[1]); - FileDescriptorServer sconn(c2sfd[0], s2cfd[1]); - FileDescriptorClient cconn(s2cfd[0], c2sfd[1]); + StubServer server(sconn); + server.StartListening(); + StubClient client(cconn); - StubServer server(sconn); - server.StartListening(); - StubClient client(cconn); + CHECK(client.addNumbers(3, 4) == 7); + CHECK(client.addNumbers2(3.2, 4.2) == 7.4); + CHECK(client.sayHello("Test") == "Hello Test"); + CHECK(client.methodWithoutParameters() == "foo"); + CHECK(client.isEqual("str1", "str1") == true); + CHECK(client.isEqual("str1", "str2") == false); - CHECK(client.addNumbers(3,4) == 7); - CHECK(client.addNumbers2(3.2,4.2) == 7.4); - CHECK(client.sayHello("Test") == "Hello Test"); - CHECK(client.methodWithoutParameters() == "foo"); - CHECK(client.isEqual("str1", "str1") == true); - CHECK(client.isEqual("str1", "str2") == false); + Json::Value result = client.buildObject("Test", 33); + CHECK(result["name"].asString() == "Test"); + CHECK(result["age"].asInt() == 33); - Json::Value result = client.buildObject("Test", 33); - CHECK(result["name"].asString() == "Test"); - CHECK(result["age"].asInt() == 33); + server.StopListening(); - server.StopListening(); - - close(c2sfd[0]); - close(c2sfd[1]); - close(s2cfd[0]); - close(s2cfd[1]); + close(c2sfd[0]); + close(c2sfd[1]); + close(s2cfd[0]); + close(s2cfd[1]); } #endif - #endif diff --git a/src/test/test_server.cpp b/src/test/test_server.cpp index e68995c2..02086f5f 100644 --- a/src/test/test_server.cpp +++ b/src/test/test_server.cpp @@ -7,376 +7,410 @@ * @license See attached LICENSE.txt ************************************************************************/ -#include -#include "testserver.h" #include "mockserverconnector.h" +#include "testserver.h" +#include #define TEST_MODULE "[server]" using namespace jsonrpc; using namespace std; -namespace testserver -{ - struct F { - MockServerConnector c; - TestServer server; +namespace testserver { +struct F { + MockServerConnector c; + TestServer server; - F() : server(c) {} - }; + F() : server(c) {} +}; - struct F1 { - MockServerConnector c; - TestServer server; +struct F1 { + MockServerConnector c; + TestServer server; - F1() : server(c, JSONRPC_SERVER_V1) {} - }; -} + F1() : server(c, JSONRPC_SERVER_V1) {} +}; +} // namespace testserver using namespace testserver; -TEST_CASE_METHOD(F, "test_server_v2_method_success", TEST_MODULE) -{ - c.SetRequest("{\"jsonrpc\":\"2.0\", \"id\": 1, \"method\": \"sayHello\",\"params\":{\"name\":\"Peter\"}}"); - CHECK(c.GetJsonResponse()["result"].asString() == "Hello: Peter!"); - CHECK(c.GetJsonResponse()["id"].asInt() == 1); - CHECK(c.GetJsonResponse()["jsonrpc"].asString() == "2.0"); - CHECK(c.GetJsonResponse().isMember("error") == false); - - c.SetRequest("{\"jsonrpc\":\"2.0\", \"id\": 1, \"method\": \"add\",\"params\":{\"value1\":5,\"value2\":7}}"); - CHECK(c.GetJsonResponse()["result"].asInt() == 12); - CHECK(c.GetJsonResponse()["id"].asInt() == 1); - CHECK(c.GetJsonResponse()["jsonrpc"].asString() == "2.0"); - CHECK(c.GetJsonResponse().isMember("error") == false); - - c.SetRequest("{\"jsonrpc\":\"2.0\", \"id\": 1, \"method\": \"sub\",\"params\":[5,7]}"); - CHECK(c.GetJsonResponse()["result"].asInt() == -2); - CHECK(c.GetJsonResponse()["id"].asInt() == 1); - CHECK(c.GetJsonResponse()["jsonrpc"].asString() == "2.0"); - CHECK(c.GetJsonResponse().isMember("error") == false); - - c.SetRequest("{\"jsonrpc\":\"2.0\", \"id\": null, \"method\": \"sub\",\"params\":[5,7]}"); - CHECK(c.GetJsonResponse()["result"].asInt() == -2); - CHECK(c.GetJsonResponse()["id"].isNull() == true); - CHECK(c.GetJsonResponse()["jsonrpc"].asString() == "2.0"); - CHECK(c.GetJsonResponse().isMember("error") == false); - - c.SetRequest("{\"jsonrpc\":\"2.0\", \"id\": \"1\", \"method\": \"sub\",\"params\":[5,7]}"); - CHECK(c.GetJsonResponse()["result"].asInt() == -2); - CHECK(c.GetJsonResponse()["id"].asString() == "1"); - CHECK(c.GetJsonResponse()["jsonrpc"].asString() == "2.0"); - CHECK(c.GetJsonResponse().isMember("error") == false); - - c.SetRequest("{\"jsonrpc\":\"2.0\", \"id\": 4294967295, \"method\": \"sub\",\"params\":[5,7]}"); - CHECK(c.GetJsonResponse()["result"].asInt() == -2); - CHECK(c.GetJsonResponse()["id"].asLargestUInt() == (unsigned long)4294967295); - CHECK(c.GetJsonResponse()["jsonrpc"].asString() == "2.0"); - CHECK(c.GetJsonResponse().isMember("error") == false); - +TEST_CASE_METHOD(F, "test_server_v2_method_success", TEST_MODULE) { + c.SetRequest("{\"jsonrpc\":\"2.0\", \"id\": 1, \"method\": " + "\"sayHello\",\"params\":{\"name\":\"Peter\"}}"); + CHECK(c.GetJsonResponse()["result"].asString() == "Hello: Peter!"); + CHECK(c.GetJsonResponse()["id"].asInt() == 1); + CHECK(c.GetJsonResponse()["jsonrpc"].asString() == "2.0"); + CHECK(c.GetJsonResponse().isMember("error") == false); + + c.SetRequest("{\"jsonrpc\":\"2.0\", \"id\": 1, \"method\": " + "\"add\",\"params\":{\"value1\":5,\"value2\":7}}"); + CHECK(c.GetJsonResponse()["result"].asInt() == 12); + CHECK(c.GetJsonResponse()["id"].asInt() == 1); + CHECK(c.GetJsonResponse()["jsonrpc"].asString() == "2.0"); + CHECK(c.GetJsonResponse().isMember("error") == false); + + c.SetRequest( + "{\"jsonrpc\":\"2.0\", \"id\": 1, \"method\": \"sub\",\"params\":[5,7]}"); + CHECK(c.GetJsonResponse()["result"].asInt() == -2); + CHECK(c.GetJsonResponse()["id"].asInt() == 1); + CHECK(c.GetJsonResponse()["jsonrpc"].asString() == "2.0"); + CHECK(c.GetJsonResponse().isMember("error") == false); + + c.SetRequest("{\"jsonrpc\":\"2.0\", \"id\": null, \"method\": " + "\"sub\",\"params\":[5,7]}"); + CHECK(c.GetJsonResponse()["result"].asInt() == -2); + CHECK(c.GetJsonResponse()["id"].isNull() == true); + CHECK(c.GetJsonResponse()["jsonrpc"].asString() == "2.0"); + CHECK(c.GetJsonResponse().isMember("error") == false); + + c.SetRequest("{\"jsonrpc\":\"2.0\", \"id\": \"1\", \"method\": " + "\"sub\",\"params\":[5,7]}"); + CHECK(c.GetJsonResponse()["result"].asInt() == -2); + CHECK(c.GetJsonResponse()["id"].asString() == "1"); + CHECK(c.GetJsonResponse()["jsonrpc"].asString() == "2.0"); + CHECK(c.GetJsonResponse().isMember("error") == false); + + c.SetRequest("{\"jsonrpc\":\"2.0\", \"id\": 4294967295, \"method\": " + "\"sub\",\"params\":[5,7]}"); + CHECK(c.GetJsonResponse()["result"].asInt() == -2); + CHECK(c.GetJsonResponse()["id"].asLargestUInt() == (unsigned long)4294967295); + CHECK(c.GetJsonResponse()["jsonrpc"].asString() == "2.0"); + CHECK(c.GetJsonResponse().isMember("error") == false); } -TEST_CASE_METHOD(F, "test_server_v2_notification_success", TEST_MODULE) -{ - c.SetRequest("{\"jsonrpc\":\"2.0\", \"method\": \"initCounter\",\"params\":{\"value\": 33}}"); - CHECK(server.getCnt() == 33); - CHECK(c.GetResponse() == ""); +TEST_CASE_METHOD(F, "test_server_v2_notification_success", TEST_MODULE) { + c.SetRequest("{\"jsonrpc\":\"2.0\", \"method\": " + "\"initCounter\",\"params\":{\"value\": 33}}"); + CHECK(server.getCnt() == 33); + CHECK(c.GetResponse() == ""); - c.SetRequest("{\"jsonrpc\":\"2.0\", \"method\": \"incrementCounter\",\"params\":{\"value\": 33}}"); - CHECK(server.getCnt() == 66); - CHECK(c.GetResponse() == ""); + c.SetRequest("{\"jsonrpc\":\"2.0\", \"method\": " + "\"incrementCounter\",\"params\":{\"value\": 33}}"); + CHECK(server.getCnt() == 66); + CHECK(c.GetResponse() == ""); } -TEST_CASE_METHOD(F, "test_server_v2_invalidjson", TEST_MODULE) -{ - c.SetRequest("{\"jsonrpc\":\"2."); - CHECK(c.GetJsonResponse()["error"]["code"] == -32700); - CHECK(c.GetJsonResponse().isMember("result") == false); +TEST_CASE_METHOD(F, "test_server_v2_invalidjson", TEST_MODULE) { + c.SetRequest("{\"jsonrpc\":\"2."); + CHECK(c.GetJsonResponse()["error"]["code"] == -32700); + CHECK(c.GetJsonResponse().isMember("result") == false); } -TEST_CASE_METHOD(F, "test_server_v2_invalidrequest", TEST_MODULE) -{ - //wrong rpc version - c.SetRequest("{\"jsonrpc\":\"1.0\", \"id\": 1, \"method\": \"sayHello\",\"params\":{\"name\":\"Peter\"}}"); - CHECK(c.GetJsonResponse()["error"]["code"] == -32600); - CHECK(c.GetJsonResponse().isMember("result") == false); - - //wrong rpc version type - c.SetRequest("{\"jsonrpc\":2.0, \"id\": 1, \"method\": \"sayHello\",\"params\":{\"name\":\"Peter\"}}"); - CHECK(c.GetJsonResponse()["error"]["code"] == -32600); - CHECK(c.GetJsonResponse().isMember("result") == false); - - //no method name - c.SetRequest("{\"jsonrpc\":\"2.0\", \"id\": 1,\"params\":{\"name\":\"Peter\"}}"); - CHECK(c.GetJsonResponse()["error"]["code"] == -32600); - CHECK(c.GetJsonResponse().isMember("result") == false); - - //wrong method name type - c.SetRequest("{\"jsonrpc\":\"2.0\", \"id\": 1, \"method\": {}, \"params\":{\"name\":\"Peter\"}}"); - CHECK(c.GetJsonResponse()["error"]["code"] == -32600); - CHECK(c.GetJsonResponse().isMember("result") == false); - - //invalid param structure - c.SetRequest("{\"jsonrpc\":\"2.0\", \"id\": 1, \"method\": \"sayHello\",\"params\":1}"); - CHECK(c.GetJsonResponse()["error"]["code"] == -32600); - CHECK(c.GetJsonResponse().isMember("result") == false); - - // - c.SetRequest("{\"jsonrpc\":\"2.0\", \"id\": 3.2, \"method\": \"sayHello\",\"params\":{\"name\":\"Peter\"}}"); - CHECK(c.GetJsonResponse()["error"]["code"] == -32600); - CHECK(c.GetJsonResponse().isMember("result") == false); - - c.SetRequest("{\"jsonrpc\":\"2.0\", \"id\": 1, \"method\": 3,\"params\":{\"name\":\"Peter\"}}"); - CHECK(c.GetJsonResponse()["error"]["code"] == -32600); - CHECK(c.GetJsonResponse().isMember("result") == false); - - c.SetRequest("{}"); - CHECK(c.GetJsonResponse()["error"]["code"] == -32600); - CHECK(c.GetJsonResponse().isMember("result") == false); - - c.SetRequest("[]"); - CHECK(c.GetJsonResponse()["error"]["code"] == -32600); - CHECK(c.GetJsonResponse().isMember("result") == false); - - c.SetRequest("23"); - CHECK(c.GetJsonResponse()["error"]["code"] == -32600); - CHECK(c.GetJsonResponse().isMember("result") == false); +TEST_CASE_METHOD(F, "test_server_v2_invalidrequest", TEST_MODULE) { + // wrong rpc version + c.SetRequest("{\"jsonrpc\":\"1.0\", \"id\": 1, \"method\": " + "\"sayHello\",\"params\":{\"name\":\"Peter\"}}"); + CHECK(c.GetJsonResponse()["error"]["code"] == -32600); + CHECK(c.GetJsonResponse().isMember("result") == false); + + // wrong rpc version type + c.SetRequest("{\"jsonrpc\":2.0, \"id\": 1, \"method\": " + "\"sayHello\",\"params\":{\"name\":\"Peter\"}}"); + CHECK(c.GetJsonResponse()["error"]["code"] == -32600); + CHECK(c.GetJsonResponse().isMember("result") == false); + + // no method name + c.SetRequest( + "{\"jsonrpc\":\"2.0\", \"id\": 1,\"params\":{\"name\":\"Peter\"}}"); + CHECK(c.GetJsonResponse()["error"]["code"] == -32600); + CHECK(c.GetJsonResponse().isMember("result") == false); + + // wrong method name type + c.SetRequest("{\"jsonrpc\":\"2.0\", \"id\": 1, \"method\": {}, " + "\"params\":{\"name\":\"Peter\"}}"); + CHECK(c.GetJsonResponse()["error"]["code"] == -32600); + CHECK(c.GetJsonResponse().isMember("result") == false); + + // invalid param structure + c.SetRequest("{\"jsonrpc\":\"2.0\", \"id\": 1, \"method\": " + "\"sayHello\",\"params\":1}"); + CHECK(c.GetJsonResponse()["error"]["code"] == -32600); + CHECK(c.GetJsonResponse().isMember("result") == false); + + // + c.SetRequest("{\"jsonrpc\":\"2.0\", \"id\": 3.2, \"method\": " + "\"sayHello\",\"params\":{\"name\":\"Peter\"}}"); + CHECK(c.GetJsonResponse()["error"]["code"] == -32600); + CHECK(c.GetJsonResponse().isMember("result") == false); + + c.SetRequest("{\"jsonrpc\":\"2.0\", \"id\": 1, \"method\": " + "3,\"params\":{\"name\":\"Peter\"}}"); + CHECK(c.GetJsonResponse()["error"]["code"] == -32600); + CHECK(c.GetJsonResponse().isMember("result") == false); + + c.SetRequest("{}"); + CHECK(c.GetJsonResponse()["error"]["code"] == -32600); + CHECK(c.GetJsonResponse().isMember("result") == false); + + c.SetRequest("[]"); + CHECK(c.GetJsonResponse()["error"]["code"] == -32600); + CHECK(c.GetJsonResponse().isMember("result") == false); + + c.SetRequest("23"); + CHECK(c.GetJsonResponse()["error"]["code"] == -32600); + CHECK(c.GetJsonResponse().isMember("result") == false); } -TEST_CASE_METHOD(F, "test_server_v2_method_error", TEST_MODULE) -{ - //invalid methodname - c.SetRequest("{\"jsonrpc\":\"2.0\", \"id\": 1, \"method\": \"sayHello2\",\"params\":{\"name\":\"Peter\"}}"); - CHECK(c.GetJsonResponse()["error"]["code"] == -32601); - CHECK(c.GetJsonResponse().isMember("result") == false); - - //call notification as procedure - c.SetRequest("{\"jsonrpc\":\"2.0\", \"id\": 1, \"method\": \"initCounter\",\"params\":{\"value\":3}}"); - CHECK(c.GetJsonResponse()["error"]["code"] == -32605); - CHECK(c.GetJsonResponse().isMember("result") == false); - - //call procedure as notification - c.SetRequest("{\"jsonrpc\":\"2.0\", \"method\": \"sayHello\",\"params\":{\"name\":\"Peter\"}}"); - CHECK(c.GetJsonResponse()["error"]["code"] == -32604); - CHECK(c.GetJsonResponse().isMember("result") == false); - - c.SetRequest("{\"jsonrpc\":\"2.0\", \"method\": \"sub\",\"params\":{\"value1\":3, \"value\": 4}}"); - CHECK(c.GetJsonResponse()["error"]["code"] == -32604); - CHECK(c.GetJsonResponse().isMember("result") == false); - - c.SetRequest("{\"jsonrpc\":\"2.0\", \"method\": \"add\",\"params\":[3,4]}"); - CHECK(c.GetJsonResponse()["error"]["code"] == -32604); - CHECK(c.GetJsonResponse().isMember("result") == false); - - //userspace exception - c.SetRequest("{\"jsonrpc\":\"2.0\", \"id\": 1, \"method\": \"exceptionMethod\"}"); - CHECK(c.GetJsonResponse()["error"]["code"] == -32099); - CHECK(c.GetJsonResponse()["error"]["message"] == "User exception"); - CHECK(c.GetJsonResponse()["error"]["data"][0] == 33); - CHECK(c.GetJsonResponse().isMember("result") == false); +TEST_CASE_METHOD(F, "test_server_v2_method_error", TEST_MODULE) { + // invalid methodname + c.SetRequest("{\"jsonrpc\":\"2.0\", \"id\": 1, \"method\": " + "\"sayHello2\",\"params\":{\"name\":\"Peter\"}}"); + CHECK(c.GetJsonResponse()["error"]["code"] == -32601); + CHECK(c.GetJsonResponse().isMember("result") == false); + + // call notification as procedure + c.SetRequest("{\"jsonrpc\":\"2.0\", \"id\": 1, \"method\": " + "\"initCounter\",\"params\":{\"value\":3}}"); + CHECK(c.GetJsonResponse()["error"]["code"] == -32605); + CHECK(c.GetJsonResponse().isMember("result") == false); + + // call procedure as notification + c.SetRequest("{\"jsonrpc\":\"2.0\", \"method\": " + "\"sayHello\",\"params\":{\"name\":\"Peter\"}}"); + CHECK(c.GetJsonResponse()["error"]["code"] == -32604); + CHECK(c.GetJsonResponse().isMember("result") == false); + + c.SetRequest("{\"jsonrpc\":\"2.0\", \"method\": " + "\"sub\",\"params\":{\"value1\":3, \"value\": 4}}"); + CHECK(c.GetJsonResponse()["error"]["code"] == -32604); + CHECK(c.GetJsonResponse().isMember("result") == false); + + c.SetRequest("{\"jsonrpc\":\"2.0\", \"method\": \"add\",\"params\":[3,4]}"); + CHECK(c.GetJsonResponse()["error"]["code"] == -32604); + CHECK(c.GetJsonResponse().isMember("result") == false); + + // userspace exception + c.SetRequest( + "{\"jsonrpc\":\"2.0\", \"id\": 1, \"method\": \"exceptionMethod\"}"); + CHECK(c.GetJsonResponse()["error"]["code"] == -32099); + CHECK(c.GetJsonResponse()["error"]["message"] == "User exception"); + CHECK(c.GetJsonResponse()["error"]["data"][0] == 33); + CHECK(c.GetJsonResponse().isMember("result") == false); } -TEST_CASE_METHOD(F, "test_server_v2_params_error", TEST_MODULE) -{ - //invalid param type - c.SetRequest("{\"jsonrpc\":\"2.0\", \"id\": 1, \"method\": \"sayHello\",\"params\":{\"name\":23}}"); - CHECK(c.GetJsonResponse()["error"]["code"] == -32602); - CHECK(c.GetJsonResponse().isMember("result") == false); - - //invalid param name - c.SetRequest("{\"jsonrpc\":\"2.0\", \"id\": 1, \"method\": \"sayHello\",\"params\":{\"name2\":\"Peter\"}}"); - CHECK(c.GetJsonResponse()["error"]["code"] == -32602); - CHECK(c.GetJsonResponse().isMember("result") == false); - - //invalid parameter passing mode (array instead of object) - c.SetRequest("{\"jsonrpc\":\"2.0\", \"id\": 1, \"method\": \"sayHello\",\"params\":[\"Peter\"]}"); - CHECK(c.GetJsonResponse()["error"]["code"] == -32602); - CHECK(c.GetJsonResponse().isMember("result") == false); - - //missing parameter field - c.SetRequest("{\"jsonrpc\":\"2.0\", \"id\": 1, \"method\": \"sayHello\"}"); - CHECK(c.GetJsonResponse()["error"]["code"] == -32602); - CHECK(c.GetJsonResponse().isMember("result") == false); +TEST_CASE_METHOD(F, "test_server_v2_params_error", TEST_MODULE) { + // invalid param type + c.SetRequest("{\"jsonrpc\":\"2.0\", \"id\": 1, \"method\": " + "\"sayHello\",\"params\":{\"name\":23}}"); + CHECK(c.GetJsonResponse()["error"]["code"] == -32602); + CHECK(c.GetJsonResponse().isMember("result") == false); + + // invalid param name + c.SetRequest("{\"jsonrpc\":\"2.0\", \"id\": 1, \"method\": " + "\"sayHello\",\"params\":{\"name2\":\"Peter\"}}"); + CHECK(c.GetJsonResponse()["error"]["code"] == -32602); + CHECK(c.GetJsonResponse().isMember("result") == false); + + // invalid parameter passing mode (array instead of object) + c.SetRequest("{\"jsonrpc\":\"2.0\", \"id\": 1, \"method\": " + "\"sayHello\",\"params\":[\"Peter\"]}"); + CHECK(c.GetJsonResponse()["error"]["code"] == -32602); + CHECK(c.GetJsonResponse().isMember("result") == false); + + // missing parameter field + c.SetRequest("{\"jsonrpc\":\"2.0\", \"id\": 1, \"method\": \"sayHello\"}"); + CHECK(c.GetJsonResponse()["error"]["code"] == -32602); + CHECK(c.GetJsonResponse().isMember("result") == false); } -TEST_CASE_METHOD(F, "test_server_v2_batchcall_success", TEST_MODULE) -{ - //Simple Batchcall with only methods - c.SetRequest("[{\"jsonrpc\":\"2.0\", \"id\": 1, \"method\": \"sayHello\",\"params\":{\"name\":\"Peter\"}},{\"jsonrpc\":\"2.0\", \"id\": 2, \"method\": \"add\",\"params\":{\"value1\":23,\"value2\": 33}}]"); - - CHECK(c.GetJsonResponse().size() == 2); - CHECK(c.GetJsonResponse()[0]["result"].asString() == "Hello: Peter!"); - CHECK(c.GetJsonResponse()[0]["id"].asInt() == 1); - CHECK(c.GetJsonResponse()[1]["result"].asInt() == 56); - CHECK(c.GetJsonResponse()[1]["id"].asInt() == 2); - - //Batchcall containing methods and notifications - c.SetRequest("[{\"jsonrpc\":\"2.0\", \"id\": 1, \"method\": \"sayHello\",\"params\":{\"name\":\"Peter\"}},{\"jsonrpc\":\"2.0\", \"method\": \"initCounter\",\"params\":{\"value\":23}}]"); - - CHECK(c.GetJsonResponse().size() == 1); - CHECK(c.GetJsonResponse()[0]["result"].asString() == "Hello: Peter!"); - CHECK(c.GetJsonResponse()[0]["id"].asInt() == 1); - CHECK(server.getCnt() == 23); - - //Batchcall containing only notifications - c.SetRequest("[{\"jsonrpc\":\"2.0\", \"method\": \"initCounter\",\"params\":{\"value\":23}},{\"jsonrpc\":\"2.0\", \"method\": \"initCounter\",\"params\":{\"value\":23}}]"); - - CHECK(c.GetResponse() == ""); +TEST_CASE_METHOD(F, "test_server_v2_batchcall_success", TEST_MODULE) { + // Simple Batchcall with only methods + c.SetRequest("[{\"jsonrpc\":\"2.0\", \"id\": 1, \"method\": " + "\"sayHello\",\"params\":{\"name\":\"Peter\"}},{\"jsonrpc\":\"2." + "0\", \"id\": 2, \"method\": " + "\"add\",\"params\":{\"value1\":23,\"value2\": 33}}]"); + + CHECK(c.GetJsonResponse().size() == 2); + CHECK(c.GetJsonResponse()[0]["result"].asString() == "Hello: Peter!"); + CHECK(c.GetJsonResponse()[0]["id"].asInt() == 1); + CHECK(c.GetJsonResponse()[1]["result"].asInt() == 56); + CHECK(c.GetJsonResponse()[1]["id"].asInt() == 2); + + // Batchcall containing methods and notifications + c.SetRequest("[{\"jsonrpc\":\"2.0\", \"id\": 1, \"method\": " + "\"sayHello\",\"params\":{\"name\":\"Peter\"}},{\"jsonrpc\":\"2." + "0\", \"method\": \"initCounter\",\"params\":{\"value\":23}}]"); + + CHECK(c.GetJsonResponse().size() == 1); + CHECK(c.GetJsonResponse()[0]["result"].asString() == "Hello: Peter!"); + CHECK(c.GetJsonResponse()[0]["id"].asInt() == 1); + CHECK(server.getCnt() == 23); + + // Batchcall containing only notifications + c.SetRequest("[{\"jsonrpc\":\"2.0\", \"method\": " + "\"initCounter\",\"params\":{\"value\":23}},{\"jsonrpc\":\"2." + "0\", \"method\": \"initCounter\",\"params\":{\"value\":23}}]"); + + CHECK(c.GetResponse() == ""); } -TEST_CASE_METHOD(F, "test_server_v2_batchcall_error", TEST_MODULE) -{ - //success and error responses - c.SetRequest("[{\"jsonrpc\":\"2.0\", \"id\": 1, \"method\": \"sayHello\",\"params\":{\"name\":\"Peter\"}},{},{\"jsonrpc\":\"2.0\", \"id\": 3, \"method\": \"sayHello\",\"params\":{\"name\":\"Peter3\"}}]"); - CHECK(c.GetJsonResponse().size() == 3); - CHECK(c.GetJsonResponse()[0]["result"].asString() == "Hello: Peter!"); - CHECK(c.GetJsonResponse()[0]["id"].asInt() == 1); - CHECK(c.GetJsonResponse()[1]["error"]["code"].asInt() == -32600); - CHECK(c.GetJsonResponse()[1]["id"].isNull() == true); - CHECK(c.GetJsonResponse()[2]["result"].asString() == "Hello: Peter3!"); - CHECK(c.GetJsonResponse()[2]["id"].asInt() == 3); - - //only invalid requests - c.SetRequest("[1,2,3]"); - CHECK(c.GetJsonResponse().size() == 3); - CHECK(c.GetJsonResponse()[0]["error"]["code"].asInt() == -32600); - CHECK(c.GetJsonResponse()[1]["error"]["code"].asInt() == -32600); - CHECK(c.GetJsonResponse()[2]["error"]["code"].asInt() == -32600); +TEST_CASE_METHOD(F, "test_server_v2_batchcall_error", TEST_MODULE) { + // success and error responses + c.SetRequest("[{\"jsonrpc\":\"2.0\", \"id\": 1, \"method\": " + "\"sayHello\",\"params\":{\"name\":\"Peter\"}},{},{\"jsonrpc\":" + "\"2.0\", \"id\": 3, \"method\": " + "\"sayHello\",\"params\":{\"name\":\"Peter3\"}}]"); + CHECK(c.GetJsonResponse().size() == 3); + CHECK(c.GetJsonResponse()[0]["result"].asString() == "Hello: Peter!"); + CHECK(c.GetJsonResponse()[0]["id"].asInt() == 1); + CHECK(c.GetJsonResponse()[1]["error"]["code"].asInt() == -32600); + CHECK(c.GetJsonResponse()[1]["id"].isNull() == true); + CHECK(c.GetJsonResponse()[2]["result"].asString() == "Hello: Peter3!"); + CHECK(c.GetJsonResponse()[2]["id"].asInt() == 3); + + // only invalid requests + c.SetRequest("[1,2,3]"); + CHECK(c.GetJsonResponse().size() == 3); + CHECK(c.GetJsonResponse()[0]["error"]["code"].asInt() == -32600); + CHECK(c.GetJsonResponse()[1]["error"]["code"].asInt() == -32600); + CHECK(c.GetJsonResponse()[2]["error"]["code"].asInt() == -32600); } -TEST_CASE_METHOD(F1, "test_server_v1_method_success", TEST_MODULE) -{ - c.SetRequest("{\"id\": 1, \"method\": \"sub\",\"params\":[5,7]}}"); - CHECK(c.GetJsonResponse()["result"].asInt() == -2); - CHECK(c.GetJsonResponse()["id"].asInt() == 1); - CHECK(c.GetJsonResponse().isMember("jsonrpc") == false); - CHECK(c.GetJsonResponse().isMember("error") == true); - CHECK(c.GetJsonRequest()["error"] == Json::nullValue); - - c.SetRequest("{\"id\": \"1\", \"method\": \"sub\",\"params\":[5,7]}"); - CHECK(c.GetJsonResponse()["result"].asInt() == -2); - CHECK(c.GetJsonResponse()["id"].asString() == "1"); - CHECK(c.GetJsonResponse().isMember("jsonrpc") == false); - CHECK(c.GetJsonResponse().isMember("error") == true); - CHECK(c.GetJsonRequest()["error"] == Json::nullValue); - - c.SetRequest("{\"jsonrpc\":\"2.0\", \"id\": \"1\", \"method\": \"sub\",\"params\":[5,7]}"); - CHECK(c.GetJsonResponse()["result"].asInt() == -2); - CHECK(c.GetJsonResponse()["id"].asString() == "1"); - CHECK(c.GetJsonResponse().isMember("jsonrpc") == false); - CHECK(c.GetJsonResponse().isMember("error") == true); - CHECK(c.GetJsonRequest()["error"] == Json::nullValue); +TEST_CASE_METHOD(F1, "test_server_v1_method_success", TEST_MODULE) { + c.SetRequest("{\"id\": 1, \"method\": \"sub\",\"params\":[5,7]}}"); + CHECK(c.GetJsonResponse()["result"].asInt() == -2); + CHECK(c.GetJsonResponse()["id"].asInt() == 1); + CHECK(c.GetJsonResponse().isMember("jsonrpc") == false); + CHECK(c.GetJsonResponse().isMember("error") == true); + CHECK(c.GetJsonRequest()["error"] == Json::nullValue); + + c.SetRequest("{\"id\": \"1\", \"method\": \"sub\",\"params\":[5,7]}"); + CHECK(c.GetJsonResponse()["result"].asInt() == -2); + CHECK(c.GetJsonResponse()["id"].asString() == "1"); + CHECK(c.GetJsonResponse().isMember("jsonrpc") == false); + CHECK(c.GetJsonResponse().isMember("error") == true); + CHECK(c.GetJsonRequest()["error"] == Json::nullValue); + + c.SetRequest("{\"jsonrpc\":\"2.0\", \"id\": \"1\", \"method\": " + "\"sub\",\"params\":[5,7]}"); + CHECK(c.GetJsonResponse()["result"].asInt() == -2); + CHECK(c.GetJsonResponse()["id"].asString() == "1"); + CHECK(c.GetJsonResponse().isMember("jsonrpc") == false); + CHECK(c.GetJsonResponse().isMember("error") == true); + CHECK(c.GetJsonRequest()["error"] == Json::nullValue); } -TEST_CASE_METHOD(F1, "test_server_v1_notification_success", TEST_MODULE) -{ - c.SetRequest("{\"id\": null, \"method\": \"initZero\", \"params\": null}"); - CHECK(server.getCnt() == 0); - CHECK(c.GetResponse() == ""); +TEST_CASE_METHOD(F1, "test_server_v1_notification_success", TEST_MODULE) { + c.SetRequest("{\"id\": null, \"method\": \"initZero\", \"params\": null}"); + CHECK(server.getCnt() == 0); + CHECK(c.GetResponse() == ""); } -TEST_CASE_METHOD(F1, "test_server_v1_method_invalid_request", TEST_MODULE) -{ - c.SetRequest("{\"method\": \"sub\", \"params\": []}"); - CHECK(c.GetJsonResponse()["error"]["code"] == -32600); - CHECK(c.GetJsonResponse()["result"] == Json::nullValue); - - c.SetRequest("{\"id\": 1, \"method\": \"sub\", \"params\": {\"foo\": true}}"); - CHECK(c.GetJsonResponse()["error"]["code"] == -32600); - CHECK(c.GetJsonResponse()["result"] == Json::nullValue); - - c.SetRequest("{\"id\": 1, \"method\": \"sub\", \"params\": true}"); - CHECK(c.GetJsonResponse()["error"]["code"] == -32600); - CHECK(c.GetJsonResponse()["result"] == Json::nullValue); +TEST_CASE_METHOD(F1, "test_server_v1_method_invalid_request", TEST_MODULE) { + c.SetRequest("{\"method\": \"sub\", \"params\": []}"); + CHECK(c.GetJsonResponse()["error"]["code"] == -32600); + CHECK(c.GetJsonResponse()["result"] == Json::nullValue); - c.SetRequest("{\"id\": 1, \"params\": []}"); - CHECK(c.GetJsonResponse()["error"]["code"] == -32600); - CHECK(c.GetJsonResponse()["result"] == Json::nullValue); + c.SetRequest("{\"id\": 1, \"method\": \"sub\", \"params\": {\"foo\": true}}"); + CHECK(c.GetJsonResponse()["error"]["code"] == -32600); + CHECK(c.GetJsonResponse()["result"] == Json::nullValue); - c.SetRequest("{}"); - CHECK(c.GetJsonResponse()["error"]["code"] == -32600); - CHECK(c.GetJsonResponse()["result"] == Json::nullValue); + c.SetRequest("{\"id\": 1, \"method\": \"sub\", \"params\": true}"); + CHECK(c.GetJsonResponse()["error"]["code"] == -32600); + CHECK(c.GetJsonResponse()["result"] == Json::nullValue); - c.SetRequest("[]"); - CHECK(c.GetJsonResponse()["error"]["code"] == -32600); - CHECK(c.GetJsonResponse()["result"] == Json::nullValue); + c.SetRequest("{\"id\": 1, \"params\": []}"); + CHECK(c.GetJsonResponse()["error"]["code"] == -32600); + CHECK(c.GetJsonResponse()["result"] == Json::nullValue); - c.SetRequest("23"); - CHECK(c.GetJsonResponse()["error"]["code"] == -32600); - CHECK(c.GetJsonResponse()["result"] == Json::nullValue); -} + c.SetRequest("{}"); + CHECK(c.GetJsonResponse()["error"]["code"] == -32600); + CHECK(c.GetJsonResponse()["result"] == Json::nullValue); -TEST_CASE_METHOD(F1, "test_server_v1_method_error", TEST_MODULE) -{ - c.SetRequest("{\"id\": 1, \"method\": \"sub\", \"params\": [33]}"); - CHECK(c.GetJsonResponse()["error"]["code"] == -32602); - CHECK(c.GetJsonResponse()["result"] ==Json::nullValue); - - c.SetRequest("{\"id\": 1, \"method\": \"sub\", \"params\": [33, \"foo\"]}"); - CHECK(c.GetJsonResponse()["error"]["code"] == -32602); - CHECK(c.GetJsonResponse()["result"] == Json::nullValue); - - c.SetRequest("{\"id\": 1, \"method\": \"sub\"}"); - CHECK(c.GetJsonResponse()["error"]["code"] == -32600); - CHECK(c.GetJsonResponse()["result"] == Json::nullValue); - - //userspace exception - c.SetRequest("{\"id\": 1, \"method\": \"exceptionMethod\",\"params\":null}"); - CHECK(c.GetJsonResponse()["error"]["code"] == -32099); - CHECK(c.GetJsonResponse()["error"]["message"] == "User exception"); - CHECK(c.GetJsonResponse()["result"] == Json::nullValue); -} + c.SetRequest("[]"); + CHECK(c.GetJsonResponse()["error"]["code"] == -32600); + CHECK(c.GetJsonResponse()["result"] == Json::nullValue); -TEST_CASE("test_server_hybrid", TEST_MODULE) -{ - MockServerConnector c; - TestServer server(c, JSONRPC_SERVER_V1V2); - - c.SetRequest("{\"id\": 1, \"method\": \"sub\",\"params\":[5,7]}}"); - CHECK(c.GetJsonResponse()["result"].asInt() == -2); - CHECK(c.GetJsonResponse()["id"].asInt() == 1); - CHECK(c.GetJsonResponse().isMember("jsonrpc") == false); - CHECK(c.GetJsonResponse().isMember("error") == true); - CHECK(c.GetJsonRequest()["error"] == Json::nullValue); - - c.SetRequest("{\"id\": null, \"method\": \"initZero\", \"params\": null}"); - CHECK(server.getCnt() == 0); - CHECK(c.GetResponse() == ""); - - c.SetRequest("{\"jsonrpc\":\"2.0\", \"id\": 1, \"method\": \"sayHello\",\"params\":{\"name\":\"Peter\"}}"); - CHECK(c.GetJsonResponse()["result"].asString() == "Hello: Peter!"); - CHECK(c.GetJsonResponse()["id"].asInt() == 1); - CHECK(c.GetJsonResponse()["jsonrpc"].asString() == "2.0"); - CHECK(c.GetJsonResponse().isMember("error") == false); - - c.SetRequest("{\"jsonrpc\":\"2.0\", \"method\": \"initCounter\",\"params\":{\"value\": 33}}"); - CHECK(server.getCnt() == 33); - CHECK(c.GetResponse() == ""); - - c.SetRequest("{\"jsonrpc\":\"2.0\", \"params\":{\"value\": 33}}"); - CHECK(c.GetJsonResponse()["error"]["code"] == -32600); - CHECK(c.GetJsonResponse().isMember("result") == false); - - c.SetRequest("{\"jsonrpc\":\"2.0\", \"params\":{\"value\": 33"); - CHECK(c.GetJsonResponse()["error"]["code"] == -32700); - CHECK(c.GetJsonResponse().isMember("result") == false); + c.SetRequest("23"); + CHECK(c.GetJsonResponse()["error"]["code"] == -32600); + CHECK(c.GetJsonResponse()["result"] == Json::nullValue); } -TEST_CASE("test_server_abstractserver", TEST_MODULE) -{ - MockServerConnector c; - TestServer server(c, JSONRPC_SERVER_V1V2); +TEST_CASE_METHOD(F1, "test_server_v1_method_error", TEST_MODULE) { + c.SetRequest("{\"id\": 1, \"method\": \"sub\", \"params\": [33]}"); + CHECK(c.GetJsonResponse()["error"]["code"] == -32602); + CHECK(c.GetJsonResponse()["result"] == Json::nullValue); - CHECK(server.bindAndAddNotification(Procedure("testMethod", PARAMS_BY_NAME, JSON_STRING, "name", JSON_STRING, NULL), &TestServer::initCounter) == false); - CHECK(server.bindAndAddMethod(Procedure("initCounter", PARAMS_BY_NAME, "value", JSON_INTEGER, NULL), &TestServer::sayHello) == false); + c.SetRequest("{\"id\": 1, \"method\": \"sub\", \"params\": [33, \"foo\"]}"); + CHECK(c.GetJsonResponse()["error"]["code"] == -32602); + CHECK(c.GetJsonResponse()["result"] == Json::nullValue); - CHECK(server.bindAndAddMethod(Procedure("testMethod", PARAMS_BY_NAME, JSON_STRING, "name", JSON_STRING, NULL), &TestServer::sayHello) == true); - CHECK(server.bindAndAddMethod(Procedure("testMethod", PARAMS_BY_NAME, JSON_STRING, "name", JSON_STRING, NULL), &TestServer::sayHello) == false); + c.SetRequest("{\"id\": 1, \"method\": \"sub\"}"); + CHECK(c.GetJsonResponse()["error"]["code"] == -32600); + CHECK(c.GetJsonResponse()["result"] == Json::nullValue); - CHECK(server.bindAndAddNotification(Procedure("testNotification", PARAMS_BY_NAME, "value", JSON_INTEGER, NULL), &TestServer::initCounter) == true); - CHECK(server.bindAndAddNotification(Procedure("testNotification", PARAMS_BY_NAME, "value", JSON_INTEGER, NULL), &TestServer::initCounter) == false); + // userspace exception + c.SetRequest("{\"id\": 1, \"method\": \"exceptionMethod\",\"params\":null}"); + CHECK(c.GetJsonResponse()["error"]["code"] == -32099); + CHECK(c.GetJsonResponse()["error"]["message"] == "User exception"); + CHECK(c.GetJsonResponse()["result"] == Json::nullValue); +} - CHECK(server.StartListening() == true); - CHECK(server.StopListening() == true); +TEST_CASE("test_server_hybrid", TEST_MODULE) { + MockServerConnector c; + TestServer server(c, JSONRPC_SERVER_V1V2); + + c.SetRequest("{\"id\": 1, \"method\": \"sub\",\"params\":[5,7]}}"); + CHECK(c.GetJsonResponse()["result"].asInt() == -2); + CHECK(c.GetJsonResponse()["id"].asInt() == 1); + CHECK(c.GetJsonResponse().isMember("jsonrpc") == false); + CHECK(c.GetJsonResponse().isMember("error") == true); + CHECK(c.GetJsonRequest()["error"] == Json::nullValue); + + c.SetRequest("{\"id\": null, \"method\": \"initZero\", \"params\": null}"); + CHECK(server.getCnt() == 0); + CHECK(c.GetResponse() == ""); + + c.SetRequest("{\"jsonrpc\":\"2.0\", \"id\": 1, \"method\": " + "\"sayHello\",\"params\":{\"name\":\"Peter\"}}"); + CHECK(c.GetJsonResponse()["result"].asString() == "Hello: Peter!"); + CHECK(c.GetJsonResponse()["id"].asInt() == 1); + CHECK(c.GetJsonResponse()["jsonrpc"].asString() == "2.0"); + CHECK(c.GetJsonResponse().isMember("error") == false); + + c.SetRequest("{\"jsonrpc\":\"2.0\", \"method\": " + "\"initCounter\",\"params\":{\"value\": 33}}"); + CHECK(server.getCnt() == 33); + CHECK(c.GetResponse() == ""); + + c.SetRequest("{\"jsonrpc\":\"2.0\", \"params\":{\"value\": 33}}"); + CHECK(c.GetJsonResponse()["error"]["code"] == -32600); + CHECK(c.GetJsonResponse().isMember("result") == false); + + c.SetRequest("{\"jsonrpc\":\"2.0\", \"params\":{\"value\": 33"); + CHECK(c.GetJsonResponse()["error"]["code"] == -32700); + CHECK(c.GetJsonResponse().isMember("result") == false); +} - MockServerConnector c2; - CHECK(c2.SetRequest("abcd") == false); +TEST_CASE("test_server_abstractserver", TEST_MODULE) { + MockServerConnector c; + TestServer server(c, JSONRPC_SERVER_V1V2); + + CHECK(server.bindAndAddNotification(Procedure("testMethod", PARAMS_BY_NAME, + JSON_STRING, "name", + JSON_STRING, NULL), + &TestServer::initCounter) == false); + CHECK(server.bindAndAddMethod(Procedure("initCounter", PARAMS_BY_NAME, + "value", JSON_INTEGER, NULL), + &TestServer::sayHello) == false); + + CHECK( + server.bindAndAddMethod(Procedure("testMethod", PARAMS_BY_NAME, + JSON_STRING, "name", JSON_STRING, NULL), + &TestServer::sayHello) == true); + CHECK( + server.bindAndAddMethod(Procedure("testMethod", PARAMS_BY_NAME, + JSON_STRING, "name", JSON_STRING, NULL), + &TestServer::sayHello) == false); + + CHECK(server.bindAndAddNotification(Procedure("testNotification", + PARAMS_BY_NAME, "value", + JSON_INTEGER, NULL), + &TestServer::initCounter) == true); + CHECK(server.bindAndAddNotification(Procedure("testNotification", + PARAMS_BY_NAME, "value", + JSON_INTEGER, NULL), + &TestServer::initCounter) == false); + + CHECK(server.StartListening() == true); + CHECK(server.StopListening() == true); } diff --git a/src/test/test_stubgenerator.cpp b/src/test/test_stubgenerator.cpp index 968e8a14..fb8c3fca 100644 --- a/src/test/test_stubgenerator.cpp +++ b/src/test/test_stubgenerator.cpp @@ -11,10 +11,11 @@ #include #include -#include #include #include +#include #include +#include #include #include @@ -22,173 +23,236 @@ using namespace jsonrpc; using namespace std; -namespace teststubgen -{ - struct F { - FILE* stdout; - FILE* stderr; - vector stubgens; - vector procedures; - F() - { - stdout = fopen("stdout.txt", "w"); - stderr = fopen("stderr.txt", "w"); - } - - ~F() - { - fclose(stdout); - fclose(stderr); - } - }; -} +namespace teststubgen { +struct F { + FILE *stdout; + FILE *stderr; + vector stubgens; + vector procedures; + F() { + stdout = fopen("stdout.txt", "w"); + stderr = fopen("stderr.txt", "w"); + } + + ~F() { + fclose(stdout); + fclose(stderr); + } +}; +} // namespace teststubgen using namespace teststubgen; #define TEST_MODULE "[stubgenerator]" -TEST_CASE("test stubgen cppclient", TEST_MODULE) -{ - stringstream stream; - vector procedures = SpecificationParser::GetProceduresFromFile("testspec6.json"); - CPPClientStubGenerator stubgen("ns1::ns2::TestStubClient", procedures, stream); - stubgen.generateStub(); - string result = stream.str(); - - CHECK(result.find("#ifndef JSONRPC_CPP_STUB_NS1_NS2_TESTSTUBCLIENT_H_") != string::npos); - CHECK(result.find("#define JSONRPC_CPP_STUB_NS1_NS2_TESTSTUBCLIENT_H_") != string::npos); - CHECK(result.find("namespace ns1") != string::npos); - CHECK(result.find("namespace ns2") != string::npos); - CHECK(result.find("class TestStubClient : public jsonrpc::Client") != string::npos); - CHECK(result.find("std::string test_method(const std::string& name) throw (jsonrpc::JsonRpcException)") != string::npos); - CHECK(result.find("void test_notification(const std::string& name) throw (jsonrpc::JsonRpcException)") != string::npos); - CHECK(result.find("double test_method2(const Json::Value& object, const Json::Value& values) throw (jsonrpc::JsonRpcException)") != string::npos); - CHECK(result.find("void test_notification2(const Json::Value& object, const Json::Value& values) throw (jsonrpc::JsonRpcException)") != string::npos); - CHECK(result.find("#endif //JSONRPC_CPP_STUB_NS1_NS2_TESTSTUBCLIENT_H_") != string::npos); - - CHECK(CPPHelper::class2Filename("ns1::ns2::TestClass") == "testclass.h"); +TEST_CASE("test stubgen cppclient", TEST_MODULE) { + stringstream stream; + vector procedures = + SpecificationParser::GetProceduresFromFile("testspec6.json"); + CPPClientStubGenerator stubgen("ns1::ns2::TestStubClient", procedures, + stream); + stubgen.generateStub(); + string result = stream.str(); + + CHECK(result.find("#ifndef JSONRPC_CPP_STUB_NS1_NS2_TESTSTUBCLIENT_H_") != + string::npos); + CHECK(result.find("#define JSONRPC_CPP_STUB_NS1_NS2_TESTSTUBCLIENT_H_") != + string::npos); + CHECK(result.find("namespace ns1") != string::npos); + CHECK(result.find("namespace ns2") != string::npos); + CHECK(result.find("class TestStubClient : public jsonrpc::Client") != + string::npos); + CHECK(result.find("std::string test_method(const std::string& name) ") != + string::npos); + CHECK(result.find("void test_notification(const std::string& name) ") != + string::npos); + CHECK(result.find("double test_method2(const Json::Value& object, const " + "Json::Value& values) ") != string::npos); + CHECK(result.find("void test_notification2(const Json::Value& object, const " + "Json::Value& values) ") != string::npos); + CHECK(result.find("#endif //JSONRPC_CPP_STUB_NS1_NS2_TESTSTUBCLIENT_H_") != + string::npos); + + CHECK(CPPHelper::class2Filename("ns1::ns2::TestClass") == "testclass.h"); } -TEST_CASE("test stubgen cppserver", TEST_MODULE) -{ - stringstream stream; - vector procedures = SpecificationParser::GetProceduresFromFile("testspec6.json"); - CPPServerStubGenerator stubgen("ns1::ns2::TestStubServer", procedures, stream); - stubgen.generateStub(); - string result = stream.str(); - - CHECK(result.find("#ifndef JSONRPC_CPP_STUB_NS1_NS2_TESTSTUBSERVER_H_") != string::npos); - CHECK(result.find("#define JSONRPC_CPP_STUB_NS1_NS2_TESTSTUBSERVER_H_") != string::npos); - CHECK(result.find("namespace ns1") != string::npos); - CHECK(result.find("namespace ns2") != string::npos); - CHECK(result.find("class TestStubServer : public jsonrpc::AbstractServer") != string::npos); - CHECK(result.find("virtual std::string test_method(const std::string& name) = 0;") != string::npos); - CHECK(result.find("virtual void test_notification(const std::string& name) = 0;") != string::npos); - CHECK(result.find("virtual double test_method2(const Json::Value& object, const Json::Value& values) = 0;") != string::npos); - CHECK(result.find("virtual void test_notification2(const Json::Value& object, const Json::Value& values) = 0;") != string::npos); - CHECK(result.find("this->bindAndAddMethod(jsonrpc::Procedure(\"test.method\", jsonrpc::PARAMS_BY_NAME, jsonrpc::JSON_STRING, \"name\",jsonrpc::JSON_STRING, NULL), &ns1::ns2::TestStubServer::test_methodI);") != string::npos); - CHECK(result.find("#endif //JSONRPC_CPP_STUB_NS1_NS2_TESTSTUBSERVER_H_") != string::npos); +TEST_CASE("test stubgen cppserver", TEST_MODULE) { + stringstream stream; + vector procedures = + SpecificationParser::GetProceduresFromFile("testspec6.json"); + CPPServerStubGenerator stubgen("ns1::ns2::TestStubServer", procedures, + stream); + stubgen.generateStub(); + string result = stream.str(); + + CHECK(result.find("#ifndef JSONRPC_CPP_STUB_NS1_NS2_TESTSTUBSERVER_H_") != + string::npos); + CHECK(result.find("#define JSONRPC_CPP_STUB_NS1_NS2_TESTSTUBSERVER_H_") != + string::npos); + CHECK(result.find("namespace ns1") != string::npos); + CHECK(result.find("namespace ns2") != string::npos); + CHECK(result.find("class TestStubServer : public " + "jsonrpc::AbstractServer") != string::npos); + CHECK(result.find( + "virtual std::string test_method(const std::string& name) = 0;") != + string::npos); + CHECK(result.find( + "virtual void test_notification(const std::string& name) = 0;") != + string::npos); + CHECK(result.find("virtual double test_method2(const Json::Value& object, " + "const Json::Value& values) = 0;") != string::npos); + CHECK(result.find("virtual void test_notification2(const Json::Value& " + "object, const Json::Value& values) = 0;") != string::npos); + CHECK(result.find("this->bindAndAddMethod(jsonrpc::Procedure(\"test.method\"," + " jsonrpc::PARAMS_BY_NAME, jsonrpc::JSON_STRING, " + "\"name\",jsonrpc::JSON_STRING, NULL), " + "&ns1::ns2::TestStubServer::test_methodI);") != + string::npos); + CHECK(result.find("#endif //JSONRPC_CPP_STUB_NS1_NS2_TESTSTUBSERVER_H_") != + string::npos); } -TEST_CASE("test_stubgen_jsclient", TEST_MODULE) -{ - stringstream stream; - vector procedures = SpecificationParser::GetProceduresFromFile("testspec6.json"); - JSClientStubGenerator stubgen("TestStubClient", procedures, stream); - stubgen.generateStub(); - string result = stream.str(); - - CHECK(result.find("function TestStubClient(url) {") != string::npos); - CHECK(result.find("TestStubClient.prototype.test_method = function(name, callbackSuccess, callbackError)") != string::npos); - CHECK(result.find("TestStubClient.prototype.test_notification = function(name, callbackSuccess, callbackError)") != string::npos); - CHECK(result.find("TestStubClient.prototype.test_method2 = function(object, values, callbackSuccess, callbackError)") != string::npos); - CHECK(result.find("TestStubClient.prototype.test_notification2 = function(object, values, callbackSuccess, callbackError)") != string::npos); - - CHECK(JSClientStubGenerator::class2Filename("TestClass") == "testclass.js"); +TEST_CASE("test_stubgen_jsclient", TEST_MODULE) { + stringstream stream; + vector procedures = + SpecificationParser::GetProceduresFromFile("testspec6.json"); + JSClientStubGenerator stubgen("TestStubClient", procedures, stream); + stubgen.generateStub(); + string result = stream.str(); + + CHECK(result.find("function TestStubClient(url) {") != string::npos); + CHECK(result.find("TestStubClient.prototype.test_method = function(name, " + "callbackSuccess, callbackError)") != string::npos); + CHECK(result.find("TestStubClient.prototype.test_notification = " + "function(name, callbackSuccess, callbackError)") != + string::npos); + CHECK(result.find("TestStubClient.prototype.test_method2 = function(object, " + "values, callbackSuccess, callbackError)") != string::npos); + CHECK(result.find("TestStubClient.prototype.test_notification2 = " + "function(object, values, callbackSuccess, " + "callbackError)") != string::npos); + + CHECK(JSClientStubGenerator::class2Filename("TestClass") == "testclass.js"); } -TEST_CASE("test_stubgen_indentation", TEST_MODULE) -{ - stringstream stream; - CodeGenerator cg(stream); - cg.setIndentSymbol(" "); - cg.increaseIndentation(); - cg.write("abc"); - CHECK(stream.str() == " abc"); - - stringstream stream2; - CodeGenerator cg2(stream2); - cg2.setIndentSymbol("\t"); - cg2.increaseIndentation(); - cg2.write("abc"); - CHECK(stream2.str() == "\tabc"); +TEST_CASE("test_stubgen_pyclient", TEST_MODULE) { + stringstream stream; + vector procedures = + SpecificationParser::GetProceduresFromFile("testspec6.json"); + PythonClientStubGenerator stubgen("TestStubClient", procedures, stream); + stubgen.generateStub(); + string result = stream.str(); + + CHECK(result.find("from jsonrpc_pyclient import client") != string::npos); + CHECK(result.find("class TestStubClient(client.Client):") != string::npos); + CHECK(result.find("def __init__(self, connector, version='2.0'):") != + string::npos); + CHECK(result.find("def test_method(self, name):") != string::npos); + CHECK(result.find("def test_notification(self, name):") != string::npos); + CHECK(result.find("def test_method2(self, object, values):") != string::npos); + CHECK(result.find("def test_notification2(self, object, values):") != + string::npos); + + CHECK(PythonClientStubGenerator::class2Filename("TestClass") == + "testclass.py"); } -TEST_CASE_METHOD(F, "test_stubgen_factory_help", TEST_MODULE) -{ - const char* argv[2] = {"jsonrpcstub","-h"}; - CHECK(StubGeneratorFactory::createStubGenerators(2, (char**)argv, procedures, stubgens, stdout, stderr) == true); - CHECK(stubgens.empty() == true); - CHECK(procedures.empty() == true); +TEST_CASE("test_stubgen_indentation", TEST_MODULE) { + stringstream stream; + CodeGenerator cg(stream); + cg.setIndentSymbol(" "); + cg.increaseIndentation(); + cg.write("abc"); + CHECK(stream.str() == " abc"); + + stringstream stream2; + CodeGenerator cg2(stream2); + cg2.setIndentSymbol("\t"); + cg2.increaseIndentation(); + cg2.write("abc"); + CHECK(stream2.str() == "\tabc"); } -TEST_CASE_METHOD(F, "test_stubgen_factory_version", TEST_MODULE) -{ - const char* argv[2] = {"jsonrpcstub","--version"}; - - CHECK(StubGeneratorFactory::createStubGenerators(2, (char**)argv, procedures, stubgens, stdout, stderr) == true); - CHECK(stubgens.empty() == true); - CHECK(procedures.empty() == true); +TEST_CASE_METHOD(F, "test_stubgen_factory_help", TEST_MODULE) { + const char *argv[2] = {"jsonrpcstub", "-h"}; + CHECK(StubGeneratorFactory::createStubGenerators( + 2, (char **)argv, procedures, stubgens, stdout, stderr) == true); + CHECK(stubgens.empty() == true); + CHECK(procedures.empty() == true); } -TEST_CASE_METHOD(F, "test_stubgen_factory_error", TEST_MODULE) -{ - const char* argv[2] = {"jsonrpcstub","--cpp-client=TestClient"}; - - CHECK(StubGeneratorFactory::createStubGenerators(2, (char**)argv, procedures, stubgens, stdout, stderr) == false); - CHECK(stubgens.empty() == true); - CHECK(procedures.empty() == true); +TEST_CASE_METHOD(F, "test_stubgen_factory_version", TEST_MODULE) { + const char *argv[2] = {"jsonrpcstub", "--version"}; - vector stubgens2; - vector procedures2; - const char* argv2[2] = {"jsonrpcstub","--cpxp-client=TestClient"}; - - CHECK(StubGeneratorFactory::createStubGenerators(2, (char**)argv2, procedures2, stubgens2, stdout, stderr) == false); - CHECK(stubgens2.empty() == true); - CHECK(procedures2.empty() == true); - - vector stubgens3; - vector procedures3; - const char* argv3[3] = {"jsonrpcstub", "testspec1.json", "--cpp-client=TestClient"}; + CHECK(StubGeneratorFactory::createStubGenerators( + 2, (char **)argv, procedures, stubgens, stdout, stderr) == true); + CHECK(stubgens.empty() == true); + CHECK(procedures.empty() == true); +} - CHECK(StubGeneratorFactory::createStubGenerators(3, (char**)argv3, procedures3, stubgens3, stdout, stderr) == false); - CHECK(stubgens3.empty() == true); - CHECK(procedures3.empty() == true); +TEST_CASE_METHOD(F, "test_stubgen_factory_error", TEST_MODULE) { + const char *argv[2] = {"jsonrpcstub", "--cpp-client=TestClient"}; + + CHECK(StubGeneratorFactory::createStubGenerators( + 2, (char **)argv, procedures, stubgens, stdout, stderr) == false); + CHECK(stubgens.empty() == true); + CHECK(procedures.empty() == true); + + vector stubgens2; + vector procedures2; + const char *argv2[2] = {"jsonrpcstub", "--cpxp-client=TestClient"}; + + CHECK(StubGeneratorFactory::createStubGenerators(2, (char **)argv2, + procedures2, stubgens2, + stdout, stderr) == false); + CHECK(stubgens2.empty() == true); + CHECK(procedures2.empty() == true); + + vector stubgens3; + vector procedures3; + const char *argv3[3] = {"jsonrpcstub", "testspec1.json", + "--cpp-client=TestClient"}; + + CHECK(StubGeneratorFactory::createStubGenerators(3, (char **)argv3, + procedures3, stubgens3, + stdout, stderr) == false); + CHECK(stubgens3.empty() == true); + CHECK(procedures3.empty() == true); } -TEST_CASE_METHOD(F, "test_stubgen_factory_success", TEST_MODULE) -{ - vector stubgens; - vector procedures; - const char* argv[5] = {"jsonrpcstub", "testspec6.json", "--js-client=TestClient", "--cpp-client=TestClient", "--cpp-server=TestServer"}; +TEST_CASE_METHOD(F, "test_stubgen_factory_success", TEST_MODULE) { + vector stubgens; + vector procedures; + const char *argv[5] = {"jsonrpcstub", "testspec6.json", + "--js-client=TestClient", "--cpp-client=TestClient", + "--cpp-server=TestServer"}; - CHECK(StubGeneratorFactory::createStubGenerators(5, (char**)argv, procedures, stubgens, stdout, stderr) == true); - CHECK(stubgens.size() == 3); - CHECK(procedures.size() == 7); + CHECK(StubGeneratorFactory::createStubGenerators( + 5, (char **)argv, procedures, stubgens, stdout, stderr) == true); + CHECK(stubgens.size() == 3); + CHECK(procedures.size() == 7); - StubGeneratorFactory::deleteStubGenerators(stubgens); + StubGeneratorFactory::deleteStubGenerators(stubgens); } -TEST_CASE_METHOD(F, "test_stubgen_factory_fileoverride", TEST_MODULE) -{ - vector stubgens; - vector procedures; - const char* argv[9] = {"jsonrpcstub", "testspec6.json", "--js-client=TestClient", "--cpp-client=TestClient", "--cpp-server=TestServer", "--cpp-client-file=client.h", "--cpp-server-file=server.h", "--js-client-file=client.js", "-v"}; - - CHECK(StubGeneratorFactory::createStubGenerators(9, (char**)argv, procedures, stubgens, stdout, stderr) == true); - CHECK(stubgens.size() == 3); - CHECK(procedures.size() == 7); - StubGeneratorFactory::deleteStubGenerators(stubgens); +TEST_CASE_METHOD(F, "test_stubgen_factory_fileoverride", TEST_MODULE) { + vector stubgens; + vector procedures; + const char *argv[9] = {"jsonrpcstub", + "testspec6.json", + "--js-client=TestClient", + "--cpp-client=TestClient", + "--cpp-server=TestServer", + "--cpp-client-file=client.h", + "--cpp-server-file=server.h", + "--js-client-file=client.js", + "-v"}; + + CHECK(StubGeneratorFactory::createStubGenerators( + 9, (char **)argv, procedures, stubgens, stdout, stderr) == true); + CHECK(stubgens.size() == 3); + CHECK(procedures.size() == 7); + StubGeneratorFactory::deleteStubGenerators(stubgens); } #endif diff --git a/src/test/testhttpserver.cpp b/src/test/testhttpserver.cpp index 5c0d2bda..6e6db4b6 100644 --- a/src/test/testhttpserver.cpp +++ b/src/test/testhttpserver.cpp @@ -11,72 +11,68 @@ using namespace jsonrpc; -TestHttpServer::TestHttpServer(int port) : - port(port) -{ -} +TestHttpServer::TestHttpServer(int port) : port(port) {} -bool TestHttpServer::StartListening() -{ - this->daemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, this->port, NULL, NULL, TestHttpServer::callback, this, MHD_OPTION_END); - return (this->daemon != NULL); +bool TestHttpServer::StartListening() { + this->daemon = + MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, this->port, NULL, NULL, + TestHttpServer::callback, this, MHD_OPTION_END); + return (this->daemon != NULL); } -bool TestHttpServer::StopListening() -{ - MHD_stop_daemon(this->daemon); - return true; +bool TestHttpServer::StopListening() { + MHD_stop_daemon(this->daemon); + return true; } -bool TestHttpServer::SendResponse(const std::string &response, void *addInfo) -{ - (void)response; - (void)addInfo; - return true; +bool TestHttpServer::SendResponse(const std::string &response, void *addInfo) { + (void)response; + (void)addInfo; + return true; } -void TestHttpServer::SetResponse(const std::string &response) -{ - this->response = response; +void TestHttpServer::SetResponse(const std::string &response) { + this->response = response; } -std::string TestHttpServer::GetHeader(const std::string &key) -{ - if (this->headers.find(key) != this->headers.end()) - return this->headers[key]; - return ""; +std::string TestHttpServer::GetHeader(const std::string &key) { + if (this->headers.find(key) != this->headers.end()) + return this->headers[key]; + return ""; } -int TestHttpServer::callback(void *cls, MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **con_cls) -{ - (void)upload_data; - (void)upload_data_size; - (void)url; - (void)method; - (void)version; - TestHttpServer* _this = static_cast(cls); - if (*con_cls == NULL) - { - *con_cls = cls; - _this->headers.clear(); - } - else - { - MHD_get_connection_values(connection, MHD_HEADER_KIND, header_iterator, cls); - struct MHD_Response *result = MHD_create_response_from_buffer(_this->response.size(),(void *) _this->response.c_str(), MHD_RESPMEM_MUST_COPY); - MHD_add_response_header(result, "Content-Type", "application/json"); - MHD_add_response_header(result, "Access-Control-Allow-Origin", "*"); - MHD_queue_response(connection, MHD_HTTP_OK, result); - MHD_destroy_response(result); - } +int TestHttpServer::callback(void *cls, MHD_Connection *connection, + const char *url, const char *method, + const char *version, const char *upload_data, + size_t *upload_data_size, void **con_cls) { + (void)upload_data; + (void)upload_data_size; + (void)url; + (void)method; + (void)version; + TestHttpServer *_this = static_cast(cls); + if (*con_cls == NULL) { + *con_cls = cls; + _this->headers.clear(); + } else { + MHD_get_connection_values(connection, MHD_HEADER_KIND, header_iterator, + cls); + struct MHD_Response *result = MHD_create_response_from_buffer( + _this->response.size(), (void *)_this->response.c_str(), + MHD_RESPMEM_MUST_COPY); + MHD_add_response_header(result, "Content-Type", "application/json"); + MHD_add_response_header(result, "Access-Control-Allow-Origin", "*"); + MHD_queue_response(connection, MHD_HTTP_OK, result); + MHD_destroy_response(result); + } - return MHD_YES; + return MHD_YES; } -int TestHttpServer::header_iterator(void *cls, MHD_ValueKind kind, const char *key, const char *value) -{ - (void)kind; - TestHttpServer* _this = static_cast(cls); - _this->headers[key] = value; - return MHD_YES; +int TestHttpServer::header_iterator(void *cls, MHD_ValueKind kind, + const char *key, const char *value) { + (void)kind; + TestHttpServer *_this = static_cast(cls); + _this->headers[key] = value; + return MHD_YES; } diff --git a/src/test/testredisserver.cpp b/src/test/testredisserver.cpp new file mode 100644 index 00000000..f70398d8 --- /dev/null +++ b/src/test/testredisserver.cpp @@ -0,0 +1,113 @@ +/************************************************************************* + * libjson-rpc-cpp + ************************************************************************* + * @file testredisserver.cpp + * @date 24.08.2017 + * @author Jacques Software + * @license See attached LICENSE.txt + ************************************************************************/ + +#include "testredisserver.h" + +#include +#include +#include +#include + +using namespace std; +using namespace jsonrpc; + +#define REDIS_BIN "redis-server" +#define REDIS_BIN_DIR "/usr/bin/" +#define REDIS_CONF "redis.conf" +#define REDIS_MAXLINES 255 +#define REDIS_KEY "accept connections" + +TestRedisServer::TestRedisServer() + : pid(0), maxlines(REDIS_MAXLINES), key(REDIS_KEY) { + this->Start(); +} + +TestRedisServer::~TestRedisServer() { this->Stop(); } + +bool TestRedisServer::Start() { + int pipefd[2]; + + pipe(pipefd); + + pid = fork(); + if (pid < 0) { + cerr << "ERROR: Failed to fork for redis server!" << endl; + return -1; + } + + if (pid == 0) { + this->StartProcess(pipefd); + } + + int ret = this->WaitProcess(pipefd); + if (ret == false) { + cerr << "ERROR: Server failed to start.\n"; + this->Stop(); + return -1; + } + + return true; +} + +bool TestRedisServer::Stop() { + if (pid != 0) { + kill(pid, 9); + pid = 0; + return true; + } + return false; +} + +bool TestRedisServer::WaitProcess(int pipefd[2]) { + close(pipefd[1]); + FILE *output = fdopen(pipefd[0], "r"); + + unsigned int i = 0; + do { + i++; + char *buffer = NULL; + size_t n; + getline(&buffer, &n, output); + if (n <= 0) { + free(buffer); + continue; + } + string line(buffer); + free(buffer); + + size_t found = line.find(key); + if (found != string::npos) { + return true; + } + + } while (i < maxlines); + + return false; +} + +void TestRedisServer::StartProcess(int pipefd[2]) { + // Close the unused read end + close(pipefd[0]); + + // We don't want input going to this process or errors from it. + FILE *f_null = fopen("/dev/null", "r+"); + if (f_null == NULL) { + cerr << "ERROR: Failed to open /dev/null for redis server!" << endl; + } + dup2(fileno(f_null), STDIN_FILENO); + dup2(fileno(f_null), STDERR_FILENO); + fclose(f_null); + + // Redirect output to our filedescriptor + dup2(pipefd[1], STDOUT_FILENO); + + // Start redis server with our redis.conf + execlp(REDIS_BIN, REDIS_BIN, REDIS_CONF, (char *)NULL); + exit(-1); +} diff --git a/src/test/testredisserver.h b/src/test/testredisserver.h new file mode 100644 index 00000000..b45c72bb --- /dev/null +++ b/src/test/testredisserver.h @@ -0,0 +1,83 @@ +/************************************************************************* + * libjson-rpc-cpp + ************************************************************************* + * @file testredisserver.h + * @date 24.08.2017 + * @author Jacques Software + * @license See attached LICENSE.txt + ************************************************************************/ + +#ifndef JSONRPC_TESTREDISSERVER_H +#define JSONRPC_TESTREDISSERVER_H + +#include +#include + +namespace jsonrpc { + + /** + * This class is used to spawn a redis-server in the background and is + * used to aid in running unit tests. + */ + class TestRedisServer + { + public: + TestRedisServer(); + ~TestRedisServer(); + + /** + * This method is used to spawn a redis-server in the background. + * @return true on success, false for failure. + */ + bool Start(); + + + /** + * This method will kill the redis server when we are done with it. + * @return true on success, false for failure. + */ + bool Stop(); + + private: + + /** + * This is a helper method for Start. This should be called + * by the child process and will setup the pipes for communication and + * start the redis server. + * @param pipefd The pipe that will be used for communication. + */ + void StartProcess(int pipefd[2]); + + + /** + * This is a helper method for Start. This method is used to + * block until the redis-server has finished starting up. + * Uses key - Scan for this string to signal success. + * Uses maxlines - Scan this many lines before giving up. + * @param pipefd The pipe that will be used for communication. + * @return true on success, false for failure. + */ + bool WaitProcess(int pipefd[2]); + + private: + + /** + * @brief The pid of the background redis-server. + */ + pid_t pid; + + + /** + * @brief The maximum number of lines to search for the key. + */ + unsigned int maxlines; + + /** + * @brief The key string to indicate the server is ready + */ + std::string key; + }; + +} // namespace jsonrpc + +#endif // JSONRPC_TESTREDISSERVER_H diff --git a/src/test/testserver.cpp b/src/test/testserver.cpp index e1515e5b..06248cc3 100644 --- a/src/test/testserver.cpp +++ b/src/test/testserver.cpp @@ -13,79 +13,86 @@ using namespace std; using namespace jsonrpc; -TestServer::TestServer(AbstractServerConnector &connector, serverVersion_t type) : - AbstractServer(connector, type), - cnt(-1) -{ - this->bindAndAddMethod(Procedure("sayHello", PARAMS_BY_NAME, JSON_STRING, "name", JSON_STRING, NULL), &TestServer::sayHello); - this->bindAndAddMethod(Procedure("getCounterValue", PARAMS_BY_NAME, JSON_INTEGER, NULL), &TestServer::getCounterValue); - this->bindAndAddMethod(Procedure("add", PARAMS_BY_NAME, JSON_INTEGER, "value1", JSON_INTEGER, "value2", JSON_INTEGER, NULL), &TestServer::add); - this->bindAndAddMethod(Procedure("sub", PARAMS_BY_POSITION, JSON_INTEGER, "value1", JSON_INTEGER, "value2", JSON_INTEGER, NULL), &TestServer::sub); - this->bindAndAddMethod(Procedure("exceptionMethod", PARAMS_BY_POSITION, JSON_INTEGER, NULL), &TestServer::exceptionMethod); - - this->bindAndAddNotification(Procedure("initCounter", PARAMS_BY_NAME, "value", JSON_INTEGER, NULL), &TestServer::initCounter); - this->bindAndAddNotification(Procedure("incrementCounter", PARAMS_BY_NAME, "value", JSON_INTEGER, NULL), &TestServer::incrementCounter); - this->bindAndAddNotification(Procedure("initZero", PARAMS_BY_POSITION, NULL), &TestServer::initZero); +TestServer::TestServer(AbstractServerConnector &connector, serverVersion_t type) + : AbstractServer(connector, type), cnt(-1) { + this->bindAndAddMethod(Procedure("sayHello", PARAMS_BY_NAME, JSON_STRING, + "name", JSON_STRING, NULL), + &TestServer::sayHello); + this->bindAndAddMethod( + Procedure("getCounterValue", PARAMS_BY_NAME, JSON_INTEGER, NULL), + &TestServer::getCounterValue); + this->bindAndAddMethod(Procedure("add", PARAMS_BY_NAME, JSON_INTEGER, + "value1", JSON_INTEGER, "value2", + JSON_INTEGER, NULL), + &TestServer::add); + this->bindAndAddMethod(Procedure("sub", PARAMS_BY_POSITION, JSON_INTEGER, + "value1", JSON_INTEGER, "value2", + JSON_INTEGER, NULL), + &TestServer::sub); + this->bindAndAddMethod( + Procedure("exceptionMethod", PARAMS_BY_POSITION, JSON_INTEGER, NULL), + &TestServer::exceptionMethod); + + this->bindAndAddNotification( + Procedure("initCounter", PARAMS_BY_NAME, "value", JSON_INTEGER, NULL), + &TestServer::initCounter); + this->bindAndAddNotification(Procedure("incrementCounter", PARAMS_BY_NAME, + "value", JSON_INTEGER, NULL), + &TestServer::incrementCounter); + this->bindAndAddNotification(Procedure("initZero", PARAMS_BY_POSITION, NULL), + &TestServer::initZero); } -void TestServer::sayHello(const Json::Value &request, Json::Value& response) -{ - response = "Hello: " + request["name"].asString() + "!"; +void TestServer::sayHello(const Json::Value &request, Json::Value &response) { + response = "Hello: " + request["name"].asString() + "!"; } -void TestServer::getCounterValue(const Json::Value &request, Json::Value &response) -{ - (void)request; - response = cnt; +void TestServer::getCounterValue(const Json::Value &request, + Json::Value &response) { + (void)request; + response = cnt; } -void TestServer::add(const Json::Value &request, Json::Value &response) -{ - response = request["value1"].asInt() + request["value2"].asInt(); +void TestServer::add(const Json::Value &request, Json::Value &response) { + response = request["value1"].asInt() + request["value2"].asInt(); } -void TestServer::sub(const Json::Value &request, Json::Value &response) -{ - response = request[0].asInt() - request[1].asInt(); +void TestServer::sub(const Json::Value &request, Json::Value &response) { + response = request[0].asInt() - request[1].asInt(); } -void TestServer::exceptionMethod(const Json::Value &request, Json::Value &response) -{ - (void)request; - (void)response; - Json::Value data; - data.append(33); - throw JsonRpcException(-32099, "User exception", data); +void TestServer::exceptionMethod(const Json::Value &request, + Json::Value &response) { + (void)request; + (void)response; + Json::Value data; + data.append(33); + throw JsonRpcException(-32099, "User exception", data); } -void TestServer::initCounter(const Json::Value &request) -{ - cnt= request["value"].asInt(); +void TestServer::initCounter(const Json::Value &request) { + cnt = request["value"].asInt(); } -void TestServer::incrementCounter(const Json::Value &request) -{ - cnt+= request["value"].asInt(); +void TestServer::incrementCounter(const Json::Value &request) { + cnt += request["value"].asInt(); } -void TestServer::initZero(const Json::Value &request) -{ - (void)request; - cnt = 0; +void TestServer::initZero(const Json::Value &request) { + (void)request; + cnt = 0; } -int TestServer::getCnt() -{ - return cnt; -} +int TestServer::getCnt() { return cnt; } -bool TestServer::bindAndAddMethod(const Procedure& proc, AbstractServer::methodPointer_t pointer) -{ - return AbstractServer::bindAndAddMethod(proc, pointer); +bool TestServer::bindAndAddMethod( + const Procedure &proc, + AbstractServer::methodPointer_t pointer) { + return AbstractServer::bindAndAddMethod(proc, pointer); } -bool TestServer::bindAndAddNotification(const Procedure& proc, AbstractServer::notificationPointer_t pointer) -{ - return AbstractServer::bindAndAddNotification(proc, pointer); +bool TestServer::bindAndAddNotification( + const Procedure &proc, + AbstractServer::notificationPointer_t pointer) { + return AbstractServer::bindAndAddNotification(proc, pointer); } -