summaryrefslogtreecommitdiff
path: root/boost/beast/websocket
diff options
context:
space:
mode:
Diffstat (limited to 'boost/beast/websocket')
-rw-r--r--boost/beast/websocket/detail/decorator.hpp315
-rw-r--r--boost/beast/websocket/detail/error.hpp78
-rw-r--r--boost/beast/websocket/detail/frame.hpp40
-rw-r--r--boost/beast/websocket/detail/hybi13.hpp51
-rw-r--r--boost/beast/websocket/detail/hybi13.ipp67
-rw-r--r--boost/beast/websocket/detail/impl_base.hpp487
-rw-r--r--boost/beast/websocket/detail/mask.hpp18
-rw-r--r--boost/beast/websocket/detail/pausation.hpp162
-rw-r--r--boost/beast/websocket/detail/pmd_extension.hpp293
-rw-r--r--boost/beast/websocket/detail/pmd_extension.ipp310
-rw-r--r--boost/beast/websocket/detail/prng.hpp123
-rw-r--r--boost/beast/websocket/detail/prng.ipp327
-rw-r--r--boost/beast/websocket/detail/service.hpp78
-rw-r--r--boost/beast/websocket/detail/service.ipp65
-rw-r--r--boost/beast/websocket/detail/soft_mutex.hpp112
-rw-r--r--boost/beast/websocket/detail/stream_base.hpp413
-rw-r--r--boost/beast/websocket/detail/type_traits.hpp4
-rw-r--r--boost/beast/websocket/detail/utf8_checker.hpp337
-rw-r--r--boost/beast/websocket/detail/utf8_checker.ipp331
-rw-r--r--boost/beast/websocket/error.hpp6
-rw-r--r--boost/beast/websocket/impl/accept.hpp862
-rw-r--r--boost/beast/websocket/impl/accept.ipp774
-rw-r--r--boost/beast/websocket/impl/close.hpp405
-rw-r--r--boost/beast/websocket/impl/close.ipp447
-rw-r--r--boost/beast/websocket/impl/error.hpp44
-rw-r--r--boost/beast/websocket/impl/error.ipp218
-rw-r--r--boost/beast/websocket/impl/handshake.hpp540
-rw-r--r--boost/beast/websocket/impl/handshake.ipp427
-rw-r--r--boost/beast/websocket/impl/ping.hpp329
-rw-r--r--boost/beast/websocket/impl/ping.ipp282
-rw-r--r--boost/beast/websocket/impl/read.hpp1286
-rw-r--r--boost/beast/websocket/impl/read.ipp1368
-rw-r--r--boost/beast/websocket/impl/rfc6455.hpp (renamed from boost/beast/websocket/impl/rfc6455.ipp)6
-rw-r--r--boost/beast/websocket/impl/ssl.hpp (renamed from boost/beast/websocket/impl/ssl.ipp)18
-rw-r--r--boost/beast/websocket/impl/stream.hpp359
-rw-r--r--boost/beast/websocket/impl/stream.ipp894
-rw-r--r--boost/beast/websocket/impl/stream_impl.hpp939
-rw-r--r--boost/beast/websocket/impl/teardown.hpp198
-rw-r--r--boost/beast/websocket/impl/teardown.ipp230
-rw-r--r--boost/beast/websocket/impl/write.hpp784
-rw-r--r--boost/beast/websocket/impl/write.ipp897
-rw-r--r--boost/beast/websocket/option.hpp2
-rw-r--r--boost/beast/websocket/rfc6455.hpp14
-rw-r--r--boost/beast/websocket/role.hpp48
-rw-r--r--boost/beast/websocket/ssl.hpp36
-rw-r--r--boost/beast/websocket/stream.hpp3277
-rw-r--r--boost/beast/websocket/stream_base.hpp178
-rw-r--r--boost/beast/websocket/stream_fwd.hpp6
-rw-r--r--boost/beast/websocket/teardown.hpp61
49 files changed, 9605 insertions, 8941 deletions
diff --git a/boost/beast/websocket/detail/decorator.hpp b/boost/beast/websocket/detail/decorator.hpp
new file mode 100644
index 0000000000..cab7f9a298
--- /dev/null
+++ b/boost/beast/websocket/detail/decorator.hpp
@@ -0,0 +1,315 @@
+//
+// Copyright (c) 2016-2019 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_DECORATOR_HPP
+#define BOOST_BEAST_WEBSOCKET_DETAIL_DECORATOR_HPP
+
+#include <boost/beast/websocket/rfc6455.hpp>
+#include <boost/beast/core/detail/type_traits.hpp>
+#include <boost/core/exchange.hpp>
+#include <boost/type_traits/make_void.hpp>
+#include <algorithm>
+#include <memory>
+#include <new>
+#include <type_traits>
+#include <utility>
+
+namespace boost {
+namespace beast {
+namespace websocket {
+namespace detail {
+
+// VFALCO NOTE: When this is two traits, one for
+// request and one for response,
+// Visual Studio 2015 fails.
+
+template<class T, class U, class = void>
+struct can_invoke_with : std::false_type
+{
+};
+
+template<class T, class U>
+struct can_invoke_with<T, U, boost::void_t<decltype(
+ std::declval<T&>()(std::declval<U&>()))>>
+ : std::true_type
+{
+};
+
+template<class T>
+using is_decorator = std::integral_constant<bool,
+ can_invoke_with<T, request_type>::value ||
+ can_invoke_with<T, response_type>::value>;
+
+class decorator
+{
+ friend class decorator_test;
+
+ struct incomplete;
+
+ struct exemplar
+ {
+ void (incomplete::*mf)();
+ std::shared_ptr<incomplete> sp;
+ void* param;
+ };
+
+ union storage
+ {
+ void* p_;
+ void (*fn_)();
+ typename std::aligned_storage<
+ sizeof(exemplar),
+ alignof(exemplar)>::type buf_;
+ };
+
+ struct vtable
+ {
+ void (*move)(
+ storage& dst, storage& src) noexcept;
+ void (*destroy)(storage& dst) noexcept;
+ void (*invoke_req)(
+ storage& dst, request_type& req);
+ void (*invoke_res)(
+ storage& dst, response_type& req);
+
+ static void move_fn(
+ storage&, storage&) noexcept
+ {
+ }
+
+ static void destroy_fn(
+ storage&) noexcept
+ {
+ }
+
+ static void invoke_req_fn(
+ storage&, request_type&)
+ {
+ }
+
+ static void invoke_res_fn(
+ storage&, response_type&)
+ {
+ }
+
+ static vtable const* get_default()
+ {
+ static const vtable impl{
+ &move_fn,
+ &destroy_fn,
+ &invoke_req_fn,
+ &invoke_res_fn
+ };
+ return &impl;
+ }
+ };
+
+ template<class F, bool Inline =
+ (sizeof(F) <= sizeof(storage) &&
+ alignof(F) <= alignof(storage) &&
+ std::is_nothrow_move_constructible<F>::value)>
+ struct vtable_impl;
+
+ storage storage_;
+ vtable const* vtable_ = vtable::get_default();
+
+ // VFALCO NOTE: When this is two traits, one for
+ // request and one for response,
+ // Visual Studio 2015 fails.
+
+ template<class T, class U, class = void>
+ struct maybe_invoke
+ {
+ void
+ operator()(T&, U&)
+ {
+ }
+ };
+
+ template<class T, class U>
+ struct maybe_invoke<T, U, boost::void_t<decltype(
+ std::declval<T&>()(std::declval<U&>()))>>
+ {
+ void
+ operator()(T& t, U& u)
+ {
+ t(u);
+ }
+ };
+
+public:
+ decorator() = default;
+ decorator(decorator const&) = delete;
+ decorator& operator=(decorator const&) = delete;
+
+ ~decorator()
+ {
+ vtable_->destroy(storage_);
+ }
+
+ decorator(decorator&& other) noexcept
+ : vtable_(boost::exchange(
+ other.vtable_, vtable::get_default()))
+ {
+ vtable_->move(
+ storage_, other.storage_);
+ }
+
+ template<class F,
+ class = typename std::enable_if<
+ ! std::is_convertible<
+ F, decorator>::value>::type>
+ explicit
+ decorator(F&& f)
+ : vtable_(vtable_impl<
+ typename std::decay<F>::type>::
+ construct(storage_, std::forward<F>(f)))
+ {
+ }
+
+ decorator&
+ operator=(decorator&& other) noexcept
+ {
+ vtable_->destroy(storage_);
+ vtable_ = boost::exchange(
+ other.vtable_, vtable::get_default());
+ vtable_->move(storage_, other.storage_);
+ return *this;
+ }
+
+ void
+ operator()(request_type& req)
+ {
+ vtable_->invoke_req(storage_, req);
+ }
+
+ void
+ operator()(response_type& res)
+ {
+ vtable_->invoke_res(storage_, res);
+ }
+};
+
+template<class F>
+struct decorator::vtable_impl<F, true>
+{
+ template<class Arg>
+ static
+ vtable const*
+ construct(storage& dst, Arg&& arg)
+ {
+ ::new (static_cast<void*>(&dst.buf_)) F(
+ std::forward<Arg>(arg));
+ return get();
+ }
+
+ static
+ void
+ move(storage& dst, storage& src) noexcept
+ {
+ auto& f = *reinterpret_cast<F*>(&src.buf_);
+ ::new (&dst.buf_) F(std::move(f));
+ }
+
+ static
+ void
+ destroy(storage& dst) noexcept
+ {
+ reinterpret_cast<F*>(&dst.buf_)->~F();
+ }
+
+ static
+ void
+ invoke_req(storage& dst, request_type& req)
+ {
+ maybe_invoke<F, request_type>{}(
+ *reinterpret_cast<F*>(&dst.buf_), req);
+ }
+
+ static
+ void
+ invoke_res(storage& dst, response_type& res)
+ {
+ maybe_invoke<F, response_type>{}(
+ *reinterpret_cast<F*>(&dst.buf_), res);
+ }
+
+ static
+ vtable
+ const* get()
+ {
+ static constexpr vtable impl{
+ &move,
+ &destroy,
+ &invoke_req,
+ &invoke_res};
+ return &impl;
+ }
+};
+
+template<class F>
+struct decorator::vtable_impl<F, false>
+{
+ template<class Arg>
+ static
+ vtable const*
+ construct(storage& dst, Arg&& arg)
+ {
+ dst.p_ = new F(std::forward<Arg>(arg));
+ return get();
+ }
+
+ static
+ void
+ move(storage& dst, storage& src) noexcept
+ {
+ dst.p_ = src.p_;
+ }
+
+ static
+ void
+ destroy(storage& dst) noexcept
+ {
+ delete static_cast<F*>(dst.p_);
+ }
+
+ static
+ void
+ invoke_req(
+ storage& dst, request_type& req)
+ {
+ maybe_invoke<F, request_type>{}(
+ *static_cast<F*>(dst.p_), req);
+ }
+
+ static
+ void
+ invoke_res(
+ storage& dst, response_type& res)
+ {
+ maybe_invoke<F, response_type>{}(
+ *static_cast<F*>(dst.p_), res);
+ }
+
+ static
+ vtable const*
+ get()
+ {
+ static constexpr vtable impl{&move,
+ &destroy, &invoke_req, &invoke_res};
+ return &impl;
+ }
+};
+
+} // detail
+} // websocket
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/websocket/detail/error.hpp b/boost/beast/websocket/detail/error.hpp
deleted file mode 100644
index 1cf63fc68a..0000000000
--- a/boost/beast/websocket/detail/error.hpp
+++ /dev/null
@@ -1,78 +0,0 @@
-//
-// 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_HPP
-#define BOOST_BEAST_WEBSOCKET_DETAIL_ERROR_HPP
-
-#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 be79a8af39..1603bb556e 100644
--- a/boost/beast/websocket/detail/frame.hpp
+++ b/boost/beast/websocket/detail/frame.hpp
@@ -1,5 +1,5 @@
//
-// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+// Copyright (c) 2016-2019 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)
@@ -10,6 +10,7 @@
#ifndef BOOST_BEAST_WEBSOCKET_DETAIL_FRAME_HPP
#define BOOST_BEAST_WEBSOCKET_DETAIL_FRAME_HPP
+#include <boost/beast/core/buffer_traits.hpp>
#include <boost/beast/websocket/error.hpp>
#include <boost/beast/websocket/rfc6455.hpp>
#include <boost/beast/websocket/detail/utf8_checker.hpp>
@@ -18,7 +19,15 @@
#include <boost/beast/core/static_string.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/assert.hpp>
+// This is for <boost/endian/buffers.hpp>
+#if BOOST_WORKAROUND(BOOST_MSVC, > 0)
+# pragma warning (push)
+# pragma warning (disable: 4127) // conditional expression is constant
+#endif
#include <boost/endian/buffers.hpp>
+#if BOOST_WORKAROUND(BOOST_MSVC, > 0)
+# pragma warning (pop)
+#endif
#include <cstdint>
namespace boost {
@@ -184,8 +193,6 @@ template<class DynamicBuffer>
void
write(DynamicBuffer& db, frame_header const& fh)
{
- using boost::asio::buffer;
- using boost::asio::buffer_copy;
using namespace boost::endian;
std::size_t n;
std::uint8_t b[14];
@@ -220,8 +227,8 @@ write(DynamicBuffer& db, frame_header const& fh)
native_to_little_uint32(fh.key, &b[n]);
n += 4;
}
- db.commit(buffer_copy(
- db.prepare(n), buffer(b)));
+ db.commit(net::buffer_copy(
+ db.prepare(n), net::buffer(b)));
}
// Read data from buffers
@@ -231,12 +238,9 @@ template<class Buffers>
void
read_ping(ping_data& data, Buffers const& bs)
{
- using boost::asio::buffer_copy;
- using boost::asio::buffer_size;
- using boost::asio::mutable_buffer;
- BOOST_ASSERT(buffer_size(bs) <= data.max_size());
- data.resize(buffer_size(bs));
- buffer_copy(mutable_buffer{
+ BOOST_ASSERT(buffer_bytes(bs) <= data.max_size());
+ data.resize(buffer_bytes(bs));
+ net::buffer_copy(net::mutable_buffer{
data.data(), data.size()}, bs);
}
@@ -250,16 +254,13 @@ read_close(
Buffers const& bs,
error_code& ec)
{
- using boost::asio::buffer;
- using boost::asio::buffer_copy;
- using boost::asio::buffer_size;
using namespace boost::endian;
- auto n = buffer_size(bs);
+ auto n = buffer_bytes(bs);
BOOST_ASSERT(n <= 125);
if(n == 0)
{
cr = close_reason{};
- ec.assign(0, ec.category());
+ ec = {};
return;
}
if(n == 1)
@@ -271,7 +272,7 @@ read_close(
buffers_suffix<Buffers> cb(bs);
{
std::uint8_t b[2];
- buffer_copy(buffer(b), cb);
+ net::buffer_copy(net::buffer(b), cb);
cr.code = big_uint16_to_native(&b[0]);
cb.consume(2);
n -= 2;
@@ -285,7 +286,8 @@ read_close(
if(n > 0)
{
cr.reason.resize(n);
- buffer_copy(buffer(&cr.reason[0], n), cb);
+ net::buffer_copy(
+ net::buffer(&cr.reason[0], n), cb);
if(! check_utf8(
cr.reason.data(), cr.reason.size()))
{
@@ -298,7 +300,7 @@ read_close(
{
cr.reason = "";
}
- ec.assign(0, ec.category());
+ ec = {};
}
} // detail
diff --git a/boost/beast/websocket/detail/hybi13.hpp b/boost/beast/websocket/detail/hybi13.hpp
index 424a9cc9e5..7aeae2a386 100644
--- a/boost/beast/websocket/detail/hybi13.hpp
+++ b/boost/beast/websocket/detail/hybi13.hpp
@@ -1,5 +1,5 @@
//
-// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+// Copyright (c) 2016-2019 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)
@@ -13,13 +13,7 @@
#include <boost/beast/core/static_string.hpp>
#include <boost/beast/core/string.hpp>
#include <boost/beast/core/detail/base64.hpp>
-#include <boost/beast/core/detail/sha1.hpp>
-#include <boost/beast/websocket/detail/stream_base.hpp>
-#include <boost/assert.hpp>
-#include <array>
#include <cstdint>
-#include <string>
-#include <type_traits>
namespace boost {
namespace beast {
@@ -32,46 +26,23 @@ using sec_ws_key_type = static_string<
using sec_ws_accept_type = static_string<
beast::detail::base64::encoded_size(20)>;
-inline
+BOOST_BEAST_DECL
void
-make_sec_ws_key(sec_ws_key_type& key)
-{
- auto p = stream_prng::prng();
- char a[16];
- for(int i = 0; i < 16; i += 4)
- {
- auto const v = p->secure();
- a[i ] = v & 0xff;
- a[i+1] = (v >> 8) & 0xff;
- a[i+2] = (v >> 16) & 0xff;
- a[i+3] = (v >> 24) & 0xff;
- }
- key.resize(key.max_size());
- key.resize(beast::detail::base64::encode(
- key.data(), &a[0], 16));
-}
+make_sec_ws_key(sec_ws_key_type& key);
-template<class = void>
+BOOST_BEAST_DECL
void
-make_sec_ws_accept(sec_ws_accept_type& accept,
- string_view key)
-{
- BOOST_ASSERT(key.size() <= sec_ws_key_type::max_size_n);
- static_string<sec_ws_key_type::max_size_n + 36> m(key);
- m.append("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
- beast::detail::sha1_context ctx;
- beast::detail::init(ctx);
- beast::detail::update(ctx, m.data(), m.size());
- char digest[beast::detail::sha1_context::digest_size];
- beast::detail::finish(ctx, &digest[0]);
- accept.resize(accept.max_size());
- accept.resize(beast::detail::base64::encode(
- accept.data(), &digest[0], sizeof(digest)));
-}
+make_sec_ws_accept(
+ sec_ws_accept_type& accept,
+ string_view key);
} // detail
} // websocket
} // beast
} // boost
+#if BOOST_BEAST_HEADER_ONLY
+#include <boost/beast/websocket/detail/hybi13.ipp>
+#endif
+
#endif
diff --git a/boost/beast/websocket/detail/hybi13.ipp b/boost/beast/websocket/detail/hybi13.ipp
new file mode 100644
index 0000000000..b0ed57756b
--- /dev/null
+++ b/boost/beast/websocket/detail/hybi13.ipp
@@ -0,0 +1,67 @@
+//
+// Copyright (c) 2016-2019 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_HYBI13_IPP
+#define BOOST_BEAST_WEBSOCKET_DETAIL_HYBI13_IPP
+
+#include <boost/beast/websocket/detail/hybi13.hpp>
+#include <boost/beast/core/detail/sha1.hpp>
+#include <boost/beast/websocket/detail/prng.hpp>
+
+#include <boost/assert.hpp>
+#include <cstdint>
+#include <string>
+
+namespace boost {
+namespace beast {
+namespace websocket {
+namespace detail {
+
+void
+make_sec_ws_key(sec_ws_key_type& key)
+{
+ auto g = make_prng(true);
+ char a[16];
+ for(int i = 0; i < 16; i += 4)
+ {
+ auto const v = g();
+ a[i ] = v & 0xff;
+ a[i+1] = (v >> 8) & 0xff;
+ a[i+2] = (v >> 16) & 0xff;
+ a[i+3] = (v >> 24) & 0xff;
+ }
+ key.resize(key.max_size());
+ key.resize(beast::detail::base64::encode(
+ key.data(), &a[0], 16));
+}
+
+void
+make_sec_ws_accept(
+ sec_ws_accept_type& accept,
+ string_view key)
+{
+ BOOST_ASSERT(key.size() <= sec_ws_key_type::max_size_n);
+ static_string<sec_ws_key_type::max_size_n + 36> m(key);
+ m.append("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
+ beast::detail::sha1_context ctx;
+ beast::detail::init(ctx);
+ beast::detail::update(ctx, m.data(), m.size());
+ char digest[beast::detail::sha1_context::digest_size];
+ beast::detail::finish(ctx, &digest[0]);
+ accept.resize(accept.max_size());
+ accept.resize(beast::detail::base64::encode(
+ accept.data(), &digest[0], sizeof(digest)));
+}
+
+} // detail
+} // websocket
+} // beast
+} // boost
+
+#endif // BOOST_BEAST_WEBSOCKET_DETAIL_HYBI13_IPP
diff --git a/boost/beast/websocket/detail/impl_base.hpp b/boost/beast/websocket/detail/impl_base.hpp
new file mode 100644
index 0000000000..fc5bf0bcec
--- /dev/null
+++ b/boost/beast/websocket/detail/impl_base.hpp
@@ -0,0 +1,487 @@
+//
+// Copyright (c) 2016-2019 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_IMPL_BASE_HPP
+#define BOOST_BEAST_WEBSOCKET_DETAIL_IMPL_BASE_HPP
+
+#include <boost/beast/websocket/option.hpp>
+#include <boost/beast/websocket/detail/frame.hpp>
+#include <boost/beast/websocket/detail/pmd_extension.hpp>
+#include <boost/beast/core/buffer_traits.hpp>
+#include <boost/beast/core/role.hpp>
+#include <boost/beast/http/empty_body.hpp>
+#include <boost/beast/http/message.hpp>
+#include <boost/beast/http/string_body.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/beast/core/detail/clamp.hpp>
+#include <boost/asio/buffer.hpp>
+#include <cstdint>
+#include <memory>
+#include <stdexcept>
+
+namespace boost {
+namespace beast {
+namespace websocket {
+namespace detail {
+
+//------------------------------------------------------------------------------
+
+template<bool deflateSupported>
+struct impl_base;
+
+template<>
+struct impl_base<true>
+{
+ // 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
+ }
+
+ // Compress a buffer sequence
+ // Returns: `true` if more calls are needed
+ //
+ template<class ConstBufferSequence>
+ bool
+ deflate(
+ net::mutable_buffer& out,
+ buffers_suffix<ConstBufferSequence>& cb,
+ bool fin,
+ std::size_t& total_in,
+ error_code& ec)
+ {
+ 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::buffers_range_ref(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 = {};
+ 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 = buffer_bytes(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 = {};
+ 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 = net::buffer(out.data(), zs.total_out);
+ return false;
+ }
+ }
+ }
+ ec = {};
+ out = net::buffer(out.data(), zs.total_out);
+ return true;
+ }
+
+ void
+ 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();
+ }
+ }
+
+ void
+ inflate(
+ zlib::z_params& zs,
+ zlib::Flush flush,
+ error_code& ec)
+ {
+ pmd_->zi.write(zs, flush, ec);
+ }
+
+ void
+ 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.clear();
+ }
+ }
+
+ template<class Body, class Allocator>
+ void
+ build_response_pmd(
+ http::response<http::string_body>& res,
+ http::request<Body,
+ http::basic_fields<Allocator>> const& req);
+
+ void
+ on_response_pmd(
+ http::response<http::string_body> const& res)
+ {
+ detail::pmd_offer offer;
+ detail::pmd_read(offer, res);
+ // VFALCO see if offer satisfies pmd_config_,
+ // return an error if not.
+ pmd_config_ = offer; // overwrite for now
+ }
+
+ template<class Allocator>
+ void
+ do_pmd_config(
+ http::basic_fields<Allocator> const& h)
+ {
+ detail::pmd_read(pmd_config_, h);
+ }
+
+ void
+ set_option_pmd(permessage_deflate const& o)
+ {
+ if( o.server_max_window_bits > 15 ||
+ o.server_max_window_bits < 9)
+ BOOST_THROW_EXCEPTION(std::invalid_argument{
+ "invalid server_max_window_bits"});
+ if( o.client_max_window_bits > 15 ||
+ o.client_max_window_bits < 9)
+ BOOST_THROW_EXCEPTION(std::invalid_argument{
+ "invalid client_max_window_bits"});
+ if( o.compLevel < 0 ||
+ o.compLevel > 9)
+ BOOST_THROW_EXCEPTION(std::invalid_argument{
+ "invalid compLevel"});
+ if( o.memLevel < 1 ||
+ o.memLevel > 9)
+ BOOST_THROW_EXCEPTION(std::invalid_argument{
+ "invalid memLevel"});
+ pmd_opts_ = o;
+ }
+
+ void
+ get_option_pmd(permessage_deflate& o)
+ {
+ o = pmd_opts_;
+ }
+
+
+ void
+ build_request_pmd(http::request<http::empty_body>& req)
+ {
+ if(pmd_opts_.client_enable)
+ {
+ detail::pmd_offer config;
+ config.accept = true;
+ config.server_max_window_bits =
+ pmd_opts_.server_max_window_bits;
+ config.client_max_window_bits =
+ pmd_opts_.client_max_window_bits;
+ config.server_no_context_takeover =
+ pmd_opts_.server_no_context_takeover;
+ config.client_no_context_takeover =
+ pmd_opts_.client_no_context_takeover;
+ detail::pmd_write(req, config);
+ }
+ }
+
+ void
+ open_pmd(role_type role)
+ {
+ if(((role == role_type::client &&
+ pmd_opts_.client_enable) ||
+ (role == role_type::server &&
+ pmd_opts_.server_enable)) &&
+ pmd_config_.accept)
+ {
+ detail::pmd_normalize(pmd_config_);
+ pmd_.reset(::new 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,
+ 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,
+ zlib::Strategy::normal);
+ }
+ }
+ }
+
+ void close_pmd()
+ {
+ pmd_.reset();
+ }
+
+ bool pmd_enabled() const
+ {
+ return pmd_ != nullptr;
+ }
+
+ std::size_t
+ read_size_hint_pmd(
+ std::size_t initial_size,
+ bool rd_done,
+ std::uint64_t rd_remain,
+ detail::frame_header const& rd_fh) 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<>
+struct impl_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(
+ net::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)
+ {
+ }
+
+ template<class Body, class Allocator>
+ void
+ build_response_pmd(
+ http::response<http::string_body>&,
+ http::request<Body,
+ http::basic_fields<Allocator>> const&);
+
+ void
+ on_response_pmd(
+ http::response<http::string_body> const&)
+ {
+ }
+
+ template<class Allocator>
+ void
+ do_pmd_config(http::basic_fields<Allocator> const&)
+ {
+ }
+
+ void
+ set_option_pmd(permessage_deflate const& o)
+ {
+ if(o.client_enable || o.server_enable)
+ {
+ // Can't enable permessage-deflate
+ // when deflateSupported == false.
+ //
+ BOOST_THROW_EXCEPTION(std::invalid_argument{
+ "deflateSupported == false"});
+ }
+ }
+
+ void
+ get_option_pmd(permessage_deflate& o)
+ {
+ o = {};
+ o.client_enable = false;
+ o.server_enable = false;
+ }
+
+ void
+ build_request_pmd(
+ http::request<http::empty_body>&)
+ {
+ }
+
+ void open_pmd(role_type)
+ {
+ }
+
+ void close_pmd()
+ {
+ }
+
+ bool pmd_enabled() const
+ {
+ return false;
+ }
+
+ std::size_t
+ read_size_hint_pmd(
+ std::size_t initial_size,
+ bool rd_done,
+ std::uint64_t rd_remain,
+ frame_header const& rd_fh) 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;
+ }
+};
+
+} // detail
+} // websocket
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/websocket/detail/mask.hpp b/boost/beast/websocket/detail/mask.hpp
index 9f43a5782b..ade89fdae5 100644
--- a/boost/beast/websocket/detail/mask.hpp
+++ b/boost/beast/websocket/detail/mask.hpp
@@ -1,5 +1,5 @@
//
-// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+// Copyright (c) 2016-2019 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)
@@ -11,7 +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/beast/core/buffers_range.hpp>
#include <boost/asio/buffer.hpp>
#include <array>
#include <climits>
@@ -49,7 +49,7 @@ rol(std::array<unsigned char, N>& v, std::size_t n)
//
inline
void
-mask_inplace(boost::asio::mutable_buffer& b, prepared_key& key)
+mask_inplace(net::mutable_buffer& b, prepared_key& key)
{
auto n = b.size();
auto mask = key; // avoid aliasing
@@ -71,12 +71,16 @@ mask_inplace(boost::asio::mutable_buffer& b, prepared_key& key)
// Apply mask in place
//
-template<class MutableBuffers, class KeyType>
+template<
+ class MutableBufferSequence,
+ class KeyType>
void
-mask_inplace(MutableBuffers const& bs, KeyType& key)
+mask_inplace(
+ MutableBufferSequence const& buffers,
+ KeyType& key)
{
- for(boost::asio::mutable_buffer b :
- beast::detail::buffers_range(bs))
+ for(net::mutable_buffer b :
+ beast::buffers_range_ref(buffers))
mask_inplace(b, key);
}
diff --git a/boost/beast/websocket/detail/pausation.hpp b/boost/beast/websocket/detail/pausation.hpp
deleted file mode 100644
index e3870b038a..0000000000
--- a/boost/beast/websocket/detail/pausation.hpp
+++ /dev/null
@@ -1,162 +0,0 @@
-//
-// 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_PAUSATION_HPP
-#define BOOST_BEAST_WEBSOCKET_DETAIL_PAUSATION_HPP
-
-#include <boost/beast/core/detail/allocator.hpp>
-#include <boost/asio/associated_allocator.hpp>
-#include <boost/assert.hpp>
-#include <memory>
-#include <utility>
-
-namespace boost {
-namespace beast {
-namespace websocket {
-namespace detail {
-
-// A container that holds a suspended, asynchronous composed
-// operation. The contained object may be invoked later to
-// resume the operation, or the container may be destroyed.
-//
-class pausation
-{
- struct handler
- {
- handler() = default;
- handler(handler &&) = delete;
- handler(handler const&) = delete;
- virtual ~handler() = default;
- virtual void destroy() = 0;
- virtual void invoke() = 0;
- };
-
- template<class Handler>
- class impl : public handler
- {
- Handler h_;
-
- public:
- template<class DeducedHandler>
- impl(DeducedHandler&& h)
- : h_(std::forward<DeducedHandler>(h))
- {
- }
-
- void
- destroy() override
- {
- 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
- invoke() override
- {
- 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();
- }
- };
-
- handler* h_ = nullptr;
-
-public:
- pausation() = default;
- pausation(pausation const&) = delete;
- pausation& operator=(pausation const&) = delete;
-
- ~pausation()
- {
- if(h_)
- h_->destroy();
- }
-
- pausation(pausation&& other)
- {
- boost::ignore_unused(other);
- BOOST_ASSERT(! other.h_);
- }
-
- pausation&
- operator=(pausation&& other)
- {
- boost::ignore_unused(other);
- BOOST_ASSERT(! h_);
- BOOST_ASSERT(! other.h_);
- return *this;
- }
-
- template<class CompletionHandler>
- void
- emplace(CompletionHandler&& handler);
-
- explicit
- operator bool() const
- {
- return h_ != nullptr;
- }
-
- bool
- maybe_invoke()
- {
- if(h_)
- {
- auto const h = h_;
- h_ = nullptr;
- h->invoke();
- return true;
- }
- return false;
- }
-};
-
-template<class CompletionHandler>
-void
-pausation::emplace(CompletionHandler&& handler)
-{
- 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
-} // websocket
-} // beast
-} // boost
-
-#endif
diff --git a/boost/beast/websocket/detail/pmd_extension.hpp b/boost/beast/websocket/detail/pmd_extension.hpp
index ce4f2425cf..cdad99baa4 100644
--- a/boost/beast/websocket/detail/pmd_extension.hpp
+++ b/boost/beast/websocket/detail/pmd_extension.hpp
@@ -1,5 +1,5 @@
//
-// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+// Copyright (c) 2016-2019 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)
@@ -11,13 +11,8 @@
#define BOOST_BEAST_WEBSOCKET_DETAIL_PMD_EXTENSION_HPP
#include <boost/beast/core/error.hpp>
-#include <boost/beast/core/buffers_suffix.hpp>
-#include <boost/beast/core/read_size.hpp>
-#include <boost/beast/zlib/deflate_stream.hpp>
-#include <boost/beast/zlib/inflate_stream.hpp>
#include <boost/beast/websocket/option.hpp>
#include <boost/beast/http/rfc7230.hpp>
-#include <boost/asio/buffer.hpp>
#include <utility>
#include <type_traits>
@@ -48,28 +43,24 @@ struct pmd_offer
bool client_no_context_takeover;
};
-template<class = void>
+BOOST_BEAST_DECL
int
-parse_bits(string_view s)
-{
- if(s.size() == 0)
- return -1;
- if(s.size() > 2)
- return -1;
- if(s[0] < '1' || s[0] > '9')
- return -1;
- unsigned i = 0;
- for(auto c : s)
- {
- if(c < '0' || c > '9')
- return -1;
- auto const i0 = i;
- i = 10 * i + (c - '0');
- if(i < i0)
- return -1;
- }
- return static_cast<int>(i);
-}
+parse_bits(string_view s);
+
+BOOST_BEAST_DECL
+void
+pmd_read_impl(pmd_offer& offer, http::ext_list const& list);
+
+BOOST_BEAST_DECL
+static_string<512>
+pmd_write_impl(pmd_offer const& offer);
+
+BOOST_BEAST_DECL
+static_string<512>
+pmd_negotiate_impl(
+ pmd_offer& config,
+ pmd_offer const& offer,
+ permessage_deflate const& o);
// Parse permessage-deflate request fields
//
@@ -78,126 +69,9 @@ void
pmd_read(pmd_offer& offer,
http::basic_fields<Allocator> const& fields)
{
- offer.accept = false;
- offer.server_max_window_bits= 0;
- offer.client_max_window_bits = 0;
- offer.server_no_context_takeover = false;
- offer.client_no_context_takeover = false;
-
http::ext_list list{
fields["Sec-WebSocket-Extensions"]};
- for(auto const& ext : list)
- {
- if(iequals(ext.first, "permessage-deflate"))
- {
- for(auto const& param : ext.second)
- {
- if(iequals(param.first,
- "server_max_window_bits"))
- {
- if(offer.server_max_window_bits != 0)
- {
- // The negotiation offer contains multiple
- // extension parameters with the same name.
- //
- return; // MUST decline
- }
- if(param.second.empty())
- {
- // The negotiation offer extension
- // parameter is missing the value.
- //
- return; // MUST decline
- }
- offer.server_max_window_bits =
- parse_bits(param.second);
- if( offer.server_max_window_bits < 8 ||
- offer.server_max_window_bits > 15)
- {
- // The negotiation offer contains an
- // extension parameter with an invalid value.
- //
- return; // MUST decline
- }
- }
- else if(iequals(param.first,
- "client_max_window_bits"))
- {
- if(offer.client_max_window_bits != 0)
- {
- // The negotiation offer contains multiple
- // extension parameters with the same name.
- //
- return; // MUST decline
- }
- if(! param.second.empty())
- {
- offer.client_max_window_bits =
- parse_bits(param.second);
- if( offer.client_max_window_bits < 8 ||
- offer.client_max_window_bits > 15)
- {
- // The negotiation offer contains an
- // extension parameter with an invalid value.
- //
- return; // MUST decline
- }
- }
- else
- {
- offer.client_max_window_bits = -1;
- }
- }
- else if(iequals(param.first,
- "server_no_context_takeover"))
- {
- if(offer.server_no_context_takeover)
- {
- // The negotiation offer contains multiple
- // extension parameters with the same name.
- //
- return; // MUST decline
- }
- if(! param.second.empty())
- {
- // The negotiation offer contains an
- // extension parameter with an invalid value.
- //
- return; // MUST decline
- }
- offer.server_no_context_takeover = true;
- }
- else if(iequals(param.first,
- "client_no_context_takeover"))
- {
- if(offer.client_no_context_takeover)
- {
- // The negotiation offer contains multiple
- // extension parameters with the same name.
- //
- return; // MUST decline
- }
- if(! param.second.empty())
- {
- // The negotiation offer contains an
- // extension parameter with an invalid value.
- //
- return; // MUST decline
- }
- offer.client_no_context_takeover = true;
- }
- else
- {
- // The negotiation offer contains an extension
- // parameter not defined for use in an offer.
- //
- return; // MUST decline
- }
- }
- offer.accept = true;
- return;
- }
- }
+ detail::pmd_read_impl(offer, list);
}
// Set permessage-deflate fields for a client offer
@@ -207,42 +81,7 @@ void
pmd_write(http::basic_fields<Allocator>& fields,
pmd_offer const& offer)
{
- static_string<512> s;
- s = "permessage-deflate";
- if(offer.server_max_window_bits != 0)
- {
- if(offer.server_max_window_bits != -1)
- {
- s += "; server_max_window_bits=";
- s += to_static_string(
- offer.server_max_window_bits);
- }
- else
- {
- s += "; server_max_window_bits";
- }
- }
- if(offer.client_max_window_bits != 0)
- {
- if(offer.client_max_window_bits != -1)
- {
- s += "; client_max_window_bits=";
- s += to_static_string(
- offer.client_max_window_bits);
- }
- else
- {
- s += "; client_max_window_bits";
- }
- }
- if(offer.server_no_context_takeover)
- {
- s += "; server_no_context_takeover";
- }
- if(offer.client_no_context_takeover)
- {
- s += "; client_no_context_takeover";
- }
+ auto s = detail::pmd_write_impl(offer);
fields.set(http::field::sec_websocket_extensions, s);
}
@@ -263,102 +102,24 @@ pmd_negotiate(
}
config.accept = true;
- static_string<512> s = "permessage-deflate";
-
- config.server_no_context_takeover =
- offer.server_no_context_takeover ||
- o.server_no_context_takeover;
- if(config.server_no_context_takeover)
- s += "; server_no_context_takeover";
-
- config.client_no_context_takeover =
- o.client_no_context_takeover ||
- offer.client_no_context_takeover;
- if(config.client_no_context_takeover)
- s += "; client_no_context_takeover";
-
- if(offer.server_max_window_bits != 0)
- config.server_max_window_bits = (std::min)(
- offer.server_max_window_bits,
- o.server_max_window_bits);
- else
- config.server_max_window_bits =
- o.server_max_window_bits;
- if(config.server_max_window_bits < 15)
- {
- // ZLib's deflateInit silently treats 8 as
- // 9 due to a bug, so prevent 8 from being used.
- //
- if(config.server_max_window_bits < 9)
- config.server_max_window_bits = 9;
-
- s += "; server_max_window_bits=";
- s += to_static_string(
- config.server_max_window_bits);
- }
-
- switch(offer.client_max_window_bits)
- {
- case -1:
- // extension parameter is present with no value
- config.client_max_window_bits =
- o.client_max_window_bits;
- if(config.client_max_window_bits < 15)
- {
- s += "; client_max_window_bits=";
- s += to_static_string(
- config.client_max_window_bits);
- }
- break;
-
- case 0:
- /* extension parameter is absent.
-
- If a received extension negotiation offer doesn't have the
- "client_max_window_bits" extension parameter, the corresponding
- extension negotiation response to the offer MUST NOT include the
- "client_max_window_bits" extension parameter.
- */
- if(o.client_max_window_bits == 15)
- config.client_max_window_bits = 15;
- else
- config.accept = false;
- break;
-
- default:
- // extension parameter has value in [8..15]
- config.client_max_window_bits = (std::min)(
- o.client_max_window_bits,
- offer.client_max_window_bits);
- s += "; client_max_window_bits=";
- s += to_static_string(
- config.client_max_window_bits);
- break;
- }
+ auto s = detail::pmd_negotiate_impl(config, offer, o);
if(config.accept)
fields.set(http::field::sec_websocket_extensions, s);
}
// Normalize the server's response
//
-inline
+BOOST_BEAST_DECL
void
-pmd_normalize(pmd_offer& offer)
-{
- if(offer.accept)
- {
- if( offer.server_max_window_bits == 0)
- offer.server_max_window_bits = 15;
-
- if( offer.client_max_window_bits == 0 ||
- offer.client_max_window_bits == -1)
- offer.client_max_window_bits = 15;
- }
-}
+pmd_normalize(pmd_offer& offer);
} // detail
} // websocket
} // beast
} // boost
+#if BOOST_BEAST_HEADER_ONLY
+#include <boost/beast/websocket/detail/pmd_extension.ipp>
+#endif
+
#endif
diff --git a/boost/beast/websocket/detail/pmd_extension.ipp b/boost/beast/websocket/detail/pmd_extension.ipp
new file mode 100644
index 0000000000..790998e8ff
--- /dev/null
+++ b/boost/beast/websocket/detail/pmd_extension.ipp
@@ -0,0 +1,310 @@
+//
+// Copyright (c) 2016-2019 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_PMD_EXTENSION_IPP
+#define BOOST_BEAST_WEBSOCKET_DETAIL_PMD_EXTENSION_IPP
+
+#include <boost/beast/websocket/detail/pmd_extension.hpp>
+
+namespace boost {
+namespace beast {
+namespace websocket {
+namespace detail {
+
+int
+parse_bits(string_view s)
+{
+ if(s.size() == 0)
+ return -1;
+ if(s.size() > 2)
+ return -1;
+ if(s[0] < '1' || s[0] > '9')
+ return -1;
+ unsigned i = 0;
+ for(auto c : s)
+ {
+ if(c < '0' || c > '9')
+ return -1;
+ auto const i0 = i;
+ i = 10 * i + (c - '0');
+ if(i < i0)
+ return -1;
+ }
+ return static_cast<int>(i);
+}
+
+// Parse permessage-deflate request fields
+//
+void
+pmd_read_impl(pmd_offer& offer, http::ext_list const& list)
+{
+ offer.accept = false;
+ offer.server_max_window_bits= 0;
+ offer.client_max_window_bits = 0;
+ offer.server_no_context_takeover = false;
+ offer.client_no_context_takeover = false;
+
+ for(auto const& ext : list)
+ {
+ if(iequals(ext.first, "permessage-deflate"))
+ {
+ for(auto const& param : ext.second)
+ {
+ if(iequals(param.first,
+ "server_max_window_bits"))
+ {
+ if(offer.server_max_window_bits != 0)
+ {
+ // The negotiation offer contains multiple
+ // extension parameters with the same name.
+ //
+ return; // MUST decline
+ }
+ if(param.second.empty())
+ {
+ // The negotiation offer extension
+ // parameter is missing the value.
+ //
+ return; // MUST decline
+ }
+ offer.server_max_window_bits =
+ parse_bits(param.second);
+ if( offer.server_max_window_bits < 8 ||
+ offer.server_max_window_bits > 15)
+ {
+ // The negotiation offer contains an
+ // extension parameter with an invalid value.
+ //
+ return; // MUST decline
+ }
+ }
+ else if(iequals(param.first,
+ "client_max_window_bits"))
+ {
+ if(offer.client_max_window_bits != 0)
+ {
+ // The negotiation offer contains multiple
+ // extension parameters with the same name.
+ //
+ return; // MUST decline
+ }
+ if(! param.second.empty())
+ {
+ offer.client_max_window_bits =
+ parse_bits(param.second);
+ if( offer.client_max_window_bits < 8 ||
+ offer.client_max_window_bits > 15)
+ {
+ // The negotiation offer contains an
+ // extension parameter with an invalid value.
+ //
+ return; // MUST decline
+ }
+ }
+ else
+ {
+ offer.client_max_window_bits = -1;
+ }
+ }
+ else if(iequals(param.first,
+ "server_no_context_takeover"))
+ {
+ if(offer.server_no_context_takeover)
+ {
+ // The negotiation offer contains multiple
+ // extension parameters with the same name.
+ //
+ return; // MUST decline
+ }
+ if(! param.second.empty())
+ {
+ // The negotiation offer contains an
+ // extension parameter with an invalid value.
+ //
+ return; // MUST decline
+ }
+ offer.server_no_context_takeover = true;
+ }
+ else if(iequals(param.first,
+ "client_no_context_takeover"))
+ {
+ if(offer.client_no_context_takeover)
+ {
+ // The negotiation offer contains multiple
+ // extension parameters with the same name.
+ //
+ return; // MUST decline
+ }
+ if(! param.second.empty())
+ {
+ // The negotiation offer contains an
+ // extension parameter with an invalid value.
+ //
+ return; // MUST decline
+ }
+ offer.client_no_context_takeover = true;
+ }
+ else
+ {
+ // The negotiation offer contains an extension
+ // parameter not defined for use in an offer.
+ //
+ return; // MUST decline
+ }
+ }
+ offer.accept = true;
+ return;
+ }
+ }
+}
+
+static_string<512>
+pmd_write_impl(pmd_offer const& offer)
+{
+ static_string<512> s = "permessage-deflate";
+ if(offer.server_max_window_bits != 0)
+ {
+ if(offer.server_max_window_bits != -1)
+ {
+ s += "; server_max_window_bits=";
+ s += to_static_string(
+ offer.server_max_window_bits);
+ }
+ else
+ {
+ s += "; server_max_window_bits";
+ }
+ }
+ if(offer.client_max_window_bits != 0)
+ {
+ if(offer.client_max_window_bits != -1)
+ {
+ s += "; client_max_window_bits=";
+ s += to_static_string(
+ offer.client_max_window_bits);
+ }
+ else
+ {
+ s += "; client_max_window_bits";
+ }
+ }
+ if(offer.server_no_context_takeover)
+ {
+ s += "; server_no_context_takeover";
+ }
+ if(offer.client_no_context_takeover)
+ {
+ s += "; client_no_context_takeover";
+ }
+
+ return s;
+}
+
+static_string<512>
+pmd_negotiate_impl(
+ pmd_offer& config,
+ pmd_offer const& offer,
+ permessage_deflate const& o)
+{
+ static_string<512> s = "permessage-deflate";
+
+ config.server_no_context_takeover =
+ offer.server_no_context_takeover ||
+ o.server_no_context_takeover;
+ if(config.server_no_context_takeover)
+ s += "; server_no_context_takeover";
+
+ config.client_no_context_takeover =
+ o.client_no_context_takeover ||
+ offer.client_no_context_takeover;
+ if(config.client_no_context_takeover)
+ s += "; client_no_context_takeover";
+
+ if(offer.server_max_window_bits != 0)
+ config.server_max_window_bits = (std::min)(
+ offer.server_max_window_bits,
+ o.server_max_window_bits);
+ else
+ config.server_max_window_bits =
+ o.server_max_window_bits;
+ if(config.server_max_window_bits < 15)
+ {
+ // ZLib's deflateInit silently treats 8 as
+ // 9 due to a bug, so prevent 8 from being used.
+ //
+ if(config.server_max_window_bits < 9)
+ config.server_max_window_bits = 9;
+
+ s += "; server_max_window_bits=";
+ s += to_static_string(
+ config.server_max_window_bits);
+ }
+
+ switch(offer.client_max_window_bits)
+ {
+ case -1:
+ // extension parameter is present with no value
+ config.client_max_window_bits =
+ o.client_max_window_bits;
+ if(config.client_max_window_bits < 15)
+ {
+ s += "; client_max_window_bits=";
+ s += to_static_string(
+ config.client_max_window_bits);
+ }
+ break;
+
+ case 0:
+ /* extension parameter is absent.
+
+ If a received extension negotiation offer doesn't have the
+ "client_max_window_bits" extension parameter, the corresponding
+ extension negotiation response to the offer MUST NOT include the
+ "client_max_window_bits" extension parameter.
+ */
+ if(o.client_max_window_bits == 15)
+ config.client_max_window_bits = 15;
+ else
+ config.accept = false;
+ break;
+
+ default:
+ // extension parameter has value in [8..15]
+ config.client_max_window_bits = (std::min)(
+ o.client_max_window_bits,
+ offer.client_max_window_bits);
+ s += "; client_max_window_bits=";
+ s += to_static_string(
+ config.client_max_window_bits);
+ break;
+ }
+
+ return s;
+}
+
+void
+pmd_normalize(pmd_offer& offer)
+{
+ if(offer.accept)
+ {
+ if( offer.server_max_window_bits == 0)
+ offer.server_max_window_bits = 15;
+
+ if( offer.client_max_window_bits == 0 ||
+ offer.client_max_window_bits == -1)
+ offer.client_max_window_bits = 15;
+ }
+}
+
+} // detail
+} // websocket
+} // beast
+} // boost
+
+#endif // BOOST_BEAST_WEBSOCKET_DETAIL_PMD_EXTENSION_IPP
diff --git a/boost/beast/websocket/detail/prng.hpp b/boost/beast/websocket/detail/prng.hpp
new file mode 100644
index 0000000000..35409b5837
--- /dev/null
+++ b/boost/beast/websocket/detail/prng.hpp
@@ -0,0 +1,123 @@
+//
+// Copyright (c) 2016-2019 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_PRNG_HPP
+#define BOOST_BEAST_WEBSOCKET_DETAIL_PRNG_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <cstdint>
+#include <limits>
+#include <random>
+
+namespace boost {
+namespace beast {
+namespace websocket {
+namespace detail {
+
+// Type-erased UniformRandomBitGenerator
+// with 32-bit unsigned integer value_type
+//
+class prng
+{
+protected:
+ virtual ~prng() = default;
+ virtual prng& acquire() noexcept = 0;
+ virtual void release() noexcept = 0;
+ virtual std::uint32_t operator()() noexcept = 0;
+
+public:
+ using value_type = std::uint32_t;
+
+ class ref
+ {
+ prng& p_;
+
+ public:
+ ref& operator=(ref const&) = delete;
+
+ ~ref()
+ {
+ p_.release();
+ }
+
+ explicit
+ ref(prng& p) noexcept
+ : p_(p.acquire())
+ {
+ }
+
+ ref(ref const& other) noexcept
+ : p_(other.p_.acquire())
+ {
+ }
+
+ value_type
+ operator()() noexcept
+ {
+ return p_();
+ }
+
+ static
+ value_type constexpr
+ min() noexcept
+ {
+ return (std::numeric_limits<
+ value_type>::min)();
+ }
+
+ static
+ value_type constexpr
+ max() noexcept
+ {
+ return (std::numeric_limits<
+ value_type>::max)();
+ }
+ };
+};
+
+//------------------------------------------------------------------------------
+
+// Manually seed the prngs, must be called
+// before acquiring a prng for the first time.
+//
+BOOST_BEAST_DECL
+std::uint32_t const*
+prng_seed(std::seed_seq* ss = nullptr);
+
+// Acquire a PRNG using the no-TLS implementation
+//
+BOOST_BEAST_DECL
+prng::ref
+make_prng_no_tls(bool secure);
+
+// Acquire a PRNG using the TLS implementation
+//
+#ifndef BOOST_NO_CXX11_THREAD_LOCAL
+BOOST_BEAST_DECL
+prng::ref
+make_prng_tls(bool secure);
+#endif
+
+// Acquire a PRNG using the TLS implementation if it
+// is available, otherwise using the no-TLS implementation.
+//
+BOOST_BEAST_DECL
+prng::ref
+make_prng(bool secure);
+
+} // detail
+} // websocket
+} // beast
+} // boost
+
+#ifdef BOOST_BEAST_HEADER_ONLY
+#include <boost/beast/websocket/detail/prng.ipp>
+#endif
+
+#endif
diff --git a/boost/beast/websocket/detail/prng.ipp b/boost/beast/websocket/detail/prng.ipp
new file mode 100644
index 0000000000..2807ca96e9
--- /dev/null
+++ b/boost/beast/websocket/detail/prng.ipp
@@ -0,0 +1,327 @@
+//
+// Copyright (c) 2016-2019 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_PRNG_IPP
+#define BOOST_BEAST_WEBSOCKET_DETAIL_PRNG_IPP
+
+#include <boost/beast/websocket/detail/prng.hpp>
+#include <boost/beast/core/detail/chacha.hpp>
+#include <boost/beast/core/detail/pcg.hpp>
+#include <boost/align/aligned_alloc.hpp>
+#include <boost/throw_exception.hpp>
+#include <atomic>
+#include <cstdlib>
+#include <mutex>
+#include <new>
+#include <random>
+#include <stdexcept>
+
+namespace boost {
+namespace beast {
+namespace websocket {
+namespace detail {
+
+//------------------------------------------------------------------------------
+
+std::uint32_t const*
+prng_seed(std::seed_seq* ss)
+{
+ struct data
+ {
+ std::uint32_t v[8];
+
+ explicit
+ data(std::seed_seq* pss)
+ {
+ if(! pss)
+ {
+ std::random_device g;
+ std::seed_seq ss{
+ g(), g(), g(), g(),
+ g(), g(), g(), g()};
+ ss.generate(v, v+8);
+ }
+ else
+ {
+ pss->generate(v, v+8);
+ }
+ }
+ };
+ static data const d(ss);
+ return d.v;
+}
+
+//------------------------------------------------------------------------------
+
+template<class T>
+class prng_pool
+{
+ std::mutex m_;
+ T* head_ = nullptr;
+
+public:
+ static
+ prng_pool&
+ instance()
+ {
+ static prng_pool p;
+ return p;
+ }
+
+ ~prng_pool()
+ {
+ for(auto p = head_; p;)
+ {
+ auto next = p->next;
+ p->~T();
+ boost::alignment::aligned_free(p);
+ p = next;
+ }
+ }
+
+ prng::ref
+ acquire()
+ {
+ {
+ std::lock_guard<
+ std::mutex> g(m_);
+ if(head_)
+ {
+ auto p = head_;
+ head_ = head_->next;
+ return prng::ref(*p);
+ }
+ }
+ auto p = boost::alignment::aligned_alloc(
+ 16, sizeof(T));
+ if(! p)
+ BOOST_THROW_EXCEPTION(std::bad_alloc{});
+ return prng::ref(*(::new(p) T()));
+ }
+
+ void
+ release(T& t)
+ {
+ std::lock_guard<
+ std::mutex> g(m_);
+ t.next = head_;
+ head_ = &t;
+ }
+};
+
+prng::ref
+make_prng_no_tls(bool secure)
+{
+ class fast_prng final : public prng
+ {
+ int refs_ = 0;
+ beast::detail::pcg r_;
+
+ public:
+ fast_prng* next = nullptr;
+
+ fast_prng()
+ : r_(
+ []{
+ auto const pv = prng_seed();
+ return
+ ((static_cast<std::uint64_t>(pv[0])<<32)+pv[1]) ^
+ ((static_cast<std::uint64_t>(pv[2])<<32)+pv[3]) ^
+ ((static_cast<std::uint64_t>(pv[4])<<32)+pv[5]) ^
+ ((static_cast<std::uint64_t>(pv[6])<<32)+pv[7]);
+ }(),
+ []{
+ static std::atomic<
+ std::uint32_t> nonce{0};
+ return ++nonce;
+ }())
+ {
+ }
+
+ protected:
+ prng&
+ acquire() noexcept override
+ {
+ ++refs_;
+ return *this;
+ }
+
+ void
+ release() noexcept override
+ {
+ if(--refs_ == 0)
+ prng_pool<fast_prng>::instance().release(*this);
+ }
+
+ value_type
+ operator()() noexcept override
+ {
+ return r_();
+ }
+ };
+
+ class secure_prng final : public prng
+ {
+ int refs_ = 0;
+ beast::detail::chacha<20> r_;
+
+ public:
+ secure_prng* next = nullptr;
+
+ secure_prng()
+ : r_(prng_seed(), []
+ {
+ static std::atomic<
+ std::uint64_t> nonce{0};
+ return ++nonce;
+ }())
+ {
+ }
+
+ protected:
+ prng&
+ acquire() noexcept override
+ {
+ ++refs_;
+ return *this;
+ }
+
+ void
+ release() noexcept override
+ {
+ if(--refs_ == 0)
+ prng_pool<secure_prng>::instance().release(*this);
+ }
+
+ value_type
+ operator()() noexcept override
+ {
+ return r_();
+ }
+ };
+
+ if(secure)
+ return prng_pool<secure_prng>::instance().acquire();
+
+ return prng_pool<fast_prng>::instance().acquire();
+}
+
+//------------------------------------------------------------------------------
+
+#ifndef BOOST_NO_CXX11_THREAD_LOCAL
+
+prng::ref
+make_prng_tls(bool secure)
+{
+ class fast_prng final : public prng
+ {
+ beast::detail::pcg r_;
+
+ public:
+ fast_prng()
+ : r_(
+ []{
+ auto const pv = prng_seed();
+ return
+ ((static_cast<std::uint64_t>(pv[0])<<32)+pv[1]) ^
+ ((static_cast<std::uint64_t>(pv[2])<<32)+pv[3]) ^
+ ((static_cast<std::uint64_t>(pv[4])<<32)+pv[5]) ^
+ ((static_cast<std::uint64_t>(pv[6])<<32)+pv[7]);
+ }(),
+ []{
+ static std::atomic<
+ std::uint32_t> nonce{0};
+ return ++nonce;
+ }())
+ {
+ }
+
+ protected:
+ prng&
+ acquire() noexcept override
+ {
+ return *this;
+ }
+
+ void
+ release() noexcept override
+ {
+ }
+
+ value_type
+ operator()() noexcept override
+ {
+ return r_();
+ }
+ };
+
+ class secure_prng final : public prng
+ {
+ beast::detail::chacha<20> r_;
+
+ public:
+ secure_prng()
+ : r_(prng_seed(), []
+ {
+ static std::atomic<
+ std::uint64_t> nonce{0};
+ return ++nonce;
+ }())
+ {
+ }
+
+ protected:
+ prng&
+ acquire() noexcept override
+ {
+ return *this;
+ }
+
+ void
+ release() noexcept override
+ {
+ }
+
+ value_type
+ operator()() noexcept override
+ {
+ return r_();
+ }
+ };
+
+ if(secure)
+ {
+ thread_local secure_prng sp;
+ return prng::ref(sp);
+ }
+
+ thread_local fast_prng fp;
+ return prng::ref(fp);
+}
+
+#endif
+
+//------------------------------------------------------------------------------
+
+prng::ref
+make_prng(bool secure)
+{
+#ifdef BOOST_NO_CXX11_THREAD_LOCAL
+ return make_prng_no_tls(secure);
+#else
+ return make_prng_tls(secure);
+#endif
+}
+
+} // detail
+} // websocket
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/websocket/detail/service.hpp b/boost/beast/websocket/detail/service.hpp
new file mode 100644
index 0000000000..c9a493b4aa
--- /dev/null
+++ b/boost/beast/websocket/detail/service.hpp
@@ -0,0 +1,78 @@
+//
+// Copyright (c) 2016-2019 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_SERVICE_HPP
+#define BOOST_BEAST_WEBSOCKET_DETAIL_SERVICE_HPP
+
+#include <boost/beast/core/detail/service_base.hpp>
+#include <boost/asio/execution_context.hpp>
+#include <boost/enable_shared_from_this.hpp>
+#include <mutex>
+#include <vector>
+
+namespace boost {
+namespace beast {
+namespace websocket {
+namespace detail {
+
+class service
+ : public beast::detail::service_base<service>
+{
+public:
+ class impl_type
+ : public boost::enable_shared_from_this<impl_type>
+ {
+ service& svc_;
+ std::size_t index_;
+
+ friend class service;
+
+ public:
+ virtual ~impl_type() = default;
+
+ BOOST_BEAST_DECL
+ explicit
+ impl_type(net::execution_context& ctx);
+
+ BOOST_BEAST_DECL
+ void
+ remove();
+
+ virtual
+ void
+ shutdown() = 0;
+ };
+
+private:
+ std::mutex m_;
+ std::vector<impl_type*> v_;
+
+ BOOST_BEAST_DECL
+ void
+ shutdown() override;
+
+public:
+ BOOST_BEAST_DECL
+ explicit
+ service(net::execution_context& ctx)
+ : beast::detail::service_base<service>(ctx)
+ {
+ }
+};
+
+} // detail
+} // websocket
+} // beast
+} // boost
+
+#if BOOST_BEAST_HEADER_ONLY
+#include <boost/beast/websocket/detail/service.ipp>
+#endif
+
+#endif
diff --git a/boost/beast/websocket/detail/service.ipp b/boost/beast/websocket/detail/service.ipp
new file mode 100644
index 0000000000..86e9a97298
--- /dev/null
+++ b/boost/beast/websocket/detail/service.ipp
@@ -0,0 +1,65 @@
+//
+// Copyright (c) 2016-2019 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_SERVICE_IPP
+#define BOOST_BEAST_WEBSOCKET_DETAIL_SERVICE_IPP
+
+#include <boost/beast/websocket/detail/service.hpp>
+
+namespace boost {
+namespace beast {
+namespace websocket {
+namespace detail {
+
+service::
+impl_type::
+impl_type(net::execution_context& ctx)
+ : svc_(net::use_service<service>(ctx))
+{
+ std::lock_guard<std::mutex> g(svc_.m_);
+ index_ = svc_.v_.size();
+ svc_.v_.push_back(this);
+}
+
+void
+service::
+impl_type::
+remove()
+{
+ std::lock_guard<std::mutex> g(svc_.m_);
+ auto& other = *svc_.v_.back();
+ other.index_ = index_;
+ svc_.v_[index_] = &other;
+ svc_.v_.pop_back();
+}
+
+//---
+
+void
+service::
+shutdown()
+{
+ std::vector<boost::weak_ptr<impl_type>> v;
+ {
+ std::lock_guard<std::mutex> g(m_);
+ v.reserve(v_.size());
+ for(auto p : v_)
+ v.emplace_back(p->weak_from_this());
+ }
+ for(auto wp : v)
+ if(auto sp = wp.lock())
+ sp->shutdown();
+}
+
+} // detail
+} // websocket
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/websocket/detail/soft_mutex.hpp b/boost/beast/websocket/detail/soft_mutex.hpp
new file mode 100644
index 0000000000..04b6f18873
--- /dev/null
+++ b/boost/beast/websocket/detail/soft_mutex.hpp
@@ -0,0 +1,112 @@
+//
+// Copyright (c) 2016-2019 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_SOFT_MUTEX_HPP
+#define BOOST_BEAST_WEBSOCKET_DETAIL_SOFT_MUTEX_HPP
+
+#include <boost/assert.hpp>
+
+namespace boost {
+namespace beast {
+namespace websocket {
+namespace detail {
+
+// used to order reads, writes in websocket streams
+
+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_(boost::exchange(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 noexcept
+ {
+ return id_ != 0;
+ }
+
+ template<class T>
+ bool
+ is_locked(T const*) const noexcept
+ {
+ 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*) noexcept
+ {
+ if(id_ != T::id)
+ return false;
+ id_ = 0;
+ return true;
+ }
+};
+
+} // detail
+} // websocket
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/websocket/detail/stream_base.hpp b/boost/beast/websocket/detail/stream_base.hpp
deleted file mode 100644
index 4da5747e0b..0000000000
--- a/boost/beast/websocket/detail/stream_base.hpp
+++ /dev/null
@@ -1,413 +0,0 @@
-//
-// 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/beast/core/detail/chacha.hpp>
-#include <boost/beast/core/detail/integer_sequence.hpp>
-#include <boost/align/aligned_alloc.hpp>
-#include <boost/asio/buffer.hpp>
-#include <boost/core/exchange.hpp>
-#include <atomic>
-#include <cstdint>
-#include <memory>
-#include <new>
-#include <random>
-
-// Turn this on to avoid using thread_local
-//#define BOOST_BEAST_NO_THREAD_LOCAL 1
-
-#ifdef BOOST_BEAST_NO_THREAD_LOCAL
-#include <atomic>
-#include <mutex>
-#endif
-
-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_(boost::exchange(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;
- }
-};
-
-//------------------------------------------------------------------------------
-
-struct stream_prng
-{
- bool secure_prng_ = true;
-
- struct prng_type
- {
- std::minstd_rand fast;
- beast::detail::chacha<20> secure;
-
-#if BOOST_BEAST_NO_THREAD_LOCAL
- prng_type* next = nullptr;
-#endif
-
- prng_type(std::uint32_t const* v, std::uint64_t stream)
- : fast(static_cast<typename decltype(fast)::result_type>(
- v[0] + v[1] + v[2] + v[3] + v[4] + v[5] + v[6] + v[7] + stream))
- , secure(v, stream)
- {
- }
- };
-
- class prng_ref
- {
- prng_type* p_;
-
- public:
- prng_ref& operator=(prng_ref&&) = delete;
-
- explicit
- prng_ref(prng_type& p)
- : p_(&p)
- {
- }
-
- prng_ref(prng_ref&& other)
- : p_(boost::exchange(other.p_, nullptr))
- {
- }
-
-#ifdef BOOST_BEAST_NO_THREAD_LOCAL
- ~prng_ref()
- {
- if(p_)
- pool::impl().release(*p_);
- }
-#endif
-
- prng_type*
- operator->() const
- {
- return p_;
- }
- };
-
-#ifndef BOOST_BEAST_NO_THREAD_LOCAL
- static
- prng_ref
- prng()
- {
- static std::atomic<std::uint64_t> stream{0};
- thread_local prng_type p{seed(), stream++};
- return prng_ref(p);
- }
-
-#else
- static
- prng_ref
- prng()
- {
- return prng_ref(pool::impl().acquire());
- }
-
-#endif
-
- static
- std::uint32_t const*
- seed(std::seed_seq* ss = nullptr)
- {
- static seed_data d(ss);
- return d.v;
- }
-
- std::uint32_t
- create_mask()
- {
- auto p = prng();
- if(secure_prng_)
- for(;;)
- if(auto key = p->secure())
- return key;
- for(;;)
- if(auto key = p->fast())
- return key;
- }
-
-private:
- struct seed_data
- {
- std::uint32_t v[8];
-
- explicit
- seed_data(std::seed_seq* pss)
- {
- if(! pss)
- {
- std::random_device g;
- std::seed_seq ss{
- g(), g(), g(), g(), g(), g(), g(), g()};
- ss.generate(v, v+8);
- }
- else
- {
- pss->generate(v, v+8);
- }
- }
- };
-
-#ifdef BOOST_BEAST_NO_THREAD_LOCAL
- class pool
- {
- prng_type* head_ = nullptr;
- std::atomic<std::uint64_t> n_{0};
- std::mutex m_;
-
- public:
- ~pool()
- {
- for(auto p = head_; p;)
- {
- auto next = p->next;
- p->~prng_type();
- boost::alignment::aligned_free(p);
- p = next;
- }
- }
-
- prng_type&
- acquire()
- {
- for(;;)
- {
- std::lock_guard<std::mutex> lock(m_);
- if(! head_)
- break;
- auto p = head_;
- head_ = head_->next;
- return *p;
- }
- auto p = boost::alignment::aligned_alloc(
- 16, sizeof(prng_type));
- if(! p)
- BOOST_THROW_EXCEPTION(std::bad_alloc{});
- return *(new(p) prng_type(seed(), n_++));
- }
-
- void
- release(prng_type& p)
- {
- std::lock_guard<std::mutex> lock(m_);
- p.next = head_;
- head_ = &p;
- }
-
- static
- pool&
- impl()
- {
- static pool instance;
- return instance;
- }
- };
-#endif
-};
-
-//------------------------------------------------------------------------------
-
-template<bool deflateSupported>
-struct stream_base : stream_prng
-{
- // 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> : stream_prng
-{
- // 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 6913174fd0..507360c8c1 100644
--- a/boost/beast/websocket/detail/type_traits.hpp
+++ b/boost/beast/websocket/detail/type_traits.hpp
@@ -1,5 +1,5 @@
//
-// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+// Copyright (c) 2016-2019 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)
@@ -11,7 +11,7 @@
#define BOOST_BEAST_WEBSOCKET_DETAIL_TYPE_TRAITS_HPP
#include <boost/beast/websocket/rfc6455.hpp>
-#include <boost/beast/core/detail/type_traits.hpp>
+#include <boost/beast/core/detail/is_invocable.hpp>
namespace boost {
namespace beast {
diff --git a/boost/beast/websocket/detail/utf8_checker.hpp b/boost/beast/websocket/detail/utf8_checker.hpp
index ea6e61a982..c997cfb6fa 100644
--- a/boost/beast/websocket/detail/utf8_checker.hpp
+++ b/boost/beast/websocket/detail/utf8_checker.hpp
@@ -1,5 +1,5 @@
//
-// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+// Copyright (c) 2016-2019 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)
@@ -10,10 +10,9 @@
#ifndef BOOST_BEAST_WEBSOCKET_DETAIL_UTF8_CHECKER_HPP
#define BOOST_BEAST_WEBSOCKET_DETAIL_UTF8_CHECKER_HPP
-#include <boost/beast/core/type_traits.hpp>
+#include <boost/beast/core/buffers_range.hpp>
#include <boost/asio/buffer.hpp>
-#include <boost/assert.hpp>
-#include <algorithm>
+
#include <cstdint>
namespace boost {
@@ -27,8 +26,7 @@ namespace detail {
valid. The write function may be called incrementally with segmented UTF8
sequences. The finish function determines if all processed text is valid.
*/
-template<class = void>
-class utf8_checker_t
+class utf8_checker
{
std::size_t need_ = 0; // chars we need to finish the code point
std::uint8_t* p_ = cp_; // current position in temp buffer
@@ -37,11 +35,13 @@ class utf8_checker_t
public:
/** Prepare to process text as valid utf8
*/
+ BOOST_BEAST_DECL
void
reset();
/** Check that all processed text is valid utf8
*/
+ BOOST_BEAST_DECL
bool
finish();
@@ -49,6 +49,7 @@ public:
@return `true` if the text is valid utf8 or false otherwise.
*/
+ BOOST_BEAST_DECL
bool
write(std::uint8_t const* in, std::size_t size);
@@ -61,34 +62,16 @@ public:
write(ConstBufferSequence const& bs);
};
-template<class _>
-void
-utf8_checker_t<_>::
-reset()
-{
- need_ = 0;
- p_ = cp_;
-}
-
-template<class _>
-bool
-utf8_checker_t<_>::
-finish()
-{
- auto const success = need_ == 0;
- reset();
- return success;
-}
-template<class _>
template<class ConstBufferSequence>
bool
-utf8_checker_t<_>::
-write(ConstBufferSequence const& bs)
+utf8_checker::
+write(ConstBufferSequence const& buffers)
{
- static_assert(boost::asio::is_const_buffer_sequence<ConstBufferSequence>::value,
- "ConstBufferSequence requirements not met");
- for(auto b : beast::detail::buffers_range(bs))
+ static_assert(
+ net::is_const_buffer_sequence<ConstBufferSequence>::value,
+ "ConstBufferSequence type requirements not met");
+ for(auto b : beast::buffers_range_ref(buffers))
if(! write(static_cast<
std::uint8_t const*>(b.data()),
b.size()))
@@ -96,300 +79,18 @@ write(ConstBufferSequence const& bs)
return true;
}
-template<class _>
-bool
-utf8_checker_t<_>::
-write(std::uint8_t const* in, std::size_t size)
-{
- auto const valid =
- [](std::uint8_t const*& p)
- {
- if(p[0] < 128)
- {
- ++p;
- return true;
- }
- if((p[0] & 0xe0) == 0xc0)
- {
- if( (p[1] & 0xc0) != 0x80 ||
- (p[0] & 0x1e) == 0) // overlong
- return false;
- p += 2;
- return true;
- }
- if((p[0] & 0xf0) == 0xe0)
- {
- if( (p[1] & 0xc0) != 0x80
- || (p[2] & 0xc0) != 0x80
- || (p[0] == 0xe0 && (p[1] & 0x20) == 0) // overlong
- || (p[0] == 0xed && (p[1] & 0x20) == 0x20) // surrogate
- //|| (p[0] == 0xef && p[1] == 0xbf && (p[2] & 0xfe) == 0xbe) // U+FFFE or U+FFFF
- )
- return false;
- p += 3;
- return true;
- }
- if((p[0] & 0xf8) == 0xf0)
- {
- if( (p[0] & 0x07) >= 0x05 // invalid F5...FF characters
- || (p[1] & 0xc0) != 0x80
- || (p[2] & 0xc0) != 0x80
- || (p[3] & 0xc0) != 0x80
- || (p[0] == 0xf0 && (p[1] & 0x30) == 0) // overlong
- || (p[0] == 0xf4 && p[1] > 0x8f) || p[0] > 0xf4 // > U+10FFFF
- )
- return false;
- p += 4;
- return true;
- }
- return false;
- };
- auto const fail_fast =
- [&]()
- {
- if(cp_[0] < 128)
- {
- return false;
- }
-
- const auto& p = cp_; // alias, only to keep this code similar to valid() above
- const auto known_only = p_ - cp_;
- if (known_only == 1)
- {
- if((p[0] & 0xe0) == 0xc0)
- {
- return ((p[0] & 0x1e) == 0); // overlong
- }
- if((p[0] & 0xf0) == 0xe0)
- {
- return false;
- }
- if((p[0] & 0xf8) == 0xf0)
- {
- return ((p[0] & 0x07) >= 0x05); // invalid F5...FF characters
- }
- }
- else if (known_only == 2)
- {
- if((p[0] & 0xe0) == 0xc0)
- {
- return ((p[1] & 0xc0) != 0x80 ||
- (p[0] & 0x1e) == 0); // overlong
- }
- if((p[0] & 0xf0) == 0xe0)
- {
- return ( (p[1] & 0xc0) != 0x80
- || (p[0] == 0xe0 && (p[1] & 0x20) == 0) // overlong
- || (p[0] == 0xed && (p[1] & 0x20) == 0x20)); // surrogate
- }
- if((p[0] & 0xf8) == 0xf0)
- {
- return ( (p[0] & 0x07) >= 0x05 // invalid F5...FF characters
- || (p[1] & 0xc0) != 0x80
- || (p[0] == 0xf0 && (p[1] & 0x30) == 0) // overlong
- || (p[0] == 0xf4 && p[1] > 0x8f) || p[0] > 0xf4); // > U+10FFFF
- }
- }
- else if (known_only == 3)
- {
- if((p[0] & 0xe0) == 0xc0)
- {
- return ( (p[1] & 0xc0) != 0x80
- || (p[0] & 0x1e) == 0); // overlong
- }
- if((p[0] & 0xf0) == 0xe0)
- {
- return ( (p[1] & 0xc0) != 0x80
- || (p[2] & 0xc0) != 0x80
- || (p[0] == 0xe0 && (p[1] & 0x20) == 0) // overlong
- || (p[0] == 0xed && (p[1] & 0x20) == 0x20)); // surrogate
- //|| (p[0] == 0xef && p[1] == 0xbf && (p[2] & 0xfe) == 0xbe) // U+FFFE or U+FFFF
- }
- if((p[0] & 0xf8) == 0xf0)
- {
- return ( (p[0] & 0x07) >= 0x05 // invalid F5...FF characters
- || (p[1] & 0xc0) != 0x80
- || (p[2] & 0xc0) != 0x80
- || (p[0] == 0xf0 && (p[1] & 0x30) == 0) // overlong
- || (p[0] == 0xf4 && p[1] > 0x8f) || p[0] > 0xf4); // > U+10FFFF
- }
- }
- return true;
- };
- auto const needed =
- [](std::uint8_t const v)
- {
- if(v < 128)
- return 1;
- if(v < 192)
- return 0;
- if(v < 224)
- return 2;
- if(v < 240)
- return 3;
- if(v < 248)
- return 4;
- return 0;
- };
-
- auto const end = in + size;
-
- // Finish up any incomplete code point
- if(need_ > 0)
- {
- // Calculate what we have
- auto n = (std::min)(size, need_);
- size -= n;
- need_ -= n;
-
- // Add characters to the code point
- while(n--)
- *p_++ = *in++;
- BOOST_ASSERT(p_ <= cp_ + 4);
-
- // Still incomplete?
- if(need_ > 0)
- {
- // Incomplete code point
- BOOST_ASSERT(in == end);
-
- // Do partial validation on the incomplete
- // code point, this is called "Fail fast"
- // in Autobahn|Testsuite parlance.
- return ! fail_fast();
- }
-
- // Complete code point, validate it
- std::uint8_t const* p = &cp_[0];
- if(! valid(p))
- return false;
- p_ = cp_;
- }
- if(size <= sizeof(std::size_t))
- goto slow;
-
- // Align `in` to sizeof(std::size_t) boundary
- {
- auto const in0 = in;
- auto last = reinterpret_cast<std::uint8_t const*>(
- ((reinterpret_cast<std::uintptr_t>(in) + sizeof(std::size_t) - 1) /
- sizeof(std::size_t)) * sizeof(std::size_t));
-
- // Check one character at a time for low-ASCII
- while(in < last)
- {
- if(*in & 0x80)
- {
- // Not low-ASCII so switch to slow loop
- size = size - (in - in0);
- goto slow;
- }
- ++in;
- }
- size = size - (in - in0);
- }
-
- // Fast loop: Process 4 or 8 low-ASCII characters at a time
- {
- auto const in0 = in;
- auto last = in + size - 7;
- auto constexpr mask = static_cast<
- std::size_t>(0x8080808080808080 & ~std::size_t{0});
- while(in < last)
- {
-#if 0
- std::size_t temp;
- std::memcpy(&temp, in, sizeof(temp));
- if((temp & mask) != 0)
-#else
- // Technically UB but works on all known platforms
- if((*reinterpret_cast<std::size_t const*>(in) & mask) != 0)
-#endif
- {
- size = size - (in - in0);
- goto slow;
- }
- in += sizeof(std::size_t);
- }
- // There's at least one more full code point left
- last += 4;
- while(in < last)
- if(! valid(in))
- return false;
- goto tail;
- }
-
-slow:
- // Slow loop: Full validation on one code point at a time
- {
- auto last = in + size - 3;
- while(in < last)
- if(! valid(in))
- return false;
- }
-
-tail:
- // Handle the remaining bytes. The last
- // characters could split a code point so
- // we save the partial code point for later.
- //
- // On entry to the loop, `in` points to the
- // beginning of a code point.
- //
- for(;;)
- {
- // Number of chars left
- auto n = end - in;
- if(! n)
- break;
-
- // Chars we need to finish this code point
- auto const need = needed(*in);
- if(need == 0)
- return false;
- if(need <= n)
- {
- // Check a whole code point
- if(! valid(in))
- return false;
- }
- else
- {
- // Calculate how many chars we need
- // to finish this partial code point
- need_ = need - n;
-
- // Save the partial code point
- while(n--)
- *p_++ = *in++;
- BOOST_ASSERT(in == end);
- BOOST_ASSERT(p_ <= cp_ + 4);
-
- // Do partial validation on the incomplete
- // code point, this is called "Fail fast"
- // in Autobahn|Testsuite parlance.
- return ! fail_fast();
- }
- }
- return true;
-}
-
-using utf8_checker = utf8_checker_t<>;
-
-template<class = void>
+BOOST_BEAST_DECL
bool
-check_utf8(char const* p, std::size_t n)
-{
- utf8_checker c;
- if(! c.write(reinterpret_cast<const uint8_t*>(p), n))
- return false;
- return c.finish();
-}
+check_utf8(char const* p, std::size_t n);
} // detail
} // websocket
} // beast
} // boost
+#if BOOST_BEAST_HEADER_ONLY
+#include <boost/beast/websocket/detail/utf8_checker.ipp>
+#endif
+
#endif
diff --git a/boost/beast/websocket/detail/utf8_checker.ipp b/boost/beast/websocket/detail/utf8_checker.ipp
new file mode 100644
index 0000000000..64a293456b
--- /dev/null
+++ b/boost/beast/websocket/detail/utf8_checker.ipp
@@ -0,0 +1,331 @@
+//
+// Copyright (c) 2016-2019 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_UTF8_CHECKER_IPP
+#define BOOST_BEAST_WEBSOCKET_DETAIL_UTF8_CHECKER_IPP
+
+#include <boost/beast/websocket/detail/utf8_checker.hpp>
+
+#include <boost/assert.hpp>
+
+namespace boost {
+namespace beast {
+namespace websocket {
+namespace detail {
+
+void
+utf8_checker::
+reset()
+{
+ need_ = 0;
+ p_ = cp_;
+}
+
+bool
+utf8_checker::
+finish()
+{
+ auto const success = need_ == 0;
+ reset();
+ return success;
+}
+
+bool
+utf8_checker::
+write(std::uint8_t const* in, std::size_t size)
+{
+ auto const valid =
+ [](std::uint8_t const*& p)
+ {
+ if(p[0] < 128)
+ {
+ ++p;
+ return true;
+ }
+ if((p[0] & 0xe0) == 0xc0)
+ {
+ if( (p[1] & 0xc0) != 0x80 ||
+ (p[0] & 0x1e) == 0) // overlong
+ return false;
+ p += 2;
+ return true;
+ }
+ if((p[0] & 0xf0) == 0xe0)
+ {
+ if( (p[1] & 0xc0) != 0x80
+ || (p[2] & 0xc0) != 0x80
+ || (p[0] == 0xe0 && (p[1] & 0x20) == 0) // overlong
+ || (p[0] == 0xed && (p[1] & 0x20) == 0x20) // surrogate
+ //|| (p[0] == 0xef && p[1] == 0xbf && (p[2] & 0xfe) == 0xbe) // U+FFFE or U+FFFF
+ )
+ return false;
+ p += 3;
+ return true;
+ }
+ if((p[0] & 0xf8) == 0xf0)
+ {
+ if( (p[0] & 0x07) >= 0x05 // invalid F5...FF characters
+ || (p[1] & 0xc0) != 0x80
+ || (p[2] & 0xc0) != 0x80
+ || (p[3] & 0xc0) != 0x80
+ || (p[0] == 0xf0 && (p[1] & 0x30) == 0) // overlong
+ || (p[0] == 0xf4 && p[1] > 0x8f) || p[0] > 0xf4 // > U+10FFFF
+ )
+ return false;
+ p += 4;
+ return true;
+ }
+ return false;
+ };
+ auto const fail_fast =
+ [&]()
+ {
+ if(cp_[0] < 128)
+ {
+ return false;
+ }
+
+ const auto& p = cp_; // alias, only to keep this code similar to valid() above
+ const auto known_only = p_ - cp_;
+ if (known_only == 1)
+ {
+ if((p[0] & 0xe0) == 0xc0)
+ {
+ return ((p[0] & 0x1e) == 0); // overlong
+ }
+ if((p[0] & 0xf0) == 0xe0)
+ {
+ return false;
+ }
+ if((p[0] & 0xf8) == 0xf0)
+ {
+ return ((p[0] & 0x07) >= 0x05); // invalid F5...FF characters
+ }
+ }
+ else if (known_only == 2)
+ {
+ if((p[0] & 0xe0) == 0xc0)
+ {
+ return ((p[1] & 0xc0) != 0x80 ||
+ (p[0] & 0x1e) == 0); // overlong
+ }
+ if((p[0] & 0xf0) == 0xe0)
+ {
+ return ( (p[1] & 0xc0) != 0x80
+ || (p[0] == 0xe0 && (p[1] & 0x20) == 0) // overlong
+ || (p[0] == 0xed && (p[1] & 0x20) == 0x20)); // surrogate
+ }
+ if((p[0] & 0xf8) == 0xf0)
+ {
+ return ( (p[0] & 0x07) >= 0x05 // invalid F5...FF characters
+ || (p[1] & 0xc0) != 0x80
+ || (p[0] == 0xf0 && (p[1] & 0x30) == 0) // overlong
+ || (p[0] == 0xf4 && p[1] > 0x8f) || p[0] > 0xf4); // > U+10FFFF
+ }
+ }
+ else if (known_only == 3)
+ {
+ if((p[0] & 0xe0) == 0xc0)
+ {
+ return ( (p[1] & 0xc0) != 0x80
+ || (p[0] & 0x1e) == 0); // overlong
+ }
+ if((p[0] & 0xf0) == 0xe0)
+ {
+ return ( (p[1] & 0xc0) != 0x80
+ || (p[2] & 0xc0) != 0x80
+ || (p[0] == 0xe0 && (p[1] & 0x20) == 0) // overlong
+ || (p[0] == 0xed && (p[1] & 0x20) == 0x20)); // surrogate
+ //|| (p[0] == 0xef && p[1] == 0xbf && (p[2] & 0xfe) == 0xbe) // U+FFFE or U+FFFF
+ }
+ if((p[0] & 0xf8) == 0xf0)
+ {
+ return ( (p[0] & 0x07) >= 0x05 // invalid F5...FF characters
+ || (p[1] & 0xc0) != 0x80
+ || (p[2] & 0xc0) != 0x80
+ || (p[0] == 0xf0 && (p[1] & 0x30) == 0) // overlong
+ || (p[0] == 0xf4 && p[1] > 0x8f) || p[0] > 0xf4); // > U+10FFFF
+ }
+ }
+ return true;
+ };
+ auto const needed =
+ [](std::uint8_t const v)
+ {
+ if(v < 128)
+ return 1;
+ if(v < 192)
+ return 0;
+ if(v < 224)
+ return 2;
+ if(v < 240)
+ return 3;
+ if(v < 248)
+ return 4;
+ return 0;
+ };
+
+ auto const end = in + size;
+
+ // Finish up any incomplete code point
+ if(need_ > 0)
+ {
+ // Calculate what we have
+ auto n = (std::min)(size, need_);
+ size -= n;
+ need_ -= n;
+
+ // Add characters to the code point
+ while(n--)
+ *p_++ = *in++;
+ BOOST_ASSERT(p_ <= cp_ + 4);
+
+ // Still incomplete?
+ if(need_ > 0)
+ {
+ // Incomplete code point
+ BOOST_ASSERT(in == end);
+
+ // Do partial validation on the incomplete
+ // code point, this is called "Fail fast"
+ // in Autobahn|Testsuite parlance.
+ return ! fail_fast();
+ }
+
+ // Complete code point, validate it
+ std::uint8_t const* p = &cp_[0];
+ if(! valid(p))
+ return false;
+ p_ = cp_;
+ }
+
+ if(size <= sizeof(std::size_t))
+ goto slow;
+
+ // Align `in` to sizeof(std::size_t) boundary
+ {
+ auto const in0 = in;
+ auto last = reinterpret_cast<std::uint8_t const*>(
+ ((reinterpret_cast<std::uintptr_t>(in) + sizeof(std::size_t) - 1) /
+ sizeof(std::size_t)) * sizeof(std::size_t));
+
+ // Check one character at a time for low-ASCII
+ while(in < last)
+ {
+ if(*in & 0x80)
+ {
+ // Not low-ASCII so switch to slow loop
+ size = size - (in - in0);
+ goto slow;
+ }
+ ++in;
+ }
+ size = size - (in - in0);
+ }
+
+ // Fast loop: Process 4 or 8 low-ASCII characters at a time
+ {
+ auto const in0 = in;
+ auto last = in + size - 7;
+ auto constexpr mask = static_cast<
+ std::size_t>(0x8080808080808080 & ~std::size_t{0});
+ while(in < last)
+ {
+#if 0
+ std::size_t temp;
+ std::memcpy(&temp, in, sizeof(temp));
+ if((temp & mask) != 0)
+#else
+ // Technically UB but works on all known platforms
+ if((*reinterpret_cast<std::size_t const*>(in) & mask) != 0)
+#endif
+ {
+ size = size - (in - in0);
+ goto slow;
+ }
+ in += sizeof(std::size_t);
+ }
+ // There's at least one more full code point left
+ last += 4;
+ while(in < last)
+ if(! valid(in))
+ return false;
+ goto tail;
+ }
+
+slow:
+ // Slow loop: Full validation on one code point at a time
+ {
+ auto last = in + size - 3;
+ while(in < last)
+ if(! valid(in))
+ return false;
+ }
+
+tail:
+ // Handle the remaining bytes. The last
+ // characters could split a code point so
+ // we save the partial code point for later.
+ //
+ // On entry to the loop, `in` points to the
+ // beginning of a code point.
+ //
+ for(;;)
+ {
+ // Number of chars left
+ auto n = end - in;
+ if(! n)
+ break;
+
+ // Chars we need to finish this code point
+ auto const need = needed(*in);
+ if(need == 0)
+ return false;
+ if(need <= n)
+ {
+ // Check a whole code point
+ if(! valid(in))
+ return false;
+ }
+ else
+ {
+ // Calculate how many chars we need
+ // to finish this partial code point
+ need_ = need - n;
+
+ // Save the partial code point
+ while(n--)
+ *p_++ = *in++;
+ BOOST_ASSERT(in == end);
+ BOOST_ASSERT(p_ <= cp_ + 4);
+
+ // Do partial validation on the incomplete
+ // code point, this is called "Fail fast"
+ // in Autobahn|Testsuite parlance.
+ return ! fail_fast();
+ }
+ }
+ return true;
+}
+
+bool
+check_utf8(char const* p, std::size_t n)
+{
+ utf8_checker c;
+ if(! c.write(reinterpret_cast<const uint8_t*>(p), n))
+ return false;
+ return c.finish();
+}
+
+} // detail
+} // websocket
+} // beast
+} // boost
+
+#endif // BOOST_BEAST_WEBSOCKET_DETAIL_UTF8_CHECKER_IPP
diff --git a/boost/beast/websocket/error.hpp b/boost/beast/websocket/error.hpp
index d4e5197100..cc510912d9 100644
--- a/boost/beast/websocket/error.hpp
+++ b/boost/beast/websocket/error.hpp
@@ -1,5 +1,5 @@
//
-// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+// Copyright (c) 2016-2019 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)
@@ -10,7 +10,6 @@
#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>
@@ -249,6 +248,9 @@ enum class condition
} // beast
} // boost
+#include <boost/beast/websocket/impl/error.hpp>
+#ifdef BOOST_BEAST_HEADER_ONLY
#include <boost/beast/websocket/impl/error.ipp>
+#endif
#endif
diff --git a/boost/beast/websocket/impl/accept.hpp b/boost/beast/websocket/impl/accept.hpp
new file mode 100644
index 0000000000..4b989b56c7
--- /dev/null
+++ b/boost/beast/websocket/impl/accept.hpp
@@ -0,0 +1,862 @@
+//
+// Copyright (c) 2016-2019 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_IMPL_ACCEPT_IPP
+#define BOOST_BEAST_WEBSOCKET_IMPL_ACCEPT_IPP
+
+#include <boost/beast/websocket/impl/stream_impl.hpp>
+#include <boost/beast/websocket/detail/type_traits.hpp>
+#include <boost/beast/http/empty_body.hpp>
+#include <boost/beast/http/parser.hpp>
+#include <boost/beast/http/read.hpp>
+#include <boost/beast/http/string_body.hpp>
+#include <boost/beast/http/write.hpp>
+#include <boost/beast/core/async_base.hpp>
+#include <boost/beast/core/buffer_traits.hpp>
+#include <boost/beast/core/stream_traits.hpp>
+#include <boost/beast/core/detail/buffer.hpp>
+#include <boost/beast/core/detail/type_traits.hpp>
+#include <boost/beast/version.hpp>
+#include <boost/asio/coroutine.hpp>
+#include <boost/asio/post.hpp>
+#include <boost/assert.hpp>
+#include <boost/throw_exception.hpp>
+#include <memory>
+#include <type_traits>
+
+namespace boost {
+namespace beast {
+namespace websocket {
+
+//------------------------------------------------------------------------------
+
+namespace detail {
+
+template<class Body, class Allocator>
+void
+impl_base<true>::
+build_response_pmd(
+ http::response<http::string_body>& res,
+ http::request<Body,
+ http::basic_fields<Allocator>> const& req)
+{
+ pmd_offer offer;
+ pmd_offer unused;
+ pmd_read(offer, req);
+ pmd_negotiate(res, unused, offer, pmd_opts_);
+}
+
+template<class Body, class Allocator>
+void
+impl_base<false>::
+build_response_pmd(
+ http::response<http::string_body>&,
+ http::request<Body,
+ http::basic_fields<Allocator>> const&)
+{
+}
+
+} // detail
+
+template<class NextLayer, bool deflateSupported>
+template<class Body, class Allocator, class Decorator>
+response_type
+stream<NextLayer, deflateSupported>::impl_type::
+build_response(
+ http::request<Body,
+ http::basic_fields<Allocator>> const& req,
+ Decorator const& decorator,
+ error_code& result)
+{
+ auto const decorate =
+ [this, &decorator](response_type& res)
+ {
+ decorator_opt(res);
+ decorator(res);
+ if(! res.count(http::field::server))
+ {
+ // VFALCO this is weird..
+ BOOST_STATIC_ASSERT(sizeof(
+ BOOST_BEAST_VERSION_STRING) < 20);
+ static_string<20> s(BOOST_BEAST_VERSION_STRING);
+ res.set(http::field::server, s);
+ }
+ };
+ auto err =
+ [&](error e)
+ {
+ result = e;
+ response_type res;
+ res.version(req.version());
+ res.result(http::status::bad_request);
+ res.body() = result.message();
+ res.prepare_payload();
+ decorate(res);
+ return res;
+ };
+ if(req.version() != 11)
+ return err(error::bad_http_version);
+ if(req.method() != http::verb::get)
+ return err(error::bad_method);
+ if(! req.count(http::field::host))
+ 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;
+ }
+ }
+
+ response_type res;
+ res.result(http::status::switching_protocols);
+ res.version(req.version());
+ res.set(http::field::upgrade, "websocket");
+ res.set(http::field::connection, "upgrade");
+ {
+ detail::sec_ws_accept_type acc;
+ detail::make_sec_ws_accept(acc, key);
+ res.set(http::field::sec_websocket_accept, acc);
+ }
+ this->build_response_pmd(res, req);
+ decorate(res);
+ result = {};
+ return res;
+}
+
+//------------------------------------------------------------------------------
+
+/** Respond to an HTTP request
+*/
+template<class NextLayer, bool deflateSupported>
+template<class Handler>
+class stream<NextLayer, deflateSupported>::response_op
+ : public beast::stable_async_base<
+ Handler, beast::executor_type<stream>>
+ , public net::coroutine
+{
+ boost::weak_ptr<impl_type> wp_;
+ error_code result_; // must come before res_
+ response_type& res_;
+
+public:
+ template<
+ class Handler_,
+ class Body, class Allocator,
+ class Decorator>
+ response_op(
+ Handler_&& h,
+ boost::shared_ptr<impl_type> const& sp,
+ http::request<Body,
+ http::basic_fields<Allocator>> const& req,
+ Decorator const& decorator,
+ bool cont = false)
+ : stable_async_base<Handler,
+ beast::executor_type<stream>>(
+ std::forward<Handler_>(h),
+ sp->stream().get_executor())
+ , wp_(sp)
+ , res_(beast::allocate_stable<response_type>(*this,
+ sp->build_response(req, decorator, result_)))
+ {
+ (*this)({}, 0, cont);
+ }
+
+ void operator()(
+ error_code ec = {},
+ std::size_t bytes_transferred = 0,
+ bool cont = true)
+ {
+ boost::ignore_unused(bytes_transferred);
+ auto sp = wp_.lock();
+ if(! sp)
+ {
+ ec = net::error::operation_aborted;
+ return this->complete(cont, ec);
+ }
+ auto& impl = *sp;
+ BOOST_ASIO_CORO_REENTER(*this)
+ {
+ impl.change_status(status::handshake);
+ impl.update_timer(this->get_executor());
+
+ // Send response
+ BOOST_ASIO_CORO_YIELD
+ http::async_write(
+ impl.stream(), res_, std::move(*this));
+ if(impl.check_stop_now(ec))
+ goto upcall;
+ if(! ec)
+ ec = result_;
+ if(! ec)
+ {
+ impl.do_pmd_config(res_);
+ impl.open(role_type::server);
+ }
+ upcall:
+ this->complete(cont, ec);
+ }
+ }
+};
+
+//------------------------------------------------------------------------------
+
+// read and respond to an upgrade request
+//
+template<class NextLayer, bool deflateSupported>
+template<class Handler, class Decorator>
+class stream<NextLayer, deflateSupported>::accept_op
+ : public beast::stable_async_base<
+ Handler, beast::executor_type<stream>>
+ , public net::coroutine
+{
+ boost::weak_ptr<impl_type> wp_;
+ http::request_parser<http::empty_body>& p_;
+ Decorator d_;
+
+public:
+ template<class Handler_, class Buffers>
+ accept_op(
+ Handler_&& h,
+ boost::shared_ptr<impl_type> const& sp,
+ Decorator const& decorator,
+ Buffers const& buffers)
+ : stable_async_base<Handler,
+ beast::executor_type<stream>>(
+ std::forward<Handler_>(h),
+ sp->stream().get_executor())
+ , wp_(sp)
+ , p_(beast::allocate_stable<
+ http::request_parser<http::empty_body>>(*this))
+ , d_(decorator)
+ {
+ auto& impl = *sp;
+ error_code ec;
+ auto const mb =
+ beast::detail::dynamic_buffer_prepare(
+ impl.rd_buf, buffer_bytes(buffers),
+ ec, error::buffer_overflow);
+ if(! ec)
+ impl.rd_buf.commit(
+ net::buffer_copy(*mb, buffers));
+ (*this)(ec);
+ }
+
+ void operator()(
+ error_code ec = {},
+ std::size_t bytes_transferred = 0,
+ bool cont = true)
+ {
+ boost::ignore_unused(bytes_transferred);
+ auto sp = wp_.lock();
+ if(! sp)
+ {
+ ec = net::error::operation_aborted;
+ return this->complete(cont, ec);
+ }
+ auto& impl = *sp;
+ BOOST_ASIO_CORO_REENTER(*this)
+ {
+ impl.change_status(status::handshake);
+ impl.update_timer(this->get_executor());
+
+ // The constructor could have set ec
+ if(ec)
+ goto upcall;
+
+ BOOST_ASIO_CORO_YIELD
+ http::async_read(impl.stream(),
+ impl.rd_buf, p_, std::move(*this));
+ if(ec == http::error::end_of_stream)
+ ec = error::closed;
+ if(impl.check_stop_now(ec))
+ goto upcall;
+
+ {
+ // Arguments from our state must be
+ // moved to the stack before releasing
+ // the handler.
+ auto const req = p_.release();
+ auto const decorator = d_;
+ response_op<Handler>(
+ this->release_handler(),
+ sp, req, decorator, true);
+ return;
+ }
+
+ upcall:
+ this->complete(cont, ec);
+ }
+ }
+};
+
+template<class NextLayer, bool deflateSupported>
+struct stream<NextLayer, deflateSupported>::
+ run_response_op
+{
+ template<
+ class AcceptHandler,
+ class Body, class Allocator,
+ class Decorator>
+ void
+ operator()(
+ AcceptHandler&& h,
+ boost::shared_ptr<impl_type> const& sp,
+ http::request<Body,
+ http::basic_fields<Allocator>> const* m,
+ Decorator const& d)
+ {
+ // If you get an error on the following line it means
+ // that your handler does not meet the documented type
+ // requirements for the handler.
+
+ static_assert(
+ beast::detail::is_invocable<AcceptHandler,
+ void(error_code)>::value,
+ "AcceptHandler type requirements not met");
+
+ response_op<
+ typename std::decay<AcceptHandler>::type>(
+ std::forward<AcceptHandler>(h), sp, *m, d);
+ }
+};
+
+template<class NextLayer, bool deflateSupported>
+struct stream<NextLayer, deflateSupported>::
+ run_accept_op
+{
+ template<
+ class AcceptHandler,
+ class Decorator,
+ class Buffers>
+ void
+ operator()(
+ AcceptHandler&& h,
+ boost::shared_ptr<impl_type> const& sp,
+ Decorator const& d,
+ Buffers const& b)
+ {
+ // If you get an error on the following line it means
+ // that your handler does not meet the documented type
+ // requirements for the handler.
+
+ static_assert(
+ beast::detail::is_invocable<AcceptHandler,
+ void(error_code)>::value,
+ "AcceptHandler type requirements not met");
+
+ accept_op<
+ typename std::decay<AcceptHandler>::type,
+ Decorator>(
+ std::forward<AcceptHandler>(h),
+ sp,
+ d,
+ b);
+ }
+};
+
+//------------------------------------------------------------------------------
+
+template<class NextLayer, bool deflateSupported>
+template<class Body, class Allocator,
+ class Decorator>
+void
+stream<NextLayer, deflateSupported>::
+do_accept(
+ http::request<Body,
+ http::basic_fields<Allocator>> const& req,
+ Decorator const& decorator,
+ error_code& ec)
+{
+ impl_->change_status(status::handshake);
+
+ error_code result;
+ auto const res = impl_->build_response(req, decorator, result);
+ http::write(impl_->stream(), res, ec);
+ if(ec)
+ return;
+ ec = result;
+ if(ec)
+ {
+ // VFALCO TODO Respect keep alive setting, perform
+ // teardown if Connection: close.
+ return;
+ }
+ impl_->do_pmd_config(res);
+ impl_->open(role_type::server);
+}
+
+template<class NextLayer, bool deflateSupported>
+template<class Buffers, class Decorator>
+void
+stream<NextLayer, deflateSupported>::
+do_accept(
+ Buffers const& buffers,
+ Decorator const& decorator,
+ error_code& ec)
+{
+ impl_->reset();
+ auto const mb =
+ beast::detail::dynamic_buffer_prepare(
+ impl_->rd_buf, buffer_bytes(buffers), ec,
+ error::buffer_overflow);
+ if(ec)
+ return;
+ impl_->rd_buf.commit(net::buffer_copy(*mb, buffers));
+
+ http::request_parser<http::empty_body> p;
+ http::read(next_layer(), impl_->rd_buf, p, ec);
+ if(ec == http::error::end_of_stream)
+ ec = error::closed;
+ if(ec)
+ return;
+ do_accept(p.get(), decorator, ec);
+}
+
+//------------------------------------------------------------------------------
+
+template<class NextLayer, bool deflateSupported>
+void
+stream<NextLayer, deflateSupported>::
+accept()
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream type requirements not met");
+ error_code ec;
+ accept(ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+}
+
+template<class NextLayer, bool deflateSupported>
+void
+stream<NextLayer, deflateSupported>::
+accept(error_code& ec)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream type requirements not met");
+ do_accept(
+ net::const_buffer{},
+ &default_decorate_res, ec);
+}
+
+template<class NextLayer, bool deflateSupported>
+template<class ConstBufferSequence>
+typename std::enable_if<! http::detail::is_header<
+ ConstBufferSequence>::value>::type
+stream<NextLayer, deflateSupported>::
+accept(ConstBufferSequence const& buffers)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream type requirements not met");
+ static_assert(net::is_const_buffer_sequence<
+ ConstBufferSequence>::value,
+ "ConstBufferSequence type requirements not met");
+ error_code ec;
+ accept(buffers, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+}
+template<class NextLayer, bool deflateSupported>
+template<class ConstBufferSequence>
+typename std::enable_if<! http::detail::is_header<
+ ConstBufferSequence>::value>::type
+stream<NextLayer, deflateSupported>::
+accept(
+ ConstBufferSequence const& buffers, error_code& ec)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream type requirements not met");
+ static_assert(net::is_const_buffer_sequence<
+ ConstBufferSequence>::value,
+ "ConstBufferSequence type requirements not met");
+ do_accept(buffers, &default_decorate_res, ec);
+}
+
+
+template<class NextLayer, bool deflateSupported>
+template<class Body, class Allocator>
+void
+stream<NextLayer, deflateSupported>::
+accept(
+ http::request<Body,
+ http::basic_fields<Allocator>> const& req)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream type requirements not met");
+ error_code ec;
+ accept(req, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+}
+
+template<class NextLayer, bool deflateSupported>
+template<class Body, class Allocator>
+void
+stream<NextLayer, deflateSupported>::
+accept(
+ http::request<Body,
+ http::basic_fields<Allocator>> const& req,
+ error_code& ec)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream type requirements not met");
+ impl_->reset();
+ do_accept(req, &default_decorate_res, ec);
+}
+
+//------------------------------------------------------------------------------
+
+template<class NextLayer, bool deflateSupported>
+template<
+ class AcceptHandler>
+BOOST_BEAST_ASYNC_RESULT1(AcceptHandler)
+stream<NextLayer, deflateSupported>::
+async_accept(
+ AcceptHandler&& handler)
+{
+ static_assert(is_async_stream<next_layer_type>::value,
+ "AsyncStream type requirements not met");
+ impl_->reset();
+ return net::async_initiate<
+ AcceptHandler,
+ void(error_code)>(
+ run_accept_op{},
+ handler,
+ impl_,
+ &default_decorate_res,
+ net::const_buffer{});
+}
+
+template<class NextLayer, bool deflateSupported>
+template<
+ class ResponseDecorator,
+ class AcceptHandler>
+BOOST_BEAST_ASYNC_RESULT1(AcceptHandler)
+stream<NextLayer, deflateSupported>::
+async_accept_ex(
+ ResponseDecorator const& decorator,
+ AcceptHandler&& handler)
+{
+ static_assert(is_async_stream<next_layer_type>::value,
+ "AsyncStream type requirements not met");
+ static_assert(detail::is_response_decorator<
+ ResponseDecorator>::value,
+ "ResponseDecorator requirements not met");
+ impl_->reset();
+ return net::async_initiate<
+ AcceptHandler,
+ void(error_code)>(
+ run_accept_op{},
+ handler,
+ impl_,
+ decorator,
+ net::const_buffer{});
+}
+
+template<class NextLayer, bool deflateSupported>
+template<
+ class ConstBufferSequence,
+ class AcceptHandler>
+typename std::enable_if<
+ ! http::detail::is_header<ConstBufferSequence>::value,
+ BOOST_BEAST_ASYNC_RESULT1(AcceptHandler)>::type
+stream<NextLayer, deflateSupported>::
+async_accept(
+ ConstBufferSequence const& buffers,
+ AcceptHandler&& handler)
+{
+ static_assert(is_async_stream<next_layer_type>::value,
+ "AsyncStream type requirements not met");
+ static_assert(net::is_const_buffer_sequence<
+ ConstBufferSequence>::value,
+ "ConstBufferSequence type requirements not met");
+ impl_->reset();
+ return net::async_initiate<
+ AcceptHandler,
+ void(error_code)>(
+ run_accept_op{},
+ handler,
+ impl_,
+ &default_decorate_res,
+ buffers);
+}
+
+template<class NextLayer, bool deflateSupported>
+template<
+ class ConstBufferSequence,
+ class ResponseDecorator,
+ class AcceptHandler>
+typename std::enable_if<
+ ! http::detail::is_header<ConstBufferSequence>::value,
+ BOOST_BEAST_ASYNC_RESULT1(AcceptHandler)>::type
+stream<NextLayer, deflateSupported>::
+async_accept_ex(
+ ConstBufferSequence const& buffers,
+ ResponseDecorator const& decorator,
+ AcceptHandler&& handler)
+{
+ static_assert(is_async_stream<next_layer_type>::value,
+ "AsyncStream type requirements not met");
+ static_assert(net::is_const_buffer_sequence<
+ ConstBufferSequence>::value,
+ "ConstBufferSequence type requirements not met");
+ static_assert(detail::is_response_decorator<
+ ResponseDecorator>::value,
+ "ResponseDecorator requirements not met");
+ impl_->reset();
+ return net::async_initiate<
+ AcceptHandler,
+ void(error_code)>(
+ run_accept_op{},
+ handler,
+ impl_,
+ decorator,
+ buffers);
+}
+
+template<class NextLayer, bool deflateSupported>
+template<
+ class Body, class Allocator,
+ class AcceptHandler>
+BOOST_BEAST_ASYNC_RESULT1(AcceptHandler)
+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 type requirements not met");
+ impl_->reset();
+ return net::async_initiate<
+ AcceptHandler,
+ void(error_code)>(
+ run_response_op{},
+ handler,
+ impl_,
+ &req,
+ &default_decorate_res);
+}
+
+template<class NextLayer, bool deflateSupported>
+template<
+ class Body, class Allocator,
+ class ResponseDecorator,
+ class AcceptHandler>
+BOOST_BEAST_ASYNC_RESULT1(AcceptHandler)
+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 type requirements not met");
+ static_assert(detail::is_response_decorator<
+ ResponseDecorator>::value,
+ "ResponseDecorator requirements not met");
+ impl_->reset();
+ return net::async_initiate<
+ AcceptHandler,
+ void(error_code)>(
+ run_response_op{},
+ handler,
+ impl_,
+ &req,
+ decorator);
+}
+
+//------------------------------------------------------------------------------
+
+template<class NextLayer, bool deflateSupported>
+template<class ResponseDecorator>
+void
+stream<NextLayer, deflateSupported>::
+accept_ex(ResponseDecorator const& decorator)
+{
+#ifndef BOOST_BEAST_ALLOW_DEPRECATED
+ static_assert(sizeof(ResponseDecorator) == 0,
+ BOOST_BEAST_DEPRECATION_STRING);
+#endif
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream type requirements not met");
+ static_assert(detail::is_response_decorator<
+ ResponseDecorator>::value,
+ "ResponseDecorator requirements not met");
+ error_code ec;
+ accept_ex(decorator, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+}
+
+template<class NextLayer, bool deflateSupported>
+template<class ResponseDecorator>
+void
+stream<NextLayer, deflateSupported>::
+accept_ex(ResponseDecorator const& decorator, error_code& ec)
+{
+#ifndef BOOST_BEAST_ALLOW_DEPRECATED
+ static_assert(sizeof(ResponseDecorator) == 0,
+ BOOST_BEAST_DEPRECATION_STRING);
+#endif
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream type requirements not met");
+ static_assert(detail::is_response_decorator<
+ ResponseDecorator>::value,
+ "ResponseDecorator requirements not met");
+ do_accept(
+ net::const_buffer{},
+ decorator, ec);
+}
+
+template<class NextLayer, bool deflateSupported>
+template<
+ class ConstBufferSequence,
+ class ResponseDecorator>
+typename std::enable_if<! http::detail::is_header<
+ ConstBufferSequence>::value>::type
+stream<NextLayer, deflateSupported>::
+accept_ex(
+ ConstBufferSequence const& buffers,
+ ResponseDecorator const &decorator)
+{
+#ifndef BOOST_BEAST_ALLOW_DEPRECATED
+ static_assert(sizeof(ResponseDecorator) == 0,
+ BOOST_BEAST_DEPRECATION_STRING);
+#endif
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream type requirements not met");
+ static_assert(net::is_const_buffer_sequence<
+ ConstBufferSequence>::value,
+ "ConstBufferSequence type requirements not met");
+ static_assert(detail::is_response_decorator<
+ ResponseDecorator>::value,
+ "ResponseDecorator requirements not met");
+ error_code ec;
+ accept_ex(buffers, decorator, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+}
+
+template<class NextLayer, bool deflateSupported>
+template<
+ class ConstBufferSequence,
+ class ResponseDecorator>
+typename std::enable_if<! http::detail::is_header<
+ ConstBufferSequence>::value>::type
+stream<NextLayer, deflateSupported>::
+accept_ex(
+ ConstBufferSequence const& buffers,
+ ResponseDecorator const& decorator,
+ error_code& ec)
+{
+#ifndef BOOST_BEAST_ALLOW_DEPRECATED
+ static_assert(sizeof(ResponseDecorator) == 0,
+ BOOST_BEAST_DEPRECATION_STRING);
+#endif
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream type requirements not met");
+ static_assert(net::is_const_buffer_sequence<
+ ConstBufferSequence>::value,
+ "ConstBufferSequence type requirements not met");
+ static_assert(net::is_const_buffer_sequence<
+ ConstBufferSequence>::value,
+ "ConstBufferSequence type requirements not met");
+ do_accept(buffers, decorator, ec);
+}
+
+template<class NextLayer, bool deflateSupported>
+template<
+ class Body, class Allocator,
+ class ResponseDecorator>
+void
+stream<NextLayer, deflateSupported>::
+accept_ex(
+ http::request<Body,
+ http::basic_fields<Allocator>> const& req,
+ ResponseDecorator const& decorator)
+{
+#ifndef BOOST_BEAST_ALLOW_DEPRECATED
+ static_assert(sizeof(ResponseDecorator) == 0,
+ BOOST_BEAST_DEPRECATION_STRING);
+#endif
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream type requirements not met");
+ static_assert(detail::is_response_decorator<
+ ResponseDecorator>::value,
+ "ResponseDecorator requirements not met");
+ error_code ec;
+ accept_ex(req, decorator, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+}
+
+template<class NextLayer, bool deflateSupported>
+template<
+ class Body, class Allocator,
+ class ResponseDecorator>
+void
+stream<NextLayer, deflateSupported>::
+accept_ex(
+ http::request<Body,
+ http::basic_fields<Allocator>> const& req,
+ ResponseDecorator const& decorator,
+ error_code& ec)
+{
+#ifndef BOOST_BEAST_ALLOW_DEPRECATED
+ static_assert(sizeof(ResponseDecorator) == 0,
+ BOOST_BEAST_DEPRECATION_STRING);
+#endif
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream type requirements not met");
+ static_assert(detail::is_response_decorator<
+ ResponseDecorator>::value,
+ "ResponseDecorator requirements not met");
+ impl_->reset();
+ do_accept(req, decorator, ec);
+}
+
+} // websocket
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/websocket/impl/accept.ipp b/boost/beast/websocket/impl/accept.ipp
deleted file mode 100644
index 5a322990a3..0000000000
--- a/boost/beast/websocket/impl/accept.ipp
+++ /dev/null
@@ -1,774 +0,0 @@
-//
-// 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_IMPL_ACCEPT_IPP
-#define BOOST_BEAST_WEBSOCKET_IMPL_ACCEPT_IPP
-
-#include <boost/beast/websocket/detail/type_traits.hpp>
-#include <boost/beast/http/empty_body.hpp>
-#include <boost/beast/http/parser.hpp>
-#include <boost/beast/http/read.hpp>
-#include <boost/beast/http/string_body.hpp>
-#include <boost/beast/http/write.hpp>
-#include <boost/beast/core/buffers_prefix.hpp>
-#include <boost/beast/core/handler_ptr.hpp>
-#include <boost/beast/core/detail/buffer.hpp>
-#include <boost/beast/core/detail/type_traits.hpp>
-#include <boost/asio/coroutine.hpp>
-#include <boost/asio/associated_allocator.hpp>
-#include <boost/asio/associated_executor.hpp>
-#include <boost/asio/executor_work_guard.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>
-#include <memory>
-#include <type_traits>
-
-namespace boost {
-namespace beast {
-namespace websocket {
-
-// Respond to an upgrade HTTP request
-template<class NextLayer, bool deflateSupported>
-template<class Handler>
-class stream<NextLayer, deflateSupported>::response_op
- : public boost::asio::coroutine
-{
- struct data
- {
- stream<NextLayer, deflateSupported>& ws;
- boost::asio::executor_work_guard<decltype(std::declval<
- stream<NextLayer, deflateSupported>&>().get_executor())> wg;
- error_code result;
- response_type res;
-
- template<class Body, class Allocator, class Decorator>
- data(
- Handler const&,
- stream<NextLayer, deflateSupported>& ws_,
- http::request<Body,
- http::basic_fields<Allocator>> const& req,
- Decorator const& decorator)
- : ws(ws_)
- , wg(ws.get_executor())
- , res(ws_.build_response(req, decorator, result))
- {
- }
- };
-
- handler_ptr<data, Handler> d_;
-
-public:
- response_op(response_op&&) = default;
- response_op(response_op const&) = delete;
-
- template<class DeducedHandler, class... Args>
- response_op(DeducedHandler&& h,
- stream<NextLayer, deflateSupported>& ws, Args&&... args)
- : d_(std::forward<DeducedHandler>(h),
- ws, std::forward<Args>(args)...)
- {
- }
-
- using allocator_type =
- boost::asio::associated_allocator_t<Handler>;
-
- allocator_type
- get_allocator() const noexcept
- {
- return (boost::asio::get_associated_allocator)(d_.handler());
- }
-
- using executor_type = boost::asio::associated_executor_t<
- Handler, decltype(std::declval<
- stream<NextLayer, deflateSupported>&>().get_executor())>;
-
- executor_type
- get_executor() const noexcept
- {
- return (boost::asio::get_associated_executor)(
- d_.handler(), d_->ws.get_executor());
- }
-
- void operator()(
- error_code ec = {},
- std::size_t bytes_transferred = 0);
-
- friend
- bool asio_handler_is_continuation(response_op* op)
- {
- using boost::asio::asio_handler_is_continuation;
- 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, bool deflateSupported>
-template<class Handler>
-void
-stream<NextLayer, deflateSupported>::
-response_op<Handler>::
-operator()(
- error_code ec,
- std::size_t)
-{
- auto& d = *d_;
- BOOST_ASIO_CORO_REENTER(*this)
- {
- // Send response
- BOOST_ASIO_CORO_YIELD
- http::async_write(d.ws.next_layer(),
- d.res, std::move(*this));
- if(! ec)
- ec = d.result;
- if(! ec)
- {
- d.ws.do_pmd_config(d.res, is_deflate_supported{});
- d.ws.open(role_type::server);
- }
- {
- auto wg = std::move(d.wg);
- d_.invoke(ec);
- }
- }
-}
-
-//------------------------------------------------------------------------------
-
-// read and respond to an upgrade request
-//
-template<class NextLayer, bool deflateSupported>
-template<class Decorator, class Handler>
-class stream<NextLayer, deflateSupported>::accept_op
- : public boost::asio::coroutine
-{
- struct data
- {
- stream<NextLayer, deflateSupported>& ws;
- boost::asio::executor_work_guard<decltype(std::declval<
- stream<NextLayer, deflateSupported>&>().get_executor())> wg;
- Decorator decorator;
- http::request_parser<http::empty_body> p;
- data(
- Handler const&,
- stream<NextLayer, deflateSupported>& ws_,
- Decorator const& decorator_)
- : ws(ws_)
- , wg(ws.get_executor())
- , decorator(decorator_)
- {
- }
- };
-
- handler_ptr<data, Handler> d_;
-
-public:
- accept_op(accept_op&&) = default;
- accept_op(accept_op const&) = delete;
-
- template<class DeducedHandler, class... Args>
- accept_op(DeducedHandler&& h,
- stream<NextLayer, deflateSupported>& ws, Args&&... args)
- : d_(std::forward<DeducedHandler>(h),
- ws, std::forward<Args>(args)...)
- {
- }
-
- using allocator_type =
- boost::asio::associated_allocator_t<Handler>;
-
- allocator_type
- get_allocator() const noexcept
- {
- return (boost::asio::get_associated_allocator)(d_.handler());
- }
-
- using executor_type = boost::asio::associated_executor_t<
- Handler, decltype(std::declval<stream<NextLayer, deflateSupported>&>().get_executor())>;
-
- executor_type
- get_executor() const noexcept
- {
- return (boost::asio::get_associated_executor)(
- d_.handler(), d_->ws.get_executor());
- }
-
- template<class Buffers>
- void run(Buffers const& buffers);
-
- void operator()(
- error_code ec = {},
- std::size_t bytes_used = 0);
-
- friend
- bool asio_handler_is_continuation(accept_op* op)
- {
- using boost::asio::asio_handler_is_continuation;
- 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, bool deflateSupported>
-template<class Decorator, class Handler>
-template<class Buffers>
-void
-stream<NextLayer, deflateSupported>::
-accept_op<Decorator, Handler>::
-run(Buffers const& buffers)
-{
- using boost::asio::buffer_copy;
- using boost::asio::buffer_size;
- auto& d = *d_;
- error_code ec;
- auto const mb = beast::detail::dynamic_buffer_prepare(
- d.ws.rd_buf_, buffer_size(buffers), ec,
- error::buffer_overflow);
- if(ec)
- return (*this)(ec);
- d.ws.rd_buf_.commit(buffer_copy(*mb, buffers));
- (*this)(ec);
-}
-
-template<class NextLayer, bool deflateSupported>
-template<class Decorator, class Handler>
-void
-stream<NextLayer, deflateSupported>::
-accept_op<Decorator, Handler>::
-operator()(error_code ec, std::size_t)
-{
- auto& d = *d_;
- BOOST_ASIO_CORO_REENTER(*this)
- {
- if(ec)
- {
- BOOST_ASIO_CORO_YIELD
- boost::asio::post(
- d.ws.get_executor(),
- bind_handler(std::move(*this), ec));
- }
- else
- {
- BOOST_ASIO_CORO_YIELD
- http::async_read(
- d.ws.next_layer(), d.ws.rd_buf_,
- d.p, std::move(*this));
- if(ec == http::error::end_of_stream)
- ec = error::closed;
- if(! ec)
- {
- // Arguments from our state must be
- // moved to the stack before releasing
- // the handler.
- auto& ws = d.ws;
- auto const req = d.p.release();
- auto const decorator = d.decorator;
- auto wg = std::move(d.wg);
- #if 1
- return response_op<Handler>{
- d_.release_handler(),
- ws, req, decorator}(ec);
- #else
- // VFALCO This *should* work but breaks
- // coroutine invariants in the unit test.
- // Also it calls reset() when it shouldn't.
- return ws.async_accept_ex(
- req, decorator, d_.release_handler());
- #endif
- }
- }
- {
- auto wg = std::move(d.wg);
- d_.invoke(ec);
- }
- }
-}
-
-//------------------------------------------------------------------------------
-
-template<class NextLayer, bool deflateSupported>
-void
-stream<NextLayer, deflateSupported>::
-accept()
-{
- static_assert(is_sync_stream<next_layer_type>::value,
- "SyncStream requirements not met");
- error_code ec;
- accept(ec);
- if(ec)
- BOOST_THROW_EXCEPTION(system_error{ec});
-}
-
-template<class NextLayer, bool deflateSupported>
-template<class ResponseDecorator>
-void
-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_response_decorator<
- ResponseDecorator>::value,
- "ResponseDecorator requirements not met");
- error_code ec;
- accept_ex(decorator, ec);
- if(ec)
- BOOST_THROW_EXCEPTION(system_error{ec});
-}
-
-template<class NextLayer, bool deflateSupported>
-void
-stream<NextLayer, deflateSupported>::
-accept(error_code& ec)
-{
- static_assert(is_sync_stream<next_layer_type>::value,
- "SyncStream requirements not met");
- reset();
- do_accept(&default_decorate_res, ec);
-}
-
-template<class NextLayer, bool deflateSupported>
-template<class ResponseDecorator>
-void
-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_response_decorator<
- ResponseDecorator>::value,
- "ResponseDecorator requirements not met");
- reset();
- do_accept(decorator, ec);
-}
-
-template<class NextLayer, bool deflateSupported>
-template<class ConstBufferSequence>
-typename std::enable_if<! http::detail::is_header<
- ConstBufferSequence>::value>::type
-stream<NextLayer, deflateSupported>::
-accept(ConstBufferSequence const& buffers)
-{
- static_assert(is_sync_stream<next_layer_type>::value,
- "SyncStream requirements not met");
- static_assert(boost::asio::is_const_buffer_sequence<
- ConstBufferSequence>::value,
- "ConstBufferSequence requirements not met");
- error_code ec;
- accept(buffers, ec);
- if(ec)
- BOOST_THROW_EXCEPTION(system_error{ec});
-}
-
-template<class NextLayer, bool deflateSupported>
-template<
- class ConstBufferSequence,
- class ResponseDecorator>
-typename std::enable_if<! http::detail::is_header<
- ConstBufferSequence>::value>::type
-stream<NextLayer, deflateSupported>::
-accept_ex(
- ConstBufferSequence const& buffers,
- ResponseDecorator const &decorator)
-{
- static_assert(is_sync_stream<next_layer_type>::value,
- "SyncStream requirements not met");
- static_assert(boost::asio::is_const_buffer_sequence<
- ConstBufferSequence>::value,
- "ConstBufferSequence requirements not met");
- static_assert(detail::is_response_decorator<
- ResponseDecorator>::value,
- "ResponseDecorator requirements not met");
- error_code ec;
- accept_ex(buffers, decorator, ec);
- if(ec)
- BOOST_THROW_EXCEPTION(system_error{ec});
-}
-
-template<class NextLayer, bool deflateSupported>
-template<class ConstBufferSequence>
-typename std::enable_if<! http::detail::is_header<
- ConstBufferSequence>::value>::type
-stream<NextLayer, deflateSupported>::
-accept(
- ConstBufferSequence const& buffers, error_code& ec)
-{
- static_assert(is_sync_stream<next_layer_type>::value,
- "SyncStream requirements not met");
- static_assert(boost::asio::is_const_buffer_sequence<
- ConstBufferSequence>::value,
- "ConstBufferSequence requirements not met");
- using boost::asio::buffer_copy;
- using boost::asio::buffer_size;
- reset();
- auto const mb = beast::detail::dynamic_buffer_prepare(
- rd_buf_, buffer_size(buffers), ec,
- error::buffer_overflow);
- if(ec)
- return;
- rd_buf_.commit(buffer_copy(*mb, buffers));
- do_accept(&default_decorate_res, ec);
-}
-
-template<class NextLayer, bool deflateSupported>
-template<
- class ConstBufferSequence,
- class ResponseDecorator>
-typename std::enable_if<! http::detail::is_header<
- ConstBufferSequence>::value>::type
-stream<NextLayer, deflateSupported>::
-accept_ex(
- ConstBufferSequence const& buffers,
- ResponseDecorator const& decorator,
- error_code& ec)
-{
- static_assert(is_sync_stream<next_layer_type>::value,
- "SyncStream requirements not met");
- static_assert(boost::asio::is_const_buffer_sequence<
- ConstBufferSequence>::value,
- "ConstBufferSequence requirements not met");
- static_assert(boost::asio::is_const_buffer_sequence<
- ConstBufferSequence>::value,
- "ConstBufferSequence requirements not met");
- using boost::asio::buffer_copy;
- using boost::asio::buffer_size;
- reset();
- auto const mb = beast::detail::dynamic_buffer_prepare(
- rd_buf_, buffer_size(buffers), ec,
- error::buffer_overflow);
- if(ec)
- return;
- rd_buf_.commit(buffer_copy(*mb, buffers));
- do_accept(decorator, ec);
-}
-
-template<class NextLayer, bool deflateSupported>
-template<class Body, class Allocator>
-void
-stream<NextLayer, deflateSupported>::
-accept(
- http::request<Body,
- http::basic_fields<Allocator>> const& req)
-{
- static_assert(is_sync_stream<next_layer_type>::value,
- "SyncStream requirements not met");
- error_code ec;
- accept(req, ec);
- if(ec)
- BOOST_THROW_EXCEPTION(system_error{ec});
-}
-
-template<class NextLayer, bool deflateSupported>
-template<
- class Body, class Allocator,
- class ResponseDecorator>
-void
-stream<NextLayer, deflateSupported>::
-accept_ex(
- http::request<Body,
- http::basic_fields<Allocator>> const& req,
- ResponseDecorator const& decorator)
-{
- static_assert(is_sync_stream<next_layer_type>::value,
- "SyncStream requirements not met");
- static_assert(detail::is_response_decorator<
- ResponseDecorator>::value,
- "ResponseDecorator requirements not met");
- error_code ec;
- accept_ex(req, decorator, ec);
- if(ec)
- BOOST_THROW_EXCEPTION(system_error{ec});
-}
-
-template<class NextLayer, bool deflateSupported>
-template<class Body, class Allocator>
-void
-stream<NextLayer, deflateSupported>::
-accept(
- http::request<Body,
- http::basic_fields<Allocator>> const& req,
- error_code& ec)
-{
- static_assert(is_sync_stream<next_layer_type>::value,
- "SyncStream requirements not met");
- reset();
- do_accept(req, &default_decorate_res, ec);
-}
-
-template<class NextLayer, bool deflateSupported>
-template<
- class Body, class Allocator,
- class ResponseDecorator>
-void
-stream<NextLayer, deflateSupported>::
-accept_ex(
- http::request<Body,
- http::basic_fields<Allocator>> const& req,
- ResponseDecorator const& decorator,
- error_code& ec)
-{
- static_assert(is_sync_stream<next_layer_type>::value,
- "SyncStream requirements not met");
- static_assert(detail::is_response_decorator<
- ResponseDecorator>::value,
- "ResponseDecorator requirements not met");
- reset();
- do_accept(req, decorator, ec);
-}
-
-//------------------------------------------------------------------------------
-
-template<class NextLayer, bool deflateSupported>
-template<
- class AcceptHandler>
-BOOST_ASIO_INITFN_RESULT_TYPE(
- AcceptHandler, void(error_code))
-stream<NextLayer, deflateSupported>::
-async_accept(
- AcceptHandler&& handler)
-{
- static_assert(is_async_stream<next_layer_type>::value,
- "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))>{
- std::move(init.completion_handler),
- *this,
- &default_decorate_res}({});
- return init.result.get();
-}
-
-template<class NextLayer, bool deflateSupported>
-template<
- class ResponseDecorator,
- class AcceptHandler>
-BOOST_ASIO_INITFN_RESULT_TYPE(
- AcceptHandler, void(error_code))
-stream<NextLayer, deflateSupported>::
-async_accept_ex(
- ResponseDecorator const& decorator,
- AcceptHandler&& handler)
-{
- static_assert(is_async_stream<next_layer_type>::value,
- "AsyncStream requirements not met");
- static_assert(detail::is_response_decorator<
- ResponseDecorator>::value,
- "ResponseDecorator requirements not met");
- BOOST_BEAST_HANDLER_INIT(
- AcceptHandler, void(error_code));
- reset();
- accept_op<
- ResponseDecorator,
- BOOST_ASIO_HANDLER_TYPE(
- AcceptHandler, void(error_code))>{
- std::move(init.completion_handler),
- *this,
- decorator}({});
- return init.result.get();
-}
-
-template<class NextLayer, bool deflateSupported>
-template<
- class ConstBufferSequence,
- class AcceptHandler>
-typename std::enable_if<
- ! http::detail::is_header<ConstBufferSequence>::value,
- BOOST_ASIO_INITFN_RESULT_TYPE(
- AcceptHandler, void(error_code))>::type
-stream<NextLayer, deflateSupported>::
-async_accept(
- ConstBufferSequence const& buffers,
- AcceptHandler&& handler)
-{
- static_assert(is_async_stream<next_layer_type>::value,
- "AsyncStream requirements not met");
- static_assert(boost::asio::is_const_buffer_sequence<
- ConstBufferSequence>::value,
- "ConstBufferSequence 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))>{
- std::move(init.completion_handler),
- *this,
- &default_decorate_res}.run(buffers);
- return init.result.get();
-}
-
-template<class NextLayer, bool deflateSupported>
-template<
- class ConstBufferSequence,
- class ResponseDecorator,
- class AcceptHandler>
-typename std::enable_if<
- ! http::detail::is_header<ConstBufferSequence>::value,
- BOOST_ASIO_INITFN_RESULT_TYPE(
- AcceptHandler, void(error_code))>::type
-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 not met");
- static_assert(boost::asio::is_const_buffer_sequence<
- ConstBufferSequence>::value,
- "ConstBufferSequence requirements not met");
- static_assert(detail::is_response_decorator<
- ResponseDecorator>::value,
- "ResponseDecorator requirements not met");
- BOOST_BEAST_HANDLER_INIT(
- AcceptHandler, void(error_code));
- reset();
- accept_op<
- ResponseDecorator,
- BOOST_ASIO_HANDLER_TYPE(
- AcceptHandler, void(error_code))>{
- std::move(init.completion_handler),
- *this,
- decorator}.run(buffers);
- return init.result.get();
-}
-
-template<class NextLayer, bool deflateSupported>
-template<
- class Body, class Allocator,
- class AcceptHandler>
-BOOST_ASIO_INITFN_RESULT_TYPE(
- AcceptHandler, void(error_code))
-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 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))>{
- std::move(init.completion_handler),
- *this,
- req,
- &default_decorate_res}();
- return init.result.get();
-}
-
-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, 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 not met");
- static_assert(detail::is_response_decorator<
- ResponseDecorator>::value,
- "ResponseDecorator 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))>{
- std::move(init.completion_handler),
- *this,
- req,
- decorator}();
- return init.result.get();
-}
-
-//------------------------------------------------------------------------------
-
-template<class NextLayer, bool deflateSupported>
-template<class Decorator>
-void
-stream<NextLayer, deflateSupported>::
-do_accept(
- Decorator const& decorator,
- error_code& ec)
-{
- http::request_parser<http::empty_body> p;
- http::read(next_layer(), rd_buf_, p, ec);
- if(ec == http::error::end_of_stream)
- ec = error::closed;
- if(ec)
- return;
- do_accept(p.get(), decorator, ec);
-}
-
-template<class NextLayer, bool deflateSupported>
-template<class Body, class Allocator,
- class Decorator>
-void
-stream<NextLayer, deflateSupported>::
-do_accept(
- http::request<Body,
- http::basic_fields<Allocator>> const& req,
- Decorator const& decorator,
- error_code& ec)
-{
- error_code result;
- auto const res = build_response(req, decorator, result);
- http::write(stream_, res, ec);
- if(ec)
- return;
- ec = result;
- if(ec)
- {
- // VFALCO TODO Respect keep alive setting, perform
- // teardown if Connection: close.
- return;
- }
- do_pmd_config(res, is_deflate_supported{});
- open(role_type::server);
-}
-
-} // websocket
-} // beast
-} // boost
-
-#endif
diff --git a/boost/beast/websocket/impl/close.hpp b/boost/beast/websocket/impl/close.hpp
new file mode 100644
index 0000000000..03078d9232
--- /dev/null
+++ b/boost/beast/websocket/impl/close.hpp
@@ -0,0 +1,405 @@
+//
+// Copyright (c) 2016-2019 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_IMPL_CLOSE_HPP
+#define BOOST_BEAST_WEBSOCKET_IMPL_CLOSE_HPP
+
+#include <boost/beast/websocket/teardown.hpp>
+#include <boost/beast/websocket/detail/mask.hpp>
+#include <boost/beast/websocket/impl/stream_impl.hpp>
+#include <boost/beast/core/async_base.hpp>
+#include <boost/beast/core/flat_static_buffer.hpp>
+#include <boost/beast/core/stream_traits.hpp>
+#include <boost/beast/core/detail/bind_continuation.hpp>
+#include <boost/asio/coroutine.hpp>
+#include <boost/asio/post.hpp>
+#include <boost/throw_exception.hpp>
+#include <memory>
+
+namespace boost {
+namespace beast {
+namespace websocket {
+
+/* Close the WebSocket Connection
+
+ This composed operation sends the close frame if it hasn't already
+ been sent, then reads and discards frames until receiving a close
+ frame. Finally it invokes the teardown operation to shut down the
+ underlying connection.
+*/
+template<class NextLayer, bool deflateSupported>
+template<class Handler>
+class stream<NextLayer, deflateSupported>::close_op
+ : public beast::stable_async_base<
+ Handler, beast::executor_type<stream>>
+ , public net::coroutine
+{
+ boost::weak_ptr<impl_type> wp_;
+ error_code ev_;
+ detail::frame_buffer& fb_;
+
+public:
+ static constexpr int id = 5; // for soft_mutex
+
+ template<class Handler_>
+ close_op(
+ Handler_&& h,
+ boost::shared_ptr<impl_type> const& sp,
+ close_reason const& cr)
+ : stable_async_base<Handler,
+ beast::executor_type<stream>>(
+ std::forward<Handler_>(h),
+ sp->stream().get_executor())
+ , wp_(sp)
+ , fb_(beast::allocate_stable<
+ detail::frame_buffer>(*this))
+ {
+ // Serialize the close frame
+ sp->template write_close<
+ flat_static_buffer_base>(fb_, cr);
+ (*this)({}, 0, false);
+ }
+
+ void
+ operator()(
+ error_code ec = {},
+ std::size_t bytes_transferred = 0,
+ bool cont = true)
+ {
+ using beast::detail::clamp;
+ auto sp = wp_.lock();
+ if(! sp)
+ {
+ ec = net::error::operation_aborted;
+ return this->complete(cont, ec);
+ }
+ auto& impl = *sp;
+ BOOST_ASIO_CORO_REENTER(*this)
+ {
+ // Acquire the write lock
+ if(! impl.wr_block.try_lock(this))
+ {
+ BOOST_ASIO_CORO_YIELD
+ impl.op_close.emplace(std::move(*this));
+ impl.wr_block.lock(this);
+ BOOST_ASIO_CORO_YIELD
+ net::post(std::move(*this));
+ BOOST_ASSERT(impl.wr_block.is_locked(this));
+ }
+ if(impl.check_stop_now(ec))
+ goto upcall;
+
+ // Can't call close twice
+ // TODO return a custom error code
+ BOOST_ASSERT(! impl.wr_close);
+
+ // Send close frame
+ impl.wr_close = true;
+ impl.change_status(status::closing);
+ impl.update_timer(this->get_executor());
+ BOOST_ASIO_CORO_YIELD
+ net::async_write(impl.stream(), fb_.data(),
+ beast::detail::bind_continuation(std::move(*this)));
+ if(impl.check_stop_now(ec))
+ goto upcall;
+
+ if(impl.rd_close)
+ {
+ // This happens when the read_op gets a close frame
+ // at the same time close_op is sending the close frame.
+ // The read_op will be suspended on the write block.
+ goto teardown;
+ }
+
+ // Acquire the read lock
+ if(! impl.rd_block.try_lock(this))
+ {
+ BOOST_ASIO_CORO_YIELD
+ impl.op_r_close.emplace(std::move(*this));
+ impl.rd_block.lock(this);
+ BOOST_ASIO_CORO_YIELD
+ net::post(std::move(*this));
+ BOOST_ASSERT(impl.rd_block.is_locked(this));
+ if(impl.check_stop_now(ec))
+ goto upcall;
+ BOOST_ASSERT(! impl.rd_close);
+ }
+
+ // Read until a receiving a close frame
+ // TODO There should be a timeout on this
+ if(impl.rd_remain > 0)
+ goto read_payload;
+ for(;;)
+ {
+ // Read frame header
+ while(! impl.parse_fh(
+ impl.rd_fh, impl.rd_buf, ev_))
+ {
+ if(ev_)
+ goto teardown;
+ BOOST_ASIO_CORO_YIELD
+ impl.stream().async_read_some(
+ impl.rd_buf.prepare(read_size(
+ impl.rd_buf, impl.rd_buf.max_size())),
+ beast::detail::bind_continuation(std::move(*this)));
+ impl.rd_buf.commit(bytes_transferred);
+ if(impl.check_stop_now(ec))
+ goto upcall;
+ }
+ if(detail::is_control(impl.rd_fh.op))
+ {
+ // Discard ping or pong frame
+ if(impl.rd_fh.op != detail::opcode::close)
+ {
+ impl.rd_buf.consume(clamp(impl.rd_fh.len));
+ continue;
+ }
+
+ // Process close frame
+ // TODO Should we invoke the control callback?
+ BOOST_ASSERT(! impl.rd_close);
+ impl.rd_close = true;
+ auto const mb = buffers_prefix(
+ clamp(impl.rd_fh.len),
+ impl.rd_buf.data());
+ if(impl.rd_fh.len > 0 && impl.rd_fh.mask)
+ detail::mask_inplace(mb, impl.rd_key);
+ detail::read_close(impl.cr, mb, ev_);
+ if(ev_)
+ goto teardown;
+ impl.rd_buf.consume(clamp(impl.rd_fh.len));
+ goto teardown;
+ }
+
+ read_payload:
+ // Discard message frame
+ while(impl.rd_buf.size() < impl.rd_remain)
+ {
+ impl.rd_remain -= impl.rd_buf.size();
+ impl.rd_buf.consume(impl.rd_buf.size());
+ BOOST_ASIO_CORO_YIELD
+ impl.stream().async_read_some(
+ impl.rd_buf.prepare(read_size(
+ impl.rd_buf, impl.rd_buf.max_size())),
+ beast::detail::bind_continuation(std::move(*this)));
+ impl.rd_buf.commit(bytes_transferred);
+ if(impl.check_stop_now(ec))
+ goto upcall;
+ }
+ BOOST_ASSERT(impl.rd_buf.size() >= impl.rd_remain);
+ impl.rd_buf.consume(clamp(impl.rd_remain));
+ impl.rd_remain = 0;
+ }
+
+ teardown:
+ // Teardown
+ BOOST_ASSERT(impl.wr_block.is_locked(this));
+ using beast::websocket::async_teardown;
+ BOOST_ASIO_CORO_YIELD
+ async_teardown(impl.role, impl.stream(),
+ beast::detail::bind_continuation(std::move(*this)));
+ BOOST_ASSERT(impl.wr_block.is_locked(this));
+ if(ec == net::error::eof)
+ {
+ // Rationale:
+ // http://stackoverflow.com/questions/25587403/boost-asio-ssl-async-shutdown-always-finishes-with-an-error
+ ec = {};
+ }
+ if(! ec)
+ ec = ev_;
+ if(ec)
+ impl.change_status(status::failed);
+ else
+ impl.change_status(status::closed);
+ impl.close();
+
+ upcall:
+ impl.wr_block.unlock(this);
+ impl.rd_block.try_unlock(this)
+ && impl.op_r_rd.maybe_invoke();
+ impl.op_rd.maybe_invoke()
+ || impl.op_idle_ping.maybe_invoke()
+ || impl.op_ping.maybe_invoke()
+ || impl.op_wr.maybe_invoke();
+ this->complete(cont, ec);
+ }
+ }
+};
+
+template<class NextLayer, bool deflateSupported>
+struct stream<NextLayer, deflateSupported>::
+ run_close_op
+{
+ template<class CloseHandler>
+ void
+ operator()(
+ CloseHandler&& h,
+ boost::shared_ptr<impl_type> const& sp,
+ close_reason const& cr)
+ {
+ // If you get an error on the following line it means
+ // that your handler does not meet the documented type
+ // requirements for the handler.
+
+ static_assert(
+ beast::detail::is_invocable<CloseHandler,
+ void(error_code)>::value,
+ "CloseHandler type requirements not met");
+
+ close_op<
+ typename std::decay<CloseHandler>::type>(
+ std::forward<CloseHandler>(h),
+ sp,
+ cr);
+ }
+};
+
+//------------------------------------------------------------------------------
+
+template<class NextLayer, bool deflateSupported>
+void
+stream<NextLayer, deflateSupported>::
+close(close_reason const& cr)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream type requirements not met");
+ error_code ec;
+ close(cr, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+}
+
+template<class NextLayer, bool deflateSupported>
+void
+stream<NextLayer, deflateSupported>::
+close(close_reason const& cr, error_code& ec)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream type requirements not met");
+ using beast::detail::clamp;
+ auto& impl = *impl_;
+ ec = {};
+ if(impl.check_stop_now(ec))
+ return;
+ BOOST_ASSERT(! impl.rd_close);
+
+ // Can't call close twice
+ // TODO return a custom error code
+ BOOST_ASSERT(! impl.wr_close);
+
+ // Send close frame
+ {
+ impl.wr_close = true;
+ impl.change_status(status::closing);
+ detail::frame_buffer fb;
+ impl.template write_close<flat_static_buffer_base>(fb, cr);
+ net::write(impl.stream(), fb.data(), ec);
+ if(impl.check_stop_now(ec))
+ return;
+ }
+
+ // Read until a receiving a close frame
+ error_code ev;
+ if(impl.rd_remain > 0)
+ goto read_payload;
+ for(;;)
+ {
+ // Read frame header
+ while(! impl.parse_fh(
+ impl.rd_fh, impl.rd_buf, ev))
+ {
+ if(ev)
+ {
+ // Protocol violation
+ return do_fail(close_code::none, ev, ec);
+ }
+ impl.rd_buf.commit(impl.stream().read_some(
+ impl.rd_buf.prepare(read_size(
+ impl.rd_buf, impl.rd_buf.max_size())), ec));
+ if(impl.check_stop_now(ec))
+ return;
+ }
+
+ if(detail::is_control(impl.rd_fh.op))
+ {
+ // Discard ping/pong frame
+ if(impl.rd_fh.op != detail::opcode::close)
+ {
+ impl.rd_buf.consume(clamp(impl.rd_fh.len));
+ continue;
+ }
+
+ // Handle close frame
+ // TODO Should we invoke the control callback?
+ BOOST_ASSERT(! impl.rd_close);
+ impl.rd_close = true;
+ auto const mb = buffers_prefix(
+ clamp(impl.rd_fh.len),
+ impl.rd_buf.data());
+ if(impl.rd_fh.len > 0 && impl.rd_fh.mask)
+ detail::mask_inplace(mb, impl.rd_key);
+ detail::read_close(impl.cr, mb, ev);
+ if(ev)
+ {
+ // Protocol violation
+ return do_fail(close_code::none, ev, ec);
+ }
+ impl.rd_buf.consume(clamp(impl.rd_fh.len));
+ break;
+ }
+
+ read_payload:
+ // Discard message frame
+ while(impl.rd_buf.size() < impl.rd_remain)
+ {
+ impl.rd_remain -= impl.rd_buf.size();
+ impl.rd_buf.consume(impl.rd_buf.size());
+ impl.rd_buf.commit(
+ impl.stream().read_some(
+ impl.rd_buf.prepare(
+ read_size(
+ impl.rd_buf,
+ impl.rd_buf.max_size())),
+ ec));
+ if(impl.check_stop_now(ec))
+ return;
+ }
+ BOOST_ASSERT(
+ impl.rd_buf.size() >= impl.rd_remain);
+ impl.rd_buf.consume(clamp(impl.rd_remain));
+ impl.rd_remain = 0;
+ }
+ // _Close the WebSocket Connection_
+ do_fail(close_code::none, error::closed, ec);
+ if(ec == error::closed)
+ ec = {};
+}
+
+template<class NextLayer, bool deflateSupported>
+template<class CloseHandler>
+BOOST_BEAST_ASYNC_RESULT1(CloseHandler)
+stream<NextLayer, deflateSupported>::
+async_close(close_reason const& cr, CloseHandler&& handler)
+{
+ static_assert(is_async_stream<next_layer_type>::value,
+ "AsyncStream type requirements not met");
+ return net::async_initiate<
+ CloseHandler,
+ void(error_code)>(
+ run_close_op{},
+ handler,
+ impl_,
+ cr);
+}
+
+} // websocket
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/websocket/impl/close.ipp b/boost/beast/websocket/impl/close.ipp
deleted file mode 100644
index 62ddffa23d..0000000000
--- a/boost/beast/websocket/impl/close.ipp
+++ /dev/null
@@ -1,447 +0,0 @@
-//
-// 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_IMPL_CLOSE_IPP
-#define BOOST_BEAST_WEBSOCKET_IMPL_CLOSE_IPP
-
-#include <boost/beast/websocket/teardown.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/config.hpp>
-#include <boost/asio/associated_allocator.hpp>
-#include <boost/asio/associated_executor.hpp>
-#include <boost/asio/coroutine.hpp>
-#include <boost/asio/executor_work_guard.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>
-
-namespace boost {
-namespace beast {
-namespace websocket {
-
-/* Close the WebSocket Connection
-
- This composed operation sends the close frame if it hasn't already
- been sent, then reads and discards frames until receiving a close
- frame. Finally it invokes the teardown operation to shut down the
- underlying connection.
-*/
-template<class NextLayer, bool deflateSupported>
-template<class Handler>
-class stream<NextLayer, deflateSupported>::close_op
- : public boost::asio::coroutine
-{
- struct state
- {
- stream<NextLayer, deflateSupported>& ws;
- boost::asio::executor_work_guard<decltype(std::declval<
- stream<NextLayer, deflateSupported>&>().get_executor())> wg;
- detail::frame_buffer fb;
- error_code ev;
- bool cont = false;
-
- state(
- Handler const&,
- stream<NextLayer, deflateSupported>& ws_,
- close_reason const& cr)
- : ws(ws_)
- , wg(ws.get_executor())
- {
- // Serialize the close frame
- ws.template write_close<
- flat_static_buffer_base>(fb, cr);
- }
- };
-
- handler_ptr<state, Handler> d_;
-
-public:
- static constexpr int id = 4; // for soft_mutex
-
- close_op(close_op&&) = default;
- close_op(close_op const&) = delete;
-
- template<class DeducedHandler>
- close_op(
- DeducedHandler&& h,
- stream<NextLayer, deflateSupported>& ws,
- close_reason const& cr)
- : d_(std::forward<DeducedHandler>(h), ws, cr)
- {
- }
-
- using allocator_type =
- boost::asio::associated_allocator_t<Handler>;
-
- allocator_type
- get_allocator() const noexcept
- {
- return (boost::asio::get_associated_allocator)(d_.handler());
- }
-
- using executor_type = boost::asio::associated_executor_t<
- Handler, decltype(std::declval<stream<NextLayer, deflateSupported>&>().get_executor())>;
-
- executor_type
- get_executor() const noexcept
- {
- return (boost::asio::get_associated_executor)(
- d_.handler(), d_->ws.get_executor());
- }
-
- void
- operator()(
- error_code ec = {},
- std::size_t bytes_transferred = 0,
- bool cont = true);
-
- friend
- bool asio_handler_is_continuation(close_op* op)
- {
- using boost::asio::asio_handler_is_continuation;
- 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, bool deflateSupported>
-template<class Handler>
-void
-stream<NextLayer, deflateSupported>::
-close_op<Handler>::
-operator()(
- error_code ec,
- std::size_t bytes_transferred,
- bool cont)
-{
- using beast::detail::clamp;
- auto& d = *d_;
- d.cont = cont;
- BOOST_ASIO_CORO_REENTER(*this)
- {
- // Attempt to acquire write block
- if(! d.ws.wr_block_.try_lock(this))
- {
- // Suspend
- BOOST_ASIO_CORO_YIELD
- d.ws.paused_close_.emplace(std::move(*this));
-
- // Acquire the write block
- 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_.is_locked(this));
- }
-
- // Make sure the stream is open
- if(! d.ws.check_open(ec))
- goto upcall;
-
- // Can't call close twice
- BOOST_ASSERT(! d.ws.wr_close_);
-
- // Change status to closing
- BOOST_ASSERT(d.ws.status_ == status::open);
- d.ws.status_ = status::closing;
-
- // Send close frame
- d.ws.wr_close_ = true;
- BOOST_ASIO_CORO_YIELD
- boost::asio::async_write(d.ws.stream_,
- d.fb.data(), std::move(*this));
- if(! d.ws.check_ok(ec))
- goto upcall;
-
- if(d.ws.rd_close_)
- {
- // This happens when the read_op gets a close frame
- // at the same time close_op is sending the close frame.
- // The read_op will be suspended on the write block.
- goto teardown;
- }
-
- // Maybe suspend
- if(! d.ws.rd_block_.try_lock(this))
- {
- // Suspend
- BOOST_ASIO_CORO_YIELD
- d.ws.paused_r_close_.emplace(std::move(*this));
-
- // Acquire the read block
- 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_.is_locked(this));
-
- // Make sure the stream is open
- BOOST_ASSERT(d.ws.status_ != status::open);
- BOOST_ASSERT(d.ws.status_ != status::closed);
- if( d.ws.status_ == status::failed)
- goto upcall;
-
- BOOST_ASSERT(! d.ws.rd_close_);
- }
-
- // Drain
- if(d.ws.rd_remain_ > 0)
- goto read_payload;
- for(;;)
- {
- // Read frame header
- while(! d.ws.parse_fh(
- d.ws.rd_fh_, d.ws.rd_buf_, d.ev))
- {
- 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_,
- d.ws.rd_buf_.max_size())),
- std::move(*this));
- if(! d.ws.check_ok(ec))
- goto upcall;
- d.ws.rd_buf_.commit(bytes_transferred);
- }
- if(detail::is_control(d.ws.rd_fh_.op))
- {
- // Process control frame
- if(d.ws.rd_fh_.op == detail::opcode::close)
- {
- BOOST_ASSERT(! d.ws.rd_close_);
- d.ws.rd_close_ = true;
- auto const mb = buffers_prefix(
- clamp(d.ws.rd_fh_.len),
- 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, d.ev);
- if(d.ev)
- goto teardown;
- d.ws.rd_buf_.consume(clamp(d.ws.rd_fh_.len));
- goto teardown;
- }
- d.ws.rd_buf_.consume(clamp(d.ws.rd_fh_.len));
- }
- else
- {
- read_payload:
- while(d.ws.rd_buf_.size() < d.ws.rd_remain_)
- {
- d.ws.rd_remain_ -= d.ws.rd_buf_.size();
- d.ws.rd_buf_.consume(d.ws.rd_buf_.size());
- BOOST_ASIO_CORO_YIELD
- d.ws.stream_.async_read_some(
- d.ws.rd_buf_.prepare(read_size(d.ws.rd_buf_,
- d.ws.rd_buf_.max_size())),
- std::move(*this));
- if(! d.ws.check_ok(ec))
- goto upcall;
- d.ws.rd_buf_.commit(bytes_transferred);
- }
- BOOST_ASSERT(d.ws.rd_buf_.size() >= d.ws.rd_remain_);
- d.ws.rd_buf_.consume(clamp(d.ws.rd_remain_));
- d.ws.rd_remain_ = 0;
- }
- }
-
- teardown:
- // Teardown
- 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_.is_locked(this));
- if(ec == boost::asio::error::eof)
- {
- // Rationale:
- // http://stackoverflow.com/questions/25587403/boost-asio-ssl-async-shutdown-always-finishes-with-an-error
- ec.assign(0, ec.category());
- }
- if(! ec)
- ec = d.ev;
- if(ec)
- d.ws.status_ = status::failed;
- else
- d.ws.status_ = status::closed;
- d.ws.close();
-
- upcall:
- 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();
- if(! d.cont)
- {
- BOOST_ASIO_CORO_YIELD
- boost::asio::post(
- d.ws.get_executor(),
- bind_handler(std::move(*this), ec));
- }
- {
- auto wg = std::move(d.wg);
- d_.invoke(ec);
- }
- }
-}
-
-//------------------------------------------------------------------------------
-
-template<class NextLayer, bool deflateSupported>
-void
-stream<NextLayer, deflateSupported>::
-close(close_reason const& cr)
-{
- static_assert(is_sync_stream<next_layer_type>::value,
- "SyncStream requirements not met");
- error_code ec;
- close(cr, ec);
- if(ec)
- BOOST_THROW_EXCEPTION(system_error{ec});
-}
-
-template<class NextLayer, bool deflateSupported>
-void
-stream<NextLayer, deflateSupported>::
-close(close_reason const& cr, error_code& ec)
-{
- static_assert(is_sync_stream<next_layer_type>::value,
- "SyncStream requirements not met");
- using beast::detail::clamp;
- ec.assign(0, ec.category());
- // Make sure the stream is open
- if(! check_open(ec))
- return;
- // If rd_close_ is set then we already sent a close
- BOOST_ASSERT(! rd_close_);
- BOOST_ASSERT(! wr_close_);
- wr_close_ = true;
- {
- detail::frame_buffer fb;
- write_close<flat_static_buffer_base>(fb, cr);
- boost::asio::write(stream_, fb.data(), ec);
- }
- if(! check_ok(ec))
- return;
- status_ = status::closing;
- error_code result;
- // Drain the connection
- if(rd_remain_ > 0)
- goto read_payload;
- for(;;)
- {
- // Read frame header
- while(! parse_fh(rd_fh_, rd_buf_, result))
- {
- if(result)
- return do_fail(
- close_code::none, result, ec);
- auto const bytes_transferred =
- stream_.read_some(
- rd_buf_.prepare(read_size(rd_buf_,
- rd_buf_.max_size())), ec);
- if(! check_ok(ec))
- return;
- rd_buf_.commit(bytes_transferred);
- }
- if(detail::is_control(rd_fh_.op))
- {
- // Process control frame
- if(rd_fh_.op == detail::opcode::close)
- {
- BOOST_ASSERT(! rd_close_);
- rd_close_ = true;
- auto const mb = buffers_prefix(
- clamp(rd_fh_.len),
- rd_buf_.mutable_data());
- if(rd_fh_.len > 0 && rd_fh_.mask)
- detail::mask_inplace(mb, rd_key_);
- detail::read_close(cr_, mb, result);
- if(result)
- {
- // Protocol violation
- return do_fail(
- close_code::none, result, ec);
- }
- rd_buf_.consume(clamp(rd_fh_.len));
- break;
- }
- rd_buf_.consume(clamp(rd_fh_.len));
- }
- else
- {
- read_payload:
- while(rd_buf_.size() < rd_remain_)
- {
- rd_remain_ -= rd_buf_.size();
- rd_buf_.consume(rd_buf_.size());
- auto const bytes_transferred =
- stream_.read_some(
- rd_buf_.prepare(read_size(rd_buf_,
- rd_buf_.max_size())), ec);
- if(! check_ok(ec))
- return;
- rd_buf_.commit(bytes_transferred);
- }
- BOOST_ASSERT(rd_buf_.size() >= rd_remain_);
- rd_buf_.consume(clamp(rd_remain_));
- rd_remain_ = 0;
- }
- }
- // _Close the WebSocket Connection_
- do_fail(close_code::none, error::closed, ec);
- if(ec == error::closed)
- ec.assign(0, ec.category());
-}
-
-template<class NextLayer, bool deflateSupported>
-template<class CloseHandler>
-BOOST_ASIO_INITFN_RESULT_TYPE(
- CloseHandler, void(error_code))
-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_BEAST_HANDLER_INIT(
- CloseHandler, void(error_code));
- close_op<BOOST_ASIO_HANDLER_TYPE(
- CloseHandler, void(error_code))>{
- std::move(init.completion_handler), *this, cr}(
- {}, 0, false);
- return init.result.get();
-}
-
-} // websocket
-} // beast
-} // boost
-
-#endif
diff --git a/boost/beast/websocket/impl/error.hpp b/boost/beast/websocket/impl/error.hpp
new file mode 100644
index 0000000000..1f3a4e7ac6
--- /dev/null
+++ b/boost/beast/websocket/impl/error.hpp
@@ -0,0 +1,44 @@
+//
+// Copyright (c) 2016-2019 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_IMPL_ERROR_HPP
+#define BOOST_BEAST_WEBSOCKET_IMPL_ERROR_HPP
+
+namespace boost {
+namespace system {
+template<>
+struct is_error_code_enum<::boost::beast::websocket::error>
+{
+ static bool const value = true;
+};
+template<>
+struct is_error_condition_enum<::boost::beast::websocket::condition>
+{
+ static bool const value = true;
+};
+} // system
+} // boost
+
+namespace boost {
+namespace beast {
+namespace websocket {
+
+BOOST_BEAST_DECL
+error_code
+make_error_code(error e);
+
+BOOST_BEAST_DECL
+error_condition
+make_error_condition(condition c);
+
+} // websocket
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/websocket/impl/error.ipp b/boost/beast/websocket/impl/error.ipp
index 56a20e2551..68296dc47b 100644
--- a/boost/beast/websocket/impl/error.ipp
+++ b/boost/beast/websocket/impl/error.ipp
@@ -1,5 +1,5 @@
//
-// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+// Copyright (c) 2016-2019 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)
@@ -10,134 +10,133 @@
#ifndef BOOST_BEAST_WEBSOCKET_IMPL_ERROR_IPP
#define BOOST_BEAST_WEBSOCKET_IMPL_ERROR_IPP
+#include <boost/beast/websocket/error.hpp>
+
namespace boost {
namespace beast {
namespace websocket {
namespace detail {
-inline
-const char*
-error_codes::
-name() const noexcept
+class error_codes : public error_category
{
- return "boost.beast.websocket";
-}
+public:
+ const char*
+ name() const noexcept override
+ {
+ return "boost.beast.websocket";
+ }
-inline
-std::string
-error_codes::
-message(int ev) const
-{
- switch(static_cast<error>(ev))
+ std::string
+ message(int ev) const override
{
- 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";
-
- 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";
-
- 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";
+ switch(static_cast<error>(ev))
+ {
+ 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";
+
+ 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";
+
+ 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";
+ }
}
-}
-inline
-error_condition
-error_codes::
-default_error_condition(int ev) const noexcept
-{
- switch(static_cast<error>(ev))
+ error_condition
+ default_error_condition(int ev) const noexcept override
{
- 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;
+ switch(static_cast<error>(ev))
+ {
+ 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
+class error_conditions : public error_category
{
- return "boost.beast.websocket";
-}
+public:
+ const char*
+ name() const noexcept override
+ {
+ return "boost.beast.websocket";
+ }
-inline
-std::string
-error_conditions::
-message(int cv) const
-{
- switch(static_cast<condition>(cv))
+ std::string
+ message(int cv) const override
{
- default:
- case condition::handshake_failed: return "The WebSocket handshake failed";
- case condition::protocol_violation: return "A WebSocket protocol violation occurred";
+ 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
-inline
error_code
make_error_code(error e)
{
@@ -146,7 +145,6 @@ make_error_code(error e)
std::underlying_type<error>::type>(e), cat};
}
-inline
error_condition
make_error_condition(condition c)
{
diff --git a/boost/beast/websocket/impl/handshake.hpp b/boost/beast/websocket/impl/handshake.hpp
new file mode 100644
index 0000000000..21fba99738
--- /dev/null
+++ b/boost/beast/websocket/impl/handshake.hpp
@@ -0,0 +1,540 @@
+//
+// Copyright (c) 2016-2019 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_IMPL_HANDSHAKE_HPP
+#define BOOST_BEAST_WEBSOCKET_IMPL_HANDSHAKE_HPP
+
+#include <boost/beast/websocket/impl/stream_impl.hpp>
+#include <boost/beast/websocket/detail/type_traits.hpp>
+#include <boost/beast/http/empty_body.hpp>
+#include <boost/beast/http/message.hpp>
+#include <boost/beast/http/read.hpp>
+#include <boost/beast/http/write.hpp>
+#include <boost/beast/core/async_base.hpp>
+#include <boost/beast/core/flat_buffer.hpp>
+#include <boost/beast/core/stream_traits.hpp>
+#include <boost/asio/coroutine.hpp>
+#include <boost/assert.hpp>
+#include <boost/throw_exception.hpp>
+#include <memory>
+
+namespace boost {
+namespace beast {
+namespace websocket {
+
+//------------------------------------------------------------------------------
+
+// send the upgrade request and process the response
+//
+template<class NextLayer, bool deflateSupported>
+template<class Handler>
+class stream<NextLayer, deflateSupported>::handshake_op
+ : public beast::stable_async_base<Handler,
+ beast::executor_type<stream>>
+ , public net::coroutine
+{
+ struct data
+ {
+ // VFALCO This really should be two separate
+ // composed operations, to save on memory
+ request_type req;
+ http::response_parser<
+ typename response_type::body_type> p;
+ flat_buffer fb;
+ bool overflow = false; // could be a member of the op
+
+ explicit
+ data(request_type&& req_)
+ : req(std::move(req_))
+ {
+ }
+ };
+
+ boost::weak_ptr<impl_type> wp_;
+ detail::sec_ws_key_type key_;
+ response_type* res_p_;
+ data& d_;
+
+public:
+ template<class Handler_>
+ handshake_op(
+ Handler_&& h,
+ boost::shared_ptr<impl_type> const& sp,
+ request_type&& req,
+ detail::sec_ws_key_type key,
+ response_type* res_p)
+ : stable_async_base<Handler,
+ beast::executor_type<stream>>(
+ std::forward<Handler_>(h),
+ sp->stream().get_executor())
+ , wp_(sp)
+ , key_(key)
+ , res_p_(res_p)
+ , d_(beast::allocate_stable<data>(
+ *this, std::move(req)))
+ {
+ sp->reset(); // VFALCO I don't like this
+ (*this)({}, 0, false);
+ }
+
+ void
+ operator()(
+ error_code ec = {},
+ std::size_t bytes_used = 0,
+ bool cont = true)
+ {
+ boost::ignore_unused(bytes_used);
+ auto sp = wp_.lock();
+ if(! sp)
+ {
+ ec = net::error::operation_aborted;
+ return this->complete(cont, ec);
+ }
+ auto& impl = *sp;
+ BOOST_ASIO_CORO_REENTER(*this)
+ {
+ impl.change_status(status::handshake);
+ impl.update_timer(this->get_executor());
+
+ // write HTTP request
+ impl.do_pmd_config(d_.req);
+ BOOST_ASIO_CORO_YIELD
+ http::async_write(impl.stream(),
+ d_.req, std::move(*this));
+ if(impl.check_stop_now(ec))
+ goto upcall;
+
+ // read HTTP response
+ BOOST_ASIO_CORO_YIELD
+ http::async_read(impl.stream(),
+ impl.rd_buf, d_.p,
+ std::move(*this));
+ if(ec == http::error::buffer_overflow)
+ {
+ // If the response overflows the internal
+ // read buffer, switch to a dynamically
+ // allocated flat buffer.
+
+ d_.fb.commit(net::buffer_copy(
+ d_.fb.prepare(impl.rd_buf.size()),
+ impl.rd_buf.data()));
+ impl.rd_buf.clear();
+
+ BOOST_ASIO_CORO_YIELD
+ http::async_read(impl.stream(),
+ d_.fb, d_.p, std::move(*this));
+
+ if(! ec)
+ {
+ // Copy any leftovers back into the read
+ // buffer, since this represents websocket
+ // frame data.
+
+ if(d_.fb.size() <= impl.rd_buf.capacity())
+ {
+ impl.rd_buf.commit(net::buffer_copy(
+ impl.rd_buf.prepare(d_.fb.size()),
+ d_.fb.data()));
+ }
+ else
+ {
+ ec = http::error::buffer_overflow;
+ }
+ }
+
+ // Do this before the upcall
+ d_.fb.clear();
+ }
+ if(impl.check_stop_now(ec))
+ goto upcall;
+
+ // success
+ impl.reset_idle();
+ impl.on_response(d_.p.get(), key_, ec);
+ if(res_p_)
+ swap(d_.p.get(), *res_p_);
+
+ upcall:
+ this->complete(cont ,ec);
+ }
+ }
+};
+
+template<class NextLayer, bool deflateSupported>
+struct stream<NextLayer, deflateSupported>::
+ run_handshake_op
+{
+ template<class HandshakeHandler>
+ void operator()(
+ HandshakeHandler&& h,
+ boost::shared_ptr<impl_type> const& sp,
+ request_type&& req,
+ detail::sec_ws_key_type key,
+ response_type* res_p)
+ {
+ // If you get an error on the following line it means
+ // that your handler does not meet the documented type
+ // requirements for the handler.
+
+ static_assert(
+ beast::detail::is_invocable<HandshakeHandler,
+ void(error_code)>::value,
+ "HandshakeHandler type requirements not met");
+
+ handshake_op<
+ typename std::decay<HandshakeHandler>::type>(
+ std::forward<HandshakeHandler>(h),
+ sp, std::move(req), key, res_p);
+ }
+};
+
+//------------------------------------------------------------------------------
+
+template<class NextLayer, bool deflateSupported>
+template<class RequestDecorator>
+void
+stream<NextLayer, deflateSupported>::
+do_handshake(
+ response_type* res_p,
+ string_view host,
+ string_view target,
+ RequestDecorator const& decorator,
+ error_code& ec)
+{
+ auto& impl = *impl_;
+ impl.change_status(status::handshake);
+ impl.reset();
+ detail::sec_ws_key_type key;
+ {
+ auto const req = impl.build_request(
+ key, host, target, decorator);
+ impl.do_pmd_config(req);
+ http::write(impl.stream(), req, ec);
+ }
+ if(impl.check_stop_now(ec))
+ return;
+ http::response_parser<
+ typename response_type::body_type> p;
+ http::read(next_layer(), impl.rd_buf, p, ec);
+ if(ec == http::error::buffer_overflow)
+ {
+ // If the response overflows the internal
+ // read buffer, switch to a dynamically
+ // allocated flat buffer.
+
+ flat_buffer fb;
+ fb.commit(net::buffer_copy(
+ fb.prepare(impl.rd_buf.size()),
+ impl.rd_buf.data()));
+ impl.rd_buf.clear();
+
+ http::read(next_layer(), fb, p, ec);;
+
+ if(! ec)
+ {
+ // Copy any leftovers back into the read
+ // buffer, since this represents websocket
+ // frame data.
+
+ if(fb.size() <= impl.rd_buf.capacity())
+ {
+ impl.rd_buf.commit(net::buffer_copy(
+ impl.rd_buf.prepare(fb.size()),
+ fb.data()));
+ }
+ else
+ {
+ ec = http::error::buffer_overflow;
+ }
+ }
+ }
+ if(impl.check_stop_now(ec))
+ return;
+
+ impl.on_response(p.get(), key, ec);
+ if(impl.check_stop_now(ec))
+ return;
+
+ if(res_p)
+ *res_p = p.release();
+}
+
+//------------------------------------------------------------------------------
+
+template<class NextLayer, bool deflateSupported>
+template<class HandshakeHandler>
+BOOST_BEAST_ASYNC_RESULT1(HandshakeHandler)
+stream<NextLayer, deflateSupported>::
+async_handshake(
+ string_view host,
+ string_view target,
+ HandshakeHandler&& handler)
+{
+ static_assert(is_async_stream<next_layer_type>::value,
+ "AsyncStream type requirements not met");
+ detail::sec_ws_key_type key;
+ auto req = impl_->build_request(
+ key, host, target, &default_decorate_req);
+ return net::async_initiate<
+ HandshakeHandler,
+ void(error_code)>(
+ run_handshake_op{},
+ handler,
+ impl_,
+ std::move(req),
+ key,
+ nullptr);
+}
+
+template<class NextLayer, bool deflateSupported>
+template<class HandshakeHandler>
+BOOST_BEAST_ASYNC_RESULT1(HandshakeHandler)
+stream<NextLayer, deflateSupported>::
+async_handshake(
+ response_type& res,
+ string_view host,
+ string_view target,
+ HandshakeHandler&& handler)
+{
+ static_assert(is_async_stream<next_layer_type>::value,
+ "AsyncStream type requirements not met");
+ detail::sec_ws_key_type key;
+ auto req = impl_->build_request(
+ key, host, target, &default_decorate_req);
+ return net::async_initiate<
+ HandshakeHandler,
+ void(error_code)>(
+ run_handshake_op{},
+ handler,
+ impl_,
+ std::move(req),
+ key,
+ &res);
+}
+
+template<class NextLayer, bool deflateSupported>
+void
+stream<NextLayer, deflateSupported>::
+handshake(string_view host,
+ string_view target)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream type requirements not met");
+ error_code ec;
+ handshake(
+ host, target, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+}
+
+template<class NextLayer, bool deflateSupported>
+void
+stream<NextLayer, deflateSupported>::
+handshake(response_type& res,
+ string_view host,
+ string_view target)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream type requirements not met");
+ error_code ec;
+ handshake(res, host, target, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+}
+
+template<class NextLayer, bool deflateSupported>
+void
+stream<NextLayer, deflateSupported>::
+handshake(string_view host,
+ string_view target, error_code& ec)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream type requirements not met");
+ do_handshake(nullptr,
+ host, target, &default_decorate_req, ec);
+}
+
+template<class NextLayer, bool deflateSupported>
+void
+stream<NextLayer, deflateSupported>::
+handshake(response_type& res,
+ string_view host,
+ string_view target,
+ error_code& ec)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream type requirements not met");
+ do_handshake(&res,
+ host, target, &default_decorate_req, ec);
+}
+
+//------------------------------------------------------------------------------
+
+template<class NextLayer, bool deflateSupported>
+template<class RequestDecorator>
+void
+stream<NextLayer, deflateSupported>::
+handshake_ex(string_view host,
+ string_view target,
+ RequestDecorator const& decorator)
+{
+#ifndef BOOST_BEAST_ALLOW_DEPRECATED
+ static_assert(sizeof(RequestDecorator) == 0,
+ BOOST_BEAST_DEPRECATION_STRING);
+#endif
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream type requirements not met");
+ static_assert(detail::is_request_decorator<
+ RequestDecorator>::value,
+ "RequestDecorator requirements not met");
+ error_code ec;
+ handshake_ex(host, target, decorator, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+}
+
+template<class NextLayer, bool deflateSupported>
+template<class RequestDecorator>
+void
+stream<NextLayer, deflateSupported>::
+handshake_ex(response_type& res,
+ string_view host,
+ string_view target,
+ RequestDecorator const& decorator)
+{
+#ifndef BOOST_BEAST_ALLOW_DEPRECATED
+ static_assert(sizeof(RequestDecorator) == 0,
+ BOOST_BEAST_DEPRECATION_STRING);
+#endif
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream type requirements not met");
+ static_assert(detail::is_request_decorator<
+ RequestDecorator>::value,
+ "RequestDecorator requirements not met");
+ error_code ec;
+ handshake_ex(res, host, target, decorator, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+}
+
+template<class NextLayer, bool deflateSupported>
+template<class RequestDecorator>
+void
+stream<NextLayer, deflateSupported>::
+handshake_ex(string_view host,
+ string_view target,
+ RequestDecorator const& decorator,
+ error_code& ec)
+{
+#ifndef BOOST_BEAST_ALLOW_DEPRECATED
+ static_assert(sizeof(RequestDecorator) == 0,
+ BOOST_BEAST_DEPRECATION_STRING);
+#endif
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream type requirements not met");
+ static_assert(detail::is_request_decorator<
+ RequestDecorator>::value,
+ "RequestDecorator requirements not met");
+ do_handshake(nullptr,
+ host, target, decorator, ec);
+}
+
+template<class NextLayer, bool deflateSupported>
+template<class RequestDecorator>
+void
+stream<NextLayer, deflateSupported>::
+handshake_ex(response_type& res,
+ string_view host,
+ string_view target,
+ RequestDecorator const& decorator,
+ error_code& ec)
+{
+#ifndef BOOST_BEAST_ALLOW_DEPRECATED
+ static_assert(sizeof(RequestDecorator) == 0,
+ BOOST_BEAST_DEPRECATION_STRING);
+#endif
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream type requirements not met");
+ static_assert(detail::is_request_decorator<
+ RequestDecorator>::value,
+ "RequestDecorator requirements not met");
+ do_handshake(&res,
+ host, target, decorator, ec);
+}
+
+template<class NextLayer, bool deflateSupported>
+template<class RequestDecorator, class HandshakeHandler>
+BOOST_BEAST_ASYNC_RESULT1(HandshakeHandler)
+stream<NextLayer, deflateSupported>::
+async_handshake_ex(string_view host,
+ string_view target,
+ RequestDecorator const& decorator,
+ HandshakeHandler&& handler)
+{
+#ifndef BOOST_BEAST_ALLOW_DEPRECATED
+ static_assert(sizeof(RequestDecorator) == 0,
+ BOOST_BEAST_DEPRECATION_STRING);
+#endif
+ static_assert(is_async_stream<next_layer_type>::value,
+ "AsyncStream type requirements not met");
+ static_assert(detail::is_request_decorator<
+ RequestDecorator>::value,
+ "RequestDecorator requirements not met");
+ detail::sec_ws_key_type key;
+ auto req = impl_->build_request(
+ key, host, target, decorator);
+ return net::async_initiate<
+ HandshakeHandler,
+ void(error_code)>(
+ run_handshake_op{},
+ handler,
+ impl_,
+ std::move(req),
+ key,
+ nullptr);
+}
+
+template<class NextLayer, bool deflateSupported>
+template<class RequestDecorator, class HandshakeHandler>
+BOOST_BEAST_ASYNC_RESULT1(HandshakeHandler)
+stream<NextLayer, deflateSupported>::
+async_handshake_ex(response_type& res,
+ string_view host,
+ string_view target,
+ RequestDecorator const& decorator,
+ HandshakeHandler&& handler)
+{
+#ifndef BOOST_BEAST_ALLOW_DEPRECATED
+ static_assert(sizeof(RequestDecorator) == 0,
+ BOOST_BEAST_DEPRECATION_STRING);
+#endif
+ static_assert(is_async_stream<next_layer_type>::value,
+ "AsyncStream type requirements not met");
+ static_assert(detail::is_request_decorator<
+ RequestDecorator>::value,
+ "RequestDecorator requirements not met");
+ detail::sec_ws_key_type key;
+ auto req = impl_->build_request(
+ key, host, target, decorator);
+ return net::async_initiate<
+ HandshakeHandler,
+ void(error_code)>(
+ run_handshake_op{},
+ handler,
+ impl_,
+ std::move(req),
+ key,
+ &res);
+}
+
+} // websocket
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/websocket/impl/handshake.ipp b/boost/beast/websocket/impl/handshake.ipp
deleted file mode 100644
index a1a826be8a..0000000000
--- a/boost/beast/websocket/impl/handshake.ipp
+++ /dev/null
@@ -1,427 +0,0 @@
-//
-// 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_IMPL_HANDSHAKE_IPP
-#define BOOST_BEAST_WEBSOCKET_IMPL_HANDSHAKE_IPP
-
-#include <boost/beast/websocket/detail/type_traits.hpp>
-#include <boost/beast/http/empty_body.hpp>
-#include <boost/beast/http/message.hpp>
-#include <boost/beast/http/read.hpp>
-#include <boost/beast/http/write.hpp>
-#include <boost/beast/core/handler_ptr.hpp>
-#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/executor_work_guard.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>
-
-namespace boost {
-namespace beast {
-namespace websocket {
-
-//------------------------------------------------------------------------------
-
-// send the upgrade request and process the response
-//
-template<class NextLayer, bool deflateSupported>
-template<class Handler>
-class stream<NextLayer, deflateSupported>::handshake_op
- : public boost::asio::coroutine
-{
- struct data
- {
- stream<NextLayer, deflateSupported>& ws;
- boost::asio::executor_work_guard<decltype(std::declval<
- stream<NextLayer, deflateSupported>&>().get_executor())> wg;
- response_type* res_p;
- detail::sec_ws_key_type key;
- http::request<http::empty_body> req;
- response_type res;
-
- template<class Decorator>
- data(
- Handler const&,
- stream<NextLayer, deflateSupported>& ws_,
- response_type* res_p_,
- string_view host,
- string_view target,
- Decorator const& decorator)
- : ws(ws_)
- , wg(ws.get_executor())
- , res_p(res_p_)
- , req(ws.build_request(key,
- host, target, decorator))
- {
- ws.reset();
- }
- };
-
- handler_ptr<data, Handler> d_;
-
-public:
- handshake_op(handshake_op&&) = default;
- handshake_op(handshake_op const&) = delete;
-
- template<class DeducedHandler, class... Args>
- handshake_op(DeducedHandler&& h,
- stream<NextLayer, deflateSupported>& ws, Args&&... args)
- : d_(std::forward<DeducedHandler>(h),
- ws, std::forward<Args>(args)...)
- {
- }
-
- using allocator_type =
- boost::asio::associated_allocator_t<Handler>;
-
- allocator_type
- get_allocator() const noexcept
- {
- return (boost::asio::get_associated_allocator)(d_.handler());
- }
-
- using executor_type = boost::asio::associated_executor_t<
- Handler, decltype(std::declval<stream<NextLayer, deflateSupported>&>().get_executor())>;
-
- executor_type
- get_executor() const noexcept
- {
- return (boost::asio::get_associated_executor)(
- d_.handler(), d_->ws.get_executor());
- }
-
- void
- operator()(
- error_code ec = {},
- std::size_t bytes_used = 0);
-
- friend
- bool asio_handler_is_continuation(handshake_op* op)
- {
- using boost::asio::asio_handler_is_continuation;
- 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, bool deflateSupported>
-template<class Handler>
-void
-stream<NextLayer, deflateSupported>::
-handshake_op<Handler>::
-operator()(error_code ec, std::size_t)
-{
- auto& d = *d_;
- BOOST_ASIO_CORO_REENTER(*this)
- {
- // Send HTTP Upgrade
- 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));
- if(ec)
- goto upcall;
-
- // VFALCO We could pre-serialize the request to
- // a single buffer, send that instead,
- // and delete the buffer here. The buffer
- // could be a variable block at the end
- // of handler_ptr's allocation.
-
- // Read HTTP response
- BOOST_ASIO_CORO_YIELD
- http::async_read(d.ws.next_layer(),
- d.ws.rd_buf_, d.res,
- std::move(*this));
- if(ec)
- goto upcall;
- d.ws.on_response(d.res, d.key, ec);
- if(d.res_p)
- swap(d.res, *d.res_p);
- upcall:
- {
- auto wg = std::move(d.wg);
- d_.invoke(ec);
- }
- }
-}
-
-template<class NextLayer, bool deflateSupported>
-template<class HandshakeHandler>
-BOOST_ASIO_INITFN_RESULT_TYPE(
- HandshakeHandler, void(error_code))
-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_BEAST_HANDLER_INIT(
- HandshakeHandler, void(error_code));
- handshake_op<BOOST_ASIO_HANDLER_TYPE(
- HandshakeHandler, void(error_code))>{
- std::move(init.completion_handler), *this, nullptr, host,
- target, &default_decorate_req}();
- return init.result.get();
-}
-
-template<class NextLayer, bool deflateSupported>
-template<class HandshakeHandler>
-BOOST_ASIO_INITFN_RESULT_TYPE(
- HandshakeHandler, void(error_code))
-stream<NextLayer, deflateSupported>::
-async_handshake(response_type& res,
- string_view host,
- string_view target,
- HandshakeHandler&& handler)
-{
- static_assert(is_async_stream<next_layer_type>::value,
- "AsyncStream requirements not met");
- BOOST_BEAST_HANDLER_INIT(
- HandshakeHandler, void(error_code));
- handshake_op<BOOST_ASIO_HANDLER_TYPE(
- HandshakeHandler, void(error_code))>{
- std::move(init.completion_handler), *this, &res, host,
- target, &default_decorate_req}();
- return init.result.get();
-}
-
-template<class NextLayer, bool deflateSupported>
-template<class RequestDecorator, class HandshakeHandler>
-BOOST_ASIO_INITFN_RESULT_TYPE(
- HandshakeHandler, void(error_code))
-stream<NextLayer, deflateSupported>::
-async_handshake_ex(string_view host,
- string_view target,
- RequestDecorator const& decorator,
- HandshakeHandler&& handler)
-{
- static_assert(is_async_stream<next_layer_type>::value,
- "AsyncStream requirements not met");
- static_assert(detail::is_request_decorator<
- RequestDecorator>::value,
- "RequestDecorator requirements not met");
- BOOST_BEAST_HANDLER_INIT(
- HandshakeHandler, void(error_code));
- handshake_op<BOOST_ASIO_HANDLER_TYPE(
- HandshakeHandler, void(error_code))>{
- std::move(init.completion_handler), *this, nullptr, host,
- target, decorator}();
- return init.result.get();
-}
-
-template<class NextLayer, bool deflateSupported>
-template<class RequestDecorator, class HandshakeHandler>
-BOOST_ASIO_INITFN_RESULT_TYPE(
- HandshakeHandler, void(error_code))
-stream<NextLayer, deflateSupported>::
-async_handshake_ex(response_type& res,
- string_view host,
- string_view target,
- RequestDecorator const& decorator,
- HandshakeHandler&& handler)
-{
- static_assert(is_async_stream<next_layer_type>::value,
- "AsyncStream requirements not met");
- static_assert(detail::is_request_decorator<
- RequestDecorator>::value,
- "RequestDecorator requirements not met");
- BOOST_BEAST_HANDLER_INIT(
- HandshakeHandler, void(error_code));
- handshake_op<BOOST_ASIO_HANDLER_TYPE(
- HandshakeHandler, void(error_code))>{
- std::move(init.completion_handler), *this, &res, host,
- target, decorator}();
- return init.result.get();
-}
-
-template<class NextLayer, bool deflateSupported>
-void
-stream<NextLayer, deflateSupported>::
-handshake(string_view host,
- string_view target)
-{
- static_assert(is_sync_stream<next_layer_type>::value,
- "SyncStream requirements not met");
- error_code ec;
- handshake(
- host, target, ec);
- if(ec)
- BOOST_THROW_EXCEPTION(system_error{ec});
-}
-
-template<class NextLayer, bool deflateSupported>
-void
-stream<NextLayer, deflateSupported>::
-handshake(response_type& res,
- string_view host,
- string_view target)
-{
- static_assert(is_sync_stream<next_layer_type>::value,
- "SyncStream requirements not met");
- error_code ec;
- handshake(res, host, target, ec);
- if(ec)
- BOOST_THROW_EXCEPTION(system_error{ec});
-}
-
-template<class NextLayer, bool deflateSupported>
-template<class RequestDecorator>
-void
-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_request_decorator<
- RequestDecorator>::value,
- "RequestDecorator requirements not met");
- error_code ec;
- handshake_ex(host, target, decorator, ec);
- if(ec)
- BOOST_THROW_EXCEPTION(system_error{ec});
-}
-
-template<class NextLayer, bool deflateSupported>
-template<class RequestDecorator>
-void
-stream<NextLayer, deflateSupported>::
-handshake_ex(response_type& res,
- 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_request_decorator<
- RequestDecorator>::value,
- "RequestDecorator requirements not met");
- error_code ec;
- handshake_ex(res, host, target, decorator, ec);
- if(ec)
- BOOST_THROW_EXCEPTION(system_error{ec});
-}
-
-template<class NextLayer, bool deflateSupported>
-void
-stream<NextLayer, deflateSupported>::
-handshake(string_view host,
- string_view target, error_code& ec)
-{
- static_assert(is_sync_stream<next_layer_type>::value,
- "SyncStream requirements not met");
- do_handshake(nullptr,
- host, target, &default_decorate_req, ec);
-}
-
-template<class NextLayer, bool deflateSupported>
-void
-stream<NextLayer, deflateSupported>::
-handshake(response_type& res,
- string_view host,
- string_view target,
- error_code& ec)
-{
- static_assert(is_sync_stream<next_layer_type>::value,
- "SyncStream requirements not met");
- do_handshake(&res,
- host, target, &default_decorate_req, ec);
-}
-
-template<class NextLayer, bool deflateSupported>
-template<class RequestDecorator>
-void
-stream<NextLayer, deflateSupported>::
-handshake_ex(string_view host,
- string_view target,
- RequestDecorator const& decorator,
- error_code& ec)
-{
- static_assert(is_sync_stream<next_layer_type>::value,
- "SyncStream requirements not met");
- static_assert(detail::is_request_decorator<
- RequestDecorator>::value,
- "RequestDecorator requirements not met");
- do_handshake(nullptr,
- host, target, decorator, ec);
-}
-
-template<class NextLayer, bool deflateSupported>
-template<class RequestDecorator>
-void
-stream<NextLayer, deflateSupported>::
-handshake_ex(response_type& res,
- string_view host,
- string_view target,
- RequestDecorator const& decorator,
- error_code& ec)
-{
- static_assert(is_sync_stream<next_layer_type>::value,
- "SyncStream requirements not met");
- static_assert(detail::is_request_decorator<
- RequestDecorator>::value,
- "RequestDecorator requirements not met");
- do_handshake(&res,
- host, target, decorator, ec);
-}
-
-//------------------------------------------------------------------------------
-
-template<class NextLayer, bool deflateSupported>
-template<class RequestDecorator>
-void
-stream<NextLayer, deflateSupported>::
-do_handshake(
- response_type* res_p,
- string_view host,
- string_view target,
- RequestDecorator const& decorator,
- error_code& ec)
-{
- response_type res;
- reset();
- detail::sec_ws_key_type key;
- {
- auto const req = build_request(
- key, host, target, decorator);
- do_pmd_config(req, is_deflate_supported{});
- http::write(stream_, req, ec);
- }
- if(ec)
- return;
- http::read(next_layer(), rd_buf_, res, ec);
- if(ec)
- return;
- on_response(res, key, ec);
- if(res_p)
- *res_p = std::move(res);
-}
-
-} // websocket
-} // beast
-} // boost
-
-#endif
diff --git a/boost/beast/websocket/impl/ping.hpp b/boost/beast/websocket/impl/ping.hpp
new file mode 100644
index 0000000000..350505c081
--- /dev/null
+++ b/boost/beast/websocket/impl/ping.hpp
@@ -0,0 +1,329 @@
+//
+// Copyright (c) 2016-2019 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_IMPL_PING_HPP
+#define BOOST_BEAST_WEBSOCKET_IMPL_PING_HPP
+
+#include <boost/beast/core/async_base.hpp>
+#include <boost/beast/core/bind_handler.hpp>
+#include <boost/beast/core/stream_traits.hpp>
+#include <boost/beast/core/detail/bind_continuation.hpp>
+#include <boost/beast/websocket/detail/frame.hpp>
+#include <boost/beast/websocket/impl/stream_impl.hpp>
+#include <boost/asio/coroutine.hpp>
+#include <boost/asio/post.hpp>
+#include <boost/throw_exception.hpp>
+#include <memory>
+
+namespace boost {
+namespace beast {
+namespace websocket {
+
+/*
+ This composed operation handles sending ping and pong frames.
+ It only sends the frames it does not make attempts to read
+ any frame data.
+*/
+template<class NextLayer, bool deflateSupported>
+template<class Handler>
+class stream<NextLayer, deflateSupported>::ping_op
+ : public beast::stable_async_base<
+ Handler, beast::executor_type<stream>>
+ , public net::coroutine
+{
+ boost::weak_ptr<impl_type> wp_;
+ detail::frame_buffer& fb_;
+
+public:
+ static constexpr int id = 3; // for soft_mutex
+
+ template<class Handler_>
+ ping_op(
+ Handler_&& h,
+ boost::shared_ptr<impl_type> const& sp,
+ detail::opcode op,
+ ping_data const& payload)
+ : stable_async_base<Handler,
+ beast::executor_type<stream>>(
+ std::forward<Handler_>(h),
+ sp->stream().get_executor())
+ , wp_(sp)
+ , fb_(beast::allocate_stable<
+ detail::frame_buffer>(*this))
+ {
+ // Serialize the ping or pong frame
+ sp->template write_ping<
+ flat_static_buffer_base>(fb_, op, payload);
+ (*this)({}, 0, false);
+ }
+
+ void operator()(
+ error_code ec = {},
+ std::size_t bytes_transferred = 0,
+ bool cont = true)
+ {
+ boost::ignore_unused(bytes_transferred);
+ auto sp = wp_.lock();
+ if(! sp)
+ {
+ ec = net::error::operation_aborted;
+ return this->complete(cont, ec);
+ }
+ auto& impl = *sp;
+ BOOST_ASIO_CORO_REENTER(*this)
+ {
+ // Acquire the write lock
+ if(! impl.wr_block.try_lock(this))
+ {
+ BOOST_ASIO_CORO_YIELD
+ impl.op_ping.emplace(std::move(*this));
+ impl.wr_block.lock(this);
+ BOOST_ASIO_CORO_YIELD
+ net::post(std::move(*this));
+ BOOST_ASSERT(impl.wr_block.is_locked(this));
+ }
+ if(impl.check_stop_now(ec))
+ goto upcall;
+
+ // Send ping frame
+ BOOST_ASIO_CORO_YIELD
+ net::async_write(impl.stream(), fb_.data(),
+ beast::detail::bind_continuation(std::move(*this)));
+ if(impl.check_stop_now(ec))
+ goto upcall;
+
+ upcall:
+ impl.wr_block.unlock(this);
+ impl.op_close.maybe_invoke()
+ || impl.op_idle_ping.maybe_invoke()
+ || impl.op_rd.maybe_invoke()
+ || impl.op_wr.maybe_invoke();
+ this->complete(cont, ec);
+ }
+ }
+};
+
+//------------------------------------------------------------------------------
+
+// sends the idle ping
+template<class NextLayer, bool deflateSupported>
+template<class Executor>
+class stream<NextLayer, deflateSupported>::idle_ping_op
+ : public net::coroutine
+ , public boost::empty_value<Executor>
+{
+ boost::weak_ptr<impl_type> wp_;
+ std::unique_ptr<detail::frame_buffer> fb_;
+
+public:
+ static constexpr int id = 4; // for soft_mutex
+
+ using executor_type = Executor;
+
+ executor_type
+ get_executor() const noexcept
+ {
+ return this->get();
+ }
+
+ idle_ping_op(
+ boost::shared_ptr<impl_type> const& sp,
+ Executor const& ex)
+ : boost::empty_value<Executor>(
+ boost::empty_init_t{}, ex)
+ , wp_(sp)
+ , fb_(new detail::frame_buffer)
+ {
+ if(! sp->idle_pinging)
+ {
+ // Create the ping frame
+ ping_data payload; // empty for now
+ sp->template write_ping<
+ flat_static_buffer_base>(*fb_,
+ detail::opcode::ping, payload);
+
+ sp->idle_pinging = true;
+ (*this)({}, 0);
+ }
+ else
+ {
+ // if we are already in the middle of sending
+ // an idle ping, don't bother sending another.
+ }
+ }
+
+ void operator()(
+ error_code ec = {},
+ std::size_t bytes_transferred = 0)
+ {
+ boost::ignore_unused(bytes_transferred);
+ auto sp = wp_.lock();
+ if(! sp)
+ return;
+ auto& impl = *sp;
+ BOOST_ASIO_CORO_REENTER(*this)
+ {
+ // Acquire the write lock
+ if(! impl.wr_block.try_lock(this))
+ {
+ BOOST_ASIO_CORO_YIELD
+ impl.op_idle_ping.emplace(std::move(*this));
+ impl.wr_block.lock(this);
+ BOOST_ASIO_CORO_YIELD
+ net::post(this->get(), std::move(*this));
+ BOOST_ASSERT(impl.wr_block.is_locked(this));
+ }
+ if(impl.check_stop_now(ec))
+ goto upcall;
+
+ // Send ping frame
+ BOOST_ASIO_CORO_YIELD
+ net::async_write(impl.stream(), fb_->data(),
+ //beast::detail::bind_continuation(std::move(*this)));
+ std::move(*this));
+ if(impl.check_stop_now(ec))
+ goto upcall;
+
+ upcall:
+ BOOST_ASSERT(sp->idle_pinging);
+ sp->idle_pinging = false;
+ impl.wr_block.unlock(this);
+ impl.op_close.maybe_invoke()
+ || impl.op_ping.maybe_invoke()
+ || impl.op_rd.maybe_invoke()
+ || impl.op_wr.maybe_invoke();
+ }
+ }
+};
+
+template<class NextLayer, bool deflateSupported>
+struct stream<NextLayer, deflateSupported>::
+ run_ping_op
+{
+ template<class WriteHandler>
+ void
+ operator()(
+ WriteHandler&& h,
+ boost::shared_ptr<impl_type> const& sp,
+ detail::opcode op,
+ ping_data const& p)
+ {
+ // If you get an error on the following line it means
+ // that your handler does not meet the documented type
+ // requirements for the handler.
+
+ static_assert(
+ beast::detail::is_invocable<WriteHandler,
+ void(error_code)>::value,
+ "WriteHandler type requirements not met");
+
+ ping_op<
+ typename std::decay<WriteHandler>::type>(
+ std::forward<WriteHandler>(h),
+ sp,
+ op,
+ p);
+ }
+};
+
+//------------------------------------------------------------------------------
+
+template<class NextLayer, bool deflateSupported>
+void
+stream<NextLayer, deflateSupported>::
+ping(ping_data const& payload)
+{
+ error_code ec;
+ ping(payload, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+}
+
+template<class NextLayer, bool deflateSupported>
+void
+stream<NextLayer, deflateSupported>::
+ping(ping_data const& payload, error_code& ec)
+{
+ if(impl_->check_stop_now(ec))
+ return;
+ detail::frame_buffer fb;
+ impl_->template write_ping<flat_static_buffer_base>(
+ fb, detail::opcode::ping, payload);
+ net::write(impl_->stream(), fb.data(), ec);
+ if(impl_->check_stop_now(ec))
+ return;
+}
+
+template<class NextLayer, bool deflateSupported>
+void
+stream<NextLayer, deflateSupported>::
+pong(ping_data const& payload)
+{
+ error_code ec;
+ pong(payload, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+}
+
+template<class NextLayer, bool deflateSupported>
+void
+stream<NextLayer, deflateSupported>::
+pong(ping_data const& payload, error_code& ec)
+{
+ if(impl_->check_stop_now(ec))
+ return;
+ detail::frame_buffer fb;
+ impl_->template write_ping<flat_static_buffer_base>(
+ fb, detail::opcode::pong, payload);
+ net::write(impl_->stream(), fb.data(), ec);
+ if(impl_->check_stop_now(ec))
+ return;
+}
+
+template<class NextLayer, bool deflateSupported>
+template<class WriteHandler>
+BOOST_BEAST_ASYNC_RESULT1(WriteHandler)
+stream<NextLayer, deflateSupported>::
+async_ping(ping_data const& payload, WriteHandler&& handler)
+{
+ static_assert(is_async_stream<next_layer_type>::value,
+ "AsyncStream type requirements not met");
+ return net::async_initiate<
+ WriteHandler,
+ void(error_code)>(
+ run_ping_op{},
+ handler,
+ impl_,
+ detail::opcode::ping,
+ payload);
+}
+
+template<class NextLayer, bool deflateSupported>
+template<class WriteHandler>
+BOOST_BEAST_ASYNC_RESULT1(WriteHandler)
+stream<NextLayer, deflateSupported>::
+async_pong(ping_data const& payload, WriteHandler&& handler)
+{
+ static_assert(is_async_stream<next_layer_type>::value,
+ "AsyncStream type requirements not met");
+ return net::async_initiate<
+ WriteHandler,
+ void(error_code)>(
+ run_ping_op{},
+ handler,
+ impl_,
+ detail::opcode::pong,
+ payload);
+}
+
+} // websocket
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/websocket/impl/ping.ipp b/boost/beast/websocket/impl/ping.ipp
deleted file mode 100644
index 6ceaaeabae..0000000000
--- a/boost/beast/websocket/impl/ping.ipp
+++ /dev/null
@@ -1,282 +0,0 @@
-//
-// 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_IMPL_PING_IPP
-#define BOOST_BEAST_WEBSOCKET_IMPL_PING_IPP
-
-#include <boost/beast/core/bind_handler.hpp>
-#include <boost/beast/core/handler_ptr.hpp>
-#include <boost/beast/core/type_traits.hpp>
-#include <boost/beast/core/detail/config.hpp>
-#include <boost/beast/websocket/detail/frame.hpp>
-#include <boost/asio/associated_allocator.hpp>
-#include <boost/asio/associated_executor.hpp>
-#include <boost/asio/coroutine.hpp>
-#include <boost/asio/executor_work_guard.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>
-
-namespace boost {
-namespace beast {
-namespace websocket {
-
-/*
- This composed operation handles sending ping and pong frames.
- It only sends the frames it does not make attempts to read
- any frame data.
-*/
-template<class NextLayer, bool deflateSupported>
-template<class Handler>
-class stream<NextLayer, deflateSupported>::ping_op
- : public boost::asio::coroutine
-{
- struct state
- {
- stream<NextLayer, deflateSupported>& ws;
- boost::asio::executor_work_guard<decltype(std::declval<
- stream<NextLayer, deflateSupported>&>().get_executor())> wg;
- detail::frame_buffer fb;
-
- state(
- Handler const&,
- stream<NextLayer, deflateSupported>& ws_,
- detail::opcode op,
- ping_data const& payload)
- : ws(ws_)
- , wg(ws.get_executor())
- {
- // Serialize the control frame
- ws.template write_ping<
- flat_static_buffer_base>(
- fb, op, payload);
- }
- };
-
- handler_ptr<state, Handler> d_;
-
-public:
- static constexpr int id = 3; // for soft_mutex
-
- ping_op(ping_op&&) = default;
- ping_op(ping_op const&) = delete;
-
- template<class DeducedHandler>
- ping_op(
- DeducedHandler&& h,
- stream<NextLayer, deflateSupported>& ws,
- detail::opcode op,
- ping_data const& payload)
- : d_(std::forward<DeducedHandler>(h),
- ws, op, payload)
- {
- }
-
- using allocator_type =
- boost::asio::associated_allocator_t<Handler>;
-
- allocator_type
- get_allocator() const noexcept
- {
- return (boost::asio::get_associated_allocator)(d_.handler());
- }
-
- using executor_type = boost::asio::associated_executor_t<
- Handler, decltype(std::declval<stream<NextLayer, deflateSupported>&>().get_executor())>;
-
- executor_type
- get_executor() const noexcept
- {
- return (boost::asio::get_associated_executor)(
- d_.handler(), d_->ws.get_executor());
- }
-
- void operator()(
- error_code ec = {},
- std::size_t bytes_transferred = 0);
-
- friend
- bool asio_handler_is_continuation(ping_op* op)
- {
- using boost::asio::asio_handler_is_continuation;
- 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, bool deflateSupported>
-template<class Handler>
-void
-stream<NextLayer, deflateSupported>::
-ping_op<Handler>::
-operator()(error_code ec, std::size_t)
-{
- auto& d = *d_;
- BOOST_ASIO_CORO_REENTER(*this)
- {
- // Maybe suspend
- if(d.ws.wr_block_.try_lock(this))
- {
- // Make sure the stream is open
- if(! d.ws.check_open(ec))
- {
- BOOST_ASIO_CORO_YIELD
- boost::asio::post(
- d.ws.get_executor(),
- bind_handler(std::move(*this), ec));
- goto upcall;
- }
- }
- else
- {
- // Suspend
- BOOST_ASIO_CORO_YIELD
- d.ws.paused_ping_.emplace(std::move(*this));
-
- // Acquire the write block
- 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_.is_locked(this));
-
- // Make sure the stream is open
- if(! d.ws.check_open(ec))
- goto upcall;
- }
-
- // Send ping frame
- BOOST_ASIO_CORO_YIELD
- boost::asio::async_write(d.ws.stream_,
- d.fb.data(), std::move(*this));
- if(! d.ws.check_ok(ec))
- goto upcall;
-
- upcall:
- d.ws.wr_block_.unlock(this);
- d.ws.paused_close_.maybe_invoke() ||
- d.ws.paused_rd_.maybe_invoke() ||
- d.ws.paused_wr_.maybe_invoke();
- {
- auto wg = std::move(d.wg);
- d_.invoke(ec);
- }
- }
-}
-
-//------------------------------------------------------------------------------
-
-template<class NextLayer, bool deflateSupported>
-void
-stream<NextLayer, deflateSupported>::
-ping(ping_data const& payload)
-{
- error_code ec;
- ping(payload, ec);
- if(ec)
- BOOST_THROW_EXCEPTION(system_error{ec});
-}
-
-template<class NextLayer, bool deflateSupported>
-void
-stream<NextLayer, deflateSupported>::
-ping(ping_data const& payload, error_code& ec)
-{
- // Make sure the stream is open
- if(! check_open(ec))
- return;
- detail::frame_buffer fb;
- write_ping<flat_static_buffer_base>(
- fb, detail::opcode::ping, payload);
- boost::asio::write(stream_, fb.data(), ec);
- if(! check_ok(ec))
- return;
-}
-
-template<class NextLayer, bool deflateSupported>
-void
-stream<NextLayer, deflateSupported>::
-pong(ping_data const& payload)
-{
- error_code ec;
- pong(payload, ec);
- if(ec)
- BOOST_THROW_EXCEPTION(system_error{ec});
-}
-
-template<class NextLayer, bool deflateSupported>
-void
-stream<NextLayer, deflateSupported>::
-pong(ping_data const& payload, error_code& ec)
-{
- // Make sure the stream is open
- if(! check_open(ec))
- return;
- detail::frame_buffer fb;
- write_ping<flat_static_buffer_base>(
- fb, detail::opcode::pong, payload);
- boost::asio::write(stream_, fb.data(), ec);
- if(! check_ok(ec))
- return;
-}
-
-template<class NextLayer, bool deflateSupported>
-template<class WriteHandler>
-BOOST_ASIO_INITFN_RESULT_TYPE(
- WriteHandler, void(error_code))
-stream<NextLayer, deflateSupported>::
-async_ping(ping_data const& payload, WriteHandler&& handler)
-{
- static_assert(is_async_stream<next_layer_type>::value,
- "AsyncStream requirements not met");
- BOOST_BEAST_HANDLER_INIT(
- WriteHandler, void(error_code));
- ping_op<BOOST_ASIO_HANDLER_TYPE(
- WriteHandler, void(error_code))>{
- std::move(init.completion_handler), *this,
- detail::opcode::ping, payload}();
- return init.result.get();
-}
-
-template<class NextLayer, bool deflateSupported>
-template<class WriteHandler>
-BOOST_ASIO_INITFN_RESULT_TYPE(
- WriteHandler, void(error_code))
-stream<NextLayer, deflateSupported>::
-async_pong(ping_data const& payload, WriteHandler&& handler)
-{
- static_assert(is_async_stream<next_layer_type>::value,
- "AsyncStream requirements not met");
- BOOST_BEAST_HANDLER_INIT(
- WriteHandler, void(error_code));
- ping_op<BOOST_ASIO_HANDLER_TYPE(
- WriteHandler, void(error_code))>{
- std::move(init.completion_handler), *this,
- detail::opcode::pong, payload}();
- return init.result.get();
-}
-
-} // websocket
-} // beast
-} // boost
-
-#endif
diff --git a/boost/beast/websocket/impl/read.hpp b/boost/beast/websocket/impl/read.hpp
new file mode 100644
index 0000000000..8be18e5975
--- /dev/null
+++ b/boost/beast/websocket/impl/read.hpp
@@ -0,0 +1,1286 @@
+//
+// Copyright (c) 2016-2019 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_IMPL_READ_HPP
+#define BOOST_BEAST_WEBSOCKET_IMPL_READ_HPP
+
+#include <boost/beast/core/buffer_traits.hpp>
+#include <boost/beast/websocket/teardown.hpp>
+#include <boost/beast/websocket/detail/mask.hpp>
+#include <boost/beast/websocket/impl/stream_impl.hpp>
+#include <boost/beast/core/async_base.hpp>
+#include <boost/beast/core/bind_handler.hpp>
+#include <boost/beast/core/buffers_prefix.hpp>
+#include <boost/beast/core/buffers_suffix.hpp>
+#include <boost/beast/core/flat_static_buffer.hpp>
+#include <boost/beast/core/read_size.hpp>
+#include <boost/beast/core/stream_traits.hpp>
+#include <boost/beast/core/detail/bind_continuation.hpp>
+#include <boost/beast/core/detail/buffer.hpp>
+#include <boost/beast/core/detail/clamp.hpp>
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/asio/coroutine.hpp>
+#include <boost/asio/post.hpp>
+#include <boost/assert.hpp>
+#include <boost/config.hpp>
+#include <boost/optional.hpp>
+#include <boost/throw_exception.hpp>
+#include <algorithm>
+#include <limits>
+#include <memory>
+
+namespace boost {
+namespace beast {
+namespace websocket {
+
+/* Read some message data into a buffer sequence.
+
+ Also reads and handles control frames.
+*/
+template<class NextLayer, bool deflateSupported>
+template<class Handler, class MutableBufferSequence>
+class stream<NextLayer, deflateSupported>::read_some_op
+ : public beast::async_base<
+ Handler, beast::executor_type<stream>>
+ , public net::coroutine
+{
+ boost::weak_ptr<impl_type> wp_;
+ MutableBufferSequence bs_;
+ buffers_suffix<MutableBufferSequence> cb_;
+ std::size_t bytes_written_ = 0;
+ error_code result_;
+ close_code code_;
+ bool did_read_ = false;
+
+public:
+ static constexpr int id = 1; // for soft_mutex
+
+ template<class Handler_>
+ read_some_op(
+ Handler_&& h,
+ boost::shared_ptr<impl_type> const& sp,
+ MutableBufferSequence const& bs)
+ : async_base<
+ Handler, beast::executor_type<stream>>(
+ std::forward<Handler_>(h),
+ sp->stream().get_executor())
+ , wp_(sp)
+ , bs_(bs)
+ , cb_(bs)
+ , code_(close_code::none)
+ {
+ (*this)({}, 0, false);
+ }
+
+ void operator()(
+ error_code ec = {},
+ std::size_t bytes_transferred = 0,
+ bool cont = true)
+ {
+ using beast::detail::clamp;
+ auto sp = wp_.lock();
+ if(! sp)
+ {
+ ec = net::error::operation_aborted;
+ bytes_written_ = 0;
+ return this->complete(cont, ec, bytes_written_);
+ }
+ auto& impl = *sp;
+ BOOST_ASIO_CORO_REENTER(*this)
+ {
+ impl.update_timer(this->get_executor());
+
+ acquire_read_lock:
+ // Acquire the read lock
+ if(! impl.rd_block.try_lock(this))
+ {
+ do_suspend:
+ BOOST_ASIO_CORO_YIELD
+ impl.op_r_rd.emplace(std::move(*this));
+ impl.rd_block.lock(this);
+ BOOST_ASIO_CORO_YIELD
+ net::post(std::move(*this));
+ BOOST_ASSERT(impl.rd_block.is_locked(this));
+
+ // VFALCO Is this check correct here?
+ BOOST_ASSERT(! ec && impl.check_stop_now(ec));
+ if(impl.check_stop_now(ec))
+ {
+ BOOST_ASSERT(ec == net::error::operation_aborted);
+ goto upcall;
+ }
+ // VFALCO Should never get here
+
+ // The only way to get read blocked is if
+ // a `close_op` wrote a close frame
+ BOOST_ASSERT(impl.wr_close);
+ BOOST_ASSERT(impl.status_ != status::open);
+ ec = net::error::operation_aborted;
+ goto upcall;
+ }
+ else
+ {
+ // Make sure the stream is not closed
+ if( impl.status_ == status::closed ||
+ impl.status_ == status::failed)
+ {
+ ec = net::error::operation_aborted;
+ goto upcall;
+ }
+ }
+
+ // if status_ == status::closing, we want to suspend
+ // the read operation until the close completes,
+ // then finish the read with operation_aborted.
+
+ loop:
+ BOOST_ASSERT(impl.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
+ //
+ if(impl.rd_remain == 0 &&
+ (! impl.rd_fh.fin || impl.rd_done))
+ {
+ // Read frame header
+ while(! impl.parse_fh(
+ impl.rd_fh, impl.rd_buf, result_))
+ {
+ if(result_)
+ {
+ // _Fail the WebSocket Connection_
+ if(result_ == error::message_too_big)
+ code_ = close_code::too_big;
+ else
+ code_ = close_code::protocol_error;
+ goto close;
+ }
+ BOOST_ASSERT(impl.rd_block.is_locked(this));
+ BOOST_ASIO_CORO_YIELD
+ impl.stream().async_read_some(
+ impl.rd_buf.prepare(read_size(
+ impl.rd_buf, impl.rd_buf.max_size())),
+ std::move(*this));
+ BOOST_ASSERT(impl.rd_block.is_locked(this));
+ impl.rd_buf.commit(bytes_transferred);
+ if(impl.check_stop_now(ec))
+ goto upcall;
+ impl.reset_idle();
+
+ // Allow a close operation
+ // to acquire the read block
+ impl.rd_block.unlock(this);
+ if( impl.op_r_close.maybe_invoke())
+ {
+ // Suspend
+ BOOST_ASSERT(impl.rd_block.is_locked());
+ goto do_suspend;
+ }
+ // Acquire read block
+ impl.rd_block.lock(this);
+ }
+ // Immediately apply the mask to the portion
+ // of the buffer holding payload data.
+ if(impl.rd_fh.len > 0 && impl.rd_fh.mask)
+ detail::mask_inplace(buffers_prefix(
+ clamp(impl.rd_fh.len),
+ impl.rd_buf.data()),
+ impl.rd_key);
+ if(detail::is_control(impl.rd_fh.op))
+ {
+ // Clear this otherwise the next
+ // frame will be considered final.
+ impl.rd_fh.fin = false;
+
+ // Handle ping frame
+ if(impl.rd_fh.op == detail::opcode::ping)
+ {
+ if(impl.ctrl_cb)
+ {
+ if(! cont)
+ {
+ BOOST_ASIO_CORO_YIELD
+ net::post(std::move(*this));
+ BOOST_ASSERT(cont);
+ // VFALCO call check_stop_now() here?
+ }
+ }
+ {
+ auto const b = buffers_prefix(
+ clamp(impl.rd_fh.len),
+ impl.rd_buf.data());
+ auto const len = buffer_bytes(b);
+ BOOST_ASSERT(len == impl.rd_fh.len);
+ ping_data payload;
+ detail::read_ping(payload, b);
+ impl.rd_buf.consume(len);
+ // Ignore ping when closing
+ if(impl.status_ == status::closing)
+ goto loop;
+ if(impl.ctrl_cb)
+ impl.ctrl_cb(
+ frame_type::ping, payload);
+ impl.rd_fb.clear();
+ impl.template write_ping<
+ flat_static_buffer_base>(impl.rd_fb,
+ detail::opcode::pong, payload);
+ }
+
+ // Allow a close operation
+ // to acquire the read block
+ impl.rd_block.unlock(this);
+ impl.op_r_close.maybe_invoke();
+
+ // Acquire the write lock
+ if(! impl.wr_block.try_lock(this))
+ {
+ BOOST_ASIO_CORO_YIELD
+ impl.op_rd.emplace(std::move(*this));
+ impl.wr_block.lock(this);
+ BOOST_ASIO_CORO_YIELD
+ net::post(std::move(*this));
+ BOOST_ASSERT(impl.wr_block.is_locked(this));
+ if(impl.check_stop_now(ec))
+ goto upcall;
+ }
+
+ // Send pong
+ BOOST_ASSERT(impl.wr_block.is_locked(this));
+ BOOST_ASIO_CORO_YIELD
+ net::async_write(
+ impl.stream(), impl.rd_fb.data(),
+ beast::detail::bind_continuation(std::move(*this)));
+ BOOST_ASSERT(impl.wr_block.is_locked(this));
+ if(impl.check_stop_now(ec))
+ goto upcall;
+ impl.wr_block.unlock(this);
+ impl.op_close.maybe_invoke()
+ || impl.op_idle_ping.maybe_invoke()
+ || impl.op_ping.maybe_invoke()
+ || impl.op_wr.maybe_invoke();
+ goto acquire_read_lock;
+ }
+
+ // Handle pong frame
+ if(impl.rd_fh.op == detail::opcode::pong)
+ {
+ // Ignore pong when closing
+ if(! impl.wr_close && impl.ctrl_cb)
+ {
+ if(! cont)
+ {
+ BOOST_ASIO_CORO_YIELD
+ net::post(std::move(*this));
+ BOOST_ASSERT(cont);
+ }
+ }
+ auto const cb = buffers_prefix(clamp(
+ impl.rd_fh.len), impl.rd_buf.data());
+ auto const len = buffer_bytes(cb);
+ BOOST_ASSERT(len == impl.rd_fh.len);
+ ping_data payload;
+ detail::read_ping(payload, cb);
+ impl.rd_buf.consume(len);
+ // Ignore pong when closing
+ if(! impl.wr_close && impl.ctrl_cb)
+ impl.ctrl_cb(frame_type::pong, payload);
+ goto loop;
+ }
+
+ // Handle close frame
+ BOOST_ASSERT(impl.rd_fh.op == detail::opcode::close);
+ {
+ if(impl.ctrl_cb)
+ {
+ if(! cont)
+ {
+ BOOST_ASIO_CORO_YIELD
+ net::post(std::move(*this));
+ BOOST_ASSERT(cont);
+ }
+ }
+ auto const cb = buffers_prefix(clamp(
+ impl.rd_fh.len), impl.rd_buf.data());
+ auto const len = buffer_bytes(cb);
+ BOOST_ASSERT(len == impl.rd_fh.len);
+ BOOST_ASSERT(! impl.rd_close);
+ impl.rd_close = true;
+ close_reason cr;
+ detail::read_close(cr, cb, result_);
+ if(result_)
+ {
+ // _Fail the WebSocket Connection_
+ code_ = close_code::protocol_error;
+ goto close;
+ }
+ impl.cr = cr;
+ impl.rd_buf.consume(len);
+ if(impl.ctrl_cb)
+ impl.ctrl_cb(frame_type::close,
+ impl.cr.reason);
+ // See if we are already closing
+ if(impl.status_ == status::closing)
+ {
+ // _Close the WebSocket Connection_
+ BOOST_ASSERT(impl.wr_close);
+ code_ = close_code::none;
+ 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);
+ result_ = error::closed;
+ goto close;
+ }
+ }
+ if(impl.rd_fh.len == 0 && ! impl.rd_fh.fin)
+ {
+ // Empty non-final frame
+ goto loop;
+ }
+ impl.rd_done = false;
+ }
+ if(! impl.rd_deflated())
+ {
+ if(impl.rd_remain > 0)
+ {
+ if(impl.rd_buf.size() == 0 && impl.rd_buf.max_size() >
+ (std::min)(clamp(impl.rd_remain),
+ buffer_bytes(cb_)))
+ {
+ // Fill the read buffer first, otherwise we
+ // get fewer bytes at the cost of one I/O.
+ BOOST_ASIO_CORO_YIELD
+ impl.stream().async_read_some(
+ impl.rd_buf.prepare(read_size(
+ impl.rd_buf, impl.rd_buf.max_size())),
+ std::move(*this));
+ impl.rd_buf.commit(bytes_transferred);
+ if(impl.check_stop_now(ec))
+ goto upcall;
+ impl.reset_idle();
+ if(impl.rd_fh.mask)
+ detail::mask_inplace(buffers_prefix(clamp(
+ impl.rd_remain), impl.rd_buf.data()),
+ impl.rd_key);
+ }
+ if(impl.rd_buf.size() > 0)
+ {
+ // Copy from the read buffer.
+ // The mask was already applied.
+ bytes_transferred = net::buffer_copy(cb_,
+ impl.rd_buf.data(), clamp(impl.rd_remain));
+ auto const mb = buffers_prefix(
+ bytes_transferred, cb_);
+ impl.rd_remain -= bytes_transferred;
+ if(impl.rd_op == detail::opcode::text)
+ {
+ if(! impl.rd_utf8.write(mb) ||
+ (impl.rd_remain == 0 && impl.rd_fh.fin &&
+ ! impl.rd_utf8.finish()))
+ {
+ // _Fail the WebSocket Connection_
+ code_ = close_code::bad_payload;
+ result_ = error::bad_frame_payload;
+ goto close;
+ }
+ }
+ bytes_written_ += bytes_transferred;
+ impl.rd_size += bytes_transferred;
+ impl.rd_buf.consume(bytes_transferred);
+ }
+ else
+ {
+ // Read into caller's buffer
+ BOOST_ASSERT(impl.rd_remain > 0);
+ BOOST_ASSERT(buffer_bytes(cb_) > 0);
+ BOOST_ASSERT(buffer_bytes(buffers_prefix(
+ clamp(impl.rd_remain), cb_)) > 0);
+ BOOST_ASIO_CORO_YIELD
+ impl.stream().async_read_some(buffers_prefix(
+ clamp(impl.rd_remain), cb_), std::move(*this));
+ if(impl.check_stop_now(ec))
+ goto upcall;
+ impl.reset_idle();
+ BOOST_ASSERT(bytes_transferred > 0);
+ auto const mb = buffers_prefix(
+ bytes_transferred, cb_);
+ impl.rd_remain -= bytes_transferred;
+ if(impl.rd_fh.mask)
+ detail::mask_inplace(mb, impl.rd_key);
+ if(impl.rd_op == detail::opcode::text)
+ {
+ if(! impl.rd_utf8.write(mb) ||
+ (impl.rd_remain == 0 && impl.rd_fh.fin &&
+ ! impl.rd_utf8.finish()))
+ {
+ // _Fail the WebSocket Connection_
+ code_ = close_code::bad_payload;
+ result_ = error::bad_frame_payload;
+ goto close;
+ }
+ }
+ bytes_written_ += bytes_transferred;
+ impl.rd_size += bytes_transferred;
+ }
+ }
+ impl.rd_done = impl.rd_remain == 0 && impl.rd_fh.fin;
+ }
+ else
+ {
+ // Read compressed message frame payload:
+ // inflate even if rd_fh_.len == 0, otherwise we
+ // never emit the end-of-stream deflate block.
+ while(buffer_bytes(cb_) > 0)
+ {
+ if( impl.rd_remain > 0 &&
+ impl.rd_buf.size() == 0 &&
+ ! did_read_)
+ {
+ // read new
+ BOOST_ASIO_CORO_YIELD
+ impl.stream().async_read_some(
+ impl.rd_buf.prepare(read_size(
+ impl.rd_buf, impl.rd_buf.max_size())),
+ std::move(*this));
+ if(impl.check_stop_now(ec))
+ goto upcall;
+ impl.reset_idle();
+ BOOST_ASSERT(bytes_transferred > 0);
+ impl.rd_buf.commit(bytes_transferred);
+ if(impl.rd_fh.mask)
+ detail::mask_inplace(
+ buffers_prefix(clamp(impl.rd_remain),
+ impl.rd_buf.data()), impl.rd_key);
+ did_read_ = true;
+ }
+ zlib::z_params zs;
+ {
+ auto const out = buffers_front(cb_);
+ zs.next_out = out.data();
+ zs.avail_out = out.size();
+ BOOST_ASSERT(zs.avail_out > 0);
+ }
+ if(impl.rd_remain > 0)
+ {
+ if(impl.rd_buf.size() > 0)
+ {
+ // use what's there
+ auto const in = buffers_prefix(
+ clamp(impl.rd_remain), buffers_front(
+ impl.rd_buf.data()));
+ zs.avail_in = in.size();
+ zs.next_in = in.data();
+ }
+ else
+ {
+ break;
+ }
+ }
+ else if(impl.rd_fh.fin)
+ {
+ // append the empty block codes
+ std::uint8_t constexpr
+ empty_block[4] = { 0x00, 0x00, 0xff, 0xff };
+ zs.next_in = empty_block;
+ zs.avail_in = sizeof(empty_block);
+ impl.inflate(zs, zlib::Flush::sync, ec);
+ if(! ec)
+ {
+ // https://github.com/madler/zlib/issues/280
+ if(zs.total_out > 0)
+ ec = error::partial_deflate_block;
+ }
+ if(impl.check_stop_now(ec))
+ goto upcall;
+ impl.do_context_takeover_read(impl.role);
+ impl.rd_done = true;
+ break;
+ }
+ else
+ {
+ break;
+ }
+ impl.inflate(zs, zlib::Flush::sync, ec);
+ if(impl.check_stop_now(ec))
+ goto upcall;
+ if(impl.rd_msg_max && beast::detail::sum_exceeds(
+ impl.rd_size, zs.total_out, impl.rd_msg_max))
+ {
+ // _Fail the WebSocket Connection_
+ code_ = close_code::too_big;
+ result_ = error::message_too_big;
+ goto close;
+ }
+ cb_.consume(zs.total_out);
+ impl.rd_size += zs.total_out;
+ impl.rd_remain -= zs.total_in;
+ impl.rd_buf.consume(zs.total_in);
+ bytes_written_ += zs.total_out;
+ }
+ if(impl.rd_op == detail::opcode::text)
+ {
+ // check utf8
+ if(! impl.rd_utf8.write(
+ buffers_prefix(bytes_written_, bs_)) || (
+ impl.rd_done && ! impl.rd_utf8.finish()))
+ {
+ // _Fail the WebSocket Connection_
+ code_ = close_code::bad_payload;
+ result_ = error::bad_frame_payload;
+ goto close;
+ }
+ }
+ }
+ goto upcall;
+
+ close:
+ // Acquire the write lock
+ if(! impl.wr_block.try_lock(this))
+ {
+ BOOST_ASIO_CORO_YIELD
+ impl.op_rd.emplace(std::move(*this));
+ impl.wr_block.lock(this);
+ BOOST_ASIO_CORO_YIELD
+ net::post(std::move(*this));
+ BOOST_ASSERT(impl.wr_block.is_locked(this));
+ if(impl.check_stop_now(ec))
+ goto upcall;
+ }
+
+ impl.change_status(status::closing);
+
+ if(! impl.wr_close)
+ {
+ impl.wr_close = true;
+
+ // Serialize close frame
+ impl.rd_fb.clear();
+ impl.template write_close<
+ flat_static_buffer_base>(
+ impl.rd_fb, code_);
+
+ // Send close frame
+ BOOST_ASSERT(impl.wr_block.is_locked(this));
+ BOOST_ASIO_CORO_YIELD
+ net::async_write(impl.stream(), impl.rd_fb.data(),
+ beast::detail::bind_continuation(std::move(*this)));
+ BOOST_ASSERT(impl.wr_block.is_locked(this));
+ if(impl.check_stop_now(ec))
+ goto upcall;
+ }
+
+ // Teardown
+ using beast::websocket::async_teardown;
+ BOOST_ASSERT(impl.wr_block.is_locked(this));
+ BOOST_ASIO_CORO_YIELD
+ async_teardown(impl.role, impl.stream(),
+ beast::detail::bind_continuation(std::move(*this)));
+ BOOST_ASSERT(impl.wr_block.is_locked(this));
+ if(ec == net::error::eof)
+ {
+ // Rationale:
+ // http://stackoverflow.com/questions/25587403/boost-asio-ssl-async-shutdown-always-finishes-with-an-error
+ ec = {};
+ }
+ if(! ec)
+ ec = result_;
+ if(ec && ec != error::closed)
+ impl.change_status(status::failed);
+ else
+ impl.change_status(status::closed);
+ impl.close();
+
+ upcall:
+ impl.rd_block.try_unlock(this);
+ impl.op_r_close.maybe_invoke();
+ if(impl.wr_block.try_unlock(this))
+ impl.op_close.maybe_invoke()
+ || impl.op_idle_ping.maybe_invoke()
+ || impl.op_ping.maybe_invoke()
+ || impl.op_wr.maybe_invoke();
+ this->complete(cont, ec, bytes_written_);
+ }
+ }
+};
+
+//------------------------------------------------------------------------------
+
+template<class NextLayer, bool deflateSupported>
+template<class Handler, class DynamicBuffer>
+class stream<NextLayer, deflateSupported>::read_op
+ : public beast::async_base<
+ Handler, beast::executor_type<stream>>
+ , public net::coroutine
+{
+ boost::weak_ptr<impl_type> wp_;
+ DynamicBuffer& b_;
+ std::size_t limit_;
+ std::size_t bytes_written_ = 0;
+ bool some_;
+
+public:
+ template<class Handler_>
+ read_op(
+ Handler_&& h,
+ boost::shared_ptr<impl_type> const& sp,
+ DynamicBuffer& b,
+ std::size_t limit,
+ bool some)
+ : async_base<Handler,
+ beast::executor_type<stream>>(
+ std::forward<Handler_>(h),
+ sp->stream().get_executor())
+ , wp_(sp)
+ , b_(b)
+ , limit_(limit ? limit : (
+ std::numeric_limits<std::size_t>::max)())
+ , some_(some)
+ {
+ (*this)({}, 0, false);
+ }
+
+ void operator()(
+ error_code ec = {},
+ std::size_t bytes_transferred = 0,
+ bool cont = true)
+ {
+ using beast::detail::clamp;
+ auto sp = wp_.lock();
+ if(! sp)
+ {
+ ec = net::error::operation_aborted;
+ bytes_written_ = 0;
+ return this->complete(cont, ec, bytes_written_);
+ }
+ auto& impl = *sp;
+ using mutable_buffers_type = typename
+ DynamicBuffer::mutable_buffers_type;
+ boost::optional<mutable_buffers_type> mb;
+ BOOST_ASIO_CORO_REENTER(*this)
+ {
+ do
+ {
+ mb = beast::detail::dynamic_buffer_prepare(b_,
+ clamp(impl.read_size_hint_db(b_), limit_),
+ ec, error::buffer_overflow);
+ if(impl.check_stop_now(ec))
+ goto upcall;
+
+ // VFALCO TODO use boost::beast::bind_continuation
+ BOOST_ASIO_CORO_YIELD
+ read_some_op<read_op, mutable_buffers_type>(
+ std::move(*this), sp, *mb);
+ b_.commit(bytes_transferred);
+ bytes_written_ += bytes_transferred;
+ if(ec)
+ goto upcall;
+ }
+ while(! some_ && ! impl.rd_done);
+
+ upcall:
+ this->complete(cont, ec, bytes_written_);
+ }
+ }
+};
+
+template<class NextLayer, bool deflateSupported>
+struct stream<NextLayer, deflateSupported>::
+ run_read_some_op
+{
+ template<
+ class ReadHandler,
+ class MutableBufferSequence>
+ void
+ operator()(
+ ReadHandler&& h,
+ boost::shared_ptr<impl_type> const& sp,
+ MutableBufferSequence const& b)
+ {
+ // If you get an error on the following line it means
+ // that your handler does not meet the documented type
+ // requirements for the handler.
+
+ static_assert(
+ beast::detail::is_invocable<ReadHandler,
+ void(error_code, std::size_t)>::value,
+ "ReadHandler type requirements not met");
+
+ read_some_op<
+ typename std::decay<ReadHandler>::type,
+ MutableBufferSequence>(
+ std::forward<ReadHandler>(h),
+ sp,
+ b);
+ }
+};
+
+template<class NextLayer, bool deflateSupported>
+struct stream<NextLayer, deflateSupported>::
+ run_read_op
+{
+ template<
+ class ReadHandler,
+ class DynamicBuffer>
+ void
+ operator()(
+ ReadHandler&& h,
+ boost::shared_ptr<impl_type> const& sp,
+ DynamicBuffer* b,
+ std::size_t limit,
+ bool some)
+ {
+ // If you get an error on the following line it means
+ // that your handler does not meet the documented type
+ // requirements for the handler.
+
+ static_assert(
+ beast::detail::is_invocable<ReadHandler,
+ void(error_code, std::size_t)>::value,
+ "ReadHandler type requirements not met");
+
+ read_op<
+ typename std::decay<ReadHandler>::type,
+ DynamicBuffer>(
+ std::forward<ReadHandler>(h),
+ sp,
+ *b,
+ limit,
+ some);
+ }
+};
+
+//------------------------------------------------------------------------------
+
+template<class NextLayer, bool deflateSupported>
+template<class DynamicBuffer>
+std::size_t
+stream<NextLayer, deflateSupported>::
+read(DynamicBuffer& buffer)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream type requirements not met");
+ static_assert(
+ net::is_dynamic_buffer<DynamicBuffer>::value,
+ "DynamicBuffer type requirements not met");
+ error_code ec;
+ auto const bytes_written = read(buffer, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+ return bytes_written;
+}
+
+template<class NextLayer, bool deflateSupported>
+template<class DynamicBuffer>
+std::size_t
+stream<NextLayer, deflateSupported>::
+read(DynamicBuffer& buffer, error_code& ec)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream type requirements not met");
+ static_assert(
+ net::is_dynamic_buffer<DynamicBuffer>::value,
+ "DynamicBuffer type requirements not met");
+ std::size_t bytes_written = 0;
+ do
+ {
+ bytes_written += read_some(buffer, 0, ec);
+ if(ec)
+ return bytes_written;
+ }
+ while(! is_message_done());
+ return bytes_written;
+}
+
+template<class NextLayer, bool deflateSupported>
+template<class DynamicBuffer, class ReadHandler>
+BOOST_BEAST_ASYNC_RESULT2(ReadHandler)
+stream<NextLayer, deflateSupported>::
+async_read(DynamicBuffer& buffer, ReadHandler&& handler)
+{
+ static_assert(is_async_stream<next_layer_type>::value,
+ "AsyncStream type requirements not met");
+ static_assert(
+ net::is_dynamic_buffer<DynamicBuffer>::value,
+ "DynamicBuffer type requirements not met");
+ return net::async_initiate<
+ ReadHandler,
+ void(error_code, std::size_t)>(
+ run_read_op{},
+ handler,
+ impl_,
+ &buffer,
+ 0,
+ false);
+}
+
+//------------------------------------------------------------------------------
+
+template<class NextLayer, bool deflateSupported>
+template<class DynamicBuffer>
+std::size_t
+stream<NextLayer, deflateSupported>::
+read_some(
+ DynamicBuffer& buffer,
+ std::size_t limit)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream type requirements not met");
+ static_assert(
+ net::is_dynamic_buffer<DynamicBuffer>::value,
+ "DynamicBuffer type requirements not met");
+ error_code ec;
+ auto const bytes_written =
+ read_some(buffer, limit, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+ return bytes_written;
+}
+
+template<class NextLayer, bool deflateSupported>
+template<class DynamicBuffer>
+std::size_t
+stream<NextLayer, deflateSupported>::
+read_some(
+ DynamicBuffer& buffer,
+ std::size_t limit,
+ error_code& ec)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream type requirements not met");
+ static_assert(
+ net::is_dynamic_buffer<DynamicBuffer>::value,
+ "DynamicBuffer type requirements not met");
+ using beast::detail::clamp;
+ if(! limit)
+ limit = (std::numeric_limits<std::size_t>::max)();
+ auto const size =
+ clamp(read_size_hint(buffer), limit);
+ BOOST_ASSERT(size > 0);
+ auto mb = beast::detail::dynamic_buffer_prepare(
+ buffer, size, ec, error::buffer_overflow);
+ if(impl_->check_stop_now(ec))
+ return 0;
+ auto const bytes_written = read_some(*mb, ec);
+ buffer.commit(bytes_written);
+ return bytes_written;
+}
+
+template<class NextLayer, bool deflateSupported>
+template<class DynamicBuffer, class ReadHandler>
+BOOST_BEAST_ASYNC_RESULT2(ReadHandler)
+stream<NextLayer, deflateSupported>::
+async_read_some(
+ DynamicBuffer& buffer,
+ std::size_t limit,
+ ReadHandler&& handler)
+{
+ static_assert(is_async_stream<next_layer_type>::value,
+ "AsyncStream type requirements not met");
+ static_assert(
+ net::is_dynamic_buffer<DynamicBuffer>::value,
+ "DynamicBuffer type requirements not met");
+ return net::async_initiate<
+ ReadHandler,
+ void(error_code, std::size_t)>(
+ run_read_op{},
+ handler,
+ impl_,
+ &buffer,
+ limit,
+ true);
+}
+
+//------------------------------------------------------------------------------
+
+template<class NextLayer, bool deflateSupported>
+template<class MutableBufferSequence>
+std::size_t
+stream<NextLayer, deflateSupported>::
+read_some(
+ MutableBufferSequence const& buffers)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream type requirements not met");
+ static_assert(net::is_mutable_buffer_sequence<
+ MutableBufferSequence>::value,
+ "MutableBufferSequence type requirements not met");
+ error_code ec;
+ auto const bytes_written = read_some(buffers, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+ return bytes_written;
+}
+
+template<class NextLayer, bool deflateSupported>
+template<class MutableBufferSequence>
+std::size_t
+stream<NextLayer, deflateSupported>::
+read_some(
+ MutableBufferSequence const& buffers,
+ error_code& ec)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream type requirements not met");
+ static_assert(net::is_mutable_buffer_sequence<
+ MutableBufferSequence>::value,
+ "MutableBufferSequence type requirements not met");
+ using beast::detail::clamp;
+ auto& impl = *impl_;
+ close_code code{};
+ std::size_t bytes_written = 0;
+ ec = {};
+ // Make sure the stream is open
+ if(impl.check_stop_now(ec))
+ return bytes_written;
+loop:
+ // 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
+ //
+ if(impl.rd_remain == 0 && (
+ ! impl.rd_fh.fin || impl.rd_done))
+ {
+ // Read frame header
+ error_code result;
+ while(! impl.parse_fh(impl.rd_fh, impl.rd_buf, result))
+ {
+ if(result)
+ {
+ // _Fail the WebSocket Connection_
+ 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 =
+ impl.stream().read_some(
+ impl.rd_buf.prepare(read_size(
+ impl.rd_buf, impl.rd_buf.max_size())),
+ ec);
+ impl.rd_buf.commit(bytes_transferred);
+ if(impl.check_stop_now(ec))
+ return bytes_written;
+ }
+ // Immediately apply the mask to the portion
+ // of the buffer holding payload data.
+ if(impl.rd_fh.len > 0 && impl.rd_fh.mask)
+ detail::mask_inplace(buffers_prefix(
+ clamp(impl.rd_fh.len), impl.rd_buf.data()),
+ impl.rd_key);
+ if(detail::is_control(impl.rd_fh.op))
+ {
+ // Get control frame payload
+ auto const b = buffers_prefix(
+ clamp(impl.rd_fh.len), impl.rd_buf.data());
+ auto const len = buffer_bytes(b);
+ BOOST_ASSERT(len == impl.rd_fh.len);
+
+ // Clear this otherwise the next
+ // frame will be considered final.
+ impl.rd_fh.fin = false;
+
+ // Handle ping frame
+ if(impl.rd_fh.op == detail::opcode::ping)
+ {
+ ping_data payload;
+ detail::read_ping(payload, b);
+ impl.rd_buf.consume(len);
+ if(impl.wr_close)
+ {
+ // Ignore ping when closing
+ goto loop;
+ }
+ if(impl.ctrl_cb)
+ impl.ctrl_cb(frame_type::ping, payload);
+ detail::frame_buffer fb;
+ impl.template write_ping<flat_static_buffer_base>(fb,
+ detail::opcode::pong, payload);
+ net::write(impl.stream(), fb.data(), ec);
+ if(impl.check_stop_now(ec))
+ return bytes_written;
+ goto loop;
+ }
+ // Handle pong frame
+ if(impl.rd_fh.op == detail::opcode::pong)
+ {
+ ping_data payload;
+ detail::read_ping(payload, b);
+ impl.rd_buf.consume(len);
+ if(impl.ctrl_cb)
+ impl.ctrl_cb(frame_type::pong, payload);
+ goto loop;
+ }
+ // Handle close frame
+ BOOST_ASSERT(impl.rd_fh.op == detail::opcode::close);
+ {
+ BOOST_ASSERT(! impl.rd_close);
+ impl.rd_close = true;
+ close_reason cr;
+ detail::read_close(cr, b, result);
+ if(result)
+ {
+ // _Fail the WebSocket Connection_
+ do_fail(close_code::protocol_error,
+ result, ec);
+ return bytes_written;
+ }
+ impl.cr = cr;
+ impl.rd_buf.consume(len);
+ if(impl.ctrl_cb)
+ impl.ctrl_cb(frame_type::close, impl.cr.reason);
+ BOOST_ASSERT(! impl.wr_close);
+ // _Start the WebSocket Closing Handshake_
+ do_fail(
+ cr.code == close_code::none ?
+ close_code::normal :
+ static_cast<close_code>(cr.code),
+ error::closed, ec);
+ return bytes_written;
+ }
+ }
+ if(impl.rd_fh.len == 0 && ! impl.rd_fh.fin)
+ {
+ // Empty non-final frame
+ goto loop;
+ }
+ impl.rd_done = false;
+ }
+ else
+ {
+ ec = {};
+ }
+ if(! impl.rd_deflated())
+ {
+ if(impl.rd_remain > 0)
+ {
+ if(impl.rd_buf.size() == 0 && impl.rd_buf.max_size() >
+ (std::min)(clamp(impl.rd_remain),
+ buffer_bytes(buffers)))
+ {
+ // Fill the read buffer first, otherwise we
+ // get fewer bytes at the cost of one I/O.
+ impl.rd_buf.commit(impl.stream().read_some(
+ impl.rd_buf.prepare(read_size(impl.rd_buf,
+ impl.rd_buf.max_size())), ec));
+ if(impl.check_stop_now(ec))
+ return bytes_written;
+ if(impl.rd_fh.mask)
+ detail::mask_inplace(
+ buffers_prefix(clamp(impl.rd_remain),
+ impl.rd_buf.data()), impl.rd_key);
+ }
+ if(impl.rd_buf.size() > 0)
+ {
+ // Copy from the read buffer.
+ // The mask was already applied.
+ auto const bytes_transferred = net::buffer_copy(
+ buffers, impl.rd_buf.data(),
+ clamp(impl.rd_remain));
+ auto const mb = buffers_prefix(
+ bytes_transferred, buffers);
+ impl.rd_remain -= bytes_transferred;
+ if(impl.rd_op == detail::opcode::text)
+ {
+ if(! impl.rd_utf8.write(mb) ||
+ (impl.rd_remain == 0 && impl.rd_fh.fin &&
+ ! impl.rd_utf8.finish()))
+ {
+ // _Fail the WebSocket Connection_
+ do_fail(close_code::bad_payload,
+ error::bad_frame_payload, ec);
+ return bytes_written;
+ }
+ }
+ bytes_written += bytes_transferred;
+ impl.rd_size += bytes_transferred;
+ impl.rd_buf.consume(bytes_transferred);
+ }
+ else
+ {
+ // Read into caller's buffer
+ BOOST_ASSERT(impl.rd_remain > 0);
+ BOOST_ASSERT(buffer_bytes(buffers) > 0);
+ BOOST_ASSERT(buffer_bytes(buffers_prefix(
+ clamp(impl.rd_remain), buffers)) > 0);
+ auto const bytes_transferred =
+ impl.stream().read_some(buffers_prefix(
+ clamp(impl.rd_remain), buffers), ec);
+ // VFALCO What if some bytes were written?
+ if(impl.check_stop_now(ec))
+ return bytes_written;
+ BOOST_ASSERT(bytes_transferred > 0);
+ auto const mb = buffers_prefix(
+ bytes_transferred, buffers);
+ impl.rd_remain -= bytes_transferred;
+ if(impl.rd_fh.mask)
+ detail::mask_inplace(mb, impl.rd_key);
+ if(impl.rd_op == detail::opcode::text)
+ {
+ if(! impl.rd_utf8.write(mb) ||
+ (impl.rd_remain == 0 && impl.rd_fh.fin &&
+ ! impl.rd_utf8.finish()))
+ {
+ // _Fail the WebSocket Connection_
+ do_fail(close_code::bad_payload,
+ error::bad_frame_payload, ec);
+ return bytes_written;
+ }
+ }
+ bytes_written += bytes_transferred;
+ impl.rd_size += bytes_transferred;
+ }
+ }
+ impl.rd_done = impl.rd_remain == 0 && impl.rd_fh.fin;
+ }
+ else
+ {
+ // Read compressed message frame payload:
+ // inflate even if rd_fh_.len == 0, otherwise we
+ // never emit the end-of-stream deflate block.
+ //
+ bool did_read = false;
+ buffers_suffix<MutableBufferSequence> cb(buffers);
+ while(buffer_bytes(cb) > 0)
+ {
+ zlib::z_params zs;
+ {
+ auto const out = beast::buffers_front(cb);
+ zs.next_out = out.data();
+ zs.avail_out = out.size();
+ BOOST_ASSERT(zs.avail_out > 0);
+ }
+ if(impl.rd_remain > 0)
+ {
+ if(impl.rd_buf.size() > 0)
+ {
+ // use what's there
+ auto const in = buffers_prefix(
+ clamp(impl.rd_remain), beast::buffers_front(
+ impl.rd_buf.data()));
+ zs.avail_in = in.size();
+ zs.next_in = in.data();
+ }
+ else if(! did_read)
+ {
+ // read new
+ auto const bytes_transferred =
+ impl.stream().read_some(
+ impl.rd_buf.prepare(read_size(
+ impl.rd_buf, impl.rd_buf.max_size())),
+ ec);
+ if(impl.check_stop_now(ec))
+ return bytes_written;
+ BOOST_ASSERT(bytes_transferred > 0);
+ impl.rd_buf.commit(bytes_transferred);
+ if(impl.rd_fh.mask)
+ detail::mask_inplace(
+ buffers_prefix(clamp(impl.rd_remain),
+ impl.rd_buf.data()), impl.rd_key);
+ auto const in = buffers_prefix(
+ clamp(impl.rd_remain), buffers_front(
+ impl.rd_buf.data()));
+ zs.avail_in = in.size();
+ zs.next_in = in.data();
+ did_read = true;
+ }
+ else
+ {
+ break;
+ }
+ }
+ else if(impl.rd_fh.fin)
+ {
+ // append the empty block codes
+ static std::uint8_t constexpr
+ empty_block[4] = {
+ 0x00, 0x00, 0xff, 0xff };
+ zs.next_in = empty_block;
+ zs.avail_in = sizeof(empty_block);
+ impl.inflate(zs, zlib::Flush::sync, ec);
+ if(! ec)
+ {
+ // https://github.com/madler/zlib/issues/280
+ if(zs.total_out > 0)
+ ec = error::partial_deflate_block;
+ }
+ if(impl.check_stop_now(ec))
+ return bytes_written;
+ impl.do_context_takeover_read(impl.role);
+ impl.rd_done = true;
+ break;
+ }
+ else
+ {
+ break;
+ }
+ impl.inflate(zs, zlib::Flush::sync, ec);
+ if(impl.check_stop_now(ec))
+ return bytes_written;
+ if(impl.rd_msg_max && beast::detail::sum_exceeds(
+ impl.rd_size, zs.total_out, impl.rd_msg_max))
+ {
+ do_fail(close_code::too_big,
+ error::message_too_big, ec);
+ return bytes_written;
+ }
+ cb.consume(zs.total_out);
+ impl.rd_size += zs.total_out;
+ impl.rd_remain -= zs.total_in;
+ impl.rd_buf.consume(zs.total_in);
+ bytes_written += zs.total_out;
+ }
+ if(impl.rd_op == detail::opcode::text)
+ {
+ // check utf8
+ if(! impl.rd_utf8.write(beast::buffers_prefix(
+ bytes_written, buffers)) || (
+ impl.rd_done && ! impl.rd_utf8.finish()))
+ {
+ // _Fail the WebSocket Connection_
+ do_fail(close_code::bad_payload,
+ error::bad_frame_payload, ec);
+ return bytes_written;
+ }
+ }
+ }
+ return bytes_written;
+}
+
+template<class NextLayer, bool deflateSupported>
+template<class MutableBufferSequence, class ReadHandler>
+BOOST_BEAST_ASYNC_RESULT2(ReadHandler)
+stream<NextLayer, deflateSupported>::
+async_read_some(
+ MutableBufferSequence const& buffers,
+ ReadHandler&& handler)
+{
+ static_assert(is_async_stream<next_layer_type>::value,
+ "AsyncStream type requirements not met");
+ static_assert(net::is_mutable_buffer_sequence<
+ MutableBufferSequence>::value,
+ "MutableBufferSequence type requirements not met");
+ return net::async_initiate<
+ ReadHandler,
+ void(error_code, std::size_t)>(
+ run_read_some_op{},
+ handler,
+ impl_,
+ buffers);
+}
+
+} // websocket
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/websocket/impl/read.ipp b/boost/beast/websocket/impl/read.ipp
deleted file mode 100644
index 3fb7f223f2..0000000000
--- a/boost/beast/websocket/impl/read.ipp
+++ /dev/null
@@ -1,1368 +0,0 @@
-//
-// 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_IMPL_READ_IPP
-#define BOOST_BEAST_WEBSOCKET_IMPL_READ_IPP
-
-#include <boost/beast/websocket/teardown.hpp>
-#include <boost/beast/core/bind_handler.hpp>
-#include <boost/beast/core/buffers_prefix.hpp>
-#include <boost/beast/core/buffers_suffix.hpp>
-#include <boost/beast/core/flat_static_buffer.hpp>
-#include <boost/beast/core/type_traits.hpp>
-#include <boost/beast/core/detail/buffer.hpp>
-#include <boost/beast/core/detail/clamp.hpp>
-#include <boost/beast/core/detail/config.hpp>
-#include <boost/asio/associated_allocator.hpp>
-#include <boost/asio/associated_executor.hpp>
-#include <boost/asio/coroutine.hpp>
-#include <boost/asio/executor_work_guard.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>
-#include <boost/optional.hpp>
-#include <boost/throw_exception.hpp>
-#include <algorithm>
-#include <limits>
-#include <memory>
-
-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, bool deflateSupported>
-template<
- class MutableBufferSequence,
- class Handler>
-class stream<NextLayer, deflateSupported>::read_some_op
- : public boost::asio::coroutine
-{
- Handler h_;
- stream<NextLayer, deflateSupported>& ws_;
- boost::asio::executor_work_guard<decltype(std::declval<
- stream<NextLayer, deflateSupported>&>().get_executor())> wg_;
- MutableBufferSequence bs_;
- buffers_suffix<MutableBufferSequence> cb_;
- std::size_t bytes_written_ = 0;
- 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&) = delete;
-
- template<class DeducedHandler>
- read_some_op(
- DeducedHandler&& h,
- stream<NextLayer, deflateSupported>& ws,
- MutableBufferSequence const& bs)
- : h_(std::forward<DeducedHandler>(h))
- , ws_(ws)
- , wg_(ws_.get_executor())
- , bs_(bs)
- , cb_(bs)
- , code_(close_code::none)
- {
- }
-
- using allocator_type =
- boost::asio::associated_allocator_t<Handler>;
-
- allocator_type
- get_allocator() const noexcept
- {
- return (boost::asio::get_associated_allocator)(h_);
- }
-
- using executor_type = boost::asio::associated_executor_t<
- Handler, decltype(std::declval<stream<NextLayer, deflateSupported>&>().get_executor())>;
-
- executor_type
- get_executor() const noexcept
- {
- return (boost::asio::get_associated_executor)(
- h_, ws_.get_executor());
- }
-
- Handler&
- handler()
- {
- return h_;
- }
-
- void operator()(
- error_code ec = {},
- std::size_t bytes_transferred = 0,
- bool cont = true);
-
- friend
- bool asio_handler_is_continuation(read_some_op* op)
- {
- using boost::asio::asio_handler_is_continuation;
- 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, bool deflateSupported>
-template<class MutableBufferSequence, class Handler>
-void
-stream<NextLayer, deflateSupported>::
-read_some_op<MutableBufferSequence, Handler>::
-operator()(
- error_code ec,
- std::size_t bytes_transferred,
- bool cont)
-{
- using beast::detail::clamp;
- using boost::asio::buffer;
- using boost::asio::buffer_size;
- cont_ = cont;
- BOOST_ASIO_CORO_REENTER(*this)
- {
- // Maybe suspend
- do_maybe_suspend:
- if(ws_.rd_block_.try_lock(this))
- {
- // Make sure the stream is not closed
- if( ws_.status_ == status::closed ||
- ws_.status_ == status::failed)
- {
- ec = boost::asio::error::operation_aborted;
- goto upcall;
- }
- }
- else
- {
- do_suspend:
- // Suspend
- BOOST_ASIO_CORO_YIELD
- ws_.paused_r_rd_.emplace(std::move(*this));
-
- // Acquire the read block
- ws_.rd_block_.lock(this);
-
- // Resume
- BOOST_ASIO_CORO_YIELD
- boost::asio::post(
- ws_.get_executor(), std::move(*this));
- BOOST_ASSERT(ws_.rd_block_.is_locked(this));
-
- // The only way to get read blocked is if
- // a `close_op` wrote a close frame
- BOOST_ASSERT(ws_.wr_close_);
- BOOST_ASSERT(ws_.status_ != status::open);
- ec = boost::asio::error::operation_aborted;
- goto upcall;
- }
-
- // if status_ == status::closing, we want to suspend
- // the read operation until the close completes,
- // then finish the read with operation_aborted.
-
- loop:
- 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
- //
- if(ws_.rd_remain_ == 0 &&
- (! ws_.rd_fh_.fin || ws_.rd_done_))
- {
- // Read frame header
- while(! ws_.parse_fh(
- ws_.rd_fh_, ws_.rd_buf_, result_))
- {
- if(result_)
- {
- // _Fail the WebSocket Connection_
- if(result_ == error::message_too_big)
- code_ = close_code::too_big;
- else
- code_ = close_code::protocol_error;
- goto close;
- }
- 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_.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
- ws_.rd_block_.unlock(this);
- if( ws_.paused_r_close_.maybe_invoke())
- {
- // Suspend
- BOOST_ASSERT(ws_.rd_block_.is_locked());
- goto do_suspend;
- }
- // Acquire read block
- 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_.mutable_data()),
- ws_.rd_key_);
- if(detail::is_control(ws_.rd_fh_.op))
- {
- // Clear this otherwise the next
- // frame will be considered final.
- ws_.rd_fh_.fin = false;
-
- // 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),
- ws_.rd_buf_.data());
- auto const len = buffer_size(b);
- BOOST_ASSERT(len == ws_.rd_fh_.len);
- ping_data payload;
- detail::read_ping(payload, b);
- ws_.rd_buf_.consume(len);
- // Ignore ping when closing
- if(ws_.status_ == status::closing)
- goto loop;
- if(ws_.ctrl_cb_)
- 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);
- }
-
- // Allow a close operation
- // to acquire the read block
- ws_.rd_block_.unlock(this);
- ws_.paused_r_close_.maybe_invoke();
-
- // Maybe suspend
- if(! ws_.wr_block_.try_lock(this))
- {
- // Suspend
- BOOST_ASIO_CORO_YIELD
- ws_.paused_rd_.emplace(std::move(*this));
-
- // Acquire the write block
- ws_.wr_block_.lock(this);
-
- // Resume
- BOOST_ASIO_CORO_YIELD
- boost::asio::post(
- ws_.get_executor(), std::move(*this));
- BOOST_ASSERT(ws_.wr_block_.is_locked(this));
-
- // Make sure the stream is open
- if(! ws_.check_open(ec))
- goto upcall;
- }
-
- // Send pong
- 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_.is_locked(this));
- if(! ws_.check_ok(ec))
- goto upcall;
- ws_.wr_block_.unlock(this);
- ws_.paused_close_.maybe_invoke() ||
- ws_.paused_ping_.maybe_invoke() ||
- ws_.paused_wr_.maybe_invoke();
- goto do_maybe_suspend;
- }
- // 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);
- ping_data payload;
- detail::read_ping(payload, cb);
- ws_.rd_buf_.consume(len);
- // Ignore pong when closing
- if(! ws_.wr_close_ && ws_.ctrl_cb_)
- ws_.ctrl_cb_(frame_type::pong, payload);
- goto loop;
- }
- // 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);
- BOOST_ASSERT(len == ws_.rd_fh_.len);
- BOOST_ASSERT(! ws_.rd_close_);
- ws_.rd_close_ = true;
- close_reason cr;
- detail::read_close(cr, cb, result_);
- if(result_)
- {
- // _Fail the WebSocket Connection_
- code_ = close_code::protocol_error;
- goto close;
- }
- ws_.cr_ = cr;
- ws_.rd_buf_.consume(len);
- if(ws_.ctrl_cb_)
- ws_.ctrl_cb_(frame_type::close,
- ws_.cr_.reason);
- // See if we are already closing
- if(ws_.status_ == status::closing)
- {
- // _Close the WebSocket Connection_
- BOOST_ASSERT(ws_.wr_close_);
- code_ = close_code::none;
- 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);
- result_ = error::closed;
- goto close;
- }
- }
- if(ws_.rd_fh_.len == 0 && ! ws_.rd_fh_.fin)
- {
- // Empty non-final frame
- goto loop;
- }
- ws_.rd_done_ = false;
- }
- if(! ws_.rd_deflated())
- {
- if(ws_.rd_remain_ > 0)
- {
- if(ws_.rd_buf_.size() == 0 && ws_.rd_buf_.max_size() >
- (std::min)(clamp(ws_.rd_remain_),
- buffer_size(cb_)))
- {
- // Fill the read buffer first, otherwise we
- // get fewer bytes at the cost of one I/O.
- 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));
- if(! ws_.check_ok(ec))
- goto upcall;
- ws_.rd_buf_.commit(bytes_transferred);
- if(ws_.rd_fh_.mask)
- detail::mask_inplace(buffers_prefix(clamp(
- ws_.rd_remain_), ws_.rd_buf_.mutable_data()),
- ws_.rd_key_);
- }
- if(ws_.rd_buf_.size() > 0)
- {
- // Copy from the read buffer.
- // The mask was already applied.
- bytes_transferred = buffer_copy(cb_,
- ws_.rd_buf_.data(), clamp(ws_.rd_remain_));
- auto const mb = buffers_prefix(
- bytes_transferred, cb_);
- ws_.rd_remain_ -= bytes_transferred;
- if(ws_.rd_op_ == detail::opcode::text)
- {
- if(! ws_.rd_utf8_.write(mb) ||
- (ws_.rd_remain_ == 0 && ws_.rd_fh_.fin &&
- ! ws_.rd_utf8_.finish()))
- {
- // _Fail the WebSocket Connection_
- code_ = close_code::bad_payload;
- result_ = error::bad_frame_payload;
- goto close;
- }
- }
- bytes_written_ += bytes_transferred;
- ws_.rd_size_ += bytes_transferred;
- ws_.rd_buf_.consume(bytes_transferred);
- }
- else
- {
- // Read into caller's buffer
- BOOST_ASSERT(ws_.rd_remain_ > 0);
- BOOST_ASSERT(buffer_size(cb_) > 0);
- BOOST_ASSERT(buffer_size(buffers_prefix(
- clamp(ws_.rd_remain_), cb_)) > 0);
- BOOST_ASIO_CORO_YIELD
- ws_.stream_.async_read_some(buffers_prefix(
- clamp(ws_.rd_remain_), cb_), std::move(*this));
- if(! ws_.check_ok(ec))
- goto upcall;
- BOOST_ASSERT(bytes_transferred > 0);
- auto const mb = buffers_prefix(
- bytes_transferred, cb_);
- ws_.rd_remain_ -= bytes_transferred;
- if(ws_.rd_fh_.mask)
- detail::mask_inplace(mb, ws_.rd_key_);
- if(ws_.rd_op_ == detail::opcode::text)
- {
- if(! ws_.rd_utf8_.write(mb) ||
- (ws_.rd_remain_ == 0 && ws_.rd_fh_.fin &&
- ! ws_.rd_utf8_.finish()))
- {
- // _Fail the WebSocket Connection_
- code_ = close_code::bad_payload;
- result_ = error::bad_frame_payload;
- goto close;
- }
- }
- bytes_written_ += bytes_transferred;
- ws_.rd_size_ += bytes_transferred;
- }
- }
- ws_.rd_done_ = ws_.rd_remain_ == 0 && ws_.rd_fh_.fin;
- }
- else
- {
- // Read compressed message frame payload:
- // inflate even if rd_fh_.len == 0, otherwise we
- // never emit the end-of-stream deflate block.
- while(buffer_size(cb_) > 0)
- {
- if( ws_.rd_remain_ > 0 &&
- ws_.rd_buf_.size() == 0 &&
- ! did_read_)
- {
- // read new
- 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));
- if(! ws_.check_ok(ec))
- goto upcall;
- BOOST_ASSERT(bytes_transferred > 0);
- ws_.rd_buf_.commit(bytes_transferred);
- if(ws_.rd_fh_.mask)
- detail::mask_inplace(
- buffers_prefix(clamp(ws_.rd_remain_),
- ws_.rd_buf_.mutable_data()), ws_.rd_key_);
- did_read_ = true;
- }
- zlib::z_params zs;
- {
- auto const out = buffers_front(cb_);
- zs.next_out = out.data();
- zs.avail_out = out.size();
- BOOST_ASSERT(zs.avail_out > 0);
- }
- if(ws_.rd_remain_ > 0)
- {
- if(ws_.rd_buf_.size() > 0)
- {
- // use what's there
- auto const in = buffers_prefix(
- clamp(ws_.rd_remain_), buffers_front(
- ws_.rd_buf_.data()));
- zs.avail_in = in.size();
- zs.next_in = in.data();
- }
- else
- {
- break;
- }
- }
- else if(ws_.rd_fh_.fin)
- {
- // append the empty block codes
- static std::uint8_t constexpr
- empty_block[4] = {
- 0x00, 0x00, 0xff, 0xff };
- zs.next_in = empty_block;
- zs.avail_in = sizeof(empty_block);
- ws_.inflate(zs, zlib::Flush::sync, ec);
- if(! ec)
- {
- // https://github.com/madler/zlib/issues/280
- if(zs.total_out > 0)
- ec = error::partial_deflate_block;
- }
- if(! ws_.check_ok(ec))
- goto upcall;
- ws_.do_context_takeover_read(ws_.role_);
- ws_.rd_done_ = true;
- break;
- }
- else
- {
- break;
- }
- ws_.inflate(zs, zlib::Flush::sync, ec);
- if(! ws_.check_ok(ec))
- goto upcall;
- if(ws_.rd_msg_max_ && beast::detail::sum_exceeds(
- ws_.rd_size_, zs.total_out, ws_.rd_msg_max_))
- {
- // _Fail the WebSocket Connection_
- code_ = close_code::too_big;
- result_ = error::message_too_big;
- goto close;
- }
- cb_.consume(zs.total_out);
- ws_.rd_size_ += zs.total_out;
- ws_.rd_remain_ -= zs.total_in;
- ws_.rd_buf_.consume(zs.total_in);
- bytes_written_ += zs.total_out;
- }
- if(ws_.rd_op_ == detail::opcode::text)
- {
- // check utf8
- if(! ws_.rd_utf8_.write(
- buffers_prefix(bytes_written_, bs_)) || (
- ws_.rd_done_ && ! ws_.rd_utf8_.finish()))
- {
- // _Fail the WebSocket Connection_
- code_ = close_code::bad_payload;
- result_ = error::bad_frame_payload;
- goto close;
- }
- }
- }
- goto upcall;
-
- close:
- // Try to acquire the write block
- if(! ws_.wr_block_.try_lock(this))
- {
- // Suspend
- BOOST_ASIO_CORO_YIELD
- ws_.paused_rd_.emplace(std::move(*this));
-
- // Acquire the write block
- ws_.wr_block_.lock(this);
-
- // Resume
- BOOST_ASIO_CORO_YIELD
- boost::asio::post(
- ws_.get_executor(), std::move(*this));
- BOOST_ASSERT(ws_.wr_block_.is_locked(this));
-
- // Make sure the stream is open
- if(! ws_.check_open(ec))
- goto upcall;
- }
-
- // Set the status
- ws_.status_ = status::closing;
-
- if(! ws_.wr_close_)
- {
- ws_.wr_close_ = true;
-
- // Serialize close frame
- ws_.rd_fb_.reset();
- ws_.template write_close<
- flat_static_buffer_base>(
- ws_.rd_fb_, code_);
-
- // Send close frame
- 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_.is_locked(this));
- if(! ws_.check_ok(ec))
- goto upcall;
- }
-
- // Teardown
- using beast::websocket::async_teardown;
- 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_.is_locked(this));
- if(ec == boost::asio::error::eof)
- {
- // Rationale:
- // http://stackoverflow.com/questions/25587403/boost-asio-ssl-async-shutdown-always-finishes-with-an-error
- ec.assign(0, ec.category());
- }
- if(! ec)
- ec = result_;
- if(ec && ec != error::closed)
- ws_.status_ = status::failed;
- else
- ws_.status_ = status::closed;
- ws_.close();
-
- upcall:
- ws_.rd_block_.try_unlock(this);
- ws_.paused_r_close_.maybe_invoke();
- if(ws_.wr_block_.try_unlock(this))
- ws_.paused_close_.maybe_invoke() ||
- ws_.paused_ping_.maybe_invoke() ||
- ws_.paused_wr_.maybe_invoke();
- if(! cont_)
- {
- BOOST_ASIO_CORO_YIELD
- boost::asio::post(
- ws_.get_executor(),
- bind_handler(std::move(*this),
- ec, bytes_written_));
- }
- h_(ec, bytes_written_);
- }
-}
-
-//------------------------------------------------------------------------------
-
-template<class NextLayer, bool deflateSupported>
-template<
- class DynamicBuffer,
- class Handler>
-class stream<NextLayer, deflateSupported>::read_op
- : public boost::asio::coroutine
-{
- Handler h_;
- stream<NextLayer, deflateSupported>& ws_;
- boost::asio::executor_work_guard<decltype(std::declval<
- stream<NextLayer, deflateSupported>&>().get_executor())> wg_;
- DynamicBuffer& b_;
- std::size_t limit_;
- std::size_t bytes_written_ = 0;
- bool some_;
-
-public:
- using allocator_type =
- boost::asio::associated_allocator_t<Handler>;
-
- read_op(read_op&&) = default;
- read_op(read_op const&) = delete;
-
- template<class DeducedHandler>
- read_op(
- DeducedHandler&& h,
- stream<NextLayer, deflateSupported>& ws,
- DynamicBuffer& b,
- std::size_t limit,
- bool some)
- : h_(std::forward<DeducedHandler>(h))
- , ws_(ws)
- , wg_(ws_.get_executor())
- , b_(b)
- , limit_(limit ? limit : (
- std::numeric_limits<std::size_t>::max)())
- , some_(some)
- {
- }
-
- allocator_type
- get_allocator() const noexcept
- {
- return (boost::asio::get_associated_allocator)(h_);
- }
-
- using executor_type = boost::asio::associated_executor_t<
- Handler, decltype(std::declval<stream<NextLayer, deflateSupported>&>().get_executor())>;
-
- executor_type
- get_executor() const noexcept
- {
- return (boost::asio::get_associated_executor)(
- h_, ws_.get_executor());
- }
-
- void operator()(
- error_code ec = {},
- std::size_t bytes_transferred = 0);
-
- friend
- bool asio_handler_is_continuation(read_op* op)
- {
- using boost::asio::asio_handler_is_continuation;
- 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, bool deflateSupported>
-template<class DynamicBuffer, class Handler>
-void
-stream<NextLayer, deflateSupported>::
-read_op<DynamicBuffer, Handler>::
-operator()(
- error_code ec,
- std::size_t bytes_transferred)
-{
- using beast::detail::clamp;
- BOOST_ASIO_CORO_REENTER(*this)
- {
- do
- {
- BOOST_ASIO_CORO_YIELD
- {
- auto mb = beast::detail::dynamic_buffer_prepare(b_,
- clamp(ws_.read_size_hint(b_), limit_),
- ec, error::buffer_overflow);
- if(ec)
- boost::asio::post(
- ws_.get_executor(),
- bind_handler(
- std::move(*this), ec, 0));
- else
- read_some_op<typename
- DynamicBuffer::mutable_buffers_type,
- read_op>(std::move(*this), ws_, *mb)(
- {}, 0, false);
- return;
- }
- if(ec)
- break;
- b_.commit(bytes_transferred);
- bytes_written_ += bytes_transferred;
- }
- while(! some_ && ! ws_.is_message_done());
- h_(ec, bytes_written_);
- }
-}
-
-//------------------------------------------------------------------------------
-
-template<class NextLayer, bool deflateSupported>
-template<class DynamicBuffer>
-std::size_t
-stream<NextLayer, deflateSupported>::
-read(DynamicBuffer& buffer)
-{
- static_assert(is_sync_stream<next_layer_type>::value,
- "SyncStream requirements not met");
- static_assert(
- boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
- "DynamicBuffer requirements not met");
- error_code ec;
- auto const bytes_written = read(buffer, ec);
- if(ec)
- BOOST_THROW_EXCEPTION(system_error{ec});
- return bytes_written;
-}
-
-template<class NextLayer, bool deflateSupported>
-template<class DynamicBuffer>
-std::size_t
-stream<NextLayer, deflateSupported>::
-read(DynamicBuffer& buffer, error_code& ec)
-{
- static_assert(is_sync_stream<next_layer_type>::value,
- "SyncStream requirements not met");
- static_assert(
- boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
- "DynamicBuffer requirements not met");
- std::size_t bytes_written = 0;
- do
- {
- bytes_written += read_some(buffer, 0, ec);
- if(ec)
- return bytes_written;
- }
- while(! is_message_done());
- return bytes_written;
-}
-
-template<class NextLayer, bool deflateSupported>
-template<class DynamicBuffer, class ReadHandler>
-BOOST_ASIO_INITFN_RESULT_TYPE(
- ReadHandler, void(error_code, std::size_t))
-stream<NextLayer, deflateSupported>::
-async_read(DynamicBuffer& buffer, ReadHandler&& handler)
-{
- static_assert(is_async_stream<next_layer_type>::value,
- "AsyncStream requirements not met");
- static_assert(
- boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
- "DynamicBuffer requirements not met");
- 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))>{
- std::move(init.completion_handler),
- *this,
- buffer,
- 0,
- false}();
- return init.result.get();
-}
-
-//------------------------------------------------------------------------------
-
-template<class NextLayer, bool deflateSupported>
-template<class DynamicBuffer>
-std::size_t
-stream<NextLayer, deflateSupported>::
-read_some(
- DynamicBuffer& buffer,
- std::size_t limit)
-{
- static_assert(is_sync_stream<next_layer_type>::value,
- "SyncStream requirements not met");
- static_assert(
- boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
- "DynamicBuffer requirements not met");
- error_code ec;
- auto const bytes_written =
- read_some(buffer, limit, ec);
- if(ec)
- BOOST_THROW_EXCEPTION(system_error{ec});
- return bytes_written;
-}
-
-template<class NextLayer, bool deflateSupported>
-template<class DynamicBuffer>
-std::size_t
-stream<NextLayer, deflateSupported>::
-read_some(
- DynamicBuffer& buffer,
- std::size_t limit,
- error_code& ec)
-{
- static_assert(is_sync_stream<next_layer_type>::value,
- "SyncStream requirements not met");
- static_assert(
- boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
- "DynamicBuffer requirements not met");
- using beast::detail::clamp;
- if(! limit)
- limit = (std::numeric_limits<std::size_t>::max)();
- auto const size =
- clamp(read_size_hint(buffer), limit);
- BOOST_ASSERT(size > 0);
- auto mb = beast::detail::dynamic_buffer_prepare(
- buffer, size, ec, error::buffer_overflow);
- if(ec)
- return 0;
- auto const bytes_written = read_some(*mb, ec);
- buffer.commit(bytes_written);
- return bytes_written;
-}
-
-template<class NextLayer, bool deflateSupported>
-template<class DynamicBuffer, class ReadHandler>
-BOOST_ASIO_INITFN_RESULT_TYPE(
- ReadHandler, void(error_code, std::size_t))
-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 not met");
- static_assert(
- boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
- "DynamicBuffer requirements not met");
- 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))>{
- std::move(init.completion_handler),
- *this,
- buffer,
- limit,
- true}({}, 0);
- return init.result.get();
-}
-
-//------------------------------------------------------------------------------
-
-template<class NextLayer, bool deflateSupported>
-template<class MutableBufferSequence>
-std::size_t
-stream<NextLayer, deflateSupported>::
-read_some(
- MutableBufferSequence const& buffers)
-{
- static_assert(is_sync_stream<next_layer_type>::value,
- "SyncStream requirements not met");
- static_assert(boost::asio::is_mutable_buffer_sequence<
- MutableBufferSequence>::value,
- "MutableBufferSequence requirements not met");
- error_code ec;
- auto const bytes_written = read_some(buffers, ec);
- if(ec)
- BOOST_THROW_EXCEPTION(system_error{ec});
- return bytes_written;
-}
-
-template<class NextLayer, bool deflateSupported>
-template<class MutableBufferSequence>
-std::size_t
-stream<NextLayer, deflateSupported>::
-read_some(
- MutableBufferSequence const& buffers,
- error_code& ec)
-{
- static_assert(is_sync_stream<next_layer_type>::value,
- "SyncStream requirements not met");
- static_assert(boost::asio::is_mutable_buffer_sequence<
- MutableBufferSequence>::value,
- "MutableBufferSequence requirements not met");
- using beast::detail::clamp;
- using boost::asio::buffer;
- using boost::asio::buffer_size;
- close_code code{};
- std::size_t bytes_written = 0;
- ec.assign(0, ec.category());
- // Make sure the stream is open
- if(! check_open(ec))
- return 0;
-loop:
- // 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
- //
- if(rd_remain_ == 0 && (! rd_fh_.fin || rd_done_))
- {
- // Read frame header
- error_code result;
- while(! parse_fh(rd_fh_, rd_buf_, result))
- {
- if(result)
- {
- // _Fail the WebSocket Connection_
- 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 =
- stream_.read_some(
- rd_buf_.prepare(read_size(
- rd_buf_, rd_buf_.max_size())),
- ec);
- if(! check_ok(ec))
- return bytes_written;
- rd_buf_.commit(bytes_transferred);
- }
- // Immediately apply the mask to the portion
- // 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_.mutable_data()),
- rd_key_);
- if(detail::is_control(rd_fh_.op))
- {
- // Get control frame payload
- auto const b = buffers_prefix(
- clamp(rd_fh_.len), rd_buf_.data());
- auto const len = buffer_size(b);
- BOOST_ASSERT(len == rd_fh_.len);
-
- // Clear this otherwise the next
- // frame will be considered final.
- rd_fh_.fin = false;
-
- // Handle ping frame
- if(rd_fh_.op == detail::opcode::ping)
- {
- ping_data payload;
- detail::read_ping(payload, b);
- rd_buf_.consume(len);
- if(wr_close_)
- {
- // Ignore ping when closing
- goto loop;
- }
- if(ctrl_cb_)
- ctrl_cb_(frame_type::ping, payload);
- detail::frame_buffer fb;
- write_ping<flat_static_buffer_base>(fb,
- detail::opcode::pong, payload);
- boost::asio::write(stream_, fb.data(), ec);
- if(! check_ok(ec))
- return bytes_written;
- goto loop;
- }
- // Handle pong frame
- if(rd_fh_.op == detail::opcode::pong)
- {
- ping_data payload;
- detail::read_ping(payload, b);
- rd_buf_.consume(len);
- if(ctrl_cb_)
- ctrl_cb_(frame_type::pong, payload);
- goto loop;
- }
- // Handle close frame
- BOOST_ASSERT(rd_fh_.op == detail::opcode::close);
- {
- BOOST_ASSERT(! rd_close_);
- rd_close_ = true;
- close_reason cr;
- detail::read_close(cr, b, result);
- if(result)
- {
- // _Fail the WebSocket Connection_
- do_fail(close_code::protocol_error,
- result, ec);
- return bytes_written;
- }
- cr_ = cr;
- rd_buf_.consume(len);
- if(ctrl_cb_)
- ctrl_cb_(frame_type::close, cr_.reason);
- BOOST_ASSERT(! wr_close_);
- // _Start the WebSocket Closing Handshake_
- do_fail(
- cr.code == close_code::none ?
- close_code::normal :
- static_cast<close_code>(cr.code),
- error::closed, ec);
- return bytes_written;
- }
- }
- if(rd_fh_.len == 0 && ! rd_fh_.fin)
- {
- // Empty non-final frame
- goto loop;
- }
- rd_done_ = false;
- }
- else
- {
- ec.assign(0, ec.category());
- }
- if(! this->rd_deflated())
- {
- if(rd_remain_ > 0)
- {
- if(rd_buf_.size() == 0 && rd_buf_.max_size() >
- (std::min)(clamp(rd_remain_),
- buffer_size(buffers)))
- {
- // Fill the read buffer first, otherwise we
- // get fewer bytes at the cost of one I/O.
- rd_buf_.commit(stream_.read_some(
- rd_buf_.prepare(read_size(rd_buf_,
- rd_buf_.max_size())), ec));
- if(! check_ok(ec))
- return bytes_written;
- if(rd_fh_.mask)
- detail::mask_inplace(
- buffers_prefix(clamp(rd_remain_),
- rd_buf_.mutable_data()), rd_key_);
- }
- if(rd_buf_.size() > 0)
- {
- // Copy from the read buffer.
- // The mask was already applied.
- auto const bytes_transferred =
- buffer_copy(buffers, rd_buf_.data(),
- clamp(rd_remain_));
- auto const mb = buffers_prefix(
- bytes_transferred, buffers);
- rd_remain_ -= bytes_transferred;
- if(rd_op_ == detail::opcode::text)
- {
- if(! rd_utf8_.write(mb) ||
- (rd_remain_ == 0 && rd_fh_.fin &&
- ! rd_utf8_.finish()))
- {
- // _Fail the WebSocket Connection_
- do_fail(close_code::bad_payload,
- error::bad_frame_payload, ec);
- return bytes_written;
- }
- }
- bytes_written += bytes_transferred;
- rd_size_ += bytes_transferred;
- rd_buf_.consume(bytes_transferred);
- }
- else
- {
- // Read into caller's buffer
- BOOST_ASSERT(rd_remain_ > 0);
- BOOST_ASSERT(buffer_size(buffers) > 0);
- BOOST_ASSERT(buffer_size(buffers_prefix(
- clamp(rd_remain_), buffers)) > 0);
- auto const bytes_transferred =
- stream_.read_some(buffers_prefix(
- clamp(rd_remain_), buffers), ec);
- if(! check_ok(ec))
- return bytes_written;
- BOOST_ASSERT(bytes_transferred > 0);
- auto const mb = buffers_prefix(
- bytes_transferred, buffers);
- rd_remain_ -= bytes_transferred;
- if(rd_fh_.mask)
- detail::mask_inplace(mb, rd_key_);
- if(rd_op_ == detail::opcode::text)
- {
- if(! rd_utf8_.write(mb) ||
- (rd_remain_ == 0 && rd_fh_.fin &&
- ! rd_utf8_.finish()))
- {
- // _Fail the WebSocket Connection_
- do_fail(close_code::bad_payload,
- error::bad_frame_payload, ec);
- return bytes_written;
- }
- }
- bytes_written += bytes_transferred;
- rd_size_ += bytes_transferred;
- }
- }
- rd_done_ = rd_remain_ == 0 && rd_fh_.fin;
- }
- else
- {
- // Read compressed message frame payload:
- // inflate even if rd_fh_.len == 0, otherwise we
- // never emit the end-of-stream deflate block.
- //
- bool did_read = false;
- buffers_suffix<MutableBufferSequence> cb{buffers};
- while(buffer_size(cb) > 0)
- {
- zlib::z_params zs;
- {
- auto const out = buffers_front(cb);
- zs.next_out = out.data();
- zs.avail_out = out.size();
- BOOST_ASSERT(zs.avail_out > 0);
- }
- if(rd_remain_ > 0)
- {
- if(rd_buf_.size() > 0)
- {
- // use what's there
- auto const in = buffers_prefix(
- clamp(rd_remain_), buffers_front(
- rd_buf_.data()));
- zs.avail_in = in.size();
- zs.next_in = in.data();
- }
- else if(! did_read)
- {
- // read new
- auto const bytes_transferred =
- stream_.read_some(
- rd_buf_.prepare(read_size(
- rd_buf_, rd_buf_.max_size())),
- ec);
- if(! check_ok(ec))
- return bytes_written;
- BOOST_ASSERT(bytes_transferred > 0);
- rd_buf_.commit(bytes_transferred);
- if(rd_fh_.mask)
- detail::mask_inplace(
- buffers_prefix(clamp(rd_remain_),
- rd_buf_.mutable_data()), rd_key_);
- auto const in = buffers_prefix(
- clamp(rd_remain_), buffers_front(
- rd_buf_.data()));
- zs.avail_in = in.size();
- zs.next_in = in.data();
- did_read = true;
- }
- else
- {
- break;
- }
- }
- else if(rd_fh_.fin)
- {
- // append the empty block codes
- static std::uint8_t constexpr
- empty_block[4] = {
- 0x00, 0x00, 0xff, 0xff };
- zs.next_in = empty_block;
- zs.avail_in = sizeof(empty_block);
- this->inflate(zs, zlib::Flush::sync, ec);
- if(! ec)
- {
- // https://github.com/madler/zlib/issues/280
- if(zs.total_out > 0)
- ec = error::partial_deflate_block;
- }
- if(! check_ok(ec))
- return bytes_written;
- this->do_context_takeover_read(role_);
- rd_done_ = true;
- break;
- }
- else
- {
- break;
- }
- 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::message_too_big, ec);
- return bytes_written;
- }
- cb.consume(zs.total_out);
- rd_size_ += zs.total_out;
- rd_remain_ -= zs.total_in;
- rd_buf_.consume(zs.total_in);
- bytes_written += zs.total_out;
- }
- if(rd_op_ == detail::opcode::text)
- {
- // check utf8
- if(! rd_utf8_.write(
- buffers_prefix(bytes_written, buffers)) || (
- rd_done_ && ! rd_utf8_.finish()))
- {
- // _Fail the WebSocket Connection_
- do_fail(close_code::bad_payload,
- error::bad_frame_payload, ec);
- return bytes_written;
- }
- }
- }
- return bytes_written;
-}
-
-template<class NextLayer, bool deflateSupported>
-template<class MutableBufferSequence, class ReadHandler>
-BOOST_ASIO_INITFN_RESULT_TYPE(
- ReadHandler, void(error_code, std::size_t))
-stream<NextLayer, deflateSupported>::
-async_read_some(
- MutableBufferSequence const& buffers,
- ReadHandler&& handler)
-{
- static_assert(is_async_stream<next_layer_type>::value,
- "AsyncStream requirements not met");
- static_assert(boost::asio::is_mutable_buffer_sequence<
- MutableBufferSequence>::value,
- "MutableBufferSequence requirements not met");
- 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))>{
- std::move(init.completion_handler), *this, buffers}(
- {}, 0, false);
- return init.result.get();
-}
-
-} // websocket
-} // beast
-} // boost
-
-#endif
diff --git a/boost/beast/websocket/impl/rfc6455.ipp b/boost/beast/websocket/impl/rfc6455.hpp
index 07fdc30686..d7b80810cf 100644
--- a/boost/beast/websocket/impl/rfc6455.ipp
+++ b/boost/beast/websocket/impl/rfc6455.hpp
@@ -1,5 +1,5 @@
//
-// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+// Copyright (c) 2016-2019 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)
@@ -7,8 +7,8 @@
// Official repository: https://github.com/boostorg/beast
//
-#ifndef BOOST_BEAST_WEBSOCKET_IMPL_RFC6455_IPP
-#define BOOST_BEAST_WEBSOCKET_IMPL_RFC6455_IPP
+#ifndef BOOST_BEAST_WEBSOCKET_IMPL_RFC6455_HPP
+#define BOOST_BEAST_WEBSOCKET_IMPL_RFC6455_HPP
#include <boost/beast/http/fields.hpp>
#include <boost/beast/http/rfc7230.hpp>
diff --git a/boost/beast/websocket/impl/ssl.ipp b/boost/beast/websocket/impl/ssl.hpp
index 442b45086a..f553022619 100644
--- a/boost/beast/websocket/impl/ssl.ipp
+++ b/boost/beast/websocket/impl/ssl.hpp
@@ -1,5 +1,5 @@
//
-// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+// Copyright (c) 2016-2019 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)
@@ -7,21 +7,20 @@
// Official repository: https://github.com/boostorg/beast
//
-#ifndef BOOST_BEAST_WEBSOCKET_IMPL_SSL_IPP_INCLUDED
-#define BOOST_BEAST_WEBSOCKET_IMPL_SSL_IPP_INCLUDED
+#ifndef BOOST_BEAST_WEBSOCKET_IMPL_SSL_HPP
+#define BOOST_BEAST_WEBSOCKET_IMPL_SSL_HPP
#include <utility>
namespace boost {
namespace beast {
-namespace websocket {
/*
-See
-http://stackoverflow.com/questions/32046034/what-is-the-proper-way-to-securely-disconnect-an-asio-ssl-socket/32054476#32054476
+ See
+ http://stackoverflow.com/questions/32046034/what-is-the-proper-way-to-securely-disconnect-an-asio-ssl-socket/32054476#32054476
-Behavior of ssl::stream regarding close_
+ Behavior of ssl::stream regarding close_notify
If the remote host calls async_shutdown then the
local host's async_read will complete with eof.
@@ -35,7 +34,7 @@ template<class AsyncStream>
void
teardown(
role_type,
- boost::asio::ssl::stream<AsyncStream>& stream,
+ net::ssl::stream<AsyncStream>& stream,
error_code& ec)
{
stream.shutdown(ec);
@@ -47,14 +46,13 @@ template<
void
async_teardown(
role_type,
- boost::asio::ssl::stream<AsyncStream>& stream,
+ net::ssl::stream<AsyncStream>& stream,
TeardownHandler&& handler)
{
stream.async_shutdown(
std::forward<TeardownHandler>(handler));
}
-} // websocket
} // beast
} // boost
diff --git a/boost/beast/websocket/impl/stream.hpp b/boost/beast/websocket/impl/stream.hpp
new file mode 100644
index 0000000000..150748c453
--- /dev/null
+++ b/boost/beast/websocket/impl/stream.hpp
@@ -0,0 +1,359 @@
+//
+// Copyright (c) 2016-2019 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_IMPL_STREAM_HPP
+#define BOOST_BEAST_WEBSOCKET_IMPL_STREAM_HPP
+
+#include <boost/beast/core/buffer_traits.hpp>
+#include <boost/beast/websocket/rfc6455.hpp>
+#include <boost/beast/websocket/teardown.hpp>
+#include <boost/beast/websocket/detail/hybi13.hpp>
+#include <boost/beast/websocket/detail/mask.hpp>
+#include <boost/beast/websocket/impl/stream_impl.hpp>
+#include <boost/beast/version.hpp>
+#include <boost/beast/http/read.hpp>
+#include <boost/beast/http/write.hpp>
+#include <boost/beast/http/rfc7230.hpp>
+#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/flat_static_buffer.hpp>
+#include <boost/beast/core/detail/clamp.hpp>
+#include <boost/beast/core/detail/type_traits.hpp>
+#include <boost/asio/bind_executor.hpp>
+#include <boost/asio/steady_timer.hpp>
+#include <boost/assert.hpp>
+#include <boost/endian/buffers.hpp>
+#include <boost/make_shared.hpp>
+#include <boost/throw_exception.hpp>
+#include <algorithm>
+#include <chrono>
+#include <memory>
+#include <stdexcept>
+#include <utility>
+
+namespace boost {
+namespace beast {
+namespace websocket {
+
+template<class NextLayer, bool deflateSupported>
+stream<NextLayer, deflateSupported>::
+~stream()
+{
+ if(impl_)
+ impl_->remove();
+}
+
+template<class NextLayer, bool deflateSupported>
+template<class... Args>
+stream<NextLayer, deflateSupported>::
+stream(Args&&... args)
+ : impl_(boost::make_shared<impl_type>(
+ std::forward<Args>(args)...))
+{
+ BOOST_ASSERT(impl_->rd_buf.max_size() >=
+ max_control_frame_size);
+}
+
+template<class NextLayer, bool deflateSupported>
+auto
+stream<NextLayer, deflateSupported>::
+get_executor() const noexcept ->
+ executor_type
+{
+ return impl_->stream().get_executor();
+}
+
+template<class NextLayer, bool deflateSupported>
+auto
+stream<NextLayer, deflateSupported>::
+next_layer() noexcept ->
+ next_layer_type&
+{
+ return impl_->stream();
+}
+
+template<class NextLayer, bool deflateSupported>
+auto
+stream<NextLayer, deflateSupported>::
+next_layer() const noexcept ->
+ next_layer_type const&
+{
+ return impl_->stream();
+}
+
+template<class NextLayer, bool deflateSupported>
+bool
+stream<NextLayer, deflateSupported>::
+is_open() const noexcept
+{
+ return impl_->status_ == status::open;
+}
+
+template<class NextLayer, bool deflateSupported>
+bool
+stream<NextLayer, deflateSupported>::
+got_binary() const noexcept
+{
+ return impl_->rd_op == detail::opcode::binary;
+}
+
+template<class NextLayer, bool deflateSupported>
+bool
+stream<NextLayer, deflateSupported>::
+is_message_done() const noexcept
+{
+ return impl_->rd_done;
+}
+
+template<class NextLayer, bool deflateSupported>
+close_reason const&
+stream<NextLayer, deflateSupported>::
+reason() const noexcept
+{
+ return impl_->cr;
+}
+
+template<class NextLayer, bool deflateSupported>
+std::size_t
+stream<NextLayer, deflateSupported>::
+read_size_hint(
+ std::size_t initial_size) const
+{
+ return impl_->read_size_hint_pmd(
+ initial_size, impl_->rd_done,
+ impl_->rd_remain, impl_->rd_fh);
+}
+
+template<class NextLayer, bool deflateSupported>
+template<class DynamicBuffer, class>
+std::size_t
+stream<NextLayer, deflateSupported>::
+read_size_hint(DynamicBuffer& buffer) const
+{
+ static_assert(
+ net::is_dynamic_buffer<DynamicBuffer>::value,
+ "DynamicBuffer type requirements not met");
+ auto const initial_size = (std::min)(
+ +tcp_frame_size,
+ buffer.max_size() - buffer.size());
+ if(initial_size == 0)
+ return 1; // buffer is full
+ return read_size_hint(initial_size);
+}
+
+//------------------------------------------------------------------------------
+//
+// Settings
+//
+//------------------------------------------------------------------------------
+
+// decorator
+
+template<class NextLayer, bool deflateSupported>
+void
+stream<NextLayer, deflateSupported>::
+set_option(decorator opt)
+{
+ impl_->decorator_opt = std::move(opt.d_);
+}
+
+// timeout
+
+template<class NextLayer, bool deflateSupported>
+void
+stream<NextLayer, deflateSupported>::
+get_option(timeout& opt)
+{
+ opt = impl_->timeout_opt;
+}
+
+template<class NextLayer, bool deflateSupported>
+void
+stream<NextLayer, deflateSupported>::
+set_option(timeout const& opt)
+{
+ impl_->set_option(opt);
+}
+
+//
+
+template<class NextLayer, bool deflateSupported>
+void
+stream<NextLayer, deflateSupported>::
+set_option(permessage_deflate const& o)
+{
+ impl_->set_option_pmd(o);
+}
+
+template<class NextLayer, bool deflateSupported>
+void
+stream<NextLayer, deflateSupported>::
+get_option(permessage_deflate& o)
+{
+ impl_->get_option_pmd(o);
+}
+
+template<class NextLayer, bool deflateSupported>
+void
+stream<NextLayer, deflateSupported>::
+auto_fragment(bool value)
+{
+ impl_->wr_frag_opt = value;
+}
+
+template<class NextLayer, bool deflateSupported>
+bool
+stream<NextLayer, deflateSupported>::
+auto_fragment() const
+{
+ return impl_->wr_frag_opt;
+}
+
+template<class NextLayer, bool deflateSupported>
+void
+stream<NextLayer, deflateSupported>::
+binary(bool value)
+{
+ impl_->wr_opcode = value ?
+ detail::opcode::binary :
+ detail::opcode::text;
+}
+
+template<class NextLayer, bool deflateSupported>
+bool
+stream<NextLayer, deflateSupported>::
+binary() const
+{
+ return impl_->wr_opcode == detail::opcode::binary;
+}
+
+template<class NextLayer, bool deflateSupported>
+void
+stream<NextLayer, deflateSupported>::
+control_callback(std::function<
+ void(frame_type, string_view)> cb)
+{
+ impl_->ctrl_cb = std::move(cb);
+}
+
+template<class NextLayer, bool deflateSupported>
+void
+stream<NextLayer, deflateSupported>::
+control_callback()
+{
+ impl_->ctrl_cb = {};
+}
+
+template<class NextLayer, bool deflateSupported>
+void
+stream<NextLayer, deflateSupported>::
+read_message_max(std::size_t amount)
+{
+ impl_->rd_msg_max = amount;
+}
+
+template<class NextLayer, bool deflateSupported>
+std::size_t
+stream<NextLayer, deflateSupported>::
+read_message_max() const
+{
+ return impl_->rd_msg_max;
+}
+
+template<class NextLayer, bool deflateSupported>
+void
+stream<NextLayer, deflateSupported>::
+secure_prng(bool value)
+{
+ this->impl_->secure_prng_ = value;
+}
+
+template<class NextLayer, bool deflateSupported>
+void
+stream<NextLayer, deflateSupported>::
+write_buffer_bytes(std::size_t amount)
+{
+ if(amount < 8)
+ BOOST_THROW_EXCEPTION(std::invalid_argument{
+ "write buffer size underflow"});
+ impl_->wr_buf_opt = amount;
+}
+
+template<class NextLayer, bool deflateSupported>
+std::size_t
+stream<NextLayer, deflateSupported>::
+write_buffer_bytes() const
+{
+ return impl_->wr_buf_opt;
+}
+
+template<class NextLayer, bool deflateSupported>
+void
+stream<NextLayer, deflateSupported>::
+text(bool value)
+{
+ impl_->wr_opcode = value ?
+ detail::opcode::text :
+ detail::opcode::binary;
+}
+
+template<class NextLayer, bool deflateSupported>
+bool
+stream<NextLayer, deflateSupported>::
+text() const
+{
+ return impl_->wr_opcode == detail::opcode::text;
+}
+
+//------------------------------------------------------------------------------
+
+// _Fail the WebSocket Connection_
+template<class NextLayer, bool deflateSupported>
+void
+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
+ error_code& ec) // set to the error, else set to ev
+{
+ BOOST_ASSERT(ev);
+ impl_->change_status(status::closing);
+ if(code != close_code::none && ! impl_->wr_close)
+ {
+ impl_->wr_close = true;
+ detail::frame_buffer fb;
+ impl_->template write_close<
+ flat_static_buffer_base>(fb, code);
+ net::write(impl_->stream(), fb.data(), ec);
+ if(impl_->check_stop_now(ec))
+ return;
+ }
+ using beast::websocket::teardown;
+ teardown(impl_->role, impl_->stream(), ec);
+ if(ec == net::error::eof)
+ {
+ // Rationale:
+ // http://stackoverflow.com/questions/25587403/boost-asio-ssl-async-shutdown-always-finishes-with-an-error
+ ec = {};
+ }
+ if(! ec)
+ ec = ev;
+ if(ec && ec != error::closed)
+ impl_->change_status(status::failed);
+ else
+ impl_->change_status(status::closed);
+ impl_->close();
+}
+
+} // websocket
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/websocket/impl/stream.ipp b/boost/beast/websocket/impl/stream.ipp
deleted file mode 100644
index 4aa729956b..0000000000
--- a/boost/beast/websocket/impl/stream.ipp
+++ /dev/null
@@ -1,894 +0,0 @@
-//
-// 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_IMPL_STREAM_IPP
-#define BOOST_BEAST_WEBSOCKET_IMPL_STREAM_IPP
-
-#include <boost/beast/websocket/rfc6455.hpp>
-#include <boost/beast/websocket/teardown.hpp>
-#include <boost/beast/websocket/detail/hybi13.hpp>
-#include <boost/beast/websocket/detail/pmd_extension.hpp>
-#include <boost/beast/version.hpp>
-#include <boost/beast/http/read.hpp>
-#include <boost/beast/http/write.hpp>
-#include <boost/beast/http/rfc7230.hpp>
-#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/flat_static_buffer.hpp>
-#include <boost/beast/core/type_traits.hpp>
-#include <boost/beast/core/detail/clamp.hpp>
-#include <boost/beast/core/detail/type_traits.hpp>
-#include <boost/assert.hpp>
-#include <boost/endian/buffers.hpp>
-#include <boost/make_unique.hpp>
-#include <boost/throw_exception.hpp>
-#include <algorithm>
-#include <memory>
-#include <stdexcept>
-#include <utility>
-
-#include <iostream>
-
-namespace boost {
-namespace beast {
-namespace websocket {
-
-template<class NextLayer, bool deflateSupported>
-template<class... Args>
-stream<NextLayer, deflateSupported>::
-stream(Args&&... args)
- : stream_(std::forward<Args>(args)...)
-{
- BOOST_ASSERT(rd_buf_.max_size() >=
- max_control_frame_size);
-}
-
-template<class NextLayer, bool deflateSupported>
-template<class DynamicBuffer, class>
-std::size_t
-stream<NextLayer, deflateSupported>::
-read_size_hint(DynamicBuffer& buffer) const
-{
- static_assert(
- boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
- "DynamicBuffer requirements not met");
- auto const initial_size = (std::min)(
- +tcp_frame_size,
- buffer.max_size() - buffer.size());
- if(initial_size == 0)
- return 1; // buffer is full
- return read_size_hint(initial_size);
-}
-
-//------------------------------------------------------------------------------
-
-template<class NextLayer, bool deflateSupported>
-void
-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)
- BOOST_THROW_EXCEPTION(std::invalid_argument{
- "invalid server_max_window_bits"});
- if( o.client_max_window_bits > 15 ||
- o.client_max_window_bits < 9)
- BOOST_THROW_EXCEPTION(std::invalid_argument{
- "invalid client_max_window_bits"});
- if( o.compLevel < 0 ||
- o.compLevel > 9)
- BOOST_THROW_EXCEPTION(std::invalid_argument{
- "invalid compLevel"});
- if( o.memLevel < 1 ||
- o.memLevel > 9)
- BOOST_THROW_EXCEPTION(std::invalid_argument{
- "invalid memLevel"});
- 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, bool deflateSupported>
-void
-stream<NextLayer, deflateSupported>::
-open(role_type role)
-{
- // VFALCO TODO analyze and remove dupe code in reset()
- role_ = role;
- status_ = status::open;
- rd_remain_ = 0;
- rd_cont_ = false;
- rd_done_ = true;
- // Can't clear this because accept uses it
- //rd_buf_.reset();
- 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;
-
- wr_cont_ = false;
- wr_buf_size_ = 0;
-
- 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(this->pmd_config_);
- this->pmd_.reset(new typename
- detail::stream_base<deflateSupported>::pmd_type);
- if(role_ == role_type::client)
- {
- 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
- {
- 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, bool deflateSupported>
-void
-stream<NextLayer, deflateSupported>::
-close()
-{
- wr_buf_.reset();
- close_pmd(is_deflate_supported{});
-}
-
-template<class NextLayer, bool deflateSupported>
-void
-stream<NextLayer, deflateSupported>::
-reset()
-{
- BOOST_ASSERT(status_ != status::open);
- rd_remain_ = 0;
- rd_cont_ = false;
- rd_done_ = true;
- rd_buf_.consume(rd_buf_.size());
- rd_fh_.fin = false;
- 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, bool deflateSupported>
-inline
-void
-stream<NextLayer, deflateSupported>::
-begin_msg(std::true_type)
-{
- wr_frag_ = wr_frag_opt_;
- wr_compress_ = static_cast<bool>(this->pmd_);
-
- // Maintain the write buffer
- if( wr_compress_ ||
- 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();
- }
-}
-
-// 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, bool deflateSupported>
-template<class DynamicBuffer>
-bool
-stream<NextLayer, deflateSupported>::
-parse_fh(
- detail::frame_header& fh,
- DynamicBuffer& b,
- error_code& ec)
-{
- using boost::asio::buffer;
- using boost::asio::buffer_copy;
- using boost::asio::buffer_size;
- if(buffer_size(b.data()) < 2)
- {
- // need more bytes
- ec.assign(0, ec.category());
- return false;
- }
- buffers_suffix<typename
- DynamicBuffer::const_buffers_type> cb{
- b.data()};
- std::size_t need;
- {
- std::uint8_t tmp[2];
- cb.consume(buffer_copy(buffer(tmp), cb));
- fh.len = tmp[1] & 0x7f;
- switch(fh.len)
- {
- case 126: need = 2; break;
- case 127: need = 8; break;
- default:
- need = 0;
- }
- fh.mask = (tmp[1] & 0x80) != 0;
- if(fh.mask)
- need += 4;
- if(buffer_size(cb) < need)
- {
- // need more bytes
- ec.assign(0, ec.category());
- return false;
- }
- fh.op = static_cast<
- detail::opcode>(tmp[0] & 0x0f);
- fh.fin = (tmp[0] & 0x80) != 0;
- fh.rsv1 = (tmp[0] & 0x40) != 0;
- fh.rsv2 = (tmp[0] & 0x20) != 0;
- fh.rsv3 = (tmp[0] & 0x10) != 0;
- }
- switch(fh.op)
- {
- case detail::opcode::binary:
- case detail::opcode::text:
- if(rd_cont_)
- {
- // new data frame when continuation expected
- ec = error::bad_data_frame;
- return false;
- }
- if(fh.rsv2 || fh.rsv3 ||
- ! this->rd_deflated(fh.rsv1))
- {
- // reserved bits not cleared
- ec = error::bad_reserved_bits;
- return false;
- }
- break;
-
- case detail::opcode::cont:
- if(! rd_cont_)
- {
- // continuation without an active message
- ec = error::bad_continuation;
- return false;
- }
- if(fh.rsv1 || fh.rsv2 || fh.rsv3)
- {
- // reserved bits not cleared
- ec = error::bad_reserved_bits;
- return false;
- }
- break;
-
- default:
- if(detail::is_reserved(fh.op))
- {
- // reserved opcode
- ec = error::bad_opcode;
- return false;
- }
- if(! fh.fin)
- {
- // fragmented control message
- ec = error::bad_control_fragment;
- return false;
- }
- if(fh.len > 125)
- {
- // invalid length for control message
- ec = error::bad_control_size;
- return false;
- }
- if(fh.rsv1 || fh.rsv2 || fh.rsv3)
- {
- // reserved bits not cleared
- ec = error::bad_reserved_bits;
- return false;
- }
- break;
- }
- if(role_ == role_type::server && ! fh.mask)
- {
- // unmasked frame from client
- ec = error::bad_unmasked_frame;
- return false;
- }
- if(role_ == role_type::client && fh.mask)
- {
- // masked frame from server
- ec = error::bad_masked_frame;
- return false;
- }
- if(detail::is_control(fh.op) &&
- buffer_size(cb) < need + fh.len)
- {
- // Make the entire control frame payload
- // get read in before we return `true`
- return false;
- }
- switch(fh.len)
- {
- case 126:
- {
- std::uint8_t tmp[2];
- BOOST_ASSERT(buffer_size(cb) >= sizeof(tmp));
- cb.consume(buffer_copy(buffer(tmp), cb));
- fh.len = detail::big_uint16_to_native(&tmp[0]);
- if(fh.len < 126)
- {
- // length not canonical
- ec = error::bad_size;
- return false;
- }
- break;
- }
- case 127:
- {
- std::uint8_t tmp[8];
- BOOST_ASSERT(buffer_size(cb) >= sizeof(tmp));
- cb.consume(buffer_copy(buffer(tmp), cb));
- fh.len = detail::big_uint64_to_native(&tmp[0]);
- if(fh.len < 65536)
- {
- // length not canonical
- ec = error::bad_size;
- return false;
- }
- break;
- }
- }
- if(fh.mask)
- {
- std::uint8_t tmp[4];
- BOOST_ASSERT(buffer_size(cb) >= sizeof(tmp));
- cb.consume(buffer_copy(buffer(tmp), cb));
- fh.key = detail::little_uint32_to_native(&tmp[0]);
- detail::prepare_key(rd_key_, fh.key);
- }
- else
- {
- // initialize this otherwise operator== breaks
- fh.key = 0;
- }
- if(! detail::is_control(fh.op))
- {
- if(fh.op != detail::opcode::cont)
- {
- rd_size_ = 0;
- rd_op_ = fh.op;
- }
- else
- {
- if(rd_size_ > (std::numeric_limits<
- std::uint64_t>::max)() - fh.len)
- {
- // message size exceeds configured limit
- ec = error::message_too_big;
- return false;
- }
- }
- if(! this->rd_deflated())
- {
- if(rd_msg_max_ && beast::detail::sum_exceeds(
- rd_size_, fh.len, rd_msg_max_))
- {
- // 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));
- ec.assign(0, ec.category());
- return true;
-}
-
-template<class NextLayer, bool deflateSupported>
-template<class DynamicBuffer>
-void
-stream<NextLayer, deflateSupported>::
-write_close(DynamicBuffer& db, close_reason const& cr)
-{
- using namespace boost::endian;
- detail::frame_header fh;
- fh.op = detail::opcode::close;
- fh.fin = true;
- fh.rsv1 = false;
- fh.rsv2 = false;
- fh.rsv3 = false;
- fh.len = cr.code == close_code::none ?
- 0 : 2 + cr.reason.size();
- if(role_ == role_type::client)
- {
- fh.mask = true;
- fh.key = this->create_mask();
- }
- else
- {
- fh.mask = false;
- }
- detail::write(db, fh);
- if(cr.code != close_code::none)
- {
- detail::prepared_key key;
- if(fh.mask)
- detail::prepare_key(key, fh.key);
- {
- std::uint8_t tmp[2];
- ::new(&tmp[0]) big_uint16_buf_t{
- (std::uint16_t)cr.code};
- auto mb = db.prepare(2);
- boost::asio::buffer_copy(mb,
- boost::asio::buffer(tmp));
- if(fh.mask)
- detail::mask_inplace(mb, key);
- db.commit(2);
- }
- if(! cr.reason.empty())
- {
- auto mb = db.prepare(cr.reason.size());
- boost::asio::buffer_copy(mb,
- boost::asio::const_buffer(
- cr.reason.data(), cr.reason.size()));
- if(fh.mask)
- detail::mask_inplace(mb, key);
- db.commit(cr.reason.size());
- }
- }
-}
-
-template<class NextLayer, bool deflateSupported>
-template<class DynamicBuffer>
-void
-stream<NextLayer, deflateSupported>::
-write_ping(DynamicBuffer& db,
- detail::opcode code, ping_data const& data)
-{
- detail::frame_header fh;
- fh.op = code;
- fh.fin = true;
- fh.rsv1 = false;
- fh.rsv2 = false;
- fh.rsv3 = false;
- fh.len = data.size();
- fh.mask = role_ == role_type::client;
- if(fh.mask)
- fh.key = this->create_mask();
- detail::write(db, fh);
- if(data.empty())
- return;
- detail::prepared_key key;
- if(fh.mask)
- detail::prepare_key(key, fh.key);
- auto mb = db.prepare(data.size());
- boost::asio::buffer_copy(mb,
- boost::asio::const_buffer(
- data.data(), data.size()));
- if(fh.mask)
- detail::mask_inplace(mb, key);
- db.commit(data.size());
-}
-
-//------------------------------------------------------------------------------
-
-template<class NextLayer, bool deflateSupported>
-template<class Decorator>
-request_type
-stream<NextLayer, deflateSupported>::
-build_request(detail::sec_ws_key_type& key,
- string_view host, string_view target,
- Decorator const& decorator)
-{
- request_type req;
- req.target(target);
- req.version(11);
- req.method(http::verb::get);
- req.set(http::field::host, host);
- req.set(http::field::upgrade, "websocket");
- req.set(http::field::connection, "upgrade");
- detail::make_sec_ws_key(key);
- req.set(http::field::sec_websocket_key, key);
- req.set(http::field::sec_websocket_version, "13");
- 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 =
- this->pmd_opts_.server_max_window_bits;
- config.client_max_window_bits =
- this->pmd_opts_.client_max_window_bits;
- config.server_no_context_takeover =
- this->pmd_opts_.server_no_context_takeover;
- config.client_no_context_takeover =
- this->pmd_opts_.client_no_context_takeover;
- detail::pmd_write(req, config);
- }
-}
-
-template<class NextLayer, bool deflateSupported>
-template<class Body, class Allocator, class Decorator>
-response_type
-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)
- {
- decorator(res);
- if(! res.count(http::field::server))
- {
- BOOST_STATIC_ASSERT(sizeof(BOOST_BEAST_VERSION_STRING) < 20);
- static_string<20> s(BOOST_BEAST_VERSION_STRING);
- res.set(http::field::server, s);
- }
- };
- auto err =
- [&](error e)
- {
- result = e;
- response_type res;
- res.version(req.version());
- res.result(http::status::bad_request);
- res.body() = result.message();
- res.prepare_payload();
- decorate(res);
- return res;
- };
- if(req.version() != 11)
- return err(error::bad_http_version);
- if(req.method() != http::verb::get)
- return err(error::bad_method);
- if(! req.count(http::field::host))
- 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;
- }
- }
-
- response_type res;
- res.result(http::status::switching_protocols);
- res.version(req.version());
- res.set(http::field::upgrade, "websocket");
- res.set(http::field::connection, "upgrade");
- {
- detail::sec_ws_accept_type acc;
- 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, 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, deflateSupported>::
-on_response(
- response_type const& res,
- detail::sec_ws_key_type const& key,
- error_code& ec)
-{
- 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);
- {
- 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(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.
- this->pmd_config_ = offer; // overwrite for now
-}
-
-// _Fail the WebSocket Connection_
-template<class NextLayer, bool deflateSupported>
-void
-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
- error_code& ec) // set to the error, else set to ev
-{
- BOOST_ASSERT(ev);
- status_ = status::closing;
- if(code != close_code::none && ! wr_close_)
- {
- wr_close_ = true;
- detail::frame_buffer fb;
- write_close<
- flat_static_buffer_base>(fb, code);
- boost::asio::write(stream_, fb.data(), ec);
- if(! check_ok(ec))
- return;
- }
- using beast::websocket::teardown;
- teardown(role_, stream_, ec);
- if(ec == boost::asio::error::eof)
- {
- // Rationale:
- // http://stackoverflow.com/questions/25587403/boost-asio-ssl-async-shutdown-always-finishes-with-an-error
- ec.assign(0, ec.category());
- }
- if(! ec)
- ec = ev;
- if(ec && ec != error::closed)
- status_ = status::failed;
- else
- status_ = status::closed;
- close();
-}
-
-} // websocket
-} // beast
-} // boost
-
-#endif
diff --git a/boost/beast/websocket/impl/stream_impl.hpp b/boost/beast/websocket/impl/stream_impl.hpp
new file mode 100644
index 0000000000..ec5557c361
--- /dev/null
+++ b/boost/beast/websocket/impl/stream_impl.hpp
@@ -0,0 +1,939 @@
+//
+// Copyright (c) 2016-2019 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_IMPL_STREAM_IMPL_HPP
+#define BOOST_BEAST_WEBSOCKET_IMPL_STREAM_IMPL_HPP
+
+#include <boost/beast/websocket/rfc6455.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/pmd_extension.hpp>
+#include <boost/beast/websocket/detail/prng.hpp>
+#include <boost/beast/websocket/detail/service.hpp>
+#include <boost/beast/websocket/detail/soft_mutex.hpp>
+#include <boost/beast/websocket/detail/utf8_checker.hpp>
+#include <boost/beast/http/read.hpp>
+#include <boost/beast/http/write.hpp>
+#include <boost/beast/http/rfc7230.hpp>
+#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/flat_static_buffer.hpp>
+#include <boost/beast/core/saved_handler.hpp>
+#include <boost/beast/core/static_buffer.hpp>
+#include <boost/beast/core/stream_traits.hpp>
+#include <boost/beast/core/detail/clamp.hpp>
+#include <boost/beast/core/detail/type_traits.hpp>
+#include <boost/beast/version.hpp>
+#include <boost/asio/bind_executor.hpp>
+#include <boost/asio/steady_timer.hpp>
+#include <boost/core/empty_value.hpp>
+#include <boost/enable_shared_from_this.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/optional.hpp>
+
+namespace boost {
+namespace beast {
+namespace websocket {
+
+template<
+ class NextLayer, bool deflateSupported>
+struct stream<NextLayer, deflateSupported>::impl_type
+ : boost::empty_value<NextLayer>
+ , detail::service::impl_type
+ , detail::impl_base<deflateSupported>
+{
+ NextLayer& stream() noexcept
+ {
+ return this->boost::empty_value<
+ NextLayer>::get();
+ }
+
+ boost::weak_ptr<impl_type>
+ weak_from_this()
+ {
+ return boost::static_pointer_cast<
+ impl_type>(this->detail::service::
+ impl_type::shared_from_this());
+ }
+
+ boost::shared_ptr<impl_type>
+ shared_this()
+ {
+ return boost::static_pointer_cast<
+ impl_type>(this->detail::service::
+ impl_type::shared_from_this());
+ }
+
+ net::steady_timer timer; // used for timeouts
+ close_reason cr; // set from received close frame
+ control_cb_type ctrl_cb; // control callback
+
+ std::size_t rd_msg_max /* max message size */ = 16 * 1024 * 1024;
+ std::uint64_t rd_size /* total size of current message so far */ = 0;
+ 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
+ detail::frame_buffer rd_fb; // to write control frames (during reads)
+ detail::utf8_checker rd_utf8; // to validate utf8
+ static_buffer<
+ +tcp_frame_size> rd_buf; // buffer for reads
+ detail::opcode rd_op /* current message binary or text */ = detail::opcode::text;
+ bool rd_cont /* `true` if the next frame is a continuation */ = false;
+ bool rd_done /* set when a message is done */ = true;
+ bool rd_close /* did we read a close frame? */ = false;
+ detail::soft_mutex rd_block; // op currently reading
+
+ role_type role /* server or client */ = role_type::client;
+ status status_ /* state of the object */ = status::closed;
+
+ 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 */ = false;
+ bool wr_frag /* autofrag the current message */ = false;
+ bool wr_frag_opt /* autofrag option setting */ = true;
+ bool wr_compress /* compress current message */ = false;
+ detail::opcode wr_opcode /* message type */ = detail::opcode::text;
+ std::unique_ptr<
+ std::uint8_t[]> wr_buf; // write buffer
+ std::size_t wr_buf_size /* write buffer size (current message) */ = 0;
+ std::size_t wr_buf_opt /* write buffer size option setting */ = 4096;
+ detail::fh_buffer wr_fb; // header buffer used for writes
+
+ saved_handler op_rd; // paused read op
+ saved_handler op_wr; // paused write op
+ saved_handler op_ping; // paused ping op
+ saved_handler op_idle_ping; // paused idle ping op
+ saved_handler op_close; // paused close op
+ saved_handler op_r_rd; // paused read op (async read)
+ saved_handler op_r_close; // paused close op (async read)
+
+ bool idle_pinging = false;
+ bool secure_prng_ = true;
+ bool ec_delivered = false;
+ bool timed_out = false;
+ int idle_counter = 0;
+
+ detail::decorator decorator_opt; // Decorator for HTTP messages
+ timeout timeout_opt; // Timeout/idle settings
+
+ template<class... Args>
+ impl_type(Args&&... args)
+ : boost::empty_value<NextLayer>(
+ boost::empty_init_t{},
+ std::forward<Args>(args)...)
+ , detail::service::impl_type(
+ this->boost::empty_value<NextLayer>::get().get_executor().context())
+ , timer(this->boost::empty_value<NextLayer>::get().get_executor())
+ {
+ timeout_opt.handshake_timeout = none();
+ timeout_opt.idle_timeout = none();
+ timeout_opt.keep_alive_pings = false;
+ }
+
+ void
+ shutdown() override
+ {
+ op_rd.reset();
+ op_wr.reset();
+ op_ping.reset();
+ op_idle_ping.reset();
+ op_close.reset();
+ op_r_rd.reset();
+ op_r_close.reset();
+ }
+
+ void
+ open(role_type role_)
+ {
+ // VFALCO TODO analyze and remove dupe code in reset()
+ timer.expires_at(never());
+ timed_out = false;
+ cr.code = close_code::none;
+ role = role_;
+ status_ = status::open;
+ rd_remain = 0;
+ rd_cont = false;
+ rd_done = true;
+ // Can't clear this because accept uses it
+ //rd_buf.reset();
+ 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();
+
+ wr_cont = false;
+ wr_buf_size = 0;
+
+ this->open_pmd(role);
+ }
+
+ void
+ close()
+ {
+ timer.cancel();
+ wr_buf.reset();
+ this->close_pmd();
+ }
+
+ void
+ reset()
+ {
+ BOOST_ASSERT(status_ != status::open);
+ timer.expires_at(never());
+ cr.code = close_code::none;
+ rd_remain = 0;
+ rd_cont = false;
+ rd_done = true;
+ rd_buf.consume(rd_buf.size());
+ rd_fh.fin = false;
+ 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();
+
+ // VFALCO Is this needed?
+ timer.cancel();
+ }
+
+ // Called before each write frame
+ void
+ begin_msg()
+ {
+ wr_frag = wr_frag_opt;
+
+ // Maintain the write buffer
+ if( this->pmd_enabled() ||
+ 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 Decorator>
+ request_type
+ build_request(
+ detail::sec_ws_key_type& key,
+ string_view host, string_view target,
+ Decorator const& decorator);
+
+ void
+ on_response(
+ response_type const& res,
+ detail::sec_ws_key_type const& key,
+ error_code& ec);
+
+ template<class Body, class Allocator, class Decorator>
+ response_type
+ build_response(
+ http::request<Body,
+ http::basic_fields<Allocator>> const& req,
+ Decorator const& decorator,
+ error_code& result);
+
+ // Attempt to read a complete frame header.
+ // Returns `false` if more bytes are needed
+ template<class DynamicBuffer>
+ bool
+ parse_fh(detail::frame_header& fh,
+ DynamicBuffer& b, error_code& ec);
+
+ std::uint32_t
+ create_mask()
+ {
+ auto g = detail::make_prng(secure_prng_);
+ for(;;)
+ if(auto key = g())
+ return key;
+ }
+
+ std::size_t
+ read_size_hint(std::size_t initial_size) const
+ {
+ return this->read_size_hint_pmd(
+ initial_size, rd_done, rd_remain, rd_fh);
+ }
+
+ template<class DynamicBuffer>
+ std::size_t
+ read_size_hint_db(DynamicBuffer& buffer) const
+ {
+ auto const initial_size = (std::min)(
+ +tcp_frame_size,
+ buffer.max_size() - buffer.size());
+ if(initial_size == 0)
+ return 1; // buffer is full
+ return this->read_size_hint(initial_size);
+ }
+
+ template<class DynamicBuffer>
+ void
+ write_ping(DynamicBuffer& db,
+ detail::opcode code, ping_data const& data);
+
+ template<class DynamicBuffer>
+ void
+ write_close(DynamicBuffer& db, close_reason const& cr);
+
+ //--------------------------------------------------------------------------
+
+ void
+ set_option(timeout const& opt)
+ {
+ if( opt.handshake_timeout == none() &&
+ opt.idle_timeout == none())
+ {
+ // turn timer off
+ timer.cancel();
+ timer.expires_at(never());
+ }
+
+ timeout_opt = opt;
+ }
+
+ // Determine if an operation should stop and
+ // deliver an error code to the completion handler.
+ //
+ // This function must be called at the beginning
+ // of every composed operation, and every time a
+ // composed operation receives an intermediate
+ // completion.
+ //
+ bool
+ check_stop_now(error_code& ec)
+ {
+ // Deliver the timeout to the first caller
+ if(timed_out)
+ {
+ timed_out = false;
+ ec = beast::error::timeout;
+ return true;
+ }
+
+ // If the stream is closed then abort
+ if( status_ == status::closed ||
+ status_ == status::failed)
+ {
+ //BOOST_ASSERT(ec_delivered);
+ ec = net::error::operation_aborted;
+ return true;
+ }
+
+ // If no error then keep going
+ if(! ec)
+ return false;
+
+ // Is this the first error seen?
+ if(ec_delivered)
+ {
+ // No, so abort
+ ec = net::error::operation_aborted;
+ return true;
+ }
+
+ // Deliver the error to the completion handler
+ ec_delivered = true;
+ if(status_ != status::closed)
+ status_ = status::failed;
+ return true;
+ }
+
+ // Change the status of the stream
+ void
+ change_status(status new_status)
+ {
+ switch(new_status)
+ {
+ case status::handshake:
+ break;
+
+ case status::open:
+ break;
+
+ case status::closing:
+ //BOOST_ASSERT(status_ == status::open);
+ break;
+
+ case status::failed:
+ case status::closed:
+ // this->close(); // Is this right?
+ break;
+
+ default:
+ break;
+ }
+ status_ = new_status;
+ }
+
+ // Called to disarm the idle timeout counter
+ void
+ reset_idle()
+ {
+ idle_counter = 0;
+ }
+
+ // Maintain the expiration timer
+ template<class Executor>
+ void
+ update_timer(Executor const& ex)
+ {
+ switch(status_)
+ {
+ case status::handshake:
+ BOOST_ASSERT(idle_counter == 0);
+ if(! is_timer_set() &&
+ timeout_opt.handshake_timeout != none())
+ {
+ timer.expires_after(
+ timeout_opt.handshake_timeout);
+ timer.async_wait(
+ timeout_handler<Executor>(
+ ex, this->weak_from_this()));
+ }
+ break;
+
+ case status::open:
+ if(timeout_opt.idle_timeout != none())
+ {
+ idle_counter = 0;
+ if(timeout_opt.keep_alive_pings)
+ timer.expires_after(
+ timeout_opt.idle_timeout / 2);
+ else
+ timer.expires_after(
+ timeout_opt.idle_timeout);
+ timer.async_wait(
+ timeout_handler<Executor>(
+ ex, this->weak_from_this()));
+ }
+ else
+ {
+ timer.cancel();
+ timer.expires_at(never());
+ }
+ break;
+
+ case status::closing:
+ if(timeout_opt.handshake_timeout != none())
+ {
+ idle_counter = 0;
+ timer.expires_after(
+ timeout_opt.handshake_timeout);
+ timer.async_wait(
+ timeout_handler<Executor>(
+ ex, this->weak_from_this()));
+ }
+ else
+ {
+ BOOST_ASSERT(! is_timer_set());
+ }
+ break;
+
+ case status::failed:
+ case status::closed:
+ // this->close(); // Is this right?
+ timer.cancel();
+ timer.expires_at(never());
+ break;
+ }
+ }
+
+private:
+ bool
+ is_timer_set() const
+ {
+ return timer.expiry() != never();
+ }
+
+ template<class Executor>
+ class timeout_handler
+ : boost::empty_value<Executor>
+ {
+ boost::weak_ptr<impl_type> wp_;
+
+ public:
+ timeout_handler(
+ Executor const& ex,
+ boost::weak_ptr<impl_type>&& wp)
+ : boost::empty_value<Executor>(
+ boost::empty_init_t{}, ex)
+ , wp_(std::move(wp))
+ {
+ }
+
+ using executor_type = Executor;
+
+ executor_type
+ get_executor() const noexcept
+ {
+ return this->get();
+ }
+
+ void
+ operator()(error_code ec)
+ {
+ // timer canceled?
+ if(ec == net::error::operation_aborted)
+ return;
+ BOOST_ASSERT(! ec);
+
+ // stream destroyed?
+ auto sp = wp_.lock();
+ if(! sp)
+ return;
+ auto& impl = *sp;
+
+ switch(impl.status_)
+ {
+ case status::handshake:
+ impl.timed_out = true;
+ close_socket(get_lowest_layer(impl.stream()));
+ return;
+
+ case status::open:
+ // timeout was disabled
+ if(impl.timeout_opt.idle_timeout == none())
+ return;
+
+ if( impl.timeout_opt.keep_alive_pings &&
+ impl.idle_counter < 1)
+ {
+ idle_ping_op<Executor>(sp, get_executor());
+
+ ++impl.idle_counter;
+ impl.timer.expires_after(
+ impl.timeout_opt.idle_timeout / 2);
+ impl.timer.async_wait(std::move(*this));
+ return;
+ }
+
+ // timeout
+ impl.timed_out = true;
+ close_socket(get_lowest_layer(impl.stream()));
+ return;
+
+ case status::closing:
+ impl.timed_out = true;
+ close_socket(get_lowest_layer(impl.stream()));
+ return;
+
+ case status::closed:
+ case status::failed:
+ // nothing to do?
+ return;
+ }
+ }
+ };
+};
+
+//--------------------------------------------------------------------------
+//
+// client
+//
+//--------------------------------------------------------------------------
+
+template<class NextLayer, bool deflateSupported>
+template<class Decorator>
+request_type
+stream<NextLayer, deflateSupported>::impl_type::
+build_request(
+ detail::sec_ws_key_type& key,
+ string_view host, string_view target,
+ Decorator const& decorator)
+{
+ request_type req;
+ req.target(target);
+ req.version(11);
+ req.method(http::verb::get);
+ req.set(http::field::host, host);
+ req.set(http::field::upgrade, "websocket");
+ req.set(http::field::connection, "upgrade");
+ detail::make_sec_ws_key(key);
+ req.set(http::field::sec_websocket_key, key);
+ req.set(http::field::sec_websocket_version, "13");
+ this->build_request_pmd(req);
+ decorator_opt(req);
+ decorator(req);
+ if(! req.count(http::field::user_agent))
+ req.set(http::field::user_agent,
+ BOOST_BEAST_VERSION_STRING);
+ return req;
+}
+
+// Called when the WebSocket Upgrade response is received
+template<class NextLayer, bool deflateSupported>
+void
+stream<NextLayer, deflateSupported>::impl_type::
+on_response(
+ response_type const& res,
+ detail::sec_ws_key_type const& key,
+ error_code& ec)
+{
+ 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);
+ {
+ 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(it->value()) != 0)
+ return err(error::bad_sec_accept);
+ }
+
+ ec = {};
+ this->on_response_pmd(res);
+ this->open(role_type::client);
+}
+
+//------------------------------------------------------------------------------
+
+// Attempt to read a complete frame header.
+// Returns `false` if more bytes are needed
+template<class NextLayer, bool deflateSupported>
+template<class DynamicBuffer>
+bool
+stream<NextLayer, deflateSupported>::impl_type::
+parse_fh(
+ detail::frame_header& fh,
+ DynamicBuffer& b,
+ error_code& ec)
+{
+ if(buffer_bytes(b.data()) < 2)
+ {
+ // need more bytes
+ ec = {};
+ return false;
+ }
+ buffers_suffix<typename
+ DynamicBuffer::const_buffers_type> cb{
+ b.data()};
+ std::size_t need;
+ {
+ std::uint8_t tmp[2];
+ cb.consume(net::buffer_copy(
+ net::buffer(tmp), cb));
+ fh.len = tmp[1] & 0x7f;
+ switch(fh.len)
+ {
+ case 126: need = 2; break;
+ case 127: need = 8; break;
+ default:
+ need = 0;
+ }
+ fh.mask = (tmp[1] & 0x80) != 0;
+ if(fh.mask)
+ need += 4;
+ if(buffer_bytes(cb) < need)
+ {
+ // need more bytes
+ ec = {};
+ return false;
+ }
+ fh.op = static_cast<
+ detail::opcode>(tmp[0] & 0x0f);
+ fh.fin = (tmp[0] & 0x80) != 0;
+ fh.rsv1 = (tmp[0] & 0x40) != 0;
+ fh.rsv2 = (tmp[0] & 0x20) != 0;
+ fh.rsv3 = (tmp[0] & 0x10) != 0;
+ }
+ switch(fh.op)
+ {
+ case detail::opcode::binary:
+ case detail::opcode::text:
+ if(rd_cont)
+ {
+ // new data frame when continuation expected
+ ec = error::bad_data_frame;
+ return false;
+ }
+ if(fh.rsv2 || fh.rsv3 ||
+ ! this->rd_deflated(fh.rsv1))
+ {
+ // reserved bits not cleared
+ ec = error::bad_reserved_bits;
+ return false;
+ }
+ break;
+
+ case detail::opcode::cont:
+ if(! rd_cont)
+ {
+ // continuation without an active message
+ ec = error::bad_continuation;
+ return false;
+ }
+ if(fh.rsv1 || fh.rsv2 || fh.rsv3)
+ {
+ // reserved bits not cleared
+ ec = error::bad_reserved_bits;
+ return false;
+ }
+ break;
+
+ default:
+ if(detail::is_reserved(fh.op))
+ {
+ // reserved opcode
+ ec = error::bad_opcode;
+ return false;
+ }
+ if(! fh.fin)
+ {
+ // fragmented control message
+ ec = error::bad_control_fragment;
+ return false;
+ }
+ if(fh.len > 125)
+ {
+ // invalid length for control message
+ ec = error::bad_control_size;
+ return false;
+ }
+ if(fh.rsv1 || fh.rsv2 || fh.rsv3)
+ {
+ // reserved bits not cleared
+ ec = error::bad_reserved_bits;
+ return false;
+ }
+ break;
+ }
+ if(role == role_type::server && ! fh.mask)
+ {
+ // unmasked frame from client
+ ec = error::bad_unmasked_frame;
+ return false;
+ }
+ if(role == role_type::client && fh.mask)
+ {
+ // masked frame from server
+ ec = error::bad_masked_frame;
+ return false;
+ }
+ if(detail::is_control(fh.op) &&
+ buffer_bytes(cb) < need + fh.len)
+ {
+ // Make the entire control frame payload
+ // get read in before we return `true`
+ return false;
+ }
+ switch(fh.len)
+ {
+ case 126:
+ {
+ std::uint8_t tmp[2];
+ BOOST_ASSERT(buffer_bytes(cb) >= sizeof(tmp));
+ cb.consume(net::buffer_copy(net::buffer(tmp), cb));
+ fh.len = detail::big_uint16_to_native(&tmp[0]);
+ if(fh.len < 126)
+ {
+ // length not canonical
+ ec = error::bad_size;
+ return false;
+ }
+ break;
+ }
+ case 127:
+ {
+ std::uint8_t tmp[8];
+ BOOST_ASSERT(buffer_bytes(cb) >= sizeof(tmp));
+ cb.consume(net::buffer_copy(net::buffer(tmp), cb));
+ fh.len = detail::big_uint64_to_native(&tmp[0]);
+ if(fh.len < 65536)
+ {
+ // length not canonical
+ ec = error::bad_size;
+ return false;
+ }
+ break;
+ }
+ }
+ if(fh.mask)
+ {
+ std::uint8_t tmp[4];
+ BOOST_ASSERT(buffer_bytes(cb) >= sizeof(tmp));
+ cb.consume(net::buffer_copy(net::buffer(tmp), cb));
+ fh.key = detail::little_uint32_to_native(&tmp[0]);
+ detail::prepare_key(rd_key, fh.key);
+ }
+ else
+ {
+ // initialize this otherwise operator== breaks
+ fh.key = 0;
+ }
+ if(! detail::is_control(fh.op))
+ {
+ if(fh.op != detail::opcode::cont)
+ {
+ rd_size = 0;
+ rd_op = fh.op;
+ }
+ else
+ {
+ if(rd_size > (std::numeric_limits<
+ std::uint64_t>::max)() - fh.len)
+ {
+ // message size exceeds configured limit
+ ec = error::message_too_big;
+ return false;
+ }
+ }
+ if(! this->rd_deflated())
+ {
+ if(rd_msg_max && beast::detail::sum_exceeds(
+ rd_size, fh.len, rd_msg_max))
+ {
+ // 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_bytes(cb));
+ ec = {};
+ return true;
+}
+
+template<class NextLayer, bool deflateSupported>
+template<class DynamicBuffer>
+void
+stream<NextLayer, deflateSupported>::impl_type::
+write_ping(DynamicBuffer& db,
+ detail::opcode code, ping_data const& data)
+{
+ detail::frame_header fh;
+ fh.op = code;
+ fh.fin = true;
+ fh.rsv1 = false;
+ fh.rsv2 = false;
+ fh.rsv3 = false;
+ fh.len = data.size();
+ fh.mask = role == role_type::client;
+ if(fh.mask)
+ fh.key = create_mask();
+ detail::write(db, fh);
+ if(data.empty())
+ return;
+ detail::prepared_key key;
+ if(fh.mask)
+ detail::prepare_key(key, fh.key);
+ auto mb = db.prepare(data.size());
+ net::buffer_copy(mb,
+ net::const_buffer(
+ data.data(), data.size()));
+ if(fh.mask)
+ detail::mask_inplace(mb, key);
+ db.commit(data.size());
+}
+
+template<class NextLayer, bool deflateSupported>
+template<class DynamicBuffer>
+void
+stream<NextLayer, deflateSupported>::impl_type::
+write_close(DynamicBuffer& db, close_reason const& cr)
+{
+ using namespace boost::endian;
+ detail::frame_header fh;
+ fh.op = detail::opcode::close;
+ fh.fin = true;
+ fh.rsv1 = false;
+ fh.rsv2 = false;
+ fh.rsv3 = false;
+ fh.len = cr.code == close_code::none ?
+ 0 : 2 + cr.reason.size();
+ if(role == role_type::client)
+ {
+ fh.mask = true;
+ fh.key = create_mask();
+ }
+ else
+ {
+ fh.mask = false;
+ }
+ detail::write(db, fh);
+ if(cr.code != close_code::none)
+ {
+ detail::prepared_key key;
+ if(fh.mask)
+ detail::prepare_key(key, fh.key);
+ {
+ std::uint8_t tmp[2];
+ ::new(&tmp[0]) big_uint16_buf_t{
+ (std::uint16_t)cr.code};
+ auto mb = db.prepare(2);
+ net::buffer_copy(mb,
+ net::buffer(tmp));
+ if(fh.mask)
+ detail::mask_inplace(mb, key);
+ db.commit(2);
+ }
+ if(! cr.reason.empty())
+ {
+ auto mb = db.prepare(cr.reason.size());
+ net::buffer_copy(mb,
+ net::const_buffer(
+ cr.reason.data(), cr.reason.size()));
+ if(fh.mask)
+ detail::mask_inplace(mb, key);
+ db.commit(cr.reason.size());
+ }
+ }
+}
+
+} // websocket
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/websocket/impl/teardown.hpp b/boost/beast/websocket/impl/teardown.hpp
new file mode 100644
index 0000000000..8e0913762c
--- /dev/null
+++ b/boost/beast/websocket/impl/teardown.hpp
@@ -0,0 +1,198 @@
+//
+// Copyright (c) 2016-2019 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_IMPL_TEARDOWN_HPP
+#define BOOST_BEAST_WEBSOCKET_IMPL_TEARDOWN_HPP
+
+#include <boost/beast/core/async_base.hpp>
+#include <boost/beast/core/bind_handler.hpp>
+#include <boost/beast/core/stream_traits.hpp>
+#include <boost/beast/core/detail/bind_continuation.hpp>
+#include <boost/beast/core/detail/type_traits.hpp>
+#include <boost/asio/coroutine.hpp>
+#include <boost/asio/post.hpp>
+#include <memory>
+
+namespace boost {
+namespace beast {
+namespace websocket {
+
+namespace detail {
+
+template<
+ class Protocol, class Executor,
+ class Handler>
+class teardown_tcp_op
+ : public beast::async_base<
+ Handler, beast::executor_type<
+ net::basic_stream_socket<
+ Protocol, Executor>>>
+ , public net::coroutine
+{
+ using socket_type =
+ net::basic_stream_socket<Protocol, Executor>;
+
+ socket_type& s_;
+ role_type role_;
+ bool nb_;
+
+public:
+ template<class Handler_>
+ teardown_tcp_op(
+ Handler_&& h,
+ socket_type& s,
+ role_type role)
+ : async_base<Handler,
+ beast::executor_type<
+ net::basic_stream_socket<
+ Protocol, Executor>>>(
+ std::forward<Handler_>(h),
+ s.get_executor())
+ , s_(s)
+ , role_(role)
+ {
+ (*this)({}, 0, false);
+ }
+
+ void
+ operator()(
+ error_code ec = {},
+ std::size_t bytes_transferred = 0,
+ bool cont = true)
+ {
+ BOOST_ASIO_CORO_REENTER(*this)
+ {
+ nb_ = s_.non_blocking();
+ s_.non_blocking(true, ec);
+ if(ec)
+ goto upcall;
+ if(role_ == role_type::server)
+ s_.shutdown(net::socket_base::shutdown_send, ec);
+ if(ec)
+ goto upcall;
+ for(;;)
+ {
+ {
+ char buf[2048];
+ s_.read_some(net::buffer(buf), ec);
+ }
+ if(ec == net::error::would_block)
+ {
+ BOOST_ASIO_CORO_YIELD
+ s_.async_wait(
+ net::socket_base::wait_read,
+ beast::detail::bind_continuation(std::move(*this)));
+ continue;
+ }
+ if(ec)
+ {
+ if(ec != net::error::eof)
+ goto upcall;
+ ec = {};
+ break;
+ }
+ if(bytes_transferred == 0)
+ {
+ // happens sometimes
+ // https://github.com/boostorg/beast/issues/1373
+ break;
+ }
+ }
+ if(role_ == role_type::client)
+ s_.shutdown(net::socket_base::shutdown_send, ec);
+ if(ec)
+ goto upcall;
+ s_.close(ec);
+ upcall:
+ if(! cont)
+ {
+ BOOST_ASIO_CORO_YIELD
+ net::post(bind_front_handler(
+ std::move(*this), ec));
+ }
+ {
+ error_code ignored;
+ s_.non_blocking(nb_, ignored);
+ }
+ this->complete_now(ec);
+ }
+ }
+};
+
+} // detail
+
+//------------------------------------------------------------------------------
+
+template<class Protocol, class Executor>
+void
+teardown(
+ role_type role,
+ net::basic_stream_socket<
+ Protocol, Executor>& socket,
+ error_code& ec)
+{
+ if(role == role_type::server)
+ socket.shutdown(
+ net::socket_base::shutdown_send, ec);
+ if(ec)
+ return;
+ for(;;)
+ {
+ char buf[2048];
+ auto const bytes_transferred =
+ socket.read_some(net::buffer(buf), ec);
+ if(ec)
+ {
+ if(ec != net::error::eof)
+ return;
+ ec = {};
+ break;
+ }
+ if(bytes_transferred == 0)
+ {
+ // happens sometimes
+ // https://github.com/boostorg/beast/issues/1373
+ break;
+ }
+ }
+ if(role == role_type::client)
+ socket.shutdown(
+ net::socket_base::shutdown_send, ec);
+ if(ec)
+ return;
+ socket.close(ec);
+}
+
+template<
+ class Protocol, class Executor,
+ class TeardownHandler>
+void
+async_teardown(
+ role_type role,
+ net::basic_stream_socket<
+ Protocol, Executor>& socket,
+ TeardownHandler&& handler)
+{
+ static_assert(beast::detail::is_invocable<
+ TeardownHandler, void(error_code)>::value,
+ "TeardownHandler type requirements not met");
+ detail::teardown_tcp_op<
+ Protocol,
+ Executor,
+ typename std::decay<TeardownHandler>::type>(
+ std::forward<TeardownHandler>(handler),
+ socket,
+ role);
+}
+
+} // websocket
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/websocket/impl/teardown.ipp b/boost/beast/websocket/impl/teardown.ipp
deleted file mode 100644
index fe94d55274..0000000000
--- a/boost/beast/websocket/impl/teardown.ipp
+++ /dev/null
@@ -1,230 +0,0 @@
-//
-// 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_IMPL_TEARDOWN_IPP
-#define BOOST_BEAST_WEBSOCKET_IMPL_TEARDOWN_IPP
-
-#include <boost/beast/core/bind_handler.hpp>
-#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/executor_work_guard.hpp>
-#include <boost/asio/handler_continuation_hook.hpp>
-#include <boost/asio/handler_invoke_hook.hpp>
-#include <boost/asio/post.hpp>
-#include <memory>
-
-namespace boost {
-namespace beast {
-namespace websocket {
-
-namespace detail {
-
-template<class Handler>
-class teardown_tcp_op : public boost::asio::coroutine
-{
- using socket_type =
- boost::asio::ip::tcp::socket;
-
- Handler h_;
- socket_type& s_;
- boost::asio::executor_work_guard<decltype(std::declval<
- socket_type&>().get_executor())> wg_;
- role_type role_;
- bool nb_;
-
-public:
- teardown_tcp_op(teardown_tcp_op&& other) = default;
- teardown_tcp_op(teardown_tcp_op const& other) = default;
-
- template<class DeducedHandler>
- teardown_tcp_op(
- DeducedHandler&& h,
- socket_type& s,
- role_type role)
- : h_(std::forward<DeducedHandler>(h))
- , s_(s)
- , wg_(s_.get_executor())
- , role_(role)
- {
- }
-
- using allocator_type =
- boost::asio::associated_allocator_t<Handler>;
-
- allocator_type
- get_allocator() const noexcept
- {
- return (boost::asio::get_associated_allocator)(h_);
- }
-
- using executor_type = boost::asio::associated_executor_t<
- Handler, decltype(std::declval<socket_type&>().get_executor())>;
-
- executor_type
- get_executor() const noexcept
- {
- return (boost::asio::get_associated_executor)(
- h_, s_.get_executor());
- }
-
- void
- operator()(
- error_code ec = {},
- std::size_t bytes_transferred = 0);
-
- friend
- bool asio_handler_is_continuation(teardown_tcp_op* op)
- {
- using boost::asio::asio_handler_is_continuation;
- 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 bytes_transferred)
-{
- using boost::asio::buffer;
- using tcp = boost::asio::ip::tcp;
- BOOST_ASIO_CORO_REENTER(*this)
- {
- 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)
- {
- BOOST_ASIO_CORO_YIELD
- boost::asio::post(
- s_.get_executor(),
- bind_handler(std::move(*this), ec, 0));
- goto upcall;
- }
- for(;;)
- {
- {
- 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;
- }
- }
- 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);
- }
-}
-
-} // detail
-
-//------------------------------------------------------------------------------
-
-inline
-void
-teardown(
- role_type role,
- boost::asio::ip::tcp::socket& socket,
- error_code& ec)
-{
- using boost::asio::buffer;
- if(role == role_type::server)
- socket.shutdown(
- boost::asio::ip::tcp::socket::shutdown_send, ec);
- if(ec)
- return;
- for(;;)
- {
- 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);
-}
-
-template<class TeardownHandler>
-inline
-void
-async_teardown(
- role_type role,
- boost::asio::ip::tcp::socket& socket,
- TeardownHandler&& handler)
-{
- static_assert(beast::is_completion_handler<
- TeardownHandler, void(error_code)>::value,
- "TeardownHandler requirements not met");
- detail::teardown_tcp_op<typename std::decay<
- TeardownHandler>::type>{std::forward<
- TeardownHandler>(handler), socket,
- role}();
-}
-
-} // websocket
-} // beast
-} // boost
-
-#endif
diff --git a/boost/beast/websocket/impl/write.hpp b/boost/beast/websocket/impl/write.hpp
new file mode 100644
index 0000000000..318693f512
--- /dev/null
+++ b/boost/beast/websocket/impl/write.hpp
@@ -0,0 +1,784 @@
+//
+// Copyright (c) 2016-2019 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_IMPL_WRITE_HPP
+#define BOOST_BEAST_WEBSOCKET_IMPL_WRITE_HPP
+
+#include <boost/beast/websocket/detail/mask.hpp>
+#include <boost/beast/core/async_base.hpp>
+#include <boost/beast/core/bind_handler.hpp>
+#include <boost/beast/core/buffer_traits.hpp>
+#include <boost/beast/core/buffers_cat.hpp>
+#include <boost/beast/core/buffers_prefix.hpp>
+#include <boost/beast/core/buffers_range.hpp>
+#include <boost/beast/core/buffers_suffix.hpp>
+#include <boost/beast/core/flat_static_buffer.hpp>
+#include <boost/beast/core/stream_traits.hpp>
+#include <boost/beast/core/detail/bind_continuation.hpp>
+#include <boost/beast/core/detail/clamp.hpp>
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/websocket/detail/frame.hpp>
+#include <boost/beast/websocket/impl/stream_impl.hpp>
+#include <boost/asio/coroutine.hpp>
+#include <boost/assert.hpp>
+#include <boost/config.hpp>
+#include <boost/throw_exception.hpp>
+#include <algorithm>
+#include <memory>
+
+namespace boost {
+namespace beast {
+namespace websocket {
+
+template<class NextLayer, bool deflateSupported>
+template<class Handler, class Buffers>
+class stream<NextLayer, deflateSupported>::write_some_op
+ : public beast::async_base<
+ Handler, beast::executor_type<stream>>
+ , public net::coroutine
+{
+ enum
+ {
+ do_nomask_nofrag,
+ do_nomask_frag,
+ do_mask_nofrag,
+ do_mask_frag,
+ do_deflate
+ };
+
+ boost::weak_ptr<impl_type> wp_;
+ 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_;
+ int how_;
+ bool fin_;
+ bool more_ = false; // for ubsan
+ bool cont_ = false;
+
+public:
+ static constexpr int id = 2; // for soft_mutex
+
+ template<class Handler_>
+ write_some_op(
+ Handler_&& h,
+ boost::shared_ptr<impl_type> const& sp,
+ bool fin,
+ Buffers const& bs)
+ : beast::async_base<Handler,
+ beast::executor_type<stream>>(
+ std::forward<Handler_>(h),
+ sp->stream().get_executor())
+ , wp_(sp)
+ , cb_(bs)
+ , fin_(fin)
+ {
+ auto& impl = *sp;
+
+ // Set up the outgoing frame header
+ if(! impl.wr_cont)
+ {
+ impl.begin_msg();
+ fh_.rsv1 = impl.wr_compress;
+ }
+ else
+ {
+ fh_.rsv1 = false;
+ }
+ fh_.rsv2 = false;
+ fh_.rsv3 = false;
+ fh_.op = impl.wr_cont ?
+ detail::opcode::cont : impl.wr_opcode;
+ fh_.mask =
+ impl.role == role_type::client;
+
+ // Choose a write algorithm
+ if(impl.wr_compress)
+ {
+ how_ = do_deflate;
+ }
+ else if(! fh_.mask)
+ {
+ if(! impl.wr_frag)
+ {
+ how_ = do_nomask_nofrag;
+ }
+ else
+ {
+ BOOST_ASSERT(impl.wr_buf_size != 0);
+ remain_ = buffer_bytes(cb_);
+ if(remain_ > impl.wr_buf_size)
+ how_ = do_nomask_frag;
+ else
+ how_ = do_nomask_nofrag;
+ }
+ }
+ else
+ {
+ if(! impl.wr_frag)
+ {
+ how_ = do_mask_nofrag;
+ }
+ else
+ {
+ BOOST_ASSERT(impl.wr_buf_size != 0);
+ remain_ = buffer_bytes(cb_);
+ if(remain_ > impl.wr_buf_size)
+ how_ = do_mask_frag;
+ else
+ how_ = do_mask_nofrag;
+ }
+ }
+ (*this)({}, 0, false);
+ }
+
+ void operator()(
+ error_code ec = {},
+ std::size_t bytes_transferred = 0,
+ bool cont = true);
+};
+
+template<class NextLayer, bool deflateSupported>
+template<class Buffers, class Handler>
+void
+stream<NextLayer, deflateSupported>::
+write_some_op<Buffers, Handler>::
+operator()(
+ error_code ec,
+ std::size_t bytes_transferred,
+ bool cont)
+{
+ using beast::detail::clamp;
+ std::size_t n;
+ net::mutable_buffer b;
+ auto sp = wp_.lock();
+ if(! sp)
+ {
+ ec = net::error::operation_aborted;
+ bytes_transferred_ = 0;
+ return this->complete(cont, ec, bytes_transferred_);
+ }
+ auto& impl = *sp;
+ BOOST_ASIO_CORO_REENTER(*this)
+ {
+ // Acquire the write lock
+ if(! impl.wr_block.try_lock(this))
+ {
+ do_suspend:
+ BOOST_ASIO_CORO_YIELD
+ impl.op_wr.emplace(std::move(*this));
+ impl.wr_block.lock(this);
+ BOOST_ASIO_CORO_YIELD
+ net::post(std::move(*this));
+ BOOST_ASSERT(impl.wr_block.is_locked(this));
+ }
+ if(impl.check_stop_now(ec))
+ goto upcall;
+
+ //------------------------------------------------------------------
+
+ if(how_ == do_nomask_nofrag)
+ {
+ // send a single frame
+ fh_.fin = fin_;
+ fh_.len = buffer_bytes(cb_);
+ impl.wr_fb.clear();
+ detail::write<flat_static_buffer_base>(
+ impl.wr_fb, fh_);
+ impl.wr_cont = ! fin_;
+ BOOST_ASIO_CORO_YIELD
+ net::async_write(impl.stream(),
+ buffers_cat(impl.wr_fb.data(), cb_),
+ beast::detail::bind_continuation(std::move(*this)));
+ bytes_transferred_ += clamp(fh_.len);
+ if(impl.check_stop_now(ec))
+ goto upcall;
+ goto upcall;
+ }
+
+ //------------------------------------------------------------------
+
+ if(how_ == do_nomask_frag)
+ {
+ // send multiple frames
+ for(;;)
+ {
+ n = clamp(remain_, impl.wr_buf_size);
+ fh_.len = n;
+ remain_ -= n;
+ fh_.fin = fin_ ? remain_ == 0 : false;
+ impl.wr_fb.clear();
+ detail::write<flat_static_buffer_base>(
+ impl.wr_fb, fh_);
+ impl.wr_cont = ! fin_;
+ // Send frame
+ BOOST_ASIO_CORO_YIELD
+ net::async_write(impl.stream(), buffers_cat(
+ impl.wr_fb.data(),
+ buffers_prefix(clamp(fh_.len), cb_)),
+ beast::detail::bind_continuation(std::move(*this)));
+ n = clamp(fh_.len); // restore `n` on yield
+ bytes_transferred_ += n;
+ if(impl.check_stop_now(ec))
+ goto upcall;
+ if(remain_ == 0)
+ break;
+ cb_.consume(n);
+ fh_.op = detail::opcode::cont;
+
+ // Give up the write lock in between each frame
+ // so that outgoing control frames might be sent.
+ impl.wr_block.unlock(this);
+ if( impl.op_close.maybe_invoke()
+ || impl.op_idle_ping.maybe_invoke()
+ || impl.op_rd.maybe_invoke()
+ || impl.op_ping.maybe_invoke())
+ {
+ BOOST_ASSERT(impl.wr_block.is_locked());
+ goto do_suspend;
+ }
+ impl.wr_block.lock(this);
+ }
+ goto upcall;
+ }
+
+ //------------------------------------------------------------------
+
+ if(how_ == do_mask_nofrag)
+ {
+ // send a single frame using multiple writes
+ remain_ = beast::buffer_bytes(cb_);
+ fh_.fin = fin_;
+ fh_.len = remain_;
+ fh_.key = impl.create_mask();
+ detail::prepare_key(key_, fh_.key);
+ impl.wr_fb.clear();
+ detail::write<flat_static_buffer_base>(
+ impl.wr_fb, fh_);
+ n = clamp(remain_, impl.wr_buf_size);
+ net::buffer_copy(net::buffer(
+ impl.wr_buf.get(), n), cb_);
+ detail::mask_inplace(net::buffer(
+ impl.wr_buf.get(), n), key_);
+ remain_ -= n;
+ impl.wr_cont = ! fin_;
+ // write frame header and some payload
+ BOOST_ASIO_CORO_YIELD
+ net::async_write(impl.stream(), buffers_cat(
+ impl.wr_fb.data(),
+ net::buffer(impl.wr_buf.get(), n)),
+ beast::detail::bind_continuation(std::move(*this)));
+ // VFALCO What about consuming the buffer on error?
+ bytes_transferred_ +=
+ bytes_transferred - impl.wr_fb.size();
+ if(impl.check_stop_now(ec))
+ goto upcall;
+ while(remain_ > 0)
+ {
+ cb_.consume(impl.wr_buf_size);
+ n = clamp(remain_, impl.wr_buf_size);
+ net::buffer_copy(net::buffer(
+ impl.wr_buf.get(), n), cb_);
+ detail::mask_inplace(net::buffer(
+ impl.wr_buf.get(), n), key_);
+ remain_ -= n;
+ // write more payload
+ BOOST_ASIO_CORO_YIELD
+ net::async_write(impl.stream(),
+ net::buffer(impl.wr_buf.get(), n),
+ beast::detail::bind_continuation(std::move(*this)));
+ bytes_transferred_ += bytes_transferred;
+ if(impl.check_stop_now(ec))
+ goto upcall;
+ }
+ goto upcall;
+ }
+
+ //------------------------------------------------------------------
+
+ if(how_ == do_mask_frag)
+ {
+ // send multiple frames
+ for(;;)
+ {
+ n = clamp(remain_, impl.wr_buf_size);
+ remain_ -= n;
+ fh_.len = n;
+ fh_.key = impl.create_mask();
+ fh_.fin = fin_ ? remain_ == 0 : false;
+ detail::prepare_key(key_, fh_.key);
+ net::buffer_copy(net::buffer(
+ impl.wr_buf.get(), n), cb_);
+ detail::mask_inplace(net::buffer(
+ impl.wr_buf.get(), n), key_);
+ impl.wr_fb.clear();
+ detail::write<flat_static_buffer_base>(
+ impl.wr_fb, fh_);
+ impl.wr_cont = ! fin_;
+ // Send frame
+ BOOST_ASIO_CORO_YIELD
+ net::async_write(impl.stream(), buffers_cat(
+ impl.wr_fb.data(),
+ net::buffer(impl.wr_buf.get(), n)),
+ beast::detail::bind_continuation(std::move(*this)));
+ n = bytes_transferred - impl.wr_fb.size();
+ bytes_transferred_ += n;
+ if(impl.check_stop_now(ec))
+ goto upcall;
+ if(remain_ == 0)
+ break;
+ cb_.consume(n);
+ fh_.op = detail::opcode::cont;
+ // Give up the write lock in between each frame
+ // so that outgoing control frames might be sent.
+ impl.wr_block.unlock(this);
+ if( impl.op_close.maybe_invoke()
+ || impl.op_idle_ping.maybe_invoke()
+ || impl.op_rd.maybe_invoke()
+ || impl.op_ping.maybe_invoke())
+ {
+ BOOST_ASSERT(impl.wr_block.is_locked());
+ goto do_suspend;
+ }
+ impl.wr_block.lock(this);
+ }
+ goto upcall;
+ }
+
+ //------------------------------------------------------------------
+
+ if(how_ == do_deflate)
+ {
+ // send compressed frames
+ for(;;)
+ {
+ b = net::buffer(impl.wr_buf.get(),
+ impl.wr_buf_size);
+ more_ = impl.deflate(b, cb_, fin_, in_, ec);
+ if(impl.check_stop_now(ec))
+ goto upcall;
+ n = buffer_bytes(b);
+ if(n == 0)
+ {
+ // The input was consumed, but there is
+ // no output due to compression latency.
+ BOOST_ASSERT(! fin_);
+ BOOST_ASSERT(buffer_bytes(cb_) == 0);
+ goto upcall;
+ }
+ if(fh_.mask)
+ {
+ fh_.key = impl.create_mask();
+ detail::prepared_key key;
+ detail::prepare_key(key, fh_.key);
+ detail::mask_inplace(b, key);
+ }
+ fh_.fin = ! more_;
+ fh_.len = n;
+ impl.wr_fb.clear();
+ detail::write<
+ flat_static_buffer_base>(impl.wr_fb, fh_);
+ impl.wr_cont = ! fin_;
+ // Send frame
+ BOOST_ASIO_CORO_YIELD
+ net::async_write(impl.stream(), buffers_cat(
+ impl.wr_fb.data(), b),
+ beast::detail::bind_continuation(std::move(*this)));
+ bytes_transferred_ += in_;
+ if(impl.check_stop_now(ec))
+ goto upcall;
+ if(more_)
+ {
+ fh_.op = detail::opcode::cont;
+ fh_.rsv1 = false;
+ // Give up the write lock in between each frame
+ // so that outgoing control frames might be sent.
+ impl.wr_block.unlock(this);
+ if( impl.op_close.maybe_invoke()
+ || impl.op_idle_ping.maybe_invoke()
+ || impl.op_rd.maybe_invoke()
+ || impl.op_ping.maybe_invoke())
+ {
+ BOOST_ASSERT(impl.wr_block.is_locked());
+ goto do_suspend;
+ }
+ impl.wr_block.lock(this);
+ }
+ else
+ {
+ if(fh_.fin)
+ impl.do_context_takeover_write(impl.role);
+ goto upcall;
+ }
+ }
+ }
+
+ //--------------------------------------------------------------------------
+
+ upcall:
+ impl.wr_block.unlock(this);
+ impl.op_close.maybe_invoke()
+ || impl.op_idle_ping.maybe_invoke()
+ || impl.op_rd.maybe_invoke()
+ || impl.op_ping.maybe_invoke();
+ this->complete(cont, ec, bytes_transferred_);
+ }
+}
+
+template<class NextLayer, bool deflateSupported>
+struct stream<NextLayer, deflateSupported>::
+ run_write_some_op
+{
+ template<
+ class WriteHandler,
+ class ConstBufferSequence>
+ void
+ operator()(
+ WriteHandler&& h,
+ boost::shared_ptr<impl_type> const& sp,
+ bool fin,
+ ConstBufferSequence const& b)
+ {
+ // If you get an error on the following line it means
+ // that your handler does not meet the documented type
+ // requirements for the handler.
+
+ static_assert(
+ beast::detail::is_invocable<WriteHandler,
+ void(error_code, std::size_t)>::value,
+ "WriteHandler type requirements not met");
+
+ write_some_op<
+ typename std::decay<WriteHandler>::type,
+ ConstBufferSequence>(
+ std::forward<WriteHandler>(h),
+ sp,
+ fin,
+ b);
+ }
+};
+
+//------------------------------------------------------------------------------
+
+template<class NextLayer, bool deflateSupported>
+template<class ConstBufferSequence>
+std::size_t
+stream<NextLayer, deflateSupported>::
+write_some(bool fin, ConstBufferSequence const& buffers)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream type requirements not met");
+ static_assert(net::is_const_buffer_sequence<
+ ConstBufferSequence>::value,
+ "ConstBufferSequence type requirements not met");
+ error_code ec;
+ auto const bytes_transferred =
+ write_some(fin, buffers, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+ return bytes_transferred;
+}
+
+template<class NextLayer, bool deflateSupported>
+template<class ConstBufferSequence>
+std::size_t
+stream<NextLayer, deflateSupported>::
+write_some(bool fin,
+ ConstBufferSequence const& buffers, error_code& ec)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream type requirements not met");
+ static_assert(net::is_const_buffer_sequence<
+ ConstBufferSequence>::value,
+ "ConstBufferSequence type requirements not met");
+ using beast::detail::clamp;
+ auto& impl = *impl_;
+ std::size_t bytes_transferred = 0;
+ ec = {};
+ if(impl.check_stop_now(ec))
+ return bytes_transferred;
+ detail::frame_header fh;
+ if(! impl.wr_cont)
+ {
+ impl.begin_msg();
+ fh.rsv1 = impl.wr_compress;
+ }
+ else
+ {
+ fh.rsv1 = false;
+ }
+ fh.rsv2 = false;
+ fh.rsv3 = false;
+ fh.op = impl.wr_cont ?
+ detail::opcode::cont : impl.wr_opcode;
+ fh.mask = impl.role == role_type::client;
+ auto remain = buffer_bytes(buffers);
+ if(impl.wr_compress)
+ {
+
+ buffers_suffix<
+ ConstBufferSequence> cb(buffers);
+ for(;;)
+ {
+ auto b = net::buffer(
+ impl.wr_buf.get(), impl.wr_buf_size);
+ auto const more = impl.deflate(
+ b, cb, fin, bytes_transferred, ec);
+ if(impl.check_stop_now(ec))
+ return bytes_transferred;
+ auto const n = buffer_bytes(b);
+ if(n == 0)
+ {
+ // The input was consumed, but there
+ // is no output due to compression
+ // latency.
+ BOOST_ASSERT(! fin);
+ BOOST_ASSERT(buffer_bytes(cb) == 0);
+ fh.fin = false;
+ break;
+ }
+ if(fh.mask)
+ {
+ fh.key = this->impl_->create_mask();
+ detail::prepared_key key;
+ detail::prepare_key(key, fh.key);
+ detail::mask_inplace(b, key);
+ }
+ fh.fin = ! more;
+ fh.len = n;
+ detail::fh_buffer fh_buf;
+ detail::write<
+ flat_static_buffer_base>(fh_buf, fh);
+ impl.wr_cont = ! fin;
+ net::write(impl.stream(),
+ buffers_cat(fh_buf.data(), b), ec);
+ if(impl.check_stop_now(ec))
+ return bytes_transferred;
+ if(! more)
+ break;
+ fh.op = detail::opcode::cont;
+ fh.rsv1 = false;
+ }
+ if(fh.fin)
+ impl.do_context_takeover_write(impl.role);
+ }
+ else if(! fh.mask)
+ {
+ if(! impl.wr_frag)
+ {
+ // no mask, no autofrag
+ fh.fin = fin;
+ fh.len = remain;
+ detail::fh_buffer fh_buf;
+ detail::write<
+ flat_static_buffer_base>(fh_buf, fh);
+ impl.wr_cont = ! fin;
+ net::write(impl.stream(),
+ buffers_cat(fh_buf.data(), buffers), ec);
+ if(impl.check_stop_now(ec))
+ return bytes_transferred;
+ bytes_transferred += remain;
+ }
+ else
+ {
+ // no mask, autofrag
+ BOOST_ASSERT(impl.wr_buf_size != 0);
+ buffers_suffix<
+ ConstBufferSequence> cb{buffers};
+ for(;;)
+ {
+ auto const n = clamp(remain, impl.wr_buf_size);
+ remain -= n;
+ fh.len = n;
+ fh.fin = fin ? remain == 0 : false;
+ detail::fh_buffer fh_buf;
+ detail::write<
+ flat_static_buffer_base>(fh_buf, fh);
+ impl.wr_cont = ! fin;
+ net::write(impl.stream(),
+ beast::buffers_cat(fh_buf.data(),
+ beast::buffers_prefix(n, cb)), ec);
+ bytes_transferred += n;
+ if(impl.check_stop_now(ec))
+ return bytes_transferred;
+ if(remain == 0)
+ break;
+ fh.op = detail::opcode::cont;
+ cb.consume(n);
+ }
+ }
+ }
+ else if(! impl.wr_frag)
+ {
+ // mask, no autofrag
+ fh.fin = fin;
+ fh.len = remain;
+ fh.key = this->impl_->create_mask();
+ detail::prepared_key key;
+ detail::prepare_key(key, fh.key);
+ detail::fh_buffer fh_buf;
+ detail::write<
+ flat_static_buffer_base>(fh_buf, fh);
+ buffers_suffix<
+ ConstBufferSequence> cb{buffers};
+ {
+ auto const n =
+ clamp(remain, impl.wr_buf_size);
+ auto const b =
+ net::buffer(impl.wr_buf.get(), n);
+ net::buffer_copy(b, cb);
+ cb.consume(n);
+ remain -= n;
+ detail::mask_inplace(b, key);
+ impl.wr_cont = ! fin;
+ net::write(impl.stream(),
+ buffers_cat(fh_buf.data(), b), ec);
+ bytes_transferred += n;
+ if(impl.check_stop_now(ec))
+ return bytes_transferred;
+ }
+ while(remain > 0)
+ {
+ auto const n =
+ clamp(remain, impl.wr_buf_size);
+ auto const b =
+ net::buffer(impl.wr_buf.get(), n);
+ net::buffer_copy(b, cb);
+ cb.consume(n);
+ remain -= n;
+ detail::mask_inplace(b, key);
+ net::write(impl.stream(), b, ec);
+ bytes_transferred += n;
+ if(impl.check_stop_now(ec))
+ return bytes_transferred;
+ }
+ }
+ else
+ {
+ // mask, autofrag
+ BOOST_ASSERT(impl.wr_buf_size != 0);
+ buffers_suffix<
+ ConstBufferSequence> cb(buffers);
+ for(;;)
+ {
+ fh.key = this->impl_->create_mask();
+ detail::prepared_key key;
+ detail::prepare_key(key, fh.key);
+ auto const n =
+ clamp(remain, impl.wr_buf_size);
+ auto const b =
+ net::buffer(impl.wr_buf.get(), n);
+ net::buffer_copy(b, cb);
+ detail::mask_inplace(b, key);
+ fh.len = n;
+ remain -= n;
+ fh.fin = fin ? remain == 0 : false;
+ impl.wr_cont = ! fh.fin;
+ detail::fh_buffer fh_buf;
+ detail::write<
+ flat_static_buffer_base>(fh_buf, fh);
+ net::write(impl.stream(),
+ buffers_cat(fh_buf.data(), b), ec);
+ bytes_transferred += n;
+ if(impl.check_stop_now(ec))
+ return bytes_transferred;
+ if(remain == 0)
+ break;
+ fh.op = detail::opcode::cont;
+ cb.consume(n);
+ }
+ }
+ return bytes_transferred;
+}
+
+template<class NextLayer, bool deflateSupported>
+template<class ConstBufferSequence, class WriteHandler>
+BOOST_BEAST_ASYNC_RESULT2(WriteHandler)
+stream<NextLayer, deflateSupported>::
+async_write_some(bool fin,
+ ConstBufferSequence const& bs, WriteHandler&& handler)
+{
+ static_assert(is_async_stream<next_layer_type>::value,
+ "AsyncStream type requirements not met");
+ static_assert(net::is_const_buffer_sequence<
+ ConstBufferSequence>::value,
+ "ConstBufferSequence type requirements not met");
+ return net::async_initiate<
+ WriteHandler,
+ void(error_code, std::size_t)>(
+ run_write_some_op{},
+ handler,
+ impl_,
+ fin,
+ bs);
+}
+
+//------------------------------------------------------------------------------
+
+template<class NextLayer, bool deflateSupported>
+template<class ConstBufferSequence>
+std::size_t
+stream<NextLayer, deflateSupported>::
+write(ConstBufferSequence const& buffers)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream type requirements not met");
+ static_assert(net::is_const_buffer_sequence<
+ ConstBufferSequence>::value,
+ "ConstBufferSequence type requirements not met");
+ error_code ec;
+ auto const bytes_transferred = write(buffers, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+ return bytes_transferred;
+}
+
+template<class NextLayer, bool deflateSupported>
+template<class ConstBufferSequence>
+std::size_t
+stream<NextLayer, deflateSupported>::
+write(ConstBufferSequence const& buffers, error_code& ec)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream type requirements not met");
+ static_assert(net::is_const_buffer_sequence<
+ ConstBufferSequence>::value,
+ "ConstBufferSequence type requirements not met");
+ return write_some(true, buffers, ec);
+}
+
+template<class NextLayer, bool deflateSupported>
+template<class ConstBufferSequence, class WriteHandler>
+BOOST_BEAST_ASYNC_RESULT2(WriteHandler)
+stream<NextLayer, deflateSupported>::
+async_write(
+ ConstBufferSequence const& bs, WriteHandler&& handler)
+{
+ static_assert(is_async_stream<next_layer_type>::value,
+ "AsyncStream type requirements not met");
+ static_assert(net::is_const_buffer_sequence<
+ ConstBufferSequence>::value,
+ "ConstBufferSequence type requirements not met");
+ return net::async_initiate<
+ WriteHandler,
+ void(error_code, std::size_t)>(
+ run_write_some_op{},
+ handler,
+ impl_,
+ true,
+ bs);
+}
+
+} // websocket
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/websocket/impl/write.ipp b/boost/beast/websocket/impl/write.ipp
deleted file mode 100644
index 08b9943d8a..0000000000
--- a/boost/beast/websocket/impl/write.ipp
+++ /dev/null
@@ -1,897 +0,0 @@
-//
-// 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_IMPL_WRITE_IPP
-#define BOOST_BEAST_WEBSOCKET_IMPL_WRITE_IPP
-
-#include <boost/beast/core/bind_handler.hpp>
-#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/flat_static_buffer.hpp>
-#include <boost/beast/core/type_traits.hpp>
-#include <boost/beast/core/detail/clamp.hpp>
-#include <boost/beast/core/detail/config.hpp>
-#include <boost/beast/websocket/detail/frame.hpp>
-#include <boost/asio/associated_allocator.hpp>
-#include <boost/asio/associated_executor.hpp>
-#include <boost/asio/coroutine.hpp>
-#include <boost/asio/executor_work_guard.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>
-#include <algorithm>
-#include <memory>
-
-namespace boost {
-namespace beast {
-namespace websocket {
-
-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, deflateSupported>::write_some_op
- : public boost::asio::coroutine
-{
- Handler h_;
- stream<NextLayer, deflateSupported>& ws_;
- boost::asio::executor_work_guard<decltype(std::declval<
- stream<NextLayer, deflateSupported>&>().get_executor())> wg_;
- 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_;
- int how_;
- bool fin_;
- bool more_ = false; // for ubsan
- 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&) = delete;
-
- template<class DeducedHandler>
- write_some_op(
- DeducedHandler&& h,
- stream<NextLayer, deflateSupported>& ws,
- bool fin,
- Buffers const& bs)
- : h_(std::forward<DeducedHandler>(h))
- , ws_(ws)
- , wg_(ws_.get_executor())
- , cb_(bs)
- , fin_(fin)
- {
- }
-
- using allocator_type =
- boost::asio::associated_allocator_t<Handler>;
-
- allocator_type
- get_allocator() const noexcept
- {
- return (boost::asio::get_associated_allocator)(h_);
- }
-
- using executor_type = boost::asio::associated_executor_t<
- Handler, decltype(std::declval<stream<NextLayer, deflateSupported>&>().get_executor())>;
-
- executor_type
- get_executor() const noexcept
- {
- return (boost::asio::get_associated_executor)(
- h_, ws_.get_executor());
- }
-
- Handler&
- handler()
- {
- return h_;
- }
-
- void operator()(
- error_code ec = {},
- std::size_t bytes_transferred = 0,
- bool cont = true);
-
- friend
- bool asio_handler_is_continuation(write_some_op* op)
- {
- using boost::asio::asio_handler_is_continuation;
- 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, bool deflateSupported>
-template<class Buffers, class Handler>
-void
-stream<NextLayer, deflateSupported>::
-write_some_op<Buffers, Handler>::
-operator()(
- error_code ec,
- std::size_t bytes_transferred,
- bool cont)
-{
- using beast::detail::clamp;
- using boost::asio::buffer;
- using boost::asio::buffer_copy;
- using boost::asio::buffer_size;
- using boost::asio::mutable_buffer;
- enum
- {
- do_nomask_nofrag,
- do_nomask_frag,
- do_mask_nofrag,
- do_mask_frag,
- do_deflate
- };
- std::size_t n;
- boost::asio::mutable_buffer b;
- cont_ = cont;
- BOOST_ASIO_CORO_REENTER(*this)
- {
- // Set up the outgoing frame header
- if(! ws_.wr_cont_)
- {
- ws_.begin_msg();
- fh_.rsv1 = ws_.wr_compress_;
- }
- else
- {
- fh_.rsv1 = false;
- }
- fh_.rsv2 = false;
- fh_.rsv3 = false;
- fh_.op = ws_.wr_cont_ ?
- detail::opcode::cont : ws_.wr_opcode_;
- fh_.mask =
- ws_.role_ == role_type::client;
-
- // Choose a write algorithm
- if(ws_.wr_compress_)
- {
- how_ = do_deflate;
- }
- else if(! fh_.mask)
- {
- if(! ws_.wr_frag_)
- {
- how_ = do_nomask_nofrag;
- }
- else
- {
- BOOST_ASSERT(ws_.wr_buf_size_ != 0);
- remain_ = buffer_size(cb_);
- if(remain_ > ws_.wr_buf_size_)
- how_ = do_nomask_frag;
- else
- how_ = do_nomask_nofrag;
- }
- }
- else
- {
- if(! ws_.wr_frag_)
- {
- how_ = do_mask_nofrag;
- }
- else
- {
- BOOST_ASSERT(ws_.wr_buf_size_ != 0);
- remain_ = buffer_size(cb_);
- if(remain_ > ws_.wr_buf_size_)
- how_ = do_mask_frag;
- else
- how_ = do_mask_nofrag;
- }
- }
-
- // Maybe suspend
- if(ws_.wr_block_.try_lock(this))
- {
- // Make sure the stream is open
- if(! ws_.check_open(ec))
- goto upcall;
- }
- else
- {
- do_suspend:
- // Suspend
- BOOST_ASIO_CORO_YIELD
- ws_.paused_wr_.emplace(std::move(*this));
-
- // Acquire the write block
- ws_.wr_block_.lock(this);
-
- // Resume
- BOOST_ASIO_CORO_YIELD
- boost::asio::post(
- ws_.get_executor(), std::move(*this));
- BOOST_ASSERT(ws_.wr_block_.is_locked(this));
-
- // Make sure the stream is open
- if(! ws_.check_open(ec))
- goto upcall;
- }
-
- //------------------------------------------------------------------
-
- if(how_ == do_nomask_nofrag)
- {
- fh_.fin = fin_;
- fh_.len = buffer_size(cb_);
- ws_.wr_fb_.reset();
- detail::write<flat_static_buffer_base>(
- ws_.wr_fb_, fh_);
- ws_.wr_cont_ = ! fin_;
- // Send frame
- BOOST_ASIO_CORO_YIELD
- boost::asio::async_write(ws_.stream_,
- buffers_cat(ws_.wr_fb_.data(), cb_),
- std::move(*this));
- if(! ws_.check_ok(ec))
- goto upcall;
- bytes_transferred_ += clamp(fh_.len);
- goto upcall;
- }
-
- //------------------------------------------------------------------
-
- else if(how_ == do_nomask_frag)
- {
- for(;;)
- {
- n = clamp(remain_, ws_.wr_buf_size_);
- fh_.len = n;
- remain_ -= n;
- fh_.fin = fin_ ? remain_ == 0 : false;
- ws_.wr_fb_.reset();
- detail::write<flat_static_buffer_base>(
- ws_.wr_fb_, fh_);
- ws_.wr_cont_ = ! fin_;
- // Send frame
- BOOST_ASIO_CORO_YIELD
- boost::asio::async_write(
- ws_.stream_, buffers_cat(
- ws_.wr_fb_.data(), buffers_prefix(
- clamp(fh_.len), cb_)),
- std::move(*this));
- if(! ws_.check_ok(ec))
- goto upcall;
- n = clamp(fh_.len); // because yield
- bytes_transferred_ += n;
- if(remain_ == 0)
- break;
- cb_.consume(n);
- fh_.op = detail::opcode::cont;
- // Allow outgoing control frames to
- // be sent in between message frames
- 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_.is_locked());
- goto do_suspend;
- }
- ws_.wr_block_.lock(this);
- }
- goto upcall;
- }
-
- //------------------------------------------------------------------
-
- else if(how_ == do_mask_nofrag)
- {
- remain_ = buffer_size(cb_);
- fh_.fin = fin_;
- fh_.len = remain_;
- fh_.key = ws_.create_mask();
- detail::prepare_key(key_, fh_.key);
- ws_.wr_fb_.reset();
- detail::write<flat_static_buffer_base>(
- ws_.wr_fb_, fh_);
- n = clamp(remain_, ws_.wr_buf_size_);
- buffer_copy(buffer(
- ws_.wr_buf_.get(), n), cb_);
- detail::mask_inplace(buffer(
- ws_.wr_buf_.get(), n), key_);
- remain_ -= n;
- ws_.wr_cont_ = ! fin_;
- // Send frame header and partial payload
- BOOST_ASIO_CORO_YIELD
- boost::asio::async_write(
- ws_.stream_, buffers_cat(ws_.wr_fb_.data(),
- buffer(ws_.wr_buf_.get(), n)),
- std::move(*this));
- if(! ws_.check_ok(ec))
- goto upcall;
- bytes_transferred_ +=
- bytes_transferred - ws_.wr_fb_.size();
- while(remain_ > 0)
- {
- cb_.consume(ws_.wr_buf_size_);
- n = clamp(remain_, ws_.wr_buf_size_);
- buffer_copy(buffer(
- ws_.wr_buf_.get(), n), cb_);
- detail::mask_inplace(buffer(
- ws_.wr_buf_.get(), n), key_);
- remain_ -= n;
- // Send partial payload
- BOOST_ASIO_CORO_YIELD
- boost::asio::async_write(ws_.stream_,
- buffer(ws_.wr_buf_.get(), n),
- std::move(*this));
- if(! ws_.check_ok(ec))
- goto upcall;
- bytes_transferred_ += bytes_transferred;
- }
- goto upcall;
- }
-
- //------------------------------------------------------------------
-
- else if(how_ == do_mask_frag)
- {
- for(;;)
- {
- n = clamp(remain_, ws_.wr_buf_size_);
- remain_ -= n;
- fh_.len = n;
- fh_.key = ws_.create_mask();
- fh_.fin = fin_ ? remain_ == 0 : false;
- detail::prepare_key(key_, fh_.key);
- buffer_copy(buffer(
- ws_.wr_buf_.get(), n), cb_);
- detail::mask_inplace(buffer(
- ws_.wr_buf_.get(), n), key_);
- ws_.wr_fb_.reset();
- detail::write<flat_static_buffer_base>(
- ws_.wr_fb_, fh_);
- ws_.wr_cont_ = ! fin_;
- // Send frame
- BOOST_ASIO_CORO_YIELD
- boost::asio::async_write(ws_.stream_,
- buffers_cat(ws_.wr_fb_.data(),
- buffer(ws_.wr_buf_.get(), n)),
- std::move(*this));
- if(! ws_.check_ok(ec))
- goto upcall;
- n = bytes_transferred - ws_.wr_fb_.size();
- bytes_transferred_ += n;
- if(remain_ == 0)
- break;
- cb_.consume(n);
- fh_.op = detail::opcode::cont;
- // Allow outgoing control frames to
- // be sent in between message frames:
- 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_.is_locked());
- goto do_suspend;
- }
- ws_.wr_block_.lock(this);
- }
- goto upcall;
- }
-
- //------------------------------------------------------------------
-
- else if(how_ == do_deflate)
- {
- for(;;)
- {
- b = buffer(ws_.wr_buf_.get(),
- ws_.wr_buf_size_);
- more_ = ws_.deflate(b, cb_, fin_, in_, ec);
- if(! ws_.check_ok(ec))
- goto upcall;
- n = buffer_size(b);
- if(n == 0)
- {
- // The input was consumed, but there
- // is no output due to compression
- // latency.
- BOOST_ASSERT(! fin_);
- BOOST_ASSERT(buffer_size(cb_) == 0);
- goto upcall;
- }
- if(fh_.mask)
- {
- fh_.key = ws_.create_mask();
- detail::prepared_key key;
- detail::prepare_key(key, fh_.key);
- detail::mask_inplace(b, key);
- }
- fh_.fin = ! more_;
- fh_.len = n;
- ws_.wr_fb_.reset();
- detail::write<
- flat_static_buffer_base>(ws_.wr_fb_, fh_);
- ws_.wr_cont_ = ! fin_;
- // Send frame
- BOOST_ASIO_CORO_YIELD
- boost::asio::async_write(ws_.stream_,
- buffers_cat(ws_.wr_fb_.data(), b),
- std::move(*this));
- if(! ws_.check_ok(ec))
- goto upcall;
- bytes_transferred_ += in_;
- if(more_)
- {
- fh_.op = detail::opcode::cont;
- fh_.rsv1 = false;
- // Allow outgoing control frames to
- // be sent in between message frames:
- 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_.is_locked());
- goto do_suspend;
- }
- ws_.wr_block_.lock(this);
- }
- else
- {
- if(fh_.fin)
- ws_.do_context_takeover_write(ws_.role_);
- goto upcall;
- }
- }
- }
-
- //--------------------------------------------------------------------------
-
- upcall:
- ws_.wr_block_.unlock(this);
- ws_.paused_close_.maybe_invoke() ||
- ws_.paused_rd_.maybe_invoke() ||
- ws_.paused_ping_.maybe_invoke();
- if(! cont_)
- {
- BOOST_ASIO_CORO_YIELD
- boost::asio::post(
- ws_.get_executor(),
- bind_handler(std::move(*this), ec, bytes_transferred_));
- }
- h_(ec, bytes_transferred_);
- }
-}
-
-//------------------------------------------------------------------------------
-
-template<class NextLayer, bool deflateSupported>
-template<class ConstBufferSequence>
-std::size_t
-stream<NextLayer, deflateSupported>::
-write_some(bool fin, ConstBufferSequence const& buffers)
-{
- static_assert(is_sync_stream<next_layer_type>::value,
- "SyncStream requirements not met");
- static_assert(boost::asio::is_const_buffer_sequence<
- ConstBufferSequence>::value,
- "ConstBufferSequence requirements not met");
- error_code ec;
- auto const bytes_transferred =
- write_some(fin, buffers, ec);
- if(ec)
- BOOST_THROW_EXCEPTION(system_error{ec});
- return bytes_transferred;
-}
-
-template<class NextLayer, bool deflateSupported>
-template<class ConstBufferSequence>
-std::size_t
-stream<NextLayer, deflateSupported>::
-write_some(bool fin,
- ConstBufferSequence const& buffers, error_code& ec)
-{
- static_assert(is_sync_stream<next_layer_type>::value,
- "SyncStream requirements not met");
- static_assert(boost::asio::is_const_buffer_sequence<
- ConstBufferSequence>::value,
- "ConstBufferSequence requirements not met");
- using beast::detail::clamp;
- using boost::asio::buffer;
- using boost::asio::buffer_copy;
- using boost::asio::buffer_size;
- std::size_t bytes_transferred = 0;
- ec.assign(0, ec.category());
- // Make sure the stream is open
- if(! check_open(ec))
- return bytes_transferred;
- detail::frame_header fh;
- if(! wr_cont_)
- {
- begin_msg();
- fh.rsv1 = wr_compress_;
- }
- else
- {
- fh.rsv1 = false;
- }
- fh.rsv2 = false;
- fh.rsv3 = false;
- fh.op = wr_cont_ ?
- detail::opcode::cont : wr_opcode_;
- fh.mask = role_ == role_type::client;
- auto remain = buffer_size(buffers);
- if(wr_compress_)
- {
- buffers_suffix<
- ConstBufferSequence> cb{buffers};
- for(;;)
- {
- auto b = buffer(
- wr_buf_.get(), wr_buf_size_);
- auto const more = this->deflate(
- b, cb, fin, bytes_transferred, ec);
- if(! check_ok(ec))
- return bytes_transferred;
- auto const n = buffer_size(b);
- if(n == 0)
- {
- // The input was consumed, but there
- // is no output due to compression
- // latency.
- BOOST_ASSERT(! fin);
- BOOST_ASSERT(buffer_size(cb) == 0);
- fh.fin = false;
- break;
- }
- if(fh.mask)
- {
- fh.key = this->create_mask();
- detail::prepared_key key;
- detail::prepare_key(key, fh.key);
- detail::mask_inplace(b, key);
- }
- fh.fin = ! more;
- fh.len = n;
- detail::fh_buffer fh_buf;
- detail::write<
- flat_static_buffer_base>(fh_buf, fh);
- wr_cont_ = ! fin;
- boost::asio::write(stream_,
- buffers_cat(fh_buf.data(), b), ec);
- if(! check_ok(ec))
- return bytes_transferred;
- if(! more)
- break;
- fh.op = detail::opcode::cont;
- fh.rsv1 = false;
- }
- if(fh.fin)
- this->do_context_takeover_write(role_);
- }
- else if(! fh.mask)
- {
- if(! wr_frag_)
- {
- // no mask, no autofrag
- fh.fin = fin;
- fh.len = remain;
- detail::fh_buffer fh_buf;
- detail::write<
- flat_static_buffer_base>(fh_buf, fh);
- wr_cont_ = ! fin;
- boost::asio::write(stream_,
- buffers_cat(fh_buf.data(), buffers), ec);
- if(! check_ok(ec))
- return bytes_transferred;
- bytes_transferred += remain;
- }
- else
- {
- // no mask, autofrag
- BOOST_ASSERT(wr_buf_size_ != 0);
- buffers_suffix<
- ConstBufferSequence> cb{buffers};
- for(;;)
- {
- auto const n = clamp(remain, wr_buf_size_);
- remain -= n;
- fh.len = n;
- fh.fin = fin ? remain == 0 : false;
- detail::fh_buffer fh_buf;
- detail::write<
- flat_static_buffer_base>(fh_buf, fh);
- wr_cont_ = ! fin;
- boost::asio::write(stream_,
- buffers_cat(fh_buf.data(),
- buffers_prefix(n, cb)), ec);
- if(! check_ok(ec))
- return bytes_transferred;
- bytes_transferred += n;
- if(remain == 0)
- break;
- fh.op = detail::opcode::cont;
- cb.consume(n);
- }
- }
- }
- else if(! wr_frag_)
- {
- // mask, no autofrag
- fh.fin = fin;
- fh.len = remain;
- fh.key = this->create_mask();
- detail::prepared_key key;
- detail::prepare_key(key, fh.key);
- detail::fh_buffer fh_buf;
- detail::write<
- flat_static_buffer_base>(fh_buf, fh);
- buffers_suffix<
- ConstBufferSequence> cb{buffers};
- {
- auto const n = clamp(remain, wr_buf_size_);
- auto const b = buffer(wr_buf_.get(), n);
- buffer_copy(b, cb);
- cb.consume(n);
- remain -= n;
- detail::mask_inplace(b, key);
- wr_cont_ = ! fin;
- boost::asio::write(stream_,
- buffers_cat(fh_buf.data(), b), ec);
- if(! check_ok(ec))
- return bytes_transferred;
- bytes_transferred += n;
- }
- while(remain > 0)
- {
- auto const n = clamp(remain, wr_buf_size_);
- auto const b = buffer(wr_buf_.get(), n);
- buffer_copy(b, cb);
- cb.consume(n);
- remain -= n;
- detail::mask_inplace(b, key);
- boost::asio::write(stream_, b, ec);
- if(! check_ok(ec))
- return bytes_transferred;
- bytes_transferred += n;
- }
- }
- else
- {
- // mask, autofrag
- BOOST_ASSERT(wr_buf_size_ != 0);
- buffers_suffix<
- ConstBufferSequence> cb{buffers};
- for(;;)
- {
- fh.key = this->create_mask();
- detail::prepared_key key;
- detail::prepare_key(key, fh.key);
- auto const n = clamp(remain, wr_buf_size_);
- auto const b = buffer(wr_buf_.get(), n);
- buffer_copy(b, cb);
- detail::mask_inplace(b, key);
- fh.len = n;
- remain -= n;
- fh.fin = fin ? remain == 0 : false;
- wr_cont_ = ! fh.fin;
- detail::fh_buffer fh_buf;
- detail::write<
- flat_static_buffer_base>(fh_buf, fh);
- boost::asio::write(stream_,
- buffers_cat(fh_buf.data(), b), ec);
- if(! check_ok(ec))
- return bytes_transferred;
- bytes_transferred += n;
- if(remain == 0)
- break;
- fh.op = detail::opcode::cont;
- cb.consume(n);
- }
- }
- return bytes_transferred;
-}
-
-template<class NextLayer, bool deflateSupported>
-template<class ConstBufferSequence, class WriteHandler>
-BOOST_ASIO_INITFN_RESULT_TYPE(
- WriteHandler, void(error_code, std::size_t))
-stream<NextLayer, deflateSupported>::
-async_write_some(bool fin,
- ConstBufferSequence const& bs, WriteHandler&& handler)
-{
- static_assert(is_async_stream<next_layer_type>::value,
- "AsyncStream requirements not met");
- static_assert(boost::asio::is_const_buffer_sequence<
- ConstBufferSequence>::value,
- "ConstBufferSequence requirements not met");
- 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))>{
- std::move(init.completion_handler), *this, fin, bs}(
- {}, 0, false);
- return init.result.get();
-}
-
-//------------------------------------------------------------------------------
-
-template<class NextLayer, bool deflateSupported>
-template<class ConstBufferSequence>
-std::size_t
-stream<NextLayer, deflateSupported>::
-write(ConstBufferSequence const& buffers)
-{
- static_assert(is_sync_stream<next_layer_type>::value,
- "SyncStream requirements not met");
- static_assert(boost::asio::is_const_buffer_sequence<
- ConstBufferSequence>::value,
- "ConstBufferSequence requirements not met");
- error_code ec;
- auto const bytes_transferred = write(buffers, ec);
- if(ec)
- BOOST_THROW_EXCEPTION(system_error{ec});
- return bytes_transferred;
-}
-
-template<class NextLayer, bool deflateSupported>
-template<class ConstBufferSequence>
-std::size_t
-stream<NextLayer, deflateSupported>::
-write(ConstBufferSequence const& buffers, error_code& ec)
-{
- static_assert(is_sync_stream<next_layer_type>::value,
- "SyncStream requirements not met");
- static_assert(boost::asio::is_const_buffer_sequence<
- ConstBufferSequence>::value,
- "ConstBufferSequence requirements not met");
- return write_some(true, buffers, ec);
-}
-
-template<class NextLayer, bool deflateSupported>
-template<class ConstBufferSequence, class WriteHandler>
-BOOST_ASIO_INITFN_RESULT_TYPE(
- WriteHandler, void(error_code, std::size_t))
-stream<NextLayer, deflateSupported>::
-async_write(
- ConstBufferSequence const& bs, WriteHandler&& handler)
-{
- static_assert(is_async_stream<next_layer_type>::value,
- "AsyncStream requirements not met");
- static_assert(boost::asio::is_const_buffer_sequence<
- ConstBufferSequence>::value,
- "ConstBufferSequence requirements not met");
- 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))>{
- std::move(init.completion_handler), *this, true, bs}(
- {}, 0, false);
- return init.result.get();
-}
-
-} // websocket
-} // beast
-} // boost
-
-#endif
diff --git a/boost/beast/websocket/option.hpp b/boost/beast/websocket/option.hpp
index 83788994d1..62ebfa4a2b 100644
--- a/boost/beast/websocket/option.hpp
+++ b/boost/beast/websocket/option.hpp
@@ -1,5 +1,5 @@
//
-// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+// Copyright (c) 2016-2019 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)
diff --git a/boost/beast/websocket/rfc6455.hpp b/boost/beast/websocket/rfc6455.hpp
index 921f896351..c3f012699d 100644
--- a/boost/beast/websocket/rfc6455.hpp
+++ b/boost/beast/websocket/rfc6455.hpp
@@ -1,5 +1,5 @@
//
-// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+// Copyright (c) 2016-2019 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)
@@ -13,7 +13,9 @@
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/static_string.hpp>
#include <boost/beast/core/string.hpp>
+#include <boost/beast/http/empty_body.hpp>
#include <boost/beast/http/message.hpp>
+#include <boost/beast/http/string_body.hpp>
#include <array>
#include <cstdint>
@@ -21,6 +23,12 @@ namespace boost {
namespace beast {
namespace websocket {
+/// The type of object holding HTTP Upgrade requests
+using request_type = http::request<http::empty_body>;
+
+/// The type of object holding HTTP Upgrade responses
+using response_type = http::response<http::string_body>;
+
/** Returns `true` if the specified HTTP request is a WebSocket Upgrade.
This function returns `true` when the passed HTTP Request
@@ -36,7 +44,7 @@ namespace websocket {
@par Example
@code
- void handle_connection(boost::asio::ip::tcp::socket& sock)
+ void handle_connection(net::ip::tcp::socket& sock)
{
boost::beast::flat_buffer buffer;
boost::beast::http::request<boost::beast::http::string_body> req;
@@ -210,6 +218,6 @@ struct close_reason
} // beast
} // boost
-#include <boost/beast/websocket/impl/rfc6455.ipp>
+#include <boost/beast/websocket/impl/rfc6455.hpp>
#endif
diff --git a/boost/beast/websocket/role.hpp b/boost/beast/websocket/role.hpp
index d886936585..1efb2eb888 100644
--- a/boost/beast/websocket/role.hpp
+++ b/boost/beast/websocket/role.hpp
@@ -1,5 +1,5 @@
//
-// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+// Copyright (c) 2016-2019 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)
@@ -12,48 +12,24 @@
#include <boost/beast/core/detail/config.hpp>
+#ifndef BOOST_BEAST_ALLOW_DEPRECATED
+
+#error This file is deprecated interface, #define BOOST_BEAST_ALLOW_DEPRECATED to allow it
+
+#else
+
+#include <boost/beast/core/role.hpp>
+
namespace boost {
namespace beast {
namespace websocket {
-/** The role of the websocket stream endpoint.
-
- Whether the endpoint is a client or server affects the
- behavior of the <em>Close the WebSocket Connection</em>
- operation described in rfc6455 section 7.1.1.
- The shutdown behavior depends on the type of the next
- layer template parameter used to construct the @ref stream.
- Other next layer types including user-defined types
- may implement different role-based behavior when
- performing the close operation.
-
- The default implementation for @ref stream when the next
- layer type is a `boost::asio::ip::tcp::socket` behaves
- as follows:
-
- @li In the client role, a TCP/IP shutdown is sent after
- reading all remaining data on the connection.
-
- @li In the server role, a TCP/IP shutdown is sent before
- reading all remaining data on the connection.
-
- When the next layer type is a `boost::asio::ssl::stream`,
- the connection is closed by performing the SSL closing
- handshake corresponding to the role type, client or server.
-
- @see https://tools.ietf.org/html/rfc6455#section-7.1.1
-*/
-enum class role_type
-{
- /// The stream is operating as a client.
- client,
-
- /// The stream is operating as a server.
- server
-};
+using role_type = beast::role_type;
} // websocket
} // beast
} // boost
#endif
+
+#endif
diff --git a/boost/beast/websocket/ssl.hpp b/boost/beast/websocket/ssl.hpp
index d51921a481..b7d62a3729 100644
--- a/boost/beast/websocket/ssl.hpp
+++ b/boost/beast/websocket/ssl.hpp
@@ -1,5 +1,5 @@
//
-// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+// Copyright (c) 2016-2019 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)
@@ -17,15 +17,14 @@
namespace boost {
namespace beast {
-namespace websocket {
-/** Tear down a `boost::asio::ssl::stream`.
+/** Tear down a `net::ssl::stream`.
This tears down a connection. The implementation will call
the overload of this function based on the `Stream` parameter
used to consruct the socket. When `Stream` is a user defined
- type, and not a `boost::asio::ip::tcp::socket` or any
- `boost::asio::ssl::stream`, callers are responsible for
+ type, and not a `net::ip::tcp::socket` or any
+ `net::ssl::stream`, callers are responsible for
providing a suitable overload of this function.
@param role The role of the local endpoint
@@ -38,16 +37,16 @@ template<class SyncStream>
void
teardown(
role_type role,
- boost::asio::ssl::stream<SyncStream>& stream,
+ net::ssl::stream<SyncStream>& stream,
error_code& ec);
-/** Start tearing down a `boost::asio::ssl::stream`.
+/** Start tearing down a `net::ssl::stream`.
This begins tearing down a connection asynchronously.
The implementation will call the overload of this function
based on the `Stream` parameter used to consruct the socket.
When `Stream` is a user defined type, and not a
- `boost::asio::ip::tcp::socket` or any `boost::asio::ssl::stream`,
+ `net::ip::tcp::socket` or any `net::ssl::stream`,
callers are responsible for providing a suitable overload
of this function.
@@ -55,30 +54,31 @@ teardown(
@param stream The stream to tear down.
- @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(
+ @param handler The completion handler to invoke when the operation
+ completes. The implementation takes ownership of the handler by
+ performing a decay-copy. The equivalent function signature of
+ the handler must be:
+ @code
+ void handler(
error_code const& error // result of operation
- ); @endcode
+ );
+ @endcode
Regardless of whether the asynchronous operation completes
immediately or not, the handler will not be invoked from within
this function. Invocation of the handler will be performed in a
- manner equivalent to using boost::asio::io_context::post().
+ manner equivalent to using `net::post`.
*/
template<class AsyncStream, class TeardownHandler>
-inline
void
async_teardown(
role_type role,
- boost::asio::ssl::stream<AsyncStream>& stream,
+ net::ssl::stream<AsyncStream>& stream,
TeardownHandler&& handler);
-} // websocket
} // beast
} // boost
-#include <boost/beast/websocket/impl/ssl.ipp>
+#include <boost/beast/websocket/impl/ssl.hpp>
#endif
diff --git a/boost/beast/websocket/stream.hpp b/boost/beast/websocket/stream.hpp
index cb8a00fca9..a9d79d2de4 100644
--- a/boost/beast/websocket/stream.hpp
+++ b/boost/beast/websocket/stream.hpp
@@ -1,5 +1,5 @@
//
-// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+// Copyright (c) 2016-2019 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)
@@ -13,41 +13,33 @@
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/websocket/error.hpp>
#include <boost/beast/websocket/option.hpp>
-#include <boost/beast/websocket/role.hpp>
#include <boost/beast/websocket/rfc6455.hpp>
+#include <boost/beast/websocket/stream_base.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/impl_base.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/websocket/detail/prng.hpp>
+#include <boost/beast/core/role.hpp>
+#include <boost/beast/core/stream_traits.hpp>
#include <boost/beast/core/string.hpp>
#include <boost/beast/core/detail/type_traits.hpp>
-#include <boost/beast/http/empty_body.hpp>
-#include <boost/beast/http/message.hpp>
-#include <boost/beast/http/string_body.hpp>
#include <boost/beast/http/detail/type_traits.hpp>
#include <boost/asio/async_result.hpp>
#include <boost/asio/error.hpp>
+#include <boost/shared_ptr.hpp>
#include <algorithm>
#include <cstdint>
#include <functional>
#include <limits>
+#include <memory>
#include <type_traits>
+#include <random>
namespace boost {
namespace beast {
namespace websocket {
-/// The type of object holding HTTP Upgrade requests
-using request_type = http::request<http::empty_body>;
-
-/// The type of object holding HTTP Upgrade responses
-using response_type = http::response<http::string_body>;
-
/** The type of received control frame.
Values of this type are passed to the control frame
@@ -76,7 +68,7 @@ class frame_test;
The @ref stream class template provides asynchronous and blocking
message-oriented functionality necessary for clients and servers
to utilize the WebSocket protocol.
-
+
For asynchronous operations, the application must ensure
that they are are all performed within the same implicit
or explicit strand.
@@ -88,24 +80,22 @@ class frame_test;
operations are performed within the same implicit or explicit strand.
@par Example
-
- To use the @ref stream template with an `ip::tcp::socket`,
- you would write:
-
+ To declare the @ref stream object with a @ref tcp_stream in a
+ multi-threaded asynchronous program using a strand, you may write:
@code
- websocket::stream<ip::tcp::socket> ws{io_context};
+ websocket::stream<tcp_stream> ws{net::io_context::strand(ioc)};
@endcode
- Alternatively, you can write:
+ Alternatively, for a single-threaded or synchronous application
+ you may write:
@code
- ip::tcp::socket sock{io_context};
- websocket::stream<ip::tcp::socket&> ws{sock};
+ websocket::stream<tcp_stream> ws(ioc);
@endcode
@tparam NextLayer The type representing the next layer, to which
data will be read and written during operations. For synchronous
- operations, the type must support the @b SyncStream concept.
+ operations, the type must support the <em>SyncStream</em> concept.
For asynchronous operations, the type must support the
- @b AsyncStream concept.
+ <em>AsyncStream</em> concept.
@tparam deflateSupported A `bool` indicating whether or not the
stream will be capable of negotiating the permessage-deflate websocket
@@ -117,26 +107,47 @@ class frame_test;
are pending asynchronous operations associated with it.
@par Concepts
- @b AsyncStream,
- @b DynamicBuffer,
- @b SyncStream
+ @li <em>AsyncStream</em>
+ @li <em>DynamicBuffer</em>
+ @li <em>SyncStream</em>
+
+ @see
+ @li <a href="https://tools.ietf.org/html/rfc6455#section-4.1">Websocket Opening Handshake Client Requirements (RFC6455)</a>
+ @li <a href="https://tools.ietf.org/html/rfc6455#section-4.2">Websocket Opening Handshake Server Requirements (RFC6455)</a>
+ @li <a href="https://tools.ietf.org/html/rfc6455#section-7.1.2">Websocket Closing Handshake (RFC6455)</a>
+ @li <a href="https://tools.ietf.org/html/rfc6455#section-5.5.1">Websocket Close (RFC6455)</a>
+ @li <a href="https://tools.ietf.org/html/rfc6455#section-5.5.2">WebSocket Ping (RFC6455)</a>
+ @li <a href="https://tools.ietf.org/html/rfc6455#section-5.5.3">WebSocket Pong (RFC6455)</a>
+ @li <a href="https://tools.ietf.org/html/rfc7230#section-5.4">Host field (RFC7230)</a>
+ @li <a href="https://tools.ietf.org/html/rfc7230#section-3.1.1">request-target (RFC7230)</a>
+ @li <a href="https://tools.ietf.org/html/rfc7230#section-5.3.1">origin-form (RFC7230)</a>
*/
template<
class NextLayer,
bool deflateSupported>
class stream
#if ! BOOST_BEAST_DOXYGEN
- : private detail::stream_base<deflateSupported>
+ : private stream_base
#endif
{
+ struct impl_type;
+
+ boost::shared_ptr<impl_type> impl_;
+
+ using time_point = typename
+ std::chrono::steady_clock::time_point;
+
+ using control_cb_type =
+ std::function<void(frame_type, string_view)>;
+
friend class close_test;
friend class frame_test;
friend class ping_test;
- friend class read1_test;
friend class read2_test;
+ friend class read3_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.
@@ -144,75 +155,10 @@ class stream
static std::size_t constexpr max_control_frame_size = 2 + 8 + 4 + 125;
static std::size_t constexpr tcp_frame_size = 1536;
- using control_cb_type =
- std::function<void(frame_type, string_view)>;
-
- enum class status
+ static time_point never() noexcept
{
- open,
- closing,
- closed,
- failed
- };
-
- NextLayer stream_; // the wrapped stream
- close_reason cr_; // set from received close frame
- control_cb_type ctrl_cb_; // control callback
-
- std::size_t rd_msg_max_ // max message size
- = 16 * 1024 * 1024;
- std::uint64_t rd_size_ // total size of current message so far
- = 0;
- 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
- detail::frame_buffer rd_fb_; // to write control frames (during reads)
- detail::utf8_checker rd_utf8_; // to validate utf8
- static_buffer<
- +tcp_frame_size> rd_buf_; // buffer for reads
- detail::opcode rd_op_ // current message binary or text
- = detail::opcode::text;
- bool rd_cont_ // `true` if the next frame is a continuation
- = false;
- bool rd_done_ // set when a message is done
- = true;
- bool rd_close_ // did we read a close frame?
- = false;
- detail::soft_mutex rd_block_; // op currently reading
-
- role_type role_ // server or client
- = role_type::client;
- status status_
- = status::closed;
-
- 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
- = false;
- bool wr_frag_ // autofrag the current message
- = false;
- bool wr_frag_opt_ // autofrag option setting
- = true;
- bool wr_compress_ // compress current message
- = false;
- detail::opcode wr_opcode_ // message type
- = detail::opcode::text;
- std::unique_ptr<
- std::uint8_t[]> wr_buf_; // write buffer
- std::size_t wr_buf_size_ // write buffer size (current message)
- = 0;
- std::size_t wr_buf_opt_ // write buffer size option setting
- = 4096;
- detail::fh_buffer wr_fb_; // header buffer used for writes
-
- detail::pausation paused_rd_; // paused read op
- 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 (async read)
- detail::pausation paused_r_close_;// paused close op (async read)
+ return (time_point::max)();
+ }
public:
/// Indicates if the permessage-deflate extension is supported
@@ -223,11 +169,9 @@ public:
using next_layer_type =
typename std::remove_reference<NextLayer>::type;
- /// The type of the lowest layer.
- 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;
+ using executor_type =
+ beast::executor_type<next_layer_type>;
/** Destructor
@@ -236,27 +180,20 @@ public:
@note A stream object must not be destroyed while there
are pending asynchronous operations associated with it.
*/
- ~stream() = default;
+ ~stream();
/** Constructor
If `NextLayer` is move constructible, this function
will move-construct a new stream from the existing stream.
- @note The behavior of move assignment on or from streams
- with active or pending operations is undefined.
+ After the move, the only valid operation on the moved-from
+ object is destruction.
*/
stream(stream&&) = default;
- /** Assignment
-
- If `NextLayer` is move assignable, this function
- will move-assign a new stream from the existing stream.
-
- @note The behavior of move assignment on or from streams
- with active or pending operations is undefined.
- */
- stream& operator=(stream&&) = default;
+ /// Move assignment (deleted)
+ stream& operator=(stream&&) = delete;
/** Constructor
@@ -276,17 +213,14 @@ public:
//--------------------------------------------------------------------------
/** Get the executor associated with the object.
-
+
This function may be used to obtain the executor object that the
stream uses to dispatch handlers for asynchronous operations.
@return A copy of the executor that stream will use to dispatch handlers.
*/
executor_type
- get_executor() noexcept
- {
- return stream_.get_executor();
- }
+ get_executor() const noexcept;
/** Get a reference to the next layer
@@ -297,10 +231,7 @@ public:
stream layers.
*/
next_layer_type&
- next_layer()
- {
- return stream_;
- }
+ next_layer() noexcept;
/** Get a reference to the next layer
@@ -311,38 +242,7 @@ public:
stream layers.
*/
next_layer_type const&
- next_layer() const
- {
- return stream_;
- }
-
- /** Get a reference to the lowest layer
-
- This function returns a reference to the lowest layer
- in a stack of stream layers.
-
- @return A reference to the lowest layer in the stack of
- stream layers.
- */
- lowest_layer_type&
- lowest_layer()
- {
- return stream_.lowest_layer();
- }
-
- /** Get a reference to the lowest layer
-
- This function returns a reference to the lowest layer
- in a stack of stream layers.
-
- @return A reference to the lowest layer in the stack of
- stream layers. Ownership is not transferred to the caller.
- */
- lowest_layer_type const&
- lowest_layer() const
- {
- return stream_.lowest_layer();
- }
+ next_layer() const noexcept;
//--------------------------------------------------------------------------
//
@@ -356,10 +256,7 @@ public:
no error has occurred.
*/
bool
- is_open() const
- {
- return status_ == status::open;
- }
+ is_open() const noexcept;
/** Returns `true` if the latest message data indicates binary.
@@ -371,10 +268,7 @@ public:
undefined.
*/
bool
- got_binary() const
- {
- return rd_op_ == detail::opcode::binary;
- }
+ got_binary() const noexcept;
/** Returns `true` if the latest message data indicates text.
@@ -393,20 +287,14 @@ public:
/// Returns `true` if the last completed read finished the current message.
bool
- is_message_done() const
- {
- return rd_done_;
- }
+ is_message_done() const noexcept;
- /** Returns the close reason received from the peer.
+ /** Returns the close reason received from the remote peer.
This is only valid after a read completes with error::closed.
*/
close_reason const&
- reason() const
- {
- return cr_;
- }
+ reason() const noexcept;
/** Returns a suggested maximum buffer size for the next call to read.
@@ -423,11 +311,7 @@ public:
*/
std::size_t
read_size_hint(
- std::size_t initial_size = +tcp_frame_size) const
- {
- return read_size_hint(initial_size,
- is_deflate_supported{});
- }
+ std::size_t initial_size = +tcp_frame_size) const;
/** Returns a suggested maximum buffer size for the next call to read.
@@ -458,23 +342,33 @@ public:
//
//--------------------------------------------------------------------------
+#if BOOST_BEAST_DOXYGEN
+ template<class Option>
+ void
+ get_option(Option& opt);
+
+ template<class Option>
+ void
+ set_option(Option opt);
+#else
+
+ void set_option(decorator opt);
+
+ void get_option(timeout& opt);
+ void set_option(timeout const& opt);
+#endif
+
/** 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(o, is_deflate_supported{});
- }
+ set_option(permessage_deflate const& o);
/// Get the permessage-deflate extension options
void
- get_option(permessage_deflate& o)
- {
- get_option(o, is_deflate_supported{});
- }
+ get_option(permessage_deflate& o);
/** Set the automatic fragmentation option.
@@ -496,17 +390,11 @@ public:
@endcode
*/
void
- auto_fragment(bool value)
- {
- wr_frag_opt_ = value;
- }
+ auto_fragment(bool value);
/// Returns `true` if the automatic fragmentation option is set.
bool
- auto_fragment() const
- {
- return wr_frag_opt_;
- }
+ auto_fragment() const;
/** Set the binary message write option.
@@ -528,19 +416,11 @@ public:
@endcode
*/
void
- binary(bool value)
- {
- wr_opcode_ = value ?
- detail::opcode::binary :
- detail::opcode::text;
- }
+ binary(bool value);
/// Returns `true` if the binary message write option is set.
bool
- binary() const
- {
- return wr_opcode_ == detail::opcode::binary;
- }
+ binary() const;
/** Set a callback to be invoked on each incoming control frame.
@@ -587,20 +467,14 @@ public:
in undefined behavior.
*/
void
- control_callback(std::function<void(frame_type, string_view)> cb)
- {
- ctrl_cb_ = std::move(cb);
- }
+ control_callback(std::function<void(frame_type, string_view)> cb);
/** Reset the control frame callback.
This function removes any previously set control frame callback.
*/
void
- control_callback()
- {
- ctrl_cb_ = {};
- }
+ control_callback();
/** Set the maximum incoming message size option.
@@ -620,17 +494,11 @@ public:
@param amount The limit on the size of incoming messages.
*/
void
- read_message_max(std::size_t amount)
- {
- rd_msg_max_ = amount;
- }
+ read_message_max(std::size_t amount);
/// Returns the maximum incoming message size setting.
std::size_t
- read_message_max() const
- {
- return rd_msg_max_;
- }
+ read_message_max() const;
/** Set whether the PRNG is cryptographically secure
@@ -658,10 +526,7 @@ public:
cryptographically secure.
*/
void
- secure_prng(bool value)
- {
- this->secure_prng_ = value;
- }
+ secure_prng(bool value);
/** Set the write buffer size option.
@@ -683,26 +548,17 @@ public:
@par Example
Setting the write buffer size.
@code
- ws.write_buffer_size(8192);
+ ws.write_buffer_bytes(8192);
@endcode
@param amount The size of the write buffer in bytes.
*/
void
- write_buffer_size(std::size_t amount)
- {
- if(amount < 8)
- BOOST_THROW_EXCEPTION(std::invalid_argument{
- "write buffer size underflow"});
- wr_buf_opt_ = amount;
- };
+ write_buffer_bytes(std::size_t amount);
/// Returns the size of the write buffer.
std::size_t
- write_buffer_size() const
- {
- return wr_buf_opt_;
- }
+ write_buffer_bytes() const;
/** Set the text message write option.
@@ -724,19 +580,23 @@ public:
@endcode
*/
void
- text(bool value)
- {
- wr_opcode_ = value ?
- detail::opcode::text :
- detail::opcode::binary;
- }
+ text(bool value);
/// Returns `true` if the text message write option is set.
bool
- text() const
- {
- return wr_opcode_ == detail::opcode::text;
- }
+ text() const;
+
+ /*
+ timer settings
+
+ * Timer is disabled
+ * Close on timeout
+ - no complete frame received, OR
+ - no complete frame sent
+ * Ping on timeout
+ - ping on no complete frame received
+ * if can't ping?
+ */
//--------------------------------------------------------------------------
//
@@ -744,92 +604,100 @@ public:
//
//--------------------------------------------------------------------------
- /** Send an HTTP WebSocket Upgrade request and receive the response.
+ /** Perform the WebSocket handshake in the client role.
- This function is used to synchronously send the WebSocket
- upgrade HTTP request. The call blocks until one of the
- following conditions is true:
+ This function is used to perform the
+ <a href="https://en.wikipedia.org/wiki/WebSocket#Protocol_handshake">WebSocket handshake</a>,
+ required before messages can be sent and received. During the handshake,
+ the client sends the Websocket Upgrade HTTP request, and the server
+ replies with an HTTP response indicating the result of the handshake.
+
+ The call blocks until one of the following conditions is true:
@li The request is sent and the response is received.
- @li An error occurs on the stream
+ @li An error occurs.
- This function is implemented in terms of one or more calls to the
- next layer's `read_some` and `write_some` functions.
+ The algorithm, known as a <em>composed operation</em>, is implemented
+ in terms of calls to the next layer's `read_some` and `write_some`
+ functions.
- The operation is successful if the received HTTP response indicates
- a successful HTTP Upgrade (represented by a Status-Code of 101,
- "switching protocols").
+ The handshake is successful if the received HTTP response
+ indicates the upgrade was accepted by the server, represented by a
+ <a href="https://tools.ietf.org/html/rfc7230#section-3.1.2">status-code</a>
+ of @ref beast::http::status::switching_protocols.
- @param host The name of the remote host,
- required by the HTTP protocol.
+ @param host The name of the remote host. This is required by
+ the HTTP protocol to set the "Host" header field.
- @param target The Request Target, which may not be empty,
- required by the HTTP protocol.
+ @param target The request-target, in origin-form. The server may use the
+ target to distinguish different services on the same listening port.
@throws system_error Thrown on failure.
@par Example
@code
- websocket::stream<ip::tcp::socket> ws{io_context};
- ...
- try
- {
- ws.handshake("localhost", "/");
- }
- catch(...)
- {
- // An error occurred.
- }
+ ws.handshake("localhost", "/");
@endcode
+
+ @see
+ @li <a href="https://tools.ietf.org/html/rfc6455#section-4.1">Websocket Opening Handshake Client Requirements (RFC6455)</a>
+ @li <a href="https://tools.ietf.org/html/rfc7230#section-5.4">Host field (RFC7230)</a>
+ @li <a href="https://tools.ietf.org/html/rfc7230#section-3.1.1">request-target (RFC7230)</a>
+ @li <a href="https://tools.ietf.org/html/rfc7230#section-5.3.1">origin-form (RFC7230)</a>
*/
void
handshake(
string_view host,
string_view target);
- /** Send an HTTP WebSocket Upgrade request and receive the response.
+ /** Perform the WebSocket handshake in the client role.
+
+ This function is used to perform the
+ <a href="https://en.wikipedia.org/wiki/WebSocket#Protocol_handshake">WebSocket handshake</a>,
+ required before messages can be sent and received. During the handshake,
+ the client sends the Websocket Upgrade HTTP request, and the server
+ replies with an HTTP response indicating the result of the handshake.
- This function is used to synchronously send the WebSocket
- upgrade HTTP request. The call blocks until one of the
- following conditions is true:
+ The call blocks until one of the following conditions is true:
@li The request is sent and the response is received.
- @li An error occurs on the stream
+ @li An error occurs.
- This function is implemented in terms of one or more calls to the
- next layer's `read_some` and `write_some` functions.
+ The algorithm, known as a <em>composed operation</em>, is implemented
+ in terms of calls to the next layer's `read_some` and `write_some`
+ functions.
- The operation is successful if the received HTTP response indicates
- a successful HTTP Upgrade (represented by a Status-Code of 101,
- "switching protocols").
+ The handshake is successful if the received HTTP response
+ indicates the upgrade was accepted by the server, represented by a
+ <a href="https://tools.ietf.org/html/rfc7230#section-3.1.2">status-code</a>
+ of @ref beast::http::status::switching_protocols.
@param res The HTTP Upgrade response returned by the remote
- endpoint.
+ endpoint. The caller may use the response to access any
+ additional information sent by the server.
- @param host The name of the remote host,
- required by the HTTP protocol.
+ @param host The name of the remote host. This is required by
+ the HTTP protocol to set the "Host" header field.
- @param target The Request Target, which may not be empty,
- required by the HTTP protocol.
+ @param target The request-target, in origin-form. The server may use the
+ target to distinguish different services on the same listening port.
@throws system_error Thrown on failure.
@par Example
@code
- websocket::stream<ip::tcp::socket> ws{io_context};
- ...
- try
- {
- response_type res;
- ws.handshake(res, "localhost", "/");
- }
- catch(...)
- {
- // An error occurred.
- }
+ response_type res;
+ ws.handshake(res, "localhost", "/");
+ std::cout << res;
@endcode
+
+ @see
+ @li <a href="https://tools.ietf.org/html/rfc6455#section-4.1">Websocket Opening Handshake Client Requirements (RFC6455)</a>
+ @li <a href="https://tools.ietf.org/html/rfc7230#section-5.4">Host field (RFC7230)</a>
+ @li <a href="https://tools.ietf.org/html/rfc7230#section-3.1.1">request-target (RFC7230)</a>
+ @li <a href="https://tools.ietf.org/html/rfc7230#section-5.3.1">origin-form (RFC7230)</a>
*/
void
handshake(
@@ -837,165 +705,48 @@ public:
string_view host,
string_view target);
- /** Send an HTTP WebSocket Upgrade request and receive the response.
+ /** Perform the WebSocket handshake in the client role.
- This function is used to synchronously send the WebSocket
- upgrade HTTP request. The call blocks until one of the
- following conditions is true:
+ This function is used to perform the
+ <a href="https://en.wikipedia.org/wiki/WebSocket#Protocol_handshake">WebSocket handshake</a>,
+ required before messages can be sent and received. During the handshake,
+ the client sends the Websocket Upgrade HTTP request, and the server
+ replies with an HTTP response indicating the result of the handshake.
- @li The request is sent and the response is received.
-
- @li An error occurs on the stream
-
- This function is implemented in terms of one or more calls to the
- next layer's `read_some` and `write_some` functions.
-
- The operation is successful if the received HTTP response indicates
- a successful HTTP Upgrade (represented by a Status-Code of 101,
- "switching protocols").
-
- @param host The name of the remote host,
- required by the HTTP protocol.
-
- @param target The Request Target, which may not be empty,
- required by the HTTP protocol.
-
- @param decorator A function object which will be called to modify
- the HTTP request object generated by the implementation. This
- could be used to set the User-Agent field, subprotocols, or other
- application or HTTP specific fields. The object will be called
- with this equivalent signature:
- @code void decorator(
- request_type& req
- ); @endcode
-
- @throws system_error Thrown on failure.
-
- @par Example
- @code
- websocket::stream<ip::tcp::socket> ws{io_context};
- ...
- try
- {
- ws.handshake("localhost", "/",
- [](request_type& req)
- {
- req.set(field::user_agent, "Beast");
- });
- }
- catch(...)
- {
- // An error occurred.
- }
- @endcode
- */
- template<class RequestDecorator>
- void
- handshake_ex(
- string_view host,
- string_view target,
- RequestDecorator const& decorator);
-
- /** Send an HTTP WebSocket Upgrade request and receive the response.
-
- This function is used to synchronously send the WebSocket
- upgrade HTTP request. The call blocks until one of the
- following conditions is true:
-
- @li The request is sent and the response is received.
-
- @li An error occurs on the stream
-
- This function is implemented in terms of one or more calls to the
- next layer's `read_some` and `write_some` functions.
-
- The operation is successful if the received HTTP response indicates
- a successful HTTP Upgrade (represented by a Status-Code of 101,
- "switching protocols").
-
- @param res The HTTP Upgrade response returned by the remote
- endpoint.
-
- @param host The name of the remote host,
- required by the HTTP protocol.
-
- @param target The Request Target, which may not be empty,
- required by the HTTP protocol.
-
- @param decorator A function object which will be called to modify
- the HTTP request object generated by the implementation. This
- could be used to set the User-Agent field, subprotocols, or other
- application or HTTP specific fields. The object will be called
- with this equivalent signature:
- @code void decorator(
- request_type& req
- ); @endcode
-
- @throws system_error Thrown on failure.
-
- @par Example
- @code
- websocket::stream<ip::tcp::socket> ws{io_context};
- ...
- try
- {
- response_type res;
- ws.handshake(res, "localhost", "/",
- [](request_type& req)
- {
- req.set(field::user_agent, "Beast");
- });
- }
- catch(...)
- {
- // An error occurred.
- }
- @endcode
- */
- template<class RequestDecorator>
- void
- handshake_ex(
- response_type& res,
- string_view host,
- string_view target,
- RequestDecorator const& decorator);
-
- /** Send an HTTP WebSocket Upgrade request and receive the response.
-
- This function is used to synchronously send the WebSocket
- upgrade HTTP request. The call blocks until one of the
- following conditions is true:
+ The call blocks until one of the following conditions is true:
@li The request is sent and the response is received.
- @li An error occurs on the stream
+ @li An error occurs.
- This function is implemented in terms of one or more calls to the
- next layer's `read_some` and `write_some` functions.
+ The algorithm, known as a <em>composed operation</em>, is implemented
+ in terms of calls to the next layer's `read_some` and `write_some`
+ functions.
- The operation is successful if the received HTTP response indicates
- a successful HTTP Upgrade (represented by a Status-Code of 101,
- "switching protocols").
+ The handshake is successful if the received HTTP response
+ indicates the upgrade was accepted by the server, represented by a
+ <a href="https://tools.ietf.org/html/rfc7230#section-3.1.2">status-code</a>
+ of @ref beast::http::status::switching_protocols.
- @param host The name of the remote host,
- required by the HTTP protocol.
+ @param host The name of the remote host. This is required by
+ the HTTP protocol to set the "Host" header field.
- @param target The Request Target, which may not be empty,
- required by the HTTP protocol.
+ @param target The request-target, in origin-form. The server may use the
+ target to distinguish different services on the same listening port.
@param ec Set to indicate what error occurred, if any.
@par Example
@code
- websocket::stream<ip::tcp::socket> ws{io_context};
- ...
error_code ec;
- ws.handshake(host, target, ec);
- if(ec)
- {
- // An error occurred.
- }
+ ws.handshake("localhost", "/", ec);
@endcode
+
+ @see
+ @li <a href="https://tools.ietf.org/html/rfc6455#section-4.1">Websocket Opening Handshake Client Requirements (RFC6455)</a>
+ @li <a href="https://tools.ietf.org/html/rfc7230#section-5.4">Host field (RFC7230)</a>
+ @li <a href="https://tools.ietf.org/html/rfc7230#section-3.1.1">request-target (RFC7230)</a>
+ @li <a href="https://tools.ietf.org/html/rfc7230#section-5.3.1">origin-form (RFC7230)</a>
*/
void
handshake(
@@ -1003,46 +754,55 @@ public:
string_view target,
error_code& ec);
- /** Send an HTTP WebSocket Upgrade request and receive the response.
+ /** Perform the WebSocket handshake in the client role.
- This function is used to synchronously send the WebSocket
- upgrade HTTP request. The call blocks until one of the
- following conditions is true:
+ This function is used to perform the
+ <a href="https://en.wikipedia.org/wiki/WebSocket#Protocol_handshake">WebSocket handshake</a>,
+ required before messages can be sent and received. During the handshake,
+ the client sends the Websocket Upgrade HTTP request, and the server
+ replies with an HTTP response indicating the result of the handshake.
+
+ The call blocks until one of the following conditions is true:
@li The request is sent and the response is received.
- @li An error occurs on the stream
+ @li An error occurs.
- This function is implemented in terms of one or more calls to the
- next layer's `read_some` and `write_some` functions.
+ The algorithm, known as a <em>composed operation</em>, is implemented
+ in terms of calls to the next layer's `read_some` and `write_some`
+ functions.
- The operation is successful if the received HTTP response indicates
- a successful HTTP Upgrade (represented by a Status-Code of 101,
- "switching protocols").
+ The handshake is successful if the received HTTP response
+ indicates the upgrade was accepted by the server, represented by a
+ <a href="https://tools.ietf.org/html/rfc7230#section-3.1.2">status-code</a>
+ of @ref beast::http::status::switching_protocols.
@param res The HTTP Upgrade response returned by the remote
- endpoint. If `ec` is set, the returned value is undefined.
+ endpoint. The caller may use the response to access any
+ additional information sent by the server.
- @param host The name of the remote host,
- required by the HTTP protocol.
+ @param host The name of the remote host. This is required by
+ the HTTP protocol to set the "Host" header field.
- @param target The Request Target, which may not be empty,
- required by the HTTP protocol.
+ @param target The request-target, in origin-form. The server may use the
+ target to distinguish different services on the same listening port.
@param ec Set to indicate what error occurred, if any.
@par Example
@code
- websocket::stream<ip::tcp::socket> ws{io_context};
- ...
error_code ec;
response_type res;
- ws.handshake(res, host, target, ec);
- if(ec)
- {
- // An error occurred.
- }
+ ws.handshake(res, "localhost", "/", ec);
+ if(! ec)
+ std::cout << res;
@endcode
+
+ @see
+ @li <a href="https://tools.ietf.org/html/rfc6455#section-4.1">Websocket Opening Handshake Client Requirements (RFC6455)</a>
+ @li <a href="https://tools.ietf.org/html/rfc7230#section-5.4">Host field (RFC7230)</a>
+ @li <a href="https://tools.ietf.org/html/rfc7230#section-3.1.1">request-target (RFC7230)</a>
+ @li <a href="https://tools.ietf.org/html/rfc7230#section-5.3.1">origin-form (RFC7230)</a>
*/
void
handshake(
@@ -1051,349 +811,158 @@ public:
string_view target,
error_code& ec);
- /** Send an HTTP WebSocket Upgrade request and receive the response.
-
- This function is used to synchronously send the WebSocket
- upgrade HTTP request. The call blocks until one of the
- following conditions is true:
-
- @li The request is sent and the response is received.
+ /** Perform the WebSocket handshake asynchronously in the client role.
- @li An error occurs on the stream
+ This initiating function is used to asynchronously begin performing the
+ <a href="https://en.wikipedia.org/wiki/WebSocket#Protocol_handshake">WebSocket handshake</a>,
+ required before messages can be sent and received. During the handshake,
+ the client sends the Websocket Upgrade HTTP request, and the server
+ replies with an HTTP response indicating the result of the handshake.
- This function is implemented in terms of one or more calls to the
- next layer's `read_some` and `write_some` functions.
+ This call always returns immediately. The asynchronous operation
+ will continue until one of the following conditions is true:
- The operation is successful if the received HTTP response indicates
- a successful HTTP Upgrade (represented by a Status-Code of 101,
- "switching protocols").
-
- @param host The name of the remote host,
- required by the HTTP protocol.
-
- @param target The Request Target, which may not be empty,
- required by the HTTP protocol.
-
- @param decorator A function object which will be called to modify
- the HTTP request object generated by the implementation. This
- could be used to set the User-Agent field, subprotocols, or other
- application or HTTP specific fields. The object will be called
- with this equivalent signature:
- @code void decorator(
- request_type& req
- ); @endcode
+ @li The request is sent and the response is received.
- @param ec Set to indicate what error occurred, if any.
+ @li An error occurs.
- @par Example
+ The algorithm, known as a <em>composed asynchronous operation</em>,
+ is implemented in terms of calls to the next layer's `async_read_some`
+ and `async_write_some` functions. No other operation may be performed
+ on the stream until this operation completes.
+
+ The handshake is successful if the received HTTP response
+ indicates the upgrade was accepted by the server, represented by a
+ <a href="https://tools.ietf.org/html/rfc7230#section-3.1.2">status-code</a>
+ of @ref beast::http::status::switching_protocols.
+
+ @param host The name of the remote host. This is required by
+ the HTTP protocol to set the "Host" header field.
+ The implementation will not access the string data after the
+ initiating function returns.
+
+ @param target The request-target, in origin-form. The server may use the
+ target to distinguish different services on the same listening port.
+ The implementation will not access the string data after the
+ initiating function returns.
+
+ @param handler The completion handler to invoke when the operation
+ completes. The implementation takes ownership of the handler by
+ performing a decay-copy. The equivalent function signature of
+ the handler must be:
@code
- websocket::stream<ip::tcp::socket> ws{io_context};
- ...
- error_code ec;
- ws.handshake("localhost", "/",
- [](request_type& req)
- {
- req.set(field::user_agent, "Beast");
- },
- ec);
- if(ec)
- {
- // An error occurred.
- }
+ void handler(
+ error_code const& ec // Result of operation
+ );
@endcode
- */
- template<class RequestDecorator>
- void
- handshake_ex(
- string_view host,
- string_view target,
- RequestDecorator const& decorator,
- error_code& ec);
-
- /** Send an HTTP WebSocket Upgrade request and receive the response.
-
- This function is used to synchronously send the WebSocket
- upgrade HTTP request. The call blocks until one of the
- following conditions is true:
-
- @li The request is sent and the response is received.
-
- @li An error occurs on the stream
-
- This function is implemented in terms of one or more calls to the
- next layer's `read_some` and `write_some` functions.
-
- The operation is successful if the received HTTP response indicates
- a successful HTTP Upgrade (represented by a Status-Code of 101,
- "switching protocols").
-
- @param res The HTTP Upgrade response returned by the remote
- endpoint.
-
- @param host The name of the remote host,
- required by the HTTP protocol.
-
- @param target The Request Target, which may not be empty,
- required by the HTTP protocol.
-
- @param decorator A function object which will be called to modify
- the HTTP request object generated by the implementation. This
- could be used to set the User-Agent field, subprotocols, or other
- application or HTTP specific fields. The object will be called
- with this equivalent signature:
- @code void decorator(
- request_type& req
- ); @endcode
-
- @param ec Set to indicate what error occurred, if any.
+ Regardless of whether the asynchronous operation completes
+ immediately or not, the handler will not be invoked from within
+ this function. Invocation of the handler will be performed in a
+ manner equivalent to using `net::post`.
@par Example
@code
- websocket::stream<ip::tcp::socket> ws{io_context};
- ...
- error_code ec;
- response_type res;
- ws.handshake(res, "localhost", "/",
- [](request_type& req)
+ ws.async_handshake("localhost", "/",
+ [](error_code ec)
{
- req.set(field::user_agent, "Beast");
- },
- ec);
- if(ec)
- {
- // An error occurred.
- }
+ if(ec)
+ std::cerr << "Error: " << ec.message() << "\n";
+ });
@endcode
- */
- template<class RequestDecorator>
- void
- handshake_ex(
- response_type& res,
- string_view host,
- string_view target,
- RequestDecorator const& decorator,
- error_code& ec);
-
- /** Start an asynchronous operation to send an upgrade request and receive the response.
-
- This function is used to asynchronously send the HTTP WebSocket
- upgrade request and receive the HTTP WebSocket Upgrade response.
- This function call always returns immediately. The asynchronous
- operation will continue until one of the following conditions is
- true:
-
- @li The request is sent and the response is received.
-
- @li An error occurs on the stream
-
- This operation is implemented in terms of one or more calls to the
- next layer's `async_read_some` and `async_write_some` functions, and
- is known as a <em>composed operation</em>. The program must ensure
- that the stream performs no other operations until this operation
- completes.
-
- The operation is successful if the received HTTP response indicates
- a successful HTTP Upgrade (represented by a Status-Code of 101,
- "switching protocols").
-
- @param host The name of the remote host, required by
- the HTTP protocol. Copies may be made as needed.
- @param target The Request Target, which may not be empty,
- required by the HTTP protocol. Copies of this parameter may
- be made as needed.
-
- @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
- Regardless of whether the asynchronous operation completes
- immediately or not, the handler will not be invoked from within
- this function. Invocation of the handler will be performed in a
- manner equivalent to using `boost::asio::io_context::post`.
+ @see
+ @li <a href="https://tools.ietf.org/html/rfc6455#section-4.1">Websocket Opening Handshake Client Requirements (RFC6455)</a>
+ @li <a href="https://tools.ietf.org/html/rfc7230#section-5.4">Host field (RFC7230)</a>
+ @li <a href="https://tools.ietf.org/html/rfc7230#section-3.1.1">request-target (RFC7230)</a>
+ @li <a href="https://tools.ietf.org/html/rfc7230#section-5.3.1">origin-form (RFC7230)</a>
*/
template<class HandshakeHandler>
- BOOST_ASIO_INITFN_RESULT_TYPE(
- HandshakeHandler, void(error_code))
+ BOOST_BEAST_ASYNC_RESULT1(HandshakeHandler)
async_handshake(
string_view host,
string_view target,
HandshakeHandler&& handler);
- /** Start an asynchronous operation to send an upgrade request and receive the response.
+ /** Perform the WebSocket handshake asynchronously in the client role.
+
+ This initiating function is used to asynchronously begin performing the
+ <a href="https://en.wikipedia.org/wiki/WebSocket#Protocol_handshake">WebSocket handshake</a>,
+ required before messages can be sent and received. During the handshake,
+ the client sends the Websocket Upgrade HTTP request, and the server
+ replies with an HTTP response indicating the result of the handshake.
- This function is used to asynchronously send the HTTP WebSocket
- upgrade request and receive the HTTP WebSocket Upgrade response.
- This function call always returns immediately. The asynchronous
- operation will continue until one of the following conditions is
- true:
+ This call always returns immediately. The asynchronous operation
+ will continue until one of the following conditions is true:
@li The request is sent and the response is received.
- @li An error occurs on the stream
+ @li An error occurs.
- This operation is implemented in terms of one or more calls to the
- next layer's `async_read_some` and `async_write_some` functions, and
- is known as a <em>composed operation</em>. The program must ensure
- that the stream performs no other operations until this operation
- completes.
+ The algorithm, known as a <em>composed asynchronous operation</em>,
+ is implemented in terms of calls to the next layer's `async_read_some`
+ and `async_write_some` functions. No other operation may be performed
+ on the stream until this operation completes.
- The operation is successful if the received HTTP response indicates
- a successful HTTP Upgrade (represented by a Status-Code of 101,
- "switching protocols").
+ The handshake is successful if the received HTTP response
+ indicates the upgrade was accepted by the server, represented by a
+ <a href="https://tools.ietf.org/html/rfc7230#section-3.1.2">status-code</a>
+ of @ref beast::http::status::switching_protocols.
@param res The HTTP Upgrade response returned by the remote
- endpoint. The caller must ensure this object is valid for at
- least until the completion handler is invoked.
-
- @param host The name of the remote host, required by
- the HTTP protocol. Copies may be made as needed.
-
- @param target The Request Target, which may not be empty,
- required by the HTTP protocol. Copies of this parameter may
- be made as needed.
-
- @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
+ endpoint. The caller may use the response to access any
+ additional information sent by the server. This object will
+ be assigned before the completion handler is invoked.
+
+ @param host The name of the remote host. This is required by
+ the HTTP protocol to set the "Host" header field.
+ The implementation will not access the string data after the
+ initiating function returns.
+
+ @param target The request-target, in origin-form. The server may use the
+ target to distinguish different services on the same listening port.
+ The implementation will not access the string data after the
+ initiating function returns.
+
+ @param handler The completion handler to invoke when the operation
+ completes. The implementation takes ownership of the handler by
+ performing a decay-copy. The equivalent function signature of
+ the handler must be:
+ @code
+ void handler(
+ error_code const& ec // Result of operation
+ );
+ @endcode
Regardless of whether the asynchronous operation completes
immediately or not, the handler will not be invoked from within
this function. Invocation of the handler will be performed in a
- manner equivalent to using `boost::asio::io_context::post`.
- */
- template<class HandshakeHandler>
- BOOST_ASIO_INITFN_RESULT_TYPE(
- HandshakeHandler, void(error_code))
- async_handshake(
- response_type& res,
- string_view host,
- string_view target,
- HandshakeHandler&& handler);
+ manner equivalent to using `net::post`.
- /** Start an asynchronous operation to send an upgrade request and receive the response.
-
- This function is used to asynchronously send the HTTP WebSocket
- upgrade request and receive the HTTP WebSocket Upgrade response.
- This function call always returns immediately. The asynchronous
- operation will continue until one of the following conditions is
- true:
-
- @li The request is sent and the response is received.
-
- @li An error occurs on the stream
-
- This operation is implemented in terms of one or more calls to the
- next layer's `async_read_some` and `async_write_some` functions, and
- is known as a <em>composed operation</em>. The program must ensure
- that the stream performs no other operations until this operation
- completes.
-
- The operation is successful if the received HTTP response indicates
- a successful HTTP Upgrade (represented by a Status-Code of 101,
- "switching protocols").
-
- @param host The name of the remote host, required by
- the HTTP protocol. Copies may be made as needed.
-
- @param target The Request Target, which may not be empty,
- required by the HTTP protocol. Copies of this parameter may
- be made as needed.
-
- @param decorator A function object which will be called to modify
- the HTTP request object generated by the implementation. This
- could be used to set the User-Agent field, subprotocols, or other
- application or HTTP specific fields. The object will be called
- with this equivalent signature:
- @code void decorator(
- request_type& req
- ); @endcode
-
- @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
- Regardless of whether the asynchronous operation completes
- immediately or not, the handler will not be invoked from within
- this function. Invocation of the handler will be performed in a
- manner equivalent to using `boost::asio::io_context::post`.
- */
- template<class RequestDecorator, class HandshakeHandler>
- BOOST_ASIO_INITFN_RESULT_TYPE(
- HandshakeHandler, void(error_code))
- async_handshake_ex(
- string_view host,
- string_view target,
- RequestDecorator const& decorator,
- HandshakeHandler&& handler);
-
- /** Start an asynchronous operation to send an upgrade request and receive the response.
-
- This function is used to asynchronously send the HTTP WebSocket
- upgrade request and receive the HTTP WebSocket Upgrade response.
- This function call always returns immediately. The asynchronous
- operation will continue until one of the following conditions is
- true:
-
- @li The request is sent and the response is received.
-
- @li An error occurs on the stream
-
- This operation is implemented in terms of one or more calls to the
- next layer's `async_read_some` and `async_write_some` functions, and
- is known as a <em>composed operation</em>. The program must ensure
- that the stream performs no other operations until this operation
- completes.
+ @par Example
+ @code
+ response_type res;
+ ws.async_handshake(res, "localhost", "/",
+ [&res](error_code ec)
+ {
+ if(ec)
+ std::cerr << "Error: " << ec.message() << "\n";
+ else
+ std::cout << res;
- The operation is successful if the received HTTP response indicates
- a successful HTTP Upgrade (represented by a Status-Code of 101,
- "switching protocols").
+ });
+ @endcode
- @param res The HTTP Upgrade response returned by the remote
- endpoint. The caller must ensure this object is valid for at
- least until the completion handler is invoked.
-
- @param host The name of the remote host, required by
- the HTTP protocol. Copies may be made as needed.
-
- @param target The Request Target, which may not be empty,
- required by the HTTP protocol. Copies of this parameter may
- be made as needed.
-
- @param decorator A function object which will be called to modify
- the HTTP request object generated by the implementation. This
- could be used to set the User-Agent field, subprotocols, or other
- application or HTTP specific fields. The object will be called
- with this equivalent signature:
- @code void decorator(
- request_type& req
- ); @endcode
-
- @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
- Regardless of whether the asynchronous operation completes
- immediately or not, the handler will not be invoked from within
- this function. Invocation of the handler will be performed in a
- manner equivalent to using `boost::asio::io_context::post`.
+ @see
+ @li <a href="https://tools.ietf.org/html/rfc6455#section-4.1">Websocket Opening Handshake Client Requirements (RFC6455)</a>
+ @li <a href="https://tools.ietf.org/html/rfc7230#section-5.4">Host field (RFC7230)</a>
+ @li <a href="https://tools.ietf.org/html/rfc7230#section-3.1.1">request-target (RFC7230)</a>
+ @li <a href="https://tools.ietf.org/html/rfc7230#section-5.3.1">origin-form (RFC7230)</a>
*/
- template<class RequestDecorator, class HandshakeHandler>
- BOOST_ASIO_INITFN_RESULT_TYPE(
- HandshakeHandler, void(error_code))
- async_handshake_ex(
+ template<class HandshakeHandler>
+ BOOST_BEAST_ASYNC_RESULT1(HandshakeHandler)
+ async_handshake(
response_type& res,
string_view host,
string_view target,
- RequestDecorator const& decorator,
HandshakeHandler&& handler);
//--------------------------------------------------------------------------
@@ -1402,198 +971,123 @@ public:
//
//--------------------------------------------------------------------------
- /** Read and respond to a WebSocket HTTP Upgrade request.
+ /** Perform the WebSocket handshake in the server role.
- This function is used to synchronously read an HTTP WebSocket
- Upgrade request and send the HTTP response. The call blocks
- until one of the following conditions is true:
+ This function is used to perform the
+ <a href="https://en.wikipedia.org/wiki/WebSocket#Protocol_handshake">WebSocket handshake</a>,
+ required before messages can be sent and received. During the handshake,
+ the client sends the Websocket Upgrade HTTP request, and the server
+ replies with an HTTP response indicating the result of the handshake.
- @li The request is received and the response finishes sending.
+ The call blocks until one of the following conditions is true:
+
+ @li The request is received and the response is sent.
- @li An error occurs on the stream.
+ @li An error occurs.
- This function is implemented in terms of one or more calls to
- the next layer's `read_some` and `write_some` functions.
+ The algorithm, known as a <em>composed operation</em>, is implemented
+ in terms of calls to the next layer's `read_some` and `write_some`
+ functions.
- If the stream receives a valid HTTP WebSocket Upgrade request,
- an HTTP response is sent back indicating a successful upgrade.
- When this call returns, the stream is then ready to send and
- receive WebSocket protocol frames and messages.
- If the HTTP Upgrade request is invalid or cannot be satisfied,
- an HTTP response is sent indicating the reason and status code
- (typically 400, "Bad Request"). This counts as a failure.
+ If a valid upgrade request is received, an HTTP response with a
+ <a href="https://tools.ietf.org/html/rfc7230#section-3.1.2">status-code</a>
+ of @ref beast::http::status::switching_protocols is sent to
+ the peer, otherwise a non-successful error is associated with
+ the operation.
- The implementation uses fixed size internal storage to
- receive the request. If the request is too large, the error
- @ref error::buffer_overflow will be indicated. Applications
- that wish to receive larger requests should first read the
- request using their own buffer and a suitable overload of
- @ref http::read or @ref http::async_read, then call @ref accept
- or @ref async_accept with the request.
+ If the request size exceeds the capacity of the stream's
+ internal buffer, the error @ref error::buffer_overflow will be
+ indicated. To handle larger requests, an application should
+ read the HTTP request directly using @ref http::read and then
+ pass the request to the appropriate overload of @ref accept or
+ @ref async_accept
@throws system_error Thrown on failure.
+
+ @see
+ @li <a href="https://tools.ietf.org/html/rfc6455#section-4.2">Websocket Opening Handshake Server Requirements (RFC6455)</a>
*/
void
accept();
/** Read and respond to a WebSocket HTTP Upgrade request.
- This function is used to synchronously read an HTTP WebSocket
- Upgrade request and send the HTTP response. The call blocks
- until one of the following conditions is true:
-
- @li The request is received and the response finishes sending.
-
- @li An error occurs on the stream.
-
- This function is implemented in terms of one or more calls to
- the next layer's `read_some` and `write_some` functions.
-
- If the stream receives a valid HTTP WebSocket Upgrade request,
- an HTTP response is sent back indicating a successful upgrade.
- When this call returns, the stream is then ready to send and
- receive WebSocket protocol frames and messages.
- If the HTTP Upgrade request is invalid or cannot be satisfied,
- an HTTP response is sent indicating the reason and status code
- (typically 400, "Bad Request"). This counts as a failure.
-
- The implementation uses fixed size internal storage to
- receive the request. If the request is too large, the error
- @ref error::buffer_overflow will be indicated. Applications
- that wish to receive larger requests should first read the
- request using their own buffer and a suitable overload of
- @ref http::read or @ref http::async_read, then call @ref accept
- or @ref async_accept with the request.
-
- @param decorator A function object which will be called to modify
- the HTTP response object delivered by the implementation. This
- could be used to set the Server field, subprotocols, or other
- application or HTTP specific fields. The object will be called
- with this equivalent signature:
- @code void decorator(
- response_type& res
- ); @endcode
-
- @throws system_error Thrown on failure.
- */
- template<class ResponseDecorator>
- void
- accept_ex(ResponseDecorator const& decorator);
+ This function is used to perform the
+ <a href="https://en.wikipedia.org/wiki/WebSocket#Protocol_handshake">WebSocket handshake</a>,
+ required before messages can be sent and received. During the handshake,
+ the client sends the Websocket Upgrade HTTP request, and the server
+ replies with an HTTP response indicating the result of the handshake.
- /** Read and respond to a WebSocket HTTP Upgrade request.
-
- This function is used to synchronously read an HTTP WebSocket
- Upgrade request and send the HTTP response. The call blocks
- until one of the following conditions is true:
+ The call blocks until one of the following conditions is true:
- @li The request is received and the response finishes sending.
+ @li The request is received and the response is sent.
- @li An error occurs on the stream.
+ @li An error occurs.
- This function is implemented in terms of one or more calls to
- the next layer's `read_some` and `write_some` functions.
+ The algorithm, known as a <em>composed operation</em>, is implemented
+ in terms of calls to the next layer's `read_some` and `write_some`
+ functions.
- If the stream receives a valid HTTP WebSocket Upgrade request,
- an HTTP response is sent back indicating a successful upgrade.
- When this call returns, the stream is then ready to send and
- receive WebSocket protocol frames and messages.
- If the HTTP Upgrade request is invalid or cannot be satisfied,
- an HTTP response is sent indicating the reason and status code
- (typically 400, "Bad Request"). This counts as a failure.
+ If a valid upgrade request is received, an HTTP response with a
+ <a href="https://tools.ietf.org/html/rfc7230#section-3.1.2">status-code</a>
+ of @ref beast::http::status::switching_protocols is sent to
+ the peer, otherwise a non-successful error is associated with
+ the operation.
- The implementation uses fixed size internal storage to
- receive the request. If the request is too large, the error
- @ref error::buffer_overflow will be indicated. Applications
- that wish to receive larger requests should first read the
- request using their own buffer and a suitable overload of
- @ref http::read or @ref http::async_read, then call @ref accept
- or @ref async_accept with the request.
+ If the request size exceeds the capacity of the stream's
+ internal buffer, the error @ref error::buffer_overflow will be
+ indicated. To handle larger requests, an application should
+ read the HTTP request directly using @ref http::read and then
+ pass the request to the appropriate overload of @ref accept or
+ @ref async_accept
@param ec Set to indicate what error occurred, if any.
+
+ @see
+ @li <a href="https://tools.ietf.org/html/rfc6455#section-4.2">Websocket Opening Handshake Server Requirements (RFC6455)</a>
*/
void
accept(error_code& ec);
/** Read and respond to a WebSocket HTTP Upgrade request.
- This function is used to synchronously read an HTTP WebSocket
- Upgrade request and send the HTTP response. The call blocks
- until one of the following conditions is true:
-
- @li The request is received and the response finishes sending.
-
- @li An error occurs on the stream.
-
- This function is implemented in terms of one or more calls to
- the next layer's `read_some` and `write_some` functions.
-
- If the stream receives a valid HTTP WebSocket Upgrade request,
- an HTTP response is sent back indicating a successful upgrade.
- When this call returns, the stream is then ready to send and
- receive WebSocket protocol frames and messages.
- If the HTTP Upgrade request is invalid or cannot be satisfied,
- an HTTP response is sent indicating the reason and status code
- (typically 400, "Bad Request"). This counts as a failure.
-
- The implementation uses fixed size internal storage to
- receive the request. If the request is too large, the error
- @ref error::buffer_overflow will be indicated. Applications
- that wish to receive larger requests should first read the
- request using their own buffer and a suitable overload of
- @ref http::read or @ref http::async_read, then call @ref accept
- or @ref async_accept with the request.
-
- @param decorator A function object which will be called to modify
- the HTTP response object delivered by the implementation. This
- could be used to set the Server field, subprotocols, or other
- application or HTTP specific fields. The object will be called
- with this equivalent signature:
- @code void decorator(
- response_type& res
- ); @endcode
+ This function is used to perform the
+ <a href="https://en.wikipedia.org/wiki/WebSocket#Protocol_handshake">WebSocket handshake</a>,
+ required before messages can be sent and received. During the handshake,
+ the client sends the Websocket Upgrade HTTP request, and the server
+ replies with an HTTP response indicating the result of the handshake.
- @param ec Set to indicate what error occurred, if any.
- */
- template<class ResponseDecorator>
- void
- accept_ex(
- ResponseDecorator const& decorator,
- error_code& ec);
-
- /** Read and respond to a WebSocket HTTP Upgrade request.
-
- This function is used to synchronously read an HTTP WebSocket
- Upgrade request and send the HTTP response. The call blocks
- until one of the following conditions is true:
+ The call blocks until one of the following conditions is true:
- @li The request is received and the response finishes sending.
+ @li The request is received and the response is sent.
- @li An error occurs on the stream.
+ @li An error occurs.
- This function is implemented in terms of one or more calls to
- the next layer's `read_some` and `write_some` functions.
+ The algorithm, known as a <em>composed operation</em>, is implemented
+ in terms of calls to the next layer's `read_some` and `write_some`
+ functions.
- If the stream receives a valid HTTP WebSocket Upgrade request,
- an HTTP response is sent back indicating a successful upgrade.
- When this call returns, the stream is then ready to send and
- receive WebSocket protocol frames and messages.
- If the HTTP Upgrade request is invalid or cannot be satisfied,
- an HTTP response is sent indicating the reason and status code
- (typically 400, "Bad Request"). This counts as a failure.
+ If a valid upgrade request is received, an HTTP response with a
+ <a href="https://tools.ietf.org/html/rfc7230#section-3.1.2">status-code</a>
+ of @ref beast::http::status::switching_protocols is sent to
+ the peer, otherwise a non-successful error is associated with
+ the operation.
- The implementation uses fixed size internal storage to
- receive the request. If the request is too large, the error
- @ref error::buffer_overflow will be indicated. Applications
- that wish to receive larger requests should first read the
- request using their own buffer and a suitable overload of
- @ref http::read or @ref http::async_read, then call @ref accept
- or @ref async_accept with the request.
+ If the request size exceeds the capacity of the stream's
+ internal buffer, the error @ref error::buffer_overflow will be
+ indicated. To handle larger requests, an application should
+ read the HTTP request directly using @ref http::read and then
+ pass the request to the appropriate overload of @ref accept or
+ @ref async_accept
@param buffers Caller provided data that has already been
received on the stream. The implementation will copy the
caller provided data before the function returns.
@throws system_error Thrown on failure.
+
+ @see
+ @li <a href="https://tools.ietf.org/html/rfc6455#section-4.2">Websocket Opening Handshake Server Requirements (RFC6455)</a>
*/
template<class ConstBufferSequence>
#if BOOST_BEAST_DOXYGEN
@@ -1606,94 +1100,43 @@ public:
/** Read and respond to a WebSocket HTTP Upgrade request.
- This function is used to synchronously read an HTTP WebSocket
- Upgrade request and send the HTTP response. The call blocks
- until one of the following conditions is true:
+ This function is used to perform the
+ <a href="https://en.wikipedia.org/wiki/WebSocket#Protocol_handshake">WebSocket handshake</a>,
+ required before messages can be sent and received. During the handshake,
+ the client sends the Websocket Upgrade HTTP request, and the server
+ replies with an HTTP response indicating the result of the handshake.
- @li The request is received and the response finishes sending.
-
- @li An error occurs on the stream.
-
- This function is implemented in terms of one or more calls to
- the next layer's `read_some` and `write_some` functions.
-
- If the stream receives a valid HTTP WebSocket Upgrade request,
- an HTTP response is sent back indicating a successful upgrade.
- When this call returns, the stream is then ready to send and
- receive WebSocket protocol frames and messages.
- If the HTTP Upgrade request is invalid or cannot be satisfied,
- an HTTP response is sent indicating the reason and status code
- (typically 400, "Bad Request"). This counts as a failure.
-
- The implementation uses fixed size internal storage to
- receive the request. If the request is too large, the error
- @ref error::buffer_overflow will be indicated. Applications
- that wish to receive larger requests should first read the
- request using their own buffer and a suitable overload of
- @ref http::read or @ref http::async_read, then call @ref accept
- or @ref async_accept with the request.
-
- @param buffers Caller provided data that has already been
- received on the stream. The implementation will copy the
- caller provided data before the function returns.
-
- @param decorator A function object which will be called to modify
- the HTTP response object delivered by the implementation. This
- could be used to set the Server field, subprotocols, or other
- application or HTTP specific fields. The object will be called
- with this equivalent signature:
- @code void decorator(
- response_type& res
- ); @endcode
-
- @throws system_error Thrown on failure.
- */
- template<class ConstBufferSequence,
- class ResponseDecorator>
-#if BOOST_BEAST_DOXYGEN
- void
-#else
- typename std::enable_if<! http::detail::is_header<
- ConstBufferSequence>::value>::type
-#endif
- accept_ex(
- ConstBufferSequence const& buffers,
- ResponseDecorator const& decorator);
-
- /** Read and respond to a WebSocket HTTP Upgrade request.
-
- This function is used to synchronously read an HTTP WebSocket
- Upgrade request and send the HTTP response. The call blocks
- until one of the following conditions is true:
+ The call blocks until one of the following conditions is true:
- @li The request is received and the response finishes sending.
+ @li The request is received and the response is sent.
- @li An error occurs on the stream.
+ @li An error occurs.
- This function is implemented in terms of one or more calls to
- the next layer's `read_some` and `write_some` functions.
+ The algorithm, known as a <em>composed operation</em>, is implemented
+ in terms of calls to the next layer's `read_some` and `write_some`
+ functions.
- If the stream receives a valid HTTP WebSocket Upgrade request,
- an HTTP response is sent back indicating a successful upgrade.
- When this call returns, the stream is then ready to send and
- receive WebSocket protocol frames and messages.
- If the HTTP Upgrade request is invalid or cannot be satisfied,
- an HTTP response is sent indicating the reason and status code
- (typically 400, "Bad Request"). This counts as a failure.
+ If a valid upgrade request is received, an HTTP response with a
+ <a href="https://tools.ietf.org/html/rfc7230#section-3.1.2">status-code</a>
+ of @ref beast::http::status::switching_protocols is sent to
+ the peer, otherwise a non-successful error is associated with
+ the operation.
- The implementation uses fixed size internal storage to
- receive the request. If the request is too large, the error
- @ref error::buffer_overflow will be indicated. Applications
- that wish to receive larger requests should first read the
- request using their own buffer and a suitable overload of
- @ref http::read or @ref http::async_read, then call @ref accept
- or @ref async_accept with the request.
+ If the request size exceeds the capacity of the stream's
+ internal buffer, the error @ref error::buffer_overflow will be
+ indicated. To handle larger requests, an application should
+ read the HTTP request directly using @ref http::read and then
+ pass the request to the appropriate overload of @ref accept or
+ @ref async_accept
@param buffers Caller provided data that has already been
received on the stream. The implementation will copy the
caller provided data before the function returns.
@param ec Set to indicate what error occurred, if any.
+
+ @see
+ @li <a href="https://tools.ietf.org/html/rfc6455#section-4.2">Websocket Opening Handshake Server Requirements (RFC6455)</a>
*/
template<class ConstBufferSequence>
#if BOOST_BEAST_DOXYGEN
@@ -1706,88 +1149,38 @@ public:
ConstBufferSequence const& buffers,
error_code& ec);
- /** Read and respond to a WebSocket HTTP Upgrade request.
-
- This function is used to synchronously read an HTTP WebSocket
- Upgrade request and send the HTTP response. The call blocks
- until one of the following conditions is true:
-
- @li The request is received and the response finishes sending.
-
- @li An error occurs on the stream.
-
- This function is implemented in terms of one or more calls to
- the next layer's `read_some` and `write_some` functions.
-
- If the stream receives a valid HTTP WebSocket Upgrade request,
- an HTTP response is sent back indicating a successful upgrade.
- When this call returns, the stream is then ready to send and
- receive WebSocket protocol frames and messages.
- If the HTTP Upgrade request is invalid or cannot be satisfied,
- an HTTP response is sent indicating the reason and status code
- (typically 400, "Bad Request"). This counts as a failure.
-
- The implementation uses fixed size internal storage to
- receive the request. If the request is too large, the error
- @ref error::buffer_overflow will be indicated. Applications
- that wish to receive larger requests should first read the
- request using their own buffer and a suitable overload of
- @ref http::read or @ref http::async_read, then call @ref accept
- or @ref async_accept with the request.
-
- @param buffers Caller provided data that has already been
- received on the stream. The implementation will copy the
- caller provided data before the function returns.
-
- @param decorator A function object which will be called to modify
- the HTTP response object delivered by the implementation. This
- could be used to set the Server field, subprotocols, or other
- application or HTTP specific fields. The object will be called
- with this equivalent signature:
- @code void decorator(
- response_type& res
- ); @endcode
-
- @param ec Set to indicate what error occurred, if any.
- */
- template<class ConstBufferSequence, class ResponseDecorator>
-#if BOOST_BEAST_DOXYGEN
- void
-#else
- typename std::enable_if<! http::detail::is_header<
- ConstBufferSequence>::value>::type
-#endif
- accept_ex(
- ConstBufferSequence const& buffers,
- ResponseDecorator const& decorator,
- error_code& ec);
-
/** Respond to a WebSocket HTTP Upgrade request
- This function is used to synchronously send the HTTP response
- to an HTTP request possibly containing a WebSocket Upgrade.
+ This function is used to perform the
+ <a href="https://en.wikipedia.org/wiki/WebSocket#Protocol_handshake">WebSocket handshake</a>,
+ required before messages can be sent and received. During the handshake,
+ the client sends the Websocket Upgrade HTTP request, and the server
+ replies with an HTTP response indicating the result of the handshake.
+
The call blocks until one of the following conditions is true:
- @li The response finishes sending.
+ @li The response is sent.
- @li An error occurs on the stream.
+ @li An error occurs.
- This function is implemented in terms of one or more calls to
- the next layer's `read_some` and `write_some` functions.
+ The algorithm, known as a <em>composed operation</em>, is implemented
+ in terms of calls to the next layer's `read_some` and `write_some`
+ functions.
- If the stream receives a valid HTTP WebSocket Upgrade request,
- an HTTP response is sent back indicating a successful upgrade.
- When this call returns, the stream is then ready to send and
- receive WebSocket protocol frames and messages.
- If the HTTP Upgrade request is invalid or cannot be satisfied,
- an HTTP response is sent indicating the reason and status code
- (typically 400, "Bad Request"). This counts as a failure.
+ If a valid upgrade request is received, an HTTP response with a
+ <a href="https://tools.ietf.org/html/rfc7230#section-3.1.2">status-code</a>
+ of @ref beast::http::status::switching_protocols is sent to
+ the peer, otherwise a non-successful error is associated with
+ the operation.
@param req An object containing the HTTP Upgrade request.
Ownership is not transferred, the implementation will not
access this object from other threads.
@throws system_error Thrown on failure.
+
+ @see
+ @li <a href="https://tools.ietf.org/html/rfc6455#section-4.2">Websocket Opening Handshake Server Requirements (RFC6455)</a>
*/
template<class Body, class Allocator>
void
@@ -1796,73 +1189,36 @@ public:
/** Respond to a WebSocket HTTP Upgrade request
- This function is used to synchronously send the HTTP response
- to an HTTP request possibly containing a WebSocket Upgrade.
- The call blocks until one of the following conditions is true:
-
- @li The response finishes sending.
-
- @li An error occurs on the stream.
-
- This function is implemented in terms of one or more calls to
- the next layer's `read_some` and `write_some` functions.
-
- If the stream receives a valid HTTP WebSocket Upgrade request,
- an HTTP response is sent back indicating a successful upgrade.
- When this call returns, the stream is then ready to send and
- receive WebSocket protocol frames and messages.
- If the HTTP Upgrade request is invalid or cannot be satisfied,
- an HTTP response is sent indicating the reason and status code
- (typically 400, "Bad Request"). This counts as a failure.
-
- @param req An object containing the HTTP Upgrade request.
- Ownership is not transferred, the implementation will not
- access this object from other threads.
-
- @param decorator A function object which will be called to modify
- the HTTP response object delivered by the implementation. This
- could be used to set the Server field, subprotocols, or other
- application or HTTP specific fields. The object will be called
- with this equivalent signature:
- @code void decorator(
- response_type& res
- ); @endcode
-
- @throws system_error Thrown on failure.
- */
- template<class Body, class Allocator,
- class ResponseDecorator>
- void
- accept_ex(http::request<Body,
- http::basic_fields<Allocator>> const& req,
- ResponseDecorator const& decorator);
-
- /** Respond to a WebSocket HTTP Upgrade request
+ This function is used to perform the
+ <a href="https://en.wikipedia.org/wiki/WebSocket#Protocol_handshake">WebSocket handshake</a>,
+ required before messages can be sent and received. During the handshake,
+ the client sends the Websocket Upgrade HTTP request, and the server
+ replies with an HTTP response indicating the result of the handshake.
- This function is used to synchronously send the HTTP response
- to an HTTP request possibly containing a WebSocket Upgrade.
The call blocks until one of the following conditions is true:
- @li The response finishes sending.
+ @li The response is sent.
- @li An error occurs on the stream.
+ @li An error occurs.
- This function is implemented in terms of one or more calls to
- the next layer's `read_some` and `write_some` functions.
+ The algorithm, known as a <em>composed operation</em>, is implemented
+ in terms of calls to the next layer's `read_some` and `write_some`
+ functions.
- If the stream receives a valid HTTP WebSocket Upgrade request,
- an HTTP response is sent back indicating a successful upgrade.
- When this call returns, the stream is then ready to send and
- receive WebSocket protocol frames and messages.
- If the HTTP Upgrade request is invalid or cannot be satisfied,
- an HTTP response is sent indicating the reason and status code
- (typically 400, "Bad Request"). This counts as a failure.
+ If a valid upgrade request is received, an HTTP response with a
+ <a href="https://tools.ietf.org/html/rfc7230#section-3.1.2">status-code</a>
+ of @ref beast::http::status::switching_protocols is sent to
+ the peer, otherwise a non-successful error is associated with
+ the operation.
@param req An object containing the HTTP Upgrade request.
Ownership is not transferred, the implementation will not
access this object from other threads.
@param ec Set to indicate what error occurred, if any.
+
+ @see
+ @li <a href="https://tools.ietf.org/html/rfc6455#section-4.2">Websocket Opening Handshake Server Requirements (RFC6455)</a>
*/
template<class Body, class Allocator>
void
@@ -1870,271 +1226,92 @@ public:
http::basic_fields<Allocator>> const& req,
error_code& ec);
- /** Respond to a WebSocket HTTP Upgrade request
+ /** Perform the WebSocket handshake asynchronously in the server role.
- This function is used to synchronously send the HTTP response
- to an HTTP request possibly containing a WebSocket Upgrade.
- The call blocks until one of the following conditions is true:
-
- @li The response finishes sending.
-
- @li An error occurs on the stream.
+ This initiating function is used to asynchronously begin performing the
+ <a href="https://en.wikipedia.org/wiki/WebSocket#Protocol_handshake">WebSocket handshake</a>,
+ required before messages can be sent and received. During the handshake,
+ the client sends the Websocket Upgrade HTTP request, and the server
+ replies with an HTTP response indicating the result of the handshake.
- This function is implemented in terms of one or more calls to
- the next layer's `read_some` and `write_some` functions.
+ This call always returns immediately. The asynchronous operation
+ will continue until one of the following conditions is true:
- If the stream receives a valid HTTP WebSocket Upgrade request,
- an HTTP response is sent back indicating a successful upgrade.
- When this call returns, the stream is then ready to send and
- receive WebSocket protocol frames and messages.
- If the HTTP Upgrade request is invalid or cannot be satisfied,
- an HTTP response is sent indicating the reason and status code
- (typically 400, "Bad Request"). This counts as a failure.
+ @li The request is received and the response is sent.
- @param req An object containing the HTTP Upgrade request.
- Ownership is not transferred, the implementation will not
- access this object from other threads.
-
- @param decorator A function object which will be called to modify
- the HTTP response object delivered by the implementation. This
- could be used to set the Server field, subprotocols, or other
- application or HTTP specific fields. The object will be called
- with this equivalent signature:
- @code void decorator(
- response_type& res
- ); @endcode
-
- @param ec Set to indicate what error occurred, if any.
- */
- template<class Body, class Allocator,
- class ResponseDecorator>
- void
- accept_ex(http::request<Body,
- http::basic_fields<Allocator>> const& req,
- ResponseDecorator const& decorator,
- error_code& ec);
+ @li An error occurs.
- /** Start reading and responding to a WebSocket HTTP Upgrade request.
-
- This function is used to asynchronously read an HTTP WebSocket
- Upgrade request and send the HTTP response. The function call
- always returns immediately. The asynchronous operation will
- continue until one of the following conditions is true:
-
- @li The request is received and the response finishes sending.
-
- @li An error occurs on the stream.
-
- This operation is implemented in terms of one or more calls to
- the next layer's `async_read_some` and `async_write_some`
- functions, and is known as a <em>composed operation</em>. The
- program must ensure that the stream performs no other
- asynchronous operations until this operation completes.
-
- If the stream receives a valid HTTP WebSocket Upgrade request,
- an HTTP response is sent back indicating a successful upgrade.
- When the completion handler is invoked, the stream is then
- ready to send and receive WebSocket protocol frames and
- messages.
- If the HTTP Upgrade request is invalid or cannot be satisfied,
- an HTTP response is sent indicating the reason and status code
- (typically 400, "Bad Request"). This counts as a failure, and
- the completion handler will be invoked with a suitable error
- code set.
-
- The implementation uses fixed size internal storage to
- receive the request. If the request is too large, the error
- @ref error::buffer_overflow will be indicated. Applications
- that wish to receive larger requests should first read the
- request using their own buffer and a suitable overload of
- @ref http::read or @ref http::async_read, then call @ref accept
- or @ref async_accept with the request.
-
- @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(
+ The algorithm, known as a <em>composed asynchronous operation</em>,
+ is implemented in terms of calls to the next layer's `async_read_some`
+ and `async_write_some` functions. No other operation may be performed
+ on the stream until this operation completes.
+
+ If a valid upgrade request is received, an HTTP response with a
+ <a href="https://tools.ietf.org/html/rfc7230#section-3.1.2">status-code</a>
+ of @ref beast::http::status::switching_protocols is sent to
+ the peer, otherwise a non-successful error is associated with
+ the operation.
+
+ If the request size exceeds the capacity of the stream's
+ internal buffer, the error @ref error::buffer_overflow will be
+ indicated. To handle larger requests, an application should
+ read the HTTP request directly using @ref http::async_read and then
+ pass the request to the appropriate overload of @ref accept or
+ @ref async_accept
+
+ @param handler The completion handler to invoke when the operation
+ completes. The implementation takes ownership of the handler by
+ performing a decay-copy. The equivalent function signature of
+ the handler must be:
+ @code
+ void handler(
error_code const& ec // Result of operation
- ); @endcode
+ );
+ @endcode
Regardless of whether the asynchronous operation completes
immediately or not, the handler will not be invoked from within
this function. Invocation of the handler will be performed in a
- manner equivalent to using `boost::asio::io_context::post`.
+ manner equivalent to using `net::post`.
+
+ @see
+ @li <a href="https://tools.ietf.org/html/rfc6455#section-4.2">Websocket Opening Handshake Server Requirements (RFC6455)</a>
*/
template<class AcceptHandler>
- BOOST_ASIO_INITFN_RESULT_TYPE(
- AcceptHandler, void(error_code))
+ BOOST_BEAST_ASYNC_RESULT1(AcceptHandler)
async_accept(AcceptHandler&& handler);
- /** Start reading and responding to a WebSocket HTTP Upgrade request.
-
- This function is used to asynchronously read an HTTP WebSocket
- Upgrade request and send the HTTP response. The function call
- always returns immediately. The asynchronous operation will
- continue until one of the following conditions is true:
-
- @li The request is received and the response finishes sending.
-
- @li An error occurs on the stream.
-
- This operation is implemented in terms of one or more calls to
- the next layer's `async_read_some` and `async_write_some`
- functions, and is known as a <em>composed operation</em>. The
- program must ensure that the stream performs no other
- asynchronous operations until this operation completes.
-
- If the stream receives a valid HTTP WebSocket Upgrade request,
- an HTTP response is sent back indicating a successful upgrade.
- When the completion handler is invoked, the stream is then
- ready to send and receive WebSocket protocol frames and
- messages.
- If the HTTP Upgrade request is invalid or cannot be satisfied,
- an HTTP response is sent indicating the reason and status code
- (typically 400, "Bad Request"). This counts as a failure, and
- the completion handler will be invoked with a suitable error
- code set.
-
- The implementation uses fixed size internal storage to
- receive the request. If the request is too large, the error
- @ref error::buffer_overflow will be indicated. Applications
- that wish to receive larger requests should first read the
- request using their own buffer and a suitable overload of
- @ref http::read or @ref http::async_read, then call @ref accept
- or @ref async_accept with the request.
-
- @param decorator A function object which will be called to modify
- the HTTP response object delivered by the implementation. This
- could be used to set the Server field, subprotocols, or other
- application or HTTP specific fields. The object will be called
- with this equivalent signature:
- @code void decorator(
- response_type& res
- ); @endcode
-
- @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
- Regardless of whether the asynchronous operation completes
- immediately or not, the handler will not be invoked from within
- this function. Invocation of the handler will be performed in a
- manner equivalent to using `boost::asio::io_context::post`.
- */
- template<
- class ResponseDecorator,
- class AcceptHandler>
- BOOST_ASIO_INITFN_RESULT_TYPE(
- AcceptHandler, void(error_code))
- async_accept_ex(
- ResponseDecorator const& decorator,
- AcceptHandler&& handler);
+ /** Perform the WebSocket handshake asynchronously in the server role.
- /** Start reading and responding to a WebSocket HTTP Upgrade request.
-
- This function is used to asynchronously read an HTTP WebSocket
- Upgrade request and send the HTTP response. The function call
- always returns immediately. The asynchronous operation will
- continue until one of the following conditions is true:
-
- @li The request is received and the response finishes sending.
-
- @li An error occurs on the stream.
-
- This operation is implemented in terms of one or more calls to
- the next layer's `async_read_some` and `async_write_some`
- functions, and is known as a <em>composed operation</em>. The
- program must ensure that the stream performs no other
- asynchronous operations until this operation completes.
-
- If the stream receives a valid HTTP WebSocket Upgrade request,
- an HTTP response is sent back indicating a successful upgrade.
- When the completion handler is invoked, the stream is then
- ready to send and receive WebSocket protocol frames and
- messages.
- If the HTTP Upgrade request is invalid or cannot be satisfied,
- an HTTP response is sent indicating the reason and status code
- (typically 400, "Bad Request"). This counts as a failure, and
- the completion handler will be invoked with a suitable error
- code set.
-
- The implementation uses fixed size internal storage to
- receive the request. If the request is too large, the error
- @ref error::buffer_overflow will be indicated. Applications
- that wish to receive larger requests should first read the
- request using their own buffer and a suitable overload of
- @ref http::read or @ref http::async_read, then call @ref accept
- or @ref async_accept with the request.
+ This initiating function is used to asynchronously begin performing the
+ <a href="https://en.wikipedia.org/wiki/WebSocket#Protocol_handshake">WebSocket handshake</a>,
+ required before messages can be sent and received. During the handshake,
+ the client sends the Websocket Upgrade HTTP request, and the server
+ replies with an HTTP response indicating the result of the handshake.
- @param buffers Caller provided data that has already been
- received on the stream. This may be used for implementations
- allowing multiple protocols on the same stream. The
- buffered data will first be applied to the handshake, and
- then to received WebSocket frames. The implementation will
- copy the caller provided data before the function returns.
+ This call always returns immediately. The asynchronous operation
+ will continue until one of the following conditions is true:
- @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
- Regardless of whether the asynchronous operation completes
- immediately or not, the handler will not be invoked from within
- this function. Invocation of the handler will be performed in a
- manner equivalent to using `boost::asio::io_context::post`.
- */
- template<
- class ConstBufferSequence,
- class AcceptHandler>
-#if BOOST_BEAST_DOXYGEN
- void_or_deduced
-#else
- typename std::enable_if<
- ! http::detail::is_header<ConstBufferSequence>::value,
- BOOST_ASIO_INITFN_RESULT_TYPE(
- AcceptHandler, void(error_code))>::type
-#endif
- async_accept(
- ConstBufferSequence const& buffers,
- AcceptHandler&& handler);
+ @li The request is received and the response is sent.
- /** Start reading and responding to a WebSocket HTTP Upgrade request.
-
- This function is used to asynchronously read an HTTP WebSocket
- Upgrade request and send the HTTP response. The function call
- always returns immediately. The asynchronous operation will
- continue until one of the following conditions is true:
-
- @li The request is received and the response finishes sending.
-
- @li An error occurs on the stream.
-
- This operation is implemented in terms of one or more calls to
- the next layer's `async_read_some` and `async_write_some`
- functions, and is known as a <em>composed operation</em>. The
- program must ensure that the stream performs no other
- asynchronous operations until this operation completes.
-
- If the stream receives a valid HTTP WebSocket Upgrade request,
- an HTTP response is sent back indicating a successful upgrade.
- When the completion handler is invoked, the stream is then
- ready to send and receive WebSocket protocol frames and
- messages.
- If the HTTP Upgrade request is invalid or cannot be satisfied,
- an HTTP response is sent indicating the reason and status code
- (typically 400, "Bad Request"). This counts as a failure, and
- the completion handler will be invoked with a suitable error
- code set.
-
- The implementation uses fixed size internal storage to
- receive the request. If the request is too large, the error
- @ref error::buffer_overflow will be indicated. Applications
- that wish to receive larger requests should first read the
- request using their own buffer and a suitable overload of
- @ref http::read or @ref http::async_read, then call @ref accept
- or @ref async_accept with the request.
+ @li An error occurs.
+
+ The algorithm, known as a <em>composed asynchronous operation</em>,
+ is implemented in terms of calls to the next layer's `async_read_some`
+ and `async_write_some` functions. No other operation may be performed
+ on the stream until this operation completes.
+
+ If a valid upgrade request is received, an HTTP response with a
+ <a href="https://tools.ietf.org/html/rfc7230#section-3.1.2">status-code</a>
+ of @ref beast::http::status::switching_protocols is sent to
+ the peer, otherwise a non-successful error is associated with
+ the operation.
+
+ If the request size exceeds the capacity of the stream's
+ internal buffer, the error @ref error::buffer_overflow will be
+ indicated. To handle larger requests, an application should
+ read the HTTP request directly using @ref http::async_read and then
+ pass the request to the appropriate overload of @ref accept or
+ @ref async_accept
@param buffers Caller provided data that has already been
received on the stream. This may be used for implementations
@@ -2143,259 +1320,203 @@ public:
then to received WebSocket frames. The implementation will
copy the caller provided data before the function returns.
- @param decorator A function object which will be called to modify
- the HTTP response object delivered by the implementation. This
- could be used to set the Server field, subprotocols, or other
- application or HTTP specific fields. The object will be called
- with this equivalent signature:
- @code void decorator(
- response_type& res
- ); @endcode
-
- @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(
+ @param handler The completion handler to invoke when the operation
+ completes. The implementation takes ownership of the handler by
+ performing a decay-copy. The equivalent function signature of
+ the handler must be:
+ @code
+ void handler(
error_code const& ec // Result of operation
- ); @endcode
+ );
+ @endcode
Regardless of whether the asynchronous operation completes
immediately or not, the handler will not be invoked from within
this function. Invocation of the handler will be performed in a
- manner equivalent to using `boost::asio::io_context::post`.
+ manner equivalent to using `net::post`.
+
+ @see
+ @li <a href="https://tools.ietf.org/html/rfc6455#section-4.2">Websocket Opening Handshake Server Requirements (RFC6455)</a>
*/
template<
class ConstBufferSequence,
- class ResponseDecorator,
class AcceptHandler>
#if BOOST_BEAST_DOXYGEN
void_or_deduced
#else
typename std::enable_if<
! http::detail::is_header<ConstBufferSequence>::value,
- BOOST_ASIO_INITFN_RESULT_TYPE(
- AcceptHandler, void(error_code))>::type
+ BOOST_BEAST_ASYNC_RESULT1(AcceptHandler)>::type
#endif
- async_accept_ex(
+ async_accept(
ConstBufferSequence const& buffers,
- ResponseDecorator const& decorator,
AcceptHandler&& handler);
- /** Start responding to a WebSocket HTTP Upgrade request.
-
- This function is used to asynchronously send the HTTP response
- to an HTTP request possibly containing a WebSocket Upgrade
- request. The function call always returns immediately. The
- asynchronous operation will continue until one of the following
- conditions is true:
-
- @li The response finishes sending.
-
- @li An error occurs on the stream.
-
- This operation is implemented in terms of one or more calls 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 operations until this operation
- completes.
-
- If the stream receives a valid HTTP WebSocket Upgrade request,
- an HTTP response is sent back indicating a successful upgrade.
- When the completion handler is invoked, the stream is then
- ready to send and receive WebSocket protocol frames and
- messages.
- If the HTTP Upgrade request is invalid or cannot be satisfied,
- an HTTP response is sent indicating the reason and status code
- (typically 400, "Bad Request"). This counts as a failure, and
- the completion handler will be invoked with a suitable error
- code set.
-
- @param req An object containing the HTTP Upgrade request.
- Ownership is not transferred, the implementation will not access
- this object from other threads.
-
- @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
- Regardless of whether the asynchronous operation completes
- immediately or not, the handler will not be invoked from within
- this function. Invocation of the handler will be performed in a
- manner equivalent to using `boost::asio::io_context::post`.
- */
- template<
- class Body, class Allocator,
- class AcceptHandler>
- BOOST_ASIO_INITFN_RESULT_TYPE(
- AcceptHandler, void(error_code))
- async_accept(
- http::request<Body,
- http::basic_fields<Allocator>> const& req,
- AcceptHandler&& handler);
+ /** Perform the WebSocket handshake asynchronously in the server role.
- /** Start responding to a WebSocket HTTP Upgrade request.
+ This initiating function is used to asynchronously begin performing the
+ <a href="https://en.wikipedia.org/wiki/WebSocket#Protocol_handshake">WebSocket handshake</a>,
+ required before messages can be sent and received. During the handshake,
+ the client sends the Websocket Upgrade HTTP request, and the server
+ replies with an HTTP response indicating the result of the handshake.
- This function is used to asynchronously send the HTTP response
- to an HTTP request possibly containing a WebSocket Upgrade
- request. The function call always returns immediately. The
- asynchronous operation will continue until one of the following
- conditions is true:
+ This call always returns immediately. The asynchronous operation
+ will continue until one of the following conditions is true:
- @li The response finishes sending.
+ @li The request is received and the response is sent.
- @li An error occurs on the stream.
+ @li An error occurs.
- This operation is implemented in terms of one or more calls 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 operations until this operation
- completes.
+ The algorithm, known as a <em>composed asynchronous operation</em>,
+ is implemented in terms of calls to the next layer's `async_read_some`
+ and `async_write_some` functions. No other operation may be performed
+ on the stream until this operation completes.
- If the stream receives a valid HTTP WebSocket Upgrade request,
- an HTTP response is sent back indicating a successful upgrade.
- When the completion handler is invoked, the stream is then
- ready to send and receive WebSocket protocol frames and
- messages.
- If the HTTP Upgrade request is invalid or cannot be satisfied,
- an HTTP response is sent indicating the reason and status code
- (typically 400, "Bad Request"). This counts as a failure, and
- the completion handler will be invoked with a suitable error
- code set.
+ If a valid upgrade request is received, an HTTP response with a
+ <a href="https://tools.ietf.org/html/rfc7230#section-3.1.2">status-code</a>
+ of @ref beast::http::status::switching_protocols is sent to
+ the peer, otherwise a non-successful error is associated with
+ the operation.
@param req An object containing the HTTP Upgrade request.
Ownership is not transferred, the implementation will not access
this object from other threads.
- @param decorator A function object which will be called to modify
- the HTTP response object delivered by the implementation. This
- could be used to set the Server field, subprotocols, or other
- application or HTTP specific fields. The object will be called
- with this equivalent signature:
- @code void decorator(
- response_type& res
- ); @endcode
-
- @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(
+ @param handler The completion handler to invoke when the operation
+ completes. The implementation takes ownership of the handler by
+ performing a decay-copy. The equivalent function signature of
+ the handler must be:
+ @code
+ void handler(
error_code const& ec // Result of operation
- ); @endcode
+ );
+ @endcode
Regardless of whether the asynchronous operation completes
immediately or not, the handler will not be invoked from within
this function. Invocation of the handler will be performed in a
- manner equivalent to using `boost::asio::io_context::post`.
+ manner equivalent to using `net::post`.
+
+ @see
+ @li <a href="https://tools.ietf.org/html/rfc6455#section-4.2">Websocket Opening Handshake Server Requirements (RFC6455)</a>
*/
template<
class Body, class Allocator,
- class ResponseDecorator,
class AcceptHandler>
- BOOST_ASIO_INITFN_RESULT_TYPE(
- AcceptHandler, void(error_code))
- async_accept_ex(
+ BOOST_BEAST_ASYNC_RESULT1(AcceptHandler)
+ async_accept(
http::request<Body,
http::basic_fields<Allocator>> const& req,
- ResponseDecorator const& decorator,
AcceptHandler&& handler);
//--------------------------------------------------------------------------
//
- // Control Frames
+ // Close Frames
//
//--------------------------------------------------------------------------
- /** Send a WebSocket close frame.
+ /** Send a websocket close control frame.
- This function is used to synchronously send a close frame on
- the stream. The call blocks until one of the following is true:
+ This function is used to send a
+ <a href="https://tools.ietf.org/html/rfc6455#section-5.5.1">close frame</a>,
+ which begins the websocket closing handshake. The session ends when
+ both ends of the connection have sent and received a close frame.
- @li The close frame finishes sending.
+ The call blocks until one of the following conditions is true:
+
+ @li The close frame is written.
- @li An error occurs on the stream.
+ @li An error occurs.
+
+ The algorithm, known as a <em>composed operation</em>, is implemented
+ in terms of calls to the next layer's `write_some` function.
- This function is implemented in terms of one or more calls
- to the next layer's `write_some` functions.
+ After beginning the closing handshake, the program should not write
+ further message data, pings, or pongs. Instead, the program should
+ continue reading message data until an error occurs. A read returning
+ @ref error::closed indicates a successful connection closure.
+ @param cr The reason for the close.
If the close reason specifies a close code other than
@ref beast::websocket::close_code::none, the close frame is
sent with the close code and optional reason string. Otherwise,
the close frame is sent with no payload.
- Callers should not attempt to write WebSocket data after
- initiating the close. Instead, callers should continue
- reading until an error occurs. A read returning @ref error::closed
- indicates a successful connection closure.
-
- @param cr The reason for the close.
-
@throws system_error Thrown on failure.
+
+ @see
+ @li <a href="https://tools.ietf.org/html/rfc6455#section-7.1.2">Websocket Closing Handshake (RFC6455)</a>
*/
void
close(close_reason const& cr);
- /** Send a WebSocket close frame.
+ /** Send a websocket close control frame.
- This function is used to synchronously send a close frame on
- the stream. The call blocks until one of the following is true:
+ This function is used to send a
+ <a href="https://tools.ietf.org/html/rfc6455#section-5.5.1">close frame</a>,
+ which begins the websocket closing handshake. The session ends when
+ both ends of the connection have sent and received a close frame.
- @li The close frame finishes sending.
+ The call blocks until one of the following conditions is true:
- @li An error occurs on the stream.
+ @li The close frame is written.
- This function is implemented in terms of one or more calls
- to the next layer's `write_some` functions.
+ @li An error occurs.
+
+ The algorithm, known as a <em>composed operation</em>, is implemented
+ in terms of calls to the next layer's `write_some` function.
+
+ After beginning the closing handshake, the program should not write
+ further message data, pings, or pongs. Instead, the program should
+ continue reading message data until an error occurs. A read returning
+ @ref error::closed indicates a successful connection closure.
+ @param cr The reason for the close.
If the close reason specifies a close code other than
@ref beast::websocket::close_code::none, the close frame is
sent with the close code and optional reason string. Otherwise,
the close frame is sent with no payload.
- Callers should not attempt to write WebSocket data after
- initiating the close. Instead, callers should continue
- reading until an error occurs. A read returning @ref error::closed
- indicates a successful connection closure.
-
- @param cr The reason for the close.
-
@param ec Set to indicate what error occurred, if any.
+
+ @see
+ @li <a href="https://tools.ietf.org/html/rfc6455#section-7.1.2">Websocket Closing Handshake (RFC6455)</a>
*/
void
close(close_reason const& cr, error_code& ec);
- /** Start an asynchronous operation to send a WebSocket close frame.
+ /** Send a websocket close control frame asynchronously.
- This function is used to asynchronously send a close frame on
- the stream. This function call always returns immediately. The
- asynchronous operation will continue until one of the following
- conditions is true:
+ This function is used to asynchronously send a
+ <a href="https://tools.ietf.org/html/rfc6455#section-5.5.1">close frame</a>,
+ which begins the websocket closing handshake. The session ends when
+ both ends of the connection have sent and received a close frame.
+
+ This call always returns immediately. The asynchronous operation
+ will continue until one of the following conditions is true:
@li The close frame finishes sending.
- @li An error occurs on the stream.
+ @li An error occurs.
- This operation is implemented in terms of one or more calls 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 @ref async_ping,
- @ref async_write, @ref async_write_some, or @ref async_close)
- until this operation completes.
+ The algorithm, known as a <em>composed asynchronous operation</em>,
+ is implemented in terms of calls to the next layer's `async_write_some`
+ function. No other operations except for message reading operations
+ should be initiated on the stream after a close operation is started.
+ After beginning the closing handshake, the program should not write
+ further message data, pings, or pongs. Instead, the program should
+ continue reading message data until an error occurs. A read returning
+ @ref error::closed indicates a successful connection closure.
+
+ @param cr The reason for the close.
If the close reason specifies a close code other than
@ref beast::websocket::close_code::none, the close frame is
sent with the close code and optional reason string. Otherwise,
the close frame is sent with no payload.
- Callers should not attempt to write WebSocket data after
- initiating the close. Instead, callers should continue
- reading until an error occurs. A read returning @ref error::closed
- indicates a successful connection closure.
-
- @param cr The reason for the close.
-
- @param handler Invoked when the operation completes.
- The handler may be moved or copied as needed.
- The function signature of the handler must be:
+ @param handler The completion handler to invoke when the operation
+ completes. The implementation takes ownership of the handler by
+ performing a decay-copy. The equivalent function signature of
+ the handler must be:
@code
void handler(
error_code const& ec // Result of operation
@@ -2404,24 +1525,36 @@ public:
Regardless of whether the asynchronous operation completes
immediately or not, the handler will not be invoked from within
this function. Invocation of the handler will be performed in a
- manner equivalent to using `boost::asio::io_context::post`.
+ manner equivalent to using `net::post`.
+
+ @see
+ @li <a href="https://tools.ietf.org/html/rfc6455#section-7.1.2">Websocket Closing Handshake (RFC6455)</a>
*/
template<class CloseHandler>
- BOOST_ASIO_INITFN_RESULT_TYPE(
- CloseHandler, void(error_code))
+ BOOST_BEAST_ASYNC_RESULT1(CloseHandler)
async_close(close_reason const& cr, CloseHandler&& handler);
- /** Send a WebSocket ping frame.
+ //--------------------------------------------------------------------------
+ //
+ // Ping/Pong Frames
+ //
+ //--------------------------------------------------------------------------
+
+ /** Send a websocket ping control frame.
- This function is used to synchronously send a ping frame on
- the stream. The call blocks until one of the following is true:
+ This function is used to send a
+ <a href="https://tools.ietf.org/html/rfc6455#section-5.5.2">ping frame</a>,
+ which usually elicits an automatic pong control frame response from
+ the peer.
- @li The ping frame finishes sending.
+ The call blocks until one of the following conditions is true:
- @li An error occurs on the stream.
+ @li The ping frame is written.
- This function is implemented in terms of one or more calls to the
- next layer's `write_some` functions.
+ @li An error occurs.
+
+ The algorithm, known as a <em>composed operation</em>, is implemented
+ in terms of calls to the next layer's `write_some` function.
@param payload The payload of the ping message, which may be empty.
@@ -2430,17 +1563,21 @@ public:
void
ping(ping_data const& payload);
- /** Send a WebSocket ping frame.
+ /** Send a websocket ping control frame.
- This function is used to synchronously send a ping frame on
- the stream. The call blocks until one of the following is true:
+ This function is used to send a
+ <a href="https://tools.ietf.org/html/rfc6455#section-5.5.2">ping frame</a>,
+ which usually elicits an automatic pong control frame response from
+ the peer.
- @li The ping frame finishes sending.
+ The call blocks until one of the following conditions is true:
- @li An error occurs on the stream.
+ @li The ping frame is written.
- This function is implemented in terms of one or more calls to the
- next layer's `write_some` functions.
+ @li An error occurs.
+
+ The algorithm, known as a <em>composed operation</em>, is implemented
+ in terms of calls to the next layer's `write_some` function.
@param payload The payload of the ping message, which may be empty.
@@ -2449,31 +1586,35 @@ public:
void
ping(ping_data const& payload, error_code& ec);
- /** Start an asynchronous operation to send a WebSocket ping frame.
+ /** Send a websocket ping control frame asynchronously.
- This function is used to asynchronously send a ping frame to
- the stream. The function call always returns immediately. The
- asynchronous operation will continue until one of the following
- is true:
+ This function is used to asynchronously send a
+ <a href="https://tools.ietf.org/html/rfc6455#section-5.5.2">ping frame</a>,
+ which usually elicits an automatic pong control frame response from
+ the peer.
- @li The entire ping frame is sent.
+ @li The ping frame is written.
- @li An error occurs on the stream.
+ @li An error occurs.
- This operation is implemented in terms of one or more calls 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 writes until this operation completes.
+ The algorithm, known as a <em>composed asynchronous operation</em>,
+ is implemented in terms of calls to the next layer's `async_write_some`
+ function. The program must ensure that no other calls to @ref ping,
+ @ref pong, @ref async_ping, or @ref async_pong are performed until
+ this operation completes.
If a close frame is sent or received before the ping frame is
- sent, the completion handler will be called with the error
- set to `boost::asio::error::operation_aborted`.
+ sent, the error received by this completion handler will be
+ `net::error::operation_aborted`.
@param payload The payload of the ping message, which may be empty.
+ The implementation will not access the contents of this object after
+ the initiating function returns.
- @param handler Invoked when the operation completes.
- The handler may be moved or copied as needed.
- The function signature of the handler must be:
+ @param handler The completion handler to invoke when the operation
+ completes. The implementation takes ownership of the handler by
+ performing a decay-copy. The equivalent function signature of
+ the handler must be:
@code
void handler(
error_code const& ec // Result of operation
@@ -2482,29 +1623,31 @@ public:
Regardless of whether the asynchronous operation completes
immediately or not, the handler will not be invoked from within
this function. Invocation of the handler will be performed in a
- manner equivalent to using `boost::asio::io_context::post`.
+ manner equivalent to using `net::post`.
*/
template<class WriteHandler>
- BOOST_ASIO_INITFN_RESULT_TYPE(
- WriteHandler, void(error_code))
+ BOOST_BEAST_ASYNC_RESULT1(WriteHandler)
async_ping(ping_data const& payload, WriteHandler&& handler);
- /** Send a WebSocket pong frame.
+ /** Send a websocket pong control frame.
+
+ This function is used to send a
+ <a href="https://tools.ietf.org/html/rfc6455#section-5.5.3">pong frame</a>,
+ which is usually sent automatically in response to a ping frame
+ from the remote peer.
- This function is used to synchronously send a pong frame on
- the stream. The call blocks until one of the following is true:
+ The call blocks until one of the following conditions is true:
- @li The pong frame finishes sending.
+ @li The pong frame is written.
- @li An error occurs on the stream.
+ @li An error occurs.
- This function is implemented in terms of one or more calls to the
- next layer's `write_some` functions.
+ The algorithm, known as a <em>composed operation</em>, is implemented
+ in terms of calls to the next layer's `write_some` function.
- The WebSocket protocol allows pong frames to be sent from either
- end at any time. It is not necessary to first receive a ping in
- order to send a pong. The remote peer may use the receipt of a
- pong frame as an indication that the connection is not dead.
+ WebSocket allows pong frames to be sent at any time, without first
+ receiving a ping. An unsolicited pong sent in this fashion may
+ indicate to the remote peer that the connection is still active.
@param payload The payload of the pong message, which may be empty.
@@ -2513,22 +1656,25 @@ public:
void
pong(ping_data const& payload);
- /** Send a WebSocket pong frame.
+ /** Send a websocket pong control frame.
- This function is used to synchronously send a pong frame on
- the stream. The call blocks until one of the following is true:
+ This function is used to send a
+ <a href="https://tools.ietf.org/html/rfc6455#section-5.5.3">pong frame</a>,
+ which is usually sent automatically in response to a ping frame
+ from the remote peer.
- @li The pong frame finishes sending.
+ The call blocks until one of the following conditions is true:
- @li An error occurs on the stream.
+ @li The pong frame is written.
- This function is implemented in terms of one or more calls to the
- next layer's `write_some` functions.
+ @li An error occurs.
+
+ The algorithm, known as a <em>composed operation</em>, is implemented
+ in terms of calls to the next layer's `write_some` function.
- The WebSocket protocol allows pong frames to be sent from either
- end at any time. It is not necessary to first receive a ping in
- order to send a pong. The remote peer may use the receipt of a
- pong frame as an indication that the connection is not dead.
+ WebSocket allows pong frames to be sent at any time, without first
+ receiving a ping. An unsolicited pong sent in this fashion may
+ indicate to the remote peer that the connection is still active.
@param payload The payload of the pong message, which may be empty.
@@ -2537,36 +1683,39 @@ public:
void
pong(ping_data const& payload, error_code& ec);
- /** Start an asynchronous operation to send a WebSocket pong frame.
+ /** Send a websocket pong control frame asynchronously.
- This function is used to asynchronously send a pong frame to
- the stream. The function call always returns immediately. The
- asynchronous operation will continue until one of the following
- is true:
+ This function is used to asynchronously send a
+ <a href="https://tools.ietf.org/html/rfc6455#section-5.5.3">pong frame</a>,
+ which is usually sent automatically in response to a ping frame
+ from the remote peer.
- @li The entire pong frame is sent.
+ @li The pong frame is written.
- @li An error occurs on the stream.
-
- This operation is implemented in terms of one or more calls 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 writes until this operation completes.
+ @li An error occurs.
- The WebSocket protocol allows pong frames to be sent from either
- end at any time. It is not necessary to first receive a ping in
- order to send a pong. The remote peer may use the receipt of a
- pong frame as an indication that the connection is not dead.
+ The algorithm, known as a <em>composed asynchronous operation</em>,
+ is implemented in terms of calls to the next layer's `async_write_some`
+ function. The program must ensure that no other calls to @ref ping,
+ @ref pong, @ref async_ping, or @ref async_pong are performed until
+ this operation completes.
If a close frame is sent or received before the pong frame is
- sent, the completion handler will be called with the error
- set to `boost::asio::error::operation_aborted`.
+ sent, the error received by this completion handler will be
+ `net::error::operation_aborted`.
+
+ WebSocket allows pong frames to be sent at any time, without first
+ receiving a ping. An unsolicited pong sent in this fashion may
+ indicate to the remote peer that the connection is still active.
@param payload The payload of the pong message, which may be empty.
+ The implementation will not access the contents of this object after
+ the initiating function returns.
- @param handler Invoked when the operation completes.
- The handler may be moved or copied as needed.
- The function signature of the handler must be:
+ @param handler The completion handler to invoke when the operation
+ completes. The implementation takes ownership of the handler by
+ performing a decay-copy. The equivalent function signature of
+ the handler must be:
@code
void handler(
error_code const& ec // Result of operation
@@ -2575,11 +1724,10 @@ public:
Regardless of whether the asynchronous operation completes
immediately or not, the handler will not be invoked from within
this function. Invocation of the handler will be performed in a
- manner equivalent to using `boost::asio::io_context::post`.
+ manner equivalent to using `net::post`.
*/
template<class WriteHandler>
- BOOST_ASIO_INITFN_RESULT_TYPE(
- WriteHandler, void(error_code))
+ BOOST_BEAST_ASYNC_RESULT1(WriteHandler)
async_pong(ping_data const& payload, WriteHandler&& handler);
//--------------------------------------------------------------------------
@@ -2588,10 +1736,10 @@ public:
//
//--------------------------------------------------------------------------
- /** Read a message
+ /** Read a complete message.
+
+ This function is used to read a complete message.
- This function is used to synchronously read a complete
- message from the stream.
The call blocks until one of the following is true:
@li A complete message is received.
@@ -2599,43 +1747,42 @@ public:
@li A close frame is received. In this case the error indicated by
the function will be @ref error::closed.
- @li An error occurs on the stream.
+ @li An error occurs.
- This operation is implemented in terms of one or more calls to the next
- layer's `read_some` and `write_some` functions.
+ The algorithm, known as a <em>composed operation</em>, is implemented
+ in terms of calls to the next layer's `read_some` and `write_some`
+ functions.
- Received message data, if any, is appended to the input area of the
- buffer. The functions @ref got_binary and @ref got_text may be used
+ Received message data is appended to the buffer.
+ The functions @ref got_binary and @ref got_text may be used
to query the stream and determine the type of the last received message.
- While this operation is active, the implementation will read incoming
- control frames and handle them automatically as follows:
+ Until the call returns, the implementation will read incoming control
+ frames and handle them automatically as follows:
@li The @ref control_callback will be invoked for each control frame.
@li For each received ping frame, a pong frame will be
automatically sent.
- @li If a close frame is received, the WebSocket close procedure is
+ @li If a close frame is received, the WebSocket closing handshake is
performed. In this case, when the function returns, the error
@ref error::closed will be indicated.
@return The number of message payload bytes appended to the buffer.
- @param buffer A dynamic buffer to hold the message data after any
- masking or decompression has been applied.
+ @param buffer A dynamic buffer to append message data to.
- @throws system_error Thrown to indicate an error. The corresponding
- error code may be retrieved from the exception object for inspection.
+ @throws system_error Thrown on failure.
*/
template<class DynamicBuffer>
std::size_t
read(DynamicBuffer& buffer);
- /** Read a message
+ /** Read a complete message.
+
+ This function is used to read a complete message.
- This function is used to synchronously read a complete
- message from the stream.
The call blocks until one of the following is true:
@li A complete message is received.
@@ -2643,31 +1790,31 @@ public:
@li A close frame is received. In this case the error indicated by
the function will be @ref error::closed.
- @li An error occurs on the stream.
+ @li An error occurs.
- This operation is implemented in terms of one or more calls to the next
- layer's `read_some` and `write_some` functions.
+ The algorithm, known as a <em>composed operation</em>, is implemented
+ in terms of calls to the next layer's `read_some` and `write_some`
+ functions.
- Received message data, if any, is appended to the input area of the
- buffer. The functions @ref got_binary and @ref got_text may be used
+ Received message data is appended to the buffer.
+ The functions @ref got_binary and @ref got_text may be used
to query the stream and determine the type of the last received message.
- While this operation is active, the implementation will read incoming
- control frames and handle them automatically as follows:
+ Until the call returns, the implementation will read incoming control
+ frames and handle them automatically as follows:
@li The @ref control_callback will be invoked for each control frame.
@li For each received ping frame, a pong frame will be
automatically sent.
- @li If a close frame is received, the WebSocket close procedure is
+ @li If a close frame is received, the WebSocket closing handshake is
performed. In this case, when the function returns, the error
@ref error::closed will be indicated.
@return The number of message payload bytes appended to the buffer.
- @param buffer A dynamic buffer to hold the message data after any
- masking or decompression has been applied.
+ @param buffer A dynamic buffer to append message data to.
@param ec Set to indicate what error occurred, if any.
*/
@@ -2675,32 +1822,31 @@ public:
std::size_t
read(DynamicBuffer& buffer, error_code& ec);
- /** Read a message asynchronously
+ /** Read a complete message asynchronously.
- This function is used to asynchronously read a complete
- message from the stream.
- The function call always returns immediately.
- The asynchronous operation will continue until one of the
- following is true:
+ This function is used to asynchronously read a complete message.
+
+ This call always returns immediately. The asynchronous operation
+ will continue until one of the following conditions is true:
@li A complete message is received.
@li A close frame is received. In this case the error indicated by
the function will be @ref error::closed.
- @li An error occurs on the stream.
+ @li An error occurs.
- This operation is implemented in terms of one or more calls to the
- next layer's `async_read_some` and `async_write_some` functions,
- and is known as a <em>composed operation</em>. The program must
- ensure that the stream performs no other reads until this operation
- completes.
+ The algorithm, known as a <em>composed asynchronous operation</em>,
+ is implemented in terms of calls to the next layer's `async_read_some`
+ and `async_write_some` functions. The program must ensure that no other
+ calls to @ref read, @ref read_some, @ref async_read, or @ref async_read_some
+ are performed until this operation completes.
- Received message data, if any, is appended to the input area of the
- buffer. The functions @ref got_binary and @ref got_text may be used
+ Received message data is appended to the buffer.
+ The functions @ref got_binary and @ref got_text may be used
to query the stream and determine the type of the last received message.
- While this operation is active, the implementation will read incoming
+ Until the operation completes, the implementation will read incoming
control frames and handle them automatically as follows:
@li The @ref control_callback will be invoked for each control frame.
@@ -2712,19 +1858,17 @@ public:
performed. In this case, when the function returns, the error
@ref error::closed will be indicated.
- Because of the need to handle control frames, asynchronous read
- operations can cause writes to take place. These writes are managed
- transparently; callers can still have one active asynchronous
- read and asynchronous write operation pending simultaneously
- (a user initiated call to @ref async_close counts as a write).
+ Pong frames and close frames sent by the implementation while the
+ read operation is outstanding do not prevent the application from
+ also writing message data, sending pings, sending pongs, or sending
+ close frames.
- @param buffer A dynamic buffer to hold the message data after
- any masking or decompression has been applied. This object must
- remain valid until the handler is called.
+ @param buffer A dynamic buffer to append message data to.
- @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:
+ @param handler The completion handler to invoke when the operation
+ completes. The implementation takes ownership of the handler by
+ performing a decay-copy. The equivalent function signature of
+ the handler must be:
@code
void handler(
error_code const& ec, // Result of operation
@@ -2734,62 +1878,60 @@ public:
Regardless of whether the asynchronous operation completes
immediately or not, the handler will not be invoked from within
this function. Invocation of the handler will be performed in a
- manner equivalent to using `boost::asio::io_context::post`.
+ manner equivalent to using `net::post`.
*/
template<class DynamicBuffer, class ReadHandler>
- BOOST_ASIO_INITFN_RESULT_TYPE(
- ReadHandler, void(error_code, std::size_t))
+ BOOST_BEAST_ASYNC_RESULT2(ReadHandler)
async_read(
DynamicBuffer& buffer,
ReadHandler&& handler);
//--------------------------------------------------------------------------
- /** Read part of a message
+ /** Read some message data.
+
+ This function is used to read some message data.
- This function is used to synchronously read some
- message data from the stream.
The call blocks until one of the following is true:
- @li Some or all of the message is received.
+ @li Some message data is received.
@li A close frame is received. In this case the error indicated by
the function will be @ref error::closed.
- @li An error occurs on the stream.
+ @li An error occurs.
- This operation is implemented in terms of one or more calls to the next
- layer's `read_some` and `write_some` functions.
+ The algorithm, known as a <em>composed operation</em>, is implemented
+ in terms of calls to the next layer's `read_some` and `write_some`
+ functions.
- Received message data, if any, is appended to the input area of the
- buffer. The functions @ref got_binary and @ref got_text may be used
+ Received message data is appended to the buffer.
+ The functions @ref got_binary and @ref got_text may be used
to query the stream and determine the type of the last received message.
The function @ref is_message_done may be called to determine if the
message received by the last read operation is complete.
- While this operation is active, the implementation will read incoming
- control frames and handle them automatically as follows:
+ Until the call returns, the implementation will read incoming control
+ frames and handle them automatically as follows:
@li The @ref control_callback will be invoked for each control frame.
@li For each received ping frame, a pong frame will be
automatically sent.
- @li If a close frame is received, the WebSocket close procedure is
+ @li If a close frame is received, the WebSocket closing handshake is
performed. In this case, when the function returns, the error
@ref error::closed will be indicated.
@return The number of message payload bytes appended to the buffer.
- @param buffer A dynamic buffer to hold the message data after any
- masking or decompression has been applied.
+ @param buffer A dynamic buffer to append message data to.
@param limit An upper limit on the number of bytes this function
will append into the buffer. If this value is zero, then a reasonable
size will be chosen automatically.
- @throws system_error Thrown to indicate an error. The corresponding
- error code may be retrieved from the exception object for inspection.
+ @throws system_error Thrown on failure.
*/
template<class DynamicBuffer>
std::size_t
@@ -2797,44 +1939,44 @@ public:
DynamicBuffer& buffer,
std::size_t limit);
- /** Read part of a message
+ /** Read some message data.
+
+ This function is used to read some message data.
- This function is used to synchronously read some
- message data from the stream.
The call blocks until one of the following is true:
- @li Some or all of the message is received.
+ @li Some message data is received.
@li A close frame is received. In this case the error indicated by
the function will be @ref error::closed.
- @li An error occurs on the stream.
+ @li An error occurs.
- This operation is implemented in terms of one or more calls to the next
- layer's `read_some` and `write_some` functions.
+ The algorithm, known as a <em>composed operation</em>, is implemented
+ in terms of calls to the next layer's `read_some` and `write_some`
+ functions.
- Received message data, if any, is appended to the input area of the
- buffer. The functions @ref got_binary and @ref got_text may be used
+ Received message data is appended to the buffer.
+ The functions @ref got_binary and @ref got_text may be used
to query the stream and determine the type of the last received message.
The function @ref is_message_done may be called to determine if the
message received by the last read operation is complete.
- While this operation is active, the implementation will read incoming
- control frames and handle them automatically as follows:
+ Until the call returns, the implementation will read incoming control
+ frames and handle them automatically as follows:
@li The @ref control_callback will be invoked for each control frame.
@li For each received ping frame, a pong frame will be
automatically sent.
- @li If a close frame is received, the WebSocket close procedure is
+ @li If a close frame is received, the WebSocket closing handshake is
performed. In this case, when the function returns, the error
@ref error::closed will be indicated.
@return The number of message payload bytes appended to the buffer.
- @param buffer A dynamic buffer to hold the message data after any
- masking or decompression has been applied.
+ @param buffer A dynamic buffer to append message data to.
@param limit An upper limit on the number of bytes this function
will append into the buffer. If this value is zero, then a reasonable
@@ -2849,34 +1991,31 @@ public:
std::size_t limit,
error_code& ec);
- /** Read part of a message asynchronously
+ /** Read some message data asynchronously.
+
+ This function is used to asynchronously read some message data.
- This function is used to asynchronously read part of a
- message from the stream.
- The function call always returns immediately.
- The asynchronous operation will continue until one of the
- following is true:
+ This call always returns immediately. The asynchronous operation
+ will continue until one of the following conditions is true:
- @li Some or all of the message is received.
+ @li Some message data is received.
@li A close frame is received. In this case the error indicated by
the function will be @ref error::closed.
- @li An error occurs on the stream.
+ @li An error occurs.
- This operation is implemented in terms of one or more calls to the
- next layer's `async_read_some` and `async_write_some` functions,
- and is known as a <em>composed operation</em>. The program must
- ensure that the stream performs no other reads until this operation
- completes.
+ The algorithm, known as a <em>composed asynchronous operation</em>,
+ is implemented in terms of calls to the next layer's `async_read_some`
+ and `async_write_some` functions. The program must ensure that no other
+ calls to @ref read, @ref read_some, @ref async_read, or @ref async_read_some
+ are performed until this operation completes.
- Received message data, if any, is appended to the input area of the
- buffer. The functions @ref got_binary and @ref got_text may be used
+ Received message data is appended to the buffer.
+ The functions @ref got_binary and @ref got_text may be used
to query the stream and determine the type of the last received message.
- The function @ref is_message_done may be called to determine if the
- message received by the last read operation is complete.
- While this operation is active, the implementation will read incoming
+ Until the operation completes, the implementation will read incoming
control frames and handle them automatically as follows:
@li The @ref control_callback will be invoked for each control frame.
@@ -2888,23 +2027,21 @@ public:
performed. In this case, when the function returns, the error
@ref error::closed will be indicated.
- Because of the need to handle control frames, asynchronous read
- operations can cause writes to take place. These writes are managed
- transparently; callers can still have one active asynchronous
- read and asynchronous write operation pending simultaneously
- (a user initiated call to @ref async_close counts as a write).
+ Pong frames and close frames sent by the implementation while the
+ read operation is outstanding do not prevent the application from
+ also writing message data, sending pings, sending pongs, or sending
+ close frames.
- @param buffer A dynamic buffer to hold the message data after
- any masking or decompression has been applied. This object must
- remain valid until the handler is called.
+ @param buffer A dynamic buffer to append message data to.
@param limit An upper limit on the number of bytes this function
will append into the buffer. If this value is zero, then a reasonable
size will be chosen automatically.
- @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:
+ @param handler The completion handler to invoke when the operation
+ completes. The implementation takes ownership of the handler by
+ performing a decay-copy. The equivalent function signature of
+ the handler must be:
@code
void handler(
error_code const& ec, // Result of operation
@@ -2914,11 +2051,10 @@ public:
Regardless of whether the asynchronous operation completes
immediately or not, the handler will not be invoked from within
this function. Invocation of the handler will be performed in a
- manner equivalent to using `boost::asio::io_context::post`.
+ manner equivalent to using `net::post`.
*/
template<class DynamicBuffer, class ReadHandler>
- BOOST_ASIO_INITFN_RESULT_TYPE(
- ReadHandler, void(error_code, std::size_t))
+ BOOST_BEAST_ASYNC_RESULT2(ReadHandler)
async_read_some(
DynamicBuffer& buffer,
std::size_t limit,
@@ -2926,93 +2062,92 @@ public:
//--------------------------------------------------------------------------
- /** Read part of a message
+ /** Read some message data.
+
+ This function is used to read some message data.
- This function is used to synchronously read some
- message data from the stream.
The call blocks until one of the following is true:
- @li Some or all of the message is received.
+ @li Some message data is received.
@li A close frame is received. In this case the error indicated by
the function will be @ref error::closed.
- @li An error occurs on the stream.
+ @li An error occurs.
- This operation is implemented in terms of one or more calls to the next
- layer's `read_some` and `write_some` functions.
+ The algorithm, known as a <em>composed operation</em>, is implemented
+ in terms of calls to the next layer's `read_some` and `write_some`
+ functions.
- Received message data, if any, is written to the buffer sequence.
The functions @ref got_binary and @ref got_text may be used
to query the stream and determine the type of the last received message.
The function @ref is_message_done may be called to determine if the
message received by the last read operation is complete.
- While this operation is active, the implementation will read incoming
- control frames and handle them automatically as follows:
+ Until the call returns, the implementation will read incoming control
+ frames and handle them automatically as follows:
@li The @ref control_callback will be invoked for each control frame.
@li For each received ping frame, a pong frame will be
automatically sent.
- @li If a close frame is received, the WebSocket close procedure is
+ @li If a close frame is received, the WebSocket closing handshake is
performed. In this case, when the function returns, the error
@ref error::closed will be indicated.
- @return The number of message payload bytes written to the
- buffer sequence.
+ @return The number of message payload bytes appended to the buffer.
- @param buffers A buffer sequence to hold the message data after any
- masking or decompression has been applied.
+ @param buffers A buffer sequence to write message data into.
+ The previous contents of the buffers will be overwritten, starting
+ from the beginning.
- @throws system_error Thrown to indicate an error. The corresponding
- error code may be retrieved from the exception object for inspection.
+ @throws system_error Thrown on failure.
*/
template<class MutableBufferSequence>
std::size_t
read_some(
MutableBufferSequence const& buffers);
- /** Read part of a message
+ /** Read some message data.
+
+ This function is used to read some message data.
- This function is used to synchronously read some
- message data from the stream.
The call blocks until one of the following is true:
- @li Some or all of the message is received.
+ @li Some message data is received.
@li A close frame is received. In this case the error indicated by
the function will be @ref error::closed.
- @li An error occurs on the stream.
+ @li An error occurs.
- This operation is implemented in terms of one or more calls to the next
- layer's `read_some` and `write_some` functions.
+ The algorithm, known as a <em>composed operation</em>, is implemented
+ in terms of calls to the next layer's `read_some` and `write_some`
+ functions.
- Received message data, if any, is written to the buffer sequence.
The functions @ref got_binary and @ref got_text may be used
to query the stream and determine the type of the last received message.
The function @ref is_message_done may be called to determine if the
message received by the last read operation is complete.
- While this operation is active, the implementation will read incoming
- control frames and handle them automatically as follows:
+ Until the call returns, the implementation will read incoming control
+ frames and handle them automatically as follows:
@li The @ref control_callback will be invoked for each control frame.
@li For each received ping frame, a pong frame will be
automatically sent.
- @li If a close frame is received, the WebSocket close procedure is
+ @li If a close frame is received, the WebSocket closing handshake is
performed. In this case, when the function returns, the error
@ref error::closed will be indicated.
- @return The number of message payload bytes written to the
- buffer sequence.
+ @return The number of message payload bytes appended to the buffer.
- @param buffers A buffer sequence to hold the message data after any
- masking or decompression has been applied.
+ @param buffers A buffer sequence to write message data into.
+ The previous contents of the buffers will be overwritten, starting
+ from the beginning.
@param ec Set to indicate what error occurred, if any.
*/
@@ -3022,34 +2157,31 @@ public:
MutableBufferSequence const& buffers,
error_code& ec);
- /** Read part of a message asynchronously
+ /** Read some message data asynchronously.
+
+ This function is used to asynchronously read some message data.
- This function is used to asynchronously read part of a
- message from the stream.
- The function call always returns immediately.
- The asynchronous operation will continue until one of the
- following is true:
+ This call always returns immediately. The asynchronous operation
+ will continue until one of the following conditions is true:
- @li Some or all of the message is received.
+ @li Some message data is received.
@li A close frame is received. In this case the error indicated by
the function will be @ref error::closed.
- @li An error occurs on the stream.
+ @li An error occurs.
- This operation is implemented in terms of one or more calls to the
- next layer's `async_read_some` and `async_write_some` functions,
- and is known as a <em>composed operation</em>. The program must
- ensure that the stream performs no other reads until this operation
- completes.
+ The algorithm, known as a <em>composed asynchronous operation</em>,
+ is implemented in terms of calls to the next layer's `async_read_some`
+ and `async_write_some` functions. The program must ensure that no other
+ calls to @ref read, @ref read_some, @ref async_read, or @ref async_read_some
+ are performed until this operation completes.
- Received message data, if any, is written to the buffer sequence.
+ Received message data is appended to the buffer.
The functions @ref got_binary and @ref got_text may be used
to query the stream and determine the type of the last received message.
- The function @ref is_message_done may be called to determine if the
- message received by the last read operation is complete.
- While this operation is active, the implementation will read incoming
+ Until the operation completes, the implementation will read incoming
control frames and handle them automatically as follows:
@li The @ref control_callback will be invoked for each control frame.
@@ -3061,37 +2193,37 @@ public:
performed. In this case, when the function returns, the error
@ref error::closed will be indicated.
- Because of the need to handle control frames, asynchronous read
- operations can cause writes to take place. These writes are managed
- transparently; callers can still have one active asynchronous
- read and asynchronous write operation pending simultaneously
- (a user initiated call to @ref async_close counts as a write).
-
- @param buffers The buffer sequence into which message data will
- be placed after any masking or decompresison has been applied.
- The implementation will make copies of this object as needed,
- but ownership of the underlying memory is not transferred.
- The caller is responsible for ensuring that the memory
- locations pointed to by the buffer sequence remains valid
- until the completion handler is called.
-
- @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:
+ Pong frames and close frames sent by the implementation while the
+ read operation is outstanding do not prevent the application from
+ also writing message data, sending pings, sending pongs, or sending
+ close frames.
+
+ @param buffers A buffer sequence to write message data into.
+ The previous contents of the buffers will be overwritten, starting
+ from the beginning.
+ The implementation will make copies of this object as needed, but
+ but ownership of the underlying memory is not transferred. The
+ caller is responsible for ensuring that the memory locations
+ pointed to by the buffer sequence remain valid until the
+ completion handler is called.
+
+ @param handler The completion handler to invoke when the operation
+ completes. The implementation takes ownership of the handler by
+ performing a decay-copy. The equivalent function signature of
+ the handler must be:
@code
void handler(
error_code const& ec, // Result of operation
- std::size_t bytes_written // Number of bytes written to the buffer sequence
+ std::size_t bytes_written // Number of bytes written to the buffers
);
@endcode
Regardless of whether the asynchronous operation completes
immediately or not, the handler will not be invoked from within
this function. Invocation of the handler will be performed in a
- manner equivalent to using `boost::asio::io_context::post`.
+ manner equivalent to using `net::post`.
*/
template<class MutableBufferSequence, class ReadHandler>
- BOOST_ASIO_INITFN_RESULT_TYPE(
- ReadHandler, void(error_code, std::size_t))
+ BOOST_BEAST_ASYNC_RESULT2(ReadHandler)
async_read_some(
MutableBufferSequence const& buffers,
ReadHandler&& handler);
@@ -3102,18 +2234,18 @@ public:
//
//--------------------------------------------------------------------------
- /** Write a message to the stream.
+ /** Write a complete message.
- This function is used to synchronously write a message to
- the stream. The call blocks until one of the following conditions
- is met:
+ This function is used to write a complete message.
+
+ The call blocks until one of the following is true:
- @li The entire message is sent.
+ @li The message is written.
@li An error occurs.
- This operation is implemented in terms of one or more calls to the
- next layer's `write_some` function.
+ The algorithm, known as a <em>composed operation</em>, is implemented
+ in terms of calls to the next layer's `write_some` function.
The current setting of the @ref binary option controls
whether the message opcode is set to text or binary. If the
@@ -3121,38 +2253,28 @@ public:
into one or more frames as necessary. The actual payload contents
sent may be transformed as per the WebSocket protocol settings.
- @param buffers The buffers containing the entire message
- payload. The implementation will make copies of this object
- as needed, but ownership of the underlying memory is not
- transferred. The caller is responsible for ensuring that
- the memory locations pointed to by buffers remains valid
- until the completion handler is called.
+ @param buffers The buffers containing the message to send.
- @return The number of bytes written from the buffers.
- If an error occurred, this will be less than the sum
- of the buffer sizes.
+ @return The number of bytes sent from the buffers.
@throws system_error Thrown on failure.
-
- @note This function always sends an entire message. To
- send a message in fragments, use @ref write_some.
*/
template<class ConstBufferSequence>
std::size_t
write(ConstBufferSequence const& buffers);
- /** Write a message to the stream.
+ /** Write a complete message.
- This function is used to synchronously write a message to
- the stream. The call blocks until one of the following conditions
- is met:
+ This function is used to write a complete message.
- @li The entire message is sent.
+ The call blocks until one of the following is true:
+
+ @li The complete message is written.
@li An error occurs.
- This operation is implemented in terms of one or more calls to the
- next layer's `write_some` function.
+ The algorithm, known as a <em>composed operation</em>, is implemented
+ in terms of calls to the next layer's `write_some` function.
The current setting of the @ref binary option controls
whether the message opcode is set to text or binary. If the
@@ -3160,45 +2282,32 @@ public:
into one or more frames as necessary. The actual payload contents
sent may be transformed as per the WebSocket protocol settings.
- @param buffers The buffers containing the entire message
- payload. The implementation will make copies of this object
- as needed, but ownership of the underlying memory is not
- transferred. The caller is responsible for ensuring that
- the memory locations pointed to by buffers remains valid
- until the completion handler is called.
-
- @return The number of bytes written from the buffers.
- If an error occurred, this will be less than the sum
- of the buffer sizes.
+ @param buffers The buffers containing the message to send.
@param ec Set to indicate what error occurred, if any.
- @throws system_error Thrown on failure.
-
- @note This function always sends an entire message. To
- send a message in fragments, use @ref write_some.
+ @return The number of bytes sent from the buffers.
*/
template<class ConstBufferSequence>
std::size_t
write(ConstBufferSequence const& buffers, error_code& ec);
- /** Start an asynchronous operation to write a message to the stream.
+ /** Write a complete message asynchronously.
+
+ This function is used to asynchronously write a complete message.
- This function is used to asynchronously write a message to
- the stream. The function call always returns immediately.
- The asynchronous operation will continue until one of the
- following conditions is true:
+ This call always returns immediately. The asynchronous operation
+ will continue until one of the following conditions is true:
- @li The entire message is sent.
+ @li The complete message is written.
@li An error occurs.
- This operation is implemented in terms of one or more calls
- 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
- @ref async_write, @ref async_write_some, or
- @ref async_close).
+ The algorithm, known as a <em>composed asynchronous operation</em>,
+ is implemented in terms of calls to the next layer's
+ `async_write_some` function. The program must ensure that no other
+ calls to @ref write, @ref write_some, @ref async_write, or
+ @ref async_write_some are performed until this operation completes.
The current setting of the @ref binary option controls
whether the message opcode is set to text or binary. If the
@@ -3206,66 +2315,61 @@ public:
into one or more frames as necessary. The actual payload contents
sent may be transformed as per the WebSocket protocol settings.
- @param buffers The buffers containing the entire message
+ @param buffers A buffer sequence containing the entire message
payload. The implementation will make copies of this object
as needed, but ownership of the underlying memory is not
transferred. The caller is responsible for ensuring that
the memory locations pointed to by buffers remains valid
until the completion handler is called.
- @param handler Invoked when the operation completes.
- The handler may be moved or copied as needed.
- The function signature of the handler must be:
+ @param handler The completion handler to invoke when the operation
+ completes. The implementation takes ownership of the handler by
+ performing a decay-copy. 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
+ std::size_t bytes_transferred // Number of bytes sent from the
// buffers. If an error occurred,
- // this will be less than the sum
- // of the buffer sizes.
+ // this will be less than the buffer_size.
);
@endcode
Regardless of whether the asynchronous operation completes
immediately or not, the handler will not be invoked from within
this function. Invocation of the handler will be performed in a
- manner equivalent to using `boost::asio::io_context::post`.
+ manner equivalent to using `net::post`.
*/
template<
class ConstBufferSequence,
class WriteHandler>
- BOOST_ASIO_INITFN_RESULT_TYPE(
- WriteHandler, void(error_code, std::size_t))
+ BOOST_BEAST_ASYNC_RESULT2(WriteHandler)
async_write(
ConstBufferSequence const& buffers,
WriteHandler&& handler);
- /** Write partial message data on the stream.
+ /** Write some message data.
- This function is used to write some or all of a message's
- payload to the stream. The call will block until one of the
- following conditions is true:
+ This function is used to send part of a message.
- @li A frame is sent.
+ The call blocks until one of the following is true:
- @li Message data is transferred to the write buffer.
+ @li The message data is written.
@li An error occurs.
- This operation is implemented in terms of one or more calls
- to the stream's `write_some` function.
+ The algorithm, known as a <em>composed operation</em>, is implemented
+ in terms of calls to the next layer's `write_some` function.
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
- the @ref binary option. The actual payload sent may be
- transformed as per the WebSocket protocol settings.
+ will be set to text or binary based on the current setting of
+ the @ref binary (or @ref text) option. The actual payload sent
+ may be transformed as per the WebSocket protocol settings.
@param fin `true` if this is the last part of the message.
- @param buffers The input buffer sequence holding the data to write.
+ @param buffers The buffers containing the message part to send.
- @return The number of bytes written from the buffers.
- If an error occurred, this will be less than the sum
- of the buffer sizes.
+ @return The number of bytes sent from the buffers.
@throws system_error Thrown on failure.
*/
@@ -3273,35 +2377,31 @@ public:
std::size_t
write_some(bool fin, ConstBufferSequence const& buffers);
- /** Write partial message data on the stream.
+ /** Write some message data.
- This function is used to write some or all of a message's
- payload to the stream. The call will block until one of the
- following conditions is true:
+ This function is used to send part of a message.
- @li A frame is sent.
+ The call blocks until one of the following is true:
- @li Message data is transferred to the write buffer.
+ @li The message data is written.
@li An error occurs.
- This operation is implemented in terms of one or more calls
- to the stream's `write_some` function.
+ The algorithm, known as a <em>composed operation</em>, is implemented
+ in terms of calls to the next layer's `write_some` function.
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
- the @ref binary option. The actual payload sent may be
- transformed as per the WebSocket protocol settings.
+ will be set to text or binary based on the current setting of
+ the @ref binary (or @ref text) option. The actual payload sent
+ may be transformed as per the WebSocket protocol settings.
@param fin `true` if this is the last part of the message.
- @param buffers The input buffer sequence holding the data to write.
+ @param buffers The buffers containing the message part to send.
@param ec Set to indicate what error occurred, if any.
- @return The number of bytes written from the buffers.
- If an error occurred, this will be less than the sum
- of the buffer sizes.
+ @return The number of bytes sent from the buffers.
@return The number of bytes consumed in the input buffers.
*/
@@ -3310,258 +2410,220 @@ public:
write_some(bool fin,
ConstBufferSequence const& buffers, error_code& ec);
- /** Start an asynchronous operation to send a message frame on the stream.
+ /** Write some message data asynchronously.
+
+ This function is used to asynchronously write part of a message.
- This function is used to asynchronously write a message frame
- on the stream. This function call always returns immediately.
- The asynchronous operation will continue until one of the following
- conditions is true:
+ This call always returns immediately. The asynchronous operation
+ will continue until one of the following conditions is true:
- @li The entire frame is sent.
+ @li The message data is written.
@li An error occurs.
- This operation is implemented in terms of one or more calls
- to the next layer's `async_write_some` functions, and is known
- 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 @ref async_write, @ref async_write_some,
- or @ref async_close).
+ The algorithm, known as a <em>composed asynchronous operation</em>,
+ is implemented in terms of calls to the next layer's
+ `async_write_some` function. The program must ensure that no other
+ calls to @ref write, @ref write_some, @ref async_write, or
+ @ref async_write_some are performed until this operation completes.
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
- the @ref binary option. The actual payload sent may be
- transformed as per the WebSocket protocol settings.
+ will be set to text or binary based on the current setting of
+ the @ref binary (or @ref text) option. The actual payload sent
+ may be transformed as per the WebSocket protocol settings.
@param fin `true` if this is the last part of the message.
- @param buffers A object meeting the requirements of
- ConstBufferSequence which holds the payload data before any
- masking or compression. Although the buffers object may be copied
- as necessary, ownership of the underlying buffers is retained by
- the caller, which must guarantee that they remain valid until
- the handler is called.
-
- @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(
+ @param buffers The buffers containing the message part to send.
+ The implementation will make copies of this object
+ as needed, but ownership of the underlying memory is not
+ transferred. The caller is responsible for ensuring that
+ the memory locations pointed to by buffers remains valid
+ until the completion handler is called.
+
+ @param handler The completion handler to invoke when the operation
+ completes. The implementation takes ownership of the handler by
+ performing a decay-copy. 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
+ std::size_t bytes_transferred // Number of bytes sent from the
// buffers. If an error occurred,
- // this will be less than the sum
- // of the buffer sizes.
- ); @endcode
+ // this will be less than the buffer_size.
+ );
+ @endcode
+ Regardless of whether the asynchronous operation completes
+ immediately or not, the handler will not be invoked from within
+ this function. Invocation of the handler will be performed in a
+ manner equivalent to using `net::post`.
*/
template<class ConstBufferSequence, class WriteHandler>
- BOOST_ASIO_INITFN_RESULT_TYPE(
- WriteHandler, void(error_code, std::size_t))
+ BOOST_BEAST_ASYNC_RESULT2(WriteHandler)
async_write_some(bool fin,
ConstBufferSequence const& buffers, WriteHandler&& handler);
-private:
- template<class, class> class accept_op;
- template<class> class close_op;
- template<class> class handshake_op;
- template<class> class ping_op;
- template<class, class> class read_some_op;
- template<class, class> class read_op;
- template<class> class response_op;
- template<class, class> class write_some_op;
- template<class, class> class write_op;
-
- static void default_decorate_req(request_type&) {}
- static void default_decorate_res(response_type&) {}
+ //
+ // Deprecated
+ //
+#if ! BOOST_BEAST_DOXYGEN
+ template<class RequestDecorator>
void
- set_option(permessage_deflate const& o, std::true_type);
+ handshake_ex(
+ string_view host,
+ string_view target,
+ RequestDecorator const& decorator);
+ template<class RequestDecorator>
void
- set_option(permessage_deflate const&, std::false_type);
+ handshake_ex(
+ response_type& res,
+ string_view host,
+ string_view target,
+ RequestDecorator const& decorator);
+ template<class RequestDecorator>
void
- get_option(permessage_deflate& o, std::true_type)
- {
- o = this->pmd_opts_;
- }
+ handshake_ex(
+ string_view host,
+ string_view target,
+ RequestDecorator const& decorator,
+ error_code& ec);
+ template<class RequestDecorator>
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()
- {
- 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)
- {
- if(status_ != status::open)
- {
- ec = boost::asio::error::operation_aborted;
- return false;
- }
- ec.assign(0, ec.category());
- return true;
- }
+ handshake_ex(
+ response_type& res,
+ string_view host,
+ string_view target,
+ RequestDecorator const& decorator,
+ error_code& ec);
- bool
- check_ok(error_code& ec)
- {
- if(ec)
- {
- if(status_ != status::closed)
- status_ = status::failed;
- return false;
- }
- return true;
- }
+ template<class RequestDecorator, class HandshakeHandler>
+ BOOST_BEAST_ASYNC_RESULT1(HandshakeHandler)
+ async_handshake_ex(
+ string_view host,
+ string_view target,
+ RequestDecorator const& decorator,
+ HandshakeHandler&& handler);
- template<class DynamicBuffer>
- bool
- parse_fh(
- detail::frame_header& fh,
- DynamicBuffer& b,
- error_code& ec);
+ template<class RequestDecorator, class HandshakeHandler>
+ BOOST_BEAST_ASYNC_RESULT1(HandshakeHandler)
+ async_handshake_ex(
+ response_type& res,
+ string_view host,
+ string_view target,
+ RequestDecorator const& decorator,
+ HandshakeHandler&& handler);
- template<class DynamicBuffer>
+ template<class ResponseDecorator>
void
- write_close(DynamicBuffer& b, close_reason const& rc);
+ accept_ex(ResponseDecorator const& decorator);
- template<class DynamicBuffer>
+ template<class ResponseDecorator>
void
- write_ping(DynamicBuffer& b,
- detail::opcode op, ping_data const& data);
+ accept_ex(
+ ResponseDecorator const& decorator,
+ error_code& ec);
- //
- // upgrade
- //
+ template<class ConstBufferSequence,
+ class ResponseDecorator>
+ typename std::enable_if<! http::detail::is_header<
+ ConstBufferSequence>::value>::type
+ accept_ex(
+ ConstBufferSequence const& buffers,
+ ResponseDecorator const& decorator);
- template<class Decorator>
- request_type
- build_request(detail::sec_ws_key_type& key,
- string_view host,
- string_view target,
- Decorator const& decorator);
+ template<class ConstBufferSequence, class ResponseDecorator>
+ typename std::enable_if<! http::detail::is_header<
+ ConstBufferSequence>::value>::type
+ accept_ex(
+ ConstBufferSequence const& buffers,
+ ResponseDecorator const& decorator,
+ error_code& ec);
+ template<class Body, class Allocator,
+ class ResponseDecorator>
void
- build_request_pmd(request_type& req, std::true_type);
+ accept_ex(http::request<Body,
+ http::basic_fields<Allocator>> const& req,
+ ResponseDecorator const& decorator);
+ template<class Body, class Allocator,
+ class ResponseDecorator>
void
- build_request_pmd(request_type&, std::false_type)
- {
- }
+ accept_ex(http::request<Body,
+ http::basic_fields<Allocator>> const& req,
+ ResponseDecorator const& decorator,
+ error_code& ec);
template<
- class Body, class Allocator, class Decorator>
- response_type
- build_response(
- http::request<Body,
- http::basic_fields<Allocator>> const& req,
- Decorator const& decorator,
- error_code& ec);
+ class ResponseDecorator,
+ class AcceptHandler>
+ BOOST_BEAST_ASYNC_RESULT1(AcceptHandler)
+ async_accept_ex(
+ ResponseDecorator const& decorator,
+ AcceptHandler&& handler);
- template<class Body, class Allocator>
- void
- build_response_pmd(
- response_type& res,
- http::request<Body,
- http::basic_fields<Allocator>> const& req,
- std::true_type);
+ template<
+ class ConstBufferSequence,
+ class ResponseDecorator,
+ class AcceptHandler>
+ typename std::enable_if<
+ ! http::detail::is_header<ConstBufferSequence>::value,
+ BOOST_BEAST_ASYNC_RESULT1(AcceptHandler)>::type
+ async_accept_ex(
+ ConstBufferSequence const& buffers,
+ ResponseDecorator const& decorator,
+ AcceptHandler&& handler);
- template<class Body, class Allocator>
- void
- build_response_pmd(
- response_type&,
+ template<
+ class Body, class Allocator,
+ class ResponseDecorator,
+ class AcceptHandler>
+ BOOST_BEAST_ASYNC_RESULT1(AcceptHandler)
+ async_accept_ex(
http::request<Body,
- http::basic_fields<Allocator>> const&,
- std::false_type)
- {
- }
+ http::basic_fields<Allocator>> const& req,
+ ResponseDecorator const& decorator,
+ AcceptHandler&& handler);
+#endif
- void
- on_response(
- response_type const& res,
- detail::sec_ws_key_type const& key,
- error_code& ec);
+private:
+ template<class, class> class accept_op;
+ template<class> class close_op;
+ template<class> class handshake_op;
+ template<class> class ping_op;
+ template<class> class idle_ping_op;
+ template<class, class> class read_some_op;
+ template<class, class> class read_op;
+ template<class> class response_op;
+ template<class, class> class write_some_op;
+ template<class, class> class write_op;
- void
- on_response_pmd(
- response_type const& res,
- std::true_type);
+ struct run_accept_op;
+ struct run_close_op;
+ struct run_handshake_op;
+ struct run_ping_op;
+ struct run_idle_ping_op;
+ struct run_read_some_op;
+ struct run_read_op;
+ struct run_response_op;
+ struct run_write_some_op;
+ struct run_write_op;
- void
- on_response_pmd(
- response_type const&,
- std::false_type)
- {
- }
+ static void default_decorate_req(request_type&) {}
+ static void default_decorate_res(response_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>
+ template<class Buffers, class Decorator>
void
do_accept(
+ Buffers const& buffers,
Decorator const& decorator,
error_code& ec);
@@ -3620,19 +2682,20 @@ inline
void
seed_prng(std::seed_seq& ss)
{
- detail::stream_prng::seed(&ss);
+ detail::prng_seed(&ss);
}
} // websocket
} // beast
} // boost
-#include <boost/beast/websocket/impl/accept.ipp>
-#include <boost/beast/websocket/impl/close.ipp>
-#include <boost/beast/websocket/impl/handshake.ipp>
-#include <boost/beast/websocket/impl/ping.ipp>
-#include <boost/beast/websocket/impl/read.ipp>
-#include <boost/beast/websocket/impl/stream.ipp>
-#include <boost/beast/websocket/impl/write.ipp>
+#include <boost/beast/websocket/impl/stream_impl.hpp> // must be first
+#include <boost/beast/websocket/impl/accept.hpp>
+#include <boost/beast/websocket/impl/close.hpp>
+#include <boost/beast/websocket/impl/handshake.hpp>
+#include <boost/beast/websocket/impl/ping.hpp>
+#include <boost/beast/websocket/impl/read.hpp>
+#include <boost/beast/websocket/impl/stream.hpp>
+#include <boost/beast/websocket/impl/write.hpp>
#endif
diff --git a/boost/beast/websocket/stream_base.hpp b/boost/beast/websocket/stream_base.hpp
new file mode 100644
index 0000000000..a214553f2e
--- /dev/null
+++ b/boost/beast/websocket/stream_base.hpp
@@ -0,0 +1,178 @@
+//
+// Copyright (c) 2016-2019 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/core/detail/config.hpp>
+#include <boost/beast/websocket/detail/decorator.hpp>
+#include <boost/beast/core/role.hpp>
+#include <chrono>
+#include <type_traits>
+
+namespace boost {
+namespace beast {
+namespace websocket {
+
+/** This class is used as a base for the @ref websocket::stream class template to group common types and constants.
+*/
+struct stream_base
+{
+ /// The type used to represent durations
+ using duration =
+ std::chrono::steady_clock::duration;
+
+ /// The type used to represent time points
+ using time_point =
+ std::chrono::steady_clock::time_point;
+
+ /// Returns the special time_point value meaning "never"
+ static
+ time_point
+ never() noexcept
+ {
+ return (time_point::max)();
+ }
+
+ /// Returns the special duration value meaning "none"
+ static
+ duration
+ none() noexcept
+ {
+ return (duration::max)();
+ }
+
+ /** Stream option used to adjust HTTP fields of WebSocket upgrade request and responses.
+ */
+ class decorator
+ {
+ detail::decorator d_;
+
+ template<class, bool>
+ friend class stream;
+
+ public:
+ // Move Constructor
+ decorator(decorator&&) = default;
+
+ /** Construct a decorator option.
+
+ @param f An invocable function object. Ownership of
+ the function object is transferred by decay-copy.
+ */
+ template<class Decorator
+#ifndef BOOST_BEAST_DOXYGEN
+ ,class = typename std::enable_if<
+ detail::is_decorator<
+ Decorator>::value>::type
+#endif
+ >
+ explicit
+ decorator(Decorator&& f)
+ : d_(std::forward<Decorator>(f))
+ {
+ }
+ };
+
+ /** Stream option to control the behavior of websocket timeouts.
+
+ Timeout features are available for asynchronous operations only.
+ */
+ struct timeout
+ {
+ /** Time limit on handshake, accept, and close operations:
+
+ This value whether or not there is a time limit, and the
+ duration of that time limit, for asynchronous handshake,
+ accept, and close operations. If this is equal to the
+ value @ref none then there will be no time limit. Otherwise,
+ if any of the applicable operations takes longer than this
+ amount of time, the operation will be canceled and a
+ timeout error delivered to the completion handler.
+ */
+ duration handshake_timeout;
+
+ /** The time limit after which a connection is considered idle.
+ */
+ duration idle_timeout;
+
+ /** Automatic ping setting.
+
+ If the idle interval is set, this setting affects the
+ behavior of the stream when no data is received for the
+ timeout interval as follows:
+
+ @li When `keep_alive_pings` is `true`, an idle ping will be
+ sent automatically. If another timeout interval elapses
+ with no received data then the connection will be closed.
+ An outstanding read operation must be pending, which will
+ complete immediately the error @ref beast::error::timeout.
+
+ @li When `keep_alive_pings` is `false`, the connection will be closed.
+ An outstanding read operation must be pending, which will
+ complete immediately the error @ref beast::error::timeout.
+ */
+ bool keep_alive_pings;
+
+ /** Construct timeout settings with suggested values for a role.
+
+ This constructs the timeout settings with a predefined set
+ of values which varies depending on the desired role. The
+ values are selected upon construction, regardless of the
+ current or actual role in use on the stream.
+
+ @par Example
+ This statement sets the timeout settings of the stream to
+ the suggested values for the server role:
+ @code
+ @endcode
+
+ @param role The role of the websocket stream
+ (@ref role_type::client or @ref role_type::server).
+ */
+ static
+ timeout
+ suggested(role_type role) noexcept
+ {
+ timeout opt{};
+ switch(role)
+ {
+ case role_type::client:
+ opt.handshake_timeout = std::chrono::seconds(30);
+ opt.idle_timeout = none();
+ opt.keep_alive_pings = false;
+ break;
+
+ case role_type::server:
+ opt.handshake_timeout = std::chrono::seconds(30);
+ opt.idle_timeout = std::chrono::seconds(300);
+ opt.keep_alive_pings = true;
+ break;
+ }
+ return opt;
+ }
+ };
+
+protected:
+ enum class status
+ {
+ //none,
+ handshake,
+ open,
+ closing,
+ closed,
+ failed // VFALCO Is this needed?
+ };
+};
+
+} // websocket
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/websocket/stream_fwd.hpp b/boost/beast/websocket/stream_fwd.hpp
index 97ffcb49ed..6d17636399 100644
--- a/boost/beast/websocket/stream_fwd.hpp
+++ b/boost/beast/websocket/stream_fwd.hpp
@@ -1,5 +1,5 @@
//
-// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+// Copyright (c) 2016-2019 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)
@@ -12,6 +12,8 @@
#include <boost/beast/core/detail/config.hpp>
+//[code_websocket_1h
+
namespace boost {
namespace beast {
namespace websocket {
@@ -25,4 +27,6 @@ class stream;
} // beast
} // boost
+//]
+
#endif
diff --git a/boost/beast/websocket/teardown.hpp b/boost/beast/websocket/teardown.hpp
index a255756dad..257cb1cbda 100644
--- a/boost/beast/websocket/teardown.hpp
+++ b/boost/beast/websocket/teardown.hpp
@@ -1,5 +1,5 @@
//
-// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+// Copyright (c) 2016-2019 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)
@@ -12,8 +12,8 @@
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/error.hpp>
-#include <boost/beast/websocket/role.hpp>
-#include <boost/asio/ip/tcp.hpp>
+#include <boost/beast/core/role.hpp>
+#include <boost/asio/basic_stream_socket.hpp>
#include <type_traits>
namespace boost {
@@ -25,8 +25,8 @@ namespace websocket {
This tears down a connection. The implementation will call
the overload of this function based on the `Socket` parameter
used to consruct the socket. When `Socket` is a user defined
- type, and not a `boost::asio::ip::tcp::socket` or any
- `boost::asio::ssl::stream`, callers are responsible for
+ type, and not a `net::ip::tcp::socket` or any
+ `net::ssl::stream`, callers are responsible for
providing a suitable overload of this function.
@param role The role of the local endpoint
@@ -61,7 +61,7 @@ teardown(
The implementation will call the overload of this function
based on the `Socket` parameter used to consruct the socket.
When `Stream` is a user defined type, and not a
- `boost::asio::ip::tcp::socket` or any `boost::asio::ssl::stream`,
+ `net::ip::tcp::socket` or any `net::ssl::stream`,
callers are responsible for providing a suitable overload
of this function.
@@ -69,17 +69,19 @@ teardown(
@param socket The socket to tear down.
- @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(
+ @param handler The completion handler to invoke when the operation
+ completes. The implementation takes ownership of the handler by
+ performing a decay-copy. The equivalent function signature of
+ the handler must be:
+ @code
+ void handler(
error_code const& error // result of operation
);
@endcode
Regardless of whether the asynchronous operation completes
immediately or not, the handler will not be invoked from within
this function. Invocation of the handler will be performed in a
- manner equivalent to using boost::asio::io_context::post().
+ manner equivalent to using `net::post`.
*/
template<
@@ -110,13 +112,13 @@ async_teardown(
namespace websocket {
-/** Tear down a `boost::asio::ip::tcp::socket`.
+/** Tear down a `net::ip::tcp::socket`.
This tears down a connection. The implementation will call
the overload of this function based on the `Stream` parameter
used to consruct the socket. When `Stream` is a user defined
- type, and not a `boost::asio::ip::tcp::socket` or any
- `boost::asio::ssl::stream`, callers are responsible for
+ type, and not a `net::ip::tcp::socket` or any
+ `net::ssl::stream`, callers are responsible for
providing a suitable overload of this function.
@param role The role of the local endpoint
@@ -125,19 +127,21 @@ namespace websocket {
@param ec Set to the error if any occurred.
*/
+template<class Protocol, class Executor>
void
teardown(
role_type role,
- boost::asio::ip::tcp::socket& socket,
+ net::basic_stream_socket<
+ Protocol, Executor>& socket,
error_code& ec);
-/** Start tearing down a `boost::asio::ip::tcp::socket`.
+/** Start tearing down a `net::ip::tcp::socket`.
This begins tearing down a connection asynchronously.
The implementation will call the overload of this function
based on the `Stream` parameter used to consruct the socket.
When `Stream` is a user defined type, and not a
- `boost::asio::ip::tcp::socket` or any `boost::asio::ssl::stream`,
+ `net::ip::tcp::socket` or any `net::ssl::stream`,
callers are responsible for providing a suitable overload
of this function.
@@ -145,30 +149,35 @@ teardown(
@param socket The socket to tear down.
- @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(
+ @param handler The completion handler to invoke when the operation
+ completes. The implementation takes ownership of the handler by
+ performing a decay-copy. The equivalent function signature of
+ the handler must be:
+ @code
+ void handler(
error_code const& error // result of operation
);
- @endcode
+ @endcode
Regardless of whether the asynchronous operation completes
immediately or not, the handler will not be invoked from within
this function. Invocation of the handler will be performed in a
- manner equivalent to using boost::asio::io_context::post().
+ manner equivalent to using `net::post`.
*/
-template<class TeardownHandler>
+template<
+ class Protocol, class Executor,
+ class TeardownHandler>
void
async_teardown(
role_type role,
- boost::asio::ip::tcp::socket& socket,
+ net::basic_stream_socket<
+ Protocol, Executor>& socket,
TeardownHandler&& handler);
} // websocket
} // beast
} // boost
-#include <boost/beast/websocket/impl/teardown.ipp>
+#include <boost/beast/websocket/impl/teardown.hpp>
#endif