NS-3 based Named Data Networking (NDN) simulator
ndnSIM 2.5: NDN, CCN, CCNx, content centric networks
API Documentation
enabled.hpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2015, Peter Thorson. All rights reserved.
3  *
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.
14  *
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.
25  *
26  */
27 
28 #ifndef WEBSOCKETPP_PROCESSOR_EXTENSION_PERMESSAGEDEFLATE_HPP
29 #define WEBSOCKETPP_PROCESSOR_EXTENSION_PERMESSAGEDEFLATE_HPP
30 
31 
37 #include <websocketpp/error.hpp>
38 
40 
41 #include "zlib.h"
42 
43 #include <algorithm>
44 #include <string>
45 #include <vector>
46 
47 namespace websocketpp {
48 namespace extensions {
49 
51 
88 namespace permessage_deflate {
89 
91 namespace error {
92 enum value {
94  general = 1,
95 
98 
101 
104 
107 
110 
113 
116 };
117 
119 class category : public lib::error_category {
120 public:
121  category() {}
122 
123  char const * name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ {
124  return "websocketpp.extension.permessage-deflate";
125  }
126 
127  std::string message(int value) const {
128  switch(value) {
129  case general:
130  return "Generic permessage-compress error";
131  case invalid_attributes:
132  return "Invalid extension attributes";
134  return "Invalid extension attribute value";
135  case invalid_mode:
136  return "Invalid permessage-deflate negotiation mode";
138  return "Unsupported extension attributes";
140  return "Invalid value for max_window_bits";
141  case zlib_error:
142  return "A zlib function returned an error";
143  case uninitialized:
144  return "Deflate extension must be initialized before use";
145  default:
146  return "Unknown permessage-compress error";
147  }
148  }
149 };
150 
152 inline lib::error_category const & get_category() {
153  static category instance;
154  return instance;
155 }
156 
158 inline lib::error_code make_error_code(error::value e) {
159  return lib::error_code(static_cast<int>(e), get_category());
160 }
161 
162 } // namespace error
163 } // namespace permessage_deflate
164 } // namespace extensions
165 } // namespace websocketpp
166 
168 template<> struct is_error_code_enum
170 {
171  static bool const value = true;
172 };
174 namespace websocketpp {
175 namespace extensions {
176 namespace permessage_deflate {
177 
179 static uint8_t const default_server_max_window_bits = 15;
181 
187 static uint8_t const min_server_max_window_bits = 8;
189 static uint8_t const max_server_max_window_bits = 15;
190 
192 static uint8_t const default_client_max_window_bits = 15;
194 
200 static uint8_t const min_client_max_window_bits = 8;
202 static uint8_t const max_client_max_window_bits = 15;
203 
204 namespace mode {
205 enum value {
207  accept = 1,
214 };
215 } // namespace mode
216 
217 template <typename config>
218 class enabled {
219 public:
221  : m_enabled(false)
222  , m_server_no_context_takeover(false)
223  , m_client_no_context_takeover(false)
224  , m_server_max_window_bits(15)
225  , m_client_max_window_bits(15)
226  , m_server_max_window_bits_mode(mode::accept)
227  , m_client_max_window_bits_mode(mode::accept)
228  , m_initialized(false)
229  , m_compress_buffer_size(8192)
230  {
231  m_dstate.zalloc = Z_NULL;
232  m_dstate.zfree = Z_NULL;
233  m_dstate.opaque = Z_NULL;
234 
235  m_istate.zalloc = Z_NULL;
236  m_istate.zfree = Z_NULL;
237  m_istate.opaque = Z_NULL;
238  m_istate.avail_in = 0;
239  m_istate.next_in = Z_NULL;
240  }
241 
243  if (!m_initialized) {
244  return;
245  }
246 
247  int ret = deflateEnd(&m_dstate);
248 
249  if (ret != Z_OK) {
250  //std::cout << "error cleaning up zlib compression state"
251  // << std::endl;
252  }
253 
254  ret = inflateEnd(&m_istate);
255 
256  if (ret != Z_OK) {
257  //std::cout << "error cleaning up zlib decompression state"
258  // << std::endl;
259  }
260  }
261 
263 
273  lib::error_code init(bool is_server) {
274  uint8_t deflate_bits;
275  uint8_t inflate_bits;
276 
277  if (is_server) {
278  deflate_bits = m_server_max_window_bits;
279  inflate_bits = m_client_max_window_bits;
280  } else {
281  deflate_bits = m_client_max_window_bits;
282  inflate_bits = m_server_max_window_bits;
283  }
284 
285  int ret = deflateInit2(
286  &m_dstate,
287  Z_DEFAULT_COMPRESSION,
288  Z_DEFLATED,
289  -1*deflate_bits,
290  4, // memory level 1-9
291  Z_DEFAULT_STRATEGY
292  );
293 
294  if (ret != Z_OK) {
296  }
297 
298  ret = inflateInit2(
299  &m_istate,
300  -1*inflate_bits
301  );
302 
303  if (ret != Z_OK) {
305  }
306 
307  m_compress_buffer.reset(new unsigned char[m_compress_buffer_size]);
308  m_decompress_buffer.reset(new unsigned char[m_compress_buffer_size]);
309  if ((m_server_no_context_takeover && is_server) ||
310  (m_client_no_context_takeover && !is_server))
311  {
312  m_flush = Z_FULL_FLUSH;
313  } else {
314  m_flush = Z_SYNC_FLUSH;
315  }
316  m_initialized = true;
317  return lib::error_code();
318  }
319 
321 
326  bool is_implemented() const {
327  return true;
328  }
329 
331 
337  bool is_enabled() const {
338  return m_enabled;
339  }
340 
342 
363  m_server_no_context_takeover = true;
364  }
365 
367 
382  m_client_no_context_takeover = true;
383  }
384 
386 
415  lib::error_code set_server_max_window_bits(uint8_t bits, mode::value mode) {
416  if (bits < min_server_max_window_bits || bits > max_server_max_window_bits) {
418  }
419 
420  // See note in doc comment above about what is happening here
421  if (bits == 8) {
422  bits = 9;
423  }
424 
425  m_server_max_window_bits = bits;
426  m_server_max_window_bits_mode = mode;
427 
428  return lib::error_code();
429  }
430 
432 
460  lib::error_code set_client_max_window_bits(uint8_t bits, mode::value mode) {
461  if (bits < min_client_max_window_bits || bits > max_client_max_window_bits) {
463  }
464 
465  // See note in doc comment above about what is happening here
466  if (bits == 8) {
467  bits = 9;
468  }
469 
470  m_client_max_window_bits = bits;
471  m_client_max_window_bits_mode = mode;
472 
473  return lib::error_code();
474  }
475 
477 
483  std::string generate_offer() const {
484  // TODO: this should be dynamically generated based on user settings
485  return "permessage-deflate; client_no_context_takeover; client_max_window_bits";
486  }
487 
489 
496  lib::error_code validate_offer(http::attribute_list const &) {
497  return lib::error_code();
498  }
499 
501 
510  err_str_pair ret;
511 
512  http::attribute_list::const_iterator it;
513  for (it = offer.begin(); it != offer.end(); ++it) {
514  if (it->first == "server_no_context_takeover") {
515  negotiate_server_no_context_takeover(it->second,ret.first);
516  } else if (it->first == "client_no_context_takeover") {
517  negotiate_client_no_context_takeover(it->second,ret.first);
518  } else if (it->first == "server_max_window_bits") {
519  negotiate_server_max_window_bits(it->second,ret.first);
520  } else if (it->first == "client_max_window_bits") {
521  negotiate_client_max_window_bits(it->second,ret.first);
522  } else {
524  }
525 
526  if (ret.first) {
527  break;
528  }
529  }
530 
531  if (ret.first == lib::error_code()) {
532  m_enabled = true;
533  ret.second = generate_response();
534  }
535 
536  return ret;
537  }
538 
540 
548  lib::error_code compress(std::string const & in, std::string & out) {
549  if (!m_initialized) {
551  }
552 
553  size_t output;
554 
555  if (in.empty()) {
556  uint8_t buf[6] = {0x02, 0x00, 0x00, 0x00, 0xff, 0xff};
557  out.append((char *)(buf),6);
558  return lib::error_code();
559  }
560 
561  m_dstate.avail_in = in.size();
562  m_dstate.next_in = (unsigned char *)(const_cast<char *>(in.data()));
563 
564  do {
565  // Output to local buffer
566  m_dstate.avail_out = m_compress_buffer_size;
567  m_dstate.next_out = m_compress_buffer.get();
568 
569  deflate(&m_dstate, m_flush);
570 
571  output = m_compress_buffer_size - m_dstate.avail_out;
572 
573  out.append((char *)(m_compress_buffer.get()),output);
574  } while (m_dstate.avail_out == 0);
575 
576  return lib::error_code();
577  }
578 
580 
586  lib::error_code decompress(uint8_t const * buf, size_t len, std::string &
587  out)
588  {
589  if (!m_initialized) {
591  }
592 
593  int ret;
594 
595  m_istate.avail_in = len;
596  m_istate.next_in = const_cast<unsigned char *>(buf);
597 
598  do {
599  m_istate.avail_out = m_compress_buffer_size;
600  m_istate.next_out = m_decompress_buffer.get();
601 
602  ret = inflate(&m_istate, Z_SYNC_FLUSH);
603 
604  if (ret == Z_NEED_DICT || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR) {
606  }
607 
608  out.append(
609  reinterpret_cast<char *>(m_decompress_buffer.get()),
610  m_compress_buffer_size - m_istate.avail_out
611  );
612  } while (m_istate.avail_out == 0);
613 
614  return lib::error_code();
615  }
616 private:
618 
621  std::string generate_response() {
622  std::string ret = "permessage-deflate";
623 
624  if (m_server_no_context_takeover) {
625  ret += "; server_no_context_takeover";
626  }
627 
628  if (m_client_no_context_takeover) {
629  ret += "; client_no_context_takeover";
630  }
631 
632  if (m_server_max_window_bits < default_server_max_window_bits) {
633  std::stringstream s;
634  s << int(m_server_max_window_bits);
635  ret += "; server_max_window_bits="+s.str();
636  }
637 
638  if (m_client_max_window_bits < default_client_max_window_bits) {
639  std::stringstream s;
640  s << int(m_client_max_window_bits);
641  ret += "; client_max_window_bits="+s.str();
642  }
643 
644  return ret;
645  }
646 
648 
652  void negotiate_server_no_context_takeover(std::string const & value,
653  lib::error_code & ec)
654  {
655  if (!value.empty()) {
657  return;
658  }
659 
660  m_server_no_context_takeover = true;
661  }
662 
664 
668  void negotiate_client_no_context_takeover(std::string const & value,
669  lib::error_code & ec)
670  {
671  if (!value.empty()) {
673  return;
674  }
675 
676  m_client_no_context_takeover = true;
677  }
678 
680 
701  void negotiate_server_max_window_bits(std::string const & value,
702  lib::error_code & ec)
703  {
704  uint8_t bits = uint8_t(atoi(value.c_str()));
705 
706  if (bits < min_server_max_window_bits || bits > max_server_max_window_bits) {
708  m_server_max_window_bits = default_server_max_window_bits;
709  return;
710  }
711 
712  switch (m_server_max_window_bits_mode) {
713  case mode::decline:
714  m_server_max_window_bits = default_server_max_window_bits;
715  break;
716  case mode::accept:
717  m_server_max_window_bits = bits;
718  break;
719  case mode::largest:
720  m_server_max_window_bits = std::min(bits,m_server_max_window_bits);
721  break;
722  case mode::smallest:
723  m_server_max_window_bits = min_server_max_window_bits;
724  break;
725  default:
727  m_server_max_window_bits = default_server_max_window_bits;
728  }
729 
730  // See note in doc comment
731  if (m_server_max_window_bits == 8) {
732  m_server_max_window_bits = 9;
733  }
734  }
735 
737 
757  void negotiate_client_max_window_bits(std::string const & value,
758  lib::error_code & ec)
759  {
760  uint8_t bits = uint8_t(atoi(value.c_str()));
761 
762  if (value.empty()) {
764  } else if (bits < min_client_max_window_bits ||
765  bits > max_client_max_window_bits)
766  {
768  m_client_max_window_bits = default_client_max_window_bits;
769  return;
770  }
771 
772  switch (m_client_max_window_bits_mode) {
773  case mode::decline:
774  m_client_max_window_bits = default_client_max_window_bits;
775  break;
776  case mode::accept:
777  m_client_max_window_bits = bits;
778  break;
779  case mode::largest:
780  m_client_max_window_bits = std::min(bits,m_client_max_window_bits);
781  break;
782  case mode::smallest:
783  m_client_max_window_bits = min_client_max_window_bits;
784  break;
785  default:
787  m_client_max_window_bits = default_client_max_window_bits;
788  }
789 
790  // See note in doc comment
791  if (m_client_max_window_bits == 8) {
792  m_client_max_window_bits = 9;
793  }
794  }
795 
796  bool m_enabled;
797  bool m_server_no_context_takeover;
798  bool m_client_no_context_takeover;
799  uint8_t m_server_max_window_bits;
800  uint8_t m_client_max_window_bits;
801  mode::value m_server_max_window_bits_mode;
802  mode::value m_client_max_window_bits_mode;
803 
804  bool m_initialized;
805  int m_flush;
806  size_t m_compress_buffer_size;
807  lib::unique_ptr_uchar_array m_compress_buffer;
808  lib::unique_ptr_uchar_array m_decompress_buffer;
809  z_stream m_dstate;
810  z_stream m_istate;
811 };
812 
813 } // namespace permessage_deflate
814 } // namespace extensions
815 } // namespace websocketpp
816 
817 #endif // WEBSOCKETPP_PROCESSOR_EXTENSION_PERMESSAGEDEFLATE_HPP
lib::error_code set_server_max_window_bits(uint8_t bits, mode::value mode)
Limit server LZ77 sliding window size.
Definition: enabled.hpp:415
void enable_client_no_context_takeover()
Reset client&#39;s outgoing LZ77 sliding window for each new message.
Definition: enabled.hpp:381
static uint8_t const default_server_max_window_bits
Default value for server_max_window_bits as defined by RFC 7692.
Definition: enabled.hpp:179
Decline any value the remote endpoint offers. Insist on defaults.
Definition: enabled.hpp:209
lib::error_category const & get_category()
Get a reference to a static copy of the permessage-deflate error category.
Definition: enabled.hpp:152
Accept any value the remote endpoint offers.
Definition: enabled.hpp:207
std::pair< lib::error_code, std::string > err_str_pair
Combination error code / string type for returning two values.
Definition: error.hpp:41
#define _WEBSOCKETPP_NOEXCEPT_TOKEN_
Definition: cpp11.hpp:113
char const * name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_
Definition: enabled.hpp:123
lib::error_code init(bool is_server)
Initialize zlib state.
Definition: enabled.hpp:273
Use the smallest value common to both offers.
Definition: enabled.hpp:213
lib::error_code set_client_max_window_bits(uint8_t bits, mode::value mode)
Limit client LZ77 sliding window size.
Definition: enabled.hpp:460
lib::error_code validate_offer(http::attribute_list const &)
Validate extension response.
Definition: enabled.hpp:496
lib::error_code decompress(uint8_t const *buf, size_t len, std::string &out)
Decompress bytes.
Definition: enabled.hpp:586
Use the largest value common to both offers.
Definition: enabled.hpp:211
bool is_implemented() const
Test if this object implements the permessage-deflate specification.
Definition: enabled.hpp:326
lib::error_code make_error_code(error::value e)
Create an error code in the permessage-deflate category.
Definition: enabled.hpp:158
static uint8_t const min_client_max_window_bits
Minimum value for client_max_window_bits as defined by RFC 7692.
Definition: enabled.hpp:200
bool is_enabled() const
Test if the extension was negotiated for this connection.
Definition: enabled.hpp:337
Namespace for the WebSocket++ project.
Definition: base64.hpp:41
static uint8_t const max_server_max_window_bits
Maximum value for server_max_window_bits as defined by RFC 7692.
Definition: enabled.hpp:189
#define _WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_
err_str_pair negotiate(http::attribute_list const &offer)
Negotiate extension.
Definition: enabled.hpp:509
std::map< std::string, std::string > attribute_list
The type of an HTTP attribute list.
Definition: constants.hpp:45
void enable_server_no_context_takeover()
Reset server&#39;s outgoing LZ77 sliding window for each new message.
Definition: enabled.hpp:362
std::string generate_offer() const
Generate extension offer.
Definition: enabled.hpp:483
static uint8_t const min_server_max_window_bits
Minimum value for server_max_window_bits as defined by RFC 7692.
Definition: enabled.hpp:187
lib::error_code compress(std::string const &in, std::string &out)
Compress bytes.
Definition: enabled.hpp:548
#define _WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_
static uint8_t const max_client_max_window_bits
Maximum value for client_max_window_bits as defined by RFC 7692.
Definition: enabled.hpp:202
boost::scoped_array< unsigned char > unique_ptr_uchar_array
Definition: memory.hpp:82
static uint8_t const default_client_max_window_bits
Default value for client_max_window_bits as defined by RFC 7692.
Definition: enabled.hpp:192