From 254a4fa7e6c9b904329a48dda789f0eb09aeeb40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20To=C5=A1kan?= Date: Fri, 1 Oct 2021 09:20:36 +0200 Subject: [PATCH 01/40] Fixed C++ compiler warning in alignment calc --- websocketpp/common/md5.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/websocketpp/common/md5.hpp b/websocketpp/common/md5.hpp index 81a676813..9899e1688 100644 --- a/websocketpp/common/md5.hpp +++ b/websocketpp/common/md5.hpp @@ -198,7 +198,7 @@ static void md5_process(md5_state_t *pms, md5_byte_t const * data /*[64]*/) { * On little-endian machines, we can process properly aligned * data without copying it. */ - if (!((data - (md5_byte_t const *)0) & 3)) { + if ((uintptr_t)((const void*)data) % 4 == 0) { /* data are properly aligned */ X = (md5_word_t const *)data; } else { From d8c78d4aeda4e3a52bd9e89c0cc8adc6c03f19fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20To=C5=A1kan?= Date: Thu, 18 Aug 2022 15:35:42 +0200 Subject: [PATCH 02/40] Build fixes and data race fixed --- examples/testee_server/testee_server.cpp | 4 ++-- test/connection/connection.cpp | 4 ++-- test/roles/client.cpp | 4 ++-- test/transport/integration.cpp | 4 ++-- websocketpp/common/network.hpp | 23 +++++------------------ websocketpp/config/core.hpp | 4 ++-- websocketpp/config/core_client.hpp | 4 ++-- websocketpp/config/debug.hpp | 4 ++-- websocketpp/config/minimal_server.hpp | 4 ++-- websocketpp/endpoint.hpp | 5 +++-- websocketpp/frame.hpp | 2 +- websocketpp/logger/levels.hpp | 6 +++--- websocketpp/logger/stub.hpp | 4 ++-- websocketpp/logger/syslog.hpp | 6 +++--- 14 files changed, 33 insertions(+), 45 deletions(-) diff --git a/examples/testee_server/testee_server.cpp b/examples/testee_server/testee_server.cpp index cf876f7e1..69b1abadf 100644 --- a/examples/testee_server/testee_server.cpp +++ b/examples/testee_server/testee_server.cpp @@ -61,9 +61,9 @@ struct testee_config : public websocketpp::config::asio { typedef websocketpp::transport::asio::endpoint transport_type; - static const websocketpp::log::level elog_level = + static constexpr websocketpp::log::level elog_level = websocketpp::log::elevel::none; - static const websocketpp::log::level alog_level = + static constexpr websocketpp::log::level alog_level = websocketpp::log::alevel::none; /// permessage_compress extension diff --git a/test/connection/connection.cpp b/test/connection/connection.cpp index 4cb2aca57..0b48e7f1f 100644 --- a/test/connection/connection.cpp +++ b/test/connection/connection.cpp @@ -127,8 +127,8 @@ struct debug_config_client : public websocketpp::config::core { typedef core::endpoint_base endpoint_base; typedef connection_extension connection_base; - static const websocketpp::log::level elog_level = websocketpp::log::elevel::none; - static const websocketpp::log::level alog_level = websocketpp::log::alevel::none; + static constexpr websocketpp::log::level elog_level = websocketpp::log::elevel::none; + static constexpr websocketpp::log::level alog_level = websocketpp::log::alevel::none; }; struct connection_setup { diff --git a/test/roles/client.cpp b/test/roles/client.cpp index a58556249..b9d8ba2d0 100644 --- a/test/roles/client.cpp +++ b/test/roles/client.cpp @@ -57,8 +57,8 @@ struct stub_config : public websocketpp::config::core { typedef core::endpoint_base endpoint_base; - static const websocketpp::log::level elog_level = websocketpp::log::elevel::none; - static const websocketpp::log::level alog_level = websocketpp::log::alevel::none; + static constexpr websocketpp::log::level elog_level = websocketpp::log::elevel::none; + static constexpr websocketpp::log::level alog_level = websocketpp::log::alevel::none; }; typedef websocketpp::client client; diff --git a/test/transport/integration.cpp b/test/transport/integration.cpp index c083cfd01..473c5df40 100644 --- a/test/transport/integration.cpp +++ b/test/transport/integration.cpp @@ -69,8 +69,8 @@ struct config : public websocketpp::config::asio_client { typedef websocketpp::transport::asio::endpoint transport_type; - //static const websocketpp::log::level elog_level = websocketpp::log::elevel::all; - //static const websocketpp::log::level alog_level = websocketpp::log::alevel::all; + //static constexpr websocketpp::log::level elog_level = websocketpp::log::elevel::all; + //static constexpr websocketpp::log::level alog_level = websocketpp::log::alevel::all; /// Length of time before an opening handshake is aborted static const long timeout_open_handshake = 500; diff --git a/websocketpp/common/network.hpp b/websocketpp/common/network.hpp index 26a4cb9ea..4bd838349 100644 --- a/websocketpp/common/network.hpp +++ b/websocketpp/common/network.hpp @@ -37,21 +37,12 @@ #endif #include +#include namespace websocketpp { namespace lib { namespace net { -inline bool is_little_endian() { - short int val = 0x1; - char *ptr = reinterpret_cast(&val); - return (ptr[0] == 1); -} - -#define TYP_INIT 0 -#define TYP_SMLE 1 -#define TYP_BIGE 2 - /// Convert 64 bit value to network byte order /** * This method is prefixed to avoid conflicts with operating system level @@ -64,18 +55,14 @@ inline bool is_little_endian() { * @return src converted to network byte order */ inline uint64_t _htonll(uint64_t src) { - static int typ = TYP_INIT; - unsigned char c; + if constexpr (::std::endian::native == ::std::endian::big) + return src; + union { uint64_t ull; unsigned char c[8]; } x; - if (typ == TYP_INIT) { - x.ull = 0x01; - typ = (x.c[7] == 0x01ULL) ? TYP_BIGE : TYP_SMLE; - } - if (typ == TYP_BIGE) - return src; + unsigned char c; x.ull = src; c = x.c[0]; x.c[0] = x.c[7]; x.c[7] = c; c = x.c[1]; x.c[1] = x.c[6]; x.c[6] = c; diff --git a/websocketpp/config/core.hpp b/websocketpp/config/core.hpp index 93981aa0e..b056ce5a7 100644 --- a/websocketpp/config/core.hpp +++ b/websocketpp/config/core.hpp @@ -173,7 +173,7 @@ struct core { * * Default is all except for development/debug level errors */ - static const websocketpp::log::level elog_level = + static constexpr websocketpp::log::level elog_level = websocketpp::log::elevel::all ^ websocketpp::log::elevel::devel; /// Default static access logging channels @@ -186,7 +186,7 @@ struct core { * * Default is all except for development/debug level access messages */ - static const websocketpp::log::level alog_level = + static constexpr websocketpp::log::level alog_level = websocketpp::log::alevel::all ^ websocketpp::log::alevel::devel; /// Size of the per-connection read buffer diff --git a/websocketpp/config/core_client.hpp b/websocketpp/config/core_client.hpp index dadf8a4e7..28b2e9c93 100644 --- a/websocketpp/config/core_client.hpp +++ b/websocketpp/config/core_client.hpp @@ -181,7 +181,7 @@ struct core_client { * * Default is all except for development/debug level errors */ - static const websocketpp::log::level elog_level = + static constexpr websocketpp::log::level elog_level = websocketpp::log::elevel::all ^ websocketpp::log::elevel::devel; /// Default static access logging channels @@ -194,7 +194,7 @@ struct core_client { * * Default is all except for development/debug level access messages */ - static const websocketpp::log::level alog_level = + static constexpr websocketpp::log::level alog_level = websocketpp::log::alevel::all ^ websocketpp::log::alevel::devel; /// diff --git a/websocketpp/config/debug.hpp b/websocketpp/config/debug.hpp index 223f72fbb..fa841e236 100644 --- a/websocketpp/config/debug.hpp +++ b/websocketpp/config/debug.hpp @@ -173,7 +173,7 @@ struct debug_core { * * Default is all except for development/debug level errors */ - static const websocketpp::log::level elog_level = + static constexpr websocketpp::log::level elog_level = websocketpp::log::elevel::all; /// Default static access logging channels @@ -186,7 +186,7 @@ struct debug_core { * * Default is all except for development/debug level access messages */ - static const websocketpp::log::level alog_level = + static constexpr websocketpp::log::level alog_level = websocketpp::log::alevel::all; /// diff --git a/websocketpp/config/minimal_server.hpp b/websocketpp/config/minimal_server.hpp index dd1aedb9d..01c12cb3b 100644 --- a/websocketpp/config/minimal_server.hpp +++ b/websocketpp/config/minimal_server.hpp @@ -199,7 +199,7 @@ struct minimal_server { * * Default is all except for development/debug level errors */ - static const websocketpp::log::level elog_level = + static constexpr websocketpp::log::level elog_level = websocketpp::log::elevel::none; /// Default static access logging channels @@ -212,7 +212,7 @@ struct minimal_server { * * Default is all except for development/debug level access messages */ - static const websocketpp::log::level alog_level = + static constexpr websocketpp::log::level alog_level = websocketpp::log::alevel::none; /// diff --git a/websocketpp/endpoint.hpp b/websocketpp/endpoint.hpp index 10f525689..59ff5c06d 100644 --- a/websocketpp/endpoint.hpp +++ b/websocketpp/endpoint.hpp @@ -32,6 +32,7 @@ #include +#include #include #include @@ -91,8 +92,8 @@ class endpoint : public config::transport_type, public config::endpoint_base { //friend connection; explicit endpoint(bool p_is_server) - : m_alog(new alog_type(config::alog_level, log::channel_type_hint::access)) - , m_elog(new elog_type(config::elog_level, log::channel_type_hint::error)) + : m_alog(lib::make_shared(config::alog_level, log::channel_type_hint::access)) + , m_elog(lib::make_shared(config::elog_level, log::channel_type_hint::error)) , m_user_agent(::websocketpp::user_agent) , m_open_handshake_timeout_dur(config::timeout_open_handshake) , m_close_handshake_timeout_dur(config::timeout_close_handshake) diff --git a/websocketpp/frame.hpp b/websocketpp/frame.hpp index 34f2846b8..f2bd45bf6 100644 --- a/websocketpp/frame.hpp +++ b/websocketpp/frame.hpp @@ -613,7 +613,7 @@ inline size_t circshift_prepared_key(size_t prepared_key, size_t offset) { if (offset == 0) { return prepared_key; } - if (lib::net::is_little_endian()) { + if (::std::endian::native == ::std::endian::little) { size_t temp = prepared_key << (sizeof(size_t)-offset)*8; return (prepared_key >> offset*8) | temp; } else { diff --git a/websocketpp/logger/levels.hpp b/websocketpp/logger/levels.hpp index cd7ccd690..4adce8e94 100644 --- a/websocketpp/logger/levels.hpp +++ b/websocketpp/logger/levels.hpp @@ -48,11 +48,11 @@ struct channel_type_hint { typedef uint32_t value; /// No information - static value const none = 0; + static constexpr value none = 0; /// Access log - static value const access = 1; + static constexpr value access = 1; /// Error log - static value const error = 2; + static constexpr value error = 2; }; /// Package of log levels for logging errors diff --git a/websocketpp/logger/stub.hpp b/websocketpp/logger/stub.hpp index 2db6da7df..e805a7653 100644 --- a/websocketpp/logger/stub.hpp +++ b/websocketpp/logger/stub.hpp @@ -44,14 +44,14 @@ class stub { /** * @param hint A channel type specific hint for how to construct the logger */ - explicit stub(channel_type_hint::value) {} + explicit stub(channel_type_hint) {} /// Construct the logger /** * @param default_channels A set of channels to statically enable * @param hint A channel type specific hint for how to construct the logger */ - stub(level, channel_type_hint::value) {} + stub(level, channel_type_hint) {} _WEBSOCKETPP_CONSTEXPR_TOKEN_ stub() {} /// Dynamically enable the given list of channels diff --git a/websocketpp/logger/syslog.hpp b/websocketpp/logger/syslog.hpp index 513abee4a..96fbd0d89 100644 --- a/websocketpp/logger/syslog.hpp +++ b/websocketpp/logger/syslog.hpp @@ -51,7 +51,7 @@ class syslog : public basic { /** * @param hint A channel type specific hint for how to construct the logger */ - syslog(channel_type_hint::value hint = + syslog(channel_type_hint hint = channel_type_hint::access) : basic(hint), m_channel_type_hint(hint) {} @@ -60,7 +60,7 @@ class syslog : public basic { * @param channels A set of channels to statically enable * @param hint A channel type specific hint for how to construct the logger */ - syslog(level channels, channel_type_hint::value hint = + syslog(level channels, channel_type_hint hint = channel_type_hint::access) : basic(channels, hint), m_channel_type_hint(hint) {} @@ -137,7 +137,7 @@ class syslog : public basic { return default_level; } - channel_type_hint::value m_channel_type_hint; + channel_type_hint m_channel_type_hint; }; } // log From d590e6a8880fe4a3a34885a610018b6c9ed0d796 Mon Sep 17 00:00:00 2001 From: begasus Date: Wed, 13 Nov 2019 10:13:46 +0100 Subject: [PATCH 03/40] Haiku check for -lnetwork --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9043db327..d7d94ec12 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -158,6 +158,7 @@ if (BUILD_TESTS OR BUILD_EXAMPLES) else() set (WEBSOCKETPP_PLATFORM_LIBS pthread rt) endif() + endif() set (WEBSOCKETPP_PLATFORM_TLS_LIBS ssl crypto) set (WEBSOCKETPP_BOOST_LIBS system thread) set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") From 47a9d3c8b1778beb9a4aa5ab678d204af8994203 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20To=C5=A1kan?= Date: Thu, 18 Aug 2022 17:30:38 +0200 Subject: [PATCH 04/40] Added move semantics --- websocketpp/connection.hpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/websocketpp/connection.hpp b/websocketpp/connection.hpp index f1e311ea2..8a239f0ec 100644 --- a/websocketpp/connection.hpp +++ b/websocketpp/connection.hpp @@ -1107,16 +1107,15 @@ class connection * (exception free version) */ void set_body(std::string const & value); + + /// @copydoc websocketpp::connection::set_body(std::string const &) + void set_body(std::string && value); #endif // _WEBSOCKETPP_NO_EXCEPTIONS_ #ifdef _WEBSOCKETPP_MOVE_SEMANTICS_ /// @copydoc websocketpp::connection::set_body(std::string const &, lib::error_code &) void set_body(std::string && value, lib::error_code & ec); -#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ - /// @copydoc websocketpp::connection::set_body(std::string const &) - void set_body(std::string && value); -#endif // _WEBSOCKETPP_NO_EXCEPTIONS_ #endif // _WEBSOCKETPP_MOVE_SEMANTICS_ /// Append a header (exception free) From e7d3eef028a95b27ad481c13b11b46a6fb318d52 Mon Sep 17 00:00:00 2001 From: Peter Thorson Date: Sun, 26 Jul 2020 12:02:32 -0500 Subject: [PATCH 05/40] Add guards for functions requiring C++11 features --- websocketpp/impl/connection_impl.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/websocketpp/impl/connection_impl.hpp b/websocketpp/impl/connection_impl.hpp index 6ab7414af..a7b155fe6 100644 --- a/websocketpp/impl/connection_impl.hpp +++ b/websocketpp/impl/connection_impl.hpp @@ -667,6 +667,7 @@ void connection::append_header(std::string const & key, } } } +#endif // _WEBSOCKETPP_MOVE_SEMANTICS_ #ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ template From b207cc94075802b8fee6cd1d4ac994f9c51fdcc3 Mon Sep 17 00:00:00 2001 From: Peter Thorson Date: Fri, 31 Jul 2020 06:57:49 -0500 Subject: [PATCH 06/40] [feature] Add support for exception free mode fixes #792 --- websocketpp/connection.hpp | 7 +++++-- websocketpp/impl/connection_impl.hpp | 1 - 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/websocketpp/connection.hpp b/websocketpp/connection.hpp index 8a239f0ec..428035c93 100644 --- a/websocketpp/connection.hpp +++ b/websocketpp/connection.hpp @@ -1108,11 +1108,14 @@ class connection */ void set_body(std::string const & value); - /// @copydoc websocketpp::connection::set_body(std::string const &) - void set_body(std::string && value); #endif // _WEBSOCKETPP_NO_EXCEPTIONS_ #ifdef _WEBSOCKETPP_MOVE_SEMANTICS_ +#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ + /// @copydoc websocketpp::connection::set_body(std::string const &) + void set_body(std::string && value); +#endif + /// @copydoc websocketpp::connection::set_body(std::string const &, lib::error_code &) void set_body(std::string && value, lib::error_code & ec); diff --git a/websocketpp/impl/connection_impl.hpp b/websocketpp/impl/connection_impl.hpp index a7b155fe6..6ab7414af 100644 --- a/websocketpp/impl/connection_impl.hpp +++ b/websocketpp/impl/connection_impl.hpp @@ -667,7 +667,6 @@ void connection::append_header(std::string const & key, } } } -#endif // _WEBSOCKETPP_MOVE_SEMANTICS_ #ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ template From 5904e8d6a1668240255c39373f79ce8f8d80454f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20To=C5=A1kan?= Date: Thu, 18 Aug 2022 17:27:50 +0200 Subject: [PATCH 07/40] Fixed some no_exception cases --- websocketpp/connection.hpp | 36 ++++++++++++--------- websocketpp/endpoint.hpp | 2 ++ websocketpp/transport/asio/security/tls.hpp | 2 +- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/websocketpp/connection.hpp b/websocketpp/connection.hpp index 428035c93..a3318b252 100644 --- a/websocketpp/connection.hpp +++ b/websocketpp/connection.hpp @@ -777,6 +777,11 @@ class connection /// exception free variant of pong void pong(std::string const & payload, lib::error_code & ec); +#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ + // exception variant of close + void close(close::status::value const code, std::string const & reason); +#endif // _WEBSOCKETPP_NO_EXCEPTIONS_ + /// Close the connection /** * Initiates the close handshake process. @@ -797,9 +802,6 @@ class connection * @param code The close code to send * @param reason The close reason to send */ - void close(close::status::value const code, std::string const & reason); - - /// exception free variant of close void close(close::status::value const code, std::string const & reason, lib::error_code & ec); @@ -1031,9 +1033,8 @@ class connection * @see websocketpp::http::status_code::value (list of valid codes) */ void set_status(http::status_code::value code); -#endif // _WEBSOCKETPP_NO_EXCEPTIONS_ - - /// Set response status code and message (exception free) + + /// Set response status code and message (exception) /** * Sets the response status code and message to independent custom values. * use set_status(status_code::value) to set the code and have the standard @@ -1042,18 +1043,16 @@ class connection * This member function is valid only from the http() and validate() handler * callbacks. * - * @since 0.9.0 - * * @param[in] code Code to set * @param[in] msg Message to set - * @param[out] ec A status code describing the outcome of the operation - * @see websocketpp::http::response::set_status() + * @throw websocketpp::exception + * @see websocketpp::http::parser::response::set_status() * @see websocketpp::http::status_code::value (list of valid codes) */ - void set_status(http::status_code::value code, std::string const & msg, - lib::error_code & ec); + void set_status(http::status_code::value code, std::string const & msg); +#endif // _WEBSOCKETPP_NO_EXCEPTIONS_ - /// Set response status code and message (exception) + /// Set response status code and message (exception free) /** * Sets the response status code and message to independent custom values. * use set_status(status_code::value) to set the code and have the standard @@ -1062,13 +1061,16 @@ class connection * This member function is valid only from the http() and validate() handler * callbacks. * + * @since 0.9.0 + * * @param[in] code Code to set * @param[in] msg Message to set - * @throw websocketpp::exception - * @see websocketpp::http::parser::response::set_status() + * @param[out] ec A status code describing the outcome of the operation + * @see websocketpp::http::response::set_status() * @see websocketpp::http::status_code::value (list of valid codes) */ - void set_status(http::status_code::value code, std::string const & msg); + void set_status(http::status_code::value code, std::string const & msg, + lib::error_code & ec); /// Set response body content (exception free) /** @@ -1145,6 +1147,7 @@ class connection void append_header(std::string const & key, std::string const & val, lib::error_code & ec); +#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ /// Append a header (exception) /** * Set the value of a header in the handshake HTTP request or response. If @@ -1165,6 +1168,7 @@ class connection * lib::error_code &) (exception free version) */ void append_header(std::string const & key, std::string const & val); +#endif // _WEBSOCKETPP_NO_EXCEPTIONS_ /// Replace a header (exception free) /** diff --git a/websocketpp/endpoint.hpp b/websocketpp/endpoint.hpp index 59ff5c06d..b6bca189b 100644 --- a/websocketpp/endpoint.hpp +++ b/websocketpp/endpoint.hpp @@ -583,8 +583,10 @@ class endpoint : public config::transport_type, public config::endpoint_base { void close(connection_hdl hdl, close::status::value const code, std::string const & reason, lib::error_code & ec); +#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ void close(connection_hdl hdl, close::status::value const code, std::string const & reason); +#endif // _WEBSOCKETPP_NO_EXCEPTIONS_ /// Send a ping to a specific connection /** diff --git a/websocketpp/transport/asio/security/tls.hpp b/websocketpp/transport/asio/security/tls.hpp index 25b0d29d0..76e5b7d09 100644 --- a/websocketpp/transport/asio/security/tls.hpp +++ b/websocketpp/transport/asio/security/tls.hpp @@ -239,7 +239,7 @@ class connection : public lib::enable_shared_from_this { lib::asio::error_code ec_addr; // run the hostname through make_address to check if it is a valid IP literal - lib::asio::ip::address addr = lib::asio::ip::make_address(host, ec_addr); + lib::asio::ip::make_address(host, ec_addr); // If the parsing as an IP literal fails, proceed to register the hostname // with the TLS handshake via SNI. From c6e1f905f823aa1285d938a148070f9216f35d67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20To=C5=A1kan?= Date: Thu, 18 Aug 2022 18:04:41 +0200 Subject: [PATCH 08/40] Fixed boost incompatibility --- websocketpp/transport/asio/security/tls.hpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/websocketpp/transport/asio/security/tls.hpp b/websocketpp/transport/asio/security/tls.hpp index 76e5b7d09..b01b1f401 100644 --- a/websocketpp/transport/asio/security/tls.hpp +++ b/websocketpp/transport/asio/security/tls.hpp @@ -238,8 +238,12 @@ class connection : public lib::enable_shared_from_this { std::string const & host = m_uri->get_host(); lib::asio::error_code ec_addr; - // run the hostname through make_address to check if it is a valid IP literal + // run the hostname through make_address to check if it is a valid IP literal +#if (BOOST_VERSION/100000) == 1 && ((BOOST_VERSION/100)%1000) < 66 + lib::asio::ip::address::from_string(host, ec_addr); +#else lib::asio::ip::make_address(host, ec_addr); +#endif // BOOST_VERSION // If the parsing as an IP literal fails, proceed to register the hostname // with the TLS handshake via SNI. From aef0a48ccebc95e46933cddfde437964bc7a9432 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20To=C5=A1kan?= Date: Mon, 7 Nov 2022 11:48:23 +0100 Subject: [PATCH 09/40] Fixed shutdown problem in OpenSSL 3 --- websocketpp/transport/asio/security/tls.hpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/websocketpp/transport/asio/security/tls.hpp b/websocketpp/transport/asio/security/tls.hpp index b01b1f401..dea07b021 100644 --- a/websocketpp/transport/asio/security/tls.hpp +++ b/websocketpp/transport/asio/security/tls.hpp @@ -339,6 +339,7 @@ class connection : public lib::enable_shared_from_this { } void async_shutdown(socket::shutdown_handler callback) { + callback = lib::bind(&connection::handle_async_shutdown, this, callback, lib::placeholders::_1); if (m_strand) { m_socket->async_shutdown(m_strand->wrap(callback)); } else { @@ -346,6 +347,21 @@ class connection : public lib::enable_shared_from_this { } } + void handle_async_shutdown(socket::shutdown_handler callback, lib::asio::error_code const & ec) + { + if (ec == lib::asio::error::operation_aborted) + { + const int shutdownState = SSL_get_shutdown(get_socket().native_handle()); + if (shutdownState & SSL_RECEIVED_SHUTDOWN) + { + callback(lib::asio::error_code(lib::asio::error::not_connected, ec.category())); + return; + } + } + + callback(ec); + } + public: /// Translate any security policy specific information about an error code /** From de9052786c18e0b91866e0b3f78113eb713950bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20To=C5=A1kan?= Date: Tue, 3 Jan 2023 21:01:08 +0100 Subject: [PATCH 10/40] Feature: Added deferred handshake option --- examples/debug_server/debug_server.cpp | 4 +- .../subprotocol_server/subprotocol_server.cpp | 4 +- test/connection/connection.cpp | 4 +- test/roles/server.cpp | 4 +- websocketpp/connection.hpp | 23 ++++++- websocketpp/impl/connection_impl.hpp | 65 +++++++++++++------ 6 files changed, 74 insertions(+), 30 deletions(-) diff --git a/examples/debug_server/debug_server.cpp b/examples/debug_server/debug_server.cpp index 8d1ff0ee6..9427300e0 100644 --- a/examples/debug_server/debug_server.cpp +++ b/examples/debug_server/debug_server.cpp @@ -93,9 +93,9 @@ using websocketpp::lib::bind; // pull out the type of messages sent by our config typedef server::message_ptr message_ptr; -bool validate(server *, websocketpp::connection_hdl) { +session::validation::value validate(server *, websocketpp::connection_hdl) { //sleep(6); - return true; + return session::validation::accept; } void on_http(server* s, websocketpp::connection_hdl hdl) { diff --git a/examples/subprotocol_server/subprotocol_server.cpp b/examples/subprotocol_server/subprotocol_server.cpp index c194d6dc0..c8f17fdec 100644 --- a/examples/subprotocol_server/subprotocol_server.cpp +++ b/examples/subprotocol_server/subprotocol_server.cpp @@ -12,7 +12,7 @@ using websocketpp::lib::bind; using websocketpp::lib::ref; -bool validate(server & s, connection_hdl hdl) { +session::validation::value validate(server & s, connection_hdl hdl) { server::connection_ptr con = s.get_con_from_hdl(hdl); std::cout << "Cache-Control: " << con->get_request_header("Cache-Control") << std::endl; @@ -28,7 +28,7 @@ bool validate(server & s, connection_hdl hdl) { con->select_subprotocol(subp_requests[0]); } - return true; + return session::validation::accept; } int main() { diff --git a/test/connection/connection.cpp b/test/connection/connection.cpp index 0b48e7f1f..8f2fcd28f 100644 --- a/test/connection/connection.cpp +++ b/test/connection/connection.cpp @@ -155,10 +155,10 @@ void validate_func(server* s, websocketpp::connection_hdl hdl, message_ptr msg) s->send(hdl, msg->get_payload(), msg->get_opcode()); } -bool validate_set_ua(server* s, websocketpp::connection_hdl hdl) { +session::validation::value validate_set_ua(server* s, websocketpp::connection_hdl hdl) { server::connection_ptr con = s->get_con_from_hdl(hdl); con->replace_header("Server","foo"); - return true; + return session::validation::accept; } void http_func(server* s, websocketpp::connection_hdl hdl) { diff --git a/test/roles/server.cpp b/test/roles/server.cpp index b283570b9..3d4598a01 100644 --- a/test/roles/server.cpp +++ b/test/roles/server.cpp @@ -91,7 +91,7 @@ void echo_func(server* s, websocketpp::connection_hdl hdl, message_ptr msg) { s->send(hdl, msg->get_payload(), msg->get_opcode()); } -bool validate_func_subprotocol(server* s, std::string* out, std::string accept, +session::validation::value validate_func_subprotocol(server* s, std::string* out, std::string accept, websocketpp::connection_hdl hdl) { server::connection_ptr con = s->get_con_from_hdl(hdl); @@ -111,7 +111,7 @@ bool validate_func_subprotocol(server* s, std::string* out, std::string accept, con->select_subprotocol(accept); } - return true; + return session::validation::accept; } void open_func_subprotocol(server* s, std::string* out, websocketpp::connection_hdl hdl) { diff --git a/websocketpp/connection.hpp b/websocketpp/connection.hpp index a3318b252..295cbc8dd 100644 --- a/websocketpp/connection.hpp +++ b/websocketpp/connection.hpp @@ -48,6 +48,17 @@ namespace websocketpp { +namespace session{ + namespace validation { + // validation types supported by the validate handler + enum value { + reject = 0, + accept = 1, + defer = 2 + }; + } // namespace http_state +} // namespace session + /// The type and function signature of an open handler /** * The open handler is called once for every successful WebSocket connection @@ -126,7 +137,7 @@ typedef lib::function pong_timeout_handler; * should be accepted. Additional methods may be called during the function to * set response headers, set HTTP return/error codes, etc. */ -typedef lib::function validate_handler; +typedef lib::function validate_handler; /// The type and function signature of a http handler /** @@ -1514,6 +1525,12 @@ class connection m_connection_hdl = hdl; transport_con_type::set_handle(hdl); } + + /// Accept or reject a websocket connection request that was previouslly deferred by the validate handler + /** + * @param accept True to accept the websocket connection, false to reject it. + */ + lib::error_code deferred_accept(bool accept); protected: void handle_transport_init(lib::error_code const & ec); @@ -1523,7 +1540,9 @@ class connection /// Perform WebSocket handshake validation of m_request using m_processor. /// set m_response and return an error code indicating status. - lib::error_code process_handshake_request(); + session::validation::value process_handshake_request(lib::error_code & ec); + + lib::error_code finalize_handshake_response(bool accept); private: diff --git a/websocketpp/impl/connection_impl.hpp b/websocketpp/impl/connection_impl.hpp index 6ab7414af..1535e69fd 100644 --- a/websocketpp/impl/connection_impl.hpp +++ b/websocketpp/impl/connection_impl.hpp @@ -1022,12 +1022,13 @@ void connection::handle_read_handshake(lib::error_code const & ec, m_internal_state = istate::PROCESS_HTTP_REQUEST; // We have the complete request. Process it. - lib::error_code handshake_ec = this->process_handshake_request(); + lib::error_code handshake_ec; + session::validation::value action = this->process_handshake_request(handshake_ec); // Write a response if this is a websocket connection or if it is an // HTTP connection for which the response has not been deferred or // started yet by a different system (i.e. still in init state). - if (!m_is_http || m_http_state == session::http_state::init) { + if ((!m_is_http && action != session::validation::defer) || (m_is_http && m_http_state == session::http_state::init)) { this->write_http_response(handshake_ec); } } else { @@ -1153,11 +1154,11 @@ void connection::handle_read_frame(lib::error_code const & ec, lib::error_code consume_ec; - if (m_alog->static_test(log::alevel::devel)) { + /*if (m_alog->static_test(log::alevel::devel)) { std::stringstream s; s << "Processing Bytes: " << utility::to_hex(reinterpret_cast(m_buf)+p,bytes_transferred-p); m_alog->write(log::alevel::devel,s.str()); - } + }*/ p += m_processor->consume( reinterpret_cast(m_buf)+p, @@ -1285,7 +1286,7 @@ lib::error_code connection::initialize_processor() { } template -lib::error_code connection::process_handshake_request() { +session::validation::value connection::process_handshake_request(lib::error_code & ec) { m_alog->write(log::alevel::devel,"process handshake request"); if (!processor::is_websocket_handshake(m_request)) { @@ -1301,7 +1302,8 @@ lib::error_code connection::process_handshake_request() { if (!m_uri->get_valid()) { m_alog->write(log::alevel::devel, "Bad request: failed to parse uri"); m_response.set_status(http::status_code::bad_request); - return error::make_error_code(error::invalid_uri); + ec = error::make_error_code(error::invalid_uri); + return session::validation::reject; } if (m_http_handler) { @@ -1309,24 +1311,27 @@ lib::error_code connection::process_handshake_request() { m_http_handler(m_connection_hdl); if (m_state == session::state::closed) { - return error::make_error_code(error::http_connection_ended); + ec = error::make_error_code(error::http_connection_ended); + return session::validation::reject; } } else { m_response.set_status(http::status_code::upgrade_required); - return error::make_error_code(error::upgrade_required); + ec = error::make_error_code(error::upgrade_required); + return session::validation::reject; } - return lib::error_code(); + ec.clear(); + return session::validation::reject; } - lib::error_code ec = m_processor->validate_handshake(m_request); + ec = m_processor->validate_handshake(m_request); // Validate: make sure all required elements are present. if (ec){ // Not a valid handshake request m_alog->write(log::alevel::devel, "Bad request " + ec.message()); m_response.set_status(http::status_code::bad_request); - return ec; + return session::validation::reject; } // Read extension parameters and set up values necessary for the end user @@ -1339,7 +1344,8 @@ lib::error_code connection::process_handshake_request() { // a failed connection attempt. m_elog->write(log::elevel::info, "Bad request: " + neg_results.first.message()); m_response.set_status(http::status_code::bad_request); - return neg_results.first; + ec = neg_results.first; + return session::validation::reject; } else if (neg_results.first) { // There was a fatal error in extension processing that is probably our // fault. Consider extension negotiation to have failed and continue as @@ -1363,7 +1369,8 @@ lib::error_code connection::process_handshake_request() { if (!m_uri->get_valid()) { m_alog->write(log::alevel::devel, "Bad request: failed to parse uri"); m_response.set_status(http::status_code::bad_request); - return error::make_error_code(error::invalid_uri); + ec = error::make_error_code(error::invalid_uri); + return session::validation::reject; } // extract subprotocols @@ -1375,7 +1382,20 @@ lib::error_code connection::process_handshake_request() { } // Ask application to validate the connection - if (!m_validate_handler || m_validate_handler(m_connection_hdl)) { + session::validation::value action = session::validation::accept; + if (m_validate_handler) + action = m_validate_handler(m_connection_hdl); + + if (action != session::validation::defer) + ec = finalize_handshake_response(action == session::validation::accept); + + return action; +} + +template +lib::error_code connection::finalize_handshake_response(bool accept) { + lib::error_code ec; + if (accept) { m_response.set_status(http::status_code::switching_protocols); // Write the appropriate response headers based on request and @@ -1383,12 +1403,9 @@ lib::error_code connection::process_handshake_request() { ec = m_processor->process_handshake(m_request,m_subprotocol,m_response); if (ec) { - std::stringstream s; - s << "Processing error: " << ec << "(" << ec.message() << ")"; - m_alog->write(log::alevel::devel, s.str()); + log_err(log::elevel::devel, "Processing", ec); m_response.set_status(http::status_code::internal_server_error); - return ec; } } else { // User application has rejected the handshake @@ -1401,10 +1418,18 @@ lib::error_code connection::process_handshake_request() { m_response.set_status(http::status_code::bad_request); } - return error::make_error_code(error::rejected); + ec = error::make_error_code(error::rejected); } - return lib::error_code(); + return ec; +} + +template +lib::error_code connection::deferred_accept(bool accept) +{ + const lib::error_code ec = finalize_handshake_response(accept); + this->write_http_response(ec); + return get_ec(); } template From 215a2782055c81edf3bf3c82379f38641308a302 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20To=C5=A1kan?= Date: Wed, 4 Jan 2023 12:15:27 +0100 Subject: [PATCH 11/40] Changed handlers to use references, reducing call overhead --- docs/simple_broadcast_server.cpp | 6 +-- docs/simple_count_server_thread.cpp | 4 +- .../associative_storage.cpp | 8 ++-- .../broadcast_server/broadcast_server.cpp | 6 +-- examples/debug_client/debug_client.cpp | 12 ++--- examples/debug_server/debug_server.cpp | 10 ++--- examples/echo_client/echo_client.cpp | 2 +- examples/echo_server/echo_server.cpp | 2 +- .../echo_server_both/echo_server_both.cpp | 4 +- examples/echo_server_tls/echo_server_tls.cpp | 6 +-- .../enriched_storage/enriched_storage.cpp | 6 +-- .../external_io_service.cpp | 2 +- examples/handler_switch/handler_switch.cpp | 4 +- examples/iostream_server/iostream_server.cpp | 2 +- examples/scratch_client/scratch_client.cpp | 8 ++-- examples/scratch_server/scratch_server.cpp | 2 +- .../simple_broadcast_server.cpp | 6 +-- examples/sip_client/sip_client.cpp | 4 +- .../subprotocol_server/subprotocol_server.cpp | 2 +- .../telemetry_server/telemetry_server.cpp | 6 +-- examples/testee_client/testee_client.cpp | 2 +- examples/testee_server/testee_server.cpp | 4 +- examples/utility_client/utility_client.cpp | 10 ++--- test/connection/connection.cpp | 20 ++++----- test/connection/connection_tu2.cpp | 2 +- test/connection/connection_tu2.hpp | 2 +- test/roles/server.cpp | 10 ++--- test/transport/integration.cpp | 28 ++++++------ tutorials/utility_client/step4.cpp | 6 +-- tutorials/utility_client/step5.cpp | 8 ++-- tutorials/utility_client/step6.cpp | 10 ++--- tutorials/utility_client/utility_client.md | 20 ++++----- tutorials/utility_server/step2.cpp | 2 +- websocketpp/common/connection_hdl.hpp | 1 + websocketpp/connection.hpp | 20 ++++----- websocketpp/endpoint.hpp | 44 +++++++++---------- websocketpp/impl/endpoint_impl.hpp | 40 ++++++++--------- websocketpp/transport/asio/connection.hpp | 2 +- websocketpp/transport/asio/security/none.hpp | 2 +- websocketpp/transport/asio/security/tls.hpp | 4 +- websocketpp/transport/base/connection.hpp | 2 +- websocketpp/transport/iostream/connection.hpp | 2 +- websocketpp/transport/stub/connection.hpp | 2 +- 43 files changed, 173 insertions(+), 172 deletions(-) diff --git a/docs/simple_broadcast_server.cpp b/docs/simple_broadcast_server.cpp index 955d71115..64eb1b60f 100644 --- a/docs/simple_broadcast_server.cpp +++ b/docs/simple_broadcast_server.cpp @@ -20,15 +20,15 @@ class broadcast_server { m_server.set_message_handler(bind(&broadcast_server::on_message,this,::_1,::_2)); } - void on_open(connection_hdl hdl) { + void on_open(connection_hdl_ref hdl) { m_connections.insert(hdl); } - void on_close(connection_hdl hdl) { + void on_close(connection_hdl_ref hdl) { m_connections.erase(hdl); } - void on_message(connection_hdl hdl, server::message_ptr msg) { + void on_message(connection_hdl_ref hdl, server::message_ptr msg) { for (auto it : m_connections) { m_server.send(it,msg); } diff --git a/docs/simple_count_server_thread.cpp b/docs/simple_count_server_thread.cpp index a30af9948..cfd2ade17 100644 --- a/docs/simple_count_server_thread.cpp +++ b/docs/simple_count_server_thread.cpp @@ -19,12 +19,12 @@ class count_server { m_server.set_close_handler(bind(&count_server::on_close,this,_1)); } - void on_open(connection_hdl hdl) { + void on_open(connection_hdl_ref hdl) { std::lock_guard lock(m_mutex); m_connections.insert(hdl); } - void on_close(connection_hdl hdl) { + void on_close(connection_hdl_ref hdl) { std::lock_guard lock(m_mutex); m_connections.erase(hdl); } diff --git a/examples/associative_storage/associative_storage.cpp b/examples/associative_storage/associative_storage.cpp index ea5956c6d..9a9e08531 100644 --- a/examples/associative_storage/associative_storage.cpp +++ b/examples/associative_storage/associative_storage.cpp @@ -27,7 +27,7 @@ class print_server { m_server.set_message_handler(bind(&print_server::on_message,this,::_1,::_2)); } - void on_open(connection_hdl hdl) { + void on_open(connection_hdl_ref hdl) { connection_data data; data.sessionid = m_next_sessionid++; @@ -36,7 +36,7 @@ class print_server { m_connections[hdl] = data; } - void on_close(connection_hdl hdl) { + void on_close(connection_hdl_ref hdl) { connection_data& data = get_data_from_hdl(hdl); std::cout << "Closing connection " << data.name @@ -50,7 +50,7 @@ class print_server { << lib_ec.message() << "/" << trans_ec.message() << std::endl; } - void on_message(connection_hdl hdl, server::message_ptr msg) { + void on_message(connection_hdl_ref hdl, server::message_ptr msg) { connection_data& data = get_data_from_hdl(hdl); if (data.name.empty()) { @@ -63,7 +63,7 @@ class print_server { } } - connection_data& get_data_from_hdl(connection_hdl hdl) { + connection_data& get_data_from_hdl(connection_hdl_ref hdl) { auto it = m_connections.find(hdl); if (it == m_connections.end()) { diff --git a/examples/broadcast_server/broadcast_server.cpp b/examples/broadcast_server/broadcast_server.cpp index 35976d35a..5b8664f76 100644 --- a/examples/broadcast_server/broadcast_server.cpp +++ b/examples/broadcast_server/broadcast_server.cpp @@ -83,7 +83,7 @@ class broadcast_server { //} } - void on_open(connection_hdl hdl) { + void on_open(connection_hdl_ref hdl) { { lock_guard guard(m_action_lock); //std::cout << "on_open" << std::endl; @@ -92,7 +92,7 @@ class broadcast_server { m_action_cond.notify_one(); } - void on_close(connection_hdl hdl) { + void on_close(connection_hdl_ref hdl) { { lock_guard guard(m_action_lock); //std::cout << "on_close" << std::endl; @@ -101,7 +101,7 @@ class broadcast_server { m_action_cond.notify_one(); } - void on_message(connection_hdl hdl, server::message_ptr msg) { + void on_message(connection_hdl_ref hdl, server::message_ptr msg) { // queue message up for sending by processing thread { lock_guard guard(m_action_lock); diff --git a/examples/debug_client/debug_client.cpp b/examples/debug_client/debug_client.cpp index 457c24934..67c2cf4a6 100644 --- a/examples/debug_client/debug_client.cpp +++ b/examples/debug_client/debug_client.cpp @@ -87,11 +87,11 @@ class perftest { m_endpoint.run(); } - void on_socket_init(websocketpp::connection_hdl) { + void on_socket_init(websocketpp::connection_hdl_ref) { m_socket_init = std::chrono::high_resolution_clock::now(); } - context_ptr on_tls_init(websocketpp::connection_hdl) { + context_ptr on_tls_init(websocketpp::connection_hdl_ref) { m_tls_init = std::chrono::high_resolution_clock::now(); context_ptr ctx = websocketpp::lib::make_shared(boost::asio::ssl::context::tlsv1); @@ -106,7 +106,7 @@ class perftest { return ctx; } - void on_fail(websocketpp::connection_hdl hdl) { + void on_fail(websocketpp::connection_hdl_ref hdl) { client::connection_ptr con = m_endpoint.get_con_from_hdl(hdl); std::cout << "Fail handler" << std::endl; @@ -118,15 +118,15 @@ class perftest { std::cout << con->get_ec() << " - " << con->get_ec().message() << std::endl; } - void on_open(websocketpp::connection_hdl hdl) { + void on_open(websocketpp::connection_hdl_ref hdl) { m_open = std::chrono::high_resolution_clock::now(); m_endpoint.send(hdl, "", websocketpp::frame::opcode::text); } - void on_message(websocketpp::connection_hdl hdl, message_ptr) { + void on_message(websocketpp::connection_hdl_ref hdl, message_ptr) { m_message = std::chrono::high_resolution_clock::now(); m_endpoint.close(hdl,websocketpp::close::status::going_away,""); } - void on_close(websocketpp::connection_hdl) { + void on_close(websocketpp::connection_hdl_ref) { m_close = std::chrono::high_resolution_clock::now(); std::cout << "Socket Init: " << std::chrono::duration_cast(m_socket_init-m_start).count() << std::endl; diff --git a/examples/debug_server/debug_server.cpp b/examples/debug_server/debug_server.cpp index 9427300e0..858d48929 100644 --- a/examples/debug_server/debug_server.cpp +++ b/examples/debug_server/debug_server.cpp @@ -93,12 +93,12 @@ using websocketpp::lib::bind; // pull out the type of messages sent by our config typedef server::message_ptr message_ptr; -session::validation::value validate(server *, websocketpp::connection_hdl) { +session::validation::value validate(server *, websocketpp::connection_hdl_ref) { //sleep(6); return session::validation::accept; } -void on_http(server* s, websocketpp::connection_hdl hdl) { +void on_http(server* s, websocketpp::connection_hdl_ref hdl) { server::connection_ptr con = s->get_con_from_hdl(hdl); std::string res = con->get_request_body(); @@ -110,18 +110,18 @@ void on_http(server* s, websocketpp::connection_hdl hdl) { con->set_status(websocketpp::http::status_code::ok); } -void on_fail(server* s, websocketpp::connection_hdl hdl) { +void on_fail(server* s, websocketpp::connection_hdl_ref hdl) { server::connection_ptr con = s->get_con_from_hdl(hdl); std::cout << "Fail handler: " << con->get_ec() << " " << con->get_ec().message() << std::endl; } -void on_close(websocketpp::connection_hdl) { +void on_close(websocketpp::connection_hdl_ref) { std::cout << "Close handler" << std::endl; } // Define a callback to handle incoming messages -void on_message(server* s, websocketpp::connection_hdl hdl, message_ptr msg) { +void on_message(server* s, websocketpp::connection_hdl_ref hdl, message_ptr msg) { std::cout << "on_message called with hdl: " << hdl.lock().get() << " and message: " << msg->get_payload() << std::endl; diff --git a/examples/echo_client/echo_client.cpp b/examples/echo_client/echo_client.cpp index 59af6614f..9a8cc3f26 100644 --- a/examples/echo_client/echo_client.cpp +++ b/examples/echo_client/echo_client.cpp @@ -41,7 +41,7 @@ typedef websocketpp::config::asio_client::message_type::ptr message_ptr; // This message handler will be invoked once for each incoming message. It // prints the message and then sends a copy of the message back to the server. -void on_message(client* c, websocketpp::connection_hdl hdl, message_ptr msg) { +void on_message(client* c, websocketpp::connection_hdl_ref hdl, message_ptr msg) { std::cout << "on_message called with hdl: " << hdl.lock().get() << " and message: " << msg->get_payload() << std::endl; diff --git a/examples/echo_server/echo_server.cpp b/examples/echo_server/echo_server.cpp index aca3f6a9a..9cddd5864 100644 --- a/examples/echo_server/echo_server.cpp +++ b/examples/echo_server/echo_server.cpp @@ -15,7 +15,7 @@ using websocketpp::lib::error_code; typedef server::message_ptr message_ptr; // Define a callback to handle incoming messages -void on_message(server* s, websocketpp::connection_hdl hdl, message_ptr msg) { +void on_message(server* s, websocketpp::connection_hdl_ref hdl, message_ptr msg) { std::cout << "on_message called with hdl: " << hdl.lock().get() << " and message: " << msg->get_payload() << std::endl; diff --git a/examples/echo_server_both/echo_server_both.cpp b/examples/echo_server_both/echo_server_both.cpp index 06b50514a..93cf93d37 100644 --- a/examples/echo_server_both/echo_server_both.cpp +++ b/examples/echo_server_both/echo_server_both.cpp @@ -20,7 +20,7 @@ typedef websocketpp::lib::shared_ptr context_ptr; // The shared on_message handler takes a template parameter so the function can // resolve any endpoint dependent types like message_ptr or connection_ptr template -void on_message(EndpointType* s, websocketpp::connection_hdl hdl, +void on_message(EndpointType* s, websocketpp::connection_hdl_ref hdl, typename EndpointType::message_ptr msg) { std::cout << "on_message called with hdl: " << hdl.lock().get() @@ -46,7 +46,7 @@ std::string get_password() { return "test"; } -context_ptr on_tls_init(websocketpp::connection_hdl hdl) { +context_ptr on_tls_init(websocketpp::connection_hdl_ref hdl) { std::cout << "on_tls_init called with hdl: " << hdl.lock().get() << std::endl; context_ptr ctx(new boost::asio::ssl::context(boost::asio::ssl::context::tlsv1)); diff --git a/examples/echo_server_tls/echo_server_tls.cpp b/examples/echo_server_tls/echo_server_tls.cpp index ef14a4a22..6890d2605 100644 --- a/examples/echo_server_tls/echo_server_tls.cpp +++ b/examples/echo_server_tls/echo_server_tls.cpp @@ -51,7 +51,7 @@ using websocketpp::lib::error_code; typedef websocketpp::config::asio::message_type::ptr message_ptr; typedef websocketpp::lib::shared_ptr context_ptr; -void on_message(server* s, websocketpp::connection_hdl hdl, message_ptr msg) { +void on_message(server* s, websocketpp::connection_hdl_ref hdl, message_ptr msg) { std::cout << "on_message called with hdl: " << hdl.lock().get() << " and message: " << msg->get_payload() << std::endl; @@ -70,7 +70,7 @@ void on_end_accept(error_code lib_ec, error_code trans_ec) { << lib_ec.message() << "/" << trans_ec.message() << std::endl; } -void on_http(server* s, websocketpp::connection_hdl hdl) { +void on_http(server* s, websocketpp::connection_hdl_ref hdl) { server::connection_ptr con = s->get_con_from_hdl(hdl); con->set_body("Hello World!"); @@ -88,7 +88,7 @@ enum tls_mode { MOZILLA_MODERN = 2 }; -context_ptr on_tls_init(tls_mode mode, websocketpp::connection_hdl hdl) { +context_ptr on_tls_init(tls_mode mode, websocketpp::connection_hdl_ref hdl) { namespace asio = websocketpp::lib::asio; std::cout << "on_tls_init called with hdl: " << hdl.lock().get() << std::endl; diff --git a/examples/enriched_storage/enriched_storage.cpp b/examples/enriched_storage/enriched_storage.cpp index 63a2e8e90..0d9d9b4e7 100644 --- a/examples/enriched_storage/enriched_storage.cpp +++ b/examples/enriched_storage/enriched_storage.cpp @@ -46,13 +46,13 @@ class print_server { m_server.set_message_handler(bind(&print_server::on_message,this,::_1,::_2)); } - void on_open(connection_hdl hdl) { + void on_open(connection_hdl_ref hdl) { connection_ptr con = m_server.get_con_from_hdl(hdl); con->sessionid = m_next_sessionid++; } - void on_close(connection_hdl hdl) { + void on_close(connection_hdl_ref hdl) { connection_ptr con = m_server.get_con_from_hdl(hdl); std::cout << "Closing connection " << con->name @@ -64,7 +64,7 @@ class print_server { << lib_ec.message() << "/" << trans_ec.message() << std::endl; } - void on_message(connection_hdl hdl, server::message_ptr msg) { + void on_message(connection_hdl_ref hdl, server::message_ptr msg) { connection_ptr con = m_server.get_con_from_hdl(hdl); if (con->name.empty()) { diff --git a/examples/external_io_service/external_io_service.cpp b/examples/external_io_service/external_io_service.cpp index 6aa23985e..8d84b286a 100644 --- a/examples/external_io_service/external_io_service.cpp +++ b/examples/external_io_service/external_io_service.cpp @@ -39,7 +39,7 @@ using websocketpp::lib::error_code; typedef websocketpp::server ws_echo_server; // Define a callback to handle incoming messages -void on_message(ws_echo_server* s, websocketpp::connection_hdl hdl, ws_echo_server::message_ptr msg) { +void on_message(ws_echo_server* s, websocketpp::connection_hdl_ref hdl, ws_echo_server::message_ptr msg) { std::cout << "on_message called with hdl: " << hdl.lock().get() << " and message: " << msg->get_payload() << std::endl; diff --git a/examples/handler_switch/handler_switch.cpp b/examples/handler_switch/handler_switch.cpp index 8e56a91b1..9e3027125 100644 --- a/examples/handler_switch/handler_switch.cpp +++ b/examples/handler_switch/handler_switch.cpp @@ -11,11 +11,11 @@ using websocketpp::lib::placeholders::_2; using websocketpp::lib::bind; using websocketpp::lib::ref; -void custom_on_msg(server & s, connection_hdl hdl, server::message_ptr msg) { +void custom_on_msg(server & s, connection_hdl_ref hdl, server::message_ptr msg) { std::cout << "Message sent to custom handler" << std::endl; } -void default_on_msg(server & s, connection_hdl hdl, server::message_ptr msg) { +void default_on_msg(server & s, connection_hdl_ref hdl, server::message_ptr msg) { std::cout << "Message sent to default handler" << std::endl; if (msg->get_payload() == "upgrade") { diff --git a/examples/iostream_server/iostream_server.cpp b/examples/iostream_server/iostream_server.cpp index 16d59c4b3..d070cccb6 100644 --- a/examples/iostream_server/iostream_server.cpp +++ b/examples/iostream_server/iostream_server.cpp @@ -16,7 +16,7 @@ using websocketpp::lib::error_code; typedef server::message_ptr message_ptr; // Define a callback to handle incoming messages -void on_message(server* s, websocketpp::connection_hdl hdl, message_ptr msg) { +void on_message(server* s, websocketpp::connection_hdl_ref hdl, message_ptr msg) { if (msg->get_opcode() == websocketpp::frame::opcode::text) { s->get_alog().write(websocketpp::log::alevel::app, "Text Message Received: "+msg->get_payload()); diff --git a/examples/scratch_client/scratch_client.cpp b/examples/scratch_client/scratch_client.cpp index a6a1fbcfe..1856fd42e 100644 --- a/examples/scratch_client/scratch_client.cpp +++ b/examples/scratch_client/scratch_client.cpp @@ -46,7 +46,7 @@ class connection_metadata { public: typedef websocketpp::lib::shared_ptr ptr; - connection_metadata(int id, websocketpp::connection_hdl hdl, std::string uri) + connection_metadata(int id, websocketpp::connection_hdl_ref hdl, std::string uri) : m_id(id) , m_hdl(hdl) , m_status("Connecting") @@ -54,14 +54,14 @@ class connection_metadata { , m_server("N/A") {} - void on_open(client * c, websocketpp::connection_hdl hdl) { + void on_open(client * c, websocketpp::connection_hdl_ref hdl) { m_status = "Open"; client::connection_ptr con = c->get_con_from_hdl(hdl); m_server = con->get_response_header("Server"); } - void on_fail(client * c, websocketpp::connection_hdl hdl) { + void on_fail(client * c, websocketpp::connection_hdl_ref hdl) { m_status = "Failed"; client::connection_ptr con = c->get_con_from_hdl(hdl); @@ -69,7 +69,7 @@ class connection_metadata { m_error_reason = con->get_ec().message(); } - void on_close(client * c, websocketpp::connection_hdl hdl) { + void on_close(client * c, websocketpp::connection_hdl_ref hdl) { m_status = "Closed"; client::connection_ptr con = c->get_con_from_hdl(hdl); std::stringstream s; diff --git a/examples/scratch_server/scratch_server.cpp b/examples/scratch_server/scratch_server.cpp index 901ae8e5d..ab536a078 100644 --- a/examples/scratch_server/scratch_server.cpp +++ b/examples/scratch_server/scratch_server.cpp @@ -53,7 +53,7 @@ typedef websocketpp::server server; typedef server::message_ptr message_ptr; // Define a callback to handle incoming messages -void on_message(server* s, websocketpp::connection_hdl hdl, message_ptr msg) { +void on_message(server* s, websocketpp::connection_hdl_ref hdl, message_ptr msg) { /*std::cout << "on_message called with hdl: " << hdl.lock().get() << " and message (" << msg->get_payload().size() << "): " << msg->get_payload() << std::endl; diff --git a/examples/simple_broadcast_server/simple_broadcast_server.cpp b/examples/simple_broadcast_server/simple_broadcast_server.cpp index bdea98732..dcaa8a881 100644 --- a/examples/simple_broadcast_server/simple_broadcast_server.cpp +++ b/examples/simple_broadcast_server/simple_broadcast_server.cpp @@ -20,11 +20,11 @@ class broadcast_server { m_server.set_message_handler(bind(&broadcast_server::on_message,this,::_1,::_2)); } - void on_open(connection_hdl hdl) { + void on_open(connection_hdl_ref hdl) { m_connections.insert(hdl); } - void on_close(connection_hdl hdl) { + void on_close(connection_hdl_ref hdl) { m_connections.erase(hdl); } @@ -33,7 +33,7 @@ class broadcast_server { << lib_ec.message() << "/" << trans_ec.message() << std::endl; } - void on_message(connection_hdl hdl, server::message_ptr msg) { + void on_message(connection_hdl_ref hdl, server::message_ptr msg) { for (auto it : m_connections) { m_server.send(it,msg); } diff --git a/examples/sip_client/sip_client.cpp b/examples/sip_client/sip_client.cpp index 66fa85784..6988a1655 100644 --- a/examples/sip_client/sip_client.cpp +++ b/examples/sip_client/sip_client.cpp @@ -23,7 +23,7 @@ client sip_client; bool received; -void on_open(client* c, websocketpp::connection_hdl hdl) { +void on_open(client* c, websocketpp::connection_hdl_ref hdl) { // now it is safe to use the connection std::cout << "connection ready" << std::endl; @@ -33,7 +33,7 @@ void on_open(client* c, websocketpp::connection_hdl hdl) { sip_client.send(hdl, SIP_msg.c_str(), websocketpp::frame::opcode::text); } -void on_message(client* c, websocketpp::connection_hdl hdl, message_ptr msg) { +void on_message(client* c, websocketpp::connection_hdl_ref hdl, message_ptr msg) { client::connection_ptr con = sip_client.get_con_from_hdl(hdl); std::cout << "Received a reply:" << std::endl; diff --git a/examples/subprotocol_server/subprotocol_server.cpp b/examples/subprotocol_server/subprotocol_server.cpp index c8f17fdec..9178d4822 100644 --- a/examples/subprotocol_server/subprotocol_server.cpp +++ b/examples/subprotocol_server/subprotocol_server.cpp @@ -12,7 +12,7 @@ using websocketpp::lib::bind; using websocketpp::lib::ref; -session::validation::value validate(server & s, connection_hdl hdl) { +session::validation::value validate(server & s, connection_hdl_ref hdl) { server::connection_ptr con = s.get_con_from_hdl(hdl); std::cout << "Cache-Control: " << con->get_request_header("Cache-Control") << std::endl; diff --git a/examples/telemetry_server/telemetry_server.cpp b/examples/telemetry_server/telemetry_server.cpp index 22c155edf..7c55a801c 100644 --- a/examples/telemetry_server/telemetry_server.cpp +++ b/examples/telemetry_server/telemetry_server.cpp @@ -109,7 +109,7 @@ class telemetry_server { set_timer(); } - void on_http(connection_hdl hdl) { + void on_http(connection_hdl_ref hdl) { // Upgrade our connection handle to a full connection_ptr server::connection_ptr con = m_endpoint.get_con_from_hdl(hdl); @@ -156,11 +156,11 @@ class telemetry_server { con->set_status(websocketpp::http::status_code::ok); } - void on_open(connection_hdl hdl) { + void on_open(connection_hdl_ref hdl) { m_connections.insert(hdl); } - void on_close(connection_hdl hdl) { + void on_close(connection_hdl_ref hdl) { m_connections.erase(hdl); } diff --git a/examples/testee_client/testee_client.cpp b/examples/testee_client/testee_client.cpp index b66e63353..01bca4602 100644 --- a/examples/testee_client/testee_client.cpp +++ b/examples/testee_client/testee_client.cpp @@ -80,7 +80,7 @@ typedef websocketpp::config::asio_client::message_type::ptr message_ptr; int case_count = 0; -void on_message(client* c, websocketpp::connection_hdl hdl, message_ptr msg) { +void on_message(client* c, websocketpp::connection_hdl_ref hdl, message_ptr msg) { client::connection_ptr con = c->get_con_from_hdl(hdl); if (con->get_resource() == "/getCaseCount") { diff --git a/examples/testee_server/testee_server.cpp b/examples/testee_server/testee_server.cpp index 69b1abadf..394d5e60a 100644 --- a/examples/testee_server/testee_server.cpp +++ b/examples/testee_server/testee_server.cpp @@ -84,11 +84,11 @@ using websocketpp::lib::error_code; typedef server::message_ptr message_ptr; // Define a callback to handle incoming messages -void on_message(server* s, websocketpp::connection_hdl hdl, message_ptr msg) { +void on_message(server* s, websocketpp::connection_hdl_ref hdl, message_ptr msg) { s->send(hdl, msg->get_payload(), msg->get_opcode()); } -void on_socket_init(websocketpp::connection_hdl, boost::asio::ip::tcp::socket & s) { +void on_socket_init(websocketpp::connection_hdl_ref, boost::asio::ip::tcp::socket & s) { boost::asio::ip::tcp::no_delay option(true); s.set_option(option); } diff --git a/examples/utility_client/utility_client.cpp b/examples/utility_client/utility_client.cpp index aa43b8e39..5c38d5235 100644 --- a/examples/utility_client/utility_client.cpp +++ b/examples/utility_client/utility_client.cpp @@ -46,7 +46,7 @@ class connection_metadata { public: typedef websocketpp::lib::shared_ptr ptr; - connection_metadata(int id, websocketpp::connection_hdl hdl, std::string uri) + connection_metadata(int id, websocketpp::connection_hdl_ref hdl, std::string uri) : m_id(id) , m_hdl(hdl) , m_status("Connecting") @@ -54,14 +54,14 @@ class connection_metadata { , m_server("N/A") {} - void on_open(client * c, websocketpp::connection_hdl hdl) { + void on_open(client * c, websocketpp::connection_hdl_ref hdl) { m_status = "Open"; client::connection_ptr con = c->get_con_from_hdl(hdl); m_server = con->get_response_header("Server"); } - void on_fail(client * c, websocketpp::connection_hdl hdl) { + void on_fail(client * c, websocketpp::connection_hdl_ref hdl) { m_status = "Failed"; client::connection_ptr con = c->get_con_from_hdl(hdl); @@ -69,7 +69,7 @@ class connection_metadata { m_error_reason = con->get_ec().message(); } - void on_close(client * c, websocketpp::connection_hdl hdl) { + void on_close(client * c, websocketpp::connection_hdl_ref hdl) { m_status = "Closed"; client::connection_ptr con = c->get_con_from_hdl(hdl); std::stringstream s; @@ -79,7 +79,7 @@ class connection_metadata { m_error_reason = s.str(); } - void on_message(websocketpp::connection_hdl, client::message_ptr msg) { + void on_message(websocketpp::connection_hdl_ref, client::message_ptr msg) { if (msg->get_opcode() == websocketpp::frame::opcode::text) { m_messages.push_back("<< " + msg->get_payload()); } else { diff --git a/test/connection/connection.cpp b/test/connection/connection.cpp index 8f2fcd28f..bd080fb5c 100644 --- a/test/connection/connection.cpp +++ b/test/connection/connection.cpp @@ -147,21 +147,21 @@ struct connection_setup { typedef websocketpp::client debug_client; typedef websocketpp::server debug_server; -/*void echo_func(server* s, websocketpp::connection_hdl hdl, message_ptr msg) { +/*void echo_func(server* s, websocketpp::connection_hdl_ref hdl, message_ptr msg) { s->send(hdl, msg->get_payload(), msg->get_opcode()); }*/ -void validate_func(server* s, websocketpp::connection_hdl hdl, message_ptr msg) { +void validate_func(server* s, websocketpp::connection_hdl_ref hdl, message_ptr msg) { s->send(hdl, msg->get_payload(), msg->get_opcode()); } -session::validation::value validate_set_ua(server* s, websocketpp::connection_hdl hdl) { +session::validation::value validate_set_ua(server* s, websocketpp::connection_hdl_ref hdl) { server::connection_ptr con = s->get_con_from_hdl(hdl); con->replace_header("Server","foo"); return session::validation::accept; } -void http_func(server* s, websocketpp::connection_hdl hdl) { +void http_func(server* s, websocketpp::connection_hdl_ref hdl) { using namespace websocketpp::http; server::connection_ptr con = s->get_con_from_hdl(hdl); @@ -175,7 +175,7 @@ void http_func(server* s, websocketpp::connection_hdl hdl) { BOOST_CHECK_EQUAL(con->get_response_msg(), status_code::get_string(status_code::ok)); } -void http_func_with_move(server* s, websocketpp::connection_hdl hdl) { +void http_func_with_move(server* s, websocketpp::connection_hdl_ref hdl) { using namespace websocketpp::http; server::connection_ptr con = s->get_con_from_hdl(hdl); @@ -189,7 +189,7 @@ void http_func_with_move(server* s, websocketpp::connection_hdl hdl) { BOOST_CHECK_EQUAL(con->get_response_msg(), status_code::get_string(status_code::ok)); } -void defer_http_func(server* s, bool * deferred, websocketpp::connection_hdl hdl) { +void defer_http_func(server* s, bool * deferred, websocketpp::connection_hdl_ref hdl) { *deferred = true; server::connection_ptr con = s->get_con_from_hdl(hdl); @@ -199,7 +199,7 @@ void defer_http_func(server* s, bool * deferred, websocketpp::connection_hdl hdl } void check_on_fail(server* s, websocketpp::lib::error_code ec, bool & called, - websocketpp::connection_hdl hdl) + websocketpp::connection_hdl_ref hdl) { server::connection_ptr con = s->get_con_from_hdl(hdl); @@ -207,17 +207,17 @@ void check_on_fail(server* s, websocketpp::lib::error_code ec, bool & called, called = true; } -void on_open_print(server* s, websocketpp::connection_hdl hdl) +void on_open_print(server* s, websocketpp::connection_hdl_ref hdl) { server::connection_ptr con = s->get_con_from_hdl(hdl); std::cout << con->get_uri() << std::endl; } -void fail_on_open(websocketpp::connection_hdl) { +void fail_on_open(websocketpp::connection_hdl_ref) { BOOST_CHECK(false); } -void fail_on_http(websocketpp::connection_hdl) { +void fail_on_http(websocketpp::connection_hdl_ref) { BOOST_CHECK(false); } diff --git a/test/connection/connection_tu2.cpp b/test/connection/connection_tu2.cpp index 540962486..bbc94abb1 100644 --- a/test/connection/connection_tu2.cpp +++ b/test/connection/connection_tu2.cpp @@ -27,7 +27,7 @@ #include "connection_tu2.hpp" -void echo_func(server* s, websocketpp::connection_hdl hdl, message_ptr msg) { +void echo_func(server* s, websocketpp::connection_hdl_ref hdl, message_ptr msg) { s->send(hdl, msg->get_payload(), msg->get_opcode()); } diff --git a/test/connection/connection_tu2.hpp b/test/connection/connection_tu2.hpp index 8990ce00c..39e87431f 100644 --- a/test/connection/connection_tu2.hpp +++ b/test/connection/connection_tu2.hpp @@ -46,6 +46,6 @@ using websocketpp::lib::placeholders::_1; using websocketpp::lib::placeholders::_2; using websocketpp::lib::bind; -void echo_func(server* s, websocketpp::connection_hdl hdl, message_ptr msg); +void echo_func(server* s, websocketpp::connection_hdl_ref hdl, message_ptr msg); std::string run_server_test(std::string input, bool log = false); std::string run_server_test(server & s, std::string input, bool log = false); diff --git a/test/roles/server.cpp b/test/roles/server.cpp index 3d4598a01..f17bb08ec 100644 --- a/test/roles/server.cpp +++ b/test/roles/server.cpp @@ -87,12 +87,12 @@ std::string run_server_test(server& s, std::string input) { } /* handler library*/ -void echo_func(server* s, websocketpp::connection_hdl hdl, message_ptr msg) { +void echo_func(server* s, websocketpp::connection_hdl_ref hdl, message_ptr msg) { s->send(hdl, msg->get_payload(), msg->get_opcode()); } session::validation::value validate_func_subprotocol(server* s, std::string* out, std::string accept, - websocketpp::connection_hdl hdl) + websocketpp::connection_hdl_ref hdl) { server::connection_ptr con = s->get_con_from_hdl(hdl); @@ -114,7 +114,7 @@ session::validation::value validate_func_subprotocol(server* s, std::string* out return session::validation::accept; } -void open_func_subprotocol(server* s, std::string* out, websocketpp::connection_hdl hdl) { +void open_func_subprotocol(server* s, std::string* out, websocketpp::connection_hdl_ref hdl) { server::connection_ptr con = s->get_con_from_hdl(hdl); *out = con->get_subprotocol(); @@ -289,13 +289,13 @@ struct test_config : public websocketpp::config::core { typedef test_transport::endpoint transport_type; }; -void handle_fail(websocketpp::server * s, websocketpp::lib::error_code * rec, websocketpp::connection_hdl hdl) { +void handle_fail(websocketpp::server * s, websocketpp::lib::error_code * rec, websocketpp::connection_hdl_ref hdl) { websocketpp::server::connection_ptr con = s->get_con_from_hdl(hdl); *rec = con->get_ec(); } -void handle_fail_count(websocketpp::server * s, websocketpp::lib::error_code rec, int * count, websocketpp::connection_hdl hdl) { +void handle_fail_count(websocketpp::server * s, websocketpp::lib::error_code rec, int * count, websocketpp::connection_hdl_ref hdl) { websocketpp::server::connection_ptr con = s->get_con_from_hdl(hdl); if (con->get_ec() == rec) { diff --git a/test/transport/integration.cpp b/test/transport/integration.cpp index 473c5df40..8c4f5e52f 100644 --- a/test/transport/integration.cpp +++ b/test/transport/integration.cpp @@ -136,7 +136,7 @@ using websocketpp::lib::placeholders::_2; using websocketpp::lib::bind; template -void close_after_timeout(T & e, websocketpp::connection_hdl hdl, long timeout) { +void close_after_timeout(T & e, websocketpp::connection_hdl_ref hdl, long timeout) { sleep(timeout); websocketpp::lib::error_code ec; @@ -273,16 +273,16 @@ void run_dummy_client(std::string port) { } } -bool on_ping(server * s, websocketpp::connection_hdl, std::string) { +bool on_ping(server * s, websocketpp::connection_hdl_ref, std::string) { s->get_alog().write(websocketpp::log::alevel::app,"got ping"); return false; } -void cancel_on_open(server * s, websocketpp::connection_hdl) { +void cancel_on_open(server * s, websocketpp::connection_hdl_ref) { s->stop_listening(); } -void stop_on_close(server * s, websocketpp::connection_hdl hdl) { +void stop_on_close(server * s, websocketpp::connection_hdl_ref hdl) { server::connection_ptr con = s->get_con_from_hdl(hdl); //BOOST_CHECK_EQUAL( con->get_local_close_code(), websocketpp::close::status::normal ); //BOOST_CHECK_EQUAL( con->get_remote_close_code(), websocketpp::close::status::normal ); @@ -290,38 +290,38 @@ void stop_on_close(server * s, websocketpp::connection_hdl hdl) { } template -void ping_on_open(T * c, std::string payload, websocketpp::connection_hdl hdl) { +void ping_on_open(T * c, std::string payload, websocketpp::connection_hdl_ref hdl) { typename T::connection_ptr con = c->get_con_from_hdl(hdl); websocketpp::lib::error_code ec; con->ping(payload,ec); BOOST_CHECK_EQUAL(ec, websocketpp::lib::error_code()); } -void fail_on_pong(websocketpp::connection_hdl, std::string) { +void fail_on_pong(websocketpp::connection_hdl_ref, std::string) { BOOST_FAIL( "expected no pong handler" ); } -void fail_on_pong_timeout(websocketpp::connection_hdl, std::string) { +void fail_on_pong_timeout(websocketpp::connection_hdl_ref, std::string) { BOOST_FAIL( "expected no pong timeout" ); } -void req_pong(std::string expected_payload, websocketpp::connection_hdl, +void req_pong(std::string expected_payload, websocketpp::connection_hdl_ref, std::string payload) { BOOST_CHECK_EQUAL( expected_payload, payload ); } -void fail_on_open(websocketpp::connection_hdl) { +void fail_on_open(websocketpp::connection_hdl_ref) { BOOST_FAIL( "expected no open handler" ); } -void delay(websocketpp::connection_hdl, long duration) { +void delay(websocketpp::connection_hdl_ref, long duration) { sleep(duration); } template void check_ec(T * c, websocketpp::lib::error_code ec, - websocketpp::connection_hdl hdl) + websocketpp::connection_hdl_ref hdl) { typename T::connection_ptr con = c->get_con_from_hdl(hdl); BOOST_CHECK_EQUAL( con->get_ec(), ec ); @@ -331,7 +331,7 @@ void check_ec(T * c, websocketpp::lib::error_code ec, template void check_ec_and_stop(T * e, websocketpp::lib::error_code ec, - websocketpp::connection_hdl hdl) + websocketpp::connection_hdl_ref hdl) { typename T::connection_ptr con = e->get_con_from_hdl(hdl); BOOST_CHECK_EQUAL( con->get_ec(), ec ); @@ -342,7 +342,7 @@ void check_ec_and_stop(T * e, websocketpp::lib::error_code ec, template void req_pong_timeout(T * c, std::string expected_payload, - websocketpp::connection_hdl hdl, std::string payload) + websocketpp::connection_hdl_ref hdl, std::string payload) { typename T::connection_ptr con = c->get_con_from_hdl(hdl); BOOST_CHECK_EQUAL( payload, expected_payload ); @@ -350,7 +350,7 @@ void req_pong_timeout(T * c, std::string expected_payload, } template -void close(T * e, websocketpp::connection_hdl hdl) { +void close(T * e, websocketpp::connection_hdl_ref hdl) { e->get_con_from_hdl(hdl)->close(websocketpp::close::status::normal,""); } diff --git a/tutorials/utility_client/step4.cpp b/tutorials/utility_client/step4.cpp index 73aee65a8..12fdbbca2 100644 --- a/tutorials/utility_client/step4.cpp +++ b/tutorials/utility_client/step4.cpp @@ -46,7 +46,7 @@ class connection_metadata { public: typedef websocketpp::lib::shared_ptr ptr; - connection_metadata(int id, websocketpp::connection_hdl hdl, std::string uri) + connection_metadata(int id, websocketpp::connection_hdl_ref hdl, std::string uri) : m_id(id) , m_hdl(hdl) , m_status("Connecting") @@ -54,14 +54,14 @@ class connection_metadata { , m_server("N/A") {} - void on_open(client * c, websocketpp::connection_hdl hdl) { + void on_open(client * c, websocketpp::connection_hdl_ref hdl) { m_status = "Open"; client::connection_ptr con = c->get_con_from_hdl(hdl); m_server = con->get_response_header("Server"); } - void on_fail(client * c, websocketpp::connection_hdl hdl) { + void on_fail(client * c, websocketpp::connection_hdl_ref hdl) { m_status = "Failed"; client::connection_ptr con = c->get_con_from_hdl(hdl); diff --git a/tutorials/utility_client/step5.cpp b/tutorials/utility_client/step5.cpp index 3e57b3c24..eab8f704f 100644 --- a/tutorials/utility_client/step5.cpp +++ b/tutorials/utility_client/step5.cpp @@ -46,7 +46,7 @@ class connection_metadata { public: typedef websocketpp::lib::shared_ptr ptr; - connection_metadata(int id, websocketpp::connection_hdl hdl, std::string uri) + connection_metadata(int id, websocketpp::connection_hdl_ref hdl, std::string uri) : m_id(id) , m_hdl(hdl) , m_status("Connecting") @@ -54,14 +54,14 @@ class connection_metadata { , m_server("N/A") {} - void on_open(client * c, websocketpp::connection_hdl hdl) { + void on_open(client * c, websocketpp::connection_hdl_ref hdl) { m_status = "Open"; client::connection_ptr con = c->get_con_from_hdl(hdl); m_server = con->get_response_header("Server"); } - void on_fail(client * c, websocketpp::connection_hdl hdl) { + void on_fail(client * c, websocketpp::connection_hdl_ref hdl) { m_status = "Failed"; client::connection_ptr con = c->get_con_from_hdl(hdl); @@ -69,7 +69,7 @@ class connection_metadata { m_error_reason = con->get_ec().message(); } - void on_close(client * c, websocketpp::connection_hdl hdl) { + void on_close(client * c, websocketpp::connection_hdl_ref hdl) { m_status = "Closed"; client::connection_ptr con = c->get_con_from_hdl(hdl); std::stringstream s; diff --git a/tutorials/utility_client/step6.cpp b/tutorials/utility_client/step6.cpp index 208b95db6..8ec1aee9e 100644 --- a/tutorials/utility_client/step6.cpp +++ b/tutorials/utility_client/step6.cpp @@ -46,7 +46,7 @@ class connection_metadata { public: typedef websocketpp::lib::shared_ptr ptr; - connection_metadata(int id, websocketpp::connection_hdl hdl, std::string uri) + connection_metadata(int id, websocketpp::connection_hdl_ref hdl, std::string uri) : m_id(id) , m_hdl(hdl) , m_status("Connecting") @@ -54,14 +54,14 @@ class connection_metadata { , m_server("N/A") {} - void on_open(client * c, websocketpp::connection_hdl hdl) { + void on_open(client * c, websocketpp::connection_hdl_ref hdl) { m_status = "Open"; client::connection_ptr con = c->get_con_from_hdl(hdl); m_server = con->get_response_header("Server"); } - void on_fail(client * c, websocketpp::connection_hdl hdl) { + void on_fail(client * c, websocketpp::connection_hdl_ref hdl) { m_status = "Failed"; client::connection_ptr con = c->get_con_from_hdl(hdl); @@ -69,7 +69,7 @@ class connection_metadata { m_error_reason = con->get_ec().message(); } - void on_close(client * c, websocketpp::connection_hdl hdl) { + void on_close(client * c, websocketpp::connection_hdl_ref hdl) { m_status = "Closed"; client::connection_ptr con = c->get_con_from_hdl(hdl); std::stringstream s; @@ -79,7 +79,7 @@ class connection_metadata { m_error_reason = s.str(); } - void on_message(websocketpp::connection_hdl, client::message_ptr msg) { + void on_message(websocketpp::connection_hdl_ref, client::message_ptr msg) { if (msg->get_opcode() == websocketpp::frame::opcode::text) { m_messages.push_back("<< " + msg->get_payload()); } else { diff --git a/tutorials/utility_client/utility_client.md b/tutorials/utility_client/utility_client.md index 696113df8..c1b097211 100644 --- a/tutorials/utility_client/utility_client.md +++ b/tutorials/utility_client/utility_client.md @@ -380,7 +380,7 @@ class connection_metadata { public: typedef websocketpp::lib::shared_ptr ptr; - connection_metadata(int id, websocketpp::connection_hdl hdl, std::string uri) + connection_metadata(int id, websocketpp::connection_hdl_ref hdl, std::string uri) : m_id(id) , m_hdl(hdl) , m_status("Connecting") @@ -388,14 +388,14 @@ public: , m_server("N/A") {} - void on_open(client * c, websocketpp::connection_hdl hdl) { + void on_open(client * c, websocketpp::connection_hdl_ref hdl) { m_status = "Open"; client::connection_ptr con = c->get_con_from_hdl(hdl); m_server = con->get_response_header("Server"); } - void on_fail(client * c, websocketpp::connection_hdl hdl) { + void on_fail(client * c, websocketpp::connection_hdl_ref hdl) { m_status = "Failed"; client::connection_ptr con = c->get_con_from_hdl(hdl); @@ -554,7 +554,7 @@ During the close handler call WebSocket++ connections offer the following method The `connection_metadata::on_close` method is added. This method retrieves the close code and reason from the closing handshake and stores it in the local error reason field. ~~~{.cpp} -void on_close(client * c, websocketpp::connection_hdl hdl) { +void on_close(client * c, websocketpp::connection_hdl_ref hdl) { m_status = "Closed"; client::connection_ptr con = c->get_con_from_hdl(hdl); std::stringstream s; @@ -678,13 +678,13 @@ This step adds a command to send a message on a given connection and updates the Messages are sent using `endpoint::send`. This is a thread safe method that may be called from anywhere to queue a message for sending on the specified connection. There are three send overloads for use with different scenarios. -Each method takes a `connection_hdl` to indicate which connection to send the message on as well as a `frame::opcode::value` to indicate which opcode to label the message as. All overloads are also available with an exception free varient that fills in a a status/error code instead of throwing. +Each method takes a `connection_hdl_ref` to indicate which connection to send the message on as well as a `frame::opcode::value` to indicate which opcode to label the message as. All overloads are also available with an exception free varient that fills in a a status/error code instead of throwing. -The first overload, `connection_hdl hdl, std::string const & payload, frame::opcode::value op`, takes a `std::string`. The string contents are copied into an internal buffer and can be safely modified after calling send. +The first overload, `connection_hdl_ref hdl, std::string const & payload, frame::opcode::value op`, takes a `std::string`. The string contents are copied into an internal buffer and can be safely modified after calling send. -The second overload, `connection_hdl hdl, void const * payload, size_t len, frame::opcode::value op`, takes a void * buffer and length. The buffer contents are copied and can be safely modified after calling send. +The second overload, `connection_hdl_ref hdl, void const * payload, size_t len, frame::opcode::value op`, takes a void * buffer and length. The buffer contents are copied and can be safely modified after calling send. -The third overload, `connection_hdl hdl, message_ptr msg`, takes a WebSocket++ `message_ptr`. This overload allows a message to be constructed in place before the call to send. It also may allow a single message buffer to be sent multiple times, including to multiple connections, without copying. Whether or not this actually happens depends on other factors such as whether compression is enabled. The contents of the message buffer may not be safely modified after being sent. +The third overload, `connection_hdl_ref hdl, message_ptr msg`, takes a WebSocket++ `message_ptr`. This overload allows a message to be constructed in place before the call to send. It also may allow a single message buffer to be sent multiple times, including to multiple connections, without copying. Whether or not this actually happens depends on other factors such as whether compression is enabled. The contents of the message buffer may not be safely modified after being sent. > ###### Terminology: Outgoing WebSocket message queueing & flow control > In many configurations, such as when the Asio based transport is in use, WebSocket++ is an asynchronous system. As such the `endpoint::send` method may return before any bytes are actually written to the outgoing socket. In cases where send is called multiple times in quick succession messages may be coalesced and sent in the same operation or even the same TCP packet. When this happens the message boundaries are preserved (each call to send will produce a separate message). @@ -759,14 +759,14 @@ for (it = data.m_messages.begin(); it != data.m_messages.end(); ++it) { #### Receiving Messages -Messages are received by registering a message handler. This handler will be called once per message received and its signature is `void on_message(websocketpp::connection_hdl hdl, endpoint::message_ptr msg)`. The `connection_hdl`, like the similar parameter from the other handlers is a handle for the connection that the message was received on. The `message_ptr` is a pointer to an object that can be queried for the message payload, opcode, and other metadata. Note that the message_ptr type, as well as its underlying message type, is dependent on how your endpoint is configured and may be different for different configs. +Messages are received by registering a message handler. This handler will be called once per message received and its signature is `void on_message(websocketpp::connection_hdl_ref hdl, endpoint::message_ptr msg)`. The `connection_hdl_ref`, like the similar parameter from the other handlers is a handle for the connection that the message was received on. The `message_ptr` is a pointer to an object that can be queried for the message payload, opcode, and other metadata. Note that the message_ptr type, as well as its underlying message type, is dependent on how your endpoint is configured and may be different for different configs. #### Add a message handler to method to `connection_metadata` The message receiving behave that we are implementing will be to collect all messages sent and received and to print them in order when the show connection command is run. The sent messages are already being added to that list. Now we add a message handler that pushes received messages to the list as well. Text messages are pushed as-is. Binary messages are first converted to printable hexadecimal format. ~~~{.cpp} -void on_message(websocketpp::connection_hdl hdl, client::message_ptr msg) { +void on_message(websocketpp::connection_hdl_ref hdl, client::message_ptr msg) { if (msg->get_opcode() == websocketpp::frame::opcode::text) { m_messages.push_back(msg->get_payload()); } else { diff --git a/tutorials/utility_server/step2.cpp b/tutorials/utility_server/step2.cpp index a2815bbb9..c416a0fac 100644 --- a/tutorials/utility_server/step2.cpp +++ b/tutorials/utility_server/step2.cpp @@ -56,7 +56,7 @@ class utility_server { )); } - void echo_handler(websocketpp::connection_hdl hdl, server::message_ptr msg) { + void echo_handler(websocketpp::connection_hdl_ref hdl, server::message_ptr msg) { // write a new message m_endpoint.send(hdl, msg->get_payload(), msg->get_opcode()); } diff --git a/websocketpp/common/connection_hdl.hpp b/websocketpp/common/connection_hdl.hpp index 1044c88e3..51e27a27a 100644 --- a/websocketpp/common/connection_hdl.hpp +++ b/websocketpp/common/connection_hdl.hpp @@ -46,6 +46,7 @@ namespace websocketpp { * that owns the handler. */ typedef lib::weak_ptr connection_hdl; +typedef const connection_hdl& connection_hdl_ref; } // namespace websocketpp diff --git a/websocketpp/connection.hpp b/websocketpp/connection.hpp index 295cbc8dd..e28cde8ed 100644 --- a/websocketpp/connection.hpp +++ b/websocketpp/connection.hpp @@ -67,7 +67,7 @@ namespace session{ * upgrade the connection to the WebSocket protocol will trigger the http * handler instead of fail/open. */ -typedef lib::function open_handler; +typedef lib::function open_handler; /// The type and function signature of a close handler /** @@ -77,7 +77,7 @@ typedef lib::function open_handler; * The close handler will be called exactly once for every connection for which * the open handler was called. */ -typedef lib::function close_handler; +typedef lib::function close_handler; /// The type and function signature of a fail handler /** @@ -87,7 +87,7 @@ typedef lib::function close_handler; * upgrade the connection to the WebSocket protocol will trigger the http * handler instead of fail/open. */ -typedef lib::function fail_handler; +typedef lib::function fail_handler; /// The type and function signature of an interrupt handler /** @@ -99,7 +99,7 @@ typedef lib::function fail_handler; * This is typically used by another application thread to schedule some tasks * that can only be run from within the handler chain for thread safety reasons. */ -typedef lib::function interrupt_handler; +typedef lib::function interrupt_handler; /// The type and function signature of a ping handler /** @@ -109,7 +109,7 @@ typedef lib::function interrupt_handler; * true if a pong response should be sent, false if the pong response should be * suppressed. */ -typedef lib::function ping_handler; +typedef lib::function ping_handler; /// The type and function signature of a pong handler /** @@ -117,14 +117,14 @@ typedef lib::function ping_handler; * control frame. The string argument contains the pong payload. The payload is * a binary string up to 126 bytes in length. */ -typedef lib::function pong_handler; +typedef lib::function pong_handler; /// The type and function signature of a pong timeout handler /** * The pong timeout handler is called when a ping goes unanswered by a pong for * longer than the locally specified timeout period. */ -typedef lib::function pong_timeout_handler; +typedef lib::function pong_timeout_handler; /// The type and function signature of a validate handler /** @@ -137,7 +137,7 @@ typedef lib::function pong_timeout_handler; * should be accepted. Additional methods may be called during the function to * set response headers, set HTTP return/error codes, etc. */ -typedef lib::function validate_handler; +typedef lib::function validate_handler; /// The type and function signature of a http handler /** @@ -159,7 +159,7 @@ typedef lib::function validate_handl * handlers may override the response status code to deliver any type of * response. */ -typedef lib::function http_handler; +typedef lib::function http_handler; // typedef lib::function read_handler; @@ -1521,7 +1521,7 @@ class connection * @param hdl A connection_hdl that the connection will use to refer * to itself. */ - void set_handle(connection_hdl hdl) { + void set_handle(connection_hdl_ref hdl) { m_connection_hdl = hdl; transport_con_type::set_handle(hdl); } diff --git a/websocketpp/endpoint.hpp b/websocketpp/endpoint.hpp index b6bca189b..8afa38cc1 100644 --- a/websocketpp/endpoint.hpp +++ b/websocketpp/endpoint.hpp @@ -482,8 +482,8 @@ class endpoint : public config::transport_type, public config::endpoint_base { * default and an exception free variant. */ - void interrupt(connection_hdl hdl, lib::error_code & ec); - void interrupt(connection_hdl hdl); + void interrupt(connection_hdl_ref hdl, lib::error_code & ec); + void interrupt(connection_hdl_ref hdl); /// Pause reading of new data (exception free) /** @@ -505,10 +505,10 @@ class endpoint : public config::transport_type, public config::endpoint_base { * * If reading is paused for this connection already nothing is changed. */ - void pause_reading(connection_hdl hdl, lib::error_code & ec); + void pause_reading(connection_hdl_ref hdl, lib::error_code & ec); /// Pause reading of new data - void pause_reading(connection_hdl hdl); + void pause_reading(connection_hdl_ref hdl); /// Resume reading of new data (exception free) /** @@ -517,10 +517,10 @@ class endpoint : public config::transport_type, public config::endpoint_base { * * If reading is not paused for this connection already nothing is changed. */ - void resume_reading(connection_hdl hdl, lib::error_code & ec); + void resume_reading(connection_hdl_ref hdl, lib::error_code & ec); /// Resume reading of new data - void resume_reading(connection_hdl hdl); + void resume_reading(connection_hdl_ref hdl); /// Send deferred HTTP Response /** @@ -535,7 +535,7 @@ class endpoint : public config::transport_type, public config::endpoint_base { * @param hdl The connection to send the response on * @param ec A status code, zero on success, non-zero otherwise */ - void send_http_response(connection_hdl hdl, lib::error_code & ec); + void send_http_response(connection_hdl_ref hdl, lib::error_code & ec); /// Send deferred HTTP Response (exception free) /** @@ -549,7 +549,7 @@ class endpoint : public config::transport_type, public config::endpoint_base { * * @param hdl The connection to send the response on */ - void send_http_response(connection_hdl hdl); + void send_http_response(connection_hdl_ref hdl); /// Create a message and add it to the outgoing send queue (exception free) /** @@ -560,7 +560,7 @@ class endpoint : public config::transport_type, public config::endpoint_base { * @param [in] op The opcode to generated the message with. * @param [out] ec A code to fill in for errors */ - void send(connection_hdl hdl, std::string const & payload, + void send(connection_hdl_ref hdl, std::string const & payload, frame::opcode::value op, lib::error_code & ec); /// Create a message and add it to the outgoing send queue /** @@ -570,21 +570,21 @@ class endpoint : public config::transport_type, public config::endpoint_base { * @param [in] payload The payload string to generated the message with * @param [in] op The opcode to generated the message with. */ - void send(connection_hdl hdl, std::string const & payload, + void send(connection_hdl_ref hdl, std::string const & payload, frame::opcode::value op); - void send(connection_hdl hdl, void const * payload, size_t len, + void send(connection_hdl_ref hdl, void const * payload, size_t len, frame::opcode::value op, lib::error_code & ec); - void send(connection_hdl hdl, void const * payload, size_t len, + void send(connection_hdl_ref hdl, void const * payload, size_t len, frame::opcode::value op); - void send(connection_hdl hdl, message_ptr msg, lib::error_code & ec); - void send(connection_hdl hdl, message_ptr msg); + void send(connection_hdl_ref hdl, message_ptr msg, lib::error_code & ec); + void send(connection_hdl_ref hdl, message_ptr msg); - void close(connection_hdl hdl, close::status::value const code, + void close(connection_hdl_ref hdl, close::status::value const code, std::string const & reason, lib::error_code & ec); #ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ - void close(connection_hdl hdl, close::status::value const code, + void close(connection_hdl_ref hdl, close::status::value const code, std::string const & reason); #endif // _WEBSOCKETPP_NO_EXCEPTIONS_ @@ -596,7 +596,7 @@ class endpoint : public config::transport_type, public config::endpoint_base { * @param [in] payload The payload string to send. * @param [out] ec A reference to an error code to fill in */ - void ping(connection_hdl hdl, std::string const & payload, + void ping(connection_hdl_ref hdl, std::string const & payload, lib::error_code & ec); /// Send a ping to a specific connection /** @@ -607,7 +607,7 @@ class endpoint : public config::transport_type, public config::endpoint_base { * @param [in] hdl The connection_hdl of the connection to send to. * @param [in] payload The payload string to send. */ - void ping(connection_hdl hdl, std::string const & payload); + void ping(connection_hdl_ref hdl, std::string const & payload); /// Send a pong to a specific connection /** @@ -617,7 +617,7 @@ class endpoint : public config::transport_type, public config::endpoint_base { * @param [in] payload The payload string to send. * @param [out] ec A reference to an error code to fill in */ - void pong(connection_hdl hdl, std::string const & payload, + void pong(connection_hdl_ref hdl, std::string const & payload, lib::error_code & ec); /// Send a pong to a specific connection /** @@ -628,7 +628,7 @@ class endpoint : public config::transport_type, public config::endpoint_base { * @param [in] hdl The connection_hdl of the connection to send to. * @param [in] payload The payload string to send. */ - void pong(connection_hdl hdl, std::string const & payload); + void pong(connection_hdl_ref hdl, std::string const & payload); /// Retrieves a connection_ptr from a connection_hdl (exception free) /** @@ -644,7 +644,7 @@ class endpoint : public config::transport_type, public config::endpoint_base { * * @return the connection_ptr. May be NULL if the handle was invalid. */ - connection_ptr get_con_from_hdl(connection_hdl hdl, lib::error_code & ec) { + connection_ptr get_con_from_hdl(connection_hdl_ref hdl, lib::error_code & ec) { connection_ptr con = lib::static_pointer_cast( hdl.lock()); if (!con) { @@ -655,7 +655,7 @@ class endpoint : public config::transport_type, public config::endpoint_base { #ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ /// Retrieves a connection_ptr from a connection_hdl (exception version) - connection_ptr get_con_from_hdl(connection_hdl hdl) { + connection_ptr get_con_from_hdl(connection_hdl_ref hdl) { lib::error_code ec; connection_ptr con = this->get_con_from_hdl(hdl,ec); if (ec) { diff --git a/websocketpp/impl/endpoint_impl.hpp b/websocketpp/impl/endpoint_impl.hpp index e945a78f3..a70efb842 100644 --- a/websocketpp/impl/endpoint_impl.hpp +++ b/websocketpp/impl/endpoint_impl.hpp @@ -91,7 +91,7 @@ endpoint::create_connection(lib::error_code & ec) { } template -void endpoint::interrupt(connection_hdl hdl, lib::error_code & ec) +void endpoint::interrupt(connection_hdl_ref hdl, lib::error_code & ec) { connection_ptr con = get_con_from_hdl(hdl,ec); if (ec) {return;} @@ -102,7 +102,7 @@ void endpoint::interrupt(connection_hdl hdl, lib::error_code } template -void endpoint::pause_reading(connection_hdl hdl, lib::error_code & ec) +void endpoint::pause_reading(connection_hdl_ref hdl, lib::error_code & ec) { connection_ptr con = get_con_from_hdl(hdl,ec); if (ec) {return;} @@ -111,7 +111,7 @@ void endpoint::pause_reading(connection_hdl hdl, lib::error_c } template -void endpoint::resume_reading(connection_hdl hdl, lib::error_code & ec) +void endpoint::resume_reading(connection_hdl_ref hdl, lib::error_code & ec) { connection_ptr con = get_con_from_hdl(hdl,ec); if (ec) {return;} @@ -120,7 +120,7 @@ void endpoint::resume_reading(connection_hdl hdl, lib::error_ } template -void endpoint::send_http_response(connection_hdl hdl, +void endpoint::send_http_response(connection_hdl_ref hdl, lib::error_code & ec) { connection_ptr con = get_con_from_hdl(hdl,ec); @@ -129,7 +129,7 @@ void endpoint::send_http_response(connection_hdl hdl, } template -void endpoint::send(connection_hdl hdl, std::string const & payload, +void endpoint::send(connection_hdl_ref hdl, std::string const & payload, frame::opcode::value op, lib::error_code & ec) { connection_ptr con = get_con_from_hdl(hdl,ec); @@ -139,7 +139,7 @@ void endpoint::send(connection_hdl hdl, std::string const & p } template -void endpoint::send(connection_hdl hdl, void const * payload, +void endpoint::send(connection_hdl_ref hdl, void const * payload, size_t len, frame::opcode::value op, lib::error_code & ec) { connection_ptr con = get_con_from_hdl(hdl,ec); @@ -148,7 +148,7 @@ void endpoint::send(connection_hdl hdl, void const * payload, } template -void endpoint::send(connection_hdl hdl, message_ptr msg, +void endpoint::send(connection_hdl_ref hdl, message_ptr msg, lib::error_code & ec) { connection_ptr con = get_con_from_hdl(hdl,ec); @@ -157,7 +157,7 @@ void endpoint::send(connection_hdl hdl, message_ptr msg, } template -void endpoint::close(connection_hdl hdl, close::status::value +void endpoint::close(connection_hdl_ref hdl, close::status::value const code, std::string const & reason, lib::error_code & ec) { @@ -167,7 +167,7 @@ void endpoint::close(connection_hdl hdl, close::status::value } template -void endpoint::ping(connection_hdl hdl, std::string const & +void endpoint::ping(connection_hdl_ref hdl, std::string const & payload, lib::error_code & ec) { connection_ptr con = get_con_from_hdl(hdl,ec); @@ -176,7 +176,7 @@ void endpoint::ping(connection_hdl hdl, std::string const & } template -void endpoint::pong(connection_hdl hdl, std::string const & payload, +void endpoint::pong(connection_hdl_ref hdl, std::string const & payload, lib::error_code & ec) { connection_ptr con = get_con_from_hdl(hdl,ec); @@ -188,35 +188,35 @@ void endpoint::pong(connection_hdl hdl, std::string const & p // If exceptions are enabled, define wrapper methods that throw exceptions template -void endpoint::interrupt(connection_hdl hdl) { +void endpoint::interrupt(connection_hdl_ref hdl) { lib::error_code ec; interrupt(hdl,ec); if (ec) { throw exception(ec); } } template -void endpoint::pause_reading(connection_hdl hdl) { +void endpoint::pause_reading(connection_hdl_ref hdl) { lib::error_code ec; pause_reading(hdl,ec); if (ec) { throw exception(ec); } } template -void endpoint::resume_reading(connection_hdl hdl) { +void endpoint::resume_reading(connection_hdl_ref hdl) { lib::error_code ec; resume_reading(hdl,ec); if (ec) { throw exception(ec); } } template -void endpoint::send_http_response(connection_hdl hdl) { +void endpoint::send_http_response(connection_hdl_ref hdl) { lib::error_code ec; send_http_response(hdl,ec); if (ec) { throw exception(ec); } } template -void endpoint::send(connection_hdl hdl, std::string const & payload, +void endpoint::send(connection_hdl_ref hdl, std::string const & payload, frame::opcode::value op) { lib::error_code ec; @@ -225,7 +225,7 @@ void endpoint::send(connection_hdl hdl, std::string const & p } template -void endpoint::send(connection_hdl hdl, void const * payload, +void endpoint::send(connection_hdl_ref hdl, void const * payload, size_t len, frame::opcode::value op) { lib::error_code ec; @@ -234,14 +234,14 @@ void endpoint::send(connection_hdl hdl, void const * payload, } template -void endpoint::send(connection_hdl hdl, message_ptr msg) { +void endpoint::send(connection_hdl_ref hdl, message_ptr msg) { lib::error_code ec; send(hdl,msg,ec); if (ec) { throw exception(ec); } } template -void endpoint::close(connection_hdl hdl, close::status::value +void endpoint::close(connection_hdl_ref hdl, close::status::value const code, std::string const & reason) { lib::error_code ec; @@ -250,7 +250,7 @@ void endpoint::close(connection_hdl hdl, close::status::value } template -void endpoint::ping(connection_hdl hdl, std::string const & payload) +void endpoint::ping(connection_hdl_ref hdl, std::string const & payload) { lib::error_code ec; ping(hdl,payload,ec); @@ -258,7 +258,7 @@ void endpoint::ping(connection_hdl hdl, std::string const & p } template -void endpoint::pong(connection_hdl hdl, std::string const & payload) +void endpoint::pong(connection_hdl_ref hdl, std::string const & payload) { lib::error_code ec; pong(hdl,payload,ec); diff --git a/websocketpp/transport/asio/connection.hpp b/websocketpp/transport/asio/connection.hpp index ea18e3e6d..e9543fb71 100644 --- a/websocketpp/transport/asio/connection.hpp +++ b/websocketpp/transport/asio/connection.hpp @@ -1019,7 +1019,7 @@ class connection : public config::socket_type::socket_con_type { * @param hdl A connection_hdl that the transport will use to refer * to itself */ - void set_handle(connection_hdl hdl) { + void set_handle(connection_hdl_ref hdl) { m_connection_hdl = hdl; socket_con_type::set_handle(hdl); } diff --git a/websocketpp/transport/asio/security/none.hpp b/websocketpp/transport/asio/security/none.hpp index c679378ed..51f4224a4 100644 --- a/websocketpp/transport/asio/security/none.hpp +++ b/websocketpp/transport/asio/security/none.hpp @@ -232,7 +232,7 @@ class connection : public lib::enable_shared_from_this { * * @param hdl The new handle */ - void set_handle(connection_hdl hdl) { + void set_handle(connection_hdl_ref hdl) { m_hdl = hdl; } diff --git a/websocketpp/transport/asio/security/tls.hpp b/websocketpp/transport/asio/security/tls.hpp index dea07b021..b9f4b52ce 100644 --- a/websocketpp/transport/asio/security/tls.hpp +++ b/websocketpp/transport/asio/security/tls.hpp @@ -52,7 +52,7 @@ namespace tls_socket { typedef lib::function&)> socket_init_handler; /// The signature of the tls_init_handler for this socket policy -typedef lib::function(connection_hdl)> +typedef lib::function(connection_hdl_ref)> tls_init_handler; /// TLS enabled Asio connection socket component @@ -305,7 +305,7 @@ class connection : public lib::enable_shared_from_this { * * @param hdl The new handle */ - void set_handle(connection_hdl hdl) { + void set_handle(connection_hdl_ref hdl) { m_hdl = hdl; } diff --git a/websocketpp/transport/base/connection.hpp b/websocketpp/transport/base/connection.hpp index f76d40913..0e0618cd2 100644 --- a/websocketpp/transport/base/connection.hpp +++ b/websocketpp/transport/base/connection.hpp @@ -75,7 +75,7 @@ namespace websocketpp { * write * * **set_handle**\n - * `void set_handle(connection_hdl hdl)`\n + * `void set_handle(connection_hdl_ref hdl)`\n * Called by WebSocket++ to let this policy know the hdl to the connection. It * may be stored for later use or ignored/discarded. This handle should be used * if the policy adds any connection handlers. Connection handlers must be diff --git a/websocketpp/transport/iostream/connection.hpp b/websocketpp/transport/iostream/connection.hpp index 899a3e256..e22f8414a 100644 --- a/websocketpp/transport/iostream/connection.hpp +++ b/websocketpp/transport/iostream/connection.hpp @@ -562,7 +562,7 @@ class connection : public lib::enable_shared_from_this< connection > { /** * @param hdl The new handle */ - void set_handle(connection_hdl hdl) { + void set_handle(connection_hdl_ref hdl) { m_connection_hdl = hdl; } diff --git a/websocketpp/transport/stub/connection.hpp b/websocketpp/transport/stub/connection.hpp index 5bbbf0d79..410d9031b 100644 --- a/websocketpp/transport/stub/connection.hpp +++ b/websocketpp/transport/stub/connection.hpp @@ -247,7 +247,7 @@ class connection : public lib::enable_shared_from_this< connection > { /** * @param hdl The new handle */ - void set_handle(connection_hdl hdl) {} + void set_handle(connection_hdl_ref hdl) {} /// Call given handler back within the transport's event system (if present) /** From 1af6804ab1abcb540124d40c3682fa83859763f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20To=C5=A1kan?= Date: Mon, 23 Jan 2023 18:01:19 +0100 Subject: [PATCH 12/40] Added HTTP client capability --- test/http/parser.cpp | 12 +- test/transport/integration.cpp | 4 + test/utility/uri.cpp | 49 ++---- websocketpp/config/core.hpp | 2 + websocketpp/config/core_client.hpp | 2 + websocketpp/config/debug.hpp | 2 + websocketpp/config/minimal_server.hpp | 2 + websocketpp/connection.hpp | 74 ++++++++- websocketpp/endpoint.hpp | 32 +++- websocketpp/error.hpp | 7 +- websocketpp/http/impl/response.hpp | 16 +- websocketpp/http/response.hpp | 24 +-- websocketpp/impl/connection_impl.hpp | 170 ++++++++++++++------ websocketpp/impl/endpoint_impl.hpp | 3 + websocketpp/processors/hybi00.hpp | 5 +- websocketpp/processors/hybi13.hpp | 2 +- websocketpp/processors/processor.hpp | 6 +- websocketpp/transport/asio/connection.hpp | 4 +- websocketpp/transport/asio/endpoint.hpp | 2 +- websocketpp/transport/asio/security/tls.hpp | 13 +- websocketpp/uri.hpp | 170 ++++++++++---------- websocketpp/version.hpp | 4 +- 22 files changed, 401 insertions(+), 204 deletions(-) diff --git a/test/http/parser.cpp b/test/http/parser.cpp index fbe196668..cd8661f28 100644 --- a/test/http/parser.cpp +++ b/test/http/parser.cpp @@ -855,7 +855,7 @@ BOOST_AUTO_TEST_CASE( wikipedia_example_response ) { BOOST_CHECK_EQUAL(ec, websocketpp::lib::error_code()); BOOST_CHECK_EQUAL( pos, 159 ); - BOOST_CHECK( r.headers_ready() == true ); + BOOST_CHECK( r.has_received(websocketpp::http::parser::response::state::HEADERS) == true ); BOOST_CHECK_EQUAL( r.get_version(), "HTTP/1.1" ); BOOST_CHECK_EQUAL( r.get_status_code(), websocketpp::http::status_code::switching_protocols ); BOOST_CHECK_EQUAL( r.get_status_msg(), "Switching Protocols" ); @@ -878,7 +878,7 @@ BOOST_AUTO_TEST_CASE( wikipedia_example_response_trailing ) { BOOST_CHECK_EQUAL(ec, websocketpp::lib::error_code()); BOOST_CHECK_EQUAL( pos, 159 ); - BOOST_CHECK( r.headers_ready() == true ); + BOOST_CHECK( r.has_received(websocketpp::http::parser::response::state::HEADERS) == true ); BOOST_CHECK_EQUAL( r.get_version(), "HTTP/1.1" ); BOOST_CHECK_EQUAL( r.get_status_code(), websocketpp::http::status_code::switching_protocols ); BOOST_CHECK_EQUAL( r.get_status_msg(), "Switching Protocols" ); @@ -901,7 +901,7 @@ BOOST_AUTO_TEST_CASE( wikipedia_example_response_trailing_large ) { BOOST_CHECK_EQUAL(ec, websocketpp::lib::error_code()); BOOST_CHECK_EQUAL( pos, 159 ); - BOOST_CHECK( r.headers_ready() == true ); + BOOST_CHECK( r.has_received(websocketpp::http::parser::response::state::HEADERS) == true ); BOOST_CHECK_EQUAL( r.get_version(), "HTTP/1.1" ); BOOST_CHECK_EQUAL( r.get_status_code(), websocketpp::http::status_code::switching_protocols ); BOOST_CHECK_EQUAL( r.get_status_msg(), "Switching Protocols" ); @@ -923,7 +923,7 @@ BOOST_AUTO_TEST_CASE( response_with_non_standard_lws ) { BOOST_CHECK_EQUAL(ec, websocketpp::lib::error_code()); BOOST_CHECK_EQUAL( pos, 158 ); - BOOST_CHECK( r.headers_ready() ); + BOOST_CHECK( r.has_received(websocketpp::http::parser::response::state::HEADERS) ); BOOST_CHECK_EQUAL( r.get_version(), "HTTP/1.1" ); BOOST_CHECK_EQUAL( r.get_status_code(), websocketpp::http::status_code::switching_protocols ); BOOST_CHECK_EQUAL( r.get_status_msg(), "Switching Protocols" ); @@ -945,7 +945,7 @@ BOOST_AUTO_TEST_CASE( plain_http_response ) { BOOST_CHECK_EQUAL(ec, websocketpp::lib::error_code()); BOOST_CHECK_EQUAL( pos, 405 ); - BOOST_CHECK( r.headers_ready() == true ); + BOOST_CHECK( r.has_received(websocketpp::http::parser::response::state::HEADERS) == true ); BOOST_CHECK( r.ready() == true ); BOOST_CHECK_EQUAL( r.get_version(), "HTTP/1.1" ); BOOST_CHECK_EQUAL( r.get_status_code(), websocketpp::http::status_code::ok ); @@ -975,7 +975,7 @@ BOOST_AUTO_TEST_CASE( parse_istream ) { BOOST_CHECK_EQUAL(ec, websocketpp::lib::error_code()); BOOST_CHECK_EQUAL( pos, 405 ); - BOOST_CHECK_EQUAL( r.headers_ready(), true ); + BOOST_CHECK_EQUAL( r.has_received(websocketpp::http::parser::response::state::HEADERS), true ); BOOST_CHECK_EQUAL( r.ready(), true ); } diff --git a/test/transport/integration.cpp b/test/transport/integration.cpp index 8c4f5e52f..213ef75a0 100644 --- a/test/transport/integration.cpp +++ b/test/transport/integration.cpp @@ -76,6 +76,8 @@ struct config : public websocketpp::config::asio_client { static const long timeout_open_handshake = 500; /// Length of time before a closing handshake is aborted static const long timeout_close_handshake = 500; + /// Length of time before a HTTP body read is aborted + static const long timeout_http_read_body = 500; /// Length of time to wait for a pong after a ping static const long timeout_pong = 500; }; @@ -118,6 +120,8 @@ struct config_tls : public websocketpp::config::asio_tls_client { static const long timeout_open_handshake = 500; /// Length of time before a closing handshake is aborted static const long timeout_close_handshake = 500; + /// Length of time before a HTTP body read is aborted + static const long timeout_http_read_body = 500; /// Length of time to wait for a pong after a ping static const long timeout_pong = 500; }; diff --git a/test/utility/uri.cpp b/test/utility/uri.cpp index f6d18726a..82d7bb7e3 100644 --- a/test/utility/uri.cpp +++ b/test/utility/uri.cpp @@ -40,10 +40,9 @@ BOOST_AUTO_TEST_CASE( uri_valid ) { websocketpp::uri uri("ws://localhost:9000/chat"); - BOOST_CHECK( uri.get_valid() ); BOOST_CHECK( !uri.get_secure() ); - BOOST_CHECK_EQUAL( uri.get_scheme(), "ws"); - BOOST_CHECK_EQUAL( uri.get_host(), "localhost"); + BOOST_CHECK_EQUAL( uri.get_type(), websocketpp::uri::websocket ); + BOOST_CHECK_EQUAL( uri.get_host(), "localhost" ); BOOST_CHECK_EQUAL( uri.get_port(), 9000 ); BOOST_CHECK_EQUAL( uri.get_resource(), "/chat" ); BOOST_CHECK_EQUAL( uri.get_query(), "" ); @@ -161,9 +160,8 @@ BOOST_AUTO_TEST_CASE( uri_invalid_ipv6 ) { BOOST_AUTO_TEST_CASE( uri_valid_no_slash ) { websocketpp::uri uri("ws://localhost"); - BOOST_CHECK( uri.get_valid() ); BOOST_CHECK( !uri.get_secure() ); - BOOST_CHECK_EQUAL( uri.get_scheme(), "ws"); + BOOST_CHECK_EQUAL( uri.get_type(), websocketpp::uri::websocket ); BOOST_CHECK_EQUAL( uri.get_host(), "localhost"); BOOST_CHECK_EQUAL( uri.get_port(), 80 ); BOOST_CHECK_EQUAL( uri.get_resource(), "/" ); @@ -172,9 +170,8 @@ BOOST_AUTO_TEST_CASE( uri_valid_no_slash ) { BOOST_AUTO_TEST_CASE( uri_valid_no_slash_with_fragment ) { websocketpp::uri uri("ws://localhost#foo"); - BOOST_CHECK( uri.get_valid() ); BOOST_CHECK( !uri.get_secure() ); - BOOST_CHECK_EQUAL( uri.get_scheme(), "ws"); + BOOST_CHECK_EQUAL( uri.get_type(), websocketpp::uri::websocket ); BOOST_CHECK_EQUAL( uri.get_host(), "localhost"); BOOST_CHECK_EQUAL( uri.get_port(), 80 ); BOOST_CHECK_EQUAL( uri.get_resource(), "#foo" ); @@ -184,9 +181,8 @@ BOOST_AUTO_TEST_CASE( uri_valid_no_slash_with_fragment ) { BOOST_AUTO_TEST_CASE( uri_valid_no_port_unsecure ) { websocketpp::uri uri("ws://localhost/chat"); - BOOST_CHECK( uri.get_valid() ); BOOST_CHECK( !uri.get_secure() ); - BOOST_CHECK_EQUAL( uri.get_scheme(), "ws"); + BOOST_CHECK_EQUAL( uri.get_type(), websocketpp::uri::websocket ); BOOST_CHECK_EQUAL( uri.get_host(), "localhost"); BOOST_CHECK_EQUAL( uri.get_port(), 80 ); BOOST_CHECK_EQUAL( uri.get_resource(), "/chat" ); @@ -196,9 +192,8 @@ BOOST_AUTO_TEST_CASE( uri_valid_no_port_unsecure ) { BOOST_AUTO_TEST_CASE( uri_valid_no_port_secure ) { websocketpp::uri uri("wss://localhost/chat"); - BOOST_CHECK( uri.get_valid() ); BOOST_CHECK( uri.get_secure() ); - BOOST_CHECK_EQUAL( uri.get_scheme(), "wss"); + BOOST_CHECK_EQUAL( uri.get_type(), websocketpp::uri::websocket ); BOOST_CHECK_EQUAL( uri.get_host(), "localhost"); BOOST_CHECK_EQUAL( uri.get_port(), 443 ); BOOST_CHECK_EQUAL( uri.get_resource(), "/chat" ); @@ -208,9 +203,8 @@ BOOST_AUTO_TEST_CASE( uri_valid_no_port_secure ) { BOOST_AUTO_TEST_CASE( uri_valid_no_resource ) { websocketpp::uri uri("wss://localhost:9000"); - BOOST_CHECK( uri.get_valid() ); BOOST_CHECK( uri.get_secure() ); - BOOST_CHECK_EQUAL( uri.get_scheme(), "wss"); + BOOST_CHECK_EQUAL( uri.get_type(), websocketpp::uri::websocket ); BOOST_CHECK_EQUAL( uri.get_host(), "localhost"); BOOST_CHECK_EQUAL( uri.get_port(), 9000 ); BOOST_CHECK_EQUAL( uri.get_resource(), "/" ); @@ -220,9 +214,8 @@ BOOST_AUTO_TEST_CASE( uri_valid_no_resource ) { BOOST_AUTO_TEST_CASE( uri_valid_ipv6_literal ) { websocketpp::uri uri("wss://[::1]:9000/chat"); - BOOST_CHECK( uri.get_valid() ); BOOST_CHECK( uri.get_secure() ); - BOOST_CHECK_EQUAL( uri.get_scheme(), "wss"); + BOOST_CHECK_EQUAL( uri.get_type(), websocketpp::uri::websocket ); BOOST_CHECK_EQUAL( uri.get_host(), "::1"); BOOST_CHECK_EQUAL( uri.get_port(), 9000 ); BOOST_CHECK_EQUAL( uri.get_resource(), "/chat" ); @@ -235,9 +228,8 @@ BOOST_AUTO_TEST_CASE( uri_valid_ipv6_literal ) { BOOST_AUTO_TEST_CASE( uri_valid_ipv6_literal_default_port ) { websocketpp::uri uri("wss://[::1]/chat"); - BOOST_CHECK( uri.get_valid() ); BOOST_CHECK( uri.get_secure() ); - BOOST_CHECK_EQUAL( uri.get_scheme(), "wss"); + BOOST_CHECK_EQUAL( uri.get_type(), websocketpp::uri::websocket ); BOOST_CHECK_EQUAL( uri.get_host(), "::1"); BOOST_CHECK_EQUAL( uri.get_port(), 443 ); BOOST_CHECK_EQUAL( uri.get_resource(), "/chat" ); @@ -250,9 +242,8 @@ BOOST_AUTO_TEST_CASE( uri_valid_ipv6_literal_default_port ) { BOOST_AUTO_TEST_CASE( uri_valid_2 ) { websocketpp::uri uri("wss://thor-websocket.zaphoyd.net:88/"); - BOOST_CHECK( uri.get_valid() ); BOOST_CHECK( uri.get_secure() ); - BOOST_CHECK_EQUAL( uri.get_scheme(), "wss"); + BOOST_CHECK_EQUAL( uri.get_type(), websocketpp::uri::websocket ); BOOST_CHECK_EQUAL( uri.get_host(), "thor-websocket.zaphoyd.net"); BOOST_CHECK_EQUAL( uri.get_port(), 88 ); BOOST_CHECK_EQUAL( uri.get_resource(), "/" ); @@ -277,9 +268,8 @@ BOOST_AUTO_TEST_CASE( uri_invalid_scheme ) { BOOST_AUTO_TEST_CASE( uri_http_scheme ) { websocketpp::uri uri("http://localhost:9000/chat"); - BOOST_CHECK( uri.get_valid() ); BOOST_CHECK( !uri.get_secure() ); - BOOST_CHECK_EQUAL( uri.get_scheme(), "http"); + BOOST_CHECK_EQUAL( uri.get_type(), websocketpp::uri::http ); BOOST_CHECK_EQUAL( uri.get_host(), "localhost"); BOOST_CHECK_EQUAL( uri.get_port(), 9000 ); BOOST_CHECK_EQUAL( uri.get_resource(), "/chat" ); @@ -289,9 +279,8 @@ BOOST_AUTO_TEST_CASE( uri_http_scheme ) { BOOST_AUTO_TEST_CASE( uri_valid_ipv4_literal ) { websocketpp::uri uri("wss://127.0.0.1:9000/chat"); - BOOST_CHECK( uri.get_valid() ); BOOST_CHECK( uri.get_secure() ); - BOOST_CHECK_EQUAL( uri.get_scheme(), "wss"); + BOOST_CHECK_EQUAL( uri.get_type(), websocketpp::uri::websocket ); BOOST_CHECK_EQUAL( uri.get_host(), "127.0.0.1"); BOOST_CHECK_EQUAL( uri.get_port(), 9000 ); BOOST_CHECK_EQUAL( uri.get_resource(), "/chat" ); @@ -301,9 +290,8 @@ BOOST_AUTO_TEST_CASE( uri_valid_ipv4_literal ) { BOOST_AUTO_TEST_CASE( uri_valid_3 ) { websocketpp::uri uri("wss://localhost:9000/chat/foo/bar"); - BOOST_CHECK( uri.get_valid() ); BOOST_CHECK( uri.get_secure() ); - BOOST_CHECK_EQUAL( uri.get_scheme(), "wss"); + BOOST_CHECK_EQUAL( uri.get_type(), websocketpp::uri::websocket ); BOOST_CHECK_EQUAL( uri.get_host(), "localhost"); BOOST_CHECK_EQUAL( uri.get_port(), 9000 ); BOOST_CHECK_EQUAL( uri.get_resource(), "/chat/foo/bar" ); @@ -355,9 +343,8 @@ BOOST_AUTO_TEST_CASE( uri_invalid_free_delim ) { BOOST_AUTO_TEST_CASE( uri_valid_4 ) { websocketpp::uri uri("wss://localhost:9000/chat/foo/bar?foo=bar"); - BOOST_CHECK( uri.get_valid() ); BOOST_CHECK( uri.get_secure() ); - BOOST_CHECK_EQUAL( uri.get_scheme(), "wss" ); + BOOST_CHECK_EQUAL( uri.get_type(), websocketpp::uri::websocket ); BOOST_CHECK_EQUAL( uri.get_host(), "localhost"); BOOST_CHECK_EQUAL( uri.get_port(), 9000 ); BOOST_CHECK_EQUAL( uri.get_resource(), "/chat/foo/bar?foo=bar" ); @@ -368,9 +355,8 @@ BOOST_AUTO_TEST_CASE( uri_valid_4 ) { BOOST_AUTO_TEST_CASE( uri_valid_v4_mapped ) { websocketpp::uri uri("wss://[0000:0000:0000:0000:0000:0000:192.168.1.1]:9000/"); - BOOST_CHECK( uri.get_valid() ); BOOST_CHECK( uri.get_secure() ); - BOOST_CHECK_EQUAL( uri.get_scheme(), "wss" ); + BOOST_CHECK_EQUAL( uri.get_type(), websocketpp::uri::websocket ); BOOST_CHECK_EQUAL( uri.get_host(), "0000:0000:0000:0000:0000:0000:192.168.1.1"); BOOST_CHECK_EQUAL( uri.get_port(), 9000 ); BOOST_CHECK_EQUAL( uri.get_resource(), "/" ); @@ -380,9 +366,8 @@ BOOST_AUTO_TEST_CASE( uri_valid_v4_mapped ) { BOOST_AUTO_TEST_CASE( uri_valid_v6_mixed_case ) { websocketpp::uri uri("wss://[::10aB]:9000/"); - BOOST_CHECK( uri.get_valid() == true ); - BOOST_CHECK( uri.get_secure() == true ); - BOOST_CHECK_EQUAL( uri.get_scheme(), "wss" ); + BOOST_CHECK( uri.get_secure() ); + BOOST_CHECK_EQUAL( uri.get_type(), websocketpp::uri::websocket ); BOOST_CHECK_EQUAL( uri.get_host(), "::10aB"); BOOST_CHECK_EQUAL( uri.get_port(), 9000 ); BOOST_CHECK_EQUAL( uri.get_resource(), "/" ); diff --git a/websocketpp/config/core.hpp b/websocketpp/config/core.hpp index b056ce5a7..bd8f3edb5 100644 --- a/websocketpp/config/core.hpp +++ b/websocketpp/config/core.hpp @@ -152,6 +152,8 @@ struct core { static const long timeout_open_handshake = 5000; /// Length of time before a closing handshake is aborted static const long timeout_close_handshake = 5000; + /// Length of time before a HTTP body read is aborted + static const long timeout_http_read_body = 5000; /// Length of time to wait for a pong after a ping static const long timeout_pong = 5000; diff --git a/websocketpp/config/core_client.hpp b/websocketpp/config/core_client.hpp index 28b2e9c93..1e3611af1 100644 --- a/websocketpp/config/core_client.hpp +++ b/websocketpp/config/core_client.hpp @@ -160,6 +160,8 @@ struct core_client { static const long timeout_open_handshake = 5000; /// Length of time before a closing handshake is aborted static const long timeout_close_handshake = 5000; + /// Length of time before a HTTP body read is aborted + static const long timeout_http_read_body = 5000; /// Length of time to wait for a pong after a ping static const long timeout_pong = 5000; diff --git a/websocketpp/config/debug.hpp b/websocketpp/config/debug.hpp index fa841e236..54c67da4e 100644 --- a/websocketpp/config/debug.hpp +++ b/websocketpp/config/debug.hpp @@ -152,6 +152,8 @@ struct debug_core { static const long timeout_open_handshake = 5000; /// Length of time before a closing handshake is aborted static const long timeout_close_handshake = 5000; + /// Length of time before a HTTP body read is aborted + static const long timeout_http_read_body = 5000; /// Length of time to wait for a pong after a ping static const long timeout_pong = 5000; diff --git a/websocketpp/config/minimal_server.hpp b/websocketpp/config/minimal_server.hpp index 01c12cb3b..65f08e7d2 100644 --- a/websocketpp/config/minimal_server.hpp +++ b/websocketpp/config/minimal_server.hpp @@ -178,6 +178,8 @@ struct minimal_server { static const long timeout_open_handshake = 5000; /// Length of time before a closing handshake is aborted static const long timeout_close_handshake = 5000; + /// Length of time before a HTTP body read is aborted + static const long timeout_http_read_body = 5000; /// Length of time to wait for a pong after a ping static const long timeout_pong = 5000; diff --git a/websocketpp/connection.hpp b/websocketpp/connection.hpp index e28cde8ed..ee92686b6 100644 --- a/websocketpp/connection.hpp +++ b/websocketpp/connection.hpp @@ -322,6 +322,7 @@ class connection , m_user_agent(ua) , m_open_handshake_timeout_dur(config::timeout_open_handshake) , m_close_handshake_timeout_dur(config::timeout_close_handshake) + , m_http_read_timeout_dur(config::timeout_http_read_body) , m_pong_timeout_dur(config::timeout_pong) , m_max_message_size(config::max_message_size) , m_state(session::state::connecting) @@ -537,6 +538,32 @@ class connection m_close_handshake_timeout_dur = dur; } + /// Set http body read timeout + /** + * Sets the length of time the library will wait after the HTTP body begins + * to be read before cancelling it. This can be used to guard from http + * responses which are much larger than you anticipate (eg. files) or when + * the responder takes too long to write the body of the response. + * + * Connections that time out will have their http handlers called with the + * http_body_read_timeout error code stored in the connection (use get_ec). + * + * The default value is specified via the compile time config value + * 'timeout_close_handshake'. The default value in the core config + * is 5000ms. A value of 0 will disable the timer entirely. + * + * To be effective, the transport you are using must support timers. See + * the documentation for your transport policy for details about its + * timer support. + * + * @since 0.8.4 + * + * @param dur The length of the http body read timeout in ms + */ + void set_http_body_read_timeout(long dur) { + m_http_read_timeout_dur = dur; + } + /// Set pong timeout /** * Sets the length of time the library will wait for a pong response to a @@ -1270,6 +1297,23 @@ class connection request_type const & get_request() const { return m_request; } + + /// Set request object + /** + * Direct access to setting the request object. + * This can be used to set the request that the should be sent when using + * the connection as a HTTP request instead of a WS initiator. + * Do not use this in WS connections! + * + * Note use of this method involves using behavior specific to the + * configured HTTP policy. Such behavior may not work with alternate HTTP + * policies. + * + * @since 0.8.4 + * + * @param req the request object to use + */ + void set_request(request_type const & req, lib::error_code& ec); /// Get response object /** @@ -1289,6 +1333,31 @@ class connection response_type const & get_response() const { return m_response; } + +#ifdef _WEBSOCKETPP_MOVE_SEMANTICS_ + /// Get response object by using std::move + /** + * Direct access to the HTTP response sent or received as a part of the + * opening handshake. This can be used to call methods of the response + * object that are not part of the standard request API that connection + * wraps. + * + * IMPORTANT: The response in this connection object is invalid for use + * after this operation. Intended only to be used on a throw-away + * single-use HTTP request connection to prevent copying the response. + * + * Note use of this method involves using behavior specific to the + * configured HTTP policy. Such behavior may not work with alternate HTTP + * policies. + * + * @since 0.8.4 + * + * @return A const reference to the raw response object + */ + response_type take_response() { + return std::move(m_response); + } +#endif /// Defer HTTP Response until later (Exception free) /** @@ -1470,6 +1539,8 @@ class connection void handle_open_handshake_timeout(lib::error_code const & ec); void handle_close_handshake_timeout(lib::error_code const & ec); + void handle_read_body_timeout(lib::error_code const & ec); + void handle_read_frame(lib::error_code const & ec, size_t bytes_transferred); void read_frame(); @@ -1690,6 +1761,7 @@ class connection /// constant values long m_open_handshake_timeout_dur; long m_close_handshake_timeout_dur; + long m_http_read_timeout_dur; long m_pong_timeout_dur; size_t m_max_message_size; @@ -1724,7 +1796,7 @@ class connection /// @todo this is not memory efficient. this value is not used after the /// handshake. - std::string m_handshake_buffer; + std::string m_http_message_buffer; /// Pointer to the processor object for this connection /** diff --git a/websocketpp/endpoint.hpp b/websocketpp/endpoint.hpp index 8afa38cc1..a4ff091fa 100644 --- a/websocketpp/endpoint.hpp +++ b/websocketpp/endpoint.hpp @@ -97,6 +97,7 @@ class endpoint : public config::transport_type, public config::endpoint_base { , m_user_agent(::websocketpp::user_agent) , m_open_handshake_timeout_dur(config::timeout_open_handshake) , m_close_handshake_timeout_dur(config::timeout_close_handshake) + , m_http_read_timeout_dur(config::timeout_http_read_body) , m_pong_timeout_dur(config::timeout_pong) , m_max_message_size(config::max_message_size) , m_max_http_body_size(config::max_http_body_size) @@ -112,7 +113,7 @@ class endpoint : public config::transport_type, public config::endpoint_base { /// Destructor - ~endpoint() {} + virtual ~endpoint() {} #ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ // no copy constructor because endpoints are not copyable @@ -144,6 +145,7 @@ class endpoint : public config::transport_type, public config::endpoint_base { , m_open_handshake_timeout_dur(o.m_open_handshake_timeout_dur) , m_close_handshake_timeout_dur(o.m_close_handshake_timeout_dur) + , m_http_read_timeout_dur(o.m_http_read_timeout_dur) , m_pong_timeout_dur(o.m_pong_timeout_dur) , m_max_message_size(o.m_max_message_size) , m_max_http_body_size(o.m_max_http_body_size) @@ -382,6 +384,33 @@ class endpoint : public config::transport_type, public config::endpoint_base { m_close_handshake_timeout_dur = dur; } + /// Set http body read timeout + /** + * Sets the length of time the library will wait after the HTTP body begins + * to be read before cancelling it. This can be used to guard from http + * responses which are much larger than you anticipate (eg. files) or when + * the responder takes too long to write the body of the response. + * + * Connections that time out will have their http handlers called with the + * http_body_read_timeout error code stored in the connection (use get_ec). + * + * The default value is specified via the compile time config value + * 'timeout_close_handshake'. The default value in the core config + * is 5000ms. A value of 0 will disable the timer entirely. + * + * To be effective, the transport you are using must support timers. See + * the documentation for your transport policy for details about its + * timer support. + * + * @since 0.8.4 + * + * @param dur The length of the http body read timeout in ms + */ + void set_http_body_read_timeout(long dur) { + scoped_lock_type guard(m_mutex); + m_http_read_timeout_dur = dur; + } + /// Set pong timeout /** * Sets the length of time the library will wait for a pong response to a @@ -686,6 +715,7 @@ class endpoint : public config::transport_type, public config::endpoint_base { long m_open_handshake_timeout_dur; long m_close_handshake_timeout_dur; + long m_http_read_timeout_dur; long m_pong_timeout_dur; size_t m_max_message_size; size_t m_max_http_body_size; diff --git a/websocketpp/error.hpp b/websocketpp/error.hpp index 20f409dce..6121783a1 100644 --- a/websocketpp/error.hpp +++ b/websocketpp/error.hpp @@ -146,7 +146,10 @@ enum value { extension_neg_failed, /// General transport error, consult more specific transport error code - transport_error + transport_error, + + /// HTTP body read timed out + http_body_read_timeout }; // enum value @@ -226,6 +229,8 @@ class category : public lib::error_category { return "Extension negotiation failed"; case error::transport_error: return "An error occurred in the underlying transport. Consult transport error code for more details."; + case error::http_body_read_timeout: + return "Reading the HTTP response body timed out"; default: return "Unknown"; } diff --git a/websocketpp/http/impl/response.hpp b/websocketpp/http/impl/response.hpp index 8890ee103..2ed0baf45 100644 --- a/websocketpp/http/impl/response.hpp +++ b/websocketpp/http/impl/response.hpp @@ -40,13 +40,13 @@ namespace http { namespace parser { inline size_t response::consume(char const * buf, size_t len, lib::error_code & ec) { - if (m_state == DONE) { + if (m_state == state::DONE) { // the response is already complete. End immediately without reading. ec = lib::error_code(); return 0; } - if (m_state == BODY) { + if (m_state == state::BODY) { // The headers are complete, but we are still expecting more body // bytes. Process body bytes. return this->process_body(buf,len,ec); @@ -119,7 +119,7 @@ inline size_t response::consume(char const * buf, size_t len, lib::error_code & // If we are still looking for a response line then this request // is incomplete - if (m_state == RESPONSE_LINE) { + if (m_state == state::RESPONSE_LINE) { ec = error::make_error_code(error::incomplete_request); return 0; } @@ -140,7 +140,7 @@ inline size_t response::consume(char const * buf, size_t len, lib::error_code & } // transition state to reading the response body - m_state = BODY; + m_state = state::BODY; // calculate how many bytes in the local buffer are bytes we didn't // use for the headers. @@ -168,9 +168,9 @@ inline size_t response::consume(char const * buf, size_t len, lib::error_code & return read; } else { // we got a line - if (m_state == RESPONSE_LINE) { + if (m_state == state::RESPONSE_LINE) { ec = this->process(begin,end); - m_state = HEADERS; + m_state = state::HEADERS; } else { ec = this->process_header(begin,end); } @@ -322,7 +322,7 @@ inline lib::error_code response::process(std::string::iterator begin, inline size_t response::process_body(char const * buf, size_t len, lib::error_code & ec) { // If no content length was set then we read forever and never set m_ready if (m_read == 0) { - m_state = DONE; + m_state = state::DONE; ec = lib::error_code(); return 0; } @@ -334,7 +334,7 @@ inline size_t response::process_body(char const * buf, size_t len, lib::error_co // if we have more bytes than we need read, read only the amount needed // then set done state to_read = m_read; - m_state = DONE; + m_state = state::DONE; } else { // we need more bytes than are available, read them all to_read = len; diff --git a/websocketpp/http/response.hpp b/websocketpp/http/response.hpp index 64bce76a8..ffb18140c 100644 --- a/websocketpp/http/response.hpp +++ b/websocketpp/http/response.hpp @@ -59,11 +59,18 @@ class response : public parser { typedef response type; typedef lib::shared_ptr ptr; + enum class state { + RESPONSE_LINE = 0, + HEADERS = 1, + BODY = 2, + DONE = 3 + }; + response() : m_read(0) , m_buf(lib::make_shared()) , m_status_code(status_code::uninitialized) - , m_state(RESPONSE_LINE) {} + , m_state(state::RESPONSE_LINE) {} /// Process bytes in the input buffer /** @@ -142,12 +149,12 @@ class response : public parser { * @note will never return true if the content length header is not present */ bool ready() const { - return m_state == DONE; + return m_state == state::DONE; } - /// Returns true if the response headers are fully parsed. - bool headers_ready() const { - return (m_state == BODY || m_state == DONE); + /// Returns true if a particular state has been passed + bool has_received(state s) const { + return m_state > s; } /// Returns the full raw response @@ -197,13 +204,6 @@ class response : public parser { /// Helper function for processing body bytes size_t process_body(char const * buf, size_t len, lib::error_code & ec); - enum state { - RESPONSE_LINE = 0, - HEADERS = 1, - BODY = 2, - DONE = 3 - }; - std::string m_status_msg; size_t m_read; lib::shared_ptr m_buf; diff --git a/websocketpp/impl/connection_impl.hpp b/websocketpp/impl/connection_impl.hpp index 1535e69fd..449461f61 100644 --- a/websocketpp/impl/connection_impl.hpp +++ b/websocketpp/impl/connection_impl.hpp @@ -109,7 +109,7 @@ lib::error_code connection::send(typename config::message_type::ptr msg) { scoped_lock_type lock(m_connection_state_lock); - if (m_state != session::state::open) { + if (m_state != session::state::open || m_is_http) { return error::make_error_code(error::invalid_state); } } @@ -159,7 +159,7 @@ void connection::ping(std::string const& payload, lib::error_code& ec) { { scoped_lock_type lock(m_connection_state_lock); - if (m_state != session::state::open) { + if (m_state != session::state::open || m_is_http) { std::stringstream ss; ss << "connection::ping called from invalid state " << m_state; m_alog->write(log::alevel::devel,ss.str()); @@ -442,6 +442,7 @@ template void connection::set_uri(uri_ptr uri) { //scoped_lock_type lock(m_connection_state_lock); m_uri = uri; + m_is_http = m_uri && m_uri->get_type() == uri::http; } @@ -647,6 +648,18 @@ void connection::set_body(std::string && value) { #endif // _WEBSOCKETPP_NO_EXCEPTIONS_ #endif // _WEBSOCKETPP_MOVE_SEMANTICS_ +template +void connection::set_request(request_type const & req, + lib::error_code& ec) +{ + if (m_internal_state != istate::USER_INIT) { + ec = error::make_error_code(error::invalid_state); + return; + } + + m_request = req; +} + template void connection::append_header(std::string const & key, std::string const & val, lib::error_code & ec) @@ -866,11 +879,30 @@ void connection::handle_transport_init(lib::error_code const & ec) { m_internal_state = istate::READ_HTTP_REQUEST; this->read_handshake(1); } else { - // We are a client. Set the processor to the version specified in the - // config file and send a handshake request. - m_internal_state = istate::WRITE_HTTP_REQUEST; - m_processor = get_processor(config::client_version); - this->send_http_request(); + // We are a client. + m_internal_state = istate::WRITE_HTTP_REQUEST; + if (!m_is_http) + { + // Set the processor to the version specified in the config file and send a handshake request. + m_processor = get_processor(config::client_version); + + // Have the protocol processor fill in the appropriate fields based on the + // selected client version + if (m_processor) { + lib::error_code ec; + ec = m_processor->client_handshake_request(m_request,m_uri, + m_requested_subprotocols); + + if (ec) { + log_err(log::elevel::fatal,"Internal library error: Processor",ec); + return; + } + } else { + m_elog->write(log::elevel::fatal,"Internal library error: missing processor"); + return; + } + } + this->send_http_request(); } } @@ -1294,10 +1326,7 @@ session::validation::value connection::process_handshake_request(lib::er m_alog->write(log::alevel::devel,"HTTP REQUEST"); // extract URI from request - m_uri = processor::get_uri_from_host( - m_request, - (transport_con_type::is_secure() ? "https" : "http") - ); + m_uri = processor::get_uri_from_host(m_request, uri::http, transport_con_type::is_secure()); if (!m_uri->get_valid()) { m_alog->write(log::alevel::devel, "Bad request: failed to parse uri"); @@ -1409,7 +1438,7 @@ lib::error_code connection::finalize_handshake_response(bool accept) { } } else { // User application has rejected the handshake - m_alog->write(log::alevel::devel, "USER REJECT"); + m_alog->write(log::alevel::devel, "WebSocket connection rejected"); // Use Bad Request if the user handler did not provide a more // specific http response error code. @@ -1462,14 +1491,14 @@ void connection::write_http_response(lib::error_code const & ec) { // have the processor generate the raw bytes for the wire (if it exists) if (m_processor) { - m_handshake_buffer = m_processor->get_raw(m_response); + m_http_message_buffer = m_processor->get_raw(m_response); } else { // a processor wont exist for raw HTTP responses. - m_handshake_buffer = m_response.raw(); + m_http_message_buffer = m_response.raw(); } if (m_alog->static_test(log::alevel::devel)) { - m_alog->write(log::alevel::devel,"Raw Handshake response:\n"+m_handshake_buffer); + m_alog->write(log::alevel::devel,"Raw Handshake response:\n"+m_http_message_buffer); if (!m_response.get_header("Sec-WebSocket-Key3").empty()) { m_alog->write(log::alevel::devel, utility::to_hex(m_response.get_header("Sec-WebSocket-Key3"))); @@ -1478,8 +1507,8 @@ void connection::write_http_response(lib::error_code const & ec) { // write raw bytes transport_con_type::async_write( - m_handshake_buffer.data(), - m_handshake_buffer.size(), + m_http_message_buffer.data(), + m_http_message_buffer.size(), lib::bind( &type::handle_write_http_response, type::get_shared(), @@ -1577,23 +1606,7 @@ void connection::send_http_request() { // TODO: origin header? - // Have the protocol processor fill in the appropriate fields based on the - // selected client version - if (m_processor) { - lib::error_code ec; - ec = m_processor->client_handshake_request(m_request,m_uri, - m_requested_subprotocols); - - if (ec) { - log_err(log::elevel::fatal,"Internal library error: Processor",ec); - return; - } - } else { - m_elog->write(log::elevel::fatal,"Internal library error: missing processor"); - return; - } - - // Unless the user has overridden the user agent, send generic WS++ UA. + // Unless the user has overridden the user agent, send generic UA. if (m_request.get_header("User-Agent").empty()) { if (!m_user_agent.empty()) { m_request.replace_header("User-Agent",m_user_agent); @@ -1602,10 +1615,10 @@ void connection::send_http_request() { } } - m_handshake_buffer = m_request.raw(); + m_http_message_buffer = m_request.raw(); if (m_alog->static_test(log::alevel::devel)) { - m_alog->write(log::alevel::devel,"Raw Handshake request:\n"+m_handshake_buffer); + m_alog->write(log::alevel::devel,"Raw Handshake request:\n"+m_http_message_buffer); } if (m_open_handshake_timeout_dur > 0) { @@ -1620,8 +1633,8 @@ void connection::send_http_request() { } transport_con_type::async_write( - m_handshake_buffer.data(), - m_handshake_buffer.size(), + m_http_message_buffer.data(), + m_http_message_buffer.size(), lib::bind( &type::handle_send_http_request, type::get_shared(), @@ -1644,6 +1657,8 @@ void connection::handle_send_http_request(lib::error_code const & ec) { ecm = error::make_error_code(error::invalid_state); } else { m_internal_state = istate::READ_HTTP_RESPONSE; + if (m_is_http) + m_state = session::state::open; } } else if (m_state == session::state::closed) { // The connection was canceled while the response was being sent, @@ -1694,7 +1709,7 @@ void connection::handle_read_http_response(lib::error_code const & ec, if (!ecm) { scoped_lock_type lock(m_connection_state_lock); - if (m_state == session::state::connecting) { + if (m_state == session::state::connecting || (m_is_http && m_state == session::state::open)) { if (m_internal_state != istate::READ_HTTP_RESPONSE) { ecm = error::make_error_code(error::invalid_state); } @@ -1718,9 +1733,13 @@ void connection::handle_read_http_response(lib::error_code const & ec, return; } - log_err(log::elevel::rerror,"handle_read_http_response",ecm); - this->terminate(ecm); - return; + // for http requests, it's normal to receive EOF + if (!(ecm == transport::error::eof && m_is_http)) + { + log_err(log::elevel::rerror,"handle_read_http_response",ecm); + this->terminate(ecm); + return; + } } size_t bytes_processed = 0; @@ -1739,12 +1758,56 @@ void connection::handle_read_http_response(lib::error_code const & ec, m_alog->write(log::alevel::devel,std::string("Raw response: ")+m_response.raw()); - if (m_response.headers_ready()) { - if (m_handshake_timer) { + if (m_response.has_received(response_type::state::HEADERS)) { + if (m_handshake_timer) { m_handshake_timer->cancel(); m_handshake_timer.reset(); } + if (m_is_http) + { + if (m_response.has_received(response_type::state::BODY)) + { + this->terminate({}); + } else { + // The HTTP parser reported that it was not ready and wants more data. + // Assert that it actually consumed all the data present before overwriting + // the buffer. This should always be the case. + if (bytes_transferred != bytes_processed) { + m_elog->write(log::elevel::fatal,"Assertion Failed: HTTP response parser failed to consume all bytes from a read request."); + this->terminate(make_error_code(error::general)); + return; + } + + // Start a timer if we have not yet + // so we don't wait forever for the http body + // it's okay to use the handskahe timer as it's not being used by anything else + if (m_http_read_timeout_dur > 0 && !m_handshake_timer) { + m_handshake_timer = transport_con_type::set_timer( + m_http_read_timeout_dur, + lib::bind( + &type::handle_close_handshake_timeout, + type::get_shared(), + lib::placeholders::_1 + ) + ); + } + + transport_con_type::async_read_at_least( + 1, + m_buf, + config::connection_read_buffer_size, + lib::bind( + &type::handle_read_http_response, + type::get_shared(), + lib::placeholders::_1, + lib::placeholders::_2 + ) + ); + } + return; + } + lib::error_code validate_ec = m_processor->validate_server_handshake_response( m_request, m_response @@ -1847,6 +1910,21 @@ void connection::handle_close_handshake_timeout( } } +template +void connection::handle_read_body_timeout(lib::error_code const & ec) +{ + if (ec == transport::error::operation_aborted) { + m_alog->write(log::alevel::devel,"asio http read body timer cancelled"); + } else if (ec) { + m_alog->write(log::alevel::devel, + "asio handle_read_body_timeout error: "+ec.message()); + // TODO: ignore or fail here? + } else { + m_alog->write(log::alevel::devel, "asio http read body timer expired"); + terminate(make_error_code(error::http_body_read_timeout)); + } +} + template void connection::terminate(lib::error_code const & ec) { if (m_alog->static_test(log::alevel::devel)) { @@ -1862,7 +1940,7 @@ void connection::terminate(lib::error_code const & ec) { // Cancel ping timer if (m_ping_timer) { m_ping_timer->cancel(); - m_handshake_timer.reset(); + m_ping_timer.reset(); } terminate_status tstat = unknown; @@ -1875,6 +1953,8 @@ void connection::terminate(lib::error_code const & ec) { // TODO: does any of this need a mutex? if (m_is_http) { m_http_state = session::http_state::closed; + if (m_http_handler) + m_http_handler(m_connection_hdl); } if (m_state == session::state::connecting) { m_state = session::state::closed; diff --git a/websocketpp/impl/endpoint_impl.hpp b/websocketpp/impl/endpoint_impl.hpp index a70efb842..b7b83e16f 100644 --- a/websocketpp/impl/endpoint_impl.hpp +++ b/websocketpp/impl/endpoint_impl.hpp @@ -72,6 +72,9 @@ endpoint::create_connection(lib::error_code & ec) { } if (m_close_handshake_timeout_dur != config::timeout_close_handshake) { con->set_close_handshake_timeout(m_close_handshake_timeout_dur); + } + if (m_http_read_timeout_dur != config::timeout_http_read_body) { + con->set_http_body_read_timeout(m_http_read_timeout_dur); } if (m_pong_timeout_dur != config::timeout_pong) { con->set_pong_timeout(m_pong_timeout_dur); diff --git a/websocketpp/processors/hybi00.hpp b/websocketpp/processors/hybi00.hpp index 173eff05b..0db08e80d 100644 --- a/websocketpp/processors/hybi00.hpp +++ b/websocketpp/processors/hybi00.hpp @@ -228,9 +228,10 @@ class hybi00 : public processor { if (last_colon == std::string::npos || (last_sbrace != std::string::npos && last_sbrace > last_colon)) { - return lib::make_shared(base::m_secure, h, request.get_uri()); + return lib::make_shared(uri::websocket, base::m_secure, + h, request.get_uri()); } else { - return lib::make_shared(base::m_secure, + return lib::make_shared(uri::websocket, base::m_secure, h.substr(0,last_colon), h.substr(last_colon+1), request.get_uri()); diff --git a/websocketpp/processors/hybi13.hpp b/websocketpp/processors/hybi13.hpp index 91813f620..beca4b915 100644 --- a/websocketpp/processors/hybi13.hpp +++ b/websocketpp/processors/hybi13.hpp @@ -349,7 +349,7 @@ class hybi13 : public processor { } uri_ptr get_uri(request_type const & request) const { - return get_uri_from_host(request,(base::m_secure ? "wss" : "ws")); + return get_uri_from_host(request, uri::websocket, base::m_secure); } /// Process new websocket connection bytes diff --git a/websocketpp/processors/processor.hpp b/websocketpp/processors/processor.hpp index 5131cc45e..71b61bab9 100644 --- a/websocketpp/processors/processor.hpp +++ b/websocketpp/processors/processor.hpp @@ -133,7 +133,7 @@ int get_websocket_version(request_type& r) { * @return A uri_pointer that encodes the value of the host header. */ template -uri_ptr get_uri_from_host(request_type & request, std::string scheme) { +uri_ptr get_uri_from_host(request_type & request, uri::type scheme, bool secure) { std::string h = request.get_header("Host"); size_t last_colon = h.rfind(":"); @@ -146,9 +146,9 @@ uri_ptr get_uri_from_host(request_type & request, std::string scheme) { if (last_colon == std::string::npos || (last_sbrace != std::string::npos && last_sbrace > last_colon)) { - return lib::make_shared(scheme, h, request.get_uri()); + return lib::make_shared(scheme, secure, h, request.get_uri()); } else { - return lib::make_shared(scheme, + return lib::make_shared(scheme, secure, h.substr(0,last_colon), h.substr(last_colon+1), request.get_uri()); diff --git a/websocketpp/transport/asio/connection.hpp b/websocketpp/transport/asio/connection.hpp index e9543fb71..032f6a5ab 100644 --- a/websocketpp/transport/asio/connection.hpp +++ b/websocketpp/transport/asio/connection.hpp @@ -106,6 +106,8 @@ class connection : public config::socket_type::socket_con_type { m_alog->write(log::alevel::devel,"asio con transport constructor"); } + virtual ~connection() {} + /// Get a shared pointer to this component ptr get_shared() { return lib::static_pointer_cast(socket_con_type::get_shared()); @@ -791,7 +793,7 @@ class connection : public config::socket_type::socket_con_type { return; } - if (!m_proxy_data->res.headers_ready()) { + if (!m_proxy_data->res.has_received(response_type::state::HEADERS)) { // we read until the headers were done in theory but apparently // they aren't. Internal endpoint error. callback(make_error_code(error::general)); diff --git a/websocketpp/transport/asio/endpoint.hpp b/websocketpp/transport/asio/endpoint.hpp index 125b87642..ce1112f17 100644 --- a/websocketpp/transport/asio/endpoint.hpp +++ b/websocketpp/transport/asio/endpoint.hpp @@ -102,7 +102,7 @@ class endpoint : public config::socket_type { //std::cout << "transport::asio::endpoint constructor" << std::endl; } - ~endpoint() { + virtual ~endpoint() { // clean up our io_service if we were initialized with an internal one. // Explicitly destroy local objects diff --git a/websocketpp/transport/asio/security/tls.hpp b/websocketpp/transport/asio/security/tls.hpp index b9f4b52ce..c795f9a92 100644 --- a/websocketpp/transport/asio/security/tls.hpp +++ b/websocketpp/transport/asio/security/tls.hpp @@ -349,17 +349,24 @@ class connection : public lib::enable_shared_from_this { void handle_async_shutdown(socket::shutdown_handler callback, lib::asio::error_code const & ec) { + lib::asio::error_code code { ec }; if (ec == lib::asio::error::operation_aborted) { const int shutdownState = SSL_get_shutdown(get_socket().native_handle()); if (shutdownState & SSL_RECEIVED_SHUTDOWN) { - callback(lib::asio::error_code(lib::asio::error::not_connected, ec.category())); - return; + code = lib::asio::error_code(lib::asio::error::not_connected, ec.category()); } } - callback(ec); + if (ec == lib::asio::error::eof) + { + // Rationale: + // http://stackoverflow.com/questions/25587403/boost-asio-ssl-async-shutdown-always-finishes-with-an-error + code.clear(); + } + + callback(code); } public: diff --git a/websocketpp/uri.hpp b/websocketpp/uri.hpp index b2e6ebaaf..7661238ee 100644 --- a/websocketpp/uri.hpp +++ b/websocketpp/uri.hpp @@ -47,7 +47,6 @@ static uint16_t const uri_default_port = 80; static uint16_t const uri_default_secure_port = 443; - /// A group of helper methods for parsing and validating URIs against RFC 3986 namespace uri_helper { @@ -389,6 +388,8 @@ inline bool reg_name(std::string::const_iterator start, std::string::const_itera return true; } +const std::string schema_separtator { "://" }; + } // end namespace uri_helper @@ -396,35 +397,60 @@ inline bool reg_name(std::string::const_iterator start, std::string::const_itera class uri { public: - explicit uri(std::string const & uri_string) : m_valid(false), m_ipv6_literal(false) { - std::string::const_iterator it; + enum type{ + invalid, + http, + websocket, + relative + }; + + uri() : m_type(invalid) {} + uri(uri const & copy) = default; + explicit uri(std::string const & uri_string) : m_type(invalid), m_ipv6_literal(false) { + + if (uri_string.empty()) + return; + + const size_t schema_end = uri_string.find(uri_helper::schema_separtator); + if (schema_end != std::string::npos) + { + const std::string schema(uri_string.substr(0, schema_end)); + if (schema == "wss") + { + m_secure = true; + m_type = websocket; + } + else if (schema == "ws") + { + m_secure = false; + m_type = websocket; + } + else if (schema == "https") + { + m_secure = false; + m_type = http; + } + else if (schema == "http") + { + m_secure = true; + m_type = http; + } else { + return; + } + } else if (uri_string.starts_with('/')) { + m_type = relative; + m_resource = uri_string; + return; + } else { + return; // invalid! + } + + std::string::const_iterator it = uri_string.begin() + schema_end + uri_helper::schema_separtator.length(); std::string::const_iterator temp; int state = 0; - it = uri_string.begin(); - size_t uri_len = uri_string.length(); - - // extract scheme. We only consider Websocket and HTTP URI schemes as valid - if (uri_len >= 7 && std::equal(it,it+6,"wss://")) { - m_secure = true; - m_scheme = "wss"; - it += 6; - } else if (uri_len >= 6 && std::equal(it,it+5,"ws://")) { - m_secure = false; - m_scheme = "ws"; - it += 5; - } else if (uri_len >= 8 && std::equal(it,it+7,"http://")) { - m_secure = false; - m_scheme = "http"; - it += 7; - } else if (uri_len >= 9 && std::equal(it,it+8,"https://")) { - m_secure = true; - m_scheme = "https"; - it += 8; - } else { - return; - } + std::string schema; // extract host. // either a host string @@ -447,10 +473,12 @@ class uri { } if (temp == uri_string.end()) { + m_type = invalid; return; } else { // validate IPv6 literal parts if (!uri_helper::ipv6_literal(it,temp)) { + m_type = invalid; return; } else { m_ipv6_literal = true; @@ -473,6 +501,7 @@ class uri { ++it; } else { // problem + m_type = invalid; return; } } else { @@ -508,6 +537,7 @@ class uri { // either @, [, or ] // @ = userinfo fragment // [ and ] = illegal, basically + m_type = invalid; return; } } else { @@ -525,6 +555,7 @@ class uri { // if we stop parsing the port and there wasn't actually a port // we have an invalid URI if (port.empty()) { + m_type = invalid; return; } state = 3; @@ -535,6 +566,7 @@ class uri { // if we stop parsing the port and there wasn't actually a port // we have an invalid URI if (port.empty()) { + m_type = invalid; return; } state = 3; @@ -549,6 +581,7 @@ class uri { m_port = get_port_from_string(port, ec); if (ec) { + m_type = invalid; return; } @@ -560,37 +593,29 @@ class uri { } // todo: validate path component - - - m_valid = true; } - uri(bool secure, std::string const & host, uint16_t port, + uri(type schema, bool secure, std::string const & host, uint16_t port, std::string const & resource) - : m_scheme(secure ? "wss" : "ws") + : m_type(schema) , m_host(host) , m_resource(resource.empty() ? "/" : resource) , m_port(port) , m_secure(secure) { m_ipv6_literal = uri_helper::ipv6_literal(host.begin(), host.end()); - m_valid = m_ipv6_literal || uri_helper::reg_name(host.begin(), host.end()); + if (!m_ipv6_literal && !uri_helper::reg_name(host.begin(), host.end())) + m_type = invalid; } - uri(bool secure, std::string const & host, std::string const & resource) - : m_scheme(secure ? "wss" : "ws") - , m_host(host) - , m_resource(resource.empty() ? "/" : resource) - , m_port(secure ? uri_default_secure_port : uri_default_port) - , m_secure(secure) + uri(type schema, bool secure, std::string const & host, std::string const & resource) : + uri(schema, secure, host, secure ? uri_default_secure_port : uri_default_port, resource) { - m_ipv6_literal = uri_helper::ipv6_literal(host.begin(), host.end()); - m_valid = m_ipv6_literal || uri_helper::reg_name(host.begin(), host.end()); } - uri(bool secure, std::string const & host, std::string const & port, + uri(type schema, bool secure, std::string const & host, std::string const & port, std::string const & resource) - : m_scheme(secure ? "wss" : "ws") + : m_type(schema) , m_host(host) , m_resource(resource.empty() ? "/" : resource) , m_secure(secure) @@ -599,50 +624,26 @@ class uri { m_port = get_port_from_string(port,ec); m_ipv6_literal = uri_helper::ipv6_literal(host.begin(), host.end()); - m_valid = !ec && (m_ipv6_literal || uri_helper::reg_name(host.begin(), host.end())); + if (ec || !(m_ipv6_literal || uri_helper::reg_name(host.begin(), host.end()))) + m_type = invalid; } - uri(std::string const & scheme, std::string const & host, uint16_t port, - std::string const & resource) - : m_scheme(scheme) - , m_host(host) - , m_resource(resource.empty() ? "/" : resource) - , m_port(port) - , m_secure(scheme == "wss" || scheme == "https") - { - m_ipv6_literal = uri_helper::ipv6_literal(host.begin(), host.end()); - m_valid = m_ipv6_literal || uri_helper::reg_name(host.begin(), host.end()); - } - - uri(std::string scheme, std::string const & host, std::string const & resource) - : m_scheme(scheme) - , m_host(host) - , m_resource(resource.empty() ? "/" : resource) - , m_port((scheme == "wss" || scheme == "https") ? uri_default_secure_port : uri_default_port) - , m_secure(scheme == "wss" || scheme == "https") - { - m_ipv6_literal = uri_helper::ipv6_literal(host.begin(), host.end()); - m_valid = m_ipv6_literal || uri_helper::reg_name(host.begin(), host.end()); - } - - uri(std::string const & scheme, std::string const & host, - std::string const & port, std::string const & resource) - : m_scheme(scheme) - , m_host(host) - , m_resource(resource.empty() ? "/" : resource) - , m_secure(scheme == "wss" || scheme == "https") - { - lib::error_code ec; - m_port = get_port_from_string(port,ec); - m_ipv6_literal = uri_helper::ipv6_literal(host.begin(), host.end()); + bool get_valid() const { + return m_type != invalid; + } - m_valid = !ec && (m_ipv6_literal || uri_helper::reg_name(host.begin(), host.end())); + bool is_absolute() const { + return m_type == http || m_type == websocket; } - bool get_valid() const { - return m_valid; + bool is_relative() const { + return m_type == relative; } + type get_type() const { + return m_type; + } + // Check whether the host of this URI is an IPv6 literal address /** * @since 0.8.3 @@ -656,8 +657,8 @@ class uri { return m_secure; } - std::string const & get_scheme() const { - return m_scheme; + std::string get_scheme() const { + return is_absolute() ? (m_type == http ? (m_secure ? "https" : "http") : (m_secure ? "wss" : "ws")) : std::string(); } std::string const & get_host() const { @@ -707,7 +708,7 @@ class uri { std::string str() const { std::stringstream s; - s << m_scheme << "://"; + s << get_scheme() << "://"; if (m_ipv6_literal) { s << "[" << m_host << "]"; } else { @@ -730,7 +731,7 @@ class uri { * @return query portion of the URI. */ std::string get_query() const { - std::size_t found = m_resource.find('?'); + const std::size_t found = m_resource.find('?'); if (found != std::string::npos) { return m_resource.substr(found + 1); } else { @@ -778,12 +779,11 @@ class uri { return static_cast(t_port); } - std::string m_scheme; + type m_type; std::string m_host; std::string m_resource; uint16_t m_port; bool m_secure; - bool m_valid; bool m_ipv6_literal; }; diff --git a/websocketpp/version.hpp b/websocketpp/version.hpp index db263a1e9..0fd6b6fd2 100644 --- a/websocketpp/version.hpp +++ b/websocketpp/version.hpp @@ -44,7 +44,7 @@ static int const major_version = 0; /// Library minor version number static int const minor_version = 8; /// Library patch version number -static int const patch_version = 3; +static int const patch_version = 4; /// Library pre-release flag /** * This is a textual flag indicating the type and number for pre-release @@ -54,7 +54,7 @@ static int const patch_version = 3; static char const prerelease_flag[] = "dev"; /// Default user agent string -static char const user_agent[] = "WebSocket++/0.8.3-dev"; +static char const user_agent[] = "WebSocket++/0.8.4-dev"; } // namespace websocketpp From ef40eb7603c7dc26820b7302d58d40231c438238 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20To=C5=A1kan?= Date: Tue, 24 Jan 2023 19:02:30 +0100 Subject: [PATCH 13/40] Set HTTP version if unset --- websocketpp/impl/connection_impl.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/websocketpp/impl/connection_impl.hpp b/websocketpp/impl/connection_impl.hpp index 449461f61..5e517348e 100644 --- a/websocketpp/impl/connection_impl.hpp +++ b/websocketpp/impl/connection_impl.hpp @@ -901,6 +901,9 @@ void connection::handle_transport_init(lib::error_code const & ec) { m_elog->write(log::elevel::fatal,"Internal library error: missing processor"); return; } + } else { + if (m_request.get_version().empty()) + m_request.set_version("HTTP/1.1"); } this->send_http_request(); } From 6a8ed963cdea800a77daf4b3deb60420232659f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20To=C5=A1kan?= Date: Wed, 25 Jan 2023 14:05:17 +0100 Subject: [PATCH 14/40] Support chunked transfer encoding (HTTP 1.1) --- websocketpp/http/constants.hpp | 4 +- websocketpp/http/impl/parser.hpp | 50 +++++++++++++++++--- websocketpp/http/impl/request.hpp | 10 ++-- websocketpp/http/impl/response.hpp | 70 ++++++++++------------------ websocketpp/http/parser.hpp | 4 +- websocketpp/http/response.hpp | 7 +-- websocketpp/impl/connection_impl.hpp | 26 ++++++----- 7 files changed, 96 insertions(+), 75 deletions(-) diff --git a/websocketpp/http/constants.hpp b/websocketpp/http/constants.hpp index 02382f101..1d97d3dc0 100644 --- a/websocketpp/http/constants.hpp +++ b/websocketpp/http/constants.hpp @@ -55,8 +55,8 @@ typedef std::map attribute_list; */ typedef std::vector< std::pair > parameter_list; -/// Literal value of the HTTP header delimiter -static char const header_delimiter[] = "\r\n"; +/// The sequency used for new lines +static char const http_crlf[] = "\r\n"; /// Literal value of the HTTP header separator static char const header_separator[] = ":"; diff --git a/websocketpp/http/impl/parser.hpp b/websocketpp/http/impl/parser.hpp index c8c0eee78..0c8a3205f 100644 --- a/websocketpp/http/impl/parser.hpp +++ b/websocketpp/http/impl/parser.hpp @@ -149,6 +149,10 @@ inline bool parser::prepare_body(lib::error_code & ec) { // I believe this will only work up to 32bit sizes. Is there a need for // > 4GiB HTTP payloads? m_body_bytes_needed = std::strtoul(cl_header.c_str(),&end,10); + if (end != cl_header.cend().base()) { + ec = error::make_error_code(error::invalid_format); + return false; + } if (m_body_bytes_needed > m_body_bytes_max) { ec = error::make_error_code(error::body_too_large); @@ -159,10 +163,8 @@ inline bool parser::prepare_body(lib::error_code & ec) { ec = lib::error_code(); return true; } else if (get_header("Transfer-Encoding") == "chunked") { - // ec = error::make_error_code(error::unsupported_transfer_encoding); - // TODO: support for chunked transfers? Is that too much HTTP logic? - //m_body_encoding = body_encoding::chunked; - return false; + m_body_encoding = body_encoding::chunked; + return true; } else { ec = lib::error_code(); return false; @@ -173,13 +175,49 @@ inline size_t parser::process_body(char const * buf, size_t len, lib::error_code & ec) { if (m_body_encoding == body_encoding::plain) { - size_t processed = (std::min)(m_body_bytes_needed,len); + const size_t processed = std::min(m_body_bytes_needed, len); m_body.append(buf,processed); m_body_bytes_needed -= processed; ec = lib::error_code(); return processed; } else if (m_body_encoding == body_encoding::chunked) { - ec = error::make_error_code(error::unsupported_transfer_encoding); + // for chunked encoding, read chunks of the body + if (m_body_bytes_needed) { // reading previouslly started chunk, same as plain encoding! + const size_t processed = std::min(m_body_bytes_needed, len); + m_body.append(buf,processed); + m_body_bytes_needed -= processed; + ec = lib::error_code(); + return processed; + } else { // new chunk + // sizes of chunks which are given by the first byte of the response body + const char* newline = std::search(buf, buf + len, http_crlf, http_crlf + sizeof(http_crlf) - 1); + if (newline == buf + len) + { + ec = error::make_error_code(error::invalid_format); + return 0; + } else { + const std::string chunkSizeHex(buf, newline); + + char * end; + m_body_bytes_needed = std::strtoul(chunkSizeHex.c_str(),&end,16); + if (end != chunkSizeHex.cend().base()) { + ec = error::make_error_code(error::invalid_format); + return 0; + } + + if (m_body_bytes_needed + m_body.size() > m_body_bytes_max) { + ec = error::make_error_code(error::body_too_large); + return 0; + } + + if (m_body_bytes_needed == 0) { // this is how the last chunk is marked + return len; // pretend we handled everything! + } + + const size_t processed = (newline - buf) + sizeof(http_crlf) - 1; + return processed + process_body(buf + processed, len - processed, ec); + } + } return 0; // TODO: support for chunked transfers? } else { diff --git a/websocketpp/http/impl/request.hpp b/websocketpp/http/impl/request.hpp index eae77a606..acb7888a7 100644 --- a/websocketpp/http/impl/request.hpp +++ b/websocketpp/http/impl/request.hpp @@ -80,8 +80,8 @@ inline size_t request::consume(char const * buf, size_t len, lib::error_code & e end = std::search( begin, m_buf->end(), - header_delimiter, - header_delimiter+sizeof(header_delimiter)-1 + http_crlf, + http_crlf+sizeof(http_crlf)-1 ); if (end == m_buf->end()) { @@ -113,7 +113,7 @@ inline size_t request::consume(char const * buf, size_t len, lib::error_code & e // represents a line to be processed // update count of header bytes read so far - m_header_bytes += (end-begin+sizeof(header_delimiter)); + m_header_bytes += (end-begin+sizeof(http_crlf)); if (m_header_bytes > max_header_size) { @@ -138,7 +138,7 @@ inline size_t request::consume(char const * buf, size_t len, lib::error_code & e // other logic. bytes_processed = ( len - static_cast(m_buf->end()-end) - + sizeof(header_delimiter) - 1 + + sizeof(http_crlf) - 1 ); // frees memory used temporarily during request parsing @@ -187,7 +187,7 @@ inline size_t request::consume(char const * buf, size_t len, lib::error_code & e // if we got here it means there is another header line to read. // advance our cursor to the first character after the most recent // delimiter found. - begin = end+(sizeof(header_delimiter)-1); + begin = end+(sizeof(http_crlf)-1); } } diff --git a/websocketpp/http/impl/response.hpp b/websocketpp/http/impl/response.hpp index 2ed0baf45..f0cbe515c 100644 --- a/websocketpp/http/impl/response.hpp +++ b/websocketpp/http/impl/response.hpp @@ -70,8 +70,8 @@ inline size_t response::consume(char const * buf, size_t len, lib::error_code & end = std::search( begin, m_buf->end(), - header_delimiter, - header_delimiter + sizeof(header_delimiter) - 1 + http_crlf, + http_crlf + sizeof(http_crlf) - 1 ); if (end == m_buf->end()) { @@ -95,8 +95,6 @@ inline size_t response::consume(char const * buf, size_t len, lib::error_code & m_buf->resize(static_cast(end-begin)); } - m_read += len; - ec = lib::error_code(); return len; } @@ -105,7 +103,7 @@ inline size_t response::consume(char const * buf, size_t len, lib::error_code & // represents a line to be processed // update count of header bytes read so far - m_header_bytes += (end-begin+sizeof(header_delimiter)); + m_header_bytes += (end-begin+sizeof(http_crlf)); if (m_header_bytes > max_header_size) { // This read exceeded max header size @@ -124,20 +122,8 @@ inline size_t response::consume(char const * buf, size_t len, lib::error_code & return 0; } - // TODO: grab content-length - std::string length = get_header("Content-Length"); - - if (length.empty()) { - // no content length found, read indefinitely? - m_read = 0; - } else { - std::istringstream ss(length); - - if ((ss >> m_read).fail()) { - ec = error::make_error_code(error::invalid_format); - return 0; - } - } + if (!prepare_body(ec)) + return 0; // transition state to reading the response body m_state = state::BODY; @@ -146,7 +132,7 @@ inline size_t response::consume(char const * buf, size_t len, lib::error_code & // use for the headers. size_t read = ( len - static_cast(m_buf->end() - end) - + sizeof(header_delimiter) - 1 + + sizeof(http_crlf) - 1 ); // if there were bytes left process them as body bytes. @@ -158,9 +144,14 @@ inline size_t response::consume(char const * buf, size_t len, lib::error_code & read += this->process_body(buf+read,(len-read),ec); } if (ec) { + m_state = state::DONE; return 0; } + if (m_body_bytes_needed == 0) { + m_state = state::DONE; + } + // frees memory used temporarily during header parsing m_buf.reset(); @@ -182,7 +173,7 @@ inline size_t response::consume(char const * buf, size_t len, lib::error_code & // if we got here it means there is another header line to read. // advance our cursor to the first character after the most recent // delimiter found. - begin = end+(sizeof(header_delimiter) - 1); + begin = end+(sizeof(http_crlf) - 1); } } @@ -320,30 +311,19 @@ inline lib::error_code response::process(std::string::iterator begin, } inline size_t response::process_body(char const * buf, size_t len, lib::error_code & ec) { - // If no content length was set then we read forever and never set m_ready - if (m_read == 0) { - m_state = state::DONE; - ec = lib::error_code(); - return 0; - } - - // Otherwise m_read is the number of bytes left. - size_t to_read; - - if (len >= m_read) { - // if we have more bytes than we need read, read only the amount needed - // then set done state - to_read = m_read; - m_state = state::DONE; - } else { - // we need more bytes than are available, read them all - to_read = len; - } - - m_body.append(buf,to_read); - m_read -= to_read; - ec = lib::error_code(); - return to_read; + size_t processed = 0; + do + { + const size_t processed_chunk = parser::process_body(buf, len, ec); + buf += processed_chunk; + len -= processed_chunk; + processed += processed_chunk; + } while (!ec && m_body_bytes_needed && len); + + if (ec || m_body_bytes_needed == 0) + m_state = state::DONE; + + return processed; } } // namespace parser diff --git a/websocketpp/http/parser.hpp b/websocketpp/http/parser.hpp index 2b4d194c9..b44ed5b22 100644 --- a/websocketpp/http/parser.hpp +++ b/websocketpp/http/parser.hpp @@ -402,6 +402,8 @@ class parser { , m_body_bytes_needed(0) , m_body_bytes_max(max_body_size) , m_body_encoding(body_encoding::unknown) {} + + virtual ~parser() {} /// Get the HTTP version string /** @@ -605,7 +607,7 @@ class parser { * @param [out] ec A status code describing the outcome of the operation. * @return The number of bytes processed */ - size_t process_body(char const * buf, size_t len, lib::error_code & ec); + virtual size_t process_body(char const * buf, size_t len, lib::error_code & ec); /// Check if the parser is done parsing the body /** diff --git a/websocketpp/http/response.hpp b/websocketpp/http/response.hpp index ffb18140c..110bcaff2 100644 --- a/websocketpp/http/response.hpp +++ b/websocketpp/http/response.hpp @@ -67,8 +67,7 @@ class response : public parser { }; response() - : m_read(0) - , m_buf(lib::make_shared()) + : m_buf(lib::make_shared()) , m_status_code(status_code::uninitialized) , m_state(state::RESPONSE_LINE) {} @@ -201,11 +200,9 @@ class response : public parser { /// Helper function for consume. Process response line lib::error_code process(std::string::iterator begin, std::string::iterator end); - /// Helper function for processing body bytes - size_t process_body(char const * buf, size_t len, lib::error_code & ec); + virtual size_t process_body(char const * buf, size_t len, lib::error_code & ec) override; std::string m_status_msg; - size_t m_read; lib::shared_ptr m_buf; status_code::value m_status_code; state m_state; diff --git a/websocketpp/impl/connection_impl.hpp b/websocketpp/impl/connection_impl.hpp index 5e517348e..bf06ca2f7 100644 --- a/websocketpp/impl/connection_impl.hpp +++ b/websocketpp/impl/connection_impl.hpp @@ -904,6 +904,7 @@ void connection::handle_transport_init(lib::error_code const & ec) { } else { if (m_request.get_version().empty()) m_request.set_version("HTTP/1.1"); + m_request.append_header("Host", m_uri->get_host_port()); } this->send_http_request(); } @@ -1728,26 +1729,29 @@ void connection::handle_read_http_response(lib::error_code const & ec, } } - if (ecm) { - if (ecm == transport::error::eof && m_state == session::state::closed) { + bool should_parse = !ecm; + if (ecm.value() == transport::error::eof) { + if (m_state == session::state::closed) { // we expect to get eof if the connection is closed already m_alog->write(log::alevel::devel, "got (expected) eof/state error from closed con"); return; - } - - // for http requests, it's normal to receive EOF - if (!(ecm == transport::error::eof && m_is_http)) - { - log_err(log::elevel::rerror,"handle_read_http_response",ecm); - this->terminate(ecm); - return; + } else if (m_is_http) { + // for http requests, it's normal to receive EOF + should_parse = true; } } + + if (!should_parse) + { + log_err(log::elevel::rerror,"handle_read_http_response",ecm); + this->terminate(ecm); + return; + } size_t bytes_processed = 0; - lib::error_code consume_ec; + lib::error_code consume_ec(ecm); bytes_processed = m_response.consume(m_buf, bytes_transferred, consume_ec); if (consume_ec) { From 4a2b16056e60323ccff8be474c63eda599b9c6a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20To=C5=A1kan?= Date: Wed, 25 Jan 2023 14:24:57 +0100 Subject: [PATCH 15/40] Fixed parsing of requests without bodies --- websocketpp/http/impl/parser.hpp | 2 +- websocketpp/http/impl/response.hpp | 13 ++++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/websocketpp/http/impl/parser.hpp b/websocketpp/http/impl/parser.hpp index 0c8a3205f..ad266db4a 100644 --- a/websocketpp/http/impl/parser.hpp +++ b/websocketpp/http/impl/parser.hpp @@ -167,7 +167,7 @@ inline bool parser::prepare_body(lib::error_code & ec) { return true; } else { ec = lib::error_code(); - return false; + return true; // valid! means there is no body to read } } diff --git a/websocketpp/http/impl/response.hpp b/websocketpp/http/impl/response.hpp index f0cbe515c..d9e9e5395 100644 --- a/websocketpp/http/impl/response.hpp +++ b/websocketpp/http/impl/response.hpp @@ -42,7 +42,7 @@ namespace parser { inline size_t response::consume(char const * buf, size_t len, lib::error_code & ec) { if (m_state == state::DONE) { // the response is already complete. End immediately without reading. - ec = lib::error_code(); + ec.clear(); return 0; } @@ -95,7 +95,7 @@ inline size_t response::consume(char const * buf, size_t len, lib::error_code & m_buf->resize(static_cast(end-begin)); } - ec = lib::error_code(); + ec.clear(); return len; } @@ -125,6 +125,13 @@ inline size_t response::consume(char const * buf, size_t len, lib::error_code & if (!prepare_body(ec)) return 0; + if (m_body_encoding == body_encoding::unknown) { + m_state = state::DONE; + m_buf.reset(); + ec.clear(); + return len; // pretend we read everything! + } + // transition state to reading the response body m_state = state::BODY; @@ -155,7 +162,7 @@ inline size_t response::consume(char const * buf, size_t len, lib::error_code & // frees memory used temporarily during header parsing m_buf.reset(); - ec = lib::error_code(); + ec.clear(); return read; } else { // we got a line From 88fd69f27a5d729404c133f9e20d77dd9a6c9dd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20To=C5=A1kan?= Date: Wed, 25 Jan 2023 16:05:27 +0100 Subject: [PATCH 16/40] Set request URI if empty --- websocketpp/impl/connection_impl.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/websocketpp/impl/connection_impl.hpp b/websocketpp/impl/connection_impl.hpp index bf06ca2f7..27c9d67ba 100644 --- a/websocketpp/impl/connection_impl.hpp +++ b/websocketpp/impl/connection_impl.hpp @@ -905,6 +905,8 @@ void connection::handle_transport_init(lib::error_code const & ec) { if (m_request.get_version().empty()) m_request.set_version("HTTP/1.1"); m_request.append_header("Host", m_uri->get_host_port()); + if (m_request.get_uri().empty()) + m_request.set_uri(get_uri()->get_resource()); } this->send_http_request(); } From acf4c5400d3caec97052509aa84b6c4790fedecc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20To=C5=A1kan?= Date: Wed, 25 Jan 2023 18:22:38 +0100 Subject: [PATCH 17/40] Fixed URI secure flag not set correctly --- websocketpp/uri.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/websocketpp/uri.hpp b/websocketpp/uri.hpp index 7661238ee..4845528e1 100644 --- a/websocketpp/uri.hpp +++ b/websocketpp/uri.hpp @@ -427,12 +427,12 @@ class uri { } else if (schema == "https") { - m_secure = false; + m_secure = true; m_type = http; } else if (schema == "http") { - m_secure = true; + m_secure = false; m_type = http; } else { return; From bb0ddee28a8801202e43a382aca5984c30d6e17a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20To=C5=A1kan?= Date: Wed, 25 Jan 2023 18:55:53 +0100 Subject: [PATCH 18/40] Fixed Double host entry & websocket initiator requests --- websocketpp/http/impl/parser.hpp | 10 ++++------ websocketpp/http/impl/response.hpp | 6 ++++-- websocketpp/impl/connection_impl.hpp | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/websocketpp/http/impl/parser.hpp b/websocketpp/http/impl/parser.hpp index ad266db4a..a18f43ec0 100644 --- a/websocketpp/http/impl/parser.hpp +++ b/websocketpp/http/impl/parser.hpp @@ -141,6 +141,8 @@ inline bool parser::parse_parameter_list(std::string const & in, } inline bool parser::prepare_body(lib::error_code & ec) { + ec.clear(); + if (!get_header("Content-Length").empty()) { std::string const & cl_header = get_header("Content-Length"); char * end; @@ -160,14 +162,12 @@ inline bool parser::prepare_body(lib::error_code & ec) { } m_body_encoding = body_encoding::plain; - ec = lib::error_code(); - return true; + return m_body_bytes_needed; } else if (get_header("Transfer-Encoding") == "chunked") { m_body_encoding = body_encoding::chunked; return true; } else { - ec = lib::error_code(); - return true; // valid! means there is no body to read + return false; } } @@ -218,8 +218,6 @@ inline size_t parser::process_body(char const * buf, size_t len, return processed + process_body(buf + processed, len - processed, ec); } } - return 0; - // TODO: support for chunked transfers? } else { ec = error::make_error_code(error::unknown_transfer_encoding); return 0; diff --git a/websocketpp/http/impl/response.hpp b/websocketpp/http/impl/response.hpp index d9e9e5395..3ba6f1592 100644 --- a/websocketpp/http/impl/response.hpp +++ b/websocketpp/http/impl/response.hpp @@ -122,10 +122,12 @@ inline size_t response::consume(char const * buf, size_t len, lib::error_code & return 0; } - if (!prepare_body(ec)) + const bool need_more = prepare_body(ec); + if (ec) { return 0; + } - if (m_body_encoding == body_encoding::unknown) { + if (m_body_encoding == body_encoding::unknown || !need_more) { m_state = state::DONE; m_buf.reset(); ec.clear(); diff --git a/websocketpp/impl/connection_impl.hpp b/websocketpp/impl/connection_impl.hpp index 27c9d67ba..d255cd93b 100644 --- a/websocketpp/impl/connection_impl.hpp +++ b/websocketpp/impl/connection_impl.hpp @@ -904,7 +904,7 @@ void connection::handle_transport_init(lib::error_code const & ec) { } else { if (m_request.get_version().empty()) m_request.set_version("HTTP/1.1"); - m_request.append_header("Host", m_uri->get_host_port()); + m_request.replace_header("Host", m_uri->get_host_port()); if (m_request.get_uri().empty()) m_request.set_uri(get_uri()->get_resource()); } From b4f946bca6936da63a48271ba6efca3b290c8750 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20To=C5=A1kan?= Date: Thu, 26 Jan 2023 15:37:49 +0100 Subject: [PATCH 19/40] Fixed relative HTTP requests --- websocketpp/http/impl/parser.hpp | 45 +++++++++++++++------------- websocketpp/http/impl/request.hpp | 10 +++---- websocketpp/http/impl/response.hpp | 16 +++------- websocketpp/impl/connection_impl.hpp | 23 +++++++++++--- 4 files changed, 51 insertions(+), 43 deletions(-) diff --git a/websocketpp/http/impl/parser.hpp b/websocketpp/http/impl/parser.hpp index a18f43ec0..9a01c0849 100644 --- a/websocketpp/http/impl/parser.hpp +++ b/websocketpp/http/impl/parser.hpp @@ -174,6 +174,10 @@ inline bool parser::prepare_body(lib::error_code & ec) { inline size_t parser::process_body(char const * buf, size_t len, lib::error_code & ec) { + if (!len) { + return 0; + } + if (m_body_encoding == body_encoding::plain) { const size_t processed = std::min(m_body_bytes_needed, len); m_body.append(buf,processed); @@ -195,28 +199,27 @@ inline size_t parser::process_body(char const * buf, size_t len, { ec = error::make_error_code(error::invalid_format); return 0; - } else { - const std::string chunkSizeHex(buf, newline); - - char * end; - m_body_bytes_needed = std::strtoul(chunkSizeHex.c_str(),&end,16); - if (end != chunkSizeHex.cend().base()) { - ec = error::make_error_code(error::invalid_format); - return 0; - } - - if (m_body_bytes_needed + m_body.size() > m_body_bytes_max) { - ec = error::make_error_code(error::body_too_large); - return 0; - } - - if (m_body_bytes_needed == 0) { // this is how the last chunk is marked - return len; // pretend we handled everything! - } - - const size_t processed = (newline - buf) + sizeof(http_crlf) - 1; - return processed + process_body(buf + processed, len - processed, ec); } + + const std::string chunkSizeHex(buf, newline); + char * end; + m_body_bytes_needed = std::strtoul(chunkSizeHex.c_str(),&end,16); + if (end != chunkSizeHex.cend().base()) { + ec = error::make_error_code(error::invalid_format); + return 0; + } + + if (m_body_bytes_needed + m_body.size() > m_body_bytes_max) { + ec = error::make_error_code(error::body_too_large); + return 0; + } + + if (m_body_bytes_needed == 0) { // this is how the last chunk is marked + return len; // pretend we handled everything! + } + + const size_t processed = (newline - buf) + sizeof(http_crlf) - 1; + return processed + process_body(buf + processed, len - processed, ec); } } else { ec = error::make_error_code(error::unknown_transfer_encoding); diff --git a/websocketpp/http/impl/request.hpp b/websocketpp/http/impl/request.hpp index acb7888a7..e58eae956 100644 --- a/websocketpp/http/impl/request.hpp +++ b/websocketpp/http/impl/request.hpp @@ -161,15 +161,13 @@ inline size_t request::consume(char const * buf, size_t len, lib::error_code & e if (body_ready()) { m_ready = true; } - ec = lib::error_code(); - return bytes_processed; } else { m_ready = true; - - // return number of bytes processed (starting bytes - bytes left) - ec = lib::error_code(); - return bytes_processed; } + + // return number of bytes processed (starting bytes - bytes left) + ec = lib::error_code(); + return bytes_processed; } else { // we got a line with content if (m_method.empty()) { diff --git a/websocketpp/http/impl/response.hpp b/websocketpp/http/impl/response.hpp index 3ba6f1592..8f53de00f 100644 --- a/websocketpp/http/impl/response.hpp +++ b/websocketpp/http/impl/response.hpp @@ -122,21 +122,14 @@ inline size_t response::consume(char const * buf, size_t len, lib::error_code & return 0; } + // transition state to reading the response body + m_state = state::BODY; + const bool need_more = prepare_body(ec); if (ec) { return 0; } - if (m_body_encoding == body_encoding::unknown || !need_more) { - m_state = state::DONE; - m_buf.reset(); - ec.clear(); - return len; // pretend we read everything! - } - - // transition state to reading the response body - m_state = state::BODY; - // calculate how many bytes in the local buffer are bytes we didn't // use for the headers. size_t read = ( @@ -149,11 +142,10 @@ inline size_t response::consume(char const * buf, size_t len, lib::error_code & // It is possible that there are still some bytes not read. These // will be 'returned' to the caller by having the return value be // less than len. - if (read < len) { + if (read < len && need_more) { read += this->process_body(buf+read,(len-read),ec); } if (ec) { - m_state = state::DONE; return 0; } diff --git a/websocketpp/impl/connection_impl.hpp b/websocketpp/impl/connection_impl.hpp index d255cd93b..f3879153b 100644 --- a/websocketpp/impl/connection_impl.hpp +++ b/websocketpp/impl/connection_impl.hpp @@ -901,12 +901,27 @@ void connection::handle_transport_init(lib::error_code const & ec) { m_elog->write(log::elevel::fatal,"Internal library error: missing processor"); return; } - } else { + } else { // regular http request if (m_request.get_version().empty()) m_request.set_version("HTTP/1.1"); - m_request.replace_header("Host", m_uri->get_host_port()); - if (m_request.get_uri().empty()) - m_request.set_uri(get_uri()->get_resource()); + + std::string host_port; + std::string resource; + + const uri request_uri(m_request.get_uri()); + if (request_uri.is_absolute()) { + host_port = request_uri.get_host_port(); + resource = request_uri.get_resource(); + } else { + host_port = m_uri->get_host_port(); + resource = m_uri->get_resource(); + if (resource.ends_with('/')) // prevent double slashes! + resource.pop_back(); + resource += request_uri.get_resource(); // get_resource should always have a leading slash + } + + m_request.replace_header("Host", host_port);; + m_request.set_uri(resource); } this->send_http_request(); } From b96df34a9e2e1d8bfb174214383ea03e37d92b2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20To=C5=A1kan?= Date: Fri, 27 Jan 2023 17:01:59 +0100 Subject: [PATCH 20/40] Fixed multiple callbacks to m_http_handler when in client mode --- websocketpp/impl/connection_impl.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/websocketpp/impl/connection_impl.hpp b/websocketpp/impl/connection_impl.hpp index f3879153b..de35c69cd 100644 --- a/websocketpp/impl/connection_impl.hpp +++ b/websocketpp/impl/connection_impl.hpp @@ -1975,9 +1975,9 @@ void connection::terminate(lib::error_code const & ec) { } // TODO: does any of this need a mutex? - if (m_is_http) { + if (m_is_http && m_http_state != session::http_state::closed) { m_http_state = session::http_state::closed; - if (m_http_handler) + if (!m_is_server && m_http_handler) // only call m_http_handler here for client connections m_http_handler(m_connection_hdl); } if (m_state == session::state::connecting) { From 99ab78db4d75e34fc431ce044b177c5cfd8f2bf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20To=C5=A1kan?= Date: Wed, 1 Feb 2023 14:30:27 +0100 Subject: [PATCH 21/40] Added follow HTTP redirect option --- websocketpp/connection.hpp | 28 ++++++++++++++++++++++++++ websocketpp/endpoint.hpp | 26 ++++++++++++++++++++++++ websocketpp/http/constants.hpp | 12 +++++++++++ websocketpp/impl/connection_impl.hpp | 30 ++++++++++++++++++++++++++-- websocketpp/impl/endpoint_impl.hpp | 2 ++ 5 files changed, 96 insertions(+), 2 deletions(-) diff --git a/websocketpp/connection.hpp b/websocketpp/connection.hpp index ee92686b6..3f0b8bd21 100644 --- a/websocketpp/connection.hpp +++ b/websocketpp/connection.hpp @@ -320,6 +320,7 @@ class connection lib::placeholders::_1 )) , m_user_agent(ua) + , m_max_redirects(0) , m_open_handshake_timeout_dur(config::timeout_open_handshake) , m_close_handshake_timeout_dur(config::timeout_close_handshake) , m_http_read_timeout_dur(config::timeout_http_read_body) @@ -616,6 +617,30 @@ class connection m_processor->set_max_message_size(new_value); } } + + /// Get maximum number of redirects + /** + * Get maximum number of redirects to follow before returning. + * If 0, never follow redirects (default) + * + * @since 0.8.4 + */ + size_t get_max_redirects() const { + return m_max_redirects; + } + + /// Set maximum number of redirects + /** + * Set maximum number of redirects to follow before returning. + * If 0, never follow redirects (default) + * + * @since 0.8.4 + * + * @param new_value The value to set as the max number of redirects to. + */ + void set_max_redirects(size_t new_value) { + m_max_redirects = new_value; + } /// Get maximum HTTP message body size /** @@ -1743,6 +1768,9 @@ class connection // static settings std::string const m_user_agent; + // dynamic settings (per-connection) + size_t m_max_redirects; + /// Pointer to the connection handle connection_hdl m_connection_hdl; diff --git a/websocketpp/endpoint.hpp b/websocketpp/endpoint.hpp index a4ff091fa..ba2eb285e 100644 --- a/websocketpp/endpoint.hpp +++ b/websocketpp/endpoint.hpp @@ -101,6 +101,7 @@ class endpoint : public config::transport_type, public config::endpoint_base { , m_pong_timeout_dur(config::timeout_pong) , m_max_message_size(config::max_message_size) , m_max_http_body_size(config::max_http_body_size) + , m_max_redirects(0) , m_is_server(p_is_server) { m_alog->set_channels(config::alog_level); @@ -465,6 +466,30 @@ class endpoint : public config::transport_type, public config::endpoint_base { m_max_message_size = new_value; } + /// Get maximum number of redirects + /** + * Get maximum number of redirects to follow before returning. + * If 0, never follow redirects (default) + * + * @since 0.8.4 + */ + size_t get_max_redirects() const { + return m_max_redirects; + } + + /// Set maximum number of redirects + /** + * Set maximum number of redirects to follow before returning. + * If 0, never follow redirects (default) + * + * @since 0.8.4 + * + * @param new_value The value to set as the max number of redirects to. + */ + void set_max_redirects(size_t new_value) { + m_max_redirects = new_value; + } + /// Get maximum HTTP message body size /** * Get maximum HTTP message body size. Maximum message body size determines @@ -719,6 +744,7 @@ class endpoint : public config::transport_type, public config::endpoint_base { long m_pong_timeout_dur; size_t m_max_message_size; size_t m_max_http_body_size; + size_t m_max_redirects; rng_type m_rng; diff --git a/websocketpp/http/constants.hpp b/websocketpp/http/constants.hpp index 1d97d3dc0..218f14fc2 100644 --- a/websocketpp/http/constants.hpp +++ b/websocketpp/http/constants.hpp @@ -179,6 +179,18 @@ enum value { network_authentication_required = 511 }; +/// Given a status code value, return true if it is a redirect +/** + * + * @param[in] code The HTTP status code to check + * @return True if the status code is a redirect code + * @see websocketpp::http::status_code::value (list of valid codes) + */ +inline bool is_redirect(value code) { + // multiple_choices is not a valid redirect as we can't just go to the location specified in the Location header + return code > multiple_choices && code <= temporary_redirect; +} + /// Given a status code value, return the default status message /** * diff --git a/websocketpp/impl/connection_impl.hpp b/websocketpp/impl/connection_impl.hpp index de35c69cd..fb780b721 100644 --- a/websocketpp/impl/connection_impl.hpp +++ b/websocketpp/impl/connection_impl.hpp @@ -1788,8 +1788,34 @@ void connection::handle_read_http_response(lib::error_code const & ec, m_handshake_timer.reset(); } - if (m_is_http) - { + // follow redirect if possible + if (m_max_redirects && http::status_code::is_redirect(m_response.get_status_code())) { + m_max_redirects--; + + const uri redirect_uri(m_response.get_header("Location")); + if (redirect_uri.get_valid()) { + std::string host_port; + std::string resource; + + if (redirect_uri.is_absolute()) { + host_port = redirect_uri.get_host_port(); + resource = redirect_uri.get_resource(); + } else { + host_port = m_uri->get_host_port(); + resource = m_uri->get_resource(); + if (resource.ends_with('/')) // prevent double slashes! + resource.pop_back(); + resource += redirect_uri.get_resource(); // get_resource should always have a leading slash + } + + m_request.replace_header("Host", host_port);; + m_request.set_uri(resource); + this->send_http_request(); + return; + } + } + + if (m_is_http) { if (m_response.has_received(response_type::state::BODY)) { this->terminate({}); diff --git a/websocketpp/impl/endpoint_impl.hpp b/websocketpp/impl/endpoint_impl.hpp index b7b83e16f..2089dfea4 100644 --- a/websocketpp/impl/endpoint_impl.hpp +++ b/websocketpp/impl/endpoint_impl.hpp @@ -67,6 +67,8 @@ endpoint::create_connection(lib::error_code & ec) { con->set_validate_handler(m_validate_handler); con->set_message_handler(m_message_handler); + con->set_max_redirects(m_max_redirects); + if (m_open_handshake_timeout_dur != config::timeout_open_handshake) { con->set_open_handshake_timeout(m_open_handshake_timeout_dur); } From 4fb9604bda0d76d5ea317feff8fad1e2ea9c020a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20To=C5=A1kan?= Date: Wed, 1 Feb 2023 14:39:00 +0100 Subject: [PATCH 22/40] Removed typo in cmakelists --- CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d7d94ec12..9043db327 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -158,7 +158,6 @@ if (BUILD_TESTS OR BUILD_EXAMPLES) else() set (WEBSOCKETPP_PLATFORM_LIBS pthread rt) endif() - endif() set (WEBSOCKETPP_PLATFORM_TLS_LIBS ssl crypto) set (WEBSOCKETPP_BOOST_LIBS system thread) set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") From 4fe3d8cadb7dfdff2f4bad2b39d30c65b2c280ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20To=C5=A1kan?= Date: Mon, 27 Feb 2023 20:33:37 +0100 Subject: [PATCH 23/40] Fixed init handler being called twice when setting TLS SNI extension failed --- websocketpp/transport/asio/security/tls.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/websocketpp/transport/asio/security/tls.hpp b/websocketpp/transport/asio/security/tls.hpp index c795f9a92..cd4fa9914 100644 --- a/websocketpp/transport/asio/security/tls.hpp +++ b/websocketpp/transport/asio/security/tls.hpp @@ -254,6 +254,7 @@ class connection : public lib::enable_shared_from_this { get_socket().native_handle(), host.c_str()); if (!(1 == res)) { callback(socket::make_error_code(socket::error::tls_failed_sni_hostname)); + return; } } } From 236e6b90a658d460d260a04bf6e3788ab06c0d14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20To=C5=A1kan?= Date: Thu, 30 Mar 2023 15:34:40 +0200 Subject: [PATCH 24/40] Use GET method for HTTP request if none provided --- websocketpp/impl/connection_impl.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/websocketpp/impl/connection_impl.hpp b/websocketpp/impl/connection_impl.hpp index fb780b721..961e88702 100644 --- a/websocketpp/impl/connection_impl.hpp +++ b/websocketpp/impl/connection_impl.hpp @@ -904,6 +904,9 @@ void connection::handle_transport_init(lib::error_code const & ec) { } else { // regular http request if (m_request.get_version().empty()) m_request.set_version("HTTP/1.1"); + + if (m_request.get_method().empty()) + m_request.set_method("GET"); std::string host_port; std::string resource; From 780cebe5bc9f460e6ed265a13636825df4af2475 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20To=C5=A1kan?= Date: Mon, 7 Aug 2023 13:45:44 +0200 Subject: [PATCH 25/40] Fixed http response timeouts --- test/transport/integration.cpp | 8 ++-- websocketpp/config/core.hpp | 4 +- websocketpp/config/core_client.hpp | 4 +- websocketpp/config/debug.hpp | 4 +- websocketpp/config/minimal_server.hpp | 4 +- websocketpp/connection.hpp | 24 +++++------ websocketpp/endpoint.hpp | 6 +-- websocketpp/error.hpp | 8 ++-- websocketpp/impl/connection_impl.hpp | 57 ++++++++++++++++----------- websocketpp/impl/endpoint_impl.hpp | 4 +- 10 files changed, 66 insertions(+), 57 deletions(-) diff --git a/test/transport/integration.cpp b/test/transport/integration.cpp index 213ef75a0..9a4cac5c1 100644 --- a/test/transport/integration.cpp +++ b/test/transport/integration.cpp @@ -76,8 +76,8 @@ struct config : public websocketpp::config::asio_client { static const long timeout_open_handshake = 500; /// Length of time before a closing handshake is aborted static const long timeout_close_handshake = 500; - /// Length of time before a HTTP body read is aborted - static const long timeout_http_read_body = 500; + /// Length of time before after sending a request to wait before timing out + static const long timeout_read_http_response = 500; /// Length of time to wait for a pong after a ping static const long timeout_pong = 500; }; @@ -120,8 +120,8 @@ struct config_tls : public websocketpp::config::asio_tls_client { static const long timeout_open_handshake = 500; /// Length of time before a closing handshake is aborted static const long timeout_close_handshake = 500; - /// Length of time before a HTTP body read is aborted - static const long timeout_http_read_body = 500; + /// Length of time before after sending a request to wait before timing out + static const long timeout_read_http_response = 500; /// Length of time to wait for a pong after a ping static const long timeout_pong = 500; }; diff --git a/websocketpp/config/core.hpp b/websocketpp/config/core.hpp index bd8f3edb5..6fa12bef4 100644 --- a/websocketpp/config/core.hpp +++ b/websocketpp/config/core.hpp @@ -152,8 +152,8 @@ struct core { static const long timeout_open_handshake = 5000; /// Length of time before a closing handshake is aborted static const long timeout_close_handshake = 5000; - /// Length of time before a HTTP body read is aborted - static const long timeout_http_read_body = 5000; + /// Length of time before after sending a request to wait before timing out + static const long timeout_read_http_response = 5000; /// Length of time to wait for a pong after a ping static const long timeout_pong = 5000; diff --git a/websocketpp/config/core_client.hpp b/websocketpp/config/core_client.hpp index 1e3611af1..011c5b81c 100644 --- a/websocketpp/config/core_client.hpp +++ b/websocketpp/config/core_client.hpp @@ -160,8 +160,8 @@ struct core_client { static const long timeout_open_handshake = 5000; /// Length of time before a closing handshake is aborted static const long timeout_close_handshake = 5000; - /// Length of time before a HTTP body read is aborted - static const long timeout_http_read_body = 5000; + /// Length of time before after sending a request to wait before timing out + static const long timeout_read_http_response = 5000; /// Length of time to wait for a pong after a ping static const long timeout_pong = 5000; diff --git a/websocketpp/config/debug.hpp b/websocketpp/config/debug.hpp index 54c67da4e..d25328b1c 100644 --- a/websocketpp/config/debug.hpp +++ b/websocketpp/config/debug.hpp @@ -152,8 +152,8 @@ struct debug_core { static const long timeout_open_handshake = 5000; /// Length of time before a closing handshake is aborted static const long timeout_close_handshake = 5000; - /// Length of time before a HTTP body read is aborted - static const long timeout_http_read_body = 5000; + /// Length of time before after sending a request to wait before timing out + static const long timeout_read_http_response = 5000; /// Length of time to wait for a pong after a ping static const long timeout_pong = 5000; diff --git a/websocketpp/config/minimal_server.hpp b/websocketpp/config/minimal_server.hpp index 65f08e7d2..a6f872448 100644 --- a/websocketpp/config/minimal_server.hpp +++ b/websocketpp/config/minimal_server.hpp @@ -178,8 +178,8 @@ struct minimal_server { static const long timeout_open_handshake = 5000; /// Length of time before a closing handshake is aborted static const long timeout_close_handshake = 5000; - /// Length of time before a HTTP body read is aborted - static const long timeout_http_read_body = 5000; + /// Length of time before after sending a request to wait before timing out + static const long timeout_read_http_response = 5000; /// Length of time to wait for a pong after a ping static const long timeout_pong = 5000; diff --git a/websocketpp/connection.hpp b/websocketpp/connection.hpp index 3f0b8bd21..80c451cd4 100644 --- a/websocketpp/connection.hpp +++ b/websocketpp/connection.hpp @@ -323,7 +323,7 @@ class connection , m_max_redirects(0) , m_open_handshake_timeout_dur(config::timeout_open_handshake) , m_close_handshake_timeout_dur(config::timeout_close_handshake) - , m_http_read_timeout_dur(config::timeout_http_read_body) + , m_http_response_timeout_dur(config::timeout_read_http_response) , m_pong_timeout_dur(config::timeout_pong) , m_max_message_size(config::max_message_size) , m_state(session::state::connecting) @@ -539,18 +539,19 @@ class connection m_close_handshake_timeout_dur = dur; } - /// Set http body read timeout + /// Set http response timeout /** - * Sets the length of time the library will wait after the HTTP body begins - * to be read before cancelling it. This can be used to guard from http + * Sets the length of time the library will wait for the response to be read + * before cancelling the request. This can be used to guard from http * responses which are much larger than you anticipate (eg. files) or when * the responder takes too long to write the body of the response. + * Timer starts when the request has been fully sent. * * Connections that time out will have their http handlers called with the - * http_body_read_timeout error code stored in the connection (use get_ec). + * http_read_response_timeout error code stored in the connection (use get_ec). * * The default value is specified via the compile time config value - * 'timeout_close_handshake'. The default value in the core config + * 'timeout_http_read_response'. The default value in the core config * is 5000ms. A value of 0 will disable the timer entirely. * * To be effective, the transport you are using must support timers. See @@ -559,10 +560,10 @@ class connection * * @since 0.8.4 * - * @param dur The length of the http body read timeout in ms + * @param dur The length of the http response timeout in ms */ - void set_http_body_read_timeout(long dur) { - m_http_read_timeout_dur = dur; + void set_http_response_timeout(long dur) { + m_http_response_timeout_dur = dur; } /// Set pong timeout @@ -1563,8 +1564,7 @@ class connection void handle_open_handshake_timeout(lib::error_code const & ec); void handle_close_handshake_timeout(lib::error_code const & ec); - - void handle_read_body_timeout(lib::error_code const & ec); + void handle_read_response_timeout(lib::error_code const & ec); void handle_read_frame(lib::error_code const & ec, size_t bytes_transferred); void read_frame(); @@ -1789,7 +1789,7 @@ class connection /// constant values long m_open_handshake_timeout_dur; long m_close_handshake_timeout_dur; - long m_http_read_timeout_dur; + long m_http_response_timeout_dur; long m_pong_timeout_dur; size_t m_max_message_size; diff --git a/websocketpp/endpoint.hpp b/websocketpp/endpoint.hpp index ba2eb285e..1f1195d17 100644 --- a/websocketpp/endpoint.hpp +++ b/websocketpp/endpoint.hpp @@ -97,7 +97,7 @@ class endpoint : public config::transport_type, public config::endpoint_base { , m_user_agent(::websocketpp::user_agent) , m_open_handshake_timeout_dur(config::timeout_open_handshake) , m_close_handshake_timeout_dur(config::timeout_close_handshake) - , m_http_read_timeout_dur(config::timeout_http_read_body) + , m_http_read_timeout_dur(config::timeout_read_http_response) , m_pong_timeout_dur(config::timeout_pong) , m_max_message_size(config::max_message_size) , m_max_http_body_size(config::max_http_body_size) @@ -393,7 +393,7 @@ class endpoint : public config::transport_type, public config::endpoint_base { * the responder takes too long to write the body of the response. * * Connections that time out will have their http handlers called with the - * http_body_read_timeout error code stored in the connection (use get_ec). + * http_read_response_timeout error code stored in the connection (use get_ec). * * The default value is specified via the compile time config value * 'timeout_close_handshake'. The default value in the core config @@ -407,7 +407,7 @@ class endpoint : public config::transport_type, public config::endpoint_base { * * @param dur The length of the http body read timeout in ms */ - void set_http_body_read_timeout(long dur) { + void set_http_response_timeout(long dur) { scoped_lock_type guard(m_mutex); m_http_read_timeout_dur = dur; } diff --git a/websocketpp/error.hpp b/websocketpp/error.hpp index 6121783a1..b94a11a74 100644 --- a/websocketpp/error.hpp +++ b/websocketpp/error.hpp @@ -148,8 +148,8 @@ enum value { /// General transport error, consult more specific transport error code transport_error, - /// HTTP body read timed out - http_body_read_timeout + /// Reading HTTP response timed out + http_read_response_timeout }; // enum value @@ -229,8 +229,8 @@ class category : public lib::error_category { return "Extension negotiation failed"; case error::transport_error: return "An error occurred in the underlying transport. Consult transport error code for more details."; - case error::http_body_read_timeout: - return "Reading the HTTP response body timed out"; + case error::http_read_response_timeout: + return "Reading the HTTP response timed out"; default: return "Unknown"; } diff --git a/websocketpp/impl/connection_impl.hpp b/websocketpp/impl/connection_impl.hpp index 961e88702..79c26c2dd 100644 --- a/websocketpp/impl/connection_impl.hpp +++ b/websocketpp/impl/connection_impl.hpp @@ -1709,6 +1709,27 @@ void connection::handle_send_http_request(lib::error_code const & ec) { return; } + if (m_is_http) + { + if (m_handshake_timer) { + m_handshake_timer->cancel(); + m_handshake_timer.reset(); + } + + // Start a timer so we don't wait forever for the http body + // it's okay to use the handskahe timer as it's not being used by anything else + if (m_http_response_timeout_dur > 0) { + m_handshake_timer = transport_con_type::set_timer( + m_http_response_timeout_dur, + lib::bind( + &type::handle_read_response_timeout, + type::get_shared(), + lib::placeholders::_1 + ) + ); + } + } + transport_con_type::async_read_at_least( 1, m_buf, @@ -1786,11 +1807,13 @@ void connection::handle_read_http_response(lib::error_code const & ec, m_alog->write(log::alevel::devel,std::string("Raw response: ")+m_response.raw()); if (m_response.has_received(response_type::state::HEADERS)) { - if (m_handshake_timer) { - m_handshake_timer->cancel(); - m_handshake_timer.reset(); - } - + if (!m_is_http) + { + if (m_handshake_timer) { + m_handshake_timer->cancel(); + m_handshake_timer.reset(); + } + } // follow redirect if possible if (m_max_redirects && http::status_code::is_redirect(m_response.get_status_code())) { m_max_redirects--; @@ -1832,20 +1855,6 @@ void connection::handle_read_http_response(lib::error_code const & ec, return; } - // Start a timer if we have not yet - // so we don't wait forever for the http body - // it's okay to use the handskahe timer as it's not being used by anything else - if (m_http_read_timeout_dur > 0 && !m_handshake_timer) { - m_handshake_timer = transport_con_type::set_timer( - m_http_read_timeout_dur, - lib::bind( - &type::handle_close_handshake_timeout, - type::get_shared(), - lib::placeholders::_1 - ) - ); - } - transport_con_type::async_read_at_least( 1, m_buf, @@ -1964,17 +1973,17 @@ void connection::handle_close_handshake_timeout( } template -void connection::handle_read_body_timeout(lib::error_code const & ec) +void connection::handle_read_response_timeout(lib::error_code const & ec) { if (ec == transport::error::operation_aborted) { - m_alog->write(log::alevel::devel,"asio http read body timer cancelled"); + m_alog->write(log::alevel::devel,"asio read http response timer cancelled"); } else if (ec) { m_alog->write(log::alevel::devel, - "asio handle_read_body_timeout error: "+ec.message()); + "asio handle_read_response_timeout error: "+ec.message()); // TODO: ignore or fail here? } else { - m_alog->write(log::alevel::devel, "asio http read body timer expired"); - terminate(make_error_code(error::http_body_read_timeout)); + m_alog->write(log::alevel::devel, "asio read http response timer expired"); + terminate(make_error_code(error::http_read_response_timeout)); } } diff --git a/websocketpp/impl/endpoint_impl.hpp b/websocketpp/impl/endpoint_impl.hpp index 2089dfea4..ba84c3a70 100644 --- a/websocketpp/impl/endpoint_impl.hpp +++ b/websocketpp/impl/endpoint_impl.hpp @@ -75,8 +75,8 @@ endpoint::create_connection(lib::error_code & ec) { if (m_close_handshake_timeout_dur != config::timeout_close_handshake) { con->set_close_handshake_timeout(m_close_handshake_timeout_dur); } - if (m_http_read_timeout_dur != config::timeout_http_read_body) { - con->set_http_body_read_timeout(m_http_read_timeout_dur); + if (m_http_read_timeout_dur != config::timeout_read_http_response) { + con->set_http_response_timeout(m_http_read_timeout_dur); } if (m_pong_timeout_dur != config::timeout_pong) { con->set_pong_timeout(m_pong_timeout_dur); From 017b1a1c3daf47e7522a60cc1e32d2d0909f166a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20To=C5=A1kan?= Date: Fri, 8 Sep 2023 13:25:36 +0200 Subject: [PATCH 26/40] Added option to handle download progress --- websocketpp/connection.hpp | 23 +++++++++++++++++++++ websocketpp/http/impl/parser.hpp | 18 ++++++++++------ websocketpp/http/parser.hpp | 31 ++++++++++++++++++++++++++++ websocketpp/impl/connection_impl.hpp | 7 ++++++- websocketpp/processors/hybi00.hpp | 18 +++++++++++++--- websocketpp/version.hpp | 2 +- 6 files changed, 88 insertions(+), 11 deletions(-) diff --git a/websocketpp/connection.hpp b/websocketpp/connection.hpp index 80c451cd4..562ac1f7c 100644 --- a/websocketpp/connection.hpp +++ b/websocketpp/connection.hpp @@ -126,6 +126,13 @@ typedef lib::function pong_handler; */ typedef lib::function pong_timeout_handler; +/// The type and function signature of a progress handler +/** + * The progress handler is called when bytes of the HTTP message body are received (for plain HTTP requests) + * The float parameter is the progress relative to the full body size (0 to 1 range) + */ +typedef lib::function progress_handler; + /// The type and function signature of a validate handler /** * The validate handler is called after a WebSocket handshake has been received @@ -487,6 +494,16 @@ class connection m_message_handler = h; } + /// Set progress handler + /** + * The progress handler is called when bytes making up a HTTP body are processed + * + * @param h The new progress_handler + */ + void set_progress_handler(progress_handler h) { + m_progress_handler = h; + } + ////////////////////////////////////////// // Connection timeouts and other limits // ////////////////////////////////////////// @@ -673,6 +690,7 @@ class connection */ void set_max_http_body_size(size_t new_value) { m_request.set_max_body_size(new_value); + m_response.set_max_body_size(new_value); } ////////////////////////////////// @@ -1059,6 +1077,10 @@ class connection std::string const & get_response_msg() const { return m_response.get_status_msg(); } + + void consume_response_body() { + m_response.consume_body(); + } /// Set response status code and message (exception free) /** @@ -1785,6 +1807,7 @@ class connection http_handler m_http_handler; validate_handler m_validate_handler; message_handler m_message_handler; + progress_handler m_progress_handler; /// constant values long m_open_handshake_timeout_dur; diff --git a/websocketpp/http/impl/parser.hpp b/websocketpp/http/impl/parser.hpp index 9a01c0849..cd2903156 100644 --- a/websocketpp/http/impl/parser.hpp +++ b/websocketpp/http/impl/parser.hpp @@ -115,7 +115,7 @@ inline lib::error_code parser::set_body(std::string const & value) { return lib::error_code(); } - if (value.size() > m_body_bytes_max) { + if (m_body_bytes_max && value.size() > m_body_bytes_max) { return error::make_error_code(error::body_too_large); } @@ -128,6 +128,11 @@ inline lib::error_code parser::set_body(std::string const & value) { return lib::error_code(); } +inline void parser::consume_body() +{ + m_body.clear(); +} + inline bool parser::parse_parameter_list(std::string const & in, parameter_list & out) const { @@ -150,13 +155,13 @@ inline bool parser::prepare_body(lib::error_code & ec) { // TODO: not 100% sure what the compatibility of this method is. Also, // I believe this will only work up to 32bit sizes. Is there a need for // > 4GiB HTTP payloads? - m_body_bytes_needed = std::strtoul(cl_header.c_str(),&end,10); + m_body_bytes_total = m_body_bytes_needed = std::strtoul(cl_header.c_str(),&end,10); if (end != cl_header.cend().base()) { ec = error::make_error_code(error::invalid_format); return false; } - if (m_body_bytes_needed > m_body_bytes_max) { + if (m_body_bytes_max && m_body_bytes_total > m_body_bytes_max) { ec = error::make_error_code(error::body_too_large); return false; } @@ -180,7 +185,7 @@ inline size_t parser::process_body(char const * buf, size_t len, if (m_body_encoding == body_encoding::plain) { const size_t processed = std::min(m_body_bytes_needed, len); - m_body.append(buf,processed); + m_body.append(buf, processed); m_body_bytes_needed -= processed; ec = lib::error_code(); return processed; @@ -188,7 +193,7 @@ inline size_t parser::process_body(char const * buf, size_t len, // for chunked encoding, read chunks of the body if (m_body_bytes_needed) { // reading previouslly started chunk, same as plain encoding! const size_t processed = std::min(m_body_bytes_needed, len); - m_body.append(buf,processed); + m_body.append(buf, processed); m_body_bytes_needed -= processed; ec = lib::error_code(); return processed; @@ -204,12 +209,13 @@ inline size_t parser::process_body(char const * buf, size_t len, const std::string chunkSizeHex(buf, newline); char * end; m_body_bytes_needed = std::strtoul(chunkSizeHex.c_str(),&end,16); + m_body_bytes_total += m_body_bytes_needed; if (end != chunkSizeHex.cend().base()) { ec = error::make_error_code(error::invalid_format); return 0; } - if (m_body_bytes_needed + m_body.size() > m_body_bytes_max) { + if (m_body_bytes_max && m_body_bytes_total > m_body_bytes_max) { ec = error::make_error_code(error::body_too_large); return 0; } diff --git a/websocketpp/http/parser.hpp b/websocketpp/http/parser.hpp index b44ed5b22..45f581524 100644 --- a/websocketpp/http/parser.hpp +++ b/websocketpp/http/parser.hpp @@ -400,6 +400,7 @@ class parser { parser() : m_header_bytes(0) , m_body_bytes_needed(0) + , m_body_bytes_total(0) , m_body_bytes_max(max_body_size) , m_body_encoding(body_encoding::unknown) {} @@ -518,6 +519,27 @@ class parser { return m_body; } + /// Get the progress of the HTTP body download relative to the expected body size. + /** + * Effectively calculated as (downloaded bytes) / get_total_body_size() + * + * @return The progress (0 to 1 range) + */ + float get_progress() const { + return m_body_bytes_total ? (double(m_body_bytes_total - m_body_bytes_needed) / m_body_bytes_total) : 1.f; + } + + /// Get the expected HTTP body size + /** + * For plain encoding, this is the Content-Length + * For chunked encoding, this is the total number of bytes transfered (changes as new chunks are processed). + * + * @return The total size of the HTTP body. + */ + size_t get_total_body_size() const { + return m_body_bytes_total; + } + /// Set body content /** * Set the body content of the HTTP response to the parameter string. Note @@ -532,6 +554,14 @@ class parser { */ lib::error_code set_body(std::string const & value); + /// Consume body content (keep string at current capacity ) + /** + * Keeps the body string at current capacity, but resets its size + * + * @since 0.8.5 + */ + void consume_body(); + /// Get body size limit /** * Retrieves the maximum number of bytes to parse & buffer before canceling @@ -637,6 +667,7 @@ class parser { std::string m_body; size_t m_body_bytes_needed; + size_t m_body_bytes_total; size_t m_body_bytes_max; body_encoding::value m_body_encoding; }; diff --git a/websocketpp/impl/connection_impl.hpp b/websocketpp/impl/connection_impl.hpp index 79c26c2dd..ca8d72433 100644 --- a/websocketpp/impl/connection_impl.hpp +++ b/websocketpp/impl/connection_impl.hpp @@ -657,7 +657,9 @@ void connection::set_request(request_type const & req, return; } + const size_t max_body_size = req.get_max_body_size(); m_request = req; + m_request.set_max_body_size(max_body_size); } template @@ -1804,7 +1806,7 @@ void connection::handle_read_http_response(lib::error_code const & ec, return; } - m_alog->write(log::alevel::devel,std::string("Raw response: ")+m_response.raw()); + // m_alog->write(log::alevel::devel,std::string("Raw response: ")+m_response.raw()); if (m_response.has_received(response_type::state::HEADERS)) { if (!m_is_http) @@ -1842,6 +1844,9 @@ void connection::handle_read_http_response(lib::error_code const & ec, } if (m_is_http) { + if (m_progress_handler) + m_progress_handler(m_connection_hdl, m_response.get_progress()); + if (m_response.has_received(response_type::state::BODY)) { this->terminate({}); diff --git a/websocketpp/processors/hybi00.hpp b/websocketpp/processors/hybi00.hpp index 0db08e80d..0cd51fc09 100644 --- a/websocketpp/processors/hybi00.hpp +++ b/websocketpp/processors/hybi00.hpp @@ -177,9 +177,21 @@ class hybi00 : public processor { } std::string get_raw(response_type const & res) const { - response_type temp = res; - temp.remove_header("Sec-WebSocket-Key3"); - return temp.raw() + res.get_header("Sec-WebSocket-Key3"); + // TODO: validation. Make sure all required fields have been set? + std::stringstream ret; + + ret << get_version() << " " << res.get_status_code() << " " << res.get_status_msg() << "\r\n"; + for (const auto& header : res.get_headers()) { + if (header.first != "Sec-WebSocket-Key3") + ret << header.first << ": " << header.second << "\r\n"; + } + ret << "\r\n"; + + ret << res.get_body(); + + ret << res.get_header("Sec-WebSocket-Key3"); + + return ret.str(); } std::string const & get_origin(request_type const & r) const { diff --git a/websocketpp/version.hpp b/websocketpp/version.hpp index 0fd6b6fd2..81cc0d179 100644 --- a/websocketpp/version.hpp +++ b/websocketpp/version.hpp @@ -44,7 +44,7 @@ static int const major_version = 0; /// Library minor version number static int const minor_version = 8; /// Library patch version number -static int const patch_version = 4; +static int const patch_version = 5; /// Library pre-release flag /** * This is a textual flag indicating the type and number for pre-release From efdccb096cc5e130f34d0df902602f63f33ef888 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20To=C5=A1kan?= Date: Fri, 20 Oct 2023 18:41:54 +0200 Subject: [PATCH 27/40] Fix compile warning --- websocketpp/uri.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/websocketpp/uri.hpp b/websocketpp/uri.hpp index 4845528e1..0afb2299d 100644 --- a/websocketpp/uri.hpp +++ b/websocketpp/uri.hpp @@ -404,9 +404,9 @@ class uri { relative }; - uri() : m_type(invalid) {} - uri(uri const & copy) = default; - explicit uri(std::string const & uri_string) : m_type(invalid), m_ipv6_literal(false) { + uri() = default; + + explicit uri(std::string const & uri_string) : m_ipv6_literal(false) { if (uri_string.empty()) return; @@ -779,7 +779,7 @@ class uri { return static_cast(t_port); } - type m_type; + type m_type = invalid; std::string m_host; std::string m_resource; uint16_t m_port; From 9676b09d556cec3063eaa65bd07ca94168fbbe29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20To=C5=A1kan?= Date: Tue, 12 Mar 2024 13:30:30 +0100 Subject: [PATCH 28/40] Fixed const version of member method --- websocketpp/transport/asio/security/none.hpp | 4 ++++ websocketpp/transport/asio/security/tls.hpp | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/websocketpp/transport/asio/security/none.hpp b/websocketpp/transport/asio/security/none.hpp index 51f4224a4..c314f0333 100644 --- a/websocketpp/transport/asio/security/none.hpp +++ b/websocketpp/transport/asio/security/none.hpp @@ -125,6 +125,10 @@ class connection : public lib::enable_shared_from_this { return *m_socket; } + const lib::asio::ip::tcp::socket & get_raw_socket() const { + return *m_socket; + } + /// Get the remote endpoint address /** * The iostream transport has no information about the ultimate remote diff --git a/websocketpp/transport/asio/security/tls.hpp b/websocketpp/transport/asio/security/tls.hpp index cd4fa9914..cd37431cb 100644 --- a/websocketpp/transport/asio/security/tls.hpp +++ b/websocketpp/transport/asio/security/tls.hpp @@ -104,6 +104,10 @@ class connection : public lib::enable_shared_from_this { return m_socket->lowest_layer(); } + const socket_type::lowest_layer_type & get_raw_socket() const { + return m_socket->lowest_layer(); + } + /// Retrieve a pointer to the layer below the ssl stream /** * This is used internally. From fa804a4b59b95dfecbd14b23397c2165002452d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20To=C5=A1kan?= Date: Mon, 15 Apr 2024 11:09:23 +0200 Subject: [PATCH 29/40] Added HTTP content encoding support --- CMakeLists.txt | 58 +++- websocketpp/connection.hpp | 29 +- websocketpp/http/constants.hpp | 74 ++++- websocketpp/http/encoding.hpp | 480 +++++++++++++++++++++++++++ websocketpp/http/impl/parser.hpp | 92 +++-- websocketpp/http/impl/request.hpp | 49 ++- websocketpp/http/impl/response.hpp | 16 +- websocketpp/http/parser.hpp | 71 ++-- websocketpp/http/request.hpp | 19 +- websocketpp/http/response.hpp | 2 + websocketpp/impl/connection_impl.hpp | 80 ++++- websocketpp/version.hpp | 4 +- 12 files changed, 878 insertions(+), 96 deletions(-) create mode 100644 websocketpp/http/encoding.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 9043db327..d46f17386 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,8 +9,50 @@ cmake_minimum_required (VERSION 2.8.8) set (WEBSOCKETPP_ROOT ${CMAKE_CURRENT_SOURCE_DIR}) set (WEBSOCKETPP_INCLUDE ${WEBSOCKETPP_ROOT}/websocketpp) set (WEBSOCKETPP_BUILD_ROOT ${CMAKE_CURRENT_BINARY_DIR}) -set (WEBSOCKETPP_BIN ${WEBSOCKETPP_BUILD_ROOT}/bin) -set (WEBSOCKETPP_LIB ${WEBSOCKETPP_BUILD_ROOT}/lib) +set (WEBSOCKETPP_ENCODING_LIBS "") +set (WEBSOCKETPP_ENCODING_DEFS "") + +# Include our cmake macros +set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) +include (CMakeHelpers) + +############ Encoding/decoding support + +set(WEBSOCKETPP_WITH_GZIP FALSE CACHE BOOL "If true, build support for LZ77 compression. No requirements - added to code via https://github.com/ivan-tkatchev/yalz77.") +set(WEBSOCKETPP_WITH_DEFLATE FALSE CACHE BOOL "If true, build support for deflate compression. Requires zlib.") +if (WEBSOCKETPP_WITH_GZIP OR WEBSOCKETPP_WITH_DEFLATE) + find_package(ZLIB REQUIRED) + if (ZLIB_FOUND) + list(APPEND WEBSOCKETPP_ENCODING_LIBS ${ZLIB_LIBRARIES}) + list(APPEND WEBSOCKETPP_INCLUDE ${ZLIB_INCLUDE_DIRS}) + if (WEBSOCKETPP_WITH_DEFLATE) + list(APPEND WEBSOCKETPP_ENCODING_DEFS WEBSOCKETPP_WITH_DEFLATE=1) + endif () + if (WEBSOCKETPP_WITH_GZIP) + list(APPEND WEBSOCKETPP_ENCODING_DEFS WEBSOCKETPP_WITH_GZIP=1) + endif () + endif () +endif () + +set(WEBSOCKETPP_WITH_BROTLI FALSE CACHE BOOL "If true, build support for brotli compression. Requires brotli.") +if (WEBSOCKETPP_WITH_BROTLI) + find_package(Brotli REQUIRED) + if (BROTLI_FOUND) + list(APPEND WEBSOCKETPP_ENCODING_LIBS ${BROTLI_LIBRARIES}) + list(APPEND WEBSOCKETPP_INCLUDE ${BROTLI_INCLUDE_DIRS}) + list(APPEND WEBSOCKETPP_ENCODING_DEFS WEBSOCKETPP_WITH_BROTLI=1) + endif () +endif () + +set(WEBSOCKETPP_WITH_ZSTD FALSE CACHE BOOL "If true, build support for zstd compression. Requires zstd.") +if (WEBSOCKETPP_WITH_ZSTD) + find_package(zstd REQUIRED) + if (ZSTD_FOUND) + list(APPEND WEBSOCKETPP_ENCODING_LIBS ${ZSTD_LIBRARIES}) + list(APPEND WEBSOCKETPP_INCLUDE ${ZSTD_INCLUDE_DIRS}) + list(APPEND WEBSOCKETPP_ENCODING_DEFS WEBSOCKETPP_WITH_ZSTD=1) + endif () +endif () # CMake install step prefix. I assume linux users want the prefix to # be the default /usr or /usr/local so this is only adjusted on Windows. @@ -23,8 +65,8 @@ endif () ############ Project name and version set (WEBSOCKETPP_MAJOR_VERSION 0) -set (WEBSOCKETPP_MINOR_VERSION 8) -set (WEBSOCKETPP_PATCH_VERSION 3) +set (WEBSOCKETPP_MINOR_VERSION 9) +set (WEBSOCKETPP_PATCH_VERSION 0) set (WEBSOCKETPP_VERSION ${WEBSOCKETPP_MAJOR_VERSION}.${WEBSOCKETPP_MINOR_VERSION}.${WEBSOCKETPP_PATCH_VERSION}) if(POLICY CMP0048) @@ -66,10 +108,6 @@ endif () # Disable unnecessary build types set (CMAKE_CONFIGURATION_TYPES "Release;RelWithDebInfo;Debug" CACHE STRING "Configurations" FORCE) -# Include our cmake macros -set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) -include (CMakeHelpers) - ############ Build customization @@ -288,3 +326,7 @@ install (FILES "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/websocketpp-configVersion.cmake" DESTINATION "${INSTALL_CMAKE_DIR}" COMPONENT dev) +add_library(websocketpp INTERFACE) +target_link_libraries(websocketpp INTERFACE ${WEBSOCKETPP_ENCODING_LIBS}) +target_compile_definitions(websocketpp INTERFACE ${WEBSOCKETPP_ENCODING_DEFS}) + diff --git a/websocketpp/connection.hpp b/websocketpp/connection.hpp index 562ac1f7c..7f298e675 100644 --- a/websocketpp/connection.hpp +++ b/websocketpp/connection.hpp @@ -41,6 +41,7 @@ #include #include +#include #include #include #include @@ -248,6 +249,20 @@ namespace http_state { } // namespace session +namespace http{ +struct body_options{ + // The encoding of the body that was given to connection::set_body() + std::optional input_encoding = {}; + + // The desired transfer encodings to consider when writing the body + std::vector transfer_encoding = {}; + + // The encodings to consider for body compression, if client indicated they support them via Accept-Encoding. + // Specify in order of preference (first is best). + std::vector output_encoding = {}; +}; +} + /// Represents an individual WebSocket connection template class connection @@ -1175,7 +1190,7 @@ class connection * @see websocketpp::http::response::set_body * @see set_body(std::string const &) (exception version) */ - void set_body(std::string const & value, lib::error_code & ec); + void set_body(std::string const & value, const http::body_options& opts, lib::error_code & ec); #ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ /// Set response body content (exception) @@ -1194,18 +1209,18 @@ class connection * @see set_body(std::string const &, lib::error_code &) * (exception free version) */ - void set_body(std::string const & value); + void set_body(std::string const & value, const http::body_options& opts); #endif // _WEBSOCKETPP_NO_EXCEPTIONS_ #ifdef _WEBSOCKETPP_MOVE_SEMANTICS_ #ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ /// @copydoc websocketpp::connection::set_body(std::string const &) - void set_body(std::string && value); + void set_body(std::string && value, const http::body_options& opts); #endif /// @copydoc websocketpp::connection::set_body(std::string const &, lib::error_code &) - void set_body(std::string && value, lib::error_code & ec); + void set_body(std::string && value, const http::body_options& opts, lib::error_code & ec); #endif // _WEBSOCKETPP_MOVE_SEMANTICS_ @@ -1361,7 +1376,7 @@ class connection * * @param req the request object to use */ - void set_request(request_type const & req, lib::error_code& ec); + void set_request(request_type req, lib::error_code& ec); /// Get response object /** @@ -1662,7 +1677,9 @@ class connection lib::error_code finalize_handshake_response(bool accept); private: - + + /// Server-side check for valid content-encoding when replying to a request + void check_body_encoding(std::string & body, const http::body_options& opts, lib::error_code & ec); /// Completes m_response, serializes it, and sends it out on the wire. void write_http_response(lib::error_code const & ec); diff --git a/websocketpp/http/constants.hpp b/websocketpp/http/constants.hpp index 218f14fc2..068e99c07 100644 --- a/websocketpp/http/constants.hpp +++ b/websocketpp/http/constants.hpp @@ -33,6 +33,7 @@ #include #include #include +#include #include @@ -352,8 +353,14 @@ enum value { /// The transfer encoding is not supported unsupported_transfer_encoding, - /// The transfer encoding is unknown - unknown_transfer_encoding, + /// The content encoding is not supported + unsupported_content_encoding, + + /// The transfer encoding is invalid + unknown_transfer_encoding, + + /// The content encoding is invalid + unknown_content_encoding, /// A header line was missing a separator missing_header_separator, @@ -388,8 +395,12 @@ inline status_code::value get_status_code(error::value value) { return status_code::request_entity_too_large; case error::unsupported_transfer_encoding: return status_code::internal_server_error; - case error::unknown_transfer_encoding: - return status_code::bad_request; + case error::unsupported_content_encoding: + return status_code::internal_server_error; + case error::unknown_transfer_encoding: + return status_code::bad_request; + case error::unknown_content_encoding: + return status_code::bad_request; case error::missing_header_separator: return status_code::bad_request; case error::request_header_fields_too_large: @@ -458,6 +469,61 @@ inline lib::error_code make_error_code(error::value e) { } } // namespace error + +namespace content_encoding { +enum value : uint8_t { + compress = 1, + deflate, + gzip, + brotli, + zstd +}; + +inline std::string to_string(value encoding) { + switch (encoding) + { + case compress: return "compress"; + case deflate: return "deflate"; + case gzip: return "gzip"; + case brotli: return "br"; + case zstd: return "zstd"; + default: return {}; + } +} +inline std::optional from_string(const std::string& str) { + if (str == "compress") return compress; + else if (str == "deflate") return deflate; + else if (str == "gzip" || str == "x-gzip") return gzip; + else if (str == "br") return brotli; + else if (str == "zstd") return zstd; + else return {}; +} +} // namespace content_encoding + +namespace transfer_encoding { +enum value : uint8_t { + chunked, + compress, + deflate, + gzip +}; + +inline std::string to_string(value encoding) { + switch (encoding) + { + case chunked: + return "chunked"; + default: + return content_encoding::to_string(static_cast(encoding)); + } +} +} // namespace transfer_encoding + +const std::string Header_ContentLength = "Content-Length"; +const std::string Header_ContentEncoding = "Content-Encoding"; +const std::string Header_TransferEncoding = "Transfer-Encoding"; +const std::string Header_AcceptEncoding = "Accept-Encoding"; + } // namespace http } // namespace websocketpp diff --git a/websocketpp/http/encoding.hpp b/websocketpp/http/encoding.hpp new file mode 100644 index 000000000..ecd9acec8 --- /dev/null +++ b/websocketpp/http/encoding.hpp @@ -0,0 +1,480 @@ +// +// Created by Dan Toškan on 11.4.2024. +// + +#ifndef HTTP_ENCODING_HPP +#define HTTP_ENCODING_HPP + +#include + +#ifndef WEBSOCKETPP_WITH_GZIP +#define WEBSOCKETPP_WITH_GZIP 0 +#elif WEBSOCKETPP_WITH_GZIP +#include +#endif + +#ifndef WEBSOCKETPP_WITH_DEFLATE +#define WEBSOCKETPP_WITH_DEFLATE 0 +#elif WEBSOCKETPP_WITH_DEFLATE +#include +#endif + +#ifndef WEBSOCKETPP_WITH_BROTLI +#define WEBSOCKETPP_WITH_BROTLI 0 +#elif WEBSOCKETPP_WITH_BROTLI +#include +#include +#endif + +#ifndef WEBSOCKETPP_WITH_ZSTD +#define WEBSOCKETPP_WITH_ZSTD 0 +#elif WEBSOCKETPP_WITH_ZSTD +#include "zstd.h" +#endif + +namespace websocketpp +{ +namespace http +{ + +inline constexpr bool is_encoding_supported(content_encoding::value encoding) +{ + switch (encoding) + { + case content_encoding::gzip: return WEBSOCKETPP_WITH_GZIP; break; + case content_encoding::deflate: return WEBSOCKETPP_WITH_DEFLATE; break; + case content_encoding::brotli: return WEBSOCKETPP_WITH_BROTLI; break; + case content_encoding::compress: return false; break; // unsupported because browsers don't support it + case content_encoding::zstd: return WEBSOCKETPP_WITH_ZSTD; break; + default: break; + } + return false; +} + +} // namespace http + +namespace encoding +{ +#if WEBSOCKETPP_WITH_GZIP +namespace gzip +{ +constexpr int MOD_GZIP_ZLIB_WINDOWSIZE = 15; +constexpr int MOD_GZIP_ZLIB_CFACTOR = 9; +inline std::string compress(const std::string& str, lib::error_code& ec, + int compressionlevel = Z_BEST_COMPRESSION) +{ + z_stream zs; // z_stream is zlib's control structure + memset(&zs, 0, sizeof(zs)); + + if (deflateInit2(&zs, + compressionlevel, + Z_DEFLATED, + MOD_GZIP_ZLIB_WINDOWSIZE + 16, + MOD_GZIP_ZLIB_CFACTOR, + Z_DEFAULT_STRATEGY) != Z_OK + ) { + throw(std::runtime_error("deflateInit2 failed while compressing.")); + } + + zs.next_in = (Bytef*)str.data(); + zs.avail_in = str.size(); // set the z_stream's input + + int ret; + char outbuffer[32768]; + std::string outstring; + + // retrieve the compressed bytes blockwise + do { + zs.next_out = reinterpret_cast(outbuffer); + zs.avail_out = sizeof(outbuffer); + + ret = deflate(&zs, Z_FINISH); + + if (outstring.size() < zs.total_out) { + // append the block to the output string + outstring.append(outbuffer, + zs.total_out - outstring.size()); + } + } while (ret == Z_OK); + + deflateEnd(&zs); + + if (ret != Z_STREAM_END) { // an error occurred that was not EOF + std::ostringstream oss; + oss << "Exception during zlib compression: (" << ret << ") " << zs.msg; + throw(std::runtime_error(oss.str())); + } + + return outstring; +} + +inline std::string decompress(const std::string& str, lib::error_code& ec) +{ + z_stream zs; // z_stream is zlib's control structure + memset(&zs, 0, sizeof(zs)); + + if (inflateInit2(&zs, MOD_GZIP_ZLIB_WINDOWSIZE + 16) != Z_OK) + throw(std::runtime_error("inflateInit failed while decompressing.")); + + zs.next_in = (Bytef*)str.data(); + zs.avail_in = str.size(); + + int ret; + char outbuffer[32768]; + std::string outstring; + + // get the decompressed bytes blockwise using repeated calls to inflate + do { + zs.next_out = reinterpret_cast(outbuffer); + zs.avail_out = sizeof(outbuffer); + + ret = inflate(&zs, 0); + + if (outstring.size() < zs.total_out) { + outstring.append(outbuffer, + zs.total_out - outstring.size()); + } + + } while (ret == Z_OK); + + inflateEnd(&zs); + + if (ret != Z_STREAM_END) { // an error occurred that was not EOF + std::ostringstream oss; + oss << "Exception during zlib decompression: (" << ret << ") " + << zs.msg; + throw(std::runtime_error(oss.str())); + } + + return outstring; +} + +} // namespace gzip +#endif +#if WEBSOCKETPP_WITH_DEFLATE +namespace zlib +{ +constexpr int ZLIB_BUFFER_SIZE = 10240; +/** Compress a STL string using zlib with given compression level and return* the binary data. */ +inline std::string compress(const std::string& str, lib::error_code& ec, int compressionlevel = Z_BEST_COMPRESSION) +{ + z_stream zs; // z_stream is zlib's control structure + memset(&zs, 0, sizeof(zs)); + + if (deflateInit(&zs, compressionlevel) != Z_OK) + throw(std::runtime_error("deflateInit failed while compressing.")); + + zs.next_in = (Bytef*)str.data(); + zs.avail_in = str.size(); // set the z_stream's input + + int ret; + char outbuffer[ZLIB_BUFFER_SIZE]; + std::string outstring; + + // retrieve the compressed bytes blockwise + do { + zs.next_out = reinterpret_cast(outbuffer); + zs.avail_out = sizeof(outbuffer); + + ret = deflate(&zs, Z_FINISH); + + if (outstring.size() < zs.total_out) + { + // append the block to the output string + outstring.append(outbuffer, zs.total_out - outstring.size()); + } + } while (ret == Z_OK); + + deflateEnd(&zs); + + if (ret != Z_STREAM_END) + { // an error occurred that was not EOF + std::ostringstream oss; + oss << "Exception during zlib compression: (" << ret << ") " << zs.msg; + throw(std::runtime_error(oss.str())); + } + + return outstring; +} + +/** Decompress an STL string using zlib and return the original data. */ +inline std::string decompress(const std::string& str, lib::error_code& ec) +{ + z_stream zs; // z_stream is zlib's control structure + memset(&zs, 0, sizeof(zs)); + + if (inflateInit(&zs) != Z_OK) + throw(std::runtime_error("inflateInit failed while decompressing.")); + + zs.next_in = (Bytef*)str.data(); + zs.avail_in = str.size(); + + int ret; + char outbuffer[ZLIB_BUFFER_SIZE]; + std::string outstring; + + // get the decompressed bytes blockwise using repeated calls to inflate + do { + zs.next_out = reinterpret_cast(outbuffer); + zs.avail_out = sizeof(outbuffer); + + ret = inflate(&zs, 0); + + if (outstring.size() < zs.total_out) + { + outstring.append(outbuffer, zs.total_out - outstring.size()); + } + + } while (ret == Z_OK); + + inflateEnd(&zs); + + if (ret != Z_STREAM_END) + { // an error occurred that was not EOF + std::ostringstream oss; + oss << "Exception during zlib decompression: (" << ret << ") " << zs.msg; + throw(std::runtime_error(oss.str())); + } + + return outstring; +} +} // namespace zlib +#endif + +#if WEBSOCKETPP_WITH_BROTLI +namespace brotli +{ +constexpr size_t BROTLI_BUFFER_SIZE = 2048; +inline std::string compress(const std::string& data, lib::error_code& ec) +{ + auto instance = BrotliEncoderCreateInstance(nullptr, nullptr, nullptr); + std::array buffer; + std::stringstream result; + + size_t available_in = data.length(), available_out = buffer.size(); + const uint8_t* next_in = reinterpret_cast(data.c_str()); + uint8_t* next_out = buffer.data(); + + do { + BrotliEncoderCompressStream(instance, BROTLI_OPERATION_FINISH, &available_in, &next_in, &available_out, &next_out, nullptr); + result.write(reinterpret_cast(buffer.data()), buffer.size() - available_out); + available_out = buffer.size(); + next_out = buffer.data(); + } while (!(available_in == 0 && BrotliEncoderIsFinished(instance))); + + BrotliEncoderDestroyInstance(instance); + return result.str(); +} + +inline std::string decompress(const std::string& data, lib::error_code& ec) +{ + auto instance = BrotliDecoderCreateInstance(nullptr, nullptr, nullptr); + std::array buffer; + std::stringstream result; + + size_t available_in = data.length(), available_out = buffer.size(); + const uint8_t* next_in = reinterpret_cast(data.c_str()); + uint8_t* next_out = buffer.data(); + BrotliDecoderResult oneshot_result; + + do { + oneshot_result = BrotliDecoderDecompressStream(instance, &available_in, &next_in, &available_out, &next_out, nullptr); + result.write(reinterpret_cast(buffer.data()), buffer.size() - available_out); + available_out = buffer.size(); + next_out = buffer.data(); + } while (!(available_in == 0 && oneshot_result == BROTLI_DECODER_RESULT_SUCCESS)); + + BrotliDecoderDestroyInstance(instance); + return result.str(); +} +} // namespace brotli +#endif + +#if WEBSOCKETPP_WITH_COMPRESS +namespace lzw +{ +const int DICTIONARY_SIZE = 256; +// Compress a string to a list of output symbols. +// The result will be written to the output iterator +// starting at "result"; the final iterator is returned. +inline std::string compress(const std::string& data, lib::error_code& ec) +{ + std::vector compressed; + auto result = std::back_inserter(compressed); + + // Build the dictionary. + int dictSize = DICTIONARY_SIZE; + std::map dictionary; + for (int i = 0; i < DICTIONARY_SIZE; i++) dictionary[std::string(1, i)] = i; + + std::string w; + for (std::string::const_iterator it = data.begin(); it != data.end(); ++it) + { + char c = *it; + std::string wc = w + c; + if (dictionary.count(wc)) + w = wc; + else + { + *result++ = dictionary[w]; + // Add wc to the dictionary. + dictionary[wc] = dictSize++; + w = std::string(1, c); + } + } + + // Output the code for w. + if (!w.empty()) + *result = dictionary[w]; + + return std::string((const char*)compressed.data(), compressed.size() * sizeof(int)); +} + +// Decompress a list of output ks to a string. +// "begin" and "end" must form a valid range of ints +inline std::string decompress(const std::string& data, lib::error_code& ec) +{ + // Build the dictionary. + int dictSize = DICTIONARY_SIZE; + std::map dictionary; + for (int i = 0; i < DICTIONARY_SIZE; i++) dictionary[i] = std::string(1, i); + + const int* begin = (const int*)data.c_str(); + const int* const end = (const int*)(data.c_str() + data.size()); + + std::string w(1, *begin++); + std::string result = w; + std::string entry; + for (; begin != end; begin++) + { + const int& k = *begin; + if (dictionary.count(k)) + entry = dictionary[k]; + else if (k == dictSize) + entry = w + w[0]; + else + throw "Bad compressed k"; + + result += entry; + + // Add w+entry[0] to the dictionary. + dictionary[dictSize++] = w + entry[0]; + + w = entry; + } + return result; +} + +} // namespace lzw +#endif + +#if WEBSOCKETPP_WITH_ZSTD +namespace zstd { +inline std::string compress(const std::string& data, lib::error_code& ec, int compress_level = ZSTD_CLEVEL_DEFAULT) { + + size_t est_compress_size = ZSTD_compressBound(data.size()); + + std::string comp_buffer; + comp_buffer.resize(est_compress_size); + + auto compress_size = + ZSTD_compress((void*)comp_buffer.data(), est_compress_size, data.data(), + data.size(), compress_level); + + comp_buffer.resize(compress_size); + comp_buffer.shrink_to_fit(); + + return comp_buffer; +} + +inline std::string decompress(const std::string& data, lib::error_code& ec) { + + size_t const est_decomp_size = + ZSTD_getDecompressedSize(data.data(), data.size()); + + std::string decomp_buffer; + decomp_buffer.resize(est_decomp_size); + + size_t const decomp_size = ZSTD_decompress( + (void*)decomp_buffer.data(), est_decomp_size, data.data(), data.size()); + + decomp_buffer.resize(decomp_size); + decomp_buffer.shrink_to_fit(); + return decomp_buffer; +} + +} // namespace zstd +#endif + +inline std::string decompress(http::content_encoding::value encoding, bool is_transfer_encoding, const std::string& str, lib::error_code& ec) +{ + switch (encoding) + { +#if WEBSOCKETPP_WITH_DEFLATE + case http::content_encoding::deflate: + return zlib::decompress(str, ec); + break; +#endif +#if WEBSOCKETPP_WITH_BROTLI + case http::content_encoding::brotli: + return brotli::decompress(str, ec); + break; +#endif +#if WEBSOCKETPP_WITH_GZIP + case http::content_encoding::gzip: + return gzip::decompress(str, ec); + break; +#endif +#if WEBSOCKETPP_WITH_ZSTD + case http::content_encoding::zstd: + return zstd::decompress(str, ec); + break; +#endif + default: break; + } + + ec = http::error::make_error_code(is_transfer_encoding ? http::error::unsupported_transfer_encoding : http::error::unsupported_content_encoding); + return {}; +} + +inline std::string compress(http::content_encoding::value encoding, bool is_transfer_encoding, const std::string& str, lib::error_code& ec) +{ + switch (encoding) + { +#if WEBSOCKETPP_WITH_DEFLATE + case http::content_encoding::deflate: + return zlib::compress(str, ec); + break; +#endif +#if WEBSOCKETPP_WITH_BROTLI + case http::content_encoding::brotli: + return brotli::compress(str, ec); + break; +#endif +#if WEBSOCKETPP_WITH_COMPRESS + case http::content_encoding::compress: + return lzw::compress(str, ec); + break; +#endif +#if WEBSOCKETPP_WITH_GZIP + case http::content_encoding::gzip: + return gzip::compress(str, ec); + break; +#endif +#if WEBSOCKETPP_WITH_ZSTD + case http::content_encoding::zstd: + return zstd::compress(str, ec); + break; +#endif + default: break; + } + + ec = http::error::make_error_code(is_transfer_encoding ? http::error::unsupported_transfer_encoding : http::error::unsupported_content_encoding); + return {}; +} + +} // namespace encoding + +} // namespace websocketpp + +#endif // HTTP_CONSTANTS_HPP diff --git a/websocketpp/http/impl/parser.hpp b/websocketpp/http/impl/parser.hpp index cd2903156..df381143d 100644 --- a/websocketpp/http/impl/parser.hpp +++ b/websocketpp/http/impl/parser.hpp @@ -105,10 +105,10 @@ inline lib::error_code parser::remove_header(std::string const & key) return lib::error_code(); } -inline lib::error_code parser::set_body(std::string const & value) { +inline lib::error_code parser::set_body(std::string value) { lib::error_code ec; if (value.size() == 0) { - ec = remove_header("Content-Length"); + ec = remove_header(Header_ContentLength); if (ec) { return ec; } m_body.clear(); @@ -121,10 +121,10 @@ inline lib::error_code parser::set_body(std::string const & value) { std::stringstream len; len << value.size(); - ec = replace_header("Content-Length", len.str()); + ec = replace_header(Header_ContentLength, len.str()); if (ec) { return ec; } - m_body = value; + m_body = std::move(value); return lib::error_code(); } @@ -148,10 +148,58 @@ inline bool parser::parse_parameter_list(std::string const & in, inline bool parser::prepare_body(lib::error_code & ec) { ec.clear(); - if (!get_header("Content-Length").empty()) { - std::string const & cl_header = get_header("Content-Length"); + parameter_list ce_list; + if (!get_header_as_plist(Header_ContentEncoding, ce_list)) { + for (const auto& param : ce_list) { + auto encoding = content_encoding::from_string(param.first); + if (encoding) + { + m_content_encoding.push_back(*encoding); + } + else { + ec = error::make_error_code(error::unknown_content_encoding); + return false; + } + } + + if (m_content_encoding.size() > 3) + { + ec = error::make_error_code(error::unsupported_content_encoding); + return false; + } + } + + parameter_list te_list; + if (!get_header_as_plist(Header_TransferEncoding, te_list) && !te_list.empty()) { + for (const auto& param : te_list) { + if (param.first == "gzip" || param.first == "x-gzip") + m_transfer_encoding.push_back(transfer_encoding::gzip); + else if (param.first == "compress") + m_transfer_encoding.push_back(transfer_encoding::compress); + else if (param.first == "deflate") + m_transfer_encoding.push_back(transfer_encoding::deflate); + else if (param.first == "chunked") + m_transfer_encoding.push_back(transfer_encoding::chunked); + else { + ec = error::make_error_code(error::unknown_transfer_encoding); + return false; + } + } + + if (m_transfer_encoding.size() > 3) + { + ec = error::make_error_code(error::unsupported_transfer_encoding); + return false; + } + + if (std::find(m_transfer_encoding.begin(), m_transfer_encoding.end(), transfer_encoding::chunked) != m_transfer_encoding.end()) + return true; // no Content-Length for chunked encoding! + } + + const std::string cl_header = get_header(Header_ContentLength); + if (!cl_header.empty()) { char * end; - + // TODO: not 100% sure what the compatibility of this method is. Also, // I believe this will only work up to 32bit sizes. Is there a need for // > 4GiB HTTP payloads? @@ -160,17 +208,13 @@ inline bool parser::prepare_body(lib::error_code & ec) { ec = error::make_error_code(error::invalid_format); return false; } - + if (m_body_bytes_max && m_body_bytes_total > m_body_bytes_max) { ec = error::make_error_code(error::body_too_large); return false; } - - m_body_encoding = body_encoding::plain; + return m_body_bytes_needed; - } else if (get_header("Transfer-Encoding") == "chunked") { - m_body_encoding = body_encoding::chunked; - return true; } else { return false; } @@ -179,19 +223,12 @@ inline bool parser::prepare_body(lib::error_code & ec) { inline size_t parser::process_body(char const * buf, size_t len, lib::error_code & ec) { - if (!len) { + if (!len) return 0; - } - if (m_body_encoding == body_encoding::plain) { - const size_t processed = std::min(m_body_bytes_needed, len); - m_body.append(buf, processed); - m_body_bytes_needed -= processed; - ec = lib::error_code(); - return processed; - } else if (m_body_encoding == body_encoding::chunked) { + if (std::find(m_transfer_encoding.begin(), m_transfer_encoding.end(), transfer_encoding::chunked) != m_transfer_encoding.end()) { // for chunked encoding, read chunks of the body - if (m_body_bytes_needed) { // reading previouslly started chunk, same as plain encoding! + if (m_body_bytes_needed) { // reading previously started chunk, same as plain encoding! const size_t processed = std::min(m_body_bytes_needed, len); m_body.append(buf, processed); m_body_bytes_needed -= processed; @@ -214,7 +251,7 @@ inline size_t parser::process_body(char const * buf, size_t len, ec = error::make_error_code(error::invalid_format); return 0; } - + if (m_body_bytes_max && m_body_bytes_total > m_body_bytes_max) { ec = error::make_error_code(error::body_too_large); return 0; @@ -228,8 +265,11 @@ inline size_t parser::process_body(char const * buf, size_t len, return processed + process_body(buf + processed, len - processed, ec); } } else { - ec = error::make_error_code(error::unknown_transfer_encoding); - return 0; + const size_t processed = std::min(m_body_bytes_needed, len); + m_body.append(buf, processed); + m_body_bytes_needed -= processed; + ec = lib::error_code(); + return processed; } } diff --git a/websocketpp/http/impl/request.hpp b/websocketpp/http/impl/request.hpp index e58eae956..e46559f65 100644 --- a/websocketpp/http/impl/request.hpp +++ b/websocketpp/http/impl/request.hpp @@ -33,6 +33,7 @@ #include #include +#include namespace websocketpp { namespace http { @@ -41,13 +42,13 @@ namespace parser { inline size_t request::consume(char const * buf, size_t len, lib::error_code & ec) { size_t bytes_processed = 0; - + if (m_ready) { // the request is already complete. End immediately without reading. ec = lib::error_code(); return 0; } - + if (m_body_bytes_needed > 0) { // The headers are complete, but we are still expecting more body // bytes. Process body bytes. @@ -66,7 +67,7 @@ inline size_t request::consume(char const * buf, size_t len, lib::error_code & e // at this point we have an incomplete request still waiting for headers // copy new candidate bytes into our local buffer. This buffer may have - // leftover bytes from previous calls. Not all of these bytes are + // leftover bytes from previous calls. Not all of these bytes are // necessarily header bytes (they might be body or even data after this // request entirely for a keepalive request) m_buf->append(buf,len); @@ -95,9 +96,9 @@ inline size_t request::consume(char const * buf, size_t len, lib::error_code & e } // We are out of bytes but not over any limits yet. Discard the - // processed bytes and copy the remaining unprecessed bytes to the + // processed bytes and copy the remaining unprecessed bytes to the // beginning of the buffer in prep for another call to consume. - + // If there are no processed bytes in the buffer right now don't // copy the unprocessed ones over themselves. if (begin != m_buf->begin()) { @@ -114,7 +115,7 @@ inline size_t request::consume(char const * buf, size_t len, lib::error_code & e // update count of header bytes read so far m_header_bytes += (end-begin+sizeof(http_crlf)); - + if (m_header_bytes > max_header_size) { // This read exceeded max header size @@ -145,14 +146,28 @@ inline size_t request::consume(char const * buf, size_t len, lib::error_code & e m_buf.reset(); // if this was not an upgrade request and has a content length - // continue capturing content-length bytes and expose them as a + // continue capturing content-length bytes and expose them as a // request body. - + bool need_more = prepare_body(ec); if (ec) { return 0; } + parameter_list acc_enc_list; + if (!get_header_as_plist(Header_AcceptEncoding, acc_enc_list)) { + m_accept_encoding = std::vector(); + for (const auto& param : acc_enc_list) { + auto encoding = content_encoding::from_string(param.first); + if (encoding) { + m_accept_encoding->push_back(*encoding); + } else { + ec = error::make_error_code(error::unknown_content_encoding); + return 0; + } + } + } + if (need_more) { bytes_processed += process_body(buf+bytes_processed,len-bytes_processed,ec); if (ec) { @@ -255,6 +270,24 @@ inline lib::error_code request::process(std::string::iterator begin, std::string return set_version(std::string(cursor_end+1,end)); } +inline void request::set_accepted_encodings(std::vector encodings) +{ + m_accept_encoding = std::move(encodings); + remove_header(Header_AcceptEncoding); + for (auto it = m_accept_encoding->begin(); it != m_accept_encoding->end();) + { + if (!is_encoding_supported(*it)) + { + it = m_accept_encoding->erase(it); + continue; + } + + append_header(Header_AcceptEncoding, content_encoding::to_string(*it)); + + ++it; + } +} + } // namespace parser } // namespace http } // namespace websocketpp diff --git a/websocketpp/http/impl/response.hpp b/websocketpp/http/impl/response.hpp index 8f53de00f..e353493b7 100644 --- a/websocketpp/http/impl/response.hpp +++ b/websocketpp/http/impl/response.hpp @@ -34,11 +34,20 @@ #include #include +#include namespace websocketpp { namespace http { namespace parser { +inline void response::on_parsing_completed(lib::error_code & ec) { + m_state = state::DONE; + + for (auto decode = m_content_encoding.rbegin(); !ec && decode != m_content_encoding.rend(); decode++) { + m_body = encoding::decompress(*decode, false, m_body, ec); + } +} + inline size_t response::consume(char const * buf, size_t len, lib::error_code & ec) { if (m_state == state::DONE) { // the response is already complete. End immediately without reading. @@ -150,7 +159,10 @@ inline size_t response::consume(char const * buf, size_t len, lib::error_code & } if (m_body_bytes_needed == 0) { - m_state = state::DONE; + on_parsing_completed(ec); + if (ec) { + return 0; + } } // frees memory used temporarily during header parsing @@ -322,7 +334,7 @@ inline size_t response::process_body(char const * buf, size_t len, lib::error_co } while (!ec && m_body_bytes_needed && len); if (ec || m_body_bytes_needed == 0) - m_state = state::DONE; + on_parsing_completed(ec); return processed; } diff --git a/websocketpp/http/parser.hpp b/websocketpp/http/parser.hpp index 45f581524..2d4bddbd0 100644 --- a/websocketpp/http/parser.hpp +++ b/websocketpp/http/parser.hpp @@ -49,14 +49,6 @@ namespace state { }; } -namespace body_encoding { - enum value { - unknown, - plain, - chunked - }; -} - typedef std::map header_list; /// Read and return the next token in the stream @@ -402,10 +394,10 @@ class parser { , m_body_bytes_needed(0) , m_body_bytes_total(0) , m_body_bytes_max(max_body_size) - , m_body_encoding(body_encoding::unknown) {} + {} virtual ~parser() {} - + /// Get the HTTP version string /** * @return The version string for this parser @@ -440,7 +432,7 @@ class parser { * * @param [in] key The name/key of the HTTP header to use as input. * @param [out] out The parameter list to store extracted parameters in. - * @return Whether or not the input was a valid parameter list. + * @return Whether or not the input was a valid parameter list. (true means invalid, false means valid) */ bool get_header_as_plist(std::string const & key, parameter_list & out) const; @@ -462,13 +454,13 @@ class parser { * will be appended to the existing value. * * Note: per HTTP specs header values are compared case insensitively. - * + * * @todo Should there be any restrictions on which keys are allowed? * * @see replace_header * * @since 0.9.0 (return value added, exceptions removed) - * + * * @param [in] key The name/key of the header to append to. * @param [in] val The value to append. * @return A status code describing the outcome of the operation. @@ -486,7 +478,7 @@ class parser { * @see append_header * * @since 0.9.0 (return value added) - * + * * @param [in] key The name/key of the header to append to. * @param [in] val The value to append. * @return A status code describing the outcome of the operation. @@ -501,7 +493,7 @@ class parser { * Note: per HTTP specs header values are compared case insensitively. * * @since 0.9.0 (return value added) - * + * * @param [in] key The name/key of the header to remove. * @return A status code describing the outcome of the operation. */ @@ -548,11 +540,11 @@ class parser { * via replace_header("Content-Length") after calling set_body() * * @since 0.9.0 (return value added) - * + * * @param value String data to include as the body content. * @return A status code describing the outcome of the operation. */ - lib::error_code set_body(std::string const & value); + lib::error_code set_body(std::string value); /// Consume body content (keep string at current capacity ) /** @@ -575,6 +567,30 @@ class parser { return m_body_bytes_max; } + /// Get transfer encoding + /** + * The list of transfer encodings of the body, the order in which they were applied. + * + * @since 0.9.0 + * + * @return The list of transfer encodings of the body, the order in which they were applied. + */ + const std::vector& get_transfer_encoding() const { + return m_transfer_encoding; + } + + /// Get content encoding + /** + * The list of content encodings of the body, the order in which they were applied. + * + * @since 0.9.0 + * + * @return The list of content encodings of the body, the order in which they were applied. + */ + const std::vector& get_content_encoding() const { + return m_content_encoding; + } + /// Set body size limit /** * Set the maximum number of bytes to parse and buffer before canceling a @@ -592,7 +608,7 @@ class parser { /** * @param [in] in The input string. * @param [out] out The parameter list to store extracted parameters in. - * @return Whether or not the input was a valid parameter list. + * @return Whether or not the input was a valid parameter list (true means invalid, false means valid) */ bool parse_parameter_list(std::string const & in, parameter_list & out) const; @@ -600,7 +616,7 @@ class parser { /// Process a header line /** * @since 0.9.0 (return value added, exceptions removed) - * + * * @param [in] begin An iterator to the beginning of the sequence. * @param [in] end An iterator to the end of the sequence. * @return A status code describing the outcome of the operation. @@ -662,14 +678,15 @@ class parser { std::string m_version; header_list m_headers; - - size_t m_header_bytes; - - std::string m_body; - size_t m_body_bytes_needed; - size_t m_body_bytes_total; - size_t m_body_bytes_max; - body_encoding::value m_body_encoding; + + size_t m_header_bytes; + + std::string m_body; + size_t m_body_bytes_needed; + size_t m_body_bytes_total; + size_t m_body_bytes_max; + std::vector m_transfer_encoding; + std::vector m_content_encoding; }; } // namespace parser diff --git a/websocketpp/http/request.hpp b/websocketpp/http/request.hpp index ca8c5ac8c..aebd8bebb 100644 --- a/websocketpp/http/request.hpp +++ b/websocketpp/http/request.hpp @@ -124,6 +124,16 @@ class request : public parser { return m_uri; } + const std::optional>& get_accepted_encodings() const { + return m_accept_encoding; + } + + bool accepts_encoding(content_encoding::value encoding) const { + return m_accept_encoding && std::find(m_accept_encoding->begin(), m_accept_encoding->end(), encoding) != m_accept_encoding->end(); + } + + void set_accepted_encodings(std::vector encodings); + private: /// Helper function for message::consume. Process request line /** @@ -135,10 +145,11 @@ class request : public parser { */ lib::error_code process(std::string::iterator begin, std::string::iterator end); - lib::shared_ptr m_buf; - std::string m_method; - std::string m_uri; - bool m_ready; + lib::shared_ptr m_buf; + std::string m_method; + std::string m_uri; + bool m_ready; + std::optional> m_accept_encoding; }; } // namespace parser diff --git a/websocketpp/http/response.hpp b/websocketpp/http/response.hpp index 110bcaff2..c446ba773 100644 --- a/websocketpp/http/response.hpp +++ b/websocketpp/http/response.hpp @@ -202,6 +202,8 @@ class response : public parser { virtual size_t process_body(char const * buf, size_t len, lib::error_code & ec) override; + void on_parsing_completed(lib::error_code & ec); + std::string m_status_msg; lib::shared_ptr m_buf; status_code::value m_status_code; diff --git a/websocketpp/impl/connection_impl.hpp b/websocketpp/impl/connection_impl.hpp index ca8d72433..b4ae0e71b 100644 --- a/websocketpp/impl/connection_impl.hpp +++ b/websocketpp/impl/connection_impl.hpp @@ -38,6 +38,8 @@ #include #include +#include + #include #include #include @@ -601,7 +603,56 @@ void connection::set_status(http::status_code::value code, #endif // _WEBSOCKETPP_NO_EXCEPTIONS_ template -void connection::set_body(std::string const & value, +void connection::check_body_encoding(std::string & body, const http::body_options& opts, lib::error_code & ec) +{ + if (opts.input_encoding) + { + if (m_request.accepts_encoding(*opts.input_encoding)) { + append_header(http::Header_ContentEncoding, http::content_encoding::to_string(*opts.input_encoding), ec); + return; // body already in correct format! + } + else { + body = encoding::decompress(*opts.input_encoding, false, body, ec); + } + } + + for (http::content_encoding::value encode_body : opts.output_encoding) { + if (ec) break; + + if (!http::is_encoding_supported(encode_body)) continue; + + if (!m_request.accepts_encoding(encode_body)) continue; + + body = encoding::compress(encode_body, true, body, ec); + if (!ec) { + append_header(http::Header_ContentEncoding, http::content_encoding::to_string(encode_body), ec); + } + + break; // only encode in a single format + } + + for (http::transfer_encoding::value encode_body : opts.transfer_encoding) { + if (ec) break; + + if (encode_body == http::transfer_encoding::chunked) continue; + + const http::content_encoding::value as_content_encoding = static_cast(encode_body); + + if (!http::is_encoding_supported(as_content_encoding)) continue; + + if (!m_request.accepts_encoding(as_content_encoding)) continue; + + body = encoding::compress(as_content_encoding, true, body, ec); + if (!ec) { + append_header(http::Header_ContentEncoding, http::content_encoding::to_string(as_content_encoding), ec); + } + + // allow multiple encodings + } +} + +template +void connection::set_body(std::string const & value, const http::body_options& opts, lib::error_code & ec) { if (m_internal_state != istate::PROCESS_HTTP_REQUEST) { @@ -609,14 +660,20 @@ void connection::set_body(std::string const & value, return; } - ec = m_response.set_body(value); + std::string body(value); + check_body_encoding(body, opts, ec); + if (ec) { + return; + } + + ec = m_response.set_body(std::move(body)); } #ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ template -void connection::set_body(std::string const & value) { +void connection::set_body(std::string const & value, const http::body_options& opts) { lib::error_code ec; - this->set_body(value, ec); + this->set_body(value, opts, ec); if (ec) { throw exception("Call to set_body from invalid state", ec); } @@ -625,7 +682,7 @@ void connection::set_body(std::string const & value) { #ifdef _WEBSOCKETPP_MOVE_SEMANTICS_ template -void connection::set_body(std::string && value, +void connection::set_body(std::string && value, const http::body_options& opts, lib::error_code & ec) { if (m_internal_state != istate::PROCESS_HTTP_REQUEST) { @@ -633,14 +690,19 @@ void connection::set_body(std::string && value, return; } + check_body_encoding(value, opts, ec); + if (ec) { + return; + } + ec = m_response.set_body(std::move(value)); } #ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ template -void connection::set_body(std::string && value) { +void connection::set_body(std::string && value, const http::body_options& opts) { lib::error_code ec; - this->set_body(std::move(value), ec); + this->set_body(std::move(value), opts, ec); if (ec) { throw exception("Call to set_body from invalid state", ec); } @@ -649,7 +711,7 @@ void connection::set_body(std::string && value) { #endif // _WEBSOCKETPP_MOVE_SEMANTICS_ template -void connection::set_request(request_type const & req, +void connection::set_request(request_type req, lib::error_code& ec) { if (m_internal_state != istate::USER_INIT) { @@ -658,7 +720,7 @@ void connection::set_request(request_type const & req, } const size_t max_body_size = req.get_max_body_size(); - m_request = req; + m_request = std::move(req); m_request.set_max_body_size(max_body_size); } diff --git a/websocketpp/version.hpp b/websocketpp/version.hpp index 81cc0d179..e47a72ff5 100644 --- a/websocketpp/version.hpp +++ b/websocketpp/version.hpp @@ -42,9 +42,9 @@ namespace websocketpp { /// Library major version number static int const major_version = 0; /// Library minor version number -static int const minor_version = 8; +static int const minor_version = 9; /// Library patch version number -static int const patch_version = 5; +static int const patch_version = 0; /// Library pre-release flag /** * This is a textual flag indicating the type and number for pre-release From 2ad7c05f25432adb986f4e412cc9b1ac423f5de2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20To=C5=A1kan?= Date: Mon, 15 Apr 2024 12:19:29 +0200 Subject: [PATCH 30/40] Fixed linker error --- CMakeLists.txt | 1 + websocketpp/impl/connection_impl.hpp | 1 + 2 files changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index d46f17386..bebb4ded5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -329,4 +329,5 @@ install (FILES add_library(websocketpp INTERFACE) target_link_libraries(websocketpp INTERFACE ${WEBSOCKETPP_ENCODING_LIBS}) target_compile_definitions(websocketpp INTERFACE ${WEBSOCKETPP_ENCODING_DEFS}) +target_include_directories(websocketpp INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/websocketpp/impl/connection_impl.hpp b/websocketpp/impl/connection_impl.hpp index b4ae0e71b..036b26a28 100644 --- a/websocketpp/impl/connection_impl.hpp +++ b/websocketpp/impl/connection_impl.hpp @@ -605,6 +605,7 @@ void connection::set_status(http::status_code::value code, template void connection::check_body_encoding(std::string & body, const http::body_options& opts, lib::error_code & ec) { + remove_header(http::Header_ContentEncoding, ec); if (opts.input_encoding) { if (m_request.accepts_encoding(*opts.input_encoding)) { From 06e32e616baa80d712f847dd1533ac65e515a25a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20To=C5=A1kan?= Date: Mon, 15 Apr 2024 12:34:23 +0200 Subject: [PATCH 31/40] Remove usages of compress encoding --- websocketpp/http/encoding.hpp | 83 ----------------------------------- 1 file changed, 83 deletions(-) diff --git a/websocketpp/http/encoding.hpp b/websocketpp/http/encoding.hpp index ecd9acec8..96dbcbeeb 100644 --- a/websocketpp/http/encoding.hpp +++ b/websocketpp/http/encoding.hpp @@ -290,84 +290,6 @@ inline std::string decompress(const std::string& data, lib::error_code& ec) } // namespace brotli #endif -#if WEBSOCKETPP_WITH_COMPRESS -namespace lzw -{ -const int DICTIONARY_SIZE = 256; -// Compress a string to a list of output symbols. -// The result will be written to the output iterator -// starting at "result"; the final iterator is returned. -inline std::string compress(const std::string& data, lib::error_code& ec) -{ - std::vector compressed; - auto result = std::back_inserter(compressed); - - // Build the dictionary. - int dictSize = DICTIONARY_SIZE; - std::map dictionary; - for (int i = 0; i < DICTIONARY_SIZE; i++) dictionary[std::string(1, i)] = i; - - std::string w; - for (std::string::const_iterator it = data.begin(); it != data.end(); ++it) - { - char c = *it; - std::string wc = w + c; - if (dictionary.count(wc)) - w = wc; - else - { - *result++ = dictionary[w]; - // Add wc to the dictionary. - dictionary[wc] = dictSize++; - w = std::string(1, c); - } - } - - // Output the code for w. - if (!w.empty()) - *result = dictionary[w]; - - return std::string((const char*)compressed.data(), compressed.size() * sizeof(int)); -} - -// Decompress a list of output ks to a string. -// "begin" and "end" must form a valid range of ints -inline std::string decompress(const std::string& data, lib::error_code& ec) -{ - // Build the dictionary. - int dictSize = DICTIONARY_SIZE; - std::map dictionary; - for (int i = 0; i < DICTIONARY_SIZE; i++) dictionary[i] = std::string(1, i); - - const int* begin = (const int*)data.c_str(); - const int* const end = (const int*)(data.c_str() + data.size()); - - std::string w(1, *begin++); - std::string result = w; - std::string entry; - for (; begin != end; begin++) - { - const int& k = *begin; - if (dictionary.count(k)) - entry = dictionary[k]; - else if (k == dictSize) - entry = w + w[0]; - else - throw "Bad compressed k"; - - result += entry; - - // Add w+entry[0] to the dictionary. - dictionary[dictSize++] = w + entry[0]; - - w = entry; - } - return result; -} - -} // namespace lzw -#endif - #if WEBSOCKETPP_WITH_ZSTD namespace zstd { inline std::string compress(const std::string& data, lib::error_code& ec, int compress_level = ZSTD_CLEVEL_DEFAULT) { @@ -451,11 +373,6 @@ inline std::string compress(http::content_encoding::value encoding, bool is_tran return brotli::compress(str, ec); break; #endif -#if WEBSOCKETPP_WITH_COMPRESS - case http::content_encoding::compress: - return lzw::compress(str, ec); - break; -#endif #if WEBSOCKETPP_WITH_GZIP case http::content_encoding::gzip: return gzip::compress(str, ec); From 1d91646618611a4756f825beefd49b3b12393802 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20To=C5=A1kan?= Date: Mon, 15 Apr 2024 16:48:24 +0200 Subject: [PATCH 32/40] feat(igplatform): Initial implementation of new deployment system (SW-1545) --- websocketpp/connection.hpp | 4 ++-- websocketpp/http/parser.hpp | 4 ++-- websocketpp/impl/connection_impl.hpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/websocketpp/connection.hpp b/websocketpp/connection.hpp index 7f298e675..51f103948 100644 --- a/websocketpp/connection.hpp +++ b/websocketpp/connection.hpp @@ -130,9 +130,9 @@ typedef lib::function pong_timeout_handler /// The type and function signature of a progress handler /** * The progress handler is called when bytes of the HTTP message body are received (for plain HTTP requests) - * The float parameter is the progress relative to the full body size (0 to 1 range) + * The two size parameters passed on are the completed and total size of the HTTP response, respectively. */ -typedef lib::function progress_handler; +typedef lib::function progress_handler; /// The type and function signature of a validate handler /** diff --git a/websocketpp/http/parser.hpp b/websocketpp/http/parser.hpp index 2d4bddbd0..ffbc23db5 100644 --- a/websocketpp/http/parser.hpp +++ b/websocketpp/http/parser.hpp @@ -517,8 +517,8 @@ class parser { * * @return The progress (0 to 1 range) */ - float get_progress() const { - return m_body_bytes_total ? (double(m_body_bytes_total - m_body_bytes_needed) / m_body_bytes_total) : 1.f; + size_t get_transferred_body_size() const { + return m_body_bytes_total ? (m_body_bytes_total - m_body_bytes_needed) : m_body.size(); } /// Get the expected HTTP body size diff --git a/websocketpp/impl/connection_impl.hpp b/websocketpp/impl/connection_impl.hpp index 036b26a28..db91e0472 100644 --- a/websocketpp/impl/connection_impl.hpp +++ b/websocketpp/impl/connection_impl.hpp @@ -1908,7 +1908,7 @@ void connection::handle_read_http_response(lib::error_code const & ec, if (m_is_http) { if (m_progress_handler) - m_progress_handler(m_connection_hdl, m_response.get_progress()); + m_progress_handler(m_connection_hdl, m_response.get_transferred_body_size(), m_response.get_total_body_size()); if (m_response.has_received(response_type::state::BODY)) { From 9eebf01609e3344717dc13bda4fc75794d254f77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20To=C5=A1kan?= Date: Tue, 16 Apr 2024 14:20:26 +0200 Subject: [PATCH 33/40] Updated CMake to minimum 3 --- CMakeLists.txt | 25 ++----------------------- cmake/CMakeHelpers.cmake | 10 +++++----- websocketpp/CMakeLists.txt | 2 -- 3 files changed, 7 insertions(+), 30 deletions(-) delete mode 100644 websocketpp/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index bebb4ded5..3acb3bcf1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,8 +2,8 @@ ############ Setup project and cmake # Minimum cmake requirement. We should require a quite recent # cmake for the dependency find macros etc. to be up to date. -cmake_minimum_required (VERSION 2.8.8) - +cmake_minimum_required (VERSION 3.0.0) +project(websocketpp VERSION 0.9.0) ############ Paths set (WEBSOCKETPP_ROOT ${CMAKE_CURRENT_SOURCE_DIR}) @@ -63,22 +63,6 @@ if (WIN32) set (CMAKE_INSTALL_PREFIX "${WEBSOCKETPP_ROOT}/install" CACHE PATH "") endif () -############ Project name and version -set (WEBSOCKETPP_MAJOR_VERSION 0) -set (WEBSOCKETPP_MINOR_VERSION 9) -set (WEBSOCKETPP_PATCH_VERSION 0) -set (WEBSOCKETPP_VERSION ${WEBSOCKETPP_MAJOR_VERSION}.${WEBSOCKETPP_MINOR_VERSION}.${WEBSOCKETPP_PATCH_VERSION}) - -if(POLICY CMP0048) - cmake_policy(GET CMP0048 _version_policy) -endif() - -if(_version_allowed STREQUAL NEW) - project (websocketpp VERSION ${WEBSOCKETPP_VERSION}) -else() - project (websocketpp) -endif() - set_property(GLOBAL PROPERTY USE_FOLDERS ON) include(GNUInstallDirs) @@ -292,9 +276,6 @@ endif() ############ Add projects -# Add main library -add_subdirectory (websocketpp) - # Add examples if (BUILD_EXAMPLES) include_subdirs ("examples") @@ -307,8 +288,6 @@ endif () print_used_build_config() -export (PACKAGE websocketpp) - include(CMakePackageConfigHelpers) configure_package_config_file(websocketpp-config.cmake.in "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/websocketpp-config.cmake" diff --git a/cmake/CMakeHelpers.cmake b/cmake/CMakeHelpers.cmake index f6036325b..c75f86346 100644 --- a/cmake/CMakeHelpers.cmake +++ b/cmake/CMakeHelpers.cmake @@ -1,25 +1,25 @@ # Print build configuration macro (print_used_build_config) - message ("\n=========== Used Build Configuration =============\n") + message (STATUS "=========== Websocketpp Build Configuration =============") message (STATUS "ENABLE_CPP11 = " ${ENABLE_CPP11}) message (STATUS "BUILD_EXAMPLES = " ${BUILD_EXAMPLES}) message (STATUS "BUILD_TESTS = " ${BUILD_TESTS}) - message ("") + message (STATUS "") message (STATUS "WEBSOCKETPP_ROOT = " ${WEBSOCKETPP_ROOT}) message (STATUS "WEBSOCKETPP_BIN = " ${WEBSOCKETPP_BIN}) message (STATUS "WEBSOCKETPP_LIB = " ${WEBSOCKETPP_LIB}) message (STATUS "Install prefix = " ${CMAKE_INSTALL_PREFIX}) - message ("") + message (STATUS "") message (STATUS "WEBSOCKETPP_BOOST_LIBS = ${WEBSOCKETPP_BOOST_LIBS}") message (STATUS "WEBSOCKETPP_PLATFORM_LIBS = ${WEBSOCKETPP_PLATFORM_LIBS}") message (STATUS "WEBSOCKETPP_PLATFORM_TLS_LIBS = ${WEBSOCKETPP_PLATFORM_TLS_LIBS}") - message ("") + message (STATUS "") message (STATUS "OPENSSL_FOUND = ${OPENSSL_FOUND}") message (STATUS "OPENSSL_INCLUDE_DIR = ${OPENSSL_INCLUDE_DIR}") message (STATUS "OPENSSL_LIBRARIES = ${OPENSSL_LIBRARIES}") message (STATUS "OPENSSL_VERSION = ${OPENSSL_VERSION}") - message ("") + message (STATUS "===========================================================") endmacro () # Adds the given folder_name into the source files of the current project. diff --git a/websocketpp/CMakeLists.txt b/websocketpp/CMakeLists.txt deleted file mode 100644 index 3ea8cc1c4..000000000 --- a/websocketpp/CMakeLists.txt +++ /dev/null @@ -1,2 +0,0 @@ -init_target("websocketpp") -final_target () From 2a4c4a5cdb59143be951e3f46e376ae0396801e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20To=C5=A1kan?= Date: Thu, 18 Apr 2024 12:45:18 +0200 Subject: [PATCH 34/40] Added missing cmake files to find Brotli & zstd --- cmake/FindBrotli.cmake | 45 ++++++++++++++++++++++++++++++++++++++++++ cmake/Findzstd.cmake | 29 +++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 cmake/FindBrotli.cmake create mode 100644 cmake/Findzstd.cmake diff --git a/cmake/FindBrotli.cmake b/cmake/FindBrotli.cmake new file mode 100644 index 000000000..1617e1afa --- /dev/null +++ b/cmake/FindBrotli.cmake @@ -0,0 +1,45 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### +include(FindPackageHandleStandardArgs) + +find_path(BROTLI_INCLUDE_DIR "brotli/decode.h") + +find_library(BROTLICOMMON_LIBRARY NAMES brotlicommon) +find_library(BROTLIDEC_LIBRARY NAMES brotlidec) +find_library(BROTLIENC_LIBRARY NAMES brotlienc) + +find_package_handle_standard_args(Brotli + FOUND_VAR + BROTLI_FOUND + REQUIRED_VARS + BROTLIDEC_LIBRARY + BROTLIENC_LIBRARY + BROTLICOMMON_LIBRARY + BROTLI_INCLUDE_DIR + FAIL_MESSAGE + "Could NOT find Brotli" +) + +set(BROTLI_INCLUDE_DIRS ${BROTLI_INCLUDE_DIR}) +set(BROTLI_LIBRARIES ${BROTLICOMMON_LIBRARY} ${BROTLIDEC_LIBRARY} ${BROTLIENC_LIBRARY}) \ No newline at end of file diff --git a/cmake/Findzstd.cmake b/cmake/Findzstd.cmake new file mode 100644 index 000000000..7ef48ef1c --- /dev/null +++ b/cmake/Findzstd.cmake @@ -0,0 +1,29 @@ +# - Find zstd +# Find the zstd compression library and includes +# +# ZSTD_INCLUDE_DIRS - where to find zstd.h, etc. +# ZSTD_LIBRARIES - List of libraries when using zstd. +# ZSTD_FOUND - True if zstd found. + +find_path(ZSTD_INCLUDE_DIRS + NAMES zstd.h + HINTS ${zstd_ROOT_DIR}/include) + +find_library(ZSTD_LIBRARIES + NAMES zstd + HINTS ${zstd_ROOT_DIR}/lib) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(zstd DEFAULT_MSG ZSTD_LIBRARIES ZSTD_INCLUDE_DIRS) + +mark_as_advanced( + ZSTD_LIBRARIES + ZSTD_INCLUDE_DIRS) + +if(ZSTD_FOUND AND NOT (TARGET zstd::zstd)) + add_library (zstd::zstd UNKNOWN IMPORTED) + set_target_properties(zstd::zstd + PROPERTIES + IMPORTED_LOCATION ${ZSTD_LIBRARIES} + INTERFACE_INCLUDE_DIRECTORIES ${ZSTD_INCLUDE_DIRS}) +endif() \ No newline at end of file From 2d2a2348b9b4b4ce2a95a2f34b30575c1f6c62e4 Mon Sep 17 00:00:00 2001 From: Uros Droftina Date: Mon, 13 May 2024 13:45:55 +0200 Subject: [PATCH 35/40] feat(jackpot): Support keycloak in jackpot server and client (SW-1007) * added comparison operator for uri --- websocketpp/uri.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/websocketpp/uri.hpp b/websocketpp/uri.hpp index 0afb2299d..b055d9843 100644 --- a/websocketpp/uri.hpp +++ b/websocketpp/uri.hpp @@ -739,6 +739,8 @@ class uri { } } + bool operator==(const uri& rhs) const = default; + // get fragment // hi <3 From 41cc1d6ecf93f4d8743e690e72dce9da6dd0cd17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20To=C5=A1kan?= Date: Thu, 23 May 2024 12:31:02 +0200 Subject: [PATCH 36/40] Renamed io_service to io_context (io_service is a deprecated name in boost) --- SConstruct | 4 +- changelog.md | 14 +- docs/faq.dox | 12 +- .../broadcast_server/broadcast_server.cpp | 2 +- examples/debug_client/debug_client.cpp | 2 +- examples/debug_server/debug_server.cpp | 2 +- examples/echo_client/echo_client.cpp | 2 +- examples/echo_server/echo_server.cpp | 2 +- .../echo_server_both/echo_server_both.cpp | 8 +- examples/echo_server_tls/echo_server_tls.cpp | 2 +- .../CMakeLists.txt | 2 +- .../SConscript | 4 +- .../external_io_context.cpp} | 6 +- .../tcp_echo_server.hpp | 6 +- examples/print_client/print_client.cpp | 2 +- .../print_client_tls/print_client_tls.cpp | 2 +- examples/scratch_server/scratch_server.cpp | 2 +- examples/sip_client/sip_client.cpp | 2 +- .../telemetry_client/telemetry_client.cpp | 4 +- .../telemetry_server/telemetry_server.cpp | 2 +- examples/testee_client/testee_client.cpp | 2 +- examples/testee_server/testee_server.cpp | 2 +- roadmap.md | 2 +- test/endpoint/endpoint.cpp | 2 +- test/transport/asio/timers.cpp | 8 +- test/transport/integration.cpp | 22 +-- tutorials/utility_server/step1.cpp | 2 +- tutorials/utility_server/step2.cpp | 2 +- tutorials/utility_server/utility_server.md | 8 +- websocketpp/roles/server_endpoint.hpp | 4 +- websocketpp/transport/asio/base.hpp | 2 +- websocketpp/transport/asio/connection.hpp | 36 ++--- websocketpp/transport/asio/endpoint.hpp | 140 +++++++++--------- websocketpp/transport/asio/security/none.hpp | 14 +- websocketpp/transport/asio/security/tls.hpp | 18 +-- websocketpp/transport/debug/endpoint.hpp | 2 +- websocketpp/transport/iostream/endpoint.hpp | 2 +- websocketpp/transport/stub/endpoint.hpp | 2 +- 38 files changed, 176 insertions(+), 176 deletions(-) rename examples/{external_io_service => external_io_context}/CMakeLists.txt (87%) rename examples/{external_io_service => external_io_context}/SConscript (72%) rename examples/{external_io_service/external_io_service.cpp => external_io_context/external_io_context.cpp} (96%) rename examples/{external_io_service => external_io_context}/tcp_echo_server.hpp (95%) diff --git a/SConstruct b/SConstruct index c5ae19369..f87e4e8b9 100644 --- a/SConstruct +++ b/SConstruct @@ -273,8 +273,8 @@ subprotocol_server = SConscript('#/examples/subprotocol_server/SConscript',varia # telemetry_server telemetry_server = SConscript('#/examples/telemetry_server/SConscript',variant_dir = builddir + 'telemetry_server',duplicate = 0) -# external_io_service -external_io_service = SConscript('#/examples/external_io_service/SConscript',variant_dir = builddir + 'external_io_service',duplicate = 0) +# external_io_context +external_io_context = SConscript('#/examples/external_io_context/SConscript',variant_dir = builddir + 'external_io_context',duplicate = 0) if not env['PLATFORM'].startswith('win'): # iostream_server diff --git a/changelog.md b/changelog.md index 9e0b0a278..d36d3ab2b 100644 --- a/changelog.md +++ b/changelog.md @@ -211,7 +211,7 @@ in the installer and test system. - Improvement: Removes use of empty strings ("") in favor of `string::clear()` and `string::empty()`. This avoids generating unnecessary temporary objects. #468 Thank you Vladislav Yaroslavlev for reporting and a patch. -- Documentation: Adds an example demonstrating the use of external `io_service` +- Documentation: Adds an example demonstrating the use of external `io_context` - Documentation: Adds a simple `echo_client` example. - Documentation: Begins migration of the web based user manual into Doxygen. - Bug: Fix memory leak when `init_asio` produces an error. #454 Thank you Mark @@ -232,7 +232,7 @@ in the installer and test system. - Bug: Fix an issue where TLS includes were broken for Asio Standalone builds. Thank you giachi and Bastien Brunnenstein for reporting. #491 - Bug: Remove the use of cached read and write handlers in the Asio transport. - This feature caused memory leaks when the `io_service` the connection was + This feature caused memory leaks when the `io_context` the connection was running on was abruptly stopped. There isn't a clean and safe way of using this optimization without global state and the associated locks. The locks perform worse. Thank you Xavier Gibert for reporting, test cases, and code. @@ -315,7 +315,7 @@ in the installer and test system. - Improvement: Message payload logging now prints text for text messages rather than binary. - Improvement: Overhaul of handshake state machine. Should make it impossible - for exceptions to bubble out of transport methods like `io_service::run`. + for exceptions to bubble out of transport methods like `io_context::run`. - Improvement: Overhaul of handshake error reporting. Fail handler error codes will be more detailed and precise. Adds new [fail] and [http] logging channels that log failed websocket connections and successful HTTP connections @@ -385,7 +385,7 @@ in the installer and test system. 0.3.0 - 2014-08-10 - Feature: Adds `start_perpetual` and `stop_perpetual` methods to asio transport - These may be used to replace manually managed `asio::io_service::work` objects + These may be used to replace manually managed `asio::io_context::work` objects - Feature: Allow setting pong and handshake timeouts at runtime. - Feature: Allows changing the listen backlog queue length. - Feature: Split tcp init into pre and post init. @@ -427,7 +427,7 @@ in the installer and test system. reference counted pointers. #310 Thank you otaras for reporting. - Bug: Fix issue with const endpoint accessors (such as `get_user_agent`) not compiling due to non-const mutex use. #292 Thank you logofive for reporting. -- Bug: Fix handler allocation crash with multithreaded `io_service`. +- Bug: Fix handler allocation crash with multithreaded `io_context`. - Bug: Fixes incorrect whitespace handling in header parsing. #301 Thank you Wolfram Schroers for reporting - Bug: Fix a crash when parsing empty HTTP headers. Thank you Thingol for @@ -457,7 +457,7 @@ in the installer and test system. - Updates bundled sha1 library to one with a cleaner interface and more straight-forward license. Thank you lotodore for reporting and Evgeni Golov for reviewing. #294 -- Re-introduces strands to asio transport, allowing `io_service` thread pools to +- Re-introduces strands to asio transport, allowing `io_context` thread pools to be used (with some limitations). - Removes endpoint code that kept track of a connection list that was never used anywhere. Removes a lock and reduces connection creation/deletion complexity @@ -482,7 +482,7 @@ in the installer and test system. - Refactors `asio_transport` endpoint and adds full documentation and exception free varients of all methods. - Removes `asio_transport` endpoint method cancel(). Use `stop_listen()` instead -- Wrap internal `io_service` `run_one()` method +- Wrap internal `io_context` `run_one()` method - Suppress error when trying to shut down a connection that was already closed 0.3.0-alpha3 - 2013-07-16 diff --git a/docs/faq.dox b/docs/faq.dox index 9f417ec41..c89e85b61 100644 --- a/docs/faq.dox +++ b/docs/faq.dox @@ -29,19 +29,19 @@ Note: some browsers will allow the connection to continue if they requested a su ### How do I cleanly exit an Asio transport based program -The Asio transport based clients and servers use the Asio library's underlying `io_service` to handle asyncronous networking operations. The standard behavior of the io_service is to run until there are no async operations left and then return. WebSocket++, when using the Asio transport, behaves like a standard Asio application. If you want your WebSocket++/Asio based program to stop network operations and cleanly close all sockets you will want to do the following: +The Asio transport based clients and servers use the Asio library's underlying `io_context` to handle asyncronous networking operations. The standard behavior of the io_context is to run until there are no async operations left and then return. WebSocket++, when using the Asio transport, behaves like a standard Asio application. If you want your WebSocket++/Asio based program to stop network operations and cleanly close all sockets you will want to do the following: - For servers, call `websocketpp::transport::asio::endpoint::stop_listening` to initiate the closing of the server listening socket. - For clients, if you have engaged perpetual mode with `websocketpp::transport::asio::endpoint::start_perpetual`, disable it with `websocketpp::transport::asio::endpoint::stop_perpetual`. - For both, run `websocketpp::endpoint::close` or `websocketpp::connection::close` on all currently outstanding connections. This will initiate the WebSocket closing handshake for these connections -- Wait. Asio is asyncronous. When the calls to the above methods (stop_listening, close, etc) complete the server *will still be listening*, the connections *will still be active* until the io_service gets around to asyncronously processing the socket and WebSocket protocol closing handshakes. The `io_service::run` method will exit cleanly and automatically when all operations are complete. +- Wait. Asio is asyncronous. When the calls to the above methods (stop_listening, close, etc) complete the server *will still be listening*, the connections *will still be active* until the io_context gets around to asyncronously processing the socket and WebSocket protocol closing handshakes. The `io_context::run` method will exit cleanly and automatically when all operations are complete. -__WARNING__: Asio's `io_service` has a method called `stop`. WebSocket++ wraps this method as `websocketpp::transport::asio::endpoint::stop`. While this operation has a benign sounding name, it is a powerful and destructive operation that should only be used in special cases. If you are using `io_service::stop` or `endpoint::stop` without a very good reason your program is likely broken and may exhibit erratic behavior. Specifically, `io_service::stop` stops the processing of events entirely. This does not give current operations (such as socket closing handshakes) the opportunity to finish. It will leave your sockets in a dangling state that may invoke operating system level timeouts or other errors. +__WARNING__: Asio's `io_context` has a method called `stop`. WebSocket++ wraps this method as `websocketpp::transport::asio::endpoint::stop`. While this operation has a benign sounding name, it is a powerful and destructive operation that should only be used in special cases. If you are using `io_context::stop` or `endpoint::stop` without a very good reason your program is likely broken and may exhibit erratic behavior. Specifically, `io_context::stop` stops the processing of events entirely. This does not give current operations (such as socket closing handshakes) the opportunity to finish. It will leave your sockets in a dangling state that may invoke operating system level timeouts or other errors. __Special cases__: -- If your client uses the `start_perpetual` method it will prevent the io_service from exiting even if it has nothing to do. This is useful if you want a client endpoint to idle in the background to allow new connections to be formed on demand rather than generating a new endpoint for each. -- If you are using an external io_service and/or are placing non-WebSocket++ operations on the `io_service` those operations may keep the `io_service` open even after all WebSocket++ operations have completed. -- If you are using `poll`/`poll_one`/`run_one` or otherwise manually driving the `io_service` event loop you may need to adjust usage to make sure you are correctly recognizing the "done with work" and "not done but idling / `io_service::work`" cases. +- If your client uses the `start_perpetual` method it will prevent the io_context from exiting even if it has nothing to do. This is useful if you want a client endpoint to idle in the background to allow new connections to be formed on demand rather than generating a new endpoint for each. +- If you are using an external io_context and/or are placing non-WebSocket++ operations on the `io_context` those operations may keep the `io_context` open even after all WebSocket++ operations have completed. +- If you are using `poll`/`poll_one`/`run_one` or otherwise manually driving the `io_context` event loop you may need to adjust usage to make sure you are correctly recognizing the "done with work" and "not done but idling / `io_context::work`" cases. ### Is there a way to check the validity of a `connection_hdl`? diff --git a/examples/broadcast_server/broadcast_server.cpp b/examples/broadcast_server/broadcast_server.cpp index 5b8664f76..88b07a628 100644 --- a/examples/broadcast_server/broadcast_server.cpp +++ b/examples/broadcast_server/broadcast_server.cpp @@ -75,7 +75,7 @@ class broadcast_server { return; } - // Start the ASIO io_service run loop + // Start the ASIO io_context run loop //try { m_server.run(); //} catch (const std::exception & e) { diff --git a/examples/debug_client/debug_client.cpp b/examples/debug_client/debug_client.cpp index 67c2cf4a6..2c7d94b0f 100644 --- a/examples/debug_client/debug_client.cpp +++ b/examples/debug_client/debug_client.cpp @@ -82,7 +82,7 @@ class perftest { m_endpoint.connect(con); - // Start the ASIO io_service run loop + // Start the ASIO io_context run loop m_start = std::chrono::high_resolution_clock::now(); m_endpoint.run(); } diff --git a/examples/debug_server/debug_server.cpp b/examples/debug_server/debug_server.cpp index 858d48929..b76826fd5 100644 --- a/examples/debug_server/debug_server.cpp +++ b/examples/debug_server/debug_server.cpp @@ -162,7 +162,7 @@ int main() { // Start the server accept loop echo_server.start_accept(); - // Start the ASIO io_service run loop + // Start the ASIO io_context run loop echo_server.run(); } catch (websocketpp::exception const & e) { std::cout << e.what() << std::endl; diff --git a/examples/echo_client/echo_client.cpp b/examples/echo_client/echo_client.cpp index 9a8cc3f26..58ae3fc35 100644 --- a/examples/echo_client/echo_client.cpp +++ b/examples/echo_client/echo_client.cpp @@ -87,7 +87,7 @@ int main(int argc, char* argv[]) { // exchanged until the event loop starts running in the next line. c.connect(con); - // Start the ASIO io_service run loop + // Start the ASIO io_context run loop // this will cause a single connection to be made to the server. c.run() // will exit when this connection is closed. c.run(); diff --git a/examples/echo_server/echo_server.cpp b/examples/echo_server/echo_server.cpp index 9cddd5864..89dd662bd 100644 --- a/examples/echo_server/echo_server.cpp +++ b/examples/echo_server/echo_server.cpp @@ -62,7 +62,7 @@ int main() { // Start the server accept loop echo_server.start_accept(&on_end_accept); - // Start the ASIO io_service run loop + // Start the ASIO io_context run loop echo_server.run(); } catch (websocketpp::exception const & e) { std::cout << e.what() << std::endl; diff --git a/examples/echo_server_both/echo_server_both.cpp b/examples/echo_server_both/echo_server_both.cpp index 93cf93d37..24075729d 100644 --- a/examples/echo_server_both/echo_server_both.cpp +++ b/examples/echo_server_both/echo_server_both.cpp @@ -65,13 +65,13 @@ context_ptr on_tls_init(websocketpp::connection_hdl_ref hdl) { } int main() { - // set up an external io_service to run both endpoints on. This is not + // set up an external io_context to run both endpoints on. This is not // strictly necessary, but simplifies thread management a bit. - boost::asio::io_service ios; + boost::asio::io_context ios; // set up plain endpoint server_plain endpoint_plain; - // initialize asio with our external io_service rather than an internal one + // initialize asio with our external io_context rather than an internal one endpoint_plain.init_asio(&ios); endpoint_plain.set_message_handler( bind(&on_message,&endpoint_plain,::_1,::_2)); @@ -89,6 +89,6 @@ int main() { endpoint_tls.listen(443); endpoint_tls.start_accept(&on_end_accept); - // Start the ASIO io_service run loop running both endpoints + // Start the ASIO io_context run loop running both endpoints ios.run(); } diff --git a/examples/echo_server_tls/echo_server_tls.cpp b/examples/echo_server_tls/echo_server_tls.cpp index 6890d2605..cad719890 100644 --- a/examples/echo_server_tls/echo_server_tls.cpp +++ b/examples/echo_server_tls/echo_server_tls.cpp @@ -155,7 +155,7 @@ int main() { // Start the server accept loop echo_server.start_accept(&on_end_accept); - // Start the ASIO io_service run loop + // Start the ASIO io_context run loop echo_server.run(); } diff --git a/examples/external_io_service/CMakeLists.txt b/examples/external_io_context/CMakeLists.txt similarity index 87% rename from examples/external_io_service/CMakeLists.txt rename to examples/external_io_context/CMakeLists.txt index 5223da1be..a0e89ec96 100644 --- a/examples/external_io_service/CMakeLists.txt +++ b/examples/external_io_context/CMakeLists.txt @@ -2,7 +2,7 @@ file (GLOB SOURCE_FILES *.cpp) file (GLOB HEADER_FILES *.hpp) -init_target (external_io_service) +init_target (external_io_context) build_executable (${TARGET_NAME} ${SOURCE_FILES} ${HEADER_FILES}) diff --git a/examples/external_io_service/SConscript b/examples/external_io_context/SConscript similarity index 72% rename from examples/external_io_service/SConscript rename to examples/external_io_context/SConscript index 0abf3e175..c6cd9740d 100644 --- a/examples/external_io_service/SConscript +++ b/examples/external_io_context/SConscript @@ -15,9 +15,9 @@ prgs = [] # if a C++11 environment is available build using that, otherwise use boost if 'WSPP_CPP11_ENABLED' in env_cpp11: ALL_LIBS = boostlibs(['system'],env_cpp11) + [platform_libs] + [polyfill_libs] - prgs += env_cpp11.Program('external_io_service', ["external_io_service.cpp"], LIBS = ALL_LIBS) + prgs += env_cpp11.Program('external_io_context', ["external_io_context.cpp"], LIBS = ALL_LIBS) else: ALL_LIBS = boostlibs(['system'],env) + [platform_libs] + [polyfill_libs] - prgs += env.Program('external_io_service', ["external_io_service.cpp"], LIBS = ALL_LIBS) + prgs += env.Program('external_io_context', ["external_io_context.cpp"], LIBS = ALL_LIBS) Return('prgs') diff --git a/examples/external_io_service/external_io_service.cpp b/examples/external_io_context/external_io_context.cpp similarity index 96% rename from examples/external_io_service/external_io_service.cpp rename to examples/external_io_context/external_io_context.cpp index 8d84b286a..df32c969a 100644 --- a/examples/external_io_service/external_io_service.cpp +++ b/examples/external_io_context/external_io_context.cpp @@ -66,7 +66,7 @@ void on_end_accept(error_code lib_ec, error_code trans_ec) { } int main() { - asio::io_service service; + asio::io_context service; // Add a TCP echo server on port 9003 tcp_echo_server custom_http_server(service, 9003); @@ -77,7 +77,7 @@ int main() { ws_server.clear_access_channels(websocketpp::log::alevel::frame_payload); // The only difference in this code between an internal and external - // io_service is the different constructor to init_asio + // io_context is the different constructor to init_asio ws_server.init_asio(&service); // Register our message handler @@ -87,6 +87,6 @@ int main() { // TODO: add a timer? - // Start the Asio io_service run loop for all + // Start the Asio io_context run loop for all service.run(); } \ No newline at end of file diff --git a/examples/external_io_service/tcp_echo_server.hpp b/examples/external_io_context/tcp_echo_server.hpp similarity index 95% rename from examples/external_io_service/tcp_echo_server.hpp rename to examples/external_io_context/tcp_echo_server.hpp index ef4ce1855..a1460bc05 100644 --- a/examples/external_io_service/tcp_echo_server.hpp +++ b/examples/external_io_context/tcp_echo_server.hpp @@ -44,7 +44,7 @@ namespace asio = websocketpp::lib::asio; struct tcp_echo_session : websocketpp::lib::enable_shared_from_this { typedef websocketpp::lib::shared_ptr ptr; - tcp_echo_session(asio::io_service & service) : m_socket(service) {} + tcp_echo_session(asio::io_context & service) : m_socket(service) {} void start() { m_socket.async_read_some(asio::buffer(m_buffer, sizeof(m_buffer)), @@ -72,7 +72,7 @@ struct tcp_echo_session : websocketpp::lib::enable_shared_from_thisget_handle(); // Queue the connection. No DNS queries or network connections will be - // made until the io_service event loop is run. + // made until the io_context event loop is run. m_client.connect(con); - // Create a thread to run the ASIO io_service event loop + // Create a thread to run the ASIO io_context event loop websocketpp::lib::thread asio_thread(&client::run, &m_client); // Create a thread to run the telemetry loop diff --git a/examples/telemetry_server/telemetry_server.cpp b/examples/telemetry_server/telemetry_server.cpp index 7c55a801c..cd8cec857 100644 --- a/examples/telemetry_server/telemetry_server.cpp +++ b/examples/telemetry_server/telemetry_server.cpp @@ -69,7 +69,7 @@ class telemetry_server { // Set the initial timer to start telemetry set_timer(); - // Start the ASIO io_service run loop + // Start the ASIO io_context run loop try { m_endpoint.run(); } catch (websocketpp::exception const & e) { diff --git a/examples/testee_client/testee_client.cpp b/examples/testee_client/testee_client.cpp index 01bca4602..2295c87c6 100644 --- a/examples/testee_client/testee_client.cpp +++ b/examples/testee_client/testee_client.cpp @@ -117,7 +117,7 @@ int main(int argc, char* argv[]) { client::connection_ptr con = c.get_connection(uri+"/getCaseCount", ec); c.connect(con); - // Start the ASIO io_service run loop + // Start the ASIO io_context run loop c.run(); std::cout << "case count: " << case_count << std::endl; diff --git a/examples/testee_server/testee_server.cpp b/examples/testee_server/testee_server.cpp index 394d5e60a..09c6e100c 100644 --- a/examples/testee_server/testee_server.cpp +++ b/examples/testee_server/testee_server.cpp @@ -131,7 +131,7 @@ int main(int argc, char * argv[]) { // Start the server accept loop testee_server.start_accept(&on_end_accept); - // Start the ASIO io_service run loop + // Start the ASIO io_context run loop if (num_threads == 1) { testee_server.run(); } else { diff --git a/roadmap.md b/roadmap.md index b35c54ad0..c6103322f 100644 --- a/roadmap.md +++ b/roadmap.md @@ -16,7 +16,7 @@ Complete & Tested: - open_handler - close_handler - echo_server & echo_server_tls -- External io_service support +- External io_context support - TLS support - exception/error handling - Timeouts diff --git a/test/endpoint/endpoint.cpp b/test/endpoint/endpoint.cpp index b4c429ebc..749624f23 100644 --- a/test/endpoint/endpoint.cpp +++ b/test/endpoint/endpoint.cpp @@ -53,7 +53,7 @@ BOOST_AUTO_TEST_CASE( initialize_server_asio ) { BOOST_AUTO_TEST_CASE( initialize_server_asio_external ) { websocketpp::server s; - boost::asio::io_service ios; + boost::asio::io_context ios; s.init_asio(&ios); } diff --git a/test/transport/asio/timers.cpp b/test/transport/asio/timers.cpp index aa03d6189..391b755ff 100644 --- a/test/transport/asio/timers.cpp +++ b/test/transport/asio/timers.cpp @@ -54,9 +54,9 @@ void run_dummy_server(int port) { using boost::asio::ip::tcp; try { - boost::asio::io_service io_service; - tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v6(), port)); - tcp::socket socket(io_service); + boost::asio::io_context io_context; + tcp::acceptor acceptor(io_context, tcp::endpoint(tcp::v6(), port)); + tcp::socket socket(io_context); acceptor.accept(socket); for (;;) { @@ -79,7 +79,7 @@ void run_dummy_server(int port) { // Wait for the specified time period then fail the test void run_test_timer(long value) { - boost::asio::io_service ios; + boost::asio::io_context ios; boost::asio::deadline_timer t(ios,boost::posix_time::milliseconds(value)); boost::system::error_code ec; t.wait(ec); diff --git a/test/transport/integration.cpp b/test/transport/integration.cpp index 9a4cac5c1..ef4735255 100644 --- a/test/transport/integration.cpp +++ b/test/transport/integration.cpp @@ -225,9 +225,9 @@ void run_dummy_server(int port) { using boost::asio::ip::tcp; try { - boost::asio::io_service io_service; - tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v6(), port)); - tcp::socket socket(io_service); + boost::asio::io_context io_context; + tcp::acceptor acceptor(io_context, tcp::endpoint(tcp::v6(), port)); + tcp::socket socket(io_context); acceptor.accept(socket); for (;;) { @@ -252,11 +252,11 @@ void run_dummy_client(std::string port) { using boost::asio::ip::tcp; try { - boost::asio::io_service io_service; - tcp::resolver resolver(io_service); + boost::asio::io_context io_context; + tcp::resolver resolver(io_context); tcp::resolver::query query("localhost", port); tcp::resolver::iterator iterator = resolver.resolve(query); - tcp::socket socket(io_service); + tcp::socket socket(io_context); boost::asio::connect(socket, iterator); for (;;) { @@ -362,11 +362,11 @@ class test_deadline_timer { public: test_deadline_timer(int seconds) - : m_timer(m_io_service, boost::posix_time::seconds(seconds)) + : m_timer(m_io_context, boost::posix_time::seconds(seconds)) { m_timer.async_wait(bind(&test_deadline_timer::expired, this, ::_1)); - std::size_t (boost::asio::io_service::*run)() = &boost::asio::io_service::run; - m_timer_thread = websocketpp::lib::thread(websocketpp::lib::bind(run, &m_io_service)); + std::size_t (boost::asio::io_context::*run)() = &boost::asio::io_context::run; + m_timer_thread = websocketpp::lib::thread(websocketpp::lib::bind(run, &m_io_context)); } ~test_deadline_timer() { @@ -383,7 +383,7 @@ class test_deadline_timer BOOST_FAIL("Test timed out"); } - boost::asio::io_service m_io_service; + boost::asio::io_context m_io_context; boost::asio::deadline_timer m_timer; websocketpp::lib::thread m_timer_thread; }; @@ -545,7 +545,7 @@ BOOST_AUTO_TEST_CASE( client_runs_out_of_work ) { c.run(); - // This test checks that an io_service with no work ends immediately. + // This test checks that an io_context with no work ends immediately. BOOST_CHECK(true); } diff --git a/tutorials/utility_server/step1.cpp b/tutorials/utility_server/step1.cpp index c0e464382..dae4a2ab9 100644 --- a/tutorials/utility_server/step1.cpp +++ b/tutorials/utility_server/step1.cpp @@ -57,7 +57,7 @@ class utility_server { // Queues a connection accept operation m_endpoint.start_accept(); - // Start the Asio io_service run loop + // Start the Asio io_context run loop m_endpoint.run(); } private: diff --git a/tutorials/utility_server/step2.cpp b/tutorials/utility_server/step2.cpp index c416a0fac..cde153864 100644 --- a/tutorials/utility_server/step2.cpp +++ b/tutorials/utility_server/step2.cpp @@ -68,7 +68,7 @@ class utility_server { // Queues a connection accept operation m_endpoint.start_accept(); - // Start the Asio io_service run loop + // Start the Asio io_context run loop m_endpoint.run(); } private: diff --git a/tutorials/utility_server/utility_server.md b/tutorials/utility_server/utility_server.md index 1c7ee3fc1..461eed071 100644 --- a/tutorials/utility_server/utility_server.md +++ b/tutorials/utility_server/utility_server.md @@ -56,7 +56,7 @@ m_endpoint.set_access_channels(websocketpp::log::alevel::all ^ websocketpp::log: Next, we initialize the transport system underlying the endpoint. This method is specific to the Asio transport not WebSocket++ core. It will not be necessary or present in endpoints that use a non-asio config. -> **Note:** This example uses an internal Asio `io_service` that is managed by the endpoint itself. This is a simple arrangement suitable for programs where WebSocket++ is the only code using Asio. If you have an existing program that already manages an `io_service` object or want to build a new program where WebSocket++ handlers share an io_service with other handlers you can pass the `io_service` you want WebSocket++ to register its handlers on to the `init_asio()` method and it will use it instead of generating and managing its own. [TODO: FAQ link instead?] +> **Note:** This example uses an internal Asio `io_context` that is managed by the endpoint itself. This is a simple arrangement suitable for programs where WebSocket++ is the only code using Asio. If you have an existing program that already manages an `io_context` object or want to build a new program where WebSocket++ handlers share an io_context with other handlers you can pass the `io_context` you want WebSocket++ to register its handlers on to the `init_asio()` method and it will use it instead of generating and managing its own. [TODO: FAQ link instead?] ~~~{.cpp} m_endpoint.init_asio(); @@ -64,7 +64,7 @@ m_endpoint.init_asio(); #### `utility_server::run` method -In addition to the constructor, we also add a run method that sets up the listening socket, begins accepting connections, starts the Asio io_service event loop. +In addition to the constructor, we also add a run method that sets up the listening socket, begins accepting connections, starts the Asio io_context event loop. ~~~{.cpp} // Listen on port 9002 @@ -73,7 +73,7 @@ m_endpoint.listen(9002); // Queues a connection accept operation m_endpoint.start_accept(); -// Start the Asio io_service run loop +// Start the Asio io_context run loop m_endpoint.run(); ~~~ @@ -123,7 +123,7 @@ public: // Queues a connection accept operation m_endpoint.start_accept(); - // Start the Asio io_service run loop + // Start the Asio io_context run loop m_endpoint.run(); } private: diff --git a/websocketpp/roles/server_endpoint.hpp b/websocketpp/roles/server_endpoint.hpp index 04fee18f9..b69281fdb 100644 --- a/websocketpp/roles/server_endpoint.hpp +++ b/websocketpp/roles/server_endpoint.hpp @@ -137,8 +137,8 @@ class server : public endpoint,config> { /// Starts the server's async connection acceptance loop (exception free) /** * Initiates the server connection acceptance loop. Must be called after - * listen. This method will have no effect until the underlying io_service - * starts running. It may be called after the io_service is already running. + * listen. This method will have no effect until the underlying io_context + * starts running. It may be called after the io_context is already running. * * Refer to documentation for the transport policy you are using for * instructions on how to stop this acceptance loop. diff --git a/websocketpp/transport/asio/base.hpp b/websocketpp/transport/asio/base.hpp index b945fe11f..8d980ad7c 100644 --- a/websocketpp/transport/asio/base.hpp +++ b/websocketpp/transport/asio/base.hpp @@ -40,7 +40,7 @@ namespace websocketpp { namespace transport { /// Transport policy that uses asio /** - * This policy uses a single asio io_service to provide transport + * This policy uses a single asio io_context to provide transport * services to a WebSocket++ endpoint. */ namespace asio { diff --git a/websocketpp/transport/asio/connection.hpp b/websocketpp/transport/asio/connection.hpp index 032f6a5ab..c04f7895a 100644 --- a/websocketpp/transport/asio/connection.hpp +++ b/websocketpp/transport/asio/connection.hpp @@ -85,10 +85,10 @@ class connection : public config::socket_type::socket_con_type { typedef typename config::response_type response_type; typedef typename response_type::ptr response_ptr; - /// Type of a pointer to the Asio io_service being used - typedef lib::asio::io_service * io_service_ptr; - /// Type of a pointer to the Asio io_service::strand being used - typedef lib::shared_ptr strand_ptr; + /// Type of a pointer to the Asio io_context being used + typedef lib::asio::io_context * io_context_ptr; + /// Type of a pointer to the Asio io_context::strand being used + typedef lib::shared_ptr strand_ptr; /// Type of a pointer to the Asio timer class typedef lib::shared_ptr timer_ptr; @@ -97,7 +97,7 @@ class connection : public config::socket_type::socket_con_type { // to the public api. friend class endpoint; - // generate and manage our own io_service + // generate and manage our own io_context explicit connection(bool is_server, const lib::shared_ptr & alog, const lib::shared_ptr & elog) : m_is_server(is_server) , m_alog(alog) @@ -321,7 +321,7 @@ class connection : public config::socket_type::socket_con_type { timer_ptr set_timer(long duration, timer_handler callback) { timer_ptr new_timer( new lib::asio::steady_timer( - *m_io_service, + *m_io_context, lib::asio::milliseconds(duration)) ); @@ -401,7 +401,7 @@ class connection : public config::socket_type::socket_con_type { /// Initialize transport for reading /** * init_asio is called once immediately after construction to initialize - * Asio components to the io_service + * Asio components to the io_context * * The transport initialization sequence consists of the following steps: * - Pre-init: the underlying socket is initialized to the point where @@ -459,21 +459,21 @@ class connection : public config::socket_type::socket_con_type { /// Finish constructing the transport /** * init_asio is called once immediately after construction to initialize - * Asio components to the io_service. + * Asio components to the io_context. * - * @param io_service A pointer to the io_service to register with this + * @param io_context A pointer to the io_context to register with this * connection * * @return Status code for the success or failure of the initialization */ - lib::error_code init_asio (io_service_ptr io_service) { - m_io_service = io_service; + lib::error_code init_asio (io_context_ptr io_context) { + m_io_context = io_context; if (config::enable_multithreading) { - m_strand.reset(new lib::asio::io_service::strand(*io_service)); + m_strand.reset(new lib::asio::io_context::strand(*io_context)); } - lib::error_code ec = socket_con_type::init_asio(io_service, m_strand, + lib::error_code ec = socket_con_type::init_asio(io_context, m_strand, m_is_server); return ec; @@ -1032,18 +1032,18 @@ class connection : public config::socket_type::socket_con_type { */ lib::error_code interrupt(interrupt_handler handler) { if (config::enable_multithreading) { - m_io_service->post(m_strand->wrap(handler)); + m_io_context->post(m_strand->wrap(handler)); } else { - m_io_service->post(handler); + m_io_context->post(handler); } return lib::error_code(); } lib::error_code dispatch(dispatch_handler handler) { if (config::enable_multithreading) { - m_io_service->post(m_strand->wrap(handler)); + m_io_context->post(m_strand->wrap(handler)); } else { - m_io_service->post(handler); + m_io_context->post(handler); } return lib::error_code(); } @@ -1192,7 +1192,7 @@ class connection : public config::socket_type::socket_con_type { lib::shared_ptr m_proxy_data; // transport resources - io_service_ptr m_io_service; + io_context_ptr m_io_context; strand_ptr m_strand; connection_hdl m_connection_hdl; diff --git a/websocketpp/transport/asio/endpoint.hpp b/websocketpp/transport/asio/endpoint.hpp index ce1112f17..e109c5295 100644 --- a/websocketpp/transport/asio/endpoint.hpp +++ b/websocketpp/transport/asio/endpoint.hpp @@ -77,24 +77,24 @@ class endpoint : public config::socket_type { /// associated with this endpoint transport component typedef typename transport_con_type::ptr transport_con_ptr; - /// Type of a pointer to the ASIO io_service being used - typedef lib::asio::io_service * io_service_ptr; + /// Type of a pointer to the ASIO io_context being used + typedef lib::asio::io_context * io_context_ptr; /// Type of a shared pointer to the acceptor being used typedef lib::shared_ptr acceptor_ptr; /// Type of a shared pointer to the resolver being used typedef lib::shared_ptr resolver_ptr; /// Type of timer handle typedef lib::shared_ptr timer_ptr; - /// Type of a shared pointer to an io_service work object - typedef lib::shared_ptr work_ptr; + /// Type of a shared pointer to an io_context work object + typedef lib::shared_ptr work_ptr; /// Type of socket pre-bind handler typedef lib::function tcp_pre_bind_handler; - // generate and manage our own io_service + // generate and manage our own io_context explicit endpoint() - : m_io_service(NULL) - , m_external_io_service(false) + : m_io_context(NULL) + , m_external_io_context(false) , m_listen_backlog(lib::asio::socket_base::max_connections) , m_reuse_addr(false) , m_state(UNINITIALIZED) @@ -103,14 +103,14 @@ class endpoint : public config::socket_type { } virtual ~endpoint() { - // clean up our io_service if we were initialized with an internal one. + // clean up our io_context if we were initialized with an internal one. // Explicitly destroy local objects m_acceptor.reset(); m_resolver.reset(); m_work.reset(); - if (m_state != UNINITIALIZED && !m_external_io_service) { - delete m_io_service; + if (m_state != UNINITIALIZED && !m_external_io_context) { + delete m_io_context; } } @@ -132,8 +132,8 @@ class endpoint : public config::socket_type { : config::socket_type(std::move(src)) , m_tcp_pre_init_handler(src.m_tcp_pre_init_handler) , m_tcp_post_init_handler(src.m_tcp_post_init_handler) - , m_io_service(src.m_io_service) - , m_external_io_service(src.m_external_io_service) + , m_io_context(src.m_io_context) + , m_external_io_context(src.m_external_io_context) , m_acceptor(src.m_acceptor) , m_listen_backlog(lib::asio::socket_base::max_connections) , m_reuse_addr(src.m_reuse_addr) @@ -141,23 +141,23 @@ class endpoint : public config::socket_type { , m_alog(src.m_alog) , m_state(src.m_state) { - src.m_io_service = NULL; - src.m_external_io_service = false; + src.m_io_context = NULL; + src.m_external_io_context = false; src.m_acceptor = NULL; src.m_state = UNINITIALIZED; } /*endpoint & operator= (const endpoint && rhs) { if (this != &rhs) { - m_io_service = rhs.m_io_service; - m_external_io_service = rhs.m_external_io_service; + m_io_context = rhs.m_io_context; + m_external_io_context = rhs.m_external_io_context; m_acceptor = rhs.m_acceptor; m_listen_backlog = rhs.m_listen_backlog; m_reuse_addr = rhs.m_reuse_addr; m_state = rhs.m_state; - rhs.m_io_service = NULL; - rhs.m_external_io_service = false; + rhs.m_io_context = NULL; + rhs.m_external_io_context = false; rhs.m_acceptor = NULL; rhs.m_listen_backlog = lib::asio::socket_base::max_connections; rhs.m_state = UNINITIALIZED; @@ -173,16 +173,16 @@ class endpoint : public config::socket_type { return socket_type::is_secure(); } - /// initialize asio transport with external io_service (exception free) + /// initialize asio transport with external io_context (exception free) /** * Initialize the ASIO transport policy for this endpoint using the provided - * io_service object. asio_init must be called exactly once on any endpoint + * io_context object. asio_init must be called exactly once on any endpoint * that uses transport::asio before it can be used. * - * @param ptr A pointer to the io_service to use for asio events + * @param ptr A pointer to the io_context to use for asio events * @param ec Set to indicate what error occurred, if any. */ - void init_asio(io_service_ptr ptr, lib::error_code & ec) { + void init_asio(io_context_ptr ptr, lib::error_code & ec) { if (m_state != UNINITIALIZED) { m_elog->write(log::elevel::library, "asio::init_asio called from the wrong state"); @@ -193,36 +193,36 @@ class endpoint : public config::socket_type { m_alog->write(log::alevel::devel,"asio::init_asio"); - m_io_service = ptr; - m_external_io_service = true; - m_acceptor.reset(new lib::asio::ip::tcp::acceptor(*m_io_service)); + m_io_context = ptr; + m_external_io_context = true; + m_acceptor.reset(new lib::asio::ip::tcp::acceptor(*m_io_context)); m_state = READY; ec = lib::error_code(); } #ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ - /// initialize asio transport with external io_service + /// initialize asio transport with external io_context /** * Initialize the ASIO transport policy for this endpoint using the provided - * io_service object. asio_init must be called exactly once on any endpoint + * io_context object. asio_init must be called exactly once on any endpoint * that uses transport::asio before it can be used. * - * @param ptr A pointer to the io_service to use for asio events + * @param ptr A pointer to the io_context to use for asio events */ - void init_asio(io_service_ptr ptr) { + void init_asio(io_context_ptr ptr) { lib::error_code ec; init_asio(ptr,ec); if (ec) { throw exception(ec); } } #endif // _WEBSOCKETPP_NO_EXCEPTIONS_ - /// Initialize asio transport with internal io_service (exception free) + /// Initialize asio transport with internal io_context (exception free) /** * This method of initialization will allocate and use an internally managed - * io_service. + * io_context. * - * @see init_asio(io_service_ptr ptr) + * @see init_asio(io_context_ptr ptr) * * @param ec Set to indicate what error occurred, if any. */ @@ -232,22 +232,22 @@ class endpoint : public config::socket_type { // TODO: remove the use of auto_ptr when C++98/03 support is no longer // necessary. #ifdef _WEBSOCKETPP_CPP11_MEMORY_ - lib::unique_ptr service(new lib::asio::io_service()); + lib::unique_ptr service(new lib::asio::io_context()); #else - lib::auto_ptr service(new lib::asio::io_service()); + lib::auto_ptr service(new lib::asio::io_context()); #endif init_asio(service.get(), ec); if( !ec ) service.release(); // Call was successful, transfer ownership - m_external_io_service = false; + m_external_io_context = false; } #ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ - /// Initialize asio transport with internal io_service + /// Initialize asio transport with internal io_context /** * This method of initialization will allocate and use an internally managed - * io_service. + * io_context. * - * @see init_asio(io_service_ptr ptr) + * @see init_asio(io_context_ptr ptr) */ void init_asio() { // Use a smart pointer until the call is successful and ownership has @@ -255,14 +255,14 @@ class endpoint : public config::socket_type { // TODO: remove the use of auto_ptr when C++98/03 support is no longer // necessary. #ifdef _WEBSOCKETPP_CPP11_MEMORY_ - lib::unique_ptr service(new lib::asio::io_service()); + lib::unique_ptr service(new lib::asio::io_context()); #else - lib::auto_ptr service(new lib::asio::io_service()); + lib::auto_ptr service(new lib::asio::io_context()); #endif init_asio( service.get() ); // If control got this far without an exception, then ownership has successfully been taken service.release(); - m_external_io_service = false; + m_external_io_context = false; } #endif // _WEBSOCKETPP_NO_EXCEPTIONS_ @@ -368,19 +368,19 @@ class endpoint : public config::socket_type { m_reuse_addr = value; } - /// Retrieve a reference to the endpoint's io_service + /// Retrieve a reference to the endpoint's io_context /** - * The io_service may be an internal or external one. This may be used to - * call methods of the io_service that are not explicitly wrapped by the + * The io_context may be an internal or external one. This may be used to + * call methods of the io_context that are not explicitly wrapped by the * endpoint. * * This method is only valid after the endpoint has been initialized with * `init_asio`. No error will be returned if it isn't. * - * @return A reference to the endpoint's io_service + * @return A reference to the endpoint's io_context */ - lib::asio::io_service & get_io_service() { - return *m_io_service; + lib::asio::io_context & get_io_context() { + return *m_io_context; } /// Get local TCP endpoint @@ -501,7 +501,7 @@ class endpoint : public config::socket_type { * The endpoint must have been initialized by calling init_asio before * listening. * - * Once listening the underlying io_service will be kept open indefinitely. + * Once listening the underlying io_context will be kept open indefinitely. * Calling endpoint::stop_listening will stop the endpoint from accepting * new connections. See the documentation for stop listening for more details * about shutting down Asio Transport based endpoints. @@ -518,7 +518,7 @@ class endpoint : public config::socket_type { lib::error_code & ec) { using lib::asio::ip::tcp; - tcp::resolver r(*m_io_service); + tcp::resolver r(*m_io_context); tcp::resolver::query query(host, service); tcp::resolver::iterator endpoint_iterator = r.resolve(query); tcp::resolver::iterator end; @@ -621,7 +621,7 @@ class endpoint : public config::socket_type { * The endpoint must have been initialized by calling init_asio before * listening. * - * Once listening the underlying io_service will be kept open indefinitely. + * Once listening the underlying io_context will be kept open indefinitely. * Calling endpoint::stop_listening will stop the endpoint from accepting * new connections. See the documentation for stop listening for more * details about shutting down Asio Transport based endpoints. @@ -663,42 +663,42 @@ class endpoint : public config::socket_type { return (m_state == LISTENING); } - /// wraps the run method of the internal io_service object + /// wraps the run method of the internal io_context object std::size_t run() { - return m_io_service->run(); + return m_io_context->run(); } - /// wraps the run_one method of the internal io_service object + /// wraps the run_one method of the internal io_context object /** * @since 0.3.0-alpha4 */ std::size_t run_one() { - return m_io_service->run_one(); + return m_io_context->run_one(); } - /// wraps the stop method of the internal io_service object + /// wraps the stop method of the internal io_context object void stop() { - m_io_service->stop(); + m_io_context->stop(); } - /// wraps the poll method of the internal io_service object + /// wraps the poll method of the internal io_context object std::size_t poll() { - return m_io_service->poll(); + return m_io_context->poll(); } - /// wraps the poll_one method of the internal io_service object + /// wraps the poll_one method of the internal io_context object std::size_t poll_one() { - return m_io_service->poll_one(); + return m_io_context->poll_one(); } - /// wraps the reset method of the internal io_service object + /// wraps the reset method of the internal io_context object void reset() { - m_io_service->reset(); + m_io_context->reset(); } - /// wraps the stopped method of the internal io_service object + /// wraps the stopped method of the internal io_context object bool stopped() const { - return m_io_service->stopped(); + return m_io_context->stopped(); } /// Marks the endpoint as perpetual, stopping it from exiting when empty @@ -714,7 +714,7 @@ class endpoint : public config::socket_type { * @since 0.3.0 */ void start_perpetual() { - m_work.reset(new lib::asio::io_service::work(*m_io_service)); + m_work.reset(new lib::asio::io_context::work(*m_io_context)); } /// Clears the endpoint's perpetual flag, allowing it to exit when empty @@ -743,7 +743,7 @@ class endpoint : public config::socket_type { */ timer_ptr set_timer(long duration, timer_handler callback) { timer_ptr new_timer = lib::make_shared( - *m_io_service, + *m_io_context, lib::asio::milliseconds(duration) ); @@ -880,7 +880,7 @@ class endpoint : public config::socket_type { // Create a resolver if (!m_resolver) { - m_resolver.reset(new lib::asio::ip::tcp::resolver(*m_io_service)); + m_resolver.reset(new lib::asio::ip::tcp::resolver(*m_io_context)); } tcon->set_uri(u); @@ -1148,7 +1148,7 @@ class endpoint : public config::socket_type { lib::error_code ec; - ec = tcon->init_asio(m_io_service); + ec = tcon->init_asio(m_io_context); if (ec) {return ec;} tcon->set_tcp_pre_init_handler(m_tcp_pre_init_handler); @@ -1187,8 +1187,8 @@ class endpoint : public config::socket_type { tcp_init_handler m_tcp_post_init_handler; // Network Resources - io_service_ptr m_io_service; - bool m_external_io_service; + io_context_ptr m_io_context; + bool m_external_io_context; acceptor_ptr m_acceptor; resolver_ptr m_resolver; work_ptr m_work; diff --git a/websocketpp/transport/asio/security/none.hpp b/websocketpp/transport/asio/security/none.hpp index c314f0333..1c564732d 100644 --- a/websocketpp/transport/asio/security/none.hpp +++ b/websocketpp/transport/asio/security/none.hpp @@ -62,10 +62,10 @@ class connection : public lib::enable_shared_from_this { /// Type of a shared pointer to this connection socket component typedef lib::shared_ptr ptr; - /// Type of a pointer to the Asio io_service being used - typedef lib::asio::io_service* io_service_ptr; - /// Type of a pointer to the Asio io_service strand being used - typedef lib::shared_ptr strand_ptr; + /// Type of a pointer to the Asio io_context being used + typedef lib::asio::io_context* io_context_ptr; + /// Type of a pointer to the Asio io_context strand being used + typedef lib::shared_ptr strand_ptr; /// Type of the ASIO socket being used typedef lib::asio::ip::tcp::socket socket_type; /// Type of a shared pointer to the socket being used. @@ -160,14 +160,14 @@ class connection : public lib::enable_shared_from_this { /// Perform one time initializations /** * init_asio is called once immediately after construction to initialize - * Asio components to the io_service. At this stage the connection is + * Asio components to the io_context. At this stage the connection is * speculative, the server may not have actually received a new connection. * - * @param service A pointer to the endpoint's io_service + * @param service A pointer to the endpoint's io_context * @param strand A shared pointer to the connection's asio strand * @param is_server Whether or not the endpoint is a server or not. */ - lib::error_code init_asio (io_service_ptr service, strand_ptr, bool) + lib::error_code init_asio (io_context_ptr service, strand_ptr, bool) { if (m_state != UNINITIALIZED) { return socket::make_error_code(socket::error::invalid_state); diff --git a/websocketpp/transport/asio/security/tls.hpp b/websocketpp/transport/asio/security/tls.hpp index cd37431cb..3c0799dc1 100644 --- a/websocketpp/transport/asio/security/tls.hpp +++ b/websocketpp/transport/asio/security/tls.hpp @@ -71,10 +71,10 @@ class connection : public lib::enable_shared_from_this { typedef lib::asio::ssl::stream socket_type; /// Type of a shared pointer to the ASIO socket being used typedef lib::shared_ptr socket_ptr; - /// Type of a pointer to the ASIO io_service being used - typedef lib::asio::io_service * io_service_ptr; - /// Type of a pointer to the ASIO io_service strand being used - typedef lib::shared_ptr strand_ptr; + /// Type of a pointer to the ASIO io_context being used + typedef lib::asio::io_context * io_context_ptr; + /// Type of a pointer to the ASIO io_context strand being used + typedef lib::shared_ptr strand_ptr; /// Type of a shared pointer to the ASIO TLS context being used typedef lib::shared_ptr context_ptr; @@ -180,13 +180,13 @@ class connection : public lib::enable_shared_from_this { /// Perform one time initializations /** * init_asio is called once immediately after construction to initialize - * Asio components to the io_service + * Asio components to the io_context * - * @param service A pointer to the endpoint's io_service + * @param service A pointer to the endpoint's io_context * @param strand A pointer to the connection's strand * @param is_server Whether or not the endpoint is a server or not. */ - lib::error_code init_asio (io_service_ptr service, strand_ptr strand, + lib::error_code init_asio (io_context_ptr service, strand_ptr strand, bool is_server) { if (!m_tls_init_handler) { @@ -199,7 +199,7 @@ class connection : public lib::enable_shared_from_this { } m_socket.reset(new socket_type(*service, *m_context)); - m_io_service = service; + m_io_context = service; m_strand = strand; m_is_server = is_server; @@ -423,7 +423,7 @@ class connection : public lib::enable_shared_from_this { } } - io_service_ptr m_io_service; + io_context_ptr m_io_context; strand_ptr m_strand; context_ptr m_context; socket_ptr m_socket; diff --git a/websocketpp/transport/debug/endpoint.hpp b/websocketpp/transport/debug/endpoint.hpp index adc89b382..360644165 100644 --- a/websocketpp/transport/debug/endpoint.hpp +++ b/websocketpp/transport/debug/endpoint.hpp @@ -60,7 +60,7 @@ class endpoint { /// associated connection transport component typedef typename transport_con_type::ptr transport_con_ptr; - // generate and manage our own io_service + // generate and manage our own io_context explicit endpoint() { //std::cout << "transport::iostream::endpoint constructor" << std::endl; diff --git a/websocketpp/transport/iostream/endpoint.hpp b/websocketpp/transport/iostream/endpoint.hpp index 14cba7255..257472db8 100644 --- a/websocketpp/transport/iostream/endpoint.hpp +++ b/websocketpp/transport/iostream/endpoint.hpp @@ -64,7 +64,7 @@ class endpoint { /// associated connection transport component typedef typename transport_con_type::ptr transport_con_ptr; - // generate and manage our own io_service + // generate and manage our own io_context explicit endpoint() : m_output_stream(NULL), m_is_secure(false) { //std::cout << "transport::iostream::endpoint constructor" << std::endl; diff --git a/websocketpp/transport/stub/endpoint.hpp b/websocketpp/transport/stub/endpoint.hpp index eb6570a4c..e0d5f2d3a 100644 --- a/websocketpp/transport/stub/endpoint.hpp +++ b/websocketpp/transport/stub/endpoint.hpp @@ -60,7 +60,7 @@ class endpoint { /// associated connection transport component typedef typename transport_con_type::ptr transport_con_ptr; - // generate and manage our own io_service + // generate and manage our own io_context explicit endpoint() { //std::cout << "transport::iostream::endpoint constructor" << std::endl; From da3cfbf499306c6c432fa6da0424b032602e70a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20To=C5=A1kan?= Date: Wed, 21 Aug 2024 13:08:31 +0200 Subject: [PATCH 37/40] fix(uri): Parsing of link-local IPv6 and zoneids --- websocketpp/processors/processor.hpp | 11 +++++++++-- websocketpp/uri.hpp | 1 + 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/websocketpp/processors/processor.hpp b/websocketpp/processors/processor.hpp index 71b61bab9..eaf4cf0b0 100644 --- a/websocketpp/processors/processor.hpp +++ b/websocketpp/processors/processor.hpp @@ -138,6 +138,13 @@ uri_ptr get_uri_from_host(request_type & request, uri::type scheme, bool secure) size_t last_colon = h.rfind(":"); size_t last_sbrace = h.rfind("]"); + std::string port, hostname; + if (last_sbrace != std::string::npos) + { + hostname = h.substr(1, last_sbrace - 1); + } else { + hostname = h.substr(0, last_colon); + } // no : = hostname with no port // last : before ] = ipv6 literal with no port @@ -146,10 +153,10 @@ uri_ptr get_uri_from_host(request_type & request, uri::type scheme, bool secure) if (last_colon == std::string::npos || (last_sbrace != std::string::npos && last_sbrace > last_colon)) { - return lib::make_shared(scheme, secure, h, request.get_uri()); + return lib::make_shared(scheme, secure, hostname, request.get_uri()); } else { return lib::make_shared(scheme, secure, - h.substr(0,last_colon), + hostname, h.substr(last_colon+1), request.get_uri()); } diff --git a/websocketpp/uri.hpp b/websocketpp/uri.hpp index b055d9843..4b30c6622 100644 --- a/websocketpp/uri.hpp +++ b/websocketpp/uri.hpp @@ -297,6 +297,7 @@ inline bool hex4(std::string::const_iterator start, std::string::const_iterator */ inline bool ipv6_literal(std::string::const_iterator start, std::string::const_iterator end) { // initial range check + end = std::find(start, end, '%'); if (end-start > 45 && end-start >= 2) { return false; } From de9b25c3735f66d2a15db10cc3e6d274da5313a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20To=C5=A1kan?= Date: Tue, 27 Aug 2024 13:02:16 +0200 Subject: [PATCH 38/40] Better hostname resolution errors --- websocketpp/transport/asio/endpoint.hpp | 17 ++++++----------- websocketpp/transport/base/connection.hpp | 5 +++++ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/websocketpp/transport/asio/endpoint.hpp b/websocketpp/transport/asio/endpoint.hpp index e109c5295..03ccbb8b2 100644 --- a/websocketpp/transport/asio/endpoint.hpp +++ b/websocketpp/transport/asio/endpoint.hpp @@ -912,21 +912,16 @@ class endpoint : public config::socket_type { port = pu->get_port_str(); } - tcp::resolver::query query(host,port); - if (m_alog->static_test(log::alevel::devel)) { m_alog->write(log::alevel::devel, "starting async DNS resolve for "+host+":"+port); } - timer_ptr dns_timer; - - dns_timer = tcon->set_timer( + timer_ptr dns_timer = tcon->set_timer( config::timeout_dns_resolve, lib::bind( &type::handle_resolve_timeout, this, - dns_timer, cb, lib::placeholders::_1 ) @@ -934,7 +929,7 @@ class endpoint : public config::socket_type { if (config::enable_multithreading) { m_resolver->async_resolve( - query, + host, port, tcon->get_strand()->wrap(lib::bind( &type::handle_resolve, this, @@ -947,7 +942,7 @@ class endpoint : public config::socket_type { ); } else { m_resolver->async_resolve( - query, + host, port, lib::bind( &type::handle_resolve, this, @@ -970,7 +965,7 @@ class endpoint : public config::socket_type { * @param callback The function to call back * @param ec A status code indicating an error, if any. */ - void handle_resolve_timeout(timer_ptr, connect_handler callback, + void handle_resolve_timeout(connect_handler callback, lib::error_code const & ec) { lib::error_code ret_ec; @@ -985,7 +980,7 @@ class endpoint : public config::socket_type { log_err(log::elevel::devel,"asio handle_resolve_timeout",ec); ret_ec = ec; } else { - ret_ec = make_error_code(transport::error::timeout); + ret_ec = make_error_code(transport::error::resolve_failed); } m_alog->write(log::alevel::devel,"DNS resolution timed out"); @@ -993,7 +988,7 @@ class endpoint : public config::socket_type { callback(ret_ec); } - void handle_resolve(transport_con_ptr tcon, timer_ptr dns_timer, + void handle_resolve(transport_con_ptr tcon, const timer_ptr& dns_timer, connect_handler callback, lib::asio::error_code const & ec, lib::asio::ip::tcp::resolver::iterator iterator) { diff --git a/websocketpp/transport/base/connection.hpp b/websocketpp/transport/base/connection.hpp index 0e0618cd2..0967c6469 100644 --- a/websocketpp/transport/base/connection.hpp +++ b/websocketpp/transport/base/connection.hpp @@ -173,6 +173,9 @@ enum value { /// Timer expired timeout, + /// Hostname resolution failed or timed out + resolve_failed, + /// read or write after shutdown action_after_shutdown, @@ -206,6 +209,8 @@ class category : public lib::error_category { return "TLS Short Read"; case timeout: return "Timer Expired"; + case resolve_failed: + return "Hostname resolution failed or timed out"; case action_after_shutdown: return "A transport action was requested after shutdown"; case tls_error: From b0ac95300d1edcdf64f22b00042e947151e9fc3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20To=C5=A1kan?= Date: Thu, 24 Oct 2024 11:06:31 +0200 Subject: [PATCH 39/40] fix(websocketpp): Bump cmake minimum version as older versions will be unsupported --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3acb3bcf1..5fefcbf01 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ ############ Setup project and cmake # Minimum cmake requirement. We should require a quite recent # cmake for the dependency find macros etc. to be up to date. -cmake_minimum_required (VERSION 3.0.0) +cmake_minimum_required (VERSION 3.5) project(websocketpp VERSION 0.9.0) ############ Paths From 259b5a77cee28bf5468b8cb5e944785118f34a3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20To=C5=A1kan?= Date: Thu, 30 Jan 2025 16:48:56 +0100 Subject: [PATCH 40/40] feat(websockets): Add utility function to append resource to URI (NOJIRA) --- websocketpp/uri.hpp | 49 +++++++++++++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/websocketpp/uri.hpp b/websocketpp/uri.hpp index 4b30c6622..aed61238f 100644 --- a/websocketpp/uri.hpp +++ b/websocketpp/uri.hpp @@ -262,7 +262,7 @@ inline bool ipv4_literal(std::string::const_iterator start, std::string::const_i } } } - + // check final octet return (counter == 3 && dec_octet(cursor,end)); } @@ -301,7 +301,7 @@ inline bool ipv6_literal(std::string::const_iterator start, std::string::const_i if (end-start > 45 && end-start >= 2) { return false; } - + // peal off and count hex4s until we run out of colons, // note the abbreviation marker if we see one. std::string::const_iterator cursor = start; @@ -327,7 +327,7 @@ inline bool ipv6_literal(std::string::const_iterator start, std::string::const_i } it++; } - + // final bit either needs to be a hex4 or an IPv4 literal if (cursor == end) { // fine @@ -338,11 +338,11 @@ inline bool ipv6_literal(std::string::const_iterator start, std::string::const_i } else { return false; } - + if ((abbr == 0 && count != 8) || (abbr == 1 && count > 7) || abbr > 1) { return false; } - + return true; } @@ -408,10 +408,10 @@ class uri { uri() = default; explicit uri(std::string const & uri_string) : m_ipv6_literal(false) { - + if (uri_string.empty()) return; - + const size_t schema_end = uri_string.find(uri_helper::schema_separtator); if (schema_end != std::string::npos) { @@ -492,8 +492,8 @@ class uri { } else if (*it == '/' || *it == '?' || *it == '#') { // todo: better path parsing state = 2; - - // we don't increment the iterator here because we want the + + // we don't increment the iterator here because we want the // delimiter to be read again as a part of the path } else if (*it == ':') { state = 1; @@ -532,7 +532,7 @@ class uri { // end hostname and start parsing path state = 2; - // we don't increment the iterator here because we want the + // we don't increment the iterator here because we want the // delimiter to be read again as a part of the path } else { // either @, [, or ] @@ -545,7 +545,7 @@ class uri { m_host += *it; ++it; } - + } } @@ -572,10 +572,10 @@ class uri { } state = 3; - // we don't increment the iterator here because we want the + // we don't increment the iterator here because we want the // delimiter to be read again as a part of the path } - + } lib::error_code ec; @@ -588,7 +588,7 @@ class uri { // step back one so the first char of the path delimiter doesn't get eaten m_resource.append(it,uri_string.end()); - + if (m_resource.empty()) { m_resource = "/"; } @@ -629,6 +629,25 @@ class uri { m_type = invalid; } + uri resource(const std::string& resource) const + { + uri ret = *this; + if (ret.get_resource().ends_with('/')) + { + if (resource.starts_with('/')) + ret.m_resource += resource.substr(1); + else + ret.m_resource += resource; + }else + { + if (resource.starts_with('/')) + ret.m_resource += resource; + else + ret.m_resource += "/" + resource; + } + return ret; + } + bool get_valid() const { return m_type != invalid; } @@ -677,7 +696,7 @@ class uri { } else { p << m_host << ":" << m_port; } - + return p.str(); } }