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/system_error.hpp> 36 #include <websocketpp/error.hpp> 38 #include <websocketpp/extensions/extension.hpp> 118 class category :
public lib::error_category {
122 char const * name()
const _WEBSOCKETPP_NOEXCEPT_TOKEN_ {
123 return "websocketpp.extension.permessage-deflate";
126 std::string message(
int value)
const {
129 return "Generic permessage-compress error";
130 case invalid_attributes:
131 return "Invalid extension attributes";
132 case invalid_attribute_value:
133 return "Invalid extension attribute value";
135 return "Invalid permessage-deflate negotiation mode";
136 case unsupported_attributes:
137 return "Unsupported extension attributes";
138 case invalid_max_window_bits:
139 return "Invalid value for max_window_bits";
141 return "A zlib function returned an error";
143 return "Deflate extension must be initialized before use";
145 return "Unknown permessage-compress error";
158 return lib::error_code(
static_cast<
int>(e), get_category());
166 _WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_
167 template<>
struct is_error_code_enum
168 <websocketpp::extensions::permessage_deflate::error::value>
170 static bool const value =
true;
172 _WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_
216 template <
typename config>
221 , m_server_no_context_takeover(
false)
222 , m_client_no_context_takeover(
false)
223 , m_server_max_window_bits(15)
224 , m_client_max_window_bits(15)
225 , m_server_max_window_bits_mode(mode::accept)
226 , m_client_max_window_bits_mode(mode::accept)
227 , m_initialized(
false)
228 , m_compress_buffer_size(16384)
230 m_dstate.zalloc = Z_NULL;
231 m_dstate.zfree = Z_NULL;
232 m_dstate.opaque = Z_NULL;
234 m_istate.zalloc = Z_NULL;
235 m_istate.zfree = Z_NULL;
236 m_istate.opaque = Z_NULL;
237 m_istate.avail_in = 0;
238 m_istate.next_in = Z_NULL;
242 if (!m_initialized) {
246 int ret = deflateEnd(&m_dstate);
253 ret = inflateEnd(&m_istate);
273 uint8_t deflate_bits;
274 uint8_t inflate_bits;
277 deflate_bits = m_server_max_window_bits;
278 inflate_bits = m_client_max_window_bits;
280 deflate_bits = m_client_max_window_bits;
281 inflate_bits = m_server_max_window_bits;
284 int ret = deflateInit2(
286 Z_DEFAULT_COMPRESSION,
306 m_compress_buffer.reset(
new unsigned char[m_compress_buffer_size]);
307 if ((m_server_no_context_takeover && is_server) ||
308 (m_client_no_context_takeover && !is_server))
310 m_flush = Z_FULL_FLUSH;
312 m_flush = Z_SYNC_FLUSH;
314 m_initialized =
true;
315 return lib::error_code();
361 m_server_no_context_takeover =
true;
380 m_client_no_context_takeover =
true;
415 return error::make_error_code(error::invalid_max_window_bits);
423 m_server_max_window_bits = bits;
424 m_server_max_window_bits_mode = mode;
426 return lib::error_code();
460 return error::make_error_code(error::invalid_max_window_bits);
468 m_client_max_window_bits = bits;
469 m_client_max_window_bits_mode = mode;
471 return lib::error_code();
483 return "permessage-deflate; client_no_context_takeover; client_max_window_bits";
495 return lib::error_code();
510 http::attribute_list::const_iterator it;
511 for (it = offer.begin(); it != offer.end(); ++it) {
512 if (it->first ==
"server_no_context_takeover") {
513 negotiate_server_no_context_takeover(it->second,ret.first);
514 }
else if (it->first ==
"client_no_context_takeover") {
515 negotiate_client_no_context_takeover(it->second,ret.first);
516 }
else if (it->first ==
"server_max_window_bits") {
517 negotiate_server_max_window_bits(it->second,ret.first);
518 }
else if (it->first ==
"client_max_window_bits") {
519 negotiate_client_max_window_bits(it->second,ret.first);
521 ret.first = make_error_code(error::invalid_attributes);
529 if (ret.first == lib::error_code()) {
531 ret.second = generate_response();
547 if (!m_initialized) {
554 uint8_t buf[6] = {0x02, 0x00, 0x00, 0x00, 0xff, 0xff};
555 out.append((
char *)(buf),6);
556 return lib::error_code();
559 m_dstate.avail_in = in.size();
560 m_dstate.next_in = (
unsigned char *)(
const_cast<
char *>(in.data()));
564 m_dstate.avail_out = m_compress_buffer_size;
565 m_dstate.next_out = m_compress_buffer.get();
567 deflate(&m_dstate, m_flush);
569 output = m_compress_buffer_size - m_dstate.avail_out;
571 out.append((
char *)(m_compress_buffer.get()),output);
572 }
while (m_dstate.avail_out == 0);
574 return lib::error_code();
587 if (!m_initialized) {
593 m_istate.avail_in = len;
594 m_istate.next_in =
const_cast<
unsigned char *>(buf);
597 m_istate.avail_out = m_compress_buffer_size;
598 m_istate.next_out = m_compress_buffer.get();
600 ret = inflate(&m_istate, Z_SYNC_FLUSH);
602 if (ret == Z_NEED_DICT || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR) {
607 reinterpret_cast<
char *>(m_compress_buffer.get()),
608 m_compress_buffer_size - m_istate.avail_out
610 }
while (m_istate.avail_out == 0);
612 return lib::error_code();
619 std::string generate_response() {
620 std::string ret =
"permessage-deflate";
622 if (m_server_no_context_takeover) {
623 ret +=
"; server_no_context_takeover";
626 if (m_client_no_context_takeover) {
627 ret +=
"; client_no_context_takeover";
632 s <<
int(m_server_max_window_bits);
633 ret +=
"; server_max_window_bits="+s.str();
638 s <<
int(m_client_max_window_bits);
639 ret +=
"; client_max_window_bits="+s.str();
650 void negotiate_server_no_context_takeover(std::string
const & value,
651 lib::error_code & ec)
653 if (!value.empty()) {
654 ec = make_error_code(error::invalid_attribute_value);
658 m_server_no_context_takeover =
true;
666 void negotiate_client_no_context_takeover(std::string
const & value,
667 lib::error_code & ec)
669 if (!value.empty()) {
670 ec = make_error_code(error::invalid_attribute_value);
674 m_client_no_context_takeover =
true;
699 void negotiate_server_max_window_bits(std::string
const & value,
700 lib::error_code & ec)
702 uint8_t bits = uint8_t(atoi(value.c_str()));
705 ec = make_error_code(error::invalid_attribute_value);
710 switch (m_server_max_window_bits_mode) {
715 m_server_max_window_bits = bits;
718 m_server_max_window_bits = std::min(bits,m_server_max_window_bits);
724 ec = make_error_code(error::invalid_mode);
729 if (m_server_max_window_bits == 8) {
730 m_server_max_window_bits = 9;
755 void negotiate_client_max_window_bits(std::string
const & value,
756 lib::error_code & ec)
758 uint8_t bits = uint8_t(atoi(value.c_str()));
765 ec = make_error_code(error::invalid_attribute_value);
770 switch (m_client_max_window_bits_mode) {
775 m_client_max_window_bits = bits;
778 m_client_max_window_bits = std::min(bits,m_client_max_window_bits);
784 ec = make_error_code(error::invalid_mode);
789 if (m_client_max_window_bits == 8) {
790 m_client_max_window_bits = 9;
795 bool m_server_no_context_takeover;
796 bool m_client_no_context_takeover;
797 uint8_t m_server_max_window_bits;
798 uint8_t m_client_max_window_bits;
799 mode::value m_server_max_window_bits_mode;
800 mode::value m_client_max_window_bits_mode;
804 size_t m_compress_buffer_size;
805 lib::unique_ptr_uchar_array m_compress_buffer;
lib::error_code set_server_max_window_bits(uint8_t bits, mode::value mode)
Limit server LZ77 sliding window size.
void enable_client_no_context_takeover()
Reset client's outgoing LZ77 sliding window for each new message.
static uint8_t const default_server_max_window_bits
Default value for server_max_window_bits as defined by RFC 7692.
lib::error_category const & get_category()
Get a reference to a static copy of the permessage-deflate error category.
Invalid value for max_window_bits.
Invalid megotiation mode.
Invalid extension attribute value.
lib::error_code init(bool is_server)
Initialize zlib state.
lib::error_code set_client_max_window_bits(uint8_t bits, mode::value mode)
Limit client LZ77 sliding window size.
lib::error_code validate_offer(http::attribute_list const &)
Validate extension response.
lib::error_code decompress(uint8_t const *buf, size_t len, std::string &out)
Decompress bytes.
bool is_implemented() const
Test if this object implements the permessage-deflate specification.
lib::error_code make_error_code(error::value e)
Create an error code in the permessage-deflate category.
static uint8_t const min_client_max_window_bits
Minimum value for client_max_window_bits as defined by RFC 7692.
Implementation of RFC 7692, the permessage-deflate WebSocket extension.
bool is_enabled() const
Test if the extension was negotiated for this connection.
static uint8_t const max_server_max_window_bits
Maximum value for server_max_window_bits as defined by RFC 7692.
err_str_pair negotiate(http::attribute_list const &offer)
Negotiate extension.
void handle_accept(connection_ptr con, lib::error_code const &ec)
Handler callback for start_accept.
Unsupported extension attributes.
void enable_server_no_context_takeover()
Reset server's outgoing LZ77 sliding window for each new message.
std::string generate_offer() const
Generate extension offer.
static uint8_t const min_server_max_window_bits
Minimum value for server_max_window_bits as defined by RFC 7692.
lib::error_code compress(std::string const &in, std::string &out)
Compress bytes.
static uint8_t const max_client_max_window_bits
Maximum value for client_max_window_bits as defined by RFC 7692.
Invalid extension attributes.
static uint8_t const default_client_max_window_bits
Default value for client_max_window_bits as defined by RFC 7692.