mirror of
https://github.com/XRPLF/rippled.git
synced 2026-04-29 15:37:57 +00:00
Add s2c_max_window_bits negotiation
This commit is contained in:
@@ -44,10 +44,12 @@ typedef websocketpp::extensions::permessage_deflate::disabled<config> disabled_t
|
||||
struct ext_vars {
|
||||
enabled_type exts;
|
||||
enabled_type extc;
|
||||
websocketpp::lib::error_code ec;
|
||||
websocketpp::err_str_pair esp;
|
||||
websocketpp::http::attribute_list attr;
|
||||
};
|
||||
namespace pmde = websocketpp::extensions::permessage_deflate::error;
|
||||
namespace pmd_mode = websocketpp::extensions::permessage_deflate::mode;
|
||||
|
||||
// Ensure the disabled extension behaves appropriately disabled
|
||||
|
||||
@@ -157,6 +159,102 @@ BOOST_AUTO_TEST_CASE( negotiate_c2s_no_context_takeover_server_initiated ) {
|
||||
BOOST_CHECK_EQUAL( v.esp.second, "permessage-deflate; c2s_no_context_takeover");
|
||||
}
|
||||
|
||||
|
||||
// Negotiate s2c_max_window_bits
|
||||
BOOST_AUTO_TEST_CASE( negotiate_s2c_max_window_bits_invalid ) {
|
||||
ext_vars v;
|
||||
|
||||
std::vector<std::string> values;
|
||||
values.push_back("");
|
||||
values.push_back("foo");
|
||||
values.push_back("7");
|
||||
values.push_back("16");
|
||||
|
||||
std::vector<std::string>::const_iterator it;
|
||||
for (it = values.begin(); it != values.end(); ++it) {
|
||||
v.attr["s2c_max_window_bits"] = *it;
|
||||
|
||||
v.esp = v.exts.negotiate(v.attr);
|
||||
BOOST_CHECK( !v.exts.is_enabled() );
|
||||
BOOST_CHECK_EQUAL( v.esp.first, pmde::make_error_code(pmde::invalid_attribute_value) );
|
||||
BOOST_CHECK_EQUAL( v.esp.second, "");
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( negotiate_s2c_max_window_bits_valid ) {
|
||||
ext_vars v;
|
||||
v.attr["s2c_max_window_bits"] = "8";
|
||||
|
||||
v.esp = v.exts.negotiate(v.attr);
|
||||
BOOST_CHECK( v.exts.is_enabled() );
|
||||
BOOST_CHECK_EQUAL( v.esp.first, websocketpp::lib::error_code() );
|
||||
BOOST_CHECK_EQUAL( v.esp.second, "permessage-deflate; s2c_max_window_bits=8");
|
||||
|
||||
v.attr["s2c_max_window_bits"] = "15";
|
||||
|
||||
v.esp = v.exts.negotiate(v.attr);
|
||||
BOOST_CHECK( v.exts.is_enabled() );
|
||||
BOOST_CHECK_EQUAL( v.esp.first, websocketpp::lib::error_code() );
|
||||
BOOST_CHECK_EQUAL( v.esp.second, "permessage-deflate");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( invalid_set_s2c_max_window_bits ) {
|
||||
ext_vars v;
|
||||
|
||||
v.ec = v.exts.set_s2c_max_window_bits(7,pmd_mode::decline);
|
||||
BOOST_CHECK_EQUAL(v.ec,pmde::make_error_code(pmde::invalid_max_window_bits));
|
||||
|
||||
v.ec = v.exts.set_s2c_max_window_bits(16,pmd_mode::decline);
|
||||
BOOST_CHECK_EQUAL(v.ec,pmde::make_error_code(pmde::invalid_max_window_bits));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( negotiate_s2c_max_window_bits_decline ) {
|
||||
ext_vars v;
|
||||
v.attr["s2c_max_window_bits"] = "8";
|
||||
|
||||
v.ec = v.exts.set_s2c_max_window_bits(15,pmd_mode::decline);
|
||||
v.esp = v.exts.negotiate(v.attr);
|
||||
BOOST_CHECK( v.exts.is_enabled() );
|
||||
BOOST_CHECK_EQUAL( v.ec, websocketpp::lib::error_code() );
|
||||
BOOST_CHECK_EQUAL( v.esp.first, websocketpp::lib::error_code() );
|
||||
BOOST_CHECK_EQUAL( v.esp.second, "permessage-deflate");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( negotiate_s2c_max_window_bits_accept ) {
|
||||
ext_vars v;
|
||||
v.attr["s2c_max_window_bits"] = "8";
|
||||
|
||||
v.ec = v.exts.set_s2c_max_window_bits(15,pmd_mode::accept);
|
||||
v.esp = v.exts.negotiate(v.attr);
|
||||
BOOST_CHECK( v.exts.is_enabled() );
|
||||
BOOST_CHECK_EQUAL( v.ec, websocketpp::lib::error_code() );
|
||||
BOOST_CHECK_EQUAL( v.esp.first, websocketpp::lib::error_code() );
|
||||
BOOST_CHECK_EQUAL( v.esp.second, "permessage-deflate; s2c_max_window_bits=8");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( negotiate_s2c_max_window_bits_largest ) {
|
||||
ext_vars v;
|
||||
v.attr["s2c_max_window_bits"] = "8";
|
||||
|
||||
v.ec = v.exts.set_s2c_max_window_bits(15,pmd_mode::largest);
|
||||
v.esp = v.exts.negotiate(v.attr);
|
||||
BOOST_CHECK( v.exts.is_enabled() );
|
||||
BOOST_CHECK_EQUAL( v.ec, websocketpp::lib::error_code() );
|
||||
BOOST_CHECK_EQUAL( v.esp.first, websocketpp::lib::error_code() );
|
||||
BOOST_CHECK_EQUAL( v.esp.second, "permessage-deflate; s2c_max_window_bits=8");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( negotiate_s2c_max_window_bits_smallest ) {
|
||||
ext_vars v;
|
||||
v.attr["s2c_max_window_bits"] = "8";
|
||||
|
||||
v.ec = v.exts.set_s2c_max_window_bits(15,pmd_mode::smallest);
|
||||
v.esp = v.exts.negotiate(v.attr);
|
||||
BOOST_CHECK( v.exts.is_enabled() );
|
||||
BOOST_CHECK_EQUAL( v.ec, websocketpp::lib::error_code() );
|
||||
BOOST_CHECK_EQUAL( v.esp.first, websocketpp::lib::error_code() );
|
||||
BOOST_CHECK_EQUAL( v.esp.second, "permessage-deflate; s2c_max_window_bits=8");
|
||||
}
|
||||
// Combinations
|
||||
BOOST_AUTO_TEST_CASE( negotiate_all_client_initiated ) {
|
||||
ext_vars v;
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
|
||||
#include "zlib.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -91,8 +92,14 @@ enum value {
|
||||
/// Invalid extension attribute value
|
||||
invalid_attribute_value,
|
||||
|
||||
/// Invalid megotiation mode
|
||||
invalid_mode,
|
||||
|
||||
/// Unsupported extension attributes
|
||||
unsupported_attributes,
|
||||
|
||||
/// Invalid value for max_window_bits
|
||||
invalid_max_window_bits,
|
||||
|
||||
/// ZLib Error
|
||||
zlib_error,
|
||||
@@ -118,8 +125,12 @@ public:
|
||||
return "Invalid extension attributes";
|
||||
case invalid_attribute_value:
|
||||
return "Invalid extension attribute value";
|
||||
case invalid_mode:
|
||||
return "Invalid permessage-deflate negotiation mode";
|
||||
case unsupported_attributes:
|
||||
return "Unsupported extension attributes";
|
||||
case invalid_max_window_bits:
|
||||
return "Invalid value for max_window_bits";
|
||||
case zlib_error:
|
||||
return "A zlib function returned an error";
|
||||
case uninitialized:
|
||||
@@ -157,15 +168,44 @@ namespace websocketpp {
|
||||
namespace extensions {
|
||||
namespace permessage_deflate {
|
||||
|
||||
/// Default value for s2c_max_window_bits as defined by RFC6455
|
||||
static uint8_t const default_s2c_max_window_bits = 15;
|
||||
/// Minimum value for s2c_max_window_bits as defined by RFC6455
|
||||
static uint8_t const min_s2c_max_window_bits = 8;
|
||||
/// Maximum value for s2c_max_window_bits as defined by RFC6455
|
||||
static uint8_t const max_s2c_max_window_bits = 15;
|
||||
|
||||
/// Default value for c2s_max_window_bits as defined by RFC6455
|
||||
static uint8_t const default_c2s_max_window_bits = 15;
|
||||
/// Minimum value for c2s_max_window_bits as defined by RFC6455
|
||||
static uint8_t const min_c2s_max_window_bits = 8;
|
||||
/// Maximum value for c2s_max_window_bits as defined by RFC6455
|
||||
static uint8_t const max_c2s_max_window_bits = 15;
|
||||
|
||||
namespace mode {
|
||||
enum value {
|
||||
/// Accept any value the remote endpoint offers
|
||||
accept = 1,
|
||||
/// Decline any value the remote endpoint offers. Insist on defaults.
|
||||
decline,
|
||||
/// Use the largest value common to both offers
|
||||
largest,
|
||||
/// Use the smallest value common to both offers
|
||||
smallest
|
||||
};
|
||||
} // namespace mode
|
||||
|
||||
template <typename config>
|
||||
class enabled {
|
||||
public:
|
||||
enabled()
|
||||
: m_enabled(false)
|
||||
, m_c2s_no_context_takeover(false)
|
||||
, m_s2c_no_context_takeover(false)
|
||||
, m_c2s_no_context_takeover(false)
|
||||
, m_s2c_max_window_bits(15)
|
||||
, m_c2s_max_window_bits(15)
|
||||
, m_s2c_max_window_bits(15) {}
|
||||
, m_s2c_max_window_bits_mode(mode::accept)
|
||||
, m_c2s_max_window_bits_mode(mode::accept) {}
|
||||
|
||||
/// Test if this object impliments the permessage-deflate specification
|
||||
/**
|
||||
@@ -242,6 +282,35 @@ public:
|
||||
m_c2s_no_context_takeover = true;
|
||||
}
|
||||
|
||||
/// Limit server LZ77 sliding window size
|
||||
/**
|
||||
*
|
||||
*
|
||||
* The bits setting is the base 2 logarithm of the window size to use to
|
||||
* compress outgoing messages. The permitted range is 8 to 15 inclusive.
|
||||
* 8 represents a 256 byte window and 15 a 32KiB window. The default
|
||||
* setting is 15.
|
||||
*
|
||||
* Mode Options:
|
||||
* - accept: Accept whatever the remote endpoint offers.
|
||||
* - decline: Decline any offers to deviate from the defaults
|
||||
* - largest: Accept largest window size acceptable to both endpoints
|
||||
* - smallest: Accept smallest window size acceptiable to both endpoints
|
||||
*
|
||||
* @param bits The size to request for the outgoing window size
|
||||
* @param mode The mode to use for negotiating this parameter
|
||||
* @return A status code
|
||||
*/
|
||||
lib::error_code set_s2c_max_window_bits(uint8_t bits, mode::value mode) {
|
||||
if (bits < min_s2c_max_window_bits || bits > max_s2c_max_window_bits) {
|
||||
return error::make_error_code(error::invalid_max_window_bits);
|
||||
}
|
||||
m_s2c_max_window_bits = bits;
|
||||
m_s2c_max_window_bits_mode = mode;
|
||||
|
||||
return lib::error_code();
|
||||
}
|
||||
|
||||
/// Generate extension offer
|
||||
/**
|
||||
* Creates an offer string to include in the Sec-WebSocket-Extensions
|
||||
@@ -283,6 +352,8 @@ public:
|
||||
negotiate_s2c_no_context_takeover(it->second,ret.first);
|
||||
} else if (it->first == "c2s_no_context_takeover") {
|
||||
negotiate_c2s_no_context_takeover(it->second,ret.first);
|
||||
} else if (it->first == "s2c_max_window_bits") {
|
||||
negotiate_s2c_max_window_bits(it->second,ret.first);
|
||||
} else {
|
||||
ret.first = make_error_code(error::invalid_attributes);
|
||||
}
|
||||
@@ -338,6 +409,11 @@ private:
|
||||
ret += "; c2s_no_context_takeover";
|
||||
}
|
||||
|
||||
if (m_s2c_max_window_bits < default_s2c_max_window_bits) {
|
||||
std::stringstream s;
|
||||
s << int(m_s2c_max_window_bits);
|
||||
ret += "; s2c_max_window_bits="+s.str();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -351,6 +427,7 @@ private:
|
||||
{
|
||||
if (!value.empty()) {
|
||||
ec = make_error_code(error::invalid_attribute_value);
|
||||
return;
|
||||
}
|
||||
|
||||
m_s2c_no_context_takeover = true;
|
||||
@@ -366,16 +443,64 @@ private:
|
||||
{
|
||||
if (!value.empty()) {
|
||||
ec = make_error_code(error::invalid_attribute_value);
|
||||
return;
|
||||
}
|
||||
|
||||
m_c2s_no_context_takeover = true;
|
||||
}
|
||||
|
||||
/// Negotiate s2c_max_window_bits attribute
|
||||
/**
|
||||
* When this method starts, m_s2c_max_window_bits will contain the server's
|
||||
* preferred value and m_s2c_max_window_bits_mode will contain the mode the
|
||||
* server wants to use to for negotiation
|
||||
*
|
||||
* options:
|
||||
* - decline (refuse to use the attribute)
|
||||
* - accept (use whatever the client says)
|
||||
* - largest value (use largest possible value)
|
||||
* - smallest value (use smallest possible value)
|
||||
*
|
||||
* @param [in] value The value of the attribute from the offer
|
||||
* @param [out] ec A reference to the error code to return errors via
|
||||
*/
|
||||
void negotiate_s2c_max_window_bits(std::string const & value,
|
||||
lib::error_code & ec)
|
||||
{
|
||||
uint8_t bits = uint8_t(atoi(value.c_str()));
|
||||
|
||||
if (bits < min_s2c_max_window_bits || bits > max_s2c_max_window_bits) {
|
||||
ec = make_error_code(error::invalid_attribute_value);
|
||||
m_s2c_max_window_bits = default_s2c_max_window_bits;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (m_s2c_max_window_bits_mode) {
|
||||
case mode::decline:
|
||||
m_s2c_max_window_bits = default_s2c_max_window_bits;
|
||||
break;
|
||||
case mode::accept:
|
||||
m_s2c_max_window_bits = bits;
|
||||
break;
|
||||
case mode::largest:
|
||||
m_s2c_max_window_bits = std::min(bits,m_s2c_max_window_bits);
|
||||
break;
|
||||
case mode::smallest:
|
||||
m_s2c_max_window_bits = min_s2c_max_window_bits;
|
||||
break;
|
||||
default:
|
||||
ec = make_error_code(error::invalid_mode);
|
||||
m_s2c_max_window_bits = default_s2c_max_window_bits;
|
||||
}
|
||||
}
|
||||
|
||||
bool m_enabled;
|
||||
bool m_c2s_no_context_takeover;
|
||||
bool m_s2c_no_context_takeover;
|
||||
uint8_t m_c2s_max_window_bits;
|
||||
bool m_c2s_no_context_takeover;
|
||||
uint8_t m_s2c_max_window_bits;
|
||||
uint8_t m_c2s_max_window_bits;
|
||||
mode::value m_s2c_max_window_bits_mode;
|
||||
mode::value m_c2s_max_window_bits_mode;
|
||||
};
|
||||
|
||||
} // namespace permessage_deflate
|
||||
|
||||
Reference in New Issue
Block a user