2018-09-27 23:56:48 +02:00
|
|
|
/*
|
|
|
|
* 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 <string>
|
|
|
|
#include <vector>
|
|
|
|
#include <functional>
|
|
|
|
#include <memory>
|
|
|
|
#include <mutex>
|
2018-10-01 23:46:11 +02:00
|
|
|
#include <atomic>
|
2019-02-21 03:59:07 +01:00
|
|
|
#include <list>
|
2018-09-27 23:56:48 +02:00
|
|
|
|
2018-11-10 03:23:49 +01:00
|
|
|
#include "IXWebSocketSendInfo.h"
|
2018-11-07 23:54:44 +01:00
|
|
|
#include "IXWebSocketPerMessageDeflate.h"
|
2018-11-10 03:23:49 +01:00
|
|
|
#include "IXWebSocketPerMessageDeflateOptions.h"
|
|
|
|
#include "IXWebSocketHttpHeaders.h"
|
2019-01-02 16:45:07 +01:00
|
|
|
#include "IXCancellationRequest.h"
|
2019-01-03 05:07:54 +01:00
|
|
|
#include "IXWebSocketHandshake.h"
|
2019-02-21 03:59:07 +01:00
|
|
|
#include "IXProgressCallback.h"
|
2018-11-07 23:54:44 +01:00
|
|
|
|
2019-02-21 03:59:07 +01:00
|
|
|
namespace ix
|
2018-09-27 23:56:48 +02:00
|
|
|
{
|
|
|
|
class Socket;
|
|
|
|
|
|
|
|
class WebSocketTransport
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
enum ReadyStateValues
|
|
|
|
{
|
|
|
|
CLOSING,
|
|
|
|
CLOSED,
|
|
|
|
CONNECTING,
|
|
|
|
OPEN
|
|
|
|
};
|
|
|
|
|
2018-10-25 21:01:47 +02:00
|
|
|
enum MessageKind
|
|
|
|
{
|
|
|
|
MSG,
|
|
|
|
PING,
|
2019-03-11 19:12:43 +01:00
|
|
|
PONG,
|
|
|
|
FRAGMENT
|
2018-10-25 21:01:47 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
using OnMessageCallback = std::function<void(const std::string&,
|
2018-11-10 03:23:49 +01:00
|
|
|
size_t,
|
2018-11-15 00:52:28 +01:00
|
|
|
bool,
|
2018-10-25 21:01:47 +02:00
|
|
|
MessageKind)>;
|
2018-10-26 03:51:19 +02:00
|
|
|
using OnCloseCallback = std::function<void(uint16_t,
|
2018-11-10 03:23:49 +01:00
|
|
|
const std::string&,
|
|
|
|
size_t)>;
|
2018-09-27 23:56:48 +02:00
|
|
|
|
|
|
|
WebSocketTransport();
|
|
|
|
~WebSocketTransport();
|
|
|
|
|
2019-01-24 21:42:49 +01:00
|
|
|
void configure(const WebSocketPerMessageDeflateOptions& perMessageDeflateOptions,
|
2019-03-20 05:31:43 +01:00
|
|
|
int heartBeatPeriod);
|
2018-09-27 23:56:48 +02:00
|
|
|
|
2019-01-04 03:33:08 +01:00
|
|
|
WebSocketInitResult connectToUrl(const std::string& url, // Client
|
|
|
|
int timeoutSecs);
|
|
|
|
WebSocketInitResult connectToSocket(int fd, // Server
|
|
|
|
int timeoutSecs);
|
2018-12-30 06:53:33 +01:00
|
|
|
|
2018-09-27 23:56:48 +02:00
|
|
|
void poll();
|
2019-02-21 03:59:07 +01:00
|
|
|
WebSocketSendInfo sendBinary(const std::string& message,
|
|
|
|
const OnProgressCallback& onProgressCallback);
|
2018-11-10 03:23:49 +01:00
|
|
|
WebSocketSendInfo sendPing(const std::string& message);
|
2018-09-27 23:56:48 +02:00
|
|
|
void close();
|
|
|
|
ReadyStateValues getReadyState() const;
|
|
|
|
void setReadyState(ReadyStateValues readyStateValue);
|
2018-10-26 03:51:19 +02:00
|
|
|
void setOnCloseCallback(const OnCloseCallback& onCloseCallback);
|
2018-09-27 23:56:48 +02:00
|
|
|
void dispatch(const OnMessageCallback& onMessageCallback);
|
2019-03-14 07:09:45 +01:00
|
|
|
size_t bufferedAmount() const;
|
2018-09-27 23:56:48 +02:00
|
|
|
|
|
|
|
private:
|
|
|
|
std::string _url;
|
|
|
|
|
|
|
|
struct wsheader_type {
|
|
|
|
unsigned header_size;
|
|
|
|
bool fin;
|
2018-11-07 20:45:17 +01:00
|
|
|
bool rsv1;
|
2018-09-27 23:56:48 +02:00
|
|
|
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];
|
|
|
|
};
|
|
|
|
|
2019-02-21 03:59:07 +01:00
|
|
|
// 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
|
2018-09-27 23:56:48 +02:00
|
|
|
std::vector<uint8_t> _rxbuf;
|
2019-02-21 03:59:07 +01:00
|
|
|
|
|
|
|
// Contains all messages that are waiting to be sent
|
2018-09-27 23:56:48 +02:00
|
|
|
std::vector<uint8_t> _txbuf;
|
|
|
|
mutable std::mutex _txbufMutex;
|
|
|
|
|
2019-02-21 03:59:07 +01:00
|
|
|
// 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::vector<uint8_t>> _chunks;
|
|
|
|
|
|
|
|
// Fragments are 32K long
|
|
|
|
static constexpr size_t kChunkSize = 1 << 15;
|
|
|
|
|
|
|
|
// Underlying TCP socket
|
2018-09-27 23:56:48 +02:00
|
|
|
std::shared_ptr<Socket> _socket;
|
|
|
|
|
2019-02-21 03:59:07 +01:00
|
|
|
// Hold the state of the connection (OPEN, CLOSED, etc...)
|
2018-09-27 23:56:48 +02:00
|
|
|
std::atomic<ReadyStateValues> _readyState;
|
|
|
|
|
2018-10-26 03:51:19 +02:00
|
|
|
OnCloseCallback _onCloseCallback;
|
|
|
|
uint16_t _closeCode;
|
|
|
|
std::string _closeReason;
|
2018-11-10 03:23:49 +01:00
|
|
|
size_t _closeWireSize;
|
2018-10-26 03:51:19 +02:00
|
|
|
mutable std::mutex _closeDataMutex;
|
2018-09-27 23:56:48 +02:00
|
|
|
|
2019-02-21 03:59:07 +01:00
|
|
|
// Data used for Per Message Deflate compression (with zlib)
|
2018-11-07 23:54:44 +01:00
|
|
|
WebSocketPerMessageDeflate _perMessageDeflate;
|
2018-11-10 03:23:49 +01:00
|
|
|
WebSocketPerMessageDeflateOptions _perMessageDeflateOptions;
|
|
|
|
std::atomic<bool> _enablePerMessageDeflate;
|
2018-11-07 23:54:44 +01:00
|
|
|
|
2018-12-15 01:28:11 +01:00
|
|
|
// Used to cancel dns lookup + socket connect + http upgrade
|
|
|
|
std::atomic<bool> _requestInitCancellation;
|
2019-02-21 03:59:07 +01:00
|
|
|
|
2019-01-24 21:42:49 +01:00
|
|
|
// Optional Heartbeat
|
|
|
|
int _heartBeatPeriod;
|
|
|
|
static const int kDefaultHeartBeatPeriod;
|
|
|
|
const static std::string kHeartBeatPingMessage;
|
2019-01-26 01:11:39 +01:00
|
|
|
mutable std::mutex _lastSendTimePointMutex;
|
|
|
|
std::chrono::time_point<std::chrono::steady_clock> _lastSendTimePoint;
|
|
|
|
|
2019-03-21 02:34:24 +01:00
|
|
|
// No data was send through the socket for longer than the heartbeat period
|
2019-01-29 00:14:49 +01:00
|
|
|
bool heartBeatPeriodExceeded();
|
2018-12-15 01:28:11 +01:00
|
|
|
|
2018-09-27 23:56:48 +02:00
|
|
|
void sendOnSocket();
|
2019-02-21 03:59:07 +01:00
|
|
|
WebSocketSendInfo sendData(wsheader_type::opcode_type type,
|
2018-11-12 18:00:55 +01:00
|
|
|
const std::string& message,
|
2019-02-21 03:59:07 +01:00
|
|
|
bool compress,
|
|
|
|
const OnProgressCallback& onProgressCallback = nullptr);
|
|
|
|
|
|
|
|
void sendFragment(wsheader_type::opcode_type type,
|
|
|
|
bool fin,
|
|
|
|
std::string::const_iterator begin,
|
|
|
|
std::string::const_iterator end,
|
|
|
|
bool compress);
|
2018-11-10 03:23:49 +01:00
|
|
|
|
2019-02-21 03:59:07 +01:00
|
|
|
void emitMessage(MessageKind messageKind,
|
2018-11-10 03:23:49 +01:00
|
|
|
const std::string& message,
|
|
|
|
const wsheader_type& ws,
|
|
|
|
const OnMessageCallback& onMessageCallback);
|
2018-09-27 23:56:48 +02:00
|
|
|
|
|
|
|
bool isSendBufferEmpty() const;
|
|
|
|
void appendToSendBuffer(const std::vector<uint8_t>& header,
|
|
|
|
std::string::const_iterator begin,
|
|
|
|
std::string::const_iterator end,
|
|
|
|
uint64_t message_size,
|
|
|
|
uint8_t masking_key[4]);
|
|
|
|
|
|
|
|
unsigned getRandomUnsigned();
|
2018-10-26 03:51:19 +02:00
|
|
|
void unmaskReceiveBuffer(const wsheader_type& ws);
|
2019-02-21 03:59:07 +01:00
|
|
|
|
|
|
|
std::string getMergedChunks() const;
|
2018-09-27 23:56:48 +02:00
|
|
|
};
|
|
|
|
}
|