summaryrefslogtreecommitdiff
path: root/boost/beast/websocket
diff options
context:
space:
mode:
Diffstat (limited to 'boost/beast/websocket')
-rw-r--r--boost/beast/websocket/detail/error.hpp78
-rw-r--r--boost/beast/websocket/detail/frame.hpp25
-rw-r--r--boost/beast/websocket/detail/mask.hpp177
-rw-r--r--boost/beast/websocket/detail/pausation.hpp203
-rw-r--r--boost/beast/websocket/detail/pmd_extension.hpp80
-rw-r--r--boost/beast/websocket/detail/stream_base.hpp221
-rw-r--r--boost/beast/websocket/detail/type_traits.hpp4
-rw-r--r--boost/beast/websocket/detail/utf8_checker.hpp6
-rw-r--r--boost/beast/websocket/error.hpp225
-rw-r--r--boost/beast/websocket/impl/accept.ipp243
-rw-r--r--boost/beast/websocket/impl/close.ipp133
-rw-r--r--boost/beast/websocket/impl/error.ipp170
-rw-r--r--boost/beast/websocket/impl/handshake.ipp133
-rw-r--r--boost/beast/websocket/impl/ping.ipp92
-rw-r--r--boost/beast/websocket/impl/read.ipp377
-rw-r--r--boost/beast/websocket/impl/rfc6455.ipp4
-rw-r--r--boost/beast/websocket/impl/stream.ipp545
-rw-r--r--boost/beast/websocket/impl/teardown.ipp123
-rw-r--r--boost/beast/websocket/impl/write.ipp231
-rw-r--r--boost/beast/websocket/ssl.hpp6
-rw-r--r--boost/beast/websocket/stream.hpp412
-rw-r--r--boost/beast/websocket/stream_fwd.hpp28
-rw-r--r--boost/beast/websocket/teardown.hpp12
23 files changed, 2191 insertions, 1337 deletions
diff --git a/boost/beast/websocket/detail/error.hpp b/boost/beast/websocket/detail/error.hpp
new file mode 100644
index 0000000000..57b837bd4c
--- /dev/null
+++ b/boost/beast/websocket/detail/error.hpp
@@ -0,0 +1,78 @@
+//
+// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// Official repository: https://github.com/boostorg/beast
+//
+
+#ifndef BOOST_BEAST_WEBSOCKET_DETAIL_ERROR_IPP
+#define BOOST_BEAST_WEBSOCKET_DETAIL_ERROR_IPP
+
+#include <boost/beast/core/error.hpp>
+#include <boost/beast/core/string.hpp>
+
+namespace boost {
+
+namespace beast {
+namespace websocket {
+enum class error;
+enum class condition;
+} // websocket
+} // beast
+
+namespace system {
+template<>
+struct is_error_code_enum<beast::websocket::error>
+{
+ static bool const value = true;
+};
+template<>
+struct is_error_condition_enum<beast::websocket::condition>
+{
+ static bool const value = true;
+};
+} // system
+
+namespace beast {
+namespace websocket {
+namespace detail {
+
+class error_codes : public error_category
+{
+public:
+ const char*
+ name() const noexcept override;
+
+ std::string
+ message(int ev) const override;
+
+ error_condition
+ default_error_condition(int ev) const noexcept override;
+};
+
+class error_conditions : public error_category
+{
+public:
+ const char*
+ name() const noexcept override;
+
+ std::string
+ message(int cv) const override;
+};
+
+} // detail
+
+error_code
+make_error_code(error e);
+
+error_condition
+make_error_condition(condition c);
+
+} // websocket
+} // beast
+
+} // boost
+
+#endif
diff --git a/boost/beast/websocket/detail/frame.hpp b/boost/beast/websocket/detail/frame.hpp
index 95080aeefc..c3bd1690a9 100644
--- a/boost/beast/websocket/detail/frame.hpp
+++ b/boost/beast/websocket/detail/frame.hpp
@@ -10,6 +10,7 @@
#ifndef BOOST_BEAST_WEBSOCKET_DETAIL_FRAME_HPP
#define BOOST_BEAST_WEBSOCKET_DETAIL_FRAME_HPP
+#include <boost/beast/websocket/error.hpp>
#include <boost/beast/websocket/rfc6455.hpp>
#include <boost/beast/websocket/detail/utf8_checker.hpp>
#include <boost/beast/core/buffers_suffix.hpp>
@@ -75,7 +76,7 @@ native_to_little_uint32(std::uint32_t v, void* buf)
p[3] = (v >> 24) & 0xff;
}
-/** WebSocket frame header opcodes. */
+// frame header opcodes
enum class opcode : std::uint8_t
{
cont = 0,
@@ -110,8 +111,7 @@ struct frame_header
};
// holds the largest possible frame header
-using fh_buffer =
- flat_static_buffer<14>;
+using fh_buffer = flat_static_buffer<14>;
// holds the largest possible control frame
using frame_buffer =
@@ -245,8 +245,10 @@ read_ping(ping_data& data, Buffers const& bs)
//
template<class Buffers>
void
-read_close(close_reason& cr,
- Buffers const& bs, close_code& code)
+read_close(
+ close_reason& cr,
+ Buffers const& bs,
+ error_code& ec)
{
using boost::asio::buffer;
using boost::asio::buffer_copy;
@@ -257,12 +259,13 @@ read_close(close_reason& cr,
if(n == 0)
{
cr = close_reason{};
- code = close_code::none;
+ ec.assign(0, ec.category());
return;
}
if(n == 1)
{
- code = close_code::protocol_error;
+ // invalid payload size == 1
+ ec = error::bad_close_size;
return;
}
buffers_suffix<Buffers> cb(bs);
@@ -274,7 +277,8 @@ read_close(close_reason& cr,
n -= 2;
if(! is_valid_close_code(cr.code))
{
- code = close_code::protocol_error;
+ // invalid close code
+ ec = error::bad_close_code;
return;
}
}
@@ -285,7 +289,8 @@ read_close(close_reason& cr,
if(! check_utf8(
cr.reason.data(), cr.reason.size()))
{
- code = close_code::protocol_error;
+ // not valid utf-8
+ ec = error::bad_close_payload;
return;
}
}
@@ -293,7 +298,7 @@ read_close(close_reason& cr,
{
cr.reason = "";
}
- code = close_code::none;
+ ec.assign(0, ec.category());
}
} // detail
diff --git a/boost/beast/websocket/detail/mask.hpp b/boost/beast/websocket/detail/mask.hpp
index 92ea0a4238..8958d793b8 100644
--- a/boost/beast/websocket/detail/mask.hpp
+++ b/boost/beast/websocket/detail/mask.hpp
@@ -11,6 +11,7 @@
#define BOOST_BEAST_WEBSOCKET_DETAIL_MASK_HPP
#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/core/detail/type_traits.hpp>
#include <boost/asio/buffer.hpp>
#include <array>
#include <climits>
@@ -83,179 +84,59 @@ using maskgen = maskgen_t<std::minstd_rand>;
//------------------------------------------------------------------------------
-using prepared_key =
- std::conditional<sizeof(void*) == 8,
- std::uint64_t, std::uint32_t>::type;
+using prepared_key = std::array<unsigned char, 4>;
inline
void
-prepare_key(std::uint32_t& prepared, std::uint32_t key)
+prepare_key(prepared_key& prepared, std::uint32_t key)
{
- prepared = key;
+ prepared[0] = (key >> 0) & 0xff;
+ prepared[1] = (key >> 8) & 0xff;
+ prepared[2] = (key >> 16) & 0xff;
+ prepared[3] = (key >> 24) & 0xff;
}
-inline
+template<std::size_t N>
void
-prepare_key(std::uint64_t& prepared, std::uint32_t key)
-{
- prepared =
- (static_cast<std::uint64_t>(key) << 32) | key;
-}
-
-template<class T>
-inline
-typename std::enable_if<std::is_integral<T>::value, T>::type
-ror(T t, unsigned n = 1)
+rol(std::array<unsigned char, N>& v, std::size_t n)
{
- auto constexpr bits =
- static_cast<unsigned>(
- sizeof(T) * CHAR_BIT);
- n &= bits-1;
- return static_cast<T>((t << (bits - n)) | (
- static_cast<typename std::make_unsigned<T>::type>(t) >> n));
+ auto v0 = v;
+ for(std::size_t i = 0; i < v.size(); ++i )
+ v[i] = v0[(i + n) % v.size()];
}
-// 32-bit optimized
-//
-template<class = void>
-void
-mask_inplace_fast(
- boost::asio::mutable_buffer const& b,
- std::uint32_t& key)
-{
- auto n = b.size();
- auto p = reinterpret_cast<std::uint8_t*>(b.data());
- if(n >= sizeof(key))
- {
- // Bring p to 4-byte alignment
- auto const i = reinterpret_cast<
- std::uintptr_t>(p) & (sizeof(key)-1);
- switch(i)
- {
- case 1: p[2] ^= static_cast<std::uint8_t>(key >> 16); BOOST_BEAST_FALLTHROUGH;
- case 2: p[1] ^= static_cast<std::uint8_t>(key >> 8); BOOST_BEAST_FALLTHROUGH;
- case 3: p[0] ^= static_cast<std::uint8_t>(key);
- {
- auto const d = static_cast<unsigned>(sizeof(key) - i);
- key = ror(key, 8*d);
- n -= d;
- p += d;
- BOOST_BEAST_FALLTHROUGH;
- }
- default:
- break;
- }
- }
-
- // Mask 4 bytes at a time
- for(auto i = n / sizeof(key); i; --i)
- {
- *reinterpret_cast<
- std::uint32_t*>(p) ^= key;
- p += sizeof(key);
- }
-
- // Leftovers
- n &= sizeof(key)-1;
- switch(n)
- {
- case 3: p[2] ^= static_cast<std::uint8_t>(key >> 16); BOOST_BEAST_FALLTHROUGH;
- case 2: p[1] ^= static_cast<std::uint8_t>(key >> 8); BOOST_BEAST_FALLTHROUGH;
- case 1: p[0] ^= static_cast<std::uint8_t>(key);
- key = ror(key, static_cast<unsigned>(8*n));
- BOOST_BEAST_FALLTHROUGH;
- default:
- break;
- }
-}
-
-// 64-bit optimized
+// Apply mask in place
//
-template<class = void>
+inline
void
-mask_inplace_fast(
- boost::asio::mutable_buffer const& b,
- std::uint64_t& key)
+mask_inplace(boost::asio::mutable_buffer& b, prepared_key& key)
{
auto n = b.size();
- auto p = reinterpret_cast<std::uint8_t*>(b.data());
- if(n >= sizeof(key))
+ auto mask = key; // avoid aliasing
+ auto p = reinterpret_cast<unsigned char*>(b.data());
+ while(n >= 4)
{
- // Bring p to 8-byte alignment
- auto const i = reinterpret_cast<
- std::uintptr_t>(p) & (sizeof(key)-1);
- switch(i)
- {
- case 1: p[6] ^= static_cast<std::uint8_t>(key >> 48);
- case 2: p[5] ^= static_cast<std::uint8_t>(key >> 40);
- case 3: p[4] ^= static_cast<std::uint8_t>(key >> 32);
- case 4: p[3] ^= static_cast<std::uint8_t>(key >> 24);
- case 5: p[2] ^= static_cast<std::uint8_t>(key >> 16);
- case 6: p[1] ^= static_cast<std::uint8_t>(key >> 8);
- case 7: p[0] ^= static_cast<std::uint8_t>(key);
- {
- auto const d = static_cast<
- unsigned>(sizeof(key) - i);
- key = ror(key, 8*d);
- n -= d;
- p += d;
- }
- default:
- break;
- }
+ for(int i = 0; i < 4; ++i)
+ p[i] ^= mask[i];
+ p += 4;
+ n -= 4;
}
-
- // Mask 8 bytes at a time
- for(auto i = n / sizeof(key); i; --i)
+ if(n > 0)
{
- *reinterpret_cast<
- std::uint64_t*>(p) ^= key;
- p += sizeof(key);
+ for(std::size_t i = 0; i < n; ++i)
+ p[i] ^= mask[i];
+ rol(key, n);
}
-
- // Leftovers
- n &= sizeof(key)-1;
- switch(n)
- {
- case 7: p[6] ^= static_cast<std::uint8_t>(key >> 48);
- case 6: p[5] ^= static_cast<std::uint8_t>(key >> 40);
- case 5: p[4] ^= static_cast<std::uint8_t>(key >> 32);
- case 4: p[3] ^= static_cast<std::uint8_t>(key >> 24);
- case 3: p[2] ^= static_cast<std::uint8_t>(key >> 16);
- case 2: p[1] ^= static_cast<std::uint8_t>(key >> 8);
- case 1: p[0] ^= static_cast<std::uint8_t>(key);
- key = ror(key, static_cast<unsigned>(8*n));
- default:
- break;
- }
-}
-
-inline
-void
-mask_inplace(
- boost::asio::mutable_buffer const& b,
- std::uint32_t& key)
-{
- mask_inplace_fast(b, key);
-}
-
-inline
-void
-mask_inplace(
- boost::asio::mutable_buffer const& b,
- std::uint64_t& key)
-{
- mask_inplace_fast(b, key);
}
// Apply mask in place
//
template<class MutableBuffers, class KeyType>
void
-mask_inplace(
- MutableBuffers const& bs, KeyType& key)
+mask_inplace(MutableBuffers const& bs, KeyType& key)
{
- for(boost::asio::mutable_buffer b : bs)
+ for(boost::asio::mutable_buffer b :
+ beast::detail::buffers_range(bs))
mask_inplace(b, key);
}
diff --git a/boost/beast/websocket/detail/pausation.hpp b/boost/beast/websocket/detail/pausation.hpp
index f51ee10327..e3870b038a 100644
--- a/boost/beast/websocket/detail/pausation.hpp
+++ b/boost/beast/websocket/detail/pausation.hpp
@@ -10,13 +10,10 @@
#ifndef BOOST_BEAST_WEBSOCKET_DETAIL_PAUSATION_HPP
#define BOOST_BEAST_WEBSOCKET_DETAIL_PAUSATION_HPP
-#include <boost/beast/core/handler_ptr.hpp>
+#include <boost/beast/core/detail/allocator.hpp>
#include <boost/asio/associated_allocator.hpp>
-#include <boost/asio/coroutine.hpp>
#include <boost/assert.hpp>
-#include <array>
#include <memory>
-#include <new>
#include <utility>
namespace boost {
@@ -30,124 +27,59 @@ namespace detail {
//
class pausation
{
- struct base
+ struct handler
{
- base() = default;
- base(base &&) = delete;
- base(base const&) = delete;
- virtual ~base() = default;
- virtual void operator()() = 0;
+ handler() = default;
+ handler(handler &&) = delete;
+ handler(handler const&) = delete;
+ virtual ~handler() = default;
+ virtual void destroy() = 0;
+ virtual void invoke() = 0;
};
- template<class F>
- struct holder : base
+ template<class Handler>
+ class impl : public handler
{
- F f;
-
- holder(holder&&) = default;
-
- template<class U>
- explicit
- holder(U&& u)
- : f(std::forward<U>(u))
- {
- }
-
- void
- operator()() override
- {
- F f_(std::move(f));
- this->~holder();
- // invocation of f_() can
- // assign a new object to *this.
- f_();
- }
- };
-
- struct exemplar : boost::asio::coroutine
- {
- struct H
- {
- void operator()();
- };
-
- struct T
- {
- using handler_type = H;
- };
-
- handler_ptr<T, H> hp;
-
- void operator()();
- };
-
- template<class Op>
- class saved_op
- {
- Op* op_ = nullptr;
+ Handler h_;
public:
- ~saved_op()
- {
- if(op_)
- {
- Op op(std::move(*op_));
- op_->~Op();
- typename std::allocator_traits<
- boost::asio::associated_allocator_t<Op>>::
- template rebind_alloc<Op> alloc{
- boost::asio::get_associated_allocator(op)};
- std::allocator_traits<
- decltype(alloc)>::deallocate(alloc, op_, 1);
- }
- }
-
- saved_op(saved_op&& other)
- : op_(other.op_)
+ template<class DeducedHandler>
+ impl(DeducedHandler&& h)
+ : h_(std::forward<DeducedHandler>(h))
{
- other.op_ = nullptr;
}
- saved_op& operator=(saved_op&& other)
- {
- BOOST_ASSERT(! op_);
- op_ = other.op_;
- other.op_ = 0;
- return *this;
- }
-
- explicit
- saved_op(Op&& op)
+ void
+ destroy() override
{
- typename std::allocator_traits<
- boost::asio::associated_allocator_t<Op>>::
- template rebind_alloc<Op> alloc{
- boost::asio::get_associated_allocator(op)};
- auto const p = std::allocator_traits<
- decltype(alloc)>::allocate(alloc, 1);
- op_ = new(p) Op{std::move(op)};
+ Handler h(std::move(h_));
+ typename beast::detail::allocator_traits<
+ boost::asio::associated_allocator_t<
+ Handler>>::template rebind_alloc<impl> alloc{
+ boost::asio::get_associated_allocator(h)};
+ beast::detail::allocator_traits<
+ decltype(alloc)>::destroy(alloc, this);
+ beast::detail::allocator_traits<
+ decltype(alloc)>::deallocate(alloc, this, 1);
}
void
- operator()()
+ invoke() override
{
- BOOST_ASSERT(op_);
- Op op{std::move(*op_)};
- typename std::allocator_traits<
- boost::asio::associated_allocator_t<Op>>::
- template rebind_alloc<Op> alloc{
- boost::asio::get_associated_allocator(op)};
- std::allocator_traits<
- decltype(alloc)>::deallocate(alloc, op_, 1);
- op_ = nullptr;
- op();
+ Handler h(std::move(h_));
+ typename beast::detail::allocator_traits<
+ boost::asio::associated_allocator_t<
+ Handler>>::template rebind_alloc<impl> alloc{
+ boost::asio::get_associated_allocator(h)};
+ beast::detail::allocator_traits<
+ decltype(alloc)>::destroy(alloc, this);
+ beast::detail::allocator_traits<
+ decltype(alloc)>::deallocate(alloc, this, 1);
+ h();
}
};
- using buf_type = char[sizeof(holder<exemplar>)];
-
- base* base_ = nullptr;
- alignas(holder<exemplar>) buf_type buf_;
+ handler* h_ = nullptr;
public:
pausation() = default;
@@ -156,69 +88,70 @@ public:
~pausation()
{
- if(base_)
- base_->~base();
+ if(h_)
+ h_->destroy();
}
pausation(pausation&& other)
{
boost::ignore_unused(other);
- BOOST_ASSERT(! other.base_);
+ BOOST_ASSERT(! other.h_);
}
pausation&
operator=(pausation&& other)
{
boost::ignore_unused(other);
- BOOST_ASSERT(! base_);
- BOOST_ASSERT(! other.base_);
+ BOOST_ASSERT(! h_);
+ BOOST_ASSERT(! other.h_);
return *this;
}
- template<class F>
- void
- emplace(F&& f);
-
- template<class F>
+ template<class CompletionHandler>
void
- save(F&& f);
+ emplace(CompletionHandler&& handler);
explicit
operator bool() const
{
- return base_ != nullptr;
+ return h_ != nullptr;
}
bool
maybe_invoke()
{
- if(base_)
+ if(h_)
{
- auto const basep = base_;
- base_ = nullptr;
- (*basep)();
+ auto const h = h_;
+ h_ = nullptr;
+ h->invoke();
return true;
}
return false;
}
};
-template<class F>
+template<class CompletionHandler>
void
-pausation::emplace(F&& f)
+pausation::emplace(CompletionHandler&& handler)
{
- using type = holder<typename std::decay<F>::type>;
- static_assert(sizeof(buf_type) >= sizeof(type),
- "buffer too small");
- BOOST_ASSERT(! base_);
- base_ = ::new(buf_) type{std::forward<F>(f)};
-}
-
-template<class F>
-void
-pausation::save(F&& f)
-{
- emplace(saved_op<F>{std::move(f)});
+ BOOST_ASSERT(! h_);
+ typename beast::detail::allocator_traits<
+ boost::asio::associated_allocator_t<
+ CompletionHandler>>::template rebind_alloc<
+ impl<CompletionHandler>> alloc{
+ boost::asio::get_associated_allocator(handler)};
+ using A = decltype(alloc);
+ auto const d =
+ [&alloc](impl<CompletionHandler>* p)
+ {
+ beast::detail::allocator_traits<A>::deallocate(alloc, p, 1);
+ };
+ std::unique_ptr<impl<CompletionHandler>, decltype(d)> p{
+ beast::detail::allocator_traits<A>::allocate(alloc, 1), d};
+ beast::detail::allocator_traits<A>::construct(
+ alloc, p.get(), std::forward<CompletionHandler>(handler));
+ h_ = p.release();
}
} // detail
diff --git a/boost/beast/websocket/detail/pmd_extension.hpp b/boost/beast/websocket/detail/pmd_extension.hpp
index a28b844cd7..ce4f2425cf 100644
--- a/boost/beast/websocket/detail/pmd_extension.hpp
+++ b/boost/beast/websocket/detail/pmd_extension.hpp
@@ -19,6 +19,7 @@
#include <boost/beast/http/rfc7230.hpp>
#include <boost/asio/buffer.hpp>
#include <utility>
+#include <type_traits>
namespace boost {
namespace beast {
@@ -355,85 +356,6 @@ pmd_normalize(pmd_offer& offer)
}
}
-//--------------------------------------------------------------------
-
-// Compress a buffer sequence
-// Returns: `true` if more calls are needed
-//
-template<class DeflateStream, class ConstBufferSequence>
-bool
-deflate(
- DeflateStream& zo,
- boost::asio::mutable_buffer& out,
- buffers_suffix<ConstBufferSequence>& cb,
- bool fin,
- std::size_t& total_in,
- error_code& ec)
-{
- using boost::asio::buffer;
- BOOST_ASSERT(out.size() >= 6);
- zlib::z_params zs;
- zs.avail_in = 0;
- zs.next_in = nullptr;
- zs.avail_out = out.size();
- zs.next_out = out.data();
- for(auto in : beast::detail::buffers_range(cb))
- {
- zs.avail_in = in.size();
- if(zs.avail_in == 0)
- continue;
- zs.next_in = in.data();
- zo.write(zs, zlib::Flush::none, ec);
- if(ec)
- {
- if(ec != zlib::error::need_buffers)
- return false;
- BOOST_ASSERT(zs.avail_out == 0);
- BOOST_ASSERT(zs.total_out == out.size());
- ec.assign(0, ec.category());
- break;
- }
- if(zs.avail_out == 0)
- {
- BOOST_ASSERT(zs.total_out == out.size());
- break;
- }
- BOOST_ASSERT(zs.avail_in == 0);
- }
- total_in = zs.total_in;
- cb.consume(zs.total_in);
- if(zs.avail_out > 0 && fin)
- {
- auto const remain = boost::asio::buffer_size(cb);
- if(remain == 0)
- {
- // Inspired by Mark Adler
- // https://github.com/madler/zlib/issues/149
- //
- // VFALCO We could do this flush twice depending
- // on how much space is in the output.
- zo.write(zs, zlib::Flush::block, ec);
- BOOST_ASSERT(! ec || ec == zlib::error::need_buffers);
- if(ec == zlib::error::need_buffers)
- ec.assign(0, ec.category());
- if(ec)
- return false;
- if(zs.avail_out >= 6)
- {
- zo.write(zs, zlib::Flush::full, ec);
- BOOST_ASSERT(! ec);
- // remove flush marker
- zs.total_out -= 4;
- out = buffer(out.data(), zs.total_out);
- return false;
- }
- }
- }
- ec.assign(0, ec.category());
- out = buffer(out.data(), zs.total_out);
- return true;
-}
-
} // detail
} // websocket
} // beast
diff --git a/boost/beast/websocket/detail/stream_base.hpp b/boost/beast/websocket/detail/stream_base.hpp
new file mode 100644
index 0000000000..2e93cce3d4
--- /dev/null
+++ b/boost/beast/websocket/detail/stream_base.hpp
@@ -0,0 +1,221 @@
+//
+// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// Official repository: https://github.com/boostorg/beast
+//
+
+#ifndef BOOST_BEAST_WEBSOCKET_STREAM_BASE_HPP
+#define BOOST_BEAST_WEBSOCKET_STREAM_BASE_HPP
+
+#include <boost/beast/websocket/option.hpp>
+#include <boost/beast/websocket/detail/pmd_extension.hpp>
+#include <boost/beast/zlib/deflate_stream.hpp>
+#include <boost/beast/zlib/inflate_stream.hpp>
+#include <boost/beast/core/buffers_suffix.hpp>
+#include <boost/beast/core/error.hpp>
+#include <boost/asio/buffer.hpp>
+#include <cstdint>
+#include <memory>
+
+namespace boost {
+namespace beast {
+namespace websocket {
+namespace detail {
+
+// used to order reads and writes
+class soft_mutex
+{
+ int id_ = 0;
+
+public:
+ soft_mutex() = default;
+ soft_mutex(soft_mutex const&) = delete;
+ soft_mutex& operator=(soft_mutex const&) = delete;
+
+ soft_mutex(soft_mutex&& other) noexcept
+ : id_(other.id_)
+ {
+ other.id_ = 0;
+ }
+
+ soft_mutex& operator=(soft_mutex&& other) noexcept
+ {
+ id_ = other.id_;
+ other.id_ = 0;
+ return *this;
+ }
+
+ // VFALCO I'm not too happy that this function is needed
+ void reset()
+ {
+ id_ = 0;
+ }
+
+ bool is_locked() const
+ {
+ return id_ != 0;
+ }
+
+ template<class T>
+ bool is_locked(T const*) const
+ {
+ return id_ == T::id;
+ }
+
+ template<class T>
+ void lock(T const*)
+ {
+ BOOST_ASSERT(id_ == 0);
+ id_ = T::id;
+ }
+
+ template<class T>
+ void unlock(T const*)
+ {
+ BOOST_ASSERT(id_ == T::id);
+ id_ = 0;
+ }
+
+ template<class T>
+ bool try_lock(T const*)
+ {
+ // If this assert goes off it means you are attempting to
+ // simultaneously initiate more than one of same asynchronous
+ // operation, which is not allowed. For example, you must wait
+ // for an async_read to complete before performing another
+ // async_read.
+ //
+ BOOST_ASSERT(id_ != T::id);
+ if(id_ != 0)
+ return false;
+ id_ = T::id;
+ return true;
+ }
+
+ template<class T>
+ bool try_unlock(T const*)
+ {
+ if(id_ != T::id)
+ return false;
+ id_ = 0;
+ return true;
+ }
+};
+
+template<bool deflateSupported>
+struct stream_base
+{
+ // State information for the permessage-deflate extension
+ struct pmd_type
+ {
+ // `true` if current read message is compressed
+ bool rd_set = false;
+
+ zlib::deflate_stream zo;
+ zlib::inflate_stream zi;
+ };
+
+ std::unique_ptr<pmd_type> pmd_; // pmd settings or nullptr
+ permessage_deflate pmd_opts_; // local pmd options
+ detail::pmd_offer pmd_config_; // offer (client) or negotiation (server)
+
+ // return `true` if current message is deflated
+ bool
+ rd_deflated() const
+ {
+ return pmd_ && pmd_->rd_set;
+ }
+
+ // set whether current message is deflated
+ // returns `false` on protocol violation
+ bool
+ rd_deflated(bool rsv1)
+ {
+ if(pmd_)
+ {
+ pmd_->rd_set = rsv1;
+ return true;
+ }
+ return ! rsv1; // pmd not negotiated
+ }
+
+ template<class ConstBufferSequence>
+ bool
+ deflate(
+ boost::asio::mutable_buffer& out,
+ buffers_suffix<ConstBufferSequence>& cb,
+ bool fin,
+ std::size_t& total_in,
+ error_code& ec);
+
+ void
+ do_context_takeover_write(role_type role);
+
+ void
+ inflate(
+ zlib::z_params& zs,
+ zlib::Flush flush,
+ error_code& ec);
+
+ void
+ do_context_takeover_read(role_type role);
+};
+
+template<>
+struct stream_base<false>
+{
+ // These stubs are for avoiding linking in the zlib
+ // code when permessage-deflate is not enabled.
+
+ bool
+ rd_deflated() const
+ {
+ return false;
+ }
+
+ bool
+ rd_deflated(bool rsv1)
+ {
+ return ! rsv1;
+ }
+
+ template<class ConstBufferSequence>
+ bool
+ deflate(
+ boost::asio::mutable_buffer&,
+ buffers_suffix<ConstBufferSequence>&,
+ bool,
+ std::size_t&,
+ error_code&)
+ {
+ return false;
+ }
+
+ void
+ do_context_takeover_write(role_type)
+ {
+ }
+
+ void
+ inflate(
+ zlib::z_params&,
+ zlib::Flush,
+ error_code&)
+ {
+ }
+
+ void
+ do_context_takeover_read(role_type)
+ {
+ }
+};
+
+} // detail
+} // websocket
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/websocket/detail/type_traits.hpp b/boost/beast/websocket/detail/type_traits.hpp
index 6c2806146c..6913174fd0 100644
--- a/boost/beast/websocket/detail/type_traits.hpp
+++ b/boost/beast/websocket/detail/type_traits.hpp
@@ -19,12 +19,12 @@ namespace websocket {
namespace detail {
template<class F>
-using is_RequestDecorator =
+using is_request_decorator =
typename beast::detail::is_invocable<F,
void(request_type&)>::type;
template<class F>
-using is_ResponseDecorator =
+using is_response_decorator =
typename beast::detail::is_invocable<F,
void(response_type&)>::type;
diff --git a/boost/beast/websocket/detail/utf8_checker.hpp b/boost/beast/websocket/detail/utf8_checker.hpp
index e86310f051..61c241bc19 100644
--- a/boost/beast/websocket/detail/utf8_checker.hpp
+++ b/boost/beast/websocket/detail/utf8_checker.hpp
@@ -151,13 +151,13 @@ write(std::uint8_t const* in, std::size_t size)
{
default:
BOOST_ASSERT(false);
- BOOST_BEAST_FALLTHROUGH;
+ BOOST_FALLTHROUGH;
case 1:
cp_[1] = 0x81;
- BOOST_BEAST_FALLTHROUGH;
+ BOOST_FALLTHROUGH;
case 2:
cp_[2] = 0x81;
- BOOST_BEAST_FALLTHROUGH;
+ BOOST_FALLTHROUGH;
case 3:
cp_[3] = 0x81;
break;
diff --git a/boost/beast/websocket/error.hpp b/boost/beast/websocket/error.hpp
index 139a20879d..d4e5197100 100644
--- a/boost/beast/websocket/error.hpp
+++ b/boost/beast/websocket/error.hpp
@@ -10,6 +10,7 @@
#ifndef BOOST_BEAST_WEBSOCKET_ERROR_HPP
#define BOOST_BEAST_WEBSOCKET_ERROR_HPP
+#include <boost/beast/websocket/detail/error.hpp>
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/error.hpp>
@@ -20,22 +21,230 @@ namespace websocket {
/// Error codes returned from @ref beast::websocket::stream operations.
enum class error
{
- /// Both sides performed a WebSocket close
+ /** The WebSocket stream was gracefully closed at both endpoints
+ */
closed = 1,
- /// WebSocket connection failed, protocol violation
- failed,
+/* The error codes error::failed and error::handshake_failed
+ are no longer in use. Please change your code to compare values
+ of type error_code against condition::handshake_failed
+ and condition::protocol_violation instead.
+
+ Apologies for the inconvenience.
- /// Upgrade handshake failed
- handshake_failed,
+ - VFALCO
+*/
+#if ! BOOST_BEAST_DOXYGEN
+ unused1 = 2, // failed
+ unused2 = 3, // handshake_failed
+#endif
- /// buffer overflow
+ /** The WebSocket operation caused a dynamic buffer overflow
+ */
buffer_overflow,
- /// partial deflate block
- partial_deflate_block
+ /** The WebSocket stream produced an incomplete deflate block
+ */
+ partial_deflate_block,
+
+ /** The WebSocket message exceeded the locally configured limit
+ */
+ message_too_big,
+
+ //
+ // Handshake failure errors
+ //
+ // These will compare equal to condition::handshake_failed
+ //
+
+ /** The WebSocket handshake was not HTTP/1.1
+
+ Error codes with this value will compare equal to @ref condition::handshake_failed
+ */
+ bad_http_version,
+
+ /** The WebSocket handshake method was not GET
+
+ Error codes with this value will compare equal to @ref condition::handshake_failed
+ */
+ bad_method,
+
+ /** The WebSocket handshake Host field is missing
+
+ Error codes with this value will compare equal to @ref condition::handshake_failed
+ */
+ no_host,
+
+ /** The WebSocket handshake Connection field is missing
+
+ Error codes with this value will compare equal to @ref condition::handshake_failed
+ */
+ no_connection,
+
+ /** The WebSocket handshake Connection field is missing the upgrade token
+
+ Error codes with this value will compare equal to @ref condition::handshake_failed
+ */
+ no_connection_upgrade,
+
+ /** The WebSocket handshake Upgrade field is missing
+
+ Error codes with this value will compare equal to @ref condition::handshake_failed
+ */
+ no_upgrade,
+
+ /** The WebSocket handshake Upgrade field is missing the websocket token
+
+ Error codes with this value will compare equal to @ref condition::handshake_failed
+ */
+ no_upgrade_websocket,
+
+ /** The WebSocket handshake Sec-WebSocket-Key field is missing
+
+ Error codes with this value will compare equal to @ref condition::handshake_failed
+ */
+ no_sec_key,
+
+ /** The WebSocket handshake Sec-WebSocket-Key field is invalid
+
+ Error codes with this value will compare equal to @ref condition::handshake_failed
+ */
+ bad_sec_key,
+
+ /** The WebSocket handshake Sec-WebSocket-Version field is missing
+
+ Error codes with this value will compare equal to @ref condition::handshake_failed
+ */
+ no_sec_version,
+
+ /** The WebSocket handshake Sec-WebSocket-Version field is invalid
+
+ Error codes with this value will compare equal to @ref condition::handshake_failed
+ */
+ bad_sec_version,
+
+ /** The WebSocket handshake Sec-WebSocket-Accept field is missing
+
+ Error codes with this value will compare equal to @ref condition::handshake_failed
+ */
+ no_sec_accept,
+
+ /** The WebSocket handshake Sec-WebSocket-Accept field is invalid
+
+ Error codes with this value will compare equal to @ref condition::handshake_failed
+ */
+ bad_sec_accept,
+
+ /** The WebSocket handshake was declined by the remote peer
+
+ Error codes with this value will compare equal to @ref condition::handshake_failed
+ */
+ upgrade_declined,
+
+ //
+ // Protocol errors
+ //
+ // These will compare equal to condition::protocol_violation
+ //
+
+ /** The WebSocket frame contained an illegal opcode
+
+ Error codes with this value will compare equal to @ref condition::protocol_violation
+ */
+ bad_opcode,
+
+ /** The WebSocket data frame was unexpected
+
+ Error codes with this value will compare equal to @ref condition::protocol_violation
+ */
+ bad_data_frame,
+
+ /** The WebSocket continuation frame was unexpected
+
+ Error codes with this value will compare equal to @ref condition::protocol_violation
+ */
+ bad_continuation,
+
+ /** The WebSocket frame contained illegal reserved bits
+
+ Error codes with this value will compare equal to @ref condition::protocol_violation
+ */
+ bad_reserved_bits,
+
+ /** The WebSocket control frame was fragmented
+
+ Error codes with this value will compare equal to @ref condition::protocol_violation
+ */
+ bad_control_fragment,
+
+ /** The WebSocket control frame size was invalid
+
+ Error codes with this value will compare equal to @ref condition::protocol_violation
+ */
+ bad_control_size,
+
+ /** The WebSocket frame was unmasked
+
+ Error codes with this value will compare equal to @ref condition::protocol_violation
+ */
+ bad_unmasked_frame,
+
+ /** The WebSocket frame was masked
+
+ Error codes with this value will compare equal to @ref condition::protocol_violation
+ */
+ bad_masked_frame,
+
+ /** The WebSocket frame size was not canonical
+
+ Error codes with this value will compare equal to @ref condition::protocol_violation
+ */
+ bad_size,
+
+ /** The WebSocket frame payload was not valid utf8
+
+ Error codes with this value will compare equal to @ref condition::protocol_violation
+ */
+ bad_frame_payload,
+
+ /** The WebSocket close frame reason code was invalid
+
+ Error codes with this value will compare equal to @ref condition::protocol_violation
+ */
+ bad_close_code,
+
+ /** The WebSocket close frame payload size was invalid
+
+ Error codes with this value will compare equal to @ref condition::protocol_violation
+ */
+ bad_close_size,
+
+ /** The WebSocket close frame payload was not valid utf8
+
+ Error codes with this value will compare equal to @ref condition::protocol_violation
+ */
+ bad_close_payload
};
+/// Error conditions corresponding to sets of error codes.
+enum class condition
+{
+ /** The WebSocket 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,
+
+ /** A WebSocket protocol violation occurred
+
+ This condition indicates that the remote peer on the WebSocket
+ connection sent data which violated the protocol.
+ */
+ protocol_violation
+ };
+
} // websocket
} // beast
} // boost
diff --git a/boost/beast/websocket/impl/accept.ipp b/boost/beast/websocket/impl/accept.ipp
index e52a74efa5..9daf7b311b 100644
--- a/boost/beast/websocket/impl/accept.ipp
+++ b/boost/beast/websocket/impl/accept.ipp
@@ -23,6 +23,7 @@
#include <boost/asio/associated_allocator.hpp>
#include <boost/asio/associated_executor.hpp>
#include <boost/asio/handler_continuation_hook.hpp>
+#include <boost/asio/handler_invoke_hook.hpp>
#include <boost/asio/post.hpp>
#include <boost/assert.hpp>
#include <boost/throw_exception.hpp>
@@ -34,22 +35,26 @@ namespace beast {
namespace websocket {
// Respond to an upgrade HTTP request
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class Handler>
-class stream<NextLayer>::response_op
+class stream<NextLayer, deflateSupported>::response_op
: public boost::asio::coroutine
{
struct data
{
- stream<NextLayer>& ws;
+ stream<NextLayer, deflateSupported>& ws;
+ error_code result;
response_type res;
template<class Body, class Allocator, class Decorator>
- data(Handler&, stream<NextLayer>& ws_, http::request<
- Body, http::basic_fields<Allocator>> const& req,
- Decorator const& decorator)
+ data(
+ Handler const&,
+ stream<NextLayer, deflateSupported>& ws_,
+ http::request<Body,
+ http::basic_fields<Allocator>> const& req,
+ Decorator const& decorator)
: ws(ws_)
- , res(ws_.build_response(req, decorator))
+ , res(ws_.build_response(req, decorator, result))
{
}
};
@@ -58,11 +63,11 @@ class stream<NextLayer>::response_op
public:
response_op(response_op&&) = default;
- response_op(response_op const&) = default;
+ response_op(response_op const&) = delete;
template<class DeducedHandler, class... Args>
response_op(DeducedHandler&& h,
- stream<NextLayer>& ws, Args&&... args)
+ stream<NextLayer, deflateSupported>& ws, Args&&... args)
: d_(std::forward<DeducedHandler>(h),
ws, std::forward<Args>(args)...)
{
@@ -74,16 +79,17 @@ public:
allocator_type
get_allocator() const noexcept
{
- return boost::asio::get_associated_allocator(d_.handler());
+ return (boost::asio::get_associated_allocator)(d_.handler());
}
using executor_type = boost::asio::associated_executor_t<
- Handler, decltype(std::declval<stream<NextLayer>&>().get_executor())>;
+ Handler, decltype(std::declval<
+ stream<NextLayer, deflateSupported>&>().get_executor())>;
executor_type
get_executor() const noexcept
{
- return boost::asio::get_associated_executor(
+ return (boost::asio::get_associated_executor)(
d_.handler(), d_->ws.get_executor());
}
@@ -98,12 +104,20 @@ public:
return asio_handler_is_continuation(
std::addressof(op->d_.handler()));
}
+
+ template<class Function>
+ friend
+ void asio_handler_invoke(Function&& f, response_op* op)
+ {
+ using boost::asio::asio_handler_invoke;
+ asio_handler_invoke(f, std::addressof(op->d_.handler()));
+ }
};
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class Handler>
void
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
response_op<Handler>::
operator()(
error_code ec,
@@ -116,12 +130,11 @@ operator()(
BOOST_ASIO_CORO_YIELD
http::async_write(d.ws.next_layer(),
d.res, std::move(*this));
- if(! ec && d.res.result() !=
- http::status::switching_protocols)
- ec = error::handshake_failed;
+ if(! ec)
+ ec = d.result;
if(! ec)
{
- pmd_read(d.ws.pmd_config_, d.res);
+ d.ws.do_pmd_config(d.res, is_deflate_supported{});
d.ws.open(role_type::server);
}
d_.invoke(ec);
@@ -132,18 +145,20 @@ operator()(
// read and respond to an upgrade request
//
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class Decorator, class Handler>
-class stream<NextLayer>::accept_op
+class stream<NextLayer, deflateSupported>::accept_op
: public boost::asio::coroutine
{
struct data
{
- stream<NextLayer>& ws;
+ stream<NextLayer, deflateSupported>& ws;
Decorator decorator;
http::request_parser<http::empty_body> p;
- data(Handler&, stream<NextLayer>& ws_,
- Decorator const& decorator_)
+ data(
+ Handler const&,
+ stream<NextLayer, deflateSupported>& ws_,
+ Decorator const& decorator_)
: ws(ws_)
, decorator(decorator_)
{
@@ -154,11 +169,11 @@ class stream<NextLayer>::accept_op
public:
accept_op(accept_op&&) = default;
- accept_op(accept_op const&) = default;
+ accept_op(accept_op const&) = delete;
template<class DeducedHandler, class... Args>
accept_op(DeducedHandler&& h,
- stream<NextLayer>& ws, Args&&... args)
+ stream<NextLayer, deflateSupported>& ws, Args&&... args)
: d_(std::forward<DeducedHandler>(h),
ws, std::forward<Args>(args)...)
{
@@ -170,16 +185,16 @@ public:
allocator_type
get_allocator() const noexcept
{
- return boost::asio::get_associated_allocator(d_.handler());
+ return (boost::asio::get_associated_allocator)(d_.handler());
}
using executor_type = boost::asio::associated_executor_t<
- Handler, decltype(std::declval<stream<NextLayer>&>().get_executor())>;
+ Handler, decltype(std::declval<stream<NextLayer, deflateSupported>&>().get_executor())>;
executor_type
get_executor() const noexcept
{
- return boost::asio::get_associated_executor(
+ return (boost::asio::get_associated_executor)(
d_.handler(), d_->ws.get_executor());
}
@@ -197,13 +212,21 @@ public:
return asio_handler_is_continuation(
std::addressof(op->d_.handler()));
}
+
+ template<class Function>
+ friend
+ void asio_handler_invoke(Function&& f, accept_op* op)
+ {
+ using boost::asio::asio_handler_invoke;
+ asio_handler_invoke(f, std::addressof(op->d_.handler()));
+ }
};
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class Decorator, class Handler>
template<class Buffers>
void
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
accept_op<Decorator, Handler>::
run(Buffers const& buffers)
{
@@ -228,10 +251,10 @@ run(Buffers const& buffers)
(*this)(ec);
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class Decorator, class Handler>
void
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
accept_op<Decorator, Handler>::
operator()(error_code ec, std::size_t)
{
@@ -280,9 +303,9 @@ operator()(error_code ec, std::size_t)
//------------------------------------------------------------------------------
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
void
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
accept()
{
static_assert(is_sync_stream<next_layer_type>::value,
@@ -293,15 +316,15 @@ accept()
BOOST_THROW_EXCEPTION(system_error{ec});
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class ResponseDecorator>
void
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
accept_ex(ResponseDecorator const& decorator)
{
static_assert(is_sync_stream<next_layer_type>::value,
"SyncStream requirements not met");
- static_assert(detail::is_ResponseDecorator<
+ static_assert(detail::is_response_decorator<
ResponseDecorator>::value,
"ResponseDecorator requirements not met");
error_code ec;
@@ -310,9 +333,9 @@ accept_ex(ResponseDecorator const& decorator)
BOOST_THROW_EXCEPTION(system_error{ec});
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
void
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
accept(error_code& ec)
{
static_assert(is_sync_stream<next_layer_type>::value,
@@ -321,26 +344,26 @@ accept(error_code& ec)
do_accept(&default_decorate_res, ec);
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class ResponseDecorator>
void
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
accept_ex(ResponseDecorator const& decorator, error_code& ec)
{
static_assert(is_sync_stream<next_layer_type>::value,
"SyncStream requirements not met");
- static_assert(detail::is_ResponseDecorator<
+ static_assert(detail::is_response_decorator<
ResponseDecorator>::value,
"ResponseDecorator requirements not met");
reset();
do_accept(decorator, ec);
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class ConstBufferSequence>
typename std::enable_if<! http::detail::is_header<
ConstBufferSequence>::value>::type
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
accept(ConstBufferSequence const& buffers)
{
static_assert(is_sync_stream<next_layer_type>::value,
@@ -354,13 +377,13 @@ accept(ConstBufferSequence const& buffers)
BOOST_THROW_EXCEPTION(system_error{ec});
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<
class ConstBufferSequence,
class ResponseDecorator>
typename std::enable_if<! http::detail::is_header<
ConstBufferSequence>::value>::type
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
accept_ex(
ConstBufferSequence const& buffers,
ResponseDecorator const &decorator)
@@ -370,7 +393,7 @@ accept_ex(
static_assert(boost::asio::is_const_buffer_sequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
- static_assert(detail::is_ResponseDecorator<
+ static_assert(detail::is_response_decorator<
ResponseDecorator>::value,
"ResponseDecorator requirements not met");
error_code ec;
@@ -379,11 +402,11 @@ accept_ex(
BOOST_THROW_EXCEPTION(system_error{ec});
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class ConstBufferSequence>
typename std::enable_if<! http::detail::is_header<
ConstBufferSequence>::value>::type
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
accept(
ConstBufferSequence const& buffers, error_code& ec)
{
@@ -412,13 +435,13 @@ accept(
do_accept(&default_decorate_res, ec);
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<
class ConstBufferSequence,
class ResponseDecorator>
typename std::enable_if<! http::detail::is_header<
ConstBufferSequence>::value>::type
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
accept_ex(
ConstBufferSequence const& buffers,
ResponseDecorator const& decorator,
@@ -451,10 +474,10 @@ accept_ex(
do_accept(decorator, ec);
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class Body, class Allocator>
void
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
accept(
http::request<Body,
http::basic_fields<Allocator>> const& req)
@@ -467,12 +490,12 @@ accept(
BOOST_THROW_EXCEPTION(system_error{ec});
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<
class Body, class Allocator,
class ResponseDecorator>
void
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
accept_ex(
http::request<Body,
http::basic_fields<Allocator>> const& req,
@@ -480,7 +503,7 @@ accept_ex(
{
static_assert(is_sync_stream<next_layer_type>::value,
"SyncStream requirements not met");
- static_assert(detail::is_ResponseDecorator<
+ static_assert(detail::is_response_decorator<
ResponseDecorator>::value,
"ResponseDecorator requirements not met");
error_code ec;
@@ -489,10 +512,10 @@ accept_ex(
BOOST_THROW_EXCEPTION(system_error{ec});
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class Body, class Allocator>
void
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
accept(
http::request<Body,
http::basic_fields<Allocator>> const& req,
@@ -504,12 +527,12 @@ accept(
do_accept(req, &default_decorate_res, ec);
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<
class Body, class Allocator,
class ResponseDecorator>
void
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
accept_ex(
http::request<Body,
http::basic_fields<Allocator>> const& req,
@@ -518,7 +541,7 @@ accept_ex(
{
static_assert(is_sync_stream<next_layer_type>::value,
"SyncStream requirements not met");
- static_assert(detail::is_ResponseDecorator<
+ static_assert(detail::is_response_decorator<
ResponseDecorator>::value,
"ResponseDecorator requirements not met");
reset();
@@ -527,60 +550,60 @@ accept_ex(
//------------------------------------------------------------------------------
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<
class AcceptHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(
AcceptHandler, void(error_code))
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
async_accept(
AcceptHandler&& handler)
{
static_assert(is_async_stream<next_layer_type>::value,
- "AsyncStream requirements requirements not met");
- boost::asio::async_completion<AcceptHandler,
- void(error_code)> init{handler};
+ "AsyncStream requirements not met");
+ BOOST_BEAST_HANDLER_INIT(
+ AcceptHandler, void(error_code));
reset();
accept_op<
decltype(&default_decorate_res),
BOOST_ASIO_HANDLER_TYPE(
AcceptHandler, void(error_code))>{
- init.completion_handler,
+ std::move(init.completion_handler),
*this,
&default_decorate_res}({});
return init.result.get();
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<
class ResponseDecorator,
class AcceptHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(
AcceptHandler, void(error_code))
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
async_accept_ex(
ResponseDecorator const& decorator,
AcceptHandler&& handler)
{
static_assert(is_async_stream<next_layer_type>::value,
- "AsyncStream requirements requirements not met");
- static_assert(detail::is_ResponseDecorator<
+ "AsyncStream requirements not met");
+ static_assert(detail::is_response_decorator<
ResponseDecorator>::value,
"ResponseDecorator requirements not met");
- boost::asio::async_completion<AcceptHandler,
- void(error_code)> init{handler};
+ BOOST_BEAST_HANDLER_INIT(
+ AcceptHandler, void(error_code));
reset();
accept_op<
ResponseDecorator,
BOOST_ASIO_HANDLER_TYPE(
AcceptHandler, void(error_code))>{
- init.completion_handler,
+ std::move(init.completion_handler),
*this,
decorator}({});
return init.result.get();
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<
class ConstBufferSequence,
class AcceptHandler>
@@ -588,30 +611,30 @@ typename std::enable_if<
! http::detail::is_header<ConstBufferSequence>::value,
BOOST_ASIO_INITFN_RESULT_TYPE(
AcceptHandler, void(error_code))>::type
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
async_accept(
ConstBufferSequence const& buffers,
AcceptHandler&& handler)
{
static_assert(is_async_stream<next_layer_type>::value,
- "AsyncStream requirements requirements not met");
+ "AsyncStream requirements not met");
static_assert(boost::asio::is_const_buffer_sequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
- boost::asio::async_completion<AcceptHandler,
- void(error_code)> init{handler};
+ BOOST_BEAST_HANDLER_INIT(
+ AcceptHandler, void(error_code));
reset();
accept_op<
decltype(&default_decorate_res),
BOOST_ASIO_HANDLER_TYPE(
AcceptHandler, void(error_code))>{
- init.completion_handler,
+ std::move(init.completion_handler),
*this,
&default_decorate_res}.run(buffers);
return init.result.get();
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<
class ConstBufferSequence,
class ResponseDecorator,
@@ -620,86 +643,86 @@ typename std::enable_if<
! http::detail::is_header<ConstBufferSequence>::value,
BOOST_ASIO_INITFN_RESULT_TYPE(
AcceptHandler, void(error_code))>::type
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
async_accept_ex(
ConstBufferSequence const& buffers,
ResponseDecorator const& decorator,
AcceptHandler&& handler)
{
static_assert(is_async_stream<next_layer_type>::value,
- "AsyncStream requirements requirements not met");
+ "AsyncStream requirements not met");
static_assert(boost::asio::is_const_buffer_sequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
- static_assert(detail::is_ResponseDecorator<
+ static_assert(detail::is_response_decorator<
ResponseDecorator>::value,
"ResponseDecorator requirements not met");
- boost::asio::async_completion<AcceptHandler,
- void(error_code)> init{handler};
+ BOOST_BEAST_HANDLER_INIT(
+ AcceptHandler, void(error_code));
reset();
accept_op<
ResponseDecorator,
BOOST_ASIO_HANDLER_TYPE(
AcceptHandler, void(error_code))>{
- init.completion_handler,
+ std::move(init.completion_handler),
*this,
decorator}.run(buffers);
return init.result.get();
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<
class Body, class Allocator,
class AcceptHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(
AcceptHandler, void(error_code))
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
async_accept(
http::request<Body, http::basic_fields<Allocator>> const& req,
AcceptHandler&& handler)
{
static_assert(is_async_stream<next_layer_type>::value,
- "AsyncStream requirements requirements not met");
- boost::asio::async_completion<AcceptHandler,
- void(error_code)> init{handler};
+ "AsyncStream requirements not met");
+ BOOST_BEAST_HANDLER_INIT(
+ AcceptHandler, void(error_code));
reset();
using boost::asio::asio_handler_is_continuation;
response_op<
BOOST_ASIO_HANDLER_TYPE(
AcceptHandler, void(error_code))>{
- init.completion_handler,
+ std::move(init.completion_handler),
*this,
req,
&default_decorate_res}();
return init.result.get();
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<
class Body, class Allocator,
class ResponseDecorator,
class AcceptHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(
AcceptHandler, void(error_code))
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
async_accept_ex(
http::request<Body, http::basic_fields<Allocator>> const& req,
ResponseDecorator const& decorator,
AcceptHandler&& handler)
{
static_assert(is_async_stream<next_layer_type>::value,
- "AsyncStream requirements requirements not met");
- static_assert(detail::is_ResponseDecorator<
+ "AsyncStream requirements not met");
+ static_assert(detail::is_response_decorator<
ResponseDecorator>::value,
"ResponseDecorator requirements not met");
- boost::asio::async_completion<AcceptHandler,
- void(error_code)> init{handler};
+ BOOST_BEAST_HANDLER_INIT(
+ AcceptHandler, void(error_code));
reset();
using boost::asio::asio_handler_is_continuation;
response_op<
BOOST_ASIO_HANDLER_TYPE(
AcceptHandler, void(error_code))>{
- init.completion_handler,
+ std::move(init.completion_handler),
*this,
req,
decorator}();
@@ -708,10 +731,10 @@ async_accept_ex(
//------------------------------------------------------------------------------
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class Decorator>
void
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
do_accept(
Decorator const& decorator,
error_code& ec)
@@ -725,28 +748,30 @@ do_accept(
do_accept(p.get(), decorator, ec);
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class Body, class Allocator,
class Decorator>
void
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
do_accept(
- http::request<Body,http::basic_fields<Allocator>> const& req,
+ http::request<Body,
+ http::basic_fields<Allocator>> const& req,
Decorator const& decorator,
error_code& ec)
{
- auto const res = build_response(req, decorator);
+ error_code result;
+ auto const res = build_response(req, decorator, result);
http::write(stream_, res, ec);
if(ec)
return;
- if(res.result() != http::status::switching_protocols)
+ ec = result;
+ if(ec)
{
- ec = error::handshake_failed;
// VFALCO TODO Respect keep alive setting, perform
// teardown if Connection: close.
return;
}
- pmd_read(pmd_config_, res);
+ do_pmd_config(res, is_deflate_supported{});
open(role_type::server);
}
diff --git a/boost/beast/websocket/impl/close.ipp b/boost/beast/websocket/impl/close.ipp
index 0349dd1409..7b0e1ff648 100644
--- a/boost/beast/websocket/impl/close.ipp
+++ b/boost/beast/websocket/impl/close.ipp
@@ -19,6 +19,7 @@
#include <boost/asio/associated_executor.hpp>
#include <boost/asio/coroutine.hpp>
#include <boost/asio/handler_continuation_hook.hpp>
+#include <boost/asio/handler_invoke_hook.hpp>
#include <boost/asio/post.hpp>
#include <boost/throw_exception.hpp>
#include <memory>
@@ -34,25 +35,23 @@ namespace websocket {
frame. Finally it invokes the teardown operation to shut down the
underlying connection.
*/
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class Handler>
-class stream<NextLayer>::close_op
+class stream<NextLayer, deflateSupported>::close_op
: public boost::asio::coroutine
{
struct state
{
- stream<NextLayer>& ws;
+ stream<NextLayer, deflateSupported>& ws;
detail::frame_buffer fb;
error_code ev;
- token tok;
bool cont = false;
state(
- Handler&,
- stream<NextLayer>& ws_,
+ Handler const&,
+ stream<NextLayer, deflateSupported>& ws_,
close_reason const& cr)
: ws(ws_)
- , tok(ws.tok_.unique())
{
// Serialize the close frame
ws.template write_close<
@@ -63,13 +62,15 @@ class stream<NextLayer>::close_op
handler_ptr<state, Handler> d_;
public:
+ static constexpr int id = 4; // for soft_mutex
+
close_op(close_op&&) = default;
- close_op(close_op const&) = default;
+ close_op(close_op const&) = delete;
template<class DeducedHandler>
close_op(
DeducedHandler&& h,
- stream<NextLayer>& ws,
+ stream<NextLayer, deflateSupported>& ws,
close_reason const& cr)
: d_(std::forward<DeducedHandler>(h), ws, cr)
{
@@ -81,16 +82,16 @@ public:
allocator_type
get_allocator() const noexcept
{
- return boost::asio::get_associated_allocator(d_.handler());
+ return (boost::asio::get_associated_allocator)(d_.handler());
}
using executor_type = boost::asio::associated_executor_t<
- Handler, decltype(std::declval<stream<NextLayer>&>().get_executor())>;
+ Handler, decltype(std::declval<stream<NextLayer, deflateSupported>&>().get_executor())>;
executor_type
get_executor() const noexcept
{
- return boost::asio::get_associated_executor(
+ return (boost::asio::get_associated_executor)(
d_.handler(), d_->ws.get_executor());
}
@@ -107,12 +108,21 @@ public:
return op->d_->cont || asio_handler_is_continuation(
std::addressof(op->d_.handler()));
}
+
+ template<class Function>
+ friend
+ void asio_handler_invoke(Function&& f, close_op* op)
+ {
+ using boost::asio::asio_handler_invoke;
+ asio_handler_invoke(f,
+ std::addressof(op->d_.handler()));
+ }
};
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class Handler>
void
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
close_op<Handler>::
operator()(
error_code ec,
@@ -121,16 +131,12 @@ operator()(
{
using beast::detail::clamp;
auto& d = *d_;
- close_code code{};
d.cont = cont;
BOOST_ASIO_CORO_REENTER(*this)
{
// Maybe suspend
- if(! d.ws.wr_block_)
+ if(d.ws.wr_block_.try_lock(this))
{
- // Acquire the write block
- d.ws.wr_block_ = d.tok;
-
// Make sure the stream is open
if(! d.ws.check_open(ec))
goto upcall;
@@ -138,19 +144,17 @@ operator()(
else
{
// Suspend
- BOOST_ASSERT(d.ws.wr_block_ != d.tok);
BOOST_ASIO_CORO_YIELD
d.ws.paused_close_.emplace(std::move(*this));
// Acquire the write block
- BOOST_ASSERT(! d.ws.wr_block_);
- d.ws.wr_block_ = d.tok;
+ d.ws.wr_block_.lock(this);
// Resume
BOOST_ASIO_CORO_YIELD
boost::asio::post(
d.ws.get_executor(), std::move(*this));
- BOOST_ASSERT(d.ws.wr_block_ == d.tok);
+ BOOST_ASSERT(d.ws.wr_block_.is_locked(this));
// Make sure the stream is open
if(! d.ws.check_open(ec))
@@ -181,27 +185,20 @@ operator()(
}
// Maybe suspend
- if(! d.ws.rd_block_)
- {
- // Acquire the read block
- d.ws.rd_block_ = d.tok;
- }
- else
+ if(! d.ws.rd_block_.try_lock(this))
{
// Suspend
- BOOST_ASSERT(d.ws.rd_block_ != d.tok);
BOOST_ASIO_CORO_YIELD
d.ws.paused_r_close_.emplace(std::move(*this));
// Acquire the read block
- BOOST_ASSERT(! d.ws.rd_block_);
- d.ws.rd_block_ = d.tok;
+ d.ws.rd_block_.lock(this);
// Resume
BOOST_ASIO_CORO_YIELD
boost::asio::post(
d.ws.get_executor(), std::move(*this));
- BOOST_ASSERT(d.ws.rd_block_ == d.tok);
+ BOOST_ASSERT(d.ws.rd_block_.is_locked(this));
// Make sure the stream is open
BOOST_ASSERT(d.ws.status_ != status::open);
@@ -219,13 +216,10 @@ operator()(
{
// Read frame header
while(! d.ws.parse_fh(
- d.ws.rd_fh_, d.ws.rd_buf_, code))
+ d.ws.rd_fh_, d.ws.rd_buf_, d.ev))
{
- if(code != close_code::none)
- {
- d.ev = error::failed;
+ if(d.ev)
goto teardown;
- }
BOOST_ASIO_CORO_YIELD
d.ws.stream_.async_read_some(
d.ws.rd_buf_.prepare(read_size(d.ws.rd_buf_,
@@ -244,16 +238,12 @@ operator()(
d.ws.rd_close_ = true;
auto const mb = buffers_prefix(
clamp(d.ws.rd_fh_.len),
- d.ws.rd_buf_.data());
+ d.ws.rd_buf_.mutable_data());
if(d.ws.rd_fh_.len > 0 && d.ws.rd_fh_.mask)
detail::mask_inplace(mb, d.ws.rd_key_);
- detail::read_close(d.ws.cr_, mb, code);
- if(code != close_code::none)
- {
- // Protocol error
- d.ev = error::failed;
+ detail::read_close(d.ws.cr_, mb, d.ev);
+ if(d.ev)
goto teardown;
- }
d.ws.rd_buf_.consume(clamp(d.ws.rd_fh_.len));
goto teardown;
}
@@ -283,12 +273,12 @@ operator()(
teardown:
// Teardown
- BOOST_ASSERT(d.ws.wr_block_ == d.tok);
+ BOOST_ASSERT(d.ws.wr_block_.is_locked(this));
using beast::websocket::async_teardown;
BOOST_ASIO_CORO_YIELD
async_teardown(d.ws.role_,
d.ws.stream_, std::move(*this));
- BOOST_ASSERT(d.ws.wr_block_ == d.tok);
+ BOOST_ASSERT(d.ws.wr_block_.is_locked(this));
if(ec == boost::asio::error::eof)
{
// Rationale:
@@ -304,13 +294,10 @@ operator()(
d.ws.close();
upcall:
- BOOST_ASSERT(d.ws.wr_block_ == d.tok);
- d.ws.wr_block_.reset();
- if(d.ws.rd_block_ == d.tok)
- {
- d.ws.rd_block_.reset();
+ BOOST_ASSERT(d.ws.wr_block_.is_locked(this));
+ d.ws.wr_block_.unlock(this);
+ if(d.ws.rd_block_.try_unlock(this))
d.ws.paused_r_rd_.maybe_invoke();
- }
d.ws.paused_rd_.maybe_invoke() ||
d.ws.paused_ping_.maybe_invoke() ||
d.ws.paused_wr_.maybe_invoke();
@@ -327,9 +314,9 @@ operator()(
//------------------------------------------------------------------------------
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
void
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
close(close_reason const& cr)
{
static_assert(is_sync_stream<next_layer_type>::value,
@@ -340,9 +327,9 @@ close(close_reason const& cr)
BOOST_THROW_EXCEPTION(system_error{ec});
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
void
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
close(close_reason const& cr, error_code& ec)
{
static_assert(is_sync_stream<next_layer_type>::value,
@@ -364,18 +351,18 @@ close(close_reason const& cr, error_code& ec)
if(! check_ok(ec))
return;
status_ = status::closing;
+ error_code result;
// 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))
+ while(! parse_fh(rd_fh_, rd_buf_, result))
{
- if(code != close_code::none)
- return do_fail(close_code::none,
- error::failed, ec);
+ if(result)
+ return do_fail(
+ close_code::none, result, ec);
auto const bytes_transferred =
stream_.read_some(
rd_buf_.prepare(read_size(rd_buf_,
@@ -393,15 +380,15 @@ close(close_reason const& cr, error_code& ec)
rd_close_ = true;
auto const mb = buffers_prefix(
clamp(rd_fh_.len),
- rd_buf_.data());
+ 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)
+ detail::read_close(cr_, mb, result);
+ if(result)
{
- // Protocol error
- return do_fail(close_code::none,
- error::failed, ec);
+ // Protocol violation
+ return do_fail(
+ close_code::none, result, ec);
}
rd_buf_.consume(clamp(rd_fh_.len));
break;
@@ -434,20 +421,20 @@ close(close_reason const& cr, error_code& ec)
ec.assign(0, ec.category());
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class CloseHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(
CloseHandler, void(error_code))
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
async_close(close_reason const& cr, CloseHandler&& handler)
{
static_assert(is_async_stream<next_layer_type>::value,
"AsyncStream requirements not met");
- boost::asio::async_completion<CloseHandler,
- void(error_code)> init{handler};
+ BOOST_BEAST_HANDLER_INIT(
+ CloseHandler, void(error_code));
close_op<BOOST_ASIO_HANDLER_TYPE(
CloseHandler, void(error_code))>{
- init.completion_handler, *this, cr}(
+ std::move(init.completion_handler), *this, cr}(
{}, 0, false);
return init.result.get();
}
diff --git a/boost/beast/websocket/impl/error.ipp b/boost/beast/websocket/impl/error.ipp
index ed18829cfe..56a20e2551 100644
--- a/boost/beast/websocket/impl/error.ipp
+++ b/boost/beast/websocket/impl/error.ipp
@@ -11,71 +11,128 @@
#define BOOST_BEAST_WEBSOCKET_IMPL_ERROR_IPP
namespace boost {
-
-namespace system {
-template<>
-struct is_error_code_enum<beast::websocket::error>
-{
- static bool const value = true;
-};
-} // system
-
namespace beast {
namespace websocket {
namespace detail {
-class websocket_error_category : public error_category
+inline
+const char*
+error_codes::
+name() const noexcept
{
-public:
- const char*
- name() const noexcept override
- {
- return "boost.beast.websocket";
- }
+ return "boost.beast.websocket";
+}
- std::string
- message(int ev) const override
+inline
+std::string
+error_codes::
+message(int ev) const
+{
+ switch(static_cast<error>(ev))
{
- switch(static_cast<error>(ev))
- {
- default:
- case error::failed: return "WebSocket connection failed due to a protocol violation";
- case error::closed: return "WebSocket connection closed normally";
- case error::handshake_failed: return "WebSocket upgrade handshake failed";
- case error::buffer_overflow: return "WebSocket dynamic buffer overflow";
- case error::partial_deflate_block: return "WebSocket partial deflate block";
- }
- }
+ default:
+ case error::closed: return "The WebSocket stream was gracefully closed at both endpoints";
+ case error::buffer_overflow: return "The WebSocket operation caused a dynamic buffer overflow";
+ case error::partial_deflate_block: return "The WebSocket stream produced an incomplete deflate block";
+ case error::message_too_big: return "The WebSocket message exceeded the locally configured limit";
- error_condition
- default_error_condition(int ev) const noexcept override
- {
- return error_condition(ev, *this);
- }
+ case error::bad_http_version: return "The WebSocket handshake was not HTTP/1.1";
+ case error::bad_method: return "The WebSocket handshake method was not GET";
+ case error::no_host: return "The WebSocket handshake Host field is missing";
+ case error::no_connection: return "The WebSocket handshake Connection field is missing";
+ case error::no_connection_upgrade: return "The WebSocket handshake Connection field is missing the upgrade token";
+ case error::no_upgrade: return "The WebSocket handshake Upgrade field is missing";
+ case error::no_upgrade_websocket: return "The WebSocket handshake Upgrade field is missing the websocket token";
+ case error::no_sec_key: return "The WebSocket handshake Sec-WebSocket-Key field is missing";
+ case error::bad_sec_key: return "The WebSocket handshake Sec-WebSocket-Key field is invalid";
+ case error::no_sec_version: return "The WebSocket handshake Sec-WebSocket-Version field is missing";
+ case error::bad_sec_version: return "The WebSocket handshake Sec-WebSocket-Version field is invalid";
+ case error::no_sec_accept: return "The WebSocket handshake Sec-WebSocket-Accept field is missing";
+ case error::bad_sec_accept: return "The WebSocket handshake Sec-WebSocket-Accept field is invalid";
+ case error::upgrade_declined: return "The WebSocket handshake was declined by the remote peer";
- bool
- equivalent(int ev,
- error_condition const& condition
- ) const noexcept override
- {
- return condition.value() == ev &&
- &condition.category() == this;
+ case error::bad_opcode: return "The WebSocket frame contained an illegal opcode";
+ case error::bad_data_frame: return "The WebSocket data frame was unexpected";
+ case error::bad_continuation: return "The WebSocket continuation frame was unexpected";
+ case error::bad_reserved_bits: return "The WebSocket frame contained illegal reserved bits";
+ case error::bad_control_fragment: return "The WebSocket control frame was fragmented";
+ case error::bad_control_size: return "The WebSocket control frame size was invalid";
+ case error::bad_unmasked_frame: return "The WebSocket frame was unmasked";
+ case error::bad_masked_frame: return "The WebSocket frame was masked";
+ case error::bad_size: return "The WebSocket frame size was not canonical";
+ case error::bad_frame_payload: return "The WebSocket frame payload was not valid utf8";
+ case error::bad_close_code: return "The WebSocket close frame reason code was invalid";
+ case error::bad_close_size: return "The WebSocket close frame payload size was invalid";
+ case error::bad_close_payload: return "The WebSocket close frame payload was not valid utf8";
}
+}
- bool
- equivalent(error_code const& error, int ev) const noexcept override
+inline
+error_condition
+error_codes::
+default_error_condition(int ev) const noexcept
+{
+ switch(static_cast<error>(ev))
{
- return error.value() == ev &&
- &error.category() == this;
+ default:
+ case error::closed:
+ case error::buffer_overflow:
+ case error::partial_deflate_block:
+ case error::message_too_big:
+ return {ev, *this};
+
+ case error::bad_http_version:
+ case error::bad_method:
+ case error::no_host:
+ case error::no_connection:
+ case error::no_connection_upgrade:
+ case error::no_upgrade:
+ case error::no_upgrade_websocket:
+ case error::no_sec_key:
+ case error::bad_sec_key:
+ case error::no_sec_version:
+ case error::bad_sec_version:
+ case error::no_sec_accept:
+ case error::bad_sec_accept:
+ case error::upgrade_declined:
+ return condition::handshake_failed;
+
+ case error::bad_opcode:
+ case error::bad_data_frame:
+ case error::bad_continuation:
+ case error::bad_reserved_bits:
+ case error::bad_control_fragment:
+ case error::bad_control_size:
+ case error::bad_unmasked_frame:
+ case error::bad_masked_frame:
+ case error::bad_size:
+ case error::bad_frame_payload:
+ case error::bad_close_code:
+ case error::bad_close_size:
+ case error::bad_close_payload:
+ return condition::protocol_violation;
}
-};
+}
+
+inline
+const char*
+error_conditions::
+name() const noexcept
+{
+ return "boost.beast.websocket";
+}
inline
-error_category const&
-get_error_category()
+std::string
+error_conditions::
+message(int cv) const
{
- static detail::websocket_error_category const cat{};
- return cat;
+ switch(static_cast<condition>(cv))
+ {
+ default:
+ case condition::handshake_failed: return "The WebSocket handshake failed";
+ case condition::protocol_violation: return "A WebSocket protocol violation occurred";
+ }
}
} // detail
@@ -84,9 +141,18 @@ inline
error_code
make_error_code(error e)
{
- return error_code(
- static_cast<std::underlying_type<error>::type>(e),
- detail::get_error_category());
+ static detail::error_codes const cat{};
+ return error_code{static_cast<
+ std::underlying_type<error>::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<condition>::type>(c), cat};
}
} // websocket
diff --git a/boost/beast/websocket/impl/handshake.ipp b/boost/beast/websocket/impl/handshake.ipp
index cdd8d47342..8e33e1a890 100644
--- a/boost/beast/websocket/impl/handshake.ipp
+++ b/boost/beast/websocket/impl/handshake.ipp
@@ -21,6 +21,7 @@
#include <boost/asio/associated_executor.hpp>
#include <boost/asio/coroutine.hpp>
#include <boost/asio/handler_continuation_hook.hpp>
+#include <boost/asio/handler_invoke_hook.hpp>
#include <boost/assert.hpp>
#include <boost/throw_exception.hpp>
#include <memory>
@@ -33,25 +34,27 @@ namespace websocket {
// send the upgrade request and process the response
//
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class Handler>
-class stream<NextLayer>::handshake_op
+class stream<NextLayer, deflateSupported>::handshake_op
: public boost::asio::coroutine
{
struct data
{
- stream<NextLayer>& ws;
+ stream<NextLayer, deflateSupported>& ws;
response_type* res_p;
detail::sec_ws_key_type key;
http::request<http::empty_body> req;
response_type res;
template<class Decorator>
- data(Handler&, stream<NextLayer>& ws_,
+ data(
+ Handler const&,
+ stream<NextLayer, deflateSupported>& ws_,
response_type* res_p_,
- string_view host,
- string_view target,
- Decorator const& decorator)
+ string_view host,
+ string_view target,
+ Decorator const& decorator)
: ws(ws_)
, res_p(res_p_)
, req(ws.build_request(key,
@@ -65,11 +68,11 @@ class stream<NextLayer>::handshake_op
public:
handshake_op(handshake_op&&) = default;
- handshake_op(handshake_op const&) = default;
+ handshake_op(handshake_op const&) = delete;
template<class DeducedHandler, class... Args>
handshake_op(DeducedHandler&& h,
- stream<NextLayer>& ws, Args&&... args)
+ stream<NextLayer, deflateSupported>& ws, Args&&... args)
: d_(std::forward<DeducedHandler>(h),
ws, std::forward<Args>(args)...)
{
@@ -81,16 +84,16 @@ public:
allocator_type
get_allocator() const noexcept
{
- return boost::asio::get_associated_allocator(d_.handler());
+ return (boost::asio::get_associated_allocator)(d_.handler());
}
using executor_type = boost::asio::associated_executor_t<
- Handler, decltype(std::declval<stream<NextLayer>&>().get_executor())>;
+ Handler, decltype(std::declval<stream<NextLayer, deflateSupported>&>().get_executor())>;
executor_type
get_executor() const noexcept
{
- return boost::asio::get_associated_executor(
+ return (boost::asio::get_associated_executor)(
d_.handler(), d_->ws.get_executor());
}
@@ -106,19 +109,29 @@ public:
return asio_handler_is_continuation(
std::addressof(op->d_.handler()));
}
+
+ template<class Function>
+ friend
+ void asio_handler_invoke(Function&& f, handshake_op* op)
+ {
+ using boost::asio::asio_handler_invoke;
+ asio_handler_invoke(f,
+ std::addressof(op->d_.handler()));
+ }
};
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class Handler>
void
-stream<NextLayer>::handshake_op<Handler>::
+stream<NextLayer, deflateSupported>::
+handshake_op<Handler>::
operator()(error_code ec, std::size_t)
{
auto& d = *d_;
BOOST_ASIO_CORO_REENTER(*this)
{
// Send HTTP Upgrade
- pmd_read(d.ws.pmd_config_, d.req);
+ d.ws.do_pmd_config(d.req, is_deflate_supported{});
BOOST_ASIO_CORO_YIELD
http::async_write(d.ws.stream_,
d.req, std::move(*this));
@@ -146,31 +159,31 @@ operator()(error_code ec, std::size_t)
}
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class HandshakeHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(
HandshakeHandler, void(error_code))
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
async_handshake(string_view host,
string_view target,
HandshakeHandler&& handler)
{
static_assert(is_async_stream<next_layer_type>::value,
"AsyncStream requirements not met");
- boost::asio::async_completion<HandshakeHandler,
- void(error_code)> init{handler};
+ BOOST_BEAST_HANDLER_INIT(
+ HandshakeHandler, void(error_code));
handshake_op<BOOST_ASIO_HANDLER_TYPE(
HandshakeHandler, void(error_code))>{
- init.completion_handler, *this, nullptr, host,
+ std::move(init.completion_handler), *this, nullptr, host,
target, &default_decorate_req}();
return init.result.get();
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class HandshakeHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(
HandshakeHandler, void(error_code))
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
async_handshake(response_type& res,
string_view host,
string_view target,
@@ -178,20 +191,20 @@ async_handshake(response_type& res,
{
static_assert(is_async_stream<next_layer_type>::value,
"AsyncStream requirements not met");
- boost::asio::async_completion<HandshakeHandler,
- void(error_code)> init{handler};
+ BOOST_BEAST_HANDLER_INIT(
+ HandshakeHandler, void(error_code));
handshake_op<BOOST_ASIO_HANDLER_TYPE(
HandshakeHandler, void(error_code))>{
- init.completion_handler, *this, &res, host,
+ std::move(init.completion_handler), *this, &res, host,
target, &default_decorate_req}();
return init.result.get();
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class RequestDecorator, class HandshakeHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(
HandshakeHandler, void(error_code))
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
async_handshake_ex(string_view host,
string_view target,
RequestDecorator const& decorator,
@@ -199,23 +212,23 @@ async_handshake_ex(string_view host,
{
static_assert(is_async_stream<next_layer_type>::value,
"AsyncStream requirements not met");
- static_assert(detail::is_RequestDecorator<
+ static_assert(detail::is_request_decorator<
RequestDecorator>::value,
"RequestDecorator requirements not met");
- boost::asio::async_completion<HandshakeHandler,
- void(error_code)> init{handler};
+ BOOST_BEAST_HANDLER_INIT(
+ HandshakeHandler, void(error_code));
handshake_op<BOOST_ASIO_HANDLER_TYPE(
HandshakeHandler, void(error_code))>{
- init.completion_handler, *this, nullptr, host,
+ std::move(init.completion_handler), *this, nullptr, host,
target, decorator}();
return init.result.get();
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class RequestDecorator, class HandshakeHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(
HandshakeHandler, void(error_code))
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
async_handshake_ex(response_type& res,
string_view host,
string_view target,
@@ -224,21 +237,21 @@ async_handshake_ex(response_type& res,
{
static_assert(is_async_stream<next_layer_type>::value,
"AsyncStream requirements not met");
- static_assert(detail::is_RequestDecorator<
+ static_assert(detail::is_request_decorator<
RequestDecorator>::value,
"RequestDecorator requirements not met");
- boost::asio::async_completion<HandshakeHandler,
- void(error_code)> init{handler};
+ BOOST_BEAST_HANDLER_INIT(
+ HandshakeHandler, void(error_code));
handshake_op<BOOST_ASIO_HANDLER_TYPE(
HandshakeHandler, void(error_code))>{
- init.completion_handler, *this, &res, host,
+ std::move(init.completion_handler), *this, &res, host,
target, decorator}();
return init.result.get();
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
void
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
handshake(string_view host,
string_view target)
{
@@ -251,9 +264,9 @@ handshake(string_view host,
BOOST_THROW_EXCEPTION(system_error{ec});
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
void
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
handshake(response_type& res,
string_view host,
string_view target)
@@ -266,17 +279,17 @@ handshake(response_type& res,
BOOST_THROW_EXCEPTION(system_error{ec});
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class RequestDecorator>
void
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
handshake_ex(string_view host,
string_view target,
RequestDecorator const& decorator)
{
static_assert(is_sync_stream<next_layer_type>::value,
"SyncStream requirements not met");
- static_assert(detail::is_RequestDecorator<
+ static_assert(detail::is_request_decorator<
RequestDecorator>::value,
"RequestDecorator requirements not met");
error_code ec;
@@ -285,10 +298,10 @@ handshake_ex(string_view host,
BOOST_THROW_EXCEPTION(system_error{ec});
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class RequestDecorator>
void
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
handshake_ex(response_type& res,
string_view host,
string_view target,
@@ -296,7 +309,7 @@ handshake_ex(response_type& res,
{
static_assert(is_sync_stream<next_layer_type>::value,
"SyncStream requirements not met");
- static_assert(detail::is_RequestDecorator<
+ static_assert(detail::is_request_decorator<
RequestDecorator>::value,
"RequestDecorator requirements not met");
error_code ec;
@@ -305,9 +318,9 @@ handshake_ex(response_type& res,
BOOST_THROW_EXCEPTION(system_error{ec});
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
void
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
handshake(string_view host,
string_view target, error_code& ec)
{
@@ -317,9 +330,9 @@ handshake(string_view host,
host, target, &default_decorate_req, ec);
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
void
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
handshake(response_type& res,
string_view host,
string_view target,
@@ -331,10 +344,10 @@ handshake(response_type& res,
host, target, &default_decorate_req, ec);
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class RequestDecorator>
void
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
handshake_ex(string_view host,
string_view target,
RequestDecorator const& decorator,
@@ -342,17 +355,17 @@ handshake_ex(string_view host,
{
static_assert(is_sync_stream<next_layer_type>::value,
"SyncStream requirements not met");
- static_assert(detail::is_RequestDecorator<
+ static_assert(detail::is_request_decorator<
RequestDecorator>::value,
"RequestDecorator requirements not met");
do_handshake(nullptr,
host, target, decorator, ec);
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class RequestDecorator>
void
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
handshake_ex(response_type& res,
string_view host,
string_view target,
@@ -361,7 +374,7 @@ handshake_ex(response_type& res,
{
static_assert(is_sync_stream<next_layer_type>::value,
"SyncStream requirements not met");
- static_assert(detail::is_RequestDecorator<
+ static_assert(detail::is_request_decorator<
RequestDecorator>::value,
"RequestDecorator requirements not met");
do_handshake(&res,
@@ -370,10 +383,10 @@ handshake_ex(response_type& res,
//------------------------------------------------------------------------------
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class RequestDecorator>
void
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
do_handshake(
response_type* res_p,
string_view host,
@@ -387,7 +400,7 @@ do_handshake(
{
auto const req = build_request(
key, host, target, decorator);
- pmd_read(pmd_config_, req);
+ do_pmd_config(req, is_deflate_supported{});
http::write(stream_, req, ec);
}
if(ec)
diff --git a/boost/beast/websocket/impl/ping.ipp b/boost/beast/websocket/impl/ping.ipp
index 79003261de..c7deb9c37c 100644
--- a/boost/beast/websocket/impl/ping.ipp
+++ b/boost/beast/websocket/impl/ping.ipp
@@ -19,6 +19,7 @@
#include <boost/asio/associated_executor.hpp>
#include <boost/asio/coroutine.hpp>
#include <boost/asio/handler_continuation_hook.hpp>
+#include <boost/asio/handler_invoke_hook.hpp>
#include <boost/asio/post.hpp>
#include <boost/throw_exception.hpp>
#include <memory>
@@ -32,24 +33,22 @@ namespace websocket {
It only sends the frames it does not make attempts to read
any frame data.
*/
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class Handler>
-class stream<NextLayer>::ping_op
+class stream<NextLayer, deflateSupported>::ping_op
: public boost::asio::coroutine
{
struct state
{
- stream<NextLayer>& ws;
+ stream<NextLayer, deflateSupported>& ws;
detail::frame_buffer fb;
- token tok;
state(
- Handler&,
- stream<NextLayer>& ws_,
+ Handler const&,
+ stream<NextLayer, deflateSupported>& ws_,
detail::opcode op,
ping_data const& payload)
: ws(ws_)
- , tok(ws.tok_.unique())
{
// Serialize the control frame
ws.template write_ping<
@@ -61,13 +60,15 @@ class stream<NextLayer>::ping_op
handler_ptr<state, Handler> d_;
public:
+ static constexpr int id = 3; // for soft_mutex
+
ping_op(ping_op&&) = default;
- ping_op(ping_op const&) = default;
+ ping_op(ping_op const&) = delete;
template<class DeducedHandler>
ping_op(
DeducedHandler&& h,
- stream<NextLayer>& ws,
+ stream<NextLayer, deflateSupported>& ws,
detail::opcode op,
ping_data const& payload)
: d_(std::forward<DeducedHandler>(h),
@@ -81,16 +82,16 @@ public:
allocator_type
get_allocator() const noexcept
{
- return boost::asio::get_associated_allocator(d_.handler());
+ return (boost::asio::get_associated_allocator)(d_.handler());
}
using executor_type = boost::asio::associated_executor_t<
- Handler, decltype(std::declval<stream<NextLayer>&>().get_executor())>;
+ Handler, decltype(std::declval<stream<NextLayer, deflateSupported>&>().get_executor())>;
executor_type
get_executor() const noexcept
{
- return boost::asio::get_associated_executor(
+ return (boost::asio::get_associated_executor)(
d_.handler(), d_->ws.get_executor());
}
@@ -105,12 +106,21 @@ public:
return asio_handler_is_continuation(
std::addressof(op->d_.handler()));
}
+
+ template<class Function>
+ friend
+ void asio_handler_invoke(Function&& f, ping_op* op)
+ {
+ using boost::asio::asio_handler_invoke;
+ asio_handler_invoke(
+ f, std::addressof(op->d_.handler()));
+ }
};
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class Handler>
void
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
ping_op<Handler>::
operator()(error_code ec, std::size_t)
{
@@ -118,11 +128,8 @@ operator()(error_code ec, std::size_t)
BOOST_ASIO_CORO_REENTER(*this)
{
// Maybe suspend
- if(! d.ws.wr_block_)
+ if(d.ws.wr_block_.try_lock(this))
{
- // Acquire the write block
- d.ws.wr_block_ = d.tok;
-
// Make sure the stream is open
if(! d.ws.check_open(ec))
{
@@ -136,19 +143,17 @@ operator()(error_code ec, std::size_t)
else
{
// Suspend
- BOOST_ASSERT(d.ws.wr_block_ != d.tok);
BOOST_ASIO_CORO_YIELD
d.ws.paused_ping_.emplace(std::move(*this));
// Acquire the write block
- BOOST_ASSERT(! d.ws.wr_block_);
- d.ws.wr_block_ = d.tok;
+ d.ws.wr_block_.lock(this);
// Resume
BOOST_ASIO_CORO_YIELD
boost::asio::post(
d.ws.get_executor(), std::move(*this));
- BOOST_ASSERT(d.ws.wr_block_ == d.tok);
+ BOOST_ASSERT(d.ws.wr_block_.is_locked(this));
// Make sure the stream is open
if(! d.ws.check_open(ec))
@@ -163,8 +168,7 @@ operator()(error_code ec, std::size_t)
goto upcall;
upcall:
- BOOST_ASSERT(d.ws.wr_block_ == d.tok);
- d.ws.wr_block_.reset();
+ d.ws.wr_block_.unlock(this);
d.ws.paused_close_.maybe_invoke() ||
d.ws.paused_rd_.maybe_invoke() ||
d.ws.paused_wr_.maybe_invoke();
@@ -174,9 +178,9 @@ operator()(error_code ec, std::size_t)
//------------------------------------------------------------------------------
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
void
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
ping(ping_data const& payload)
{
error_code ec;
@@ -185,9 +189,9 @@ ping(ping_data const& payload)
BOOST_THROW_EXCEPTION(system_error{ec});
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
void
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
ping(ping_data const& payload, error_code& ec)
{
// Make sure the stream is open
@@ -201,9 +205,9 @@ ping(ping_data const& payload, error_code& ec)
return;
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
void
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
pong(ping_data const& payload)
{
error_code ec;
@@ -212,9 +216,9 @@ pong(ping_data const& payload)
BOOST_THROW_EXCEPTION(system_error{ec});
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
void
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
pong(ping_data const& payload, error_code& ec)
{
// Make sure the stream is open
@@ -228,38 +232,38 @@ pong(ping_data const& payload, error_code& ec)
return;
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class WriteHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(
WriteHandler, void(error_code))
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
async_ping(ping_data const& payload, WriteHandler&& handler)
{
static_assert(is_async_stream<next_layer_type>::value,
- "AsyncStream requirements requirements not met");
- boost::asio::async_completion<WriteHandler,
- void(error_code)> init{handler};
+ "AsyncStream requirements not met");
+ BOOST_BEAST_HANDLER_INIT(
+ WriteHandler, void(error_code));
ping_op<BOOST_ASIO_HANDLER_TYPE(
WriteHandler, void(error_code))>{
- init.completion_handler, *this,
+ std::move(init.completion_handler), *this,
detail::opcode::ping, payload}();
return init.result.get();
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class WriteHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(
WriteHandler, void(error_code))
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
async_pong(ping_data const& payload, WriteHandler&& handler)
{
static_assert(is_async_stream<next_layer_type>::value,
- "AsyncStream requirements requirements not met");
- boost::asio::async_completion<WriteHandler,
- void(error_code)> init{handler};
+ "AsyncStream requirements not met");
+ BOOST_BEAST_HANDLER_INIT(
+ WriteHandler, void(error_code));
ping_op<BOOST_ASIO_HANDLER_TYPE(
WriteHandler, void(error_code))>{
- init.completion_handler, *this,
+ std::move(init.completion_handler), *this,
detail::opcode::pong, payload}();
return init.result.get();
}
diff --git a/boost/beast/websocket/impl/read.ipp b/boost/beast/websocket/impl/read.ipp
index 422cc3766b..1dfbd01e72 100644
--- a/boost/beast/websocket/impl/read.ipp
+++ b/boost/beast/websocket/impl/read.ipp
@@ -22,6 +22,7 @@
#include <boost/asio/associated_executor.hpp>
#include <boost/asio/coroutine.hpp>
#include <boost/asio/handler_continuation_hook.hpp>
+#include <boost/asio/handler_invoke_hook.hpp>
#include <boost/asio/post.hpp>
#include <boost/assert.hpp>
#include <boost/config.hpp>
@@ -35,42 +36,75 @@ namespace boost {
namespace beast {
namespace websocket {
+namespace detail {
+
+template<>
+inline
+void
+stream_base<true>::
+inflate(
+ zlib::z_params& zs,
+ zlib::Flush flush,
+ error_code& ec)
+{
+ this->pmd_->zi.write(zs, flush, ec);
+}
+
+template<>
+inline
+void
+stream_base<true>::
+do_context_takeover_read(role_type role)
+{
+ if((role == role_type::client &&
+ pmd_config_.server_no_context_takeover) ||
+ (role == role_type::server &&
+ pmd_config_.client_no_context_takeover))
+ {
+ pmd_->zi.reset();
+ }
+}
+
+} // detail
+
+//------------------------------------------------------------------------------
+
/* Read some message frame data.
Also reads and handles control frames.
*/
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<
class MutableBufferSequence,
class Handler>
-class stream<NextLayer>::read_some_op
+class stream<NextLayer, deflateSupported>::read_some_op
: public boost::asio::coroutine
{
Handler h_;
- stream<NextLayer>& ws_;
+ stream<NextLayer, deflateSupported>& ws_;
MutableBufferSequence bs_;
buffers_suffix<MutableBufferSequence> cb_;
std::size_t bytes_written_ = 0;
- error_code ev_;
- token tok_;
+ error_code result_;
close_code code_;
bool did_read_ = false;
bool cont_ = false;
public:
+ static constexpr int id = 1; // for soft_mutex
+
read_some_op(read_some_op&&) = default;
- read_some_op(read_some_op const&) = default;
+ read_some_op(read_some_op const&) = delete;
template<class DeducedHandler>
read_some_op(
DeducedHandler&& h,
- stream<NextLayer>& ws,
+ stream<NextLayer, deflateSupported>& ws,
MutableBufferSequence const& bs)
: h_(std::forward<DeducedHandler>(h))
, ws_(ws)
, bs_(bs)
, cb_(bs)
- , tok_(ws_.tok_.unique())
, code_(close_code::none)
{
}
@@ -81,16 +115,16 @@ public:
allocator_type
get_allocator() const noexcept
{
- return boost::asio::get_associated_allocator(h_);
+ return (boost::asio::get_associated_allocator)(h_);
}
using executor_type = boost::asio::associated_executor_t<
- Handler, decltype(std::declval<stream<NextLayer>&>().get_executor())>;
+ Handler, decltype(std::declval<stream<NextLayer, deflateSupported>&>().get_executor())>;
executor_type
get_executor() const noexcept
{
- return boost::asio::get_associated_executor(
+ return (boost::asio::get_associated_executor)(
h_, ws_.get_executor());
}
@@ -112,12 +146,20 @@ public:
return op->cont_ || asio_handler_is_continuation(
std::addressof(op->h_));
}
+
+ template<class Function>
+ friend
+ void asio_handler_invoke(Function&& f, read_some_op* op)
+ {
+ using boost::asio::asio_handler_invoke;
+ asio_handler_invoke(f, std::addressof(op->h_));
+ }
};
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class MutableBufferSequence, class Handler>
void
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
read_some_op<MutableBufferSequence, Handler>::
operator()(
error_code ec,
@@ -127,17 +169,13 @@ operator()(
using beast::detail::clamp;
using boost::asio::buffer;
using boost::asio::buffer_size;
- close_code code{};
cont_ = cont;
BOOST_ASIO_CORO_REENTER(*this)
{
// Maybe suspend
do_maybe_suspend:
- if(! ws_.rd_block_)
+ if(ws_.rd_block_.try_lock(this))
{
- // Acquire the read block
- ws_.rd_block_ = tok_;
-
// Make sure the stream is not closed
if( ws_.status_ == status::closed ||
ws_.status_ == status::failed)
@@ -150,19 +188,17 @@ operator()(
{
do_suspend:
// Suspend
- BOOST_ASSERT(ws_.rd_block_ != tok_);
BOOST_ASIO_CORO_YIELD
- ws_.paused_r_rd_.save(std::move(*this));
+ ws_.paused_r_rd_.emplace(std::move(*this));
// Acquire the read block
- BOOST_ASSERT(! ws_.rd_block_);
- ws_.rd_block_ = tok_;
+ ws_.rd_block_.lock(this);
// Resume
BOOST_ASIO_CORO_YIELD
boost::asio::post(
ws_.get_executor(), std::move(*this));
- BOOST_ASSERT(ws_.rd_block_ == tok_);
+ BOOST_ASSERT(ws_.rd_block_.is_locked(this));
// The only way to get read blocked is if
// a `close_op` wrote a close frame
@@ -177,7 +213,7 @@ operator()(
// then finish the read with operation_aborted.
loop:
- BOOST_ASSERT(ws_.rd_block_ == tok_);
+ BOOST_ASSERT(ws_.rd_block_.is_locked(this));
// See if we need to read a frame header. This
// condition is structured to give the decompressor
// a chance to emit the final empty deflate block
@@ -187,45 +223,46 @@ operator()(
{
// Read frame header
while(! ws_.parse_fh(
- ws_.rd_fh_, ws_.rd_buf_, code))
+ ws_.rd_fh_, ws_.rd_buf_, result_))
{
- if(code != close_code::none)
+ if(result_)
{
// _Fail the WebSocket Connection_
- code_ = code;
- ev_ = error::failed;
+ if(result_ == error::message_too_big)
+ code_ = close_code::too_big;
+ else
+ code_ = close_code::protocol_error;
goto close;
}
- BOOST_ASSERT(ws_.rd_block_ == tok_);
+ BOOST_ASSERT(ws_.rd_block_.is_locked(this));
BOOST_ASIO_CORO_YIELD
ws_.stream_.async_read_some(
ws_.rd_buf_.prepare(read_size(
ws_.rd_buf_, ws_.rd_buf_.max_size())),
std::move(*this));
- BOOST_ASSERT(ws_.rd_block_ == tok_);
+ BOOST_ASSERT(ws_.rd_block_.is_locked(this));
if(! ws_.check_ok(ec))
goto upcall;
ws_.rd_buf_.commit(bytes_transferred);
// Allow a close operation
// to acquire the read block
- BOOST_ASSERT(ws_.rd_block_ == tok_);
- ws_.rd_block_.reset();
+ ws_.rd_block_.unlock(this);
if( ws_.paused_r_close_.maybe_invoke())
{
// Suspend
- BOOST_ASSERT(ws_.rd_block_);
+ BOOST_ASSERT(ws_.rd_block_.is_locked());
goto do_suspend;
}
// Acquire read block
- ws_.rd_block_ = tok_;
+ ws_.rd_block_.lock(this);
}
// Immediately apply the mask to the portion
// of the buffer holding payload data.
if(ws_.rd_fh_.len > 0 && ws_.rd_fh_.mask)
detail::mask_inplace(buffers_prefix(
clamp(ws_.rd_fh_.len),
- ws_.rd_buf_.data()),
+ ws_.rd_buf_.mutable_data()),
ws_.rd_key_);
if(detail::is_control(ws_.rd_fh_.op))
{
@@ -236,6 +273,17 @@ operator()(
// Handle ping frame
if(ws_.rd_fh_.op == detail::opcode::ping)
{
+ if(ws_.ctrl_cb_)
+ {
+ if(! cont_)
+ {
+ BOOST_ASIO_CORO_YIELD
+ boost::asio::post(
+ ws_.get_executor(),
+ std::move(*this));
+ BOOST_ASSERT(cont_);
+ }
+ }
{
auto const b = buffers_prefix(
clamp(ws_.rd_fh_.len),
@@ -249,43 +297,34 @@ operator()(
if(ws_.status_ == status::closing)
goto loop;
if(ws_.ctrl_cb_)
- ws_.ctrl_cb_(frame_type::ping, payload);
+ ws_.ctrl_cb_(
+ frame_type::ping, payload);
ws_.rd_fb_.reset();
ws_.template write_ping<
flat_static_buffer_base>(ws_.rd_fb_,
detail::opcode::pong, payload);
}
- //BOOST_ASSERT(! ws_.paused_r_close_);
-
// Allow a close operation
// to acquire the read block
- BOOST_ASSERT(ws_.rd_block_ == tok_);
- ws_.rd_block_.reset();
+ ws_.rd_block_.unlock(this);
ws_.paused_r_close_.maybe_invoke();
// Maybe suspend
- if(! ws_.wr_block_)
- {
- // Acquire the write block
- ws_.wr_block_ = tok_;
- }
- else
+ if(! ws_.wr_block_.try_lock(this))
{
// Suspend
- BOOST_ASSERT(ws_.wr_block_ != tok_);
BOOST_ASIO_CORO_YIELD
- ws_.paused_rd_.save(std::move(*this));
+ ws_.paused_rd_.emplace(std::move(*this));
// Acquire the write block
- BOOST_ASSERT(! ws_.wr_block_);
- ws_.wr_block_ = tok_;
+ ws_.wr_block_.lock(this);
// Resume
BOOST_ASIO_CORO_YIELD
boost::asio::post(
ws_.get_executor(), std::move(*this));
- BOOST_ASSERT(ws_.wr_block_ == tok_);
+ BOOST_ASSERT(ws_.wr_block_.is_locked(this));
// Make sure the stream is open
if(! ws_.check_open(ec))
@@ -293,14 +332,14 @@ operator()(
}
// Send pong
- BOOST_ASSERT(ws_.wr_block_ == tok_);
+ BOOST_ASSERT(ws_.wr_block_.is_locked(this));
BOOST_ASIO_CORO_YIELD
boost::asio::async_write(ws_.stream_,
ws_.rd_fb_.data(), std::move(*this));
- BOOST_ASSERT(ws_.wr_block_ == tok_);
+ BOOST_ASSERT(ws_.wr_block_.is_locked(this));
if(! ws_.check_ok(ec))
goto upcall;
- ws_.wr_block_.reset();
+ ws_.wr_block_.unlock(this);
ws_.paused_close_.maybe_invoke() ||
ws_.paused_ping_.maybe_invoke() ||
ws_.paused_wr_.maybe_invoke();
@@ -309,11 +348,22 @@ operator()(
// Handle pong frame
if(ws_.rd_fh_.op == detail::opcode::pong)
{
+ // Ignore pong when closing
+ if(! ws_.wr_close_ && ws_.ctrl_cb_)
+ {
+ if(! cont_)
+ {
+ BOOST_ASIO_CORO_YIELD
+ boost::asio::post(
+ ws_.get_executor(),
+ std::move(*this));
+ BOOST_ASSERT(cont_);
+ }
+ }
auto const cb = buffers_prefix(clamp(
ws_.rd_fh_.len), ws_.rd_buf_.data());
auto const len = buffer_size(cb);
BOOST_ASSERT(len == ws_.rd_fh_.len);
- code = close_code::none;
ping_data payload;
detail::read_ping(payload, cb);
ws_.rd_buf_.consume(len);
@@ -325,6 +375,17 @@ operator()(
// Handle close frame
BOOST_ASSERT(ws_.rd_fh_.op == detail::opcode::close);
{
+ if(ws_.ctrl_cb_)
+ {
+ if(! cont_)
+ {
+ BOOST_ASIO_CORO_YIELD
+ boost::asio::post(
+ ws_.get_executor(),
+ std::move(*this));
+ BOOST_ASSERT(cont_);
+ }
+ }
auto const cb = buffers_prefix(clamp(
ws_.rd_fh_.len), ws_.rd_buf_.data());
auto const len = buffer_size(cb);
@@ -332,12 +393,11 @@ operator()(
BOOST_ASSERT(! ws_.rd_close_);
ws_.rd_close_ = true;
close_reason cr;
- detail::read_close(cr, cb, code);
- if(code != close_code::none)
+ detail::read_close(cr, cb, result_);
+ if(result_)
{
// _Fail the WebSocket Connection_
- code_ = code;
- ev_ = error::failed;
+ code_ = close_code::protocol_error;
goto close;
}
ws_.cr_ = cr;
@@ -351,14 +411,14 @@ operator()(
// _Close the WebSocket Connection_
BOOST_ASSERT(ws_.wr_close_);
code_ = close_code::none;
- ev_ = error::closed;
+ result_ = error::closed;
goto close;
}
// _Start the WebSocket Closing Handshake_
code_ = cr.code == close_code::none ?
close_code::normal :
static_cast<close_code>(cr.code);
- ev_ = error::closed;
+ result_ = error::closed;
goto close;
}
}
@@ -369,7 +429,7 @@ operator()(
}
ws_.rd_done_ = false;
}
- if(! ws_.pmd_ || ! ws_.pmd_->rd_set)
+ if(! ws_.rd_deflated())
{
if(ws_.rd_remain_ > 0)
{
@@ -389,7 +449,7 @@ operator()(
ws_.rd_buf_.commit(bytes_transferred);
if(ws_.rd_fh_.mask)
detail::mask_inplace(buffers_prefix(clamp(
- ws_.rd_remain_), ws_.rd_buf_.data()),
+ ws_.rd_remain_), ws_.rd_buf_.mutable_data()),
ws_.rd_key_);
}
if(ws_.rd_buf_.size() > 0)
@@ -409,7 +469,7 @@ operator()(
{
// _Fail the WebSocket Connection_
code_ = close_code::bad_payload;
- ev_ = error::failed;
+ result_ = error::bad_frame_payload;
goto close;
}
}
@@ -443,7 +503,7 @@ operator()(
{
// _Fail the WebSocket Connection_
code_ = close_code::bad_payload;
- ev_ = error::failed;
+ result_ = error::bad_frame_payload;
goto close;
}
}
@@ -477,7 +537,7 @@ operator()(
if(ws_.rd_fh_.mask)
detail::mask_inplace(
buffers_prefix(clamp(ws_.rd_remain_),
- ws_.rd_buf_.data()), ws_.rd_key_);
+ ws_.rd_buf_.mutable_data()), ws_.rd_key_);
did_read_ = true;
}
zlib::z_params zs;
@@ -511,7 +571,7 @@ operator()(
0x00, 0x00, 0xff, 0xff };
zs.next_in = empty_block;
zs.avail_in = sizeof(empty_block);
- ws_.pmd_->zi.write(zs, zlib::Flush::sync, ec);
+ ws_.inflate(zs, zlib::Flush::sync, ec);
if(! ec)
{
// https://github.com/madler/zlib/issues/280
@@ -520,12 +580,7 @@ operator()(
}
if(! ws_.check_ok(ec))
goto upcall;
- if(
- (ws_.role_ == role_type::client &&
- ws_.pmd_config_.server_no_context_takeover) ||
- (ws_.role_ == role_type::server &&
- ws_.pmd_config_.client_no_context_takeover))
- ws_.pmd_->zi.reset();
+ ws_.do_context_takeover_read(ws_.role_);
ws_.rd_done_ = true;
break;
}
@@ -533,7 +588,7 @@ operator()(
{
break;
}
- ws_.pmd_->zi.write(zs, zlib::Flush::sync, ec);
+ ws_.inflate(zs, zlib::Flush::sync, ec);
if(! ws_.check_ok(ec))
goto upcall;
if(ws_.rd_msg_max_ && beast::detail::sum_exceeds(
@@ -541,7 +596,7 @@ operator()(
{
// _Fail the WebSocket Connection_
code_ = close_code::too_big;
- ev_ = error::failed;
+ result_ = error::message_too_big;
goto close;
}
cb_.consume(zs.total_out);
@@ -559,7 +614,7 @@ operator()(
{
// _Fail the WebSocket Connection_
code_ = close_code::bad_payload;
- ev_ = error::failed;
+ result_ = error::bad_frame_payload;
goto close;
}
}
@@ -567,30 +622,25 @@ operator()(
goto upcall;
close:
- if(! ws_.wr_block_)
+ if(ws_.wr_block_.try_lock(this))
{
- // Acquire the write block
- ws_.wr_block_ = tok_;
-
// Make sure the stream is open
BOOST_ASSERT(ws_.status_ == status::open);
}
else
{
// Suspend
- BOOST_ASSERT(ws_.wr_block_ != tok_);
BOOST_ASIO_CORO_YIELD
- ws_.paused_rd_.save(std::move(*this));
+ ws_.paused_rd_.emplace(std::move(*this));
// Acquire the write block
- BOOST_ASSERT(! ws_.wr_block_);
- ws_.wr_block_ = tok_;
+ ws_.wr_block_.lock(this);
// Resume
BOOST_ASIO_CORO_YIELD
boost::asio::post(
ws_.get_executor(), std::move(*this));
- BOOST_ASSERT(ws_.wr_block_ == tok_);
+ BOOST_ASSERT(ws_.wr_block_.is_locked(this));
// Make sure the stream is open
if(! ws_.check_open(ec))
@@ -611,23 +661,23 @@ operator()(
ws_.rd_fb_, code_);
// Send close frame
- BOOST_ASSERT(ws_.wr_block_ == tok_);
+ BOOST_ASSERT(ws_.wr_block_.is_locked(this));
BOOST_ASIO_CORO_YIELD
boost::asio::async_write(
ws_.stream_, ws_.rd_fb_.data(),
std::move(*this));
- BOOST_ASSERT(ws_.wr_block_ == tok_);
+ BOOST_ASSERT(ws_.wr_block_.is_locked(this));
if(! ws_.check_ok(ec))
goto upcall;
}
// Teardown
using beast::websocket::async_teardown;
- BOOST_ASSERT(ws_.wr_block_ == tok_);
+ BOOST_ASSERT(ws_.wr_block_.is_locked(this));
BOOST_ASIO_CORO_YIELD
async_teardown(ws_.role_,
ws_.stream_, std::move(*this));
- BOOST_ASSERT(ws_.wr_block_ == tok_);
+ BOOST_ASSERT(ws_.wr_block_.is_locked(this));
if(ec == boost::asio::error::eof)
{
// Rationale:
@@ -635,7 +685,7 @@ operator()(
ec.assign(0, ec.category());
}
if(! ec)
- ec = ev_;
+ ec = result_;
if(ec && ec != error::closed)
ws_.status_ = status::failed;
else
@@ -643,16 +693,12 @@ operator()(
ws_.close();
upcall:
- if(ws_.rd_block_ == tok_)
- ws_.rd_block_.reset();
+ ws_.rd_block_.try_unlock(this);
ws_.paused_r_close_.maybe_invoke();
- if(ws_.wr_block_ == tok_)
- {
- ws_.wr_block_.reset();
+ if(ws_.wr_block_.try_unlock(this))
ws_.paused_close_.maybe_invoke() ||
ws_.paused_ping_.maybe_invoke() ||
ws_.paused_wr_.maybe_invoke();
- }
if(! cont_)
return boost::asio::post(
ws_.stream_.get_executor(),
@@ -664,15 +710,15 @@ operator()(
//------------------------------------------------------------------------------
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<
class DynamicBuffer,
class Handler>
-class stream<NextLayer>::read_op
+class stream<NextLayer, deflateSupported>::read_op
: public boost::asio::coroutine
{
Handler h_;
- stream<NextLayer>& ws_;
+ stream<NextLayer, deflateSupported>& ws_;
DynamicBuffer& b_;
std::size_t limit_;
std::size_t bytes_written_ = 0;
@@ -683,12 +729,12 @@ public:
boost::asio::associated_allocator_t<Handler>;
read_op(read_op&&) = default;
- read_op(read_op const&) = default;
+ read_op(read_op const&) = delete;
template<class DeducedHandler>
read_op(
DeducedHandler&& h,
- stream<NextLayer>& ws,
+ stream<NextLayer, deflateSupported>& ws,
DynamicBuffer& b,
std::size_t limit,
bool some)
@@ -704,16 +750,16 @@ public:
allocator_type
get_allocator() const noexcept
{
- return boost::asio::get_associated_allocator(h_);
+ return (boost::asio::get_associated_allocator)(h_);
}
using executor_type = boost::asio::associated_executor_t<
- Handler, decltype(std::declval<stream<NextLayer>&>().get_executor())>;
+ Handler, decltype(std::declval<stream<NextLayer, deflateSupported>&>().get_executor())>;
executor_type
get_executor() const noexcept
{
- return boost::asio::get_associated_executor(
+ return (boost::asio::get_associated_executor)(
h_, ws_.get_executor());
}
@@ -728,12 +774,20 @@ public:
return asio_handler_is_continuation(
std::addressof(op->h_));
}
+
+ template<class Function>
+ friend
+ void asio_handler_invoke(Function&& f, read_op* op)
+ {
+ using boost::asio::asio_handler_invoke;
+ asio_handler_invoke(f, std::addressof(op->h_));
+ }
};
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class DynamicBuffer, class Handler>
void
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
read_op<DynamicBuffer, Handler>::
operator()(
error_code ec,
@@ -781,10 +835,10 @@ operator()(
//------------------------------------------------------------------------------
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class DynamicBuffer>
std::size_t
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
read(DynamicBuffer& buffer)
{
static_assert(is_sync_stream<next_layer_type>::value,
@@ -799,10 +853,10 @@ read(DynamicBuffer& buffer)
return bytes_written;
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class DynamicBuffer>
std::size_t
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
read(DynamicBuffer& buffer, error_code& ec)
{
static_assert(is_sync_stream<next_layer_type>::value,
@@ -821,25 +875,25 @@ read(DynamicBuffer& buffer, error_code& ec)
return bytes_written;
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class DynamicBuffer, class ReadHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(
ReadHandler, void(error_code, std::size_t))
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
async_read(DynamicBuffer& buffer, ReadHandler&& handler)
{
static_assert(is_async_stream<next_layer_type>::value,
- "AsyncStream requirements requirements not met");
+ "AsyncStream requirements not met");
static_assert(
boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
- boost::asio::async_completion<
- ReadHandler, void(error_code, std::size_t)> init{handler};
+ BOOST_BEAST_HANDLER_INIT(
+ ReadHandler, void(error_code, std::size_t));
read_op<
DynamicBuffer,
BOOST_ASIO_HANDLER_TYPE(
ReadHandler, void(error_code, std::size_t))>{
- init.completion_handler,
+ std::move(init.completion_handler),
*this,
buffer,
0,
@@ -849,10 +903,10 @@ async_read(DynamicBuffer& buffer, ReadHandler&& handler)
//------------------------------------------------------------------------------
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class DynamicBuffer>
std::size_t
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
read_some(
DynamicBuffer& buffer,
std::size_t limit)
@@ -870,10 +924,10 @@ read_some(
return bytes_written;
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class DynamicBuffer>
std::size_t
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
read_some(
DynamicBuffer& buffer,
std::size_t limit,
@@ -906,28 +960,28 @@ read_some(
return bytes_written;
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class DynamicBuffer, class ReadHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(
ReadHandler, void(error_code, std::size_t))
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
async_read_some(
DynamicBuffer& buffer,
std::size_t limit,
ReadHandler&& handler)
{
static_assert(is_async_stream<next_layer_type>::value,
- "AsyncStream requirements requirements not met");
+ "AsyncStream requirements not met");
static_assert(
boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
- boost::asio::async_completion<ReadHandler,
- void(error_code, std::size_t)> init{handler};
+ BOOST_BEAST_HANDLER_INIT(
+ ReadHandler, void(error_code, std::size_t));
read_op<
DynamicBuffer,
BOOST_ASIO_HANDLER_TYPE(
ReadHandler, void(error_code, std::size_t))>{
- init.completion_handler,
+ std::move(init.completion_handler),
*this,
buffer,
limit,
@@ -937,10 +991,10 @@ async_read_some(
//------------------------------------------------------------------------------
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class MutableBufferSequence>
std::size_t
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
read_some(
MutableBufferSequence const& buffers)
{
@@ -956,10 +1010,10 @@ read_some(
return bytes_written;
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class MutableBufferSequence>
std::size_t
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
read_some(
MutableBufferSequence const& buffers,
error_code& ec)
@@ -986,12 +1040,17 @@ loop:
if(rd_remain_ == 0 && (! rd_fh_.fin || rd_done_))
{
// Read frame header
- while(! parse_fh(rd_fh_, rd_buf_, code))
+ error_code result;
+ while(! parse_fh(rd_fh_, rd_buf_, result))
{
- if(code != close_code::none)
+ if(result)
{
// _Fail the WebSocket Connection_
- do_fail(code, error::failed, ec);
+ if(result == error::message_too_big)
+ code = close_code::too_big;
+ else
+ code = close_code::protocol_error;
+ do_fail(code, result, ec);
return bytes_written;
}
auto const bytes_transferred =
@@ -1007,7 +1066,7 @@ loop:
// of the buffer holding payload data.
if(rd_fh_.len > 0 && rd_fh_.mask)
detail::mask_inplace(buffers_prefix(
- clamp(rd_fh_.len), rd_buf_.data()),
+ clamp(rd_fh_.len), rd_buf_.mutable_data()),
rd_key_);
if(detail::is_control(rd_fh_.op))
{
@@ -1058,11 +1117,12 @@ loop:
BOOST_ASSERT(! rd_close_);
rd_close_ = true;
close_reason cr;
- detail::read_close(cr, b, code);
- if(code != close_code::none)
+ detail::read_close(cr, b, result);
+ if(result)
{
// _Fail the WebSocket Connection_
- do_fail(code, error::failed, ec);
+ do_fail(close_code::protocol_error,
+ result, ec);
return bytes_written;
}
cr_ = cr;
@@ -1090,7 +1150,7 @@ loop:
{
ec.assign(0, ec.category());
}
- if(! pmd_ || ! pmd_->rd_set)
+ if(! this->rd_deflated())
{
if(rd_remain_ > 0)
{
@@ -1108,7 +1168,7 @@ loop:
if(rd_fh_.mask)
detail::mask_inplace(
buffers_prefix(clamp(rd_remain_),
- rd_buf_.data()), rd_key_);
+ rd_buf_.mutable_data()), rd_key_);
}
if(rd_buf_.size() > 0)
{
@@ -1127,10 +1187,8 @@ loop:
! rd_utf8_.finish()))
{
// _Fail the WebSocket Connection_
- do_fail(
- close_code::bad_payload,
- error::failed,
- ec);
+ do_fail(close_code::bad_payload,
+ error::bad_frame_payload, ec);
return bytes_written;
}
}
@@ -1164,7 +1222,7 @@ loop:
{
// _Fail the WebSocket Connection_
do_fail(close_code::bad_payload,
- error::failed, ec);
+ error::bad_frame_payload, ec);
return bytes_written;
}
}
@@ -1217,7 +1275,7 @@ loop:
if(rd_fh_.mask)
detail::mask_inplace(
buffers_prefix(clamp(rd_remain_),
- rd_buf_.data()), rd_key_);
+ rd_buf_.mutable_data()), rd_key_);
auto const in = buffers_prefix(
clamp(rd_remain_), buffers_front(
rd_buf_.data()));
@@ -1238,7 +1296,7 @@ loop:
0x00, 0x00, 0xff, 0xff };
zs.next_in = empty_block;
zs.avail_in = sizeof(empty_block);
- pmd_->zi.write(zs, zlib::Flush::sync, ec);
+ this->inflate(zs, zlib::Flush::sync, ec);
if(! ec)
{
// https://github.com/madler/zlib/issues/280
@@ -1247,12 +1305,7 @@ loop:
}
if(! check_ok(ec))
return bytes_written;
- if(
- (role_ == role_type::client &&
- pmd_config_.server_no_context_takeover) ||
- (role_ == role_type::server &&
- pmd_config_.client_no_context_takeover))
- pmd_->zi.reset();
+ this->do_context_takeover_read(role_);
rd_done_ = true;
break;
}
@@ -1260,14 +1313,14 @@ loop:
{
break;
}
- pmd_->zi.write(zs, zlib::Flush::sync, ec);
+ this->inflate(zs, zlib::Flush::sync, ec);
if(! check_ok(ec))
return bytes_written;
if(rd_msg_max_ && beast::detail::sum_exceeds(
rd_size_, zs.total_out, rd_msg_max_))
{
do_fail(close_code::too_big,
- error::failed, ec);
+ error::message_too_big, ec);
return bytes_written;
}
cb.consume(zs.total_out);
@@ -1285,7 +1338,7 @@ loop:
{
// _Fail the WebSocket Connection_
do_fail(close_code::bad_payload,
- error::failed, ec);
+ error::bad_frame_payload, ec);
return bytes_written;
}
}
@@ -1293,25 +1346,25 @@ loop:
return bytes_written;
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class MutableBufferSequence, class ReadHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(
ReadHandler, void(error_code, std::size_t))
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
async_read_some(
MutableBufferSequence const& buffers,
ReadHandler&& handler)
{
static_assert(is_async_stream<next_layer_type>::value,
- "AsyncStream requirements requirements not met");
+ "AsyncStream requirements not met");
static_assert(boost::asio::is_mutable_buffer_sequence<
MutableBufferSequence>::value,
"MutableBufferSequence requirements not met");
- boost::asio::async_completion<ReadHandler,
- void(error_code, std::size_t)> init{handler};
+ BOOST_BEAST_HANDLER_INIT(
+ ReadHandler, void(error_code, std::size_t));
read_some_op<MutableBufferSequence, BOOST_ASIO_HANDLER_TYPE(
ReadHandler, void(error_code, std::size_t))>{
- init.completion_handler,*this, buffers}(
+ std::move(init.completion_handler), *this, buffers}(
{}, 0, false);
return init.result.get();
}
@@ -1320,4 +1373,4 @@ async_read_some(
} // beast
} // boost
-#endif \ No newline at end of file
+#endif
diff --git a/boost/beast/websocket/impl/rfc6455.ipp b/boost/beast/websocket/impl/rfc6455.ipp
index d688b63d1b..07fdc30686 100644
--- a/boost/beast/websocket/impl/rfc6455.ipp
+++ b/boost/beast/websocket/impl/rfc6455.ipp
@@ -26,9 +26,9 @@ is_upgrade(http::header<true,
return false;
if(req.method() != http::verb::get)
return false;
- if(! http::token_list{req["Connection"]}.exists("upgrade"))
+ if(! http::token_list{req[http::field::connection]}.exists("upgrade"))
return false;
- if(! http::token_list{req["Upgrade"]}.exists("websocket"))
+ if(! http::token_list{req[http::field::upgrade]}.exists("websocket"))
return false;
return true;
}
diff --git a/boost/beast/websocket/impl/stream.ipp b/boost/beast/websocket/impl/stream.ipp
index f8e4a5fc01..cf747c230d 100644
--- a/boost/beast/websocket/impl/stream.ipp
+++ b/boost/beast/websocket/impl/stream.ipp
@@ -40,55 +40,20 @@ namespace boost {
namespace beast {
namespace websocket {
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class... Args>
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
stream(Args&&... args)
: stream_(std::forward<Args>(args)...)
- , tok_(1)
{
BOOST_ASSERT(rd_buf_.max_size() >=
max_control_frame_size);
}
-template<class NextLayer>
-std::size_t
-stream<NextLayer>::
-read_size_hint(
- std::size_t initial_size) const
-{
- using beast::detail::clamp;
- std::size_t result;
- BOOST_ASSERT(initial_size > 0);
- if(! pmd_ || (! rd_done_ && ! pmd_->rd_set))
- {
- // current message is uncompressed
-
- 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;
- }
- }
- result = (std::max)(
- initial_size, clamp(rd_remain_));
-done:
- BOOST_ASSERT(result != 0);
- return result;
-}
-
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class DynamicBuffer, class>
std::size_t
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
read_size_hint(DynamicBuffer& buffer) const
{
static_assert(
@@ -102,10 +67,12 @@ read_size_hint(DynamicBuffer& buffer) const
return read_size_hint(initial_size);
}
-template<class NextLayer>
+//------------------------------------------------------------------------------
+
+template<class NextLayer, bool deflateSupported>
void
-stream<NextLayer>::
-set_option(permessage_deflate const& o)
+stream<NextLayer, deflateSupported>::
+set_option(permessage_deflate const& o, std::true_type)
{
if( o.server_max_window_bits > 15 ||
o.server_max_window_bits < 9)
@@ -123,14 +90,27 @@ set_option(permessage_deflate const& o)
o.memLevel > 9)
BOOST_THROW_EXCEPTION(std::invalid_argument{
"invalid memLevel"});
- pmd_opts_ = o;
+ this->pmd_opts_ = o;
}
-//------------------------------------------------------------------------------
+template<class NextLayer, bool deflateSupported>
+void
+stream<NextLayer, deflateSupported>::
+set_option(permessage_deflate const& o, std::false_type)
+{
+ if(o.client_enable || o.server_enable)
+ {
+ // Can't enable permessage-deflate
+ // when deflateSupported == false.
+ //
+ BOOST_THROW_EXCEPTION(std::invalid_argument{
+ "deflateSupported == false"});
+ }
+}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
void
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
open(role_type role)
{
// VFALCO TODO analyze and remove dupe code in reset()
@@ -144,6 +124,9 @@ open(role_type role)
rd_fh_.fin = false;
rd_close_ = false;
wr_close_ = false;
+ // These should not be necessary, because all completion
+ // handlers must be allowed to execute otherwise the
+ // stream exhibits undefined behavior.
wr_block_.reset();
rd_block_.reset();
cr_.code = close_code::none;
@@ -151,47 +134,59 @@ open(role_type role)
wr_cont_ = false;
wr_buf_size_ = 0;
- if(((role_ == role_type::client && pmd_opts_.client_enable) ||
- (role_ == role_type::server && pmd_opts_.server_enable)) &&
- pmd_config_.accept)
+ open_pmd(is_deflate_supported{});
+}
+
+template<class NextLayer, bool deflateSupported>
+inline
+void
+stream<NextLayer, deflateSupported>::
+open_pmd(std::true_type)
+{
+ if(((role_ == role_type::client &&
+ this->pmd_opts_.client_enable) ||
+ (role_ == role_type::server &&
+ this->pmd_opts_.server_enable)) &&
+ this->pmd_config_.accept)
{
- pmd_normalize(pmd_config_);
- pmd_.reset(new pmd_t);
+ pmd_normalize(this->pmd_config_);
+ this->pmd_.reset(new typename
+ detail::stream_base<deflateSupported>::pmd_type);
if(role_ == role_type::client)
{
- pmd_->zi.reset(
- pmd_config_.server_max_window_bits);
- pmd_->zo.reset(
- pmd_opts_.compLevel,
- pmd_config_.client_max_window_bits,
- pmd_opts_.memLevel,
+ this->pmd_->zi.reset(
+ this->pmd_config_.server_max_window_bits);
+ this->pmd_->zo.reset(
+ this->pmd_opts_.compLevel,
+ this->pmd_config_.client_max_window_bits,
+ this->pmd_opts_.memLevel,
zlib::Strategy::normal);
}
else
{
- pmd_->zi.reset(
- pmd_config_.client_max_window_bits);
- pmd_->zo.reset(
- pmd_opts_.compLevel,
- pmd_config_.server_max_window_bits,
- pmd_opts_.memLevel,
+ this->pmd_->zi.reset(
+ this->pmd_config_.client_max_window_bits);
+ this->pmd_->zo.reset(
+ this->pmd_opts_.compLevel,
+ this->pmd_config_.server_max_window_bits,
+ this->pmd_opts_.memLevel,
zlib::Strategy::normal);
}
}
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
void
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
close()
{
wr_buf_.reset();
- pmd_.reset();
+ close_pmd(is_deflate_supported{});
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
void
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
reset()
{
BOOST_ASSERT(status_ != status::open);
@@ -203,19 +198,23 @@ reset()
rd_close_ = false;
wr_close_ = false;
wr_cont_ = false;
+ // These should not be necessary, because all completion
+ // handlers must be allowed to execute otherwise the
+ // stream exhibits undefined behavior.
wr_block_.reset();
rd_block_.reset();
cr_.code = close_code::none;
}
// Called before each write frame
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
+inline
void
-stream<NextLayer>::
-begin_msg()
+stream<NextLayer, deflateSupported>::
+begin_msg(std::true_type)
{
wr_frag_ = wr_frag_opt_;
- wr_compress_ = static_cast<bool>(pmd_);
+ wr_compress_ = static_cast<bool>(this->pmd_);
// Maintain the write buffer
if( wr_compress_ ||
@@ -235,31 +234,118 @@ begin_msg()
}
}
+// Called before each write frame
+template<class NextLayer, bool deflateSupported>
+inline
+void
+stream<NextLayer, deflateSupported>::
+begin_msg(std::false_type)
+{
+ wr_frag_ = wr_frag_opt_;
+
+ // Maintain the write buffer
+ if(role_ == role_type::client)
+ {
+ if(! wr_buf_ || wr_buf_size_ != wr_buf_opt_)
+ {
+ wr_buf_size_ = wr_buf_opt_;
+ wr_buf_ = boost::make_unique_noinit<
+ std::uint8_t[]>(wr_buf_size_);
+ }
+ }
+ else
+ {
+ wr_buf_size_ = wr_buf_opt_;
+ wr_buf_.reset();
+ }
+}
+
+template<class NextLayer, bool deflateSupported>
+std::size_t
+stream<NextLayer, deflateSupported>::
+read_size_hint(
+ std::size_t initial_size,
+ std::true_type) const
+{
+ using beast::detail::clamp;
+ std::size_t result;
+ BOOST_ASSERT(initial_size > 0);
+ if(! this->pmd_ || (! rd_done_ && ! this->pmd_->rd_set))
+ {
+ // current message is uncompressed
+
+ 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;
+ }
+ }
+ result = (std::max)(
+ initial_size, clamp(rd_remain_));
+done:
+ BOOST_ASSERT(result != 0);
+ return result;
+}
+
+template<class NextLayer, bool deflateSupported>
+std::size_t
+stream<NextLayer, deflateSupported>::
+read_size_hint(
+ std::size_t initial_size,
+ std::false_type) const
+{
+ using beast::detail::clamp;
+ std::size_t result;
+ BOOST_ASSERT(initial_size > 0);
+ // compression is not supported
+ if(rd_done_)
+ {
+ // first message frame
+ result = initial_size;
+ }
+ else if(rd_fh_.fin)
+ {
+ // last message frame
+ BOOST_ASSERT(rd_remain_ > 0);
+ result = clamp(rd_remain_);
+ }
+ else
+ {
+ result = (std::max)(
+ initial_size, clamp(rd_remain_));
+ }
+ BOOST_ASSERT(result != 0);
+ return result;
+}
+
//------------------------------------------------------------------------------
// Attempt to read a complete frame header.
// Returns `false` if more bytes are needed
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class DynamicBuffer>
bool
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
parse_fh(
detail::frame_header& fh,
DynamicBuffer& b,
- close_code& code)
+ error_code& ec)
{
using boost::asio::buffer;
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
- auto const err =
- [&](close_code cv)
- {
- code = cv;
- return false;
- };
if(buffer_size(b.data()) < 2)
{
- code = close_code::none;
+ // need more bytes
+ ec.assign(0, ec.category());
return false;
}
buffers_suffix<typename
@@ -282,7 +368,8 @@ parse_fh(
need += 4;
if(buffer_size(cb) < need)
{
- code = close_code::none;
+ // need more bytes
+ ec.assign(0, ec.category());
return false;
}
fh.op = static_cast<
@@ -299,28 +386,30 @@ parse_fh(
if(rd_cont_)
{
// new data frame when continuation expected
- return err(close_code::protocol_error);
+ ec = error::bad_data_frame;
+ return false;
}
- if((fh.rsv1 && ! pmd_) ||
- fh.rsv2 || fh.rsv3)
+ if(fh.rsv2 || fh.rsv3 ||
+ ! this->rd_deflated(fh.rsv1))
{
// reserved bits not cleared
- return err(close_code::protocol_error);
+ ec = error::bad_reserved_bits;
+ return false;
}
- if(pmd_)
- pmd_->rd_set = fh.rsv1;
break;
case detail::opcode::cont:
if(! rd_cont_)
{
// continuation without an active message
- return err(close_code::protocol_error);
+ ec = error::bad_continuation;
+ return false;
}
if(fh.rsv1 || fh.rsv2 || fh.rsv3)
{
// reserved bits not cleared
- return err(close_code::protocol_error);
+ ec = error::bad_reserved_bits;
+ return false;
}
break;
@@ -328,31 +417,41 @@ parse_fh(
if(detail::is_reserved(fh.op))
{
// reserved opcode
- return err(close_code::protocol_error);
+ ec = error::bad_opcode;
+ return false;
}
if(! fh.fin)
{
// fragmented control message
- return err(close_code::protocol_error);
+ ec = error::bad_control_fragment;
+ return false;
}
if(fh.len > 125)
{
// invalid length for control message
- return err(close_code::protocol_error);
+ ec = error::bad_control_size;
+ return false;
}
if(fh.rsv1 || fh.rsv2 || fh.rsv3)
{
// reserved bits not cleared
- return err(close_code::protocol_error);
+ ec = error::bad_reserved_bits;
+ return false;
}
break;
}
- // unmasked frame from client
if(role_ == role_type::server && ! fh.mask)
- return err(close_code::protocol_error);
- // masked frame from server
+ {
+ // unmasked frame from client
+ ec = error::bad_unmasked_frame;
+ return false;
+ }
if(role_ == role_type::client && fh.mask)
- return err(close_code::protocol_error);
+ {
+ // masked frame from server
+ ec = error::bad_masked_frame;
+ return false;
+ }
if(detail::is_control(fh.op) &&
buffer_size(cb) < need + fh.len)
{
@@ -368,9 +467,12 @@ parse_fh(
BOOST_ASSERT(buffer_size(cb) >= sizeof(tmp));
cb.consume(buffer_copy(buffer(tmp), cb));
fh.len = detail::big_uint16_to_native(&tmp[0]);
- // length not canonical
if(fh.len < 126)
- return err(close_code::protocol_error);
+ {
+ // length not canonical
+ ec = error::bad_size;
+ return false;
+ }
break;
}
case 127:
@@ -379,9 +481,12 @@ parse_fh(
BOOST_ASSERT(buffer_size(cb) >= sizeof(tmp));
cb.consume(buffer_copy(buffer(tmp), cb));
fh.len = detail::big_uint64_to_native(&tmp[0]);
- // length not canonical
if(fh.len < 65536)
- return err(close_code::protocol_error);
+ {
+ // length not canonical
+ ec = error::bad_size;
+ return false;
+ }
break;
}
}
@@ -409,26 +514,34 @@ parse_fh(
{
if(rd_size_ > (std::numeric_limits<
std::uint64_t>::max)() - fh.len)
- return err(close_code::too_big);
+ {
+ // message size exceeds configured limit
+ ec = error::message_too_big;
+ return false;
+ }
}
- if(! pmd_ || ! pmd_->rd_set)
+ if(! this->rd_deflated())
{
if(rd_msg_max_ && beast::detail::sum_exceeds(
rd_size_, fh.len, rd_msg_max_))
- return err(close_code::too_big);
+ {
+ // message size exceeds configured limit
+ ec = error::message_too_big;
+ return false;
+ }
}
rd_cont_ = ! fh.fin;
rd_remain_ = fh.len;
}
b.consume(b.size() - buffer_size(cb));
- code = close_code::none;
+ ec.assign(0, ec.category());
return true;
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class DynamicBuffer>
void
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
write_close(DynamicBuffer& db, close_reason const& cr)
{
using namespace boost::endian;
@@ -479,10 +592,10 @@ write_close(DynamicBuffer& db, close_reason const& cr)
}
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class DynamicBuffer>
void
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
write_ping(DynamicBuffer& db,
detail::opcode code, ping_data const& data)
{
@@ -513,14 +626,13 @@ write_ping(DynamicBuffer& db,
//------------------------------------------------------------------------------
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class Decorator>
request_type
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
build_request(detail::sec_ws_key_type& key,
- string_view host,
- string_view target,
- Decorator const& decorator)
+ string_view host, string_view target,
+ Decorator const& decorator)
{
request_type req;
req.target(target);
@@ -532,34 +644,45 @@ build_request(detail::sec_ws_key_type& key,
detail::make_sec_ws_key(key, wr_gen_);
req.set(http::field::sec_websocket_key, key);
req.set(http::field::sec_websocket_version, "13");
- if(pmd_opts_.client_enable)
+ build_request_pmd(req, is_deflate_supported{});
+ decorator(req);
+ if(! req.count(http::field::user_agent))
+ req.set(http::field::user_agent,
+ BOOST_BEAST_VERSION_STRING);
+ return req;
+}
+
+template<class NextLayer, bool deflateSupported>
+inline
+void
+stream<NextLayer, deflateSupported>::
+build_request_pmd(request_type& req, std::true_type)
+{
+ if(this->pmd_opts_.client_enable)
{
detail::pmd_offer config;
config.accept = true;
config.server_max_window_bits =
- pmd_opts_.server_max_window_bits;
+ this->pmd_opts_.server_max_window_bits;
config.client_max_window_bits =
- pmd_opts_.client_max_window_bits;
+ this->pmd_opts_.client_max_window_bits;
config.server_no_context_takeover =
- pmd_opts_.server_no_context_takeover;
+ this->pmd_opts_.server_no_context_takeover;
config.client_no_context_takeover =
- pmd_opts_.client_no_context_takeover;
+ this->pmd_opts_.client_no_context_takeover;
detail::pmd_write(req, config);
}
- decorator(req);
- if(! req.count(http::field::user_agent))
- req.set(http::field::user_agent,
- BOOST_BEAST_VERSION_STRING);
- return req;
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class Body, class Allocator, class Decorator>
response_type
-stream<NextLayer>::
-build_response(http::request<Body,
- http::basic_fields<Allocator>> const& req,
- Decorator const& decorator)
+stream<NextLayer, deflateSupported>::
+build_response(
+ http::request<Body,
+ http::basic_fields<Allocator>> const& req,
+ Decorator const& decorator,
+ error_code& result)
{
auto const decorate =
[&decorator](response_type& res)
@@ -573,40 +696,58 @@ build_response(http::request<Body,
}
};
auto err =
- [&](std::string const& text)
+ [&](error e)
{
+ result = e;
response_type res;
res.version(req.version());
res.result(http::status::bad_request);
- res.body() = text;
+ res.body() = result.message();
res.prepare_payload();
decorate(res);
return res;
};
- if(req.version() < 11)
- return err("HTTP version 1.1 required");
+ if(req.version() != 11)
+ return err(error::bad_http_version);
if(req.method() != http::verb::get)
- return err("Wrong method");
- if(! is_upgrade(req))
- return err("Expected Upgrade request");
+ return err(error::bad_method);
if(! req.count(http::field::host))
- return err("Missing Host");
- if(! req.count(http::field::sec_websocket_key))
- return err("Missing Sec-WebSocket-Key");
- auto const key = req[http::field::sec_websocket_key];
- if(key.size() > detail::sec_ws_key_type::max_size_n)
- return err("Invalid Sec-WebSocket-Key");
- {
- auto const version =
- req[http::field::sec_websocket_version];
- if(version.empty())
- return err("Missing Sec-WebSocket-Version");
- if(version != "13")
+ return err(error::no_host);
+ {
+ auto const it = req.find(http::field::connection);
+ if(it == req.end())
+ return err(error::no_connection);
+ if(! http::token_list{it->value()}.exists("upgrade"))
+ return err(error::no_connection_upgrade);
+ }
+ {
+ auto const it = req.find(http::field::upgrade);
+ if(it == req.end())
+ return err(error::no_upgrade);
+ if(! http::token_list{it->value()}.exists("websocket"))
+ return err(error::no_upgrade_websocket);
+ }
+ string_view key;
+ {
+ auto const it = req.find(http::field::sec_websocket_key);
+ if(it == req.end())
+ return err(error::no_sec_key);
+ key = it->value();
+ if(key.size() > detail::sec_ws_key_type::max_size_n)
+ return err(error::bad_sec_key);
+ }
+ {
+ auto const it = req.find(http::field::sec_websocket_version);
+ if(it == req.end())
+ return err(error::no_sec_version);
+ if(it->value() != "13")
{
response_type res;
res.result(http::status::upgrade_required);
res.version(req.version());
res.set(http::field::sec_websocket_version, "13");
+ result = error::bad_sec_version;
+ res.body() = result.message();
res.prepare_payload();
decorate(res);
return res;
@@ -614,12 +755,6 @@ build_response(http::request<Body,
}
response_type res;
- {
- detail::pmd_offer offer;
- detail::pmd_offer unused;
- pmd_read(offer, req);
- pmd_negotiate(res, unused, offer, pmd_opts_);
- }
res.result(http::status::switching_protocols);
res.version(req.version());
res.set(http::field::upgrade, "websocket");
@@ -629,53 +764,95 @@ build_response(http::request<Body,
detail::make_sec_ws_accept(acc, key);
res.set(http::field::sec_websocket_accept, acc);
}
+ build_response_pmd(res, req, is_deflate_supported{});
decorate(res);
+ result = {};
return res;
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
+template<class Body, class Allocator>
+inline
+void
+stream<NextLayer, deflateSupported>::
+build_response_pmd(
+ response_type& res,
+ http::request<Body,
+ http::basic_fields<Allocator>> const& req,
+ std::true_type)
+{
+ detail::pmd_offer offer;
+ detail::pmd_offer unused;
+ pmd_read(offer, req);
+ pmd_negotiate(res, unused, offer, this->pmd_opts_);
+}
+
+// Called when the WebSocket Upgrade response is received
+template<class NextLayer, bool deflateSupported>
void
-stream<NextLayer>::
-on_response(response_type const& res,
- detail::sec_ws_key_type const& key, error_code& ec)
+stream<NextLayer, deflateSupported>::
+on_response(
+ response_type const& res,
+ detail::sec_ws_key_type const& key,
+ error_code& ec)
{
- bool const success = [&]()
+ auto const err =
+ [&](error e)
+ {
+ ec = e;
+ };
+ if(res.result() != http::status::switching_protocols)
+ return err(error::upgrade_declined);
+ if(res.version() != 11)
+ return err(error::bad_http_version);
{
- if(res.version() < 11)
- return false;
- if(res.result() != http::status::switching_protocols)
- return false;
- if(! http::token_list{res[http::field::connection]}.exists("upgrade"))
- return false;
- if(! http::token_list{res[http::field::upgrade]}.exists("websocket"))
- return false;
- if(res.count(http::field::sec_websocket_accept) != 1)
- return false;
+ auto const it = res.find(http::field::connection);
+ if(it == res.end())
+ return err(error::no_connection);
+ if(! http::token_list{it->value()}.exists("upgrade"))
+ return err(error::no_connection_upgrade);
+ }
+ {
+ auto const it = res.find(http::field::upgrade);
+ if(it == res.end())
+ return err(error::no_upgrade);
+ if(! http::token_list{it->value()}.exists("websocket"))
+ return err(error::no_upgrade_websocket);
+ }
+ {
+ auto const it = res.find(http::field::sec_websocket_accept);
+ if(it == res.end())
+ return err(error::no_sec_accept);
detail::sec_ws_accept_type acc;
detail::make_sec_ws_accept(acc, key);
- if(acc.compare(
- res[http::field::sec_websocket_accept]) != 0)
- return false;
- return true;
- }();
- if(! success)
- {
- ec = error::handshake_failed;
- return;
+ if(acc.compare(it->value()) != 0)
+ return err(error::bad_sec_accept);
}
+
ec.assign(0, ec.category());
+ on_response_pmd(res, is_deflate_supported{});
+ open(role_type::client);
+}
+
+template<class NextLayer, bool deflateSupported>
+inline
+void
+stream<NextLayer, deflateSupported>::
+on_response_pmd(
+ response_type const& res,
+ std::true_type)
+{
detail::pmd_offer offer;
pmd_read(offer, res);
// VFALCO see if offer satisfies pmd_config_,
// return an error if not.
- pmd_config_ = offer; // overwrite for now
- open(role_type::client);
+ this->pmd_config_ = offer; // overwrite for now
}
// _Fail the WebSocket Connection_
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
void
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
do_fail(
std::uint16_t code, // if set, send a close frame first
error_code ev, // error code to use upon success
diff --git a/boost/beast/websocket/impl/teardown.ipp b/boost/beast/websocket/impl/teardown.ipp
index 8f4475f246..add6b2773d 100644
--- a/boost/beast/websocket/impl/teardown.ipp
+++ b/boost/beast/websocket/impl/teardown.ipp
@@ -14,7 +14,9 @@
#include <boost/beast/core/type_traits.hpp>
#include <boost/asio/associated_allocator.hpp>
#include <boost/asio/associated_executor.hpp>
+#include <boost/asio/coroutine.hpp>
#include <boost/asio/handler_continuation_hook.hpp>
+#include <boost/asio/handler_invoke_hook.hpp>
#include <boost/asio/post.hpp>
#include <memory>
@@ -25,7 +27,7 @@ namespace websocket {
namespace detail {
template<class Handler>
-class teardown_tcp_op
+class teardown_tcp_op : public boost::asio::coroutine
{
using socket_type =
boost::asio::ip::tcp::socket;
@@ -33,7 +35,7 @@ class teardown_tcp_op
Handler h_;
socket_type& s_;
role_type role_;
- int step_ = 0;
+ bool nb_;
public:
teardown_tcp_op(teardown_tcp_op&& other) = default;
@@ -56,7 +58,7 @@ public:
allocator_type
get_allocator() const noexcept
{
- return boost::asio::get_associated_allocator(h_);
+ return (boost::asio::get_associated_allocator)(h_);
}
using executor_type = boost::asio::associated_executor_t<
@@ -65,7 +67,7 @@ public:
executor_type
get_executor() const noexcept
{
- return boost::asio::get_associated_executor(
+ return (boost::asio::get_associated_executor)(
h_, s_.get_executor());
}
@@ -78,60 +80,83 @@ public:
bool asio_handler_is_continuation(teardown_tcp_op* op)
{
using boost::asio::asio_handler_is_continuation;
- return op->step_ >= 3 ||
- asio_handler_is_continuation(std::addressof(op->h_));
+ return asio_handler_is_continuation(
+ std::addressof(op->h_));
+ }
+
+ template<class Function>
+ friend
+ void asio_handler_invoke(Function&& f, teardown_tcp_op* op)
+ {
+ using boost::asio::asio_handler_invoke;
+ asio_handler_invoke(f, std::addressof(op->h_));
}
};
template<class Handler>
void
teardown_tcp_op<Handler>::
-operator()(error_code ec, std::size_t)
+operator()(error_code ec, std::size_t bytes_transferred)
{
using boost::asio::buffer;
using tcp = boost::asio::ip::tcp;
- switch(step_)
+ BOOST_ASIO_CORO_REENTER(*this)
{
- case 0:
+ nb_ = s_.non_blocking();
s_.non_blocking(true, ec);
+ if(! ec)
+ {
+ if(role_ == role_type::server)
+ s_.shutdown(tcp::socket::shutdown_send, ec);
+ }
if(ec)
{
- step_ = 1;
- return boost::asio::post(
+ BOOST_ASIO_CORO_YIELD
+ boost::asio::post(
s_.get_executor(),
bind_handler(std::move(*this), ec, 0));
+ goto upcall;
}
- step_ = 2;
- if(role_ == role_type::server)
- s_.shutdown(tcp::socket::shutdown_send, ec);
- goto do_read;
-
- case 1:
- break;
-
- case 2:
- step_ = 3;
-
- case 3:
- if(ec != boost::asio::error::would_block)
- break;
+ for(;;)
{
- char buf[2048];
- s_.read_some(
- boost::asio::buffer(buf), ec);
+ {
+ char buf[2048];
+ s_.read_some(
+ boost::asio::buffer(buf), ec);
+ }
+ if(ec == boost::asio::error::would_block)
+ {
+ BOOST_ASIO_CORO_YIELD
+ s_.async_wait(
+ boost::asio::ip::tcp::socket::wait_read,
+ std::move(*this));
+ continue;
+ }
if(ec)
+ {
+ if(ec != boost::asio::error::eof)
+ goto upcall;
+ ec = {};
+ break;
+ }
+ if(bytes_transferred == 0)
+ {
+ // happens sometimes
break;
+ }
}
-
- do_read:
- return s_.async_read_some(
- boost::asio::null_buffers{},
- std::move(*this));
+ if(role_ == role_type::client)
+ s_.shutdown(tcp::socket::shutdown_send, ec);
+ if(ec)
+ goto upcall;
+ s_.close(ec);
+ upcall:
+ {
+ error_code ignored;
+ s_.non_blocking(nb_, ignored);
+ }
+ h_(ec);
}
- if(role_ == role_type::client)
- s_.shutdown(tcp::socket::shutdown_send, ec);
- s_.close(ec);
- h_(ec);
}
} // detail
@@ -149,17 +174,31 @@ teardown(
if(role == role_type::server)
socket.shutdown(
boost::asio::ip::tcp::socket::shutdown_send, ec);
- while(! ec)
+ if(ec)
+ return;
+ for(;;)
{
- char buf[8192];
- auto const n = socket.read_some(
- buffer(buf), ec);
- if(! n)
+ char buf[2048];
+ auto const bytes_transferred =
+ socket.read_some(buffer(buf), ec);
+ if(ec)
+ {
+ if(ec != boost::asio::error::eof)
+ return;
+ ec = {};
break;
+ }
+ if(bytes_transferred == 0)
+ {
+ // happens sometimes
+ break;
+ }
}
if(role == role_type::client)
socket.shutdown(
boost::asio::ip::tcp::socket::shutdown_send, ec);
+ if(ec)
+ return;
socket.close(ec);
}
diff --git a/boost/beast/websocket/impl/write.ipp b/boost/beast/websocket/impl/write.ipp
index b04f2826fc..d12f2f9e12 100644
--- a/boost/beast/websocket/impl/write.ipp
+++ b/boost/beast/websocket/impl/write.ipp
@@ -14,7 +14,6 @@
#include <boost/beast/core/buffers_cat.hpp>
#include <boost/beast/core/buffers_prefix.hpp>
#include <boost/beast/core/buffers_suffix.hpp>
-#include <boost/beast/core/handler_ptr.hpp>
#include <boost/beast/core/flat_static_buffer.hpp>
#include <boost/beast/core/type_traits.hpp>
#include <boost/beast/core/detail/clamp.hpp>
@@ -24,6 +23,7 @@
#include <boost/asio/associated_executor.hpp>
#include <boost/asio/coroutine.hpp>
#include <boost/asio/handler_continuation_hook.hpp>
+#include <boost/asio/handler_invoke_hook.hpp>
#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <boost/throw_exception.hpp>
@@ -34,39 +34,139 @@ namespace boost {
namespace beast {
namespace websocket {
-template<class NextLayer>
+namespace detail {
+
+// Compress a buffer sequence
+// Returns: `true` if more calls are needed
+//
+template<>
+template<class ConstBufferSequence>
+bool
+stream_base<true>::
+deflate(
+ boost::asio::mutable_buffer& out,
+ buffers_suffix<ConstBufferSequence>& cb,
+ bool fin,
+ std::size_t& total_in,
+ error_code& ec)
+{
+ using boost::asio::buffer;
+ BOOST_ASSERT(out.size() >= 6);
+ auto& zo = this->pmd_->zo;
+ zlib::z_params zs;
+ zs.avail_in = 0;
+ zs.next_in = nullptr;
+ zs.avail_out = out.size();
+ zs.next_out = out.data();
+ for(auto in : beast::detail::buffers_range(cb))
+ {
+ zs.avail_in = in.size();
+ if(zs.avail_in == 0)
+ continue;
+ zs.next_in = in.data();
+ zo.write(zs, zlib::Flush::none, ec);
+ if(ec)
+ {
+ if(ec != zlib::error::need_buffers)
+ return false;
+ BOOST_ASSERT(zs.avail_out == 0);
+ BOOST_ASSERT(zs.total_out == out.size());
+ ec.assign(0, ec.category());
+ break;
+ }
+ if(zs.avail_out == 0)
+ {
+ BOOST_ASSERT(zs.total_out == out.size());
+ break;
+ }
+ BOOST_ASSERT(zs.avail_in == 0);
+ }
+ total_in = zs.total_in;
+ cb.consume(zs.total_in);
+ if(zs.avail_out > 0 && fin)
+ {
+ auto const remain = boost::asio::buffer_size(cb);
+ if(remain == 0)
+ {
+ // Inspired by Mark Adler
+ // https://github.com/madler/zlib/issues/149
+ //
+ // VFALCO We could do this flush twice depending
+ // on how much space is in the output.
+ zo.write(zs, zlib::Flush::block, ec);
+ BOOST_ASSERT(! ec || ec == zlib::error::need_buffers);
+ if(ec == zlib::error::need_buffers)
+ ec.assign(0, ec.category());
+ if(ec)
+ return false;
+ if(zs.avail_out >= 6)
+ {
+ zo.write(zs, zlib::Flush::full, ec);
+ BOOST_ASSERT(! ec);
+ // remove flush marker
+ zs.total_out -= 4;
+ out = buffer(out.data(), zs.total_out);
+ return false;
+ }
+ }
+ }
+ ec.assign(0, ec.category());
+ out = buffer(out.data(), zs.total_out);
+ return true;
+}
+
+template<>
+inline
+void
+stream_base<true>::
+do_context_takeover_write(role_type role)
+{
+ if((role == role_type::client &&
+ this->pmd_config_.client_no_context_takeover) ||
+ (role == role_type::server &&
+ this->pmd_config_.server_no_context_takeover))
+ {
+ this->pmd_->zo.reset();
+ }
+}
+
+} // detail
+
+//------------------------------------------------------------------------------
+
+template<class NextLayer, bool deflateSupported>
template<class Buffers, class Handler>
-class stream<NextLayer>::write_some_op
+class stream<NextLayer, deflateSupported>::write_some_op
: public boost::asio::coroutine
{
Handler h_;
- stream<NextLayer>& ws_;
+ stream<NextLayer, deflateSupported>& ws_;
buffers_suffix<Buffers> cb_;
detail::frame_header fh_;
detail::prepared_key key_;
std::size_t bytes_transferred_ = 0;
std::size_t remain_;
std::size_t in_;
- token tok_;
int how_;
bool fin_;
bool more_;
bool cont_ = false;
public:
+ static constexpr int id = 2; // for soft_mutex
+
write_some_op(write_some_op&&) = default;
- write_some_op(write_some_op const&) = default;
+ write_some_op(write_some_op const&) = delete;
template<class DeducedHandler>
write_some_op(
DeducedHandler&& h,
- stream<NextLayer>& ws,
+ stream<NextLayer, deflateSupported>& ws,
bool fin,
Buffers const& bs)
: h_(std::forward<DeducedHandler>(h))
, ws_(ws)
, cb_(bs)
- , tok_(ws_.tok_.unique())
, fin_(fin)
{
}
@@ -77,16 +177,16 @@ public:
allocator_type
get_allocator() const noexcept
{
- return boost::asio::get_associated_allocator(h_);
+ return (boost::asio::get_associated_allocator)(h_);
}
using executor_type = boost::asio::associated_executor_t<
- Handler, decltype(std::declval<stream<NextLayer>&>().get_executor())>;
+ Handler, decltype(std::declval<stream<NextLayer, deflateSupported>&>().get_executor())>;
executor_type
get_executor() const noexcept
{
- return boost::asio::get_associated_executor(
+ return (boost::asio::get_associated_executor)(
h_, ws_.get_executor());
}
@@ -108,12 +208,21 @@ public:
return op->cont_ || asio_handler_is_continuation(
std::addressof(op->h_));
}
+
+ template<class Function>
+ friend
+ void asio_handler_invoke(Function&& f, write_some_op* op)
+ {
+ using boost::asio::asio_handler_invoke;
+ asio_handler_invoke(
+ f, std::addressof(op->h_));
+ }
};
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class Buffers, class Handler>
void
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
write_some_op<Buffers, Handler>::
operator()(
error_code ec,
@@ -194,11 +303,8 @@ operator()(
}
// Maybe suspend
- if(! ws_.wr_block_)
+ if(ws_.wr_block_.try_lock(this))
{
- // Acquire the write block
- ws_.wr_block_ = tok_;
-
// Make sure the stream is open
if(! ws_.check_open(ec))
goto upcall;
@@ -207,19 +313,17 @@ operator()(
{
do_suspend:
// Suspend
- BOOST_ASSERT(ws_.wr_block_ != tok_);
BOOST_ASIO_CORO_YIELD
- ws_.paused_wr_.save(std::move(*this));
+ ws_.paused_wr_.emplace(std::move(*this));
// Acquire the write block
- BOOST_ASSERT(! ws_.wr_block_);
- ws_.wr_block_ = tok_;
+ ws_.wr_block_.lock(this);
// Resume
BOOST_ASIO_CORO_YIELD
boost::asio::post(
ws_.get_executor(), std::move(*this));
- BOOST_ASSERT(ws_.wr_block_ == tok_);
+ BOOST_ASSERT(ws_.wr_block_.is_locked(this));
// Make sure the stream is open
if(! ws_.check_open(ec))
@@ -278,15 +382,15 @@ operator()(
fh_.op = detail::opcode::cont;
// Allow outgoing control frames to
// be sent in between message frames
- ws_.wr_block_.reset();
+ ws_.wr_block_.unlock(this);
if( ws_.paused_close_.maybe_invoke() ||
ws_.paused_rd_.maybe_invoke() ||
ws_.paused_ping_.maybe_invoke())
{
- BOOST_ASSERT(ws_.wr_block_);
+ BOOST_ASSERT(ws_.wr_block_.is_locked());
goto do_suspend;
}
- ws_.wr_block_ = tok_;
+ ws_.wr_block_.lock(this);
}
goto upcall;
}
@@ -377,15 +481,15 @@ operator()(
fh_.op = detail::opcode::cont;
// Allow outgoing control frames to
// be sent in between message frames:
- ws_.wr_block_.reset();
+ ws_.wr_block_.unlock(this);
if( ws_.paused_close_.maybe_invoke() ||
ws_.paused_rd_.maybe_invoke() ||
ws_.paused_ping_.maybe_invoke())
{
- BOOST_ASSERT(ws_.wr_block_);
+ BOOST_ASSERT(ws_.wr_block_.is_locked());
goto do_suspend;
}
- ws_.wr_block_ = tok_;
+ ws_.wr_block_.lock(this);
}
goto upcall;
}
@@ -398,8 +502,7 @@ operator()(
{
b = buffer(ws_.wr_buf_.get(),
ws_.wr_buf_size_);
- more_ = detail::deflate(ws_.pmd_->zo,
- b, cb_, fin_, in_, ec);
+ more_ = ws_.deflate(b, cb_, fin_, in_, ec);
if(! ws_.check_ok(ec))
goto upcall;
n = buffer_size(b);
@@ -439,24 +542,20 @@ operator()(
fh_.rsv1 = false;
// Allow outgoing control frames to
// be sent in between message frames:
- ws_.wr_block_.reset();
+ ws_.wr_block_.unlock(this);
if( ws_.paused_close_.maybe_invoke() ||
ws_.paused_rd_.maybe_invoke() ||
ws_.paused_ping_.maybe_invoke())
{
- BOOST_ASSERT(ws_.wr_block_);
+ BOOST_ASSERT(ws_.wr_block_.is_locked());
goto do_suspend;
}
- ws_.wr_block_ = tok_;
+ ws_.wr_block_.lock(this);
}
else
{
- if(fh_.fin && (
- (ws_.role_ == role_type::client &&
- ws_.pmd_config_.client_no_context_takeover) ||
- (ws_.role_ == role_type::server &&
- ws_.pmd_config_.server_no_context_takeover)))
- ws_.pmd_->zo.reset();
+ if(fh_.fin)
+ ws_.do_context_takeover_write(ws_.role_);
goto upcall;
}
}
@@ -465,25 +564,24 @@ operator()(
//--------------------------------------------------------------------------
upcall:
- BOOST_ASSERT(ws_.wr_block_ == tok_);
- ws_.wr_block_.reset();
+ ws_.wr_block_.unlock(this);
ws_.paused_close_.maybe_invoke() ||
ws_.paused_rd_.maybe_invoke() ||
ws_.paused_ping_.maybe_invoke();
if(! cont_)
return boost::asio::post(
ws_.stream_.get_executor(),
- bind_handler(h_, ec, bytes_transferred_));
+ bind_handler(std::move(h_), ec, bytes_transferred_));
h_(ec, bytes_transferred_);
}
}
//------------------------------------------------------------------------------
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class ConstBufferSequence>
std::size_t
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
write_some(bool fin, ConstBufferSequence const& buffers)
{
static_assert(is_sync_stream<next_layer_type>::value,
@@ -499,10 +597,10 @@ write_some(bool fin, ConstBufferSequence const& buffers)
return bytes_transferred;
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class ConstBufferSequence>
std::size_t
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
write_some(bool fin,
ConstBufferSequence const& buffers, error_code& ec)
{
@@ -544,9 +642,8 @@ write_some(bool fin,
{
auto b = buffer(
wr_buf_.get(), wr_buf_size_);
- auto const more = detail::deflate(
- pmd_->zo, b, cb, fin,
- bytes_transferred, ec);
+ auto const more = this->deflate(
+ b, cb, fin, bytes_transferred, ec);
if(! check_ok(ec))
return bytes_transferred;
auto const n = buffer_size(b);
@@ -582,12 +679,8 @@ write_some(bool fin,
fh.op = detail::opcode::cont;
fh.rsv1 = false;
}
- if(fh.fin && (
- (role_ == role_type::client &&
- pmd_config_.client_no_context_takeover) ||
- (role_ == role_type::server &&
- pmd_config_.server_no_context_takeover)))
- pmd_->zo.reset();
+ if(fh.fin)
+ this->do_context_takeover_write(role_);
}
else if(! fh.mask)
{
@@ -712,11 +805,11 @@ write_some(bool fin,
return bytes_transferred;
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class ConstBufferSequence, class WriteHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(
WriteHandler, void(error_code, std::size_t))
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
async_write_some(bool fin,
ConstBufferSequence const& bs, WriteHandler&& handler)
{
@@ -725,21 +818,21 @@ async_write_some(bool fin,
static_assert(boost::asio::is_const_buffer_sequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
- boost::asio::async_completion<WriteHandler,
- void(error_code, std::size_t)> init{handler};
+ BOOST_BEAST_HANDLER_INIT(
+ WriteHandler, void(error_code, std::size_t));
write_some_op<ConstBufferSequence, BOOST_ASIO_HANDLER_TYPE(
WriteHandler, void(error_code, std::size_t))>{
- init.completion_handler, *this, fin, bs}(
+ std::move(init.completion_handler), *this, fin, bs}(
{}, 0, false);
return init.result.get();
}
//------------------------------------------------------------------------------
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class ConstBufferSequence>
std::size_t
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
write(ConstBufferSequence const& buffers)
{
static_assert(is_sync_stream<next_layer_type>::value,
@@ -754,10 +847,10 @@ write(ConstBufferSequence const& buffers)
return bytes_transferred;
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class ConstBufferSequence>
std::size_t
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
write(ConstBufferSequence const& buffers, error_code& ec)
{
static_assert(is_sync_stream<next_layer_type>::value,
@@ -768,11 +861,11 @@ write(ConstBufferSequence const& buffers, error_code& ec)
return write_some(true, buffers, ec);
}
-template<class NextLayer>
+template<class NextLayer, bool deflateSupported>
template<class ConstBufferSequence, class WriteHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(
WriteHandler, void(error_code, std::size_t))
-stream<NextLayer>::
+stream<NextLayer, deflateSupported>::
async_write(
ConstBufferSequence const& bs, WriteHandler&& handler)
{
@@ -781,11 +874,11 @@ async_write(
static_assert(boost::asio::is_const_buffer_sequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
- boost::asio::async_completion<WriteHandler,
- void(error_code, std::size_t)> init{handler};
+ BOOST_BEAST_HANDLER_INIT(
+ WriteHandler, void(error_code, std::size_t));
write_some_op<ConstBufferSequence, BOOST_ASIO_HANDLER_TYPE(
WriteHandler, void(error_code, std::size_t))>{
- init.completion_handler, *this, true, bs}(
+ std::move(init.completion_handler), *this, true, bs}(
{}, 0, false);
return init.result.get();
}
diff --git a/boost/beast/websocket/ssl.hpp b/boost/beast/websocket/ssl.hpp
index 44d5dc8082..d51921a481 100644
--- a/boost/beast/websocket/ssl.hpp
+++ b/boost/beast/websocket/ssl.hpp
@@ -55,9 +55,9 @@ teardown(
@param stream The stream to tear down.
- @param handler The handler to be called when the request completes.
- Copies will be made of the handler as required. The equivalent
- function signature of the handler must be:
+ @param handler Invoked when the operation completes.
+ The handler may be moved or copied as needed.
+ The equivalent function signature of the handler must be:
@code void handler(
error_code const& error // result of operation
); @endcode
diff --git a/boost/beast/websocket/stream.hpp b/boost/beast/websocket/stream.hpp
index b6d10834ef..90cb753294 100644
--- a/boost/beast/websocket/stream.hpp
+++ b/boost/beast/websocket/stream.hpp
@@ -15,11 +15,13 @@
#include <boost/beast/websocket/option.hpp>
#include <boost/beast/websocket/role.hpp>
#include <boost/beast/websocket/rfc6455.hpp>
+#include <boost/beast/websocket/stream_fwd.hpp>
#include <boost/beast/websocket/detail/frame.hpp>
#include <boost/beast/websocket/detail/hybi13.hpp>
#include <boost/beast/websocket/detail/mask.hpp>
#include <boost/beast/websocket/detail/pausation.hpp>
#include <boost/beast/websocket/detail/pmd_extension.hpp>
+#include <boost/beast/websocket/detail/stream_base.hpp>
#include <boost/beast/websocket/detail/utf8_checker.hpp>
#include <boost/beast/core/static_buffer.hpp>
#include <boost/beast/core/string.hpp>
@@ -28,8 +30,6 @@
#include <boost/beast/http/message.hpp>
#include <boost/beast/http/string_body.hpp>
#include <boost/beast/http/detail/type_traits.hpp>
-#include <boost/beast/zlib/deflate_stream.hpp>
-#include <boost/beast/zlib/inflate_stream.hpp>
#include <boost/asio/async_result.hpp>
#include <boost/asio/error.hpp>
#include <algorithm>
@@ -38,16 +38,10 @@
#include <limits>
#include <type_traits>
-#include <boost/asio/io_context.hpp> // DEPRECATED
-
namespace boost {
namespace beast {
namespace websocket {
-namespace detail {
-class frame_test;
-}
-
/// The type of object holding HTTP Upgrade requests
using request_type = http::request<http::empty_body>;
@@ -71,6 +65,10 @@ enum class frame_type
pong
};
+namespace detail {
+class frame_test;
+} // detail
+
//--------------------------------------------------------------------
/** Provides message-oriented functionality using WebSocket.
@@ -109,6 +107,12 @@ enum class frame_type
For asynchronous operations, the type must support the
@b AsyncStream concept.
+ @tparam deflateSupported A `bool` indicating whether or not the
+ stream will be capable of negotiating the permessage-deflate websocket
+ extension. Note that even if this is set to `true`, the permessage
+ deflate options (set by the caller at runtime) must still have the
+ feature enabled for a successful negotiation to occur.
+
@note A stream object must not be moved or destroyed while there
are pending asynchronous operations associated with it.
@@ -117,16 +121,22 @@ enum class frame_type
@b DynamicBuffer,
@b SyncStream
*/
-template<class NextLayer>
+template<
+ class NextLayer,
+ bool deflateSupported>
class stream
+#ifndef BOOST_BEAST_DOXYGEN
+ : private detail::stream_base<deflateSupported>
+#endif
{
friend class close_test;
friend class frame_test;
friend class ping_test;
- friend class read_test;
+ friend class read1_test;
+ friend class read2_test;
friend class stream_test;
friend class write_test;
-
+
/* The read buffer has to be at least as large
as the largest possible control frame including
the frame header.
@@ -134,36 +144,9 @@ class stream
static std::size_t constexpr max_control_frame_size = 2 + 8 + 4 + 125;
static std::size_t constexpr tcp_frame_size = 1536;
- struct op {};
-
using control_cb_type =
std::function<void(frame_type, string_view)>;
- // tokens are used to order reads and writes
- class token
- {
- unsigned char id_ = 0;
- public:
- token() = default;
- token(token const&) = default;
- explicit token(unsigned char id) : id_(id) {}
- operator bool() const { return id_ != 0; }
- bool operator==(token const& t) { return id_ == t.id_; }
- bool operator!=(token const& t) { return id_ != t.id_; }
- token unique() { token t{id_++}; if(id_ == 0) ++id_; return t; }
- void reset() { id_ = 0; }
- };
-
- // State information for the permessage-deflate extension
- struct pmd_t
- {
- // `true` if current read message is compressed
- bool rd_set = false;
-
- zlib::deflate_stream zo;
- zlib::inflate_stream zi;
- };
-
enum class status
{
open,
@@ -183,8 +166,7 @@ class stream
std::uint64_t rd_remain_ // message frame bytes left in current frame
= 0;
detail::frame_header rd_fh_; // current frame header
- detail::prepared_key rd_key_ // current stateful mask key
- = 0;
+ detail::prepared_key rd_key_; // current stateful mask key
detail::frame_buffer rd_fb_; // to write control frames (during reads)
detail::utf8_checker rd_utf8_; // to validate utf8
static_buffer<
@@ -197,15 +179,14 @@ class stream
= true;
bool rd_close_ // did we read a close frame?
= false;
- token rd_block_; // op currenly reading
+ detail::soft_mutex rd_block_; // op currently reading
- token tok_; // used to order asynchronous ops
role_type role_ // server or client
= role_type::client;
status status_
= status::closed;
- token wr_block_; // op currenly writing
+ detail::soft_mutex wr_block_; // op currently writing
bool wr_close_ // did we write a close frame?
= false;
bool wr_cont_ // next write is a continuation
@@ -231,21 +212,20 @@ class stream
detail::pausation paused_wr_; // paused write op
detail::pausation paused_ping_; // paused ping op
detail::pausation paused_close_; // paused close op
- detail::pausation paused_r_rd_; // paused read op (read)
- detail::pausation paused_r_close_;// paused close op (read)
-
- std::unique_ptr<pmd_t> pmd_; // pmd settings or nullptr
- permessage_deflate pmd_opts_; // local pmd options
- detail::pmd_offer pmd_config_; // offer (client) or negotiation (server)
+ detail::pausation paused_r_rd_; // paused read op (async read)
+ detail::pausation paused_r_close_;// paused close op (async read)
public:
+ /// Indicates if the permessage-deflate extension is supported
+ using is_deflate_supported =
+ std::integral_constant<bool, deflateSupported>;
+
/// The type of the next layer.
using next_layer_type =
typename std::remove_reference<NextLayer>::type;
/// The type of the lowest layer.
- using lowest_layer_type =
- typename get_lowest_layer<next_layer_type>::type;
+ using lowest_layer_type = get_lowest_layer<next_layer_type>;
/// The type of the executor associated with the object.
using executor_type = typename next_layer_type::executor_type;
@@ -444,7 +424,11 @@ public:
*/
std::size_t
read_size_hint(
- std::size_t initial_size = +tcp_frame_size) const;
+ std::size_t initial_size = +tcp_frame_size) const
+ {
+ return read_size_hint(initial_size,
+ is_deflate_supported{});
+ }
/** Returns a suggested maximum buffer size for the next call to read.
@@ -475,15 +459,22 @@ public:
//
//--------------------------------------------------------------------------
- /// Set the permessage-deflate extension options
+ /** Set the permessage-deflate extension options
+
+ @throws invalid_argument if `deflateSupported == false`, and either
+ `client_enable` or `server_enable` is `true`.
+ */
void
- set_option(permessage_deflate const& o);
+ set_option(permessage_deflate const& o)
+ {
+ set_option(o, is_deflate_supported{});
+ }
/// Get the permessage-deflate extension options
void
get_option(permessage_deflate& o)
{
- o = pmd_opts_;
+ get_option(o, is_deflate_supported{});
}
/** Set the automatic fragmentation option.
@@ -580,27 +571,26 @@ public:
string_view payload // The payload in the frame
);
@endcode
- The implementation type-erases the callback without requiring
- a dynamic allocation. For this reason, the callback object is
- passed by a non-constant reference.
+ The implementation type-erases the callback which may require
+ a dynamic allocation. To prevent the possiblity of a dynamic
+ allocation, use `std::ref` to wrap the callback.
If the read operation which receives the control frame is
an asynchronous operation, the callback will be invoked using
the same method as that used to invoke the final handler.
- @note It is not necessary to send a close frame upon receipt
- of a close frame. The implementation does this automatically.
- Attempting to send a close frame after a close frame is
- received will result in undefined behavior.
+ @note Incoming ping and close frames are automatically
+ handled. Pings are responded to with pongs, and a close frame
+ is responded to with a close frame leading to the closure of
+ the stream. It is not necessary to manually send pings, pongs,
+ or close frames from inside the control callback.
+ Attempting to manually send a close frame from inside the
+ control callback after receiving a close frame will result
+ in undefined behavior.
*/
- template<class Callback>
void
- control_callback(Callback& cb)
+ control_callback(std::function<void(frame_type, string_view)> cb)
{
- // Callback may not be constant, caller is responsible for
- // managing the lifetime of the callback. Copies are not made.
- BOOST_STATIC_ASSERT(! std::is_const<Callback>::value);
-
- ctrl_cb_ = std::ref(cb);
+ ctrl_cb_ = std::move(cb);
}
/** Reset the control frame callback.
@@ -1183,9 +1173,9 @@ public:
required by the HTTP protocol. Copies of this parameter may
be made as needed.
- @param handler The handler to be called when the request completes.
- Copies will be made of the handler as required. The equivalent
- function signature of the handler must be:
+ @param handler Invoked when the operation completes.
+ The handler may be moved or copied as needed.
+ The equivalent function signature of the handler must be:
@code void handler(
error_code const& ec // Result of operation
); @endcode
@@ -1235,9 +1225,9 @@ public:
required by the HTTP protocol. Copies of this parameter may
be made as needed.
- @param handler The handler to be called when the request completes.
- Copies will be made of the handler as required. The equivalent
- function signature of the handler must be:
+ @param handler Invoked when the operation completes.
+ The handler may be moved or copied as needed.
+ The equivalent function signature of the handler must be:
@code void handler(
error_code const& ec // Result of operation
); @endcode
@@ -1293,9 +1283,9 @@ public:
request_type& req
); @endcode
- @param handler The handler to be called when the request completes.
- Copies will be made of the handler as required. The equivalent
- function signature of the handler must be:
+ @param handler Invoked when the operation completes.
+ The handler may be moved or copied as needed.
+ The equivalent function signature of the handler must be:
@code void handler(
error_code const& ec // Result of operation
); @endcode
@@ -1355,9 +1345,9 @@ public:
request_type& req
); @endcode
- @param handler The handler to be called when the request completes.
- Copies will be made of the handler as required. The equivalent
- function signature of the handler must be:
+ @param handler Invoked when the operation completes.
+ The handler may be moved or copied as needed.
+ The equivalent function signature of the handler must be:
@code void handler(
error_code const& ec // Result of operation
); @endcode
@@ -1930,9 +1920,9 @@ public:
@ref http::read or @ref http::async_read, then call @ref accept
or @ref async_accept with the request.
- @param handler The handler to be called when the request
- completes. Copies will be made of the handler as required. The
- equivalent function signature of the handler must be:
+ @param handler Invoked when the operation completes.
+ The handler may be moved or copied as needed.
+ The equivalent function signature of the handler must be:
@code void handler(
error_code const& ec // Result of operation
); @endcode
@@ -1991,9 +1981,9 @@ public:
response_type& res
); @endcode
- @param handler The handler to be called when the request
- completes. Copies will be made of the handler as required. The
- equivalent function signature of the handler must be:
+ @param handler Invoked when the operation completes.
+ The handler may be moved or copied as needed.
+ The equivalent function signature of the handler must be:
@code void handler(
error_code const& ec // Result of operation
); @endcode
@@ -2054,9 +2044,9 @@ public:
then to received WebSocket frames. The implementation will
copy the caller provided data before the function returns.
- @param handler The handler to be called when the request
- completes. Copies will be made of the handler as required. The
- equivalent function signature of the handler must be:
+ @param handler Invoked when the operation completes.
+ The handler may be moved or copied as needed.
+ The equivalent function signature of the handler must be:
@code void handler(
error_code const& ec // Result of operation
); @endcode
@@ -2132,9 +2122,9 @@ public:
response_type& res
); @endcode
- @param handler The handler to be called when the request
- completes. Copies will be made of the handler as required. The
- equivalent function signature of the handler must be:
+ @param handler Invoked when the operation completes.
+ The handler may be moved or copied as needed.
+ The equivalent function signature of the handler must be:
@code void handler(
error_code const& ec // Result of operation
); @endcode
@@ -2193,9 +2183,9 @@ public:
Ownership is not transferred, the implementation will not access
this object from other threads.
- @param handler The handler to be called when the request
- completes. Copies will be made of the handler as required. The
- equivalent function signature of the handler must be:
+ @param handler Invoked when the operation completes.
+ The handler may be moved or copied as needed.
+ The equivalent function signature of the handler must be:
@code void handler(
error_code const& ec // Result of operation
); @endcode
@@ -2256,9 +2246,9 @@ public:
response_type& res
); @endcode
- @param handler The handler to be called when the request
- completes. Copies will be made of the handler as required. The
- equivalent function signature of the handler must be:
+ @param handler Invoked when the operation completes.
+ The handler may be moved or copied as needed.
+ The equivalent function signature of the handler must be:
@code void handler(
error_code const& ec // Result of operation
); @endcode
@@ -2358,8 +2348,8 @@ public:
next layer's `async_write_some` functions, and is known as a
<em>composed operation</em>. The program must ensure that the
stream performs no other write operations (such as @ref async_ping,
- @ref stream::async_write, @ref stream::async_write_some, or
- @ref stream::async_close) until this operation completes.
+ @ref async_write, @ref async_write_some, or @ref async_close)
+ until this operation completes.
If the close reason specifies a close code other than
@ref beast::websocket::close_code::none, the close frame is
@@ -2373,9 +2363,9 @@ public:
@param cr The reason for the close.
- @param handler The handler to be called when the close operation
- completes. Copies will be made of the handler as required. The
- function signature of the handler must be:
+ @param handler Invoked when the operation completes.
+ The handler may be moved or copied as needed.
+ The function signature of the handler must be:
@code
void handler(
error_code const& ec // Result of operation
@@ -2451,9 +2441,9 @@ public:
@param payload The payload of the ping message, which may be empty.
- @param handler The handler to be called when the read operation
- completes. Copies will be made of the handler as required. The
- function signature of the handler must be:
+ @param handler Invoked when the operation completes.
+ The handler may be moved or copied as needed.
+ The function signature of the handler must be:
@code
void handler(
error_code const& ec // Result of operation
@@ -2544,9 +2534,9 @@ public:
@param payload The payload of the pong message, which may be empty.
- @param handler The handler to be called when the read operation
- completes. Copies will be made of the handler as required. The
- function signature of the handler must be:
+ @param handler Invoked when the operation completes.
+ The handler may be moved or copied as needed.
+ The function signature of the handler must be:
@code
void handler(
error_code const& ec // Result of operation
@@ -2702,9 +2692,9 @@ public:
any masking or decompression has been applied. This object must
remain valid until the handler is called.
- @param handler The handler to be called when the read operation
- completes. Copies will be made of the handler as required. The
- equivalent function signature of the handler must be:
+ @param handler Invoked when the operation completes.
+ The handler may be moved or copied as needed.
+ The equivalent function signature of the handler must be:
@code
void handler(
error_code const& ec, // Result of operation
@@ -2882,9 +2872,9 @@ public:
will append into the buffer. If this value is zero, then a reasonable
size will be chosen automatically.
- @param handler The handler to be called when the read operation
- completes. Copies will be made of the handler as required. The
- equivalent function signature of the handler must be:
+ @param handler Invoked when the operation completes.
+ The handler may be moved or copied as needed.
+ The equivalent function signature of the handler must be:
@code
void handler(
error_code const& ec, // Result of operation
@@ -3055,9 +3045,9 @@ public:
locations pointed to by the buffer sequence remains valid
until the completion handler is called.
- @param handler The handler to be called when the read operation
- completes. Copies will be made of the handler as required. The
- equivalent function signature of the handler must be:
+ @param handler Invoked when the operation completes.
+ The handler may be moved or copied as needed.
+ The equivalent function signature of the handler must be:
@code
void handler(
error_code const& ec, // Result of operation
@@ -3177,8 +3167,8 @@ public:
to the next layer's `async_write_some` functions, and is known
as a <em>composed operation</em>. The program must ensure that
the stream performs no other write operations (such as
- stream::async_write, stream::async_write_some, or
- stream::async_close).
+ @ref async_write, @ref async_write_some, or
+ @ref async_close).
The current setting of the @ref binary option controls
whether the message opcode is set to text or binary. If the
@@ -3193,9 +3183,9 @@ public:
the memory locations pointed to by buffers remains valid
until the completion handler is called.
- @param handler The handler to be called when the write operation
- completes. Copies will be made of the handler as required. The
- function signature of the handler must be:
+ @param handler Invoked when the operation completes.
+ The handler may be moved or copied as needed.
+ The function signature of the handler must be:
@code
void handler(
error_code const& ec, // Result of operation
@@ -3306,8 +3296,8 @@ public:
as a <em>composed operation</em>. The actual payload sent
may be transformed as per the WebSocket protocol settings. The
program must ensure that the stream performs no other write
- operations (such as stream::async_write, stream::async_write_some,
- or stream::async_close).
+ operations (such as @ref async_write, @ref async_write_some,
+ or @ref async_close).
If this is the beginning of a new message, the message opcode
will be set to text or binary as per the current setting of
@@ -3323,9 +3313,9 @@ public:
the caller, which must guarantee that they remain valid until
the handler is called.
- @param handler The handler to be called when the write completes.
- Copies will be made of the handler as required. The equivalent
- function signature of the handler must be:
+ @param handler Invoked when the operation completes.
+ The handler may be moved or copied as needed.
+ The equivalent function signature of the handler must be:
@code void handler(
error_code const& ec, // Result of operation
std::size_t bytes_transferred // Number of bytes written from the
@@ -3343,10 +3333,8 @@ public:
private:
template<class, class> class accept_op;
template<class> class close_op;
- template<class> class fail_op;
template<class> class handshake_op;
template<class> class ping_op;
- template<class> class read_fh_op;
template<class, class> class read_some_op;
template<class, class> class read_op;
template<class> class response_op;
@@ -3356,10 +3344,65 @@ private:
static void default_decorate_req(request_type&) {}
static void default_decorate_res(response_type&) {}
+ void
+ set_option(permessage_deflate const& o, std::true_type);
+
+ void
+ set_option(permessage_deflate const&, std::false_type);
+
+ void
+ get_option(permessage_deflate& o, std::true_type)
+ {
+ o = this->pmd_opts_;
+ }
+
+ void
+ get_option(permessage_deflate& o, std::false_type)
+ {
+ o = {};
+ o.client_enable = false;
+ o.server_enable = false;
+ }
+
void open(role_type role);
+
+ void open_pmd(std::true_type);
+
+ void open_pmd(std::false_type)
+ {
+ }
+
void close();
+
+ void close_pmd(std::true_type)
+ {
+ this->pmd_.reset();
+ }
+
+ void close_pmd(std::false_type)
+ {
+ }
+
void reset();
- void begin_msg();
+
+ void begin_msg()
+ {
+ begin_msg(is_deflate_supported{});
+ }
+
+ void begin_msg(std::true_type);
+
+ void begin_msg(std::false_type);
+
+ std::size_t
+ read_size_hint(
+ std::size_t initial_size,
+ std::true_type) const;
+
+ std::size_t
+ read_size_hint(
+ std::size_t initial_size,
+ std::false_type) const;
bool
check_open(error_code& ec)
@@ -3387,8 +3430,10 @@ private:
template<class DynamicBuffer>
bool
- parse_fh(detail::frame_header& fh,
- DynamicBuffer& b, close_code& code);
+ parse_fh(
+ detail::frame_header& fh,
+ DynamicBuffer& b,
+ error_code& ec);
template<class DynamicBuffer>
void
@@ -3399,6 +3444,10 @@ private:
write_ping(DynamicBuffer& b,
detail::opcode op, ping_data const& data);
+ //
+ // upgrade
+ //
+
template<class Decorator>
request_type
build_request(detail::sec_ws_key_type& key,
@@ -3406,28 +3455,95 @@ private:
string_view target,
Decorator const& decorator);
- template<class Body,
- class Allocator, class Decorator>
+ void
+ build_request_pmd(request_type& req, std::true_type);
+
+ void
+ build_request_pmd(request_type&, std::false_type)
+ {
+ }
+
+ template<
+ class Body, class Allocator, class Decorator>
response_type
- build_response(http::request<Body,
- http::basic_fields<Allocator>> const& req,
- Decorator const& decorator);
+ build_response(
+ http::request<Body,
+ http::basic_fields<Allocator>> const& req,
+ Decorator const& decorator,
+ error_code& ec);
+ template<class Body, class Allocator>
void
- on_response(response_type const& resp,
- detail::sec_ws_key_type const& key, error_code& ec);
+ build_response_pmd(
+ response_type& res,
+ http::request<Body,
+ http::basic_fields<Allocator>> const& req,
+ std::true_type);
+
+ template<class Body, class Allocator>
+ void
+ build_response_pmd(
+ response_type&,
+ http::request<Body,
+ http::basic_fields<Allocator>> const&,
+ std::false_type)
+ {
+ }
+
+ void
+ on_response(
+ response_type const& res,
+ detail::sec_ws_key_type const& key,
+ error_code& ec);
+
+ void
+ on_response_pmd(
+ response_type const& res,
+ std::true_type);
+
+ void
+ on_response_pmd(
+ response_type const&,
+ std::false_type)
+ {
+ }
+
+ //
+ // accept / handshake
+ //
+
+ template<class Allocator>
+ void
+ do_pmd_config(
+ http::basic_fields<Allocator> const& h,
+ std::true_type)
+ {
+ pmd_read(this->pmd_config_, h);
+ }
+
+ template<class Allocator>
+ void
+ do_pmd_config(
+ http::basic_fields<Allocator> const&,
+ std::false_type)
+ {
+ }
template<class Decorator>
void
- do_accept(Decorator const& decorator,
+ do_accept(
+ Decorator const& decorator,
error_code& ec);
- template<class Body, class Allocator,
+ template<
+ class Body, class Allocator,
class Decorator>
void
- do_accept(http::request<Body,
- http::basic_fields<Allocator>> const& req,
- Decorator const& decorator, error_code& ec);
+ do_accept(
+ http::request<Body,
+ http::basic_fields<Allocator>> const& req,
+ Decorator const& decorator,
+ error_code& ec);
template<class RequestDecorator>
void
@@ -3436,6 +3552,10 @@ private:
RequestDecorator const& decorator,
error_code& ec);
+ //
+ // fail
+ //
+
void
do_fail(
std::uint16_t code,
diff --git a/boost/beast/websocket/stream_fwd.hpp b/boost/beast/websocket/stream_fwd.hpp
new file mode 100644
index 0000000000..97ffcb49ed
--- /dev/null
+++ b/boost/beast/websocket/stream_fwd.hpp
@@ -0,0 +1,28 @@
+//
+// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// Official repository: https://github.com/boostorg/beast
+//
+
+#ifndef BOOST_BEAST_WEBSOCKET_STREAM_FWD_HPP
+#define BOOST_BEAST_WEBSOCKET_STREAM_FWD_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+
+namespace boost {
+namespace beast {
+namespace websocket {
+
+template<
+ class NextLayer,
+ bool deflateSupported = true>
+class stream;
+
+} // websocket
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/websocket/teardown.hpp b/boost/beast/websocket/teardown.hpp
index 289b57c853..a255756dad 100644
--- a/boost/beast/websocket/teardown.hpp
+++ b/boost/beast/websocket/teardown.hpp
@@ -69,9 +69,9 @@ teardown(
@param socket The socket to tear down.
- @param handler The handler to be called when the request completes.
- Copies will be made of the handler as required. The equivalent
- function signature of the handler must be:
+ @param handler Invoked when the operation completes.
+ The handler may be moved or copied as needed.
+ The equivalent function signature of the handler must be:
@code void handler(
error_code const& error // result of operation
);
@@ -145,9 +145,9 @@ teardown(
@param socket The socket to tear down.
- @param handler The handler to be called when the request completes.
- Copies will be made of the handler as required. The equivalent
- function signature of the handler must be:
+ @param handler Invoked when the operation completes.
+ The handler may be moved or copied as needed.
+ The equivalent function signature of the handler must be:
@code void handler(
error_code const& error // result of operation
);