7 #include <restinio/exception.hpp> 8 #include <restinio/websocket/impl/utf8.hpp> 9 #include <restinio/websocket/impl/ws_parser.hpp> 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;
143 return masked_byte ^ m_mask[ (m_processed_bytes_count++) % 4 ];
152 m_mask = mask_array_t{
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 >(
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 ) )
219 switch( frame.m_opcode )
221 case opcode_t::text_frame:
222 if( !frame.m_final_flag )
223 m_previous_data_frame = previous_data_frame_t::text;
226 case opcode_t::binary_frame:
227 if( !frame.m_final_flag )
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;
244 m_unmasker.reset( frame.m_masking_key );
255 if( m_working_state == working_state_t::empty_state )
256 throw exception_t(
"current state is empty" );
264 for( size_t i = 0; i < size; ++i )
266 process_payload_byte(
267 static_cast<std::uint8_t>(data[i]) );
280 if( m_working_state == working_state_t::empty_state )
281 throw exception_t(
"current state is empty" );
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]) ));
308 if( m_current_frame.m_final_flag )
310 if( !m_utf8_checker.final() )
314 m_utf8_checker.reset();
322 if( m_previous_data_frame != previous_data_frame_t::none &&
323 !is_control_frame(m_current_frame.m_opcode) &&
324 m_current_frame.m_final_flag )
336 return this_frame_validation_state;
348 m_utf8_checker.reset();
366 if( !is_valid_opcode( frame.m_opcode ) )
371 else if( is_control_frame(frame.m_opcode) && frame.m_final_flag ==
false )
376 else if( frame.m_mask_flag ==
false )
381 else if( frame.m_rsv1_flag != 0 ||
382 frame.m_rsv2_flag != 0 ||
383 frame.m_rsv3_flag != 0)
388 else if( is_control_frame(frame.m_opcode) && frame.payload_len() >
389 websocket_max_payload_size_without_ext )
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 ) )
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};
429 read_number_from_big_endian_bytes(
430 status_code,m_expected_close_code.m_loaded_data );
432 validate_close_code( status_code );
437 if( !m_utf8_checker.process_byte( byte ) )
462 if( m_previous_data_frame == previous_data_frame_t::none &&
463 opcode == opcode_t::continuation_frame )
470 else if( m_previous_data_frame !=
471 previous_data_frame_t::none &&
472 is_data_frame( opcode ) )
487 if( close_code < 1000 ||
488 (close_code > 1011 && close_code < 3000) ||
497 if( close_code == 1004 ||
498 close_code == 1005 ||
void set_validation_state(validation_state_t state)
Try to set validation state.
unmasker_t m_unmasker
Unmask payload coming from client side.
void validate_close_code(uint16_t close_code)
Validate close code.
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.
uint8_t unmask_byte(uint8_t masked_byte)
Do unmask operation.
bool is_data_frame(opcode_t opcode)
Check frame is data frame.
const char * validation_state_str(validation_state_t state)
Helper function for logging validation states.
bool validate_frame_header(const message_details_t &frame)
Validate frame header.
size_t m_processed_bytes_count
Processed bytes counter.
bool is_state_still_valid() const
Check validation state is still valid.
ws_protocol_validator_t(bool do_unmask)
utf8_checker_t m_utf8_checker
UTF-8 checker for text frames and close frames.
unmasker_t(uint32_t masking_key)
validation_state_t process_new_frame(const message_details_t &frame)
Start work with new frame.
std::uint8_t process_payload_byte(std::uint8_t byte)
Process payload byte.
working_state_t m_working_state
Working state.
bool m_unmask_flag
This flag set if it's need to unmask payload parts.
working_state_t
Validator's orking states.
validation_state_t
States of validated frame.
Class for websocket protocol validations.
void reset(uint32_t masking_key)
Reset to initial state.
expected_data_t m_expected_close_code
Buffer for accumulating 2 bytes of close code.
void reset()
Reset to initial state.
message_details_t m_current_frame
Current frame details.
ws_protocol_validator_t()=default
validation_state_t process_next_payload_part(const char *data, size_t size)
Validate next part of current frame.
bool is_control_frame(opcode_t opcode)
Check frame is control frame.
previous_data_frame_t
Previous unfinished data frame type.
validation_state_t finish_frame()
Make final checks of payload if it is necessary and reset state.
previous_data_frame_t m_previous_data_frame
Previous unfinished data frame.
validation_state_t m_validation_state
Current validation state.
bool check_previous_frame_type(opcode_t opcode)
Check previous frame type.
std::enable_if< std::is_same< Parameter_Container, query_string_params_t >::value||std::is_same< Parameter_Container, router::route_params_t >::value, optional_t< Value_Type > >::type opt_value(const Parameter_Container ¶ms, string_view_t key)
Gets the value of a parameter specified by key wrapped in optional_t<Value_Type> if parameter exists ...
mask_array_t m_mask
Bytes array with masking key.