#pragma once
#include "Error.h"
#include "Frame.h"
#include "Message.h"
#include "DeflateExt.h"
#include "Utf8Checker.h"
#include <deque>
#include <bitset>
#include <iterator>
#include <panda/refcnt.h>
#include <panda/string.h>
#include <panda/optional.h>
#include <panda/protocol/http/MessageParser.h>
namespace panda { namespace protocol { namespace websocket {
using panda::string;
using panda::IteratorPair;
enum class DeflateFlag { NO, DEFAULT, YES };
struct Parser : virtual panda::Refcnt {
// include child classes to solve cross-dependencies without moving one-liners to *.cc files (to avoid performance loses)
#include "Parser-FrameSender.h"
#include "Parser-MessageBuilder.h"
#include "Parser-MessageIterator.h"
using DeflateConfig = DeflateExt::Config;
struct Config {
Config () {}
size_t max_frame_size = 0;
size_t max_message_size = 0;
size_t max_handshake_size = http::SIZE_UNLIMITED;
bool check_utf8 = false;
optional<DeflateConfig> deflate = DeflateConfig();
};
void configure (const Config& cfg);
size_t max_frame_size () const { return _max_frame_size; }
size_t max_message_size () const { return _max_message_size; }
size_t max_handshake_size () const { return _max_handshake_size; }
bool established () const { return _flags[ESTABLISHED]; }
bool recv_closed () const { return _flags[RECV_CLOSED]; }
bool send_closed () const { return _flags[SEND_CLOSED]; }
FrameIteratorPair get_frames () {
return FrameIteratorPair(FrameIterator(this, _get_frame()), FrameIterator(this, NULL));
}
MessageIteratorPair get_messages () {
return MessageIteratorPair(MessageIterator(this, _get_message()), MessageIterator(this, NULL));
}
FrameIteratorPair get_frames (string&& buf) {
if (_buffer) _buffer += std::move(buf); // user has not iterated previous call to get_frames till the end or remainder after handshake on client side
else _buffer = std::move(buf);
return get_frames();
}
FrameIteratorPair get_frames (const string& buf) {
if (_buffer) _buffer += buf;
else _buffer = buf;
return get_frames();
}
MessageIteratorPair get_messages (string&& buf) {
if (_buffer) _buffer += std::move(buf);
else _buffer = std::move(buf);
return get_messages();
}
MessageIteratorPair get_messages (const string& buf) {
if (_buffer) _buffer += buf;
else _buffer = buf;
return get_messages();
}
FrameSender start_message (Opcode opcode = Opcode::BINARY, DeflateFlag deflate = DeflateFlag::NO) {
_check_send();
if (_flags[SEND_FRAME]) throw Error("can't start message: previous message wasn't finished");
_flags.set(SEND_FRAME);
_send_opcode = opcode;
if (_deflate_ext && deflate != DeflateFlag::NO) _flags.set(SEND_DEFLATE);
return FrameSender(*this);
}
FrameSender start_message (DeflateFlag deflate) { return start_message(Opcode::BINARY, deflate); }
string send_frame (IsFinal final = IsFinal::NO) {
bool deflate = _flags[SEND_DEFLATE];
auto header = _prepare_frame_header(final);
if (final == IsFinal::YES && deflate) _deflate_ext->reset_tx();
return Frame::compile(header);
}
string send_frame (string_view payload, IsFinal final = IsFinal::NO) {
bool deflate = _flags[SEND_DEFLATE];
auto header = _prepare_frame_header(final);
return deflate ? Frame::compile(header, _deflate_ext->compress(payload, final)) :
Frame::compile(header, payload);
}
template <typename It, typename T = decltype(*std::declval<It>()), typename = std::enable_if_t<std::is_convertible<T, string_view>::value>>
string send_frame (It&& payload_begin, It&& payload_end, IsFinal final = IsFinal::NO) {
bool deflate = _flags[SEND_DEFLATE];
auto header = _prepare_frame_header(final);
return deflate ? Frame::compile(header, _deflate_ext->compress(payload_begin, payload_end, final)) :
Frame::compile(header, payload_begin, payload_end);
}
MessageBuilder message () { return MessageBuilder(*this); }
string send_control (Opcode opcode, string_view payload = {}) {
if (payload.length() > Frame::MAX_CONTROL_PAYLOAD) {
panda_log_critical("control frame payload is too long");
payload = payload.substr(0, Frame::MAX_CONTROL_PAYLOAD);
}
auto header = _prepare_control_header(opcode);
return Frame::compile(header, payload);
}
string send_ping (string_view payload = {}) { return send_control(Opcode::PING, payload); }
string send_pong (string_view payload = {}) { return send_control(Opcode::PONG, payload); }
string send_close () { return send_control(Opcode::CLOSE); }
string send_close (uint16_t code, string_view close_payload = {}) {
string payload = FrameHeader::compile_close_payload(code, close_payload);
return send_control(Opcode::CLOSE, payload);
}
uint16_t suggested_close_code () const { return _suggested_close_code; }
virtual void reset ();
bool is_deflate_active () const { return (bool)_deflate_ext; }
const optional<DeflateConfig>& deflate_config () const { return _deflate_cfg; }
optional<DeflateConfig> effective_deflate_config () const {
if (!_deflate_ext) return {};
return _deflate_ext->effective_config();
}
void no_deflate () {
if (!_flags[ESTABLISHED]) _deflate_cfg.reset();
}
bool should_deflate(Opcode opcode, size_t payload_length) const;
virtual ~Parser () {}
protected:
using DeflateExtPtr = std::unique_ptr<DeflateExt>;
static const int ESTABLISHED = 1; // connection has been established
static const int RECV_FRAME = 2; // frame mode receive
static const int RECV_MESSAGE = 3; // message mode receive
static const int RECV_INFLATE = 4; // receiving compressed message
static const int RECV_TEXT = 5; // receiving text message
static const int RECV_CLOSED = 6; // no more messages from peer (close packet received)
static const int SEND_FRAME = 7; // outgoing message started
static const int SEND_DEFLATE = 8; // sending compressed message
static const int SEND_CLOSED = 9; // no more messages from user (close packet sent)
static const int LAST_FLAG = SEND_CLOSED;
size_t _max_frame_size;
size_t _max_message_size;
size_t _max_handshake_size;
bool _check_utf8;
std::bitset<32> _flags = 0;
string _buffer;
optional<DeflateConfig> _deflate_cfg;
DeflateExtPtr _deflate_ext;
Parser (bool recv_mask_required, Config cfg = Config()) : _recv_mask_required(recv_mask_required), _message_frame(recv_mask_required, cfg.max_frame_size) {
configure(cfg);
}
private:
bool _recv_mask_required;
FrameSP _frame; // current frame being received (frame mode)
int _frame_count = 0; // frame count for current message being received
MessageSP _message; // current message being received (message mode)
Frame _message_frame; // current frame being received (message mode)
int _sent_frame_count = 0; // frame count for current message being sent (frame mode)
Opcode _send_opcode; // opcode for first frame to be sent (frame mode)
uint16_t _suggested_close_code = 0; // suggested close code to send to peer after error or receiving close frame from peer
Utf8Checker _utf8_checker;
std::deque<string> _simple_payload_tmp;
FrameSP _get_frame ();
MessageSP _get_message ();
bool _parse_frame (Frame&);
void _check_send () const {
if (!_flags[ESTABLISHED]) throw Error("not established");
if (_flags[SEND_CLOSED]) throw Error("close sent, can't send anymore");
}
FrameHeader _prepare_control_header (Opcode);
FrameHeader _prepare_frame_header (IsFinal);
friend struct FrameSender;
friend struct MessageBuilder;
};
using FrameIteratorPair = Parser::FrameIteratorPair;
using FrameIterator = Parser::FrameIterator;
using MessageIteratorPair = Parser::MessageIteratorPair;
using MessageIterator = Parser::MessageIterator;
using FrameSender = Parser::FrameSender;
using MessageBuilder = Parser::MessageBuilder;
using ParserSP = iptr<Parser>;
}}}