FreeRDP-WebConnect WebSockets gateway  1.0.0.167
 All Classes Namespaces Functions Variables Typedefs Enumerations Friends Pages
wsframe.hpp
1 /*
2  * This file has been derived from the WebSockets++ project at
3  * https://github.com/zaphoyd/websocketpp which is licensed under a BSD-license.
4  *
5  * Copyright (c) 2011, Peter Thorson. All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are met:
9  * * Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * * Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in the
13  * documentation and/or other materials provided with the distribution.
14  * * Neither the name of the WebSocket++ Project nor the
15  * names of its contributors may be used to endorse or promote products
16  * derived from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
22  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  *
29  */
30 
31 #ifndef WEBSOCKET_FRAME_HPP
32 #define WEBSOCKET_FRAME_HPP
33 
34 #include "wscommon.hpp"
35 #include "wsutf8.hpp"
36 #include "btexception.hpp"
37 
38 #if defined(_WIN32)
39 #include <winsock2.h>
40 #else
41 #include <arpa/inet.h>
42 #endif
43 
44 #include <vector>
45 #include <cstring>
46 #include <iostream>
47 #include <algorithm>
48 
49 static int64_t htonll(int64_t v) {
50  static int HOST_IS_LE = 0x1234;
51  if (HOST_IS_LE == 0x1234)
52  HOST_IS_LE = (htons(1) != 1);
53  if (HOST_IS_LE) {
54  union { uint32_t hv[2]; int64_t v; } u;
55  u.hv[0] = htonl(v >> 32);
56  u.hv[1] = htonl(v & 0x0FFFFFFFFULL);
57  return u.v;
58  }
59  return v;
60 }
61 #define ntohll(x) htonll(x)
62 
63 namespace tracing {
64 
70  class wserror : public exception
71  {
72  public:
73  enum {
74  FATAL_ERROR = 0, // force session end
75  SOFT_ERROR = 1, // should log and ignore
76  PROTOCOL_VIOLATION = 2, // must end session
77  PAYLOAD_VIOLATION = 3, // should end session
78  INTERNAL_ENDPOINT_ERROR = 4,// cleanly end session
79  MESSAGE_TOO_BIG = 5, // ???
80  OUT_OF_MESSAGES = 6
81  };
82 
88  explicit wserror(const std::string& __arg, int code = wserror::FATAL_ERROR)
89  : exception(), msg(__arg) , ecode(code) { }
91  virtual ~wserror() throw() { }
96  virtual const char* what() const throw()
97  { return msg.c_str(); }
102  virtual int code() const { return ecode; }
103 
104  private:
105  std::string msg;
106  int ecode;
107  };
108 }
109 
110 namespace wspp {
111 
115  class simple_rng {
116  public:
118  simple_rng() : seed(::time(NULL)) { }
123  int32_t gen() {
124 #ifdef _WIN32
125  return ::rand();
126 #else
127  return ::rand_r(&seed);
128 #endif
129  }
130  private:
131  unsigned int seed;
132  };
133 
134  namespace frame {
135 
136  /* policies to abstract out
137 
138  - random number generation
139  - utf8 validation
140 
141  rng
142  int32_t gen()
143 
144 
145  class boost_random {
146  public:
147  boost_random()
148  int32_t gen();
149  private:
150  boost::random::random_device m_rng;
151  boost::random::variate_generator<boost::random::random_device&,boost::random::uniform_int_distribution<> > m_gen;
152  }
153 
154 */
155 
156  template <class rng_policy>
160  class parser {
161  private:
162  // basic payload byte flags
163  static const uint8_t BPB0_OPCODE = 0x0F;
164  static const uint8_t BPB0_RSV3 = 0x10;
165  static const uint8_t BPB0_RSV2 = 0x20;
166  static const uint8_t BPB0_RSV1 = 0x40;
167  static const uint8_t BPB0_FIN = 0x80;
168  static const uint8_t BPB1_PAYLOAD = 0x7F;
169  static const uint8_t BPB1_MASK = 0x80;
170 
171  static const uint8_t BASIC_PAYLOAD_16BIT_CODE = 0x7E; // 126
172  static const uint8_t BASIC_PAYLOAD_64BIT_CODE = 0x7F; // 127
173 
174  static const unsigned int BASIC_HEADER_LENGTH = 2;
175  static const unsigned int MAX_HEADER_LENGTH = 14;
176  static const uint8_t extended_header_length = 12;
177  static const uint64_t max_payload_size = 100000000; // 100MB
178 
179  public:
184  parser(rng_policy& rng)
185  : m_state(STATE_BASIC_HEADER)
186  , m_bytes_needed(BASIC_HEADER_LENGTH)
187  , m_degraded(false)
188  , m_payload(std::vector<unsigned char>())
189  , m_rng(rng)
190  {
191  reset();
192  }
193 
198  bool ready() const {
199  return (m_state == STATE_READY);
200  }
201 
205  void reset() {
206  m_state = STATE_BASIC_HEADER;
207  m_bytes_needed = BASIC_HEADER_LENGTH;
208  m_degraded = false;
209  m_payload.clear();
210  std::fill(m_header,m_header+MAX_HEADER_LENGTH,0);
211  }
212 
222  void consume(std::istream &s) {
223  try {
224  switch (m_state) {
225  case STATE_BASIC_HEADER:
226  s.read(&m_header[BASIC_HEADER_LENGTH-m_bytes_needed],m_bytes_needed);
227 
228  m_bytes_needed -= s.gcount();
229 
230  if (m_bytes_needed == 0) {
231  process_basic_header();
232 
233  validate_basic_header();
234 
235  if (m_bytes_needed > 0) {
236  m_state = STATE_EXTENDED_HEADER;
237  } else {
238  process_extended_header();
239 
240  if (m_bytes_needed == 0) {
241  m_state = STATE_READY;
242  process_payload();
243 
244  } else {
245  m_state = STATE_PAYLOAD;
246  }
247  }
248  }
249  break;
250  case STATE_EXTENDED_HEADER:
251  s.read(&m_header[get_header_len()-m_bytes_needed],m_bytes_needed);
252 
253  m_bytes_needed -= s.gcount();
254 
255  if (m_bytes_needed == 0) {
256  process_extended_header();
257  if (m_bytes_needed == 0) {
258  m_state = STATE_READY;
259  process_payload();
260  } else {
261  m_state = STATE_PAYLOAD;
262  }
263  }
264  break;
265  case STATE_PAYLOAD:
266  s.read(reinterpret_cast<char *>(&m_payload[m_payload.size()-m_bytes_needed]),
267  m_bytes_needed);
268 
269  m_bytes_needed -= s.gcount();
270 
271  if (m_bytes_needed == 0) {
272  m_state = STATE_READY;
273  process_payload();
274  }
275  break;
276  case STATE_RECOVERY:
277  // Recovery state discards all bytes that are not the first byte
278  // of a close frame.
279  do {
280  s.read(reinterpret_cast<char *>(&m_header[0]),1);
281  if (int(static_cast<unsigned char>(m_header[0])) == 0x88) {
282  //(BPB0_FIN && CONNECTION_CLOSE)
283  m_bytes_needed--;
284  m_state = STATE_BASIC_HEADER;
285  break;
286  }
287  } while (s.gcount() > 0);
288  break;
289  default:
290  break;
291  }
292 
293  } catch (const std::exception & e) {
294  // After this point all non-close frames must be considered garbage,
295  // including the current one. Reset it and put the reading frame into
296  // a recovery state.
297  if (m_degraded == true) {
298  throw tracing::wserror("An error occurred while trying to gracefully recover from a less serious frame error.");
299  } else {
300  reset();
301  m_state = STATE_RECOVERY;
302  m_degraded = true;
303 
304  throw e;
305  }
306  }
307  }
308 
313  std::string get_header_str() {
314  return std::string(m_header, get_header_len());
315  }
316 
321  std::string get_payload_str() const {
322  return std::string(m_payload.begin(), m_payload.end());
323  }
324 
329  std::vector<unsigned char> &get_payload() {
330  return m_payload;
331  }
332 
337  bool is_control() const {
338  return (opcode::is_control(get_opcode()));
339  }
340 
345  void set_fin(bool fin) {
346  if (fin) {
347  m_header[0] |= BPB0_FIN;
348  } else {
349  m_header[0] &= (0xFF ^ BPB0_FIN);
350  }
351  }
352 
357  opcode::value get_opcode() const {
358  return frame::opcode::value(m_header[0] & BPB0_OPCODE);
359  }
360 
365  void set_opcode(opcode::value op) {
366  if (opcode::reserved(op)) {
367  throw tracing::wserror("reserved opcode",tracing::wserror::PROTOCOL_VIOLATION);
368  }
369 
370  if (opcode::invalid(op)) {
371  throw tracing::wserror("invalid opcode",tracing::wserror::PROTOCOL_VIOLATION);
372  }
373 
374  if (is_control() && get_basic_size() > limits::PAYLOAD_SIZE_BASIC) {
375  throw tracing::wserror("control frames can't have large payloads",tracing::wserror::PROTOCOL_VIOLATION);
376  }
377 
378  m_header[0] &= (0xFF ^ BPB0_OPCODE); // clear op bits
379  m_header[0] |= op; // set op bits
380  }
381 
386  void set_masked(bool masked) {
387  if (masked) {
388  m_header[1] |= BPB1_MASK;
389  generate_masking_key();
390  } else {
391  m_header[1] &= (0xFF ^ BPB1_MASK);
392  clear_masking_key();
393  }
394  }
395 
400  void set_payload(const std::string& source) {
401  set_payload_helper(source.size());
402 
403  std::copy(source.begin(),source.end(),m_payload.begin());
404  }
405 
410  void set_payload(const std::vector<unsigned char>& source) {
411  set_payload_helper(source.size());
412 
413  std::copy(source.begin(),source.end(),m_payload.begin());
414  }
415 
420  close::status::value get_close_code() const {
421  if (m_payload.size() == 0) {
422  return close::status::NO_STATUS;
423  } else {
424  return close::status::value(get_raw_close_code());
425  }
426  }
427 
432  std::string get_close_reason() const {
433  if (m_payload.size() > 2) {
434  return get_payload_str().substr(2);
435  } else {
436  return std::string();
437  }
438  }
439 
440  private:
446  uint64_t get_bytes_needed() const {
447  return m_bytes_needed;
448  }
449 
450  // get pointers to underlying buffers
451  char* get_header() {
452  return m_header;
453  }
454  char* get_extended_header() {
455  return m_header+BASIC_HEADER_LENGTH;
456  }
457  unsigned int get_header_len() const {
458  unsigned int temp = 2;
459 
460  if (get_masked()) {
461  temp += 4;
462  }
463 
464  if (get_basic_size() == 126) {
465  temp += 2;
466  } else if (get_basic_size() == 127) {
467  temp += 8;
468  }
469 
470  return temp;
471  }
472 
473  char* get_masking_key() {
474  return &m_header[get_header_len()-4];
475  }
476 
477  // get and set header bits
478  bool get_fin() const {
479  return ((m_header[0] & BPB0_FIN) == BPB0_FIN);
480  }
481  bool get_rsv1() const {
482  return ((m_header[0] & BPB0_RSV1) == BPB0_RSV1);
483  }
484  void set_rsv1(bool b) {
485  if (b) {
486  m_header[0] |= BPB0_RSV1;
487  } else {
488  m_header[0] &= (0xFF ^ BPB0_RSV1);
489  }
490  }
491 
492  bool get_rsv2() const {
493  return ((m_header[0] & BPB0_RSV2) == BPB0_RSV2);
494  }
495  void set_rsv2(bool b) {
496  if (b) {
497  m_header[0] |= BPB0_RSV2;
498  } else {
499  m_header[0] &= (0xFF ^ BPB0_RSV2);
500  }
501  }
502 
503  bool get_rsv3() const {
504  return ((m_header[0] & BPB0_RSV3) == BPB0_RSV3);
505  }
506  void set_rsv3(bool b) {
507  if (b) {
508  m_header[0] |= BPB0_RSV3;
509  } else {
510  m_header[0] &= (0xFF ^ BPB0_RSV3);
511  }
512  }
513 
514  bool get_masked() const {
515  return ((m_header[1] & BPB1_MASK) == BPB1_MASK);
516  }
517  uint8_t get_basic_size() const {
518  return m_header[1] & BPB1_PAYLOAD;
519  }
520  size_t get_payload_size() const {
521  if (m_state != STATE_READY && m_state != STATE_PAYLOAD) {
522  // TODO: how to handle errors like this?
523  throw "attempted to get payload size before reading full header";
524  }
525 
526  return m_payload.size();
527  }
528 
529  close::status::value get_close_status() const {
530  if (get_payload_size() == 0) {
531  return close::status::NO_STATUS;
532  } else if (get_payload_size() >= 2) {
533  char val[2] = { m_payload[0], m_payload[1] };
534  uint16_t code;
535 
536  std::copy(val,val+sizeof(code),&code);
537  code = ntohs(code);
538 
539  return close::status::value(code);
540  } else {
541  return close::status::PROTOCOL_ERROR;
542  }
543  }
544  std::string get_close_msg() const {
545  if (get_payload_size() > 2) {
546  uint32_t state = utf8_validator::UTF8_ACCEPT;
547  uint32_t codep = 0;
548  validate_utf8(&state,&codep,2);
549  if (state != utf8_validator::UTF8_ACCEPT) {
550  throw tracing::wserror("Invalid UTF-8 Data",tracing::wserror::PAYLOAD_VIOLATION);
551  }
552  return std::string(m_payload.begin()+2,m_payload.end());
553  } else {
554  return std::string();
555  }
556  }
557 
558  void set_payload_helper(uint64_t s) {
559  if (s > max_payload_size) {
560  throw tracing::wserror("requested payload is over implementation defined limit",tracing::wserror::MESSAGE_TOO_BIG);
561  }
562 
563  // limits imposed by the websocket spec
564  if (is_control() && s > limits::PAYLOAD_SIZE_BASIC) {
565  throw tracing::wserror("control frames can't have large payloads",tracing::wserror::PROTOCOL_VIOLATION);
566  }
567 
568  bool masked = get_masked();
569 
570  if (s <= limits::PAYLOAD_SIZE_BASIC) {
571  m_header[1] = s;
572  } else if (s <= limits::PAYLOAD_SIZE_EXTENDED) {
573  m_header[1] = BASIC_PAYLOAD_16BIT_CODE;
574 
575  // this reinterprets the second pair of bytes in m_header as a
576  // 16 bit int and writes the payload size there as an integer
577  // in network byte order
578  *reinterpret_cast<uint16_t*>(&m_header[BASIC_HEADER_LENGTH]) = htons(s);
579  } else if (s <= limits::PAYLOAD_SIZE_JUMBO) {
580  m_header[1] = BASIC_PAYLOAD_64BIT_CODE;
581  *reinterpret_cast<uint64_t*>(&m_header[BASIC_HEADER_LENGTH]) = htonll(s);
582  } else {
583  throw tracing::wserror("payload size limit is 63 bits",tracing::wserror::PROTOCOL_VIOLATION);
584  }
585 
586  if (masked) {
587  m_header[1] |= BPB1_MASK;
588  }
589 
590  m_payload.resize(s);
591  }
592 
593  void set_status(close::status::value status,const std::string message = "") {
594  // check for valid statuses
595  if (close::status::invalid(status)) {
596  std::stringstream err;
597  err << "Status code " << status << " is invalid";
598  throw tracing::wserror(err.str());
599  }
600 
601  if (close::status::reserved(status)) {
602  std::stringstream err;
603  err << "Status code " << status << " is reserved";
604  throw tracing::wserror(err.str());
605  }
606 
607  m_payload.resize(2+message.size());
608 
609  char val[2];
610 
611  *reinterpret_cast<uint16_t*>(&val[0]) = htons(status);
612 
613  bool masked = get_masked();
614 
615  m_header[1] = message.size()+2;
616 
617  if (masked) {
618  m_header[1] |= BPB1_MASK;
619  }
620 
621  m_payload[0] = val[0];
622  m_payload[1] = val[1];
623 
624  std::copy(message.begin(),message.end(),m_payload.begin()+2);
625  }
626 
627  std::string print_frame() const {
628  std::stringstream f;
629 
630  unsigned int len = get_header_len();
631 
632  f << "frame: ";
633  // print header
634  for (unsigned int i = 0; i < len; i++) {
635  f << std::hex << (unsigned short)m_header[i] << " ";
636  }
637  // print message
638  if (m_payload.size() > 50) {
639  f << "[payload of " << m_payload.size() << " bytes]";
640  } else {
641  std::vector<unsigned char>::const_iterator it;
642  for (it = m_payload.begin(); it != m_payload.end(); it++) {
643  f << *it;
644  }
645  }
646  return f.str();
647  }
648 
649  // reads basic header, sets and returns m_header_bits_needed
650  void process_basic_header() {
651  m_bytes_needed = get_header_len() - BASIC_HEADER_LENGTH;
652  }
653  void process_extended_header() {
654  uint8_t s = get_basic_size();
655  uint64_t payload_size;
656  int mask_index = BASIC_HEADER_LENGTH;
657 
658  if (s <= limits::PAYLOAD_SIZE_BASIC) {
659  payload_size = s;
660  } else if (s == BASIC_PAYLOAD_16BIT_CODE) {
661  // reinterpret the second two bytes as a 16 bit integer in network
662  // byte order. Convert to host byte order and store locally.
663  payload_size = ntohs(*(
664  reinterpret_cast<uint16_t*>(&m_header[BASIC_HEADER_LENGTH])
665  ));
666 
667  if (payload_size < s) {
668  std::stringstream err;
669  err << "payload length not minimally encoded. Using 16 bit form for payload size: " << payload_size;
670  m_bytes_needed = payload_size;
671  throw tracing::wserror(err.str(),tracing::wserror::PROTOCOL_VIOLATION);
672  }
673 
674  mask_index += 2;
675  } else if (s == BASIC_PAYLOAD_64BIT_CODE) {
676  // reinterpret the second eight bytes as a 64 bit integer in
677  // network byte order. Convert to host byte order and store.
678  payload_size = ntohll(*(
679  reinterpret_cast<uint64_t*>(&m_header[BASIC_HEADER_LENGTH])
680  ));
681 
682  if (payload_size <= limits::PAYLOAD_SIZE_EXTENDED) {
683  m_bytes_needed = payload_size;
684  throw tracing::wserror("payload length not minimally encoded",
685  tracing::wserror::PROTOCOL_VIOLATION);
686  }
687 
688  mask_index += 8;
689  } else {
690  // TODO: shouldn't be here how to handle?
691  throw tracing::wserror("invalid get_basic_size in process_extended_header");
692  }
693 
694  if (get_masked() == 0) {
695  clear_masking_key();
696  } else {
697  // TODO: this should be removed entirely once it is confirmed to not
698  // be used by anything.
699  // std::copy(m_header[mask_index],m_header[mask_index+4],m_masking_key);
700  /*m_masking_key[0] = m_header[mask_index+0];
701  m_masking_key[1] = m_header[mask_index+1];
702  m_masking_key[2] = m_header[mask_index+2];
703  m_masking_key[3] = m_header[mask_index+3];*/
704  }
705 
706  if (payload_size > max_payload_size) {
707  // TODO: frame/message size limits
708  // TODO: find a way to throw a server error without coupling frame
709  // with server
710  // throw wspp::server_error("got frame with payload greater than maximum frame buffer size.");
711  throw "Got frame with payload greater than maximum frame buffer size.";
712  }
713  m_payload.resize(payload_size);
714  m_bytes_needed = payload_size;
715  }
716 
717  void process_payload() {
718  if (get_masked()) {
719  char *masking_key = get_masking_key();
720 
721  for (uint64_t i = 0; i < m_payload.size(); i++) {
722  m_payload[i] = (m_payload[i] ^ masking_key[i%4]);
723  }
724  }
725  }
726 
727  // experiment with more efficient masking code.
728  void process_payload2() {
729  // unmask payload one byte at a time
730 
731  //uint64_t key = (*((uint32_t*)m_masking_key;)) << 32;
732  //key += *((uint32_t*)m_masking_key);
733 
734  // might need to switch byte order
735  /*uint32_t key = *((uint32_t*)m_masking_key);
736 
737  // 4
738 
739  uint64_t i = 0;
740  uint64_t s = (m_payload.size() / 4);
741 
742  // chunks of 4
743  for (i = 0; i < s; i+=4) {
744  ((uint32_t*)(&m_payload[0]))[i] = (((uint32_t*)(&m_payload[0]))[i] ^ key);
745  }
746 
747  // finish the last few
748  for (i = s; i < m_payload.size(); i++) {
749  m_payload[i] = (m_payload[i] ^ m_masking_key[i%4]);
750  }*/
751  }
752 
753  void validate_utf8(uint32_t* state,uint32_t* codep,size_t offset = 0) const {
754  for (size_t i = offset; i < m_payload.size(); i++) {
755  using utf8_validator::decode;
756 
757  if (decode(state,codep,m_payload[i]) == utf8_validator::UTF8_REJECT) {
758  throw tracing::wserror("Invalid UTF-8 Data",tracing::wserror::PAYLOAD_VIOLATION);
759  }
760  }
761  }
762  void validate_basic_header() const {
763  // check for control frame size
764  if (is_control() && get_basic_size() > limits::PAYLOAD_SIZE_BASIC) {
765  throw tracing::wserror("Control Frame is too large",tracing::wserror::PROTOCOL_VIOLATION);
766  }
767 
768  // check for reserved bits
769  if (get_rsv1() || get_rsv2() || get_rsv3()) {
770  throw tracing::wserror("Reserved bit used",tracing::wserror::PROTOCOL_VIOLATION);
771  }
772 
773  // check for reserved opcodes
774  if (opcode::reserved(get_opcode())) {
775  throw tracing::wserror("Reserved opcode used",tracing::wserror::PROTOCOL_VIOLATION);
776  }
777 
778  // check for fragmented control message
779  if (is_control() && !get_fin()) {
780  throw tracing::wserror("Fragmented control message",tracing::wserror::PROTOCOL_VIOLATION);
781  }
782  }
783 
784  void generate_masking_key() {
785  *(reinterpret_cast<int32_t *>(&m_header[get_header_len()-4])) = m_rng.gen();
786  }
787  void clear_masking_key() {
788  // this is a no-op as clearing the mask bit also changes the get_header_len
789  // method to not include these byte ranges. Whenever the masking bit is re-
790  // set a new key is generated anyways.
791  }
792 
793 
794  private:
795  uint16_t get_raw_close_code() const {
796  if (m_payload.size() <= 1) {
797  throw tracing::wserror("get_raw_close_code called with invalid size",tracing::wserror::FATAL_ERROR);
798  }
799 
800  union {uint16_t i;char c[2];} val;
801 
802  val.c[0] = m_payload[0];
803  val.c[1] = m_payload[1];
804 
805  return ntohs(val.i);
806  }
807 
808  static const uint8_t STATE_BASIC_HEADER = 1;
809  static const uint8_t STATE_EXTENDED_HEADER = 2;
810  static const uint8_t STATE_PAYLOAD = 3;
811  static const uint8_t STATE_READY = 4;
812  static const uint8_t STATE_RECOVERY = 5;
813 
814  uint8_t m_state;
815  uint64_t m_bytes_needed;
816  bool m_degraded;
817 
818  char m_header[MAX_HEADER_LENGTH];
819  std::vector<unsigned char> m_payload;
820 
821  rng_policy& m_rng;
822  };
823 
824  }
825 }
826 
827 #endif // WEBSOCKET_FRAME_HPP