28 #ifndef WEBSOCKETPP_PROCESSOR_EXTENSION_PERMESSAGEDEFLATE_HPP
29 #define WEBSOCKETPP_PROCESSOR_EXTENSION_PERMESSAGEDEFLATE_HPP
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>
39 #include <websocketpp/extensions/extension.hpp>
119 class category :
public lib::error_category {
123 char const * name()
const _WEBSOCKETPP_NOEXCEPT_TOKEN_ {
124 return "websocketpp.extension.permessage-deflate";
127 std::string message(
int value)
const {
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";
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";
142 return "A zlib function returned an error";
144 return "Deflate extension must be initialized before use";
146 return "Unknown permessage-compress error";
153 static category instance;
159 return lib::error_code(
static_cast<
int>(e), get_category());
167 _WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_
168 template<>
struct is_error_code_enum
169 <websocketpp::extensions::permessage_deflate::error::value>
171 static bool const value =
true;
173 _WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_
217 template <
typename config>
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)
231 m_dstate.zalloc = Z_NULL;
232 m_dstate.zfree = Z_NULL;
233 m_dstate.opaque = Z_NULL;
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;
243 if (!m_initialized) {
247 int ret = deflateEnd(&m_dstate);
254 ret = inflateEnd(&m_istate);
274 uint8_t deflate_bits;
275 uint8_t inflate_bits;
278 deflate_bits = m_server_max_window_bits;
279 inflate_bits = m_client_max_window_bits;
281 deflate_bits = m_client_max_window_bits;
282 inflate_bits = m_server_max_window_bits;
285 int ret = deflateInit2(
287 Z_DEFAULT_COMPRESSION,
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))
312 m_flush = Z_FULL_FLUSH;
314 m_flush = Z_SYNC_FLUSH;
316 m_initialized =
true;
317 return lib::error_code();
363 m_server_no_context_takeover =
true;
382 m_client_no_context_takeover =
true;
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);
425 m_server_max_window_bits = bits;
426 m_server_max_window_bits_mode = mode;
428 return lib::error_code();
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);
470 m_client_max_window_bits = bits;
471 m_client_max_window_bits_mode = mode;
473 return lib::error_code();
485 return "permessage-deflate; client_no_context_takeover; client_max_window_bits";
497 return lib::error_code();
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);
523 ret.first = make_error_code(error::invalid_attributes);
531 if (ret.first == lib::error_code()) {
533 ret.second = generate_response();
549 if (!m_initialized) {
556 uint8_t buf[6] = {0x02, 0x00, 0x00, 0x00, 0xff, 0xff};
557 out.append((
char *)(buf),6);
558 return lib::error_code();
561 m_dstate.avail_in = in.size();
562 m_dstate.next_in = (
unsigned char *)(
const_cast<
char *>(in.data()));
566 m_dstate.avail_out = m_compress_buffer_size;
567 m_dstate.next_out = m_compress_buffer.get();
569 deflate(&m_dstate, m_flush);
571 output = m_compress_buffer_size - m_dstate.avail_out;
573 out.append((
char *)(m_compress_buffer.get()),output);
574 }
while (m_dstate.avail_out == 0);
576 return lib::error_code();
589 if (!m_initialized) {
595 m_istate.avail_in = len;
596 m_istate.next_in =
const_cast<
unsigned char *>(buf);
599 m_istate.avail_out = m_compress_buffer_size;
600 m_istate.next_out = m_decompress_buffer.get();
602 ret = inflate(&m_istate, Z_SYNC_FLUSH);
604 if (ret == Z_NEED_DICT || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR) {
605 return make_error_code(error::zlib_error);
609 reinterpret_cast<
char *>(m_decompress_buffer.get()),
610 m_compress_buffer_size - m_istate.avail_out
612 }
while (m_istate.avail_out == 0);
614 return lib::error_code();
621 std::string generate_response() {
622 std::string ret =
"permessage-deflate";
624 if (m_server_no_context_takeover) {
625 ret +=
"; server_no_context_takeover";
628 if (m_client_no_context_takeover) {
629 ret +=
"; client_no_context_takeover";
632 if (m_server_max_window_bits < default_server_max_window_bits) {
634 s <<
int(m_server_max_window_bits);
635 ret +=
"; server_max_window_bits="+s.str();
638 if (m_client_max_window_bits < default_client_max_window_bits) {
640 s <<
int(m_client_max_window_bits);
641 ret +=
"; client_max_window_bits="+s.str();
652 void negotiate_server_no_context_takeover(std::string
const & value,
653 lib::error_code & ec)
655 if (!value.empty()) {
656 ec = make_error_code(error::invalid_attribute_value);
660 m_server_no_context_takeover =
true;
668 void negotiate_client_no_context_takeover(std::string
const & value,
669 lib::error_code & ec)
671 if (!value.empty()) {
672 ec = make_error_code(error::invalid_attribute_value);
676 m_client_no_context_takeover =
true;
701 void negotiate_server_max_window_bits(std::string
const & value,
702 lib::error_code & ec)
704 uint8_t bits = uint8_t(atoi(value.c_str()));
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;
712 switch (m_server_max_window_bits_mode) {
714 m_server_max_window_bits = default_server_max_window_bits;
717 m_server_max_window_bits = bits;
720 m_server_max_window_bits = std::min(bits,m_server_max_window_bits);
723 m_server_max_window_bits = min_server_max_window_bits;
726 ec = make_error_code(error::invalid_mode);
727 m_server_max_window_bits = default_server_max_window_bits;
731 if (m_server_max_window_bits == 8) {
732 m_server_max_window_bits = 9;
757 void negotiate_client_max_window_bits(std::string
const & value,
758 lib::error_code & ec)
760 uint8_t bits = uint8_t(atoi(value.c_str()));
763 bits = default_client_max_window_bits;
764 }
else if (bits < min_client_max_window_bits ||
765 bits > max_client_max_window_bits)
767 ec = make_error_code(error::invalid_attribute_value);
768 m_client_max_window_bits = default_client_max_window_bits;
772 switch (m_client_max_window_bits_mode) {
774 m_client_max_window_bits = default_client_max_window_bits;
777 m_client_max_window_bits = bits;
780 m_client_max_window_bits = std::min(bits,m_client_max_window_bits);
783 m_client_max_window_bits = min_client_max_window_bits;
786 ec = make_error_code(error::invalid_mode);
787 m_client_max_window_bits = default_client_max_window_bits;
791 if (m_client_max_window_bits == 8) {
792 m_client_max_window_bits = 9;
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;
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;