Compare commits

..

31 Commits

Author SHA1 Message Date
c1403df74a (cmake) default TLS back to mbedtls on Windows Universal Platform 2020-05-08 09:31:53 -07:00
3912e22b28 give websocket_subprotocol test more time to establish a connection 2020-05-08 09:26:05 -07:00
c9d5b4a581 Moved fPIC option to the top of the CMakeLists (#197)
The fPIC option was not properly registered before
2020-05-08 08:00:51 -07:00
9f8643032d fix dumb compile error 2020-05-06 22:07:47 -07:00
0772ef7ef5 (cobra bots) add a --heartbeat_timeout option to specify when the bot should terminate because no events are received 2020-05-06 22:01:48 -07:00
c030a62c8b openSSLLockingCallback should be static 2020-05-06 16:57:53 -07:00
931530b101 only register the crypto lock callback if no-one has registered them before us 2020-05-06 16:49:04 -07:00
6c205b983e (openssl tls) when OpenSSL is older than 1.1, register the crypto locking callback to be thread safe. Should fix lots of CI failures 2020-05-06 16:26:30 -07:00
a65b334961 assert that the timeout is non zero in makeCancellationRequestWithTimeout 2020-05-06 15:53:27 -07:00
2de8aafcbc another windows build error in IXUdpSocket ... 2020-05-05 08:29:39 -07:00
f075f586e1 fix windows compile error with UdpSocket::recvfrom 2020-05-05 08:15:01 -07:00
93cb898989 fix compile error with UdpSocket::recvfrom 2020-05-05 08:03:04 -07:00
e4da62547b add reference to multiple projects using IXWebSocket 2020-05-05 07:52:02 -07:00
2b4c06e6d2 UdpSocket::recvfrom last argument does not have to be a uint32_t 2020-05-05 07:49:07 -07:00
7337ed34a6 Added asynchronous udp receive function (#193)
* Added asynchronous udp receive function

* Remove receive_async and added low level recv, which is non-blocking.

* Remove thread include

* Moved unix include to IXNetSystem.h
2020-05-05 07:47:41 -07:00
15355188d5 (http client) rework a bit PATCH pull request, fix compile error with setForceBody and initialize _forceBody to false 2020-05-05 07:43:55 -07:00
8760c87635 add PATCH and add option to enforce a http request body write (#195)
* add PATCH and add option to enforce a http request body write

* remove private bool prop
2020-05-05 07:38:55 -07:00
2786631e3b clang-format 2020-05-04 17:19:25 -07:00
1b30061a4d remove unused variable 2020-05-04 17:18:21 -07:00
af003fc79b (ixbots) fix tsan data race error when accessing verbose parameter 2020-05-04 17:15:35 -07:00
4f17cd5e74 (cobra bots) do not use a queue to store messages pending processing, let the bot handle queuing 2020-05-04 15:45:11 -07:00
b04764489c (doc) add link to a project using ixwebsocket #187 2020-05-04 09:21:39 -07:00
fc4a4bfb7c fix #194 / linux needs to built with position independant code 2020-05-03 12:19:58 -07:00
9e54fd5f1a Fix CMake/zlibstatic-related regression (#192)
* cmake: add export() and install(EXPORT) for easier packageability

Enable the package to be more readily packageable as a system-wide
install or as a third-party dependency to another CMake-base project

This does not change CMake version requirements AFAICT

* CMake: link-in OpenSSL::Crypto

* CMake: explicitly manage dependencies. Fixes building with zlibstatic
2020-05-02 22:08:58 -07:00
1096f62196 cmake: add export() and install(EXPORT) for easier packageability (#190)
* cmake: add export() and install(EXPORT) for easier packageability

Enable the package to be more readily packageable as a system-wide
install or as a third-party dependency to another CMake-base project

This does not change CMake version requirements AFAICT

* CMake: link-in OpenSSL::Crypto
2020-05-02 20:20:59 -07:00
b34d9f6a06 uwp fixes 2020-05-01 11:27:59 -07:00
b21e2506bf (ci) add universal windows platform 2020-05-01 10:44:10 -07:00
303f99a432 refine previous commit 2020-05-01 10:37:25 -07:00
a42ccea8dd disable unicode 2020-05-01 09:22:03 -07:00
beb26bc096 use ninja for local builds 2020-04-29 11:53:56 -07:00
b45980f0f6 (http client) better current request cancellation support when the HttpClient destructor is invoked (see #189) 2020-04-29 11:53:23 -07:00
43 changed files with 301 additions and 2149 deletions

View File

@ -9,8 +9,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: make test
run: make test
- name: make test_make
run: make test_make
mac_tsan_sectransport:
runs-on: macOS-latest
@ -19,6 +19,15 @@ jobs:
- name: make test_tsan
run: make test_tsan
mac_tsan_openssl:
runs-on: macOS-latest
steps:
- uses: actions/checkout@v1
- name: install openssl
run: brew install openssl@1.1
- name: make test
run: make test_tsan_openssl
mac_tsan_mbedtls:
runs-on: macOS-latest
steps:
@ -28,7 +37,7 @@ jobs:
- name: make test
run: make test_tsan_mbedtls
windows_openssl:
windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v1
@ -43,6 +52,17 @@ jobs:
#- 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

@ -12,6 +12,10 @@ set (CMAKE_CXX_STANDARD 14)
set (CXX_STANDARD_REQUIRED ON)
set (CMAKE_CXX_EXTENSIONS OFF)
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
endif()
if (UNIX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic")
endif()
@ -119,6 +123,11 @@ if (USE_TLS)
if (NOT USE_MBED_TLS AND NOT USE_OPEN_SSL) # unless we want something else
set(USE_SECURE_TRANSPORT ON)
endif()
# default to mbedtls on uwp (universal windows platform) if nothing is configured
elseif (${CMAKE_SYSTEM_NAME} MATCHES "WindowsStore")
if (NOT USE_OPEN_SSL) # unless we want something else
set(USE_MBED_TLS ON)
endif()
else() # default to OpenSSL on all other platforms
if (NOT USE_MBED_TLS) # Unless mbedtls is requested
set(USE_OPEN_SSL ON)
@ -144,6 +153,8 @@ add_library( ixwebsocket STATIC
${IXWEBSOCKET_HEADERS}
)
add_library ( ixwebsocket::ixwebsocket ALIAS ixwebsocket )
if (USE_TLS)
target_compile_definitions(ixwebsocket PUBLIC IXWEBSOCKET_USE_TLS)
if (USE_MBED_TLS)
@ -165,26 +176,28 @@ if (USE_TLS)
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)
# This is for MacPort OpenSSL 1.0
# set(CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} /opt/local/lib/openssl-1.0)
# set(CMAKE_INCLUDE_PATH ${CMAKE_INCLUDE_PATH} /opt/local/include/openssl-1.0)
endif()
# This OPENSSL_FOUND check is to help find a cmake manually configured OpenSSL
if (NOT OPENSSL_FOUND)
find_package(OpenSSL REQUIRED)
include(FindOpenSSL)
endif()
message(STATUS "OpenSSL: " ${OPENSSL_VERSION})
add_definitions(${OPENSSL_DEFINITIONS})
target_include_directories(ixwebsocket PUBLIC ${OPENSSL_INCLUDE_DIR})
target_link_libraries(ixwebsocket ${OPENSSL_LIBRARIES})
target_link_libraries(ixwebsocket PUBLIC OpenSSL::SSL OpenSSL::Crypto)
elseif (USE_MBED_TLS)
message(STATUS "TLS configured to use mbedtls")
find_package(MbedTLS REQUIRED)
target_include_directories(ixwebsocket PUBLIC ${MBEDTLS_INCLUDE_DIRS})
target_link_libraries(ixwebsocket ${MBEDTLS_LIBRARIES})
target_link_libraries(ixwebsocket PUBLIC ${MBEDTLS_LIBRARIES})
elseif (USE_SECURE_TRANSPORT)
message(STATUS "TLS configured to use secure transport")
target_link_libraries(ixwebsocket "-framework foundation" "-framework security")
target_link_libraries(ixwebsocket PUBLIC "-framework foundation" "-framework security")
endif()
endif()
@ -194,25 +207,25 @@ if (NOT ZLIB_FOUND)
endif()
if (ZLIB_FOUND)
include_directories(${ZLIB_INCLUDE_DIRS})
target_link_libraries(ixwebsocket ${ZLIB_LIBRARIES})
target_link_libraries(ixwebsocket PUBLIC ${ZLIB_LIBRARIES})
else()
include_directories(third_party/zlib ${CMAKE_CURRENT_BINARY_DIR}/third_party/zlib)
add_subdirectory(third_party/zlib)
target_link_libraries(ixwebsocket zlibstatic)
add_subdirectory(third_party/zlib EXCLUDE_FROM_ALL)
target_link_libraries(ixwebsocket PRIVATE $<LINK_ONLY:zlibstatic>)
endif()
if (WIN32)
target_link_libraries(ixwebsocket wsock32 ws2_32 shlwapi)
target_link_libraries(ixwebsocket PUBLIC wsock32 ws2_32 shlwapi)
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
if (USE_TLS)
target_link_libraries(ixwebsocket Crypt32)
target_link_libraries(ixwebsocket PUBLIC Crypt32)
endif()
endif()
if (UNIX)
find_package(Threads)
target_link_libraries(ixwebsocket ${CMAKE_THREAD_LIBS_INIT})
target_link_libraries(ixwebsocket PUBLIC ${CMAKE_THREAD_LIBS_INIT})
endif()
@ -225,15 +238,18 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
target_compile_options(ixwebsocket PRIVATE /MP)
endif()
target_include_directories(ixwebsocket PUBLIC ${IXWEBSOCKET_INCLUDE_DIRS})
target_include_directories(ixwebsocket PUBLIC $<BUILD_INTERFACE:${IXWEBSOCKET_INCLUDE_DIRS}> $<INSTALL_INTERFACE:include/ixwebsocket>)
set_target_properties(ixwebsocket PROPERTIES PUBLIC_HEADER "${IXWEBSOCKET_HEADERS}")
install(TARGETS ixwebsocket
ARCHIVE DESTINATION ${CMAKE_INSTALL_PREFIX}/lib
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_PREFIX}/include/ixwebsocket/
install(TARGETS ixwebsocket EXPORT ixwebsocket
ARCHIVE DESTINATION ${CMAKE_INSTALL_PREFIX}/lib
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_PREFIX}/include/ixwebsocket/
)
install(EXPORT ixwebsocket NAMESPACE ixwebsocket:: DESTINATION lib/cmake/ixwebsocket)
export(EXPORT ixwebsocket NAMESPACE ixwebsocket:: FILE ixwebsocketConfig.cmake)
if (USE_WS OR USE_TEST)
add_subdirectory(ixcore)
add_subdirectory(ixcrypto)
@ -251,7 +267,3 @@ if (USE_WS OR USE_TEST)
add_subdirectory(test)
endif()
endif()
if (USE_LUAROCKS)
add_subdirectory(luarocks)
endif()

View File

@ -45,3 +45,6 @@ IXWebSocket client code is autobahn compliant beginning with the 6.0.0 version.
If your company or project is using this library, feel free to open an issue or PR to amend this list.
- [Machine Zone](https://www.mz.com)
- [dis-light](https://gitlab.com/HCInk/dis-light), a discord library with a node frontend.
- [libDiscordBot](https://github.com/tostc/libDiscordBot/tree/master), a work in progress discord library
- [gwebsocket](https://github.com/norrbotten/gwebsocket), a websocket (lua) module for Garry's Mod

View File

@ -1,6 +1,26 @@
# Changelog
All changes to this project will be documented in this file.
## [9.5.7] - 2020-05-08
(cmake) default TLS back to mbedtls on Windows Universal Platform
## [9.5.6] - 2020-05-06
(cobra bots) add a --heartbeat_timeout option to specify when the bot should terminate because no events are received
## [9.5.5] - 2020-05-06
(openssl tls) when OpenSSL is older than 1.1, register the crypto locking callback to be thread safe. Should fix lots of CI failures
## [9.5.4] - 2020-05-04
(cobra bots) do not use a queue to store messages pending processing, let the bot handle queuing
## [9.5.3] - 2020-04-29
(http client) better current request cancellation support when the HttpClient destructor is invoked (see #189)
## [9.5.2] - 2020-04-27
(cmake) fix cmake broken tls option parsing

View File

@ -8,7 +8,6 @@ set (IXBOTS_SOURCES
ixbots/IXCobraToSentryBot.cpp
ixbots/IXCobraToStatsdBot.cpp
ixbots/IXCobraToStdoutBot.cpp
ixbots/IXQueueManager.cpp
ixbots/IXStatsdClient.cpp
)
@ -17,7 +16,6 @@ set (IXBOTS_HEADERS
ixbots/IXCobraToSentryBot.h
ixbots/IXCobraToStatsdBot.h
ixbots/IXCobraToStdoutBot.h
ixbots/IXQueueManager.h
ixbots/IXStatsdClient.h
)

View File

@ -6,7 +6,6 @@
#include "IXCobraBot.h"
#include "IXQueueManager.h"
#include <ixcobra/IXCobraConnection.h>
#include <ixcore/utils/IXCoreLogger.h>
@ -22,17 +21,14 @@ namespace ix
const std::string& channel,
const std::string& filter,
const std::string& position,
bool verbose,
size_t maxQueueSize,
bool useQueue,
bool enableHeartbeat,
int heartBeatTimeout,
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);
@ -43,8 +39,6 @@ namespace ix
std::atomic<bool> throttled(false);
std::atomic<bool> fatalCobraError(false);
QueueManager queueManager(maxQueueSize);
auto timer = [&sentCount,
&receivedCount,
&sentCountTotal,
@ -85,7 +79,7 @@ namespace ix
std::thread t1(timer);
auto heartbeat = [&sentCount, &receivedCount, &stop, &enableHeartbeat] {
auto heartbeat = [&sentCount, &receivedCount, &stop, &enableHeartbeat, &heartBeatTimeout, &fatalCobraError] {
std::string state("na");
if (!enableHeartbeat) return;
@ -101,11 +95,12 @@ namespace ix
if (currentState == state)
{
CoreLogger::error("no messages received or sent for 1 minute, exiting");
exit(1);
fatalCobraError = true;
break;
}
state = currentState;
auto duration = std::chrono::minutes(1);
auto duration = std::chrono::seconds(heartBeatTimeout);
std::this_thread::sleep_for(duration);
}
@ -114,40 +109,6 @@ namespace ix
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,
@ -155,13 +116,9 @@ namespace ix
&channel,
&filter,
&subscriptionPosition,
&jsonWriter,
verbose,
&throttled,
&receivedCount,
&fatalCobraError,
&useQueue,
&queueManager,
&sentCount](const CobraEventPtr& event) {
if (event->type == ix::CobraEventType::Open)
{
@ -186,21 +143,11 @@ namespace ix
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
@ -211,28 +158,9 @@ namespace ix
++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");
}
}
_onBotMessageCallback(
msg, position, throttled,
fatalCobraError, sentCount);
});
}
else if (event->type == ix::CobraEventType::Subscribed)
@ -308,9 +236,6 @@ namespace ix
// heartbeat thread
if (t2.joinable()) t2.join();
// sentry sender thread
t3.join();
return fatalCobraError ? -1 : (int64_t) sentCount;
}

View File

@ -14,11 +14,11 @@
namespace ix
{
using OnBotMessageCallback = std::function<bool(const Json::Value&,
using OnBotMessageCallback = std::function<void(const Json::Value&,
const std::string&,
const bool verbose,
std::atomic<bool>&,
std::atomic<bool>&)>;
std::atomic<bool>&,
std::atomic<uint64_t>&)>;
class CobraBot
{
@ -29,10 +29,8 @@ namespace ix
const std::string& channel,
const std::string& filter,
const std::string& position,
bool verbose,
size_t maxQueueSize,
bool useQueue,
bool enableHeartbeat,
int heartBeatTimeout,
int runtime);
void setOnBotMessageCallback(const OnBotMessageCallback& callback);

View File

@ -7,7 +7,6 @@
#include "IXCobraToSentryBot.h"
#include "IXCobraBot.h"
#include "IXQueueManager.h"
#include <ixcobra/IXCobraConnection.h>
#include <ixcore/utils/IXCoreLogger.h>
@ -23,95 +22,67 @@ namespace ix
const std::string& position,
SentryClient& sentryClient,
bool verbose,
size_t maxQueueSize,
bool enableHeartbeat,
int heartBeatTimeout,
int runtime)
{
CobraBot bot;
bot.setOnBotMessageCallback([&sentryClient](const Json::Value& msg,
bot.setOnBotMessageCallback([&sentryClient, &verbose](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;
if (!response)
{
CoreLogger::warn("Null HTTP Response");
return false;
}
if (verbose)
{
for (auto it : response->headers)
std::atomic<bool>& /*fatalCobraError*/,
std::atomic<uint64_t>& sentCount) -> void {
sentryClient.send(msg, verbose,
[&sentCount, &throttled](const HttpResponsePtr& response) {
if (!response)
{
CoreLogger::info(it.first + ": " + it.second);
CoreLogger::warn("Null HTTP Response");
return;
}
CoreLogger::info("Upload size: " + std::to_string(response->uploadSize));
CoreLogger::info("Download size: " + std::to_string(response->downloadSize));
CoreLogger::info("Status: " + std::to_string(response->statusCode));
if (response->errorCode != HttpErrorCode::Ok)
if (response->statusCode == 200)
{
CoreLogger::info("error message: " + response->errorMsg);
sentCount++;
}
if (response->headers["Content-Type"] != "application/octet-stream")
else
{
CoreLogger::info("payload: " + response->payload);
}
}
CoreLogger::error("Error sending data to sentry: " + std::to_string(response->statusCode));
CoreLogger::error("Response: " + response->payload);
bool success = response->statusCode == 200;
if (!success)
{
CoreLogger::error("Error sending data to sentry: " + std::to_string(response->statusCode));
CoreLogger::error("Body: " + ret.second);
CoreLogger::error("Response: " + response->payload);
// Error 429 Too Many Requests
if (response->statusCode == 429)
{
auto retryAfter = response->headers["Retry-After"];
std::stringstream ss;
ss << retryAfter;
int seconds;
ss >> seconds;
if (!ss.eof() || ss.fail())
// Error 429 Too Many Requests
if (response->statusCode == 429)
{
seconds = 30;
CoreLogger::warn("Error parsing Retry-After header. "
"Using " + retryAfter + " for the sleep duration");
auto retryAfter = response->headers["Retry-After"];
std::stringstream ss;
ss << retryAfter;
int seconds;
ss >> seconds;
if (!ss.eof() || ss.fail())
{
seconds = 30;
CoreLogger::warn("Error parsing Retry-After header. "
"Using " + retryAfter + " for the sleep duration");
}
CoreLogger::warn("Error 429 - Too Many Requests. ws will sleep "
"and retry after " + retryAfter + " seconds");
throttled = true;
auto duration = std::chrono::seconds(seconds);
std::this_thread::sleep_for(duration);
throttled = false;
}
CoreLogger::warn("Error 429 - Too Many Requests. ws will sleep "
"and retry after " + retryAfter + " seconds");
throttled = true;
auto duration = std::chrono::seconds(seconds);
std::this_thread::sleep_for(duration);
throttled = false;
}
}
return success;
});
});
bool useQueue = true;
return bot.run(config,
channel,
filter,
position,
verbose,
maxQueueSize,
useQueue,
enableHeartbeat,
heartBeatTimeout,
runtime);
}
} // namespace ix

View File

@ -18,7 +18,7 @@ namespace ix
const std::string& position,
SentryClient& sentryClient,
bool verbose,
size_t maxQueueSize,
bool enableHeartbeat,
int heartBeatTimeout,
int runtime);
} // namespace ix

View File

@ -7,7 +7,6 @@
#include "IXCobraToStatsdBot.h"
#include "IXCobraBot.h"
#include "IXQueueManager.h"
#include "IXStatsdClient.h"
#include <chrono>
#include <ixcobra/IXCobraConnection.h>
@ -63,8 +62,8 @@ namespace ix
const std::string& gauge,
const std::string& timer,
bool verbose,
size_t maxQueueSize,
bool enableHeartbeat,
int heartBeatTimeout,
int runtime)
{
ix::CobraConnection conn;
@ -75,11 +74,11 @@ namespace ix
CobraBot bot;
bot.setOnBotMessageCallback(
[&statsdClient, &tokens, &gauge, &timer](const Json::Value& msg,
[&statsdClient, &tokens, &gauge, &timer, &verbose](const Json::Value& msg,
const std::string& /*position*/,
const bool verbose,
std::atomic<bool>& /*throttled*/,
std::atomic<bool>& fatalCobraError) -> bool {
std::atomic<bool>& fatalCobraError,
std::atomic<uint64_t>& sentCount) -> void {
std::string id;
for (auto&& attr : tokens)
{
@ -122,7 +121,7 @@ namespace ix
{
CoreLogger::error("Gauge " + gauge + " is not a numeric type");
fatalCobraError = true;
return false;
return;
}
if (verbose)
@ -140,19 +139,15 @@ namespace ix
}
}
return true;
sentCount++;
});
bool useQueue = true;
return bot.run(config,
channel,
filter,
position,
verbose,
maxQueueSize,
useQueue,
enableHeartbeat,
heartBeatTimeout,
runtime);
}
} // namespace ix

View File

@ -22,7 +22,7 @@ namespace ix
const std::string& gauge,
const std::string& timer,
bool verbose,
size_t maxQueueSize,
bool enableHeartbeat,
int heartBeatTimeout,
int runtime);
} // namespace ix

View File

@ -7,7 +7,6 @@
#include "IXCobraToStdoutBot.h"
#include "IXCobraBot.h"
#include "IXQueueManager.h"
#include <chrono>
#include <iostream>
#include <sstream>
@ -70,9 +69,8 @@ namespace ix
const std::string& position,
bool fluentd,
bool quiet,
bool verbose,
size_t maxQueueSize,
bool enableHeartbeat,
int heartBeatTimeout,
int runtime)
{
CobraBot bot;
@ -81,27 +79,22 @@ namespace ix
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 {
std::atomic<bool>& /*fatalCobraError*/,
std::atomic<uint64_t>& sentCount) -> void {
if (!quiet)
{
writeToStdout(fluentd, jsonWriter, msg, position);
}
return true;
sentCount++;
});
bool useQueue = false;
return bot.run(config,
channel,
filter,
position,
verbose,
maxQueueSize,
useQueue,
enableHeartbeat,
heartBeatTimeout,
runtime);
}
} // namespace ix

View File

@ -18,8 +18,7 @@ namespace ix
const std::string& position,
bool fluentd,
bool quiet,
bool verbose,
size_t maxQueueSize,
bool enableHeartbeat,
int heartBeatTimeout,
int runtime);
} // namespace ix

View File

@ -1,67 +0,0 @@
/*
* IXQueueManager.cpp
* Author: Benjamin Sergeant
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
*/
#include "IXQueueManager.h"
#include <algorithm>
#include <vector>
namespace ix
{
std::pair<Json::Value, std::string> QueueManager::pop()
{
std::unique_lock<std::mutex> lock(_mutex);
if (_queues.empty())
{
Json::Value val;
return std::make_pair(val, std::string());
}
std::vector<std::string> games;
for (auto it : _queues)
{
games.push_back(it.first);
}
std::random_shuffle(games.begin(), games.end());
std::string game = games[0];
auto duration = std::chrono::seconds(1);
_condition.wait_for(lock, duration);
if (_queues[game].empty())
{
Json::Value val;
return std::make_pair(val, std::string());
}
auto msg = _queues[game].front();
_queues[game].pop();
return msg;
}
void QueueManager::add(const Json::Value& msg, const std::string& position)
{
std::unique_lock<std::mutex> lock(_mutex);
std::string game;
if (msg.isMember("device") && msg["device"].isMember("game"))
{
game = msg["device"]["game"].asString();
}
if (game.empty()) return;
// if the sending is not fast enough there is no point
// in queuing too many events.
if (_queues[game].size() < _maxQueueSize)
{
_queues[game].push(std::make_pair(msg, position));
_condition.notify_one();
}
}
} // namespace ix

View File

@ -1,35 +0,0 @@
/*
* IXQueueManager.h
* Author: Benjamin Sergeant
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include <condition_variable>
#include <json/json.h>
#include <map>
#include <mutex>
#include <queue>
#include <stddef.h>
namespace ix
{
class QueueManager
{
public:
QueueManager(size_t maxQueueSize)
: _maxQueueSize(maxQueueSize)
{
}
std::pair<Json::Value, std::string> pop();
void add(const Json::Value& msg, const std::string& position);
private:
std::map<std::string, std::queue<std::pair<Json::Value, std::string>>> _queues;
std::mutex _mutex;
std::condition_variable _condition;
size_t _maxQueueSize;
};
} // namespace ix

View File

@ -226,20 +226,23 @@ namespace ix
return _jsonWriter.write(payload);
}
std::pair<HttpResponsePtr, std::string> SentryClient::send(const Json::Value& msg, bool verbose)
void SentryClient::send(
const Json::Value& msg,
bool verbose,
const OnResponseCallback& onResponseCallback)
{
auto args = _httpClient->createRequest();
args->url = _url;
args->verb = HttpClient::kPost;
args->extraHeaders["X-Sentry-Auth"] = SentryClient::computeAuthHeader();
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->body = computePayload(msg);
std::string body = computePayload(msg);
HttpResponsePtr response = _httpClient->post(_url, body, args);
return std::make_pair(response, body);
_httpClient->performRequest(args, onResponseCallback);
}
// https://sentry.io/api/12345/minidump?sentry_key=abcdefgh");

View File

@ -21,12 +21,9 @@ namespace ix
SentryClient(const std::string& dsn);
~SentryClient() = default;
std::pair<HttpResponsePtr, std::string> send(const Json::Value& msg, bool verbose);
Json::Value parseLuaStackTrace(const std::string& stack);
// Mostly for testing
void setTLSOptions(const SocketTLSOptions& tlsOptions);
void send(const Json::Value& msg,
bool verbose,
const OnResponseCallback& onResponseCallback);
void uploadMinidump(const std::string& sentryMetadata,
const std::string& minidumpBytes,
@ -39,6 +36,12 @@ namespace ix
bool verbose,
const OnResponseCallback& onResponseCallback);
Json::Value parseLuaStackTrace(const std::string& stack);
// Mostly for testing
void setTLSOptions(const SocketTLSOptions& tlsOptions);
private:
int64_t getTimestamp();
std::string computeAuthHeader();

View File

@ -6,6 +6,7 @@
#include "IXCancellationRequest.h"
#include <cassert>
#include <chrono>
namespace ix
@ -13,6 +14,8 @@ namespace ix
CancellationRequest makeCancellationRequestWithTimeout(
int secs, std::atomic<bool>& requestInitCancellation)
{
assert(secs > 0);
auto start = std::chrono::system_clock::now();
auto timeout = std::chrono::seconds(secs);

View File

@ -4,6 +4,19 @@
* 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

@ -25,10 +25,12 @@ namespace ix
const std::string HttpClient::kHead = "HEAD";
const std::string HttpClient::kDel = "DEL";
const std::string HttpClient::kPut = "PUT";
const std::string HttpClient::kPatch = "PATCH";
HttpClient::HttpClient(bool async)
: _async(async)
, _stop(false)
, _forceBody(false)
{
if (!_async) return;
@ -49,6 +51,11 @@ namespace ix
_tlsOptions = tlsOptions;
}
void HttpClient::setForceBody(bool value)
{
_forceBody = value;
}
HttpRequestArgsPtr HttpClient::createRequest(const std::string& url, const std::string& verb)
{
auto request = std::make_shared<HttpRequestArgs>();
@ -192,7 +199,7 @@ namespace ix
ss << "User-Agent: " << userAgent() << "\r\n";
}
if (verb == kPost || verb == kPut)
if (verb == kPost || verb == kPut || verb == kPatch || _forceBody)
{
ss << "Content-Length: " << body.size() << "\r\n";
@ -220,11 +227,10 @@ 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, requestInitCancellation);
makeCancellationRequestWithTimeout(args->connectTimeout, _stop);
bool success = _socket->connect(host, port, errMsg, isCancellationRequested);
if (!success)
@ -242,8 +248,7 @@ namespace ix
}
// Make a new cancellation object dealing with transfer timeout
isCancellationRequested =
makeCancellationRequestWithTimeout(args->transferTimeout, requestInitCancellation);
isCancellationRequested = makeCancellationRequestWithTimeout(args->transferTimeout, _stop);
if (args->verbose)
{
@ -562,6 +567,20 @@ namespace ix
return request(url, kPut, body, args);
}
HttpResponsePtr HttpClient::patch(const std::string& url,
const HttpParameters& httpParameters,
HttpRequestArgsPtr args)
{
return request(url, kPatch, serializeHttpParameters(httpParameters), args);
}
HttpResponsePtr HttpClient::patch(const std::string& url,
const std::string& body,
const HttpRequestArgsPtr args)
{
return request(url, kPatch, body, args);
}
std::string HttpClient::urlEncode(const std::string& value)
{
std::ostringstream escaped;

View File

@ -46,12 +46,19 @@ namespace ix
const std::string& body,
HttpRequestArgsPtr args);
HttpResponsePtr patch(const std::string& url,
const HttpParameters& httpParameters,
HttpRequestArgsPtr args);
HttpResponsePtr patch(const std::string& url,
const std::string& body,
HttpRequestArgsPtr args);
HttpResponsePtr request(const std::string& url,
const std::string& verb,
const std::string& body,
HttpRequestArgsPtr args,
int redirects = 0);
void setForceBody(bool value);
// Async API
HttpRequestArgsPtr createRequest(const std::string& url = std::string(),
const std::string& verb = HttpClient::kGet);
@ -78,6 +85,7 @@ namespace ix
const static std::string kHead;
const static std::string kDel;
const static std::string kPut;
const static std::string kPatch;
private:
void log(const std::string& msg, HttpRequestArgsPtr args);
@ -86,7 +94,6 @@ namespace ix
// Async API background thread runner
void run();
// Async API
bool _async;
std::queue<std::pair<HttpRequestArgsPtr, OnResponseCallback>> _queue;
@ -99,5 +106,7 @@ namespace ix
std::mutex _mutex; // to protect accessing the _socket (only one socket per client)
SocketTLSOptions _tlsOptions;
bool _forceBody;
};
} // namespace ix

View File

@ -19,6 +19,7 @@ typedef unsigned long int nfds_t;
#else
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/ip.h>

View File

@ -85,6 +85,8 @@ namespace ix
std::atomic<bool> SocketOpenSSL::_openSSLInitializationSuccessful(false);
std::once_flag SocketOpenSSL::_openSSLInitFlag;
std::unique_ptr<std::mutex[]> SocketOpenSSL::_openSSLMutexes =
std::make_unique<std::mutex[]>(CRYPTO_num_locks());
SocketOpenSSL::SocketOpenSSL(const SocketTLSOptions& tlsOptions, int fd)
: Socket(fd)
@ -106,6 +108,11 @@ namespace ix
if (!OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, nullptr)) return;
#else
(void) OPENSSL_config(nullptr);
if (CRYPTO_get_locking_callback() != nullptr)
{
CRYPTO_set_locking_callback(SocketOpenSSL::openSSLLockingCallback);
}
#endif
(void) OpenSSL_add_ssl_algorithms();
@ -114,6 +121,21 @@ namespace ix
_openSSLInitializationSuccessful = true;
}
void SocketOpenSSL::openSSLLockingCallback(int mode,
int type,
const char* /*file*/,
int /*line*/)
{
if (mode & CRYPTO_LOCK)
{
_openSSLMutexes[type].lock();
}
else
{
_openSSLMutexes[type].unlock();
}
}
std::string SocketOpenSSL::getSSLError(int ret)
{
unsigned long e;

View File

@ -49,6 +49,9 @@ namespace ix
bool handleTLSOptions(std::string& errMsg);
bool openSSLServerHandshake(std::string& errMsg);
// Required for OpenSSL < 1.1
static void openSSLLockingCallback(int mode, int type, const char* /*file*/, int /*line*/);
SSL* _ssl_connection;
SSL_CTX* _ssl_context;
const SSL_METHOD* _ssl_method;
@ -58,6 +61,7 @@ namespace ix
static std::once_flag _openSSLInitFlag;
static std::atomic<bool> _openSSLInitializationSuccessful;
static std::unique_ptr<std::mutex[]> _openSSLMutexes;
};
} // namespace ix

View File

@ -44,6 +44,18 @@ namespace ix
return err;
}
bool UdpSocket::isWaitNeeded()
{
int err = getErrno();
if (err == EWOULDBLOCK || err == EAGAIN || err == EINPROGRESS)
{
return true;
}
return false;
}
void UdpSocket::closeSocket(int fd)
{
#ifdef _WIN32
@ -62,6 +74,13 @@ namespace ix
return false;
}
#ifdef _WIN32
unsigned long nonblocking = 1;
ioctlsocket(_sockfd, FIONBIO, &nonblocking);
#else
fcntl(_sockfd, F_SETFL, O_NONBLOCK); // make socket non blocking
#endif
memset(&_server, 0, sizeof(_server));
_server.sin_family = AF_INET;
_server.sin_port = htons(port);
@ -93,4 +112,15 @@ namespace ix
return (ssize_t)::sendto(
_sockfd, buffer.data(), buffer.size(), 0, (struct sockaddr*) &_server, sizeof(_server));
}
ssize_t UdpSocket::recvfrom(char* buffer, size_t length)
{
#ifdef _WIN32
int addressLen = (int) sizeof(_server);
#else
socklen_t addressLen = (socklen_t) sizeof(_server);
#endif
return (ssize_t)::recvfrom(
_sockfd, buffer, length, 0, (struct sockaddr*) &_server, &addressLen);
}
} // namespace ix

View File

@ -28,9 +28,12 @@ namespace ix
// Virtual methods
bool init(const std::string& host, int port, std::string& errMsg);
ssize_t sendto(const std::string& buffer);
ssize_t recvfrom(char* buffer, size_t length);
void close();
static int getErrno();
static bool isWaitNeeded();
static void closeSocket(int fd);
private:

View File

@ -6,4 +6,4 @@
#pragma once
#define IX_WEBSOCKET_VERSION "9.5.2"
#define IX_WEBSOCKET_VERSION "9.5.7"

View File

@ -1,30 +0,0 @@
#
# Author: Benjamin Sergeant
# Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
#
cmake_minimum_required (VERSION 3.4.1)
project (luarocks)
# There's -Weverything too for clang
if (NOT WIN32)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic")
endif()
set (CMAKE_CXX_STANDARD 14)
option(USE_TLS "Add TLS support" ON)
include_directories(luarocks .)
add_executable(luarocks main.cpp)
include(FindLua)
find_package(lua REQUIRED)
target_link_libraries(luarocks ${LUA_LIBRARIES})
target_include_directories(luarocks PRIVATE ${LUA_INCLUDE_DIR})
# library with the most dependencies come first
target_link_libraries(luarocks ixwebsocket)
install(TARGETS luarocks RUNTIME DESTINATION bin)

View File

@ -1,163 +0,0 @@
/*
* LuaWebSocket.h
* Author: Benjamin Sergeant
* Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include <iostream>
#include <string>
extern "C"
{
#include "lua.h"
#include "lauxlib.h"
}
#include "luawrapper.hpp"
#include <ixwebsocket/IXWebSocket.h>
#include <chrono>
#include <thread>
#include <queue>
#include <mutex>
namespace ix
{
class LuaWebSocket
{
public:
LuaWebSocket()
{
_webSocket.setOnMessageCallback([this](const WebSocketMessagePtr& msg) {
if (msg->type == ix::WebSocketMessageType::Message)
{
std::lock_guard<std::mutex> lock(_queueMutex);
_queue.push(msg->str);
}
});
}
void setUrl(const std::string& url) { _webSocket.setUrl(url); }
const std::string& getUrl() { return _webSocket.getUrl(); }
void start() { _webSocket.start(); }
void send(const std::string& msg) { _webSocket.send(msg); }
const std::string& getMessage()
{
std::lock_guard<std::mutex> lock(_queueMutex);
return _queue.front();
}
bool hasMessage()
{
std::lock_guard<std::mutex> lock(_queueMutex);
return !_queue.empty();
}
void popMessage()
{
std::lock_guard<std::mutex> lock(_queueMutex);
_queue.pop();
}
private:
WebSocket _webSocket;
std::queue<std::string> _queue;
std::mutex _queueMutex;
};
LuaWebSocket* WebSocket_new(lua_State* /*L*/)
{
LuaWebSocket* webSocket = new LuaWebSocket();
return webSocket;
}
int WebSocket_getUrl(lua_State* L)
{
LuaWebSocket* luaWebSocket = luaW_check<LuaWebSocket>(L, 1);
lua_pushstring(L, luaWebSocket->getUrl().c_str());
return 1;
}
int WebSocket_setUrl(lua_State* L)
{
LuaWebSocket* luaWebSocket = luaW_check<LuaWebSocket>(L, 1);
const char* url = luaL_checkstring(L, 2);
luaWebSocket->setUrl(url);
return 0;
}
int WebSocket_start(lua_State* L)
{
LuaWebSocket* luaWebSocket = luaW_check<LuaWebSocket>(L, 1);
luaWebSocket->start();
return 0;
}
int WebSocket_send(lua_State* L)
{
LuaWebSocket* luaWebSocket = luaW_check<LuaWebSocket>(L, 1);
const char* msg = luaL_checkstring(L, 2);
luaWebSocket->send(msg);
return 0;
}
int WebSocket_getMessage(lua_State* L)
{
LuaWebSocket* luaWebSocket = luaW_check<LuaWebSocket>(L, 1);
lua_pushstring(L, luaWebSocket->getMessage().c_str());
return 1;
}
int WebSocket_hasMessage(lua_State* L)
{
LuaWebSocket* luaWebSocket = luaW_check<LuaWebSocket>(L, 1);
lua_pushboolean(L, luaWebSocket->hasMessage());
return 1;
}
int WebSocket_popMessage(lua_State* L)
{
LuaWebSocket* luaWebSocket = luaW_check<LuaWebSocket>(L, 1);
luaWebSocket->popMessage();
return 1;
}
// FIXME: This should be a static method, or be part of a different module
int WebSocket_sleep(lua_State* L)
{
// LuaWebSocket* luaWebSocket = luaW_check<LuaWebSocket>(L, 1);
auto duration = luaL_checkinteger(L, 2);
std::this_thread::sleep_for(std::chrono::milliseconds(duration));
return 0;
}
static luaL_Reg WebSocket_table[] = {
{ NULL, NULL }
};
static luaL_Reg WebSocket_metatable[] = {
{ "getUrl", WebSocket_getUrl },
{ "setUrl", WebSocket_setUrl },
{ "start", WebSocket_start },
{ "send", WebSocket_send },
{ "getMessage", WebSocket_getMessage },
{ "hasMessage", WebSocket_hasMessage },
{ "popMessage", WebSocket_popMessage },
{ "sleep", WebSocket_sleep },
{ NULL, NULL }
};
int luaopen_WebSocket(lua_State* L)
{
luaW_register<LuaWebSocket>(L,
"WebSocket",
WebSocket_table,
WebSocket_metatable,
WebSocket_new
);
return 1;
}
}

View File

@ -1,56 +0,0 @@
#ifndef PLAYER_HPP
#define PLAYER_HPP
#include <iostream>
class Player
{
public:
Player(const char* name, unsigned int health) :
m_Name(name),
m_Health(health)
{
}
void info()
{
std::cout << m_Name << " have " << m_Health << " HP" << std::endl;
}
void say(const char* text)
{
std::cout << m_Name << ": " << text << std::endl;
}
void heal(Player* target)
{
target->setHealth(100);
}
const char* getName()
{
return m_Name;
}
unsigned int getHealth()
{
return m_Health;
}
bool setHealth(unsigned int health)
{
if (health >= 0 && health <= 100)
{
m_Health = health;
return true;
}
else
return false;
}
private:
const char* m_Name;
unsigned int m_Health;
};
#endif // PLAYER_HPP

View File

@ -1,7 +0,0 @@
Wrapper based on https://github.com/LuaxY/cpp-lua
Examples to build C++
https://github.com/siffiejoe/lua-fltk4lua/blob/master/fltk4lua-scm-0.rockspec
https://github.com/lua4web/refser/blob/master/rockspecs/refser-0.2-1.rockspec

View File

@ -1,30 +0,0 @@
--
-- make luarocks && (cd luarocks ; ../build/luarocks/luarocks)
--
-- ... git push test with ssh key
--
local webSocket = WebSocket.new()
webSocket:setUrl("ws://localhost:8008")
print("Url: " .. webSocket:getUrl())
-- Start the background thread
webSocket:start()
local i = 0
while true
do
print("Sending message...")
webSocket:send("msg_" .. tostring(i));
i = i + 1
print("Waiting 1s...")
webSocket:sleep(1000)
if webSocket:hasMessage() then
local msg = webSocket:getMessage()
print("Received: " .. msg)
webSocket:popMessage()
end
end

View File

@ -1,15 +0,0 @@
/*
* functions.hpp
* Author: Benjamin Sergeant
* Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include <iostream>
int lua_info(lua_State* /*L*/)
{
std::cout << "C++ Lua v0.1" << std::endl << std::endl;
return 0;
}

View File

@ -1,709 +0,0 @@
/*
* Copyright (c) 2010-2013 Alexander Ames
* Alexander.Ames@gmail.com
* See Copyright Notice at the end of this file
*/
// API Summary:
//
// LuaWrapper is a library designed to help bridge the gab between Lua and
// C++. It is designed to be small (a single header file), simple, fast,
// and typesafe. It has no external dependencies, and does not need to be
// precompiled; the header can simply be dropped into a project and used
// immediately. It even supports class inheritance to a certain degree. Objects
// can be created in either Lua or C++, and passed back and forth.
//
// The main functions of interest are the following:
// luaW_is<T>
// luaW_to<T>
// luaW_check<T>
// luaW_push<T>
// luaW_register<T>
// luaW_setfuncs<T>
// luaW_extend<T, U>
// luaW_hold<T>
// luaW_release<T>
//
// These functions allow you to manipulate arbitrary classes just like you
// would the primitive types (e.g. numbers or strings). If you are familiar
// with the normal Lua API the behavior of these functions should be very
// intuative.
//
// For more information see the README and the comments below
#ifndef LUA_WRAPPER_H_
#define LUA_WRAPPER_H_
// If you are linking against Lua compiled in C++, define LUAW_NO_EXTERN_C
#ifndef LUAW_NO_EXTERN_C
extern "C"
{
#endif // LUAW_NO_EXTERN_C
#include "lua.h"
#include "lauxlib.h"
#ifndef LUAW_NO_EXTERN_C
}
#endif // LUAW_NO_EXTERN_C
#define LUAW_POSTCTOR_KEY "__postctor"
#define LUAW_EXTENDS_KEY "__extends"
#define LUAW_STORAGE_KEY "storage"
#define LUAW_CACHE_KEY "cache"
#define LUAW_CACHE_METATABLE_KEY "cachemetatable"
#define LUAW_HOLDS_KEY "holds"
#define LUAW_WRAPPER_KEY "LuaWrapper"
// A simple utility function to adjust a given index
// Useful for when a parameter index needs to be adjusted
// after pushing or popping things off the stack
inline int luaW_correctindex(lua_State* /*L*/, int index, int correction)
{
return index < 0 ? index - correction : index;
}
// These are the default allocator and deallocator. If you would prefer an
// alternative option, you may select a different function when registering
// your class.
template <typename T>
T* luaW_defaultallocator(lua_State*)
{
return new T();
}
template <typename T>
void luaW_defaultdeallocator(lua_State*, T* obj)
{
delete obj;
}
// The identifier function is responsible for pushing a value unique to each
// object on to the stack. Most of the time, this can simply be the address
// of the pointer, but sometimes that is not adaquate. For example, if you
// are using shared_ptr you would need to push the address of the object the
// shared_ptr represents, rather than the address of the shared_ptr itself.
template <typename T>
void luaW_defaultidentifier(lua_State* L, T* obj)
{
lua_pushlightuserdata(L, obj);
}
// This class is what is used by LuaWrapper to contain the userdata. data
// stores a pointer to the object itself, and cast is used to cast toward the
// base class if there is one and it is necessary. Rather than use RTTI and
// typid to compare types, I use the clever trick of using the cast to compare
// types. Because there is at most one cast per type, I can use it to identify
// when and object is the type I want. This is only used internally.
struct luaW_Userdata
{
luaW_Userdata(void* vptr = NULL, luaW_Userdata (*udcast)(const luaW_Userdata&) = NULL)
: data(vptr), cast(udcast) {}
void* data;
luaW_Userdata (*cast)(const luaW_Userdata&);
};
// This class cannot actually to be instantiated. It is used only hold the
// table name and other information.
template <typename T>
class LuaWrapper
{
public:
static const char* classname;
static void (*identifier)(lua_State*, T*);
static T* (*allocator)(lua_State*);
static void (*deallocator)(lua_State*, T*);
static luaW_Userdata (*cast)(const luaW_Userdata&);
private:
LuaWrapper();
};
template <typename T> const char* LuaWrapper<T>::classname;
template <typename T> void (*LuaWrapper<T>::identifier)(lua_State*, T*);
template <typename T> T* (*LuaWrapper<T>::allocator)(lua_State*);
template <typename T> void (*LuaWrapper<T>::deallocator)(lua_State*, T*);
template <typename T> luaW_Userdata (*LuaWrapper<T>::cast)(const luaW_Userdata&);
// Cast from an object of type T to an object of type U. This template
// function is instantiated by calling luaW_extend<T, U>(L). This is only used
// internally.
template <typename T, typename U>
luaW_Userdata luaW_cast(const luaW_Userdata& obj)
{
return luaW_Userdata(static_cast<U*>(static_cast<T*>(obj.data)), LuaWrapper<U>::cast);
}
template <typename T, typename U>
void luaW_identify(lua_State* L, T* obj)
{
LuaWrapper<U>::identifier(L, static_cast<U*>(obj));
}
template <typename T>
inline void luaW_wrapperfield(lua_State* L, const char* field)
{
lua_getfield(L, LUA_REGISTRYINDEX, LUAW_WRAPPER_KEY); // ... LuaWrapper
lua_getfield(L, -1, field); // ... LuaWrapper LuaWrapper.field
lua_getfield(L, -1, LuaWrapper<T>::classname); // ... LuaWrapper LuaWrapper.field LuaWrapper.field.class
lua_replace(L, -3); // ... LuaWrapper.field.class LuaWrapper.field
lua_pop(L, 1); // ... LuaWrapper.field.class
}
// Analogous to lua_is(boolean|string|*)
//
// Returns 1 if the value at the given acceptable index is of type T (or if
// strict is false, convertable to type T) and 0 otherwise.
template <typename T>
bool luaW_is(lua_State *L, int index, bool strict = false)
{
bool equal = false;// lua_isnil(L, index);
if (!equal && lua_isuserdata(L, index) && lua_getmetatable(L, index))
{
// ... ud ... udmt
luaL_getmetatable(L, LuaWrapper<T>::classname); // ... ud ... udmt Tmt
equal = lua_rawequal(L, -1, -2) != 0;
if (!equal && !strict)
{
lua_getfield(L, -2, LUAW_EXTENDS_KEY); // ... ud ... udmt Tmt udmt.extends
for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1))
{
// ... ud ... udmt Tmt udmt.extends k v
equal = lua_rawequal(L, -1, -4) != 0;
if (equal)
{
lua_pop(L, 2); // ... ud ... udmt Tmt udmt.extends
break;
}
}
lua_pop(L, 1); // ... ud ... udmt Tmt
}
lua_pop(L, 2); // ... ud ...
}
return equal;
}
// Analogous to lua_to(boolean|string|*)
//
// Converts the given acceptable index to a T*. That value must be of (or
// convertable to) type T; otherwise, returns NULL.
template <typename T>
T* luaW_to(lua_State* L, int index, bool strict = false)
{
if (luaW_is<T>(L, index, strict))
{
luaW_Userdata* pud = static_cast<luaW_Userdata*>(lua_touserdata(L, index));
luaW_Userdata ud;
while (!strict && LuaWrapper<T>::cast != pud->cast)
{
ud = pud->cast(*pud);
pud = &ud;
}
return static_cast<T*>(pud->data);
}
return NULL;
}
// Analogous to luaL_check(boolean|string|*)
//
// Converts the given acceptable index to a T*. That value must be of (or
// convertable to) type T; otherwise, an error is raised.
template <typename T>
T* luaW_check(lua_State* L, int index, bool strict = false)
{
T* obj = NULL;
if (luaW_is<T>(L, index, strict))
{
luaW_Userdata* pud = (luaW_Userdata*)lua_touserdata(L, index);
luaW_Userdata ud;
while (!strict && LuaWrapper<T>::cast != pud->cast)
{
ud = pud->cast(*pud);
pud = &ud;
}
obj = (T*)pud->data;
}
else
{
const char *msg = lua_pushfstring(L, "%s expected, got %s", LuaWrapper<T>::classname, luaL_typename(L, index));
luaL_argerror(L, index, msg);
}
return obj;
}
template <typename T>
T* luaW_opt(lua_State* L, int index, T* fallback = NULL, bool strict = false)
{
if (lua_isnil(L, index))
return fallback;
else
return luaW_check<T>(L, index, strict);
}
// Analogous to lua_push(boolean|string|*)
//
// Pushes a userdata of type T onto the stack. If this object already exists in
// the Lua environment, it will assign the existing storage table to it.
// Otherwise, a new storage table will be created for it.
template <typename T>
void luaW_push(lua_State* L, T* obj)
{
if (obj)
{
LuaWrapper<T>::identifier(L, obj); // ... id
luaW_wrapperfield<T>(L, LUAW_CACHE_KEY); // ... id cache
lua_pushvalue(L, -2); // ... id cache id
lua_gettable(L, -2); // ... id cache obj
if (lua_isnil(L, -1))
{
// Create the new luaW_userdata and place it in the cache
lua_pop(L, 1); // ... id cache
lua_insert(L, -2); // ... cache id
luaW_Userdata* ud = static_cast<luaW_Userdata*>(lua_newuserdata(L, sizeof(luaW_Userdata))); // ... cache id obj
ud->data = obj;
ud->cast = LuaWrapper<T>::cast;
lua_pushvalue(L, -1); // ... cache id obj obj
lua_insert(L, -4); // ... obj cache id obj
lua_settable(L, -3); // ... obj cache
luaL_getmetatable(L, LuaWrapper<T>::classname); // ... obj cache mt
lua_setmetatable(L, -3); // ... obj cache
lua_pop(L, 1); // ... obj
}
else
{
lua_replace(L, -3); // ... obj cache
lua_pop(L, 1); // ... obj
}
}
else
{
lua_pushnil(L);
}
}
// Instructs LuaWrapper that it owns the userdata, and can manage its memory.
// When all references to the object are removed, Lua is free to garbage
// collect it and delete the object.
//
// Returns true if luaW_hold took hold of the object, and false if it was
// already held
template <typename T>
bool luaW_hold(lua_State* L, T* obj)
{
luaW_wrapperfield<T>(L, LUAW_HOLDS_KEY); // ... holds
LuaWrapper<T>::identifier(L, obj); // ... holds id
lua_pushvalue(L, -1); // ... holds id id
lua_gettable(L, -3); // ... holds id hold
// If it's not held, hold it
if (!lua_toboolean(L, -1))
{
// Apply hold boolean
lua_pop(L, 1); // ... holds id
lua_pushboolean(L, true); // ... holds id true
lua_settable(L, -3); // ... holds
lua_pop(L, 1); // ...
return true;
}
lua_pop(L, 3); // ...
return false;
}
// Releases LuaWrapper's hold on an object. This allows the user to remove
// all references to an object in Lua and ensure that Lua will not attempt to
// garbage collect it.
//
// This function takes the index of the identifier for an object rather than
// the object itself. This is because needs to be able to run after the object
// has already been deallocated. A wrapper is provided for when it is more
// convenient to pass in the object directly.
template <typename T>
void luaW_release(lua_State* L, int index)
{
luaW_wrapperfield<T>(L, LUAW_HOLDS_KEY); // ... id ... holds
lua_pushvalue(L, luaW_correctindex(L, index, 1)); // ... id ... holds id
lua_pushnil(L); // ... id ... holds id nil
lua_settable(L, -3); // ... id ... holds
lua_pop(L, 1); // ... id ...
}
template <typename T>
void luaW_release(lua_State* L, T* obj)
{
LuaWrapper<T>::identifier(L, obj); // ... id
luaW_release<T>(L, -1); // ... id
lua_pop(L, 1); // ...
}
// This function is called from Lua, not C++
//
// Calls the lua post-constructor (LUAW_POSTCTOR_KEY or "__postctor") on a
// userdata. Assumes the userdata is on top of the stack, and numargs arguments
// are below it. This runs the LUAW_POSTCTOR_KEY function on T's metatable,
// using the object as the first argument and whatever else is below it as
// the rest of the arguments This exists to allow types to adjust values in
// thier storage table, which can not be created until after the constructor is
// called.
template <typename T>
void luaW_postconstructor(lua_State* L, int numargs)
{
// ... args... ud
lua_getfield(L, -1, LUAW_POSTCTOR_KEY); // ... args... ud ud.__postctor
if (lua_type(L, -1) == LUA_TFUNCTION)
{
lua_pushvalue(L, -2); // ... args... ud ud.__postctor ud
lua_insert(L, -3 - numargs); // ... ud args... ud ud.__postctor
lua_insert(L, -3 - numargs); // ... ud.__postctor ud args... ud
lua_insert(L, -3 - numargs); // ... ud ud.__postctor ud args...
lua_call(L, numargs + 1, 0); // ... ud
}
else
{
lua_pop(L, 1); // ... ud
}
}
// This function is generally called from Lua, not C++
//
// Creates an object of type T using the constructor and subsequently calls the
// post-constructor on it.
template <typename T>
inline int luaW_new(lua_State* L, int args)
{
T* obj = LuaWrapper<T>::allocator(L);
luaW_push<T>(L, obj);
luaW_hold<T>(L, obj);
luaW_postconstructor<T>(L, args);
return 1;
}
template <typename T>
int luaW_new(lua_State* L)
{
return luaW_new<T>(L, lua_gettop(L));
}
// This function is called from Lua, not C++
//
// The default metamethod to call when indexing into lua userdata representing
// an object of type T. This will first check the userdata's environment table
// and if it's not found there it will check the metatable. This is done so
// individual userdata can be treated as a table, and can hold thier own
// values.
template <typename T>
int luaW_index(lua_State* L)
{
// obj key
T* obj = luaW_to<T>(L, 1);
luaW_wrapperfield<T>(L, LUAW_STORAGE_KEY); // obj key storage
LuaWrapper<T>::identifier(L, obj); // obj key storage id
lua_gettable(L, -2); // obj key storage store
// Check if storage table exists
if (!lua_isnil(L, -1))
{
lua_pushvalue(L, -3); // obj key storage store key
lua_gettable(L, -2); // obj key storage store store[k]
}
// If either there is no storage table or the key wasn't found
// then fall back to the metatable
if (lua_isnil(L, -1))
{
lua_settop(L, 2); // obj key
lua_getmetatable(L, -2); // obj key mt
lua_pushvalue(L, -2); // obj key mt k
lua_gettable(L, -2); // obj key mt mt[k]
}
return 1;
}
// This function is called from Lua, not C++
//
// The default metamethod to call when creating a new index on lua userdata
// representing an object of type T. This will index into the the userdata's
// environment table that it keeps for personal storage. This is done so
// individual userdata can be treated as a table, and can hold thier own
// values.
template <typename T>
int luaW_newindex(lua_State* L)
{
// obj key value
T* obj = luaW_check<T>(L, 1);
luaW_wrapperfield<T>(L, LUAW_STORAGE_KEY); // obj key value storage
LuaWrapper<T>::identifier(L, obj); // obj key value storage id
lua_pushvalue(L, -1); // obj key value storage id id
lua_gettable(L, -3); // obj key value storage id store
// Add the storage table if there isn't one already
if (lua_isnil(L, -1))
{
lua_pop(L, 1); // obj key value storage id
lua_newtable(L); // obj key value storage id store
lua_pushvalue(L, -1); // obj key value storage id store store
lua_insert(L, -3); // obj key value storage store id store
lua_settable(L, -4); // obj key value storage store
}
lua_pushvalue(L, 2); // obj key value ... store key
lua_pushvalue(L, 3); // obj key value ... store key value
lua_settable(L, -3); // obj key value ... store
return 0;
}
// This function is called from Lua, not C++
//
// The __gc metamethod handles cleaning up userdata. The userdata's reference
// count is decremented and if this is the final reference to the userdata its
// environment table is nil'd and pointer deleted with the destructor callback.
template <typename T>
int luaW_gc(lua_State* L)
{
// obj
T* obj = luaW_to<T>(L, 1);
LuaWrapper<T>::identifier(L, obj); // obj key value storage id
luaW_wrapperfield<T>(L, LUAW_HOLDS_KEY); // obj id counts count holds
lua_pushvalue(L, 2); // obj id counts count holds id
lua_gettable(L, -2); // obj id counts count holds hold
if (lua_toboolean(L, -1) && LuaWrapper<T>::deallocator)
{
LuaWrapper<T>::deallocator(L, obj);
}
luaW_wrapperfield<T>(L, LUAW_STORAGE_KEY); // obj id counts count holds hold storage
lua_pushvalue(L, 2); // obj id counts count holds hold storage id
lua_pushnil(L); // obj id counts count holds hold storage id nil
lua_settable(L, -3); // obj id counts count holds hold storage
luaW_release<T>(L, 2);
return 0;
}
// Thakes two tables and registers them with Lua to the table on the top of the
// stack.
//
// This function is only called from LuaWrapper internally.
inline void luaW_registerfuncs(lua_State* L, const luaL_Reg defaulttable[], const luaL_Reg table[])
{
// ... T
#if LUA_VERSION_NUM > 501
if (defaulttable)
luaL_setfuncs(L, defaulttable, 0); // ... T
if (table)
luaL_setfuncs(L, table, 0); // ... T
#else
if (defaulttable)
luaL_register(L, NULL, defaulttable); // ... T
if (table)
luaL_register(L, NULL, table); // ... T
#endif
}
// Initializes the LuaWrapper tables used to track internal state.
//
// This function is only called from LuaWrapper internally.
inline void luaW_initialize(lua_State* L)
{
// Ensure that the LuaWrapper table is set up
lua_getfield(L, LUA_REGISTRYINDEX, LUAW_WRAPPER_KEY); // ... LuaWrapper
if (lua_isnil(L, -1))
{
lua_newtable(L); // ... nil {}
lua_pushvalue(L, -1); // ... nil {} {}
lua_setfield(L, LUA_REGISTRYINDEX, LUAW_WRAPPER_KEY); // ... nil LuaWrapper
// Create a storage table
lua_newtable(L); // ... LuaWrapper nil {}
lua_setfield(L, -2, LUAW_STORAGE_KEY); // ... nil LuaWrapper
// Create a holds table
lua_newtable(L); // ... LuaWrapper {}
lua_setfield(L, -2, LUAW_HOLDS_KEY); // ... nil LuaWrapper
// Create a cache table, with weak values so that the userdata will not
// be ref counted
lua_newtable(L); // ... nil LuaWrapper {}
lua_setfield(L, -2, LUAW_CACHE_KEY); // ... nil LuaWrapper
lua_newtable(L); // ... nil LuaWrapper {}
lua_pushstring(L, "v"); // ... nil LuaWrapper {} "v"
lua_setfield(L, -2, "__mode"); // ... nil LuaWrapper {}
lua_setfield(L, -2, LUAW_CACHE_METATABLE_KEY); // ... nil LuaWrapper
lua_pop(L, 1); // ... nil
}
lua_pop(L, 1); // ...
}
// Run luaW_register or luaW_setfuncs to create a table and metatable for your
// class. These functions create a table with filled with the function from
// the table argument in addition to the functions new and build (This is
// generally for things you think of as static methods in C++). The given
// metatable argument becomes a metatable for each object of your class. These
// can be thought of as member functions or methods.
//
// You may also supply constructors and destructors for classes that do not
// have a default constructor or that require special set up or tear down. You
// may specify NULL as the constructor, which means that you will not be able
// to call the new function on your class table. You will need to manually push
// objects from C++. By default, the default constructor is used to create
// objects and a simple call to delete is used to destroy them.
//
// By default LuaWrapper uses the address of C++ object to identify unique
// objects. In some cases this is not desired, such as in the case of
// shared_ptrs. Two shared_ptrs may themselves have unique locations in memory
// but still represent the same object. For cases like that, you may specify an
// identifier function which is responsible for pushing a key representing your
// object on to the stack.
//
// luaW_register will set table as the new value of the global of the given
// name. luaW_setfuncs is identical to luaW_register, but it does not set the
// table globally. As with luaL_register and luaL_setfuncs, both funcstions
// leave the new table on the top of the stack.
template <typename T>
void luaW_setfuncs(lua_State* L, const char* classname, const luaL_Reg* table, const luaL_Reg* metatable, T* (*allocator)(lua_State*) = luaW_defaultallocator<T>, void (*deallocator)(lua_State*, T*) = luaW_defaultdeallocator<T>, void (*identifier)(lua_State*, T*) = luaW_defaultidentifier<T>)
{
luaW_initialize(L);
LuaWrapper<T>::classname = classname;
LuaWrapper<T>::identifier = identifier;
LuaWrapper<T>::allocator = allocator;
LuaWrapper<T>::deallocator = deallocator;
const luaL_Reg defaulttable[] =
{
{ "new", luaW_new<T> },
{ NULL, NULL }
};
const luaL_Reg defaultmetatable[] =
{
{ "__index", luaW_index<T> },
{ "__newindex", luaW_newindex<T> },
{ "__gc", luaW_gc<T> },
{ NULL, NULL }
};
// Set up per-type tables
lua_getfield(L, LUA_REGISTRYINDEX, LUAW_WRAPPER_KEY); // ... LuaWrapper
lua_getfield(L, -1, LUAW_STORAGE_KEY); // ... LuaWrapper LuaWrapper.storage
lua_newtable(L); // ... LuaWrapper LuaWrapper.storage {}
lua_setfield(L, -2, LuaWrapper<T>::classname); // ... LuaWrapper LuaWrapper.storage
lua_pop(L, 1); // ... LuaWrapper
lua_getfield(L, -1, LUAW_HOLDS_KEY); // ... LuaWrapper LuaWrapper.holds
lua_newtable(L); // ... LuaWrapper LuaWrapper.holds {}
lua_setfield(L, -2, LuaWrapper<T>::classname); // ... LuaWrapper LuaWrapper.holds
lua_pop(L, 1); // ... LuaWrapper
lua_getfield(L, -1, LUAW_CACHE_KEY); // ... LuaWrapper LuaWrapper.cache
lua_newtable(L); // ... LuaWrapper LuaWrapper.cache {}
luaW_wrapperfield<T>(L, LUAW_CACHE_METATABLE_KEY); // ... LuaWrapper LuaWrapper.cache {} cmt
lua_setmetatable(L, -2); // ... LuaWrapper LuaWrapper.cache {}
lua_setfield(L, -2, LuaWrapper<T>::classname); // ... LuaWrapper LuaWrapper.cache
lua_pop(L, 2); // ...
// Open table
lua_newtable(L); // ... T
luaW_registerfuncs(L, allocator ? defaulttable : NULL, table); // ... T
// Open metatable, set up extends table
luaL_newmetatable(L, classname); // ... T mt
lua_newtable(L); // ... T mt {}
lua_setfield(L, -2, LUAW_EXTENDS_KEY); // ... T mt
luaW_registerfuncs(L, defaultmetatable, metatable); // ... T mt
lua_setfield(L, -2, "metatable"); // ... T
}
template <typename T>
void luaW_register(lua_State* L, const char* classname, const luaL_Reg* table, const luaL_Reg* metatable, T* (*allocator)(lua_State*) = luaW_defaultallocator<T>, void (*deallocator)(lua_State*, T*) = luaW_defaultdeallocator<T>, void (*identifier)(lua_State*, T*) = luaW_defaultidentifier<T>)
{
luaW_setfuncs(L, classname, table, metatable, allocator, deallocator, identifier); // ... T
lua_pushvalue(L, -1); // ... T T
lua_setglobal(L, classname); // ... T
}
// luaW_extend is used to declare that class T inherits from class U. All
// functions in the base class will be available to the derived class (except
// when they share a function name, in which case the derived class's function
// wins). This also allows luaW_to<T> to cast your object apropriately, as
// casts straight through a void pointer do not work.
template <typename T, typename U>
void luaW_extend(lua_State* L)
{
if(!LuaWrapper<T>::classname)
luaL_error(L, "attempting to call extend on a type that has not been registered");
if(!LuaWrapper<U>::classname)
luaL_error(L, "attempting to extend %s by a type that has not been registered", LuaWrapper<T>::classname);
LuaWrapper<T>::cast = luaW_cast<T, U>;
LuaWrapper<T>::identifier = luaW_identify<T, U>;
luaL_getmetatable(L, LuaWrapper<T>::classname); // mt
luaL_getmetatable(L, LuaWrapper<U>::classname); // mt emt
// Point T's metatable __index at U's metatable for inheritance
lua_newtable(L); // mt emt {}
lua_pushvalue(L, -2); // mt emt {} emt
lua_setfield(L, -2, "__index"); // mt emt {}
lua_setmetatable(L, -3); // mt emt
// Set up per-type tables to point at parent type
lua_getfield(L, LUA_REGISTRYINDEX, LUAW_WRAPPER_KEY); // ... LuaWrapper
lua_getfield(L, -1, LUAW_STORAGE_KEY); // ... LuaWrapper LuaWrapper.storage
lua_getfield(L, -1, LuaWrapper<U>::classname); // ... LuaWrapper LuaWrapper.storage U
lua_setfield(L, -2, LuaWrapper<T>::classname); // ... LuaWrapper LuaWrapper.storage
lua_pop(L, 1); // ... LuaWrapper
lua_getfield(L, -1, LUAW_HOLDS_KEY); // ... LuaWrapper LuaWrapper.holds
lua_getfield(L, -1, LuaWrapper<U>::classname); // ... LuaWrapper LuaWrapper.holds U
lua_setfield(L, -2, LuaWrapper<T>::classname); // ... LuaWrapper LuaWrapper.holds
lua_pop(L, 1); // ... LuaWrapper
lua_getfield(L, -1, LUAW_CACHE_KEY); // ... LuaWrapper LuaWrapper.cache
lua_getfield(L, -1, LuaWrapper<U>::classname); // ... LuaWrapper LuaWrapper.cache U
lua_setfield(L, -2, LuaWrapper<T>::classname); // ... LuaWrapper LuaWrapper.cache
lua_pop(L, 2); // ...
// Make a list of all types that inherit from U, for type checking
lua_getfield(L, -2, LUAW_EXTENDS_KEY); // mt emt mt.extends
lua_pushvalue(L, -2); // mt emt mt.extends emt
lua_setfield(L, -2, LuaWrapper<U>::classname); // mt emt mt.extends
lua_getfield(L, -2, LUAW_EXTENDS_KEY); // mt emt mt.extends emt.extends
for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1))
{
// mt emt mt.extends emt.extends k v
lua_pushvalue(L, -2); // mt emt mt.extends emt.extends k v k
lua_pushvalue(L, -2); // mt emt mt.extends emt.extends k v k
lua_rawset(L, -6); // mt emt mt.extends emt.extends k v
}
lua_pop(L, 4); // mt emt
}
/*
* Copyright (c) 2010-2013 Alexander Ames
*
* 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.
*/
#endif // LUA_WRAPPER_H_

View File

@ -1,714 +0,0 @@
/*
* Copyright (c) 2010-2013 Alexander Ames
* Alexander.Ames@gmail.com
* See Copyright Notice at the end of this file
*/
// API Summary:
//
// This is a set of utility functions that add to the functionalit of
// LuaWrapper. Over time I found myself repeating a few patterns, such as
// writing many trvial getter and setter functions, and wanting pass ownership
// of one object to another and have the Lua script properly handle that case.
//
// This file contains the additional functions that I've added but that do
// not really belong in the core API.
#ifndef LUAWRAPPERUTILS_HPP_
#define LUAWRAPPERUTILS_HPP_
#include "luawrapper.hpp"
#ifndef LUAW_NO_CXX11
#include <type_traits>
#endif
#ifndef LUAW_STD
#define LUAW_STD std
#endif
////////////////////////////////////////////////////////////////////////////////
//
// A set of templated luaL_check and lua_push functions for use in the getters
// and setters below
//
// It is often useful to override luaU_check, luaU_to and/or luaU_push to
// operate on your own simple types rather than register your type with
// LuaWrapper, especially with small objects.
//
template<typename U, typename = void>
struct luaU_Impl
{
static U luaU_check(lua_State* L, int index);
static U luaU_to (lua_State* L, int index);
static void luaU_push (lua_State* L, const U& value);
static void luaU_push (lua_State* L, U& value);
};
template<typename U> U luaU_check(lua_State* L, int index) { return luaU_Impl<U>::luaU_check(L, index); }
template<typename U> U luaU_to (lua_State* L, int index) { return luaU_Impl<U>::luaU_to (L, index); }
template<typename U> void luaU_push (lua_State* L, const U& value) { luaU_Impl<U>::luaU_push (L, value); }
template<typename U> void luaU_push (lua_State* L, U& value) { luaU_Impl<U>::luaU_push (L, value); }
////////////////////////////////////////////////////////////////////////////////
//
// This is slightly different than the previous three functions in that you
// shouldn't need to write your own version of it, since it uses luaU_check
// automatically.
//
template<typename U>
U luaU_opt(lua_State* L, int index, const U& fallback = U())
{
if (lua_isnil(L, index))
return fallback;
else
return luaU_Impl<U>::luaU_check(L, index);
}
template<>
struct luaU_Impl<bool>
{
static bool luaU_check(lua_State* L, int index) { return lua_toboolean (L, index) != 0; }
static bool luaU_to (lua_State* L, int index) { return lua_toboolean (L, index) != 0; }
static void luaU_push (lua_State* L, const bool& value) { lua_pushboolean(L, value); }
};
template<>
struct luaU_Impl<const char*>
{
static const char* luaU_check(lua_State* L, int index) { return luaL_checkstring(L, index); }
static const char* luaU_to (lua_State* L, int index) { return lua_tostring (L, index); }
static void luaU_push (lua_State* L, const char* const& value) { lua_pushstring (L, value); }
};
template<>
struct luaU_Impl<unsigned int>
{
static unsigned int luaU_check(lua_State* L, int index) { return static_cast<unsigned int>(luaL_checkinteger(L, index)); }
static unsigned int luaU_to (lua_State* L, int index) { return static_cast<unsigned int>(lua_tointeger (L, index)); }
static void luaU_push (lua_State* L, const unsigned int& value) { lua_pushinteger (L, value); }
};
template<>
struct luaU_Impl<int>
{
static int luaU_check(lua_State* L, int index) { return static_cast<int>(luaL_checkinteger(L, index)); }
static int luaU_to (lua_State* L, int index) { return static_cast<int>(lua_tointeger (L, index)); }
static void luaU_push (lua_State* L, const int& value) { lua_pushinteger (L, value); }
};
template<>
struct luaU_Impl<unsigned char>
{
static unsigned char luaU_check(lua_State* L, int index) { return static_cast<unsigned char>(luaL_checkinteger(L, index)); }
static unsigned char luaU_to (lua_State* L, int index) { return static_cast<unsigned char>(lua_tointeger (L, index)); }
static void luaU_push (lua_State* L, const unsigned char& value) { lua_pushinteger (L, value); }
};
template<>
struct luaU_Impl<char>
{
static char luaU_check(lua_State* L, int index) { return static_cast<char>(luaL_checkinteger(L, index)); }
static char luaU_to (lua_State* L, int index) { return static_cast<char>(lua_tointeger (L, index)); }
static void luaU_push (lua_State* L, const char& value) { lua_pushinteger (L, value); }
};
template<>
struct luaU_Impl<float>
{
static float luaU_check(lua_State* L, int index) { return static_cast<float>(luaL_checknumber(L, index)); }
static float luaU_to (lua_State* L, int index) { return static_cast<float>(lua_tonumber (L, index)); }
static void luaU_push (lua_State* L, const float& value) { lua_pushnumber (L, value); }
};
template<>
struct luaU_Impl<double>
{
static double luaU_check(lua_State* L, int index) { return static_cast<double>(luaL_checknumber(L, index)); }
static double luaU_to (lua_State* L, int index) { return static_cast<double>(lua_tonumber (L, index)); }
static void luaU_push (lua_State* L, const double& value) { lua_pushnumber (L, value); }
};
#ifndef LUAW_NO_CXX11
template<typename T>
struct luaU_Impl<T, typename LUAW_STD::enable_if<LUAW_STD::is_enum<T>::value>::type>
{
static T luaU_check(lua_State* L, int index) { return static_cast<T>(luaL_checkinteger (L, index)); }
static T luaU_to (lua_State* L, int index) { return static_cast<T>(lua_tointeger (L, index)); }
static void luaU_push (lua_State* L, const T& value) { lua_pushnumber(L, static_cast<int>(value )); }
};
template<typename T>
struct luaU_Impl<T*, typename LUAW_STD::enable_if<LUAW_STD::is_class<T>::value>::type>
{
static T* luaU_check( lua_State* L, int index) { return luaW_check<T>(L, index); }
static T* luaU_to ( lua_State* L, int index) { return luaW_to <T>(L, index); }
static void luaU_push ( lua_State* L, T*& value) { luaW_push <T>(L, value); }
static void luaU_push ( lua_State* L, T* value) { luaW_push <T>(L, value); }
};
#endif
///////////////////////////////////////////////////////////////////////////////
//
// These are just some functions I've always felt should exist
//
template <typename U>
inline U luaU_getfield(lua_State* L, int index, const char* field)
{
#ifndef LUAW_NO_CXX11
static_assert(!std::is_same<U, const char*>::value,
"luaU_getfield is not safe to use on const char*'s. (The string will be popped from the stack.)");
#endif
lua_getfield(L, index, field);
U val = luaU_to<U>(L, -1);
lua_pop(L, 1);
return val;
}
template <typename U>
inline U luaU_checkfield(lua_State* L, int index, const char* field)
{
#ifndef LUAW_NO_CXX11
static_assert(!std::is_same<U, const char*>::value,
"luaU_checkfield is not safe to use on const char*'s. (The string will be popped from the stack.)");
#endif
lua_getfield(L, index, field);
U val = luaU_check<U>(L, -1);
lua_pop(L, 1);
return val;
}
template <typename U>
inline void luaU_setfield(lua_State* L, int index, const char* field, U val)
{
luaU_push<U>(L, val);
lua_setfield(L, luaW_correctindex(L, index, 1), field);
}
///////////////////////////////////////////////////////////////////////////////
//
// A set of trivial getter and setter templates. These templates are designed
// to call trivial getters or setters.
//
// There are four forms:
// 1. Getting or setting a public member variable that is of a primitive type
// 2. Getting or setting a public member variable that is a pointer to an
// object
// 3. Getting or setting a private member variable that is of a primitive type
// through a getter or setter
// 3. Getting or setting a private member variable that is is a pointer to an
// object through a getter or setter
//
// The interface to all of them is the same however. In addition to plain
// getter and setter functions, there is a getset which does both - if an
// argument is supplied it attempts to set the value, and in either case it
// returns the value. In your lua table declaration in C++ rather than write
// individiual wrappers for each getter and setter you may do the following:
//
// static luaL_reg Foo_metatable[] =
// {
// { "GetBar", luaU_get<Foo, bool, &Widget::GetBar> },
// { "SetBar", luaU_set<Foo, bool, &Widget::SetBar> },
// { "Bar", luaU_getset<Foo, bool, &Widget::GetBar, &Widget::SetBar> },
// { NULL, NULL }
// }
//
// Getters and setters must have one of the following signatures:
// void T::Setter(U value);
// void T::Setter(U* value);
// void T::Setter(const U& value);
// U Getter() const;
// U* Getter() const;
//
// In this example, the variable Foo::bar is private and so it is accessed
// through getter and setter functions. If Foo::bar were public, it could be
// accessed directly, like so:
//
// static luaL_reg Foo_metatable[] =
// {
// { "GetBar", luaU_get<Foo, bool, &Widget::bar> },
// { "SetBar", luaU_set<Foo, bool, &Widget::bar> },
// { "Bar", luaU_getset<Foo, bool, &Widget::bar> },
// }
//
// In a Lua script, you can now use foo:GetBar(), foo:SetBar() and foo:Bar()
//
template <typename T, typename U, U T::*Member>
int luaU_get(lua_State* L)
{
T* obj = luaW_check<T>(L, 1);
luaU_push<U>(L, obj->*Member);
return 1;
}
template <typename T, typename U, U* T::*Member>
int luaU_get(lua_State* L)
{
T* obj = luaW_check<T>(L, 1);
luaW_push<U>(L, obj->*Member);
return 1;
}
template <typename T, typename U, U (T::*Getter)() const>
int luaU_get(lua_State* L)
{
T* obj = luaW_check<T>(L, 1);
luaU_push<U>(L, (obj->*Getter)());
return 1;
}
template <typename T, typename U, const U& (T::*Getter)() const>
int luaU_get(lua_State* L)
{
T* obj = luaW_check<T>(L, 1);
luaU_push<U>(L, (obj->*Getter)());
return 1;
}
template <typename T, typename U, U* (T::*Getter)() const>
int luaU_get(lua_State* L)
{
T* obj = luaW_check<T>(L, 1);
luaW_push<U>(L, (obj->*Getter)());
return 1;
}
template <typename T, typename U, U T::*Member>
int luaU_set(lua_State* L)
{
T* obj = luaW_check<T>(L, 1);
if (obj)
obj->*Member = luaU_check<U>(L, 2);
return 0;
}
template <typename T, typename U, U* T::*Member>
int luaU_set(lua_State* L)
{
T* obj = luaW_check<T>(L, 1);
if (obj)
{
U* member = luaW_opt<U>(L, 2);
obj->*Member = member;
}
return 0;
}
template <typename T, typename U, const U* T::*Member>
int luaU_set(lua_State* L)
{
T* obj = luaW_check<T>(L, 1);
if (obj)
{
U* member = luaW_opt<U>(L, 2);
obj->*Member = member;
}
return 0;
}
template <typename T, typename U, const U* T::*Member>
int luaU_setandrelease(lua_State* L)
{
T* obj = luaW_check<T>(L, 1);
if (obj)
{
U* member = luaW_opt<U>(L, 2);
obj->*Member = member;
if (member)
luaW_release<U>(L, member);
}
return 0;
}
template <typename T, typename U, void (T::*Setter)(U)>
int luaU_set(lua_State* L)
{
T* obj = luaW_check<T>(L, 1);
if (obj)
(obj->*Setter)(luaU_check<U>(L, 2));
return 0;
}
template <typename T, typename U, void (T::*Setter)(const U&)>
int luaU_set(lua_State* L)
{
T* obj = luaW_check<T>(L, 1);
if (obj)
(obj->*Setter)(luaU_check<U>(L, 2));
return 0;
}
template <typename T, typename U, void (T::*Setter)(U*)>
int luaU_set(lua_State* L)
{
T* obj = luaW_check<T>(L, 1);
if (obj)
{
U* member = luaW_opt<U>(L, 2);
(obj->*Setter)(member);
}
return 0;
}
template <typename T, typename U, void (T::*Setter)(U*)>
int luaU_setandrelease(lua_State* L)
{
T* obj = luaW_check<T>(L, 1);
if (obj)
{
U* member = luaW_opt<U>(L, 2);
(obj->*Setter)(member);
if (member)
luaW_release<U>(L, member);
}
return 0;
}
template <typename T, typename U, U T::*Member>
int luaU_getset(lua_State* L)
{
T* obj = luaW_check<T>(L, 1);
if (obj && lua_gettop(L) >= 2)
{
obj->*Member = luaU_check<U>(L, 2);
return 0;
}
else
{
luaU_push<U>(L, obj->*Member);
return 1;
}
}
template <typename T, typename U, U* T::*Member>
int luaU_getset(lua_State* L)
{
T* obj = luaW_check<T>(L, 1);
if (obj && lua_gettop(L) >= 2)
{
U* member = luaW_opt<U>(L, 2);
obj->*Member = member;
return 0;
}
else
{
luaW_push<U>(L, obj->*Member);
return 1;
}
}
template <typename T, typename U, U* T::*Member>
int luaU_getsetandrelease(lua_State* L)
{
T* obj = luaW_check<T>(L, 1);
if (obj && lua_gettop(L) >= 2)
{
U* member = luaW_opt<U>(L, 2);
obj->*Member = member;
if (member)
luaW_release<U>(L, member);
return 0;
}
else
{
luaW_push<U>(L, obj->*Member);
return 1;
}
}
template <typename T, typename U, U (T::*Getter)() const, void (T::*Setter)(U)>
int luaU_getset(lua_State* L)
{
T* obj = luaW_check<T>(L, 1);
if (obj && lua_gettop(L) >= 2)
{
(obj->*Setter)(luaU_check<U>(L, 2));
return 0;
}
else
{
luaU_push<U>(L, (obj->*Getter)());
return 1;
}
}
template <typename T, typename U, U (T::*Getter)() const, void (T::*Setter)(const U&)>
int luaU_getset(lua_State* L)
{
T* obj = luaW_check<T>(L, 1);
if (obj && lua_gettop(L) >= 2)
{
(obj->*Setter)(luaU_check<U>(L, 2));
return 0;
}
else
{
luaU_push<U>(L, (obj->*Getter)());
return 1;
}
}
template <typename T, typename U, const U& (T::*Getter)() const, void (T::*Setter)(const U&)>
int luaU_getset(lua_State* L)
{
T* obj = luaW_check<T>(L, 1);
if (obj && lua_gettop(L) >= 2)
{
(obj->*Setter)(luaU_check<U>(L, 2));
return 0;
}
else
{
luaU_push<U>(L, (obj->*Getter)());
return 1;
}
}
template <typename T, typename U, U* (T::*Getter)() const, void (T::*Setter)(U*)>
int luaU_getset(lua_State* L)
{
T* obj = luaW_check<T>(L, 1);
if (obj && lua_gettop(L) >= 2)
{
U* member = luaW_opt<U>(L, 2);
(obj->*Setter)(member);
return 0;
}
else
{
luaW_push<U>(L, (obj->*Getter)());
return 1;
}
}
template <typename T, typename U, U* (T::*Getter)() const, void (T::*Setter)(U*)>
int luaU_getsetandrelease(lua_State* L)
{
T* obj = luaW_check<T>(L, 1);
if (obj && lua_gettop(L) >= 2)
{
U* member = luaW_opt<U>(L, 2);
(obj->*Setter)(member);
if (member)
luaW_release<U>(L, member);
return 0;
}
else
{
luaW_push<U>(L, (obj->*Getter)());
return 1;
}
}
#if !defined(_WIN32) && !defined(LUAW_NO_CXX11)
///////////////////////////////////////////////////////////////////////////////
//
// luaU_func is a special macro that expands into a simple function wrapper.
// Unlike the getter setters above, you merely need to name the function you
// would like to wrap.
//
// For example,
//
// struct Foo
// {
// int DoSomething(int, const char*);
// };
//
// static luaL_reg Foo_metatable[] =
// {
// { "DoSomething", luaU_func(&Foo::DoSomething) },
// { NULL, NULL }
// }
//
// This macro will expand based on the function signature of Foo::DoSomething
// In this example, it would expand into the following wrapper:
//
// luaU_push(luaW_check<T>(L, 1)->DoSomething(luaU_check<int>(L, 2), luaU_check<const char*>(L, 3)));
// return 1;
//
// In this example there is only one member function called DoSomething. In some
// cases there may be multiple overrides for a function. For those cases, an
// additional macro has been provided, luaU_funcsig, which takes the entire
// function signature. The arguments to the macro reflect the order they would
// appear in the function signature: return type, type name, function name, and
// finally a list of all the argument types. For example:
//
// struct Foo
// {
// int DoSomething (const char*);
// int DoSomething (const char*, int);
// };
//
// const struct luaL_Reg Foo_metatable[] =
// {
// {"DoSomething1", luaU_funcsig(int, Foo, DoSomething, const char*)) },
// {"DoSomething1", luaU_funcsig(int, Foo, DoSomething, const char*, int)) },
// { NULL, NULL }
// };
//
// These macros and it's underlying templates are somewhat experimental and some
// refinements are probably needed. There are cases where it does not
// currently work and I expect some changes can be made to refine its behavior.
//
#define luaU_func(memberfunc) &luaU_FuncWrapper<decltype(memberfunc),memberfunc>::call
#define luaU_funcsig(returntype, type, funcname, ...) luaU_func(static_cast<returntype (type::*)(__VA_ARGS__)>(&type::funcname))
template<int... ints> struct luaU_IntPack { };
template<int start, int count, int... tail> struct luaU_MakeIntRangeType { typedef typename luaU_MakeIntRangeType<start, count-1, start+count-1, tail...>::type type; };
template<int start, int... tail> struct luaU_MakeIntRangeType<start, 0, tail...> { typedef luaU_IntPack<tail...> type; };
template<int start, int count> inline typename luaU_MakeIntRangeType<start, count>::type luaU_makeIntRange() { return typename luaU_MakeIntRangeType<start, count>::type(); }
template<class MemFunPtrType, MemFunPtrType MemberFunc> struct luaU_FuncWrapper;
template<class T, class ReturnType, class... Args, ReturnType(T::*MemberFunc)(Args...)>
struct luaU_FuncWrapper<ReturnType (T::*)(Args...), MemberFunc>
{
public:
static int call(lua_State* L)
{
return callImpl(L, luaU_makeIntRange<2,sizeof...(Args)>());
}
private:
template<int... indices>
static int callImpl(lua_State* L, luaU_IntPack<indices...>)
{
luaU_push<ReturnType>(L, (luaW_check<T>(L, 1)->*MemberFunc)(luaU_check<Args>(L, indices)...));
return 1;
}
};
#endif
///////////////////////////////////////////////////////////////////////////////
//
// Calls the copy constructor for an object of type T.
// Arguments may be passed in, in case they're needed for the postconstructor
//
// e.g.
//
// C++:
// luaL_reg Foo_Metatable[] =
// {
// { "clone", luaU_clone<Foo> },
// { NULL, NULL }
// };
//
// Lua:
// foo = Foo.new()
// foo2 = foo:clone()
//
template <typename T>
int luaU_clone(lua_State* L)
{
// obj ...
T* obj = new T(*luaW_check<T>(L, 1));
lua_remove(L, 1); // ...
int numargs = lua_gettop(L);
luaW_push<T>(L, obj); // ... clone
luaW_hold<T>(L, obj);
luaW_postconstructor<T>(L, numargs);
return 1;
}
///////////////////////////////////////////////////////////////////////////////
//
// luaU_build is intended to be used to initialize many values by passing in a
// table. They keys of the table are used as function names, and values are
// used as arguments to the function. This is intended to be used on functions
// that are simple setters.
//
// For example, if luaU_build is set as the post constructor, you can
// initialize an object as so:
//
// f = Foo.new
// {
// X = 10;
// Y = 20;
// }
//
// After the object is constructed, luaU_build will do the equivalent of
// calling f:X(10) and f:Y(20).
//
template <typename T>
int luaU_build(lua_State* L)
{
// obj {}
lua_insert(L, -2); // {} obj
if (lua_type(L, 1) == LUA_TTABLE)
{
for (lua_pushnil(L); lua_next(L, 1); lua_pop(L, 1))
{
// {} obj k v
lua_pushvalue(L, -2); // {} obj k v k
lua_gettable(L, -4); // {} obj k v ud[k]
lua_pushvalue(L, -4); // {} obj k v ud[k] ud
lua_pushvalue(L, -3); // {} obj k v ud[k] ud v
lua_call(L, 2, 0); // {} obj k v
}
// {} ud
}
return 0;
}
///////////////////////////////////////////////////////////////////////////////
//
// Takes the object of type T at the top of the stack and stores it in on a
// table with the name storagetable, on the table at the specified index.
//
// You may manually call luaW_hold and luaW_release to handle pointer
// ownership, but it is often easier to simply store a Lua userdata on a table
// that is owned by its parent. This ensures that your object will not be
// prematurely freed, and that it can only be destoryed after its parent.
//
// e.g.
//
// Foo* parent = luaW_check<Foo>(L, 1);
// Bar* child = luaW_check<Bar>(L, 2);
// if (parent && child)
// {
// luaU_store<Bar>(L, 1, "Children");
// parent->AddChild(child);
// }
//
template <typename T>
void luaU_store(lua_State* L, int index, const char* storagetable, const char* key = NULL)
{
// ... store ... obj
lua_getfield(L, index, storagetable); // ... store ... obj store.storagetable
if (key)
lua_pushstring(L, key); // ... store ... obj store.storagetable key
else
LuaWrapper<T>::identifier(L, luaW_to<T>(L, -2)); // ... store ... obj store.storagetable key
lua_pushvalue(L, -3); // ... store ... obj store.storagetable key obj
lua_settable(L, -3); // ... store ... obj store.storagetable
lua_pop(L, 1); // ... store ... obj
}
/*
* Copyright (c) 2010-2011 Alexander Ames
*
* 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.
*/
#endif // LUAWRAPPERUTILS_HPP_

View File

@ -1,64 +0,0 @@
/*
* main.cpp
* Author: Benjamin Sergeant
* Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
*/
#include <iostream>
extern "C"
{
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}
#include "functions.hpp"
#include "LuaWebSocket.h"
#include <iostream>
int main()
{
lua_State* L = luaL_newstate();
luaL_openlibs(L);
// Functions
lua_register(L, "info", lua_info);
// Objets
ix::luaopen_WebSocket(L);
//
// Simple version does little error handling
// luaL_dofile(L, "echo_client.lua");
//
std::string luaFile("echo_client.lua");
int loadStatus = luaL_loadfile(L, luaFile.c_str());
if (loadStatus)
{
std::cerr << "Error loading " << luaFile << std::endl;
std::cerr << lua_tostring(L, -1) << std::endl;
return 1;
}
else
{
std::cout << "loaded " << luaFile << std::endl;
}
//
// Capture lua errors
//
try
{
lua_call(L, 0, 0);
lua_close(L);
}
catch (const std::runtime_error& ex)
{
lua_close(L);
std::cerr << ex.what() << std::endl;
return 1;
}
return 0;
}

View File

@ -20,7 +20,7 @@ install: brew
# Release, Debug, MinSizeRel, RelWithDebInfo are the build types
#
brew:
mkdir -p build && (cd build ; cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_TEST=1 .. ; make -j 4 install)
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_TEST=1 .. ; ninja install)
# Docker default target. We've add problem with OpenSSL and TLS 1.3 (on the
# server side ?) and I can't work-around it easily, so we're using mbedtls on
@ -103,6 +103,10 @@ 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)
@ -144,7 +148,7 @@ test_tsan_mbedtls:
(cd test ; python2.7 run.py -r)
build_test_openssl:
mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_OPEN_SSL=1 -DUSE_TEST=1 .. ; make -j 4)
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_OPEN_SSL=1 -DUSE_TEST=1 .. ; ninja install)
test_openssl: build_test_openssl
(cd test ; python2.7 run.py -r)
@ -159,9 +163,6 @@ test_no_ssl:
mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_TEST=1 .. ; make -j 4)
(cd test ; python2.7 run.py -r)
luarocks:
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DUSE_LUAROCKS=1 .. ; ninja)
ws_test: ws
(cd ws ; env DEBUG=1 PATH=../ws/build:$$PATH bash test_ws.sh)
@ -188,4 +189,3 @@ doc:
.PHONY: test
.PHONY: build
.PHONY: ws
.PHONY: luarocks

View File

@ -141,8 +141,8 @@ TEST_CASE("Cobra_to_sentry_bot", "[cobra_bots]")
std::string filter;
std::string position("$");
bool verbose = true;
size_t maxQueueSize = 10;
bool enableHeartbeat = false;
int heartBeatTimeout = 60;
// FIXME: try to get this working with https instead of http
// to regress the TLS 1.3 OpenSSL bug
@ -166,8 +166,8 @@ TEST_CASE("Cobra_to_sentry_bot", "[cobra_bots]")
position,
sentryClient,
verbose,
maxQueueSize,
enableHeartbeat,
heartBeatTimeout,
runtime);
//
// We want at least 2 messages to be sent

View File

@ -90,8 +90,8 @@ TEST_CASE("Cobra_to_statsd_bot", "[cobra_bots]")
std::string filter;
std::string position("$");
bool verbose = true;
size_t maxQueueSize = 10;
bool enableHeartbeat = false;
int heartBeatTimeout = 60;
// Only run the bot for 3 seconds
int runtime = 3;
@ -123,8 +123,8 @@ TEST_CASE("Cobra_to_statsd_bot", "[cobra_bots]")
gauge,
timer,
verbose,
maxQueueSize,
enableHeartbeat,
heartBeatTimeout,
runtime);
//
// We want at least 2 messages to be sent

View File

@ -87,10 +87,9 @@ TEST_CASE("Cobra_to_stdout_bot", "[cobra_bots]")
std::string filter;
std::string position("$");
bool verbose = true;
bool quiet = false;
size_t maxQueueSize = 10;
bool enableHeartbeat = false;
int heartBeatTimeout = 60;
// Only run the bot for 3 seconds
int runtime = 3;
@ -104,9 +103,8 @@ TEST_CASE("Cobra_to_stdout_bot", "[cobra_bots]")
position,
fluentd,
quiet,
verbose,
maxQueueSize,
enableHeartbeat,
heartBeatTimeout,
runtime);
//
// We want at least 2 messages to be sent

View File

@ -4,6 +4,14 @@
* 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

@ -96,7 +96,7 @@ TEST_CASE("subprotocol", "[websocket_subprotocol]")
int attempts = 0;
while (!connected)
{
REQUIRE(attempts++ < 10);
REQUIRE(attempts++ < 100);
ix::msleep(10);
}

View File

@ -19,8 +19,8 @@
#include <ixwebsocket/IXNetSystem.h>
#include <ixwebsocket/IXSocket.h>
#include <ixwebsocket/IXUserAgent.h>
#include <spdlog/spdlog.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/spdlog.h>
#include <sstream>
#include <string>
@ -31,10 +31,6 @@
#define getpid _getpid
#endif
int helper()
{
return 0;
}
int main(int argc, char** argv)
{
@ -152,9 +148,9 @@ int main(int argc, char** argv)
int delayMs = -1;
int count = 1;
uint32_t maxWaitBetweenReconnectionRetries;
size_t maxQueueSize = 100;
int pingIntervalSecs = 30;
int runtime = -1; // run indefinitely
int heartBeatTimeout = 60;
auto addTLSOptions = [&tlsOptions, &verifyNone](CLI::App* app) {
app->add_option(
@ -292,6 +288,7 @@ int main(int argc, char** argv)
cobraSubscribeApp->add_flag("-q", quiet, "Quiet / only display stats");
cobraSubscribeApp->add_flag("--fluentd", fluentd, "Write fluentd prefix");
cobraSubscribeApp->add_option("--runtime", runtime, "Runtime in seconds");
cobraSubscribeApp->add_option("--heartbeat_timeout", heartBeatTimeout, "Heartbeat timeout");
addTLSOptions(cobraSubscribeApp);
addCobraConfig(cobraSubscribeApp);
@ -332,25 +329,21 @@ int main(int argc, char** argv)
cobra2statsd->add_option("--pidfile", pidfile, "Pid file");
cobra2statsd->add_option("--filter", filter, "Stream SQL Filter");
cobra2statsd->add_option("--position", position, "Stream position");
cobra2statsd->add_option("--queue_size",
maxQueueSize,
"Size of the queue to hold messages before they are sent to Sentry");
cobra2statsd->add_option("--runtime", runtime, "Runtime in seconds");
cobra2statsd->add_option("--heartbeat_timeout", heartBeatTimeout, "Heartbeat timeout");
addTLSOptions(cobra2statsd);
addCobraConfig(cobra2statsd);
CLI::App* cobra2sentry = app.add_subcommand("cobra_to_sentry", "Cobra metrics to sentry");
cobra2sentry->fallthrough();
cobra2sentry->add_option("--dsn", dsn, "Sentry DSN");
cobra2sentry->add_option("--queue_size",
maxQueueSize,
"Size of the queue to hold messages before they are sent to Sentry");
cobra2sentry->add_option("channel", channel, "Channel")->required();
cobra2sentry->add_flag("-v", verbose, "Verbose");
cobra2sentry->add_option("--pidfile", pidfile, "Pid file");
cobra2sentry->add_option("--filter", filter, "Stream SQL Filter");
cobra2sentry->add_option("--position", position, "Stream position");
cobra2sentry->add_option("--runtime", runtime, "Runtime in seconds");
cobra2sentry->add_option("--heartbeat_timeout", heartBeatTimeout, "Heartbeat timeout");
addTLSOptions(cobra2sentry);
addCobraConfig(cobra2sentry);
@ -539,9 +532,8 @@ int main(int argc, char** argv)
position,
fluentd,
quiet,
verbose,
maxQueueSize,
enableHeartbeat,
heartBeatTimeout,
runtime);
ret = (int) sentCount;
}
@ -584,8 +576,8 @@ int main(int argc, char** argv)
gauge,
timer,
verbose,
maxQueueSize,
enableHeartbeat,
heartBeatTimeout,
runtime);
}
}
@ -602,8 +594,8 @@ int main(int argc, char** argv)
position,
sentryClient,
verbose,
maxQueueSize,
enableHeartbeat,
heartBeatTimeout,
runtime);
}
else if (app.got_subcommand("cobra_metrics_to_redis"))