mirror of
https://github.com/XRPLF/rippled.git
synced 2026-04-29 15:37:57 +00:00
Merge pull request #322 from zaphoyd/max-message-size
Adds the ability to specify a max message size
This commit is contained in:
@@ -13,6 +13,7 @@ HEAD
|
||||
the main thread.
|
||||
- Feature: Adds the ability to specify whether or not to use the `SO_REUSEADDR` TCP socket
|
||||
option. The default for this value has been changed from `true` to `false`.
|
||||
- Feature: Adds the ability to specify a maximum message size.
|
||||
- Improvement: Open, close, and pong timeouts can be disabled entirely by setting their
|
||||
duration to 0.
|
||||
- Improvement: Numerous performance improvements. Including: tuned default
|
||||
|
||||
@@ -192,6 +192,33 @@ BOOST_AUTO_TEST_CASE( basic_client_websocket ) {
|
||||
BOOST_CHECK_EQUAL(ref, output.str());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( set_max_message_size ) {
|
||||
std::string input = "GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n\r\n";
|
||||
|
||||
// After the handshake, add a single frame with a message that is too long.
|
||||
char frame0[10] = {char(0x82), char(0x83), 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01};
|
||||
input.append(frame0, 10);
|
||||
|
||||
std::string output = "HTTP/1.1 101 Switching Protocols\r\nConnection: upgrade\r\nSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\nServer: foo\r\nUpgrade: websocket\r\n\r\n";
|
||||
|
||||
// After the handshake, add a single frame with a close message with message too big
|
||||
// error code.
|
||||
char frame1[4] = {char(0x88), 0x19, 0x03, char(0xf1)};
|
||||
output.append(frame1, 4);
|
||||
output.append("A message was too large");
|
||||
|
||||
server s;
|
||||
s.set_user_agent("");
|
||||
s.set_validate_handler(bind(&validate_set_ua,&s,::_1));
|
||||
s.set_max_message_size(2);
|
||||
|
||||
BOOST_CHECK_EQUAL(run_server_test(s,input), output);
|
||||
}
|
||||
|
||||
// TODO: set max message size in client endpoint test case
|
||||
// TODO: set max message size mid connection test case
|
||||
// TODO: [maybe] set max message size in open handler
|
||||
|
||||
/*
|
||||
|
||||
BOOST_AUTO_TEST_CASE( user_reject_origin ) {
|
||||
|
||||
@@ -45,6 +45,8 @@ struct stub_config {
|
||||
<websocketpp::message_buffer::alloc::con_msg_manager> message_type;
|
||||
typedef websocketpp::message_buffer::alloc::con_msg_manager<message_type>
|
||||
con_msg_manager_type;
|
||||
|
||||
static const size_t max_message_size = 16000000;
|
||||
};
|
||||
|
||||
struct processor_setup {
|
||||
|
||||
@@ -50,6 +50,8 @@ struct stub_config {
|
||||
|
||||
typedef websocketpp::random::none::int_generator<uint32_t> rng_type;
|
||||
|
||||
static const size_t max_message_size = 16000000;
|
||||
|
||||
/// Extension related config
|
||||
static const bool enable_extensions = false;
|
||||
|
||||
|
||||
@@ -50,6 +50,8 @@ struct stub_config {
|
||||
|
||||
typedef websocketpp::random::none::int_generator<uint32_t> rng_type;
|
||||
|
||||
static const size_t max_message_size = 16000000;
|
||||
|
||||
/// Extension related config
|
||||
static const bool enable_extensions = false;
|
||||
|
||||
|
||||
@@ -60,6 +60,7 @@ struct stub_config {
|
||||
typedef websocketpp::extensions::permessage_deflate::disabled
|
||||
<permessage_deflate_config> permessage_deflate_type;
|
||||
|
||||
static const size_t max_message_size = 16000000;
|
||||
static const bool enable_extensions = false;
|
||||
};
|
||||
|
||||
@@ -81,6 +82,7 @@ struct stub_config_ext {
|
||||
typedef websocketpp::extensions::permessage_deflate::enabled
|
||||
<permessage_deflate_config> permessage_deflate_type;
|
||||
|
||||
static const size_t max_message_size = 16000000;
|
||||
static const bool enable_extensions = true;
|
||||
};
|
||||
|
||||
@@ -489,6 +491,36 @@ BOOST_AUTO_TEST_CASE( prepare_data_frame ) {
|
||||
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( single_frame_message_too_large ) {
|
||||
processor_setup env(true);
|
||||
|
||||
env.p.set_max_message_size(3);
|
||||
|
||||
uint8_t frame0[10] = {0x82, 0x84, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01};
|
||||
|
||||
// read message that is one byte too large
|
||||
BOOST_CHECK_EQUAL( env.p.consume(frame0,10,env.ec), 6 );
|
||||
BOOST_CHECK_EQUAL( env.ec, websocketpp::processor::error::message_too_big );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( multiple_frame_message_too_large ) {
|
||||
processor_setup env(true);
|
||||
|
||||
env.p.set_max_message_size(4);
|
||||
|
||||
uint8_t frame0[8] = {0x02, 0x82, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01};
|
||||
uint8_t frame1[9] = {0x80, 0x83, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01};
|
||||
|
||||
// read first message frame with size under the limit
|
||||
BOOST_CHECK_EQUAL( env.p.consume(frame0,8,env.ec), 8 );
|
||||
BOOST_CHECK( !env.ec );
|
||||
|
||||
// read second message frame that puts the size over the limit
|
||||
BOOST_CHECK_EQUAL( env.p.consume(frame1,9,env.ec), 6 );
|
||||
BOOST_CHECK_EQUAL( env.ec, websocketpp::processor::error::message_too_big );
|
||||
}
|
||||
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE( client_handshake_request ) {
|
||||
processor_setup env(false);
|
||||
|
||||
@@ -132,4 +132,4 @@ BOOST_AUTO_TEST_CASE( version_non_numeric ) {
|
||||
r.consume(handshake.c_str(),handshake.size());
|
||||
|
||||
BOOST_CHECK(websocketpp::processor::get_websocket_version(r) == -1);
|
||||
}
|
||||
}
|
||||
@@ -215,6 +215,18 @@ struct core {
|
||||
*/
|
||||
static const bool silent_close = false;
|
||||
|
||||
/// Default maximum message size
|
||||
/**
|
||||
* Default value for the processor's maximum message size. Maximum message size
|
||||
* determines the point at which the library will fail a connection with the
|
||||
* message_too_big protocol error.
|
||||
*
|
||||
* The default is 32MB
|
||||
*
|
||||
* @since 0.4.0-alpha1
|
||||
*/
|
||||
static const size_t max_message_size = 32000000;
|
||||
|
||||
/// Global flag for enabling/disabling extensions
|
||||
static const bool enable_extensions = true;
|
||||
|
||||
|
||||
@@ -216,6 +216,18 @@ struct core_client {
|
||||
*/
|
||||
static const bool silent_close = false;
|
||||
|
||||
/// Default maximum message size
|
||||
/**
|
||||
* Default value for the processor's maximum message size. Maximum message size
|
||||
* determines the point at which the library will fail a connection with the
|
||||
* message_too_big protocol error.
|
||||
*
|
||||
* The default is 32MB
|
||||
*
|
||||
* @since 0.4.0-alpha1
|
||||
*/
|
||||
static const size_t max_message_size = 32000000;
|
||||
|
||||
/// Global flag for enabling/disabling extensions
|
||||
static const bool enable_extensions = true;
|
||||
|
||||
|
||||
@@ -216,6 +216,18 @@ struct debug_core {
|
||||
*/
|
||||
static const bool silent_close = false;
|
||||
|
||||
/// Default maximum message size
|
||||
/**
|
||||
* Default value for the processor's maximum message size. Maximum message size
|
||||
* determines the point at which the library will fail a connection with the
|
||||
* message_too_big protocol error.
|
||||
*
|
||||
* The default is 32MB
|
||||
*
|
||||
* @since 0.4.0-alpha1
|
||||
*/
|
||||
static const size_t max_message_size = 32000000;
|
||||
|
||||
/// Global flag for enabling/disabling extensions
|
||||
static const bool enable_extensions = true;
|
||||
|
||||
|
||||
@@ -297,6 +297,7 @@ public:
|
||||
, m_open_handshake_timeout_dur(config::timeout_open_handshake)
|
||||
, m_close_handshake_timeout_dur(config::timeout_close_handshake)
|
||||
, m_pong_timeout_dur(config::timeout_pong)
|
||||
, m_max_message_size(config::max_message_size)
|
||||
, m_state(session::state::connecting)
|
||||
, m_internal_state(session::internal_state::USER_INIT)
|
||||
, m_msg_manager(new con_msg_manager_type())
|
||||
@@ -456,9 +457,9 @@ public:
|
||||
m_message_handler = h;
|
||||
}
|
||||
|
||||
/////////////////////////
|
||||
// Connection timeouts //
|
||||
/////////////////////////
|
||||
//////////////////////////////////////////
|
||||
// Connection timeouts and other limits //
|
||||
//////////////////////////////////////////
|
||||
|
||||
/// Set open handshake timeout
|
||||
/**
|
||||
@@ -529,6 +530,38 @@ public:
|
||||
m_pong_timeout_dur = dur;
|
||||
}
|
||||
|
||||
/// Get maximum message size
|
||||
/**
|
||||
* Get maximum message size. Maximum message size determines the point at which the
|
||||
* connection will fail a connection with the message_too_big protocol error.
|
||||
*
|
||||
* The default is set by the endpoint that creates the connection.
|
||||
*
|
||||
* @since 0.4.0-alpha1
|
||||
*/
|
||||
size_t get_max_message_size() const {
|
||||
return m_max_message_size;
|
||||
}
|
||||
|
||||
/// Set maximum message size
|
||||
/**
|
||||
* Set maximum message size. Maximum message size determines the point at which the
|
||||
* connection will fail a connection with the message_too_big protocol error. This
|
||||
* value may be changed during the connection.
|
||||
*
|
||||
* The default is set by the endpoint that creates the connection.
|
||||
*
|
||||
* @since 0.4.0-alpha1
|
||||
*
|
||||
* @param new_value The value to set as the maximum message size.
|
||||
*/
|
||||
void set_max_message_size(size_t new_value) {
|
||||
m_max_message_size = new_value;
|
||||
if (m_processor) {
|
||||
m_processor->set_max_message_size(new_value);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////
|
||||
// Uncategorized public methods //
|
||||
//////////////////////////////////
|
||||
@@ -1345,6 +1378,7 @@ private:
|
||||
long m_open_handshake_timeout_dur;
|
||||
long m_close_handshake_timeout_dur;
|
||||
long m_pong_timeout_dur;
|
||||
size_t m_max_message_size;
|
||||
|
||||
/// External connection state
|
||||
/**
|
||||
|
||||
@@ -94,6 +94,7 @@ public:
|
||||
, m_open_handshake_timeout_dur(config::timeout_open_handshake)
|
||||
, m_close_handshake_timeout_dur(config::timeout_close_handshake)
|
||||
, m_pong_timeout_dur(config::timeout_pong)
|
||||
, m_max_message_size(config::max_message_size)
|
||||
, m_is_server(p_is_server)
|
||||
{
|
||||
m_alog.set_channels(config::alog_level);
|
||||
@@ -272,9 +273,9 @@ public:
|
||||
m_message_handler = h;
|
||||
}
|
||||
|
||||
/////////////////////////
|
||||
// Connection timeouts //
|
||||
/////////////////////////
|
||||
//////////////////////////////////////////
|
||||
// Connection timeouts and other limits //
|
||||
//////////////////////////////////////////
|
||||
|
||||
/// Set open handshake timeout
|
||||
/**
|
||||
@@ -348,6 +349,36 @@ public:
|
||||
m_pong_timeout_dur = dur;
|
||||
}
|
||||
|
||||
/// Get default maximum message size
|
||||
/**
|
||||
* Get the default maximum message size that will be used for new connections created
|
||||
* by this endpoint. The maximum message size determines the point at which the
|
||||
* connection will fail a connection with the message_too_big protocol error.
|
||||
*
|
||||
* The default is set by the max_message_size value from the template config
|
||||
*
|
||||
* @since 0.4.0-alpha1
|
||||
*/
|
||||
size_t get_max_message_size() const {
|
||||
return m_max_message_size;
|
||||
}
|
||||
|
||||
/// Set default maximum message size
|
||||
/**
|
||||
* Set the default maximum message size that will be used for new connections created
|
||||
* by this endpoint. Maximum message size determines the point at which the connection
|
||||
* will fail a connection with the message_too_big protocol error.
|
||||
*
|
||||
* The default is set by the max_message_size value from the template config
|
||||
*
|
||||
* @since 0.4.0-alpha1
|
||||
*
|
||||
* @param new_value The value to set as the maximum message size.
|
||||
*/
|
||||
void set_max_message_size(size_t new_value) {
|
||||
m_max_message_size = new_value;
|
||||
}
|
||||
|
||||
/*************************************/
|
||||
/* Connection pass through functions */
|
||||
/*************************************/
|
||||
@@ -534,6 +565,7 @@ private:
|
||||
long m_open_handshake_timeout_dur;
|
||||
long m_close_handshake_timeout_dur;
|
||||
long m_pong_timeout_dur;
|
||||
size_t m_max_message_size;
|
||||
|
||||
rng_type m_rng;
|
||||
|
||||
|
||||
@@ -932,7 +932,7 @@ void connection<config>::handle_read_frame(lib::error_code const & ec,
|
||||
return;
|
||||
} else {
|
||||
lib::error_code close_ec;
|
||||
this->close(processor::error::to_ws(ec),ec.message(),close_ec);
|
||||
this->close(processor::error::to_ws(consume_ec),consume_ec.message(),close_ec);
|
||||
|
||||
if (close_ec) {
|
||||
m_elog.write(log::elevel::fatal,
|
||||
@@ -1945,49 +1945,49 @@ template <typename config>
|
||||
typename connection<config>::processor_ptr
|
||||
connection<config>::get_processor(int version) const {
|
||||
// TODO: allow disabling certain versions
|
||||
|
||||
processor_ptr p;
|
||||
|
||||
switch (version) {
|
||||
case 0:
|
||||
return processor_ptr(
|
||||
new processor::hybi00<config>(
|
||||
transport_con_type::is_secure(),
|
||||
m_is_server,
|
||||
m_msg_manager
|
||||
)
|
||||
);
|
||||
p.reset(new processor::hybi00<config>(
|
||||
transport_con_type::is_secure(),
|
||||
m_is_server,
|
||||
m_msg_manager
|
||||
));
|
||||
break;
|
||||
case 7:
|
||||
return processor_ptr(
|
||||
new processor::hybi07<config>(
|
||||
transport_con_type::is_secure(),
|
||||
m_is_server,
|
||||
m_msg_manager,
|
||||
m_rng
|
||||
)
|
||||
);
|
||||
p.reset(new processor::hybi07<config>(
|
||||
transport_con_type::is_secure(),
|
||||
m_is_server,
|
||||
m_msg_manager,
|
||||
m_rng
|
||||
));
|
||||
break;
|
||||
case 8:
|
||||
return processor_ptr(
|
||||
new processor::hybi08<config>(
|
||||
transport_con_type::is_secure(),
|
||||
m_is_server,
|
||||
m_msg_manager,
|
||||
m_rng
|
||||
)
|
||||
);
|
||||
p.reset(new processor::hybi08<config>(
|
||||
transport_con_type::is_secure(),
|
||||
m_is_server,
|
||||
m_msg_manager,
|
||||
m_rng
|
||||
));
|
||||
break;
|
||||
case 13:
|
||||
return processor_ptr(
|
||||
new processor::hybi13<config>(
|
||||
transport_con_type::is_secure(),
|
||||
m_is_server,
|
||||
m_msg_manager,
|
||||
m_rng
|
||||
)
|
||||
);
|
||||
p.reset(new processor::hybi13<config>(
|
||||
transport_con_type::is_secure(),
|
||||
m_is_server,
|
||||
m_msg_manager,
|
||||
m_rng
|
||||
));
|
||||
break;
|
||||
default:
|
||||
return processor_ptr();
|
||||
return p;
|
||||
}
|
||||
|
||||
// Settings not configured by the constructor
|
||||
p->set_max_message_size(m_max_message_size);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
template <typename config>
|
||||
|
||||
@@ -74,6 +74,9 @@ endpoint<connection,config>::create_connection() {
|
||||
if (m_pong_timeout_dur == config::timeout_pong) {
|
||||
con->set_pong_timeout(m_pong_timeout_dur);
|
||||
}
|
||||
if (m_max_message_size != config::max_message_size) {
|
||||
con->set_max_message_size(m_max_message_size);
|
||||
}
|
||||
|
||||
lib::error_code ec;
|
||||
|
||||
|
||||
@@ -369,11 +369,25 @@ public:
|
||||
m_current_msg = &m_control_msg;
|
||||
} else {
|
||||
if (!m_data_msg.msg_ptr) {
|
||||
if (m_bytes_needed > base::m_max_message_size) {
|
||||
ec = make_error_code(error::message_too_big);
|
||||
break;
|
||||
}
|
||||
|
||||
m_data_msg = msg_metadata(
|
||||
m_msg_manager->get_message(op,m_bytes_needed),
|
||||
frame::get_masking_key(m_basic_header,m_extended_header)
|
||||
);
|
||||
} else {
|
||||
// Fetch the underlying payload buffer from the data message we
|
||||
// are writing into.
|
||||
std::string & out = m_data_msg.msg_ptr->get_raw_payload();
|
||||
|
||||
if (out.size() + m_bytes_needed > base::m_max_message_size) {
|
||||
ec = make_error_code(error::message_too_big);
|
||||
break;
|
||||
}
|
||||
|
||||
// Each frame starts a new masking key. All other state
|
||||
// remains between frames.
|
||||
m_data_msg.prepared_key = prepare_masking_key(
|
||||
@@ -382,7 +396,8 @@ public:
|
||||
m_extended_header
|
||||
)
|
||||
);
|
||||
// TODO: reserve space in the existing message for the new bytes
|
||||
|
||||
out.reserve(out.size() + m_bytes_needed);
|
||||
}
|
||||
m_current_msg = &m_data_msg;
|
||||
}
|
||||
|
||||
@@ -161,13 +161,43 @@ public:
|
||||
|
||||
explicit processor(bool secure, bool p_is_server)
|
||||
: m_secure(secure)
|
||||
, m_server(p_is_server) {}
|
||||
, m_server(p_is_server)
|
||||
, m_max_message_size(config::max_message_size)
|
||||
{}
|
||||
|
||||
virtual ~processor() {}
|
||||
|
||||
/// Get the protocol version of this processor
|
||||
virtual int get_version() const = 0;
|
||||
|
||||
/// Get maximum message size
|
||||
/**
|
||||
* Get maximum message size. Maximum message size determines the point at which the
|
||||
* processor will fail a connection with the message_too_big protocol error.
|
||||
*
|
||||
* The default is retrieved from the max_message_size value from the template config
|
||||
*
|
||||
* @since 0.4.0-alpha1
|
||||
*/
|
||||
size_t get_max_message_size() const {
|
||||
return m_max_message_size;
|
||||
}
|
||||
|
||||
/// Set maximum message size
|
||||
/**
|
||||
* Set maximum message size. Maximum message size determines the point at which the
|
||||
* processor will fail a connection with the message_too_big protocol error.
|
||||
*
|
||||
* The default is retrieved from the max_message_size value from the template config
|
||||
*
|
||||
* @since 0.4.0-alpha1
|
||||
*
|
||||
* @param new_value The value to set as the maximum message size.
|
||||
*/
|
||||
void set_max_message_size(size_t new_value) {
|
||||
m_max_message_size = new_value;
|
||||
}
|
||||
|
||||
/// Returns whether or not the permessage_compress extension is implemented
|
||||
/**
|
||||
* Compile time flag that indicates whether this processor has implemented
|
||||
@@ -196,8 +226,7 @@ public:
|
||||
* @return A status code, 0 on success, non-zero for specific sorts of
|
||||
* failure
|
||||
*/
|
||||
virtual lib::error_code validate_handshake(request_type const & request)
|
||||
const = 0;
|
||||
virtual lib::error_code validate_handshake(request_type const & request) const = 0;
|
||||
|
||||
/// Calculate the appropriate response for this websocket request
|
||||
/**
|
||||
@@ -236,8 +265,7 @@ public:
|
||||
virtual std::string get_raw(response_type const & request) const = 0;
|
||||
|
||||
/// Return the value of the header containing the CORS origin.
|
||||
virtual std::string const & get_origin(request_type const & request)
|
||||
const = 0;
|
||||
virtual std::string const & get_origin(request_type const & request) const = 0;
|
||||
|
||||
/// Extracts requested subprotocols from a handshake request
|
||||
/**
|
||||
@@ -310,8 +338,7 @@ public:
|
||||
* Performs validation, masking, compression, etc. will return an error if
|
||||
* there was an error, otherwise msg will be ready to be written
|
||||
*/
|
||||
virtual lib::error_code prepare_data_frame(message_ptr in, message_ptr out)
|
||||
= 0;
|
||||
virtual lib::error_code prepare_data_frame(message_ptr in, message_ptr out) = 0;
|
||||
|
||||
/// Prepare a ping frame
|
||||
/**
|
||||
@@ -324,8 +351,8 @@ public:
|
||||
*
|
||||
* @return Status code, zero on success, non-zero on failure
|
||||
*/
|
||||
virtual lib::error_code prepare_ping(std::string const & in,
|
||||
message_ptr out) const = 0;
|
||||
virtual lib::error_code prepare_ping(std::string const & in, message_ptr out) const
|
||||
= 0;
|
||||
|
||||
/// Prepare a pong frame
|
||||
/**
|
||||
@@ -338,8 +365,8 @@ public:
|
||||
*
|
||||
* @return Status code, zero on success, non-zero on failure
|
||||
*/
|
||||
virtual lib::error_code prepare_pong(std::string const & in,
|
||||
message_ptr out) const = 0;
|
||||
virtual lib::error_code prepare_pong(std::string const & in, message_ptr out) const
|
||||
= 0;
|
||||
|
||||
/// Prepare a close frame
|
||||
/**
|
||||
@@ -361,6 +388,7 @@ public:
|
||||
protected:
|
||||
bool const m_secure;
|
||||
bool const m_server;
|
||||
size_t m_max_message_size;
|
||||
};
|
||||
|
||||
} // namespace processor
|
||||
|
||||
Reference in New Issue
Block a user