Compare commits
28 Commits
Author | SHA1 | Date | |
---|---|---|---|
44a813caae | |||
78af792f23 | |||
1ee8e31483 | |||
e927dcca3a | |||
86c2ab450e | |||
f3d562ff02 | |||
3c27228585 | |||
9d7807b25c | |||
6256264d78 | |||
04c9985030 | |||
99fe6ea493 | |||
acab19fe9f | |||
2f59fcc02d | |||
8a344788ba | |||
5d641bc267 | |||
0c4866420a | |||
8ed7c660d4 | |||
4cbf85f4d4 | |||
a8ff91ac3c | |||
4cb3326078 | |||
87625a8805 | |||
b75264b98f | |||
21f42210a2 | |||
27287aea50 | |||
39f69bd2b6 | |||
00dc8bacee | |||
9e24475fd6 | |||
61bcc9d27d |
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -1,6 +1,26 @@
|
||||
# Changelog
|
||||
All changes to this project will be documented in this file.
|
||||
|
||||
## [8.0.0] - 2020-01-26
|
||||
|
||||
(SocketServer) add ability to bind on an ipv6 address
|
||||
|
||||
## [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
|
||||
|
@ -33,11 +33,16 @@ vcpkg install ixwebsocket
|
||||
|
||||
### Conan
|
||||
|
||||
Support for building with conan was contributed by Olivia Zoe (thanks!). The package name to reference is `IXWebSocket/5.0.0@LunarWatcher/stable`, and a list of the uploaded versions is available on [Bintray](https://bintray.com/oliviazoe0/conan-packages/IXWebSocket%3ALunarWatcher). The package is in the process to be published to the official conan package repo, but in the meantime, it can be accessed by adding a new remote
|
||||
[  ](https://bintray.com/conan/conan-center/ixwebsocket%3A_/_latestVersion)
|
||||
|
||||
```
|
||||
conan remote add remote_name_here https://api.bintray.com/conan/oliviazoe0/conan-packages
|
||||
```
|
||||
Conan is currently supported through a recipe in [Conan Center](https://github.com/conan-io/conan-center-index/tree/master/recipes/ixwebsocket) ([Bintray entry](https://bintray.com/conan/conan-center/ixwebsocket%3A_)).
|
||||
|
||||
Package reference
|
||||
|
||||
* Conan 1.21.0 and up: `ixwebsocket/7.9.2`
|
||||
* Earlier versions: `ixwebsocket/7.9.2@_/_`
|
||||
|
||||
Note that the version listed here might not be the latest one. See Bintray or the recipe itself for the latest version. If you're migrating from the previous, custom Bintray remote, note that the package reference _has_ to be lower-case.
|
||||
|
||||
### Docker
|
||||
|
||||
|
@ -17,8 +17,8 @@
|
||||
|
||||
namespace ix
|
||||
{
|
||||
RedisServer::RedisServer(int port, const std::string& host, int backlog, size_t maxConnections)
|
||||
: SocketServer(port, host, backlog, maxConnections)
|
||||
RedisServer::RedisServer(int port, const std::string& host, int backlog, size_t maxConnections, int addressFamily)
|
||||
: SocketServer(port, host, backlog, maxConnections, addressFamily)
|
||||
, _connectedClientsCount(0)
|
||||
, _stopHandlingConnections(false)
|
||||
{
|
||||
|
@ -25,7 +25,8 @@ namespace ix
|
||||
RedisServer(int port = SocketServer::kDefaultPort,
|
||||
const std::string& host = SocketServer::kDefaultHost,
|
||||
int backlog = SocketServer::kDefaultTcpBacklog,
|
||||
size_t maxConnections = SocketServer::kDefaultMaxConnections);
|
||||
size_t maxConnections = SocketServer::kDefaultMaxConnections,
|
||||
int addressFamily = SocketServer::kDefaultAddressFamily);
|
||||
virtual ~RedisServer();
|
||||
virtual void stop() final;
|
||||
|
||||
|
@ -42,8 +42,8 @@ namespace
|
||||
|
||||
namespace ix
|
||||
{
|
||||
HttpServer::HttpServer(int port, const std::string& host, int backlog, size_t maxConnections)
|
||||
: SocketServer(port, host, backlog, maxConnections)
|
||||
HttpServer::HttpServer(int port, const std::string& host, int backlog, size_t maxConnections, int addressFamily)
|
||||
: SocketServer(port, host, backlog, maxConnections, addressFamily)
|
||||
, _connectedClientsCount(0)
|
||||
{
|
||||
setDefaultConnectionCallback();
|
||||
|
@ -28,7 +28,8 @@ namespace ix
|
||||
HttpServer(int port = SocketServer::kDefaultPort,
|
||||
const std::string& host = SocketServer::kDefaultHost,
|
||||
int backlog = SocketServer::kDefaultTcpBacklog,
|
||||
size_t maxConnections = SocketServer::kDefaultMaxConnections);
|
||||
size_t maxConnections = SocketServer::kDefaultMaxConnections,
|
||||
int addressFamily = SocketServer::kDefaultAddressFamily);
|
||||
virtual ~HttpServer();
|
||||
virtual void stop() final;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -265,11 +265,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)
|
||||
{
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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:
|
||||
|
@ -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
|
@ -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
|
@ -21,15 +21,18 @@ namespace ix
|
||||
const std::string SocketServer::kDefaultHost("127.0.0.1");
|
||||
const int SocketServer::kDefaultTcpBacklog(5);
|
||||
const size_t SocketServer::kDefaultMaxConnections(32);
|
||||
const int SocketServer::kDefaultAddressFamily(AF_INET);
|
||||
|
||||
SocketServer::SocketServer(int port,
|
||||
const std::string& host,
|
||||
int backlog,
|
||||
size_t maxConnections)
|
||||
size_t maxConnections,
|
||||
int addressFamily)
|
||||
: _port(port)
|
||||
, _host(host)
|
||||
, _backlog(backlog)
|
||||
, _maxConnections(maxConnections)
|
||||
, _addressFamily(addressFamily)
|
||||
, _serverFd(-1)
|
||||
, _stop(false)
|
||||
, _stopGc(false)
|
||||
@ -56,10 +59,15 @@ namespace ix
|
||||
|
||||
std::pair<bool, std::string> SocketServer::listen()
|
||||
{
|
||||
struct sockaddr_in server; // server address information
|
||||
if (_addressFamily != AF_INET && _addressFamily != AF_INET6)
|
||||
{
|
||||
std::string errMsg("SocketServer::listen() AF_INET and AF_INET6 are currently "
|
||||
"the only supported address families");
|
||||
return std::make_pair(false, errMsg);
|
||||
}
|
||||
|
||||
// Get a socket for accepting connections.
|
||||
if ((_serverFd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
|
||||
if ((_serverFd = socket(_addressFamily, SOCK_STREAM, 0)) < 0)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "SocketServer::listen() error creating socket): " << strerror(Socket::getErrno());
|
||||
@ -79,27 +87,59 @@ namespace ix
|
||||
return std::make_pair(false, ss.str());
|
||||
}
|
||||
|
||||
// Bind the socket to the server address.
|
||||
server.sin_family = AF_INET;
|
||||
server.sin_port = htons(_port);
|
||||
|
||||
// Using INADDR_ANY trigger a pop-up box as binding to any address is detected
|
||||
// by the osx firewall. We need to codesign the binary with a self-signed cert
|
||||
// to allow that, but this is a bit of a pain. (this is what node or python would do).
|
||||
//
|
||||
// Using INADDR_LOOPBACK also does not work ... while it should.
|
||||
// We default to 127.0.0.1 (localhost)
|
||||
//
|
||||
server.sin_addr.s_addr = inet_addr(_host.c_str());
|
||||
|
||||
if (bind(_serverFd, (struct sockaddr*) &server, sizeof(server)) < 0)
|
||||
if (_addressFamily == AF_INET)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "SocketServer::listen() error calling bind "
|
||||
<< "at address " << _host << ":" << _port << " : " << strerror(Socket::getErrno());
|
||||
struct sockaddr_in server;
|
||||
server.sin_family = _addressFamily;
|
||||
server.sin_port = htons(_port);
|
||||
|
||||
Socket::closeSocket(_serverFd);
|
||||
return std::make_pair(false, ss.str());
|
||||
if (inet_pton(_addressFamily, _host.c_str(), &server.sin_addr.s_addr) <= 0)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "SocketServer::listen() error calling inet_pton "
|
||||
<< "at address " << _host << ":" << _port << " : " << strerror(Socket::getErrno());
|
||||
|
||||
Socket::closeSocket(_serverFd);
|
||||
return std::make_pair(false, ss.str());
|
||||
}
|
||||
|
||||
// Bind the socket to the server address.
|
||||
if (bind(_serverFd, (struct sockaddr*) &server, sizeof(server)) < 0)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "SocketServer::listen() error calling bind "
|
||||
<< "at address " << _host << ":" << _port << " : " << strerror(Socket::getErrno());
|
||||
|
||||
Socket::closeSocket(_serverFd);
|
||||
return std::make_pair(false, ss.str());
|
||||
}
|
||||
}
|
||||
else // AF_INET6
|
||||
{
|
||||
struct sockaddr_in6 server;
|
||||
server.sin6_family = _addressFamily;
|
||||
server.sin6_port = htons(_port);
|
||||
|
||||
if (inet_pton(_addressFamily, _host.c_str(), &server.sin6_addr) <= 0)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "SocketServer::listen() error calling inet_pton "
|
||||
<< "at address " << _host << ":" << _port << " : " << strerror(Socket::getErrno());
|
||||
|
||||
Socket::closeSocket(_serverFd);
|
||||
return std::make_pair(false, ss.str());
|
||||
}
|
||||
|
||||
// Bind the socket to the server address.
|
||||
if (bind(_serverFd, (struct sockaddr*) &server, sizeof(server)) < 0)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "SocketServer::listen() error calling bind "
|
||||
<< "at address " << _host << ":" << _port << " : " << strerror(Socket::getErrno());
|
||||
|
||||
Socket::closeSocket(_serverFd);
|
||||
return std::make_pair(false, ss.str());
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -36,7 +36,8 @@ namespace ix
|
||||
SocketServer(int port = SocketServer::kDefaultPort,
|
||||
const std::string& host = SocketServer::kDefaultHost,
|
||||
int backlog = SocketServer::kDefaultTcpBacklog,
|
||||
size_t maxConnections = SocketServer::kDefaultMaxConnections);
|
||||
size_t maxConnections = SocketServer::kDefaultMaxConnections,
|
||||
int addressFamily = SocketServer::kDefaultAddressFamily);
|
||||
virtual ~SocketServer();
|
||||
virtual void stop();
|
||||
|
||||
@ -49,6 +50,7 @@ namespace ix
|
||||
const static std::string kDefaultHost;
|
||||
const static int kDefaultTcpBacklog;
|
||||
const static size_t kDefaultMaxConnections;
|
||||
const static int kDefaultAddressFamily;
|
||||
|
||||
void start();
|
||||
std::pair<bool, std::string> listen();
|
||||
@ -69,6 +71,7 @@ namespace ix
|
||||
std::string _host;
|
||||
int _backlog;
|
||||
size_t _maxConnections;
|
||||
int _addressFamily;
|
||||
|
||||
// socket for accepting connections
|
||||
int _serverFd;
|
||||
|
@ -169,6 +169,7 @@ namespace ix
|
||||
// wait until working thread will exit
|
||||
// it will exit after close operation is finished
|
||||
_stop = true;
|
||||
_sleepCondition.notify_one();
|
||||
_thread.join();
|
||||
_stop = false;
|
||||
}
|
||||
@ -282,8 +283,13 @@ namespace ix
|
||||
// Only sleep if we are retrying
|
||||
if (duration.count() > 0)
|
||||
{
|
||||
// to do: make sleeping conditional
|
||||
std::this_thread::sleep_for(duration);
|
||||
std::unique_lock<std::mutex> lock(_sleepMutex);
|
||||
_sleepCondition.wait_for(lock, duration);
|
||||
}
|
||||
|
||||
if (_stop)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Try to connect synchronously
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <condition_variable>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
@ -140,6 +141,10 @@ namespace ix
|
||||
static const uint32_t kDefaultMaxWaitBetweenReconnectionRetries;
|
||||
uint32_t _maxWaitBetweenReconnectionRetries;
|
||||
|
||||
// Make the sleeping in the automatic reconnection cancellable
|
||||
std::mutex _sleepMutex;
|
||||
std::condition_variable _sleepCondition;
|
||||
|
||||
std::atomic<int> _handshakeTimeoutSecs;
|
||||
static const int kDefaultHandShakeTimeoutSecs;
|
||||
|
||||
|
@ -23,8 +23,9 @@ namespace ix
|
||||
const std::string& host,
|
||||
int backlog,
|
||||
size_t maxConnections,
|
||||
int handshakeTimeoutSecs)
|
||||
: SocketServer(port, host, backlog, maxConnections)
|
||||
int handshakeTimeoutSecs,
|
||||
int addressFamily)
|
||||
: SocketServer(port, host, backlog, maxConnections, addressFamily)
|
||||
, _handshakeTimeoutSecs(handshakeTimeoutSecs)
|
||||
, _enablePong(kDefaultEnablePong)
|
||||
{
|
||||
@ -106,7 +107,6 @@ namespace ix
|
||||
}
|
||||
}
|
||||
|
||||
logInfo("WebSocketServer::handleConnection() done");
|
||||
connectionState->setTerminated();
|
||||
}
|
||||
|
||||
|
@ -29,7 +29,8 @@ namespace ix
|
||||
const std::string& host = SocketServer::kDefaultHost,
|
||||
int backlog = SocketServer::kDefaultTcpBacklog,
|
||||
size_t maxConnections = SocketServer::kDefaultMaxConnections,
|
||||
int handshakeTimeoutSecs = WebSocketServer::kDefaultHandShakeTimeoutSecs);
|
||||
int handshakeTimeoutSecs = WebSocketServer::kDefaultHandShakeTimeoutSecs,
|
||||
int addressFamily = SocketServer::kDefaultAddressFamily);
|
||||
virtual ~WebSocketServer();
|
||||
virtual void stop() final;
|
||||
|
||||
@ -41,6 +42,8 @@ namespace ix
|
||||
// Get all the connected clients
|
||||
std::set<std::shared_ptr<WebSocket>> getClients();
|
||||
|
||||
const static int kDefaultHandShakeTimeoutSecs;
|
||||
|
||||
private:
|
||||
// Member variables
|
||||
int _handshakeTimeoutSecs;
|
||||
@ -51,7 +54,6 @@ namespace ix
|
||||
std::mutex _clientsMutex;
|
||||
std::set<std::shared_ptr<WebSocket>> _clients;
|
||||
|
||||
const static int kDefaultHandShakeTimeoutSecs;
|
||||
const static bool kDefaultEnablePong;
|
||||
|
||||
// Methods
|
||||
|
@ -350,28 +350,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 +720,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 +1034,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 +1065,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;
|
||||
|
@ -99,7 +99,6 @@ namespace ix
|
||||
bool remote = false);
|
||||
|
||||
void closeSocket();
|
||||
ssize_t send();
|
||||
|
||||
ReadyState getReadyState() const;
|
||||
void setReadyState(ReadyState readyState);
|
||||
@ -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,
|
||||
|
@ -6,4 +6,4 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#define IX_WEBSOCKET_VERSION "7.9.2"
|
||||
#define IX_WEBSOCKET_VERSION "8.0.0"
|
||||
|
@ -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()
|
||||
|
@ -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"]
|
||||
|
@ -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
|
||||
```
|
||||
|
36
test/compatibility/python/websockets/broadcast_server.py
Normal file
36
test/compatibility/python/websockets/broadcast_server.py
Normal 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()
|
||||
|
43
test/compatibility/python/websockets/broadcast_server_ssl.py
Normal file
43
test/compatibility/python/websockets/broadcast_server_ssl.py
Normal 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()
|
||||
|
||||
|
@ -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()
|
||||
|
27
test/compatibility/python/websockets/echo_server_ssl.py
Normal file
27
test/compatibility/python/websockets/echo_server_ssl.py
Normal 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()
|
16
test/compatibility/python/websockets/entrypoint.sh
Normal file
16
test/compatibility/python/websockets/entrypoint.sh
Normal 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
|
1
test/compatibility/python/websockets/localhost.pem
Symbolic link
1
test/compatibility/python/websockets/localhost.pem
Symbolic link
@ -0,0 +1 @@
|
||||
trusted-client-crt.pem
|
@ -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
|
||||
|
19
test/compatibility/python/websockets/trusted-server-crt.pem
Normal file
19
test/compatibility/python/websockets/trusted-server-crt.pem
Normal 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-----
|
27
test/compatibility/python/websockets/trusted-server-key.pem
Normal file
27
test/compatibility/python/websockets/trusted-server-key.pem
Normal 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-----
|
1432
test/compatibility/python/websockets/vendor/protocol.py
vendored
Normal file
1432
test/compatibility/python/websockets/vendor/protocol.py
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@ -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)
|
||||
|
11
ws/ws.cpp
11
ws/ws.cpp
@ -94,6 +94,7 @@ int main(int argc, char** argv)
|
||||
bool disableAutomaticReconnection = false;
|
||||
bool disablePerMessageDeflate = false;
|
||||
bool greetings = false;
|
||||
bool ipv6 = false;
|
||||
bool binaryMode = false;
|
||||
bool redirect = false;
|
||||
bool version = false;
|
||||
@ -171,6 +172,7 @@ int main(int argc, char** argv)
|
||||
echoServerApp->add_option("--port", port, "Port");
|
||||
echoServerApp->add_option("--host", hostname, "Hostname");
|
||||
echoServerApp->add_flag("-g", greetings, "Verbose");
|
||||
echoServerApp->add_flag("-6", ipv6, "IpV6");
|
||||
addTLSOptions(echoServerApp);
|
||||
|
||||
CLI::App* broadcastServerApp = app.add_subcommand("broadcast_server", "Broadcasting server");
|
||||
@ -338,6 +340,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
|
||||
@ -387,7 +392,7 @@ int main(int argc, char** argv)
|
||||
}
|
||||
else if (app.got_subcommand("echo_server"))
|
||||
{
|
||||
ret = ix::ws_echo_server_main(port, greetings, hostname, tlsOptions);
|
||||
ret = ix::ws_echo_server_main(port, greetings, hostname, tlsOptions, ipv6);
|
||||
}
|
||||
else if (app.got_subcommand("broadcast_server"))
|
||||
{
|
||||
@ -509,6 +514,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());
|
||||
|
6
ws/ws.h
6
ws/ws.h
@ -29,7 +29,9 @@ namespace ix
|
||||
int ws_echo_server_main(int port,
|
||||
bool greetings,
|
||||
const std::string& hostname,
|
||||
const ix::SocketTLSOptions& tlsOptions);
|
||||
const ix::SocketTLSOptions& tlsOptions,
|
||||
bool ipv6);
|
||||
|
||||
int ws_broadcast_server_main(int port,
|
||||
const std::string& hostname,
|
||||
const ix::SocketTLSOptions& tlsOptions);
|
||||
@ -163,4 +165,6 @@ namespace ix
|
||||
const std::string& project,
|
||||
const std::string& key,
|
||||
bool verbose);
|
||||
|
||||
int ws_dns_lookup(const std::string& hostname);
|
||||
} // namespace ix
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
34
ws/ws_dns_lookup.cpp
Normal 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
|
@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
#include <ixwebsocket/IXWebSocketServer.h>
|
||||
#include <ixwebsocket/IXNetSystem.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <sstream>
|
||||
|
||||
@ -13,11 +14,18 @@ namespace ix
|
||||
int ws_echo_server_main(int port,
|
||||
bool greetings,
|
||||
const std::string& hostname,
|
||||
const ix::SocketTLSOptions& tlsOptions)
|
||||
const ix::SocketTLSOptions& tlsOptions,
|
||||
bool ipv6)
|
||||
{
|
||||
spdlog::info("Listening on {}:{}", hostname, port);
|
||||
|
||||
ix::WebSocketServer server(port, hostname);
|
||||
ix::WebSocketServer server(port,
|
||||
hostname,
|
||||
SocketServer::kDefaultTcpBacklog,
|
||||
SocketServer::kDefaultMaxConnections,
|
||||
WebSocketServer::kDefaultHandShakeTimeoutSecs,
|
||||
(ipv6) ? AF_INET6 : AF_INET);
|
||||
|
||||
server.setTLSOptions(tlsOptions);
|
||||
|
||||
server.setOnConnectionCallback(
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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,
|
||||
|
Reference in New Issue
Block a user