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>
47 namespace extensions {
83 namespace permessage_deflate {
118 char const * name()
const _WEBSOCKETPP_NOEXCEPT_TOKEN_ {
119 return "websocketpp.extension.permessage-deflate";
122 std::string message(
int value)
const {
125 return "Generic permessage-compress error";
127 return "Invalid extension attributes";
129 return "Invalid extension attribute value";
131 return "Invalid permessage-deflate negotiation mode";
133 return "Unsupported extension attributes";
135 return "Invalid value for max_window_bits";
137 return "A zlib function returned an error";
139 return "Object must be initialized before use";
141 return "Unknown permessage-compress error";
154 return lib::error_code(static_cast<int>(e),
get_category());
162 _WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_
163 template<>
struct is_error_code_enum
166 static bool const value =
true;
168 _WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_
170 namespace extensions {
171 namespace permessage_deflate {
200 template <
typename config>
205 , m_s2c_no_context_takeover(
false)
206 , m_c2s_no_context_takeover(
false)
207 , m_s2c_max_window_bits(15)
208 , m_c2s_max_window_bits(15)
209 , m_s2c_max_window_bits_mode(mode::accept)
210 , m_c2s_max_window_bits_mode(mode::accept)
211 , m_initialized(
false)
212 , m_compress_buffer_size(16384)
214 m_dstate.zalloc = Z_NULL;
215 m_dstate.zfree = Z_NULL;
216 m_dstate.opaque = Z_NULL;
218 m_istate.zalloc = Z_NULL;
219 m_istate.zfree = Z_NULL;
220 m_istate.opaque = Z_NULL;
221 m_istate.avail_in = 0;
222 m_istate.next_in = Z_NULL;
226 if (!m_initialized) {
230 int ret = deflateEnd(&m_dstate);
237 ret = inflateEnd(&m_istate);
252 uint8_t deflate_bits;
253 uint8_t inflate_bits;
256 deflate_bits = m_s2c_max_window_bits;
257 inflate_bits = m_c2s_max_window_bits;
259 deflate_bits = m_c2s_max_window_bits;
260 inflate_bits = m_s2c_max_window_bits;
263 int ret = deflateInit2(
265 Z_DEFAULT_COMPRESSION,
285 m_compress_buffer.reset(
new unsigned char[m_compress_buffer_size]);
286 m_initialized =
true;
287 return lib::error_code();
333 m_s2c_no_context_takeover =
true;
352 m_c2s_no_context_takeover =
true;
378 if (bits < min_s2c_max_window_bits || bits > max_s2c_max_window_bits) {
381 m_s2c_max_window_bits = bits;
382 m_s2c_max_window_bits_mode = mode;
384 return lib::error_code();
409 if (bits < min_c2s_max_window_bits || bits > max_c2s_max_window_bits) {
412 m_c2s_max_window_bits = bits;
413 m_c2s_max_window_bits_mode = mode;
415 return lib::error_code();
453 http::attribute_list::const_iterator it;
454 for (it = offer.begin(); it != offer.end(); ++it) {
455 if (it->first ==
"s2c_no_context_takeover") {
456 negotiate_s2c_no_context_takeover(it->second,ret.first);
457 }
else if (it->first ==
"c2s_no_context_takeover") {
458 negotiate_c2s_no_context_takeover(it->second,ret.first);
459 }
else if (it->first ==
"s2c_max_window_bits") {
460 negotiate_s2c_max_window_bits(it->second,ret.first);
461 }
else if (it->first ==
"c2s_max_window_bits") {
462 negotiate_c2s_max_window_bits(it->second,ret.first);
472 if (ret.first == lib::error_code()) {
474 ret.second = generate_response();
486 lib::error_code
compress(std::string
const & in, std::string & out) {
487 if (!m_initialized) {
493 m_dstate.avail_out = m_compress_buffer_size;
494 m_dstate.next_in = (
unsigned char *)(const_cast<char *>(in.data()));
498 m_dstate.avail_out = m_compress_buffer_size;
499 m_dstate.next_out = m_compress_buffer.get();
501 deflate(&m_dstate, Z_SYNC_FLUSH);
503 output = m_compress_buffer_size - m_dstate.avail_out;
505 out.append((
char *)(m_compress_buffer.get()),output);
506 }
while (m_dstate.avail_out == 0);
508 return lib::error_code();
518 lib::error_code
decompress(uint8_t
const * buf,
size_t len, std::string &
521 if (!m_initialized) {
527 m_istate.avail_in = len;
528 m_istate.next_in =
const_cast<unsigned char *
>(buf);
531 m_istate.avail_out = m_compress_buffer_size;
532 m_istate.next_out = m_compress_buffer.get();
534 ret = inflate(&m_istate, Z_SYNC_FLUSH);
536 if (ret == Z_NEED_DICT || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR) {
541 reinterpret_cast<char *>(m_compress_buffer.get()),
542 m_compress_buffer_size - m_istate.avail_out
544 }
while (m_istate.avail_out == 0);
546 return lib::error_code();
553 std::string generate_response() {
554 std::string ret =
"permessage-deflate";
556 if (m_s2c_no_context_takeover) {
557 ret +=
"; s2c_no_context_takeover";
560 if (m_c2s_no_context_takeover) {
561 ret +=
"; c2s_no_context_takeover";
564 if (m_s2c_max_window_bits < default_s2c_max_window_bits) {
566 s << int(m_s2c_max_window_bits);
567 ret +=
"; s2c_max_window_bits="+s.str();
570 if (m_c2s_max_window_bits < default_c2s_max_window_bits) {
572 s << int(m_c2s_max_window_bits);
573 ret +=
"; c2s_max_window_bits="+s.str();
584 void negotiate_s2c_no_context_takeover(std::string
const &
value,
585 lib::error_code & ec)
587 if (!value.empty()) {
592 m_s2c_no_context_takeover =
true;
600 void negotiate_c2s_no_context_takeover(std::string
const & value,
601 lib::error_code & ec)
603 if (!value.empty()) {
608 m_c2s_no_context_takeover =
true;
627 void negotiate_s2c_max_window_bits(std::string
const & value,
628 lib::error_code & ec)
630 uint8_t bits = uint8_t(atoi(value.c_str()));
632 if (bits < min_s2c_max_window_bits || bits > max_s2c_max_window_bits) {
638 switch (m_s2c_max_window_bits_mode) {
643 m_s2c_max_window_bits = bits;
646 m_s2c_max_window_bits = std::min(bits,m_s2c_max_window_bits);
672 void negotiate_c2s_max_window_bits(std::string
const & value,
673 lib::error_code & ec)
675 uint8_t bits = uint8_t(atoi(value.c_str()));
679 }
else if (bits < min_c2s_max_window_bits ||
680 bits > max_c2s_max_window_bits)
687 switch (m_c2s_max_window_bits_mode) {
692 m_c2s_max_window_bits = bits;
695 m_c2s_max_window_bits = std::min(bits,m_c2s_max_window_bits);
707 bool m_s2c_no_context_takeover;
708 bool m_c2s_no_context_takeover;
709 uint8_t m_s2c_max_window_bits;
710 uint8_t m_c2s_max_window_bits;
715 size_t m_compress_buffer_size;
716 lib::unique_ptr_uchar_array m_compress_buffer;
725 #endif // WEBSOCKETPP_PROCESSOR_EXTENSION_PERMESSAGEDEFLATE_HPP
static uint8_t const default_c2s_max_window_bits
Default value for c2s_max_window_bits as defined by RFC6455.
uint16_t value
The type of a close code value.
static uint8_t const max_s2c_max_window_bits
Maximum value for s2c_max_window_bits as defined by RFC6455.
lib::error_category const & get_category()
Get a reference to a static copy of the permessage-deflate error category.
std::pair< lib::error_code, std::string > err_str_pair
Combination error code / string type for returning two values.
Invalid value for max_window_bits.
Invalid megotiation mode.
lib::error_code init()
Initialize zlib state.
Invalid extension attribute value.
lib::error_code set_c2s_max_window_bits(uint8_t bits, mode::value mode)
Limit client LZ77 sliding window size.
lib::error_code validate_offer(http::attribute_list const &response)
Validate extension response.
lib::error_code decompress(uint8_t const *buf, size_t len, std::string &out)
Decompress bytes.
lib::error_code make_error_code(error::value e)
Create an error code in the permessage-deflate category.
static uint8_t const min_c2s_max_window_bits
Minimum value for c2s_max_window_bits as defined by RFC6455.
Namespace for the WebSocket++ project.
err_str_pair negotiate(http::attribute_list const &offer)
Negotiate extension.
static uint8_t const max_c2s_max_window_bits
Maximum value for c2s_max_window_bits as defined by RFC6455.
bool is_implemented() const
Test if this object impliments the permessage-deflate specification.
std::map< std::string, std::string > attribute_list
The type of an HTTP attribute list.
Unsupported extension attributes.
bool is_enabled() const
Test if the extension was negotiated for this connection.
void enable_c2s_no_context_takeover()
Reset client's outgoing LZ77 sliding window for each new message.
static uint8_t const min_s2c_max_window_bits
Minimum value for s2c_max_window_bits as defined by RFC6455.
static uint8_t const default_s2c_max_window_bits
Default value for s2c_max_window_bits as defined by RFC6455.
lib::error_code compress(std::string const &in, std::string &out)
Compress bytes.
lib::error_code set_s2c_max_window_bits(uint8_t bits, mode::value mode)
Limit server LZ77 sliding window size.
Invalid extension attributes.
std::string generate_offer() const
Generate extension offer.
Permessage-deflate error category.
void enable_s2c_no_context_takeover()
Reset server's outgoing LZ77 sliding window for each new message.