WebSocket++  0.8.3-dev
C++ websocket client/server library
enabled.hpp
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 
32 #include <websocketpp/common/cpp11.hpp>
33 #include <websocketpp/common/memory.hpp>
34 #include <websocketpp/common/platforms.hpp>
35 #include <websocketpp/common/stdint.hpp>
36 #include <websocketpp/common/system_error.hpp>
37 #include <websocketpp/error.hpp>
38 
39 #include <websocketpp/extensions/extension.hpp>
40 
41 #include "zlib.h"
42 
43 #include <algorithm>
44 #include <string>
45 #include <vector>
46 
47 namespace websocketpp {
48 namespace extensions {
49 
50 /// Implementation of RFC 7692, the permessage-deflate WebSocket extension
51 /**
52  * ### permessage-deflate interface
53  *
54  * **init**\n
55  * `lib::error_code init(bool is_server)`\n
56  * Performs initialization
57  *
58  * **is_implimented**\n
59  * `bool is_implimented()`\n
60  * Returns whether or not the object impliments the extension or not
61  *
62  * **is_enabled**\n
63  * `bool is_enabled()`\n
64  * Returns whether or not the extension was negotiated for the current
65  * connection
66  *
67  * **generate_offer**\n
68  * `std::string generate_offer() const`\n
69  * Create an extension offer string based on local policy
70  *
71  * **validate_response**\n
72  * `lib::error_code validate_response(http::attribute_list const & response)`\n
73  * Negotiate the parameters of extension use
74  *
75  * **negotiate**\n
76  * `err_str_pair negotiate(http::attribute_list const & attributes)`\n
77  * Negotiate the parameters of extension use
78  *
79  * **compress**\n
80  * `lib::error_code compress(std::string const & in, std::string & out)`\n
81  * Compress the bytes in `in` and append them to `out`
82  *
83  * **decompress**\n
84  * `lib::error_code decompress(uint8_t const * buf, size_t len, std::string &
85  * out)`\n
86  * Decompress `len` bytes from `buf` and append them to string `out`
87  */
88 namespace permessage_deflate {
89 
90 /// Permessage deflate error values
91 namespace error {
92 enum value {
93  /// Catch all
94  general = 1,
95 
96  /// Invalid extension attributes
98 
99  /// Invalid extension attribute value
101 
102  /// Invalid megotiation mode
104 
105  /// Unsupported extension attributes
107 
108  /// Invalid value for max_window_bits
110 
111  /// ZLib Error
113 
114  /// Uninitialized
116 };
117 
118 /// Permessage-deflate error category
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";
133  case invalid_attribute_value:
134  return "Invalid extension attribute value";
135  case invalid_mode:
136  return "Invalid permessage-deflate negotiation mode";
137  case unsupported_attributes:
138  return "Unsupported extension attributes";
139  case invalid_max_window_bits:
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 
151 /// Get a reference to a static copy of the permessage-deflate error category
152 inline lib::error_category const & get_category() {
153  static category instance;
154  return instance;
155 }
156 
157 /// Create an error code in the permessage-deflate category
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 
167 _WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_
168 template<> struct is_error_code_enum
169  <websocketpp::extensions::permessage_deflate::error::value>
170 {
171  static bool const value = true;
172 };
173 _WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_
174 namespace websocketpp {
175 namespace extensions {
176 namespace permessage_deflate {
177 
178 /// Default value for server_max_window_bits as defined by RFC 7692
180 /// Minimum value for server_max_window_bits as defined by RFC 7692
181 /**
182  * NOTE: A value of 8 is not actually supported by zlib, the deflate
183  * library that WebSocket++ uses. To preserve backwards compatibility
184  * with RFC 7692 and previous versions of the library a value of 8
185  * is accepted by the library but will always be negotiated as 9.
186  */
188 /// Maximum value for server_max_window_bits as defined by RFC 7692
190 
191 /// Default value for client_max_window_bits as defined by RFC 7692
193 /// Minimum value for client_max_window_bits as defined by RFC 7692
194 /**
195  * NOTE: A value of 8 is not actually supported by zlib, the deflate
196  * library that WebSocket++ uses. To preserve backwards compatibility
197  * with RFC 7692 and previous versions of the library a value of 8
198  * is accepted by the library but will always be negotiated as 9.
199  */
201 /// Maximum value for client_max_window_bits as defined by RFC 7692
203 
204 namespace mode {
205 enum value {
206  /// Accept any value the remote endpoint offers
207  accept = 1,
208  /// Decline any value the remote endpoint offers. Insist on defaults.
209  decline,
210  /// Use the largest value common to both offers
211  largest,
212  /// Use the smallest value common to both offers
213  smallest
214 };
215 } // namespace mode
216 
217 template <typename config>
218 class enabled {
219 public:
220  enabled()
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 
242  ~enabled() {
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 
262  /// Initialize zlib state
263  /**
264  * Note: this should be called *after* the negotiation methods. It will use
265  * information from the negotiation to determine how to initialize the zlib
266  * data structures.
267  *
268  * @todo memory level, strategy, etc are hardcoded
269  *
270  * @param is_server True to initialize as a server, false for a client.
271  * @return A code representing the error that occurred, if any
272  */
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 
320  /// Test if this object implements the permessage-deflate specification
321  /**
322  * Because this object does implieent it, it will always return true.
323  *
324  * @return Whether or not this object implements permessage-deflate
325  */
326  bool is_implemented() const {
327  return true;
328  }
329 
330  /// Test if the extension was negotiated for this connection
331  /**
332  * Retrieves whether or not this extension is in use based on the initial
333  * handshake extension negotiations.
334  *
335  * @return Whether or not the extension is in use
336  */
337  bool is_enabled() const {
338  return m_enabled;
339  }
340 
341  /// Reset server's outgoing LZ77 sliding window for each new message
342  /**
343  * Enabling this setting will cause the server's compressor to reset the
344  * compression state (the LZ77 sliding window) for every message. This
345  * means that the compressor will not look back to patterns in previous
346  * messages to improve compression. This will reduce the compression
347  * efficiency for large messages somewhat and small messages drastically.
348  *
349  * This option may reduce server compressor memory usage and client
350  * decompressor memory usage.
351  * @todo Document to what extent memory usage will be reduced
352  *
353  * For clients, this option is dependent on server support. Enabling it
354  * via this method does not guarantee that it will be successfully
355  * negotiated, only that it will be requested.
356  *
357  * For servers, no client support is required. Enabling this option on a
358  * server will result in its use. The server will signal to clients that
359  * the option will be in use so they can optimize resource usage if they
360  * are able.
361  */
363  m_server_no_context_takeover = true;
364  }
365 
366  /// Reset client's outgoing LZ77 sliding window for each new message
367  /**
368  * Enabling this setting will cause the client's compressor to reset the
369  * compression state (the LZ77 sliding window) for every message. This
370  * means that the compressor will not look back to patterns in previous
371  * messages to improve compression. This will reduce the compression
372  * efficiency for large messages somewhat and small messages drastically.
373  *
374  * This option may reduce client compressor memory usage and server
375  * decompressor memory usage.
376  * @todo Document to what extent memory usage will be reduced
377  *
378  * This option is supported by all compliant clients and servers. Enabling
379  * it via either endpoint should be sufficient to ensure it is used.
380  */
382  m_client_no_context_takeover = true;
383  }
384 
385  /// Limit server LZ77 sliding window size
386  /**
387  * The bits setting is the base 2 logarithm of the maximum window size that
388  * the server must use to compress outgoing messages. The permitted range
389  * is 9 to 15 inclusive. 9 represents a 512 byte window and 15 a 32KiB
390  * window. The default setting is 15.
391  *
392  * Mode Options:
393  * - accept: Accept whatever the remote endpoint offers.
394  * - decline: Decline any offers to deviate from the defaults
395  * - largest: Accept largest window size acceptable to both endpoints
396  * - smallest: Accept smallest window size acceptiable to both endpoints
397  *
398  * This setting is dependent on server support. A client requesting this
399  * setting may be rejected by the server or have the exact value used
400  * adjusted by the server. A server may unilaterally set this value without
401  * client support.
402  *
403  * NOTE: The permessage-deflate spec specifies that a value of 8 is allowed.
404  * Prior to version 0.8.0 a value of 8 was also allowed by this library.
405  * zlib, the deflate compression library that WebSocket++ uses has always
406  * silently adjusted a value of 8 to 9. In recent versions of zlib (1.2.9
407  * and greater) a value of 8 is now explicitly rejected. WebSocket++ 0.8.0
408  * continues to perform the 8->9 conversion for backwards compatibility
409  * purposes but this should be considered deprecated functionality.
410  *
411  * @param bits The size to request for the outgoing window size
412  * @param mode The mode to use for negotiating this parameter
413  * @return A status code
414  */
416  if (bits < min_server_max_window_bits || bits > max_server_max_window_bits) {
417  return error::make_error_code(error::invalid_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 
431  /// Limit client LZ77 sliding window size
432  /**
433  * The bits setting is the base 2 logarithm of the window size that the
434  * client must use to compress outgoing messages. The permitted range is 9
435  * to 15 inclusive. 9 represents a 512 byte window and 15 a 32KiB window.
436  * The default setting is 15.
437  *
438  * Mode Options:
439  * - accept: Accept whatever the remote endpoint offers.
440  * - decline: Decline any offers to deviate from the defaults
441  * - largest: Accept largest window size acceptable to both endpoints
442  * - smallest: Accept smallest window size acceptiable to both endpoints
443  *
444  * This setting is dependent on client support. A client may limit its own
445  * outgoing window size unilaterally. A server may only limit the client's
446  * window size if the remote client supports that feature.
447  *
448  * NOTE: The permessage-deflate spec specifies that a value of 8 is allowed.
449  * Prior to version 0.8.0 a value of 8 was also allowed by this library.
450  * zlib, the deflate compression library that WebSocket++ uses has always
451  * silently adjusted a value of 8 to 9. In recent versions of zlib (1.2.9
452  * and greater) a value of 8 is now explicitly rejected. WebSocket++ 0.8.0
453  * continues to perform the 8->9 conversion for backwards compatibility
454  * purposes but this should be considered deprecated functionality.
455  *
456  * @param bits The size to request for the outgoing window size
457  * @param mode The mode to use for negotiating this parameter
458  * @return A status code
459  */
461  if (bits < min_client_max_window_bits || bits > max_client_max_window_bits) {
462  return error::make_error_code(error::invalid_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 
476  /// Generate extension offer
477  /**
478  * Creates an offer string to include in the Sec-WebSocket-Extensions
479  * header of outgoing client requests.
480  *
481  * @return A WebSocket extension offer string for this extension
482  */
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 
488  /// Validate extension response
489  /**
490  * Confirm that the server has negotiated settings compatible with our
491  * original offer and apply those settings to the extension state.
492  *
493  * @param response The server response attribute list to validate
494  * @return Validation error or 0 on success
495  */
497  return lib::error_code();
498  }
499 
500  /// Negotiate extension
501  /**
502  * Confirm that the client's extension negotiation offer has settings
503  * compatible with local policy. If so, generate a reply and apply those
504  * settings to the extension state.
505  *
506  * @param offer Attribute from client's offer
507  * @return Status code and value to return to remote endpoint
508  */
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 {
523  ret.first = make_error_code(error::invalid_attributes);
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 
539  /// Compress bytes
540  /**
541  * @todo: avail_in/out is 32 bit, need to fix for cases of >32 bit frames
542  * on 64 bit machines.
543  *
544  * @param [in] in String to compress
545  * @param [out] out String to append compressed bytes to
546  * @return Error or status code
547  */
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 
579  /// Decompress bytes
580  /**
581  * @param buf Byte buffer to decompress
582  * @param len Length of buf
583  * @param out String to append decompressed bytes to
584  * @return Error or status code
585  */
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) {
605  return make_error_code(error::zlib_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:
617  /// Generate negotiation response
618  /**
619  * @return Generate extension negotiation reponse string to send to client
620  */
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 
647  /// Negotiate server_no_context_takeover attribute
648  /**
649  * @param [in] value The value of the attribute from the offer
650  * @param [out] ec A reference to the error code to return errors via
651  */
652  void negotiate_server_no_context_takeover(std::string const & value,
653  lib::error_code & ec)
654  {
655  if (!value.empty()) {
656  ec = make_error_code(error::invalid_attribute_value);
657  return;
658  }
659 
660  m_server_no_context_takeover = true;
661  }
662 
663  /// Negotiate client_no_context_takeover attribute
664  /**
665  * @param [in] value The value of the attribute from the offer
666  * @param [out] ec A reference to the error code to return errors via
667  */
668  void negotiate_client_no_context_takeover(std::string const & value,
669  lib::error_code & ec)
670  {
671  if (!value.empty()) {
672  ec = make_error_code(error::invalid_attribute_value);
673  return;
674  }
675 
676  m_client_no_context_takeover = true;
677  }
678 
679  /// Negotiate server_max_window_bits attribute
680  /**
681  * When this method starts, m_server_max_window_bits will contain the server's
682  * preferred value and m_server_max_window_bits_mode will contain the mode the
683  * server wants to use to for negotiation. `value` contains the value the
684  * client requested that we use.
685  *
686  * options:
687  * - decline (ignore value, offer our default instead)
688  * - accept (use the value requested by the client)
689  * - largest (use largest value acceptable to both)
690  * - smallest (use smallest possible value)
691  *
692  * NOTE: As a value of 8 is no longer explicitly supported by zlib but might
693  * be requested for negotiation by an older client/server, if the result of
694  * the negotiation would be to send a value of 8, a value of 9 is offered
695  * instead. This ensures that WebSocket++ will only ever negotiate connections
696  * with compression settings explicitly supported by zlib.
697  *
698  * @param [in] value The value of the attribute from the offer
699  * @param [out] ec A reference to the error code to return errors via
700  */
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) {
707  ec = make_error_code(error::invalid_attribute_value);
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:
726  ec = make_error_code(error::invalid_mode);
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 
736  /// Negotiate client_max_window_bits attribute
737  /**
738  * When this method starts, m_client_max_window_bits and m_c2s_max_window_mode
739  * will contain the server's preferred values for window size and
740  * negotiation mode.
741  *
742  * options:
743  * - decline (ignore value, offer our default instead)
744  * - accept (use the value requested by the client)
745  * - largest (use largest value acceptable to both)
746  * - smallest (use smallest possible value)
747  *
748  * NOTE: As a value of 8 is no longer explicitly supported by zlib but might
749  * be requested for negotiation by an older client/server, if the result of
750  * the negotiation would be to send a value of 8, a value of 9 is offered
751  * instead. This ensures that WebSocket++ will only ever negotiate connections
752  * with compression settings explicitly supported by zlib.
753  *
754  * @param [in] value The value of the attribute from the offer
755  * @param [out] ec A reference to the error code to return errors via
756  */
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()) {
763  bits = default_client_max_window_bits;
764  } else if (bits < min_client_max_window_bits ||
765  bits > max_client_max_window_bits)
766  {
767  ec = make_error_code(error::invalid_attribute_value);
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:
786  ec = make_error_code(error::invalid_mode);
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
websocketpp::extensions::permessage_deflate::enabled::enable_client_no_context_takeover
void enable_client_no_context_takeover()
Reset client's outgoing LZ77 sliding window for each new message.
Definition: enabled.hpp:381
websocketpp::extensions::permessage_deflate::enabled::set_server_max_window_bits
lib::error_code set_server_max_window_bits(uint8_t bits, mode::value mode)
Limit server LZ77 sliding window size.
Definition: enabled.hpp:415
websocketpp::extensions::permessage_deflate::error::invalid_attributes
@ invalid_attributes
Invalid extension attributes.
Definition: enabled.hpp:97
websocketpp::extensions::permessage_deflate::min_server_max_window_bits
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
websocketpp::extensions::permessage_deflate::error::make_error_code
lib::error_code make_error_code(error::value e)
Create an error code in the permessage-deflate category.
Definition: enabled.hpp:158
websocketpp::extensions::permessage_deflate::min_client_max_window_bits
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
websocketpp::extensions::permessage_deflate::max_server_max_window_bits
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
websocketpp::extensions::permessage_deflate::enabled
Definition: enabled.hpp:218
websocketpp::extensions::permessage_deflate::enabled::init
lib::error_code init(bool is_server)
Initialize zlib state.
Definition: enabled.hpp:273
websocketpp::extensions::permessage_deflate::default_client_max_window_bits
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
websocketpp::extensions::permessage_deflate::error::zlib_error
@ zlib_error
ZLib Error.
Definition: enabled.hpp:112
websocketpp::extensions::permessage_deflate::error::value
value
Definition: enabled.hpp:92
websocketpp::extensions::permessage_deflate::enabled::set_client_max_window_bits
lib::error_code set_client_max_window_bits(uint8_t bits, mode::value mode)
Limit client LZ77 sliding window size.
Definition: enabled.hpp:460
websocketpp::extensions
Definition: extension.hpp:50
websocketpp::extensions::permessage_deflate::enabled::is_implemented
bool is_implemented() const
Test if this object implements the permessage-deflate specification.
Definition: enabled.hpp:326
websocketpp::versions_supported
static std::vector< int > const versions_supported(helper, helper+4)
Container that stores the list of protocol versions supported.
websocketpp::extensions::permessage_deflate::enabled::validate_offer
lib::error_code validate_offer(http::attribute_list const &)
Validate extension response.
Definition: enabled.hpp:496
websocketpp::extensions::permessage_deflate::enabled::decompress
lib::error_code decompress(uint8_t const *buf, size_t len, std::string &out)
Decompress bytes.
Definition: enabled.hpp:586
websocketpp::extensions::permessage_deflate::default_server_max_window_bits
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
websocketpp::extensions::permessage_deflate::max_client_max_window_bits
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
websocketpp::extensions::permessage_deflate::error::invalid_mode
@ invalid_mode
Invalid megotiation mode.
Definition: enabled.hpp:103
websocketpp::extensions::permessage_deflate::error::invalid_attribute_value
@ invalid_attribute_value
Invalid extension attribute value.
Definition: enabled.hpp:100
websocketpp::extensions::permessage_deflate
Implementation of RFC 7692, the permessage-deflate WebSocket extension.
Definition: disabled.hpp:44
websocketpp::extensions::permessage_deflate::enabled::is_enabled
bool is_enabled() const
Test if the extension was negotiated for this connection.
Definition: enabled.hpp:337
websocketpp::extensions::permessage_deflate::enabled::negotiate
err_str_pair negotiate(http::attribute_list const &offer)
Negotiate extension.
Definition: enabled.hpp:509
websocketpp::extensions::permessage_deflate::enabled::generate_offer
std::string generate_offer() const
Generate extension offer.
Definition: enabled.hpp:483
websocketpp::extensions::permessage_deflate::error::invalid_max_window_bits
@ invalid_max_window_bits
Invalid value for max_window_bits.
Definition: enabled.hpp:109
websocketpp::extensions::permessage_deflate::enabled::enable_server_no_context_takeover
void enable_server_no_context_takeover()
Reset server's outgoing LZ77 sliding window for each new message.
Definition: enabled.hpp:362
websocketpp::extensions::permessage_deflate::enabled::compress
lib::error_code compress(std::string const &in, std::string &out)
Compress bytes.
Definition: enabled.hpp:548
websocketpp::extensions::permessage_deflate::error::uninitialized
@ uninitialized
Uninitialized.
Definition: enabled.hpp:115
websocketpp::extensions::permessage_deflate::error
Permessage deflate error values.
Definition: enabled.hpp:91
websocketpp::extensions::permessage_deflate::error::unsupported_attributes
@ unsupported_attributes
Unsupported extension attributes.
Definition: enabled.hpp:106
websocketpp::extensions::permessage_deflate::error::get_category
lib::error_category const & get_category()
Get a reference to a static copy of the permessage-deflate error category.
Definition: enabled.hpp:152
websocketpp::extensions::permessage_deflate::error::general
@ general
Catch all.
Definition: enabled.hpp:94