add boost on mac

This commit is contained in:
Bassem Girgis
2019-08-10 16:38:17 -05:00
parent 861b918727
commit be945cb63b
14105 changed files with 2714968 additions and 0 deletions

View File

@@ -0,0 +1,315 @@
//
// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
#ifndef BOOST_BEAST_WEBSOCKET_DETAIL_DECORATOR_HPP
#define BOOST_BEAST_WEBSOCKET_DETAIL_DECORATOR_HPP
#include <boost/beast/websocket/rfc6455.hpp>
#include <boost/beast/core/detail/type_traits.hpp>
#include <boost/core/exchange.hpp>
#include <boost/type_traits/make_void.hpp>
#include <algorithm>
#include <memory>
#include <new>
#include <type_traits>
#include <utility>
namespace boost {
namespace beast {
namespace websocket {
namespace detail {
// VFALCO NOTE: When this is two traits, one for
// request and one for response,
// Visual Studio 2015 fails.
template<class T, class U, class = void>
struct can_invoke_with : std::false_type
{
};
template<class T, class U>
struct can_invoke_with<T, U, boost::void_t<decltype(
std::declval<T&>()(std::declval<U&>()))>>
: std::true_type
{
};
template<class T>
using is_decorator = std::integral_constant<bool,
can_invoke_with<T, request_type>::value ||
can_invoke_with<T, response_type>::value>;
class decorator
{
friend class decorator_test;
struct incomplete;
struct exemplar
{
void (incomplete::*mf)();
std::shared_ptr<incomplete> sp;
void* param;
};
union storage
{
void* p_;
void (*fn_)();
typename std::aligned_storage<
sizeof(exemplar),
alignof(exemplar)>::type buf_;
};
struct vtable
{
void (*move)(
storage& dst, storage& src) noexcept;
void (*destroy)(storage& dst) noexcept;
void (*invoke_req)(
storage& dst, request_type& req);
void (*invoke_res)(
storage& dst, response_type& req);
static void move_fn(
storage&, storage&) noexcept
{
}
static void destroy_fn(
storage&) noexcept
{
}
static void invoke_req_fn(
storage&, request_type&)
{
}
static void invoke_res_fn(
storage&, response_type&)
{
}
static vtable const* get_default()
{
static const vtable impl{
&move_fn,
&destroy_fn,
&invoke_req_fn,
&invoke_res_fn
};
return &impl;
}
};
template<class F, bool Inline =
(sizeof(F) <= sizeof(storage) &&
alignof(F) <= alignof(storage) &&
std::is_nothrow_move_constructible<F>::value)>
struct vtable_impl;
storage storage_;
vtable const* vtable_ = vtable::get_default();
// VFALCO NOTE: When this is two traits, one for
// request and one for response,
// Visual Studio 2015 fails.
template<class T, class U, class = void>
struct maybe_invoke
{
void
operator()(T&, U&)
{
}
};
template<class T, class U>
struct maybe_invoke<T, U, boost::void_t<decltype(
std::declval<T&>()(std::declval<U&>()))>>
{
void
operator()(T& t, U& u)
{
t(u);
}
};
public:
decorator() = default;
decorator(decorator const&) = delete;
decorator& operator=(decorator const&) = delete;
~decorator()
{
vtable_->destroy(storage_);
}
decorator(decorator&& other) noexcept
: vtable_(boost::exchange(
other.vtable_, vtable::get_default()))
{
vtable_->move(
storage_, other.storage_);
}
template<class F,
class = typename std::enable_if<
! std::is_convertible<
F, decorator>::value>::type>
explicit
decorator(F&& f)
: vtable_(vtable_impl<
typename std::decay<F>::type>::
construct(storage_, std::forward<F>(f)))
{
}
decorator&
operator=(decorator&& other) noexcept
{
vtable_->destroy(storage_);
vtable_ = boost::exchange(
other.vtable_, vtable::get_default());
vtable_->move(storage_, other.storage_);
return *this;
}
void
operator()(request_type& req)
{
vtable_->invoke_req(storage_, req);
}
void
operator()(response_type& res)
{
vtable_->invoke_res(storage_, res);
}
};
template<class F>
struct decorator::vtable_impl<F, true>
{
template<class Arg>
static
vtable const*
construct(storage& dst, Arg&& arg)
{
::new (static_cast<void*>(&dst.buf_)) F(
std::forward<Arg>(arg));
return get();
}
static
void
move(storage& dst, storage& src) noexcept
{
auto& f = *reinterpret_cast<F*>(&src.buf_);
::new (&dst.buf_) F(std::move(f));
}
static
void
destroy(storage& dst) noexcept
{
reinterpret_cast<F*>(&dst.buf_)->~F();
}
static
void
invoke_req(storage& dst, request_type& req)
{
maybe_invoke<F, request_type>{}(
*reinterpret_cast<F*>(&dst.buf_), req);
}
static
void
invoke_res(storage& dst, response_type& res)
{
maybe_invoke<F, response_type>{}(
*reinterpret_cast<F*>(&dst.buf_), res);
}
static
vtable
const* get()
{
static constexpr vtable impl{
&move,
&destroy,
&invoke_req,
&invoke_res};
return &impl;
}
};
template<class F>
struct decorator::vtable_impl<F, false>
{
template<class Arg>
static
vtable const*
construct(storage& dst, Arg&& arg)
{
dst.p_ = new F(std::forward<Arg>(arg));
return get();
}
static
void
move(storage& dst, storage& src) noexcept
{
dst.p_ = src.p_;
}
static
void
destroy(storage& dst) noexcept
{
delete static_cast<F*>(dst.p_);
}
static
void
invoke_req(
storage& dst, request_type& req)
{
maybe_invoke<F, request_type>{}(
*static_cast<F*>(dst.p_), req);
}
static
void
invoke_res(
storage& dst, response_type& res)
{
maybe_invoke<F, response_type>{}(
*static_cast<F*>(dst.p_), res);
}
static
vtable const*
get()
{
static constexpr vtable impl{&move,
&destroy, &invoke_req, &invoke_res};
return &impl;
}
};
} // detail
} // websocket
} // beast
} // boost
#endif

View File

@@ -0,0 +1,311 @@
//
// 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/buffers_suffix.hpp>
#include <boost/beast/core/flat_static_buffer.hpp>
#include <boost/beast/core/static_string.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/assert.hpp>
// This is for <boost/endian/buffers.hpp>
#if BOOST_WORKAROUND(BOOST_MSVC, > 0)
# pragma warning (push)
# pragma warning (disable: 4127) // conditional expression is constant
#endif
#include <boost/endian/buffers.hpp>
#if BOOST_WORKAROUND(BOOST_MSVC, > 0)
# pragma warning (pop)
#endif
#include <cstdint>
namespace boost {
namespace beast {
namespace websocket {
namespace detail {
inline
std::uint16_t
big_uint16_to_native(void const* buf)
{
auto const p = static_cast<
std::uint8_t const*>(buf);
return (p[0]<<8) + p[1];
}
inline
std::uint64_t
big_uint64_to_native(void const* buf)
{
auto const p = static_cast<
std::uint8_t const*>(buf);
return
(static_cast<std::uint64_t>(p[0])<<56) +
(static_cast<std::uint64_t>(p[1])<<48) +
(static_cast<std::uint64_t>(p[2])<<40) +
(static_cast<std::uint64_t>(p[3])<<32) +
(static_cast<std::uint64_t>(p[4])<<24) +
(static_cast<std::uint64_t>(p[5])<<16) +
(static_cast<std::uint64_t>(p[6])<< 8) +
p[7];
}
inline
std::uint32_t
little_uint32_to_native(void const* buf)
{
auto const p = static_cast<
std::uint8_t const*>(buf);
return
p[0] +
(static_cast<std::uint32_t>(p[1])<< 8) +
(static_cast<std::uint32_t>(p[2])<<16) +
(static_cast<std::uint32_t>(p[3])<<24);
}
inline
void
native_to_little_uint32(std::uint32_t v, void* buf)
{
auto p = static_cast<std::uint8_t*>(buf);
p[0] = v & 0xff;
p[1] = (v >> 8) & 0xff;
p[2] = (v >> 16) & 0xff;
p[3] = (v >> 24) & 0xff;
}
// 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)
{
using namespace boost::endian;
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;
::new(&b[2]) big_uint16_buf_t{
(std::uint16_t)fh.len};
n = 4;
}
else
{
b[1] |= 127;
::new(&b[2]) big_uint64_buf_t{fh.len};
n = 10;
}
if(fh.mask)
{
native_to_little_uint32(fh.key, &b[n]);
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)
{
using namespace boost::endian;
auto 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;
}
buffers_suffix<Buffers> cb(bs);
{
std::uint8_t b[2];
net::buffer_copy(net::buffer(b), cb);
cr.code = big_uint16_to_native(&b[0]);
cb.consume(2);
n -= 2;
if(! is_valid_close_code(cr.code))
{
// invalid close code
ec = error::bad_close_code;
return;
}
}
if(n > 0)
{
cr.reason.resize(n);
net::buffer_copy(
net::buffer(&cr.reason[0], n), cb);
if(! check_utf8(
cr.reason.data(), cr.reason.size()))
{
// not valid utf-8
ec = error::bad_close_payload;
return;
}
}
else
{
cr.reason = "";
}
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,67 @@
//
// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
#ifndef BOOST_BEAST_WEBSOCKET_DETAIL_HYBI13_IPP
#define BOOST_BEAST_WEBSOCKET_DETAIL_HYBI13_IPP
#include <boost/beast/websocket/detail/hybi13.hpp>
#include <boost/beast/core/detail/sha1.hpp>
#include <boost/beast/websocket/detail/prng.hpp>
#include <boost/assert.hpp>
#include <cstdint>
#include <string>
namespace boost {
namespace beast {
namespace websocket {
namespace detail {
void
make_sec_ws_key(sec_ws_key_type& key)
{
auto g = make_prng(true);
char a[16];
for(int i = 0; i < 16; i += 4)
{
auto const v = g();
a[i ] = v & 0xff;
a[i+1] = (v >> 8) & 0xff;
a[i+2] = (v >> 16) & 0xff;
a[i+3] = (v >> 24) & 0xff;
}
key.resize(key.max_size());
key.resize(beast::detail::base64::encode(
key.data(), &a[0], 16));
}
void
make_sec_ws_accept(
sec_ws_accept_type& accept,
string_view key)
{
BOOST_ASSERT(key.size() <= sec_ws_key_type::max_size_n);
static_string<sec_ws_key_type::max_size_n + 36> m(key);
m.append("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
beast::detail::sha1_context ctx;
beast::detail::init(ctx);
beast::detail::update(ctx, m.data(), m.size());
char digest[beast::detail::sha1_context::digest_size];
beast::detail::finish(ctx, &digest[0]);
accept.resize(accept.max_size());
accept.resize(beast::detail::base64::encode(
accept.data(), &digest[0], sizeof(digest)));
}
} // detail
} // websocket
} // beast
} // boost
#endif // BOOST_BEAST_WEBSOCKET_DETAIL_HYBI13_IPP

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,92 @@
//
// 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>;
inline
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;
}
template<std::size_t N>
void
rol(std::array<unsigned char, N>& 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
//
inline
void
mask_inplace(net::mutable_buffer& b, prepared_key& key)
{
auto n = b.size();
auto 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);
}
}
// Apply mask in place
//
template<
class MutableBufferSequence,
class KeyType>
void
mask_inplace(
MutableBufferSequence const& buffers,
KeyType& key)
{
for(net::mutable_buffer b :
beast::buffers_range_ref(buffers))
mask_inplace(b, key);
}
} // 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(iequals(ext.first, "permessage-deflate"))
{
for(auto const& param : ext.second)
{
if(iequals(param.first,
"server_max_window_bits"))
{
if(offer.server_max_window_bits != 0)
{
// The negotiation offer contains multiple
// extension parameters with the same name.
//
return; // MUST decline
}
if(param.second.empty())
{
// The negotiation offer extension
// parameter is missing the value.
//
return; // MUST decline
}
offer.server_max_window_bits =
parse_bits(param.second);
if( offer.server_max_window_bits < 8 ||
offer.server_max_window_bits > 15)
{
// The negotiation offer contains an
// extension parameter with an invalid value.
//
return; // MUST decline
}
}
else if(iequals(param.first,
"client_max_window_bits"))
{
if(offer.client_max_window_bits != 0)
{
// The negotiation offer contains multiple
// extension parameters with the same name.
//
return; // MUST decline
}
if(! param.second.empty())
{
offer.client_max_window_bits =
parse_bits(param.second);
if( offer.client_max_window_bits < 8 ||
offer.client_max_window_bits > 15)
{
// The negotiation offer contains an
// extension parameter with an invalid value.
//
return; // MUST decline
}
}
else
{
offer.client_max_window_bits = -1;
}
}
else if(iequals(param.first,
"server_no_context_takeover"))
{
if(offer.server_no_context_takeover)
{
// The negotiation offer contains multiple
// extension parameters with the same name.
//
return; // MUST decline
}
if(! param.second.empty())
{
// The negotiation offer contains an
// extension parameter with an invalid value.
//
return; // MUST decline
}
offer.server_no_context_takeover = true;
}
else if(iequals(param.first,
"client_no_context_takeover"))
{
if(offer.client_no_context_takeover)
{
// The negotiation offer contains multiple
// extension parameters with the same name.
//
return; // MUST decline
}
if(! param.second.empty())
{
// The negotiation offer contains an
// extension parameter with an invalid value.
//
return; // MUST decline
}
offer.client_no_context_takeover = true;
}
else
{
// The negotiation offer contains an extension
// parameter not defined for use in an offer.
//
return; // MUST decline
}
}
offer.accept = true;
return;
}
}
}
static_string<512>
pmd_write_impl(pmd_offer const& offer)
{
static_string<512> s = "permessage-deflate";
if(offer.server_max_window_bits != 0)
{
if(offer.server_max_window_bits != -1)
{
s += "; server_max_window_bits=";
s += to_static_string(
offer.server_max_window_bits);
}
else
{
s += "; server_max_window_bits";
}
}
if(offer.client_max_window_bits != 0)
{
if(offer.client_max_window_bits != -1)
{
s += "; client_max_window_bits=";
s += to_static_string(
offer.client_max_window_bits);
}
else
{
s += "; client_max_window_bits";
}
}
if(offer.server_no_context_takeover)
{
s += "; server_no_context_takeover";
}
if(offer.client_no_context_takeover)
{
s += "; client_no_context_takeover";
}
return s;
}
static_string<512>
pmd_negotiate_impl(
pmd_offer& config,
pmd_offer const& offer,
permessage_deflate const& o)
{
static_string<512> s = "permessage-deflate";
config.server_no_context_takeover =
offer.server_no_context_takeover ||
o.server_no_context_takeover;
if(config.server_no_context_takeover)
s += "; server_no_context_takeover";
config.client_no_context_takeover =
o.client_no_context_takeover ||
offer.client_no_context_takeover;
if(config.client_no_context_takeover)
s += "; client_no_context_takeover";
if(offer.server_max_window_bits != 0)
config.server_max_window_bits = (std::min)(
offer.server_max_window_bits,
o.server_max_window_bits);
else
config.server_max_window_bits =
o.server_max_window_bits;
if(config.server_max_window_bits < 15)
{
// ZLib's deflateInit silently treats 8 as
// 9 due to a bug, so prevent 8 from being used.
//
if(config.server_max_window_bits < 9)
config.server_max_window_bits = 9;
s += "; server_max_window_bits=";
s += to_static_string(
config.server_max_window_bits);
}
switch(offer.client_max_window_bits)
{
case -1:
// extension parameter is present with no value
config.client_max_window_bits =
o.client_max_window_bits;
if(config.client_max_window_bits < 15)
{
s += "; client_max_window_bits=";
s += to_static_string(
config.client_max_window_bits);
}
break;
case 0:
/* extension parameter is absent.
If a received extension negotiation offer doesn't have the
"client_max_window_bits" extension parameter, the corresponding
extension negotiation response to the offer MUST NOT include the
"client_max_window_bits" extension parameter.
*/
if(o.client_max_window_bits == 15)
config.client_max_window_bits = 15;
else
config.accept = false;
break;
default:
// extension parameter has value in [8..15]
config.client_max_window_bits = (std::min)(
o.client_max_window_bits,
offer.client_max_window_bits);
s += "; client_max_window_bits=";
s += to_static_string(
config.client_max_window_bits);
break;
}
return s;
}
void
pmd_normalize(pmd_offer& offer)
{
if(offer.accept)
{
if( offer.server_max_window_bits == 0)
offer.server_max_window_bits = 15;
if( offer.client_max_window_bits == 0 ||
offer.client_max_window_bits == -1)
offer.client_max_window_bits = 15;
}
}
} // detail
} // websocket
} // beast
} // boost
#endif // BOOST_BEAST_WEBSOCKET_DETAIL_PMD_EXTENSION_IPP

View File

@@ -0,0 +1,123 @@
//
// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
#ifndef BOOST_BEAST_WEBSOCKET_DETAIL_PRNG_HPP
#define BOOST_BEAST_WEBSOCKET_DETAIL_PRNG_HPP
#include <boost/beast/core/detail/config.hpp>
#include <cstdint>
#include <limits>
#include <random>
namespace boost {
namespace beast {
namespace websocket {
namespace detail {
// Type-erased UniformRandomBitGenerator
// with 32-bit unsigned integer value_type
//
class prng
{
protected:
virtual ~prng() = default;
virtual prng& acquire() noexcept = 0;
virtual void release() noexcept = 0;
virtual std::uint32_t operator()() noexcept = 0;
public:
using value_type = std::uint32_t;
class ref
{
prng& p_;
public:
ref& operator=(ref const&) = delete;
~ref()
{
p_.release();
}
explicit
ref(prng& p) noexcept
: p_(p.acquire())
{
}
ref(ref const& other) noexcept
: p_(other.p_.acquire())
{
}
value_type
operator()() noexcept
{
return p_();
}
static
value_type constexpr
min() noexcept
{
return (std::numeric_limits<
value_type>::min)();
}
static
value_type constexpr
max() noexcept
{
return (std::numeric_limits<
value_type>::max)();
}
};
};
//------------------------------------------------------------------------------
// Manually seed the prngs, must be called
// before acquiring a prng for the first time.
//
BOOST_BEAST_DECL
std::uint32_t const*
prng_seed(std::seed_seq* ss = nullptr);
// Acquire a PRNG using the no-TLS implementation
//
BOOST_BEAST_DECL
prng::ref
make_prng_no_tls(bool secure);
// Acquire a PRNG using the TLS implementation
//
#ifndef BOOST_NO_CXX11_THREAD_LOCAL
BOOST_BEAST_DECL
prng::ref
make_prng_tls(bool secure);
#endif
// Acquire a PRNG using the TLS implementation if it
// is available, otherwise using the no-TLS implementation.
//
BOOST_BEAST_DECL
prng::ref
make_prng(bool secure);
} // detail
} // websocket
} // beast
} // boost
#ifdef BOOST_BEAST_HEADER_ONLY
#include <boost/beast/websocket/detail/prng.ipp>
#endif
#endif

View File

@@ -0,0 +1,327 @@
//
// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
#ifndef BOOST_BEAST_WEBSOCKET_DETAIL_PRNG_IPP
#define BOOST_BEAST_WEBSOCKET_DETAIL_PRNG_IPP
#include <boost/beast/websocket/detail/prng.hpp>
#include <boost/beast/core/detail/chacha.hpp>
#include <boost/beast/core/detail/pcg.hpp>
#include <boost/align/aligned_alloc.hpp>
#include <boost/throw_exception.hpp>
#include <atomic>
#include <cstdlib>
#include <mutex>
#include <new>
#include <random>
#include <stdexcept>
namespace boost {
namespace beast {
namespace websocket {
namespace detail {
//------------------------------------------------------------------------------
std::uint32_t const*
prng_seed(std::seed_seq* ss)
{
struct data
{
std::uint32_t v[8];
explicit
data(std::seed_seq* pss)
{
if(! pss)
{
std::random_device g;
std::seed_seq ss{
g(), g(), g(), g(),
g(), g(), g(), g()};
ss.generate(v, v+8);
}
else
{
pss->generate(v, v+8);
}
}
};
static data const d(ss);
return d.v;
}
//------------------------------------------------------------------------------
template<class T>
class prng_pool
{
std::mutex m_;
T* head_ = nullptr;
public:
static
prng_pool&
instance()
{
static prng_pool p;
return p;
}
~prng_pool()
{
for(auto p = head_; p;)
{
auto next = p->next;
p->~T();
boost::alignment::aligned_free(p);
p = next;
}
}
prng::ref
acquire()
{
{
std::lock_guard<
std::mutex> g(m_);
if(head_)
{
auto p = head_;
head_ = head_->next;
return prng::ref(*p);
}
}
auto p = boost::alignment::aligned_alloc(
16, sizeof(T));
if(! p)
BOOST_THROW_EXCEPTION(std::bad_alloc{});
return prng::ref(*(::new(p) T()));
}
void
release(T& t)
{
std::lock_guard<
std::mutex> g(m_);
t.next = head_;
head_ = &t;
}
};
prng::ref
make_prng_no_tls(bool secure)
{
class fast_prng final : public prng
{
int refs_ = 0;
beast::detail::pcg r_;
public:
fast_prng* next = nullptr;
fast_prng()
: r_(
[]{
auto const pv = prng_seed();
return
((static_cast<std::uint64_t>(pv[0])<<32)+pv[1]) ^
((static_cast<std::uint64_t>(pv[2])<<32)+pv[3]) ^
((static_cast<std::uint64_t>(pv[4])<<32)+pv[5]) ^
((static_cast<std::uint64_t>(pv[6])<<32)+pv[7]);
}(),
[]{
static std::atomic<
std::uint32_t> nonce{0};
return ++nonce;
}())
{
}
protected:
prng&
acquire() noexcept override
{
++refs_;
return *this;
}
void
release() noexcept override
{
if(--refs_ == 0)
prng_pool<fast_prng>::instance().release(*this);
}
value_type
operator()() noexcept override
{
return r_();
}
};
class secure_prng final : public prng
{
int refs_ = 0;
beast::detail::chacha<20> r_;
public:
secure_prng* next = nullptr;
secure_prng()
: r_(prng_seed(), []
{
static std::atomic<
std::uint64_t> nonce{0};
return ++nonce;
}())
{
}
protected:
prng&
acquire() noexcept override
{
++refs_;
return *this;
}
void
release() noexcept override
{
if(--refs_ == 0)
prng_pool<secure_prng>::instance().release(*this);
}
value_type
operator()() noexcept override
{
return r_();
}
};
if(secure)
return prng_pool<secure_prng>::instance().acquire();
return prng_pool<fast_prng>::instance().acquire();
}
//------------------------------------------------------------------------------
#ifndef BOOST_NO_CXX11_THREAD_LOCAL
prng::ref
make_prng_tls(bool secure)
{
class fast_prng final : public prng
{
beast::detail::pcg r_;
public:
fast_prng()
: r_(
[]{
auto const pv = prng_seed();
return
((static_cast<std::uint64_t>(pv[0])<<32)+pv[1]) ^
((static_cast<std::uint64_t>(pv[2])<<32)+pv[3]) ^
((static_cast<std::uint64_t>(pv[4])<<32)+pv[5]) ^
((static_cast<std::uint64_t>(pv[6])<<32)+pv[7]);
}(),
[]{
static std::atomic<
std::uint32_t> nonce{0};
return ++nonce;
}())
{
}
protected:
prng&
acquire() noexcept override
{
return *this;
}
void
release() noexcept override
{
}
value_type
operator()() noexcept override
{
return r_();
}
};
class secure_prng final : public prng
{
beast::detail::chacha<20> r_;
public:
secure_prng()
: r_(prng_seed(), []
{
static std::atomic<
std::uint64_t> nonce{0};
return ++nonce;
}())
{
}
protected:
prng&
acquire() noexcept override
{
return *this;
}
void
release() noexcept override
{
}
value_type
operator()() noexcept override
{
return r_();
}
};
if(secure)
{
thread_local secure_prng sp;
return prng::ref(sp);
}
thread_local fast_prng fp;
return prng::ref(fp);
}
#endif
//------------------------------------------------------------------------------
prng::ref
make_prng(bool secure)
{
#ifdef BOOST_NO_CXX11_THREAD_LOCAL
return make_prng_no_tls(secure);
#else
return make_prng_tls(secure);
#endif
}
} // detail
} // websocket
} // beast
} // boost
#endif

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