diff --git a/CHANGELOG.md b/CHANGELOG.md index 63c797b6..a9b39300 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ WebSocket: * Redistribute the read tests in the translation units * Refactor error headers +* Add WebSocket error conditions -------------------------------------------------------------------------------- diff --git a/include/boost/beast/websocket/detail/error.hpp b/include/boost/beast/websocket/detail/error.hpp index 8c065ebd..7c3ee3e8 100644 --- a/include/boost/beast/websocket/detail/error.hpp +++ b/include/boost/beast/websocket/detail/error.hpp @@ -18,6 +18,7 @@ namespace boost { namespace beast { namespace websocket { enum class error; +enum class condition; } // websocket } // beast @@ -27,6 +28,11 @@ struct is_error_code_enum { static bool const value = true; }; +template<> +struct is_error_condition_enum +{ + static bool const value = true; +}; } // system namespace beast { @@ -37,7 +43,7 @@ class error_codes : public error_category { template string_view - get_message(error ev) const; + message(error e) const; public: const char* @@ -49,7 +55,43 @@ public: std::string message(int ev) const override { - return get_message(static_cast(ev)).to_string(); + return message( + static_cast(ev)).to_string(); + } +}; + +class error_conditions : public error_category +{ + template + string_view + message(condition c) const; + + template + bool + equivalent(error_code const& ec, + condition c) const noexcept; + +public: + const char* + name() const noexcept override + { + return "boost.beast.websocket"; + } + + std::string + message(int cv) const override + { + return message( + static_cast(cv)).to_string(); + } + + bool + equivalent( + error_code const& ec, + int cv) const noexcept + { + return equivalent(ec, + static_cast(cv)); } }; @@ -64,6 +106,15 @@ make_error_code(error e) std::underlying_type::type>(e), cat}; } +inline +error_condition +make_error_condition(condition c) +{ + static detail::error_conditions const cat{}; + return error_condition{static_cast< + std::underlying_type::type>(c), cat}; +} + } // websocket } // beast diff --git a/include/boost/beast/websocket/error.hpp b/include/boost/beast/websocket/error.hpp index 62b7fb09..03303428 100644 --- a/include/boost/beast/websocket/error.hpp +++ b/include/boost/beast/websocket/error.hpp @@ -37,6 +37,18 @@ enum class error partial_deflate_block }; +/// Error conditions corresponding to sets of error codes. +enum class condition +{ + /** Handshake failed + + This condition indicates that the WebSocket handshake failed. If + the corresponding HTTP response indicates the keep-alive behavior, + then the handshake may be reattempted. + */ + handshake_failed = 1, + }; + } // websocket } // beast } // boost diff --git a/include/boost/beast/websocket/impl/error.ipp b/include/boost/beast/websocket/impl/error.ipp index 33c68016..f5b78279 100644 --- a/include/boost/beast/websocket/impl/error.ipp +++ b/include/boost/beast/websocket/impl/error.ipp @@ -18,9 +18,9 @@ namespace detail { template string_view error_codes:: -get_message(error ev) const +message(error e) const { - switch(ev) + switch(e) { default: case error::failed: return "WebSocket connection failed due to a protocol violation"; @@ -31,6 +31,42 @@ get_message(error ev) const } } +template +string_view +error_conditions:: +message(condition c) const +{ + switch(c) + { + default: + case condition::handshake_failed: return "WebSocket upgrade handshake failed"; + } +} + +template +bool +error_conditions:: +equivalent( + error_code const& ec, + condition c) const noexcept +{ + if(ec.category() == error_code{error{}}.category()) + { + switch(c) + { + case condition::handshake_failed: + switch(static_cast(ec.value())) + { + case error::handshake_failed: + return true; + } + return false; + } + } + return false; +} + + } // detail } // websocket } // beast diff --git a/test/beast/websocket/error.cpp b/test/beast/websocket/error.cpp index 5852fa04..ff89071e 100644 --- a/test/beast/websocket/error.cpp +++ b/test/beast/websocket/error.cpp @@ -20,23 +20,28 @@ namespace websocket { class error_test : public unit_test::suite { public: - void check(error ev) + void check(error e) { - auto const ec = make_error_code(ev); + auto const ec = make_error_code(e); ec.category().name(); BEAST_EXPECT(! ec.message().empty()); #if 0 BEAST_EXPECT(std::addressof(ec.category()) == std::addressof(detail::get_error_category())); BEAST_EXPECT(detail::get_error_category().equivalent( - static_cast::type>(ev), + static_cast::type>(e), ec.category().default_error_condition( - static_cast::type>(ev)))); + static_cast::type>(e)))); BEAST_EXPECT(detail::get_error_category().equivalent( - ec, static_cast::type>(ev))); + ec, static_cast::type>(e))); #endif } + void check(error e, condition c) + { + BEAST_EXPECT(error_code{e} == c); + } + void run() override { check(error::closed); @@ -44,6 +49,8 @@ public: check(error::handshake_failed); check(error::buffer_overflow); check(error::partial_deflate_block); + + check(error::handshake_failed, condition::handshake_failed); } };