(websocket proxy server) add ability to map different hosts to different websocket servers, using a json config file
This commit is contained in:
parent
dcbafae35a
commit
8c5b28adce
@ -2,6 +2,10 @@
|
|||||||
|
|
||||||
All changes to this project will be documented in this file.
|
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
|
## [10.1.8] - 2020-08-12
|
||||||
|
|
||||||
(ws) on macOS, with OpenSSL or MbedTLS, use /etc/ssl/cert.pem as the system certs
|
(ws) on macOS, with OpenSSL or MbedTLS, use /etc/ssl/cert.pem as the system certs
|
||||||
|
@ -43,6 +43,7 @@ namespace ix
|
|||||||
const std::string& hostname,
|
const std::string& hostname,
|
||||||
const ix::SocketTLSOptions& tlsOptions,
|
const ix::SocketTLSOptions& tlsOptions,
|
||||||
const std::string& remoteUrl,
|
const std::string& remoteUrl,
|
||||||
|
const RemoteUrlsMapping& remoteUrlsMapping,
|
||||||
bool /*verbose*/)
|
bool /*verbose*/)
|
||||||
{
|
{
|
||||||
ix::WebSocketServer server(port, hostname);
|
ix::WebSocketServer server(port, hostname);
|
||||||
@ -53,61 +54,75 @@ namespace ix
|
|||||||
};
|
};
|
||||||
server.setConnectionStateFactory(factory);
|
server.setConnectionStateFactory(factory);
|
||||||
|
|
||||||
server.setOnConnectionCallback([remoteUrl](std::weak_ptr<ix::WebSocket> webSocket,
|
server.setOnConnectionCallback(
|
||||||
std::shared_ptr<ConnectionState> connectionState,
|
[remoteUrl, remoteUrlsMapping](std::weak_ptr<ix::WebSocket> webSocket,
|
||||||
std::unique_ptr<ConnectionInfo> connectionInfo) {
|
std::shared_ptr<ConnectionState> connectionState,
|
||||||
auto state = std::dynamic_pointer_cast<ProxyConnectionState>(connectionState);
|
std::unique_ptr<ConnectionInfo> connectionInfo) {
|
||||||
auto remoteIp = connectionInfo->remoteIp;
|
auto state = std::dynamic_pointer_cast<ProxyConnectionState>(connectionState);
|
||||||
|
auto remoteIp = connectionInfo->remoteIp;
|
||||||
|
|
||||||
// Server connection
|
// Server connection
|
||||||
state->webSocket().setOnMessageCallback(
|
state->webSocket().setOnMessageCallback(
|
||||||
[webSocket, state, remoteIp](const WebSocketMessagePtr& msg) {
|
[webSocket, state, remoteIp](const WebSocketMessagePtr& msg) {
|
||||||
if (msg->type == ix::WebSocketMessageType::Close)
|
if (msg->type == ix::WebSocketMessageType::Close)
|
||||||
{
|
|
||||||
state->setTerminated();
|
|
||||||
}
|
|
||||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
|
||||||
{
|
|
||||||
auto ws = webSocket.lock();
|
|
||||||
if (ws)
|
|
||||||
{
|
{
|
||||||
ws->send(msg->str, msg->binary);
|
state->setTerminated();
|
||||||
}
|
}
|
||||||
}
|
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||||
});
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
{
|
{
|
||||||
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)
|
|
||||||
{
|
// Client connection
|
||||||
state->webSocket().close(msg->closeInfo.code, msg->closeInfo.reason);
|
auto ws = webSocket.lock();
|
||||||
}
|
if (ws)
|
||||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
{
|
||||||
{
|
ws->setOnMessageCallback([state, remoteUrl, remoteUrlsMapping](
|
||||||
state->webSocket().send(msg->str, msg->binary);
|
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();
|
auto res = server.listen();
|
||||||
if (!res.first)
|
if (!res.first)
|
||||||
|
@ -7,14 +7,18 @@
|
|||||||
|
|
||||||
#include "IXSocketTLSOptions.h"
|
#include "IXSocketTLSOptions.h"
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <map>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
|
using RemoteUrlsMapping = std::map<std::string, std::string>;
|
||||||
|
|
||||||
int websocket_proxy_server_main(int port,
|
int websocket_proxy_server_main(int port,
|
||||||
const std::string& hostname,
|
const std::string& hostname,
|
||||||
const ix::SocketTLSOptions& tlsOptions,
|
const ix::SocketTLSOptions& tlsOptions,
|
||||||
const std::string& remoteUrl,
|
const std::string& remoteUrl,
|
||||||
|
const RemoteUrlsMapping& remoteUrlsMapping,
|
||||||
bool verbose);
|
bool verbose);
|
||||||
} // namespace ix
|
} // namespace ix
|
||||||
|
@ -6,4 +6,4 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define IX_WEBSOCKET_VERSION "10.1.8"
|
#define IX_WEBSOCKET_VERSION "10.1.9"
|
||||||
|
3
makefile
3
makefile
@ -48,6 +48,9 @@ ws_mbedtls:
|
|||||||
ws_no_ssl:
|
ws_no_ssl:
|
||||||
mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_WS=1 .. ; make -j 4)
|
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:
|
uninstall:
|
||||||
xargs rm -fv < build/install_manifest.txt
|
xargs rm -fv < build/install_manifest.txt
|
||||||
|
|
||||||
|
6
ws/proxyConfig.json
Normal file
6
ws/proxyConfig.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"remote_urls": {
|
||||||
|
"echo.localhost:8008": "ws://localhost:9009",
|
||||||
|
"cobra.localhost:8008": "ws://localhost:8765"
|
||||||
|
}
|
||||||
|
}
|
55
ws/ws.cpp
55
ws/ws.cpp
@ -24,6 +24,7 @@
|
|||||||
#include <ixwebsocket/IXSocket.h>
|
#include <ixwebsocket/IXSocket.h>
|
||||||
#include <ixwebsocket/IXUserAgent.h>
|
#include <ixwebsocket/IXUserAgent.h>
|
||||||
#include <ixwebsocket/IXWebSocketProxyServer.h>
|
#include <ixwebsocket/IXWebSocketProxyServer.h>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
#include <spdlog/sinks/basic_file_sink.h>
|
#include <spdlog/sinks/basic_file_sink.h>
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
@ -36,6 +37,32 @@
|
|||||||
#define getpid _getpid
|
#define getpid _getpid
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
std::pair<bool, std::vector<uint8_t>> load(const std::string& path)
|
||||||
|
{
|
||||||
|
std::vector<uint8_t> 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<std::streamsize>(size));
|
||||||
|
|
||||||
|
return std::make_pair(true, memblock);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<bool, std::string> 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)
|
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 redisHosts("127.0.0.1");
|
||||||
std::string redisPassword;
|
std::string redisPassword;
|
||||||
std::string appsConfigPath("appsConfig.json");
|
std::string appsConfigPath("appsConfig.json");
|
||||||
|
std::string configPath;
|
||||||
std::string subprotocol;
|
std::string subprotocol;
|
||||||
std::string remoteHost;
|
std::string remoteHost;
|
||||||
std::string minidump;
|
std::string minidump;
|
||||||
@ -452,6 +480,8 @@ int main(int argc, char** argv)
|
|||||||
proxyServerApp->add_option("--host", hostname, "Hostname");
|
proxyServerApp->add_option("--host", hostname, "Hostname");
|
||||||
proxyServerApp->add_option("--remote_host", remoteHost, "Remote Hostname");
|
proxyServerApp->add_option("--remote_host", remoteHost, "Remote Hostname");
|
||||||
proxyServerApp->add_flag("-v", verbose, "Verbose");
|
proxyServerApp->add_flag("-v", verbose, "Verbose");
|
||||||
|
proxyServerApp->add_option("--config_path", configPath, "Path to config data")
|
||||||
|
->check(CLI::ExistingPath);
|
||||||
addTLSOptions(proxyServerApp);
|
addTLSOptions(proxyServerApp);
|
||||||
|
|
||||||
CLI::App* minidumpApp = app.add_subcommand("upload_minidump", "Upload a minidump to sentry");
|
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"))
|
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"))
|
else if (app.got_subcommand("upload_minidump"))
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user