feat():initial version

This commit is contained in:
2026-06-09 20:16:47 +08:00
commit 85fbb3188c
14809 changed files with 3044607 additions and 0 deletions

View File

@@ -0,0 +1,314 @@
//
// 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/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 = *beast::detail::launder_cast<F*>(&src.buf_);
::new (&dst.buf_) F(std::move(f));
}
static
void
destroy(storage& dst) noexcept
{
beast::detail::launder_cast<F*>(&dst.buf_)->~F();
}
static
void
invoke_req(storage& dst, request_type& req)
{
maybe_invoke<F, request_type>{}(
*beast::detail::launder_cast<F*>(&dst.buf_), req);
}
static
void
invoke_res(storage& dst, response_type& res)
{
maybe_invoke<F, response_type>{}(
*beast::detail::launder_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

View File

@@ -0,0 +1,247 @@
//
// 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_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>
#include <boost/beast/core/flat_static_buffer.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/assert.hpp>
#include <boost/endian/conversion.hpp>
#include <cstdint>
namespace boost {
namespace beast {
namespace websocket {
namespace detail {
// frame header opcodes
enum class opcode : std::uint8_t
{
cont = 0,
text = 1,
binary = 2,
rsv3 = 3,
rsv4 = 4,
rsv5 = 5,
rsv6 = 6,
rsv7 = 7,
close = 8,
ping = 9,
pong = 10,
crsvb = 11,
crsvc = 12,
crsvd = 13,
crsve = 14,
crsvf = 15
};
// Contents of a WebSocket frame header
struct frame_header
{
std::uint64_t len;
std::uint32_t key;
opcode op;
bool fin : 1;
bool mask : 1;
bool rsv1 : 1;
bool rsv2 : 1;
bool rsv3 : 1;
};
// holds the largest possible frame header
using fh_buffer = flat_static_buffer<14>;
// holds the largest possible control frame
using frame_buffer =
flat_static_buffer< 2 + 8 + 4 + 125 >;
inline
bool constexpr
is_reserved(opcode op)
{
return
(op >= opcode::rsv3 && op <= opcode::rsv7) ||
(op >= opcode::crsvb && op <= opcode::crsvf);
}
inline
bool constexpr
is_valid(opcode op)
{
return op <= opcode::crsvf;
}
inline
bool constexpr
is_control(opcode op)
{
return op >= opcode::close;
}
inline
bool
is_valid_close_code(std::uint16_t v)
{
switch(v)
{
case close_code::normal: // 1000
case close_code::going_away: // 1001
case close_code::protocol_error: // 1002
case close_code::unknown_data: // 1003
case close_code::bad_payload: // 1007
case close_code::policy_error: // 1008
case close_code::too_big: // 1009
case close_code::needs_extension: // 1010
case close_code::internal_error: // 1011
case close_code::service_restart: // 1012
case close_code::try_again_later: // 1013
return true;
// explicitly reserved
case close_code::reserved1: // 1004
case close_code::no_status: // 1005
case close_code::abnormal: // 1006
case close_code::reserved2: // 1014
case close_code::reserved3: // 1015
return false;
}
// reserved
if(v >= 1016 && v <= 2999)
return false;
// not used
if(v <= 999)
return false;
return true;
}
//------------------------------------------------------------------------------
// Write frame header to dynamic buffer
//
template<class DynamicBuffer>
void
write(DynamicBuffer& db, frame_header const& fh)
{
std::size_t n;
std::uint8_t b[14];
b[0] = (fh.fin ? 0x80 : 0x00) | static_cast<std::uint8_t>(fh.op);
if(fh.rsv1)
b[0] |= 0x40;
if(fh.rsv2)
b[0] |= 0x20;
if(fh.rsv3)
b[0] |= 0x10;
b[1] = fh.mask ? 0x80 : 0x00;
if(fh.len <= 125)
{
b[1] |= fh.len;
n = 2;
}
else if(fh.len <= 65535)
{
b[1] |= 126;
auto len_be = endian::native_to_big(
static_cast<std::uint16_t>(fh.len));
std::memcpy(&b[2], &len_be, sizeof(len_be));
n = 4;
}
else
{
b[1] |= 127;
auto len_be = endian::native_to_big(
static_cast<std::uint64_t>(fh.len));
std::memcpy(&b[2], &len_be, sizeof(len_be));
n = 10;
}
if(fh.mask)
{
auto key_le = endian::native_to_little(
static_cast<std::uint32_t>(fh.key));
std::memcpy(&b[n], &key_le, sizeof(key_le));
n += 4;
}
db.commit(net::buffer_copy(
db.prepare(n), net::buffer(b)));
}
// Read data from buffers
// This is for ping and pong payloads
//
template<class Buffers>
void
read_ping(ping_data& data, Buffers const& bs)
{
BOOST_ASSERT(buffer_bytes(bs) <= data.max_size());
data.resize(buffer_bytes(bs));
net::buffer_copy(net::mutable_buffer{
data.data(), data.size()}, bs);
}
// Read close_reason, return true on success
// This is for the close payload
//
template<class Buffers>
void
read_close(
close_reason& cr,
Buffers const& bs,
error_code& ec)
{
auto const n = buffer_bytes(bs);
BOOST_ASSERT(n <= 125);
if(n == 0)
{
cr = close_reason{};
ec = {};
return;
}
if(n == 1)
{
// invalid payload size == 1
ec = error::bad_close_size;
return;
}
std::uint16_t code_be;
cr.reason.resize(n - 2);
std::array<net::mutable_buffer, 2> out_bufs{{
net::mutable_buffer(&code_be, sizeof(code_be)),
net::mutable_buffer(&cr.reason[0], n - 2)}};
net::buffer_copy(out_bufs, bs);
cr.code = endian::big_to_native(code_be);
if(! is_valid_close_code(cr.code))
{
// invalid close code
ec = error::bad_close_code;
return;
}
if(n > 2 && !check_utf8(
cr.reason.data(), cr.reason.size()))
{
// not valid utf-8
ec = error::bad_close_payload;
return;
}
ec = {};
}
} // detail
} // websocket
} // beast
} // boost
#endif

View File

@@ -0,0 +1,48 @@
//
// 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_HPP
#define BOOST_BEAST_WEBSOCKET_DETAIL_HYBI13_HPP
#include <boost/beast/core/static_string.hpp>
#include <boost/beast/core/string.hpp>
#include <boost/beast/core/detail/base64.hpp>
#include <cstdint>
namespace boost {
namespace beast {
namespace websocket {
namespace detail {
using sec_ws_key_type = static_string<
beast::detail::base64::encoded_size(16)>;
using sec_ws_accept_type = static_string<
beast::detail::base64::encoded_size(20)>;
BOOST_BEAST_DECL
void
make_sec_ws_key(sec_ws_key_type& key);
BOOST_BEAST_DECL
void
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

View File

@@ -0,0 +1,62 @@
//
// 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);
std::uint32_t a[4];
for (auto& v : a)
v = g();
key.resize(key.max_size());
key.resize(beast::detail::base64::encode(
key.data(), &a[0], sizeof(a)));
}
void
make_sec_ws_accept(
sec_ws_accept_type& accept,
string_view key)
{
BOOST_ASSERT(key.size() <= sec_ws_key_type::max_size_n);
using namespace beast::detail::string_literals;
auto const guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"_sv;
beast::detail::sha1_context ctx;
beast::detail::init(ctx);
beast::detail::update(ctx, key.data(), key.size());
beast::detail::update(ctx, guid.data(), guid.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

View File

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

View File

@@ -0,0 +1,63 @@
//
// 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_MASK_HPP
#define BOOST_BEAST_WEBSOCKET_DETAIL_MASK_HPP
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/buffers_range.hpp>
#include <boost/asio/buffer.hpp>
#include <array>
#include <climits>
#include <cstdint>
#include <random>
#include <type_traits>
namespace boost {
namespace beast {
namespace websocket {
namespace detail {
using prepared_key = std::array<unsigned char, 4>;
BOOST_BEAST_DECL
void
prepare_key(prepared_key& prepared, std::uint32_t key);
// Apply mask in place
//
BOOST_BEAST_DECL
void
mask_inplace(net::mutable_buffer const& b, prepared_key& key);
// Apply mask in place
//
template<class MutableBufferSequence>
void
mask_inplace(
MutableBufferSequence const& buffers,
prepared_key& key)
{
for(net::mutable_buffer b :
beast::buffers_range_ref(buffers))
detail::mask_inplace(b, key);
}
} // detail
} // websocket
} // beast
} // boost
#if BOOST_BEAST_HEADER_ONLY
#include <boost/beast/websocket/detail/mask.ipp>
#endif
#endif

View File

@@ -0,0 +1,66 @@
//
// 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_MASK_IPP
#define BOOST_BEAST_WEBSOCKET_DETAIL_MASK_IPP
#include <boost/beast/websocket/detail/mask.hpp>
namespace boost {
namespace beast {
namespace websocket {
namespace detail {
void
prepare_key(prepared_key& prepared, std::uint32_t key)
{
prepared[0] = (key >> 0) & 0xff;
prepared[1] = (key >> 8) & 0xff;
prepared[2] = (key >> 16) & 0xff;
prepared[3] = (key >> 24) & 0xff;
}
inline
void
rol(prepared_key& v, std::size_t n)
{
auto v0 = v;
for(std::size_t i = 0; i < v.size(); ++i )
v[i] = v0[(i + n) % v.size()];
}
// Apply mask in place
//
void
mask_inplace(net::mutable_buffer const& b, prepared_key& key)
{
auto n = b.size();
auto const mask = key; // avoid aliasing
auto p = static_cast<unsigned char*>(b.data());
while(n >= 4)
{
for(int i = 0; i < 4; ++i)
p[i] ^= mask[i];
p += 4;
n -= 4;
}
if(n > 0)
{
for(std::size_t i = 0; i < n; ++i)
p[i] ^= mask[i];
rol(key, n);
}
}
} // detail
} // websocket
} // beast
} // boost
#endif

View File

@@ -0,0 +1,125 @@
//
// 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_HPP
#define BOOST_BEAST_WEBSOCKET_DETAIL_PMD_EXTENSION_HPP
#include <boost/beast/core/error.hpp>
#include <boost/beast/websocket/option.hpp>
#include <boost/beast/http/rfc7230.hpp>
#include <utility>
#include <type_traits>
namespace boost {
namespace beast {
namespace websocket {
namespace detail {
// permessage-deflate offer parameters
//
// "context takeover" means:
// preserve sliding window across messages
//
struct pmd_offer
{
bool accept;
// 0 = absent, or 8..15
int server_max_window_bits;
// -1 = present, 0 = absent, or 8..15
int client_max_window_bits;
// `true` if server_no_context_takeover offered
bool server_no_context_takeover;
// `true` if client_no_context_takeover offered
bool client_no_context_takeover;
};
BOOST_BEAST_DECL
int
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
//
template<class Allocator>
void
pmd_read(pmd_offer& offer,
http::basic_fields<Allocator> const& fields)
{
http::ext_list list{
fields["Sec-WebSocket-Extensions"]};
detail::pmd_read_impl(offer, list);
}
// Set permessage-deflate fields for a client offer
//
template<class Allocator>
void
pmd_write(http::basic_fields<Allocator>& fields,
pmd_offer const& offer)
{
auto s = detail::pmd_write_impl(offer);
fields.set(http::field::sec_websocket_extensions, s);
}
// Negotiate a permessage-deflate client offer
//
template<class Allocator>
void
pmd_negotiate(
http::basic_fields<Allocator>& fields,
pmd_offer& config,
pmd_offer const& offer,
permessage_deflate const& o)
{
if(! (offer.accept && o.server_enable))
{
config.accept = false;
return;
}
config.accept = true;
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
//
BOOST_BEAST_DECL
void
pmd_normalize(pmd_offer& offer);
} // detail
} // websocket
} // beast
} // boost
#if BOOST_BEAST_HEADER_ONLY
#include <boost/beast/websocket/detail/pmd_extension.ipp>
#endif
#endif

View File

@@ -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(beast::iequals(ext.first, "permessage-deflate"))
{
for(auto const& param : ext.second)
{
if(beast::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(beast::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(beast::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(beast::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

View File

@@ -0,0 +1,50 @@
//
// 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 <boost/config.hpp>
#include <cstdint>
#include <random>
namespace boost {
namespace beast {
namespace websocket {
namespace detail {
using generator = std::uint32_t(*)();
//------------------------------------------------------------------------------
// 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 TLS implementation if it
// is available, otherwise using the no-TLS implementation.
//
BOOST_BEAST_DECL
generator
make_prng(bool secure);
} // detail
} // websocket
} // beast
} // boost
#ifdef BOOST_BEAST_HEADER_ONLY
#include <boost/beast/websocket/detail/prng.ipp>
#endif
#endif

View File

@@ -0,0 +1,151 @@
//
// 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 <atomic>
#include <cstdlib>
#include <mutex>
#include <random>
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;
}
//------------------------------------------------------------------------------
inline
std::uint32_t
make_nonce()
{
static std::atomic<std::uint32_t> nonce{0};
return ++nonce;
}
inline
beast::detail::pcg make_pcg()
{
auto const pv = prng_seed();
return beast::detail::pcg{
((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]), make_nonce()};
}
#ifdef BOOST_NO_CXX11_THREAD_LOCAL
inline
std::uint32_t
secure_generate()
{
struct generator
{
std::uint32_t operator()()
{
std::lock_guard<std::mutex> guard{mtx};
return gen();
}
beast::detail::chacha<20> gen;
std::mutex mtx;
};
static generator gen{beast::detail::chacha<20>{prng_seed(), make_nonce()}};
return gen();
}
inline
std::uint32_t
fast_generate()
{
struct generator
{
std::uint32_t operator()()
{
std::lock_guard<std::mutex> guard{mtx};
return gen();
}
beast::detail::pcg gen;
std::mutex mtx;
};
static generator gen{make_pcg()};
return gen();
}
#else
inline
std::uint32_t
secure_generate()
{
thread_local static beast::detail::chacha<20> gen{prng_seed(), make_nonce()};
return gen();
}
inline
std::uint32_t
fast_generate()
{
thread_local static beast::detail::pcg gen{make_pcg()};
return gen();
}
#endif
generator
make_prng(bool secure)
{
if (secure)
return &secure_generate;
else
return &fast_generate;
}
} // detail
} // websocket
} // beast
} // boost
#endif

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,36 @@
//
// 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_TYPE_TRAITS_HPP
#define BOOST_BEAST_WEBSOCKET_DETAIL_TYPE_TRAITS_HPP
#include <boost/beast/websocket/rfc6455.hpp>
#include <boost/beast/core/detail/is_invocable.hpp>
namespace boost {
namespace beast {
namespace websocket {
namespace detail {
template<class F>
using is_request_decorator =
typename beast::detail::is_invocable<F,
void(request_type&)>::type;
template<class F>
using is_response_decorator =
typename beast::detail::is_invocable<F,
void(response_type&)>::type;
} // detail
} // websocket
} // beast
} // boost
#endif

View File

@@ -0,0 +1,96 @@
//
// 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_HPP
#define BOOST_BEAST_WEBSOCKET_DETAIL_UTF8_CHECKER_HPP
#include <boost/beast/core/buffers_range.hpp>
#include <boost/asio/buffer.hpp>
#include <cstdint>
namespace boost {
namespace beast {
namespace websocket {
namespace detail {
/** A UTF8 validator.
This validator can be used to check if a buffer containing UTF8 text is
valid. The write function may be called incrementally with segmented UTF8
sequences. The finish function determines if all processed text is valid.
*/
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
std::uint8_t cp_[4]; // a temp buffer for the code point
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();
/** Check if text is valid UTF8
@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);
/** Check if text is valid UTF8
@return `true` if the text is valid utf8 or false otherwise.
*/
template<class ConstBufferSequence>
bool
write(ConstBufferSequence const& bs);
};
template<class ConstBufferSequence>
bool
utf8_checker::
write(ConstBufferSequence const& buffers)
{
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()))
return false;
return true;
}
BOOST_BEAST_DECL
bool
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

View File

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

View File

@@ -0,0 +1,256 @@
//
// 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_ERROR_HPP
#define BOOST_BEAST_WEBSOCKET_ERROR_HPP
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/error.hpp>
namespace boost {
namespace beast {
namespace websocket {
/// Error codes returned from @ref beast::websocket::stream operations.
enum class error
{
/** The WebSocket stream was gracefully closed at both endpoints
*/
closed = 1,
/* The error codes error::failed and error::handshake_failed
are no longer in use. Please change your code to compare values
of type error_code against condition::handshake_failed
and condition::protocol_violation instead.
Apologies for the inconvenience.
- VFALCO
*/
#if ! BOOST_BEAST_DOXYGEN
unused1 = 2, // failed
unused2 = 3, // handshake_failed
#endif
/** The WebSocket operation caused a dynamic buffer overflow
*/
buffer_overflow,
/** The WebSocket stream produced an incomplete deflate block
*/
partial_deflate_block,
/** The WebSocket message exceeded the locally configured limit
*/
message_too_big,
//
// Handshake failure errors
//
// These will compare equal to condition::handshake_failed
//
/** The WebSocket handshake was not HTTP/1.1
Error codes with this value will compare equal to @ref condition::handshake_failed
*/
bad_http_version,
/** The WebSocket handshake method was not GET
Error codes with this value will compare equal to @ref condition::handshake_failed
*/
bad_method,
/** The WebSocket handshake Host field is missing
Error codes with this value will compare equal to @ref condition::handshake_failed
*/
no_host,
/** The WebSocket handshake Connection field is missing
Error codes with this value will compare equal to @ref condition::handshake_failed
*/
no_connection,
/** The WebSocket handshake Connection field is missing the upgrade token
Error codes with this value will compare equal to @ref condition::handshake_failed
*/
no_connection_upgrade,
/** The WebSocket handshake Upgrade field is missing
Error codes with this value will compare equal to @ref condition::handshake_failed
*/
no_upgrade,
/** The WebSocket handshake Upgrade field is missing the websocket token
Error codes with this value will compare equal to @ref condition::handshake_failed
*/
no_upgrade_websocket,
/** The WebSocket handshake Sec-WebSocket-Key field is missing
Error codes with this value will compare equal to @ref condition::handshake_failed
*/
no_sec_key,
/** The WebSocket handshake Sec-WebSocket-Key field is invalid
Error codes with this value will compare equal to @ref condition::handshake_failed
*/
bad_sec_key,
/** The WebSocket handshake Sec-WebSocket-Version field is missing
Error codes with this value will compare equal to @ref condition::handshake_failed
*/
no_sec_version,
/** The WebSocket handshake Sec-WebSocket-Version field is invalid
Error codes with this value will compare equal to @ref condition::handshake_failed
*/
bad_sec_version,
/** The WebSocket handshake Sec-WebSocket-Accept field is missing
Error codes with this value will compare equal to @ref condition::handshake_failed
*/
no_sec_accept,
/** The WebSocket handshake Sec-WebSocket-Accept field is invalid
Error codes with this value will compare equal to @ref condition::handshake_failed
*/
bad_sec_accept,
/** The WebSocket handshake was declined by the remote peer
Error codes with this value will compare equal to @ref condition::handshake_failed
*/
upgrade_declined,
//
// Protocol errors
//
// These will compare equal to condition::protocol_violation
//
/** The WebSocket frame contained an illegal opcode
Error codes with this value will compare equal to @ref condition::protocol_violation
*/
bad_opcode,
/** The WebSocket data frame was unexpected
Error codes with this value will compare equal to @ref condition::protocol_violation
*/
bad_data_frame,
/** The WebSocket continuation frame was unexpected
Error codes with this value will compare equal to @ref condition::protocol_violation
*/
bad_continuation,
/** The WebSocket frame contained illegal reserved bits
Error codes with this value will compare equal to @ref condition::protocol_violation
*/
bad_reserved_bits,
/** The WebSocket control frame was fragmented
Error codes with this value will compare equal to @ref condition::protocol_violation
*/
bad_control_fragment,
/** The WebSocket control frame size was invalid
Error codes with this value will compare equal to @ref condition::protocol_violation
*/
bad_control_size,
/** The WebSocket frame was unmasked
Error codes with this value will compare equal to @ref condition::protocol_violation
*/
bad_unmasked_frame,
/** The WebSocket frame was masked
Error codes with this value will compare equal to @ref condition::protocol_violation
*/
bad_masked_frame,
/** The WebSocket frame size was not canonical
Error codes with this value will compare equal to @ref condition::protocol_violation
*/
bad_size,
/** The WebSocket frame payload was not valid utf8
Error codes with this value will compare equal to @ref condition::protocol_violation
*/
bad_frame_payload,
/** The WebSocket close frame reason code was invalid
Error codes with this value will compare equal to @ref condition::protocol_violation
*/
bad_close_code,
/** The WebSocket close frame payload size was invalid
Error codes with this value will compare equal to @ref condition::protocol_violation
*/
bad_close_size,
/** The WebSocket close frame payload was not valid utf8
Error codes with this value will compare equal to @ref condition::protocol_violation
*/
bad_close_payload
};
/// Error conditions corresponding to sets of error codes.
enum class condition
{
/** The WebSocket handshake failed
This condition indicates that the WebSocket handshake failed. If
the corresponding HTTP response indicates the keep-alive behavior,
then the handshake may be reattempted.
*/
handshake_failed = 1,
/** A WebSocket protocol violation occurred
This condition indicates that the remote peer on the WebSocket
connection sent data which violated the protocol.
*/
protocol_violation
};
} // websocket
} // beast
} // boost
#include <boost/beast/websocket/impl/error.hpp>
#ifdef BOOST_BEAST_HEADER_ONLY
#include <boost/beast/websocket/impl/error.ipp>
#endif
#endif

View File

@@ -0,0 +1,633 @@
//
// 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/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))
res.set(http::field::server,
string_view(BOOST_BEAST_VERSION_STRING));
};
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 asio::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
{
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
"websocket::async_accept"));
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 asio::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
{
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
"websocket::async_accept"));
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<
BOOST_BEAST_ASYNC_TPARAM1 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 ConstBufferSequence,
BOOST_BEAST_ASYNC_TPARAM1 AcceptHandler>
BOOST_BEAST_ASYNC_RESULT1(AcceptHandler)
stream<NextLayer, deflateSupported>::
async_accept(
ConstBufferSequence const& buffers,
AcceptHandler&& handler,
typename std::enable_if<
! http::detail::is_header<
ConstBufferSequence>::value>::type*
)
{
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 Body, class Allocator,
BOOST_BEAST_ASYNC_TPARAM1 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);
}
} // websocket
} // beast
} // boost
#endif

View File

@@ -0,0 +1,453 @@
//
// 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 asio::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
{
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
"websocket::async_close"));
impl.op_close.emplace(std::move(*this));
}
impl.wr_block.lock(this);
BOOST_ASIO_CORO_YIELD
{
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
"websocket::async_close"));
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
{
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
"websocket::async_close"));
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
{
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
"websocket::async_close"));
impl.op_r_close.emplace(std::move(*this));
}
impl.rd_block.lock(this);
BOOST_ASIO_CORO_YIELD
{
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
"websocket::async_close"));
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
{
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
"websocket::async_close"));
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
{
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
"websocket::async_close"));
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
{
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
"websocket::async_close"));
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<BOOST_BEAST_ASYNC_TPARAM1 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

View File

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

View File

@@ -0,0 +1,160 @@
//
// 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_IPP
#define BOOST_BEAST_WEBSOCKET_IMPL_ERROR_IPP
#include <boost/beast/websocket/error.hpp>
namespace boost {
namespace beast {
namespace websocket {
namespace detail {
class error_codes : public error_category
{
public:
const char*
name() const noexcept override
{
return "boost.beast.websocket";
}
std::string
message(int ev) const override
{
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";
}
}
error_condition
default_error_condition(int ev) const noexcept override
{
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;
}
}
};
class error_conditions : public error_category
{
public:
const char*
name() const noexcept override
{
return "boost.beast.websocket";
}
std::string
message(int cv) const override
{
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
error_code
make_error_code(error e)
{
static detail::error_codes const cat{};
return error_code{static_cast<
std::underlying_type<error>::type>(e), cat};
}
error_condition
make_error_condition(condition c)
{
static detail::error_conditions const cat{};
return error_condition{static_cast<
std::underlying_type<condition>::type>(c), cat};
}
} // websocket
} // beast
} // boost
#endif

View File

@@ -0,0 +1,399 @@
//
// 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 asio::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
{
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
"websocket::async_handshake"));
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
{
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
"websocket::async_handshake"));
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
{
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
"websocket::async_handshake"));
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<BOOST_BEAST_ASYNC_TPARAM1 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<BOOST_BEAST_ASYNC_TPARAM1 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);
}
} // websocket
} // beast
} // boost
#endif

View File

@@ -0,0 +1,365 @@
//
// 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 asio::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
{
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
"websocket::async_ping"));
impl.op_ping.emplace(std::move(*this));
}
impl.wr_block.lock(this);
BOOST_ASIO_CORO_YIELD
{
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
"websocket::async_ping"));
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
{
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
"websocket::async_ping"));
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 asio::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
{
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
"websocket::async_ping"));
impl.op_idle_ping.emplace(std::move(*this));
}
impl.wr_block.lock(this);
BOOST_ASIO_CORO_YIELD
{
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
"websocket::async_ping"));
net::post(
this->get_executor(), 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
{
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
"websocket::async_ping"));
net::async_write(impl.stream(), fb_->data(),
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<BOOST_BEAST_ASYNC_TPARAM1 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<BOOST_BEAST_ASYNC_TPARAM1 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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,40 @@
//
// 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_RFC6455_HPP
#define BOOST_BEAST_WEBSOCKET_IMPL_RFC6455_HPP
#include <boost/beast/http/fields.hpp>
#include <boost/beast/http/rfc7230.hpp>
namespace boost {
namespace beast {
namespace websocket {
template<class Allocator>
bool
is_upgrade(http::header<true,
http::basic_fields<Allocator>> const& req)
{
if(req.version() < 11)
return false;
if(req.method() != http::verb::get)
return false;
if(! http::token_list{req[http::field::connection]}.exists("upgrade"))
return false;
if(! http::token_list{req[http::field::upgrade]}.exists("websocket"))
return false;
return true;
}
} // websocket
} // beast
} // boost
#endif

View File

@@ -0,0 +1,109 @@
//
// 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_SSL_HPP
#define BOOST_BEAST_WEBSOCKET_IMPL_SSL_HPP
#include <utility>
#include <boost/beast/websocket/teardown.hpp>
#include <boost/asio/compose.hpp>
#include <boost/asio/coroutine.hpp>
namespace boost {
namespace beast {
/*
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_notify
If the remote host calls async_shutdown then the
local host's async_read will complete with eof.
If both hosts call async_shutdown then the calls
to async_shutdown will complete with eof.
*/
template<class AsyncStream>
void
teardown(
role_type role,
boost::asio::ssl::stream<AsyncStream>& stream,
error_code& ec)
{
stream.shutdown(ec);
using boost::beast::websocket::teardown;
error_code ec2;
teardown(role, stream.next_layer(), ec ? ec2 : ec);
}
namespace detail {
template<class AsyncStream>
struct ssl_shutdown_op
: boost::asio::coroutine
{
ssl_shutdown_op(
boost::asio::ssl::stream<AsyncStream>& s,
role_type role)
: s_(s)
, role_(role)
{
}
template<class Self>
void
operator()(Self& self, error_code ec = {}, std::size_t bytes_transferred = 0)
{
BOOST_ASIO_CORO_REENTER(*this)
{
BOOST_ASIO_CORO_YIELD
s_.async_shutdown(std::move(self));
ec_ = ec;
using boost::beast::websocket::async_teardown;
BOOST_ASIO_CORO_YIELD
async_teardown(role_, s_.next_layer(), std::move(self));
if (!ec_)
ec_ = ec;
self.complete(ec_);
}
}
private:
boost::asio::ssl::stream<AsyncStream>& s_;
role_type role_;
error_code ec_;
};
} // detail
template<
class AsyncStream,
class TeardownHandler>
void
async_teardown(
role_type role,
boost::asio::ssl::stream<AsyncStream>& stream,
TeardownHandler&& handler)
{
return boost::asio::async_compose<TeardownHandler, void(error_code)>(
detail::ssl_shutdown_op<AsyncStream>(stream, role),
handler,
stream);
}
} // beast
} // boost
#endif

View File

@@ -0,0 +1,351 @@
//
// 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/asio/steady_timer.hpp>
#include <boost/assert.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() 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");
return impl_->read_size_hint_db(buffer);
}
//------------------------------------------------------------------------------
//
// 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

View File

@@ -0,0 +1,992 @@
//
// 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/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 */
bool wr_compress_opt /* compress message setting */ = true;
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->get_context(
this->boost::empty_value<NextLayer>::get().get_executor()))
, 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();
}
void
time_out()
{
timed_out = true;
change_status(status::closed);
close_socket(get_lowest_layer(stream()));
}
// Called just before sending
// the first frame of each message
void
begin_msg()
{
wr_frag = wr_frag_opt;
wr_compress =
this->pmd_enabled() && wr_compress_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;
}
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_pmd(
initial_size, rd_done, rd_remain, rd_fh);
}
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);
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
"websocket::check_stop_now"
));
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);
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
"websocket::check_stop_now"
));
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);
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
"websocket::check_stop_now"
));
timer.async_wait(
timeout_handler<Executor>(
ex, this->weak_from_this()));
}
else
{
// VFALCO This assert goes off when there's also
// a pending read with the timer set. The bigger
// fix is to give close its own timeout, instead
// of using the handshake timeout.
// 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:
template<class Executor>
static net::execution_context&
get_context(Executor const& ex,
typename std::enable_if< net::execution::is_executor<Executor>::value >::type* = 0)
{
return net::query(ex, net::execution::context);
}
template<class Executor>
static net::execution_context&
get_context(Executor const& ex,
typename std::enable_if< !net::execution::is_executor<Executor>::value >::type* = 0)
{
return ex.context();
}
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.time_out();
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)
{
{
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
"websocket::timeout_handler"
));
idle_ping_op<Executor>(sp, get_executor());
}
++impl.idle_counter;
impl.timer.expires_after(
impl.timeout_opt.idle_timeout / 2);
{
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
"websocket::timeout_handler"
));
impl.timer.async_wait(std::move(*this));
}
return;
}
impl.time_out();
return;
case status::closing:
impl.time_out();
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);
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::uint16_t len_be;
BOOST_ASSERT(buffer_bytes(cb) >= sizeof(len_be));
cb.consume(net::buffer_copy(
net::mutable_buffer(&len_be, sizeof(len_be)), cb));
fh.len = endian::big_to_native(len_be);
if(fh.len < 126)
{
// length not canonical
ec = error::bad_size;
return false;
}
break;
}
case 127:
{
std::uint64_t len_be;
BOOST_ASSERT(buffer_bytes(cb) >= sizeof(len_be));
cb.consume(net::buffer_copy(
net::mutable_buffer(&len_be, sizeof(len_be)), cb));
fh.len = endian::big_to_native(len_be);
if(fh.len < 65536)
{
// length not canonical
ec = error::bad_size;
return false;
}
break;
}
}
if(fh.mask)
{
std::uint32_t key_le;
BOOST_ASSERT(buffer_bytes(cb) >= sizeof(key_le));
cb.consume(net::buffer_copy(
net::mutable_buffer(&key_le, sizeof(key_le)), cb));
fh.key = endian::little_to_native(key_le);
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);
{
auto code_be = endian::native_to_big<std::uint16_t>(cr.code);
auto mb = db.prepare(2);
net::buffer_copy(mb,
net::const_buffer(&code_be, sizeof(code_be)));
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

View File

@@ -0,0 +1,213 @@
//
// 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/is_invocable.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 asio::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)
, nb_(false)
{
(*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
{
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
"websocket::tcp::async_teardown"
));
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
{
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
"websocket::tcp::async_teardown"
));
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

View File

@@ -0,0 +1,856 @@
//
// 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 asio::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
{
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
fin_ ?
"websocket::async_write" :
"websocket::async_write_some"
));
impl.op_wr.emplace(std::move(*this));
}
impl.wr_block.lock(this);
BOOST_ASIO_CORO_YIELD
{
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
fin_ ?
"websocket::async_write" :
"websocket::async_write_some"
));
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
{
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
fin_ ?
"websocket::async_write" :
"websocket::async_write_some"
));
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
{
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
fin_ ?
"websocket::async_write" :
"websocket::async_write_some"
));
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
{
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
fin_ ?
"websocket::async_write" :
"websocket::async_write_some"
));
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
{
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
fin_ ?
"websocket::async_write" :
"websocket::async_write_some"
));
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
{
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
fin_ ?
"websocket::async_write" :
"websocket::async_write_some"
));
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
{
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
fin_ ?
"websocket::async_write" :
"websocket::async_write_some"
));
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, BOOST_BEAST_ASYNC_TPARAM2 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, BOOST_BEAST_ASYNC_TPARAM2 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

View File

@@ -0,0 +1,64 @@
//
// 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_OPTION_HPP
#define BOOST_BEAST_WEBSOCKET_OPTION_HPP
#include <boost/beast/core/detail/config.hpp>
namespace boost {
namespace beast {
namespace websocket {
/** permessage-deflate extension options.
These settings control the permessage-deflate extension,
which allows messages to be compressed.
@note Objects of this type are used with
@ref beast::websocket::stream::set_option.
*/
struct permessage_deflate
{
/// `true` to offer the extension in the server role
bool server_enable = false;
/// `true` to offer the extension in the client role
bool client_enable = false;
/** Maximum server window bits to offer
@note Due to a bug in ZLib, this value must be greater than 8.
*/
int server_max_window_bits = 15;
/** Maximum client window bits to offer
@note Due to a bug in ZLib, this value must be greater than 8.
*/
int client_max_window_bits = 15;
/// `true` if server_no_context_takeover desired
bool server_no_context_takeover = false;
/// `true` if client_no_context_takeover desired
bool client_no_context_takeover = false;
/// Deflate compression level 0..9
int compLevel = 8;
/// Deflate memory level, 1..9
int memLevel = 4;
};
} // websocket
} // beast
} // boost
#endif

View File

@@ -0,0 +1,223 @@
//
// 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_RFC6455_HPP
#define BOOST_BEAST_WEBSOCKET_RFC6455_HPP
#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>
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
indicates a WebSocket Upgrade. It does not validate the
contents of the fields: it just trivially accepts requests
which could only possibly be a valid or invalid WebSocket
Upgrade message.
Callers who wish to manually read HTTP requests in their
server implementation can use this function to determine if
the request should be routed to an instance of
@ref websocket::stream.
@par Example
@code
void handle_connection(net::ip::tcp::socket& sock)
{
boost::beast::flat_buffer buffer;
boost::beast::http::request<boost::beast::http::string_body> req;
boost::beast::http::read(sock, buffer, req);
if(boost::beast::websocket::is_upgrade(req))
{
boost::beast::websocket::stream<decltype(sock)> ws{std::move(sock)};
ws.accept(req);
}
}
@endcode
@param req The HTTP Request object to check.
@return `true` if the request is a WebSocket Upgrade.
*/
template<class Allocator>
bool
is_upgrade(beast::http::header<true,
http::basic_fields<Allocator>> const& req);
/** Close status codes.
These codes accompany close frames.
@see <a href="https://tools.ietf.org/html/rfc6455#section-7.4.1">RFC 6455 7.4.1 Defined Status Codes</a>
*/
enum close_code : std::uint16_t
{
/// Normal closure; the connection successfully completed whatever purpose for which it was created.
normal = 1000,
/// The endpoint is going away, either because of a server failure or because the browser is navigating away from the page that opened the connection.
going_away = 1001,
/// The endpoint is terminating the connection due to a protocol error.
protocol_error = 1002,
/// The connection is being terminated because the endpoint received data of a type it cannot accept (for example, a text-only endpoint received binary data).
unknown_data = 1003,
/// The endpoint is terminating the connection because a message was received that contained inconsistent data (e.g., non-UTF-8 data within a text message).
bad_payload = 1007,
/// The endpoint is terminating the connection because it received a message that violates its policy. This is a generic status code, used when codes 1003 and 1009 are not suitable.
policy_error = 1008,
/// The endpoint is terminating the connection because a data frame was received that is too large.
too_big = 1009,
/// The client is terminating the connection because it expected the server to negotiate one or more extension, but the server didn't.
needs_extension = 1010,
/// The server is terminating the connection because it encountered an unexpected condition that prevented it from fulfilling the request.
internal_error = 1011,
/// The server is terminating the connection because it is restarting.
service_restart = 1012,
/// The server is terminating the connection due to a temporary condition, e.g. it is overloaded and is casting off some of its clients.
try_again_later = 1013,
//----
//
// The following are illegal on the wire
//
/** Used internally to mean "no error"
This code is reserved and may not be sent.
*/
none = 0,
/** Reserved for future use by the WebSocket standard.
This code is reserved and may not be sent.
*/
reserved1 = 1004,
/** No status code was provided even though one was expected.
This code is reserved and may not be sent.
*/
no_status = 1005,
/** Connection was closed without receiving a close frame
This code is reserved and may not be sent.
*/
abnormal = 1006,
/** Reserved for future use by the WebSocket standard.
This code is reserved and may not be sent.
*/
reserved2 = 1014,
/** Reserved for future use by the WebSocket standard.
This code is reserved and may not be sent.
*/
reserved3 = 1015
//
//----
//last = 5000 // satisfy warnings
};
/// The type representing the reason string in a close frame.
using reason_string = static_string<123, char>;
/// The type representing the payload of ping and pong messages.
using ping_data = static_string<125, char>;
/** Description of the close reason.
This object stores the close code (if any) and the optional
utf-8 encoded implementation defined reason string.
*/
struct close_reason
{
/// The close code.
std::uint16_t code = close_code::none;
/// The optional utf8-encoded reason string.
reason_string reason;
/** Default constructor.
The code will be none. Default constructed objects
will explicitly convert to bool as `false`.
*/
close_reason() = default;
/// Construct from a code.
close_reason(std::uint16_t code_)
: code(code_)
{
}
/// Construct from a reason string. code is @ref close_code::normal.
close_reason(string_view s)
: code(close_code::normal)
, reason(s)
{
}
/// Construct from a reason string literal. code is @ref close_code::normal.
close_reason(char const* s)
: code(close_code::normal)
, reason(s)
{
}
/// Construct from a close code and reason string.
close_reason(close_code code_, string_view s)
: code(code_)
, reason(s)
{
}
/// Returns `true` if a code was specified
operator bool() const
{
return code != close_code::none;
}
};
} // websocket
} // beast
} // boost
#include <boost/beast/websocket/impl/rfc6455.hpp>
#endif

View File

@@ -0,0 +1,84 @@
//
// 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_SSL_HPP
#define BOOST_BEAST_WEBSOCKET_SSL_HPP
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/websocket/teardown.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/ssl/stream.hpp>
namespace boost {
namespace beast {
/** 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 `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
@param stream The stream to tear down.
@param ec Set to the error if any occurred.
*/
template<class SyncStream>
void
teardown(
role_type role,
net::ssl::stream<SyncStream>& stream,
error_code& ec);
/** 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
`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
@param stream The stream to tear down.
@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 `net::post`.
*/
template<class AsyncStream, class TeardownHandler>
void
async_teardown(
role_type role,
net::ssl::stream<AsyncStream>& stream,
TeardownHandler&& handler);
} // beast
} // boost
#include <boost/beast/websocket/impl/ssl.hpp>
#endif

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -0,0 +1,32 @@
//
// 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_FWD_HPP
#define BOOST_BEAST_WEBSOCKET_STREAM_FWD_HPP
#include <boost/beast/core/detail/config.hpp>
//[code_websocket_1h
namespace boost {
namespace beast {
namespace websocket {
template<
class NextLayer,
bool deflateSupported = true>
class stream;
} // websocket
} // beast
} // boost
//]
#endif

View File

@@ -0,0 +1,183 @@
//
// 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_TEARDOWN_HPP
#define BOOST_BEAST_WEBSOCKET_TEARDOWN_HPP
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/error.hpp>
#include <boost/beast/core/role.hpp>
#include <boost/asio/basic_stream_socket.hpp>
#include <type_traits>
namespace boost {
namespace beast {
namespace websocket {
/** Tear down a connection.
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 `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
@param socket The socket to tear down.
@param ec Set to the error if any occurred.
*/
template<class Socket>
void
teardown(
role_type role,
Socket& socket,
error_code& ec)
{
boost::ignore_unused(role, socket, ec);
/*
If you are trying to use OpenSSL and this goes off, you need to
add an include for <boost/beast/websocket/ssl.hpp>.
If you are creating an instance of beast::websocket::stream with your
own user defined type, you must provide an overload of teardown with
the corresponding signature (including the role_type).
*/
static_assert(sizeof(Socket)==-1,
"Unknown Socket type in teardown.");
}
/** Start tearing down a connection.
This begins tearing down a connection asynchronously.
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
`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
@param socket The socket to tear down.
@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 `net::post`.
*/
template<
class Socket,
class TeardownHandler>
void
async_teardown(
role_type role,
Socket& socket,
TeardownHandler&& handler)
{
boost::ignore_unused(role, socket, handler);
/*
If you are trying to use OpenSSL and this goes off, you need to
add an include for <boost/beast/websocket/ssl.hpp>.
If you are creating an instance of beast::websocket::stream with your
own user defined type, you must provide an overload of teardown with
the corresponding signature (including the role_type).
*/
static_assert(sizeof(Socket)==-1,
"Unknown Socket type in async_teardown.");
}
} // websocket
//------------------------------------------------------------------------------
namespace websocket {
/** 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 `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
@param socket The socket to tear down.
@param ec Set to the error if any occurred.
*/
template<class Protocol, class Executor>
void
teardown(
role_type role,
net::basic_stream_socket<
Protocol, Executor>& socket,
error_code& ec);
/** 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
`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
@param socket The socket to tear down.
@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 `net::post`.
*/
template<
class Protocol, class Executor,
class TeardownHandler>
void
async_teardown(
role_type role,
net::basic_stream_socket<
Protocol, Executor>& socket,
TeardownHandler&& handler);
} // websocket
} // beast
} // boost
#include <boost/beast/websocket/impl/teardown.hpp>
#endif