(ws) trim ws dependencies, only depends on ixcrypto and ixcore
This commit is contained in:
		@@ -2,6 +2,10 @@
 | 
			
		||||
 | 
			
		||||
All changes to this project will be documented in this file.
 | 
			
		||||
 | 
			
		||||
## [11.0.7] - 2020-12-25
 | 
			
		||||
 | 
			
		||||
(ws) trim ws dependencies, only depends on ixcrypto and ixcore
 | 
			
		||||
 | 
			
		||||
## [11.0.6] - 2020-12-22
 | 
			
		||||
 | 
			
		||||
(build) rename makefile to makefile.dev to ease cmake BuildExternal (fix #261)
 | 
			
		||||
 
 | 
			
		||||
@@ -6,4 +6,4 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#define IX_WEBSOCKET_VERSION "11.0.6"
 | 
			
		||||
#define IX_WEBSOCKET_VERSION "11.0.7"
 | 
			
		||||
 
 | 
			
		||||
@@ -11,9 +11,6 @@ if (NOT WIN32)
 | 
			
		||||
  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic")
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread")
 | 
			
		||||
# set(CMAKE_LD_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread")
 | 
			
		||||
 | 
			
		||||
set (CMAKE_CXX_STANDARD 11)
 | 
			
		||||
 | 
			
		||||
option(USE_TLS "Add TLS support" ON)
 | 
			
		||||
@@ -21,48 +18,21 @@ option(USE_TLS "Add TLS support" ON)
 | 
			
		||||
include_directories(ws .)
 | 
			
		||||
include_directories(ws ..)
 | 
			
		||||
include_directories(ws ../third_party/cpp-linenoise)
 | 
			
		||||
include_directories(ws ../third_party/cli11)
 | 
			
		||||
include_directories(ws ../third_party/msgpack11)
 | 
			
		||||
 | 
			
		||||
find_package(JsonCpp)
 | 
			
		||||
if (NOT JSONCPP_FOUND)
 | 
			
		||||
  include_directories(../third_party/jsoncpp)
 | 
			
		||||
  set(JSONCPP_SOURCES ../third_party/jsoncpp/jsoncpp.cpp)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
if (USE_PYTHON)
 | 
			
		||||
  find_package(Python COMPONENTS Development)
 | 
			
		||||
  if (NOT Python_FOUND)
 | 
			
		||||
    message(FATAL_ERROR "Python3 not found")
 | 
			
		||||
  endif()
 | 
			
		||||
  message("Python_FOUND:${Python_FOUND}")
 | 
			
		||||
  message("Python_VERSION:${Python_VERSION}")
 | 
			
		||||
  message("Python_Development_FOUND:${Python_Development_FOUND}")
 | 
			
		||||
  message("Python_LIBRARIES:${Python_LIBRARIES}")
 | 
			
		||||
endif()
 | 
			
		||||
include(FetchContent)
 | 
			
		||||
 | 
			
		||||
add_executable(ws
 | 
			
		||||
  ../third_party/msgpack11/msgpack11.cpp
 | 
			
		||||
  ../third_party/cpp-linenoise/linenoise.cpp
 | 
			
		||||
  ${JSONCPP_SOURCES}
 | 
			
		||||
  ws.cpp)
 | 
			
		||||
 | 
			
		||||
# library with the most dependencies come first
 | 
			
		||||
target_link_libraries(ws ixbots)
 | 
			
		||||
target_link_libraries(ws ixsnake)
 | 
			
		||||
target_link_libraries(ws ixcobra)
 | 
			
		||||
target_link_libraries(ws ixsentry)
 | 
			
		||||
target_link_libraries(ws ixredis)
 | 
			
		||||
target_link_libraries(ws ixwebsocket)
 | 
			
		||||
target_link_libraries(ws ixcrypto)
 | 
			
		||||
target_link_libraries(ws ixcore)
 | 
			
		||||
 | 
			
		||||
target_link_libraries(ws spdlog)
 | 
			
		||||
if (USE_PYTHON)
 | 
			
		||||
  target_link_libraries(ws ${Python_LIBRARIES})
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
if (JSONCPP_FOUND)
 | 
			
		||||
  target_include_directories(ws PUBLIC ${JSONCPP_INCLUDE_DIRS})
 | 
			
		||||
  target_link_libraries(ws ${JSONCPP_LIBRARIES})
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
install(TARGETS ws RUNTIME DESTINATION bin)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										830
									
								
								ws/ws.cpp
									
									
									
									
									
								
							
							
						
						
									
										830
									
								
								ws/ws.cpp
									
									
									
									
									
								
							@@ -8,30 +8,18 @@
 | 
			
		||||
// Main driver for websocket utilities
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#include "IXBench.h"
 | 
			
		||||
#include "linenoise.hpp"
 | 
			
		||||
#include "nlohmann/json.hpp"
 | 
			
		||||
#include <CLI11.hpp>
 | 
			
		||||
#include <atomic>
 | 
			
		||||
#include <chrono>
 | 
			
		||||
#include <cli11/CLI11.hpp>
 | 
			
		||||
#include <condition_variable>
 | 
			
		||||
#include <fstream>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <ixbots/IXCobraMetricsToRedisBot.h>
 | 
			
		||||
#include <ixbots/IXCobraToCobraBot.h>
 | 
			
		||||
#include <ixbots/IXCobraToPythonBot.h>
 | 
			
		||||
#include <ixbots/IXCobraToSentryBot.h>
 | 
			
		||||
#include <ixbots/IXCobraToStatsdBot.h>
 | 
			
		||||
#include <ixbots/IXCobraToStdoutBot.h>
 | 
			
		||||
#include <ixcobra/IXCobraMetricsPublisher.h>
 | 
			
		||||
#include <ixcore/utils/IXCoreLogger.h>
 | 
			
		||||
#include <ixcrypto/IXBase64.h>
 | 
			
		||||
#include <ixcrypto/IXHash.h>
 | 
			
		||||
#include <ixcrypto/IXUuid.h>
 | 
			
		||||
#include <ixredis/IXRedisClient.h>
 | 
			
		||||
#include <ixredis/IXRedisServer.h>
 | 
			
		||||
#include <ixsentry/IXSentryClient.h>
 | 
			
		||||
#include <ixsnake/IXSnakeServer.h>
 | 
			
		||||
#include <ixwebsocket/IXBench.h>
 | 
			
		||||
#include <ixwebsocket/IXDNSLookup.h>
 | 
			
		||||
#include <ixwebsocket/IXGzipCodec.h>
 | 
			
		||||
#include <ixwebsocket/IXHttpClient.h>
 | 
			
		||||
@@ -45,9 +33,8 @@
 | 
			
		||||
#include <ixwebsocket/IXWebSocketHttpHeaders.h>
 | 
			
		||||
#include <ixwebsocket/IXWebSocketProxyServer.h>
 | 
			
		||||
#include <ixwebsocket/IXWebSocketServer.h>
 | 
			
		||||
#include <msgpack11/msgpack11.hpp>
 | 
			
		||||
#include <msgpack11.hpp>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include <nlohmann/json.hpp>
 | 
			
		||||
#include <queue>
 | 
			
		||||
#include <spdlog/sinks/basic_file_sink.h>
 | 
			
		||||
#include <spdlog/spdlog.h>
 | 
			
		||||
@@ -64,7 +51,6 @@
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// for convenience
 | 
			
		||||
using json = nlohmann::json;
 | 
			
		||||
using msgpack11::MsgPack;
 | 
			
		||||
 | 
			
		||||
namespace
 | 
			
		||||
@@ -647,27 +633,30 @@ namespace ix
 | 
			
		||||
 | 
			
		||||
    std::pair<std::string, std::string> WebSocketChat::decodeMessage(const std::string& str)
 | 
			
		||||
    {
 | 
			
		||||
        auto j = json::parse(str);
 | 
			
		||||
        std::string errMsg;
 | 
			
		||||
        MsgPack msg = MsgPack::parse(str, errMsg);
 | 
			
		||||
 | 
			
		||||
        std::string msg_user = j["user"];
 | 
			
		||||
        std::string msg_text = j["text"];
 | 
			
		||||
        std::string msg_user = msg["user"].string_value();
 | 
			
		||||
        std::string msg_text = msg["text"].string_value();
 | 
			
		||||
 | 
			
		||||
        return std::pair<std::string, std::string>(msg_user, msg_text);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::string WebSocketChat::encodeMessage(const std::string& text)
 | 
			
		||||
    {
 | 
			
		||||
        json j;
 | 
			
		||||
        j["user"] = _user;
 | 
			
		||||
        j["text"] = text;
 | 
			
		||||
        std::map<MsgPack, MsgPack> obj;
 | 
			
		||||
        obj["user"] = _user;
 | 
			
		||||
        obj["text"] = text;
 | 
			
		||||
 | 
			
		||||
        std::string output = j.dump();
 | 
			
		||||
        MsgPack msg(obj);
 | 
			
		||||
 | 
			
		||||
        std::string output = msg.dump();
 | 
			
		||||
        return output;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void WebSocketChat::sendMessage(const std::string& text)
 | 
			
		||||
    {
 | 
			
		||||
        _webSocket.sendText(encodeMessage(text));
 | 
			
		||||
        _webSocket.sendBinary(encodeMessage(text));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int ws_chat_main(const std::string& url, const std::string& user)
 | 
			
		||||
@@ -697,156 +686,6 @@ namespace ix
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int ws_cobra_metrics_publish_main(const ix::CobraConfig& config,
 | 
			
		||||
                                      const std::string& channel,
 | 
			
		||||
                                      const std::string& path,
 | 
			
		||||
                                      bool stress)
 | 
			
		||||
    {
 | 
			
		||||
        std::atomic<int> sentMessages(0);
 | 
			
		||||
        std::atomic<int> ackedMessages(0);
 | 
			
		||||
        CobraConnection::setPublishTrackerCallback(
 | 
			
		||||
            [&sentMessages, &ackedMessages](bool sent, bool acked) {
 | 
			
		||||
                if (sent) sentMessages++;
 | 
			
		||||
                if (acked) ackedMessages++;
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        CobraMetricsPublisher cobraMetricsPublisher;
 | 
			
		||||
        cobraMetricsPublisher.enable(true);
 | 
			
		||||
        cobraMetricsPublisher.configure(config, channel);
 | 
			
		||||
 | 
			
		||||
        while (!cobraMetricsPublisher.isAuthenticated())
 | 
			
		||||
            ;
 | 
			
		||||
 | 
			
		||||
        std::ifstream f(path);
 | 
			
		||||
        std::string str((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>());
 | 
			
		||||
 | 
			
		||||
        Json::Value data;
 | 
			
		||||
        Json::Reader reader;
 | 
			
		||||
        if (!reader.parse(str, data)) return 1;
 | 
			
		||||
 | 
			
		||||
        if (!stress)
 | 
			
		||||
        {
 | 
			
		||||
            auto msgId = cobraMetricsPublisher.push(channel, data);
 | 
			
		||||
            spdlog::info("Sent message: {}", msgId);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            // Stress mode to try to trigger server and client bugs
 | 
			
		||||
            while (true)
 | 
			
		||||
            {
 | 
			
		||||
                for (int i = 0; i < 1000; ++i)
 | 
			
		||||
                {
 | 
			
		||||
                    cobraMetricsPublisher.push(channel, data);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                cobraMetricsPublisher.suspend();
 | 
			
		||||
                cobraMetricsPublisher.resume();
 | 
			
		||||
 | 
			
		||||
                // FIXME: investigate why without this check we trigger a lock
 | 
			
		||||
                while (!cobraMetricsPublisher.isAuthenticated())
 | 
			
		||||
                    ;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Wait a bit for the message to get a chance to be sent
 | 
			
		||||
        // there isn't any ack on publish right now so it's the best we can do
 | 
			
		||||
        // FIXME: this comment is a lie now
 | 
			
		||||
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
 | 
			
		||||
 | 
			
		||||
        spdlog::info("Sent messages: {} Acked messages {}", sentMessages, ackedMessages);
 | 
			
		||||
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int ws_cobra_publish_main(const ix::CobraConfig& config,
 | 
			
		||||
                              const std::string& channel,
 | 
			
		||||
                              const std::string& path)
 | 
			
		||||
    {
 | 
			
		||||
        std::ifstream f(path);
 | 
			
		||||
        std::string str((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>());
 | 
			
		||||
 | 
			
		||||
        Json::Value data;
 | 
			
		||||
        Json::Reader reader;
 | 
			
		||||
        if (!reader.parse(str, data))
 | 
			
		||||
        {
 | 
			
		||||
            spdlog::info("Input file is not a JSON file");
 | 
			
		||||
            return 1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ix::CobraConnection conn;
 | 
			
		||||
        conn.configure(config);
 | 
			
		||||
 | 
			
		||||
        // Display incoming messages
 | 
			
		||||
        std::atomic<bool> authenticated(false);
 | 
			
		||||
        std::atomic<bool> messageAcked(false);
 | 
			
		||||
 | 
			
		||||
        conn.setEventCallback(
 | 
			
		||||
            [&conn, &channel, &data, &authenticated, &messageAcked](const CobraEventPtr& event) {
 | 
			
		||||
                if (event->type == ix::CobraEventType::Open)
 | 
			
		||||
                {
 | 
			
		||||
                    spdlog::info("Publisher connected");
 | 
			
		||||
 | 
			
		||||
                    for (auto&& it : event->headers)
 | 
			
		||||
                    {
 | 
			
		||||
                        spdlog::info("{}: {}", it.first, it.second);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else if (event->type == ix::CobraEventType::Closed)
 | 
			
		||||
                {
 | 
			
		||||
                    spdlog::info("Subscriber closed: {}", event->errMsg);
 | 
			
		||||
                }
 | 
			
		||||
                else if (event->type == ix::CobraEventType::Authenticated)
 | 
			
		||||
                {
 | 
			
		||||
                    spdlog::info("Publisher authenticated");
 | 
			
		||||
                    authenticated = true;
 | 
			
		||||
 | 
			
		||||
                    Json::Value channels;
 | 
			
		||||
                    channels[0] = channel;
 | 
			
		||||
                    auto msgId = conn.publish(channels, data);
 | 
			
		||||
 | 
			
		||||
                    spdlog::info("Published msg {}", msgId);
 | 
			
		||||
                }
 | 
			
		||||
                else if (event->type == ix::CobraEventType::Subscribed)
 | 
			
		||||
                {
 | 
			
		||||
                    spdlog::info("Publisher: subscribed to channel {}", event->subscriptionId);
 | 
			
		||||
                }
 | 
			
		||||
                else if (event->type == ix::CobraEventType::UnSubscribed)
 | 
			
		||||
                {
 | 
			
		||||
                    spdlog::info("Publisher: unsubscribed from channel {}", event->subscriptionId);
 | 
			
		||||
                }
 | 
			
		||||
                else if (event->type == ix::CobraEventType::Error)
 | 
			
		||||
                {
 | 
			
		||||
                    spdlog::error("Publisher: error {}", event->errMsg);
 | 
			
		||||
                }
 | 
			
		||||
                else if (event->type == ix::CobraEventType::Published)
 | 
			
		||||
                {
 | 
			
		||||
                    spdlog::info("Published message id {} acked", event->msgId);
 | 
			
		||||
                    messageAcked = true;
 | 
			
		||||
                }
 | 
			
		||||
                else if (event->type == ix::CobraEventType::Pong)
 | 
			
		||||
                {
 | 
			
		||||
                    spdlog::info("Received websocket pong");
 | 
			
		||||
                }
 | 
			
		||||
                else if (event->type == ix::CobraEventType::HandshakeError)
 | 
			
		||||
                {
 | 
			
		||||
                    spdlog::error("Subscriber: Handshake error: {}", event->errMsg);
 | 
			
		||||
                }
 | 
			
		||||
                else if (event->type == ix::CobraEventType::AuthenticationError)
 | 
			
		||||
                {
 | 
			
		||||
                    spdlog::error("Subscriber: Authentication error: {}", event->errMsg);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        conn.connect();
 | 
			
		||||
 | 
			
		||||
        while (!authenticated)
 | 
			
		||||
            ;
 | 
			
		||||
        while (!messageAcked)
 | 
			
		||||
            ;
 | 
			
		||||
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    class WebSocketConnect
 | 
			
		||||
    {
 | 
			
		||||
    public:
 | 
			
		||||
@@ -2229,199 +2068,6 @@ namespace ix
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int ws_redis_cli_main(const std::string& hostname, int port, const std::string& password)
 | 
			
		||||
    {
 | 
			
		||||
        RedisClient redisClient;
 | 
			
		||||
        if (!redisClient.connect(hostname, port))
 | 
			
		||||
        {
 | 
			
		||||
            spdlog::info("Cannot connect to redis host");
 | 
			
		||||
            return 1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!password.empty())
 | 
			
		||||
        {
 | 
			
		||||
            std::string authResponse;
 | 
			
		||||
            if (!redisClient.auth(password, authResponse))
 | 
			
		||||
            {
 | 
			
		||||
                std::stringstream ss;
 | 
			
		||||
                spdlog::info("Cannot authenticated to redis");
 | 
			
		||||
                return 1;
 | 
			
		||||
            }
 | 
			
		||||
            spdlog::info("Auth response: {}", authResponse);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        while (true)
 | 
			
		||||
        {
 | 
			
		||||
            // Read line
 | 
			
		||||
            std::string line;
 | 
			
		||||
            std::string prompt;
 | 
			
		||||
            prompt += hostname;
 | 
			
		||||
            prompt += ":";
 | 
			
		||||
            prompt += std::to_string(port);
 | 
			
		||||
            prompt += "> ";
 | 
			
		||||
            auto quit = linenoise::Readline(prompt.c_str(), line);
 | 
			
		||||
 | 
			
		||||
            if (quit)
 | 
			
		||||
            {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            std::stringstream ss(line);
 | 
			
		||||
            std::vector<std::string> args;
 | 
			
		||||
            std::string arg;
 | 
			
		||||
 | 
			
		||||
            while (ss.good())
 | 
			
		||||
            {
 | 
			
		||||
                ss >> arg;
 | 
			
		||||
                args.push_back(arg);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            std::string errMsg;
 | 
			
		||||
            auto response = redisClient.send(args, errMsg);
 | 
			
		||||
            if (!errMsg.empty())
 | 
			
		||||
            {
 | 
			
		||||
                spdlog::error("(error) {}", errMsg);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                if (response.first != RespType::String)
 | 
			
		||||
                {
 | 
			
		||||
                    std::cout << "(" << redisClient.getRespTypeDescription(response.first) << ")"
 | 
			
		||||
                              << " ";
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                std::cout << response.second << std::endl;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            linenoise::AddHistory(line.c_str());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int ws_redis_publish_main(const std::string& hostname,
 | 
			
		||||
                              int port,
 | 
			
		||||
                              const std::string& password,
 | 
			
		||||
                              const std::string& channel,
 | 
			
		||||
                              const std::string& message,
 | 
			
		||||
                              int count)
 | 
			
		||||
    {
 | 
			
		||||
        RedisClient redisClient;
 | 
			
		||||
        if (!redisClient.connect(hostname, port))
 | 
			
		||||
        {
 | 
			
		||||
            spdlog::info("Cannot connect to redis host");
 | 
			
		||||
            return 1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!password.empty())
 | 
			
		||||
        {
 | 
			
		||||
            std::string authResponse;
 | 
			
		||||
            if (!redisClient.auth(password, authResponse))
 | 
			
		||||
            {
 | 
			
		||||
                std::stringstream ss;
 | 
			
		||||
                spdlog::info("Cannot authenticated to redis");
 | 
			
		||||
                return 1;
 | 
			
		||||
            }
 | 
			
		||||
            spdlog::info("Auth response: {}", authResponse);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        std::string errMsg;
 | 
			
		||||
        for (int i = 0; i < count; i++)
 | 
			
		||||
        {
 | 
			
		||||
            if (!redisClient.publish(channel, message, errMsg))
 | 
			
		||||
            {
 | 
			
		||||
                spdlog::error("Error publishing to channel {} error {}", channel, errMsg);
 | 
			
		||||
                return 1;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int ws_redis_server_main(int port, const std::string& hostname)
 | 
			
		||||
    {
 | 
			
		||||
        spdlog::info("Listening on {}:{}", hostname, port);
 | 
			
		||||
 | 
			
		||||
        ix::RedisServer server(port, hostname);
 | 
			
		||||
 | 
			
		||||
        auto res = server.listen();
 | 
			
		||||
        if (!res.first)
 | 
			
		||||
        {
 | 
			
		||||
            spdlog::info(res.second);
 | 
			
		||||
            return 1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        server.start();
 | 
			
		||||
        server.wait();
 | 
			
		||||
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int ws_redis_subscribe_main(const std::string& hostname,
 | 
			
		||||
                                int port,
 | 
			
		||||
                                const std::string& password,
 | 
			
		||||
                                const std::string& channel,
 | 
			
		||||
                                bool verbose)
 | 
			
		||||
    {
 | 
			
		||||
        RedisClient redisClient;
 | 
			
		||||
        if (!redisClient.connect(hostname, port))
 | 
			
		||||
        {
 | 
			
		||||
            spdlog::info("Cannot connect to redis host");
 | 
			
		||||
            return 1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!password.empty())
 | 
			
		||||
        {
 | 
			
		||||
            std::string authResponse;
 | 
			
		||||
            if (!redisClient.auth(password, authResponse))
 | 
			
		||||
            {
 | 
			
		||||
                std::stringstream ss;
 | 
			
		||||
                spdlog::info("Cannot authenticated to redis");
 | 
			
		||||
                return 1;
 | 
			
		||||
            }
 | 
			
		||||
            spdlog::info("Auth response: {}", authResponse);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        std::atomic<int> msgPerSeconds(0);
 | 
			
		||||
        std::atomic<int> msgCount(0);
 | 
			
		||||
 | 
			
		||||
        auto callback = [&msgPerSeconds, &msgCount, verbose](const std::string& message) {
 | 
			
		||||
            if (verbose)
 | 
			
		||||
            {
 | 
			
		||||
                spdlog::info("recived: {}", message);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            msgPerSeconds++;
 | 
			
		||||
            msgCount++;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        auto responseCallback = [](const std::string& redisResponse) {
 | 
			
		||||
            spdlog::info("Redis subscribe response: {}", redisResponse);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        auto timer = [&msgPerSeconds, &msgCount] {
 | 
			
		||||
            while (true)
 | 
			
		||||
            {
 | 
			
		||||
                spdlog::info("#messages {} msg/s {}", msgCount, msgPerSeconds);
 | 
			
		||||
 | 
			
		||||
                msgPerSeconds = 0;
 | 
			
		||||
                auto duration = std::chrono::seconds(1);
 | 
			
		||||
                std::this_thread::sleep_for(duration);
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        std::thread t(timer);
 | 
			
		||||
 | 
			
		||||
        spdlog::info("Subscribing to {} ...", channel);
 | 
			
		||||
        if (!redisClient.subscribe(channel, responseCallback, callback))
 | 
			
		||||
        {
 | 
			
		||||
            spdlog::info("Error subscribing to channel {}", channel);
 | 
			
		||||
            return 1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    class WebSocketSender
 | 
			
		||||
    {
 | 
			
		||||
    public:
 | 
			
		||||
@@ -2707,132 +2353,6 @@ namespace ix
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int ws_sentry_minidump_upload(const std::string& metadataPath,
 | 
			
		||||
                                  const std::string& minidump,
 | 
			
		||||
                                  const std::string& project,
 | 
			
		||||
                                  const std::string& key,
 | 
			
		||||
                                  bool verbose)
 | 
			
		||||
    {
 | 
			
		||||
        SentryClient sentryClient((std::string()));
 | 
			
		||||
 | 
			
		||||
        // Read minidump file from disk
 | 
			
		||||
        std::string minidumpBytes = readBytes(minidump);
 | 
			
		||||
 | 
			
		||||
        // Read json data
 | 
			
		||||
        std::string sentryMetadata = readBytes(metadataPath);
 | 
			
		||||
 | 
			
		||||
        std::atomic<bool> done(false);
 | 
			
		||||
 | 
			
		||||
        sentryClient.uploadMinidump(
 | 
			
		||||
            sentryMetadata,
 | 
			
		||||
            minidumpBytes,
 | 
			
		||||
            project,
 | 
			
		||||
            key,
 | 
			
		||||
            verbose,
 | 
			
		||||
            [verbose, &done](const HttpResponsePtr& response) {
 | 
			
		||||
                if (verbose)
 | 
			
		||||
                {
 | 
			
		||||
                    for (auto it : response->headers)
 | 
			
		||||
                    {
 | 
			
		||||
                        spdlog::info("{}: {}", it.first, it.second);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    spdlog::info("Upload size: {}", response->uploadSize);
 | 
			
		||||
                    spdlog::info("Download size: {}", response->downloadSize);
 | 
			
		||||
 | 
			
		||||
                    spdlog::info("Status: {}", response->statusCode);
 | 
			
		||||
                    if (response->errorCode != HttpErrorCode::Ok)
 | 
			
		||||
                    {
 | 
			
		||||
                        spdlog::info("error message: {}", response->errorMsg);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if (response->headers["Content-Type"] != "application/octet-stream")
 | 
			
		||||
                    {
 | 
			
		||||
                        spdlog::info("body: {}", response->body);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (response->statusCode != 200)
 | 
			
		||||
                {
 | 
			
		||||
                    spdlog::error("Error sending data to sentry: {}", response->statusCode);
 | 
			
		||||
                    spdlog::error("Status: {}", response->statusCode);
 | 
			
		||||
                    spdlog::error("Response: {}", response->body);
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    spdlog::info("Event sent to sentry");
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                done = true;
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        int i = 0;
 | 
			
		||||
 | 
			
		||||
        while (!done)
 | 
			
		||||
        {
 | 
			
		||||
            std::chrono::duration<double, std::milli> duration(10);
 | 
			
		||||
            std::this_thread::sleep_for(duration);
 | 
			
		||||
 | 
			
		||||
            if (i++ > 5000) break; // wait 5 seconds max
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!done)
 | 
			
		||||
        {
 | 
			
		||||
            spdlog::error("Error: timing out trying to sent a crash to sentry");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int ws_snake_main(int port,
 | 
			
		||||
                      const std::string& hostname,
 | 
			
		||||
                      const std::string& redisHosts,
 | 
			
		||||
                      int redisPort,
 | 
			
		||||
                      const std::string& redisPassword,
 | 
			
		||||
                      bool verbose,
 | 
			
		||||
                      const std::string& appsConfigPath,
 | 
			
		||||
                      const SocketTLSOptions& socketTLSOptions,
 | 
			
		||||
                      bool disablePong,
 | 
			
		||||
                      const std::string& republishChannel)
 | 
			
		||||
    {
 | 
			
		||||
        snake::AppConfig appConfig;
 | 
			
		||||
        appConfig.port = port;
 | 
			
		||||
        appConfig.hostname = hostname;
 | 
			
		||||
        appConfig.verbose = verbose;
 | 
			
		||||
        appConfig.redisPort = redisPort;
 | 
			
		||||
        appConfig.redisPassword = redisPassword;
 | 
			
		||||
        appConfig.socketTLSOptions = socketTLSOptions;
 | 
			
		||||
        appConfig.disablePong = disablePong;
 | 
			
		||||
        appConfig.republishChannel = republishChannel;
 | 
			
		||||
 | 
			
		||||
        // Parse config file
 | 
			
		||||
        auto res = readAsString(appsConfigPath);
 | 
			
		||||
        bool found = res.first;
 | 
			
		||||
        if (!found)
 | 
			
		||||
        {
 | 
			
		||||
            spdlog::error("Cannot read content of {}", appsConfigPath);
 | 
			
		||||
            return 1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        auto apps = nlohmann::json::parse(res.second);
 | 
			
		||||
        appConfig.apps = apps["apps"];
 | 
			
		||||
 | 
			
		||||
        std::string token;
 | 
			
		||||
        std::stringstream tokenStream(redisHosts);
 | 
			
		||||
        while (std::getline(tokenStream, token, ';'))
 | 
			
		||||
        {
 | 
			
		||||
            appConfig.redisHosts.push_back(token);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Display config on the terminal for debugging
 | 
			
		||||
        dumpConfig(appConfig);
 | 
			
		||||
 | 
			
		||||
        snake::SnakeServer snakeServer(appConfig);
 | 
			
		||||
        snakeServer.runForever();
 | 
			
		||||
 | 
			
		||||
        return 0; // should never reach this
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int ws_transfer_main(int port,
 | 
			
		||||
                         const std::string& hostname,
 | 
			
		||||
                         const ix::SocketTLSOptions& tlsOptions)
 | 
			
		||||
@@ -3030,22 +2550,10 @@ int main(int argc, char** argv)
 | 
			
		||||
    std::string filter;
 | 
			
		||||
    std::string position;
 | 
			
		||||
    std::string message;
 | 
			
		||||
    std::string password;
 | 
			
		||||
    std::string prefix("ws.test.v0");
 | 
			
		||||
    std::string fields;
 | 
			
		||||
    std::string gauge;
 | 
			
		||||
    std::string timer;
 | 
			
		||||
    std::string dsn;
 | 
			
		||||
    std::string redisHosts("127.0.0.1");
 | 
			
		||||
    std::string redisPassword;
 | 
			
		||||
    std::string appsConfigPath("appsConfig.json");
 | 
			
		||||
    std::string configPath;
 | 
			
		||||
    std::string subprotocol;
 | 
			
		||||
    std::string remoteHost;
 | 
			
		||||
    std::string minidump;
 | 
			
		||||
    std::string metadata;
 | 
			
		||||
    std::string project;
 | 
			
		||||
    std::string key;
 | 
			
		||||
    std::string logfile;
 | 
			
		||||
    std::string moduleName;
 | 
			
		||||
    std::string republishChannel;
 | 
			
		||||
@@ -3055,8 +2563,6 @@ int main(int argc, char** argv)
 | 
			
		||||
    std::string filename;
 | 
			
		||||
    std::string httpHeaderAuthorization;
 | 
			
		||||
    ix::SocketTLSOptions tlsOptions;
 | 
			
		||||
    ix::CobraConfig cobraConfig;
 | 
			
		||||
    ix::CobraBotConfig cobraBotConfig;
 | 
			
		||||
    std::string ciphers;
 | 
			
		||||
    std::string redirectUrl;
 | 
			
		||||
    bool headersOnly = false;
 | 
			
		||||
@@ -3079,8 +2585,6 @@ int main(int argc, char** argv)
 | 
			
		||||
    bool disablePong = false;
 | 
			
		||||
    bool debug = false;
 | 
			
		||||
    int port = 8008;
 | 
			
		||||
    int redisPort = 6379;
 | 
			
		||||
    int statsdPort = 8125;
 | 
			
		||||
    int connectTimeOut = 60;
 | 
			
		||||
    int transferTimeout = 1800;
 | 
			
		||||
    int maxRedirects = 5;
 | 
			
		||||
@@ -3110,32 +2614,6 @@ int main(int argc, char** argv)
 | 
			
		||||
        app->add_flag("--verify_none", verifyNone, "Disable peer cert verification");
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    auto addCobraConfig = [&cobraConfig](CLI::App* app) {
 | 
			
		||||
        app->add_option("--appkey", cobraConfig.appkey, "Appkey")->required();
 | 
			
		||||
        app->add_option("--endpoint", cobraConfig.endpoint, "Endpoint")->required();
 | 
			
		||||
        app->add_option("--rolename", cobraConfig.rolename, "Role name")->required();
 | 
			
		||||
        app->add_option("--rolesecret", cobraConfig.rolesecret, "Role secret")->required();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    auto addCobraBotConfig = [&cobraBotConfig](CLI::App* app) {
 | 
			
		||||
        app->add_option("--appkey", cobraBotConfig.cobraConfig.appkey, "Appkey")->required();
 | 
			
		||||
        app->add_option("--endpoint", cobraBotConfig.cobraConfig.endpoint, "Endpoint")->required();
 | 
			
		||||
        app->add_option("--rolename", cobraBotConfig.cobraConfig.rolename, "Role name")->required();
 | 
			
		||||
        app->add_option("--rolesecret", cobraBotConfig.cobraConfig.rolesecret, "Role secret")
 | 
			
		||||
            ->required();
 | 
			
		||||
        app->add_option("--channel", cobraBotConfig.channel, "Channel")->required();
 | 
			
		||||
        app->add_option("--filter", cobraBotConfig.filter, "Filter");
 | 
			
		||||
        app->add_option("--position", cobraBotConfig.position, "Position");
 | 
			
		||||
        app->add_option("--runtime", cobraBotConfig.runtime, "Runtime");
 | 
			
		||||
        app->add_option("--heartbeat", cobraBotConfig.enableHeartbeat, "Runtime");
 | 
			
		||||
        app->add_option("--heartbeat_timeout", cobraBotConfig.heartBeatTimeout, "Runtime");
 | 
			
		||||
        app->add_flag(
 | 
			
		||||
            "--limit_received_events", cobraBotConfig.limitReceivedEvents, "Max events per minute");
 | 
			
		||||
        app->add_option(
 | 
			
		||||
            "--max_events_per_minute", cobraBotConfig.maxEventsPerMinute, "Max events per minute");
 | 
			
		||||
        app->add_option("--batch_size", cobraBotConfig.batchSize, "Subscription batch size");
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    app.add_flag("--version", version, "Print ws version");
 | 
			
		||||
    app.add_option("--logfile", logfile, "path where all logs will be redirected");
 | 
			
		||||
 | 
			
		||||
@@ -3250,129 +2728,6 @@ int main(int argc, char** argv)
 | 
			
		||||
    httpClientApp->add_option("--transfer-timeout", transferTimeout, "Transfer timeout");
 | 
			
		||||
    addTLSOptions(httpClientApp);
 | 
			
		||||
 | 
			
		||||
    CLI::App* redisCliApp = app.add_subcommand("redis_cli", "Redis cli");
 | 
			
		||||
    redisCliApp->fallthrough();
 | 
			
		||||
    redisCliApp->add_option("--port", redisPort, "Port");
 | 
			
		||||
    redisCliApp->add_option("--host", hostname, "Hostname");
 | 
			
		||||
    redisCliApp->add_option("--password", password, "Password");
 | 
			
		||||
 | 
			
		||||
    CLI::App* redisPublishApp = app.add_subcommand("redis_publish", "Redis publisher");
 | 
			
		||||
    redisPublishApp->fallthrough();
 | 
			
		||||
    redisPublishApp->add_option("--port", redisPort, "Port");
 | 
			
		||||
    redisPublishApp->add_option("--host", hostname, "Hostname");
 | 
			
		||||
    redisPublishApp->add_option("--password", password, "Password");
 | 
			
		||||
    redisPublishApp->add_option("channel", channel, "Channel")->required();
 | 
			
		||||
    redisPublishApp->add_option("message", message, "Message")->required();
 | 
			
		||||
    redisPublishApp->add_option("-c", count, "Count");
 | 
			
		||||
 | 
			
		||||
    CLI::App* redisSubscribeApp = app.add_subcommand("redis_subscribe", "Redis subscriber");
 | 
			
		||||
    redisSubscribeApp->fallthrough();
 | 
			
		||||
    redisSubscribeApp->add_option("--port", redisPort, "Port");
 | 
			
		||||
    redisSubscribeApp->add_option("--host", hostname, "Hostname");
 | 
			
		||||
    redisSubscribeApp->add_option("--password", password, "Password");
 | 
			
		||||
    redisSubscribeApp->add_option("channel", channel, "Channel")->required();
 | 
			
		||||
    redisSubscribeApp->add_flag("-v", verbose, "Verbose");
 | 
			
		||||
    redisSubscribeApp->add_option("--pidfile", pidfile, "Pid file");
 | 
			
		||||
 | 
			
		||||
    CLI::App* cobraSubscribeApp = app.add_subcommand("cobra_subscribe", "Cobra subscriber");
 | 
			
		||||
    cobraSubscribeApp->fallthrough();
 | 
			
		||||
    cobraSubscribeApp->add_option("--pidfile", pidfile, "Pid file");
 | 
			
		||||
    cobraSubscribeApp->add_flag("-q", quiet, "Quiet / only display stats");
 | 
			
		||||
    cobraSubscribeApp->add_flag("--fluentd", fluentd, "Write fluentd prefix");
 | 
			
		||||
    addTLSOptions(cobraSubscribeApp);
 | 
			
		||||
    addCobraBotConfig(cobraSubscribeApp);
 | 
			
		||||
 | 
			
		||||
    CLI::App* cobraPublish = app.add_subcommand("cobra_publish", "Cobra publisher");
 | 
			
		||||
    cobraPublish->fallthrough();
 | 
			
		||||
    cobraPublish->add_option("--channel", channel, "Channel")->required();
 | 
			
		||||
    cobraPublish->add_option("--pidfile", pidfile, "Pid file");
 | 
			
		||||
    cobraPublish->add_option("path", path, "Path to the file to send")
 | 
			
		||||
        ->required()
 | 
			
		||||
        ->check(CLI::ExistingPath);
 | 
			
		||||
    addTLSOptions(cobraPublish);
 | 
			
		||||
    addCobraConfig(cobraPublish);
 | 
			
		||||
 | 
			
		||||
    CLI::App* cobraMetricsPublish =
 | 
			
		||||
        app.add_subcommand("cobra_metrics_publish", "Cobra metrics publisher");
 | 
			
		||||
    cobraMetricsPublish->fallthrough();
 | 
			
		||||
    cobraMetricsPublish->add_option("--channel", channel, "Channel")->required();
 | 
			
		||||
    cobraMetricsPublish->add_option("--pidfile", pidfile, "Pid file");
 | 
			
		||||
    cobraMetricsPublish->add_option("path", path, "Path to the file to send")
 | 
			
		||||
        ->required()
 | 
			
		||||
        ->check(CLI::ExistingPath);
 | 
			
		||||
    cobraMetricsPublish->add_flag("--stress", stress, "Stress mode");
 | 
			
		||||
    addTLSOptions(cobraMetricsPublish);
 | 
			
		||||
    addCobraConfig(cobraMetricsPublish);
 | 
			
		||||
 | 
			
		||||
    CLI::App* cobra2statsd = app.add_subcommand("cobra_to_statsd", "Cobra to statsd");
 | 
			
		||||
    cobra2statsd->fallthrough();
 | 
			
		||||
    cobra2statsd->add_option("--host", hostname, "Statsd host");
 | 
			
		||||
    cobra2statsd->add_option("--port", statsdPort, "Statsd port");
 | 
			
		||||
    cobra2statsd->add_option("--prefix", prefix, "Statsd prefix");
 | 
			
		||||
    cobra2statsd->add_option("--fields", fields, "Extract fields for naming the event")->join();
 | 
			
		||||
    cobra2statsd->add_option("--gauge", gauge, "Value to extract, and use as a statsd gauge")
 | 
			
		||||
        ->join();
 | 
			
		||||
    cobra2statsd->add_option("--timer", timer, "Value to extract, and use as a statsd timer")
 | 
			
		||||
        ->join();
 | 
			
		||||
    cobra2statsd->add_flag("-v", verbose, "Verbose");
 | 
			
		||||
    cobra2statsd->add_option("--pidfile", pidfile, "Pid file");
 | 
			
		||||
    addTLSOptions(cobra2statsd);
 | 
			
		||||
    addCobraBotConfig(cobra2statsd);
 | 
			
		||||
 | 
			
		||||
    CLI::App* cobra2cobra = app.add_subcommand("cobra_to_cobra", "Cobra to Cobra");
 | 
			
		||||
    cobra2cobra->fallthrough();
 | 
			
		||||
    cobra2cobra->add_option("--republish", republishChannel, "Republish channel");
 | 
			
		||||
    cobra2cobra->add_option("--publisher_rolename", publisherRolename, "Publisher Role name")
 | 
			
		||||
        ->required();
 | 
			
		||||
    cobra2cobra->add_option("--publisher_rolesecret", publisherRolesecret, "Publisher Role secret")
 | 
			
		||||
        ->required();
 | 
			
		||||
    cobra2cobra->add_flag("-q", quiet, "Quiet");
 | 
			
		||||
    addTLSOptions(cobra2cobra);
 | 
			
		||||
    addCobraBotConfig(cobra2cobra);
 | 
			
		||||
 | 
			
		||||
    CLI::App* cobra2python = app.add_subcommand("cobra_to_python", "Cobra to python");
 | 
			
		||||
    cobra2python->fallthrough();
 | 
			
		||||
    cobra2python->add_option("--host", hostname, "Statsd host");
 | 
			
		||||
    cobra2python->add_option("--port", statsdPort, "Statsd port");
 | 
			
		||||
    cobra2python->add_option("--prefix", prefix, "Statsd prefix");
 | 
			
		||||
    cobra2python->add_option("--module", moduleName, "Python module");
 | 
			
		||||
    cobra2python->add_option("--pidfile", pidfile, "Pid file");
 | 
			
		||||
    addTLSOptions(cobra2python);
 | 
			
		||||
    addCobraBotConfig(cobra2python);
 | 
			
		||||
 | 
			
		||||
    CLI::App* cobra2sentry = app.add_subcommand("cobra_to_sentry", "Cobra to sentry");
 | 
			
		||||
    cobra2sentry->fallthrough();
 | 
			
		||||
    cobra2sentry->add_option("--dsn", dsn, "Sentry DSN");
 | 
			
		||||
    cobra2sentry->add_flag("-v", verbose, "Verbose");
 | 
			
		||||
    cobra2sentry->add_option("--pidfile", pidfile, "Pid file");
 | 
			
		||||
    addTLSOptions(cobra2sentry);
 | 
			
		||||
    addCobraBotConfig(cobra2sentry);
 | 
			
		||||
 | 
			
		||||
    CLI::App* cobra2redisApp =
 | 
			
		||||
        app.add_subcommand("cobra_metrics_to_redis", "Cobra metrics to redis");
 | 
			
		||||
    cobra2redisApp->fallthrough();
 | 
			
		||||
    cobra2redisApp->add_option("--pidfile", pidfile, "Pid file");
 | 
			
		||||
    cobra2redisApp->add_option("--hostname", hostname, "Redis hostname");
 | 
			
		||||
    cobra2redisApp->add_option("--port", redisPort, "Redis port");
 | 
			
		||||
    cobra2redisApp->add_flag("-v", verbose, "Verbose");
 | 
			
		||||
    addTLSOptions(cobra2redisApp);
 | 
			
		||||
    addCobraBotConfig(cobra2redisApp);
 | 
			
		||||
 | 
			
		||||
    CLI::App* snakeApp = app.add_subcommand("snake", "Snake server");
 | 
			
		||||
    snakeApp->fallthrough();
 | 
			
		||||
    snakeApp->add_option("--port", port, "Connection url");
 | 
			
		||||
    snakeApp->add_option("--host", hostname, "Hostname");
 | 
			
		||||
    snakeApp->add_option("--pidfile", pidfile, "Pid file");
 | 
			
		||||
    snakeApp->add_option("--redis_hosts", redisHosts, "Redis hosts");
 | 
			
		||||
    snakeApp->add_option("--redis_port", redisPort, "Redis hosts");
 | 
			
		||||
    snakeApp->add_option("--redis_password", redisPassword, "Redis password");
 | 
			
		||||
    snakeApp->add_option("--apps_config_path", appsConfigPath, "Path to auth data")
 | 
			
		||||
        ->check(CLI::ExistingPath);
 | 
			
		||||
    snakeApp->add_option("--republish_channel", republishChannel, "Republish channel");
 | 
			
		||||
    snakeApp->add_flag("-v", verbose, "Verbose");
 | 
			
		||||
    snakeApp->add_flag("-d", disablePong, "Disable Pongs");
 | 
			
		||||
    addTLSOptions(snakeApp);
 | 
			
		||||
 | 
			
		||||
    CLI::App* httpServerApp = app.add_subcommand("httpd", "HTTP server");
 | 
			
		||||
    httpServerApp->fallthrough();
 | 
			
		||||
    httpServerApp->add_option("--port", port, "Port");
 | 
			
		||||
@@ -3387,11 +2742,6 @@ int main(int argc, char** argv)
 | 
			
		||||
    autobahnApp->add_option("--url", url, "url");
 | 
			
		||||
    autobahnApp->add_flag("-q", quiet, "Quiet");
 | 
			
		||||
 | 
			
		||||
    CLI::App* redisServerApp = app.add_subcommand("redis_server", "Redis server");
 | 
			
		||||
    redisServerApp->fallthrough();
 | 
			
		||||
    redisServerApp->add_option("--port", port, "Port");
 | 
			
		||||
    redisServerApp->add_option("--host", hostname, "Hostname");
 | 
			
		||||
 | 
			
		||||
    CLI::App* proxyServerApp = app.add_subcommand("proxy_server", "Proxy server");
 | 
			
		||||
    proxyServerApp->fallthrough();
 | 
			
		||||
    proxyServerApp->add_option("--port", port, "Port");
 | 
			
		||||
@@ -3403,18 +2753,6 @@ int main(int argc, char** argv)
 | 
			
		||||
    addGenericOptions(proxyServerApp);
 | 
			
		||||
    addTLSOptions(proxyServerApp);
 | 
			
		||||
 | 
			
		||||
    CLI::App* minidumpApp = app.add_subcommand("upload_minidump", "Upload a minidump to sentry");
 | 
			
		||||
    minidumpApp->fallthrough();
 | 
			
		||||
    minidumpApp->add_option("--minidump", minidump, "Minidump path")
 | 
			
		||||
        ->required()
 | 
			
		||||
        ->check(CLI::ExistingPath);
 | 
			
		||||
    minidumpApp->add_option("--metadata", metadata, "Metadata path")
 | 
			
		||||
        ->required()
 | 
			
		||||
        ->check(CLI::ExistingPath);
 | 
			
		||||
    minidumpApp->add_option("--project", project, "Sentry Project")->required();
 | 
			
		||||
    minidumpApp->add_option("--key", key, "Sentry Key")->required();
 | 
			
		||||
    minidumpApp->add_flag("-v", verbose, "Verbose");
 | 
			
		||||
 | 
			
		||||
    CLI::App* dnsLookupApp = app.add_subcommand("dnslookup", "DNS lookup");
 | 
			
		||||
    dnsLookupApp->fallthrough();
 | 
			
		||||
    dnsLookupApp->add_option("host", hostname, "Hostname")->required();
 | 
			
		||||
@@ -3495,14 +2833,6 @@ int main(int argc, char** argv)
 | 
			
		||||
        spdlog::set_level(spdlog::level::info);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Cobra config
 | 
			
		||||
    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("connect"))
 | 
			
		||||
    {
 | 
			
		||||
@@ -3581,111 +2911,6 @@ int main(int argc, char** argv)
 | 
			
		||||
                                      compressRequest,
 | 
			
		||||
                                      tlsOptions);
 | 
			
		||||
    }
 | 
			
		||||
    else if (app.got_subcommand("redis_cli"))
 | 
			
		||||
    {
 | 
			
		||||
        ret = ix::ws_redis_cli_main(hostname, redisPort, password);
 | 
			
		||||
    }
 | 
			
		||||
    else if (app.got_subcommand("redis_publish"))
 | 
			
		||||
    {
 | 
			
		||||
        ret = ix::ws_redis_publish_main(hostname, redisPort, password, channel, message, count);
 | 
			
		||||
    }
 | 
			
		||||
    else if (app.got_subcommand("redis_subscribe"))
 | 
			
		||||
    {
 | 
			
		||||
        ret = ix::ws_redis_subscribe_main(hostname, redisPort, password, channel, verbose);
 | 
			
		||||
    }
 | 
			
		||||
    else if (app.got_subcommand("cobra_subscribe"))
 | 
			
		||||
    {
 | 
			
		||||
        int64_t sentCount = ix::cobra_to_stdout_bot(cobraBotConfig, fluentd, quiet);
 | 
			
		||||
        ret = (int) sentCount;
 | 
			
		||||
    }
 | 
			
		||||
    else if (app.got_subcommand("cobra_publish"))
 | 
			
		||||
    {
 | 
			
		||||
        ret = ix::ws_cobra_publish_main(cobraConfig, channel, path);
 | 
			
		||||
    }
 | 
			
		||||
    else if (app.got_subcommand("cobra_metrics_publish"))
 | 
			
		||||
    {
 | 
			
		||||
        ret = ix::ws_cobra_metrics_publish_main(cobraConfig, channel, path, stress);
 | 
			
		||||
    }
 | 
			
		||||
    else if (app.got_subcommand("cobra_to_statsd"))
 | 
			
		||||
    {
 | 
			
		||||
        if (!timer.empty() && !gauge.empty())
 | 
			
		||||
        {
 | 
			
		||||
            spdlog::error("--gauge and --timer options are exclusive. "
 | 
			
		||||
                          "you can only supply one");
 | 
			
		||||
            ret = 1;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            ix::StatsdClient statsdClient(hostname, statsdPort, prefix, verbose);
 | 
			
		||||
 | 
			
		||||
            std::string errMsg;
 | 
			
		||||
            bool initialized = statsdClient.init(errMsg);
 | 
			
		||||
            if (!initialized)
 | 
			
		||||
            {
 | 
			
		||||
                spdlog::error(errMsg);
 | 
			
		||||
                ret = 1;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                ret = (int) ix::cobra_to_statsd_bot(
 | 
			
		||||
                    cobraBotConfig, statsdClient, fields, gauge, timer, verbose);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    else if (app.got_subcommand("cobra_to_python"))
 | 
			
		||||
    {
 | 
			
		||||
        ix::StatsdClient statsdClient(hostname, statsdPort, prefix, verbose);
 | 
			
		||||
 | 
			
		||||
        std::string errMsg;
 | 
			
		||||
        bool initialized = statsdClient.init(errMsg);
 | 
			
		||||
        if (!initialized)
 | 
			
		||||
        {
 | 
			
		||||
            spdlog::error(errMsg);
 | 
			
		||||
            ret = 1;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            ret = (int) ix::cobra_to_python_bot(cobraBotConfig, statsdClient, moduleName);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    else if (app.got_subcommand("cobra_to_sentry"))
 | 
			
		||||
    {
 | 
			
		||||
        ix::SentryClient sentryClient(dsn);
 | 
			
		||||
        sentryClient.setTLSOptions(tlsOptions);
 | 
			
		||||
 | 
			
		||||
        ret = (int) ix::cobra_to_sentry_bot(cobraBotConfig, sentryClient, verbose);
 | 
			
		||||
    }
 | 
			
		||||
    else if (app.got_subcommand("cobra_metrics_to_redis"))
 | 
			
		||||
    {
 | 
			
		||||
        ix::RedisClient redisClient;
 | 
			
		||||
        if (!redisClient.connect(hostname, redisPort))
 | 
			
		||||
        {
 | 
			
		||||
            spdlog::error("Cannot connect to redis host {}:{}", redisHosts, redisPort);
 | 
			
		||||
            return 1;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            ret = (int) ix::cobra_metrics_to_redis_bot(cobraBotConfig, redisClient, verbose);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    else if (app.got_subcommand("cobra_to_cobra"))
 | 
			
		||||
    {
 | 
			
		||||
        ret = (int) ix::cobra_to_cobra_bot(
 | 
			
		||||
            cobraBotConfig, republishChannel, publisherRolename, publisherRolesecret);
 | 
			
		||||
    }
 | 
			
		||||
    else if (app.got_subcommand("snake"))
 | 
			
		||||
    {
 | 
			
		||||
        ret = ix::ws_snake_main(port,
 | 
			
		||||
                                hostname,
 | 
			
		||||
                                redisHosts,
 | 
			
		||||
                                redisPort,
 | 
			
		||||
                                redisPassword,
 | 
			
		||||
                                verbose,
 | 
			
		||||
                                appsConfigPath,
 | 
			
		||||
                                tlsOptions,
 | 
			
		||||
                                disablePong,
 | 
			
		||||
                                republishChannel);
 | 
			
		||||
    }
 | 
			
		||||
    else if (app.got_subcommand("httpd"))
 | 
			
		||||
    {
 | 
			
		||||
        ret = ix::ws_httpd_main(port, hostname, redirect, redirectUrl, debug, tlsOptions);
 | 
			
		||||
@@ -3694,10 +2919,6 @@ int main(int argc, char** argv)
 | 
			
		||||
    {
 | 
			
		||||
        ret = ix::ws_autobahn_main(url, quiet);
 | 
			
		||||
    }
 | 
			
		||||
    else if (app.got_subcommand("redis_server"))
 | 
			
		||||
    {
 | 
			
		||||
        ret = ix::ws_redis_server_main(port, hostname);
 | 
			
		||||
    }
 | 
			
		||||
    else if (app.got_subcommand("proxy_server"))
 | 
			
		||||
    {
 | 
			
		||||
        ix::RemoteUrlsMapping remoteUrlsMapping;
 | 
			
		||||
@@ -3712,12 +2933,23 @@ int main(int argc, char** argv)
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                auto jsonData = nlohmann::json::parse(res.second);
 | 
			
		||||
                auto remoteUrls = jsonData["remote_urls"];
 | 
			
		||||
                // Split by \n
 | 
			
		||||
                std::string token;
 | 
			
		||||
                std::stringstream tokenStream(res.second);
 | 
			
		||||
 | 
			
		||||
                for (auto& el : remoteUrls.items())
 | 
			
		||||
                while (std::getline(tokenStream, token))
 | 
			
		||||
                {
 | 
			
		||||
                    remoteUrlsMapping[el.key()] = el.value();
 | 
			
		||||
                    std::size_t pos = token.rfind(':');
 | 
			
		||||
 | 
			
		||||
                    // Bail out if last '.' is found
 | 
			
		||||
                    if (pos == std::string::npos) continue;
 | 
			
		||||
 | 
			
		||||
                    auto key = token.substr(0, pos);
 | 
			
		||||
                    auto val = token.substr(pos + 1);
 | 
			
		||||
 | 
			
		||||
                    spdlog::info("{}: {}", key, val);
 | 
			
		||||
 | 
			
		||||
                    remoteUrlsMapping[key] = val;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -3725,10 +2957,6 @@ int main(int argc, char** argv)
 | 
			
		||||
        ret = ix::websocket_proxy_server_main(
 | 
			
		||||
            port, hostname, tlsOptions, remoteHost, remoteUrlsMapping, verbose);
 | 
			
		||||
    }
 | 
			
		||||
    else if (app.got_subcommand("upload_minidump"))
 | 
			
		||||
    {
 | 
			
		||||
        ret = ix::ws_sentry_minidump_upload(metadata, minidump, project, key, verbose);
 | 
			
		||||
    }
 | 
			
		||||
    else if (app.got_subcommand("dnslookup"))
 | 
			
		||||
    {
 | 
			
		||||
        ret = ix::ws_dns_lookup(hostname);
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user