Compare commits

..

5 Commits

Author SHA1 Message Date
5036e338c7 enable unittest 2020-07-31 22:24:43 -07:00
80fb8cfb59 zlib.h included in ifdefs 2020-07-31 22:19:33 -07:00
43c0ae0812 disable unix unittest for testing windows 2020-07-31 22:14:05 -07:00
dcc447ec4a user agent zlib fix 2020-07-31 22:11:16 -07:00
5127094f0e zlib is optional 2020-07-31 22:07:20 -07:00
269 changed files with 60032 additions and 17168 deletions

View File

@ -1,66 +0,0 @@
name: docker
# When its time to do a release do a build for amd64
# and push all of them to Docker Hub.
# Only trigger on semver shaped tags.
on:
push:
tags:
- "v*.*.*"
jobs:
login:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Prepare
id: prep
run: |
DOCKER_IMAGE=machinezone/ws
VERSION=edge
if [[ $GITHUB_REF == refs/tags/* ]]; then
VERSION=${GITHUB_REF#refs/tags/v}
fi
if [ "${{ github.event_name }}" = "schedule" ]; then
VERSION=nightly
fi
TAGS="${DOCKER_IMAGE}:${VERSION}"
if [[ $VERSION =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
TAGS="$TAGS,${DOCKER_IMAGE}:latest"
fi
echo ::set-output name=tags::${TAGS}
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@master
- name: Cache Docker layers
uses: actions/cache@v2
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-
- name: Login to GitHub Package Registry
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GHCR_TOKEN }}
- name: Build and push
id: docker_build
uses: docker/build-push-action@v2-build-push
with:
builder: ${{ steps.buildx.outputs.name }}
context: .
file: ./Dockerfile
target: prod
platforms: linux/amd64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.prep.outputs.tags }}
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache

View File

@ -1,13 +0,0 @@
name: linux
on:
push:
paths-ignore:
- 'docs/**'
jobs:
linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: make test_make
run: make test_make

View File

@ -1,15 +0,0 @@
name: mac_tsan_mbedtls
on:
push:
paths-ignore:
- 'docs/**'
jobs:
mac_tsan_mbedtls:
runs-on: macOS-latest
steps:
- uses: actions/checkout@v1
- name: install mbedtls
run: brew install mbedtls
- name: make test
run: make test_tsan_mbedtls

View File

@ -1,15 +0,0 @@
name: mac_tsan_openssl
on:
push:
paths-ignore:
- 'docs/**'
jobs:
mac_tsan_openssl:
runs-on: macOS-latest
steps:
- uses: actions/checkout@v1
- name: install openssl
run: brew install openssl@1.1
- name: make test
run: make test_tsan_openssl

View File

@ -1,13 +0,0 @@
name: mac_tsan_sectransport
on:
push:
paths-ignore:
- 'docs/**'
jobs:
mac_tsan_sectransport:
runs-on: macOS-latest
steps:
- uses: actions/checkout@v1
- name: make test_tsan
run: make test_tsan

View File

@ -1,38 +0,0 @@
name: uwp
on:
push:
paths-ignore:
- 'docs/**'
jobs:
uwp:
runs-on: windows-latest
steps:
- uses: actions/checkout@v1
- uses: seanmiddleditch/gha-setup-vsdevenv@master
- run: |
mkdir build
cd build
cmake -DCMAKE_TOOLCHAIN_FILE=c:/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION="10.0" -DCMAKE_CXX_COMPILER=cl.exe -DUSE_TEST=1 -DUSE_ZLIB=0 ..
- run: cmake --build build
#
# Windows with OpenSSL is working but disabled as it takes 13 minutes (10 for openssl) to build with vcpkg
#
# windows_openssl:
# runs-on: windows-latest
# steps:
# - uses: actions/checkout@v1
# - uses: seanmiddleditch/gha-setup-vsdevenv@master
# - run: |
# vcpkg install zlib:x64-windows
# vcpkg install openssl:x64-windows
# - run: |
# mkdir build
# cd build
# cmake -DCMAKE_TOOLCHAIN_FILE=c:/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_CXX_COMPILER=cl.exe -DUSE_OPEN_SSL=1 -DUSE_TLS=1 -DUSE_WS=1 -DUSE_TEST=1 ..
# - run: cmake --build build
#
# # Running the unittest does not work, the binary cannot be found
# #- run: ../build/test/ixwebsocket_unittest.exe
# # working-directory: test

View File

@ -15,6 +15,5 @@ jobs:
cd build
cmake -DCMAKE_CXX_COMPILER=cl.exe -DUSE_WS=1 -DUSE_TEST=1 -DUSE_ZLIB=0 ..
- run: cmake --build build
#- run: ../build/test/ixwebsocket_unittest.exe
# working-directory: test
- run: ../build/test/ixwebsocket_unittest.exe
working-directory: test

View File

@ -1,19 +0,0 @@
# Find package structure taken from libcurl
include(FindPackageHandleStandardArgs)
find_path(DEFLATE_INCLUDE_DIRS libdeflate.h)
find_library(DEFLATE_LIBRARY deflate)
find_package_handle_standard_args(DEFLATE
FOUND_VAR
DEFLATE_FOUND
REQUIRED_VARS
DEFLATE_LIBRARY
DEFLATE_INCLUDE_DIRS
FAIL_MESSAGE
"Could NOT find deflate"
)
set(DEFLATE_INCLUDE_DIRS ${DEFLATE_INCLUDE_DIRS})
set(DEFLATE_LIBRARIES ${DEFLATE_LIBRARY})

View File

@ -31,7 +31,6 @@ set( IXWEBSOCKET_SOURCES
ixwebsocket/IXDNSLookup.cpp
ixwebsocket/IXExponentialBackoff.cpp
ixwebsocket/IXGetFreePort.cpp
ixwebsocket/IXGzipCodec.cpp
ixwebsocket/IXHttp.cpp
ixwebsocket/IXHttpClient.cpp
ixwebsocket/IXHttpServer.cpp
@ -63,11 +62,11 @@ set( IXWEBSOCKET_SOURCES
set( IXWEBSOCKET_HEADERS
ixwebsocket/IXBench.h
ixwebsocket/IXCancellationRequest.h
ixwebsocket/IXConnectionInfo.h
ixwebsocket/IXConnectionState.h
ixwebsocket/IXDNSLookup.h
ixwebsocket/IXExponentialBackoff.h
ixwebsocket/IXGetFreePort.h
ixwebsocket/IXGzipCodec.h
ixwebsocket/IXHttp.h
ixwebsocket/IXHttpClient.h
ixwebsocket/IXHttpServer.h
@ -195,19 +194,13 @@ option(USE_ZLIB "Enable zlib support" TRUE)
if (USE_ZLIB)
# Use ZLIB_ROOT CMake variable if you need to use your own zlib
find_package(ZLIB REQUIRED)
include_directories(${ZLIB_INCLUDE_DIRS})
target_link_libraries(ixwebsocket ${ZLIB_LIBRARIES})
find_package(ZLIB)
if (ZLIB_FOUND)
include_directories(${ZLIB_INCLUDE_DIRS})
target_link_libraries(ixwebsocket ${ZLIB_LIBRARIES})
target_compile_definitions(ixwebsocket PUBLIC IXWEBSOCKET_USE_ZLIB)
endif()
# brew install libdeflate
find_package(DEFLATE)
if (DEFLATE_FOUND)
include_directories(${DEFLATE_INCLUDE_DIRS})
target_link_libraries(ixwebsocket ${DEFLATE_LIBRARIES})
target_compile_definitions(ixwebsocket PUBLIC IXWEBSOCKET_USE_DEFLATE)
target_compile_definitions(ixwebsocket PUBLIC IXWEBSOCKET_USE_ZLIB)
endif()
endif()
if (WIN32)
@ -261,16 +254,7 @@ if (USE_WS OR USE_TEST)
add_subdirectory(ixsentry)
add_subdirectory(ixbots)
include(FetchContent)
FetchContent_Declare(spdlog
GIT_REPOSITORY "https://github.com/gabime/spdlog"
GIT_TAG "v1.8.0")
FetchContent_Declare(jsoncpp
GIT_REPOSITORY "https://github.com/open-source-parsers/jsoncpp"
GIT_TAG "1.9.4")
FetchContent_MakeAvailable(spdlog jsoncpp)
add_subdirectory(third_party/spdlog spdlog)
if (USE_WS)
add_subdirectory(ws)

View File

@ -2,7 +2,7 @@
IXWebSocket is a C++ library for WebSocket client and server development. It has minimal dependencies (no boost), is very simple to use and support everything you'll likely need for websocket dev (SSL, deflate compression, compiles on most platforms, etc...). HTTP client and server code is also available, but it hasn't received as much testing.
It is been used on big mobile video game titles sending and receiving tons of messages since 2017 (iOS and Android). It was tested on macOS, iOS, Linux, Android, Windows and FreeBSD. Note that the MinGW compiler is not supported at this point. Two important design goals are simplicity and correctness.
It is been used on big mobile video game titles sending and receiving tons of messages since 2017 (iOS and Android). It was tested on macOS, iOS, Linux, Android, Windows and FreeBSD. Two important design goals are simplicity and correctness.
```cpp
/*

View File

@ -1,11 +1,67 @@
version: "3.3"
version: "3"
services:
push:
entrypoint: ws push_server --host 0.0.0.0
image: ${DOCKER_REPO}/ws:build
# snake:
# image: bsergean/ws:build
# entrypoint: ws snake --port 8767 --host 0.0.0.0 --redis_hosts redis1
# ports:
# - "8767:8767"
# networks:
# - ws-net
# depends_on:
# - redis1
autoroute:
entrypoint: ws autoroute ws://push:8008
image: ${DOCKER_REPO}/ws:build
depends_on:
- push
# proxy:
# image: bsergean/ws:build
# entrypoint: strace ws proxy_server --remote_host 'wss://cobra.addsrv.com' --host 0.0.0.0 --port 8765 -v
# ports:
# - "8765:8765"
# networks:
# - ws-net
#pyproxy:
# image: bsergean/ws_proxy:build
# entrypoint: /usr/bin/ws_proxy.py --remote_url 'wss://cobra.addsrv.com' --host 0.0.0.0 --port 8765
# ports:
# - "8765:8765"
# networks:
# - ws-net
# # ws:
# # security_opt:
# # - seccomp:unconfined
# # cap_add:
# # - SYS_PTRACE
# # stdin_open: true
# # tty: true
# # image: bsergean/ws:build
# # entrypoint: sh
# # networks:
# # - ws-net
# # depends_on:
# # - redis1
# #
# # redis1:
# # image: redis:alpine
# # networks:
# # - ws-net
# #
# # statsd:
# # image: jaconel/statsd
# # ports:
# # - "8125:8125"
# # environment:
# # - STATSD_DUMP_MSG=true
# # - GRAPHITE_HOST=127.0.0.1
# # networks:
# # - ws-net
compile:
image: alpine
entrypoint: sh
stdin_open: true
tty: true
volumes:
- /Users/bsergeant/src/foss:/home/bsergean/src/foss
networks:
ws-net:

View File

@ -2,7 +2,7 @@ FROM alpine:3.12 as build
RUN apk add --no-cache \
gcc g++ musl-dev linux-headers \
cmake mbedtls-dev make zlib-dev python3-dev ninja git
cmake mbedtls-dev make zlib-dev python3-dev ninja
RUN addgroup -S app && \
adduser -S -G app app && \
@ -20,7 +20,7 @@ RUN make ws_mbedtls_install && \
FROM alpine:3.12 as runtime
RUN apk add --no-cache libstdc++ mbedtls ca-certificates python3 strace && \
RUN apk add --no-cache libstdc++ mbedtls ca-certificates python3 && \
addgroup -S app && \
adduser -S -G app app

View File

@ -1,139 +1,6 @@
# Changelog
All changes to this project will be documented in this file.
## [10.4.7] - 2020-09-28
(ws) add gzip and gunzip ws sub commands
## [10.4.6] - 2020-09-26
(cmake) use FetchContent cmake module to retrieve jsoncpp third party dependency
## [10.4.5] - 2020-09-26
(cmake) use FetchContent cmake module to retrieve spdlog third party dependency
## [10.4.4] - 2020-09-22
(cobra connection) retrieve cobra server connection id from the cobra handshake message and display it in ws clients, metrics publisher and bots
## [10.4.3] - 2020-09-22
(cobra 2 cobra) specify as an HTTP header which channel we will republish to
## [10.4.2] - 2020-09-18
(cobra bots) change an error log to a warning log when reconnecting because no messages were received for a minute
## [10.4.1] - 2020-09-18
(cobra connection and bots) set an HTTP header when connecting to help with debugging bots
## [10.4.0] - 2020-09-12
(http server) read body request when the Content-Length is specified + set timeout to read the request to 30 seconds max by default, and make it configurable as a constructor parameter
## [10.3.5] - 2020-09-09
(ws) autoroute command exit on its own once all messages have been received
## [10.3.4] - 2020-09-04
(docker) ws docker file installs strace
## [10.3.3] - 2020-09-02
(ws) echo_client command renamed to autoroute. Command exit once the server close the connection. push_server commands exit once N messages have been sent.
## [10.3.2] - 2020-08-31
(ws + cobra bots) add a cobra_to_cobra ws subcommand to subscribe to a channel and republish received events to a different channel
## [10.3.1] - 2020-08-28
(socket servers) merge the ConnectionInfo class with the ConnectionState one, which simplify all the server apis
## [10.3.0] - 2020-08-26
(ws) set the main thread name, to help with debugging in XCode, gdb, lldb etc...
## [10.2.9] - 2020-08-19
(ws) cobra to python bot / take a module python name as argument foo.bar.baz instead of a path foo/bar/baz.py
## [10.2.8] - 2020-08-19
(ws) on Linux with mbedtls, when the system ca certs are specified (the default) pick up sensible OS supplied paths (tested with CentOS and Alpine)
## [10.2.7] - 2020-08-18
(ws push_server) on the server side, stop sending and close the connection when the remote end has disconnected
## [10.2.6] - 2020-08-17
(ixwebsocket) replace std::unique_ptr<unsigned char[]> with std::array for some fixed arrays (which are in C++11)
## [10.2.5] - 2020-08-15
(ws) merge all ws_*.cpp files into a single one to speedup compilation
## [10.2.4] - 2020-08-15
(socket server) in the loop accepting connections, call select without a timeout on unix to avoid busy looping, and only wake up when a new connection happens
## [10.2.3] - 2020-08-15
(socket server) instead of busy looping with a sleep, only wake up the GC thread when a new thread will have to be joined, (we know that thanks to the ConnectionState OnSetTerminated callback
## [10.2.2] - 2020-08-15
(socket server) add a callback to the ConnectionState to be invoked when the connection is terminated. This will be used by the SocketServer in the future to know on time that the associated connection thread can be terminated.
## [10.2.1] - 2020-08-15
(socket server) do not create a select interrupt object everytime when polling for notifications while waiting for new connections, instead use a persistent one which is a member variable
## [10.2.0] - 2020-08-14
(ixwebsocket client) handle HTTP redirects
## [10.2.0] - 2020-08-13
(ws) upgrade to latest version of nlohmann json (3.9.1 from 3.2.0)
## [10.1.9] - 2020-08-13
(websocket proxy server) add ability to map different hosts to different websocket servers, using a json config file
## [10.1.8] - 2020-08-12
(ws) on macOS, with OpenSSL or MbedTLS, use /etc/ssl/cert.pem as the system certs
## [10.1.7] - 2020-08-11
(ws) -q option imply info log level, not warning log level
## [10.1.6] - 2020-08-06
(websocket server) Handle programmer error when the server callback is not registered properly (fix #227)
## [10.1.5] - 2020-08-02
(ws) Add a new ws sub-command, push_server. This command runs a server which sends many messages in a loop to a websocket client. We can receive above 200,000 messages per second (cf #235).
## [10.1.4] - 2020-08-02
(ws) Add a new ws sub-command, echo_client. This command sends a message to an echo server, and send back to a server whatever message it does receive. When connecting to a local ws echo_server, on my MacBook Pro 2015 I can send/receive around 30,000 messages per second. (cf #235)
## [10.1.3] - 2020-08-02
(ws) ws echo_server. Add a -q option to only enable warning and error log levels. This is useful for bench-marking so that we do not print a lot of things on the console. (cf #235)
## [10.1.2] - 2020-07-31
(build) make using zlib optional, with the caveat that some http and websocket features are not available when zlib is absent
## [10.1.1] - 2020-07-29
(websocket client) onProgressCallback not called for short messages on a websocket (fix #233)

View File

@ -17,7 +17,6 @@ There is a unittest which can be executed by typing `make test`.
Options for building:
* `-DUSE_ZLIB=1` will enable zlib support, required for http client + server + websocket per message deflate extension
* `-DUSE_TLS=1` will enable TLS support
* `-DUSE_OPEN_SSL=1` will use [openssl](https://www.openssl.org/) for the TLS support (default on Linux and Windows)
* `-DUSE_MBED_TLS=1` will use [mbedlts](https://tls.mbed.org/) for the TLS support

View File

@ -1,37 +0,0 @@
## WebSocket Client performance
We will run a client and a server on the same machine, connecting to localhost. This bench is run on a MacBook Pro from 2015. We can receive over 200,000 (small) messages per second, another way to put it is that it takes 5 micro-second to receive and process one message. This is an indication about the minimal latency to receive messages.
### Receiving messages
By using the push_server ws sub-command, the server will send the same message in a loop to any connected client.
```
ws push_server -q --send_msg 'yo'
```
By using the echo_client ws sub-command, with the -m (mute or no_send), we will display statistics on how many messages we can receive per second.
```
$ ws echo_client -m ws://localhost:8008
[2020-08-02 12:31:17.284] [info] ws_echo_client: connected
[2020-08-02 12:31:17.284] [info] Uri: /
[2020-08-02 12:31:17.284] [info] Headers:
[2020-08-02 12:31:17.284] [info] Connection: Upgrade
[2020-08-02 12:31:17.284] [info] Sec-WebSocket-Accept: byy/pMK2d0PtRwExaaiOnXJTQHo=
[2020-08-02 12:31:17.284] [info] Server: ixwebsocket/10.1.4 macos ssl/SecureTransport zlib 1.2.11
[2020-08-02 12:31:17.284] [info] Upgrade: websocket
[2020-08-02 12:31:17.663] [info] messages received: 0 per second 2595307 total
[2020-08-02 12:31:18.668] [info] messages received: 79679 per second 2674986 total
[2020-08-02 12:31:19.668] [info] messages received: 207438 per second 2882424 total
[2020-08-02 12:31:20.673] [info] messages received: 209207 per second 3091631 total
[2020-08-02 12:31:21.676] [info] messages received: 216056 per second 3307687 total
[2020-08-02 12:31:22.680] [info] messages received: 214927 per second 3522614 total
[2020-08-02 12:31:23.684] [info] messages received: 216960 per second 3739574 total
[2020-08-02 12:31:24.688] [info] messages received: 215232 per second 3954806 total
[2020-08-02 12:31:25.691] [info] messages received: 212300 per second 4167106 total
[2020-08-02 12:31:26.694] [info] messages received: 212501 per second 4379607 total
[2020-08-02 12:31:27.699] [info] messages received: 212330 per second 4591937 total
[2020-08-02 12:31:28.702] [info] messages received: 216511 per second 4808448 total
```

View File

@ -67,28 +67,9 @@ webSocket.stop()
### Sending messages
`WebSocketSendInfo result = websocket.send("foo")` will send a message.
`websocket.send("foo")` will send a message.
If the connection was closed, sending will fail, and the success field of the result object will be set to false. There could also be a compression error in which case the compressError field will be set to true. The payloadSize field and wireSize fields will tell you respectively how much bytes the message weight, and how many bytes were sent on the wire (potentially compressed + counting the message header (a few bytes).
There is an optional progress callback that can be passed in as the second argument. If a message is large it will be fragmented into chunks which will be sent independantly. Everytime the we can write a fragment into the OS network cache, the callback will be invoked. If a user wants to cancel a slow send, false should be returned from within the callback.
Here is an example code snippet copied from the ws send sub-command. Each fragment weights 32K, so the total integer is the wireSize divided by 32K. As an example if you are sending 32M of data, uncompressed, total will be 1000. current will be set to 0 for the first fragment, then 1, 2 etc...
```
auto result =
_webSocket.sendBinary(serializedMsg, [this, throttle](int current, int total) -> bool {
spdlog::info("ws_send: Step {} out of {}", current + 1, total);
if (throttle)
{
std::chrono::duration<double, std::milli> duration(10);
std::this_thread::sleep_for(duration);
}
return _connected;
});
```
If the connection was closed and sending failed, the return value will be set to false.
### ReadyState
@ -280,9 +261,10 @@ ix::WebSocketServer server(port);
server.setOnConnectionCallback(
[&server](std::weak_ptr<WebSocket> webSocket,
std::shared_ptr<ConnectionState> connectionState)
std::shared_ptr<ConnectionState> connectionState,
std::unique_ptr<ConnectionInfo> connectionInfo)
{
std::cout << "Remote ip: " << connectionState->remoteIp << std::endl;
std::cout << "Remote ip: " << connectionInfo->remoteIp << std::endl;
auto ws = webSocket.lock();
if (ws)
@ -358,12 +340,13 @@ The webSocket reference is guaranteed to be always valid ; by design the callbac
ix::WebSocketServer server(port);
server.setOnClientMessageCallback(std::shared_ptr<ConnectionState> connectionState,
ConnectionInfo& connectionInfo,
WebSocket& webSocket,
const WebSocketMessagePtr& msg)
{
// The ConnectionState object contains information about the connection,
// The ConnectionInfo object contains information about the connection,
// at this point only the client ip address and the port.
std::cout << "Remote ip: " << connectionState->getRemoteIp();
std::cout << "Remote ip: " << connectionInfo.remoteIp << std::endl;
if (msg->type == ix::WebSocketMessageType::Open)
{
@ -469,7 +452,7 @@ out = httpClient.post(url, std::string("foo=bar"), args);
auto statusCode = response->statusCode; // Can be HttpErrorCode::Ok, HttpErrorCode::UrlMalformed, etc...
auto errorCode = response->errorCode; // 200, 404, etc...
auto responseHeaders = response->headers; // All the headers in a special case-insensitive unordered_map of (string, string)
auto body = response->body; // All the bytes from the response as an std::string
auto payload = response->payload; // All the bytes from the response as an std::string
auto errorMsg = response->errorMsg; // Descriptive error message in case of failure
auto uploadSize = response->uploadSize; // Byte count of uploaded data
auto downloadSize = response->downloadSize; // Byte count of downloaded data
@ -517,11 +500,12 @@ If you want to handle how requests are processed, implement the setOnConnectionC
```cpp
setOnConnectionCallback(
[this](HttpRequestPtr request,
std::shared_ptr<ConnectionState> connectionState) -> HttpResponsePtr
std::shared_ptr<ConnectionState> /*connectionState*/,
std::unique_ptr<ConnectionInfo> connectionInfo) -> HttpResponsePtr
{
// Build a string for the response
std::stringstream ss;
ss << connectionState->getRemoteIp();
ss << connectionInfo->remoteIp
<< " "
<< request->method
<< " "

View File

@ -204,24 +204,6 @@ Listening on 127.0.0.1:8008
If you connect to ws://127.0.0.1:8008, the proxy will connect to ws://127.0.0.1:9000 and pass all traffic to this server.
You can also use a more complex setup if you want to redirect to different websocket servers based on the hostname your client is trying to connect to. If you have multiple CNAME aliases that point to the same server.
A JSON config file is used to express that mapping ; here connecting to echo.jeanserge.com will proxy the client to ws://localhost:8008 on the local machine (which actually runs ws echo_server), while connecting to bavarde.jeanserge.com will proxy the client to ws://localhost:5678 where a cobra python server is running. As a side note you will need a wildcard SSL certificate if you want to have SSL enabled on that machine.
```json
{
"remote_urls": {
"echo.jeanserge.com": "ws://localhost:8008",
"bavarde.jeanserge.com": "ws://localhost:5678"
}
}
```
The --config_path option is required to instruct ws proxy_server to read that file.
```
ws proxy_server --config_path proxyConfig.json --port 8765
```
## File transfer
```

View File

@ -5,7 +5,6 @@
set (IXBOTS_SOURCES
ixbots/IXCobraBot.cpp
ixbots/IXCobraToCobraBot.cpp
ixbots/IXCobraToSentryBot.cpp
ixbots/IXCobraToStatsdBot.cpp
ixbots/IXCobraToStdoutBot.cpp
@ -17,7 +16,6 @@ set (IXBOTS_SOURCES
set (IXBOTS_HEADERS
ixbots/IXCobraBot.h
ixbots/IXCobraBotConfig.h
ixbots/IXCobraToCobraBot.h
ixbots/IXCobraToSentryBot.h
ixbots/IXCobraToStatsdBot.h
ixbots/IXCobraToStdoutBot.h
@ -31,6 +29,11 @@ add_library(ixbots STATIC
${IXBOTS_HEADERS}
)
find_package(JsonCpp)
if (NOT JSONCPP_FOUND)
set(JSONCPP_INCLUDE_DIRS ../third_party/jsoncpp)
endif()
if (USE_PYTHON)
target_compile_definitions(ixbots PUBLIC IXBOTS_USE_PYTHON)
find_package(Python COMPONENTS Development)
@ -43,11 +46,12 @@ set(IXBOTS_INCLUDE_DIRS
../ixwebsocket
../ixcobra
../ixredis
../ixsentry)
../ixsentry
${JSONCPP_INCLUDE_DIRS}
${SPDLOG_INCLUDE_DIRS})
if (USE_PYTHON)
set(IXBOTS_INCLUDE_DIRS ${IXBOTS_INCLUDE_DIRS} ${Python_INCLUDE_DIRS})
endif()
target_include_directories( ixbots PUBLIC ${IXBOTS_INCLUDE_DIRS} )
target_link_libraries( ixbots jsoncpp_static )

View File

@ -31,8 +31,6 @@ namespace ix
auto limitReceivedEvents = botConfig.limitReceivedEvents;
auto batchSize = botConfig.batchSize;
config.headers["X-Cobra-Channel"] = channel;
ix::CobraConnection conn;
conn.configure(config);
conn.connect();
@ -130,7 +128,7 @@ namespace ix
ss << "no messages received or sent for "
<< heartBeatTimeout << " seconds, reconnecting";
CoreLogger::warn(ss.str());
CoreLogger::error(ss.str());
stalledConnection = true;
}
state = currentState;
@ -170,11 +168,7 @@ namespace ix
}
else if (event->type == ix::CobraEventType::Closed)
{
CoreLogger::info("Subscriber closed: " + event->errMsg);
}
else if (event->type == ix::CobraEventType::Handshake)
{
CoreLogger::info("Subscriber: Cobra handshake connection id: " + event->connectionId);
CoreLogger::info("Subscriber closed: {}" + event->errMsg);
}
else if (event->type == ix::CobraEventType::Authenticated)
{

View File

@ -1,45 +0,0 @@
/*
* IXCobraToCobraBot.cpp
* Author: Benjamin Sergeant
* Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
*/
#include "IXCobraToCobraBot.h"
#include "IXCobraBot.h"
#include <ixcobra/IXCobraMetricsPublisher.h>
#include <sstream>
namespace ix
{
int64_t cobra_to_cobra_bot(const ix::CobraBotConfig& cobraBotConfig,
const std::string& republishChannel,
const std::string& publisherRolename,
const std::string& publisherRolesecret)
{
CobraBot bot;
CobraMetricsPublisher cobraMetricsPublisher;
CobraConfig cobraPublisherConfig = cobraBotConfig.cobraConfig;
cobraPublisherConfig.rolename = publisherRolename;
cobraPublisherConfig.rolesecret = publisherRolesecret;
cobraPublisherConfig.headers["X-Cobra-Republish-Channel"] = republishChannel;
cobraMetricsPublisher.configure(cobraPublisherConfig, republishChannel);
bot.setOnBotMessageCallback(
[&republishChannel, &cobraMetricsPublisher](const Json::Value& msg,
const std::string& /*position*/,
std::atomic<bool>& /*throttled*/,
std::atomic<bool>& /*fatalCobraError*/,
std::atomic<uint64_t>& sentCount) -> void {
Json::Value msgWithNoId(msg);
msgWithNoId.removeMember("id");
cobraMetricsPublisher.push(republishChannel, msg);
sentCount++;
});
return bot.run(cobraBotConfig);
}
} // namespace ix

View File

@ -1,20 +0,0 @@
/*
* IXCobraToCobraBot.h
* Author: Benjamin Sergeant
* Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include <cstdint>
#include <ixbots/IXStatsdClient.h>
#include "IXCobraBotConfig.h"
#include <stddef.h>
#include <string>
namespace ix
{
int64_t cobra_to_cobra_bot(const ix::CobraBotConfig& config,
const std::string& republishChannel,
const std::string& publisherRolename,
const std::string& publisherRolesecret);
} // namespace ix

View File

@ -102,7 +102,7 @@ namespace ix
{
int64_t cobra_to_python_bot(const ix::CobraBotConfig& config,
StatsdClient& statsdClient,
const std::string& moduleName)
const std::string& scriptPath)
{
#ifndef IXBOTS_USE_PYTHON
CoreLogger::error("Command is disabled. "
@ -113,7 +113,10 @@ namespace ix
Py_InitializeEx(0); // 0 arg so that we do not install signal handlers
// which prevent us from using Ctrl-C
PyObject* pyModuleName = PyUnicode_DecodeFSDefault(moduleName.c_str());
size_t lastIndex = scriptPath.find_last_of(".");
std::string modulePath = scriptPath.substr(0, lastIndex);
PyObject* pyModuleName = PyUnicode_DecodeFSDefault(modulePath.c_str());
if (pyModuleName == nullptr)
{

View File

@ -15,5 +15,5 @@ namespace ix
{
int64_t cobra_to_python_bot(const ix::CobraBotConfig& config,
StatsdClient& statsdClient,
const std::string& moduleName);
const std::string& scriptPath);
} // namespace ix

View File

@ -41,7 +41,7 @@ namespace ix
else
{
CoreLogger::error("Error sending data to sentry: " + std::to_string(response->statusCode));
CoreLogger::error("Response: " + response->body);
CoreLogger::error("Response: " + response->payload);
// Error 429 Too Many Requests
if (response->statusCode == 429)

View File

@ -22,11 +22,16 @@ add_library(ixcobra STATIC
${IXCOBRA_HEADERS}
)
find_package(JsonCpp)
if (NOT JSONCPP_FOUND)
set(JSONCPP_INCLUDE_DIRS ../third_party/jsoncpp)
endif()
set(IXCOBRA_INCLUDE_DIRS
.
..
../ixcore
../ixcrypto)
../ixcrypto
${JSONCPP_INCLUDE_DIRS})
target_include_directories( ixcobra PUBLIC ${IXCOBRA_INCLUDE_DIRS} )
target_link_libraries( ixcobra jsoncpp_static )

View File

@ -8,7 +8,6 @@
#include <ixwebsocket/IXSocketTLSOptions.h>
#include <ixwebsocket/IXWebSocketPerMessageDeflateOptions.h>
#include <ixwebsocket/IXWebSocketHttpHeaders.h>
namespace ix
{
@ -20,7 +19,6 @@ namespace ix
std::string rolesecret;
WebSocketPerMessageDeflateOptions webSocketPerMessageDeflateOptions;
SocketTLSOptions socketTLSOptions;
WebSocketHttpHeaders headers;
CobraConfig(const std::string& a = std::string(),
const std::string& e = std::string(),

View File

@ -91,14 +91,13 @@ namespace ix
const std::string& errorMsg,
const WebSocketHttpHeaders& headers,
const std::string& subscriptionId,
CobraConnection::MsgId msgId,
const std::string& connectionId)
CobraConnection::MsgId msgId)
{
std::lock_guard<std::mutex> lock(_eventCallbackMutex);
if (_eventCallback)
{
_eventCallback(
std::make_unique<CobraEvent>(eventType, errorMsg, headers, subscriptionId, msgId, connectionId));
std::make_unique<CobraEvent>(eventType, errorMsg, headers, subscriptionId, msgId));
}
}
@ -256,8 +255,7 @@ namespace ix
const std::string& rolename,
const std::string& rolesecret,
const WebSocketPerMessageDeflateOptions& webSocketPerMessageDeflateOptions,
const SocketTLSOptions& socketTLSOptions,
const WebSocketHttpHeaders& headers)
const SocketTLSOptions& socketTLSOptions)
{
_roleName = rolename;
_roleSecret = rolesecret;
@ -271,7 +269,6 @@ namespace ix
_webSocket->setUrl(url);
_webSocket->setPerMessageDeflateOptions(webSocketPerMessageDeflateOptions);
_webSocket->setTLSOptions(socketTLSOptions);
_webSocket->setExtraHeaders(headers);
// Send a websocket ping every N seconds (N = 30) now
// This should keep the connection open and prevent some load balancers such as
@ -286,8 +283,7 @@ namespace ix
config.rolename,
config.rolesecret,
config.webSocketPerMessageDeflateOptions,
config.socketTLSOptions,
config.headers);
config.socketTLSOptions);
}
//
@ -353,18 +349,6 @@ namespace ix
if (!nonce.isString()) return false;
if (!data.isMember("connection_id")) return false;
Json::Value connectionId = data["connection_id"];
if (!connectionId.isString()) return false;
invokeEventCallback(ix::CobraEventType::Handshake,
std::string(),
WebSocketHttpHeaders(),
std::string(),
0,
connectionId.asString());
return sendAuthMessage(nonce.asString());
}

View File

@ -56,8 +56,7 @@ namespace ix
const std::string& rolename,
const std::string& rolesecret,
const WebSocketPerMessageDeflateOptions& webSocketPerMessageDeflateOptions,
const SocketTLSOptions& socketTLSOptions,
const WebSocketHttpHeaders& headers);
const SocketTLSOptions& socketTLSOptions);
void configure(const ix::CobraConfig& config);
@ -158,9 +157,7 @@ namespace ix
const std::string& errorMsg = std::string(),
const WebSocketHttpHeaders& headers = WebSocketHttpHeaders(),
const std::string& subscriptionId = std::string(),
uint64_t msgId = std::numeric_limits<uint64_t>::max(),
const std::string& connectionId = std::string());
uint64_t msgId = std::numeric_limits<uint64_t>::max());
void invokeErrorCallback(const std::string& errorMsg, const std::string& serializedPdu);
/// Tells whether the internal queue is empty or not

View File

@ -21,20 +21,17 @@ namespace ix
const ix::WebSocketHttpHeaders& headers;
const std::string& subscriptionId;
uint64_t msgId; // CobraConnection::MsgId
const std::string& connectionId;
CobraEvent(ix::CobraEventType t,
const std::string& e,
const ix::WebSocketHttpHeaders& h,
const std::string& s,
uint64_t m,
const std::string& c)
uint64_t m)
: type(t)
, errMsg(e)
, headers(h)
, subscriptionId(s)
, msgId(m)
, connectionId(c)
{
;
}

View File

@ -20,7 +20,6 @@ namespace ix
Pong = 7,
HandshakeError = 8,
AuthenticationError = 9,
SubscriptionError = 10,
Handshake = 11
SubscriptionError = 10
};
}

View File

@ -24,7 +24,6 @@ namespace ix
{
_cobra_connection.setEventCallback([](const CobraEventPtr& event) {
std::stringstream ss;
ix::LogLevel logLevel = LogLevel::Info;
if (event->type == ix::CobraEventType::Open)
{
@ -35,10 +34,6 @@ namespace ix
ss << it.first << ": " << it.second << std::endl;
}
}
else if (event->type == ix::CobraEventType::Handshake)
{
ss << "Cobra handshake connection id: " << event->connectionId;
}
else if (event->type == ix::CobraEventType::Authenticated)
{
ss << "Authenticated";
@ -46,7 +41,6 @@ namespace ix
else if (event->type == ix::CobraEventType::Error)
{
ss << "Error: " << event->errMsg;
logLevel = ix::LogLevel::Error;
}
else if (event->type == ix::CobraEventType::Closed)
{
@ -63,7 +57,6 @@ namespace ix
else if (event->type == ix::CobraEventType::Published)
{
ss << "Published message " << event->msgId << " acked";
logLevel = ix::LogLevel::Debug;
}
else if (event->type == ix::CobraEventType::Pong)
{
@ -72,20 +65,17 @@ namespace ix
else if (event->type == ix::CobraEventType::HandshakeError)
{
ss << "Handshake error: " << event->errMsg;
logLevel = ix::LogLevel::Error;
}
else if (event->type == ix::CobraEventType::AuthenticationError)
{
ss << "Authentication error: " << event->errMsg;
logLevel = ix::LogLevel::Error;
}
else if (event->type == ix::CobraEventType::SubscriptionError)
{
ss << "Subscription error: " << event->errMsg;
logLevel = ix::LogLevel::Error;
}
CoreLogger::log(ss.str().c_str(), logLevel);
CoreLogger::log(ss.str().c_str());
});
}

View File

@ -19,16 +19,4 @@ namespace ix
return hashAddress;
}
uint64_t djb2HashStr(const std::string& data)
{
uint64_t hashAddress = 5381;
for (size_t i = 0; i < data.size(); ++i)
{
hashAddress = ((hashAddress << 5) + hashAddress) + data[i];
}
return hashAddress;
}
} // namespace ix

View File

@ -8,10 +8,8 @@
#include <cstdint>
#include <vector>
#include <string>
namespace ix
{
uint64_t djb2Hash(const std::vector<uint8_t>& data);
uint64_t djb2HashStr(const std::string& data);
}

View File

@ -45,9 +45,10 @@ namespace ix
}
void RedisServer::handleConnection(std::unique_ptr<Socket> socket,
std::shared_ptr<ConnectionState> connectionState)
std::shared_ptr<ConnectionState> connectionState,
std::unique_ptr<ConnectionInfo> connectionInfo)
{
logInfo("New connection from remote ip " + connectionState->getRemoteIp());
logInfo("New connection from remote ip " + connectionInfo->remoteIp);
_connectedClientsCount++;

View File

@ -44,7 +44,8 @@ namespace ix
// Methods
virtual void handleConnection(std::unique_ptr<Socket>,
std::shared_ptr<ConnectionState> connectionState) final;
std::shared_ptr<ConnectionState> connectionState,
std::unique_ptr<ConnectionInfo> connectionInfo) final;
virtual size_t getConnectedClientsCount() final;
bool startsWith(const std::string& str, const std::string& start);

View File

@ -16,10 +16,15 @@ add_library(ixsentry STATIC
${IXSENTRY_HEADERS}
)
find_package(JsonCpp)
if (NOT JSONCPP_FOUND)
set(JSONCPP_INCLUDE_DIRS ../third_party/jsoncpp)
endif()
set(IXSENTRY_INCLUDE_DIRS
.
..
../ixcore)
../ixcore
${JSONCPP_INCLUDE_DIRS})
target_include_directories( ixsentry PUBLIC ${IXSENTRY_INCLUDE_DIRS} )
target_link_libraries( ixsentry jsoncpp_static )

View File

@ -61,10 +61,11 @@ namespace snake
_server.setOnClientMessageCallback(
[this](std::shared_ptr<ix::ConnectionState> connectionState,
ix::ConnectionInfo& connectionInfo,
ix::WebSocket& webSocket,
const ix::WebSocketMessagePtr& msg) {
auto state = std::dynamic_pointer_cast<SnakeConnectionState>(connectionState);
auto remoteIp = connectionState->getRemoteIp();
auto remoteIp = connectionInfo.remoteIp;
std::stringstream ss;
ss << "[" << state->getId() << "] ";

View File

@ -12,8 +12,10 @@ namespace ix
{
Bench::Bench(const std::string& description)
: _description(description)
, _start(std::chrono::high_resolution_clock::now())
, _reported(false)
{
reset();
;
}
Bench::~Bench()
@ -24,38 +26,19 @@ namespace ix
}
}
void Bench::reset()
{
_start = std::chrono::high_resolution_clock::now();
_reported = false;
}
void Bench::report()
{
auto now = std::chrono::high_resolution_clock::now();
auto microseconds = std::chrono::duration_cast<std::chrono::microseconds>(now - _start);
auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(now - _start);
_duration = microseconds.count();
std::cerr << _description << " completed in " << _duration << " us" << std::endl;
_ms = milliseconds.count();
std::cerr << _description << " completed in " << _ms << "ms" << std::endl;
setReported();
}
void Bench::record()
{
auto now = std::chrono::high_resolution_clock::now();
auto microseconds = std::chrono::duration_cast<std::chrono::microseconds>(now - _start);
_duration = microseconds.count();
}
void Bench::setReported()
{
_reported = true;
}
uint64_t Bench::getDuration() const
{
return _duration;
return _ms;
}
} // namespace ix

View File

@ -3,7 +3,6 @@
* Author: Benjamin Sergeant
* Copyright (c) 2017-2020 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include <chrono>
#include <stdint.h>
@ -17,16 +16,13 @@ namespace ix
Bench(const std::string& description);
~Bench();
void reset();
void record();
void report();
void setReported();
uint64_t getDuration() const;
private:
std::string _description;
std::chrono::time_point<std::chrono::high_resolution_clock> _start;
uint64_t _duration;
uint64_t _ms;
bool _reported;
};
} // namespace ix

View File

@ -0,0 +1,25 @@
/*
* IXConnectionInfo.h
* Author: Benjamin Sergeant
* Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include <string>
namespace ix
{
struct ConnectionInfo
{
std::string remoteIp;
int remotePort;
ConnectionInfo(const std::string& r = std::string(), int p = 0)
: remoteIp(r)
, remotePort(p)
{
;
}
};
} // namespace ix

View File

@ -31,11 +31,6 @@ namespace ix
return std::make_shared<ConnectionState>();
}
void ConnectionState::setOnSetTerminatedCallback(const OnSetTerminatedCallback& callback)
{
_onSetTerminatedCallback = callback;
}
bool ConnectionState::isTerminated() const
{
return _terminated;
@ -44,30 +39,5 @@ namespace ix
void ConnectionState::setTerminated()
{
_terminated = true;
if (_onSetTerminatedCallback)
{
_onSetTerminatedCallback();
}
}
const std::string& ConnectionState::getRemoteIp()
{
return _remoteIp;
}
int ConnectionState::getRemotePort()
{
return _remotePort;
}
void ConnectionState::setRemoteIp(const std::string& remoteIp)
{
_remoteIp = remoteIp;
}
void ConnectionState::setRemotePort(int remotePort)
{
_remotePort = remotePort;
}
} // namespace ix

View File

@ -7,15 +7,12 @@
#pragma once
#include <atomic>
#include <functional>
#include <memory>
#include <stdint.h>
#include <string>
namespace ix
{
using OnSetTerminatedCallback = std::function<void()>;
class ConnectionState
{
public:
@ -28,27 +25,12 @@ namespace ix
void setTerminated();
bool isTerminated() const;
const std::string& getRemoteIp();
int getRemotePort();
static std::shared_ptr<ConnectionState> createConnectionState();
private:
void setOnSetTerminatedCallback(const OnSetTerminatedCallback& callback);
void setRemoteIp(const std::string& remoteIp);
void setRemotePort(int remotePort);
protected:
std::atomic<bool> _terminated;
std::string _id;
OnSetTerminatedCallback _onSetTerminatedCallback;
static std::atomic<uint64_t> _globalId;
std::string _remoteIp;
int _remotePort;
friend class SocketServer;
};
} // namespace ix

View File

@ -1,170 +0,0 @@
/*
* IXGzipCodec.cpp
* Author: Benjamin Sergeant
* Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
*/
#include "IXGzipCodec.h"
#include "IXBench.h"
#include <array>
#include <string.h>
#ifdef IXWEBSOCKET_USE_ZLIB
#include <zlib.h>
#endif
#ifdef IXWEBSOCKET_USE_DEFLATE
#include <libdeflate.h>
#endif
namespace ix
{
#ifdef IXWEBSOCKET_USE_ZLIB
std::string gzipCompress(const std::string& str)
{
#ifdef IXWEBSOCKET_USE_DEFLATE
int compressionLevel = 6;
struct libdeflate_compressor *compressor;
{
Bench bench("creating compressor");
compressor =
libdeflate_alloc_compressor(compressionLevel);
}
const void *uncompressed_data = str.data();
size_t uncompressed_size = str.size();
void *compressed_data;
size_t actual_compressed_size;
size_t max_compressed_size;
max_compressed_size = libdeflate_gzip_compress_bound(compressor,
uncompressed_size);
{
Bench bench("alloc data");
compressed_data = malloc(max_compressed_size);
}
if (compressed_data == NULL)
{
return std::string();
}
{
Bench bench("compressing data");
actual_compressed_size = libdeflate_gzip_compress(
compressor,
uncompressed_data,
uncompressed_size,
compressed_data,
max_compressed_size);
}
if (actual_compressed_size == 0)
{
free(compressed_data);
return std::string();
}
libdeflate_free_compressor(compressor);
std::string out;
{
Bench bench("append data");
out.append(reinterpret_cast<char*>(compressed_data), actual_compressed_size);
}
{
Bench bench("free data");
free(compressed_data);
}
return out;
#else
z_stream zs; // z_stream is zlib's control structure
memset(&zs, 0, sizeof(zs));
// deflateInit2 configure the file format: request gzip instead of deflate
const int windowBits = 15;
const int GZIP_ENCODING = 16;
deflateInit2(&zs,
Z_DEFAULT_COMPRESSION,
Z_DEFLATED,
windowBits | GZIP_ENCODING,
8,
Z_DEFAULT_STRATEGY);
zs.next_in = (Bytef*) str.data();
zs.avail_in = (uInt) str.size(); // set the z_stream's input
int ret;
char outbuffer[32768];
std::string outstring;
// retrieve the compressed bytes blockwise
do
{
zs.next_out = reinterpret_cast<Bytef*>(outbuffer);
zs.avail_out = sizeof(outbuffer);
ret = deflate(&zs, Z_FINISH);
if (outstring.size() < zs.total_out)
{
// append the block to the output string
outstring.append(outbuffer, zs.total_out - outstring.size());
}
} while (ret == Z_OK);
deflateEnd(&zs);
return outstring;
#endif
}
bool gzipDecompress(const std::string& in, std::string& out)
{
z_stream inflateState;
memset(&inflateState, 0, sizeof(inflateState));
inflateState.zalloc = Z_NULL;
inflateState.zfree = Z_NULL;
inflateState.opaque = Z_NULL;
inflateState.avail_in = 0;
inflateState.next_in = Z_NULL;
if (inflateInit2(&inflateState, 16 + MAX_WBITS) != Z_OK)
{
return false;
}
inflateState.avail_in = (uInt) in.size();
inflateState.next_in = (unsigned char*) (const_cast<char*>(in.data()));
const int kBufferSize = 1 << 14;
std::array<unsigned char, kBufferSize> compressBuffer;
do
{
inflateState.avail_out = (uInt) kBufferSize;
inflateState.next_out = &compressBuffer.front();
int ret = inflate(&inflateState, Z_SYNC_FLUSH);
if (ret == Z_NEED_DICT || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR)
{
inflateEnd(&inflateState);
return false;
}
out.append(reinterpret_cast<char*>(&compressBuffer.front()),
kBufferSize - inflateState.avail_out);
} while (inflateState.avail_out == 0);
inflateEnd(&inflateState);
return true;
}
#endif
} // namespace ix

View File

@ -1,15 +0,0 @@
/*
* IXGzipCodec.h
* Author: Benjamin Sergeant
* Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include <string>
namespace ix
{
std::string gzipCompress(const std::string& str);
bool gzipDecompress(const std::string& in, std::string& out);
} // namespace ix

View File

@ -93,12 +93,14 @@ namespace ix
}
std::tuple<bool, std::string, HttpRequestPtr> Http::parseRequest(
std::unique_ptr<Socket>& socket, int timeoutSecs)
std::unique_ptr<Socket>& socket)
{
HttpRequestPtr httpRequest;
std::atomic<bool> requestInitCancellation(false);
int timeoutSecs = 5; // FIXME
auto isCancellationRequested =
makeCancellationRequestWithTimeout(timeoutSecs, requestInitCancellation);
@ -128,36 +130,7 @@ namespace ix
return std::make_tuple(false, "Error parsing HTTP headers", httpRequest);
}
std::string body;
if (headers.find("Content-Length") != headers.end())
{
int contentLength = 0;
try
{
contentLength = std::stoi(headers["Content-Length"]);
}
catch (std::exception)
{
return std::make_tuple(
false, "Error parsing HTTP Header 'Content-Length'", httpRequest);
}
if (contentLength < 0)
{
return std::make_tuple(
false, "Error: 'Content-Length' should be a positive integer", httpRequest);
}
auto res = socket->readBytes(contentLength, nullptr, isCancellationRequested);
if (!res.first)
{
return std::make_tuple(
false, std::string("Error reading request: ") + res.second, httpRequest);
}
body = res.second;
}
httpRequest = std::make_shared<HttpRequest>(uri, method, httpVersion, body, headers);
httpRequest = std::make_shared<HttpRequest>(uri, method, httpVersion, headers);
return std::make_tuple(true, "", httpRequest);
}
@ -178,7 +151,7 @@ namespace ix
// Write headers
ss.str("");
ss << "Content-Length: " << response->body.size() << "\r\n";
ss << "Content-Length: " << response->payload.size() << "\r\n";
for (auto&& it : response->headers)
{
ss << it.first << ": " << it.second << "\r\n";
@ -190,6 +163,6 @@ namespace ix
return false;
}
return response->body.empty() ? true : socket->writeBytes(response->body, nullptr);
return response->payload.empty() ? true : socket->writeBytes(response->payload, nullptr);
}
} // namespace ix

View File

@ -39,7 +39,7 @@ namespace ix
std::string description;
HttpErrorCode errorCode;
WebSocketHttpHeaders headers;
std::string body;
std::string payload;
std::string errorMsg;
uint64_t uploadSize;
uint64_t downloadSize;
@ -48,7 +48,7 @@ namespace ix
const std::string& des = std::string(),
const HttpErrorCode& c = HttpErrorCode::Ok,
const WebSocketHttpHeaders& h = WebSocketHttpHeaders(),
const std::string& b = std::string(),
const std::string& p = std::string(),
const std::string& e = std::string(),
uint64_t u = 0,
uint64_t d = 0)
@ -56,7 +56,7 @@ namespace ix
, description(des)
, errorCode(c)
, headers(h)
, body(b)
, payload(p)
, errorMsg(e)
, uploadSize(u)
, downloadSize(d)
@ -95,18 +95,15 @@ namespace ix
std::string uri;
std::string method;
std::string version;
std::string body;
WebSocketHttpHeaders headers;
HttpRequest(const std::string& u,
const std::string& m,
const std::string& v,
const std::string& b,
const WebSocketHttpHeaders& h = WebSocketHttpHeaders())
: uri(u)
, method(m)
, version(v)
, body(b)
, headers(h)
{
}
@ -118,7 +115,7 @@ namespace ix
{
public:
static std::tuple<bool, std::string, HttpRequestPtr> parseRequest(
std::unique_ptr<Socket>& socket, int timeoutSecs);
std::unique_ptr<Socket>& socket);
static bool sendResponse(HttpResponsePtr response, std::unique_ptr<Socket>& socket);
static std::pair<std::string, int> parseStatusLine(const std::string& line);

View File

@ -6,7 +6,6 @@
#include "IXHttpClient.h"
#include "IXGzipCodec.h"
#include "IXSocketFactory.h"
#include "IXUrlParser.h"
#include "IXUserAgent.h"
@ -18,6 +17,10 @@
#include <sstream>
#include <vector>
#ifdef IXWEBSOCKET_USE_ZLIB
#include <zlib.h>
#endif
namespace ix
{
const std::string HttpClient::kPost = "POST";
@ -497,12 +500,12 @@ namespace ix
downloadSize = payload.size();
#ifdef IXWEBSOCKET_USE_ZLIB
// If the content was compressed with gzip, decode it
if (headers["Content-Encoding"] == "gzip")
{
#ifdef IXWEBSOCKET_USE_ZLIB
std::string decompressedPayload;
if (!gzipDecompress(payload, decompressedPayload))
if (!gzipInflate(payload, decompressedPayload))
{
std::string errorMsg("Error decompressing payload");
return std::make_shared<HttpResponse>(code,
@ -515,18 +518,8 @@ namespace ix
downloadSize);
}
payload = decompressedPayload;
#else
std::string errorMsg("ixwebsocket was not compiled with gzip support on");
return std::make_shared<HttpResponse>(code,
description,
HttpErrorCode::Gzip,
headers,
payload,
errorMsg,
uploadSize,
downloadSize);
#endif
}
#endif
return std::make_shared<HttpResponse>(code,
description,
@ -686,6 +679,53 @@ namespace ix
return ss.str();
}
#ifdef IXWEBSOCKET_USE_ZLIB
bool HttpClient::gzipInflate(const std::string& in, std::string& out)
{
z_stream inflateState;
std::memset(&inflateState, 0, sizeof(inflateState));
inflateState.zalloc = Z_NULL;
inflateState.zfree = Z_NULL;
inflateState.opaque = Z_NULL;
inflateState.avail_in = 0;
inflateState.next_in = Z_NULL;
if (inflateInit2(&inflateState, 16 + MAX_WBITS) != Z_OK)
{
return false;
}
inflateState.avail_in = (uInt) in.size();
inflateState.next_in = (unsigned char*) (const_cast<char*>(in.data()));
const int kBufferSize = 1 << 14;
std::unique_ptr<unsigned char[]> compressBuffer =
std::make_unique<unsigned char[]>(kBufferSize);
do
{
inflateState.avail_out = (uInt) kBufferSize;
inflateState.next_out = compressBuffer.get();
int ret = inflate(&inflateState, Z_SYNC_FLUSH);
if (ret == Z_NEED_DICT || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR)
{
inflateEnd(&inflateState);
return false;
}
out.append(reinterpret_cast<char*>(compressBuffer.get()),
kBufferSize - inflateState.avail_out);
} while (inflateState.avail_out == 0);
inflateEnd(&inflateState);
return true;
}
#endif
void HttpClient::log(const std::string& msg, HttpRequestArgsPtr args)
{
if (args->logger)

View File

@ -90,6 +90,10 @@ namespace ix
private:
void log(const std::string& msg, HttpRequestArgsPtr args);
#ifdef IXWEBSOCKET_USE_ZLIB
bool gzipInflate(const std::string& in, std::string& out);
#endif
// Async API background thread runner
void run();
// Async API

View File

@ -6,7 +6,6 @@
#include "IXHttpServer.h"
#include "IXGzipCodec.h"
#include "IXNetSystem.h"
#include "IXSocketConnect.h"
#include "IXUserAgent.h"
@ -15,6 +14,10 @@
#include <sstream>
#include <vector>
#ifdef IXWEBSOCKET_USE_ZLIB
#include <zlib.h>
#endif
namespace
{
std::pair<bool, std::vector<uint8_t>> load(const std::string& path)
@ -40,21 +43,59 @@ namespace
auto vec = res.second;
return std::make_pair(res.first, std::string(vec.begin(), vec.end()));
}
#ifdef IXWEBSOCKET_USE_ZLIB
std::string gzipCompress(const std::string& str)
{
z_stream zs; // z_stream is zlib's control structure
memset(&zs, 0, sizeof(zs));
// deflateInit2 configure the file format: request gzip instead of deflate
const int windowBits = 15;
const int GZIP_ENCODING = 16;
deflateInit2(&zs,
Z_DEFAULT_COMPRESSION,
Z_DEFLATED,
windowBits | GZIP_ENCODING,
8,
Z_DEFAULT_STRATEGY);
zs.next_in = (Bytef*) str.data();
zs.avail_in = (uInt) str.size(); // set the z_stream's input
int ret;
char outbuffer[32768];
std::string outstring;
// retrieve the compressed bytes blockwise
do
{
zs.next_out = reinterpret_cast<Bytef*>(outbuffer);
zs.avail_out = sizeof(outbuffer);
ret = deflate(&zs, Z_FINISH);
if (outstring.size() < zs.total_out)
{
// append the block to the output string
outstring.append(outbuffer, zs.total_out - outstring.size());
}
} while (ret == Z_OK);
deflateEnd(&zs);
return outstring;
}
#endif
} // namespace
namespace ix
{
const int HttpServer::kDefaultTimeoutSecs(30);
HttpServer::HttpServer(int port,
const std::string& host,
int backlog,
size_t maxConnections,
int addressFamily,
int timeoutSecs)
HttpServer::HttpServer(
int port, const std::string& host, int backlog, size_t maxConnections, int addressFamily)
: SocketServer(port, host, backlog, maxConnections, addressFamily)
, _connectedClientsCount(0)
, _timeoutSecs(timeoutSecs)
{
setDefaultConnectionCallback();
}
@ -79,16 +120,18 @@ namespace ix
}
void HttpServer::handleConnection(std::unique_ptr<Socket> socket,
std::shared_ptr<ConnectionState> connectionState)
std::shared_ptr<ConnectionState> connectionState,
std::unique_ptr<ConnectionInfo> connectionInfo)
{
_connectedClientsCount++;
auto ret = Http::parseRequest(socket, _timeoutSecs);
auto ret = Http::parseRequest(socket);
// FIXME: handle errors in parseRequest
if (std::get<0>(ret))
{
auto response = _onConnectionCallback(std::get<2>(ret), connectionState);
auto response =
_onConnectionCallback(std::get<2>(ret), connectionState, std::move(connectionInfo));
if (!Http::sendResponse(response, socket))
{
logError("Cannot send response");
@ -108,7 +151,8 @@ namespace ix
{
setOnConnectionCallback(
[this](HttpRequestPtr request,
std::shared_ptr<ConnectionState> connectionState) -> HttpResponsePtr {
std::shared_ptr<ConnectionState> /*connectionState*/,
std::unique_ptr<ConnectionInfo> connectionInfo) -> HttpResponsePtr {
std::string uri(request->uri);
if (uri.empty() || uri == "/")
{
@ -140,8 +184,8 @@ namespace ix
// Log request
std::stringstream ss;
ss << connectionState->getRemoteIp() << ":" << connectionState->getRemotePort()
<< " " << request->method << " " << request->headers["User-Agent"] << " "
ss << connectionInfo->remoteIp << ":" << connectionInfo->remotePort << " "
<< request->method << " " << request->headers["User-Agent"] << " "
<< request->uri << " " << content.size();
logInfo(ss.str());
@ -165,16 +209,16 @@ namespace ix
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Redirections
//
setOnConnectionCallback(
[this,
redirectUrl](HttpRequestPtr request,
std::shared_ptr<ConnectionState> connectionState) -> HttpResponsePtr {
[this, redirectUrl](HttpRequestPtr request,
std::shared_ptr<ConnectionState> /*connectionState*/,
std::unique_ptr<ConnectionInfo> connectionInfo) -> HttpResponsePtr {
WebSocketHttpHeaders headers;
headers["Server"] = userAgent();
// Log request
std::stringstream ss;
ss << connectionState->getRemoteIp() << ":" << connectionState->getRemotePort()
<< " " << request->method << " " << request->headers["User-Agent"] << " "
ss << connectionInfo->remoteIp << ":" << connectionInfo->remotePort << " "
<< request->method << " " << request->headers["User-Agent"] << " "
<< request->uri;
logInfo(ss.str());

View File

@ -23,14 +23,15 @@ namespace ix
{
public:
using OnConnectionCallback =
std::function<HttpResponsePtr(HttpRequestPtr, std::shared_ptr<ConnectionState>)>;
std::function<HttpResponsePtr(HttpRequestPtr,
std::shared_ptr<ConnectionState>,
std::unique_ptr<ConnectionInfo> connectionInfo)>;
HttpServer(int port = SocketServer::kDefaultPort,
const std::string& host = SocketServer::kDefaultHost,
int backlog = SocketServer::kDefaultTcpBacklog,
size_t maxConnections = SocketServer::kDefaultMaxConnections,
int addressFamily = SocketServer::kDefaultAddressFamily,
int timeoutSecs = HttpServer::kDefaultTimeoutSecs);
int addressFamily = SocketServer::kDefaultAddressFamily);
virtual ~HttpServer();
virtual void stop() final;
@ -43,12 +44,10 @@ namespace ix
OnConnectionCallback _onConnectionCallback;
std::atomic<int> _connectedClientsCount;
const static int kDefaultTimeoutSecs;
int _timeoutSecs;
// Methods
virtual void handleConnection(std::unique_ptr<Socket>,
std::shared_ptr<ConnectionState> connectionState) final;
std::shared_ptr<ConnectionState> connectionState,
std::unique_ptr<ConnectionInfo> connectionInfo) final;
virtual size_t getConnectedClientsCount() final;
void setDefaultConnectionCallback();

View File

@ -8,9 +8,6 @@
namespace ix
{
const uint64_t SelectInterrupt::kSendRequest = 1;
const uint64_t SelectInterrupt::kCloseRequest = 2;
SelectInterrupt::SelectInterrupt()
{
;

View File

@ -6,7 +6,6 @@
#pragma once
#include <memory>
#include <stdint.h>
#include <string>
@ -24,11 +23,5 @@ namespace ix
virtual bool clear();
virtual uint64_t read();
virtual int getFd() const;
// Used as special codes for pipe communication
static const uint64_t kSendRequest;
static const uint64_t kCloseRequest;
};
using SelectInterruptPtr = std::unique_ptr<SelectInterrupt>;
} // namespace ix

View File

@ -27,6 +27,8 @@ namespace ix
{
const int Socket::kDefaultPollNoTimeout = -1; // No poll timeout by default
const int Socket::kDefaultPollTimeout = kDefaultPollNoTimeout;
const uint64_t Socket::kSendRequest = 1;
const uint64_t Socket::kCloseRequest = 2;
constexpr size_t Socket::kChunkSize;
Socket::Socket(int fd)
@ -94,11 +96,11 @@ namespace ix
{
uint64_t value = selectInterrupt->read();
if (value == SelectInterrupt::kSendRequest)
if (value == kSendRequest)
{
pollResult = PollResultType::SendRequest;
}
else if (value == SelectInterrupt::kCloseRequest)
else if (value == kCloseRequest)
{
pollResult = PollResultType::CloseRequest;
}

View File

@ -34,10 +34,12 @@ typedef SSIZE_T ssize_t;
#include "IXCancellationRequest.h"
#include "IXProgressCallback.h"
#include "IXSelectInterrupt.h"
namespace ix
{
class SelectInterrupt;
using SelectInterruptPtr = std::unique_ptr<SelectInterrupt>;
enum class PollResultType
{
ReadyForRead = 0,
@ -94,6 +96,11 @@ namespace ix
int sockfd,
const SelectInterruptPtr& selectInterrupt);
// Used as special codes for pipe communication
static const uint64_t kSendRequest;
static const uint64_t kCloseRequest;
protected:
std::atomic<int> _sockfd;
std::mutex _socketMutex;

View File

@ -8,7 +8,6 @@
#include "IXNetSystem.h"
#include "IXSelectInterrupt.h"
#include "IXSelectInterruptFactory.h"
#include "IXSetThreadName.h"
#include "IXSocket.h"
#include "IXSocketConnect.h"
@ -37,7 +36,6 @@ namespace ix
, _stop(false)
, _stopGc(false)
, _connectionStateFactory(&ConnectionState::createConnectionState)
, _acceptSelectInterrupt(createSelectInterrupt())
{
}
@ -60,16 +58,6 @@ namespace ix
std::pair<bool, std::string> SocketServer::listen()
{
std::string acceptSelectInterruptInitErrorMsg;
if (!_acceptSelectInterrupt->init(acceptSelectInterruptInitErrorMsg))
{
std::stringstream ss;
ss << "SocketServer::listen() error in SelectInterrupt::init: "
<< acceptSelectInterruptInitErrorMsg;
return std::make_pair(false, ss.str());
}
if (_addressFamily != AF_INET && _addressFamily != AF_INET6)
{
std::string errMsg("SocketServer::listen() AF_INET and AF_INET6 are currently "
@ -205,12 +193,6 @@ namespace ix
if (_thread.joinable())
{
_stop = true;
// Wake up select
if (!_acceptSelectInterrupt->notify(SelectInterrupt::kCloseRequest))
{
logError("SocketServer::stop: Cannot wake up from select");
}
_thread.join();
_stop = false;
}
@ -219,7 +201,6 @@ namespace ix
if (_gcThread.joinable())
{
_stopGc = true;
_conditionVariableGC.notify_one();
_gcThread.join();
_stopGc = false;
}
@ -268,22 +249,18 @@ namespace ix
// Set the socket to non blocking mode, so that accept calls are not blocking
SocketConnect::configure(_serverFd);
setThreadName("SocketServer::accept");
setThreadName("SocketServer::listen");
for (;;)
{
if (_stop) return;
// Use poll to check whether a new connection is in progress
int timeoutMs = -1;
#ifdef _WIN32
// select cannot be interrupted on Windows so we need to pass a small timeout
timeoutMs = 10;
#endif
int timeoutMs = 10;
bool readyToRead = true;
auto selectInterrupt = std::make_unique<SelectInterrupt>();
PollResultType pollResult =
Socket::poll(readyToRead, timeoutMs, _serverFd, _acceptSelectInterrupt);
Socket::poll(readyToRead, timeoutMs, _serverFd, selectInterrupt);
if (pollResult == PollResultType::Error)
{
@ -331,14 +308,12 @@ namespace ix
continue;
}
// Retrieve connection info, the ip address of the remote peer/client)
std::string remoteIp;
int remotePort;
std::unique_ptr<ConnectionInfo> connectionInfo;
if (_addressFamily == AF_INET)
{
char remoteIp4[INET_ADDRSTRLEN];
if (inet_ntop(AF_INET, &client.sin_addr, remoteIp4, INET_ADDRSTRLEN) == nullptr)
char remoteIp[INET_ADDRSTRLEN];
if (inet_ntop(AF_INET, &client.sin_addr, remoteIp, INET_ADDRSTRLEN) == nullptr)
{
int err = Socket::getErrno();
std::stringstream ss;
@ -351,13 +326,12 @@ namespace ix
continue;
}
remotePort = client.sin_port;
remoteIp = remoteIp4;
connectionInfo = std::make_unique<ConnectionInfo>(remoteIp, client.sin_port);
}
else // AF_INET6
{
char remoteIp6[INET6_ADDRSTRLEN];
if (inet_ntop(AF_INET6, &client.sin_addr, remoteIp6, INET6_ADDRSTRLEN) == nullptr)
char remoteIp[INET6_ADDRSTRLEN];
if (inet_ntop(AF_INET6, &client.sin_addr, remoteIp, INET6_ADDRSTRLEN) == nullptr)
{
int err = Socket::getErrno();
std::stringstream ss;
@ -370,8 +344,7 @@ namespace ix
continue;
}
remotePort = client.sin_port;
remoteIp = remoteIp6;
connectionInfo = std::make_unique<ConnectionInfo>(remoteIp, client.sin_port);
}
std::shared_ptr<ConnectionState> connectionState;
@ -379,9 +352,6 @@ namespace ix
{
connectionState = _connectionStateFactory();
}
connectionState->setOnSetTerminatedCallback([this] { onSetTerminatedCallback(); });
connectionState->setRemoteIp(remoteIp);
connectionState->setRemotePort(remotePort);
if (_stop) return;
@ -409,10 +379,13 @@ namespace ix
// Launch the handleConnection work asynchronously in its own thread.
std::lock_guard<std::mutex> lock(_connectionsThreadsMutex);
_connectionsThreads.push_back(std::make_pair(
connectionState,
std::thread(
&SocketServer::handleConnection, this, std::move(socket), connectionState)));
_connectionsThreads.push_back(
std::make_pair(connectionState,
std::thread(&SocketServer::handleConnection,
this,
std::move(socket),
connectionState,
std::move(connectionInfo))));
}
}
@ -438,14 +411,8 @@ namespace ix
break;
}
// Unless we are stopping the server, wait for a connection
// to be terminated to run the threads GC, instead of busy waiting
// with a sleep
if (!_stopGc)
{
std::unique_lock<std::mutex> lock(_conditionVariableMutexGC);
_conditionVariableGC.wait(lock);
}
// Sleep a little bit then keep cleaning up
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
@ -453,11 +420,4 @@ namespace ix
{
_socketTLSOptions = socketTLSOptions;
}
void SocketServer::onSetTerminatedCallback()
{
// a connection got terminated, we can run the connection thread GC,
// so wake up the thread responsible for that
_conditionVariableGC.notify_one();
}
} // namespace ix

View File

@ -6,8 +6,8 @@
#pragma once
#include "IXConnectionInfo.h"
#include "IXConnectionState.h"
#include "IXSelectInterrupt.h"
#include "IXSocketTLSOptions.h"
#include <atomic>
#include <condition_variable>
@ -84,7 +84,6 @@ namespace ix
// background thread to wait for incoming connections
std::thread _thread;
void run();
void onSetTerminatedCallback();
// background thread to cleanup (join) terminated threads
std::atomic<bool> _stopGc;
@ -104,7 +103,8 @@ namespace ix
ConnectionStateFactory _connectionStateFactory;
virtual void handleConnection(std::unique_ptr<Socket>,
std::shared_ptr<ConnectionState> connectionState) = 0;
std::shared_ptr<ConnectionState> connectionState,
std::unique_ptr<ConnectionInfo> connectionInfo) = 0;
virtual size_t getConnectedClientsCount() = 0;
// Returns true if all connection threads are joined
@ -112,13 +112,5 @@ namespace ix
size_t getConnectionsThreadsCount();
SocketTLSOptions _socketTLSOptions;
// to wake up from select
SelectInterruptPtr _acceptSelectInterrupt;
// used by the gc thread, to know that a thread needs to be garbage collected
// as a connection
std::condition_variable _conditionVariableGC;
std::mutex _conditionVariableMutexGC;
};
} // namespace ix

View File

@ -54,7 +54,6 @@ namespace ix
std::lock_guard<std::mutex> lock(_configMutex);
_url = url;
}
void WebSocket::setExtraHeaders(const WebSocketHttpHeaders& headers)
{
std::lock_guard<std::mutex> lock(_configMutex);
@ -406,11 +405,6 @@ namespace ix
_onMessageCallback = callback;
}
bool WebSocket::isOnMessageCallbackRegistered() const
{
return _onMessageCallback != nullptr;
}
void WebSocket::setTrafficTrackerCallback(const OnTrafficTrackerCallback& callback)
{
_onTrafficTrackerCallback = callback;

View File

@ -84,7 +84,6 @@ namespace ix
const std::string& reason = WebSocketCloseConstants::kNormalClosureMessage);
void setOnMessageCallback(const OnMessageCallback& callback);
bool isOnMessageCallbackRegistered() const;
static void setTrafficTrackerCallback(const OnTrafficTrackerCallback& callback);
static void resetTrafficTrackerCallback();

View File

@ -170,11 +170,20 @@ namespace ix
{
std::stringstream ss;
ss << "Expecting HTTP/1.1, got " << httpVersion << ". "
<< "Rejecting connection to " << url << ", status: " << status
<< "Rejecting connection to " << host << ":" << port << ", status: " << status
<< ", HTTP Status line: " << line;
return WebSocketInitResult(false, status, ss.str());
}
// We want an 101 HTTP status
if (status != 101)
{
std::stringstream ss;
ss << "Expecting status 101 (Switching Protocol), got " << status
<< " status connecting to " << host << ":" << port << ", HTTP Status line: " << line;
return WebSocketInitResult(false, status, ss.str());
}
auto result = parseHttpHeaders(_socket, isCancellationRequested);
auto headersValid = result.first;
auto headers = result.second;
@ -184,17 +193,6 @@ namespace ix
return WebSocketInitResult(false, status, "Error parsing HTTP headers");
}
// We want an 101 HTTP status for websocket, otherwise it could be
// a redirection (like 301)
if (status != 101)
{
std::stringstream ss;
ss << "Expecting status 101 (Switching Protocol), got " << status
<< " status connecting to " << url << ", HTTP Status line: " << line;
return WebSocketInitResult(false, status, ss.str(), headers, path);
}
// Check the presence of the connection field
if (headers.find("connection") == headers.end())
{

View File

@ -16,6 +16,8 @@ namespace
// is treated as a char* and the null termination (\x00) makes it
// look like an empty string.
const std::string kEmptyUncompressedBlock = std::string("\x00\x00\xff\xff", 4);
const int kBufferSize = 1 << 14;
} // namespace
namespace ix
@ -24,6 +26,7 @@ namespace ix
// Compressor
//
WebSocketPerMessageDeflateCompressor::WebSocketPerMessageDeflateCompressor()
: _compressBufferSize(kBufferSize)
{
#ifdef IXWEBSOCKET_USE_ZLIB
memset(&_deflateState, 0, sizeof(_deflateState));
@ -54,6 +57,8 @@ namespace ix
if (ret != Z_OK) return false;
_compressBuffer = std::make_unique<unsigned char[]>(_compressBufferSize);
_flush = (clientNoContextTakeOver) ? Z_FULL_FLUSH : Z_SYNC_FLUSH;
return true;
@ -140,14 +145,14 @@ namespace ix
do
{
// Output to local buffer
_deflateState.avail_out = (uInt) _compressBuffer.size();
_deflateState.next_out = &_compressBuffer.front();
_deflateState.avail_out = (uInt) _compressBufferSize;
_deflateState.next_out = _compressBuffer.get();
deflate(&_deflateState, _flush);
output = _compressBuffer.size() - _deflateState.avail_out;
output = _compressBufferSize - _deflateState.avail_out;
out.insert(out.end(), _compressBuffer.begin(), _compressBuffer.begin() + output);
out.insert(out.end(), _compressBuffer.get(), _compressBuffer.get() + output);
} while (_deflateState.avail_out == 0);
if (endsWithEmptyUnCompressedBlock(out))
@ -165,6 +170,7 @@ namespace ix
// Decompressor
//
WebSocketPerMessageDeflateDecompressor::WebSocketPerMessageDeflateDecompressor()
: _compressBufferSize(kBufferSize)
{
#ifdef IXWEBSOCKET_USE_ZLIB
memset(&_inflateState, 0, sizeof(_inflateState));
@ -192,6 +198,8 @@ namespace ix
if (ret != Z_OK) return false;
_compressBuffer = std::make_unique<unsigned char[]>(_compressBufferSize);
_flush = (clientNoContextTakeOver) ? Z_FULL_FLUSH : Z_SYNC_FLUSH;
return true;
@ -224,8 +232,8 @@ namespace ix
do
{
_inflateState.avail_out = (uInt) _compressBuffer.size();
_inflateState.next_out = &_compressBuffer.front();
_inflateState.avail_out = (uInt) _compressBufferSize;
_inflateState.next_out = _compressBuffer.get();
int ret = inflate(&_inflateState, Z_SYNC_FLUSH);
@ -234,8 +242,8 @@ namespace ix
return false; // zlib error
}
out.append(reinterpret_cast<char*>(&_compressBuffer.front()),
_compressBuffer.size() - _inflateState.avail_out);
out.append(reinterpret_cast<char*>(_compressBuffer.get()),
_compressBufferSize - _inflateState.avail_out);
} while (_inflateState.avail_out == 0);
return true;

View File

@ -9,7 +9,7 @@
#ifdef IXWEBSOCKET_USE_ZLIB
#include "zlib.h"
#endif
#include <array>
#include <memory>
#include <string>
#include <vector>
@ -34,7 +34,8 @@ namespace ix
bool endsWithEmptyUnCompressedBlock(const T& value);
int _flush;
std::array<unsigned char, 1 << 14> _compressBuffer;
size_t _compressBufferSize;
std::unique_ptr<unsigned char[]> _compressBuffer;
#ifdef IXWEBSOCKET_USE_ZLIB
z_stream _deflateState;
@ -52,7 +53,8 @@ namespace ix
private:
int _flush;
std::array<unsigned char, 1 << 14> _compressBuffer;
size_t _compressBufferSize;
std::unique_ptr<unsigned char[]> _compressBuffer;
#ifdef IXWEBSOCKET_USE_ZLIB
z_stream _inflateState;

View File

@ -43,7 +43,6 @@ namespace ix
const std::string& hostname,
const ix::SocketTLSOptions& tlsOptions,
const std::string& remoteUrl,
const RemoteUrlsMapping& remoteUrlsMapping,
bool /*verbose*/)
{
ix::WebSocketServer server(port, hostname);
@ -54,74 +53,61 @@ namespace ix
};
server.setConnectionStateFactory(factory);
server.setOnConnectionCallback(
[remoteUrl, remoteUrlsMapping](std::weak_ptr<ix::WebSocket> webSocket,
std::shared_ptr<ConnectionState> connectionState) {
auto state = std::dynamic_pointer_cast<ProxyConnectionState>(connectionState);
auto remoteIp = connectionState->getRemoteIp();
server.setOnConnectionCallback([remoteUrl](std::weak_ptr<ix::WebSocket> webSocket,
std::shared_ptr<ConnectionState> connectionState,
std::unique_ptr<ConnectionInfo> connectionInfo) {
auto state = std::dynamic_pointer_cast<ProxyConnectionState>(connectionState);
auto remoteIp = connectionInfo->remoteIp;
// Server connection
state->webSocket().setOnMessageCallback(
[webSocket, state, remoteIp](const WebSocketMessagePtr& msg) {
if (msg->type == ix::WebSocketMessageType::Close)
// Server connection
state->webSocket().setOnMessageCallback(
[webSocket, state, remoteIp](const WebSocketMessagePtr& msg) {
if (msg->type == ix::WebSocketMessageType::Close)
{
state->setTerminated();
}
else if (msg->type == ix::WebSocketMessageType::Message)
{
auto ws = webSocket.lock();
if (ws)
{
state->setTerminated();
ws->send(msg->str, msg->binary);
}
else if (msg->type == ix::WebSocketMessageType::Message)
}
});
// Client connection
auto ws = webSocket.lock();
if (ws)
{
ws->setOnMessageCallback([state, remoteUrl](const WebSocketMessagePtr& msg) {
if (msg->type == ix::WebSocketMessageType::Open)
{
// Connect to the 'real' server
std::string url(remoteUrl);
url += msg->openInfo.uri;
state->webSocket().setUrl(url);
state->webSocket().disableAutomaticReconnection();
state->webSocket().start();
// we should sleep here for a bit until we've established the
// connection with the remote server
while (state->webSocket().getReadyState() != ReadyState::Open)
{
auto ws = webSocket.lock();
if (ws)
{
ws->send(msg->str, msg->binary);
}
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
});
// Client connection
auto ws = webSocket.lock();
if (ws)
{
ws->setOnMessageCallback([state, remoteUrl, remoteUrlsMapping](
const WebSocketMessagePtr& msg) {
if (msg->type == ix::WebSocketMessageType::Open)
{
// Connect to the 'real' server
std::string url(remoteUrl);
// maybe we want a different url based on the mapping
std::string host = msg->openInfo.headers["Host"];
auto it = remoteUrlsMapping.find(host);
if (it != remoteUrlsMapping.end())
{
url = it->second;
}
// append the uri to form the full url
// (say ws://localhost:1234/foo/?bar=baz)
url += msg->openInfo.uri;
state->webSocket().setUrl(url);
state->webSocket().disableAutomaticReconnection();
state->webSocket().start();
// we should sleep here for a bit until we've established the
// connection with the remote server
while (state->webSocket().getReadyState() != ReadyState::Open)
{
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
else if (msg->type == ix::WebSocketMessageType::Close)
{
state->webSocket().close(msg->closeInfo.code, msg->closeInfo.reason);
}
else if (msg->type == ix::WebSocketMessageType::Message)
{
state->webSocket().send(msg->str, msg->binary);
}
});
}
});
}
else if (msg->type == ix::WebSocketMessageType::Close)
{
state->webSocket().close(msg->closeInfo.code, msg->closeInfo.reason);
}
else if (msg->type == ix::WebSocketMessageType::Message)
{
state->webSocket().send(msg->str, msg->binary);
}
});
}
});
auto res = server.listen();
if (!res.first)

View File

@ -7,18 +7,14 @@
#include "IXSocketTLSOptions.h"
#include <cstdint>
#include <map>
#include <stddef.h>
#include <string>
namespace ix
{
using RemoteUrlsMapping = std::map<std::string, std::string>;
int websocket_proxy_server_main(int port,
const std::string& hostname,
const ix::SocketTLSOptions& tlsOptions,
const std::string& remoteUrl,
const RemoteUrlsMapping& remoteUrlsMapping,
bool verbose);
} // namespace ix

View File

@ -77,29 +77,22 @@ namespace ix
}
void WebSocketServer::handleConnection(std::unique_ptr<Socket> socket,
std::shared_ptr<ConnectionState> connectionState)
std::shared_ptr<ConnectionState> connectionState,
std::unique_ptr<ConnectionInfo> connectionInfo)
{
setThreadName("WebSocketServer::" + connectionState->getId());
auto webSocket = std::make_shared<WebSocket>();
if (_onConnectionCallback)
{
_onConnectionCallback(webSocket, connectionState);
if (!webSocket->isOnMessageCallbackRegistered())
{
logError("WebSocketServer Application developer error: Server callback improperly "
"registerered.");
logError("Missing call to setOnMessageCallback inside setOnConnectionCallback.");
connectionState->setTerminated();
return;
}
_onConnectionCallback(webSocket, connectionState, std::move(connectionInfo));
}
else if (_onClientMessageCallback)
{
webSocket->setOnMessageCallback(
[this, &ws = *webSocket.get(), connectionState](const WebSocketMessagePtr& msg) {
_onClientMessageCallback(connectionState, ws, msg);
[this, &ws = *webSocket.get(), connectionState, &ci = *connectionInfo.get()](
const WebSocketMessagePtr& msg) {
_onClientMessageCallback(connectionState, ci, ws, msg);
});
}
else

View File

@ -23,10 +23,14 @@ namespace ix
{
public:
using OnConnectionCallback =
std::function<void(std::weak_ptr<WebSocket>, std::shared_ptr<ConnectionState>)>;
std::function<void(std::weak_ptr<WebSocket>,
std::shared_ptr<ConnectionState>,
std::unique_ptr<ConnectionInfo> connectionInfo)>;
using OnClientMessageCallback = std::function<void(
std::shared_ptr<ConnectionState>, WebSocket&, const WebSocketMessagePtr&)>;
using OnClientMessageCallback = std::function<void(std::shared_ptr<ConnectionState>,
ConnectionInfo&,
WebSocket&,
const WebSocketMessagePtr&)>;
WebSocketServer(int port = SocketServer::kDefaultPort,
const std::string& host = SocketServer::kDefaultHost,
@ -65,7 +69,8 @@ namespace ix
// Methods
virtual void handleConnection(std::unique_ptr<Socket> socket,
std::shared_ptr<ConnectionState> connectionState);
std::shared_ptr<ConnectionState> connectionState,
std::unique_ptr<ConnectionInfo> connectionInfo);
virtual size_t getConnectedClientsCount() final;
};
} // namespace ix

View File

@ -107,62 +107,36 @@ namespace ix
std::string protocol, host, path, query;
int port;
std::string remoteUrl(url);
WebSocketInitResult result;
const int maxRedirections = 10;
for (int i = 0; i < maxRedirections; ++i)
if (!UrlParser::parse(url, protocol, host, path, query, port))
{
if (!UrlParser::parse(remoteUrl, protocol, host, path, query, port))
{
std::stringstream ss;
ss << "Could not parse url: '" << url << "'";
return WebSocketInitResult(false, 0, ss.str());
}
std::string errorMsg;
bool tls = protocol == "wss";
_socket = createSocket(tls, -1, errorMsg, _socketTLSOptions);
_perMessageDeflate = std::make_unique<WebSocketPerMessageDeflate>();
if (!_socket)
{
return WebSocketInitResult(false, 0, errorMsg);
}
WebSocketHandshake webSocketHandshake(_requestInitCancellation,
_socket,
_perMessageDeflate,
_perMessageDeflateOptions,
_enablePerMessageDeflate);
result = webSocketHandshake.clientHandshake(
remoteUrl, headers, host, path, port, timeoutSecs);
if (result.http_status >= 300 && result.http_status < 400)
{
auto it = result.headers.find("Location");
if (it == result.headers.end())
{
std::stringstream ss;
ss << "Missing Location Header for HTTP Redirect response. "
<< "Rejecting connection to " << url << ", status: " << result.http_status;
result.errorStr = ss.str();
break;
}
remoteUrl = it->second;
continue;
}
if (result.success)
{
setReadyState(ReadyState::OPEN);
}
return result;
std::stringstream ss;
ss << "Could not parse url: '" << url << "'";
return WebSocketInitResult(false, 0, ss.str());
}
std::string errorMsg;
bool tls = protocol == "wss";
_socket = createSocket(tls, -1, errorMsg, _socketTLSOptions);
_perMessageDeflate = std::make_unique<WebSocketPerMessageDeflate>();
if (!_socket)
{
return WebSocketInitResult(false, 0, errorMsg);
}
WebSocketHandshake webSocketHandshake(_requestInitCancellation,
_socket,
_perMessageDeflate,
_perMessageDeflateOptions,
_enablePerMessageDeflate);
auto result =
webSocketHandshake.clientHandshake(url, headers, host, path, port, timeoutSecs);
if (result.success)
{
setReadyState(ReadyState::OPEN);
}
return result;
}
@ -659,7 +633,7 @@ namespace ix
// send back the CLOSE frame
sendCloseFrame(code, reason);
wakeUpFromPoll(SelectInterrupt::kCloseRequest);
wakeUpFromPoll(Socket::kCloseRequest);
bool remote = true;
closeSocketAndSwitchToClosedState(code, reason, _rxbuf.size(), remote);
@ -879,7 +853,7 @@ namespace ix
// Request to flush the send buffer on the background thread if it isn't empty
if (!isSendBufferEmpty())
{
wakeUpFromPoll(SelectInterrupt::kSendRequest);
wakeUpFromPoll(Socket::kSendRequest);
// FIXME: we should have a timeout when sending large messages: see #131
if (_blockingSend && !flushSendBuffer())
@ -1148,7 +1122,7 @@ namespace ix
sendCloseFrame(code, reason);
// wake up the poll, but do not close yet
wakeUpFromPoll(SelectInterrupt::kSendRequest);
wakeUpFromPoll(Socket::kSendRequest);
}
size_t WebSocketTransport::bufferedAmount() const

View File

@ -6,4 +6,4 @@
#pragma once
#define IX_WEBSOCKET_VERSION "10.4.7"
#define IX_WEBSOCKET_VERSION "10.1.1"

View File

@ -34,13 +34,10 @@ ws:
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_WS=1 .. && ninja install)
ws_install:
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=MinSizeRel -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_WS=1 .. -DUSE_TEST=0 && ninja install)
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=MinSizeRel -DUSE_ZLIB=0 -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_WS=1 .. && ninja install)
ws_install_release:
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=MinSizeRel -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_WS=1 .. -DUSE_TEST=0 && ninja install)
ws_openssl_install:
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_OPEN_SSL=1 .. ; ninja install)
ws_openssl:
mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_WS=1 -DUSE_OPEN_SSL=1 .. ; make -j 4)
ws_mbedtls:
mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_WS=1 -DUSE_MBED_TLS=1 .. ; make -j 4)
@ -48,9 +45,6 @@ ws_mbedtls:
ws_no_ssl:
mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_WS=1 .. ; make -j 4)
ws_no_python:
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=MinSizeRel -DUSE_TLS=1 -DUSE_WS=1 .. ; ninja install)
uninstall:
xargs rm -fv < build/install_manifest.txt

View File

@ -2,22 +2,39 @@
# Author: Benjamin Sergeant
# Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
#
cmake_minimum_required (VERSION 3.14)
cmake_minimum_required (VERSION 3.4.1)
project (ixwebsocket_unittest)
set (CMAKE_CXX_STANDARD 14)
option(USE_TLS "Add TLS support" ON)
if (MAC)
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/../third_party/sanitizers-cmake/cmake" ${CMAKE_MODULE_PATH})
find_package(Sanitizers)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread")
set(CMAKE_LD_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread")
option(USE_TLS "Add TLS support" ON)
endif()
include_directories(
${PROJECT_SOURCE_DIR}/Catch2/single_include
../third_party
../third_party/msgpack11
../third_party/spdlog/include
../ws
)
add_definitions(-DSPDLOG_COMPILED_LIB=1)
find_package(JsonCpp)
if (NOT JSONCPP_FOUND)
include_directories(../third_party/jsoncpp)
set(JSONCPP_SOURCES ../third_party/jsoncpp/jsoncpp.cpp)
endif()
# Shared sources
set (SOURCES
${JSONCPP_SOURCES}
test_runner.cpp
IXTest.cpp
../third_party/msgpack11/msgpack11.cpp
@ -98,7 +115,6 @@ target_link_libraries(ixwebsocket_unittest ixcrypto)
target_link_libraries(ixwebsocket_unittest ixcore)
target_link_libraries(ixwebsocket_unittest spdlog)
target_link_libraries(ixwebsocket_unittest jsoncpp_static)
if (USE_PYTHON)
target_link_libraries(ixwebsocket_unittest ${Python_LIBRARIES})
endif()

View File

@ -95,14 +95,15 @@ TEST_CASE("Cobra_to_sentry_bot", "[cobra_bots]")
sentryServer.setOnConnectionCallback(
[](HttpRequestPtr request,
std::shared_ptr<ConnectionState> connectionState) -> HttpResponsePtr {
std::shared_ptr<ConnectionState> /*connectionState*/,
std::unique_ptr<ConnectionInfo> connectionInfo) -> HttpResponsePtr {
WebSocketHttpHeaders headers;
headers["Server"] = userAgent();
// Log request
std::stringstream ss;
ss << connectionState->getRemoteIp() << ":" << connectionState->getRemotePort()
<< " " << request->method << " " << request->headers["User-Agent"] << " "
ss << connectionInfo->remoteIp << ":" << connectionInfo->remotePort << " "
<< request->method << " " << request->headers["User-Agent"] << " "
<< request->uri;
if (request->method == "POST")

View File

@ -63,54 +63,6 @@ TEST_CASE("http server", "[httpd]")
server.stop();
}
SECTION("Posting plain text data to a local HTTP server")
{
int port = getFreePort();
ix::HttpServer server(port, "127.0.0.1");
server.setOnConnectionCallback(
[](HttpRequestPtr request, std::shared_ptr<ConnectionState>) -> HttpResponsePtr {
if (request->method == "POST")
{
return std::make_shared<HttpResponse>(
200, "OK", HttpErrorCode::Ok, WebSocketHttpHeaders(), request->body);
}
return std::make_shared<HttpResponse>(400, "BAD REQUEST");
});
auto res = server.listen();
REQUIRE(res.first);
server.start();
HttpClient httpClient;
WebSocketHttpHeaders headers;
headers["Content-Type"] = "text/plain";
std::string url("http://127.0.0.1:");
url += std::to_string(port);
auto args = httpClient.createRequest(url);
args->extraHeaders = headers;
args->connectTimeout = 60;
args->transferTimeout = 60;
args->verbose = true;
args->logger = [](const std::string& msg) { std::cout << msg; };
args->body = "Hello World!";
auto response = httpClient.post(url, args->body, args);
std::cerr << "Status: " << response->statusCode << std::endl;
std::cerr << "Error message: " << response->errorMsg << std::endl;
std::cerr << "Body: " << response->body << std::endl;
REQUIRE(response->errorCode == HttpErrorCode::Ok);
REQUIRE(response->statusCode == 200);
REQUIRE(response->body == args->body);
server.stop();
}
}
TEST_CASE("http server redirection", "[httpd_redirect]")

View File

@ -85,10 +85,11 @@ namespace ix
bool startWebSocketEchoServer(ix::WebSocketServer& server)
{
server.setOnClientMessageCallback(
[&server](std::shared_ptr<ConnectionState> connectionState,
[&server](std::shared_ptr<ConnectionState> /*connectionState*/,
ConnectionInfo& connectionInfo,
WebSocket& webSocket,
const ix::WebSocketMessagePtr& msg) {
auto remoteIp = connectionState->getRemoteIp();
auto remoteIp = connectionInfo.remoteIp;
if (msg->type == ix::WebSocketMessageType::Open)
{
TLogger() << "New connection";

View File

@ -191,9 +191,11 @@ namespace
server.setOnClientMessageCallback(
[&server, &connectionId](std::shared_ptr<ConnectionState> connectionState,
ConnectionInfo& connectionInfo,
WebSocket& webSocket,
const ix::WebSocketMessagePtr& msg) {
auto remoteIp = connectionState->getRemoteIp();
auto remoteIp = connectionInfo.remoteIp;
if (msg->type == ix::WebSocketMessageType::Open)
{

View File

@ -195,9 +195,10 @@ namespace
{
server.setOnClientMessageCallback(
[&server](std::shared_ptr<ConnectionState> connectionState,
ConnectionInfo& connectionInfo,
WebSocket& webSocket,
const ix::WebSocketMessagePtr& msg) {
auto remoteIp = connectionState->getRemoteIp();
auto remoteIp = connectionInfo.remoteIp;
if (msg->type == ix::WebSocketMessageType::Open)
{
TLogger() << "New connection";
@ -285,27 +286,27 @@ TEST_CASE("Websocket_chat", "[websocket_chat]")
int attempts = 0;
while (chatA.getReceivedMessagesCount() != 3 || chatB.getReceivedMessagesCount() != 3)
{
CHECK(attempts++ < 10);
REQUIRE(attempts++ < 10);
ix::msleep(1000);
}
chatA.stop();
chatB.stop();
CHECK(chatA.getReceivedMessagesCount() == 3);
CHECK(chatB.getReceivedMessagesCount() == 3);
REQUIRE(chatA.getReceivedMessagesCount() == 3);
REQUIRE(chatB.getReceivedMessagesCount() == 3);
CHECK(chatB.getReceivedMessages()[0] == "from A1");
CHECK(chatB.getReceivedMessages()[1] == "from A2");
CHECK(chatB.getReceivedMessages()[2] == "from A3");
REQUIRE(chatB.getReceivedMessages()[0] == "from A1");
REQUIRE(chatB.getReceivedMessages()[1] == "from A2");
REQUIRE(chatB.getReceivedMessages()[2] == "from A3");
CHECK(chatA.getReceivedMessages()[0] == "from B1");
CHECK(chatA.getReceivedMessages()[1] == "from B2");
CHECK(chatA.getReceivedMessages()[2].size() == bigMessage.size());
REQUIRE(chatA.getReceivedMessages()[0] == "from B1");
REQUIRE(chatA.getReceivedMessages()[1] == "from B2");
REQUIRE(chatA.getReceivedMessages()[2].size() == bigMessage.size());
// Give us 1000ms for the server to notice that clients went away
ix::msleep(1000);
CHECK(server.getClients().size() == 0);
REQUIRE(server.getClients().size() == 0);
ix::reportWebSocketTraffic();
}

View File

@ -171,9 +171,10 @@ namespace
server.setOnClientMessageCallback(
[&receivedCloseCode, &receivedCloseReason, &receivedCloseRemote, &mutexWrite](
std::shared_ptr<ConnectionState> connectionState,
ConnectionInfo& connectionInfo,
WebSocket& /*webSocket*/,
const ix::WebSocketMessagePtr& msg) {
auto remoteIp = connectionState->getRemoteIp();
auto remoteIp = connectionInfo.remoteIp;
if (msg->type == ix::WebSocketMessageType::Open)
{
TLogger() << "New server connection";

View File

@ -35,9 +35,11 @@ namespace ix
server.setOnClientMessageCallback(
[&server, &connectionId](std::shared_ptr<ConnectionState> connectionState,
ConnectionInfo& connectionInfo,
WebSocket& webSocket,
const ix::WebSocketMessagePtr& msg) {
auto remoteIp = connectionState->getRemoteIp();
auto remoteIp = connectionInfo.remoteIp;
if (msg->type == ix::WebSocketMessageType::Open)
{

View File

@ -18,9 +18,10 @@ bool startServer(ix::WebSocketServer& server, std::string& subProtocols)
{
server.setOnClientMessageCallback(
[&server, &subProtocols](std::shared_ptr<ConnectionState> connectionState,
ConnectionInfo& connectionInfo,
WebSocket& webSocket,
const ix::WebSocketMessagePtr& msg) {
auto remoteIp = connectionState->getRemoteIp();
auto remoteIp = connectionInfo.remoteIp;
if (msg->type == ix::WebSocketMessageType::Open)
{
TLogger() << "New connection";

View File

@ -1,171 +0,0 @@
/*
* lws-minimal-ws-client
*
* Written in 2010-2019 by Andy Green <andy@warmcat.com>
*
* This file is made available under the Creative Commons CC0 1.0
* Universal Public Domain Dedication.
*
* This demonstrates the a minimal ws client using lws.
*
* Original programs connects to https://libwebsockets.org/ and makes a
* wss connection to the dumb-increment protocol there. While
* connected, it prints the numbers it is being sent by
* dumb-increment protocol.
*
* This is modified to make a test client which counts how much messages
* per second can be received.
*
* libwebsockets$ make && ./a.out
* g++ --std=c++14 -I/usr/local/opt/openssl/include devnull_client.cpp -lwebsockets
* messages received: 0 per second 0 total
* [2020/08/02 19:22:21:4774] U: LWS minimal ws client rx [-d <logs>] [--h2]
* [2020/08/02 19:22:21:4814] U: callback_dumb_increment: established
* messages received: 0 per second 0 total
* messages received: 180015 per second 180015 total
* messages received: 172866 per second 352881 total
* messages received: 176177 per second 529058 total
* messages received: 174191 per second 703249 total
* messages received: 193397 per second 896646 total
* messages received: 196385 per second 1093031 total
* messages received: 194593 per second 1287624 total
* messages received: 189484 per second 1477108 total
* messages received: 200825 per second 1677933 total
* messages received: 183542 per second 1861475 total
* ^C[2020/08/02 19:22:33:4450] U: Completed OK
*
*/
#include <atomic>
#include <iostream>
#include <libwebsockets.h>
#include <signal.h>
#include <string.h>
#include <thread>
static int interrupted;
static struct lws* client_wsi;
std::atomic<uint64_t> receivedCount(0);
static int callback_dumb_increment(
struct lws* wsi, enum lws_callback_reasons reason, void* user, void* in, size_t len)
{
switch (reason)
{
/* because we are protocols[0] ... */
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
lwsl_err("CLIENT_CONNECTION_ERROR: %s\n", in ? (char*) in : "(null)");
client_wsi = NULL;
break;
case LWS_CALLBACK_CLIENT_ESTABLISHED: lwsl_user("%s: established\n", __func__); break;
case LWS_CALLBACK_CLIENT_RECEIVE: receivedCount++; break;
case LWS_CALLBACK_CLIENT_CLOSED: client_wsi = NULL; break;
default: break;
}
return lws_callback_http_dummy(wsi, reason, user, in, len);
}
static const struct lws_protocols protocols[] = {{
"dumb-increment-protocol",
callback_dumb_increment,
0,
0,
},
{NULL, NULL, 0, 0}};
static void sigint_handler(int sig)
{
interrupted = 1;
}
int main(int argc, const char** argv)
{
uint64_t receivedCountTotal(0);
uint64_t receivedCountPerSecs(0);
auto timer = [&receivedCountTotal, &receivedCountPerSecs] {
while (!interrupted)
{
std::cerr << "messages received: " << receivedCountPerSecs << " per second "
<< receivedCountTotal << " total" << std::endl;
receivedCountPerSecs = receivedCount - receivedCountTotal;
receivedCountTotal += receivedCountPerSecs;
auto duration = std::chrono::seconds(1);
std::this_thread::sleep_for(duration);
}
};
std::thread t1(timer);
struct lws_context_creation_info info;
struct lws_client_connect_info i;
struct lws_context* context;
const char* p;
int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE
/* for LLL_ verbosity above NOTICE to be built into lws, lws
* must have been configured with -DCMAKE_BUILD_TYPE=DEBUG
* instead of =RELEASE */
/* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */
/* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */
/* | LLL_DEBUG */;
signal(SIGINT, sigint_handler);
if ((p = lws_cmdline_option(argc, argv, "-d"))) logs = atoi(p);
lws_set_log_level(logs, NULL);
lwsl_user("LWS minimal ws client rx [-d <logs>] [--h2]\n");
memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
info.port = CONTEXT_PORT_NO_LISTEN; /* we do not run any server */
info.protocols = protocols;
info.timeout_secs = 10;
/*
* since we know this lws context is only ever going to be used with
* one client wsis / fds / sockets at a time, let lws know it doesn't
* have to use the default allocations for fd tables up to ulimit -n.
* It will just allocate for 1 internal and 1 (+ 1 http2 nwsi) that we
* will use.
*/
info.fd_limit_per_thread = 1 + 1 + 1;
context = lws_create_context(&info);
if (!context)
{
lwsl_err("lws init failed\n");
return 1;
}
memset(&i, 0, sizeof i); /* otherwise uninitialized garbage */
i.context = context;
i.port = 8008;
i.address = "127.0.0.1";
i.path = "/";
i.host = i.address;
i.origin = i.address;
i.protocol = protocols[0].name; /* "dumb-increment-protocol" */
i.pwsi = &client_wsi;
if (lws_cmdline_option(argc, argv, "--h2")) i.alpn = "h2";
lws_client_connect_via_info(&i);
while (n >= 0 && client_wsi && !interrupted)
n = lws_service(context, 0);
lws_context_destroy(context);
lwsl_user("Completed %s\n", receivedCount > 10 ? "OK" : "Failed");
t1.join();
return receivedCount > 10;
}

View File

@ -1,2 +0,0 @@
bin
obj

View File

@ -1,99 +0,0 @@
//
// Main.cs
// Author: Benjamin Sergeant
// Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
//
// In a different terminal, start a push server:
// $ ws push_server -q
//
// $ dotnet run
// messages received per second: 145157
// messages received per second: 141405
// messages received per second: 152202
// messages received per second: 157149
// messages received per second: 157673
// messages received per second: 153594
// messages received per second: 157830
// messages received per second: 158422
//
using System;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
public class DevNullClientCli
{
private static int receivedMessage = 0;
public static async Task<byte[]> ReceiveAsync(ClientWebSocket ws, CancellationToken token)
{
int bufferSize = 8192; // 8K
var buffer = new byte[bufferSize];
var offset = 0;
var free = buffer.Length;
while (true)
{
var result = await ws.ReceiveAsync(new ArraySegment<byte>(buffer, offset, free), token).ConfigureAwait(false);
offset += result.Count;
free -= result.Count;
if (result.EndOfMessage) break;
if (free == 0)
{
// No free space
// Resize the outgoing buffer
var newSize = buffer.Length + bufferSize;
var newBuffer = new byte[newSize];
Array.Copy(buffer, 0, newBuffer, 0, offset);
buffer = newBuffer;
free = buffer.Length - offset;
}
}
return buffer;
}
private static void OnTimedEvent(object source, EventArgs e)
{
Console.WriteLine($"messages received per second: {receivedMessage}");
receivedMessage = 0;
}
public static async Task ReceiveMessagesAsync(string url)
{
var ws = new ClientWebSocket();
System.Uri uri = new System.Uri(url);
var cancellationToken = CancellationToken.None;
try
{
await ws.ConnectAsync(uri, cancellationToken).ConfigureAwait(false);
while (true)
{
var data = await DevNullClientCli.ReceiveAsync(ws, cancellationToken);
receivedMessage += 1;
}
}
catch (System.Net.WebSockets.WebSocketException e)
{
Console.WriteLine($"WebSocket error: {e}");
return;
}
}
public static async Task Main()
{
var timer = new System.Timers.Timer(1000);
timer.Elapsed += OnTimedEvent;
timer.Enabled = true;
timer.Start();
var url = "ws://localhost:8008";
await ReceiveMessagesAsync(url);
}
}

View File

@ -1,6 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
</Project>

View File

@ -1,42 +0,0 @@
//
// With ws@7.3.1
// and
// node --version
// v13.11.0
//
// In a different terminal, start a push server:
// $ ws push_server -q
//
// $ node devnull_client.js
// messages received per second: 16643
// messages received per second: 28065
// messages received per second: 28432
// messages received per second: 22207
// messages received per second: 28805
// messages received per second: 28694
// messages received per second: 28180
// messages received per second: 28601
// messages received per second: 28698
// messages received per second: 28931
// messages received per second: 27975
//
const WebSocket = require('ws');
const ws = new WebSocket('ws://localhost:8008');
ws.on('open', function open() {
ws.send('hello from node');
});
var receivedMessages = 0;
setInterval(function timeout() {
console.log(`messages received per second: ${receivedMessages}`)
receivedMessages = 0;
}, 1000);
ws.on('message', function incoming(data) {
receivedMessages += 1;
});

View File

@ -1,44 +0,0 @@
#!/usr/bin/env python3
# websocket send client
import argparse
import asyncio
import websockets
try:
import uvloop
uvloop.install()
except ImportError:
print('uvloop not available')
pass
msgCount = 0
async def timer():
global msgCount
while True:
print(f'Received messages: {msgCount}')
msgCount = 0
await asyncio.sleep(1)
async def client(url):
global msgCount
asyncio.ensure_future(timer())
async with websockets.connect(url) as ws:
async for message in ws:
msgCount += 1
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='websocket proxy.')
parser.add_argument('--url', help='Remote websocket url',
default='wss://echo.websocket.org')
args = parser.parse_args()
asyncio.get_event_loop().run_until_complete(client(args.url))

View File

@ -10,7 +10,7 @@ import websockets
async def echo(websocket, path):
while True:
msg = await websocket.recv()
# print(f'Received {len(msg)} bytes')
print(f'Received {len(msg)} bytes')
await websocket.send(msg)
host = os.getenv('BIND_HOST', 'localhost')

View File

@ -1,6 +0,0 @@
```
export GEM_HOME=$HOME/local/gems
bundle install faye-websocket
```
https://stackoverflow.com/questions/486995/ruby-equivalent-of-virtualenv

View File

@ -1,59 +0,0 @@
#
# $ ruby --version
# ruby 2.6.3p62 (2019-04-16 revision 67580) [universal.x86_64-darwin19]
#
# Install a gem locally by setting GEM_HOME
# https://stackoverflow.com/questions/486995/ruby-equivalent-of-virtualenv
# export GEM_HOME=$HOME/local/gems
# bundle install faye-websocket
#
# In a different terminal, start a push server:
# $ ws push_server -q
#
# $ ruby devnull_client.rb
# [:open]
# Connected to server
# messages received per second: 115926
# messages received per second: 119156
# messages received per second: 119156
# messages received per second: 119157
# messages received per second: 119156
# messages received per second: 119156
# messages received per second: 119157
# messages received per second: 119156
# messages received per second: 119156
# messages received per second: 119157
# messages received per second: 119156
# messages received per second: 119157
# messages received per second: 119156
# ^C[:close, 1006, ""]
#
require 'faye/websocket'
require 'eventmachine'
EM.run {
ws = Faye::WebSocket::Client.new('ws://127.0.0.1:8008')
counter = 0
EM.add_periodic_timer(1) do
print "messages received per second: #{counter}\n"
counter = 0 # reset counter
end
ws.on :open do |event|
p [:open]
print "Connected to server\n"
end
ws.on :message do |event|
# Uncomment the next line to validate that we receive something correct
# p [:message, event.data]
counter += 1
end
ws.on :close do |event|
p [:close, event.code, event.reason]
ws = nil
end
}

316
third_party/jsoncpp/json/json-forwards.h vendored Normal file
View File

@ -0,0 +1,316 @@
/// Json-cpp amalgamated forward header (http://jsoncpp.sourceforge.net/).
/// It is intended to be used with #include "json/json-forwards.h"
/// This header provides forward declaration for all JsonCpp types.
// //////////////////////////////////////////////////////////////////////
// Beginning of content of file: LICENSE
// //////////////////////////////////////////////////////////////////////
/*
The JsonCpp library's source code, including accompanying documentation,
tests and demonstration applications, are licensed under the following
conditions...
Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all
jurisdictions which recognize such a disclaimer. In such jurisdictions,
this software is released into the Public Domain.
In jurisdictions which do not recognize Public Domain property (e.g. Germany as of
2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur and
The JsonCpp Authors, and is released under the terms of the MIT License (see below).
In jurisdictions which recognize Public Domain property, the user of this
software may choose to accept it either as 1) Public Domain, 2) under the
conditions of the MIT License (see below), or 3) under the terms of dual
Public Domain/MIT License conditions described here, as they choose.
The MIT License is about as close to Public Domain as a license can get, and is
described in clear, concise terms at:
http://en.wikipedia.org/wiki/MIT_License
The full text of the MIT License follows:
========================================================================
Copyright (c) 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
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.
========================================================================
(END LICENSE TEXT)
The MIT license is compatible with both the GPL and commercial
software, affording one all of the rights of Public Domain with the
minor nuisance of being required to keep the above copyright notice
and license text in the source code. Note also that by accepting the
Public Domain "license" you can re-license your copy using whatever
license you like.
*/
// //////////////////////////////////////////////////////////////////////
// End of content of file: LICENSE
// //////////////////////////////////////////////////////////////////////
#ifndef JSON_FORWARD_AMALGAMATED_H_INCLUDED
# define JSON_FORWARD_AMALGAMATED_H_INCLUDED
/// If defined, indicates that the source file is amalgamated
/// to prevent private header inclusion.
#define JSON_IS_AMALGAMATION
// //////////////////////////////////////////////////////////////////////
// Beginning of content of file: include/json/config.h
// //////////////////////////////////////////////////////////////////////
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_CONFIG_H_INCLUDED
#define JSON_CONFIG_H_INCLUDED
#include <cstddef>
#include <cstdint>
#include <istream>
#include <memory>
#include <ostream>
#include <sstream>
#include <string>
#include <type_traits>
// If non-zero, the library uses exceptions to report bad input instead of C
// assertion macros. The default is to use exceptions.
#ifndef JSON_USE_EXCEPTION
#define JSON_USE_EXCEPTION 1
#endif
// Temporary, tracked for removal with issue #982.
#ifndef JSON_USE_NULLREF
#define JSON_USE_NULLREF 1
#endif
/// If defined, indicates that the source file is amalgamated
/// to prevent private header inclusion.
/// Remarks: it is automatically defined in the generated amalgamated header.
// #define JSON_IS_AMALGAMATION
// Export macros for DLL visibility
#if defined(JSON_DLL_BUILD)
#if defined(_MSC_VER) || defined(__MINGW32__)
#define JSON_API __declspec(dllexport)
#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
#elif defined(__GNUC__) || defined(__clang__)
#define JSON_API __attribute__((visibility("default")))
#endif // if defined(_MSC_VER)
#elif defined(JSON_DLL)
#if defined(_MSC_VER) || defined(__MINGW32__)
#define JSON_API __declspec(dllimport)
#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
#endif // if defined(_MSC_VER)
#endif // ifdef JSON_DLL_BUILD
#if !defined(JSON_API)
#define JSON_API
#endif
#if defined(_MSC_VER) && _MSC_VER < 1800
#error \
"ERROR: Visual Studio 12 (2013) with _MSC_VER=1800 is the oldest supported compiler with sufficient C++11 capabilities"
#endif
#if defined(_MSC_VER) && _MSC_VER < 1900
// As recommended at
// https://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010
extern JSON_API int msvc_pre1900_c99_snprintf(char* outBuf, size_t size,
const char* format, ...);
#define jsoncpp_snprintf msvc_pre1900_c99_snprintf
#else
#define jsoncpp_snprintf std::snprintf
#endif
// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for
// integer
// Storages, and 64 bits integer support is disabled.
// #define JSON_NO_INT64 1
// JSONCPP_OVERRIDE is maintained for backwards compatibility of external tools.
// C++11 should be used directly in JSONCPP.
#define JSONCPP_OVERRIDE override
#if __cplusplus >= 201103L
#define JSONCPP_NOEXCEPT noexcept
#define JSONCPP_OP_EXPLICIT explicit
#elif defined(_MSC_VER) && _MSC_VER < 1900
#define JSONCPP_NOEXCEPT throw()
#define JSONCPP_OP_EXPLICIT explicit
#elif defined(_MSC_VER) && _MSC_VER >= 1900
#define JSONCPP_NOEXCEPT noexcept
#define JSONCPP_OP_EXPLICIT explicit
#else
#define JSONCPP_NOEXCEPT throw()
#define JSONCPP_OP_EXPLICIT
#endif
#ifdef __clang__
#if __has_extension(attribute_deprecated_with_message)
#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message)))
#endif
#elif defined(__GNUC__) // not clang (gcc comes later since clang emulates gcc)
#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message)))
#elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
#define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__))
#endif // GNUC version
#elif defined(_MSC_VER) // MSVC (after clang because clang on Windows emulates
// MSVC)
#define JSONCPP_DEPRECATED(message) __declspec(deprecated(message))
#endif // __clang__ || __GNUC__ || _MSC_VER
#if !defined(JSONCPP_DEPRECATED)
#define JSONCPP_DEPRECATED(message)
#endif // if !defined(JSONCPP_DEPRECATED)
#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ >= 6))
#define JSON_USE_INT64_DOUBLE_CONVERSION 1
#endif
#if !defined(JSON_IS_AMALGAMATION)
#include "allocator.h"
#include "version.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
namespace Json {
using Int = int;
using UInt = unsigned int;
#if defined(JSON_NO_INT64)
using LargestInt = int;
using LargestUInt = unsigned int;
#undef JSON_HAS_INT64
#else // if defined(JSON_NO_INT64)
// For Microsoft Visual use specific types as long long is not supported
#if defined(_MSC_VER) // Microsoft Visual Studio
using Int64 = __int64;
using UInt64 = unsigned __int64;
#else // if defined(_MSC_VER) // Other platforms, use long long
using Int64 = int64_t;
using UInt64 = uint64_t;
#endif // if defined(_MSC_VER)
using LargestInt = Int64;
using LargestUInt = UInt64;
#define JSON_HAS_INT64
#endif // if defined(JSON_NO_INT64)
template <typename T>
using Allocator =
typename std::conditional<JSONCPP_USING_SECURE_MEMORY, SecureAllocator<T>,
std::allocator<T>>::type;
using String = std::basic_string<char, std::char_traits<char>, Allocator<char>>;
using IStringStream =
std::basic_istringstream<String::value_type, String::traits_type,
String::allocator_type>;
using OStringStream =
std::basic_ostringstream<String::value_type, String::traits_type,
String::allocator_type>;
using IStream = std::istream;
using OStream = std::ostream;
} // namespace Json
// Legacy names (formerly macros).
using JSONCPP_STRING = Json::String;
using JSONCPP_ISTRINGSTREAM = Json::IStringStream;
using JSONCPP_OSTRINGSTREAM = Json::OStringStream;
using JSONCPP_ISTREAM = Json::IStream;
using JSONCPP_OSTREAM = Json::OStream;
#endif // JSON_CONFIG_H_INCLUDED
// //////////////////////////////////////////////////////////////////////
// End of content of file: include/json/config.h
// //////////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////////
// Beginning of content of file: include/json/forwards.h
// //////////////////////////////////////////////////////////////////////
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_FORWARDS_H_INCLUDED
#define JSON_FORWARDS_H_INCLUDED
#if !defined(JSON_IS_AMALGAMATION)
#include "config.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
namespace Json {
// writer.h
class StreamWriter;
class StreamWriterBuilder;
class Writer;
class FastWriter;
class StyledWriter;
class StyledStreamWriter;
// reader.h
class Reader;
class CharReader;
class CharReaderBuilder;
// json_features.h
class Features;
// value.h
using ArrayIndex = unsigned int;
class StaticString;
class Path;
class PathArgument;
class Value;
class ValueIteratorBase;
class ValueIterator;
class ValueConstIterator;
} // namespace Json
#endif // JSON_FORWARDS_H_INCLUDED
// //////////////////////////////////////////////////////////////////////
// End of content of file: include/json/forwards.h
// //////////////////////////////////////////////////////////////////////
#endif //ifndef JSON_FORWARD_AMALGAMATED_H_INCLUDED

2354
third_party/jsoncpp/json/json.h vendored Normal file

File diff suppressed because it is too large Load Diff

5304
third_party/jsoncpp/jsoncpp.cpp vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

108
third_party/spdlog/.clang-format vendored Normal file
View File

@ -0,0 +1,108 @@
---
Language: Cpp
# BasedOnStyle: LLVM
AccessModifierOffset: -4
AlignAfterOpenBracket: DontAlign
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Right
AlignOperands: true
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: true
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: true
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterClass: true
AfterControlStatement: true
AfterEnum: true
AfterFunction: true
AfterNamespace: false
AfterObjCDeclaration: true
AfterStruct: true
AfterUnion: true
BeforeCatch: true
BeforeElse: true
IndentBraces: false
SplitEmptyFunction: false
SplitEmptyRecord: false
SplitEmptyNamespace: false
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Custom
BreakBeforeInheritanceComma: false
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: true
BreakConstructorInitializers: BeforeColon
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 140
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IncludeCategories:
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 2
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
Priority: 3
- Regex: '.*'
Priority: 1
IncludeIsMainRegex: '(Test)?$'
IndentCaseLabels: false
IndentWidth: 4
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right
ReflowComments: true
SortIncludes: false
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp11
TabWidth: 8
UseTab: Never
...

54
third_party/spdlog/.clang-tidy vendored Normal file
View File

@ -0,0 +1,54 @@
Checks: 'cppcoreguidelines-*,
performance-*,
modernize-*,
google-*,
misc-*,
cert-*,
readability-*,
clang-analyzer-*,
-performance-unnecessary-value-param,
-modernize-use-trailing-return-type,
-google-runtime-references,
-misc-non-private-member-variables-in-classes,
-readability-braces-around-statements,
-google-readability-braces-around-statements,
-cppcoreguidelines-avoid-magic-numbers,
-readability-magic-numbers,
-readability-magic-numbers,
-cppcoreguidelines-pro-type-vararg,
-cppcoreguidelines-pro-bounds-pointer-arithmetic,
-cppcoreguidelines-avoid-c-arrays,
-modernize-avoid-c-arrays,
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
-readability-named-parameter,
-cert-env33-c
'
WarningsAsErrors: ''
HeaderFilterRegex: '*spdlog/[^f].*'
AnalyzeTemporaryDtors: false
FormatStyle: none
CheckOptions:
- key: google-readability-braces-around-statements.ShortStatementLines
value: '1'
- key: google-readability-function-size.StatementThreshold
value: '800'
- key: google-readability-namespace-comments.ShortNamespaceLines
value: '10'
- key: google-readability-namespace-comments.SpacesBeforeComments
value: '2'
- key: modernize-loop-convert.MaxCopySize
value: '16'
- key: modernize-loop-convert.MinConfidence
value: reasonable
- key: modernize-loop-convert.NamingStyle
value: CamelCase
- key: modernize-pass-by-value.IncludeStyle
value: llvm
- key: modernize-replace-auto-ptr.IncludeStyle
value: llvm
- key: modernize-use-nullptr.NullMacros
value: 'NULL'

1
third_party/spdlog/.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
* text=false

83
third_party/spdlog/.gitignore vendored Normal file
View File

@ -0,0 +1,83 @@
# Auto generated files
build/*
*.slo
*.lo
*.o
*.obj
*.suo
*.tlog
*.ilk
*.log
*.pdb
*.idb
*.iobj
*.ipdb
*.opensdf
*.sdf
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
# Codelite
.codelite
# KDevelop
*.kdev4
# .orig files
*.orig
# example files
example/*
!example/example.cpp
!example/bench.cpp
!example/utils.h
!example/Makefile*
!example/example.sln
!example/example.vcxproj
!example/CMakeLists.txt
!example/meson.build
!example/multisink.cpp
!example/jni
# generated files
generated
# Cmake
CMakeCache.txt
CMakeFiles
CMakeScripts
Makefile
cmake_install.cmake
install_manifest.txt
/tests/tests.VC.VC.opendb
/tests/tests.VC.db
/tests/tests
/tests/logs/*
# idea
.idea/
cmake-build-*/
*.db
*.ipch
*.filters
*.db-wal
*.opendb
*.db-shm
*.vcxproj
*.tcl
*.user
*.sln

112
third_party/spdlog/.travis.yml vendored Normal file
View File

@ -0,0 +1,112 @@
# Adapted from various sources, including:
# - Louis Dionne's Hana: https://github.com/ldionne/hana
# - Paul Fultz II's FIT: https://github.com/pfultz2/Fit
# - Eric Niebler's range-v3: https://github.com/ericniebler/range-v3
sudo: required
language: cpp
# gcc 4.8
addons: &gcc48
apt:
packages:
- g++-4.8
sources:
- ubuntu-toolchain-r-test
# gcc 7.0
addons: &gcc7
apt:
packages:
- g++-7
sources:
- ubuntu-toolchain-r-test
# Clang 3.5
addons: &clang35
apt:
packages:
- clang-3.5
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-precise-3.5
# Clang 7.0
addons: &clang70
apt:
packages:
- clang-7
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-trusty-7
matrix:
include:
# Test gcc-4.8: C++11, Build=Debug/Release
- env: GCC_VERSION=4.8 BUILD_TYPE=Debug CPP=11
os: linux
addons: *gcc48
- env: GCC_VERSION=4.8 BUILD_TYPE=Release CPP=11
os: linux
addons: *gcc48
- env: GCC_VERSION=7 BUILD_TYPE=Release CPP=11
os: linux
addons: *gcc7
# Test clang-3.5: C++11, Build=Debug/Release
- env: CLANG_VERSION=3.5 BUILD_TYPE=Debug CPP=11
os: linux
addons: *clang35
- env: CLANG_VERSION=3.5 BUILD_TYPE=Release CPP=11
os: linux
addons: *clang35
# Test clang-7.0: C++11, Build=Debug, ASAN=On
- env: CLANG_VERSION=7 BUILD_TYPE=Debug CPP=11 ASAN=On TSAN=Off
dist: bionic
- env: CLANG_VERSION=7 BUILD_TYPE=Release CPP=11 ASAN=On TSAN=Off
dist: bionic
# osx
- env: BUILD_TYPE=Release CPP=11 ASAN=Off TSAN=Off
os: osx
before_script:
- if [ -n "$GCC_VERSION" ]; then export CXX="g++-${GCC_VERSION}" CC="gcc-${GCC_VERSION}"; fi
- if [ -n "$CLANG_VERSION" ]; then export CXX="clang++-${CLANG_VERSION}" CC="clang-${CLANG_VERSION}"; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export CXX="clang++" CC="clang"; fi
- which $CXX
- which $CC
- $CXX --version
- cmake --version
script:
- cd ${TRAVIS_BUILD_DIR}
- mkdir -p build && cd build
- |
cmake .. \
--warn-uninitialized \
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
-DCMAKE_CXX_STANDARD=$CPP \
-DSPDLOG_BUILD_EXAMPLE=ON \
-DSPDLOG_BUILD_EXAMPLE_HO=ON \
-DSPDLOG_ENABLE_WARNINGS=ON \
-DSPDLOG_BUILD_BENCH=OFF \
-DSPDLOG_BUILD_TESTS=ON \
-DSPDLOG_BUILD_TESTS_HO=OFf \
-DSPDLOG_SANITIZE_ADDRESS=$ASAN
- make VERBOSE=1 -j2
- ctest -j2 --output-on-failure
notifications:
email: false

335
third_party/spdlog/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,335 @@
# Copyright(c) 2019 spdlog authors
# Distributed under the MIT License (http://opensource.org/licenses/MIT)
cmake_minimum_required(VERSION 3.2)
if(${CMAKE_VERSION} VERSION_LESS 3.11)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
else()
cmake_policy(VERSION 3.11)
endif()
ENABLE_LANGUAGE(C)
#---------------------------------------------------------------------------------------
# Start spdlog project
#---------------------------------------------------------------------------------------
include(cmake/utils.cmake)
include(cmake/ide.cmake)
spdlog_extract_version()
project(spdlog VERSION ${SPDLOG_VERSION} LANGUAGES CXX)
message(STATUS "Build spdlog: ${SPDLOG_VERSION}")
include(GNUInstallDirs)
#---------------------------------------------------------------------------------------
# Set default build to release
#---------------------------------------------------------------------------------------
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose Release or Debug" FORCE)
endif()
#---------------------------------------------------------------------------------------
# Compiler config
#---------------------------------------------------------------------------------------
if (NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
endif()
set(CMAKE_CXX_EXTENSIONS OFF)
if(CMAKE_SYSTEM_NAME MATCHES "CYGWIN")
set(CMAKE_CXX_EXTENSIONS ON)
endif()
#---------------------------------------------------------------------------------------
# Set SPDLOG_MASTER_PROJECT to ON if we are building spdlog
#---------------------------------------------------------------------------------------
# Check if spdlog is being used directly or via add_subdirectory, but allow overriding
if (NOT DEFINED SPDLOG_MASTER_PROJECT)
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
set(SPDLOG_MASTER_PROJECT ON)
else()
set(SPDLOG_MASTER_PROJECT OFF)
endif()
endif ()
# build shared option
option(SPDLOG_BUILD_SHARED "Build shared library" OFF)
# precompiled headers option
option(SPDLOG_ENABLE_PCH "Build static or shared library using precompiled header to speed up compilation time" OFF)
# example options
option(SPDLOG_BUILD_EXAMPLE "Build example" ${SPDLOG_MASTER_PROJECT})
option(SPDLOG_BUILD_EXAMPLE_HO "Build header only example" OFF)
# testing options
option(SPDLOG_BUILD_TESTS "Build tests" OFF)
option(SPDLOG_BUILD_TESTS_HO "Build tests using the header only version" OFF)
# bench options
option(SPDLOG_BUILD_BENCH "Build benchmarks (Requires https://github.com/google/benchmark.git to be installed)" OFF)
# sanitizer options
option(SPDLOG_SANITIZE_ADDRESS "Enable address sanitizer in tests" OFF)
# warning options
option(SPDLOG_BUILD_WARNINGS "Enable compiler warnings" OFF)
# install options
option(SPDLOG_INSTALL "Generate the install target" ${SPDLOG_MASTER_PROJECT})
option(SPDLOG_FMT_EXTERNAL "Use external fmt library instead of bundled" OFF)
option(SPDLOG_FMT_EXTERNAL_HO "Use external fmt header-only library instead of bundled" OFF)
option(SPDLOG_NO_EXCEPTIONS "Compile with -fno-exceptions. Call abort() on any spdlog exceptions" OFF)
if (SPDLOG_FMT_EXTERNAL AND SPDLOG_FMT_EXTERNAL_HO)
message(FATAL_ERROR "SPDLOG_FMT_EXTERNAL and SPDLOG_FMT_EXTERNAL_HO are mutually exclusive")
endif()
# misc tweakme options
if(WIN32)
option(SPDLOG_WCHAR_SUPPORT "Support wchar api" OFF)
option(SPDLOG_WCHAR_FILENAMES "Support wchar filenames" OFF)
endif()
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
option(SPDLOG_CLOCK_COARSE "Use the much faster (but much less accurate) CLOCK_REALTIME_COARSE instead of the regular clock," OFF)
endif()
option(SPDLOG_PREVENT_CHILD_FD "Prevent from child processes to inherit log file descriptors" OFF)
option(SPDLOG_NO_THREAD_ID "prevent spdlog from querying the thread id on each log call if thread id is not needed" OFF)
option(SPDLOG_NO_TLS "prevent spdlog from using thread local storage" OFF)
option(SPDLOG_NO_ATOMIC_LEVELS "prevent spdlog from using of std::atomic log levels (use only if your code never modifies log levels concurrently" OFF)
# clang-tidy
if(${CMAKE_VERSION} VERSION_GREATER "3.5")
option(SPDLOG_TIDY "run clang-tidy" OFF)
endif()
if(SPDLOG_TIDY)
set(CMAKE_CXX_CLANG_TIDY "clang-tidy")
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
message(STATUS "Enabled clang-tidy")
endif()
find_package(Threads REQUIRED)
message(STATUS "Build type: " ${CMAKE_BUILD_TYPE})
#---------------------------------------------------------------------------------------
# Static/Shared library (shared not supported in windows yet)
#---------------------------------------------------------------------------------------
set(SPDLOG_SRCS
src/spdlog.cpp
src/stdout_sinks.cpp
src/color_sinks.cpp
src/file_sinks.cpp
src/async.cpp
src/cfg.cpp)
if(NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO)
list(APPEND SPDLOG_SRCS src/fmt.cpp)
endif()
if(WIN32 AND SPDLOG_BUILD_SHARED)
list(APPEND SPDLOG_SRCS ${CMAKE_CURRENT_BINARY_DIR}/version.rc)
endif()
if (SPDLOG_BUILD_SHARED)
add_library(spdlog SHARED ${SPDLOG_SRCS} ${SPDLOG_ALL_HEADERS})
target_compile_definitions(spdlog PUBLIC SPDLOG_SHARED_LIB)
if(WIN32)
target_compile_options(spdlog PUBLIC /wd4251 /wd4275)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/version.rc.in ${CMAKE_CURRENT_BINARY_DIR}/version.rc @ONLY)
endif()
if(NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO)
target_compile_definitions(spdlog PRIVATE FMT_EXPORT PUBLIC FMT_SHARED)
endif()
else()
add_library(spdlog STATIC ${SPDLOG_SRCS} ${SPDLOG_ALL_HEADERS})
endif()
add_library(spdlog::spdlog ALIAS spdlog)
target_compile_definitions(spdlog PUBLIC SPDLOG_COMPILED_LIB)
target_include_directories(spdlog PUBLIC
"$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>")
target_link_libraries(spdlog PUBLIC Threads::Threads)
spdlog_enable_warnings(spdlog)
set_target_properties(spdlog PROPERTIES VERSION ${SPDLOG_VERSION} SOVERSION ${SPDLOG_VERSION_MAJOR})
set_target_properties(spdlog PROPERTIES DEBUG_POSTFIX d)
if(COMMAND target_precompile_headers AND SPDLOG_ENABLE_PCH)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/pch.h.in ${PROJECT_BINARY_DIR}/spdlog_pch.h @ONLY)
target_precompile_headers(spdlog PRIVATE ${PROJECT_BINARY_DIR}/spdlog_pch.h)
endif()
#---------------------------------------------------------------------------------------
# Header only version
#---------------------------------------------------------------------------------------
add_library(spdlog_header_only INTERFACE)
add_library(spdlog::spdlog_header_only ALIAS spdlog_header_only)
target_include_directories(spdlog_header_only INTERFACE
"$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>")
target_link_libraries(spdlog_header_only INTERFACE Threads::Threads)
#---------------------------------------------------------------------------------------
# Use fmt package if using external fmt
#---------------------------------------------------------------------------------------
if(SPDLOG_FMT_EXTERNAL OR SPDLOG_FMT_EXTERNAL_HO)
if (NOT TARGET fmt::fmt)
find_package(fmt REQUIRED)
endif ()
target_compile_definitions(spdlog PUBLIC SPDLOG_FMT_EXTERNAL)
target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_FMT_EXTERNAL)
# use external fmt-header-nly
if(SPDLOG_FMT_EXTERNAL_HO)
target_link_libraries(spdlog PUBLIC fmt::fmt-header-only)
target_link_libraries(spdlog_header_only INTERFACE fmt::fmt-header-only)
else() # use external compile fmt
target_link_libraries(spdlog PUBLIC fmt::fmt)
target_link_libraries(spdlog_header_only INTERFACE fmt::fmt)
endif()
set(PKG_CONFIG_REQUIRES fmt) # add dependency to pkg-config
endif()
#---------------------------------------------------------------------------------------
# Misc definitions according to tweak options
#---------------------------------------------------------------------------------------
if(SPDLOG_WCHAR_SUPPORT)
target_compile_definitions(spdlog PUBLIC SPDLOG_WCHAR_TO_UTF8_SUPPORT)
target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_WCHAR_TO_UTF8_SUPPORT)
endif()
if(SPDLOG_WCHAR_FILENAMES)
target_compile_definitions(spdlog PUBLIC SPDLOG_WCHAR_FILENAMES)
target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_WCHAR_FILENAMES)
endif()
if(SPDLOG_NO_EXCEPTIONS)
target_compile_definitions(spdlog PUBLIC SPDLOG_NO_EXCEPTIONS)
target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_NO_EXCEPTIONS)
if(NOT MSVC)
target_compile_options(spdlog PRIVATE -fno-exceptions)
endif()
endif()
if(SPDLOG_CLOCK_COARSE)
target_compile_definitions(spdlog PRIVATE SPDLOG_CLOCK_COARSE)
target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_CLOCK_COARSE)
endif()
if(SPDLOG_PREVENT_CHILD_FD)
target_compile_definitions(spdlog PRIVATE SPDLOG_PREVENT_CHILD_FD)
target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_PREVENT_CHILD_FD)
endif()
if(SPDLOG_NO_THREAD_ID)
target_compile_definitions(spdlog PRIVATE SPDLOG_NO_THREAD_ID)
target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_NO_THREAD_ID)
endif()
if(SPDLOG_NO_TLS)
target_compile_definitions(spdlog PRIVATE SPDLOG_NO_TLS)
target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_NO_TLS)
endif()
if(SPDLOG_NO_ATOMIC_LEVELS)
target_compile_definitions(spdlog PUBLIC SPDLOG_NO_ATOMIC_LEVELS)
target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_NO_ATOMIC_LEVELS)
endif()
#---------------------------------------------------------------------------------------
# Build binaries
#---------------------------------------------------------------------------------------
if(SPDLOG_BUILD_EXAMPLE OR SPDLOG_BUILD_EXAMPLE_HO)
message(STATUS "Generating example(s)")
add_subdirectory(example)
spdlog_enable_warnings(example)
if(SPDLOG_BUILD_EXAMPLE_HO)
spdlog_enable_warnings(example_header_only)
endif()
endif()
if(SPDLOG_BUILD_TESTS OR SPDLOG_BUILD_TESTS_HO)
message(STATUS "Generating tests")
enable_testing()
add_subdirectory(tests)
endif()
if(SPDLOG_BUILD_BENCH)
message(STATUS "Generating benchmarks")
add_subdirectory(bench)
endif()
#---------------------------------------------------------------------------------------
# Install
#---------------------------------------------------------------------------------------
if (SPDLOG_INSTALL)
message(STATUS "Generating install")
set(project_config_in "${CMAKE_CURRENT_LIST_DIR}/cmake/spdlogConfig.cmake.in")
set(project_config_out "${CMAKE_CURRENT_BINARY_DIR}/spdlogConfig.cmake")
set(config_targets_file "spdlogConfigTargets.cmake")
set(version_config_file "${CMAKE_CURRENT_BINARY_DIR}/spdlogConfigVersion.cmake")
set(export_dest_dir "${CMAKE_INSTALL_LIBDIR}/cmake/spdlog")
set(pkgconfig_install_dir "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
set(pkg_config "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.pc")
#---------------------------------------------------------------------------------------
# Include files
#---------------------------------------------------------------------------------------
install(DIRECTORY include/ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" PATTERN "fmt/bundled" EXCLUDE)
install(TARGETS spdlog spdlog_header_only EXPORT spdlog
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
if(NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO)
install(DIRECTORY include/${PROJECT_NAME}/fmt/bundled/
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}/fmt/bundled/")
endif()
#---------------------------------------------------------------------------------------
# Install pkg-config file
#---------------------------------------------------------------------------------------
get_target_property(PKG_CONFIG_DEFINES spdlog INTERFACE_COMPILE_DEFINITIONS)
string(REPLACE ";" " -D" PKG_CONFIG_DEFINES "${PKG_CONFIG_DEFINES}")
string(CONCAT PKG_CONFIG_DEFINES "-D" "${PKG_CONFIG_DEFINES}")
configure_file("cmake/${PROJECT_NAME}.pc.in" "${pkg_config}" @ONLY)
install(FILES "${pkg_config}" DESTINATION "${pkgconfig_install_dir}")
#---------------------------------------------------------------------------------------
# Install CMake config files
#---------------------------------------------------------------------------------------
install(EXPORT spdlog
DESTINATION ${export_dest_dir}
NAMESPACE spdlog::
FILE ${config_targets_file})
include(CMakePackageConfigHelpers)
configure_file("${project_config_in}" "${project_config_out}" @ONLY)
write_basic_package_version_file("${version_config_file}" COMPATIBILITY SameMajorVersion)
install(FILES
"${project_config_out}"
"${version_config_file}" DESTINATION "${export_dest_dir}")
#---------------------------------------------------------------------------------------
# Support creation of installable packages
#---------------------------------------------------------------------------------------
include(cmake/spdlogCPack.cmake)
endif ()

24
third_party/spdlog/INSTALL vendored Normal file
View File

@ -0,0 +1,24 @@
Header only version:
==================================================================
Just copy the files to your build tree and use a C++11 compiler.
Or use CMake:
add_executable(example_header_only example.cpp)
target_link_libraries(example_header_only spdlog::spdlog_header_only)
Compiled library version:
==================================================================
CMake:
add_executable(example example.cpp)
target_link_libraries(example spdlog::spdlog)
Or copy src/spdlog.cpp to your build tree and pass the -DSPDLOG_COMPILED_LIB to the compiler.
Tested on:
gcc 4.8.1 and above
clang 3.5
Visual Studio 2013

26
third_party/spdlog/LICENSE vendored Normal file
View File

@ -0,0 +1,26 @@
The MIT License (MIT)
Copyright (c) 2016 Gabi Melman.
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.
-- NOTE: Third party dependecy used by this sofware --
This software depends on the fmt lib (MIT License),
and users must comply to its license: https://github.com/fmtlib/fmt/blob/master/LICENSE.rst

406
third_party/spdlog/README.md vendored Normal file
View File

@ -0,0 +1,406 @@
# spdlog
Very fast, header-only/compiled, C++ logging library. [![Build Status](https://travis-ci.org/gabime/spdlog.svg?branch=master)](https://travis-ci.org/gabime/spdlog)&nbsp; [![Build status](https://ci.appveyor.com/api/projects/status/d2jnxclg20vd0o50?svg=true)](https://ci.appveyor.com/project/gabime/spdlog) [![Release](https://img.shields.io/github/release/gabime/spdlog.svg)](https://github.com/gabime/spdlog/releases/latest)
## Install
#### Header only version
Copy the source [folder](https://github.com/gabime/spdlog/tree/v1.x/include/spdlog) to your build tree and use a C++11 compiler.
#### Static lib version (recommended - much faster compile times)
```console
$ git clone https://github.com/gabime/spdlog.git
$ cd spdlog && mkdir build && cd build
$ cmake .. && make -j
```
see example [CMakeLists.txt](https://github.com/gabime/spdlog/blob/v1.x/example/CMakeLists.txt) on how to use.
## Platforms
* Linux, FreeBSD, OpenBSD, Solaris, AIX
* Windows (msvc 2013+, cygwin)
* macOS (clang 3.5+)
* Android
## Package managers:
* Homebrew: `brew install spdlog`
* MacPorts: `sudo port install spdlog`
* FreeBSD: `cd /usr/ports/devel/spdlog/ && make install clean`
* Fedora: `yum install spdlog`
* Gentoo: `emerge dev-libs/spdlog`
* Arch Linux: `pacman -S spdlog`
* vcpkg: `vcpkg install spdlog`
* conan: `spdlog/[>=1.4.1]`
* conda: `conda install -c conda-forge spdlog`
## Features
* Very fast (see [benchmarks](#benchmarks) below).
* Headers only or compiled version
* Feature rich formatting, using the excellent [fmt](https://github.com/fmtlib/fmt) library.
* **New!** [Backtrace](#backtrace-support) support - store debug messages in a ring buffer and display later on demand.
* Asynchronous mode (optional)
* [Custom](https://github.com/gabime/spdlog/wiki/3.-Custom-formatting) formatting.
* Multi/Single threaded loggers.
* Various log targets:
* Rotating log files.
* Daily log files.
* Console logging (colors supported).
* syslog.
* Windows debugger (```OutputDebugString(..)```)
* Easily extendable with custom log targets (just implement a single function in the [sink](include/spdlog/sinks/sink.h) interface).
* Log filtering - log levels can be modified in runtime as well as in compile time.
* Support for loading log levels from argv or from environment var.
## Usage samples
#### Basic usage
```c++
#include "spdlog/spdlog.h"
#include "spdlog/sinks/basic_file_sink.h"
int main()
{
spdlog::info("Welcome to spdlog!");
spdlog::error("Some error message with arg: {}", 1);
spdlog::warn("Easy padding in numbers like {:08d}", 12);
spdlog::critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
spdlog::info("Support for floats {:03.2f}", 1.23456);
spdlog::info("Positional args are {1} {0}..", "too", "supported");
spdlog::info("{:<30}", "left aligned");
spdlog::set_level(spdlog::level::debug); // Set global log level to debug
spdlog::debug("This message should be displayed..");
// change log pattern
spdlog::set_pattern("[%H:%M:%S %z] [%n] [%^---%L---%$] [thread %t] %v");
// Compile time log levels
// define SPDLOG_ACTIVE_LEVEL to desired level
SPDLOG_TRACE("Some trace message with param {}", 42);
SPDLOG_DEBUG("Some debug message");
// Set the default logger to file logger
auto file_logger = spdlog::basic_logger_mt("basic_logger", "logs/basic.txt");
spdlog::set_default_logger(file_logger);
}
```
#### Load log levels from env variable or from argv
```c++
#include "spdlog/cfg/env.h"
void load_levels_example()
{
// Set the log level to "info" and mylogger to to "trace":
// SPDLOG_LEVEL=info,mylogger=trace && ./example
spdlog::cfg::load_env_levels();
// or from command line:
// ./example SPDLOG_LEVEL=info,mylogger=trace
// #include "spdlog/cfg/argv.h" // for loading levels from argv
// spdlog::cfg::load_argv_levels(args, argv);
}
```
#### Create stdout/stderr logger object
```c++
#include "spdlog/spdlog.h"
#include "spdlog/sinks/stdout_color_sinks.h"
void stdout_example()
{
// create color multi threaded logger
auto console = spdlog::stdout_color_mt("console");
auto err_logger = spdlog::stderr_color_mt("stderr");
spdlog::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name)");
}
```
---
#### Basic file logger
```c++
#include "spdlog/sinks/basic_file_sink.h"
void basic_logfile_example()
{
try
{
auto my_logger = spdlog::basic_logger_mt("basic_logger", "logs/basic-log.txt");
}
catch (const spdlog::spdlog_ex &ex)
{
std::cout << "Log init failed: " << ex.what() << std::endl;
}
}
```
---
#### Rotating files
```c++
#include "spdlog/sinks/rotating_file_sink.h"
void rotating_example()
{
// Create a file rotating logger with 5mb size max and 3 rotated files
auto rotating_logger = spdlog::rotating_logger_mt("some_logger_name", "logs/rotating.txt", 1048576 * 5, 3);
}
```
---
#### Daily files
```c++
#include "spdlog/sinks/daily_file_sink.h"
void daily_example()
{
// Create a daily logger - a new file is created every day on 2:30am
auto daily_logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
}
```
---
#### Backtrace support
```c++
// Loggers can store in a ring buffer all messages (including debug/trace) and display later on demand.
// When needed, call dump_backtrace() to see them
spdlog::enable_backtrace(32); // create ring buffer with capacity of 32 messages
// or my_logger->enable_backtrace(32)..
for(int i = 0; i < 100; i++)
{
spdlog::debug("Backtrace message {}", i); // not logged yet..
}
// e.g. if some error happened:
spdlog::dump_backtrace(); // log them now! show the last 32 messages
// or my_logger->dump_backtrace(32)..
```
---
#### Periodic flush
```c++
// periodically flush all *registered* loggers every 3 seconds:
// warning: only use if all your loggers are thread safe ("_mt" loggers)
spdlog::flush_every(std::chrono::seconds(3));
```
---
#### Log binary data in hex
```c++
// many types of std::container<char> types can be used.
// ranges are supported too.
// format flags:
// {:X} - print in uppercase.
// {:s} - don't separate each byte with space.
// {:p} - don't print the position on each line start.
// {:n} - don't split the output to lines.
#include "spdlog/fmt/bin_to_hex.h"
void binary_example()
{
auto console = spdlog::get("console");
std::array<char, 80> buf;
console->info("Binary example: {}", spdlog::to_hex(buf));
console->info("Another binary example:{:n}", spdlog::to_hex(std::begin(buf), std::begin(buf) + 10));
// more examples:
// logger->info("uppercase: {:X}", spdlog::to_hex(buf));
// logger->info("uppercase, no delimiters: {:Xs}", spdlog::to_hex(buf));
// logger->info("uppercase, no delimiters, no position info: {:Xsp}", spdlog::to_hex(buf));
}
```
---
#### Logger with multi sinks - each with different format and log level
```c++
// create logger with 2 targets with different log levels and formats.
// the console will show only warnings or errors, while the file will log all.
void multi_sink_example()
{
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
console_sink->set_level(spdlog::level::warn);
console_sink->set_pattern("[multi_sink_example] [%^%l%$] %v");
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/multisink.txt", true);
file_sink->set_level(spdlog::level::trace);
spdlog::logger logger("multi_sink", {console_sink, file_sink});
logger.set_level(spdlog::level::debug);
logger.warn("this should appear in both console and file");
logger.info("this message should not appear in the console, only in the file");
}
```
---
#### Asynchronous logging
```c++
#include "spdlog/async.h"
#include "spdlog/sinks/basic_file_sink.h"
void async_example()
{
// default thread pool settings can be modified *before* creating the async logger:
// spdlog::init_thread_pool(8192, 1); // queue with 8k items and 1 backing thread.
auto async_file = spdlog::basic_logger_mt<spdlog::async_factory>("async_file_logger", "logs/async_log.txt");
// alternatively:
// auto async_file = spdlog::create_async<spdlog::sinks::basic_file_sink_mt>("async_file_logger", "logs/async_log.txt");
}
```
---
#### Asynchronous logger with multi sinks
```c++
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/sinks/rotating_file_sink.h"
void multi_sink_example2()
{
spdlog::init_thread_pool(8192, 1);
auto stdout_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt >();
auto rotating_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>("mylog.txt", 1024*1024*10, 3);
std::vector<spdlog::sink_ptr> sinks {stdout_sink, rotating_sink};
auto logger = std::make_shared<spdlog::async_logger>("loggername", sinks.begin(), sinks.end(), spdlog::thread_pool(), spdlog::async_overflow_policy::block);
spdlog::register_logger(logger);
}
```
---
#### User defined types
```c++
// user defined types logging by implementing operator<<
#include "spdlog/fmt/ostr.h" // must be included
struct my_type
{
int i;
template<typename OStream>
friend OStream &operator<<(OStream &os, const my_type &c)
{
return os << "[my_type i=" << c.i << "]";
}
};
void user_defined_example()
{
spdlog::get("console")->info("user defined type: {}", my_type{14});
}
```
---
#### User defined flags in the log pattern
```c++
// Log patterns can contain custom flags.
// the following example will add new flag '%*' - which will be bound to a <my_formatter_flag> instance.
#include "spdlog/pattern_formatter.h"
class my_formatter_flag : public spdlog::custom_flag_formatter
{
public:
void format(const spdlog::details::log_msg &, const std::tm &, spdlog::memory_buf_t &dest) override
{
std::string some_txt = "custom-flag";
dest.append(some_txt.data(), some_txt.data() + some_txt.size());
}
std::unique_ptr<custom_flag_formatter> clone() const override
{
return spdlog::details::make_unique<my_formatter_flag>();
}
};
void custom_flags_example()
{
auto formatter = std::make_unique<spdlog::pattern_formatter>();
formatter->add_flag<my_formatter_flag>('*').set_pattern("[%n] [%*] [%^%l%$] %v");
spdlog::set_formatter(std::move(formatter));
}
```
---
#### Custom error handler
```c++
void err_handler_example()
{
// can be set globally or per logger(logger->set_error_handler(..))
spdlog::set_error_handler([](const std::string &msg) { spdlog::get("console")->error("*** LOGGER ERROR ***: {}", msg); });
spdlog::get("console")->info("some invalid message to trigger an error {}{}{}{}", 3);
}
```
---
#### syslog
```c++
#include "spdlog/sinks/syslog_sink.h"
void syslog_example()
{
std::string ident = "spdlog-example";
auto syslog_logger = spdlog::syslog_logger_mt("syslog", ident, LOG_PID);
syslog_logger->warn("This is warning that will end up in syslog.");
}
```
---
#### Android example
```c++
#include "spdlog/sinks/android_sink.h"
void android_example()
{
std::string tag = "spdlog-android";
auto android_logger = spdlog::android_logger_mt("android", tag);
android_logger->critical("Use \"adb shell logcat\" to view this message.");
}
```
## Benchmarks
Below are some [benchmarks](https://github.com/gabime/spdlog/blob/v1.x/bench/bench.cpp) done in Ubuntu 64 bit, Intel i7-4770 CPU @ 3.40GHz
#### Synchronous mode
```
[info] **************************************************************
[info] Single thread, 1,000,000 iterations
[info] **************************************************************
[info] basic_st Elapsed: 0.17 secs 5,777,626/sec
[info] rotating_st Elapsed: 0.18 secs 5,475,894/sec
[info] daily_st Elapsed: 0.20 secs 5,062,659/sec
[info] empty_logger Elapsed: 0.07 secs 14,127,300/sec
[info] **************************************************************
[info] C-string (400 bytes). Single thread, 1,000,000 iterations
[info] **************************************************************
[info] basic_st Elapsed: 0.41 secs 2,412,483/sec
[info] rotating_st Elapsed: 0.72 secs 1,389,196/sec
[info] daily_st Elapsed: 0.42 secs 2,393,298/sec
[info] null_st Elapsed: 0.04 secs 27,446,957/sec
[info] **************************************************************
[info] 10 threads, competing over the same logger object, 1,000,000 iterations
[info] **************************************************************
[info] basic_mt Elapsed: 0.60 secs 1,659,613/sec
[info] rotating_mt Elapsed: 0.62 secs 1,612,493/sec
[info] daily_mt Elapsed: 0.61 secs 1,638,305/sec
[info] null_mt Elapsed: 0.16 secs 6,272,758/sec
```
#### ASynchronous mode
```
[info] -------------------------------------------------
[info] Messages : 1,000,000
[info] Threads : 10
[info] Queue : 8,192 slots
[info] Queue memory : 8,192 x 272 = 2,176 KB
[info] -------------------------------------------------
[info]
[info] *********************************
[info] Queue Overflow Policy: block
[info] *********************************
[info] Elapsed: 1.70784 secs 585,535/sec
[info] Elapsed: 1.69805 secs 588,910/sec
[info] Elapsed: 1.7026 secs 587,337/sec
[info]
[info] *********************************
[info] Queue Overflow Policy: overrun
[info] *********************************
[info] Elapsed: 0.372816 secs 2,682,285/sec
[info] Elapsed: 0.379758 secs 2,633,255/sec
[info] Elapsed: 0.373532 secs 2,677,147/sec
```
## Documentation
Documentation can be found in the [wiki](https://github.com/gabime/spdlog/wiki/1.-QuickStart) pages.

38
third_party/spdlog/appveyor.yml vendored Normal file
View File

@ -0,0 +1,38 @@
version: 1.0.{build}
image: Visual Studio 2017
environment:
matrix:
- GENERATOR: '"Visual Studio 14 2015"'
BUILD_TYPE: Debug
WCHAR: 'OFF'
- GENERATOR: '"Visual Studio 14 2015"'
BUILD_TYPE: Release
WCHAR: 'ON'
- GENERATOR: '"Visual Studio 14 2015 Win64"'
BUILD_TYPE: Debug
WCHAR: 'ON'
- GENERATOR: '"Visual Studio 14 2015 Win64"'
BUILD_TYPE: Release
WCHAR: 'ON'
- GENERATOR: '"Visual Studio 15 2017 Win64"'
BUILD_TYPE: Debug
WCHAR: 'ON'
- GENERATOR: '"Visual Studio 15 2017 Win64"'
BUILD_TYPE: Release
WCHAR: 'OFf'
build_script:
- cmd: >-
set
mkdir build
cd build
set PATH=%PATH%:C:\Program Files\Git\usr\bin
cmake .. -G %GENERATOR% -DCMAKE_BUILD_TYPE=%BUILD_TYPE% -DSPDLOG_WCHAR_SUPPORT=%WCHAR% -DSPDLOG_BUILD_EXAMPLE=ON -DSPDLOG_BUILD_EXAMPLE_HO=ON -DSPDLOG_BUILD_TESTS=ON -DSPDLOG_BUILD_TESTS_HO=OFF -DSPDLOG_ENABLE_WARNINGS=ON
cmake --build . --config %BUILD_TYPE%
test_script:
- ctest -VV -C "%BUILD_TYPE%"

Some files were not shown because too many files have changed in this diff Show More