From d9aa4983103a1798c87eda0a79acca11442ffa21 Mon Sep 17 00:00:00 2001 From: Peter Thorson Date: Mon, 10 Feb 2014 08:37:47 -0600 Subject: [PATCH 1/7] whitespace cleanup --- websocketpp/processors/processor.hpp | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/websocketpp/processors/processor.hpp b/websocketpp/processors/processor.hpp index ae3edd5504..505b0e3c3d 100644 --- a/websocketpp/processors/processor.hpp +++ b/websocketpp/processors/processor.hpp @@ -196,8 +196,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 +235,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 +308,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 +321,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 +335,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 /** From ba6320ba1cec1b8ea653c2e21fe445225f7f01f4 Mon Sep 17 00:00:00 2001 From: Peter Thorson Date: Mon, 10 Feb 2014 08:38:54 -0600 Subject: [PATCH 2/7] Add maximum message size functionality to processors --- test/processors/hybi00.cpp | 2 ++ test/processors/hybi07.cpp | 2 ++ test/processors/hybi08.cpp | 2 ++ test/processors/hybi13.cpp | 32 +++++++++++++++++++++++++++ test/processors/processor.cpp | 2 +- websocketpp/config/core.hpp | 12 ++++++++++ websocketpp/config/core_client.hpp | 12 ++++++++++ websocketpp/processors/hybi13.hpp | 14 ++++++++++++ websocketpp/processors/processor.hpp | 33 +++++++++++++++++++++++++++- 9 files changed, 109 insertions(+), 2 deletions(-) diff --git a/test/processors/hybi00.cpp b/test/processors/hybi00.cpp index 65b5f55750..de5a4e6de6 100644 --- a/test/processors/hybi00.cpp +++ b/test/processors/hybi00.cpp @@ -45,6 +45,8 @@ struct stub_config { message_type; typedef websocketpp::message_buffer::alloc::con_msg_manager con_msg_manager_type; + + static const size_t max_message_size = 16000000; }; struct processor_setup { diff --git a/test/processors/hybi07.cpp b/test/processors/hybi07.cpp index 2cddc8a52a..59668082aa 100644 --- a/test/processors/hybi07.cpp +++ b/test/processors/hybi07.cpp @@ -50,6 +50,8 @@ struct stub_config { typedef websocketpp::random::none::int_generator rng_type; + static const size_t max_message_size = 16000000; + /// Extension related config static const bool enable_extensions = false; diff --git a/test/processors/hybi08.cpp b/test/processors/hybi08.cpp index 60da59e797..369de77db8 100644 --- a/test/processors/hybi08.cpp +++ b/test/processors/hybi08.cpp @@ -50,6 +50,8 @@ struct stub_config { typedef websocketpp::random::none::int_generator rng_type; + static const size_t max_message_size = 16000000; + /// Extension related config static const bool enable_extensions = false; diff --git a/test/processors/hybi13.cpp b/test/processors/hybi13.cpp index 0cacb567a7..e4c1bff2f9 100644 --- a/test/processors/hybi13.cpp +++ b/test/processors/hybi13.cpp @@ -60,6 +60,7 @@ struct stub_config { typedef websocketpp::extensions::permessage_deflate::disabled 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_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); diff --git a/test/processors/processor.cpp b/test/processors/processor.cpp index aa3c038b2c..0505646616 100644 --- a/test/processors/processor.cpp +++ b/test/processors/processor.cpp @@ -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); -} +} \ No newline at end of file diff --git a/websocketpp/config/core.hpp b/websocketpp/config/core.hpp index e400da5025..32ddb3ebec 100644 --- a/websocketpp/config/core.hpp +++ b/websocketpp/config/core.hpp @@ -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; diff --git a/websocketpp/config/core_client.hpp b/websocketpp/config/core_client.hpp index 328bcc867c..51cfa85c79 100644 --- a/websocketpp/config/core_client.hpp +++ b/websocketpp/config/core_client.hpp @@ -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; diff --git a/websocketpp/processors/hybi13.hpp b/websocketpp/processors/hybi13.hpp index d7d1a1edfe..14b2d3fa9d 100644 --- a/websocketpp/processors/hybi13.hpp +++ b/websocketpp/processors/hybi13.hpp @@ -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( diff --git a/websocketpp/processors/processor.hpp b/websocketpp/processors/processor.hpp index 505b0e3c3d..69b9ab186a 100644 --- a/websocketpp/processors/processor.hpp +++ b/websocketpp/processors/processor.hpp @@ -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 @@ -358,6 +388,7 @@ public: protected: bool const m_secure; bool const m_server; + size_t m_max_message_size; }; } // namespace processor From 722a67a91032f90491126a72e7ac6b8f4c11059c Mon Sep 17 00:00:00 2001 From: Peter Thorson Date: Mon, 10 Feb 2014 08:39:20 -0600 Subject: [PATCH 3/7] add message payload size reservation --- websocketpp/processors/hybi13.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/websocketpp/processors/hybi13.hpp b/websocketpp/processors/hybi13.hpp index 14b2d3fa9d..92a4e2ea70 100644 --- a/websocketpp/processors/hybi13.hpp +++ b/websocketpp/processors/hybi13.hpp @@ -396,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; } From 81e0c44b34f9b4d219501f00998e80a112d86446 Mon Sep 17 00:00:00 2001 From: Peter Thorson Date: Mon, 10 Feb 2014 09:16:47 -0600 Subject: [PATCH 4/7] update debug config with max_message_size constant --- websocketpp/config/debug.hpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/websocketpp/config/debug.hpp b/websocketpp/config/debug.hpp index 691229a667..a708de142a 100644 --- a/websocketpp/config/debug.hpp +++ b/websocketpp/config/debug.hpp @@ -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; From 7c08cae15bbafba406b297b4a69d698d3cbe948b Mon Sep 17 00:00:00 2001 From: Peter Thorson Date: Mon, 10 Feb 2014 09:17:43 -0600 Subject: [PATCH 5/7] fix bug that caused all error close frames to have a blank code --- websocketpp/impl/connection_impl.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/websocketpp/impl/connection_impl.hpp b/websocketpp/impl/connection_impl.hpp index c8ff46916b..7492a8b998 100644 --- a/websocketpp/impl/connection_impl.hpp +++ b/websocketpp/impl/connection_impl.hpp @@ -932,7 +932,7 @@ void connection::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, From 180543b9e6cec89b95bc7bd756443ca28364c401 Mon Sep 17 00:00:00 2001 From: Peter Thorson Date: Mon, 10 Feb 2014 09:21:07 -0600 Subject: [PATCH 6/7] Adds the ability to specify a maximum message size --- changelog.md | 1 + test/connection/connection.cpp | 27 ++++++++++++ websocketpp/connection.hpp | 40 +++++++++++++++-- websocketpp/endpoint.hpp | 38 +++++++++++++++-- websocketpp/impl/connection_impl.hpp | 64 ++++++++++++++-------------- websocketpp/impl/endpoint_impl.hpp | 3 ++ 6 files changed, 135 insertions(+), 38 deletions(-) diff --git a/changelog.md b/changelog.md index c451629aa0..b0dd0bb227 100644 --- a/changelog.md +++ b/changelog.md @@ -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 diff --git a/test/connection/connection.cpp b/test/connection/connection.cpp index 39043d1fd5..401ea0ff2b 100644 --- a/test/connection/connection.cpp +++ b/test/connection/connection.cpp @@ -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 ) { diff --git a/websocketpp/connection.hpp b/websocketpp/connection.hpp index 6e1e6c5818..cfa8ff4448 100644 --- a/websocketpp/connection.hpp +++ b/websocketpp/connection.hpp @@ -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 /** diff --git a/websocketpp/endpoint.hpp b/websocketpp/endpoint.hpp index 35cc50eac0..6c08b131e8 100644 --- a/websocketpp/endpoint.hpp +++ b/websocketpp/endpoint.hpp @@ -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; diff --git a/websocketpp/impl/connection_impl.hpp b/websocketpp/impl/connection_impl.hpp index 7492a8b998..078eacf979 100644 --- a/websocketpp/impl/connection_impl.hpp +++ b/websocketpp/impl/connection_impl.hpp @@ -1945,49 +1945,49 @@ template typename connection::processor_ptr connection::get_processor(int version) const { // TODO: allow disabling certain versions + + processor_ptr p; + switch (version) { case 0: - return processor_ptr( - new processor::hybi00( - transport_con_type::is_secure(), - m_is_server, - m_msg_manager - ) - ); + p.reset(new processor::hybi00( + transport_con_type::is_secure(), + m_is_server, + m_msg_manager + )); break; case 7: - return processor_ptr( - new processor::hybi07( - transport_con_type::is_secure(), - m_is_server, - m_msg_manager, - m_rng - ) - ); + p.reset(new processor::hybi07( + transport_con_type::is_secure(), + m_is_server, + m_msg_manager, + m_rng + )); break; case 8: - return processor_ptr( - new processor::hybi08( - transport_con_type::is_secure(), - m_is_server, - m_msg_manager, - m_rng - ) - ); + p.reset(new processor::hybi08( + transport_con_type::is_secure(), + m_is_server, + m_msg_manager, + m_rng + )); break; case 13: - return processor_ptr( - new processor::hybi13( - transport_con_type::is_secure(), - m_is_server, - m_msg_manager, - m_rng - ) - ); + p.reset(new processor::hybi13( + 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 diff --git a/websocketpp/impl/endpoint_impl.hpp b/websocketpp/impl/endpoint_impl.hpp index 3101ec8bdd..356858dde3 100644 --- a/websocketpp/impl/endpoint_impl.hpp +++ b/websocketpp/impl/endpoint_impl.hpp @@ -74,6 +74,9 @@ endpoint::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; From 1622cdde042f21fd96bd0105a68985c0135210f2 Mon Sep 17 00:00:00 2001 From: Peter Thorson Date: Mon, 10 Feb 2014 20:51:37 -0600 Subject: [PATCH 7/7] Retry the travis build --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index b0dd0bb227..344d9f138b 100644 --- a/changelog.md +++ b/changelog.md @@ -13,7 +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. +- 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