2 * Copyright (c) 2014, Peter Thorson. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 * * Redistributions of source code must retain the above copyright
7 * notice, this list of conditions and the following disclaimer.
8 * * Redistributions in binary form must reproduce the above copyright
9 * notice, this list of conditions and the following disclaimer in the
10 * documentation and/or other materials provided with the distribution.
11 * * Neither the name of the WebSocket++ Project nor the
12 * names of its contributors may be used to endorse or promote products
13 * derived from this software without specific prior written permission.
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
19 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 #ifndef WEBSOCKETPP_FRAME_HPP
29 #define WEBSOCKETPP_FRAME_HPP
34 #include <websocketpp/common/system_error.hpp>
35 #include <websocketpp/common/network.hpp>
37 #include <websocketpp/utilities.hpp>
39 namespace websocketpp {
40 /// Data structures and utility functions for manipulating WebSocket frames
42 * namespace frame provides a number of data structures and utility functions
43 * for reading, writing, and manipulating binary encoded WebSocket frames.
47 /// Minimum length of a WebSocket frame header.
48 static unsigned int const BASIC_HEADER_LENGTH = 2;
49 /// Maximum length of a WebSocket header
50 static unsigned int const MAX_HEADER_LENGTH = 14;
51 /// Maximum length of the variable portion of the WebSocket header
52 static unsigned int const MAX_EXTENDED_HEADER_LENGTH = 12;
54 /// Two byte conversion union
55 union uint16_converter {
60 /// Four byte conversion union
61 union uint32_converter {
66 /// Eight byte conversion union
67 union uint64_converter {
72 /// Constants and utility functions related to WebSocket opcodes
74 * WebSocket Opcodes are 4 bits. See RFC6455 section 5.2.
113 /// Check if an opcode is reserved
115 * @param v The opcode to test.
116 * @return Whether or not the opcode is reserved.
118 inline bool reserved(value v) {
119 return (v >= rsv3 && v <= rsv7) ||
120 (v >= control_rsvb && v <= control_rsvf);
123 /// Check if an opcode is invalid
125 * Invalid opcodes are negative or require greater than 4 bits to store.
127 * @param v The opcode to test.
128 * @return Whether or not the opcode is invalid.
130 inline bool invalid(value v) {
131 return (v > 0xF || v < 0);
134 /// Check if an opcode is for a control frame
136 * @param v The opcode to test.
137 * @return Whether or not the opcode is a control opcode.
139 inline bool is_control(value v) {
144 /// Constants related to frame and payload limits
146 /// Minimum length of a WebSocket frame header.
147 static unsigned int const basic_header_length = 2;
149 /// Maximum length of a WebSocket header
150 static unsigned int const max_header_length = 14;
152 /// Maximum length of the variable portion of the WebSocket header
153 static unsigned int const max_extended_header_length = 12;
155 /// Maximum size of a basic WebSocket payload
156 static uint8_t const payload_size_basic = 125;
158 /// Maximum size of an extended WebSocket payload (basic payload = 126)
159 static uint16_t const payload_size_extended = 0xFFFF; // 2^16, 65535
161 /// Maximum size of a jumbo WebSocket payload (basic payload = 127)
162 static uint64_t const payload_size_jumbo = 0x7FFFFFFFFFFFFFFFLL;//2^63
164 /// Maximum size of close frame reason
166 * This is payload_size_basic - 2 bytes (as first two bytes are used for
169 static uint8_t const close_reason_size = 123;
173 // masks for fields in the basic header
174 static uint8_t const BHB0_OPCODE = 0x0F;
175 static uint8_t const BHB0_RSV3 = 0x10;
176 static uint8_t const BHB0_RSV2 = 0x20;
177 static uint8_t const BHB0_RSV1 = 0x40;
178 static uint8_t const BHB0_FIN = 0x80;
180 static uint8_t const BHB1_PAYLOAD = 0x7F;
181 static uint8_t const BHB1_MASK = 0x80;
183 static uint8_t const payload_size_code_16bit = 0x7E; // 126
184 static uint8_t const payload_size_code_64bit = 0x7F; // 127
186 typedef uint32_converter masking_key_type;
188 /// The constant size component of a WebSocket frame header
189 struct basic_header {
190 basic_header() : b0(0x00),b1(0x00) {}
192 basic_header(uint8_t p0, uint8_t p1) : b0(p0), b1(p1) {}
194 basic_header(opcode::value op, uint64_t size, bool fin, bool mask,
195 bool rsv1 = false, bool rsv2 = false, bool rsv3 = false) : b0(0x00),
210 b0 |= (op & BHB0_OPCODE);
218 if (size <= limits::payload_size_basic) {
219 basic_value = static_cast<uint8_t>(size);
220 } else if (size <= limits::payload_size_extended) {
221 basic_value = payload_size_code_16bit;
223 basic_value = payload_size_code_64bit;
234 /// The variable size component of a WebSocket frame header
235 struct extended_header {
237 std::fill_n(this->bytes,MAX_EXTENDED_HEADER_LENGTH,0x00);
240 extended_header(uint64_t payload_size) {
241 std::fill_n(this->bytes,MAX_EXTENDED_HEADER_LENGTH,0x00);
243 copy_payload(payload_size);
246 extended_header(uint64_t payload_size, uint32_t masking_key) {
247 std::fill_n(this->bytes,MAX_EXTENDED_HEADER_LENGTH,0x00);
250 int offset = copy_payload(payload_size);
253 uint32_converter temp32;
254 temp32.i = masking_key;
255 std::copy(temp32.c,temp32.c+4,bytes+offset);
258 uint8_t bytes[MAX_EXTENDED_HEADER_LENGTH];
260 int copy_payload(uint64_t payload_size) {
261 int payload_offset = 0;
263 if (payload_size <= limits::payload_size_basic) {
265 } else if (payload_size <= limits::payload_size_extended) {
269 uint64_converter temp64;
270 temp64.i = lib::net::_htonll(payload_size);
271 std::copy(temp64.c+payload_offset,temp64.c+8,bytes);
273 return 8-payload_offset;
277 bool get_fin(basic_header const &h);
278 void set_fin(basic_header &h, bool value);
279 bool get_rsv1(basic_header const &h);
280 void set_rsv1(basic_header &h, bool value);
281 bool get_rsv2(basic_header const &h);
282 void set_rsv2(basic_header &h, bool value);
283 bool get_rsv3(basic_header const &h);
284 void set_rsv3(basic_header &h, bool value);
285 opcode::value get_opcode(basic_header const &h);
286 bool get_masked(basic_header const &h);
287 void set_masked(basic_header &h, bool value);
288 uint8_t get_basic_size(basic_header const &);
289 size_t get_header_len(basic_header const &);
290 unsigned int get_masking_key_offset(basic_header const &);
292 std::string write_header(basic_header const &, extended_header const &);
293 masking_key_type get_masking_key(basic_header const &, extended_header const &);
294 uint16_t get_extended_size(extended_header const &);
295 uint64_t get_jumbo_size(extended_header const &);
296 uint64_t get_payload_size(basic_header const &, extended_header const &);
298 size_t prepare_masking_key(masking_key_type const & key);
299 size_t circshift_prepared_key(size_t prepared_key, size_t offset);
301 // Functions for performing xor based masking and unmasking
302 template <typename input_iter, typename output_iter>
303 void byte_mask(input_iter b, input_iter e, output_iter o, masking_key_type
304 const & key, size_t key_offset = 0);
305 template <typename iter_type>
306 void byte_mask(iter_type b, iter_type e, masking_key_type const & key,
307 size_t key_offset = 0);
308 void word_mask_exact(uint8_t * input, uint8_t * output, size_t length,
309 masking_key_type const & key);
310 void word_mask_exact(uint8_t * data, size_t length, masking_key_type const &
312 size_t word_mask_circ(uint8_t * input, uint8_t * output, size_t length,
313 size_t prepared_key);
314 size_t word_mask_circ(uint8_t * data, size_t length, size_t prepared_key);
316 /// Check whether the frame's FIN bit is set.
318 * @param [in] h The basic header to extract from.
319 * @return True if the header's fin bit is set.
321 inline bool get_fin(basic_header const & h) {
322 return ((h.b0 & BHB0_FIN) == BHB0_FIN);
325 /// Set the frame's FIN bit
327 * @param [out] h Header to set.
328 * @param [in] value Value to set it to.
330 inline void set_fin(basic_header & h, bool value) {
331 h.b0 = (value ? h.b0 | BHB0_FIN : h.b0 & ~BHB0_FIN);
334 /// check whether the frame's RSV1 bit is set
336 * @param [in] h The basic header to extract from.
337 * @return True if the header's RSV1 bit is set.
339 inline bool get_rsv1(const basic_header &h) {
340 return ((h.b0 & BHB0_RSV1) == BHB0_RSV1);
343 /// Set the frame's RSV1 bit
345 * @param [out] h Header to set.
346 * @param [in] value Value to set it to.
348 inline void set_rsv1(basic_header &h, bool value) {
349 h.b0 = (value ? h.b0 | BHB0_RSV1 : h.b0 & ~BHB0_RSV1);
352 /// check whether the frame's RSV2 bit is set
354 * @param [in] h The basic header to extract from.
355 * @return True if the header's RSV2 bit is set.
357 inline bool get_rsv2(const basic_header &h) {
358 return ((h.b0 & BHB0_RSV2) == BHB0_RSV2);
361 /// Set the frame's RSV2 bit
363 * @param [out] h Header to set.
364 * @param [in] value Value to set it to.
366 inline void set_rsv2(basic_header &h, bool value) {
367 h.b0 = (value ? h.b0 | BHB0_RSV2 : h.b0 & ~BHB0_RSV2);
370 /// check whether the frame's RSV3 bit is set
372 * @param [in] h The basic header to extract from.
373 * @return True if the header's RSV3 bit is set.
375 inline bool get_rsv3(const basic_header &h) {
376 return ((h.b0 & BHB0_RSV3) == BHB0_RSV3);
379 /// Set the frame's RSV3 bit
381 * @param [out] h Header to set.
382 * @param [in] value Value to set it to.
384 inline void set_rsv3(basic_header &h, bool value) {
385 h.b0 = (value ? h.b0 | BHB0_RSV3 : h.b0 & ~BHB0_RSV3);
388 /// Extract opcode from basic header
390 * @param [in] h The basic header to extract from.
391 * @return The opcode value of the header.
393 inline opcode::value get_opcode(const basic_header &h) {
394 return opcode::value(h.b0 & BHB0_OPCODE);
397 /// check whether the frame is masked
399 * @param [in] h The basic header to extract from.
400 * @return True if the header mask bit is set.
402 inline bool get_masked(basic_header const & h) {
403 return ((h.b1 & BHB1_MASK) == BHB1_MASK);
406 /// Set the frame's MASK bit
408 * @param [out] h Header to set.
409 * @param value Value to set it to.
411 inline void set_masked(basic_header & h, bool value) {
412 h.b1 = (value ? h.b1 | BHB1_MASK : h.b1 & ~BHB1_MASK);
415 /// Extracts the raw payload length specified in the basic header
417 * A basic WebSocket frame header contains a 7 bit value that represents the
418 * payload size. There are two reserved values that are used to indicate that
419 * the actual payload size will not fit in 7 bits and that the full payload
420 * size is included in a separate field. The values are as follows:
422 * PAYLOAD_SIZE_CODE_16BIT (0x7E) indicates that the actual payload is less
425 * PAYLOAD_SIZE_CODE_64BIT (0x7F) indicates that the actual payload is less
428 * @param [in] h Basic header to read value from.
429 * @return The exact size encoded in h.
431 inline uint8_t get_basic_size(const basic_header &h) {
432 return h.b1 & BHB1_PAYLOAD;
435 /// Calculates the full length of the header based on the first bytes.
437 * A WebSocket frame header always has at least two bytes. Encoded within the
438 * first two bytes is all the information necessary to calculate the full
439 * (variable) header length. get_header_len() calculates the full header
440 * length for the given two byte basic header.
442 * @param h Basic frame header to extract size from.
443 * @return Full length of the extended header.
445 inline size_t get_header_len(basic_header const & h) {
446 // TODO: check extensions?
448 // masking key offset represents the space used for the extended length
450 size_t size = BASIC_HEADER_LENGTH + get_masking_key_offset(h);
452 // If the header is masked there is a 4 byte masking key
460 /// Calculate the offset location of the masking key within the extended header
462 * Calculate the offset location of the masking key within the extended header
463 * using information from its corresponding basic header
465 * @param h Corresponding basic header to calculate from.
467 * @return byte offset of the first byte of the masking key
469 inline unsigned int get_masking_key_offset(const basic_header &h) {
470 if (get_basic_size(h) == payload_size_code_16bit) {
472 } else if (get_basic_size(h) == payload_size_code_64bit) {
479 /// Generate a properly sized contiguous string that encodes a full frame header
481 * Copy the basic header h and extended header e into a properly sized
482 * contiguous frame header string for the purposes of writing out to the wire.
484 * @param h The basic header to include
485 * @param e The extended header to include
487 * @return A contiguous string containing h and e
489 inline std::string prepare_header(const basic_header &h, const
494 ret.push_back(char(h.b0));
495 ret.push_back(char(h.b1));
497 reinterpret_cast<const char*>(e.bytes),
498 get_header_len(h)-BASIC_HEADER_LENGTH
504 /// Extract the masking key from a frame header
506 * Note that while read and written as an integer at times, this value is not
507 * an integer and should never be interpreted as one. Big and little endian
508 * machines will generate and store masking keys differently without issue as
509 * long as the integer values remain irrelivant.
511 * @param h The basic header to extract from
512 * @param e The extended header to extract from
514 * @return The masking key as an integer.
516 inline masking_key_type get_masking_key(const basic_header &h, const
519 masking_key_type temp32;
521 if (!get_masked(h)) {
524 unsigned int offset = get_masking_key_offset(h);
525 std::copy(e.bytes+offset,e.bytes+offset+4,temp32.c);
531 /// Extract the extended size field from an extended header
533 * It is the responsibility of the caller to verify that e is a valid extended
534 * header. This function assumes that e contains an extended payload size.
536 * @param e The extended header to extract from
538 * @return The size encoded in the extended header in host byte order
540 inline uint16_t get_extended_size(const extended_header &e) {
541 uint16_converter temp16;
542 std::copy(e.bytes,e.bytes+2,temp16.c);
543 return ntohs(temp16.i);
546 /// Extract the jumbo size field from an extended header
548 * It is the responsibility of the caller to verify that e is a valid extended
549 * header. This function assumes that e contains a jumbo payload size.
551 * @param e The extended header to extract from
553 * @return The size encoded in the extended header in host byte order
555 inline uint64_t get_jumbo_size(const extended_header &e) {
556 uint64_converter temp64;
557 std::copy(e.bytes,e.bytes+8,temp64.c);
558 return lib::net::_ntohll(temp64.i);
561 /// Extract the full payload size field from a WebSocket header
563 * It is the responsibility of the caller to verify that h and e together
564 * represent a valid WebSocket frame header. This function assumes only that h
565 * and e are valid. It uses information in the basic header to determine where
566 * to look for the payload_size
568 * @param h The basic header to extract from
569 * @param e The extended header to extract from
571 * @return The size encoded in the combined header in host byte order.
573 inline uint64_t get_payload_size(const basic_header &h, const
576 uint8_t val = get_basic_size(h);
578 if (val <= limits::payload_size_basic) {
580 } else if (val == payload_size_code_16bit) {
581 return get_extended_size(e);
583 return get_jumbo_size(e);
587 /// Extract a masking key into a value the size of a machine word.
589 * Machine word size must be 4 or 8.
591 * @param key Masking key to extract from
593 * @return prepared key as a machine word
595 inline size_t prepare_masking_key(const masking_key_type& key) {
596 size_t low_bits = static_cast<size_t>(key.i);
598 if (sizeof(size_t) == 8) {
599 uint64_t high_bits = static_cast<size_t>(key.i);
600 return static_cast<size_t>((high_bits << 32) | low_bits);
606 /// circularly shifts the supplied prepared masking key by offset bytes
608 * Prepared_key must be the output of prepare_masking_key with the associated
609 * restrictions on the machine word size. offset must be greater than or equal
610 * to zero and less than sizeof(size_t).
612 inline size_t circshift_prepared_key(size_t prepared_key, size_t offset) {
613 if (lib::net::is_little_endian()) {
614 size_t temp = prepared_key << (sizeof(size_t)-offset)*8;
615 return (prepared_key >> offset*8) | temp;
617 size_t temp = prepared_key >> (sizeof(size_t)-offset)*8;
618 return (prepared_key << offset*8) | temp;
622 /// Byte by byte mask/unmask
624 * Iterator based byte by byte masking and unmasking for WebSocket payloads.
625 * Performs masking in place using the supplied key offset by the supplied
626 * offset number of bytes.
628 * This function is simple and can be done in place on input with arbitrary
629 * lengths and does not vary based on machine word size. It is slow.
631 * @param b Beginning iterator to start masking
633 * @param e Ending iterator to end masking
635 * @param o Beginning iterator to store masked results
637 * @param key 32 bit key to mask with.
639 * @param key_offset offset value to start masking at.
641 template <typename input_iter, typename output_iter>
642 void byte_mask(input_iter first, input_iter last, output_iter result,
643 masking_key_type const & key, size_t key_offset)
645 size_t key_index = key_offset%4;
646 while (first != last) {
647 *result = *first ^ key.c[key_index++];
654 /// Byte by byte mask/unmask (in place)
656 * Iterator based byte by byte masking and unmasking for WebSocket payloads.
657 * Performs masking in place using the supplied key offset by the supplied
658 * offset number of bytes.
660 * This function is simple and can be done in place on input with arbitrary
661 * lengths and does not vary based on machine word size. It is slow.
663 * @param b Beginning iterator to start masking
665 * @param e Ending iterator to end masking
667 * @param key 32 bit key to mask with.
669 * @param key_offset offset value to start masking at.
671 template <typename iter_type>
672 void byte_mask(iter_type b, iter_type e, masking_key_type const & key,
675 byte_mask(b,e,b,key,key_offset);
678 /// Exact word aligned mask/unmask
680 * Balanced combination of byte by byte and circular word by word masking.
681 * Best used to mask complete messages at once. Has much higher setup costs than
682 * word_mask_circ but works with exact sized buffers.
684 * Buffer based word by word masking and unmasking for WebSocket payloads.
685 * Masking is done in word by word chunks with the remainder not divisible by
686 * the word size done byte by byte.
688 * input and output must both be at least length bytes. Exactly length bytes
691 * @param input buffer to mask or unmask
693 * @param output buffer to store the output. May be the same as input.
695 * @param length length of data buffer
697 * @param key Masking key to use
699 inline void word_mask_exact(uint8_t* input, uint8_t* output, size_t length,
700 const masking_key_type& key)
702 size_t prepared_key = prepare_masking_key(key);
703 size_t n = length/sizeof(size_t);
704 size_t* input_word = reinterpret_cast<size_t*>(input);
705 size_t* output_word = reinterpret_cast<size_t*>(output);
707 for (size_t i = 0; i < n; i++) {
708 output_word[i] = input_word[i] ^ prepared_key;
711 for (size_t i = n*sizeof(size_t); i < length; i++) {
712 output[i] = input[i] ^ key.c[i%4];
716 /// Exact word aligned mask/unmask (in place)
718 * In place version of word_mask_exact
720 * @see word_mask_exact
722 * @param data buffer to read and write from
724 * @param length length of data buffer
726 * @param key Masking key to use
728 inline void word_mask_exact(uint8_t* data, size_t length, const
729 masking_key_type& key)
731 word_mask_exact(data,data,length,key);
734 /// Circular word aligned mask/unmask
736 * Performs a circular mask/unmask in word sized chunks using pre-prepared keys
737 * that store state between calls. Best for providing streaming masking or
738 * unmasking of small chunks at a time of a larger message. Requires that the
739 * underlying allocated size of the data buffer be a multiple of the word size.
740 * Data in the buffer after `length` will be overwritten only with the same
741 * values that were originally present.
743 * Buffer based word by word masking and unmasking for WebSocket payloads.
744 * Performs masking in place using the supplied key. Casts the data buffer to
745 * an array of size_t's and performs masking word by word. The underlying
746 * buffer size must be a muliple of the word size.
748 * word_mask returns a copy of prepared_key circularly shifted based on the
749 * length value. The returned value may be fed back into word_mask when more
752 * input and output must both have length at least:
753 * ceil(length/sizeof(size_t))*sizeof(size_t)
754 * Exactly that many bytes will be written, although only exactly length bytes
755 * will be changed (trailing bytes will be replaced without masking)
757 * @param data Character buffer to mask
759 * @param length Length of data
761 * @param prepared_key Prepared key to use.
763 * @return the prepared_key shifted to account for the input length
765 inline size_t word_mask_circ(uint8_t * input, uint8_t * output, size_t length,
768 size_t n = length / sizeof(size_t); // whole words
769 size_t l = length - (n * sizeof(size_t)); // remaining bytes
770 size_t * input_word = reinterpret_cast<size_t *>(input);
771 size_t * output_word = reinterpret_cast<size_t *>(output);
774 for (size_t i = 0; i < n; i++) {
775 output_word[i] = input_word[i] ^ prepared_key;
778 // mask partial word at the end
779 size_t start = length - l;
780 uint8_t * byte_key = reinterpret_cast<uint8_t *>(&prepared_key);
781 for (size_t i = 0; i < l; ++i) {
782 output[start+i] = input[start+i] ^ byte_key[i];
785 return circshift_prepared_key(prepared_key,l);
788 /// Circular word aligned mask/unmask (in place)
790 * In place version of word_mask_circ
792 * @see word_mask_circ
794 * @param data Character buffer to read from and write to
796 * @param length Length of data
798 * @param prepared_key Prepared key to use.
800 * @return the prepared_key shifted to account for the input length
802 inline size_t word_mask_circ(uint8_t* data, size_t length, size_t prepared_key){
803 return word_mask_circ(data,data,length,prepared_key);
806 /// Circular byte aligned mask/unmask
808 * Performs a circular mask/unmask in byte sized chunks using pre-prepared keys
809 * that store state between calls. Best for providing streaming masking or
810 * unmasking of small chunks at a time of a larger message. Requires that the
811 * underlying allocated size of the data buffer be a multiple of the word size.
812 * Data in the buffer after `length` will be overwritten only with the same
813 * values that were originally present.
815 * word_mask returns a copy of prepared_key circularly shifted based on the
816 * length value. The returned value may be fed back into byte_mask when more
819 * @param data Character buffer to mask
821 * @param length Length of data
823 * @param prepared_key Prepared key to use.
825 * @return the prepared_key shifted to account for the input length
827 inline size_t byte_mask_circ(uint8_t * input, uint8_t * output, size_t length,
830 uint32_converter key;
831 key.i = prepared_key;
833 for (size_t i = 0; i < length; ++i) {
834 output[i] = input[i] ^ key.c[i % 4];
837 return circshift_prepared_key(prepared_key,length % 4);
840 /// Circular byte aligned mask/unmask (in place)
842 * In place version of byte_mask_circ
844 * @see byte_mask_circ
846 * @param data Character buffer to read from and write to
848 * @param length Length of data
850 * @param prepared_key Prepared key to use.
852 * @return the prepared_key shifted to account for the input length
854 inline size_t byte_mask_circ(uint8_t* data, size_t length, size_t prepared_key){
855 return byte_mask_circ(data,data,length,prepared_key);
859 } // namespace websocketpp
861 #endif //WEBSOCKETPP_FRAME_HPP