Compare commits

...

20 Commits

Author SHA1 Message Date
22118d68d2 session resume wip, for mac only 2020-01-24 22:36:23 -08:00
4c15964d43 (ws) add a dnslookup sub-command, to get the ip address of a remote host 2020-01-22 21:11:48 -08:00
99fe6ea493 fix #144 - get rid of stubbed windows ssl schannel backend 2020-01-14 13:39:48 -08:00
acab19fe9f install gdb in the alpine dockerfile 2020-01-14 13:37:28 -08:00
2f59fcc02d activate chat test on Windows 2020-01-13 16:44:10 -08:00
8a344788ba simplify socket interface 2020-01-12 22:30:16 -08:00
5d641bc267 use curl code for large apple ssl send 2020-01-12 22:04:06 -08:00
0c4866420a test broadcast server 2020-01-12 21:32:26 -08:00
8ed7c660d4 more doc 2020-01-12 11:16:54 -08:00
4cbf85f4d4 (openssl + mbedssl) fix #140, can send large files with ws send over ssl / still broken with apple ssl 2020-01-12 11:08:44 -08:00
a8ff91ac3c compatibility websocket server / add non ssl echo_server which does not run into the ssl error (#140) 2020-01-12 10:34:33 -08:00
4cb3326078 (apple ssl) model write method after the OpenSSL one for consistency
More test code for #140.
2020-01-10 18:32:34 -08:00
87625a8805 fix broken autobahn link 2020-01-09 15:30:33 -08:00
b75264b98f poll: handle POLLNVAL 2020-01-09 13:45:58 -08:00
21f42210a2 formatting 2020-01-09 13:45:31 -08:00
27287aea50 ws send / detect disconnection earlier 2020-01-09 13:30:08 -08:00
39f69bd2b6 socket polling / handle more error case such as hanged socket 2020-01-09 12:26:57 -08:00
00dc8bacee websocket closing / handle failure to flush send buffer as an error case 2020-01-09 12:17:37 -08:00
9e24475fd6 ws send / check that we are connected when waiting for the send buffer to be flushed, and if so, abort 2020-01-09 12:16:12 -08:00
61bcc9d27d refactor receiving socket code in its own method 2020-01-09 12:00:42 -08:00
41 changed files with 1901 additions and 287 deletions

View File

@ -129,9 +129,6 @@ if (USE_TLS)
elseif (APPLE AND NOT USE_OPEN_SSL)
list( APPEND IXWEBSOCKET_HEADERS ixwebsocket/IXSocketAppleSSL.h)
list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/IXSocketAppleSSL.cpp)
elseif (WIN32 AND NOT USE_OPEN_SSL)
list( APPEND IXWEBSOCKET_HEADERS ixwebsocket/IXSocketSChannel.h)
list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/IXSocketSChannel.cpp)
else()
set(USE_OPEN_SSL ON)
list( APPEND IXWEBSOCKET_HEADERS ixwebsocket/IXSocketOpenSSL.h)

View File

@ -38,7 +38,7 @@ Interested? Go read the [docs](https://machinezone.github.io/IXWebSocket/)! If t
IXWebSocket is actively being developed, check out the [changelog](https://machinezone.github.io/IXWebSocket/CHANGELOG/) to know what's cooking. If you are looking for a real time messaging service (the chat-like 'server' your websocket code will talk to) with many features such as history, backed by Redis, look at [cobra](https://github.com/machinezone/cobra).
IXWebSocket client code is autobahn compliant beginning with the 6.0.0 version. See the current [test results](https://bsergean.github.io/IXWebSocket/autobahn/index.html). Some tests are still failing in the server code.
IXWebSocket client code is autobahn compliant beginning with the 6.0.0 version. See the current [test results](https://bsergean.github.io/autobahn/reports/clients/index.html). Some tests are still failing in the server code.
## Users

View File

@ -21,6 +21,7 @@ FROM alpine:3.11 as runtime
RUN apk add --no-cache libstdc++
RUN apk add --no-cache strace
RUN apk add --no-cache gdb
RUN addgroup -S app && adduser -S -G app app
COPY --chown=app:app --from=build /usr/local/bin/ws /usr/local/bin/ws
@ -36,4 +37,3 @@ WORKDIR /home/app
ENTRYPOINT ["ws"]
EXPOSE 8008
C

View File

@ -1,6 +1,22 @@
# Changelog
All changes to this project will be documented in this file.
## [7.9.6] - 2020-01-22
(ws) add a dnslookup sub-command, to get the ip address of a remote host
## [7.9.5] - 2020-01-14
(windows) fix #144, get rid of stubbed/un-implemented windows schannel ssl backend
## [7.9.4] - 2020-01-12
(openssl + mbedssl) fix #140, can send large files with ws send over ssl / still broken with apple ssl
## [7.9.3] - 2020-01-10
(apple ssl) model write method after the OpenSSL one for consistency
## [7.9.2] - 2020-01-06
(apple ssl) unify read and write ssl utility code

View File

@ -54,14 +54,17 @@ namespace ix
// to ::poll does fix that.
//
// However poll isn't as portable as select and has bugs on Windows, so we
// should write a shim to fallback to select on those platforms. See
// have a shim to fallback to select on those platforms. See
// https://github.com/mpv-player/mpv/pull/5203/files for such a select wrapper.
//
nfds_t nfds = 1;
struct pollfd fds[2];
memset(fds, 0, sizeof(fds));
fds[0].fd = sockfd;
fds[0].events = (readyToRead) ? POLLIN : POLLOUT;
// this is ignored by poll, but our select based poll wrapper on Windows needs it
fds[0].events |= POLLERR;
// File descriptor used to interrupt select when needed
@ -132,6 +135,11 @@ namespace ix
}
#endif
}
else if (sockfd != -1 && (fds[0].revents & POLLERR || fds[0].revents & POLLHUP ||
fds[0].revents & POLLNVAL))
{
pollResult = PollResultType::Error;
}
return pollResult;
}

View File

@ -73,7 +73,7 @@ namespace ix
virtual void close();
virtual ssize_t send(char* buffer, size_t length);
virtual ssize_t send(const std::string& buffer);
ssize_t send(const std::string& buffer);
virtual ssize_t recv(void* buffer, size_t length);
// Blocking and cancellable versions, working with socket that can be set

View File

@ -20,6 +20,7 @@
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sstream>
#define socketerrno errno
#include <Security/SecureTransport.h>
@ -31,12 +32,17 @@ namespace ix
, _sslContext(nullptr)
, _tlsOptions(tlsOptions)
{
;
_sslContext = SSLCreateContext(kCFAllocatorDefault, kSSLClientSide, kSSLStreamType);
SSLSetIOFuncs(
_sslContext, SocketAppleSSL::readFromSocket, SocketAppleSSL::writeToSocket);
}
SocketAppleSSL::~SocketAppleSSL()
{
SocketAppleSSL::close();
CFRelease(_sslContext);
_sslContext = nullptr;
Socket::close();
}
std::string SocketAppleSSL::getSSLErrorDescription(OSStatus status)
@ -177,14 +183,16 @@ namespace ix
_sockfd = SocketConnect::connect(host, port, errMsg, isCancellationRequested);
if (_sockfd == -1) return false;
_sslContext = SSLCreateContext(kCFAllocatorDefault, kSSLClientSide, kSSLStreamType);
SSLSetIOFuncs(
_sslContext, SocketAppleSSL::readFromSocket, SocketAppleSSL::writeToSocket);
SSLSetConnection(_sslContext, (SSLConnectionRef)(long) _sockfd);
SSLSetProtocolVersionMin(_sslContext, kTLSProtocol12);
SSLSetPeerDomainName(_sslContext, host.c_str(), host.size());
// Record a peer id, which speed up SSL connection when reconnecting to the same host
std::stringstream ss;
ss << host << ":" << port;
_peerId = ss.str();
SSLSetPeerID(_sslContext, (void*) _peerId.c_str(), _peerId.size());
if (_tlsOptions.isPeerVerifyDisabled())
{
Boolean option(1);
@ -227,12 +235,7 @@ namespace ix
{
std::lock_guard<std::mutex> lock(_mutex);
if (_sslContext == nullptr) return;
SSLClose(_sslContext);
CFRelease(_sslContext);
_sslContext = nullptr;
Socket::close();
}
@ -265,11 +268,6 @@ namespace ix
return -1;
}
ssize_t SocketAppleSSL::send(const std::string& buffer)
{
return send((char*) &buffer[0], buffer.size());
}
// No wait support
ssize_t SocketAppleSSL::recv(void* buf, size_t nbyte)
{

View File

@ -30,7 +30,6 @@ namespace ix
virtual void close() final;
virtual ssize_t send(char* buffer, size_t length) final;
virtual ssize_t send(const std::string& buffer) final;
virtual ssize_t recv(void* buffer, size_t length) final;
private:
@ -42,6 +41,8 @@ namespace ix
mutable std::mutex _mutex; // AppleSSL routines are not thread-safe
SocketTLSOptions _tlsOptions;
std::string _peerId;
};
} // namespace ix

View File

@ -19,10 +19,12 @@
#include <linux/tcp.h>
#endif
#include <iostream>
namespace ix
{
//
// This function can be cancelled every 50 ms
// This function can be cancelled every 10 ms
// This is important so that we don't block the main UI thread when shutting down a
// connection which is already trying to reconnect, and can be blocked waiting for
// ::connect to respond.
@ -44,8 +46,15 @@ namespace ix
// block us for too long
SocketConnect::configure(fd);
auto start = std::chrono::system_clock::now();
int res = ::connect(fd, address->ai_addr, address->ai_addrlen);
auto now = std::chrono::system_clock::now();
auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(now - start);
auto ms = milliseconds.count();
std::cout << "tcp connection completed in " << ms << "ms" << std::endl;
if (res == -1 && !Socket::isWaitNeeded())
{
errMsg = strerror(Socket::getErrno());
@ -98,11 +107,19 @@ namespace ix
std::string& errMsg,
const CancellationRequest& isCancellationRequested)
{
auto start = std::chrono::system_clock::now();
//
// First do DNS resolution
//
auto dnsLookup = std::make_shared<DNSLookup>(hostname, port);
struct addrinfo* res = dnsLookup->resolve(errMsg, isCancellationRequested);
auto now = std::chrono::system_clock::now();
auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(now - start);
auto ms = milliseconds.count();
std::cout << "dns resolution completed in " << ms << "ms" << std::endl;
if (res == nullptr)
{
return -1;

View File

@ -14,8 +14,6 @@
#include "IXSocketOpenSSL.h"
#elif __APPLE__
#include "IXSocketAppleSSL.h"
#elif defined(_WIN32)
#include "IXSocketSChannel.h"
#endif
#else
@ -46,8 +44,6 @@ namespace ix
socket = std::make_shared<SocketMbedTLS>(tlsOptions, fd);
#elif defined(IXWEBSOCKET_USE_OPEN_SSL)
socket = std::make_shared<SocketOpenSSL>(tlsOptions, fd);
#elif defined(_WIN32)
socket = std::make_shared<SocketSChannel>(tlsOptions, fd);
#elif defined(__APPLE__)
socket = std::make_shared<SocketAppleSSL>(tlsOptions, fd);
#endif

View File

@ -230,35 +230,23 @@ namespace ix
ssize_t SocketMbedTLS::send(char* buf, size_t nbyte)
{
ssize_t sent = 0;
std::lock_guard<std::mutex> lock(_mutex);
while (nbyte > 0)
ssize_t res = mbedtls_ssl_write(&_ssl, (unsigned char*) buf, nbyte);
if (res > 0)
{
std::lock_guard<std::mutex> lock(_mutex);
ssize_t res = mbedtls_ssl_write(&_ssl, (unsigned char*) buf, nbyte);
if (res > 0)
{
nbyte -= res;
sent += res;
}
else if (res == MBEDTLS_ERR_SSL_WANT_READ || res == MBEDTLS_ERR_SSL_WANT_WRITE)
{
errno = EWOULDBLOCK;
return -1;
}
else
{
return -1;
}
return res;
}
else if (res == MBEDTLS_ERR_SSL_WANT_READ || res == MBEDTLS_ERR_SSL_WANT_WRITE)
{
errno = EWOULDBLOCK;
return -1;
}
else
{
return -1;
}
return sent;
}
ssize_t SocketMbedTLS::send(const std::string& buffer)
{
return send((char*) &buffer[0], buffer.size());
}
ssize_t SocketMbedTLS::recv(void* buf, size_t nbyte)

View File

@ -35,7 +35,6 @@ namespace ix
virtual void close() final;
virtual ssize_t send(char* buffer, size_t length) final;
virtual ssize_t send(const std::string& buffer) final;
virtual ssize_t recv(void* buffer, size_t length) final;
private:

View File

@ -603,42 +603,30 @@ namespace ix
ssize_t SocketOpenSSL::send(char* buf, size_t nbyte)
{
ssize_t sent = 0;
std::lock_guard<std::mutex> lock(_mutex);
while (nbyte > 0)
if (_ssl_connection == nullptr || _ssl_context == nullptr)
{
std::lock_guard<std::mutex> lock(_mutex);
if (_ssl_connection == nullptr || _ssl_context == nullptr)
{
return 0;
}
ERR_clear_error();
ssize_t write_result = SSL_write(_ssl_connection, buf + sent, (int) nbyte);
int reason = SSL_get_error(_ssl_connection, (int) write_result);
if (reason == SSL_ERROR_NONE)
{
nbyte -= write_result;
sent += write_result;
}
else if (reason == SSL_ERROR_WANT_READ || reason == SSL_ERROR_WANT_WRITE)
{
errno = EWOULDBLOCK;
return -1;
}
else
{
return -1;
}
return 0;
}
return sent;
}
ssize_t SocketOpenSSL::send(const std::string& buffer)
{
return send((char*) &buffer[0], buffer.size());
ERR_clear_error();
ssize_t write_result = SSL_write(_ssl_connection, buf, (int) nbyte);
int reason = SSL_get_error(_ssl_connection, (int) write_result);
if (reason == SSL_ERROR_NONE)
{
return write_result;
}
else if (reason == SSL_ERROR_WANT_READ || reason == SSL_ERROR_WANT_WRITE)
{
errno = EWOULDBLOCK;
return -1;
}
else
{
return -1;
}
}
ssize_t SocketOpenSSL::recv(void* buf, size_t nbyte)

View File

@ -33,7 +33,6 @@ namespace ix
virtual void close() final;
virtual ssize_t send(char* buffer, size_t length) final;
virtual ssize_t send(const std::string& buffer) final;
virtual ssize_t recv(void* buffer, size_t length) final;
private:

View File

@ -1,103 +0,0 @@
/*
* IXSocketSChannel.cpp
* Author: Benjamin Sergeant
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
*
* See https://docs.microsoft.com/en-us/windows/desktop/WinSock/using-secure-socket-extensions
*
* https://github.com/pauldotknopf/WindowsSDK7-Samples/blob/master/netds/winsock/securesocket/stcpclient/tcpclient.c
*
* This is the right example to look at:
* https://www.codeproject.com/Articles/1000189/A-Working-TCP-Client-and-Server-With-SSL
*
* Similar code is available from this git repo
* https://github.com/david-maw/StreamSSL
*/
#include "IXSocketSChannel.h"
#ifdef _WIN32
#include <WS2tcpip.h>
#include <WinSock2.h>
#include <basetsd.h>
#include <io.h>
#include <schannel.h>
#include <ws2def.h>
#define WIN32_LEAN_AND_MEAN
#ifndef UNICODE
#define UNICODE
#endif
#include <mstcpip.h>
#include <ntdsapi.h>
#include <rpc.h>
#include <stdio.h>
#include <tchar.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#define RECV_DATA_BUF_SIZE 256
// Link with ws2_32.lib
#pragma comment(lib, "Ws2_32.lib")
// link with fwpuclnt.lib for Winsock secure socket extensions
#pragma comment(lib, "fwpuclnt.lib")
// link with ntdsapi.lib for DsMakeSpn function
#pragma comment(lib, "ntdsapi.lib")
// The following function assumes that Winsock
// has already been initialized
#else
#error("This file should only be built on Windows")
#endif
namespace ix
{
SocketSChannel::SocketSChannel()
{
;
}
SocketSChannel::~SocketSChannel()
{
}
bool SocketSChannel::connect(const std::string& host, int port, std::string& errMsg)
{
return Socket::connect(host, port, errMsg, nullptr);
}
void SocketSChannel::secureSocket()
{
// there will be a lot to do here ...
}
void SocketSChannel::close()
{
Socket::close();
}
ssize_t SocketSChannel::send(char* buf, size_t nbyte)
{
return Socket::send(buf, nbyte);
}
ssize_t SocketSChannel::send(const std::string& buffer)
{
return Socket::send(buffer);
}
ssize_t SocketSChannel::recv(void* buf, size_t nbyte)
{
return Socket::recv(buf, nbyte);
}
} // namespace ix

View File

@ -1,32 +0,0 @@
/*
* IXSocketSChannel.h
* Author: Benjamin Sergeant
* Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include "IXSocket.h"
namespace ix
{
class SocketSChannel final : public Socket
{
public:
SocketSChannel();
~SocketSChannel();
virtual bool connect(const std::string& host, int port, std::string& errMsg) final;
virtual void close() final;
// The important override
virtual void secureSocket() final;
virtual ssize_t send(char* buffer, size_t length) final;
virtual ssize_t send(const std::string& buffer) final;
virtual ssize_t recv(void* buffer, size_t length) final;
private:
};
} // namespace ix

View File

@ -15,6 +15,9 @@
#include <random>
#include <sstream>
#include <iostream>
#include <chrono>
namespace ix
{
@ -97,8 +100,16 @@ namespace ix
auto isCancellationRequested =
makeCancellationRequestWithTimeout(timeoutSecs, _requestInitCancellation);
auto start = std::chrono::system_clock::now();
std::string errMsg;
bool success = _socket->connect(host, port, errMsg, isCancellationRequested);
auto now = std::chrono::system_clock::now();
auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(now - start);
auto ms = milliseconds.count();
std::cout << "connection completed in " << ms << "ms" << std::endl;
if (!success)
{
std::stringstream ss;

View File

@ -151,7 +151,18 @@ namespace ix
std::string errorMsg;
bool tls = protocol == "wss";
_socket = createSocket(tls, -1, errorMsg, _socketTLSOptions);
if (_host == host)
{
_socket->close();
}
else
{
_socket = createSocket(tls, -1, errorMsg, _socketTLSOptions);
}
// Record the host for later
_host = host;
if (!_socket)
{
@ -350,28 +361,9 @@ namespace ix
}
else if (pollResult == PollResultType::ReadyForRead)
{
while (true)
if (!receiveFromSocket())
{
ssize_t ret = _socket->recv((char*) &_readbuf[0], _readbuf.size());
if (ret < 0 && Socket::isWaitNeeded())
{
break;
}
else if (ret <= 0)
{
// if there are received data pending to be processed, then delay the abnormal
// closure to after dispatch (other close code/reason could be read from the
// buffer)
closeSocket();
return PollResult::AbnormalClose;
}
else
{
_rxbuf.insert(_rxbuf.end(), _readbuf.begin(), _readbuf.begin() + ret);
}
return PollResult::AbnormalClose;
}
}
else if (pollResult == PollResultType::Error)
@ -739,7 +731,7 @@ namespace ix
// if an abnormal closure was raised in poll, and nothing else triggered a CLOSED state in
// the received and processed data then close the connection
if (pollResult == PollResult::AbnormalClose)
if (pollResult != PollResult::Succeeded)
{
_rxbuf.clear();
@ -1053,19 +1045,17 @@ namespace ix
wsheader_type::TEXT_FRAME, message, _enablePerMessageDeflate, onProgressCallback);
}
ssize_t WebSocketTransport::send()
{
std::lock_guard<std::mutex> lock(_socketMutex);
return _socket->send((char*) &_txbuf[0], _txbuf.size());
}
bool WebSocketTransport::sendOnSocket()
{
std::lock_guard<std::mutex> lock(_txbufMutex);
while (_txbuf.size())
{
ssize_t ret = send();
ssize_t ret = 0;
{
std::lock_guard<std::mutex> lock(_socketMutex);
ret = _socket->send((char*) &_txbuf[0], _txbuf.size());
}
if (ret < 0 && Socket::isWaitNeeded())
{
@ -1086,6 +1076,34 @@ namespace ix
return true;
}
bool WebSocketTransport::receiveFromSocket()
{
while (true)
{
ssize_t ret = _socket->recv((char*) &_readbuf[0], _readbuf.size());
if (ret < 0 && Socket::isWaitNeeded())
{
break;
}
else if (ret <= 0)
{
// if there are received data pending to be processed, then delay the abnormal
// closure to after dispatch (other close code/reason could be read from the
// buffer)
closeSocket();
return false;
}
else
{
_rxbuf.insert(_rxbuf.end(), _readbuf.begin(), _readbuf.begin() + ret);
}
}
return true;
}
void WebSocketTransport::sendCloseFrame(uint16_t code, const std::string& reason)
{
bool compress = false;

View File

@ -99,7 +99,6 @@ namespace ix
bool remote = false);
void closeSocket();
ssize_t send();
ReadyState getReadyState() const;
void setReadyState(ReadyState readyState);
@ -108,7 +107,7 @@ namespace ix
size_t bufferedAmount() const;
private:
std::string _url;
std::string _host;
struct wsheader_type
{
@ -245,6 +244,8 @@ namespace ix
bool flushSendBuffer();
bool sendOnSocket();
bool receiveFromSocket();
WebSocketSendInfo sendData(wsheader_type::opcode_type type,
const std::string& message,
bool compress,

View File

@ -6,4 +6,4 @@
#pragma once
#define IX_WEBSOCKET_VERSION "7.9.2"
#define IX_WEBSOCKET_VERSION "7.9.6"

View File

@ -55,12 +55,12 @@ set (SOURCES
IXDNSLookupTest.cpp
IXWebSocketSubProtocolTest.cpp
IXSentryClientTest.cpp
IXWebSocketChatTest.cpp
)
# Some unittest don't work on windows yet
if (UNIX)
list(APPEND SOURCES
IXWebSocketChatTest.cpp
IXWebSocketCloseTest.cpp
)
endif()

View File

@ -1,8 +1,17 @@
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"]
COPY vendor/protocol.py /usr/local/lib/python3.8/site-packages/websockets/protocol.py
COPY *.py /usr/bin/
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/*.py
RUN mkdir /certs
COPY *.pem /certs/
WORKDIR /certs
EXPOSE 8765 8766
CMD ["sh", "/usr/bin/entrypoint.sh"]

View File

@ -830,3 +830,17 @@ $ honcho start
15:29:59 system | nginx.1 stopped (rc=0)
15:29:59 system | websocket_server.1 stopped (rc=-15)
```
## Sending large files over SSL
Running inside docker
```
$ make docker && make server_ssl
```
On the client
```
$ make ws_mbedtls && cp build/ws/ws /usr/local/bin/ws && ws send --verify_none wss://localhost:8766 /tmp/big_file
```

View File

@ -0,0 +1,36 @@
#!/usr/bin/env python
# WS server example
import asyncio
import os
import websockets
clients = set()
async def echo(websocket, path):
clients.add(websocket)
try:
while True:
msg = await websocket.recv()
for ws in clients:
if ws != websocket:
print(f'Sending {len(msg)} bytes to {ws}')
await ws.send(msg)
except websockets.exceptions.ConnectionClosedOK:
print('Client terminating')
clients.remove(websocket)
host = os.getenv('BIND_HOST', 'localhost')
print(f'Serving on {host}:8766')
start_server = websockets.serve(echo, host, 8766, max_size=2 ** 30)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()

View File

@ -0,0 +1,43 @@
#!/usr/bin/env python
# WS server example
import asyncio
import os
import pathlib
import ssl
import websockets
clients = set()
async def echo(websocket, path):
clients.add(websocket)
try:
while True:
msg = await websocket.recv()
for ws in clients:
if ws != websocket:
print(f'Sending {len(msg)} bytes to {ws}')
await ws.send(msg)
except websockets.exceptions.ConnectionClosedOK:
print('Client terminating')
clients.remove(websocket)
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
ssl_context.load_cert_chain('trusted-server-crt.pem',
'trusted-server-key.pem')
host = os.getenv('BIND_HOST', 'localhost')
print(f'Serving on {host}:8766')
start_server = websockets.serve(echo, host, 8766, max_size=2 ** 30, ssl=ssl_context)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()

View File

@ -3,6 +3,7 @@
# WS server example
import asyncio
import os
import websockets
@ -11,9 +12,10 @@ async def echo(websocket, path):
print(f'Received {len(msg)} bytes')
await websocket.send(msg)
host = os.getenv('BIND_HOST', 'localhost')
print(f'Serving on {host}:8766')
print('Serving on localhost:8766')
start_server = websockets.serve(echo, 'localhost', 8766, max_size=2 ** 25)
start_server = websockets.serve(echo, host, 8766, max_size=2 ** 30)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()

View File

@ -0,0 +1,27 @@
#!/usr/bin/env python
# WS server example
import asyncio
import os
import pathlib
import ssl
import websockets
async def echo(websocket, path):
msg = await websocket.recv()
print(f'Received {len(msg)} bytes')
await websocket.send(msg)
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
ssl_context.load_cert_chain('trusted-server-crt.pem',
'trusted-server-key.pem')
host = os.getenv('BIND_HOST', 'localhost')
print(f'Serving on {host}:8766')
start_server = websockets.serve(echo, host, 8766, max_size=2 ** 30, ssl=ssl_context)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()

View File

@ -0,0 +1,16 @@
#!/bin/sh
case $MODE in
echo_server)
python /usr/bin/echo_server.py
;;
echo_server_ssl)
python /usr/bin/echo_server_ssl.py
;;
broadcast_server)
python /usr/bin/broadcast_server.py
;;
broadcast_server_ssl)
python /usr/bin/broadcast_server_ssl.py
;;
esac

View File

@ -0,0 +1 @@
trusted-client-crt.pem

View File

@ -3,7 +3,7 @@ all:
.PHONY: docker
NAME := bsergean/ws_proxy
NAME := bsergean/echo_server
TAG := $(shell cat DOCKER_VERSION)
IMG := ${NAME}:${TAG}
LATEST := ${NAME}:latest
@ -21,3 +21,15 @@ docker_push:
docker tag ${IMG} ${LATEST}
docker push ${LATEST}
docker push ${IMG}
echo_server:
docker run -p 8766:8766 -e BIND_HOST=0.0.0.0 -e MODE=echo_server -it --rm bsergean/echo_server:build
echo_server_ssl:
docker run -p 8766:8766 -e BIND_HOST=0.0.0.0 -e MODE=echo_server_ssl -it --rm bsergean/echo_server:build
broadcast_server:
docker run -p 8766:8766 -e BIND_HOST=0.0.0.0 -e MODE=broadcast_server -it --rm bsergean/echo_server:build
broadcast_server_ssl:
docker run -p 8766:8766 -e BIND_HOST=0.0.0.0 -e MODE=broadcast_server_ssl -it --rm bsergean/echo_server:build

View File

@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDLDCCAhSgAwIBAgIJALyEpMxNH62fMA0GCSqGSIb3DQEBCwUAMEExFDASBgNV
BAoMC21hY2hpbmV6b25lMRQwEgYDVQQKDAtJWFdlYlNvY2tldDETMBEGA1UEAwwK
dHJ1c3RlZC1jYTAeFw0xOTEyMjQwMDM3MzVaFw0yMDEyMjMwMDM3MzVaMEUxFDAS
BgNVBAoMC21hY2hpbmV6b25lMRQwEgYDVQQKDAtJWFdlYlNvY2tldDEXMBUGA1UE
AwwOdHJ1c3RlZC1zZXJ2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
AQCv0T68TZZ7nab+UWPhssGLrInE0egzWn1AF20RkJv1ePIyU0rQbDxuuP+HQbXD
FzF6vo2j+5p+VxxvUOfko9V6xad3cB4T3AoFrT5sYI8gQX1uby6pjqVX16TK5t+c
i56aNhUXdmcWhuUzlIMIauvueohd+pNj6E6weWqCf8QFD6KYPgK3wWCR4VfWA5QY
RJUhv2aI9HrC9P4Mg0mut8LYURRQvGxOhtbAw76FJ6IgBujpgI5GLHgVK5Q1GlXK
8W7RlNKNmxX+mzK2D6nHixCUGvrFk9nZgZiaHI/h5x0IGXu0sbwlTPjqQ4Axpofw
G1FDi/er4FaGCzU4CKmc7rxRAgMBAAGjIzAhMB8GA1UdEQQYMBaCCWxvY2FsaG9z
dIIJMTI3LjAuMC4xMA0GCSqGSIb3DQEBCwUAA4IBAQBkUB6smmApnBfr2eDwKJew
GQUMUAa7TlyANYlwQ6EjbAH7H6oNf7Sm63Go2Y72JjZPw3OvZl3QcvvS14QxYJ71
kRdvJ1mhEbIaA2QkdZCuDmcQGLfVEyo0j5q03amQKt9QtSv9MsX1Ok2HqGL17Tf1
QiUqlkzGCqMIsU20X8QzqwYCGzYZeTFtwLYi75za15Uo/6tE2KwzU7oUhuIebOaS
Sa+s2Y1TjpbWyw9usnuQWQ0k1FJR78F1mKJGghmPBoySBHJdLkLYOMhE1u2shgk5
N0muMcDRTeHLxm1aBPLHtkRbW3QscEQB6GkT2Dt4U66qNV2CY7Gk0F5xxOrGBC/9
-----END CERTIFICATE-----

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAr9E+vE2We52m/lFj4bLBi6yJxNHoM1p9QBdtEZCb9XjyMlNK
0Gw8brj/h0G1wxcxer6No/uaflccb1Dn5KPVesWnd3AeE9wKBa0+bGCPIEF9bm8u
qY6lV9ekyubfnIuemjYVF3ZnFoblM5SDCGrr7nqIXfqTY+hOsHlqgn/EBQ+imD4C
t8FgkeFX1gOUGESVIb9miPR6wvT+DINJrrfC2FEUULxsTobWwMO+hSeiIAbo6YCO
Rix4FSuUNRpVyvFu0ZTSjZsV/psytg+px4sQlBr6xZPZ2YGYmhyP4ecdCBl7tLG8
JUz46kOAMaaH8BtRQ4v3q+BWhgs1OAipnO68UQIDAQABAoIBAG/bIR2uAyJMT7UX
VQN/tbFGKTRmE2Owm2UOQl7dcMvAkd5AraViZyROYIvN23TuKZWc7AI7DbR5eWa8
w3vsW+JLI9tSImCiKmIoMUHEQOrVn5aF99r6HOmBEZ/hOLyg+1vDMrIFq1pioimp
v5+4XrgPjvizddgnMQEHjiLOZIiOtin+alixN/W41Ij0jOtRycM5wq3Xr/0RAs5A
ziNeQvWdvDwqa6L9upHZpFfYqP/+KflJPlHLfEkBHZtZQF3uy5tQ1VusfVMO3Xvb
Ljk6RBnD9dKayreD9NVzotr36zYEy/V1oGJcP/8AD1xmDA0/2Kb+bfm+WQHG5wp6
o09zsZECgYEA5Y3d79Nrfi6InVaZ0r5Y+XXqSZFTsgFnxRseVEbuK4jvrh7eC9jW
pWoaXDh8W6RoT59BPSCKtbQ9mjdECh+aJ6MOeCzCiGSppJboOhC1iVFqFuQLDRO7
w+2NgkhOvNnJJJYmdTwfokTLmaRUiNqwWAtBm+h7py9e5eXujzqt4+UCgYEAxBKL
OO8CWRb0yGlwKoCwYgGCsVauvbZHaELOAKJUB6H+YhZ+SJqd915u8aYs5nbcMyne
5kuewzd+32WpkykI0Fz4SrGvDETKB5/Yynblp9m69LNdgYoVWgQqQocXVw0nD/nA
KQdFSBZZRExXC/aUAa55txFJitMC4FjgTENgR/0CgYAS/OonxVg15sl8Ika1DPO1
JtDLZw8CQWWBA1494GQhC8GvqHP7jOMsaZtml3GJ7w6Fz4mI8eEnaJJT6FBjefu5
XZ57yFALEjCKIcVx0CIECsz4ucJEQaadbU/wP+TrcCRYN2dU+TUwqfohaltnupct
oTi7Gb7otF1oLN3P0S3DFQKBgEnVjdXPszunOGBrzBBFS6ZsWTG8qarJBFTPq1Fz
z17ccrWvMLjYeJnZVr/qyseyhLNDlit02IE82ar4VoYTEr2b9Ofzxy5AjS+X0wRT
B6JQjGVvUcvhGq8+GEfbJT/jtQ0ACIuqsD04JT9h2/mmTg/gCveUK/R6B4BCF5zA
VnZlAoGBANOG5T7KsOH+Hbb//dEmfZYMQmkO/+1Pac9eP4uDFNAVF00M6XHf5LrR
gRp5t46drDtLgxdufN0oUItp8y74EuMa9FHlBQVZRaSqYF4bCgf2PieGApbKWG2n
QhnxYfZqf9S2oVK95EHiJxmtumOFBL7YI9NdzNEeoJExS5Bw6kUn
-----END RSA PRIVATE KEY-----

File diff suppressed because it is too large Load Diff

View File

@ -65,6 +65,7 @@ add_executable(ws
ws_autobahn.cpp
ws_proxy_server.cpp
ws_sentry_minidump_upload.cpp
ws_dns_lookup.cpp
ws.cpp)
target_link_libraries(ws ixsnake)

View File

@ -338,6 +338,9 @@ int main(int argc, char** argv)
minidumpApp->add_option("--key", key, "Sentry Key")->required();
minidumpApp->add_flag("-v", verbose, "Verbose");
CLI::App* dnsLookupApp = app.add_subcommand("dnslookup", "DNS lookup");
dnsLookupApp->add_option("host", hostname, "Hostname")->required();
CLI11_PARSE(app, argc, argv);
// pid file handling
@ -509,6 +512,10 @@ int main(int argc, char** argv)
{
ret = ix::ws_sentry_minidump_upload(metadata, minidump, project, key, verbose);
}
else if (app.got_subcommand("dnslookup"))
{
ret = ix::ws_dns_lookup(hostname);
}
else if (version)
{
spdlog::info("ws {}", ix::userAgent());

View File

@ -163,4 +163,6 @@ namespace ix
const std::string& project,
const std::string& key,
bool verbose);
int ws_dns_lookup(const std::string& hostname);
} // namespace ix

View File

@ -72,7 +72,7 @@ namespace ix
size_t bufferedAmount = client->bufferedAmount();
spdlog::info("{} bytes left to be sent", bufferedAmount);
std::chrono::duration<double, std::milli> duration(10);
std::chrono::duration<double, std::milli> duration(500);
std::this_thread::sleep_for(duration);
} while (client->bufferedAmount() != 0);
}

View File

@ -10,12 +10,12 @@
//
#include "nlohmann/json.hpp"
#include <iostream>
#include <ixwebsocket/IXSocket.h>
#include <ixwebsocket/IXWebSocket.h>
#include <queue>
#include <spdlog/spdlog.h>
#include <sstream>
#include <iostream>
// for convenience
using json = nlohmann::json;

34
ws/ws_dns_lookup.cpp Normal file
View File

@ -0,0 +1,34 @@
/*
* ws_dns_lookup.cpp
* Author: Benjamin Sergeant
* Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
*/
#include <atomic>
#include <spdlog/spdlog.h>
#include <sstream>
#include <ixwebsocket/IXNetSystem.h>
#include <ixwebsocket/IXDNSLookup.h>
namespace ix
{
int ws_dns_lookup(const std::string& hostname)
{
auto dnsLookup = std::make_shared<DNSLookup>(hostname, 80);
std::string errMsg;
struct addrinfo* res;
res = dnsLookup->resolve(errMsg, [] { return false; });
auto addr = res->ai_addr;
char str[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &addr, str, INET_ADDRSTRLEN);
spdlog::info("host: {} ip: {}", hostname, str);
return 0;
}
} // namespace ix

View File

@ -45,6 +45,8 @@ namespace ix
ix::WebSocket _webSocket;
bool _enablePerMessageDeflate;
std::atomic<bool> _connected;
std::mutex _conditionVariableMutex;
std::condition_variable _condition;
@ -56,6 +58,7 @@ namespace ix
const ix::SocketTLSOptions& tlsOptions)
: _url(url)
, _enablePerMessageDeflate(enablePerMessageDeflate)
, _connected(false)
{
_webSocket.disableAutomaticReconnection();
_webSocket.setTLSOptions(tlsOptions);
@ -119,6 +122,8 @@ namespace ix
std::stringstream ss;
if (msg->type == ix::WebSocketMessageType::Open)
{
_connected = true;
_condition.notify_one();
log("ws_send: connected");
@ -131,6 +136,8 @@ namespace ix
}
else if (msg->type == ix::WebSocketMessageType::Close)
{
_connected = false;
ss << "ws_send: connection closed:";
ss << " code " << msg->closeInfo.code;
ss << " reason " << msg->closeInfo.reason << std::endl;
@ -234,7 +241,7 @@ namespace ix
{
std::vector<uint8_t> content;
{
Bench bench("load file from disk");
Bench bench("ws_send: load file from disk");
content = load(filename);
}
@ -250,18 +257,22 @@ namespace ix
MsgPack msg(pdu);
Bench bench("Sending file through websocket");
auto result = _webSocket.sendBinary(msg.dump(), [throttle](int current, int total) -> bool {
spdlog::info("ws_send: Step {} out of {}", current, total);
auto serializedMsg = msg.dump();
spdlog::info("ws_send: sending {} bytes", serializedMsg.size());
if (throttle)
{
std::chrono::duration<double, std::milli> duration(10);
std::this_thread::sleep_for(duration);
}
Bench bench("ws_send: Sending file through websocket");
auto result =
_webSocket.sendBinary(serializedMsg, [this, throttle](int current, int total) -> bool {
spdlog::info("ws_send: Step {} out of {}", current + 1, total);
return true;
});
if (throttle)
{
std::chrono::duration<double, std::milli> duration(10);
std::this_thread::sleep_for(duration);
}
return _connected;
});
if (!result.success)
{
@ -269,22 +280,37 @@ namespace ix
return false;
}
if (!_connected)
{
spdlog::error("ws_send: Got disconnected from the server");
return false;
}
spdlog::info("ws_send: sent {} bytes", serializedMsg.size());
do
{
size_t bufferedAmount = _webSocket.bufferedAmount();
spdlog::info("ws_send: {} bytes left to be sent", bufferedAmount);
std::chrono::duration<double, std::milli> duration(10);
std::chrono::duration<double, std::milli> duration(500);
std::this_thread::sleep_for(duration);
} while (_webSocket.bufferedAmount() != 0);
} while (_webSocket.bufferedAmount() != 0 && _connected);
bench.report();
auto duration = bench.getDuration();
auto transferRate = 1000 * content.size() / duration;
transferRate /= (1024 * 1024);
spdlog::info("ws_send: Send transfer rate: {} MB/s", transferRate);
if (_connected)
{
bench.report();
auto duration = bench.getDuration();
auto transferRate = 1000 * content.size() / duration;
transferRate /= (1024 * 1024);
spdlog::info("ws_send: Send transfer rate: {} MB/s", transferRate);
}
else
{
spdlog::error("ws_send: Got disconnected from the server");
}
return true;
return _connected;
}
void wsSend(const std::string& url,
@ -304,6 +330,10 @@ namespace ix
webSocketSender.waitForAck();
spdlog::info("ws_send: Done !");
}
else
{
spdlog::error("ws_send: Error sending file.");
}
webSocketSender.stop();
}

View File

@ -102,7 +102,7 @@ namespace ix
size_t bufferedAmount = client->bufferedAmount();
spdlog::info(
"{}: [client {}]: has readystate {} bytes left to be sent",
"{}: [client {}]: has readystate {} bytes left to be sent {}",
"ws_transfer",
id,
readyStateString,