Compare commits
	
		
			8 Commits
		
	
	
		
			v9.6.1
			...
			feature/lu
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 22fc8e981d | ||
|  | fb0de53efd | ||
|  | 62a7483e41 | ||
|  | 36fbdd0daa | ||
|  | feff2e38c0 | ||
|  | a040ff06e8 | ||
|  | 755493eaf3 | ||
|  | 4c61aede2e | 
							
								
								
									
										26
									
								
								.github/workflows/ccpp.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										26
									
								
								.github/workflows/ccpp.yml
									
									
									
									
										vendored
									
									
								
							| @@ -9,8 +9,8 @@ jobs: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|     - uses: actions/checkout@v1 | ||||
|     - name: make test_make | ||||
|       run: make test_make | ||||
|     - name: make test | ||||
|       run: make test | ||||
|  | ||||
|   mac_tsan_sectransport: | ||||
|     runs-on: macOS-latest | ||||
| @@ -19,15 +19,6 @@ 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: | ||||
| @@ -37,7 +28,7 @@ jobs: | ||||
|     - name: make test | ||||
|       run: make test_tsan_mbedtls | ||||
|  | ||||
|   windows: | ||||
|   windows_openssl: | ||||
|     runs-on: windows-latest | ||||
|     steps: | ||||
|     - uses: actions/checkout@v1 | ||||
| @@ -52,17 +43,6 @@ 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 | ||||
| # | ||||
|   | ||||
| @@ -12,10 +12,6 @@ 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() | ||||
| @@ -123,11 +119,6 @@ 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 windows if nothing is configured | ||||
|     elseif (WIN32) | ||||
|       if (NOT USE_OPEN_SSL) # unless we want something else | ||||
|         set(USE_MBED_TLS ON) | ||||
|       endif() | ||||
|     else() # default to OpenSSL on all other platforms | ||||
|       if (NOT USE_MBED_TLS) # Unless mbedtls is requested | ||||
|         set(USE_OPEN_SSL ON) | ||||
| @@ -153,8 +144,6 @@ 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) | ||||
| @@ -176,28 +165,26 @@ 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) | ||||
| 	    include(FindOpenSSL) | ||||
|       find_package(OpenSSL REQUIRED) | ||||
|     endif() | ||||
|     message(STATUS "OpenSSL: " ${OPENSSL_VERSION}) | ||||
|  | ||||
|     target_link_libraries(ixwebsocket PUBLIC OpenSSL::SSL OpenSSL::Crypto) | ||||
|     add_definitions(${OPENSSL_DEFINITIONS}) | ||||
|     target_include_directories(ixwebsocket PUBLIC ${OPENSSL_INCLUDE_DIR}) | ||||
|     target_link_libraries(ixwebsocket ${OPENSSL_LIBRARIES}) | ||||
|   elseif (USE_MBED_TLS) | ||||
|     message(STATUS "TLS configured to use mbedtls") | ||||
|  | ||||
|     find_package(MbedTLS REQUIRED) | ||||
|     target_include_directories(ixwebsocket PUBLIC ${MBEDTLS_INCLUDE_DIRS}) | ||||
|     target_link_libraries(ixwebsocket PUBLIC ${MBEDTLS_LIBRARIES}) | ||||
|     target_link_libraries(ixwebsocket ${MBEDTLS_LIBRARIES}) | ||||
|   elseif (USE_SECURE_TRANSPORT) | ||||
|     message(STATUS "TLS configured to use secure transport") | ||||
|     target_link_libraries(ixwebsocket PUBLIC "-framework foundation" "-framework security") | ||||
|     target_link_libraries(ixwebsocket "-framework foundation" "-framework security") | ||||
|   endif() | ||||
| endif() | ||||
|  | ||||
| @@ -207,25 +194,25 @@ if (NOT ZLIB_FOUND) | ||||
| endif() | ||||
| if (ZLIB_FOUND) | ||||
|   include_directories(${ZLIB_INCLUDE_DIRS}) | ||||
|   target_link_libraries(ixwebsocket PUBLIC ${ZLIB_LIBRARIES}) | ||||
|   target_link_libraries(ixwebsocket ${ZLIB_LIBRARIES}) | ||||
| else() | ||||
|   include_directories(third_party/zlib ${CMAKE_CURRENT_BINARY_DIR}/third_party/zlib) | ||||
|   add_subdirectory(third_party/zlib EXCLUDE_FROM_ALL) | ||||
|   target_link_libraries(ixwebsocket PRIVATE $<LINK_ONLY:zlibstatic>) | ||||
|   add_subdirectory(third_party/zlib) | ||||
|   target_link_libraries(ixwebsocket zlibstatic) | ||||
| endif() | ||||
|  | ||||
| if (WIN32) | ||||
|   target_link_libraries(ixwebsocket PUBLIC wsock32 ws2_32 shlwapi) | ||||
|   target_link_libraries(ixwebsocket wsock32 ws2_32 shlwapi) | ||||
|   add_definitions(-D_CRT_SECURE_NO_WARNINGS) | ||||
|  | ||||
|   if (USE_TLS) | ||||
|     target_link_libraries(ixwebsocket PUBLIC Crypt32) | ||||
|     target_link_libraries(ixwebsocket Crypt32) | ||||
|   endif() | ||||
| endif() | ||||
|  | ||||
| if (UNIX) | ||||
|   find_package(Threads) | ||||
|   target_link_libraries(ixwebsocket PUBLIC ${CMAKE_THREAD_LIBS_INIT}) | ||||
|   target_link_libraries(ixwebsocket ${CMAKE_THREAD_LIBS_INIT}) | ||||
| endif() | ||||
|  | ||||
|  | ||||
| @@ -238,18 +225,15 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") | ||||
|     target_compile_options(ixwebsocket PRIVATE /MP) | ||||
| endif() | ||||
|  | ||||
| target_include_directories(ixwebsocket PUBLIC $<BUILD_INTERFACE:${IXWEBSOCKET_INCLUDE_DIRS}> $<INSTALL_INTERFACE:include/ixwebsocket>) | ||||
| target_include_directories(ixwebsocket PUBLIC ${IXWEBSOCKET_INCLUDE_DIRS}) | ||||
|  | ||||
| set_target_properties(ixwebsocket PROPERTIES PUBLIC_HEADER "${IXWEBSOCKET_HEADERS}") | ||||
|  | ||||
| install(TARGETS ixwebsocket EXPORT ixwebsocket | ||||
| 	ARCHIVE DESTINATION ${CMAKE_INSTALL_PREFIX}/lib | ||||
| 	PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_PREFIX}/include/ixwebsocket/ | ||||
| install(TARGETS 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) | ||||
| @@ -267,3 +251,7 @@ if (USE_WS OR USE_TEST) | ||||
|       add_subdirectory(test) | ||||
|   endif() | ||||
| endif() | ||||
|  | ||||
| if (USE_LUAROCKS) | ||||
|     add_subdirectory(luarocks) | ||||
| endif() | ||||
|   | ||||
| @@ -45,7 +45,3 @@ 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 | ||||
| - [DisCPP](https://github.com/DisCPP/DisCPP), a simple but feature rich Discord API wrapper | ||||
|   | ||||
| @@ -2,7 +2,7 @@ FROM alpine:3.11 as build | ||||
|  | ||||
| RUN apk add --no-cache \ | ||||
|     gcc g++ musl-dev linux-headers \ | ||||
|     cmake mbedtls-dev make zlib-dev ninja | ||||
|     cmake mbedtls-dev make zlib-dev | ||||
|  | ||||
| RUN addgroup -S app && \ | ||||
|     adduser -S -G app app && \ | ||||
|   | ||||
| @@ -1,42 +1,6 @@ | ||||
| # Changelog | ||||
| All changes to this project will be documented in this file. | ||||
|  | ||||
| ## [9.6.1] - 2020-05-17 | ||||
|  | ||||
| (windows + tls) mbedtls is the default windows tls backend + add ability to load system certificates with mbdetls on windows | ||||
|  | ||||
| ## [9.6.0] - 2020-05-12 | ||||
|  | ||||
| (ixbots) add options to limit how many messages per minute should be processed | ||||
|  | ||||
| ## [9.5.9] - 2020-05-12 | ||||
|  | ||||
| (ixbots) add new class to configure a bot to simplify passing options around | ||||
|  | ||||
| ## [9.5.8] - 2020-05-08 | ||||
|  | ||||
| (openssl tls) (openssl < 1.1) logic inversion - crypto locking callback are not registered properly | ||||
|  | ||||
| ## [9.5.7] - 2020-05-08 | ||||
|  | ||||
| (cmake) default TLS back to mbedtls on Windows Universal Platform | ||||
|  | ||||
| ## [9.5.6] - 2020-05-06 | ||||
|  | ||||
| (cobra bots) add a --heartbeat_timeout option to specify when the bot should terminate because no events are received | ||||
|  | ||||
| ## [9.5.5] - 2020-05-06 | ||||
|  | ||||
| (openssl tls) when OpenSSL is older than 1.1, register the crypto locking callback to be thread safe. Should fix lots of CI failures | ||||
|  | ||||
| ## [9.5.4] - 2020-05-04 | ||||
|  | ||||
| (cobra bots) do not use a queue to store messages pending processing, let the bot handle queuing | ||||
|  | ||||
| ## [9.5.3] - 2020-04-29 | ||||
|  | ||||
| (http client) better current request cancellation support when the HttpClient destructor is invoked (see #189) | ||||
|  | ||||
| ## [9.5.2] - 2020-04-27 | ||||
|  | ||||
| (cmake) fix cmake broken tls option parsing | ||||
|   | ||||
| @@ -8,15 +8,16 @@ set (IXBOTS_SOURCES | ||||
|     ixbots/IXCobraToSentryBot.cpp | ||||
|     ixbots/IXCobraToStatsdBot.cpp | ||||
|     ixbots/IXCobraToStdoutBot.cpp | ||||
|     ixbots/IXQueueManager.cpp | ||||
|     ixbots/IXStatsdClient.cpp | ||||
| ) | ||||
|  | ||||
| set (IXBOTS_HEADERS | ||||
|     ixbots/IXCobraBot.h | ||||
|     ixbots/IXCobraBotConfig.h | ||||
|     ixbots/IXCobraToSentryBot.h | ||||
|     ixbots/IXCobraToStatsdBot.h | ||||
|     ixbots/IXCobraToStdoutBot.h | ||||
|     ixbots/IXQueueManager.h | ||||
|     ixbots/IXStatsdClient.h | ||||
| ) | ||||
|  | ||||
|   | ||||
| @@ -6,6 +6,7 @@ | ||||
|  | ||||
| #include "IXCobraBot.h" | ||||
|  | ||||
| #include "IXQueueManager.h" | ||||
| #include <ixcobra/IXCobraConnection.h> | ||||
| #include <ixcore/utils/IXCoreLogger.h> | ||||
|  | ||||
| @@ -17,33 +18,32 @@ | ||||
|  | ||||
| namespace ix | ||||
| { | ||||
|     int64_t CobraBot::run(const CobraBotConfig& botConfig) | ||||
|     int64_t CobraBot::run(const CobraConfig& config, | ||||
|                           const std::string& channel, | ||||
|                           const std::string& filter, | ||||
|                           const std::string& position, | ||||
|                           bool verbose, | ||||
|                           size_t maxQueueSize, | ||||
|                           bool useQueue, | ||||
|                           bool enableHeartbeat, | ||||
|                           int runtime) | ||||
|     { | ||||
|         auto config = botConfig.cobraConfig; | ||||
|         auto channel = botConfig.channel; | ||||
|         auto filter = botConfig.filter; | ||||
|         auto position = botConfig.position; | ||||
|         auto enableHeartbeat = botConfig.enableHeartbeat; | ||||
|         auto heartBeatTimeout = botConfig.heartBeatTimeout; | ||||
|         auto runtime = botConfig.runtime; | ||||
|         auto maxEventsPerMinute = botConfig.maxEventsPerMinute; | ||||
|         auto limitReceivedEvents = botConfig.limitReceivedEvents; | ||||
|  | ||||
|         ix::CobraConnection conn; | ||||
|         conn.configure(config); | ||||
|         conn.connect(); | ||||
|  | ||||
|         Json::FastWriter jsonWriter; | ||||
|         std::atomic<uint64_t> sentCount(0); | ||||
|         std::atomic<uint64_t> receivedCount(0); | ||||
|         uint64_t sentCountTotal(0); | ||||
|         uint64_t receivedCountTotal(0); | ||||
|         uint64_t sentCountPerSecs(0); | ||||
|         uint64_t receivedCountPerSecs(0); | ||||
|         std::atomic<int> receivedCountPerMinutes(0); | ||||
|         std::atomic<bool> stop(false); | ||||
|         std::atomic<bool> throttled(false); | ||||
|         std::atomic<bool> fatalCobraError(false); | ||||
|         int minuteCounter = 0; | ||||
|  | ||||
|         QueueManager queueManager(maxQueueSize); | ||||
|  | ||||
|         auto timer = [&sentCount, | ||||
|                       &receivedCount, | ||||
| @@ -51,8 +51,6 @@ namespace ix | ||||
|                       &receivedCountTotal, | ||||
|                       &sentCountPerSecs, | ||||
|                       &receivedCountPerSecs, | ||||
|                       &receivedCountPerMinutes, | ||||
|                       &minuteCounter, | ||||
|                       &stop] { | ||||
|             while (!stop) | ||||
|             { | ||||
| @@ -73,19 +71,13 @@ namespace ix | ||||
|                 CoreLogger::info(ss.str()); | ||||
|  | ||||
|                 receivedCountPerSecs = receivedCount - receivedCountTotal; | ||||
|                 sentCountPerSecs = sentCount - sentCountTotal; | ||||
|                 sentCountPerSecs = sentCount - receivedCountTotal; | ||||
|  | ||||
|                 receivedCountTotal += receivedCountPerSecs; | ||||
|                 sentCountTotal += sentCountPerSecs; | ||||
|  | ||||
|                 auto duration = std::chrono::seconds(1); | ||||
|                 std::this_thread::sleep_for(duration); | ||||
|  | ||||
|                 if (minuteCounter++ == 60) | ||||
|                 { | ||||
|                     receivedCountPerMinutes = 0; | ||||
|                     minuteCounter = 0; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             CoreLogger::info("timer thread done"); | ||||
| @@ -93,7 +85,7 @@ namespace ix | ||||
|  | ||||
|         std::thread t1(timer); | ||||
|  | ||||
|         auto heartbeat = [&sentCount, &receivedCount, &stop, &enableHeartbeat, &heartBeatTimeout, &fatalCobraError] { | ||||
|         auto heartbeat = [&sentCount, &receivedCount, &stop, &enableHeartbeat] { | ||||
|             std::string state("na"); | ||||
|  | ||||
|             if (!enableHeartbeat) return; | ||||
| @@ -109,12 +101,11 @@ namespace ix | ||||
|                 if (currentState == state) | ||||
|                 { | ||||
|                     CoreLogger::error("no messages received or sent for 1 minute, exiting"); | ||||
|                     fatalCobraError = true; | ||||
|                     break; | ||||
|                     exit(1); | ||||
|                 } | ||||
|                 state = currentState; | ||||
|  | ||||
|                 auto duration = std::chrono::seconds(heartBeatTimeout); | ||||
|                 auto duration = std::chrono::minutes(1); | ||||
|                 std::this_thread::sleep_for(duration); | ||||
|             } | ||||
|  | ||||
| @@ -123,6 +114,40 @@ 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, | ||||
| @@ -130,12 +155,13 @@ namespace ix | ||||
|                                &channel, | ||||
|                                &filter, | ||||
|                                &subscriptionPosition, | ||||
|                                &jsonWriter, | ||||
|                                verbose, | ||||
|                                &throttled, | ||||
|                                &receivedCount, | ||||
|                                &receivedCountPerMinutes, | ||||
|                                maxEventsPerMinute, | ||||
|                                limitReceivedEvents, | ||||
|                                &fatalCobraError, | ||||
|                                &useQueue, | ||||
|                                &queueManager, | ||||
|                                &sentCount](const CobraEventPtr& event) { | ||||
|             if (event->type == ix::CobraEventType::Open) | ||||
|             { | ||||
| @@ -156,34 +182,58 @@ namespace ix | ||||
|                 CoreLogger::info("Subscribing to " + channel); | ||||
|                 CoreLogger::info("Subscribing at position " + subscriptionPosition); | ||||
|                 CoreLogger::info("Subscribing with filter " + filter); | ||||
|                 conn.subscribe(channel, filter, subscriptionPosition, | ||||
|                     [&sentCount, &receivedCountPerMinutes, | ||||
|                      maxEventsPerMinute, limitReceivedEvents, | ||||
|                      &throttled, &receivedCount, | ||||
|                      &subscriptionPosition, &fatalCobraError, | ||||
|                      this](const Json::Value& msg, const std::string& position) { | ||||
|                         subscriptionPosition = position; | ||||
|                         ++receivedCount; | ||||
|                 conn.subscribe(channel, | ||||
|                                filter, | ||||
|                                subscriptionPosition, | ||||
|                                [this, | ||||
|                                 &jsonWriter, | ||||
|                                 verbose, | ||||
|                                 &throttled, | ||||
|                                 &receivedCount, | ||||
|                                 &queueManager, | ||||
|                                 &useQueue, | ||||
|                                 &subscriptionPosition, | ||||
|                                 &fatalCobraError, | ||||
|                                 &sentCount](const Json::Value& msg, const std::string& position) { | ||||
|                                    if (verbose) | ||||
|                                    { | ||||
|                                        CoreLogger::info("Subscriber received message " | ||||
|                                                         + position + " -> " + jsonWriter.write(msg)); | ||||
|                                    } | ||||
|  | ||||
|                         ++receivedCountPerMinutes; | ||||
|                         if (limitReceivedEvents) | ||||
|                         { | ||||
|                             if (receivedCountPerMinutes > maxEventsPerMinute) | ||||
|                             { | ||||
|                                 return; | ||||
|                             } | ||||
|                         } | ||||
|                                    subscriptionPosition = position; | ||||
|  | ||||
|                         // If we cannot send to sentry fast enough, drop the message | ||||
|                         if (throttled) | ||||
|                         { | ||||
|                             return; | ||||
|                         } | ||||
|                                    // If we cannot send to sentry fast enough, drop the message | ||||
|                                    if (throttled) | ||||
|                                    { | ||||
|                                        return; | ||||
|                                    } | ||||
|  | ||||
|                         _onBotMessageCallback( | ||||
|                             msg, position, throttled, | ||||
|                             fatalCobraError, sentCount); | ||||
|                     }); | ||||
|                                    ++receivedCount; | ||||
|  | ||||
|                                    if (useQueue) | ||||
|                                    { | ||||
|                                        queueManager.add(msg, position); | ||||
|                                    } | ||||
|                                    else | ||||
|                                    { | ||||
|                                        if (_onBotMessageCallback && | ||||
|                                            _onBotMessageCallback( | ||||
|                                                msg, position, verbose, throttled, fatalCobraError)) | ||||
|                                        { | ||||
|                                            // That might be too noisy | ||||
|                                            if (verbose) | ||||
|                                            { | ||||
|                                                CoreLogger::info("cobra bot: sending succesfull"); | ||||
|                                            } | ||||
|                                            ++sentCount; | ||||
|                                        } | ||||
|                                        else | ||||
|                                        { | ||||
|                                            CoreLogger::error("cobra bot: error sending"); | ||||
|                                        } | ||||
|                                    } | ||||
|                                }); | ||||
|             } | ||||
|             else if (event->type == ix::CobraEventType::Subscribed) | ||||
|             { | ||||
| @@ -258,6 +308,9 @@ namespace ix | ||||
|         // heartbeat thread | ||||
|         if (t2.joinable()) t2.join(); | ||||
|  | ||||
|         // sentry sender thread | ||||
|         t3.join(); | ||||
|  | ||||
|         return fatalCobraError ? -1 : (int64_t) sentCount; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -8,24 +8,33 @@ | ||||
|  | ||||
| #include <atomic> | ||||
| #include <functional> | ||||
| #include "IXCobraBotConfig.h" | ||||
| #include <ixcobra/IXCobraConfig.h> | ||||
| #include <json/json.h> | ||||
| #include <stddef.h> | ||||
|  | ||||
| namespace ix | ||||
| { | ||||
|     using OnBotMessageCallback = std::function<void(const Json::Value&, | ||||
|     using OnBotMessageCallback = std::function<bool(const Json::Value&, | ||||
|                                                     const std::string&, | ||||
|                                                     const bool verbose, | ||||
|                                                     std::atomic<bool>&, | ||||
|                                                     std::atomic<bool>&, | ||||
|                                                     std::atomic<uint64_t>&)>; | ||||
|                                                     std::atomic<bool>&)>; | ||||
|  | ||||
|     class CobraBot | ||||
|     { | ||||
|     public: | ||||
|         CobraBot() = default; | ||||
|  | ||||
|         int64_t run(const CobraBotConfig& botConfig); | ||||
|         int64_t run(const CobraConfig& config, | ||||
|                     const std::string& channel, | ||||
|                     const std::string& filter, | ||||
|                     const std::string& position, | ||||
|                     bool verbose, | ||||
|                     size_t maxQueueSize, | ||||
|                     bool useQueue, | ||||
|                     bool enableHeartbeat, | ||||
|                     int runtime); | ||||
|  | ||||
|         void setOnBotMessageCallback(const OnBotMessageCallback& callback); | ||||
|  | ||||
|     private: | ||||
|   | ||||
| @@ -1,31 +0,0 @@ | ||||
| /* | ||||
|  *  IXCobraBotConfig.h | ||||
|  *  Author: Benjamin Sergeant | ||||
|  *  Copyright (c) 2020 Machine Zone, Inc. All rights reserved. | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <string> | ||||
| #include <limits> | ||||
| #include <ixcobra/IXCobraConfig.h> | ||||
|  | ||||
| #ifdef max | ||||
| #undef max | ||||
| #endif | ||||
|  | ||||
| namespace ix | ||||
| { | ||||
|     struct CobraBotConfig | ||||
|     { | ||||
|         CobraConfig cobraConfig; | ||||
|         std::string channel; | ||||
|         std::string filter; | ||||
|         std::string position = std::string("$"); | ||||
|         bool enableHeartbeat = true; | ||||
|         int heartBeatTimeout = 60; | ||||
|         int runtime = -1; | ||||
|         int maxEventsPerMinute = std::numeric_limits<int>::max(); | ||||
|         bool limitReceivedEvents = false; | ||||
|     }; | ||||
| } // namespace ix | ||||
| @@ -7,6 +7,7 @@ | ||||
| #include "IXCobraToSentryBot.h" | ||||
|  | ||||
| #include "IXCobraBot.h" | ||||
| #include "IXQueueManager.h" | ||||
| #include <ixcobra/IXCobraConnection.h> | ||||
| #include <ixcore/utils/IXCoreLogger.h> | ||||
|  | ||||
| @@ -16,61 +17,101 @@ | ||||
|  | ||||
| namespace ix | ||||
| { | ||||
|     int64_t cobra_to_sentry_bot(const CobraBotConfig& config, | ||||
|     int64_t cobra_to_sentry_bot(const CobraConfig& config, | ||||
|                                 const std::string& channel, | ||||
|                                 const std::string& filter, | ||||
|                                 const std::string& position, | ||||
|                                 SentryClient& sentryClient, | ||||
|                                 bool verbose) | ||||
|                                 bool verbose, | ||||
|                                 size_t maxQueueSize, | ||||
|                                 bool enableHeartbeat, | ||||
|                                 int runtime) | ||||
|     { | ||||
|         CobraBot bot; | ||||
|         bot.setOnBotMessageCallback([&sentryClient, &verbose](const Json::Value& msg, | ||||
|         bot.setOnBotMessageCallback([&sentryClient](const Json::Value& msg, | ||||
|                                                     const std::string& /*position*/, | ||||
|                                                     const bool verbose, | ||||
|                                                     std::atomic<bool>& throttled, | ||||
|                                                     std::atomic<bool>& /*fatalCobraError*/, | ||||
|                                                     std::atomic<uint64_t>& sentCount) -> void { | ||||
|             sentryClient.send(msg, verbose, | ||||
|                 [&sentCount, &throttled](const HttpResponsePtr& response) { | ||||
|                 if (!response) | ||||
|                                                     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) | ||||
|                 { | ||||
|                     CoreLogger::warn("Null HTTP Response"); | ||||
|                     return; | ||||
|                     CoreLogger::info(it.first + ": " + it.second); | ||||
|                 } | ||||
|  | ||||
|                 if (response->statusCode == 200) | ||||
|                 { | ||||
|                     sentCount++; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     CoreLogger::error("Error sending data to sentry: " + std::to_string(response->statusCode)); | ||||
|                     CoreLogger::error("Response: " + response->payload); | ||||
|                 CoreLogger::info("Upload size: " + std::to_string(response->uploadSize)); | ||||
|                 CoreLogger::info("Download size: " + std::to_string(response->downloadSize)); | ||||
|  | ||||
|                     // Error 429 Too Many Requests | ||||
|                     if (response->statusCode == 429) | ||||
|                 CoreLogger::info("Status: " + std::to_string(response->statusCode)); | ||||
|                 if (response->errorCode != HttpErrorCode::Ok) | ||||
|                 { | ||||
|                     CoreLogger::info("error message: " + response->errorMsg); | ||||
|                 } | ||||
|  | ||||
|                 if (response->headers["Content-Type"] != "application/octet-stream") | ||||
|                 { | ||||
|                     CoreLogger::info("payload: " + response->payload); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             bool success = response->statusCode == 200; | ||||
|  | ||||
|             if (!success) | ||||
|             { | ||||
|                 CoreLogger::error("Error sending data to sentry: " + std::to_string(response->statusCode)); | ||||
|                 CoreLogger::error("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()) | ||||
|                     { | ||||
|                         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; | ||||
|                         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; | ||||
|                 } | ||||
|             }); | ||||
|             } | ||||
|  | ||||
|             return success; | ||||
|         }); | ||||
|  | ||||
|         return bot.run(config); | ||||
|         bool useQueue = true; | ||||
|  | ||||
|         return bot.run(config, | ||||
|                        channel, | ||||
|                        filter, | ||||
|                        position, | ||||
|                        verbose, | ||||
|                        maxQueueSize, | ||||
|                        useQueue, | ||||
|                        enableHeartbeat, | ||||
|                        runtime); | ||||
|     } | ||||
| } // namespace ix | ||||
|   | ||||
| @@ -6,13 +6,19 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <cstdint> | ||||
| #include "IXCobraBotConfig.h" | ||||
| #include <ixcobra/IXCobraConfig.h> | ||||
| #include <ixsentry/IXSentryClient.h> | ||||
| #include <string> | ||||
|  | ||||
| namespace ix | ||||
| { | ||||
|     int64_t cobra_to_sentry_bot(const CobraBotConfig& config, | ||||
|     int64_t cobra_to_sentry_bot(const CobraConfig& config, | ||||
|                                 const std::string& channel, | ||||
|                                 const std::string& filter, | ||||
|                                 const std::string& position, | ||||
|                                 SentryClient& sentryClient, | ||||
|                                 bool verbose); | ||||
|                                 bool verbose, | ||||
|                                 size_t maxQueueSize, | ||||
|                                 bool enableHeartbeat, | ||||
|                                 int runtime); | ||||
| } // namespace ix | ||||
|   | ||||
| @@ -7,6 +7,7 @@ | ||||
| #include "IXCobraToStatsdBot.h" | ||||
|  | ||||
| #include "IXCobraBot.h" | ||||
| #include "IXQueueManager.h" | ||||
| #include "IXStatsdClient.h" | ||||
| #include <chrono> | ||||
| #include <ixcobra/IXCobraConnection.h> | ||||
| @@ -53,22 +54,32 @@ namespace ix | ||||
|         return val; | ||||
|     } | ||||
|  | ||||
|     int64_t cobra_to_statsd_bot(const ix::CobraBotConfig& config, | ||||
|     int64_t cobra_to_statsd_bot(const ix::CobraConfig& config, | ||||
|                                 const std::string& channel, | ||||
|                                 const std::string& filter, | ||||
|                                 const std::string& position, | ||||
|                                 StatsdClient& statsdClient, | ||||
|                                 const std::string& fields, | ||||
|                                 const std::string& gauge, | ||||
|                                 const std::string& timer, | ||||
|                                 bool verbose) | ||||
|                                 bool verbose, | ||||
|                                 size_t maxQueueSize, | ||||
|                                 bool enableHeartbeat, | ||||
|                                 int runtime) | ||||
|     { | ||||
|         ix::CobraConnection conn; | ||||
|         conn.configure(config); | ||||
|         conn.connect(); | ||||
|  | ||||
|         auto tokens = parseFields(fields); | ||||
|  | ||||
|         CobraBot bot; | ||||
|         bot.setOnBotMessageCallback( | ||||
|             [&statsdClient, &tokens, &gauge, &timer, &verbose](const Json::Value& msg, | ||||
|             [&statsdClient, &tokens, &gauge, &timer](const Json::Value& msg, | ||||
|                                                      const std::string& /*position*/, | ||||
|                                                      const bool verbose, | ||||
|                                                      std::atomic<bool>& /*throttled*/, | ||||
|                                                      std::atomic<bool>& fatalCobraError, | ||||
|                                                      std::atomic<uint64_t>& sentCount) -> void { | ||||
|                                                      std::atomic<bool>& fatalCobraError) -> bool { | ||||
|                 std::string id; | ||||
|                 for (auto&& attr : tokens) | ||||
|                 { | ||||
| @@ -111,7 +122,7 @@ namespace ix | ||||
|                     { | ||||
|                         CoreLogger::error("Gauge " + gauge + " is not a numeric type"); | ||||
|                         fatalCobraError = true; | ||||
|                         return; | ||||
|                         return false; | ||||
|                     } | ||||
|  | ||||
|                     if (verbose) | ||||
| @@ -129,9 +140,19 @@ namespace ix | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 sentCount++; | ||||
|                 return true; | ||||
|             }); | ||||
|  | ||||
|         return bot.run(config); | ||||
|         bool useQueue = true; | ||||
|  | ||||
|         return bot.run(config, | ||||
|                        channel, | ||||
|                        filter, | ||||
|                        position, | ||||
|                        verbose, | ||||
|                        maxQueueSize, | ||||
|                        useQueue, | ||||
|                        enableHeartbeat, | ||||
|                        runtime); | ||||
|     } | ||||
| } // namespace ix | ||||
|   | ||||
| @@ -7,16 +7,22 @@ | ||||
|  | ||||
| #include <cstdint> | ||||
| #include <ixbots/IXStatsdClient.h> | ||||
| #include "IXCobraBotConfig.h" | ||||
| #include <ixcobra/IXCobraConfig.h> | ||||
| #include <stddef.h> | ||||
| #include <string> | ||||
|  | ||||
| namespace ix | ||||
| { | ||||
|     int64_t cobra_to_statsd_bot(const ix::CobraBotConfig& config, | ||||
|     int64_t cobra_to_statsd_bot(const ix::CobraConfig& config, | ||||
|                                 const std::string& channel, | ||||
|                                 const std::string& filter, | ||||
|                                 const std::string& position, | ||||
|                                 StatsdClient& statsdClient, | ||||
|                                 const std::string& fields, | ||||
|                                 const std::string& gauge, | ||||
|                                 const std::string& timer, | ||||
|                                 bool verbose); | ||||
|                                 bool verbose, | ||||
|                                 size_t maxQueueSize, | ||||
|                                 bool enableHeartbeat, | ||||
|                                 int runtime); | ||||
| } // namespace ix | ||||
|   | ||||
| @@ -7,6 +7,7 @@ | ||||
| #include "IXCobraToStdoutBot.h" | ||||
|  | ||||
| #include "IXCobraBot.h" | ||||
| #include "IXQueueManager.h" | ||||
| #include <chrono> | ||||
| #include <iostream> | ||||
| #include <sstream> | ||||
| @@ -63,9 +64,16 @@ namespace ix | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     int64_t cobra_to_stdout_bot(const ix::CobraBotConfig& config, | ||||
|     int64_t cobra_to_stdout_bot(const CobraConfig& config, | ||||
|                                 const std::string& channel, | ||||
|                                 const std::string& filter, | ||||
|                                 const std::string& position, | ||||
|                                 bool fluentd, | ||||
|                                 bool quiet) | ||||
|                                 bool quiet, | ||||
|                                 bool verbose, | ||||
|                                 size_t maxQueueSize, | ||||
|                                 bool enableHeartbeat, | ||||
|                                 int runtime) | ||||
|     { | ||||
|         CobraBot bot; | ||||
|         auto jsonWriter = makeStreamWriter(); | ||||
| @@ -73,16 +81,27 @@ 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*/, | ||||
|                                             std::atomic<uint64_t>& sentCount) -> void { | ||||
|                                             std::atomic<bool> & | ||||
|                                             /*fatalCobraError*/) -> bool { | ||||
|                 if (!quiet) | ||||
|                 { | ||||
|                     writeToStdout(fluentd, jsonWriter, msg, position); | ||||
|                 } | ||||
|                 sentCount++; | ||||
|                 return true; | ||||
|             }); | ||||
|  | ||||
|         return bot.run(config); | ||||
|         bool useQueue = false; | ||||
|  | ||||
|         return bot.run(config, | ||||
|                        channel, | ||||
|                        filter, | ||||
|                        position, | ||||
|                        verbose, | ||||
|                        maxQueueSize, | ||||
|                        useQueue, | ||||
|                        enableHeartbeat, | ||||
|                        runtime); | ||||
|     } | ||||
| } // namespace ix | ||||
|   | ||||
| @@ -6,13 +6,20 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <cstdint> | ||||
| #include "IXCobraBotConfig.h" | ||||
| #include <ixcobra/IXCobraConfig.h> | ||||
| #include <stddef.h> | ||||
| #include <string> | ||||
|  | ||||
| namespace ix | ||||
| { | ||||
|     int64_t cobra_to_stdout_bot(const ix::CobraBotConfig& config, | ||||
|     int64_t cobra_to_stdout_bot(const ix::CobraConfig& config, | ||||
|                                 const std::string& channel, | ||||
|                                 const std::string& filter, | ||||
|                                 const std::string& position, | ||||
|                                 bool fluentd, | ||||
|                                 bool quiet); | ||||
|                                 bool quiet, | ||||
|                                 bool verbose, | ||||
|                                 size_t maxQueueSize, | ||||
|                                 bool enableHeartbeat, | ||||
|                                 int runtime); | ||||
| } // namespace ix | ||||
|   | ||||
							
								
								
									
										67
									
								
								ixbots/ixbots/IXQueueManager.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								ixbots/ixbots/IXQueueManager.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,67 @@ | ||||
| /* | ||||
|  *  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 | ||||
							
								
								
									
										35
									
								
								ixbots/ixbots/IXQueueManager.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								ixbots/ixbots/IXQueueManager.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| /* | ||||
|  *  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 | ||||
| @@ -226,23 +226,20 @@ namespace ix | ||||
|         return _jsonWriter.write(payload); | ||||
|     } | ||||
|  | ||||
|     void SentryClient::send( | ||||
|         const Json::Value& msg, | ||||
|         bool verbose, | ||||
|         const OnResponseCallback& onResponseCallback) | ||||
|     std::pair<HttpResponsePtr, std::string> SentryClient::send(const Json::Value& msg, bool verbose) | ||||
|     { | ||||
|         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); | ||||
|  | ||||
|         _httpClient->performRequest(args, onResponseCallback); | ||||
|         std::string body = computePayload(msg); | ||||
|         HttpResponsePtr response = _httpClient->post(_url, body, args); | ||||
|  | ||||
|         return std::make_pair(response, body); | ||||
|     } | ||||
|  | ||||
|     // https://sentry.io/api/12345/minidump?sentry_key=abcdefgh"); | ||||
|   | ||||
| @@ -21,9 +21,12 @@ namespace ix | ||||
|         SentryClient(const std::string& dsn); | ||||
|         ~SentryClient() = default; | ||||
|  | ||||
|         void send(const Json::Value& msg, | ||||
|                   bool verbose, | ||||
|                   const OnResponseCallback& onResponseCallback); | ||||
|         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 uploadMinidump(const std::string& sentryMetadata, | ||||
|                             const std::string& minidumpBytes, | ||||
| @@ -36,12 +39,6 @@ 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(); | ||||
|   | ||||
| @@ -6,7 +6,6 @@ | ||||
|  | ||||
| #include "IXCancellationRequest.h" | ||||
|  | ||||
| #include <cassert> | ||||
| #include <chrono> | ||||
|  | ||||
| namespace ix | ||||
| @@ -14,8 +13,6 @@ 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); | ||||
|  | ||||
|   | ||||
| @@ -4,19 +4,6 @@ | ||||
|  *  Copyright (c) 2018 Machine Zone, Inc. All rights reserved. | ||||
|  */ | ||||
|  | ||||
| // | ||||
| // On Windows Universal Platform (uwp), gai_strerror defaults behavior is to returns wchar_t | ||||
| // which is different from all other platforms. We want the non unicode version. | ||||
| // See https://github.com/microsoft/vcpkg/pull/11030 | ||||
| // We could do this in IXNetSystem.cpp but so far we are only using gai_strerror in here. | ||||
| // | ||||
| #ifdef _UNICODE | ||||
| #undef _UNICODE | ||||
| #endif | ||||
| #ifdef UNICODE | ||||
| #undef UNICODE | ||||
| #endif | ||||
|  | ||||
| #include "IXDNSLookup.h" | ||||
|  | ||||
| #include "IXNetSystem.h" | ||||
|   | ||||
| @@ -25,12 +25,10 @@ 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; | ||||
|  | ||||
| @@ -51,11 +49,6 @@ 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>(); | ||||
| @@ -199,7 +192,7 @@ namespace ix | ||||
|             ss << "User-Agent: " << userAgent() << "\r\n"; | ||||
|         } | ||||
|  | ||||
|         if (verb == kPost || verb == kPut || verb == kPatch || _forceBody) | ||||
|         if (verb == kPost || verb == kPut) | ||||
|         { | ||||
|             ss << "Content-Length: " << body.size() << "\r\n"; | ||||
|  | ||||
| @@ -227,10 +220,11 @@ namespace ix | ||||
|  | ||||
|         std::string req(ss.str()); | ||||
|         std::string errMsg; | ||||
|         std::atomic<bool> requestInitCancellation(false); | ||||
|  | ||||
|         // Make a cancellation object dealing with connection timeout | ||||
|         auto isCancellationRequested = | ||||
|             makeCancellationRequestWithTimeout(args->connectTimeout, _stop); | ||||
|             makeCancellationRequestWithTimeout(args->connectTimeout, requestInitCancellation); | ||||
|  | ||||
|         bool success = _socket->connect(host, port, errMsg, isCancellationRequested); | ||||
|         if (!success) | ||||
| @@ -248,7 +242,8 @@ namespace ix | ||||
|         } | ||||
|  | ||||
|         // Make a new cancellation object dealing with transfer timeout | ||||
|         isCancellationRequested = makeCancellationRequestWithTimeout(args->transferTimeout, _stop); | ||||
|         isCancellationRequested = | ||||
|             makeCancellationRequestWithTimeout(args->transferTimeout, requestInitCancellation); | ||||
|  | ||||
|         if (args->verbose) | ||||
|         { | ||||
| @@ -567,20 +562,6 @@ 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; | ||||
|   | ||||
| @@ -46,19 +46,12 @@ 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); | ||||
| @@ -85,7 +78,6 @@ 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); | ||||
| @@ -94,6 +86,7 @@ namespace ix | ||||
|  | ||||
|         // Async API background thread runner | ||||
|         void run(); | ||||
|  | ||||
|         // Async API | ||||
|         bool _async; | ||||
|         std::queue<std::pair<HttpRequestArgsPtr, OnResponseCallback>> _queue; | ||||
| @@ -106,7 +99,5 @@ namespace ix | ||||
|         std::mutex _mutex; // to protect accessing the _socket (only one socket per client) | ||||
|  | ||||
|         SocketTLSOptions _tlsOptions; | ||||
|  | ||||
|         bool _forceBody; | ||||
|     }; | ||||
| } // namespace ix | ||||
|   | ||||
| @@ -19,7 +19,6 @@ 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> | ||||
|   | ||||
| @@ -43,55 +43,6 @@ namespace ix | ||||
|         mbedtls_pk_init(&_pkey); | ||||
|     } | ||||
|  | ||||
|     bool SocketMbedTLS::loadSystemCertificates(std::string& errorMsg) | ||||
|     { | ||||
| #ifdef _WIN32 | ||||
|         DWORD flags = CERT_STORE_READONLY_FLAG | CERT_STORE_OPEN_EXISTING_FLAG | | ||||
|                       CERT_SYSTEM_STORE_CURRENT_USER; | ||||
|         HCERTSTORE systemStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, flags, L"Root"); | ||||
|  | ||||
|         if (!systemStore) | ||||
|         { | ||||
|             errorMsg = "CertOpenStore failed with "; | ||||
|             errorMsg += std::to_string(GetLastError()); | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         PCCERT_CONTEXT certificateIterator = NULL; | ||||
|  | ||||
|         int certificateCount = 0; | ||||
|         while (certificateIterator = CertEnumCertificatesInStore(systemStore, certificateIterator)) | ||||
|         { | ||||
|             if (certificateIterator->dwCertEncodingType & X509_ASN_ENCODING) | ||||
|             { | ||||
|                 int ret = mbedtls_x509_crt_parse(&_cacert, | ||||
|                                                  certificateIterator->pbCertEncoded, | ||||
|                                                  certificateIterator->cbCertEncoded); | ||||
|                 if (ret == 0) | ||||
|                 { | ||||
|                     ++certificateCount; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         CertFreeCertificateContext(certificateIterator); | ||||
|         CertCloseStore(systemStore, 0); | ||||
|  | ||||
|         if (certificateCount == 0) | ||||
|         { | ||||
|             errorMsg = "No certificates found"; | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
| #else | ||||
|         // On macOS we can query the system cert location from the keychain | ||||
|         // On Linux we could try to fetch some local files based on the distribution | ||||
|         // On Android we could use JNI to get to the system certs | ||||
|         return false; | ||||
| #endif | ||||
|     } | ||||
|  | ||||
|     bool SocketMbedTLS::init(const std::string& host, bool isClient, std::string& errMsg) | ||||
|     { | ||||
|         initMBedTLS(); | ||||
| @@ -145,15 +96,13 @@ namespace ix | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             // FIXME: should we call mbedtls_ssl_conf_verify ? | ||||
|             mbedtls_ssl_conf_authmode(&_conf, MBEDTLS_SSL_VERIFY_REQUIRED); | ||||
|  | ||||
|             // FIXME: should we call mbedtls_ssl_conf_verify ? | ||||
|  | ||||
|             if (_tlsOptions.isUsingSystemDefaults()) | ||||
|             { | ||||
|                 if (!loadSystemCertificates(errMsg)) | ||||
|                 { | ||||
|                     return false; | ||||
|                 } | ||||
|                 ; // FIXME | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|   | ||||
| @@ -52,7 +52,6 @@ namespace ix | ||||
|  | ||||
|         bool init(const std::string& host, bool isClient, std::string& errMsg); | ||||
|         void initMBedTLS(); | ||||
|         bool loadSystemCertificates(std::string& errMsg); | ||||
|     }; | ||||
|  | ||||
| } // namespace ix | ||||
|   | ||||
| @@ -85,8 +85,6 @@ 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) | ||||
| @@ -108,11 +106,6 @@ 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(); | ||||
| @@ -121,21 +114,6 @@ 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; | ||||
|   | ||||
| @@ -49,9 +49,6 @@ 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; | ||||
| @@ -61,7 +58,6 @@ namespace ix | ||||
|  | ||||
|         static std::once_flag _openSSLInitFlag; | ||||
|         static std::atomic<bool> _openSSLInitializationSuccessful; | ||||
|         static std::unique_ptr<std::mutex[]> _openSSLMutexes; | ||||
|     }; | ||||
|  | ||||
| } // namespace ix | ||||
|   | ||||
| @@ -44,18 +44,6 @@ 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 | ||||
| @@ -74,13 +62,6 @@ 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); | ||||
| @@ -112,15 +93,4 @@ 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 | ||||
|   | ||||
| @@ -28,12 +28,9 @@ 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: | ||||
|   | ||||
| @@ -6,4 +6,4 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #define IX_WEBSOCKET_VERSION "9.6.1" | ||||
| #define IX_WEBSOCKET_VERSION "9.5.2" | ||||
|   | ||||
							
								
								
									
										30
									
								
								luarocks/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								luarocks/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| # | ||||
| # 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) | ||||
							
								
								
									
										163
									
								
								luarocks/LuaWebSocket.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										163
									
								
								luarocks/LuaWebSocket.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,163 @@ | ||||
| /* | ||||
|  *  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; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										56
									
								
								luarocks/Player.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								luarocks/Player.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,56 @@ | ||||
| #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 | ||||
							
								
								
									
										7
									
								
								luarocks/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								luarocks/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| 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 | ||||
|  | ||||
|  | ||||
							
								
								
									
										30
									
								
								luarocks/echo_client.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								luarocks/echo_client.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| -- | ||||
| -- 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 | ||||
							
								
								
									
										15
									
								
								luarocks/functions.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								luarocks/functions.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| /* | ||||
|  *  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; | ||||
| } | ||||
							
								
								
									
										709
									
								
								luarocks/luawrapper.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										709
									
								
								luarocks/luawrapper.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,709 @@ | ||||
| /* | ||||
|  * 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_ | ||||
							
								
								
									
										714
									
								
								luarocks/luawrapperutil.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										714
									
								
								luarocks/luawrapperutil.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,714 @@ | ||||
| /* | ||||
|  * 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_ | ||||
							
								
								
									
										64
									
								
								luarocks/main.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								luarocks/main.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,64 @@ | ||||
| /* | ||||
|  *  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; | ||||
| } | ||||
							
								
								
									
										14
									
								
								makefile
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								makefile
									
									
									
									
									
								
							| @@ -20,13 +20,13 @@ install: brew | ||||
| # Release, Debug, MinSizeRel, RelWithDebInfo are the build types | ||||
| # | ||||
| brew: | ||||
| 	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_TEST=1 .. ; ninja install) | ||||
| 	mkdir -p build && (cd build ; cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_TEST=1 .. ; make -j 4 install) | ||||
|  | ||||
| # Docker default target. We've add problem with OpenSSL and TLS 1.3 (on the | ||||
| # server side ?) and I can't work-around it easily, so we're using mbedtls on | ||||
| # Linux for the SSL backend, which works great. | ||||
| ws_mbedtls_install: | ||||
| 	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=MinSizeRel -DUSE_TLS=1 -DUSE_WS=1 -DUSE_MBED_TLS=1 .. ; ninja install) | ||||
| 	mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=MinSizeRel -DUSE_TLS=1 -DUSE_WS=1 -DUSE_MBED_TLS=1 .. ; make -j 4 install) | ||||
|  | ||||
| ws: | ||||
| 	mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 .. ; make -j 4) | ||||
| @@ -103,10 +103,6 @@ test_server: | ||||
| # env TEST=Websocket_chat make test | ||||
| # env TEST=heartbeat make test | ||||
| test: | ||||
| 	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_TEST=1 .. ; ninja install) | ||||
| 	(cd test ; python2.7 run.py -r) | ||||
|  | ||||
| test_make: | ||||
| 	mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_TEST=1 .. ; make -j 4) | ||||
| 	(cd test ; python2.7 run.py -r) | ||||
|  | ||||
| @@ -148,7 +144,7 @@ test_tsan_mbedtls: | ||||
| 	(cd test ; python2.7 run.py -r) | ||||
|  | ||||
| build_test_openssl: | ||||
| 	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_OPEN_SSL=1 -DUSE_TEST=1 .. ; ninja install) | ||||
| 	mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_OPEN_SSL=1 -DUSE_TEST=1 .. ; make -j 4) | ||||
|  | ||||
| test_openssl: build_test_openssl | ||||
| 	(cd test ; python2.7 run.py -r) | ||||
| @@ -163,6 +159,9 @@ 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) | ||||
|  | ||||
| @@ -189,3 +188,4 @@ doc: | ||||
| .PHONY: test | ||||
| .PHONY: build | ||||
| .PHONY: ws | ||||
| .PHONY: luarocks | ||||
|   | ||||
| @@ -138,12 +138,11 @@ TEST_CASE("Cobra_to_sentry_bot", "[cobra_bots]") | ||||
|  | ||||
|         std::thread publisherThread(runPublisher, config, channel); | ||||
|  | ||||
|         ix::CobraBotConfig cobraBotConfig; | ||||
|         cobraBotConfig.cobraConfig = config; | ||||
|         cobraBotConfig.channel = channel; | ||||
|         cobraBotConfig.runtime = 3; // Only run the bot for 3 seconds | ||||
|         cobraBotConfig.enableHeartbeat = false; | ||||
|         std::string filter; | ||||
|         std::string position("$"); | ||||
|         bool verbose = true; | ||||
|         size_t maxQueueSize = 10; | ||||
|         bool enableHeartbeat = false; | ||||
|  | ||||
|         // FIXME: try to get this working with https instead of http | ||||
|         //        to regress the TLS 1.3 OpenSSL bug | ||||
| @@ -158,7 +157,18 @@ TEST_CASE("Cobra_to_sentry_bot", "[cobra_bots]") | ||||
|         SentryClient sentryClient(dsn); | ||||
|         sentryClient.setTLSOptions(tlsOptionsClient); | ||||
|  | ||||
|         int64_t sentCount = cobra_to_sentry_bot(cobraBotConfig, sentryClient, verbose); | ||||
|         // Only run the bot for 3 seconds | ||||
|         int runtime = 3; | ||||
|  | ||||
|         int64_t sentCount = cobra_to_sentry_bot(config, | ||||
|                                                 channel, | ||||
|                                                 filter, | ||||
|                                                 position, | ||||
|                                                 sentryClient, | ||||
|                                                 verbose, | ||||
|                                                 maxQueueSize, | ||||
|                                                 enableHeartbeat, | ||||
|                                                 runtime); | ||||
|         // | ||||
|         // We want at least 2 messages to be sent | ||||
|         // | ||||
|   | ||||
| @@ -87,11 +87,14 @@ TEST_CASE("Cobra_to_statsd_bot", "[cobra_bots]") | ||||
|  | ||||
|         std::thread publisherThread(runPublisher, config, channel); | ||||
|  | ||||
|         ix::CobraBotConfig cobraBotConfig; | ||||
|         cobraBotConfig.cobraConfig = config; | ||||
|         cobraBotConfig.channel = channel; | ||||
|         cobraBotConfig.runtime = 3; // Only run the bot for 3 seconds | ||||
|         cobraBotConfig.enableHeartbeat = false; | ||||
|         std::string filter; | ||||
|         std::string position("$"); | ||||
|         bool verbose = true; | ||||
|         size_t maxQueueSize = 10; | ||||
|         bool enableHeartbeat = false; | ||||
|  | ||||
|         // Only run the bot for 3 seconds | ||||
|         int runtime = 3; | ||||
|  | ||||
|         std::string hostname("127.0.0.1"); | ||||
|         // std::string hostname("www.google.com"); | ||||
| @@ -110,10 +113,19 @@ TEST_CASE("Cobra_to_statsd_bot", "[cobra_bots]") | ||||
|         std::string fields("device.game\ndevice.os_name"); | ||||
|         std::string gauge; | ||||
|         std::string timer; | ||||
|         bool verbose = true; | ||||
|  | ||||
|         int64_t sentCount = | ||||
|             ix::cobra_to_statsd_bot(cobraBotConfig, statsdClient, fields, gauge, timer, verbose); | ||||
|         int64_t sentCount = ix::cobra_to_statsd_bot(config, | ||||
|                                                     channel, | ||||
|                                                     filter, | ||||
|                                                     position, | ||||
|                                                     statsdClient, | ||||
|                                                     fields, | ||||
|                                                     gauge, | ||||
|                                                     timer, | ||||
|                                                     verbose, | ||||
|                                                     maxQueueSize, | ||||
|                                                     enableHeartbeat, | ||||
|                                                     runtime); | ||||
|         // | ||||
|         // We want at least 2 messages to be sent | ||||
|         // | ||||
|   | ||||
| @@ -85,17 +85,29 @@ TEST_CASE("Cobra_to_stdout_bot", "[cobra_bots]") | ||||
|  | ||||
|         std::thread publisherThread(runPublisher, config, channel); | ||||
|  | ||||
|         ix::CobraBotConfig cobraBotConfig; | ||||
|         cobraBotConfig.cobraConfig = config; | ||||
|         cobraBotConfig.channel = channel; | ||||
|         cobraBotConfig.runtime = 3; // Only run the bot for 3 seconds | ||||
|         cobraBotConfig.enableHeartbeat = false; | ||||
|         std::string filter; | ||||
|         std::string position("$"); | ||||
|         bool verbose = true; | ||||
|         bool quiet = false; | ||||
|         size_t maxQueueSize = 10; | ||||
|         bool enableHeartbeat = false; | ||||
|  | ||||
|         // Only run the bot for 3 seconds | ||||
|         int runtime = 3; | ||||
|  | ||||
|         // We could try to capture the output ... not sure how. | ||||
|         bool fluentd = true; | ||||
|  | ||||
|         int64_t sentCount = ix::cobra_to_stdout_bot(cobraBotConfig, fluentd, quiet); | ||||
|         int64_t sentCount = ix::cobra_to_stdout_bot(config, | ||||
|                                                     channel, | ||||
|                                                     filter, | ||||
|                                                     position, | ||||
|                                                     fluentd, | ||||
|                                                     quiet, | ||||
|                                                     verbose, | ||||
|                                                     maxQueueSize, | ||||
|                                                     enableHeartbeat, | ||||
|                                                     runtime); | ||||
|         // | ||||
|         // We want at least 2 messages to be sent | ||||
|         // | ||||
|   | ||||
| @@ -4,14 +4,6 @@ | ||||
|  *  Copyright (c) 2019 Machine Zone. All rights reserved. | ||||
|  */ | ||||
|  | ||||
| // Using inet_addr will trigger an error on uwp without this | ||||
| // FIXME: use a different api | ||||
| #ifdef _WIN32 | ||||
| #ifndef _WINSOCK_DEPRECATED_NO_WARNINGS | ||||
| #define _WINSOCK_DEPRECATED_NO_WARNINGS | ||||
| #endif | ||||
| #endif | ||||
|  | ||||
| #include "IXGetFreePort.h" | ||||
|  | ||||
| #include <ixwebsocket/IXNetSystem.h> | ||||
|   | ||||
| @@ -93,11 +93,10 @@ TEST_CASE("subprotocol", "[websocket_subprotocol]") | ||||
|         webSocket.setUrl(url); | ||||
|         webSocket.start(); | ||||
|  | ||||
|         // Give us 3 seconds to connect | ||||
|         int attempts = 0; | ||||
|         while (!connected) | ||||
|         { | ||||
|             REQUIRE(attempts++ < 300); | ||||
|             REQUIRE(attempts++ < 10); | ||||
|             ix::msleep(10); | ||||
|         } | ||||
|  | ||||
|   | ||||
							
								
								
									
										93
									
								
								ws/ws.cpp
									
									
									
									
									
								
							
							
						
						
									
										93
									
								
								ws/ws.cpp
									
									
									
									
									
								
							| @@ -19,8 +19,8 @@ | ||||
| #include <ixwebsocket/IXNetSystem.h> | ||||
| #include <ixwebsocket/IXSocket.h> | ||||
| #include <ixwebsocket/IXUserAgent.h> | ||||
| #include <spdlog/sinks/basic_file_sink.h> | ||||
| #include <spdlog/spdlog.h> | ||||
| #include <spdlog/sinks/basic_file_sink.h> | ||||
| #include <sstream> | ||||
| #include <string> | ||||
|  | ||||
| @@ -31,6 +31,10 @@ | ||||
| #define getpid _getpid | ||||
| #endif | ||||
|  | ||||
| int helper() | ||||
| { | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int main(int argc, char** argv) | ||||
| { | ||||
| @@ -120,7 +124,6 @@ int main(int argc, char** argv) | ||||
|     std::string logfile; | ||||
|     ix::SocketTLSOptions tlsOptions; | ||||
|     ix::CobraConfig cobraConfig; | ||||
|     ix::CobraBotConfig cobraBotConfig; | ||||
|     std::string ciphers; | ||||
|     std::string redirectUrl; | ||||
|     bool headersOnly = false; | ||||
| @@ -149,7 +152,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 | ||||
|  | ||||
|     auto addTLSOptions = [&tlsOptions, &verifyNone](CLI::App* app) { | ||||
|         app->add_option( | ||||
| @@ -173,24 +178,6 @@ int main(int argc, char** argv) | ||||
|         app->add_option("--rolesecret", cobraConfig.rolesecret, "Role secret")->required(); | ||||
|     }; | ||||
|  | ||||
|     auto addCobraBotConfig = [&cobraBotConfig](CLI::App* app) { | ||||
|         app->add_option("--appkey", cobraBotConfig.cobraConfig.appkey, "Appkey")->required(); | ||||
|         app->add_option("--endpoint", cobraBotConfig.cobraConfig.endpoint, "Endpoint")->required(); | ||||
|         app->add_option("--rolename", cobraBotConfig.cobraConfig.rolename, "Role name")->required(); | ||||
|         app->add_option("--rolesecret", cobraBotConfig.cobraConfig.rolesecret, "Role secret") | ||||
|             ->required(); | ||||
|         app->add_option("--channel", cobraBotConfig.channel, "Channel")->required(); | ||||
|         app->add_option("--filter", cobraBotConfig.filter, "Filter"); | ||||
|         app->add_option("--position", cobraBotConfig.position, "Position"); | ||||
|         app->add_option("--runtime", cobraBotConfig.runtime, "Runtime"); | ||||
|         app->add_option("--heartbeat", cobraBotConfig.enableHeartbeat, "Runtime"); | ||||
|         app->add_option("--heartbeat_timeout", cobraBotConfig.heartBeatTimeout, "Runtime"); | ||||
|         app->add_flag( | ||||
|             "--limit_received_events", cobraBotConfig.limitReceivedEvents, "Max events per minute"); | ||||
|         app->add_option( | ||||
|             "--max_events_per_minute", cobraBotConfig.maxEventsPerMinute, "Max events per minute"); | ||||
|     }; | ||||
|  | ||||
|     app.add_flag("--version", version, "Print ws version"); | ||||
|     app.add_option("--logfile", logfile, "path where all logs will be redirected"); | ||||
|  | ||||
| @@ -298,11 +285,15 @@ int main(int argc, char** argv) | ||||
|  | ||||
|     CLI::App* cobraSubscribeApp = app.add_subcommand("cobra_subscribe", "Cobra subscriber"); | ||||
|     cobraSubscribeApp->fallthrough(); | ||||
|     cobraSubscribeApp->add_option("--channel", channel, "Channel")->required(); | ||||
|     cobraSubscribeApp->add_option("--pidfile", pidfile, "Pid file"); | ||||
|     cobraSubscribeApp->add_option("--filter", filter, "Stream SQL Filter"); | ||||
|     cobraSubscribeApp->add_option("--position", position, "Stream position"); | ||||
|     cobraSubscribeApp->add_flag("-q", quiet, "Quiet / only display stats"); | ||||
|     cobraSubscribeApp->add_flag("--fluentd", fluentd, "Write fluentd prefix"); | ||||
|     cobraSubscribeApp->add_option("--runtime", runtime, "Runtime in seconds"); | ||||
|     addTLSOptions(cobraSubscribeApp); | ||||
|     addCobraBotConfig(cobraSubscribeApp); | ||||
|     addCobraConfig(cobraSubscribeApp); | ||||
|  | ||||
|     CLI::App* cobraPublish = app.add_subcommand("cobra_publish", "Cobra publisher"); | ||||
|     cobraPublish->fallthrough(); | ||||
| @@ -336,18 +327,32 @@ int main(int argc, char** argv) | ||||
|         ->join(); | ||||
|     cobra2statsd->add_option("--timer", timer, "Value to extract, and use as a statsd timer") | ||||
|         ->join(); | ||||
|     cobra2statsd->add_option("channel", channel, "Channel")->required(); | ||||
|     cobra2statsd->add_flag("-v", verbose, "Verbose"); | ||||
|     cobra2statsd->add_option("--pidfile", pidfile, "Pid file"); | ||||
|     cobra2statsd->add_option("--filter", filter, "Stream SQL Filter"); | ||||
|     cobra2statsd->add_option("--position", position, "Stream position"); | ||||
|     cobra2statsd->add_option("--queue_size", | ||||
|                              maxQueueSize, | ||||
|                              "Size of the queue to hold messages before they are sent to Sentry"); | ||||
|     cobra2statsd->add_option("--runtime", runtime, "Runtime in seconds"); | ||||
|     addTLSOptions(cobra2statsd); | ||||
|     addCobraBotConfig(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"); | ||||
|     addTLSOptions(cobra2sentry); | ||||
|     addCobraBotConfig(cobra2sentry); | ||||
|     addCobraConfig(cobra2sentry); | ||||
|  | ||||
|     CLI::App* cobra2redisApp = | ||||
|         app.add_subcommand("cobra_metrics_to_redis", "Cobra metrics to redis"); | ||||
| @@ -458,10 +463,6 @@ int main(int argc, char** argv) | ||||
|     cobraConfig.webSocketPerMessageDeflateOptions = ix::WebSocketPerMessageDeflateOptions(true); | ||||
|     cobraConfig.socketTLSOptions = tlsOptions; | ||||
|  | ||||
|     cobraBotConfig.cobraConfig.webSocketPerMessageDeflateOptions = | ||||
|         ix::WebSocketPerMessageDeflateOptions(true); | ||||
|     cobraBotConfig.cobraConfig.socketTLSOptions = tlsOptions; | ||||
|  | ||||
|     int ret = 1; | ||||
|     if (app.got_subcommand("transfer")) | ||||
|     { | ||||
| @@ -531,7 +532,17 @@ int main(int argc, char** argv) | ||||
|     } | ||||
|     else if (app.got_subcommand("cobra_subscribe")) | ||||
|     { | ||||
|         int64_t sentCount = ix::cobra_to_stdout_bot(cobraBotConfig, fluentd, quiet); | ||||
|         bool enableHeartbeat = true; | ||||
|         int64_t sentCount = ix::cobra_to_stdout_bot(cobraConfig, | ||||
|                                                     channel, | ||||
|                                                     filter, | ||||
|                                                     position, | ||||
|                                                     fluentd, | ||||
|                                                     quiet, | ||||
|                                                     verbose, | ||||
|                                                     maxQueueSize, | ||||
|                                                     enableHeartbeat, | ||||
|                                                     runtime); | ||||
|         ret = (int) sentCount; | ||||
|     } | ||||
|     else if (app.got_subcommand("cobra_publish")) | ||||
| @@ -552,6 +563,7 @@ int main(int argc, char** argv) | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             bool enableHeartbeat = true; | ||||
|             ix::StatsdClient statsdClient(hostname, statsdPort, prefix); | ||||
|  | ||||
|             std::string errMsg; | ||||
| @@ -563,17 +575,36 @@ int main(int argc, char** argv) | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 ret = (int) ix::cobra_to_statsd_bot( | ||||
|                     cobraBotConfig, statsdClient, fields, gauge, timer, verbose); | ||||
|                 ret = (int) ix::cobra_to_statsd_bot(cobraConfig, | ||||
|                                                     channel, | ||||
|                                                     filter, | ||||
|                                                     position, | ||||
|                                                     statsdClient, | ||||
|                                                     fields, | ||||
|                                                     gauge, | ||||
|                                                     timer, | ||||
|                                                     verbose, | ||||
|                                                     maxQueueSize, | ||||
|                                                     enableHeartbeat, | ||||
|                                                     runtime); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     else if (app.got_subcommand("cobra_to_sentry")) | ||||
|     { | ||||
|         bool enableHeartbeat = true; | ||||
|         ix::SentryClient sentryClient(dsn); | ||||
|         sentryClient.setTLSOptions(tlsOptions); | ||||
|  | ||||
|         ret = (int) ix::cobra_to_sentry_bot(cobraBotConfig, sentryClient, verbose); | ||||
|         ret = (int) ix::cobra_to_sentry_bot(cobraConfig, | ||||
|                                             channel, | ||||
|                                             filter, | ||||
|                                             position, | ||||
|                                             sentryClient, | ||||
|                                             verbose, | ||||
|                                             maxQueueSize, | ||||
|                                             enableHeartbeat, | ||||
|                                             runtime); | ||||
|     } | ||||
|     else if (app.got_subcommand("cobra_metrics_to_redis")) | ||||
|     { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user