reformat everything with clang-format

This commit is contained in:
Benjamin Sergeant 2019-09-23 10:25:23 -07:00
parent 398c4fbf99
commit cd3c9d879c
92 changed files with 3158 additions and 3348 deletions

View File

@ -10,14 +10,13 @@
namespace ix namespace ix
{ {
CancellationRequest makeCancellationRequestWithTimeout(int secs, CancellationRequest makeCancellationRequestWithTimeout(
std::atomic<bool>& requestInitCancellation) int secs, std::atomic<bool>& requestInitCancellation)
{ {
auto start = std::chrono::system_clock::now(); auto start = std::chrono::system_clock::now();
auto timeout = std::chrono::seconds(secs); auto timeout = std::chrono::seconds(secs);
auto isCancellationRequested = [&requestInitCancellation, start, timeout]() -> bool auto isCancellationRequested = [&requestInitCancellation, start, timeout]() -> bool {
{
// Was an explicit cancellation requested ? // Was an explicit cancellation requested ?
if (requestInitCancellation) return true; if (requestInitCancellation) return true;
@ -30,4 +29,4 @@ namespace ix
return isCancellationRequested; return isCancellationRequested;
} }
} } // namespace ix

View File

@ -10,7 +10,8 @@ namespace ix
{ {
std::atomic<uint64_t> ConnectionState::_globalId(0); std::atomic<uint64_t> ConnectionState::_globalId(0);
ConnectionState::ConnectionState() : _terminated(false) ConnectionState::ConnectionState()
: _terminated(false)
{ {
computeId(); computeId();
} }
@ -39,5 +40,4 @@ namespace ix
{ {
_terminated = true; _terminated = true;
} }
} } // namespace ix

View File

@ -5,22 +5,22 @@
*/ */
#include "IXDNSLookup.h" #include "IXDNSLookup.h"
#include "IXNetSystem.h"
#include <string.h> #include "IXNetSystem.h"
#include <chrono> #include <chrono>
#include <string.h>
#include <thread> #include <thread>
namespace ix namespace ix
{ {
const int64_t DNSLookup::kDefaultWait = 1; // ms const int64_t DNSLookup::kDefaultWait = 1; // ms
DNSLookup::DNSLookup(const std::string& hostname, int port, int64_t wait) : DNSLookup::DNSLookup(const std::string& hostname, int port, int64_t wait)
_hostname(hostname), : _hostname(hostname)
_port(port), , _port(port)
_wait(wait), , _wait(wait)
_res(nullptr), , _res(nullptr)
_done(false) , _done(false)
{ {
; ;
} }
@ -38,8 +38,7 @@ namespace ix
std::string sport = std::to_string(port); std::string sport = std::to_string(port);
struct addrinfo* res; struct addrinfo* res;
int getaddrinfo_result = getaddrinfo(hostname.c_str(), sport.c_str(), int getaddrinfo_result = getaddrinfo(hostname.c_str(), sport.c_str(), &hints, &res);
&hints, &res);
if (getaddrinfo_result) if (getaddrinfo_result)
{ {
errMsg = gai_strerror(getaddrinfo_result); errMsg = gai_strerror(getaddrinfo_result);
@ -56,8 +55,8 @@ namespace ix
: resolveUnCancellable(errMsg, isCancellationRequested); : resolveUnCancellable(errMsg, isCancellationRequested);
} }
struct addrinfo* DNSLookup::resolveUnCancellable(std::string& errMsg, struct addrinfo* DNSLookup::resolveUnCancellable(
const CancellationRequest& isCancellationRequested) std::string& errMsg, const CancellationRequest& isCancellationRequested)
{ {
errMsg = "no error"; errMsg = "no error";
@ -71,8 +70,8 @@ namespace ix
return getAddrInfo(_hostname, _port, errMsg); return getAddrInfo(_hostname, _port, errMsg);
} }
struct addrinfo* DNSLookup::resolveCancellable(std::string& errMsg, struct addrinfo* DNSLookup::resolveCancellable(
const CancellationRequest& isCancellationRequested) std::string& errMsg, const CancellationRequest& isCancellationRequested)
{ {
errMsg = "no error"; errMsg = "no error";
@ -126,7 +125,9 @@ namespace ix
return getRes(); return getRes();
} }
void DNSLookup::run(std::weak_ptr<DNSLookup> self, std::string hostname, int port) // thread runner void DNSLookup::run(std::weak_ptr<DNSLookup> self,
std::string hostname,
int port) // thread runner
{ {
// We don't want to read or write into members variables of an object that could be // We don't want to read or write into members variables of an object that could be
// gone, so we use temporary variables (res) or we pass in by copy everything that // gone, so we use temporary variables (res) or we pass in by copy everything that
@ -167,4 +168,4 @@ namespace ix
std::lock_guard<std::mutex> lock(_resMutex); std::lock_guard<std::mutex> lock(_resMutex);
return _res; return _res;
} }
} } // namespace ix

View File

@ -10,9 +10,8 @@
namespace ix namespace ix
{ {
uint32_t calculateRetryWaitMilliseconds( uint32_t calculateRetryWaitMilliseconds(uint32_t retry_count,
uint32_t retry_count, uint32_t maxWaitBetweenReconnectionRetries)
uint32_t maxWaitBetweenReconnectionRetries)
{ {
uint32_t wait_time = std::pow(2, retry_count) * 100; uint32_t wait_time = std::pow(2, retry_count) * 100;
@ -23,4 +22,4 @@ namespace ix
return wait_time; return wait_time;
} }
} } // namespace ix

View File

@ -10,7 +10,6 @@
namespace ix namespace ix
{ {
uint32_t calculateRetryWaitMilliseconds( uint32_t calculateRetryWaitMilliseconds(uint32_t retry_count,
uint32_t retry_count, uint32_t maxWaitBetweenReconnectionRetries);
uint32_t maxWaitBetweenReconnectionRetries);
} // namespace ix } // namespace ix

View File

@ -5,9 +5,9 @@
*/ */
#include "IXHttp.h" #include "IXHttp.h"
#include "IXCancellationRequest.h" #include "IXCancellationRequest.h"
#include "IXSocket.h" #include "IXSocket.h"
#include <sstream> #include <sstream>
#include <vector> #include <vector>
@ -57,7 +57,8 @@ namespace ix
return std::make_pair(httpVersion, statusCode); return std::make_pair(httpVersion, statusCode);
} }
std::tuple<std::string, std::string, std::string> Http::parseRequestLine(const std::string& line) std::tuple<std::string, std::string, std::string> Http::parseRequestLine(
const std::string& line)
{ {
// Request-Line = Method SP Request-URI SP HTTP-Version CRLF // Request-Line = Method SP Request-URI SP HTTP-Version CRLF
std::string token; std::string token;
@ -114,8 +115,8 @@ namespace ix
// Parse request line (GET /foo HTTP/1.1\r\n) // Parse request line (GET /foo HTTP/1.1\r\n)
auto requestLine = Http::parseRequestLine(line); auto requestLine = Http::parseRequestLine(line);
auto method = std::get<0>(requestLine); auto method = std::get<0>(requestLine);
auto uri = std::get<1>(requestLine); auto uri = std::get<1>(requestLine);
auto httpVersion = std::get<2>(requestLine); auto httpVersion = std::get<2>(requestLine);
// Retrieve and validate HTTP headers // Retrieve and validate HTTP headers
@ -161,8 +162,6 @@ namespace ix
return false; return false;
} }
return response->payload.empty() return response->payload.empty() ? true : socket->writeBytes(response->payload, nullptr);
? true
: socket->writeBytes(response->payload, nullptr);
} }
} } // namespace ix

View File

@ -115,8 +115,7 @@ namespace ix
std::shared_ptr<Socket> socket); std::shared_ptr<Socket> socket);
static bool sendResponse(HttpResponsePtr response, std::shared_ptr<Socket> socket); static bool sendResponse(HttpResponsePtr response, std::shared_ptr<Socket> socket);
static std::pair<std::string, int> parseStatusLine( static std::pair<std::string, int> parseStatusLine(const std::string& line);
const std::string& line);
static std::tuple<std::string, std::string, std::string> parseRequestLine( static std::tuple<std::string, std::string, std::string> parseRequestLine(
const std::string& line); const std::string& line);
static std::string trim(const std::string& str); static std::string trim(const std::string& str);

View File

@ -5,17 +5,16 @@
*/ */
#include "IXHttpClient.h" #include "IXHttpClient.h"
#include "IXSocketFactory.h"
#include "IXUrlParser.h" #include "IXUrlParser.h"
#include "IXUserAgent.h" #include "IXUserAgent.h"
#include "IXWebSocketHttpHeaders.h" #include "IXWebSocketHttpHeaders.h"
#include "IXSocketFactory.h"
#include <sstream>
#include <iomanip>
#include <vector>
#include <cstring>
#include <assert.h> #include <assert.h>
#include <cstring>
#include <iomanip>
#include <sstream>
#include <vector>
#include <zlib.h> #include <zlib.h>
namespace ix namespace ix
@ -26,7 +25,9 @@ namespace ix
const std::string HttpClient::kDel = "DEL"; const std::string HttpClient::kDel = "DEL";
const std::string HttpClient::kPut = "PUT"; const std::string HttpClient::kPut = "PUT";
HttpClient::HttpClient(bool async) : _async(async), _stop(false) HttpClient::HttpClient(bool async)
: _async(async)
, _stop(false)
{ {
if (!_async) return; if (!_async) return;
@ -42,8 +43,7 @@ namespace ix
_thread.join(); _thread.join();
} }
HttpRequestArgsPtr HttpClient::createRequest(const std::string& url, HttpRequestArgsPtr HttpClient::createRequest(const std::string& url, const std::string& verb)
const std::string& verb)
{ {
auto request = std::make_shared<HttpRequestArgs>(); auto request = std::make_shared<HttpRequestArgs>();
request->url = url; request->url = url;
@ -106,12 +106,11 @@ namespace ix
} }
} }
HttpResponsePtr HttpClient::request( HttpResponsePtr HttpClient::request(const std::string& url,
const std::string& url, const std::string& verb,
const std::string& verb, const std::string& body,
const std::string& body, HttpRequestArgsPtr args,
HttpRequestArgsPtr args, int redirects)
int redirects)
{ {
// We only have one socket connection, so we cannot // We only have one socket connection, so we cannot
// make multiple requests concurrently. // make multiple requests concurrently.
@ -131,9 +130,14 @@ namespace ix
{ {
std::stringstream ss; std::stringstream ss;
ss << "Cannot parse url: " << url; ss << "Cannot parse url: " << url;
return std::make_shared<HttpResponse>(code, description, HttpErrorCode::UrlMalformed, return std::make_shared<HttpResponse>(code,
headers, payload, ss.str(), description,
uploadSize, downloadSize); HttpErrorCode::UrlMalformed,
headers,
payload,
ss.str(),
uploadSize,
downloadSize);
} }
bool tls = protocol == "https"; bool tls = protocol == "https";
@ -143,9 +147,14 @@ namespace ix
if (!_socket) if (!_socket)
{ {
return std::make_shared<HttpResponse>(code, description, HttpErrorCode::CannotCreateSocket, return std::make_shared<HttpResponse>(code,
headers, payload, errorMsg, description,
uploadSize, downloadSize); HttpErrorCode::CannotCreateSocket,
headers,
payload,
errorMsg,
uploadSize,
downloadSize);
} }
// Build request string // Build request string
@ -155,7 +164,8 @@ namespace ix
if (args->compress) if (args->compress)
{ {
ss << "Accept-Encoding: gzip" << "\r\n"; ss << "Accept-Encoding: gzip"
<< "\r\n";
} }
// Append extra headers // Append extra headers
@ -167,7 +177,8 @@ namespace ix
// Set a default Accept header if none is present // Set a default Accept header if none is present
if (headers.find("Accept") == headers.end()) if (headers.find("Accept") == headers.end())
{ {
ss << "Accept: */*" << "\r\n"; ss << "Accept: */*"
<< "\r\n";
} }
// Set a default User agent if none is present // Set a default User agent if none is present
@ -183,7 +194,8 @@ namespace ix
// Set default Content-Type if unspecified // Set default Content-Type if unspecified
if (args->extraHeaders.find("Content-Type") == args->extraHeaders.end()) if (args->extraHeaders.find("Content-Type") == args->extraHeaders.end())
{ {
ss << "Content-Type: application/x-www-form-urlencoded" << "\r\n"; ss << "Content-Type: application/x-www-form-urlencoded"
<< "\r\n";
} }
ss << "\r\n"; ss << "\r\n";
ss << body; ss << body;
@ -206,9 +218,14 @@ namespace ix
{ {
std::stringstream ss; std::stringstream ss;
ss << "Cannot connect to url: " << url << " / error : " << errMsg; ss << "Cannot connect to url: " << url << " / error : " << errMsg;
return std::make_shared<HttpResponse>(code, description, HttpErrorCode::CannotConnect, return std::make_shared<HttpResponse>(code,
headers, payload, ss.str(), description,
uploadSize, downloadSize); HttpErrorCode::CannotConnect,
headers,
payload,
ss.str(),
uploadSize,
downloadSize);
} }
// Make a new cancellation object dealing with transfer timeout // Make a new cancellation object dealing with transfer timeout
@ -222,8 +239,7 @@ namespace ix
<< "to " << host << ":" << port << std::endl << "to " << host << ":" << port << std::endl
<< "request size: " << req.size() << " bytes" << std::endl << "request size: " << req.size() << " bytes" << std::endl
<< "=============" << std::endl << "=============" << std::endl
<< req << req << "=============" << std::endl
<< "=============" << std::endl
<< std::endl; << std::endl;
log(ss.str(), args); log(ss.str(), args);
@ -232,9 +248,14 @@ namespace ix
if (!_socket->writeBytes(req, isCancellationRequested)) if (!_socket->writeBytes(req, isCancellationRequested))
{ {
std::string errorMsg("Cannot send request"); std::string errorMsg("Cannot send request");
return std::make_shared<HttpResponse>(code, description, HttpErrorCode::SendError, return std::make_shared<HttpResponse>(code,
headers, payload, errorMsg, description,
uploadSize, downloadSize); HttpErrorCode::SendError,
headers,
payload,
errorMsg,
uploadSize,
downloadSize);
} }
uploadSize = req.size(); uploadSize = req.size();
@ -246,9 +267,14 @@ namespace ix
if (!lineValid) if (!lineValid)
{ {
std::string errorMsg("Cannot retrieve status line"); std::string errorMsg("Cannot retrieve status line");
return std::make_shared<HttpResponse>(code, description, HttpErrorCode::CannotReadStatusLine, return std::make_shared<HttpResponse>(code,
headers, payload, errorMsg, description,
uploadSize, downloadSize); HttpErrorCode::CannotReadStatusLine,
headers,
payload,
errorMsg,
uploadSize,
downloadSize);
} }
if (args->verbose) if (args->verbose)
@ -261,9 +287,14 @@ namespace ix
if (sscanf(line.c_str(), "HTTP/1.1 %d", &code) != 1) if (sscanf(line.c_str(), "HTTP/1.1 %d", &code) != 1)
{ {
std::string errorMsg("Cannot parse response code from status line"); std::string errorMsg("Cannot parse response code from status line");
return std::make_shared<HttpResponse>(code, description, HttpErrorCode::MissingStatus, return std::make_shared<HttpResponse>(code,
headers, payload, errorMsg, description,
uploadSize, downloadSize); HttpErrorCode::MissingStatus,
headers,
payload,
errorMsg,
uploadSize,
downloadSize);
} }
auto result = parseHttpHeaders(_socket, isCancellationRequested); auto result = parseHttpHeaders(_socket, isCancellationRequested);
@ -273,9 +304,14 @@ namespace ix
if (!headersValid) if (!headersValid)
{ {
std::string errorMsg("Cannot parse http headers"); std::string errorMsg("Cannot parse http headers");
return std::make_shared<HttpResponse>(code, description, HttpErrorCode::HeaderParsingError, return std::make_shared<HttpResponse>(code,
headers, payload, errorMsg, description,
uploadSize, downloadSize); HttpErrorCode::HeaderParsingError,
headers,
payload,
errorMsg,
uploadSize,
downloadSize);
} }
// Redirect ? // Redirect ?
@ -284,30 +320,45 @@ namespace ix
if (headers.find("Location") == headers.end()) if (headers.find("Location") == headers.end())
{ {
std::string errorMsg("Missing location header for redirect"); std::string errorMsg("Missing location header for redirect");
return std::make_shared<HttpResponse>(code, description, HttpErrorCode::MissingLocation, return std::make_shared<HttpResponse>(code,
headers, payload, errorMsg, description,
uploadSize, downloadSize); HttpErrorCode::MissingLocation,
headers,
payload,
errorMsg,
uploadSize,
downloadSize);
} }
if (redirects >= args->maxRedirects) if (redirects >= args->maxRedirects)
{ {
std::stringstream ss; std::stringstream ss;
ss << "Too many redirects: " << redirects; ss << "Too many redirects: " << redirects;
return std::make_shared<HttpResponse>(code, description, HttpErrorCode::TooManyRedirects, return std::make_shared<HttpResponse>(code,
headers, payload, ss.str(), description,
uploadSize, downloadSize); HttpErrorCode::TooManyRedirects,
headers,
payload,
ss.str(),
uploadSize,
downloadSize);
} }
// Recurse // Recurse
std::string location = headers["Location"]; std::string location = headers["Location"];
return request(location, verb, body, args, redirects+1); return request(location, verb, body, args, redirects + 1);
} }
if (verb == "HEAD") if (verb == "HEAD")
{ {
return std::make_shared<HttpResponse>(code, description, HttpErrorCode::Ok, return std::make_shared<HttpResponse>(code,
headers, payload, std::string(), description,
uploadSize, downloadSize); HttpErrorCode::Ok,
headers,
payload,
std::string(),
uploadSize,
downloadSize);
} }
// Parse response: // Parse response:
@ -320,15 +371,19 @@ namespace ix
payload.reserve(contentLength); payload.reserve(contentLength);
auto chunkResult = _socket->readBytes(contentLength, auto chunkResult = _socket->readBytes(
args->onProgressCallback, contentLength, args->onProgressCallback, isCancellationRequested);
isCancellationRequested);
if (!chunkResult.first) if (!chunkResult.first)
{ {
errorMsg = "Cannot read chunk"; errorMsg = "Cannot read chunk";
return std::make_shared<HttpResponse>(code, description, HttpErrorCode::ChunkReadError, return std::make_shared<HttpResponse>(code,
headers, payload, errorMsg, description,
uploadSize, downloadSize); HttpErrorCode::ChunkReadError,
headers,
payload,
errorMsg,
uploadSize,
downloadSize);
} }
payload += chunkResult.second; payload += chunkResult.second;
} }
@ -344,9 +399,14 @@ namespace ix
if (!lineResult.first) if (!lineResult.first)
{ {
return std::make_shared<HttpResponse>(code, description, HttpErrorCode::ChunkReadError, return std::make_shared<HttpResponse>(code,
headers, payload, errorMsg, description,
uploadSize, downloadSize); HttpErrorCode::ChunkReadError,
headers,
payload,
errorMsg,
uploadSize,
downloadSize);
} }
uint64_t chunkSize; uint64_t chunkSize;
@ -357,23 +417,26 @@ namespace ix
if (args->verbose) if (args->verbose)
{ {
std::stringstream oss; std::stringstream oss;
oss << "Reading " << chunkSize << " bytes" oss << "Reading " << chunkSize << " bytes" << std::endl;
<< std::endl;
log(oss.str(), args); log(oss.str(), args);
} }
payload.reserve(payload.size() + (size_t) chunkSize); payload.reserve(payload.size() + (size_t) chunkSize);
// Read a chunk // Read a chunk
auto chunkResult = _socket->readBytes((size_t) chunkSize, auto chunkResult = _socket->readBytes(
args->onProgressCallback, (size_t) chunkSize, args->onProgressCallback, isCancellationRequested);
isCancellationRequested);
if (!chunkResult.first) if (!chunkResult.first)
{ {
errorMsg = "Cannot read chunk"; errorMsg = "Cannot read chunk";
return std::make_shared<HttpResponse>(code, description, HttpErrorCode::ChunkReadError, return std::make_shared<HttpResponse>(code,
headers, payload, errorMsg, description,
uploadSize, downloadSize); HttpErrorCode::ChunkReadError,
headers,
payload,
errorMsg,
uploadSize,
downloadSize);
} }
payload += chunkResult.second; payload += chunkResult.second;
@ -382,9 +445,14 @@ namespace ix
if (!lineResult.first) if (!lineResult.first)
{ {
return std::make_shared<HttpResponse>(code, description, HttpErrorCode::ChunkReadError, return std::make_shared<HttpResponse>(code,
headers, payload, errorMsg, description,
uploadSize, downloadSize); HttpErrorCode::ChunkReadError,
headers,
payload,
errorMsg,
uploadSize,
downloadSize);
} }
if (chunkSize == 0) break; if (chunkSize == 0) break;
@ -397,9 +465,14 @@ namespace ix
else else
{ {
std::string errorMsg("Cannot read http body"); std::string errorMsg("Cannot read http body");
return std::make_shared<HttpResponse>(code, description, HttpErrorCode::CannotReadBody, return std::make_shared<HttpResponse>(code,
headers, payload, errorMsg, description,
uploadSize, downloadSize); HttpErrorCode::CannotReadBody,
headers,
payload,
errorMsg,
uploadSize,
downloadSize);
} }
downloadSize = payload.size(); downloadSize = payload.size();
@ -411,32 +484,39 @@ namespace ix
if (!gzipInflate(payload, decompressedPayload)) if (!gzipInflate(payload, decompressedPayload))
{ {
std::string errorMsg("Error decompressing payload"); std::string errorMsg("Error decompressing payload");
return std::make_shared<HttpResponse>(code, description, HttpErrorCode::Gzip, return std::make_shared<HttpResponse>(code,
headers, payload, errorMsg, description,
uploadSize, downloadSize); HttpErrorCode::Gzip,
headers,
payload,
errorMsg,
uploadSize,
downloadSize);
} }
payload = decompressedPayload; payload = decompressedPayload;
} }
return std::make_shared<HttpResponse>(code, description, HttpErrorCode::Ok, return std::make_shared<HttpResponse>(code,
headers, payload, std::string(), description,
uploadSize, downloadSize); HttpErrorCode::Ok,
headers,
payload,
std::string(),
uploadSize,
downloadSize);
} }
HttpResponsePtr HttpClient::get(const std::string& url, HttpResponsePtr HttpClient::get(const std::string& url, HttpRequestArgsPtr args)
HttpRequestArgsPtr args)
{ {
return request(url, kGet, std::string(), args); return request(url, kGet, std::string(), args);
} }
HttpResponsePtr HttpClient::head(const std::string& url, HttpResponsePtr HttpClient::head(const std::string& url, HttpRequestArgsPtr args)
HttpRequestArgsPtr args)
{ {
return request(url, kHead, std::string(), args); return request(url, kHead, std::string(), args);
} }
HttpResponsePtr HttpClient::del(const std::string& url, HttpResponsePtr HttpClient::del(const std::string& url, HttpRequestArgsPtr args)
HttpRequestArgsPtr args)
{ {
return request(url, kDel, std::string(), args); return request(url, kDel, std::string(), args);
} }
@ -475,8 +555,7 @@ namespace ix
escaped.fill('0'); escaped.fill('0');
escaped << std::hex; escaped << std::hex;
for (std::string::const_iterator i = value.begin(), n = value.end(); for (std::string::const_iterator i = value.begin(), n = value.end(); i != n; ++i)
i != n; ++i)
{ {
std::string::value_type c = (*i); std::string::value_type c = (*i);
@ -504,21 +583,17 @@ namespace ix
for (auto&& it : httpParameters) for (auto&& it : httpParameters)
{ {
ss << urlEncode(it.first) ss << urlEncode(it.first) << "=" << urlEncode(it.second);
<< "="
<< urlEncode(it.second);
if (i++ < (count-1)) if (i++ < (count - 1))
{ {
ss << "&"; ss << "&";
} }
} }
return ss.str(); return ss.str();
} }
bool HttpClient::gzipInflate( bool HttpClient::gzipInflate(const std::string& in, std::string& out)
const std::string& in,
std::string& out)
{ {
z_stream inflateState; z_stream inflateState;
std::memset(&inflateState, 0, sizeof(inflateState)); std::memset(&inflateState, 0, sizeof(inflateState));
@ -529,13 +604,13 @@ namespace ix
inflateState.avail_in = 0; inflateState.avail_in = 0;
inflateState.next_in = Z_NULL; inflateState.next_in = Z_NULL;
if (inflateInit2(&inflateState, 16+MAX_WBITS) != Z_OK) if (inflateInit2(&inflateState, 16 + MAX_WBITS) != Z_OK)
{ {
return false; return false;
} }
inflateState.avail_in = (uInt) in.size(); inflateState.avail_in = (uInt) in.size();
inflateState.next_in = (unsigned char *)(const_cast<char *>(in.data())); inflateState.next_in = (unsigned char*) (const_cast<char*>(in.data()));
const int kBufferSize = 1 << 14; const int kBufferSize = 1 << 14;
@ -555,22 +630,19 @@ namespace ix
return false; return false;
} }
out.append( out.append(reinterpret_cast<char*>(compressBuffer.get()),
reinterpret_cast<char *>(compressBuffer.get()), kBufferSize - inflateState.avail_out);
kBufferSize - inflateState.avail_out
);
} while (inflateState.avail_out == 0); } while (inflateState.avail_out == 0);
inflateEnd(&inflateState); inflateEnd(&inflateState);
return true; return true;
} }
void HttpClient::log(const std::string& msg, void HttpClient::log(const std::string& msg, HttpRequestArgsPtr args)
HttpRequestArgsPtr args)
{ {
if (args->logger) if (args->logger)
{ {
args->logger(msg); args->logger(msg);
} }
} }
} } // namespace ix

View File

@ -5,13 +5,13 @@
*/ */
#include "IXHttpServer.h" #include "IXHttpServer.h"
#include "IXNetSystem.h"
#include "IXSocketConnect.h" #include "IXSocketConnect.h"
#include "IXSocketFactory.h" #include "IXSocketFactory.h"
#include "IXNetSystem.h" #include <fstream>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include <fstream>
#include <vector> #include <vector>
namespace namespace
@ -28,7 +28,7 @@ namespace
file.seekg(0, file.beg); file.seekg(0, file.beg);
memblock.resize((size_t) size); memblock.resize((size_t) size);
file.read((char*)&memblock.front(), static_cast<std::streamsize>(size)); file.read((char*) &memblock.front(), static_cast<std::streamsize>(size));
return std::make_pair(true, memblock); return std::make_pair(true, memblock);
} }
@ -39,15 +39,13 @@ namespace
auto vec = res.second; auto vec = res.second;
return std::make_pair(res.first, std::string(vec.begin(), vec.end())); return std::make_pair(res.first, std::string(vec.begin(), vec.end()));
} }
} } // namespace
namespace ix namespace ix
{ {
HttpServer::HttpServer(int port, HttpServer::HttpServer(int port, const std::string& host, int backlog, size_t maxConnections)
const std::string& host, : SocketServer(port, host, backlog, maxConnections)
int backlog, , _connectedClientsCount(0)
size_t maxConnections) : SocketServer(port, host, backlog, maxConnections),
_connectedClientsCount(0)
{ {
setDefaultConnectionCallback(); setDefaultConnectionCallback();
} }
@ -71,9 +69,7 @@ namespace ix
_onConnectionCallback = callback; _onConnectionCallback = callback;
} }
void HttpServer::handleConnection( void HttpServer::handleConnection(int fd, std::shared_ptr<ConnectionState> connectionState)
int fd,
std::shared_ptr<ConnectionState> connectionState)
{ {
_connectedClientsCount++; _connectedClientsCount++;
@ -109,8 +105,7 @@ namespace ix
{ {
setOnConnectionCallback( setOnConnectionCallback(
[this](HttpRequestPtr request, [this](HttpRequestPtr request,
std::shared_ptr<ConnectionState> /*connectionState*/) -> HttpResponsePtr std::shared_ptr<ConnectionState> /*connectionState*/) -> HttpResponsePtr {
{
std::string uri(request->uri); std::string uri(request->uri);
if (uri.empty() || uri == "/") if (uri.empty() || uri == "/")
{ {
@ -122,23 +117,16 @@ namespace ix
bool found = res.first; bool found = res.first;
if (!found) if (!found)
{ {
return std::make_shared<HttpResponse>(404, "Not Found", return std::make_shared<HttpResponse>(
HttpErrorCode::Ok, 404, "Not Found", HttpErrorCode::Ok, WebSocketHttpHeaders(), std::string());
WebSocketHttpHeaders(),
std::string());
} }
std::string content = res.second; std::string content = res.second;
// Log request // Log request
std::stringstream ss; std::stringstream ss;
ss << request->method ss << request->method << " " << request->headers["User-Agent"] << " "
<< " " << request->uri << " " << content.size();
<< request->headers["User-Agent"]
<< " "
<< request->uri
<< " "
<< content.size();
logInfo(ss.str()); logInfo(ss.str());
WebSocketHttpHeaders headers; WebSocketHttpHeaders headers;
@ -151,11 +139,8 @@ namespace ix
headers[it.first] = it.second; headers[it.first] = it.second;
} }
return std::make_shared<HttpResponse>(200, "OK", return std::make_shared<HttpResponse>(
HttpErrorCode::Ok, 200, "OK", HttpErrorCode::Ok, headers, content);
headers, });
content);
}
);
} }
} } // namespace ix

View File

@ -42,7 +42,7 @@ namespace ix
// //
// So we make it a select wrapper // So we make it a select wrapper
// //
int poll(struct pollfd *fds, nfds_t nfds, int timeout) int poll(struct pollfd* fds, nfds_t nfds, int timeout)
{ {
#ifdef _WIN32 #ifdef _WIN32
int maxfd = 0; int maxfd = 0;
@ -53,7 +53,7 @@ namespace ix
for (nfds_t i = 0; i < nfds; ++i) for (nfds_t i = 0; i < nfds; ++i)
{ {
struct pollfd *fd = &fds[i]; struct pollfd* fd = &fds[i];
if (fd->fd > maxfd) if (fd->fd > maxfd)
{ {
@ -77,8 +77,7 @@ namespace ix
tv.tv_sec = timeout / 1000; tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000; tv.tv_usec = (timeout % 1000) * 1000;
int ret = select(maxfd + 1, &readfds, &writefds, &errorfds, int ret = select(maxfd + 1, &readfds, &writefds, &errorfds, timeout != -1 ? &tv : NULL);
timeout != -1 ? &tv : NULL);
if (ret < 0) if (ret < 0)
{ {
@ -87,7 +86,7 @@ namespace ix
for (nfds_t i = 0; i < nfds; ++i) for (nfds_t i = 0; i < nfds; ++i)
{ {
struct pollfd *fd = &fds[i]; struct pollfd* fd = &fds[i];
fd->revents = 0; fd->revents = 0;
if (FD_ISSET(fd->fd, &readfds)) if (FD_ISSET(fd->fd, &readfds))

View File

@ -42,5 +42,4 @@ namespace ix
{ {
return -1; return -1;
} }
} } // namespace ix

View File

@ -26,14 +26,13 @@
#include "IXSelectInterruptEventFd.h" #include "IXSelectInterruptEventFd.h"
#include <sys/eventfd.h>
#include <unistd.h> // for write
#include <string.h> // for strerror
#include <fcntl.h>
#include <errno.h>
#include <assert.h> #include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <sstream> #include <sstream>
#include <string.h> // for strerror
#include <sys/eventfd.h>
#include <unistd.h> // for write
namespace ix namespace ix
{ {
@ -113,4 +112,4 @@ namespace ix
{ {
return _eventfd; return _eventfd;
} }
} } // namespace ix

View File

@ -7,9 +7,9 @@
#include "IXSelectInterruptFactory.h" #include "IXSelectInterruptFactory.h"
#if defined(__linux__) || defined(__APPLE__) #if defined(__linux__) || defined(__APPLE__)
# include <ixwebsocket/IXSelectInterruptPipe.h> #include <ixwebsocket/IXSelectInterruptPipe.h>
#else #else
# include <ixwebsocket/IXSelectInterrupt.h> #include <ixwebsocket/IXSelectInterrupt.h>
#endif #endif
namespace ix namespace ix
@ -22,4 +22,4 @@ namespace ix
return std::make_shared<SelectInterrupt>(); return std::make_shared<SelectInterrupt>();
#endif #endif
} }
} } // namespace ix

View File

@ -10,12 +10,12 @@
#include "IXSelectInterruptPipe.h" #include "IXSelectInterruptPipe.h"
#include <unistd.h> // for write
#include <string.h> // for strerror
#include <fcntl.h>
#include <errno.h>
#include <assert.h> #include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <sstream> #include <sstream>
#include <string.h> // for strerror
#include <unistd.h> // for write
namespace ix namespace ix
{ {
@ -143,4 +143,4 @@ namespace ix
return _fildes[kPipeReadIndex]; return _fildes[kPipeReadIndex];
} }
} } // namespace ix

View File

@ -5,21 +5,20 @@
*/ */
#include "IXSocket.h" #include "IXSocket.h"
#include "IXSocketConnect.h"
#include "IXNetSystem.h" #include "IXNetSystem.h"
#include "IXSelectInterrupt.h" #include "IXSelectInterrupt.h"
#include "IXSelectInterruptFactory.h" #include "IXSelectInterruptFactory.h"
#include "IXSocketConnect.h"
#include <algorithm>
#include <assert.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <assert.h>
#include <stdint.h>
#include <fcntl.h>
#include <sys/types.h> #include <sys/types.h>
#include <algorithm>
#ifdef min #ifdef min
#undef min #undef min
#endif #endif
@ -32,9 +31,9 @@ namespace ix
const uint64_t Socket::kCloseRequest = 2; const uint64_t Socket::kCloseRequest = 2;
constexpr size_t Socket::kChunkSize; constexpr size_t Socket::kChunkSize;
Socket::Socket(int fd) : Socket::Socket(int fd)
_sockfd(fd), : _sockfd(fd)
_selectInterrupt(createSelectInterrupt()) , _selectInterrupt(createSelectInterrupt())
{ {
; ;
} }
@ -123,8 +122,7 @@ namespace ix
// getsockopt() puts the errno value for connect into optval so 0 // getsockopt() puts the errno value for connect into optval so 0
// means no-error. // means no-error.
if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, &optlen) == -1 || if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, &optlen) == -1 || optval != 0)
optval != 0)
{ {
pollResult = PollResultType::Error; pollResult = PollResultType::Error;
@ -201,7 +199,7 @@ namespace ix
ssize_t Socket::send(const std::string& buffer) ssize_t Socket::send(const std::string& buffer)
{ {
return send((char*)&buffer[0], buffer.size()); return send((char*) &buffer[0], buffer.size());
} }
ssize_t Socket::recv(void* buffer, size_t length) ssize_t Socket::recv(void* buffer, size_t length)
@ -263,7 +261,7 @@ namespace ix
{ {
if (isCancellationRequested && isCancellationRequested()) return false; if (isCancellationRequested && isCancellationRequested()) return false;
ssize_t ret = send((char*)&str[offset], len); ssize_t ret = send((char*) &str[offset], len);
// We wrote some bytes, as needed, all good. // We wrote some bytes, as needed, all good.
if (ret > 0) if (ret > 0)
@ -292,8 +290,7 @@ namespace ix
} }
} }
bool Socket::readByte(void* buffer, bool Socket::readByte(void* buffer, const CancellationRequest& isCancellationRequested)
const CancellationRequest& isCancellationRequested)
{ {
while (true) while (true)
{ {
@ -332,7 +329,7 @@ namespace ix
std::string line; std::string line;
line.reserve(64); line.reserve(64);
for (int i = 0; i < 2 || (line[i-2] != '\r' && line[i-1] != '\n'); ++i) for (int i = 0; i < 2 || (line[i - 2] != '\r' && line[i - 1] != '\n'); ++i)
{ {
if (!readByte(&c, isCancellationRequested)) if (!readByte(&c, isCancellationRequested))
{ {
@ -365,13 +362,11 @@ namespace ix
} }
size_t size = std::min(kChunkSize, length - output.size()); size_t size = std::min(kChunkSize, length - output.size());
ssize_t ret = recv((char*)&_readBuffer[0], size); ssize_t ret = recv((char*) &_readBuffer[0], size);
if (ret > 0) if (ret > 0)
{ {
output.insert(output.end(), output.insert(output.end(), _readBuffer.begin(), _readBuffer.begin() + ret);
_readBuffer.begin(),
_readBuffer.begin() + ret);
} }
else if (ret <= 0 && !Socket::isWaitNeeded()) else if (ret <= 0 && !Socket::isWaitNeeded())
{ {
@ -388,7 +383,6 @@ namespace ix
} }
} }
return std::make_pair(true, std::string(output.begin(), return std::make_pair(true, std::string(output.begin(), output.end()));
output.end()));
} }
} } // namespace ix

View File

@ -6,11 +6,13 @@
* Adapted from Satori SDK Apple SSL code. * Adapted from Satori SDK Apple SSL code.
*/ */
#include "IXSocketAppleSSL.h" #include "IXSocketAppleSSL.h"
#include "IXSocketConnect.h"
#include "IXSocketConnect.h"
#include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <netdb.h> #include <netdb.h>
#include <netinet/tcp.h> #include <netinet/tcp.h>
#include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -18,132 +20,125 @@
#include <sys/time.h> #include <sys/time.h>
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> #include <unistd.h>
#include <stdint.h>
#include <errno.h>
#define socketerrno errno #define socketerrno errno
#include <Security/SecureTransport.h> #include <Security/SecureTransport.h>
namespace { namespace
OSStatus read_from_socket(SSLConnectionRef connection, void *data, size_t *len)
{ {
int fd = (int) (long) connection; OSStatus read_from_socket(SSLConnectionRef connection, void* data, size_t* len)
if (fd < 0)
return errSSLInternal;
assert(data != nullptr);
assert(len != nullptr);
size_t requested_sz = *len;
ssize_t status = read(fd, data, requested_sz);
if (status > 0)
{ {
*len = (size_t) status; int fd = (int) (long) connection;
if (requested_sz > *len) if (fd < 0) return errSSLInternal;
return errSSLWouldBlock;
else
return noErr;
}
else if (0 == status)
{
*len = 0;
return errSSLClosedGraceful;
}
else
{
*len = 0;
switch (errno) {
case ENOENT:
return errSSLClosedGraceful;
case EAGAIN: assert(data != nullptr);
assert(len != nullptr);
size_t requested_sz = *len;
ssize_t status = read(fd, data, requested_sz);
if (status > 0)
{
*len = (size_t) status;
if (requested_sz > *len)
return errSSLWouldBlock; return errSSLWouldBlock;
else
case ECONNRESET: return noErr;
return errSSLClosedAbort;
default:
return errSecIO;
} }
} else if (0 == status)
}
OSStatus write_to_socket(SSLConnectionRef connection, const void *data, size_t *len)
{
int fd = (int) (long) connection;
if (fd < 0)
return errSSLInternal;
assert(data != nullptr);
assert(len != nullptr);
size_t to_write_sz = *len;
ssize_t status = write(fd, data, to_write_sz);
if (status > 0)
{
*len = (size_t) status;
if (to_write_sz > *len)
return errSSLWouldBlock;
else
return noErr;
}
else if (0 == status)
{
*len = 0;
return errSSLClosedGraceful;
}
else
{
*len = 0;
if (EAGAIN == errno)
{ {
return errSSLWouldBlock; *len = 0;
return errSSLClosedGraceful;
} }
else else
{ {
return errSecIO; *len = 0;
} switch (errno)
}
}
std::string getSSLErrorDescription(OSStatus status)
{
std::string errMsg("Unknown SSL error.");
CFErrorRef error = CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainOSStatus, status, NULL);
if (error)
{
CFStringRef message = CFErrorCopyDescription(error);
if (message)
{
char localBuffer[128];
Boolean success;
success = CFStringGetCString(message, localBuffer, 128,
CFStringGetSystemEncoding());
if (success)
{ {
errMsg = localBuffer; case ENOENT: return errSSLClosedGraceful;
case EAGAIN: return errSSLWouldBlock;
case ECONNRESET: return errSSLClosedAbort;
default: return errSecIO;
} }
CFRelease(message);
} }
CFRelease(error);
} }
return errMsg; OSStatus write_to_socket(SSLConnectionRef connection, const void* data, size_t* len)
} {
int fd = (int) (long) connection;
if (fd < 0) return errSSLInternal;
assert(data != nullptr);
assert(len != nullptr);
size_t to_write_sz = *len;
ssize_t status = write(fd, data, to_write_sz);
if (status > 0)
{
*len = (size_t) status;
if (to_write_sz > *len)
return errSSLWouldBlock;
else
return noErr;
}
else if (0 == status)
{
*len = 0;
return errSSLClosedGraceful;
}
else
{
*len = 0;
if (EAGAIN == errno)
{
return errSSLWouldBlock;
}
else
{
return errSecIO;
}
}
}
std::string getSSLErrorDescription(OSStatus status)
{
std::string errMsg("Unknown SSL error.");
CFErrorRef error = CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainOSStatus, status, NULL);
if (error)
{
CFStringRef message = CFErrorCopyDescription(error);
if (message)
{
char localBuffer[128];
Boolean success;
success =
CFStringGetCString(message, localBuffer, 128, CFStringGetSystemEncoding());
if (success)
{
errMsg = localBuffer;
}
CFRelease(message);
}
CFRelease(error);
}
return errMsg;
}
} // anonymous namespace } // anonymous namespace
namespace ix namespace ix
{ {
SocketAppleSSL::SocketAppleSSL(const SocketTLSOptions& tlsOptions, int fd) : Socket(fd), SocketAppleSSL::SocketAppleSSL(const SocketTLSOptions& tlsOptions, int fd)
_sslContext(nullptr), : Socket(fd)
_tlsOptions(tlsOptions) , _sslContext(nullptr)
, _tlsOptions(tlsOptions)
{ {
; ;
} }
@ -169,14 +164,14 @@ namespace ix
_sslContext = SSLCreateContext(kCFAllocatorDefault, kSSLClientSide, kSSLStreamType); _sslContext = SSLCreateContext(kCFAllocatorDefault, kSSLClientSide, kSSLStreamType);
SSLSetIOFuncs(_sslContext, read_from_socket, write_to_socket); SSLSetIOFuncs(_sslContext, read_from_socket, write_to_socket);
SSLSetConnection(_sslContext, (SSLConnectionRef) (long) _sockfd); SSLSetConnection(_sslContext, (SSLConnectionRef)(long) _sockfd);
SSLSetProtocolVersionMin(_sslContext, kTLSProtocol12); SSLSetProtocolVersionMin(_sslContext, kTLSProtocol12);
SSLSetPeerDomainName(_sslContext, host.c_str(), host.size()); SSLSetPeerDomainName(_sslContext, host.c_str(), host.size());
do { do
{
status = SSLHandshake(_sslContext); status = SSLHandshake(_sslContext);
} while (errSSLWouldBlock == status || } while (errSSLWouldBlock == status || errSSLServerAuthCompleted == status);
errSSLServerAuthCompleted == status);
} }
if (noErr != status) if (noErr != status)
@ -206,7 +201,8 @@ namespace ix
{ {
ssize_t ret = 0; ssize_t ret = 0;
OSStatus status; OSStatus status;
do { do
{
size_t processed = 0; size_t processed = 0;
std::lock_guard<std::mutex> lock(_mutex); std::lock_guard<std::mutex> lock(_mutex);
status = SSLWrite(_sslContext, buf, nbyte, &processed); status = SSLWrite(_sslContext, buf, nbyte, &processed);
@ -215,14 +211,13 @@ namespace ix
nbyte -= processed; nbyte -= processed;
} while (nbyte > 0 && errSSLWouldBlock == status); } while (nbyte > 0 && errSSLWouldBlock == status);
if (ret == 0 && errSSLClosedAbort != status) if (ret == 0 && errSSLClosedAbort != status) ret = -1;
ret = -1;
return ret; return ret;
} }
ssize_t SocketAppleSSL::send(const std::string& buffer) ssize_t SocketAppleSSL::send(const std::string& buffer)
{ {
return send((char*)&buffer[0], buffer.size()); return send((char*) &buffer[0], buffer.size());
} }
// No wait support // No wait support
@ -235,13 +230,11 @@ namespace ix
std::lock_guard<std::mutex> lock(_mutex); std::lock_guard<std::mutex> lock(_mutex);
status = SSLRead(_sslContext, buf, nbyte, &processed); status = SSLRead(_sslContext, buf, nbyte, &processed);
if (processed > 0) if (processed > 0) return (ssize_t) processed;
return (ssize_t) processed;
// The connection was reset, inform the caller that this // The connection was reset, inform the caller that this
// Socket should close // Socket should close
if (status == errSSLClosedGraceful || if (status == errSSLClosedGraceful || status == errSSLClosedNoNotify ||
status == errSSLClosedNoNotify ||
status == errSSLClosedAbort) status == errSSLClosedAbort)
{ {
errno = ECONNRESET; errno = ECONNRESET;
@ -257,4 +250,4 @@ namespace ix
return -1; return -1;
} }
} } // namespace ix

View File

@ -5,36 +5,34 @@
*/ */
#include "IXSocketConnect.h" #include "IXSocketConnect.h"
#include "IXDNSLookup.h" #include "IXDNSLookup.h"
#include "IXNetSystem.h" #include "IXNetSystem.h"
#include "IXSocket.h" #include "IXSocket.h"
#include <string.h>
#include <fcntl.h> #include <fcntl.h>
#include <string.h>
#include <sys/types.h> #include <sys/types.h>
// Android needs extra headers for TCP_NODELAY and IPPROTO_TCP // Android needs extra headers for TCP_NODELAY and IPPROTO_TCP
#ifdef ANDROID #ifdef ANDROID
# include <linux/in.h> #include <linux/in.h>
# include <linux/tcp.h> #include <linux/tcp.h>
#endif #endif
namespace ix namespace ix
{ {
// //
// This function can be cancelled every 50 ms // This function can be cancelled every 50 ms
// This is important so that we don't block the main UI thread when shutting down a connection which is // This is important so that we don't block the main UI thread when shutting down a connection
// already trying to reconnect, and can be blocked waiting for ::connect to respond. // which is already trying to reconnect, and can be blocked waiting for ::connect to respond.
// //
int SocketConnect::connectToAddress(const struct addrinfo *address, int SocketConnect::connectToAddress(const struct addrinfo* address,
std::string& errMsg, std::string& errMsg,
const CancellationRequest& isCancellationRequested) const CancellationRequest& isCancellationRequested)
{ {
errMsg = "no error"; errMsg = "no error";
int fd = socket(address->ai_family, int fd = socket(address->ai_family, address->ai_socktype, address->ai_protocol);
address->ai_socktype,
address->ai_protocol);
if (fd < 0) if (fd < 0)
{ {
errMsg = "Cannot create a socket"; errMsg = "Cannot create a socket";
@ -74,8 +72,7 @@ namespace ix
else if (pollResult == PollResultType::Error) else if (pollResult == PollResultType::Error)
{ {
Socket::closeSocket(fd); Socket::closeSocket(fd);
errMsg = std::string("Connect error: ") + errMsg = std::string("Connect error: ") + strerror(Socket::getErrno());
strerror(Socket::getErrno());
return -1; return -1;
} }
else if (pollResult == PollResultType::ReadyForWrite) else if (pollResult == PollResultType::ReadyForWrite)
@ -85,8 +82,7 @@ namespace ix
else else
{ {
Socket::closeSocket(fd); Socket::closeSocket(fd);
errMsg = std::string("Connect error: ") + errMsg = std::string("Connect error: ") + strerror(Socket::getErrno());
strerror(Socket::getErrno());
return -1; return -1;
} }
} }
@ -105,7 +101,7 @@ namespace ix
// First do DNS resolution // First do DNS resolution
// //
auto dnsLookup = std::make_shared<DNSLookup>(hostname, port); auto dnsLookup = std::make_shared<DNSLookup>(hostname, port);
struct addrinfo *res = dnsLookup->resolve(errMsg, isCancellationRequested); struct addrinfo* res = dnsLookup->resolve(errMsg, isCancellationRequested);
if (res == nullptr) if (res == nullptr)
{ {
return -1; return -1;
@ -114,7 +110,7 @@ namespace ix
int sockfd = -1; int sockfd = -1;
// iterate through the records to find a working peer // iterate through the records to find a working peer
struct addrinfo *address; struct addrinfo* address;
for (address = res; address != nullptr; address = address->ai_next) for (address = res; address != nullptr; address = address->ai_next)
{ {
// //
@ -149,8 +145,7 @@ namespace ix
// 3. (apple) prevent SIGPIPE from being emitted when the remote end disconnect // 3. (apple) prevent SIGPIPE from being emitted when the remote end disconnect
#ifdef SO_NOSIGPIPE #ifdef SO_NOSIGPIPE
int value = 1; int value = 1;
setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void*) &value, sizeof(value));
(void *)&value, sizeof(value));
#endif #endif
} }
} } // namespace ix

View File

@ -7,15 +7,16 @@
#pragma once #pragma once
#include "IXSocketTLSOptions.h"
#include <memory> #include <memory>
#include <string> #include <string>
#include "IXSocketTLSOptions.h"
namespace ix namespace ix
{ {
class Socket; class Socket;
std::shared_ptr<Socket> createSocket(bool tls, std::string& errorMsg, const SocketTLSOptions& tlsOptions); std::shared_ptr<Socket> createSocket(bool tls,
std::string& errorMsg,
const SocketTLSOptions& tlsOptions);
std::shared_ptr<Socket> createSocket(int fd, std::string& errorMsg); std::shared_ptr<Socket> createSocket(int fd, std::string& errorMsg);
} // namespace ix } // namespace ix

View File

@ -9,16 +9,16 @@
*/ */
#include "IXSocketMbedTLS.h" #include "IXSocketMbedTLS.h"
#include "IXSocketConnect.h"
#include "IXNetSystem.h" #include "IXNetSystem.h"
#include "IXSocket.h" #include "IXSocket.h"
#include "IXSocketConnect.h"
#include <string.h> #include <string.h>
namespace ix namespace ix
{ {
SocketMbedTLS::SocketMbedTLS(const SocketTLSOptions& tlsOptions) : SocketMbedTLS::SocketMbedTLS(const SocketTLSOptions& tlsOptions)
_tlsOptions(tlsOptions) : _tlsOptions(tlsOptions)
{ {
; ;
} }
@ -36,13 +36,13 @@ namespace ix
mbedtls_ssl_config_init(&_conf); mbedtls_ssl_config_init(&_conf);
mbedtls_ctr_drbg_init(&_ctr_drbg); mbedtls_ctr_drbg_init(&_ctr_drbg);
const char *pers = "IXSocketMbedTLS"; const char* pers = "IXSocketMbedTLS";
mbedtls_entropy_init(&_entropy); mbedtls_entropy_init(&_entropy);
if (mbedtls_ctr_drbg_seed(&_ctr_drbg, if (mbedtls_ctr_drbg_seed(&_ctr_drbg,
mbedtls_entropy_func, mbedtls_entropy_func,
&_entropy, &_entropy,
(const unsigned char *) pers, (const unsigned char*) pers,
strlen(pers)) != 0) strlen(pers)) != 0)
{ {
errMsg = "Setting entropy seed failed"; errMsg = "Setting entropy seed failed";
@ -52,7 +52,7 @@ namespace ix
if (mbedtls_ssl_config_defaults(&_conf, if (mbedtls_ssl_config_defaults(&_conf,
MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_IS_CLIENT,
MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_TRANSPORT_STREAM,
MBEDTLS_SSL_PRESET_DEFAULT ) != 0) MBEDTLS_SSL_PRESET_DEFAULT) != 0)
{ {
errMsg = "Setting config default failed"; errMsg = "Setting config default failed";
return false; return false;
@ -102,8 +102,7 @@ namespace ix
{ {
std::lock_guard<std::mutex> lock(_mutex); std::lock_guard<std::mutex> lock(_mutex);
res = mbedtls_ssl_handshake(&_ssl); res = mbedtls_ssl_handshake(&_ssl);
} } while (res == MBEDTLS_ERR_SSL_WANT_READ || res == MBEDTLS_ERR_SSL_WANT_WRITE);
while (res == MBEDTLS_ERR_SSL_WANT_READ || res == MBEDTLS_ERR_SSL_WANT_WRITE);
if (res != 0) if (res != 0)
{ {
@ -142,13 +141,18 @@ namespace ix
ssize_t res = mbedtls_ssl_write(&_ssl, (unsigned char*) buf, nbyte); ssize_t res = mbedtls_ssl_write(&_ssl, (unsigned char*) buf, nbyte);
if (res > 0) { if (res > 0)
{
nbyte -= res; nbyte -= res;
sent += res; sent += res;
} else if (res == MBEDTLS_ERR_SSL_WANT_READ || res == MBEDTLS_ERR_SSL_WANT_WRITE) { }
else if (res == MBEDTLS_ERR_SSL_WANT_READ || res == MBEDTLS_ERR_SSL_WANT_WRITE)
{
errno = EWOULDBLOCK; errno = EWOULDBLOCK;
return -1; return -1;
} else { }
else
{
return -1; return -1;
} }
} }
@ -157,7 +161,7 @@ namespace ix
ssize_t SocketMbedTLS::send(const std::string& buffer) ssize_t SocketMbedTLS::send(const std::string& buffer)
{ {
return send((char*)&buffer[0], buffer.size()); return send((char*) &buffer[0], buffer.size());
} }
ssize_t SocketMbedTLS::recv(void* buf, size_t nbyte) ssize_t SocketMbedTLS::recv(void* buf, size_t nbyte)
@ -181,4 +185,4 @@ namespace ix
} }
} }
} } // namespace ix

View File

@ -121,7 +121,9 @@ namespace ix
}); });
SSL_CTX_set_verify_depth(ctx, 4); SSL_CTX_set_verify_depth(ctx, 4);
} else { }
else
{
SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, nullptr); SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, nullptr);
} }

View File

@ -13,12 +13,12 @@
#include "IXSocketSChannel.h" #include "IXSocketSChannel.h"
#ifdef _WIN32 #ifdef _WIN32
# include <basetsd.h> #include <WS2tcpip.h>
# include <WinSock2.h> #include <WinSock2.h>
# include <ws2def.h> #include <basetsd.h>
# include <WS2tcpip.h> #include <io.h>
# include <schannel.h> #include <schannel.h>
# include <io.h> #include <ws2def.h>
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
@ -26,14 +26,15 @@
#define UNICODE #define UNICODE
#endif #endif
#include <windows.h>
#include <winsock2.h>
#include <mstcpip.h> #include <mstcpip.h>
#include <ws2tcpip.h>
#include <rpc.h>
#include <ntdsapi.h> #include <ntdsapi.h>
#include <rpc.h>
#include <stdio.h> #include <stdio.h>
#include <tchar.h> #include <tchar.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#define RECV_DATA_BUF_SIZE 256 #define RECV_DATA_BUF_SIZE 256
@ -50,12 +51,8 @@
// has already been initialized // has already been initialized
#else #else
# error("This file should only be built on Windows") #error("This file should only be built on Windows")
#endif #endif
namespace ix namespace ix
@ -67,12 +64,9 @@ namespace ix
SocketSChannel::~SocketSChannel() SocketSChannel::~SocketSChannel()
{ {
} }
bool SocketSChannel::connect(const std::string& host, bool SocketSChannel::connect(const std::string& host, int port, std::string& errMsg)
int port,
std::string& errMsg)
{ {
return Socket::connect(host, port, errMsg, nullptr); return Socket::connect(host, port, errMsg, nullptr);
} }
@ -103,4 +97,4 @@ namespace ix
return Socket::recv(buf, nbyte); return Socket::recv(buf, nbyte);
} }
} } // namespace ix

View File

@ -5,14 +5,14 @@
*/ */
#include "IXSocketServer.h" #include "IXSocketServer.h"
#include "IXNetSystem.h"
#include "IXSocket.h" #include "IXSocket.h"
#include "IXSocketConnect.h" #include "IXSocketConnect.h"
#include "IXNetSystem.h" #include <assert.h>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include <string.h> #include <string.h>
#include <assert.h>
namespace ix namespace ix
{ {
@ -24,17 +24,16 @@ namespace ix
SocketServer::SocketServer(int port, SocketServer::SocketServer(int port,
const std::string& host, const std::string& host,
int backlog, int backlog,
size_t maxConnections) : size_t maxConnections)
_port(port), : _port(port)
_host(host), , _host(host)
_backlog(backlog), , _backlog(backlog)
_maxConnections(maxConnections), , _maxConnections(maxConnections)
_serverFd(-1), , _serverFd(-1)
_stop(false), , _stop(false)
_stopGc(false), , _stopGc(false)
_connectionStateFactory(&ConnectionState::createConnectionState) , _connectionStateFactory(&ConnectionState::createConnectionState)
{ {
} }
SocketServer::~SocketServer() SocketServer::~SocketServer()
@ -62,21 +61,18 @@ namespace ix
if ((_serverFd = socket(AF_INET, SOCK_STREAM, 0)) < 0) if ((_serverFd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{ {
std::stringstream ss; std::stringstream ss;
ss << "SocketServer::listen() error creating socket): " ss << "SocketServer::listen() error creating socket): " << strerror(Socket::getErrno());
<< strerror(Socket::getErrno());
return std::make_pair(false, ss.str()); return std::make_pair(false, ss.str());
} }
// Make that socket reusable. (allow restarting this server at will) // Make that socket reusable. (allow restarting this server at will)
int enable = 1; int enable = 1;
if (setsockopt(_serverFd, SOL_SOCKET, SO_REUSEADDR, if (setsockopt(_serverFd, SOL_SOCKET, SO_REUSEADDR, (char*) &enable, sizeof(enable)) < 0)
(char*) &enable, sizeof(enable)) < 0)
{ {
std::stringstream ss; std::stringstream ss;
ss << "SocketServer::listen() error calling setsockopt(SO_REUSEADDR) " ss << "SocketServer::listen() error calling setsockopt(SO_REUSEADDR) "
<< "at address " << _host << ":" << _port << "at address " << _host << ":" << _port << " : " << strerror(Socket::getErrno());
<< " : " << strerror(Socket::getErrno());
Socket::closeSocket(_serverFd); Socket::closeSocket(_serverFd);
return std::make_pair(false, ss.str()); return std::make_pair(false, ss.str());
@ -84,7 +80,7 @@ namespace ix
// Bind the socket to the server address. // Bind the socket to the server address.
server.sin_family = AF_INET; server.sin_family = AF_INET;
server.sin_port = htons(_port); server.sin_port = htons(_port);
// Using INADDR_ANY trigger a pop-up box as binding to any address is detected // 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 // by the osx firewall. We need to codesign the binary with a self-signed cert
@ -95,12 +91,11 @@ namespace ix
// //
server.sin_addr.s_addr = inet_addr(_host.c_str()); server.sin_addr.s_addr = inet_addr(_host.c_str());
if (bind(_serverFd, (struct sockaddr *)&server, sizeof(server)) < 0) if (bind(_serverFd, (struct sockaddr*) &server, sizeof(server)) < 0)
{ {
std::stringstream ss; std::stringstream ss;
ss << "SocketServer::listen() error calling bind " ss << "SocketServer::listen() error calling bind "
<< "at address " << _host << ":" << _port << "at address " << _host << ":" << _port << " : " << strerror(Socket::getErrno());
<< " : " << strerror(Socket::getErrno());
Socket::closeSocket(_serverFd); Socket::closeSocket(_serverFd);
return std::make_pair(false, ss.str()); return std::make_pair(false, ss.str());
@ -113,8 +108,7 @@ namespace ix
{ {
std::stringstream ss; std::stringstream ss;
ss << "SocketServer::listen() error calling listen " ss << "SocketServer::listen() error calling listen "
<< "at address " << _host << ":" << _port << "at address " << _host << ":" << _port << " : " << strerror(Socket::getErrno());
<< " : " << strerror(Socket::getErrno());
Socket::closeSocket(_serverFd); Socket::closeSocket(_serverFd);
return std::make_pair(false, ss.str()); return std::make_pair(false, ss.str());
@ -186,7 +180,7 @@ namespace ix
{ {
std::lock_guard<std::mutex> lock(_connectionsThreadsMutex); std::lock_guard<std::mutex> lock(_connectionsThreadsMutex);
auto it = _connectionsThreads.begin(); auto it = _connectionsThreads.begin();
auto itEnd = _connectionsThreads.end(); auto itEnd = _connectionsThreads.end();
while (it != itEnd) while (it != itEnd)
{ {
@ -221,8 +215,7 @@ namespace ix
if (pollResult == PollResultType::Error) if (pollResult == PollResultType::Error)
{ {
std::stringstream ss; std::stringstream ss;
ss << "SocketServer::run() error in select: " ss << "SocketServer::run() error in select: " << strerror(Socket::getErrno());
<< strerror(Socket::getErrno());
logError(ss.str()); logError(ss.str());
continue; continue;
} }
@ -238,15 +231,15 @@ namespace ix
socklen_t addressLen = sizeof(client); socklen_t addressLen = sizeof(client);
memset(&client, 0, sizeof(client)); memset(&client, 0, sizeof(client));
if ((clientFd = accept(_serverFd, (struct sockaddr *)&client, &addressLen)) < 0) if ((clientFd = accept(_serverFd, (struct sockaddr*) &client, &addressLen)) < 0)
{ {
if (!Socket::isWaitNeeded()) if (!Socket::isWaitNeeded())
{ {
// FIXME: that error should be propagated // FIXME: that error should be propagated
int err = Socket::getErrno(); int err = Socket::getErrno();
std::stringstream ss; std::stringstream ss;
ss << "SocketServer::run() error accepting connection: " ss << "SocketServer::run() error accepting connection: " << err << ", "
<< err << ", " << strerror(err); << strerror(err);
logError(ss.str()); logError(ss.str());
} }
continue; continue;
@ -255,8 +248,7 @@ namespace ix
if (getConnectedClientsCount() >= _maxConnections) if (getConnectedClientsCount() >= _maxConnections)
{ {
std::stringstream ss; std::stringstream ss;
ss << "SocketServer::run() reached max connections = " ss << "SocketServer::run() reached max connections = " << _maxConnections << ". "
<< _maxConnections << ". "
<< "Not accepting connection"; << "Not accepting connection";
logError(ss.str()); logError(ss.str());
@ -276,11 +268,8 @@ namespace ix
// Launch the handleConnection work asynchronously in its own thread. // Launch the handleConnection work asynchronously in its own thread.
std::lock_guard<std::mutex> lock(_connectionsThreadsMutex); std::lock_guard<std::mutex> lock(_connectionsThreadsMutex);
_connectionsThreads.push_back(std::make_pair( _connectionsThreads.push_back(std::make_pair(
connectionState, connectionState,
std::thread(&SocketServer::handleConnection, std::thread(&SocketServer::handleConnection, this, clientFd, connectionState)));
this,
clientFd,
connectionState)));
} }
} }
@ -308,5 +297,4 @@ namespace ix
std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::this_thread::sleep_for(std::chrono::milliseconds(10));
} }
} }
} } // namespace ix

View File

@ -4,16 +4,17 @@
* Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved. * Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved.
*/ */
#include <assert.h>
#include "IXSocketTLSOptions.h" #include "IXSocketTLSOptions.h"
#include <assert.h>
namespace ix namespace ix
{ {
SocketTLSOptions::SocketTLSOptions()
SocketTLSOptions::SocketTLSOptions() { {
#ifndef IXWEBSOCKET_USE_TLS #ifndef IXWEBSOCKET_USE_TLS
assert(false && "To use TLS features the library must be compiled with USE_TLS"); assert(false && "To use TLS features the library must be compiled with USE_TLS");
#endif #endif
} }
bool SocketTLSOptions::isUsingClientCert() const bool SocketTLSOptions::isUsingClientCert() const

View File

@ -18,7 +18,7 @@ namespace ix
std::string certFile; std::string certFile;
// the key used for signing/encryption // the key used for signing/encryption
std::string keyFile; std::string keyFile;
// the ca certificate (or certificate bundle) file containing // the ca certificate (or certificate bundle) file containing
// certificates to be trusted by peers; use 'SYSTEM' to // certificates to be trusted by peers; use 'SYSTEM' to
// leverage the system defaults, use 'NONE' to disable peer verification // leverage the system defaults, use 'NONE' to disable peer verification
std::string caFile = "SYSTEM"; std::string caFile = "SYSTEM";

View File

@ -5,6 +5,7 @@
*/ */
#include "IXUrlParser.h" #include "IXUrlParser.h"
#include "LUrlParser.h" #include "LUrlParser.h"
namespace ix namespace ix
@ -24,9 +25,9 @@ namespace ix
} }
protocol = res.m_Scheme; protocol = res.m_Scheme;
host = res.m_Host; host = res.m_Host;
path = res.m_Path; path = res.m_Path;
query = res.m_Query; query = res.m_Query;
if (!res.GetPort(&port)) if (!res.GetPort(&port))
{ {
@ -64,4 +65,4 @@ namespace ix
return true; return true;
} }
} } // namespace ix

View File

@ -5,44 +5,44 @@
*/ */
#include "IXUserAgent.h" #include "IXUserAgent.h"
#include "IXWebSocketVersion.h"
#include "IXWebSocketVersion.h"
#include <sstream> #include <sstream>
#include <zlib.h> #include <zlib.h>
// Platform name // Platform name
#if defined(_WIN32) #if defined(_WIN32)
#define PLATFORM_NAME "windows" // Windows #define PLATFORM_NAME "windows" // Windows
#elif defined(_WIN64) #elif defined(_WIN64)
#define PLATFORM_NAME "windows" // Windows #define PLATFORM_NAME "windows" // Windows
#elif defined(__CYGWIN__) && !defined(_WIN32) #elif defined(__CYGWIN__) && !defined(_WIN32)
#define PLATFORM_NAME "windows" // Windows (Cygwin POSIX under Microsoft Window) #define PLATFORM_NAME "windows" // Windows (Cygwin POSIX under Microsoft Window)
#elif defined(__ANDROID__) #elif defined(__ANDROID__)
#define PLATFORM_NAME "android" // Android (implies Linux, so it must come first) #define PLATFORM_NAME "android" // Android (implies Linux, so it must come first)
#elif defined(__linux__) #elif defined(__linux__)
#define PLATFORM_NAME "linux" // Debian, Ubuntu, Gentoo, Fedora, openSUSE, RedHat, Centos and other #define PLATFORM_NAME "linux" // Debian, Ubuntu, Gentoo, Fedora, openSUSE, RedHat, Centos and other
#elif defined(__unix__) || !defined(__APPLE__) && defined(__MACH__) #elif defined(__unix__) || !defined(__APPLE__) && defined(__MACH__)
#include <sys/param.h> #include <sys/param.h>
#if defined(BSD) #if defined(BSD)
#define PLATFORM_NAME "bsd" // FreeBSD, NetBSD, OpenBSD, DragonFly BSD #define PLATFORM_NAME "bsd" // FreeBSD, NetBSD, OpenBSD, DragonFly BSD
#endif #endif
#elif defined(__hpux) #elif defined(__hpux)
#define PLATFORM_NAME "hp-ux" // HP-UX #define PLATFORM_NAME "hp-ux" // HP-UX
#elif defined(_AIX) #elif defined(_AIX)
#define PLATFORM_NAME "aix" // IBM AIX #define PLATFORM_NAME "aix" // IBM AIX
#elif defined(__APPLE__) && defined(__MACH__) // Apple OSX and iOS (Darwin) #elif defined(__APPLE__) && defined(__MACH__) // Apple OSX and iOS (Darwin)
#include <TargetConditionals.h> #include <TargetConditionals.h>
#if TARGET_IPHONE_SIMULATOR == 1 #if TARGET_IPHONE_SIMULATOR == 1
#define PLATFORM_NAME "ios" // Apple iOS #define PLATFORM_NAME "ios" // Apple iOS
#elif TARGET_OS_IPHONE == 1 #elif TARGET_OS_IPHONE == 1
#define PLATFORM_NAME "ios" // Apple iOS #define PLATFORM_NAME "ios" // Apple iOS
#elif TARGET_OS_MAC == 1 #elif TARGET_OS_MAC == 1
#define PLATFORM_NAME "macos" // Apple OSX #define PLATFORM_NAME "macos" // Apple OSX
#endif #endif
#elif defined(__sun) && defined(__SVR4) #elif defined(__sun) && defined(__SVR4)
#define PLATFORM_NAME "solaris" // Oracle Solaris, Open Indiana #define PLATFORM_NAME "solaris" // Oracle Solaris, Open Indiana
#else #else
#define PLATFORM_NAME "unknown platform" #define PLATFORM_NAME "unknown platform"
#endif #endif
// SSL // SSL
@ -80,4 +80,4 @@ namespace ix
return ss.str(); return ss.str();
} }
} } // namespace ix

View File

@ -24,7 +24,7 @@
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. * SOFTWARE.
*/ */
/* /*
* IXUtf8Validator.h * IXUtf8Validator.h
@ -48,20 +48,31 @@ namespace ix
/// Lookup table for the UTF8 decode state machine /// Lookup table for the UTF8 decode state machine
static uint8_t const utf8d[] = { static uint8_t const utf8d[] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..1f 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3f 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1f
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40..5f 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 60..7f 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20..3f
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9f 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // a0..bf 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..5f
8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // c0..df 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, // e0..ef 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60..7f
0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, // f0..ff 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, // s0..s0 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9f
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, // s1..s2 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, // s3..s4 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // a0..bf
1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, // s5..s6 8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // s7..s8 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // c0..df
0xa, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // e0..ef
0xb, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // f0..ff
0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2
1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4
1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6
1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1,
1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // s7..s8
}; };
/// Decode the next byte of a UTF8 sequence /// Decode the next byte of a UTF8 sequence
@ -71,16 +82,14 @@ namespace ix
* @param [in] byte The byte to input * @param [in] byte The byte to input
* @return The ending state of the decode operation * @return The ending state of the decode operation
*/ */
inline uint32_t decodeNextByte(uint32_t * state, uint32_t * codep, uint8_t byte) inline uint32_t decodeNextByte(uint32_t* state, uint32_t* codep, uint8_t byte)
{ {
uint32_t type = utf8d[byte]; uint32_t type = utf8d[byte];
*codep = (*state != utf8_accept) ? *codep = (*state != utf8_accept) ? (byte & 0x3fu) | (*codep << 6) : (0xff >> type) & (byte);
(byte & 0x3fu) | (*codep << 6) :
(0xff >> type) & (byte);
*state = utf8d[256 + *state*16 + type]; *state = utf8d[256 + *state * 16 + type];
return *state; return *state;
} }
/// Provides streaming UTF8 validation functionality /// Provides streaming UTF8 validation functionality
@ -88,7 +97,11 @@ namespace ix
{ {
public: public:
/// Construct and initialize the validator /// Construct and initialize the validator
Utf8Validator() : m_state(utf8_accept),m_codepoint(0) {} Utf8Validator()
: m_state(utf8_accept)
, m_codepoint(0)
{
}
/// Advance the state of the validator with the next input byte /// Advance the state of the validator with the next input byte
/** /**
@ -97,7 +110,7 @@ namespace ix
*/ */
bool consume(uint8_t byte) bool consume(uint8_t byte)
{ {
if (decodeNextByte(&m_state,&m_codepoint,byte) == utf8_reject) if (decodeNextByte(&m_state, &m_codepoint, byte) == utf8_reject)
{ {
return false; return false;
} }
@ -110,16 +123,13 @@ namespace ix
* @param end Input iterator to the end of the input range * @param end Input iterator to the end of the input range
* @return Whether or not decoding the bytes resulted in a validation error. * @return Whether or not decoding the bytes resulted in a validation error.
*/ */
template <typename iterator_type> template<typename iterator_type>
bool decode(iterator_type begin, iterator_type end) bool decode(iterator_type begin, iterator_type end)
{ {
for (iterator_type it = begin; it != end; ++it) for (iterator_type it = begin; it != end; ++it)
{ {
unsigned int result = decodeNextByte( unsigned int result =
&m_state, decodeNextByte(&m_state, &m_codepoint, static_cast<uint8_t>(*it));
&m_codepoint,
static_cast<uint8_t>(*it)
);
if (result == utf8_reject) if (result == utf8_reject)
{ {
@ -144,9 +154,10 @@ namespace ix
m_state = utf8_accept; m_state = utf8_accept;
m_codepoint = 0; m_codepoint = 0;
} }
private: private:
uint32_t m_state; uint32_t m_state;
uint32_t m_codepoint; uint32_t m_codepoint;
}; };
/// Validate a UTF8 string /// Validate a UTF8 string
@ -154,10 +165,10 @@ namespace ix
* convenience function that creates a Validator, validates a complete string * convenience function that creates a Validator, validates a complete string
* and returns the result. * and returns the result.
*/ */
inline bool validateUtf8(std::string const & s) inline bool validateUtf8(std::string const& s)
{ {
Utf8Validator v; Utf8Validator v;
if (!v.decode(s.begin(),s.end())) if (!v.decode(s.begin(), s.end()))
{ {
return false; return false;
} }

View File

@ -5,13 +5,13 @@
*/ */
#include "IXWebSocket.h" #include "IXWebSocket.h"
#include "IXSetThreadName.h"
#include "IXWebSocketHandshake.h"
#include "IXExponentialBackoff.h"
#include "IXUtf8Validator.h"
#include <cmath> #include "IXExponentialBackoff.h"
#include "IXSetThreadName.h"
#include "IXUtf8Validator.h"
#include "IXWebSocketHandshake.h"
#include <cassert> #include <cassert>
#include <cmath>
namespace ix namespace ix
@ -23,26 +23,26 @@ namespace ix
const bool WebSocket::kDefaultEnablePong(true); const bool WebSocket::kDefaultEnablePong(true);
const uint32_t WebSocket::kDefaultMaxWaitBetweenReconnectionRetries(10 * 1000); // 10s const uint32_t WebSocket::kDefaultMaxWaitBetweenReconnectionRetries(10 * 1000); // 10s
WebSocket::WebSocket() : WebSocket::WebSocket()
_onMessageCallback(OnMessageCallback()), : _onMessageCallback(OnMessageCallback())
_stop(false), , _stop(false)
_automaticReconnection(true), , _automaticReconnection(true)
_maxWaitBetweenReconnectionRetries(kDefaultMaxWaitBetweenReconnectionRetries), , _maxWaitBetweenReconnectionRetries(kDefaultMaxWaitBetweenReconnectionRetries)
_handshakeTimeoutSecs(kDefaultHandShakeTimeoutSecs), , _handshakeTimeoutSecs(kDefaultHandShakeTimeoutSecs)
_enablePong(kDefaultEnablePong), , _enablePong(kDefaultEnablePong)
_pingIntervalSecs(kDefaultPingIntervalSecs), , _pingIntervalSecs(kDefaultPingIntervalSecs)
_pingTimeoutSecs(kDefaultPingTimeoutSecs) , _pingTimeoutSecs(kDefaultPingTimeoutSecs)
{ {
_ws.setOnCloseCallback( _ws.setOnCloseCallback(
[this](uint16_t code, const std::string& reason, size_t wireSize, bool remote) [this](uint16_t code, const std::string& reason, size_t wireSize, bool remote) {
{
_onMessageCallback( _onMessageCallback(
std::make_shared<WebSocketMessage>( std::make_shared<WebSocketMessage>(WebSocketMessageType::Close,
WebSocketMessageType::Close, "", wireSize, "",
WebSocketErrorInfo(), WebSocketOpenInfo(), wireSize,
WebSocketCloseInfo(code, reason, remote))); WebSocketErrorInfo(),
} WebSocketOpenInfo(),
); WebSocketCloseInfo(code, reason, remote)));
});
} }
WebSocket::~WebSocket() WebSocket::~WebSocket()
@ -67,7 +67,8 @@ namespace ix
return _url; return _url;
} }
void WebSocket::setPerMessageDeflateOptions(const WebSocketPerMessageDeflateOptions& perMessageDeflateOptions) void WebSocket::setPerMessageDeflateOptions(
const WebSocketPerMessageDeflateOptions& perMessageDeflateOptions)
{ {
std::lock_guard<std::mutex> lock(_configMutex); std::lock_guard<std::mutex> lock(_configMutex);
_perMessageDeflateOptions = perMessageDeflateOptions; _perMessageDeflateOptions = perMessageDeflateOptions;
@ -159,8 +160,7 @@ namespace ix
_thread = std::thread(&WebSocket::run, this); _thread = std::thread(&WebSocket::run, this);
} }
void WebSocket::stop(uint16_t code, void WebSocket::stop(uint16_t code, const std::string& reason)
const std::string& reason)
{ {
close(code, reason); close(code, reason);
@ -192,11 +192,12 @@ namespace ix
} }
_onMessageCallback( _onMessageCallback(
std::make_shared<WebSocketMessage>( std::make_shared<WebSocketMessage>(WebSocketMessageType::Open,
WebSocketMessageType::Open, "", 0, "",
WebSocketErrorInfo(), 0,
WebSocketOpenInfo(status.uri, status.headers), WebSocketErrorInfo(),
WebSocketCloseInfo())); WebSocketOpenInfo(status.uri, status.headers),
WebSocketCloseInfo()));
return status; return status;
} }
@ -218,11 +219,12 @@ namespace ix
} }
_onMessageCallback( _onMessageCallback(
std::make_shared<WebSocketMessage>( std::make_shared<WebSocketMessage>(WebSocketMessageType::Open,
WebSocketMessageType::Open, "", 0, "",
WebSocketErrorInfo(), 0,
WebSocketOpenInfo(status.uri, status.headers), WebSocketErrorInfo(),
WebSocketCloseInfo())); WebSocketOpenInfo(status.uri, status.headers),
WebSocketCloseInfo()));
return status; return status;
} }
@ -236,8 +238,7 @@ namespace ix
return getReadyState() == ReadyState::Closing; return getReadyState() == ReadyState::Closing;
} }
void WebSocket::close(uint16_t code, void WebSocket::close(uint16_t code, const std::string& reason)
const std::string& reason)
{ {
_ws.close(code, reason); _ws.close(code, reason);
} }
@ -281,20 +282,22 @@ namespace ix
if (_automaticReconnection) if (_automaticReconnection)
{ {
duration = millis(calculateRetryWaitMilliseconds(retries++, _maxWaitBetweenReconnectionRetries)); duration = millis(calculateRetryWaitMilliseconds(
retries++, _maxWaitBetweenReconnectionRetries));
connectErr.wait_time = duration.count(); connectErr.wait_time = duration.count();
connectErr.retries = retries; connectErr.retries = retries;
} }
connectErr.reason = status.errorStr; connectErr.reason = status.errorStr;
connectErr.http_status = status.http_status; connectErr.http_status = status.http_status;
_onMessageCallback( _onMessageCallback(std::make_shared<WebSocketMessage>(WebSocketMessageType::Error,
std::make_shared<WebSocketMessage>( "",
WebSocketMessageType::Error, "", 0, 0,
connectErr, WebSocketOpenInfo(), connectErr,
WebSocketCloseInfo())); WebSocketOpenInfo(),
WebSocketCloseInfo()));
} }
} }
} }
@ -330,8 +333,7 @@ namespace ix
[this](const std::string& msg, [this](const std::string& msg,
size_t wireSize, size_t wireSize,
bool decompressionError, bool decompressionError,
WebSocketTransport::MessageKind messageKind) WebSocketTransport::MessageKind messageKind) {
{
WebSocketMessageType webSocketMessageType; WebSocketMessageType webSocketMessageType;
switch (messageKind) switch (messageKind)
{ {
@ -339,22 +341,26 @@ namespace ix
case WebSocketTransport::MessageKind::MSG_BINARY: case WebSocketTransport::MessageKind::MSG_BINARY:
{ {
webSocketMessageType = WebSocketMessageType::Message; webSocketMessageType = WebSocketMessageType::Message;
} break; }
break;
case WebSocketTransport::MessageKind::PING: case WebSocketTransport::MessageKind::PING:
{ {
webSocketMessageType = WebSocketMessageType::Ping; webSocketMessageType = WebSocketMessageType::Ping;
} break; }
break;
case WebSocketTransport::MessageKind::PONG: case WebSocketTransport::MessageKind::PONG:
{ {
webSocketMessageType = WebSocketMessageType::Pong; webSocketMessageType = WebSocketMessageType::Pong;
} break; }
break;
case WebSocketTransport::MessageKind::FRAGMENT: case WebSocketTransport::MessageKind::FRAGMENT:
{ {
webSocketMessageType = WebSocketMessageType::Fragment; webSocketMessageType = WebSocketMessageType::Fragment;
} break; }
break;
} }
WebSocketErrorInfo webSocketErrorInfo; WebSocketErrorInfo webSocketErrorInfo;
@ -362,11 +368,13 @@ namespace ix
bool binary = messageKind == WebSocketTransport::MessageKind::MSG_BINARY; bool binary = messageKind == WebSocketTransport::MessageKind::MSG_BINARY;
_onMessageCallback( _onMessageCallback(std::make_shared<WebSocketMessage>(webSocketMessageType,
std::make_shared<WebSocketMessage>( msg,
webSocketMessageType, msg, wireSize, wireSize,
webSocketErrorInfo, WebSocketOpenInfo(), webSocketErrorInfo,
WebSocketCloseInfo(), binary)); WebSocketOpenInfo(),
WebSocketCloseInfo(),
binary));
WebSocket::invokeTrafficTrackerCallback(msg.size(), true); WebSocket::invokeTrafficTrackerCallback(msg.size(), true);
}); });
@ -453,17 +461,20 @@ namespace ix
case SendMessageKind::Text: case SendMessageKind::Text:
{ {
webSocketSendInfo = _ws.sendText(text, onProgressCallback); webSocketSendInfo = _ws.sendText(text, onProgressCallback);
} break; }
break;
case SendMessageKind::Binary: case SendMessageKind::Binary:
{ {
webSocketSendInfo = _ws.sendBinary(text, onProgressCallback); webSocketSendInfo = _ws.sendBinary(text, onProgressCallback);
} break; }
break;
case SendMessageKind::Ping: case SendMessageKind::Ping:
{ {
webSocketSendInfo = _ws.sendPing(text); webSocketSendInfo = _ws.sendPing(text);
} break; }
break;
} }
WebSocket::invokeTrafficTrackerCallback(webSocketSendInfo.wireSize, false); WebSocket::invokeTrafficTrackerCallback(webSocketSendInfo.wireSize, false);
@ -475,10 +486,10 @@ namespace ix
{ {
switch (_ws.getReadyState()) switch (_ws.getReadyState())
{ {
case ix::WebSocketTransport::ReadyState::OPEN : return ReadyState::Open; case ix::WebSocketTransport::ReadyState::OPEN: return ReadyState::Open;
case ix::WebSocketTransport::ReadyState::CONNECTING: return ReadyState::Connecting; case ix::WebSocketTransport::ReadyState::CONNECTING: return ReadyState::Connecting;
case ix::WebSocketTransport::ReadyState::CLOSING : return ReadyState::Closing; case ix::WebSocketTransport::ReadyState::CLOSING: return ReadyState::Closing;
case ix::WebSocketTransport::ReadyState::CLOSED : return ReadyState::Closed; case ix::WebSocketTransport::ReadyState::CLOSED: return ReadyState::Closed;
default: return ReadyState::Closed; default: return ReadyState::Closed;
} }
} }
@ -487,10 +498,10 @@ namespace ix
{ {
switch (readyState) switch (readyState)
{ {
case ReadyState::Open : return "OPEN"; case ReadyState::Open: return "OPEN";
case ReadyState::Connecting: return "CONNECTING"; case ReadyState::Connecting: return "CONNECTING";
case ReadyState::Closing : return "CLOSING"; case ReadyState::Closing: return "CLOSING";
case ReadyState::Closed : return "CLOSED"; case ReadyState::Closed: return "CLOSED";
default: return "UNKNOWN"; default: return "UNKNOWN";
} }
} }
@ -514,4 +525,4 @@ namespace ix
{ {
return _ws.bufferedAmount(); return _ws.bufferedAmount();
} }
} } // namespace ix

View File

@ -22,10 +22,15 @@ namespace ix
const std::string WebSocketCloseConstants::kProtocolErrorMessage("Protocol error"); const std::string WebSocketCloseConstants::kProtocolErrorMessage("Protocol error");
const std::string WebSocketCloseConstants::kNoStatusCodeErrorMessage("No status code"); const std::string WebSocketCloseConstants::kNoStatusCodeErrorMessage("No status code");
const std::string WebSocketCloseConstants::kProtocolErrorReservedBitUsed("Reserved bit used"); const std::string WebSocketCloseConstants::kProtocolErrorReservedBitUsed("Reserved bit used");
const std::string WebSocketCloseConstants::kProtocolErrorPingPayloadOversized("Ping reason control frame with payload length > 125 octets"); const std::string WebSocketCloseConstants::kProtocolErrorPingPayloadOversized(
const std::string WebSocketCloseConstants::kProtocolErrorCodeControlMessageFragmented("Control message fragmented"); "Ping reason control frame with payload length > 125 octets");
const std::string WebSocketCloseConstants::kProtocolErrorCodeDataOpcodeOutOfSequence("Fragmentation: data message out of sequence"); const std::string WebSocketCloseConstants::kProtocolErrorCodeControlMessageFragmented(
const std::string WebSocketCloseConstants::kProtocolErrorCodeContinuationOpCodeOutOfSequence("Fragmentation: continuation opcode out of sequence"); "Control message fragmented");
const std::string WebSocketCloseConstants::kInvalidFramePayloadDataMessage("Invalid frame payload data"); const std::string WebSocketCloseConstants::kProtocolErrorCodeDataOpcodeOutOfSequence(
"Fragmentation: data message out of sequence");
const std::string WebSocketCloseConstants::kProtocolErrorCodeContinuationOpCodeOutOfSequence(
"Fragmentation: continuation opcode out of sequence");
const std::string WebSocketCloseConstants::kInvalidFramePayloadDataMessage(
"Invalid frame payload data");
const std::string WebSocketCloseConstants::kInvalidCloseCodeMessage("Invalid close code"); const std::string WebSocketCloseConstants::kInvalidCloseCodeMessage("Invalid close code");
} } // namespace ix

View File

@ -5,50 +5,45 @@
*/ */
#include "IXWebSocketHandshake.h" #include "IXWebSocketHandshake.h"
#include "IXHttp.h"
#include "IXSocketConnect.h" #include "IXSocketConnect.h"
#include "IXUrlParser.h" #include "IXUrlParser.h"
#include "IXHttp.h"
#include "IXUserAgent.h" #include "IXUserAgent.h"
#include "libwshandshake.hpp" #include "libwshandshake.hpp"
#include <sstream>
#include <random>
#include <algorithm> #include <algorithm>
#include <random>
#include <sstream>
namespace ix namespace ix
{ {
WebSocketHandshake::WebSocketHandshake(std::atomic<bool>& requestInitCancellation, WebSocketHandshake::WebSocketHandshake(
std::shared_ptr<Socket> socket, std::atomic<bool>& requestInitCancellation,
WebSocketPerMessageDeflate& perMessageDeflate, std::shared_ptr<Socket> socket,
WebSocketPerMessageDeflateOptions& perMessageDeflateOptions, WebSocketPerMessageDeflate& perMessageDeflate,
std::atomic<bool>& enablePerMessageDeflate) : WebSocketPerMessageDeflateOptions& perMessageDeflateOptions,
_requestInitCancellation(requestInitCancellation), std::atomic<bool>& enablePerMessageDeflate)
_socket(socket), : _requestInitCancellation(requestInitCancellation)
_perMessageDeflate(perMessageDeflate), , _socket(socket)
_perMessageDeflateOptions(perMessageDeflateOptions), , _perMessageDeflate(perMessageDeflate)
_enablePerMessageDeflate(enablePerMessageDeflate) , _perMessageDeflateOptions(perMessageDeflateOptions)
, _enablePerMessageDeflate(enablePerMessageDeflate)
{ {
} }
bool WebSocketHandshake::insensitiveStringCompare(const std::string& a, const std::string& b) bool WebSocketHandshake::insensitiveStringCompare(const std::string& a, const std::string& b)
{ {
return std::equal(a.begin(), a.end(), return std::equal(a.begin(), a.end(), b.begin(), b.end(), [](char a, char b) {
b.begin(), b.end(), return tolower(a) == tolower(b);
[](char a, char b) });
{
return tolower(a) == tolower(b);
});
} }
std::string WebSocketHandshake::genRandomString(const int len) std::string WebSocketHandshake::genRandomString(const int len)
{ {
std::string alphanum = std::string alphanum = "0123456789"
"0123456789" "ABCDEFGH"
"ABCDEFGH" "abcdefgh";
"abcdefgh";
std::random_device r; std::random_device r;
std::default_random_engine e1(r()); std::default_random_engine e1(r());
@ -88,12 +83,13 @@ namespace ix
return WebSocketInitResult(false, code, reason); return WebSocketInitResult(false, code, reason);
} }
WebSocketInitResult WebSocketHandshake::clientHandshake(const std::string& url, WebSocketInitResult WebSocketHandshake::clientHandshake(
const WebSocketHttpHeaders& extraHeaders, const std::string& url,
const std::string& host, const WebSocketHttpHeaders& extraHeaders,
const std::string& path, const std::string& host,
int port, const std::string& path,
int timeoutSecs) int port,
int timeoutSecs)
{ {
_requestInitCancellation = false; _requestInitCancellation = false;
@ -105,9 +101,7 @@ namespace ix
if (!success) if (!success)
{ {
std::stringstream ss; std::stringstream ss;
ss << "Unable to connect to " << host ss << "Unable to connect to " << host << " on port " << port << ", error: " << errMsg;
<< " on port " << port
<< ", error: " << errMsg;
return WebSocketInitResult(false, 0, ss.str()); return WebSocketInitResult(false, 0, ss.str());
} }
@ -123,7 +117,7 @@ namespace ix
std::stringstream ss; std::stringstream ss;
ss << "GET " << path << " HTTP/1.1\r\n"; ss << "GET " << path << " HTTP/1.1\r\n";
ss << "Host: "<< host << ":" << port << "\r\n"; ss << "Host: " << host << ":" << port << "\r\n";
ss << "Upgrade: websocket\r\n"; ss << "Upgrade: websocket\r\n";
ss << "Connection: Upgrade\r\n"; ss << "Connection: Upgrade\r\n";
ss << "Sec-WebSocket-Version: 13\r\n"; ss << "Sec-WebSocket-Version: 13\r\n";
@ -149,7 +143,8 @@ namespace ix
if (!_socket->writeBytes(ss.str(), isCancellationRequested)) if (!_socket->writeBytes(ss.str(), isCancellationRequested))
{ {
return WebSocketInitResult(false, 0, std::string("Failed sending GET request to ") + url); return WebSocketInitResult(
false, 0, std::string("Failed sending GET request to ") + url);
} }
// Read HTTP status line // Read HTTP status line
@ -159,8 +154,8 @@ namespace ix
if (!lineValid) if (!lineValid)
{ {
return WebSocketInitResult(false, 0, return WebSocketInitResult(
std::string("Failed reading HTTP status line from ") + url); false, 0, std::string("Failed reading HTTP status line from ") + url);
} }
// Validate status // Validate status
@ -173,8 +168,7 @@ namespace ix
{ {
std::stringstream ss; std::stringstream ss;
ss << "Expecting HTTP/1.1, got " << httpVersion << ". " ss << "Expecting HTTP/1.1, got " << httpVersion << ". "
<< "Rejecting connection to " << host << ":" << port << "Rejecting connection to " << host << ":" << port << ", status: " << status
<< ", status: " << status
<< ", HTTP Status line: " << line; << ", HTTP Status line: " << line;
return WebSocketInitResult(false, status, ss.str()); return WebSocketInitResult(false, status, ss.str());
} }
@ -183,8 +177,7 @@ namespace ix
if (status != 101) if (status != 101)
{ {
std::stringstream ss; std::stringstream ss;
ss << "Got bad status connecting to " << host << ":" << port ss << "Got bad status connecting to " << host << ":" << port << ", status: " << status
<< ", status: " << status
<< ", HTTP Status line: " << line; << ", HTTP Status line: " << line;
return WebSocketInitResult(false, status, ss.str()); return WebSocketInitResult(false, status, ss.str());
} }
@ -269,8 +262,8 @@ namespace ix
// Validate request line (GET /foo HTTP/1.1\r\n) // Validate request line (GET /foo HTTP/1.1\r\n)
auto requestLine = Http::parseRequestLine(line); auto requestLine = Http::parseRequestLine(line);
auto method = std::get<0>(requestLine); auto method = std::get<0>(requestLine);
auto uri = std::get<1>(requestLine); auto uri = std::get<1>(requestLine);
auto httpVersion = std::get<2>(requestLine); auto httpVersion = std::get<2>(requestLine);
if (method != "GET") if (method != "GET")
@ -280,7 +273,8 @@ namespace ix
if (httpVersion != "HTTP/1.1") if (httpVersion != "HTTP/1.1")
{ {
return sendErrorResponse(400, "Invalid HTTP version, need HTTP/1.1, got: " + httpVersion); return sendErrorResponse(400,
"Invalid HTTP version, need HTTP/1.1, got: " + httpVersion);
} }
// Retrieve and validate HTTP headers // Retrieve and validate HTTP headers
@ -305,8 +299,10 @@ namespace ix
if (!insensitiveStringCompare(headers["upgrade"], "WebSocket")) if (!insensitiveStringCompare(headers["upgrade"], "WebSocket"))
{ {
return sendErrorResponse(400, "Invalid Upgrade header, " return sendErrorResponse(400,
"need WebSocket, got " + headers["upgrade"]); "Invalid Upgrade header, "
"need WebSocket, got " +
headers["upgrade"]);
} }
if (headers.find("sec-websocket-version") == headers.end()) if (headers.find("sec-websocket-version") == headers.end())
@ -322,8 +318,10 @@ namespace ix
if (version != 13) if (version != 13)
{ {
return sendErrorResponse(400, "Invalid Sec-WebSocket-Version, " return sendErrorResponse(400,
"need 13, got " + ss.str()); "Invalid Sec-WebSocket-Version, "
"need 13, got " +
ss.str());
} }
} }
@ -349,7 +347,7 @@ namespace ix
if (!_perMessageDeflate.init(webSocketPerMessageDeflateOptions)) if (!_perMessageDeflate.init(webSocketPerMessageDeflateOptions))
{ {
return WebSocketInitResult( return WebSocketInitResult(
false, 0,"Failed to initialize per message deflate engine"); false, 0, "Failed to initialize per message deflate engine");
} }
ss << webSocketPerMessageDeflateOptions.generateHeader(); ss << webSocketPerMessageDeflateOptions.generateHeader();
} }
@ -358,9 +356,10 @@ namespace ix
if (!_socket->writeBytes(ss.str(), isCancellationRequested)) if (!_socket->writeBytes(ss.str(), isCancellationRequested))
{ {
return WebSocketInitResult(false, 0, std::string("Failed sending response to ") + remote); return WebSocketInitResult(
false, 0, std::string("Failed sending response to ") + remote);
} }
return WebSocketInitResult(true, 200, "", headers, uri); return WebSocketInitResult(true, 200, "", headers, uri);
} }
} } // namespace ix

View File

@ -49,13 +49,12 @@ namespace ix
WebSocketPerMessageDeflateOptions& perMessageDeflateOptions, WebSocketPerMessageDeflateOptions& perMessageDeflateOptions,
std::atomic<bool>& enablePerMessageDeflate); std::atomic<bool>& enablePerMessageDeflate);
WebSocketInitResult clientHandshake( WebSocketInitResult clientHandshake(const std::string& url,
const std::string& url, const WebSocketHttpHeaders& extraHeaders,
const WebSocketHttpHeaders& extraHeaders, const std::string& host,
const std::string& host, const std::string& path,
const std::string& path, int port,
int port, int timeoutSecs);
int timeoutSecs);
WebSocketInitResult serverHandshake(int fd, int timeoutSecs); WebSocketInitResult serverHandshake(int fd, int timeoutSecs);

View File

@ -5,13 +5,15 @@
*/ */
#include "IXWebSocketHttpHeaders.h" #include "IXWebSocketHttpHeaders.h"
#include "IXSocket.h" #include "IXSocket.h"
#include <algorithm> #include <algorithm>
#include <locale> #include <locale>
namespace ix namespace ix
{ {
bool CaseInsensitiveLess::NocaseCompare::operator()(const unsigned char & c1, const unsigned char & c2) const bool CaseInsensitiveLess::NocaseCompare::operator()(const unsigned char& c1,
const unsigned char& c2) const
{ {
#ifdef _WIN32 #ifdef _WIN32
return std::tolower(c1, std::locale()) < std::tolower(c2, std::locale()); return std::tolower(c1, std::locale()) < std::tolower(c2, std::locale());
@ -20,17 +22,17 @@ namespace ix
#endif #endif
} }
bool CaseInsensitiveLess::operator()(const std::string & s1, const std::string & s2) const bool CaseInsensitiveLess::operator()(const std::string& s1, const std::string& s2) const
{ {
return std::lexicographical_compare return std::lexicographical_compare(s1.begin(),
(s1.begin(), s1.end(), // source range s1.end(), // source range
s2.begin(), s2.end(), // dest range s2.begin(),
NocaseCompare()); // comparison s2.end(), // dest range
NocaseCompare()); // comparison
} }
std::pair<bool, WebSocketHttpHeaders> parseHttpHeaders( std::pair<bool, WebSocketHttpHeaders> parseHttpHeaders(
std::shared_ptr<Socket> socket, std::shared_ptr<Socket> socket, const CancellationRequest& isCancellationRequested)
const CancellationRequest& isCancellationRequested)
{ {
WebSocketHttpHeaders headers; WebSocketHttpHeaders headers;
@ -41,11 +43,9 @@ namespace ix
{ {
int colon = 0; int colon = 0;
for (i = 0; for (i = 0; i < 2 || (i < 1023 && line[i - 2] != '\r' && line[i - 1] != '\n'); ++i)
i < 2 || (i < 1023 && line[i-2] != '\r' && line[i-1] != '\n');
++i)
{ {
if (!socket->readByte(line+i, isCancellationRequested)) if (!socket->readByte(line + i, isCancellationRequested))
{ {
return std::make_pair(false, headers); return std::make_pair(false, headers);
} }
@ -79,4 +79,4 @@ namespace ix
return std::make_pair(true, headers); return std::make_pair(true, headers);
} }
} } // namespace ix

View File

@ -8,7 +8,6 @@
namespace ix namespace ix
{ {
WebSocketMessageQueue::WebSocketMessageQueue(WebSocket* websocket) WebSocketMessageQueue::WebSocketMessageQueue(WebSocket* websocket)
{ {
bindWebsocket(websocket); bindWebsocket(websocket);
@ -24,7 +23,7 @@ namespace ix
bindWebsocket(nullptr); bindWebsocket(nullptr);
} }
void WebSocketMessageQueue::bindWebsocket(WebSocket * websocket) void WebSocketMessageQueue::bindWebsocket(WebSocket* websocket)
{ {
if (_websocket == websocket) return; if (_websocket == websocket) return;
@ -40,8 +39,7 @@ namespace ix
// bind new // bind new
if (_websocket) if (_websocket)
{ {
_websocket->setOnMessageCallback([this](const WebSocketMessagePtr& msg) _websocket->setOnMessageCallback([this](const WebSocketMessagePtr& msg) {
{
std::lock_guard<std::mutex> lock(_messagesMutex); std::lock_guard<std::mutex> lock(_messagesMutex);
_messages.emplace_back(std::move(msg)); _messages.emplace_back(std::move(msg));
}); });
@ -74,8 +72,7 @@ namespace ix
void WebSocketMessageQueue::poll(int count) void WebSocketMessageQueue::poll(int count)
{ {
if (!_onMessageUserCallback) if (!_onMessageUserCallback) return;
return;
WebSocketMessagePtr message; WebSocketMessagePtr message;
@ -86,4 +83,4 @@ namespace ix
} }
} }
} } // namespace ix

View File

@ -41,19 +41,21 @@
* - Added more documentation. * - Added more documentation.
* *
* Per message Deflate RFC: https://tools.ietf.org/html/rfc7692 * Per message Deflate RFC: https://tools.ietf.org/html/rfc7692
* Chrome websocket -> https://github.com/chromium/chromium/tree/2ca8c5037021c9d2ecc00b787d58a31ed8fc8bcb/net/websockets * Chrome websocket ->
* https://github.com/chromium/chromium/tree/2ca8c5037021c9d2ecc00b787d58a31ed8fc8bcb/net/websockets
* *
*/ */
#include "IXWebSocketPerMessageDeflate.h" #include "IXWebSocketPerMessageDeflate.h"
#include "IXWebSocketPerMessageDeflateOptions.h"
#include "IXWebSocketPerMessageDeflateCodec.h" #include "IXWebSocketPerMessageDeflateCodec.h"
#include "IXWebSocketPerMessageDeflateOptions.h"
namespace ix namespace ix
{ {
WebSocketPerMessageDeflate::WebSocketPerMessageDeflate() : WebSocketPerMessageDeflate::WebSocketPerMessageDeflate()
_compressor(std::make_unique<WebSocketPerMessageDeflateCompressor>()), : _compressor(std::make_unique<WebSocketPerMessageDeflateCompressor>())
_decompressor(std::make_unique<WebSocketPerMessageDeflateDecompressor>()) , _decompressor(std::make_unique<WebSocketPerMessageDeflateDecompressor>())
{ {
; ;
} }
@ -63,10 +65,10 @@ namespace ix
; ;
} }
bool WebSocketPerMessageDeflate::init(const WebSocketPerMessageDeflateOptions& perMessageDeflateOptions) bool WebSocketPerMessageDeflate::init(
const WebSocketPerMessageDeflateOptions& perMessageDeflateOptions)
{ {
bool clientNoContextTakeover = bool clientNoContextTakeover = perMessageDeflateOptions.getClientNoContextTakeover();
perMessageDeflateOptions.getClientNoContextTakeover();
uint8_t deflateBits = perMessageDeflateOptions.getClientMaxWindowBits(); uint8_t deflateBits = perMessageDeflateOptions.getClientMaxWindowBits();
uint8_t inflateBits = perMessageDeflateOptions.getServerMaxWindowBits(); uint8_t inflateBits = perMessageDeflateOptions.getServerMaxWindowBits();
@ -75,16 +77,14 @@ namespace ix
_decompressor->init(inflateBits, clientNoContextTakeover); _decompressor->init(inflateBits, clientNoContextTakeover);
} }
bool WebSocketPerMessageDeflate::compress(const std::string& in, bool WebSocketPerMessageDeflate::compress(const std::string& in, std::string& out)
std::string& out)
{ {
return _compressor->compress(in, out); return _compressor->compress(in, out);
} }
bool WebSocketPerMessageDeflate::decompress(const std::string& in, bool WebSocketPerMessageDeflate::decompress(const std::string& in, std::string& out)
std::string &out)
{ {
return _decompressor->decompress(in, out); return _decompressor->decompress(in, out);
} }
} } // namespace ix

View File

@ -5,8 +5,8 @@
*/ */
#include "IXWebSocketPerMessageDeflateCodec.h" #include "IXWebSocketPerMessageDeflateCodec.h"
#include "IXWebSocketPerMessageDeflateOptions.h"
#include "IXWebSocketPerMessageDeflateOptions.h"
#include <cassert> #include <cassert>
#include <string.h> #include <string.h>
@ -18,7 +18,7 @@ namespace
const std::string kEmptyUncompressedBlock = std::string("\x00\x00\xff\xff", 4); const std::string kEmptyUncompressedBlock = std::string("\x00\x00\xff\xff", 4);
const int kBufferSize = 1 << 14; const int kBufferSize = 1 << 14;
} } // namespace
namespace ix namespace ix
{ {
@ -26,7 +26,7 @@ namespace ix
// Compressor // Compressor
// //
WebSocketPerMessageDeflateCompressor::WebSocketPerMessageDeflateCompressor() WebSocketPerMessageDeflateCompressor::WebSocketPerMessageDeflateCompressor()
: _compressBufferSize(kBufferSize) : _compressBufferSize(kBufferSize)
{ {
memset(&_deflateState, 0, sizeof(_deflateState)); memset(&_deflateState, 0, sizeof(_deflateState));
@ -43,22 +43,18 @@ namespace ix
bool WebSocketPerMessageDeflateCompressor::init(uint8_t deflateBits, bool WebSocketPerMessageDeflateCompressor::init(uint8_t deflateBits,
bool clientNoContextTakeOver) bool clientNoContextTakeOver)
{ {
int ret = deflateInit2( int ret = deflateInit2(&_deflateState,
&_deflateState, Z_DEFAULT_COMPRESSION,
Z_DEFAULT_COMPRESSION, Z_DEFLATED,
Z_DEFLATED, -1 * deflateBits,
-1*deflateBits, 4, // memory level 1-9
4, // memory level 1-9 Z_DEFAULT_STRATEGY);
Z_DEFAULT_STRATEGY
);
if (ret != Z_OK) return false; if (ret != Z_OK) return false;
_compressBuffer = std::make_unique<unsigned char[]>(_compressBufferSize); _compressBuffer = std::make_unique<unsigned char[]>(_compressBufferSize);
_flush = (clientNoContextTakeOver) _flush = (clientNoContextTakeOver) ? Z_FULL_FLUSH : Z_SYNC_FLUSH;
? Z_FULL_FLUSH
: Z_SYNC_FLUSH;
return true; return true;
} }
@ -70,8 +66,7 @@ namespace ix
return std::equal(ending.rbegin(), ending.rend(), value.rbegin()); return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
} }
bool WebSocketPerMessageDeflateCompressor::compress(const std::string& in, bool WebSocketPerMessageDeflateCompressor::compress(const std::string& in, std::string& out)
std::string& out)
{ {
// //
// 7.2.1. Compression // 7.2.1. Compression
@ -95,7 +90,7 @@ namespace ix
if (in.empty()) if (in.empty())
{ {
uint8_t buf[6] = {0x02, 0x00, 0x00, 0x00, 0xff, 0xff}; uint8_t buf[6] = {0x02, 0x00, 0x00, 0x00, 0xff, 0xff};
out.append((char *)(buf), 6); out.append((char*) (buf), 6);
return true; return true;
} }
@ -112,7 +107,7 @@ namespace ix
output = _compressBufferSize - _deflateState.avail_out; output = _compressBufferSize - _deflateState.avail_out;
out.append((char *)(_compressBuffer.get()),output); out.append((char*) (_compressBuffer.get()), output);
} while (_deflateState.avail_out == 0); } while (_deflateState.avail_out == 0);
if (endsWith(out, kEmptyUncompressedBlock)) if (endsWith(out, kEmptyUncompressedBlock))
@ -127,7 +122,7 @@ namespace ix
// Decompressor // Decompressor
// //
WebSocketPerMessageDeflateDecompressor::WebSocketPerMessageDeflateDecompressor() WebSocketPerMessageDeflateDecompressor::WebSocketPerMessageDeflateDecompressor()
: _compressBufferSize(kBufferSize) : _compressBufferSize(kBufferSize)
{ {
memset(&_inflateState, 0, sizeof(_inflateState)); memset(&_inflateState, 0, sizeof(_inflateState));
@ -146,24 +141,18 @@ namespace ix
bool WebSocketPerMessageDeflateDecompressor::init(uint8_t inflateBits, bool WebSocketPerMessageDeflateDecompressor::init(uint8_t inflateBits,
bool clientNoContextTakeOver) bool clientNoContextTakeOver)
{ {
int ret = inflateInit2( int ret = inflateInit2(&_inflateState, -1 * inflateBits);
&_inflateState,
-1*inflateBits
);
if (ret != Z_OK) return false; if (ret != Z_OK) return false;
_compressBuffer = std::make_unique<unsigned char[]>(_compressBufferSize); _compressBuffer = std::make_unique<unsigned char[]>(_compressBufferSize);
_flush = (clientNoContextTakeOver) _flush = (clientNoContextTakeOver) ? Z_FULL_FLUSH : Z_SYNC_FLUSH;
? Z_FULL_FLUSH
: Z_SYNC_FLUSH;
return true; return true;
} }
bool WebSocketPerMessageDeflateDecompressor::decompress(const std::string& in, bool WebSocketPerMessageDeflateDecompressor::decompress(const std::string& in, std::string& out)
std::string& out)
{ {
// //
// 7.2.2. Decompression // 7.2.2. Decompression
@ -179,7 +168,7 @@ namespace ix
inFixed += kEmptyUncompressedBlock; inFixed += kEmptyUncompressedBlock;
_inflateState.avail_in = (uInt) inFixed.size(); _inflateState.avail_in = (uInt) inFixed.size();
_inflateState.next_in = (unsigned char *)(const_cast<char *>(inFixed.data())); _inflateState.next_in = (unsigned char*) (const_cast<char*>(inFixed.data()));
do do
{ {
@ -193,13 +182,10 @@ namespace ix
return false; // zlib error return false; // zlib error
} }
out.append( out.append(reinterpret_cast<char*>(_compressBuffer.get()),
reinterpret_cast<char *>(_compressBuffer.get()), _compressBufferSize - _inflateState.avail_out);
_compressBufferSize - _inflateState.avail_out
);
} while (_inflateState.avail_out == 0); } while (_inflateState.avail_out == 0);
return true; return true;
} }
} } // namespace ix

View File

@ -6,9 +6,9 @@
#include "IXWebSocketPerMessageDeflateOptions.h" #include "IXWebSocketPerMessageDeflateOptions.h"
#include <sstream>
#include <algorithm> #include <algorithm>
#include <cctype> #include <cctype>
#include <sstream>
namespace ix namespace ix
{ {
@ -48,7 +48,8 @@ namespace ix
// //
// Server response could look like that: // Server response could look like that:
// //
// Sec-WebSocket-Extensions: permessage-deflate; client_no_context_takeover; server_no_context_takeover // Sec-WebSocket-Extensions: permessage-deflate; client_no_context_takeover;
// server_no_context_takeover
// //
WebSocketPerMessageDeflateOptions::WebSocketPerMessageDeflateOptions(std::string extension) WebSocketPerMessageDeflateOptions::WebSocketPerMessageDeflateOptions(std::string extension)
{ {
@ -92,8 +93,7 @@ namespace ix
// Sanitize values to be in the proper range [8, 15] in // Sanitize values to be in the proper range [8, 15] in
// case a server would give us bogus values // case a server would give us bogus values
_serverMaxWindowBits = _serverMaxWindowBits =
std::min(maxServerMaxWindowBits, std::min(maxServerMaxWindowBits, std::max(x, minServerMaxWindowBits));
std::max(x, minServerMaxWindowBits));
} }
if (startsWith(token, "client_max_window_bits=")) if (startsWith(token, "client_max_window_bits="))
@ -107,8 +107,7 @@ namespace ix
// Sanitize values to be in the proper range [8, 15] in // Sanitize values to be in the proper range [8, 15] in
// case a server would give us bogus values // case a server would give us bogus values
_clientMaxWindowBits = _clientMaxWindowBits =
std::min(maxClientMaxWindowBits, std::min(maxClientMaxWindowBits, std::max(x, minClientMaxWindowBits));
std::max(x, minClientMaxWindowBits));
sanitizeClientMaxWindowBits(); sanitizeClientMaxWindowBits();
} }
@ -175,11 +174,10 @@ namespace ix
std::string WebSocketPerMessageDeflateOptions::removeSpaces(const std::string& str) std::string WebSocketPerMessageDeflateOptions::removeSpaces(const std::string& str)
{ {
std::string out(str); std::string out(str);
out.erase(std::remove_if(out.begin(), out.erase(
out.end(), std::remove_if(out.begin(), out.end(), [](unsigned char x) { return std::isspace(x); }),
[](unsigned char x){ return std::isspace(x); }), out.end());
out.end());
return out; return out;
} }
} } // namespace ix

View File

@ -5,13 +5,13 @@
*/ */
#include "IXWebSocketServer.h" #include "IXWebSocketServer.h"
#include "IXWebSocketTransport.h"
#include "IXWebSocket.h"
#include "IXSocketConnect.h"
#include "IXNetSystem.h"
#include <sstream> #include "IXNetSystem.h"
#include "IXSocketConnect.h"
#include "IXWebSocket.h"
#include "IXWebSocketTransport.h"
#include <future> #include <future>
#include <sstream>
#include <string.h> #include <string.h>
namespace ix namespace ix
@ -23,11 +23,11 @@ namespace ix
const std::string& host, const std::string& host,
int backlog, int backlog,
size_t maxConnections, size_t maxConnections,
int handshakeTimeoutSecs) : SocketServer(port, host, backlog, maxConnections), int handshakeTimeoutSecs)
_handshakeTimeoutSecs(handshakeTimeoutSecs), : SocketServer(port, host, backlog, maxConnections)
_enablePong(kDefaultEnablePong) , _handshakeTimeoutSecs(handshakeTimeoutSecs)
, _enablePong(kDefaultEnablePong)
{ {
} }
WebSocketServer::~WebSocketServer() WebSocketServer::~WebSocketServer()
@ -63,9 +63,7 @@ namespace ix
_onConnectionCallback = callback; _onConnectionCallback = callback;
} }
void WebSocketServer::handleConnection( void WebSocketServer::handleConnection(int fd, std::shared_ptr<ConnectionState> connectionState)
int fd,
std::shared_ptr<ConnectionState> connectionState)
{ {
auto webSocket = std::make_shared<WebSocket>(); auto webSocket = std::make_shared<WebSocket>();
_onConnectionCallback(webSocket, connectionState); _onConnectionCallback(webSocket, connectionState);
@ -93,10 +91,8 @@ namespace ix
else else
{ {
std::stringstream ss; std::stringstream ss;
ss << "WebSocketServer::handleConnection() HTTP status: " ss << "WebSocketServer::handleConnection() HTTP status: " << status.http_status
<< status.http_status << " error: " << status.errorStr;
<< " error: "
<< status.errorStr;
logError(ss.str()); logError(ss.str());
} }
@ -126,4 +122,4 @@ namespace ix
std::lock_guard<std::mutex> lock(_clientsMutex); std::lock_guard<std::mutex> lock(_clientsMutex);
return _clients.size(); return _clients.size();
} }
} } // namespace ix

View File

@ -32,24 +32,23 @@
// Adapted from https://github.com/dhbaird/easywsclient // Adapted from https://github.com/dhbaird/easywsclient
// //
#include "IXSocketTLSOptions.h"
#include "IXWebSocketTransport.h" #include "IXWebSocketTransport.h"
#include "IXSocketFactory.h"
#include "IXSocketTLSOptions.h"
#include "IXUrlParser.h"
#include "IXUtf8Validator.h"
#include "IXWebSocketHandshake.h" #include "IXWebSocketHandshake.h"
#include "IXWebSocketHttpHeaders.h" #include "IXWebSocketHttpHeaders.h"
#include "IXUrlParser.h"
#include "IXSocketFactory.h"
#include "IXUtf8Validator.h"
#include <string.h>
#include <stdlib.h>
#include <cstdlib>
#include <vector>
#include <string>
#include <cstdarg>
#include <sstream>
#include <chrono> #include <chrono>
#include <cstdarg>
#include <cstdlib>
#include <sstream>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <thread> #include <thread>
#include <vector>
namespace namespace
@ -65,7 +64,7 @@ namespace
return a; return a;
} }
} } // namespace
namespace ix namespace ix
{ {
@ -76,24 +75,24 @@ namespace ix
const int WebSocketTransport::kClosingMaximumWaitingDelayInMs(300); const int WebSocketTransport::kClosingMaximumWaitingDelayInMs(300);
constexpr size_t WebSocketTransport::kChunkSize; constexpr size_t WebSocketTransport::kChunkSize;
WebSocketTransport::WebSocketTransport() : WebSocketTransport::WebSocketTransport()
_useMask(true), : _useMask(true)
_compressedMessage(false), , _compressedMessage(false)
_readyState(ReadyState::CLOSED), , _readyState(ReadyState::CLOSED)
_closeCode(WebSocketCloseConstants::kInternalErrorCode), , _closeCode(WebSocketCloseConstants::kInternalErrorCode)
_closeReason(WebSocketCloseConstants::kInternalErrorMessage), , _closeReason(WebSocketCloseConstants::kInternalErrorMessage)
_closeWireSize(0), , _closeWireSize(0)
_closeRemote(false), , _closeRemote(false)
_enablePerMessageDeflate(false), , _enablePerMessageDeflate(false)
_requestInitCancellation(false), , _requestInitCancellation(false)
_closingTimePoint(std::chrono::steady_clock::now()), , _closingTimePoint(std::chrono::steady_clock::now())
_enablePong(kDefaultEnablePong), , _enablePong(kDefaultEnablePong)
_pingIntervalSecs(kDefaultPingIntervalSecs), , _pingIntervalSecs(kDefaultPingIntervalSecs)
_pingTimeoutSecs(kDefaultPingTimeoutSecs), , _pingTimeoutSecs(kDefaultPingTimeoutSecs)
_pingIntervalOrTimeoutGCDSecs(-1), , _pingIntervalOrTimeoutGCDSecs(-1)
_nextGCDTimePoint(std::chrono::steady_clock::now()), , _nextGCDTimePoint(std::chrono::steady_clock::now())
_lastSendPingTimePoint(std::chrono::steady_clock::now()), , _lastSendPingTimePoint(std::chrono::steady_clock::now())
_lastReceivePongTimePoint(std::chrono::steady_clock::now()) , _lastReceivePongTimePoint(std::chrono::steady_clock::now())
{ {
_readbuf.resize(kChunkSize); _readbuf.resize(kChunkSize);
} }
@ -103,11 +102,12 @@ namespace ix
; ;
} }
void WebSocketTransport::configure(const WebSocketPerMessageDeflateOptions& perMessageDeflateOptions, void WebSocketTransport::configure(
const SocketTLSOptions& socketTLSOptions, const WebSocketPerMessageDeflateOptions& perMessageDeflateOptions,
bool enablePong, const SocketTLSOptions& socketTLSOptions,
int pingIntervalSecs, bool enablePong,
int pingTimeoutSecs) int pingIntervalSecs,
int pingTimeoutSecs)
{ {
_perMessageDeflateOptions = perMessageDeflateOptions; _perMessageDeflateOptions = perMessageDeflateOptions;
_enablePerMessageDeflate = _perMessageDeflateOptions.enabled(); _enablePerMessageDeflate = _perMessageDeflateOptions.enabled();
@ -118,8 +118,8 @@ namespace ix
if (pingIntervalSecs > 0 && pingTimeoutSecs > 0) if (pingIntervalSecs > 0 && pingTimeoutSecs > 0)
{ {
_pingIntervalOrTimeoutGCDSecs = greatestCommonDivisor(pingIntervalSecs, _pingIntervalOrTimeoutGCDSecs =
pingTimeoutSecs); greatestCommonDivisor(pingIntervalSecs, pingTimeoutSecs);
} }
else if (_pingTimeoutSecs > 0) else if (_pingTimeoutSecs > 0)
{ {
@ -132,10 +132,9 @@ namespace ix
} }
// Client // Client
WebSocketInitResult WebSocketTransport::connectToUrl( WebSocketInitResult WebSocketTransport::connectToUrl(const std::string& url,
const std::string& url, const WebSocketHttpHeaders& headers,
const WebSocketHttpHeaders& headers, int timeoutSecs)
int timeoutSecs)
{ {
std::lock_guard<std::mutex> lock(_socketMutex); std::lock_guard<std::mutex> lock(_socketMutex);
@ -144,8 +143,7 @@ namespace ix
if (!UrlParser::parse(url, protocol, host, path, query, port)) if (!UrlParser::parse(url, protocol, host, path, query, port))
{ {
return WebSocketInitResult(false, 0, return WebSocketInitResult(false, 0, std::string("Could not parse URL ") + url);
std::string("Could not parse URL ") + url);
} }
std::string errorMsg; std::string errorMsg;
@ -163,8 +161,8 @@ namespace ix
_perMessageDeflateOptions, _perMessageDeflateOptions,
_enablePerMessageDeflate); _enablePerMessageDeflate);
auto result = webSocketHandshake.clientHandshake(url, headers, host, path, auto result =
port, timeoutSecs); webSocketHandshake.clientHandshake(url, headers, host, path, port, timeoutSecs);
if (result.success) if (result.success)
{ {
setReadyState(ReadyState::OPEN); setReadyState(ReadyState::OPEN);
@ -247,15 +245,15 @@ namespace ix
if (_pingIntervalOrTimeoutGCDSecs > 0) if (_pingIntervalOrTimeoutGCDSecs > 0)
{ {
_nextGCDTimePoint = std::chrono::steady_clock::now() + std::chrono::seconds(_pingIntervalOrTimeoutGCDSecs); _nextGCDTimePoint = std::chrono::steady_clock::now() +
std::chrono::seconds(_pingIntervalOrTimeoutGCDSecs);
} }
} }
// Only consider send PING time points for that computation. // Only consider send PING time points for that computation.
bool WebSocketTransport::pingIntervalExceeded() bool WebSocketTransport::pingIntervalExceeded()
{ {
if (_pingIntervalSecs <= 0) if (_pingIntervalSecs <= 0) return false;
return false;
std::lock_guard<std::mutex> lock(_lastSendPingTimePointMutex); std::lock_guard<std::mutex> lock(_lastSendPingTimePointMutex);
auto now = std::chrono::steady_clock::now(); auto now = std::chrono::steady_clock::now();
@ -264,8 +262,7 @@ namespace ix
bool WebSocketTransport::pingTimeoutExceeded() bool WebSocketTransport::pingTimeoutExceeded()
{ {
if (_pingTimeoutSecs <= 0) if (_pingTimeoutSecs <= 0) return false;
return false;
std::lock_guard<std::mutex> lock(_lastReceivePongTimePointMutex); std::lock_guard<std::mutex> lock(_lastReceivePongTimePointMutex);
auto now = std::chrono::steady_clock::now(); auto now = std::chrono::steady_clock::now();
@ -302,7 +299,8 @@ namespace ix
// No timeout if state is not OPEN, otherwise computed // No timeout if state is not OPEN, otherwise computed
// pingIntervalOrTimeoutGCD (equals to -1 if no ping and no ping timeout are set) // pingIntervalOrTimeoutGCD (equals to -1 if no ping and no ping timeout are set)
int lastingTimeoutDelayInMs = (_readyState != ReadyState::OPEN) ? 0 : _pingIntervalOrTimeoutGCDSecs; int lastingTimeoutDelayInMs =
(_readyState != ReadyState::OPEN) ? 0 : _pingIntervalOrTimeoutGCDSecs;
if (_pingIntervalOrTimeoutGCDSecs > 0) if (_pingIntervalOrTimeoutGCDSecs > 0)
{ {
@ -317,7 +315,10 @@ namespace ix
} }
else else
{ {
lastingTimeoutDelayInMs = (int)std::chrono::duration_cast<std::chrono::milliseconds>(_nextGCDTimePoint - now).count(); lastingTimeoutDelayInMs =
(int) std::chrono::duration_cast<std::chrono::milliseconds>(_nextGCDTimePoint -
now)
.count();
} }
} }
@ -365,7 +366,7 @@ namespace ix
{ {
while (true) while (true)
{ {
ssize_t ret = _socket->recv((char*)&_readbuf[0], _readbuf.size()); ssize_t ret = _socket->recv((char*) &_readbuf[0], _readbuf.size());
if (ret < 0 && Socket::isWaitNeeded()) if (ret < 0 && Socket::isWaitNeeded())
{ {
@ -373,8 +374,9 @@ namespace ix
} }
else if (ret <= 0) else if (ret <= 0)
{ {
// if there are received data pending to be processed, then delay the abnormal closure // if there are received data pending to be processed, then delay the abnormal
// to after dispatch (other close code/reason could be read from the buffer) // closure to after dispatch (other close code/reason could be read from the
// buffer)
closeSocket(); closeSocket();
@ -382,9 +384,7 @@ namespace ix
} }
else else
{ {
_rxbuf.insert(_rxbuf.end(), _rxbuf.insert(_rxbuf.end(), _readbuf.begin(), _readbuf.begin() + ret);
_readbuf.begin(),
_readbuf.begin() + ret);
} }
} }
} }
@ -429,7 +429,7 @@ namespace ix
{ {
for (size_t i = 0; i != (size_t) message_size; ++i) for (size_t i = 0; i != (size_t) message_size; ++i)
{ {
*(_txbuf.end() - (size_t) message_size + i) ^= masking_key[i&0x3]; *(_txbuf.end() - (size_t) message_size + i) ^= masking_key[i & 0x3];
} }
} }
} }
@ -440,7 +440,7 @@ namespace ix
{ {
for (size_t j = 0; j != ws.N; ++j) for (size_t j = 0; j != ws.N; ++j)
{ {
_rxbuf[j+ws.header_size] ^= ws.masking_key[j&0x3]; _rxbuf[j + ws.header_size] ^= ws.masking_key[j & 0x3];
} }
} }
} }
@ -473,16 +473,17 @@ namespace ix
while (true) while (true)
{ {
wsheader_type ws; wsheader_type ws;
if (_rxbuf.size() < 2) break; /* Need at least 2 */ if (_rxbuf.size() < 2) break; /* Need at least 2 */
const uint8_t * data = (uint8_t *) &_rxbuf[0]; // peek, but don't consume const uint8_t* data = (uint8_t*) &_rxbuf[0]; // peek, but don't consume
ws.fin = (data[0] & 0x80) == 0x80; ws.fin = (data[0] & 0x80) == 0x80;
ws.rsv1 = (data[0] & 0x40) == 0x40; ws.rsv1 = (data[0] & 0x40) == 0x40;
ws.rsv2 = (data[0] & 0x20) == 0x20; ws.rsv2 = (data[0] & 0x20) == 0x20;
ws.rsv3 = (data[0] & 0x10) == 0x10; ws.rsv3 = (data[0] & 0x10) == 0x10;
ws.opcode = (wsheader_type::opcode_type) (data[0] & 0x0f); ws.opcode = (wsheader_type::opcode_type)(data[0] & 0x0f);
ws.mask = (data[1] & 0x80) == 0x80; ws.mask = (data[1] & 0x80) == 0x80;
ws.N0 = (data[1] & 0x7f); ws.N0 = (data[1] & 0x7f);
ws.header_size = 2 + (ws.N0 == 126? 2 : 0) + (ws.N0 == 127? 8 : 0) + (ws.mask? 4 : 0); ws.header_size =
2 + (ws.N0 == 126 ? 2 : 0) + (ws.N0 == 127 ? 8 : 0) + (ws.mask ? 4 : 0);
if (_rxbuf.size() < ws.header_size) break; /* Need: ws.header_size - _rxbuf.size() */ if (_rxbuf.size() < ws.header_size) break; /* Need: ws.header_size - _rxbuf.size() */
if ((ws.rsv1 && !_enablePerMessageDeflate) || ws.rsv2 || ws.rsv3) if ((ws.rsv1 && !_enablePerMessageDeflate) || ws.rsv2 || ws.rsv3)
@ -533,10 +534,10 @@ namespace ix
if (ws.mask) if (ws.mask)
{ {
ws.masking_key[0] = ((uint8_t) data[i+0]) << 0; ws.masking_key[0] = ((uint8_t) data[i + 0]) << 0;
ws.masking_key[1] = ((uint8_t) data[i+1]) << 0; ws.masking_key[1] = ((uint8_t) data[i + 1]) << 0;
ws.masking_key[2] = ((uint8_t) data[i+2]) << 0; ws.masking_key[2] = ((uint8_t) data[i + 2]) << 0;
ws.masking_key[3] = ((uint8_t) data[i+3]) << 0; ws.masking_key[3] = ((uint8_t) data[i + 3]) << 0;
} }
else else
{ {
@ -546,16 +547,14 @@ namespace ix
ws.masking_key[3] = 0; ws.masking_key[3] = 0;
} }
if (_rxbuf.size() < ws.header_size+ws.N) if (_rxbuf.size() < ws.header_size + ws.N)
{ {
return; /* Need: ws.header_size+ws.N - _rxbuf.size() */ return; /* Need: ws.header_size+ws.N - _rxbuf.size() */
} }
if (!ws.fin && ( if (!ws.fin && (ws.opcode == wsheader_type::PING || ws.opcode == wsheader_type::PONG ||
ws.opcode == wsheader_type::PING ws.opcode == wsheader_type::CLOSE))
|| ws.opcode == wsheader_type::PONG {
|| ws.opcode == wsheader_type::CLOSE
)){
// Control messages should not be fragmented // Control messages should not be fragmented
close(WebSocketCloseConstants::kProtocolErrorCode, close(WebSocketCloseConstants::kProtocolErrorCode,
WebSocketCloseConstants::kProtocolErrorCodeControlMessageFragmented); WebSocketCloseConstants::kProtocolErrorCodeControlMessageFragmented);
@ -563,22 +562,19 @@ namespace ix
} }
unmaskReceiveBuffer(ws); unmaskReceiveBuffer(ws);
std::string frameData(_rxbuf.begin()+ws.header_size, std::string frameData(_rxbuf.begin() + ws.header_size,
_rxbuf.begin()+ws.header_size+(size_t) ws.N); _rxbuf.begin() + ws.header_size + (size_t) ws.N);
// We got a whole message, now do something with it: // We got a whole message, now do something with it:
if ( if (ws.opcode == wsheader_type::TEXT_FRAME ||
ws.opcode == wsheader_type::TEXT_FRAME ws.opcode == wsheader_type::BINARY_FRAME ||
|| ws.opcode == wsheader_type::BINARY_FRAME ws.opcode == wsheader_type::CONTINUATION)
|| ws.opcode == wsheader_type::CONTINUATION {
) {
if (ws.opcode != wsheader_type::CONTINUATION) if (ws.opcode != wsheader_type::CONTINUATION)
{ {
_fragmentedMessageKind = _fragmentedMessageKind = (ws.opcode == wsheader_type::TEXT_FRAME)
(ws.opcode == wsheader_type::TEXT_FRAME) ? MessageKind::MSG_TEXT
? MessageKind::MSG_TEXT : MessageKind::MSG_BINARY;
: MessageKind::MSG_BINARY;
_compressedMessage = _enablePerMessageDeflate && ws.rsv1; _compressedMessage = _enablePerMessageDeflate && ws.rsv1;
@ -592,8 +588,9 @@ namespace ix
else if (_chunks.empty()) else if (_chunks.empty())
{ {
// Continuation message need to follow a non-fin TEXT or BINARY message // Continuation message need to follow a non-fin TEXT or BINARY message
close(WebSocketCloseConstants::kProtocolErrorCode, close(
WebSocketCloseConstants::kProtocolErrorCodeContinuationOpCodeOutOfSequence); WebSocketCloseConstants::kProtocolErrorCode,
WebSocketCloseConstants::kProtocolErrorCodeContinuationOpCodeOutOfSequence);
} }
// //
@ -601,10 +598,8 @@ namespace ix
// //
if (ws.fin && _chunks.empty()) if (ws.fin && _chunks.empty())
{ {
emitMessage(_fragmentedMessageKind, emitMessage(
frameData, _fragmentedMessageKind, frameData, _compressedMessage, onMessageCallback);
_compressedMessage,
onMessageCallback);
_compressedMessage = false; _compressedMessage = false;
} }
@ -621,8 +616,10 @@ namespace ix
if (ws.fin) if (ws.fin)
{ {
emitMessage(_fragmentedMessageKind, getMergedChunks(), emitMessage(_fragmentedMessageKind,
_compressedMessage, onMessageCallback); getMergedChunks(),
_compressedMessage,
onMessageCallback);
_chunks.clear(); _chunks.clear();
_compressedMessage = false; _compressedMessage = false;
@ -668,8 +665,8 @@ namespace ix
if (ws.N >= 2) if (ws.N >= 2)
{ {
// Extract the close code first, available as the first 2 bytes // Extract the close code first, available as the first 2 bytes
code |= ((uint64_t) _rxbuf[ws.header_size]) << 8; code |= ((uint64_t) _rxbuf[ws.header_size]) << 8;
code |= ((uint64_t) _rxbuf[ws.header_size+1]) << 0; code |= ((uint64_t) _rxbuf[ws.header_size + 1]) << 0;
// Get the reason. // Get the reason.
if (ws.N > 2) if (ws.N > 2)
@ -690,13 +687,11 @@ namespace ix
// Full list of status code and status range is defined in the dedicated // Full list of status code and status range is defined in the dedicated
// RFC section at https://tools.ietf.org/html/rfc6455#page-45 // RFC section at https://tools.ietf.org/html/rfc6455#page-45
// //
if (code < 1000 || code == 1004 || code == 1006 || if (code < 1000 || code == 1004 || code == 1006 || (code > 1013 && code < 3000))
(code > 1013 && code < 3000))
{ {
// build up an error message containing the bad error code // build up an error message containing the bad error code
std::stringstream ss; std::stringstream ss;
ss << WebSocketCloseConstants::kInvalidCloseCodeMessage ss << WebSocketCloseConstants::kInvalidCloseCodeMessage << ": " << code;
<< ": " << code;
reason = ss.str(); reason = ss.str();
code = WebSocketCloseConstants::kProtocolErrorCode; code = WebSocketCloseConstants::kProtocolErrorCode;
@ -722,8 +717,8 @@ namespace ix
} }
else else
{ {
// we got the CLOSE frame answer from our close, so we can close the connection if // we got the CLOSE frame answer from our close, so we can close the connection
// the code/reason are the same // if the code/reason are the same
bool identicalReason; bool identicalReason;
{ {
std::lock_guard<std::mutex> lock(_closeDataMutex); std::lock_guard<std::mutex> lock(_closeDataMutex);
@ -746,8 +741,7 @@ namespace ix
} }
// Erase the message that has been processed from the input/read buffer // Erase the message that has been processed from the input/read buffer
_rxbuf.erase(_rxbuf.begin(), _rxbuf.erase(_rxbuf.begin(), _rxbuf.begin() + ws.header_size + (size_t) ws.N);
_rxbuf.begin() + ws.header_size + (size_t) ws.N);
} }
// if an abnormal closure was raised in poll, and nothing else triggered a CLOSED state in // if an abnormal closure was raised in poll, and nothing else triggered a CLOSED state in
@ -756,7 +750,8 @@ namespace ix
{ {
_rxbuf.clear(); _rxbuf.clear();
// if we previously closed the connection (CLOSING state), then set state to CLOSED (code/reason were set before) // if we previously closed the connection (CLOSING state), then set state to CLOSED
// (code/reason were set before)
if (_readyState == ReadyState::CLOSING) if (_readyState == ReadyState::CLOSING)
{ {
closeSocket(); closeSocket();
@ -767,7 +762,8 @@ namespace ix
{ {
closeSocketAndSwitchToClosedState(WebSocketCloseConstants::kAbnormalCloseCode, closeSocketAndSwitchToClosedState(WebSocketCloseConstants::kAbnormalCloseCode,
WebSocketCloseConstants::kAbnormalCloseMessage, WebSocketCloseConstants::kAbnormalCloseMessage,
0, false); 0,
false);
} }
} }
} }
@ -832,16 +828,14 @@ namespace ix
{ {
auto now = std::chrono::system_clock::now(); auto now = std::chrono::system_clock::now();
auto seconds = auto seconds =
std::chrono::duration_cast<std::chrono::seconds>( std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count();
now.time_since_epoch()).count();
return static_cast<unsigned>(seconds); return static_cast<unsigned>(seconds);
} }
WebSocketSendInfo WebSocketTransport::sendData( WebSocketSendInfo WebSocketTransport::sendData(wsheader_type::opcode_type type,
wsheader_type::opcode_type type, const std::string& message,
const std::string& message, bool compress,
bool compress, const OnProgressCallback& onProgressCallback)
const OnProgressCallback& onProgressCallback)
{ {
if (_readyState != ReadyState::OPEN && _readyState != ReadyState::CLOSING) if (_readyState != ReadyState::OPEN && _readyState != ReadyState::CLOSING)
{ {
@ -895,10 +889,10 @@ namespace ix
std::string::const_iterator begin = message_begin; std::string::const_iterator begin = message_begin;
std::string::const_iterator end = message_end; std::string::const_iterator end = message_end;
for (uint64_t i = 0 ; i < steps; ++i) for (uint64_t i = 0; i < steps; ++i)
{ {
bool firstStep = i == 0; bool firstStep = i == 0;
bool lastStep = (i+1) == steps; bool lastStep = (i + 1) == steps;
bool fin = lastStep; bool fin = lastStep;
end = begin + kChunkSize; end = begin + kChunkSize;
@ -916,7 +910,7 @@ namespace ix
// Send message // Send message
sendFragment(opcodeType, fin, begin, end, compress); sendFragment(opcodeType, fin, begin, end, compress);
if (onProgressCallback && !onProgressCallback((int)i, (int) steps)) if (onProgressCallback && !onProgressCallback((int) i, (int) steps))
{ {
break; break;
} }
@ -946,14 +940,13 @@ namespace ix
uint8_t masking_key[4] = {}; uint8_t masking_key[4] = {};
masking_key[0] = (x >> 24); masking_key[0] = (x >> 24);
masking_key[1] = (x >> 16) & 0xff; masking_key[1] = (x >> 16) & 0xff;
masking_key[2] = (x >> 8) & 0xff; masking_key[2] = (x >> 8) & 0xff;
masking_key[3] = (x) & 0xff; masking_key[3] = (x) &0xff;
std::vector<uint8_t> header; std::vector<uint8_t> header;
header.assign(2 + header.assign(2 + (message_size >= 126 ? 2 : 0) + (message_size >= 65536 ? 6 : 0) +
(message_size >= 126 ? 2 : 0) + (_useMask ? 4 : 0),
(message_size >= 65536 ? 6 : 0) + 0);
(_useMask ? 4 : 0), 0);
header[0] = type; header[0] = type;
// The fin bit indicate that this is the last fragment. Fin is French for end. // The fin bit indicate that this is the last fragment. Fin is French for end.
@ -1004,8 +997,8 @@ namespace ix
header[5] = (message_size >> 32) & 0xff; header[5] = (message_size >> 32) & 0xff;
header[6] = (message_size >> 24) & 0xff; header[6] = (message_size >> 24) & 0xff;
header[7] = (message_size >> 16) & 0xff; header[7] = (message_size >> 16) & 0xff;
header[8] = (message_size >> 8) & 0xff; header[8] = (message_size >> 8) & 0xff;
header[9] = (message_size >> 0) & 0xff; header[9] = (message_size >> 0) & 0xff;
if (_useMask) if (_useMask)
{ {
@ -1017,8 +1010,7 @@ namespace ix
} }
// _txbuf will keep growing until it can be transmitted over the socket: // _txbuf will keep growing until it can be transmitted over the socket:
appendToSendBuffer(header, message_begin, message_end, appendToSendBuffer(header, message_begin, message_end, message_size, masking_key);
message_size, masking_key);
// Now actually send this data // Now actually send this data
sendOnSocket(); sendOnSocket();
@ -1038,28 +1030,26 @@ namespace ix
return info; return info;
} }
WebSocketSendInfo WebSocketTransport::sendBinary( WebSocketSendInfo WebSocketTransport::sendBinary(const std::string& message,
const std::string& message, const OnProgressCallback& onProgressCallback)
const OnProgressCallback& onProgressCallback)
{ {
return sendData(wsheader_type::BINARY_FRAME, message, return sendData(
_enablePerMessageDeflate, onProgressCallback); wsheader_type::BINARY_FRAME, message, _enablePerMessageDeflate, onProgressCallback);
} }
WebSocketSendInfo WebSocketTransport::sendText( WebSocketSendInfo WebSocketTransport::sendText(const std::string& message,
const std::string& message, const OnProgressCallback& onProgressCallback)
const OnProgressCallback& onProgressCallback)
{ {
return sendData(wsheader_type::TEXT_FRAME, message, return sendData(
_enablePerMessageDeflate, onProgressCallback); wsheader_type::TEXT_FRAME, message, _enablePerMessageDeflate, onProgressCallback);
} }
ssize_t WebSocketTransport::send() ssize_t WebSocketTransport::send()
{ {
std::lock_guard<std::mutex> lock(_socketMutex); std::lock_guard<std::mutex> lock(_socketMutex);
return _socket->send((char*)&_txbuf[0], _txbuf.size()); return _socket->send((char*) &_txbuf[0], _txbuf.size());
} }
void WebSocketTransport::sendOnSocket() void WebSocketTransport::sendOnSocket()
@ -1096,7 +1086,7 @@ namespace ix
{ {
// See list of close events here: // See list of close events here:
// https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent // https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent
std::string closure{(char)(code >> 8), (char)(code & 0xff)}; std::string closure {(char) (code >> 8), (char) (code & 0xff)};
// copy reason after code // copy reason after code
closure.append(reason); closure.append(reason);
@ -1116,8 +1106,10 @@ namespace ix
_socket->close(); _socket->close();
} }
void WebSocketTransport::closeSocketAndSwitchToClosedState( void WebSocketTransport::closeSocketAndSwitchToClosedState(uint16_t code,
uint16_t code, const std::string& reason, size_t closeWireSize, bool remote) const std::string& reason,
size_t closeWireSize,
bool remote)
{ {
closeSocket(); closeSocket();
@ -1133,8 +1125,10 @@ namespace ix
_requestInitCancellation = false; _requestInitCancellation = false;
} }
void WebSocketTransport::close( void WebSocketTransport::close(uint16_t code,
uint16_t code, const std::string& reason, size_t closeWireSize, bool remote) const std::string& reason,
size_t closeWireSize,
bool remote)
{ {
_requestInitCancellation = true; _requestInitCancellation = true;

View File

@ -25,7 +25,6 @@
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <string> #include <string>
#include <vector> #include <vector>
namespace ix namespace ix

View File

@ -32,232 +32,249 @@
#include <stdlib.h> #include <stdlib.h>
// check if the scheme name is valid // check if the scheme name is valid
static bool IsSchemeValid( const std::string& SchemeName ) static bool IsSchemeValid(const std::string& SchemeName)
{ {
for ( auto c : SchemeName ) for (auto c : SchemeName)
{ {
if ( !isalpha( c ) && c != '+' && c != '-' && c != '.' ) return false; if (!isalpha(c) && c != '+' && c != '-' && c != '.') return false;
} }
return true; return true;
} }
bool LUrlParser::clParseURL::GetPort( int* OutPort ) const bool LUrlParser::clParseURL::GetPort(int* OutPort) const
{ {
if ( !IsValid() ) { return false; } if (!IsValid())
{
return false;
}
int Port = atoi( m_Port.c_str() ); int Port = atoi(m_Port.c_str());
if ( Port <= 0 || Port > 65535 ) { return false; } if (Port <= 0 || Port > 65535)
{
return false;
}
if ( OutPort ) { *OutPort = Port; } if (OutPort)
{
*OutPort = Port;
}
return true; return true;
} }
// based on RFC 1738 and RFC 3986 // based on RFC 1738 and RFC 3986
LUrlParser::clParseURL LUrlParser::clParseURL::ParseURL( const std::string& URL ) LUrlParser::clParseURL LUrlParser::clParseURL::ParseURL(const std::string& URL)
{ {
LUrlParser::clParseURL Result; LUrlParser::clParseURL Result;
const char* CurrentString = URL.c_str(); const char* CurrentString = URL.c_str();
/* /*
* <scheme>:<scheme-specific-part> * <scheme>:<scheme-specific-part>
* <scheme> := [a-z\+\-\.]+ * <scheme> := [a-z\+\-\.]+
* For resiliency, programs interpreting URLs should treat upper case letters as equivalent to lower case in scheme names * For resiliency, programs interpreting URLs should treat upper case letters as equivalent to
*/ *lower case in scheme names
*/
// try to read scheme // try to read scheme
{ {
const char* LocalString = strchr( CurrentString, ':' ); const char* LocalString = strchr(CurrentString, ':');
if ( !LocalString ) if (!LocalString)
{ {
return clParseURL( LUrlParserError_NoUrlCharacter ); return clParseURL(LUrlParserError_NoUrlCharacter);
} }
// save the scheme name // save the scheme name
Result.m_Scheme = std::string( CurrentString, LocalString - CurrentString ); Result.m_Scheme = std::string(CurrentString, LocalString - CurrentString);
if ( !IsSchemeValid( Result.m_Scheme ) ) if (!IsSchemeValid(Result.m_Scheme))
{ {
return clParseURL( LUrlParserError_InvalidSchemeName ); return clParseURL(LUrlParserError_InvalidSchemeName);
} }
// scheme should be lowercase // scheme should be lowercase
std::transform( Result.m_Scheme.begin(), Result.m_Scheme.end(), Result.m_Scheme.begin(), ::tolower ); std::transform(
Result.m_Scheme.begin(), Result.m_Scheme.end(), Result.m_Scheme.begin(), ::tolower);
// skip ':' // skip ':'
CurrentString = LocalString+1; CurrentString = LocalString + 1;
} }
/* /*
* //<user>:<password>@<host>:<port>/<url-path> * //<user>:<password>@<host>:<port>/<url-path>
* any ":", "@" and "/" must be normalized * any ":", "@" and "/" must be normalized
*/ */
// skip "//" // skip "//"
if ( *CurrentString++ != '/' ) return clParseURL( LUrlParserError_NoDoubleSlash ); if (*CurrentString++ != '/') return clParseURL(LUrlParserError_NoDoubleSlash);
if ( *CurrentString++ != '/' ) return clParseURL( LUrlParserError_NoDoubleSlash ); if (*CurrentString++ != '/') return clParseURL(LUrlParserError_NoDoubleSlash);
// check if the user name and password are specified // check if the user name and password are specified
bool bHasUserName = false; bool bHasUserName = false;
const char* LocalString = CurrentString; const char* LocalString = CurrentString;
while ( *LocalString ) while (*LocalString)
{ {
if ( *LocalString == '@' ) if (*LocalString == '@')
{ {
// user name and password are specified // user name and password are specified
bHasUserName = true; bHasUserName = true;
break; break;
} }
else if ( *LocalString == '/' ) else if (*LocalString == '/')
{ {
// end of <host>:<port> specification // end of <host>:<port> specification
bHasUserName = false; bHasUserName = false;
break; break;
} }
LocalString++; LocalString++;
} }
// user name and password // user name and password
LocalString = CurrentString; LocalString = CurrentString;
if ( bHasUserName ) if (bHasUserName)
{ {
// read user name // read user name
while ( *LocalString && *LocalString != ':' && *LocalString != '@' ) LocalString++; while (*LocalString && *LocalString != ':' && *LocalString != '@')
LocalString++;
Result.m_UserName = std::string( CurrentString, LocalString - CurrentString ); Result.m_UserName = std::string(CurrentString, LocalString - CurrentString);
// proceed with the current pointer // proceed with the current pointer
CurrentString = LocalString; CurrentString = LocalString;
if ( *CurrentString == ':' ) if (*CurrentString == ':')
{ {
// skip ':' // skip ':'
CurrentString++; CurrentString++;
// read password // read password
LocalString = CurrentString; LocalString = CurrentString;
while ( *LocalString && *LocalString != '@' ) LocalString++; while (*LocalString && *LocalString != '@')
LocalString++;
Result.m_Password = std::string( CurrentString, LocalString - CurrentString ); Result.m_Password = std::string(CurrentString, LocalString - CurrentString);
CurrentString = LocalString; CurrentString = LocalString;
} }
// skip '@' // skip '@'
if ( *CurrentString != '@' ) if (*CurrentString != '@')
{ {
return clParseURL( LUrlParserError_NoAtSign ); return clParseURL(LUrlParserError_NoAtSign);
} }
CurrentString++; CurrentString++;
} }
bool bHasBracket = ( *CurrentString == '[' ); bool bHasBracket = (*CurrentString == '[');
// go ahead, read the host name // go ahead, read the host name
LocalString = CurrentString; LocalString = CurrentString;
while ( *LocalString ) while (*LocalString)
{ {
if ( bHasBracket && *LocalString == ']' ) if (bHasBracket && *LocalString == ']')
{ {
// end of IPv6 address // end of IPv6 address
LocalString++; LocalString++;
break; break;
} }
else if ( !bHasBracket && ( *LocalString == ':' || *LocalString == '/' ) ) else if (!bHasBracket && (*LocalString == ':' || *LocalString == '/'))
{ {
// port number is specified // port number is specified
break; break;
} }
LocalString++; LocalString++;
} }
Result.m_Host = std::string( CurrentString, LocalString - CurrentString ); Result.m_Host = std::string(CurrentString, LocalString - CurrentString);
CurrentString = LocalString; CurrentString = LocalString;
// is port number specified? // is port number specified?
if ( *CurrentString == ':' ) if (*CurrentString == ':')
{ {
CurrentString++; CurrentString++;
// read port number // read port number
LocalString = CurrentString; LocalString = CurrentString;
while ( *LocalString && *LocalString != '/' ) LocalString++; while (*LocalString && *LocalString != '/')
LocalString++;
Result.m_Port = std::string( CurrentString, LocalString - CurrentString ); Result.m_Port = std::string(CurrentString, LocalString - CurrentString);
CurrentString = LocalString; CurrentString = LocalString;
} }
// end of string // end of string
if ( !*CurrentString ) if (!*CurrentString)
{ {
Result.m_ErrorCode = LUrlParserError_Ok; Result.m_ErrorCode = LUrlParserError_Ok;
return Result; return Result;
} }
// skip '/' // skip '/'
if ( *CurrentString != '/' ) if (*CurrentString != '/')
{ {
return clParseURL( LUrlParserError_NoSlash ); return clParseURL(LUrlParserError_NoSlash);
} }
CurrentString++; CurrentString++;
// parse the path // parse the path
LocalString = CurrentString; LocalString = CurrentString;
while ( *LocalString && *LocalString != '#' && *LocalString != '?' ) LocalString++; while (*LocalString && *LocalString != '#' && *LocalString != '?')
LocalString++;
Result.m_Path = std::string( CurrentString, LocalString - CurrentString ); Result.m_Path = std::string(CurrentString, LocalString - CurrentString);
CurrentString = LocalString; CurrentString = LocalString;
// check for query // check for query
if ( *CurrentString == '?' ) if (*CurrentString == '?')
{ {
// skip '?' // skip '?'
CurrentString++; CurrentString++;
// read query // read query
LocalString = CurrentString; LocalString = CurrentString;
while ( *LocalString && *LocalString != '#' ) LocalString++; while (*LocalString && *LocalString != '#')
LocalString++;
Result.m_Query = std::string( CurrentString, LocalString - CurrentString ); Result.m_Query = std::string(CurrentString, LocalString - CurrentString);
CurrentString = LocalString; CurrentString = LocalString;
} }
// check for fragment // check for fragment
if ( *CurrentString == '#' ) if (*CurrentString == '#')
{ {
// skip '#' // skip '#'
CurrentString++; CurrentString++;
// read fragment // read fragment
LocalString = CurrentString; LocalString = CurrentString;
while ( *LocalString ) LocalString++; while (*LocalString)
LocalString++;
Result.m_Fragment = std::string( CurrentString, LocalString - CurrentString ); Result.m_Fragment = std::string(CurrentString, LocalString - CurrentString);
} }
Result.m_ErrorCode = LUrlParserError_Ok; Result.m_ErrorCode = LUrlParserError_Ok;
return Result; return Result;
} }

View File

@ -62,7 +62,10 @@ namespace LUrlParser
} }
/// return 'true' if the parsing was successful /// return 'true' if the parsing was successful
bool IsValid() const { return m_ErrorCode == LUrlParserError_Ok; } bool IsValid() const
{
return m_ErrorCode == LUrlParserError_Ok;
}
/// helper to convert the port number to int, return 'true' if the port is valid (within the /// helper to convert the port number to int, return 'true' if the port is valid (within the
/// 0..65535 range) /// 0..65535 range)

View File

@ -17,4 +17,4 @@ namespace ix
// //
pthread_setname_np(name.substr(0, 63).c_str()); pthread_setname_np(name.substr(0, 63).c_str());
} }
} } // namespace ix

View File

@ -15,7 +15,6 @@ namespace ix
// See prctl and PR_SET_NAME property in // See prctl and PR_SET_NAME property in
// http://man7.org/linux/man-pages/man2/prctl.2.html // http://man7.org/linux/man-pages/man2/prctl.2.html
// //
pthread_setname_np(pthread_self(), pthread_setname_np(pthread_self(), name.substr(0, 15).c_str());
name.substr(0, 15).c_str());
} }
} } // namespace ix

View File

@ -4,13 +4,14 @@
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved. * Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
*/ */
#include "../IXSetThreadName.h" #include "../IXSetThreadName.h"
#include <Windows.h> #include <Windows.h>
namespace ix namespace ix
{ {
const DWORD MS_VC_EXCEPTION = 0x406D1388; const DWORD MS_VC_EXCEPTION = 0x406D1388;
#pragma pack(push,8) #pragma pack(push, 8)
typedef struct tagTHREADNAME_INFO typedef struct tagTHREADNAME_INFO
{ {
DWORD dwType; // Must be 0x1000. DWORD dwType; // Must be 0x1000.
@ -30,7 +31,8 @@ namespace ix
__try __try
{ {
RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)& info); RaiseException(
MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*) &info);
} }
__except (EXCEPTION_EXECUTE_HANDLER) __except (EXCEPTION_EXECUTE_HANDLER)
{ {
@ -41,4 +43,4 @@ namespace ix
{ {
SetThreadName(-1, name.c_str()); SetThreadName(-1, name.c_str());
} }
} } // namespace ix

View File

@ -4,14 +4,13 @@
* Copyright (c) 2017 Machine Zone. All rights reserved. * Copyright (c) 2017 Machine Zone. All rights reserved.
*/ */
#include <iostream> #include "IXSnakeServer.h"
#include "IXTest.h"
#include "catch.hpp"
#include <chrono> #include <chrono>
#include <iostream>
#include <ixcobra/IXCobraConnection.h> #include <ixcobra/IXCobraConnection.h>
#include <ixcrypto/IXUuid.h> #include <ixcrypto/IXUuid.h>
#include "IXTest.h"
#include "IXSnakeServer.h"
#include "catch.hpp"
using namespace ix; using namespace ix;
@ -22,67 +21,64 @@ namespace
void setupTrafficTrackerCallback() void setupTrafficTrackerCallback()
{ {
ix::CobraConnection::setTrafficTrackerCallback( ix::CobraConnection::setTrafficTrackerCallback([](size_t size, bool incoming) {
[](size_t size, bool incoming) if (incoming)
{ {
if (incoming) incomingBytes += size;
{
incomingBytes += size;
}
else
{
outgoingBytes += size;
}
} }
); else
{
outgoingBytes += size;
}
});
} }
class SatoriChat class SatoriChat
{ {
public: public:
SatoriChat(const std::string& user, SatoriChat(const std::string& user,
const std::string& session, const std::string& session,
const std::string& endpoint); const std::string& endpoint);
void subscribe(const std::string& channel); void subscribe(const std::string& channel);
void start(); void start();
void stop(); void stop();
void run(); void run();
bool isReady() const; bool isReady() const;
void sendMessage(const std::string& text); void sendMessage(const std::string& text);
size_t getReceivedMessagesCount() const; size_t getReceivedMessagesCount() const;
bool hasPendingMessages() const; bool hasPendingMessages() const;
Json::Value popMessage(); Json::Value popMessage();
private: private:
std::string _user; std::string _user;
std::string _session; std::string _session;
std::string _endpoint; std::string _endpoint;
std::queue<Json::Value> _publish_queue; std::queue<Json::Value> _publish_queue;
mutable std::mutex _queue_mutex; mutable std::mutex _queue_mutex;
std::thread _thread; std::thread _thread;
std::atomic<bool> _stop; std::atomic<bool> _stop;
ix::CobraConnection _conn; ix::CobraConnection _conn;
std::atomic<bool> _connectedAndSubscribed; std::atomic<bool> _connectedAndSubscribed;
std::queue<Json::Value> _receivedQueue; std::queue<Json::Value> _receivedQueue;
std::mutex _logMutex; std::mutex _logMutex;
}; };
SatoriChat::SatoriChat(const std::string& user, SatoriChat::SatoriChat(const std::string& user,
const std::string& session, const std::string& session,
const std::string& endpoint) : const std::string& endpoint)
_user(user), : _user(user)
_session(session), , _session(session)
_endpoint(endpoint), , _endpoint(endpoint)
_stop(false), , _stop(false)
_connectedAndSubscribed(false) , _connectedAndSubscribed(false)
{ {
} }
@ -127,35 +123,30 @@ namespace
void SatoriChat::subscribe(const std::string& channel) void SatoriChat::subscribe(const std::string& channel)
{ {
std::string filter; std::string filter;
_conn.subscribe(channel, filter, _conn.subscribe(channel, filter, [this](const Json::Value& msg) {
[this](const Json::Value& msg) std::cout << msg.toStyledString() << std::endl;
{ if (!msg.isObject()) return;
std::cout << msg.toStyledString() << std::endl; if (!msg.isMember("user")) return;
if (!msg.isObject()) return; if (!msg.isMember("text")) return;
if (!msg.isMember("user")) return; if (!msg.isMember("session")) return;
if (!msg.isMember("text")) return;
if (!msg.isMember("session")) return;
std::string msg_user = msg["user"].asString(); std::string msg_user = msg["user"].asString();
std::string msg_text = msg["text"].asString(); std::string msg_text = msg["text"].asString();
std::string msg_session = msg["session"].asString(); std::string msg_session = msg["session"].asString();
// We are not interested in messages // We are not interested in messages
// from a different session. // from a different session.
if (msg_session != _session) return; if (msg_session != _session) return;
// We are not interested in our own messages // We are not interested in our own messages
if (msg_user == _user) return; if (msg_user == _user) return;
_receivedQueue.push(msg); _receivedQueue.push(msg);
std::stringstream ss; std::stringstream ss;
ss << std::endl ss << std::endl << msg_user << " > " << msg_text << std::endl << _user << " > ";
<< msg_user << " > " << msg_text log(ss.str());
<< std::endl });
<< _user << " > ";
log(ss.str());
});
} }
void SatoriChat::sendMessage(const std::string& text) void SatoriChat::sendMessage(const std::string& text)
@ -189,50 +180,46 @@ namespace
std::string role = "_sub"; std::string role = "_sub";
std::string secret = "66B1dA3ED5fA074EB5AE84Dd8CE3b5ba"; std::string secret = "66B1dA3ED5fA074EB5AE84Dd8CE3b5ba";
_conn.configure(appkey, _endpoint, role, secret, _conn.configure(
ix::WebSocketPerMessageDeflateOptions(true)); appkey, _endpoint, role, secret, ix::WebSocketPerMessageDeflateOptions(true));
_conn.connect(); _conn.connect();
_conn.setEventCallback( _conn.setEventCallback([this, channel](ix::CobraConnectionEventType eventType,
[this, channel] const std::string& errMsg,
(ix::CobraConnectionEventType eventType, const ix::WebSocketHttpHeaders& /*headers*/,
const std::string& errMsg, const std::string& subscriptionId,
const ix::WebSocketHttpHeaders& /*headers*/, CobraConnection::MsgId msgId) {
const std::string& subscriptionId, if (eventType == ix::CobraConnection_EventType_Open)
CobraConnection::MsgId msgId)
{ {
if (eventType == ix::CobraConnection_EventType_Open) log("Subscriber connected: " + _user);
{
log("Subscriber connected: " + _user);
}
else if (eventType == ix::CobraConnection_EventType_Authenticated)
{
log("Subscriber authenticated: " + _user);
subscribe(channel);
}
else if (eventType == ix::CobraConnection_EventType_Error)
{
log(errMsg + _user);
}
else if (eventType == ix::CobraConnection_EventType_Closed)
{
log("Connection closed: " + _user);
}
else if (eventType == ix::CobraConnection_EventType_Subscribed)
{
log("Subscription ok: " + _user + " subscription_id " + subscriptionId);
_connectedAndSubscribed = true;
}
else if (eventType == ix::CobraConnection_EventType_UnSubscribed)
{
log("Unsubscription ok: " + _user + " subscription_id " + subscriptionId);
}
else if (eventType == ix::CobraConnection_EventType_Published)
{
Logger() << "Subscriber: published message acked: " << msgId;
}
} }
); else if (eventType == ix::CobraConnection_EventType_Authenticated)
{
log("Subscriber authenticated: " + _user);
subscribe(channel);
}
else if (eventType == ix::CobraConnection_EventType_Error)
{
log(errMsg + _user);
}
else if (eventType == ix::CobraConnection_EventType_Closed)
{
log("Connection closed: " + _user);
}
else if (eventType == ix::CobraConnection_EventType_Subscribed)
{
log("Subscription ok: " + _user + " subscription_id " + subscriptionId);
_connectedAndSubscribed = true;
}
else if (eventType == ix::CobraConnection_EventType_UnSubscribed)
{
log("Unsubscription ok: " + _user + " subscription_id " + subscriptionId);
}
else if (eventType == ix::CobraConnection_EventType_Published)
{
Logger() << "Subscriber: published message acked: " << msgId;
}
});
while (!_stop) while (!_stop)
{ {
@ -261,19 +248,15 @@ namespace
ix::msleep(50); ix::msleep(50);
_conn.disconnect(); _conn.disconnect();
_conn.setEventCallback([] _conn.setEventCallback([](ix::CobraConnectionEventType /*eventType*/,
(ix::CobraConnectionEventType /*eventType*/, const std::string& /*errMsg*/,
const std::string& /*errMsg*/, const ix::WebSocketHttpHeaders& /*headers*/,
const ix::WebSocketHttpHeaders& /*headers*/, const std::string& /*subscriptionId*/,
const std::string& /*subscriptionId*/, CobraConnection::MsgId /*msgId*/) { ; });
CobraConnection::MsgId /*msgId*/)
{
;
});
snakeServer.stop(); snakeServer.stop();
} }
} } // namespace
TEST_CASE("Cobra_chat", "[cobra_chat]") TEST_CASE("Cobra_chat", "[cobra_chat]")
{ {

View File

@ -3,14 +3,13 @@
* Copyright (c) 2018 Machine Zone. All rights reserved. * Copyright (c) 2018 Machine Zone. All rights reserved.
*/ */
#include <iostream>
#include <set>
#include <ixcrypto/IXUuid.h>
#include <ixcobra/IXCobraMetricsPublisher.h>
#include "IXTest.h"
#include "IXSnakeServer.h" #include "IXSnakeServer.h"
#include "IXTest.h"
#include "catch.hpp" #include "catch.hpp"
#include <iostream>
#include <ixcobra/IXCobraMetricsPublisher.h>
#include <ixcrypto/IXUuid.h>
#include <set>
using namespace ix; using namespace ix;
@ -45,70 +44,67 @@ namespace
gMessageCount = 0; gMessageCount = 0;
ix::CobraConnection conn; ix::CobraConnection conn;
conn.configure(APPKEY, endpoint, SUBSCRIBER_ROLE, SUBSCRIBER_SECRET, conn.configure(APPKEY,
endpoint,
SUBSCRIBER_ROLE,
SUBSCRIBER_SECRET,
ix::WebSocketPerMessageDeflateOptions(true)); ix::WebSocketPerMessageDeflateOptions(true));
conn.connect(); conn.connect();
conn.setEventCallback( conn.setEventCallback([&conn](ix::CobraConnectionEventType eventType,
[&conn] const std::string& errMsg,
(ix::CobraConnectionEventType eventType, const ix::WebSocketHttpHeaders& /*headers*/,
const std::string& errMsg, const std::string& subscriptionId,
const ix::WebSocketHttpHeaders& /*headers*/, CobraConnection::MsgId msgId) {
const std::string& subscriptionId, if (eventType == ix::CobraConnection_EventType_Open)
CobraConnection::MsgId msgId)
{ {
if (eventType == ix::CobraConnection_EventType_Open) Logger() << "Subscriber connected:";
{ }
Logger() << "Subscriber connected:"; if (eventType == ix::CobraConnection_EventType_Error)
} {
if (eventType == ix::CobraConnection_EventType_Error) Logger() << "Subscriber error:" << errMsg;
{ }
Logger() << "Subscriber error:" << errMsg; else if (eventType == ix::CobraConnection_EventType_Authenticated)
} {
else if (eventType == ix::CobraConnection_EventType_Authenticated) log("Subscriber authenticated");
{ std::string filter;
log("Subscriber authenticated"); conn.subscribe(CHANNEL, filter, [](const Json::Value& msg) {
std::string filter; log(msg.toStyledString());
conn.subscribe(CHANNEL, filter,
[](const Json::Value& msg)
{
log(msg.toStyledString());
std::string id = msg["id"].asString(); std::string id = msg["id"].asString();
{ {
std::lock_guard<std::mutex> guard(gProtectIds); std::lock_guard<std::mutex> guard(gProtectIds);
gIds.insert(id); gIds.insert(id);
} }
gMessageCount++; gMessageCount++;
}); });
} }
else if (eventType == ix::CobraConnection_EventType_Subscribed) else if (eventType == ix::CobraConnection_EventType_Subscribed)
{
Logger() << "Subscriber: subscribed to channel " << subscriptionId;
if (subscriptionId == CHANNEL)
{ {
Logger() << "Subscriber: subscribed to channel " << subscriptionId; gSubscriberConnectedAndSubscribed = true;
if (subscriptionId == CHANNEL)
{
gSubscriberConnectedAndSubscribed = true;
}
else
{
Logger() << "Subscriber: unexpected channel " << subscriptionId;
}
} }
else if (eventType == ix::CobraConnection_EventType_UnSubscribed) else
{ {
Logger() << "Subscriber: ununexpected from channel " << subscriptionId; Logger() << "Subscriber: unexpected channel " << subscriptionId;
if (subscriptionId != CHANNEL)
{
Logger() << "Subscriber: unexpected channel " << subscriptionId;
}
}
else if (eventType == ix::CobraConnection_EventType_Published)
{
Logger() << "Subscriber: published message acked: " << msgId;
} }
} }
); else if (eventType == ix::CobraConnection_EventType_UnSubscribed)
{
Logger() << "Subscriber: ununexpected from channel " << subscriptionId;
if (subscriptionId != CHANNEL)
{
Logger() << "Subscriber: unexpected channel " << subscriptionId;
}
}
else if (eventType == ix::CobraConnection_EventType_Published)
{
Logger() << "Subscriber: published message acked: " << msgId;
}
});
while (!gStop) while (!gStop)
{ {
@ -121,7 +117,7 @@ namespace
gUniqueMessageIdsCount = gIds.size(); gUniqueMessageIdsCount = gIds.size();
} }
} } // namespace
TEST_CASE("Cobra_Metrics_Publisher", "[cobra]") TEST_CASE("Cobra_Metrics_Publisher", "[cobra]")
{ {
@ -158,8 +154,8 @@ TEST_CASE("Cobra_Metrics_Publisher", "[cobra]")
ix::CobraMetricsPublisher cobraMetricsPublisher; ix::CobraMetricsPublisher cobraMetricsPublisher;
bool perMessageDeflate = true; bool perMessageDeflate = true;
cobraMetricsPublisher.configure(APPKEY, endpoint, CHANNEL, cobraMetricsPublisher.configure(
PUBLISHER_ROLE, PUBLISHER_SECRET, perMessageDeflate); APPKEY, endpoint, CHANNEL, PUBLISHER_ROLE, PUBLISHER_SECRET, perMessageDeflate);
cobraMetricsPublisher.setSession(uuid4()); cobraMetricsPublisher.setSession(uuid4());
cobraMetricsPublisher.enable(true); // disabled by default, needs to be enabled to be active cobraMetricsPublisher.enable(true); // disabled by default, needs to be enabled to be active
@ -189,7 +185,7 @@ TEST_CASE("Cobra_Metrics_Publisher", "[cobra]")
// (msg #6) // (msg #6)
cobraMetricsPublisher.setRateControl({ cobraMetricsPublisher.setRateControl({
{"sms_metric_C_id", 1}, // published once per minute (60 seconds) max {"sms_metric_C_id", 1}, // published once per minute (60 seconds) max
}); });
// (msg #7) // (msg #7)
cobraMetricsPublisher.push("sms_metric_C_id", data); cobraMetricsPublisher.push("sms_metric_C_id", data);
@ -205,7 +201,7 @@ TEST_CASE("Cobra_Metrics_Publisher", "[cobra]")
log("Testing suspend/resume now, which will disconnect the cobraMetricsPublisher."); log("Testing suspend/resume now, which will disconnect the cobraMetricsPublisher.");
// Test suspend + resume // Test suspend + resume
for (int i = 0 ; i < 3 ; ++i) for (int i = 0; i < 3; ++i)
{ {
cobraMetricsPublisher.suspend(); cobraMetricsPublisher.suspend();
ix::msleep(500); ix::msleep(500);
@ -214,7 +210,7 @@ TEST_CASE("Cobra_Metrics_Publisher", "[cobra]")
cobraMetricsPublisher.push("sms_metric_D_id", data); // will not be sent this time cobraMetricsPublisher.push("sms_metric_D_id", data); // will not be sent this time
cobraMetricsPublisher.resume(); cobraMetricsPublisher.resume();
ix::msleep(2000); // give cobra 2s to connect ix::msleep(2000); // give cobra 2s to connect
REQUIRE(cobraMetricsPublisher.isConnected()); // Check that we are connected now REQUIRE(cobraMetricsPublisher.isConnected()); // Check that we are connected now
cobraMetricsPublisher.push("sms_metric_E_id", data); cobraMetricsPublisher.push("sms_metric_E_id", data);

View File

@ -4,11 +4,10 @@
* Copyright (c) 2018 Machine Zone. All rights reserved. * Copyright (c) 2018 Machine Zone. All rights reserved.
*/ */
#include "catch.hpp"
#include "IXTest.h" #include "IXTest.h"
#include <ixwebsocket/IXDNSLookup.h> #include "catch.hpp"
#include <iostream> #include <iostream>
#include <ixwebsocket/IXDNSLookup.h>
using namespace ix; using namespace ix;
@ -32,7 +31,11 @@ TEST_CASE("dns", "[net]")
auto dnsLookup = std::make_shared<DNSLookup>("wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww", 80); auto dnsLookup = std::make_shared<DNSLookup>("wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww", 80);
std::string errMsg; std::string errMsg;
struct addrinfo* res = dnsLookup->resolve(errMsg, [] { return false; }); struct addrinfo* res = dnsLookup->resolve(errMsg,
[]
{
return false;
});
std::cerr << "Error message: " << errMsg << std::endl; std::cerr << "Error message: " << errMsg << std::endl;
REQUIRE(res == nullptr); REQUIRE(res == nullptr);
} }
@ -43,7 +46,11 @@ TEST_CASE("dns", "[net]")
std::string errMsg; std::string errMsg;
// The callback returning true means we are requesting cancellation // The callback returning true means we are requesting cancellation
struct addrinfo* res = dnsLookup->resolve(errMsg, [] { return true; }); struct addrinfo* res = dnsLookup->resolve(errMsg,
[]
{
return true;
});
std::cerr << "Error message: " << errMsg << std::endl; std::cerr << "Error message: " << errMsg << std::endl;
REQUIRE(res == nullptr); REQUIRE(res == nullptr);
} }

View File

@ -5,11 +5,11 @@
*/ */
#include "IXGetFreePort.h" #include "IXGetFreePort.h"
#include <ixwebsocket/IXNetSystem.h> #include <ixwebsocket/IXNetSystem.h>
#include <ixwebsocket/IXSocket.h> #include <ixwebsocket/IXSocket.h>
#include <string>
#include <random> #include <random>
#include <string>
namespace ix namespace ix
{ {
@ -30,8 +30,7 @@ namespace ix
} }
int enable = 1; int enable = 1;
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char*) &enable, sizeof(enable)) < 0)
(char*) &enable, sizeof(enable)) < 0)
{ {
return getAnyFreePortRandom(); return getAnyFreePortRandom();
} }
@ -39,10 +38,10 @@ namespace ix
// Bind to port 0. This is the standard way to get a free port. // Bind to port 0. This is the standard way to get a free port.
struct sockaddr_in server; // server address information struct sockaddr_in server; // server address information
server.sin_family = AF_INET; server.sin_family = AF_INET;
server.sin_port = htons(0); server.sin_port = htons(0);
server.sin_addr.s_addr = inet_addr("127.0.0.1"); server.sin_addr.s_addr = inet_addr("127.0.0.1");
if (bind(sockfd, (struct sockaddr *)&server, sizeof(server)) < 0) if (bind(sockfd, (struct sockaddr*) &server, sizeof(server)) < 0)
{ {
Socket::closeSocket(sockfd); Socket::closeSocket(sockfd);
return getAnyFreePortRandom(); return getAnyFreePortRandom();
@ -50,7 +49,7 @@ namespace ix
struct sockaddr_in sa; // server address information struct sockaddr_in sa; // server address information
socklen_t len = sizeof(sa); socklen_t len = sizeof(sa);
if (getsockname(sockfd, (struct sockaddr *) &sa, &len) < 0) if (getsockname(sockfd, (struct sockaddr*) &sa, &len) < 0)
{ {
Socket::closeSocket(sockfd); Socket::closeSocket(sockfd);
return getAnyFreePortRandom(); return getAnyFreePortRandom();
@ -67,11 +66,11 @@ namespace ix
while (true) while (true)
{ {
#if defined(__has_feature) #if defined(__has_feature)
# if __has_feature(address_sanitizer) #if __has_feature(address_sanitizer)
int port = getAnyFreePortRandom(); int port = getAnyFreePortRandom();
# else #else
int port = getAnyFreePort(); int port = getAnyFreePort();
# endif #endif
#else #else
int port = getAnyFreePort(); int port = getAnyFreePort();
#endif #endif

View File

@ -4,11 +4,10 @@
* Copyright (c) 2019 Machine Zone. All rights reserved. * Copyright (c) 2019 Machine Zone. All rights reserved.
*/ */
#include "catch.hpp"
#include <iostream> #include <iostream>
#include <ixwebsocket/IXHttpClient.h> #include <ixwebsocket/IXHttpClient.h>
#include "catch.hpp"
using namespace ix; using namespace ix;
TEST_CASE("http client", "[http]") TEST_CASE("http client", "[http]")
@ -28,14 +27,10 @@ TEST_CASE("http client", "[http]")
args->maxRedirects = 10; args->maxRedirects = 10;
args->verbose = true; args->verbose = true;
args->compress = true; args->compress = true;
args->logger = [](const std::string& msg) args->logger = [](const std::string& msg) { std::cout << msg; };
{ args->onProgressCallback = [](int current, int total) -> bool {
std::cout << msg; std::cerr << "\r"
}; << "Downloaded " << current << " bytes out of " << total;
args->onProgressCallback = [](int current, int total) -> bool
{
std::cerr << "\r" << "Downloaded "
<< current << " bytes out of " << total;
return true; return true;
}; };
@ -70,14 +65,10 @@ TEST_CASE("http client", "[http]")
args->maxRedirects = 10; args->maxRedirects = 10;
args->verbose = true; args->verbose = true;
args->compress = true; args->compress = true;
args->logger = [](const std::string& msg) args->logger = [](const std::string& msg) { std::cout << msg; };
{ args->onProgressCallback = [](int current, int total) -> bool {
std::cout << msg; std::cerr << "\r"
}; << "Downloaded " << current << " bytes out of " << total;
args->onProgressCallback = [](int current, int total) -> bool
{
std::cerr << "\r" << "Downloaded "
<< current << " bytes out of " << total;
return true; return true;
}; };
@ -113,23 +104,18 @@ TEST_CASE("http client", "[http]")
args->maxRedirects = 10; args->maxRedirects = 10;
args->verbose = true; args->verbose = true;
args->compress = true; args->compress = true;
args->logger = [](const std::string& msg) args->logger = [](const std::string& msg) { std::cout << msg; };
{ args->onProgressCallback = [](int current, int total) -> bool {
std::cout << msg; std::cerr << "\r"
}; << "Downloaded " << current << " bytes out of " << total;
args->onProgressCallback = [](int current, int total) -> bool
{
std::cerr << "\r" << "Downloaded "
<< current << " bytes out of " << total;
return true; return true;
}; };
std::atomic<bool> requestCompleted(false); std::atomic<bool> requestCompleted(false);
std::atomic<int> statusCode(0); std::atomic<int> statusCode(0);
httpClient.performRequest(args, [&requestCompleted, &statusCode] httpClient.performRequest(
(const HttpResponsePtr& response) args, [&requestCompleted, &statusCode](const HttpResponsePtr& response) {
{
std::cerr << "Upload size: " << response->uploadSize << std::endl; std::cerr << "Upload size: " << response->uploadSize << std::endl;
std::cerr << "Download size: " << response->downloadSize << std::endl; std::cerr << "Download size: " << response->downloadSize << std::endl;
std::cerr << "Status: " << response->statusCode << std::endl; std::cerr << "Status: " << response->statusCode << std::endl;
@ -138,8 +124,7 @@ TEST_CASE("http client", "[http]")
// In case of failure, print response->errorMsg // In case of failure, print response->errorMsg
statusCode = response->statusCode; statusCode = response->statusCode;
requestCompleted = true; requestCompleted = true;
} });
);
int wait = 0; int wait = 0;
while (wait < 5000) while (wait < 5000)
@ -171,14 +156,10 @@ TEST_CASE("http client", "[http]")
args->maxRedirects = 10; args->maxRedirects = 10;
args->verbose = true; args->verbose = true;
args->compress = true; args->compress = true;
args->logger = [](const std::string& msg) args->logger = [](const std::string& msg) { std::cout << msg; };
{ args->onProgressCallback = [](int current, int total) -> bool {
std::cout << msg; std::cerr << "\r"
}; << "Downloaded " << current << " bytes out of " << total;
args->onProgressCallback = [](int current, int total) -> bool
{
std::cerr << "\r" << "Downloaded "
<< current << " bytes out of " << total;
return true; return true;
}; };
@ -189,9 +170,10 @@ TEST_CASE("http client", "[http]")
for (int i = 0; i < 3; ++i) for (int i = 0; i < 3; ++i)
{ {
httpClient.performRequest(args, [i, &requestCompleted, &statusCode0, &statusCode1, &statusCode2] httpClient.performRequest(
(const HttpResponsePtr& response) args,
{ [i, &requestCompleted, &statusCode0, &statusCode1, &statusCode2](
const HttpResponsePtr& response) {
std::cerr << "Upload size: " << response->uploadSize << std::endl; std::cerr << "Upload size: " << response->uploadSize << std::endl;
std::cerr << "Download size: " << response->downloadSize << std::endl; std::cerr << "Download size: " << response->downloadSize << std::endl;
std::cerr << "Status: " << response->statusCode << std::endl; std::cerr << "Status: " << response->statusCode << std::endl;
@ -211,8 +193,7 @@ TEST_CASE("http client", "[http]")
statusCode2 = response->statusCode; statusCode2 = response->statusCode;
requestCompleted = true; requestCompleted = true;
} }
} });
);
} }
int wait = 0; int wait = 0;

View File

@ -4,12 +4,11 @@
* Copyright (c) 2019 Machine Zone. All rights reserved. * Copyright (c) 2019 Machine Zone. All rights reserved.
*/ */
#include "IXGetFreePort.h"
#include "catch.hpp"
#include <iostream> #include <iostream>
#include <ixwebsocket/IXHttpClient.h> #include <ixwebsocket/IXHttpClient.h>
#include <ixwebsocket/IXHttpServer.h> #include <ixwebsocket/IXHttpServer.h>
#include "IXGetFreePort.h"
#include "catch.hpp"
using namespace ix; using namespace ix;
@ -39,14 +38,10 @@ TEST_CASE("http server", "[httpd]")
args->maxRedirects = 10; args->maxRedirects = 10;
args->verbose = true; args->verbose = true;
args->compress = true; args->compress = true;
args->logger = [](const std::string& msg) args->logger = [](const std::string& msg) { std::cout << msg; };
{ args->onProgressCallback = [](int current, int total) -> bool {
std::cout << msg; std::cerr << "\r"
}; << "Downloaded " << current << " bytes out of " << total;
args->onProgressCallback = [](int current, int total) -> bool
{
std::cerr << "\r" << "Downloaded "
<< current << " bytes out of " << total;
return true; return true;
}; };

View File

@ -4,52 +4,50 @@
* Copyright (c) 2019 Machine Zone. All rights reserved. * Copyright (c) 2019 Machine Zone. All rights reserved.
*/ */
#include "catch.hpp"
#include <iostream> #include <iostream>
#include <ixwebsocket/IXHttp.h> #include <ixwebsocket/IXHttp.h>
#include "catch.hpp"
#include <string.h> #include <string.h>
namespace ix namespace ix
{ {
TEST_CASE("http", "[http]")
TEST_CASE("http", "[http]")
{
SECTION("Normal case")
{ {
std::string line = "HTTP/1.1 200"; SECTION("Normal case")
auto result = Http::parseStatusLine(line); {
std::string line = "HTTP/1.1 200";
auto result = Http::parseStatusLine(line);
REQUIRE(result.first == "HTTP/1.1"); REQUIRE(result.first == "HTTP/1.1");
REQUIRE(result.second == 200); REQUIRE(result.second == 200);
}
SECTION("http/1.0 case")
{
std::string line = "HTTP/1.0 200";
auto result = Http::parseStatusLine(line);
REQUIRE(result.first == "HTTP/1.0");
REQUIRE(result.second == 200);
}
SECTION("empty case")
{
std::string line = "";
auto result = Http::parseStatusLine(line);
REQUIRE(result.first == "");
REQUIRE(result.second == -1);
}
SECTION("empty case")
{
std::string line = "HTTP/1.1";
auto result = Http::parseStatusLine(line);
REQUIRE(result.first == "HTTP/1.1");
REQUIRE(result.second == -1);
}
} }
SECTION("http/1.0 case") } // namespace ix
{
std::string line = "HTTP/1.0 200";
auto result = Http::parseStatusLine(line);
REQUIRE(result.first == "HTTP/1.0");
REQUIRE(result.second == 200);
}
SECTION("empty case")
{
std::string line = "";
auto result = Http::parseStatusLine(line);
REQUIRE(result.first == "");
REQUIRE(result.second == -1);
}
SECTION("empty case")
{
std::string line = "HTTP/1.1";
auto result = Http::parseStatusLine(line);
REQUIRE(result.first == "HTTP/1.1");
REQUIRE(result.second == -1);
}
}
}

View File

@ -4,11 +4,10 @@
* Copyright (c) 2018 Machine Zone. All rights reserved. * Copyright (c) 2018 Machine Zone. All rights reserved.
*/ */
#include "catch.hpp"
#include "IXTest.h" #include "IXTest.h"
#include <ixwebsocket/IXSocketConnect.h> #include "catch.hpp"
#include <iostream> #include <iostream>
#include <ixwebsocket/IXSocketConnect.h>
using namespace ix; using namespace ix;

View File

@ -4,13 +4,12 @@
* Copyright (c) 2019 Machine Zone. All rights reserved. * Copyright (c) 2019 Machine Zone. All rights reserved.
*/ */
#include <iostream>
#include <ixwebsocket/IXSocketFactory.h>
#include <ixwebsocket/IXSocket.h>
#include <ixwebsocket/IXCancellationRequest.h>
#include "IXTest.h" #include "IXTest.h"
#include "catch.hpp" #include "catch.hpp"
#include <iostream>
#include <ixwebsocket/IXCancellationRequest.h>
#include <ixwebsocket/IXSocket.h>
#include <ixwebsocket/IXSocketFactory.h>
#include <string.h> #include <string.h>
using namespace ix; using namespace ix;
@ -33,8 +32,7 @@ namespace ix
Logger() << "errMsg: " << errMsg; Logger() << "errMsg: " << errMsg;
REQUIRE(success); REQUIRE(success);
Logger() << "Sending request: " << request Logger() << "Sending request: " << request << "to " << host << ":" << port;
<< "to " << host << ":" << port;
REQUIRE(socket->writeBytes(request, isCancellationRequested)); REQUIRE(socket->writeBytes(request, isCancellationRequested));
auto lineResult = socket->readLine(isCancellationRequested); auto lineResult = socket->readLine(isCancellationRequested);
@ -49,11 +47,12 @@ namespace ix
REQUIRE(sscanf(line.c_str(), "HTTP/1.1 %d", &status) == 1); REQUIRE(sscanf(line.c_str(), "HTTP/1.1 %d", &status) == 1);
REQUIRE(status == expectedStatus); REQUIRE(status == expectedStatus);
} }
} } // namespace ix
TEST_CASE("socket", "[socket]") TEST_CASE("socket", "[socket]")
{ {
SECTION("Connect to a local websocket server over a free port. Send GET request without header. Should return 400") SECTION("Connect to a local websocket server over a free port. Send GET request without "
"header. Should return 400")
{ {
// Start a server first which we'll hit with our socket code // Start a server first which we'll hit with our socket code
int port = getFreePort(); int port = getFreePort();
@ -78,7 +77,8 @@ TEST_CASE("socket", "[socket]")
} }
#if defined(IXWEBSOCKET_USE_TLS) #if defined(IXWEBSOCKET_USE_TLS)
SECTION("Connect to google HTTPS server over port 443. Send GET request without header. Should return 200") SECTION("Connect to google HTTPS server over port 443. Send GET request without header. Should "
"return 200")
{ {
std::string errMsg; std::string errMsg;
bool tls = true; bool tls = true;

View File

@ -5,19 +5,19 @@
*/ */
#include "IXTest.h" #include "IXTest.h"
#include <ixwebsocket/IXWebSocket.h>
#include <ixwebsocket/IXNetSystem.h>
#include <chrono> #include <chrono>
#include <thread>
#include <mutex>
#include <string>
#include <fstream> #include <fstream>
#include <iostream>
#include <stdlib.h>
#include <stack>
#include <iomanip> #include <iomanip>
#include <iostream>
#include <ixwebsocket/IXNetSystem.h>
#include <ixwebsocket/IXWebSocket.h>
#include <mutex>
#include <random> #include <random>
#include <stack>
#include <stdlib.h>
#include <string>
#include <thread>
namespace ix namespace ix
@ -29,19 +29,16 @@ namespace ix
void setupWebSocketTrafficTrackerCallback() void setupWebSocketTrafficTrackerCallback()
{ {
ix::WebSocket::setTrafficTrackerCallback( ix::WebSocket::setTrafficTrackerCallback([](size_t size, bool incoming) {
[](size_t size, bool incoming) if (incoming)
{ {
if (incoming) incomingBytes += size;
{
incomingBytes += size;
}
else
{
outgoingBytes += size;
}
} }
); else
{
outgoingBytes += size;
}
});
} }
void reportWebSocketTraffic() void reportWebSocketTraffic()
@ -61,8 +58,7 @@ namespace ix
{ {
auto now = std::chrono::system_clock::now(); auto now = std::chrono::system_clock::now();
auto seconds = auto seconds =
std::chrono::duration_cast<std::chrono::seconds>( std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count();
now.time_since_epoch()).count();
return std::to_string(seconds); return std::to_string(seconds);
} }
@ -72,18 +68,15 @@ namespace ix
Logger() << msg; Logger() << msg;
} }
void hexDump(const std::string& prefix, void hexDump(const std::string& prefix, const std::string& s)
const std::string& s)
{ {
std::ostringstream ss; std::ostringstream ss;
bool upper_case = false; bool upper_case = false;
for (std::string::size_type i = 0; i < s.length(); ++i) for (std::string::size_type i = 0; i < s.length(); ++i)
{ {
ss << std::hex ss << std::hex << std::setfill('0') << std::setw(2)
<< std::setfill('0') << (upper_case ? std::uppercase : std::nouppercase) << (int) s[i];
<< std::setw(2)
<< (upper_case ? std::uppercase : std::nouppercase) << (int)s[i];
} }
std::cout << prefix << ": " << s << " => " << ss.str() << std::endl; std::cout << prefix << ": " << s << " => " << ss.str() << std::endl;
@ -91,41 +84,36 @@ namespace ix
bool startWebSocketEchoServer(ix::WebSocketServer& server) bool startWebSocketEchoServer(ix::WebSocketServer& server)
{ {
server.setOnConnectionCallback( server.setOnConnectionCallback([&server](std::shared_ptr<ix::WebSocket> webSocket,
[&server](std::shared_ptr<ix::WebSocket> webSocket, std::shared_ptr<ConnectionState> connectionState) {
std::shared_ptr<ConnectionState> connectionState) webSocket->setOnMessageCallback(
{ [webSocket, connectionState, &server](const ix::WebSocketMessagePtr& msg) {
webSocket->setOnMessageCallback( if (msg->type == ix::WebSocketMessageType::Open)
[webSocket, connectionState, &server](const ix::WebSocketMessagePtr& msg)
{ {
if (msg->type == ix::WebSocketMessageType::Open) Logger() << "New connection";
Logger() << "Uri: " << msg->openInfo.uri;
Logger() << "Headers:";
for (auto it : msg->openInfo.headers)
{ {
Logger() << "New connection"; Logger() << it.first << ": " << it.second;
Logger() << "Uri: " << msg->openInfo.uri;
Logger() << "Headers:";
for (auto it : msg->openInfo.headers)
{
Logger() << it.first << ": " << it.second;
}
} }
else if (msg->type == ix::WebSocketMessageType::Close) }
else if (msg->type == ix::WebSocketMessageType::Close)
{
Logger() << "Closed connection";
}
else if (msg->type == ix::WebSocketMessageType::Message)
{
for (auto&& client : server.getClients())
{ {
Logger() << "Closed connection"; if (client != webSocket)
}
else if (msg->type == ix::WebSocketMessageType::Message)
{
for (auto&& client : server.getClients())
{ {
if (client != webSocket) client->send(msg->str, msg->binary);
{
client->send(msg->str, msg->binary);
}
} }
} }
} }
); });
} });
);
auto res = server.listen(); auto res = server.listen();
if (!res.first) if (!res.first)
@ -150,7 +138,7 @@ namespace ix
file.seekg(0, file.beg); file.seekg(0, file.beg);
memblock.resize((size_t) size); memblock.resize((size_t) size);
file.read((char*)&memblock.front(), static_cast<std::streamsize>(size)); file.read((char*) &memblock.front(), static_cast<std::streamsize>(size));
return memblock; return memblock;
} }
@ -190,4 +178,4 @@ namespace ix
return appConfig; return appConfig;
} }
} } // namespace ix

View File

@ -6,10 +6,10 @@
#pragma once #pragma once
#include "IXAppConfig.h"
#include "IXGetFreePort.h" #include "IXGetFreePort.h"
#include <iostream> #include <iostream>
#include <ixwebsocket/IXWebSocketServer.h> #include <ixwebsocket/IXWebSocketServer.h>
#include "IXAppConfig.h"
#include <mutex> #include <mutex>
#include <spdlog/spdlog.h> #include <spdlog/spdlog.h>
#include <sstream> #include <sstream>

View File

@ -4,6 +4,7 @@
* Copyright (c) 2019 Machine Zone. All rights reserved. * Copyright (c) 2019 Machine Zone. All rights reserved.
*/ */
#include "catch.hpp"
#include <ixwebsocket/IXCancellationRequest.h> #include <ixwebsocket/IXCancellationRequest.h>
#include <ixwebsocket/IXConnectionState.h> #include <ixwebsocket/IXConnectionState.h>
#include <ixwebsocket/IXDNSLookup.h> #include <ixwebsocket/IXDNSLookup.h>
@ -39,8 +40,6 @@
#include <ixwebsocket/LUrlParser.h> #include <ixwebsocket/LUrlParser.h>
#include <ixwebsocket/libwshandshake.hpp> #include <ixwebsocket/libwshandshake.hpp>
#include "catch.hpp"
using namespace ix; using namespace ix;
TEST_CASE("unity build", "[unity_build]") TEST_CASE("unity build", "[unity_build]")

View File

@ -4,105 +4,115 @@
* Copyright (c) 2019 Machine Zone. All rights reserved. * Copyright (c) 2019 Machine Zone. All rights reserved.
*/ */
#include <iostream>
#include <ixwebsocket/IXUrlParser.h>
#include "IXTest.h" #include "IXTest.h"
#include "catch.hpp" #include "catch.hpp"
#include <iostream>
#include <ixwebsocket/IXUrlParser.h>
#include <string.h> #include <string.h>
using namespace ix; using namespace ix;
namespace ix namespace ix
{ {
TEST_CASE("urlParser", "[urlParser]")
TEST_CASE("urlParser", "[urlParser]")
{
SECTION("http://google.com")
{ {
std::string url = "http://google.com"; SECTION("http://google.com")
std::string protocol, host, path, query; {
int port; std::string url = "http://google.com";
bool res; std::string protocol, host, path, query;
int port;
bool res;
res = UrlParser::parse(url, protocol, host, path, query, port); res = UrlParser::parse(url, protocol, host, path, query, port);
REQUIRE(res); REQUIRE(res);
REQUIRE(protocol == "http"); REQUIRE(protocol == "http");
REQUIRE(host == "google.com"); REQUIRE(host == "google.com");
REQUIRE(path == "/"); REQUIRE(path == "/");
REQUIRE(query == ""); REQUIRE(query == "");
REQUIRE(port == 80); // default port for http REQUIRE(port == 80); // default port for http
}
SECTION("https://google.com")
{
std::string url = "https://google.com";
std::string protocol, host, path, query;
int port;
bool res;
res = UrlParser::parse(url, protocol, host, path, query, port);
REQUIRE(res);
REQUIRE(protocol == "https");
REQUIRE(host == "google.com");
REQUIRE(path == "/");
REQUIRE(query == "");
REQUIRE(port == 443); // default port for https
}
SECTION("ws://google.com")
{
std::string url = "ws://google.com";
std::string protocol, host, path, query;
int port;
bool res;
res = UrlParser::parse(url, protocol, host, path, query, port);
REQUIRE(res);
REQUIRE(protocol == "ws");
REQUIRE(host == "google.com");
REQUIRE(path == "/");
REQUIRE(query == "");
REQUIRE(port == 80); // default port for ws
}
SECTION("wss://google.com/ws?arg=value")
{
std::string url = "wss://google.com/ws?arg=value&arg2=value2";
std::string protocol, host, path, query;
int port;
bool res;
res = UrlParser::parse(url, protocol, host, path, query, port);
REQUIRE(res);
REQUIRE(protocol == "wss");
REQUIRE(host == "google.com");
REQUIRE(path == "/ws?arg=value&arg2=value2");
REQUIRE(query == "arg=value&arg2=value2");
REQUIRE(port == 443); // default port for wss
}
SECTION("real test")
{
std::string url =
"ws://127.0.0.1:7350/"
"ws?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9."
"eyJleHAiOjE1NTcxNzAwNzIsInVpZCI6ImMwZmZjOGE1LTk4OTktNDAwYi1hNGU5LTJjNWM3NjFmNWQxZi"
"IsInVzbiI6InN2YmhOdlNJSmEifQ.5L8BUbpTA4XAHlSrdwhIVlrlIpRtjExepim7Yh5eEO4&status="
"true&format=protobuf";
std::string protocol, host, path, query;
int port;
bool res;
res = UrlParser::parse(url, protocol, host, path, query, port);
REQUIRE(res);
REQUIRE(protocol == "ws");
REQUIRE(host == "127.0.0.1");
REQUIRE(path ==
"/ws?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9."
"eyJleHAiOjE1NTcxNzAwNzIsInVpZCI6ImMwZmZjOGE1LTk4OTktNDAwYi1hNGU5LTJjNWM3NjFmNW"
"QxZiIsInVzbiI6InN2YmhOdlNJSmEifQ.5L8BUbpTA4XAHlSrdwhIVlrlIpRtjExepim7Yh5eEO4&"
"status=true&format=protobuf");
REQUIRE(query ==
"token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9."
"eyJleHAiOjE1NTcxNzAwNzIsInVpZCI6ImMwZmZjOGE1LTk4OTktNDAwYi1hNGU5LTJjNWM3NjFmNW"
"QxZiIsInVzbiI6InN2YmhOdlNJSmEifQ.5L8BUbpTA4XAHlSrdwhIVlrlIpRtjExepim7Yh5eEO4&"
"status=true&format=protobuf");
REQUIRE(port == 7350);
}
} }
SECTION("https://google.com") } // namespace ix
{
std::string url = "https://google.com";
std::string protocol, host, path, query;
int port;
bool res;
res = UrlParser::parse(url, protocol, host, path, query, port);
REQUIRE(res);
REQUIRE(protocol == "https");
REQUIRE(host == "google.com");
REQUIRE(path == "/");
REQUIRE(query == "");
REQUIRE(port == 443); // default port for https
}
SECTION("ws://google.com")
{
std::string url = "ws://google.com";
std::string protocol, host, path, query;
int port;
bool res;
res = UrlParser::parse(url, protocol, host, path, query, port);
REQUIRE(res);
REQUIRE(protocol == "ws");
REQUIRE(host == "google.com");
REQUIRE(path == "/");
REQUIRE(query == "");
REQUIRE(port == 80); // default port for ws
}
SECTION("wss://google.com/ws?arg=value")
{
std::string url = "wss://google.com/ws?arg=value&arg2=value2";
std::string protocol, host, path, query;
int port;
bool res;
res = UrlParser::parse(url, protocol, host, path, query, port);
REQUIRE(res);
REQUIRE(protocol == "wss");
REQUIRE(host == "google.com");
REQUIRE(path == "/ws?arg=value&arg2=value2");
REQUIRE(query == "arg=value&arg2=value2");
REQUIRE(port == 443); // default port for wss
}
SECTION("real test")
{
std::string url = "ws://127.0.0.1:7350/ws?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NTcxNzAwNzIsInVpZCI6ImMwZmZjOGE1LTk4OTktNDAwYi1hNGU5LTJjNWM3NjFmNWQxZiIsInVzbiI6InN2YmhOdlNJSmEifQ.5L8BUbpTA4XAHlSrdwhIVlrlIpRtjExepim7Yh5eEO4&status=true&format=protobuf";
std::string protocol, host, path, query;
int port;
bool res;
res = UrlParser::parse(url, protocol, host, path, query, port);
REQUIRE(res);
REQUIRE(protocol == "ws");
REQUIRE(host == "127.0.0.1");
REQUIRE(path == "/ws?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NTcxNzAwNzIsInVpZCI6ImMwZmZjOGE1LTk4OTktNDAwYi1hNGU5LTJjNWM3NjFmNWQxZiIsInVzbiI6InN2YmhOdlNJSmEifQ.5L8BUbpTA4XAHlSrdwhIVlrlIpRtjExepim7Yh5eEO4&status=true&format=protobuf");
REQUIRE(query == "token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NTcxNzAwNzIsInVpZCI6ImMwZmZjOGE1LTk4OTktNDAwYi1hNGU5LTJjNWM3NjFmNWQxZiIsInVzbiI6InN2YmhOdlNJSmEifQ.5L8BUbpTA4XAHlSrdwhIVlrlIpRtjExepim7Yh5eEO4&status=true&format=protobuf");
REQUIRE(port == 7350);
}
}
}

View File

@ -9,17 +9,15 @@
// websocket_chat_server/broacast-server.js // websocket_chat_server/broacast-server.js
// //
#include "IXTest.h"
#include "catch.hpp"
#include "msgpack11.hpp"
#include <iostream> #include <iostream>
#include <sstream>
#include <vector>
#include <mutex>
#include <ixwebsocket/IXWebSocket.h> #include <ixwebsocket/IXWebSocket.h>
#include <ixwebsocket/IXWebSocketServer.h> #include <ixwebsocket/IXWebSocketServer.h>
#include "msgpack11.hpp" #include <mutex>
#include <sstream>
#include "IXTest.h" #include <vector>
#include "catch.hpp"
using msgpack11::MsgPack; using msgpack11::MsgPack;
using namespace ix; using namespace ix;
@ -28,41 +26,37 @@ namespace
{ {
class WebSocketChat class WebSocketChat
{ {
public: public:
WebSocketChat(const std::string& user, WebSocketChat(const std::string& user, const std::string& session, int port);
const std::string& session,
int port);
void subscribe(const std::string& channel); void subscribe(const std::string& channel);
void start(); void start();
void stop(); void stop();
bool isReady() const; bool isReady() const;
void sendMessage(const std::string& text); void sendMessage(const std::string& text);
size_t getReceivedMessagesCount() const; size_t getReceivedMessagesCount() const;
const std::vector<std::string>& getReceivedMessages() const; const std::vector<std::string>& getReceivedMessages() const;
std::string encodeMessage(const std::string& text); std::string encodeMessage(const std::string& text);
std::pair<std::string, std::string> decodeMessage(const std::string& str); std::pair<std::string, std::string> decodeMessage(const std::string& str);
void appendMessage(const std::string& message); void appendMessage(const std::string& message);
private: private:
std::string _user; std::string _user;
std::string _session; std::string _session;
int _port; int _port;
ix::WebSocket _webSocket; ix::WebSocket _webSocket;
std::vector<std::string> _receivedMessages; std::vector<std::string> _receivedMessages;
mutable std::mutex _mutex; mutable std::mutex _mutex;
}; };
WebSocketChat::WebSocketChat(const std::string& user, WebSocketChat::WebSocketChat(const std::string& user, const std::string& session, int port)
const std::string& session, : _user(user)
int port) : , _session(session)
_user(user), , _port(port)
_session(session),
_port(port)
{ {
; ;
} }
@ -100,10 +94,7 @@ namespace
std::string url; std::string url;
{ {
std::stringstream ss; std::stringstream ss;
ss << "ws://127.0.0.1:" ss << "ws://127.0.0.1:" << _port << "/" << _user;
<< _port
<< "/"
<< _user;
url = ss.str(); url = ss.str();
} }
@ -113,70 +104,61 @@ namespace
std::stringstream ss; std::stringstream ss;
log(std::string("Connecting to url: ") + url); log(std::string("Connecting to url: ") + url);
_webSocket.setOnMessageCallback( _webSocket.setOnMessageCallback([this](const ix::WebSocketMessagePtr& msg) {
[this](const ix::WebSocketMessagePtr& msg) std::stringstream ss;
if (msg->type == ix::WebSocketMessageType::Open)
{ {
std::stringstream ss; ss << "cmd_websocket_chat: user " << _user << " Connected !";
if (msg->type == ix::WebSocketMessageType::Open) log(ss.str());
{ }
ss << "cmd_websocket_chat: user " else if (msg->type == ix::WebSocketMessageType::Close)
<< _user {
<< " Connected !"; ss << "cmd_websocket_chat: user " << _user << " disconnected !";
log(ss.str()); log(ss.str());
} }
else if (msg->type == ix::WebSocketMessageType::Close) else if (msg->type == ix::WebSocketMessageType::Message)
{ {
ss << "cmd_websocket_chat: user " auto result = decodeMessage(msg->str);
<< _user
<< " disconnected !";
log(ss.str());
}
else if (msg->type == ix::WebSocketMessageType::Message)
{
auto result = decodeMessage(msg->str);
// Our "chat" / "broacast" node.js server does not send us // Our "chat" / "broacast" node.js server does not send us
// the messages we send, so we don't need to have a msg_user != user // the messages we send, so we don't need to have a msg_user != user
// as we do for the satori chat example. // as we do for the satori chat example.
// store text // store text
appendMessage(result.second); appendMessage(result.second);
std::string payload = result.second; std::string payload = result.second;
if (payload.size() > 2000) if (payload.size() > 2000)
{ {
payload = "<message too large>"; payload = "<message too large>";
} }
ss << std::endl ss << std::endl << result.first << " > " << payload << std::endl << _user << " > ";
<< result.first << " > " << payload log(ss.str());
<< std::endl }
<< _user << " > "; else if (msg->type == ix::WebSocketMessageType::Error)
log(ss.str()); {
} ss << "cmd_websocket_chat: Error ! " << msg->errorInfo.reason;
else if (msg->type == ix::WebSocketMessageType::Error) log(ss.str());
{ }
ss << "cmd_websocket_chat: Error ! " << msg->errorInfo.reason; else if (msg->type == ix::WebSocketMessageType::Ping)
log(ss.str()); {
} log("cmd_websocket_chat: received ping message");
else if (msg->type == ix::WebSocketMessageType::Ping) }
{ else if (msg->type == ix::WebSocketMessageType::Pong)
log("cmd_websocket_chat: received ping message"); {
} log("cmd_websocket_chat: received pong message");
else if (msg->type == ix::WebSocketMessageType::Pong) }
{ else if (msg->type == ix::WebSocketMessageType::Fragment)
log("cmd_websocket_chat: received pong message"); {
} log("cmd_websocket_chat: received message fragment");
else if (msg->type == ix::WebSocketMessageType::Fragment) }
{ else
log("cmd_websocket_chat: received message fragment"); {
} ss << "Unexpected ix::WebSocketMessageType";
else log(ss.str());
{ }
ss << "Unexpected ix::WebSocketMessageType"; });
log(ss.str());
}
});
_webSocket.start(); _webSocket.start();
} }
@ -211,42 +193,37 @@ namespace
bool startServer(ix::WebSocketServer& server) bool startServer(ix::WebSocketServer& server)
{ {
server.setOnConnectionCallback( server.setOnConnectionCallback([&server](std::shared_ptr<ix::WebSocket> webSocket,
[&server](std::shared_ptr<ix::WebSocket> webSocket, std::shared_ptr<ConnectionState> connectionState) {
std::shared_ptr<ConnectionState> connectionState) webSocket->setOnMessageCallback(
{ [webSocket, connectionState, &server](const ix::WebSocketMessagePtr& msg) {
webSocket->setOnMessageCallback( if (msg->type == ix::WebSocketMessageType::Open)
[webSocket, connectionState, &server](const ix::WebSocketMessagePtr& msg)
{ {
if (msg->type == ix::WebSocketMessageType::Open) Logger() << "New connection";
Logger() << "id: " << connectionState->getId();
Logger() << "Uri: " << msg->openInfo.uri;
Logger() << "Headers:";
for (auto it : msg->openInfo.headers)
{ {
Logger() << "New connection"; Logger() << it.first << ": " << it.second;
Logger() << "id: " << connectionState->getId();
Logger() << "Uri: " << msg->openInfo.uri;
Logger() << "Headers:";
for (auto it : msg->openInfo.headers)
{
Logger() << it.first << ": " << it.second;
}
} }
else if (msg->type == ix::WebSocketMessageType::Close) }
else if (msg->type == ix::WebSocketMessageType::Close)
{
log("Closed connection");
}
else if (msg->type == ix::WebSocketMessageType::Message)
{
for (auto&& client : server.getClients())
{ {
log("Closed connection"); if (client != webSocket)
}
else if (msg->type == ix::WebSocketMessageType::Message)
{
for (auto&& client : server.getClients())
{ {
if (client != webSocket) client->sendBinary(msg->str);
{
client->sendBinary(msg->str);
}
} }
} }
} }
); });
} });
);
auto res = server.listen(); auto res = server.listen();
if (!res.first) if (!res.first)
@ -258,7 +235,7 @@ namespace
server.start(); server.start();
return true; return true;
} }
} } // namespace
TEST_CASE("Websocket_chat", "[websocket_chat]") TEST_CASE("Websocket_chat", "[websocket_chat]")
{ {
@ -305,8 +282,7 @@ TEST_CASE("Websocket_chat", "[websocket_chat]")
// Wait until all messages are received. 10s timeout // Wait until all messages are received. 10s timeout
int attempts = 0; int attempts = 0;
while (chatA.getReceivedMessagesCount() != 3 || while (chatA.getReceivedMessagesCount() != 3 || chatB.getReceivedMessagesCount() != 3)
chatB.getReceivedMessagesCount() != 3)
{ {
REQUIRE(attempts++ < 10); REQUIRE(attempts++ < 10);
ix::msleep(1000); ix::msleep(1000);

View File

@ -4,15 +4,13 @@
* Copyright (c) 2019 Machine Zone. All rights reserved. * Copyright (c) 2019 Machine Zone. All rights reserved.
*/ */
#include "IXTest.h"
#include "catch.hpp"
#include <iostream> #include <iostream>
#include <sstream>
#include <queue>
#include <ixwebsocket/IXWebSocket.h> #include <ixwebsocket/IXWebSocket.h>
#include <ixwebsocket/IXWebSocketServer.h> #include <ixwebsocket/IXWebSocketServer.h>
#include <queue>
#include "IXTest.h" #include <sstream>
#include "catch.hpp"
using namespace ix; using namespace ix;
@ -20,30 +18,30 @@ namespace
{ {
class WebSocketClient class WebSocketClient
{ {
public: public:
WebSocketClient(int port); WebSocketClient(int port);
void subscribe(const std::string& channel); void subscribe(const std::string& channel);
void start(); void start();
void stop(); void stop();
void stop(uint16_t code, const std::string& reason); void stop(uint16_t code, const std::string& reason);
bool isReady() const; bool isReady() const;
uint16_t getCloseCode(); uint16_t getCloseCode();
const std::string& getCloseReason(); const std::string& getCloseReason();
bool getCloseRemote(); bool getCloseRemote();
bool hasConnectionError() const; bool hasConnectionError() const;
private: private:
ix::WebSocket _webSocket; ix::WebSocket _webSocket;
int _port; int _port;
mutable std::mutex _mutexCloseData; mutable std::mutex _mutexCloseData;
uint16_t _closeCode; uint16_t _closeCode;
std::string _closeReason; std::string _closeReason;
bool _closeRemote; bool _closeRemote;
std::atomic<bool> _connectionError; std::atomic<bool> _connectionError;
}; };
WebSocketClient::WebSocketClient(int port) WebSocketClient::WebSocketClient(int port)
@ -102,9 +100,7 @@ namespace
std::string url; std::string url;
{ {
std::stringstream ss; std::stringstream ss;
ss << "ws://localhost:" ss << "ws://localhost:" << _port << "/";
<< _port
<< "/";
url = ss.str(); url = ss.str();
} }
@ -115,57 +111,52 @@ namespace
std::stringstream ss; std::stringstream ss;
log(std::string("Connecting to url: ") + url); log(std::string("Connecting to url: ") + url);
_webSocket.setOnMessageCallback( _webSocket.setOnMessageCallback([this](const ix::WebSocketMessagePtr& msg) {
[this](const ix::WebSocketMessagePtr& msg) std::stringstream ss;
if (msg->type == ix::WebSocketMessageType::Open)
{
log("client connected");
}
else if (msg->type == ix::WebSocketMessageType::Close)
{ {
std::stringstream ss; std::stringstream ss;
if (msg->type == ix::WebSocketMessageType::Open) ss << "client disconnected(" << msg->closeInfo.code << "," << msg->closeInfo.reason
{ << ")";
log("client connected"); log(ss.str());
}
else if (msg->type == ix::WebSocketMessageType::Close)
{
std::stringstream ss;
ss << "client disconnected("
<< msg->closeInfo.code
<< ","
<< msg->closeInfo.reason
<< ")";
log(ss.str());
std::lock_guard<std::mutex> lck(_mutexCloseData); std::lock_guard<std::mutex> lck(_mutexCloseData);
_closeCode = msg->closeInfo.code; _closeCode = msg->closeInfo.code;
_closeReason = std::string(msg->closeInfo.reason); _closeReason = std::string(msg->closeInfo.reason);
_closeRemote = msg->closeInfo.remote; _closeRemote = msg->closeInfo.remote;
} }
else if (msg->type == ix::WebSocketMessageType::Error) else if (msg->type == ix::WebSocketMessageType::Error)
{ {
_connectionError = true; _connectionError = true;
ss << "Error ! " << msg->errorInfo.reason; ss << "Error ! " << msg->errorInfo.reason;
log(ss.str()); log(ss.str());
} }
else if (msg->type == ix::WebSocketMessageType::Pong) else if (msg->type == ix::WebSocketMessageType::Pong)
{ {
ss << "Received pong message " << msg->str; ss << "Received pong message " << msg->str;
log(ss.str()); log(ss.str());
} }
else if (msg->type == ix::WebSocketMessageType::Ping) else if (msg->type == ix::WebSocketMessageType::Ping)
{ {
ss << "Received ping message " << msg->str; ss << "Received ping message " << msg->str;
log(ss.str()); log(ss.str());
} }
else if (msg->type == ix::WebSocketMessageType::Message) else if (msg->type == ix::WebSocketMessageType::Message)
{ {
ss << "Received message " << msg->str; ss << "Received message " << msg->str;
log(ss.str()); log(ss.str());
} }
else else
{ {
ss << "Invalid ix::WebSocketMessageType"; ss << "Invalid ix::WebSocketMessageType";
log(ss.str()); log(ss.str());
} }
}); });
_webSocket.start(); _webSocket.start();
} }
@ -178,43 +169,41 @@ namespace
{ {
// A dev/null server // A dev/null server
server.setOnConnectionCallback( server.setOnConnectionCallback(
[&receivedCloseCode, &receivedCloseReason, &receivedCloseRemote, &mutexWrite](std::shared_ptr<ix::WebSocket> webSocket, [&receivedCloseCode, &receivedCloseReason, &receivedCloseRemote, &mutexWrite](
std::shared_ptr<ConnectionState> connectionState) std::shared_ptr<ix::WebSocket> webSocket,
{ std::shared_ptr<ConnectionState> connectionState) {
webSocket->setOnMessageCallback( webSocket->setOnMessageCallback([webSocket,
[webSocket, connectionState, &receivedCloseCode, &receivedCloseReason, &receivedCloseRemote, &mutexWrite](const ix::WebSocketMessagePtr& msg) connectionState,
&receivedCloseCode,
&receivedCloseReason,
&receivedCloseRemote,
&mutexWrite](const ix::WebSocketMessagePtr& msg) {
if (msg->type == ix::WebSocketMessageType::Open)
{ {
if (msg->type == ix::WebSocketMessageType::Open) Logger() << "New server connection";
Logger() << "id: " << connectionState->getId();
Logger() << "Uri: " << msg->openInfo.uri;
Logger() << "Headers:";
for (auto it : msg->openInfo.headers)
{ {
Logger() << "New server connection"; Logger() << it.first << ": " << it.second;
Logger() << "id: " << connectionState->getId();
Logger() << "Uri: " << msg->openInfo.uri;
Logger() << "Headers:";
for (auto it : msg->openInfo.headers)
{
Logger() << it.first << ": " << it.second;
}
}
else if (msg->type == ix::WebSocketMessageType::Close)
{
std::stringstream ss;
ss << "Server closed connection("
<< msg->closeInfo.code
<< ","
<< msg->closeInfo.reason
<< ")";
log(ss.str());
std::lock_guard<std::mutex> lck(mutexWrite);
receivedCloseCode = msg->closeInfo.code;
receivedCloseReason = std::string(msg->closeInfo.reason);
receivedCloseRemote = msg->closeInfo.remote;
} }
} }
); else if (msg->type == ix::WebSocketMessageType::Close)
} {
); std::stringstream ss;
ss << "Server closed connection(" << msg->closeInfo.code << ","
<< msg->closeInfo.reason << ")";
log(ss.str());
std::lock_guard<std::mutex> lck(mutexWrite);
receivedCloseCode = msg->closeInfo.code;
receivedCloseReason = std::string(msg->closeInfo.reason);
receivedCloseRemote = msg->closeInfo.remote;
}
});
});
auto res = server.listen(); auto res = server.listen();
if (!res.first) if (!res.first)
@ -226,7 +215,7 @@ namespace
server.start(); server.start();
return true; return true;
} }
} } // namespace
TEST_CASE("Websocket_client_close_default", "[close]") TEST_CASE("Websocket_client_close_default", "[close]")
{ {
@ -242,7 +231,11 @@ TEST_CASE("Websocket_client_close_default", "[close]")
std::string serverReceivedCloseReason(""); std::string serverReceivedCloseReason("");
std::mutex mutexWrite; std::mutex mutexWrite;
REQUIRE(startServer(server, serverReceivedCloseCode, serverReceivedCloseReason, serverReceivedCloseRemote, mutexWrite)); REQUIRE(startServer(server,
serverReceivedCloseCode,
serverReceivedCloseReason,
serverReceivedCloseRemote,
mutexWrite));
std::string session = ix::generateSessionId(); std::string session = ix::generateSessionId();
WebSocketClient webSocketClient(port); WebSocketClient webSocketClient(port);
@ -301,7 +294,11 @@ TEST_CASE("Websocket_client_close_params_given", "[close]")
std::string serverReceivedCloseReason(""); std::string serverReceivedCloseReason("");
std::mutex mutexWrite; std::mutex mutexWrite;
REQUIRE(startServer(server, serverReceivedCloseCode, serverReceivedCloseReason, serverReceivedCloseRemote, mutexWrite)); REQUIRE(startServer(server,
serverReceivedCloseCode,
serverReceivedCloseReason,
serverReceivedCloseRemote,
mutexWrite));
std::string session = ix::generateSessionId(); std::string session = ix::generateSessionId();
WebSocketClient webSocketClient(port); WebSocketClient webSocketClient(port);
@ -359,7 +356,11 @@ TEST_CASE("Websocket_server_close", "[close]")
std::string serverReceivedCloseReason(""); std::string serverReceivedCloseReason("");
std::mutex mutexWrite; std::mutex mutexWrite;
REQUIRE(startServer(server, serverReceivedCloseCode, serverReceivedCloseReason, serverReceivedCloseRemote, mutexWrite)); REQUIRE(startServer(server,
serverReceivedCloseCode,
serverReceivedCloseReason,
serverReceivedCloseRemote,
mutexWrite));
std::string session = ix::generateSessionId(); std::string session = ix::generateSessionId();
WebSocketClient webSocketClient(port); WebSocketClient webSocketClient(port);
@ -417,7 +418,11 @@ TEST_CASE("Websocket_server_close_immediatly", "[close]")
std::string serverReceivedCloseReason(""); std::string serverReceivedCloseReason("");
std::mutex mutexWrite; std::mutex mutexWrite;
REQUIRE(startServer(server, serverReceivedCloseCode, serverReceivedCloseReason, serverReceivedCloseRemote, mutexWrite)); REQUIRE(startServer(server,
serverReceivedCloseCode,
serverReceivedCloseReason,
serverReceivedCloseRemote,
mutexWrite));
std::string session = ix::generateSessionId(); std::string session = ix::generateSessionId();
WebSocketClient webSocketClient(port); WebSocketClient webSocketClient(port);

View File

@ -4,12 +4,11 @@
* Copyright (c) 2019 Machine Zone. All rights reserved. * Copyright (c) 2019 Machine Zone. All rights reserved.
*/ */
#include <ixwebsocket/IXWebSocket.h>
#include <ixwebsocket/IXWebSocketServer.h>
#include <ixwebsocket/IXWebSocketMessageQueue.h>
#include "IXTest.h" #include "IXTest.h"
#include "catch.hpp" #include "catch.hpp"
#include <ixwebsocket/IXWebSocket.h>
#include <ixwebsocket/IXWebSocketMessageQueue.h>
#include <ixwebsocket/IXWebSocketServer.h>
#include <thread> #include <thread>
using namespace ix; using namespace ix;
@ -18,42 +17,37 @@ namespace
{ {
bool startServer(ix::WebSocketServer& server) bool startServer(ix::WebSocketServer& server)
{ {
server.setOnConnectionCallback( server.setOnConnectionCallback([&server](std::shared_ptr<ix::WebSocket> webSocket,
[&server](std::shared_ptr<ix::WebSocket> webSocket, std::shared_ptr<ConnectionState> connectionState) {
std::shared_ptr<ConnectionState> connectionState)
{
webSocket->setOnMessageCallback( webSocket->setOnMessageCallback(
[connectionState, &server](const WebSocketMessagePtr& msg) [connectionState, &server](const WebSocketMessagePtr& msg) {
{ if (msg->type == ix::WebSocketMessageType::Open)
if (msg->type == ix::WebSocketMessageType::Open)
{
Logger() << "New connection";
connectionState->computeId();
Logger() << "id: " << connectionState->getId();
Logger() << "Uri: " << msg->openInfo.uri;
Logger() << "Headers:";
for (auto&& it : msg->openInfo.headers)
{ {
Logger() << it.first << ": " << it.second; Logger() << "New connection";
connectionState->computeId();
Logger() << "id: " << connectionState->getId();
Logger() << "Uri: " << msg->openInfo.uri;
Logger() << "Headers:";
for (auto&& it : msg->openInfo.headers)
{
Logger() << it.first << ": " << it.second;
}
} }
} else if (msg->type == ix::WebSocketMessageType::Close)
else if (msg->type == ix::WebSocketMessageType::Close) {
{ Logger() << "Closed connection";
Logger() << "Closed connection"; }
} else if (msg->type == ix::WebSocketMessageType::Message)
else if (msg->type == ix::WebSocketMessageType::Message) {
{ Logger() << "Message received: " << msg->str;
Logger() << "Message received: " << msg->str;
for (auto&& client : server.getClients()) for (auto&& client : server.getClients())
{ {
client->send(msg->str); client->send(msg->str);
}
} }
} });
} });
);
}
);
auto res = server.listen(); auto res = server.listen();
if (!res.first) if (!res.first)
@ -73,8 +67,7 @@ namespace
{ {
msgQ.bindWebsocket(&ws); msgQ.bindWebsocket(&ws);
msgQ.setOnMessageCallback([this](const WebSocketMessagePtr& msg) msgQ.setOnMessageCallback([this](const WebSocketMessagePtr& msg) {
{
REQUIRE(mainThreadId == std::this_thread::get_id()); REQUIRE(mainThreadId == std::this_thread::get_id());
std::stringstream ss; std::stringstream ss;
@ -153,7 +146,10 @@ namespace
} }
} }
bool isSucceeded() const { return succeeded; } bool isSucceeded() const
{
return succeeded;
}
private: private:
WebSocket ws; WebSocket ws;
@ -163,7 +159,7 @@ namespace
std::thread::id mainThreadId; std::thread::id mainThreadId;
bool succeeded = false; bool succeeded = false;
}; };
} } // namespace
TEST_CASE("Websocket_message_queue", "[websocket_message_q]") TEST_CASE("Websocket_message_queue", "[websocket_message_q]")
{ {

View File

@ -4,15 +4,13 @@
* Copyright (c) 2019 Machine Zone. All rights reserved. * Copyright (c) 2019 Machine Zone. All rights reserved.
*/ */
#include "IXTest.h"
#include "catch.hpp"
#include <iostream> #include <iostream>
#include <sstream>
#include <queue>
#include <ixwebsocket/IXWebSocket.h> #include <ixwebsocket/IXWebSocket.h>
#include <ixwebsocket/IXWebSocketServer.h> #include <ixwebsocket/IXWebSocketServer.h>
#include <queue>
#include "IXTest.h" #include <sstream>
#include "catch.hpp"
using namespace ix; using namespace ix;
@ -20,23 +18,23 @@ namespace
{ {
class WebSocketClient class WebSocketClient
{ {
public: public:
WebSocketClient(int port, bool useHeartBeatMethod); WebSocketClient(int port, bool useHeartBeatMethod);
void start(); void start();
void stop(); void stop();
bool isReady() const; bool isReady() const;
void sendMessage(const std::string& text); void sendMessage(const std::string& text);
private: private:
ix::WebSocket _webSocket; ix::WebSocket _webSocket;
int _port; int _port;
bool _useHeartBeatMethod; bool _useHeartBeatMethod;
}; };
WebSocketClient::WebSocketClient(int port, bool useHeartBeatMethod) WebSocketClient::WebSocketClient(int port, bool useHeartBeatMethod)
: _port(port), : _port(port)
_useHeartBeatMethod(useHeartBeatMethod) , _useHeartBeatMethod(useHeartBeatMethod)
{ {
; ;
} }
@ -56,9 +54,7 @@ namespace
std::string url; std::string url;
{ {
std::stringstream ss; std::stringstream ss;
ss << "ws://127.0.0.1:" ss << "ws://127.0.0.1:" << _port << "/";
<< _port
<< "/";
url = ss.str(); url = ss.str();
} }
@ -79,48 +75,46 @@ namespace
std::stringstream ss; std::stringstream ss;
log(std::string("Connecting to url: ") + url); log(std::string("Connecting to url: ") + url);
_webSocket.setOnMessageCallback( _webSocket.setOnMessageCallback([](ix::WebSocketMessageType messageType,
[](ix::WebSocketMessageType messageType, const std::string& str,
const std::string& str, size_t wireSize,
size_t wireSize, const ix::WebSocketErrorInfo& error,
const ix::WebSocketErrorInfo& error, const ix::WebSocketOpenInfo& openInfo,
const ix::WebSocketOpenInfo& openInfo, const ix::WebSocketCloseInfo& closeInfo) {
const ix::WebSocketCloseInfo& closeInfo) std::stringstream ss;
if (messageType == ix::WebSocketMessageType::Open)
{ {
std::stringstream ss; log("client connected");
if (messageType == ix::WebSocketMessageType::Open) }
{ else if (messageType == ix::WebSocketMessageType::Close)
log("client connected"); {
} log("client disconnected");
else if (messageType == ix::WebSocketMessageType::Close) }
{ else if (messageType == ix::WebSocketMessageType::Error)
log("client disconnected"); {
} ss << "Error ! " << error.reason;
else if (messageType == ix::WebSocketMessageType::Error) log(ss.str());
{ }
ss << "Error ! " << error.reason; else if (messageType == ix::WebSocketMessageType::Pong)
log(ss.str()); {
} ss << "Received pong message " << str;
else if (messageType == ix::WebSocketMessageType::Pong) log(ss.str());
{ }
ss << "Received pong message " << str; else if (messageType == ix::WebSocketMessageType::Ping)
log(ss.str()); {
} ss << "Received ping message " << str;
else if (messageType == ix::WebSocketMessageType::Ping) log(ss.str());
{ }
ss << "Received ping message " << str; else if (messageType == ix::WebSocketMessageType::Message)
log(ss.str()); {
} // too many messages to log
else if (messageType == ix::WebSocketMessageType::Message) }
{ else
// too many messages to log {
} ss << "Invalid ix::WebSocketMessageType";
else log(ss.str());
{ }
ss << "Invalid ix::WebSocketMessageType"; });
log(ss.str());
}
});
_webSocket.start(); _webSocket.start();
} }
@ -135,16 +129,15 @@ namespace
// A dev/null server // A dev/null server
server.setOnConnectionCallback( server.setOnConnectionCallback(
[&server, &receivedPingMessages](std::shared_ptr<ix::WebSocket> webSocket, [&server, &receivedPingMessages](std::shared_ptr<ix::WebSocket> webSocket,
std::shared_ptr<ConnectionState> connectionState) std::shared_ptr<ConnectionState> connectionState) {
{
webSocket->setOnMessageCallback( webSocket->setOnMessageCallback(
[webSocket, connectionState, &server, &receivedPingMessages](ix::WebSocketMessageType messageType, [webSocket, connectionState, &server, &receivedPingMessages](
const std::string& str, ix::WebSocketMessageType messageType,
size_t wireSize, const std::string& str,
const ix::WebSocketErrorInfo& error, size_t wireSize,
const ix::WebSocketOpenInfo& openInfo, const ix::WebSocketErrorInfo& error,
const ix::WebSocketCloseInfo& closeInfo) const ix::WebSocketOpenInfo& openInfo,
{ const ix::WebSocketCloseInfo& closeInfo) {
if (messageType == ix::WebSocketMessageType::Open) if (messageType == ix::WebSocketMessageType::Open)
{ {
Logger() << "New server connection"; Logger() << "New server connection";
@ -168,15 +161,13 @@ namespace
else if (messageType == ix::WebSocketMessageType::Message) else if (messageType == ix::WebSocketMessageType::Message)
{ {
// to many messages to log // to many messages to log
for(auto client: server.getClients()) for (auto client : server.getClients())
{ {
client->sendText("reply"); client->sendText("reply");
} }
} }
} });
); });
}
);
auto res = server.listen(); auto res = server.listen();
if (!res.first) if (!res.first)
@ -188,7 +179,7 @@ namespace
server.start(); server.start();
return true; return true;
} }
} } // namespace
TEST_CASE("Websocket_ping_no_data_sent_setPingInterval", "[setPingInterval]") TEST_CASE("Websocket_ping_no_data_sent_setPingInterval", "[setPingInterval]")
{ {
@ -282,7 +273,8 @@ TEST_CASE("Websocket_ping_data_sent_setPingInterval", "[setPingInterval]")
TEST_CASE("Websocket_ping_data_sent_setPingInterval_half_full", "[setPingInterval]") TEST_CASE("Websocket_ping_data_sent_setPingInterval_half_full", "[setPingInterval]")
{ {
SECTION("Make sure that ping messages are sent, even if other messages are sent continuously during a given time") SECTION("Make sure that ping messages are sent, even if other messages are sent continuously "
"during a given time")
{ {
ix::setupWebSocketTrafficTrackerCallback(); ix::setupWebSocketTrafficTrackerCallback();
@ -309,7 +301,7 @@ TEST_CASE("Websocket_ping_data_sent_setPingInterval_half_full", "[setPingInterva
// send continuously for 1100ms // send continuously for 1100ms
auto now = std::chrono::steady_clock::now(); auto now = std::chrono::steady_clock::now();
while(std::chrono::steady_clock::now() - now <= std::chrono::milliseconds(900)) while (std::chrono::steady_clock::now() - now <= std::chrono::milliseconds(900))
{ {
webSocketClient.sendMessage("message"); webSocketClient.sendMessage("message");
ix::msleep(1); ix::msleep(1);
@ -335,7 +327,8 @@ TEST_CASE("Websocket_ping_data_sent_setPingInterval_half_full", "[setPingInterva
TEST_CASE("Websocket_ping_data_sent_setPingInterval_full", "[setPingInterval]") TEST_CASE("Websocket_ping_data_sent_setPingInterval_full", "[setPingInterval]")
{ {
SECTION("Make sure that ping messages are sent, even if other messages are sent continuously for longer than ping interval") SECTION("Make sure that ping messages are sent, even if other messages are sent continuously "
"for longer than ping interval")
{ {
ix::setupWebSocketTrafficTrackerCallback(); ix::setupWebSocketTrafficTrackerCallback();
@ -362,7 +355,7 @@ TEST_CASE("Websocket_ping_data_sent_setPingInterval_full", "[setPingInterval]")
// send continuously for 1100ms // send continuously for 1100ms
auto now = std::chrono::steady_clock::now(); auto now = std::chrono::steady_clock::now();
while(std::chrono::steady_clock::now() - now <= std::chrono::milliseconds(1100)) while (std::chrono::steady_clock::now() - now <= std::chrono::milliseconds(1100))
{ {
webSocketClient.sendMessage("message"); webSocketClient.sendMessage("message");
ix::msleep(1); ix::msleep(1);

View File

@ -4,15 +4,13 @@
* Copyright (c) 2019 Machine Zone. All rights reserved. * Copyright (c) 2019 Machine Zone. All rights reserved.
*/ */
#include "IXTest.h"
#include "catch.hpp"
#include <iostream> #include <iostream>
#include <sstream>
#include <queue>
#include <ixwebsocket/IXWebSocket.h> #include <ixwebsocket/IXWebSocket.h>
#include <ixwebsocket/IXWebSocketServer.h> #include <ixwebsocket/IXWebSocketServer.h>
#include <queue>
#include "IXTest.h" #include <sstream>
#include "catch.hpp"
using namespace ix; using namespace ix;
@ -20,32 +18,32 @@ namespace
{ {
class WebSocketClient class WebSocketClient
{ {
public: public:
WebSocketClient(int port, int pingInterval, int pingTimeout); WebSocketClient(int port, int pingInterval, int pingTimeout);
void start(); void start();
void stop(); void stop();
bool isReady() const; bool isReady() const;
bool isClosed() const; bool isClosed() const;
void sendMessage(const std::string& text); void sendMessage(const std::string& text);
int getReceivedPongMessages(); int getReceivedPongMessages();
bool closedDueToPingTimeout(); bool closedDueToPingTimeout();
private: private:
ix::WebSocket _webSocket; ix::WebSocket _webSocket;
int _port; int _port;
int _pingInterval; int _pingInterval;
int _pingTimeout; int _pingTimeout;
std::atomic<int> _receivedPongMessages; std::atomic<int> _receivedPongMessages;
std::atomic<bool> _closedDueToPingTimeout; std::atomic<bool> _closedDueToPingTimeout;
}; };
WebSocketClient::WebSocketClient(int port, int pingInterval, int pingTimeout) WebSocketClient::WebSocketClient(int port, int pingInterval, int pingTimeout)
: _port(port), : _port(port)
_receivedPongMessages(0), , _receivedPongMessages(0)
_closedDueToPingTimeout(false), , _closedDueToPingTimeout(false)
_pingInterval(pingInterval), , _pingInterval(pingInterval)
_pingTimeout(pingTimeout) , _pingTimeout(pingTimeout)
{ {
; ;
} }
@ -70,9 +68,7 @@ namespace
std::string url; std::string url;
{ {
std::stringstream ss; std::stringstream ss;
ss << "ws://127.0.0.1:" ss << "ws://127.0.0.1:" << _port << "/";
<< _port
<< "/";
url = ss.str(); url = ss.str();
} }
@ -88,58 +84,54 @@ namespace
std::stringstream ss; std::stringstream ss;
log(std::string("Connecting to url: ") + url); log(std::string("Connecting to url: ") + url);
_webSocket.setOnMessageCallback( _webSocket.setOnMessageCallback([this](ix::WebSocketMessageType messageType,
[this](ix::WebSocketMessageType messageType, const std::string& str,
const std::string& str, size_t wireSize,
size_t wireSize, const ix::WebSocketErrorInfo& error,
const ix::WebSocketErrorInfo& error, const ix::WebSocketOpenInfo& openInfo,
const ix::WebSocketOpenInfo& openInfo, const ix::WebSocketCloseInfo& closeInfo) {
const ix::WebSocketCloseInfo& closeInfo) std::stringstream ss;
if (messageType == ix::WebSocketMessageType::Open)
{ {
std::stringstream ss; log("client connected");
if (messageType == ix::WebSocketMessageType::Open) }
{ else if (messageType == ix::WebSocketMessageType::Close)
log("client connected"); {
log("client disconnected");
} if (msg->closeInfo.code == 1011)
else if (messageType == ix::WebSocketMessageType::Close)
{ {
log("client disconnected"); _closedDueToPingTimeout = true;
}
}
else if (messageType == ix::WebSocketMessageType::Error)
{
ss << "Error ! " << error.reason;
log(ss.str());
}
else if (messageType == ix::WebSocketMessageType::Pong)
{
_receivedPongMessages++;
if (msg->closeInfo.code == 1011) ss << "Received pong message " << str;
{ log(ss.str());
_closedDueToPingTimeout = true; }
} else if (messageType == ix::WebSocketMessageType::Ping)
{
} ss << "Received ping message " << str;
else if (messageType == ix::WebSocketMessageType::Error) log(ss.str());
{ }
ss << "Error ! " << error.reason; else if (messageType == ix::WebSocketMessageType::Message)
log(ss.str()); {
} ss << "Received message " << str;
else if (messageType == ix::WebSocketMessageType::Pong) log(ss.str());
{ }
_receivedPongMessages++; else
{
ss << "Received pong message " << str; ss << "Invalid ix::WebSocketMessageType";
log(ss.str()); log(ss.str());
} }
else if (messageType == ix::WebSocketMessageType::Ping) });
{
ss << "Received ping message " << str;
log(ss.str());
}
else if (messageType == ix::WebSocketMessageType::Message)
{
ss << "Received message " << str;
log(ss.str());
}
else
{
ss << "Invalid ix::WebSocketMessageType";
log(ss.str());
}
});
_webSocket.start(); _webSocket.start();
} }
@ -159,21 +151,22 @@ namespace
return _closedDueToPingTimeout; return _closedDueToPingTimeout;
} }
bool startServer(ix::WebSocketServer& server, std::atomic<int>& receivedPingMessages, bool enablePong) bool startServer(ix::WebSocketServer& server,
std::atomic<int>& receivedPingMessages,
bool enablePong)
{ {
// A dev/null server // A dev/null server
server.setOnConnectionCallback( server.setOnConnectionCallback(
[&server, &receivedPingMessages](std::shared_ptr<ix::WebSocket> webSocket, [&server, &receivedPingMessages](std::shared_ptr<ix::WebSocket> webSocket,
std::shared_ptr<ConnectionState> connectionState) std::shared_ptr<ConnectionState> connectionState) {
{
webSocket->setOnMessageCallback( webSocket->setOnMessageCallback(
[webSocket, connectionState, &server, &receivedPingMessages](ix::WebSocketMessageType messageType, [webSocket, connectionState, &server, &receivedPingMessages](
const std::string& str, ix::WebSocketMessageType messageType,
size_t wireSize, const std::string& str,
const ix::WebSocketErrorInfo& error, size_t wireSize,
const ix::WebSocketOpenInfo& openInfo, const ix::WebSocketErrorInfo& error,
const ix::WebSocketCloseInfo& closeInfo) const ix::WebSocketOpenInfo& openInfo,
{ const ix::WebSocketCloseInfo& closeInfo) {
if (messageType == ix::WebSocketMessageType::Open) if (messageType == ix::WebSocketMessageType::Open)
{ {
Logger() << "New server connection"; Logger() << "New server connection";
@ -194,10 +187,8 @@ namespace
log("Server received a ping"); log("Server received a ping");
receivedPingMessages++; receivedPingMessages++;
} }
} });
); });
}
);
if (!enablePong) if (!enablePong)
{ {
@ -215,7 +206,7 @@ namespace
server.start(); server.start();
return true; return true;
} }
} } // namespace
TEST_CASE("Websocket_ping_timeout_not_checked", "[setPingTimeout]") TEST_CASE("Websocket_ping_timeout_not_checked", "[setPingTimeout]")
{ {
@ -336,7 +327,7 @@ TEST_CASE("Websocket_no_ping_but_timeout", "[setPingTimeout]")
REQUIRE(startServer(server, serverReceivedPingMessages, enablePong)); REQUIRE(startServer(server, serverReceivedPingMessages, enablePong));
std::string session = ix::generateSessionId(); std::string session = ix::generateSessionId();
int pingIntervalSecs = -1; // no ping set int pingIntervalSecs = -1; // no ping set
int pingTimeoutSecs = 3; int pingTimeoutSecs = 3;
WebSocketClient webSocketClient(port, pingIntervalSecs, pingTimeoutSecs); WebSocketClient webSocketClient(port, pingIntervalSecs, pingTimeoutSecs);
webSocketClient.start(); webSocketClient.start();

View File

@ -4,15 +4,13 @@
* Copyright (c) 2019 Machine Zone. All rights reserved. * Copyright (c) 2019 Machine Zone. All rights reserved.
*/ */
#include "IXTest.h"
#include "catch.hpp"
#include <iostream> #include <iostream>
#include <ixwebsocket/IXSocket.h> #include <ixwebsocket/IXSocket.h>
#include <ixwebsocket/IXSocketFactory.h>
#include <ixwebsocket/IXWebSocket.h> #include <ixwebsocket/IXWebSocket.h>
#include <ixwebsocket/IXWebSocketServer.h> #include <ixwebsocket/IXWebSocketServer.h>
#include <ixwebsocket/IXSocketFactory.h>
#include "IXTest.h"
#include "catch.hpp"
using namespace ix; using namespace ix;
@ -28,55 +26,48 @@ namespace ix
} }
}; };
bool startServer(ix::WebSocketServer& server, bool startServer(ix::WebSocketServer& server, std::string& connectionId)
std::string& connectionId)
{ {
auto factory = []() -> std::shared_ptr<ConnectionState> auto factory = []() -> std::shared_ptr<ConnectionState> {
{
return std::make_shared<ConnectionStateCustom>(); return std::make_shared<ConnectionStateCustom>();
}; };
server.setConnectionStateFactory(factory); server.setConnectionStateFactory(factory);
server.setOnConnectionCallback( server.setOnConnectionCallback([&server, &connectionId](
[&server, &connectionId](std::shared_ptr<ix::WebSocket> webSocket, std::shared_ptr<ix::WebSocket> webSocket,
std::shared_ptr<ConnectionState> connectionState) std::shared_ptr<ConnectionState> connectionState) {
{ webSocket->setOnMessageCallback([webSocket, connectionState, &connectionId, &server](
webSocket->setOnMessageCallback( const ix::WebSocketMessagePtr& msg) {
[webSocket, connectionState, if (msg->type == ix::WebSocketMessageType::Open)
&connectionId, &server](const ix::WebSocketMessagePtr& msg) {
Logger() << "New connection";
connectionState->computeId();
Logger() << "id: " << connectionState->getId();
Logger() << "Uri: " << msg->openInfo.uri;
Logger() << "Headers:";
for (auto it : msg->openInfo.headers)
{ {
if (msg->type == ix::WebSocketMessageType::Open) Logger() << it.first << ": " << it.second;
{ }
Logger() << "New connection";
connectionState->computeId();
Logger() << "id: " << connectionState->getId();
Logger() << "Uri: " << msg->openInfo.uri;
Logger() << "Headers:";
for (auto it : msg->openInfo.headers)
{
Logger() << it.first << ": " << it.second;
}
connectionId = connectionState->getId(); connectionId = connectionState->getId();
} }
else if (msg->type == ix::WebSocketMessageType::Close) else if (msg->type == ix::WebSocketMessageType::Close)
{
Logger() << "Closed connection";
}
else if (msg->type == ix::WebSocketMessageType::Message)
{
for (auto&& client : server.getClients())
{
if (client != webSocket)
{ {
Logger() << "Closed connection"; client->send(msg->str);
}
else if (msg->type == ix::WebSocketMessageType::Message)
{
for (auto&& client : server.getClients())
{
if (client != webSocket)
{
client->send(msg->str);
}
}
} }
} }
); }
} });
); });
auto res = server.listen(); auto res = server.listen();
if (!res.first) if (!res.first)
@ -88,7 +79,7 @@ namespace ix
server.start(); server.start();
return true; return true;
} }
} } // namespace ix
TEST_CASE("Websocket_server", "[websocket_server]") TEST_CASE("Websocket_server", "[websocket_server]")
{ {
@ -103,10 +94,7 @@ TEST_CASE("Websocket_server", "[websocket_server]")
bool tls = false; bool tls = false;
std::shared_ptr<Socket> socket = createSocket(tls, errMsg); std::shared_ptr<Socket> socket = createSocket(tls, errMsg);
std::string host("127.0.0.1"); std::string host("127.0.0.1");
auto isCancellationRequested = []() -> bool auto isCancellationRequested = []() -> bool { return false; };
{
return false;
};
bool success = socket->connect(host, port, errMsg, isCancellationRequested); bool success = socket->connect(host, port, errMsg, isCancellationRequested);
REQUIRE(success); REQUIRE(success);
@ -139,10 +127,7 @@ TEST_CASE("Websocket_server", "[websocket_server]")
bool tls = false; bool tls = false;
std::shared_ptr<Socket> socket = createSocket(tls, errMsg); std::shared_ptr<Socket> socket = createSocket(tls, errMsg);
std::string host("127.0.0.1"); std::string host("127.0.0.1");
auto isCancellationRequested = []() -> bool auto isCancellationRequested = []() -> bool { return false; };
{
return false;
};
bool success = socket->connect(host, port, errMsg, isCancellationRequested); bool success = socket->connect(host, port, errMsg, isCancellationRequested);
REQUIRE(success); REQUIRE(success);
@ -178,10 +163,7 @@ TEST_CASE("Websocket_server", "[websocket_server]")
bool tls = false; bool tls = false;
std::shared_ptr<Socket> socket = createSocket(tls, errMsg); std::shared_ptr<Socket> socket = createSocket(tls, errMsg);
std::string host("127.0.0.1"); std::string host("127.0.0.1");
auto isCancellationRequested = []() -> bool auto isCancellationRequested = []() -> bool { return false; };
{
return false;
};
bool success = socket->connect(host, port, errMsg, isCancellationRequested); bool success = socket->connect(host, port, errMsg, isCancellationRequested);
REQUIRE(success); REQUIRE(success);

View File

@ -4,13 +4,12 @@
* Copyright (c) 2017 Machine Zone. All rights reserved. * Copyright (c) 2017 Machine Zone. All rights reserved.
*/ */
#include <iostream>
#include <sstream>
#include <set>
#include <ixwebsocket/IXWebSocket.h>
#include "IXTest.h" #include "IXTest.h"
#include "catch.hpp" #include "catch.hpp"
#include <iostream>
#include <ixwebsocket/IXWebSocket.h>
#include <set>
#include <sstream>
using namespace ix; using namespace ix;
@ -19,19 +18,19 @@ namespace
const std::string WEBSOCKET_DOT_ORG_URL("wss://echo.websocket.org"); const std::string WEBSOCKET_DOT_ORG_URL("wss://echo.websocket.org");
const std::string GOOGLE_URL("wss://google.com"); const std::string GOOGLE_URL("wss://google.com");
const std::string UNKNOWN_URL("wss://asdcasdcaasdcasdcasdcasdcasdcasdcasassdd.com"); const std::string UNKNOWN_URL("wss://asdcasdcaasdcasdcasdcasdcasdcasdcasassdd.com");
} } // namespace
namespace namespace
{ {
class IXWebSocketTestConnectionDisconnection class IXWebSocketTestConnectionDisconnection
{ {
public: public:
IXWebSocketTestConnectionDisconnection(); IXWebSocketTestConnectionDisconnection();
void start(const std::string& url); void start(const std::string& url);
void stop(); void stop();
private: private:
ix::WebSocket _webSocket; ix::WebSocket _webSocket;
}; };
IXWebSocketTestConnectionDisconnection::IXWebSocketTestConnectionDisconnection() IXWebSocketTestConnectionDisconnection::IXWebSocketTestConnectionDisconnection()
@ -51,45 +50,43 @@ namespace
std::stringstream ss; std::stringstream ss;
log(std::string("Connecting to url: ") + url); log(std::string("Connecting to url: ") + url);
_webSocket.setOnMessageCallback( _webSocket.setOnMessageCallback([](const ix::WebSocketMessagePtr& msg) {
[](const ix::WebSocketMessagePtr& msg) std::stringstream ss;
if (msg->type == ix::WebSocketMessageType::Open)
{ {
std::stringstream ss; log("TestConnectionDisconnection: connected !");
if (msg->type == ix::WebSocketMessageType::Open) }
{ else if (msg->type == ix::WebSocketMessageType::Close)
log("TestConnectionDisconnection: connected !"); {
} log("TestConnectionDisconnection: disconnected !");
else if (msg->type == ix::WebSocketMessageType::Close) }
{ else if (msg->type == ix::WebSocketMessageType::Error)
log("TestConnectionDisconnection: disconnected !"); {
} ss << "TestConnectionDisconnection: Error! ";
else if (msg->type == ix::WebSocketMessageType::Error) ss << msg->errorInfo.reason;
{ log(ss.str());
ss << "TestConnectionDisconnection: Error! "; }
ss << msg->errorInfo.reason; else if (msg->type == ix::WebSocketMessageType::Message)
log(ss.str()); {
} log("TestConnectionDisconnection: received message.!");
else if (msg->type == ix::WebSocketMessageType::Message) }
{ else if (msg->type == ix::WebSocketMessageType::Ping)
log("TestConnectionDisconnection: received message.!"); {
} log("TestConnectionDisconnection: received ping message.!");
else if (msg->type == ix::WebSocketMessageType::Ping) }
{ else if (msg->type == ix::WebSocketMessageType::Pong)
log("TestConnectionDisconnection: received ping message.!"); {
} log("TestConnectionDisconnection: received pong message.!");
else if (msg->type == ix::WebSocketMessageType::Pong) }
{ else if (msg->type == ix::WebSocketMessageType::Fragment)
log("TestConnectionDisconnection: received pong message.!"); {
} log("TestConnectionDisconnection: received fragment.!");
else if (msg->type == ix::WebSocketMessageType::Fragment) }
{ else
log("TestConnectionDisconnection: received fragment.!"); {
} log("Invalid ix::WebSocketMessageType");
else }
{ });
log("Invalid ix::WebSocketMessageType");
}
});
_webSocket.enableAutomaticReconnection(); _webSocket.enableAutomaticReconnection();
REQUIRE(_webSocket.isAutomaticReconnectionEnabled() == true); REQUIRE(_webSocket.isAutomaticReconnectionEnabled() == true);
@ -100,11 +97,12 @@ namespace
// Start the connection // Start the connection
_webSocket.start(); _webSocket.start();
} }
} } // namespace
// //
// We try to connect to different servers, and make sure there are no crashes. // We try to connect to different servers, and make sure there are no crashes.
// FIXME: We could do more checks (make sure that we were not able to connect to unknown servers, etc...) // FIXME: We could do more checks (make sure that we were not able to connect to unknown servers,
// etc...)
// //
TEST_CASE("websocket_connections", "[websocket]") TEST_CASE("websocket_connections", "[websocket]")
{ {
@ -121,7 +119,8 @@ TEST_CASE("websocket_connections", "[websocket]")
test.stop(); test.stop();
} }
SECTION("Try to connect and disconnect with different timing, not enough time to succesfully connect") SECTION("Try to connect and disconnect with different timing, not enough time to succesfully "
"connect")
{ {
IXWebSocketTestConnectionDisconnection test; IXWebSocketTestConnectionDisconnection test;
log(std::string("50 Runs")); log(std::string("50 Runs"));
@ -141,7 +140,8 @@ TEST_CASE("websocket_connections", "[websocket]")
// This test breaks on travis CI - Ubuntu Xenial + gcc + tsan // This test breaks on travis CI - Ubuntu Xenial + gcc + tsan
// We should fix this. // We should fix this.
SECTION("Try to connect and disconnect with different timing, from not enough time to successfull connect") SECTION("Try to connect and disconnect with different timing, from not enough time to "
"successfull connect")
{ {
IXWebSocketTestConnectionDisconnection test; IXWebSocketTestConnectionDisconnection test;
log(std::string("20 Runs")); log(std::string("20 Runs"));
@ -152,7 +152,7 @@ TEST_CASE("websocket_connections", "[websocket]")
test.start(WEBSOCKET_DOT_ORG_URL); test.start(WEBSOCKET_DOT_ORG_URL);
log(std::string("Sleeping")); log(std::string("Sleeping"));
ix::msleep(i*50); ix::msleep(i * 50);
log(std::string("Stopping")); log(std::string("Stopping"));
test.stop(); test.stop();

View File

@ -6,20 +6,15 @@
#define CATCH_CONFIG_RUNNER #define CATCH_CONFIG_RUNNER
#include "catch.hpp" #include "catch.hpp"
#include <spdlog/spdlog.h>
#include <ixwebsocket/IXNetSystem.h>
#include <ixcore/utils/IXCoreLogger.h> #include <ixcore/utils/IXCoreLogger.h>
#include <ixwebsocket/IXNetSystem.h>
#include <spdlog/spdlog.h>
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
ix::initNetSystem(); ix::initNetSystem();
ix::IXCoreLogger::LogFunc logFunc = [](const char* msg) ix::IXCoreLogger::LogFunc logFunc = [](const char* msg) { spdlog::info(msg); };
{
spdlog::info(msg);
};
ix::IXCoreLogger::setLogFunction(logFunc); ix::IXCoreLogger::setLogFunction(logFunc);
int result = Catch::Session().run(argc, argv); int result = Catch::Session().run(argc, argv);

View File

@ -5,14 +5,14 @@
*/ */
#include "IXRedisClient.h" #include "IXRedisClient.h"
#include <ixwebsocket/IXSocketFactory.h>
#include <ixwebsocket/IXSocket.h>
#include <iostream>
#include <sstream>
#include <iomanip>
#include <vector>
#include <cstring> #include <cstring>
#include <iomanip>
#include <iostream>
#include <ixwebsocket/IXSocket.h>
#include <ixwebsocket/IXSocketFactory.h>
#include <sstream>
#include <vector>
namespace ix namespace ix
{ {
@ -31,8 +31,7 @@ namespace ix
return _socket->connect(hostname, port, errMsg, nullptr); return _socket->connect(hostname, port, errMsg, nullptr);
} }
bool RedisClient::auth(const std::string& password, bool RedisClient::auth(const std::string& password, std::string& response)
std::string& response)
{ {
response.clear(); response.clear();
@ -203,7 +202,7 @@ namespace ix
int arraySize; int arraySize;
{ {
std::stringstream ss; std::stringstream ss;
ss << line.substr(1, line.size()-1); ss << line.substr(1, line.size() - 1);
ss >> arraySize; ss >> arraySize;
} }
@ -220,7 +219,7 @@ namespace ix
// => $7 (7 bytes) // => $7 (7 bytes)
int stringSize; int stringSize;
std::stringstream ss; std::stringstream ss;
ss << line.substr(1, line.size()-1); ss << line.substr(1, line.size() - 1);
ss >> stringSize; ss >> stringSize;
auto readResult = _socket->readBytes(stringSize, nullptr, nullptr); auto readResult = _socket->readBytes(stringSize, nullptr, nullptr);
@ -246,4 +245,4 @@ namespace ix
{ {
_stop = true; _stop = true;
} }
} } // namespace ix

View File

@ -6,9 +6,9 @@
#pragma once #pragma once
#include <atomic>
#include <functional> #include <functional>
#include <memory> #include <memory>
#include <atomic>
namespace ix namespace ix
{ {
@ -20,7 +20,10 @@ namespace ix
using OnRedisSubscribeResponseCallback = std::function<void(const std::string&)>; using OnRedisSubscribeResponseCallback = std::function<void(const std::string&)>;
using OnRedisSubscribeCallback = std::function<void(const std::string&)>; using OnRedisSubscribeCallback = std::function<void(const std::string&)>;
RedisClient() : _stop(false) {} RedisClient()
: _stop(false)
{
}
~RedisClient() = default; ~RedisClient() = default;
bool connect(const std::string& hostname, int port); bool connect(const std::string& hostname, int port);

View File

@ -8,17 +8,16 @@
#include <chrono> #include <chrono>
#include <iostream> #include <iostream>
#include <spdlog/spdlog.h>
#include <ixwebsocket/IXWebSocketHttpHeaders.h> #include <ixwebsocket/IXWebSocketHttpHeaders.h>
#include <spdlog/spdlog.h>
namespace ix namespace ix
{ {
SentryClient::SentryClient(const std::string& dsn) : SentryClient::SentryClient(const std::string& dsn)
_dsn(dsn), : _dsn(dsn)
_validDsn(false), , _validDsn(false)
_luaFrameRegex("\t([^/]+):([0-9]+): in function '([^/]+)'") , _luaFrameRegex("\t([^/]+):([0-9]+): in function '([^/]+)'")
{ {
const std::regex dsnRegex("(http[s]?)://([^:]+):([^@]+)@([^/]+)/([0-9]+)"); const std::regex dsnRegex("(http[s]?)://([^:]+):([^@]+)@([^/]+)/([0-9]+)");
std::smatch group; std::smatch group;
@ -168,8 +167,7 @@ namespace ix
return _jsonWriter.write(payload); return _jsonWriter.write(payload);
} }
std::pair<HttpResponsePtr, std::string> SentryClient::send(const Json::Value& msg, std::pair<HttpResponsePtr, std::string> SentryClient::send(const Json::Value& msg, bool verbose)
bool verbose)
{ {
auto args = _httpClient.createRequest(); auto args = _httpClient.createRequest();
args->extraHeaders["X-Sentry-Auth"] = SentryClient::computeAuthHeader(); args->extraHeaders["X-Sentry-Auth"] = SentryClient::computeAuthHeader();
@ -177,10 +175,7 @@ namespace ix
args->transferTimeout = 5 * 60; args->transferTimeout = 5 * 60;
args->followRedirects = true; args->followRedirects = true;
args->verbose = verbose; args->verbose = verbose;
args->logger = [](const std::string& msg) args->logger = [](const std::string& msg) { spdlog::info("request logger: {}", msg); };
{
spdlog::info("request logger: {}", msg);
};
std::string body = computePayload(msg); std::string body = computePayload(msg);
HttpResponsePtr response = _httpClient.post(_url, body, args); HttpResponsePtr response = _httpClient.post(_url, body, args);

View File

@ -4,24 +4,20 @@
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved. * Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
*/ */
#include "IXSnakeProtocol.h"
#include "IXAppConfig.h" #include "IXAppConfig.h"
#include "IXSnakeProtocol.h"
#include <iostream> #include <iostream>
#include <ixcrypto/IXUuid.h> #include <ixcrypto/IXUuid.h>
namespace snake namespace snake
{ {
bool isAppKeyValid( bool isAppKeyValid(const AppConfig& appConfig, std::string appkey)
const AppConfig& appConfig,
std::string appkey)
{ {
return appConfig.apps.count(appkey) != 0; return appConfig.apps.count(appkey) != 0;
} }
std::string getRoleSecret( std::string getRoleSecret(const AppConfig& appConfig, std::string appkey, std::string role)
const AppConfig& appConfig,
std::string appkey,
std::string role)
{ {
if (!isAppKeyValid(appConfig, appkey)) if (!isAppKeyValid(appConfig, appkey))
{ {
@ -49,4 +45,4 @@ namespace snake
std::cout << "redis password: " << appConfig.redisPassword << std::endl; std::cout << "redis password: " << appConfig.redisPassword << std::endl;
std::cout << "redis port: " << appConfig.redisPort << std::endl; std::cout << "redis port: " << appConfig.redisPort << std::endl;
} }
} } // namespace snake

View File

@ -16,16 +16,37 @@ namespace snake
class SnakeConnectionState : public ix::ConnectionState class SnakeConnectionState : public ix::ConnectionState
{ {
public: public:
std::string getNonce() { return _nonce; } std::string getNonce()
void setNonce(const std::string& nonce) { _nonce = nonce; } {
return _nonce;
}
void setNonce(const std::string& nonce)
{
_nonce = nonce;
}
std::string appkey() { return _appkey; } std::string appkey()
void setAppkey(const std::string& appkey) { _appkey = appkey; } {
return _appkey;
}
void setAppkey(const std::string& appkey)
{
_appkey = appkey;
}
std::string role() { return _role; } std::string role()
void setRole(const std::string& role) { _role = role; } {
return _role;
}
void setRole(const std::string& role)
{
_role = role;
}
ix::RedisClient& redisClient() { return _redisClient; } ix::RedisClient& redisClient()
{
return _redisClient;
}
std::future<void> fut; std::future<void> fut;

View File

@ -6,41 +6,32 @@
#include "IXSnakeProtocol.h" #include "IXSnakeProtocol.h"
#include <ixwebsocket/IXWebSocket.h>
#include <ixcrypto/IXHMac.h>
#include "IXSnakeConnectionState.h"
#include "IXAppConfig.h" #include "IXAppConfig.h"
#include "IXSnakeConnectionState.h"
#include "nlohmann/json.hpp" #include "nlohmann/json.hpp"
#include <sstream>
#include <iostream> #include <iostream>
#include <ixcrypto/IXHMac.h>
#include <ixwebsocket/IXWebSocket.h>
#include <sstream>
namespace snake namespace snake
{ {
void handleError( void handleError(const std::string& action,
const std::string& action, std::shared_ptr<ix::WebSocket> ws,
std::shared_ptr<ix::WebSocket> ws, nlohmann::json pdu,
nlohmann::json pdu, const std::string& errMsg)
const std::string& errMsg)
{ {
std::string actionError(action); std::string actionError(action);
actionError += "/error"; actionError += "/error";
nlohmann::json response = { nlohmann::json response = {
{"action", actionError}, {"action", actionError}, {"id", pdu.value("id", 1)}, {"body", {{"reason", errMsg}}}};
{"id", pdu.value("id", 1)},
{"body", {
{"reason", errMsg}
}}
};
ws->sendText(response.dump()); ws->sendText(response.dump());
} }
void handleHandshake( void handleHandshake(std::shared_ptr<SnakeConnectionState> state,
std::shared_ptr<SnakeConnectionState> state, std::shared_ptr<ix::WebSocket> ws,
std::shared_ptr<ix::WebSocket> ws, const nlohmann::json& pdu)
const nlohmann::json& pdu)
{ {
std::string role = pdu["body"]["data"]["role"]; std::string role = pdu["body"]["data"]["role"];
@ -50,13 +41,10 @@ namespace snake
nlohmann::json response = { nlohmann::json response = {
{"action", "auth/handshake/ok"}, {"action", "auth/handshake/ok"},
{"id", pdu.value("id", 1)}, {"id", pdu.value("id", 1)},
{"body", { {"body",
{"data", { {
{"nonce", state->getNonce()}, {"data", {{"nonce", state->getNonce()}, {"connection_id", state->getId()}}},
{"connection_id", state->getId()} }}};
}},
}}
};
auto serializedResponse = response.dump(); auto serializedResponse = response.dump();
std::cout << "response = " << serializedResponse << std::endl; std::cout << "response = " << serializedResponse << std::endl;
@ -64,11 +52,10 @@ namespace snake
ws->sendText(serializedResponse); ws->sendText(serializedResponse);
} }
void handleAuth( void handleAuth(std::shared_ptr<SnakeConnectionState> state,
std::shared_ptr<SnakeConnectionState> state, std::shared_ptr<ix::WebSocket> ws,
std::shared_ptr<ix::WebSocket> ws, const AppConfig& appConfig,
const AppConfig& appConfig, const nlohmann::json& pdu)
const nlohmann::json& pdu)
{ {
auto secret = getRoleSecret(appConfig, state->appkey(), state->role()); auto secret = getRoleSecret(appConfig, state->appkey(), state->role());
std::cout << "secret = " << secret << std::endl; std::cout << "secret = " << secret << std::endl;
@ -78,11 +65,7 @@ namespace snake
nlohmann::json response = { nlohmann::json response = {
{"action", "auth/authenticate/error"}, {"action", "auth/authenticate/error"},
{"id", pdu.value("id", 1)}, {"id", pdu.value("id", 1)},
{"body", { {"body", {{"error", "authentication_failed"}, {"reason", "invalid secret"}}}};
{"error", "authentication_failed"},
{"reason", "invalid secret"}
}}
};
ws->sendText(response.dump()); ws->sendText(response.dump());
return; return;
} }
@ -102,28 +85,20 @@ namespace snake
nlohmann::json response = { nlohmann::json response = {
{"action", "auth/authenticate/error"}, {"action", "auth/authenticate/error"},
{"id", pdu.value("id", 1)}, {"id", pdu.value("id", 1)},
{"body", { {"body", {{"error", "authentication_failed"}, {"reason", "invalid hash"}}}};
{"error", "authentication_failed"},
{"reason", "invalid hash"}
}}
};
ws->sendText(response.dump()); ws->sendText(response.dump());
return; return;
} }
nlohmann::json response = { nlohmann::json response = {
{"action", "auth/authenticate/ok"}, {"action", "auth/authenticate/ok"}, {"id", pdu.value("id", 1)}, {"body", {}}};
{"id", pdu.value("id", 1)},
{"body", {}}
};
ws->sendText(response.dump()); ws->sendText(response.dump());
} }
void handlePublish( void handlePublish(std::shared_ptr<SnakeConnectionState> state,
std::shared_ptr<SnakeConnectionState> state, std::shared_ptr<ix::WebSocket> ws,
std::shared_ptr<ix::WebSocket> ws, const nlohmann::json& pdu)
const nlohmann::json& pdu)
{ {
std::vector<std::string> channels; std::vector<std::string> channels;
@ -150,9 +125,7 @@ namespace snake
for (auto&& channel : channels) for (auto&& channel : channels)
{ {
std::stringstream ss; std::stringstream ss;
ss << state->appkey() ss << state->appkey() << "::" << channel;
<< "::"
<< channel;
std::string errMsg; std::string errMsg;
if (!state->redisClient().publish(ss.str(), pdu.dump(), errMsg)) if (!state->redisClient().publish(ss.str(), pdu.dump(), errMsg))
@ -165,10 +138,7 @@ namespace snake
} }
nlohmann::json response = { nlohmann::json response = {
{"action", "rtm/publish/ok"}, {"action", "rtm/publish/ok"}, {"id", pdu.value("id", 1)}, {"body", {}}};
{"id", pdu.value("id", 1)},
{"body", {}}
};
ws->sendText(response.dump()); ws->sendText(response.dump());
} }
@ -176,19 +146,16 @@ namespace snake
// //
// FIXME: this is not cancellable. We should be able to cancel the redis subscription // FIXME: this is not cancellable. We should be able to cancel the redis subscription
// //
void handleRedisSubscription( void handleRedisSubscription(std::shared_ptr<SnakeConnectionState> state,
std::shared_ptr<SnakeConnectionState> state, std::shared_ptr<ix::WebSocket> ws,
std::shared_ptr<ix::WebSocket> ws, const AppConfig& appConfig,
const AppConfig& appConfig, const nlohmann::json& pdu)
const nlohmann::json& pdu)
{ {
std::string channel = pdu["body"]["channel"]; std::string channel = pdu["body"]["channel"];
std::string subscriptionId = channel; std::string subscriptionId = channel;
std::stringstream ss; std::stringstream ss;
ss << state->appkey() ss << state->appkey() << "::" << channel;
<< "::"
<< channel;
std::string appChannel(ss.str()); std::string appChannel(ss.str());
@ -224,8 +191,7 @@ namespace snake
} }
int id = 0; int id = 0;
auto callback = [ws, &id, &subscriptionId](const std::string& messageStr) auto callback = [ws, &id, &subscriptionId](const std::string& messageStr) {
{
auto msg = nlohmann::json::parse(messageStr); auto msg = nlohmann::json::parse(messageStr);
msg = msg["body"]["message"]; msg = msg["body"]["message"];
@ -233,27 +199,18 @@ namespace snake
nlohmann::json response = { nlohmann::json response = {
{"action", "rtm/subscription/data"}, {"action", "rtm/subscription/data"},
{"id", id++}, {"id", id++},
{"body", { {"body", {{"subscription_id", subscriptionId}, {"messages", {msg}}}}};
{"subscription_id", subscriptionId},
{"messages", {msg}}
}}
};
ws->sendText(response.dump()); ws->sendText(response.dump());
}; };
auto responseCallback = [ws, pdu, &subscriptionId](const std::string& redisResponse) auto responseCallback = [ws, pdu, &subscriptionId](const std::string& redisResponse) {
{
std::cout << "Redis subscribe response: " << redisResponse << std::endl; std::cout << "Redis subscribe response: " << redisResponse << std::endl;
// Success // Success
nlohmann::json response = { nlohmann::json response = {{"action", "rtm/subscribe/ok"},
{"action", "rtm/subscribe/ok"}, {"id", pdu.value("id", 1)},
{"id", pdu.value("id", 1)}, {"body", {{"subscription_id", subscriptionId}}}};
{"body", {
{"subscription_id", subscriptionId}
}}
};
ws->sendText(response.dump()); ws->sendText(response.dump());
}; };
@ -267,24 +224,18 @@ namespace snake
} }
} }
void handleSubscribe( void handleSubscribe(std::shared_ptr<SnakeConnectionState> state,
std::shared_ptr<SnakeConnectionState> state, std::shared_ptr<ix::WebSocket> ws,
std::shared_ptr<ix::WebSocket> ws, const AppConfig& appConfig,
const AppConfig& appConfig, const nlohmann::json& pdu)
const nlohmann::json& pdu)
{ {
state->fut = std::async(std::launch::async, state->fut =
handleRedisSubscription, std::async(std::launch::async, handleRedisSubscription, state, ws, appConfig, pdu);
state,
ws,
appConfig,
pdu);
} }
void handleUnSubscribe( void handleUnSubscribe(std::shared_ptr<SnakeConnectionState> state,
std::shared_ptr<SnakeConnectionState> state, std::shared_ptr<ix::WebSocket> ws,
std::shared_ptr<ix::WebSocket> ws, const nlohmann::json& pdu)
const nlohmann::json& pdu)
{ {
// extract subscription_id // extract subscription_id
auto body = pdu["body"]; auto body = pdu["body"];
@ -292,21 +243,16 @@ namespace snake
state->redisClient().stop(); state->redisClient().stop();
nlohmann::json response = { nlohmann::json response = {{"action", "rtm/unsubscribe/ok"},
{"action", "rtm/unsubscribe/ok"}, {"id", pdu.value("id", 1)},
{"id", pdu.value("id", 1)}, {"body", {{"subscription_id", subscriptionId}}}};
{"body", {
{"subscription_id", subscriptionId}
}}
};
ws->sendText(response.dump()); ws->sendText(response.dump());
} }
void processCobraMessage( void processCobraMessage(std::shared_ptr<SnakeConnectionState> state,
std::shared_ptr<SnakeConnectionState> state, std::shared_ptr<ix::WebSocket> ws,
std::shared_ptr<ix::WebSocket> ws, const AppConfig& appConfig,
const AppConfig& appConfig, const std::string& str)
const std::string& str)
{ {
auto pdu = nlohmann::json::parse(str); auto pdu = nlohmann::json::parse(str);
std::cout << "Got " << str << std::endl; std::cout << "Got " << str << std::endl;
@ -339,4 +285,4 @@ namespace snake
std::cerr << "Unhandled action: " << action << std::endl; std::cerr << "Unhandled action: " << action << std::endl;
} }
} }
} } // namespace snake

View File

@ -5,18 +5,18 @@
*/ */
#include "IXSnakeServer.h" #include "IXSnakeServer.h"
#include "IXSnakeProtocol.h"
#include "IXSnakeConnectionState.h"
#include "IXAppConfig.h"
#include "IXAppConfig.h"
#include "IXSnakeConnectionState.h"
#include "IXSnakeProtocol.h"
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
namespace snake namespace snake
{ {
SnakeServer::SnakeServer(const AppConfig& appConfig) : SnakeServer::SnakeServer(const AppConfig& appConfig)
_appConfig(appConfig), : _appConfig(appConfig)
_server(appConfig.port, appConfig.hostname) , _server(appConfig.port, appConfig.hostname)
{ {
; ;
} }
@ -32,7 +32,7 @@ namespace snake
idx = path.rfind('='); idx = path.rfind('=');
if (idx != std::string::npos) if (idx != std::string::npos)
{ {
std::string appkey = path.substr(idx+1); std::string appkey = path.substr(idx + 1);
return appkey; return appkey;
} }
else else
@ -45,21 +45,18 @@ namespace snake
{ {
std::cout << "Listening on " << _appConfig.hostname << ":" << _appConfig.port << std::endl; std::cout << "Listening on " << _appConfig.hostname << ":" << _appConfig.port << std::endl;
auto factory = []() -> std::shared_ptr<ix::ConnectionState> auto factory = []() -> std::shared_ptr<ix::ConnectionState> {
{
return std::make_shared<SnakeConnectionState>(); return std::make_shared<SnakeConnectionState>();
}; };
_server.setConnectionStateFactory(factory); _server.setConnectionStateFactory(factory);
_server.setOnConnectionCallback( _server.setOnConnectionCallback(
[this](std::shared_ptr<ix::WebSocket> webSocket, [this](std::shared_ptr<ix::WebSocket> webSocket,
std::shared_ptr<ix::ConnectionState> connectionState) std::shared_ptr<ix::ConnectionState> connectionState) {
{
auto state = std::dynamic_pointer_cast<SnakeConnectionState>(connectionState); auto state = std::dynamic_pointer_cast<SnakeConnectionState>(connectionState);
webSocket->setOnMessageCallback( webSocket->setOnMessageCallback(
[this, webSocket, state](const ix::WebSocketMessagePtr& msg) [this, webSocket, state](const ix::WebSocketMessagePtr& msg) {
{
if (msg->type == ix::WebSocketMessageType::Open) if (msg->type == ix::WebSocketMessageType::Open)
{ {
std::cerr << "New connection" << std::endl; std::cerr << "New connection" << std::endl;
@ -84,16 +81,16 @@ namespace snake
else if (msg->type == ix::WebSocketMessageType::Close) else if (msg->type == ix::WebSocketMessageType::Close)
{ {
std::cerr << "Closed connection" std::cerr << "Closed connection"
<< " code " << msg->closeInfo.code << " code " << msg->closeInfo.code << " reason "
<< " reason " << msg->closeInfo.reason << std::endl; << msg->closeInfo.reason << std::endl;
} }
else if (msg->type == ix::WebSocketMessageType::Error) else if (msg->type == ix::WebSocketMessageType::Error)
{ {
std::stringstream ss; std::stringstream ss;
ss << "Connection error: " << msg->errorInfo.reason << std::endl; ss << "Connection error: " << msg->errorInfo.reason << std::endl;
ss << "#retries: " << msg->errorInfo.retries << std::endl; ss << "#retries: " << msg->errorInfo.retries << std::endl;
ss << "Wait time(ms): " << msg->errorInfo.wait_time << std::endl; ss << "Wait time(ms): " << msg->errorInfo.wait_time << std::endl;
ss << "HTTP Status: " << msg->errorInfo.http_status << std::endl; ss << "HTTP Status: " << msg->errorInfo.http_status << std::endl;
std::cerr << ss.str(); std::cerr << ss.str();
} }
else if (msg->type == ix::WebSocketMessageType::Fragment) else if (msg->type == ix::WebSocketMessageType::Fragment)
@ -105,10 +102,8 @@ namespace snake
std::cerr << "Received " << msg->wireSize << " bytes" << std::endl; std::cerr << "Received " << msg->wireSize << " bytes" << std::endl;
processCobraMessage(state, webSocket, _appConfig, msg->str); processCobraMessage(state, webSocket, _appConfig, msg->str);
} }
} });
); });
}
);
auto res = _server.listen(); auto res = _server.listen();
if (!res.first) if (!res.first)
@ -133,4 +128,4 @@ namespace snake
{ {
_server.stop(); _server.stop();
} }
} } // namespace snake

112
ws/ws.cpp
View File

@ -9,27 +9,22 @@
// //
#include "ws.h" #include "ws.h"
#include <string>
#include <sstream>
#include <iostream>
#include <fstream>
#include <cli11/CLI11.hpp> #include <cli11/CLI11.hpp>
#include <spdlog/spdlog.h> #include <fstream>
#include <iostream>
#include <ixwebsocket/IXSocket.h>
#include <ixwebsocket/IXNetSystem.h>
#include <ixcore/utils/IXCoreLogger.h> #include <ixcore/utils/IXCoreLogger.h>
#include <ixwebsocket/IXNetSystem.h>
#include <ixwebsocket/IXSocket.h>
#include <spdlog/spdlog.h>
#include <sstream>
#include <string>
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
ix::initNetSystem(); ix::initNetSystem();
ix::IXCoreLogger::LogFunc logFunc = [](const char* msg) ix::IXCoreLogger::LogFunc logFunc = [](const char* msg) { spdlog::info(msg); };
{
spdlog::info(msg);
};
ix::IXCoreLogger::setLogFunction(logFunc); ix::IXCoreLogger::setLogFunction(logFunc);
// Display command. // Display command.
@ -43,7 +38,7 @@ int main(int argc, char** argv)
std::cout << std::endl; std::cout << std::endl;
} }
CLI::App app{"ws is a websocket tool"}; CLI::App app {"ws is a websocket tool"};
app.require_subcommand(); app.require_subcommand();
std::string url("ws://127.0.0.1:8008"); std::string url("ws://127.0.0.1:8008");
@ -94,13 +89,16 @@ int main(int argc, char** argv)
CLI::App* sendApp = app.add_subcommand("send", "Send a file"); CLI::App* sendApp = app.add_subcommand("send", "Send a file");
sendApp->add_option("url", url, "Connection url")->required(); sendApp->add_option("url", url, "Connection url")->required();
sendApp->add_option("path", path, "Path to the file to send") sendApp->add_option("path", path, "Path to the file to send")
->required()->check(CLI::ExistingPath); ->required()
->check(CLI::ExistingPath);
sendApp->add_option("--pidfile", pidfile, "Pid file"); sendApp->add_option("--pidfile", pidfile, "Pid file");
CLI::App* receiveApp = app.add_subcommand("receive", "Receive a file"); CLI::App* receiveApp = app.add_subcommand("receive", "Receive a file");
receiveApp->add_option("url", url, "Connection url")->required(); receiveApp->add_option("url", url, "Connection url")->required();
receiveApp->add_option("--delay", delayMs, "Delay (ms) to wait after receiving a fragment" receiveApp->add_option("--delay",
" to artificially slow down the receiver"); delayMs,
"Delay (ms) to wait after receiving a fragment"
" to artificially slow down the receiver");
receiveApp->add_option("--pidfile", pidfile, "Pid file"); receiveApp->add_option("--pidfile", pidfile, "Pid file");
CLI::App* transferApp = app.add_subcommand("transfer", "Broadcasting server"); CLI::App* transferApp = app.add_subcommand("transfer", "Broadcasting server");
@ -114,7 +112,9 @@ int main(int argc, char** argv)
connectApp->add_flag("-d", disableAutomaticReconnection, "Disable Automatic Reconnection"); connectApp->add_flag("-d", disableAutomaticReconnection, "Disable Automatic Reconnection");
connectApp->add_flag("-x", disablePerMessageDeflate, "Disable per message deflate"); connectApp->add_flag("-x", disablePerMessageDeflate, "Disable per message deflate");
connectApp->add_flag("-b", binaryMode, "Send in binary mode"); connectApp->add_flag("-b", binaryMode, "Send in binary mode");
connectApp->add_option("--max_wait", maxWaitBetweenReconnectionRetries, "Max Wait Time between reconnection retries"); connectApp->add_option("--max_wait",
maxWaitBetweenReconnectionRetries,
"Max Wait Time between reconnection retries");
CLI::App* chatApp = app.add_subcommand("chat", "Group chat"); CLI::App* chatApp = app.add_subcommand("chat", "Group chat");
chatApp->add_option("url", url, "Connection url")->required(); chatApp->add_option("url", url, "Connection url")->required();
@ -181,9 +181,11 @@ int main(int argc, char** argv)
cobraPublish->add_option("channel", channel, "Channel")->required(); cobraPublish->add_option("channel", channel, "Channel")->required();
cobraPublish->add_option("--pidfile", pidfile, "Pid file"); cobraPublish->add_option("--pidfile", pidfile, "Pid file");
cobraPublish->add_option("path", path, "Path to the file to send") cobraPublish->add_option("path", path, "Path to the file to send")
->required()->check(CLI::ExistingPath); ->required()
->check(CLI::ExistingPath);
CLI::App* cobraMetricsPublish = app.add_subcommand("cobra_metrics_publish", "Cobra metrics publisher"); CLI::App* cobraMetricsPublish =
app.add_subcommand("cobra_metrics_publish", "Cobra metrics publisher");
cobraMetricsPublish->add_option("--appkey", appkey, "Appkey"); cobraMetricsPublish->add_option("--appkey", appkey, "Appkey");
cobraMetricsPublish->add_option("--endpoint", endpoint, "Endpoint"); cobraMetricsPublish->add_option("--endpoint", endpoint, "Endpoint");
cobraMetricsPublish->add_option("--rolename", rolename, "Role name"); cobraMetricsPublish->add_option("--rolename", rolename, "Role name");
@ -191,7 +193,8 @@ int main(int argc, char** argv)
cobraMetricsPublish->add_option("channel", channel, "Channel")->required(); cobraMetricsPublish->add_option("channel", channel, "Channel")->required();
cobraMetricsPublish->add_option("--pidfile", pidfile, "Pid file"); cobraMetricsPublish->add_option("--pidfile", pidfile, "Pid file");
cobraMetricsPublish->add_option("path", path, "Path to the file to send") cobraMetricsPublish->add_option("path", path, "Path to the file to send")
->required()->check(CLI::ExistingPath); ->required()
->check(CLI::ExistingPath);
cobraMetricsPublish->add_flag("--stress", stress, "Stress mode"); cobraMetricsPublish->add_flag("--stress", stress, "Stress mode");
CLI::App* cobra2statsd = app.add_subcommand("cobra_to_statsd", "Cobra to statsd"); CLI::App* cobra2statsd = app.add_subcommand("cobra_to_statsd", "Cobra to statsd");
@ -269,8 +272,11 @@ int main(int argc, char** argv)
} }
else if (app.got_subcommand("connect")) else if (app.got_subcommand("connect"))
{ {
ret = ix::ws_connect_main(url, headers, disableAutomaticReconnection, ret = ix::ws_connect_main(url,
disablePerMessageDeflate, binaryMode, headers,
disableAutomaticReconnection,
disablePerMessageDeflate,
binaryMode,
maxWaitBetweenReconnectionRetries); maxWaitBetweenReconnectionRetries);
} }
else if (app.got_subcommand("chat")) else if (app.got_subcommand("chat"))
@ -291,15 +297,22 @@ int main(int argc, char** argv)
} }
else if (app.got_subcommand("curl")) else if (app.got_subcommand("curl"))
{ {
ret = ix::ws_http_client_main(url, headers, data, headersOnly, ret = ix::ws_http_client_main(url,
connectTimeOut, transferTimeout, headers,
followRedirects, maxRedirects, verbose, data,
save, output, compress); headersOnly,
connectTimeOut,
transferTimeout,
followRedirects,
maxRedirects,
verbose,
save,
output,
compress);
} }
else if (app.got_subcommand("redis_publish")) else if (app.got_subcommand("redis_publish"))
{ {
ret = ix::ws_redis_publish_main(hostname, redisPort, password, ret = ix::ws_redis_publish_main(hostname, redisPort, password, channel, message, count);
channel, message, count);
} }
else if (app.got_subcommand("redis_subscribe")) else if (app.got_subcommand("redis_subscribe"))
{ {
@ -307,42 +320,41 @@ int main(int argc, char** argv)
} }
else if (app.got_subcommand("cobra_subscribe")) else if (app.got_subcommand("cobra_subscribe"))
{ {
ret = ix::ws_cobra_subscribe_main(appkey, endpoint, ret = ix::ws_cobra_subscribe_main(
rolename, rolesecret, appkey, endpoint, rolename, rolesecret, channel, filter, quiet);
channel, filter, quiet);
} }
else if (app.got_subcommand("cobra_publish")) else if (app.got_subcommand("cobra_publish"))
{ {
ret = ix::ws_cobra_publish_main(appkey, endpoint, ret = ix::ws_cobra_publish_main(appkey, endpoint, rolename, rolesecret, channel, path);
rolename, rolesecret,
channel, path);
} }
else if (app.got_subcommand("cobra_metrics_publish")) else if (app.got_subcommand("cobra_metrics_publish"))
{ {
ret = ix::ws_cobra_metrics_publish_main(appkey, endpoint, ret = ix::ws_cobra_metrics_publish_main(
rolename, rolesecret, appkey, endpoint, rolename, rolesecret, channel, path, stress);
channel, path, stress);
} }
else if (app.got_subcommand("cobra_to_statsd")) else if (app.got_subcommand("cobra_to_statsd"))
{ {
ret = ix::ws_cobra_to_statsd_main(appkey, endpoint, ret = ix::ws_cobra_to_statsd_main(appkey,
rolename, rolesecret, endpoint,
channel, filter, hostname, statsdPort, rolename,
prefix, fields, verbose); rolesecret,
channel,
filter,
hostname,
statsdPort,
prefix,
fields,
verbose);
} }
else if (app.got_subcommand("cobra_to_sentry")) else if (app.got_subcommand("cobra_to_sentry"))
{ {
ret = ix::ws_cobra_to_sentry_main(appkey, endpoint, ret = ix::ws_cobra_to_sentry_main(
rolename, rolesecret, appkey, endpoint, rolename, rolesecret, channel, filter, dsn, verbose, strict, jobs);
channel, filter, dsn,
verbose, strict, jobs);
} }
else if (app.got_subcommand("snake")) else if (app.got_subcommand("snake"))
{ {
ret = ix::ws_snake_main(port, hostname, ret = ix::ws_snake_main(
redisHosts, redisPort, port, hostname, redisHosts, redisPort, redisPassword, verbose, appsConfigPath);
redisPassword, verbose,
appsConfigPath);
} }
else if (app.got_subcommand("httpd")) else if (app.got_subcommand("httpd"))
{ {

View File

@ -21,7 +21,8 @@
// //
// //
// 2 Run the test server (using docker) // 2 Run the test server (using docker)
// docker run -it --rm -v "${PWD}/config:/config" -v "${PWD}/reports:/reports" -p 9001:9001 --name fuzzingserver crossbario/autobahn-testsuite // docker run -it --rm -v "${PWD}/config:/config" -v "${PWD}/reports:/reports" -p 9001:9001 --name
// fuzzingserver crossbario/autobahn-testsuite
// //
// 3. Run this command // 3. Run this command
// ws autobahn -q --url ws://localhost:9001 // ws autobahn -q --url ws://localhost:9001
@ -29,15 +30,14 @@
// 4. A HTML report will be generated, you can inspect it to see if you are compliant or not // 4. A HTML report will be generated, you can inspect it to see if you are compliant or not
// //
#include <iostream>
#include <sstream>
#include <atomic> #include <atomic>
#include <mutex>
#include <condition_variable> #include <condition_variable>
#include <ixwebsocket/IXWebSocket.h> #include <iostream>
#include <ixwebsocket/IXSocket.h> #include <ixwebsocket/IXSocket.h>
#include <ixwebsocket/IXWebSocket.h>
#include <mutex>
#include <spdlog/spdlog.h> #include <spdlog/spdlog.h>
#include <sstream>
namespace namespace
@ -53,31 +53,31 @@ namespace
return str.substr(0, n) + "..."; return str.substr(0, n) + "...";
} }
} }
} } // namespace
namespace ix namespace ix
{ {
class AutobahnTestCase class AutobahnTestCase
{ {
public: public:
AutobahnTestCase(const std::string& _url, bool quiet); AutobahnTestCase(const std::string& _url, bool quiet);
void run(); void run();
private: private:
void log(const std::string& msg); void log(const std::string& msg);
std::string _url; std::string _url;
ix::WebSocket _webSocket; ix::WebSocket _webSocket;
bool _quiet; bool _quiet;
std::mutex _mutex; std::mutex _mutex;
std::condition_variable _condition; std::condition_variable _condition;
}; };
AutobahnTestCase::AutobahnTestCase(const std::string& url, bool quiet) : AutobahnTestCase::AutobahnTestCase(const std::string& url, bool quiet)
_url(url), : _url(url)
_quiet(quiet) , _quiet(quiet)
{ {
_webSocket.disableAutomaticReconnection(); _webSocket.disableAutomaticReconnection();
@ -102,67 +102,63 @@ namespace ix
std::stringstream ss; std::stringstream ss;
log(std::string("Connecting to url: ") + _url); log(std::string("Connecting to url: ") + _url);
_webSocket.setOnMessageCallback( _webSocket.setOnMessageCallback([this](const ix::WebSocketMessagePtr& msg) {
[this](const ix::WebSocketMessagePtr& msg) std::stringstream ss;
if (msg->type == ix::WebSocketMessageType::Open)
{ {
std::stringstream ss; log("autobahn: connected");
if (msg->type == ix::WebSocketMessageType::Open) ss << "Uri: " << msg->openInfo.uri << std::endl;
ss << "Handshake Headers:" << std::endl;
for (auto it : msg->openInfo.headers)
{ {
log("autobahn: connected"); ss << it.first << ": " << it.second << std::endl;
ss << "Uri: " << msg->openInfo.uri << std::endl;
ss << "Handshake Headers:" << std::endl;
for (auto it : msg->openInfo.headers)
{
ss << it.first << ": " << it.second << std::endl;
}
} }
else if (msg->type == ix::WebSocketMessageType::Close) }
{ else if (msg->type == ix::WebSocketMessageType::Close)
ss << "autobahn: connection closed:"; {
ss << " code " << msg->closeInfo.code; ss << "autobahn: connection closed:";
ss << " reason " << msg->closeInfo.reason << std::endl; ss << " code " << msg->closeInfo.code;
ss << " reason " << msg->closeInfo.reason << std::endl;
_condition.notify_one(); _condition.notify_one();
} }
else if (msg->type == ix::WebSocketMessageType::Message) else if (msg->type == ix::WebSocketMessageType::Message)
{ {
ss << "Received " << msg->wireSize << " bytes" << std::endl; ss << "Received " << msg->wireSize << " bytes" << std::endl;
ss << "autobahn: received message: " ss << "autobahn: received message: " << truncate(msg->str, 40) << std::endl;
<< truncate(msg->str, 40)
<< std::endl;
_webSocket.send(msg->str, msg->binary); _webSocket.send(msg->str, msg->binary);
} }
else if (msg->type == ix::WebSocketMessageType::Error) else if (msg->type == ix::WebSocketMessageType::Error)
{ {
ss << "Connection error: " << msg->errorInfo.reason << std::endl; ss << "Connection error: " << msg->errorInfo.reason << std::endl;
ss << "#retries: " << msg->errorInfo.retries << std::endl; ss << "#retries: " << msg->errorInfo.retries << std::endl;
ss << "Wait time(ms): " << msg->errorInfo.wait_time << std::endl; ss << "Wait time(ms): " << msg->errorInfo.wait_time << std::endl;
ss << "HTTP Status: " << msg->errorInfo.http_status << std::endl; ss << "HTTP Status: " << msg->errorInfo.http_status << std::endl;
// And error can happen, in which case the test-case is marked done // And error can happen, in which case the test-case is marked done
_condition.notify_one(); _condition.notify_one();
} }
else if (msg->type == ix::WebSocketMessageType::Fragment) else if (msg->type == ix::WebSocketMessageType::Fragment)
{ {
ss << "Received message fragment" << std::endl; ss << "Received message fragment" << std::endl;
} }
else if (msg->type == ix::WebSocketMessageType::Ping) else if (msg->type == ix::WebSocketMessageType::Ping)
{ {
ss << "Received ping" << std::endl; ss << "Received ping" << std::endl;
} }
else if (msg->type == ix::WebSocketMessageType::Pong) else if (msg->type == ix::WebSocketMessageType::Pong)
{ {
ss << "Received pong" << std::endl; ss << "Received pong" << std::endl;
} }
else else
{ {
ss << "Invalid ix::WebSocketMessageType" << std::endl; ss << "Invalid ix::WebSocketMessageType" << std::endl;
} }
log(ss.str()); log(ss.str());
}); });
_webSocket.start(); _webSocket.start();
@ -184,27 +180,24 @@ namespace ix
std::atomic<bool> success(true); std::atomic<bool> success(true);
std::condition_variable condition; std::condition_variable condition;
webSocket.setOnMessageCallback( webSocket.setOnMessageCallback([&condition, &success](const ix::WebSocketMessagePtr& msg) {
[&condition, &success](const ix::WebSocketMessagePtr& msg) if (msg->type == ix::WebSocketMessageType::Close)
{ {
if (msg->type == ix::WebSocketMessageType::Close) std::cerr << "Report generated" << std::endl;
{ condition.notify_one();
std::cerr << "Report generated" << std::endl;
condition.notify_one();
}
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() << std::endl;
success = false;
}
} }
); 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() << std::endl;
success = false;
}
});
webSocket.start(); webSocket.start();
std::mutex mutex; std::mutex mutex;
@ -231,33 +224,30 @@ namespace ix
int count = -1; int count = -1;
std::condition_variable condition; std::condition_variable condition;
webSocket.setOnMessageCallback( webSocket.setOnMessageCallback([&condition, &count](const ix::WebSocketMessagePtr& msg) {
[&condition, &count](const ix::WebSocketMessagePtr& msg) if (msg->type == ix::WebSocketMessageType::Close)
{ {
if (msg->type == ix::WebSocketMessageType::Close) condition.notify_one();
{
condition.notify_one();
}
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() << std::endl;
condition.notify_one();
}
else if (msg->type == ix::WebSocketMessageType::Message)
{
// response is a string
std::stringstream ss;
ss << msg->str;
ss >> count;
}
} }
); 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() << std::endl;
condition.notify_one();
}
else if (msg->type == ix::WebSocketMessageType::Message)
{
// response is a string
std::stringstream ss;
ss << msg->str;
ss >> count;
}
});
webSocket.start(); webSocket.start();
std::mutex mutex; std::mutex mutex;
@ -289,17 +279,14 @@ namespace ix
testCasesCount++; testCasesCount++;
for (int i = 1 ; i < testCasesCount; ++i) for (int i = 1; i < testCasesCount; ++i)
{ {
spdlog::info("Execute test case {}", i); spdlog::info("Execute test case {}", i);
int caseNumber = i; int caseNumber = i;
std::stringstream ss; std::stringstream ss;
ss << url ss << url << "/runCase?case=" << caseNumber << "&agent=ixwebsocket";
<< "/runCase?case="
<< caseNumber
<< "&agent=ixwebsocket";
std::string url(ss.str()); std::string url(ss.str());
@ -309,4 +296,4 @@ namespace ix
return generateReport(url) ? 0 : 1; return generateReport(url) ? 0 : 1;
} }
} } // namespace ix

View File

@ -5,8 +5,8 @@
*/ */
#include <iostream> #include <iostream>
#include <sstream>
#include <ixwebsocket/IXWebSocketServer.h> #include <ixwebsocket/IXWebSocketServer.h>
#include <sstream>
namespace ix namespace ix
{ {
@ -16,75 +16,67 @@ namespace ix
ix::WebSocketServer server(port, hostname); ix::WebSocketServer server(port, hostname);
server.setOnConnectionCallback( server.setOnConnectionCallback([&server](std::shared_ptr<WebSocket> webSocket,
[&server](std::shared_ptr<WebSocket> webSocket, std::shared_ptr<ConnectionState> connectionState) {
std::shared_ptr<ConnectionState> connectionState) webSocket->setOnMessageCallback([webSocket, connectionState, &server](
{ const WebSocketMessagePtr& msg) {
webSocket->setOnMessageCallback( if (msg->type == ix::WebSocketMessageType::Open)
[webSocket, connectionState, &server](const WebSocketMessagePtr& msg) {
std::cerr << "New connection" << std::endl;
std::cerr << "id: " << connectionState->getId() << std::endl;
std::cerr << "Uri: " << msg->openInfo.uri << std::endl;
std::cerr << "Headers:" << std::endl;
for (auto it : msg->openInfo.headers)
{ {
if (msg->type == ix::WebSocketMessageType::Open) 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;
}
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::Fragment)
{
std::cerr << "Received message fragment" << std::endl;
}
else if (msg->type == ix::WebSocketMessageType::Message)
{
std::cerr << "Received " << msg->wireSize << " bytes" << std::endl;
for (auto&& client : server.getClients())
{
if (client != webSocket)
{ {
std::cerr << "New connection" << std::endl; client->send(msg->str, msg->binary, [](int current, int total) -> bool {
std::cerr << "id: " << connectionState->getId() << std::endl; std::cerr << "Step " << current << " out of " << total << std::endl;
std::cerr << "Uri: " << msg->openInfo.uri << std::endl; return true;
std::cerr << "Headers:" << std::endl; });
for (auto it : msg->openInfo.headers)
do
{ {
std::cerr << it.first << ": " << it.second << std::endl; size_t bufferedAmount = client->bufferedAmount();
} std::cerr << bufferedAmount << " bytes left to be sent"
} << std::endl;
else if (msg->type == ix::WebSocketMessageType::Close)
{
std::cerr << "Closed connection"
<< " code " << msg->closeInfo.code
<< " reason " << msg->closeInfo.reason << std::endl;
}
else if (msg->type == ix::WebSocketMessageType::Error)
{
std::stringstream ss;
ss << "Connection error: " << msg->errorInfo.reason << std::endl;
ss << "#retries: " << msg->errorInfo.retries << std::endl;
ss << "Wait time(ms): " << msg->errorInfo.wait_time << std::endl;
ss << "HTTP Status: " << msg->errorInfo.http_status << std::endl;
std::cerr << ss.str();
}
else if (msg->type == ix::WebSocketMessageType::Fragment)
{
std::cerr << "Received message fragment" << std::endl;
}
else if (msg->type == ix::WebSocketMessageType::Message)
{
std::cerr << "Received " << msg->wireSize << " bytes" << std::endl;
for (auto&& client : server.getClients()) std::chrono::duration<double, std::milli> duration(10);
{ std::this_thread::sleep_for(duration);
if (client != webSocket) } while (client->bufferedAmount() != 0);
{
client->send(msg->str,
msg->binary,
[](int current, int total) -> bool
{
std::cerr << "Step " << current
<< " out of " << total << std::endl;
return true;
});
do
{
size_t bufferedAmount = client->bufferedAmount();
std::cerr << bufferedAmount << " bytes left to be sent" << std::endl;
std::chrono::duration<double, std::milli> duration(10);
std::this_thread::sleep_for(duration);
} while (client->bufferedAmount() != 0);
}
}
} }
} }
); }
} });
); });
auto res = server.listen(); auto res = server.listen();
if (!res.first) if (!res.first)
@ -98,4 +90,4 @@ namespace ix
return 0; return 0;
} }
} } // namespace ix

View File

@ -9,13 +9,12 @@
// Broadcast server can be ran with `ws broadcast_server` // Broadcast server can be ran with `ws broadcast_server`
// //
#include <iostream>
#include <sstream>
#include <queue>
#include <ixwebsocket/IXWebSocket.h>
#include <ixwebsocket/IXSocket.h>
#include "nlohmann/json.hpp" #include "nlohmann/json.hpp"
#include <iostream>
#include <ixwebsocket/IXSocket.h>
#include <ixwebsocket/IXWebSocket.h>
#include <queue>
#include <sstream>
// for convenience // for convenience
using json = nlohmann::json; using json = nlohmann::json;
@ -24,34 +23,32 @@ namespace ix
{ {
class WebSocketChat class WebSocketChat
{ {
public: public:
WebSocketChat(const std::string& url, WebSocketChat(const std::string& url, const std::string& user);
const std::string& user);
void subscribe(const std::string& channel); void subscribe(const std::string& channel);
void start(); void start();
void stop(); void stop();
bool isReady() const; bool isReady() const;
void sendMessage(const std::string& text); void sendMessage(const std::string& text);
size_t getReceivedMessagesCount() const; size_t getReceivedMessagesCount() const;
std::string encodeMessage(const std::string& text); std::string encodeMessage(const std::string& text);
std::pair<std::string, std::string> decodeMessage(const std::string& str); std::pair<std::string, std::string> decodeMessage(const std::string& str);
private: private:
std::string _url; std::string _url;
std::string _user; std::string _user;
ix::WebSocket _webSocket; ix::WebSocket _webSocket;
std::queue<std::string> _receivedQueue; std::queue<std::string> _receivedQueue;
void log(const std::string& msg); void log(const std::string& msg);
}; };
WebSocketChat::WebSocketChat(const std::string& url, WebSocketChat::WebSocketChat(const std::string& url, const std::string& user)
const std::string& user) : : _url(url)
_url(url), , _user(user)
_user(user)
{ {
; ;
} }
@ -83,64 +80,57 @@ namespace ix
std::stringstream ss; std::stringstream ss;
log(std::string("Connecting to url: ") + _url); log(std::string("Connecting to url: ") + _url);
_webSocket.setOnMessageCallback( _webSocket.setOnMessageCallback([this](const WebSocketMessagePtr& msg) {
[this](const WebSocketMessagePtr& msg) std::stringstream ss;
if (msg->type == ix::WebSocketMessageType::Open)
{ {
std::stringstream ss; log("ws chat: connected");
if (msg->type == ix::WebSocketMessageType::Open) std::cout << "Uri: " << msg->openInfo.uri << std::endl;
std::cout << "Handshake Headers:" << std::endl;
for (auto it : msg->openInfo.headers)
{ {
log("ws chat: connected"); std::cout << it.first << ": " << it.second << std::endl;
std::cout << "Uri: " << msg->openInfo.uri << std::endl; }
std::cout << "Handshake Headers:" << std::endl;
for (auto it : msg->openInfo.headers)
{
std::cout << it.first << ": " << it.second << std::endl;
}
ss << "ws chat: user " ss << "ws chat: user " << _user << " Connected !";
<< _user log(ss.str());
<< " Connected !"; }
log(ss.str()); else if (msg->type == ix::WebSocketMessageType::Close)
} {
else if (msg->type == ix::WebSocketMessageType::Close) ss << "ws chat: user " << _user << " disconnected !"
{ << " code " << msg->closeInfo.code << " reason " << msg->closeInfo.reason;
ss << "ws chat: user " log(ss.str());
<< _user }
<< " disconnected !" else if (msg->type == ix::WebSocketMessageType::Message)
<< " code " << msg->closeInfo.code {
<< " reason " << msg->closeInfo.reason; auto result = decodeMessage(msg->str);
log(ss.str());
}
else if (msg->type == ix::WebSocketMessageType::Message)
{
auto result = decodeMessage(msg->str);
// Our "chat" / "broacast" node.js server does not send us // Our "chat" / "broacast" node.js server does not send us
// the messages we send, so we don't have to filter it out. // the messages we send, so we don't have to filter it out.
// store text // store text
_receivedQueue.push(result.second); _receivedQueue.push(result.second);
ss << std::endl ss << std::endl
<< result.first << "(" << msg->wireSize << " bytes)" << " > " << result.second << result.first << "(" << msg->wireSize << " bytes)"
<< std::endl << " > " << result.second << std::endl
<< _user << " > "; << _user << " > ";
log(ss.str()); log(ss.str());
} }
else if (msg->type == ix::WebSocketMessageType::Error) else if (msg->type == ix::WebSocketMessageType::Error)
{ {
ss << "Connection error: " << msg->errorInfo.reason << std::endl; ss << "Connection error: " << msg->errorInfo.reason << std::endl;
ss << "#retries: " << msg->errorInfo.retries << std::endl; ss << "#retries: " << msg->errorInfo.retries << std::endl;
ss << "Wait time(ms): " << msg->errorInfo.wait_time << std::endl; ss << "Wait time(ms): " << msg->errorInfo.wait_time << std::endl;
ss << "HTTP Status: " << msg->errorInfo.http_status << std::endl; ss << "HTTP Status: " << msg->errorInfo.http_status << std::endl;
log(ss.str()); log(ss.str());
} }
else else
{ {
ss << "Invalid ix::WebSocketMessageType"; ss << "Invalid ix::WebSocketMessageType";
log(ss.str()); log(ss.str());
} }
}); });
_webSocket.start(); _webSocket.start();
} }
@ -170,8 +160,7 @@ namespace ix
_webSocket.sendText(encodeMessage(text)); _webSocket.sendText(encodeMessage(text));
} }
int ws_chat_main(const std::string& url, int ws_chat_main(const std::string& url, const std::string& user)
const std::string& user)
{ {
std::cout << "Type Ctrl-D to exit prompt..." << std::endl; std::cout << "Type Ctrl-D to exit prompt..." << std::endl;
WebSocketChat webSocketChat(url, user); WebSocketChat webSocketChat(url, user);
@ -196,4 +185,4 @@ namespace ix
return 0; return 0;
} }
} } // namespace ix

View File

@ -4,15 +4,15 @@
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved. * Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
*/ */
#include <iostream>
#include <fstream>
#include <sstream>
#include <chrono>
#include <thread>
#include <atomic> #include <atomic>
#include <jsoncpp/json/json.h> #include <chrono>
#include <fstream>
#include <iostream>
#include <ixcobra/IXCobraMetricsPublisher.h> #include <ixcobra/IXCobraMetricsPublisher.h>
#include <jsoncpp/json/json.h>
#include <spdlog/spdlog.h> #include <spdlog/spdlog.h>
#include <sstream>
#include <thread>
namespace ix namespace ix
{ {
@ -27,25 +27,23 @@ namespace ix
std::atomic<int> sentMessages(0); std::atomic<int> sentMessages(0);
std::atomic<int> ackedMessages(0); std::atomic<int> ackedMessages(0);
CobraConnection::setPublishTrackerCallback( CobraConnection::setPublishTrackerCallback(
[&sentMessages, &ackedMessages](bool sent, bool acked) [&sentMessages, &ackedMessages](bool sent, bool acked) {
{
if (sent) sentMessages++; if (sent) sentMessages++;
if (acked) ackedMessages++; if (acked) ackedMessages++;
} });
);
CobraMetricsPublisher cobraMetricsPublisher; CobraMetricsPublisher cobraMetricsPublisher;
cobraMetricsPublisher.enable(true); cobraMetricsPublisher.enable(true);
bool enablePerMessageDeflate = true; bool enablePerMessageDeflate = true;
cobraMetricsPublisher.configure(appkey, endpoint, channel, cobraMetricsPublisher.configure(
rolename, rolesecret, enablePerMessageDeflate); appkey, endpoint, channel, rolename, rolesecret, enablePerMessageDeflate);
while (!cobraMetricsPublisher.isAuthenticated()) ; while (!cobraMetricsPublisher.isAuthenticated())
;
std::ifstream f(path); std::ifstream f(path);
std::string str((std::istreambuf_iterator<char>(f)), std::string str((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>());
std::istreambuf_iterator<char>());
Json::Value data; Json::Value data;
Json::Reader reader; Json::Reader reader;
@ -61,7 +59,7 @@ namespace ix
// Stress mode to try to trigger server and client bugs // Stress mode to try to trigger server and client bugs
while (true) while (true)
{ {
for (int i = 0 ; i < 1000; ++i) for (int i = 0; i < 1000; ++i)
{ {
cobraMetricsPublisher.push(channel, data); cobraMetricsPublisher.push(channel, data);
} }
@ -70,7 +68,8 @@ namespace ix
cobraMetricsPublisher.resume(); cobraMetricsPublisher.resume();
// FIXME: investigate why without this check we trigger a lock // FIXME: investigate why without this check we trigger a lock
while (!cobraMetricsPublisher.isAuthenticated()) ; while (!cobraMetricsPublisher.isAuthenticated())
;
} }
} }
@ -83,5 +82,4 @@ namespace ix
return 0; return 0;
} }
} } // namespace ix

View File

@ -4,17 +4,17 @@
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved. * Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
*/ */
#include <iostream>
#include <fstream>
#include <sstream>
#include <chrono>
#include <thread>
#include <atomic> #include <atomic>
#include <mutex> #include <chrono>
#include <condition_variable> #include <condition_variable>
#include <jsoncpp/json/json.h> #include <fstream>
#include <iostream>
#include <ixcobra/IXCobraMetricsPublisher.h> #include <ixcobra/IXCobraMetricsPublisher.h>
#include <jsoncpp/json/json.h>
#include <mutex>
#include <spdlog/spdlog.h> #include <spdlog/spdlog.h>
#include <sstream>
#include <thread>
namespace ix namespace ix
{ {
@ -26,8 +26,7 @@ namespace ix
const std::string& path) const std::string& path)
{ {
std::ifstream f(path); std::ifstream f(path);
std::string str((std::istreambuf_iterator<char>(f)), std::string str((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>());
std::istreambuf_iterator<char>());
Json::Value data; Json::Value data;
Json::Reader reader; Json::Reader reader;
@ -38,9 +37,8 @@ namespace ix
} }
ix::CobraConnection conn; ix::CobraConnection conn;
conn.configure(appkey, endpoint, conn.configure(
rolename, rolesecret, appkey, endpoint, rolename, rolesecret, ix::WebSocketPerMessageDeflateOptions(true));
ix::WebSocketPerMessageDeflateOptions(true));
conn.connect(); conn.connect();
// Display incoming messages // Display incoming messages
@ -48,59 +46,58 @@ namespace ix
std::atomic<bool> messageAcked(false); std::atomic<bool> messageAcked(false);
std::condition_variable condition; std::condition_variable condition;
conn.setEventCallback( conn.setEventCallback([&conn, &channel, &data, &authenticated, &messageAcked, &condition](
[&conn, &channel, &data, &authenticated, &messageAcked, &condition] ix::CobraConnectionEventType eventType,
(ix::CobraConnectionEventType eventType, const std::string& errMsg,
const std::string& errMsg, const ix::WebSocketHttpHeaders& headers,
const ix::WebSocketHttpHeaders& headers, const std::string& subscriptionId,
const std::string& subscriptionId, CobraConnection::MsgId msgId) {
CobraConnection::MsgId msgId) if (eventType == ix::CobraConnection_EventType_Open)
{ {
if (eventType == ix::CobraConnection_EventType_Open) spdlog::info("Publisher connected");
{
spdlog::info("Publisher connected");
for (auto it : headers) for (auto it : headers)
{
spdlog::info("{}: {}", it.first, it.second);
}
}
else if (eventType == ix::CobraConnection_EventType_Authenticated)
{ {
spdlog::info("Publisher authenticated"); spdlog::info("{}: {}", it.first, it.second);
authenticated = true;
Json::Value channels;
channels[0] = channel;
auto msgId = conn.publish(channels, data);
spdlog::info("Published msg {}", msgId);
}
else if (eventType == ix::CobraConnection_EventType_Subscribed)
{
spdlog::info("Publisher: subscribed to channel {}", subscriptionId);
}
else if (eventType == ix::CobraConnection_EventType_UnSubscribed)
{
spdlog::info("Publisher: unsubscribed from channel {}", subscriptionId);
}
else if (eventType == ix::CobraConnection_EventType_Error)
{
spdlog::error("Publisher: error {}", errMsg);
condition.notify_one();
}
else if (eventType == ix::CobraConnection_EventType_Published)
{
spdlog::info("Published message id {} acked", msgId);
messageAcked = true;
condition.notify_one();
} }
} }
); else if (eventType == ix::CobraConnection_EventType_Authenticated)
{
spdlog::info("Publisher authenticated");
authenticated = true;
while (!authenticated) ; Json::Value channels;
while (!messageAcked) ; channels[0] = channel;
auto msgId = conn.publish(channels, data);
spdlog::info("Published msg {}", msgId);
}
else if (eventType == ix::CobraConnection_EventType_Subscribed)
{
spdlog::info("Publisher: subscribed to channel {}", subscriptionId);
}
else if (eventType == ix::CobraConnection_EventType_UnSubscribed)
{
spdlog::info("Publisher: unsubscribed from channel {}", subscriptionId);
}
else if (eventType == ix::CobraConnection_EventType_Error)
{
spdlog::error("Publisher: error {}", errMsg);
condition.notify_one();
}
else if (eventType == ix::CobraConnection_EventType_Published)
{
spdlog::info("Published message id {} acked", msgId);
messageAcked = true;
condition.notify_one();
}
});
while (!authenticated)
;
while (!messageAcked)
;
return 0; return 0;
} }
} } // namespace ix

View File

@ -4,14 +4,13 @@
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved. * Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
*/ */
#include <iostream>
#include <sstream>
#include <chrono>
#include <thread>
#include <atomic> #include <atomic>
#include <chrono>
#include <iostream>
#include <ixcobra/IXCobraConnection.h> #include <ixcobra/IXCobraConnection.h>
#include <spdlog/spdlog.h> #include <spdlog/spdlog.h>
#include <sstream>
#include <thread>
namespace ix namespace ix
{ {
@ -24,9 +23,8 @@ namespace ix
bool quiet) bool quiet)
{ {
ix::CobraConnection conn; ix::CobraConnection conn;
conn.configure(appkey, endpoint, conn.configure(
rolename, rolesecret, appkey, endpoint, rolename, rolesecret, ix::WebSocketPerMessageDeflateOptions(true));
ix::WebSocketPerMessageDeflateOptions(true));
conn.connect(); conn.connect();
Json::FastWriter jsonWriter; Json::FastWriter jsonWriter;
@ -35,13 +33,11 @@ namespace ix
std::atomic<int> msgPerSeconds(0); std::atomic<int> msgPerSeconds(0);
std::atomic<int> msgCount(0); std::atomic<int> msgCount(0);
auto timer = [&msgPerSeconds, &msgCount] auto timer = [&msgPerSeconds, &msgCount] {
{
while (true) while (true)
{ {
std::cout << "#messages " << msgCount << " " std::cout << "#messages " << msgCount << " "
<< "msg/s " << msgPerSeconds << "msg/s " << msgPerSeconds << std::endl;
<< std::endl;
msgPerSeconds = 0; msgPerSeconds = 0;
auto duration = std::chrono::seconds(1); auto duration = std::chrono::seconds(1);
@ -52,13 +48,12 @@ namespace ix
std::thread t(timer); std::thread t(timer);
conn.setEventCallback( conn.setEventCallback(
[&conn, &channel, &jsonWriter, &filter, &msgCount, &msgPerSeconds, &quiet] [&conn, &channel, &jsonWriter, &filter, &msgCount, &msgPerSeconds, &quiet](
(ix::CobraConnectionEventType eventType, ix::CobraConnectionEventType eventType,
const std::string& errMsg, const std::string& errMsg,
const ix::WebSocketHttpHeaders& headers, const ix::WebSocketHttpHeaders& headers,
const std::string& subscriptionId, const std::string& subscriptionId,
CobraConnection::MsgId msgId) CobraConnection::MsgId msgId) {
{
if (eventType == ix::CobraConnection_EventType_Open) if (eventType == ix::CobraConnection_EventType_Open)
{ {
spdlog::info("Subscriber connected"); spdlog::info("Subscriber connected");
@ -71,18 +66,18 @@ namespace ix
else if (eventType == ix::CobraConnection_EventType_Authenticated) else if (eventType == ix::CobraConnection_EventType_Authenticated)
{ {
spdlog::info("Subscriber authenticated"); spdlog::info("Subscriber authenticated");
conn.subscribe(channel, filter, conn.subscribe(
[&jsonWriter, &quiet, channel,
&msgPerSeconds, &msgCount](const Json::Value& msg) filter,
{ [&jsonWriter, &quiet, &msgPerSeconds, &msgCount](const Json::Value& msg) {
if (!quiet) if (!quiet)
{ {
std::cout << jsonWriter.write(msg) << std::endl; std::cout << jsonWriter.write(msg) << std::endl;
} }
msgPerSeconds++; msgPerSeconds++;
msgCount++; msgCount++;
}); });
} }
else if (eventType == ix::CobraConnection_EventType_Subscribed) else if (eventType == ix::CobraConnection_EventType_Subscribed)
{ {
@ -100,8 +95,7 @@ namespace ix
{ {
spdlog::error("Published message hacked: {}", msgId); spdlog::error("Published message hacked: {}", msgId);
} }
} });
);
while (true) while (true)
{ {
@ -111,4 +105,4 @@ namespace ix
return 0; return 0;
} }
} } // namespace ix

View File

@ -4,19 +4,18 @@
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved. * Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
*/ */
#include <iostream>
#include <sstream>
#include <chrono>
#include <thread>
#include <atomic>
#include <vector>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <ixcobra/IXCobraConnection.h>
#include <spdlog/spdlog.h>
#include "IXSentryClient.h" #include "IXSentryClient.h"
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <iostream>
#include <ixcobra/IXCobraConnection.h>
#include <mutex>
#include <queue>
#include <spdlog/spdlog.h>
#include <sstream>
#include <thread>
#include <vector>
namespace ix namespace ix
{ {
@ -32,9 +31,8 @@ namespace ix
int jobs) int jobs)
{ {
ix::CobraConnection conn; ix::CobraConnection conn;
conn.configure(appkey, endpoint, conn.configure(
rolename, rolesecret, appkey, endpoint, rolename, rolesecret, ix::WebSocketPerMessageDeflateOptions(true));
ix::WebSocketPerMessageDeflateOptions(true));
conn.connect(); conn.connect();
Json::FastWriter jsonWriter; Json::FastWriter jsonWriter;
@ -48,10 +46,15 @@ namespace ix
std::condition_variable progressCondition; std::condition_variable progressCondition;
std::queue<Json::Value> queue; std::queue<Json::Value> queue;
auto sentrySender = [&condition, &progressCondition, &conditionVariableMutex, auto sentrySender = [&condition,
&queue, verbose, &errorSending, &sentCount, &progressCondition,
&stop, &dsn] &conditionVariableMutex,
{ &queue,
verbose,
&errorSending,
&sentCount,
&stop,
&dsn] {
SentryClient sentryClient(dsn); SentryClient sentryClient(dsn);
while (true) while (true)
@ -60,7 +63,7 @@ namespace ix
{ {
std::unique_lock<std::mutex> lock(conditionVariableMutex); std::unique_lock<std::mutex> lock(conditionVariableMutex);
condition.wait(lock, [&queue, &stop]{ return !queue.empty() && !stop; }); condition.wait(lock, [&queue, &stop] { return !queue.empty() && !stop; });
msg = queue.front(); msg = queue.front();
queue.pop(); queue.pop();
@ -94,88 +97,93 @@ namespace ix
pool.push_back(std::thread(sentrySender)); pool.push_back(std::thread(sentrySender));
} }
conn.setEventCallback( conn.setEventCallback([&conn,
[&conn, &channel, &filter, &jsonWriter, &channel,
verbose, &receivedCount, &sentCount, &filter,
&condition, &conditionVariableMutex, &jsonWriter,
&progressCondition, &queue] verbose,
(ix::CobraConnectionEventType eventType, &receivedCount,
const std::string& errMsg, &sentCount,
const ix::WebSocketHttpHeaders& headers, &condition,
const std::string& subscriptionId, &conditionVariableMutex,
CobraConnection::MsgId msgId) &progressCondition,
&queue](ix::CobraConnectionEventType eventType,
const std::string& errMsg,
const ix::WebSocketHttpHeaders& headers,
const std::string& subscriptionId,
CobraConnection::MsgId msgId) {
if (eventType == ix::CobraConnection_EventType_Open)
{ {
if (eventType == ix::CobraConnection_EventType_Open) spdlog::info("Subscriber connected");
{
spdlog::info("Subscriber connected");
for (auto it : headers) for (auto it : headers)
{
spdlog::info("{}: {}", it.first, it.second);
}
}
if (eventType == ix::CobraConnection_EventType_Closed)
{ {
spdlog::info("Subscriber closed"); spdlog::info("{}: {}", it.first, it.second);
} }
else if (eventType == ix::CobraConnection_EventType_Authenticated) }
{ if (eventType == ix::CobraConnection_EventType_Closed)
std::cerr << "Subscriber authenticated" << std::endl; {
conn.subscribe(channel, filter, spdlog::info("Subscriber closed");
[&jsonWriter, verbose, }
&sentCount, &receivedCount, else if (eventType == ix::CobraConnection_EventType_Authenticated)
&condition, &conditionVariableMutex, {
&progressCondition, &queue] std::cerr << "Subscriber authenticated" << std::endl;
(const Json::Value& msg) conn.subscribe(channel,
filter,
[&jsonWriter,
verbose,
&sentCount,
&receivedCount,
&condition,
&conditionVariableMutex,
&progressCondition,
&queue](const Json::Value& msg) {
if (verbose)
{ {
if (verbose) spdlog::info(jsonWriter.write(msg));
{ }
spdlog::info(jsonWriter.write(msg));
}
// If we cannot send to sentry fast enough, drop the message // If we cannot send to sentry fast enough, drop the message
const uint64_t scaleFactor = 2; const uint64_t scaleFactor = 2;
if (sentCount != 0 && if (sentCount != 0 && receivedCount != 0 &&
receivedCount != 0 && (sentCount * scaleFactor < receivedCount))
(sentCount * scaleFactor < receivedCount)) {
{ spdlog::warn("message dropped: sending is backlogged !");
spdlog::warn("message dropped: sending is backlogged !");
condition.notify_one();
progressCondition.notify_one();
return;
}
++receivedCount;
{
std::unique_lock<std::mutex> lock(conditionVariableMutex);
queue.push(msg);
}
condition.notify_one(); condition.notify_one();
progressCondition.notify_one(); progressCondition.notify_one();
}); return;
} }
else if (eventType == ix::CobraConnection_EventType_Subscribed)
{ ++receivedCount;
spdlog::info("Subscriber: subscribed to channel {}", subscriptionId);
} {
else if (eventType == ix::CobraConnection_EventType_UnSubscribed) std::unique_lock<std::mutex> lock(conditionVariableMutex);
{ queue.push(msg);
spdlog::info("Subscriber: unsubscribed from channel {}", subscriptionId); }
}
else if (eventType == ix::CobraConnection_EventType_Error) condition.notify_one();
{ progressCondition.notify_one();
spdlog::error("Subscriber: error {}", errMsg); });
}
else if (eventType == ix::CobraConnection_EventType_Published)
{
spdlog::error("Published message hacked: {}", msgId);
}
} }
); else if (eventType == ix::CobraConnection_EventType_Subscribed)
{
spdlog::info("Subscriber: subscribed to channel {}", subscriptionId);
}
else if (eventType == ix::CobraConnection_EventType_UnSubscribed)
{
spdlog::info("Subscriber: unsubscribed from channel {}", subscriptionId);
}
else if (eventType == ix::CobraConnection_EventType_Error)
{
spdlog::error("Subscriber: error {}", errMsg);
}
else if (eventType == ix::CobraConnection_EventType_Published)
{
spdlog::error("Published message hacked: {}", msgId);
}
});
std::mutex progressConditionVariableMutex; std::mutex progressConditionVariableMutex;
while (true) while (true)
@ -200,4 +208,4 @@ namespace ix
return (strict && errorSending) ? 1 : 0; return (strict && errorSending) ? 1 : 0;
} }
} } // namespace ix

View File

@ -4,15 +4,14 @@
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved. * Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
*/ */
#include <iostream>
#include <sstream>
#include <chrono>
#include <thread>
#include <atomic> #include <atomic>
#include <vector> #include <chrono>
#include <iostream>
#include <ixcobra/IXCobraConnection.h> #include <ixcobra/IXCobraConnection.h>
#include <spdlog/spdlog.h> #include <spdlog/spdlog.h>
#include <sstream>
#include <thread>
#include <vector>
#ifndef _WIN32 #ifndef _WIN32
#include <statsd_client.h> #include <statsd_client.h>
@ -41,8 +40,7 @@ namespace ix
// Extract an attribute from a Json Value. // Extract an attribute from a Json Value.
// extractAttr("foo.bar", {"foo": {"bar": "baz"}}) => baz // extractAttr("foo.bar", {"foo": {"bar": "baz"}}) => baz
// //
std::string extractAttr(const std::string& attr, std::string extractAttr(const std::string& attr, const Json::Value& jsonValue)
const Json::Value& jsonValue)
{ {
// Split by . // Split by .
std::string token; std::string token;
@ -71,9 +69,8 @@ namespace ix
bool verbose) bool verbose)
{ {
ix::CobraConnection conn; ix::CobraConnection conn;
conn.configure(appkey, endpoint, conn.configure(
rolename, rolesecret, appkey, endpoint, rolename, rolesecret, ix::WebSocketPerMessageDeflateOptions(true));
ix::WebSocketPerMessageDeflateOptions(true));
conn.connect(); conn.connect();
auto tokens = parseFields(fields); auto tokens = parseFields(fields);
@ -90,72 +87,75 @@ namespace ix
Json::FastWriter jsonWriter; Json::FastWriter jsonWriter;
uint64_t msgCount = 0; uint64_t msgCount = 0;
conn.setEventCallback( conn.setEventCallback([&conn,
[&conn, &channel, &filter, &jsonWriter, &statsdClient, verbose, &tokens, &prefix, &msgCount] &channel,
(ix::CobraConnectionEventType eventType, &filter,
const std::string& errMsg, &jsonWriter,
const ix::WebSocketHttpHeaders& headers, &statsdClient,
const std::string& subscriptionId, verbose,
CobraConnection::MsgId msgId) &tokens,
&prefix,
&msgCount](ix::CobraConnectionEventType eventType,
const std::string& errMsg,
const ix::WebSocketHttpHeaders& headers,
const std::string& subscriptionId,
CobraConnection::MsgId msgId) {
if (eventType == ix::CobraConnection_EventType_Open)
{ {
if (eventType == ix::CobraConnection_EventType_Open) spdlog::info("Subscriber connected");
{
spdlog::info("Subscriber connected");
for (auto it : headers) for (auto it : headers)
{
spdlog::info("{}: {}", it.first, it.second);
}
}
if (eventType == ix::CobraConnection_EventType_Closed)
{ {
spdlog::info("Subscriber closed"); spdlog::info("{}: {}", it.first, it.second);
}
else if (eventType == ix::CobraConnection_EventType_Authenticated)
{
spdlog::info("Subscriber authenticated");
conn.subscribe(channel, filter,
[&jsonWriter, &statsdClient,
verbose, &tokens, &prefix, &msgCount]
(const Json::Value& msg)
{
if (verbose)
{
spdlog::info(jsonWriter.write(msg));
}
std::string id;
for (auto&& attr : tokens)
{
id += ".";
id += extractAttr(attr, msg);
}
spdlog::info("{} {}{}", msgCount++, prefix, id);
#ifndef _WIN32
statsdClient.count(id, 1);
#endif
});
}
else if (eventType == ix::CobraConnection_EventType_Subscribed)
{
spdlog::info("Subscriber: subscribed to channel {}", subscriptionId);
}
else if (eventType == ix::CobraConnection_EventType_UnSubscribed)
{
spdlog::info("Subscriber: unsubscribed from channel {}", subscriptionId);
}
else if (eventType == ix::CobraConnection_EventType_Error)
{
spdlog::error("Subscriber: error {}", errMsg);
}
else if (eventType == ix::CobraConnection_EventType_Published)
{
spdlog::error("Published message hacked: {}", msgId);
} }
} }
); if (eventType == ix::CobraConnection_EventType_Closed)
{
spdlog::info("Subscriber closed");
}
else if (eventType == ix::CobraConnection_EventType_Authenticated)
{
spdlog::info("Subscriber authenticated");
conn.subscribe(channel,
filter,
[&jsonWriter, &statsdClient, verbose, &tokens, &prefix, &msgCount](
const Json::Value& msg) {
if (verbose)
{
spdlog::info(jsonWriter.write(msg));
}
std::string id;
for (auto&& attr : tokens)
{
id += ".";
id += extractAttr(attr, msg);
}
spdlog::info("{} {}{}", msgCount++, prefix, id);
#ifndef _WIN32
statsdClient.count(id, 1);
#endif
});
}
else if (eventType == ix::CobraConnection_EventType_Subscribed)
{
spdlog::info("Subscriber: subscribed to channel {}", subscriptionId);
}
else if (eventType == ix::CobraConnection_EventType_UnSubscribed)
{
spdlog::info("Subscriber: unsubscribed from channel {}", subscriptionId);
}
else if (eventType == ix::CobraConnection_EventType_Error)
{
spdlog::error("Subscriber: error {}", errMsg);
}
else if (eventType == ix::CobraConnection_EventType_Published)
{
spdlog::error("Published message hacked: {}", msgId);
}
});
while (true) while (true)
{ {
@ -165,4 +165,4 @@ namespace ix
return 0; return 0;
} }
} } // namespace ix

View File

@ -4,41 +4,40 @@
* Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved. * Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved.
*/ */
#include <iostream>
#include <sstream>
#include <ixwebsocket/IXWebSocket.h>
#include <ixwebsocket/IXSocket.h>
#include "linenoise.hpp" #include "linenoise.hpp"
#include <iostream>
#include <ixwebsocket/IXSocket.h>
#include <ixwebsocket/IXWebSocket.h>
#include <sstream>
namespace ix namespace ix
{ {
class WebSocketConnect class WebSocketConnect
{ {
public: public:
WebSocketConnect(const std::string& _url, WebSocketConnect(const std::string& _url,
const std::string& headers, const std::string& headers,
bool disableAutomaticReconnection, bool disableAutomaticReconnection,
bool disablePerMessageDeflate, bool disablePerMessageDeflate,
bool binaryMode, bool binaryMode,
uint32_t maxWaitBetweenReconnectionRetries); uint32_t maxWaitBetweenReconnectionRetries);
void subscribe(const std::string& channel); void subscribe(const std::string& channel);
void start(); void start();
void stop(); void stop();
void sendMessage(const std::string& text); void sendMessage(const std::string& text);
private: private:
std::string _url; std::string _url;
WebSocketHttpHeaders _headers; WebSocketHttpHeaders _headers;
ix::WebSocket _webSocket; ix::WebSocket _webSocket;
bool _disablePerMessageDeflate; bool _disablePerMessageDeflate;
bool _binaryMode; bool _binaryMode;
void log(const std::string& msg); void log(const std::string& msg);
WebSocketHttpHeaders parseHeaders(const std::string& data); WebSocketHttpHeaders parseHeaders(const std::string& data);
}; };
WebSocketConnect::WebSocketConnect(const std::string& url, WebSocketConnect::WebSocketConnect(const std::string& url,
@ -46,10 +45,10 @@ namespace ix
bool disableAutomaticReconnection, bool disableAutomaticReconnection,
bool disablePerMessageDeflate, bool disablePerMessageDeflate,
bool binaryMode, bool binaryMode,
uint32_t maxWaitBetweenReconnectionRetries) : uint32_t maxWaitBetweenReconnectionRetries)
_url(url), : _url(url)
_disablePerMessageDeflate(disablePerMessageDeflate), , _disablePerMessageDeflate(disablePerMessageDeflate)
_binaryMode(binaryMode) , _binaryMode(binaryMode)
{ {
if (disableAutomaticReconnection) if (disableAutomaticReconnection)
{ {
@ -81,7 +80,7 @@ namespace ix
if (pos == std::string::npos) continue; if (pos == std::string::npos) continue;
auto key = token.substr(0, pos); auto key = token.substr(0, pos);
auto val = token.substr(pos+1); auto val = token.substr(pos + 1);
std::cerr << key << ": " << val << std::endl; std::cerr << key << ": " << val << std::endl;
headers[key] = val; headers[key] = val;
@ -114,61 +113,58 @@ namespace ix
std::stringstream ss; std::stringstream ss;
log(std::string("Connecting to url: ") + _url); log(std::string("Connecting to url: ") + _url);
_webSocket.setOnMessageCallback( _webSocket.setOnMessageCallback([this](const ix::WebSocketMessagePtr& msg) {
[this](const ix::WebSocketMessagePtr& msg) std::stringstream ss;
if (msg->type == ix::WebSocketMessageType::Open)
{ {
std::stringstream ss; log("ws_connect: connected");
if (msg->type == ix::WebSocketMessageType::Open) std::cout << "Uri: " << msg->openInfo.uri << std::endl;
std::cout << "Handshake Headers:" << std::endl;
for (auto it : msg->openInfo.headers)
{ {
log("ws_connect: connected"); std::cout << it.first << ": " << it.second << std::endl;
std::cout << "Uri: " << msg->openInfo.uri << std::endl;
std::cout << "Handshake Headers:" << std::endl;
for (auto it : msg->openInfo.headers)
{
std::cout << it.first << ": " << it.second << std::endl;
}
} }
else if (msg->type == ix::WebSocketMessageType::Close) }
{ else if (msg->type == ix::WebSocketMessageType::Close)
ss << "ws_connect: connection closed:"; {
ss << " code " << msg->closeInfo.code; ss << "ws_connect: connection closed:";
ss << " reason " << msg->closeInfo.reason << std::endl; ss << " code " << msg->closeInfo.code;
log(ss.str()); ss << " reason " << msg->closeInfo.reason << std::endl;
} log(ss.str());
else if (msg->type == ix::WebSocketMessageType::Message) }
{ else if (msg->type == ix::WebSocketMessageType::Message)
std::cerr << "Received " << msg->wireSize << " bytes" << std::endl; {
std::cerr << "Received " << msg->wireSize << " bytes" << std::endl;
ss << "ws_connect: received message: " ss << "ws_connect: received message: " << msg->str;
<< msg->str; log(ss.str());
log(ss.str()); }
} else if (msg->type == ix::WebSocketMessageType::Error)
else if (msg->type == ix::WebSocketMessageType::Error) {
{ ss << "Connection error: " << msg->errorInfo.reason << std::endl;
ss << "Connection error: " << msg->errorInfo.reason << std::endl; ss << "#retries: " << msg->errorInfo.retries << std::endl;
ss << "#retries: " << msg->errorInfo.retries << std::endl; ss << "Wait time(ms): " << msg->errorInfo.wait_time << std::endl;
ss << "Wait time(ms): " << msg->errorInfo.wait_time << std::endl; ss << "HTTP Status: " << msg->errorInfo.http_status << std::endl;
ss << "HTTP Status: " << msg->errorInfo.http_status << std::endl; log(ss.str());
log(ss.str()); }
} else if (msg->type == ix::WebSocketMessageType::Fragment)
else if (msg->type == ix::WebSocketMessageType::Fragment) {
{ std::cerr << "Received message fragment" << std::endl;
std::cerr << "Received message fragment" << std::endl; }
} else if (msg->type == ix::WebSocketMessageType::Ping)
else if (msg->type == ix::WebSocketMessageType::Ping) {
{ std::cerr << "Received ping" << std::endl;
std::cerr << "Received ping" << std::endl; }
} else if (msg->type == ix::WebSocketMessageType::Pong)
else if (msg->type == ix::WebSocketMessageType::Pong) {
{ std::cerr << "Received pong" << std::endl;
std::cerr << "Received pong" << std::endl; }
} else
else {
{ ss << "Invalid ix::WebSocketMessageType";
ss << "Invalid ix::WebSocketMessageType"; log(ss.str());
log(ss.str()); }
} });
});
_webSocket.start(); _webSocket.start();
} }
@ -237,5 +233,4 @@ namespace ix
return 0; return 0;
} }
} } // namespace ix

View File

@ -5,8 +5,8 @@
*/ */
#include <iostream> #include <iostream>
#include <sstream>
#include <ixwebsocket/IXWebSocketServer.h> #include <ixwebsocket/IXWebSocketServer.h>
#include <sstream>
namespace ix namespace ix
{ {
@ -18,11 +18,9 @@ namespace ix
server.setOnConnectionCallback( server.setOnConnectionCallback(
[greetings](std::shared_ptr<ix::WebSocket> webSocket, [greetings](std::shared_ptr<ix::WebSocket> webSocket,
std::shared_ptr<ConnectionState> connectionState) std::shared_ptr<ConnectionState> connectionState) {
{
webSocket->setOnMessageCallback( webSocket->setOnMessageCallback(
[webSocket, connectionState, greetings](const WebSocketMessagePtr& msg) [webSocket, connectionState, greetings](const WebSocketMessagePtr& msg) {
{
if (msg->type == ix::WebSocketMessageType::Open) if (msg->type == ix::WebSocketMessageType::Open)
{ {
std::cerr << "New connection" << std::endl; std::cerr << "New connection" << std::endl;
@ -42,29 +40,25 @@ namespace ix
else if (msg->type == ix::WebSocketMessageType::Close) else if (msg->type == ix::WebSocketMessageType::Close)
{ {
std::cerr << "Closed connection" std::cerr << "Closed connection"
<< " code " << msg->closeInfo.code << " code " << msg->closeInfo.code << " reason "
<< " reason " << msg->closeInfo.reason << std::endl; << msg->closeInfo.reason << std::endl;
} }
else if (msg->type == ix::WebSocketMessageType::Error) else if (msg->type == ix::WebSocketMessageType::Error)
{ {
std::stringstream ss; std::stringstream ss;
ss << "Connection error: " << msg->errorInfo.reason << std::endl; ss << "Connection error: " << msg->errorInfo.reason << std::endl;
ss << "#retries: " << msg->errorInfo.retries << std::endl; ss << "#retries: " << msg->errorInfo.retries << std::endl;
ss << "Wait time(ms): " << msg->errorInfo.wait_time << std::endl; ss << "Wait time(ms): " << msg->errorInfo.wait_time << std::endl;
ss << "HTTP Status: " << msg->errorInfo.http_status << std::endl; ss << "HTTP Status: " << msg->errorInfo.http_status << std::endl;
std::cerr << ss.str(); std::cerr << ss.str();
} }
else if (msg->type == ix::WebSocketMessageType::Message) else if (msg->type == ix::WebSocketMessageType::Message)
{ {
std::cerr << "Received " std::cerr << "Received " << msg->wireSize << " bytes" << std::endl;
<< msg->wireSize << " bytes"
<< std::endl;
webSocket->send(msg->str, msg->binary); webSocket->send(msg->str, msg->binary);
} }
} });
); });
}
);
auto res = server.listen(); auto res = server.listen();
if (!res.first) if (!res.first)
@ -78,4 +72,4 @@ namespace ix
return 0; return 0;
} }
} } // namespace ix

View File

@ -4,11 +4,11 @@
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved. * Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
*/ */
#include <iostream>
#include <sstream>
#include <fstream> #include <fstream>
#include <iostream>
#include <ixwebsocket/IXHttpClient.h> #include <ixwebsocket/IXHttpClient.h>
#include <ixwebsocket/IXWebSocketHttpHeaders.h> #include <ixwebsocket/IXWebSocketHttpHeaders.h>
#include <sstream>
namespace ix namespace ix
{ {
@ -19,7 +19,7 @@ namespace ix
idx = path.rfind('/'); idx = path.rfind('/');
if (idx != std::string::npos) if (idx != std::string::npos)
{ {
std::string filename = path.substr(idx+1); std::string filename = path.substr(idx + 1);
return filename; return filename;
} }
else else
@ -44,7 +44,7 @@ namespace ix
if (pos == std::string::npos) continue; if (pos == std::string::npos) continue;
auto key = token.substr(0, pos); auto key = token.substr(0, pos);
auto val = token.substr(pos+1); auto val = token.substr(pos + 1);
std::cerr << key << ": " << val << std::endl; std::cerr << key << ": " << val << std::endl;
headers[key] = val; headers[key] = val;
@ -73,7 +73,7 @@ namespace ix
if (pos == std::string::npos) continue; if (pos == std::string::npos) continue;
auto key = token.substr(0, pos); auto key = token.substr(0, pos);
auto val = token.substr(pos+1); auto val = token.substr(pos + 1);
std::cerr << key << ": " << val << std::endl; std::cerr << key << ": " << val << std::endl;
httpParameters[key] = val; httpParameters[key] = val;
@ -104,14 +104,10 @@ namespace ix
args->maxRedirects = maxRedirects; args->maxRedirects = maxRedirects;
args->verbose = verbose; args->verbose = verbose;
args->compress = compress; args->compress = compress;
args->logger = [](const std::string& msg) args->logger = [](const std::string& msg) { std::cout << msg; };
{ args->onProgressCallback = [](int current, int total) -> bool {
std::cout << msg; std::cerr << "\r"
}; << "Downloaded " << current << " bytes out of " << total;
args->onProgressCallback = [](int current, int total) -> bool
{
std::cerr << "\r" << "Downloaded "
<< current << " bytes out of " << total;
return true; return true;
}; };
@ -160,7 +156,7 @@ namespace ix
std::cout << "Writing to disk: " << filename << std::endl; std::cout << "Writing to disk: " << filename << std::endl;
std::ofstream out(filename); std::ofstream out(filename);
out.write((char*)&response->payload.front(), response->payload.size()); out.write((char*) &response->payload.front(), response->payload.size());
out.close(); out.close();
} }
else else
@ -173,11 +169,12 @@ namespace ix
{ {
std::cerr << "Binary output can mess up your terminal." << std::endl; std::cerr << "Binary output can mess up your terminal." << std::endl;
std::cerr << "Use the -O flag to save the file to disk." << std::endl; std::cerr << "Use the -O flag to save the file to disk." << std::endl;
std::cerr << "You can also use the --output option to specify a filename." << std::endl; std::cerr << "You can also use the --output option to specify a filename."
<< std::endl;
} }
} }
} }
return 0; return 0;
} }
} } // namespace ix

View File

@ -4,12 +4,12 @@
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved. * Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
*/ */
#include <iostream>
#include <vector>
#include <fstream> #include <fstream>
#include <sstream> #include <iostream>
#include <ixwebsocket/IXHttpServer.h> #include <ixwebsocket/IXHttpServer.h>
#include <spdlog/spdlog.h> #include <spdlog/spdlog.h>
#include <sstream>
#include <vector>
namespace ix namespace ix
{ {
@ -31,4 +31,4 @@ namespace ix
return 0; return 0;
} }
} } // namespace ix

View File

@ -5,33 +5,33 @@
*/ */
#include <iostream> #include <iostream>
#include <sstream>
#include <ixwebsocket/IXWebSocket.h>
#include <ixwebsocket/IXSocket.h> #include <ixwebsocket/IXSocket.h>
#include <ixwebsocket/IXWebSocket.h>
#include <sstream>
namespace ix namespace ix
{ {
class WebSocketPingPong class WebSocketPingPong
{ {
public: public:
WebSocketPingPong(const std::string& _url); WebSocketPingPong(const std::string& _url);
void subscribe(const std::string& channel); void subscribe(const std::string& channel);
void start(); void start();
void stop(); void stop();
void ping(const std::string& text); void ping(const std::string& text);
void send(const std::string& text); void send(const std::string& text);
private: private:
std::string _url; std::string _url;
ix::WebSocket _webSocket; ix::WebSocket _webSocket;
void log(const std::string& msg); void log(const std::string& msg);
}; };
WebSocketPingPong::WebSocketPingPong(const std::string& url) : WebSocketPingPong::WebSocketPingPong(const std::string& url)
_url(url) : _url(url)
{ {
; ;
} }
@ -53,63 +53,57 @@ namespace ix
std::stringstream ss; std::stringstream ss;
log(std::string("Connecting to url: ") + _url); log(std::string("Connecting to url: ") + _url);
_webSocket.setOnMessageCallback( _webSocket.setOnMessageCallback([this](const ix::WebSocketMessagePtr& msg) {
[this](const ix::WebSocketMessagePtr& msg) std::cerr << "Received " << msg->wireSize << " bytes" << std::endl;
std::stringstream ss;
if (msg->type == ix::WebSocketMessageType::Open)
{ {
std::cerr << "Received " << msg->wireSize << " bytes" << std::endl; log("ping_pong: connected");
std::stringstream ss; std::cout << "Uri: " << msg->openInfo.uri << std::endl;
if (msg->type == ix::WebSocketMessageType::Open) std::cout << "Handshake Headers:" << std::endl;
for (auto it : msg->openInfo.headers)
{ {
log("ping_pong: connected"); std::cout << it.first << ": " << it.second << std::endl;
std::cout << "Uri: " << msg->openInfo.uri << std::endl;
std::cout << "Handshake Headers:" << std::endl;
for (auto it : msg->openInfo.headers)
{
std::cout << it.first << ": " << it.second << std::endl;
}
} }
else if (msg->type == ix::WebSocketMessageType::Close) }
{ else if (msg->type == ix::WebSocketMessageType::Close)
ss << "ping_pong: disconnected:" {
<< " code " << msg->closeInfo.code ss << "ping_pong: disconnected:"
<< " reason " << msg->closeInfo.reason << " code " << msg->closeInfo.code << " reason " << msg->closeInfo.reason
<< msg->str; << msg->str;
log(ss.str()); log(ss.str());
} }
else if (msg->type == ix::WebSocketMessageType::Message) else if (msg->type == ix::WebSocketMessageType::Message)
{ {
ss << "ping_pong: received message: " ss << "ping_pong: received message: " << msg->str;
<< msg->str; log(ss.str());
log(ss.str()); }
} else if (msg->type == ix::WebSocketMessageType::Ping)
else if (msg->type == ix::WebSocketMessageType::Ping) {
{ ss << "ping_pong: received ping message: " << msg->str;
ss << "ping_pong: received ping message: " log(ss.str());
<< msg->str; }
log(ss.str()); else if (msg->type == ix::WebSocketMessageType::Pong)
} {
else if (msg->type == ix::WebSocketMessageType::Pong) ss << "ping_pong: received pong message: " << msg->str;
{ log(ss.str());
ss << "ping_pong: received pong message: " }
<< msg->str; else if (msg->type == ix::WebSocketMessageType::Error)
log(ss.str()); {
} ss << "Connection error: " << msg->errorInfo.reason << std::endl;
else if (msg->type == ix::WebSocketMessageType::Error) ss << "#retries: " << msg->errorInfo.retries << std::endl;
{ ss << "Wait time(ms): " << msg->errorInfo.wait_time << std::endl;
ss << "Connection error: " << msg->errorInfo.reason << std::endl; ss << "HTTP Status: " << msg->errorInfo.http_status << std::endl;
ss << "#retries: " << msg->errorInfo.retries << std::endl; log(ss.str());
ss << "Wait time(ms): " << msg->errorInfo.wait_time << std::endl; }
ss << "HTTP Status: " << msg->errorInfo.http_status << std::endl; else
log(ss.str()); {
} ss << "Invalid ix::WebSocketMessageType";
else log(ss.str());
{ }
ss << "Invalid ix::WebSocketMessageType"; });
log(ss.str());
}
});
_webSocket.start(); _webSocket.start();
} }
@ -118,7 +112,8 @@ namespace ix
{ {
if (!_webSocket.ping(text).success) if (!_webSocket.ping(text).success)
{ {
std::cerr << "Failed to send ping message. Message too long (> 125 bytes) or endpoint is disconnected" std::cerr << "Failed to send ping message. Message too long (> 125 bytes) or endpoint "
"is disconnected"
<< std::endl; << std::endl;
} }
} }
@ -160,4 +155,4 @@ namespace ix
return 0; return 0;
} }
} } // namespace ix

View File

@ -4,19 +4,19 @@
* Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved. * Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved.
*/ */
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <condition_variable>
#include <mutex>
#include <chrono> #include <chrono>
#include <ixwebsocket/IXWebSocket.h> #include <condition_variable>
#include <ixwebsocket/IXSocket.h> #include <fstream>
#include <ixcrypto/IXUuid.h> #include <iostream>
#include <ixcrypto/IXBase64.h> #include <ixcrypto/IXBase64.h>
#include <ixcrypto/IXHash.h> #include <ixcrypto/IXHash.h>
#include <ixcrypto/IXUuid.h>
#include <ixwebsocket/IXSocket.h>
#include <ixwebsocket/IXWebSocket.h>
#include <msgpack11/msgpack11.hpp> #include <msgpack11/msgpack11.hpp>
#include <mutex>
#include <sstream>
#include <vector>
using msgpack11::MsgPack; using msgpack11::MsgPack;
@ -24,42 +24,40 @@ namespace ix
{ {
class WebSocketReceiver class WebSocketReceiver
{ {
public: public:
WebSocketReceiver(const std::string& _url, WebSocketReceiver(const std::string& _url, bool enablePerMessageDeflate, int delayMs);
bool enablePerMessageDeflate,
int delayMs);
void subscribe(const std::string& channel); void subscribe(const std::string& channel);
void start(); void start();
void stop(); void stop();
void waitForConnection(); void waitForConnection();
void waitForMessage(); void waitForMessage();
void handleMessage(const std::string& str); void handleMessage(const std::string& str);
private: private:
std::string _url; std::string _url;
std::string _id; std::string _id;
ix::WebSocket _webSocket; ix::WebSocket _webSocket;
bool _enablePerMessageDeflate; bool _enablePerMessageDeflate;
int _delayMs; int _delayMs;
int _receivedFragmentCounter; int _receivedFragmentCounter;
std::mutex _conditionVariableMutex; std::mutex _conditionVariableMutex;
std::condition_variable _condition; std::condition_variable _condition;
std::string extractFilename(const std::string& path); std::string extractFilename(const std::string& path);
void handleError(const std::string& errMsg, const std::string& id); void handleError(const std::string& errMsg, const std::string& id);
void log(const std::string& msg); void log(const std::string& msg);
}; };
WebSocketReceiver::WebSocketReceiver(const std::string& url, WebSocketReceiver::WebSocketReceiver(const std::string& url,
bool enablePerMessageDeflate, bool enablePerMessageDeflate,
int delayMs) : int delayMs)
_url(url), : _url(url)
_enablePerMessageDeflate(enablePerMessageDeflate), , _enablePerMessageDeflate(enablePerMessageDeflate)
_delayMs(delayMs), , _delayMs(delayMs)
_receivedFragmentCounter(0) , _receivedFragmentCounter(0)
{ {
; ;
} }
@ -98,7 +96,7 @@ namespace ix
idx = path.rfind('/'); idx = path.rfind('/');
if (idx != std::string::npos) if (idx != std::string::npos)
{ {
std::string filename = path.substr(idx+1); std::string filename = path.substr(idx + 1);
return filename; return filename;
} }
else else
@ -107,8 +105,7 @@ namespace ix
} }
} }
void WebSocketReceiver::handleError(const std::string& errMsg, void WebSocketReceiver::handleError(const std::string& errMsg, const std::string& id)
const std::string& id)
{ {
std::map<MsgPack, MsgPack> pdu; std::map<MsgPack, MsgPack> pdu;
pdu["kind"] = "error"; pdu["kind"] = "error";
@ -156,7 +153,7 @@ namespace ix
std::cout << "Writing to disk: " << filenameTmp << std::endl; std::cout << "Writing to disk: " << filenameTmp << std::endl;
std::ofstream out(filenameTmp); std::ofstream out(filenameTmp);
out.write((char*)&content.front(), content.size()); out.write((char*) &content.front(), content.size());
out.close(); out.close();
std::cout << "Renaming " << filenameTmp << " to " << filename << std::endl; std::cout << "Renaming " << filenameTmp << " to " << filename << std::endl;
@ -182,70 +179,66 @@ namespace ix
std::stringstream ss; std::stringstream ss;
log(std::string("Connecting to url: ") + _url); log(std::string("Connecting to url: ") + _url);
_webSocket.setOnMessageCallback( _webSocket.setOnMessageCallback([this](const ix::WebSocketMessagePtr& msg) {
[this](const ix::WebSocketMessagePtr& msg) std::stringstream ss;
if (msg->type == ix::WebSocketMessageType::Open)
{ {
std::stringstream ss; _condition.notify_one();
if (msg->type == ix::WebSocketMessageType::Open)
{
_condition.notify_one();
log("ws_receive: connected"); log("ws_receive: connected");
std::cout << "Uri: " << msg->openInfo.uri << std::endl; std::cout << "Uri: " << msg->openInfo.uri << std::endl;
std::cout << "Handshake Headers:" << std::endl; std::cout << "Handshake Headers:" << std::endl;
for (auto it : msg->openInfo.headers) for (auto it : msg->openInfo.headers)
{
std::cout << it.first << ": " << it.second << std::endl;
}
}
else if (msg->type == ix::WebSocketMessageType::Close)
{ {
ss << "ws_receive: connection closed:"; std::cout << it.first << ": " << it.second << std::endl;
ss << " code " << msg->closeInfo.code;
ss << " reason " << msg->closeInfo.reason << std::endl;
log(ss.str());
} }
else if (msg->type == ix::WebSocketMessageType::Message) }
{ else if (msg->type == ix::WebSocketMessageType::Close)
ss << "ws_receive: transfered " << msg->wireSize << " bytes"; {
log(ss.str()); ss << "ws_receive: connection closed:";
handleMessage(msg->str); ss << " code " << msg->closeInfo.code;
_condition.notify_one(); ss << " reason " << msg->closeInfo.reason << std::endl;
} log(ss.str());
else if (msg->type == ix::WebSocketMessageType::Fragment) }
{ else if (msg->type == ix::WebSocketMessageType::Message)
ss << "ws_receive: received fragment " << _receivedFragmentCounter++; {
log(ss.str()); ss << "ws_receive: transfered " << msg->wireSize << " bytes";
log(ss.str());
handleMessage(msg->str);
_condition.notify_one();
}
else if (msg->type == ix::WebSocketMessageType::Fragment)
{
ss << "ws_receive: received fragment " << _receivedFragmentCounter++;
log(ss.str());
if (_delayMs > 0) if (_delayMs > 0)
{
// Introduce an arbitrary delay, to simulate a slow connection
std::chrono::duration<double, std::milli> duration(_delayMs);
std::this_thread::sleep_for(duration);
}
}
else if (msg->type == ix::WebSocketMessageType::Error)
{ {
ss << "ws_receive "; // Introduce an arbitrary delay, to simulate a slow connection
ss << "Connection error: " << msg->errorInfo.reason << std::endl; std::chrono::duration<double, std::milli> duration(_delayMs);
ss << "#retries: " << msg->errorInfo.retries << std::endl; std::this_thread::sleep_for(duration);
ss << "Wait time(ms): " << msg->errorInfo.wait_time << std::endl;
ss << "HTTP Status: " << msg->errorInfo.http_status << std::endl;
log(ss.str());
} }
else }
{ else if (msg->type == ix::WebSocketMessageType::Error)
ss << "Invalid ix::WebSocketMessageType"; {
log(ss.str()); ss << "ws_receive ";
} 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;
log(ss.str());
}
else
{
ss << "Invalid ix::WebSocketMessageType";
log(ss.str());
}
});
_webSocket.start(); _webSocket.start();
} }
void wsReceive(const std::string& url, void wsReceive(const std::string& url, bool enablePerMessageDeflate, int delayMs)
bool enablePerMessageDeflate,
int delayMs)
{ {
WebSocketReceiver webSocketReceiver(url, enablePerMessageDeflate, delayMs); WebSocketReceiver webSocketReceiver(url, enablePerMessageDeflate, delayMs);
webSocketReceiver.start(); webSocketReceiver.start();
@ -261,11 +254,9 @@ namespace ix
webSocketReceiver.stop(); webSocketReceiver.stop();
} }
int ws_receive_main(const std::string& url, int ws_receive_main(const std::string& url, bool enablePerMessageDeflate, int delayMs)
bool enablePerMessageDeflate,
int delayMs)
{ {
wsReceive(url, enablePerMessageDeflate, delayMs); wsReceive(url, enablePerMessageDeflate, delayMs);
return 0; return 0;
} }
} } // namespace ix

View File

@ -4,9 +4,9 @@
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved. * Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
*/ */
#include "IXRedisClient.h"
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include "IXRedisClient.h"
namespace ix namespace ix
{ {
@ -41,8 +41,7 @@ namespace ix
{ {
if (!redisClient.publish(channel, message, errMsg)) if (!redisClient.publish(channel, message, errMsg))
{ {
std::cerr << "Error publishing to channel " << channel std::cerr << "Error publishing to channel " << channel << "error: " << errMsg
<< "error: " << errMsg
<< std::endl; << std::endl;
return 1; return 1;
} }
@ -50,4 +49,4 @@ namespace ix
return 0; return 0;
} }
} } // namespace ix

View File

@ -4,12 +4,12 @@
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved. * Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
*/ */
#include "IXRedisClient.h"
#include <atomic>
#include <chrono>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include <chrono>
#include <thread> #include <thread>
#include <atomic>
#include "IXRedisClient.h"
namespace ix namespace ix
{ {
@ -41,9 +41,7 @@ namespace ix
std::atomic<int> msgPerSeconds(0); std::atomic<int> msgPerSeconds(0);
std::atomic<int> msgCount(0); std::atomic<int> msgCount(0);
auto callback = [&msgPerSeconds, &msgCount, verbose] auto callback = [&msgPerSeconds, &msgCount, verbose](const std::string& message) {
(const std::string& message)
{
if (verbose) if (verbose)
{ {
std::cout << "received: " << message << std::endl; std::cout << "received: " << message << std::endl;
@ -53,18 +51,15 @@ namespace ix
msgCount++; msgCount++;
}; };
auto responseCallback = [](const std::string& redisResponse) auto responseCallback = [](const std::string& redisResponse) {
{
std::cout << "Redis subscribe response: " << redisResponse << std::endl; std::cout << "Redis subscribe response: " << redisResponse << std::endl;
}; };
auto timer = [&msgPerSeconds, &msgCount] auto timer = [&msgPerSeconds, &msgCount] {
{
while (true) while (true)
{ {
std::cout << "#messages " << msgCount << " " std::cout << "#messages " << msgCount << " "
<< "msg/s " << msgPerSeconds << "msg/s " << msgPerSeconds << std::endl;
<< std::endl;
msgPerSeconds = 0; msgPerSeconds = 0;
auto duration = std::chrono::seconds(1); auto duration = std::chrono::seconds(1);
@ -83,4 +78,4 @@ namespace ix
return 0; return 0;
} }
} } // namespace ix

View File

@ -4,19 +4,19 @@
* Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved. * Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved.
*/ */
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <condition_variable>
#include <mutex>
#include <chrono> #include <chrono>
#include <ixwebsocket/IXWebSocket.h> #include <condition_variable>
#include <ixwebsocket/IXSocket.h> #include <fstream>
#include <ixcrypto/IXUuid.h> #include <iostream>
#include <ixcrypto/IXBase64.h> #include <ixcrypto/IXBase64.h>
#include <ixcrypto/IXHash.h> #include <ixcrypto/IXHash.h>
#include <ixcrypto/IXUuid.h>
#include <ixwebsocket/IXSocket.h>
#include <ixwebsocket/IXWebSocket.h>
#include <msgpack11/msgpack11.hpp> #include <msgpack11/msgpack11.hpp>
#include <mutex>
#include <sstream>
#include <vector>
using msgpack11::MsgPack; using msgpack11::MsgPack;
@ -24,35 +24,33 @@ namespace ix
{ {
class WebSocketSender class WebSocketSender
{ {
public: public:
WebSocketSender(const std::string& _url, WebSocketSender(const std::string& _url, bool enablePerMessageDeflate);
bool enablePerMessageDeflate);
void subscribe(const std::string& channel); void subscribe(const std::string& channel);
void start(); void start();
void stop(); void stop();
void waitForConnection(); void waitForConnection();
void waitForAck(); void waitForAck();
void sendMessage(const std::string& filename, bool throttle); void sendMessage(const std::string& filename, bool throttle);
private: private:
std::string _url; std::string _url;
std::string _id; std::string _id;
ix::WebSocket _webSocket; ix::WebSocket _webSocket;
bool _enablePerMessageDeflate; bool _enablePerMessageDeflate;
std::mutex _conditionVariableMutex; std::mutex _conditionVariableMutex;
std::condition_variable _condition; std::condition_variable _condition;
void log(const std::string& msg); void log(const std::string& msg);
}; };
WebSocketSender::WebSocketSender(const std::string& url, WebSocketSender::WebSocketSender(const std::string& url, bool enablePerMessageDeflate)
bool enablePerMessageDeflate) : : _url(url)
_url(url), , _enablePerMessageDeflate(enablePerMessageDeflate)
_enablePerMessageDeflate(enablePerMessageDeflate)
{ {
; ;
} }
@ -95,7 +93,7 @@ namespace ix
file.seekg(0, file.beg); file.seekg(0, file.beg);
memblock.resize((size_t) size); memblock.resize((size_t) size);
file.read((char*)&memblock.front(), static_cast<std::streamsize>(size)); file.read((char*) &memblock.front(), static_cast<std::streamsize>(size));
return memblock; return memblock;
} }
@ -111,114 +109,110 @@ namespace ix
std::stringstream ss; std::stringstream ss;
log(std::string("Connecting to url: ") + _url); log(std::string("Connecting to url: ") + _url);
_webSocket.setOnMessageCallback( _webSocket.setOnMessageCallback([this](const WebSocketMessagePtr& msg) {
[this](const WebSocketMessagePtr& msg) std::stringstream ss;
if (msg->type == ix::WebSocketMessageType::Open)
{ {
std::stringstream ss; _condition.notify_one();
if (msg->type == ix::WebSocketMessageType::Open)
{
_condition.notify_one();
log("ws_send: connected"); log("ws_send: connected");
std::cout << "Uri: " << msg->openInfo.uri << std::endl; std::cout << "Uri: " << msg->openInfo.uri << std::endl;
std::cout << "Handshake Headers:" << std::endl; std::cout << "Handshake Headers:" << std::endl;
for (auto it : msg->openInfo.headers) for (auto it : msg->openInfo.headers)
{
std::cout << it.first << ": " << it.second << std::endl;
}
}
else if (msg->type == ix::WebSocketMessageType::Close)
{ {
ss << "ws_send: connection closed:"; std::cout << it.first << ": " << it.second << std::endl;
ss << " code " << msg->closeInfo.code;
ss << " reason " << msg->closeInfo.reason << std::endl;
log(ss.str());
} }
else if (msg->type == ix::WebSocketMessageType::Message) }
{ else if (msg->type == ix::WebSocketMessageType::Close)
_condition.notify_one(); {
ss << "ws_send: connection closed:";
ss << " code " << msg->closeInfo.code;
ss << " reason " << msg->closeInfo.reason << std::endl;
log(ss.str());
}
else if (msg->type == ix::WebSocketMessageType::Message)
{
_condition.notify_one();
ss << "ws_send: received message (" << msg->wireSize << " bytes)"; ss << "ws_send: received message (" << msg->wireSize << " bytes)";
log(ss.str()); log(ss.str());
std::string errMsg; std::string errMsg;
MsgPack data = MsgPack::parse(msg->str, errMsg); MsgPack data = MsgPack::parse(msg->str, errMsg);
if (!errMsg.empty()) if (!errMsg.empty())
{ {
std::cerr << "Invalid MsgPack response" << std::endl; std::cerr << "Invalid MsgPack response" << std::endl;
return; return;
} }
std::string id = data["id"].string_value(); std::string id = data["id"].string_value();
if (_id != id) if (_id != id)
{
std::cerr << "Invalid id" << std::endl;
}
}
else if (msg->type == ix::WebSocketMessageType::Error)
{ {
ss << "ws_send "; std::cerr << "Invalid id" << std::endl;
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;
log(ss.str());
} }
else }
{ else if (msg->type == ix::WebSocketMessageType::Error)
ss << "Invalid ix::WebSocketMessageType"; {
log(ss.str()); ss << "ws_send ";
} 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;
log(ss.str());
}
else
{
ss << "Invalid ix::WebSocketMessageType";
log(ss.str());
}
});
_webSocket.start(); _webSocket.start();
} }
class Bench class Bench
{ {
public: public:
Bench(const std::string& description) : Bench(const std::string& description)
_description(description), : _description(description)
_start(std::chrono::system_clock::now()), , _start(std::chrono::system_clock::now())
_reported(false) , _reported(false)
{
;
}
~Bench()
{
if (!_reported)
{ {
; report();
} }
}
~Bench() void report()
{ {
if (!_reported) auto now = std::chrono::system_clock::now();
{ auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(now - _start);
report();
}
}
void report() _ms = milliseconds.count();
{ std::cout << _description << " completed in " << _ms << "ms" << std::endl;
auto now = std::chrono::system_clock::now();
auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(now - _start);
_ms = milliseconds.count(); _reported = true;
std::cout << _description << " completed in " }
<< _ms << "ms" << std::endl;
_reported = true; uint64_t getDuration() const
} {
return _ms;
}
uint64_t getDuration() const private:
{ std::string _description;
return _ms; std::chrono::time_point<std::chrono::system_clock> _start;
} uint64_t _ms;
bool _reported;
private:
std::string _description;
std::chrono::time_point<std::chrono::system_clock> _start;
uint64_t _ms;
bool _reported;
}; };
void WebSocketSender::sendMessage(const std::string& filename, void WebSocketSender::sendMessage(const std::string& filename, bool throttle)
bool throttle)
{ {
std::vector<uint8_t> content; std::vector<uint8_t> content;
{ {
@ -239,9 +233,7 @@ namespace ix
MsgPack msg(pdu); MsgPack msg(pdu);
Bench bench("Sending file through websocket"); Bench bench("Sending file through websocket");
_webSocket.sendBinary(msg.dump(), _webSocket.sendBinary(msg.dump(), [throttle](int current, int total) -> bool {
[throttle](int current, int total) -> bool
{
std::cout << "ws_send: Step " << current << " out of " << total << std::endl; std::cout << "ws_send: Step " << current << " out of " << total << std::endl;
if (throttle) if (throttle)
@ -256,8 +248,7 @@ namespace ix
do do
{ {
size_t bufferedAmount = _webSocket.bufferedAmount(); size_t bufferedAmount = _webSocket.bufferedAmount();
std::cout << "ws_send: " << bufferedAmount std::cout << "ws_send: " << bufferedAmount << " bytes left to be sent" << std::endl;
<< " bytes left to be sent" << std::endl;
std::chrono::duration<double, std::milli> duration(10); std::chrono::duration<double, std::milli> duration(10);
std::this_thread::sleep_for(duration); std::this_thread::sleep_for(duration);
@ -289,8 +280,7 @@ namespace ix
webSocketSender.stop(); webSocketSender.stop();
} }
int ws_send_main(const std::string& url, int ws_send_main(const std::string& url, const std::string& path)
const std::string& path)
{ {
bool throttle = false; bool throttle = false;
bool enablePerMessageDeflate = false; bool enablePerMessageDeflate = false;
@ -298,4 +288,4 @@ namespace ix
wsSend(url, path, enablePerMessageDeflate, throttle); wsSend(url, path, enablePerMessageDeflate, throttle);
return 0; return 0;
} }
} } // namespace ix

View File

@ -5,10 +5,9 @@
*/ */
#include "IXSnakeServer.h" #include "IXSnakeServer.h"
#include <fstream>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include <fstream>
namespace namespace
{ {
@ -24,7 +23,7 @@ namespace
file.seekg(0, file.beg); file.seekg(0, file.beg);
memblock.resize((size_t) size); memblock.resize((size_t) size);
file.read((char*)&memblock.front(), static_cast<std::streamsize>(size)); file.read((char*) &memblock.front(), static_cast<std::streamsize>(size));
return memblock; return memblock;
} }
@ -34,7 +33,7 @@ namespace
auto vec = load(path); auto vec = load(path);
return std::string(vec.begin(), vec.end()); return std::string(vec.begin(), vec.end());
} }
} } // namespace
namespace ix namespace ix
{ {
@ -80,4 +79,4 @@ namespace ix
return 0; // should never reach this return 0; // should never reach this
} }
} } // namespace ix

View File

@ -5,8 +5,8 @@
*/ */
#include <iostream> #include <iostream>
#include <sstream>
#include <ixwebsocket/IXWebSocketServer.h> #include <ixwebsocket/IXWebSocketServer.h>
#include <sstream>
namespace ix namespace ix
{ {
@ -16,76 +16,67 @@ namespace ix
ix::WebSocketServer server(port, hostname); ix::WebSocketServer server(port, hostname);
server.setOnConnectionCallback( server.setOnConnectionCallback([&server](std::shared_ptr<ix::WebSocket> webSocket,
[&server](std::shared_ptr<ix::WebSocket> webSocket, std::shared_ptr<ConnectionState> connectionState) {
std::shared_ptr<ConnectionState> connectionState) webSocket->setOnMessageCallback([webSocket, connectionState, &server](
{ const WebSocketMessagePtr& msg) {
webSocket->setOnMessageCallback( if (msg->type == ix::WebSocketMessageType::Open)
[webSocket, connectionState, &server](const WebSocketMessagePtr& msg) {
std::cerr << "New connection" << std::endl;
std::cerr << "id: " << connectionState->getId() << std::endl;
std::cerr << "Uri: " << msg->openInfo.uri << std::endl;
std::cerr << "Headers:" << std::endl;
for (auto it : msg->openInfo.headers)
{ {
if (msg->type == ix::WebSocketMessageType::Open) 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;
}
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::Fragment)
{
std::cerr << "Received message fragment " << std::endl;
}
else if (msg->type == ix::WebSocketMessageType::Message)
{
std::cerr << "Received " << msg->wireSize << " bytes" << std::endl;
for (auto&& client : server.getClients())
{
if (client != webSocket)
{ {
std::cerr << "New connection" << std::endl; client->send(msg->str, msg->binary, [](int current, int total) -> bool {
std::cerr << "id: " << connectionState->getId() << std::endl; std::cerr << "ws_transfer: Step " << current << " out of " << total
std::cerr << "Uri: " << msg->openInfo.uri << std::endl; << std::endl;
std::cerr << "Headers:" << std::endl; return true;
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;
}
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::Fragment)
{
std::cerr << "Received message fragment "
<< std::endl;
}
else if (msg->type == ix::WebSocketMessageType::Message)
{
std::cerr << "Received " << msg->wireSize << " bytes" << std::endl;
for (auto&& client : server.getClients())
{
if (client != webSocket)
{
client->send(msg->str,
msg->binary,
[](int current, int total) -> bool
{
std::cerr << "ws_transfer: Step " << current
<< " out of " << total << std::endl;
return true;
});
do do
{ {
size_t bufferedAmount = client->bufferedAmount(); size_t bufferedAmount = client->bufferedAmount();
std::cerr << "ws_transfer: " << bufferedAmount std::cerr << "ws_transfer: " << bufferedAmount
<< " bytes left to be sent" << std::endl; << " bytes left to be sent" << std::endl;
std::chrono::duration<double, std::milli> duration(10); std::chrono::duration<double, std::milli> duration(10);
std::this_thread::sleep_for(duration); std::this_thread::sleep_for(duration);
} while (client->bufferedAmount() != 0); } while (client->bufferedAmount() != 0);
}
}
} }
} }
); }
} });
); });
auto res = server.listen(); auto res = server.listen();
if (!res.first) if (!res.first)
@ -99,4 +90,4 @@ namespace ix
return 0; return 0;
} }
} } // namespace ix