ws_parser.hpp
Go to the documentation of this file.
/*
restinio
*/
#pragma once
#include <restinio/exception.hpp>
#include <restinio/websocket/message.hpp>
#include <restinio/utils/impl/bitops.hpp>
#include <cstdint>
#include <vector>
#include <list>
#include <stdexcept>
namespace restinio
{
namespace websocket
{
namespace basic
{
using byte_t = unsigned char;
using raw_data_t = std::string;
namespace impl
{
constexpr size_t websocket_first_two_bytes_size = 2;
constexpr size_t websocket_max_payload_size_without_ext = 125;
constexpr size_t websocket_short_ext_payload_length = 2;
constexpr size_t websocket_long_ext_payload_length = 8;
constexpr size_t websocket_short_ext_len_code = 126;
constexpr size_t websocket_long_ext_len_code = 127;
constexpr size_t websocket_masking_key_size = 4;
constexpr byte_t bit_flag_7 = 0x80;
constexpr byte_t bit_flag_6 = 0x40;
constexpr byte_t bit_flag_5 = 0x20;
constexpr byte_t bit_flag_4 = 0x10;
constexpr byte_t opcode_mask = 0x0F;
constexpr byte_t payload_len_mask = 0x7F;
//
// message_details_t
//
class message_details_t
{
public:
message_details_t() = default;
message_details_t(
final_frame_flag_t final_flag,
opcode_t opcode,
size_t payload_len ) noexcept
: m_final_flag{ final_frame == final_flag }
, m_opcode{ opcode }
{
init_payload_len( payload_len );
}
message_details_t(
final_frame_flag_t final_flag,
opcode_t opcode,
size_t payload_len,
std::uint32_t masking_key )
: m_final_flag{ final_frame == final_flag }
, m_opcode{ opcode }
, m_mask_flag( true )
, m_masking_key( masking_key )
{
init_payload_len( payload_len );
}
std::uint64_t
payload_len() const
{
// 126 and 127 are codes of ext payload. 125 and lower are real payload len.
return m_payload_len > websocket_max_payload_size_without_ext? m_ext_payload_len: m_payload_len;
}
void
set_masking_key( std::uint32_t value )
{
m_masking_key = value;
m_mask_flag = true;
}
bool m_final_flag = true;
bool m_rsv1_flag = false;
bool m_rsv2_flag = false;
bool m_rsv3_flag = false;
opcode_t m_opcode = opcode_t::continuation_frame;
bool m_mask_flag = false;
std::uint8_t m_payload_len = 0;
std::uint64_t m_ext_payload_len = 0;
std::uint32_t m_masking_key = 0;
private:
void
init_payload_len( size_t payload_len )
{
if( payload_len > websocket_max_payload_size_without_ext )
{
// if payload greater than 2bytes-number.
m_payload_len = payload_len > 0xFFFF ?
websocket_long_ext_len_code:
websocket_short_ext_len_code;
m_ext_payload_len = payload_len;
}
else
{
m_payload_len = static_cast< std::uint8_t >( payload_len );
}
}
};
//
// expected_data_t
//
struct expected_data_t
{
expected_data_t() = default;
expected_data_t( size_t expected_size )
: m_expected_size{ expected_size }
{
m_loaded_data.reserve( m_expected_size );
}
size_t m_expected_size{0};
raw_data_t m_loaded_data;
bool
all_bytes_loaded() const
{
return m_loaded_data.size() == m_expected_size;
}
bool
add_byte_and_check_size( byte_t byte )
{
if( m_loaded_data.size() == m_expected_size )
throw exception_t("Cannot add one more bytes to expected data.");
m_loaded_data.push_back(static_cast<raw_data_t::value_type>(byte));
return all_bytes_loaded();
}
void
reset( size_t expected_size )
{
m_expected_size = expected_size;
m_loaded_data.clear();
m_loaded_data.reserve( expected_size );
}
};
//
// read_number_from_big_endian_bytes
//
template <typename T>
inline void
read_number_from_big_endian_bytes( T & number, const raw_data_t & data )
{
number = T{};
for( const auto byte: data )
{
number <<= 8;
number |= static_cast<std::uint8_t>( byte );
}
}
//
// write_number_to_big_endian_bytes
//
template <int Bytes>
inline void
write_number_to_big_endian_bytes( std::uint64_t& number, raw_data_t & data )
{
for( auto i = 0 ; i < Bytes ; ++i )
{
auto shift_value = (Bytes - i - 1) * 8;
data.push_back( static_cast<raw_data_t::value_type>(
(number >> shift_value) & 0xFF) );
}
}
//
// ws_parser_t
//
class ws_parser_t
{
public:
size_t
parser_execute( const char * data, size_t size )
{
size_t parsed_bytes = 0;
while( parsed_bytes < size &&
m_current_state != state_t::header_parsed )
{
byte_t byte = static_cast< byte_t >( data[parsed_bytes] );
process_byte( byte );
parsed_bytes++;
}
return parsed_bytes;
}
bool
header_parsed() const
{
return m_current_state == state_t::header_parsed;
}
void
reset()
{
m_current_state = state_t::waiting_for_first_2_bytes;
m_current_msg = message_details_t();
m_expected_data.reset( websocket_first_two_bytes_size );
}
const message_details_t &
current_message() const
{
return m_current_msg;
}
private:
expected_data_t m_expected_data{ websocket_first_two_bytes_size };
message_details_t m_current_msg;
enum class state_t
{
waiting_for_first_2_bytes,
waiting_for_ext_len,
waiting_for_mask_key,
header_parsed
};
state_t m_current_state = state_t::waiting_for_first_2_bytes;
void
process_byte( byte_t byte )
{
if( m_expected_data.add_byte_and_check_size(byte) )
{
switch( m_current_state )
{
case state_t::waiting_for_first_2_bytes:
process_first_2_bytes();
break;
case state_t::waiting_for_ext_len:
process_extended_length();
break;
case state_t::waiting_for_mask_key:
process_masking_key();
break;
case state_t::header_parsed:
break;
}
}
}
void
process_first_2_bytes()
{
parse_first_2_bytes(
m_expected_data.m_loaded_data );
size_t payload_len = m_current_msg.m_payload_len;
if( payload_len > websocket_max_payload_size_without_ext )
{
size_t expected_data_size = payload_len == websocket_short_ext_len_code?
websocket_short_ext_payload_length:
websocket_long_ext_payload_length;
m_expected_data.reset( expected_data_size );
m_current_state = state_t::waiting_for_ext_len;
}
else if( m_current_msg.m_mask_flag )
{
size_t expected_data_size = websocket_masking_key_size;
m_expected_data.reset( expected_data_size );
m_current_state = state_t::waiting_for_mask_key;
}
else
{
size_t expected_data_size = payload_len;
m_expected_data.reset( expected_data_size );
m_current_state = state_t::header_parsed;
}
}
void
process_extended_length()
{
parse_ext_payload_len(
m_current_msg.m_payload_len,
m_expected_data.m_loaded_data );
if( m_current_msg.m_mask_flag )
{
size_t expected_data_size = websocket_masking_key_size;
m_expected_data.reset( expected_data_size );
m_current_state = state_t::waiting_for_mask_key;
}
else
{
m_current_state = state_t::header_parsed;
}
}
void
process_masking_key()
{
parse_masking_key(
m_current_msg.m_mask_flag,
m_expected_data.m_loaded_data );
m_current_state = state_t::header_parsed;
}
void
parse_first_2_bytes(
const raw_data_t & data )
{
m_current_msg.m_final_flag = (data[0] & bit_flag_7) != 0;
m_current_msg.m_rsv1_flag = (data[0] & bit_flag_6) != 0;
m_current_msg.m_rsv2_flag = (data[0] & bit_flag_5) != 0;
m_current_msg.m_rsv3_flag = (data[0] & bit_flag_4) != 0;
m_current_msg.m_opcode = static_cast< opcode_t >( data[0] & opcode_mask );
m_current_msg.m_mask_flag = (data[1] & bit_flag_7) != 0;
m_current_msg.m_payload_len = data[1] & payload_len_mask;
}
void
parse_ext_payload_len(
std::uint8_t payload_len,
const raw_data_t & data )
{
if( payload_len == websocket_short_ext_len_code )
{
read_number_from_big_endian_bytes(
m_current_msg.m_ext_payload_len, data );
}
else if( payload_len == websocket_long_ext_len_code )
{
read_number_from_big_endian_bytes(
m_current_msg.m_ext_payload_len, data );
}
}
void
parse_masking_key(
bool mask_flag,
const raw_data_t & data )
{
if( mask_flag )
{
read_number_from_big_endian_bytes(
m_current_msg.m_masking_key, data );
}
}
};
inline void
mask_unmask_payload( std::uint32_t masking_key, raw_data_t & payload )
{
using namespace ::restinio::utils::impl::bitops;
const std::size_t MASK_SIZE = 4;
const uint8_t mask[ MASK_SIZE ] = {
n_bits_from< std::uint8_t, 24 >(masking_key),
n_bits_from< std::uint8_t, 16 >(masking_key),
n_bits_from< std::uint8_t, 8 >(masking_key),
n_bits_from< std::uint8_t, 0 >(masking_key),
};
const auto payload_size = payload.size();
for( std::size_t i = 0; i < payload_size; )
{
for( std::size_t j = 0; j < MASK_SIZE && i < payload_size; ++j, ++i )
{
payload[ i ] ^= mask[ j ];
}
}
}
inline raw_data_t
write_message_details( const message_details_t & message )
{
raw_data_t result;
byte_t byte = 0x00;
if( message.m_final_flag ) byte |= bit_flag_7;
if( message.m_rsv1_flag ) byte |= bit_flag_6;
if( message.m_rsv2_flag ) byte |= bit_flag_5;
if( message.m_rsv3_flag ) byte |= bit_flag_4;
byte |= static_cast< std::uint8_t> (message.m_opcode) & opcode_mask;
result.push_back( static_cast<raw_data_t::value_type>(byte) );
byte = 0x00;
if( message.m_mask_flag )
byte |= bit_flag_7;
auto length = message.m_payload_len;
if( length < websocket_short_ext_len_code )
{
byte |= length;
result.push_back( static_cast<raw_data_t::value_type>(byte) );
}
else if ( length == websocket_short_ext_len_code )
{
byte |= websocket_short_ext_len_code;
result.push_back( static_cast<raw_data_t::value_type>(byte) );
auto ext_len = message.m_ext_payload_len;
write_number_to_big_endian_bytes< websocket_short_ext_payload_length>(
ext_len, result );
}
else if ( length == websocket_long_ext_len_code )
{
byte |= websocket_long_ext_len_code;
result.push_back( static_cast<raw_data_t::value_type>(byte) );
auto ext_len = message.m_ext_payload_len;
write_number_to_big_endian_bytes< websocket_long_ext_payload_length >(
ext_len, result );
}
if( message.m_mask_flag )
{
using namespace ::restinio::utils::impl::bitops;
using ch_type = raw_data_t::value_type;
const auto key = message.m_masking_key;
result.push_back( n_bits_from< ch_type, 24 >(key) );
result.push_back( n_bits_from< ch_type, 16 >(key) );
result.push_back( n_bits_from< ch_type, 8 >(key) );
result.push_back( n_bits_from< ch_type, 0 >(key) );
}
return result;
}
inline raw_data_t
write_message_details(
final_frame_flag_t final_flag,
opcode_t opcode,
size_t payload_len )
{
return write_message_details(
message_details_t{ final_flag, opcode, payload_len } );
}
inline raw_data_t
write_message_details(
final_frame_flag_t final_flag,
opcode_t opcode,
size_t payload_len,
std::uint32_t masking_key )
{
return write_message_details(
message_details_t{ final_flag, opcode, payload_len, masking_key } );
}
} /* namespace impl */
} /* namespace basic */
} /* namespace websocket */
} /* namespace restinio */
bitops.hpp
restinio::exception_t
Exception class for all exceptions thrown by RESTinio.
Definition: exception.hpp:26
restinio::websocket::basic::impl::message_details_t
Websocket message class with more detailed protocol information.
Definition: ws_parser.hpp:63
restinio::websocket::basic::impl::message_details_t::m_rsv2_flag
bool m_rsv2_flag
Definition: ws_parser.hpp:112
restinio::websocket::basic::impl::message_details_t::payload_len
std::uint64_t payload_len() const
Get payload len.
Definition: ws_parser.hpp:92
restinio::websocket::basic::impl::message_details_t::m_rsv1_flag
bool m_rsv1_flag
Reserved flags.
Definition: ws_parser.hpp:111
restinio::websocket::basic::impl::message_details_t::message_details_t
message_details_t()=default
restinio::websocket::basic::impl::message_details_t::m_rsv3_flag
bool m_rsv3_flag
Definition: ws_parser.hpp:113
restinio::websocket::basic::impl::message_details_t::m_masking_key
std::uint32_t m_masking_key
Masking key.
Definition: ws_parser.hpp:132
restinio::websocket::basic::impl::message_details_t::m_payload_len
std::uint8_t m_payload_len
Payload len.
Definition: ws_parser.hpp:126
restinio::websocket::basic::impl::message_details_t::message_details_t
message_details_t(final_frame_flag_t final_flag, opcode_t opcode, size_t payload_len, std::uint32_t masking_key)
Definition: ws_parser.hpp:77
restinio::websocket::basic::impl::message_details_t::m_ext_payload_len
std::uint64_t m_ext_payload_len
Ext payload len.
Definition: ws_parser.hpp:129
restinio::websocket::basic::impl::message_details_t::m_final_flag
bool m_final_flag
Final flag.
Definition: ws_parser.hpp:107
restinio::websocket::basic::impl::message_details_t::message_details_t
message_details_t(final_frame_flag_t final_flag, opcode_t opcode, size_t payload_len) noexcept
Definition: ws_parser.hpp:67
restinio::websocket::basic::impl::message_details_t::init_payload_len
void init_payload_len(size_t payload_len)
Initialize payload len.
Definition: ws_parser.hpp:142
restinio::websocket::basic::impl::message_details_t::m_mask_flag
bool m_mask_flag
Mask flag.
Definition: ws_parser.hpp:120
restinio::websocket::basic::impl::message_details_t::set_masking_key
void set_masking_key(std::uint32_t value)
Set masking key.
Definition: ws_parser.hpp:100
restinio::websocket::basic::impl::message_details_t::m_opcode
opcode_t m_opcode
Opcode.
Definition: ws_parser.hpp:117
restinio::websocket::basic::impl::ws_parser_t
Websocket parser.
Definition: ws_parser.hpp:260
restinio::websocket::basic::impl::ws_parser_t::process_extended_length
void process_extended_length()
Process extended length.
Definition: ws_parser.hpp:405
restinio::websocket::basic::impl::ws_parser_t::m_current_msg
message_details_t m_current_msg
Current websocket message details.
Definition: ws_parser.hpp:317
restinio::websocket::basic::impl::ws_parser_t::reset
void reset()
Reset internal state.
Definition: ws_parser.hpp:294
restinio::websocket::basic::impl::ws_parser_t::current_message
const message_details_t & current_message() const
Get current mesasge details.
Definition: ws_parser.hpp:303
restinio::websocket::basic::impl::ws_parser_t::process_byte
void process_byte(byte_t byte)
Process one byte of incoming buffer.
Definition: ws_parser.hpp:333
restinio::websocket::basic::impl::ws_parser_t::parse_first_2_bytes
void parse_first_2_bytes(const raw_data_t &data)
Parse first two bytes of message from buffer.
Definition: ws_parser.hpp:440
restinio::websocket::basic::impl::ws_parser_t::parse_ext_payload_len
void parse_ext_payload_len(std::uint8_t payload_len, const raw_data_t &data)
Parse extended length from buffer.
Definition: ws_parser.hpp:456
restinio::websocket::basic::impl::ws_parser_t::parse_masking_key
void parse_masking_key(bool mask_flag, const raw_data_t &data)
Parse masking key from buffer.
Definition: ws_parser.hpp:474
restinio::websocket::basic::impl::ws_parser_t::state_t
state_t
Internal state.
Definition: ws_parser.hpp:321
restinio::websocket::basic::impl::ws_parser_t::state_t::waiting_for_ext_len
@ waiting_for_ext_len
restinio::websocket::basic::impl::ws_parser_t::state_t::waiting_for_first_2_bytes
@ waiting_for_first_2_bytes
restinio::websocket::basic::impl::ws_parser_t::state_t::header_parsed
@ header_parsed
restinio::websocket::basic::impl::ws_parser_t::state_t::waiting_for_mask_key
@ waiting_for_mask_key
restinio::websocket::basic::impl::ws_parser_t::parser_execute
size_t parser_execute(const char *data, size_t size)
Parse piece of data from buffer.
Definition: ws_parser.hpp:265
restinio::websocket::basic::impl::ws_parser_t::process_masking_key
void process_masking_key()
Process extended length.
Definition: ws_parser.hpp:429
restinio::websocket::basic::impl::ws_parser_t::header_parsed
bool header_parsed() const
Check header of current websocket message is parsed.
Definition: ws_parser.hpp:284
restinio::websocket::basic::impl::ws_parser_t::m_current_state
state_t m_current_state
Current state.
Definition: ws_parser.hpp:329
restinio::websocket::basic::impl::ws_parser_t::m_expected_data
expected_data_t m_expected_data
Buffer for parts of websocket message with known size.
Definition: ws_parser.hpp:314
restinio::websocket::basic::impl::ws_parser_t::process_first_2_bytes
void process_first_2_bytes()
Process first two bytes of message.
Definition: ws_parser.hpp:367
exception.hpp
message.hpp
restinio::utils::impl::bitops::details::mask
constexpr T mask(unsigned bits_to_extract)
Definition: bitops.hpp:24
restinio::utils::impl::bitops
Definition: bitops.hpp:19
restinio::utils::sha1::details::byte
unsigned int byte(digest_t::value_type v)
Definition: sha1.hpp:365
restinio::websocket::basic::impl::read_number_from_big_endian_bytes
void read_number_from_big_endian_bytes(T &number, const raw_data_t &data)
Read number from buffer with network bytes order.
Definition: ws_parser.hpp:221
restinio::websocket::basic::impl::mask_unmask_payload
void mask_unmask_payload(std::uint32_t masking_key, raw_data_t &payload)
Do msak/unmask operation with buffer.
Definition: ws_parser.hpp:488
restinio::websocket::basic::impl::bit_flag_7
constexpr byte_t bit_flag_7
Definition: ws_parser.hpp:49
restinio::websocket::basic::impl::websocket_max_payload_size_without_ext
constexpr size_t websocket_max_payload_size_without_ext
Definition: ws_parser.hpp:42
restinio::websocket::basic::impl::websocket_long_ext_payload_length
constexpr size_t websocket_long_ext_payload_length
Definition: ws_parser.hpp:44
restinio::websocket::basic::impl::write_message_details
raw_data_t write_message_details(const message_details_t &message)
Serialize websocket message details into bytes buffer.
Definition: ws_parser.hpp:515
restinio::websocket::basic::impl::websocket_short_ext_len_code
constexpr size_t websocket_short_ext_len_code
Definition: ws_parser.hpp:45
restinio::websocket::basic::impl::bit_flag_5
constexpr byte_t bit_flag_5
Definition: ws_parser.hpp:51
restinio::websocket::basic::impl::payload_len_mask
constexpr byte_t payload_len_mask
Definition: ws_parser.hpp:54
restinio::websocket::basic::impl::bit_flag_4
constexpr byte_t bit_flag_4
Definition: ws_parser.hpp:52
restinio::websocket::basic::impl::websocket_short_ext_payload_length
constexpr size_t websocket_short_ext_payload_length
Definition: ws_parser.hpp:43
restinio::websocket::basic::impl::bit_flag_6
constexpr byte_t bit_flag_6
Definition: ws_parser.hpp:50
restinio::websocket::basic::impl::opcode_mask
constexpr byte_t opcode_mask
Definition: ws_parser.hpp:53
restinio::websocket::basic::impl::websocket_long_ext_len_code
constexpr size_t websocket_long_ext_len_code
Definition: ws_parser.hpp:46
restinio::websocket::basic::impl::websocket_masking_key_size
constexpr size_t websocket_masking_key_size
Definition: ws_parser.hpp:47
restinio::websocket::basic::impl::websocket_first_two_bytes_size
constexpr size_t websocket_first_two_bytes_size
Websocket parser constants.
Definition: ws_parser.hpp:41
restinio::websocket::basic::impl::write_number_to_big_endian_bytes
void write_number_to_big_endian_bytes(std::uint64_t &number, raw_data_t &data)
Save number to buffer with network bytes order.
Definition: ws_parser.hpp:238
restinio::websocket::basic::opcode_t
opcode_t
Definition: message.hpp:37
restinio::websocket::basic::raw_data_t
std::string raw_data_t
Bytes buffer.
Definition: ws_parser.hpp:34
restinio::websocket::basic::byte_t
unsigned char byte_t
Alias for byte.
Definition: ws_parser.hpp:31
restinio::websocket::basic::final_frame
constexpr final_frame_flag_t final_frame
Definition: message.hpp:135
restinio::websocket::basic::final_frame_flag_t
final_frame_flag_t
WS frame (message) "final"/"not final" flag.
Definition: message.hpp:133
restinio
Definition: asio_include.hpp:21
restinio::websocket::basic::impl::expected_data_t
Data with expected size.
Definition: ws_parser.hpp:166
restinio::websocket::basic::impl::expected_data_t::add_byte_and_check_size
bool add_byte_and_check_size(byte_t byte)
Try to add one more byte to loaded data and check loaded data size.
Definition: ws_parser.hpp:194
restinio::websocket::basic::impl::expected_data_t::reset
void reset(size_t expected_size)
Reset internal state on next expected data size.
Definition: ws_parser.hpp:206
restinio::websocket::basic::impl::expected_data_t::all_bytes_loaded
bool all_bytes_loaded() const
Check all bytes are loaded.
Definition: ws_parser.hpp:183
restinio::websocket::basic::impl::expected_data_t::expected_data_t
expected_data_t()=default
restinio::websocket::basic::impl::expected_data_t::expected_data_t
expected_data_t(size_t expected_size)
Definition: ws_parser.hpp:169
restinio::websocket::basic::impl::expected_data_t::m_expected_size
size_t m_expected_size
Expected data size in bytes.
Definition: ws_parser.hpp:176
restinio::websocket::basic::impl::expected_data_t::m_loaded_data
raw_data_t m_loaded_data
Buffer for accumulating data.
Definition: ws_parser.hpp:179