mirror of
https://github.com/vdemydiuk/mtapi.git
synced 2026-08-13 18:58:09 +00:00
Added thirdparty: boost library
This commit is contained in:
+315
@@ -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/core/exchange.hpp>
|
||||
#include <boost/type_traits/aligned_storage.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 boost::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
|
||||
+247
@@ -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
|
||||
BOOST_BEAST_ASSIGN_EC(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
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_close_code);
|
||||
return;
|
||||
}
|
||||
|
||||
if(n > 2 && !check_utf8(
|
||||
cr.reason.data(), cr.reason.size()))
|
||||
{
|
||||
// not valid utf-8
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_close_payload);
|
||||
return;
|
||||
}
|
||||
ec = {};
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // websocket
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
+48
@@ -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
|
||||
+62
@@ -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::static_capacity);
|
||||
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
|
||||
+497
@@ -0,0 +1,497 @@
|
||||
//
|
||||
// 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::sync, 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;
|
||||
}
|
||||
|
||||
bool should_compress(std::size_t n_bytes) const
|
||||
{
|
||||
return n_bytes >= pmd_opts_.msg_size_threshold;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
bool should_compress(std::size_t) 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
|
||||
+63
@@ -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
|
||||
+66
@@ -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
|
||||
+125
@@ -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, to_string_view(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, to_string_view(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
|
||||
+310
@@ -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
|
||||
+50
@@ -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
|
||||
+151
@@ -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
|
||||
+78
@@ -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
|
||||
+65
@@ -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
|
||||
+112
@@ -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
|
||||
+36
@@ -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
|
||||
+96
@@ -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
|
||||
+331
@@ -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
|
||||
Reference in New Issue
Block a user