Compare commits

..

1 Commits

Author SHA1 Message Date
2cbe198497 try to run the unittest 2020-04-05 12:39:19 -07:00
1813 changed files with 454045 additions and 14808 deletions

View File

@ -9,8 +9,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: make test_make
run: make test_make
- name: make test
run: make test
mac_tsan_sectransport:
runs-on: macOS-latest
@ -24,7 +24,7 @@ jobs:
steps:
- uses: actions/checkout@v1
- name: install openssl
run: brew install openssl@1.1
run: brew install openssl
- name: make test
run: make test_tsan_openssl
@ -37,7 +37,7 @@ jobs:
- name: make test
run: make test_tsan_mbedtls
windows:
windows_no_tls:
runs-on: windows-latest
steps:
- uses: actions/checkout@v1
@ -45,24 +45,30 @@ jobs:
- run: |
mkdir build
cd build
cmake -DCMAKE_CXX_COMPILER=cl.exe -DUSE_WS=1 -DUSE_TEST=1 ..
cmake -DCMAKE_CXX_COMPILER=cl.exe -DUSE_TEST=1 ..
- run: cmake --build build
- run: |
cd test
..\build\test\ixwebsocket_unittest.exe
# windows_mbedtls:
# runs-on: windows-latest
# steps:
# - uses: actions/checkout@v1
# - uses: seanmiddleditch/gha-setup-vsdevenv@master
# - run: |
# vcpkg install zlib:x64-windows
# vcpkg install mbedtls:x64-windows
# - run: |
# mkdir build
# cd build
# cmake -DCMAKE_TOOLCHAIN_FILE=c:/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_CXX_COMPILER=cl.exe -DUSE_MBED_TLS=1 -DUSE_TLS=1 -DUSE_WS=1 -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
uwp:
runs-on: windows-latest
steps:
- uses: actions/checkout@v1
- uses: seanmiddleditch/gha-setup-vsdevenv@master
- run: |
mkdir build
cd build
cmake -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION="10.0" -DCMAKE_CXX_COMPILER=cl.exe -DUSE_TEST=1 ..
- run: cmake --build build
#
# Windows with OpenSSL is working but disabled as it takes 13 minutes (10 for openssl) to build with vcpkg
#

View File

@ -1,19 +0,0 @@
name: Mark stale issues and pull requests
on:
schedule:
- cron: "0 0 * * *"
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v1
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: 'Stale issue message'
stale-pr-message: 'Stale pull request message'
stale-issue-label: 'no-issue-activity'
stale-pr-label: 'no-pr-activity'

View File

@ -1,12 +1,7 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v2.5.0
rev: v2.3.0
hooks:
- id: check-yaml
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/pocc/pre-commit-hooks
rev: v1.1.1
hooks:
- id: clang-format
args: [-i, -style=file]

View File

@ -5,7 +5,7 @@ include(FindPackageHandleStandardArgs)
find_path(JSONCPP_INCLUDE_DIRS json/json.h)
find_library(JSONCPP_LIBRARY jsoncpp)
find_package_handle_standard_args(JsonCpp
find_package_handle_standard_args(JSONCPP
FOUND_VAR
JSONCPP_FOUND
REQUIRED_VARS

View File

@ -44,11 +44,13 @@ set( IXWEBSOCKET_SOURCES
ixwebsocket/IXWebSocketCloseConstants.cpp
ixwebsocket/IXWebSocketHandshake.cpp
ixwebsocket/IXWebSocketHttpHeaders.cpp
ixwebsocket/IXWebSocketMessageQueue.cpp
ixwebsocket/IXWebSocketPerMessageDeflate.cpp
ixwebsocket/IXWebSocketPerMessageDeflateCodec.cpp
ixwebsocket/IXWebSocketPerMessageDeflateOptions.cpp
ixwebsocket/IXWebSocketServer.cpp
ixwebsocket/IXWebSocketTransport.cpp
ixwebsocket/LUrlParser.cpp
)
set( IXWEBSOCKET_HEADERS
@ -79,10 +81,10 @@ set( IXWEBSOCKET_HEADERS
ixwebsocket/IXWebSocketCloseInfo.h
ixwebsocket/IXWebSocketErrorInfo.h
ixwebsocket/IXWebSocketHandshake.h
ixwebsocket/IXWebSocketHandshakeKeyGen.h
ixwebsocket/IXWebSocketHttpHeaders.h
ixwebsocket/IXWebSocketInitResult.h
ixwebsocket/IXWebSocketMessage.h
ixwebsocket/IXWebSocketMessageQueue.h
ixwebsocket/IXWebSocketMessageType.h
ixwebsocket/IXWebSocketOpenInfo.h
ixwebsocket/IXWebSocketPerMessageDeflate.h
@ -92,6 +94,8 @@ set( IXWEBSOCKET_HEADERS
ixwebsocket/IXWebSocketServer.h
ixwebsocket/IXWebSocketTransport.h
ixwebsocket/IXWebSocketVersion.h
ixwebsocket/LUrlParser.h
ixwebsocket/libwshandshake.hpp
)
if (UNIX)
@ -109,33 +113,31 @@ elseif (${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/freebsd/IXSetThreadName_freebsd.cpp)
else()
list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/linux/IXSetThreadName_linux.cpp)
list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/IXSelectInterruptEventFd.cpp)
list( APPEND IXWEBSOCKET_HEADERS ixwebsocket/IXSelectInterruptEventFd.h)
endif()
option(USE_TLS "Enable TLS support" FALSE)
if (USE_TLS)
# default to securetranport on Apple if nothing is configured
if (APPLE)
if (NOT USE_MBED_TLS AND NOT USE_OPEN_SSL) # unless we want something else
set(USE_SECURE_TRANSPORT ON)
endif()
else() # default to OpenSSL on all other platforms
if (NOT USE_MBED_TLS) # Unless mbedtls is requested
set(USE_OPEN_SSL ON)
endif()
option(USE_MBED_TLS "Use Mbed TLS" OFF)
option(USE_OPEN_SSL "Use OpenSSL" OFF)
# default to mbedtls on windows if nothing is configured
if (WIN32 AND NOT USE_OPEN_SSL AND NOT USE_MBED_TLS)
option(USE_MBED_TLS "Use Mbed TLS" ON)
endif()
if (USE_MBED_TLS)
list( APPEND IXWEBSOCKET_HEADERS ixwebsocket/IXSocketMbedTLS.h)
list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/IXSocketMbedTLS.cpp)
elseif (USE_SECURE_TRANSPORT)
elseif (APPLE AND NOT USE_OPEN_SSL)
list( APPEND IXWEBSOCKET_HEADERS ixwebsocket/IXSocketAppleSSL.h)
list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/IXSocketAppleSSL.cpp)
elseif (USE_OPEN_SSL)
else()
set(USE_OPEN_SSL ON)
list( APPEND IXWEBSOCKET_HEADERS ixwebsocket/IXSocketOpenSSL.h)
list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/IXSocketOpenSSL.cpp)
else()
message(FATAL_ERROR "TLS Configuration error: unknown backend")
endif()
endif()
@ -150,45 +152,55 @@ if (USE_TLS)
target_compile_definitions(ixwebsocket PUBLIC IXWEBSOCKET_USE_MBED_TLS)
elseif (USE_OPEN_SSL)
target_compile_definitions(ixwebsocket PUBLIC IXWEBSOCKET_USE_OPEN_SSL)
elseif (USE_SECURE_TRANSPORT)
target_compile_definitions(ixwebsocket PUBLIC IXWEBSOCKET_USE_SECURE_TRANSPORT)
else()
message(FATAL_ERROR "TLS Configuration error: unknown backend")
endif()
endif()
if (USE_TLS)
if (USE_OPEN_SSL)
message(STATUS "TLS configured to use openssl")
if (APPLE AND USE_TLS AND NOT USE_MBED_TLS AND NOT USE_OPEN_SSL)
target_link_libraries(ixwebsocket "-framework foundation" "-framework security")
endif()
# Help finding Homebrew's OpenSSL on macOS
if (APPLE)
set(CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} /usr/local/opt/openssl/lib)
set(CMAKE_INCLUDE_PATH ${CMAKE_INCLUDE_PATH} /usr/local/opt/openssl/include)
endif()
if (WIN32)
target_link_libraries(ixwebsocket wsock32 ws2_32 shlwapi)
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
endif()
# This OPENSSL_FOUND check is to help find a cmake manually configured OpenSSL
if (NOT OPENSSL_FOUND)
find_package(OpenSSL REQUIRED)
endif()
message(STATUS "OpenSSL: " ${OPENSSL_VERSION})
if (UNIX)
find_package(Threads)
target_link_libraries(ixwebsocket ${CMAKE_THREAD_LIBS_INIT})
endif()
add_definitions(${OPENSSL_DEFINITIONS})
target_include_directories(ixwebsocket PUBLIC ${OPENSSL_INCLUDE_DIR})
target_link_libraries(ixwebsocket ${OPENSSL_LIBRARIES})
elseif (USE_MBED_TLS)
message(STATUS "TLS configured to use mbedtls")
if (USE_TLS AND USE_OPEN_SSL)
# Help finding Homebrew's OpenSSL on macOS
if (APPLE)
set(CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} /usr/local/opt/openssl/lib)
set(CMAKE_INCLUDE_PATH ${CMAKE_INCLUDE_PATH} /usr/local/opt/openssl/include)
endif()
if(NOT OPENSSL_FOUND)
find_package(OpenSSL REQUIRED)
endif()
add_definitions(${OPENSSL_DEFINITIONS})
message(STATUS "OpenSSL: " ${OPENSSL_VERSION})
include_directories(${OPENSSL_INCLUDE_DIR})
target_link_libraries(ixwebsocket ${OPENSSL_LIBRARIES})
endif()
if (USE_TLS AND USE_MBED_TLS)
# FIXME I'm not too sure that this USE_VENDORED_THIRD_PARTY thing works
if (USE_VENDORED_THIRD_PARTY)
set (ENABLE_PROGRAMS OFF)
add_subdirectory(third_party/mbedtls)
include_directories(third_party/mbedtls/include)
target_link_libraries(ixwebsocket mbedtls)
else()
find_package(MbedTLS REQUIRED)
target_include_directories(ixwebsocket PUBLIC ${MBEDTLS_INCLUDE_DIRS})
target_link_libraries(ixwebsocket ${MBEDTLS_LIBRARIES})
elseif (USE_SECURE_TRANSPORT)
message(STATUS "TLS configured to use secure transport")
target_link_libraries(ixwebsocket "-framework foundation" "-framework security")
endif()
endif()
# This ZLIB_FOUND check is to help find a cmake manually configured zlib
if (NOT ZLIB_FOUND)
find_package(ZLIB)
endif()
@ -201,21 +213,6 @@ else()
target_link_libraries(ixwebsocket zlibstatic)
endif()
if (WIN32)
target_link_libraries(ixwebsocket wsock32 ws2_32 shlwapi)
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
if (USE_TLS)
target_link_libraries(ixwebsocket Crypt32)
endif()
endif()
if (UNIX)
find_package(Threads)
target_link_libraries(ixwebsocket ${CMAKE_THREAD_LIBS_INIT})
endif()
set( IXWEBSOCKET_INCLUDE_DIRS
${CMAKE_CURRENT_SOURCE_DIR}
)

View File

@ -1,82 +1,6 @@
# Changelog
All changes to this project will be documented in this file.
## [9.5.4] - 2020-05-01
(windows) fix build for universal windows platform
## [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
(cmake) fix cmake broken tls option parsing
## [9.5.1] - 2020-04-27
(http client) Set default values for most HttpRequestArgs struct members (fix #185)
## [9.5.0] - 2020-04-25
(ssl) Default to OpenSSL on Windows, since it can load the system certificates by default
## [9.4.1] - 2020-04-25
(header) Add a space between header name and header value since most http parsers expects it, although it it not required. Cf #184 and #155
## [9.4.0] - 2020-04-24
(ssl) Add support for supplying SSL CA from memory, for OpenSSL and MbedTLS backends
## [9.3.3] - 2020-04-17
(ixbots) display sent/receive message, per seconds as accumulated
## [9.3.2] - 2020-04-17
(ws) add a --logfile option to configure all logs to go to a file
## [9.3.1] - 2020-04-16
(cobra bots) add a utility class to factor out the common bots features (heartbeat) and move all bots to used it + convert cobra_subscribe to be a bot and add a unittest for it
## [9.3.0] - 2020-04-15
(websocket) add a positive number to the heartbeat message sent, incremented each time the heartbeat is sent
## [9.2.9] - 2020-04-15
(ixcobra) change cobra event callback to use a struct instead of several objects, which is more flexible/extensible
## [9.2.8] - 2020-04-15
(ixcobra) make CobraConnection_EventType an enum class (CobraEventType)
## [9.2.7] - 2020-04-14
(ixsentry) add a library method to upload a payload directly to sentry
## [9.2.6] - 2020-04-14
(ixcobra) snake server / handle invalid incoming json messages + cobra subscriber in fluentd mode insert a created_at timestamp entry
## [9.2.5] - 2020-04-13
(websocket) WebSocketMessagePtr is a unique_ptr instead of a shared_ptr
## [9.2.4] - 2020-04-13
(websocket) use persistent member variable as temp variables to encode/decode zlib messages in order to reduce transient allocations
## [9.2.3] - 2020-04-13
(ws) add a --runtime option to ws cobra_subscribe to optionally limit how much time it will run
## [9.2.2] - 2020-04-04
(third_party deps) fix #177, update bundled spdlog to 1.6.0
## [9.2.1] - 2020-04-04
(windows) when using OpenSSL, the system store is used to populate the cacert. No need to ship a cacert.pem file with your app.

View File

@ -18,12 +18,10 @@ There is a unittest which can be executed by typing `make test`.
Options for building:
* `-DUSE_TLS=1` will enable TLS support
* `-DUSE_OPEN_SSL=1` will use [openssl](https://www.openssl.org/) for the TLS support (default on Linux and Windows)
* `-DUSE_MBED_TLS=1` will use [mbedlts](https://tls.mbed.org/) for the TLS support
* `-DUSE_MBED_TLS=1` will use [mbedlts](https://tls.mbed.org/) for the TLS support (default on Windows)
* `-DUSE_WS=1` will build the ws interactive command line tool
* `-DUSE_TEST=1` will build the unittest
If you are on Windows, look at the [appveyor](https://github.com/machinezone/IXWebSocket/blob/master/appveyor.yml) file (not maintained much though) or rather the [github actions](https://github.com/machinezone/IXWebSocket/blob/master/.github/workflows/ccpp.yml#L40) which have instructions for building dependencies.
If you are on Windows, look at the [appveyor](https://github.com/machinezone/IXWebSocket/blob/master/appveyor.yml) file that has instructions for building dependencies.
It is also possible to externally include the project, so that everything is fetched over the wire when you build like so:
@ -61,7 +59,7 @@ Note that the version listed here might not be the latest one. See Bintray or th
There is a Dockerfile for running the unittest on Linux, and to run the `ws` tool. It is also available on the docker registry.
```
docker run docker.pkg.github.com/machinezone/ixwebsocket/ws:latest --help
docker run bsergean/ws
```
To use docker-compose you must make a docker container first.

View File

@ -38,7 +38,8 @@ The regression test is running after each commit on github actions for multiple
## Limitations
* On some configuration (mostly Android) certificate validation needs to be setup so that SocketTLSOptions.caFile point to a pem file, such as the one distributed by Firefox. Unless that setup is done connecting to a wss endpoint will display an error. With mbedtls the message will contain `error in handshake : X509 - Certificate verification failed, e.g. CRL, CA or signature check failed`.
* On Windows and Android certificate validation needs to be setup so that SocketTLSOptions.caFile point to a pem file, such as the one distributed by Firefox. Unless that setup is done connecting to a wss endpoint will display an error. On Windows with mbedtls the message will contain `error in handshake : X509 - Certificate verification failed, e.g. CRL, CA or signature check failed`.
* There is no convenient way to embed a ca cert.
* Automatic reconnection works at the TCP socket level, and will detect remote end disconnects. However, if the device/computer network become unreachable (by turning off wifi), it is quite hard to reliably and timely detect it at the socket level using `recv` and `send` error codes. [Here](https://stackoverflow.com/questions/14782143/linux-socket-how-to-detect-disconnected-network-in-a-client-program) is a good discussion on the subject. This behavior is consistent with other runtimes such as node.js. One way to detect a disconnected device with low level C code is to do a name resolution with DNS but this can be expensive. Mobile devices have good and reliable API to do that.
* The server code is using select to detect incoming data, and creates one OS thread per connection. This is not as scalable as strategies using epoll or kqueue.

View File

@ -4,7 +4,7 @@ Notes on how we can update the different packages for ixwebsocket.
Visit the [releases](https://github.com/machinezone/IXWebSocket/releases) page on Github. A tag must have been made first.
Download the latest entry.
Download the latest entry.
```
$ cd /tmp

View File

@ -447,7 +447,7 @@ Additional TLS options can be configured by passing a `ix::SocketTLSOptions` ins
webSocket.setTLSOptions({
.certFile = "path/to/cert/file.pem",
.keyFile = "path/to/key/file.pem",
.caFile = "path/to/trust/bundle/file.pem", // as a file, or in memory buffer in PEM format
.caFile = "path/to/trust/bundle/file.pem",
.tls = true // required in server mode
});
```
@ -461,7 +461,6 @@ On a server, this is necessary for TLS support.
Specifying `caFile` configures the trusted roots bundle file (in PEM format) that will be used to verify peer certificates.
- The special value of `SYSTEM` (the default) indicates that the system-configured trust bundle should be used; this is generally what you want when connecting to any publicly exposed API/server.
- The special value of `NONE` can be used to disable peer verification; this is only recommended to rule out certificate verification when testing connectivity.
- If the value contain the special value `-----BEGIN CERTIFICATE-----`, the value will be read from memory, and not from a file. This is convenient on platforms like Android where reading / writing to the file system can be challenging without proper permissions, or without knowing the location of a temp directory.
For a client, specifying `caFile` can be used if connecting to a server that uses a self-signed cert, or when using a custom CA in an internal environment.

View File

@ -4,19 +4,15 @@
#
set (IXBOTS_SOURCES
ixbots/IXCobraBot.cpp
ixbots/IXCobraToSentryBot.cpp
ixbots/IXCobraToStatsdBot.cpp
ixbots/IXCobraToStdoutBot.cpp
ixbots/IXQueueManager.cpp
ixbots/IXStatsdClient.cpp
)
set (IXBOTS_HEADERS
ixbots/IXCobraBot.h
ixbots/IXCobraToSentryBot.h
ixbots/IXCobraToStatsdBot.h
ixbots/IXCobraToStdoutBot.h
ixbots/IXQueueManager.h
ixbots/IXStatsdClient.h
)
@ -31,6 +27,11 @@ if (NOT JSONCPP_FOUND)
set(JSONCPP_INCLUDE_DIRS ../third_party/jsoncpp)
endif()
find_package(SpdLog)
if (NOT SPDLOG_FOUND)
set(SPDLOG_INCLUDE_DIRS ../third_party/spdlog/include)
endif()
set(IXBOTS_INCLUDE_DIRS
.
..

View File

@ -1,321 +0,0 @@
/*
* IXCobraBot.cpp
* Author: Benjamin Sergeant
* Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
*/
#include "IXCobraBot.h"
#include "IXQueueManager.h"
#include <ixcobra/IXCobraConnection.h>
#include <ixcore/utils/IXCoreLogger.h>
#include <algorithm>
#include <chrono>
#include <sstream>
#include <thread>
#include <vector>
namespace ix
{
int64_t CobraBot::run(const CobraConfig& config,
const std::string& channel,
const std::string& filter,
const std::string& position,
bool verbose,
size_t maxQueueSize,
bool useQueue,
bool enableHeartbeat,
int runtime)
{
ix::CobraConnection conn;
conn.configure(config);
conn.connect();
Json::FastWriter jsonWriter;
std::atomic<uint64_t> sentCount(0);
std::atomic<uint64_t> receivedCount(0);
uint64_t sentCountTotal(0);
uint64_t receivedCountTotal(0);
uint64_t sentCountPerSecs(0);
uint64_t receivedCountPerSecs(0);
std::atomic<bool> stop(false);
std::atomic<bool> throttled(false);
std::atomic<bool> fatalCobraError(false);
QueueManager queueManager(maxQueueSize);
auto timer = [&sentCount,
&receivedCount,
&sentCountTotal,
&receivedCountTotal,
&sentCountPerSecs,
&receivedCountPerSecs,
&stop] {
while (!stop)
{
//
// We cannot write to sentCount and receivedCount
// as those are used externally, so we need to introduce
// our own counters
//
std::stringstream ss;
ss << "messages received "
<< receivedCountPerSecs
<< " "
<< receivedCountTotal
<< " sent "
<< sentCountPerSecs
<< " "
<< sentCountTotal;
CoreLogger::info(ss.str());
receivedCountPerSecs = receivedCount - receivedCountTotal;
sentCountPerSecs = sentCount - receivedCountTotal;
receivedCountTotal += receivedCountPerSecs;
sentCountTotal += sentCountPerSecs;
auto duration = std::chrono::seconds(1);
std::this_thread::sleep_for(duration);
}
CoreLogger::info("timer thread done");
};
std::thread t1(timer);
auto heartbeat = [&sentCount, &receivedCount, &stop, &enableHeartbeat] {
std::string state("na");
if (!enableHeartbeat) return;
while (!stop)
{
std::stringstream ss;
ss << "messages received " << receivedCount;
ss << "messages sent " << sentCount;
std::string currentState = ss.str();
if (currentState == state)
{
CoreLogger::error("no messages received or sent for 1 minute, exiting");
exit(1);
}
state = currentState;
auto duration = std::chrono::minutes(1);
std::this_thread::sleep_for(duration);
}
CoreLogger::info("heartbeat thread done");
};
std::thread t2(heartbeat);
auto sender =
[this, &queueManager, verbose, &sentCount, &stop, &throttled, &fatalCobraError] {
while (true)
{
auto data = queueManager.pop();
Json::Value msg = data.first;
std::string position = data.second;
if (stop) break;
if (msg.isNull()) continue;
if (_onBotMessageCallback &&
_onBotMessageCallback(msg, position, verbose, throttled, fatalCobraError))
{
// That might be too noisy
if (verbose)
{
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);
conn.setEventCallback([this,
&conn,
&channel,
&filter,
&subscriptionPosition,
&jsonWriter,
verbose,
&throttled,
&receivedCount,
&fatalCobraError,
&useQueue,
&queueManager,
&sentCount](const CobraEventPtr& event) {
if (event->type == ix::CobraEventType::Open)
{
CoreLogger::info("Subscriber connected");
for (auto&& it : event->headers)
{
CoreLogger::info(it.first + "::" + it.second);
}
}
else if (event->type == ix::CobraEventType::Closed)
{
CoreLogger::info("Subscriber closed: {}" + event->errMsg);
}
else if (event->type == ix::CobraEventType::Authenticated)
{
CoreLogger::info("Subscriber authenticated");
CoreLogger::info("Subscribing to " + channel);
CoreLogger::info("Subscribing at position " + subscriptionPosition);
CoreLogger::info("Subscribing with filter " + filter);
conn.subscribe(channel,
filter,
subscriptionPosition,
[this,
&jsonWriter,
verbose,
&throttled,
&receivedCount,
&queueManager,
&useQueue,
&subscriptionPosition,
&fatalCobraError,
&sentCount](const Json::Value& msg, const std::string& position) {
if (verbose)
{
CoreLogger::info("Subscriber received message "
+ position + " -> " + jsonWriter.write(msg));
}
subscriptionPosition = position;
// If we cannot send to sentry fast enough, drop the message
if (throttled)
{
return;
}
++receivedCount;
if (useQueue)
{
queueManager.add(msg, position);
}
else
{
if (_onBotMessageCallback &&
_onBotMessageCallback(
msg, position, verbose, throttled, fatalCobraError))
{
// That might be too noisy
if (verbose)
{
CoreLogger::info("cobra bot: sending succesfull");
}
++sentCount;
}
else
{
CoreLogger::error("cobra bot: error sending");
}
}
});
}
else if (event->type == ix::CobraEventType::Subscribed)
{
CoreLogger::info("Subscriber: subscribed to channel " + event->subscriptionId);
}
else if (event->type == ix::CobraEventType::UnSubscribed)
{
CoreLogger::info("Subscriber: unsubscribed from channel " + event->subscriptionId);
}
else if (event->type == ix::CobraEventType::Error)
{
CoreLogger::error("Subscriber: error " + event->errMsg);
}
else if (event->type == ix::CobraEventType::Published)
{
CoreLogger::error("Published message hacked: " + std::to_string(event->msgId));
}
else if (event->type == ix::CobraEventType::Pong)
{
CoreLogger::info("Received websocket pong: " + event->errMsg);
}
else if (event->type == ix::CobraEventType::HandshakeError)
{
CoreLogger::error("Subscriber: Handshake error: " + event->errMsg);
fatalCobraError = true;
}
else if (event->type == ix::CobraEventType::AuthenticationError)
{
CoreLogger::error("Subscriber: Authentication error: " + event->errMsg);
fatalCobraError = true;
}
else if (event->type == ix::CobraEventType::SubscriptionError)
{
CoreLogger::error("Subscriber: Subscription error: " + event->errMsg);
fatalCobraError = true;
}
});
// Run forever
if (runtime == -1)
{
while (true)
{
auto duration = std::chrono::seconds(1);
std::this_thread::sleep_for(duration);
if (fatalCobraError) break;
}
}
// Run for a duration, used by unittesting now
else
{
for (int i = 0; i < runtime; ++i)
{
auto duration = std::chrono::seconds(1);
std::this_thread::sleep_for(duration);
if (fatalCobraError) break;
}
}
//
// Cleanup.
// join all the bg threads and stop them.
//
conn.disconnect();
stop = true;
// progress thread
t1.join();
// heartbeat thread
if (t2.joinable()) t2.join();
// sentry sender thread
t3.join();
return fatalCobraError ? -1 : (int64_t) sentCount;
}
void CobraBot::setOnBotMessageCallback(const OnBotMessageCallback& callback)
{
_onBotMessageCallback = callback;
}
} // namespace ix

View File

@ -1,43 +0,0 @@
/*
* IXCobraBot.h
* Author: Benjamin Sergeant
* Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include <atomic>
#include <functional>
#include <ixcobra/IXCobraConfig.h>
#include <json/json.h>
#include <stddef.h>
namespace ix
{
using OnBotMessageCallback = std::function<bool(const Json::Value&,
const std::string&,
const bool verbose,
std::atomic<bool>&,
std::atomic<bool>&)>;
class CobraBot
{
public:
CobraBot() = default;
int64_t run(const CobraConfig& config,
const std::string& channel,
const std::string& filter,
const std::string& position,
bool verbose,
size_t maxQueueSize,
bool useQueue,
bool enableHeartbeat,
int runtime);
void setOnBotMessageCallback(const OnBotMessageCallback& callback);
private:
OnBotMessageCallback _onBotMessageCallback;
};
} // namespace ix

View File

@ -5,113 +5,301 @@
*/
#include "IXCobraToSentryBot.h"
#include "IXCobraBot.h"
#include "IXQueueManager.h"
#include <ixcobra/IXCobraConnection.h>
#include <ixcore/utils/IXCoreLogger.h>
#include <chrono>
#include <ixcobra/IXCobraConnection.h>
#include <spdlog/spdlog.h>
#include <sstream>
#include <thread>
#include <vector>
namespace ix
{
int64_t cobra_to_sentry_bot(const CobraConfig& config,
const std::string& channel,
const std::string& filter,
const std::string& position,
SentryClient& sentryClient,
bool verbose,
size_t maxQueueSize,
bool enableHeartbeat,
int runtime)
int cobra_to_sentry_bot(const CobraConfig& config,
const std::string& channel,
const std::string& filter,
const std::string& position,
SentryClient& sentryClient,
bool verbose,
bool strict,
size_t maxQueueSize,
bool enableHeartbeat,
int runtime)
{
CobraBot bot;
bot.setOnBotMessageCallback([&sentryClient](const Json::Value& msg,
const std::string& /*position*/,
const bool verbose,
std::atomic<bool>& throttled,
std::atomic<bool> &
/*fatalCobraError*/) -> bool {
auto ret = sentryClient.send(msg, verbose);
HttpResponsePtr response = ret.first;
ix::CobraConnection conn;
conn.configure(config);
conn.connect();
if (!response)
Json::FastWriter jsonWriter;
std::atomic<uint64_t> sentCount(0);
std::atomic<uint64_t> receivedCount(0);
std::atomic<bool> errorSending(false);
std::atomic<bool> stop(false);
std::atomic<bool> throttled(false);
std::atomic<bool> fatalCobraError(false);
QueueManager queueManager(maxQueueSize);
auto timer = [&sentCount, &receivedCount, &stop] {
while (!stop)
{
CoreLogger::warn("Null HTTP Response");
return false;
spdlog::info("messages received {} sent {}", receivedCount, sentCount);
auto duration = std::chrono::seconds(1);
std::this_thread::sleep_for(duration);
}
if (verbose)
spdlog::info("timer thread done");
};
std::thread t1(timer);
auto heartbeat = [&sentCount, &receivedCount, &stop, &enableHeartbeat] {
std::string state("na");
if (!enableHeartbeat) return;
while (!stop)
{
for (auto it : response->headers)
{
CoreLogger::info(it.first + ": " + it.second);
}
std::stringstream ss;
ss << "messages received " << receivedCount;
ss << "messages sent " << sentCount;
CoreLogger::info("Upload size: " + std::to_string(response->uploadSize));
CoreLogger::info("Download size: " + std::to_string(response->downloadSize));
std::string currentState = ss.str();
CoreLogger::info("Status: " + std::to_string(response->statusCode));
if (response->errorCode != HttpErrorCode::Ok)
if (currentState == state)
{
CoreLogger::info("error message: " + response->errorMsg);
spdlog::error("no messages received or sent for 1 minute, exiting");
exit(1);
}
state = currentState;
if (response->headers["Content-Type"] != "application/octet-stream")
{
CoreLogger::info("payload: " + response->payload);
}
auto duration = std::chrono::minutes(1);
std::this_thread::sleep_for(duration);
}
bool success = response->statusCode == 200;
spdlog::info("heartbeat thread done");
};
if (!success)
{
CoreLogger::error("Error sending data to sentry: " + std::to_string(response->statusCode));
CoreLogger::error("Body: " + ret.second);
CoreLogger::error("Response: " + response->payload);
std::thread t2(heartbeat);
// Error 429 Too Many Requests
if (response->statusCode == 429)
auto sentrySender =
[&queueManager, verbose, &errorSending, &sentCount, &stop, &throttled, &sentryClient] {
while (true)
{
auto retryAfter = response->headers["Retry-After"];
std::stringstream ss;
ss << retryAfter;
int seconds;
ss >> seconds;
Json::Value msg = queueManager.pop();
if (!ss.eof() || ss.fail())
if (stop) break;
if (msg.isNull()) continue;
auto ret = sentryClient.send(msg, verbose);
HttpResponsePtr response = ret.first;
if (!response)
{
seconds = 30;
CoreLogger::warn("Error parsing Retry-After header. "
"Using " + retryAfter + " for the sleep duration");
spdlog::warn("Null HTTP Response");
continue;
}
CoreLogger::warn("Error 429 - Too Many Requests. ws will sleep "
"and retry after " + retryAfter + " seconds");
if (verbose)
{
for (auto it : response->headers)
{
spdlog::info("{}: {}", it.first, it.second);
}
throttled = true;
auto duration = std::chrono::seconds(seconds);
std::this_thread::sleep_for(duration);
throttled = false;
spdlog::info("Upload size: {}", response->uploadSize);
spdlog::info("Download size: {}", response->downloadSize);
spdlog::info("Status: {}", response->statusCode);
if (response->errorCode != HttpErrorCode::Ok)
{
spdlog::info("error message: {}", response->errorMsg);
}
if (response->headers["Content-Type"] != "application/octet-stream")
{
spdlog::info("payload: {}", response->payload);
}
}
if (response->statusCode != 200)
{
spdlog::error("Error sending data to sentry: {}", response->statusCode);
spdlog::error("Body: {}", ret.second);
spdlog::error("Response: {}", response->payload);
errorSending = true;
// Error 429 Too Many Requests
if (response->statusCode == 429)
{
auto retryAfter = response->headers["Retry-After"];
std::stringstream ss;
ss << retryAfter;
int seconds;
ss >> seconds;
if (!ss.eof() || ss.fail())
{
seconds = 30;
spdlog::warn("Error parsing Retry-After header. "
"Using {} for the sleep duration",
seconds);
}
spdlog::warn("Error 429 - Too Many Requests. ws will sleep "
"and retry after {} seconds",
retryAfter);
throttled = true;
auto duration = std::chrono::seconds(seconds);
std::this_thread::sleep_for(duration);
throttled = false;
}
}
else
{
++sentCount;
}
if (stop) break;
}
spdlog::info("sentrySender thread done");
};
std::thread t3(sentrySender);
conn.setEventCallback([&conn,
&channel,
&filter,
&position,
&jsonWriter,
verbose,
&throttled,
&receivedCount,
&fatalCobraError,
&queueManager](ix::CobraConnectionEventType eventType,
const std::string& errMsg,
const ix::WebSocketHttpHeaders& headers,
const std::string& subscriptionId,
CobraConnection::MsgId msgId) {
if (eventType == ix::CobraConnection_EventType_Open)
{
spdlog::info("Subscriber connected");
for (auto it : headers)
{
spdlog::info("{}: {}", it.first, it.second);
}
}
if (eventType == ix::CobraConnection_EventType_Closed)
{
spdlog::info("Subscriber closed");
}
else if (eventType == ix::CobraConnection_EventType_Authenticated)
{
spdlog::info("Subscriber authenticated");
conn.subscribe(channel,
filter,
position,
[&jsonWriter, verbose, &throttled, &receivedCount, &queueManager](
const Json::Value& msg, const std::string& position) {
if (verbose)
{
spdlog::info("Subscriber received message {} -> {}", position, jsonWriter.write(msg));
}
return success;
// If we cannot send to sentry fast enough, drop the message
if (throttled)
{
return;
}
++receivedCount;
queueManager.add(msg);
});
}
else if (eventType == ix::CobraConnection_EventType_Subscribed)
{
spdlog::info("Subscriber: subscribed to channel {}", subscriptionId);
}
else if (eventType == ix::CobraConnection_EventType_UnSubscribed)
{
spdlog::info("Subscriber: unsubscribed from channel {}", subscriptionId);
}
else if (eventType == ix::CobraConnection_EventType_Error)
{
spdlog::error("Subscriber: error {}", errMsg);
}
else if (eventType == ix::CobraConnection_EventType_Published)
{
spdlog::error("Published message hacked: {}", msgId);
}
else if (eventType == ix::CobraConnection_EventType_Pong)
{
spdlog::info("Received websocket pong");
}
else if (eventType == ix::CobraConnection_EventType_Handshake_Error)
{
spdlog::error("Subscriber: Handshake error: {}", errMsg);
fatalCobraError = true;
}
else if (eventType == ix::CobraConnection_EventType_Authentication_Error)
{
spdlog::error("Subscriber: Authentication error: {}", errMsg);
fatalCobraError = true;
}
else if (eventType == ix::CobraConnection_EventType_Subscription_Error)
{
spdlog::error("Subscriber: Subscription error: {}", errMsg);
fatalCobraError = true;
}
});
bool useQueue = true;
// Run forever
if (runtime == -1)
{
while (true)
{
auto duration = std::chrono::seconds(1);
std::this_thread::sleep_for(duration);
return bot.run(config,
channel,
filter,
position,
verbose,
maxQueueSize,
useQueue,
enableHeartbeat,
runtime);
if (strict && errorSending) break;
if (fatalCobraError) break;
}
}
// Run for a duration, used by unittesting now
else
{
for (int i = 0 ; i < runtime; ++i)
{
auto duration = std::chrono::seconds(1);
std::this_thread::sleep_for(duration);
if (strict && errorSending) break;
if (fatalCobraError) break;
}
}
//
// Cleanup.
// join all the bg threads and stop them.
//
conn.disconnect();
stop = true;
// progress thread
t1.join();
// heartbeat thread
if (t2.joinable()) t2.join();
// sentry sender thread
t3.join();
return ((strict && errorSending) || fatalCobraError) ? -1 : (int) sentCount;
}
} // namespace ix

View File

@ -5,20 +5,20 @@
*/
#pragma once
#include <cstdint>
#include <ixcobra/IXCobraConfig.h>
#include <ixsentry/IXSentryClient.h>
#include <string>
namespace ix
{
int64_t cobra_to_sentry_bot(const CobraConfig& config,
const std::string& channel,
const std::string& filter,
const std::string& position,
SentryClient& sentryClient,
bool verbose,
size_t maxQueueSize,
bool enableHeartbeat,
int runtime);
int cobra_to_sentry_bot(const CobraConfig& config,
const std::string& channel,
const std::string& filter,
const std::string& position,
SentryClient& sentryClient,
bool verbose,
bool strict,
size_t maxQueueSize,
bool enableHeartbeat,
int runtime);
} // namespace ix

View File

@ -5,14 +5,16 @@
*/
#include "IXCobraToStatsdBot.h"
#include "IXCobraBot.h"
#include "IXQueueManager.h"
#include "IXStatsdClient.h"
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <ixcobra/IXCobraConnection.h>
#include <ixcore/utils/IXCoreLogger.h>
#include <spdlog/spdlog.h>
#include <sstream>
#include <thread>
#include <vector>
namespace ix
@ -54,18 +56,18 @@ namespace ix
return val;
}
int64_t cobra_to_statsd_bot(const ix::CobraConfig& config,
const std::string& channel,
const std::string& filter,
const std::string& position,
StatsdClient& statsdClient,
const std::string& fields,
const std::string& gauge,
const std::string& timer,
bool verbose,
size_t maxQueueSize,
bool enableHeartbeat,
int runtime)
int cobra_to_statsd_bot(const ix::CobraConfig& config,
const std::string& channel,
const std::string& filter,
const std::string& position,
StatsdClient& statsdClient,
const std::string& fields,
const std::string& gauge,
const std::string& timer,
bool verbose,
size_t maxQueueSize,
bool enableHeartbeat,
int runtime)
{
ix::CobraConnection conn;
conn.configure(config);
@ -73,13 +75,65 @@ namespace ix
auto tokens = parseFields(fields);
CobraBot bot;
bot.setOnBotMessageCallback(
[&statsdClient, &tokens, &gauge, &timer](const Json::Value& msg,
const std::string& /*position*/,
const bool verbose,
std::atomic<bool>& /*throttled*/,
std::atomic<bool>& fatalCobraError) -> bool {
Json::FastWriter jsonWriter;
std::atomic<uint64_t> sentCount(0);
std::atomic<uint64_t> receivedCount(0);
std::atomic<bool> stop(false);
std::atomic<bool> fatalCobraError(false);
QueueManager queueManager(maxQueueSize);
auto progress = [&sentCount, &receivedCount, &stop] {
while (!stop)
{
spdlog::info("messages received {} sent {}", receivedCount, sentCount);
auto duration = std::chrono::seconds(1);
std::this_thread::sleep_for(duration);
}
spdlog::info("timer thread done");
};
std::thread t1(progress);
auto heartbeat = [&sentCount, &receivedCount, &stop, &enableHeartbeat] {
std::string state("na");
if (!enableHeartbeat) return;
while (!stop)
{
std::stringstream ss;
ss << "messages received " << receivedCount;
ss << "messages sent " << sentCount;
std::string currentState = ss.str();
if (currentState == state)
{
spdlog::error("no messages received or sent for 1 minute, exiting");
exit(1);
}
state = currentState;
auto duration = std::chrono::minutes(1);
std::this_thread::sleep_for(duration);
}
spdlog::info("heartbeat thread done");
};
std::thread t2(heartbeat);
auto statsdSender = [&statsdClient, &queueManager, &sentCount, &tokens, &stop, &gauge, &timer, &fatalCobraError, &verbose] {
while (true)
{
Json::Value msg = queueManager.pop();
if (stop) return;
if (msg.isNull()) continue;
std::string id;
for (auto&& attr : tokens)
{
@ -120,14 +174,14 @@ namespace ix
}
else
{
CoreLogger::error("Gauge " + gauge + " is not a numeric type");
spdlog::error("Gauge {} is not a numberic type", gauge);
fatalCobraError = true;
return false;
break;
}
if (verbose)
{
CoreLogger::info(id + " - " + attrName + " -> " + std::to_string(x));
spdlog::info("{} - {} -> {}", id, attrName, x);
}
if (!gauge.empty())
@ -140,19 +194,127 @@ namespace ix
}
}
return true;
sentCount += 1;
}
};
std::thread t3(statsdSender);
conn.setEventCallback(
[&conn, &channel, &filter, &position, &jsonWriter, verbose, &queueManager, &receivedCount, &fatalCobraError](
ix::CobraConnectionEventType eventType,
const std::string& errMsg,
const ix::WebSocketHttpHeaders& headers,
const std::string& subscriptionId,
CobraConnection::MsgId msgId) {
if (eventType == ix::CobraConnection_EventType_Open)
{
spdlog::info("Subscriber connected");
for (auto it : headers)
{
spdlog::info("{}: {}", it.first, it.second);
}
}
if (eventType == ix::CobraConnection_EventType_Closed)
{
spdlog::info("Subscriber closed");
}
else if (eventType == ix::CobraConnection_EventType_Authenticated)
{
spdlog::info("Subscriber authenticated");
conn.subscribe(channel,
filter,
position,
[&jsonWriter, &queueManager, verbose, &receivedCount](
const Json::Value& msg, const std::string& position) {
if (verbose)
{
spdlog::info("Subscriber received message {} -> {}", position, jsonWriter.write(msg));
}
receivedCount++;
++receivedCount;
queueManager.add(msg);
});
}
else if (eventType == ix::CobraConnection_EventType_Subscribed)
{
spdlog::info("Subscriber: subscribed to channel {}", subscriptionId);
}
else if (eventType == ix::CobraConnection_EventType_UnSubscribed)
{
spdlog::info("Subscriber: unsubscribed from channel {}", subscriptionId);
}
else if (eventType == ix::CobraConnection_EventType_Error)
{
spdlog::error("Subscriber: error {}", errMsg);
}
else if (eventType == ix::CobraConnection_EventType_Published)
{
spdlog::error("Published message hacked: {}", msgId);
}
else if (eventType == ix::CobraConnection_EventType_Pong)
{
spdlog::info("Received websocket pong");
}
else if (eventType == ix::CobraConnection_EventType_Handshake_Error)
{
spdlog::error("Subscriber: Handshake error: {}", errMsg);
fatalCobraError = true;
}
else if (eventType == ix::CobraConnection_EventType_Authentication_Error)
{
spdlog::error("Subscriber: Authentication error: {}", errMsg);
fatalCobraError = true;
}
else if (eventType == ix::CobraConnection_EventType_Subscription_Error)
{
spdlog::error("Subscriber: Subscription error: {}", errMsg);
fatalCobraError = true;
}
});
bool useQueue = true;
// Run forever
if (runtime == -1)
{
while (true)
{
auto duration = std::chrono::seconds(1);
std::this_thread::sleep_for(duration);
return bot.run(config,
channel,
filter,
position,
verbose,
maxQueueSize,
useQueue,
enableHeartbeat,
runtime);
if (fatalCobraError) break;
}
}
// Run for a duration, used by unittesting now
else
{
for (int i = 0 ; i < runtime; ++i)
{
auto duration = std::chrono::seconds(1);
std::this_thread::sleep_for(duration);
if (fatalCobraError) break;
}
}
//
// Cleanup.
// join all the bg threads and stop them.
//
conn.disconnect();
stop = true;
// progress thread
t1.join();
// heartbeat thread
if (t2.joinable()) t2.join();
// statsd sender thread
t3.join();
return fatalCobraError ? -1 : (int) sentCount;
}
} // namespace ix

View File

@ -5,24 +5,23 @@
*/
#pragma once
#include <cstdint>
#include <ixbots/IXStatsdClient.h>
#include <ixcobra/IXCobraConfig.h>
#include <stddef.h>
#include <ixbots/IXStatsdClient.h>
#include <string>
#include <stddef.h>
namespace ix
{
int64_t cobra_to_statsd_bot(const ix::CobraConfig& config,
const std::string& channel,
const std::string& filter,
const std::string& position,
StatsdClient& statsdClient,
const std::string& fields,
const std::string& gauge,
const std::string& timer,
bool verbose,
size_t maxQueueSize,
bool enableHeartbeat,
int runtime);
int cobra_to_statsd_bot(const ix::CobraConfig& config,
const std::string& channel,
const std::string& filter,
const std::string& position,
StatsdClient& statsdClient,
const std::string& fields,
const std::string& gauge,
const std::string& timer,
bool verbose,
size_t maxQueueSize,
bool enableHeartbeat,
int runtime);
} // namespace ix

View File

@ -1,107 +0,0 @@
/*
* IXCobraToStdoutBot.cpp
* Author: Benjamin Sergeant
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
*/
#include "IXCobraToStdoutBot.h"
#include "IXCobraBot.h"
#include "IXQueueManager.h"
#include <chrono>
#include <iostream>
#include <sstream>
namespace ix
{
using StreamWriterPtr = std::unique_ptr<Json::StreamWriter>;
StreamWriterPtr makeStreamWriter()
{
Json::StreamWriterBuilder builder;
builder["commentStyle"] = "None";
builder["indentation"] = ""; // will make the JSON object compact
std::unique_ptr<Json::StreamWriter> jsonWriter(builder.newStreamWriter());
return jsonWriter;
}
std::string timeSinceEpoch()
{
std::chrono::system_clock::time_point tp = std::chrono::system_clock::now();
std::chrono::system_clock::duration dtn = tp.time_since_epoch();
std::stringstream ss;
ss << dtn.count() * std::chrono::system_clock::period::num /
std::chrono::system_clock::period::den;
return ss.str();
}
void writeToStdout(bool fluentd,
const StreamWriterPtr& jsonWriter,
const Json::Value& msg,
const std::string& position)
{
Json::Value enveloppe;
if (fluentd)
{
enveloppe["producer"] = "cobra";
enveloppe["consumer"] = "fluentd";
Json::Value nestedMessage(msg);
nestedMessage["position"] = position;
nestedMessage["created_at"] = timeSinceEpoch();
enveloppe["message"] = nestedMessage;
jsonWriter->write(enveloppe, &std::cout);
std::cout << std::endl; // add lf and flush
}
else
{
enveloppe = msg;
std::cout << position << " ";
jsonWriter->write(enveloppe, &std::cout);
std::cout << std::endl;
}
}
int64_t cobra_to_stdout_bot(const CobraConfig& config,
const std::string& channel,
const std::string& filter,
const std::string& position,
bool fluentd,
bool quiet,
bool verbose,
size_t maxQueueSize,
bool enableHeartbeat,
int runtime)
{
CobraBot bot;
auto jsonWriter = makeStreamWriter();
bot.setOnBotMessageCallback(
[&fluentd, &quiet, &jsonWriter](const Json::Value& msg,
const std::string& position,
const bool /*verbose*/,
std::atomic<bool>& /*throttled*/,
std::atomic<bool> &
/*fatalCobraError*/) -> bool {
if (!quiet)
{
writeToStdout(fluentd, jsonWriter, msg, position);
}
return true;
});
bool useQueue = false;
return bot.run(config,
channel,
filter,
position,
verbose,
maxQueueSize,
useQueue,
enableHeartbeat,
runtime);
}
} // namespace ix

View File

@ -1,25 +0,0 @@
/*
* IXCobraToStdoutBot.h
* Author: Benjamin Sergeant
* Copyright (c) 2019-2020 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include <cstdint>
#include <ixcobra/IXCobraConfig.h>
#include <stddef.h>
#include <string>
namespace ix
{
int64_t cobra_to_stdout_bot(const ix::CobraConfig& config,
const std::string& channel,
const std::string& filter,
const std::string& position,
bool fluentd,
bool quiet,
bool verbose,
size_t maxQueueSize,
bool enableHeartbeat,
int runtime);
} // namespace ix

View File

@ -5,20 +5,19 @@
*/
#include "IXQueueManager.h"
#include <algorithm>
#include <vector>
#include <algorithm>
namespace ix
{
std::pair<Json::Value, std::string> QueueManager::pop()
Json::Value QueueManager::pop()
{
std::unique_lock<std::mutex> lock(_mutex);
if (_queues.empty())
{
Json::Value val;
return std::make_pair(val, std::string());
return val;
}
std::vector<std::string> games;
@ -36,7 +35,7 @@ namespace ix
if (_queues[game].empty())
{
Json::Value val;
return std::make_pair(val, std::string());
return val;
}
auto msg = _queues[game].front();
@ -44,7 +43,7 @@ namespace ix
return msg;
}
void QueueManager::add(const Json::Value& msg, const std::string& position)
void QueueManager::add(Json::Value msg)
{
std::unique_lock<std::mutex> lock(_mutex);
@ -60,8 +59,8 @@ namespace ix
// in queuing too many events.
if (_queues[game].size() < _maxQueueSize)
{
_queues[game].push(std::make_pair(msg, position));
_queues[game].push(msg);
_condition.notify_one();
}
}
} // namespace ix
}

View File

@ -6,12 +6,12 @@
#pragma once
#include <condition_variable>
#include <json/json.h>
#include <map>
#include <mutex>
#include <queue>
#include <stddef.h>
#include <json/json.h>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <map>
namespace ix
{
@ -23,13 +23,13 @@ namespace ix
{
}
std::pair<Json::Value, std::string> pop();
void add(const Json::Value& msg, const std::string& position);
Json::Value pop();
void add(Json::Value msg);
private:
std::map<std::string, std::queue<std::pair<Json::Value, std::string>>> _queues;
std::map<std::string, std::queue<Json::Value>> _queues;
std::mutex _mutex;
std::condition_variable _condition;
size_t _maxQueueSize;
};
} // namespace ix
}

View File

@ -39,21 +39,24 @@
#include "IXStatsdClient.h"
#include <iostream>
#include <ixwebsocket/IXNetSystem.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <iostream>
namespace ix
{
StatsdClient::StatsdClient(const std::string& host, int port, const std::string& prefix)
: _host(host)
, _port(port)
, _prefix(prefix)
, _stop(false)
StatsdClient::StatsdClient(const std::string& host,
int port,
const std::string& prefix)
: _host(host)
, _port(port)
, _prefix(prefix)
, _stop(false)
{
_thread = std::thread([this] {
_thread = std::thread([this]
{
while (!_stop)
{
flushQueue();
@ -116,8 +119,8 @@ namespace ix
cleanup(key);
char buf[256];
snprintf(
buf, sizeof(buf), "%s%s:%zd|%s\n", _prefix.c_str(), key.c_str(), value, type.c_str());
snprintf(buf, sizeof(buf), "%s%s:%zd|%s\n",
_prefix.c_str(), key.c_str(), value, type.c_str());
enqueue(buf);
return 0;
@ -139,7 +142,9 @@ namespace ix
auto ret = _socket.sendto(message);
if (ret != 0)
{
std::cerr << "error: " << strerror(UdpSocket::getErrno()) << std::endl;
std::cerr << "error: "
<< strerror(UdpSocket::getErrno())
<< std::endl;
}
_queue.pop_front();
}

View File

@ -6,20 +6,21 @@
#pragma once
#include <atomic>
#include <deque>
#include <ixwebsocket/IXUdpSocket.h>
#include <mutex>
#include <string>
#include <thread>
#include <deque>
#include <mutex>
#include <atomic>
namespace ix
{
class StatsdClient
{
public:
StatsdClient(const std::string& host = "127.0.0.1",
int port = 8125,
StatsdClient(const std::string& host="127.0.0.1",
int port=8125,
const std::string& prefix = "");
~StatsdClient();

View File

@ -14,7 +14,6 @@ set (IXCOBRA_HEADERS
ixcobra/IXCobraMetricsThreadedPublisher.h
ixcobra/IXCobraMetricsPublisher.h
ixcobra/IXCobraConfig.h
ixcobra/IXCobraEventType.h
)
add_library(ixcobra STATIC

View File

@ -6,8 +6,8 @@
#pragma once
#include <ixwebsocket/IXSocketTLSOptions.h>
#include <ixwebsocket/IXWebSocketPerMessageDeflateOptions.h>
#include <ixwebsocket/IXSocketTLSOptions.h>
namespace ix
{

View File

@ -5,17 +5,17 @@
*/
#include "IXCobraConnection.h"
#include <ixcrypto/IXHMac.h>
#include <ixwebsocket/IXWebSocket.h>
#include <ixwebsocket/IXSocketTLSOptions.h>
#include <algorithm>
#include <cassert>
#include <stdexcept>
#include <cmath>
#include <cassert>
#include <cstring>
#include <iostream>
#include <ixcrypto/IXHMac.h>
#include <ixwebsocket/IXSocketTLSOptions.h>
#include <ixwebsocket/IXWebSocket.h>
#include <sstream>
#include <stdexcept>
namespace ix
@ -26,12 +26,12 @@ namespace ix
constexpr CobraConnection::MsgId CobraConnection::kInvalidMsgId;
constexpr int CobraConnection::kPingIntervalSecs;
CobraConnection::CobraConnection()
: _webSocket(new WebSocket())
, _publishMode(CobraConnection_PublishMode_Immediate)
, _authenticated(false)
, _eventCallback(nullptr)
, _id(1)
CobraConnection::CobraConnection() :
_webSocket(new WebSocket()),
_publishMode(CobraConnection_PublishMode_Immediate),
_authenticated(false),
_eventCallback(nullptr),
_id(1)
{
_pdu["action"] = "rtm/publish";
@ -87,7 +87,7 @@ namespace ix
_eventCallback = eventCallback;
}
void CobraConnection::invokeEventCallback(ix::CobraEventType eventType,
void CobraConnection::invokeEventCallback(ix::CobraConnectionEventType eventType,
const std::string& errorMsg,
const WebSocketHttpHeaders& headers,
const std::string& subscriptionId,
@ -96,8 +96,7 @@ namespace ix
std::lock_guard<std::mutex> lock(_eventCallbackMutex);
if (_eventCallback)
{
_eventCallback(
std::make_unique<CobraEvent>(eventType, errorMsg, headers, subscriptionId, msgId));
_eventCallback(eventType, errorMsg, headers, subscriptionId, msgId);
}
}
@ -106,7 +105,7 @@ namespace ix
{
std::stringstream ss;
ss << errorMsg << " : received pdu => " << serializedPdu;
invokeEventCallback(ix::CobraEventType::Error, ss.str());
invokeEventCallback(ix::CobraConnection_EventType_Error, ss.str());
}
void CobraConnection::disconnect()
@ -117,119 +116,126 @@ namespace ix
void CobraConnection::initWebSocketOnMessageCallback()
{
_webSocket->setOnMessageCallback([this](const ix::WebSocketMessagePtr& msg) {
CobraConnection::invokeTrafficTrackerCallback(msg->wireSize, true);
std::stringstream ss;
if (msg->type == ix::WebSocketMessageType::Open)
_webSocket->setOnMessageCallback(
[this](const ix::WebSocketMessagePtr& msg)
{
invokeEventCallback(ix::CobraEventType::Open, std::string(), msg->openInfo.headers);
sendHandshakeMessage();
}
else if (msg->type == ix::WebSocketMessageType::Close)
{
_authenticated = false;
CobraConnection::invokeTrafficTrackerCallback(msg->wireSize, true);
std::stringstream ss;
ss << "Close code " << msg->closeInfo.code;
ss << " reason " << msg->closeInfo.reason;
invokeEventCallback(ix::CobraEventType::Closed, ss.str());
}
else if (msg->type == ix::WebSocketMessageType::Message)
{
Json::Value data;
Json::Reader reader;
if (!reader.parse(msg->str, data))
if (msg->type == ix::WebSocketMessageType::Open)
{
invokeErrorCallback("Invalid json", msg->str);
return;
invokeEventCallback(ix::CobraConnection_EventType_Open,
std::string(),
msg->openInfo.headers);
sendHandshakeMessage();
}
if (!data.isMember("action"))
else if (msg->type == ix::WebSocketMessageType::Close)
{
invokeErrorCallback("Missing action", msg->str);
return;
_authenticated = false;
std::stringstream ss;
ss << "Close code " << msg->closeInfo.code;
ss << " reason " << msg->closeInfo.reason;
invokeEventCallback(ix::CobraConnection_EventType_Closed,
ss.str());
}
auto action = data["action"].asString();
if (action == "auth/handshake/ok")
else if (msg->type == ix::WebSocketMessageType::Message)
{
if (!handleHandshakeResponse(data))
Json::Value data;
Json::Reader reader;
if (!reader.parse(msg->str, data))
{
invokeErrorCallback("Error extracting nonce from handshake response",
invokeErrorCallback("Invalid json", msg->str);
return;
}
if (!data.isMember("action"))
{
invokeErrorCallback("Missing action", msg->str);
return;
}
auto action = data["action"].asString();
if (action == "auth/handshake/ok")
{
if (!handleHandshakeResponse(data))
{
invokeErrorCallback("Error extracting nonce from handshake response", msg->str);
}
}
else if (action == "auth/handshake/error")
{
invokeEventCallback(ix::CobraConnection_EventType_Handshake_Error,
msg->str);
}
}
else if (action == "auth/handshake/error")
{
invokeEventCallback(ix::CobraEventType::HandshakeError, msg->str);
}
else if (action == "auth/authenticate/ok")
{
_authenticated = true;
invokeEventCallback(ix::CobraEventType::Authenticated);
flushQueue();
}
else if (action == "auth/authenticate/error")
{
invokeEventCallback(ix::CobraEventType::AuthenticationError, msg->str);
}
else if (action == "rtm/subscription/data")
{
handleSubscriptionData(data);
}
else if (action == "rtm/subscribe/ok")
{
if (!handleSubscriptionResponse(data))
else if (action == "auth/authenticate/ok")
{
invokeErrorCallback("Error processing subscribe response", msg->str);
_authenticated = true;
invokeEventCallback(ix::CobraConnection_EventType_Authenticated);
flushQueue();
}
else if (action == "auth/authenticate/error")
{
invokeEventCallback(ix::CobraConnection_EventType_Authentication_Error,
msg->str);
}
else if (action == "rtm/subscription/data")
{
handleSubscriptionData(data);
}
else if (action == "rtm/subscribe/ok")
{
if (!handleSubscriptionResponse(data))
{
invokeErrorCallback("Error processing subscribe response", msg->str);
}
}
else if (action == "rtm/subscribe/error")
{
invokeEventCallback(ix::CobraConnection_EventType_Subscription_Error,
msg->str);
}
else if (action == "rtm/unsubscribe/ok")
{
if (!handleUnsubscriptionResponse(data))
{
invokeErrorCallback("Error processing unsubscribe response", msg->str);
}
}
else if (action == "rtm/unsubscribe/error")
{
invokeErrorCallback("Unsubscription error", msg->str);
}
else if (action == "rtm/publish/ok")
{
if (!handlePublishResponse(data))
{
invokeErrorCallback("Error processing publish response", msg->str);
}
}
else if (action == "rtm/publish/error")
{
invokeErrorCallback("Publish error", msg->str);
}
else
{
invokeErrorCallback("Un-handled message type", msg->str);
}
}
else if (action == "rtm/subscribe/error")
else if (msg->type == ix::WebSocketMessageType::Error)
{
invokeEventCallback(ix::CobraEventType::SubscriptionError, msg->str);
std::stringstream ss;
ss << "Connection error: " << msg->errorInfo.reason << std::endl;
ss << "#retries: " << msg->errorInfo.retries << std::endl;
ss << "Wait time(ms): " << msg->errorInfo.wait_time << std::endl;
ss << "HTTP Status: " << msg->errorInfo.http_status << std::endl;
invokeErrorCallback(ss.str(), std::string());
}
else if (action == "rtm/unsubscribe/ok")
else if (msg->type == ix::WebSocketMessageType::Pong)
{
if (!handleUnsubscriptionResponse(data))
{
invokeErrorCallback("Error processing unsubscribe response", msg->str);
}
invokeEventCallback(ix::CobraConnection_EventType_Pong);
}
else if (action == "rtm/unsubscribe/error")
{
invokeErrorCallback("Unsubscription error", msg->str);
}
else if (action == "rtm/publish/ok")
{
if (!handlePublishResponse(data))
{
invokeErrorCallback("Error processing publish response", msg->str);
}
}
else if (action == "rtm/publish/error")
{
invokeErrorCallback("Publish error", msg->str);
}
else
{
invokeErrorCallback("Un-handled message type", msg->str);
}
}
else if (msg->type == ix::WebSocketMessageType::Error)
{
std::stringstream ss;
ss << "Connection error: " << msg->errorInfo.reason << std::endl;
ss << "#retries: " << msg->errorInfo.retries << std::endl;
ss << "Wait time(ms): " << msg->errorInfo.wait_time << std::endl;
ss << "HTTP Status: " << msg->errorInfo.http_status << std::endl;
invokeErrorCallback(ss.str(), std::string());
}
else if (msg->type == ix::WebSocketMessageType::Pong)
{
invokeEventCallback(ix::CobraEventType::Pong, msg->str);
}
});
}
@ -243,13 +249,12 @@ namespace ix
return _publishMode;
}
void CobraConnection::configure(
const std::string& appkey,
const std::string& endpoint,
const std::string& rolename,
const std::string& rolesecret,
const WebSocketPerMessageDeflateOptions& webSocketPerMessageDeflateOptions,
const SocketTLSOptions& socketTLSOptions)
void CobraConnection::configure(const std::string& appkey,
const std::string& endpoint,
const std::string& rolename,
const std::string& rolesecret,
const WebSocketPerMessageDeflateOptions& webSocketPerMessageDeflateOptions,
const SocketTLSOptions& socketTLSOptions)
{
_roleName = rolename;
_roleSecret = rolesecret;
@ -391,9 +396,8 @@ namespace ix
if (!subscriptionId.isString()) return false;
invokeEventCallback(ix::CobraEventType::Subscribed,
std::string(),
WebSocketHttpHeaders(),
invokeEventCallback(ix::CobraConnection_EventType_Subscribed,
std::string(), WebSocketHttpHeaders(),
subscriptionId.asString());
return true;
}
@ -410,9 +414,8 @@ namespace ix
if (!subscriptionId.isString()) return false;
invokeEventCallback(ix::CobraEventType::UnSubscribed,
std::string(),
WebSocketHttpHeaders(),
invokeEventCallback(ix::CobraConnection_EventType_UnSubscribed,
std::string(), WebSocketHttpHeaders(),
subscriptionId.asString());
return true;
}
@ -459,11 +462,9 @@ namespace ix
uint64_t msgId = id.asUInt64();
invokeEventCallback(ix::CobraEventType::Published,
std::string(),
WebSocketHttpHeaders(),
std::string(),
msgId);
invokeEventCallback(ix::CobraConnection_EventType_Published,
std::string(), WebSocketHttpHeaders(),
std::string(), msgId);
invokePublishTrackerCallback(false, true);
@ -493,7 +494,9 @@ namespace ix
}
std::pair<CobraConnection::MsgId, std::string> CobraConnection::prePublish(
const Json::Value& channels, const Json::Value& msg, bool addToQueue)
const Json::Value& channels,
const Json::Value& msg,
bool addToQueue)
{
std::lock_guard<std::mutex> lock(_prePublishMutex);
@ -659,7 +662,8 @@ namespace ix
bool CobraConnection::publishMessage(const std::string& serializedJson)
{
auto webSocketSendInfo = _webSocket->send(serializedJson);
CobraConnection::invokeTrafficTrackerCallback(webSocketSendInfo.wireSize, false);
CobraConnection::invokeTrafficTrackerCallback(webSocketSendInfo.wireSize,
false);
return webSocketSendInfo.success;
}

View File

@ -6,19 +6,18 @@
#pragma once
#include "IXCobraConfig.h"
#include "IXCobraEvent.h"
#include "IXCobraEventType.h"
#include <ixwebsocket/IXWebSocketHttpHeaders.h>
#include <ixwebsocket/IXWebSocketPerMessageDeflateOptions.h>
#include <json/json.h>
#include <limits>
#include <memory>
#include <mutex>
#include <queue>
#include <string>
#include <thread>
#include <unordered_map>
#include <limits>
#include "IXCobraConfig.h"
#ifdef max
#undef max
@ -29,6 +28,21 @@ namespace ix
class WebSocket;
struct SocketTLSOptions;
enum CobraConnectionEventType
{
CobraConnection_EventType_Authenticated = 0,
CobraConnection_EventType_Error = 1,
CobraConnection_EventType_Open = 2,
CobraConnection_EventType_Closed = 3,
CobraConnection_EventType_Subscribed = 4,
CobraConnection_EventType_UnSubscribed = 5,
CobraConnection_EventType_Published = 6,
CobraConnection_EventType_Pong = 7,
CobraConnection_EventType_Handshake_Error = 8,
CobraConnection_EventType_Authentication_Error = 9,
CobraConnection_EventType_Subscription_Error = 10
};
enum CobraConnectionPublishMode
{
CobraConnection_PublishMode_Immediate = 0,
@ -36,7 +50,11 @@ namespace ix
};
using SubscriptionCallback = std::function<void(const Json::Value&, const std::string&)>;
using EventCallback = std::function<void(const CobraEventPtr&)>;
using EventCallback = std::function<void(CobraConnectionEventType,
const std::string&,
const WebSocketHttpHeaders&,
const std::string&,
uint64_t msgId)>;
using TrafficTrackerCallback = std::function<void(size_t size, bool incoming)>;
using PublishTrackerCallback = std::function<void(bool sent, bool acked)>;
@ -120,9 +138,10 @@ namespace ix
/// Prepare a message for transmission
/// (update the pdu, compute a msgId, serialize json to a string)
std::pair<CobraConnection::MsgId, std::string> prePublish(const Json::Value& channels,
const Json::Value& msg,
bool addToQueue);
std::pair<CobraConnection::MsgId, std::string> prePublish(
const Json::Value& channels,
const Json::Value& msg,
bool addToQueue);
/// Attempt to send next message from the internal queue
bool publishNext();
@ -152,7 +171,7 @@ namespace ix
static void invokePublishTrackerCallback(bool sent, bool acked);
/// Invoke event callbacks
void invokeEventCallback(CobraEventType eventType,
void invokeEventCallback(CobraConnectionEventType eventType,
const std::string& errorMsg = std::string(),
const WebSocketHttpHeaders& headers = WebSocketHttpHeaders(),
const std::string& subscriptionId = std::string(),

View File

@ -1,41 +0,0 @@
/*
* IXCobraEvent.h
* Author: Benjamin Sergeant
* Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include "IXCobraEventType.h"
#include <cstdint>
#include <ixwebsocket/IXWebSocketHttpHeaders.h>
#include <memory>
#include <string>
namespace ix
{
struct CobraEvent
{
ix::CobraEventType type;
const std::string& errMsg;
const ix::WebSocketHttpHeaders& headers;
const std::string& subscriptionId;
uint64_t msgId; // CobraConnection::MsgId
CobraEvent(ix::CobraEventType t,
const std::string& e,
const ix::WebSocketHttpHeaders& h,
const std::string& s,
uint64_t m)
: type(t)
, errMsg(e)
, headers(h)
, subscriptionId(s)
, msgId(m)
{
;
}
};
using CobraEventPtr = std::unique_ptr<CobraEvent>;
} // namespace ix

View File

@ -1,25 +0,0 @@
/*
* IXCobraEventType.h
* Author: Benjamin Sergeant
* Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
*/
#pragma once
namespace ix
{
enum class CobraEventType
{
Authenticated = 0,
Error = 1,
Open = 2,
Closed = 3,
Subscribed = 4,
UnSubscribed = 5,
Published = 6,
Pong = 7,
HandshakeError = 8,
AuthenticationError = 9,
SubscriptionError = 10
};
}

View File

@ -5,9 +5,9 @@
*/
#include "IXCobraMetricsPublisher.h"
#include <ixwebsocket/IXSocketTLSOptions.h>
#include <algorithm>
#include <ixwebsocket/IXSocketTLSOptions.h>
#include <stdexcept>
@ -17,8 +17,8 @@ namespace ix
const std::string CobraMetricsPublisher::kSetRateControlId = "sms_set_rate_control_id";
const std::string CobraMetricsPublisher::kSetBlacklistId = "sms_set_blacklist_id";
CobraMetricsPublisher::CobraMetricsPublisher()
: _enabled(true)
CobraMetricsPublisher::CobraMetricsPublisher() :
_enabled(true)
{
}
@ -27,7 +27,8 @@ namespace ix
;
}
void CobraMetricsPublisher::configure(const CobraConfig& config, const std::string& channel)
void CobraMetricsPublisher::configure(const CobraConfig& config,
const std::string& channel)
{
// Configure the satori connection and start its publish background thread
_cobra_metrics_theaded_publisher.configure(config, channel);
@ -41,7 +42,7 @@ namespace ix
}
void CobraMetricsPublisher::setGenericAttributes(const std::string& attrName,
const Json::Value& value)
const Json::Value& value)
{
std::lock_guard<std::mutex> lock(_device_mutex);
_device[attrName] = value;
@ -106,7 +107,8 @@ namespace ix
auto last_update = _last_update.find(id);
if (last_update == _last_update.end()) return false;
auto timeDeltaFromLastSend = std::chrono::steady_clock::now() - last_update->second;
auto timeDeltaFromLastSend =
std::chrono::steady_clock::now() - last_update->second;
return timeDeltaFromLastSend < std::chrono::seconds(rate_control_it->second);
}
@ -121,7 +123,8 @@ namespace ix
{
auto now = std::chrono::system_clock::now();
auto ms =
std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count();
std::chrono::duration_cast<std::chrono::milliseconds>(
now.time_since_epoch()).count();
return ms;
}
@ -162,9 +165,10 @@ namespace ix
return true;
}
CobraConnection::MsgId CobraMetricsPublisher::push(const std::string& id,
const Json::Value& data,
bool shouldPushTest)
CobraConnection::MsgId CobraMetricsPublisher::push(
const std::string& id,
const Json::Value& data,
bool shouldPushTest)
{
if (shouldPushTest && !shouldPush(id)) return CobraConnection::kInvalidMsgId;

View File

@ -40,7 +40,8 @@ namespace ix
/// Configuration / set keys, etc...
/// All input data but the channel name is encrypted with rc4
void configure(const CobraConfig& config, const std::string& channel);
void configure(const CobraConfig& config,
const std::string& channel);
/// Setter for the list of blacklisted metrics ids.
/// That list is sorted internally for fast lookups
@ -67,14 +68,10 @@ namespace ix
/// shouldPush method for places where we want to be as lightweight as possible when
/// collecting metrics. When set to false, it is used so that we don't do double work when
/// computing whether a metrics should be sent or not.
CobraConnection::MsgId push(const std::string& id,
const Json::Value& data,
bool shouldPushTest = true);
CobraConnection::MsgId push(const std::string& id, const Json::Value& data, bool shouldPushTest = true);
/// Interface used by lua. msg is a json encoded string.
CobraConnection::MsgId push(const std::string& id,
const std::string& data,
bool shouldPushTest = true);
CobraConnection::MsgId push(const std::string& id, const std::string& data, bool shouldPushTest = true);
/// Tells whether a metric can be pushed.
/// A metric can be pushed if it satisfies those conditions:
@ -92,16 +89,10 @@ namespace ix
void setGenericAttributes(const std::string& attrName, const Json::Value& value);
/// Set a unique id for the session. A uuid can be used.
void setSession(const std::string& session)
{
_session = session;
}
void setSession(const std::string& session) { _session = session; }
/// Get the unique id used to identify the current session
const std::string& getSession() const
{
return _session;
}
const std::string& getSession() const { return _session; }
/// Return the number of milliseconds since the epoch (~1970)
uint64_t getMillisecondsSinceEpoch() const;

View File

@ -5,77 +5,72 @@
*/
#include "IXCobraMetricsThreadedPublisher.h"
#include <algorithm>
#include <cassert>
#include <cmath>
#include <iostream>
#include <ixcore/utils/IXCoreLogger.h>
#include <ixwebsocket/IXSetThreadName.h>
#include <ixwebsocket/IXSocketTLSOptions.h>
#include <sstream>
#include <ixcore/utils/IXCoreLogger.h>
#include <algorithm>
#include <stdexcept>
#include <cmath>
#include <cassert>
#include <iostream>
#include <sstream>
namespace ix
{
CobraMetricsThreadedPublisher::CobraMetricsThreadedPublisher()
: _stop(false)
CobraMetricsThreadedPublisher::CobraMetricsThreadedPublisher() :
_stop(false)
{
_cobra_connection.setEventCallback([](const CobraEventPtr& event) {
std::stringstream ss;
if (event->type == ix::CobraEventType::Open)
_cobra_connection.setEventCallback(
[]
(ix::CobraConnectionEventType eventType,
const std::string& errMsg,
const ix::WebSocketHttpHeaders& headers,
const std::string& subscriptionId,
CobraConnection::MsgId msgId)
{
ss << "Handshake headers" << std::endl;
std::stringstream ss;
for (auto&& it : event->headers)
if (eventType == ix::CobraConnection_EventType_Open)
{
ss << it.first << ": " << it.second << std::endl;
}
}
else if (event->type == ix::CobraEventType::Authenticated)
{
ss << "Authenticated";
}
else if (event->type == ix::CobraEventType::Error)
{
ss << "Error: " << event->errMsg;
}
else if (event->type == ix::CobraEventType::Closed)
{
ss << "Connection closed: " << event->errMsg;
}
else if (event->type == ix::CobraEventType::Subscribed)
{
ss << "Subscribed through subscription id: " << event->subscriptionId;
}
else if (event->type == ix::CobraEventType::UnSubscribed)
{
ss << "Unsubscribed through subscription id: " << event->subscriptionId;
}
else if (event->type == ix::CobraEventType::Published)
{
ss << "Published message " << event->msgId << " acked";
}
else if (event->type == ix::CobraEventType::Pong)
{
ss << "Received websocket pong";
}
else if (event->type == ix::CobraEventType::HandshakeError)
{
ss << "Handshake error: " << event->errMsg;
}
else if (event->type == ix::CobraEventType::AuthenticationError)
{
ss << "Authentication error: " << event->errMsg;
}
else if (event->type == ix::CobraEventType::SubscriptionError)
{
ss << "Subscription error: " << event->errMsg;
}
ss << "Handshake headers" << std::endl;
CoreLogger::log(ss.str().c_str());
for (auto it : headers)
{
ss << it.first << ": " << it.second << std::endl;
}
}
else if (eventType == ix::CobraConnection_EventType_Authenticated)
{
ss << "Authenticated";
}
else if (eventType == ix::CobraConnection_EventType_Error)
{
ss << "Error: " << errMsg;
}
else if (eventType == ix::CobraConnection_EventType_Closed)
{
ss << "Connection closed: " << errMsg;
}
else if (eventType == ix::CobraConnection_EventType_Subscribed)
{
ss << "Subscribed through subscription id: " << subscriptionId;
}
else if (eventType == ix::CobraConnection_EventType_UnSubscribed)
{
ss << "Unsubscribed through subscription id: " << subscriptionId;
}
else if (eventType == ix::CobraConnection_EventType_Published)
{
ss << "Published message " << msgId << " acked";
}
else if (eventType == ix::CobraConnection_EventType_Pong)
{
ss << "Received websocket pong";
}
ix::IXCoreLogger::Log(ss.str().c_str());
});
}
@ -100,10 +95,11 @@ namespace ix
void CobraMetricsThreadedPublisher::configure(const CobraConfig& config,
const std::string& channel)
{
CoreLogger::log(config.socketTLSOptions.getDescription().c_str());
ix::IXCoreLogger::Log(config.socketTLSOptions.getDescription().c_str());
_channel = channel;
_cobra_connection.configure(config);
}
void CobraMetricsThreadedPublisher::pushMessage(MessageKind messageKind)
@ -161,15 +157,13 @@ namespace ix
{
_cobra_connection.suspend();
continue;
};
break;
}; break;
case MessageKind::Resume:
{
_cobra_connection.resume();
continue;
};
break;
}; break;
case MessageKind::Message:
{
@ -177,8 +171,7 @@ namespace ix
{
_cobra_connection.publishNext();
}
};
break;
}; break;
}
}
}

View File

@ -27,7 +27,8 @@ namespace ix
~CobraMetricsThreadedPublisher();
/// Configuration / set keys, etc...
void configure(const CobraConfig& config, const std::string& channel);
void configure(const CobraConfig& config,
const std::string& channel);
/// Start the worker thread, used for background publishing
void start();

View File

@ -1,44 +1,14 @@
/*
* IXCoreLogger.cpp
* Author: Thomas Wells, Benjamin Sergeant
* Copyright (c) 2019-2020 Machine Zone, Inc. All rights reserved.
*/
#include "ixcore/utils/IXCoreLogger.h"
namespace ix
{
// Default do a no-op logger
CoreLogger::LogFunc CoreLogger::_currentLogger = [](const char*, LogLevel) {};
// Default do nothing logger
IXCoreLogger::LogFunc IXCoreLogger::_currentLogger = [](const char* /*msg*/){};
void CoreLogger::log(const char* msg, LogLevel level)
{
_currentLogger(msg, level);
}
void IXCoreLogger::Log(const char* msg)
{
_currentLogger(msg);
}
void CoreLogger::debug(const std::string& msg)
{
_currentLogger(msg.c_str(), LogLevel::Debug);
}
void CoreLogger::info(const std::string& msg)
{
_currentLogger(msg.c_str(), LogLevel::Info);
}
void CoreLogger::warn(const std::string& msg)
{
_currentLogger(msg.c_str(), LogLevel::Warning);
}
void CoreLogger::error(const std::string& msg)
{
_currentLogger(msg.c_str(), LogLevel::Error);
}
void CoreLogger::critical(const std::string& msg)
{
_currentLogger(msg.c_str(), LogLevel::Critical);
}
} // namespace ix
} // ix

View File

@ -1,41 +1,15 @@
/*
* IXCoreLogger.h
* Author: Thomas Wells, Benjamin Sergeant
* Copyright (c) 2019-2020 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include <functional>
#include <string>
namespace ix
{
enum class LogLevel
{
Debug = 0,
Info = 1,
Warning = 2,
Error = 3,
Critical = 4
};
class CoreLogger
class IXCoreLogger
{
public:
using LogFunc = std::function<void(const char*, LogLevel level)>;
using LogFunc = std::function<void(const char*)>;
static void Log(const char* msg);
static void log(const char* msg, LogLevel level = LogLevel::Debug);
static void debug(const std::string& msg);
static void info(const std::string& msg);
static void warn(const std::string& msg);
static void error(const std::string& msg);
static void critical(const std::string& msg);
static void setLogFunction(LogFunc& func)
{
_currentLogger = func;
}
static void setLogFunction(LogFunc& func) { _currentLogger = func; }
private:
static LogFunc _currentLogger;

View File

@ -29,9 +29,10 @@
namespace ix
{
static const std::string base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
std::string base64_encode(const std::string& data, size_t len)
{
@ -49,26 +50,26 @@ namespace ix
unsigned char char_array_3[3];
unsigned char char_array_4[4];
while (len--)
while(len--)
{
char_array_3[i++] = *(bytes_to_encode++);
if (i == 3)
if(i == 3)
{
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for (i = 0; (i < 4); i++)
for(i = 0; (i <4) ; i++)
ret += base64_chars[char_array_4[i]];
i = 0;
}
}
if (i)
if(i)
{
for (j = i; j < 3; j++)
for(j = i; j < 3; j++)
char_array_3[j] = '\0';
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
@ -76,11 +77,12 @@ namespace ix
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for (j = 0; (j < i + 1); j++)
for(j = 0; (j < i + 1); j++)
ret += base64_chars[char_array_4[j]];
while ((i++ < 3))
while((i++ < 3))
ret += '=';
}
return ret;
@ -93,7 +95,7 @@ namespace ix
std::string base64_decode(const std::string& encoded_string)
{
int in_len = (int) encoded_string.size();
int in_len = (int)encoded_string.size();
int i = 0;
int j = 0;
int in_ = 0;
@ -101,42 +103,40 @@ namespace ix
std::string ret;
ret.reserve(((in_len + 3) / 4) * 3);
while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_]))
while(in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_]))
{
char_array_4[i++] = encoded_string[in_];
in_++;
if (i == 4)
char_array_4[i++] = encoded_string[in_]; in_++;
if(i ==4)
{
for (i = 0; i < 4; i++)
for(i = 0; i <4; i++)
char_array_4[i] = base64_chars.find(char_array_4[i]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (i = 0; (i < 3); i++)
for(i = 0; (i < 3); i++)
ret += char_array_3[i];
i = 0;
}
}
if (i)
if(i)
{
for (j = i; j < 4; j++)
for(j = i; j <4; j++)
char_array_4[j] = 0;
for (j = 0; j < 4; j++)
for(j = 0; j <4; j++)
char_array_4[j] = base64_chars.find(char_array_4[j]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (j = 0; (j < i - 1); j++)
ret += char_array_3[j];
for(j = 0; (j < i - 1); j++) ret += char_array_3[j];
}
return ret;
}
} // namespace ix
}

View File

@ -5,17 +5,16 @@
*/
#include "IXHMac.h"
#include "IXBase64.h"
#if defined(IXCRYPTO_USE_MBED_TLS)
#include <mbedtls/md.h>
# include <mbedtls/md.h>
#elif defined(__APPLE__)
#include <CommonCrypto/CommonHMAC.h>
# include <CommonCrypto/CommonHMAC.h>
#elif defined(IXCRYPTO_USE_OPEN_SSL)
#include <openssl/hmac.h>
# include <openssl/hmac.h>
#else
#include <assert.h>
# include <assert.h>
#endif
namespace ix
@ -27,21 +26,19 @@ namespace ix
#if defined(IXCRYPTO_USE_MBED_TLS)
mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_MD5),
(unsigned char*) key.c_str(),
key.size(),
(unsigned char*) data.c_str(),
data.size(),
(unsigned char*) &hash);
(unsigned char *) key.c_str(), key.size(),
(unsigned char *) data.c_str(), data.size(),
(unsigned char *) &hash);
#elif defined(__APPLE__)
CCHmac(kCCHmacAlgMD5, key.c_str(), key.size(), data.c_str(), data.size(), &hash);
CCHmac(kCCHmacAlgMD5,
key.c_str(), key.size(),
data.c_str(), data.size(),
&hash);
#elif defined(IXCRYPTO_USE_OPEN_SSL)
HMAC(EVP_md5(),
key.c_str(),
(int) key.size(),
(unsigned char*) data.c_str(),
(int) data.size(),
(unsigned char*) hash,
nullptr);
key.c_str(), (int) key.size(),
(unsigned char *) data.c_str(), (int) data.size(),
(unsigned char *) hash, nullptr);
#else
assert(false && "hmac not implemented on this platform");
#endif
@ -50,4 +47,4 @@ namespace ix
return base64_encode(hashString, (uint32_t) hashString.size());
}
} // namespace ix
}

View File

@ -19,4 +19,4 @@ namespace ix
return hashAddress;
}
} // namespace ix
}

View File

@ -16,23 +16,23 @@
#include "IXUuid.h"
#include <iomanip>
#include <random>
#include <sstream>
#include <string>
#include <iomanip>
#include <random>
namespace ix
{
class Uuid
{
public:
Uuid();
std::string toString() const;
public:
Uuid();
std::string toString() const;
private:
uint64_t _ab;
uint64_t _cd;
private:
uint64_t _ab;
uint64_t _cd;
};
Uuid::Uuid()
@ -60,7 +60,7 @@ namespace ix
ss << std::setw(8) << (a);
ss << std::setw(4) << (b >> 16);
ss << std::setw(4) << (b & 0xFFFF);
ss << std::setw(4) << (c >> 16);
ss << std::setw(4) << (c >> 16 );
ss << std::setw(4) << (c & 0xFFFF);
ss << std::setw(8) << d;
@ -72,4 +72,4 @@ namespace ix
Uuid id;
return id.toString();
}
} // namespace ix
}

View File

@ -7,12 +7,12 @@
#include "IXSentryClient.h"
#include <chrono>
#include <fstream>
#include <iostream>
#include <ixcore/utils/IXCoreLogger.h>
#include <fstream>
#include <sstream>
#include <ixwebsocket/IXWebSocketHttpHeaders.h>
#include <ixwebsocket/IXWebSocketVersion.h>
#include <sstream>
#include <ixcore/utils/IXCoreLogger.h>
namespace ix
@ -64,8 +64,7 @@ namespace ix
std::string SentryClient::computeAuthHeader()
{
std::string securityHeader("Sentry sentry_version=5");
securityHeader += ",sentry_client=ws/";
securityHeader += std::string(IX_WEBSOCKET_VERSION);
securityHeader += ",sentry_client=ws/1.0.0";
securityHeader += ",sentry_timestamp=" + std::to_string(SentryClient::getTimestamp());
securityHeader += ",sentry_key=" + _publicKey;
securityHeader += ",sentry_secret=" + _secretKey;
@ -234,7 +233,7 @@ namespace ix
args->transferTimeout = 5 * 60;
args->followRedirects = true;
args->verbose = verbose;
args->logger = [](const std::string& msg) { CoreLogger::log(msg.c_str()); };
args->logger = [](const std::string& msg) { ix::IXCoreLogger::Log(msg.c_str()); };
std::string body = computePayload(msg);
HttpResponsePtr response = _httpClient->post(_url, body, args);
@ -246,21 +245,24 @@ namespace ix
std::string SentryClient::computeUrl(const std::string& project, const std::string& key)
{
std::stringstream ss;
ss << "https://sentry.io/api/" << project << "/minidump?sentry_key=" << key;
ss << "https://sentry.io/api/"
<< project
<< "/minidump?sentry_key="
<< key;
return ss.str();
}
//
// curl -v -X POST -F upload_file_minidump=@ws/crash.dmp
// 'https://sentry.io/api/123456/minidump?sentry_key=12344567890'
// curl -v -X POST -F upload_file_minidump=@ws/crash.dmp 'https://sentry.io/api/123456/minidump?sentry_key=12344567890'
//
void SentryClient::uploadMinidump(const std::string& sentryMetadata,
const std::string& minidumpBytes,
const std::string& project,
const std::string& key,
bool verbose,
const OnResponseCallback& onResponseCallback)
void SentryClient::uploadMinidump(
const std::string& sentryMetadata,
const std::string& minidumpBytes,
const std::string& project,
const std::string& key,
bool verbose,
const OnResponseCallback& onResponseCallback)
{
std::string multipartBoundary = _httpClient->generateMultipartBoundary();
@ -271,7 +273,7 @@ namespace ix
args->followRedirects = true;
args->verbose = verbose;
args->multipartBoundary = multipartBoundary;
args->logger = [](const std::string& msg) { CoreLogger::log(msg.c_str()); };
args->logger = [](const std::string& msg) { ix::IXCoreLogger::Log(msg.c_str()); };
HttpFormDataParameters httpFormDataParameters;
httpFormDataParameters["upload_file_minidump"] = minidumpBytes;
@ -280,27 +282,7 @@ namespace ix
httpParameters["sentry"] = sentryMetadata;
args->url = computeUrl(project, key);
args->body = _httpClient->serializeHttpFormDataParameters(
multipartBoundary, httpFormDataParameters, httpParameters);
_httpClient->performRequest(args, onResponseCallback);
}
void SentryClient::uploadPayload(const Json::Value& payload,
bool verbose,
const OnResponseCallback& onResponseCallback)
{
auto args = _httpClient->createRequest();
args->extraHeaders["X-Sentry-Auth"] = SentryClient::computeAuthHeader();
args->verb = HttpClient::kPost;
args->connectTimeout = 60;
args->transferTimeout = 5 * 60;
args->followRedirects = true;
args->verbose = verbose;
args->logger = [](const std::string& msg) { CoreLogger::log(msg.c_str()); };
args->url = _url;
args->body = _jsonWriter.write(payload);
args->body = _httpClient->serializeHttpFormDataParameters(multipartBoundary, httpFormDataParameters, httpParameters);
_httpClient->performRequest(args, onResponseCallback);
}

View File

@ -10,8 +10,8 @@
#include <ixwebsocket/IXHttpClient.h>
#include <ixwebsocket/IXSocketTLSOptions.h>
#include <json/json.h>
#include <memory>
#include <regex>
#include <memory>
namespace ix
{
@ -28,16 +28,13 @@ namespace ix
// Mostly for testing
void setTLSOptions(const SocketTLSOptions& tlsOptions);
void uploadMinidump(const std::string& sentryMetadata,
const std::string& minidumpBytes,
const std::string& project,
const std::string& key,
bool verbose,
const OnResponseCallback& onResponseCallback);
void uploadPayload(const Json::Value& payload,
bool verbose,
const OnResponseCallback& onResponseCallback);
void uploadMinidump(
const std::string& sentryMetadata,
const std::string& minidumpBytes,
const std::string& project,
const std::string& key,
bool verbose,
const OnResponseCallback& onResponseCallback);
private:
int64_t getTimestamp();

View File

@ -6,10 +6,10 @@
#pragma once
#include <ixwebsocket/IXSocketTLSOptions.h>
#include <nlohmann/json.hpp>
#include <string>
#include <vector>
#include <ixwebsocket/IXSocketTLSOptions.h>
namespace snake
{

View File

@ -28,7 +28,10 @@ namespace ix
return false;
}
CancellationRequest cancellationRequest = []() -> bool { return false; };
CancellationRequest cancellationRequest = []() -> bool
{
return false;
};
std::string errMsg;
return _socket->connect(hostname, port, errMsg, cancellationRequest);
@ -249,8 +252,9 @@ namespace ix
return true;
}
std::string RedisClient::prepareXaddCommand(const std::string& stream,
const std::string& message)
std::string RedisClient::prepareXaddCommand(
const std::string& stream,
const std::string& message)
{
std::stringstream ss;
ss << "*5\r\n";
@ -324,9 +328,7 @@ namespace ix
return streamId;
}
bool RedisClient::sendCommand(const std::string& commands,
int commandsCount,
std::string& errMsg)
bool RedisClient::sendCommand(const std::string& commands, int commandsCount, std::string& errMsg)
{
bool sent = _socket->writeBytes(commands, nullptr);
if (!sent)

View File

@ -8,10 +8,11 @@
#include <atomic>
#include <functional>
#include <ixwebsocket/IXSocket.h>
#include <memory>
#include <string>
#include <ixwebsocket/IXSocket.h>
namespace ix
{
class RedisClient
@ -38,11 +39,14 @@ namespace ix
const OnRedisSubscribeCallback& callback);
// XADD
std::string xadd(const std::string& channel,
const std::string& message,
std::string& errMsg);
std::string xadd(
const std::string& channel,
const std::string& message,
std::string& errMsg);
std::string prepareXaddCommand(const std::string& stream, const std::string& message);
std::string prepareXaddCommand(
const std::string& stream,
const std::string& message);
std::string readXaddReply(std::string& errMsg);

View File

@ -6,18 +6,17 @@
#include "IXRedisServer.h"
#include <fstream>
#include <ixwebsocket/IXCancellationRequest.h>
#include <ixwebsocket/IXNetSystem.h>
#include <ixwebsocket/IXSocket.h>
#include <ixwebsocket/IXSocketConnect.h>
#include <ixwebsocket/IXSocket.h>
#include <ixwebsocket/IXCancellationRequest.h>
#include <fstream>
#include <sstream>
#include <vector>
namespace ix
{
RedisServer::RedisServer(
int port, const std::string& host, int backlog, size_t maxConnections, int addressFamily)
RedisServer::RedisServer(int port, const std::string& host, int backlog, size_t maxConnections, int addressFamily)
: SocketServer(port, host, backlog, maxConnections, addressFamily)
, _connectedClientsCount(0)
, _stopHandlingConnections(false)
@ -115,7 +114,8 @@ namespace ix
for (auto it : _subscribers)
{
std::stringstream ss;
ss << "Subscription id: " << it.first << " #subscribers: " << it.second.size();
ss << "Subscription id: " << it.first
<< " #subscribers: " << it.second.size();
logInfo(ss.str());
}
@ -126,7 +126,8 @@ namespace ix
return _connectedClientsCount;
}
bool RedisServer::startsWith(const std::string& str, const std::string& start)
bool RedisServer::startsWith(const std::string& str,
const std::string& start)
{
return str.compare(0, start.length(), start) == 0;
}
@ -143,8 +144,9 @@ namespace ix
return ss.str();
}
bool RedisServer::parseRequest(std::unique_ptr<Socket>& socket,
std::vector<std::string>& tokens)
bool RedisServer::parseRequest(
std::unique_ptr<Socket>& socket,
std::vector<std::string>& tokens)
{
// Parse first line
auto cb = makeCancellationRequestWithTimeout(30, _stopHandlingConnections);
@ -188,8 +190,9 @@ namespace ix
return true;
}
bool RedisServer::handleCommand(std::unique_ptr<Socket>& socket,
const std::vector<std::string>& tokens)
bool RedisServer::handleCommand(
std::unique_ptr<Socket>& socket,
const std::vector<std::string>& tokens)
{
if (tokens.size() != 1) return false;
@ -204,30 +207,31 @@ namespace ix
//
ss << "*6\r\n";
ss << writeString("publish"); // 1
ss << ":3\r\n"; // 2
ss << "*0\r\n"; // 3
ss << ":1\r\n"; // 4
ss << ":2\r\n"; // 5
ss << ":1\r\n"; // 6
ss << ":3\r\n"; // 2
ss << "*0\r\n"; // 3
ss << ":1\r\n"; // 4
ss << ":2\r\n"; // 5
ss << ":1\r\n"; // 6
//
// subscribe
//
ss << "*6\r\n";
ss << writeString("subscribe"); // 1
ss << ":2\r\n"; // 2
ss << "*0\r\n"; // 3
ss << ":1\r\n"; // 4
ss << ":1\r\n"; // 5
ss << ":1\r\n"; // 6
ss << ":2\r\n"; // 2
ss << "*0\r\n"; // 3
ss << ":1\r\n"; // 4
ss << ":1\r\n"; // 5
ss << ":1\r\n"; // 6
socket->writeBytes(ss.str(), cb);
return true;
}
bool RedisServer::handleSubscribe(std::unique_ptr<Socket>& socket,
const std::vector<std::string>& tokens)
bool RedisServer::handleSubscribe(
std::unique_ptr<Socket>& socket,
const std::vector<std::string>& tokens)
{
if (tokens.size() != 2) return false;
@ -246,8 +250,9 @@ namespace ix
return true;
}
bool RedisServer::handlePublish(std::unique_ptr<Socket>& socket,
const std::vector<std::string>& tokens)
bool RedisServer::handlePublish(
std::unique_ptr<Socket>& socket,
const std::vector<std::string>& tokens)
{
if (tokens.size() != 3) return false;
@ -276,7 +281,9 @@ namespace ix
// return the number of clients that received the message.
std::stringstream ss;
ss << ":" << std::to_string(subscribers.size()) << "\r\n";
ss << ":"
<< std::to_string(subscribers.size())
<< "\r\n";
socket->writeBytes(ss.str(), cb);
return true;

View File

@ -6,13 +6,13 @@
#pragma once
#include "IXSocket.h"
#include "IXSocketServer.h"
#include "IXSocket.h"
#include <functional>
#include <map>
#include <memory>
#include <mutex>
#include <set>
#include <map>
#include <string>
#include <thread>
#include <utility> // pair
@ -50,14 +50,18 @@ namespace ix
bool startsWith(const std::string& str, const std::string& start);
std::string writeString(const std::string& str);
bool parseRequest(std::unique_ptr<Socket>& socket, std::vector<std::string>& tokens);
bool parseRequest(
std::unique_ptr<Socket>& socket,
std::vector<std::string>& tokens);
bool handlePublish(std::unique_ptr<Socket>& socket, const std::vector<std::string>& tokens);
bool handlePublish(std::unique_ptr<Socket>& socket,
const std::vector<std::string>& tokens);
bool handleSubscribe(std::unique_ptr<Socket>& socket,
const std::vector<std::string>& tokens);
bool handleCommand(std::unique_ptr<Socket>& socket, const std::vector<std::string>& tokens);
bool handleCommand(std::unique_ptr<Socket>& socket,
const std::vector<std::string>& tokens);
void cleanupSubscribers(std::unique_ptr<Socket>& socket);
};

View File

@ -10,9 +10,9 @@
#include "IXSnakeConnectionState.h"
#include "nlohmann/json.hpp"
#include <iostream>
#include <ixcore/utils/IXCoreLogger.h>
#include <ixcrypto/IXHMac.h>
#include <ixwebsocket/IXWebSocket.h>
#include <ixcore/utils/IXCoreLogger.h>
#include <sstream>
namespace snake
@ -189,8 +189,7 @@ namespace snake
nlohmann::json response = {
{"action", "rtm/subscription/data"},
{"id", id++},
{"body",
{{"subscription_id", subscriptionId}, {"position", "0-0"}, {"messages", {msg}}}}};
{"body", {{"subscription_id", subscriptionId}, {"position", "0-0"}, {"messages", {msg}}}}};
ws->sendText(response.dump());
};
@ -198,7 +197,7 @@ namespace snake
auto responseCallback = [ws, pdu, &subscriptionId](const std::string& redisResponse) {
std::stringstream ss;
ss << "Redis Response: " << redisResponse << "...";
ix::CoreLogger::log(ss.str().c_str());
ix::IXCoreLogger::Log(ss.str().c_str());
// Success
nlohmann::json response = {{"action", "rtm/subscribe/ok"},
@ -210,7 +209,7 @@ namespace snake
{
std::stringstream ss;
ss << "Subscribing to " << appChannel << "...";
ix::CoreLogger::log(ss.str().c_str());
ix::IXCoreLogger::Log(ss.str().c_str());
}
if (!redisClient.subscribe(appChannel, responseCallback, callback))
@ -252,21 +251,7 @@ namespace snake
const AppConfig& appConfig,
const std::string& str)
{
nlohmann::json pdu;
try
{
pdu = nlohmann::json::parse(str);
}
catch (const nlohmann::json::parse_error& e)
{
std::stringstream ss;
ss << "malformed json pdu: " << e.what() << " -> " << str << "";
nlohmann::json response = {{"body", {{"error", "invalid_json"}, {"reason", ss.str()}}}};
ws->sendText(response.dump());
return;
}
auto pdu = nlohmann::json::parse(str);
auto action = pdu["action"];
if (action == "auth/handshake")

View File

@ -10,8 +10,8 @@
#include "IXSnakeConnectionState.h"
#include "IXSnakeProtocol.h"
#include <iostream>
#include <ixcore/utils/IXCoreLogger.h>
#include <sstream>
#include <ixcore/utils/IXCoreLogger.h>
namespace snake
@ -29,7 +29,7 @@ namespace snake
std::stringstream ss;
ss << "Listening on " << appConfig.hostname << ":" << appConfig.port;
ix::CoreLogger::log(ss.str().c_str());
ix::IXCoreLogger::Log(ss.str().c_str());
}
//
@ -67,7 +67,6 @@ namespace snake
webSocket->setOnMessageCallback(
[this, webSocket, state](const ix::WebSocketMessagePtr& msg) {
std::stringstream ss;
ix::LogLevel logLevel = ix::LogLevel::Debug;
if (msg->type == ix::WebSocketMessageType::Open)
{
ss << "New connection" << std::endl;
@ -87,7 +86,6 @@ namespace snake
_appConfig.redisPort))
{
ss << "Cannot connect to redis host" << std::endl;
logLevel = ix::LogLevel::Error;
}
}
else if (msg->type == ix::WebSocketMessageType::Close)
@ -103,7 +101,6 @@ namespace snake
ss << "#retries: " << msg->errorInfo.retries << std::endl;
ss << "Wait time(ms): " << msg->errorInfo.wait_time << std::endl;
ss << "HTTP Status: " << msg->errorInfo.http_status << std::endl;
logLevel = ix::LogLevel::Error;
}
else if (msg->type == ix::WebSocketMessageType::Fragment)
{
@ -115,7 +112,7 @@ namespace snake
processCobraMessage(state, webSocket, _appConfig, msg->str);
}
ix::CoreLogger::log(ss.str().c_str(), logLevel);
ix::IXCoreLogger::Log(ss.str().c_str());
});
});

View File

@ -4,19 +4,6 @@
* 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 "IXNetSystem.h"

View File

@ -78,12 +78,12 @@ namespace ix
WebSocketHttpHeaders extraHeaders;
std::string body;
std::string multipartBoundary;
int connectTimeout = 60;
int transferTimeout = 1800;
bool followRedirects = true;
int maxRedirects = 5;
bool verbose = false;
bool compress = true;
int connectTimeout;
int transferTimeout;
bool followRedirects;
int maxRedirects;
bool verbose;
bool compress;
Logger logger;
OnProgressCallback onProgressCallback;
};

View File

@ -220,10 +220,11 @@ namespace ix
std::string req(ss.str());
std::string errMsg;
std::atomic<bool> requestInitCancellation(false);
// Make a cancellation object dealing with connection timeout
auto isCancellationRequested =
makeCancellationRequestWithTimeout(args->connectTimeout, _stop);
makeCancellationRequestWithTimeout(args->connectTimeout, requestInitCancellation);
bool success = _socket->connect(host, port, errMsg, isCancellationRequested);
if (!success)
@ -242,7 +243,7 @@ namespace ix
// Make a new cancellation object dealing with transfer timeout
isCancellationRequested =
makeCancellationRequestWithTimeout(args->transferTimeout, _stop);
makeCancellationRequestWithTimeout(args->transferTimeout, requestInitCancellation);
if (args->verbose)
{

View File

@ -0,0 +1,115 @@
/*
* IXSelectInterruptEventFd.cpp
* Author: Benjamin Sergeant
* Copyright (c) 2018-2019 Machine Zone, Inc. All rights reserved.
*/
//
// On Linux we use eventd to wake up select.
//
//
// Linux/Android has a special type of virtual files. select(2) will react
// when reading/writing to those files, unlike closing sockets.
//
// https://linux.die.net/man/2/eventfd
// http://www.sourcexr.com/articles/2013/10/26/lightweight-inter-process-signaling-with-eventfd
//
// eventfd was added in Linux kernel 2.x, and our oldest Android (Kitkat 4.4)
// is on Kernel 3.x
//
// cf Android/Kernel table here
// https://android.stackexchange.com/questions/51651/which-android-runs-which-linux-kernel
//
// On macOS we use UNIX pipes to wake up select.
//
#include "IXSelectInterruptEventFd.h"
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <sstream>
#include <string.h> // for strerror
#include <sys/eventfd.h>
#include <unistd.h> // for write
namespace ix
{
SelectInterruptEventFd::SelectInterruptEventFd()
{
_eventfd = -1;
}
SelectInterruptEventFd::~SelectInterruptEventFd()
{
::close(_eventfd);
}
bool SelectInterruptEventFd::init(std::string& errorMsg)
{
// calling init twice is a programming error
assert(_eventfd == -1);
_eventfd = eventfd(0, 0);
if (_eventfd < 0)
{
std::stringstream ss;
ss << "SelectInterruptEventFd::init() failed in eventfd()"
<< " : " << strerror(errno);
errorMsg = ss.str();
_eventfd = -1;
return false;
}
if (fcntl(_eventfd, F_SETFL, O_NONBLOCK) == -1)
{
std::stringstream ss;
ss << "SelectInterruptEventFd::init() failed in fcntl() call"
<< " : " << strerror(errno);
errorMsg = ss.str();
_eventfd = -1;
return false;
}
return true;
}
bool SelectInterruptEventFd::notify(uint64_t value)
{
int fd = _eventfd;
if (fd == -1) return false;
// we should write 8 bytes for an uint64_t
return write(fd, &value, sizeof(value)) == 8;
}
// TODO: return max uint64_t for errors ?
uint64_t SelectInterruptEventFd::read()
{
int fd = _eventfd;
uint64_t value = 0;
::read(fd, &value, sizeof(value));
return value;
}
bool SelectInterruptEventFd::clear()
{
if (_eventfd == -1) return false;
// 0 is a special value ; select will not wake up
uint64_t value = 0;
// we should write 8 bytes for an uint64_t
return write(_eventfd, &value, sizeof(value)) == 8;
}
int SelectInterruptEventFd::getFd() const
{
return _eventfd;
}
} // namespace ix

View File

@ -0,0 +1,31 @@
/*
* IXSelectInterruptEventFd.h
* Author: Benjamin Sergeant
* Copyright (c) 2018-2019 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include "IXSelectInterrupt.h"
#include <stdint.h>
#include <string>
namespace ix
{
class SelectInterruptEventFd final : public SelectInterrupt
{
public:
SelectInterruptEventFd();
virtual ~SelectInterruptEventFd();
bool init(std::string& errorMsg) final;
bool notify(uint64_t value) final;
bool clear() final;
uint64_t read() final;
int getFd() const final;
private:
int _eventfd;
};
} // namespace ix

View File

@ -376,8 +376,7 @@ namespace ix
{
if (isCancellationRequested && isCancellationRequested())
{
const std::string errorMsg("Cancellation Requested");
return std::make_pair(false, errorMsg);
return std::make_pair(false, std::string());
}
size_t size = std::min(kChunkSize, length - output.size());
@ -389,8 +388,7 @@ namespace ix
}
else if (ret <= 0 && !Socket::isWaitNeeded())
{
const std::string errorMsg("Recv Error");
return std::make_pair(false, errorMsg);
return std::make_pair(false, std::string());
}
if (onProgressCallback) onProgressCallback((int) output.size(), (int) length);
@ -399,8 +397,7 @@ namespace ix
// This way we are not busy looping
if (isReadyToRead(1) == PollResultType::Error)
{
const std::string errorMsg("Poll Error");
return std::make_pair(false, errorMsg);
return std::make_pair(false, std::string());
}
}

View File

@ -1,12 +1,10 @@
/*
* IXSocketAppleSSL.cpp
* Author: Benjamin Sergeant
* Copyright (c) 2017-2020 Machine Zone, Inc. All rights reserved.
* Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved.
*
* Adapted from Satori SDK Apple SSL code.
*/
#ifdef IXWEBSOCKET_USE_SECURE_TRANSPORT
#include "IXSocketAppleSSL.h"
#include "IXSocketConnect.h"
@ -309,5 +307,3 @@ namespace ix
}
} // namespace ix
#endif // IXWEBSOCKET_USE_SECURE_TRANSPORT

View File

@ -1,9 +1,8 @@
/*
* IXSocketAppleSSL.h
* Author: Benjamin Sergeant
* Copyright (c) 2017-2020 Machine Zone, Inc. All rights reserved.
* Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved.
*/
#ifdef IXWEBSOCKET_USE_SECURE_TRANSPORT
#pragma once
@ -48,5 +47,3 @@ namespace ix
};
} // namespace ix
#endif // IXWEBSOCKET_USE_SECURE_TRANSPORT

View File

@ -1,13 +1,12 @@
/*
* IXSocketMbedTLS.cpp
* Author: Benjamin Sergeant, Max Weisel
* Copyright (c) 2019-2020 Machine Zone, Inc. All rights reserved.
* Author: Benjamin Sergeant
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
*
* Some code taken from
* https://github.com/rottor12/WsClientLib/blob/master/lib/src/WsClientLib.cpp
* and mini_client.c example from mbedtls
*/
#ifdef IXWEBSOCKET_USE_MBED_TLS
#include "IXSocketMbedTLS.h"
@ -104,26 +103,10 @@ namespace ix
{
; // FIXME
}
else
else if (mbedtls_x509_crt_parse_file(&_cacert, _tlsOptions.caFile.c_str()) < 0)
{
if (_tlsOptions.isUsingInMemoryCAs())
{
const char* buffer = _tlsOptions.caFile.c_str();
size_t bufferSize =
_tlsOptions.caFile.size() + 1; // Needs to include null terminating
// character otherwise mbedtls will fail.
if (mbedtls_x509_crt_parse(
&_cacert, (const unsigned char*) buffer, bufferSize) < 0)
{
errMsg = "Cannot parse CA from memory.";
return false;
}
}
else if (mbedtls_x509_crt_parse_file(&_cacert, _tlsOptions.caFile.c_str()) < 0)
{
errMsg = "Cannot parse CA file '" + _tlsOptions.caFile + "'";
return false;
}
errMsg = "Cannot parse CA file '" + _tlsOptions.caFile + "'";
return false;
}
mbedtls_ssl_conf_ca_chain(&_conf, &_cacert, NULL);
@ -297,5 +280,3 @@ namespace ix
}
} // namespace ix
#endif // IXWEBSOCKET_USE_MBED_TLS

View File

@ -1,9 +1,8 @@
/*
* IXSocketMbedTLS.h
* Author: Benjamin Sergeant
* Copyright (c) 2019-2020 Machine Zone, Inc. All rights reserved.
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
*/
#ifdef IXWEBSOCKET_USE_MBED_TLS
#pragma once
@ -55,5 +54,3 @@ namespace ix
};
} // namespace ix
#endif // IXWEBSOCKET_USE_MBED_TLS

View File

@ -1,11 +1,10 @@
/*
* IXSocketOpenSSL.cpp
* Author: Benjamin Sergeant, Matt DeBoer, Max Weisel
* Copyright (c) 2017-2020 Machine Zone, Inc. All rights reserved.
* Author: Benjamin Sergeant, Matt DeBoer
* Copyright (c) 2017-2019 Machine Zone, Inc. All rights reserved.
*
* Adapted from Satori SDK OpenSSL code.
*/
#ifdef IXWEBSOCKET_USE_OPEN_SSL
#include "IXSocketOpenSSL.h"
@ -195,66 +194,6 @@ namespace ix
return ctx;
}
bool SocketOpenSSL::openSSLAddCARootsFromString(const std::string roots)
{
// Create certificate store
X509_STORE* certificate_store = SSL_CTX_get_cert_store(_ssl_context);
if (certificate_store == nullptr) return false;
// Configure to allow intermediate certs
X509_STORE_set_flags(certificate_store,
X509_V_FLAG_TRUSTED_FIRST | X509_V_FLAG_PARTIAL_CHAIN);
// Create a new buffer and populate it with the roots
BIO* buffer = BIO_new_mem_buf((void*) roots.c_str(), static_cast<int>(roots.length()));
if (buffer == nullptr) return false;
// Read each root in the buffer and add to the certificate store
bool success = true;
size_t number_of_roots = 0;
while (true)
{
// Read the next root in the buffer
X509* root = PEM_read_bio_X509_AUX(buffer, nullptr, nullptr, (void*) "");
if (root == nullptr)
{
// No more certs left in the buffer, we're done.
ERR_clear_error();
break;
}
// Try adding the root to the certificate store
ERR_clear_error();
if (!X509_STORE_add_cert(certificate_store, root))
{
// Failed to add. If the error is unrelated to the x509 lib or the cert already
// exists, we're safe to continue.
unsigned long error = ERR_get_error();
if (ERR_GET_LIB(error) != ERR_LIB_X509 ||
ERR_GET_REASON(error) != X509_R_CERT_ALREADY_IN_HASH_TABLE)
{
// Failed. Clean up and bail.
success = false;
X509_free(root);
break;
}
}
// Clean up and loop
X509_free(root);
number_of_roots++;
}
// Clean up buffer
BIO_free(buffer);
// Make sure we loaded at least one certificate.
if (number_of_roots == 0) success = false;
return success;
}
/**
* Check whether a hostname matches a pattern
*/
@ -463,32 +402,20 @@ namespace ix
}
#endif
}
else
else if (SSL_CTX_load_verify_locations(
_ssl_context, _tlsOptions.caFile.c_str(), NULL) != 1)
{
if (_tlsOptions.isUsingInMemoryCAs())
{
// Load from memory
openSSLAddCARootsFromString(_tlsOptions.caFile);
}
else
{
if (SSL_CTX_load_verify_locations(
_ssl_context, _tlsOptions.caFile.c_str(), NULL) != 1)
{
auto sslErr = ERR_get_error();
errMsg = "OpenSSL failed - SSL_CTX_load_verify_locations(\"" +
_tlsOptions.caFile + "\") failed: ";
errMsg += ERR_error_string(sslErr, nullptr);
return false;
}
SSL_CTX_set_verify(
_ssl_context, SSL_VERIFY_PEER, [](int preverify, X509_STORE_CTX*) -> int {
return preverify;
});
SSL_CTX_set_verify_depth(_ssl_context, 4);
}
auto sslErr = ERR_get_error();
errMsg = "OpenSSL failed - SSL_CTX_load_verify_locations(\"" + _tlsOptions.caFile +
"\") failed: ";
errMsg += ERR_error_string(sslErr, nullptr);
return false;
}
SSL_CTX_set_verify(_ssl_context,
SSL_VERIFY_PEER,
[](int preverify, X509_STORE_CTX*) -> int { return preverify; });
SSL_CTX_set_verify_depth(_ssl_context, 4);
}
else
{
@ -598,35 +525,26 @@ namespace ix
}
else
{
if (_tlsOptions.isUsingInMemoryCAs())
const char* root_ca_file = _tlsOptions.caFile.c_str();
STACK_OF(X509_NAME) * rootCAs;
rootCAs = SSL_load_client_CA_file(root_ca_file);
if (rootCAs == NULL)
{
// Load from memory
openSSLAddCARootsFromString(_tlsOptions.caFile);
auto sslErr = ERR_get_error();
errMsg = "OpenSSL failed - SSL_load_client_CA_file('" + _tlsOptions.caFile +
"') failed: ";
errMsg += ERR_error_string(sslErr, nullptr);
}
else
{
const char* root_ca_file = _tlsOptions.caFile.c_str();
STACK_OF(X509_NAME) * rootCAs;
rootCAs = SSL_load_client_CA_file(root_ca_file);
if (rootCAs == NULL)
SSL_CTX_set_client_CA_list(_ssl_context, rootCAs);
if (SSL_CTX_load_verify_locations(_ssl_context, root_ca_file, nullptr) != 1)
{
auto sslErr = ERR_get_error();
errMsg = "OpenSSL failed - SSL_load_client_CA_file('" +
_tlsOptions.caFile + "') failed: ";
errMsg = "OpenSSL failed - SSL_CTX_load_verify_locations(\"" +
_tlsOptions.caFile + "\") failed: ";
errMsg += ERR_error_string(sslErr, nullptr);
}
else
{
SSL_CTX_set_client_CA_list(_ssl_context, rootCAs);
if (SSL_CTX_load_verify_locations(
_ssl_context, root_ca_file, nullptr) != 1)
{
auto sslErr = ERR_get_error();
errMsg = "OpenSSL failed - SSL_CTX_load_verify_locations(\"" +
_tlsOptions.caFile + "\") failed: ";
errMsg += ERR_error_string(sslErr, nullptr);
}
}
}
}
@ -813,5 +731,3 @@ namespace ix
}
} // namespace ix
#endif // IXWEBSOCKET_USE_OPEN_SSL

View File

@ -1,9 +1,8 @@
/*
* IXSocketOpenSSL.h
* Author: Benjamin Sergeant, Matt DeBoer
* Copyright (c) 2017-2020 Machine Zone, Inc. All rights reserved.
* Copyright (c) 2017-2019 Machine Zone, Inc. All rights reserved.
*/
#ifdef IXWEBSOCKET_USE_OPEN_SSL
#pragma once
@ -40,7 +39,6 @@ namespace ix
void openSSLInitialize();
std::string getSSLError(int ret);
SSL_CTX* openSSLCreateContext(std::string& errMsg);
bool openSSLAddCARootsFromString(const std::string roots);
bool openSSLClientHandshake(const std::string& hostname,
std::string& errMsg,
const CancellationRequest& isCancellationRequested);
@ -61,5 +59,3 @@ namespace ix
};
} // namespace ix
#endif // IXWEBSOCKET_USE_OPEN_SSL

View File

@ -15,7 +15,6 @@ namespace ix
const char* kTLSCAFileUseSystemDefaults = "SYSTEM";
const char* kTLSCAFileDisableVerify = "NONE";
const char* kTLSCiphersUseDefault = "DEFAULT";
const char* kTLSInMemoryMarker = "-----BEGIN CERTIFICATE-----";
bool SocketTLSOptions::isValid() const
{
@ -59,11 +58,6 @@ namespace ix
return caFile == kTLSCAFileUseSystemDefaults;
}
bool SocketTLSOptions::isUsingInMemoryCAs() const
{
return caFile.find(kTLSInMemoryMarker) != std::string::npos;
}
bool SocketTLSOptions::isPeerVerifyDisabled() const
{
return caFile == kTLSCAFileDisableVerify;

View File

@ -37,8 +37,6 @@ namespace ix
bool isUsingSystemDefaults() const;
bool isUsingInMemoryCAs() const;
bool isPeerVerifyDisabled() const;
bool isUsingDefaultCiphers() const;

View File

@ -1,29 +1,4 @@
/*
* Lightweight URL & URI parser (RFC 1738, RFC 3986)
* https://github.com/corporateshark/LUrlParser
*
* The MIT License (MIT)
*
* Copyright (C) 2015 Sergey Kosarevsky (sk@linderdaum.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* IXUrlParser.cpp
* Author: Benjamin Sergeant
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
@ -31,308 +6,7 @@
#include "IXUrlParser.h"
#include <algorithm>
#include <cstring>
namespace
{
enum LUrlParserError
{
LUrlParserError_Ok = 0,
LUrlParserError_Uninitialized = 1,
LUrlParserError_NoUrlCharacter = 2,
LUrlParserError_InvalidSchemeName = 3,
LUrlParserError_NoDoubleSlash = 4,
LUrlParserError_NoAtSign = 5,
LUrlParserError_UnexpectedEndOfLine = 6,
LUrlParserError_NoSlash = 7,
};
class clParseURL
{
public:
LUrlParserError m_ErrorCode;
std::string m_Scheme;
std::string m_Host;
std::string m_Port;
std::string m_Path;
std::string m_Query;
std::string m_Fragment;
std::string m_UserName;
std::string m_Password;
clParseURL()
: m_ErrorCode(LUrlParserError_Uninitialized)
{
}
/// return 'true' if the parsing was successful
bool IsValid() const
{
return m_ErrorCode == LUrlParserError_Ok;
}
/// helper to convert the port number to int, return 'true' if the port is valid (within the
/// 0..65535 range)
bool GetPort(int* OutPort) const;
/// parse the URL
static clParseURL ParseURL(const std::string& URL);
private:
explicit clParseURL(LUrlParserError ErrorCode)
: m_ErrorCode(ErrorCode)
{
}
};
static bool IsSchemeValid(const std::string& SchemeName)
{
for (auto c : SchemeName)
{
if (!isalpha(c) && c != '+' && c != '-' && c != '.') return false;
}
return true;
}
bool clParseURL::GetPort(int* OutPort) const
{
if (!IsValid())
{
return false;
}
int Port = atoi(m_Port.c_str());
if (Port <= 0 || Port > 65535)
{
return false;
}
if (OutPort)
{
*OutPort = Port;
}
return true;
}
// based on RFC 1738 and RFC 3986
clParseURL clParseURL::ParseURL(const std::string& URL)
{
clParseURL Result;
const char* CurrentString = URL.c_str();
/*
* <scheme>:<scheme-specific-part>
* <scheme> := [a-z\+\-\.]+
* For resiliency, programs interpreting URLs should treat upper case letters as
*equivalent to lower case in scheme names
*/
// try to read scheme
{
const char* LocalString = strchr(CurrentString, ':');
if (!LocalString)
{
return clParseURL(LUrlParserError_NoUrlCharacter);
}
// save the scheme name
Result.m_Scheme = std::string(CurrentString, LocalString - CurrentString);
if (!IsSchemeValid(Result.m_Scheme))
{
return clParseURL(LUrlParserError_InvalidSchemeName);
}
// scheme should be lowercase
std::transform(
Result.m_Scheme.begin(), Result.m_Scheme.end(), Result.m_Scheme.begin(), ::tolower);
// skip ':'
CurrentString = LocalString + 1;
}
/*
* //<user>:<password>@<host>:<port>/<url-path>
* any ":", "@" and "/" must be normalized
*/
// skip "//"
if (*CurrentString++ != '/') return clParseURL(LUrlParserError_NoDoubleSlash);
if (*CurrentString++ != '/') return clParseURL(LUrlParserError_NoDoubleSlash);
// check if the user name and password are specified
bool bHasUserName = false;
const char* LocalString = CurrentString;
while (*LocalString)
{
if (*LocalString == '@')
{
// user name and password are specified
bHasUserName = true;
break;
}
else if (*LocalString == '/')
{
// end of <host>:<port> specification
bHasUserName = false;
break;
}
LocalString++;
}
// user name and password
LocalString = CurrentString;
if (bHasUserName)
{
// read user name
while (*LocalString && *LocalString != ':' && *LocalString != '@')
LocalString++;
Result.m_UserName = std::string(CurrentString, LocalString - CurrentString);
// proceed with the current pointer
CurrentString = LocalString;
if (*CurrentString == ':')
{
// skip ':'
CurrentString++;
// read password
LocalString = CurrentString;
while (*LocalString && *LocalString != '@')
LocalString++;
Result.m_Password = std::string(CurrentString, LocalString - CurrentString);
CurrentString = LocalString;
}
// skip '@'
if (*CurrentString != '@')
{
return clParseURL(LUrlParserError_NoAtSign);
}
CurrentString++;
}
bool bHasBracket = (*CurrentString == '[');
// go ahead, read the host name
LocalString = CurrentString;
while (*LocalString)
{
if (bHasBracket && *LocalString == ']')
{
// end of IPv6 address
LocalString++;
break;
}
else if (!bHasBracket && (*LocalString == ':' || *LocalString == '/'))
{
// port number is specified
break;
}
LocalString++;
}
Result.m_Host = std::string(CurrentString, LocalString - CurrentString);
CurrentString = LocalString;
// is port number specified?
if (*CurrentString == ':')
{
CurrentString++;
// read port number
LocalString = CurrentString;
while (*LocalString && *LocalString != '/')
LocalString++;
Result.m_Port = std::string(CurrentString, LocalString - CurrentString);
CurrentString = LocalString;
}
// end of string
if (!*CurrentString)
{
Result.m_ErrorCode = LUrlParserError_Ok;
return Result;
}
// skip '/'
if (*CurrentString != '/')
{
return clParseURL(LUrlParserError_NoSlash);
}
CurrentString++;
// parse the path
LocalString = CurrentString;
while (*LocalString && *LocalString != '#' && *LocalString != '?')
LocalString++;
Result.m_Path = std::string(CurrentString, LocalString - CurrentString);
CurrentString = LocalString;
// check for query
if (*CurrentString == '?')
{
// skip '?'
CurrentString++;
// read query
LocalString = CurrentString;
while (*LocalString && *LocalString != '#')
LocalString++;
Result.m_Query = std::string(CurrentString, LocalString - CurrentString);
CurrentString = LocalString;
}
// check for fragment
if (*CurrentString == '#')
{
// skip '#'
CurrentString++;
// read fragment
LocalString = CurrentString;
while (*LocalString)
LocalString++;
Result.m_Fragment = std::string(CurrentString, LocalString - CurrentString);
}
Result.m_ErrorCode = LUrlParserError_Ok;
return Result;
}
} // namespace
#include "LUrlParser.h"
namespace ix
{
@ -343,7 +17,7 @@ namespace ix
std::string& query,
int& port)
{
clParseURL res = clParseURL::ParseURL(url);
LUrlParser::clParseURL res = LUrlParser::clParseURL::ParseURL(url);
if (!res.IsValid())
{

View File

@ -34,7 +34,7 @@ namespace ix
_ws.setOnCloseCallback(
[this](uint16_t code, const std::string& reason, size_t wireSize, bool remote) {
_onMessageCallback(
std::make_unique<WebSocketMessage>(WebSocketMessageType::Close,
std::make_shared<WebSocketMessage>(WebSocketMessageType::Close,
"",
wireSize,
WebSocketErrorInfo(),
@ -193,7 +193,7 @@ namespace ix
return status;
}
_onMessageCallback(std::make_unique<WebSocketMessage>(
_onMessageCallback(std::make_shared<WebSocketMessage>(
WebSocketMessageType::Open,
"",
0,
@ -225,7 +225,7 @@ namespace ix
}
_onMessageCallback(
std::make_unique<WebSocketMessage>(WebSocketMessageType::Open,
std::make_shared<WebSocketMessage>(WebSocketMessageType::Open,
"",
0,
WebSocketErrorInfo(),
@ -310,7 +310,7 @@ namespace ix
connectErr.reason = status.errorStr;
connectErr.http_status = status.http_status;
_onMessageCallback(std::make_unique<WebSocketMessage>(WebSocketMessageType::Error,
_onMessageCallback(std::make_shared<WebSocketMessage>(WebSocketMessageType::Error,
"",
0,
connectErr,
@ -386,7 +386,7 @@ namespace ix
bool binary = messageKind == WebSocketTransport::MessageKind::MSG_BINARY;
_onMessageCallback(std::make_unique<WebSocketMessage>(webSocketMessageType,
_onMessageCallback(std::make_shared<WebSocketMessage>(webSocketMessageType,
msg,
wireSize,
webSocketErrorInfo,

View File

@ -6,9 +6,6 @@
#pragma once
#include <cstdint>
#include <string>
namespace ix
{
struct WebSocketCloseInfo

View File

@ -6,7 +6,6 @@
#pragma once
#include <cstdint>
#include <string>
namespace ix

View File

@ -10,7 +10,7 @@
#include "IXSocketConnect.h"
#include "IXUrlParser.h"
#include "IXUserAgent.h"
#include "IXWebSocketHandshakeKeyGen.h"
#include "libwshandshake.hpp"
#include <algorithm>
#include <iostream>
#include <random>
@ -133,7 +133,7 @@ namespace ix
for (auto& it : extraHeaders)
{
ss << it.first << ": " << it.second << "\r\n";
ss << it.first << ":" << it.second << "\r\n";
}
if (_enablePerMessageDeflate)

View File

@ -1,171 +0,0 @@
// Copyright (c) 2016 Alex Hultman and contributors
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgement in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
#pragma once
#include <cstddef>
#include <cstdint>
#include <string.h>
#include <string>
class WebSocketHandshakeKeyGen
{
template<int N, typename T>
struct static_for
{
void operator()(uint32_t* a, uint32_t* b)
{
static_for<N - 1, T>()(a, b);
T::template f<N - 1>(a, b);
}
};
template<typename T>
struct static_for<0, T>
{
void operator()(uint32_t* /*a*/, uint32_t* /*hash*/)
{
}
};
template<int state>
struct Sha1Loop
{
static inline uint32_t rol(uint32_t value, size_t bits)
{
return (value << bits) | (value >> (32 - bits));
}
static inline uint32_t blk(uint32_t b[16], size_t i)
{
return rol(b[(i + 13) & 15] ^ b[(i + 8) & 15] ^ b[(i + 2) & 15] ^ b[i], 1);
}
template<int i>
static inline void f(uint32_t* a, uint32_t* b)
{
switch (state)
{
case 1:
a[i % 5] +=
((a[(3 + i) % 5] & (a[(2 + i) % 5] ^ a[(1 + i) % 5])) ^ a[(1 + i) % 5]) +
b[i] + 0x5a827999 + rol(a[(4 + i) % 5], 5);
a[(3 + i) % 5] = rol(a[(3 + i) % 5], 30);
break;
case 2:
b[i] = blk(b, i);
a[(1 + i) % 5] +=
((a[(4 + i) % 5] & (a[(3 + i) % 5] ^ a[(2 + i) % 5])) ^ a[(2 + i) % 5]) +
b[i] + 0x5a827999 + rol(a[(5 + i) % 5], 5);
a[(4 + i) % 5] = rol(a[(4 + i) % 5], 30);
break;
case 3:
b[(i + 4) % 16] = blk(b, (i + 4) % 16);
a[i % 5] += (a[(3 + i) % 5] ^ a[(2 + i) % 5] ^ a[(1 + i) % 5]) +
b[(i + 4) % 16] + 0x6ed9eba1 + rol(a[(4 + i) % 5], 5);
a[(3 + i) % 5] = rol(a[(3 + i) % 5], 30);
break;
case 4:
b[(i + 8) % 16] = blk(b, (i + 8) % 16);
a[i % 5] += (((a[(3 + i) % 5] | a[(2 + i) % 5]) & a[(1 + i) % 5]) |
(a[(3 + i) % 5] & a[(2 + i) % 5])) +
b[(i + 8) % 16] + 0x8f1bbcdc + rol(a[(4 + i) % 5], 5);
a[(3 + i) % 5] = rol(a[(3 + i) % 5], 30);
break;
case 5:
b[(i + 12) % 16] = blk(b, (i + 12) % 16);
a[i % 5] += (a[(3 + i) % 5] ^ a[(2 + i) % 5] ^ a[(1 + i) % 5]) +
b[(i + 12) % 16] + 0xca62c1d6 + rol(a[(4 + i) % 5], 5);
a[(3 + i) % 5] = rol(a[(3 + i) % 5], 30);
break;
case 6: b[i] += a[4 - i];
}
}
};
static inline void sha1(uint32_t hash[5], uint32_t b[16])
{
uint32_t a[5] = {hash[4], hash[3], hash[2], hash[1], hash[0]};
static_for<16, Sha1Loop<1>>()(a, b);
static_for<4, Sha1Loop<2>>()(a, b);
static_for<20, Sha1Loop<3>>()(a, b);
static_for<20, Sha1Loop<4>>()(a, b);
static_for<20, Sha1Loop<5>>()(a, b);
static_for<5, Sha1Loop<6>>()(a, hash);
}
static inline void base64(unsigned char* src, char* dst)
{
const char* b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
for (int i = 0; i < 18; i += 3)
{
*dst++ = b64[(src[i] >> 2) & 63];
*dst++ = b64[((src[i] & 3) << 4) | ((src[i + 1] & 240) >> 4)];
*dst++ = b64[((src[i + 1] & 15) << 2) | ((src[i + 2] & 192) >> 6)];
*dst++ = b64[src[i + 2] & 63];
}
*dst++ = b64[(src[18] >> 2) & 63];
*dst++ = b64[((src[18] & 3) << 4) | ((src[19] & 240) >> 4)];
*dst++ = b64[((src[19] & 15) << 2)];
*dst++ = '=';
}
public:
static inline void generate(const std::string& inputStr, char output[28])
{
char input[25] = {};
strncpy(input, inputStr.c_str(), 25 - 1);
input[25 - 1] = '\0';
uint32_t b_output[5] = {0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0};
uint32_t b_input[16] = {0,
0,
0,
0,
0,
0,
0x32353845,
0x41464135,
0x2d453931,
0x342d3437,
0x44412d39,
0x3543412d,
0x43354142,
0x30444338,
0x35423131,
0x80000000};
for (int i = 0; i < 6; i++)
{
b_input[i] = (input[4 * i + 3] & 0xff) | (input[4 * i + 2] & 0xff) << 8 |
(input[4 * i + 1] & 0xff) << 16 | (input[4 * i + 0] & 0xff) << 24;
}
sha1(b_output, b_input);
uint32_t last_b[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 480};
sha1(b_output, last_b);
for (int i = 0; i < 5; i++)
{
uint32_t tmp = b_output[i];
char* bytes = (char*) &b_output[i];
bytes[3] = tmp & 0xff;
bytes[2] = (tmp >> 8) & 0xff;
bytes[1] = (tmp >> 16) & 0xff;
bytes[0] = (tmp >> 24) & 0xff;
}
base64((unsigned char*) b_output, output);
}
};

View File

@ -19,7 +19,7 @@ namespace ix
struct WebSocketMessage
{
WebSocketMessageType type;
const std::string& str;
std::string str;
size_t wireSize;
WebSocketErrorInfo errorInfo;
WebSocketOpenInfo openInfo;
@ -34,7 +34,7 @@ namespace ix
WebSocketCloseInfo c,
bool b = false)
: type(t)
, str(s)
, str(std::move(s))
, wireSize(w)
, errorInfo(e)
, openInfo(o)
@ -45,5 +45,5 @@ namespace ix
}
};
using WebSocketMessagePtr = std::unique_ptr<WebSocketMessage>;
using WebSocketMessagePtr = std::shared_ptr<WebSocketMessage>;
} // namespace ix

View File

@ -0,0 +1,86 @@
/*
* IXWebSocketMessageQueue.cpp
* Author: Korchynskyi Dmytro
* Copyright (c) 2017-2019 Machine Zone, Inc. All rights reserved.
*/
#include "IXWebSocketMessageQueue.h"
namespace ix
{
WebSocketMessageQueue::WebSocketMessageQueue(WebSocket* websocket)
{
bindWebsocket(websocket);
}
WebSocketMessageQueue::~WebSocketMessageQueue()
{
if (!_messages.empty())
{
// not handled all messages
}
bindWebsocket(nullptr);
}
void WebSocketMessageQueue::bindWebsocket(WebSocket* websocket)
{
if (_websocket == websocket) return;
// unbind old
if (_websocket)
{
// set dummy callback just to avoid crash
_websocket->setOnMessageCallback([](const WebSocketMessagePtr&) {});
}
_websocket = websocket;
// bind new
if (_websocket)
{
_websocket->setOnMessageCallback([this](const WebSocketMessagePtr& msg) {
std::lock_guard<std::mutex> lock(_messagesMutex);
_messages.emplace_back(std::move(msg));
});
}
}
void WebSocketMessageQueue::setOnMessageCallback(const OnMessageCallback& callback)
{
_onMessageUserCallback = callback;
}
void WebSocketMessageQueue::setOnMessageCallback(OnMessageCallback&& callback)
{
_onMessageUserCallback = std::move(callback);
}
WebSocketMessagePtr WebSocketMessageQueue::popMessage()
{
WebSocketMessagePtr message;
std::lock_guard<std::mutex> lock(_messagesMutex);
if (!_messages.empty())
{
message = std::move(_messages.front());
_messages.pop_front();
}
return message;
}
void WebSocketMessageQueue::poll(int count)
{
if (!_onMessageUserCallback) return;
WebSocketMessagePtr message;
while (count > 0 && (message = popMessage()))
{
_onMessageUserCallback(message);
--count;
}
}
} // namespace ix

View File

@ -0,0 +1,41 @@
/*
* IXWebSocketMessageQueue.h
* Author: Korchynskyi Dmytro
* Copyright (c) 2017-2019 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include "IXWebSocket.h"
#include <list>
#include <memory>
#include <thread>
namespace ix
{
//
// A helper class to dispatch websocket message callbacks in your thread.
//
class WebSocketMessageQueue
{
public:
WebSocketMessageQueue(WebSocket* websocket = nullptr);
~WebSocketMessageQueue();
void bindWebsocket(WebSocket* websocket);
void setOnMessageCallback(const OnMessageCallback& callback);
void setOnMessageCallback(OnMessageCallback&& callback);
void poll(int count = 512);
protected:
WebSocketMessagePtr popMessage();
private:
WebSocket* _websocket = nullptr;
OnMessageCallback _onMessageUserCallback;
std::mutex _messagesMutex;
std::list<WebSocketMessagePtr> _messages;
};
} // namespace ix

View File

@ -6,10 +6,6 @@
#pragma once
#include "IXWebSocketHttpHeaders.h"
#include <cstdint>
#include <string>
namespace ix
{
struct WebSocketOpenInfo

View File

@ -87,9 +87,6 @@ namespace ix
//
size_t output;
// Clear output
out.clear();
if (in.empty())
{
// See issue #167
@ -177,9 +174,6 @@ namespace ix
_inflateState.avail_in = (uInt) inFixed.size();
_inflateState.next_in = (unsigned char*) (const_cast<char*>(inFixed.data()));
// Clear output
out.clear();
do
{
_inflateState.avail_out = (uInt) _compressBufferSize;

View File

@ -62,7 +62,7 @@ namespace ix
WebSocketTransport::WebSocketTransport()
: _useMask(true)
, _blockingSend(false)
, _receivedMessageCompressed(false)
, _compressedMessage(false)
, _readyState(ReadyState::CLOSED)
, _closeCode(WebSocketCloseConstants::kInternalErrorCode)
, _closeReason(WebSocketCloseConstants::kInternalErrorMessage)
@ -74,7 +74,6 @@ namespace ix
, _enablePong(kDefaultEnablePong)
, _pingIntervalSecs(kDefaultPingIntervalSecs)
, _pongReceived(false)
, _pingCount(0)
, _lastSendPingTimePoint(std::chrono::steady_clock::now())
{
_readbuf.resize(kChunkSize);
@ -222,8 +221,7 @@ namespace ix
{
_pongReceived = false;
std::stringstream ss;
ss << kPingMessage << "::" << _pingIntervalSecs << "s"
<< "::" << _pingCount++;
ss << kPingMessage << "::" << _pingIntervalSecs << "s";
return sendPing(ss.str());
}
@ -495,7 +493,7 @@ namespace ix
? MessageKind::MSG_TEXT
: MessageKind::MSG_BINARY;
_receivedMessageCompressed = _enablePerMessageDeflate && ws.rsv1;
_compressedMessage = _enablePerMessageDeflate && ws.rsv1;
// Continuation message needs to follow a non-fin TEXT or BINARY message
if (!_chunks.empty())
@ -517,12 +515,10 @@ namespace ix
//
if (ws.fin && _chunks.empty())
{
emitMessage(_fragmentedMessageKind,
frameData,
_receivedMessageCompressed,
onMessageCallback);
emitMessage(
_fragmentedMessageKind, frameData, _compressedMessage, onMessageCallback);
_receivedMessageCompressed = false;
_compressedMessage = false;
}
else
{
@ -539,11 +535,11 @@ namespace ix
{
emitMessage(_fragmentedMessageKind,
getMergedChunks(),
_receivedMessageCompressed,
_compressedMessage,
onMessageCallback);
_chunks.clear();
_receivedMessageCompressed = false;
_compressedMessage = false;
}
else
{
@ -716,16 +712,17 @@ namespace ix
// When the RSV1 bit is 1 it means the message is compressed
if (compressedMessage && messageKind != MessageKind::FRAGMENT)
{
bool success = _perMessageDeflate->decompress(message, _decompressedMessage);
std::string decompressedMessage;
bool success = _perMessageDeflate->decompress(message, decompressedMessage);
if (messageKind == MessageKind::MSG_TEXT && !validateUtf8(_decompressedMessage))
if (messageKind == MessageKind::MSG_TEXT && !validateUtf8(decompressedMessage))
{
close(WebSocketCloseConstants::kInvalidFramePayloadData,
WebSocketCloseConstants::kInvalidFramePayloadDataMessage);
}
else
{
onMessageCallback(_decompressedMessage, wireSize, !success, messageKind);
onMessageCallback(decompressedMessage, wireSize, !success, messageKind);
}
}
else
@ -762,6 +759,7 @@ namespace ix
size_t payloadSize = message.size();
size_t wireSize = message.size();
std::string compressedMessage;
bool compressionError = false;
std::string::const_iterator message_begin = message.begin();
@ -769,7 +767,7 @@ namespace ix
if (compress)
{
if (!_perMessageDeflate->compress(message, _compressedMessage))
if (!_perMessageDeflate->compress(message, compressedMessage))
{
bool success = false;
compressionError = true;
@ -778,10 +776,10 @@ namespace ix
return WebSocketSendInfo(success, compressionError, payloadSize, wireSize);
}
compressionError = false;
wireSize = _compressedMessage.size();
wireSize = compressedMessage.size();
message_begin = _compressedMessage.begin();
message_end = _compressedMessage.end();
message_begin = compressedMessage.begin();
message_end = compressedMessage.end();
}
{

View File

@ -165,7 +165,7 @@ namespace ix
MessageKind _fragmentedMessageKind;
// Ditto for whether a message is compressed
bool _receivedMessageCompressed;
bool _compressedMessage;
// Fragments are 32K long
static constexpr size_t kChunkSize = 1 << 15;
@ -189,9 +189,6 @@ namespace ix
WebSocketPerMessageDeflateOptions _perMessageDeflateOptions;
std::atomic<bool> _enablePerMessageDeflate;
std::string _decompressedMessage;
std::string _compressedMessage;
// Used to control TLS connection behavior
SocketTLSOptions _socketTLSOptions;
@ -212,7 +209,6 @@ namespace ix
static const int kDefaultPingIntervalSecs;
static const std::string kPingMessage;
std::atomic<uint64_t> _pingCount;
// We record when ping are being sent so that we can know when to send the next one
mutable std::mutex _lastSendPingTimePointMutex;

View File

@ -6,4 +6,4 @@
#pragma once
#define IX_WEBSOCKET_VERSION "9.5.4"
#define IX_WEBSOCKET_VERSION "9.2.1"

280
ixwebsocket/LUrlParser.cpp Normal file
View File

@ -0,0 +1,280 @@
/*
* Lightweight URL & URI parser (RFC 1738, RFC 3986)
* https://github.com/corporateshark/LUrlParser
*
* The MIT License (MIT)
*
* Copyright (C) 2015 Sergey Kosarevsky (sk@linderdaum.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "LUrlParser.h"
#include <algorithm>
#include <cstring>
#include <stdlib.h>
// check if the scheme name is valid
static bool IsSchemeValid(const std::string& SchemeName)
{
for (auto c : SchemeName)
{
if (!isalpha(c) && c != '+' && c != '-' && c != '.') return false;
}
return true;
}
bool LUrlParser::clParseURL::GetPort(int* OutPort) const
{
if (!IsValid())
{
return false;
}
int Port = atoi(m_Port.c_str());
if (Port <= 0 || Port > 65535)
{
return false;
}
if (OutPort)
{
*OutPort = Port;
}
return true;
}
// based on RFC 1738 and RFC 3986
LUrlParser::clParseURL LUrlParser::clParseURL::ParseURL(const std::string& URL)
{
LUrlParser::clParseURL Result;
const char* CurrentString = URL.c_str();
/*
* <scheme>:<scheme-specific-part>
* <scheme> := [a-z\+\-\.]+
* For resiliency, programs interpreting URLs should treat upper case letters as equivalent to
*lower case in scheme names
*/
// try to read scheme
{
const char* LocalString = strchr(CurrentString, ':');
if (!LocalString)
{
return clParseURL(LUrlParserError_NoUrlCharacter);
}
// save the scheme name
Result.m_Scheme = std::string(CurrentString, LocalString - CurrentString);
if (!IsSchemeValid(Result.m_Scheme))
{
return clParseURL(LUrlParserError_InvalidSchemeName);
}
// scheme should be lowercase
std::transform(
Result.m_Scheme.begin(), Result.m_Scheme.end(), Result.m_Scheme.begin(), ::tolower);
// skip ':'
CurrentString = LocalString + 1;
}
/*
* //<user>:<password>@<host>:<port>/<url-path>
* any ":", "@" and "/" must be normalized
*/
// skip "//"
if (*CurrentString++ != '/') return clParseURL(LUrlParserError_NoDoubleSlash);
if (*CurrentString++ != '/') return clParseURL(LUrlParserError_NoDoubleSlash);
// check if the user name and password are specified
bool bHasUserName = false;
const char* LocalString = CurrentString;
while (*LocalString)
{
if (*LocalString == '@')
{
// user name and password are specified
bHasUserName = true;
break;
}
else if (*LocalString == '/')
{
// end of <host>:<port> specification
bHasUserName = false;
break;
}
LocalString++;
}
// user name and password
LocalString = CurrentString;
if (bHasUserName)
{
// read user name
while (*LocalString && *LocalString != ':' && *LocalString != '@')
LocalString++;
Result.m_UserName = std::string(CurrentString, LocalString - CurrentString);
// proceed with the current pointer
CurrentString = LocalString;
if (*CurrentString == ':')
{
// skip ':'
CurrentString++;
// read password
LocalString = CurrentString;
while (*LocalString && *LocalString != '@')
LocalString++;
Result.m_Password = std::string(CurrentString, LocalString - CurrentString);
CurrentString = LocalString;
}
// skip '@'
if (*CurrentString != '@')
{
return clParseURL(LUrlParserError_NoAtSign);
}
CurrentString++;
}
bool bHasBracket = (*CurrentString == '[');
// go ahead, read the host name
LocalString = CurrentString;
while (*LocalString)
{
if (bHasBracket && *LocalString == ']')
{
// end of IPv6 address
LocalString++;
break;
}
else if (!bHasBracket && (*LocalString == ':' || *LocalString == '/'))
{
// port number is specified
break;
}
LocalString++;
}
Result.m_Host = std::string(CurrentString, LocalString - CurrentString);
CurrentString = LocalString;
// is port number specified?
if (*CurrentString == ':')
{
CurrentString++;
// read port number
LocalString = CurrentString;
while (*LocalString && *LocalString != '/')
LocalString++;
Result.m_Port = std::string(CurrentString, LocalString - CurrentString);
CurrentString = LocalString;
}
// end of string
if (!*CurrentString)
{
Result.m_ErrorCode = LUrlParserError_Ok;
return Result;
}
// skip '/'
if (*CurrentString != '/')
{
return clParseURL(LUrlParserError_NoSlash);
}
CurrentString++;
// parse the path
LocalString = CurrentString;
while (*LocalString && *LocalString != '#' && *LocalString != '?')
LocalString++;
Result.m_Path = std::string(CurrentString, LocalString - CurrentString);
CurrentString = LocalString;
// check for query
if (*CurrentString == '?')
{
// skip '?'
CurrentString++;
// read query
LocalString = CurrentString;
while (*LocalString && *LocalString != '#')
LocalString++;
Result.m_Query = std::string(CurrentString, LocalString - CurrentString);
CurrentString = LocalString;
}
// check for fragment
if (*CurrentString == '#')
{
// skip '#'
CurrentString++;
// read fragment
LocalString = CurrentString;
while (*LocalString)
LocalString++;
Result.m_Fragment = std::string(CurrentString, LocalString - CurrentString);
}
Result.m_ErrorCode = LUrlParserError_Ok;
return Result;
}

84
ixwebsocket/LUrlParser.h Normal file
View File

@ -0,0 +1,84 @@
/*
* Lightweight URL & URI parser (RFC 1738, RFC 3986)
* https://github.com/corporateshark/LUrlParser
*
* The MIT License (MIT)
*
* Copyright (C) 2015 Sergey Kosarevsky (sk@linderdaum.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include <string>
namespace LUrlParser
{
enum LUrlParserError
{
LUrlParserError_Ok = 0,
LUrlParserError_Uninitialized = 1,
LUrlParserError_NoUrlCharacter = 2,
LUrlParserError_InvalidSchemeName = 3,
LUrlParserError_NoDoubleSlash = 4,
LUrlParserError_NoAtSign = 5,
LUrlParserError_UnexpectedEndOfLine = 6,
LUrlParserError_NoSlash = 7,
};
class clParseURL
{
public:
LUrlParserError m_ErrorCode;
std::string m_Scheme;
std::string m_Host;
std::string m_Port;
std::string m_Path;
std::string m_Query;
std::string m_Fragment;
std::string m_UserName;
std::string m_Password;
clParseURL()
: m_ErrorCode(LUrlParserError_Uninitialized)
{
}
/// return 'true' if the parsing was successful
bool IsValid() const
{
return m_ErrorCode == LUrlParserError_Ok;
}
/// helper to convert the port number to int, return 'true' if the port is valid (within the
/// 0..65535 range)
bool GetPort(int* OutPort) const;
/// parse the URL
static clParseURL ParseURL(const std::string& URL);
private:
explicit clParseURL(LUrlParserError ErrorCode)
: m_ErrorCode(ErrorCode)
{
}
};
} // namespace LUrlParser

View File

@ -0,0 +1,135 @@
// Copyright (c) 2016 Alex Hultman and contributors
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgement in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
#pragma once
#include <cstdint>
#include <cstddef>
#include <string>
#include <string.h>
class WebSocketHandshakeKeyGen {
template <int N, typename T>
struct static_for {
void operator()(uint32_t *a, uint32_t *b) {
static_for<N - 1, T>()(a, b);
T::template f<N - 1>(a, b);
}
};
template <typename T>
struct static_for<0, T> {
void operator()(uint32_t * /*a*/, uint32_t * /*hash*/) {}
};
template <int state>
struct Sha1Loop {
static inline uint32_t rol(uint32_t value, size_t bits) {return (value << bits) | (value >> (32 - bits));}
static inline uint32_t blk(uint32_t b[16], size_t i) {
return rol(b[(i + 13) & 15] ^ b[(i + 8) & 15] ^ b[(i + 2) & 15] ^ b[i], 1);
}
template <int i>
static inline void f(uint32_t *a, uint32_t *b) {
switch (state) {
case 1:
a[i % 5] += ((a[(3 + i) % 5] & (a[(2 + i) % 5] ^ a[(1 + i) % 5])) ^ a[(1 + i) % 5]) + b[i] + 0x5a827999 + rol(a[(4 + i) % 5], 5);
a[(3 + i) % 5] = rol(a[(3 + i) % 5], 30);
break;
case 2:
b[i] = blk(b, i);
a[(1 + i) % 5] += ((a[(4 + i) % 5] & (a[(3 + i) % 5] ^ a[(2 + i) % 5])) ^ a[(2 + i) % 5]) + b[i] + 0x5a827999 + rol(a[(5 + i) % 5], 5);
a[(4 + i) % 5] = rol(a[(4 + i) % 5], 30);
break;
case 3:
b[(i + 4) % 16] = blk(b, (i + 4) % 16);
a[i % 5] += (a[(3 + i) % 5] ^ a[(2 + i) % 5] ^ a[(1 + i) % 5]) + b[(i + 4) % 16] + 0x6ed9eba1 + rol(a[(4 + i) % 5], 5);
a[(3 + i) % 5] = rol(a[(3 + i) % 5], 30);
break;
case 4:
b[(i + 8) % 16] = blk(b, (i + 8) % 16);
a[i % 5] += (((a[(3 + i) % 5] | a[(2 + i) % 5]) & a[(1 + i) % 5]) | (a[(3 + i) % 5] & a[(2 + i) % 5])) + b[(i + 8) % 16] + 0x8f1bbcdc + rol(a[(4 + i) % 5], 5);
a[(3 + i) % 5] = rol(a[(3 + i) % 5], 30);
break;
case 5:
b[(i + 12) % 16] = blk(b, (i + 12) % 16);
a[i % 5] += (a[(3 + i) % 5] ^ a[(2 + i) % 5] ^ a[(1 + i) % 5]) + b[(i + 12) % 16] + 0xca62c1d6 + rol(a[(4 + i) % 5], 5);
a[(3 + i) % 5] = rol(a[(3 + i) % 5], 30);
break;
case 6:
b[i] += a[4 - i];
}
}
};
static inline void sha1(uint32_t hash[5], uint32_t b[16]) {
uint32_t a[5] = {hash[4], hash[3], hash[2], hash[1], hash[0]};
static_for<16, Sha1Loop<1>>()(a, b);
static_for<4, Sha1Loop<2>>()(a, b);
static_for<20, Sha1Loop<3>>()(a, b);
static_for<20, Sha1Loop<4>>()(a, b);
static_for<20, Sha1Loop<5>>()(a, b);
static_for<5, Sha1Loop<6>>()(a, hash);
}
static inline void base64(unsigned char *src, char *dst) {
const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
for (int i = 0; i < 18; i += 3) {
*dst++ = b64[(src[i] >> 2) & 63];
*dst++ = b64[((src[i] & 3) << 4) | ((src[i + 1] & 240) >> 4)];
*dst++ = b64[((src[i + 1] & 15) << 2) | ((src[i + 2] & 192) >> 6)];
*dst++ = b64[src[i + 2] & 63];
}
*dst++ = b64[(src[18] >> 2) & 63];
*dst++ = b64[((src[18] & 3) << 4) | ((src[19] & 240) >> 4)];
*dst++ = b64[((src[19] & 15) << 2)];
*dst++ = '=';
}
public:
static inline void generate(const std::string& inputStr, char output[28]) {
char input[25] = {};
strncpy(input, inputStr.c_str(), 25 - 1);
input[25 - 1] = '\0';
uint32_t b_output[5] = {
0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0
};
uint32_t b_input[16] = {
0, 0, 0, 0, 0, 0, 0x32353845, 0x41464135, 0x2d453931, 0x342d3437, 0x44412d39,
0x3543412d, 0x43354142, 0x30444338, 0x35423131, 0x80000000
};
for (int i = 0; i < 6; i++) {
b_input[i] = (input[4 * i + 3] & 0xff) | (input[4 * i + 2] & 0xff) << 8 | (input[4 * i + 1] & 0xff) << 16 | (input[4 * i + 0] & 0xff) << 24;
}
sha1(b_output, b_input);
uint32_t last_b[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 480};
sha1(b_output, last_b);
for (int i = 0; i < 5; i++) {
uint32_t tmp = b_output[i];
char *bytes = (char *) &b_output[i];
bytes[3] = tmp & 0xff;
bytes[2] = (tmp >> 8) & 0xff;
bytes[1] = (tmp >> 16) & 0xff;
bytes[0] = (tmp >> 24) & 0xff;
}
base64((unsigned char *) b_output, output);
}
};

View File

@ -20,7 +20,7 @@ install: brew
# Release, Debug, MinSizeRel, RelWithDebInfo are the build types
#
brew:
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)
mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_TEST=1 .. ; make -j 4 install)
# Docker default target. We've add problem with OpenSSL and TLS 1.3 (on the
# server side ?) and I can't work-around it easily, so we're using mbedtls on
@ -103,10 +103,6 @@ test_server:
# env TEST=Websocket_chat make test
# env TEST=heartbeat make 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)
(cd test ; python2.7 run.py -r)
@ -120,12 +116,10 @@ test_ubsan:
(cd build/test ; ln -sf Debug/ixwebsocket_unittest)
(cd test ; python2.7 run.py -r)
test_asan: build_test_asan
(cd test ; python2.7 run.py -r)
build_test_asan:
test_asan:
mkdir -p build && (cd build && cmake -GXcode -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_TEST=1 .. && xcodebuild -project ixwebsocket.xcodeproj -target ixwebsocket_unittest -enableAddressSanitizer YES)
(cd build/test ; ln -sf Debug/ixwebsocket_unittest)
(cd test ; python2.7 run.py -r)
test_tsan_openssl:
mkdir -p build && (cd build && cmake -GXcode -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_TEST=1 -DUSE_OPEN_SSL=1 .. && xcodebuild -project ixwebsocket.xcodeproj -target ixwebsocket_unittest -enableThreadSanitizer YES)

View File

@ -66,7 +66,13 @@ if (UNIX)
IXCobraMetricsPublisherTest.cpp
IXCobraToSentryBotTest.cpp
IXCobraToStatsdBotTest.cpp
IXCobraToStdoutBotTest.cpp
)
endif()
# Some unittest fail for dubious reason on Ubuntu Xenial with TSAN
if (MAC OR WIN32)
list(APPEND SOURCES
IXWebSocketMessageQTest.cpp
)
endif()

View File

@ -180,40 +180,44 @@ namespace
_conn.configure(_cobraConfig);
_conn.connect();
_conn.setEventCallback([this, channel](const CobraEventPtr& event) {
if (event->type == ix::CobraEventType::Open)
_conn.setEventCallback([this, channel](ix::CobraConnectionEventType eventType,
const std::string& errMsg,
const ix::WebSocketHttpHeaders& headers,
const std::string& subscriptionId,
CobraConnection::MsgId msgId) {
if (eventType == ix::CobraConnection_EventType_Open)
{
log("Subscriber connected: " + _user);
for (auto&& it : event->headers)
for (auto&& it : headers)
{
log("Headers " + it.first + " " + it.second);
}
}
else if (event->type == ix::CobraEventType::Authenticated)
else if (eventType == ix::CobraConnection_EventType_Authenticated)
{
log("Subscriber authenticated: " + _user);
subscribe(channel);
}
else if (event->type == ix::CobraEventType::Error)
else if (eventType == ix::CobraConnection_EventType_Error)
{
log(event->errMsg + _user);
log(errMsg + _user);
}
else if (event->type == ix::CobraEventType::Closed)
else if (eventType == ix::CobraConnection_EventType_Closed)
{
log("Connection closed: " + _user);
}
else if (event->type == ix::CobraEventType::Subscribed)
else if (eventType == ix::CobraConnection_EventType_Subscribed)
{
log("Subscription ok: " + _user + " subscription_id " + event->subscriptionId);
log("Subscription ok: " + _user + " subscription_id " + subscriptionId);
_connectedAndSubscribed = true;
}
else if (event->type == ix::CobraEventType::UnSubscribed)
else if (eventType == ix::CobraConnection_EventType_UnSubscribed)
{
log("Unsubscription ok: " + _user + " subscription_id " + event->subscriptionId);
log("Unsubscription ok: " + _user + " subscription_id " + subscriptionId);
}
else if (event->type == ix::CobraEventType::Published)
else if (eventType == ix::CobraConnection_EventType_Published)
{
TLogger() << "Subscriber: published message acked: " << event->msgId;
TLogger() << "Subscriber: published message acked: " << msgId;
}
});
@ -244,7 +248,11 @@ namespace
ix::msleep(50);
_conn.disconnect();
_conn.setEventCallback([](const CobraEventPtr& /*event*/) {});
_conn.setEventCallback([](ix::CobraConnectionEventType /*eventType*/,
const std::string& /*errMsg*/,
const ix::WebSocketHttpHeaders& /*headers*/,
const std::string& /*subscriptionId*/,
CobraConnection::MsgId /*msgId*/) { ; });
}
} // namespace

View File

@ -54,24 +54,24 @@ namespace
conn.configure(config);
conn.connect();
conn.setEventCallback([&conn, &channel](const CobraEventPtr& event) {
if (event->type == ix::CobraEventType::Open)
conn.setEventCallback([&conn, &channel](ix::CobraConnectionEventType eventType,
const std::string& errMsg,
const ix::WebSocketHttpHeaders& headers,
const std::string& subscriptionId,
CobraConnection::MsgId msgId) {
if (eventType == ix::CobraConnection_EventType_Open)
{
TLogger() << "Subscriber connected:";
for (auto&& it : event->headers)
for (auto&& it : headers)
{
log("Headers " + it.first + " " + it.second);
}
}
else if (event->type == ix::CobraEventType::Closed)
if (eventType == ix::CobraConnection_EventType_Error)
{
TLogger() << "Subscriber closed:" << event->errMsg;
TLogger() << "Subscriber error:" << errMsg;
}
else if (event->type == ix::CobraEventType::Error)
{
TLogger() << "Subscriber error:" << event->errMsg;
}
else if (event->type == ix::CobraEventType::Authenticated)
else if (eventType == ix::CobraConnection_EventType_Authenticated)
{
log("Subscriber authenticated");
std::string filter;
@ -92,29 +92,29 @@ namespace
gMessageCount++;
});
}
else if (event->type == ix::CobraEventType::Subscribed)
else if (eventType == ix::CobraConnection_EventType_Subscribed)
{
TLogger() << "Subscriber: subscribed to channel " << event->subscriptionId;
if (event->subscriptionId == channel)
TLogger() << "Subscriber: subscribed to channel " << subscriptionId;
if (subscriptionId == channel)
{
gSubscriberConnectedAndSubscribed = true;
}
else
{
TLogger() << "Subscriber: unexpected channel " << event->subscriptionId;
TLogger() << "Subscriber: unexpected channel " << subscriptionId;
}
}
else if (event->type == ix::CobraEventType::UnSubscribed)
else if (eventType == ix::CobraConnection_EventType_UnSubscribed)
{
TLogger() << "Subscriber: ununexpected from channel " << event->subscriptionId;
if (event->subscriptionId != channel)
TLogger() << "Subscriber: ununexpected from channel " << subscriptionId;
if (subscriptionId != channel)
{
TLogger() << "Subscriber: unexpected channel " << event->subscriptionId;
TLogger() << "Subscriber: unexpected channel " << subscriptionId;
}
}
else if (event->type == ix::CobraEventType::Published)
else if (eventType == ix::CobraConnection_EventType_Published)
{
TLogger() << "Subscriber: published message acked: " << event->msgId;
TLogger() << "Subscriber: published message acked: " << msgId;
}
});

View File

@ -141,6 +141,7 @@ TEST_CASE("Cobra_to_sentry_bot", "[cobra_bots]")
std::string filter;
std::string position("$");
bool verbose = true;
bool strict = true;
size_t maxQueueSize = 10;
bool enableHeartbeat = false;
@ -160,15 +161,16 @@ TEST_CASE("Cobra_to_sentry_bot", "[cobra_bots]")
// Only run the bot for 3 seconds
int runtime = 3;
int64_t sentCount = cobra_to_sentry_bot(config,
channel,
filter,
position,
sentryClient,
verbose,
maxQueueSize,
enableHeartbeat,
runtime);
int sentCount = cobra_to_sentry_bot(config,
channel,
filter,
position,
sentryClient,
verbose,
strict,
maxQueueSize,
enableHeartbeat,
runtime);
//
// We want at least 2 messages to be sent
//

View File

@ -114,18 +114,18 @@ TEST_CASE("Cobra_to_statsd_bot", "[cobra_bots]")
std::string gauge;
std::string timer;
int64_t sentCount = ix::cobra_to_statsd_bot(config,
channel,
filter,
position,
statsdClient,
fields,
gauge,
timer,
verbose,
maxQueueSize,
enableHeartbeat,
runtime);
int sentCount = ix::cobra_to_statsd_bot(config,
channel,
filter,
position,
statsdClient,
fields,
gauge,
timer,
verbose,
maxQueueSize,
enableHeartbeat,
runtime);
//
// We want at least 2 messages to be sent
//

View File

@ -1,127 +0,0 @@
/*
* IXCobraToStdoutTest.cpp
* Author: Benjamin Sergeant
* Copyright (c) 2020 Machine Zone. All rights reserved.
*/
#include "IXTest.h"
#include "catch.hpp"
#include <chrono>
#include <iostream>
#include <ixbots/IXCobraToStdoutBot.h>
#include <ixcobra/IXCobraConnection.h>
#include <ixcobra/IXCobraMetricsPublisher.h>
#include <ixcrypto/IXUuid.h>
#include <ixsentry/IXSentryClient.h>
#include <ixsnake/IXRedisServer.h>
#include <ixsnake/IXSnakeServer.h>
#include <ixwebsocket/IXHttpServer.h>
#include <ixwebsocket/IXUserAgent.h>
using namespace ix;
namespace
{
void runPublisher(const ix::CobraConfig& config, const std::string& channel)
{
ix::CobraMetricsPublisher cobraMetricsPublisher;
cobraMetricsPublisher.configure(config, channel);
cobraMetricsPublisher.setSession(uuid4());
cobraMetricsPublisher.enable(true);
Json::Value msg;
msg["fps"] = 60;
cobraMetricsPublisher.setGenericAttributes("game", "ody");
// Wait a bit
ix::msleep(500);
// publish some messages
cobraMetricsPublisher.push("sms_metric_A_id", msg); // (msg #1)
cobraMetricsPublisher.push("sms_metric_B_id", msg); // (msg #2)
ix::msleep(500);
cobraMetricsPublisher.push("sms_metric_A_id", msg); // (msg #3)
cobraMetricsPublisher.push("sms_metric_D_id", msg); // (msg #4)
ix::msleep(500);
cobraMetricsPublisher.push("sms_metric_A_id", msg); // (msg #4)
cobraMetricsPublisher.push("sms_metric_F_id", msg); // (msg #5)
ix::msleep(500);
}
} // namespace
TEST_CASE("Cobra_to_stdout_bot", "[cobra_bots]")
{
SECTION("Exchange and count sent/received messages.")
{
int port = getFreePort();
snake::AppConfig appConfig = makeSnakeServerConfig(port, true);
// Start a redis server
ix::RedisServer redisServer(appConfig.redisPort);
auto res = redisServer.listen();
REQUIRE(res.first);
redisServer.start();
// Start a snake server
snake::SnakeServer snakeServer(appConfig);
snakeServer.run();
// Run the bot for a small amount of time
std::string channel = ix::generateSessionId();
std::string appkey("FC2F10139A2BAc53BB72D9db967b024f");
std::string role = "_sub";
std::string secret = "66B1dA3ED5fA074EB5AE84Dd8CE3b5ba";
std::string endpoint = makeCobraEndpoint(port, true);
ix::CobraConfig config;
config.endpoint = endpoint;
config.appkey = appkey;
config.rolename = role;
config.rolesecret = secret;
config.socketTLSOptions = makeClientTLSOptions();
std::thread publisherThread(runPublisher, config, channel);
std::string filter;
std::string position("$");
bool verbose = true;
bool quiet = false;
size_t maxQueueSize = 10;
bool enableHeartbeat = false;
// Only run the bot for 3 seconds
int runtime = 3;
// We could try to capture the output ... not sure how.
bool fluentd = true;
int64_t sentCount = ix::cobra_to_stdout_bot(config,
channel,
filter,
position,
fluentd,
quiet,
verbose,
maxQueueSize,
enableHeartbeat,
runtime);
//
// We want at least 2 messages to be sent
//
REQUIRE(sentCount >= 2);
// Give us 1s for all messages to be received
ix::msleep(1000);
spdlog::info("Stopping snake server...");
snakeServer.stop();
spdlog::info("Stopping redis server...");
redisServer.stop();
publisherThread.join();
}
}

View File

@ -4,14 +4,6 @@
* 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 <ixwebsocket/IXNetSystem.h>

View File

@ -17,11 +17,8 @@
#include <ixwebsocket/IXSelectInterruptFactory.h>
#include <ixwebsocket/IXSetThreadName.h>
#include <ixwebsocket/IXSocket.h>
#include <ixwebsocket/IXSocketAppleSSL.h>
#include <ixwebsocket/IXSocketConnect.h>
#include <ixwebsocket/IXSocketFactory.h>
#include <ixwebsocket/IXSocketMbedTLS.h>
#include <ixwebsocket/IXSocketOpenSSL.h>
#include <ixwebsocket/IXSocketServer.h>
#include <ixwebsocket/IXUrlParser.h>
#include <ixwebsocket/IXWebSocket.h>
@ -29,9 +26,9 @@
#include <ixwebsocket/IXWebSocketCloseInfo.h>
#include <ixwebsocket/IXWebSocketErrorInfo.h>
#include <ixwebsocket/IXWebSocketHandshake.h>
#include <ixwebsocket/IXWebSocketHandshakeKeyGen.h>
#include <ixwebsocket/IXWebSocketHttpHeaders.h>
#include <ixwebsocket/IXWebSocketMessage.h>
#include <ixwebsocket/IXWebSocketMessageQueue.h>
#include <ixwebsocket/IXWebSocketMessageType.h>
#include <ixwebsocket/IXWebSocketOpenInfo.h>
#include <ixwebsocket/IXWebSocketPerMessageDeflate.h>
@ -40,6 +37,8 @@
#include <ixwebsocket/IXWebSocketSendInfo.h>
#include <ixwebsocket/IXWebSocketServer.h>
#include <ixwebsocket/IXWebSocketTransport.h>
#include <ixwebsocket/LUrlParser.h>
#include <ixwebsocket/libwshandshake.hpp>
using namespace ix;

View File

@ -6,8 +6,8 @@
#include "IXTest.h"
#include "catch.hpp"
#include "msgpack11.hpp"
#include <iostream>
#include "msgpack11.hpp"
#include <ixwebsocket/IXSocket.h>
#include <ixwebsocket/IXSocketFactory.h>
#include <ixwebsocket/IXWebSocket.h>
@ -130,8 +130,7 @@ namespace
}
else if (msg->type == ix::WebSocketMessageType::Error)
{
ss << "websocket_broadcast_client: " << _user << " Error ! "
<< msg->errorInfo.reason;
ss << "websocket_broadcast_client: " << _user << " Error ! " << msg->errorInfo.reason;
log(ss.str());
}
else if (msg->type == ix::WebSocketMessageType::Ping)
@ -235,7 +234,7 @@ namespace
server.start();
return true;
}
} // namespace
} // namespace ix
TEST_CASE("Websocket_broadcast_server", "[websocket_server]")
{
@ -248,7 +247,7 @@ TEST_CASE("Websocket_broadcast_server", "[websocket_server]")
std::string session = ix::generateSessionId();
std::vector<std::shared_ptr<WebSocketChat>> chatClients;
for (int i = 0; i < 10; ++i)
for (int i = 0 ; i < 10; ++i)
{
std::string user("user_" + std::to_string(i));
chatClients.push_back(std::make_shared<WebSocketChat>(user, session, port));
@ -260,7 +259,7 @@ TEST_CASE("Websocket_broadcast_server", "[websocket_server]")
while (true)
{
bool allReady = true;
for (size_t i = 0; i < chatClients.size(); ++i)
for (size_t i = 0 ; i < chatClients.size(); ++i)
{
allReady &= chatClients[i]->isReady();
}
@ -270,7 +269,7 @@ TEST_CASE("Websocket_broadcast_server", "[websocket_server]")
for (int j = 0; j < 1000; j++)
{
for (size_t i = 0; i < chatClients.size(); ++i)
for (size_t i = 0 ; i < chatClients.size(); ++i)
{
chatClients[i]->sendMessage("hello world");
}
@ -292,7 +291,7 @@ TEST_CASE("Websocket_broadcast_server", "[websocket_server]")
// Stop all clients
size_t messageCount = chatClients.size() * 50;
for (size_t i = 0; i < chatClients.size(); ++i)
for (size_t i = 0 ; i < chatClients.size(); ++i)
{
REQUIRE(chatClients[i]->getReceivedMessagesCount() >= messageCount);
chatClients[i]->stop();

View File

@ -0,0 +1,178 @@
/*
* IXWebSocketMessageQTest.cpp
* Author: Korchynskyi Dmytro
* Copyright (c) 2019 Machine Zone. All rights reserved.
*/
#include "IXTest.h"
#include "catch.hpp"
#include <ixwebsocket/IXWebSocket.h>
#include <ixwebsocket/IXWebSocketMessageQueue.h>
#include <ixwebsocket/IXWebSocketServer.h>
#include <thread>
using namespace ix;
namespace
{
bool startServer(ix::WebSocketServer& server)
{
server.setOnConnectionCallback([&server](std::shared_ptr<ix::WebSocket> webSocket,
std::shared_ptr<ConnectionState> connectionState) {
webSocket->setOnMessageCallback(
[connectionState, &server](const WebSocketMessagePtr& msg) {
if (msg->type == ix::WebSocketMessageType::Open)
{
TLogger() << "New connection";
connectionState->computeId();
TLogger() << "id: " << connectionState->getId();
TLogger() << "Uri: " << msg->openInfo.uri;
TLogger() << "Headers:";
for (auto&& it : msg->openInfo.headers)
{
TLogger() << it.first << ": " << it.second;
}
}
else if (msg->type == ix::WebSocketMessageType::Close)
{
TLogger() << "Closed connection";
}
else if (msg->type == ix::WebSocketMessageType::Message)
{
TLogger() << "Message received: " << msg->str;
for (auto&& client : server.getClients())
{
client->send(msg->str);
}
}
});
});
auto res = server.listen();
if (!res.first)
{
TLogger() << res.second;
return false;
}
server.start();
return true;
}
class MsgQTestClient
{
public:
MsgQTestClient()
{
msgQ.bindWebsocket(&ws);
msgQ.setOnMessageCallback([this](const WebSocketMessagePtr& msg) {
REQUIRE(mainThreadId == std::this_thread::get_id());
std::stringstream ss;
if (msg->type == WebSocketMessageType::Open)
{
log("client connected");
sendNextMessage();
}
else if (msg->type == WebSocketMessageType::Close)
{
log("client disconnected");
}
else if (msg->type == WebSocketMessageType::Error)
{
ss << "Error ! " << msg->errorInfo.reason;
log(ss.str());
testDone = true;
}
else if (msg->type == WebSocketMessageType::Pong)
{
ss << "Received pong message " << msg->str;
log(ss.str());
}
else if (msg->type == WebSocketMessageType::Ping)
{
ss << "Received ping message " << msg->str;
log(ss.str());
}
else if (msg->type == WebSocketMessageType::Message)
{
REQUIRE(msg->str.compare("Hey dude!") == 0);
++receivedCount;
ss << "Received message " << msg->str;
log(ss.str());
sendNextMessage();
}
else
{
ss << "Invalid WebSocketMessageType";
log(ss.str());
testDone = true;
}
});
}
void sendNextMessage()
{
if (receivedCount >= 3)
{
testDone = true;
succeeded = true;
}
else
{
auto info = ws.sendText("Hey dude!");
if (info.success)
log("sent message");
else
log("send failed");
}
}
void run(const std::string& url)
{
mainThreadId = std::this_thread::get_id();
testDone = false;
receivedCount = 0;
ws.setUrl(url);
ws.start();
while (!testDone)
{
msgQ.poll();
msleep(50);
}
}
bool isSucceeded() const
{
return succeeded;
}
private:
WebSocket ws;
WebSocketMessageQueue msgQ;
bool testDone = false;
uint32_t receivedCount = 0;
std::thread::id mainThreadId;
bool succeeded = false;
};
} // namespace
TEST_CASE("Websocket_message_queue", "[websocket_message_q]")
{
SECTION("Send several messages")
{
int port = getFreePort();
WebSocketServer server(port);
REQUIRE(startServer(server));
MsgQTestClient testClient;
testClient.run("ws://127.0.0.1:" + std::to_string(port));
REQUIRE(testClient.isSucceeded());
server.stop();
}
}

View File

@ -14,41 +14,8 @@ int main(int argc, char* argv[])
{
ix::initNetSystem();
ix::CoreLogger::LogFunc logFunc = [](const char* msg, ix::LogLevel level) {
switch (level)
{
case ix::LogLevel::Debug:
{
spdlog::debug(msg);
}
break;
case ix::LogLevel::Info:
{
spdlog::info(msg);
}
break;
case ix::LogLevel::Warning:
{
spdlog::warn(msg);
}
break;
case ix::LogLevel::Error:
{
spdlog::error(msg);
}
break;
case ix::LogLevel::Critical:
{
spdlog::critical(msg);
}
break;
}
};
ix::CoreLogger::setLogFunction(logFunc);
ix::IXCoreLogger::LogFunc logFunc = [](const char* msg) { spdlog::info(msg); };
ix::IXCoreLogger::setLogFunction(logFunc);
int result = Catch::Session().run(argc, argv);

View File

@ -1,2 +1,4 @@
DisableFormat: true
SortIncludes: false
{
"DisableFormat": true,
"SortIncludes": false
}

View File

@ -0,0 +1,41 @@
Note: This is just a template, so feel free to use/remove the unnecessary things
### Description
- Type: Bug | Enhancement\Feature Request | Question
- Priority: Blocker | Major | Minor
---------------------------------------------------------------
## Bug
**OS**
Mbed OS|linux|windows|
**mbed TLS build:**
Version: x.x.x or git commit id
OS version: x.x.x
Configuration: please attach config.h file where possible
Compiler and options (if you used a pre-built binary, please indicate how you obtained it):
Additional environment information:
**Peer device TLS stack and version**
OpenSSL|GnuTls|Chrome|NSS(Firefox)|SecureChannel (IIS/Internet Explorer/Edge)|Other
Version:
**Expected behavior**
**Actual behavior**
**Steps to reproduce**
----------------------------------------------------------------
## Enhancement\Feature Request
**Justification - why does the library need this feature?**
**Suggested enhancement**
-----------------------------------------------------------------
## Question
**Please first check for answers in the [Mbed TLS knowledge Base](https://tls.mbed.org/kb), and preferably file an issue in the [Mbed TLS support forum](https://forums.mbed.com/c/mbed-tls)**

View File

@ -0,0 +1,39 @@
Notes:
* Pull requests cannot be accepted until:
- The submitter has [accepted the online agreement here with a click through](https://developer.mbed.org/contributor_agreement/)
or for companies or those that do not wish to create an mbed account, a slightly different agreement can be found [here](https://www.mbed.com/en/about-mbed/contributor-license-agreements/)
- The PR follows the [mbed TLS coding standards](https://tls.mbed.org/kb/development/mbedtls-coding-standards)
* This is just a template, so feel free to use/remove the unnecessary things
## Description
A few sentences describing the overall goals of the pull request's commits.
## Status
**READY/IN DEVELOPMENT/HOLD**
## Requires Backporting
When there is a bug fix, it should be backported to all maintained and supported branches.
Changes do not have to be backported if:
- This PR is a new feature\enhancement
- This PR contains changes in the API. If this is true, and there is a need for the fix to be backported, the fix should be handled differently in the legacy branch
Yes | NO
Which branch?
## Migrations
If there is any API change, what's the incentive and logic for it.
YES | NO
## Additional comments
Any additional information that could be of interest
## Todos
- [ ] Tests
- [ ] Documentation
- [ ] Changelog updated
- [ ] Backported
## Steps to test or reproduce
Outline the steps to test or reproduce the PR here.

43
third_party/mbedtls/.gitignore vendored Normal file
View File

@ -0,0 +1,43 @@
# Random seed file created by test scripts and sample programs
seedfile
# CMake build artifacts:
CMakeCache.txt
CMakeFiles
CTestTestfile.cmake
cmake_install.cmake
Testing
# CMake generates *.dir/ folders for in-tree builds (used by MSVC projects), ignore all of those:
*.dir/
# MSVC files generated by CMake:
/*.sln
/*.vcxproj
/*.filters
# Test coverage build artifacts:
Coverage
*.gcno
*.gcda
# generated by scripts/memory.sh
massif-*
# MSVC build artifacts:
*.exe
*.pdb
*.ilk
*.lib
# Python build artifacts:
*.pyc
# Generated documentation:
/apidoc
# Editor navigation files:
/GPATH
/GRTAGS
/GSYMS
/GTAGS
/TAGS
/tags

3
third_party/mbedtls/.globalrc vendored Normal file
View File

@ -0,0 +1,3 @@
default:\
:langmap=c\:.c.h.function:\

52
third_party/mbedtls/.pylintrc vendored Normal file
View File

@ -0,0 +1,52 @@
[BASIC]
# We're ok with short funtion argument names.
# [invalid-name]
argument-rgx=[a-z_][a-z0-9_]*$
# Allow filter and map.
# [bad-builtin]
bad-functions=input
# We prefer docstrings, but we don't require them on all functions.
# Require them only on long functions (for some value of long).
# [missing-docstring]
docstring-min-length=10
# Allow longer methods than the default.
# [invalid-name]
method-rgx=[a-z_][a-z0-9_]{2,35}$
# Allow module names containing a dash (but no underscore or uppercase letter).
# They are whole programs, not meant to be included by another module.
# [invalid-name]
module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+)|[a-z][-0-9a-z]+)$
# Some functions don't need docstrings.
# [missing-docstring]
no-docstring-rgx=(run_)?main$
# We're ok with short local or global variable names.
# [invalid-name]
variable-rgx=[a-z_][a-z0-9_]*$
[DESIGN]
# Allow more than the default 7 attributes.
# [too-many-instance-attributes]
max-attributes=15
[FORMAT]
# Allow longer modules than the default recommended maximum.
# [too-many-lines]
max-module-lines=2000
[MESSAGES CONTROL]
disable=
[REPORTS]
# Don't diplay statistics. Just the facts.
reports=no
[VARIABLES]
# Allow unused variables if their name starts with an underscore.
# [unused-argument]
dummy-variables-rgx=_.*

48
third_party/mbedtls/.travis.yml vendored Normal file
View File

@ -0,0 +1,48 @@
language: c
compiler:
- clang
- gcc
sudo: false
cache: ccache
# blocklist
branches:
except:
- development-psa
- coverity_scan
script:
- tests/scripts/recursion.pl library/*.c
- tests/scripts/check-generated-files.sh
- tests/scripts/check-doxy-blocks.pl
- tests/scripts/check-names.sh
- tests/scripts/check-files.py
- tests/scripts/doxygen.sh
- cmake -D CMAKE_BUILD_TYPE:String="Check" .
- make
- make test
- programs/test/selftest
- OSSL_NO_DTLS=1 tests/compat.sh
- tests/ssl-opt.sh -e '\(DTLS\|SCSV\).*openssl'
- tests/scripts/test-ref-configs.pl
- tests/scripts/curves.pl
- tests/scripts/key-exchanges.pl
after_failure:
- tests/scripts/travis-log-failure.sh
env:
global:
- SEED=1
- secure: "barHldniAfXyoWOD/vcO+E6/Xm4fmcaUoC9BeKW+LwsHqlDMLvugaJnmLXkSpkbYhVL61Hzf3bo0KPJn88AFc5Rkf8oYHPjH4adMnVXkf3B9ghHCgznqHsAH3choo6tnPxaFgOwOYmLGb382nQxfE5lUdvnM/W/psQjWt66A1+k="
addons:
apt:
packages:
- doxygen
- graphviz
coverity_scan:
project:
name: "ARMmbed/mbedtls"
notification_email: simon.butcher@arm.com
build_command_prepend:
build_command: make
branch_pattern: coverity_scan

246
third_party/mbedtls/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,246 @@
cmake_minimum_required(VERSION 2.6)
if(TEST_CPP)
project("mbed TLS" C CXX)
else()
project("mbed TLS" C)
endif()
set(MBEDTLS_DIR ${CMAKE_CURRENT_SOURCE_DIR})
option(USE_PKCS11_HELPER_LIBRARY "Build mbed TLS with the pkcs11-helper library." OFF)
option(ENABLE_ZLIB_SUPPORT "Build mbed TLS with zlib library." OFF)
option(ENABLE_PROGRAMS "Build mbed TLS programs." OFF)
option(UNSAFE_BUILD "Allow unsafe builds. These builds ARE NOT SECURE." OFF)
# export the submodule flag so that crypto knows it's being built as a submodule
set( USE_CRYPTO_SUBMODULE ON )
string(REGEX MATCH "Clang" CMAKE_COMPILER_IS_CLANG "${CMAKE_C_COMPILER_ID}")
string(REGEX MATCH "GNU" CMAKE_COMPILER_IS_GNU "${CMAKE_C_COMPILER_ID}")
string(REGEX MATCH "IAR" CMAKE_COMPILER_IS_IAR "${CMAKE_C_COMPILER_ID}")
string(REGEX MATCH "MSVC" CMAKE_COMPILER_IS_MSVC "${CMAKE_C_COMPILER_ID}")
# the test suites currently have compile errors with MSVC
if(CMAKE_COMPILER_IS_MSVC)
option(ENABLE_TESTING "Build mbed TLS tests." OFF)
else()
option(ENABLE_TESTING "Build mbed TLS tests." OFF)
endif()
# Warning string - created as a list for compatibility with CMake 2.8
set(WARNING_BORDER "*******************************************************\n")
set(NULL_ENTROPY_WARN_L1 "**** WARNING! MBEDTLS_TEST_NULL_ENTROPY defined!\n")
set(NULL_ENTROPY_WARN_L2 "**** THIS BUILD HAS NO DEFINED ENTROPY SOURCES\n")
set(NULL_ENTROPY_WARN_L3 "**** AND IS *NOT* SUITABLE FOR PRODUCTION USE\n")
set(NULL_ENTROPY_WARNING "${WARNING_BORDER}"
"${NULL_ENTROPY_WARN_L1}"
"${NULL_ENTROPY_WARN_L2}"
"${NULL_ENTROPY_WARN_L3}"
"${WARNING_BORDER}")
set(CTR_DRBG_128_BIT_KEY_WARN_L1 "**** WARNING! MBEDTLS_CTR_DRBG_USE_128_BIT_KEY defined!\n")
set(CTR_DRBG_128_BIT_KEY_WARN_L2 "**** Using 128-bit keys for CTR_DRBG limits the security of generated\n")
set(CTR_DRBG_128_BIT_KEY_WARN_L3 "**** keys and operations that use random values generated to 128-bit security\n")
set(CTR_DRBG_128_BIT_KEY_WARNING "${WARNING_BORDER}"
"${CTR_DRBG_128_BIT_KEY_WARN_L1}"
"${CTR_DRBG_128_BIT_KEY_WARN_L2}"
"${CTR_DRBG_128_BIT_KEY_WARN_L3}"
"${WARNING_BORDER}")
find_package(PythonInterp)
find_package(Perl)
if(PERL_FOUND)
# If 128-bit keys are configured for CTR_DRBG, display an appropriate warning
execute_process(COMMAND ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/scripts/config.pl -f ${CMAKE_CURRENT_SOURCE_DIR}/include/mbedtls/config.h get MBEDTLS_CTR_DRBG_USE_128_BIT_KEY
RESULT_VARIABLE result)
if(${result} EQUAL 0)
message(WARNING ${CTR_DRBG_128_BIT_KEY_WARNING})
endif()
# If NULL Entropy is configured, display an appropriate warning
execute_process(COMMAND ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/scripts/config.pl -f ${CMAKE_CURRENT_SOURCE_DIR}/include/mbedtls/config.h get MBEDTLS_TEST_NULL_ENTROPY
RESULT_VARIABLE result)
if(${result} EQUAL 0)
message(WARNING ${NULL_ENTROPY_WARNING})
if(NOT UNSAFE_BUILD)
message(FATAL_ERROR "\
\n\
Warning! You have enabled MBEDTLS_TEST_NULL_ENTROPY. \
This option is not safe for production use and negates all security \
It is intended for development use only. \
\n\
To confirm you want to build with this option, re-run cmake with the \
option: \n\
cmake -DUNSAFE_BUILD=ON ")
return()
endif()
endif()
endif()
set(CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE}
CACHE STRING "Choose the type of build: None Debug Release Coverage ASan ASanDbg MemSan MemSanDbg Check CheckFull"
FORCE)
# Create a symbolic link from ${base_name} in the binary directory
# to the corresponding path in the source directory.
function(link_to_source base_name)
# Get OS dependent path to use in `execute_process`
if (CMAKE_HOST_WIN32)
#mklink is an internal command of cmd.exe it can only work with \
string(REPLACE "/" "\\" link "${CMAKE_CURRENT_BINARY_DIR}/${base_name}")
string(REPLACE "/" "\\" target "${CMAKE_CURRENT_SOURCE_DIR}/${base_name}")
else()
set(link "${CMAKE_CURRENT_BINARY_DIR}/${base_name}")
set(target "${CMAKE_CURRENT_SOURCE_DIR}/${base_name}")
endif()
if (NOT EXISTS ${link})
if (CMAKE_HOST_UNIX)
set(command ln -s ${target} ${link})
else()
if (IS_DIRECTORY ${target})
set(command cmd.exe /c mklink /j ${link} ${target})
else()
set(command cmd.exe /c mklink /h ${link} ${target})
endif()
endif()
execute_process(COMMAND ${command}
RESULT_VARIABLE result
ERROR_VARIABLE output)
if (NOT ${result} EQUAL 0)
message(FATAL_ERROR "Could not create symbolic link for: ${target} --> ${output}")
endif()
endif()
endfunction(link_to_source)
string(REGEX MATCH "Clang" CMAKE_COMPILER_IS_CLANG "${CMAKE_C_COMPILER_ID}")
if(CMAKE_COMPILER_IS_GNU)
# some warnings we want are not available with old GCC versions
# note: starting with CMake 2.8 we could use CMAKE_C_COMPILER_VERSION
execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion
OUTPUT_VARIABLE GCC_VERSION)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -W -Wdeclaration-after-statement -Wwrite-strings")
if (GCC_VERSION VERSION_GREATER 4.5 OR GCC_VERSION VERSION_EQUAL 4.5)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wlogical-op")
endif()
if (GCC_VERSION VERSION_GREATER 4.8 OR GCC_VERSION VERSION_EQUAL 4.8)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wshadow")
endif()
set(CMAKE_C_FLAGS_RELEASE "-O2")
set(CMAKE_C_FLAGS_DEBUG "-O0 -g3")
set(CMAKE_C_FLAGS_COVERAGE "-O0 -g3 --coverage")
set(CMAKE_C_FLAGS_ASAN "-Werror -fsanitize=address -fno-common -O3")
set(CMAKE_C_FLAGS_ASANDBG "-Werror -fsanitize=address -fno-common -O1 -g3 -fno-omit-frame-pointer -fno-optimize-sibling-calls ")
set(CMAKE_C_FLAGS_CHECK "-Werror -Os")
set(CMAKE_C_FLAGS_CHECKFULL "${CMAKE_C_FLAGS_CHECK} -Wcast-qual")
endif(CMAKE_COMPILER_IS_GNU)
if(CMAKE_COMPILER_IS_CLANG)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -W -Wdeclaration-after-statement -Wwrite-strings -Wpointer-arith -Wimplicit-fallthrough -Wshadow")
set(CMAKE_C_FLAGS_RELEASE "-O2")
set(CMAKE_C_FLAGS_DEBUG "-O0 -g3")
set(CMAKE_C_FLAGS_COVERAGE "-O0 -g3 --coverage")
set(CMAKE_C_FLAGS_ASAN "-Werror -fsanitize=address -fno-common -fsanitize=undefined -fno-sanitize-recover=all -O3")
set(CMAKE_C_FLAGS_ASANDBG "-Werror -fsanitize=address -fno-common -fsanitize=undefined -fno-sanitize-recover=all -O1 -g3 -fno-omit-frame-pointer -fno-optimize-sibling-calls ")
set(CMAKE_C_FLAGS_MEMSAN "-Werror -fsanitize=memory -O3")
set(CMAKE_C_FLAGS_MEMSANDBG "-Werror -fsanitize=memory -O1 -g3 -fno-omit-frame-pointer -fno-optimize-sibling-calls -fsanitize-memory-track-origins=2")
set(CMAKE_C_FLAGS_CHECK "-Werror -Os")
endif(CMAKE_COMPILER_IS_CLANG)
if(CMAKE_COMPILER_IS_IAR)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --warn_about_c_style_casts --warnings_are_errors -Ohz")
endif(CMAKE_COMPILER_IS_IAR)
if(CMAKE_COMPILER_IS_MSVC)
# Strictest warnings, and treat as errors
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W3")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /WX")
endif(CMAKE_COMPILER_IS_MSVC)
if(CMAKE_BUILD_TYPE STREQUAL "Coverage")
if(CMAKE_COMPILER_IS_GNU OR CMAKE_COMPILER_IS_CLANG)
set(CMAKE_SHARED_LINKER_FLAGS "--coverage")
endif(CMAKE_COMPILER_IS_GNU OR CMAKE_COMPILER_IS_CLANG)
endif(CMAKE_BUILD_TYPE STREQUAL "Coverage")
if(LIB_INSTALL_DIR)
else()
set(LIB_INSTALL_DIR lib)
endif()
if(ENABLE_ZLIB_SUPPORT)
find_package(ZLIB)
if(ZLIB_FOUND)
include_directories(${ZLIB_INCLUDE_DIR})
endif(ZLIB_FOUND)
endif(ENABLE_ZLIB_SUPPORT)
add_subdirectory(library)
add_subdirectory(include)
add_subdirectory(crypto/library)
add_subdirectory(crypto/include)
if(ENABLE_PROGRAMS)
add_subdirectory(programs)
endif()
ADD_CUSTOM_TARGET(apidoc
COMMAND doxygen mbedtls.doxyfile
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/doxygen)
if(ENABLE_TESTING)
enable_testing()
add_subdirectory(tests)
add_subdirectory(crypto/tests)
# additional convenience targets for Unix only
if(UNIX)
ADD_CUSTOM_TARGET(covtest
COMMAND make test
COMMAND programs/test/selftest
COMMAND tests/compat.sh
COMMAND tests/ssl-opt.sh
)
ADD_CUSTOM_TARGET(lcov
COMMAND rm -rf Coverage
COMMAND lcov --capture --initial --directory library/CMakeFiles/mbedtls.dir -o files.info
COMMAND lcov --capture --directory library/CMakeFiles/mbedtls.dir -o tests.info
COMMAND lcov --add-tracefile files.info --add-tracefile tests.info -o all.info
COMMAND lcov --remove all.info -o final.info '*.h'
COMMAND gendesc tests/Descriptions.txt -o descriptions
COMMAND genhtml --title "mbed TLS" --description-file descriptions --keep-descriptions --legend --no-branch-coverage -o Coverage final.info
COMMAND rm -f files.info tests.info all.info final.info descriptions
)
ADD_CUSTOM_TARGET(memcheck
COMMAND sed -i.bak s+/usr/bin/valgrind+`which valgrind`+ DartConfiguration.tcl
COMMAND ctest -O memcheck.log -D ExperimentalMemCheck
COMMAND tail -n1 memcheck.log | grep 'Memory checking results:' > /dev/null
COMMAND rm -f memcheck.log
COMMAND mv DartConfiguration.tcl.bak DartConfiguration.tcl
)
endif(UNIX)
endif()
# Make scripts needed for testing available in an out-of-source build.
if (NOT ${CMAKE_CURRENT_BINARY_DIR} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR})
link_to_source(scripts)
# Copy (don't link) DartConfiguration.tcl, needed for memcheck, to
# keep things simple with the sed commands in the memcheck target.
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/DartConfiguration.tcl
${CMAKE_CURRENT_BINARY_DIR}/DartConfiguration.tcl COPYONLY)
endif()

95
third_party/mbedtls/CONTRIBUTING.md vendored Normal file
View File

@ -0,0 +1,95 @@
Contributing
============
We gratefully accept bug reports and contributions from the community. There are some requirements we need to fulfill in order to be able to integrate contributions:
- As with any open source project, contributions will be reviewed by the project team and community and may need some modifications to be accepted.
- The contribution should not break API or ABI, unless there is a real justification for that. If there is an API change, the contribution, if accepted, will be merged only when there will be a major release.
Contributor License Agreement (CLA)
-----------------------------------
- All contributions, whether large or small, require a Contributor's License Agreement (CLA) to be accepted. This is because source code can possibly fall under copyright law and we need your consent to share in the ownership of the copyright.
- To accept the Contributors License Agreement (CLA), individual contributors can do this by creating an Mbed account and [accepting the online agreement here with a click through](https://developer.mbed.org/contributor_agreement/). Alternatively, for contributions from corporations, or those that do not wish to create an Mbed account, a slightly different agreement can be found [here](https://www.mbed.com/en/about-mbed/contributor-license-agreements/). This agreement should be signed and returned to Arm as described in the instructions given.
Coding Standards
----------------
- We would ask that contributions conform to [our coding standards](https://tls.mbed.org/kb/development/mbedtls-coding-standards), and that contributions are fully tested before submission, as mentioned in the [Tests](#tests) and [Continuous Integration](#continuous-integration-tests) sections.
- The code should be written in a clean and readable style.
- The code should be written in a portable generic way, that will benefit the whole community, and not only your own needs.
- The code should be secure, and will be reviewed from a security point of view as well.
Making a Contribution
---------------------
1. [Check for open issues](https://github.com/ARMmbed/mbedtls/issues) or [start a discussion](https://tls.mbed.org/discussions) around a feature idea or a bug.
1. Fork the [Mbed TLS repository on GitHub](https://github.com/ARMmbed/mbedtls) to start making your changes. As a general rule, you should use the ["development" branch](https://github.com/ARMmbed/mbedtls/tree/development) as a basis.
1. Write a test which shows that the bug was fixed or that the feature works as expected.
1. Send a pull request (PR) and work with us until it gets merged and published. Contributions may need some modifications, so a few rounds of review and fixing may be necessary. We will include your name in the ChangeLog :)
1. For quick merging, the contribution should be short, and concentrated on a single feature or topic. The larger the contribution is, the longer it would take to review it and merge it.
1. Mbed TLS is released under the Apache license, and as such, all the added files should include the Apache license header.
API/ABI Compatibility
---------------------
The project aims to minimise the impact on users upgrading to newer versions of the library and it should not be necessary for a user to make any changes to their own code to work with a newer version of the library. Unless the user has made an active decision to use newer features, a newer generation of the library or a change has been necessary due to a security issue or other significant software defect, no modifications to their own code should be necessary. To achieve this, API compatibility is maintained between different versions of Mbed TLS on the main development branch and in LTS (Long Term Support) branches.
To minimise such disruption to users, where a change to the interface is required, all changes to the ABI or API, even on the main development branch where new features are added, need to be justifiable by either being a significant enhancement, new feature or bug fix which is best resolved by an interface change.
Where changes to an existing interface are necessary, functions in the public interface which need to be changed, are marked as 'deprecated'. This is done with the preprocessor symbols `MBEDTLS_DEPRECATED_WARNING` and `MBEDTLS_DEPRECATED_REMOVED`. Then, a new function with a new name but similar if not identical behaviour to the original function containing the necessary changes should be created alongside the existing deprecated function.
When a build is made with the deprecation preprocessor symbols defined, a compiler warning will be generated to warn a user that the function will be removed at some point in the future, notifying users that they should change from the older deprecated function to the newer function at their own convenience.
Therefore, no changes are permitted to the definition of functions in the public interface which will change the API. Instead the interface can only be changed by its extension. As described above, if a function needs to be changed, a new function needs to be created alongside it, with a new name, and whatever change is necessary, such as a new parameter or the addition of a return value.
Periodically, the library will remove deprecated functions from the library which will be a breaking change in the API, but such changes will be made only in a planned, structured way that gives sufficient notice to users of the library.
Long Term Support Branches
--------------------------
Mbed TLS maintains several LTS (Long Term Support) branches, which are maintained continuously for a given period. The LTS branches are provided to allow users of the library to have a maintained, stable version of the library which contains only security fixes and fixes for other defects, without encountering additional features or API extensions which may introduce issues or change the code size or RAM usage, which can be significant considerations on some platforms. To allow users to take advantage of the LTS branches, these branches maintain backwards compatibility for both the public API and ABI.
When backporting to these branches please observe the following rules:
1. Any change to the library which changes the API or ABI cannot be backported.
2. All bug fixes that correct a defect that is also present in an LTS branch must be backported to that LTS branch. If a bug fix introduces a change to the API such as a new function, the fix should be reworked to avoid the API change. API changes without very strong justification are unlikely to be accepted.
3. If a contribution is a new feature or enhancement, no backporting is required. Exceptions to this may be additional test cases or quality improvements such as changes to build or test scripts.
It would be highly appreciated if contributions are backported to LTS branches in addition to the [development branch](https://github.com/ARMmbed/mbedtls/tree/development) by contributors.
Currently maintained LTS branches are:
1. [mbedtls-2.7](https://github.com/ARMmbed/mbedtls/tree/mbedtls-2.7)
1. [mbedtls-2.16](https://github.com/ARMmbed/mbedtls/tree/mbedtls-2.16)
Tests
-----
As mentioned, tests that show the correctness of the feature or bug fix should be added to the pull request, if no such tests exist.
Mbed TLS includes a comprehensive set of test suites in the `tests/` directory that are dynamically generated to produce the actual test source files (e.g. `test_suite_mpi.c`). These files are generated from a `function file` (e.g. `suites/test_suite_mpi.function`) and a `data file` (e.g. `suites/test_suite_mpi.data`). The function file contains the test functions. The data file contains the test cases, specified as parameters that will be passed to the test function.
[A Knowledge Base article describing how to add additional tests is available on the Mbed TLS website](https://tls.mbed.org/kb/development/test_suites).
A test script `tests/scripts/basic-build-test.sh` is available to show test coverage of the library. New code contributions should provide a similar level of code coverage to that which already exists for the library.
Sample applications, if needed, should be modified as well.
Continuous Integration Tests
----------------------------
Once a PR has been made, the Continuous Integration (CI) tests are triggered and run. You should follow the result of the CI tests, and fix failures.
It is advised to enable the [githooks scripts](https://github.com/ARMmbed/mbedtls/tree/development/tests/git-scripts) prior to pushing your changes, for catching some of the issues as early as possible.
Documentation
-------------
Mbed TLS is well documented, but if you think documentation is needed, speak out!
1. All interfaces should be documented through Doxygen. New APIs should introduce Doxygen documentation.
2. Complex parts in the code should include comments.
3. If needed, a Readme file is advised.
4. If a [Knowledge Base (KB)](https://tls.mbed.org/kb) article should be added, write this as a comment in the PR description.
5. A [ChangeLog](https://github.com/ARMmbed/mbedtls/blob/development/ChangeLog) entry should be added for this contribution.

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