Compare commits
62 Commits
feature/wi
...
feature/cp
Author | SHA1 | Date | |
---|---|---|---|
7ff12a36b9 | |||
945c692227 | |||
c16b64bcb2 | |||
886b8f54bf | |||
02810f9adf | |||
b0b451d2c7 | |||
4872b59fac | |||
bb1be240ec | |||
b008c97c83 | |||
9886a30490 | |||
4ed5206d79 | |||
33916869f1 | |||
9ddf707804 | |||
3a020a66b7 | |||
bd39e69185 | |||
9d4ca3f34e | |||
de6f3ded09 | |||
e0aace33ea | |||
16eb269e1e | |||
2319dec278 | |||
f1be48aff1 | |||
93fd44813a | |||
54d4d81bf4 | |||
ea207d8199 | |||
e8287e91e4 | |||
c0505ac7fb | |||
1af39bf0eb | |||
2e904801a0 | |||
cc72494b63 | |||
fa9a4660c6 | |||
4773af8f2f | |||
c1403df74a | |||
3912e22b28 | |||
c9d5b4a581 | |||
9f8643032d | |||
0772ef7ef5 | |||
c030a62c8b | |||
931530b101 | |||
6c205b983e | |||
a65b334961 | |||
2de8aafcbc | |||
f075f586e1 | |||
93cb898989 | |||
e4da62547b | |||
2b4c06e6d2 | |||
7337ed34a6 | |||
15355188d5 | |||
8760c87635 | |||
2786631e3b | |||
1b30061a4d | |||
af003fc79b | |||
4f17cd5e74 | |||
b04764489c | |||
fc4a4bfb7c | |||
9e54fd5f1a | |||
1096f62196 | |||
b34d9f6a06 | |||
b21e2506bf | |||
303f99a432 | |||
a42ccea8dd | |||
beb26bc096 | |||
b45980f0f6 |
75
.github/workflows/ccpp.yml
vendored
75
.github/workflows/ccpp.yml
vendored
@ -1,75 +0,0 @@
|
|||||||
name: unittest
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
paths-ignore:
|
|
||||||
- 'docs/**'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
linux:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v1
|
|
||||||
- name: make test
|
|
||||||
run: make test
|
|
||||||
|
|
||||||
mac_tsan_sectransport:
|
|
||||||
runs-on: macOS-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v1
|
|
||||||
- name: make test_tsan
|
|
||||||
run: make test_tsan
|
|
||||||
|
|
||||||
mac_tsan_openssl:
|
|
||||||
runs-on: macOS-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v1
|
|
||||||
- name: install openssl
|
|
||||||
run: brew install openssl@1.1
|
|
||||||
- name: make test
|
|
||||||
run: make test_tsan_openssl
|
|
||||||
|
|
||||||
mac_tsan_mbedtls:
|
|
||||||
runs-on: macOS-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v1
|
|
||||||
- name: install mbedtls
|
|
||||||
run: brew install mbedtls
|
|
||||||
- name: make test
|
|
||||||
run: make test_tsan_mbedtls
|
|
||||||
|
|
||||||
windows_openssl:
|
|
||||||
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, the binary cannot be found
|
|
||||||
#- run: ../build/test/ixwebsocket_unittest.exe
|
|
||||||
# working-directory: test
|
|
||||||
|
|
||||||
#
|
|
||||||
# Windows with OpenSSL is working but disabled as it takes 13 minutes (10 for openssl) to build with vcpkg
|
|
||||||
#
|
|
||||||
# windows_openssl:
|
|
||||||
# runs-on: windows-latest
|
|
||||||
# steps:
|
|
||||||
# - uses: actions/checkout@v1
|
|
||||||
# - uses: seanmiddleditch/gha-setup-vsdevenv@master
|
|
||||||
# - run: |
|
|
||||||
# vcpkg install zlib:x64-windows
|
|
||||||
# vcpkg install openssl:x64-windows
|
|
||||||
# - run: |
|
|
||||||
# mkdir build
|
|
||||||
# cd build
|
|
||||||
# cmake -DCMAKE_TOOLCHAIN_FILE=c:/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_CXX_COMPILER=cl.exe -DUSE_OPEN_SSL=1 -DUSE_TLS=1 -DUSE_WS=1 -DUSE_TEST=1 ..
|
|
||||||
# - run: cmake --build build
|
|
||||||
#
|
|
||||||
# # Running the unittest does not work, the binary cannot be found
|
|
||||||
# #- run: ../build/test/ixwebsocket_unittest.exe
|
|
||||||
# # working-directory: test
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
|||||||
# Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
|
# Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
|
||||||
#
|
#
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.4.1)
|
cmake_minimum_required(VERSION 3.4.1...3.17.2)
|
||||||
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake;${CMAKE_MODULE_PATH}")
|
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake;${CMAKE_MODULE_PATH}")
|
||||||
|
|
||||||
project(ixwebsocket C CXX)
|
project(ixwebsocket C CXX)
|
||||||
@ -12,6 +12,10 @@ set (CMAKE_CXX_STANDARD 14)
|
|||||||
set (CXX_STANDARD_REQUIRED ON)
|
set (CXX_STANDARD_REQUIRED ON)
|
||||||
set (CMAKE_CXX_EXTENSIONS OFF)
|
set (CMAKE_CXX_EXTENSIONS OFF)
|
||||||
|
|
||||||
|
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||||
|
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||||
|
endif()
|
||||||
|
|
||||||
if (UNIX)
|
if (UNIX)
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic")
|
||||||
endif()
|
endif()
|
||||||
@ -119,6 +123,11 @@ if (USE_TLS)
|
|||||||
if (NOT USE_MBED_TLS AND NOT USE_OPEN_SSL) # unless we want something else
|
if (NOT USE_MBED_TLS AND NOT USE_OPEN_SSL) # unless we want something else
|
||||||
set(USE_SECURE_TRANSPORT ON)
|
set(USE_SECURE_TRANSPORT ON)
|
||||||
endif()
|
endif()
|
||||||
|
# default to mbedtls on windows if nothing is configured
|
||||||
|
elseif (WIN32)
|
||||||
|
if (NOT USE_OPEN_SSL) # unless we want something else
|
||||||
|
set(USE_MBED_TLS ON)
|
||||||
|
endif()
|
||||||
else() # default to OpenSSL on all other platforms
|
else() # default to OpenSSL on all other platforms
|
||||||
if (NOT USE_MBED_TLS) # Unless mbedtls is requested
|
if (NOT USE_MBED_TLS) # Unless mbedtls is requested
|
||||||
set(USE_OPEN_SSL ON)
|
set(USE_OPEN_SSL ON)
|
||||||
@ -165,6 +174,10 @@ if (USE_TLS)
|
|||||||
if (APPLE)
|
if (APPLE)
|
||||||
set(CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} /usr/local/opt/openssl/lib)
|
set(CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} /usr/local/opt/openssl/lib)
|
||||||
set(CMAKE_INCLUDE_PATH ${CMAKE_INCLUDE_PATH} /usr/local/opt/openssl/include)
|
set(CMAKE_INCLUDE_PATH ${CMAKE_INCLUDE_PATH} /usr/local/opt/openssl/include)
|
||||||
|
|
||||||
|
# This is for MacPort OpenSSL 1.0
|
||||||
|
# set(CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} /opt/local/lib/openssl-1.0)
|
||||||
|
# set(CMAKE_INCLUDE_PATH ${CMAKE_INCLUDE_PATH} /opt/local/include/openssl-1.0)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# This OPENSSL_FOUND check is to help find a cmake manually configured OpenSSL
|
# This OPENSSL_FOUND check is to help find a cmake manually configured OpenSSL
|
||||||
|
@ -1 +1 @@
|
|||||||
docker/Dockerfile.alpine
|
docker/Dockerfile.centos7_httpd
|
93
README.md
93
README.md
@ -1,37 +1,72 @@
|
|||||||
## Hello world
|
## Hello world
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
IXWebSocket is a C++ library for WebSocket client and server development. It has minimal dependencies (no boost), is very simple to use and support everything you'll likely need for websocket dev (SSL, deflate compression, compiles on most platforms, etc...). HTTP client and server code is also available, but it hasn't received as much testing.
|
IXWebSocket is a C++ library for WebSocket client and server development. It has minimal dependencies (no boost), is very simple to use and support everything you'll likely need for websocket dev (SSL, deflate compression, compiles on most platforms, etc...). HTTP client and server code is also available, but it hasn't received as much testing.
|
||||||
|
|
||||||
It is been used on big mobile video game titles sending and receiving tons of messages since 2017 (iOS and Android). It was tested on macOS, iOS, Linux, Android, Windows and FreeBSD. Two important design goals are simplicity and correctness.
|
It is been used on big mobile video game titles sending and receiving tons of messages since 2017 (iOS and Android). It was tested on macOS, iOS, Linux, Android, Windows and FreeBSD. Two important design goals are simplicity and correctness.
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
// Required on Windows
|
/*
|
||||||
ix::initNetSystem();
|
* main.cpp
|
||||||
|
* Author: Benjamin Sergeant
|
||||||
|
* Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Super simple standalone example. See ws folder, unittest and doc/usage.md for more.
|
||||||
|
*
|
||||||
|
* On macOS
|
||||||
|
* $ mkdir -p build ; cd build ; cmake -DUSE_TLS=1 .. ; make -j ; make install
|
||||||
|
* $ clang++ --std=c++14 --stdlib=libc++ main.cpp -lixwebsocket -lz -framework Security -framework Foundation
|
||||||
|
* $ ./a.out
|
||||||
|
*/
|
||||||
|
|
||||||
// Our websocket object
|
#include <ixwebsocket/IXNetSystem.h>
|
||||||
ix::WebSocket webSocket;
|
#include <ixwebsocket/IXWebSocket.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
std::string url("ws://localhost:8080/");
|
int main()
|
||||||
webSocket.setUrl(url);
|
{
|
||||||
|
// Required on Windows
|
||||||
|
ix::initNetSystem();
|
||||||
|
|
||||||
// Setup a callback to be fired (in a background thread, watch out for race conditions !)
|
// Our websocket object
|
||||||
// when a message or an event (open, close, error) is received
|
ix::WebSocket webSocket;
|
||||||
webSocket.setOnMessageCallback([](const ix::WebSocketMessagePtr& msg)
|
|
||||||
|
std::string url("wss://echo.websocket.org");
|
||||||
|
webSocket.setUrl(url);
|
||||||
|
|
||||||
|
std::cout << "Connecting to " << url << "..." << std::endl;
|
||||||
|
|
||||||
|
// Setup a callback to be fired (in a background thread, watch out for race conditions !)
|
||||||
|
// when a message or an event (open, close, error) is received
|
||||||
|
webSocket.setOnMessageCallback([](const ix::WebSocketMessagePtr& msg)
|
||||||
{
|
{
|
||||||
if (msg->type == ix::WebSocketMessageType::Message)
|
if (msg->type == ix::WebSocketMessageType::Message)
|
||||||
{
|
{
|
||||||
std::cout << msg->str << std::endl;
|
std::cout << "received message: " << msg->str << std::endl;
|
||||||
|
}
|
||||||
|
else if (msg->type == ix::WebSocketMessageType::Open)
|
||||||
|
{
|
||||||
|
std::cout << "Connection established" << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// Now that our callback is setup, we can start our background thread and receive messages
|
// Now that our callback is setup, we can start our background thread and receive messages
|
||||||
webSocket.start();
|
webSocket.start();
|
||||||
|
|
||||||
// Send a message to the server (default to TEXT mode)
|
// Send a message to the server (default to TEXT mode)
|
||||||
webSocket.send("hello world");
|
webSocket.send("hello world");
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
std::string text;
|
||||||
|
std::cout << "> " << std::flush;
|
||||||
|
std::getline(std::cin, text);
|
||||||
|
|
||||||
|
webSocket.send(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Interested? Go read the [docs](https://machinezone.github.io/IXWebSocket/)! If things don't work as expected, please create an issue on GitHub, or even better a pull request if you know how to fix your problem.
|
Interested? Go read the [docs](https://machinezone.github.io/IXWebSocket/)! If things don't work as expected, please create an issue on GitHub, or even better a pull request if you know how to fix your problem.
|
||||||
@ -45,3 +80,27 @@ IXWebSocket client code is autobahn compliant beginning with the 6.0.0 version.
|
|||||||
If your company or project is using this library, feel free to open an issue or PR to amend this list.
|
If your company or project is using this library, feel free to open an issue or PR to amend this list.
|
||||||
|
|
||||||
- [Machine Zone](https://www.mz.com)
|
- [Machine Zone](https://www.mz.com)
|
||||||
|
- [Tokio](https://gitlab.com/HCInk/tokio), a discord library focused on audio playback with node bindings.
|
||||||
|
- [libDiscordBot](https://github.com/tostc/libDiscordBot/tree/master), a work in progress discord library
|
||||||
|
- [gwebsocket](https://github.com/norrbotten/gwebsocket), a websocket (lua) module for Garry's Mod
|
||||||
|
- [DisCPP](https://github.com/DisCPP/DisCPP), a simple but feature rich Discord API wrapper
|
||||||
|
|
||||||
|
## Continuous Integration
|
||||||
|
|
||||||
|
| OS | TLS | Sanitizer | Status |
|
||||||
|
|-------------------|-------------------|-------------------|-------------------|
|
||||||
|
| Linux | OpenSSL | None | [![Build2][1]][7] |
|
||||||
|
| macOS | Secure Transport | Thread Sanitizer | [![Build2][2]][7] |
|
||||||
|
| macOS | OpenSSL | Thread Sanitizer | [![Build2][3]][7] |
|
||||||
|
| macOS | MbedTLS | Thread Sanitizer | [![Build2][4]][7] |
|
||||||
|
| Windows | Disabled | None | [![Build2][5]][7] |
|
||||||
|
| UWP | Disabled | None | [![Build2][6]][7] |
|
||||||
|
|
||||||
|
[1]: https://github.com/machinezone/IXWebSocket/workflows/linux/badge.svg
|
||||||
|
[2]: https://github.com/machinezone/IXWebSocket/workflows/mac_tsan_sectransport/badge.svg
|
||||||
|
[3]: https://github.com/machinezone/IXWebSocket/workflows/mac_tsan_openssl/badge.svg
|
||||||
|
[4]: https://github.com/machinezone/IXWebSocket/workflows/mac_tsan_mbedtls/badge.svg
|
||||||
|
[5]: https://github.com/machinezone/IXWebSocket/workflows/windows/badge.svg
|
||||||
|
[6]: https://github.com/machinezone/IXWebSocket/workflows/uwp/badge.svg
|
||||||
|
[7]: https://github.com/machinezone/IXWebSocket
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ FROM alpine:3.11 as build
|
|||||||
|
|
||||||
RUN apk add --no-cache \
|
RUN apk add --no-cache \
|
||||||
gcc g++ musl-dev linux-headers \
|
gcc g++ musl-dev linux-headers \
|
||||||
cmake mbedtls-dev make zlib-dev
|
cmake mbedtls-dev make zlib-dev ninja
|
||||||
|
|
||||||
RUN addgroup -S app && \
|
RUN addgroup -S app && \
|
||||||
adduser -S -G app app && \
|
adduser -S -G app app && \
|
||||||
|
33
docker/Dockerfile.centos7_httpd
Normal file
33
docker/Dockerfile.centos7_httpd
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
FROM centos:7 as build
|
||||||
|
|
||||||
|
RUN yum install -y gcc-c++ make zlib-devel redhat-rpm-config
|
||||||
|
|
||||||
|
RUN groupadd app && useradd -g app app
|
||||||
|
RUN chown -R app:app /opt
|
||||||
|
RUN 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
|
||||||
|
COPY --chown=app:app . /opt
|
||||||
|
WORKDIR /opt
|
||||||
|
|
||||||
|
USER app
|
||||||
|
RUN [ "make", "httpd_linux" ]
|
||||||
|
RUN [ "rm", "-rf", "build" ]
|
||||||
|
|
||||||
|
FROM centos:8 as runtime
|
||||||
|
|
||||||
|
RUN groupadd app && useradd -g app app
|
||||||
|
COPY --chown=app:app --from=build /usr/local/bin/ixhttpd /usr/local/bin/ixhttpd
|
||||||
|
RUN chmod +x /usr/local/bin/ixhttpd
|
||||||
|
RUN ldd /usr/local/bin/ixhttpd
|
||||||
|
|
||||||
|
# Copy source code for gcc
|
||||||
|
COPY --chown=app:app --from=build /opt /opt
|
||||||
|
|
||||||
|
# Now run in usermode
|
||||||
|
USER app
|
||||||
|
WORKDIR /home/app
|
||||||
|
|
||||||
|
ENTRYPOINT ["ixhttpd"]
|
||||||
|
EXPOSE 9999
|
@ -1,6 +1,58 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
All changes to this project will be documented in this file.
|
All changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
## [9.6.5] - 2020-05-29
|
||||||
|
|
||||||
|
(http server) support gzip compression
|
||||||
|
|
||||||
|
## [9.6.4] - 2020-05-20
|
||||||
|
|
||||||
|
(compiler fix) support clang 5 and earlier (contributed by @LunarWatcher)
|
||||||
|
|
||||||
|
## [9.6.3] - 2020-05-18
|
||||||
|
|
||||||
|
(cmake) revert CMake changes to fix #203 and be able to use an external OpenSSL
|
||||||
|
|
||||||
|
## [9.6.2] - 2020-05-17
|
||||||
|
|
||||||
|
(cmake) make install cmake files optional to not conflict with vcpkg
|
||||||
|
|
||||||
|
## [9.6.1] - 2020-05-17
|
||||||
|
|
||||||
|
(windows + tls) mbedtls is the default windows tls backend + add ability to load system certificates with mbdetls on windows
|
||||||
|
|
||||||
|
## [9.6.0] - 2020-05-12
|
||||||
|
|
||||||
|
(ixbots) add options to limit how many messages per minute should be processed
|
||||||
|
|
||||||
|
## [9.5.9] - 2020-05-12
|
||||||
|
|
||||||
|
(ixbots) add new class to configure a bot to simplify passing options around
|
||||||
|
|
||||||
|
## [9.5.8] - 2020-05-08
|
||||||
|
|
||||||
|
(openssl tls) (openssl < 1.1) logic inversion - crypto locking callback are not registered properly
|
||||||
|
|
||||||
|
## [9.5.7] - 2020-05-08
|
||||||
|
|
||||||
|
(cmake) default TLS back to mbedtls on Windows Universal Platform
|
||||||
|
|
||||||
|
## [9.5.6] - 2020-05-06
|
||||||
|
|
||||||
|
(cobra bots) add a --heartbeat_timeout option to specify when the bot should terminate because no events are received
|
||||||
|
|
||||||
|
## [9.5.5] - 2020-05-06
|
||||||
|
|
||||||
|
(openssl tls) when OpenSSL is older than 1.1, register the crypto locking callback to be thread safe. Should fix lots of CI failures
|
||||||
|
|
||||||
|
## [9.5.4] - 2020-05-04
|
||||||
|
|
||||||
|
(cobra bots) do not use a queue to store messages pending processing, let the bot handle queuing
|
||||||
|
|
||||||
|
## [9.5.3] - 2020-04-29
|
||||||
|
|
||||||
|
(http client) better current request cancellation support when the HttpClient destructor is invoked (see #189)
|
||||||
|
|
||||||
## [9.5.2] - 2020-04-27
|
## [9.5.2] - 2020-04-27
|
||||||
|
|
||||||
(cmake) fix cmake broken tls option parsing
|
(cmake) fix cmake broken tls option parsing
|
||||||
|
@ -42,6 +42,19 @@ It is possible to get IXWebSocket through Microsoft [vcpkg](https://github.com/m
|
|||||||
```
|
```
|
||||||
vcpkg install ixwebsocket
|
vcpkg install ixwebsocket
|
||||||
```
|
```
|
||||||
|
To use the installed package within a cmake project, use the following:
|
||||||
|
```cmake
|
||||||
|
set(CMAKE_TOOLCHAIN_FILE "$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" CACHE STRING "") # this is super important in order for cmake to include the vcpkg search/lib paths!
|
||||||
|
|
||||||
|
# find library and its headers
|
||||||
|
find_path(IXWEBSOCKET_INCLUDE_DIR ixwebsocket/IXWebSocket.h)
|
||||||
|
find_library(IXWEBSOCKET_LIBRARY ixwebsocket)
|
||||||
|
# include headers
|
||||||
|
include_directories(${IXWEBSOCKET_INCLUDE_DIR})
|
||||||
|
# ...
|
||||||
|
target_link_libraries(${PROJECT_NAME} ... ${IXWEBSOCKET_LIBRARY}) # Cmake will automatically fail the generation if the lib was not found, i.e is set to NOTFOUNS
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
### Conan
|
### Conan
|
||||||
|
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||

|
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
|
|
||||||
[*WebSocket*](https://en.wikipedia.org/wiki/WebSocket) is a computer communications protocol, providing full-duplex and bi-directionnal communication channels over a single TCP connection. *IXWebSocket* is a C++ library for client and server Websocket communication, and for client and server HTTP communication. *TLS* aka *SSL* is supported. The code is derived from [easywsclient](https://github.com/dhbaird/easywsclient) and from the [Satori C SDK](https://github.com/satori-com/satori-rtm-sdk-c). It has been tested on the following platforms.
|
[*WebSocket*](https://en.wikipedia.org/wiki/WebSocket) is a computer communications protocol, providing full-duplex and bi-directionnal communication channels over a single TCP connection. *IXWebSocket* is a C++ library for client and server Websocket communication, and for client and server HTTP communication. *TLS* aka *SSL* is supported. The code is derived from [easywsclient](https://github.com/dhbaird/easywsclient) and from the [Satori C SDK](https://github.com/satori-com/satori-rtm-sdk-c). It has been tested on the following platforms.
|
||||||
|
@ -392,6 +392,8 @@ bool ok = httpClient.performRequest(args, [](const HttpResponsePtr& response)
|
|||||||
// ok will be false if your httpClient is not async
|
// ok will be false if your httpClient is not async
|
||||||
```
|
```
|
||||||
|
|
||||||
|
See this [issue](https://github.com/machinezone/IXWebSocket/issues/209) for links about uploading files with HTTP multipart.
|
||||||
|
|
||||||
## HTTP server API
|
## HTTP server API
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
|
46
httpd.cpp
Normal file
46
httpd.cpp
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* httpd.cpp
|
||||||
|
* Author: Benjamin Sergeant
|
||||||
|
* Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Buid with make httpd
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "IXHttpServer.h"
|
||||||
|
#include <sstream>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
if (argc != 3)
|
||||||
|
{
|
||||||
|
std::cerr << "Usage: " << argv[0]
|
||||||
|
<< " <port> <host>" << std::endl;
|
||||||
|
std::cerr << " " << argv[0] << " 9090 127.0.0.1" << std::endl;
|
||||||
|
std::cerr << " " << argv[0] << " 9090 0.0.0.0" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int port;
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << argv[1];
|
||||||
|
ss >> port;
|
||||||
|
std::string hostname(argv[2]);
|
||||||
|
|
||||||
|
std::cout << "Listening on " << hostname
|
||||||
|
<< ":" << port << std::endl;
|
||||||
|
|
||||||
|
ix::HttpServer server(port, hostname);
|
||||||
|
|
||||||
|
auto res = server.listen();
|
||||||
|
if (!res.first)
|
||||||
|
{
|
||||||
|
std::cout << res.second << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
server.start();
|
||||||
|
server.wait();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -8,16 +8,15 @@ set (IXBOTS_SOURCES
|
|||||||
ixbots/IXCobraToSentryBot.cpp
|
ixbots/IXCobraToSentryBot.cpp
|
||||||
ixbots/IXCobraToStatsdBot.cpp
|
ixbots/IXCobraToStatsdBot.cpp
|
||||||
ixbots/IXCobraToStdoutBot.cpp
|
ixbots/IXCobraToStdoutBot.cpp
|
||||||
ixbots/IXQueueManager.cpp
|
|
||||||
ixbots/IXStatsdClient.cpp
|
ixbots/IXStatsdClient.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set (IXBOTS_HEADERS
|
set (IXBOTS_HEADERS
|
||||||
ixbots/IXCobraBot.h
|
ixbots/IXCobraBot.h
|
||||||
|
ixbots/IXCobraBotConfig.h
|
||||||
ixbots/IXCobraToSentryBot.h
|
ixbots/IXCobraToSentryBot.h
|
||||||
ixbots/IXCobraToStatsdBot.h
|
ixbots/IXCobraToStatsdBot.h
|
||||||
ixbots/IXCobraToStdoutBot.h
|
ixbots/IXCobraToStdoutBot.h
|
||||||
ixbots/IXQueueManager.h
|
|
||||||
ixbots/IXStatsdClient.h
|
ixbots/IXStatsdClient.h
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
|
|
||||||
#include "IXCobraBot.h"
|
#include "IXCobraBot.h"
|
||||||
|
|
||||||
#include "IXQueueManager.h"
|
|
||||||
#include <ixcobra/IXCobraConnection.h>
|
#include <ixcobra/IXCobraConnection.h>
|
||||||
#include <ixcore/utils/IXCoreLogger.h>
|
#include <ixcore/utils/IXCoreLogger.h>
|
||||||
|
|
||||||
@ -18,32 +17,33 @@
|
|||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
int64_t CobraBot::run(const CobraConfig& config,
|
int64_t CobraBot::run(const CobraBotConfig& botConfig)
|
||||||
const std::string& channel,
|
|
||||||
const std::string& filter,
|
|
||||||
const std::string& position,
|
|
||||||
bool verbose,
|
|
||||||
size_t maxQueueSize,
|
|
||||||
bool useQueue,
|
|
||||||
bool enableHeartbeat,
|
|
||||||
int runtime)
|
|
||||||
{
|
{
|
||||||
|
auto config = botConfig.cobraConfig;
|
||||||
|
auto channel = botConfig.channel;
|
||||||
|
auto filter = botConfig.filter;
|
||||||
|
auto position = botConfig.position;
|
||||||
|
auto enableHeartbeat = botConfig.enableHeartbeat;
|
||||||
|
auto heartBeatTimeout = botConfig.heartBeatTimeout;
|
||||||
|
auto runtime = botConfig.runtime;
|
||||||
|
auto maxEventsPerMinute = botConfig.maxEventsPerMinute;
|
||||||
|
auto limitReceivedEvents = botConfig.limitReceivedEvents;
|
||||||
|
|
||||||
ix::CobraConnection conn;
|
ix::CobraConnection conn;
|
||||||
conn.configure(config);
|
conn.configure(config);
|
||||||
conn.connect();
|
conn.connect();
|
||||||
|
|
||||||
Json::FastWriter jsonWriter;
|
|
||||||
std::atomic<uint64_t> sentCount(0);
|
std::atomic<uint64_t> sentCount(0);
|
||||||
std::atomic<uint64_t> receivedCount(0);
|
std::atomic<uint64_t> receivedCount(0);
|
||||||
uint64_t sentCountTotal(0);
|
uint64_t sentCountTotal(0);
|
||||||
uint64_t receivedCountTotal(0);
|
uint64_t receivedCountTotal(0);
|
||||||
uint64_t sentCountPerSecs(0);
|
uint64_t sentCountPerSecs(0);
|
||||||
uint64_t receivedCountPerSecs(0);
|
uint64_t receivedCountPerSecs(0);
|
||||||
|
std::atomic<int> receivedCountPerMinutes(0);
|
||||||
std::atomic<bool> stop(false);
|
std::atomic<bool> stop(false);
|
||||||
std::atomic<bool> throttled(false);
|
std::atomic<bool> throttled(false);
|
||||||
std::atomic<bool> fatalCobraError(false);
|
std::atomic<bool> fatalCobraError(false);
|
||||||
|
int minuteCounter = 0;
|
||||||
QueueManager queueManager(maxQueueSize);
|
|
||||||
|
|
||||||
auto timer = [&sentCount,
|
auto timer = [&sentCount,
|
||||||
&receivedCount,
|
&receivedCount,
|
||||||
@ -51,6 +51,8 @@ namespace ix
|
|||||||
&receivedCountTotal,
|
&receivedCountTotal,
|
||||||
&sentCountPerSecs,
|
&sentCountPerSecs,
|
||||||
&receivedCountPerSecs,
|
&receivedCountPerSecs,
|
||||||
|
&receivedCountPerMinutes,
|
||||||
|
&minuteCounter,
|
||||||
&stop] {
|
&stop] {
|
||||||
while (!stop)
|
while (!stop)
|
||||||
{
|
{
|
||||||
@ -71,13 +73,19 @@ namespace ix
|
|||||||
CoreLogger::info(ss.str());
|
CoreLogger::info(ss.str());
|
||||||
|
|
||||||
receivedCountPerSecs = receivedCount - receivedCountTotal;
|
receivedCountPerSecs = receivedCount - receivedCountTotal;
|
||||||
sentCountPerSecs = sentCount - receivedCountTotal;
|
sentCountPerSecs = sentCount - sentCountTotal;
|
||||||
|
|
||||||
receivedCountTotal += receivedCountPerSecs;
|
receivedCountTotal += receivedCountPerSecs;
|
||||||
sentCountTotal += sentCountPerSecs;
|
sentCountTotal += sentCountPerSecs;
|
||||||
|
|
||||||
auto duration = std::chrono::seconds(1);
|
auto duration = std::chrono::seconds(1);
|
||||||
std::this_thread::sleep_for(duration);
|
std::this_thread::sleep_for(duration);
|
||||||
|
|
||||||
|
if (minuteCounter++ == 60)
|
||||||
|
{
|
||||||
|
receivedCountPerMinutes = 0;
|
||||||
|
minuteCounter = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CoreLogger::info("timer thread done");
|
CoreLogger::info("timer thread done");
|
||||||
@ -85,7 +93,7 @@ namespace ix
|
|||||||
|
|
||||||
std::thread t1(timer);
|
std::thread t1(timer);
|
||||||
|
|
||||||
auto heartbeat = [&sentCount, &receivedCount, &stop, &enableHeartbeat] {
|
auto heartbeat = [&sentCount, &receivedCount, &stop, &enableHeartbeat, &heartBeatTimeout, &fatalCobraError] {
|
||||||
std::string state("na");
|
std::string state("na");
|
||||||
|
|
||||||
if (!enableHeartbeat) return;
|
if (!enableHeartbeat) return;
|
||||||
@ -101,11 +109,12 @@ namespace ix
|
|||||||
if (currentState == state)
|
if (currentState == state)
|
||||||
{
|
{
|
||||||
CoreLogger::error("no messages received or sent for 1 minute, exiting");
|
CoreLogger::error("no messages received or sent for 1 minute, exiting");
|
||||||
exit(1);
|
fatalCobraError = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
state = currentState;
|
state = currentState;
|
||||||
|
|
||||||
auto duration = std::chrono::minutes(1);
|
auto duration = std::chrono::seconds(heartBeatTimeout);
|
||||||
std::this_thread::sleep_for(duration);
|
std::this_thread::sleep_for(duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,40 +123,6 @@ namespace ix
|
|||||||
|
|
||||||
std::thread t2(heartbeat);
|
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)
|
|
||||||
{
|
|
||||||
CoreLogger::info("cobra bot: sending succesfull");
|
|
||||||
}
|
|
||||||
++sentCount;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
CoreLogger::error("cobra bot: error sending");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stop) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
CoreLogger::info("sender thread done");
|
|
||||||
};
|
|
||||||
|
|
||||||
std::thread t3(sender);
|
|
||||||
|
|
||||||
std::string subscriptionPosition(position);
|
std::string subscriptionPosition(position);
|
||||||
|
|
||||||
conn.setEventCallback([this,
|
conn.setEventCallback([this,
|
||||||
@ -155,13 +130,12 @@ namespace ix
|
|||||||
&channel,
|
&channel,
|
||||||
&filter,
|
&filter,
|
||||||
&subscriptionPosition,
|
&subscriptionPosition,
|
||||||
&jsonWriter,
|
|
||||||
verbose,
|
|
||||||
&throttled,
|
&throttled,
|
||||||
&receivedCount,
|
&receivedCount,
|
||||||
|
&receivedCountPerMinutes,
|
||||||
|
maxEventsPerMinute,
|
||||||
|
limitReceivedEvents,
|
||||||
&fatalCobraError,
|
&fatalCobraError,
|
||||||
&useQueue,
|
|
||||||
&queueManager,
|
|
||||||
&sentCount](const CobraEventPtr& event) {
|
&sentCount](const CobraEventPtr& event) {
|
||||||
if (event->type == ix::CobraEventType::Open)
|
if (event->type == ix::CobraEventType::Open)
|
||||||
{
|
{
|
||||||
@ -182,26 +156,23 @@ namespace ix
|
|||||||
CoreLogger::info("Subscribing to " + channel);
|
CoreLogger::info("Subscribing to " + channel);
|
||||||
CoreLogger::info("Subscribing at position " + subscriptionPosition);
|
CoreLogger::info("Subscribing at position " + subscriptionPosition);
|
||||||
CoreLogger::info("Subscribing with filter " + filter);
|
CoreLogger::info("Subscribing with filter " + filter);
|
||||||
conn.subscribe(channel,
|
conn.subscribe(channel, filter, subscriptionPosition,
|
||||||
filter,
|
[&sentCount, &receivedCountPerMinutes,
|
||||||
subscriptionPosition,
|
maxEventsPerMinute, limitReceivedEvents,
|
||||||
[this,
|
&throttled, &receivedCount,
|
||||||
&jsonWriter,
|
&subscriptionPosition, &fatalCobraError,
|
||||||
verbose,
|
this](const Json::Value& msg, const std::string& position) {
|
||||||
&throttled,
|
|
||||||
&receivedCount,
|
|
||||||
&queueManager,
|
|
||||||
&useQueue,
|
|
||||||
&subscriptionPosition,
|
|
||||||
&fatalCobraError,
|
|
||||||
&sentCount](const Json::Value& msg, const std::string& position) {
|
|
||||||
if (verbose)
|
|
||||||
{
|
|
||||||
CoreLogger::info("Subscriber received message "
|
|
||||||
+ position + " -> " + jsonWriter.write(msg));
|
|
||||||
}
|
|
||||||
|
|
||||||
subscriptionPosition = position;
|
subscriptionPosition = position;
|
||||||
|
++receivedCount;
|
||||||
|
|
||||||
|
++receivedCountPerMinutes;
|
||||||
|
if (limitReceivedEvents)
|
||||||
|
{
|
||||||
|
if (receivedCountPerMinutes > maxEventsPerMinute)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If we cannot send to sentry fast enough, drop the message
|
// If we cannot send to sentry fast enough, drop the message
|
||||||
if (throttled)
|
if (throttled)
|
||||||
@ -209,30 +180,9 @@ namespace ix
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
++receivedCount;
|
|
||||||
|
|
||||||
if (useQueue)
|
|
||||||
{
|
|
||||||
queueManager.add(msg, position);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (_onBotMessageCallback &&
|
|
||||||
_onBotMessageCallback(
|
_onBotMessageCallback(
|
||||||
msg, position, verbose, throttled, fatalCobraError))
|
msg, position, throttled,
|
||||||
{
|
fatalCobraError, sentCount);
|
||||||
// That might be too noisy
|
|
||||||
if (verbose)
|
|
||||||
{
|
|
||||||
CoreLogger::info("cobra bot: sending succesfull");
|
|
||||||
}
|
|
||||||
++sentCount;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
CoreLogger::error("cobra bot: error sending");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else if (event->type == ix::CobraEventType::Subscribed)
|
else if (event->type == ix::CobraEventType::Subscribed)
|
||||||
@ -308,9 +258,6 @@ namespace ix
|
|||||||
// heartbeat thread
|
// heartbeat thread
|
||||||
if (t2.joinable()) t2.join();
|
if (t2.joinable()) t2.join();
|
||||||
|
|
||||||
// sentry sender thread
|
|
||||||
t3.join();
|
|
||||||
|
|
||||||
return fatalCobraError ? -1 : (int64_t) sentCount;
|
return fatalCobraError ? -1 : (int64_t) sentCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,33 +8,24 @@
|
|||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <ixcobra/IXCobraConfig.h>
|
#include "IXCobraBotConfig.h"
|
||||||
#include <json/json.h>
|
#include <json/json.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
using OnBotMessageCallback = std::function<bool(const Json::Value&,
|
using OnBotMessageCallback = std::function<void(const Json::Value&,
|
||||||
const std::string&,
|
const std::string&,
|
||||||
const bool verbose,
|
|
||||||
std::atomic<bool>&,
|
std::atomic<bool>&,
|
||||||
std::atomic<bool>&)>;
|
std::atomic<bool>&,
|
||||||
|
std::atomic<uint64_t>&)>;
|
||||||
|
|
||||||
class CobraBot
|
class CobraBot
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CobraBot() = default;
|
CobraBot() = default;
|
||||||
|
|
||||||
int64_t run(const CobraConfig& config,
|
int64_t run(const CobraBotConfig& botConfig);
|
||||||
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);
|
void setOnBotMessageCallback(const OnBotMessageCallback& callback);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
31
ixbots/ixbots/IXCobraBotConfig.h
Normal file
31
ixbots/ixbots/IXCobraBotConfig.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* IXCobraBotConfig.h
|
||||||
|
* Author: Benjamin Sergeant
|
||||||
|
* Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <limits>
|
||||||
|
#include <ixcobra/IXCobraConfig.h>
|
||||||
|
|
||||||
|
#ifdef max
|
||||||
|
#undef max
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace ix
|
||||||
|
{
|
||||||
|
struct CobraBotConfig
|
||||||
|
{
|
||||||
|
CobraConfig cobraConfig;
|
||||||
|
std::string channel;
|
||||||
|
std::string filter;
|
||||||
|
std::string position = std::string("$");
|
||||||
|
bool enableHeartbeat = true;
|
||||||
|
int heartBeatTimeout = 60;
|
||||||
|
int runtime = -1;
|
||||||
|
int maxEventsPerMinute = std::numeric_limits<int>::max();
|
||||||
|
bool limitReceivedEvents = false;
|
||||||
|
};
|
||||||
|
} // namespace ix
|
@ -7,7 +7,6 @@
|
|||||||
#include "IXCobraToSentryBot.h"
|
#include "IXCobraToSentryBot.h"
|
||||||
|
|
||||||
#include "IXCobraBot.h"
|
#include "IXCobraBot.h"
|
||||||
#include "IXQueueManager.h"
|
|
||||||
#include <ixcobra/IXCobraConnection.h>
|
#include <ixcobra/IXCobraConnection.h>
|
||||||
#include <ixcore/utils/IXCoreLogger.h>
|
#include <ixcore/utils/IXCoreLogger.h>
|
||||||
|
|
||||||
@ -17,60 +16,31 @@
|
|||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
int64_t cobra_to_sentry_bot(const CobraConfig& config,
|
int64_t cobra_to_sentry_bot(const CobraBotConfig& config,
|
||||||
const std::string& channel,
|
|
||||||
const std::string& filter,
|
|
||||||
const std::string& position,
|
|
||||||
SentryClient& sentryClient,
|
SentryClient& sentryClient,
|
||||||
bool verbose,
|
bool verbose)
|
||||||
size_t maxQueueSize,
|
|
||||||
bool enableHeartbeat,
|
|
||||||
int runtime)
|
|
||||||
{
|
{
|
||||||
CobraBot bot;
|
CobraBot bot;
|
||||||
bot.setOnBotMessageCallback([&sentryClient](const Json::Value& msg,
|
bot.setOnBotMessageCallback([&sentryClient, &verbose](const Json::Value& msg,
|
||||||
const std::string& /*position*/,
|
const std::string& /*position*/,
|
||||||
const bool verbose,
|
|
||||||
std::atomic<bool>& throttled,
|
std::atomic<bool>& throttled,
|
||||||
std::atomic<bool> &
|
std::atomic<bool>& /*fatalCobraError*/,
|
||||||
/*fatalCobraError*/) -> bool {
|
std::atomic<uint64_t>& sentCount) -> void {
|
||||||
auto ret = sentryClient.send(msg, verbose);
|
sentryClient.send(msg, verbose,
|
||||||
HttpResponsePtr response = ret.first;
|
[&sentCount, &throttled](const HttpResponsePtr& response) {
|
||||||
|
|
||||||
if (!response)
|
if (!response)
|
||||||
{
|
{
|
||||||
CoreLogger::warn("Null HTTP Response");
|
CoreLogger::warn("Null HTTP Response");
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (verbose)
|
if (response->statusCode == 200)
|
||||||
{
|
{
|
||||||
for (auto it : response->headers)
|
sentCount++;
|
||||||
{
|
|
||||||
CoreLogger::info(it.first + ": " + it.second);
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
CoreLogger::info("Upload size: " + std::to_string(response->uploadSize));
|
|
||||||
CoreLogger::info("Download size: " + std::to_string(response->downloadSize));
|
|
||||||
|
|
||||||
CoreLogger::info("Status: " + std::to_string(response->statusCode));
|
|
||||||
if (response->errorCode != HttpErrorCode::Ok)
|
|
||||||
{
|
|
||||||
CoreLogger::info("error message: " + response->errorMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response->headers["Content-Type"] != "application/octet-stream")
|
|
||||||
{
|
|
||||||
CoreLogger::info("payload: " + response->payload);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool success = response->statusCode == 200;
|
|
||||||
|
|
||||||
if (!success)
|
|
||||||
{
|
{
|
||||||
CoreLogger::error("Error sending data to sentry: " + std::to_string(response->statusCode));
|
CoreLogger::error("Error sending data to sentry: " + std::to_string(response->statusCode));
|
||||||
CoreLogger::error("Body: " + ret.second);
|
|
||||||
CoreLogger::error("Response: " + response->payload);
|
CoreLogger::error("Response: " + response->payload);
|
||||||
|
|
||||||
// Error 429 Too Many Requests
|
// Error 429 Too Many Requests
|
||||||
@ -98,20 +68,9 @@ namespace ix
|
|||||||
throttled = false;
|
throttled = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
return success;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
bool useQueue = true;
|
return bot.run(config);
|
||||||
|
|
||||||
return bot.run(config,
|
|
||||||
channel,
|
|
||||||
filter,
|
|
||||||
position,
|
|
||||||
verbose,
|
|
||||||
maxQueueSize,
|
|
||||||
useQueue,
|
|
||||||
enableHeartbeat,
|
|
||||||
runtime);
|
|
||||||
}
|
}
|
||||||
} // namespace ix
|
} // namespace ix
|
||||||
|
@ -6,19 +6,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <ixcobra/IXCobraConfig.h>
|
#include "IXCobraBotConfig.h"
|
||||||
#include <ixsentry/IXSentryClient.h>
|
#include <ixsentry/IXSentryClient.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
int64_t cobra_to_sentry_bot(const CobraConfig& config,
|
int64_t cobra_to_sentry_bot(const CobraBotConfig& config,
|
||||||
const std::string& channel,
|
|
||||||
const std::string& filter,
|
|
||||||
const std::string& position,
|
|
||||||
SentryClient& sentryClient,
|
SentryClient& sentryClient,
|
||||||
bool verbose,
|
bool verbose);
|
||||||
size_t maxQueueSize,
|
|
||||||
bool enableHeartbeat,
|
|
||||||
int runtime);
|
|
||||||
} // namespace ix
|
} // namespace ix
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
#include "IXCobraToStatsdBot.h"
|
#include "IXCobraToStatsdBot.h"
|
||||||
|
|
||||||
#include "IXCobraBot.h"
|
#include "IXCobraBot.h"
|
||||||
#include "IXQueueManager.h"
|
|
||||||
#include "IXStatsdClient.h"
|
#include "IXStatsdClient.h"
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <ixcobra/IXCobraConnection.h>
|
#include <ixcobra/IXCobraConnection.h>
|
||||||
@ -54,32 +53,22 @@ namespace ix
|
|||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t cobra_to_statsd_bot(const ix::CobraConfig& config,
|
int64_t cobra_to_statsd_bot(const ix::CobraBotConfig& config,
|
||||||
const std::string& channel,
|
|
||||||
const std::string& filter,
|
|
||||||
const std::string& position,
|
|
||||||
StatsdClient& statsdClient,
|
StatsdClient& statsdClient,
|
||||||
const std::string& fields,
|
const std::string& fields,
|
||||||
const std::string& gauge,
|
const std::string& gauge,
|
||||||
const std::string& timer,
|
const std::string& timer,
|
||||||
bool verbose,
|
bool verbose)
|
||||||
size_t maxQueueSize,
|
|
||||||
bool enableHeartbeat,
|
|
||||||
int runtime)
|
|
||||||
{
|
{
|
||||||
ix::CobraConnection conn;
|
|
||||||
conn.configure(config);
|
|
||||||
conn.connect();
|
|
||||||
|
|
||||||
auto tokens = parseFields(fields);
|
auto tokens = parseFields(fields);
|
||||||
|
|
||||||
CobraBot bot;
|
CobraBot bot;
|
||||||
bot.setOnBotMessageCallback(
|
bot.setOnBotMessageCallback(
|
||||||
[&statsdClient, &tokens, &gauge, &timer](const Json::Value& msg,
|
[&statsdClient, &tokens, &gauge, &timer, &verbose](const Json::Value& msg,
|
||||||
const std::string& /*position*/,
|
const std::string& /*position*/,
|
||||||
const bool verbose,
|
|
||||||
std::atomic<bool>& /*throttled*/,
|
std::atomic<bool>& /*throttled*/,
|
||||||
std::atomic<bool>& fatalCobraError) -> bool {
|
std::atomic<bool>& fatalCobraError,
|
||||||
|
std::atomic<uint64_t>& sentCount) -> void {
|
||||||
std::string id;
|
std::string id;
|
||||||
for (auto&& attr : tokens)
|
for (auto&& attr : tokens)
|
||||||
{
|
{
|
||||||
@ -122,7 +111,7 @@ namespace ix
|
|||||||
{
|
{
|
||||||
CoreLogger::error("Gauge " + gauge + " is not a numeric type");
|
CoreLogger::error("Gauge " + gauge + " is not a numeric type");
|
||||||
fatalCobraError = true;
|
fatalCobraError = true;
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (verbose)
|
if (verbose)
|
||||||
@ -140,19 +129,9 @@ namespace ix
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
sentCount++;
|
||||||
});
|
});
|
||||||
|
|
||||||
bool useQueue = true;
|
return bot.run(config);
|
||||||
|
|
||||||
return bot.run(config,
|
|
||||||
channel,
|
|
||||||
filter,
|
|
||||||
position,
|
|
||||||
verbose,
|
|
||||||
maxQueueSize,
|
|
||||||
useQueue,
|
|
||||||
enableHeartbeat,
|
|
||||||
runtime);
|
|
||||||
}
|
}
|
||||||
} // namespace ix
|
} // namespace ix
|
||||||
|
@ -7,22 +7,16 @@
|
|||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <ixbots/IXStatsdClient.h>
|
#include <ixbots/IXStatsdClient.h>
|
||||||
#include <ixcobra/IXCobraConfig.h>
|
#include "IXCobraBotConfig.h"
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
int64_t cobra_to_statsd_bot(const ix::CobraConfig& config,
|
int64_t cobra_to_statsd_bot(const ix::CobraBotConfig& config,
|
||||||
const std::string& channel,
|
|
||||||
const std::string& filter,
|
|
||||||
const std::string& position,
|
|
||||||
StatsdClient& statsdClient,
|
StatsdClient& statsdClient,
|
||||||
const std::string& fields,
|
const std::string& fields,
|
||||||
const std::string& gauge,
|
const std::string& gauge,
|
||||||
const std::string& timer,
|
const std::string& timer,
|
||||||
bool verbose,
|
bool verbose);
|
||||||
size_t maxQueueSize,
|
|
||||||
bool enableHeartbeat,
|
|
||||||
int runtime);
|
|
||||||
} // namespace ix
|
} // namespace ix
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
#include "IXCobraToStdoutBot.h"
|
#include "IXCobraToStdoutBot.h"
|
||||||
|
|
||||||
#include "IXCobraBot.h"
|
#include "IXCobraBot.h"
|
||||||
#include "IXQueueManager.h"
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
@ -64,16 +63,9 @@ namespace ix
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t cobra_to_stdout_bot(const CobraConfig& config,
|
int64_t cobra_to_stdout_bot(const ix::CobraBotConfig& config,
|
||||||
const std::string& channel,
|
|
||||||
const std::string& filter,
|
|
||||||
const std::string& position,
|
|
||||||
bool fluentd,
|
bool fluentd,
|
||||||
bool quiet,
|
bool quiet)
|
||||||
bool verbose,
|
|
||||||
size_t maxQueueSize,
|
|
||||||
bool enableHeartbeat,
|
|
||||||
int runtime)
|
|
||||||
{
|
{
|
||||||
CobraBot bot;
|
CobraBot bot;
|
||||||
auto jsonWriter = makeStreamWriter();
|
auto jsonWriter = makeStreamWriter();
|
||||||
@ -81,27 +73,16 @@ namespace ix
|
|||||||
bot.setOnBotMessageCallback(
|
bot.setOnBotMessageCallback(
|
||||||
[&fluentd, &quiet, &jsonWriter](const Json::Value& msg,
|
[&fluentd, &quiet, &jsonWriter](const Json::Value& msg,
|
||||||
const std::string& position,
|
const std::string& position,
|
||||||
const bool /*verbose*/,
|
|
||||||
std::atomic<bool>& /*throttled*/,
|
std::atomic<bool>& /*throttled*/,
|
||||||
std::atomic<bool> &
|
std::atomic<bool>& /*fatalCobraError*/,
|
||||||
/*fatalCobraError*/) -> bool {
|
std::atomic<uint64_t>& sentCount) -> void {
|
||||||
if (!quiet)
|
if (!quiet)
|
||||||
{
|
{
|
||||||
writeToStdout(fluentd, jsonWriter, msg, position);
|
writeToStdout(fluentd, jsonWriter, msg, position);
|
||||||
}
|
}
|
||||||
return true;
|
sentCount++;
|
||||||
});
|
});
|
||||||
|
|
||||||
bool useQueue = false;
|
return bot.run(config);
|
||||||
|
|
||||||
return bot.run(config,
|
|
||||||
channel,
|
|
||||||
filter,
|
|
||||||
position,
|
|
||||||
verbose,
|
|
||||||
maxQueueSize,
|
|
||||||
useQueue,
|
|
||||||
enableHeartbeat,
|
|
||||||
runtime);
|
|
||||||
}
|
}
|
||||||
} // namespace ix
|
} // namespace ix
|
||||||
|
@ -6,20 +6,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <ixcobra/IXCobraConfig.h>
|
#include "IXCobraBotConfig.h"
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
int64_t cobra_to_stdout_bot(const ix::CobraConfig& config,
|
int64_t cobra_to_stdout_bot(const ix::CobraBotConfig& config,
|
||||||
const std::string& channel,
|
|
||||||
const std::string& filter,
|
|
||||||
const std::string& position,
|
|
||||||
bool fluentd,
|
bool fluentd,
|
||||||
bool quiet,
|
bool quiet);
|
||||||
bool verbose,
|
|
||||||
size_t maxQueueSize,
|
|
||||||
bool enableHeartbeat,
|
|
||||||
int runtime);
|
|
||||||
} // namespace ix
|
} // namespace ix
|
||||||
|
@ -1,67 +0,0 @@
|
|||||||
/*
|
|
||||||
* IXQueueManager.cpp
|
|
||||||
* Author: Benjamin Sergeant
|
|
||||||
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "IXQueueManager.h"
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace ix
|
|
||||||
{
|
|
||||||
std::pair<Json::Value, std::string> QueueManager::pop()
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lock(_mutex);
|
|
||||||
|
|
||||||
if (_queues.empty())
|
|
||||||
{
|
|
||||||
Json::Value val;
|
|
||||||
return std::make_pair(val, std::string());
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> games;
|
|
||||||
for (auto it : _queues)
|
|
||||||
{
|
|
||||||
games.push_back(it.first);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::random_shuffle(games.begin(), games.end());
|
|
||||||
std::string game = games[0];
|
|
||||||
|
|
||||||
auto duration = std::chrono::seconds(1);
|
|
||||||
_condition.wait_for(lock, duration);
|
|
||||||
|
|
||||||
if (_queues[game].empty())
|
|
||||||
{
|
|
||||||
Json::Value val;
|
|
||||||
return std::make_pair(val, std::string());
|
|
||||||
}
|
|
||||||
|
|
||||||
auto msg = _queues[game].front();
|
|
||||||
_queues[game].pop();
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
void QueueManager::add(const Json::Value& msg, const std::string& position)
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lock(_mutex);
|
|
||||||
|
|
||||||
std::string game;
|
|
||||||
if (msg.isMember("device") && msg["device"].isMember("game"))
|
|
||||||
{
|
|
||||||
game = msg["device"]["game"].asString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (game.empty()) return;
|
|
||||||
|
|
||||||
// if the sending is not fast enough there is no point
|
|
||||||
// in queuing too many events.
|
|
||||||
if (_queues[game].size() < _maxQueueSize)
|
|
||||||
{
|
|
||||||
_queues[game].push(std::make_pair(msg, position));
|
|
||||||
_condition.notify_one();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // namespace ix
|
|
@ -1,35 +0,0 @@
|
|||||||
/*
|
|
||||||
* IXQueueManager.h
|
|
||||||
* Author: Benjamin Sergeant
|
|
||||||
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <condition_variable>
|
|
||||||
#include <json/json.h>
|
|
||||||
#include <map>
|
|
||||||
#include <mutex>
|
|
||||||
#include <queue>
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
namespace ix
|
|
||||||
{
|
|
||||||
class QueueManager
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
QueueManager(size_t maxQueueSize)
|
|
||||||
: _maxQueueSize(maxQueueSize)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<Json::Value, std::string> pop();
|
|
||||||
void add(const Json::Value& msg, const std::string& position);
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::map<std::string, std::queue<std::pair<Json::Value, std::string>>> _queues;
|
|
||||||
std::mutex _mutex;
|
|
||||||
std::condition_variable _condition;
|
|
||||||
size_t _maxQueueSize;
|
|
||||||
};
|
|
||||||
} // namespace ix
|
|
@ -37,9 +37,7 @@ if (USE_MBED_TLS)
|
|||||||
target_include_directories(ixcrypto PUBLIC ${MBEDTLS_INCLUDE_DIRS})
|
target_include_directories(ixcrypto PUBLIC ${MBEDTLS_INCLUDE_DIRS})
|
||||||
target_link_libraries(ixcrypto ${MBEDTLS_LIBRARIES})
|
target_link_libraries(ixcrypto ${MBEDTLS_LIBRARIES})
|
||||||
target_compile_definitions(ixcrypto PUBLIC IXCRYPTO_USE_MBED_TLS)
|
target_compile_definitions(ixcrypto PUBLIC IXCRYPTO_USE_MBED_TLS)
|
||||||
elseif (APPLE)
|
elseif (USE_OPEN_SSL)
|
||||||
elseif (WIN32)
|
|
||||||
else()
|
|
||||||
find_package(OpenSSL REQUIRED)
|
find_package(OpenSSL REQUIRED)
|
||||||
add_definitions(${OPENSSL_DEFINITIONS})
|
add_definitions(${OPENSSL_DEFINITIONS})
|
||||||
message(STATUS "OpenSSL: " ${OPENSSL_VERSION})
|
message(STATUS "OpenSSL: " ${OPENSSL_VERSION})
|
||||||
|
@ -226,20 +226,23 @@ namespace ix
|
|||||||
return _jsonWriter.write(payload);
|
return _jsonWriter.write(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<HttpResponsePtr, std::string> SentryClient::send(const Json::Value& msg, bool verbose)
|
void SentryClient::send(
|
||||||
|
const Json::Value& msg,
|
||||||
|
bool verbose,
|
||||||
|
const OnResponseCallback& onResponseCallback)
|
||||||
{
|
{
|
||||||
auto args = _httpClient->createRequest();
|
auto args = _httpClient->createRequest();
|
||||||
|
args->url = _url;
|
||||||
|
args->verb = HttpClient::kPost;
|
||||||
args->extraHeaders["X-Sentry-Auth"] = SentryClient::computeAuthHeader();
|
args->extraHeaders["X-Sentry-Auth"] = SentryClient::computeAuthHeader();
|
||||||
args->connectTimeout = 60;
|
args->connectTimeout = 60;
|
||||||
args->transferTimeout = 5 * 60;
|
args->transferTimeout = 5 * 60;
|
||||||
args->followRedirects = true;
|
args->followRedirects = true;
|
||||||
args->verbose = verbose;
|
args->verbose = verbose;
|
||||||
args->logger = [](const std::string& msg) { CoreLogger::log(msg.c_str()); };
|
args->logger = [](const std::string& msg) { CoreLogger::log(msg.c_str()); };
|
||||||
|
args->body = computePayload(msg);
|
||||||
|
|
||||||
std::string body = computePayload(msg);
|
_httpClient->performRequest(args, onResponseCallback);
|
||||||
HttpResponsePtr response = _httpClient->post(_url, body, args);
|
|
||||||
|
|
||||||
return std::make_pair(response, body);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://sentry.io/api/12345/minidump?sentry_key=abcdefgh");
|
// https://sentry.io/api/12345/minidump?sentry_key=abcdefgh");
|
||||||
|
@ -21,12 +21,9 @@ namespace ix
|
|||||||
SentryClient(const std::string& dsn);
|
SentryClient(const std::string& dsn);
|
||||||
~SentryClient() = default;
|
~SentryClient() = default;
|
||||||
|
|
||||||
std::pair<HttpResponsePtr, std::string> send(const Json::Value& msg, bool verbose);
|
void send(const Json::Value& msg,
|
||||||
|
bool verbose,
|
||||||
Json::Value parseLuaStackTrace(const std::string& stack);
|
const OnResponseCallback& onResponseCallback);
|
||||||
|
|
||||||
// Mostly for testing
|
|
||||||
void setTLSOptions(const SocketTLSOptions& tlsOptions);
|
|
||||||
|
|
||||||
void uploadMinidump(const std::string& sentryMetadata,
|
void uploadMinidump(const std::string& sentryMetadata,
|
||||||
const std::string& minidumpBytes,
|
const std::string& minidumpBytes,
|
||||||
@ -39,6 +36,12 @@ namespace ix
|
|||||||
bool verbose,
|
bool verbose,
|
||||||
const OnResponseCallback& onResponseCallback);
|
const OnResponseCallback& onResponseCallback);
|
||||||
|
|
||||||
|
Json::Value parseLuaStackTrace(const std::string& stack);
|
||||||
|
|
||||||
|
// Mostly for testing
|
||||||
|
void setTLSOptions(const SocketTLSOptions& tlsOptions);
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int64_t getTimestamp();
|
int64_t getTimestamp();
|
||||||
std::string computeAuthHeader();
|
std::string computeAuthHeader();
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include "IXCancellationRequest.h"
|
#include "IXCancellationRequest.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
@ -13,6 +14,8 @@ namespace ix
|
|||||||
CancellationRequest makeCancellationRequestWithTimeout(
|
CancellationRequest makeCancellationRequestWithTimeout(
|
||||||
int secs, std::atomic<bool>& requestInitCancellation)
|
int secs, std::atomic<bool>& requestInitCancellation)
|
||||||
{
|
{
|
||||||
|
assert(secs > 0);
|
||||||
|
|
||||||
auto start = std::chrono::system_clock::now();
|
auto start = std::chrono::system_clock::now();
|
||||||
auto timeout = std::chrono::seconds(secs);
|
auto timeout = std::chrono::seconds(secs);
|
||||||
|
|
||||||
|
@ -4,6 +4,19 @@
|
|||||||
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
|
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
//
|
||||||
|
// On Windows Universal Platform (uwp), gai_strerror defaults behavior is to returns wchar_t
|
||||||
|
// which is different from all other platforms. We want the non unicode version.
|
||||||
|
// See https://github.com/microsoft/vcpkg/pull/11030
|
||||||
|
// We could do this in IXNetSystem.cpp but so far we are only using gai_strerror in here.
|
||||||
|
//
|
||||||
|
#ifdef _UNICODE
|
||||||
|
#undef _UNICODE
|
||||||
|
#endif
|
||||||
|
#ifdef UNICODE
|
||||||
|
#undef UNICODE
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "IXDNSLookup.h"
|
#include "IXDNSLookup.h"
|
||||||
|
|
||||||
#include "IXNetSystem.h"
|
#include "IXNetSystem.h"
|
||||||
|
@ -25,10 +25,12 @@ namespace ix
|
|||||||
const std::string HttpClient::kHead = "HEAD";
|
const std::string HttpClient::kHead = "HEAD";
|
||||||
const std::string HttpClient::kDel = "DEL";
|
const std::string HttpClient::kDel = "DEL";
|
||||||
const std::string HttpClient::kPut = "PUT";
|
const std::string HttpClient::kPut = "PUT";
|
||||||
|
const std::string HttpClient::kPatch = "PATCH";
|
||||||
|
|
||||||
HttpClient::HttpClient(bool async)
|
HttpClient::HttpClient(bool async)
|
||||||
: _async(async)
|
: _async(async)
|
||||||
, _stop(false)
|
, _stop(false)
|
||||||
|
, _forceBody(false)
|
||||||
{
|
{
|
||||||
if (!_async) return;
|
if (!_async) return;
|
||||||
|
|
||||||
@ -49,6 +51,11 @@ namespace ix
|
|||||||
_tlsOptions = tlsOptions;
|
_tlsOptions = tlsOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HttpClient::setForceBody(bool value)
|
||||||
|
{
|
||||||
|
_forceBody = value;
|
||||||
|
}
|
||||||
|
|
||||||
HttpRequestArgsPtr HttpClient::createRequest(const std::string& url, const std::string& verb)
|
HttpRequestArgsPtr HttpClient::createRequest(const std::string& url, const std::string& verb)
|
||||||
{
|
{
|
||||||
auto request = std::make_shared<HttpRequestArgs>();
|
auto request = std::make_shared<HttpRequestArgs>();
|
||||||
@ -192,7 +199,7 @@ namespace ix
|
|||||||
ss << "User-Agent: " << userAgent() << "\r\n";
|
ss << "User-Agent: " << userAgent() << "\r\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (verb == kPost || verb == kPut)
|
if (verb == kPost || verb == kPut || verb == kPatch || _forceBody)
|
||||||
{
|
{
|
||||||
ss << "Content-Length: " << body.size() << "\r\n";
|
ss << "Content-Length: " << body.size() << "\r\n";
|
||||||
|
|
||||||
@ -220,11 +227,10 @@ namespace ix
|
|||||||
|
|
||||||
std::string req(ss.str());
|
std::string req(ss.str());
|
||||||
std::string errMsg;
|
std::string errMsg;
|
||||||
std::atomic<bool> requestInitCancellation(false);
|
|
||||||
|
|
||||||
// Make a cancellation object dealing with connection timeout
|
// Make a cancellation object dealing with connection timeout
|
||||||
auto isCancellationRequested =
|
auto isCancellationRequested =
|
||||||
makeCancellationRequestWithTimeout(args->connectTimeout, requestInitCancellation);
|
makeCancellationRequestWithTimeout(args->connectTimeout, _stop);
|
||||||
|
|
||||||
bool success = _socket->connect(host, port, errMsg, isCancellationRequested);
|
bool success = _socket->connect(host, port, errMsg, isCancellationRequested);
|
||||||
if (!success)
|
if (!success)
|
||||||
@ -242,8 +248,7 @@ namespace ix
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Make a new cancellation object dealing with transfer timeout
|
// Make a new cancellation object dealing with transfer timeout
|
||||||
isCancellationRequested =
|
isCancellationRequested = makeCancellationRequestWithTimeout(args->transferTimeout, _stop);
|
||||||
makeCancellationRequestWithTimeout(args->transferTimeout, requestInitCancellation);
|
|
||||||
|
|
||||||
if (args->verbose)
|
if (args->verbose)
|
||||||
{
|
{
|
||||||
@ -562,6 +567,20 @@ namespace ix
|
|||||||
return request(url, kPut, body, args);
|
return request(url, kPut, body, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HttpResponsePtr HttpClient::patch(const std::string& url,
|
||||||
|
const HttpParameters& httpParameters,
|
||||||
|
HttpRequestArgsPtr args)
|
||||||
|
{
|
||||||
|
return request(url, kPatch, serializeHttpParameters(httpParameters), args);
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpResponsePtr HttpClient::patch(const std::string& url,
|
||||||
|
const std::string& body,
|
||||||
|
const HttpRequestArgsPtr args)
|
||||||
|
{
|
||||||
|
return request(url, kPatch, body, args);
|
||||||
|
}
|
||||||
|
|
||||||
std::string HttpClient::urlEncode(const std::string& value)
|
std::string HttpClient::urlEncode(const std::string& value)
|
||||||
{
|
{
|
||||||
std::ostringstream escaped;
|
std::ostringstream escaped;
|
||||||
|
@ -46,12 +46,19 @@ namespace ix
|
|||||||
const std::string& body,
|
const std::string& body,
|
||||||
HttpRequestArgsPtr args);
|
HttpRequestArgsPtr args);
|
||||||
|
|
||||||
|
HttpResponsePtr patch(const std::string& url,
|
||||||
|
const HttpParameters& httpParameters,
|
||||||
|
HttpRequestArgsPtr args);
|
||||||
|
HttpResponsePtr patch(const std::string& url,
|
||||||
|
const std::string& body,
|
||||||
|
HttpRequestArgsPtr args);
|
||||||
|
|
||||||
HttpResponsePtr request(const std::string& url,
|
HttpResponsePtr request(const std::string& url,
|
||||||
const std::string& verb,
|
const std::string& verb,
|
||||||
const std::string& body,
|
const std::string& body,
|
||||||
HttpRequestArgsPtr args,
|
HttpRequestArgsPtr args,
|
||||||
int redirects = 0);
|
int redirects = 0);
|
||||||
|
void setForceBody(bool value);
|
||||||
// Async API
|
// Async API
|
||||||
HttpRequestArgsPtr createRequest(const std::string& url = std::string(),
|
HttpRequestArgsPtr createRequest(const std::string& url = std::string(),
|
||||||
const std::string& verb = HttpClient::kGet);
|
const std::string& verb = HttpClient::kGet);
|
||||||
@ -78,6 +85,7 @@ namespace ix
|
|||||||
const static std::string kHead;
|
const static std::string kHead;
|
||||||
const static std::string kDel;
|
const static std::string kDel;
|
||||||
const static std::string kPut;
|
const static std::string kPut;
|
||||||
|
const static std::string kPatch;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void log(const std::string& msg, HttpRequestArgsPtr args);
|
void log(const std::string& msg, HttpRequestArgsPtr args);
|
||||||
@ -86,7 +94,6 @@ namespace ix
|
|||||||
|
|
||||||
// Async API background thread runner
|
// Async API background thread runner
|
||||||
void run();
|
void run();
|
||||||
|
|
||||||
// Async API
|
// Async API
|
||||||
bool _async;
|
bool _async;
|
||||||
std::queue<std::pair<HttpRequestArgsPtr, OnResponseCallback>> _queue;
|
std::queue<std::pair<HttpRequestArgsPtr, OnResponseCallback>> _queue;
|
||||||
@ -99,5 +106,7 @@ namespace ix
|
|||||||
std::mutex _mutex; // to protect accessing the _socket (only one socket per client)
|
std::mutex _mutex; // to protect accessing the _socket (only one socket per client)
|
||||||
|
|
||||||
SocketTLSOptions _tlsOptions;
|
SocketTLSOptions _tlsOptions;
|
||||||
|
|
||||||
|
bool _forceBody;
|
||||||
};
|
};
|
||||||
} // namespace ix
|
} // namespace ix
|
||||||
|
@ -12,6 +12,8 @@
|
|||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <zlib.h>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
@ -38,6 +40,47 @@ namespace
|
|||||||
auto vec = res.second;
|
auto vec = res.second;
|
||||||
return std::make_pair(res.first, std::string(vec.begin(), vec.end()));
|
return std::make_pair(res.first, std::string(vec.begin(), vec.end()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string gzipCompress(const std::string& str)
|
||||||
|
{
|
||||||
|
z_stream zs; // z_stream is zlib's control structure
|
||||||
|
memset(&zs, 0, sizeof(zs));
|
||||||
|
|
||||||
|
// deflateInit2 configure the file format: request gzip instead of deflate
|
||||||
|
const int windowBits = 15;
|
||||||
|
const int GZIP_ENCODING = 16;
|
||||||
|
|
||||||
|
deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
|
||||||
|
windowBits | GZIP_ENCODING, 8,
|
||||||
|
Z_DEFAULT_STRATEGY);
|
||||||
|
|
||||||
|
zs.next_in = (Bytef*) str.data();
|
||||||
|
zs.avail_in = (uInt) str.size(); // set the z_stream's input
|
||||||
|
|
||||||
|
int ret;
|
||||||
|
char outbuffer[32768];
|
||||||
|
std::string outstring;
|
||||||
|
|
||||||
|
// retrieve the compressed bytes blockwise
|
||||||
|
do
|
||||||
|
{
|
||||||
|
zs.next_out = reinterpret_cast<Bytef*>(outbuffer);
|
||||||
|
zs.avail_out = sizeof(outbuffer);
|
||||||
|
|
||||||
|
ret = deflate(&zs, Z_FINISH);
|
||||||
|
|
||||||
|
if(outstring.size() < zs.total_out)
|
||||||
|
{
|
||||||
|
// append the block to the output string
|
||||||
|
outstring.append(outbuffer,
|
||||||
|
zs.total_out - outstring.size());
|
||||||
|
}
|
||||||
|
} while(ret == Z_OK);
|
||||||
|
|
||||||
|
deflateEnd(&zs);
|
||||||
|
|
||||||
|
return outstring;
|
||||||
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
@ -120,6 +163,12 @@ namespace ix
|
|||||||
|
|
||||||
std::string content = res.second;
|
std::string content = res.second;
|
||||||
|
|
||||||
|
std::string acceptEncoding = request->headers["Accept-encoding"];
|
||||||
|
if (acceptEncoding == "gzip" || acceptEncoding == "*")
|
||||||
|
{
|
||||||
|
content = gzipCompress(content);
|
||||||
|
}
|
||||||
|
|
||||||
// Log request
|
// Log request
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << request->method << " " << request->headers["User-Agent"] << " "
|
ss << request->method << " " << request->headers["User-Agent"] << " "
|
||||||
|
@ -19,6 +19,7 @@ typedef unsigned long int nfds_t;
|
|||||||
#else
|
#else
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <netinet/ip.h>
|
#include <netinet/ip.h>
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include "IXSelectInterruptFactory.h"
|
#include "IXSelectInterruptFactory.h"
|
||||||
|
|
||||||
|
#include "IXUniquePtr.h"
|
||||||
#if defined(__linux__) || defined(__APPLE__)
|
#if defined(__linux__) || defined(__APPLE__)
|
||||||
#include "IXSelectInterruptPipe.h"
|
#include "IXSelectInterruptPipe.h"
|
||||||
#else
|
#else
|
||||||
@ -17,9 +18,9 @@ namespace ix
|
|||||||
SelectInterruptPtr createSelectInterrupt()
|
SelectInterruptPtr createSelectInterrupt()
|
||||||
{
|
{
|
||||||
#if defined(__linux__) || defined(__APPLE__)
|
#if defined(__linux__) || defined(__APPLE__)
|
||||||
return std::make_unique<SelectInterruptPipe>();
|
return ix::make_unique<SelectInterruptPipe>();
|
||||||
#else
|
#else
|
||||||
return std::make_unique<SelectInterrupt>();
|
return ix::make_unique<SelectInterrupt>();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
} // namespace ix
|
} // namespace ix
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#include "IXNetSystem.h"
|
#include "IXNetSystem.h"
|
||||||
#include "IXSelectInterrupt.h"
|
#include "IXSelectInterrupt.h"
|
||||||
#include "IXSocket.h"
|
#include "IXSocket.h"
|
||||||
|
#include "IXUniquePtr.h"
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
@ -65,7 +66,7 @@ namespace ix
|
|||||||
|
|
||||||
int timeoutMs = 10;
|
int timeoutMs = 10;
|
||||||
bool readyToRead = false;
|
bool readyToRead = false;
|
||||||
auto selectInterrupt = std::make_unique<SelectInterrupt>();
|
auto selectInterrupt = ix::make_unique<SelectInterrupt>();
|
||||||
PollResultType pollResult = Socket::poll(readyToRead, timeoutMs, fd, selectInterrupt);
|
PollResultType pollResult = Socket::poll(readyToRead, timeoutMs, fd, selectInterrupt);
|
||||||
|
|
||||||
if (pollResult == PollResultType::Timeout)
|
if (pollResult == PollResultType::Timeout)
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "IXSocketFactory.h"
|
#include "IXSocketFactory.h"
|
||||||
|
#include "IXUniquePtr.h"
|
||||||
|
|
||||||
#ifdef IXWEBSOCKET_USE_TLS
|
#ifdef IXWEBSOCKET_USE_TLS
|
||||||
|
|
||||||
@ -35,17 +36,17 @@ namespace ix
|
|||||||
|
|
||||||
if (!tls)
|
if (!tls)
|
||||||
{
|
{
|
||||||
socket = std::make_unique<Socket>(fd);
|
socket = ix::make_unique<Socket>(fd);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
#ifdef IXWEBSOCKET_USE_TLS
|
#ifdef IXWEBSOCKET_USE_TLS
|
||||||
#if defined(IXWEBSOCKET_USE_MBED_TLS)
|
#if defined(IXWEBSOCKET_USE_MBED_TLS)
|
||||||
socket = std::make_unique<SocketMbedTLS>(tlsOptions, fd);
|
socket = ix::make_unique<SocketMbedTLS>(tlsOptions, fd);
|
||||||
#elif defined(IXWEBSOCKET_USE_OPEN_SSL)
|
#elif defined(IXWEBSOCKET_USE_OPEN_SSL)
|
||||||
socket = std::make_unique<SocketOpenSSL>(tlsOptions, fd);
|
socket = ix::make_unique<SocketOpenSSL>(tlsOptions, fd);
|
||||||
#elif defined(__APPLE__)
|
#elif defined(__APPLE__)
|
||||||
socket = std::make_unique<SocketAppleSSL>(tlsOptions, fd);
|
socket = ix::make_unique<SocketAppleSSL>(tlsOptions, fd);
|
||||||
#endif
|
#endif
|
||||||
#else
|
#else
|
||||||
errorMsg = "TLS support is not enabled on this platform.";
|
errorMsg = "TLS support is not enabled on this platform.";
|
||||||
|
@ -43,6 +43,55 @@ namespace ix
|
|||||||
mbedtls_pk_init(&_pkey);
|
mbedtls_pk_init(&_pkey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SocketMbedTLS::loadSystemCertificates(std::string& errorMsg)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
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;
|
||||||
|
|
||||||
|
int certificateCount = 0;
|
||||||
|
while (certificateIterator = CertEnumCertificatesInStore(systemStore, certificateIterator))
|
||||||
|
{
|
||||||
|
if (certificateIterator->dwCertEncodingType & X509_ASN_ENCODING)
|
||||||
|
{
|
||||||
|
int ret = mbedtls_x509_crt_parse(&_cacert,
|
||||||
|
certificateIterator->pbCertEncoded,
|
||||||
|
certificateIterator->cbCertEncoded);
|
||||||
|
if (ret == 0)
|
||||||
|
{
|
||||||
|
++certificateCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CertFreeCertificateContext(certificateIterator);
|
||||||
|
CertCloseStore(systemStore, 0);
|
||||||
|
|
||||||
|
if (certificateCount == 0)
|
||||||
|
{
|
||||||
|
errorMsg = "No certificates found";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
// On macOS we can query the system cert location from the keychain
|
||||||
|
// On Linux we could try to fetch some local files based on the distribution
|
||||||
|
// On Android we could use JNI to get to the system certs
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
bool SocketMbedTLS::init(const std::string& host, bool isClient, std::string& errMsg)
|
bool SocketMbedTLS::init(const std::string& host, bool isClient, std::string& errMsg)
|
||||||
{
|
{
|
||||||
initMBedTLS();
|
initMBedTLS();
|
||||||
@ -96,13 +145,15 @@ namespace ix
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mbedtls_ssl_conf_authmode(&_conf, MBEDTLS_SSL_VERIFY_REQUIRED);
|
|
||||||
|
|
||||||
// FIXME: should we call mbedtls_ssl_conf_verify ?
|
// FIXME: should we call mbedtls_ssl_conf_verify ?
|
||||||
|
mbedtls_ssl_conf_authmode(&_conf, MBEDTLS_SSL_VERIFY_REQUIRED);
|
||||||
|
|
||||||
if (_tlsOptions.isUsingSystemDefaults())
|
if (_tlsOptions.isUsingSystemDefaults())
|
||||||
{
|
{
|
||||||
; // FIXME
|
if (!loadSystemCertificates(errMsg))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -52,6 +52,7 @@ namespace ix
|
|||||||
|
|
||||||
bool init(const std::string& host, bool isClient, std::string& errMsg);
|
bool init(const std::string& host, bool isClient, std::string& errMsg);
|
||||||
void initMBedTLS();
|
void initMBedTLS();
|
||||||
|
bool loadSystemCertificates(std::string& errMsg);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ix
|
} // namespace ix
|
||||||
|
@ -85,6 +85,8 @@ namespace ix
|
|||||||
|
|
||||||
std::atomic<bool> SocketOpenSSL::_openSSLInitializationSuccessful(false);
|
std::atomic<bool> SocketOpenSSL::_openSSLInitializationSuccessful(false);
|
||||||
std::once_flag SocketOpenSSL::_openSSLInitFlag;
|
std::once_flag SocketOpenSSL::_openSSLInitFlag;
|
||||||
|
std::unique_ptr<std::mutex[]> SocketOpenSSL::_openSSLMutexes =
|
||||||
|
std::make_unique<std::mutex[]>(CRYPTO_num_locks());
|
||||||
|
|
||||||
SocketOpenSSL::SocketOpenSSL(const SocketTLSOptions& tlsOptions, int fd)
|
SocketOpenSSL::SocketOpenSSL(const SocketTLSOptions& tlsOptions, int fd)
|
||||||
: Socket(fd)
|
: Socket(fd)
|
||||||
@ -106,6 +108,11 @@ namespace ix
|
|||||||
if (!OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, nullptr)) return;
|
if (!OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, nullptr)) return;
|
||||||
#else
|
#else
|
||||||
(void) OPENSSL_config(nullptr);
|
(void) OPENSSL_config(nullptr);
|
||||||
|
|
||||||
|
if (CRYPTO_get_locking_callback() == nullptr)
|
||||||
|
{
|
||||||
|
CRYPTO_set_locking_callback(SocketOpenSSL::openSSLLockingCallback);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
(void) OpenSSL_add_ssl_algorithms();
|
(void) OpenSSL_add_ssl_algorithms();
|
||||||
@ -114,6 +121,21 @@ namespace ix
|
|||||||
_openSSLInitializationSuccessful = true;
|
_openSSLInitializationSuccessful = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SocketOpenSSL::openSSLLockingCallback(int mode,
|
||||||
|
int type,
|
||||||
|
const char* /*file*/,
|
||||||
|
int /*line*/)
|
||||||
|
{
|
||||||
|
if (mode & CRYPTO_LOCK)
|
||||||
|
{
|
||||||
|
_openSSLMutexes[type].lock();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_openSSLMutexes[type].unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::string SocketOpenSSL::getSSLError(int ret)
|
std::string SocketOpenSSL::getSSLError(int ret)
|
||||||
{
|
{
|
||||||
unsigned long e;
|
unsigned long e;
|
||||||
|
@ -49,6 +49,9 @@ namespace ix
|
|||||||
bool handleTLSOptions(std::string& errMsg);
|
bool handleTLSOptions(std::string& errMsg);
|
||||||
bool openSSLServerHandshake(std::string& errMsg);
|
bool openSSLServerHandshake(std::string& errMsg);
|
||||||
|
|
||||||
|
// Required for OpenSSL < 1.1
|
||||||
|
static void openSSLLockingCallback(int mode, int type, const char* /*file*/, int /*line*/);
|
||||||
|
|
||||||
SSL* _ssl_connection;
|
SSL* _ssl_connection;
|
||||||
SSL_CTX* _ssl_context;
|
SSL_CTX* _ssl_context;
|
||||||
const SSL_METHOD* _ssl_method;
|
const SSL_METHOD* _ssl_method;
|
||||||
@ -58,6 +61,7 @@ namespace ix
|
|||||||
|
|
||||||
static std::once_flag _openSSLInitFlag;
|
static std::once_flag _openSSLInitFlag;
|
||||||
static std::atomic<bool> _openSSLInitializationSuccessful;
|
static std::atomic<bool> _openSSLInitializationSuccessful;
|
||||||
|
static std::unique_ptr<std::mutex[]> _openSSLMutexes;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ix
|
} // namespace ix
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include "IXSocket.h"
|
#include "IXSocket.h"
|
||||||
#include "IXSocketConnect.h"
|
#include "IXSocketConnect.h"
|
||||||
#include "IXSocketFactory.h"
|
#include "IXSocketFactory.h"
|
||||||
|
#include "IXUniquePtr.h"
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@ -258,7 +259,7 @@ namespace ix
|
|||||||
// Use poll to check whether a new connection is in progress
|
// Use poll to check whether a new connection is in progress
|
||||||
int timeoutMs = 10;
|
int timeoutMs = 10;
|
||||||
bool readyToRead = true;
|
bool readyToRead = true;
|
||||||
auto selectInterrupt = std::make_unique<SelectInterrupt>();
|
auto selectInterrupt = ix::make_unique<SelectInterrupt>();
|
||||||
PollResultType pollResult =
|
PollResultType pollResult =
|
||||||
Socket::poll(readyToRead, timeoutMs, _serverFd, selectInterrupt);
|
Socket::poll(readyToRead, timeoutMs, _serverFd, selectInterrupt);
|
||||||
|
|
||||||
|
@ -44,6 +44,18 @@ namespace ix
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool UdpSocket::isWaitNeeded()
|
||||||
|
{
|
||||||
|
int err = getErrno();
|
||||||
|
|
||||||
|
if (err == EWOULDBLOCK || err == EAGAIN || err == EINPROGRESS)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void UdpSocket::closeSocket(int fd)
|
void UdpSocket::closeSocket(int fd)
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
@ -62,6 +74,13 @@ namespace ix
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
unsigned long nonblocking = 1;
|
||||||
|
ioctlsocket(_sockfd, FIONBIO, &nonblocking);
|
||||||
|
#else
|
||||||
|
fcntl(_sockfd, F_SETFL, O_NONBLOCK); // make socket non blocking
|
||||||
|
#endif
|
||||||
|
|
||||||
memset(&_server, 0, sizeof(_server));
|
memset(&_server, 0, sizeof(_server));
|
||||||
_server.sin_family = AF_INET;
|
_server.sin_family = AF_INET;
|
||||||
_server.sin_port = htons(port);
|
_server.sin_port = htons(port);
|
||||||
@ -93,4 +112,15 @@ namespace ix
|
|||||||
return (ssize_t)::sendto(
|
return (ssize_t)::sendto(
|
||||||
_sockfd, buffer.data(), buffer.size(), 0, (struct sockaddr*) &_server, sizeof(_server));
|
_sockfd, buffer.data(), buffer.size(), 0, (struct sockaddr*) &_server, sizeof(_server));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ssize_t UdpSocket::recvfrom(char* buffer, size_t length)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
int addressLen = (int) sizeof(_server);
|
||||||
|
#else
|
||||||
|
socklen_t addressLen = (socklen_t) sizeof(_server);
|
||||||
|
#endif
|
||||||
|
return (ssize_t)::recvfrom(
|
||||||
|
_sockfd, buffer, length, 0, (struct sockaddr*) &_server, &addressLen);
|
||||||
|
}
|
||||||
} // namespace ix
|
} // namespace ix
|
||||||
|
@ -28,9 +28,12 @@ namespace ix
|
|||||||
// Virtual methods
|
// Virtual methods
|
||||||
bool init(const std::string& host, int port, std::string& errMsg);
|
bool init(const std::string& host, int port, std::string& errMsg);
|
||||||
ssize_t sendto(const std::string& buffer);
|
ssize_t sendto(const std::string& buffer);
|
||||||
|
ssize_t recvfrom(char* buffer, size_t length);
|
||||||
|
|
||||||
void close();
|
void close();
|
||||||
|
|
||||||
static int getErrno();
|
static int getErrno();
|
||||||
|
static bool isWaitNeeded();
|
||||||
static void closeSocket(int fd);
|
static void closeSocket(int fd);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
18
ixwebsocket/IXUniquePtr.h
Normal file
18
ixwebsocket/IXUniquePtr.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* IXUniquePtr.h
|
||||||
|
* Author: Benjamin Sergeant
|
||||||
|
* Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace ix
|
||||||
|
{
|
||||||
|
template<typename T, typename... Args>
|
||||||
|
std::unique_ptr<T> make_unique(Args&&... args)
|
||||||
|
{
|
||||||
|
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
|
||||||
|
}
|
||||||
|
}
|
@ -33,6 +33,7 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
@ -6,4 +6,4 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define IX_WEBSOCKET_VERSION "9.5.2"
|
#define IX_WEBSOCKET_VERSION "9.6.5"
|
||||||
|
62
main.cpp
Normal file
62
main.cpp
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* main.cpp
|
||||||
|
* Author: Benjamin Sergeant
|
||||||
|
* Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Super simple standalone example. See ws folder, unittest and doc/usage.md for more.
|
||||||
|
*
|
||||||
|
* On macOS
|
||||||
|
* $ mkdir -p build ; cd build ; cmake -DUSE_TLS=1 .. ; make -j ; make install
|
||||||
|
* $ clang++ --std=c++14 --stdlib=libc++ main.cpp -lixwebsocket -lz -framework Security -framework Foundation
|
||||||
|
* $ ./a.out
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <ixwebsocket/IXNetSystem.h>
|
||||||
|
#include <ixwebsocket/IXWebSocket.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// Required on Windows
|
||||||
|
ix::initNetSystem();
|
||||||
|
|
||||||
|
// Our websocket object
|
||||||
|
ix::WebSocket webSocket;
|
||||||
|
|
||||||
|
std::string url("wss://echo.websocket.org");
|
||||||
|
webSocket.setUrl(url);
|
||||||
|
|
||||||
|
std::cout << "Connecting to " << url << "..." << std::endl;
|
||||||
|
|
||||||
|
// Setup a callback to be fired (in a background thread, watch out for race conditions !)
|
||||||
|
// when a message or an event (open, close, error) is received
|
||||||
|
webSocket.setOnMessageCallback([](const ix::WebSocketMessagePtr& msg)
|
||||||
|
{
|
||||||
|
if (msg->type == ix::WebSocketMessageType::Message)
|
||||||
|
{
|
||||||
|
std::cout << "received message: " << msg->str << std::endl;
|
||||||
|
}
|
||||||
|
else if (msg->type == ix::WebSocketMessageType::Open)
|
||||||
|
{
|
||||||
|
std::cout << "Connection established" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Now that our callback is setup, we can start our background thread and receive messages
|
||||||
|
webSocket.start();
|
||||||
|
|
||||||
|
// Send a message to the server (default to TEXT mode)
|
||||||
|
webSocket.send("hello world");
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
std::string text;
|
||||||
|
std::cout << "> " << std::flush;
|
||||||
|
std::getline(std::cin, text);
|
||||||
|
|
||||||
|
webSocket.send(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
59
makefile
59
makefile
@ -20,16 +20,16 @@ install: brew
|
|||||||
# Release, Debug, MinSizeRel, RelWithDebInfo are the build types
|
# Release, Debug, MinSizeRel, RelWithDebInfo are the build types
|
||||||
#
|
#
|
||||||
brew:
|
brew:
|
||||||
mkdir -p build && (cd build ; cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_TEST=1 .. ; make -j 4 install)
|
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_TEST=1 .. ; ninja install)
|
||||||
|
|
||||||
# Docker default target. We've add problem with OpenSSL and TLS 1.3 (on the
|
# 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
|
# server side ?) and I can't work-around it easily, so we're using mbedtls on
|
||||||
# Linux for the SSL backend, which works great.
|
# Linux for the SSL backend, which works great.
|
||||||
ws_mbedtls_install:
|
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)
|
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=MinSizeRel -DUSE_TLS=1 -DUSE_WS=1 -DUSE_MBED_TLS=1 .. ; ninja install)
|
||||||
|
|
||||||
ws:
|
ws:
|
||||||
mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 .. ; make -j 4)
|
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 .. ; ninja install)
|
||||||
|
|
||||||
ws_install:
|
ws_install:
|
||||||
mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=MinSizeRel -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)
|
||||||
@ -103,6 +103,10 @@ test_server:
|
|||||||
# env TEST=Websocket_chat make test
|
# env TEST=Websocket_chat make test
|
||||||
# env TEST=heartbeat make test
|
# env TEST=heartbeat make test
|
||||||
test:
|
test:
|
||||||
|
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_TEST=1 .. ; ninja install)
|
||||||
|
(cd test ; python2.7 run.py -r)
|
||||||
|
|
||||||
|
test_make:
|
||||||
mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_TEST=1 .. ; make -j 4)
|
mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_TEST=1 .. ; make -j 4)
|
||||||
(cd test ; python2.7 run.py -r)
|
(cd test ; python2.7 run.py -r)
|
||||||
|
|
||||||
@ -144,7 +148,7 @@ test_tsan_mbedtls:
|
|||||||
(cd test ; python2.7 run.py -r)
|
(cd test ; python2.7 run.py -r)
|
||||||
|
|
||||||
build_test_openssl:
|
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)
|
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_OPEN_SSL=1 -DUSE_TEST=1 .. ; ninja install)
|
||||||
|
|
||||||
test_openssl: build_test_openssl
|
test_openssl: build_test_openssl
|
||||||
(cd test ; python2.7 run.py -r)
|
(cd test ; python2.7 run.py -r)
|
||||||
@ -165,6 +169,53 @@ ws_test: ws
|
|||||||
autobahn_report:
|
autobahn_report:
|
||||||
cp -rvf ~/sandbox/reports/clients/* ../bsergean.github.io/IXWebSocket/autobahn/
|
cp -rvf ~/sandbox/reports/clients/* ../bsergean.github.io/IXWebSocket/autobahn/
|
||||||
|
|
||||||
|
httpd:
|
||||||
|
clang++ --std=c++11 --stdlib=libc++ -Iixwebsocket httpd.cpp \
|
||||||
|
ixwebsocket/IXSelectInterruptFactory.cpp \
|
||||||
|
ixwebsocket/IXCancellationRequest.cpp \
|
||||||
|
ixwebsocket/IXSocketTLSOptions.cpp \
|
||||||
|
ixwebsocket/IXUserAgent.cpp \
|
||||||
|
ixwebsocket/IXDNSLookup.cpp \
|
||||||
|
ixwebsocket/IXBench.cpp \
|
||||||
|
ixwebsocket/IXWebSocketHttpHeaders.cpp \
|
||||||
|
ixwebsocket/IXSelectInterruptPipe.cpp \
|
||||||
|
ixwebsocket/IXHttp.cpp \
|
||||||
|
ixwebsocket/IXSocketConnect.cpp \
|
||||||
|
ixwebsocket/IXSocket.cpp \
|
||||||
|
ixwebsocket/IXSocketServer.cpp \
|
||||||
|
ixwebsocket/IXNetSystem.cpp \
|
||||||
|
ixwebsocket/IXHttpServer.cpp \
|
||||||
|
ixwebsocket/IXSocketFactory.cpp \
|
||||||
|
ixwebsocket/IXConnectionState.cpp \
|
||||||
|
ixwebsocket/IXUrlParser.cpp \
|
||||||
|
ixwebsocket/IXSelectInterrupt.cpp \
|
||||||
|
ixwebsocket/apple/IXSetThreadName_apple.cpp \
|
||||||
|
-lz
|
||||||
|
|
||||||
|
httpd_linux:
|
||||||
|
g++ --std=c++11 -o ixhttpd httpd.cpp -Iixwebsocket \
|
||||||
|
ixwebsocket/IXSelectInterruptFactory.cpp \
|
||||||
|
ixwebsocket/IXCancellationRequest.cpp \
|
||||||
|
ixwebsocket/IXSocketTLSOptions.cpp \
|
||||||
|
ixwebsocket/IXUserAgent.cpp \
|
||||||
|
ixwebsocket/IXDNSLookup.cpp \
|
||||||
|
ixwebsocket/IXBench.cpp \
|
||||||
|
ixwebsocket/IXWebSocketHttpHeaders.cpp \
|
||||||
|
ixwebsocket/IXSelectInterruptPipe.cpp \
|
||||||
|
ixwebsocket/IXHttp.cpp \
|
||||||
|
ixwebsocket/IXSocketConnect.cpp \
|
||||||
|
ixwebsocket/IXSocket.cpp \
|
||||||
|
ixwebsocket/IXSocketServer.cpp \
|
||||||
|
ixwebsocket/IXNetSystem.cpp \
|
||||||
|
ixwebsocket/IXHttpServer.cpp \
|
||||||
|
ixwebsocket/IXSocketFactory.cpp \
|
||||||
|
ixwebsocket/IXConnectionState.cpp \
|
||||||
|
ixwebsocket/IXUrlParser.cpp \
|
||||||
|
ixwebsocket/IXSelectInterrupt.cpp \
|
||||||
|
ixwebsocket/linux/IXSetThreadName_linux.cpp \
|
||||||
|
-lz -lpthread
|
||||||
|
cp -f ixhttpd /usr/local/bin
|
||||||
|
|
||||||
# For the fork that is configured with appveyor
|
# For the fork that is configured with appveyor
|
||||||
rebase_upstream:
|
rebase_upstream:
|
||||||
git fetch upstream
|
git fetch upstream
|
||||||
|
@ -138,11 +138,12 @@ TEST_CASE("Cobra_to_sentry_bot", "[cobra_bots]")
|
|||||||
|
|
||||||
std::thread publisherThread(runPublisher, config, channel);
|
std::thread publisherThread(runPublisher, config, channel);
|
||||||
|
|
||||||
std::string filter;
|
ix::CobraBotConfig cobraBotConfig;
|
||||||
std::string position("$");
|
cobraBotConfig.cobraConfig = config;
|
||||||
|
cobraBotConfig.channel = channel;
|
||||||
|
cobraBotConfig.runtime = 3; // Only run the bot for 3 seconds
|
||||||
|
cobraBotConfig.enableHeartbeat = false;
|
||||||
bool verbose = true;
|
bool verbose = true;
|
||||||
size_t maxQueueSize = 10;
|
|
||||||
bool enableHeartbeat = false;
|
|
||||||
|
|
||||||
// FIXME: try to get this working with https instead of http
|
// FIXME: try to get this working with https instead of http
|
||||||
// to regress the TLS 1.3 OpenSSL bug
|
// to regress the TLS 1.3 OpenSSL bug
|
||||||
@ -157,18 +158,7 @@ TEST_CASE("Cobra_to_sentry_bot", "[cobra_bots]")
|
|||||||
SentryClient sentryClient(dsn);
|
SentryClient sentryClient(dsn);
|
||||||
sentryClient.setTLSOptions(tlsOptionsClient);
|
sentryClient.setTLSOptions(tlsOptionsClient);
|
||||||
|
|
||||||
// Only run the bot for 3 seconds
|
int64_t sentCount = cobra_to_sentry_bot(cobraBotConfig, sentryClient, verbose);
|
||||||
int runtime = 3;
|
|
||||||
|
|
||||||
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
|
// We want at least 2 messages to be sent
|
||||||
//
|
//
|
||||||
|
@ -87,14 +87,11 @@ TEST_CASE("Cobra_to_statsd_bot", "[cobra_bots]")
|
|||||||
|
|
||||||
std::thread publisherThread(runPublisher, config, channel);
|
std::thread publisherThread(runPublisher, config, channel);
|
||||||
|
|
||||||
std::string filter;
|
ix::CobraBotConfig cobraBotConfig;
|
||||||
std::string position("$");
|
cobraBotConfig.cobraConfig = config;
|
||||||
bool verbose = true;
|
cobraBotConfig.channel = channel;
|
||||||
size_t maxQueueSize = 10;
|
cobraBotConfig.runtime = 3; // Only run the bot for 3 seconds
|
||||||
bool enableHeartbeat = false;
|
cobraBotConfig.enableHeartbeat = false;
|
||||||
|
|
||||||
// Only run the bot for 3 seconds
|
|
||||||
int runtime = 3;
|
|
||||||
|
|
||||||
std::string hostname("127.0.0.1");
|
std::string hostname("127.0.0.1");
|
||||||
// std::string hostname("www.google.com");
|
// std::string hostname("www.google.com");
|
||||||
@ -113,19 +110,10 @@ TEST_CASE("Cobra_to_statsd_bot", "[cobra_bots]")
|
|||||||
std::string fields("device.game\ndevice.os_name");
|
std::string fields("device.game\ndevice.os_name");
|
||||||
std::string gauge;
|
std::string gauge;
|
||||||
std::string timer;
|
std::string timer;
|
||||||
|
bool verbose = true;
|
||||||
|
|
||||||
int64_t sentCount = ix::cobra_to_statsd_bot(config,
|
int64_t sentCount =
|
||||||
channel,
|
ix::cobra_to_statsd_bot(cobraBotConfig, statsdClient, fields, gauge, timer, verbose);
|
||||||
filter,
|
|
||||||
position,
|
|
||||||
statsdClient,
|
|
||||||
fields,
|
|
||||||
gauge,
|
|
||||||
timer,
|
|
||||||
verbose,
|
|
||||||
maxQueueSize,
|
|
||||||
enableHeartbeat,
|
|
||||||
runtime);
|
|
||||||
//
|
//
|
||||||
// We want at least 2 messages to be sent
|
// We want at least 2 messages to be sent
|
||||||
//
|
//
|
||||||
|
@ -85,29 +85,17 @@ TEST_CASE("Cobra_to_stdout_bot", "[cobra_bots]")
|
|||||||
|
|
||||||
std::thread publisherThread(runPublisher, config, channel);
|
std::thread publisherThread(runPublisher, config, channel);
|
||||||
|
|
||||||
std::string filter;
|
ix::CobraBotConfig cobraBotConfig;
|
||||||
std::string position("$");
|
cobraBotConfig.cobraConfig = config;
|
||||||
bool verbose = true;
|
cobraBotConfig.channel = channel;
|
||||||
|
cobraBotConfig.runtime = 3; // Only run the bot for 3 seconds
|
||||||
|
cobraBotConfig.enableHeartbeat = false;
|
||||||
bool quiet = false;
|
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.
|
// We could try to capture the output ... not sure how.
|
||||||
bool fluentd = true;
|
bool fluentd = true;
|
||||||
|
|
||||||
int64_t sentCount = ix::cobra_to_stdout_bot(config,
|
int64_t sentCount = ix::cobra_to_stdout_bot(cobraBotConfig, fluentd, quiet);
|
||||||
channel,
|
|
||||||
filter,
|
|
||||||
position,
|
|
||||||
fluentd,
|
|
||||||
quiet,
|
|
||||||
verbose,
|
|
||||||
maxQueueSize,
|
|
||||||
enableHeartbeat,
|
|
||||||
runtime);
|
|
||||||
//
|
//
|
||||||
// We want at least 2 messages to be sent
|
// We want at least 2 messages to be sent
|
||||||
//
|
//
|
||||||
|
@ -4,6 +4,14 @@
|
|||||||
* Copyright (c) 2019 Machine Zone. All rights reserved.
|
* Copyright (c) 2019 Machine Zone. All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// Using inet_addr will trigger an error on uwp without this
|
||||||
|
// FIXME: use a different api
|
||||||
|
#ifdef _WIN32
|
||||||
|
#ifndef _WINSOCK_DEPRECATED_NO_WARNINGS
|
||||||
|
#define _WINSOCK_DEPRECATED_NO_WARNINGS
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "IXGetFreePort.h"
|
#include "IXGetFreePort.h"
|
||||||
|
|
||||||
#include <ixwebsocket/IXNetSystem.h>
|
#include <ixwebsocket/IXNetSystem.h>
|
||||||
|
@ -93,10 +93,11 @@ TEST_CASE("subprotocol", "[websocket_subprotocol]")
|
|||||||
webSocket.setUrl(url);
|
webSocket.setUrl(url);
|
||||||
webSocket.start();
|
webSocket.start();
|
||||||
|
|
||||||
|
// Give us 3 seconds to connect
|
||||||
int attempts = 0;
|
int attempts = 0;
|
||||||
while (!connected)
|
while (!connected)
|
||||||
{
|
{
|
||||||
REQUIRE(attempts++ < 10);
|
REQUIRE(attempts++ < 300);
|
||||||
ix::msleep(10);
|
ix::msleep(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
36
tools/build_android.sh
Normal file
36
tools/build_android.sh
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Executable : ${HOME}/Android/Sdk/cmake/3.6.3155560/bin/cmake
|
||||||
|
# arguments :
|
||||||
|
# -H${HOME}/Dev/github-projects/googlesamples/android-ndk/hello-jni/app/src/main/cpp
|
||||||
|
# -B${HOME}/Dev/github-projects/googlesamples/android-ndk/hello-jni/app/.cxx/cmake/arm7Debug/armeabi-v7a
|
||||||
|
# -GAndroid Gradle - Ninja
|
||||||
|
# -DANDROID_ABI=armeabi-v7a
|
||||||
|
# -DANDROID_NDK=${HOME}/Android/Sdk/ndk-bundle
|
||||||
|
# -DCMAKE_LIBRARY_OUTPUT_DIRECTORY=${HOME}/Dev/github-projects/googlesamples/android-ndk/hello-jni/app/build/intermediates/cmake/arm7/debug/obj/armeabi-v7a
|
||||||
|
# -DCMAKE_BUILD_TYPE=Debug
|
||||||
|
# -DCMAKE_MAKE_PROGRAM=${HOME}/Android/Sdk/cmake/3.6.3155560/bin/ninja
|
||||||
|
# -DCMAKE_TOOLCHAIN_FILE=${HOME}/Android/Sdk/ndk-bundle/build/cmake/android.toolchain.cmake
|
||||||
|
# -DANDROID_NATIVE_API_LEVEL=23
|
||||||
|
# -DANDROID_TOOLCHAIN=clang
|
||||||
|
# jvmArgs :
|
||||||
|
#
|
||||||
|
|
||||||
|
CMAKE_TOOLCHAIN_FILE=/tools/android/android-ndk-r20-darwin/build/cmake/android.toolchain.cmake
|
||||||
|
ANDROID_HOME=/tools/android/android-sdk-darwin
|
||||||
|
ANDROID_NDK=${ANDROID_HOME}/ndk-bundle
|
||||||
|
CMAKE_DIR=${ANDROID_HOME}/cmake/3.10.2.4988404/bin
|
||||||
|
CMAKE=${CMAKE_DIR}/cmake
|
||||||
|
NINJA=${CMAKE_DIR}/ninja
|
||||||
|
|
||||||
|
${CMAKE} \
|
||||||
|
.. \
|
||||||
|
-DANDROID_NATIVE_API_LEVEL=23 \
|
||||||
|
-DANDROID_ABI=armeabi-v7a \
|
||||||
|
-DANDROID_TOOLCHAIN=clang \
|
||||||
|
-DANDROID_NDK=${ANDROID_NDK} \
|
||||||
|
-G'Unix Makefiles' \
|
||||||
|
-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE} \
|
||||||
|
-DCMAKE_MAKE_PROGRAM=make \
|
||||||
|
-DUSE_WS=1
|
89
ws/ws.cpp
89
ws/ws.cpp
@ -19,8 +19,8 @@
|
|||||||
#include <ixwebsocket/IXNetSystem.h>
|
#include <ixwebsocket/IXNetSystem.h>
|
||||||
#include <ixwebsocket/IXSocket.h>
|
#include <ixwebsocket/IXSocket.h>
|
||||||
#include <ixwebsocket/IXUserAgent.h>
|
#include <ixwebsocket/IXUserAgent.h>
|
||||||
#include <spdlog/spdlog.h>
|
|
||||||
#include <spdlog/sinks/basic_file_sink.h>
|
#include <spdlog/sinks/basic_file_sink.h>
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
@ -120,6 +120,7 @@ int main(int argc, char** argv)
|
|||||||
std::string logfile;
|
std::string logfile;
|
||||||
ix::SocketTLSOptions tlsOptions;
|
ix::SocketTLSOptions tlsOptions;
|
||||||
ix::CobraConfig cobraConfig;
|
ix::CobraConfig cobraConfig;
|
||||||
|
ix::CobraBotConfig cobraBotConfig;
|
||||||
std::string ciphers;
|
std::string ciphers;
|
||||||
std::string redirectUrl;
|
std::string redirectUrl;
|
||||||
bool headersOnly = false;
|
bool headersOnly = false;
|
||||||
@ -148,9 +149,7 @@ int main(int argc, char** argv)
|
|||||||
int delayMs = -1;
|
int delayMs = -1;
|
||||||
int count = 1;
|
int count = 1;
|
||||||
uint32_t maxWaitBetweenReconnectionRetries;
|
uint32_t maxWaitBetweenReconnectionRetries;
|
||||||
size_t maxQueueSize = 100;
|
|
||||||
int pingIntervalSecs = 30;
|
int pingIntervalSecs = 30;
|
||||||
int runtime = -1; // run indefinitely
|
|
||||||
|
|
||||||
auto addTLSOptions = [&tlsOptions, &verifyNone](CLI::App* app) {
|
auto addTLSOptions = [&tlsOptions, &verifyNone](CLI::App* app) {
|
||||||
app->add_option(
|
app->add_option(
|
||||||
@ -174,6 +173,24 @@ int main(int argc, char** argv)
|
|||||||
app->add_option("--rolesecret", cobraConfig.rolesecret, "Role secret")->required();
|
app->add_option("--rolesecret", cobraConfig.rolesecret, "Role secret")->required();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
auto addCobraBotConfig = [&cobraBotConfig](CLI::App* app) {
|
||||||
|
app->add_option("--appkey", cobraBotConfig.cobraConfig.appkey, "Appkey")->required();
|
||||||
|
app->add_option("--endpoint", cobraBotConfig.cobraConfig.endpoint, "Endpoint")->required();
|
||||||
|
app->add_option("--rolename", cobraBotConfig.cobraConfig.rolename, "Role name")->required();
|
||||||
|
app->add_option("--rolesecret", cobraBotConfig.cobraConfig.rolesecret, "Role secret")
|
||||||
|
->required();
|
||||||
|
app->add_option("--channel", cobraBotConfig.channel, "Channel")->required();
|
||||||
|
app->add_option("--filter", cobraBotConfig.filter, "Filter");
|
||||||
|
app->add_option("--position", cobraBotConfig.position, "Position");
|
||||||
|
app->add_option("--runtime", cobraBotConfig.runtime, "Runtime");
|
||||||
|
app->add_option("--heartbeat", cobraBotConfig.enableHeartbeat, "Runtime");
|
||||||
|
app->add_option("--heartbeat_timeout", cobraBotConfig.heartBeatTimeout, "Runtime");
|
||||||
|
app->add_flag(
|
||||||
|
"--limit_received_events", cobraBotConfig.limitReceivedEvents, "Max events per minute");
|
||||||
|
app->add_option(
|
||||||
|
"--max_events_per_minute", cobraBotConfig.maxEventsPerMinute, "Max events per minute");
|
||||||
|
};
|
||||||
|
|
||||||
app.add_flag("--version", version, "Print ws version");
|
app.add_flag("--version", version, "Print ws version");
|
||||||
app.add_option("--logfile", logfile, "path where all logs will be redirected");
|
app.add_option("--logfile", logfile, "path where all logs will be redirected");
|
||||||
|
|
||||||
@ -281,15 +298,11 @@ int main(int argc, char** argv)
|
|||||||
|
|
||||||
CLI::App* cobraSubscribeApp = app.add_subcommand("cobra_subscribe", "Cobra subscriber");
|
CLI::App* cobraSubscribeApp = app.add_subcommand("cobra_subscribe", "Cobra subscriber");
|
||||||
cobraSubscribeApp->fallthrough();
|
cobraSubscribeApp->fallthrough();
|
||||||
cobraSubscribeApp->add_option("--channel", channel, "Channel")->required();
|
|
||||||
cobraSubscribeApp->add_option("--pidfile", pidfile, "Pid file");
|
cobraSubscribeApp->add_option("--pidfile", pidfile, "Pid file");
|
||||||
cobraSubscribeApp->add_option("--filter", filter, "Stream SQL Filter");
|
|
||||||
cobraSubscribeApp->add_option("--position", position, "Stream position");
|
|
||||||
cobraSubscribeApp->add_flag("-q", quiet, "Quiet / only display stats");
|
cobraSubscribeApp->add_flag("-q", quiet, "Quiet / only display stats");
|
||||||
cobraSubscribeApp->add_flag("--fluentd", fluentd, "Write fluentd prefix");
|
cobraSubscribeApp->add_flag("--fluentd", fluentd, "Write fluentd prefix");
|
||||||
cobraSubscribeApp->add_option("--runtime", runtime, "Runtime in seconds");
|
|
||||||
addTLSOptions(cobraSubscribeApp);
|
addTLSOptions(cobraSubscribeApp);
|
||||||
addCobraConfig(cobraSubscribeApp);
|
addCobraBotConfig(cobraSubscribeApp);
|
||||||
|
|
||||||
CLI::App* cobraPublish = app.add_subcommand("cobra_publish", "Cobra publisher");
|
CLI::App* cobraPublish = app.add_subcommand("cobra_publish", "Cobra publisher");
|
||||||
cobraPublish->fallthrough();
|
cobraPublish->fallthrough();
|
||||||
@ -323,32 +336,18 @@ int main(int argc, char** argv)
|
|||||||
->join();
|
->join();
|
||||||
cobra2statsd->add_option("--timer", timer, "Value to extract, and use as a statsd timer")
|
cobra2statsd->add_option("--timer", timer, "Value to extract, and use as a statsd timer")
|
||||||
->join();
|
->join();
|
||||||
cobra2statsd->add_option("channel", channel, "Channel")->required();
|
|
||||||
cobra2statsd->add_flag("-v", verbose, "Verbose");
|
cobra2statsd->add_flag("-v", verbose, "Verbose");
|
||||||
cobra2statsd->add_option("--pidfile", pidfile, "Pid file");
|
cobra2statsd->add_option("--pidfile", pidfile, "Pid file");
|
||||||
cobra2statsd->add_option("--filter", filter, "Stream SQL Filter");
|
|
||||||
cobra2statsd->add_option("--position", position, "Stream position");
|
|
||||||
cobra2statsd->add_option("--queue_size",
|
|
||||||
maxQueueSize,
|
|
||||||
"Size of the queue to hold messages before they are sent to Sentry");
|
|
||||||
cobra2statsd->add_option("--runtime", runtime, "Runtime in seconds");
|
|
||||||
addTLSOptions(cobra2statsd);
|
addTLSOptions(cobra2statsd);
|
||||||
addCobraConfig(cobra2statsd);
|
addCobraBotConfig(cobra2statsd);
|
||||||
|
|
||||||
CLI::App* cobra2sentry = app.add_subcommand("cobra_to_sentry", "Cobra metrics to sentry");
|
CLI::App* cobra2sentry = app.add_subcommand("cobra_to_sentry", "Cobra metrics to sentry");
|
||||||
cobra2sentry->fallthrough();
|
cobra2sentry->fallthrough();
|
||||||
cobra2sentry->add_option("--dsn", dsn, "Sentry DSN");
|
cobra2sentry->add_option("--dsn", dsn, "Sentry DSN");
|
||||||
cobra2sentry->add_option("--queue_size",
|
|
||||||
maxQueueSize,
|
|
||||||
"Size of the queue to hold messages before they are sent to Sentry");
|
|
||||||
cobra2sentry->add_option("channel", channel, "Channel")->required();
|
|
||||||
cobra2sentry->add_flag("-v", verbose, "Verbose");
|
cobra2sentry->add_flag("-v", verbose, "Verbose");
|
||||||
cobra2sentry->add_option("--pidfile", pidfile, "Pid file");
|
cobra2sentry->add_option("--pidfile", pidfile, "Pid file");
|
||||||
cobra2sentry->add_option("--filter", filter, "Stream SQL Filter");
|
|
||||||
cobra2sentry->add_option("--position", position, "Stream position");
|
|
||||||
cobra2sentry->add_option("--runtime", runtime, "Runtime in seconds");
|
|
||||||
addTLSOptions(cobra2sentry);
|
addTLSOptions(cobra2sentry);
|
||||||
addCobraConfig(cobra2sentry);
|
addCobraBotConfig(cobra2sentry);
|
||||||
|
|
||||||
CLI::App* cobra2redisApp =
|
CLI::App* cobra2redisApp =
|
||||||
app.add_subcommand("cobra_metrics_to_redis", "Cobra metrics to redis");
|
app.add_subcommand("cobra_metrics_to_redis", "Cobra metrics to redis");
|
||||||
@ -459,6 +458,10 @@ int main(int argc, char** argv)
|
|||||||
cobraConfig.webSocketPerMessageDeflateOptions = ix::WebSocketPerMessageDeflateOptions(true);
|
cobraConfig.webSocketPerMessageDeflateOptions = ix::WebSocketPerMessageDeflateOptions(true);
|
||||||
cobraConfig.socketTLSOptions = tlsOptions;
|
cobraConfig.socketTLSOptions = tlsOptions;
|
||||||
|
|
||||||
|
cobraBotConfig.cobraConfig.webSocketPerMessageDeflateOptions =
|
||||||
|
ix::WebSocketPerMessageDeflateOptions(true);
|
||||||
|
cobraBotConfig.cobraConfig.socketTLSOptions = tlsOptions;
|
||||||
|
|
||||||
int ret = 1;
|
int ret = 1;
|
||||||
if (app.got_subcommand("transfer"))
|
if (app.got_subcommand("transfer"))
|
||||||
{
|
{
|
||||||
@ -528,17 +531,7 @@ int main(int argc, char** argv)
|
|||||||
}
|
}
|
||||||
else if (app.got_subcommand("cobra_subscribe"))
|
else if (app.got_subcommand("cobra_subscribe"))
|
||||||
{
|
{
|
||||||
bool enableHeartbeat = true;
|
int64_t sentCount = ix::cobra_to_stdout_bot(cobraBotConfig, fluentd, quiet);
|
||||||
int64_t sentCount = ix::cobra_to_stdout_bot(cobraConfig,
|
|
||||||
channel,
|
|
||||||
filter,
|
|
||||||
position,
|
|
||||||
fluentd,
|
|
||||||
quiet,
|
|
||||||
verbose,
|
|
||||||
maxQueueSize,
|
|
||||||
enableHeartbeat,
|
|
||||||
runtime);
|
|
||||||
ret = (int) sentCount;
|
ret = (int) sentCount;
|
||||||
}
|
}
|
||||||
else if (app.got_subcommand("cobra_publish"))
|
else if (app.got_subcommand("cobra_publish"))
|
||||||
@ -559,7 +552,6 @@ int main(int argc, char** argv)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
bool enableHeartbeat = true;
|
|
||||||
ix::StatsdClient statsdClient(hostname, statsdPort, prefix);
|
ix::StatsdClient statsdClient(hostname, statsdPort, prefix);
|
||||||
|
|
||||||
std::string errMsg;
|
std::string errMsg;
|
||||||
@ -571,36 +563,17 @@ int main(int argc, char** argv)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ret = (int) ix::cobra_to_statsd_bot(cobraConfig,
|
ret = (int) ix::cobra_to_statsd_bot(
|
||||||
channel,
|
cobraBotConfig, statsdClient, fields, gauge, timer, verbose);
|
||||||
filter,
|
|
||||||
position,
|
|
||||||
statsdClient,
|
|
||||||
fields,
|
|
||||||
gauge,
|
|
||||||
timer,
|
|
||||||
verbose,
|
|
||||||
maxQueueSize,
|
|
||||||
enableHeartbeat,
|
|
||||||
runtime);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (app.got_subcommand("cobra_to_sentry"))
|
else if (app.got_subcommand("cobra_to_sentry"))
|
||||||
{
|
{
|
||||||
bool enableHeartbeat = true;
|
|
||||||
ix::SentryClient sentryClient(dsn);
|
ix::SentryClient sentryClient(dsn);
|
||||||
sentryClient.setTLSOptions(tlsOptions);
|
sentryClient.setTLSOptions(tlsOptions);
|
||||||
|
|
||||||
ret = (int) ix::cobra_to_sentry_bot(cobraConfig,
|
ret = (int) ix::cobra_to_sentry_bot(cobraBotConfig, sentryClient, verbose);
|
||||||
channel,
|
|
||||||
filter,
|
|
||||||
position,
|
|
||||||
sentryClient,
|
|
||||||
verbose,
|
|
||||||
maxQueueSize,
|
|
||||||
enableHeartbeat,
|
|
||||||
runtime);
|
|
||||||
}
|
}
|
||||||
else if (app.got_subcommand("cobra_metrics_to_redis"))
|
else if (app.got_subcommand("cobra_metrics_to_redis"))
|
||||||
{
|
{
|
||||||
|
@ -109,8 +109,11 @@ namespace ix
|
|||||||
args->verbose = verbose;
|
args->verbose = verbose;
|
||||||
args->compress = compress;
|
args->compress = compress;
|
||||||
args->logger = [](const std::string& msg) { spdlog::info(msg); };
|
args->logger = [](const std::string& msg) { spdlog::info(msg); };
|
||||||
args->onProgressCallback = [](int current, int total) -> bool {
|
args->onProgressCallback = [verbose](int current, int total) -> bool {
|
||||||
|
if (verbose)
|
||||||
|
{
|
||||||
spdlog::info("Downloaded {} bytes out of {}", current, total);
|
spdlog::info("Downloaded {} bytes out of {}", current, total);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user