forked from boostorg/beast
WebSocket close will automatically drain (API Change):
fix #642 * Calls to stream::close and stream::async_close will automatically perform the required read operations Actions Required: * Remove calling code which drains the connection after calling stream::close or stream::async_close
This commit is contained in:
1 parent
dc6a08d10a
commit
64327739f0
14 files changed
+1233
-898
No files matched your search
@@ -209,6 +209,7 @@ close(close_reason const& cr, error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_stream<next_layer_type>::value,
|
||||
"SyncStream requirements not met");
|
||||
using beast::detail::clamp;
|
||||
// If rd_close_ is set then we already sent a close
|
||||
BOOST_ASSERT(! rd_close_);
|
||||
if(wr_close_)
|
||||
@@ -219,12 +220,82 @@ close(close_reason const& cr, error_code& ec)
|
||||
return;
|
||||
}
|
||||
wr_close_ = true;
|
||||
detail::frame_streambuf fb;
|
||||
write_close<flat_static_buffer_base>(fb, cr);
|
||||
boost::asio::write(stream_, fb.data(), ec);
|
||||
{
|
||||
detail::frame_streambuf fb;
|
||||
write_close<flat_static_buffer_base>(fb, cr);
|
||||
boost::asio::write(stream_, fb.data(), ec);
|
||||
}
|
||||
failed_ = !!ec;
|
||||
if(failed_)
|
||||
return;
|
||||
// Drain the connection
|
||||
close_code code{};
|
||||
if(rd_.remain > 0)
|
||||
goto read_payload;
|
||||
for(;;)
|
||||
{
|
||||
// Read frame header
|
||||
while(! parse_fh(rd_.fh, rd_.buf, code))
|
||||
{
|
||||
if(code != close_code::none)
|
||||
return do_fail(close_code::none,
|
||||
error::failed, ec);
|
||||
auto const bytes_transferred =
|
||||
stream_.read_some(
|
||||
rd_.buf.prepare(read_size(rd_.buf,
|
||||
rd_.buf.max_size())), ec);
|
||||
failed_ = !!ec;
|
||||
if(failed_)
|
||||
return;
|
||||
rd_.buf.commit(bytes_transferred);
|
||||
}
|
||||
if(detail::is_control(rd_.fh.op))
|
||||
{
|
||||
// Process control frame
|
||||
if(rd_.fh.op == detail::opcode::close)
|
||||
{
|
||||
BOOST_ASSERT(! rd_close_);
|
||||
rd_close_ = true;
|
||||
auto const mb = buffer_prefix(
|
||||
clamp(rd_.fh.len),
|
||||
rd_.buf.mutable_data());
|
||||
if(rd_.fh.len > 0 && rd_.fh.mask)
|
||||
detail::mask_inplace(mb, rd_.key);
|
||||
detail::read_close(cr_, mb, code);
|
||||
if(code != close_code::none)
|
||||
// Protocol error
|
||||
return do_fail(close_code::none,
|
||||
error::failed, ec);
|
||||
rd_.buf.consume(clamp(rd_.fh.len));
|
||||
break;
|
||||
}
|
||||
rd_.buf.consume(clamp(rd_.fh.len));
|
||||
}
|
||||
else
|
||||
{
|
||||
read_payload:
|
||||
while(rd_.buf.size() < rd_.remain)
|
||||
{
|
||||
rd_.remain -= rd_.buf.size();
|
||||
rd_.buf.consume(rd_.buf.size());
|
||||
auto const bytes_transferred =
|
||||
stream_.read_some(
|
||||
rd_.buf.prepare(read_size(rd_.buf,
|
||||
rd_.buf.max_size())), ec);
|
||||
failed_ = !!ec;
|
||||
if(failed_)
|
||||
return;
|
||||
rd_.buf.commit(bytes_transferred);
|
||||
}
|
||||
BOOST_ASSERT(rd_.buf.size() >= rd_.remain);
|
||||
rd_.buf.consume(clamp(rd_.remain));
|
||||
rd_.remain = 0;
|
||||
}
|
||||
}
|
||||
// _Close the WebSocket Connection_
|
||||
do_fail(close_code::none, error::closed, ec);
|
||||
if(ec == error::closed)
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
|
||||
@@ -39,7 +39,7 @@ class stream<NextLayer>::fail_op
|
||||
stream<NextLayer>& ws_;
|
||||
int step_ = 0;
|
||||
bool dispatched_ = false;
|
||||
close_code code_;
|
||||
std::uint16_t code_;
|
||||
error_code ev_;
|
||||
token tok_;
|
||||
|
||||
@@ -47,12 +47,11 @@ public:
|
||||
fail_op(fail_op&&) = default;
|
||||
fail_op(fail_op const&) = default;
|
||||
|
||||
// send close code, then teardown
|
||||
template<class DeducedHandler>
|
||||
fail_op(
|
||||
DeducedHandler&& h,
|
||||
stream<NextLayer>& ws,
|
||||
close_code code,
|
||||
std::uint16_t code,
|
||||
error_code ev)
|
||||
: h_(std::forward<DeducedHandler>(h))
|
||||
, ws_(ws)
|
||||
@@ -220,7 +219,7 @@ template<class NextLayer>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
do_fail(
|
||||
close_code code, // if set, send a close frame first
|
||||
std::uint16_t code, // if set, send a close frame first
|
||||
error_code ev, // error code to use upon success
|
||||
error_code& ec) // set to the error, else set to ev
|
||||
{
|
||||
@@ -256,7 +255,7 @@ template<class Handler>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
do_async_fail(
|
||||
close_code code, // if set, send a close frame first
|
||||
std::uint16_t code, // if set, send a close frame first
|
||||
error_code ev, // error code to use upon success
|
||||
Handler&& handler)
|
||||
{
|
||||
|
||||
@@ -196,34 +196,27 @@ operator()(
|
||||
{
|
||||
BOOST_ASSERT(! ws_.rd_close_);
|
||||
ws_.rd_close_ = true;
|
||||
detail::read_close(ws_.cr_, cb, code);
|
||||
close_reason cr;
|
||||
detail::read_close(cr, cb, code);
|
||||
if(code != close_code::none)
|
||||
{
|
||||
// _Fail the WebSocket Connection_
|
||||
return ws_.do_async_fail(
|
||||
code, error::failed, std::move(h_));
|
||||
}
|
||||
ws_.cr_ = cr;
|
||||
ws_.rd_.buf.consume(len);
|
||||
if(ws_.ctrl_cb_)
|
||||
ws_.ctrl_cb_(frame_type::close,
|
||||
ws_.cr_.reason);
|
||||
if(ws_.wr_close_)
|
||||
// _Close the WebSocket Connection_
|
||||
return ws_.do_async_fail(close_code::none,
|
||||
if(! ws_.wr_close_)
|
||||
// _Start the WebSocket Closing Handshake_
|
||||
return ws_.do_async_fail(
|
||||
cr.code == close_code::none ?
|
||||
close_code::normal : cr.code,
|
||||
error::closed, std::move(h_));
|
||||
auto cr = ws_.cr_;
|
||||
if(cr.code == close_code::none)
|
||||
cr.code = close_code::normal;
|
||||
cr.reason = "";
|
||||
ws_.rd_.fb.consume(ws_.rd_.fb.size());
|
||||
ws_.template write_close<
|
||||
flat_static_buffer_base>(
|
||||
ws_.rd_.fb, cr);
|
||||
// _Start the WebSocket Closing Handshake_
|
||||
return ws_.do_async_fail(
|
||||
cr.code == close_code::none ?
|
||||
close_code::normal :
|
||||
static_cast<close_code>(cr.code),
|
||||
// _Close the WebSocket Connection_
|
||||
return ws_.do_async_fail(close_code::none,
|
||||
error::closed, std::move(h_));
|
||||
}
|
||||
}
|
||||
@@ -434,11 +427,11 @@ operator()(
|
||||
goto go_maybe_fill;
|
||||
|
||||
case do_maybe_fill:
|
||||
dispatched_ = true;
|
||||
if(ec)
|
||||
break;
|
||||
if(ws_.rd_.done)
|
||||
break;
|
||||
dispatched_ = true;
|
||||
|
||||
go_maybe_fill:
|
||||
if(ws_.pmd_ && ws_.pmd_->rd_set)
|
||||
@@ -760,8 +753,9 @@ operator()(
|
||||
do_read:
|
||||
using buffers_type = typename
|
||||
DynamicBuffer::mutable_buffers_type;
|
||||
auto const rsh = ws_.read_size_hint(b_);
|
||||
auto const size = clamp(
|
||||
ws_.read_size_hint(b_), limit_);
|
||||
rsh, limit_);
|
||||
boost::optional<buffers_type> mb;
|
||||
try
|
||||
{
|
||||
@@ -996,6 +990,7 @@ loop:
|
||||
{
|
||||
if(code != close_code::none)
|
||||
{
|
||||
// _Fail the WebSocket Connection_
|
||||
do_fail(code, error::failed, ec);
|
||||
return bytes_written;
|
||||
}
|
||||
@@ -1057,25 +1052,28 @@ loop:
|
||||
{
|
||||
BOOST_ASSERT(! rd_close_);
|
||||
rd_close_ = true;
|
||||
detail::read_close(cr_, cb, code);
|
||||
close_reason cr;
|
||||
detail::read_close(cr, cb, code);
|
||||
if(code != close_code::none)
|
||||
{
|
||||
// _Fail the WebSocket Connection_
|
||||
do_fail(code, error::failed, ec);
|
||||
return bytes_written;
|
||||
}
|
||||
cr_ = cr;
|
||||
rd_.buf.consume(len);
|
||||
if(ctrl_cb_)
|
||||
ctrl_cb_(frame_type::close, cr_.reason);
|
||||
if(! wr_close_)
|
||||
{
|
||||
// _Start the WebSocket Closing Handshake_
|
||||
do_fail(
|
||||
cr_.code == close_code::none ?
|
||||
close_code::normal :
|
||||
static_cast<close_code>(cr_.code),
|
||||
error::closed,
|
||||
ec);
|
||||
cr.code == close_code::none ?
|
||||
close_code::normal : cr.code,
|
||||
error::closed, ec);
|
||||
return bytes_written;
|
||||
}
|
||||
// _Close the WebSocket Connection_
|
||||
do_fail(close_code::none, error::closed, ec);
|
||||
return bytes_written;
|
||||
}
|
||||
@@ -1129,6 +1127,7 @@ loop:
|
||||
(rd_.remain == 0 && rd_.fh.fin &&
|
||||
! rd_.utf8.finish()))
|
||||
{
|
||||
// _Fail the WebSocket Connection_
|
||||
do_fail(
|
||||
close_code::bad_payload,
|
||||
error::failed,
|
||||
@@ -1163,6 +1162,7 @@ loop:
|
||||
(rd_.remain == 0 && rd_.fh.fin &&
|
||||
! rd_.utf8.finish()))
|
||||
{
|
||||
// _Fail the WebSocket Connection_
|
||||
do_fail(
|
||||
close_code::bad_payload,
|
||||
error::failed,
|
||||
@@ -1292,6 +1292,7 @@ loop:
|
||||
rd_.remain == 0 && rd_.fh.fin &&
|
||||
! rd_.utf8.finish()))
|
||||
{
|
||||
// _Fail the WebSocket Connection_
|
||||
do_fail(
|
||||
close_code::bad_payload,
|
||||
error::failed,
|
||||
|
||||
@@ -57,39 +57,68 @@ read_size_hint(
|
||||
std::size_t initial_size) const
|
||||
{
|
||||
using beast::detail::clamp;
|
||||
// no permessage-deflate
|
||||
std::size_t result;
|
||||
BOOST_ASSERT(initial_size > 0);
|
||||
if(! pmd_ || (! rd_.done && ! pmd_->rd_set))
|
||||
{
|
||||
// fresh message
|
||||
if(rd_.done)
|
||||
return initial_size;
|
||||
// current message is uncompressed
|
||||
|
||||
if(rd_.fh.fin)
|
||||
return clamp(rd_.remain);
|
||||
if(rd_.done)
|
||||
{
|
||||
// first message frame
|
||||
result = initial_size;
|
||||
goto done;
|
||||
}
|
||||
else if(rd_.fh.fin)
|
||||
{
|
||||
// last message frame
|
||||
BOOST_ASSERT(rd_.remain > 0);
|
||||
result = clamp(rd_.remain);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
return (std::max)(
|
||||
result = (std::max)(
|
||||
initial_size, clamp(rd_.remain));
|
||||
done:
|
||||
BOOST_ASSERT(result != 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class DynamicBuffer, class>
|
||||
std::size_t
|
||||
stream<NextLayer>::
|
||||
read_size_hint(
|
||||
DynamicBuffer& buffer) const
|
||||
read_size_hint(DynamicBuffer& buffer) const
|
||||
{
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
#if 1
|
||||
auto const initial_size = (std::min)(
|
||||
+tcp_frame_size,
|
||||
buffer.max_size() - buffer.size());
|
||||
if(initial_size == 0)
|
||||
return 1; // buffer is full
|
||||
return read_size_hint(initial_size);
|
||||
|
||||
#else
|
||||
using beast::detail::clamp;
|
||||
// no permessage-deflate
|
||||
std::size_t result;
|
||||
if(! pmd_ || (! rd_.done && ! pmd_->rd_set))
|
||||
{
|
||||
// fresh message
|
||||
// current message is uncompressed
|
||||
|
||||
if(rd_.done)
|
||||
return (std::min)(
|
||||
{
|
||||
// first message frame
|
||||
auto const n = (std::min)(
|
||||
buffer.max_size(),
|
||||
(std::max)(+tcp_frame_size,
|
||||
(std::max)(
|
||||
+tcp_frame_size,
|
||||
buffer.capacity() - buffer.size()));
|
||||
if(n > 0)
|
||||
return n;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(rd_.fh.fin)
|
||||
{
|
||||
@@ -104,6 +133,10 @@ read_size_hint(
|
||||
+tcp_frame_size,
|
||||
clamp(rd_.remain)),
|
||||
buffer.capacity() - buffer.size()));
|
||||
done:
|
||||
BOOST_ASSERT(result != 0);
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
|
||||
@@ -446,10 +446,10 @@ public:
|
||||
frame and whether or not the permessage-deflate extension is
|
||||
enabled.
|
||||
|
||||
@param initial_size A size representing the caller's desired
|
||||
buffer size for when there is no information which may be used
|
||||
to calculate a more specific value. For example, when reading
|
||||
the first frame header of a message.
|
||||
@param initial_size A non-zero size representing the caller's
|
||||
desired buffer size for when there is no information which may
|
||||
be used to calculate a more specific value. For example, when
|
||||
reading the first frame header of a message.
|
||||
*/
|
||||
std::size_t
|
||||
read_size_hint(
|
||||
@@ -3816,14 +3816,14 @@ private:
|
||||
|
||||
void
|
||||
do_fail(
|
||||
close_code code,
|
||||
std::uint16_t code,
|
||||
error_code ev,
|
||||
error_code& ec);
|
||||
|
||||
template<class Handler>
|
||||
void
|
||||
do_async_fail(
|
||||
close_code code,
|
||||
std::uint16_t code,
|
||||
error_code ev,
|
||||
Handler&& handler);
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user