57 static constexpr const char* table[] =
60 "frame_header_is_valid",
61 "payload_part_is_valid",
64 "empty_mask_from_client_side",
65 "non_final_control_frame",
67 "payload_len_is_too_big",
68 "continuation_frame_without_data_frame",
69 "new_data_frame_without_finishing_previous",
74 return table[
static_cast<unsigned int>(state)];
89 return opcode == opcode_t::connection_close_frame ||
90 opcode == opcode_t::ping_frame ||
91 opcode == opcode_t::pong_frame;
106 return opcode == opcode_t::text_frame ||
107 opcode == opcode_t::binary_frame;
125 {::restinio::utils::impl::bitops::n_bits_from< std::uint8_t, 24 >(
127 ::restinio::utils::impl::bitops::n_bits_from< std::uint8_t, 16 >(
129 ::restinio::utils::impl::bitops::n_bits_from< std::uint8_t, 8 >(
131 ::restinio::utils::impl::bitops::n_bits_from< std::uint8_t, 0 >(
143 return masked_byte ^ m_mask[ (m_processed_bytes_count++) % 4 ];
150 m_processed_bytes_count = 0;
153 {::restinio::utils::impl::bitops::n_bits_from< std::uint8_t, 24 >(
155 ::restinio::utils::impl::bitops::n_bits_from< std::uint8_t, 16 >(
157 ::restinio::utils::impl::bitops::n_bits_from< std::uint8_t, 8 >(
159 ::restinio::utils::impl::bitops::n_bits_from< std::uint8_t, 0 >(
174 size_t m_processed_bytes_count{ 0 };
201 : m_unmask_flag{ do_unmask }
213 if( m_working_state != working_state_t::empty_state )
214 throw exception_t(
"another frame is processing now" );
216 if( validate_frame_header( frame ) &&
217 check_previous_frame_type( frame.
m_opcode ) )
221 case opcode_t::text_frame:
223 m_previous_data_frame = previous_data_frame_t::text;
226 case opcode_t::binary_frame:
228 m_previous_data_frame = previous_data_frame_t::binary;
231 case opcode_t::connection_close_frame:
232 m_expected_close_code.reset(2);
239 m_current_frame = frame;
240 m_working_state = working_state_t::processing_frame;
248 return m_validation_state;
255 if( m_working_state == working_state_t::empty_state )
258 if( is_state_still_valid() )
259 set_validation_state(
260 validation_state_t::payload_part_is_valid );
262 return m_validation_state;
264 for(
size_t i = 0; i < size; ++i )
266 process_payload_byte(
267 static_cast<std::uint8_t
>(data[i]) );
269 if( m_validation_state != validation_state_t::payload_part_is_valid )
273 return m_validation_state;
280 if( m_working_state == working_state_t::empty_state )
283 if( is_state_still_valid() )
284 set_validation_state(
285 validation_state_t::payload_part_is_valid );
287 return m_validation_state;
289 for(
size_t i = 0; i < size; ++i )
291 data[i] =
static_cast<char>(process_payload_byte(
292 static_cast<std::uint8_t
>(data[i]) ));
294 if( m_validation_state != validation_state_t::payload_part_is_valid )
298 return m_validation_state;
305 if( is_state_still_valid() )
306 set_validation_state( validation_state_t::frame_is_valid );
308 if( m_current_frame.m_final_flag )
310 if( !m_utf8_checker.finalized() )
311 set_validation_state(
312 validation_state_t::incorrect_utf8_data );
314 m_utf8_checker.reset();
317 m_working_state = working_state_t::empty_state;
322 if( m_previous_data_frame != previous_data_frame_t::none &&
324 m_current_frame.m_final_flag )
326 m_previous_data_frame =
327 previous_data_frame_t::none;
331 auto this_frame_validation_state = m_validation_state;
334 m_validation_state = validation_state_t::initial_state;
336 return this_frame_validation_state;
343 m_validation_state = validation_state_t::initial_state;
344 m_working_state = working_state_t::empty_state;
345 m_previous_data_frame =
346 previous_data_frame_t::none;
348 m_utf8_checker.reset();
363 set_validation_state(
364 validation_state_t::frame_header_is_valid );
368 set_validation_state(
369 validation_state_t::invalid_opcode );
373 set_validation_state(
374 validation_state_t::non_final_control_frame );
378 set_validation_state(
379 validation_state_t::empty_mask_from_client_side );
385 set_validation_state(
386 validation_state_t::non_zero_rsv_flags );
391 set_validation_state(
392 validation_state_t::payload_len_is_too_big );
395 return validation_state_t::frame_header_is_valid == m_validation_state;
408 byte = m_unmask_flag?
409 m_unmasker.unmask_byte(
byte ):
byte;
411 if( m_current_frame.m_opcode == opcode_t::text_frame ||
412 (m_current_frame.m_opcode == opcode_t::continuation_frame &&
413 m_previous_data_frame == previous_data_frame_t::text) )
415 if( !m_utf8_checker.process_byte(
byte ) )
417 set_validation_state(
418 validation_state_t::incorrect_utf8_data );
421 else if( m_current_frame.m_opcode == opcode_t::connection_close_frame )
423 if( !m_expected_close_code.all_bytes_loaded() )
425 if( m_expected_close_code.add_byte_and_check_size(
byte) )
427 uint16_t status_code{0};
430 status_code,m_expected_close_code.m_loaded_data );
432 validate_close_code( status_code );
437 if( !m_utf8_checker.process_byte(
byte ) )
438 set_validation_state(
439 validation_state_t::incorrect_utf8_data );
462 if( m_previous_data_frame == previous_data_frame_t::none &&
463 opcode == opcode_t::continuation_frame )
465 set_validation_state(
466 validation_state_t::continuation_frame_without_data_frame );
470 else if( m_previous_data_frame !=
471 previous_data_frame_t::none &&
474 set_validation_state(
475 validation_state_t::new_data_frame_without_finishing_previous );
487 if( close_code < 1000 ||
488 (close_code > 1011 && close_code < 3000) ||
491 set_validation_state(
492 validation_state_t::invalid_close_code );
497 if( close_code == 1004 ||
498 close_code == 1005 ||
501 set_validation_state(
502 validation_state_t::invalid_close_code );
512 return m_validation_state == validation_state_t::initial_state ||
513 m_validation_state == validation_state_t::frame_header_is_valid ||
514 m_validation_state == validation_state_t::payload_part_is_valid ||
515 m_validation_state == validation_state_t::frame_is_valid;
525 m_validation_state = state;
537 validation_state_t::initial_state };
565 previous_data_frame_t::none };
577 bool m_unmask_flag{
false };
Exception class for all exceptions thrown by RESTinio.
Helper class for checking UTF-8 byte sequence during parsing URI or incoming byte stream.
Websocket message class with more detailed protocol information.
std::uint64_t payload_len() const
Get payload len.
bool m_rsv1_flag
Reserved flags.
std::uint32_t m_masking_key
Masking key.
bool m_final_flag
Final flag.
bool m_mask_flag
Mask flag.
Class for websocket protocol validations.
validation_state_t process_and_unmask_next_payload_part(char *data, size_t size)
Validate next part of current frame and reset source part to unmasked data.
bool validate_frame_header(const message_details_t &frame)
Validate frame header.
ws_protocol_validator_t()=default
validation_state_t process_new_frame(const message_details_t &frame)
Start work with new frame.
bool is_state_still_valid() const
Check validation state is still valid.
working_state_t
Validator's orking states.
unmasker_t m_unmasker
Unmask payload coming from client side.
std::uint8_t process_payload_byte(std::uint8_t byte)
Process payload byte.
previous_data_frame_t
Previous unfinished data frame type.
validation_state_t process_next_payload_part(const char *data, size_t size)
Validate next part of current frame.
expected_data_t m_expected_close_code
Buffer for accumulating 2 bytes of close code.
void validate_close_code(uint16_t close_code)
Validate close code.
bool check_previous_frame_type(opcode_t opcode)
Check previous frame type.
ws_protocol_validator_t(bool do_unmask)
message_details_t m_current_frame
Current frame details.
void set_validation_state(validation_state_t state)
Try to set validation state.
validation_state_t finish_frame()
Make final checks of payload if it is necessary and reset state.
restinio::utils::utf8_checker_t m_utf8_checker
UTF-8 checker for text frames and close frames.
void reset()
Reset to initial state.
unsigned int byte(digest_t::value_type v)
bool is_data_frame(opcode_t opcode)
Check frame is data frame.
void read_number_from_big_endian_bytes(T &number, const raw_data_t &data)
Read number from buffer with network bytes order.
constexpr size_t websocket_max_payload_size_without_ext
bool is_control_frame(opcode_t opcode)
Check frame is control frame.
const char * validation_state_str(validation_state_t state)
Helper function for logging validation states.
validation_state_t
States of validated frame.
@ non_final_control_frame
@ continuation_frame_without_data_frame
@ empty_mask_from_client_side
@ new_data_frame_without_finishing_previous
bool is_valid_opcode(opcode_t opcode)
void reset(uint32_t masking_key)
Reset to initial state.
mask_array_t m_mask
Bytes array with masking key.
std::array< uint8_t, websocket_masking_key_size > mask_array_t
unmasker_t(uint32_t masking_key)
uint8_t unmask_byte(uint8_t masked_byte)
Do unmask operation.