From 9bb337fb1f0d702083125dc586941e2ac38aded6 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Mon, 27 Feb 2017 12:10:52 -0500 Subject: [PATCH] Set Beast version to 1.0.0-b30: Squashed 'src/beast/' changes from 9f10b11..1b9a714 1b9a714 Set version to 1.0.0-b30 faed9e5 Allow concurrent websocket async ping and writes: 31cda06 Fix race when write suspends 48dd38e Fix race in close frames during reads e2d1bb0 Fix race in pings during reads 36143be Set version to 1.0.0-b29 f0399b6 Fix doc link typo 787b7c2 Check ostream modifier correctly 4fa0bf6 Fix Writer return value documentation 6406da0 Document type-pun in buffer_cat 66cdb37 Fix illegal HTTP characters accepted as hex zero e64ca2f Fix Body requirements doc 6dfd9f9 Fix compilation error in non-template class fa7fea8 Fix race in writes during reads: git-subtree-dir: src/beast git-subtree-split: 1b9a71483347b7027b2fb7fe27ecea148d2e79ba --- src/beast/CHANGELOG.md | 30 +++++ src/beast/doc/examples.qbk | 2 +- src/beast/doc/types/Body.qbk | 4 +- src/beast/doc/types/Writer.qbk | 3 +- src/beast/doc/websocket.qbk | 4 +- src/beast/extras/beast/unit_test/dstream.hpp | 2 +- src/beast/extras/beast/unit_test/reporter.hpp | 1 - src/beast/extras/beast/unit_test/suite.hpp | 4 - .../include/beast/core/detail/buffer_cat.hpp | 2 + .../beast/http/detail/basic_fields.hpp | 8 +- .../include/beast/http/detail/rfc7230.hpp | 93 +++++++++----- src/beast/include/beast/version.hpp | 2 +- .../beast/websocket/detail/invokable.hpp | 5 +- .../beast/websocket/detail/stream_base.hpp | 5 +- .../include/beast/websocket/impl/close.ipp | 17 ++- .../include/beast/websocket/impl/ping.ipp | 19 ++- .../include/beast/websocket/impl/read.ipp | 36 ++++-- .../include/beast/websocket/impl/write.ipp | 114 ++++++++++++------ src/beast/include/beast/websocket/stream.hpp | 8 ++ 19 files changed, 258 insertions(+), 101 deletions(-) diff --git a/src/beast/CHANGELOG.md b/src/beast/CHANGELOG.md index 0b913b491b..383491ba70 100644 --- a/src/beast/CHANGELOG.md +++ b/src/beast/CHANGELOG.md @@ -1,3 +1,33 @@ +1.0.0-b30 + +WebSocket + +* Fix race in pings during reads +* Fix race in close frames during reads +* Fix race when write suspends +* Allow concurrent websocket async ping and writes + +-------------------------------------------------------------------------------- + +1.0.0-b29 + +* Fix compilation error in non-template class +* Document type-pun in buffer_cat +* Correctly check ostream modifier (/extras) + +HTTP + +* Fix Body requirements doc +* Fix illegal HTTP characters accepted as hex zero +* Fix Writer return value documentation + +WebSocket + +* Fix race in writes during reads +* Fix doc link typo + +-------------------------------------------------------------------------------- + 1.0.0-b28 * Split out and rename test stream classes diff --git a/src/beast/doc/examples.qbk b/src/beast/doc/examples.qbk index 418215e258..224f4d9fdc 100644 --- a/src/beast/doc/examples.qbk +++ b/src/beast/doc/examples.qbk @@ -91,7 +91,7 @@ This example demonstrates both synchronous and asynchronous WebSocket server implementations. * [@examples/websocket_async_echo_server.hpp] -* [@examples/websocket_ssync_echo_server.hpp] +* [@examples/websocket_sync_echo_server.hpp] * [@examples/websocket_echo.cpp] [heading Secure WebSocket] diff --git a/src/beast/doc/types/Body.qbk b/src/beast/doc/types/Body.qbk index e0099d92ae..b539fae655 100644 --- a/src/beast/doc/types/Body.qbk +++ b/src/beast/doc/types/Body.qbk @@ -27,7 +27,7 @@ In this table: ] ] [ - [`Body::reader`] + [`X::reader`] [] [ If present, a type meeting the requirements of @@ -36,7 +36,7 @@ In this table: ] ] [ - [`Body::writer`] + [`X::writer`] [] [ If present, a type meeting the requirements of diff --git a/src/beast/doc/types/Writer.qbk b/src/beast/doc/types/Writer.qbk index 3015b238fc..90d4e6e396 100644 --- a/src/beast/doc/types/Writer.qbk +++ b/src/beast/doc/types/Writer.qbk @@ -167,7 +167,8 @@ public: the writer must guarantee that the buffers remain valid until the next member function is invoked, which may be the destructor. - @return `true` if there is data, `false` when done, + @return `true` if there is no more data to send, + `false` when there may be more data, boost::indeterminate to suspend. @note Undefined behavior if the callee takes ownership diff --git a/src/beast/doc/websocket.qbk b/src/beast/doc/websocket.qbk index ce989f042d..77bcc2bde0 100644 --- a/src/beast/doc/websocket.qbk +++ b/src/beast/doc/websocket.qbk @@ -320,8 +320,8 @@ operations can cause socket writes. However, these writes will not compete with caller-initiated write operations. For the purposes of correctness with respect to the stream invariants, caller-initiated read operations still only count as a read. This means that callers can -have a simultaneous active read and write operation in progress, while -the implementation also automatically handles control frames. +have a simultaneously active read, write, and ping operation in progress, +while the implementation also automatically handles control frames. [heading Ping and Pong Frames] diff --git a/src/beast/extras/beast/unit_test/dstream.hpp b/src/beast/extras/beast/unit_test/dstream.hpp index c62c8060b9..44eb599beb 100644 --- a/src/beast/extras/beast/unit_test/dstream.hpp +++ b/src/beast/extras/beast/unit_test/dstream.hpp @@ -111,7 +111,7 @@ public: : std::basic_ostream(&buf_) , buf_(os) { - if(os.flags() && std::ios::unitbuf) + if(os.flags() & std::ios::unitbuf) std::unitbuf(*this); } }; diff --git a/src/beast/extras/beast/unit_test/reporter.hpp b/src/beast/extras/beast/unit_test/reporter.hpp index 771a76df9c..cb2e331772 100644 --- a/src/beast/extras/beast/unit_test/reporter.hpp +++ b/src/beast/extras/beast/unit_test/reporter.hpp @@ -283,7 +283,6 @@ reporter<_>:: on_log(std::string const& s) { os_ << s; - os_.flush(); } } // detail diff --git a/src/beast/extras/beast/unit_test/suite.hpp b/src/beast/extras/beast/unit_test/suite.hpp index 721f49a446..1120daf41c 100644 --- a/src/beast/extras/beast/unit_test/suite.hpp +++ b/src/beast/extras/beast/unit_test/suite.hpp @@ -352,10 +352,7 @@ public: { auto const& name = ss_.str(); if(! name.empty()) - { - suite_.log.flush(); suite_.runner_->testcase(name); - } } scoped_testcase(suite& self, std::stringstream& ss) @@ -394,7 +391,6 @@ suite::testcase_t::operator()( std::string const& name, abort_t abort) { suite_.abort_ = abort == abort_on_fail; - suite_.log.flush(); suite_.runner_->testcase(name); } diff --git a/src/beast/include/beast/core/detail/buffer_cat.hpp b/src/beast/include/beast/core/detail/buffer_cat.hpp index b5dcd4aaf2..60d840c220 100644 --- a/src/beast/include/beast/core/detail/buffer_cat.hpp +++ b/src/beast/include/beast/core/detail/buffer_cat.hpp @@ -82,6 +82,7 @@ class buffer_cat_helper::const_iterator iter_t& iter() { + // type-pun return *reinterpret_cast< iter_t*>(static_cast( buf_.data())); @@ -91,6 +92,7 @@ class buffer_cat_helper::const_iterator iter_t const& iter() const { + // type-pun return *reinterpret_cast< iter_t const*>(static_cast< void const*>(buf_.data())); diff --git a/src/beast/include/beast/http/detail/basic_fields.hpp b/src/beast/include/beast/http/detail/basic_fields.hpp index 3470c88fc5..1b296df3b6 100644 --- a/src/beast/include/beast/http/detail/basic_fields.hpp +++ b/src/beast/include/beast/http/detail/basic_fields.hpp @@ -94,11 +94,11 @@ protected: } }; - using list_t = typename boost::intrusive::make_list< - element, boost::intrusive::constant_time_size>::type; + using list_t = boost::intrusive::make_list>::type; - using set_t = typename boost::intrusive::make_multiset< - element, boost::intrusive::constant_time_size, + using set_t = boost::intrusive::make_multiset, boost::intrusive::compare>::type; // data diff --git a/src/beast/include/beast/http/detail/rfc7230.hpp b/src/beast/include/beast/http/detail/rfc7230.hpp index 942a9e9f91..a68b4da835 100644 --- a/src/beast/include/beast/http/detail/rfc7230.hpp +++ b/src/beast/include/beast/http/detail/rfc7230.hpp @@ -9,7 +9,6 @@ #define BEAST_HTTP_DETAIL_RFC7230_HPP #include -#include #include #include @@ -25,10 +24,10 @@ is_digit(char c) } inline -bool +char is_alpha(char c) { - static std::array constexpr tab = {{ + static char constexpr tab[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 32 @@ -37,16 +36,25 @@ is_alpha(char c) 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 80 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 112 - }}; - return tab[static_cast(c)] != 0; + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 128 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 144 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 160 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 176 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 192 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 208 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 224 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 240 + }; + static_assert(sizeof(tab) == 256, ""); + return tab[static_cast(c)]; } inline -bool +char is_text(char c) { // TEXT = - static std::array constexpr tab = {{ + static char constexpr tab[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, // 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 32 @@ -63,12 +71,13 @@ is_text(char c) 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 208 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240 - }}; - return tab[static_cast(c)] != 0; + }; + static_assert(sizeof(tab) == 256, ""); + return tab[static_cast(c)]; } inline -bool +char is_tchar(char c) { /* @@ -77,7 +86,7 @@ is_tchar(char c) "^" | "_" | "`" | "|" | "~" | DIGIT | ALPHA */ - static std::array constexpr tab = {{ + static char constexpr tab[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, // 32 @@ -86,18 +95,27 @@ is_tchar(char c) 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, // 80 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, // 112 - }}; - return tab[static_cast(c)] != 0; + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 128 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 144 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 160 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 176 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 192 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 208 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 224 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 240 + }; + static_assert(sizeof(tab) == 256, ""); + return tab[static_cast(c)]; } inline -bool +char is_qdchar(char c) { /* qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text */ - static std::array constexpr tab = {{ + static char constexpr tab[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, // 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 32 @@ -114,19 +132,20 @@ is_qdchar(char c) 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 208 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240 - }}; + }; + static_assert(sizeof(tab) == 256, ""); return tab[static_cast(c)]; } inline -bool +char is_qpchar(char c) { /* quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text ) obs-text = %x80-FF */ - static std::array constexpr tab = {{ + static char constexpr tab[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, // 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 32 @@ -143,7 +162,8 @@ is_qpchar(char c) 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 208 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240 - }}; + }; + static_assert(sizeof(tab) == 256, ""); return tab[static_cast(c)]; } @@ -161,7 +181,7 @@ to_field_char(char c) | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT */ - static std::array constexpr tab = {{ + static char constexpr tab[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '!', 0, '#', '$', '%', '&', '\'', 0, 0, '*', '+', 0, '-', '.', 0, @@ -169,8 +189,17 @@ to_field_char(char c) 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', - 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, '|', 0, '~', 0 - }}; + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, '|', 0, '~', 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + static_assert(sizeof(tab) == 256, ""); return tab[static_cast(c)]; } @@ -182,7 +211,7 @@ char to_value_char(char c) { // TEXT = - static std::array constexpr tab = {{ + static unsigned char constexpr tab[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, // 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, // 32 @@ -199,7 +228,8 @@ to_value_char(char c) 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, // 208 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, // 224 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 // 240 - }}; + }; + static_assert(sizeof(tab) == 256, ""); return static_cast(tab[static_cast(c)]); } @@ -207,7 +237,7 @@ inline std::int8_t unhex(char c) { - static std::array constexpr tab = {{ + static char constexpr tab[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 16 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 32 @@ -215,8 +245,17 @@ unhex(char c) -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 64 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 80 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 96 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // 112 - }}; + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 112 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 128 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 144 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 160 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 176 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 192 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 208 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 224 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // 240 + }; + static_assert(sizeof(tab) == 256, ""); return tab[static_cast(c)]; } diff --git a/src/beast/include/beast/version.hpp b/src/beast/include/beast/version.hpp index 42c085c0fe..f91d01a9d7 100644 --- a/src/beast/include/beast/version.hpp +++ b/src/beast/include/beast/version.hpp @@ -16,6 +16,6 @@ // #define BEAST_VERSION 100000 -#define BEAST_VERSION_STRING "1.0.0-b28" +#define BEAST_VERSION_STRING "1.0.0-b30" #endif diff --git a/src/beast/include/beast/websocket/detail/invokable.hpp b/src/beast/include/beast/websocket/detail/invokable.hpp index 62e62492fd..8070108321 100644 --- a/src/beast/include/beast/websocket/detail/invokable.hpp +++ b/src/beast/include/beast/websocket/detail/invokable.hpp @@ -98,6 +98,7 @@ public: { if(other.base_) { + // type-pun base_ = reinterpret_cast(&buf_[0]); other.base_->move(buf_); other.base_ = nullptr; @@ -109,11 +110,12 @@ public: { // Engaged invokables must be invoked before // assignment otherwise the io_service - // invariants are broken w.r.t completions. + // completion invariants are broken. BOOST_ASSERT(! base_); if(other.base_) { + // type-pun base_ = reinterpret_cast(&buf_[0]); other.base_->move(buf_); other.base_ = nullptr; @@ -147,6 +149,7 @@ invokable::emplace(F&& f) "buffer too small"); BOOST_ASSERT(! base_); ::new(buf_) holder(std::forward(f)); + // type-pun base_ = reinterpret_cast(&buf_[0]); } diff --git a/src/beast/include/beast/websocket/detail/stream_base.hpp b/src/beast/include/beast/websocket/detail/stream_base.hpp index 0a1e267975..6cd6731fba 100644 --- a/src/beast/include/beast/websocket/detail/stream_base.hpp +++ b/src/beast/include/beast/websocket/detail/stream_base.hpp @@ -67,8 +67,9 @@ protected: op* wr_block_; // op currenly writing ping_data* ping_data_; // where to put the payload - invokable rd_op_; // invoked after write completes - invokable wr_op_; // invoked after read completes + invokable rd_op_; // read parking + invokable wr_op_; // write parking + invokable ping_op_; // ping parking close_reason cr_; // set from received close frame // State information for the message being received diff --git a/src/beast/include/beast/websocket/impl/close.ipp b/src/beast/include/beast/websocket/impl/close.ipp index 0ad5992b9f..983126f924 100644 --- a/src/beast/include/beast/websocket/impl/close.ipp +++ b/src/beast/include/beast/websocket/impl/close.ipp @@ -147,25 +147,33 @@ operator()(error_code ec, bool again) boost::asio::error::operation_aborted)); return; } - // fall through + d.ws.wr_block_ = &d; + // [[fallthrough]] case 1: // send close frame + BOOST_ASSERT(d.ws.wr_block_ == &d); d.state = 99; d.ws.wr_close_ = true; - BOOST_ASSERT(! d.ws.wr_block_); - d.ws.wr_block_ = &d; boost::asio::async_write(d.ws.stream_, d.fb.data(), std::move(*this)); return; case 2: + BOOST_ASSERT(! d.ws.wr_block_); + d.ws.wr_block_ = &d; d.state = 3; + // The current context is safe but might not be + // the same as the one for this operation (since + // we are being called from a write operation). + // Call post to make sure we are invoked the same + // way as the final handler for this operation. d.ws.get_io_service().post( bind_handler(std::move(*this), ec)); return; case 3: + BOOST_ASSERT(d.ws.wr_block_ == &d); if(d.ws.failed_ || d.ws.wr_close_) { // call handler @@ -182,7 +190,8 @@ operator()(error_code ec, bool again) upcall: if(d.ws.wr_block_ == &d) d.ws.wr_block_ = nullptr; - d.ws.rd_op_.maybe_invoke(); + d.ws.rd_op_.maybe_invoke() || + d.ws.ping_op_.maybe_invoke(); d_.invoke(ec); } diff --git a/src/beast/include/beast/websocket/impl/ping.ipp b/src/beast/include/beast/websocket/impl/ping.ipp index 73990cce51..59071376c9 100644 --- a/src/beast/include/beast/websocket/impl/ping.ipp +++ b/src/beast/include/beast/websocket/impl/ping.ipp @@ -133,7 +133,7 @@ operator()(error_code ec, bool again) { // suspend d.state = 2; - d.ws.wr_op_.template emplace< + d.ws.ping_op_.template emplace< ping_op>(std::move(*this)); return; } @@ -146,24 +146,32 @@ operator()(error_code ec, bool again) boost::asio::error::operation_aborted)); return; } - // fall through + d.ws.wr_block_ = &d; + // [[fallthrough]] case 1: // send ping frame + BOOST_ASSERT(d.ws.wr_block_ == &d); d.state = 99; - BOOST_ASSERT(! d.ws.wr_block_); - d.ws.wr_block_ = &d; boost::asio::async_write(d.ws.stream_, d.fb.data(), std::move(*this)); return; case 2: + BOOST_ASSERT(! d.ws.wr_block_); + d.ws.wr_block_ = &d; d.state = 3; + // The current context is safe but might not be + // the same as the one for this operation (since + // we are being called from a write operation). + // Call post to make sure we are invoked the same + // way as the final handler for this operation. d.ws.get_io_service().post( bind_handler(std::move(*this), ec)); return; case 3: + BOOST_ASSERT(d.ws.wr_block_ == &d); if(d.ws.failed_ || d.ws.wr_close_) { // call handler @@ -180,7 +188,8 @@ operator()(error_code ec, bool again) upcall: if(d.ws.wr_block_ == &d) d.ws.wr_block_ = nullptr; - d.ws.rd_op_.maybe_invoke(); + d.ws.rd_op_.maybe_invoke() || + d.ws.wr_op_.maybe_invoke(); d_.invoke(ec); } diff --git a/src/beast/include/beast/websocket/impl/read.ipp b/src/beast/include/beast/websocket/impl/read.ipp index 6e65beea6d..8c715de5ec 100644 --- a/src/beast/include/beast/websocket/impl/read.ipp +++ b/src/beast/include/beast/websocket/impl/read.ipp @@ -499,6 +499,8 @@ operator()(error_code ec, //------------------------------------------------------------------ case do_pong_resume: + BOOST_ASSERT(! d.ws.wr_block_); + d.ws.wr_block_ = &d; d.state = do_pong_resume + 1; d.ws.get_io_service().post(bind_handler( std::move(*this), ec, bytes_transferred)); @@ -511,8 +513,7 @@ operator()(error_code ec, ec = boost::asio::error::operation_aborted; goto upcall; } - d.state = do_pong; - break; // VFALCO fall through? + // [[fallthrough]] //------------------------------------------------------------------ @@ -520,14 +521,21 @@ operator()(error_code ec, if(d.ws.wr_close_) { // ignore ping when closing + if(d.ws.wr_block_) + { + BOOST_ASSERT(d.ws.wr_block_ == &d); + d.ws.wr_block_ = nullptr; + } d.fb.reset(); d.state = do_read_fh; break; } // send pong + if(! d.ws.wr_block_) + d.ws.wr_block_ = &d; + else + BOOST_ASSERT(d.ws.wr_block_ == &d); d.state = do_pong + 1; - BOOST_ASSERT(! d.ws.wr_block_); - d.ws.wr_block_ = &d; boost::asio::async_write(d.ws.stream_, d.fb.data(), std::move(*this)); return; @@ -541,18 +549,25 @@ operator()(error_code ec, //------------------------------------------------------------------ case do_close_resume: + BOOST_ASSERT(! d.ws.wr_block_); + d.ws.wr_block_ = &d; d.state = do_close_resume + 1; + // The current context is safe but might not be + // the same as the one for this operation (since + // we are being called from a write operation). + // Call post to make sure we are invoked the same + // way as the final handler for this operation. d.ws.get_io_service().post(bind_handler( std::move(*this), ec, bytes_transferred)); return; case do_close_resume + 1: + BOOST_ASSERT(d.ws.wr_block_ == &d); if(d.ws.failed_) { // call handler - d.state = do_call_handler; ec = boost::asio::error::operation_aborted; - break; + goto upcall; } if(d.ws.wr_close_) { @@ -566,10 +581,12 @@ operator()(error_code ec, //------------------------------------------------------------------ case do_close: + if(! d.ws.wr_block_) + d.ws.wr_block_ = &d; + else + BOOST_ASSERT(d.ws.wr_block_ == &d); d.state = do_teardown; d.ws.wr_close_ = true; - BOOST_ASSERT(! d.ws.wr_block_); - d.ws.wr_block_ = &d; boost::asio::async_write(d.ws.stream_, d.fb.data(), std::move(*this)); return; @@ -656,7 +673,8 @@ operator()(error_code ec, upcall: if(d.ws.wr_block_ == &d) d.ws.wr_block_ = nullptr; - d.ws.wr_op_.maybe_invoke(); + d.ws.ping_op_.maybe_invoke() || + d.ws.wr_op_.maybe_invoke(); d_.invoke(ec); } diff --git a/src/beast/include/beast/websocket/impl/write.ipp b/src/beast/include/beast/websocket/impl/write.ipp index a278bbd565..4b0106788d 100644 --- a/src/beast/include/beast/websocket/impl/write.ipp +++ b/src/beast/include/beast/websocket/impl/write.ipp @@ -42,7 +42,7 @@ class stream::write_frame_op detail::prepared_key key; std::uint64_t remain; int state = 0; - int entry; + int entry_state; data(Handler& handler_, stream& ws_, bool fin_, Buffers const& bs) @@ -179,40 +179,44 @@ operator()(error_code ec, d.fh.mask = d.ws.role_ == detail::role_type::client; + // entry_state determines which algorithm + // we will use to send. If we suspend, we + // will transition to entry_state + 1 on + // the resume. if(d.ws.wr_.compress) { - d.entry = do_deflate; + d.entry_state = do_deflate; } else if(! d.fh.mask) { if(! d.ws.wr_.autofrag) { - d.entry = do_nomask_nofrag; + d.entry_state = do_nomask_nofrag; } else { BOOST_ASSERT(d.ws.wr_.buf_size != 0); d.remain = buffer_size(d.cb); if(d.remain > d.ws.wr_.buf_size) - d.entry = do_nomask_frag; + d.entry_state = do_nomask_frag; else - d.entry = do_nomask_nofrag; + d.entry_state = do_nomask_nofrag; } } else { if(! d.ws.wr_.autofrag) { - d.entry = do_mask_nofrag; + d.entry_state = do_mask_nofrag; } else { BOOST_ASSERT(d.ws.wr_.buf_size != 0); d.remain = buffer_size(d.cb); if(d.remain > d.ws.wr_.buf_size) - d.entry = do_mask_frag; + d.entry_state = do_mask_frag; else - d.entry = do_mask_nofrag; + d.entry_state = do_mask_nofrag; } } d.state = do_maybe_suspend; @@ -221,7 +225,13 @@ operator()(error_code ec, //---------------------------------------------------------------------- case do_nomask_nofrag: + BOOST_ASSERT(! d.ws.wr_block_); + d.ws.wr_block_ = &d; + // [[fallthrough]] + + case do_nomask_nofrag + 1: { + BOOST_ASSERT(d.ws.wr_block_ == &d); d.fh.fin = d.fin; d.fh.len = buffer_size(d.cb); detail::write( @@ -229,8 +239,6 @@ operator()(error_code ec, d.ws.wr_.cont = ! d.fin; // Send frame d.state = do_upcall; - BOOST_ASSERT(! d.ws.wr_block_); - d.ws.wr_block_ = &d; boost::asio::async_write(d.ws.stream_, buffer_cat(d.fh_buf.data(), d.cb), std::move(*this)); @@ -240,7 +248,13 @@ operator()(error_code ec, //---------------------------------------------------------------------- case do_nomask_frag: + BOOST_ASSERT(! d.ws.wr_block_); + d.ws.wr_block_ = &d; + // [[fallthrough]] + + case do_nomask_frag + 1: { + BOOST_ASSERT(d.ws.wr_block_ == &d); auto const n = clamp( d.remain, d.ws.wr_.buf_size); d.remain -= n; @@ -251,9 +265,7 @@ operator()(error_code ec, d.ws.wr_.cont = ! d.fin; // Send frame d.state = d.remain == 0 ? - do_upcall : do_nomask_frag + 1; - BOOST_ASSERT(! d.ws.wr_block_); - d.ws.wr_block_ = &d; + do_upcall : do_nomask_frag + 2; boost::asio::async_write(d.ws.stream_, buffer_cat(d.fh_buf.data(), prepare_buffers(n, d.cb)), @@ -261,27 +273,36 @@ operator()(error_code ec, return; } - case do_nomask_frag + 1: + case do_nomask_frag + 2: d.cb.consume( bytes_transferred - d.fh_buf.size()); d.fh_buf.reset(); d.fh.op = opcode::cont; if(d.ws.wr_block_ == &d) d.ws.wr_block_ = nullptr; - if(d.ws.rd_op_.maybe_invoke()) + // Allow outgoing control frames to + // be sent in between message frames: + if(d.ws.rd_op_.maybe_invoke() || + d.ws.ping_op_.maybe_invoke()) { d.state = do_maybe_suspend; d.ws.get_io_service().post( std::move(*this)); return; } - d.state = d.entry; + d.state = d.entry_state; break; //---------------------------------------------------------------------- case do_mask_nofrag: + BOOST_ASSERT(! d.ws.wr_block_); + d.ws.wr_block_ = &d; + // [[fallthrough]] + + case do_mask_nofrag + 1: { + BOOST_ASSERT(d.ws.wr_block_ == &d); d.remain = buffer_size(d.cb); d.fh.fin = d.fin; d.fh.len = d.remain; @@ -299,16 +320,14 @@ operator()(error_code ec, d.ws.wr_.cont = ! d.fin; // Send frame header and partial payload d.state = d.remain == 0 ? - do_upcall : do_mask_nofrag + 1; - BOOST_ASSERT(! d.ws.wr_block_); - d.ws.wr_block_ = &d; + do_upcall : do_mask_nofrag + 2; boost::asio::async_write(d.ws.stream_, buffer_cat(d.fh_buf.data(), b), std::move(*this)); return; } - case do_mask_nofrag + 1: + case do_mask_nofrag + 2: { d.cb.consume(d.ws.wr_.buf_size); auto const n = @@ -329,7 +348,13 @@ operator()(error_code ec, //---------------------------------------------------------------------- case do_mask_frag: + BOOST_ASSERT(! d.ws.wr_block_); + d.ws.wr_block_ = &d; + // [[fallthrough]] + + case do_mask_frag + 1: { + BOOST_ASSERT(d.ws.wr_block_ == &d); auto const n = clamp( d.remain, d.ws.wr_.buf_size); d.remain -= n; @@ -346,36 +371,43 @@ operator()(error_code ec, d.ws.wr_.cont = ! d.fin; // Send frame d.state = d.remain == 0 ? - do_upcall : do_mask_frag + 1; - BOOST_ASSERT(! d.ws.wr_block_); - d.ws.wr_block_ = &d; + do_upcall : do_mask_frag + 2; boost::asio::async_write(d.ws.stream_, buffer_cat(d.fh_buf.data(), b), std::move(*this)); return; } - case do_mask_frag + 1: + case do_mask_frag + 2: d.cb.consume( bytes_transferred - d.fh_buf.size()); d.fh_buf.reset(); d.fh.op = opcode::cont; BOOST_ASSERT(d.ws.wr_block_ == &d); d.ws.wr_block_ = nullptr; - if(d.ws.rd_op_.maybe_invoke()) + // Allow outgoing control frames to + // be sent in between message frames: + if(d.ws.rd_op_.maybe_invoke() || + d.ws.ping_op_.maybe_invoke()) { d.state = do_maybe_suspend; d.ws.get_io_service().post( std::move(*this)); return; } - d.state = d.entry; + d.state = d.entry_state; break; //---------------------------------------------------------------------- case do_deflate: + BOOST_ASSERT(! d.ws.wr_block_); + d.ws.wr_block_ = &d; + // [[fallthrough]] + + case do_deflate + 1: { + BOOST_ASSERT(d.ws.wr_block_ == &d); auto b = buffer(d.ws.wr_.buf.get(), d.ws.wr_.buf_size); auto const more = detail::deflate( @@ -414,31 +446,32 @@ operator()(error_code ec, d.ws.wr_.cont = ! d.fin; // Send frame d.state = more ? - do_deflate + 1 : do_deflate + 2; - BOOST_ASSERT(! d.ws.wr_block_); - d.ws.wr_block_ = &d; + do_deflate + 2 : do_deflate + 3; boost::asio::async_write(d.ws.stream_, buffer_cat(fh_buf.data(), b), std::move(*this)); return; } - case do_deflate + 1: + case do_deflate + 2: d.fh.op = opcode::cont; d.fh.rsv1 = false; BOOST_ASSERT(d.ws.wr_block_ == &d); d.ws.wr_block_ = nullptr; - if(d.ws.rd_op_.maybe_invoke()) + // Allow outgoing control frames to + // be sent in between message frames: + if(d.ws.rd_op_.maybe_invoke() || + d.ws.ping_op_.maybe_invoke()) { d.state = do_maybe_suspend; d.ws.get_io_service().post( std::move(*this)); return; } - d.state = d.entry; + d.state = d.entry_state; break; - case do_deflate + 2: + case do_deflate + 3: if(d.fh.fin && ( (d.ws.role_ == detail::role_type::client && d.ws.pmd_config_.client_no_context_takeover) || @@ -468,24 +501,32 @@ operator()(error_code ec, boost::asio::error::operation_aborted)); return; } - d.state = d.entry; + d.state = d.entry_state; break; } case do_maybe_suspend + 1: + BOOST_ASSERT(! d.ws.wr_block_); + d.ws.wr_block_ = &d; d.state = do_maybe_suspend + 2; + // The current context is safe but might not be + // the same as the one for this operation (since + // we are being called from a write operation). + // Call post to make sure we are invoked the same + // way as the final handler for this operation. d.ws.get_io_service().post(bind_handler( std::move(*this), ec)); return; case do_maybe_suspend + 2: + BOOST_ASSERT(d.ws.wr_block_ == &d); if(d.ws.failed_ || d.ws.wr_close_) { // call handler ec = boost::asio::error::operation_aborted; goto upcall; } - d.state = d.entry; + d.state = d.entry_state + 1; break; //---------------------------------------------------------------------- @@ -497,7 +538,8 @@ operator()(error_code ec, upcall: if(d.ws.wr_block_ == &d) d.ws.wr_block_ = nullptr; - d.ws.rd_op_.maybe_invoke(); + d.ws.rd_op_.maybe_invoke() || + d.ws.ping_op_.maybe_invoke(); d_.invoke(ec); } diff --git a/src/beast/include/beast/websocket/stream.hpp b/src/beast/include/beast/websocket/stream.hpp index 24fb129198..3a469c9048 100644 --- a/src/beast/include/beast/websocket/stream.hpp +++ b/src/beast/include/beast/websocket/stream.hpp @@ -985,6 +985,10 @@ public: composed operation. The program must ensure that the stream performs no other writes until this operation completes. + If a close frame is sent or received before the ping frame is + sent, the completion handler will be called with the error + set to `boost::asio::error::operation_aborted`. + @param payload The payload of the ping message, which may be empty. @param handler The handler to be called when the read operation @@ -1078,6 +1082,10 @@ public: order to send a pong. The remote peer may use the receipt of a pong frame as an indication that the connection is not dead. + If a close frame is sent or received before the pong frame is + sent, the completion handler will be called with the error + set to `boost::asio::error::operation_aborted`. + @param payload The payload of the pong message, which may be empty. @param handler The handler to be called when the read operation