RESTinio
Loading...
Searching...
No Matches
ws_parser.hpp
Go to the documentation of this file.
1/*
2 restinio
3*/
4
5/*!
6 Websocket.
7*/
8
9#pragma once
10
11#include <restinio/exception.hpp>
12#include <restinio/websocket/message.hpp>
13
14#include <restinio/utils/impl/bitops.hpp>
15
16#include <cstdint>
17#include <vector>
18#include <list>
19#include <stdexcept>
20
21namespace restinio
22{
23
24namespace websocket
25{
26
27namespace basic
28{
29
30//! Alias for byte.
31using byte_t = unsigned char;
32
33//! Bytes buffer.
34using raw_data_t = std::string;
35
36namespace impl
37{
38
39//! Websocket parser constants.
40//! \{
48
49constexpr byte_t bit_flag_7 = 0x80;
50constexpr byte_t bit_flag_6 = 0x40;
51constexpr byte_t bit_flag_5 = 0x20;
52constexpr byte_t bit_flag_4 = 0x10;
53constexpr byte_t opcode_mask = 0x0F;
54constexpr byte_t payload_len_mask = 0x7F;
55//! \}
56
57//
58// message_details_t
59//
60
61//! Websocket message class with more detailed protocol information.
63{
64 public:
65 message_details_t() = default;
66
68 final_frame_flag_t final_flag,
69 opcode_t opcode,
70 size_t payload_len ) noexcept
71 : m_final_flag{ final_frame == final_flag }
72 , m_opcode{ opcode }
73 {
74 init_payload_len( payload_len );
75 }
76
78 final_frame_flag_t final_flag,
79 opcode_t opcode,
80 size_t payload_len,
81 std::uint32_t masking_key )
82 : m_final_flag{ final_frame == final_flag }
83 , m_opcode{ opcode }
84 , m_mask_flag( true )
85 , m_masking_key( masking_key )
86 {
87 init_payload_len( payload_len );
88 }
89
90 //! Get payload len.
91 std::uint64_t
93 {
94 // 126 and 127 are codes of ext payload. 125 and lower are real payload len.
95 return m_payload_len > websocket_max_payload_size_without_ext? m_ext_payload_len: m_payload_len;
96 }
97
98 //! Set masking key.
99 void
100 set_masking_key( std::uint32_t value )
101 {
102 m_masking_key = value;
103 m_mask_flag = true;
104 }
105
106 //! Final flag.
107 bool m_final_flag = true;
108
109 //! Reserved flags.
110 //! \{
111 bool m_rsv1_flag = false;
112 bool m_rsv2_flag = false;
113 bool m_rsv3_flag = false;
114 //! \}
115
116 //! Opcode.
117 opcode_t m_opcode = opcode_t::continuation_frame;
118
119 //! Mask flag.
120 bool m_mask_flag = false;
121
122 //! Payload len.
123 /*!
124 It contains payload len or ext payload len code.
125 */
127
128 //! Ext payload len.
130
131 //! Masking key.
133
134 private:
135
136 //! Initialize payload len.
137 /*!
138 Set only payload length if value is lower than 126 or payload length
139 and ext payload length otherwise.
140 */
141 void
142 init_payload_len( size_t payload_len )
143 {
144 if( payload_len > websocket_max_payload_size_without_ext )
145 {
146 // if payload greater than 2bytes-number.
147 m_payload_len = payload_len > 0xFFFF ?
148 websocket_long_ext_len_code:
149 websocket_short_ext_len_code;
150
151 m_ext_payload_len = payload_len;
152 }
153 else
154 {
155 m_payload_len = static_cast< std::uint8_t >( payload_len );
156 }
157 }
158};
159
160//
161// expected_data_t
162//
163
164//! Data with expected size.
166{
167 expected_data_t() = default;
168
169 expected_data_t( size_t expected_size )
171 {
172 m_loaded_data.reserve( m_expected_size );
173 }
174
175 //! Expected data size in bytes.
177
178 //! Buffer for accumulating data.
180
181 //! Check all bytes are loaded.
182 bool
184 {
185 return m_loaded_data.size() == m_expected_size;
186 }
187
188 //! Try to add one more byte to loaded data and check loaded data size.
189 /*!
190 \return true if loaded data size equals expected size.
191 \return false otherwise.
192 */
193 bool
195 {
196 if( m_loaded_data.size() == m_expected_size )
197 throw exception_t("Cannot add one more bytes to expected data.");
198
199 m_loaded_data.push_back(static_cast<raw_data_t::value_type>(byte));
200
201 return all_bytes_loaded();
202 }
203
204 //! Reset internal state on next expected data size.
205 void
206 reset( size_t expected_size )
207 {
208 m_expected_size = expected_size;
209 m_loaded_data.clear();
210 m_loaded_data.reserve( expected_size );
211 }
212};
213
214//
215// read_number_from_big_endian_bytes
216//
217
218//! Read number from buffer with network bytes order.
219template <typename T>
220inline void
222{
223 number = T{};
224 for( const auto byte: data )
225 {
226 number <<= 8;
227 number |= static_cast<std::uint8_t>( byte );
228 }
229}
230
231//
232// write_number_to_big_endian_bytes
233//
234
235//! Save number to buffer with network bytes order.
236template <int Bytes>
237inline void
238write_number_to_big_endian_bytes( std::uint64_t& number, raw_data_t & data )
239{
240 for( auto i = 0 ; i < Bytes ; ++i )
241 {
242 auto shift_value = (Bytes - i - 1) * 8;
243 data.push_back( static_cast<raw_data_t::value_type>(
244 (number >> shift_value) & 0xFF) );
245 }
246}
247
248//
249// ws_parser_t
250//
251
252//! Websocket parser.
253/*!
254 This class can parse message from binary buffer.
255
256 It is not necessary to have all buffer before parsing. Parser can process
257 pieces of data and save intermediate state.
258*/
260{
261 public:
262
263 //! Parse piece of data from buffer.
264 size_t
265 parser_execute( const char * data, size_t size )
266 {
267 size_t parsed_bytes = 0;
268
269 while( parsed_bytes < size &&
271 {
272 byte_t byte = static_cast< byte_t >( data[parsed_bytes] );
273
274 process_byte( byte );
275
276 parsed_bytes++;
277 }
278
279 return parsed_bytes;
280 }
281
282 //! Check header of current websocket message is parsed.
283 bool
285 {
287 }
288
289 //! Reset internal state.
290 /*!
291 Need to call this function before processing next message.
292 */
293 void
295 {
298 m_expected_data.reset( websocket_first_two_bytes_size );
299 }
300
301 //! Get current mesasge details.
302 const message_details_t &
304 {
305 return m_current_msg;
306 }
307
308 private:
309
310 //! Buffer for parts of websocket message with known size.
311 /*!
312 Default value is first 2 bytes for flags and opcode.
313 */
315
316 //! Current websocket message details.
318
319 //! Internal state.
327
328 //! Current state.
330
331 //! Process one byte of incoming buffer.
332 void
334 {
335 if( m_expected_data.add_byte_and_check_size(byte) )
336 {
337 switch( m_current_state )
338 {
339
341
343 break;
344
346
348 break;
349
351
353 break;
354
356
357 break;
358 }
359 }
360 }
361
362 //! Process first two bytes of message.
363 /*!
364 Parse flags, opcode, payload length and set new state.
365 */
366 void
368 {
369 parse_first_2_bytes(
370 m_expected_data.m_loaded_data );
371
372 size_t payload_len = m_current_msg.m_payload_len;
373
374 if( payload_len > websocket_max_payload_size_without_ext )
375 {
376 size_t expected_data_size = payload_len == websocket_short_ext_len_code?
377 websocket_short_ext_payload_length:
378 websocket_long_ext_payload_length;
379
380 m_expected_data.reset( expected_data_size );
381
383 }
385 {
386 size_t expected_data_size = websocket_masking_key_size;
387 m_expected_data.reset( expected_data_size );
388
390 }
391 else
392 {
393 size_t expected_data_size = payload_len;
394 m_expected_data.reset( expected_data_size );
395
397 }
398 }
399
400 //! Process extended length.
401 /*!
402 Parse extended length and set new state.
403 */
404 void
406 {
407 parse_ext_payload_len(
408 m_current_msg.m_payload_len,
409 m_expected_data.m_loaded_data );
410
412 {
413 size_t expected_data_size = websocket_masking_key_size;
414 m_expected_data.reset( expected_data_size );
415
417 }
418 else
419 {
421 }
422 }
423
424 void
425 //! Process extended length.
426 /*!
427 Parse masking key and set new state.
428 */
430 {
431 parse_masking_key(
432 m_current_msg.m_mask_flag,
433 m_expected_data.m_loaded_data );
434
436 }
437
438 //! Parse first two bytes of message from buffer.
439 void
441 const raw_data_t & data )
442 {
443 m_current_msg.m_final_flag = (data[0] & bit_flag_7) != 0;
444 m_current_msg.m_rsv1_flag = (data[0] & bit_flag_6) != 0;
445 m_current_msg.m_rsv2_flag = (data[0] & bit_flag_5) != 0;
446 m_current_msg.m_rsv3_flag = (data[0] & bit_flag_4) != 0;
447
448 m_current_msg.m_opcode = static_cast< opcode_t >( data[0] & opcode_mask );
449
450 m_current_msg.m_mask_flag = (data[1] & bit_flag_7) != 0;
451 m_current_msg.m_payload_len = data[1] & payload_len_mask;
452 }
453
454 //! Parse extended length from buffer.
455 void
457 std::uint8_t payload_len,
458 const raw_data_t & data )
459 {
460 if( payload_len == websocket_short_ext_len_code )
461 {
462 read_number_from_big_endian_bytes(
464 }
465 else if( payload_len == websocket_long_ext_len_code )
466 {
467 read_number_from_big_endian_bytes(
469 }
470 }
471
472 //! Parse masking key from buffer.
473 void
475 bool mask_flag,
476 const raw_data_t & data )
477 {
478 if( mask_flag )
479 {
480 read_number_from_big_endian_bytes(
482 }
483 }
484};
485
486//! Do msak/unmask operation with buffer.
487inline void
488mask_unmask_payload( std::uint32_t masking_key, raw_data_t & payload )
489{
490 using namespace ::restinio::utils::impl::bitops;
491
492 const std::size_t MASK_SIZE = 4;
493 const uint8_t mask[ MASK_SIZE ] = {
494 n_bits_from< std::uint8_t, 24 >(masking_key),
495 n_bits_from< std::uint8_t, 16 >(masking_key),
496 n_bits_from< std::uint8_t, 8 >(masking_key),
497 n_bits_from< std::uint8_t, 0 >(masking_key),
498 };
499
500 const auto payload_size = payload.size();
501 for( std::size_t i = 0; i < payload_size; )
502 {
503 for( std::size_t j = 0; j < MASK_SIZE && i < payload_size; ++j, ++i )
504 {
505 payload[ i ] ^= mask[ j ];
506 }
507 }
508}
509
510//! Serialize websocket message details into bytes buffer.
511/*!
512 \return buffer with written websocket message.
513*/
514inline raw_data_t
516{
517 raw_data_t result;
518
519 byte_t byte = 0x00;
520
521 if( message.m_final_flag ) byte |= bit_flag_7;
522 if( message.m_rsv1_flag ) byte |= bit_flag_6;
523 if( message.m_rsv2_flag ) byte |= bit_flag_5;
524 if( message.m_rsv3_flag ) byte |= bit_flag_4;
525
526 byte |= static_cast< std::uint8_t> (message.m_opcode) & opcode_mask;
527
528 result.push_back( static_cast<raw_data_t::value_type>(byte) );
529
530 byte = 0x00;
531
532 if( message.m_mask_flag )
533 byte |= bit_flag_7;
534
535 auto length = message.m_payload_len;
536
537 if( length < websocket_short_ext_len_code )
538 {
539 byte |= length;
540 result.push_back( static_cast<raw_data_t::value_type>(byte) );
541 }
542 else if ( length == websocket_short_ext_len_code )
543 {
544 byte |= websocket_short_ext_len_code;
545
546 result.push_back( static_cast<raw_data_t::value_type>(byte) );
547
548 auto ext_len = message.m_ext_payload_len;
549
550 write_number_to_big_endian_bytes< websocket_short_ext_payload_length>(
551 ext_len, result );
552 }
553 else if ( length == websocket_long_ext_len_code )
554 {
555 byte |= websocket_long_ext_len_code;
556
557 result.push_back( static_cast<raw_data_t::value_type>(byte) );
558
559 auto ext_len = message.m_ext_payload_len;
560
561 write_number_to_big_endian_bytes< websocket_long_ext_payload_length >(
562 ext_len, result );
563 }
564
565 if( message.m_mask_flag )
566 {
567 using namespace ::restinio::utils::impl::bitops;
568
569 using ch_type = raw_data_t::value_type;
570
571 const auto key = message.m_masking_key;
572 result.push_back( n_bits_from< ch_type, 24 >(key) );
573 result.push_back( n_bits_from< ch_type, 16 >(key) );
574 result.push_back( n_bits_from< ch_type, 8 >(key) );
575 result.push_back( n_bits_from< ch_type, 0 >(key) );
576 }
577
578 return result;
579}
580
581//! Serialize websocket message details into bytes buffer.
582/*!
583 \return buffer with written websocket message.
584*/
585inline raw_data_t
587 final_frame_flag_t final_flag,
588 opcode_t opcode,
589 size_t payload_len )
590{
591 return write_message_details(
592 message_details_t{ final_flag, opcode, payload_len } );
593}
594
595//! Serialize websocket message details into bytes buffer.
596/*!
597 \return buffer with written websocket message.
598*/
599inline raw_data_t
601 final_frame_flag_t final_flag,
602 opcode_t opcode,
603 size_t payload_len,
604 std::uint32_t masking_key )
605{
606 return write_message_details(
607 message_details_t{ final_flag, opcode, payload_len, masking_key } );
608}
609
610} /* namespace impl */
611
612} /* namespace basic */
613
614} /* namespace websocket */
615
616} /* namespace restinio */
Exception class for all exceptions thrown by RESTinio.
Definition exception.hpp:26
exception_t(const char *err)
Definition exception.hpp:29
Websocket message class with more detailed protocol information.
Definition ws_parser.hpp:63
std::uint64_t payload_len() const
Get payload len.
Definition ws_parser.hpp:92
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
std::uint64_t m_ext_payload_len
Ext payload len.
message_details_t(final_frame_flag_t final_flag, opcode_t opcode, size_t payload_len) noexcept
Definition ws_parser.hpp:67
void init_payload_len(size_t payload_len)
Initialize payload len.
void set_masking_key(std::uint32_t value)
Set masking key.
void process_extended_length()
Process extended length.
message_details_t m_current_msg
Current websocket message details.
const message_details_t & current_message() const
Get current mesasge details.
void process_byte(byte_t byte)
Process one byte of incoming buffer.
void parse_first_2_bytes(const raw_data_t &data)
Parse first two bytes of message from buffer.
void parse_ext_payload_len(std::uint8_t payload_len, const raw_data_t &data)
Parse extended length from buffer.
void parse_masking_key(bool mask_flag, const raw_data_t &data)
Parse masking key from buffer.
size_t parser_execute(const char *data, size_t size)
Parse piece of data from buffer.
void process_masking_key()
Process extended length.
bool header_parsed() const
Check header of current websocket message is parsed.
expected_data_t m_expected_data
Buffer for parts of websocket message with known size.
void process_first_2_bytes()
Process first two bytes of message.
void read_number_from_big_endian_bytes(T &number, const raw_data_t &data)
Read number from buffer with network bytes order.
void mask_unmask_payload(std::uint32_t masking_key, raw_data_t &payload)
Do msak/unmask operation with buffer.
constexpr size_t websocket_max_payload_size_without_ext
Definition ws_parser.hpp:42
constexpr size_t websocket_long_ext_payload_length
Definition ws_parser.hpp:44
raw_data_t write_message_details(const message_details_t &message)
Serialize websocket message details into bytes buffer.
constexpr size_t websocket_short_ext_len_code
Definition ws_parser.hpp:45
constexpr byte_t payload_len_mask
Definition ws_parser.hpp:54
constexpr size_t websocket_short_ext_payload_length
Definition ws_parser.hpp:43
raw_data_t write_message_details(final_frame_flag_t final_flag, opcode_t opcode, size_t payload_len, std::uint32_t masking_key)
Serialize websocket message details into bytes buffer.
constexpr size_t websocket_long_ext_len_code
Definition ws_parser.hpp:46
raw_data_t write_message_details(final_frame_flag_t final_flag, opcode_t opcode, size_t payload_len)
Serialize websocket message details into bytes buffer.
constexpr size_t websocket_masking_key_size
Definition ws_parser.hpp:47
constexpr size_t websocket_first_two_bytes_size
Websocket parser constants.
Definition ws_parser.hpp:41
void write_number_to_big_endian_bytes(std::uint64_t &number, raw_data_t &data)
Save number to buffer with network bytes order.
unsigned char byte_t
Alias for byte.
Definition ws_parser.hpp:31
constexpr final_frame_flag_t final_frame
Definition message.hpp:135
std::string raw_data_t
Bytes buffer.
Definition ws_parser.hpp:34
final_frame_flag_t
WS frame (message) "final"/"not final" flag.
Definition message.hpp:133
bool add_byte_and_check_size(byte_t byte)
Try to add one more byte to loaded data and check loaded data size.
void reset(size_t expected_size)
Reset internal state on next expected data size.
bool all_bytes_loaded() const
Check all bytes are loaded.
size_t m_expected_size
Expected data size in bytes.
raw_data_t m_loaded_data
Buffer for accumulating data.