From 6bd1bfd4f20e26fed6c2d1fefc51abf7fd7aa243 Mon Sep 17 00:00:00 2001 From: Peter Thorson Date: Wed, 3 Jul 2013 06:19:08 -0500 Subject: [PATCH] add basic compression/decompression implimentation --- test/extension/SConscript | 4 +- test/extension/permessage_deflate.cpp | 46 ++++++ test/processors/SConscript | 4 +- .../extensions/permessage_deflate/enabled.hpp | 137 +++++++++++++++++- 4 files changed, 184 insertions(+), 7 deletions(-) diff --git a/test/extension/SConscript b/test/extension/SConscript index ed5f6f7986..8c6a371213 100644 --- a/test/extension/SConscript +++ b/test/extension/SConscript @@ -10,7 +10,7 @@ Import('polyfill_libs') env = env.Clone () env_cpp11 = env_cpp11.Clone () -BOOST_LIBS = boostlibs(['unit_test_framework','system','regex'],env) + [platform_libs] +BOOST_LIBS = boostlibs(['unit_test_framework','system','regex'],env) + [platform_libs] + ['z'] objs = env.Object('extension_boost.o', ["extension.cpp"], LIBS = BOOST_LIBS) objs += env.Object('permessage_deflate_boost.o', ["permessage_deflate.cpp"], LIBS = BOOST_LIBS) @@ -18,7 +18,7 @@ prgs = env.Program('test_extension_boost', ["extension_boost.o"], LIBS = BOOST_L prgs += env.Program('test_permessage_deflate_boost', ["permessage_deflate_boost.o"], LIBS = BOOST_LIBS) if env_cpp11.has_key('WSPP_CPP11_ENABLED'): - BOOST_LIBS_CPP11 = boostlibs(['unit_test_framework'],env_cpp11) + [platform_libs] + [polyfill_libs] + BOOST_LIBS_CPP11 = boostlibs(['unit_test_framework'],env_cpp11) + [platform_libs] + [polyfill_libs] + ['z'] objs += env_cpp11.Object('extension_stl.o', ["extension.cpp"], LIBS = BOOST_LIBS_CPP11) objs += env_cpp11.Object('permessage_deflate_stl.o', ["permessage_deflate.cpp"], LIBS = BOOST_LIBS_CPP11) prgs += env_cpp11.Program('test_extension_stl', ["extension_stl.o"], LIBS = BOOST_LIBS_CPP11) diff --git a/test/extension/permessage_deflate.cpp b/test/extension/permessage_deflate.cpp index c4ec1a0924..67509fd20b 100644 --- a/test/extension/permessage_deflate.cpp +++ b/test/extension/permessage_deflate.cpp @@ -36,6 +36,9 @@ #include +#include +#include + class config {}; typedef websocketpp::extensions::permessage_deflate::enabled enabled_type; @@ -495,3 +498,46 @@ BOOST_AUTO_TEST_CASE( negotiate_four_client_initiated ) { BOOST_CHECK_EQUAL( v.esp.first, websocketpp::lib::error_code() ); BOOST_CHECK_EQUAL( v.esp.second, "permessage-deflate; s2c_no_context_takeover; c2s_no_context_takeover; s2c_max_window_bits=10; c2s_max_window_bits=10"); } + +// Compression +/* +BOOST_AUTO_TEST_CASE( compress_data ) { + ext_vars v; + + std::string in = "Hello"; + std::string out; + std::string in2; + std::string out2; + + v.exts.init(); + + v.ec = v.exts.compress(in,out); + + std::cout << "in : " << websocketpp::utility::to_hex(in) << std::endl; + BOOST_CHECK_EQUAL( v.ec, websocketpp::lib::error_code() ); + std::cout << "out: " << websocketpp::utility::to_hex(out) << std::endl; + + in2 = out; + + v.ec = v.exts.decompress(reinterpret_cast(in2.data()),in2.size(),out2); + + BOOST_CHECK_EQUAL( v.ec, websocketpp::lib::error_code() ); + std::cout << "out: " << websocketpp::utility::to_hex(out2) << std::endl; + BOOST_CHECK_EQUAL( out, out2 ); +} + +BOOST_AUTO_TEST_CASE( decompress_data ) { + ext_vars v; + + uint8_t in[12] = {0xf2, 0x48, 0xcd, 0xc9, 0xc9, 0x07, 0x00, 0x00, 0x00, 0xff, 0xff}; + std::string out; + + v.exts.init(); + + v.ec = v.exts.decompress(in,12,out); + + BOOST_CHECK_EQUAL( v.ec, websocketpp::lib::error_code() ); + std::cout << "out: " << websocketpp::utility::to_hex(out) << std::endl; + BOOST_CHECK( false ); +} +*/ \ No newline at end of file diff --git a/test/processors/SConscript b/test/processors/SConscript index 90b7c0f677..ad29455124 100644 --- a/test/processors/SConscript +++ b/test/processors/SConscript @@ -10,7 +10,7 @@ Import('polyfill_libs') env = env.Clone () env_cpp11 = env_cpp11.Clone () -BOOST_LIBS = boostlibs(['unit_test_framework','regex','system'],env) + [platform_libs] +BOOST_LIBS = boostlibs(['unit_test_framework','regex','system'],env) + [platform_libs] + ['z'] objs = env.Object('test_processor_boost.o', ["processor.cpp"], LIBS = BOOST_LIBS) objs += env.Object('test_hybi13_boost.o', ["hybi13.cpp"], LIBS = BOOST_LIBS) @@ -27,7 +27,7 @@ prgs += env.Program('test_hybi00_boost', ["test_hybi00_boost.o"], LIBS = BOOST_L prgs += env.Program('test_extension_permessage_compress_boost', ["test_extension_permessage_compress_boost.o"], LIBS = BOOST_LIBS + ['z']) if env_cpp11.has_key('WSPP_CPP11_ENABLED'): - BOOST_LIBS_CPP11 = boostlibs(['unit_test_framework'],env_cpp11) + [platform_libs] + [polyfill_libs] + BOOST_LIBS_CPP11 = boostlibs(['unit_test_framework'],env_cpp11) + [platform_libs] + [polyfill_libs] + ['z'] # no C++11 features are used in processor so there are no C++11 versions of # these tests. objs += env_cpp11.Object('test_processor_stl.o', ["processor.cpp"], LIBS = BOOST_LIBS_CPP11) diff --git a/websocketpp/extensions/permessage_deflate/enabled.hpp b/websocketpp/extensions/permessage_deflate/enabled.hpp index 32ee5ec0b8..7fdaef541c 100644 --- a/websocketpp/extensions/permessage_deflate/enabled.hpp +++ b/websocketpp/extensions/permessage_deflate/enabled.hpp @@ -205,7 +205,85 @@ public: , m_s2c_max_window_bits(15) , m_c2s_max_window_bits(15) , m_s2c_max_window_bits_mode(mode::accept) - , m_c2s_max_window_bits_mode(mode::accept) {} + , m_c2s_max_window_bits_mode(mode::accept) + , m_initialized(false) + , m_compress_buffer_size(16384) + { + m_dstate.zalloc = Z_NULL; + m_dstate.zfree = Z_NULL; + m_dstate.opaque = Z_NULL; + + m_istate.zalloc = Z_NULL; + m_istate.zfree = Z_NULL; + m_istate.opaque = Z_NULL; + m_istate.avail_in = 0; + m_istate.next_in = Z_NULL; + } + + ~enabled() { + if (!m_initialized) { + return; + } + + int ret = deflateEnd(&m_dstate); + + if (ret != Z_OK) { + //std::cout << "error cleaning up zlib compression state" + // << std::endl; + } + + ret = inflateEnd(&m_istate); + + if (ret != Z_OK) { + //std::cout << "error cleaning up zlib decompression state" + // << std::endl; + } + } + + /// Initialize zlib state + /** + * + * @todo memory level, strategy, etc are hardcoded + * @todo server detection is hardcoded + */ + lib::error_code init() { + uint8_t deflate_bits; + uint8_t inflate_bits; + + if (true /*is_server*/) { + deflate_bits = m_s2c_max_window_bits; + inflate_bits = m_c2s_max_window_bits; + } else { + deflate_bits = m_c2s_max_window_bits; + inflate_bits = m_s2c_max_window_bits; + } + + int ret = deflateInit2( + &m_dstate, + Z_DEFAULT_COMPRESSION, + Z_DEFLATED, + -1*deflate_bits, + 8, // memory level 1-9 + /*Z_DEFAULT_STRATEGY*/Z_FIXED + ); + + if (ret != Z_OK) { + return make_error_code(error::zlib_error); + } + + ret = inflateInit2( + &m_istate, + -1*inflate_bits + ); + + if (ret != Z_OK) { + return make_error_code(error::zlib_error); + } + + m_compress_buffer.reset(new unsigned char[m_compress_buffer_size]); + m_initialized = true; + return lib::error_code(); + } /// Test if this object impliments the permessage-deflate specification /** @@ -404,7 +482,29 @@ public: * @return Error or status code */ lib::error_code compress(std::string const & in, std::string & out) { - return make_error_code(error::uninitialized); + if (!m_initialized) { + return make_error_code(error::uninitialized); + } + + size_t output; + int ret; + + m_dstate.avail_out = m_compress_buffer_size; + m_dstate.next_in = (unsigned char *)(const_cast(in.data())); + + do { + // Output to local buffer + m_dstate.avail_out = m_compress_buffer_size; + m_dstate.next_out = m_compress_buffer.get(); + + ret = deflate(&m_dstate, Z_SYNC_FLUSH); + + output = m_compress_buffer_size - m_dstate.avail_out; + + out.append((char *)(m_compress_buffer.get()),output); + } while (m_dstate.avail_out == 0); + + return lib::error_code(); } /// Decompress bytes @@ -417,7 +517,32 @@ public: lib::error_code decompress(uint8_t const * buf, size_t len, std::string & out) { - return make_error_code(error::uninitialized); + if (!m_initialized) { + return make_error_code(error::uninitialized); + } + + int ret; + + m_istate.avail_in = len; + m_istate.next_in = const_cast(buf); + + do { + m_istate.avail_out = m_compress_buffer_size; + m_istate.next_out = m_compress_buffer.get(); + + ret = inflate(&m_istate, Z_SYNC_FLUSH); + + if (ret == Z_NEED_DICT || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR) { + return make_error_code(error::zlib_error); + } + + out.append( + reinterpret_cast(m_compress_buffer.get()), + m_compress_buffer_size - m_istate.avail_out + ); + } while (m_istate.avail_out == 0); + + return lib::error_code(); } private: /// Generate negotiation response @@ -584,6 +709,12 @@ private: uint8_t m_c2s_max_window_bits; mode::value m_s2c_max_window_bits_mode; mode::value m_c2s_max_window_bits_mode; + + bool m_initialized; + size_t m_compress_buffer_size; + lib::unique_ptr_uchar_array m_compress_buffer; + z_stream m_dstate; + z_stream m_istate; }; } // namespace permessage_deflate