kopia lustrzana https://github.com/jameshball/osci-render
286 wiersze
9.6 KiB
C++
286 wiersze
9.6 KiB
C++
/*
|
|
* 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 "IXWebSocketSendData.h"
|
|
#include "IXWebSocketSendInfo.h"
|
|
#include <atomic>
|
|
#include <cstdint>
|
|
#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,
|
|
HttpRequestPtr request = nullptr);
|
|
|
|
PollResult poll();
|
|
WebSocketSendInfo sendBinary(const IXWebSocketSendData& message,
|
|
const OnProgressCallback& onProgressCallback);
|
|
WebSocketSendInfo sendText(const IXWebSocketSendData& message,
|
|
const OnProgressCallback& onProgressCallback);
|
|
WebSocketSendInfo sendPing(const IXWebSocketSendData& 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;
|
|
|
|
// set ping heartbeat message
|
|
void setPingMessage(const std::string& message, SendMessageKind pingType);
|
|
|
|
// internal
|
|
// send any type of ping packet, not only 'ping' type
|
|
WebSocketSendInfo sendHeartBeat(SendMessageKind pingType);
|
|
|
|
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;
|
|
|
|
bool _setCustomMessage;
|
|
std::string _kPingMessage;
|
|
SendMessageKind _pingType;
|
|
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();
|
|
|
|
WebSocketSendInfo sendData(wsheader_type::opcode_type type,
|
|
const IXWebSocketSendData& 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
|