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
This commit is contained in:
Vinnie Falco
2017-02-27 12:10:52 -05:00
parent 460dd8f186
commit 9bb337fb1f
19 changed files with 258 additions and 101 deletions

View File

@@ -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

View File

@@ -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]

View File

@@ -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

View File

@@ -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

View File

@@ -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]

View File

@@ -111,7 +111,7 @@ public:
: std::basic_ostream<CharT, Traits>(&buf_)
, buf_(os)
{
if(os.flags() && std::ios::unitbuf)
if(os.flags() & std::ios::unitbuf)
std::unitbuf(*this);
}
};

View File

@@ -283,7 +283,6 @@ reporter<_>::
on_log(std::string const& s)
{
os_ << s;
os_.flush();
}
} // detail

View File

@@ -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);
}

View File

@@ -82,6 +82,7 @@ class buffer_cat_helper<Bn...>::const_iterator
iter_t<I>&
iter()
{
// type-pun
return *reinterpret_cast<
iter_t<I>*>(static_cast<void*>(
buf_.data()));
@@ -91,6 +92,7 @@ class buffer_cat_helper<Bn...>::const_iterator
iter_t<I> const&
iter() const
{
// type-pun
return *reinterpret_cast<
iter_t<I> const*>(static_cast<
void const*>(buf_.data()));

View File

@@ -94,11 +94,11 @@ protected:
}
};
using list_t = typename boost::intrusive::make_list<
element, boost::intrusive::constant_time_size<false>>::type;
using list_t = boost::intrusive::make_list<element,
boost::intrusive::constant_time_size<false>>::type;
using set_t = typename boost::intrusive::make_multiset<
element, boost::intrusive::constant_time_size<true>,
using set_t = boost::intrusive::make_multiset<element,
boost::intrusive::constant_time_size<true>,
boost::intrusive::compare<less>>::type;
// data

View File

@@ -9,7 +9,6 @@
#define BEAST_HTTP_DETAIL_RFC7230_HPP
#include <boost/utility/string_ref.hpp>
#include <array>
#include <iterator>
#include <utility>
@@ -25,10 +24,10 @@ is_digit(char c)
}
inline
bool
char
is_alpha(char c)
{
static std::array<char, 256> 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<std::uint8_t>(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<std::uint8_t>(c)];
}
inline
bool
char
is_text(char c)
{
// TEXT = <any OCTET except CTLs, but including LWS>
static std::array<char, 256> 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<std::uint8_t>(c)] != 0;
};
static_assert(sizeof(tab) == 256, "");
return tab[static_cast<std::uint8_t>(c)];
}
inline
bool
char
is_tchar(char c)
{
/*
@@ -77,7 +86,7 @@ is_tchar(char c)
"^" | "_" | "`" | "|" | "~" |
DIGIT | ALPHA
*/
static std::array<char, 256> 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<std::uint8_t>(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<std::uint8_t>(c)];
}
inline
bool
char
is_qdchar(char c)
{
/*
qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text
*/
static std::array<bool, 256> 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<std::uint8_t>(c)];
}
inline
bool
char
is_qpchar(char c)
{
/*
quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
obs-text = %x80-FF
*/
static std::array<bool, 256> 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<std::uint8_t>(c)];
}
@@ -161,7 +181,7 @@ to_field_char(char c)
| "/" | "[" | "]" | "?" | "="
| "{" | "}" | SP | HT
*/
static std::array<char, 256> 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<std::uint8_t>(c)];
}
@@ -182,7 +211,7 @@ char
to_value_char(char c)
{
// TEXT = <any OCTET except CTLs, but including LWS>
static std::array<std::uint8_t, 256> 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<char>(tab[static_cast<std::uint8_t>(c)]);
}
@@ -207,7 +237,7 @@ inline
std::int8_t
unhex(char c)
{
static std::array<std::int8_t, 256> 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<std::uint8_t>(c)];
}

View File

@@ -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

View File

@@ -98,6 +98,7 @@ public:
{
if(other.base_)
{
// type-pun
base_ = reinterpret_cast<base*>(&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<base*>(&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<F>(std::forward<F>(f));
// type-pun
base_ = reinterpret_cast<base*>(&buf_[0]);
}

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -42,7 +42,7 @@ class stream<NextLayer>::write_frame_op
detail::prepared_key key;
std::uint64_t remain;
int state = 0;
int entry;
int entry_state;
data(Handler& handler_, stream<NextLayer>& 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<static_streambuf>(
@@ -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);
}

View File

@@ -985,6 +985,10 @@ public:
<em>composed operation</em>. 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