Compare commits
59 Commits
feature/ts
...
feature/wi
Author | SHA1 | Date | |
---|---|---|---|
cce7bb6a3e | |||
90eae2b403 | |||
b044a1d119 | |||
5184d5d985 | |||
aac9386f62 | |||
8db688b818 | |||
68ee57a6a7 | |||
9d79596629 | |||
0b6fd989f5 | |||
1c19a57fef | |||
a2abe861d3 | |||
0f5d15aa11 | |||
ccfd196863 | |||
9b8cfa0a37 | |||
85f6b1e0b7 | |||
64754df66c | |||
71a421eefc | |||
386ef3ab04 | |||
2c4bf8f4bd | |||
3a2c446225 | |||
35630fe7ed | |||
bea582c208 | |||
783d1d92dd | |||
415f6b4832 | |||
13d3300a40 | |||
432f0570f4 | |||
37a054723a | |||
c57cf413fb | |||
f1c106728b | |||
2eb5c9480e | |||
f9d75c9374 | |||
d1cd5e62ac | |||
f3b97097cd | |||
605be72579 | |||
49ff3789b5 | |||
96d61c6e5b | |||
9a23c5aaac | |||
d81e4d4fc0 | |||
bd44d32fdb | |||
b6abc12ecd | |||
2268b743ae | |||
1d3db5f75b | |||
296762ce06 | |||
e465f7af52 | |||
f8bf1fe7cd | |||
cfa5718e40 | |||
40c619c1ec | |||
22b02e0e5c | |||
738a3bf1c5 | |||
598fb071e3 | |||
686aface26 | |||
3073dd3f06 | |||
68c64f3f69 | |||
771ebb2a4c | |||
0fffb1e894 | |||
18164c0c38 | |||
d2db7310ff | |||
09e4584fc8 | |||
da36856d85 |
47
.github/workflows/ccpp.yml
vendored
47
.github/workflows/ccpp.yml
vendored
@ -5,40 +5,17 @@ on:
|
||||
- 'docs/**'
|
||||
|
||||
jobs:
|
||||
# linux:
|
||||
# runs-on: ubuntu-latest
|
||||
# steps:
|
||||
# - uses: actions/checkout@v1
|
||||
# - name: make test
|
||||
# run: make test
|
||||
|
||||
mac_openssl_tsan:
|
||||
runs-on: macOS-latest
|
||||
windows_mbedtls:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: install openssl
|
||||
run: brew install openssl
|
||||
- name: make test
|
||||
run: make test_tsan_openssl
|
||||
|
||||
# tsan:
|
||||
# runs-on: macOS-latest
|
||||
# steps:
|
||||
# - uses: actions/checkout@v1
|
||||
# - name: make test_tsan
|
||||
# run: make test_tsan
|
||||
|
||||
# win:
|
||||
# runs-on: windows-latest
|
||||
# steps:
|
||||
# - uses: actions/checkout@v1
|
||||
# - uses: seanmiddleditch/gha-setup-vsdevenv@master
|
||||
# - run: |
|
||||
# mkdir build
|
||||
# cd build
|
||||
# cmake -DCMAKE_CXX_COMPILER=cl.exe -DUSE_WS=1 -DUSE_TEST=1 ..
|
||||
# - run: cmake --build build
|
||||
|
||||
# # Running the unittest does not work
|
||||
# #- run: ../build/test/ixwebsocket_unittest.exe
|
||||
# # working-directory: test
|
||||
- uses: seanmiddleditch/gha-setup-vsdevenv@master
|
||||
- run: |
|
||||
vcpkg install zlib:x64-windows
|
||||
- run: |
|
||||
vcpkg install mbedtls:x64-windows
|
||||
- run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_TOOLCHAIN_FILE=c:/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_CXX_COMPILER=cl.exe -DUSE_MBED_TLS=1 -DUSE_TLS=1 -DUSE_WS=1 ..
|
||||
- run: cmake --build build
|
||||
|
2
.github/workflows/mkdocs.yml
vendored
2
.github/workflows/mkdocs.yml
vendored
@ -17,6 +17,8 @@ jobs:
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install mkdocs
|
||||
pip install mkdocs-material
|
||||
pip install pygments
|
||||
- name: Build doc
|
||||
run: |
|
||||
git pull
|
||||
|
19
.github/workflows/stale.yml
vendored
Normal file
19
.github/workflows/stale.yml
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
name: Mark stale issues and pull requests
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/stale@v1
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
stale-issue-message: 'Stale issue message'
|
||||
stale-pr-message: 'Stale pull request message'
|
||||
stale-issue-label: 'no-issue-activity'
|
||||
stale-pr-label: 'no-pr-activity'
|
@ -5,7 +5,7 @@ include(FindPackageHandleStandardArgs)
|
||||
find_path(JSONCPP_INCLUDE_DIRS json/json.h)
|
||||
find_library(JSONCPP_LIBRARY jsoncpp)
|
||||
|
||||
find_package_handle_standard_args(JSONCPP
|
||||
find_package_handle_standard_args(JsonCpp
|
||||
FOUND_VAR
|
||||
JSONCPP_FOUND
|
||||
REQUIRED_VARS
|
||||
|
@ -44,7 +44,6 @@ set( IXWEBSOCKET_SOURCES
|
||||
ixwebsocket/IXWebSocketCloseConstants.cpp
|
||||
ixwebsocket/IXWebSocketHandshake.cpp
|
||||
ixwebsocket/IXWebSocketHttpHeaders.cpp
|
||||
ixwebsocket/IXWebSocketMessageQueue.cpp
|
||||
ixwebsocket/IXWebSocketPerMessageDeflate.cpp
|
||||
ixwebsocket/IXWebSocketPerMessageDeflateCodec.cpp
|
||||
ixwebsocket/IXWebSocketPerMessageDeflateOptions.cpp
|
||||
@ -84,7 +83,6 @@ set( IXWEBSOCKET_HEADERS
|
||||
ixwebsocket/IXWebSocketHttpHeaders.h
|
||||
ixwebsocket/IXWebSocketInitResult.h
|
||||
ixwebsocket/IXWebSocketMessage.h
|
||||
ixwebsocket/IXWebSocketMessageQueue.h
|
||||
ixwebsocket/IXWebSocketMessageType.h
|
||||
ixwebsocket/IXWebSocketOpenInfo.h
|
||||
ixwebsocket/IXWebSocketPerMessageDeflate.h
|
||||
@ -120,13 +118,14 @@ endif()
|
||||
option(USE_TLS "Enable TLS support" FALSE)
|
||||
|
||||
if (USE_TLS)
|
||||
if (WIN32)
|
||||
option(USE_MBED_TLS "Use Mbed TLS" ON)
|
||||
else()
|
||||
option(USE_MBED_TLS "Use Mbed TLS" OFF)
|
||||
endif()
|
||||
option(USE_MBED_TLS "Use Mbed TLS" OFF)
|
||||
option(USE_OPEN_SSL "Use OpenSSL" OFF)
|
||||
|
||||
# default to mbedtls on windows if nothing is configured
|
||||
if (WIN32 AND NOT USE_OPEN_SSL AND NOT USE_MBED_TLS)
|
||||
option(USE_MBED_TLS "Use Mbed TLS" ON)
|
||||
endif()
|
||||
|
||||
if (USE_MBED_TLS)
|
||||
list( APPEND IXWEBSOCKET_HEADERS ixwebsocket/IXSocketMbedTLS.h)
|
||||
list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/IXSocketMbedTLS.cpp)
|
||||
@ -200,7 +199,9 @@ if (USE_TLS AND USE_MBED_TLS)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_package(ZLIB)
|
||||
if (NOT ZLIB_FOUND)
|
||||
find_package(ZLIB)
|
||||
endif()
|
||||
if (ZLIB_FOUND)
|
||||
include_directories(${ZLIB_INCLUDE_DIRS})
|
||||
target_link_libraries(ixwebsocket ${ZLIB_LIBRARIES})
|
||||
|
@ -1 +1 @@
|
||||
docker/Dockerfile.centos
|
||||
docker/Dockerfile.alpine
|
@ -1,12 +1,13 @@
|
||||
FROM alpine:3.11 as build
|
||||
|
||||
RUN apk add --no-cache gcc g++ musl-dev linux-headers cmake openssl-dev
|
||||
RUN apk add --no-cache make
|
||||
RUN apk add --no-cache zlib-dev
|
||||
RUN apk add --no-cache \
|
||||
gcc g++ musl-dev linux-headers \
|
||||
cmake mbedtls-dev make zlib-dev
|
||||
|
||||
RUN addgroup -S app && adduser -S -G app app
|
||||
RUN chown -R app:app /opt
|
||||
RUN chown -R app:app /usr/local
|
||||
RUN addgroup -S app && \
|
||||
adduser -S -G app app && \
|
||||
chown -R app:app /opt && \
|
||||
chown -R app:app /usr/local
|
||||
|
||||
# There is a bug in CMake where we cannot build from the root top folder
|
||||
# So we build from /opt
|
||||
@ -14,22 +15,21 @@ COPY --chown=app:app . /opt
|
||||
WORKDIR /opt
|
||||
|
||||
USER app
|
||||
RUN [ "make", "ws_install" ]
|
||||
RUN [ "rm", "-rf", "build" ]
|
||||
RUN make ws_mbedtls_install && \
|
||||
sh tools/trim_repo_for_docker.sh
|
||||
|
||||
FROM alpine:3.11 as runtime
|
||||
|
||||
RUN apk add --no-cache libstdc++
|
||||
RUN apk add --no-cache strace
|
||||
RUN apk add --no-cache gdb
|
||||
RUN apk add --no-cache libstdc++ mbedtls ca-certificates && \
|
||||
addgroup -S app && \
|
||||
adduser -S -G app app
|
||||
|
||||
RUN addgroup -S app && adduser -S -G app app
|
||||
COPY --chown=app:app --from=build /usr/local/bin/ws /usr/local/bin/ws
|
||||
RUN chmod +x /usr/local/bin/ws
|
||||
RUN ldd /usr/local/bin/ws
|
||||
|
||||
# Copy source code for gcc
|
||||
COPY --chown=app:app --from=build /opt /opt
|
||||
# COPY --chown=app:app --from=build /opt /opt
|
||||
|
||||
RUN chmod +x /usr/local/bin/ws && \
|
||||
ldd /usr/local/bin/ws
|
||||
|
||||
# Now run in usermode
|
||||
USER app
|
||||
|
@ -1,6 +1,102 @@
|
||||
# Changelog
|
||||
All changes to this project will be documented in this file.
|
||||
|
||||
## [9.3.3] - 2020-04-17
|
||||
|
||||
(ixbots) display sent/receive message, per seconds as accumulated
|
||||
|
||||
## [9.3.2] - 2020-04-17
|
||||
|
||||
(ws) add a --logfile option to configure all logs to go to a file
|
||||
|
||||
## [9.3.1] - 2020-04-16
|
||||
|
||||
(cobra bots) add a utility class to factor out the common bots features (heartbeat) and move all bots to used it + convert cobra_subscribe to be a bot and add a unittest for it
|
||||
|
||||
## [9.3.0] - 2020-04-15
|
||||
|
||||
(websocket) add a positive number to the heartbeat message sent, incremented each time the heartbeat is sent
|
||||
|
||||
## [9.2.9] - 2020-04-15
|
||||
|
||||
(ixcobra) change cobra event callback to use a struct instead of several objects, which is more flexible/extensible
|
||||
|
||||
## [9.2.8] - 2020-04-15
|
||||
|
||||
(ixcobra) make CobraConnection_EventType an enum class (CobraEventType)
|
||||
|
||||
## [9.2.7] - 2020-04-14
|
||||
|
||||
(ixsentry) add a library method to upload a payload directly to sentry
|
||||
|
||||
## [9.2.6] - 2020-04-14
|
||||
|
||||
(ixcobra) snake server / handle invalid incoming json messages + cobra subscriber in fluentd mode insert a created_at timestamp entry
|
||||
|
||||
## [9.2.5] - 2020-04-13
|
||||
|
||||
(websocket) WebSocketMessagePtr is a unique_ptr instead of a shared_ptr
|
||||
|
||||
## [9.2.4] - 2020-04-13
|
||||
|
||||
(websocket) use persistent member variable as temp variables to encode/decode zlib messages in order to reduce transient allocations
|
||||
|
||||
## [9.2.3] - 2020-04-13
|
||||
|
||||
(ws) add a --runtime option to ws cobra_subscribe to optionally limit how much time it will run
|
||||
|
||||
## [9.2.2] - 2020-04-04
|
||||
|
||||
(third_party deps) fix #177, update bundled spdlog to 1.6.0
|
||||
|
||||
## [9.2.1] - 2020-04-04
|
||||
|
||||
(windows) when using OpenSSL, the system store is used to populate the cacert. No need to ship a cacert.pem file with your app.
|
||||
|
||||
## [9.2.0] - 2020-04-04
|
||||
|
||||
(windows) ci: windows build with TLS (mbedtls) + verify that we can be build with OpenSSL
|
||||
|
||||
## [9.1.9] - 2020-03-30
|
||||
|
||||
(cobra to statsd bot) add ability to extract a numerical value and send a timer event to statsd, with the --timer option
|
||||
|
||||
## [9.1.8] - 2020-03-29
|
||||
|
||||
(cobra to statsd bot) bot init was missing + capture socket error
|
||||
|
||||
## [9.1.7] - 2020-03-29
|
||||
|
||||
(cobra to statsd bot) add ability to extract a numerical value and send a gauge event to statsd, with the --gauge option
|
||||
|
||||
## [9.1.6] - 2020-03-29
|
||||
|
||||
(ws cobra subscriber) use a Json::StreamWriter to write to std::cout, and save one std::string allocation for each message printed
|
||||
|
||||
## [9.1.5] - 2020-03-29
|
||||
|
||||
(docker) trim down docker image (300M -> 12M) / binary built without symbol and size optimization, and source code not copied over
|
||||
|
||||
## [9.1.4] - 2020-03-28
|
||||
|
||||
(jsoncpp) update bundled copy to version 1.9.3 (at sha 3beb37ea14aec1bdce1a6d542dc464d00f4a6cec)
|
||||
|
||||
## [9.1.3] - 2020-03-27
|
||||
|
||||
(docker) alpine docker build with release with debug info, and bundle ca-certificates
|
||||
|
||||
## [9.1.2] - 2020-03-26
|
||||
|
||||
(mac ssl) rename DarwinSSL -> SecureTransport (see this too -> https://github.com/curl/curl/issues/3733)
|
||||
|
||||
## [9.1.1] - 2020-03-26
|
||||
|
||||
(websocket) fix data race accessing _socket object without mutex protection when calling wakeUpFromPoll in WebSocketTransport.cpp
|
||||
|
||||
## [9.1.0] - 2020-03-26
|
||||
|
||||
(ixcobra) add explicit event types for handshake, authentication and subscription failure, and handle those by exiting in ws_cobra_subcribe and friends
|
||||
|
||||
## [9.0.3] - 2020-03-24
|
||||
|
||||
(ws connect) display statistics about how much time it takes to stop the connection
|
||||
|
@ -8,6 +8,8 @@ The per message deflate compression option is supported. It can lead to very nic
|
||||
|
||||
Connections can be optionally secured and encrypted with TLS/SSL when using a wss:// endpoint, or using normal un-encrypted socket with ws:// endpoints. AppleSSL is used on iOS and macOS, OpenSSL and mbedTLS can be used on Android, Linux and Windows.
|
||||
|
||||
If you are using OpenSSL, try to be on a version higher than 1.1.x as there there are thread safety problems with 1.0.x.
|
||||
|
||||
### Polling and background thread work
|
||||
|
||||
No manual polling to fetch data is required. Data is sent and received instantly by using a background thread for receiving data and the select [system](http://man7.org/linux/man-pages/man2/select.2.html) call to be notified by the OS of incoming data. No timeout is used for select so that the background thread is only woken up when data is available, to optimize battery life. This is also the recommended way of using select according to the select tutorial, section [select law](https://linux.die.net/man/2/select_tut). Read and Writes to the socket are non blocking. Data is sent right away and not enqueued by writing directly to the socket, which is [possible](https://stackoverflow.com/questions/1981372/are-parallel-calls-to-send-recv-on-the-same-socket-valid) since system socket implementations allow concurrent read/writes. However concurrent writes need to be protected with mutex.
|
||||
@ -26,7 +28,13 @@ The library has an interactive tool which is handy for testing compatibility ith
|
||||
|
||||
The unittest tries to be comprehensive, and has been running on multiple platforms, with different sanitizers such as a thread sanitizer to catch data races or the undefined behavior sanitizer.
|
||||
|
||||
The regression test is running after each commit on travis.
|
||||
The regression test is running after each commit on github actions for multiple configurations.
|
||||
|
||||
* Linux
|
||||
* macOS with thread sanitizer
|
||||
* macOS, with OpenSSL, with thread sanitizer
|
||||
* macOS, with MbedTLS, with thread sanitizer
|
||||
* Windows, with MbedTLS (the unittest is not run yet)
|
||||
|
||||
## Limitations
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
||||
## Example code
|
||||
|
||||
```cpp
|
||||
```c++
|
||||
// Required on Windows
|
||||
ix::initNetSystem();
|
||||
|
||||
|
94
docs/packages.md
Normal file
94
docs/packages.md
Normal file
@ -0,0 +1,94 @@
|
||||
Notes on how we can update the different packages for ixwebsocket.
|
||||
|
||||
## VCPKG
|
||||
|
||||
Visit the [releases](https://github.com/machinezone/IXWebSocket/releases) page on Github. A tag must have been made first.
|
||||
|
||||
Download the latest entry.
|
||||
|
||||
```
|
||||
$ cd /tmp
|
||||
/tmp$ curl -s -O -L https://github.com/machinezone/IXWebSocket/archive/v9.1.9.tar.gz
|
||||
/tmp$
|
||||
/tmp$ openssl sha512 v9.1.9.tar.gz
|
||||
SHA512(v9.1.9.tar.gz)= f1fd731b5f6a9ce6d6d10bee22a5d9d9baaa8ea0564d6c4cd7eb91dcb88a45c49b2c7fdb75f8640a3589c1b30cee33ef5df8dcbb55920d013394d1e33ddd3c8e
|
||||
```
|
||||
|
||||
Now go punch those values in the vcpkg ixwebsocket port config files. Here is what the diff look like.
|
||||
|
||||
```
|
||||
vcpkg$ git diff
|
||||
diff --git a/ports/ixwebsocket/CONTROL b/ports/ixwebsocket/CONTROL
|
||||
index db9c2adc9..4acae5c3f 100644
|
||||
--- a/ports/ixwebsocket/CONTROL
|
||||
+++ b/ports/ixwebsocket/CONTROL
|
||||
@@ -1,5 +1,5 @@
|
||||
Source: ixwebsocket
|
||||
-Version: 8.0.5
|
||||
+Version: 9.1.9
|
||||
Build-Depends: zlib
|
||||
Homepage: https://github.com/machinezone/IXWebSocket
|
||||
Description: Lightweight WebSocket Client and Server + HTTP Client and Server
|
||||
diff --git a/ports/ixwebsocket/portfile.cmake b/ports/ixwebsocket/portfile.cmake
|
||||
index de082aece..68e523a05 100644
|
||||
--- a/ports/ixwebsocket/portfile.cmake
|
||||
+++ b/ports/ixwebsocket/portfile.cmake
|
||||
@@ -1,8 +1,8 @@
|
||||
vcpkg_from_github(
|
||||
OUT_SOURCE_PATH SOURCE_PATH
|
||||
REPO machinezone/IXWebSocket
|
||||
- REF v8.0.5
|
||||
- SHA512 9dcc20d9a0629b92c62a68a8bd7c8206f18dbd9e93289b0b687ec13c478ce9ad1f3563b38c399c8277b0d3812cc78ca725786ba1dedbc3445b9bdb9b689e8add
|
||||
+ REF v9.1.9
|
||||
+ SHA512 f1fd731b5f6a9ce6d6d10bee22a5d9d9baaa8ea0564d6c4cd7eb91dcb88a45c49b2c7fdb75f8640a3589c1b30cee33ef5df8dcbb55920d013394d1e33ddd3c8e
|
||||
)
|
||||
```
|
||||
|
||||
You will need a fork of the vcpkg repo to make a pull request.
|
||||
|
||||
```
|
||||
git fetch upstream
|
||||
git co master
|
||||
git reset --hard upstream/master
|
||||
git push origin master --force
|
||||
```
|
||||
|
||||
Make the pull request (I use a new branch to do that).
|
||||
|
||||
```
|
||||
vcpkg$ git co -b feature/ixwebsocket_9.1.9
|
||||
M ports/ixwebsocket/CONTROL
|
||||
M ports/ixwebsocket/portfile.cmake
|
||||
Switched to a new branch 'feature/ixwebsocket_9.1.9'
|
||||
vcpkg$
|
||||
vcpkg$
|
||||
vcpkg$ git commit -am 'ixwebsocket: update to 9.1.9'
|
||||
[feature/ixwebsocket_9.1.9 8587a4881] ixwebsocket: update to 9.1.9
|
||||
2 files changed, 3 insertions(+), 3 deletions(-)
|
||||
vcpkg$
|
||||
vcpkg$ git push
|
||||
fatal: The current branch feature/ixwebsocket_9.1.9 has no upstream branch.
|
||||
To push the current branch and set the remote as upstream, use
|
||||
|
||||
git push --set-upstream origin feature/ixwebsocket_9.1.9
|
||||
|
||||
vcpkg$ git push --set-upstream origin feature/ixwebsocket_9.1.9
|
||||
|
||||
Enumerating objects: 11, done.
|
||||
Counting objects: 100% (11/11), done.
|
||||
Delta compression using up to 8 threads
|
||||
Compressing objects: 100% (6/6), done.
|
||||
Writing objects: 100% (6/6), 621 bytes | 621.00 KiB/s, done.
|
||||
Total 6 (delta 4), reused 0 (delta 0)
|
||||
remote: Resolving deltas: 100% (4/4), completed with 4 local objects.
|
||||
remote:
|
||||
remote: Create a pull request for 'feature/ixwebsocket_9.1.9' on GitHub by visiting:
|
||||
remote: https://github.com/bsergean/vcpkg/pull/new/feature/ixwebsocket_9.1.9
|
||||
remote:
|
||||
To https://github.com/bsergean/vcpkg.git
|
||||
* [new branch] feature/ixwebsocket_9.1.9 -> feature/ixwebsocket_9.1.9
|
||||
Branch 'feature/ixwebsocket_9.1.9' set up to track remote branch 'feature/ixwebsocket_9.1.9' from 'origin' by rebasing.
|
||||
vcpkg$
|
||||
```
|
||||
|
||||
Just visit this url, https://github.com/bsergean/vcpkg/pull/new/feature/ixwebsocket_9.1.9, printed on the console, to make the pull request.
|
@ -35,7 +35,7 @@ webSocket.setUrl(url);
|
||||
|
||||
// Optional heart beat, sent every 45 seconds when there is not any traffic
|
||||
// to make sure that load balancers do not kill an idle connection.
|
||||
webSocket.setHeartBeatPeriod(45);
|
||||
webSocket.setPingInterval(45);
|
||||
|
||||
// Per message deflate connection is enabled by default. You can tweak its parameters or disable it
|
||||
webSocket.disablePerMessageDeflate();
|
||||
@ -174,7 +174,7 @@ when there is no any traffic to make sure that load balancers do not kill an
|
||||
idle connection.
|
||||
|
||||
```cpp
|
||||
webSocket.setHeartBeatPeriod(45);
|
||||
webSocket.setPingInterval(45);
|
||||
```
|
||||
|
||||
### Supply extra HTTP headers.
|
||||
@ -436,6 +436,8 @@ setOnConnectionCallback(
|
||||
|
||||
To leverage TLS features, the library must be compiled with the option `USE_TLS=1`.
|
||||
|
||||
If you are using OpenSSL, try to be on a version higher than 1.1.x as there there are thread safety problems with 1.0.x.
|
||||
|
||||
Then, secure sockets are automatically used when connecting to a `wss://*` url.
|
||||
|
||||
Additional TLS options can be configured by passing a `ix::SocketTLSOptions` instance to the
|
||||
|
@ -4,15 +4,19 @@
|
||||
#
|
||||
|
||||
set (IXBOTS_SOURCES
|
||||
ixbots/IXCobraBot.cpp
|
||||
ixbots/IXCobraToSentryBot.cpp
|
||||
ixbots/IXCobraToStatsdBot.cpp
|
||||
ixbots/IXCobraToStdoutBot.cpp
|
||||
ixbots/IXQueueManager.cpp
|
||||
ixbots/IXStatsdClient.cpp
|
||||
)
|
||||
|
||||
set (IXBOTS_HEADERS
|
||||
ixbots/IXCobraBot.h
|
||||
ixbots/IXCobraToSentryBot.h
|
||||
ixbots/IXCobraToStatsdBot.h
|
||||
ixbots/IXCobraToStdoutBot.h
|
||||
ixbots/IXQueueManager.h
|
||||
ixbots/IXStatsdClient.h
|
||||
)
|
||||
|
303
ixbots/ixbots/IXCobraBot.cpp
Normal file
303
ixbots/ixbots/IXCobraBot.cpp
Normal file
@ -0,0 +1,303 @@
|
||||
/*
|
||||
* IXCobraBot.cpp
|
||||
* Author: Benjamin Sergeant
|
||||
* Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "IXCobraBot.h"
|
||||
#include "IXQueueManager.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <ixcobra/IXCobraConnection.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
int64_t CobraBot::run(const CobraConfig& config,
|
||||
const std::string& channel,
|
||||
const std::string& filter,
|
||||
const std::string& position,
|
||||
bool verbose,
|
||||
size_t maxQueueSize,
|
||||
bool useQueue,
|
||||
bool enableHeartbeat,
|
||||
int runtime)
|
||||
{
|
||||
ix::CobraConnection conn;
|
||||
conn.configure(config);
|
||||
conn.connect();
|
||||
|
||||
Json::FastWriter jsonWriter;
|
||||
std::atomic<uint64_t> sentCount(0);
|
||||
std::atomic<uint64_t> receivedCount(0);
|
||||
std::atomic<uint64_t> sentCountTotal(0);
|
||||
std::atomic<uint64_t> receivedCountTotal(0);
|
||||
std::atomic<uint64_t> sentCountPerSecs(0);
|
||||
std::atomic<uint64_t> receivedCountPerSecs(0);
|
||||
std::atomic<bool> stop(false);
|
||||
std::atomic<bool> throttled(false);
|
||||
std::atomic<bool> fatalCobraError(false);
|
||||
|
||||
QueueManager queueManager(maxQueueSize);
|
||||
|
||||
auto timer = [&sentCount,
|
||||
&receivedCount,
|
||||
&sentCountTotal,
|
||||
&receivedCountTotal,
|
||||
&sentCountPerSecs,
|
||||
&receivedCountPerSecs,
|
||||
&stop] {
|
||||
while (!stop)
|
||||
{
|
||||
//
|
||||
// We cannot write to sentCount and receivedCount
|
||||
// as those are used externally, so we need to introduce
|
||||
// our own counters
|
||||
//
|
||||
spdlog::info("messages received {} {} sent {} {}",
|
||||
receivedCountPerSecs,
|
||||
receivedCountTotal,
|
||||
sentCountPerSecs,
|
||||
sentCountTotal);
|
||||
receivedCountPerSecs = receivedCount - receivedCountTotal;
|
||||
sentCountPerSecs = sentCount - receivedCountTotal;
|
||||
|
||||
receivedCountTotal += receivedCountPerSecs;
|
||||
sentCountTotal += sentCountPerSecs;
|
||||
|
||||
auto duration = std::chrono::seconds(1);
|
||||
std::this_thread::sleep_for(duration);
|
||||
}
|
||||
|
||||
spdlog::info("timer thread done");
|
||||
};
|
||||
|
||||
std::thread t1(timer);
|
||||
|
||||
auto heartbeat = [&sentCount, &receivedCount, &stop, &enableHeartbeat] {
|
||||
std::string state("na");
|
||||
|
||||
if (!enableHeartbeat) return;
|
||||
|
||||
while (!stop)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "messages received " << receivedCount;
|
||||
ss << "messages sent " << sentCount;
|
||||
|
||||
std::string currentState = ss.str();
|
||||
|
||||
if (currentState == state)
|
||||
{
|
||||
spdlog::error("no messages received or sent for 1 minute, exiting");
|
||||
exit(1);
|
||||
}
|
||||
state = currentState;
|
||||
|
||||
auto duration = std::chrono::minutes(1);
|
||||
std::this_thread::sleep_for(duration);
|
||||
}
|
||||
|
||||
spdlog::info("heartbeat thread done");
|
||||
};
|
||||
|
||||
std::thread t2(heartbeat);
|
||||
|
||||
auto sender =
|
||||
[this, &queueManager, verbose, &sentCount, &stop, &throttled, &fatalCobraError] {
|
||||
|
||||
while (true)
|
||||
{
|
||||
auto data = queueManager.pop();
|
||||
Json::Value msg = data.first;
|
||||
std::string position = data.second;
|
||||
|
||||
if (stop) break;
|
||||
if (msg.isNull()) continue;
|
||||
|
||||
if (_onBotMessageCallback && _onBotMessageCallback(msg, position, verbose, throttled, fatalCobraError))
|
||||
{
|
||||
// That might be too noisy
|
||||
if (verbose)
|
||||
{
|
||||
spdlog::info("cobra bot: sending succesfull");
|
||||
}
|
||||
++sentCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
spdlog::error("cobra bot: error sending");
|
||||
}
|
||||
|
||||
if (stop) break;
|
||||
}
|
||||
|
||||
spdlog::info("sender thread done");
|
||||
};
|
||||
|
||||
std::thread t3(sender);
|
||||
|
||||
std::string subscriptionPosition(position);
|
||||
|
||||
conn.setEventCallback([this,
|
||||
&conn,
|
||||
&channel,
|
||||
&filter,
|
||||
&subscriptionPosition,
|
||||
&jsonWriter,
|
||||
verbose,
|
||||
&throttled,
|
||||
&receivedCount,
|
||||
&fatalCobraError,
|
||||
&useQueue,
|
||||
&queueManager,
|
||||
&sentCount](const CobraEventPtr& event)
|
||||
{
|
||||
if (event->type == ix::CobraEventType::Open)
|
||||
{
|
||||
spdlog::info("Subscriber connected");
|
||||
|
||||
for (auto&& it : event->headers)
|
||||
{
|
||||
spdlog::info("{}: {}", it.first, it.second);
|
||||
}
|
||||
}
|
||||
else if (event->type == ix::CobraEventType::Closed)
|
||||
{
|
||||
spdlog::info("Subscriber closed: {}", event->errMsg);
|
||||
}
|
||||
else if (event->type == ix::CobraEventType::Authenticated)
|
||||
{
|
||||
spdlog::info("Subscriber authenticated");
|
||||
spdlog::info("Subscribing to {} at position {}", channel, subscriptionPosition);
|
||||
spdlog::info("Using filter: {}", filter);
|
||||
conn.subscribe(channel,
|
||||
filter,
|
||||
subscriptionPosition,
|
||||
[this, &jsonWriter, verbose, &throttled, &receivedCount, &queueManager, &useQueue, &subscriptionPosition, &fatalCobraError, &sentCount](
|
||||
const Json::Value& msg, const std::string& position) {
|
||||
if (verbose)
|
||||
{
|
||||
spdlog::info("Subscriber received message {} -> {}", position, jsonWriter.write(msg));
|
||||
}
|
||||
|
||||
subscriptionPosition = position;
|
||||
|
||||
// If we cannot send to sentry fast enough, drop the message
|
||||
if (throttled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
++receivedCount;
|
||||
|
||||
if (useQueue)
|
||||
{
|
||||
queueManager.add(msg, position);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_onBotMessageCallback && _onBotMessageCallback(msg, position, verbose, throttled, fatalCobraError))
|
||||
{
|
||||
// That might be too noisy
|
||||
if (verbose)
|
||||
{
|
||||
spdlog::info("cobra bot: sending succesfull");
|
||||
}
|
||||
++sentCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
spdlog::error("cobra bot: error sending");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (event->type == ix::CobraEventType::Subscribed)
|
||||
{
|
||||
spdlog::info("Subscriber: subscribed to channel {}", event->subscriptionId);
|
||||
}
|
||||
else if (event->type == ix::CobraEventType::UnSubscribed)
|
||||
{
|
||||
spdlog::info("Subscriber: unsubscribed from channel {}", event->subscriptionId);
|
||||
}
|
||||
else if (event->type == ix::CobraEventType::Error)
|
||||
{
|
||||
spdlog::error("Subscriber: error {}", event->errMsg);
|
||||
}
|
||||
else if (event->type == ix::CobraEventType::Published)
|
||||
{
|
||||
spdlog::error("Published message hacked: {}", event->msgId);
|
||||
}
|
||||
else if (event->type == ix::CobraEventType::Pong)
|
||||
{
|
||||
spdlog::info("Received websocket pong: {}", event->errMsg);
|
||||
}
|
||||
else if (event->type == ix::CobraEventType::HandshakeError)
|
||||
{
|
||||
spdlog::error("Subscriber: Handshake error: {}", event->errMsg);
|
||||
fatalCobraError = true;
|
||||
}
|
||||
else if (event->type == ix::CobraEventType::AuthenticationError)
|
||||
{
|
||||
spdlog::error("Subscriber: Authentication error: {}", event->errMsg);
|
||||
fatalCobraError = true;
|
||||
}
|
||||
else if (event->type == ix::CobraEventType::SubscriptionError)
|
||||
{
|
||||
spdlog::error("Subscriber: Subscription error: {}", event->errMsg);
|
||||
fatalCobraError = true;
|
||||
}
|
||||
});
|
||||
|
||||
// Run forever
|
||||
if (runtime == -1)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
auto duration = std::chrono::seconds(1);
|
||||
std::this_thread::sleep_for(duration);
|
||||
|
||||
if (fatalCobraError) break;
|
||||
}
|
||||
}
|
||||
// Run for a duration, used by unittesting now
|
||||
else
|
||||
{
|
||||
for (int i = 0 ; i < runtime; ++i)
|
||||
{
|
||||
auto duration = std::chrono::seconds(1);
|
||||
std::this_thread::sleep_for(duration);
|
||||
|
||||
if (fatalCobraError) break;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Cleanup.
|
||||
// join all the bg threads and stop them.
|
||||
//
|
||||
conn.disconnect();
|
||||
stop = true;
|
||||
|
||||
// progress thread
|
||||
t1.join();
|
||||
|
||||
// heartbeat thread
|
||||
if (t2.joinable()) t2.join();
|
||||
|
||||
// sentry sender thread
|
||||
t3.join();
|
||||
|
||||
return fatalCobraError ? -1 : (int64_t) sentCount;
|
||||
}
|
||||
|
||||
void CobraBot::setOnBotMessageCallback(const OnBotMessageCallback& callback)
|
||||
{
|
||||
_onBotMessageCallback = callback;
|
||||
}
|
||||
}
|
43
ixbots/ixbots/IXCobraBot.h
Normal file
43
ixbots/ixbots/IXCobraBot.h
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* IXCobraBot.h
|
||||
* Author: Benjamin Sergeant
|
||||
* Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ixcobra/IXCobraConfig.h>
|
||||
#include <stddef.h>
|
||||
#include <json/json.h>
|
||||
#include <functional>
|
||||
#include <atomic>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
using OnBotMessageCallback = std::function<bool(const Json::Value&,
|
||||
const std::string&,
|
||||
const bool verbose,
|
||||
std::atomic<bool>&,
|
||||
std::atomic<bool>&)>;
|
||||
|
||||
class CobraBot
|
||||
{
|
||||
public:
|
||||
CobraBot() = default;
|
||||
|
||||
int64_t run(const CobraConfig& config,
|
||||
const std::string& channel,
|
||||
const std::string& filter,
|
||||
const std::string& position,
|
||||
bool verbose,
|
||||
size_t maxQueueSize,
|
||||
bool useQueue,
|
||||
bool enableHeartbeat,
|
||||
int runtime);
|
||||
|
||||
void setOnBotMessageCallback(const OnBotMessageCallback& callback);
|
||||
|
||||
private:
|
||||
OnBotMessageCallback _onBotMessageCallback;
|
||||
};
|
||||
}
|
@ -5,279 +5,113 @@
|
||||
*/
|
||||
|
||||
#include "IXCobraToSentryBot.h"
|
||||
#include "IXCobraBot.h"
|
||||
#include "IXQueueManager.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <ixcobra/IXCobraConnection.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
int cobra_to_sentry_bot(const CobraConfig& config,
|
||||
const std::string& channel,
|
||||
const std::string& filter,
|
||||
const std::string& position,
|
||||
SentryClient& sentryClient,
|
||||
bool verbose,
|
||||
bool strict,
|
||||
size_t maxQueueSize,
|
||||
bool enableHeartbeat,
|
||||
int runtime)
|
||||
int64_t cobra_to_sentry_bot(const CobraConfig& config,
|
||||
const std::string& channel,
|
||||
const std::string& filter,
|
||||
const std::string& position,
|
||||
SentryClient& sentryClient,
|
||||
bool verbose,
|
||||
size_t maxQueueSize,
|
||||
bool enableHeartbeat,
|
||||
int runtime)
|
||||
{
|
||||
ix::CobraConnection conn;
|
||||
conn.configure(config);
|
||||
conn.connect();
|
||||
CobraBot bot;
|
||||
bot.setOnBotMessageCallback([&sentryClient](const Json::Value& msg,
|
||||
const std::string& /*position*/,
|
||||
const bool verbose,
|
||||
std::atomic<bool>& throttled,
|
||||
std::atomic<bool>& /*fatalCobraError*/) -> bool {
|
||||
auto ret = sentryClient.send(msg, verbose);
|
||||
HttpResponsePtr response = ret.first;
|
||||
|
||||
Json::FastWriter jsonWriter;
|
||||
std::atomic<uint64_t> sentCount(0);
|
||||
std::atomic<uint64_t> receivedCount(0);
|
||||
std::atomic<bool> errorSending(false);
|
||||
std::atomic<bool> stop(false);
|
||||
std::atomic<bool> throttled(false);
|
||||
|
||||
QueueManager queueManager(maxQueueSize);
|
||||
|
||||
auto timer = [&sentCount, &receivedCount, &stop] {
|
||||
while (!stop)
|
||||
if (!response)
|
||||
{
|
||||
spdlog::info("messages received {} sent {}", receivedCount, sentCount);
|
||||
|
||||
auto duration = std::chrono::seconds(1);
|
||||
std::this_thread::sleep_for(duration);
|
||||
spdlog::warn("Null HTTP Response");
|
||||
return false;
|
||||
}
|
||||
|
||||
spdlog::info("timer thread done");
|
||||
};
|
||||
|
||||
std::thread t1(timer);
|
||||
|
||||
auto heartbeat = [&sentCount, &receivedCount, &stop, &enableHeartbeat] {
|
||||
std::string state("na");
|
||||
|
||||
if (!enableHeartbeat) return;
|
||||
|
||||
while (!stop)
|
||||
if (verbose)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "messages received " << receivedCount;
|
||||
ss << "messages sent " << sentCount;
|
||||
|
||||
std::string currentState = ss.str();
|
||||
|
||||
if (currentState == state)
|
||||
{
|
||||
spdlog::error("no messages received or sent for 1 minute, exiting");
|
||||
exit(1);
|
||||
}
|
||||
state = currentState;
|
||||
|
||||
auto duration = std::chrono::minutes(1);
|
||||
std::this_thread::sleep_for(duration);
|
||||
}
|
||||
|
||||
spdlog::info("heartbeat thread done");
|
||||
};
|
||||
|
||||
std::thread t2(heartbeat);
|
||||
|
||||
auto sentrySender =
|
||||
[&queueManager, verbose, &errorSending, &sentCount, &stop, &throttled, &sentryClient] {
|
||||
|
||||
while (true)
|
||||
{
|
||||
Json::Value msg = queueManager.pop();
|
||||
|
||||
if (stop) break;
|
||||
if (msg.isNull()) continue;
|
||||
|
||||
auto ret = sentryClient.send(msg, verbose);
|
||||
HttpResponsePtr response = ret.first;
|
||||
|
||||
if (!response)
|
||||
{
|
||||
spdlog::warn("Null HTTP Response");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (verbose)
|
||||
{
|
||||
for (auto it : response->headers)
|
||||
{
|
||||
spdlog::info("{}: {}", it.first, it.second);
|
||||
}
|
||||
|
||||
spdlog::info("Upload size: {}", response->uploadSize);
|
||||
spdlog::info("Download size: {}", response->downloadSize);
|
||||
|
||||
spdlog::info("Status: {}", response->statusCode);
|
||||
if (response->errorCode != HttpErrorCode::Ok)
|
||||
{
|
||||
spdlog::info("error message: {}", response->errorMsg);
|
||||
}
|
||||
|
||||
if (response->headers["Content-Type"] != "application/octet-stream")
|
||||
{
|
||||
spdlog::info("payload: {}", response->payload);
|
||||
}
|
||||
}
|
||||
|
||||
if (response->statusCode != 200)
|
||||
{
|
||||
spdlog::error("Error sending data to sentry: {}", response->statusCode);
|
||||
spdlog::error("Body: {}", ret.second);
|
||||
spdlog::error("Response: {}", response->payload);
|
||||
errorSending = true;
|
||||
|
||||
// Error 429 Too Many Requests
|
||||
if (response->statusCode == 429)
|
||||
{
|
||||
auto retryAfter = response->headers["Retry-After"];
|
||||
std::stringstream ss;
|
||||
ss << retryAfter;
|
||||
int seconds;
|
||||
ss >> seconds;
|
||||
|
||||
if (!ss.eof() || ss.fail())
|
||||
{
|
||||
seconds = 30;
|
||||
spdlog::warn("Error parsing Retry-After header. "
|
||||
"Using {} for the sleep duration",
|
||||
seconds);
|
||||
}
|
||||
|
||||
spdlog::warn("Error 429 - Too Many Requests. ws will sleep "
|
||||
"and retry after {} seconds",
|
||||
retryAfter);
|
||||
|
||||
throttled = true;
|
||||
auto duration = std::chrono::seconds(seconds);
|
||||
std::this_thread::sleep_for(duration);
|
||||
throttled = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
++sentCount;
|
||||
}
|
||||
|
||||
if (stop) break;
|
||||
}
|
||||
|
||||
spdlog::info("sentrySender thread done");
|
||||
};
|
||||
|
||||
std::thread t3(sentrySender);
|
||||
|
||||
conn.setEventCallback([&conn,
|
||||
&channel,
|
||||
&filter,
|
||||
&position,
|
||||
&jsonWriter,
|
||||
verbose,
|
||||
&throttled,
|
||||
&receivedCount,
|
||||
&queueManager](ix::CobraConnectionEventType eventType,
|
||||
const std::string& errMsg,
|
||||
const ix::WebSocketHttpHeaders& headers,
|
||||
const std::string& subscriptionId,
|
||||
CobraConnection::MsgId msgId) {
|
||||
if (eventType == ix::CobraConnection_EventType_Open)
|
||||
{
|
||||
spdlog::info("Subscriber connected");
|
||||
|
||||
for (auto it : headers)
|
||||
for (auto it : response->headers)
|
||||
{
|
||||
spdlog::info("{}: {}", it.first, it.second);
|
||||
}
|
||||
}
|
||||
if (eventType == ix::CobraConnection_EventType_Closed)
|
||||
{
|
||||
spdlog::info("Subscriber closed");
|
||||
}
|
||||
else if (eventType == ix::CobraConnection_EventType_Authenticated)
|
||||
{
|
||||
spdlog::info("Subscriber authenticated");
|
||||
conn.subscribe(channel,
|
||||
filter,
|
||||
position,
|
||||
[&jsonWriter, verbose, &throttled, &receivedCount, &queueManager](
|
||||
const Json::Value& msg, const std::string& position) {
|
||||
if (verbose)
|
||||
{
|
||||
spdlog::info("Subscriber received message {} -> {}", position, jsonWriter.write(msg));
|
||||
}
|
||||
|
||||
// If we cannot send to sentry fast enough, drop the message
|
||||
if (throttled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
spdlog::info("Upload size: {}", response->uploadSize);
|
||||
spdlog::info("Download size: {}", response->downloadSize);
|
||||
|
||||
++receivedCount;
|
||||
queueManager.add(msg);
|
||||
});
|
||||
spdlog::info("Status: {}", response->statusCode);
|
||||
if (response->errorCode != HttpErrorCode::Ok)
|
||||
{
|
||||
spdlog::info("error message: {}", response->errorMsg);
|
||||
}
|
||||
|
||||
if (response->headers["Content-Type"] != "application/octet-stream")
|
||||
{
|
||||
spdlog::info("payload: {}", response->payload);
|
||||
}
|
||||
}
|
||||
else if (eventType == ix::CobraConnection_EventType_Subscribed)
|
||||
|
||||
bool success = response->statusCode == 200;
|
||||
|
||||
if (!success)
|
||||
{
|
||||
spdlog::info("Subscriber: subscribed to channel {}", subscriptionId);
|
||||
}
|
||||
else if (eventType == ix::CobraConnection_EventType_UnSubscribed)
|
||||
{
|
||||
spdlog::info("Subscriber: unsubscribed from channel {}", subscriptionId);
|
||||
}
|
||||
else if (eventType == ix::CobraConnection_EventType_Error)
|
||||
{
|
||||
spdlog::error("Subscriber: error {}", errMsg);
|
||||
}
|
||||
else if (eventType == ix::CobraConnection_EventType_Published)
|
||||
{
|
||||
spdlog::error("Published message hacked: {}", msgId);
|
||||
}
|
||||
else if (eventType == ix::CobraConnection_EventType_Pong)
|
||||
{
|
||||
spdlog::info("Received websocket pong");
|
||||
spdlog::error("Error sending data to sentry: {}", response->statusCode);
|
||||
spdlog::error("Body: {}", ret.second);
|
||||
spdlog::error("Response: {}", response->payload);
|
||||
|
||||
// Error 429 Too Many Requests
|
||||
if (response->statusCode == 429)
|
||||
{
|
||||
auto retryAfter = response->headers["Retry-After"];
|
||||
std::stringstream ss;
|
||||
ss << retryAfter;
|
||||
int seconds;
|
||||
ss >> seconds;
|
||||
|
||||
if (!ss.eof() || ss.fail())
|
||||
{
|
||||
seconds = 30;
|
||||
spdlog::warn("Error parsing Retry-After header. "
|
||||
"Using {} for the sleep duration",
|
||||
seconds);
|
||||
}
|
||||
|
||||
spdlog::warn("Error 429 - Too Many Requests. ws will sleep "
|
||||
"and retry after {} seconds",
|
||||
retryAfter);
|
||||
|
||||
throttled = true;
|
||||
auto duration = std::chrono::seconds(seconds);
|
||||
std::this_thread::sleep_for(duration);
|
||||
throttled = false;
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
});
|
||||
|
||||
// Run forever
|
||||
if (runtime == -1)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
auto duration = std::chrono::seconds(1);
|
||||
std::this_thread::sleep_for(duration);
|
||||
bool useQueue = true;
|
||||
|
||||
if (strict && errorSending) break;
|
||||
}
|
||||
}
|
||||
// Run for a duration, used by unittesting now
|
||||
else
|
||||
{
|
||||
for (int i = 0 ; i < runtime; ++i)
|
||||
{
|
||||
auto duration = std::chrono::seconds(1);
|
||||
std::this_thread::sleep_for(duration);
|
||||
|
||||
if (strict && errorSending) break;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Cleanup.
|
||||
// join all the bg threads and stop them.
|
||||
//
|
||||
conn.disconnect();
|
||||
stop = true;
|
||||
|
||||
t1.join();
|
||||
if (t2.joinable()) t2.join();
|
||||
spdlog::info("heartbeat thread done");
|
||||
|
||||
t3.join();
|
||||
|
||||
return (strict && errorSending) ? -1 : (int) sentCount;
|
||||
return bot.run(config,
|
||||
channel,
|
||||
filter,
|
||||
position,
|
||||
verbose,
|
||||
maxQueueSize,
|
||||
useQueue,
|
||||
enableHeartbeat,
|
||||
runtime);
|
||||
}
|
||||
} // namespace ix
|
||||
|
@ -8,17 +8,17 @@
|
||||
#include <ixcobra/IXCobraConfig.h>
|
||||
#include <ixsentry/IXSentryClient.h>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
int cobra_to_sentry_bot(const CobraConfig& config,
|
||||
const std::string& channel,
|
||||
const std::string& filter,
|
||||
const std::string& position,
|
||||
SentryClient& sentryClient,
|
||||
bool verbose,
|
||||
bool strict,
|
||||
size_t maxQueueSize,
|
||||
bool enableHeartbeat,
|
||||
int runtime);
|
||||
int64_t cobra_to_sentry_bot(const CobraConfig& config,
|
||||
const std::string& channel,
|
||||
const std::string& filter,
|
||||
const std::string& position,
|
||||
SentryClient& sentryClient,
|
||||
bool verbose,
|
||||
size_t maxQueueSize,
|
||||
bool enableHeartbeat,
|
||||
int runtime);
|
||||
} // namespace ix
|
||||
|
@ -7,14 +7,12 @@
|
||||
#include "IXCobraToStatsdBot.h"
|
||||
#include "IXQueueManager.h"
|
||||
#include "IXStatsdClient.h"
|
||||
#include "IXCobraBot.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <ixcobra/IXCobraConnection.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
namespace ix
|
||||
@ -40,7 +38,7 @@ namespace ix
|
||||
// Extract an attribute from a Json Value.
|
||||
// extractAttr("foo.bar", {"foo": {"bar": "baz"}}) => baz
|
||||
//
|
||||
std::string extractAttr(const std::string& attr, const Json::Value& jsonValue)
|
||||
Json::Value extractAttr(const std::string& attr, const Json::Value& jsonValue)
|
||||
{
|
||||
// Split by .
|
||||
std::string token;
|
||||
@ -53,19 +51,21 @@ namespace ix
|
||||
val = val[token];
|
||||
}
|
||||
|
||||
return val.asString();
|
||||
return val;
|
||||
}
|
||||
|
||||
int cobra_to_statsd_bot(const ix::CobraConfig& config,
|
||||
const std::string& channel,
|
||||
const std::string& filter,
|
||||
const std::string& position,
|
||||
StatsdClient& statsdClient,
|
||||
const std::string& fields,
|
||||
bool verbose,
|
||||
size_t maxQueueSize,
|
||||
bool enableHeartbeat,
|
||||
int runtime)
|
||||
int64_t cobra_to_statsd_bot(const ix::CobraConfig& config,
|
||||
const std::string& channel,
|
||||
const std::string& filter,
|
||||
const std::string& position,
|
||||
StatsdClient& statsdClient,
|
||||
const std::string& fields,
|
||||
const std::string& gauge,
|
||||
const std::string& timer,
|
||||
bool verbose,
|
||||
size_t maxQueueSize,
|
||||
bool enableHeartbeat,
|
||||
int runtime)
|
||||
{
|
||||
ix::CobraConnection conn;
|
||||
conn.configure(config);
|
||||
@ -73,167 +73,85 @@ namespace ix
|
||||
|
||||
auto tokens = parseFields(fields);
|
||||
|
||||
Json::FastWriter jsonWriter;
|
||||
std::atomic<uint64_t> sentCount(0);
|
||||
std::atomic<uint64_t> receivedCount(0);
|
||||
std::atomic<bool> stop(false);
|
||||
|
||||
QueueManager queueManager(maxQueueSize);
|
||||
|
||||
auto timer = [&sentCount, &receivedCount, &stop] {
|
||||
while (!stop)
|
||||
CobraBot bot;
|
||||
bot.setOnBotMessageCallback([&statsdClient, &tokens, &gauge, &timer](const Json::Value& msg,
|
||||
const std::string& /*position*/,
|
||||
const bool verbose,
|
||||
std::atomic<bool>& /*throttled*/,
|
||||
std::atomic<bool>& fatalCobraError) -> bool {
|
||||
std::string id;
|
||||
for (auto&& attr : tokens)
|
||||
{
|
||||
spdlog::info("messages received {} sent {}", receivedCount, sentCount);
|
||||
|
||||
auto duration = std::chrono::seconds(1);
|
||||
std::this_thread::sleep_for(duration);
|
||||
id += ".";
|
||||
auto val = extractAttr(attr, msg);
|
||||
id += val.asString();
|
||||
}
|
||||
};
|
||||
|
||||
std::thread t1(timer);
|
||||
|
||||
auto heartbeat = [&sentCount, &receivedCount, &enableHeartbeat] {
|
||||
std::string state("na");
|
||||
|
||||
if (!enableHeartbeat) return;
|
||||
|
||||
while (true)
|
||||
if (gauge.empty() && timer.empty())
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "messages received " << receivedCount;
|
||||
ss << "messages sent " << sentCount;
|
||||
|
||||
std::string currentState = ss.str();
|
||||
|
||||
if (currentState == state)
|
||||
{
|
||||
spdlog::error("no messages received or sent for 1 minute, exiting");
|
||||
exit(1);
|
||||
}
|
||||
state = currentState;
|
||||
|
||||
auto duration = std::chrono::minutes(1);
|
||||
std::this_thread::sleep_for(duration);
|
||||
}
|
||||
};
|
||||
|
||||
std::thread t2(heartbeat);
|
||||
|
||||
auto statsdSender = [&statsdClient, &queueManager, &sentCount, &tokens, &stop] {
|
||||
while (true)
|
||||
{
|
||||
Json::Value msg = queueManager.pop();
|
||||
|
||||
if (stop) return;
|
||||
if (msg.isNull()) continue;
|
||||
|
||||
std::string id;
|
||||
for (auto&& attr : tokens)
|
||||
{
|
||||
id += ".";
|
||||
id += extractAttr(attr, msg);
|
||||
}
|
||||
|
||||
statsdClient.count(id, 1);
|
||||
sentCount += 1;
|
||||
}
|
||||
};
|
||||
|
||||
std::thread t3(statsdSender);
|
||||
|
||||
conn.setEventCallback(
|
||||
[&conn, &channel, &filter, &position, &jsonWriter, verbose, &queueManager, &receivedCount](
|
||||
ix::CobraConnectionEventType eventType,
|
||||
const std::string& errMsg,
|
||||
const ix::WebSocketHttpHeaders& headers,
|
||||
const std::string& subscriptionId,
|
||||
CobraConnection::MsgId msgId) {
|
||||
if (eventType == ix::CobraConnection_EventType_Open)
|
||||
{
|
||||
spdlog::info("Subscriber connected");
|
||||
|
||||
for (auto it : headers)
|
||||
{
|
||||
spdlog::info("{}: {}", it.first, it.second);
|
||||
}
|
||||
}
|
||||
if (eventType == ix::CobraConnection_EventType_Closed)
|
||||
{
|
||||
spdlog::info("Subscriber closed");
|
||||
}
|
||||
else if (eventType == ix::CobraConnection_EventType_Authenticated)
|
||||
{
|
||||
spdlog::info("Subscriber authenticated");
|
||||
conn.subscribe(channel,
|
||||
filter,
|
||||
position,
|
||||
[&jsonWriter, &queueManager, verbose, &receivedCount](
|
||||
const Json::Value& msg, const std::string& position) {
|
||||
if (verbose)
|
||||
{
|
||||
spdlog::info("Subscriber received message {} -> {}", position, jsonWriter.write(msg));
|
||||
}
|
||||
|
||||
receivedCount++;
|
||||
|
||||
++receivedCount;
|
||||
queueManager.add(msg);
|
||||
});
|
||||
}
|
||||
else if (eventType == ix::CobraConnection_EventType_Subscribed)
|
||||
{
|
||||
spdlog::info("Subscriber: subscribed to channel {}", subscriptionId);
|
||||
}
|
||||
else if (eventType == ix::CobraConnection_EventType_UnSubscribed)
|
||||
{
|
||||
spdlog::info("Subscriber: unsubscribed from channel {}", subscriptionId);
|
||||
}
|
||||
else if (eventType == ix::CobraConnection_EventType_Error)
|
||||
{
|
||||
spdlog::error("Subscriber: error {}", errMsg);
|
||||
}
|
||||
else if (eventType == ix::CobraConnection_EventType_Published)
|
||||
{
|
||||
spdlog::error("Published message hacked: {}", msgId);
|
||||
}
|
||||
else if (eventType == ix::CobraConnection_EventType_Pong)
|
||||
{
|
||||
spdlog::info("Received websocket pong");
|
||||
}
|
||||
});
|
||||
|
||||
// Run forever
|
||||
if (runtime == -1)
|
||||
{
|
||||
while (true)
|
||||
else
|
||||
{
|
||||
auto duration = std::chrono::seconds(1);
|
||||
std::this_thread::sleep_for(duration);
|
||||
std::string attrName = (!gauge.empty()) ? gauge : timer;
|
||||
auto val = extractAttr(attrName, msg);
|
||||
size_t x;
|
||||
|
||||
if (val.isInt())
|
||||
{
|
||||
x = (size_t) val.asInt();
|
||||
}
|
||||
else if (val.isInt64())
|
||||
{
|
||||
x = (size_t) val.asInt64();
|
||||
}
|
||||
else if (val.isUInt())
|
||||
{
|
||||
x = (size_t) val.asUInt();
|
||||
}
|
||||
else if (val.isUInt64())
|
||||
{
|
||||
x = (size_t) val.asUInt64();
|
||||
}
|
||||
else if (val.isDouble())
|
||||
{
|
||||
x = (size_t) val.asUInt64();
|
||||
}
|
||||
else
|
||||
{
|
||||
spdlog::error("Gauge {} is not a numeric type", gauge);
|
||||
fatalCobraError = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (verbose)
|
||||
{
|
||||
spdlog::info("{} - {} -> {}", id, attrName, x);
|
||||
}
|
||||
|
||||
if (!gauge.empty())
|
||||
{
|
||||
statsdClient.gauge(id, x);
|
||||
}
|
||||
else
|
||||
{
|
||||
statsdClient.timing(id, x);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Run for a duration, used by unittesting now
|
||||
else
|
||||
{
|
||||
for (int i = 0 ; i < runtime; ++i)
|
||||
{
|
||||
auto duration = std::chrono::seconds(1);
|
||||
std::this_thread::sleep_for(duration);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Cleanup.
|
||||
// join all the bg threads and stop them.
|
||||
//
|
||||
conn.disconnect();
|
||||
stop = true;
|
||||
return true;
|
||||
});
|
||||
|
||||
t1.join();
|
||||
if (t2.joinable()) t2.join();
|
||||
spdlog::info("heartbeat thread done");
|
||||
bool useQueue = true;
|
||||
|
||||
t3.join();
|
||||
|
||||
return (int) sentCount;
|
||||
return bot.run(config,
|
||||
channel,
|
||||
filter,
|
||||
position,
|
||||
verbose,
|
||||
maxQueueSize,
|
||||
useQueue,
|
||||
enableHeartbeat,
|
||||
runtime);
|
||||
}
|
||||
} // namespace ix
|
||||
|
@ -9,17 +9,20 @@
|
||||
#include <ixbots/IXStatsdClient.h>
|
||||
#include <string>
|
||||
#include <stddef.h>
|
||||
#include <cstdint>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
int cobra_to_statsd_bot(const ix::CobraConfig& config,
|
||||
const std::string& channel,
|
||||
const std::string& filter,
|
||||
const std::string& position,
|
||||
StatsdClient& statsdClient,
|
||||
const std::string& fields,
|
||||
bool verbose,
|
||||
size_t maxQueueSize,
|
||||
bool enableHeartbeat,
|
||||
int runtime);
|
||||
int64_t cobra_to_statsd_bot(const ix::CobraConfig& config,
|
||||
const std::string& channel,
|
||||
const std::string& filter,
|
||||
const std::string& position,
|
||||
StatsdClient& statsdClient,
|
||||
const std::string& fields,
|
||||
const std::string& gauge,
|
||||
const std::string& timer,
|
||||
bool verbose,
|
||||
size_t maxQueueSize,
|
||||
bool enableHeartbeat,
|
||||
int runtime);
|
||||
} // namespace ix
|
||||
|
106
ixbots/ixbots/IXCobraToStdoutBot.cpp
Normal file
106
ixbots/ixbots/IXCobraToStdoutBot.cpp
Normal file
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* IXCobraToStdoutBot.cpp
|
||||
* Author: Benjamin Sergeant
|
||||
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "IXCobraToStdoutBot.h"
|
||||
#include "IXCobraBot.h"
|
||||
#include "IXQueueManager.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
using StreamWriterPtr = std::unique_ptr<Json::StreamWriter>;
|
||||
|
||||
StreamWriterPtr makeStreamWriter()
|
||||
{
|
||||
Json::StreamWriterBuilder builder;
|
||||
builder["commentStyle"] = "None";
|
||||
builder["indentation"] = ""; // will make the JSON object compact
|
||||
std::unique_ptr<Json::StreamWriter> jsonWriter(builder.newStreamWriter());
|
||||
return jsonWriter;
|
||||
}
|
||||
|
||||
std::string timeSinceEpoch()
|
||||
{
|
||||
std::chrono::system_clock::time_point tp = std::chrono::system_clock::now();
|
||||
std::chrono::system_clock::duration dtn = tp.time_since_epoch();
|
||||
|
||||
std::stringstream ss;
|
||||
ss << dtn.count() * std::chrono::system_clock::period::num /
|
||||
std::chrono::system_clock::period::den;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
void writeToStdout(bool fluentd,
|
||||
const StreamWriterPtr& jsonWriter,
|
||||
const Json::Value& msg,
|
||||
const std::string& position)
|
||||
{
|
||||
Json::Value enveloppe;
|
||||
if (fluentd)
|
||||
{
|
||||
enveloppe["producer"] = "cobra";
|
||||
enveloppe["consumer"] = "fluentd";
|
||||
|
||||
Json::Value nestedMessage(msg);
|
||||
nestedMessage["position"] = position;
|
||||
nestedMessage["created_at"] = timeSinceEpoch();
|
||||
enveloppe["message"] = nestedMessage;
|
||||
|
||||
jsonWriter->write(enveloppe, &std::cout);
|
||||
std::cout << std::endl; // add lf and flush
|
||||
}
|
||||
else
|
||||
{
|
||||
enveloppe = msg;
|
||||
std::cout << position << " ";
|
||||
jsonWriter->write(enveloppe, &std::cout);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
int64_t cobra_to_stdout_bot(const CobraConfig& config,
|
||||
const std::string& channel,
|
||||
const std::string& filter,
|
||||
const std::string& position,
|
||||
bool fluentd,
|
||||
bool quiet,
|
||||
bool verbose,
|
||||
size_t maxQueueSize,
|
||||
bool enableHeartbeat,
|
||||
int runtime)
|
||||
{
|
||||
CobraBot bot;
|
||||
auto jsonWriter = makeStreamWriter();
|
||||
|
||||
bot.setOnBotMessageCallback([&fluentd, &quiet, &jsonWriter](const Json::Value& msg,
|
||||
const std::string& position,
|
||||
const bool /*verbose*/,
|
||||
std::atomic<bool>& /*throttled*/,
|
||||
std::atomic<bool>& /*fatalCobraError*/) -> bool {
|
||||
if (!quiet)
|
||||
{
|
||||
writeToStdout(fluentd, jsonWriter, msg, position);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
bool useQueue = false;
|
||||
|
||||
return bot.run(config,
|
||||
channel,
|
||||
filter,
|
||||
position,
|
||||
verbose,
|
||||
maxQueueSize,
|
||||
useQueue,
|
||||
enableHeartbeat,
|
||||
runtime);
|
||||
}
|
||||
} // namespace ix
|
25
ixbots/ixbots/IXCobraToStdoutBot.h
Normal file
25
ixbots/ixbots/IXCobraToStdoutBot.h
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* IXCobraToStdoutBot.h
|
||||
* Author: Benjamin Sergeant
|
||||
* Copyright (c) 2019-2020 Machine Zone, Inc. All rights reserved.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <ixcobra/IXCobraConfig.h>
|
||||
#include <string>
|
||||
#include <stddef.h>
|
||||
#include <cstdint>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
int64_t cobra_to_stdout_bot(const ix::CobraConfig& config,
|
||||
const std::string& channel,
|
||||
const std::string& filter,
|
||||
const std::string& position,
|
||||
bool fluentd,
|
||||
bool quiet,
|
||||
bool verbose,
|
||||
size_t maxQueueSize,
|
||||
bool enableHeartbeat,
|
||||
int runtime);
|
||||
} // namespace ix
|
@ -10,14 +10,14 @@
|
||||
|
||||
namespace ix
|
||||
{
|
||||
Json::Value QueueManager::pop()
|
||||
std::pair<Json::Value, std::string> QueueManager::pop()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_mutex);
|
||||
|
||||
if (_queues.empty())
|
||||
{
|
||||
Json::Value val;
|
||||
return val;
|
||||
return std::make_pair(val, std::string());
|
||||
}
|
||||
|
||||
std::vector<std::string> games;
|
||||
@ -35,7 +35,7 @@ namespace ix
|
||||
if (_queues[game].empty())
|
||||
{
|
||||
Json::Value val;
|
||||
return val;
|
||||
return std::make_pair(val, std::string());
|
||||
}
|
||||
|
||||
auto msg = _queues[game].front();
|
||||
@ -43,7 +43,7 @@ namespace ix
|
||||
return msg;
|
||||
}
|
||||
|
||||
void QueueManager::add(Json::Value msg)
|
||||
void QueueManager::add(const Json::Value& msg, const std::string& position)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_mutex);
|
||||
|
||||
@ -59,7 +59,7 @@ namespace ix
|
||||
// in queuing too many events.
|
||||
if (_queues[game].size() < _maxQueueSize)
|
||||
{
|
||||
_queues[game].push(msg);
|
||||
_queues[game].push(std::make_pair(msg, position));
|
||||
_condition.notify_one();
|
||||
}
|
||||
}
|
||||
|
@ -23,11 +23,11 @@ namespace ix
|
||||
{
|
||||
}
|
||||
|
||||
Json::Value pop();
|
||||
void add(Json::Value msg);
|
||||
std::pair<Json::Value, std::string> pop();
|
||||
void add(const Json::Value& msg, const std::string& position);
|
||||
|
||||
private:
|
||||
std::map<std::string, std::queue<Json::Value>> _queues;
|
||||
std::map<std::string, std::queue<std::pair<Json::Value, std::string>>> _queues;
|
||||
std::mutex _mutex;
|
||||
std::condition_variable _condition;
|
||||
size_t _maxQueueSize;
|
||||
|
@ -43,11 +43,10 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <iostream>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
const uint64_t StatsdClient::_maxQueueSize = 32768;
|
||||
|
||||
StatsdClient::StatsdClient(const std::string& host,
|
||||
int port,
|
||||
const std::string& prefix)
|
||||
@ -56,23 +55,11 @@ namespace ix
|
||||
, _prefix(prefix)
|
||||
, _stop(false)
|
||||
{
|
||||
_thread = std::thread([this] {
|
||||
_thread = std::thread([this]
|
||||
{
|
||||
while (!_stop)
|
||||
{
|
||||
std::deque<std::string> stagedQueue;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
_queue.swap(stagedQueue);
|
||||
}
|
||||
|
||||
while (!stagedQueue.empty())
|
||||
{
|
||||
auto message = stagedQueue.front();
|
||||
_socket.sendto(message);
|
||||
stagedQueue.pop_front();
|
||||
}
|
||||
|
||||
flushQueue();
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
}
|
||||
});
|
||||
@ -127,31 +114,39 @@ namespace ix
|
||||
return send(key, ms, "ms");
|
||||
}
|
||||
|
||||
int StatsdClient::send(std::string key, size_t value, const std::string &type)
|
||||
int StatsdClient::send(std::string key, size_t value, const std::string& type)
|
||||
{
|
||||
cleanup(key);
|
||||
|
||||
char buf[256];
|
||||
snprintf(buf, sizeof(buf), "%s%s:%zd|%s",
|
||||
snprintf(buf, sizeof(buf), "%s%s:%zd|%s\n",
|
||||
_prefix.c_str(), key.c_str(), value, type.c_str());
|
||||
|
||||
return send(buf);
|
||||
enqueue(buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int StatsdClient::send(const std::string &message)
|
||||
void StatsdClient::enqueue(const std::string& message)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
_queue.push_back(message);
|
||||
}
|
||||
|
||||
void StatsdClient::flushQueue()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
|
||||
if (_queue.empty() ||
|
||||
_queue.back().length() > _maxQueueSize)
|
||||
while (!_queue.empty())
|
||||
{
|
||||
_queue.push_back(message);
|
||||
auto message = _queue.front();
|
||||
auto ret = _socket.sendto(message);
|
||||
if (ret != 0)
|
||||
{
|
||||
std::cerr << "error: "
|
||||
<< strerror(UdpSocket::getErrno())
|
||||
<< std::endl;
|
||||
}
|
||||
_queue.pop_front();
|
||||
}
|
||||
else
|
||||
{
|
||||
(*_queue.rbegin()).append("\n").append(message);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // end namespace ix
|
||||
|
@ -32,11 +32,7 @@ namespace ix
|
||||
int timing(const std::string& key, size_t ms);
|
||||
|
||||
private:
|
||||
/**
|
||||
* (Low Level Api) manually send a message
|
||||
* which might be composed of several lines.
|
||||
*/
|
||||
int send(const std::string& message);
|
||||
void enqueue(const std::string& message);
|
||||
|
||||
/* (Low Level Api) manually send a message
|
||||
* type = "c", "g" or "ms"
|
||||
@ -44,6 +40,7 @@ namespace ix
|
||||
int send(std::string key, size_t value, const std::string& type);
|
||||
|
||||
void cleanup(std::string& key);
|
||||
void flushQueue();
|
||||
|
||||
UdpSocket _socket;
|
||||
|
||||
@ -56,7 +53,6 @@ namespace ix
|
||||
std::mutex _mutex; // for the queue
|
||||
|
||||
std::deque<std::string> _queue;
|
||||
static const uint64_t _maxQueueSize;
|
||||
};
|
||||
|
||||
} // end namespace ix
|
||||
|
@ -14,6 +14,7 @@ set (IXCOBRA_HEADERS
|
||||
ixcobra/IXCobraMetricsThreadedPublisher.h
|
||||
ixcobra/IXCobraMetricsPublisher.h
|
||||
ixcobra/IXCobraConfig.h
|
||||
ixcobra/IXCobraEventType.h
|
||||
)
|
||||
|
||||
add_library(ixcobra STATIC
|
||||
|
@ -87,7 +87,7 @@ namespace ix
|
||||
_eventCallback = eventCallback;
|
||||
}
|
||||
|
||||
void CobraConnection::invokeEventCallback(ix::CobraConnectionEventType eventType,
|
||||
void CobraConnection::invokeEventCallback(ix::CobraEventType eventType,
|
||||
const std::string& errorMsg,
|
||||
const WebSocketHttpHeaders& headers,
|
||||
const std::string& subscriptionId,
|
||||
@ -96,7 +96,12 @@ namespace ix
|
||||
std::lock_guard<std::mutex> lock(_eventCallbackMutex);
|
||||
if (_eventCallback)
|
||||
{
|
||||
_eventCallback(eventType, errorMsg, headers, subscriptionId, msgId);
|
||||
_eventCallback(
|
||||
std::make_unique<CobraEvent>(eventType,
|
||||
errorMsg,
|
||||
headers,
|
||||
subscriptionId,
|
||||
msgId));
|
||||
}
|
||||
}
|
||||
|
||||
@ -105,7 +110,7 @@ namespace ix
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << errorMsg << " : received pdu => " << serializedPdu;
|
||||
invokeEventCallback(ix::CobraConnection_EventType_Error, ss.str());
|
||||
invokeEventCallback(ix::CobraEventType::Error, ss.str());
|
||||
}
|
||||
|
||||
void CobraConnection::disconnect()
|
||||
@ -124,7 +129,7 @@ namespace ix
|
||||
std::stringstream ss;
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
invokeEventCallback(ix::CobraConnection_EventType_Open,
|
||||
invokeEventCallback(ix::CobraEventType::Open,
|
||||
std::string(),
|
||||
msg->openInfo.headers);
|
||||
sendHandshakeMessage();
|
||||
@ -136,7 +141,7 @@ namespace ix
|
||||
std::stringstream ss;
|
||||
ss << "Close code " << msg->closeInfo.code;
|
||||
ss << " reason " << msg->closeInfo.reason;
|
||||
invokeEventCallback(ix::CobraConnection_EventType_Closed,
|
||||
invokeEventCallback(ix::CobraEventType::Closed,
|
||||
ss.str());
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||
@ -166,17 +171,19 @@ namespace ix
|
||||
}
|
||||
else if (action == "auth/handshake/error")
|
||||
{
|
||||
invokeErrorCallback("Handshake error", msg->str);
|
||||
invokeEventCallback(ix::CobraEventType::HandshakeError,
|
||||
msg->str);
|
||||
}
|
||||
else if (action == "auth/authenticate/ok")
|
||||
{
|
||||
_authenticated = true;
|
||||
invokeEventCallback(ix::CobraConnection_EventType_Authenticated);
|
||||
invokeEventCallback(ix::CobraEventType::Authenticated);
|
||||
flushQueue();
|
||||
}
|
||||
else if (action == "auth/authenticate/error")
|
||||
{
|
||||
invokeErrorCallback("Authentication error", msg->str);
|
||||
invokeEventCallback(ix::CobraEventType::AuthenticationError,
|
||||
msg->str);
|
||||
}
|
||||
else if (action == "rtm/subscription/data")
|
||||
{
|
||||
@ -191,7 +198,8 @@ namespace ix
|
||||
}
|
||||
else if (action == "rtm/subscribe/error")
|
||||
{
|
||||
invokeErrorCallback("Subscription error", msg->str);
|
||||
invokeEventCallback(ix::CobraEventType::SubscriptionError,
|
||||
msg->str);
|
||||
}
|
||||
else if (action == "rtm/unsubscribe/ok")
|
||||
{
|
||||
@ -231,7 +239,7 @@ namespace ix
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Pong)
|
||||
{
|
||||
invokeEventCallback(ix::CobraConnection_EventType_Pong);
|
||||
invokeEventCallback(ix::CobraEventType::Pong, msg->str);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -393,7 +401,7 @@ namespace ix
|
||||
|
||||
if (!subscriptionId.isString()) return false;
|
||||
|
||||
invokeEventCallback(ix::CobraConnection_EventType_Subscribed,
|
||||
invokeEventCallback(ix::CobraEventType::Subscribed,
|
||||
std::string(), WebSocketHttpHeaders(),
|
||||
subscriptionId.asString());
|
||||
return true;
|
||||
@ -411,7 +419,7 @@ namespace ix
|
||||
|
||||
if (!subscriptionId.isString()) return false;
|
||||
|
||||
invokeEventCallback(ix::CobraConnection_EventType_UnSubscribed,
|
||||
invokeEventCallback(ix::CobraEventType::UnSubscribed,
|
||||
std::string(), WebSocketHttpHeaders(),
|
||||
subscriptionId.asString());
|
||||
return true;
|
||||
@ -459,7 +467,7 @@ namespace ix
|
||||
|
||||
uint64_t msgId = id.asUInt64();
|
||||
|
||||
invokeEventCallback(ix::CobraConnection_EventType_Published,
|
||||
invokeEventCallback(ix::CobraEventType::Published,
|
||||
std::string(), WebSocketHttpHeaders(),
|
||||
std::string(), msgId);
|
||||
|
||||
|
@ -8,6 +8,8 @@
|
||||
|
||||
#include <ixwebsocket/IXWebSocketHttpHeaders.h>
|
||||
#include <ixwebsocket/IXWebSocketPerMessageDeflateOptions.h>
|
||||
#include "IXCobraEventType.h"
|
||||
#include "IXCobraEvent.h"
|
||||
#include <json/json.h>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
@ -28,18 +30,6 @@ namespace ix
|
||||
class WebSocket;
|
||||
struct SocketTLSOptions;
|
||||
|
||||
enum CobraConnectionEventType
|
||||
{
|
||||
CobraConnection_EventType_Authenticated = 0,
|
||||
CobraConnection_EventType_Error = 1,
|
||||
CobraConnection_EventType_Open = 2,
|
||||
CobraConnection_EventType_Closed = 3,
|
||||
CobraConnection_EventType_Subscribed = 4,
|
||||
CobraConnection_EventType_UnSubscribed = 5,
|
||||
CobraConnection_EventType_Published = 6,
|
||||
CobraConnection_EventType_Pong = 7
|
||||
};
|
||||
|
||||
enum CobraConnectionPublishMode
|
||||
{
|
||||
CobraConnection_PublishMode_Immediate = 0,
|
||||
@ -47,11 +37,7 @@ namespace ix
|
||||
};
|
||||
|
||||
using SubscriptionCallback = std::function<void(const Json::Value&, const std::string&)>;
|
||||
using EventCallback = std::function<void(CobraConnectionEventType,
|
||||
const std::string&,
|
||||
const WebSocketHttpHeaders&,
|
||||
const std::string&,
|
||||
uint64_t msgId)>;
|
||||
using EventCallback = std::function<void(const CobraEventPtr&)>;
|
||||
|
||||
using TrafficTrackerCallback = std::function<void(size_t size, bool incoming)>;
|
||||
using PublishTrackerCallback = std::function<void(bool sent, bool acked)>;
|
||||
@ -168,7 +154,7 @@ namespace ix
|
||||
static void invokePublishTrackerCallback(bool sent, bool acked);
|
||||
|
||||
/// Invoke event callbacks
|
||||
void invokeEventCallback(CobraConnectionEventType eventType,
|
||||
void invokeEventCallback(CobraEventType eventType,
|
||||
const std::string& errorMsg = std::string(),
|
||||
const WebSocketHttpHeaders& headers = WebSocketHttpHeaders(),
|
||||
const std::string& subscriptionId = std::string(),
|
||||
|
41
ixcobra/ixcobra/IXCobraEvent.h
Normal file
41
ixcobra/ixcobra/IXCobraEvent.h
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* IXCobraEvent.h
|
||||
* Author: Benjamin Sergeant
|
||||
* Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IXCobraEventType.h"
|
||||
#include <ixwebsocket/IXWebSocketHttpHeaders.h>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
struct CobraEvent
|
||||
{
|
||||
ix::CobraEventType type;
|
||||
const std::string& errMsg;
|
||||
const ix::WebSocketHttpHeaders& headers;
|
||||
const std::string& subscriptionId;
|
||||
uint64_t msgId; // CobraConnection::MsgId
|
||||
|
||||
CobraEvent(ix::CobraEventType t,
|
||||
const std::string& e,
|
||||
const ix::WebSocketHttpHeaders& h,
|
||||
const std::string& s,
|
||||
uint64_t m)
|
||||
: type(t)
|
||||
, errMsg(e)
|
||||
, headers(h)
|
||||
, subscriptionId(s)
|
||||
, msgId(m)
|
||||
{
|
||||
;
|
||||
}
|
||||
};
|
||||
|
||||
using CobraEventPtr = std::unique_ptr<CobraEvent>;
|
||||
}
|
25
ixcobra/ixcobra/IXCobraEventType.h
Normal file
25
ixcobra/ixcobra/IXCobraEventType.h
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* IXCobraEventType.h
|
||||
* Author: Benjamin Sergeant
|
||||
* Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace ix
|
||||
{
|
||||
enum class CobraEventType
|
||||
{
|
||||
Authenticated = 0,
|
||||
Error = 1,
|
||||
Open = 2,
|
||||
Closed = 3,
|
||||
Subscribed = 4,
|
||||
UnSubscribed = 5,
|
||||
Published = 6,
|
||||
Pong = 7,
|
||||
HandshakeError = 8,
|
||||
AuthenticationError = 9,
|
||||
SubscriptionError = 10
|
||||
};
|
||||
}
|
@ -22,53 +22,59 @@ namespace ix
|
||||
CobraMetricsThreadedPublisher::CobraMetricsThreadedPublisher() :
|
||||
_stop(false)
|
||||
{
|
||||
_cobra_connection.setEventCallback(
|
||||
[]
|
||||
(ix::CobraConnectionEventType eventType,
|
||||
const std::string& errMsg,
|
||||
const ix::WebSocketHttpHeaders& headers,
|
||||
const std::string& subscriptionId,
|
||||
CobraConnection::MsgId msgId)
|
||||
_cobra_connection.setEventCallback([](const CobraEventPtr& event)
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
||||
if (eventType == ix::CobraConnection_EventType_Open)
|
||||
if (event->type == ix::CobraEventType::Open)
|
||||
{
|
||||
ss << "Handshake headers" << std::endl;
|
||||
|
||||
for (auto it : headers)
|
||||
for (auto&& it : event->headers)
|
||||
{
|
||||
ss << it.first << ": " << it.second << std::endl;
|
||||
}
|
||||
}
|
||||
else if (eventType == ix::CobraConnection_EventType_Authenticated)
|
||||
else if (event->type == ix::CobraEventType::Authenticated)
|
||||
{
|
||||
ss << "Authenticated";
|
||||
}
|
||||
else if (eventType == ix::CobraConnection_EventType_Error)
|
||||
else if (event->type == ix::CobraEventType::Error)
|
||||
{
|
||||
ss << "Error: " << errMsg;
|
||||
ss << "Error: " << event->errMsg;
|
||||
}
|
||||
else if (eventType == ix::CobraConnection_EventType_Closed)
|
||||
else if (event->type == ix::CobraEventType::Closed)
|
||||
{
|
||||
ss << "Connection closed: " << errMsg;
|
||||
ss << "Connection closed: " << event->errMsg;
|
||||
}
|
||||
else if (eventType == ix::CobraConnection_EventType_Subscribed)
|
||||
else if (event->type == ix::CobraEventType::Subscribed)
|
||||
{
|
||||
ss << "Subscribed through subscription id: " << subscriptionId;
|
||||
ss << "Subscribed through subscription id: " << event->subscriptionId;
|
||||
}
|
||||
else if (eventType == ix::CobraConnection_EventType_UnSubscribed)
|
||||
else if (event->type == ix::CobraEventType::UnSubscribed)
|
||||
{
|
||||
ss << "Unsubscribed through subscription id: " << subscriptionId;
|
||||
ss << "Unsubscribed through subscription id: " << event->subscriptionId;
|
||||
}
|
||||
else if (eventType == ix::CobraConnection_EventType_Published)
|
||||
else if (event->type == ix::CobraEventType::Published)
|
||||
{
|
||||
ss << "Published message " << msgId << " acked";
|
||||
ss << "Published message " << event->msgId << " acked";
|
||||
}
|
||||
else if (eventType == ix::CobraConnection_EventType_Pong)
|
||||
else if (event->type == ix::CobraEventType::Pong)
|
||||
{
|
||||
ss << "Received websocket pong";
|
||||
}
|
||||
else if (event->type == ix::CobraEventType::HandshakeError)
|
||||
{
|
||||
ss << "Handshake error: " << event->errMsg;
|
||||
}
|
||||
else if (event->type == ix::CobraEventType::AuthenticationError)
|
||||
{
|
||||
ss << "Authentication error: " << event->errMsg;
|
||||
}
|
||||
else if (event->type == ix::CobraEventType::SubscriptionError)
|
||||
{
|
||||
ss << "Subscription error: " << event->errMsg;
|
||||
}
|
||||
|
||||
ix::IXCoreLogger::Log(ss.str().c_str());
|
||||
});
|
||||
|
@ -64,7 +64,8 @@ namespace ix
|
||||
std::string SentryClient::computeAuthHeader()
|
||||
{
|
||||
std::string securityHeader("Sentry sentry_version=5");
|
||||
securityHeader += ",sentry_client=ws/1.0.0";
|
||||
securityHeader += ",sentry_client=ws/";
|
||||
securityHeader += std::string(IX_WEBSOCKET_VERSION);
|
||||
securityHeader += ",sentry_timestamp=" + std::to_string(SentryClient::getTimestamp());
|
||||
securityHeader += ",sentry_key=" + _publicKey;
|
||||
securityHeader += ",sentry_secret=" + _secretKey;
|
||||
@ -286,4 +287,24 @@ namespace ix
|
||||
|
||||
_httpClient->performRequest(args, onResponseCallback);
|
||||
}
|
||||
|
||||
void SentryClient::uploadPayload(
|
||||
const Json::Value& payload,
|
||||
bool verbose,
|
||||
const OnResponseCallback& onResponseCallback)
|
||||
{
|
||||
auto args = _httpClient->createRequest();
|
||||
args->extraHeaders["X-Sentry-Auth"] = SentryClient::computeAuthHeader();
|
||||
args->verb = HttpClient::kPost;
|
||||
args->connectTimeout = 60;
|
||||
args->transferTimeout = 5 * 60;
|
||||
args->followRedirects = true;
|
||||
args->verbose = verbose;
|
||||
args->logger = [](const std::string& msg) { ix::IXCoreLogger::Log(msg.c_str()); };
|
||||
|
||||
args->url = _url;
|
||||
args->body = _jsonWriter.write(payload);
|
||||
|
||||
_httpClient->performRequest(args, onResponseCallback);
|
||||
}
|
||||
} // namespace ix
|
||||
|
@ -36,6 +36,11 @@ namespace ix
|
||||
bool verbose,
|
||||
const OnResponseCallback& onResponseCallback);
|
||||
|
||||
void uploadPayload(
|
||||
const Json::Value& payload,
|
||||
bool verbose,
|
||||
const OnResponseCallback& onResponseCallback);
|
||||
|
||||
private:
|
||||
int64_t getTimestamp();
|
||||
std::string computeAuthHeader();
|
||||
|
@ -251,7 +251,22 @@ namespace snake
|
||||
const AppConfig& appConfig,
|
||||
const std::string& str)
|
||||
{
|
||||
auto pdu = nlohmann::json::parse(str);
|
||||
nlohmann::json pdu;
|
||||
try
|
||||
{
|
||||
pdu = nlohmann::json::parse(str);
|
||||
}
|
||||
catch (const nlohmann::json::parse_error& e)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "malformed json pdu: " << e.what() << " -> " << str << "";
|
||||
|
||||
nlohmann::json response = {{"body", {{"error", "invalid_json"},
|
||||
{"reason", ss.str()}}}};
|
||||
ws->sendText(response.dump());
|
||||
return;
|
||||
}
|
||||
|
||||
auto action = pdu["action"];
|
||||
|
||||
if (action == "auth/handshake")
|
||||
|
@ -376,7 +376,8 @@ namespace ix
|
||||
{
|
||||
if (isCancellationRequested && isCancellationRequested())
|
||||
{
|
||||
return std::make_pair(false, std::string());
|
||||
const std::string errorMsg("Cancellation Requested");
|
||||
return std::make_pair(false, errorMsg);
|
||||
}
|
||||
|
||||
size_t size = std::min(kChunkSize, length - output.size());
|
||||
@ -388,7 +389,8 @@ namespace ix
|
||||
}
|
||||
else if (ret <= 0 && !Socket::isWaitNeeded())
|
||||
{
|
||||
return std::make_pair(false, std::string());
|
||||
const std::string errorMsg("Recv Error");
|
||||
return std::make_pair(false, errorMsg);
|
||||
}
|
||||
|
||||
if (onProgressCallback) onProgressCallback((int) output.size(), (int) length);
|
||||
@ -397,7 +399,8 @@ namespace ix
|
||||
// This way we are not busy looping
|
||||
if (isReadyToRead(1) == PollResultType::Error)
|
||||
{
|
||||
return std::make_pair(false, std::string());
|
||||
const std::string errorMsg("Poll Error");
|
||||
return std::make_pair(false, errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,57 @@
|
||||
#endif
|
||||
#define socketerrno errno
|
||||
|
||||
#ifdef _WIN32
|
||||
namespace
|
||||
{
|
||||
bool loadWindowsSystemCertificates(SSL_CTX* ssl, std::string& errorMsg)
|
||||
{
|
||||
DWORD flags = CERT_STORE_READONLY_FLAG | CERT_STORE_OPEN_EXISTING_FLAG |
|
||||
CERT_SYSTEM_STORE_CURRENT_USER;
|
||||
HCERTSTORE systemStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, flags, L"Root");
|
||||
|
||||
if (!systemStore)
|
||||
{
|
||||
errorMsg = "CertOpenStore failed with ";
|
||||
errorMsg += std::to_string(GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
PCCERT_CONTEXT certificateIterator = NULL;
|
||||
X509_STORE* opensslStore = SSL_CTX_get_cert_store(ssl);
|
||||
|
||||
int certificateCount = 0;
|
||||
while (certificateIterator = CertEnumCertificatesInStore(systemStore, certificateIterator))
|
||||
{
|
||||
X509* x509 = d2i_X509(NULL,
|
||||
(const unsigned char**) &certificateIterator->pbCertEncoded,
|
||||
certificateIterator->cbCertEncoded);
|
||||
|
||||
if (x509)
|
||||
{
|
||||
if (X509_STORE_add_cert(opensslStore, x509) == 1)
|
||||
{
|
||||
++certificateCount;
|
||||
}
|
||||
|
||||
X509_free(x509);
|
||||
}
|
||||
}
|
||||
|
||||
CertFreeCertificateContext(certificateIterator);
|
||||
CertCloseStore(systemStore, 0);
|
||||
|
||||
if (certificateCount == 0)
|
||||
{
|
||||
errorMsg = "No certificates found";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace
|
||||
#endif
|
||||
|
||||
namespace ix
|
||||
{
|
||||
const std::string kDefaultCiphers =
|
||||
@ -336,6 +387,12 @@ namespace ix
|
||||
{
|
||||
if (_tlsOptions.isUsingSystemDefaults())
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (!loadWindowsSystemCertificates(_ssl_context, errMsg))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
if (SSL_CTX_set_default_verify_paths(_ssl_context) == 0)
|
||||
{
|
||||
auto sslErr = ERR_get_error();
|
||||
@ -343,6 +400,7 @@ namespace ix
|
||||
errMsg += ERR_error_string(sslErr, nullptr);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else if (SSL_CTX_load_verify_locations(
|
||||
_ssl_context, _tlsOptions.caFile.c_str(), NULL) != 1)
|
||||
|
@ -71,7 +71,7 @@ namespace ix
|
||||
#elif defined(IXWEBSOCKET_USE_OPEN_SSL)
|
||||
ss << " ssl/OpenSSL " << OPENSSL_VERSION_TEXT;
|
||||
#elif __APPLE__
|
||||
ss << " ssl/DarwinSSL";
|
||||
ss << " ssl/SecureTransport";
|
||||
#endif
|
||||
#else
|
||||
ss << " nossl";
|
||||
|
@ -34,7 +34,7 @@ namespace ix
|
||||
_ws.setOnCloseCallback(
|
||||
[this](uint16_t code, const std::string& reason, size_t wireSize, bool remote) {
|
||||
_onMessageCallback(
|
||||
std::make_shared<WebSocketMessage>(WebSocketMessageType::Close,
|
||||
std::make_unique<WebSocketMessage>(WebSocketMessageType::Close,
|
||||
"",
|
||||
wireSize,
|
||||
WebSocketErrorInfo(),
|
||||
@ -193,7 +193,7 @@ namespace ix
|
||||
return status;
|
||||
}
|
||||
|
||||
_onMessageCallback(std::make_shared<WebSocketMessage>(
|
||||
_onMessageCallback(std::make_unique<WebSocketMessage>(
|
||||
WebSocketMessageType::Open,
|
||||
"",
|
||||
0,
|
||||
@ -225,7 +225,7 @@ namespace ix
|
||||
}
|
||||
|
||||
_onMessageCallback(
|
||||
std::make_shared<WebSocketMessage>(WebSocketMessageType::Open,
|
||||
std::make_unique<WebSocketMessage>(WebSocketMessageType::Open,
|
||||
"",
|
||||
0,
|
||||
WebSocketErrorInfo(),
|
||||
@ -310,7 +310,7 @@ namespace ix
|
||||
connectErr.reason = status.errorStr;
|
||||
connectErr.http_status = status.http_status;
|
||||
|
||||
_onMessageCallback(std::make_shared<WebSocketMessage>(WebSocketMessageType::Error,
|
||||
_onMessageCallback(std::make_unique<WebSocketMessage>(WebSocketMessageType::Error,
|
||||
"",
|
||||
0,
|
||||
connectErr,
|
||||
@ -386,7 +386,7 @@ namespace ix
|
||||
|
||||
bool binary = messageKind == WebSocketTransport::MessageKind::MSG_BINARY;
|
||||
|
||||
_onMessageCallback(std::make_shared<WebSocketMessage>(webSocketMessageType,
|
||||
_onMessageCallback(std::make_unique<WebSocketMessage>(webSocketMessageType,
|
||||
msg,
|
||||
wireSize,
|
||||
webSocketErrorInfo,
|
||||
|
@ -6,6 +6,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
struct WebSocketCloseInfo
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace ix
|
||||
|
@ -19,7 +19,7 @@ namespace ix
|
||||
struct WebSocketMessage
|
||||
{
|
||||
WebSocketMessageType type;
|
||||
std::string str;
|
||||
const std::string& str;
|
||||
size_t wireSize;
|
||||
WebSocketErrorInfo errorInfo;
|
||||
WebSocketOpenInfo openInfo;
|
||||
@ -34,7 +34,7 @@ namespace ix
|
||||
WebSocketCloseInfo c,
|
||||
bool b = false)
|
||||
: type(t)
|
||||
, str(std::move(s))
|
||||
, str(s)
|
||||
, wireSize(w)
|
||||
, errorInfo(e)
|
||||
, openInfo(o)
|
||||
@ -45,5 +45,5 @@ namespace ix
|
||||
}
|
||||
};
|
||||
|
||||
using WebSocketMessagePtr = std::shared_ptr<WebSocketMessage>;
|
||||
using WebSocketMessagePtr = std::unique_ptr<WebSocketMessage>;
|
||||
} // namespace ix
|
||||
|
@ -1,86 +0,0 @@
|
||||
/*
|
||||
* IXWebSocketMessageQueue.cpp
|
||||
* Author: Korchynskyi Dmytro
|
||||
* Copyright (c) 2017-2019 Machine Zone, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "IXWebSocketMessageQueue.h"
|
||||
|
||||
namespace ix
|
||||
{
|
||||
WebSocketMessageQueue::WebSocketMessageQueue(WebSocket* websocket)
|
||||
{
|
||||
bindWebsocket(websocket);
|
||||
}
|
||||
|
||||
WebSocketMessageQueue::~WebSocketMessageQueue()
|
||||
{
|
||||
if (!_messages.empty())
|
||||
{
|
||||
// not handled all messages
|
||||
}
|
||||
|
||||
bindWebsocket(nullptr);
|
||||
}
|
||||
|
||||
void WebSocketMessageQueue::bindWebsocket(WebSocket* websocket)
|
||||
{
|
||||
if (_websocket == websocket) return;
|
||||
|
||||
// unbind old
|
||||
if (_websocket)
|
||||
{
|
||||
// set dummy callback just to avoid crash
|
||||
_websocket->setOnMessageCallback([](const WebSocketMessagePtr&) {});
|
||||
}
|
||||
|
||||
_websocket = websocket;
|
||||
|
||||
// bind new
|
||||
if (_websocket)
|
||||
{
|
||||
_websocket->setOnMessageCallback([this](const WebSocketMessagePtr& msg) {
|
||||
std::lock_guard<std::mutex> lock(_messagesMutex);
|
||||
_messages.emplace_back(std::move(msg));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void WebSocketMessageQueue::setOnMessageCallback(const OnMessageCallback& callback)
|
||||
{
|
||||
_onMessageUserCallback = callback;
|
||||
}
|
||||
|
||||
void WebSocketMessageQueue::setOnMessageCallback(OnMessageCallback&& callback)
|
||||
{
|
||||
_onMessageUserCallback = std::move(callback);
|
||||
}
|
||||
|
||||
WebSocketMessagePtr WebSocketMessageQueue::popMessage()
|
||||
{
|
||||
WebSocketMessagePtr message;
|
||||
std::lock_guard<std::mutex> lock(_messagesMutex);
|
||||
|
||||
if (!_messages.empty())
|
||||
{
|
||||
message = std::move(_messages.front());
|
||||
_messages.pop_front();
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
void WebSocketMessageQueue::poll(int count)
|
||||
{
|
||||
if (!_onMessageUserCallback) return;
|
||||
|
||||
WebSocketMessagePtr message;
|
||||
|
||||
while (count > 0 && (message = popMessage()))
|
||||
{
|
||||
_onMessageUserCallback(message);
|
||||
--count;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ix
|
@ -1,41 +0,0 @@
|
||||
/*
|
||||
* IXWebSocketMessageQueue.h
|
||||
* Author: Korchynskyi Dmytro
|
||||
* Copyright (c) 2017-2019 Machine Zone, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IXWebSocket.h"
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
//
|
||||
// A helper class to dispatch websocket message callbacks in your thread.
|
||||
//
|
||||
class WebSocketMessageQueue
|
||||
{
|
||||
public:
|
||||
WebSocketMessageQueue(WebSocket* websocket = nullptr);
|
||||
~WebSocketMessageQueue();
|
||||
|
||||
void bindWebsocket(WebSocket* websocket);
|
||||
|
||||
void setOnMessageCallback(const OnMessageCallback& callback);
|
||||
void setOnMessageCallback(OnMessageCallback&& callback);
|
||||
|
||||
void poll(int count = 512);
|
||||
|
||||
protected:
|
||||
WebSocketMessagePtr popMessage();
|
||||
|
||||
private:
|
||||
WebSocket* _websocket = nullptr;
|
||||
OnMessageCallback _onMessageUserCallback;
|
||||
std::mutex _messagesMutex;
|
||||
std::list<WebSocketMessagePtr> _messages;
|
||||
};
|
||||
} // namespace ix
|
@ -6,6 +6,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IXWebSocketHttpHeaders.h"
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
struct WebSocketOpenInfo
|
||||
|
@ -87,6 +87,9 @@ namespace ix
|
||||
//
|
||||
size_t output;
|
||||
|
||||
// Clear output
|
||||
out.clear();
|
||||
|
||||
if (in.empty())
|
||||
{
|
||||
// See issue #167
|
||||
@ -174,6 +177,9 @@ namespace ix
|
||||
_inflateState.avail_in = (uInt) inFixed.size();
|
||||
_inflateState.next_in = (unsigned char*) (const_cast<char*>(inFixed.data()));
|
||||
|
||||
// Clear output
|
||||
out.clear();
|
||||
|
||||
do
|
||||
{
|
||||
_inflateState.avail_out = (uInt) _compressBufferSize;
|
||||
|
@ -62,7 +62,7 @@ namespace ix
|
||||
WebSocketTransport::WebSocketTransport()
|
||||
: _useMask(true)
|
||||
, _blockingSend(false)
|
||||
, _compressedMessage(false)
|
||||
, _receivedMessageCompressed(false)
|
||||
, _readyState(ReadyState::CLOSED)
|
||||
, _closeCode(WebSocketCloseConstants::kInternalErrorCode)
|
||||
, _closeReason(WebSocketCloseConstants::kInternalErrorMessage)
|
||||
@ -74,6 +74,7 @@ namespace ix
|
||||
, _enablePong(kDefaultEnablePong)
|
||||
, _pingIntervalSecs(kDefaultPingIntervalSecs)
|
||||
, _pongReceived(false)
|
||||
, _pingCount(0)
|
||||
, _lastSendPingTimePoint(std::chrono::steady_clock::now())
|
||||
{
|
||||
_readbuf.resize(kChunkSize);
|
||||
@ -221,7 +222,8 @@ namespace ix
|
||||
{
|
||||
_pongReceived = false;
|
||||
std::stringstream ss;
|
||||
ss << kPingMessage << "::" << _pingIntervalSecs << "s";
|
||||
ss << kPingMessage << "::" << _pingIntervalSecs << "s"
|
||||
<< "::" << _pingCount++;
|
||||
return sendPing(ss.str());
|
||||
}
|
||||
|
||||
@ -493,7 +495,7 @@ namespace ix
|
||||
? MessageKind::MSG_TEXT
|
||||
: MessageKind::MSG_BINARY;
|
||||
|
||||
_compressedMessage = _enablePerMessageDeflate && ws.rsv1;
|
||||
_receivedMessageCompressed = _enablePerMessageDeflate && ws.rsv1;
|
||||
|
||||
// Continuation message needs to follow a non-fin TEXT or BINARY message
|
||||
if (!_chunks.empty())
|
||||
@ -515,10 +517,12 @@ namespace ix
|
||||
//
|
||||
if (ws.fin && _chunks.empty())
|
||||
{
|
||||
emitMessage(
|
||||
_fragmentedMessageKind, frameData, _compressedMessage, onMessageCallback);
|
||||
emitMessage(_fragmentedMessageKind,
|
||||
frameData,
|
||||
_receivedMessageCompressed,
|
||||
onMessageCallback);
|
||||
|
||||
_compressedMessage = false;
|
||||
_receivedMessageCompressed = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -535,11 +539,11 @@ namespace ix
|
||||
{
|
||||
emitMessage(_fragmentedMessageKind,
|
||||
getMergedChunks(),
|
||||
_compressedMessage,
|
||||
_receivedMessageCompressed,
|
||||
onMessageCallback);
|
||||
|
||||
_chunks.clear();
|
||||
_compressedMessage = false;
|
||||
_receivedMessageCompressed = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -625,7 +629,7 @@ namespace ix
|
||||
// send back the CLOSE frame
|
||||
sendCloseFrame(code, reason);
|
||||
|
||||
_socket->wakeUpFromPoll(Socket::kCloseRequest);
|
||||
wakeUpFromPoll(Socket::kCloseRequest);
|
||||
|
||||
bool remote = true;
|
||||
closeSocketAndSwitchToClosedState(code, reason, _rxbuf.size(), remote);
|
||||
@ -712,17 +716,16 @@ namespace ix
|
||||
// When the RSV1 bit is 1 it means the message is compressed
|
||||
if (compressedMessage && messageKind != MessageKind::FRAGMENT)
|
||||
{
|
||||
std::string decompressedMessage;
|
||||
bool success = _perMessageDeflate->decompress(message, decompressedMessage);
|
||||
bool success = _perMessageDeflate->decompress(message, _decompressedMessage);
|
||||
|
||||
if (messageKind == MessageKind::MSG_TEXT && !validateUtf8(decompressedMessage))
|
||||
if (messageKind == MessageKind::MSG_TEXT && !validateUtf8(_decompressedMessage))
|
||||
{
|
||||
close(WebSocketCloseConstants::kInvalidFramePayloadData,
|
||||
WebSocketCloseConstants::kInvalidFramePayloadDataMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
onMessageCallback(decompressedMessage, wireSize, !success, messageKind);
|
||||
onMessageCallback(_decompressedMessage, wireSize, !success, messageKind);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -759,7 +762,6 @@ namespace ix
|
||||
|
||||
size_t payloadSize = message.size();
|
||||
size_t wireSize = message.size();
|
||||
std::string compressedMessage;
|
||||
bool compressionError = false;
|
||||
|
||||
std::string::const_iterator message_begin = message.begin();
|
||||
@ -767,7 +769,7 @@ namespace ix
|
||||
|
||||
if (compress)
|
||||
{
|
||||
if (!_perMessageDeflate->compress(message, compressedMessage))
|
||||
if (!_perMessageDeflate->compress(message, _compressedMessage))
|
||||
{
|
||||
bool success = false;
|
||||
compressionError = true;
|
||||
@ -776,10 +778,10 @@ namespace ix
|
||||
return WebSocketSendInfo(success, compressionError, payloadSize, wireSize);
|
||||
}
|
||||
compressionError = false;
|
||||
wireSize = compressedMessage.size();
|
||||
wireSize = _compressedMessage.size();
|
||||
|
||||
message_begin = compressedMessage.begin();
|
||||
message_end = compressedMessage.end();
|
||||
message_begin = _compressedMessage.begin();
|
||||
message_end = _compressedMessage.end();
|
||||
}
|
||||
|
||||
{
|
||||
@ -845,7 +847,7 @@ namespace ix
|
||||
// Request to flush the send buffer on the background thread if it isn't empty
|
||||
if (!isSendBufferEmpty())
|
||||
{
|
||||
_socket->wakeUpFromPoll(Socket::kSendRequest);
|
||||
wakeUpFromPoll(Socket::kSendRequest);
|
||||
|
||||
// FIXME: we should have a timeout when sending large messages: see #131
|
||||
if (_blockingSend && !flushSendBuffer())
|
||||
@ -1063,6 +1065,12 @@ namespace ix
|
||||
_socket->close();
|
||||
}
|
||||
|
||||
bool WebSocketTransport::wakeUpFromPoll(uint64_t wakeUpCode)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_socketMutex);
|
||||
return _socket->wakeUpFromPoll(wakeUpCode);
|
||||
}
|
||||
|
||||
void WebSocketTransport::closeSocketAndSwitchToClosedState(uint16_t code,
|
||||
const std::string& reason,
|
||||
size_t closeWireSize,
|
||||
@ -1110,8 +1118,9 @@ namespace ix
|
||||
setReadyState(ReadyState::CLOSING);
|
||||
|
||||
sendCloseFrame(code, reason);
|
||||
|
||||
// wake up the poll, but do not close yet
|
||||
_socket->wakeUpFromPoll(Socket::kSendRequest);
|
||||
wakeUpFromPoll(Socket::kSendRequest);
|
||||
}
|
||||
|
||||
size_t WebSocketTransport::bufferedAmount() const
|
||||
|
@ -165,7 +165,7 @@ namespace ix
|
||||
MessageKind _fragmentedMessageKind;
|
||||
|
||||
// Ditto for whether a message is compressed
|
||||
bool _compressedMessage;
|
||||
bool _receivedMessageCompressed;
|
||||
|
||||
// Fragments are 32K long
|
||||
static constexpr size_t kChunkSize = 1 << 15;
|
||||
@ -189,6 +189,9 @@ namespace ix
|
||||
WebSocketPerMessageDeflateOptions _perMessageDeflateOptions;
|
||||
std::atomic<bool> _enablePerMessageDeflate;
|
||||
|
||||
std::string _decompressedMessage;
|
||||
std::string _compressedMessage;
|
||||
|
||||
// Used to control TLS connection behavior
|
||||
SocketTLSOptions _socketTLSOptions;
|
||||
|
||||
@ -209,6 +212,7 @@ namespace ix
|
||||
|
||||
static const int kDefaultPingIntervalSecs;
|
||||
static const std::string kPingMessage;
|
||||
std::atomic<uint64_t> _pingCount;
|
||||
|
||||
// We record when ping are being sent so that we can know when to send the next one
|
||||
mutable std::mutex _lastSendPingTimePointMutex;
|
||||
@ -229,6 +233,8 @@ namespace ix
|
||||
size_t closeWireSize,
|
||||
bool remote);
|
||||
|
||||
bool wakeUpFromPoll(uint64_t wakeUpCode);
|
||||
|
||||
bool flushSendBuffer();
|
||||
bool sendOnSocket();
|
||||
bool receiveFromSocket();
|
||||
|
@ -6,4 +6,4 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#define IX_WEBSOCKET_VERSION "9.0.3"
|
||||
#define IX_WEBSOCKET_VERSION "9.3.3"
|
||||
|
57
makefile
57
makefile
@ -1,5 +1,13 @@
|
||||
#
|
||||
# This makefile is just used to easily work with docker (linux build)
|
||||
# This makefile is used for convenience, and wrap simple cmake commands
|
||||
# You don't need to use it as an end user, it is more for developer.
|
||||
#
|
||||
# * work with docker (linux build)
|
||||
# * execute the unittest
|
||||
#
|
||||
# The default target will install ws, the command line tool coming with
|
||||
# IXWebSocket into /usr/local/bin
|
||||
#
|
||||
#
|
||||
all: brew
|
||||
|
||||
@ -8,14 +16,23 @@ install: brew
|
||||
# Use -DCMAKE_INSTALL_PREFIX= to install into another location
|
||||
# on osx it is good practice to make /usr/local user writable
|
||||
# sudo chown -R `whoami`/staff /usr/local
|
||||
#
|
||||
# Release, Debug, MinSizeRel, RelWithDebInfo are the build types
|
||||
#
|
||||
brew:
|
||||
mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_TEST=1 .. ; make -j 4 install)
|
||||
|
||||
# Docker default target. We've add problem with OpenSSL and TLS 1.3 (on the
|
||||
# server side ?) and I can't work-around it easily, so we're using mbedtls on
|
||||
# Linux for the SSL backend, which works great.
|
||||
ws_mbedtls_install:
|
||||
mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=MinSizeRel -DUSE_TLS=1 -DUSE_WS=1 -DUSE_MBED_TLS=1 .. ; make -j 4 install)
|
||||
|
||||
ws:
|
||||
mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 .. ; make -j 4)
|
||||
|
||||
ws_install:
|
||||
mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 .. ; make -j 4 install)
|
||||
mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=MinSizeRel -DUSE_TLS=1 -DUSE_WS=1 .. ; make -j 4 install)
|
||||
|
||||
ws_openssl:
|
||||
mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_OPEN_SSL=1 .. ; make -j 4)
|
||||
@ -23,9 +40,6 @@ ws_openssl:
|
||||
ws_mbedtls:
|
||||
mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_MBED_TLS=1 .. ; make -j 4)
|
||||
|
||||
ws_mbedtls_install:
|
||||
mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_MBED_TLS=1 .. ; make -j 4 install)
|
||||
|
||||
ws_no_ssl:
|
||||
mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_WS=1 .. ; make -j 4)
|
||||
|
||||
@ -68,6 +82,8 @@ docker_push:
|
||||
docker push ${LATEST}
|
||||
docker push ${IMG}
|
||||
|
||||
deploy: docker docker_push
|
||||
|
||||
run:
|
||||
docker run --cap-add sys_ptrace --entrypoint=sh -it bsergean/ws:build
|
||||
|
||||
@ -100,22 +116,43 @@ test_ubsan:
|
||||
(cd build/test ; ln -sf Debug/ixwebsocket_unittest)
|
||||
(cd test ; python2.7 run.py -r)
|
||||
|
||||
test_asan:
|
||||
test_asan: build_test_asan
|
||||
(cd test ; python2.7 run.py -r)
|
||||
|
||||
build_test_asan:
|
||||
mkdir -p build && (cd build && cmake -GXcode -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_TEST=1 .. && xcodebuild -project ixwebsocket.xcodeproj -target ixwebsocket_unittest -enableAddressSanitizer YES)
|
||||
(cd build/test ; ln -sf Debug/ixwebsocket_unittest)
|
||||
(cd test ; python2.7 run.py -r)
|
||||
|
||||
test_tsan_openssl:
|
||||
mkdir -p build && (cd build && cmake -GXcode -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_TEST=1 -DUSE_OPEN_SSL=1 .. && xcodebuild -project ixwebsocket.xcodeproj -target ixwebsocket_unittest -enableThreadSanitizer YES)
|
||||
(cd build/test ; ln -sf Debug/ixwebsocket_unittest)
|
||||
(cd test ; python2.7 run.py -r)
|
||||
|
||||
test_openssl:
|
||||
mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_OPEN_SSL=1 -DUSE_TEST=1 .. ; make -j 4)
|
||||
test_ubsan_openssl:
|
||||
mkdir -p build && (cd build && cmake -GXcode -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_TEST=1 -DUSE_OPEN_SSL=1 .. && xcodebuild -project ixwebsocket.xcodeproj -target ixwebsocket_unittest -enableUndefinedBehaviorSanitizer YES)
|
||||
(cd build/test ; ln -sf Debug/ixwebsocket_unittest)
|
||||
(cd test ; python2.7 run.py -r)
|
||||
|
||||
test_mbedtls:
|
||||
test_tsan_openssl_release:
|
||||
mkdir -p build && (cd build && cmake -GXcode -DCMAKE_BUILD_TYPE=Release -DUSE_TLS=1 -DUSE_TEST=1 -DUSE_OPEN_SSL=1 .. && xcodebuild -project ixwebsocket.xcodeproj -configuration Release -target ixwebsocket_unittest -enableThreadSanitizer YES)
|
||||
(cd build/test ; ln -sf Release/ixwebsocket_unittest)
|
||||
(cd test ; python2.7 run.py -r)
|
||||
|
||||
test_tsan_mbedtls:
|
||||
mkdir -p build && (cd build && cmake -GXcode -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_TEST=1 -DUSE_MBED_TLS=1 .. && xcodebuild -project ixwebsocket.xcodeproj -target ixwebsocket_unittest -enableThreadSanitizer YES)
|
||||
(cd build/test ; ln -sf Debug/ixwebsocket_unittest)
|
||||
(cd test ; python2.7 run.py -r)
|
||||
|
||||
build_test_openssl:
|
||||
mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_OPEN_SSL=1 -DUSE_TEST=1 .. ; make -j 4)
|
||||
|
||||
test_openssl: build_test_openssl
|
||||
(cd test ; python2.7 run.py -r)
|
||||
|
||||
build_test_mbedtls:
|
||||
mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_MBED_TLS=1 -DUSE_TEST=1 .. ; make -j 4)
|
||||
|
||||
test_mbedtls: build_test_mbedtls
|
||||
(cd test ; python2.7 run.py -r)
|
||||
|
||||
test_no_ssl:
|
||||
|
@ -54,6 +54,7 @@ set (SOURCES
|
||||
IXWebSocketSubProtocolTest.cpp
|
||||
IXSentryClientTest.cpp
|
||||
IXWebSocketChatTest.cpp
|
||||
IXWebSocketBroadcastTest.cpp
|
||||
)
|
||||
|
||||
# Some unittest don't work on windows yet
|
||||
@ -65,13 +66,7 @@ if (UNIX)
|
||||
IXCobraMetricsPublisherTest.cpp
|
||||
IXCobraToSentryBotTest.cpp
|
||||
IXCobraToStatsdBotTest.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
# Some unittest fail for dubious reason on Ubuntu Xenial with TSAN
|
||||
if (MAC OR WIN32)
|
||||
list(APPEND SOURCES
|
||||
IXWebSocketMessageQTest.cpp
|
||||
IXCobraToStdoutBotTest.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
|
@ -180,44 +180,40 @@ namespace
|
||||
_conn.configure(_cobraConfig);
|
||||
_conn.connect();
|
||||
|
||||
_conn.setEventCallback([this, channel](ix::CobraConnectionEventType eventType,
|
||||
const std::string& errMsg,
|
||||
const ix::WebSocketHttpHeaders& headers,
|
||||
const std::string& subscriptionId,
|
||||
CobraConnection::MsgId msgId) {
|
||||
if (eventType == ix::CobraConnection_EventType_Open)
|
||||
_conn.setEventCallback([this, channel](const CobraEventPtr& event) {
|
||||
if (event->type == ix::CobraEventType::Open)
|
||||
{
|
||||
log("Subscriber connected: " + _user);
|
||||
for (auto&& it : headers)
|
||||
for (auto&& it : event->headers)
|
||||
{
|
||||
log("Headers " + it.first + " " + it.second);
|
||||
}
|
||||
}
|
||||
else if (eventType == ix::CobraConnection_EventType_Authenticated)
|
||||
else if (event->type == ix::CobraEventType::Authenticated)
|
||||
{
|
||||
log("Subscriber authenticated: " + _user);
|
||||
subscribe(channel);
|
||||
}
|
||||
else if (eventType == ix::CobraConnection_EventType_Error)
|
||||
else if (event->type == ix::CobraEventType::Error)
|
||||
{
|
||||
log(errMsg + _user);
|
||||
log(event->errMsg + _user);
|
||||
}
|
||||
else if (eventType == ix::CobraConnection_EventType_Closed)
|
||||
else if (event->type == ix::CobraEventType::Closed)
|
||||
{
|
||||
log("Connection closed: " + _user);
|
||||
}
|
||||
else if (eventType == ix::CobraConnection_EventType_Subscribed)
|
||||
else if (event->type == ix::CobraEventType::Subscribed)
|
||||
{
|
||||
log("Subscription ok: " + _user + " subscription_id " + subscriptionId);
|
||||
log("Subscription ok: " + _user + " subscription_id " + event->subscriptionId);
|
||||
_connectedAndSubscribed = true;
|
||||
}
|
||||
else if (eventType == ix::CobraConnection_EventType_UnSubscribed)
|
||||
else if (event->type == ix::CobraEventType::UnSubscribed)
|
||||
{
|
||||
log("Unsubscription ok: " + _user + " subscription_id " + subscriptionId);
|
||||
log("Unsubscription ok: " + _user + " subscription_id " + event->subscriptionId);
|
||||
}
|
||||
else if (eventType == ix::CobraConnection_EventType_Published)
|
||||
else if (event->type == ix::CobraEventType::Published)
|
||||
{
|
||||
TLogger() << "Subscriber: published message acked: " << msgId;
|
||||
TLogger() << "Subscriber: published message acked: " << event->msgId;
|
||||
}
|
||||
});
|
||||
|
||||
@ -248,11 +244,7 @@ namespace
|
||||
ix::msleep(50);
|
||||
_conn.disconnect();
|
||||
|
||||
_conn.setEventCallback([](ix::CobraConnectionEventType /*eventType*/,
|
||||
const std::string& /*errMsg*/,
|
||||
const ix::WebSocketHttpHeaders& /*headers*/,
|
||||
const std::string& /*subscriptionId*/,
|
||||
CobraConnection::MsgId /*msgId*/) { ; });
|
||||
_conn.setEventCallback([](const CobraEventPtr& /*event*/) {});
|
||||
}
|
||||
} // namespace
|
||||
|
||||
|
@ -54,24 +54,24 @@ namespace
|
||||
conn.configure(config);
|
||||
conn.connect();
|
||||
|
||||
conn.setEventCallback([&conn, &channel](ix::CobraConnectionEventType eventType,
|
||||
const std::string& errMsg,
|
||||
const ix::WebSocketHttpHeaders& headers,
|
||||
const std::string& subscriptionId,
|
||||
CobraConnection::MsgId msgId) {
|
||||
if (eventType == ix::CobraConnection_EventType_Open)
|
||||
conn.setEventCallback([&conn, &channel](const CobraEventPtr& event) {
|
||||
if (event->type == ix::CobraEventType::Open)
|
||||
{
|
||||
TLogger() << "Subscriber connected:";
|
||||
for (auto&& it : headers)
|
||||
for (auto&& it : event->headers)
|
||||
{
|
||||
log("Headers " + it.first + " " + it.second);
|
||||
}
|
||||
}
|
||||
if (eventType == ix::CobraConnection_EventType_Error)
|
||||
else if (event->type == ix::CobraEventType::Closed)
|
||||
{
|
||||
TLogger() << "Subscriber error:" << errMsg;
|
||||
TLogger() << "Subscriber closed:" << event->errMsg;
|
||||
}
|
||||
else if (eventType == ix::CobraConnection_EventType_Authenticated)
|
||||
else if (event->type == ix::CobraEventType::Error)
|
||||
{
|
||||
TLogger() << "Subscriber error:" << event->errMsg;
|
||||
}
|
||||
else if (event->type == ix::CobraEventType::Authenticated)
|
||||
{
|
||||
log("Subscriber authenticated");
|
||||
std::string filter;
|
||||
@ -92,29 +92,29 @@ namespace
|
||||
gMessageCount++;
|
||||
});
|
||||
}
|
||||
else if (eventType == ix::CobraConnection_EventType_Subscribed)
|
||||
else if (event->type == ix::CobraEventType::Subscribed)
|
||||
{
|
||||
TLogger() << "Subscriber: subscribed to channel " << subscriptionId;
|
||||
if (subscriptionId == channel)
|
||||
TLogger() << "Subscriber: subscribed to channel " << event->subscriptionId;
|
||||
if (event->subscriptionId == channel)
|
||||
{
|
||||
gSubscriberConnectedAndSubscribed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
TLogger() << "Subscriber: unexpected channel " << subscriptionId;
|
||||
TLogger() << "Subscriber: unexpected channel " << event->subscriptionId;
|
||||
}
|
||||
}
|
||||
else if (eventType == ix::CobraConnection_EventType_UnSubscribed)
|
||||
else if (event->type == ix::CobraEventType::UnSubscribed)
|
||||
{
|
||||
TLogger() << "Subscriber: ununexpected from channel " << subscriptionId;
|
||||
if (subscriptionId != channel)
|
||||
TLogger() << "Subscriber: ununexpected from channel " << event->subscriptionId;
|
||||
if (event->subscriptionId != channel)
|
||||
{
|
||||
TLogger() << "Subscriber: unexpected channel " << subscriptionId;
|
||||
TLogger() << "Subscriber: unexpected channel " << event->subscriptionId;
|
||||
}
|
||||
}
|
||||
else if (eventType == ix::CobraConnection_EventType_Published)
|
||||
else if (event->type == ix::CobraEventType::Published)
|
||||
{
|
||||
TLogger() << "Subscriber: published message acked: " << msgId;
|
||||
TLogger() << "Subscriber: published message acked: " << event->msgId;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -141,7 +141,6 @@ TEST_CASE("Cobra_to_sentry_bot", "[cobra_bots]")
|
||||
std::string filter;
|
||||
std::string position("$");
|
||||
bool verbose = true;
|
||||
bool strict = true;
|
||||
size_t maxQueueSize = 10;
|
||||
bool enableHeartbeat = false;
|
||||
|
||||
@ -161,16 +160,15 @@ TEST_CASE("Cobra_to_sentry_bot", "[cobra_bots]")
|
||||
// Only run the bot for 3 seconds
|
||||
int runtime = 3;
|
||||
|
||||
int sentCount = cobra_to_sentry_bot(config,
|
||||
channel,
|
||||
filter,
|
||||
position,
|
||||
sentryClient,
|
||||
verbose,
|
||||
strict,
|
||||
maxQueueSize,
|
||||
enableHeartbeat,
|
||||
runtime);
|
||||
int64_t sentCount = cobra_to_sentry_bot(config,
|
||||
channel,
|
||||
filter,
|
||||
position,
|
||||
sentryClient,
|
||||
verbose,
|
||||
maxQueueSize,
|
||||
enableHeartbeat,
|
||||
runtime);
|
||||
//
|
||||
// We want at least 2 messages to be sent
|
||||
//
|
||||
|
@ -111,17 +111,21 @@ TEST_CASE("Cobra_to_statsd_bot", "[cobra_bots]")
|
||||
REQUIRE(initialized);
|
||||
|
||||
std::string fields("device.game\ndevice.os_name");
|
||||
std::string gauge;
|
||||
std::string timer;
|
||||
|
||||
int sentCount = ix::cobra_to_statsd_bot(config,
|
||||
channel,
|
||||
filter,
|
||||
position,
|
||||
statsdClient,
|
||||
fields,
|
||||
verbose,
|
||||
maxQueueSize,
|
||||
enableHeartbeat,
|
||||
runtime);
|
||||
int64_t sentCount = ix::cobra_to_statsd_bot(config,
|
||||
channel,
|
||||
filter,
|
||||
position,
|
||||
statsdClient,
|
||||
fields,
|
||||
gauge,
|
||||
timer,
|
||||
verbose,
|
||||
maxQueueSize,
|
||||
enableHeartbeat,
|
||||
runtime);
|
||||
//
|
||||
// We want at least 2 messages to be sent
|
||||
//
|
||||
|
127
test/IXCobraToStdoutBotTest.cpp
Normal file
127
test/IXCobraToStdoutBotTest.cpp
Normal file
@ -0,0 +1,127 @@
|
||||
/*
|
||||
* IXCobraToStdoutTest.cpp
|
||||
* Author: Benjamin Sergeant
|
||||
* Copyright (c) 2020 Machine Zone. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "IXTest.h"
|
||||
#include "catch.hpp"
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <ixbots/IXCobraToStdoutBot.h>
|
||||
#include <ixcobra/IXCobraConnection.h>
|
||||
#include <ixcobra/IXCobraMetricsPublisher.h>
|
||||
#include <ixcrypto/IXUuid.h>
|
||||
#include <ixsentry/IXSentryClient.h>
|
||||
#include <ixsnake/IXRedisServer.h>
|
||||
#include <ixsnake/IXSnakeServer.h>
|
||||
#include <ixwebsocket/IXHttpServer.h>
|
||||
#include <ixwebsocket/IXUserAgent.h>
|
||||
|
||||
using namespace ix;
|
||||
|
||||
namespace
|
||||
{
|
||||
void runPublisher(const ix::CobraConfig& config, const std::string& channel)
|
||||
{
|
||||
ix::CobraMetricsPublisher cobraMetricsPublisher;
|
||||
cobraMetricsPublisher.configure(config, channel);
|
||||
cobraMetricsPublisher.setSession(uuid4());
|
||||
cobraMetricsPublisher.enable(true);
|
||||
|
||||
Json::Value msg;
|
||||
msg["fps"] = 60;
|
||||
|
||||
cobraMetricsPublisher.setGenericAttributes("game", "ody");
|
||||
|
||||
// Wait a bit
|
||||
ix::msleep(500);
|
||||
|
||||
// publish some messages
|
||||
cobraMetricsPublisher.push("sms_metric_A_id", msg); // (msg #1)
|
||||
cobraMetricsPublisher.push("sms_metric_B_id", msg); // (msg #2)
|
||||
ix::msleep(500);
|
||||
|
||||
cobraMetricsPublisher.push("sms_metric_A_id", msg); // (msg #3)
|
||||
cobraMetricsPublisher.push("sms_metric_D_id", msg); // (msg #4)
|
||||
ix::msleep(500);
|
||||
|
||||
cobraMetricsPublisher.push("sms_metric_A_id", msg); // (msg #4)
|
||||
cobraMetricsPublisher.push("sms_metric_F_id", msg); // (msg #5)
|
||||
ix::msleep(500);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("Cobra_to_stdout_bot", "[cobra_bots]")
|
||||
{
|
||||
SECTION("Exchange and count sent/received messages.")
|
||||
{
|
||||
int port = getFreePort();
|
||||
snake::AppConfig appConfig = makeSnakeServerConfig(port, true);
|
||||
|
||||
// Start a redis server
|
||||
ix::RedisServer redisServer(appConfig.redisPort);
|
||||
auto res = redisServer.listen();
|
||||
REQUIRE(res.first);
|
||||
redisServer.start();
|
||||
|
||||
// Start a snake server
|
||||
snake::SnakeServer snakeServer(appConfig);
|
||||
snakeServer.run();
|
||||
|
||||
// Run the bot for a small amount of time
|
||||
std::string channel = ix::generateSessionId();
|
||||
std::string appkey("FC2F10139A2BAc53BB72D9db967b024f");
|
||||
std::string role = "_sub";
|
||||
std::string secret = "66B1dA3ED5fA074EB5AE84Dd8CE3b5ba";
|
||||
std::string endpoint = makeCobraEndpoint(port, true);
|
||||
|
||||
ix::CobraConfig config;
|
||||
config.endpoint = endpoint;
|
||||
config.appkey = appkey;
|
||||
config.rolename = role;
|
||||
config.rolesecret = secret;
|
||||
config.socketTLSOptions = makeClientTLSOptions();
|
||||
|
||||
std::thread publisherThread(runPublisher, config, channel);
|
||||
|
||||
std::string filter;
|
||||
std::string position("$");
|
||||
bool verbose = true;
|
||||
bool quiet = false;
|
||||
size_t maxQueueSize = 10;
|
||||
bool enableHeartbeat = false;
|
||||
|
||||
// Only run the bot for 3 seconds
|
||||
int runtime = 3;
|
||||
|
||||
// We could try to capture the output ... not sure how.
|
||||
bool fluentd = true;
|
||||
|
||||
int64_t sentCount = ix::cobra_to_stdout_bot(config,
|
||||
channel,
|
||||
filter,
|
||||
position,
|
||||
fluentd,
|
||||
quiet,
|
||||
verbose,
|
||||
maxQueueSize,
|
||||
enableHeartbeat,
|
||||
runtime);
|
||||
//
|
||||
// We want at least 2 messages to be sent
|
||||
//
|
||||
REQUIRE(sentCount >= 2);
|
||||
|
||||
// Give us 1s for all messages to be received
|
||||
ix::msleep(1000);
|
||||
|
||||
spdlog::info("Stopping snake server...");
|
||||
snakeServer.stop();
|
||||
|
||||
spdlog::info("Stopping redis server...");
|
||||
redisServer.stop();
|
||||
|
||||
publisherThread.join();
|
||||
}
|
||||
}
|
@ -28,7 +28,6 @@
|
||||
#include <ixwebsocket/IXWebSocketHandshake.h>
|
||||
#include <ixwebsocket/IXWebSocketHttpHeaders.h>
|
||||
#include <ixwebsocket/IXWebSocketMessage.h>
|
||||
#include <ixwebsocket/IXWebSocketMessageQueue.h>
|
||||
#include <ixwebsocket/IXWebSocketMessageType.h>
|
||||
#include <ixwebsocket/IXWebSocketOpenInfo.h>
|
||||
#include <ixwebsocket/IXWebSocketPerMessageDeflate.h>
|
||||
|
306
test/IXWebSocketBroadcastTest.cpp
Normal file
306
test/IXWebSocketBroadcastTest.cpp
Normal file
@ -0,0 +1,306 @@
|
||||
/*
|
||||
* IXWebSocketServerTest.cpp
|
||||
* Author: Benjamin Sergeant
|
||||
* Copyright (c) 2019 Machine Zone. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "IXTest.h"
|
||||
#include "catch.hpp"
|
||||
#include "msgpack11.hpp"
|
||||
#include <iostream>
|
||||
#include <ixwebsocket/IXSocket.h>
|
||||
#include <ixwebsocket/IXSocketFactory.h>
|
||||
#include <ixwebsocket/IXWebSocket.h>
|
||||
#include <ixwebsocket/IXWebSocketServer.h>
|
||||
|
||||
using msgpack11::MsgPack;
|
||||
using namespace ix;
|
||||
|
||||
namespace
|
||||
{
|
||||
class WebSocketChat
|
||||
{
|
||||
public:
|
||||
WebSocketChat(const std::string& user, const std::string& session, int port);
|
||||
|
||||
void subscribe(const std::string& channel);
|
||||
void start();
|
||||
void stop();
|
||||
bool isReady() const;
|
||||
|
||||
void sendMessage(const std::string& text);
|
||||
size_t getReceivedMessagesCount() const;
|
||||
const std::vector<std::string>& getReceivedMessages() const;
|
||||
|
||||
std::string encodeMessage(const std::string& text);
|
||||
std::pair<std::string, std::string> decodeMessage(const std::string& str);
|
||||
void appendMessage(const std::string& message);
|
||||
|
||||
private:
|
||||
std::string _user;
|
||||
std::string _session;
|
||||
int _port;
|
||||
|
||||
ix::WebSocket _webSocket;
|
||||
|
||||
std::vector<std::string> _receivedMessages;
|
||||
mutable std::mutex _mutex;
|
||||
};
|
||||
|
||||
WebSocketChat::WebSocketChat(const std::string& user, const std::string& session, int port)
|
||||
: _user(user)
|
||||
, _session(session)
|
||||
, _port(port)
|
||||
{
|
||||
_webSocket.setTLSOptions(makeClientTLSOptions());
|
||||
}
|
||||
|
||||
size_t WebSocketChat::getReceivedMessagesCount() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
return _receivedMessages.size();
|
||||
}
|
||||
|
||||
const std::vector<std::string>& WebSocketChat::getReceivedMessages() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
return _receivedMessages;
|
||||
}
|
||||
|
||||
void WebSocketChat::appendMessage(const std::string& message)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
_receivedMessages.push_back(message);
|
||||
}
|
||||
|
||||
bool WebSocketChat::isReady() const
|
||||
{
|
||||
return _webSocket.getReadyState() == ix::ReadyState::Open;
|
||||
}
|
||||
|
||||
void WebSocketChat::stop()
|
||||
{
|
||||
_webSocket.stop();
|
||||
}
|
||||
|
||||
void WebSocketChat::start()
|
||||
{
|
||||
std::string url;
|
||||
{
|
||||
bool preferTLS = true;
|
||||
url = makeCobraEndpoint(_port, preferTLS);
|
||||
}
|
||||
|
||||
_webSocket.setUrl(url);
|
||||
|
||||
std::stringstream ss;
|
||||
log(std::string("Connecting to url: ") + url);
|
||||
|
||||
_webSocket.setOnMessageCallback([this](const ix::WebSocketMessagePtr& msg) {
|
||||
std::stringstream ss;
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
ss << "websocket_broadcast_client: " << _user << " Connected !";
|
||||
log(ss.str());
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||
{
|
||||
ss << "websocket_broadcast_client: " << _user << " disconnected !";
|
||||
log(ss.str());
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||
{
|
||||
auto result = decodeMessage(msg->str);
|
||||
|
||||
// Our "chat" / "broacast" node.js server does not send us
|
||||
// the messages we send, so we don't need to have a msg_user != user
|
||||
// as we do for the satori chat example.
|
||||
|
||||
// store text
|
||||
appendMessage(result.second);
|
||||
|
||||
std::string payload = result.second;
|
||||
if (payload.size() > 2000)
|
||||
{
|
||||
payload = "<message too large>";
|
||||
}
|
||||
|
||||
ss << std::endl << result.first << " > " << payload << std::endl << _user << " > ";
|
||||
log(ss.str());
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Error)
|
||||
{
|
||||
ss << "websocket_broadcast_client: " << _user << " Error ! "
|
||||
<< msg->errorInfo.reason;
|
||||
log(ss.str());
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Ping)
|
||||
{
|
||||
log("websocket_broadcast_client: received ping message");
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Pong)
|
||||
{
|
||||
log("websocket_broadcast_client: received pong message");
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Fragment)
|
||||
{
|
||||
log("websocket_broadcast_client: received message fragment");
|
||||
}
|
||||
else
|
||||
{
|
||||
ss << "Unexpected ix::WebSocketMessageType";
|
||||
log(ss.str());
|
||||
}
|
||||
});
|
||||
|
||||
_webSocket.start();
|
||||
}
|
||||
|
||||
std::pair<std::string, std::string> WebSocketChat::decodeMessage(const std::string& str)
|
||||
{
|
||||
std::string errMsg;
|
||||
MsgPack msg = MsgPack::parse(str, errMsg);
|
||||
|
||||
std::string msg_user = msg["user"].string_value();
|
||||
std::string msg_text = msg["text"].string_value();
|
||||
|
||||
return std::pair<std::string, std::string>(msg_user, msg_text);
|
||||
}
|
||||
|
||||
std::string WebSocketChat::encodeMessage(const std::string& text)
|
||||
{
|
||||
std::map<MsgPack, MsgPack> obj;
|
||||
obj["user"] = _user;
|
||||
obj["text"] = text;
|
||||
|
||||
MsgPack msg(obj);
|
||||
|
||||
std::string output = msg.dump();
|
||||
return output;
|
||||
}
|
||||
|
||||
void WebSocketChat::sendMessage(const std::string& text)
|
||||
{
|
||||
_webSocket.sendBinary(encodeMessage(text));
|
||||
}
|
||||
|
||||
bool startServer(ix::WebSocketServer& server, std::string& connectionId)
|
||||
{
|
||||
bool preferTLS = true;
|
||||
server.setTLSOptions(makeServerTLSOptions(preferTLS));
|
||||
|
||||
server.setOnConnectionCallback([&server, &connectionId](
|
||||
std::shared_ptr<ix::WebSocket> webSocket,
|
||||
std::shared_ptr<ConnectionState> connectionState) {
|
||||
webSocket->setOnMessageCallback([webSocket, connectionState, &connectionId, &server](
|
||||
const ix::WebSocketMessagePtr& msg) {
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
TLogger() << "New connection";
|
||||
connectionState->computeId();
|
||||
TLogger() << "id: " << connectionState->getId();
|
||||
TLogger() << "Uri: " << msg->openInfo.uri;
|
||||
TLogger() << "Headers:";
|
||||
for (auto it : msg->openInfo.headers)
|
||||
{
|
||||
TLogger() << it.first << ": " << it.second;
|
||||
}
|
||||
|
||||
connectionId = connectionState->getId();
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||
{
|
||||
TLogger() << "Closed connection";
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||
{
|
||||
for (auto&& client : server.getClients())
|
||||
{
|
||||
if (client != webSocket)
|
||||
{
|
||||
client->send(msg->str, msg->binary);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
auto res = server.listen();
|
||||
if (!res.first)
|
||||
{
|
||||
TLogger() << res.second;
|
||||
return false;
|
||||
}
|
||||
|
||||
server.start();
|
||||
return true;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("Websocket_broadcast_server", "[websocket_server]")
|
||||
{
|
||||
SECTION("Connect to the server, do not send anything. Should timeout and return 400")
|
||||
{
|
||||
int port = getFreePort();
|
||||
ix::WebSocketServer server(port);
|
||||
std::string connectionId;
|
||||
REQUIRE(startServer(server, connectionId));
|
||||
|
||||
std::string session = ix::generateSessionId();
|
||||
std::vector<std::shared_ptr<WebSocketChat>> chatClients;
|
||||
for (int i = 0; i < 10; ++i)
|
||||
{
|
||||
std::string user("user_" + std::to_string(i));
|
||||
chatClients.push_back(std::make_shared<WebSocketChat>(user, session, port));
|
||||
chatClients[i]->start();
|
||||
ix::msleep(50);
|
||||
}
|
||||
|
||||
// Wait for all chat instance to be ready
|
||||
while (true)
|
||||
{
|
||||
bool allReady = true;
|
||||
for (size_t i = 0; i < chatClients.size(); ++i)
|
||||
{
|
||||
allReady &= chatClients[i]->isReady();
|
||||
}
|
||||
if (allReady) break;
|
||||
ix::msleep(10);
|
||||
}
|
||||
|
||||
for (int j = 0; j < 1000; j++)
|
||||
{
|
||||
for (size_t i = 0; i < chatClients.size(); ++i)
|
||||
{
|
||||
chatClients[i]->sendMessage("hello world");
|
||||
}
|
||||
|
||||
if (j == 250)
|
||||
{
|
||||
server.stop();
|
||||
ix::msleep(100);
|
||||
}
|
||||
if (j == 500)
|
||||
{
|
||||
server.start();
|
||||
ix::msleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
// wait 1 second
|
||||
ix::msleep(2000);
|
||||
|
||||
// Stop all clients
|
||||
size_t messageCount = chatClients.size() * 50;
|
||||
for (size_t i = 0; i < chatClients.size(); ++i)
|
||||
{
|
||||
REQUIRE(chatClients[i]->getReceivedMessagesCount() >= messageCount);
|
||||
chatClients[i]->stop();
|
||||
}
|
||||
|
||||
// Give us 500ms for the server to notice that clients went away
|
||||
ix::msleep(500);
|
||||
server.stop();
|
||||
REQUIRE(server.getClients().size() == 0);
|
||||
}
|
||||
}
|
@ -1,178 +0,0 @@
|
||||
/*
|
||||
* IXWebSocketMessageQTest.cpp
|
||||
* Author: Korchynskyi Dmytro
|
||||
* Copyright (c) 2019 Machine Zone. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "IXTest.h"
|
||||
#include "catch.hpp"
|
||||
#include <ixwebsocket/IXWebSocket.h>
|
||||
#include <ixwebsocket/IXWebSocketMessageQueue.h>
|
||||
#include <ixwebsocket/IXWebSocketServer.h>
|
||||
#include <thread>
|
||||
|
||||
using namespace ix;
|
||||
|
||||
namespace
|
||||
{
|
||||
bool startServer(ix::WebSocketServer& server)
|
||||
{
|
||||
server.setOnConnectionCallback([&server](std::shared_ptr<ix::WebSocket> webSocket,
|
||||
std::shared_ptr<ConnectionState> connectionState) {
|
||||
webSocket->setOnMessageCallback(
|
||||
[connectionState, &server](const WebSocketMessagePtr& msg) {
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
TLogger() << "New connection";
|
||||
connectionState->computeId();
|
||||
TLogger() << "id: " << connectionState->getId();
|
||||
TLogger() << "Uri: " << msg->openInfo.uri;
|
||||
TLogger() << "Headers:";
|
||||
for (auto&& it : msg->openInfo.headers)
|
||||
{
|
||||
TLogger() << it.first << ": " << it.second;
|
||||
}
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||
{
|
||||
TLogger() << "Closed connection";
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||
{
|
||||
TLogger() << "Message received: " << msg->str;
|
||||
|
||||
for (auto&& client : server.getClients())
|
||||
{
|
||||
client->send(msg->str);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
auto res = server.listen();
|
||||
if (!res.first)
|
||||
{
|
||||
TLogger() << res.second;
|
||||
return false;
|
||||
}
|
||||
|
||||
server.start();
|
||||
return true;
|
||||
}
|
||||
|
||||
class MsgQTestClient
|
||||
{
|
||||
public:
|
||||
MsgQTestClient()
|
||||
{
|
||||
msgQ.bindWebsocket(&ws);
|
||||
|
||||
msgQ.setOnMessageCallback([this](const WebSocketMessagePtr& msg) {
|
||||
REQUIRE(mainThreadId == std::this_thread::get_id());
|
||||
|
||||
std::stringstream ss;
|
||||
if (msg->type == WebSocketMessageType::Open)
|
||||
{
|
||||
log("client connected");
|
||||
sendNextMessage();
|
||||
}
|
||||
else if (msg->type == WebSocketMessageType::Close)
|
||||
{
|
||||
log("client disconnected");
|
||||
}
|
||||
else if (msg->type == WebSocketMessageType::Error)
|
||||
{
|
||||
ss << "Error ! " << msg->errorInfo.reason;
|
||||
log(ss.str());
|
||||
testDone = true;
|
||||
}
|
||||
else if (msg->type == WebSocketMessageType::Pong)
|
||||
{
|
||||
ss << "Received pong message " << msg->str;
|
||||
log(ss.str());
|
||||
}
|
||||
else if (msg->type == WebSocketMessageType::Ping)
|
||||
{
|
||||
ss << "Received ping message " << msg->str;
|
||||
log(ss.str());
|
||||
}
|
||||
else if (msg->type == WebSocketMessageType::Message)
|
||||
{
|
||||
REQUIRE(msg->str.compare("Hey dude!") == 0);
|
||||
++receivedCount;
|
||||
ss << "Received message " << msg->str;
|
||||
log(ss.str());
|
||||
sendNextMessage();
|
||||
}
|
||||
else
|
||||
{
|
||||
ss << "Invalid WebSocketMessageType";
|
||||
log(ss.str());
|
||||
testDone = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void sendNextMessage()
|
||||
{
|
||||
if (receivedCount >= 3)
|
||||
{
|
||||
testDone = true;
|
||||
succeeded = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto info = ws.sendText("Hey dude!");
|
||||
if (info.success)
|
||||
log("sent message");
|
||||
else
|
||||
log("send failed");
|
||||
}
|
||||
}
|
||||
|
||||
void run(const std::string& url)
|
||||
{
|
||||
mainThreadId = std::this_thread::get_id();
|
||||
testDone = false;
|
||||
receivedCount = 0;
|
||||
|
||||
ws.setUrl(url);
|
||||
ws.start();
|
||||
|
||||
while (!testDone)
|
||||
{
|
||||
msgQ.poll();
|
||||
msleep(50);
|
||||
}
|
||||
}
|
||||
|
||||
bool isSucceeded() const
|
||||
{
|
||||
return succeeded;
|
||||
}
|
||||
|
||||
private:
|
||||
WebSocket ws;
|
||||
WebSocketMessageQueue msgQ;
|
||||
bool testDone = false;
|
||||
uint32_t receivedCount = 0;
|
||||
std::thread::id mainThreadId;
|
||||
bool succeeded = false;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("Websocket_message_queue", "[websocket_message_q]")
|
||||
{
|
||||
SECTION("Send several messages")
|
||||
{
|
||||
int port = getFreePort();
|
||||
WebSocketServer server(port);
|
||||
REQUIRE(startServer(server));
|
||||
|
||||
MsgQTestClient testClient;
|
||||
testClient.run("ws://127.0.0.1:" + std::to_string(port));
|
||||
REQUIRE(testClient.isSucceeded());
|
||||
|
||||
server.stop();
|
||||
}
|
||||
}
|
@ -65,7 +65,7 @@ namespace
|
||||
// Set a 1 second heartbeat with the setter method to test
|
||||
if (_useHeartBeatMethod)
|
||||
{
|
||||
_webSocket.setHeartBeatPeriod(1);
|
||||
_webSocket.setPingInterval(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -378,9 +378,9 @@ TEST_CASE("Websocket_ping_data_sent_setPingInterval_full", "[setPingInterval]")
|
||||
}
|
||||
}
|
||||
|
||||
// Using setHeartBeatPeriod
|
||||
// Using setPingInterval
|
||||
|
||||
TEST_CASE("Websocket_ping_no_data_sent_setHeartBeatPeriod", "[setHeartBeatPeriod]")
|
||||
TEST_CASE("Websocket_ping_no_data_sent_setHeartBeatPeriod", "[setPingInterval]")
|
||||
{
|
||||
SECTION("Make sure that ping messages are sent when no other data are sent.")
|
||||
{
|
||||
@ -424,7 +424,7 @@ TEST_CASE("Websocket_ping_no_data_sent_setHeartBeatPeriod", "[setHeartBeatPeriod
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Websocket_ping_data_sent_setHeartBeatPeriod", "[setHeartBeatPeriod]")
|
||||
TEST_CASE("Websocket_ping_data_sent_setHeartBeatPeriod", "[setPingInterval]")
|
||||
{
|
||||
SECTION("Make sure that ping messages are sent, even if other messages are sent")
|
||||
{
|
||||
|
@ -62,7 +62,7 @@ namespace ix
|
||||
{
|
||||
if (client != webSocket)
|
||||
{
|
||||
client->send(msg->str);
|
||||
client->send(msg->str, msg->binary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
207
third_party/jsoncpp/json/json-forwards.h
vendored
207
third_party/jsoncpp/json/json-forwards.h
vendored
@ -1,4 +1,4 @@
|
||||
/// Json-cpp amalgated forward header (http://jsoncpp.sourceforge.net/).
|
||||
/// 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.
|
||||
|
||||
@ -11,13 +11,13 @@ The JsonCpp library's source code, including accompanying documentation,
|
||||
tests and demonstration applications, are licensed under the following
|
||||
conditions...
|
||||
|
||||
The author (Baptiste Lepilleur) explicitly disclaims copyright in all
|
||||
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 is
|
||||
released under the terms of the MIT License (see below).
|
||||
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
|
||||
@ -32,7 +32,7 @@ described in clear, concise terms at:
|
||||
The full text of the MIT License follows:
|
||||
|
||||
========================================================================
|
||||
Copyright (c) 2007-2010 Baptiste Lepilleur
|
||||
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
|
||||
@ -73,9 +73,9 @@ license you like.
|
||||
|
||||
|
||||
|
||||
#ifndef JSON_FORWARD_AMALGATED_H_INCLUDED
|
||||
# define JSON_FORWARD_AMALGATED_H_INCLUDED
|
||||
/// If defined, indicates that the source file is amalgated
|
||||
#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
|
||||
|
||||
@ -83,23 +83,21 @@ license you like.
|
||||
// Beginning of content of file: include/json/config.h
|
||||
// //////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Copyright 2007-2010 Baptiste Lepilleur
|
||||
// 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
|
||||
|
||||
/// If defined, indicates that json library is embedded in CppTL library.
|
||||
//# define JSON_IN_CPPTL 1
|
||||
|
||||
/// If defined, indicates that json may leverage CppTL library
|
||||
//# define JSON_USE_CPPTL 1
|
||||
/// If defined, indicates that cpptl vector based map should be used instead of
|
||||
/// std::map
|
||||
/// as Value container.
|
||||
//# define JSON_USE_CPPTL_SMALLMAP 1
|
||||
#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.
|
||||
@ -107,43 +105,49 @@ license you like.
|
||||
#define JSON_USE_EXCEPTION 1
|
||||
#endif
|
||||
|
||||
/// If defined, indicates that the source file is amalgated
|
||||
// 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 amalgated header.
|
||||
/// Remarks: it is automatically defined in the generated amalgamated header.
|
||||
// #define JSON_IS_AMALGAMATION
|
||||
|
||||
#ifdef JSON_IN_CPPTL
|
||||
#include <cpptl/config.h>
|
||||
#ifndef JSON_USE_CPPTL
|
||||
#define JSON_USE_CPPTL 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef JSON_IN_CPPTL
|
||||
#define JSON_API CPPTL_API
|
||||
#elif defined(JSON_DLL_BUILD)
|
||||
#if defined(_MSC_VER)
|
||||
// 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)
|
||||
#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_IN_CPPTL
|
||||
#endif // ifdef JSON_DLL_BUILD
|
||||
|
||||
#if !defined(JSON_API)
|
||||
#define JSON_API
|
||||
#endif
|
||||
|
||||
#if !defined(JSON_HAS_UNIQUE_PTR)
|
||||
#if __cplusplus >= 201103L
|
||||
#define JSON_HAS_UNIQUE_PTR (1)
|
||||
#elif _MSC_VER >= 1600
|
||||
#define JSON_HAS_UNIQUE_PTR (1)
|
||||
#else
|
||||
#define JSON_HAS_UNIQUE_PTR (0)
|
||||
#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
|
||||
@ -151,55 +155,96 @@ license you like.
|
||||
// Storages, and 64 bits integer support is disabled.
|
||||
// #define JSON_NO_INT64 1
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER <= 1200 // MSVC 6
|
||||
// Microsoft Visual Studio 6 only support conversion from __int64 to double
|
||||
// (no conversion from unsigned __int64).
|
||||
#define JSON_USE_INT64_DOUBLE_CONVERSION 1
|
||||
// Disable warning 4786 for VS6 caused by STL (identifier was truncated to '255'
|
||||
// characters in the debug information)
|
||||
// All projects I've ever seen with VS6 were using this globally (not bothering
|
||||
// with pragma push/pop).
|
||||
#pragma warning(disable : 4786)
|
||||
#endif // if defined(_MSC_VER) && _MSC_VER < 1200 // MSVC 6
|
||||
// JSONCPP_OVERRIDE is maintained for backwards compatibility of external tools.
|
||||
// C++11 should be used directly in JSONCPP.
|
||||
#define JSONCPP_OVERRIDE override
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER >= 1500 // MSVC 2008
|
||||
/// Indicates that the following function is deprecated.
|
||||
#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))
|
||||
#elif defined(__clang__) && defined(__has_feature)
|
||||
#if __has_feature(attribute_deprecated_with_message)
|
||||
#define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message)))
|
||||
#endif
|
||||
#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
|
||||
#define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message)))
|
||||
#elif defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
|
||||
#define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__))
|
||||
#endif
|
||||
#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 {
|
||||
typedef int Int;
|
||||
typedef unsigned int UInt;
|
||||
using Int = int;
|
||||
using UInt = unsigned int;
|
||||
#if defined(JSON_NO_INT64)
|
||||
typedef int LargestInt;
|
||||
typedef unsigned int LargestUInt;
|
||||
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
|
||||
typedef __int64 Int64;
|
||||
typedef unsigned __int64 UInt64;
|
||||
using Int64 = __int64;
|
||||
using UInt64 = unsigned __int64;
|
||||
#else // if defined(_MSC_VER) // Other platforms, use long long
|
||||
typedef long long int Int64;
|
||||
typedef unsigned long long int UInt64;
|
||||
#endif // if defined(_MSC_VER)
|
||||
typedef Int64 LargestInt;
|
||||
typedef UInt64 LargestUInt;
|
||||
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)
|
||||
} // end namespace Json
|
||||
|
||||
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
|
||||
|
||||
@ -216,7 +261,7 @@ typedef UInt64 LargestUInt;
|
||||
// Beginning of content of file: include/json/forwards.h
|
||||
// //////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Copyright 2007-2010 Baptiste Lepilleur
|
||||
// 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
|
||||
@ -231,17 +276,23 @@ typedef UInt64 LargestUInt;
|
||||
namespace Json {
|
||||
|
||||
// writer.h
|
||||
class StreamWriter;
|
||||
class StreamWriterBuilder;
|
||||
class Writer;
|
||||
class FastWriter;
|
||||
class StyledWriter;
|
||||
class StyledStreamWriter;
|
||||
|
||||
// reader.h
|
||||
class Reader;
|
||||
class CharReader;
|
||||
class CharReaderBuilder;
|
||||
|
||||
// features.h
|
||||
// json_features.h
|
||||
class Features;
|
||||
|
||||
// value.h
|
||||
typedef unsigned int ArrayIndex;
|
||||
using ArrayIndex = unsigned int;
|
||||
class StaticString;
|
||||
class Path;
|
||||
class PathArgument;
|
||||
@ -262,4 +313,4 @@ class ValueConstIterator;
|
||||
|
||||
|
||||
|
||||
#endif //ifndef JSON_FORWARD_AMALGATED_H_INCLUDED
|
||||
#endif //ifndef JSON_FORWARD_AMALGAMATED_H_INCLUDED
|
||||
|
1712
third_party/jsoncpp/json/json.h
vendored
1712
third_party/jsoncpp/json/json.h
vendored
File diff suppressed because it is too large
Load Diff
3168
third_party/jsoncpp/jsoncpp.cpp
vendored
3168
third_party/jsoncpp/jsoncpp.cpp
vendored
File diff suppressed because it is too large
Load Diff
@ -1,19 +1,32 @@
|
||||
Checks: '\
|
||||
cppcoreguidelines-*,\
|
||||
performance-*,\
|
||||
-performance-unnecessary-value-param,\
|
||||
modernize-*,\
|
||||
-modernize-use-trailing-return-type,\
|
||||
google-*,\
|
||||
-google-runtime-references,\
|
||||
misc-*,\
|
||||
-misc-non-private-member-variables-in-classes,\
|
||||
cert-*,\
|
||||
readability-*,\
|
||||
clang-analyzer-*'
|
||||
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: 'async.h|async_logger.h|common.h|details|formatter.h|logger.h|sinks|spdlog.h|tweakme.h|version.h'
|
||||
HeaderFilterRegex: '*spdlog/[^f].*'
|
||||
AnalyzeTemporaryDtors: false
|
||||
FormatStyle: none
|
||||
|
1
third_party/spdlog/.travis.yml
vendored
1
third_party/spdlog/.travis.yml
vendored
@ -97,6 +97,7 @@ script:
|
||||
-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 \
|
||||
|
91
third_party/spdlog/CMakeLists.txt
vendored
91
third_party/spdlog/CMakeLists.txt
vendored
@ -3,15 +3,27 @@
|
||||
|
||||
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(GNUInstallDirs)
|
||||
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
|
||||
#---------------------------------------------------------------------------------------
|
||||
@ -19,15 +31,21 @@ if(NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose Release or Debug" FORCE)
|
||||
endif()
|
||||
|
||||
project(spdlog VERSION ${SPDLOG_VERSION} LANGUAGES CXX)
|
||||
message(STATUS "Build spdlog: ${SPDLOG_VERSION}")
|
||||
|
||||
#---------------------------------------------------------------------------------------
|
||||
# Compiler config
|
||||
#---------------------------------------------------------------------------------------
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
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
|
||||
@ -42,16 +60,17 @@ if (NOT DEFINED SPDLOG_MASTER_PROJECT)
|
||||
endif ()
|
||||
|
||||
# build shared option
|
||||
if(NOT WIN32)
|
||||
option(SPDLOG_BUILD_SHARED "Build shared library" OFF)
|
||||
endif()
|
||||
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" ${SPDLOG_MASTER_PROJECT})
|
||||
option(SPDLOG_BUILD_TESTS "Build tests" OFF)
|
||||
option(SPDLOG_BUILD_TESTS_HO "Build tests using the header only version" OFF)
|
||||
|
||||
# bench options
|
||||
@ -60,6 +79,9 @@ option(SPDLOG_BUILD_BENCH "Build benchmarks (Requires https://github.com/google/
|
||||
# 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)
|
||||
@ -84,6 +106,17 @@ option(SPDLOG_NO_THREAD_ID "prevent spdlog from querying the thread id on each l
|
||||
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})
|
||||
#---------------------------------------------------------------------------------------
|
||||
@ -92,20 +125,29 @@ message(STATUS "Build type: " ${CMAKE_BUILD_TYPE})
|
||||
set(SPDLOG_SRCS
|
||||
src/spdlog.cpp
|
||||
src/stdout_sinks.cpp
|
||||
src/color_sinks.cpp
|
||||
src/color_sinks.cpp
|
||||
src/file_sinks.cpp
|
||||
src/async.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)
|
||||
if(WIN32)
|
||||
message(FATAL_ERROR "spdlog shared lib is not yet supported under windows")
|
||||
endif()
|
||||
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()
|
||||
@ -122,6 +164,11 @@ 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
|
||||
#---------------------------------------------------------------------------------------
|
||||
@ -209,8 +256,12 @@ endif()
|
||||
# Build binaries
|
||||
#---------------------------------------------------------------------------------------
|
||||
if(SPDLOG_BUILD_EXAMPLE OR SPDLOG_BUILD_EXAMPLE_HO)
|
||||
message(STATUS "Generating examples")
|
||||
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)
|
||||
@ -241,7 +292,10 @@ if (SPDLOG_INSTALL)
|
||||
# Include files
|
||||
#---------------------------------------------------------------------------------------
|
||||
install(DIRECTORY include/ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" PATTERN "fmt/bundled" EXCLUDE)
|
||||
install(TARGETS spdlog spdlog_header_only EXPORT spdlog DESTINATION "${CMAKE_INSTALL_LIBDIR}")
|
||||
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/
|
||||
@ -277,6 +331,5 @@ if (SPDLOG_INSTALL)
|
||||
# Support creation of installable packages
|
||||
#---------------------------------------------------------------------------------------
|
||||
include(cmake/spdlogCPack.cmake)
|
||||
|
||||
endif ()
|
||||
|
||||
|
4
third_party/spdlog/LICENSE
vendored
4
third_party/spdlog/LICENSE
vendored
@ -20,3 +20,7 @@ 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
|
||||
|
||||
|
76
third_party/spdlog/README.md
vendored
76
third_party/spdlog/README.md
vendored
@ -1,8 +1,6 @@
|
||||
# spdlog
|
||||
|
||||
Very fast, header-only/compiled, C++ logging library. [](https://travis-ci.org/gabime/spdlog) [](https://ci.appveyor.com/project/gabime/spdlog)
|
||||
|
||||
|
||||
Very fast, header-only/compiled, C++ logging library. [](https://travis-ci.org/gabime/spdlog) [](https://ci.appveyor.com/project/gabime/spdlog) [](https://github.com/gabime/spdlog/releases/latest)
|
||||
|
||||
## Install
|
||||
#### Header only version
|
||||
@ -25,20 +23,22 @@ $ cmake .. && make -j
|
||||
|
||||
## 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: `yaourt -S spdlog-git`
|
||||
* Arch Linux: `pacman -S spdlog`
|
||||
* vcpkg: `vcpkg install spdlog`
|
||||
* conan: `spdlog/[>=1.4.1]@bincrafters/stable`
|
||||
* conan: `spdlog/[>=1.4.1]`
|
||||
* conda: `conda install -c conda-forge spdlog`
|
||||
|
||||
|
||||
## Features
|
||||
* Very fast (see [benchmarks](#benchmarks) below).
|
||||
* Headers only, just copy and use. Or use as a compiled library.
|
||||
* 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 or other messages in a ring buffer and display later on demand.
|
||||
* Fast asynchronous mode (optional)
|
||||
* **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:
|
||||
@ -48,7 +48,8 @@ $ cmake .. && make -j
|
||||
* syslog.
|
||||
* Windows debugger (```OutputDebugString(..)```)
|
||||
* Easily extendable with custom log targets (just implement a single function in the [sink](include/spdlog/sinks/sink.h) interface).
|
||||
* Severity based filtering - threshold levels can be modified in runtime as well as in compile time.
|
||||
* 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
|
||||
@ -77,15 +78,30 @@ int main()
|
||||
|
||||
// Compile time log levels
|
||||
// define SPDLOG_ACTIVE_LEVEL to desired level
|
||||
SPDLOG_TRACE("Some trace message with param {}", {});
|
||||
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);
|
||||
}
|
||||
|
||||
```
|
||||
#### create stdout/stderr logger object
|
||||
#### 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"
|
||||
@ -158,7 +174,7 @@ spdlog::dump_backtrace(); // log them now! show the last 32 messages
|
||||
#### Periodic flush
|
||||
```c++
|
||||
// periodically flush all *registered* loggers every 3 seconds:
|
||||
// warning: only use if all your loggers are thread safe!
|
||||
// warning: only use if all your loggers are thread safe ("_mt" loggers)
|
||||
spdlog::flush_every(std::chrono::seconds(3));
|
||||
|
||||
```
|
||||
@ -266,6 +282,37 @@ void user_defined_example()
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
#### 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++
|
||||
@ -277,6 +324,8 @@ void err_handler_example()
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
---
|
||||
#### syslog
|
||||
```c++
|
||||
@ -321,7 +370,7 @@ Below are some [benchmarks](https://github.com/gabime/spdlog/blob/v1.x/bench/ben
|
||||
[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 sharing same logger, 1,000,000 iterations
|
||||
[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
|
||||
@ -335,7 +384,6 @@ Below are some [benchmarks](https://github.com/gabime/spdlog/blob/v1.x/bench/ben
|
||||
[info] Threads : 10
|
||||
[info] Queue : 8,192 slots
|
||||
[info] Queue memory : 8,192 x 272 = 2,176 KB
|
||||
[info] Total iters : 3
|
||||
[info] -------------------------------------------------
|
||||
[info]
|
||||
[info] *********************************
|
||||
|
2
third_party/spdlog/appveyor.yml
vendored
2
third_party/spdlog/appveyor.yml
vendored
@ -30,7 +30,7 @@ build_script:
|
||||
|
||||
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
|
||||
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%
|
||||
|
||||
|
56
third_party/spdlog/bench/bench.cpp
vendored
56
third_party/spdlog/bench/bench.cpp
vendored
@ -19,19 +19,15 @@
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
using namespace std;
|
||||
using namespace std::chrono;
|
||||
using namespace spdlog;
|
||||
using namespace spdlog::sinks;
|
||||
using namespace utils;
|
||||
|
||||
void bench(int howmany, std::shared_ptr<spdlog::logger> log);
|
||||
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count);
|
||||
void bench_default_api(int howmany, std::shared_ptr<spdlog::logger> log);
|
||||
void bench_c_string(int howmany, std::shared_ptr<spdlog::logger> log);
|
||||
|
||||
static size_t file_size = 30 * 1024 * 1024;
|
||||
static size_t rotating_files = 5;
|
||||
// void bench_default_api(int howmany, std::shared_ptr<spdlog::logger> log);
|
||||
// void bench_c_string(int howmany, std::shared_ptr<spdlog::logger> log);
|
||||
|
||||
static const size_t file_size = 30 * 1024 * 1024;
|
||||
static const size_t rotating_files = 5;
|
||||
static const int max_threads = 1000;
|
||||
|
||||
void bench_threaded_logging(int threads, int iters)
|
||||
{
|
||||
@ -116,9 +112,18 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
|
||||
if (argc > 1)
|
||||
iters = atoi(argv[1]);
|
||||
{
|
||||
iters = std::stoi(argv[1]);
|
||||
}
|
||||
if (argc > 2)
|
||||
threads = atoi(argv[2]);
|
||||
{
|
||||
threads = std::stoi(argv[2]);
|
||||
}
|
||||
|
||||
if (threads > max_threads)
|
||||
{
|
||||
throw std::runtime_error(fmt::format("Number of threads exceeds maximum({}})", max_threads));
|
||||
}
|
||||
|
||||
bench_single_threaded(iters);
|
||||
bench_threaded_logging(1, iters);
|
||||
@ -134,7 +139,10 @@ int main(int argc, char *argv[])
|
||||
|
||||
void bench(int howmany, std::shared_ptr<spdlog::logger> log)
|
||||
{
|
||||
using std::chrono::duration;
|
||||
using std::chrono::duration_cast;
|
||||
using std::chrono::high_resolution_clock;
|
||||
|
||||
auto start = high_resolution_clock::now();
|
||||
for (auto i = 0; i < howmany; ++i)
|
||||
{
|
||||
@ -150,17 +158,21 @@ void bench(int howmany, std::shared_ptr<spdlog::logger> log)
|
||||
|
||||
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count)
|
||||
{
|
||||
using std::chrono::duration;
|
||||
using std::chrono::duration_cast;
|
||||
using std::chrono::high_resolution_clock;
|
||||
vector<thread> threads;
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
threads.reserve(thread_count);
|
||||
auto start = high_resolution_clock::now();
|
||||
for (int t = 0; t < thread_count; ++t)
|
||||
{
|
||||
threads.push_back(std::thread([&]() {
|
||||
threads.emplace_back([&]() {
|
||||
for (int j = 0; j < howmany / thread_count; j++)
|
||||
{
|
||||
log->info("Hello logger: msg number {}", j);
|
||||
}
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
for (auto &t : threads)
|
||||
@ -174,9 +186,13 @@ void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count
|
||||
spdlog::drop(log->name());
|
||||
}
|
||||
|
||||
/*
|
||||
void bench_default_api(int howmany, std::shared_ptr<spdlog::logger> log)
|
||||
{
|
||||
using std::chrono::high_resolution_clock;
|
||||
using std::chrono::duration;
|
||||
using std::chrono::duration_cast;
|
||||
|
||||
auto orig_default = spdlog::default_logger();
|
||||
spdlog::set_default_logger(log);
|
||||
auto start = high_resolution_clock::now();
|
||||
@ -194,18 +210,22 @@ void bench_default_api(int howmany, std::shared_ptr<spdlog::logger> log)
|
||||
|
||||
void bench_c_string(int howmany, std::shared_ptr<spdlog::logger> log)
|
||||
{
|
||||
using std::chrono::high_resolution_clock;
|
||||
using std::chrono::duration;
|
||||
using std::chrono::duration_cast;
|
||||
|
||||
const char *msg = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum pharetra metus cursus "
|
||||
"lacus placerat congue. Nulla egestas, mauris a tincidunt tempus, enim lectus volutpat mi, eu consequat sem "
|
||||
"libero nec massa. In dapibus ipsum a diam rhoncus gravida. Etiam non dapibus eros. Donec fringilla dui sed "
|
||||
"augue pretium, nec scelerisque est maximus. Nullam convallis, sem nec blandit maximus, nisi turpis ornare "
|
||||
"nisl, sit amet volutpat neque massa eu odio. Maecenas malesuada quam ex, posuere congue nibh turpis duis.";
|
||||
using std::chrono::high_resolution_clock;
|
||||
|
||||
auto orig_default = spdlog::default_logger();
|
||||
spdlog::set_default_logger(log);
|
||||
auto start = high_resolution_clock::now();
|
||||
for (auto i = 0; i < howmany; ++i)
|
||||
{
|
||||
spdlog::log(level::info, msg);
|
||||
spdlog::log(spdlog::level::info, msg);
|
||||
}
|
||||
|
||||
auto delta = high_resolution_clock::now() - start;
|
||||
@ -214,3 +234,5 @@ void bench_c_string(int howmany, std::shared_ptr<spdlog::logger> log)
|
||||
spdlog::set_default_logger(std::move(orig_default));
|
||||
spdlog::info("{:<30} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d));
|
||||
}
|
||||
|
||||
*/
|
2
third_party/spdlog/bench/formatter-bench.cpp
vendored
2
third_party/spdlog/bench/formatter-bench.cpp
vendored
@ -6,7 +6,7 @@
|
||||
#include "benchmark/benchmark.h"
|
||||
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "spdlog/details/pattern_formatter.h"
|
||||
#include "spdlog/pattern_formatter.h"
|
||||
|
||||
void bench_formatter(benchmark::State &state, std::string pattern)
|
||||
{
|
||||
|
109
third_party/spdlog/bench/latency.cpp
vendored
109
third_party/spdlog/bench/latency.cpp
vendored
@ -51,11 +51,21 @@ void bench_disabled_macro(benchmark::State &state, std::shared_ptr<spdlog::logge
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
void bench_dev_null()
|
||||
{
|
||||
auto dev_null_st = spdlog::basic_logger_st("/dev/null_st", "/dev/null");
|
||||
benchmark::RegisterBenchmark("/dev/null_st", bench_logger, std::move(dev_null_st))->UseRealTime();
|
||||
spdlog::drop("/dev/null_st");
|
||||
|
||||
auto dev_null_mt = spdlog::basic_logger_mt("/dev/null_mt", "/dev/null");
|
||||
benchmark::RegisterBenchmark("/dev/null_mt", bench_logger, std::move(dev_null_mt))->UseRealTime();
|
||||
spdlog::drop("/dev/null_mt");
|
||||
}
|
||||
#endif // __linux__
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
|
||||
using spdlog::sinks::basic_file_sink_mt;
|
||||
using spdlog::sinks::basic_file_sink_st;
|
||||
using spdlog::sinks::null_sink_mt;
|
||||
using spdlog::sinks::null_sink_st;
|
||||
|
||||
@ -63,6 +73,8 @@ int main(int argc, char *argv[])
|
||||
size_t rotating_files = 5;
|
||||
int n_threads = benchmark::CPUInfo::Get().num_cpus;
|
||||
|
||||
auto full_bench = argc > 1 && std::string(argv[1]) == "full";
|
||||
|
||||
// disabled loggers
|
||||
auto disabled_logger = std::make_shared<spdlog::logger>("bench", std::make_shared<null_sink_mt>());
|
||||
disabled_logger->set_level(spdlog::level::off);
|
||||
@ -81,54 +93,61 @@ int main(int argc, char *argv[])
|
||||
tracing_null_logger_st->enable_backtrace(64);
|
||||
benchmark::RegisterBenchmark("null_sink_st/backtrace", bench_logger, tracing_null_logger_st);
|
||||
|
||||
// basic_st
|
||||
auto basic_st = spdlog::basic_logger_st("basic_st", "latency_logs/basic_st.log", true);
|
||||
benchmark::RegisterBenchmark("basic_st", bench_logger, std::move(basic_st))->UseRealTime();
|
||||
spdlog::drop("basic_st");
|
||||
// with backtrace of 64
|
||||
auto tracing_basic_st = spdlog::basic_logger_st("tracing_basic_st", "latency_logs/tracing_basic_st.log", true);
|
||||
tracing_basic_st->enable_backtrace(64);
|
||||
benchmark::RegisterBenchmark("basic_st/backtrace", bench_logger, std::move(tracing_basic_st))->UseRealTime();
|
||||
spdlog::drop("tracing_basic_st");
|
||||
#ifdef __linux
|
||||
bench_dev_null();
|
||||
#endif // __linux__
|
||||
|
||||
// rotating st
|
||||
auto rotating_st = spdlog::rotating_logger_st("rotating_st", "latency_logs/rotating_st.log", file_size, rotating_files);
|
||||
benchmark::RegisterBenchmark("rotating_st", bench_logger, std::move(rotating_st))->UseRealTime();
|
||||
spdlog::drop("rotating_st");
|
||||
// with backtrace of 64
|
||||
auto tracing_rotating_st =
|
||||
spdlog::rotating_logger_st("tracing_rotating_st", "latency_logs/tracing_rotating_st.log", file_size, rotating_files);
|
||||
benchmark::RegisterBenchmark("rotating_st/backtrace", bench_logger, std::move(tracing_rotating_st))->UseRealTime();
|
||||
spdlog::drop("tracing_rotating_st");
|
||||
if (full_bench)
|
||||
{
|
||||
// basic_st
|
||||
auto basic_st = spdlog::basic_logger_st("basic_st", "latency_logs/basic_st.log", true);
|
||||
benchmark::RegisterBenchmark("basic_st", bench_logger, std::move(basic_st))->UseRealTime();
|
||||
spdlog::drop("basic_st");
|
||||
// with backtrace of 64
|
||||
auto tracing_basic_st = spdlog::basic_logger_st("tracing_basic_st", "latency_logs/tracing_basic_st.log", true);
|
||||
tracing_basic_st->enable_backtrace(64);
|
||||
benchmark::RegisterBenchmark("basic_st/backtrace", bench_logger, std::move(tracing_basic_st))->UseRealTime();
|
||||
spdlog::drop("tracing_basic_st");
|
||||
|
||||
// daily st
|
||||
auto daily_st = spdlog::daily_logger_mt("daily_st", "latency_logs/daily_st.log");
|
||||
benchmark::RegisterBenchmark("daily_st", bench_logger, std::move(daily_st))->UseRealTime();
|
||||
spdlog::drop("daily_st");
|
||||
auto tracing_daily_st = spdlog::daily_logger_mt("tracing_daily_st", "latency_logs/daily_st.log");
|
||||
benchmark::RegisterBenchmark("daily_st/backtrace", bench_logger, std::move(tracing_daily_st))->UseRealTime();
|
||||
spdlog::drop("tracing_daily_st");
|
||||
// rotating st
|
||||
auto rotating_st = spdlog::rotating_logger_st("rotating_st", "latency_logs/rotating_st.log", file_size, rotating_files);
|
||||
benchmark::RegisterBenchmark("rotating_st", bench_logger, std::move(rotating_st))->UseRealTime();
|
||||
spdlog::drop("rotating_st");
|
||||
// with backtrace of 64
|
||||
auto tracing_rotating_st =
|
||||
spdlog::rotating_logger_st("tracing_rotating_st", "latency_logs/tracing_rotating_st.log", file_size, rotating_files);
|
||||
benchmark::RegisterBenchmark("rotating_st/backtrace", bench_logger, std::move(tracing_rotating_st))->UseRealTime();
|
||||
spdlog::drop("tracing_rotating_st");
|
||||
|
||||
//
|
||||
// Multi threaded bench, 10 loggers using same logger concurrently
|
||||
//
|
||||
auto null_logger_mt = std::make_shared<spdlog::logger>("bench", std::make_shared<null_sink_mt>());
|
||||
benchmark::RegisterBenchmark("null_sink_mt", bench_logger, null_logger_mt)->Threads(n_threads)->UseRealTime();
|
||||
// daily st
|
||||
auto daily_st = spdlog::daily_logger_mt("daily_st", "latency_logs/daily_st.log");
|
||||
benchmark::RegisterBenchmark("daily_st", bench_logger, std::move(daily_st))->UseRealTime();
|
||||
spdlog::drop("daily_st");
|
||||
auto tracing_daily_st = spdlog::daily_logger_mt("tracing_daily_st", "latency_logs/daily_st.log");
|
||||
benchmark::RegisterBenchmark("daily_st/backtrace", bench_logger, std::move(tracing_daily_st))->UseRealTime();
|
||||
spdlog::drop("tracing_daily_st");
|
||||
|
||||
// basic_mt
|
||||
auto basic_mt = spdlog::basic_logger_mt("basic_mt", "latency_logs/basic_mt.log", true);
|
||||
benchmark::RegisterBenchmark("basic_mt", bench_logger, std::move(basic_mt))->Threads(n_threads)->UseRealTime();
|
||||
spdlog::drop("basic_mt");
|
||||
//
|
||||
// Multi threaded bench, 10 loggers using same logger concurrently
|
||||
//
|
||||
auto null_logger_mt = std::make_shared<spdlog::logger>("bench", std::make_shared<null_sink_mt>());
|
||||
benchmark::RegisterBenchmark("null_sink_mt", bench_logger, null_logger_mt)->Threads(n_threads)->UseRealTime();
|
||||
|
||||
// rotating mt
|
||||
auto rotating_mt = spdlog::rotating_logger_mt("rotating_mt", "latency_logs/rotating_mt.log", file_size, rotating_files);
|
||||
benchmark::RegisterBenchmark("rotating_mt", bench_logger, std::move(rotating_mt))->Threads(n_threads)->UseRealTime();
|
||||
spdlog::drop("rotating_mt");
|
||||
// basic_mt
|
||||
auto basic_mt = spdlog::basic_logger_mt("basic_mt", "latency_logs/basic_mt.log", true);
|
||||
benchmark::RegisterBenchmark("basic_mt", bench_logger, std::move(basic_mt))->Threads(n_threads)->UseRealTime();
|
||||
spdlog::drop("basic_mt");
|
||||
|
||||
// daily mt
|
||||
auto daily_mt = spdlog::daily_logger_mt("daily_mt", "latency_logs/daily_mt.log");
|
||||
benchmark::RegisterBenchmark("daily_mt", bench_logger, std::move(daily_mt))->Threads(n_threads)->UseRealTime();
|
||||
spdlog::drop("daily_mt");
|
||||
// rotating mt
|
||||
auto rotating_mt = spdlog::rotating_logger_mt("rotating_mt", "latency_logs/rotating_mt.log", file_size, rotating_files);
|
||||
benchmark::RegisterBenchmark("rotating_mt", bench_logger, std::move(rotating_mt))->Threads(n_threads)->UseRealTime();
|
||||
spdlog::drop("rotating_mt");
|
||||
|
||||
// daily mt
|
||||
auto daily_mt = spdlog::daily_logger_mt("daily_mt", "latency_logs/daily_mt.log");
|
||||
benchmark::RegisterBenchmark("daily_mt", bench_logger, std::move(daily_mt))->Threads(n_threads)->UseRealTime();
|
||||
spdlog::drop("daily_mt");
|
||||
}
|
||||
|
||||
// async
|
||||
auto queue_size = 1024 * 1024 * 3;
|
||||
|
258
third_party/spdlog/cmake/pch.h.in
vendored
Normal file
258
third_party/spdlog/cmake/pch.h.in
vendored
Normal file
@ -0,0 +1,258 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
// details/pattern_formatter-inl.h
|
||||
// fmt/bin_to_hex.h
|
||||
// fmt/bundled/format-inl.h
|
||||
#include <cctype>
|
||||
|
||||
// details/file_helper-inl.h
|
||||
// details/os-inl.h
|
||||
// fmt/bundled/core.h
|
||||
// fmt/bundled/posix.h
|
||||
// logger-inl.h
|
||||
// sinks/daily_file_sink.h
|
||||
// sinks/stdout_sinks.h
|
||||
#include <cstdio>
|
||||
|
||||
// details/os-inl.h
|
||||
// fmt/bundled/posix.h
|
||||
#include <cstdlib>
|
||||
|
||||
// details/os-inl.h
|
||||
// details/pattern_formatter-inl.h
|
||||
// fmt/bundled/core.h
|
||||
// fmt/bundled/format-inl.h
|
||||
#include <cstring>
|
||||
|
||||
// details/os-inl.h
|
||||
// details/os.h
|
||||
// details/pattern_formatter-inl.h
|
||||
// details/pattern_formatter.h
|
||||
// fmt/bundled/chrono.h
|
||||
// sinks/daily_file_sink.h
|
||||
// sinks/rotating_file_sink-inl.h
|
||||
#include <ctime>
|
||||
|
||||
// fmt/bundled/format-inl.h
|
||||
#include <climits>
|
||||
|
||||
// fmt/bundled/format-inl.h
|
||||
#include <cwchar>
|
||||
|
||||
// fmt/bundled/format-inl.h
|
||||
// fmt/bundled/format.h
|
||||
#include <cmath>
|
||||
|
||||
// fmt/bundled/format-inl.h
|
||||
#include <cstdarg>
|
||||
|
||||
// details/file_helper-inl.h
|
||||
// fmt/bundled/format.h
|
||||
// fmt/bundled/posix.h
|
||||
// sinks/rotating_file_sink-inl.h
|
||||
#include <cerrno>
|
||||
|
||||
// details/circular_q.h
|
||||
// details/thread_pool-inl.h
|
||||
// fmt/bundled/format-inl.h
|
||||
#include <cassert>
|
||||
|
||||
// async_logger-inl.h
|
||||
// cfg/helpers-inl.h
|
||||
// log_levels.h
|
||||
// common.h
|
||||
// details/file_helper-inl.h
|
||||
// details/log_msg.h
|
||||
// details/os-inl.h
|
||||
// details/pattern_formatter-inl.h
|
||||
// details/pattern_formatter.h
|
||||
// details/registry-inl.h
|
||||
// details/registry.h
|
||||
// details/tcp_client-windows.h
|
||||
// details/tcp_client.h
|
||||
// fmt/bundled/core.h
|
||||
// sinks/android_sink.h
|
||||
// sinks/ansicolor_sink.h
|
||||
// sinks/basic_file_sink.h
|
||||
// sinks/daily_file_sink.h
|
||||
// sinks/dup_filter_sink.h
|
||||
// sinks/msvc_sink.h
|
||||
// sinks/ringbuffer_sink.h
|
||||
// sinks/rotating_file_sink-inl.h
|
||||
// sinks/rotating_file_sink.h
|
||||
// sinks/syslog_sink.h
|
||||
// sinks/tcp_sink.h
|
||||
// sinks/win_eventlog_sink.h
|
||||
// sinks/wincolor_sink.h
|
||||
// spdlog.h:
|
||||
#include <string>
|
||||
|
||||
// cfg/helpers-inl.h
|
||||
// fmt/bundled/chrono.h
|
||||
#include <sstream>
|
||||
|
||||
// fmt/bundled/ostream.h
|
||||
// sinks/ostream_sink.h
|
||||
#include <ostream>
|
||||
|
||||
// cfg/log_levels.h
|
||||
// details/registry-inl.h
|
||||
// details/registry.h
|
||||
#include <unordered_map>
|
||||
|
||||
// details/circular_q.h
|
||||
// details/pattern_formatter-inl.h
|
||||
// details/pattern_formatter.h
|
||||
// details/thread_pool.h
|
||||
// fmt/bundled/compile.h
|
||||
// logger.h
|
||||
// sinks/dist_sink.h
|
||||
// sinks/ringbuffer_sink.h
|
||||
// sinks/win_eventlog_sink.h
|
||||
#include <vector>
|
||||
|
||||
// details/os-inl.h
|
||||
// details/pattern_formatter-inl.h
|
||||
// sinks/ansicolor_sink.h
|
||||
// sinks/syslog_sink.h
|
||||
// sinks/systemd_sink.h
|
||||
// sinks/wincolor_sink.h
|
||||
#include <array>
|
||||
|
||||
// details/file_helper-inl.h
|
||||
// details/file_helper.h
|
||||
// sinks/rotating_file_sink-inl.h
|
||||
#include <tuple>
|
||||
|
||||
// details/os-inl.h
|
||||
// fmt/bundled/format.h
|
||||
// fmt/bundled/printf.h
|
||||
#include <limits>
|
||||
|
||||
// common.h
|
||||
// details/backtracer.h
|
||||
// details/null_mutex.h
|
||||
#include <atomic>
|
||||
|
||||
// common.h
|
||||
// details/backtracer.h
|
||||
// details/null_mutex.h
|
||||
#include <locale>
|
||||
|
||||
// common.h
|
||||
#include <initializer_list>
|
||||
|
||||
// common.h
|
||||
#include <exception>
|
||||
|
||||
// common.h
|
||||
// details/fmt_helper.h
|
||||
// fmt/bundled/core.h
|
||||
// fmt/bundled/ranges.h
|
||||
#include <type_traits>
|
||||
|
||||
// cfg/helpers-inl.h
|
||||
// details/null_mutex.h
|
||||
// details/pattern_formatter-inl.h
|
||||
#include <utility>
|
||||
|
||||
// async.h
|
||||
// async_logger-inl.h
|
||||
// common.h
|
||||
// details/pattern_formatter-inl.h
|
||||
// details/pattern_formatter.h
|
||||
// details/registry-inl.h
|
||||
// details/registry.h
|
||||
// details/thread_pool.h
|
||||
// fmt/bundled/format.h
|
||||
// sinks/ansicolor_sink.h
|
||||
// sinks/base_sink-inl.h
|
||||
// sinks/dist_sink.h
|
||||
// sinks/stdout_sinks-inl.h
|
||||
// sinks/wincolor_sink.h
|
||||
// spdlog.h
|
||||
#include <memory>
|
||||
|
||||
// async.h
|
||||
// common.h
|
||||
// details/backtracer.h
|
||||
// details/periodic_worker.h
|
||||
// details/registry-inl.h
|
||||
// details/registry.h
|
||||
// details/thread_pool.h
|
||||
// sinks/tcp_sink.h
|
||||
// spdlog.h
|
||||
#include <functional>
|
||||
|
||||
// details/mpmc_blocking_q.h
|
||||
// details/periodic_worker.h
|
||||
#include <condition_variable>
|
||||
|
||||
// details/os-inl.h
|
||||
// fmt/bundled/format.h
|
||||
// fmt/bundled/printf.h
|
||||
// sinks/dist_sink.h
|
||||
#include <algorithm>
|
||||
|
||||
// common.h
|
||||
// details/file_helper-inl.h
|
||||
// details/fmt_helper.h
|
||||
// details/os-inl.h
|
||||
// details/pattern_formatter-inl.h
|
||||
// details/pattern_formatter.h
|
||||
// details/periodic_worker.h
|
||||
// details/registry-inl.h
|
||||
// details/registry.h
|
||||
// details/thread_pool.h
|
||||
// fmt/bundled/chrono.h
|
||||
// sinks/android_sink.h
|
||||
// sinks/daily_file_sink.h
|
||||
// sinks/dup_filter_sink.h
|
||||
// sinks/rotating_file_sink-inl.h
|
||||
// sinks/rotating_file_sink.h
|
||||
// sinks/tcp_sink.h
|
||||
// spdlog.h
|
||||
#include <chrono>
|
||||
|
||||
// details/file_helper-inl.h
|
||||
// details/os-inl.h
|
||||
// details/pattern_formatter-inl.h
|
||||
// details/periodic_worker.h
|
||||
// details/thread_pool.h
|
||||
// sinks/android_sink.h
|
||||
#include <thread>
|
||||
|
||||
// async.h
|
||||
// details/backtracer.h
|
||||
// details/console_globals.h
|
||||
// details/mpmc_blocking_q.h
|
||||
// details/pattern_formatter-inl.h
|
||||
// details/periodic_worker.h
|
||||
// details/registry.h
|
||||
// sinks/android_sink.h
|
||||
// sinks/ansicolor_sink.h
|
||||
// sinks/basic_file_sink.h
|
||||
// sinks/daily_file_sink.h
|
||||
// sinks/dist_sink.h
|
||||
// sinks/dup_filter_sink.h
|
||||
// sinks/msvc_sink.h
|
||||
// sinks/null_sink.h
|
||||
// sinks/ostream_sink.h
|
||||
// sinks/ringbuffer_sink.h
|
||||
// sinks/rotating_file_sink-inl.h
|
||||
// sinks/rotating_file_sink.h
|
||||
// sinks/tcp_sink.h
|
||||
// sinks/win_eventlog_sink.h
|
||||
// sinks/wincolor_sink.h
|
||||
//
|
||||
// color_sinks.cpp
|
||||
// file_sinks.cpp
|
||||
// spdlog.cpp
|
||||
// stdout_sinks.cpp
|
||||
#include <mutex>
|
||||
|
||||
// spdlog
|
||||
#include <spdlog/common.h>
|
30
third_party/spdlog/cmake/spdlogCPack.cmake
vendored
30
third_party/spdlog/cmake/spdlogCPack.cmake
vendored
@ -1,7 +1,4 @@
|
||||
set(CPACK_GENERATOR
|
||||
TGZ
|
||||
ZIP
|
||||
)
|
||||
set(CPACK_GENERATOR "TGZ;ZIP" CACHE STRING "Semicolon separated list of generators")
|
||||
|
||||
set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY 0)
|
||||
set(CPACK_INSTALL_CMAKE_PROJECTS
|
||||
@ -22,11 +19,32 @@ set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PR
|
||||
if (PROJECT_VERSION_TWEAK)
|
||||
set(CPACK_PACKAGE_VERSION ${CPACK_PACKAGE_VERSION}.${PROJECT_VERSION_TWEAK})
|
||||
endif ()
|
||||
set(CPACK_PACKAGE_RELOCATABLE ON)
|
||||
set(CPACK_PACKAGE_RELOCATABLE ON CACHE BOOL "Build relocatable package")
|
||||
|
||||
set(CPACK_RPM_PACKAGE_LICENSE "MIT")
|
||||
set(CPACK_RPM_PACKAGE_GROUP "System Environment/Libraries")
|
||||
set(CPACK_RPM_PACKAGE_GROUP "Development/Libraries")
|
||||
set(CPACK_RPM_PACKAGE_URL ${CPACK_PROJECT_URL})
|
||||
set(CPACK_RPM_PACKAGE_DESCRIPTION "Very fast, header-only/compiled, C++ logging library.")
|
||||
|
||||
if (CPACK_PACKAGE_NAME)
|
||||
set(CPACK_RPM_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}")
|
||||
else()
|
||||
set(CPACK_RPM_FILE_NAME "${PROJECT_NAME}-${CPACK_PACKAGE_VERSION}")
|
||||
endif()
|
||||
|
||||
if (CPACK_RPM_PACKAGE_RELEASE)
|
||||
set(CPACK_RPM_FILE_NAME "${CPACK_RPM_FILE_NAME}-${CPACK_RPM_PACKAGE_RELEASE}")
|
||||
endif ()
|
||||
|
||||
if (CPACK_RPM_PACKAGE_ARCHITECTURE)
|
||||
set(CPACK_RPM_FILE_NAME "${CPACK_RPM_FILE_NAME}.${CPACK_RPM_PACKAGE_ARCHITECTURE}")
|
||||
endif ()
|
||||
set(CPACK_RPM_FILE_NAME "${CPACK_RPM_FILE_NAME}.rpm")
|
||||
|
||||
if (NOT CPACK_PACKAGE_RELOCATABLE)
|
||||
# Depend on pkgconfig rpm to create the system pkgconfig folder
|
||||
set(CPACK_RPM_PACKAGE_REQUIRES pkgconfig)
|
||||
set(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION "${CPACK_PACKAGING_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/pkgconfig")
|
||||
endif ()
|
||||
|
||||
include(CPack)
|
||||
|
23
third_party/spdlog/cmake/utils.cmake
vendored
23
third_party/spdlog/cmake/utils.cmake
vendored
@ -20,19 +20,28 @@ function(spdlog_extract_version)
|
||||
set(ver_patch ${CMAKE_MATCH_1})
|
||||
|
||||
set(SPDLOG_VERSION_MAJOR ${ver_major} PARENT_SCOPE)
|
||||
set(SPDLOG_VERSION_MINOR ${ver_minor} PARENT_SCOPE)
|
||||
set(SPDLOG_VERSION_PATCH ${ver_patch} PARENT_SCOPE)
|
||||
set (SPDLOG_VERSION "${ver_major}.${ver_minor}.${ver_patch}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
|
||||
# Turn on warnings on the given target
|
||||
function(spdlog_enable_warnings target_name)
|
||||
target_compile_options(${target_name} PRIVATE
|
||||
$<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:AppleClang>,$<CXX_COMPILER_ID:GNU>>:
|
||||
-Wall -Wextra -Wconversion -pedantic -Wfatal-errors>
|
||||
$<$<CXX_COMPILER_ID:MSVC>:/W4>)
|
||||
if(MSVC_VERSION GREATER_EQUAL 1910) #Allow non fatal security wanrnings for msvc 2015
|
||||
target_compile_options(${target_name} PRIVATE /WX)
|
||||
endif()
|
||||
if(SPDLOG_BUILD_WARNINGS)
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
list(APPEND MSVC_OPTIONS "/W3")
|
||||
if(MSVC_VERSION GREATER 1900) #Allow non fatal security wanrnings for msvc 2015
|
||||
list(APPEND MSVC_OPTIONS "/WX")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
target_compile_options(${target_name} PRIVATE
|
||||
$<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:AppleClang>,$<CXX_COMPILER_ID:GNU>>:
|
||||
-Wall -Wextra -Wconversion -pedantic -Wfatal-errors>
|
||||
$<$<CXX_COMPILER_ID:MSVC>:${MSVC_OPTIONS}>)
|
||||
endif()
|
||||
|
||||
endfunction()
|
||||
|
||||
|
||||
|
42
third_party/spdlog/cmake/version.rc.in
vendored
Normal file
42
third_party/spdlog/cmake/version.rc.in
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
#define APSTUDIO_READONLY_SYMBOLS
|
||||
#include "winres.h"
|
||||
#undef APSTUDIO_READONLY_SYMBOLS
|
||||
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION @SPDLOG_VERSION_MAJOR@,@SPDLOG_VERSION_MINOR@,@SPDLOG_VERSION_PATCH@,0
|
||||
PRODUCTVERSION @SPDLOG_VERSION_MAJOR@,@SPDLOG_VERSION_MINOR@,@SPDLOG_VERSION_PATCH@,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS 0x40004L
|
||||
FILETYPE 0x2L
|
||||
FILESUBTYPE 0x0L
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904b0"
|
||||
BEGIN
|
||||
VALUE "FileDescription", "spdlog dll\0"
|
||||
VALUE "FileVersion", "@SPDLOG_VERSION@.0\0"
|
||||
VALUE "InternalName", "spdlog.dll\0"
|
||||
VALUE "LegalCopyright", "Copyright (C) spdlog\0"
|
||||
VALUE "ProductName", "spdlog\0"
|
||||
VALUE "ProductVersion", "@SPDLOG_VERSION@.0\0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200
|
||||
END
|
||||
END
|
||||
|
||||
|
||||
|
||||
|
||||
|
6
third_party/spdlog/example/CMakeLists.txt
vendored
6
third_party/spdlog/example/CMakeLists.txt
vendored
@ -1,7 +1,7 @@
|
||||
# Copyright(c) 2019 spdlog authors
|
||||
# Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
cmake_minimum_required(VERSION 3.1)
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
project(spdlog_examples CXX)
|
||||
|
||||
if(NOT TARGET spdlog)
|
||||
@ -13,15 +13,13 @@ endif()
|
||||
# Example of using pre-compiled library
|
||||
#---------------------------------------------------------------------------------------
|
||||
add_executable(example example.cpp)
|
||||
spdlog_enable_warnings(example)
|
||||
target_link_libraries(example PRIVATE spdlog::spdlog)
|
||||
|
||||
#---------------------------------------------------------------------------------------
|
||||
# Example of using header-only library
|
||||
#---------------------------------------------------------------------------------------
|
||||
if(SPDLOG_BUILD_EXAMPLE_HO)
|
||||
add_executable(example_header_only example.cpp)
|
||||
spdlog_enable_warnings(example_header_only)
|
||||
add_executable(example_header_only example.cpp)
|
||||
target_link_libraries(example_header_only PRIVATE spdlog::spdlog_header_only)
|
||||
endif()
|
||||
|
||||
|
50
third_party/spdlog/example/example.cpp
vendored
50
third_party/spdlog/example/example.cpp
vendored
@ -6,6 +6,7 @@
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
void load_levels_example();
|
||||
void stdout_logger_example();
|
||||
void basic_example();
|
||||
void rotating_example();
|
||||
@ -17,12 +18,18 @@ void multi_sink_example();
|
||||
void user_defined_example();
|
||||
void err_handler_example();
|
||||
void syslog_example();
|
||||
void custom_flags_example();
|
||||
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "spdlog/cfg/env.h" // for loading levels from the environment variable
|
||||
|
||||
int main(int, char *[])
|
||||
{
|
||||
// Log levels can be loaded from argv/env using "SPDLOG_LEVEL"
|
||||
load_levels_example();
|
||||
|
||||
spdlog::info("Welcome to spdlog version {}.{}.{} !", SPDLOG_VER_MAJOR, SPDLOG_VER_MINOR, SPDLOG_VER_PATCH);
|
||||
|
||||
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);
|
||||
@ -64,6 +71,7 @@ int main(int, char *[])
|
||||
user_defined_example();
|
||||
err_handler_example();
|
||||
trace_example();
|
||||
custom_flags_example();
|
||||
|
||||
// Flush all *registered* loggers using a worker thread every 3 seconds.
|
||||
// note: registered loggers *must* be thread safe for this to work correctly!
|
||||
@ -116,6 +124,18 @@ void daily_example()
|
||||
auto daily_logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
|
||||
}
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
#include "spdlog/async.h"
|
||||
void async_example()
|
||||
{
|
||||
@ -154,6 +174,8 @@ void binary_example()
|
||||
// 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->info("hexdump style: {:a}", spdlog::to_hex(buf));
|
||||
// logger->info("hexdump style, 20 chars per line {:a}", spdlog::to_hex(buf, 20));
|
||||
}
|
||||
|
||||
// Compile time log levels.
|
||||
@ -230,5 +252,31 @@ void android_example()
|
||||
auto android_logger = spdlog::android_logger_mt("android", tag);
|
||||
android_logger->critical("Use \"adb shell logcat\" to view this message.");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Log patterns can contain custom flags.
|
||||
// this will add custom 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()
|
||||
{
|
||||
|
||||
using spdlog::details::make_unique; // for pre c++14
|
||||
auto formatter = make_unique<spdlog::pattern_formatter>();
|
||||
formatter->add_flag<my_formatter_flag>('*').set_pattern("[%n] [%*] [%^%l%$] %v");
|
||||
spdlog::set_formatter(std::move(formatter));
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ SPDLOG_INLINE void spdlog::async_logger::sink_it_(const details::log_msg &msg)
|
||||
}
|
||||
else
|
||||
{
|
||||
SPDLOG_THROW(spdlog_ex("async log: thread pool doesn't exist anymore"));
|
||||
throw_spdlog_ex("async log: thread pool doesn't exist anymore");
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,7 +45,7 @@ SPDLOG_INLINE void spdlog::async_logger::flush_()
|
||||
}
|
||||
else
|
||||
{
|
||||
SPDLOG_THROW(spdlog_ex("async flush: thread pool doesn't exist anymore"));
|
||||
throw_spdlog_ex("async flush: thread pool doesn't exist anymore");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@ namespace details {
|
||||
class thread_pool;
|
||||
}
|
||||
|
||||
class async_logger final : public std::enable_shared_from_this<async_logger>, public logger
|
||||
class SPDLOG_API async_logger final : public std::enable_shared_from_this<async_logger>, public logger
|
||||
{
|
||||
friend class details::thread_pool;
|
||||
|
||||
|
45
third_party/spdlog/include/spdlog/cfg/argv.h
vendored
Normal file
45
third_party/spdlog/include/spdlog/cfg/argv.h
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
#include <spdlog/cfg/helpers.h>
|
||||
#include <spdlog/details/registry.h>
|
||||
|
||||
//
|
||||
// Init log levels using each argv entry that starts with "SPDLOG_LEVEL="
|
||||
//
|
||||
// set all loggers to debug level:
|
||||
// example.exe "SPDLOG_LEVEL=debug"
|
||||
|
||||
// set logger1 to trace level
|
||||
// example.exe "SPDLOG_LEVEL=logger1=trace"
|
||||
|
||||
// turn off all logging except for logger1 and logger2:
|
||||
// example.exe "SPDLOG_LEVEL=off,logger1=debug,logger2=info"
|
||||
|
||||
namespace spdlog {
|
||||
namespace cfg {
|
||||
|
||||
// search for SPDLOG_LEVEL= in the args and use it to init the levels
|
||||
void load_argv_levels(int argc, const char **argv)
|
||||
{
|
||||
const std::string spdlog_level_prefix = "SPDLOG_LEVEL=";
|
||||
for (int i = 1; i < argc; i++)
|
||||
{
|
||||
std::string arg = argv[i];
|
||||
if (arg.find(spdlog_level_prefix) == 0)
|
||||
{
|
||||
auto levels_string = arg.substr(spdlog_level_prefix.size());
|
||||
auto levels = helpers::extract_levels(levels_string);
|
||||
details::registry::instance().update_levels(std::move(levels));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void load_argv_levels(int argc, char **argv)
|
||||
{
|
||||
load_argv_levels(argc, const_cast<const char **>(argv));
|
||||
}
|
||||
|
||||
} // namespace cfg
|
||||
} // namespace spdlog
|
36
third_party/spdlog/include/spdlog/cfg/env.h
vendored
Normal file
36
third_party/spdlog/include/spdlog/cfg/env.h
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
#include <spdlog/cfg/helpers.h>
|
||||
#include <spdlog/details/registry.h>
|
||||
#include <spdlog/details/os.h>
|
||||
|
||||
//
|
||||
// Init levels and patterns from env variables SPDLOG_LEVEL
|
||||
// Inspired from Rust's "env_logger" crate (https://crates.io/crates/env_logger).
|
||||
// Note - fallback to "info" level on unrecognized levels
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// set global level to debug:
|
||||
// export SPDLOG_LEVEL=debug
|
||||
//
|
||||
// turn off all logging except for logger1:
|
||||
// export SPDLOG_LEVEL="off,logger1=debug"
|
||||
//
|
||||
|
||||
// turn off all logging except for logger1 and logger2:
|
||||
// export SPDLOG_LEVEL="off,logger1=debug,logger2=info"
|
||||
|
||||
namespace spdlog {
|
||||
namespace cfg {
|
||||
void load_env_levels()
|
||||
{
|
||||
auto env_val = details::os::getenv("SPDLOG_LEVEL");
|
||||
auto levels = helpers::extract_levels(env_val);
|
||||
details::registry::instance().update_levels(std::move(levels));
|
||||
}
|
||||
|
||||
} // namespace cfg
|
||||
} // namespace spdlog
|
103
third_party/spdlog/include/spdlog/cfg/helpers-inl.h
vendored
Normal file
103
third_party/spdlog/include/spdlog/cfg/helpers-inl.h
vendored
Normal file
@ -0,0 +1,103 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef SPDLOG_HEADER_ONLY
|
||||
#include <spdlog/cfg/helpers.h>
|
||||
#endif
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <spdlog/details/os.h>
|
||||
#include <spdlog/details/registry.h>
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <sstream>
|
||||
|
||||
namespace spdlog {
|
||||
namespace cfg {
|
||||
namespace helpers {
|
||||
|
||||
// inplace convert to lowercase
|
||||
inline std::string &to_lower_(std::string &str)
|
||||
{
|
||||
std::transform(
|
||||
str.begin(), str.end(), str.begin(), [](char ch) { return static_cast<char>((ch >= 'A' && ch <= 'Z') ? ch + ('a' - 'A') : ch); });
|
||||
return str;
|
||||
}
|
||||
|
||||
// inplace trim spaces
|
||||
inline std::string &trim_(std::string &str)
|
||||
{
|
||||
const char *spaces = " \n\r\t";
|
||||
str.erase(str.find_last_not_of(spaces) + 1);
|
||||
str.erase(0, str.find_first_not_of(spaces));
|
||||
return str;
|
||||
}
|
||||
|
||||
// return (name,value) trimmed pair from given "name=value" string.
|
||||
// return empty string on missing parts
|
||||
// "key=val" => ("key", "val")
|
||||
// " key = val " => ("key", "val")
|
||||
// "key=" => ("key", "")
|
||||
// "val" => ("", "val")
|
||||
|
||||
inline std::pair<std::string, std::string> extract_kv_(char sep, const std::string &str)
|
||||
{
|
||||
auto n = str.find(sep);
|
||||
std::string k, v;
|
||||
if (n == std::string::npos)
|
||||
{
|
||||
v = str;
|
||||
}
|
||||
else
|
||||
{
|
||||
k = str.substr(0, n);
|
||||
v = str.substr(n + 1);
|
||||
}
|
||||
return std::make_pair(trim_(k), trim_(v));
|
||||
}
|
||||
|
||||
// return vector of key/value pairs from sequence of "K1=V1,K2=V2,.."
|
||||
// "a=AAA,b=BBB,c=CCC,.." => {("a","AAA"),("b","BBB"),("c", "CCC"),...}
|
||||
inline std::unordered_map<std::string, std::string> extract_key_vals_(const std::string &str)
|
||||
{
|
||||
std::string token;
|
||||
std::istringstream token_stream(str);
|
||||
std::unordered_map<std::string, std::string> rv{};
|
||||
while (std::getline(token_stream, token, ','))
|
||||
{
|
||||
if (token.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
auto kv = extract_kv_('=', token);
|
||||
rv[kv.first] = kv.second;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
SPDLOG_INLINE log_levels extract_levels(const std::string &input)
|
||||
{
|
||||
auto key_vals = extract_key_vals_(input);
|
||||
log_levels rv;
|
||||
|
||||
for (auto &name_level : key_vals)
|
||||
{
|
||||
auto &logger_name = name_level.first;
|
||||
auto level_name = to_lower_(name_level.second);
|
||||
auto level = level::from_str(level_name);
|
||||
// fallback to "info" if unrecognized level name
|
||||
if (level == level::off && level_name != "off")
|
||||
{
|
||||
level = level::info;
|
||||
}
|
||||
rv.set(logger_name, level);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace cfg
|
||||
} // namespace spdlog
|
28
third_party/spdlog/include/spdlog/cfg/helpers.h
vendored
Normal file
28
third_party/spdlog/include/spdlog/cfg/helpers.h
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spdlog/cfg/log_levels.h>
|
||||
|
||||
namespace spdlog {
|
||||
namespace cfg {
|
||||
namespace helpers {
|
||||
//
|
||||
// Init levels from given string
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// set global level to debug: "debug"
|
||||
// turn off all logging except for logger1: "off,logger1=debug"
|
||||
// turn off all logging except for logger1 and logger2: "off,logger1=debug,logger2=info"
|
||||
//
|
||||
SPDLOG_API log_levels extract_levels(const std::string &txt);
|
||||
} // namespace helpers
|
||||
|
||||
} // namespace cfg
|
||||
} // namespace spdlog
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
#include "helpers-inl.h"
|
||||
#endif // SPDLOG_HEADER_ONLY
|
47
third_party/spdlog/include/spdlog/cfg/log_levels.h
vendored
Normal file
47
third_party/spdlog/include/spdlog/cfg/log_levels.h
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spdlog/common.h>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace spdlog {
|
||||
namespace cfg {
|
||||
class log_levels
|
||||
{
|
||||
std::unordered_map<std::string, spdlog::level::level_enum> levels_;
|
||||
spdlog::level::level_enum default_level_ = level::info;
|
||||
|
||||
public:
|
||||
void set(const std::string &logger_name, level::level_enum lvl)
|
||||
{
|
||||
if (logger_name.empty())
|
||||
{
|
||||
default_level_ = lvl;
|
||||
}
|
||||
else
|
||||
{
|
||||
levels_[logger_name] = lvl;
|
||||
}
|
||||
}
|
||||
|
||||
void set_default(level::level_enum lvl)
|
||||
{
|
||||
default_level_ = lvl;
|
||||
}
|
||||
|
||||
level::level_enum get(const std::string &logger_name)
|
||||
{
|
||||
auto it = levels_.find(logger_name);
|
||||
return it != levels_.end() ? it->second : default_level_;
|
||||
}
|
||||
|
||||
level::level_enum default_level()
|
||||
{
|
||||
return default_level_;
|
||||
}
|
||||
};
|
||||
} // namespace cfg
|
||||
} // namespace spdlog
|
19
third_party/spdlog/include/spdlog/common-inl.h
vendored
19
third_party/spdlog/include/spdlog/common-inl.h
vendored
@ -34,6 +34,15 @@ SPDLOG_INLINE spdlog::level::level_enum from_str(const std::string &name) SPDLOG
|
||||
}
|
||||
level++;
|
||||
}
|
||||
// check also for "warn" and "err" before giving up..
|
||||
if (name == "warn")
|
||||
{
|
||||
return level::warn;
|
||||
}
|
||||
if (name == "err")
|
||||
{
|
||||
return level::err;
|
||||
}
|
||||
return level::off;
|
||||
}
|
||||
} // namespace level
|
||||
@ -54,4 +63,14 @@ SPDLOG_INLINE const char *spdlog_ex::what() const SPDLOG_NOEXCEPT
|
||||
return msg_.c_str();
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void throw_spdlog_ex(const std::string &msg, int last_errno)
|
||||
{
|
||||
SPDLOG_THROW(spdlog_ex(msg, last_errno));
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void throw_spdlog_ex(std::string msg)
|
||||
{
|
||||
SPDLOG_THROW(spdlog_ex(std::move(msg)));
|
||||
}
|
||||
|
||||
} // namespace spdlog
|
||||
|
35
third_party/spdlog/include/spdlog/common.h
vendored
35
third_party/spdlog/include/spdlog/common.h
vendored
@ -15,22 +15,20 @@
|
||||
#include <type_traits>
|
||||
#include <functional>
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX // prevent windows redefining min/max
|
||||
#endif
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
#endif //_WIN32
|
||||
|
||||
#ifdef SPDLOG_COMPILED_LIB
|
||||
#undef SPDLOG_HEADER_ONLY
|
||||
#if defined(_WIN32) && defined(SPDLOG_SHARED_LIB)
|
||||
#ifdef spdlog_EXPORTS
|
||||
#define SPDLOG_API __declspec(dllexport)
|
||||
#else
|
||||
#define SPDLOG_API __declspec(dllimport)
|
||||
#endif
|
||||
#else
|
||||
#define SPDLOG_API
|
||||
#endif
|
||||
#define SPDLOG_INLINE
|
||||
#else
|
||||
#define SPDLOG_API
|
||||
#define SPDLOG_HEADER_ONLY
|
||||
#define SPDLOG_INLINE inline
|
||||
#endif
|
||||
@ -147,6 +145,7 @@ enum level_enum
|
||||
err = SPDLOG_LEVEL_ERROR,
|
||||
critical = SPDLOG_LEVEL_CRITICAL,
|
||||
off = SPDLOG_LEVEL_OFF,
|
||||
n_levels
|
||||
};
|
||||
|
||||
#if !defined(SPDLOG_LEVEL_NAMES)
|
||||
@ -164,9 +163,9 @@ enum level_enum
|
||||
}
|
||||
#endif
|
||||
|
||||
string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
|
||||
const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
|
||||
spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT;
|
||||
SPDLOG_API string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
|
||||
SPDLOG_API const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
|
||||
SPDLOG_API spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT;
|
||||
|
||||
using level_hasher = std::hash<int>;
|
||||
} // namespace level
|
||||
@ -194,7 +193,7 @@ enum class pattern_time_type
|
||||
//
|
||||
// Log exception
|
||||
//
|
||||
class spdlog_ex : public std::exception
|
||||
class SPDLOG_API spdlog_ex : public std::exception
|
||||
{
|
||||
public:
|
||||
explicit spdlog_ex(std::string msg);
|
||||
@ -205,6 +204,9 @@ private:
|
||||
std::string msg_;
|
||||
};
|
||||
|
||||
void throw_spdlog_ex(const std::string &msg, int last_errno);
|
||||
void throw_spdlog_ex(std::string msg);
|
||||
|
||||
struct source_loc
|
||||
{
|
||||
SPDLOG_CONSTEXPR source_loc() = default;
|
||||
@ -237,7 +239,6 @@ std::unique_ptr<T> make_unique(Args &&... args)
|
||||
}
|
||||
#endif
|
||||
} // namespace details
|
||||
|
||||
} // namespace spdlog
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
|
@ -15,7 +15,7 @@
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
class backtracer
|
||||
class SPDLOG_API backtracer
|
||||
{
|
||||
mutable std::mutex mutex_;
|
||||
std::atomic<bool> enabled_{false};
|
||||
|
@ -43,14 +43,14 @@ SPDLOG_INLINE void file_helper::open(const filename_t &fname, bool truncate)
|
||||
details::os::sleep_for_millis(open_interval_);
|
||||
}
|
||||
|
||||
SPDLOG_THROW(spdlog_ex("Failed opening file " + os::filename_to_str(filename_) + " for writing", errno));
|
||||
throw_spdlog_ex("Failed opening file " + os::filename_to_str(filename_) + " for writing", errno);
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void file_helper::reopen(bool truncate)
|
||||
{
|
||||
if (filename_.empty())
|
||||
{
|
||||
SPDLOG_THROW(spdlog_ex("Failed re opening file - was not opened before"));
|
||||
throw_spdlog_ex("Failed re opening file - was not opened before");
|
||||
}
|
||||
this->open(filename_, truncate);
|
||||
}
|
||||
@ -75,7 +75,7 @@ SPDLOG_INLINE void file_helper::write(const memory_buf_t &buf)
|
||||
auto data = buf.data();
|
||||
if (std::fwrite(data, 1, msg_size, fd_) != msg_size)
|
||||
{
|
||||
SPDLOG_THROW(spdlog_ex("Failed writing to file " + os::filename_to_str(filename_), errno));
|
||||
throw_spdlog_ex("Failed writing to file " + os::filename_to_str(filename_), errno);
|
||||
}
|
||||
}
|
||||
|
||||
@ -83,7 +83,7 @@ SPDLOG_INLINE size_t file_helper::size() const
|
||||
{
|
||||
if (fd_ == nullptr)
|
||||
{
|
||||
SPDLOG_THROW(spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(filename_)));
|
||||
throw_spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(filename_));
|
||||
}
|
||||
return os::filesize(fd_);
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ namespace details {
|
||||
// When failing to open a file, retry several times(5) with a delay interval(10 ms).
|
||||
// Throw spdlog_ex exception on errors.
|
||||
|
||||
class file_helper
|
||||
class SPDLOG_API file_helper
|
||||
{
|
||||
public:
|
||||
explicit file_helper() = default;
|
||||
|
@ -20,10 +20,7 @@ inline spdlog::string_view_t to_string_view(const memory_buf_t &buf) SPDLOG_NOEX
|
||||
inline void append_string_view(spdlog::string_view_t view, memory_buf_t &dest)
|
||||
{
|
||||
auto *buf_ptr = view.data();
|
||||
if (buf_ptr != nullptr)
|
||||
{
|
||||
dest.append(buf_ptr, buf_ptr + view.size());
|
||||
}
|
||||
dest.append(buf_ptr, buf_ptr + view.size());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@ -34,10 +31,10 @@ inline void append_int(T n, memory_buf_t &dest)
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline unsigned count_digits(T n)
|
||||
inline unsigned int count_digits(T n)
|
||||
{
|
||||
using count_type = typename std::conditional<(sizeof(T) > sizeof(uint32_t)), uint64_t, uint32_t>::type;
|
||||
return static_cast<unsigned>(fmt::internal::count_digits(static_cast<count_type>(n)));
|
||||
return static_cast<unsigned int>(fmt::internal::count_digits(static_cast<count_type>(n)));
|
||||
}
|
||||
|
||||
inline void pad2(int n, memory_buf_t &dest)
|
||||
@ -66,11 +63,9 @@ template<typename T>
|
||||
inline void pad_uint(T n, unsigned int width, memory_buf_t &dest)
|
||||
{
|
||||
static_assert(std::is_unsigned<T>::value, "pad_uint must get unsigned T");
|
||||
auto digits = count_digits(n);
|
||||
if (width > digits)
|
||||
for (auto digits = count_digits(n); digits < width; digits++)
|
||||
{
|
||||
const char *zeroes = "0000000000000000000";
|
||||
dest.append(zeroes, zeroes + width - digits);
|
||||
dest.push_back('0');
|
||||
}
|
||||
append_int(n, dest);
|
||||
}
|
||||
@ -78,7 +73,18 @@ inline void pad_uint(T n, unsigned int width, memory_buf_t &dest)
|
||||
template<typename T>
|
||||
inline void pad3(T n, memory_buf_t &dest)
|
||||
{
|
||||
pad_uint(n, 3, dest);
|
||||
static_assert(std::is_unsigned<T>::value, "pad3 must get unsigned T");
|
||||
if(n < 1000)
|
||||
{
|
||||
dest.push_back(static_cast<char>(n / 100 + '0'));
|
||||
n = n % 100;
|
||||
dest.push_back(static_cast<char>((n / 10) + '0'));
|
||||
dest.push_back(static_cast<char>((n % 10) + '0'));
|
||||
}
|
||||
else
|
||||
{
|
||||
append_int(n, dest);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
struct log_msg
|
||||
struct SPDLOG_API log_msg
|
||||
{
|
||||
log_msg() = default;
|
||||
log_msg(source_loc loc, string_view_t logger_name, level::level_enum lvl, string_view_t msg);
|
||||
|
@ -11,7 +11,7 @@ namespace details {
|
||||
// Extend log_msg with internal buffer to store its payload.
|
||||
// THis is needed since log_msg holds string_views that points to stack data.
|
||||
|
||||
class log_msg_buffer : public log_msg
|
||||
class SPDLOG_API log_msg_buffer : public log_msg
|
||||
{
|
||||
memory_buf_t buffer;
|
||||
void update_string_views();
|
||||
|
@ -23,16 +23,9 @@
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX // prevent windows redefining min/max
|
||||
#endif
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <io.h> // _get_osfhandle and _isatty support
|
||||
#include <process.h> // _get_pid support
|
||||
#include <windows.h>
|
||||
#include <spdlog/details/windows_include.h>
|
||||
|
||||
#ifdef __MINGW32__
|
||||
#include <share.h>
|
||||
@ -126,23 +119,6 @@ SPDLOG_INLINE std::tm gmtime() SPDLOG_NOEXCEPT
|
||||
return gmtime(now_t);
|
||||
}
|
||||
|
||||
#ifdef SPDLOG_PREVENT_CHILD_FD
|
||||
SPDLOG_INLINE void prevent_child_fd(FILE *f)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
auto file_handle = reinterpret_cast<HANDLE>(_get_osfhandle(::_fileno(f)));
|
||||
if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0))
|
||||
SPDLOG_THROW(spdlog_ex("SetHandleInformation failed", errno));
|
||||
#else
|
||||
auto fd = ::fileno(f);
|
||||
if (::fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
|
||||
{
|
||||
SPDLOG_THROW(spdlog_ex("fcntl with FD_CLOEXEC failed", errno));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif // SPDLOG_PREVENT_CHILD_FD
|
||||
|
||||
// fopen_s on non windows for writing
|
||||
SPDLOG_INLINE bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode)
|
||||
{
|
||||
@ -152,17 +128,35 @@ SPDLOG_INLINE bool fopen_s(FILE **fp, const filename_t &filename, const filename
|
||||
#else
|
||||
*fp = ::_fsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
|
||||
#endif
|
||||
#else // unix
|
||||
*fp = ::fopen((filename.c_str()), mode.c_str());
|
||||
#endif
|
||||
|
||||
#ifdef SPDLOG_PREVENT_CHILD_FD
|
||||
// prevent child processes from inheriting log file descriptors
|
||||
#if defined(SPDLOG_PREVENT_CHILD_FD)
|
||||
if (*fp != nullptr)
|
||||
{
|
||||
prevent_child_fd(*fp);
|
||||
auto file_handle = reinterpret_cast<HANDLE>(_get_osfhandle(::_fileno(*fp)));
|
||||
if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0))
|
||||
{
|
||||
::fclose(*fp);
|
||||
*fp = nullptr;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#else // unix
|
||||
#if defined(SPDLOG_PREVENT_CHILD_FD)
|
||||
const int mode_flag = mode == SPDLOG_FILENAME_T("ab") ? O_APPEND : O_TRUNC;
|
||||
const int fd = ::open((filename.c_str()), O_CREAT | O_WRONLY | O_CLOEXEC | mode_flag, mode_t(0644));
|
||||
if (fd == -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
*fp = ::fdopen(fd, mode.c_str());
|
||||
if (*fp == nullptr)
|
||||
{
|
||||
::close(fd);
|
||||
}
|
||||
#else
|
||||
*fp = ::fopen((filename.c_str()), mode.c_str());
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return *fp == nullptr;
|
||||
}
|
||||
|
||||
@ -210,7 +204,7 @@ SPDLOG_INLINE size_t filesize(FILE *f)
|
||||
{
|
||||
if (f == nullptr)
|
||||
{
|
||||
SPDLOG_THROW(spdlog_ex("Failed getting file size. fd is null"));
|
||||
throw_spdlog_ex("Failed getting file size. fd is null");
|
||||
}
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
int fd = ::_fileno(f);
|
||||
@ -251,7 +245,8 @@ SPDLOG_INLINE size_t filesize(FILE *f)
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
SPDLOG_THROW(spdlog_ex("Failed getting file size from fd", errno));
|
||||
throw_spdlog_ex("Failed getting file size from fd", errno);
|
||||
return 0; // will not be reached.
|
||||
}
|
||||
|
||||
// Return utc offset in minutes or throw spdlog_ex on failure
|
||||
@ -267,7 +262,7 @@ SPDLOG_INLINE int utc_minutes_offset(const std::tm &tm)
|
||||
auto rv = ::GetDynamicTimeZoneInformation(&tzinfo);
|
||||
#endif
|
||||
if (rv == TIME_ZONE_ID_INVALID)
|
||||
SPDLOG_THROW(spdlog::spdlog_ex("Failed getting timezone info. ", errno));
|
||||
throw_spdlog_ex("Failed getting timezone info. ", errno);
|
||||
|
||||
int offset = -tzinfo.Bias;
|
||||
if (tm.tm_isdst)
|
||||
@ -396,13 +391,13 @@ SPDLOG_INLINE int pid() SPDLOG_NOEXCEPT
|
||||
}
|
||||
|
||||
// Determine if the terminal supports colors
|
||||
// Source: https://github.com/agauniyal/rang/
|
||||
// Based on: https://github.com/agauniyal/rang/
|
||||
SPDLOG_INLINE bool is_color_terminal() SPDLOG_NOEXCEPT
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return true;
|
||||
#else
|
||||
static constexpr std::array<const char *, 14> Terms = {
|
||||
static constexpr std::array<const char *, 14> terms = {
|
||||
{"ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm", "linux", "msys", "putty", "rxvt", "screen", "vt100", "xterm"}};
|
||||
|
||||
const char *env_p = std::getenv("TERM");
|
||||
@ -412,7 +407,7 @@ SPDLOG_INLINE bool is_color_terminal() SPDLOG_NOEXCEPT
|
||||
}
|
||||
|
||||
static const bool result =
|
||||
std::any_of(std::begin(Terms), std::end(Terms), [&](const char *term) { return std::strstr(env_p, term) != nullptr; });
|
||||
std::any_of(terms.begin(), terms.end(), [&](const char *term) { return std::strstr(env_p, term) != nullptr; });
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
@ -434,7 +429,7 @@ SPDLOG_INLINE void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target)
|
||||
{
|
||||
if (wstr.size() > static_cast<size_t>((std::numeric_limits<int>::max)()))
|
||||
{
|
||||
SPDLOG_THROW(spdlog::spdlog_ex("UTF-16 string is too big to be converted to UTF-8"));
|
||||
throw_spdlog_ex("UTF-16 string is too big to be converted to UTF-8");
|
||||
}
|
||||
|
||||
int wstr_size = static_cast<int>(wstr.size());
|
||||
@ -462,7 +457,7 @@ SPDLOG_INLINE void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target)
|
||||
}
|
||||
}
|
||||
|
||||
SPDLOG_THROW(spdlog::spdlog_ex(fmt::format("WideCharToMultiByte failed. Last error: {}", ::GetLastError())));
|
||||
throw_spdlog_ex(fmt::format("WideCharToMultiByte failed. Last error: {}", ::GetLastError()));
|
||||
}
|
||||
#endif // (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
|
||||
|
||||
@ -536,6 +531,24 @@ SPDLOG_INLINE filename_t dir_name(filename_t path)
|
||||
return pos != filename_t::npos ? path.substr(0, pos) : filename_t{};
|
||||
}
|
||||
|
||||
std::string SPDLOG_INLINE getenv(const char *field)
|
||||
{
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#if defined(__cplusplus_winrt)
|
||||
return std::string{}; // not supported under uwp
|
||||
#else
|
||||
size_t len = 0;
|
||||
char buf[128];
|
||||
bool ok = ::getenv_s(&len, buf, sizeof(buf), field) == 0;
|
||||
return ok ? buf : std::string{};
|
||||
#endif
|
||||
#else // revert to getenv
|
||||
char *buf = ::getenv(field);
|
||||
return buf ? buf : std::string{};
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace os
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
|
50
third_party/spdlog/include/spdlog/details/os.h
vendored
50
third_party/spdlog/include/spdlog/details/os.h
vendored
@ -10,15 +10,15 @@ namespace spdlog {
|
||||
namespace details {
|
||||
namespace os {
|
||||
|
||||
spdlog::log_clock::time_point now() SPDLOG_NOEXCEPT;
|
||||
SPDLOG_API spdlog::log_clock::time_point now() SPDLOG_NOEXCEPT;
|
||||
|
||||
std::tm localtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT;
|
||||
SPDLOG_API std::tm localtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT;
|
||||
|
||||
std::tm localtime() SPDLOG_NOEXCEPT;
|
||||
SPDLOG_API std::tm localtime() SPDLOG_NOEXCEPT;
|
||||
|
||||
std::tm gmtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT;
|
||||
SPDLOG_API std::tm gmtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT;
|
||||
|
||||
std::tm gmtime() SPDLOG_NOEXCEPT;
|
||||
SPDLOG_API std::tm gmtime() SPDLOG_NOEXCEPT;
|
||||
|
||||
// eol definition
|
||||
#if !defined(SPDLOG_EOL)
|
||||
@ -38,54 +38,50 @@ static const char folder_sep = '\\';
|
||||
SPDLOG_CONSTEXPR static const char folder_sep = '/';
|
||||
#endif
|
||||
|
||||
#ifdef SPDLOG_PREVENT_CHILD_FD
|
||||
void prevent_child_fd(FILE *f);
|
||||
#endif
|
||||
|
||||
// fopen_s on non windows for writing
|
||||
bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode);
|
||||
SPDLOG_API bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode);
|
||||
|
||||
// Remove filename. return 0 on success
|
||||
int remove(const filename_t &filename) SPDLOG_NOEXCEPT;
|
||||
SPDLOG_API int remove(const filename_t &filename) SPDLOG_NOEXCEPT;
|
||||
|
||||
// Remove file if exists. return 0 on success
|
||||
// Note: Non atomic (might return failure to delete if concurrently deleted by other process/thread)
|
||||
int remove_if_exists(const filename_t &filename) SPDLOG_NOEXCEPT;
|
||||
SPDLOG_API int remove_if_exists(const filename_t &filename) SPDLOG_NOEXCEPT;
|
||||
|
||||
int rename(const filename_t &filename1, const filename_t &filename2) SPDLOG_NOEXCEPT;
|
||||
SPDLOG_API int rename(const filename_t &filename1, const filename_t &filename2) SPDLOG_NOEXCEPT;
|
||||
|
||||
// Return if file exists.
|
||||
bool path_exists(const filename_t &filename) SPDLOG_NOEXCEPT;
|
||||
SPDLOG_API bool path_exists(const filename_t &filename) SPDLOG_NOEXCEPT;
|
||||
|
||||
// Return file size according to open FILE* object
|
||||
size_t filesize(FILE *f);
|
||||
SPDLOG_API size_t filesize(FILE *f);
|
||||
|
||||
// Return utc offset in minutes or throw spdlog_ex on failure
|
||||
int utc_minutes_offset(const std::tm &tm = details::os::localtime());
|
||||
SPDLOG_API int utc_minutes_offset(const std::tm &tm = details::os::localtime());
|
||||
|
||||
// Return current thread id as size_t
|
||||
// It exists because the std::this_thread::get_id() is much slower(especially
|
||||
// under VS 2013)
|
||||
size_t _thread_id() SPDLOG_NOEXCEPT;
|
||||
SPDLOG_API size_t _thread_id() SPDLOG_NOEXCEPT;
|
||||
|
||||
// Return current thread id as size_t (from thread local storage)
|
||||
size_t thread_id() SPDLOG_NOEXCEPT;
|
||||
SPDLOG_API size_t thread_id() SPDLOG_NOEXCEPT;
|
||||
|
||||
// This is avoid msvc issue in sleep_for that happens if the clock changes.
|
||||
// See https://github.com/gabime/spdlog/issues/609
|
||||
void sleep_for_millis(int milliseconds) SPDLOG_NOEXCEPT;
|
||||
SPDLOG_API void sleep_for_millis(int milliseconds) SPDLOG_NOEXCEPT;
|
||||
|
||||
std::string filename_to_str(const filename_t &filename);
|
||||
SPDLOG_API std::string filename_to_str(const filename_t &filename);
|
||||
|
||||
int pid() SPDLOG_NOEXCEPT;
|
||||
SPDLOG_API int pid() SPDLOG_NOEXCEPT;
|
||||
|
||||
// Determine if the terminal supports colors
|
||||
// Source: https://github.com/agauniyal/rang/
|
||||
bool is_color_terminal() SPDLOG_NOEXCEPT;
|
||||
SPDLOG_API bool is_color_terminal() SPDLOG_NOEXCEPT;
|
||||
|
||||
// Determine if the terminal attached
|
||||
// Source: https://github.com/agauniyal/rang/
|
||||
bool in_terminal(FILE *file) SPDLOG_NOEXCEPT;
|
||||
SPDLOG_API bool in_terminal(FILE *file) SPDLOG_NOEXCEPT;
|
||||
|
||||
#if (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
|
||||
void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target);
|
||||
@ -96,11 +92,15 @@ void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target);
|
||||
// "abc/" => "abc"
|
||||
// "abc" => ""
|
||||
// "abc///" => "abc//"
|
||||
filename_t dir_name(filename_t path);
|
||||
SPDLOG_API filename_t dir_name(filename_t path);
|
||||
|
||||
// Create a dir from the given path.
|
||||
// Return true if succeeded or if this dir already exists.
|
||||
bool create_dir(filename_t path);
|
||||
SPDLOG_API bool create_dir(filename_t path);
|
||||
|
||||
// non thread safe, cross platform getenv/getenv_s
|
||||
// return empty string if field not found
|
||||
SPDLOG_API std::string getenv(const char *field);
|
||||
|
||||
} // namespace os
|
||||
} // namespace details
|
||||
|
@ -17,7 +17,7 @@
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
|
||||
class periodic_worker
|
||||
class SPDLOG_API periodic_worker
|
||||
{
|
||||
public:
|
||||
periodic_worker(const std::function<void()> &callback_fun, std::chrono::seconds interval);
|
||||
|
@ -10,7 +10,7 @@
|
||||
#include <spdlog/common.h>
|
||||
#include <spdlog/details/periodic_worker.h>
|
||||
#include <spdlog/logger.h>
|
||||
#include <spdlog/details/pattern_formatter.h>
|
||||
#include <spdlog/pattern_formatter.h>
|
||||
|
||||
#ifndef SPDLOG_DISABLE_DEFAULT_LOGGER
|
||||
// support for the default stdout color logger
|
||||
@ -48,6 +48,9 @@ SPDLOG_INLINE registry::registry()
|
||||
|
||||
#endif // SPDLOG_DISABLE_DEFAULT_LOGGER
|
||||
}
|
||||
|
||||
SPDLOG_INLINE registry::~registry() = default;
|
||||
|
||||
SPDLOG_INLINE void registry::register_logger(std::shared_ptr<logger> new_logger)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||
@ -64,7 +67,7 @@ SPDLOG_INLINE void registry::initialize_logger(std::shared_ptr<logger> new_logge
|
||||
new_logger->set_error_handler(err_handler_);
|
||||
}
|
||||
|
||||
new_logger->set_level(level_);
|
||||
new_logger->set_level(levels_.get(new_logger->name()));
|
||||
new_logger->flush_on(flush_level_);
|
||||
|
||||
if (backtrace_n_messages_ > 0)
|
||||
@ -168,7 +171,7 @@ SPDLOG_INLINE void registry::set_level(level::level_enum log_level)
|
||||
{
|
||||
l.second->set_level(log_level);
|
||||
}
|
||||
level_ = log_level;
|
||||
levels_.set_default(log_level);
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void registry::flush_on(level::level_enum log_level)
|
||||
@ -184,7 +187,7 @@ SPDLOG_INLINE void registry::flush_on(level::level_enum log_level)
|
||||
SPDLOG_INLINE void registry::flush_every(std::chrono::seconds interval)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(flusher_mutex_);
|
||||
std::function<void()> clbk = std::bind(®istry::flush_all, this);
|
||||
auto clbk = [this]() { this->flush_all(); };
|
||||
periodic_flusher_ = details::make_unique<periodic_worker>(clbk, interval);
|
||||
}
|
||||
|
||||
@ -260,6 +263,17 @@ SPDLOG_INLINE void registry::set_automatic_registration(bool automatic_registrat
|
||||
automatic_registration_ = automatic_registration;
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void registry::update_levels(cfg::log_levels levels)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||
levels_ = std::move(levels);
|
||||
for (auto &l : loggers_)
|
||||
{
|
||||
auto &logger = l.second;
|
||||
logger->set_level(levels_.get(logger->name()));
|
||||
}
|
||||
}
|
||||
|
||||
SPDLOG_INLINE registry ®istry::instance()
|
||||
{
|
||||
static registry s_instance;
|
||||
@ -270,7 +284,7 @@ SPDLOG_INLINE void registry::throw_if_exists_(const std::string &logger_name)
|
||||
{
|
||||
if (loggers_.find(logger_name) != loggers_.end())
|
||||
{
|
||||
SPDLOG_THROW(spdlog_ex("logger with name '" + logger_name + "' already exists"));
|
||||
throw_spdlog_ex("logger with name '" + logger_name + "' already exists");
|
||||
}
|
||||
}
|
||||
|
||||
@ -280,5 +294,6 @@ SPDLOG_INLINE void registry::register_logger_(std::shared_ptr<logger> new_logger
|
||||
throw_if_exists_(logger_name);
|
||||
loggers_[logger_name] = std::move(new_logger);
|
||||
}
|
||||
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
|
@ -9,6 +9,7 @@
|
||||
// This class is thread safe
|
||||
|
||||
#include <spdlog/common.h>
|
||||
#include <spdlog/cfg/log_levels.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
@ -24,7 +25,7 @@ namespace details {
|
||||
class thread_pool;
|
||||
class periodic_worker;
|
||||
|
||||
class registry
|
||||
class SPDLOG_API registry
|
||||
{
|
||||
public:
|
||||
registry(const registry &) = delete;
|
||||
@ -79,19 +80,21 @@ public:
|
||||
|
||||
void set_automatic_registration(bool automatic_registration);
|
||||
|
||||
void update_levels(cfg::log_levels levels);
|
||||
|
||||
static registry &instance();
|
||||
|
||||
private:
|
||||
registry();
|
||||
~registry() = default;
|
||||
~registry();
|
||||
|
||||
void throw_if_exists_(const std::string &logger_name);
|
||||
void register_logger_(std::shared_ptr<logger> new_logger);
|
||||
std::mutex logger_map_mutex_, flusher_mutex_;
|
||||
std::recursive_mutex tp_mutex_;
|
||||
std::unordered_map<std::string, std::shared_ptr<logger>> loggers_;
|
||||
cfg::log_levels levels_;
|
||||
std::unique_ptr<formatter> formatter_;
|
||||
level::level_enum level_ = level::info;
|
||||
level::level_enum flush_level_ = level::off;
|
||||
void (*err_handler_)(const std::string &msg);
|
||||
std::shared_ptr<thread_pool> tp_;
|
||||
|
175
third_party/spdlog/include/spdlog/details/tcp_client-windows.h
vendored
Normal file
175
third_party/spdlog/include/spdlog/details/tcp_client-windows.h
vendored
Normal file
@ -0,0 +1,175 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
// tcp client helper
|
||||
#include <spdlog/common.h>
|
||||
#include <spdlog/details/os.h>
|
||||
|
||||
#include <windows.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
|
||||
#pragma comment(lib, "Ws2_32.lib")
|
||||
#pragma comment(lib, "Mswsock.lib")
|
||||
#pragma comment(lib, "AdvApi32.lib")
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
class tcp_client
|
||||
{
|
||||
SOCKET socket_ = INVALID_SOCKET;
|
||||
|
||||
static bool winsock_initialized_()
|
||||
{
|
||||
SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (s == INVALID_SOCKET)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
closesocket(s);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static void init_winsock_()
|
||||
{
|
||||
WSADATA wsaData;
|
||||
auto rv = WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||
if (rv != 0)
|
||||
{
|
||||
throw_winsock_error_("WSAStartup failed", ::WSAGetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
static void throw_winsock_error_(const std::string &msg, int last_error)
|
||||
{
|
||||
char buf[512];
|
||||
::FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, last_error,
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, (sizeof(buf) / sizeof(char)), NULL);
|
||||
|
||||
throw_spdlog_ex(fmt::format("tcp_sink - {}: {}", msg, buf));
|
||||
}
|
||||
|
||||
public:
|
||||
bool is_connected() const
|
||||
{
|
||||
return socket_ != INVALID_SOCKET;
|
||||
}
|
||||
|
||||
void close()
|
||||
{
|
||||
::closesocket(socket_);
|
||||
socket_ = INVALID_SOCKET;
|
||||
WSACleanup();
|
||||
}
|
||||
|
||||
SOCKET fd() const
|
||||
{
|
||||
return socket_;
|
||||
}
|
||||
|
||||
~tcp_client()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
// try to connect or throw on failure
|
||||
void connect(const std::string &host, int port)
|
||||
{
|
||||
// initialize winsock if needed
|
||||
if (!winsock_initialized_())
|
||||
{
|
||||
init_winsock_();
|
||||
}
|
||||
|
||||
if (is_connected())
|
||||
{
|
||||
close();
|
||||
}
|
||||
struct addrinfo hints
|
||||
{};
|
||||
ZeroMemory(&hints, sizeof(hints));
|
||||
|
||||
hints.ai_family = AF_INET; // IPv4
|
||||
hints.ai_socktype = SOCK_STREAM; // TCP
|
||||
hints.ai_flags = AI_NUMERICSERV; // port passed as as numeric value
|
||||
hints.ai_protocol = 0;
|
||||
|
||||
auto port_str = std::to_string(port);
|
||||
struct addrinfo *addrinfo_result;
|
||||
auto rv = ::getaddrinfo(host.c_str(), port_str.c_str(), &hints, &addrinfo_result);
|
||||
int last_error = 0;
|
||||
if (rv != 0)
|
||||
{
|
||||
last_error = ::WSAGetLastError();
|
||||
WSACleanup();
|
||||
throw_winsock_error_("getaddrinfo failed", last_error);
|
||||
}
|
||||
|
||||
// Try each address until we successfully connect(2).
|
||||
|
||||
for (auto *rp = addrinfo_result; rp != nullptr; rp = rp->ai_next)
|
||||
{
|
||||
socket_ = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
|
||||
if (socket_ == INVALID_SOCKET)
|
||||
{
|
||||
last_error = ::WSAGetLastError();
|
||||
WSACleanup();
|
||||
continue;
|
||||
}
|
||||
if (::connect(socket_, rp->ai_addr, (int)rp->ai_addrlen) == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
last_error = ::WSAGetLastError();
|
||||
close();
|
||||
}
|
||||
}
|
||||
::freeaddrinfo(addrinfo_result);
|
||||
if (socket_ == INVALID_SOCKET)
|
||||
{
|
||||
WSACleanup();
|
||||
throw_winsock_error_("connect failed", last_error);
|
||||
}
|
||||
|
||||
// set TCP_NODELAY
|
||||
int enable_flag = 1;
|
||||
::setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, (char *)&enable_flag, sizeof(enable_flag));
|
||||
}
|
||||
|
||||
// Send exactly n_bytes of the given data.
|
||||
// On error close the connection and throw.
|
||||
void send(const char *data, size_t n_bytes)
|
||||
{
|
||||
size_t bytes_sent = 0;
|
||||
while (bytes_sent < n_bytes)
|
||||
{
|
||||
const int send_flags = 0;
|
||||
auto write_result = ::send(socket_, data + bytes_sent, (int)(n_bytes - bytes_sent), send_flags);
|
||||
if (write_result == SOCKET_ERROR)
|
||||
{
|
||||
int last_error = ::WSAGetLastError();
|
||||
close();
|
||||
throw_winsock_error_("send failed", last_error);
|
||||
}
|
||||
|
||||
if (write_result == 0) // (probably should not happen but in any case..)
|
||||
{
|
||||
break;
|
||||
}
|
||||
bytes_sent += static_cast<size_t>(write_result);
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user