WebSocket++ 0.8.4
C++ websocket client/server library
Loading...
Searching...
No Matches
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#include <websocketpp/extensions/permessage_deflate/detail.hpp>
41
42#include "zlib.h"
43
44#include <algorithm>
45#include <string>
46#include <vector>
47
48namespace websocketpp {
49namespace extensions {
50
51/// Implementation of RFC 7692, the permessage-deflate WebSocket extension
52/**
53 * ### permessage-deflate interface
54 *
55 * **init**\n
56 * `lib::error_code init(bool is_server)`\n
57 * Performs initialization
58 *
59 * **is_implimented**\n
60 * `bool is_implimented()`\n
61 * Returns whether or not the object impliments the extension or not
62 *
63 * **is_enabled**\n
64 * `bool is_enabled()`\n
65 * Returns whether or not the extension was negotiated for the current
66 * connection
67 *
68 * **generate_offer**\n
69 * `std::string generate_offer() const`\n
70 * Create an extension offer string based on local policy
71 *
72 * **validate_response**\n
73 * `lib::error_code validate_response(http::attribute_list const & response)`\n
74 * Negotiate the parameters of extension use
75 *
76 * **negotiate**\n
77 * `err_str_pair negotiate(http::attribute_list const & attributes)`\n
78 * Negotiate the parameters of extension use
79 *
80 * **compress**\n
81 * `lib::error_code compress(std::string const & in, std::string & out)`\n
82 * Compress the bytes in `in` and append them to `out`
83 *
84 * **decompress**\n
85 * `lib::error_code decompress(uint8_t const * buf, size_t len, std::string &
86 * out)`\n
87 * Decompress `len` bytes from `buf` and append them to string `out`
88 */
89namespace permessage_deflate {
90
91/// Permessage deflate error values
92namespace error {
93enum value {
94 /// Catch all
96
97 /// Invalid extension attributes
99
100 /// Invalid extension attribute value
102
103 /// Invalid megotiation mode
105
106 /// Unsupported extension attributes
108
109 /// Invalid value for max_window_bits
111
112 /// Decompressed message exceeded the hard size limit
114
115 /// ZLib Error
117
118 /// Uninitialized
120};
121
122/// Permessage-deflate error category
123class category : public lib::error_category {
124public:
125 category() {}
126
127 char const * name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ {
128 return "websocketpp.extension.permessage-deflate";
129 }
130
131 std::string message(int value) const {
132 switch(value) {
133 case general:
134 return "Generic permessage-compress error";
136 return "Invalid extension attributes";
138 return "Invalid extension attribute value";
139 case invalid_mode:
140 return "Invalid permessage-deflate negotiation mode";
142 return "Unsupported extension attributes";
144 return "Invalid value for max_window_bits";
145 case message_too_big:
146 return "Message too big";
147 case zlib_error:
148 return "A zlib function returned an error";
149 case uninitialized:
150 return "Deflate extension must be initialized before use";
151 default:
152 return "Unknown permessage-compress error";
153 }
154 }
155};
156
157/// Get a reference to a static copy of the permessage-deflate error category
158inline lib::error_category const & get_category() {
159 static category instance;
160 return instance;
161}
162
163/// Create an error code in the permessage-deflate category
164inline lib::error_code make_error_code(error::value e) {
165 return lib::error_code(static_cast<int>(e), get_category());
166}
167
168} // namespace error
169} // namespace permessage_deflate
170} // namespace extensions
171} // namespace websocketpp
172
174template<> struct is_error_code_enum
176{
177 static bool const value = true;
178};
180namespace websocketpp {
181namespace extensions {
182namespace permessage_deflate {
183
184/// Default value for server_max_window_bits as defined by RFC 7692
185static uint8_t const default_server_max_window_bits = 15;
186/// Minimum value for server_max_window_bits as defined by RFC 7692
187/**
188 * NOTE: A value of 8 is not actually supported by zlib, the deflate
189 * library that WebSocket++ uses. To preserve backwards compatibility
190 * with RFC 7692 and previous versions of the library a value of 8
191 * is accepted by the library but will always be negotiated as 9.
192 */
193static uint8_t const min_server_max_window_bits = 8;
194/// Maximum value for server_max_window_bits as defined by RFC 7692
195static uint8_t const max_server_max_window_bits = 15;
196
197/// Default value for client_max_window_bits as defined by RFC 7692
198static uint8_t const default_client_max_window_bits = 15;
199/// Minimum value for client_max_window_bits as defined by RFC 7692
200/**
201 * NOTE: A value of 8 is not actually supported by zlib, the deflate
202 * library that WebSocket++ uses. To preserve backwards compatibility
203 * with RFC 7692 and previous versions of the library a value of 8
204 * is accepted by the library but will always be negotiated as 9.
205 */
206static uint8_t const min_client_max_window_bits = 8;
207/// Maximum value for client_max_window_bits as defined by RFC 7692
208static uint8_t const max_client_max_window_bits = 15;
209
210/// Default maximum decompressed message size enforced by the extension
211/**
212 * Used when the extension's configuration does not specify its own
213 * max_message_size value.
214 */
215static size_t const default_max_message_size = 32000000;
216
217namespace mode {
218enum value {
219 /// Accept any value the remote endpoint offers
220 accept = 1,
221 /// Decline any value the remote endpoint offers. Insist on defaults.
222 decline,
223 /// Use the largest value common to both offers
224 largest,
225 /// Use the smallest value common to both offers
226 smallest
227};
228} // namespace mode
229
230template <typename config>
231class enabled {
232public:
233 enabled()
234 : m_enabled(false)
235 , m_server_no_context_takeover(false)
236 , m_client_no_context_takeover(false)
237 , m_server_max_window_bits(15)
238 , m_client_max_window_bits(15)
239 , m_server_max_window_bits_mode(mode::accept)
240 , m_client_max_window_bits_mode(mode::accept)
241 , m_initialized(false)
242 , m_compress_buffer_size(8192)
243 , m_max_message_size(detail::max_message_size_or_default<
244 config, default_max_message_size>::get())
245 {
246 m_dstate.zalloc = Z_NULL;
247 m_dstate.zfree = Z_NULL;
248 m_dstate.opaque = Z_NULL;
249
250 m_istate.zalloc = Z_NULL;
251 m_istate.zfree = Z_NULL;
252 m_istate.opaque = Z_NULL;
253 m_istate.avail_in = 0;
254 m_istate.next_in = Z_NULL;
255 }
256
257 ~enabled() {
258 if (!m_initialized) {
259 return;
260 }
261
262 int ret = deflateEnd(&m_dstate);
263
264 if (ret != Z_OK) {
265 //std::cout << "error cleaning up zlib compression state"
266 // << std::endl;
267 }
268
269 ret = inflateEnd(&m_istate);
270
271 if (ret != Z_OK) {
272 //std::cout << "error cleaning up zlib decompression state"
273 // << std::endl;
274 }
275 }
276
277 /// Initialize zlib state
278 /**
279 * Note: this should be called *after* the negotiation methods. It will use
280 * information from the negotiation to determine how to initialize the zlib
281 * data structures.
282 *
283 * @todo memory level, strategy, etc are hardcoded
284 *
285 * @param is_server True to initialize as a server, false for a client.
286 * @return A code representing the error that occurred, if any
287 */
288 lib::error_code init(bool is_server) {
289 uint8_t deflate_bits;
290 uint8_t inflate_bits;
291
292 if (is_server) {
293 deflate_bits = m_server_max_window_bits;
294 inflate_bits = m_client_max_window_bits;
295 } else {
296 deflate_bits = m_client_max_window_bits;
297 inflate_bits = m_server_max_window_bits;
298 }
299
300 int ret = deflateInit2(
301 &m_dstate,
302 Z_DEFAULT_COMPRESSION,
303 Z_DEFLATED,
304 -1*deflate_bits,
305 4, // memory level 1-9
306 Z_DEFAULT_STRATEGY
307 );
308
309 if (ret != Z_OK) {
311 }
312
313 ret = inflateInit2(
314 &m_istate,
315 -1*inflate_bits
316 );
317
318 if (ret != Z_OK) {
320 }
321
322 m_compress_buffer.reset(new unsigned char[m_compress_buffer_size]);
323 m_decompress_buffer.reset(new unsigned char[m_compress_buffer_size]);
324 if ((m_server_no_context_takeover && is_server) ||
325 (m_client_no_context_takeover && !is_server))
326 {
327 m_flush = Z_FULL_FLUSH;
328 } else {
329 m_flush = Z_SYNC_FLUSH;
330 }
331 m_initialized = true;
332 return lib::error_code();
333 }
334
335 /// Test if this object implements the permessage-deflate specification
336 /**
337 * Because this object does implieent it, it will always return true.
338 *
339 * @return Whether or not this object implements permessage-deflate
340 */
341 bool is_implemented() const {
342 return true;
343 }
344
345 /// Test if the extension was negotiated for this connection
346 /**
347 * Retrieves whether or not this extension is in use based on the initial
348 * handshake extension negotiations.
349 *
350 * @return Whether or not the extension is in use
351 */
352 bool is_enabled() const {
353 return m_enabled;
354 }
355
356 /// Reset server's outgoing LZ77 sliding window for each new message
357 /**
358 * Enabling this setting will cause the server's compressor to reset the
359 * compression state (the LZ77 sliding window) for every message. This
360 * means that the compressor will not look back to patterns in previous
361 * messages to improve compression. This will reduce the compression
362 * efficiency for large messages somewhat and small messages drastically.
363 *
364 * This option may reduce server compressor memory usage and client
365 * decompressor memory usage.
366 * @todo Document to what extent memory usage will be reduced
367 *
368 * For clients, this option is dependent on server support. Enabling it
369 * via this method does not guarantee that it will be successfully
370 * negotiated, only that it will be requested.
371 *
372 * For servers, no client support is required. Enabling this option on a
373 * server will result in its use. The server will signal to clients that
374 * the option will be in use so they can optimize resource usage if they
375 * are able.
376 */
378 m_server_no_context_takeover = true;
379 }
380
381 /// Reset client's outgoing LZ77 sliding window for each new message
382 /**
383 * Enabling this setting will cause the client's compressor to reset the
384 * compression state (the LZ77 sliding window) for every message. This
385 * means that the compressor will not look back to patterns in previous
386 * messages to improve compression. This will reduce the compression
387 * efficiency for large messages somewhat and small messages drastically.
388 *
389 * This option may reduce client compressor memory usage and server
390 * decompressor memory usage.
391 * @todo Document to what extent memory usage will be reduced
392 *
393 * This option is supported by all compliant clients and servers. Enabling
394 * it via either endpoint should be sufficient to ensure it is used.
395 */
397 m_client_no_context_takeover = true;
398 }
399
400 /// Limit server LZ77 sliding window size
401 /**
402 * The bits setting is the base 2 logarithm of the maximum window size that
403 * the server must use to compress outgoing messages. The permitted range
404 * is 9 to 15 inclusive. 9 represents a 512 byte window and 15 a 32KiB
405 * window. The default setting is 15.
406 *
407 * Mode Options:
408 * - accept: Accept whatever the remote endpoint offers.
409 * - decline: Decline any offers to deviate from the defaults
410 * - largest: Accept largest window size acceptable to both endpoints
411 * - smallest: Accept smallest window size acceptiable to both endpoints
412 *
413 * This setting is dependent on server support. A client requesting this
414 * setting may be rejected by the server or have the exact value used
415 * adjusted by the server. A server may unilaterally set this value without
416 * client support.
417 *
418 * NOTE: The permessage-deflate spec specifies that a value of 8 is allowed.
419 * Prior to version 0.8.0 a value of 8 was also allowed by this library.
420 * zlib, the deflate compression library that WebSocket++ uses has always
421 * silently adjusted a value of 8 to 9. In recent versions of zlib (1.2.9
422 * and greater) a value of 8 is now explicitly rejected. WebSocket++ 0.8.0
423 * continues to perform the 8->9 conversion for backwards compatibility
424 * purposes but this should be considered deprecated functionality.
425 *
426 * @param bits The size to request for the outgoing window size
427 * @param mode The mode to use for negotiating this parameter
428 * @return A status code
429 */
430 lib::error_code set_server_max_window_bits(uint8_t bits, mode::value mode) {
433 }
434
435 // See note in doc comment above about what is happening here
436 if (bits == 8) {
437 bits = 9;
438 }
439
440 m_server_max_window_bits = bits;
441 m_server_max_window_bits_mode = mode;
442
443 return lib::error_code();
444 }
445
446 /// Limit client LZ77 sliding window size
447 /**
448 * The bits setting is the base 2 logarithm of the window size that the
449 * client must use to compress outgoing messages. The permitted range is 9
450 * to 15 inclusive. 9 represents a 512 byte window and 15 a 32KiB window.
451 * The default setting is 15.
452 *
453 * Mode Options:
454 * - accept: Accept whatever the remote endpoint offers.
455 * - decline: Decline any offers to deviate from the defaults
456 * - largest: Accept largest window size acceptable to both endpoints
457 * - smallest: Accept smallest window size acceptiable to both endpoints
458 *
459 * This setting is dependent on client support. A client may limit its own
460 * outgoing window size unilaterally. A server may only limit the client's
461 * window size if the remote client supports that feature.
462 *
463 * NOTE: The permessage-deflate spec specifies that a value of 8 is allowed.
464 * Prior to version 0.8.0 a value of 8 was also allowed by this library.
465 * zlib, the deflate compression library that WebSocket++ uses has always
466 * silently adjusted a value of 8 to 9. In recent versions of zlib (1.2.9
467 * and greater) a value of 8 is now explicitly rejected. WebSocket++ 0.8.0
468 * continues to perform the 8->9 conversion for backwards compatibility
469 * purposes but this should be considered deprecated functionality.
470 *
471 * @param bits The size to request for the outgoing window size
472 * @param mode The mode to use for negotiating this parameter
473 * @return A status code
474 */
475 lib::error_code set_client_max_window_bits(uint8_t bits, mode::value mode) {
478 }
479
480 // See note in doc comment above about what is happening here
481 if (bits == 8) {
482 bits = 9;
483 }
484
485 m_client_max_window_bits = bits;
486 m_client_max_window_bits_mode = mode;
487
488 return lib::error_code();
489 }
490
491 /// Generate extension offer
492 /**
493 * Creates an offer string to include in the Sec-WebSocket-Extensions
494 * header of outgoing client requests.
495 *
496 * @return A WebSocket extension offer string for this extension
497 */
498 std::string generate_offer() const {
499 // TODO: this should be dynamically generated based on user settings
500 return "permessage-deflate; client_no_context_takeover; client_max_window_bits";
501 }
502
503 /// Set maximum decompressed message size
504 /**
505 * Sets the maximum size, in bytes, that a message may reach after
506 * decompression. If a compressed message would inflate beyond this
507 * limit, decompression is aborted and `error::message_too_big` is
508 * returned. This bounds the worst-case memory cost of a hostile or
509 * malformed compressed payload.
510 *
511 * The initial value is taken from `Config::max_message_size` if the
512 * extension's configuration defines it, otherwise from
513 * `default_max_message_size`. Changes made via this method override
514 * that initial value for the lifetime of the extension instance.
515 *
516 * @since 0.8.3
517 *
518 * @param value The new per-message decompression limit, in bytes
519 */
520 void set_max_message_size(size_t value) {
521 m_max_message_size = value;
522 }
523
524 /// Validate extension response
525 /**
526 * Confirm that the server has negotiated settings compatible with our
527 * original offer and apply those settings to the extension state.
528 *
529 * @param response The server response attribute list to validate
530 * @return Validation error or 0 on success
531 */
532 lib::error_code validate_offer(http::attribute_list const &) {
533 return lib::error_code();
534 }
535
536 /// Negotiate extension
537 /**
538 * Confirm that the client's extension negotiation offer has settings
539 * compatible with local policy. If so, generate a reply and apply those
540 * settings to the extension state.
541 *
542 * @param offer Attribute from client's offer
543 * @return Status code and value to return to remote endpoint
544 */
545 err_str_pair negotiate(http::attribute_list const & offer) {
546 err_str_pair ret;
547
548 http::attribute_list::const_iterator it;
549 for (it = offer.begin(); it != offer.end(); ++it) {
550 if (it->first == "server_no_context_takeover") {
551 negotiate_server_no_context_takeover(it->second,ret.first);
552 } else if (it->first == "client_no_context_takeover") {
553 negotiate_client_no_context_takeover(it->second,ret.first);
554 } else if (it->first == "server_max_window_bits") {
555 negotiate_server_max_window_bits(it->second,ret.first);
556 } else if (it->first == "client_max_window_bits") {
557 negotiate_client_max_window_bits(it->second,ret.first);
558 } else {
560 }
561
562 if (ret.first) {
563 break;
564 }
565 }
566
567 if (ret.first == lib::error_code()) {
568 m_enabled = true;
569 ret.second = generate_response();
570 }
571
572 return ret;
573 }
574
575 /// Compress bytes
576 /**
577 * @param [in] in String to compress
578 * @param [out] out String to append compressed bytes to
579 * @return Error or status code
580 */
581 lib::error_code compress(std::string const & in, std::string & out) {
582 if (!m_initialized) {
584 }
585
586 size_t output;
587
588 if (in.empty()) {
589 uint8_t buf[6] = {0x02, 0x00, 0x00, 0x00, 0xff, 0xff};
590 out.append((char *)(buf),6);
591 return lib::error_code();
592 }
593
594 // zlib's avail_in is 32 bits, so give it the input in pieces, and
595 // flush only after the last piece
596 size_t input_remaining = in.size();
597 m_dstate.avail_in = 0;
598 m_dstate.next_in = (unsigned char *)(const_cast<char *>(in.data()));
599
600 do {
601 if (m_dstate.avail_in == 0 && input_remaining > 0) {
602 size_t const input = (std::min)(m_compress_buffer_size, input_remaining);
603 m_dstate.avail_in = static_cast<uInt>(input);
604 input_remaining -= input;
605 }
606
607 // Output to local buffer
608 m_dstate.avail_out = static_cast<uInt>(m_compress_buffer_size);
609 m_dstate.next_out = m_compress_buffer.get();
610
611 deflate(&m_dstate, input_remaining == 0 ? m_flush : Z_NO_FLUSH);
612
613 output = m_compress_buffer_size - m_dstate.avail_out;
614
615 out.append((char *)(m_compress_buffer.get()),output);
616 } while (m_dstate.avail_out == 0 || (m_dstate.avail_in == 0 && input_remaining > 0));
617
618 return lib::error_code();
619 }
620
621 /// Decompress bytes
622 /**
623 * Decompresses up to `len` bytes from `buf` and appends the result to
624 * `out`. Decompression aborts with `error::message_too_big` if the
625 * total size of `out` would exceed the configured per-message limit
626 * (see `set_max_message_size`); this bounds the worst-case memory
627 * cost of a hostile or malformed compressed payload.
628 *
629 * @note If this function returns `error::message_too_big`, the
630 * extension's internal inflate state is left mid-stream and is not
631 * safe to reuse. The caller must discard this instance or call
632 * `init()` again before invoking `decompress()` further. In normal
633 * use the connection is torn down on this error so reuse does not
634 * arise.
635 *
636 * @param buf Byte buffer to decompress
637 * @param len Length of buf
638 * @param out String to append decompressed bytes to
639 * @return Error or status code
640 */
641 lib::error_code decompress(uint8_t const * buf, size_t len, std::string &
642 out)
643 {
644 if (!m_initialized) {
646 }
647
648 int ret;
649
650 // zlib's avail_in is 32 bits, so give it the input in pieces
651 size_t input_remaining = len;
652 m_istate.avail_in = 0;
653 m_istate.next_in = const_cast<unsigned char *>(buf);
654
655 do {
656 if (m_istate.avail_in == 0 && input_remaining > 0) {
657 size_t const input = (std::min)(m_compress_buffer_size, input_remaining);
658 m_istate.avail_in = static_cast<uInt>(input);
659 input_remaining -= input;
660 }
661
662 size_t const remaining = out.size() < m_max_message_size
663 ? m_max_message_size - out.size()
664 : 0;
665 // Keep one extra byte of scratch space so we can detect overflow
666 // before appending past the configured per-message limit.
667 size_t const output_limit = (std::min)(m_compress_buffer_size, remaining + size_t(1));
668
669 m_istate.avail_out = static_cast<uInt>(output_limit);
670 m_istate.next_out = m_decompress_buffer.get();
671
672 ret = inflate(&m_istate, Z_SYNC_FLUSH);
673
674 if (ret == Z_NEED_DICT || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR) {
676 }
677
678 size_t const output = output_limit - static_cast<size_t>(m_istate.avail_out);
679
680 if (output > remaining) {
681 if (remaining > 0) {
682 out.append(
683 reinterpret_cast<char *>(m_decompress_buffer.get()),
684 remaining
685 );
686 }
687
689 }
690
691 out.append(
692 reinterpret_cast<char *>(m_decompress_buffer.get()),
693 output
694 );
695 } while (m_istate.avail_out == 0 || (m_istate.avail_in == 0 && input_remaining > 0));
696
697 return lib::error_code();
698 }
699
700 static bool is_message_too_big(lib::error_code const & ec) {
702 }
703
704private:
705 /// Generate negotiation response
706 /**
707 * @return Generate extension negotiation reponse string to send to client
708 */
709 std::string generate_response() {
710 std::string ret = "permessage-deflate";
711
712 if (m_server_no_context_takeover) {
713 ret += "; server_no_context_takeover";
714 }
715
716 if (m_client_no_context_takeover) {
717 ret += "; client_no_context_takeover";
718 }
719
720 if (m_server_max_window_bits < default_server_max_window_bits) {
721 std::stringstream s;
722 s << int(m_server_max_window_bits);
723 ret += "; server_max_window_bits="+s.str();
724 }
725
726 if (m_client_max_window_bits < default_client_max_window_bits) {
727 std::stringstream s;
728 s << int(m_client_max_window_bits);
729 ret += "; client_max_window_bits="+s.str();
730 }
731
732 return ret;
733 }
734
735 /// Negotiate server_no_context_takeover attribute
736 /**
737 * @param [in] value The value of the attribute from the offer
738 * @param [out] ec A reference to the error code to return errors via
739 */
740 void negotiate_server_no_context_takeover(std::string const & value,
741 lib::error_code & ec)
742 {
743 if (!value.empty()) {
745 return;
746 }
747
748 m_server_no_context_takeover = true;
749 }
750
751 /// Negotiate client_no_context_takeover attribute
752 /**
753 * @param [in] value The value of the attribute from the offer
754 * @param [out] ec A reference to the error code to return errors via
755 */
756 void negotiate_client_no_context_takeover(std::string const & value,
757 lib::error_code & ec)
758 {
759 if (!value.empty()) {
761 return;
762 }
763
764 m_client_no_context_takeover = true;
765 }
766
767 /// Negotiate server_max_window_bits attribute
768 /**
769 * When this method starts, m_server_max_window_bits will contain the server's
770 * preferred value and m_server_max_window_bits_mode will contain the mode the
771 * server wants to use to for negotiation. `value` contains the value the
772 * client requested that we use.
773 *
774 * options:
775 * - decline (ignore value, offer our default instead)
776 * - accept (use the value requested by the client)
777 * - largest (use largest value acceptable to both)
778 * - smallest (use smallest possible value)
779 *
780 * NOTE: As a value of 8 is no longer explicitly supported by zlib but might
781 * be requested for negotiation by an older client/server, if the result of
782 * the negotiation would be to send a value of 8, a value of 9 is offered
783 * instead. This ensures that WebSocket++ will only ever negotiate connections
784 * with compression settings explicitly supported by zlib.
785 *
786 * @param [in] value The value of the attribute from the offer
787 * @param [out] ec A reference to the error code to return errors via
788 */
789 void negotiate_server_max_window_bits(std::string const & value,
790 lib::error_code & ec)
791 {
792 uint8_t bits = uint8_t(atoi(value.c_str()));
793
796 m_server_max_window_bits = default_server_max_window_bits;
797 return;
798 }
799
800 switch (m_server_max_window_bits_mode) {
801 case mode::decline:
802 m_server_max_window_bits = default_server_max_window_bits;
803 break;
804 case mode::accept:
805 m_server_max_window_bits = bits;
806 break;
807 case mode::largest:
808 m_server_max_window_bits = std::min(bits,m_server_max_window_bits);
809 break;
810 case mode::smallest:
811 m_server_max_window_bits = min_server_max_window_bits;
812 break;
813 default:
815 m_server_max_window_bits = default_server_max_window_bits;
816 }
817
818 // See note in doc comment
819 if (m_server_max_window_bits == 8) {
820 m_server_max_window_bits = 9;
821 }
822 }
823
824 /// Negotiate client_max_window_bits attribute
825 /**
826 * When this method starts, m_client_max_window_bits and m_c2s_max_window_mode
827 * will contain the server's preferred values for window size and
828 * negotiation mode.
829 *
830 * options:
831 * - decline (ignore value, offer our default instead)
832 * - accept (use the value requested by the client)
833 * - largest (use largest value acceptable to both)
834 * - smallest (use smallest possible value)
835 *
836 * NOTE: As a value of 8 is no longer explicitly supported by zlib but might
837 * be requested for negotiation by an older client/server, if the result of
838 * the negotiation would be to send a value of 8, a value of 9 is offered
839 * instead. This ensures that WebSocket++ will only ever negotiate connections
840 * with compression settings explicitly supported by zlib.
841 *
842 * @param [in] value The value of the attribute from the offer
843 * @param [out] ec A reference to the error code to return errors via
844 */
845 void negotiate_client_max_window_bits(std::string const & value,
846 lib::error_code & ec)
847 {
848 uint8_t bits = uint8_t(atoi(value.c_str()));
849
850 if (value.empty()) {
852 } else if (bits < min_client_max_window_bits ||
854 {
856 m_client_max_window_bits = default_client_max_window_bits;
857 return;
858 }
859
860 switch (m_client_max_window_bits_mode) {
861 case mode::decline:
862 m_client_max_window_bits = default_client_max_window_bits;
863 break;
864 case mode::accept:
865 m_client_max_window_bits = bits;
866 break;
867 case mode::largest:
868 m_client_max_window_bits = std::min(bits,m_client_max_window_bits);
869 break;
870 case mode::smallest:
871 m_client_max_window_bits = min_client_max_window_bits;
872 break;
873 default:
875 m_client_max_window_bits = default_client_max_window_bits;
876 }
877
878 // See note in doc comment
879 if (m_client_max_window_bits == 8) {
880 m_client_max_window_bits = 9;
881 }
882 }
883
884 bool m_enabled;
885 bool m_server_no_context_takeover;
886 bool m_client_no_context_takeover;
887 uint8_t m_server_max_window_bits;
888 uint8_t m_client_max_window_bits;
889 mode::value m_server_max_window_bits_mode;
890 mode::value m_client_max_window_bits_mode;
891
892 bool m_initialized;
893 int m_flush;
894 size_t m_compress_buffer_size;
895 size_t m_max_message_size;
896 lib::unique_ptr_uchar_array m_compress_buffer;
897 lib::unique_ptr_uchar_array m_decompress_buffer;
898 z_stream m_dstate;
899 z_stream m_istate;
900};
901
902} // namespace permessage_deflate
903} // namespace extensions
904} // namespace websocketpp
905
906#endif // WEBSOCKETPP_PROCESSOR_EXTENSION_PERMESSAGEDEFLATE_HPP
lib::error_code init(bool is_server)
Initialize zlib state.
Definition enabled.hpp:288
void enable_client_no_context_takeover()
Reset client's outgoing LZ77 sliding window for each new message.
Definition enabled.hpp:396
lib::error_code compress(std::string const &in, std::string &out)
Compress bytes.
Definition enabled.hpp:581
bool is_enabled() const
Test if the extension was negotiated for this connection.
Definition enabled.hpp:352
lib::error_code decompress(uint8_t const *buf, size_t len, std::string &out)
Decompress bytes.
Definition enabled.hpp:641
lib::error_code set_client_max_window_bits(uint8_t bits, mode::value mode)
Limit client LZ77 sliding window size.
Definition enabled.hpp:475
void set_max_message_size(size_t value)
Set maximum decompressed message size.
Definition enabled.hpp:520
bool is_implemented() const
Test if this object implements the permessage-deflate specification.
Definition enabled.hpp:341
void enable_server_no_context_takeover()
Reset server's outgoing LZ77 sliding window for each new message.
Definition enabled.hpp:377
err_str_pair negotiate(http::attribute_list const &offer)
Negotiate extension.
Definition enabled.hpp:545
lib::error_code set_server_max_window_bits(uint8_t bits, mode::value mode)
Limit server LZ77 sliding window size.
Definition enabled.hpp:430
lib::error_code validate_offer(http::attribute_list const &)
Validate extension response.
Definition enabled.hpp:532
std::string generate_offer() const
Generate extension offer.
Definition enabled.hpp:498
#define _WEBSOCKETPP_NOEXCEPT_TOKEN_
Definition cpp11.hpp:115
Permessage deflate error values.
Definition enabled.hpp:92
@ unsupported_attributes
Unsupported extension attributes.
Definition enabled.hpp:107
@ invalid_max_window_bits
Invalid value for max_window_bits.
Definition enabled.hpp:110
@ message_too_big
Decompressed message exceeded the hard size limit.
Definition enabled.hpp:113
@ invalid_attribute_value
Invalid extension attribute value.
Definition enabled.hpp:101
@ invalid_attributes
Invalid extension attributes.
Definition enabled.hpp:98
lib::error_category const & get_category()
Get a reference to a static copy of the permessage-deflate error category.
Definition enabled.hpp:158
lib::error_code make_error_code(error::value e)
Create an error code in the permessage-deflate category.
Definition enabled.hpp:164
Implementation of RFC 7692, the permessage-deflate WebSocket extension.
Definition disabled.hpp:44
static size_t const default_max_message_size
Default maximum decompressed message size enforced by the extension.
Definition enabled.hpp:215
static uint8_t const default_client_max_window_bits
Default value for client_max_window_bits as defined by RFC 7692.
Definition enabled.hpp:198
static uint8_t const default_server_max_window_bits
Default value for server_max_window_bits as defined by RFC 7692.
Definition enabled.hpp:185
static uint8_t const max_client_max_window_bits
Maximum value for client_max_window_bits as defined by RFC 7692.
Definition enabled.hpp:208
static uint8_t const min_client_max_window_bits
Minimum value for client_max_window_bits as defined by RFC 7692.
Definition enabled.hpp:206
static uint8_t const min_server_max_window_bits
Minimum value for server_max_window_bits as defined by RFC 7692.
Definition enabled.hpp:193
static uint8_t const max_server_max_window_bits
Maximum value for server_max_window_bits as defined by RFC 7692.
Definition enabled.hpp:195
Namespace for the WebSocket++ project.
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_ERROR_CODE_ENUM_NS_END_
#define _WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_