Real Name

This commit is contained in:
2022-01-01 23:50:23 +01:00
parent 94a31b7608
commit 0adf122d1d
553 changed files with 155034 additions and 0 deletions
+32
View File
@@ -0,0 +1,32 @@
/*
* IXBench.h
* Author: Benjamin Sergeant
* Copyright (c) 2017-2020 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include <chrono>
#include <stdint.h>
#include <string>
namespace ix
{
class Bench
{
public:
Bench(const std::string &description);
~Bench();
void reset();
void record();
void report();
void setReported();
uint64_t getDuration() const;
private:
std::string _description;
std::chrono::time_point<std::chrono::high_resolution_clock> _start;
uint64_t _duration;
bool _reported;
};
} // namespace ix
@@ -0,0 +1,19 @@
/*
* IXCancellationRequest.h
* Author: Benjamin Sergeant
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include <atomic>
#include <functional>
namespace ix
{
using CancellationRequest = std::function<bool()>;
CancellationRequest makeCancellationRequestWithTimeout(
int seconds,
std::atomic<bool> &requestInitCancellation);
} // namespace ix
+54
View File
@@ -0,0 +1,54 @@
/*
* IXConnectionState.h
* Author: Benjamin Sergeant
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include <atomic>
#include <functional>
#include <memory>
#include <stdint.h>
#include <string>
namespace ix
{
using OnSetTerminatedCallback = std::function<void()>;
class ConnectionState
{
public:
ConnectionState();
virtual ~ConnectionState() = default;
virtual void computeId();
virtual const std::string &getId() const;
void setTerminated();
bool isTerminated() const;
const std::string &getRemoteIp();
int getRemotePort();
static std::shared_ptr<ConnectionState> createConnectionState();
private:
void setOnSetTerminatedCallback(const OnSetTerminatedCallback &callback);
void setRemoteIp(const std::string &remoteIp);
void setRemotePort(int remotePort);
protected:
std::atomic<bool> _terminated;
std::string _id;
OnSetTerminatedCallback _onSetTerminatedCallback;
static std::atomic<uint64_t> _globalId;
std::string _remoteIp;
int _remotePort;
friend class SocketServer;
};
} // namespace ix
+73
View File
@@ -0,0 +1,73 @@
/*
* IXDNSLookup.h
* Author: Benjamin Sergeant
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
*
* Resolve a hostname+port to a struct addrinfo obtained with getaddrinfo
* Does this in a background thread so that it can be cancelled, since
* getaddrinfo is a blocking call, and we don't want to block the main thread
* on Mobile.
*/
#pragma once
#include "IXCancellationRequest.h"
#include <atomic>
#include <memory>
#include <mutex>
#include <set>
#include <string>
struct addrinfo;
namespace ix
{
class DNSLookup : public std::enable_shared_from_this<DNSLookup>
{
public:
DNSLookup(const std::string &hostname,
int port,
int64_t wait = DNSLookup::kDefaultWait);
~DNSLookup() = default;
struct addrinfo *resolve(std::string &errMsg,
const CancellationRequest &isCancellationRequested,
bool cancellable = true);
void release(struct addrinfo *addr);
private:
struct addrinfo *
resolveCancellable(std::string &errMsg,
const CancellationRequest &isCancellationRequested);
struct addrinfo *resolveUnCancellable(
std::string &errMsg,
const CancellationRequest &isCancellationRequested);
static struct addrinfo *
getAddrInfo(const std::string &hostname, int port, std::string &errMsg);
void run(std::weak_ptr<DNSLookup> self,
std::string hostname,
int port); // thread runner
void setErrMsg(const std::string &errMsg);
const std::string &getErrMsg();
void setRes(struct addrinfo *addr);
struct addrinfo *getRes();
std::string _hostname;
int _port;
int64_t _wait;
const static int64_t kDefaultWait;
struct addrinfo *_res;
std::mutex _resMutex;
std::string _errMsg;
std::mutex _errMsgMutex;
std::atomic<bool> _done;
};
} // namespace ix
@@ -0,0 +1,17 @@
/*
* IXExponentialBackoff.h
* Author: Benjamin Sergeant
* Copyright (c) 2017-2019 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include <cstdint>
namespace ix
{
uint32_t
calculateRetryWaitMilliseconds(uint32_t retryCount,
uint32_t maxWaitBetweenReconnectionRetries,
uint32_t minWaitBetweenReconnectionRetries);
} // namespace ix
+12
View File
@@ -0,0 +1,12 @@
/*
* IXGetFreePort.h
* Author: Benjamin Sergeant
* Copyright (c) 2019 Machine Zone. All rights reserved.
*/
#pragma once
namespace ix
{
int getFreePort();
} // namespace ix
+15
View File
@@ -0,0 +1,15 @@
/*
* IXGzipCodec.h
* Author: Benjamin Sergeant
* Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include <string>
namespace ix
{
std::string gzipCompress(const std::string &str);
bool gzipDecompress(const std::string &in, std::string &out);
} // namespace ix
+117
View File
@@ -0,0 +1,117 @@
/*
* IXHttp.h
* Author: Benjamin Sergeant
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include "IXProgressCallback.h"
#include "IXWebSocketHttpHeaders.h"
#include <tuple>
#include <unordered_map>
namespace ix
{
enum class HttpErrorCode : int {
Ok = 0,
CannotConnect = 1,
Timeout = 2,
Gzip = 3,
UrlMalformed = 4,
CannotCreateSocket = 5,
SendError = 6,
ReadError = 7,
CannotReadStatusLine = 8,
MissingStatus = 9,
HeaderParsingError = 10,
MissingLocation = 11,
TooManyRedirects = 12,
ChunkReadError = 13,
CannotReadBody = 14,
Invalid = 100
};
struct HttpResponse {
int statusCode;
std::string description;
HttpErrorCode errorCode;
WebSocketHttpHeaders headers;
std::string body;
std::string errorMsg;
uint64_t uploadSize;
uint64_t downloadSize;
HttpResponse(int s = 0,
const std::string &des = std::string(),
const HttpErrorCode &c = HttpErrorCode::Ok,
const WebSocketHttpHeaders &h = WebSocketHttpHeaders(),
const std::string &b = std::string(),
const std::string &e = std::string(),
uint64_t u = 0,
uint64_t d = 0)
: statusCode(s), description(des), errorCode(c), headers(h), body(b),
errorMsg(e), uploadSize(u), downloadSize(d)
{
;
}
};
using HttpResponsePtr = std::shared_ptr<HttpResponse>;
using HttpParameters = std::unordered_map<std::string, std::string>;
using HttpFormDataParameters = std::unordered_map<std::string, std::string>;
using Logger = std::function<void(const std::string &)>;
using OnResponseCallback = std::function<void(const HttpResponsePtr &)>;
struct HttpRequestArgs {
std::string url;
std::string verb;
WebSocketHttpHeaders extraHeaders;
std::string body;
std::string multipartBoundary;
int connectTimeout = 60;
int transferTimeout = 1800;
bool followRedirects = true;
int maxRedirects = 5;
bool verbose = false;
bool compress = true;
bool compressRequest = false;
Logger logger;
OnProgressCallback onProgressCallback;
};
using HttpRequestArgsPtr = std::shared_ptr<HttpRequestArgs>;
struct HttpRequest {
std::string uri;
std::string method;
std::string version;
std::string body;
WebSocketHttpHeaders headers;
HttpRequest(const std::string &u,
const std::string &m,
const std::string &v,
const std::string &b,
const WebSocketHttpHeaders &h = WebSocketHttpHeaders())
: uri(u), method(m), version(v), body(b), headers(h)
{
}
};
using HttpRequestPtr = std::shared_ptr<HttpRequest>;
class Http
{
public:
static std::tuple<bool, std::string, HttpRequestPtr>
parseRequest(std::unique_ptr<Socket> &socket, int timeoutSecs);
static bool sendResponse(HttpResponsePtr response,
std::unique_ptr<Socket> &socket);
static std::pair<std::string, int> parseStatusLine(const std::string &line);
static std::tuple<std::string, std::string, std::string>
parseRequestLine(const std::string &line);
static std::string trim(const std::string &str);
};
} // namespace ix
+126
View File
@@ -0,0 +1,126 @@
/*
* IXHttpClient.h
* Author: Benjamin Sergeant
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include "IXHttp.h"
#include "IXSocket.h"
#include "IXSocketTLSOptions.h"
#include "IXWebSocketHttpHeaders.h"
#include <algorithm>
#include <atomic>
#include <condition_variable>
#include <functional>
#include <map>
#include <memory>
#include <mutex>
#include <queue>
#include <thread>
namespace ix
{
class HttpClient
{
public:
HttpClient(bool async = false);
~HttpClient();
HttpResponsePtr get(const std::string &url, HttpRequestArgsPtr args);
HttpResponsePtr head(const std::string &url, HttpRequestArgsPtr args);
HttpResponsePtr Delete(const std::string &url, HttpRequestArgsPtr args);
HttpResponsePtr post(const std::string &url,
const HttpParameters &httpParameters,
const HttpFormDataParameters &httpFormDataParameters,
HttpRequestArgsPtr args);
HttpResponsePtr post(const std::string &url,
const std::string &body,
HttpRequestArgsPtr args);
HttpResponsePtr put(const std::string &url,
const HttpParameters &httpParameters,
const HttpFormDataParameters &httpFormDataParameters,
HttpRequestArgsPtr args);
HttpResponsePtr put(const std::string &url,
const std::string &body,
HttpRequestArgsPtr args);
HttpResponsePtr patch(const std::string &url,
const HttpParameters &httpParameters,
const HttpFormDataParameters &httpFormDataParameters,
HttpRequestArgsPtr args);
HttpResponsePtr patch(const std::string &url,
const std::string &body,
HttpRequestArgsPtr args);
HttpResponsePtr request(const std::string &url,
const std::string &verb,
const std::string &body,
HttpRequestArgsPtr args,
int redirects = 0);
HttpResponsePtr
request(const std::string &url,
const std::string &verb,
const HttpParameters &httpParameters,
const HttpFormDataParameters &httpFormDataParameters,
HttpRequestArgsPtr args);
void setForceBody(bool value);
// Async API
HttpRequestArgsPtr
createRequest(const std::string &url = std::string(),
const std::string &verb = HttpClient::kGet);
bool performRequest(HttpRequestArgsPtr request,
const OnResponseCallback &onResponseCallback);
// TLS
void setTLSOptions(const SocketTLSOptions &tlsOptions);
std::string serializeHttpParameters(const HttpParameters &httpParameters);
std::string serializeHttpFormDataParameters(
const std::string &multipartBoundary,
const HttpFormDataParameters &httpFormDataParameters,
const HttpParameters &httpParameters = HttpParameters());
std::string generateMultipartBoundary();
std::string urlEncode(const std::string &value);
const static std::string kPost;
const static std::string kGet;
const static std::string kHead;
const static std::string kDelete;
const static std::string kPut;
const static std::string kPatch;
private:
void log(const std::string &msg, HttpRequestArgsPtr args);
// Async API background thread runner
void run();
// Async API
bool _async;
std::queue<std::pair<HttpRequestArgsPtr, OnResponseCallback>> _queue;
mutable std::mutex _queueMutex;
std::condition_variable _condition;
std::atomic<bool> _stop;
std::thread _thread;
std::unique_ptr<Socket> _socket;
std::recursive_mutex
_mutex; // to protect accessing the _socket (only one socket per
// client) the mutex needs to be recursive as this function
// might be called recursively to follow HTTP redirections
SocketTLSOptions _tlsOptions;
bool _forceBody;
};
} // namespace ix
+62
View File
@@ -0,0 +1,62 @@
/*
* IXHttpServer.h
* Author: Benjamin Sergeant
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include "IXHttp.h"
#include "IXSocketServer.h"
#include "IXWebSocket.h"
#include <functional>
#include <memory>
#include <mutex>
#include <set>
#include <string>
#include <thread>
#include <utility> // pair
namespace ix
{
class HttpServer final : public SocketServer
{
public:
using OnConnectionCallback =
std::function<HttpResponsePtr(HttpRequestPtr,
std::shared_ptr<ConnectionState>)>;
HttpServer(int port = SocketServer::kDefaultPort,
const std::string &host = SocketServer::kDefaultHost,
int backlog = SocketServer::kDefaultTcpBacklog,
size_t maxConnections = SocketServer::kDefaultMaxConnections,
int addressFamily = SocketServer::kDefaultAddressFamily,
int timeoutSecs = HttpServer::kDefaultTimeoutSecs);
virtual ~HttpServer();
virtual void stop() final;
void setOnConnectionCallback(const OnConnectionCallback &callback);
void makeRedirectServer(const std::string &redirectUrl);
void makeDebugServer();
int getTimeoutSecs();
private:
// Member variables
OnConnectionCallback _onConnectionCallback;
std::atomic<int> _connectedClientsCount;
const static int kDefaultTimeoutSecs;
int _timeoutSecs;
// Methods
virtual void handleConnection(
std::unique_ptr<Socket>,
std::shared_ptr<ConnectionState> connectionState) final;
virtual size_t getConnectedClientsCount() final;
void setDefaultConnectionCallback();
};
} // namespace ix
+87
View File
@@ -0,0 +1,87 @@
/*
* IXNetSystem.h
* Author: Benjamin Sergeant
* Copyright (c) 2019 Machine Zone. All rights reserved.
*/
#pragma once
#ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <winsock2.h>
#include <basetsd.h>
#include <io.h>
#include <ws2def.h>
#include <ws2tcpip.h>
#undef EWOULDBLOCK
#undef EAGAIN
#undef EINPROGRESS
#undef EBADF
#undef EINVAL
// map to WSA error codes
#define EWOULDBLOCK WSAEWOULDBLOCK
#define EAGAIN WSATRY_AGAIN
#define EINPROGRESS WSAEINPROGRESS
#define EBADF WSAEBADF
#define EINVAL WSAEINVAL
// Define our own poll on Windows, as a wrapper on top of select
typedef unsigned long int nfds_t;
// pollfd is not defined by some versions of mingw64 since _WIN32_WINNT is too
// low
#if _WIN32_WINNT < 0x0600
struct pollfd {
int fd; /* file descriptor */
short events; /* requested events */
short revents; /* returned events */
};
#define POLLIN 0x001 /* There is data to read. */
#define POLLOUT 0x004 /* Writing now will not block. */
#define POLLERR 0x008 /* Error condition. */
#define POLLHUP 0x010 /* Hung up. */
#define POLLNVAL 0x020 /* Invalid polling request. */
#endif
#else
#include <arpa/inet.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <poll.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <unistd.h>
#endif
#include <errno.h>
namespace ix
{
#ifdef _WIN32
typedef SOCKET socket_t;
#else
typedef int socket_t;
#endif
bool initNetSystem();
bool uninitNetSystem();
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
int inet_pton(int af, const char *src, void *dst);
unsigned short network_to_host_short(unsigned short value);
} // namespace ix
+14
View File
@@ -0,0 +1,14 @@
/*
* IXProgressCallback.h
* Author: Benjamin Sergeant
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include <functional>
namespace ix
{
using OnProgressCallback = std::function<bool(int current, int total)>;
}
+34
View File
@@ -0,0 +1,34 @@
/*
* IXSelectInterrupt.h
* Author: Benjamin Sergeant
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include <memory>
#include <stdint.h>
#include <string>
namespace ix
{
class SelectInterrupt
{
public:
SelectInterrupt();
virtual ~SelectInterrupt();
virtual bool init(std::string &errorMsg);
virtual bool notify(uint64_t value);
virtual bool clear();
virtual uint64_t read();
virtual int getFd() const;
// Used as special codes for pipe communication
static const uint64_t kSendRequest;
static const uint64_t kCloseRequest;
};
using SelectInterruptPtr = std::unique_ptr<SelectInterrupt>;
} // namespace ix
@@ -0,0 +1,16 @@
/*
* IXSelectInterruptFactory.h
* Author: Benjamin Sergeant
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include <memory>
namespace ix
{
class SelectInterrupt;
using SelectInterruptPtr = std::unique_ptr<SelectInterrupt>;
SelectInterruptPtr createSelectInterrupt();
} // namespace ix
@@ -0,0 +1,40 @@
/*
* IXSelectInterruptPipe.h
* Author: Benjamin Sergeant
* Copyright (c) 2018-2019 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include "IXSelectInterrupt.h"
#include <mutex>
#include <stdint.h>
#include <string>
namespace ix
{
class SelectInterruptPipe final : public SelectInterrupt
{
public:
SelectInterruptPipe();
virtual ~SelectInterruptPipe();
bool init(std::string &errorMsg) final;
bool notify(uint64_t value) final;
bool clear() final;
uint64_t read() final;
int getFd() const final;
private:
// Store file descriptors used by the communication pipe. Communication
// happens between a control thread and a background thread, which is
// blocked on select.
int _fildes[2];
mutable std::mutex _fildesMutex;
// Used to identify the read/write idx
static const int kPipeReadIndex;
static const int kPipeWriteIndex;
};
} // namespace ix
+12
View File
@@ -0,0 +1,12 @@
/*
* IXSetThreadName.h
* Author: Benjamin Sergeant
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include <string>
namespace ix
{
void setThreadName(const std::string &name);
}
+97
View File
@@ -0,0 +1,97 @@
/*
* IXSocket.h
* Author: Benjamin Sergeant
* Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include <atomic>
#include <functional>
#include <memory>
#include <mutex>
#include <string>
#ifdef _WIN32
#ifndef _SSIZE_T_DEFINED
#include <basetsd.h>
typedef SSIZE_T ssize_t;
#endif
#endif
#include "IXCancellationRequest.h"
#include "IXProgressCallback.h"
#include "IXSelectInterrupt.h"
namespace ix
{
enum class PollResultType {
ReadyForRead = 0,
ReadyForWrite = 1,
Timeout = 2,
Error = 3,
SendRequest = 4,
CloseRequest = 5
};
class Socket
{
public:
Socket(int fd = -1);
virtual ~Socket();
bool init(std::string &errorMsg);
// Functions to check whether there is activity on the socket
PollResultType poll(int timeoutMs = kDefaultPollTimeout);
bool wakeUpFromPoll(uint64_t wakeUpCode);
PollResultType isReadyToWrite(int timeoutMs);
PollResultType isReadyToRead(int timeoutMs);
// Virtual methods
virtual bool accept(std::string &errMsg);
virtual bool connect(const std::string &host,
int port,
std::string &errMsg,
const CancellationRequest &isCancellationRequested);
virtual void close();
virtual ssize_t send(char *buffer, size_t length);
ssize_t send(const std::string &buffer);
virtual ssize_t recv(void *buffer, size_t length);
// Blocking and cancellable versions, working with socket that can be set
// to non blocking mode. Used during HTTP upgrade.
bool readByte(void *buffer,
const CancellationRequest &isCancellationRequested);
bool writeBytes(const std::string &str,
const CancellationRequest &isCancellationRequested);
std::pair<bool, std::string>
readLine(const CancellationRequest &isCancellationRequested);
std::pair<bool, std::string>
readBytes(size_t length,
const OnProgressCallback &onProgressCallback,
const CancellationRequest &isCancellationRequested);
static int getErrno();
static bool isWaitNeeded();
static void closeSocket(int fd);
static PollResultType poll(bool readyToRead,
int timeoutMs,
int sockfd,
const SelectInterruptPtr &selectInterrupt);
protected:
std::atomic<int> _sockfd;
std::mutex _socketMutex;
private:
static const int kDefaultPollTimeout;
static const int kDefaultPollNoTimeout;
SelectInterruptPtr _selectInterrupt;
};
} // namespace ix
+56
View File
@@ -0,0 +1,56 @@
/*
* IXSocketAppleSSL.h
* Author: Benjamin Sergeant
* Copyright (c) 2017-2020 Machine Zone, Inc. All rights reserved.
*/
#ifdef IXWEBSOCKET_USE_SECURE_TRANSPORT
#pragma once
#include "IXCancellationRequest.h"
#include "IXSocket.h"
#include "IXSocketTLSOptions.h"
#include <Security/SecureTransport.h>
#include <Security/Security.h>
#include <mutex>
namespace ix
{
class SocketAppleSSL final : public Socket
{
public:
SocketAppleSSL(const SocketTLSOptions &tlsOptions, int fd = -1);
~SocketAppleSSL();
virtual bool accept(std::string &errMsg) final;
virtual bool
connect(const std::string &host,
int port,
std::string &errMsg,
const CancellationRequest &isCancellationRequested) final;
virtual void close() final;
virtual ssize_t send(char *buffer, size_t length) final;
virtual ssize_t recv(void *buffer, size_t length) final;
private:
static std::string getSSLErrorDescription(OSStatus status);
static OSStatus writeToSocket(SSLConnectionRef connection,
const void *data,
size_t *len);
static OSStatus
readFromSocket(SSLConnectionRef connection, void *data, size_t *len);
OSStatus tlsHandShake(std::string &errMsg,
const CancellationRequest &isCancellationRequested);
SSLContextRef _sslContext;
mutable std::mutex _mutex; // AppleSSL routines are not thread-safe
SocketTLSOptions _tlsOptions;
};
} // namespace ix
#endif // IXWEBSOCKET_USE_SECURE_TRANSPORT
+32
View File
@@ -0,0 +1,32 @@
/*
* IXSocketConnect.h
* Author: Benjamin Sergeant
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include "IXCancellationRequest.h"
#include <string>
struct addrinfo;
namespace ix
{
class SocketConnect
{
public:
static int connect(const std::string &hostname,
int port,
std::string &errMsg,
const CancellationRequest &isCancellationRequested);
static void configure(int sockfd);
private:
static int
connectToAddress(const struct addrinfo *address,
std::string &errMsg,
const CancellationRequest &isCancellationRequested);
};
} // namespace ix
+21
View File
@@ -0,0 +1,21 @@
/*
* IXSocketFactory.h
* Author: Benjamin Sergeant
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include "IXSocketTLSOptions.h"
#include <memory>
#include <string>
namespace ix
{
class Socket;
std::unique_ptr<Socket> createSocket(bool tls,
int fd,
std::string &errorMsg,
const SocketTLSOptions &tlsOptions);
} // namespace ix
+61
View File
@@ -0,0 +1,61 @@
/*
* IXSocketMbedTLS.h
* Author: Benjamin Sergeant
* Copyright (c) 2019-2020 Machine Zone, Inc. All rights reserved.
*/
#ifdef IXWEBSOCKET_USE_MBED_TLS
#pragma once
#include "IXSocket.h"
#include "IXSocketTLSOptions.h"
#include <mbedtls/ctr_drbg.h>
#include <mbedtls/debug.h>
#include <mbedtls/entropy.h>
#include <mbedtls/error.h>
#include <mbedtls/net_sockets.h>
#include <mbedtls/platform.h>
#include <mbedtls/x509.h>
#include <mbedtls/x509_crt.h>
#include <mutex>
namespace ix
{
class SocketMbedTLS final : public Socket
{
public:
SocketMbedTLS(const SocketTLSOptions &tlsOptions, int fd = -1);
~SocketMbedTLS();
virtual bool accept(std::string &errMsg) final;
virtual bool
connect(const std::string &host,
int port,
std::string &errMsg,
const CancellationRequest &isCancellationRequested) final;
virtual void close() final;
virtual ssize_t send(char *buffer, size_t length) final;
virtual ssize_t recv(void *buffer, size_t length) final;
private:
mbedtls_ssl_context _ssl;
mbedtls_ssl_config _conf;
mbedtls_entropy_context _entropy;
mbedtls_ctr_drbg_context _ctr_drbg;
mbedtls_x509_crt _cacert;
mbedtls_x509_crt _cert;
mbedtls_pk_context _pkey;
std::mutex _mutex;
SocketTLSOptions _tlsOptions;
bool init(const std::string &host, bool isClient, std::string &errMsg);
void initMBedTLS();
bool loadSystemCertificates(std::string &errMsg);
};
} // namespace ix
#endif // IXWEBSOCKET_USE_MBED_TLS
+75
View File
@@ -0,0 +1,75 @@
/*
* IXSocketOpenSSL.h
* Author: Benjamin Sergeant, Matt DeBoer
* Copyright (c) 2017-2020 Machine Zone, Inc. All rights reserved.
*/
#ifdef IXWEBSOCKET_USE_OPEN_SSL
#pragma once
#include "IXCancellationRequest.h"
#include "IXSocket.h"
#include "IXSocketTLSOptions.h"
#include <mutex>
#include <openssl/bio.h>
#include <openssl/conf.h>
#include <openssl/err.h>
#include <openssl/hmac.h>
#include <openssl/ssl.h>
namespace ix
{
class SocketOpenSSL final : public Socket
{
public:
SocketOpenSSL(const SocketTLSOptions &tlsOptions, int fd = -1);
~SocketOpenSSL();
virtual bool accept(std::string &errMsg) final;
virtual bool
connect(const std::string &host,
int port,
std::string &errMsg,
const CancellationRequest &isCancellationRequested) final;
virtual void close() final;
virtual ssize_t send(char *buffer, size_t length) final;
virtual ssize_t recv(void *buffer, size_t length) final;
private:
void openSSLInitialize();
std::string getSSLError(int ret);
SSL_CTX *openSSLCreateContext(std::string &errMsg);
bool openSSLAddCARootsFromString(const std::string roots);
bool openSSLClientHandshake(
const std::string &hostname,
std::string &errMsg,
const CancellationRequest &isCancellationRequested);
bool openSSLCheckServerCert(SSL *ssl,
const std::string &hostname,
std::string &errMsg);
bool checkHost(const std::string &host, const char *pattern);
bool handleTLSOptions(std::string &errMsg);
bool openSSLServerHandshake(std::string &errMsg);
// Required for OpenSSL < 1.1
static void openSSLLockingCallback(int mode,
int type,
const char * /*file*/,
int /*line*/);
SSL *_ssl_connection;
SSL_CTX *_ssl_context;
const SSL_METHOD *_ssl_method;
SocketTLSOptions _tlsOptions;
mutable std::mutex _mutex; // OpenSSL routines are not thread-safe
static std::once_flag _openSSLInitFlag;
static std::atomic<bool> _openSSLInitializationSuccessful;
};
} // namespace ix
#endif // IXWEBSOCKET_USE_OPEN_SSL
+134
View File
@@ -0,0 +1,134 @@
/*
* IXSocketServer.h
* Author: Benjamin Sergeant
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include "IXConnectionState.h"
#include "IXNetSystem.h"
#include "IXSelectInterrupt.h"
#include "IXSocketTLSOptions.h"
#include <atomic>
#include <condition_variable>
#include <functional>
#include <list>
#include <memory>
#include <mutex>
#include <set>
#include <string>
#include <thread>
#include <utility> // pair
namespace ix
{
class Socket;
class SocketServer
{
public:
using ConnectionStateFactory =
std::function<std::shared_ptr<ConnectionState>()>;
// Each connection is handled by its own worker thread.
// We use a list as we only care about remove and append operations.
using ConnectionThreads =
std::list<std::pair<std::shared_ptr<ConnectionState>, std::thread>>;
SocketServer(int port = SocketServer::kDefaultPort,
const std::string &host = SocketServer::kDefaultHost,
int backlog = SocketServer::kDefaultTcpBacklog,
size_t maxConnections = SocketServer::kDefaultMaxConnections,
int addressFamily = SocketServer::kDefaultAddressFamily);
virtual ~SocketServer();
virtual void stop();
// It is possible to override ConnectionState through inheritance
// this method allows user to change the factory by returning an object
// that inherits from ConnectionState but has its own methods.
void setConnectionStateFactory(
const ConnectionStateFactory &connectionStateFactory);
const static int kDefaultPort;
const static std::string kDefaultHost;
const static int kDefaultTcpBacklog;
const static size_t kDefaultMaxConnections;
const static int kDefaultAddressFamily;
void start();
std::pair<bool, std::string> listen();
void wait();
void setTLSOptions(const SocketTLSOptions &socketTLSOptions);
int getPort();
std::string getHost();
int getBacklog();
std::size_t getMaxConnections();
int getAddressFamily();
protected:
// Logging
void logError(const std::string &str);
void logInfo(const std::string &str);
void stopAcceptingConnections();
private:
// Member variables
int _port;
std::string _host;
int _backlog;
size_t _maxConnections;
int _addressFamily;
// socket for accepting connections
socket_t _serverFd;
std::atomic<bool> _stop;
std::mutex _logMutex;
// background thread to wait for incoming connections
std::thread _thread;
void run();
void onSetTerminatedCallback();
// background thread to cleanup (join) terminated threads
std::atomic<bool> _stopGc;
std::thread _gcThread;
void runGC();
// the list of (connectionState, threads) for each connections
ConnectionThreads _connectionsThreads;
std::mutex _connectionsThreadsMutex;
// used to have the main control thread for a server
// wait for a 'terminate' notification without busy polling
std::condition_variable _conditionVariable;
std::mutex _conditionVariableMutex;
// the factory to create ConnectionState objects
ConnectionStateFactory _connectionStateFactory;
virtual void
handleConnection(std::unique_ptr<Socket>,
std::shared_ptr<ConnectionState> connectionState) = 0;
virtual size_t getConnectedClientsCount() = 0;
// Returns true if all connection threads are joined
void closeTerminatedThreads();
size_t getConnectionsThreadsCount();
SocketTLSOptions _socketTLSOptions;
// to wake up from select
SelectInterruptPtr _acceptSelectInterrupt;
// used by the gc thread, to know that a thread needs to be garbage
// collected as a connection
std::condition_variable _conditionVariableGC;
std::mutex _conditionVariableMutexGC;
};
} // namespace ix
+53
View File
@@ -0,0 +1,53 @@
/*
* IXSocketTLSOptions.h
* Author: Matt DeBoer
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include <string>
namespace ix
{
struct SocketTLSOptions {
public:
// check validity of the object
bool isValid() const;
// the certificate presented to peers
std::string certFile;
// the key used for signing/encryption
std::string keyFile;
// the ca certificate (or certificate bundle) file containing
// certificates to be trusted by peers; use 'SYSTEM' to
// leverage the system defaults, use 'NONE' to disable peer verification
std::string caFile = "SYSTEM";
// list of ciphers (rsa, etc...)
std::string ciphers = "DEFAULT";
// whether tls is enabled, used for server code
bool tls = false;
bool hasCertAndKey() const;
bool isUsingSystemDefaults() const;
bool isUsingInMemoryCAs() const;
bool isPeerVerifyDisabled() const;
bool isUsingDefaultCiphers() const;
const std::string &getErrorMsg() const;
std::string getDescription() const;
private:
mutable std::string _errMsg;
mutable bool _validated = false;
};
} // namespace ix
+23
View File
@@ -0,0 +1,23 @@
/*
* IXStrCaseCompare.h
* Author: Benjamin Sergeant
* Copyright (c) 2020 Machine Zone. All rights reserved.
*/
#pragma once
#include <string>
namespace ix
{
struct CaseInsensitiveLess {
// Case Insensitive compare_less binary function
struct NocaseCompare {
bool operator()(const unsigned char &c1, const unsigned char &c2) const;
};
static bool cmp(const std::string &s1, const std::string &s2);
bool operator()(const std::string &s1, const std::string &s2) const;
};
} // namespace ix
+45
View File
@@ -0,0 +1,45 @@
/*
* IXUdpSocket.h
* Author: Benjamin Sergeant
* Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include <atomic>
#include <memory>
#include <string>
#ifdef _WIN32
#ifndef _SSIZE_T_DEFINED
#include <basetsd.h>
typedef SSIZE_T ssize_t;
#endif
#endif
#include "IXNetSystem.h"
namespace ix
{
class UdpSocket
{
public:
UdpSocket(int fd = -1);
~UdpSocket();
// Virtual methods
bool init(const std::string &host, int port, std::string &errMsg);
ssize_t sendto(const std::string &buffer);
ssize_t recvfrom(char *buffer, size_t length);
void close();
static int getErrno();
static bool isWaitNeeded();
static void closeSocket(int fd);
private:
std::atomic<int> _sockfd;
struct sockaddr_in _server;
};
} // namespace ix
+18
View File
@@ -0,0 +1,18 @@
/*
* IXUniquePtr.h
* Author: Benjamin Sergeant
* Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include <memory>
namespace ix
{
template <typename T, typename... Args>
std::unique_ptr<T> make_unique(Args &&... args)
{
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
} // namespace ix
+23
View File
@@ -0,0 +1,23 @@
/*
* IXUrlParser.h
* Author: Benjamin Sergeant
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include <string>
namespace ix
{
class UrlParser
{
public:
static bool parse(const std::string &url,
std::string &protocol,
std::string &host,
std::string &path,
std::string &query,
int &port);
};
} // namespace ix
+14
View File
@@ -0,0 +1,14 @@
/*
* IXUserAgent.h
* Author: Benjamin Sergeant
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include <string>
namespace ix
{
std::string userAgent();
} // namespace ix
+184
View File
@@ -0,0 +1,184 @@
/*
* The following code is adapted from code originally written by Bjoern
* Hoehrmann <bjoern@hoehrmann.de>. See
* http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
*
* The original license:
*
* Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* 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
* SOFTWARE.
*/
/*
* IXUtf8Validator.h
* Author: Benjamin Sergeant
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
*
* From websocketpp. Tiny modifications made for code style, function names
* etc...
*/
#pragma once
#include <cstdint>
#include <string>
namespace ix
{
/// State that represents a valid utf8 input sequence
static unsigned int const utf8_accept = 0;
/// State that represents an invalid utf8 input sequence
static unsigned int const utf8_reject = 1;
/// Lookup table for the UTF8 decode state machine
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, // 20..3f
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, // 60..7f
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
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
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
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
/**
* @param [out] state The decoder state to advance
* @param [out] codep The codepoint to fill in
* @param [in] byte The byte to input
* @return The ending state of the decode operation
*/
inline uint32_t decodeNextByte(uint32_t *state, uint32_t *codep, uint8_t byte)
{
uint32_t type = utf8d[byte];
*codep = (*state != utf8_accept) ? (byte & 0x3fu) | (*codep << 6)
: (0xff >> type) & (byte);
*state = utf8d[256 + *state * 16 + type];
return *state;
}
/// Provides streaming UTF8 validation functionality
class Utf8Validator
{
public:
/// Construct and initialize the validator
Utf8Validator() : m_state(utf8_accept), m_codepoint(0) {}
/// Advance the state of the validator with the next input byte
/**
* @param byte The byte to advance the validation state with
* @return Whether or not the byte resulted in a validation error.
*/
bool consume(uint8_t byte)
{
if (decodeNextByte(&m_state, &m_codepoint, byte) == utf8_reject) {
return false;
}
return true;
}
/// Advance Validator state with input from an iterator pair
/**
* @param begin Input iterator to the start 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.
*/
template <typename iterator_type>
bool decode(iterator_type begin, iterator_type end)
{
for (iterator_type it = begin; it != end; ++it) {
unsigned int result = decodeNextByte(&m_state,
&m_codepoint,
static_cast<uint8_t>(*it));
if (result == utf8_reject) {
return false;
}
}
return true;
}
/// Return whether the input sequence ended on a valid utf8 codepoint
/**
* @return Whether or not the input sequence ended on a valid codepoint.
*/
bool complete() { return m_state == utf8_accept; }
/// Reset the Validator to decode another message
void reset()
{
m_state = utf8_accept;
m_codepoint = 0;
}
private:
uint32_t m_state;
uint32_t m_codepoint;
};
/// Validate a UTF8 string
/**
* convenience function that creates a Validator, validates a complete string
* and returns the result.
*/
inline bool validateUtf8(std::string const &s)
{
Utf8Validator v;
if (!v.decode(s.begin(), s.end())) {
return false;
}
return v.complete();
}
} // namespace ix
+17
View File
@@ -0,0 +1,17 @@
/*
* IXUuid.h
* Author: Benjamin Sergeant
* Copyright (c) 2017 Machine Zone. All rights reserved.
*/
#pragma once
#include <string>
namespace ix
{
/**
* Generate a random uuid
*/
std::string uuid4();
} // namespace ix
+174
View File
@@ -0,0 +1,174 @@
/*
* IXWebSocket.h
* Author: Benjamin Sergeant
* Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved.
*
* WebSocket RFC
* https://tools.ietf.org/html/rfc6455
*/
#pragma once
#include "IXProgressCallback.h"
#include "IXSocketTLSOptions.h"
#include "IXWebSocketCloseConstants.h"
#include "IXWebSocketErrorInfo.h"
#include "IXWebSocketHttpHeaders.h"
#include "IXWebSocketMessage.h"
#include "IXWebSocketPerMessageDeflateOptions.h"
#include "IXWebSocketSendInfo.h"
#include "IXWebSocketTransport.h"
#include <atomic>
#include <condition_variable>
#include <mutex>
#include <string>
#include <thread>
namespace ix
{
// https://developer.mozilla.org/en-US/docs/Web/API/WebSocket#Ready_state_constants
enum class ReadyState { Connecting = 0, Open = 1, Closing = 2, Closed = 3 };
using OnMessageCallback = std::function<void(const WebSocketMessagePtr &)>;
using OnTrafficTrackerCallback =
std::function<void(size_t size, bool incoming)>;
class WebSocket
{
public:
WebSocket();
~WebSocket();
void setUrl(const std::string &url);
// send extra headers in client handshake request
void setExtraHeaders(const WebSocketHttpHeaders &headers);
void setPerMessageDeflateOptions(
const WebSocketPerMessageDeflateOptions &perMessageDeflateOptions);
void setTLSOptions(const SocketTLSOptions &socketTLSOptions);
void setPingInterval(int pingIntervalSecs);
void enablePong();
void disablePong();
void enablePerMessageDeflate();
void disablePerMessageDeflate();
void addSubProtocol(const std::string &subProtocol);
void setHandshakeTimeout(int handshakeTimeoutSecs);
// Run asynchronously, by calling start and stop.
void start();
// stop is synchronous
void stop(uint16_t code = WebSocketCloseConstants::kNormalClosureCode,
const std::string &reason =
WebSocketCloseConstants::kNormalClosureMessage);
// Run in blocking mode, by connecting first manually, and then calling run.
WebSocketInitResult connect(int timeoutSecs);
void run();
// send is in text mode by default
WebSocketSendInfo
send(const std::string &data,
bool binary = false,
const OnProgressCallback &onProgressCallback = nullptr);
WebSocketSendInfo
sendBinary(const std::string &text,
const OnProgressCallback &onProgressCallback = nullptr);
WebSocketSendInfo
sendText(const std::string &text,
const OnProgressCallback &onProgressCallback = nullptr);
WebSocketSendInfo ping(const std::string &text);
void close(uint16_t code = WebSocketCloseConstants::kNormalClosureCode,
const std::string &reason =
WebSocketCloseConstants::kNormalClosureMessage);
void setOnMessageCallback(const OnMessageCallback &callback);
bool isOnMessageCallbackRegistered() const;
static void
setTrafficTrackerCallback(const OnTrafficTrackerCallback &callback);
static void resetTrafficTrackerCallback();
ReadyState getReadyState() const;
static std::string readyStateToString(ReadyState readyState);
const std::string getUrl() const;
const WebSocketPerMessageDeflateOptions getPerMessageDeflateOptions() const;
int getPingInterval() const;
size_t bufferedAmount() const;
void enableAutomaticReconnection();
void disableAutomaticReconnection();
bool isAutomaticReconnectionEnabled() const;
void setMaxWaitBetweenReconnectionRetries(
uint32_t maxWaitBetweenReconnectionRetries);
void setMinWaitBetweenReconnectionRetries(
uint32_t minWaitBetweenReconnectionRetries);
uint32_t getMaxWaitBetweenReconnectionRetries() const;
uint32_t getMinWaitBetweenReconnectionRetries() const;
const std::vector<std::string> &getSubProtocols();
private:
WebSocketSendInfo sendMessage(const std::string &text,
SendMessageKind sendMessageKind,
const OnProgressCallback &callback = nullptr);
bool isConnected() const;
bool isClosing() const;
void checkConnection(bool firstConnectionAttempt);
static void invokeTrafficTrackerCallback(size_t size, bool incoming);
// Server
WebSocketInitResult connectToSocket(std::unique_ptr<Socket>,
int timeoutSecs,
bool enablePerMessageDeflate);
WebSocketTransport _ws;
std::string _url;
WebSocketHttpHeaders _extraHeaders;
WebSocketPerMessageDeflateOptions _perMessageDeflateOptions;
SocketTLSOptions _socketTLSOptions;
mutable std::mutex _configMutex; // protect all config variables access
OnMessageCallback _onMessageCallback;
static OnTrafficTrackerCallback _onTrafficTrackerCallback;
std::atomic<bool> _stop;
std::thread _thread;
std::mutex _writeMutex;
// Automatic reconnection
std::atomic<bool> _automaticReconnection;
static const uint32_t kDefaultMaxWaitBetweenReconnectionRetries;
static const uint32_t kDefaultMinWaitBetweenReconnectionRetries;
uint32_t _maxWaitBetweenReconnectionRetries;
uint32_t _minWaitBetweenReconnectionRetries;
// Make the sleeping in the automatic reconnection cancellable
std::mutex _sleepMutex;
std::condition_variable _sleepCondition;
std::atomic<int> _handshakeTimeoutSecs;
static const int kDefaultHandShakeTimeoutSecs;
// enable or disable PONG frame response to received PING frame
bool _enablePong;
static const bool kDefaultEnablePong;
// Optional ping and pong timeout
int _pingIntervalSecs;
int _pingTimeoutSecs;
static const int kDefaultPingIntervalSecs;
static const int kDefaultPingTimeoutSecs;
// Subprotocols
std::vector<std::string> _subProtocols;
friend class WebSocketServer;
};
} // namespace ix
@@ -0,0 +1,36 @@
/*
* IXWebSocketCloseConstants.h
* Author: Benjamin Sergeant
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include <cstdint>
#include <string>
namespace ix
{
struct WebSocketCloseConstants {
static const uint16_t kNormalClosureCode;
static const uint16_t kInternalErrorCode;
static const uint16_t kAbnormalCloseCode;
static const uint16_t kProtocolErrorCode;
static const uint16_t kNoStatusCodeErrorCode;
static const uint16_t kInvalidFramePayloadData;
static const std::string kNormalClosureMessage;
static const std::string kInternalErrorMessage;
static const std::string kAbnormalCloseMessage;
static const std::string kPingTimeoutMessage;
static const std::string kProtocolErrorMessage;
static const std::string kNoStatusCodeErrorMessage;
static const std::string kProtocolErrorReservedBitUsed;
static const std::string kProtocolErrorPingPayloadOversized;
static const std::string kProtocolErrorCodeControlMessageFragmented;
static const std::string kProtocolErrorCodeDataOpcodeOutOfSequence;
static const std::string kProtocolErrorCodeContinuationOpCodeOutOfSequence;
static const std::string kInvalidFramePayloadDataMessage;
static const std::string kInvalidCloseCodeMessage;
};
} // namespace ix
@@ -0,0 +1,27 @@
/*
* IXWebSocketCloseInfo.h
* Author: Benjamin Sergeant
* Copyright (c) 2017-2019 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include <cstdint>
#include <string>
namespace ix
{
struct WebSocketCloseInfo {
uint16_t code;
std::string reason;
bool remote;
WebSocketCloseInfo(uint16_t c = 0,
const std::string &r = std::string(),
bool rem = false)
: code(c), reason(r), remote(rem)
{
;
}
};
} // namespace ix
@@ -0,0 +1,21 @@
/*
* IXWebSocketErrorInfo.h
* Author: Benjamin Sergeant
* Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include <cstdint>
#include <string>
namespace ix
{
struct WebSocketErrorInfo {
uint32_t retries = 0;
double wait_time = 0;
int http_status = 0;
std::string reason;
bool decompressionError = false;
};
} // namespace ix
@@ -0,0 +1,57 @@
/*
* IXWebSocketHandshake.h
* Author: Benjamin Sergeant
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include "IXCancellationRequest.h"
#include "IXSocket.h"
#include "IXWebSocketHttpHeaders.h"
#include "IXWebSocketInitResult.h"
#include "IXWebSocketPerMessageDeflate.h"
#include "IXWebSocketPerMessageDeflateOptions.h"
#include <atomic>
#include <chrono>
#include <memory>
#include <string>
namespace ix
{
class WebSocketHandshake
{
public:
WebSocketHandshake(
std::atomic<bool> &requestInitCancellation,
std::unique_ptr<Socket> &_socket,
WebSocketPerMessageDeflatePtr &perMessageDeflate,
WebSocketPerMessageDeflateOptions &perMessageDeflateOptions,
std::atomic<bool> &enablePerMessageDeflate);
WebSocketInitResult
clientHandshake(const std::string &url,
const WebSocketHttpHeaders &extraHeaders,
const std::string &host,
const std::string &path,
int port,
int timeoutSecs);
WebSocketInitResult serverHandshake(int timeoutSecs,
bool enablePerMessageDeflate);
private:
std::string genRandomString(const int len);
// Parse HTTP headers
WebSocketInitResult sendErrorResponse(int code, const std::string &reason);
bool insensitiveStringCompare(const std::string &a, const std::string &b);
std::atomic<bool> &_requestInitCancellation;
std::unique_ptr<Socket> &_socket;
WebSocketPerMessageDeflatePtr &_perMessageDeflate;
WebSocketPerMessageDeflateOptions &_perMessageDeflateOptions;
std::atomic<bool> &_enablePerMessageDeflate;
};
} // namespace ix
@@ -0,0 +1,174 @@
// Copyright (c) 2016 Alex Hultman and contributors
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgement in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
#pragma once
#include <cstddef>
#include <cstdint>
#include <string.h>
#include <string>
class WebSocketHandshakeKeyGen
{
template <int N, typename T> struct static_for {
void operator()(uint32_t *a, uint32_t *b)
{
static_for<N - 1, T>()(a, b);
T::template f<N - 1>(a, b);
}
};
template <typename T> struct static_for<0, T> {
void operator()(uint32_t * /*a*/, uint32_t * /*hash*/) {}
};
template <int state> struct Sha1Loop {
static inline uint32_t rol(uint32_t value, size_t bits)
{
return (value << bits) | (value >> (32 - bits));
}
static inline uint32_t blk(uint32_t b[16], size_t i)
{
return rol(b[(i + 13) & 15] ^ b[(i + 8) & 15] ^ b[(i + 2) & 15] ^
b[i],
1);
}
template <int i> static inline void f(uint32_t *a, uint32_t *b)
{
switch (state) {
case 1:
a[i % 5] +=
((a[(3 + i) % 5] & (a[(2 + i) % 5] ^ a[(1 + i) % 5])) ^
a[(1 + i) % 5]) +
b[i] + 0x5a827999 + rol(a[(4 + i) % 5], 5);
a[(3 + i) % 5] = rol(a[(3 + i) % 5], 30);
break;
case 2:
b[i] = blk(b, i);
a[(1 + i) % 5] +=
((a[(4 + i) % 5] & (a[(3 + i) % 5] ^ a[(2 + i) % 5])) ^
a[(2 + i) % 5]) +
b[i] + 0x5a827999 + rol(a[(5 + i) % 5], 5);
a[(4 + i) % 5] = rol(a[(4 + i) % 5], 30);
break;
case 3:
b[(i + 4) % 16] = blk(b, (i + 4) % 16);
a[i % 5] +=
(a[(3 + i) % 5] ^ a[(2 + i) % 5] ^ a[(1 + i) % 5]) +
b[(i + 4) % 16] + 0x6ed9eba1 + rol(a[(4 + i) % 5], 5);
a[(3 + i) % 5] = rol(a[(3 + i) % 5], 30);
break;
case 4:
b[(i + 8) % 16] = blk(b, (i + 8) % 16);
a[i % 5] +=
(((a[(3 + i) % 5] | a[(2 + i) % 5]) & a[(1 + i) % 5]) |
(a[(3 + i) % 5] & a[(2 + i) % 5])) +
b[(i + 8) % 16] + 0x8f1bbcdc + rol(a[(4 + i) % 5], 5);
a[(3 + i) % 5] = rol(a[(3 + i) % 5], 30);
break;
case 5:
b[(i + 12) % 16] = blk(b, (i + 12) % 16);
a[i % 5] +=
(a[(3 + i) % 5] ^ a[(2 + i) % 5] ^ a[(1 + i) % 5]) +
b[(i + 12) % 16] + 0xca62c1d6 + rol(a[(4 + i) % 5], 5);
a[(3 + i) % 5] = rol(a[(3 + i) % 5], 30);
break;
case 6:
b[i] += a[4 - i];
}
}
};
static inline void sha1(uint32_t hash[5], uint32_t b[16])
{
uint32_t a[5] = {hash[4], hash[3], hash[2], hash[1], hash[0]};
static_for<16, Sha1Loop<1>>()(a, b);
static_for<4, Sha1Loop<2>>()(a, b);
static_for<20, Sha1Loop<3>>()(a, b);
static_for<20, Sha1Loop<4>>()(a, b);
static_for<20, Sha1Loop<5>>()(a, b);
static_for<5, Sha1Loop<6>>()(a, hash);
}
static inline void base64(unsigned char *src, char *dst)
{
const char *b64 =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
for (int i = 0; i < 18; i += 3) {
*dst++ = b64[(src[i] >> 2) & 63];
*dst++ = b64[((src[i] & 3) << 4) | ((src[i + 1] & 240) >> 4)];
*dst++ = b64[((src[i + 1] & 15) << 2) | ((src[i + 2] & 192) >> 6)];
*dst++ = b64[src[i + 2] & 63];
}
*dst++ = b64[(src[18] >> 2) & 63];
*dst++ = b64[((src[18] & 3) << 4) | ((src[19] & 240) >> 4)];
*dst++ = b64[((src[19] & 15) << 2)];
*dst++ = '=';
}
public:
static inline void generate(const std::string &inputStr, char output[28])
{
char input[25] = {};
strncpy(input, inputStr.c_str(), 25 - 1);
input[25 - 1] = '\0';
uint32_t b_output[5] = {0x67452301,
0xefcdab89,
0x98badcfe,
0x10325476,
0xc3d2e1f0};
uint32_t b_input[16] = {0,
0,
0,
0,
0,
0,
0x32353845,
0x41464135,
0x2d453931,
0x342d3437,
0x44412d39,
0x3543412d,
0x43354142,
0x30444338,
0x35423131,
0x80000000};
for (int i = 0; i < 6; i++) {
b_input[i] = (input[4 * i + 3] & 0xff) |
(input[4 * i + 2] & 0xff) << 8 |
(input[4 * i + 1] & 0xff) << 16 |
(input[4 * i + 0] & 0xff) << 24;
}
sha1(b_output, b_input);
uint32_t last_b[16] =
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 480};
sha1(b_output, last_b);
for (int i = 0; i < 5; i++) {
uint32_t tmp = b_output[i];
char *bytes = (char *)&b_output[i];
bytes[3] = tmp & 0xff;
bytes[2] = (tmp >> 8) & 0xff;
bytes[1] = (tmp >> 16) & 0xff;
bytes[0] = (tmp >> 24) & 0xff;
}
base64((unsigned char *)b_output, output);
}
};
@@ -0,0 +1,25 @@
/*
* IXWebSocketHttpHeaders.h
* Author: Benjamin Sergeant
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include "IXCancellationRequest.h"
#include "IXStrCaseCompare.h"
#include <map>
#include <memory>
#include <string>
namespace ix
{
class Socket;
using WebSocketHttpHeaders =
std::map<std::string, std::string, CaseInsensitiveLess>;
std::pair<bool, WebSocketHttpHeaders>
parseHttpHeaders(std::unique_ptr<Socket> &socket,
const CancellationRequest &isCancellationRequested);
} // namespace ix
@@ -0,0 +1,35 @@
/*
* IXWebSocketInitResult.h
* Author: Benjamin Sergeant
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include "IXWebSocketHttpHeaders.h"
namespace ix
{
struct WebSocketInitResult {
bool success;
int http_status;
std::string errorStr;
WebSocketHttpHeaders headers;
std::string uri;
std::string protocol;
WebSocketInitResult(bool s = false,
int status = 0,
const std::string &e = std::string(),
WebSocketHttpHeaders h = WebSocketHttpHeaders(),
const std::string &u = std::string())
{
success = s;
http_status = status;
errorStr = e;
headers = h;
uri = u;
protocol = h["Sec-WebSocket-Protocol"];
}
};
} // namespace ix
+55
View File
@@ -0,0 +1,55 @@
/*
* IXWebSocketMessage.h
* Author: Benjamin Sergeant
* Copyright (c) 2017-2019 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include "IXWebSocketCloseInfo.h"
#include "IXWebSocketErrorInfo.h"
#include "IXWebSocketMessageType.h"
#include "IXWebSocketOpenInfo.h"
#include <memory>
#include <string>
namespace ix
{
struct WebSocketMessage {
WebSocketMessageType type;
const std::string &str;
size_t wireSize;
WebSocketErrorInfo errorInfo;
WebSocketOpenInfo openInfo;
WebSocketCloseInfo closeInfo;
bool binary;
WebSocketMessage(WebSocketMessageType t,
const std::string &s,
size_t w,
WebSocketErrorInfo e,
WebSocketOpenInfo o,
WebSocketCloseInfo c,
bool b = false)
: type(t), str(s), wireSize(w), errorInfo(e), openInfo(o), closeInfo(c),
binary(b)
{
;
}
/**
* @brief Deleted overload to prevent binding `str` to a temporary, which
* would cause undefined behavior since class members don't extend lifetime
* beyond the constructor call.
*/
WebSocketMessage(WebSocketMessageType t,
std::string &&s,
size_t w,
WebSocketErrorInfo e,
WebSocketOpenInfo o,
WebSocketCloseInfo c,
bool b = false) = delete;
};
using WebSocketMessagePtr = std::unique_ptr<WebSocketMessage>;
} // namespace ix
@@ -0,0 +1,20 @@
/*
* IXWebSocketMessageType.h
* Author: Benjamin Sergeant
* Copyright (c) 2017-2019 Machine Zone, Inc. All rights reserved.
*/
#pragma once
namespace ix
{
enum class WebSocketMessageType {
Message = 0,
Open = 1,
Close = 2,
Error = 3,
Ping = 4,
Pong = 5,
Fragment = 6
};
}
+28
View File
@@ -0,0 +1,28 @@
/*
* IXWebSocketOpenInfo.h
* Author: Benjamin Sergeant
* Copyright (c) 2017-2019 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include "IXWebSocketHttpHeaders.h"
#include <cstdint>
#include <string>
namespace ix
{
struct WebSocketOpenInfo {
std::string uri;
WebSocketHttpHeaders headers;
std::string protocol;
WebSocketOpenInfo(const std::string &u = std::string(),
const WebSocketHttpHeaders &h = WebSocketHttpHeaders(),
const std::string &p = std::string())
: uri(u), headers(h), protocol(p)
{
;
}
};
} // namespace ix
@@ -0,0 +1,64 @@
/*
* Copyright (c) 2015, Peter Thorson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the WebSocket++ Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Author: Benjamin Sergeant
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
*
* Adapted from websocketpp/extensions/permessage_deflate/enabled.hpp
* (same license as MZ: https://opensource.org/licenses/BSD-3-Clause)
*/
#pragma once
#include <memory>
#include <string>
namespace ix
{
class WebSocketPerMessageDeflateOptions;
class WebSocketPerMessageDeflateCompressor;
class WebSocketPerMessageDeflateDecompressor;
class WebSocketPerMessageDeflate
{
public:
WebSocketPerMessageDeflate();
~WebSocketPerMessageDeflate();
bool
init(const WebSocketPerMessageDeflateOptions &perMessageDeflateOptions);
bool compress(const std::string &in, std::string &out);
bool decompress(const std::string &in, std::string &out);
private:
std::unique_ptr<WebSocketPerMessageDeflateCompressor> _compressor;
std::unique_ptr<WebSocketPerMessageDeflateDecompressor> _decompressor;
};
using WebSocketPerMessageDeflatePtr =
std::unique_ptr<WebSocketPerMessageDeflate>;
} // namespace ix
@@ -0,0 +1,60 @@
/*
* IXWebSocketPerMessageDeflateCodec.h
* Author: Benjamin Sergeant
* Copyright (c) 2018-2019 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#ifdef IXWEBSOCKET_USE_ZLIB
#include "zlib.h"
#endif
#include <array>
#include <string>
#include <vector>
namespace ix
{
class WebSocketPerMessageDeflateCompressor
{
public:
WebSocketPerMessageDeflateCompressor();
~WebSocketPerMessageDeflateCompressor();
bool init(uint8_t deflateBits, bool clientNoContextTakeOver);
bool compress(const std::string &in, std::string &out);
bool compress(const std::string &in, std::vector<uint8_t> &out);
bool compress(const std::vector<uint8_t> &in, std::string &out);
bool compress(const std::vector<uint8_t> &in, std::vector<uint8_t> &out);
private:
template <typename T, typename S> bool compressData(const T &in, S &out);
template <typename T> bool endsWithEmptyUnCompressedBlock(const T &value);
int _flush;
std::array<unsigned char, 1 << 14> _compressBuffer;
#ifdef IXWEBSOCKET_USE_ZLIB
z_stream _deflateState;
#endif
};
class WebSocketPerMessageDeflateDecompressor
{
public:
WebSocketPerMessageDeflateDecompressor();
~WebSocketPerMessageDeflateDecompressor();
bool init(uint8_t inflateBits, bool clientNoContextTakeOver);
bool decompress(const std::string &in, std::string &out);
private:
int _flush;
std::array<unsigned char, 1 << 14> _compressBuffer;
#ifdef IXWEBSOCKET_USE_ZLIB
z_stream _inflateState;
#endif
};
} // namespace ix
@@ -0,0 +1,47 @@
/*
* IXWebSocketPerMessageDeflateOptions.h
* Author: Benjamin Sergeant
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include <string>
namespace ix
{
class WebSocketPerMessageDeflateOptions
{
public:
WebSocketPerMessageDeflateOptions(
bool enabled = false,
bool clientNoContextTakeover = false,
bool serverNoContextTakeover = false,
uint8_t clientMaxWindowBits = kDefaultClientMaxWindowBits,
uint8_t serverMaxWindowBits = kDefaultServerMaxWindowBits);
WebSocketPerMessageDeflateOptions(std::string extension);
std::string generateHeader();
bool enabled() const;
bool getClientNoContextTakeover() const;
bool getServerNoContextTakeover() const;
uint8_t getServerMaxWindowBits() const;
uint8_t getClientMaxWindowBits() const;
static bool startsWith(const std::string &str, const std::string &start);
static std::string removeSpaces(const std::string &str);
static uint8_t const kDefaultClientMaxWindowBits;
static uint8_t const kDefaultServerMaxWindowBits;
private:
bool _enabled;
bool _clientNoContextTakeover;
bool _serverNoContextTakeover;
uint8_t _clientMaxWindowBits;
uint8_t _serverMaxWindowBits;
void sanitizeClientMaxWindowBits();
};
} // namespace ix
@@ -0,0 +1,24 @@
/*
* IXWebSocketProxyServer.h
* Author: Benjamin Sergeant
* Copyright (c) 2019-2020 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include "IXSocketTLSOptions.h"
#include <cstdint>
#include <map>
#include <stddef.h>
#include <string>
namespace ix
{
using RemoteUrlsMapping = std::map<std::string, std::string>;
int websocket_proxy_server_main(int port,
const std::string &hostname,
const ix::SocketTLSOptions &tlsOptions,
const std::string &remoteUrl,
const RemoteUrlsMapping &remoteUrlsMapping,
bool verbose);
} // namespace ix
+26
View File
@@ -0,0 +1,26 @@
/*
* IXWebSocketSendInfo.h
* Author: Benjamin Sergeant
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
*/
#pragma once
namespace ix
{
struct WebSocketSendInfo {
bool success;
bool compressionError;
size_t payloadSize;
size_t wireSize;
WebSocketSendInfo(bool s = false,
bool c = false,
size_t p = 0,
size_t w = 0)
: success(s), compressionError(c), payloadSize(p), wireSize(w)
{
;
}
};
} // namespace ix
+84
View File
@@ -0,0 +1,84 @@
/*
* IXWebSocketServer.h
* Author: Benjamin Sergeant
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include "IXSocketServer.h"
#include "IXWebSocket.h"
#include <condition_variable>
#include <functional>
#include <memory>
#include <mutex>
#include <set>
#include <string>
#include <thread>
#include <utility> // pair
namespace ix
{
class WebSocketServer : public SocketServer
{
public:
using OnConnectionCallback =
std::function<void(std::weak_ptr<WebSocket>,
std::shared_ptr<ConnectionState>)>;
using OnClientMessageCallback =
std::function<void(std::shared_ptr<ConnectionState>,
WebSocket &,
const WebSocketMessagePtr &)>;
WebSocketServer(
int port = SocketServer::kDefaultPort,
const std::string &host = SocketServer::kDefaultHost,
int backlog = SocketServer::kDefaultTcpBacklog,
size_t maxConnections = SocketServer::kDefaultMaxConnections,
int handshakeTimeoutSecs =
WebSocketServer::kDefaultHandShakeTimeoutSecs,
int addressFamily = SocketServer::kDefaultAddressFamily);
virtual ~WebSocketServer();
virtual void stop() final;
void enablePong();
void disablePong();
void disablePerMessageDeflate();
void setOnConnectionCallback(const OnConnectionCallback &callback);
void setOnClientMessageCallback(const OnClientMessageCallback &callback);
// Get all the connected clients
std::set<std::shared_ptr<WebSocket>> getClients();
void makeBroadcastServer();
bool listenAndStart();
const static int kDefaultHandShakeTimeoutSecs;
int getHandshakeTimeoutSecs();
bool isPongEnabled();
bool isPerMessageDeflateEnabled();
private:
// Member variables
int _handshakeTimeoutSecs;
bool _enablePong;
bool _enablePerMessageDeflate;
OnConnectionCallback _onConnectionCallback;
OnClientMessageCallback _onClientMessageCallback;
std::mutex _clientsMutex;
std::set<std::shared_ptr<WebSocket>> _clients;
const static bool kDefaultEnablePong;
// Methods
virtual void
handleConnection(std::unique_ptr<Socket> socket,
std::shared_ptr<ConnectionState> connectionState);
virtual size_t getConnectedClientsCount() final;
};
} // namespace ix
+261
View File
@@ -0,0 +1,261 @@
/*
* IXWebSocketTransport.h
* Author: Benjamin Sergeant
* Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved.
*/
#pragma once
//
// Adapted from https://github.com/dhbaird/easywsclient
//
#include "IXCancellationRequest.h"
#include "IXProgressCallback.h"
#include "IXSocketTLSOptions.h"
#include "IXWebSocketCloseConstants.h"
#include "IXWebSocketHandshake.h"
#include "IXWebSocketHttpHeaders.h"
#include "IXWebSocketPerMessageDeflate.h"
#include "IXWebSocketPerMessageDeflateOptions.h"
#include "IXWebSocketSendInfo.h"
#include <atomic>
#include <functional>
#include <list>
#include <memory>
#include <mutex>
#include <string>
#include <vector>
namespace ix
{
class Socket;
enum class SendMessageKind { Text, Binary, Ping };
class WebSocketTransport
{
public:
enum class ReadyState { CLOSING, CLOSED, CONNECTING, OPEN };
enum class MessageKind { MSG_TEXT, MSG_BINARY, PING, PONG, FRAGMENT };
enum class PollResult { Succeeded, AbnormalClose, CannotFlushSendBuffer };
using OnMessageCallback =
std::function<void(const std::string &, size_t, bool, MessageKind)>;
using OnCloseCallback =
std::function<void(uint16_t, const std::string &, size_t, bool)>;
WebSocketTransport();
~WebSocketTransport();
void configure(
const WebSocketPerMessageDeflateOptions &perMessageDeflateOptions,
const SocketTLSOptions &socketTLSOptions,
bool enablePong,
int pingIntervalSecs);
// Client
WebSocketInitResult connectToUrl(const std::string &url,
const WebSocketHttpHeaders &headers,
int timeoutSecs);
// Server
WebSocketInitResult connectToSocket(std::unique_ptr<Socket> socket,
int timeoutSecs,
bool enablePerMessageDeflate);
PollResult poll();
WebSocketSendInfo sendBinary(const std::string &message,
const OnProgressCallback &onProgressCallback);
WebSocketSendInfo sendText(const std::string &message,
const OnProgressCallback &onProgressCallback);
WebSocketSendInfo sendPing(const std::string &message);
void close(uint16_t code = WebSocketCloseConstants::kNormalClosureCode,
const std::string &reason =
WebSocketCloseConstants::kNormalClosureMessage,
size_t closeWireSize = 0,
bool remote = false);
void closeSocket();
ReadyState getReadyState() const;
void setReadyState(ReadyState readyState);
void setOnCloseCallback(const OnCloseCallback &onCloseCallback);
void dispatch(PollResult pollResult,
const OnMessageCallback &onMessageCallback);
size_t bufferedAmount() const;
// internal
WebSocketSendInfo sendHeartBeat();
private:
std::string _url;
struct wsheader_type {
unsigned header_size;
bool fin;
bool rsv1;
bool rsv2;
bool rsv3;
bool mask;
enum opcode_type {
CONTINUATION = 0x0,
TEXT_FRAME = 0x1,
BINARY_FRAME = 0x2,
CLOSE = 8,
PING = 9,
PONG = 0xa,
} opcode;
int N0;
uint64_t N;
uint8_t masking_key[4];
};
// Tells whether we should mask the data we send.
// client should mask but server should not
std::atomic<bool> _useMask;
// Tells whether we should flush the send buffer before
// saying that a send is complete. This is the mode for server code.
std::atomic<bool> _blockingSend;
// Buffer for reading from our socket. That buffer is never resized.
std::vector<uint8_t> _readbuf;
// Contains all messages that were fetched in the last socket read.
// This could be a mix of control messages (Close, Ping, etc...) and
// data messages. That buffer is resized
std::vector<uint8_t> _rxbuf;
// Contains all messages that are waiting to be sent
std::vector<uint8_t> _txbuf;
mutable std::mutex _txbufMutex;
// Hold fragments for multi-fragments messages in a list. We support
// receiving very large messages (tested messages up to 700M) and we cannot
// put them in a single buffer that is resized, as this operation can be
// slow when a buffer has its size increased 2 fold, while appending to a
// list has a fixed cost.
std::list<std::string> _chunks;
// Record the message kind (will be TEXT or BINARY) for a fragmented
// message, present in the first chunk, since the final chunk will be a
// CONTINUATION opcode and doesn't tell the full message kind
MessageKind _fragmentedMessageKind;
// Ditto for whether a message is compressed
bool _receivedMessageCompressed;
// Fragments are 32K long
static constexpr size_t kChunkSize = 1 << 15;
// Underlying TCP socket
std::unique_ptr<Socket> _socket;
std::mutex _socketMutex;
// Hold the state of the connection (OPEN, CLOSED, etc...)
std::atomic<ReadyState> _readyState;
OnCloseCallback _onCloseCallback;
std::string _closeReason;
mutable std::mutex _closeReasonMutex;
std::atomic<uint16_t> _closeCode;
std::atomic<size_t> _closeWireSize;
std::atomic<bool> _closeRemote;
// Data used for Per Message Deflate compression (with zlib)
WebSocketPerMessageDeflatePtr _perMessageDeflate;
WebSocketPerMessageDeflateOptions _perMessageDeflateOptions;
std::atomic<bool> _enablePerMessageDeflate;
std::string _decompressedMessage;
std::string _compressedMessage;
// Used to control TLS connection behavior
SocketTLSOptions _socketTLSOptions;
// Used to cancel dns lookup + socket connect + http upgrade
std::atomic<bool> _requestInitCancellation;
mutable std::mutex _closingTimePointMutex;
std::chrono::time_point<std::chrono::steady_clock> _closingTimePoint;
static const int kClosingMaximumWaitingDelayInMs;
// enable auto response to ping
std::atomic<bool> _enablePong;
static const bool kDefaultEnablePong;
// Optional ping and pong timeout
int _pingIntervalSecs;
std::atomic<bool> _pongReceived;
static const int kDefaultPingIntervalSecs;
static const std::string kPingMessage;
std::atomic<uint64_t> _pingCount;
// We record when ping are being sent so that we can know when to send the
// next one
mutable std::mutex _lastSendPingTimePointMutex;
std::chrono::time_point<std::chrono::steady_clock> _lastSendPingTimePoint;
// If this function returns true, it is time to send a new ping
bool pingIntervalExceeded();
void initTimePointsAfterConnect();
// after calling close(), if no CLOSE frame answer is received back from the
// remote, we should close the connexion
bool closingDelayExceeded();
void sendCloseFrame(uint16_t code, const std::string &reason);
void closeSocketAndSwitchToClosedState(uint16_t code,
const std::string &reason,
size_t closeWireSize,
bool remote);
bool wakeUpFromPoll(uint64_t wakeUpCode);
bool flushSendBuffer();
bool sendOnSocket();
bool receiveFromSocket();
template <class T>
WebSocketSendInfo
sendData(wsheader_type::opcode_type type,
const T &message,
bool compress,
const OnProgressCallback &onProgressCallback = nullptr);
template <class Iterator>
bool sendFragment(wsheader_type::opcode_type type,
bool fin,
Iterator begin,
Iterator end,
bool compress);
void emitMessage(MessageKind messageKind,
const std::string &message,
bool compressedMessage,
const OnMessageCallback &onMessageCallback);
bool isSendBufferEmpty() const;
template <class Iterator>
void appendToSendBuffer(const std::vector<uint8_t> &header,
Iterator begin,
Iterator end,
uint64_t message_size,
uint8_t masking_key[4]);
unsigned getRandomUnsigned();
void unmaskReceiveBuffer(const wsheader_type &ws);
std::string getMergedChunks() const;
void setCloseReason(const std::string &reason);
const std::string &getCloseReason() const;
};
} // namespace ix
+9
View File
@@ -0,0 +1,9 @@
/*
* IXWebSocketVersion.h
* Author: Benjamin Sergeant
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#define IX_WEBSOCKET_VERSION "11.3.2"