diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 07a9fefc..31a90840 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -2,6 +2,10 @@ All changes to this project will be documented in this file. +## [10.1.9] - 2020-08-13 + +(websocket proxy server) add ability to map different hosts to different websocket servers, using a json config file + ## [10.1.8] - 2020-08-12 (ws) on macOS, with OpenSSL or MbedTLS, use /etc/ssl/cert.pem as the system certs diff --git a/ixwebsocket/IXWebSocketProxyServer.cpp b/ixwebsocket/IXWebSocketProxyServer.cpp index 140b21ec..71526fc0 100644 --- a/ixwebsocket/IXWebSocketProxyServer.cpp +++ b/ixwebsocket/IXWebSocketProxyServer.cpp @@ -43,6 +43,7 @@ namespace ix const std::string& hostname, const ix::SocketTLSOptions& tlsOptions, const std::string& remoteUrl, + const RemoteUrlsMapping& remoteUrlsMapping, bool /*verbose*/) { ix::WebSocketServer server(port, hostname); @@ -53,61 +54,75 @@ namespace ix }; server.setConnectionStateFactory(factory); - server.setOnConnectionCallback([remoteUrl](std::weak_ptr webSocket, - std::shared_ptr connectionState, - std::unique_ptr connectionInfo) { - auto state = std::dynamic_pointer_cast(connectionState); - auto remoteIp = connectionInfo->remoteIp; + server.setOnConnectionCallback( + [remoteUrl, remoteUrlsMapping](std::weak_ptr webSocket, + std::shared_ptr connectionState, + std::unique_ptr connectionInfo) { + auto state = std::dynamic_pointer_cast(connectionState); + auto remoteIp = connectionInfo->remoteIp; - // Server connection - state->webSocket().setOnMessageCallback( - [webSocket, state, remoteIp](const WebSocketMessagePtr& msg) { - if (msg->type == ix::WebSocketMessageType::Close) - { - state->setTerminated(); - } - else if (msg->type == ix::WebSocketMessageType::Message) - { - auto ws = webSocket.lock(); - if (ws) + // Server connection + state->webSocket().setOnMessageCallback( + [webSocket, state, remoteIp](const WebSocketMessagePtr& msg) { + if (msg->type == ix::WebSocketMessageType::Close) { - ws->send(msg->str, msg->binary); + state->setTerminated(); } - } - }); - - // Client connection - auto ws = webSocket.lock(); - if (ws) - { - ws->setOnMessageCallback([state, remoteUrl](const WebSocketMessagePtr& msg) { - if (msg->type == ix::WebSocketMessageType::Open) - { - // Connect to the 'real' server - std::string url(remoteUrl); - url += msg->openInfo.uri; - state->webSocket().setUrl(url); - state->webSocket().disableAutomaticReconnection(); - state->webSocket().start(); - - // we should sleep here for a bit until we've established the - // connection with the remote server - while (state->webSocket().getReadyState() != ReadyState::Open) + else if (msg->type == ix::WebSocketMessageType::Message) { - std::this_thread::sleep_for(std::chrono::milliseconds(10)); + auto ws = webSocket.lock(); + if (ws) + { + ws->send(msg->str, msg->binary); + } } - } - else if (msg->type == ix::WebSocketMessageType::Close) - { - state->webSocket().close(msg->closeInfo.code, msg->closeInfo.reason); - } - else if (msg->type == ix::WebSocketMessageType::Message) - { - state->webSocket().send(msg->str, msg->binary); - } - }); - } - }); + }); + + // Client connection + auto ws = webSocket.lock(); + if (ws) + { + ws->setOnMessageCallback([state, remoteUrl, remoteUrlsMapping]( + const WebSocketMessagePtr& msg) { + if (msg->type == ix::WebSocketMessageType::Open) + { + // Connect to the 'real' server + std::string url(remoteUrl); + + // maybe we want a different url based on the mapping + std::string host = msg->openInfo.headers["Host"]; + auto it = remoteUrlsMapping.find(host); + if (it != remoteUrlsMapping.end()) + { + url = it->second; + } + + // append the uri to form the full url + // (say ws://localhost:1234/foo/?bar=baz) + url += msg->openInfo.uri; + + state->webSocket().setUrl(url); + state->webSocket().disableAutomaticReconnection(); + state->webSocket().start(); + + // we should sleep here for a bit until we've established the + // connection with the remote server + while (state->webSocket().getReadyState() != ReadyState::Open) + { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + } + else if (msg->type == ix::WebSocketMessageType::Close) + { + state->webSocket().close(msg->closeInfo.code, msg->closeInfo.reason); + } + else if (msg->type == ix::WebSocketMessageType::Message) + { + state->webSocket().send(msg->str, msg->binary); + } + }); + } + }); auto res = server.listen(); if (!res.first) diff --git a/ixwebsocket/IXWebSocketProxyServer.h b/ixwebsocket/IXWebSocketProxyServer.h index 268ab4cd..7a55e472 100644 --- a/ixwebsocket/IXWebSocketProxyServer.h +++ b/ixwebsocket/IXWebSocketProxyServer.h @@ -7,14 +7,18 @@ #include "IXSocketTLSOptions.h" #include +#include #include #include namespace ix { + using RemoteUrlsMapping = std::map; + int websocket_proxy_server_main(int port, const std::string& hostname, const ix::SocketTLSOptions& tlsOptions, const std::string& remoteUrl, + const RemoteUrlsMapping& remoteUrlsMapping, bool verbose); } // namespace ix diff --git a/ixwebsocket/IXWebSocketVersion.h b/ixwebsocket/IXWebSocketVersion.h index 1e9d75d7..4384ad28 100644 --- a/ixwebsocket/IXWebSocketVersion.h +++ b/ixwebsocket/IXWebSocketVersion.h @@ -6,4 +6,4 @@ #pragma once -#define IX_WEBSOCKET_VERSION "10.1.8" +#define IX_WEBSOCKET_VERSION "10.1.9" diff --git a/makefile b/makefile index 139d2fb9..103d7295 100644 --- a/makefile +++ b/makefile @@ -48,6 +48,9 @@ ws_mbedtls: ws_no_ssl: mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_WS=1 .. ; make -j 4) +ws_no_python: + mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=MinSizeRel -DUSE_TLS=1 -DUSE_WS=1 .. ; ninja install) + uninstall: xargs rm -fv < build/install_manifest.txt diff --git a/ws/proxyConfig.json b/ws/proxyConfig.json new file mode 100644 index 00000000..b1020f54 --- /dev/null +++ b/ws/proxyConfig.json @@ -0,0 +1,6 @@ +{ + "remote_urls": { + "echo.localhost:8008": "ws://localhost:9009", + "cobra.localhost:8008": "ws://localhost:8765" + } +} diff --git a/ws/ws.cpp b/ws/ws.cpp index 240f4090..e65bf99b 100644 --- a/ws/ws.cpp +++ b/ws/ws.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -36,6 +37,32 @@ #define getpid _getpid #endif +namespace +{ + std::pair> load(const std::string& path) + { + std::vector memblock; + + std::ifstream file(path); + if (!file.is_open()) return std::make_pair(false, memblock); + + file.seekg(0, file.end); + std::streamoff size = file.tellg(); + file.seekg(0, file.beg); + + memblock.resize((size_t) size); + file.read((char*) &memblock.front(), static_cast(size)); + + return std::make_pair(true, memblock); + } + + std::pair readAsString(const std::string& path) + { + auto res = load(path); + auto vec = res.second; + return std::make_pair(res.first, std::string(vec.begin(), vec.end())); + } +} // namespace int main(int argc, char** argv) { @@ -117,6 +144,7 @@ int main(int argc, char** argv) 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; @@ -452,6 +480,8 @@ int main(int argc, char** argv) proxyServerApp->add_option("--host", hostname, "Hostname"); proxyServerApp->add_option("--remote_host", remoteHost, "Remote Hostname"); proxyServerApp->add_flag("-v", verbose, "Verbose"); + proxyServerApp->add_option("--config_path", configPath, "Path to config data") + ->check(CLI::ExistingPath); addTLSOptions(proxyServerApp); CLI::App* minidumpApp = app.add_subcommand("upload_minidump", "Upload a minidump to sentry"); @@ -724,7 +754,30 @@ int main(int argc, char** argv) } else if (app.got_subcommand("proxy_server")) { - ret = ix::websocket_proxy_server_main(port, hostname, tlsOptions, remoteHost, verbose); + ix::RemoteUrlsMapping remoteUrlsMapping; + + if (!configPath.empty()) + { + auto res = readAsString(configPath); + bool found = res.first; + if (!found) + { + spdlog::error("Cannot read config file {} from disk", configPath); + } + else + { + auto jsonData = nlohmann::json::parse(res.second); + auto remoteUrls = jsonData["remote_urls"]; + + for (auto& el : remoteUrls.items()) + { + remoteUrlsMapping[el.key()] = el.value(); + } + } + } + + ret = ix::websocket_proxy_server_main( + port, hostname, tlsOptions, remoteHost, remoteUrlsMapping, verbose); } else if (app.got_subcommand("upload_minidump")) {