Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
499262f752 |
@ -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)
|
||||
else()
|
||||
elseif (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||
list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/linux/IXSetThreadName_linux.cpp)
|
||||
list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/IXSelectInterruptEventFd.cpp)
|
||||
list( APPEND IXWEBSOCKET_HEADERS ixwebsocket/IXSelectInterruptEventFd.h)
|
||||
|
@ -1 +1 @@
|
||||
7.4.0
|
||||
7.2.1
|
||||
|
@ -19,7 +19,6 @@ RUN [ "make", "ws_install" ]
|
||||
FROM alpine as runtime
|
||||
|
||||
RUN apk add --no-cache libstdc++
|
||||
RUN apk add --no-cache strace
|
||||
|
||||
RUN addgroup -S app && adduser -S -G app app
|
||||
COPY --chown=app:app --from=build /usr/local/bin/ws /usr/local/bin/ws
|
||||
|
30
README.md
30
README.md
@ -4,35 +4,7 @@
|
||||
|
||||
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 was tested on macOS, iOS, Linux, Android, Windows and FreeBSD. Two important design goals are simplicity and correctness.
|
||||
|
||||
```cpp
|
||||
# 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");
|
||||
```
|
||||
It is been used on big mobile video game titles sending and receiving tons of messages since 2017 (iOS and Android).
|
||||
|
||||
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.
|
||||
|
||||
|
@ -1,59 +1,43 @@
|
||||
version: "3"
|
||||
services:
|
||||
# snake:
|
||||
# image: bsergean/ws:build
|
||||
# entrypoint: ws snake --port 8767 --host 0.0.0.0 --redis_hosts redis1
|
||||
# ports:
|
||||
# - "8767:8767"
|
||||
# networks:
|
||||
# - ws-net
|
||||
# depends_on:
|
||||
# - redis1
|
||||
|
||||
# proxy:
|
||||
# image: bsergean/ws:build
|
||||
# entrypoint: strace ws proxy_server --remote_host 'wss://cobra.addsrv.com' --host 0.0.0.0 --port 8765 -v
|
||||
# ports:
|
||||
# - "8765:8765"
|
||||
# networks:
|
||||
# - ws-net
|
||||
|
||||
pyproxy:
|
||||
image: bsergean/ws_proxy:build
|
||||
entrypoint: /usr/bin/ws_proxy.py --remote_url 'wss://cobra.addsrv.com' --host 0.0.0.0 --port 8765
|
||||
snake:
|
||||
image: bsergean/ws:build
|
||||
entrypoint: ws snake --port 8765 --host 0.0.0.0 --redis_hosts redis1
|
||||
ports:
|
||||
- "8765:8765"
|
||||
networks:
|
||||
- ws-net
|
||||
depends_on:
|
||||
- redis1
|
||||
|
||||
# ws:
|
||||
# security_opt:
|
||||
# - seccomp:unconfined
|
||||
# cap_add:
|
||||
# - SYS_PTRACE
|
||||
# stdin_open: true
|
||||
# tty: true
|
||||
# image: bsergean/ws:build
|
||||
# entrypoint: sh
|
||||
# networks:
|
||||
# - ws-net
|
||||
# depends_on:
|
||||
# - redis1
|
||||
#
|
||||
# redis1:
|
||||
# image: redis:alpine
|
||||
# networks:
|
||||
# - ws-net
|
||||
#
|
||||
# statsd:
|
||||
# image: jaconel/statsd
|
||||
# ports:
|
||||
# - "8125:8125"
|
||||
# environment:
|
||||
# - STATSD_DUMP_MSG=true
|
||||
# - GRAPHITE_HOST=127.0.0.1
|
||||
# networks:
|
||||
# - ws-net
|
||||
ws:
|
||||
security_opt:
|
||||
- seccomp:unconfined
|
||||
cap_add:
|
||||
- SYS_PTRACE
|
||||
stdin_open: true
|
||||
tty: true
|
||||
image: bsergean/ws:build
|
||||
entrypoint: bash
|
||||
networks:
|
||||
- ws-net
|
||||
depends_on:
|
||||
- redis1
|
||||
|
||||
redis1:
|
||||
image: redis:alpine
|
||||
networks:
|
||||
- ws-net
|
||||
|
||||
statsd:
|
||||
image: jaconel/statsd
|
||||
ports:
|
||||
- "8125:8125"
|
||||
environment:
|
||||
- STATSD_DUMP_MSG=true
|
||||
- GRAPHITE_HOST=127.0.0.1
|
||||
networks:
|
||||
- ws-net
|
||||
|
||||
networks:
|
||||
ws-net:
|
||||
|
@ -1,31 +1,6 @@
|
||||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [7.4.0] - 2019-11-25
|
||||
|
||||
- (http client) Add support for multipart HTTP POST upload
|
||||
- (ixsentry) Add support for uploading a minidump to sentry
|
||||
|
||||
## [7.3.5] - 2019-11-20
|
||||
|
||||
- On Darwin SSL, add ability to skip peer verification.
|
||||
|
||||
## [7.3.4] - 2019-11-20
|
||||
|
||||
- 32-bits compile fix, courtesy of @fcojavmc
|
||||
|
||||
## [7.3.1] - 2019-11-16
|
||||
|
||||
- ws proxy_server / remote server close not forwarded to the client
|
||||
|
||||
## [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
|
||||
|
@ -9,7 +9,6 @@
|
||||
* Linux
|
||||
* Android
|
||||
* Windows
|
||||
* FreeBSD
|
||||
|
||||
## Example code
|
||||
|
||||
@ -45,7 +44,3 @@ 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.
|
||||
|
@ -195,15 +195,6 @@ 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
|
||||
|
||||
```
|
||||
|
@ -19,7 +19,7 @@ add_library(ixsentry STATIC
|
||||
set(IXSENTRY_INCLUDE_DIRS
|
||||
.
|
||||
..
|
||||
../ixcore
|
||||
../third_party)
|
||||
../third_party
|
||||
../third_party/spdlog/include)
|
||||
|
||||
target_include_directories( ixsentry PUBLIC ${IXSENTRY_INCLUDE_DIRS} )
|
||||
|
@ -8,9 +8,8 @@
|
||||
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <ixwebsocket/IXWebSocketHttpHeaders.h>
|
||||
#include <ixcore/utils/IXCoreLogger.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
|
||||
namespace ix
|
||||
@ -19,7 +18,6 @@ namespace ix
|
||||
: _dsn(dsn)
|
||||
, _validDsn(false)
|
||||
, _luaFrameRegex("\t([^/]+):([0-9]+): in function ['<]([^/]+)['>]")
|
||||
, _httpClient(std::make_shared<HttpClient>(true))
|
||||
{
|
||||
const std::regex dsnRegex("(http[s]?)://([^:]+):([^@]+)@([^/]+)/([0-9]+)");
|
||||
std::smatch group;
|
||||
@ -171,64 +169,39 @@ namespace ix
|
||||
|
||||
std::pair<HttpResponsePtr, std::string> SentryClient::send(const Json::Value& msg, bool verbose)
|
||||
{
|
||||
auto args = _httpClient->createRequest();
|
||||
auto args = _httpClient.createRequest();
|
||||
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) { ix::IXCoreLogger::Log(msg.c_str()); };
|
||||
args->logger = [](const std::string& msg) { spdlog::info("request logger: {}", msg); };
|
||||
|
||||
std::string body = computePayload(msg);
|
||||
HttpResponsePtr response = _httpClient->post(_url, body, args);
|
||||
HttpResponsePtr response = _httpClient.post(_url, body, args);
|
||||
|
||||
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("payload: {}", response->payload);
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_pair(response, body);
|
||||
}
|
||||
|
||||
// https://sentry.io/api/12345/minidump?sentry_key=abcdefgh");
|
||||
std::string SentryClient::computeUrl(const std::string& project, const std::string& key)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "https://sentry.io/api/"
|
||||
<< project
|
||||
<< "/minidump?sentry_key="
|
||||
<< key;
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
//
|
||||
// curl -v -X POST -F upload_file_minidump=@ws/crash.dmp 'https://sentry.io/api/123456/minidump?sentry_key=12344567890'
|
||||
//
|
||||
void SentryClient::uploadMinidump(
|
||||
const std::string& sentryMetadata,
|
||||
const std::string& minidumpBytes,
|
||||
const std::string& project,
|
||||
const std::string& key,
|
||||
bool verbose,
|
||||
const OnResponseCallback& onResponseCallback)
|
||||
{
|
||||
std::string multipartBoundary = _httpClient->generateMultipartBoundary();
|
||||
|
||||
auto args = _httpClient->createRequest();
|
||||
args->verb = HttpClient::kPost;
|
||||
args->connectTimeout = 60;
|
||||
args->transferTimeout = 5 * 60;
|
||||
args->followRedirects = true;
|
||||
args->verbose = verbose;
|
||||
args->multipartBoundary = multipartBoundary;
|
||||
args->logger = [](const std::string& msg) { ix::IXCoreLogger::Log(msg.c_str()); };
|
||||
|
||||
HttpFormDataParameters httpFormDataParameters;
|
||||
httpFormDataParameters["upload_file_minidump"] = minidumpBytes;
|
||||
|
||||
HttpParameters httpParameters;
|
||||
httpParameters["sentry"] = sentryMetadata;
|
||||
|
||||
args->url = computeUrl(project, key);
|
||||
args->body = _httpClient->serializeHttpFormDataParameters(multipartBoundary, httpFormDataParameters, httpParameters);
|
||||
|
||||
|
||||
_httpClient->performRequest(args, onResponseCallback);
|
||||
}
|
||||
} // namespace ix
|
||||
|
@ -10,7 +10,6 @@
|
||||
#include <ixwebsocket/IXHttpClient.h>
|
||||
#include <jsoncpp/json/json.h>
|
||||
#include <regex>
|
||||
#include <memory>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
@ -24,24 +23,12 @@ namespace ix
|
||||
|
||||
Json::Value parseLuaStackTrace(const std::string& stack);
|
||||
|
||||
void uploadMinidump(
|
||||
const std::string& sentryMetadata,
|
||||
const std::string& minidumpBytes,
|
||||
const std::string& project,
|
||||
const std::string& key,
|
||||
bool verbose,
|
||||
const OnResponseCallback& onResponseCallback);
|
||||
|
||||
private:
|
||||
int64_t getTimestamp();
|
||||
std::string computeAuthHeader();
|
||||
std::string getIso8601();
|
||||
std::string computePayload(const Json::Value& msg);
|
||||
|
||||
std::string computeUrl(const std::string& project, const std::string& key);
|
||||
|
||||
void displayReponse(HttpResponsePtr response);
|
||||
|
||||
std::string _dsn;
|
||||
bool _validDsn;
|
||||
std::string _url;
|
||||
@ -54,7 +41,7 @@ namespace ix
|
||||
|
||||
std::regex _luaFrameRegex;
|
||||
|
||||
std::shared_ptr<HttpClient> _httpClient;
|
||||
HttpClient _httpClient;
|
||||
};
|
||||
|
||||
} // namespace ix
|
||||
|
@ -20,7 +20,6 @@ namespace snake
|
||||
{
|
||||
return _nonce;
|
||||
}
|
||||
|
||||
void setNonce(const std::string& nonce)
|
||||
{
|
||||
_nonce = nonce;
|
||||
|
@ -66,7 +66,6 @@ namespace ix
|
||||
|
||||
using HttpResponsePtr = std::shared_ptr<HttpResponse>;
|
||||
using HttpParameters = std::map<std::string, std::string>;
|
||||
using HttpFormDataParameters = std::map<std::string, std::string>;
|
||||
using Logger = std::function<void(const std::string&)>;
|
||||
using OnResponseCallback = std::function<void(const HttpResponsePtr&)>;
|
||||
|
||||
@ -76,7 +75,6 @@ namespace ix
|
||||
std::string verb;
|
||||
WebSocketHttpHeaders extraHeaders;
|
||||
std::string body;
|
||||
std::string multipartBoundary;
|
||||
int connectTimeout;
|
||||
int transferTimeout;
|
||||
bool followRedirects;
|
||||
|
@ -13,7 +13,6 @@
|
||||
#include <assert.h>
|
||||
#include <cstring>
|
||||
#include <iomanip>
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <zlib.h>
|
||||
@ -199,16 +198,8 @@ namespace ix
|
||||
// Set default Content-Type if unspecified
|
||||
if (args->extraHeaders.find("Content-Type") == args->extraHeaders.end())
|
||||
{
|
||||
if (args->multipartBoundary.empty())
|
||||
{
|
||||
ss << "Content-Type: application/x-www-form-urlencoded"
|
||||
<< "\r\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
ss << "Content-Type: multipart/form-data; boundary=" << args->multipartBoundary
|
||||
<< "\r\n";
|
||||
}
|
||||
ss << "Content-Type: application/x-www-form-urlencoded"
|
||||
<< "\r\n";
|
||||
}
|
||||
ss << "\r\n";
|
||||
ss << body;
|
||||
@ -606,53 +597,6 @@ namespace ix
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string HttpClient::serializeHttpFormDataParameters(
|
||||
const std::string& multipartBoundary,
|
||||
const HttpFormDataParameters& httpFormDataParameters,
|
||||
const HttpParameters& httpParameters)
|
||||
{
|
||||
//
|
||||
// --AaB03x
|
||||
// Content-Disposition: form-data; name="submit-name"
|
||||
|
||||
// Larry
|
||||
// --AaB03x
|
||||
// Content-Disposition: form-data; name="foo.txt"; filename="file1.txt"
|
||||
// Content-Type: text/plain
|
||||
|
||||
// ... contents of file1.txt ...
|
||||
// --AaB03x--
|
||||
//
|
||||
std::stringstream ss;
|
||||
|
||||
for (auto&& it : httpFormDataParameters)
|
||||
{
|
||||
ss << "--" << multipartBoundary << "\r\n"
|
||||
<< "Content-Disposition:"
|
||||
<< " form-data; name=\"" << it.first << "\";"
|
||||
<< " filename=\"" << it.first << "\""
|
||||
<< "\r\n"
|
||||
<< "Content-Type: application/octet-stream"
|
||||
<< "\r\n"
|
||||
<< "\r\n"
|
||||
<< it.second << "\r\n";
|
||||
}
|
||||
|
||||
for (auto&& it : httpParameters)
|
||||
{
|
||||
ss << "--" << multipartBoundary << "\r\n"
|
||||
<< "Content-Disposition:"
|
||||
<< " form-data; name=\"" << it.first << "\";"
|
||||
<< "\r\n"
|
||||
<< "\r\n"
|
||||
<< it.second << "\r\n";
|
||||
}
|
||||
|
||||
ss << "--" << multipartBoundary << "\r\n";
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
bool HttpClient::gzipInflate(const std::string& in, std::string& out)
|
||||
{
|
||||
z_stream inflateState;
|
||||
@ -705,16 +649,4 @@ namespace ix
|
||||
args->logger(msg);
|
||||
}
|
||||
}
|
||||
|
||||
std::string HttpClient::generateMultipartBoundary()
|
||||
{
|
||||
std::string str("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
|
||||
|
||||
static std::random_device rd;
|
||||
static std::mt19937 generator(rd());
|
||||
|
||||
std::shuffle(str.begin(), str.end(), generator);
|
||||
|
||||
return str;
|
||||
}
|
||||
} // namespace ix
|
||||
|
@ -64,13 +64,6 @@ namespace ix
|
||||
|
||||
std::string serializeHttpParameters(const HttpParameters& httpParameters);
|
||||
|
||||
std::string serializeHttpFormDataParameters(
|
||||
const std::string& multipartBoundary,
|
||||
const HttpFormDataParameters& httpFormDataParameters,
|
||||
const HttpParameters& httpParameters = HttpParameters());
|
||||
|
||||
std::string generateMultipartBoundary();
|
||||
|
||||
std::string urlEncode(const std::string& value);
|
||||
|
||||
const static std::string kPost;
|
||||
|
@ -168,32 +168,10 @@ namespace ix
|
||||
SSLSetProtocolVersionMin(_sslContext, kTLSProtocol12);
|
||||
SSLSetPeerDomainName(_sslContext, host.c_str(), host.size());
|
||||
|
||||
if (_tlsOptions.isPeerVerifyDisabled())
|
||||
do
|
||||
{
|
||||
Boolean option(1);
|
||||
SSLSetSessionOption(_sslContext, kSSLSessionOptionBreakOnServerAuth, option);
|
||||
|
||||
do
|
||||
{
|
||||
status = SSLHandshake(_sslContext);
|
||||
} while (errSSLWouldBlock == status || errSSLServerAuthCompleted == status);
|
||||
|
||||
if (status == errSSLServerAuthCompleted)
|
||||
{
|
||||
// proceed with the handshake
|
||||
do
|
||||
{
|
||||
status = SSLHandshake(_sslContext);
|
||||
} while (errSSLWouldBlock == status || errSSLServerAuthCompleted == status);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
status = SSLHandshake(_sslContext);
|
||||
} while (errSSLWouldBlock == status || errSSLServerAuthCompleted == status);
|
||||
}
|
||||
status = SSLHandshake(_sslContext);
|
||||
} while (errSSLWouldBlock == status || errSSLServerAuthCompleted == status);
|
||||
}
|
||||
|
||||
if (noErr != status)
|
||||
|
@ -31,7 +31,6 @@ namespace ix
|
||||
std::string& errorMsg,
|
||||
const SocketTLSOptions& tlsOptions)
|
||||
{
|
||||
(void) tlsOptions;
|
||||
errorMsg.clear();
|
||||
std::shared_ptr<Socket> socket;
|
||||
|
||||
|
@ -543,7 +543,7 @@ namespace ix
|
||||
}
|
||||
|
||||
// Prevent integer overflow in the next conditional
|
||||
const uint64_t maxFrameSize(1ULL << 63);
|
||||
const uint64_t maxFrameSize(1 << 63);
|
||||
if (ws.N > maxFrameSize)
|
||||
{
|
||||
return;
|
||||
|
@ -6,4 +6,4 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#define IX_WEBSOCKET_VERSION "7.4.0"
|
||||
#define IX_WEBSOCKET_VERSION "7.1.0"
|
||||
|
1
makefile
1
makefile
@ -57,7 +57,6 @@ docker:
|
||||
docker_push:
|
||||
docker tag ${IMG} ${LATEST}
|
||||
docker push ${LATEST}
|
||||
docker push ${IMG}
|
||||
|
||||
run:
|
||||
docker run --cap-add sys_ptrace --entrypoint=sh -it bsergean/ws:build
|
||||
|
@ -8,9 +8,10 @@
|
||||
|
||||
#include "catch.hpp"
|
||||
#include <iostream>
|
||||
#include <ixsentry/IXSentryClient.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <ixsentry/IXSentryClient.h>
|
||||
|
||||
using namespace ix;
|
||||
|
||||
namespace ix
|
||||
@ -20,9 +21,7 @@ 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);
|
||||
@ -31,8 +30,7 @@ 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);
|
||||
|
@ -1 +0,0 @@
|
||||
0.0.1
|
@ -1,8 +0,0 @@
|
||||
FROM python:3.8.0-alpine3.10
|
||||
|
||||
RUN pip install websockets
|
||||
COPY ws_proxy.py /usr/bin
|
||||
RUN chmod +x /usr/bin/ws_proxy.py
|
||||
|
||||
EXPOSE 8765
|
||||
CMD ["python", "/usr/bin/ws_proxy.py"]
|
@ -1,20 +0,0 @@
|
||||
.PHONY: docker
|
||||
|
||||
NAME := bsergean/ws_proxy
|
||||
TAG := $(shell cat DOCKER_VERSION)
|
||||
IMG := ${NAME}:${TAG}
|
||||
LATEST := ${NAME}:latest
|
||||
BUILD := ${NAME}:build
|
||||
|
||||
docker_test:
|
||||
docker build -t ${BUILD} .
|
||||
|
||||
docker:
|
||||
git clean -dfx
|
||||
docker build -t ${IMG} .
|
||||
docker tag ${IMG} ${BUILD}
|
||||
|
||||
docker_push:
|
||||
docker tag ${IMG} ${LATEST}
|
||||
docker push ${LATEST}
|
||||
docker push ${IMG}
|
@ -1,47 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# websocket proxy
|
||||
|
||||
import argparse
|
||||
import asyncio
|
||||
import websockets
|
||||
|
||||
|
||||
async def hello(websocket, path):
|
||||
'''Called whenever a new connection is made to the server'''
|
||||
|
||||
url = REMOTE_URL + path
|
||||
async with websockets.connect(url) as ws:
|
||||
taskA = asyncio.create_task(clientToServer(ws, websocket))
|
||||
taskB = asyncio.create_task(serverToClient(ws, websocket))
|
||||
|
||||
await taskA
|
||||
await taskB
|
||||
|
||||
|
||||
async def clientToServer(ws, websocket):
|
||||
async for message in ws:
|
||||
await websocket.send(message)
|
||||
|
||||
|
||||
async def serverToClient(ws, websocket):
|
||||
async for message in websocket:
|
||||
await ws.send(message)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description='websocket proxy.')
|
||||
parser.add_argument('--host', help='Host to bind to.',
|
||||
default='localhost')
|
||||
parser.add_argument('--port', help='Port to bind to.',
|
||||
default=8765)
|
||||
parser.add_argument('--remote_url', help='Remote websocket url',
|
||||
default='ws://localhost:8767')
|
||||
args = parser.parse_args()
|
||||
|
||||
REMOTE_URL = args.remote_url
|
||||
|
||||
start_server = websockets.serve(hello, args.host, args.port)
|
||||
|
||||
asyncio.get_event_loop().run_until_complete(start_server)
|
||||
asyncio.get_event_loop().run_forever()
|
@ -55,8 +55,6 @@ add_executable(ws
|
||||
ws_cobra_metrics_to_redis.cpp
|
||||
ws_httpd.cpp
|
||||
ws_autobahn.cpp
|
||||
ws_proxy_server.cpp
|
||||
ws_sentry_minidump_upload.cpp
|
||||
ws.cpp)
|
||||
|
||||
target_link_libraries(ws ixsnake)
|
||||
|
26
ws/ws.cpp
26
ws/ws.cpp
@ -72,11 +72,6 @@ int main(int argc, char** argv)
|
||||
std::string redisPassword;
|
||||
std::string appsConfigPath("appsConfig.json");
|
||||
std::string subprotocol;
|
||||
std::string remoteHost;
|
||||
std::string minidump;
|
||||
std::string metadata;
|
||||
std::string project;
|
||||
std::string key;
|
||||
ix::SocketTLSOptions tlsOptions;
|
||||
std::string ciphers;
|
||||
std::string redirectUrl;
|
||||
@ -309,19 +304,6 @@ 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");
|
||||
|
||||
CLI::App* minidumpApp = app.add_subcommand("upload_minidump", "Upload a minidump to sentry");
|
||||
minidumpApp->add_option("--minidump", minidump, "Minidump path")->check(CLI::ExistingPath);
|
||||
minidumpApp->add_option("--metadata", metadata, "Hostname")->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");
|
||||
|
||||
CLI11_PARSE(app, argc, argv);
|
||||
|
||||
// pid file handling
|
||||
@ -460,14 +442,6 @@ 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 (app.got_subcommand("upload_minidump"))
|
||||
{
|
||||
ret = ix::ws_sentry_minidump_upload(metadata, minidump, project, key, verbose);
|
||||
}
|
||||
else if (version)
|
||||
{
|
||||
std::cout << "ws " << ix::userAgent() << std::endl;
|
||||
|
12
ws/ws.h
12
ws/ws.h
@ -142,16 +142,4 @@ 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);
|
||||
|
||||
int ws_sentry_minidump_upload(const std::string& metadataPath,
|
||||
const std::string& minidump,
|
||||
const std::string& project,
|
||||
const std::string& key,
|
||||
bool verbose);
|
||||
} // namespace ix
|
||||
|
@ -128,21 +128,23 @@ 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)
|
||||
{
|
||||
|
@ -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>
|
||||
@ -81,29 +81,6 @@ namespace ix
|
||||
|
||||
auto ret = sentryClient.send(msg, verbose);
|
||||
HttpResponsePtr response = ret.first;
|
||||
|
||||
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("payload: {}", response->payload);
|
||||
}
|
||||
}
|
||||
|
||||
if (response->statusCode != 200)
|
||||
{
|
||||
spdlog::error("Error sending data to sentry: {}", response->statusCode);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* ws_echo_server.cpp
|
||||
* ws_broadcast_server.cpp
|
||||
* Author: Benjamin Sergeant
|
||||
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
|
||||
*/
|
||||
|
@ -1,174 +0,0 @@
|
||||
/*
|
||||
* 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 << "server 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;
|
||||
}
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||
{
|
||||
std::cerr << "Closed connection"
|
||||
<< " code " << msg->closeInfo.code << " reason "
|
||||
<< msg->closeInfo.reason << std::endl;
|
||||
webSocket->close(msg->closeInfo.code, msg->closeInfo.reason);
|
||||
state->setTerminated();
|
||||
}
|
||||
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 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 << "client 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->webSocket().getReadyState() != ReadyState::Open)
|
||||
{
|
||||
std::cerr << "waiting for server connection establishment" << std::endl;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
}
|
||||
std::cerr << "server connection established" << std::endl;
|
||||
}
|
||||
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
|
@ -1,105 +0,0 @@
|
||||
/*
|
||||
* ws_sentry_minidump_upload.cpp
|
||||
* Author: Benjamin Sergeant
|
||||
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <fstream>
|
||||
#include <ixsentry/IXSentryClient.h>
|
||||
#include <jsoncpp/json/json.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <sstream>
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
// Assume the file exists
|
||||
std::string readBytes(const std::string& path)
|
||||
{
|
||||
std::vector<uint8_t> memblock;
|
||||
std::ifstream file(path);
|
||||
|
||||
file.seekg(0, file.end);
|
||||
std::streamoff size = file.tellg();
|
||||
file.seekg(0, file.beg);
|
||||
|
||||
memblock.resize(size);
|
||||
|
||||
file.read((char*) &memblock.front(), static_cast<std::streamsize>(size));
|
||||
|
||||
std::string bytes(memblock.begin(), memblock.end());
|
||||
return bytes;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace ix
|
||||
{
|
||||
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("payload: {}", response->payload);
|
||||
}
|
||||
}
|
||||
|
||||
if (response->statusCode != 200)
|
||||
{
|
||||
spdlog::error("Error sending data to sentry: {}", response->statusCode);
|
||||
spdlog::error("Status: {}", response->statusCode);
|
||||
spdlog::error("Response: {}", response->payload);
|
||||
}
|
||||
else
|
||||
{
|
||||
spdlog::info("Event sent to sentry");
|
||||
}
|
||||
|
||||
done = true;
|
||||
});
|
||||
|
||||
while (!done)
|
||||
{
|
||||
std::chrono::duration<double, std::milli> duration(10);
|
||||
std::this_thread::sleep_for(duration);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace ix
|
Reference in New Issue
Block a user