Commit 51badb8c authored by Valentin Platzgummer's avatar Valentin Platzgummer

Working on ros_bridge, code not working in this state.

parent c4aad10e
......@@ -367,6 +367,7 @@ INCLUDEPATH += \
src/ViewWidgets \
src/Audio \
src/comm \
src/comm/ros_bridge \
src/input \
src/lib/qmapcontrol \
src/uas \
......@@ -463,6 +464,8 @@ HEADERS += \
src/Wima/testplanimetrycalculus.h \
src/Settings/WimaSettings.h \
src/QmlControls/QmlObjectVectorModel.h \
src/comm/ros_bridge/include/jsongenerator.h \
src/comm/ros_bridge/include/messages.h \
src/comm/utilities.h
SOURCES += \
src/Snake/clipper/clipper.cpp \
......@@ -500,7 +503,8 @@ SOURCES += \
src/Wima/TestPolygonCalculus.cpp \
src/Wima/testplanimetrycalculus.cpp \
src/Settings/WimaSettings.cc \
src/QmlControls/QmlObjectVectorModel.cc
src/QmlControls/QmlObjectVectorModel.cc \
src/comm/ros_bridge/src/messages.cpp
#
# Unit Test specific configuration goes here (requires full debug build with all plugins)
......
IndentWidth: 2
AccessModifierOffset: -2
UseTab: Never
ColumnLimit: 0
MaxEmptyLinesToKeep: 2
SpaceBeforeParens: Never
BreakBeforeBraces: Custom
BraceWrapping: {BeforeElse: true, BeforeCatch: true}
NamespaceIndentation: All
# https://github.com/github/gitignore/blob/master/CMake.gitignore
CMakeCache.txt
CMakeFiles
CMakeScripts
Makefile
cmake_install.cmake
install_manifest.txt
*.cmake
#Additions to https://github.com/github/gitignore/blob/master/CMake.gitignore
Testing
compile_commands.json
.usages_clang
*.crt
*.key
# executables
ws_examples
wss_examples
crypto_test
io_test
parse_test
before_script:
- mkdir build && cd build
- export CXXFLAGS=-Werror
- export CTEST_OUTPUT_ON_FAILURE=1
.script: &compile_and_test
script:
- cmake -DCMAKE_BUILD_TYPE=Release .. && make && make test
- rm -r *
- cmake -DCMAKE_BUILD_TYPE=Release -DUSE_STANDALONE_ASIO=ON .. && make && make test
arch:
image: "registry.gitlab.com/eidheim/docker-images:arch"
<<: *compile_and_test
buster:
image: "registry.gitlab.com/eidheim/docker-images:buster"
<<: *compile_and_test
jessie:
image: "registry.gitlab.com/eidheim/docker-images:jessie"
<<: *compile_and_test
stretch:
image: "registry.gitlab.com/eidheim/docker-images:stretch"
<<: *compile_and_test
thread-safety-analysis:
image: "registry.gitlab.com/eidheim/docker-images:arch"
script:
- CXX=clang++ cmake .. && make
static-analysis:
image: "registry.gitlab.com/eidheim/docker-images:arch"
script:
- scan-build cmake .. && scan-build --status-bugs make
cmake_minimum_required (VERSION 3.0)
project (Simple-WebSocket-Server)
option(USE_STANDALONE_ASIO "set ON to use standalone Asio instead of Boost.Asio" OFF)
option(BUILD_TESTING "set ON to build library tests" OFF)
if(NOT MSVC)
add_compile_options(-std=c++11 -Wall -Wextra -Wsign-conversion)
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wthread-safety)
endif()
else()
add_compile_options(/W1)
endif()
add_library(simple-websocket-server INTERFACE)
target_include_directories(simple-websocket-server INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
find_package(Threads REQUIRED)
target_link_libraries(simple-websocket-server INTERFACE ${CMAKE_THREAD_LIBS_INIT})
# TODO 2020 when Debian Jessie LTS ends:
# Remove Boost system, thread, regex components; use Boost::<component> aliases; remove Boost target_include_directories
if(USE_STANDALONE_ASIO)
target_compile_definitions(simple-websocket-server INTERFACE USE_STANDALONE_ASIO)
find_path(ASIO_PATH asio.hpp)
if(NOT ASIO_PATH)
message(FATAL_ERROR "Standalone Asio not found")
else()
target_include_directories(simple-websocket-server INTERFACE ${ASIO_PATH})
endif()
else()
find_package(Boost 1.54.0 COMPONENTS system thread coroutine context REQUIRED)
target_link_libraries(simple-websocket-server INTERFACE ${Boost_LIBRARIES})
target_include_directories(simple-websocket-server INTERFACE ${Boost_INCLUDE_DIR})
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9)
target_compile_definitions(simple-websocket-server INTERFACE USE_BOOST_REGEX)
find_package(Boost 1.54.0 COMPONENTS regex REQUIRED)
target_link_libraries(simple-websocket-server INTERFACE ${Boost_LIBRARIES})
target_include_directories(simple-websocket-server INTERFACE ${Boost_INCLUDE_DIR})
endif()
endif()
if(WIN32)
target_link_libraries(simple-websocket-server INTERFACE ws2_32 wsock32)
endif()
if(APPLE)
set(OPENSSL_ROOT_DIR "/usr/local/opt/openssl")
endif()
find_package(OpenSSL REQUIRED)
target_link_libraries(simple-websocket-server INTERFACE ${OPENSSL_LIBRARIES})
target_include_directories(simple-websocket-server INTERFACE ${OPENSSL_INCLUDE_DIR})
# If Simple-WebSocket-Server is not a sub-project:
if("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}")
add_executable(ws_examples ws_examples.cpp)
target_link_libraries(ws_examples simple-websocket-server)
if(OPENSSL_FOUND)
add_executable(wss_examples wss_examples.cpp)
target_link_libraries(wss_examples simple-websocket-server)
endif()
set(BUILD_TESTING ON)
install(FILES asio_compatibility.hpp server_ws.hpp client_ws.hpp server_wss.hpp client_wss.hpp crypto.hpp utility.hpp status_code.hpp mutex.hpp DESTINATION include/simple-websocket-server)
endif()
if(BUILD_TESTING)
enable_testing()
add_subdirectory(tests)
endif()
The MIT License (MIT)
Copyright (c) 2014-2019 Ole Christian Eidheim
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Simple-WebSocket-Server
=================
A very simple, fast, multithreaded, platform independent WebSocket (WS) and WebSocket Secure (WSS) server and client library implemented using C++11, Asio (both Boost.Asio and standalone Asio can be used) and OpenSSL. Created to be an easy way to make WebSocket endpoints in C++.
See https://gitlab.com/eidheim/Simple-Web-Server for an easy way to make REST resources available from C++ applications. Also, feel free to check out the new C++ IDE supporting C++11/14/17: https://gitlab.com/cppit/jucipp.
### Features
* RFC 6455 mostly supported: text/binary frames, fragmented messages, ping-pong, connection close with status and reason.
* Asynchronous message handling
* Thread pool if needed
* Platform independent
* WebSocket Secure support
* Timeouts, if any of SocketServer::timeout_request and SocketServer::timeout_idle are >0 (default: SocketServer::timeout_request=5 seconds, and SocketServer::timeout_idle=0 seconds; no timeout on idle connections)
* Simple way to add WebSocket endpoints using regex for path, and anonymous functions
* An easy to use WebSocket and WebSocket Secure client library
* C++ bindings to the following OpenSSL methods: Base64, MD5, SHA1, SHA256 and SHA512 (found in crypto.hpp)
### Usage
See [ws_examples.cpp](ws_examples.cpp) or [wss_examples.cpp](wss_examples.cpp) for example usage.
### Dependencies
* Boost.Asio or standalone Asio
* OpenSSL libraries
### Compile
Compile with a C++11 supported compiler:
```sh
mkdir build
cd build
cmake ..
make
cd ..
```
#### Run server and client examples
### WS
```sh
./build/ws_examples
```
### WSS
Before running the WSS-examples, an RSA private key (server.key) and an SSL certificate (server.crt) must be created.
Then:
```
./build/wss_examples
```
#ifndef SIMPLE_WEB_ASIO_COMPATIBILITY_HPP
#define SIMPLE_WEB_ASIO_COMPATIBILITY_HPP
#include <memory>
#ifdef USE_STANDALONE_ASIO
#include <asio.hpp>
#include <asio/steady_timer.hpp>
namespace SimpleWeb {
namespace error = asio::error;
using error_code = std::error_code;
using errc = std::errc;
using system_error = std::system_error;
namespace make_error_code = std;
} // namespace SimpleWeb
#else
#include <boost/asio.hpp>
#include <boost/asio/steady_timer.hpp>
namespace SimpleWeb {
namespace asio = boost::asio;
namespace error = asio::error;
using error_code = boost::system::error_code;
namespace errc = boost::system::errc;
using system_error = boost::system::system_error;
namespace make_error_code = boost::system::errc;
} // namespace SimpleWeb
#endif
namespace SimpleWeb {
#if(USE_STANDALONE_ASIO && ASIO_VERSION >= 101300) || BOOST_ASIO_VERSION >= 101300
using io_context = asio::io_context;
using resolver_results = asio::ip::tcp::resolver::results_type;
using async_connect_endpoint = asio::ip::tcp::endpoint;
inline void restart(io_context &context) noexcept {
context.restart();
}
inline asio::ip::address make_address(const std::string &str) noexcept {
return asio::ip::make_address(str);
}
template <typename socket_type>
asio::executor get_socket_executor(socket_type &socket) {
return socket.get_executor();
}
template <typename handler_type>
void async_resolve(asio::ip::tcp::resolver &resolver, const std::pair<std::string, std::string> &host_port, handler_type &&handler) {
resolver.async_resolve(host_port.first, host_port.second, std::forward<handler_type>(handler));
}
#else
using io_context = asio::io_service;
using resolver_results = asio::ip::tcp::resolver::iterator;
using async_connect_endpoint = asio::ip::tcp::resolver::iterator;
inline void restart(io_context &context) noexcept {
context.reset();
}
inline asio::ip::address make_address(const std::string &str) noexcept {
return asio::ip::address::from_string(str);
}
template <typename socket_type>
io_context &get_socket_executor(socket_type &socket) {
return socket.get_io_service();
}
template <typename handler_type>
void async_resolve(asio::ip::tcp::resolver &resolver, const std::pair<std::string, std::string> &host_port, handler_type &&handler) {
resolver.async_resolve(asio::ip::tcp::resolver::query(host_port.first, host_port.second), std::forward<handler_type>(handler));
}
#endif
} // namespace SimpleWeb
#endif /* SIMPLE_WEB_ASIO_COMPATIBILITY_HPP */
This diff is collapsed.
#ifndef SIMPLE_WEB_CLIENT_WSS_HPP
#define SIMPLE_WEB_CLIENT_WSS_HPP
#include "client_ws.hpp"
#ifdef USE_STANDALONE_ASIO
#include <asio/ssl.hpp>
#else
#include <boost/asio/ssl.hpp>
#endif
namespace SimpleWeb {
using WSS = asio::ssl::stream<asio::ip::tcp::socket>;
template <>
class SocketClient<WSS> : public SocketClientBase<WSS> {
public:
SocketClient(const std::string &server_port_path, bool verify_certificate = true,
const std::string &cert_file = std::string(), const std::string &private_key_file = std::string(),
const std::string &verify_file = std::string())
: SocketClientBase<WSS>::SocketClientBase(server_port_path, 443), context(asio::ssl::context::tlsv12) {
if(cert_file.size() > 0 && private_key_file.size() > 0) {
context.use_certificate_chain_file(cert_file);
context.use_private_key_file(private_key_file, asio::ssl::context::pem);
}
if(verify_certificate)
context.set_verify_callback(asio::ssl::rfc2818_verification(host));
if(verify_file.size() > 0)
context.load_verify_file(verify_file);
else
context.set_default_verify_paths();
if(verify_file.size() > 0 || verify_certificate)
context.set_verify_mode(asio::ssl::verify_peer);
else
context.set_verify_mode(asio::ssl::verify_none);
};
protected:
asio::ssl::context context;
void connect() override {
LockGuard connection_lock(connection_mutex);
auto connection = this->connection = std::shared_ptr<Connection>(new Connection(handler_runner, config.timeout_idle, *io_service, context));
connection_lock.unlock();
std::pair<std::string, std::string> host_port;
if(config.proxy_server.empty())
host_port = {host, std::to_string(port)};
else {
auto proxy_host_port = parse_host_port(config.proxy_server, 8080);
host_port = {proxy_host_port.first, std::to_string(proxy_host_port.second)};
}
auto resolver = std::make_shared<asio::ip::tcp::resolver>(*io_service);
connection->set_timeout(config.timeout_request);
async_resolve(*resolver, host_port, [this, connection, resolver](const error_code &ec, resolver_results results) {
connection->cancel_timeout();
auto lock = connection->handler_runner->continue_lock();
if(!lock)
return;
if(!ec) {
connection->set_timeout(this->config.timeout_request);
asio::async_connect(connection->socket->lowest_layer(), results, [this, connection, resolver](const error_code &ec, async_connect_endpoint /*endpoint*/) {
connection->cancel_timeout();
auto lock = connection->handler_runner->continue_lock();
if(!lock)
return;
if(!ec) {
asio::ip::tcp::no_delay option(true);
error_code ec;
connection->socket->lowest_layer().set_option(option, ec);
if(!this->config.proxy_server.empty()) {
auto write_buffer = std::make_shared<asio::streambuf>();
std::ostream write_stream(write_buffer.get());
auto host_port = this->host + ':' + std::to_string(this->port);
write_stream << "CONNECT " + host_port + " HTTP/1.1\r\n"
<< "Host: " << host_port << "\r\n\r\n";
connection->set_timeout(this->config.timeout_request);
asio::async_write(connection->socket->next_layer(), *write_buffer, [this, connection, write_buffer](const error_code &ec, std::size_t /*bytes_transferred*/) {
connection->cancel_timeout();
auto lock = connection->handler_runner->continue_lock();
if(!lock)
return;
if(!ec) {
connection->set_timeout(this->config.timeout_request);
asio::async_read_until(connection->socket->next_layer(), connection->in_message->streambuf, "\r\n\r\n", [this, connection](const error_code &ec, std::size_t /*bytes_transferred*/) {
connection->cancel_timeout();
auto lock = connection->handler_runner->continue_lock();
if(!lock)
return;
if(!ec) {
if(!ResponseMessage::parse(*connection->in_message, connection->http_version, connection->status_code, connection->header))
this->connection_error(connection, make_error_code::make_error_code(errc::protocol_error));
else {
if(connection->status_code.compare(0, 3, "200") != 0)
this->connection_error(connection, make_error_code::make_error_code(errc::permission_denied));
else
this->handshake(connection);
}
}
else
this->connection_error(connection, ec);
});
}
else
this->connection_error(connection, ec);
});
}
else
this->handshake(connection);
}
else
this->connection_error(connection, ec);
});
}
else
this->connection_error(connection, ec);
});
}
void handshake(const std::shared_ptr<Connection> &connection) {
SSL_set_tlsext_host_name(connection->socket->native_handle(), this->host.c_str());
connection->set_timeout(this->config.timeout_request);
connection->socket->async_handshake(asio::ssl::stream_base::client, [this, connection](const error_code &ec) {
connection->cancel_timeout();
auto lock = connection->handler_runner->continue_lock();
if(!lock)
return;
if(!ec)
upgrade(connection);
else
this->connection_error(connection, ec);
});
}
};
} // namespace SimpleWeb
#endif /* SIMPLE_WEB_CLIENT_WSS_HPP */
#ifndef SIMPLE_WEB_CRYPTO_HPP
#define SIMPLE_WEB_CRYPTO_HPP
#include <cmath>
#include <iomanip>
#include <istream>
#include <sstream>
#include <string>
#include <vector>
#include <openssl/buffer.h>
#include <openssl/evp.h>
#include <openssl/md5.h>
#include <openssl/sha.h>
namespace SimpleWeb {
// TODO 2017: remove workaround for MSVS 2012
#if _MSC_VER == 1700 // MSVS 2012 has no definition for round()
inline double round(double x) noexcept { // Custom definition of round() for positive numbers
return floor(x + 0.5);
}
#endif
class Crypto {
const static std::size_t buffer_size = 131072;
public:
class Base64 {
public:
static std::string encode(const std::string &ascii) noexcept {
std::string base64;
BIO *bio, *b64;
BUF_MEM *bptr = BUF_MEM_new();
b64 = BIO_new(BIO_f_base64());
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
bio = BIO_new(BIO_s_mem());
BIO_push(b64, bio);
BIO_set_mem_buf(b64, bptr, BIO_CLOSE);
// Write directly to base64-buffer to avoid copy
auto base64_length = static_cast<std::size_t>(round(4 * ceil(static_cast<double>(ascii.size()) / 3.0)));
base64.resize(base64_length);
bptr->length = 0;
bptr->max = base64_length + 1;
bptr->data = &base64[0];
if(BIO_write(b64, &ascii[0], static_cast<int>(ascii.size())) <= 0 || BIO_flush(b64) <= 0)
base64.clear();
// To keep &base64[0] through BIO_free_all(b64)
bptr->length = 0;
bptr->max = 0;
bptr->data = nullptr;
BIO_free_all(b64);
return base64;
}
static std::string decode(const std::string &base64) noexcept {
std::string ascii;
// Resize ascii, however, the size is a up to two bytes too large.
ascii.resize((6 * base64.size()) / 8);
BIO *b64, *bio;
b64 = BIO_new(BIO_f_base64());
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
// TODO: Remove in 2020
#if(defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER <= 0x1000115fL) || (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2080000fL)
bio = BIO_new_mem_buf((char *)&base64[0], static_cast<int>(base64.size()));
#else
bio = BIO_new_mem_buf(&base64[0], static_cast<int>(base64.size()));
#endif
bio = BIO_push(b64, bio);
auto decoded_length = BIO_read(bio, &ascii[0], static_cast<int>(ascii.size()));
if(decoded_length > 0)
ascii.resize(static_cast<std::size_t>(decoded_length));
else
ascii.clear();
BIO_free_all(b64);
return ascii;
}
};
/// Return hex string from bytes in input string.
static std::string to_hex_string(const std::string &input) noexcept {
std::stringstream hex_stream;
hex_stream << std::hex << std::internal << std::setfill('0');
for(auto &byte : input)
hex_stream << std::setw(2) << static_cast<int>(static_cast<unsigned char>(byte));
return hex_stream.str();
}
static std::string md5(const std::string &input, std::size_t iterations = 1) noexcept {
std::string hash;
hash.resize(128 / 8);
MD5(reinterpret_cast<const unsigned char *>(&input[0]), input.size(), reinterpret_cast<unsigned char *>(&hash[0]));
for(std::size_t c = 1; c < iterations; ++c)
MD5(reinterpret_cast<const unsigned char *>(&hash[0]), hash.size(), reinterpret_cast<unsigned char *>(&hash[0]));
return hash;
}
static std::string md5(std::istream &stream, std::size_t iterations = 1) noexcept {
MD5_CTX context;
MD5_Init(&context);
std::streamsize read_length;
std::vector<char> buffer(buffer_size);
while((read_length = stream.read(&buffer[0], buffer_size).gcount()) > 0)
MD5_Update(&context, buffer.data(), static_cast<std::size_t>(read_length));
std::string hash;
hash.resize(128 / 8);
MD5_Final(reinterpret_cast<unsigned char *>(&hash[0]), &context);
for(std::size_t c = 1; c < iterations; ++c)
MD5(reinterpret_cast<const unsigned char *>(&hash[0]), hash.size(), reinterpret_cast<unsigned char *>(&hash[0]));
return hash;
}
static std::string sha1(const std::string &input, std::size_t iterations = 1) noexcept {
std::string hash;
hash.resize(160 / 8);
SHA1(reinterpret_cast<const unsigned char *>(&input[0]), input.size(), reinterpret_cast<unsigned char *>(&hash[0]));
for(std::size_t c = 1; c < iterations; ++c)
SHA1(reinterpret_cast<const unsigned char *>(&hash[0]), hash.size(), reinterpret_cast<unsigned char *>(&hash[0]));
return hash;
}
static std::string sha1(std::istream &stream, std::size_t iterations = 1) noexcept {
SHA_CTX context;
SHA1_Init(&context);
std::streamsize read_length;
std::vector<char> buffer(buffer_size);
while((read_length = stream.read(&buffer[0], buffer_size).gcount()) > 0)
SHA1_Update(&context, buffer.data(), static_cast<std::size_t>(read_length));
std::string hash;
hash.resize(160 / 8);
SHA1_Final(reinterpret_cast<unsigned char *>(&hash[0]), &context);
for(std::size_t c = 1; c < iterations; ++c)
SHA1(reinterpret_cast<const unsigned char *>(&hash[0]), hash.size(), reinterpret_cast<unsigned char *>(&hash[0]));
return hash;
}
static std::string sha256(const std::string &input, std::size_t iterations = 1) noexcept {
std::string hash;
hash.resize(256 / 8);
SHA256(reinterpret_cast<const unsigned char *>(&input[0]), input.size(), reinterpret_cast<unsigned char *>(&hash[0]));
for(std::size_t c = 1; c < iterations; ++c)
SHA256(reinterpret_cast<const unsigned char *>(&hash[0]), hash.size(), reinterpret_cast<unsigned char *>(&hash[0]));
return hash;
}
static std::string sha256(std::istream &stream, std::size_t iterations = 1) noexcept {
SHA256_CTX context;
SHA256_Init(&context);
std::streamsize read_length;
std::vector<char> buffer(buffer_size);
while((read_length = stream.read(&buffer[0], buffer_size).gcount()) > 0)
SHA256_Update(&context, buffer.data(), static_cast<std::size_t>(read_length));
std::string hash;
hash.resize(256 / 8);
SHA256_Final(reinterpret_cast<unsigned char *>(&hash[0]), &context);
for(std::size_t c = 1; c < iterations; ++c)
SHA256(reinterpret_cast<const unsigned char *>(&hash[0]), hash.size(), reinterpret_cast<unsigned char *>(&hash[0]));
return hash;
}
static std::string sha512(const std::string &input, std::size_t iterations = 1) noexcept {
std::string hash;
hash.resize(512 / 8);
SHA512(reinterpret_cast<const unsigned char *>(&input[0]), input.size(), reinterpret_cast<unsigned char *>(&hash[0]));
for(std::size_t c = 1; c < iterations; ++c)
SHA512(reinterpret_cast<const unsigned char *>(&hash[0]), hash.size(), reinterpret_cast<unsigned char *>(&hash[0]));
return hash;
}
static std::string sha512(std::istream &stream, std::size_t iterations = 1) noexcept {
SHA512_CTX context;
SHA512_Init(&context);
std::streamsize read_length;
std::vector<char> buffer(buffer_size);
while((read_length = stream.read(&buffer[0], buffer_size).gcount()) > 0)
SHA512_Update(&context, buffer.data(), static_cast<std::size_t>(read_length));
std::string hash;
hash.resize(512 / 8);
SHA512_Final(reinterpret_cast<unsigned char *>(&hash[0]), &context);
for(std::size_t c = 1; c < iterations; ++c)
SHA512(reinterpret_cast<const unsigned char *>(&hash[0]), hash.size(), reinterpret_cast<unsigned char *>(&hash[0]));
return hash;
}
/// key_size is number of bytes of the returned key.
static std::string pbkdf2(const std::string &password, const std::string &salt, int iterations, int key_size) noexcept {
std::string key;
key.resize(static_cast<std::size_t>(key_size));
PKCS5_PBKDF2_HMAC_SHA1(password.c_str(), password.size(),
reinterpret_cast<const unsigned char *>(salt.c_str()), salt.size(), iterations,
key_size, reinterpret_cast<unsigned char *>(&key[0]));
return key;
}
};
} // namespace SimpleWeb
#endif /* SIMPLE_WEB_CRYPTO_HPP */
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>WebSocket Test</title>
</head>
<body>
<script>
var ws;
window.onload=function(){
ws=new WebSocket("ws://localhost:8080/echo");
ws.onmessage=function(evt){console.log(evt.data);};
ws.onopen=function(evt){
ws.send("Hello");
}
}
window.onclose=function(){
ws.close();
}
</script>
</body>
</html>
// Based on https://clang.llvm.org/docs/ThreadSafetyAnalysis.html
#ifndef SIMPLE_WEB_MUTEX_HPP
#define SIMPLE_WEB_MUTEX_HPP
#include <mutex>
// Enable thread safety attributes only with clang.
#if defined(__clang__) && (!defined(SWIG))
#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
#else
#define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op
#endif
#define CAPABILITY(x) \
THREAD_ANNOTATION_ATTRIBUTE__(capability(x))
#define SCOPED_CAPABILITY \
THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
#define GUARDED_BY(x) \
THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
#define PT_GUARDED_BY(x) \
THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x))
#define ACQUIRED_BEFORE(...) \
THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__))
#define ACQUIRED_AFTER(...) \
THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__))
#define REQUIRES(...) \
THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__))
#define REQUIRES_SHARED(...) \
THREAD_ANNOTATION_ATTRIBUTE__(requires_shared_capability(__VA_ARGS__))
#define ACQUIRE(...) \
THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__))
#define ACQUIRE_SHARED(...) \
THREAD_ANNOTATION_ATTRIBUTE__(acquire_shared_capability(__VA_ARGS__))
#define RELEASE(...) \
THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__))
#define RELEASE_SHARED(...) \
THREAD_ANNOTATION_ATTRIBUTE__(release_shared_capability(__VA_ARGS__))
#define TRY_ACQUIRE(...) \
THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_capability(__VA_ARGS__))
#define TRY_ACQUIRE_SHARED(...) \
THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_shared_capability(__VA_ARGS__))
#define EXCLUDES(...) \
THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))
#define ASSERT_CAPABILITY(x) \
THREAD_ANNOTATION_ATTRIBUTE__(assert_capability(x))
#define ASSERT_SHARED_CAPABILITY(x) \
THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_capability(x))
#define RETURN_CAPABILITY(x) \
THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
#define NO_THREAD_SAFETY_ANALYSIS \
THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
namespace SimpleWeb {
/// Defines an annotated interface for mutexes.
class CAPABILITY("mutex") Mutex {
std::mutex mutex;
public:
void lock() ACQUIRE() {
mutex.lock();
}
void unlock() RELEASE() {
mutex.unlock();
}
};
class SCOPED_CAPABILITY LockGuard {
Mutex &mutex;
bool locked = true;
public:
LockGuard(Mutex &mutex_) ACQUIRE(mutex_) : mutex(mutex_) {
mutex.lock();
}
void unlock() RELEASE() {
mutex.unlock();
locked = false;
}
~LockGuard() RELEASE() {
if(locked)
mutex.unlock();
}
};
} // namespace SimpleWeb
#endif // SIMPLE_WEB_MUTEX_HPP
This diff is collapsed.
#ifndef SIMPLE_WEB_SERVER_WSS_HPP
#define SIMPLE_WEB_SERVER_WSS_HPP
#include "server_ws.hpp"
#include <algorithm>
#include <openssl/ssl.h>
#ifdef USE_STANDALONE_ASIO
#include <asio/ssl.hpp>
#else
#include <boost/asio/ssl.hpp>
#endif
namespace SimpleWeb {
using WSS = asio::ssl::stream<asio::ip::tcp::socket>;
template <>
class SocketServer<WSS> : public SocketServerBase<WSS> {
bool set_session_id_context = false;
public:
SocketServer(const std::string &cert_file, const std::string &private_key_file, const std::string &verify_file = std::string())
: SocketServerBase<WSS>(443), context(asio::ssl::context::tlsv12) {
context.use_certificate_chain_file(cert_file);
context.use_private_key_file(private_key_file, asio::ssl::context::pem);
if(verify_file.size() > 0) {
context.load_verify_file(verify_file);
context.set_verify_mode(asio::ssl::verify_peer | asio::ssl::verify_fail_if_no_peer_cert |
asio::ssl::verify_client_once);
set_session_id_context = true;
}
}
protected:
asio::ssl::context context;
void after_bind() override {
if(set_session_id_context) {
// Creating session_id_context from address:port but reversed due to small SSL_MAX_SSL_SESSION_ID_LENGTH
auto session_id_context = std::to_string(acceptor->local_endpoint().port()) + ':';
session_id_context.append(config.address.rbegin(), config.address.rend());
SSL_CTX_set_session_id_context(context.native_handle(), reinterpret_cast<const unsigned char *>(session_id_context.data()),
std::min<std::size_t>(session_id_context.size(), SSL_MAX_SSL_SESSION_ID_LENGTH));
}
}
void accept() override {
std::shared_ptr<Connection> connection(new Connection(handler_runner, config.timeout_idle, *io_service, context));
acceptor->async_accept(connection->socket->lowest_layer(), [this, connection](const error_code &ec) {
auto lock = connection->handler_runner->continue_lock();
if(!lock)
return;
// Immediately start accepting a new connection (if io_service hasn't been stopped)
if(ec != error::operation_aborted)
accept();
if(!ec) {
asio::ip::tcp::no_delay option(true);
connection->socket->lowest_layer().set_option(option);
connection->set_timeout(config.timeout_request);
connection->socket->async_handshake(asio::ssl::stream_base::server, [this, connection](const error_code &ec) {
auto lock = connection->handler_runner->continue_lock();
if(!lock)
return;
connection->cancel_timeout();
if(!ec)
read_handshake(connection);
});
}
});
}
};
} // namespace SimpleWeb
#endif /* SIMPLE_WEB_SERVER_WSS_HPP */
#ifndef SIMPLE_WEB_STATUS_CODE_HPP
#define SIMPLE_WEB_STATUS_CODE_HPP
#include <cstdlib>
#include <map>
#include <string>
#include <unordered_map>
#include <vector>
namespace SimpleWeb {
enum class StatusCode {
unknown = 0,
information_continue = 100,
information_switching_protocols,
information_processing,
success_ok = 200,
success_created,
success_accepted,
success_non_authoritative_information,
success_no_content,
success_reset_content,
success_partial_content,
success_multi_status,
success_already_reported,
success_im_used = 226,
redirection_multiple_choices = 300,
redirection_moved_permanently,
redirection_found,
redirection_see_other,
redirection_not_modified,
redirection_use_proxy,
redirection_switch_proxy,
redirection_temporary_redirect,
redirection_permanent_redirect,
client_error_bad_request = 400,
client_error_unauthorized,
client_error_payment_required,
client_error_forbidden,
client_error_not_found,
client_error_method_not_allowed,
client_error_not_acceptable,
client_error_proxy_authentication_required,
client_error_request_timeout,
client_error_conflict,
client_error_gone,
client_error_length_required,
client_error_precondition_failed,
client_error_payload_too_large,
client_error_uri_too_long,
client_error_unsupported_media_type,
client_error_range_not_satisfiable,
client_error_expectation_failed,
client_error_im_a_teapot,
client_error_misdirection_required = 421,
client_error_unprocessable_entity,
client_error_locked,
client_error_failed_dependency,
client_error_upgrade_required = 426,
client_error_precondition_required = 428,
client_error_too_many_requests,
client_error_request_header_fields_too_large = 431,
client_error_unavailable_for_legal_reasons = 451,
server_error_internal_server_error = 500,
server_error_not_implemented,
server_error_bad_gateway,
server_error_service_unavailable,
server_error_gateway_timeout,
server_error_http_version_not_supported,
server_error_variant_also_negotiates,
server_error_insufficient_storage,
server_error_loop_detected,
server_error_not_extended = 510,
server_error_network_authentication_required
};
inline const std::map<StatusCode, std::string> &status_code_strings() {
static const std::map<StatusCode, std::string> status_code_strings = {
{StatusCode::unknown, ""},
{StatusCode::information_continue, "100 Continue"},
{StatusCode::information_switching_protocols, "101 Switching Protocols"},
{StatusCode::information_processing, "102 Processing"},
{StatusCode::success_ok, "200 OK"},
{StatusCode::success_created, "201 Created"},
{StatusCode::success_accepted, "202 Accepted"},
{StatusCode::success_non_authoritative_information, "203 Non-Authoritative Information"},
{StatusCode::success_no_content, "204 No Content"},
{StatusCode::success_reset_content, "205 Reset Content"},
{StatusCode::success_partial_content, "206 Partial Content"},
{StatusCode::success_multi_status, "207 Multi-Status"},
{StatusCode::success_already_reported, "208 Already Reported"},
{StatusCode::success_im_used, "226 IM Used"},
{StatusCode::redirection_multiple_choices, "300 Multiple Choices"},
{StatusCode::redirection_moved_permanently, "301 Moved Permanently"},
{StatusCode::redirection_found, "302 Found"},
{StatusCode::redirection_see_other, "303 See Other"},
{StatusCode::redirection_not_modified, "304 Not Modified"},
{StatusCode::redirection_use_proxy, "305 Use Proxy"},
{StatusCode::redirection_switch_proxy, "306 Switch Proxy"},
{StatusCode::redirection_temporary_redirect, "307 Temporary Redirect"},
{StatusCode::redirection_permanent_redirect, "308 Permanent Redirect"},
{StatusCode::client_error_bad_request, "400 Bad Request"},
{StatusCode::client_error_unauthorized, "401 Unauthorized"},
{StatusCode::client_error_payment_required, "402 Payment Required"},
{StatusCode::client_error_forbidden, "403 Forbidden"},
{StatusCode::client_error_not_found, "404 Not Found"},
{StatusCode::client_error_method_not_allowed, "405 Method Not Allowed"},
{StatusCode::client_error_not_acceptable, "406 Not Acceptable"},
{StatusCode::client_error_proxy_authentication_required, "407 Proxy Authentication Required"},
{StatusCode::client_error_request_timeout, "408 Request Timeout"},
{StatusCode::client_error_conflict, "409 Conflict"},
{StatusCode::client_error_gone, "410 Gone"},
{StatusCode::client_error_length_required, "411 Length Required"},
{StatusCode::client_error_precondition_failed, "412 Precondition Failed"},
{StatusCode::client_error_payload_too_large, "413 Payload Too Large"},
{StatusCode::client_error_uri_too_long, "414 URI Too Long"},
{StatusCode::client_error_unsupported_media_type, "415 Unsupported Media Type"},
{StatusCode::client_error_range_not_satisfiable, "416 Range Not Satisfiable"},
{StatusCode::client_error_expectation_failed, "417 Expectation Failed"},
{StatusCode::client_error_im_a_teapot, "418 I'm a teapot"},
{StatusCode::client_error_misdirection_required, "421 Misdirected Request"},
{StatusCode::client_error_unprocessable_entity, "422 Unprocessable Entity"},
{StatusCode::client_error_locked, "423 Locked"},
{StatusCode::client_error_failed_dependency, "424 Failed Dependency"},
{StatusCode::client_error_upgrade_required, "426 Upgrade Required"},
{StatusCode::client_error_precondition_required, "428 Precondition Required"},
{StatusCode::client_error_too_many_requests, "429 Too Many Requests"},
{StatusCode::client_error_request_header_fields_too_large, "431 Request Header Fields Too Large"},
{StatusCode::client_error_unavailable_for_legal_reasons, "451 Unavailable For Legal Reasons"},
{StatusCode::server_error_internal_server_error, "500 Internal Server Error"},
{StatusCode::server_error_not_implemented, "501 Not Implemented"},
{StatusCode::server_error_bad_gateway, "502 Bad Gateway"},
{StatusCode::server_error_service_unavailable, "503 Service Unavailable"},
{StatusCode::server_error_gateway_timeout, "504 Gateway Timeout"},
{StatusCode::server_error_http_version_not_supported, "505 HTTP Version Not Supported"},
{StatusCode::server_error_variant_also_negotiates, "506 Variant Also Negotiates"},
{StatusCode::server_error_insufficient_storage, "507 Insufficient Storage"},
{StatusCode::server_error_loop_detected, "508 Loop Detected"},
{StatusCode::server_error_not_extended, "510 Not Extended"},
{StatusCode::server_error_network_authentication_required, "511 Network Authentication Required"}};
return status_code_strings;
}
inline StatusCode status_code(const std::string &status_code_string) noexcept {
if(status_code_string.size() < 3)
return StatusCode::unknown;
auto number = status_code_string.substr(0, 3);
if(number[0] < '0' || number[0] > '9' || number[1] < '0' || number[1] > '9' || number[2] < '0' || number[2] > '9')
return StatusCode::unknown;
class StringToStatusCode : public std::unordered_map<std::string, SimpleWeb::StatusCode> {
public:
StringToStatusCode() {
for(auto &status_code : status_code_strings())
emplace(status_code.second.substr(0, 3), status_code.first);
}
};
static StringToStatusCode string_to_status_code;
auto pos = string_to_status_code.find(number);
if(pos == string_to_status_code.end())
return static_cast<StatusCode>(atoi(number.c_str()));
return pos->second;
}
inline const std::string &status_code(StatusCode status_code_enum) noexcept {
auto pos = status_code_strings().find(status_code_enum);
if(pos == status_code_strings().end()) {
static std::string empty_string;
return empty_string;
}
return pos->second;
}
} // namespace SimpleWeb
#endif // SIMPLE_WEB_STATUS_CODE_HPP
if(NOT MSVC)
add_compile_options(-fno-access-control)
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wno-thread-safety)
endif()
add_executable(parse_test parse_test.cpp)
target_link_libraries(parse_test simple-websocket-server)
add_test(parse_test parse_test)
endif()
add_executable(crypto_test crypto_test.cpp)
target_link_libraries(crypto_test simple-websocket-server)
add_test(crypto_test crypto_test)
add_executable(io_test io_test.cpp)
target_link_libraries(io_test simple-websocket-server)
add_test(io_test io_test)
#ifndef SIMPLE_WEB_ASSERT_HPP
#define SIMPLE_WEB_ASSERT_HPP
#include <cstdlib>
#include <iostream>
#define ASSERT(e) ((void)((e) ? ((void)0) : ((void)(std::cerr << "Assertion failed: (" << #e << "), function " << __func__ << ", file " << __FILE__ << ", line " << __LINE__ << ".\n"), std::abort())))
#endif /* SIMPLE_WEB_ASSERT_HPP */
#include "assert.hpp"
#include "crypto.hpp"
#include <vector>
using namespace std;
using namespace SimpleWeb;
const vector<pair<string, string>> base64_string_tests = {
{"", ""},
{"f", "Zg=="},
{"fo", "Zm8="},
{"foo", "Zm9v"},
{"foob", "Zm9vYg=="},
{"fooba", "Zm9vYmE="},
{"foobar", "Zm9vYmFy"},
{"The itsy bitsy spider climbed up the waterspout.\r\nDown came the rain\r\nand washed the spider out.\r\nOut came the sun\r\nand dried up all the rain\r\nand the itsy bitsy spider climbed up the spout again.",
"VGhlIGl0c3kgYml0c3kgc3BpZGVyIGNsaW1iZWQgdXAgdGhlIHdhdGVyc3BvdXQuDQpEb3duIGNhbWUgdGhlIHJhaW4NCmFuZCB3YXNoZWQgdGhlIHNwaWRlciBvdXQuDQpPdXQgY2FtZSB0aGUgc3VuDQphbmQgZHJpZWQgdXAgYWxsIHRoZSByYWluDQphbmQgdGhlIGl0c3kgYml0c3kgc3BpZGVyIGNsaW1iZWQgdXAgdGhlIHNwb3V0IGFnYWluLg=="}};
const vector<pair<string, string>> md5_string_tests = {
{"", "d41d8cd98f00b204e9800998ecf8427e"},
{"The quick brown fox jumps over the lazy dog", "9e107d9d372bb6826bd81d3542a419d6"}};
const vector<pair<string, string>> sha1_string_tests = {
{"", "da39a3ee5e6b4b0d3255bfef95601890afd80709"},
{"The quick brown fox jumps over the lazy dog", "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12"}};
const vector<pair<string, string>> sha256_string_tests = {
{"", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"},
{"The quick brown fox jumps over the lazy dog", "d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592"}};
const vector<pair<string, string>> sha512_string_tests = {
{"", "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"},
{"The quick brown fox jumps over the lazy dog", "07e547d9586f6a73f73fbac0435ed76951218fb7d0c8d788a309d785436bbb642e93a252a954f23912547d1e8a3b5ed6e1bfd7097821233fa0538f3db854fee6"}};
int main() {
for(auto &string_test : base64_string_tests) {
ASSERT(Crypto::Base64::encode(string_test.first) == string_test.second);
ASSERT(Crypto::Base64::decode(string_test.second) == string_test.first);
}
for(auto &string_test : md5_string_tests) {
ASSERT(Crypto::to_hex_string(Crypto::md5(string_test.first)) == string_test.second);
stringstream ss(string_test.first);
ASSERT(Crypto::to_hex_string(Crypto::md5(ss)) == string_test.second);
}
for(auto &string_test : sha1_string_tests) {
ASSERT(Crypto::to_hex_string(Crypto::sha1(string_test.first)) == string_test.second);
stringstream ss(string_test.first);
ASSERT(Crypto::to_hex_string(Crypto::sha1(ss)) == string_test.second);
}
for(auto &string_test : sha256_string_tests) {
ASSERT(Crypto::to_hex_string(Crypto::sha256(string_test.first)) == string_test.second);
stringstream ss(string_test.first);
ASSERT(Crypto::to_hex_string(Crypto::sha256(ss)) == string_test.second);
}
for(auto &string_test : sha512_string_tests) {
ASSERT(Crypto::to_hex_string(Crypto::sha512(string_test.first)) == string_test.second);
stringstream ss(string_test.first);
ASSERT(Crypto::to_hex_string(Crypto::sha512(ss)) == string_test.second);
}
//Testing iterations
ASSERT(Crypto::to_hex_string(Crypto::sha1("Test", 1)) == "640ab2bae07bedc4c163f679a746f7ab7fb5d1fa");
ASSERT(Crypto::to_hex_string(Crypto::sha1("Test", 2)) == "af31c6cbdecd88726d0a9b3798c71ef41f1624d5");
stringstream ss("Test");
ASSERT(Crypto::to_hex_string(Crypto::sha1(ss, 2)) == "af31c6cbdecd88726d0a9b3798c71ef41f1624d5");
ASSERT(Crypto::to_hex_string(Crypto::pbkdf2("Password", "Salt", 4096, 128 / 8)) == "f66df50f8aaa11e4d9721e1312ff2e66");
ASSERT(Crypto::to_hex_string(Crypto::pbkdf2("Password", "Salt", 8192, 512 / 8)) == "a941ccbc34d1ee8ebbd1d34824a419c3dc4eac9cbc7c36ae6c7ca8725e2b618a6ad22241e787af937b0960cf85aa8ea3a258f243e05d3cc9b08af5dd93be046c");
}
#include "assert.hpp"
#include "client_ws.hpp"
#include "server_ws.hpp"
using namespace std;
typedef SimpleWeb::SocketServer<SimpleWeb::WS> WsServer;
typedef SimpleWeb::SocketClient<SimpleWeb::WS> WsClient;
int main() {
WsServer server;
server.config.port = 8080;
server.config.thread_pool_size = 4;
auto &echo = server.endpoint["^/echo/?$"];
atomic<int> server_callback_count(0);
echo.on_message = [&server_callback_count](shared_ptr<WsServer::Connection> connection, shared_ptr<WsServer::InMessage> in_message) {
auto in_message_str = in_message->string();
ASSERT(in_message_str == "Hello");
++server_callback_count;
connection->send(in_message_str, [](const SimpleWeb::error_code &ec) {
if(ec) {
cerr << ec.message() << endl;
ASSERT(false);
}
});
};
echo.on_open = [&server_callback_count](shared_ptr<WsServer::Connection> connection) {
++server_callback_count;
ASSERT(!connection->remote_endpoint_address().empty());
ASSERT(connection->remote_endpoint_port() > 0);
};
echo.on_close = [&server_callback_count](shared_ptr<WsServer::Connection> /*connection*/, int /*status*/, const string & /*reason*/) {
++server_callback_count;
};
echo.on_error = [](shared_ptr<WsServer::Connection> /*connection*/, const SimpleWeb::error_code &ec) {
cerr << ec.message() << endl;
ASSERT(false);
};
auto &echo_thrice = server.endpoint["^/echo_thrice/?$"];
echo_thrice.on_message = [](shared_ptr<WsServer::Connection> connection, shared_ptr<WsServer::InMessage> in_message) {
auto out_message = make_shared<WsServer::OutMessage>();
*out_message << in_message->string();
connection->send(out_message, [connection, out_message](const SimpleWeb::error_code &ec) {
if(!ec)
connection->send(out_message);
});
connection->send(out_message);
};
auto &fragmented_message = server.endpoint["^/fragmented_message/?$"];
fragmented_message.on_message = [&server_callback_count](shared_ptr<WsServer::Connection> connection, shared_ptr<WsServer::InMessage> in_message) {
++server_callback_count;
ASSERT(in_message->string() == "fragmented message");
connection->send("fragmented", nullptr, 1);
connection->send(" ", nullptr, 0);
connection->send("message", nullptr, 128);
};
thread server_thread([&server]() {
server.start();
});
this_thread::sleep_for(chrono::seconds(1));
// test stop
server.stop();
server_thread.join();
server_thread = thread([&server]() {
server.start();
});
this_thread::sleep_for(chrono::seconds(1));
for(size_t i = 0; i < 400; ++i) {
WsClient client("localhost:8080/echo");
atomic<int> client_callback_count(0);
atomic<bool> closed(false);
client.on_message = [&](shared_ptr<WsClient::Connection> connection, shared_ptr<WsClient::InMessage> in_message) {
ASSERT(in_message->string() == "Hello");
++client_callback_count;
ASSERT(!closed);
connection->send_close(1000);
};
client.on_open = [&](shared_ptr<WsClient::Connection> connection) {
++client_callback_count;
ASSERT(!closed);
connection->send("Hello");
};
client.on_close = [&](shared_ptr<WsClient::Connection> /*connection*/, int /*status*/, const string & /*reason*/) {
++client_callback_count;
ASSERT(!closed);
closed = true;
};
client.on_error = [](shared_ptr<WsClient::Connection> /*connection*/, const SimpleWeb::error_code &ec) {
cerr << ec.message() << endl;
ASSERT(false);
};
thread client_thread([&client]() {
client.start();
});
while(!closed)
this_thread::sleep_for(chrono::milliseconds(5));
client.stop();
client_thread.join();
ASSERT(client_callback_count == 3);
}
ASSERT(server_callback_count == 1200);
for(size_t i = 0; i < 400; ++i) {
WsClient client("localhost:8080/echo_thrice");
atomic<int> client_callback_count(0);
atomic<bool> closed(false);
client.on_message = [&](shared_ptr<WsClient::Connection> connection, shared_ptr<WsClient::InMessage> in_message) {
ASSERT(in_message->string() == "Hello");
++client_callback_count;
ASSERT(!closed);
if(client_callback_count == 4)
connection->send_close(1000);
};
client.on_open = [&](shared_ptr<WsClient::Connection> connection) {
++client_callback_count;
ASSERT(!closed);
connection->send("Hello");
};
client.on_close = [&](shared_ptr<WsClient::Connection> /*connection*/, int /*status*/, const string & /*reason*/) {
++client_callback_count;
ASSERT(!closed);
closed = true;
};
client.on_error = [](shared_ptr<WsClient::Connection> /*connection*/, const SimpleWeb::error_code &ec) {
cerr << ec.message() << endl;
ASSERT(false);
};
thread client_thread([&client]() {
client.start();
});
while(!closed)
this_thread::sleep_for(chrono::milliseconds(5));
client.stop();
client_thread.join();
ASSERT(client_callback_count == 5);
}
{
WsClient client("localhost:8080/echo");
server_callback_count = 0;
atomic<int> client_callback_count(0);
atomic<bool> closed(false);
client.on_message = [&](shared_ptr<WsClient::Connection> connection, shared_ptr<WsClient::InMessage> in_message) {
ASSERT(in_message->string() == "Hello");
++client_callback_count;
ASSERT(!closed);
if(client_callback_count == 201)
connection->send_close(1000);
};
client.on_open = [&](shared_ptr<WsClient::Connection> connection) {
++client_callback_count;
ASSERT(!closed);
for(size_t i = 0; i < 200; ++i) {
auto send_stream = make_shared<WsClient::OutMessage>();
*send_stream << "Hello";
connection->send(send_stream);
}
};
client.on_close = [&](shared_ptr<WsClient::Connection> /*connection*/, int /*status*/, const string & /*reason*/) {
++client_callback_count;
ASSERT(!closed);
closed = true;
};
client.on_error = [](shared_ptr<WsClient::Connection> /*connection*/, const SimpleWeb::error_code &ec) {
cerr << ec.message() << endl;
ASSERT(false);
};
thread client_thread([&client]() {
client.start();
});
while(!closed)
this_thread::sleep_for(chrono::milliseconds(5));
client.stop();
client_thread.join();
ASSERT(client_callback_count == 202);
ASSERT(server_callback_count == 202);
}
{
WsClient client("localhost:8080/fragmented_message");
server_callback_count = 0;
atomic<int> client_callback_count(0);
atomic<bool> closed(false);
client.on_message = [&](shared_ptr<WsClient::Connection> connection, shared_ptr<WsClient::InMessage> in_message) {
ASSERT(in_message->string() == "fragmented message");
++client_callback_count;
connection->send_close(1000);
};
client.on_open = [&](shared_ptr<WsClient::Connection> connection) {
++client_callback_count;
ASSERT(!closed);
connection->send("fragmented", nullptr, 1);
connection->send(" ", nullptr, 0);
connection->send("message", nullptr, 128);
};
client.on_close = [&](shared_ptr<WsClient::Connection> /*connection*/, int /*status*/, const string & /*reason*/) {
ASSERT(!closed);
closed = true;
};
client.on_error = [](shared_ptr<WsClient::Connection> /*connection*/, const SimpleWeb::error_code &ec) {
cerr << ec.message() << endl;
ASSERT(false);
};
thread client_thread([&client]() {
client.start();
});
while(!closed)
this_thread::sleep_for(chrono::milliseconds(5));
client.stop();
client_thread.join();
ASSERT(client_callback_count == 2);
ASSERT(server_callback_count == 1);
}
server.stop();
server_thread.join();
}
#include "assert.hpp"
#include "client_ws.hpp"
#include "server_ws.hpp"
#include <iostream>
using namespace std;
using namespace SimpleWeb;
class SocketServerTest : public SocketServerBase<WS> {
public:
SocketServerTest() : SocketServerBase<WS>::SocketServerBase(8080) {}
void accept() {}
void parse_request_test() {
std::shared_ptr<Connection> connection(new Connection(handler_runner, 0, *io_service));
ostream ss(&connection->read_buffer);
ss << "GET /test/ HTTP/1.1\r\n";
ss << "TestHeader: test\r\n";
ss << "TestHeader2:test2\r\n";
ss << "TestHeader3:test3a\r\n";
ss << "TestHeader3:test3b\r\n";
ss << "\r\n";
std::istream stream(&connection->read_buffer);
ASSERT(RequestMessage::parse(stream, connection->method, connection->path, connection->query_string, connection->http_version, connection->header));
ASSERT(connection->method == "GET");
ASSERT(connection->path == "/test/");
ASSERT(connection->http_version == "1.1");
ASSERT(connection->header.size() == 4);
auto header_it = connection->header.find("TestHeader");
ASSERT(header_it != connection->header.end() && header_it->second == "test");
header_it = connection->header.find("TestHeader2");
ASSERT(header_it != connection->header.end() && header_it->second == "test2");
header_it = connection->header.find("testheader");
ASSERT(header_it != connection->header.end() && header_it->second == "test");
header_it = connection->header.find("testheader2");
ASSERT(header_it != connection->header.end() && header_it->second == "test2");
auto range = connection->header.equal_range("testheader3");
auto first = range.first;
auto second = first;
++second;
ASSERT(range.first != connection->header.end() && range.second != connection->header.end() &&
((first->second == "test3a" && second->second == "test3b") ||
(first->second == "test3b" && second->second == "test3a")));
}
};
class SocketClientTest : public SocketClientBase<WS> {
public:
SocketClientTest(const std::string &server_port_path) : SocketClientBase<WS>::SocketClientBase(server_port_path, 80) {}
void connect() {}
void constructor_parse_test1() {
ASSERT(path == "/test");
ASSERT(host == "test.org");
ASSERT(port == 8080);
}
void constructor_parse_test2() {
ASSERT(path == "/test");
ASSERT(host == "test.org");
ASSERT(port == 80);
}
void constructor_parse_test3() {
ASSERT(path == "/");
ASSERT(host == "test.org");
ASSERT(port == 80);
}
void constructor_parse_test4() {
ASSERT(path == "/");
ASSERT(host == "test.org");
ASSERT(port == 8080);
}
void parse_response_header_test() {
auto connection = std::shared_ptr<Connection>(new Connection(handler_runner, config.timeout_idle, *io_service));
connection->in_message = std::shared_ptr<InMessage>(new InMessage());
ostream stream(&connection->in_message->streambuf);
stream << "HTTP/1.1 200 OK\r\n";
stream << "TestHeader: test\r\n";
stream << "TestHeader2:test2\r\n";
stream << "TestHeader3:test3a\r\n";
stream << "TestHeader3:test3b\r\n";
stream << "\r\n";
ASSERT(ResponseMessage::parse(*connection->in_message, connection->http_version, connection->status_code, connection->header));
ASSERT(connection->header.size() == 4);
auto header_it = connection->header.find("TestHeader");
ASSERT(header_it != connection->header.end() && header_it->second == "test");
header_it = connection->header.find("TestHeader2");
ASSERT(header_it != connection->header.end() && header_it->second == "test2");
header_it = connection->header.find("testheader");
ASSERT(header_it != connection->header.end() && header_it->second == "test");
header_it = connection->header.find("testheader2");
ASSERT(header_it != connection->header.end() && header_it->second == "test2");
auto range = connection->header.equal_range("testheader3");
auto first = range.first;
auto second = first;
++second;
ASSERT(range.first != connection->header.end() && range.second != connection->header.end() &&
((first->second == "test3a" && second->second == "test3b") ||
(first->second == "test3b" && second->second == "test3a")));
connection.reset();
}
};
int main() {
ASSERT(case_insensitive_equal("Test", "tesT"));
ASSERT(case_insensitive_equal("tesT", "test"));
ASSERT(!case_insensitive_equal("test", "tseT"));
CaseInsensitiveEqual equal;
ASSERT(equal("Test", "tesT"));
ASSERT(equal("tesT", "test"));
ASSERT(!equal("test", "tset"));
CaseInsensitiveHash hash;
ASSERT(hash("Test") == hash("tesT"));
ASSERT(hash("tesT") == hash("test"));
ASSERT(hash("test") != hash("tset"));
SocketServerTest serverTest;
serverTest.io_service = std::make_shared<io_context>();
serverTest.parse_request_test();
SocketClientTest clientTest("test.org:8080/test");
clientTest.constructor_parse_test1();
SocketClientTest clientTest2("test.org/test");
clientTest2.constructor_parse_test2();
SocketClientTest clientTest3("test.org");
clientTest3.constructor_parse_test3();
SocketClientTest clientTest4("test.org:8080");
clientTest4.io_service = std::make_shared<io_context>();
clientTest4.constructor_parse_test4();
clientTest4.parse_response_header_test();
}
This diff is collapsed.
#include "client_ws.hpp"
#include "server_ws.hpp"
using namespace std;
using WsServer = SimpleWeb::SocketServer<SimpleWeb::WS>;
using WsClient = SimpleWeb::SocketClient<SimpleWeb::WS>;
int main() {
// WebSocket (WS)-server at port 8080 using 1 thread
WsServer server;
server.config.port = 8080;
// Example 1: echo WebSocket endpoint
// Added debug messages for example use of the callbacks
// Test with the following JavaScript:
// var ws=new WebSocket("ws://localhost:8080/echo");
// ws.onmessage=function(evt){console.log(evt.data);};
// ws.send("test");
auto &echo = server.endpoint["^/echo/?$"];
echo.on_message = [](shared_ptr<WsServer::Connection> connection, shared_ptr<WsServer::InMessage> in_message) {
auto out_message = in_message->string();
cout << "Server: Message received: \"" << out_message << "\" from " << connection.get() << endl;
cout << "Server: Sending message \"" << out_message << "\" to " << connection.get() << endl;
// connection->send is an asynchronous function
connection->send(out_message, [](const SimpleWeb::error_code &ec) {
if(ec) {
cout << "Server: Error sending message. " <<
// See http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/reference.html, Error Codes for error code meanings
"Error: " << ec << ", error message: " << ec.message() << endl;
}
});
// Alternatively use streams:
// auto out_message = make_shared<WsServer::OutMessage>();
// *out_message << in_message->string();
// connection->send(out_message);
};
echo.on_open = [](shared_ptr<WsServer::Connection> connection) {
cout << "Server: Opened connection " << connection.get() << endl;
};
// See RFC 6455 7.4.1. for status codes
echo.on_close = [](shared_ptr<WsServer::Connection> connection, int status, const string & /*reason*/) {
cout << "Server: Closed connection " << connection.get() << " with status code " << status << endl;
};
// Can modify handshake response headers here if needed
echo.on_handshake = [](shared_ptr<WsServer::Connection> /*connection*/, SimpleWeb::CaseInsensitiveMultimap & /*response_header*/) {
return SimpleWeb::StatusCode::information_switching_protocols; // Upgrade to websocket
};
// See http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/reference.html, Error Codes for error code meanings
echo.on_error = [](shared_ptr<WsServer::Connection> connection, const SimpleWeb::error_code &ec) {
cout << "Server: Error in connection " << connection.get() << ". "
<< "Error: " << ec << ", error message: " << ec.message() << endl;
};
// Example 2: Echo thrice
// Demonstrating queuing of messages by sending a received message three times back to the client.
// Concurrent send operations are automatically queued by the library.
// Test with the following JavaScript:
// var ws=new WebSocket("ws://localhost:8080/echo_thrice");
// ws.onmessage=function(evt){console.log(evt.data);};
// ws.send("test");
auto &echo_thrice = server.endpoint["^/echo_thrice/?$"];
echo_thrice.on_message = [](shared_ptr<WsServer::Connection> connection, shared_ptr<WsServer::InMessage> in_message) {
auto out_message = make_shared<string>(in_message->string());
connection->send(*out_message, [connection, out_message](const SimpleWeb::error_code &ec) {
if(!ec)
connection->send(*out_message); // Sent after the first send operation is finished
});
connection->send(*out_message); // Most likely queued. Sent after the first send operation is finished.
};
// Example 3: Echo to all WebSocket endpoints
// Sending received messages to all connected clients
// Test with the following JavaScript on more than one browser windows:
// var ws=new WebSocket("ws://localhost:8080/echo_all");
// ws.onmessage=function(evt){console.log(evt.data);};
// ws.send("test");
auto &echo_all = server.endpoint["^/echo_all/?$"];
echo_all.on_message = [&server](shared_ptr<WsServer::Connection> /*connection*/, shared_ptr<WsServer::InMessage> in_message) {
auto out_message = in_message->string();
// echo_all.get_connections() can also be used to solely receive connections on this endpoint
for(auto &a_connection : server.get_connections())
a_connection->send(out_message);
};
thread server_thread([&server]() {
// Start WS-server
server.start();
});
// Wait for server to start so that the client can connect
this_thread::sleep_for(chrono::seconds(1));
// Example 4: Client communication with server
// Possible output:
// Server: Opened connection 0x7fcf21600380
// Client: Opened connection
// Client: Sending message: "Hello"
// Server: Message received: "Hello" from 0x7fcf21600380
// Server: Sending message "Hello" to 0x7fcf21600380
// Client: Message received: "Hello"
// Client: Sending close connection
// Server: Closed connection 0x7fcf21600380 with status code 1000
// Client: Closed connection with status code 1000
WsClient client("localhost:8080/echo");
client.on_message = [](shared_ptr<WsClient::Connection> connection, shared_ptr<WsClient::InMessage> in_message) {
cout << "Client: Message received: \"" << in_message->string() << "\"" << endl;
cout << "Client: Sending close connection" << endl;
connection->send_close(1000);
};
client.on_open = [](shared_ptr<WsClient::Connection> connection) {
cout << "Client: Opened connection" << endl;
string out_message("Hello");
cout << "Client: Sending message: \"" << out_message << "\"" << endl;
connection->send(out_message);
};
client.on_close = [](shared_ptr<WsClient::Connection> /*connection*/, int status, const string & /*reason*/) {
cout << "Client: Closed connection with status code " << status << endl;
};
// See http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/reference.html, Error Codes for error code meanings
client.on_error = [](shared_ptr<WsClient::Connection> /*connection*/, const SimpleWeb::error_code &ec) {
cout << "Client: Error: " << ec << ", error message: " << ec.message() << endl;
};
client.start();
server_thread.join();
}
#include "client_wss.hpp"
#include "server_wss.hpp"
using namespace std;
using WssServer = SimpleWeb::SocketServer<SimpleWeb::WSS>;
using WssClient = SimpleWeb::SocketClient<SimpleWeb::WSS>;
int main() {
// WebSocket Secure (WSS)-server at port 8080 using 1 thread
WssServer server("server.crt", "server.key");
server.config.port = 8080;
// Example 1: echo WebSocket Secure endpoint
// Added debug messages for example use of the callbacks
// Test with the following JavaScript:
// var wss=new WebSocket("wss://localhost:8080/echo");
// wss.onmessage=function(evt){console.log(evt.data);};
// wss.send("test");
auto &echo = server.endpoint["^/echo/?$"];
echo.on_message = [](shared_ptr<WssServer::Connection> connection, shared_ptr<WssServer::InMessage> in_message) {
auto out_message = in_message->string();
cout << "Server: Message received: \"" << out_message << "\" from " << connection.get() << endl;
cout << "Server: Sending message \"" << out_message << "\" to " << connection.get() << endl;
// connection->send is an asynchronous function
connection->send(out_message, [](const SimpleWeb::error_code &ec) {
if(ec) {
cout << "Server: Error sending message. " <<
// See http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/reference.html, Error Codes for error code meanings
"Error: " << ec << ", error message: " << ec.message() << endl;
}
});
// Alternatively use streams:
// auto out_message = make_shared<WssServer::OutMessage>();
// *out_message << in_message->string();
// connection->send(out_message);
};
echo.on_open = [](shared_ptr<WssServer::Connection> connection) {
cout << "Server: Opened connection " << connection.get() << endl;
};
// See RFC 6455 7.4.1. for status codes
echo.on_close = [](shared_ptr<WssServer::Connection> connection, int status, const string & /*reason*/) {
cout << "Server: Closed connection " << connection.get() << " with status code " << status << endl;
};
// Can modify handshake response header here if needed
echo.on_handshake = [](shared_ptr<WssServer::Connection> /*connection*/, SimpleWeb::CaseInsensitiveMultimap & /*response_header*/) {
return SimpleWeb::StatusCode::information_switching_protocols; // Upgrade to websocket
};
// See http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/reference.html, Error Codes for error code meanings
echo.on_error = [](shared_ptr<WssServer::Connection> connection, const SimpleWeb::error_code &ec) {
cout << "Server: Error in connection " << connection.get() << ". "
<< "Error: " << ec << ", error message: " << ec.message() << endl;
};
// Example 2: Echo thrice
// Demonstrating queuing of messages by sending a received message three times back to the client.
// Concurrent send operations are automatically queued by the library.
// Test with the following JavaScript:
// var wss=new WebSocket("wss://localhost:8080/echo_thrice");
// wss.onmessage=function(evt){console.log(evt.data);};
// wss.send("test");
auto &echo_thrice = server.endpoint["^/echo_thrice/?$"];
echo_thrice.on_message = [](shared_ptr<WssServer::Connection> connection, shared_ptr<WssServer::InMessage> in_message) {
auto out_message = make_shared<string>(in_message->string());
connection->send(*out_message, [connection, out_message](const SimpleWeb::error_code &ec) {
if(!ec)
connection->send(*out_message); // Sent after the first send operation is finished
});
connection->send(*out_message); // Most likely queued. Sent after the first send operation is finished.
};
// Example 3: Echo to all WebSocket Secure endpoints
// Sending received messages to all connected clients
// Test with the following JavaScript on more than one browser windows:
// var wss=new WebSocket("wss://localhost:8080/echo_all");
// wss.onmessage=function(evt){console.log(evt.data);};
// wss.send("test");
auto &echo_all = server.endpoint["^/echo_all/?$"];
echo_all.on_message = [&server](shared_ptr<WssServer::Connection> /*connection*/, shared_ptr<WssServer::InMessage> in_message) {
auto out_message = in_message->string();
// echo_all.get_connections() can also be used to solely receive connections on this endpoint
for(auto &a_connection : server.get_connections())
a_connection->send(out_message);
};
thread server_thread([&server]() {
// Start WSS-server
server.start();
});
// Wait for server to start so that the client can connect
this_thread::sleep_for(chrono::seconds(1));
// Example 4: Client communication with server
// Second Client() parameter set to false: no certificate verification
// Possible output:
// Server: Opened connection 0x7fcf21600380
// Client: Opened connection
// Client: Sending message: "Hello"
// Server: Message received: "Hello" from 0x7fcf21600380
// Server: Sending message "Hello" to 0x7fcf21600380
// Client: Message received: "Hello"
// Client: Sending close connection
// Server: Closed connection 0x7fcf21600380 with status code 1000
// Client: Closed connection with status code 1000
WssClient client("localhost:8080/echo", false);
client.on_message = [](shared_ptr<WssClient::Connection> connection, shared_ptr<WssClient::InMessage> in_message) {
cout << "Client: Message received: \"" << in_message->string() << "\"" << endl;
cout << "Client: Sending close connection" << endl;
connection->send_close(1000);
};
client.on_open = [](shared_ptr<WssClient::Connection> connection) {
cout << "Client: Opened connection" << endl;
string out_message("Hello");
cout << "Client: Sending message: \"" << out_message << "\"" << endl;
connection->send(out_message);
};
client.on_close = [](shared_ptr<WssClient::Connection> /*connection*/, int status, const string & /*reason*/) {
cout << "Client: Closed connection with status code " << status << endl;
};
// See http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/reference.html, Error Codes for error code meanings
client.on_error = [](shared_ptr<WssClient::Connection> /*connection*/, const SimpleWeb::error_code &ec) {
cout << "Client: Error: " << ec << ", error message: " << ec.message() << endl;
};
client.start();
server_thread.join();
}
#ifndef JSONGENERATOR_H
#define JSONGENERATOR_H
#include "rapidjson/include/rapidjson/document.h"
namespace ros_bridge {
template <class JsonDocumentType = rapidjson::Document,
class TimeType,
>
class JsonGenerator {
};
}
#endif // JSONGENERATOR_H
#ifndef MESSAGES_H
#define MESSAGES_H
#include <iostream>
#include <vector>
#include <array>
namespace ros_bridge {
//! @brief C++ representation of ros::Time
class Time{
public:
Time();
Time(uint32_t secs, uint32_t nsecs);
uint32_t getSecs() const;
uint32_t getNSecs() const;
void setSecs (uint32_t secs);
void setNSecs (uint32_t nsecs);
private:
uint32_t _secs;
uint32_t _nsecs;
};
//! @brief C++ representation of std_msgs/Header
class Header{
public:
Header();
Header(uint32_t seq, const Time &stamp, const std::string &frame_id);
uint32_t getSeq() const;
const Time &getStamp() const;
const std::string &getFrameId() const;
void setSeq (uint32_t seq);
void setStamp (const Time & stamp);
void setFrameId (const std::string &frame_id);
private:
uint32_t _seq;
Time _stamp;
std::string _frame_id;
};
//! @brief C++ representation of geometry_msgs/Point32
class Point32{
public:
Point32();
Point32(_Float32 x, _Float32 y, _Float32 z);
template<unsigned int i>
void set(_Float32 component)
{
static_assert (i < 3, "Index out of bonds.");
_components[i] = component;
}
template<unsigned int i>
_Float32 get(void) const
{
static_assert (i < 3, "Index out of bonds.");
return _components[i];
}
private:
std::array<_Float32, 3> _components;
};
//! @brief C++ representation of geometry_msgs/Polygon
template <class PointType = Point32>
class Polygon{
public:
Polygon(){}
Polygon(const std::vector<Point32> &points) : _points(points) {}
Polygon(const Polygon &poly) : _points(poly.get()) {}
const std::vector<Point32> &get() const
{
return _points;
}
void set(std::vector<Point32> &points)
{
_points = points;
}
void clear()
{
_points.clear();
}
PointType &at(unsigned int i)
{
return _points.at(i);
}
void push_back(const Point32 &p)
{
_points.push_back(p);
}
void pop_back()
{
_points.pop_back();
}
private:
std::vector<PointType> _points;
};
//! @brief C++ representation of geometry_msgs/PolygonStamped
template <class HeaderType = Header, class PolygonType = Polygon<>>
class PolygonStamped : public PolygonType{
public:
PolygonStamped();
PolygonStamped(const Header &header, const PolygonType &polygon) :
_header(header)
, PolygonType(polygon) {}
private:
Header _header;
};
//! @brief C++ representation of jsk_recognition_msgs/PolygonArray
template <class PolygonType = Polygon<>, class HeaderType = Header, template<class> class ContainerType = std::vector >
class PolygonArray{
public:
PolygonArray()
: PolygonArray(HeaderType()
, ContainerType<PolygonType>()
, ContainerType<uint32_t>()
, ContainerType<_Float32>())
{}
PolygonArray(const HeaderType &header,
const ContainerType<PolygonType> &polygons,
const ContainerType<uint32_t> &labels,
const ContainerType<_Float32> &likelihood)
: _header(header)
, _polygons(polygons)
, _labels(labels)
, _likelihood(likelihood)
{}
private:
HeaderType _header;
ContainerType<PolygonType> _polygons;
ContainerType<uint32_t> _labels;
ContainerType<_Float32> _likelihood;
};
}
#endif // MESSAGES_H
# Set the default behavior, in case people don't have core.autocrlf set.
* text=auto
# Explicitly declare text files you want to always be normalized and converted
# to native line endings on checkout.
*.cpp text
*.h text
*.txt text
*.md text
*.cmake text
*.svg text
*.dot text
*.yml text
*.in text
*.sh text
*.autopkg text
Dockerfile text
# Denote all files that are truly binary and should not be modified.
*.png binary
*.jpg binary
*.json binary
\ No newline at end of file
/bin/*
!/bin/data
!/bin/encodings
!/bin/jsonchecker
!/bin/types
/build
/doc/html
/doc/doxygen_*.db
*.a
# Temporary files created during CMake build
CMakeCache.txt
CMakeFiles
cmake_install.cmake
CTestTestfile.cmake
Makefile
RapidJSON*.cmake
RapidJSON.pc
Testing
/googletest
install_manifest.txt
Doxyfile
Doxyfile.zh-cn
DartConfiguration.tcl
*.nupkg
# Files created by OS
*.DS_Store
[submodule "thirdparty/gtest"]
path = thirdparty/gtest
url = https://github.com/google/googletest.git
sudo: required
dist: trusty
group: edge
language: cpp
cache:
- ccache
env:
global:
- USE_CCACHE=1
- CCACHE_SLOPPINESS=pch_defines,time_macros
- CCACHE_COMPRESS=1
- CCACHE_MAXSIZE=100M
- ARCH_FLAGS_x86='-m32' # #266: don't use SSE on 32-bit
- ARCH_FLAGS_x86_64='-msse4.2' # use SSE4.2 on 64-bit
- GITHUB_REPO='Tencent/rapidjson'
- secure: "HrsaCb+N66EG1HR+LWH1u51SjaJyRwJEDzqJGYMB7LJ/bfqb9mWKF1fLvZGk46W5t7TVaXRDD5KHFx9DPWvKn4gRUVkwTHEy262ah5ORh8M6n/6VVVajeV/AYt2C0sswdkDBDO4Xq+xy5gdw3G8s1A4Inbm73pUh+6vx+7ltBbk="
before_install:
- sudo apt-add-repository -y ppa:ubuntu-toolchain-r/test
- sudo apt-get update -qq
- sudo apt-get install -y cmake valgrind g++-multilib libc6-dbg:i386
matrix:
include:
# gcc
- env: CONF=release ARCH=x86 CXX11=ON
compiler: gcc
- env: CONF=release ARCH=x86_64 CXX11=ON
compiler: gcc
- env: CONF=debug ARCH=x86 CXX11=OFF
compiler: gcc
- env: CONF=debug ARCH=x86_64 CXX11=OFF
compiler: gcc
# clang
- env: CONF=debug ARCH=x86 CXX11=ON CCACHE_CPP2=yes
compiler: clang
- env: CONF=debug ARCH=x86_64 CXX11=ON CCACHE_CPP2=yes
compiler: clang
- env: CONF=debug ARCH=x86 CXX11=OFF CCACHE_CPP2=yes
compiler: clang
- env: CONF=debug ARCH=x86_64 CXX11=OFF CCACHE_CPP2=yes
compiler: clang
- env: CONF=release ARCH=x86 CXX11=ON CCACHE_CPP2=yes
compiler: clang
- env: CONF=release ARCH=x86_64 CXX11=ON CCACHE_CPP2=yes
compiler: clang
# coverage report
- env: CONF=debug ARCH=x86 CXX11=ON GCOV_FLAGS='--coverage'
compiler: gcc
cache:
- ccache
- pip
after_success:
- pip install --user cpp-coveralls
- coveralls -r .. --gcov-options '\-lp' -e thirdparty -e example -e test -e build/CMakeFiles -e include/rapidjson/msinttypes -e include/rapidjson/internal/meta.h -e include/rapidjson/error/en.h
- env: CONF=debug ARCH=x86_64 GCOV_FLAGS='--coverage'
compiler: gcc
cache:
- ccache
- pip
after_success:
- pip install --user cpp-coveralls
- coveralls -r .. --gcov-options '\-lp' -e thirdparty -e example -e test -e build/CMakeFiles -e include/rapidjson/msinttypes -e include/rapidjson/internal/meta.h -e include/rapidjson/error/en.h
- script: # Documentation task
- cd build
- cmake .. -DRAPIDJSON_HAS_STDSTRING=ON -DCMAKE_VERBOSE_MAKEFILE=ON
- make travis_doc
cache: false
addons:
apt:
packages:
- doxygen
before_script:
- ccache -s
# hack to avoid Valgrind bug (https://bugs.kde.org/show_bug.cgi?id=326469),
# exposed by merging PR#163 (using -march=native)
# TODO: Since this bug is already fixed. Remove this when valgrind can be upgraded.
- sed -i "s/-march=native//" CMakeLists.txt
- mkdir build
script:
- if [ "$CXX" = "clang++" ]; then export CXXFLAGS="-stdlib=libc++ ${CXXFLAGS}"; fi
- >
eval "ARCH_FLAGS=\${ARCH_FLAGS_${ARCH}}" ;
(cd build && cmake
-DRAPIDJSON_HAS_STDSTRING=ON
-DRAPIDJSON_BUILD_CXX11=$CXX11
-DCMAKE_VERBOSE_MAKEFILE=ON
-DCMAKE_BUILD_TYPE=$CONF
-DCMAKE_CXX_FLAGS="$ARCH_FLAGS $GCOV_FLAGS"
-DCMAKE_EXE_LINKER_FLAGS=$GCOV_FLAGS
..)
- cd build
- make tests -j 2
- make examples -j 2
- ctest -j 2 -V `[ "$CONF" = "release" ] || echo "-E perftest"`
# Change Log
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased]
## 1.1.0 - 2016-08-25
### Added
* Add GenericDocument ctor overload to specify JSON type (#369)
* Add FAQ (#372, #373, #374, #376)
* Add forward declaration header `fwd.h`
* Add @PlatformIO Library Registry manifest file (#400)
* Implement assignment operator for BigInteger (#404)
* Add comments support (#443)
* Adding coapp definition (#460)
* documenttest.cpp: EXPECT_THROW when checking empty allocator (470)
* GenericDocument: add implicit conversion to ParseResult (#480)
* Use <wchar.h> with C++ linkage on Windows ARM (#485)
* Detect little endian for Microsoft ARM targets
* Check Nan/Inf when writing a double (#510)
* Add JSON Schema Implementation (#522)
* Add iostream wrapper (#530)
* Add Jsonx example for converting JSON into JSONx (a XML format) (#531)
* Add optional unresolvedTokenIndex parameter to Pointer::Get() (#532)
* Add encoding validation option for Writer/PrettyWriter (#534)
* Add Writer::SetMaxDecimalPlaces() (#536)
* Support {0, } and {0, m} in Regex (#539)
* Add Value::Get/SetFloat(), Value::IsLossLessFloat/Double() (#540)
* Add stream position check to reader unit tests (#541)
* Add Templated accessors and range-based for (#542)
* Add (Pretty)Writer::RawValue() (#543)
* Add Document::Parse(std::string), Document::Parse(const char*, size_t length) and related APIs. (#553)
* Add move constructor for GenericSchemaDocument (#554)
* Add VS2010 and VS2015 to AppVeyor CI (#555)
* Add parse-by-parts example (#556, #562)
* Support parse number as string (#564, #589)
* Add kFormatSingleLineArray for PrettyWriter (#577)
* Added optional support for trailing commas (#584)
* Added filterkey and filterkeydom examples (#615)
* Added npm docs (#639)
* Allow options for writing and parsing NaN/Infinity (#641)
* Add std::string overload to PrettyWriter::Key() when RAPIDJSON_HAS_STDSTRING is defined (#698)
### Fixed
* Fix gcc/clang/vc warnings (#350, #394, #397, #444, #447, #473, #515, #582, #589, #595, #667)
* Fix documentation (#482, #511, #550, #557, #614, #635, #660)
* Fix emscripten alignment issue (#535)
* Fix missing allocator to uses of AddMember in document (#365)
* CMake will no longer complain that the minimum CMake version is not specified (#501)
* Make it usable with old VC8 (VS2005) (#383)
* Prohibit C++11 move from Document to Value (#391)
* Try to fix incorrect 64-bit alignment (#419)
* Check return of fwrite to avoid warn_unused_result build failures (#421)
* Fix UB in GenericDocument::ParseStream (#426)
* Keep Document value unchanged on parse error (#439)
* Add missing return statement (#450)
* Fix Document::Parse(const Ch*) for transcoding (#478)
* encodings.h: fix typo in preprocessor condition (#495)
* Custom Microsoft headers are necessary only for Visual Studio 2012 and lower (#559)
* Fix memory leak for invalid regex (26e69ffde95ba4773ab06db6457b78f308716f4b)
* Fix a bug in schema minimum/maximum keywords for 64-bit integer (e7149d665941068ccf8c565e77495521331cf390)
* Fix a crash bug in regex (#605)
* Fix schema "required" keyword cannot handle duplicated keys (#609)
* Fix cmake CMP0054 warning (#612)
* Added missing include guards in istreamwrapper.h and ostreamwrapper.h (#634)
* Fix undefined behaviour (#646)
* Fix buffer overrun using PutN (#673)
* Fix rapidjson::value::Get<std::string>() may returns wrong data (#681)
* Add Flush() for all value types (#689)
* Handle malloc() fail in PoolAllocator (#691)
* Fix builds on x32 platform. #703
### Changed
* Clarify problematic JSON license (#392)
* Move Travis to container based infrastructure (#504, #558)
* Make whitespace array more compact (#513)
* Optimize Writer::WriteString() with SIMD (#544)
* x86-64 48-bit pointer optimization for GenericValue (#546)
* Define RAPIDJSON_HAS_CXX11_RVALUE_REFS directly in clang (#617)
* Make GenericSchemaDocument constructor explicit (#674)
* Optimize FindMember when use std::string (#690)
## [1.0.2] - 2015-05-14
### Added
* Add Value::XXXMember(...) overloads for std::string (#335)
### Fixed
* Include rapidjson.h for all internal/error headers.
* Parsing some numbers incorrectly in full-precision mode (`kFullPrecisionParseFlag`) (#342)
* Fix some numbers parsed incorrectly (#336)
* Fix alignment of 64bit platforms (#328)
* Fix MemoryPoolAllocator::Clear() to clear user-buffer (0691502573f1afd3341073dd24b12c3db20fbde4)
### Changed
* CMakeLists for include as a thirdparty in projects (#334, #337)
* Change Document::ParseStream() to use stack allocator for Reader (ffbe38614732af8e0b3abdc8b50071f386a4a685)
## [1.0.1] - 2015-04-25
### Added
* Changelog following [Keep a CHANGELOG](https://github.com/olivierlacan/keep-a-changelog) suggestions.
### Fixed
* Parsing of some numbers (e.g. "1e-00011111111111") causing assertion (#314).
* Visual C++ 32-bit compilation error in `diyfp.h` (#317).
## [1.0.0] - 2015-04-22
### Added
* 100% [Coverall](https://coveralls.io/r/Tencent/rapidjson?branch=master) coverage.
* Version macros (#311)
### Fixed
* A bug in trimming long number sequence (4824f12efbf01af72b8cb6fc96fae7b097b73015).
* Double quote in unicode escape (#288).
* Negative zero roundtrip (double only) (#289).
* Standardize behavior of `memcpy()` and `malloc()` (0c5c1538dcfc7f160e5a4aa208ddf092c787be5a, #305, 0e8bbe5e3ef375e7f052f556878be0bd79e9062d).
### Removed
* Remove an invalid `Document::ParseInsitu()` API (e7f1c6dd08b522cfcf9aed58a333bd9a0c0ccbeb).
## 1.0-beta - 2015-04-8
### Added
* RFC 7159 (#101)
* Optional Iterative Parser (#76)
* Deep-copy values (#20)
* Error code and message (#27)
* ASCII Encoding (#70)
* `kParseStopWhenDoneFlag` (#83)
* `kParseFullPrecisionFlag` (881c91d696f06b7f302af6d04ec14dd08db66ceb)
* Add `Key()` to handler concept (#134)
* C++11 compatibility and support (#128)
* Optimized number-to-string and vice versa conversions (#137, #80)
* Short-String Optimization (#131)
* Local stream optimization by traits (#32)
* Travis & Appveyor Continuous Integration, with Valgrind verification (#24, #242)
* Redo all documentation (English, Simplified Chinese)
### Changed
* Copyright ownership transferred to THL A29 Limited (a Tencent company).
* Migrating from Premake to CMAKE (#192)
* Resolve all warning reports
### Removed
* Remove other JSON libraries for performance comparison (#180)
## 0.11 - 2012-11-16
## 0.1 - 2011-11-18
[Unreleased]: https://github.com/Tencent/rapidjson/compare/v1.1.0...HEAD
[1.1.0]: https://github.com/Tencent/rapidjson/compare/v1.0.2...v1.1.0
[1.0.2]: https://github.com/Tencent/rapidjson/compare/v1.0.1...v1.0.2
[1.0.1]: https://github.com/Tencent/rapidjson/compare/v1.0.0...v1.0.1
[1.0.0]: https://github.com/Tencent/rapidjson/compare/v1.0-beta...v1.0.0
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
if(POLICY CMP0025)
# detect Apple's Clang
cmake_policy(SET CMP0025 NEW)
endif()
if(POLICY CMP0054)
cmake_policy(SET CMP0054 NEW)
endif()
SET(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules)
PROJECT(RapidJSON CXX)
set(LIB_MAJOR_VERSION "1")
set(LIB_MINOR_VERSION "1")
set(LIB_PATCH_VERSION "0")
set(LIB_VERSION_STRING "${LIB_MAJOR_VERSION}.${LIB_MINOR_VERSION}.${LIB_PATCH_VERSION}")
# compile in release with debug info mode by default
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE)
endif()
# Build all binaries in a separate directory
SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
option(RAPIDJSON_BUILD_DOC "Build rapidjson documentation." ON)
option(RAPIDJSON_BUILD_EXAMPLES "Build rapidjson examples." ON)
option(RAPIDJSON_BUILD_TESTS "Build rapidjson perftests and unittests." ON)
option(RAPIDJSON_BUILD_THIRDPARTY_GTEST
"Use gtest installation in `thirdparty/gtest` by default if available" OFF)
option(RAPIDJSON_BUILD_CXX11 "Build rapidjson with C++11 (gcc/clang)" ON)
if(RAPIDJSON_BUILD_CXX11)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
endif()
option(RAPIDJSON_BUILD_ASAN "Build rapidjson with address sanitizer (gcc/clang)" OFF)
option(RAPIDJSON_BUILD_UBSAN "Build rapidjson with undefined behavior sanitizer (gcc/clang)" OFF)
option(RAPIDJSON_ENABLE_INSTRUMENTATION_OPT "Build rapidjson with -march or -mcpu options" ON)
option(RAPIDJSON_HAS_STDSTRING "" OFF)
if(RAPIDJSON_HAS_STDSTRING)
add_definitions(-DRAPIDJSON_HAS_STDSTRING)
endif()
find_program(CCACHE_FOUND ccache)
if(CCACHE_FOUND)
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache)
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache)
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Qunused-arguments -fcolor-diagnostics")
endif()
endif(CCACHE_FOUND)
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
if(RAPIDJSON_ENABLE_INSTRUMENTATION_OPT AND NOT CMAKE_CROSSCOMPILING)
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "powerpc" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "ppc64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "ppc64le")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcpu=native")
else()
#FIXME: x86 is -march=native, but doesn't mean every arch is this option. To keep original project's compatibility, I leave this except POWER.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native")
endif()
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Werror")
set(EXTRA_CXX_FLAGS -Weffc++ -Wswitch-default -Wfloat-equal -Wconversion -Wsign-conversion)
if (RAPIDJSON_BUILD_CXX11 AND CMAKE_VERSION VERSION_LESS 3.1)
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.7.0")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
endif()
endif()
if (RAPIDJSON_BUILD_ASAN)
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.8.0")
message(FATAL_ERROR "GCC < 4.8 doesn't support the address sanitizer")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address")
endif()
endif()
if (RAPIDJSON_BUILD_UBSAN)
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.9.0")
message(FATAL_ERROR "GCC < 4.9 doesn't support the undefined behavior sanitizer")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined")
endif()
endif()
elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
if(NOT CMAKE_CROSSCOMPILING)
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "powerpc" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "ppc64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "ppc64le")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcpu=native")
else()
#FIXME: x86 is -march=native, but doesn't mean every arch is this option. To keep original project's compatibility, I leave this except POWER.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native")
endif()
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Werror -Wno-missing-field-initializers")
set(EXTRA_CXX_FLAGS -Weffc++ -Wswitch-default -Wfloat-equal -Wconversion -Wimplicit-fallthrough)
if (RAPIDJSON_BUILD_CXX11 AND CMAKE_VERSION VERSION_LESS 3.1)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
endif()
if (RAPIDJSON_BUILD_ASAN)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address")
endif()
if (RAPIDJSON_BUILD_UBSAN)
if (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined-trap -fsanitize-undefined-trap-on-error")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined")
endif()
endif()
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
add_definitions(-D_CRT_SECURE_NO_WARNINGS=1)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc")
elseif (CMAKE_CXX_COMPILER_ID MATCHES "XL")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -qarch=auto")
endif()
#add extra search paths for libraries and includes
SET(INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/include" CACHE PATH "The directory the headers are installed in")
SET(LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib" CACHE STRING "Directory where lib will install")
SET(DOC_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/share/doc/${PROJECT_NAME}" CACHE PATH "Path to the documentation")
IF(UNIX OR CYGWIN)
SET(_CMAKE_INSTALL_DIR "${LIB_INSTALL_DIR}/cmake/${PROJECT_NAME}")
ELSEIF(WIN32)
SET(_CMAKE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/cmake")
ENDIF()
SET(CMAKE_INSTALL_DIR "${_CMAKE_INSTALL_DIR}" CACHE PATH "The directory cmake files are installed in")
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
if(RAPIDJSON_BUILD_DOC)
add_subdirectory(doc)
endif()
add_custom_target(travis_doc)
add_custom_command(TARGET travis_doc
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/travis-doxygen.sh)
if(RAPIDJSON_BUILD_EXAMPLES)
add_subdirectory(example)
endif()
if(RAPIDJSON_BUILD_TESTS)
if(MSVC11)
# required for VS2012 due to missing support for variadic templates
add_definitions(-D_VARIADIC_MAX=10)
endif(MSVC11)
add_subdirectory(test)
include(CTest)
endif()
# pkg-config
IF (UNIX OR CYGWIN)
CONFIGURE_FILE (${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}.pc.in
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc
@ONLY)
INSTALL (FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc
DESTINATION "${LIB_INSTALL_DIR}/pkgconfig"
COMPONENT pkgconfig)
ENDIF()
install(FILES readme.md
DESTINATION "${DOC_INSTALL_DIR}"
COMPONENT doc)
install(DIRECTORY include/rapidjson
DESTINATION "${INCLUDE_INSTALL_DIR}"
COMPONENT dev)
install(DIRECTORY example/
DESTINATION "${DOC_INSTALL_DIR}/examples"
COMPONENT examples
# Following patterns are for excluding the intermediate/object files
# from an install of in-source CMake build.
PATTERN "CMakeFiles" EXCLUDE
PATTERN "Makefile" EXCLUDE
PATTERN "cmake_install.cmake" EXCLUDE)
# Provide config and version files to be used by other applications
# ===============================
################################################################################
# Export package for use from the build tree
EXPORT( PACKAGE ${PROJECT_NAME} )
# Create the RapidJSONConfig.cmake file for other cmake projects.
# ... for the build tree
SET( CONFIG_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
SET( CONFIG_DIR ${CMAKE_CURRENT_BINARY_DIR})
SET( ${PROJECT_NAME}_INCLUDE_DIR "\${${PROJECT_NAME}_SOURCE_DIR}/include" )
CONFIGURE_FILE( ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}Config.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake @ONLY )
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}ConfigVersion.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake @ONLY)
# ... for the install tree
SET( CMAKECONFIG_INSTALL_DIR ${LIB_INSTALL_DIR}/cmake/${PROJECT_NAME} )
FILE( RELATIVE_PATH REL_INCLUDE_DIR
"${CMAKECONFIG_INSTALL_DIR}"
"${CMAKE_INSTALL_PREFIX}/include" )
SET( ${PROJECT_NAME}_INCLUDE_DIR "\${${PROJECT_NAME}_CMAKE_DIR}/${REL_INCLUDE_DIR}" )
SET( CONFIG_SOURCE_DIR )
SET( CONFIG_DIR )
CONFIGURE_FILE( ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}Config.cmake.in
${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${PROJECT_NAME}Config.cmake @ONLY )
INSTALL(FILES "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${PROJECT_NAME}Config.cmake"
DESTINATION ${CMAKECONFIG_INSTALL_DIR} )
# Install files
INSTALL(FILES
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake
DESTINATION "${CMAKE_INSTALL_DIR}"
COMPONENT dev)
SET(GTEST_SEARCH_PATH
"${GTEST_SOURCE_DIR}"
"${CMAKE_CURRENT_LIST_DIR}/../thirdparty/gtest/googletest")
IF(UNIX)
IF(RAPIDJSON_BUILD_THIRDPARTY_GTEST)
LIST(APPEND GTEST_SEARCH_PATH "/usr/src/gtest")
ELSE()
LIST(INSERT GTEST_SEARCH_PATH 1 "/usr/src/gtest")
ENDIF()
ENDIF()
FIND_PATH(GTEST_SOURCE_DIR
NAMES CMakeLists.txt src/gtest_main.cc
PATHS ${GTEST_SEARCH_PATH})
# Debian installs gtest include directory in /usr/include, thus need to look
# for include directory separately from source directory.
FIND_PATH(GTEST_INCLUDE_DIR
NAMES gtest/gtest.h
PATH_SUFFIXES include
HINTS ${GTEST_SOURCE_DIR}
PATHS ${GTEST_SEARCH_PATH})
INCLUDE(FindPackageHandleStandardArgs)
find_package_handle_standard_args(GTestSrc DEFAULT_MSG
GTEST_SOURCE_DIR
GTEST_INCLUDE_DIR)
includedir=@INCLUDE_INSTALL_DIR@
Name: @PROJECT_NAME@
Description: A fast JSON parser/generator for C++ with both SAX/DOM style API
Version: @LIB_VERSION_STRING@
URL: https://github.com/Tencent/rapidjson
Cflags: -I${includedir}
################################################################################
# RapidJSON source dir
set( RapidJSON_SOURCE_DIR "@CONFIG_SOURCE_DIR@")
################################################################################
# RapidJSON build dir
set( RapidJSON_DIR "@CONFIG_DIR@")
################################################################################
# Compute paths
get_filename_component(RapidJSON_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
set( RapidJSON_INCLUDE_DIR "@RapidJSON_INCLUDE_DIR@" )
set( RapidJSON_INCLUDE_DIRS "@RapidJSON_INCLUDE_DIR@" )
message(STATUS "RapidJSON found. Headers: ${RapidJSON_INCLUDE_DIRS}")
SET(PACKAGE_VERSION "@LIB_VERSION_STRING@")
IF (PACKAGE_FIND_VERSION VERSION_EQUAL PACKAGE_VERSION)
SET(PACKAGE_VERSION_EXACT "true")
ENDIF (PACKAGE_FIND_VERSION VERSION_EQUAL PACKAGE_VERSION)
IF (NOT PACKAGE_FIND_VERSION VERSION_GREATER PACKAGE_VERSION)
SET(PACKAGE_VERSION_COMPATIBLE "true")
ELSE (NOT PACKAGE_FIND_VERSION VERSION_GREATER PACKAGE_VERSION)
SET(PACKAGE_VERSION_UNSUITABLE "true")
ENDIF (NOT PACKAGE_FIND_VERSION VERSION_GREATER PACKAGE_VERSION)
version: 1.1.0.{build}
configuration:
- Debug
- Release
environment:
matrix:
# - VS_VERSION: 9 2008
# VS_PLATFORM: win32
# - VS_VERSION: 9 2008
# VS_PLATFORM: x64
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013
VS_VERSION: 10 2010
VS_PLATFORM: win32
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013
VS_VERSION: 10 2010
VS_PLATFORM: x64
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013
VS_VERSION: 11 2012
VS_PLATFORM: win32
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013
VS_VERSION: 11 2012
VS_PLATFORM: x64
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013
VS_VERSION: 12 2013
VS_PLATFORM: win32
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013
VS_VERSION: 12 2013
VS_PLATFORM: x64
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
VS_VERSION: 14 2015
VS_PLATFORM: win32
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
VS_VERSION: 14 2015
VS_PLATFORM: x64
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
VS_VERSION: 15 2017
VS_PLATFORM: win32
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
VS_VERSION: 15 2017
VS_PLATFORM: x64
before_build:
- git submodule update --init --recursive
- cmake -H. -BBuild/VS -G "Visual Studio %VS_VERSION%" -DCMAKE_GENERATOR_PLATFORM=%VS_PLATFORM% -DCMAKE_VERBOSE_MAKEFILE=ON -DBUILD_SHARED_LIBS=true -Wno-dev
build:
project: Build\VS\RapidJSON.sln
parallel: true
verbosity: minimal
test_script:
- cd Build\VS && if %CONFIGURATION%==Debug (ctest --verbose -E perftest --build-config %CONFIGURATION%) else (ctest --verbose --build-config %CONFIGURATION%)
{
"glossary": {
"title": "example glossary",
"GlossDiv": {
"title": "S",
"GlossList": {
"GlossEntry": {
"ID": "SGML",
"SortAs": "SGML",
"GlossTerm": "Standard Generalized Markup Language",
"Acronym": "SGML",
"Abbrev": "ISO 8879:1986",
"GlossDef": {
"para": "A meta-markup language, used to create markup languages such as DocBook.",
"GlossSeeAlso": ["GML", "XML"]
},
"GlossSee": "markup"
}
}
}
}
}
\ No newline at end of file
{"menu": {
"header": "SVG Viewer",
"items": [
{"id": "Open"},
{"id": "OpenNew", "label": "Open New"},
null,
{"id": "ZoomIn", "label": "Zoom In"},
{"id": "ZoomOut", "label": "Zoom Out"},
{"id": "OriginalView", "label": "Original View"},
null,
{"id": "Quality"},
{"id": "Pause"},
{"id": "Mute"},
null,
{"id": "Find", "label": "Find..."},
{"id": "FindAgain", "label": "Find Again"},
{"id": "Copy"},
{"id": "CopyAgain", "label": "Copy Again"},
{"id": "CopySVG", "label": "Copy SVG"},
{"id": "ViewSVG", "label": "View SVG"},
{"id": "ViewSource", "label": "View Source"},
{"id": "SaveAs", "label": "Save As"},
null,
{"id": "Help"},
{"id": "About", "label": "About Adobe CVG Viewer..."}
]
}}
\ No newline at end of file
sample.json is obtained from http://code.google.com/p/json-test-suite/downloads/detail?name=sample.zip
This diff is collapsed.
{"web-app": {
"servlet": [
{
"servlet-name": "cofaxCDS",
"servlet-class": "org.cofax.cds.CDSServlet",
"init-param": {
"configGlossary:installationAt": "Philadelphia, PA",
"configGlossary:adminEmail": "ksm@pobox.com",
"configGlossary:poweredBy": "Cofax",
"configGlossary:poweredByIcon": "/images/cofax.gif",
"configGlossary:staticPath": "/content/static",
"templateProcessorClass": "org.cofax.WysiwygTemplate",
"templateLoaderClass": "org.cofax.FilesTemplateLoader",
"templatePath": "templates",
"templateOverridePath": "",
"defaultListTemplate": "listTemplate.htm",
"defaultFileTemplate": "articleTemplate.htm",
"useJSP": false,
"jspListTemplate": "listTemplate.jsp",
"jspFileTemplate": "articleTemplate.jsp",
"cachePackageTagsTrack": 200,
"cachePackageTagsStore": 200,
"cachePackageTagsRefresh": 60,
"cacheTemplatesTrack": 100,
"cacheTemplatesStore": 50,
"cacheTemplatesRefresh": 15,
"cachePagesTrack": 200,
"cachePagesStore": 100,
"cachePagesRefresh": 10,
"cachePagesDirtyRead": 10,
"searchEngineListTemplate": "forSearchEnginesList.htm",
"searchEngineFileTemplate": "forSearchEngines.htm",
"searchEngineRobotsDb": "WEB-INF/robots.db",
"useDataStore": true,
"dataStoreClass": "org.cofax.SqlDataStore",
"redirectionClass": "org.cofax.SqlRedirection",
"dataStoreName": "cofax",
"dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver",
"dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon",
"dataStoreUser": "sa",
"dataStorePassword": "dataStoreTestQuery",
"dataStoreTestQuery": "SET NOCOUNT ON;select test='test';",
"dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log",
"dataStoreInitConns": 10,
"dataStoreMaxConns": 100,
"dataStoreConnUsageLimit": 100,
"dataStoreLogLevel": "debug",
"maxUrlLength": 500}},
{
"servlet-name": "cofaxEmail",
"servlet-class": "org.cofax.cds.EmailServlet",
"init-param": {
"mailHost": "mail1",
"mailHostOverride": "mail2"}},
{
"servlet-name": "cofaxAdmin",
"servlet-class": "org.cofax.cds.AdminServlet"},
{
"servlet-name": "fileServlet",
"servlet-class": "org.cofax.cds.FileServlet"},
{
"servlet-name": "cofaxTools",
"servlet-class": "org.cofax.cms.CofaxToolsServlet",
"init-param": {
"templatePath": "toolstemplates/",
"log": 1,
"logLocation": "/usr/local/tomcat/logs/CofaxTools.log",
"logMaxSize": "",
"dataLog": 1,
"dataLogLocation": "/usr/local/tomcat/logs/dataLog.log",
"dataLogMaxSize": "",
"removePageCache": "/content/admin/remove?cache=pages&id=",
"removeTemplateCache": "/content/admin/remove?cache=templates&id=",
"fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder",
"lookInContext": 1,
"adminGroupID": 4,
"betaServer": true}}],
"servlet-mapping": {
"cofaxCDS": "/",
"cofaxEmail": "/cofaxutil/aemail/*",
"cofaxAdmin": "/admin/*",
"fileServlet": "/static/*",
"cofaxTools": "/tools/*"},
"taglib": {
"taglib-uri": "cofax.tld",
"taglib-location": "/WEB-INF/tlds/cofax.tld"}}}
\ No newline at end of file
{"widget": {
"debug": "on",
"window": {
"title": "Sample Konfabulator Widget",
"name": "main_window",
"width": 500,
"height": 500
},
"image": {
"src": "Images/Sun.png",
"name": "sun1",
"hOffset": 250,
"vOffset": 250,
"alignment": "center"
},
"text": {
"data": "Click Here",
"size": 36,
"style": "bold",
"name": "text1",
"hOffset": 250,
"vOffset": 100,
"alignment": "center",
"onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
}
}}
\ No newline at end of file
{
"en":"I can eat glass and it doesn't hurt me.",
"zh-Hant":"我能吞下玻璃而不傷身體。",
"zh-Hans":"我能吞下玻璃而不伤身体。",
"ja":"私はガラスを食べられます。それは私を傷つけません。",
"ko":"나는 유리를 먹을 수 있어요. 그래도 아프지 않아요"
}
\ No newline at end of file
{
"en":"I can eat glass and it doesn't hurt me.",
"zh-Hant":"我能吞下玻璃而不傷身體。",
"zh-Hans":"我能吞下玻璃而不伤身体。",
"ja":"私はガラスを食べられます。それは私を傷つけません。",
"ko":"나는 유리를 먹을 수 있어요. 그래도 아프지 않아요"
}
\ No newline at end of file
"A JSON payload should be an object or array, not a string."
\ No newline at end of file
{"Extra value after close": true} "misplaced quoted value"
\ No newline at end of file
{"Illegal expression": 1 + 2}
\ No newline at end of file
{"Illegal invocation": alert()}
\ No newline at end of file
{"Numbers cannot have leading zeroes": 013}
\ No newline at end of file
{"Numbers cannot be hex": 0x14}
\ No newline at end of file
["Illegal backslash escape: \x15"]
\ No newline at end of file
["Illegal backslash escape: \017"]
\ No newline at end of file
[[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]]
\ No newline at end of file
{"Missing colon" null}
\ No newline at end of file
["Unclosed array"
\ No newline at end of file
{"Double colon":: null}
\ No newline at end of file
{"Comma instead of colon", null}
\ No newline at end of file
["Colon instead of comma": false]
\ No newline at end of file
["Bad value", truth]
\ No newline at end of file
['single quote']
\ No newline at end of file
[" tab character in string "]
\ No newline at end of file
["tab\ character\ in\ string\ "]
\ No newline at end of file
["line
break"]
\ No newline at end of file
["line\
break"]
\ No newline at end of file
{unquoted_key: "keys must be quoted"}
\ No newline at end of file
{"Comma instead if closing brace": true,
\ No newline at end of file
["mismatch"}
\ No newline at end of file
["extra comma",]
\ No newline at end of file
["double extra comma",,]
\ No newline at end of file
[ , "<-- missing value"]
\ No newline at end of file
["Comma after the close"],
\ No newline at end of file
["Extra close"]]
\ No newline at end of file
{"Extra comma": true,}
\ No newline at end of file
[
"JSON Test Pattern pass1",
{"object with 1 member":["array with 1 element"]},
{},
[],
-42,
true,
false,
null,
{
"integer": 1234567890,
"real": -9876.543210,
"e": 0.123456789e-12,
"E": 1.234567890E+34,
"": 23456789012E66,
"zero": 0,
"one": 1,
"space": " ",
"quote": "\"",
"backslash": "\\",
"controls": "\b\f\n\r\t",
"slash": "/ & \/",
"alpha": "abcdefghijklmnopqrstuvwyz",
"ALPHA": "ABCDEFGHIJKLMNOPQRSTUVWYZ",
"digit": "0123456789",
"0123456789": "digit",
"special": "`1~!@#$%^&*()_+-={':[,]}|;.</>?",
"hex": "\u0123\u4567\u89AB\uCDEF\uabcd\uef4A",
"true": true,
"false": false,
"null": null,
"array":[ ],
"object":{ },
"address": "50 St. James Street",
"url": "http://www.JSON.org/",
"comment": "// /* <!-- --",
"# -- --> */": " ",
" s p a c e d " :[1,2 , 3
,
4 , 5 , 6 ,7 ],"compact":[1,2,3,4,5,6,7],
"jsontext": "{\"object with 1 member\":[\"array with 1 element\"]}",
"quotes": "&#34; \u0022 %22 0x22 034 &#x22;",
"\/\\\"\uCAFE\uBABE\uAB98\uFCDE\ubcda\uef4A\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?"
: "A key can be any string"
},
0.5 ,98.6
,
99.44
,
1066,
1e1,
0.1e1,
1e-1,
1e00,2e+00,2e-00
,"rosebud"]
\ No newline at end of file
[[[[[[[[[[[[[[[[[[["Not too deep"]]]]]]]]]]]]]]]]]]]
\ No newline at end of file
{
"JSON Test Pattern pass3": {
"The outermost value": "must be an object or array.",
"In this test": "It is an object."
}
}
Test suite from http://json.org/JSON_checker/.
If the JSON_checker is working correctly, it must accept all of the pass*.json files and reject all of the fail*.json files.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Test data obtained from https://github.com/xpol/lua-rapidjson/tree/master/performance
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment