Compare commits

..

10 Commits

Author SHA1 Message Date
Benjamin Sergeant
521286ae88 fix android build + proxy work 2019-11-16 06:51:53 -08:00
Benjamin Sergeant
d122e12a1f bump version 2019-11-15 17:19:06 -08:00
Benjamin Sergeant
c26c3b6892 document new proxy command 2019-11-15 17:18:32 -08:00
Benjamin Sergeant
ed75d14c86 proxy works but crash when the connection is refused 2019-11-15 17:07:31 -08:00
Benjamin Sergeant
0841fcec44 add stub code for ws proxy server 2019-11-15 14:30:20 -08:00
Benjamin Sergeant
4e717abdb8 fix typo 2019-11-15 14:28:30 -08:00
Benjamin Sergeant
451d2b4253 update readme / add contributing notes 2019-11-15 14:21:28 -08:00
Benjamin Sergeant
10b4ee353d update changelog 2019-11-06 23:12:45 -08:00
Benjamin Sergeant
07822625b7 update readme 2019-11-06 23:12:45 -08:00
Benjamin Sergeant
c943e72c7b check max frame size (#119) 2019-10-28 21:53:31 -07:00
16 changed files with 266 additions and 25 deletions

View File

@@ -107,7 +107,7 @@ elseif (WIN32)
list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/windows/IXSetThreadName_windows.cpp)
elseif (${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/freebsd/IXSetThreadName_freebsd.cpp)
elseif (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
else()
list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/linux/IXSetThreadName_linux.cpp)
list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/IXSelectInterruptEventFd.cpp)
list( APPEND IXWEBSOCKET_HEADERS ixwebsocket/IXSelectInterruptEventFd.h)

View File

@@ -1 +1 @@
7.2.1
7.3.1

View File

@@ -4,7 +4,35 @@
IXWebSocket is a C++ library for WebSocket client and server development. It has minimal dependencies (no boost), is very simple to use and support everything you'll likely need for websocket dev (SSL, deflate compression, compiles on most platforms, etc...). HTTP client and server code is also available, but it hasn't received as much testing.
It is been used on big mobile video game titles sending and receiving tons of messages since 2017 (iOS and Android).
It is been used on big mobile video game titles sending and receiving tons of messages since 2017 (iOS and Android). It was tested on macOS, iOS, Linux, Android, Windows and FreeBSD. Two important design goals are simplicity and correctness.
```
# Required on Windows
ix::initNetSystem();
# Our websocket object
ix::WebSocket webSocket;
std::string url("ws://localhost:8080/");
webSocket.setUrl(url);
// Setup a callback to be fired (in a background thread, watch out for race conditions !)
// when a message or an event (open, close, error) is received
webSocket.setOnMessageCallback([](const ix::WebSocketMessagePtr& msg)
{
if (msg->type == ix::WebSocketMessageType::Message)
{
std::cout << msg->str << std::endl;
}
}
);
// Now that our callback is setup, we can start our background thread and receive messages
webSocket.start();
// Send a message to the server (default to TEXT mode)
webSocket.send("hello world");
```
Interested ? Go read the [docs](https://machinezone.github.io/IXWebSocket/) ! If things don't work as expected, please create an issue in github, or even better a pull request if you know how to fix your problem.

View File

@@ -1,6 +1,14 @@
# Changelog
All notable changes to this project will be documented in this file.
## [7.3.0] - 2019-11-15
- New ws command: `ws proxy_server`.
## [7.2.2] - 2019-11-01
- Tag a release + minor reformating.
## [7.2.1] - 2019-10-26
- Add unittest to IXSentryClient to lua backtrace parsing code

View File

@@ -9,6 +9,7 @@
* Linux
* Android
* Windows
* FreeBSD
## Example code
@@ -44,3 +45,7 @@ webSocket.send("hello world");
There are 2 main reasons that explain why IXWebSocket got written. First, we needed a C++ cross-platform client library, which should have few dependencies. What looked like the most solid one, [websocketpp](https://github.com/zaphoyd/websocketpp) did depend on boost and this was not an option for us. Secondly, there were other available libraries with fewer dependencies (C ones), but they required calling an explicit poll routine periodically to know if a client had received data from a server, which was not elegant.
We started by solving those 2 problems, then we added server websocket code, then an HTTP client, and finally a very simple HTTP server.
## Contributing
IXWebSocket is developed on [github](https://github.com/machinezone/IXWebSocket). We'd love to hear about how you use it ; opening up an issue in github is ok for that. If things don't work as expected, please create an issue in github, or even better a pull request if you know how to fix your problem.

View File

@@ -195,6 +195,15 @@ Server: Python/3.7 websockets/8.0.2
Upgrade: websocket
```
## Websocket proxy
```
ws proxy_server --remote_host ws://127.0.0.1:9000 -v
Listening on 127.0.0.1:8008
```
If you connect to ws://127.0.0.1:8008, the proxy will connect to ws://127.0.0.1:9000 and pass all traffic to this server.
## File transfer
```

View File

@@ -20,6 +20,7 @@ namespace snake
{
return _nonce;
}
void setNonce(const std::string& nonce)
{
_nonce = nonce;

View File

@@ -6,4 +6,4 @@
#pragma once
#define IX_WEBSOCKET_VERSION "7.1.0"
#define IX_WEBSOCKET_VERSION "7.3.1"

View File

@@ -8,9 +8,8 @@
#include "catch.hpp"
#include <iostream>
#include <string.h>
#include <ixsentry/IXSentryClient.h>
#include <string.h>
using namespace ix;
@@ -21,7 +20,9 @@ namespace ix
SECTION("Attempt to index nil")
{
SentryClient sentryClient("");
std::string stack = "Attempt to index nil[overlay]!\nstack traceback:\n\tfoo.lua:2661: in function 'getFoo'\n\tfoo.lua:1666: in function 'onUpdate'\n\tfoo.lua:1751: in function <foo.lua:1728>";
std::string stack = "Attempt to index nil[overlay]!\nstack traceback:\n\tfoo.lua:2661: "
"in function 'getFoo'\n\tfoo.lua:1666: in function "
"'onUpdate'\n\tfoo.lua:1751: in function <foo.lua:1728>";
auto frames = sentryClient.parseLuaStackTrace(stack);
REQUIRE(frames.size() == 3);
@@ -30,7 +31,8 @@ namespace ix
SECTION("Attempt to perform nil")
{
SentryClient sentryClient("");
std::string stack = "Attempt to perform nil - 1572111278.299\nstack traceback:\n\tfoo.lua:57: in function <foo.lua:53>";
std::string stack = "Attempt to perform nil - 1572111278.299\nstack "
"traceback:\n\tfoo.lua:57: in function <foo.lua:53>";
auto frames = sentryClient.parseLuaStackTrace(stack);
REQUIRE(frames.size() == 1);

View File

@@ -55,6 +55,7 @@ add_executable(ws
ws_cobra_metrics_to_redis.cpp
ws_httpd.cpp
ws_autobahn.cpp
ws_proxy_server.cpp
ws.cpp)
target_link_libraries(ws ixsnake)

View File

@@ -72,6 +72,7 @@ int main(int argc, char** argv)
std::string redisPassword;
std::string appsConfigPath("appsConfig.json");
std::string subprotocol;
std::string remoteHost;
ix::SocketTLSOptions tlsOptions;
std::string ciphers;
std::string redirectUrl;
@@ -304,6 +305,12 @@ int main(int argc, char** argv)
redisServerApp->add_option("--port", port, "Port");
redisServerApp->add_option("--host", hostname, "Hostname");
CLI::App* proxyServerApp = app.add_subcommand("proxy_server", "Proxy server");
proxyServerApp->add_option("--port", port, "Port");
proxyServerApp->add_option("--host", hostname, "Hostname");
proxyServerApp->add_option("--remote_host", remoteHost, "Remote Hostname");
proxyServerApp->add_flag("-v", verbose, "Verbose");
CLI11_PARSE(app, argc, argv);
// pid file handling
@@ -442,6 +449,10 @@ int main(int argc, char** argv)
{
ret = ix::ws_redis_server_main(port, hostname);
}
else if (app.got_subcommand("proxy_server"))
{
ret = ix::ws_proxy_server_main(port, hostname, tlsOptions, remoteHost, verbose);
}
else if (version)
{
std::cout << "ws " << ix::userAgent() << std::endl;

View File

@@ -142,4 +142,10 @@ namespace ix
int ws_autobahn_main(const std::string& url, bool quiet);
int ws_redis_server_main(int port, const std::string& hostname);
int ws_proxy_server_main(int port,
const std::string& hostname,
const ix::SocketTLSOptions& tlsOptions,
const std::string& remoteHost,
bool verbose);
} // namespace ix

View File

@@ -128,23 +128,21 @@ namespace ix
{
spdlog::info("Subscriber authenticated");
conn.subscribe(channel,
filter,
[&msgPerSeconds,
&msgCount,
&conditionVariableMutex,
&condition,
&queue](const Json::Value& msg) {
{
std::unique_lock<std::mutex> lock(conditionVariableMutex);
queue.push(msg);
}
conn.subscribe(
channel,
filter,
[&msgPerSeconds, &msgCount, &conditionVariableMutex, &condition, &queue](
const Json::Value& msg) {
{
std::unique_lock<std::mutex> lock(conditionVariableMutex);
queue.push(msg);
}
condition.notify_one();
condition.notify_one();
msgPerSeconds++;
msgCount++;
});
msgPerSeconds++;
msgCount++;
});
}
else if (eventType == ix::CobraConnection_EventType_Subscribed)
{

View File

@@ -4,12 +4,12 @@
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
*/
#include <ixsentry/IXSentryClient.h>
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <iostream>
#include <ixcobra/IXCobraConnection.h>
#include <ixsentry/IXSentryClient.h>
#include <mutex>
#include <queue>
#include <spdlog/spdlog.h>

View File

@@ -1,5 +1,5 @@
/*
* ws_broadcast_server.cpp
* ws_echo_server.cpp
* Author: Benjamin Sergeant
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
*/

172
ws/ws_proxy_server.cpp Normal file
View File

@@ -0,0 +1,172 @@
/*
* ws_proxy_server.cpp
* Author: Benjamin Sergeant
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
*/
#include <iostream>
#include <ixwebsocket/IXWebSocketServer.h>
#include <sstream>
namespace ix
{
class ProxyConnectionState : public ix::ConnectionState
{
public:
ProxyConnectionState()
: _connected(false)
{
}
ix::WebSocket& webSocket()
{
return _serverWebSocket;
}
bool isConnected()
{
return _connected;
}
void setConnected()
{
_connected = true;
}
private:
ix::WebSocket _serverWebSocket;
bool _connected;
};
int ws_proxy_server_main(int port,
const std::string& hostname,
const ix::SocketTLSOptions& tlsOptions,
const std::string& remoteUrl,
bool verbose)
{
std::cout << "Listening on " << hostname << ":" << port << std::endl;
ix::WebSocketServer server(port, hostname);
server.setTLSOptions(tlsOptions);
auto factory = []() -> std::shared_ptr<ix::ConnectionState> {
return std::make_shared<ProxyConnectionState>();
};
server.setConnectionStateFactory(factory);
server.setOnConnectionCallback([remoteUrl,
verbose](std::shared_ptr<ix::WebSocket> webSocket,
std::shared_ptr<ConnectionState> connectionState) {
auto state = std::dynamic_pointer_cast<ProxyConnectionState>(connectionState);
// Server connection
state->webSocket().setOnMessageCallback([webSocket, state, verbose](
const WebSocketMessagePtr& msg) {
if (msg->type == ix::WebSocketMessageType::Open)
{
std::cerr << "New connection" << std::endl;
std::cerr << "id: " << state->getId() << std::endl;
std::cerr << "Uri: " << msg->openInfo.uri << std::endl;
std::cerr << "Headers:" << std::endl;
for (auto it : msg->openInfo.headers)
{
std::cerr << it.first << ": " << it.second << std::endl;
}
state->setConnected();
}
else if (msg->type == ix::WebSocketMessageType::Close)
{
std::cerr << "Closed connection"
<< " code " << msg->closeInfo.code << " reason "
<< msg->closeInfo.reason << std::endl;
}
else if (msg->type == ix::WebSocketMessageType::Error)
{
std::stringstream ss;
ss << "Connection error: " << msg->errorInfo.reason << std::endl;
ss << "#retries: " << msg->errorInfo.retries << std::endl;
ss << "Wait time(ms): " << msg->errorInfo.wait_time << std::endl;
ss << "HTTP Status: " << msg->errorInfo.http_status << std::endl;
std::cerr << ss.str();
webSocket->close(msg->closeInfo.code, msg->closeInfo.reason);
}
else if (msg->type == ix::WebSocketMessageType::Message)
{
std::cerr << "Received " << msg->wireSize << " bytes from server" << std::endl;
if (verbose)
{
std::cerr << "payload " << msg->str << std::endl;
}
webSocket->send(msg->str, msg->binary);
}
});
// Client connection
webSocket->setOnMessageCallback([state, remoteUrl, verbose](
const WebSocketMessagePtr& msg) {
if (msg->type == ix::WebSocketMessageType::Open)
{
std::cerr << "New connection" << std::endl;
std::cerr << "id: " << state->getId() << std::endl;
std::cerr << "Uri: " << msg->openInfo.uri << std::endl;
std::cerr << "Headers:" << std::endl;
for (auto it : msg->openInfo.headers)
{
std::cerr << it.first << ": " << it.second << std::endl;
}
// Connect to the 'real' server
std::string url(remoteUrl);
url += msg->openInfo.uri;
state->webSocket().setUrl(url);
state->webSocket().start();
// we should sleep here for a bit until we've established the
// connection with the remote server
while (!state->isConnected())
{
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
else if (msg->type == ix::WebSocketMessageType::Close)
{
std::cerr << "Closed connection"
<< " code " << msg->closeInfo.code << " reason "
<< msg->closeInfo.reason << std::endl;
state->webSocket().close(msg->closeInfo.code, msg->closeInfo.reason);
}
else if (msg->type == ix::WebSocketMessageType::Error)
{
std::stringstream ss;
ss << "Connection error: " << msg->errorInfo.reason << std::endl;
ss << "#retries: " << msg->errorInfo.retries << std::endl;
ss << "Wait time(ms): " << msg->errorInfo.wait_time << std::endl;
ss << "HTTP Status: " << msg->errorInfo.http_status << std::endl;
std::cerr << ss.str();
}
else if (msg->type == ix::WebSocketMessageType::Message)
{
std::cerr << "Received " << msg->wireSize << " bytes from client" << std::endl;
if (verbose)
{
std::cerr << "payload " << msg->str << std::endl;
}
state->webSocket().send(msg->str, msg->binary);
}
});
});
auto res = server.listen();
if (!res.first)
{
std::cerr << res.second << std::endl;
return 1;
}
server.start();
server.wait();
return 0;
}
} // namespace ix