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:
Vinnie Falco committed 2017-08-04 19:02:13 -07:00
1 parent dc6a08d10a
commit 64327739f0
14 files changed
+1233 -898

No files matched your search

+160 -80
View File
@@ -50,14 +50,23 @@ class stream_impl
template<class Handler, class Buffers>
class read_op_impl;
enum class status
{
ok,
eof,
reset
};
struct state
{
friend class stream;
std::mutex m;
buffer_type b;
std::condition_variable cv;
std::unique_ptr<read_op> op;
boost::asio::io_service& ios;
bool eof = false;
status code = status::ok;
fail_counter* fc = nullptr;
std::size_t nread = 0;
std::size_t nwrite = 0;
@@ -75,7 +84,24 @@ class stream_impl
{
}
friend class stream;
~state()
{
BOOST_ASSERT(! op);
}
void
on_write()
{
if(op)
{
std::unique_ptr<read_op> op_ = std::move(op);
op_->operator()();
}
else
{
cv.notify_all();
}
}
};
state s0_;
@@ -86,52 +112,57 @@ public:
boost::asio::io_service& ios,
fail_counter* fc)
: s0_(ios, fc)
, s1_(ios, fc)
, s1_(ios, nullptr)
{
}
~stream_impl()
{
BOOST_ASSERT(! s0_.op);
BOOST_ASSERT(! s1_.op);
}
};
template<class Handler, class Buffers>
class stream_impl::read_op_impl : public stream_impl::read_op
{
state& s_;
Buffers b_;
Handler h_;
public:
read_op_impl(state& s,
Buffers const& b, Handler&& h)
: s_(s)
, b_(b)
, h_(std::move(h))
class lambda
{
}
state& s_;
Buffers b_;
Handler h_;
read_op_impl(state& s,
Buffers const& b, Handler const& h)
: s_(s)
, b_(b)
, h_(h)
{
}
public:
lambda(lambda&&) = default;
lambda(lambda const&) = default;
void
operator()() override;
};
template<class Handler, class Buffers>
void
stream_impl::
read_op_impl<Handler, Buffers>::
operator()()
{
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
s_.ios.post(
[&]()
lambda(state& s, Buffers const& b, Handler&& h)
: s_(s)
, b_(b)
, h_(std::move(h))
{
BOOST_ASSERT(s_.op);
}
lambda(state& s, Buffers const& b, Handler const& h)
: s_(s)
, b_(b)
, h_(h)
{
}
void
post()
{
s_.ios.post(std::move(*this));
}
void
operator()()
{
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
std::unique_lock<std::mutex> lock{s_.m};
BOOST_ASSERT(! s_.op);
if(s_.b.size() > 0)
{
auto const bytes_transferred = buffer_copy(
@@ -139,7 +170,6 @@ operator()()
s_.b.consume(bytes_transferred);
auto& s = s_;
Handler h{std::move(h_)};
s.op.reset(nullptr);
lock.unlock();
++s.nread;
s.ios.post(bind_handler(std::move(h),
@@ -147,17 +177,40 @@ operator()()
}
else
{
BOOST_ASSERT(s_.eof);
BOOST_ASSERT(s_.code != status::ok);
auto& s = s_;
Handler h{std::move(h_)};
s.op.reset(nullptr);
lock.unlock();
++s.nread;
s.ios.post(bind_handler(std::move(h),
boost::asio::error::eof, 0));
error_code ec;
if(s.code == status::eof)
ec = boost::asio::error::eof;
else if(s.code == status::reset)
ec = boost::asio::error::connection_reset;
s.ios.post(bind_handler(std::move(h), ec, 0));
}
});
}
}
};
lambda fn_;
public:
read_op_impl(state& s, Buffers const& b, Handler&& h)
: fn_(s, b, std::move(h))
{
}
read_op_impl(state& s, Buffers const& b, Handler const& h)
: fn_(s, b, h)
{
}
void
operator()() override
{
fn_.post();
}
};
} // detail
@@ -175,6 +228,8 @@ operator()()
*/
class stream
{
using status = detail::stream_impl::status;
std::shared_ptr<detail::stream_impl> impl_;
detail::stream_impl::state& in_;
detail::stream_impl::state& out_;
@@ -191,10 +246,30 @@ class stream
public:
using buffer_type = flat_buffer;
~stream() = default;
stream(stream&&) = default;
stream& operator=(stream const&) = delete;
/// Destructor
~stream()
{
if(! impl_)
return;
BOOST_ASSERT(! in_.op);
std::unique_lock<std::mutex> lock{out_.m};
if(out_.code == status::ok)
{
out_.code = status::reset;
out_.on_write();
}
lock.unlock();
}
stream(stream&& other)
: impl_(std::move(other.impl_))
, in_(other.in_)
, out_(other.out_)
{
}
/// Constructor
explicit
stream(
@@ -298,15 +373,17 @@ public:
buffer_size(*in_.b.data().begin())};
}
/// Clear the buffer holding the input data
/*
/// Appends a string to the pending input data
void
clear()
str(string_view s)
{
in_.b.consume((std::numeric_limits<
std::size_t>::max)());
using boost::asio::buffer;
using boost::asio::buffer_copy;
std::unique_lock<std::mutex> lock{in_.m};
in_.b.commit(buffer_copy(
in_.b.prepare(s.size()),
buffer(s.data(), s.size())));
}
*/
/// Return the number of reads
std::size_t
@@ -409,7 +486,9 @@ read_some(MutableBufferSequence const& buffers,
in_.cv.wait(lock,
[&]()
{
return in_.b.size() > 0 || in_.eof;
return
in_.b.size() > 0 ||
in_.code != status::ok;
});
std::size_t bytes_transferred;
if(in_.b.size() > 0)
@@ -421,9 +500,12 @@ read_some(MutableBufferSequence const& buffers,
}
else
{
BOOST_ASSERT(in_.eof);
BOOST_ASSERT(in_.code != status::ok);
bytes_transferred = 0;
ec = boost::asio::error::eof;
if(in_.code == status::eof)
ec = boost::asio::error::eof;
else if(in_.code == status::reset)
ec = boost::asio::error::connection_reset;
}
++in_.nread;
return bytes_transferred;
@@ -433,7 +515,8 @@ template<class MutableBufferSequence, class ReadHandler>
async_return_type<
ReadHandler, void(error_code, std::size_t)>
stream::
async_read_some(MutableBufferSequence const& buffers,
async_read_some(
MutableBufferSequence const& buffers,
ReadHandler&& handler)
{
static_assert(is_mutable_buffer_sequence<
@@ -454,14 +537,7 @@ async_read_some(MutableBufferSequence const& buffers,
}
{
std::unique_lock<std::mutex> lock{in_.m};
if(in_.eof)
{
lock.unlock();
++in_.nread;
in_.ios.post(bind_handler(init.completion_handler,
boost::asio::error::eof, 0));
}
else if(buffer_size(buffers) == 0 ||
if(buffer_size(buffers) == 0 ||
buffer_size(in_.b.data()) > 0)
{
auto const bytes_transferred = buffer_copy(
@@ -472,6 +548,18 @@ async_read_some(MutableBufferSequence const& buffers,
in_.ios.post(bind_handler(init.completion_handler,
error_code{}, bytes_transferred));
}
else if(in_.code != status::ok)
{
lock.unlock();
++in_.nread;
error_code ec;
if(in_.code == status::eof)
ec = boost::asio::error::eof;
else if(in_.code == status::reset)
ec = boost::asio::error::connection_reset;
in_.ios.post(bind_handler(
init.completion_handler, ec, 0));
}
else
{
in_.op.reset(new
@@ -492,7 +580,7 @@ write_some(ConstBufferSequence const& buffers)
static_assert(is_const_buffer_sequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
BOOST_ASSERT(! out_.eof);
BOOST_ASSERT(out_.code == status::ok);
error_code ec;
auto const bytes_transferred =
write_some(buffers, ec);
@@ -512,7 +600,7 @@ write_some(
"ConstBufferSequence requirements not met");
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
BOOST_ASSERT(! out_.eof);
BOOST_ASSERT(out_.code == status::ok);
if(in_.fc && in_.fc->fail(ec))
return 0;
auto const n = (std::min)(
@@ -521,10 +609,7 @@ write_some(
auto const bytes_transferred =
buffer_copy(out_.b.prepare(n), buffers);
out_.b.commit(bytes_transferred);
if(out_.op)
out_.op.get()->operator()();
else
out_.cv.notify_all();
out_.on_write();
lock.unlock();
++out_.nwrite;
ec.assign(0, ec.category());
@@ -543,7 +628,7 @@ async_write_some(ConstBufferSequence const& buffers,
"ConstBufferSequence requirements not met");
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
BOOST_ASSERT(! out_.eof);
BOOST_ASSERT(out_.code == status::ok);
async_completion<WriteHandler,
void(error_code, std::size_t)> init{handler};
if(in_.fc)
@@ -559,10 +644,7 @@ async_write_some(ConstBufferSequence const& buffers,
auto const bytes_transferred =
buffer_copy(out_.b.prepare(n), buffers);
out_.b.commit(bytes_transferred);
if(out_.op)
out_.op.get()->operator()();
else
out_.cv.notify_all();
out_.on_write();
lock.unlock();
++out_.nwrite;
in_.ios.post(bind_handler(init.completion_handler,
@@ -607,14 +689,12 @@ void
stream::
close()
{
BOOST_ASSERT(! in_.op);
std::lock_guard<std::mutex> lock{out_.m};
if(! out_.eof)
if(out_.code == status::ok)
{
out_.eof = true;
if(out_.op)
out_.op.get()->operator()();
else
out_.cv.notify_all();
out_.code = status::eof;
out_.on_write();
}
}