mirror of
https://github.com/boostorg/beast.git
synced 2025-08-02 06:15:24 +02:00
Add websocket::stream timeouts
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
Version 216:
|
Version 216:
|
||||||
|
|
||||||
* Refactor websocket::stream operations
|
* Refactor websocket::stream operations
|
||||||
|
* Add websocket::stream timeouts
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
#ifndef BOOST_BEAST_TEST_FAIL_COUNT_HPP
|
#ifndef BOOST_BEAST_TEST_FAIL_COUNT_HPP
|
||||||
#define BOOST_BEAST_TEST_FAIL_COUNT_HPP
|
#define BOOST_BEAST_TEST_FAIL_COUNT_HPP
|
||||||
|
|
||||||
|
#include <boost/beast/core/detail/config.hpp>
|
||||||
#include <boost/beast/core/error.hpp>
|
#include <boost/beast/core/error.hpp>
|
||||||
#include <boost/beast/_experimental/test/error.hpp>
|
#include <boost/beast/_experimental/test/error.hpp>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
@@ -62,7 +62,7 @@ public:
|
|||||||
|
|
||||||
template<class... Args>
|
template<class... Args>
|
||||||
void
|
void
|
||||||
operator()(error_code ec, Args&&... args)
|
operator()(error_code ec, Args&&...)
|
||||||
{
|
{
|
||||||
BEAST_EXPECT(! pass_); // can't call twice
|
BEAST_EXPECT(! pass_); // can't call twice
|
||||||
BEAST_EXPECTS(! ec_ || ec == *ec_,
|
BEAST_EXPECTS(! ec_ || ec == *ec_,
|
||||||
|
@@ -629,6 +629,12 @@ connect(stream& to)
|
|||||||
return from;
|
return from;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
connect(stream& s1, stream& s2)
|
||||||
|
{
|
||||||
|
s1.connect(s2);
|
||||||
|
}
|
||||||
|
|
||||||
template<class Arg1, class... ArgN>
|
template<class Arg1, class... ArgN>
|
||||||
stream
|
stream
|
||||||
connect(stream& to, Arg1&& arg1, ArgN&&... argn)
|
connect(stream& to, Arg1&& arg1, ArgN&&... argn)
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
#ifndef BOOST_BEAST_TEST_STREAM_HPP
|
#ifndef BOOST_BEAST_TEST_STREAM_HPP
|
||||||
#define BOOST_BEAST_TEST_STREAM_HPP
|
#define BOOST_BEAST_TEST_STREAM_HPP
|
||||||
|
|
||||||
|
#include <boost/beast/core/detail/config.hpp>
|
||||||
#include <boost/beast/core/bind_handler.hpp>
|
#include <boost/beast/core/bind_handler.hpp>
|
||||||
#include <boost/beast/core/flat_buffer.hpp>
|
#include <boost/beast/core/flat_buffer.hpp>
|
||||||
#include <boost/beast/core/string.hpp>
|
#include <boost/beast/core/string.hpp>
|
||||||
@@ -512,6 +513,10 @@ BOOST_BEAST_DECL
|
|||||||
stream
|
stream
|
||||||
connect(stream& to);
|
connect(stream& to);
|
||||||
|
|
||||||
|
BOOST_BEAST_DECL
|
||||||
|
void
|
||||||
|
connect(stream& s1, stream& s2);
|
||||||
|
|
||||||
template<class Arg1, class... ArgN>
|
template<class Arg1, class... ArgN>
|
||||||
stream
|
stream
|
||||||
connect(stream& to, Arg1&& arg1, ArgN&&... argn);
|
connect(stream& to, Arg1&& arg1, ArgN&&... argn);
|
||||||
|
110
include/boost/beast/_experimental/test/tcp.hpp
Normal file
110
include/boost/beast/_experimental/test/tcp.hpp
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 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_TEST_TCP_HPP
|
||||||
|
#define BOOST_BEAST_TEST_TCP_HPP
|
||||||
|
|
||||||
|
#include <boost/beast/core/detail/config.hpp>
|
||||||
|
#include <boost/beast/_experimental/unit_test/suite.hpp>
|
||||||
|
#include <boost/beast/_experimental/test/handler.hpp>
|
||||||
|
#include <boost/asio/ip/tcp.hpp>
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
namespace boost {
|
||||||
|
namespace beast {
|
||||||
|
namespace test {
|
||||||
|
|
||||||
|
/** Run an I/O context.
|
||||||
|
|
||||||
|
This function runs and dispatches handlers on the specified
|
||||||
|
I/O context, until one of the following conditions is true:
|
||||||
|
|
||||||
|
@li The I/O context runs out of work.
|
||||||
|
*/
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
run(net::io_context& ioc)
|
||||||
|
{
|
||||||
|
ioc.run();
|
||||||
|
ioc.restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Run an I/O context for a certain amount of time.
|
||||||
|
|
||||||
|
This function runs and dispatches handlers on the specified
|
||||||
|
I/O context, until one of the following conditions is true:
|
||||||
|
|
||||||
|
@li The I/O context runs out of work.
|
||||||
|
|
||||||
|
@li No completions occur and the specified amount of time has elapsed.
|
||||||
|
|
||||||
|
@param elapsed The maximum amount of time to run for.
|
||||||
|
*/
|
||||||
|
template<class Rep, class Period>
|
||||||
|
void
|
||||||
|
run_for(
|
||||||
|
net::io_context& ioc,
|
||||||
|
std::chrono::duration<Rep, Period> elapsed)
|
||||||
|
{
|
||||||
|
ioc.run_for(elapsed);
|
||||||
|
ioc.restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Connect two TCP/IP sockets together.
|
||||||
|
*/
|
||||||
|
inline
|
||||||
|
bool
|
||||||
|
connect(
|
||||||
|
net::ip::tcp::socket& s1,
|
||||||
|
net::ip::tcp::socket& s2)
|
||||||
|
|
||||||
|
{
|
||||||
|
// Sockets must use the same I/O context
|
||||||
|
BOOST_ASSERT(
|
||||||
|
std::addressof(s1.get_executor().context()) ==
|
||||||
|
std::addressof(s2.get_executor().context()));
|
||||||
|
auto& ioc = s1.get_executor().context();
|
||||||
|
s1 = net::ip::tcp::socket(ioc);
|
||||||
|
s2 = net::ip::tcp::socket(ioc);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
net::ip::tcp::acceptor a(
|
||||||
|
s1.get_executor().context());
|
||||||
|
auto ep = net::ip::tcp::endpoint(
|
||||||
|
net::ip::make_address_v4("127.0.0.1"), 0);
|
||||||
|
a.open(ep.protocol());
|
||||||
|
a.set_option(
|
||||||
|
net::socket_base::reuse_address(true));
|
||||||
|
a.bind(ep);
|
||||||
|
a.listen(0);
|
||||||
|
ep = a.local_endpoint();
|
||||||
|
a.async_accept(s2, test::success_handler());
|
||||||
|
s1.async_connect(ep, test::success_handler());
|
||||||
|
run(ioc);
|
||||||
|
if(! BEAST_EXPECT(
|
||||||
|
s1.remote_endpoint() == s2.local_endpoint()))
|
||||||
|
return false;
|
||||||
|
if(! BEAST_EXPECT(
|
||||||
|
s2.remote_endpoint() == s1.local_endpoint()))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
catch(std::exception const& e)
|
||||||
|
{
|
||||||
|
beast::unit_test::suite::this_suite()->fail(
|
||||||
|
e.what(), __FILE__, __LINE__);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // test
|
||||||
|
} // beast
|
||||||
|
} // boost
|
||||||
|
|
||||||
|
#endif
|
@@ -347,8 +347,8 @@ public:
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
wg1_.reset();
|
|
||||||
h_(std::forward<Args>(args)...);
|
h_(std::forward<Args>(args)...);
|
||||||
|
wg1_.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -18,6 +18,7 @@
|
|||||||
#include <boost/asio/basic_stream_socket.hpp>
|
#include <boost/asio/basic_stream_socket.hpp>
|
||||||
#include <boost/asio/connect.hpp>
|
#include <boost/asio/connect.hpp>
|
||||||
#include <boost/asio/executor.hpp>
|
#include <boost/asio/executor.hpp>
|
||||||
|
#include <boost/asio/is_executor.hpp>
|
||||||
#include <boost/core/empty_value.hpp>
|
#include <boost/core/empty_value.hpp>
|
||||||
#include <boost/config/workaround.hpp>
|
#include <boost/config/workaround.hpp>
|
||||||
#include <boost/optional.hpp>
|
#include <boost/optional.hpp>
|
||||||
@@ -195,6 +196,9 @@ class basic_stream
|
|||||||
: private detail::stream_base
|
: private detail::stream_base
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
|
static_assert(net::is_executor<Executor>::value,
|
||||||
|
"Executor requirements not met");
|
||||||
|
|
||||||
// friend class template declaration in a class template is ignored
|
// friend class template declaration in a class template is ignored
|
||||||
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=88672
|
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=88672
|
||||||
#if BOOST_WORKAROUND(BOOST_GCC, > 0)
|
#if BOOST_WORKAROUND(BOOST_GCC, > 0)
|
||||||
|
@@ -14,7 +14,7 @@
|
|||||||
#include <boost/beast/core/string.hpp>
|
#include <boost/beast/core/string.hpp>
|
||||||
#include <boost/beast/core/detail/base64.hpp>
|
#include <boost/beast/core/detail/base64.hpp>
|
||||||
#include <boost/beast/core/detail/sha1.hpp>
|
#include <boost/beast/core/detail/sha1.hpp>
|
||||||
#include <boost/beast/websocket/detail/stream_base.hpp>
|
#include <boost/beast/websocket/detail/prng.hpp>
|
||||||
#include <boost/assert.hpp>
|
#include <boost/assert.hpp>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
@@ -53,7 +53,8 @@ make_sec_ws_key(sec_ws_key_type& key)
|
|||||||
|
|
||||||
template<class = void>
|
template<class = void>
|
||||||
void
|
void
|
||||||
make_sec_ws_accept(sec_ws_accept_type& accept,
|
make_sec_ws_accept(
|
||||||
|
sec_ws_accept_type& accept,
|
||||||
string_view key)
|
string_view key)
|
||||||
{
|
{
|
||||||
BOOST_ASSERT(key.size() <= sec_ws_key_type::max_size_n);
|
BOOST_ASSERT(key.size() <= sec_ws_key_type::max_size_n);
|
||||||
|
@@ -7,8 +7,8 @@
|
|||||||
// Official repository: https://github.com/boostorg/beast
|
// Official repository: https://github.com/boostorg/beast
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef BOOST_BEAST_WEBSOCKET_DETAIL_STREAM_BASE_HPP
|
#ifndef BOOST_BEAST_WEBSOCKET_DETAIL_IMPL_BASE_HPP
|
||||||
#define BOOST_BEAST_WEBSOCKET_DETAIL_STREAM_BASE_HPP
|
#define BOOST_BEAST_WEBSOCKET_DETAIL_IMPL_BASE_HPP
|
||||||
|
|
||||||
#include <boost/beast/core/buffer_size.hpp>
|
#include <boost/beast/core/buffer_size.hpp>
|
||||||
#include <boost/beast/http/empty_body.hpp>
|
#include <boost/beast/http/empty_body.hpp>
|
||||||
@@ -17,9 +17,7 @@
|
|||||||
#include <boost/beast/websocket/option.hpp>
|
#include <boost/beast/websocket/option.hpp>
|
||||||
#include <boost/beast/websocket/role.hpp>
|
#include <boost/beast/websocket/role.hpp>
|
||||||
#include <boost/beast/websocket/detail/frame.hpp>
|
#include <boost/beast/websocket/detail/frame.hpp>
|
||||||
#include <boost/beast/websocket/detail/prng.hpp>
|
|
||||||
#include <boost/beast/websocket/detail/pmd_extension.hpp>
|
#include <boost/beast/websocket/detail/pmd_extension.hpp>
|
||||||
#include <boost/beast/websocket/detail/prng.hpp>
|
|
||||||
#include <boost/beast/zlib/deflate_stream.hpp>
|
#include <boost/beast/zlib/deflate_stream.hpp>
|
||||||
#include <boost/beast/zlib/inflate_stream.hpp>
|
#include <boost/beast/zlib/inflate_stream.hpp>
|
||||||
#include <boost/beast/core/buffers_suffix.hpp>
|
#include <boost/beast/core/buffers_suffix.hpp>
|
||||||
@@ -489,31 +487,6 @@ struct impl_base<false>
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
struct stream_base
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
enum class status
|
|
||||||
{
|
|
||||||
open,
|
|
||||||
closing,
|
|
||||||
closed,
|
|
||||||
failed
|
|
||||||
};
|
|
||||||
|
|
||||||
std::uint32_t
|
|
||||||
create_mask()
|
|
||||||
{
|
|
||||||
auto g = make_prng(secure_prng_);
|
|
||||||
for(;;)
|
|
||||||
if(auto key = g())
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool secure_prng_ = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // detail
|
} // detail
|
||||||
} // websocket
|
} // websocket
|
||||||
} // beast
|
} // beast
|
@@ -11,6 +11,7 @@
|
|||||||
#define BOOST_BEAST_WEBSOCKET_IMPL_ACCEPT_IPP
|
#define BOOST_BEAST_WEBSOCKET_IMPL_ACCEPT_IPP
|
||||||
|
|
||||||
#include <boost/beast/core/buffer_size.hpp>
|
#include <boost/beast/core/buffer_size.hpp>
|
||||||
|
#include <boost/beast/websocket/impl/stream_impl.hpp>
|
||||||
#include <boost/beast/websocket/detail/type_traits.hpp>
|
#include <boost/beast/websocket/detail/type_traits.hpp>
|
||||||
#include <boost/beast/http/empty_body.hpp>
|
#include <boost/beast/http/empty_body.hpp>
|
||||||
#include <boost/beast/http/parser.hpp>
|
#include <boost/beast/http/parser.hpp>
|
||||||
@@ -42,7 +43,7 @@ class stream<NextLayer, deflateSupported>::response_op
|
|||||||
Handler, beast::executor_type<stream>>
|
Handler, beast::executor_type<stream>>
|
||||||
, public net::coroutine
|
, public net::coroutine
|
||||||
{
|
{
|
||||||
stream<NextLayer, deflateSupported>& ws_;
|
boost::weak_ptr<impl_type> wp_;
|
||||||
error_code result_; // must come before res_
|
error_code result_; // must come before res_
|
||||||
response_type& res_;
|
response_type& res_;
|
||||||
|
|
||||||
@@ -53,37 +54,53 @@ public:
|
|||||||
class Decorator>
|
class Decorator>
|
||||||
response_op(
|
response_op(
|
||||||
Handler_&& h,
|
Handler_&& h,
|
||||||
stream<NextLayer, deflateSupported>& ws,
|
boost::shared_ptr<impl_type> const& sp,
|
||||||
http::request<Body, http::basic_fields<Allocator>> const& req,
|
http::request<Body,
|
||||||
Decorator const& decorator)
|
http::basic_fields<Allocator>> const& req,
|
||||||
: stable_async_op_base<
|
Decorator const& decorator,
|
||||||
Handler, beast::executor_type<stream>>(
|
bool cont = false)
|
||||||
std::forward<Handler_>(h), ws.get_executor())
|
: stable_async_op_base<Handler,
|
||||||
, ws_(ws)
|
beast::executor_type<stream>>(
|
||||||
|
std::forward<Handler_>(h),
|
||||||
|
sp->stream.get_executor())
|
||||||
|
, wp_(sp)
|
||||||
, res_(beast::allocate_stable<response_type>(*this,
|
, res_(beast::allocate_stable<response_type>(*this,
|
||||||
ws.build_response(req, decorator, result_)))
|
sp->build_response(req, decorator, result_)))
|
||||||
{
|
{
|
||||||
|
(*this)({}, 0, cont);
|
||||||
}
|
}
|
||||||
|
|
||||||
void operator()(
|
void operator()(
|
||||||
error_code ec = {},
|
error_code ec = {},
|
||||||
std::size_t bytes_transferred = 0)
|
std::size_t bytes_transferred = 0,
|
||||||
|
bool cont = true)
|
||||||
{
|
{
|
||||||
boost::ignore_unused(bytes_transferred);
|
boost::ignore_unused(bytes_transferred);
|
||||||
|
auto sp = wp_.lock();
|
||||||
|
if(! sp)
|
||||||
|
return this->invoke(cont,
|
||||||
|
net::error::operation_aborted);
|
||||||
|
auto& impl = *sp;
|
||||||
BOOST_ASIO_CORO_REENTER(*this)
|
BOOST_ASIO_CORO_REENTER(*this)
|
||||||
{
|
{
|
||||||
|
impl.change_status(status::handshake);
|
||||||
|
impl.update_timer(this->get_executor());
|
||||||
|
|
||||||
// Send response
|
// Send response
|
||||||
BOOST_ASIO_CORO_YIELD
|
BOOST_ASIO_CORO_YIELD
|
||||||
http::async_write(
|
http::async_write(
|
||||||
ws_.next_layer(), res_, std::move(*this));
|
impl.stream, res_, std::move(*this));
|
||||||
|
if(impl.check_stop_now(ec))
|
||||||
|
goto upcall;
|
||||||
if(! ec)
|
if(! ec)
|
||||||
ec = result_;
|
ec = result_;
|
||||||
if(! ec)
|
if(! ec)
|
||||||
{
|
{
|
||||||
ws_.impl_->do_pmd_config(res_);
|
impl.do_pmd_config(res_);
|
||||||
ws_.impl_->open(role_type::server);
|
impl.open(role_type::server);
|
||||||
}
|
}
|
||||||
this->invoke_now(ec);
|
upcall:
|
||||||
|
this->invoke(cont, ec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -99,90 +116,144 @@ class stream<NextLayer, deflateSupported>::accept_op
|
|||||||
Handler, beast::executor_type<stream>>
|
Handler, beast::executor_type<stream>>
|
||||||
, public net::coroutine
|
, public net::coroutine
|
||||||
{
|
{
|
||||||
stream<NextLayer, deflateSupported>& ws_;
|
boost::weak_ptr<impl_type> wp_;
|
||||||
http::request_parser<http::empty_body>& p_;
|
http::request_parser<http::empty_body>& p_;
|
||||||
Decorator d_;
|
Decorator d_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
template<class Handler_>
|
template<class Handler_, class Buffers>
|
||||||
accept_op(
|
accept_op(
|
||||||
Handler_&& h,
|
Handler_&& h,
|
||||||
stream<NextLayer, deflateSupported>& ws,
|
boost::shared_ptr<impl_type> const& sp,
|
||||||
|
Buffers const& buffers,
|
||||||
Decorator const& decorator)
|
Decorator const& decorator)
|
||||||
: stable_async_op_base<
|
: stable_async_op_base<Handler,
|
||||||
Handler, beast::executor_type<stream>>(
|
beast::executor_type<stream>>(
|
||||||
std::forward<Handler_>(h), ws.get_executor())
|
std::forward<Handler_>(h),
|
||||||
, ws_(ws)
|
sp->stream.get_executor())
|
||||||
|
, wp_(sp)
|
||||||
, p_(beast::allocate_stable<
|
, p_(beast::allocate_stable<
|
||||||
http::request_parser<http::empty_body>>(*this))
|
http::request_parser<http::empty_body>>(*this))
|
||||||
, d_(decorator)
|
, d_(decorator)
|
||||||
{
|
{
|
||||||
}
|
auto& impl = *sp;
|
||||||
|
|
||||||
template<class Buffers>
|
|
||||||
void run(Buffers const& buffers)
|
|
||||||
{
|
|
||||||
error_code ec;
|
error_code ec;
|
||||||
auto const mb = beast::detail::dynamic_buffer_prepare(
|
auto const mb =
|
||||||
ws_.impl_->rd_buf, buffer_size(buffers), ec,
|
beast::detail::dynamic_buffer_prepare(
|
||||||
error::buffer_overflow);
|
impl.rd_buf, buffer_size(buffers),
|
||||||
if(ec)
|
ec, error::buffer_overflow);
|
||||||
return (*this)(ec);
|
if(! ec)
|
||||||
ws_.impl_->rd_buf.commit(net::buffer_copy(*mb, buffers));
|
impl.rd_buf.commit(
|
||||||
|
net::buffer_copy(*mb, buffers));
|
||||||
(*this)(ec);
|
(*this)(ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
void operator()(
|
void operator()(
|
||||||
error_code ec = {},
|
error_code ec = {},
|
||||||
std::size_t bytes_transferred = 0)
|
std::size_t bytes_transferred = 0,
|
||||||
|
bool cont = true)
|
||||||
{
|
{
|
||||||
boost::ignore_unused(bytes_transferred);
|
boost::ignore_unused(bytes_transferred);
|
||||||
|
auto sp = wp_.lock();
|
||||||
|
if(! sp)
|
||||||
|
return this->invoke(cont,
|
||||||
|
net::error::operation_aborted);
|
||||||
|
auto& impl = *sp;
|
||||||
BOOST_ASIO_CORO_REENTER(*this)
|
BOOST_ASIO_CORO_REENTER(*this)
|
||||||
{
|
{
|
||||||
|
impl.change_status(status::handshake);
|
||||||
|
impl.update_timer(this->get_executor());
|
||||||
|
|
||||||
|
// The constructor could have set ec
|
||||||
if(ec)
|
if(ec)
|
||||||
|
goto upcall;
|
||||||
|
|
||||||
|
BOOST_ASIO_CORO_YIELD
|
||||||
|
http::async_read(impl.stream,
|
||||||
|
impl.rd_buf, p_, std::move(*this));
|
||||||
|
if(ec == http::error::end_of_stream)
|
||||||
|
ec = error::closed;
|
||||||
|
if(impl.check_stop_now(ec))
|
||||||
|
goto upcall;
|
||||||
|
|
||||||
{
|
{
|
||||||
BOOST_ASIO_CORO_YIELD
|
// Arguments from our state must be
|
||||||
net::post(
|
// moved to the stack before releasing
|
||||||
ws_.get_executor(),
|
// the handler.
|
||||||
beast::bind_front_handler(std::move(*this), ec));
|
auto const req = p_.release();
|
||||||
|
auto const decorator = d_;
|
||||||
|
response_op<Handler>(
|
||||||
|
this->release_handler(),
|
||||||
|
sp, req, decorator, true);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
upcall:
|
||||||
BOOST_ASIO_CORO_YIELD
|
this->invoke(cont, ec);
|
||||||
http::async_read(
|
|
||||||
ws_.next_layer(), ws_.impl_->rd_buf,
|
|
||||||
p_, std::move(*this));
|
|
||||||
if(ec == http::error::end_of_stream)
|
|
||||||
ec = error::closed;
|
|
||||||
if(! ec)
|
|
||||||
{
|
|
||||||
// Arguments from our state must be
|
|
||||||
// moved to the stack before releasing
|
|
||||||
// the handler.
|
|
||||||
auto& ws = ws_;
|
|
||||||
auto const req = p_.release();
|
|
||||||
auto const decorator = d_;
|
|
||||||
#if 1
|
|
||||||
return response_op<Handler>{
|
|
||||||
this->release_handler(),
|
|
||||||
ws, req, decorator}(ec);
|
|
||||||
#else
|
|
||||||
// VFALCO This *should* work but breaks
|
|
||||||
// coroutine invariants in the unit test.
|
|
||||||
// Also it calls reset() when it shouldn't.
|
|
||||||
return ws.async_accept_ex(
|
|
||||||
req, decorator, this->release_handler());
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this->invoke_now(ec);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template<class NextLayer, bool deflateSupported>
|
||||||
|
template<class Body, class Allocator,
|
||||||
|
class Decorator>
|
||||||
|
void
|
||||||
|
stream<NextLayer, deflateSupported>::
|
||||||
|
do_accept(
|
||||||
|
http::request<Body,
|
||||||
|
http::basic_fields<Allocator>> const& req,
|
||||||
|
Decorator const& decorator,
|
||||||
|
error_code& ec)
|
||||||
|
{
|
||||||
|
impl_->change_status(status::handshake);
|
||||||
|
|
||||||
|
error_code result;
|
||||||
|
auto const res = impl_->build_response(req, decorator, result);
|
||||||
|
http::write(impl_->stream, res, ec);
|
||||||
|
if(ec)
|
||||||
|
return;
|
||||||
|
ec = result;
|
||||||
|
if(ec)
|
||||||
|
{
|
||||||
|
// VFALCO TODO Respect keep alive setting, perform
|
||||||
|
// teardown if Connection: close.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
impl_->do_pmd_config(res);
|
||||||
|
impl_->open(role_type::server);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer, bool deflateSupported>
|
||||||
|
template<class Buffers, class Decorator>
|
||||||
|
void
|
||||||
|
stream<NextLayer, deflateSupported>::
|
||||||
|
do_accept(
|
||||||
|
Buffers const& buffers,
|
||||||
|
Decorator const& decorator,
|
||||||
|
error_code& ec)
|
||||||
|
{
|
||||||
|
impl_->reset();
|
||||||
|
auto const mb =
|
||||||
|
beast::detail::dynamic_buffer_prepare(
|
||||||
|
impl_->rd_buf, buffer_size(buffers), ec,
|
||||||
|
error::buffer_overflow);
|
||||||
|
if(ec)
|
||||||
|
return;
|
||||||
|
impl_->rd_buf.commit(net::buffer_copy(*mb, buffers));
|
||||||
|
|
||||||
|
http::request_parser<http::empty_body> p;
|
||||||
|
http::read(next_layer(), impl_->rd_buf, p, ec);
|
||||||
|
if(ec == http::error::end_of_stream)
|
||||||
|
ec = error::closed;
|
||||||
|
if(ec)
|
||||||
|
return;
|
||||||
|
do_accept(p.get(), decorator, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
template<class NextLayer, bool deflateSupported>
|
template<class NextLayer, bool deflateSupported>
|
||||||
void
|
void
|
||||||
stream<NextLayer, deflateSupported>::
|
stream<NextLayer, deflateSupported>::
|
||||||
@@ -220,8 +291,9 @@ accept(error_code& ec)
|
|||||||
{
|
{
|
||||||
static_assert(is_sync_stream<next_layer_type>::value,
|
static_assert(is_sync_stream<next_layer_type>::value,
|
||||||
"SyncStream requirements not met");
|
"SyncStream requirements not met");
|
||||||
impl_->reset();
|
do_accept(
|
||||||
do_accept(&default_decorate_res, ec);
|
net::const_buffer{},
|
||||||
|
&default_decorate_res, ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class NextLayer, bool deflateSupported>
|
template<class NextLayer, bool deflateSupported>
|
||||||
@@ -235,8 +307,9 @@ accept_ex(ResponseDecorator const& decorator, error_code& ec)
|
|||||||
static_assert(detail::is_response_decorator<
|
static_assert(detail::is_response_decorator<
|
||||||
ResponseDecorator>::value,
|
ResponseDecorator>::value,
|
||||||
"ResponseDecorator requirements not met");
|
"ResponseDecorator requirements not met");
|
||||||
impl_->reset();
|
do_accept(
|
||||||
do_accept(decorator, ec);
|
net::const_buffer{},
|
||||||
|
decorator, ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class NextLayer, bool deflateSupported>
|
template<class NextLayer, bool deflateSupported>
|
||||||
@@ -295,14 +368,7 @@ accept(
|
|||||||
static_assert(net::is_const_buffer_sequence<
|
static_assert(net::is_const_buffer_sequence<
|
||||||
ConstBufferSequence>::value,
|
ConstBufferSequence>::value,
|
||||||
"ConstBufferSequence requirements not met");
|
"ConstBufferSequence requirements not met");
|
||||||
impl_->reset();
|
do_accept(buffers, &default_decorate_res, ec);
|
||||||
auto const mb = beast::detail::dynamic_buffer_prepare(
|
|
||||||
impl_->rd_buf, buffer_size(buffers), ec,
|
|
||||||
error::buffer_overflow);
|
|
||||||
if(ec)
|
|
||||||
return;
|
|
||||||
impl_->rd_buf.commit(net::buffer_copy(*mb, buffers));
|
|
||||||
do_accept(&default_decorate_res, ec);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class NextLayer, bool deflateSupported>
|
template<class NextLayer, bool deflateSupported>
|
||||||
@@ -325,14 +391,7 @@ accept_ex(
|
|||||||
static_assert(net::is_const_buffer_sequence<
|
static_assert(net::is_const_buffer_sequence<
|
||||||
ConstBufferSequence>::value,
|
ConstBufferSequence>::value,
|
||||||
"ConstBufferSequence requirements not met");
|
"ConstBufferSequence requirements not met");
|
||||||
impl_->reset();
|
do_accept(buffers, decorator, ec);
|
||||||
auto const mb = beast::detail::dynamic_buffer_prepare(
|
|
||||||
impl_->rd_buf, buffer_size(buffers), ec,
|
|
||||||
error::buffer_overflow);
|
|
||||||
if(ec)
|
|
||||||
return;
|
|
||||||
impl_->rd_buf.commit(net::buffer_copy(*mb, buffers));
|
|
||||||
do_accept(decorator, ec);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class NextLayer, bool deflateSupported>
|
template<class NextLayer, bool deflateSupported>
|
||||||
@@ -428,10 +487,10 @@ async_accept(
|
|||||||
accept_op<
|
accept_op<
|
||||||
decltype(&default_decorate_res),
|
decltype(&default_decorate_res),
|
||||||
BOOST_ASIO_HANDLER_TYPE(
|
BOOST_ASIO_HANDLER_TYPE(
|
||||||
AcceptHandler, void(error_code))>{
|
AcceptHandler, void(error_code))>(
|
||||||
std::move(init.completion_handler),
|
std::move(init.completion_handler),
|
||||||
*this,
|
impl_, net::const_buffer{},
|
||||||
&default_decorate_res}({});
|
&default_decorate_res);;
|
||||||
return init.result.get();
|
return init.result.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -457,10 +516,10 @@ async_accept_ex(
|
|||||||
accept_op<
|
accept_op<
|
||||||
ResponseDecorator,
|
ResponseDecorator,
|
||||||
BOOST_ASIO_HANDLER_TYPE(
|
BOOST_ASIO_HANDLER_TYPE(
|
||||||
AcceptHandler, void(error_code))>{
|
AcceptHandler, void(error_code))>(
|
||||||
std::move(init.completion_handler),
|
std::move(init.completion_handler),
|
||||||
*this,
|
impl_, net::const_buffer{},
|
||||||
decorator}({});
|
decorator);
|
||||||
return init.result.get();
|
return init.result.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -488,10 +547,9 @@ async_accept(
|
|||||||
accept_op<
|
accept_op<
|
||||||
decltype(&default_decorate_res),
|
decltype(&default_decorate_res),
|
||||||
BOOST_ASIO_HANDLER_TYPE(
|
BOOST_ASIO_HANDLER_TYPE(
|
||||||
AcceptHandler, void(error_code))>{
|
AcceptHandler, void(error_code))>(
|
||||||
std::move(init.completion_handler),
|
std::move(init.completion_handler),
|
||||||
*this,
|
impl_, buffers, &default_decorate_res);
|
||||||
&default_decorate_res}.run(buffers);
|
|
||||||
return init.result.get();
|
return init.result.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -524,10 +582,9 @@ async_accept_ex(
|
|||||||
accept_op<
|
accept_op<
|
||||||
ResponseDecorator,
|
ResponseDecorator,
|
||||||
BOOST_ASIO_HANDLER_TYPE(
|
BOOST_ASIO_HANDLER_TYPE(
|
||||||
AcceptHandler, void(error_code))>{
|
AcceptHandler, void(error_code))>(
|
||||||
std::move(init.completion_handler),
|
std::move(init.completion_handler),
|
||||||
*this,
|
impl_, buffers, decorator);
|
||||||
decorator}.run(buffers);
|
|
||||||
return init.result.get();
|
return init.result.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -549,11 +606,9 @@ async_accept(
|
|||||||
impl_->reset();
|
impl_->reset();
|
||||||
response_op<
|
response_op<
|
||||||
BOOST_ASIO_HANDLER_TYPE(
|
BOOST_ASIO_HANDLER_TYPE(
|
||||||
AcceptHandler, void(error_code))>{
|
AcceptHandler, void(error_code))>(
|
||||||
std::move(init.completion_handler),
|
std::move(init.completion_handler),
|
||||||
*this,
|
impl_, req, &default_decorate_res);
|
||||||
req,
|
|
||||||
&default_decorate_res}();
|
|
||||||
return init.result.get();
|
return init.result.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -580,60 +635,12 @@ async_accept_ex(
|
|||||||
impl_->reset();
|
impl_->reset();
|
||||||
response_op<
|
response_op<
|
||||||
BOOST_ASIO_HANDLER_TYPE(
|
BOOST_ASIO_HANDLER_TYPE(
|
||||||
AcceptHandler, void(error_code))>{
|
AcceptHandler, void(error_code))>(
|
||||||
std::move(init.completion_handler),
|
std::move(init.completion_handler),
|
||||||
*this,
|
impl_, req, decorator);
|
||||||
req,
|
|
||||||
decorator}();
|
|
||||||
return init.result.get();
|
return init.result.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
template<class NextLayer, bool deflateSupported>
|
|
||||||
template<class Decorator>
|
|
||||||
void
|
|
||||||
stream<NextLayer, deflateSupported>::
|
|
||||||
do_accept(
|
|
||||||
Decorator const& decorator,
|
|
||||||
error_code& ec)
|
|
||||||
{
|
|
||||||
http::request_parser<http::empty_body> p;
|
|
||||||
http::read(next_layer(), impl_->rd_buf, p, ec);
|
|
||||||
if(ec == http::error::end_of_stream)
|
|
||||||
ec = error::closed;
|
|
||||||
if(ec)
|
|
||||||
return;
|
|
||||||
do_accept(p.get(), decorator, ec);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class NextLayer, bool deflateSupported>
|
|
||||||
template<class Body, class Allocator,
|
|
||||||
class Decorator>
|
|
||||||
void
|
|
||||||
stream<NextLayer, deflateSupported>::
|
|
||||||
do_accept(
|
|
||||||
http::request<Body,
|
|
||||||
http::basic_fields<Allocator>> const& req,
|
|
||||||
Decorator const& decorator,
|
|
||||||
error_code& ec)
|
|
||||||
{
|
|
||||||
error_code result;
|
|
||||||
auto const res = build_response(req, decorator, result);
|
|
||||||
http::write(impl_->stream, res, ec);
|
|
||||||
if(ec)
|
|
||||||
return;
|
|
||||||
ec = result;
|
|
||||||
if(ec)
|
|
||||||
{
|
|
||||||
// VFALCO TODO Respect keep alive setting, perform
|
|
||||||
// teardown if Connection: close.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
impl_->do_pmd_config(res);
|
|
||||||
impl_->open(role_type::server);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // websocket
|
} // websocket
|
||||||
} // beast
|
} // beast
|
||||||
} // boost
|
} // boost
|
||||||
|
@@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
#include <boost/beast/websocket/teardown.hpp>
|
#include <boost/beast/websocket/teardown.hpp>
|
||||||
#include <boost/beast/websocket/detail/mask.hpp>
|
#include <boost/beast/websocket/detail/mask.hpp>
|
||||||
|
#include <boost/beast/websocket/impl/stream_impl.hpp>
|
||||||
#include <boost/beast/core/async_op_base.hpp>
|
#include <boost/beast/core/async_op_base.hpp>
|
||||||
#include <boost/beast/core/flat_static_buffer.hpp>
|
#include <boost/beast/core/flat_static_buffer.hpp>
|
||||||
#include <boost/beast/core/stream_traits.hpp>
|
#include <boost/beast/core/stream_traits.hpp>
|
||||||
@@ -39,26 +40,28 @@ class stream<NextLayer, deflateSupported>::close_op
|
|||||||
Handler, beast::executor_type<stream>>
|
Handler, beast::executor_type<stream>>
|
||||||
, public net::coroutine
|
, public net::coroutine
|
||||||
{
|
{
|
||||||
stream<NextLayer, deflateSupported>& ws_;
|
boost::weak_ptr<impl_type> wp_;
|
||||||
error_code ev_;
|
error_code ev_;
|
||||||
detail::frame_buffer& fb_;
|
detail::frame_buffer& fb_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static constexpr int id = 4; // for soft_mutex
|
static constexpr int id = 5; // for soft_mutex
|
||||||
|
|
||||||
template<class Handler_>
|
template<class Handler_>
|
||||||
close_op(
|
close_op(
|
||||||
Handler_&& h,
|
Handler_&& h,
|
||||||
stream<NextLayer, deflateSupported>& ws,
|
boost::shared_ptr<impl_type> const& sp,
|
||||||
close_reason const& cr)
|
close_reason const& cr)
|
||||||
: stable_async_op_base<
|
: stable_async_op_base<Handler,
|
||||||
Handler, beast::executor_type<stream>>(
|
beast::executor_type<stream>>(
|
||||||
std::forward<Handler_>(h), ws.get_executor())
|
std::forward<Handler_>(h),
|
||||||
, ws_(ws)
|
sp->stream.get_executor())
|
||||||
, fb_(beast::allocate_stable<detail::frame_buffer>(*this))
|
, wp_(sp)
|
||||||
|
, fb_(beast::allocate_stable<
|
||||||
|
detail::frame_buffer>(*this))
|
||||||
{
|
{
|
||||||
// Serialize the close frame
|
// Serialize the close frame
|
||||||
ws.template write_close<
|
sp->template write_close<
|
||||||
flat_static_buffer_base>(fb_, cr);
|
flat_static_buffer_base>(fb_, cr);
|
||||||
(*this)({}, 0, false);
|
(*this)({}, 0, false);
|
||||||
}
|
}
|
||||||
@@ -70,14 +73,18 @@ public:
|
|||||||
bool cont = true)
|
bool cont = true)
|
||||||
{
|
{
|
||||||
using beast::detail::clamp;
|
using beast::detail::clamp;
|
||||||
auto& impl = *ws_.impl_;
|
auto sp = wp_.lock();
|
||||||
|
if(! sp)
|
||||||
|
return this->invoke(cont,
|
||||||
|
net::error::operation_aborted);
|
||||||
|
auto& impl = *sp;
|
||||||
BOOST_ASIO_CORO_REENTER(*this)
|
BOOST_ASIO_CORO_REENTER(*this)
|
||||||
{
|
{
|
||||||
// Acquire the write lock
|
// Acquire the write lock
|
||||||
if(! impl.wr_block.try_lock(this))
|
if(! impl.wr_block.try_lock(this))
|
||||||
{
|
{
|
||||||
BOOST_ASIO_CORO_YIELD
|
BOOST_ASIO_CORO_YIELD
|
||||||
impl.paused_close.emplace(std::move(*this));
|
impl.op_close.emplace(std::move(*this));
|
||||||
impl.wr_block.lock(this);
|
impl.wr_block.lock(this);
|
||||||
BOOST_ASIO_CORO_YIELD
|
BOOST_ASIO_CORO_YIELD
|
||||||
net::post(std::move(*this));
|
net::post(std::move(*this));
|
||||||
@@ -93,6 +100,7 @@ public:
|
|||||||
// Send close frame
|
// Send close frame
|
||||||
impl.wr_close = true;
|
impl.wr_close = true;
|
||||||
impl.change_status(status::closing);
|
impl.change_status(status::closing);
|
||||||
|
impl.update_timer(this->get_executor());
|
||||||
BOOST_ASIO_CORO_YIELD
|
BOOST_ASIO_CORO_YIELD
|
||||||
net::async_write(impl.stream, fb_.data(),
|
net::async_write(impl.stream, fb_.data(),
|
||||||
beast::detail::bind_continuation(std::move(*this)));
|
beast::detail::bind_continuation(std::move(*this)));
|
||||||
@@ -111,7 +119,7 @@ public:
|
|||||||
if(! impl.rd_block.try_lock(this))
|
if(! impl.rd_block.try_lock(this))
|
||||||
{
|
{
|
||||||
BOOST_ASIO_CORO_YIELD
|
BOOST_ASIO_CORO_YIELD
|
||||||
impl.paused_r_close.emplace(std::move(*this));
|
impl.op_r_close.emplace(std::move(*this));
|
||||||
impl.rd_block.lock(this);
|
impl.rd_block.lock(this);
|
||||||
BOOST_ASIO_CORO_YIELD
|
BOOST_ASIO_CORO_YIELD
|
||||||
net::post(std::move(*this));
|
net::post(std::move(*this));
|
||||||
@@ -128,7 +136,7 @@ public:
|
|||||||
for(;;)
|
for(;;)
|
||||||
{
|
{
|
||||||
// Read frame header
|
// Read frame header
|
||||||
while(! ws_.parse_fh(
|
while(! impl.parse_fh(
|
||||||
impl.rd_fh, impl.rd_buf, ev_))
|
impl.rd_fh, impl.rd_buf, ev_))
|
||||||
{
|
{
|
||||||
if(ev_)
|
if(ev_)
|
||||||
@@ -212,10 +220,11 @@ public:
|
|||||||
upcall:
|
upcall:
|
||||||
impl.wr_block.unlock(this);
|
impl.wr_block.unlock(this);
|
||||||
impl.rd_block.try_unlock(this)
|
impl.rd_block.try_unlock(this)
|
||||||
&& impl.paused_r_rd.maybe_invoke();
|
&& impl.op_r_rd.maybe_invoke();
|
||||||
impl.paused_rd.maybe_invoke()
|
impl.op_rd.maybe_invoke()
|
||||||
|| impl.paused_ping.maybe_invoke()
|
|| impl.op_idle_ping.maybe_invoke()
|
||||||
|| impl.paused_wr.maybe_invoke();
|
|| impl.op_ping.maybe_invoke()
|
||||||
|
|| impl.op_wr.maybe_invoke();
|
||||||
this->invoke(cont, ec);
|
this->invoke(cont, ec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -259,7 +268,7 @@ close(close_reason const& cr, error_code& ec)
|
|||||||
impl.wr_close = true;
|
impl.wr_close = true;
|
||||||
impl.change_status(status::closing);
|
impl.change_status(status::closing);
|
||||||
detail::frame_buffer fb;
|
detail::frame_buffer fb;
|
||||||
write_close<flat_static_buffer_base>(fb, cr);
|
impl.template write_close<flat_static_buffer_base>(fb, cr);
|
||||||
net::write(impl.stream, fb.data(), ec);
|
net::write(impl.stream, fb.data(), ec);
|
||||||
if(impl.check_stop_now(ec))
|
if(impl.check_stop_now(ec))
|
||||||
return;
|
return;
|
||||||
@@ -272,7 +281,7 @@ close(close_reason const& cr, error_code& ec)
|
|||||||
for(;;)
|
for(;;)
|
||||||
{
|
{
|
||||||
// Read frame header
|
// Read frame header
|
||||||
while(! parse_fh(
|
while(! impl.parse_fh(
|
||||||
impl.rd_fh, impl.rd_buf, ev))
|
impl.rd_fh, impl.rd_buf, ev))
|
||||||
{
|
{
|
||||||
if(ev)
|
if(ev)
|
||||||
@@ -355,7 +364,7 @@ async_close(close_reason const& cr, CloseHandler&& handler)
|
|||||||
CloseHandler, void(error_code));
|
CloseHandler, void(error_code));
|
||||||
close_op<BOOST_ASIO_HANDLER_TYPE(
|
close_op<BOOST_ASIO_HANDLER_TYPE(
|
||||||
CloseHandler, void(error_code))>(
|
CloseHandler, void(error_code))>(
|
||||||
std::move(init.completion_handler), *this, cr);
|
std::move(init.completion_handler), impl_, cr);
|
||||||
return init.result.get();
|
return init.result.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
#ifndef BOOST_BEAST_WEBSOCKET_IMPL_HANDSHAKE_HPP
|
#ifndef BOOST_BEAST_WEBSOCKET_IMPL_HANDSHAKE_HPP
|
||||||
#define BOOST_BEAST_WEBSOCKET_IMPL_HANDSHAKE_HPP
|
#define BOOST_BEAST_WEBSOCKET_IMPL_HANDSHAKE_HPP
|
||||||
|
|
||||||
|
#include <boost/beast/websocket/impl/stream_impl.hpp>
|
||||||
#include <boost/beast/websocket/detail/type_traits.hpp>
|
#include <boost/beast/websocket/detail/type_traits.hpp>
|
||||||
#include <boost/beast/http/empty_body.hpp>
|
#include <boost/beast/http/empty_body.hpp>
|
||||||
#include <boost/beast/http/message.hpp>
|
#include <boost/beast/http/message.hpp>
|
||||||
@@ -45,7 +46,7 @@ class stream<NextLayer, deflateSupported>::handshake_op
|
|||||||
response_type res;
|
response_type res;
|
||||||
};
|
};
|
||||||
|
|
||||||
stream<NextLayer, deflateSupported>& ws_;
|
boost::weak_ptr<impl_type> wp_;
|
||||||
detail::sec_ws_key_type key_;
|
detail::sec_ws_key_type key_;
|
||||||
response_type* res_p_;
|
response_type* res_p_;
|
||||||
data& d_;
|
data& d_;
|
||||||
@@ -54,54 +55,103 @@ public:
|
|||||||
template<class Handler_, class Decorator>
|
template<class Handler_, class Decorator>
|
||||||
handshake_op(
|
handshake_op(
|
||||||
Handler_&& h,
|
Handler_&& h,
|
||||||
stream& ws,
|
boost::shared_ptr<impl_type> const& sp,
|
||||||
response_type* res_p,
|
response_type* res_p,
|
||||||
string_view host, string_view target,
|
string_view host, string_view target,
|
||||||
Decorator const& decorator)
|
Decorator const& decorator)
|
||||||
: stable_async_op_base<Handler,
|
: stable_async_op_base<Handler,
|
||||||
beast::executor_type<stream>>(
|
beast::executor_type<stream>>(
|
||||||
std::forward<Handler_>(h), ws.get_executor())
|
std::forward<Handler_>(h),
|
||||||
, ws_(ws)
|
sp->stream.get_executor())
|
||||||
|
, wp_(sp)
|
||||||
, res_p_(res_p)
|
, res_p_(res_p)
|
||||||
, d_(beast::allocate_stable<data>(*this))
|
, d_(beast::allocate_stable<data>(*this))
|
||||||
{
|
{
|
||||||
d_.req = ws_.build_request(
|
d_.req = sp->build_request(
|
||||||
key_, host, target, decorator);
|
key_, host, target, decorator);
|
||||||
ws_.impl_->reset(); // VFALCO I don't like this
|
sp->reset(); // VFALCO I don't like this
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
operator()(
|
operator()(
|
||||||
error_code ec = {},
|
error_code ec = {},
|
||||||
std::size_t bytes_used = 0)
|
std::size_t bytes_used = 0,
|
||||||
|
bool cont = true)
|
||||||
{
|
{
|
||||||
boost::ignore_unused(bytes_used);
|
boost::ignore_unused(bytes_used);
|
||||||
|
auto sp = wp_.lock();
|
||||||
|
if(! sp)
|
||||||
|
return this->invoke(cont,
|
||||||
|
net::error::operation_aborted);
|
||||||
|
auto& impl = *sp;
|
||||||
BOOST_ASIO_CORO_REENTER(*this)
|
BOOST_ASIO_CORO_REENTER(*this)
|
||||||
{
|
{
|
||||||
// Send HTTP Upgrade
|
impl.change_status(status::handshake);
|
||||||
ws_.impl_->do_pmd_config(d_.req);
|
impl.update_timer(this->get_executor());
|
||||||
|
|
||||||
|
// write HTTP request
|
||||||
|
impl.do_pmd_config(d_.req);
|
||||||
BOOST_ASIO_CORO_YIELD
|
BOOST_ASIO_CORO_YIELD
|
||||||
http::async_write(ws_.impl_->stream,
|
http::async_write(impl.stream,
|
||||||
d_.req, std::move(*this));
|
d_.req, std::move(*this));
|
||||||
if(ec)
|
if(impl.check_stop_now(ec))
|
||||||
goto upcall;
|
goto upcall;
|
||||||
|
|
||||||
// Read HTTP response
|
// read HTTP response
|
||||||
BOOST_ASIO_CORO_YIELD
|
BOOST_ASIO_CORO_YIELD
|
||||||
http::async_read(ws_.next_layer(),
|
http::async_read(impl.stream,
|
||||||
ws_.impl_->rd_buf, d_.res,
|
impl.rd_buf, d_.res,
|
||||||
std::move(*this));
|
std::move(*this));
|
||||||
if(ec)
|
if(impl.check_stop_now(ec))
|
||||||
goto upcall;
|
goto upcall;
|
||||||
ws_.on_response(d_.res, key_, ec);
|
|
||||||
|
// success
|
||||||
|
impl.reset_idle();
|
||||||
|
impl.on_response(d_.res, key_, ec);
|
||||||
if(res_p_)
|
if(res_p_)
|
||||||
swap(d_.res, *res_p_);
|
swap(d_.res, *res_p_);
|
||||||
|
|
||||||
upcall:
|
upcall:
|
||||||
this->invoke_now(ec);
|
this->invoke(cont ,ec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template<class NextLayer, bool deflateSupported>
|
||||||
|
template<class RequestDecorator>
|
||||||
|
void
|
||||||
|
stream<NextLayer, deflateSupported>::
|
||||||
|
do_handshake(
|
||||||
|
response_type* res_p,
|
||||||
|
string_view host,
|
||||||
|
string_view target,
|
||||||
|
RequestDecorator const& decorator,
|
||||||
|
error_code& ec)
|
||||||
|
{
|
||||||
|
response_type res;
|
||||||
|
impl_->change_status(status::handshake);
|
||||||
|
impl_->reset();
|
||||||
|
detail::sec_ws_key_type key;
|
||||||
|
{
|
||||||
|
auto const req = impl_->build_request(
|
||||||
|
key, host, target, decorator);
|
||||||
|
this->impl_->do_pmd_config(req);
|
||||||
|
http::write(impl_->stream, req, ec);
|
||||||
|
}
|
||||||
|
if(ec)
|
||||||
|
return;
|
||||||
|
http::read(next_layer(), impl_->rd_buf, res, ec);
|
||||||
|
if(ec)
|
||||||
|
return;
|
||||||
|
impl_->on_response(res, key, ec);
|
||||||
|
if(res_p)
|
||||||
|
*res_p = std::move(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
template<class NextLayer, bool deflateSupported>
|
template<class NextLayer, bool deflateSupported>
|
||||||
template<class HandshakeHandler>
|
template<class HandshakeHandler>
|
||||||
BOOST_ASIO_INITFN_RESULT_TYPE(
|
BOOST_ASIO_INITFN_RESULT_TYPE(
|
||||||
@@ -116,9 +166,10 @@ async_handshake(string_view host,
|
|||||||
BOOST_BEAST_HANDLER_INIT(
|
BOOST_BEAST_HANDLER_INIT(
|
||||||
HandshakeHandler, void(error_code));
|
HandshakeHandler, void(error_code));
|
||||||
handshake_op<BOOST_ASIO_HANDLER_TYPE(
|
handshake_op<BOOST_ASIO_HANDLER_TYPE(
|
||||||
HandshakeHandler, void(error_code))>{
|
HandshakeHandler, void(error_code))>(
|
||||||
std::move(init.completion_handler), *this, nullptr, host,
|
std::move(init.completion_handler),
|
||||||
target, &default_decorate_req}();
|
impl_, nullptr, host, target,
|
||||||
|
&default_decorate_req)();
|
||||||
return init.result.get();
|
return init.result.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,9 +188,10 @@ async_handshake(response_type& res,
|
|||||||
BOOST_BEAST_HANDLER_INIT(
|
BOOST_BEAST_HANDLER_INIT(
|
||||||
HandshakeHandler, void(error_code));
|
HandshakeHandler, void(error_code));
|
||||||
handshake_op<BOOST_ASIO_HANDLER_TYPE(
|
handshake_op<BOOST_ASIO_HANDLER_TYPE(
|
||||||
HandshakeHandler, void(error_code))>{
|
HandshakeHandler, void(error_code))>(
|
||||||
std::move(init.completion_handler), *this, &res, host,
|
std::move(init.completion_handler),
|
||||||
target, &default_decorate_req}();
|
impl_, &res, host, target,
|
||||||
|
&default_decorate_req)();
|
||||||
return init.result.get();
|
return init.result.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,9 +213,10 @@ async_handshake_ex(string_view host,
|
|||||||
BOOST_BEAST_HANDLER_INIT(
|
BOOST_BEAST_HANDLER_INIT(
|
||||||
HandshakeHandler, void(error_code));
|
HandshakeHandler, void(error_code));
|
||||||
handshake_op<BOOST_ASIO_HANDLER_TYPE(
|
handshake_op<BOOST_ASIO_HANDLER_TYPE(
|
||||||
HandshakeHandler, void(error_code))>{
|
HandshakeHandler, void(error_code))>(
|
||||||
std::move(init.completion_handler), *this, nullptr, host,
|
std::move(init.completion_handler),
|
||||||
target, decorator}();
|
impl_, nullptr, host, target,
|
||||||
|
decorator)();
|
||||||
return init.result.get();
|
return init.result.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,9 +239,10 @@ async_handshake_ex(response_type& res,
|
|||||||
BOOST_BEAST_HANDLER_INIT(
|
BOOST_BEAST_HANDLER_INIT(
|
||||||
HandshakeHandler, void(error_code));
|
HandshakeHandler, void(error_code));
|
||||||
handshake_op<BOOST_ASIO_HANDLER_TYPE(
|
handshake_op<BOOST_ASIO_HANDLER_TYPE(
|
||||||
HandshakeHandler, void(error_code))>{
|
HandshakeHandler, void(error_code))>(
|
||||||
std::move(init.completion_handler), *this, &res, host,
|
std::move(init.completion_handler),
|
||||||
target, decorator}();
|
impl_, &res, host, target,
|
||||||
|
decorator)();
|
||||||
return init.result.get();
|
return init.result.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -324,38 +378,6 @@ handshake_ex(response_type& res,
|
|||||||
host, target, decorator, ec);
|
host, target, decorator, ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
template<class NextLayer, bool deflateSupported>
|
|
||||||
template<class RequestDecorator>
|
|
||||||
void
|
|
||||||
stream<NextLayer, deflateSupported>::
|
|
||||||
do_handshake(
|
|
||||||
response_type* res_p,
|
|
||||||
string_view host,
|
|
||||||
string_view target,
|
|
||||||
RequestDecorator const& decorator,
|
|
||||||
error_code& ec)
|
|
||||||
{
|
|
||||||
response_type res;
|
|
||||||
impl_->reset();
|
|
||||||
detail::sec_ws_key_type key;
|
|
||||||
{
|
|
||||||
auto const req = build_request(
|
|
||||||
key, host, target, decorator);
|
|
||||||
this->impl_->do_pmd_config(req);
|
|
||||||
http::write(impl_->stream, req, ec);
|
|
||||||
}
|
|
||||||
if(ec)
|
|
||||||
return;
|
|
||||||
http::read(next_layer(), impl_->rd_buf, res, ec);
|
|
||||||
if(ec)
|
|
||||||
return;
|
|
||||||
on_response(res, key, ec);
|
|
||||||
if(res_p)
|
|
||||||
*res_p = std::move(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // websocket
|
} // websocket
|
||||||
} // beast
|
} // beast
|
||||||
} // boost
|
} // boost
|
||||||
|
@@ -15,6 +15,7 @@
|
|||||||
#include <boost/beast/core/stream_traits.hpp>
|
#include <boost/beast/core/stream_traits.hpp>
|
||||||
#include <boost/beast/core/detail/bind_continuation.hpp>
|
#include <boost/beast/core/detail/bind_continuation.hpp>
|
||||||
#include <boost/beast/websocket/detail/frame.hpp>
|
#include <boost/beast/websocket/detail/frame.hpp>
|
||||||
|
#include <boost/beast/websocket/impl/stream_impl.hpp>
|
||||||
#include <boost/asio/coroutine.hpp>
|
#include <boost/asio/coroutine.hpp>
|
||||||
#include <boost/asio/post.hpp>
|
#include <boost/asio/post.hpp>
|
||||||
#include <boost/throw_exception.hpp>
|
#include <boost/throw_exception.hpp>
|
||||||
@@ -36,7 +37,7 @@ class stream<NextLayer, deflateSupported>::ping_op
|
|||||||
Handler, beast::executor_type<stream>>
|
Handler, beast::executor_type<stream>>
|
||||||
, public net::coroutine
|
, public net::coroutine
|
||||||
{
|
{
|
||||||
stream<NextLayer, deflateSupported>& ws_;
|
boost::weak_ptr<impl_type> wp_;
|
||||||
detail::frame_buffer& fb_;
|
detail::frame_buffer& fb_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -45,18 +46,19 @@ public:
|
|||||||
template<class Handler_>
|
template<class Handler_>
|
||||||
ping_op(
|
ping_op(
|
||||||
Handler_&& h,
|
Handler_&& h,
|
||||||
stream<NextLayer, deflateSupported>& ws,
|
boost::shared_ptr<impl_type> const& sp,
|
||||||
detail::opcode op,
|
detail::opcode op,
|
||||||
ping_data const& payload)
|
ping_data const& payload)
|
||||||
: stable_async_op_base<
|
: stable_async_op_base<Handler,
|
||||||
Handler, beast::executor_type<stream>>(
|
beast::executor_type<stream>>(
|
||||||
std::forward<Handler_>(h), ws.get_executor())
|
std::forward<Handler_>(h),
|
||||||
, ws_(ws)
|
sp->stream.get_executor())
|
||||||
|
, wp_(sp)
|
||||||
, fb_(beast::allocate_stable<
|
, fb_(beast::allocate_stable<
|
||||||
detail::frame_buffer>(*this))
|
detail::frame_buffer>(*this))
|
||||||
{
|
{
|
||||||
// Serialize the ping or pong frame
|
// Serialize the ping or pong frame
|
||||||
ws.template write_ping<
|
sp->template write_ping<
|
||||||
flat_static_buffer_base>(fb_, op, payload);
|
flat_static_buffer_base>(fb_, op, payload);
|
||||||
(*this)({}, 0, false);
|
(*this)({}, 0, false);
|
||||||
}
|
}
|
||||||
@@ -67,14 +69,18 @@ public:
|
|||||||
bool cont = true)
|
bool cont = true)
|
||||||
{
|
{
|
||||||
boost::ignore_unused(bytes_transferred);
|
boost::ignore_unused(bytes_transferred);
|
||||||
auto& impl = *ws_.impl_;
|
auto sp = wp_.lock();
|
||||||
|
if(! sp)
|
||||||
|
return this->invoke(cont,
|
||||||
|
net::error::operation_aborted);
|
||||||
|
auto& impl = *sp;
|
||||||
BOOST_ASIO_CORO_REENTER(*this)
|
BOOST_ASIO_CORO_REENTER(*this)
|
||||||
{
|
{
|
||||||
// Acquire the write lock
|
// Acquire the write lock
|
||||||
if(! impl.wr_block.try_lock(this))
|
if(! impl.wr_block.try_lock(this))
|
||||||
{
|
{
|
||||||
BOOST_ASIO_CORO_YIELD
|
BOOST_ASIO_CORO_YIELD
|
||||||
impl.paused_ping.emplace(std::move(*this));
|
impl.op_ping.emplace(std::move(*this));
|
||||||
impl.wr_block.lock(this);
|
impl.wr_block.lock(this);
|
||||||
BOOST_ASIO_CORO_YIELD
|
BOOST_ASIO_CORO_YIELD
|
||||||
net::post(std::move(*this));
|
net::post(std::move(*this));
|
||||||
@@ -92,9 +98,10 @@ public:
|
|||||||
|
|
||||||
upcall:
|
upcall:
|
||||||
impl.wr_block.unlock(this);
|
impl.wr_block.unlock(this);
|
||||||
impl.paused_close.maybe_invoke()
|
impl.op_close.maybe_invoke()
|
||||||
|| impl.paused_rd.maybe_invoke()
|
|| impl.op_idle_ping.maybe_invoke()
|
||||||
|| impl.paused_wr.maybe_invoke();
|
|| impl.op_rd.maybe_invoke()
|
||||||
|
|| impl.op_wr.maybe_invoke();
|
||||||
this->invoke(cont, ec);
|
this->invoke(cont, ec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -102,6 +109,103 @@ public:
|
|||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
template<
|
||||||
|
class NextLayer,
|
||||||
|
bool deflateSupported,
|
||||||
|
class Handler>
|
||||||
|
void
|
||||||
|
async_auto_ping(
|
||||||
|
stream<NextLayer, deflateSupported>& ws,
|
||||||
|
Handler&& handler)
|
||||||
|
{
|
||||||
|
using handler_type =
|
||||||
|
typename std::decay<Handler>::type;
|
||||||
|
|
||||||
|
using base_type =
|
||||||
|
beast::stable_async_op_base<
|
||||||
|
handler_type, beast::executor_type<
|
||||||
|
stream<NextLayer, deflateSupported>>>;
|
||||||
|
|
||||||
|
struct async_op : base_type, net::coroutine
|
||||||
|
{
|
||||||
|
boost::weak_ptr<impl_type> impl_;
|
||||||
|
detail::frame_buffer& fb_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static constexpr int id = 4; // for soft_mutex
|
||||||
|
|
||||||
|
async_op(
|
||||||
|
Handler&& h,
|
||||||
|
stream<NextLayer, deflateSupported>& ws)
|
||||||
|
: base_type(std::move(h), ws.get_executor())
|
||||||
|
, impl_(ws.impl_)
|
||||||
|
, fb_(beast::allocate_stable<
|
||||||
|
detail::frame_buffer>(*this))
|
||||||
|
{
|
||||||
|
// Serialize the ping or pong frame
|
||||||
|
ping_data payload;
|
||||||
|
ws.template write_ping<
|
||||||
|
flat_static_buffer_base>(fb_, op, payload);
|
||||||
|
(*this)({}, 0, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(
|
||||||
|
error_code ec = {},
|
||||||
|
std::size_t bytes_transferred = 0,
|
||||||
|
bool cont = true)
|
||||||
|
{
|
||||||
|
boost::ignore_unused(bytes_transferred);
|
||||||
|
auto sp = impl_.lock();
|
||||||
|
if(! sp)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto& impl = *ws_.impl_;
|
||||||
|
BOOST_ASIO_CORO_REENTER(*this)
|
||||||
|
{
|
||||||
|
// Acquire the write lock
|
||||||
|
if(! impl.wr_block.try_lock(this))
|
||||||
|
{
|
||||||
|
BOOST_ASIO_CORO_YIELD
|
||||||
|
impl.op_idle_ping.emplace(std::move(*this));
|
||||||
|
impl.wr_block.lock(this);
|
||||||
|
BOOST_ASIO_CORO_YIELD
|
||||||
|
net::post(std::move(*this));
|
||||||
|
BOOST_ASSERT(impl.wr_block.is_locked(this));
|
||||||
|
}
|
||||||
|
if(impl.check_stop_now(ec))
|
||||||
|
goto upcall;
|
||||||
|
|
||||||
|
// Send ping frame
|
||||||
|
BOOST_ASIO_CORO_YIELD
|
||||||
|
net::async_write(impl.stream, fb_.data(),
|
||||||
|
beast::detail::bind_continuation(std::move(*this)));
|
||||||
|
if(impl.check_stop_now(ec))
|
||||||
|
goto upcall;
|
||||||
|
|
||||||
|
upcall:
|
||||||
|
impl.wr_block.unlock(this);
|
||||||
|
impl.op_close.maybe_invoke()
|
||||||
|
|| impl.op_ping.maybe_invoke()
|
||||||
|
|| impl.op_rd.maybe_invoke()
|
||||||
|
|| impl.op_wr.maybe_invoke();
|
||||||
|
if(! cont)
|
||||||
|
{
|
||||||
|
BOOST_ASIO_CORO_YIELD
|
||||||
|
net::post(bind_front_handler(
|
||||||
|
std::move(*this), ec));
|
||||||
|
}
|
||||||
|
this->invoke(ec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async_op op(ws, std::forward<Handler>(handler));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
template<class NextLayer, bool deflateSupported>
|
template<class NextLayer, bool deflateSupported>
|
||||||
void
|
void
|
||||||
stream<NextLayer, deflateSupported>::
|
stream<NextLayer, deflateSupported>::
|
||||||
@@ -121,7 +225,7 @@ ping(ping_data const& payload, error_code& ec)
|
|||||||
if(impl_->check_stop_now(ec))
|
if(impl_->check_stop_now(ec))
|
||||||
return;
|
return;
|
||||||
detail::frame_buffer fb;
|
detail::frame_buffer fb;
|
||||||
write_ping<flat_static_buffer_base>(
|
impl_->template write_ping<flat_static_buffer_base>(
|
||||||
fb, detail::opcode::ping, payload);
|
fb, detail::opcode::ping, payload);
|
||||||
net::write(impl_->stream, fb.data(), ec);
|
net::write(impl_->stream, fb.data(), ec);
|
||||||
if(impl_->check_stop_now(ec))
|
if(impl_->check_stop_now(ec))
|
||||||
@@ -147,7 +251,7 @@ pong(ping_data const& payload, error_code& ec)
|
|||||||
if(impl_->check_stop_now(ec))
|
if(impl_->check_stop_now(ec))
|
||||||
return;
|
return;
|
||||||
detail::frame_buffer fb;
|
detail::frame_buffer fb;
|
||||||
write_ping<flat_static_buffer_base>(
|
impl_->template write_ping<flat_static_buffer_base>(
|
||||||
fb, detail::opcode::pong, payload);
|
fb, detail::opcode::pong, payload);
|
||||||
net::write(impl_->stream, fb.data(), ec);
|
net::write(impl_->stream, fb.data(), ec);
|
||||||
if(impl_->check_stop_now(ec))
|
if(impl_->check_stop_now(ec))
|
||||||
@@ -167,7 +271,7 @@ async_ping(ping_data const& payload, WriteHandler&& handler)
|
|||||||
WriteHandler, void(error_code));
|
WriteHandler, void(error_code));
|
||||||
ping_op<BOOST_ASIO_HANDLER_TYPE(
|
ping_op<BOOST_ASIO_HANDLER_TYPE(
|
||||||
WriteHandler, void(error_code))>(
|
WriteHandler, void(error_code))>(
|
||||||
std::move(init.completion_handler), *this,
|
std::move(init.completion_handler), impl_,
|
||||||
detail::opcode::ping, payload);
|
detail::opcode::ping, payload);
|
||||||
return init.result.get();
|
return init.result.get();
|
||||||
}
|
}
|
||||||
@@ -185,7 +289,7 @@ async_pong(ping_data const& payload, WriteHandler&& handler)
|
|||||||
WriteHandler, void(error_code));
|
WriteHandler, void(error_code));
|
||||||
ping_op<BOOST_ASIO_HANDLER_TYPE(
|
ping_op<BOOST_ASIO_HANDLER_TYPE(
|
||||||
WriteHandler, void(error_code))>(
|
WriteHandler, void(error_code))>(
|
||||||
std::move(init.completion_handler), *this,
|
std::move(init.completion_handler), impl_,
|
||||||
detail::opcode::pong, payload);
|
detail::opcode::pong, payload);
|
||||||
return init.result.get();
|
return init.result.get();
|
||||||
}
|
}
|
||||||
|
@@ -13,6 +13,7 @@
|
|||||||
#include <boost/beast/core/buffer_size.hpp>
|
#include <boost/beast/core/buffer_size.hpp>
|
||||||
#include <boost/beast/websocket/teardown.hpp>
|
#include <boost/beast/websocket/teardown.hpp>
|
||||||
#include <boost/beast/websocket/detail/mask.hpp>
|
#include <boost/beast/websocket/detail/mask.hpp>
|
||||||
|
#include <boost/beast/websocket/impl/stream_impl.hpp>
|
||||||
#include <boost/beast/core/async_op_base.hpp>
|
#include <boost/beast/core/async_op_base.hpp>
|
||||||
#include <boost/beast/core/bind_handler.hpp>
|
#include <boost/beast/core/bind_handler.hpp>
|
||||||
#include <boost/beast/core/buffers_prefix.hpp>
|
#include <boost/beast/core/buffers_prefix.hpp>
|
||||||
@@ -50,7 +51,7 @@ class stream<NextLayer, deflateSupported>::read_some_op
|
|||||||
Handler, beast::executor_type<stream>>
|
Handler, beast::executor_type<stream>>
|
||||||
, public net::coroutine
|
, public net::coroutine
|
||||||
{
|
{
|
||||||
stream& ws_;
|
boost::weak_ptr<impl_type> wp_;
|
||||||
MutableBufferSequence bs_;
|
MutableBufferSequence bs_;
|
||||||
buffers_suffix<MutableBufferSequence> cb_;
|
buffers_suffix<MutableBufferSequence> cb_;
|
||||||
std::size_t bytes_written_ = 0;
|
std::size_t bytes_written_ = 0;
|
||||||
@@ -64,12 +65,13 @@ public:
|
|||||||
template<class Handler_>
|
template<class Handler_>
|
||||||
read_some_op(
|
read_some_op(
|
||||||
Handler_&& h,
|
Handler_&& h,
|
||||||
stream<NextLayer, deflateSupported>& ws,
|
boost::shared_ptr<impl_type> const& sp,
|
||||||
MutableBufferSequence const& bs)
|
MutableBufferSequence const& bs)
|
||||||
: async_op_base<
|
: async_op_base<
|
||||||
Handler, beast::executor_type<stream>>(
|
Handler, beast::executor_type<stream>>(
|
||||||
std::forward<Handler_>(h), ws.get_executor())
|
std::forward<Handler_>(h),
|
||||||
, ws_(ws)
|
sp->stream.get_executor())
|
||||||
|
, wp_(sp)
|
||||||
, bs_(bs)
|
, bs_(bs)
|
||||||
, cb_(bs)
|
, cb_(bs)
|
||||||
, code_(close_code::none)
|
, code_(close_code::none)
|
||||||
@@ -83,16 +85,22 @@ public:
|
|||||||
bool cont = true)
|
bool cont = true)
|
||||||
{
|
{
|
||||||
using beast::detail::clamp;
|
using beast::detail::clamp;
|
||||||
auto& impl = *ws_.impl_;
|
auto sp = wp_.lock();
|
||||||
|
if(! sp)
|
||||||
|
return this->invoke(cont,
|
||||||
|
net::error::operation_aborted, 0);
|
||||||
|
auto& impl = *sp;
|
||||||
BOOST_ASIO_CORO_REENTER(*this)
|
BOOST_ASIO_CORO_REENTER(*this)
|
||||||
{
|
{
|
||||||
|
impl.update_timer(this->get_executor());
|
||||||
|
|
||||||
acquire_read_lock:
|
acquire_read_lock:
|
||||||
// Acquire the read lock
|
// Acquire the read lock
|
||||||
if(! impl.rd_block.try_lock(this))
|
if(! impl.rd_block.try_lock(this))
|
||||||
{
|
{
|
||||||
do_suspend:
|
do_suspend:
|
||||||
BOOST_ASIO_CORO_YIELD
|
BOOST_ASIO_CORO_YIELD
|
||||||
impl.paused_r_rd.emplace(std::move(*this));
|
impl.op_r_rd.emplace(std::move(*this));
|
||||||
impl.rd_block.lock(this);
|
impl.rd_block.lock(this);
|
||||||
BOOST_ASIO_CORO_YIELD
|
BOOST_ASIO_CORO_YIELD
|
||||||
net::post(std::move(*this));
|
net::post(std::move(*this));
|
||||||
@@ -139,7 +147,7 @@ public:
|
|||||||
(! impl.rd_fh.fin || impl.rd_done))
|
(! impl.rd_fh.fin || impl.rd_done))
|
||||||
{
|
{
|
||||||
// Read frame header
|
// Read frame header
|
||||||
while(! ws_.parse_fh(
|
while(! impl.parse_fh(
|
||||||
impl.rd_fh, impl.rd_buf, result_))
|
impl.rd_fh, impl.rd_buf, result_))
|
||||||
{
|
{
|
||||||
if(result_)
|
if(result_)
|
||||||
@@ -158,14 +166,15 @@ public:
|
|||||||
impl.rd_buf, impl.rd_buf.max_size())),
|
impl.rd_buf, impl.rd_buf.max_size())),
|
||||||
std::move(*this));
|
std::move(*this));
|
||||||
BOOST_ASSERT(impl.rd_block.is_locked(this));
|
BOOST_ASSERT(impl.rd_block.is_locked(this));
|
||||||
|
impl.rd_buf.commit(bytes_transferred);
|
||||||
if(impl.check_stop_now(ec))
|
if(impl.check_stop_now(ec))
|
||||||
goto upcall;
|
goto upcall;
|
||||||
impl.rd_buf.commit(bytes_transferred);
|
impl.reset_idle();
|
||||||
|
|
||||||
// Allow a close operation
|
// Allow a close operation
|
||||||
// to acquire the read block
|
// to acquire the read block
|
||||||
impl.rd_block.unlock(this);
|
impl.rd_block.unlock(this);
|
||||||
if( impl.paused_r_close.maybe_invoke())
|
if( impl.op_r_close.maybe_invoke())
|
||||||
{
|
{
|
||||||
// Suspend
|
// Suspend
|
||||||
BOOST_ASSERT(impl.rd_block.is_locked());
|
BOOST_ASSERT(impl.rd_block.is_locked());
|
||||||
@@ -216,7 +225,7 @@ public:
|
|||||||
impl.ctrl_cb(
|
impl.ctrl_cb(
|
||||||
frame_type::ping, payload);
|
frame_type::ping, payload);
|
||||||
impl.rd_fb.clear();
|
impl.rd_fb.clear();
|
||||||
ws_.template write_ping<
|
impl.template write_ping<
|
||||||
flat_static_buffer_base>(impl.rd_fb,
|
flat_static_buffer_base>(impl.rd_fb,
|
||||||
detail::opcode::pong, payload);
|
detail::opcode::pong, payload);
|
||||||
}
|
}
|
||||||
@@ -224,13 +233,13 @@ public:
|
|||||||
// Allow a close operation
|
// Allow a close operation
|
||||||
// to acquire the read block
|
// to acquire the read block
|
||||||
impl.rd_block.unlock(this);
|
impl.rd_block.unlock(this);
|
||||||
impl.paused_r_close.maybe_invoke();
|
impl.op_r_close.maybe_invoke();
|
||||||
|
|
||||||
// Acquire the write lock
|
// Acquire the write lock
|
||||||
if(! impl.wr_block.try_lock(this))
|
if(! impl.wr_block.try_lock(this))
|
||||||
{
|
{
|
||||||
BOOST_ASIO_CORO_YIELD
|
BOOST_ASIO_CORO_YIELD
|
||||||
impl.paused_rd.emplace(std::move(*this));
|
impl.op_rd.emplace(std::move(*this));
|
||||||
impl.wr_block.lock(this);
|
impl.wr_block.lock(this);
|
||||||
BOOST_ASIO_CORO_YIELD
|
BOOST_ASIO_CORO_YIELD
|
||||||
net::post(std::move(*this));
|
net::post(std::move(*this));
|
||||||
@@ -249,9 +258,10 @@ public:
|
|||||||
if(impl.check_stop_now(ec))
|
if(impl.check_stop_now(ec))
|
||||||
goto upcall;
|
goto upcall;
|
||||||
impl.wr_block.unlock(this);
|
impl.wr_block.unlock(this);
|
||||||
impl.paused_close.maybe_invoke() ||
|
impl.op_close.maybe_invoke()
|
||||||
impl.paused_ping.maybe_invoke() ||
|
|| impl.op_idle_ping.maybe_invoke()
|
||||||
impl.paused_wr.maybe_invoke();
|
|| impl.op_ping.maybe_invoke()
|
||||||
|
|| impl.op_wr.maybe_invoke();
|
||||||
goto acquire_read_lock;
|
goto acquire_read_lock;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,9 +274,7 @@ public:
|
|||||||
if(! cont)
|
if(! cont)
|
||||||
{
|
{
|
||||||
BOOST_ASIO_CORO_YIELD
|
BOOST_ASIO_CORO_YIELD
|
||||||
net::post(
|
net::post(std::move(*this));
|
||||||
ws_.get_executor(),
|
|
||||||
std::move(*this));
|
|
||||||
BOOST_ASSERT(cont);
|
BOOST_ASSERT(cont);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -291,9 +299,7 @@ public:
|
|||||||
if(! cont)
|
if(! cont)
|
||||||
{
|
{
|
||||||
BOOST_ASIO_CORO_YIELD
|
BOOST_ASIO_CORO_YIELD
|
||||||
net::post(
|
net::post(std::move(*this));
|
||||||
ws_.get_executor(),
|
|
||||||
std::move(*this));
|
|
||||||
BOOST_ASSERT(cont);
|
BOOST_ASSERT(cont);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -355,9 +361,10 @@ public:
|
|||||||
impl.rd_buf.prepare(read_size(
|
impl.rd_buf.prepare(read_size(
|
||||||
impl.rd_buf, impl.rd_buf.max_size())),
|
impl.rd_buf, impl.rd_buf.max_size())),
|
||||||
std::move(*this));
|
std::move(*this));
|
||||||
|
impl.rd_buf.commit(bytes_transferred);
|
||||||
if(impl.check_stop_now(ec))
|
if(impl.check_stop_now(ec))
|
||||||
goto upcall;
|
goto upcall;
|
||||||
impl.rd_buf.commit(bytes_transferred);
|
impl.reset_idle();
|
||||||
if(impl.rd_fh.mask)
|
if(impl.rd_fh.mask)
|
||||||
detail::mask_inplace(buffers_prefix(clamp(
|
detail::mask_inplace(buffers_prefix(clamp(
|
||||||
impl.rd_remain), impl.rd_buf.data()),
|
impl.rd_remain), impl.rd_buf.data()),
|
||||||
@@ -400,6 +407,7 @@ public:
|
|||||||
clamp(impl.rd_remain), cb_), std::move(*this));
|
clamp(impl.rd_remain), cb_), std::move(*this));
|
||||||
if(impl.check_stop_now(ec))
|
if(impl.check_stop_now(ec))
|
||||||
goto upcall;
|
goto upcall;
|
||||||
|
impl.reset_idle();
|
||||||
BOOST_ASSERT(bytes_transferred > 0);
|
BOOST_ASSERT(bytes_transferred > 0);
|
||||||
auto const mb = buffers_prefix(
|
auto const mb = buffers_prefix(
|
||||||
bytes_transferred, cb_);
|
bytes_transferred, cb_);
|
||||||
@@ -443,6 +451,7 @@ public:
|
|||||||
std::move(*this));
|
std::move(*this));
|
||||||
if(impl.check_stop_now(ec))
|
if(impl.check_stop_now(ec))
|
||||||
goto upcall;
|
goto upcall;
|
||||||
|
impl.reset_idle();
|
||||||
BOOST_ASSERT(bytes_transferred > 0);
|
BOOST_ASSERT(bytes_transferred > 0);
|
||||||
impl.rd_buf.commit(bytes_transferred);
|
impl.rd_buf.commit(bytes_transferred);
|
||||||
if(impl.rd_fh.mask)
|
if(impl.rd_fh.mask)
|
||||||
@@ -477,9 +486,8 @@ public:
|
|||||||
else if(impl.rd_fh.fin)
|
else if(impl.rd_fh.fin)
|
||||||
{
|
{
|
||||||
// append the empty block codes
|
// append the empty block codes
|
||||||
static std::uint8_t constexpr
|
std::uint8_t constexpr
|
||||||
empty_block[4] = {
|
empty_block[4] = { 0x00, 0x00, 0xff, 0xff };
|
||||||
0x00, 0x00, 0xff, 0xff };
|
|
||||||
zs.next_in = empty_block;
|
zs.next_in = empty_block;
|
||||||
zs.avail_in = sizeof(empty_block);
|
zs.avail_in = sizeof(empty_block);
|
||||||
impl.inflate(zs, zlib::Flush::sync, ec);
|
impl.inflate(zs, zlib::Flush::sync, ec);
|
||||||
@@ -537,7 +545,7 @@ public:
|
|||||||
if(! impl.wr_block.try_lock(this))
|
if(! impl.wr_block.try_lock(this))
|
||||||
{
|
{
|
||||||
BOOST_ASIO_CORO_YIELD
|
BOOST_ASIO_CORO_YIELD
|
||||||
impl.paused_rd.emplace(std::move(*this));
|
impl.op_rd.emplace(std::move(*this));
|
||||||
impl.wr_block.lock(this);
|
impl.wr_block.lock(this);
|
||||||
BOOST_ASIO_CORO_YIELD
|
BOOST_ASIO_CORO_YIELD
|
||||||
net::post(std::move(*this));
|
net::post(std::move(*this));
|
||||||
@@ -546,8 +554,7 @@ public:
|
|||||||
goto upcall;
|
goto upcall;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the status
|
impl.change_status(status::closing);
|
||||||
impl.status_ = status::closing;
|
|
||||||
|
|
||||||
if(! impl.wr_close)
|
if(! impl.wr_close)
|
||||||
{
|
{
|
||||||
@@ -555,7 +562,7 @@ public:
|
|||||||
|
|
||||||
// Serialize close frame
|
// Serialize close frame
|
||||||
impl.rd_fb.clear();
|
impl.rd_fb.clear();
|
||||||
ws_.template write_close<
|
impl.template write_close<
|
||||||
flat_static_buffer_base>(
|
flat_static_buffer_base>(
|
||||||
impl.rd_fb, code_);
|
impl.rd_fb, code_);
|
||||||
|
|
||||||
@@ -585,18 +592,19 @@ public:
|
|||||||
if(! ec)
|
if(! ec)
|
||||||
ec = result_;
|
ec = result_;
|
||||||
if(ec && ec != error::closed)
|
if(ec && ec != error::closed)
|
||||||
impl.status_ = status::failed;
|
impl.change_status(status::failed);
|
||||||
else
|
else
|
||||||
impl.status_ = status::closed;
|
impl.change_status(status::closed);
|
||||||
impl.close();
|
impl.close();
|
||||||
|
|
||||||
upcall:
|
upcall:
|
||||||
impl.rd_block.try_unlock(this);
|
impl.rd_block.try_unlock(this);
|
||||||
impl.paused_r_close.maybe_invoke();
|
impl.op_r_close.maybe_invoke();
|
||||||
if(impl.wr_block.try_unlock(this))
|
if(impl.wr_block.try_unlock(this))
|
||||||
impl.paused_close.maybe_invoke()
|
impl.op_close.maybe_invoke()
|
||||||
|| impl.paused_ping.maybe_invoke()
|
|| impl.op_idle_ping.maybe_invoke()
|
||||||
|| impl.paused_wr.maybe_invoke();
|
|| impl.op_ping.maybe_invoke()
|
||||||
|
|| impl.op_wr.maybe_invoke();
|
||||||
this->invoke(cont, ec, bytes_written_);
|
this->invoke(cont, ec, bytes_written_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -613,7 +621,7 @@ class stream<NextLayer, deflateSupported>::read_op
|
|||||||
Handler, beast::executor_type<stream>>
|
Handler, beast::executor_type<stream>>
|
||||||
, public net::coroutine
|
, public net::coroutine
|
||||||
{
|
{
|
||||||
stream<NextLayer, deflateSupported>& ws_;
|
boost::weak_ptr<impl_type> wp_;
|
||||||
DynamicBuffer& b_;
|
DynamicBuffer& b_;
|
||||||
std::size_t limit_;
|
std::size_t limit_;
|
||||||
std::size_t bytes_written_ = 0;
|
std::size_t bytes_written_ = 0;
|
||||||
@@ -623,14 +631,15 @@ public:
|
|||||||
template<class Handler_>
|
template<class Handler_>
|
||||||
read_op(
|
read_op(
|
||||||
Handler_&& h,
|
Handler_&& h,
|
||||||
stream<NextLayer, deflateSupported>& ws,
|
boost::shared_ptr<impl_type> const& sp,
|
||||||
DynamicBuffer& b,
|
DynamicBuffer& b,
|
||||||
std::size_t limit,
|
std::size_t limit,
|
||||||
bool some)
|
bool some)
|
||||||
: async_op_base<
|
: async_op_base<Handler,
|
||||||
Handler, beast::executor_type<stream>>(
|
beast::executor_type<stream>>(
|
||||||
std::forward<Handler_>(h), ws.get_executor())
|
std::forward<Handler_>(h),
|
||||||
, ws_(ws)
|
sp->stream.get_executor())
|
||||||
|
, wp_(sp)
|
||||||
, b_(b)
|
, b_(b)
|
||||||
, limit_(limit ? limit : (
|
, limit_(limit ? limit : (
|
||||||
std::numeric_limits<std::size_t>::max)())
|
std::numeric_limits<std::size_t>::max)())
|
||||||
@@ -645,26 +654,35 @@ public:
|
|||||||
bool cont = true)
|
bool cont = true)
|
||||||
{
|
{
|
||||||
using beast::detail::clamp;
|
using beast::detail::clamp;
|
||||||
boost::optional<typename
|
auto sp = wp_.lock();
|
||||||
DynamicBuffer::mutable_buffers_type> mb;
|
if(! sp)
|
||||||
|
return this->invoke(cont,
|
||||||
|
net::error::operation_aborted, 0);
|
||||||
|
auto& impl = *sp;
|
||||||
|
using mutable_buffers_type = typename
|
||||||
|
DynamicBuffer::mutable_buffers_type;
|
||||||
|
boost::optional<mutable_buffers_type> mb;
|
||||||
BOOST_ASIO_CORO_REENTER(*this)
|
BOOST_ASIO_CORO_REENTER(*this)
|
||||||
{
|
{
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
mb = beast::detail::dynamic_buffer_prepare(b_,
|
mb = beast::detail::dynamic_buffer_prepare(b_,
|
||||||
clamp(ws_.read_size_hint(b_), limit_),
|
clamp(impl.read_size_hint_db(b_), limit_),
|
||||||
ec, error::buffer_overflow);
|
ec, error::buffer_overflow);
|
||||||
if(ec)
|
if(impl.check_stop_now(ec))
|
||||||
goto upcall;
|
goto upcall;
|
||||||
|
|
||||||
|
// VFALCO TODO use boost::beast::bind_continuation
|
||||||
BOOST_ASIO_CORO_YIELD
|
BOOST_ASIO_CORO_YIELD
|
||||||
ws_.async_read_some(*mb,
|
read_some_op<mutable_buffers_type, read_op>(
|
||||||
beast::detail::bind_continuation(std::move(*this)));
|
std::move(*this), sp, *mb);
|
||||||
b_.commit(bytes_transferred);
|
b_.commit(bytes_transferred);
|
||||||
bytes_written_ += bytes_transferred;
|
bytes_written_ += bytes_transferred;
|
||||||
if(ec)
|
if(ec)
|
||||||
goto upcall;
|
goto upcall;
|
||||||
}
|
}
|
||||||
while(! some_ && ! ws_.is_message_done());
|
while(! some_ && ! impl.rd_done);
|
||||||
|
|
||||||
upcall:
|
upcall:
|
||||||
this->invoke(cont, ec, bytes_written_);
|
this->invoke(cont, ec, bytes_written_);
|
||||||
}
|
}
|
||||||
@@ -730,7 +748,7 @@ async_read(DynamicBuffer& buffer, ReadHandler&& handler)
|
|||||||
read_op<DynamicBuffer, BOOST_ASIO_HANDLER_TYPE(
|
read_op<DynamicBuffer, BOOST_ASIO_HANDLER_TYPE(
|
||||||
ReadHandler, void(error_code, std::size_t))>(
|
ReadHandler, void(error_code, std::size_t))>(
|
||||||
std::move(init.completion_handler),
|
std::move(init.completion_handler),
|
||||||
*this, buffer, 0, false);
|
impl_, buffer, 0, false);
|
||||||
return init.result.get();
|
return init.result.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -779,7 +797,7 @@ read_some(
|
|||||||
BOOST_ASSERT(size > 0);
|
BOOST_ASSERT(size > 0);
|
||||||
auto mb = beast::detail::dynamic_buffer_prepare(
|
auto mb = beast::detail::dynamic_buffer_prepare(
|
||||||
buffer, size, ec, error::buffer_overflow);
|
buffer, size, ec, error::buffer_overflow);
|
||||||
if(ec)
|
if(impl_->check_stop_now(ec))
|
||||||
return 0;
|
return 0;
|
||||||
auto const bytes_written = read_some(*mb, ec);
|
auto const bytes_written = read_some(*mb, ec);
|
||||||
buffer.commit(bytes_written);
|
buffer.commit(bytes_written);
|
||||||
@@ -806,7 +824,7 @@ async_read_some(
|
|||||||
read_op<DynamicBuffer, BOOST_ASIO_HANDLER_TYPE(
|
read_op<DynamicBuffer, BOOST_ASIO_HANDLER_TYPE(
|
||||||
ReadHandler, void(error_code, std::size_t))>(
|
ReadHandler, void(error_code, std::size_t))>(
|
||||||
std::move(init.completion_handler),
|
std::move(init.completion_handler),
|
||||||
*this, buffer, limit, true);
|
impl_, buffer, limit, true);
|
||||||
return init.result.get();
|
return init.result.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -862,7 +880,7 @@ loop:
|
|||||||
{
|
{
|
||||||
// Read frame header
|
// Read frame header
|
||||||
error_code result;
|
error_code result;
|
||||||
while(! parse_fh(impl.rd_fh, impl.rd_buf, result))
|
while(! impl.parse_fh(impl.rd_fh, impl.rd_buf, result))
|
||||||
{
|
{
|
||||||
if(result)
|
if(result)
|
||||||
{
|
{
|
||||||
@@ -915,7 +933,7 @@ loop:
|
|||||||
if(impl.ctrl_cb)
|
if(impl.ctrl_cb)
|
||||||
impl.ctrl_cb(frame_type::ping, payload);
|
impl.ctrl_cb(frame_type::ping, payload);
|
||||||
detail::frame_buffer fb;
|
detail::frame_buffer fb;
|
||||||
write_ping<flat_static_buffer_base>(fb,
|
impl.template write_ping<flat_static_buffer_base>(fb,
|
||||||
detail::opcode::pong, payload);
|
detail::opcode::pong, payload);
|
||||||
net::write(impl.stream, fb.data(), ec);
|
net::write(impl.stream, fb.data(), ec);
|
||||||
if(impl.check_stop_now(ec))
|
if(impl.check_stop_now(ec))
|
||||||
@@ -1186,7 +1204,7 @@ async_read_some(
|
|||||||
ReadHandler, void(error_code, std::size_t));
|
ReadHandler, void(error_code, std::size_t));
|
||||||
read_some_op<MutableBufferSequence, BOOST_ASIO_HANDLER_TYPE(
|
read_some_op<MutableBufferSequence, BOOST_ASIO_HANDLER_TYPE(
|
||||||
ReadHandler, void(error_code, std::size_t))>(
|
ReadHandler, void(error_code, std::size_t))>(
|
||||||
std::move(init.completion_handler), *this, buffers);
|
std::move(init.completion_handler), impl_, buffers);
|
||||||
return init.result.get();
|
return init.result.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -15,6 +15,7 @@
|
|||||||
#include <boost/beast/websocket/teardown.hpp>
|
#include <boost/beast/websocket/teardown.hpp>
|
||||||
#include <boost/beast/websocket/detail/hybi13.hpp>
|
#include <boost/beast/websocket/detail/hybi13.hpp>
|
||||||
#include <boost/beast/websocket/detail/mask.hpp>
|
#include <boost/beast/websocket/detail/mask.hpp>
|
||||||
|
#include <boost/beast/websocket/impl/stream_impl.hpp>
|
||||||
#include <boost/beast/version.hpp>
|
#include <boost/beast/version.hpp>
|
||||||
#include <boost/beast/http/read.hpp>
|
#include <boost/beast/http/read.hpp>
|
||||||
#include <boost/beast/http/write.hpp>
|
#include <boost/beast/http/write.hpp>
|
||||||
@@ -29,6 +30,7 @@
|
|||||||
#include <boost/asio/steady_timer.hpp>
|
#include <boost/asio/steady_timer.hpp>
|
||||||
#include <boost/assert.hpp>
|
#include <boost/assert.hpp>
|
||||||
#include <boost/endian/buffers.hpp>
|
#include <boost/endian/buffers.hpp>
|
||||||
|
#include <boost/make_shared.hpp>
|
||||||
#include <boost/make_unique.hpp>
|
#include <boost/make_unique.hpp>
|
||||||
#include <boost/throw_exception.hpp>
|
#include <boost/throw_exception.hpp>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@@ -45,7 +47,7 @@ template<class NextLayer, bool deflateSupported>
|
|||||||
template<class... Args>
|
template<class... Args>
|
||||||
stream<NextLayer, deflateSupported>::
|
stream<NextLayer, deflateSupported>::
|
||||||
stream(Args&&... args)
|
stream(Args&&... args)
|
||||||
: impl_(std::make_shared<impl_type>(
|
: impl_(boost::make_shared<impl_type>(
|
||||||
std::forward<Args>(args)...))
|
std::forward<Args>(args)...))
|
||||||
{
|
{
|
||||||
BOOST_ASSERT(impl_->rd_buf.max_size() >=
|
BOOST_ASSERT(impl_->rd_buf.max_size() >=
|
||||||
@@ -140,6 +142,30 @@ read_size_hint(DynamicBuffer& buffer) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Settings
|
||||||
|
//
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// timeout
|
||||||
|
|
||||||
|
template<class NextLayer, bool deflateSupported>
|
||||||
|
void
|
||||||
|
stream<NextLayer, deflateSupported>::
|
||||||
|
get_option(timeout& opt)
|
||||||
|
{
|
||||||
|
opt = impl_->timeout_opt;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer, bool deflateSupported>
|
||||||
|
void
|
||||||
|
stream<NextLayer, deflateSupported>::
|
||||||
|
set_option(timeout const& opt)
|
||||||
|
{
|
||||||
|
impl_->set_option(opt);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
template<class NextLayer, bool deflateSupported>
|
template<class NextLayer, bool deflateSupported>
|
||||||
void
|
void
|
||||||
@@ -229,7 +255,7 @@ void
|
|||||||
stream<NextLayer, deflateSupported>::
|
stream<NextLayer, deflateSupported>::
|
||||||
secure_prng(bool value)
|
secure_prng(bool value)
|
||||||
{
|
{
|
||||||
this->secure_prng_ = value;
|
this->impl_->secure_prng_ = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class NextLayer, bool deflateSupported>
|
template<class NextLayer, bool deflateSupported>
|
||||||
@@ -271,471 +297,6 @@ text() const
|
|||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
// Attempt to read a complete frame header.
|
|
||||||
// Returns `false` if more bytes are needed
|
|
||||||
template<class NextLayer, bool deflateSupported>
|
|
||||||
template<class DynamicBuffer>
|
|
||||||
bool
|
|
||||||
stream<NextLayer, deflateSupported>::
|
|
||||||
parse_fh(
|
|
||||||
detail::frame_header& fh,
|
|
||||||
DynamicBuffer& b,
|
|
||||||
error_code& ec)
|
|
||||||
{
|
|
||||||
if(buffer_size(b.data()) < 2)
|
|
||||||
{
|
|
||||||
// need more bytes
|
|
||||||
ec = {};
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
buffers_suffix<typename
|
|
||||||
DynamicBuffer::const_buffers_type> cb{
|
|
||||||
b.data()};
|
|
||||||
std::size_t need;
|
|
||||||
{
|
|
||||||
std::uint8_t tmp[2];
|
|
||||||
cb.consume(net::buffer_copy(
|
|
||||||
net::buffer(tmp), cb));
|
|
||||||
fh.len = tmp[1] & 0x7f;
|
|
||||||
switch(fh.len)
|
|
||||||
{
|
|
||||||
case 126: need = 2; break;
|
|
||||||
case 127: need = 8; break;
|
|
||||||
default:
|
|
||||||
need = 0;
|
|
||||||
}
|
|
||||||
fh.mask = (tmp[1] & 0x80) != 0;
|
|
||||||
if(fh.mask)
|
|
||||||
need += 4;
|
|
||||||
if(buffer_size(cb) < need)
|
|
||||||
{
|
|
||||||
// need more bytes
|
|
||||||
ec = {};
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
fh.op = static_cast<
|
|
||||||
detail::opcode>(tmp[0] & 0x0f);
|
|
||||||
fh.fin = (tmp[0] & 0x80) != 0;
|
|
||||||
fh.rsv1 = (tmp[0] & 0x40) != 0;
|
|
||||||
fh.rsv2 = (tmp[0] & 0x20) != 0;
|
|
||||||
fh.rsv3 = (tmp[0] & 0x10) != 0;
|
|
||||||
}
|
|
||||||
switch(fh.op)
|
|
||||||
{
|
|
||||||
case detail::opcode::binary:
|
|
||||||
case detail::opcode::text:
|
|
||||||
if(impl_->rd_cont)
|
|
||||||
{
|
|
||||||
// new data frame when continuation expected
|
|
||||||
ec = error::bad_data_frame;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if(fh.rsv2 || fh.rsv3 ||
|
|
||||||
! impl_->rd_deflated(fh.rsv1))
|
|
||||||
{
|
|
||||||
// reserved bits not cleared
|
|
||||||
ec = error::bad_reserved_bits;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case detail::opcode::cont:
|
|
||||||
if(! impl_->rd_cont)
|
|
||||||
{
|
|
||||||
// continuation without an active message
|
|
||||||
ec = error::bad_continuation;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if(fh.rsv1 || fh.rsv2 || fh.rsv3)
|
|
||||||
{
|
|
||||||
// reserved bits not cleared
|
|
||||||
ec = error::bad_reserved_bits;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
if(detail::is_reserved(fh.op))
|
|
||||||
{
|
|
||||||
// reserved opcode
|
|
||||||
ec = error::bad_opcode;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if(! fh.fin)
|
|
||||||
{
|
|
||||||
// fragmented control message
|
|
||||||
ec = error::bad_control_fragment;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if(fh.len > 125)
|
|
||||||
{
|
|
||||||
// invalid length for control message
|
|
||||||
ec = error::bad_control_size;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if(fh.rsv1 || fh.rsv2 || fh.rsv3)
|
|
||||||
{
|
|
||||||
// reserved bits not cleared
|
|
||||||
ec = error::bad_reserved_bits;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if(impl_->role == role_type::server && ! fh.mask)
|
|
||||||
{
|
|
||||||
// unmasked frame from client
|
|
||||||
ec = error::bad_unmasked_frame;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if(impl_->role == role_type::client && fh.mask)
|
|
||||||
{
|
|
||||||
// masked frame from server
|
|
||||||
ec = error::bad_masked_frame;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if(detail::is_control(fh.op) &&
|
|
||||||
buffer_size(cb) < need + fh.len)
|
|
||||||
{
|
|
||||||
// Make the entire control frame payload
|
|
||||||
// get read in before we return `true`
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
switch(fh.len)
|
|
||||||
{
|
|
||||||
case 126:
|
|
||||||
{
|
|
||||||
std::uint8_t tmp[2];
|
|
||||||
BOOST_ASSERT(buffer_size(cb) >= sizeof(tmp));
|
|
||||||
cb.consume(net::buffer_copy(net::buffer(tmp), cb));
|
|
||||||
fh.len = detail::big_uint16_to_native(&tmp[0]);
|
|
||||||
if(fh.len < 126)
|
|
||||||
{
|
|
||||||
// length not canonical
|
|
||||||
ec = error::bad_size;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 127:
|
|
||||||
{
|
|
||||||
std::uint8_t tmp[8];
|
|
||||||
BOOST_ASSERT(buffer_size(cb) >= sizeof(tmp));
|
|
||||||
cb.consume(net::buffer_copy(net::buffer(tmp), cb));
|
|
||||||
fh.len = detail::big_uint64_to_native(&tmp[0]);
|
|
||||||
if(fh.len < 65536)
|
|
||||||
{
|
|
||||||
// length not canonical
|
|
||||||
ec = error::bad_size;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(fh.mask)
|
|
||||||
{
|
|
||||||
std::uint8_t tmp[4];
|
|
||||||
BOOST_ASSERT(buffer_size(cb) >= sizeof(tmp));
|
|
||||||
cb.consume(net::buffer_copy(net::buffer(tmp), cb));
|
|
||||||
fh.key = detail::little_uint32_to_native(&tmp[0]);
|
|
||||||
detail::prepare_key(impl_->rd_key, fh.key);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// initialize this otherwise operator== breaks
|
|
||||||
fh.key = 0;
|
|
||||||
}
|
|
||||||
if(! detail::is_control(fh.op))
|
|
||||||
{
|
|
||||||
if(fh.op != detail::opcode::cont)
|
|
||||||
{
|
|
||||||
impl_->rd_size = 0;
|
|
||||||
impl_->rd_op = fh.op;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if(impl_->rd_size > (std::numeric_limits<
|
|
||||||
std::uint64_t>::max)() - fh.len)
|
|
||||||
{
|
|
||||||
// message size exceeds configured limit
|
|
||||||
ec = error::message_too_big;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(! impl_->rd_deflated())
|
|
||||||
{
|
|
||||||
if(impl_->rd_msg_max && beast::detail::sum_exceeds(
|
|
||||||
impl_->rd_size, fh.len, impl_->rd_msg_max))
|
|
||||||
{
|
|
||||||
// message size exceeds configured limit
|
|
||||||
ec = error::message_too_big;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl_->rd_cont = ! fh.fin;
|
|
||||||
impl_->rd_remain = fh.len;
|
|
||||||
}
|
|
||||||
b.consume(b.size() - buffer_size(cb));
|
|
||||||
ec = {};
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class NextLayer, bool deflateSupported>
|
|
||||||
template<class DynamicBuffer>
|
|
||||||
void
|
|
||||||
stream<NextLayer, deflateSupported>::
|
|
||||||
write_close(DynamicBuffer& db, close_reason const& cr)
|
|
||||||
{
|
|
||||||
using namespace boost::endian;
|
|
||||||
detail::frame_header fh;
|
|
||||||
fh.op = detail::opcode::close;
|
|
||||||
fh.fin = true;
|
|
||||||
fh.rsv1 = false;
|
|
||||||
fh.rsv2 = false;
|
|
||||||
fh.rsv3 = false;
|
|
||||||
fh.len = cr.code == close_code::none ?
|
|
||||||
0 : 2 + cr.reason.size();
|
|
||||||
if(impl_->role == role_type::client)
|
|
||||||
{
|
|
||||||
fh.mask = true;
|
|
||||||
fh.key = this->create_mask();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fh.mask = false;
|
|
||||||
}
|
|
||||||
detail::write(db, fh);
|
|
||||||
if(cr.code != close_code::none)
|
|
||||||
{
|
|
||||||
detail::prepared_key key;
|
|
||||||
if(fh.mask)
|
|
||||||
detail::prepare_key(key, fh.key);
|
|
||||||
{
|
|
||||||
std::uint8_t tmp[2];
|
|
||||||
::new(&tmp[0]) big_uint16_buf_t{
|
|
||||||
(std::uint16_t)cr.code};
|
|
||||||
auto mb = db.prepare(2);
|
|
||||||
net::buffer_copy(mb,
|
|
||||||
net::buffer(tmp));
|
|
||||||
if(fh.mask)
|
|
||||||
detail::mask_inplace(mb, key);
|
|
||||||
db.commit(2);
|
|
||||||
}
|
|
||||||
if(! cr.reason.empty())
|
|
||||||
{
|
|
||||||
auto mb = db.prepare(cr.reason.size());
|
|
||||||
net::buffer_copy(mb,
|
|
||||||
net::const_buffer(
|
|
||||||
cr.reason.data(), cr.reason.size()));
|
|
||||||
if(fh.mask)
|
|
||||||
detail::mask_inplace(mb, key);
|
|
||||||
db.commit(cr.reason.size());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class NextLayer, bool deflateSupported>
|
|
||||||
template<class DynamicBuffer>
|
|
||||||
void
|
|
||||||
stream<NextLayer, deflateSupported>::
|
|
||||||
write_ping(DynamicBuffer& db,
|
|
||||||
detail::opcode code, ping_data const& data)
|
|
||||||
{
|
|
||||||
detail::frame_header fh;
|
|
||||||
fh.op = code;
|
|
||||||
fh.fin = true;
|
|
||||||
fh.rsv1 = false;
|
|
||||||
fh.rsv2 = false;
|
|
||||||
fh.rsv3 = false;
|
|
||||||
fh.len = data.size();
|
|
||||||
fh.mask = impl_->role == role_type::client;
|
|
||||||
if(fh.mask)
|
|
||||||
fh.key = this->create_mask();
|
|
||||||
detail::write(db, fh);
|
|
||||||
if(data.empty())
|
|
||||||
return;
|
|
||||||
detail::prepared_key key;
|
|
||||||
if(fh.mask)
|
|
||||||
detail::prepare_key(key, fh.key);
|
|
||||||
auto mb = db.prepare(data.size());
|
|
||||||
net::buffer_copy(mb,
|
|
||||||
net::const_buffer(
|
|
||||||
data.data(), data.size()));
|
|
||||||
if(fh.mask)
|
|
||||||
detail::mask_inplace(mb, key);
|
|
||||||
db.commit(data.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
template<class NextLayer, bool deflateSupported>
|
|
||||||
template<class Decorator>
|
|
||||||
request_type
|
|
||||||
stream<NextLayer, deflateSupported>::
|
|
||||||
build_request(detail::sec_ws_key_type& key,
|
|
||||||
string_view host, string_view target,
|
|
||||||
Decorator const& decorator)
|
|
||||||
{
|
|
||||||
request_type req;
|
|
||||||
req.target(target);
|
|
||||||
req.version(11);
|
|
||||||
req.method(http::verb::get);
|
|
||||||
req.set(http::field::host, host);
|
|
||||||
req.set(http::field::upgrade, "websocket");
|
|
||||||
req.set(http::field::connection, "upgrade");
|
|
||||||
detail::make_sec_ws_key(key);
|
|
||||||
req.set(http::field::sec_websocket_key, key);
|
|
||||||
req.set(http::field::sec_websocket_version, "13");
|
|
||||||
impl_->build_request_pmd(req);
|
|
||||||
decorator(req);
|
|
||||||
if(! req.count(http::field::user_agent))
|
|
||||||
req.set(http::field::user_agent,
|
|
||||||
BOOST_BEAST_VERSION_STRING);
|
|
||||||
return req;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class NextLayer, bool deflateSupported>
|
|
||||||
template<class Body, class Allocator, class Decorator>
|
|
||||||
response_type
|
|
||||||
stream<NextLayer, deflateSupported>::
|
|
||||||
build_response(
|
|
||||||
http::request<Body,
|
|
||||||
http::basic_fields<Allocator>> const& req,
|
|
||||||
Decorator const& decorator,
|
|
||||||
error_code& result)
|
|
||||||
{
|
|
||||||
auto const decorate =
|
|
||||||
[&decorator](response_type& res)
|
|
||||||
{
|
|
||||||
decorator(res);
|
|
||||||
if(! res.count(http::field::server))
|
|
||||||
{
|
|
||||||
BOOST_STATIC_ASSERT(sizeof(BOOST_BEAST_VERSION_STRING) < 20);
|
|
||||||
static_string<20> s(BOOST_BEAST_VERSION_STRING);
|
|
||||||
res.set(http::field::server, s);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
auto err =
|
|
||||||
[&](error e)
|
|
||||||
{
|
|
||||||
result = e;
|
|
||||||
response_type res;
|
|
||||||
res.version(req.version());
|
|
||||||
res.result(http::status::bad_request);
|
|
||||||
res.body() = result.message();
|
|
||||||
res.prepare_payload();
|
|
||||||
decorate(res);
|
|
||||||
return res;
|
|
||||||
};
|
|
||||||
if(req.version() != 11)
|
|
||||||
return err(error::bad_http_version);
|
|
||||||
if(req.method() != http::verb::get)
|
|
||||||
return err(error::bad_method);
|
|
||||||
if(! req.count(http::field::host))
|
|
||||||
return err(error::no_host);
|
|
||||||
{
|
|
||||||
auto const it = req.find(http::field::connection);
|
|
||||||
if(it == req.end())
|
|
||||||
return err(error::no_connection);
|
|
||||||
if(! http::token_list{it->value()}.exists("upgrade"))
|
|
||||||
return err(error::no_connection_upgrade);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
auto const it = req.find(http::field::upgrade);
|
|
||||||
if(it == req.end())
|
|
||||||
return err(error::no_upgrade);
|
|
||||||
if(! http::token_list{it->value()}.exists("websocket"))
|
|
||||||
return err(error::no_upgrade_websocket);
|
|
||||||
}
|
|
||||||
string_view key;
|
|
||||||
{
|
|
||||||
auto const it = req.find(http::field::sec_websocket_key);
|
|
||||||
if(it == req.end())
|
|
||||||
return err(error::no_sec_key);
|
|
||||||
key = it->value();
|
|
||||||
if(key.size() > detail::sec_ws_key_type::max_size_n)
|
|
||||||
return err(error::bad_sec_key);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
auto const it = req.find(http::field::sec_websocket_version);
|
|
||||||
if(it == req.end())
|
|
||||||
return err(error::no_sec_version);
|
|
||||||
if(it->value() != "13")
|
|
||||||
{
|
|
||||||
response_type res;
|
|
||||||
res.result(http::status::upgrade_required);
|
|
||||||
res.version(req.version());
|
|
||||||
res.set(http::field::sec_websocket_version, "13");
|
|
||||||
result = error::bad_sec_version;
|
|
||||||
res.body() = result.message();
|
|
||||||
res.prepare_payload();
|
|
||||||
decorate(res);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
response_type res;
|
|
||||||
res.result(http::status::switching_protocols);
|
|
||||||
res.version(req.version());
|
|
||||||
res.set(http::field::upgrade, "websocket");
|
|
||||||
res.set(http::field::connection, "upgrade");
|
|
||||||
{
|
|
||||||
detail::sec_ws_accept_type acc;
|
|
||||||
detail::make_sec_ws_accept(acc, key);
|
|
||||||
res.set(http::field::sec_websocket_accept, acc);
|
|
||||||
}
|
|
||||||
impl_->build_response_pmd(res, req);
|
|
||||||
decorate(res);
|
|
||||||
result = {};
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called when the WebSocket Upgrade response is received
|
|
||||||
template<class NextLayer, bool deflateSupported>
|
|
||||||
void
|
|
||||||
stream<NextLayer, deflateSupported>::
|
|
||||||
on_response(
|
|
||||||
response_type const& res,
|
|
||||||
detail::sec_ws_key_type const& key,
|
|
||||||
error_code& ec)
|
|
||||||
{
|
|
||||||
auto const err =
|
|
||||||
[&](error e)
|
|
||||||
{
|
|
||||||
ec = e;
|
|
||||||
};
|
|
||||||
if(res.result() != http::status::switching_protocols)
|
|
||||||
return err(error::upgrade_declined);
|
|
||||||
if(res.version() != 11)
|
|
||||||
return err(error::bad_http_version);
|
|
||||||
{
|
|
||||||
auto const it = res.find(http::field::connection);
|
|
||||||
if(it == res.end())
|
|
||||||
return err(error::no_connection);
|
|
||||||
if(! http::token_list{it->value()}.exists("upgrade"))
|
|
||||||
return err(error::no_connection_upgrade);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
auto const it = res.find(http::field::upgrade);
|
|
||||||
if(it == res.end())
|
|
||||||
return err(error::no_upgrade);
|
|
||||||
if(! http::token_list{it->value()}.exists("websocket"))
|
|
||||||
return err(error::no_upgrade_websocket);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
auto const it = res.find(http::field::sec_websocket_accept);
|
|
||||||
if(it == res.end())
|
|
||||||
return err(error::no_sec_accept);
|
|
||||||
detail::sec_ws_accept_type acc;
|
|
||||||
detail::make_sec_ws_accept(acc, key);
|
|
||||||
if(acc.compare(it->value()) != 0)
|
|
||||||
return err(error::bad_sec_accept);
|
|
||||||
}
|
|
||||||
|
|
||||||
ec = {};
|
|
||||||
impl_->on_response_pmd(res);
|
|
||||||
impl_->open(role_type::client);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _Fail the WebSocket Connection_
|
// _Fail the WebSocket Connection_
|
||||||
template<class NextLayer, bool deflateSupported>
|
template<class NextLayer, bool deflateSupported>
|
||||||
void
|
void
|
||||||
@@ -746,12 +307,12 @@ do_fail(
|
|||||||
error_code& ec) // set to the error, else set to ev
|
error_code& ec) // set to the error, else set to ev
|
||||||
{
|
{
|
||||||
BOOST_ASSERT(ev);
|
BOOST_ASSERT(ev);
|
||||||
impl_->status_ = status::closing;
|
impl_->change_status(status::closing);
|
||||||
if(code != close_code::none && ! impl_->wr_close)
|
if(code != close_code::none && ! impl_->wr_close)
|
||||||
{
|
{
|
||||||
impl_->wr_close = true;
|
impl_->wr_close = true;
|
||||||
detail::frame_buffer fb;
|
detail::frame_buffer fb;
|
||||||
write_close<
|
impl_->template write_close<
|
||||||
flat_static_buffer_base>(fb, code);
|
flat_static_buffer_base>(fb, code);
|
||||||
net::write(impl_->stream, fb.data(), ec);
|
net::write(impl_->stream, fb.data(), ec);
|
||||||
if(impl_->check_stop_now(ec))
|
if(impl_->check_stop_now(ec))
|
||||||
@@ -768,9 +329,9 @@ do_fail(
|
|||||||
if(! ec)
|
if(! ec)
|
||||||
ec = ev;
|
ec = ev;
|
||||||
if(ec && ec != error::closed)
|
if(ec && ec != error::closed)
|
||||||
impl_->status_ = status::failed;
|
impl_->change_status(status::failed);
|
||||||
else
|
else
|
||||||
impl_->status_ = status::closed;
|
impl_->change_status(status::closed);
|
||||||
impl_->close();
|
impl_->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -10,16 +10,32 @@
|
|||||||
#ifndef BOOST_BEAST_WEBSOCKET_IMPL_STREAM_IMPL_HPP
|
#ifndef BOOST_BEAST_WEBSOCKET_IMPL_STREAM_IMPL_HPP
|
||||||
#define BOOST_BEAST_WEBSOCKET_IMPL_STREAM_IMPL_HPP
|
#define BOOST_BEAST_WEBSOCKET_IMPL_STREAM_IMPL_HPP
|
||||||
|
|
||||||
|
#include <boost/beast/version.hpp>
|
||||||
|
#include <boost/beast/http/read.hpp>
|
||||||
|
#include <boost/beast/http/write.hpp>
|
||||||
|
#include <boost/beast/http/rfc7230.hpp>
|
||||||
|
#include <boost/beast/core/buffers_cat.hpp>
|
||||||
|
#include <boost/beast/core/buffers_prefix.hpp>
|
||||||
|
#include <boost/beast/core/buffers_suffix.hpp>
|
||||||
|
#include <boost/beast/core/flat_static_buffer.hpp>
|
||||||
#include <boost/beast/core/saved_handler.hpp>
|
#include <boost/beast/core/saved_handler.hpp>
|
||||||
#include <boost/beast/core/static_buffer.hpp>
|
#include <boost/beast/core/static_buffer.hpp>
|
||||||
#include <boost/beast/core/stream_traits.hpp>
|
#include <boost/beast/core/stream_traits.hpp>
|
||||||
|
#include <boost/beast/core/detail/clamp.hpp>
|
||||||
|
#include <boost/beast/core/detail/type_traits.hpp>
|
||||||
|
#include <boost/beast/websocket/rfc6455.hpp>
|
||||||
#include <boost/beast/websocket/detail/frame.hpp>
|
#include <boost/beast/websocket/detail/frame.hpp>
|
||||||
|
#include <boost/beast/websocket/detail/hybi13.hpp>
|
||||||
|
#include <boost/beast/websocket/detail/mask.hpp>
|
||||||
#include <boost/beast/websocket/detail/pmd_extension.hpp>
|
#include <boost/beast/websocket/detail/pmd_extension.hpp>
|
||||||
|
#include <boost/beast/websocket/detail/prng.hpp>
|
||||||
#include <boost/beast/websocket/detail/soft_mutex.hpp>
|
#include <boost/beast/websocket/detail/soft_mutex.hpp>
|
||||||
#include <boost/beast/websocket/detail/utf8_checker.hpp>
|
#include <boost/beast/websocket/detail/utf8_checker.hpp>
|
||||||
|
#include <boost/beast/version.hpp>
|
||||||
#include <boost/asio/bind_executor.hpp>
|
#include <boost/asio/bind_executor.hpp>
|
||||||
#include <boost/asio/steady_timer.hpp>
|
#include <boost/asio/steady_timer.hpp>
|
||||||
#include <boost/core/empty_value.hpp>
|
#include <boost/core/empty_value.hpp>
|
||||||
|
#include <boost/enable_shared_from_this.hpp>
|
||||||
#include <boost/optional.hpp>
|
#include <boost/optional.hpp>
|
||||||
|
|
||||||
namespace boost {
|
namespace boost {
|
||||||
@@ -29,7 +45,7 @@ namespace websocket {
|
|||||||
template<
|
template<
|
||||||
class NextLayer, bool deflateSupported>
|
class NextLayer, bool deflateSupported>
|
||||||
struct stream<NextLayer, deflateSupported>::impl_type
|
struct stream<NextLayer, deflateSupported>::impl_type
|
||||||
: std::enable_shared_from_this<impl_type>
|
: boost::enable_shared_from_this<impl_type>
|
||||||
, detail::impl_base<deflateSupported>
|
, detail::impl_base<deflateSupported>
|
||||||
{
|
{
|
||||||
NextLayer stream; // The underlying stream
|
NextLayer stream; // The underlying stream
|
||||||
@@ -68,17 +84,25 @@ struct stream<NextLayer, deflateSupported>::impl_type
|
|||||||
std::size_t wr_buf_opt /* write buffer size option setting */ = 4096;
|
std::size_t wr_buf_opt /* write buffer size option setting */ = 4096;
|
||||||
detail::fh_buffer wr_fb; // header buffer used for writes
|
detail::fh_buffer wr_fb; // header buffer used for writes
|
||||||
|
|
||||||
saved_handler paused_rd; // paused read op
|
saved_handler op_rd; // paused read op
|
||||||
saved_handler paused_wr; // paused write op
|
saved_handler op_wr; // paused write op
|
||||||
saved_handler paused_ping; // paused ping op
|
saved_handler op_ping; // paused ping op
|
||||||
saved_handler paused_close; // paused close op
|
saved_handler op_idle_ping; // paused idle ping op
|
||||||
saved_handler paused_r_rd; // paused read op (async read)
|
saved_handler op_close; // paused close op
|
||||||
saved_handler paused_r_close; // paused close op (async read)
|
saved_handler op_r_rd; // paused read op (async read)
|
||||||
|
saved_handler op_r_close; // paused close op (async read)
|
||||||
|
|
||||||
boost::optional<bool> tm_auto_ping;
|
bool secure_prng_ = true;
|
||||||
bool tm_opt /* true if auto-timeout option is set */ = false;
|
|
||||||
bool tm_idle; // set to false on incoming frames
|
bool ec_delivered = false;
|
||||||
time_point::duration tm_dur /* duration of timer */ = std::chrono::seconds(1);
|
bool timed_out = false;
|
||||||
|
int idle_counter = 0;
|
||||||
|
|
||||||
|
// settings
|
||||||
|
|
||||||
|
timeout timeout_opt;
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
template<class... Args>
|
template<class... Args>
|
||||||
impl_type(Args&&... args)
|
impl_type(Args&&... args)
|
||||||
@@ -91,6 +115,9 @@ struct stream<NextLayer, deflateSupported>::impl_type
|
|||||||
open(role_type role_)
|
open(role_type role_)
|
||||||
{
|
{
|
||||||
// VFALCO TODO analyze and remove dupe code in reset()
|
// VFALCO TODO analyze and remove dupe code in reset()
|
||||||
|
timer.expires_at(never());
|
||||||
|
timed_out = false;
|
||||||
|
cr.code = close_code::none;
|
||||||
role = role_;
|
role = role_;
|
||||||
status_ = status::open;
|
status_ = status::open;
|
||||||
rd_remain = 0;
|
rd_remain = 0;
|
||||||
@@ -106,13 +133,10 @@ struct stream<NextLayer, deflateSupported>::impl_type
|
|||||||
// stream exhibits undefined behavior.
|
// stream exhibits undefined behavior.
|
||||||
wr_block.reset();
|
wr_block.reset();
|
||||||
rd_block.reset();
|
rd_block.reset();
|
||||||
cr.code = close_code::none;
|
|
||||||
|
|
||||||
wr_cont = false;
|
wr_cont = false;
|
||||||
wr_buf_size = 0;
|
wr_buf_size = 0;
|
||||||
|
|
||||||
tm_idle = false;
|
|
||||||
|
|
||||||
this->open_pmd(role);
|
this->open_pmd(role);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,6 +152,8 @@ struct stream<NextLayer, deflateSupported>::impl_type
|
|||||||
reset()
|
reset()
|
||||||
{
|
{
|
||||||
BOOST_ASSERT(status_ != status::open);
|
BOOST_ASSERT(status_ != status::open);
|
||||||
|
timer.expires_at(never());
|
||||||
|
cr.code = close_code::none;
|
||||||
rd_remain = 0;
|
rd_remain = 0;
|
||||||
rd_cont = false;
|
rd_cont = false;
|
||||||
rd_done = true;
|
rd_done = true;
|
||||||
@@ -141,8 +167,6 @@ struct stream<NextLayer, deflateSupported>::impl_type
|
|||||||
// stream exhibits undefined behavior.
|
// stream exhibits undefined behavior.
|
||||||
wr_block.reset();
|
wr_block.reset();
|
||||||
rd_block.reset();
|
rd_block.reset();
|
||||||
cr.code = close_code::none;
|
|
||||||
tm_idle = false;
|
|
||||||
|
|
||||||
// VFALCO Is this needed?
|
// VFALCO Is this needed?
|
||||||
timer.cancel();
|
timer.cancel();
|
||||||
@@ -173,126 +197,88 @@ struct stream<NextLayer, deflateSupported>::impl_type
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
//--------------------------------------------------------------------------
|
||||||
bool
|
|
||||||
is_timer_set() const
|
|
||||||
{
|
|
||||||
return timer.expiry() == never();
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns `true` if we try sending a ping and
|
template<class Decorator>
|
||||||
// getting a pong before closing an idle stream.
|
request_type
|
||||||
bool
|
build_request(
|
||||||
is_auto_ping_enabled() const
|
detail::sec_ws_key_type& key,
|
||||||
{
|
string_view host, string_view target,
|
||||||
if(tm_auto_ping.has_value())
|
Decorator const& decorator);
|
||||||
return *tm_auto_ping;
|
|
||||||
if(role == role_type::server)
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class Executor>
|
|
||||||
class timeout_handler
|
|
||||||
: boost::empty_value<Executor>
|
|
||||||
{
|
|
||||||
std::weak_ptr<impl_type> wp_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
timeout_handler(
|
|
||||||
Executor const& ex,
|
|
||||||
std::shared_ptr<impl_type> const& sp)
|
|
||||||
: boost::empty_value<Executor>(
|
|
||||||
boost::empty_init_t{}, ex)
|
|
||||||
, wp_(sp)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
using executor_type = Executor;
|
|
||||||
|
|
||||||
executor_type
|
|
||||||
get_executor() const
|
|
||||||
{
|
|
||||||
return this->get();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
operator()(error_code ec)
|
|
||||||
{
|
|
||||||
// timer canceled?
|
|
||||||
if(ec == net::error::operation_aborted)
|
|
||||||
return;
|
|
||||||
BOOST_ASSERT(! ec);
|
|
||||||
|
|
||||||
// stream destroyed?
|
|
||||||
auto sp = wp_.lock();
|
|
||||||
if(! sp)
|
|
||||||
return;
|
|
||||||
auto& impl = *sp;
|
|
||||||
|
|
||||||
close_socket(get_lowest_layer(impl.stream));
|
|
||||||
#if 0
|
|
||||||
if(! impl.tm_idle)
|
|
||||||
{
|
|
||||||
impl.tm_idle = true;
|
|
||||||
BOOST_VERIFY(
|
|
||||||
impl.timer.expires_after(impl.tm_dur) == 0);
|
|
||||||
return impl.timer.async_wait(std::move(*this));
|
|
||||||
}
|
|
||||||
if(impl.is_auto_ping_enabled())
|
|
||||||
{
|
|
||||||
// send ping
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// timeout
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
};
|
|
||||||
public:
|
|
||||||
|
|
||||||
// called when there is qualified activity
|
|
||||||
void
|
void
|
||||||
activity()
|
on_response(
|
||||||
|
response_type const& res,
|
||||||
|
detail::sec_ws_key_type const& key,
|
||||||
|
error_code& ec);
|
||||||
|
|
||||||
|
template<class Body, class Allocator, class Decorator>
|
||||||
|
response_type
|
||||||
|
build_response(
|
||||||
|
http::request<Body,
|
||||||
|
http::basic_fields<Allocator>> const& req,
|
||||||
|
Decorator const& decorator,
|
||||||
|
error_code& result);
|
||||||
|
|
||||||
|
// Attempt to read a complete frame header.
|
||||||
|
// Returns `false` if more bytes are needed
|
||||||
|
template<class DynamicBuffer>
|
||||||
|
bool
|
||||||
|
parse_fh(detail::frame_header& fh,
|
||||||
|
DynamicBuffer& b, error_code& ec);
|
||||||
|
|
||||||
|
std::uint32_t
|
||||||
|
create_mask()
|
||||||
{
|
{
|
||||||
tm_idle = false;
|
auto g = detail::make_prng(secure_prng_);
|
||||||
|
for(;;)
|
||||||
|
if(auto key = g())
|
||||||
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Maintain the expiration timer
|
std::size_t
|
||||||
template<class Executor>
|
read_size_hint(std::size_t initial_size) const
|
||||||
void
|
|
||||||
update_timer(Executor const& ex)
|
|
||||||
{
|
{
|
||||||
if(role == role_type::server)
|
return this->read_size_hint_pmd(
|
||||||
{
|
initial_size, rd_done, rd_remain, rd_fh);
|
||||||
if(! is_timer_set())
|
|
||||||
{
|
|
||||||
// turn timer on
|
|
||||||
timer.expires_after(tm_dur);
|
|
||||||
timer.async_wait(
|
|
||||||
timeout_handler<Executor>(
|
|
||||||
ex, this->shared_from_this()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(tm_opt && ! is_timer_set())
|
|
||||||
{
|
|
||||||
// turn timer on
|
|
||||||
timer.expires_after(tm_dur);
|
|
||||||
timer.async_wait(
|
|
||||||
timeout_handler<Executor>(
|
|
||||||
ex, this->shared_from_this()));
|
|
||||||
}
|
|
||||||
else if(! tm_opt && is_timer_set())
|
|
||||||
{
|
|
||||||
// turn timer off
|
|
||||||
timer.cancel();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class DynamicBuffer>
|
||||||
|
std::size_t
|
||||||
|
read_size_hint_db(DynamicBuffer& buffer) const
|
||||||
|
{
|
||||||
|
auto const initial_size = (std::min)(
|
||||||
|
+tcp_frame_size,
|
||||||
|
buffer.max_size() - buffer.size());
|
||||||
|
if(initial_size == 0)
|
||||||
|
return 1; // buffer is full
|
||||||
|
return this->read_size_hint(initial_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class DynamicBuffer>
|
||||||
|
void
|
||||||
|
write_ping(DynamicBuffer& db,
|
||||||
|
detail::opcode code, ping_data const& data);
|
||||||
|
|
||||||
|
template<class DynamicBuffer>
|
||||||
|
void
|
||||||
|
write_close(DynamicBuffer& db, close_reason const& cr);
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
bool ec_delivered = false;
|
void
|
||||||
|
set_option(timeout const& opt)
|
||||||
|
{
|
||||||
|
if( opt.handshake_timeout == none() &&
|
||||||
|
opt.idle_timeout == none())
|
||||||
|
{
|
||||||
|
// turn timer off
|
||||||
|
timer.cancel();
|
||||||
|
timer.expires_at(never());
|
||||||
|
}
|
||||||
|
|
||||||
|
timeout_opt = opt;
|
||||||
|
}
|
||||||
|
|
||||||
// Determine if an operation should stop and
|
// Determine if an operation should stop and
|
||||||
// deliver an error code to the completion handler.
|
// deliver an error code to the completion handler.
|
||||||
@@ -305,6 +291,14 @@ public:
|
|||||||
bool
|
bool
|
||||||
check_stop_now(error_code& ec)
|
check_stop_now(error_code& ec)
|
||||||
{
|
{
|
||||||
|
// Deliver the timeout to the first caller
|
||||||
|
if(timed_out)
|
||||||
|
{
|
||||||
|
timed_out = false;
|
||||||
|
ec = beast::error::timeout;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// If the stream is closed then abort
|
// If the stream is closed then abort
|
||||||
if( status_ == status::closed ||
|
if( status_ == status::closed ||
|
||||||
status_ == status::failed)
|
status_ == status::failed)
|
||||||
@@ -339,8 +333,11 @@ public:
|
|||||||
{
|
{
|
||||||
switch(new_status)
|
switch(new_status)
|
||||||
{
|
{
|
||||||
|
case status::handshake:
|
||||||
|
break;
|
||||||
|
|
||||||
case status::closing:
|
case status::closing:
|
||||||
BOOST_ASSERT(status_ == status::open);
|
//BOOST_ASSERT(status_ == status::open);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case status::failed:
|
case status::failed:
|
||||||
@@ -353,8 +350,639 @@ public:
|
|||||||
}
|
}
|
||||||
status_ = new_status;
|
status_ = new_status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Called to disarm the idle timeout counter
|
||||||
|
void
|
||||||
|
reset_idle()
|
||||||
|
{
|
||||||
|
idle_counter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maintain the expiration timer
|
||||||
|
template<class Executor>
|
||||||
|
void
|
||||||
|
update_timer(Executor const& ex)
|
||||||
|
{
|
||||||
|
switch(status_)
|
||||||
|
{
|
||||||
|
case status::handshake:
|
||||||
|
BOOST_ASSERT(idle_counter == 0);
|
||||||
|
if(! is_timer_set() &&
|
||||||
|
timeout_opt.handshake_timeout != none())
|
||||||
|
{
|
||||||
|
timer.expires_after(
|
||||||
|
timeout_opt.handshake_timeout);
|
||||||
|
timer.async_wait(
|
||||||
|
timeout_handler<Executor>(
|
||||||
|
ex, this->weak_from_this()));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case status::open:
|
||||||
|
if(timeout_opt.idle_timeout != none())
|
||||||
|
{
|
||||||
|
idle_counter = 0;
|
||||||
|
timer.expires_after(
|
||||||
|
timeout_opt.idle_timeout);
|
||||||
|
timer.async_wait(
|
||||||
|
timeout_handler<Executor>(
|
||||||
|
ex, this->weak_from_this()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
timer.cancel();
|
||||||
|
timer.expires_at(never());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case status::closing:
|
||||||
|
if(timeout_opt.handshake_timeout != none())
|
||||||
|
{
|
||||||
|
idle_counter = 0;
|
||||||
|
timer.expires_after(
|
||||||
|
timeout_opt.handshake_timeout);
|
||||||
|
timer.async_wait(
|
||||||
|
timeout_handler<Executor>(
|
||||||
|
ex, this->weak_from_this()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(! is_timer_set());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case status::failed:
|
||||||
|
case status::closed:
|
||||||
|
// this->close(); // Is this right?
|
||||||
|
timer.cancel();
|
||||||
|
timer.expires_at(never());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool
|
||||||
|
is_timer_set() const
|
||||||
|
{
|
||||||
|
return timer.expiry() != never();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Executor>
|
||||||
|
class timeout_handler
|
||||||
|
: boost::empty_value<Executor>
|
||||||
|
{
|
||||||
|
boost::weak_ptr<impl_type> wp_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
timeout_handler(
|
||||||
|
Executor const& ex,
|
||||||
|
boost::weak_ptr<impl_type>&& wp)
|
||||||
|
: boost::empty_value<Executor>(
|
||||||
|
boost::empty_init_t{}, ex)
|
||||||
|
, wp_(std::move(wp))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
using executor_type = Executor;
|
||||||
|
|
||||||
|
executor_type
|
||||||
|
get_executor() const
|
||||||
|
{
|
||||||
|
return this->get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
operator()(error_code ec)
|
||||||
|
{
|
||||||
|
// timer canceled?
|
||||||
|
if(ec == net::error::operation_aborted)
|
||||||
|
return;
|
||||||
|
BOOST_ASSERT(! ec);
|
||||||
|
|
||||||
|
// stream destroyed?
|
||||||
|
auto sp = wp_.lock();
|
||||||
|
if(! sp)
|
||||||
|
return;
|
||||||
|
auto& impl = *sp;
|
||||||
|
|
||||||
|
switch(impl.status_)
|
||||||
|
{
|
||||||
|
case status::handshake:
|
||||||
|
impl.timed_out = true;
|
||||||
|
close_socket(get_lowest_layer(impl.stream));
|
||||||
|
return;
|
||||||
|
|
||||||
|
case status::open:
|
||||||
|
// timeout was disabled
|
||||||
|
if(impl.timeout_opt.idle_timeout == none())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if( impl.timeout_opt.keep_alive_pings &&
|
||||||
|
impl.idle_counter < 1)
|
||||||
|
{
|
||||||
|
// <- send ping
|
||||||
|
|
||||||
|
++impl.idle_counter;
|
||||||
|
impl.timer.expires_after(
|
||||||
|
impl.timeout_opt.idle_timeout / 2);
|
||||||
|
impl.timer.async_wait(std::move(*this));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// timeout
|
||||||
|
impl.timed_out = true;
|
||||||
|
close_socket(get_lowest_layer(impl.stream));
|
||||||
|
return;
|
||||||
|
|
||||||
|
case status::closing:
|
||||||
|
impl.timed_out = true;
|
||||||
|
close_socket(get_lowest_layer(impl.stream));
|
||||||
|
return;
|
||||||
|
|
||||||
|
case status::closed:
|
||||||
|
case status::failed:
|
||||||
|
// nothing to do?
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// client
|
||||||
|
//
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template<class NextLayer, bool deflateSupported>
|
||||||
|
template<class Decorator>
|
||||||
|
request_type
|
||||||
|
stream<NextLayer, deflateSupported>::impl_type::
|
||||||
|
build_request(
|
||||||
|
detail::sec_ws_key_type& key,
|
||||||
|
string_view host, string_view target,
|
||||||
|
Decorator const& decorator)
|
||||||
|
{
|
||||||
|
request_type req;
|
||||||
|
req.target(target);
|
||||||
|
req.version(11);
|
||||||
|
req.method(http::verb::get);
|
||||||
|
req.set(http::field::host, host);
|
||||||
|
req.set(http::field::upgrade, "websocket");
|
||||||
|
req.set(http::field::connection, "upgrade");
|
||||||
|
detail::make_sec_ws_key(key);
|
||||||
|
req.set(http::field::sec_websocket_key, key);
|
||||||
|
req.set(http::field::sec_websocket_version, "13");
|
||||||
|
this->build_request_pmd(req);
|
||||||
|
decorator(req);
|
||||||
|
if(! req.count(http::field::user_agent))
|
||||||
|
req.set(http::field::user_agent,
|
||||||
|
BOOST_BEAST_VERSION_STRING);
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called when the WebSocket Upgrade response is received
|
||||||
|
template<class NextLayer, bool deflateSupported>
|
||||||
|
void
|
||||||
|
stream<NextLayer, deflateSupported>::impl_type::
|
||||||
|
on_response(
|
||||||
|
response_type const& res,
|
||||||
|
detail::sec_ws_key_type const& key,
|
||||||
|
error_code& ec)
|
||||||
|
{
|
||||||
|
auto const err =
|
||||||
|
[&](error e)
|
||||||
|
{
|
||||||
|
ec = e;
|
||||||
|
};
|
||||||
|
if(res.result() != http::status::switching_protocols)
|
||||||
|
return err(error::upgrade_declined);
|
||||||
|
if(res.version() != 11)
|
||||||
|
return err(error::bad_http_version);
|
||||||
|
{
|
||||||
|
auto const it = res.find(http::field::connection);
|
||||||
|
if(it == res.end())
|
||||||
|
return err(error::no_connection);
|
||||||
|
if(! http::token_list{it->value()}.exists("upgrade"))
|
||||||
|
return err(error::no_connection_upgrade);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto const it = res.find(http::field::upgrade);
|
||||||
|
if(it == res.end())
|
||||||
|
return err(error::no_upgrade);
|
||||||
|
if(! http::token_list{it->value()}.exists("websocket"))
|
||||||
|
return err(error::no_upgrade_websocket);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto const it = res.find(
|
||||||
|
http::field::sec_websocket_accept);
|
||||||
|
if(it == res.end())
|
||||||
|
return err(error::no_sec_accept);
|
||||||
|
detail::sec_ws_accept_type acc;
|
||||||
|
detail::make_sec_ws_accept(acc, key);
|
||||||
|
if(acc.compare(it->value()) != 0)
|
||||||
|
return err(error::bad_sec_accept);
|
||||||
|
}
|
||||||
|
|
||||||
|
ec = {};
|
||||||
|
this->on_response_pmd(res);
|
||||||
|
this->open(role_type::client);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template<class NextLayer, bool deflateSupported>
|
||||||
|
template<class Body, class Allocator, class Decorator>
|
||||||
|
response_type
|
||||||
|
stream<NextLayer, deflateSupported>::impl_type::
|
||||||
|
build_response(
|
||||||
|
http::request<Body,
|
||||||
|
http::basic_fields<Allocator>> const& req,
|
||||||
|
Decorator const& decorator,
|
||||||
|
error_code& result)
|
||||||
|
{
|
||||||
|
auto const decorate =
|
||||||
|
[&decorator](response_type& res)
|
||||||
|
{
|
||||||
|
decorator(res);
|
||||||
|
if(! res.count(http::field::server))
|
||||||
|
{
|
||||||
|
BOOST_STATIC_ASSERT(sizeof(BOOST_BEAST_VERSION_STRING) < 20);
|
||||||
|
static_string<20> s(BOOST_BEAST_VERSION_STRING);
|
||||||
|
res.set(http::field::server, s);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
auto err =
|
||||||
|
[&](error e)
|
||||||
|
{
|
||||||
|
result = e;
|
||||||
|
response_type res;
|
||||||
|
res.version(req.version());
|
||||||
|
res.result(http::status::bad_request);
|
||||||
|
res.body() = result.message();
|
||||||
|
res.prepare_payload();
|
||||||
|
decorate(res);
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
if(req.version() != 11)
|
||||||
|
return err(error::bad_http_version);
|
||||||
|
if(req.method() != http::verb::get)
|
||||||
|
return err(error::bad_method);
|
||||||
|
if(! req.count(http::field::host))
|
||||||
|
return err(error::no_host);
|
||||||
|
{
|
||||||
|
auto const it = req.find(http::field::connection);
|
||||||
|
if(it == req.end())
|
||||||
|
return err(error::no_connection);
|
||||||
|
if(! http::token_list{it->value()}.exists("upgrade"))
|
||||||
|
return err(error::no_connection_upgrade);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto const it = req.find(http::field::upgrade);
|
||||||
|
if(it == req.end())
|
||||||
|
return err(error::no_upgrade);
|
||||||
|
if(! http::token_list{it->value()}.exists("websocket"))
|
||||||
|
return err(error::no_upgrade_websocket);
|
||||||
|
}
|
||||||
|
string_view key;
|
||||||
|
{
|
||||||
|
auto const it = req.find(http::field::sec_websocket_key);
|
||||||
|
if(it == req.end())
|
||||||
|
return err(error::no_sec_key);
|
||||||
|
key = it->value();
|
||||||
|
if(key.size() > detail::sec_ws_key_type::max_size_n)
|
||||||
|
return err(error::bad_sec_key);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto const it = req.find(http::field::sec_websocket_version);
|
||||||
|
if(it == req.end())
|
||||||
|
return err(error::no_sec_version);
|
||||||
|
if(it->value() != "13")
|
||||||
|
{
|
||||||
|
response_type res;
|
||||||
|
res.result(http::status::upgrade_required);
|
||||||
|
res.version(req.version());
|
||||||
|
res.set(http::field::sec_websocket_version, "13");
|
||||||
|
result = error::bad_sec_version;
|
||||||
|
res.body() = result.message();
|
||||||
|
res.prepare_payload();
|
||||||
|
decorate(res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
response_type res;
|
||||||
|
res.result(http::status::switching_protocols);
|
||||||
|
res.version(req.version());
|
||||||
|
res.set(http::field::upgrade, "websocket");
|
||||||
|
res.set(http::field::connection, "upgrade");
|
||||||
|
{
|
||||||
|
detail::sec_ws_accept_type acc;
|
||||||
|
detail::make_sec_ws_accept(acc, key);
|
||||||
|
res.set(http::field::sec_websocket_accept, acc);
|
||||||
|
}
|
||||||
|
this->build_response_pmd(res, req);
|
||||||
|
decorate(res);
|
||||||
|
result = {};
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Attempt to read a complete frame header.
|
||||||
|
// Returns `false` if more bytes are needed
|
||||||
|
template<class NextLayer, bool deflateSupported>
|
||||||
|
template<class DynamicBuffer>
|
||||||
|
bool
|
||||||
|
stream<NextLayer, deflateSupported>::impl_type::
|
||||||
|
parse_fh(
|
||||||
|
detail::frame_header& fh,
|
||||||
|
DynamicBuffer& b,
|
||||||
|
error_code& ec)
|
||||||
|
{
|
||||||
|
if(buffer_size(b.data()) < 2)
|
||||||
|
{
|
||||||
|
// need more bytes
|
||||||
|
ec = {};
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
buffers_suffix<typename
|
||||||
|
DynamicBuffer::const_buffers_type> cb{
|
||||||
|
b.data()};
|
||||||
|
std::size_t need;
|
||||||
|
{
|
||||||
|
std::uint8_t tmp[2];
|
||||||
|
cb.consume(net::buffer_copy(
|
||||||
|
net::buffer(tmp), cb));
|
||||||
|
fh.len = tmp[1] & 0x7f;
|
||||||
|
switch(fh.len)
|
||||||
|
{
|
||||||
|
case 126: need = 2; break;
|
||||||
|
case 127: need = 8; break;
|
||||||
|
default:
|
||||||
|
need = 0;
|
||||||
|
}
|
||||||
|
fh.mask = (tmp[1] & 0x80) != 0;
|
||||||
|
if(fh.mask)
|
||||||
|
need += 4;
|
||||||
|
if(buffer_size(cb) < need)
|
||||||
|
{
|
||||||
|
// need more bytes
|
||||||
|
ec = {};
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
fh.op = static_cast<
|
||||||
|
detail::opcode>(tmp[0] & 0x0f);
|
||||||
|
fh.fin = (tmp[0] & 0x80) != 0;
|
||||||
|
fh.rsv1 = (tmp[0] & 0x40) != 0;
|
||||||
|
fh.rsv2 = (tmp[0] & 0x20) != 0;
|
||||||
|
fh.rsv3 = (tmp[0] & 0x10) != 0;
|
||||||
|
}
|
||||||
|
switch(fh.op)
|
||||||
|
{
|
||||||
|
case detail::opcode::binary:
|
||||||
|
case detail::opcode::text:
|
||||||
|
if(rd_cont)
|
||||||
|
{
|
||||||
|
// new data frame when continuation expected
|
||||||
|
ec = error::bad_data_frame;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(fh.rsv2 || fh.rsv3 ||
|
||||||
|
! this->rd_deflated(fh.rsv1))
|
||||||
|
{
|
||||||
|
// reserved bits not cleared
|
||||||
|
ec = error::bad_reserved_bits;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case detail::opcode::cont:
|
||||||
|
if(! rd_cont)
|
||||||
|
{
|
||||||
|
// continuation without an active message
|
||||||
|
ec = error::bad_continuation;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(fh.rsv1 || fh.rsv2 || fh.rsv3)
|
||||||
|
{
|
||||||
|
// reserved bits not cleared
|
||||||
|
ec = error::bad_reserved_bits;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if(detail::is_reserved(fh.op))
|
||||||
|
{
|
||||||
|
// reserved opcode
|
||||||
|
ec = error::bad_opcode;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(! fh.fin)
|
||||||
|
{
|
||||||
|
// fragmented control message
|
||||||
|
ec = error::bad_control_fragment;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(fh.len > 125)
|
||||||
|
{
|
||||||
|
// invalid length for control message
|
||||||
|
ec = error::bad_control_size;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(fh.rsv1 || fh.rsv2 || fh.rsv3)
|
||||||
|
{
|
||||||
|
// reserved bits not cleared
|
||||||
|
ec = error::bad_reserved_bits;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(role == role_type::server && ! fh.mask)
|
||||||
|
{
|
||||||
|
// unmasked frame from client
|
||||||
|
ec = error::bad_unmasked_frame;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(role == role_type::client && fh.mask)
|
||||||
|
{
|
||||||
|
// masked frame from server
|
||||||
|
ec = error::bad_masked_frame;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(detail::is_control(fh.op) &&
|
||||||
|
buffer_size(cb) < need + fh.len)
|
||||||
|
{
|
||||||
|
// Make the entire control frame payload
|
||||||
|
// get read in before we return `true`
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
switch(fh.len)
|
||||||
|
{
|
||||||
|
case 126:
|
||||||
|
{
|
||||||
|
std::uint8_t tmp[2];
|
||||||
|
BOOST_ASSERT(buffer_size(cb) >= sizeof(tmp));
|
||||||
|
cb.consume(net::buffer_copy(net::buffer(tmp), cb));
|
||||||
|
fh.len = detail::big_uint16_to_native(&tmp[0]);
|
||||||
|
if(fh.len < 126)
|
||||||
|
{
|
||||||
|
// length not canonical
|
||||||
|
ec = error::bad_size;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 127:
|
||||||
|
{
|
||||||
|
std::uint8_t tmp[8];
|
||||||
|
BOOST_ASSERT(buffer_size(cb) >= sizeof(tmp));
|
||||||
|
cb.consume(net::buffer_copy(net::buffer(tmp), cb));
|
||||||
|
fh.len = detail::big_uint64_to_native(&tmp[0]);
|
||||||
|
if(fh.len < 65536)
|
||||||
|
{
|
||||||
|
// length not canonical
|
||||||
|
ec = error::bad_size;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(fh.mask)
|
||||||
|
{
|
||||||
|
std::uint8_t tmp[4];
|
||||||
|
BOOST_ASSERT(buffer_size(cb) >= sizeof(tmp));
|
||||||
|
cb.consume(net::buffer_copy(net::buffer(tmp), cb));
|
||||||
|
fh.key = detail::little_uint32_to_native(&tmp[0]);
|
||||||
|
detail::prepare_key(rd_key, fh.key);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// initialize this otherwise operator== breaks
|
||||||
|
fh.key = 0;
|
||||||
|
}
|
||||||
|
if(! detail::is_control(fh.op))
|
||||||
|
{
|
||||||
|
if(fh.op != detail::opcode::cont)
|
||||||
|
{
|
||||||
|
rd_size = 0;
|
||||||
|
rd_op = fh.op;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(rd_size > (std::numeric_limits<
|
||||||
|
std::uint64_t>::max)() - fh.len)
|
||||||
|
{
|
||||||
|
// message size exceeds configured limit
|
||||||
|
ec = error::message_too_big;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(! this->rd_deflated())
|
||||||
|
{
|
||||||
|
if(rd_msg_max && beast::detail::sum_exceeds(
|
||||||
|
rd_size, fh.len, rd_msg_max))
|
||||||
|
{
|
||||||
|
// message size exceeds configured limit
|
||||||
|
ec = error::message_too_big;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rd_cont = ! fh.fin;
|
||||||
|
rd_remain = fh.len;
|
||||||
|
}
|
||||||
|
b.consume(b.size() - buffer_size(cb));
|
||||||
|
ec = {};
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer, bool deflateSupported>
|
||||||
|
template<class DynamicBuffer>
|
||||||
|
void
|
||||||
|
stream<NextLayer, deflateSupported>::impl_type::
|
||||||
|
write_ping(DynamicBuffer& db,
|
||||||
|
detail::opcode code, ping_data const& data)
|
||||||
|
{
|
||||||
|
detail::frame_header fh;
|
||||||
|
fh.op = code;
|
||||||
|
fh.fin = true;
|
||||||
|
fh.rsv1 = false;
|
||||||
|
fh.rsv2 = false;
|
||||||
|
fh.rsv3 = false;
|
||||||
|
fh.len = data.size();
|
||||||
|
fh.mask = role == role_type::client;
|
||||||
|
if(fh.mask)
|
||||||
|
fh.key = create_mask();
|
||||||
|
detail::write(db, fh);
|
||||||
|
if(data.empty())
|
||||||
|
return;
|
||||||
|
detail::prepared_key key;
|
||||||
|
if(fh.mask)
|
||||||
|
detail::prepare_key(key, fh.key);
|
||||||
|
auto mb = db.prepare(data.size());
|
||||||
|
net::buffer_copy(mb,
|
||||||
|
net::const_buffer(
|
||||||
|
data.data(), data.size()));
|
||||||
|
if(fh.mask)
|
||||||
|
detail::mask_inplace(mb, key);
|
||||||
|
db.commit(data.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer, bool deflateSupported>
|
||||||
|
template<class DynamicBuffer>
|
||||||
|
void
|
||||||
|
stream<NextLayer, deflateSupported>::impl_type::
|
||||||
|
write_close(DynamicBuffer& db, close_reason const& cr)
|
||||||
|
{
|
||||||
|
using namespace boost::endian;
|
||||||
|
detail::frame_header fh;
|
||||||
|
fh.op = detail::opcode::close;
|
||||||
|
fh.fin = true;
|
||||||
|
fh.rsv1 = false;
|
||||||
|
fh.rsv2 = false;
|
||||||
|
fh.rsv3 = false;
|
||||||
|
fh.len = cr.code == close_code::none ?
|
||||||
|
0 : 2 + cr.reason.size();
|
||||||
|
if(role == role_type::client)
|
||||||
|
{
|
||||||
|
fh.mask = true;
|
||||||
|
fh.key = create_mask();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fh.mask = false;
|
||||||
|
}
|
||||||
|
detail::write(db, fh);
|
||||||
|
if(cr.code != close_code::none)
|
||||||
|
{
|
||||||
|
detail::prepared_key key;
|
||||||
|
if(fh.mask)
|
||||||
|
detail::prepare_key(key, fh.key);
|
||||||
|
{
|
||||||
|
std::uint8_t tmp[2];
|
||||||
|
::new(&tmp[0]) big_uint16_buf_t{
|
||||||
|
(std::uint16_t)cr.code};
|
||||||
|
auto mb = db.prepare(2);
|
||||||
|
net::buffer_copy(mb,
|
||||||
|
net::buffer(tmp));
|
||||||
|
if(fh.mask)
|
||||||
|
detail::mask_inplace(mb, key);
|
||||||
|
db.commit(2);
|
||||||
|
}
|
||||||
|
if(! cr.reason.empty())
|
||||||
|
{
|
||||||
|
auto mb = db.prepare(cr.reason.size());
|
||||||
|
net::buffer_copy(mb,
|
||||||
|
net::const_buffer(
|
||||||
|
cr.reason.data(), cr.reason.size()));
|
||||||
|
if(fh.mask)
|
||||||
|
detail::mask_inplace(mb, key);
|
||||||
|
db.commit(cr.reason.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // websocket
|
} // websocket
|
||||||
} // beast
|
} // beast
|
||||||
} // boost
|
} // boost
|
||||||
|
@@ -24,6 +24,7 @@
|
|||||||
#include <boost/beast/core/detail/clamp.hpp>
|
#include <boost/beast/core/detail/clamp.hpp>
|
||||||
#include <boost/beast/core/detail/config.hpp>
|
#include <boost/beast/core/detail/config.hpp>
|
||||||
#include <boost/beast/websocket/detail/frame.hpp>
|
#include <boost/beast/websocket/detail/frame.hpp>
|
||||||
|
#include <boost/beast/websocket/impl/stream_impl.hpp>
|
||||||
#include <boost/asio/coroutine.hpp>
|
#include <boost/asio/coroutine.hpp>
|
||||||
#include <boost/assert.hpp>
|
#include <boost/assert.hpp>
|
||||||
#include <boost/config.hpp>
|
#include <boost/config.hpp>
|
||||||
@@ -51,7 +52,7 @@ class stream<NextLayer, deflateSupported>::write_some_op
|
|||||||
do_deflate
|
do_deflate
|
||||||
};
|
};
|
||||||
|
|
||||||
stream& ws_;
|
boost::weak_ptr<impl_type> wp_;
|
||||||
buffers_suffix<Buffers> cb_;
|
buffers_suffix<Buffers> cb_;
|
||||||
detail::frame_header fh_;
|
detail::frame_header fh_;
|
||||||
detail::prepared_key key_;
|
detail::prepared_key key_;
|
||||||
@@ -69,17 +70,18 @@ public:
|
|||||||
template<class Handler_>
|
template<class Handler_>
|
||||||
write_some_op(
|
write_some_op(
|
||||||
Handler_&& h,
|
Handler_&& h,
|
||||||
stream<NextLayer, deflateSupported>& ws,
|
boost::shared_ptr<impl_type> const& sp,
|
||||||
bool fin,
|
bool fin,
|
||||||
Buffers const& bs)
|
Buffers const& bs)
|
||||||
: beast::async_op_base<Handler,
|
: beast::async_op_base<Handler,
|
||||||
beast::executor_type<stream>>(
|
beast::executor_type<stream>>(
|
||||||
std::forward<Handler_>(h), ws.get_executor())
|
std::forward<Handler_>(h),
|
||||||
, ws_(ws)
|
sp->stream.get_executor())
|
||||||
|
, wp_(sp)
|
||||||
, cb_(bs)
|
, cb_(bs)
|
||||||
, fin_(fin)
|
, fin_(fin)
|
||||||
{
|
{
|
||||||
auto& impl = *ws_.impl_;
|
auto& impl = *sp;
|
||||||
|
|
||||||
// Set up the outgoing frame header
|
// Set up the outgoing frame header
|
||||||
if(! impl.wr_cont)
|
if(! impl.wr_cont)
|
||||||
@@ -157,7 +159,11 @@ operator()(
|
|||||||
using beast::detail::clamp;
|
using beast::detail::clamp;
|
||||||
std::size_t n;
|
std::size_t n;
|
||||||
net::mutable_buffer b;
|
net::mutable_buffer b;
|
||||||
auto& impl = *ws_.impl_;
|
auto sp = wp_.lock();
|
||||||
|
if(! sp)
|
||||||
|
return this->invoke(cont,
|
||||||
|
net::error::operation_aborted, 0);
|
||||||
|
auto& impl = *sp;
|
||||||
BOOST_ASIO_CORO_REENTER(*this)
|
BOOST_ASIO_CORO_REENTER(*this)
|
||||||
{
|
{
|
||||||
// Acquire the write lock
|
// Acquire the write lock
|
||||||
@@ -165,7 +171,7 @@ operator()(
|
|||||||
{
|
{
|
||||||
do_suspend:
|
do_suspend:
|
||||||
BOOST_ASIO_CORO_YIELD
|
BOOST_ASIO_CORO_YIELD
|
||||||
impl.paused_wr.emplace(std::move(*this));
|
impl.op_wr.emplace(std::move(*this));
|
||||||
impl.wr_block.lock(this);
|
impl.wr_block.lock(this);
|
||||||
BOOST_ASIO_CORO_YIELD
|
BOOST_ASIO_CORO_YIELD
|
||||||
net::post(std::move(*this));
|
net::post(std::move(*this));
|
||||||
@@ -228,9 +234,10 @@ operator()(
|
|||||||
// Give up the write lock in between each frame
|
// Give up the write lock in between each frame
|
||||||
// so that outgoing control frames might be sent.
|
// so that outgoing control frames might be sent.
|
||||||
impl.wr_block.unlock(this);
|
impl.wr_block.unlock(this);
|
||||||
if( impl.paused_close.maybe_invoke() ||
|
if( impl.op_close.maybe_invoke()
|
||||||
impl.paused_rd.maybe_invoke() ||
|
|| impl.op_idle_ping.maybe_invoke()
|
||||||
impl.paused_ping.maybe_invoke())
|
|| impl.op_rd.maybe_invoke()
|
||||||
|
|| impl.op_ping.maybe_invoke())
|
||||||
{
|
{
|
||||||
BOOST_ASSERT(impl.wr_block.is_locked());
|
BOOST_ASSERT(impl.wr_block.is_locked());
|
||||||
goto do_suspend;
|
goto do_suspend;
|
||||||
@@ -248,7 +255,7 @@ operator()(
|
|||||||
remain_ = beast::buffer_size(cb_);
|
remain_ = beast::buffer_size(cb_);
|
||||||
fh_.fin = fin_;
|
fh_.fin = fin_;
|
||||||
fh_.len = remain_;
|
fh_.len = remain_;
|
||||||
fh_.key = ws_.create_mask();
|
fh_.key = impl.create_mask();
|
||||||
detail::prepare_key(key_, fh_.key);
|
detail::prepare_key(key_, fh_.key);
|
||||||
impl.wr_fb.clear();
|
impl.wr_fb.clear();
|
||||||
detail::write<flat_static_buffer_base>(
|
detail::write<flat_static_buffer_base>(
|
||||||
@@ -302,7 +309,7 @@ operator()(
|
|||||||
n = clamp(remain_, impl.wr_buf_size);
|
n = clamp(remain_, impl.wr_buf_size);
|
||||||
remain_ -= n;
|
remain_ -= n;
|
||||||
fh_.len = n;
|
fh_.len = n;
|
||||||
fh_.key = ws_.create_mask();
|
fh_.key = impl.create_mask();
|
||||||
fh_.fin = fin_ ? remain_ == 0 : false;
|
fh_.fin = fin_ ? remain_ == 0 : false;
|
||||||
detail::prepare_key(key_, fh_.key);
|
detail::prepare_key(key_, fh_.key);
|
||||||
net::buffer_copy(net::buffer(
|
net::buffer_copy(net::buffer(
|
||||||
@@ -330,9 +337,10 @@ operator()(
|
|||||||
// Give up the write lock in between each frame
|
// Give up the write lock in between each frame
|
||||||
// so that outgoing control frames might be sent.
|
// so that outgoing control frames might be sent.
|
||||||
impl.wr_block.unlock(this);
|
impl.wr_block.unlock(this);
|
||||||
if( impl.paused_close.maybe_invoke() ||
|
if( impl.op_close.maybe_invoke()
|
||||||
impl.paused_rd.maybe_invoke() ||
|
|| impl.op_idle_ping.maybe_invoke()
|
||||||
impl.paused_ping.maybe_invoke())
|
|| impl.op_rd.maybe_invoke()
|
||||||
|
|| impl.op_ping.maybe_invoke())
|
||||||
{
|
{
|
||||||
BOOST_ASSERT(impl.wr_block.is_locked());
|
BOOST_ASSERT(impl.wr_block.is_locked());
|
||||||
goto do_suspend;
|
goto do_suspend;
|
||||||
@@ -365,7 +373,7 @@ operator()(
|
|||||||
}
|
}
|
||||||
if(fh_.mask)
|
if(fh_.mask)
|
||||||
{
|
{
|
||||||
fh_.key = ws_.create_mask();
|
fh_.key = impl.create_mask();
|
||||||
detail::prepared_key key;
|
detail::prepared_key key;
|
||||||
detail::prepare_key(key, fh_.key);
|
detail::prepare_key(key, fh_.key);
|
||||||
detail::mask_inplace(b, key);
|
detail::mask_inplace(b, key);
|
||||||
@@ -391,9 +399,10 @@ operator()(
|
|||||||
// Give up the write lock in between each frame
|
// Give up the write lock in between each frame
|
||||||
// so that outgoing control frames might be sent.
|
// so that outgoing control frames might be sent.
|
||||||
impl.wr_block.unlock(this);
|
impl.wr_block.unlock(this);
|
||||||
if( impl.paused_close.maybe_invoke() ||
|
if( impl.op_close.maybe_invoke()
|
||||||
impl.paused_rd.maybe_invoke() ||
|
|| impl.op_idle_ping.maybe_invoke()
|
||||||
impl.paused_ping.maybe_invoke())
|
|| impl.op_rd.maybe_invoke()
|
||||||
|
|| impl.op_ping.maybe_invoke())
|
||||||
{
|
{
|
||||||
BOOST_ASSERT(impl.wr_block.is_locked());
|
BOOST_ASSERT(impl.wr_block.is_locked());
|
||||||
goto do_suspend;
|
goto do_suspend;
|
||||||
@@ -413,15 +422,10 @@ operator()(
|
|||||||
|
|
||||||
upcall:
|
upcall:
|
||||||
impl.wr_block.unlock(this);
|
impl.wr_block.unlock(this);
|
||||||
impl.paused_close.maybe_invoke()
|
impl.op_close.maybe_invoke()
|
||||||
|| impl.paused_rd.maybe_invoke()
|
|| impl.op_idle_ping.maybe_invoke()
|
||||||
|| impl.paused_ping.maybe_invoke();
|
|| impl.op_rd.maybe_invoke()
|
||||||
if(! cont)
|
|| impl.op_ping.maybe_invoke();
|
||||||
{
|
|
||||||
BOOST_ASIO_CORO_YIELD
|
|
||||||
net::post(bind_front_handler(
|
|
||||||
std::move(*this), ec, bytes_transferred_));
|
|
||||||
}
|
|
||||||
this->invoke(cont, ec, bytes_transferred_);
|
this->invoke(cont, ec, bytes_transferred_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -507,7 +511,7 @@ write_some(bool fin,
|
|||||||
}
|
}
|
||||||
if(fh.mask)
|
if(fh.mask)
|
||||||
{
|
{
|
||||||
fh.key = this->create_mask();
|
fh.key = this->impl_->create_mask();
|
||||||
detail::prepared_key key;
|
detail::prepared_key key;
|
||||||
detail::prepare_key(key, fh.key);
|
detail::prepare_key(key, fh.key);
|
||||||
detail::mask_inplace(b, key);
|
detail::mask_inplace(b, key);
|
||||||
@@ -581,7 +585,7 @@ write_some(bool fin,
|
|||||||
// mask, no autofrag
|
// mask, no autofrag
|
||||||
fh.fin = fin;
|
fh.fin = fin;
|
||||||
fh.len = remain;
|
fh.len = remain;
|
||||||
fh.key = this->create_mask();
|
fh.key = this->impl_->create_mask();
|
||||||
detail::prepared_key key;
|
detail::prepared_key key;
|
||||||
detail::prepare_key(key, fh.key);
|
detail::prepare_key(key, fh.key);
|
||||||
detail::fh_buffer fh_buf;
|
detail::fh_buffer fh_buf;
|
||||||
@@ -629,7 +633,7 @@ write_some(bool fin,
|
|||||||
ConstBufferSequence> cb(buffers);
|
ConstBufferSequence> cb(buffers);
|
||||||
for(;;)
|
for(;;)
|
||||||
{
|
{
|
||||||
fh.key = this->create_mask();
|
fh.key = this->impl_->create_mask();
|
||||||
detail::prepared_key key;
|
detail::prepared_key key;
|
||||||
detail::prepare_key(key, fh.key);
|
detail::prepare_key(key, fh.key);
|
||||||
auto const n =
|
auto const n =
|
||||||
@@ -676,7 +680,7 @@ async_write_some(bool fin,
|
|||||||
WriteHandler, void(error_code, std::size_t));
|
WriteHandler, void(error_code, std::size_t));
|
||||||
write_some_op<ConstBufferSequence, BOOST_ASIO_HANDLER_TYPE(
|
write_some_op<ConstBufferSequence, BOOST_ASIO_HANDLER_TYPE(
|
||||||
WriteHandler, void(error_code, std::size_t))>(
|
WriteHandler, void(error_code, std::size_t))>(
|
||||||
std::move(init.completion_handler), *this, fin, bs);
|
std::move(init.completion_handler), impl_, fin, bs);
|
||||||
return init.result.get();
|
return init.result.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -731,7 +735,7 @@ async_write(
|
|||||||
WriteHandler, void(error_code, std::size_t));
|
WriteHandler, void(error_code, std::size_t));
|
||||||
write_some_op<ConstBufferSequence, BOOST_ASIO_HANDLER_TYPE(
|
write_some_op<ConstBufferSequence, BOOST_ASIO_HANDLER_TYPE(
|
||||||
WriteHandler, void(error_code, std::size_t))>(
|
WriteHandler, void(error_code, std::size_t))>(
|
||||||
std::move(init.completion_handler), *this, true, bs);
|
std::move(init.completion_handler), impl_, true, bs);
|
||||||
return init.result.get();
|
return init.result.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -15,15 +15,17 @@
|
|||||||
#include <boost/beast/websocket/option.hpp>
|
#include <boost/beast/websocket/option.hpp>
|
||||||
#include <boost/beast/websocket/role.hpp>
|
#include <boost/beast/websocket/role.hpp>
|
||||||
#include <boost/beast/websocket/rfc6455.hpp>
|
#include <boost/beast/websocket/rfc6455.hpp>
|
||||||
|
#include <boost/beast/websocket/stream_base.hpp>
|
||||||
#include <boost/beast/websocket/stream_fwd.hpp>
|
#include <boost/beast/websocket/stream_fwd.hpp>
|
||||||
#include <boost/beast/websocket/detail/pmd_extension.hpp>
|
|
||||||
#include <boost/beast/websocket/detail/stream_base.hpp>
|
|
||||||
#include <boost/beast/websocket/detail/hybi13.hpp>
|
#include <boost/beast/websocket/detail/hybi13.hpp>
|
||||||
|
#include <boost/beast/websocket/detail/impl_base.hpp>
|
||||||
|
#include <boost/beast/websocket/detail/pmd_extension.hpp>
|
||||||
#include <boost/beast/core/string.hpp>
|
#include <boost/beast/core/string.hpp>
|
||||||
#include <boost/beast/core/detail/type_traits.hpp>
|
#include <boost/beast/core/detail/type_traits.hpp>
|
||||||
#include <boost/beast/http/detail/type_traits.hpp>
|
#include <boost/beast/http/detail/type_traits.hpp>
|
||||||
#include <boost/asio/async_result.hpp>
|
#include <boost/asio/async_result.hpp>
|
||||||
#include <boost/asio/error.hpp>
|
#include <boost/asio/error.hpp>
|
||||||
|
#include <boost/shared_ptr.hpp>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
@@ -81,17 +83,16 @@ class frame_test;
|
|||||||
operations are performed within the same implicit or explicit strand.
|
operations are performed within the same implicit or explicit strand.
|
||||||
|
|
||||||
@par Example
|
@par Example
|
||||||
|
To declare the @ref stream object with a @ref tcp_stream in a
|
||||||
To use the @ref stream template with an `ip::tcp::socket`,
|
multi-threaded asynchronous program using a strand, you may write:
|
||||||
you would write:
|
|
||||||
|
|
||||||
@code
|
@code
|
||||||
websocket::stream<ip::tcp::socket> ws{io_context};
|
websocket::stream<tcp_stream<
|
||||||
|
net::io_context::strand>> ws{net::io_context::strand(ioc)};
|
||||||
@endcode
|
@endcode
|
||||||
Alternatively, you can write:
|
Alternatively, for a single-threaded or synchronous application
|
||||||
|
you may write:
|
||||||
@code
|
@code
|
||||||
ip::tcp::socket sock{io_context};
|
websocket::stream<tcp_stream<net::io_context::executor_type>> ws(ioc);
|
||||||
websocket::stream<ip::tcp::socket&> ws{sock};
|
|
||||||
@endcode
|
@endcode
|
||||||
|
|
||||||
@tparam NextLayer The type representing the next layer, to which
|
@tparam NextLayer The type representing the next layer, to which
|
||||||
@@ -130,14 +131,14 @@ template<
|
|||||||
bool deflateSupported>
|
bool deflateSupported>
|
||||||
class stream
|
class stream
|
||||||
#if ! BOOST_BEAST_DOXYGEN
|
#if ! BOOST_BEAST_DOXYGEN
|
||||||
: private detail::stream_base
|
: private stream_base
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
friend class close_test;
|
friend class close_test;
|
||||||
friend class frame_test;
|
friend class frame_test;
|
||||||
friend class ping_test;
|
friend class ping_test;
|
||||||
friend class read1_test;
|
|
||||||
friend class read2_test;
|
friend class read2_test;
|
||||||
|
friend class read3_test;
|
||||||
friend class stream_test;
|
friend class stream_test;
|
||||||
friend class write_test;
|
friend class write_test;
|
||||||
|
|
||||||
@@ -153,7 +154,7 @@ class stream
|
|||||||
|
|
||||||
struct impl_type;
|
struct impl_type;
|
||||||
|
|
||||||
std::shared_ptr<impl_type> impl_;
|
boost::shared_ptr<impl_type> impl_;
|
||||||
|
|
||||||
using time_point = typename
|
using time_point = typename
|
||||||
std::chrono::steady_clock::time_point;
|
std::chrono::steady_clock::time_point;
|
||||||
@@ -351,6 +352,20 @@ public:
|
|||||||
//
|
//
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#if BOOST_BEAST_DOXYGEN
|
||||||
|
template<class Option>
|
||||||
|
void
|
||||||
|
get_option(Option& opt);
|
||||||
|
|
||||||
|
template<class Option>
|
||||||
|
void
|
||||||
|
set_option(Option const& opt);
|
||||||
|
#else
|
||||||
|
|
||||||
|
void get_option(timeout& opt);
|
||||||
|
void set_option(timeout const& opt);
|
||||||
|
#endif
|
||||||
|
|
||||||
/** Set the permessage-deflate extension options
|
/** Set the permessage-deflate extension options
|
||||||
|
|
||||||
@throws invalid_argument if `deflateSupported == false`, and either
|
@throws invalid_argument if `deflateSupported == false`, and either
|
||||||
@@ -3478,6 +3493,7 @@ private:
|
|||||||
template<class> class close_op;
|
template<class> class close_op;
|
||||||
template<class> class handshake_op;
|
template<class> class handshake_op;
|
||||||
template<class> class ping_op;
|
template<class> class ping_op;
|
||||||
|
template<class> class auto_ping_op;
|
||||||
template<class, class> class read_some_op;
|
template<class, class> class read_some_op;
|
||||||
template<class, class> class read_op;
|
template<class, class> class read_op;
|
||||||
template<class> class response_op;
|
template<class> class response_op;
|
||||||
@@ -3487,55 +3503,14 @@ private:
|
|||||||
static void default_decorate_req(request_type&) {}
|
static void default_decorate_req(request_type&) {}
|
||||||
static void default_decorate_res(response_type&) {}
|
static void default_decorate_res(response_type&) {}
|
||||||
|
|
||||||
template<class DynamicBuffer>
|
|
||||||
bool
|
|
||||||
parse_fh(
|
|
||||||
detail::frame_header& fh,
|
|
||||||
DynamicBuffer& b,
|
|
||||||
error_code& ec);
|
|
||||||
|
|
||||||
template<class DynamicBuffer>
|
|
||||||
void
|
|
||||||
write_close(DynamicBuffer& b, close_reason const& rc);
|
|
||||||
|
|
||||||
template<class DynamicBuffer>
|
|
||||||
void
|
|
||||||
write_ping(DynamicBuffer& b,
|
|
||||||
detail::opcode op, ping_data const& data);
|
|
||||||
|
|
||||||
//
|
|
||||||
// upgrade
|
|
||||||
//
|
|
||||||
|
|
||||||
template<class Decorator>
|
|
||||||
request_type
|
|
||||||
build_request(detail::sec_ws_key_type& key,
|
|
||||||
string_view host,
|
|
||||||
string_view target,
|
|
||||||
Decorator const& decorator);
|
|
||||||
|
|
||||||
template<
|
|
||||||
class Body, class Allocator, class Decorator>
|
|
||||||
response_type
|
|
||||||
build_response(
|
|
||||||
http::request<Body,
|
|
||||||
http::basic_fields<Allocator>> const& req,
|
|
||||||
Decorator const& decorator,
|
|
||||||
error_code& ec);
|
|
||||||
|
|
||||||
void
|
|
||||||
on_response(
|
|
||||||
response_type const& res,
|
|
||||||
detail::sec_ws_key_type const& key,
|
|
||||||
error_code& ec);
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// accept / handshake
|
// accept / handshake
|
||||||
//
|
//
|
||||||
|
|
||||||
template<class Decorator>
|
template<class Buffers, class Decorator>
|
||||||
void
|
void
|
||||||
do_accept(
|
do_accept(
|
||||||
|
Buffers const& buffers,
|
||||||
Decorator const& decorator,
|
Decorator const& decorator,
|
||||||
error_code& ec);
|
error_code& ec);
|
||||||
|
|
||||||
|
139
include/boost/beast/websocket/stream_base.hpp
Normal file
139
include/boost/beast/websocket/stream_base.hpp
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
// Official repository: https://github.com/boostorg/beast
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BOOST_BEAST_WEBSOCKET_STREAM_BASE_HPP
|
||||||
|
#define BOOST_BEAST_WEBSOCKET_STREAM_BASE_HPP
|
||||||
|
|
||||||
|
#include <boost/beast/core/detail/config.hpp>
|
||||||
|
#include <boost/beast/websocket/role.hpp>
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
namespace boost {
|
||||||
|
namespace beast {
|
||||||
|
namespace websocket {
|
||||||
|
|
||||||
|
/** This class is used as a base for the @ref websocket::stream class template to group common types and constants.
|
||||||
|
*/
|
||||||
|
struct stream_base
|
||||||
|
{
|
||||||
|
/// The type used to represent durations
|
||||||
|
using duration =
|
||||||
|
std::chrono::steady_clock::duration;
|
||||||
|
|
||||||
|
/// The type used to represent time points
|
||||||
|
using time_point =
|
||||||
|
std::chrono::steady_clock::time_point;
|
||||||
|
|
||||||
|
/// Returns the special time_point value meaning "never"
|
||||||
|
static
|
||||||
|
constexpr
|
||||||
|
time_point
|
||||||
|
never() noexcept
|
||||||
|
{
|
||||||
|
return (time_point::max)();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the special duration value meaning "none"
|
||||||
|
static
|
||||||
|
constexpr
|
||||||
|
duration
|
||||||
|
none() noexcept
|
||||||
|
{
|
||||||
|
return (duration::max)();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Stream option to control the behavior of websocket timeouts.
|
||||||
|
|
||||||
|
Timeout features are available for asynchronous operations only.
|
||||||
|
*/
|
||||||
|
struct timeout
|
||||||
|
{
|
||||||
|
/** Time limit on handshake, accept, and close operations:
|
||||||
|
|
||||||
|
This value whether or not there is a time limit, and the
|
||||||
|
duration of that time limit, for asynchronous handshake,
|
||||||
|
accept, and close operations. If this is equal to the
|
||||||
|
value @ref none then there will be no time limit. Otherwise,
|
||||||
|
if any of the applicable operations takes longer than this
|
||||||
|
amount of time, the operation will be canceled and a
|
||||||
|
timeout error delivered to the completion handler.
|
||||||
|
*/
|
||||||
|
duration handshake_timeout = none();
|
||||||
|
|
||||||
|
/** The time limit after which a connection is considered idle.
|
||||||
|
*/
|
||||||
|
duration idle_timeout = none();
|
||||||
|
|
||||||
|
/** Automatic ping setting.
|
||||||
|
|
||||||
|
If the idle interval is set, this setting affects the
|
||||||
|
behavior of the stream when no data is received for the
|
||||||
|
timeout interval as follows:
|
||||||
|
|
||||||
|
@li When `keep_alive_pings` is `true`, an idle ping will be
|
||||||
|
sent automatically. If another timeout interval elapses
|
||||||
|
with no received data then the connection will be closed.
|
||||||
|
An outstanding read operation must be pending, which will
|
||||||
|
complete immediately the error @ref beast::error::timeout.
|
||||||
|
|
||||||
|
@li When `keep_alive_pings` is `false`, the connection will be closed.
|
||||||
|
An outstanding read operation must be pending, which will
|
||||||
|
complete immediately the error @ref beast::error::timeout.
|
||||||
|
*/
|
||||||
|
bool keep_alive_pings = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Construct timeout settings with suggested values for a role.
|
||||||
|
|
||||||
|
This constructs the timeout settings with a predefined set
|
||||||
|
of values which varies depending on the desired role. The
|
||||||
|
values are selected upon construction, regardless of the
|
||||||
|
current or actual role in use on the stream.
|
||||||
|
|
||||||
|
@param role The role of the websocket stream
|
||||||
|
(@ref role_type::client or @ref role_type::server).
|
||||||
|
*/
|
||||||
|
static
|
||||||
|
timeout
|
||||||
|
suggested_settings(role_type role) noexcept
|
||||||
|
{
|
||||||
|
timeout opt;
|
||||||
|
switch(role)
|
||||||
|
{
|
||||||
|
case role_type::client:
|
||||||
|
opt.handshake_timeout = std::chrono::seconds(30);
|
||||||
|
opt.idle_timeout = none();
|
||||||
|
opt.keep_alive_pings = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case role_type::server:
|
||||||
|
opt.handshake_timeout = std::chrono::seconds(30);
|
||||||
|
opt.idle_timeout = std::chrono::seconds(300);
|
||||||
|
opt.keep_alive_pings = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
enum class status
|
||||||
|
{
|
||||||
|
//none,
|
||||||
|
handshake,
|
||||||
|
open,
|
||||||
|
closing,
|
||||||
|
closed,
|
||||||
|
failed // VFALCO Is this needed?
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // websocket
|
||||||
|
} // beast
|
||||||
|
} // boost
|
||||||
|
|
||||||
|
#endif
|
@@ -17,7 +17,7 @@ add_executable (tests-beast-websocket
|
|||||||
${TEST_MAIN}
|
${TEST_MAIN}
|
||||||
Jamfile
|
Jamfile
|
||||||
_detail_prng.cpp
|
_detail_prng.cpp
|
||||||
_detail_stream_base.cpp
|
_detail_impl_base.cpp
|
||||||
test.hpp
|
test.hpp
|
||||||
_detail_prng.cpp
|
_detail_prng.cpp
|
||||||
accept.cpp
|
accept.cpp
|
||||||
@@ -29,6 +29,7 @@ add_executable (tests-beast-websocket
|
|||||||
ping.cpp
|
ping.cpp
|
||||||
read1.cpp
|
read1.cpp
|
||||||
read2.cpp
|
read2.cpp
|
||||||
|
read3.cpp
|
||||||
rfc6455.cpp
|
rfc6455.cpp
|
||||||
role.cpp
|
role.cpp
|
||||||
stream.cpp
|
stream.cpp
|
||||||
|
@@ -8,8 +8,8 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
local SOURCES =
|
local SOURCES =
|
||||||
|
_detail_impl_base.cpp
|
||||||
_detail_prng.cpp
|
_detail_prng.cpp
|
||||||
_detail_stream_base.cpp
|
|
||||||
accept.cpp
|
accept.cpp
|
||||||
close.cpp
|
close.cpp
|
||||||
error.cpp
|
error.cpp
|
||||||
@@ -19,6 +19,7 @@ local SOURCES =
|
|||||||
ping.cpp
|
ping.cpp
|
||||||
read1.cpp
|
read1.cpp
|
||||||
read2.cpp
|
read2.cpp
|
||||||
|
read3.cpp
|
||||||
rfc6455.cpp
|
rfc6455.cpp
|
||||||
role.cpp
|
role.cpp
|
||||||
stream.cpp
|
stream.cpp
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// Copyright (w) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
//
|
//
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
// 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)
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
@@ -8,4 +8,4 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
// Test that header file is self-contained.
|
// Test that header file is self-contained.
|
||||||
#include <boost/beast/websocket/detail/stream_base.hpp>
|
#include <boost/beast/websocket/detail/impl_base.hpp>
|
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// Copyright (w) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
//
|
//
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
// 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)
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// Copyright (w) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
//
|
//
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
// 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)
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
@@ -10,6 +10,7 @@
|
|||||||
// Test that header file is self-contained.
|
// Test that header file is self-contained.
|
||||||
#include <boost/beast/websocket/stream.hpp>
|
#include <boost/beast/websocket/stream.hpp>
|
||||||
|
|
||||||
|
#include <boost/beast/_experimental/test/tcp.hpp>
|
||||||
#include "test.hpp"
|
#include "test.hpp"
|
||||||
|
|
||||||
#include <boost/asio/io_context.hpp>
|
#include <boost/asio/io_context.hpp>
|
||||||
@@ -613,6 +614,94 @@ public:
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testTimeout()
|
||||||
|
{
|
||||||
|
using tcp = net::ip::tcp;
|
||||||
|
|
||||||
|
net::io_context ioc;
|
||||||
|
|
||||||
|
// success
|
||||||
|
|
||||||
|
{
|
||||||
|
stream<tcp::socket> ws1(ioc);
|
||||||
|
stream<tcp::socket> ws2(ioc);
|
||||||
|
test::connect(ws1.next_layer(), ws2.next_layer());
|
||||||
|
|
||||||
|
ws1.async_handshake("test", "/", test::success_handler());
|
||||||
|
ws2.async_accept(test::success_handler());
|
||||||
|
test::run_for(ioc, std::chrono::seconds(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
stream<test::stream> ws1(ioc);
|
||||||
|
stream<test::stream> ws2(ioc);
|
||||||
|
test::connect(ws1.next_layer(), ws2.next_layer());
|
||||||
|
|
||||||
|
ws1.async_handshake("test", "/", test::success_handler());
|
||||||
|
ws2.async_accept(test::success_handler());
|
||||||
|
test::run_for(ioc, std::chrono::seconds(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// success, timeout enabled
|
||||||
|
|
||||||
|
{
|
||||||
|
stream<tcp::socket> ws1(ioc);
|
||||||
|
stream<tcp::socket> ws2(ioc);
|
||||||
|
test::connect(ws1.next_layer(), ws2.next_layer());
|
||||||
|
|
||||||
|
ws1.set_option(stream_base::timeout{
|
||||||
|
std::chrono::milliseconds(50),
|
||||||
|
stream_base::none(),
|
||||||
|
false});
|
||||||
|
ws1.async_accept(test::success_handler());
|
||||||
|
ws2.async_handshake("test", "/", test::success_handler());
|
||||||
|
test::run_for(ioc, std::chrono::seconds(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
stream<test::stream> ws1(ioc);
|
||||||
|
stream<test::stream> ws2(ioc);
|
||||||
|
test::connect(ws1.next_layer(), ws2.next_layer());
|
||||||
|
|
||||||
|
ws1.set_option(stream_base::timeout{
|
||||||
|
std::chrono::milliseconds(50),
|
||||||
|
stream_base::none(),
|
||||||
|
false});
|
||||||
|
ws1.async_accept(test::success_handler());
|
||||||
|
ws2.async_handshake("test", "/", test::success_handler());
|
||||||
|
test::run_for(ioc, std::chrono::seconds(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// timeout
|
||||||
|
|
||||||
|
{
|
||||||
|
stream<tcp::socket> ws1(ioc);
|
||||||
|
stream<tcp::socket> ws2(ioc);
|
||||||
|
test::connect(ws1.next_layer(), ws2.next_layer());
|
||||||
|
|
||||||
|
ws1.set_option(stream_base::timeout{
|
||||||
|
std::chrono::milliseconds(50),
|
||||||
|
stream_base::none(),
|
||||||
|
false});
|
||||||
|
ws1.async_accept(test::fail_handler(beast::error::timeout));
|
||||||
|
test::run_for(ioc, std::chrono::seconds(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
stream<test::stream> ws1(ioc);
|
||||||
|
stream<test::stream> ws2(ioc);
|
||||||
|
test::connect(ws1.next_layer(), ws2.next_layer());
|
||||||
|
|
||||||
|
ws1.set_option(stream_base::timeout{
|
||||||
|
std::chrono::milliseconds(50),
|
||||||
|
stream_base::none(),
|
||||||
|
false});
|
||||||
|
ws1.async_accept(test::fail_handler(beast::error::timeout));
|
||||||
|
test::run_for(ioc, std::chrono::seconds(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
testMoveOnly()
|
testMoveOnly()
|
||||||
{
|
{
|
||||||
@@ -649,6 +738,7 @@ public:
|
|||||||
run() override
|
run() override
|
||||||
{
|
{
|
||||||
testAccept();
|
testAccept();
|
||||||
|
testTimeout();
|
||||||
testMoveOnly();
|
testMoveOnly();
|
||||||
testAsioHandlerInvoke();
|
testAsioHandlerInvoke();
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// Copyright (w) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
//
|
//
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
// 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)
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
@@ -10,6 +10,8 @@
|
|||||||
// Test that header file is self-contained.
|
// Test that header file is self-contained.
|
||||||
#include <boost/beast/websocket/stream.hpp>
|
#include <boost/beast/websocket/stream.hpp>
|
||||||
|
|
||||||
|
#include <boost/beast/_experimental/test/stream.hpp>
|
||||||
|
#include <boost/beast/_experimental/test/tcp.hpp>
|
||||||
#include "test.hpp"
|
#include "test.hpp"
|
||||||
|
|
||||||
#include <boost/asio/io_context.hpp>
|
#include <boost/asio/io_context.hpp>
|
||||||
@@ -168,6 +170,114 @@ public:
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testTimeout()
|
||||||
|
{
|
||||||
|
using tcp = net::ip::tcp;
|
||||||
|
|
||||||
|
net::io_context ioc;
|
||||||
|
|
||||||
|
// success
|
||||||
|
|
||||||
|
{
|
||||||
|
stream<tcp::socket> ws1(ioc);
|
||||||
|
stream<tcp::socket> ws2(ioc);
|
||||||
|
test::connect(ws1.next_layer(), ws2.next_layer());
|
||||||
|
ws1.async_handshake("test", "/", test::success_handler());
|
||||||
|
ws2.async_accept(test::success_handler());
|
||||||
|
test::run(ioc);
|
||||||
|
|
||||||
|
ws1.async_close({}, test::success_handler());
|
||||||
|
ws2.async_close({}, test::success_handler());
|
||||||
|
test::run(ioc);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
stream<test::stream> ws1(ioc);
|
||||||
|
stream<test::stream> ws2(ioc);
|
||||||
|
test::connect(ws1.next_layer(), ws2.next_layer());
|
||||||
|
ws1.async_handshake("test", "/", test::success_handler());
|
||||||
|
ws2.async_accept(test::success_handler());
|
||||||
|
test::run(ioc);
|
||||||
|
|
||||||
|
ws1.async_close({}, test::success_handler());
|
||||||
|
ws2.async_close({}, test::success_handler());
|
||||||
|
test::run(ioc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// success, timeout enabled
|
||||||
|
|
||||||
|
{
|
||||||
|
stream<tcp::socket> ws1(ioc);
|
||||||
|
stream<tcp::socket> ws2(ioc);
|
||||||
|
test::connect(ws1.next_layer(), ws2.next_layer());
|
||||||
|
ws1.async_handshake("test", "/", test::success_handler());
|
||||||
|
ws2.async_accept(test::success_handler());
|
||||||
|
test::run(ioc);
|
||||||
|
|
||||||
|
ws1.set_option(stream_base::timeout{
|
||||||
|
std::chrono::milliseconds(50),
|
||||||
|
stream_base::none(),
|
||||||
|
false});
|
||||||
|
ws1.async_close({}, test::success_handler());
|
||||||
|
ws2.async_close({}, test::success_handler());
|
||||||
|
test::run(ioc);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
stream<test::stream> ws1(ioc);
|
||||||
|
stream<test::stream> ws2(ioc);
|
||||||
|
test::connect(ws1.next_layer(), ws2.next_layer());
|
||||||
|
ws1.async_handshake("test", "/", test::success_handler());
|
||||||
|
ws2.async_accept(test::success_handler());
|
||||||
|
test::run(ioc);
|
||||||
|
|
||||||
|
ws1.set_option(stream_base::timeout{
|
||||||
|
std::chrono::milliseconds(50),
|
||||||
|
stream_base::none(),
|
||||||
|
false});
|
||||||
|
ws1.async_close({}, test::success_handler());
|
||||||
|
ws2.async_close({}, test::success_handler());
|
||||||
|
test::run(ioc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// timeout
|
||||||
|
|
||||||
|
{
|
||||||
|
stream<tcp::socket> ws1(ioc);
|
||||||
|
stream<tcp::socket> ws2(ioc);
|
||||||
|
test::connect(ws1.next_layer(), ws2.next_layer());
|
||||||
|
ws1.async_handshake("test", "/", test::success_handler());
|
||||||
|
ws2.async_accept(test::success_handler());
|
||||||
|
test::run(ioc);
|
||||||
|
|
||||||
|
ws1.set_option(stream_base::timeout{
|
||||||
|
std::chrono::milliseconds(50),
|
||||||
|
stream_base::none(),
|
||||||
|
false});
|
||||||
|
ws1.async_close({}, test::fail_handler(
|
||||||
|
beast::error::timeout));
|
||||||
|
test::run(ioc);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
stream<test::stream> ws1(ioc);
|
||||||
|
stream<test::stream> ws2(ioc);
|
||||||
|
test::connect(ws1.next_layer(), ws2.next_layer());
|
||||||
|
ws1.async_handshake("test", "/", test::success_handler());
|
||||||
|
ws2.async_accept(test::success_handler());
|
||||||
|
test::run(ioc);
|
||||||
|
|
||||||
|
ws1.set_option(stream_base::timeout{
|
||||||
|
std::chrono::milliseconds(50),
|
||||||
|
stream_base::none(),
|
||||||
|
false});
|
||||||
|
ws1.async_close({}, test::fail_handler(
|
||||||
|
beast::error::timeout));
|
||||||
|
test::run(ioc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
testSuspend()
|
testSuspend()
|
||||||
{
|
{
|
||||||
@@ -625,28 +735,13 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void
|
|
||||||
testAsioHandlerInvoke()
|
|
||||||
{
|
|
||||||
// make sure things compile, also can set a
|
|
||||||
// breakpoint in asio_handler_invoke to make sure
|
|
||||||
// it is instantiated.
|
|
||||||
net::io_context ioc;
|
|
||||||
net::strand<
|
|
||||||
net::io_context::executor_type> s(
|
|
||||||
ioc.get_executor());
|
|
||||||
stream<test::stream> ws{ioc};
|
|
||||||
ws.async_close({}, net::bind_executor(
|
|
||||||
s, copyable_handler{}));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
run() override
|
run() override
|
||||||
{
|
{
|
||||||
testClose();
|
testClose();
|
||||||
|
testTimeout();
|
||||||
testSuspend();
|
testSuspend();
|
||||||
testMoveOnly();
|
testMoveOnly();
|
||||||
testAsioHandlerInvoke();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// Copyright (w) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
//
|
//
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
// 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)
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
@@ -10,6 +10,9 @@
|
|||||||
// Test that header file is self-contained.
|
// Test that header file is self-contained.
|
||||||
#include <boost/beast/websocket/stream.hpp>
|
#include <boost/beast/websocket/stream.hpp>
|
||||||
|
|
||||||
|
#include <boost/beast/_experimental/test/stream.hpp>
|
||||||
|
#include <boost/beast/_experimental/test/tcp.hpp>
|
||||||
|
|
||||||
#include "test.hpp"
|
#include "test.hpp"
|
||||||
|
|
||||||
#include <boost/asio/io_context.hpp>
|
#include <boost/asio/io_context.hpp>
|
||||||
@@ -235,6 +238,96 @@ public:
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testTimeout()
|
||||||
|
{
|
||||||
|
using tcp = net::ip::tcp;
|
||||||
|
|
||||||
|
net::io_context ioc;
|
||||||
|
|
||||||
|
// success
|
||||||
|
|
||||||
|
{
|
||||||
|
stream<tcp::socket> ws1(ioc);
|
||||||
|
stream<tcp::socket> ws2(ioc);
|
||||||
|
test::connect(ws1.next_layer(), ws2.next_layer());
|
||||||
|
|
||||||
|
ws1.async_handshake("test", "/", test::success_handler());
|
||||||
|
ws2.async_accept(test::success_handler());
|
||||||
|
test::run_for(ioc, std::chrono::seconds(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
stream<test::stream> ws1(ioc);
|
||||||
|
stream<test::stream> ws2(ioc);
|
||||||
|
test::connect(ws1.next_layer(), ws2.next_layer());
|
||||||
|
|
||||||
|
ws1.async_handshake("test", "/", test::success_handler());
|
||||||
|
ws2.async_accept(test::success_handler());
|
||||||
|
test::run_for(ioc, std::chrono::seconds(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// success, timeout enabled
|
||||||
|
|
||||||
|
{
|
||||||
|
stream<tcp::socket> ws1(ioc);
|
||||||
|
stream<tcp::socket> ws2(ioc);
|
||||||
|
test::connect(ws1.next_layer(), ws2.next_layer());
|
||||||
|
|
||||||
|
ws1.set_option(stream_base::timeout{
|
||||||
|
std::chrono::milliseconds(50),
|
||||||
|
stream_base::none(),
|
||||||
|
false});
|
||||||
|
ws1.async_handshake("test", "/", test::success_handler());
|
||||||
|
ws2.async_accept(test::success_handler());
|
||||||
|
test::run_for(ioc, std::chrono::seconds(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
stream<test::stream> ws1(ioc);
|
||||||
|
stream<test::stream> ws2(ioc);
|
||||||
|
test::connect(ws1.next_layer(), ws2.next_layer());
|
||||||
|
|
||||||
|
ws1.set_option(stream_base::timeout{
|
||||||
|
std::chrono::milliseconds(50),
|
||||||
|
stream_base::none(),
|
||||||
|
false});
|
||||||
|
ws1.async_handshake("test", "/", test::success_handler());
|
||||||
|
ws2.async_accept(test::success_handler());
|
||||||
|
test::run_for(ioc, std::chrono::seconds(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// timeout
|
||||||
|
|
||||||
|
{
|
||||||
|
stream<tcp::socket> ws1(ioc);
|
||||||
|
stream<tcp::socket> ws2(ioc);
|
||||||
|
test::connect(ws1.next_layer(), ws2.next_layer());
|
||||||
|
|
||||||
|
ws1.set_option(stream_base::timeout{
|
||||||
|
std::chrono::milliseconds(50),
|
||||||
|
stream_base::none(),
|
||||||
|
false});
|
||||||
|
ws1.async_handshake("test", "/",
|
||||||
|
test::fail_handler(beast::error::timeout));
|
||||||
|
test::run_for(ioc, std::chrono::seconds(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
stream<test::stream> ws1(ioc);
|
||||||
|
stream<test::stream> ws2(ioc);
|
||||||
|
test::connect(ws1.next_layer(), ws2.next_layer());
|
||||||
|
|
||||||
|
ws1.set_option(stream_base::timeout{
|
||||||
|
std::chrono::milliseconds(50),
|
||||||
|
stream_base::none(),
|
||||||
|
false});
|
||||||
|
ws1.async_handshake("test", "/",
|
||||||
|
test::fail_handler(beast::error::timeout));
|
||||||
|
test::run_for(ioc, std::chrono::seconds(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Compression Extensions for WebSocket
|
// Compression Extensions for WebSocket
|
||||||
//
|
//
|
||||||
// https://tools.ietf.org/html/rfc7692
|
// https://tools.ietf.org/html/rfc7692
|
||||||
@@ -504,31 +597,15 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void
|
|
||||||
testAsioHandlerInvoke()
|
|
||||||
{
|
|
||||||
// make sure things compile, also can set a
|
|
||||||
// breakpoint in asio_handler_invoke to make sure
|
|
||||||
// it is instantiated.
|
|
||||||
net::io_context ioc;
|
|
||||||
net::strand<
|
|
||||||
net::io_context::executor_type> s(
|
|
||||||
ioc.get_executor());
|
|
||||||
stream<test::stream> ws{ioc};
|
|
||||||
ws.async_handshake("localhost", "/",
|
|
||||||
net::bind_executor(
|
|
||||||
s, copyable_handler{}));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
run() override
|
run() override
|
||||||
{
|
{
|
||||||
testHandshake();
|
testHandshake();
|
||||||
|
testTimeout();
|
||||||
testExtRead();
|
testExtRead();
|
||||||
testExtWrite();
|
testExtWrite();
|
||||||
testExtNegotiate();
|
testExtNegotiate();
|
||||||
testMoveOnly();
|
testMoveOnly();
|
||||||
testAsioHandlerInvoke();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// Copyright (w) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
//
|
//
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
// 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)
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
@@ -440,28 +440,12 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void
|
|
||||||
testAsioHandlerInvoke()
|
|
||||||
{
|
|
||||||
// make sure things compile, also can set a
|
|
||||||
// breakpoint in asio_handler_invoke to make sure
|
|
||||||
// it is instantiated.
|
|
||||||
net::io_context ioc;
|
|
||||||
net::strand<
|
|
||||||
net::io_context::executor_type> s(
|
|
||||||
ioc.get_executor());
|
|
||||||
stream<test::stream> ws{ioc};
|
|
||||||
ws.async_ping({}, net::bind_executor(
|
|
||||||
s, copyable_handler{}));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
run() override
|
run() override
|
||||||
{
|
{
|
||||||
testPing();
|
testPing();
|
||||||
testSuspend();
|
testSuspend();
|
||||||
testMoveOnly();
|
testMoveOnly();
|
||||||
testAsioHandlerInvoke();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
|
//
|
||||||
// Copyright (w) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
//
|
//
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
// 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)
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
@@ -10,672 +10,139 @@
|
|||||||
// Test that header file is self-contained.
|
// Test that header file is self-contained.
|
||||||
#include <boost/beast/websocket/stream.hpp>
|
#include <boost/beast/websocket/stream.hpp>
|
||||||
|
|
||||||
#include "test.hpp"
|
#include <boost/beast/_experimental/test/stream.hpp>
|
||||||
|
#include <boost/beast/_experimental/test/tcp.hpp>
|
||||||
#include <boost/asio/write.hpp>
|
#include <boost/beast/core/flat_buffer.hpp>
|
||||||
|
|
||||||
#include <boost/config/workaround.hpp>
|
|
||||||
#if BOOST_WORKAROUND(BOOST_GCC, < 80200)
|
|
||||||
#define BOOST_BEAST_SYMBOL_HIDDEN __attribute__ ((visibility("hidden")))
|
|
||||||
#else
|
|
||||||
#define BOOST_BEAST_SYMBOL_HIDDEN
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace boost {
|
namespace boost {
|
||||||
namespace beast {
|
namespace beast {
|
||||||
namespace websocket {
|
namespace websocket {
|
||||||
|
|
||||||
class BOOST_BEAST_SYMBOL_HIDDEN read1_test
|
class read1_test : public unit_test::suite
|
||||||
: public websocket_test_suite
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
template<class Wrap, bool deflateSupported>
|
|
||||||
void
|
void
|
||||||
doReadTest(
|
testTimeout()
|
||||||
Wrap const& w,
|
|
||||||
ws_type_t<deflateSupported>& ws,
|
|
||||||
close_code code)
|
|
||||||
{
|
{
|
||||||
try
|
using tcp = net::ip::tcp;
|
||||||
|
|
||||||
|
net::io_context ioc;
|
||||||
|
|
||||||
|
// success
|
||||||
|
|
||||||
{
|
{
|
||||||
multi_buffer b;
|
stream<tcp::socket> ws1(ioc);
|
||||||
w.read(ws, b);
|
stream<tcp::socket> ws2(ioc);
|
||||||
fail("", __FILE__, __LINE__);
|
test::connect(ws1.next_layer(), ws2.next_layer());
|
||||||
}
|
ws1.async_handshake("test", "/", test::success_handler());
|
||||||
catch(system_error const& se)
|
ws2.async_accept(test::success_handler());
|
||||||
{
|
test::run(ioc);
|
||||||
if(se.code() != error::closed)
|
|
||||||
throw;
|
|
||||||
BEAST_EXPECT(
|
|
||||||
ws.reason().code == code);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class Wrap, bool deflateSupported>
|
|
||||||
void
|
|
||||||
doFailTest(
|
|
||||||
Wrap const& w,
|
|
||||||
ws_type_t<deflateSupported>& ws,
|
|
||||||
error_code ev)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
multi_buffer b;
|
|
||||||
w.read(ws, b);
|
|
||||||
fail("", __FILE__, __LINE__);
|
|
||||||
}
|
|
||||||
catch(system_error const& se)
|
|
||||||
{
|
|
||||||
if(se.code() != ev)
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<bool deflateSupported = true, class Wrap>
|
|
||||||
void
|
|
||||||
doTestRead(Wrap const& w)
|
|
||||||
{
|
|
||||||
permessage_deflate pmd;
|
|
||||||
pmd.client_enable = false;
|
|
||||||
pmd.server_enable = false;
|
|
||||||
|
|
||||||
// already closed
|
|
||||||
{
|
|
||||||
echo_server es{log};
|
|
||||||
stream<test::stream, deflateSupported> ws{ioc_};
|
|
||||||
ws.next_layer().connect(es.stream());
|
|
||||||
ws.handshake("localhost", "/");
|
|
||||||
ws.close({});
|
|
||||||
try
|
|
||||||
{
|
|
||||||
multi_buffer b;
|
|
||||||
w.read(ws, b);
|
|
||||||
fail("", __FILE__, __LINE__);
|
|
||||||
}
|
|
||||||
catch(system_error const& se)
|
|
||||||
{
|
|
||||||
BEAST_EXPECTS(
|
|
||||||
se.code() == net::error::operation_aborted,
|
|
||||||
se.code().message());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// empty, fragmented message
|
|
||||||
doTest<deflateSupported>(pmd,
|
|
||||||
[&](ws_type_t<deflateSupported>& ws)
|
|
||||||
{
|
|
||||||
ws.next_layer().append(
|
|
||||||
string_view(
|
|
||||||
"\x01\x00" "\x80\x00", 4));
|
|
||||||
multi_buffer b;
|
|
||||||
w.read(ws, b);
|
|
||||||
BEAST_EXPECT(b.size() == 0);
|
|
||||||
});
|
|
||||||
|
|
||||||
// two part message
|
|
||||||
// triggers "fill the read buffer first"
|
|
||||||
doTest<deflateSupported>(pmd,
|
|
||||||
[&](ws_type_t<deflateSupported>& ws)
|
|
||||||
{
|
|
||||||
w.write_raw(ws, sbuf(
|
|
||||||
"\x01\x81\xff\xff\xff\xff"));
|
|
||||||
w.write_raw(ws, sbuf(
|
|
||||||
"\xd5"));
|
|
||||||
w.write_raw(ws, sbuf(
|
|
||||||
"\x80\x81\xff\xff\xff\xff\xd5"));
|
|
||||||
multi_buffer b;
|
|
||||||
w.read(ws, b);
|
|
||||||
BEAST_EXPECT(buffers_to_string(b.data()) == "**");
|
|
||||||
});
|
|
||||||
|
|
||||||
// ping
|
|
||||||
doTest<deflateSupported>(pmd,
|
|
||||||
[&](ws_type_t<deflateSupported>& ws)
|
|
||||||
{
|
|
||||||
put(ws.next_layer().buffer(), cbuf(
|
|
||||||
0x89, 0x00));
|
|
||||||
bool invoked = false;
|
|
||||||
ws.control_callback(
|
|
||||||
[&](frame_type kind, string_view)
|
|
||||||
{
|
|
||||||
BEAST_EXPECT(! invoked);
|
|
||||||
BEAST_EXPECT(kind == frame_type::ping);
|
|
||||||
invoked = true;
|
|
||||||
});
|
|
||||||
w.write(ws, sbuf("Hello"));
|
|
||||||
multi_buffer b;
|
|
||||||
w.read(ws, b);
|
|
||||||
BEAST_EXPECT(invoked);
|
|
||||||
BEAST_EXPECT(ws.got_text());
|
|
||||||
BEAST_EXPECT(buffers_to_string(b.data()) == "Hello");
|
|
||||||
});
|
|
||||||
|
|
||||||
// ping
|
|
||||||
doTest<deflateSupported>(pmd,
|
|
||||||
[&](ws_type_t<deflateSupported>& ws)
|
|
||||||
{
|
|
||||||
put(ws.next_layer().buffer(), cbuf(
|
|
||||||
0x88, 0x00));
|
|
||||||
bool invoked = false;
|
|
||||||
ws.control_callback(
|
|
||||||
[&](frame_type kind, string_view)
|
|
||||||
{
|
|
||||||
BEAST_EXPECT(! invoked);
|
|
||||||
BEAST_EXPECT(kind == frame_type::close);
|
|
||||||
invoked = true;
|
|
||||||
});
|
|
||||||
w.write(ws, sbuf("Hello"));
|
|
||||||
doReadTest(w, ws, close_code::none);
|
|
||||||
});
|
|
||||||
|
|
||||||
// ping then message
|
|
||||||
doTest<deflateSupported>(pmd,
|
|
||||||
[&](ws_type_t<deflateSupported>& ws)
|
|
||||||
{
|
|
||||||
bool once = false;
|
|
||||||
ws.control_callback(
|
|
||||||
[&](frame_type kind, string_view s)
|
|
||||||
{
|
|
||||||
BEAST_EXPECT(kind == frame_type::pong);
|
|
||||||
BEAST_EXPECT(! once);
|
|
||||||
once = true;
|
|
||||||
BEAST_EXPECT(s == "");
|
|
||||||
});
|
|
||||||
w.ping(ws, "");
|
|
||||||
ws.binary(true);
|
|
||||||
w.write(ws, sbuf("Hello"));
|
|
||||||
multi_buffer b;
|
|
||||||
w.read(ws, b);
|
|
||||||
BEAST_EXPECT(once);
|
|
||||||
BEAST_EXPECT(ws.got_binary());
|
|
||||||
BEAST_EXPECT(buffers_to_string(b.data()) == "Hello");
|
|
||||||
});
|
|
||||||
|
|
||||||
// ping then fragmented message
|
|
||||||
doTest<deflateSupported>(pmd,
|
|
||||||
[&](ws_type_t<deflateSupported>& ws)
|
|
||||||
{
|
|
||||||
bool once = false;
|
|
||||||
ws.control_callback(
|
|
||||||
[&](frame_type kind, string_view s)
|
|
||||||
{
|
|
||||||
BEAST_EXPECT(kind == frame_type::pong);
|
|
||||||
BEAST_EXPECT(! once);
|
|
||||||
once = true;
|
|
||||||
BEAST_EXPECT(s == "payload");
|
|
||||||
});
|
|
||||||
ws.ping("payload");
|
|
||||||
w.write_some(ws, false, sbuf("Hello, "));
|
|
||||||
w.write_some(ws, false, sbuf(""));
|
|
||||||
w.write_some(ws, true, sbuf("World!"));
|
|
||||||
multi_buffer b;
|
|
||||||
w.read(ws, b);
|
|
||||||
BEAST_EXPECT(once);
|
|
||||||
BEAST_EXPECT(buffers_to_string(b.data()) == "Hello, World!");
|
|
||||||
});
|
|
||||||
|
|
||||||
// masked message, big
|
|
||||||
doStreamLoop([&](test::stream& ts)
|
|
||||||
{
|
|
||||||
echo_server es{log, kind::async_client};
|
|
||||||
ws_type_t<deflateSupported> ws{ts};
|
|
||||||
ws.next_layer().connect(es.stream());
|
|
||||||
ws.set_option(pmd);
|
|
||||||
es.async_handshake();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
w.accept(ws);
|
|
||||||
std::string const s(2000, '*');
|
|
||||||
ws.auto_fragment(false);
|
|
||||||
ws.binary(false);
|
|
||||||
w.write(ws, net::buffer(s));
|
|
||||||
multi_buffer b;
|
|
||||||
w.read(ws, b);
|
|
||||||
BEAST_EXPECT(ws.got_text());
|
|
||||||
BEAST_EXPECT(buffers_to_string(b.data()) == s);
|
|
||||||
ws.next_layer().close();
|
|
||||||
}
|
|
||||||
catch(...)
|
|
||||||
{
|
|
||||||
ts.close();
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// close
|
|
||||||
doFailLoop([&](test::fail_count& fc)
|
|
||||||
{
|
|
||||||
echo_server es{log, kind::async};
|
|
||||||
net::io_context ioc;
|
|
||||||
stream<test::stream, deflateSupported> ws{ioc, fc};
|
|
||||||
ws.next_layer().connect(es.stream());
|
|
||||||
ws.handshake("localhost", "/");
|
|
||||||
// Cause close to be received
|
|
||||||
es.async_close();
|
|
||||||
std::size_t count = 0;
|
|
||||||
multi_buffer b;
|
|
||||||
ws.async_read(b,
|
|
||||||
[&](error_code ec, std::size_t)
|
|
||||||
{
|
|
||||||
++count;
|
|
||||||
if(ec != error::closed)
|
|
||||||
BOOST_THROW_EXCEPTION(
|
|
||||||
system_error{ec});
|
|
||||||
});
|
|
||||||
ioc.run();
|
|
||||||
BEAST_EXPECT(count == 1);
|
|
||||||
});
|
|
||||||
|
|
||||||
// already closed
|
|
||||||
doTest<deflateSupported>(pmd,
|
|
||||||
[&](ws_type_t<deflateSupported>& ws)
|
|
||||||
{
|
|
||||||
w.close(ws, {});
|
|
||||||
multi_buffer b;
|
|
||||||
doFailTest(w, ws,
|
|
||||||
net::error::operation_aborted);
|
|
||||||
});
|
|
||||||
|
|
||||||
// buffer overflow
|
|
||||||
doTest<deflateSupported>(pmd,
|
|
||||||
[&](ws_type_t<deflateSupported>& ws)
|
|
||||||
{
|
|
||||||
std::string const s = "Hello, world!";
|
|
||||||
ws.auto_fragment(false);
|
|
||||||
ws.binary(false);
|
|
||||||
w.write(ws, net::buffer(s));
|
|
||||||
try
|
|
||||||
{
|
|
||||||
multi_buffer b(3);
|
|
||||||
w.read(ws, b);
|
|
||||||
fail("", __FILE__, __LINE__);
|
|
||||||
}
|
|
||||||
catch(system_error const& se)
|
|
||||||
{
|
|
||||||
if(se.code() != error::buffer_overflow)
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// bad utf8, big
|
|
||||||
doTest<deflateSupported>(pmd,
|
|
||||||
[&](ws_type_t<deflateSupported>& ws)
|
|
||||||
{
|
|
||||||
auto const s = std::string(2000, '*') +
|
|
||||||
random_string();
|
|
||||||
ws.text(true);
|
|
||||||
w.write(ws, net::buffer(s));
|
|
||||||
doReadTest(w, ws, close_code::bad_payload);
|
|
||||||
});
|
|
||||||
|
|
||||||
// invalid fixed frame header
|
|
||||||
doTest<deflateSupported>(pmd,
|
|
||||||
[&](ws_type_t<deflateSupported>& ws)
|
|
||||||
{
|
|
||||||
w.write_raw(ws, cbuf(
|
|
||||||
0x8f, 0x80, 0xff, 0xff, 0xff, 0xff));
|
|
||||||
doReadTest(w, ws, close_code::protocol_error);
|
|
||||||
});
|
|
||||||
|
|
||||||
// bad close
|
|
||||||
doTest<deflateSupported>(pmd,
|
|
||||||
[&](ws_type_t<deflateSupported>& ws)
|
|
||||||
{
|
|
||||||
put(ws.next_layer().buffer(), cbuf(
|
|
||||||
0x88, 0x02, 0x03, 0xed));
|
|
||||||
doFailTest(w, ws, error::bad_close_code);
|
|
||||||
});
|
|
||||||
|
|
||||||
// message size above 2^64
|
|
||||||
doTest<deflateSupported>(pmd,
|
|
||||||
[&](ws_type_t<deflateSupported>& ws)
|
|
||||||
{
|
|
||||||
w.write_some(ws, false, sbuf("*"));
|
|
||||||
w.write_raw(ws, cbuf(
|
|
||||||
0x80, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
||||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff));
|
|
||||||
doReadTest(w, ws, close_code::too_big);
|
|
||||||
});
|
|
||||||
|
|
||||||
// message size exceeds max
|
|
||||||
doTest<deflateSupported>(pmd,
|
|
||||||
[&](ws_type_t<deflateSupported>& ws)
|
|
||||||
{
|
|
||||||
ws.read_message_max(1);
|
|
||||||
w.write(ws, sbuf("**"));
|
|
||||||
doFailTest(w, ws, error::message_too_big);
|
|
||||||
});
|
|
||||||
|
|
||||||
// bad utf8
|
|
||||||
doTest<deflateSupported>(pmd,
|
|
||||||
[&](ws_type_t<deflateSupported>& ws)
|
|
||||||
{
|
|
||||||
put(ws.next_layer().buffer(), cbuf(
|
|
||||||
0x81, 0x06, 0x03, 0xea, 0xf0, 0x28, 0x8c, 0xbc));
|
|
||||||
doFailTest(w, ws, error::bad_frame_payload);
|
|
||||||
});
|
|
||||||
|
|
||||||
// incomplete utf8
|
|
||||||
doTest<deflateSupported>(pmd,
|
|
||||||
[&](ws_type_t<deflateSupported>& ws)
|
|
||||||
{
|
|
||||||
std::string const s =
|
|
||||||
"Hello, world!" "\xc0";
|
|
||||||
w.write(ws, net::buffer(s));
|
|
||||||
doReadTest(w, ws, close_code::bad_payload);
|
|
||||||
});
|
|
||||||
|
|
||||||
// incomplete utf8, big
|
|
||||||
doTest<deflateSupported>(pmd,
|
|
||||||
[&](ws_type_t<deflateSupported>& ws)
|
|
||||||
{
|
|
||||||
std::string const s =
|
|
||||||
"\x81\x7e\x0f\xa1" +
|
|
||||||
std::string(4000, '*') + "\xc0";
|
|
||||||
ws.next_layer().append(s);
|
|
||||||
multi_buffer b;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
do
|
|
||||||
{
|
|
||||||
b.commit(w.read_some(ws, b.prepare(4000)));
|
|
||||||
}
|
|
||||||
while(! ws.is_message_done());
|
|
||||||
}
|
|
||||||
catch(system_error const& se)
|
|
||||||
{
|
|
||||||
if(se.code() != error::bad_frame_payload)
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// close frames
|
|
||||||
{
|
|
||||||
auto const check =
|
|
||||||
[&](error_code ev, string_view s)
|
|
||||||
{
|
|
||||||
echo_server es{log};
|
|
||||||
stream<test::stream, deflateSupported> ws{ioc_};
|
|
||||||
ws.next_layer().connect(es.stream());
|
|
||||||
w.handshake(ws, "localhost", "/");
|
|
||||||
ws.next_layer().append(s);
|
|
||||||
static_buffer<1> b;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
w.read(ws, b);
|
|
||||||
fail("", __FILE__, __LINE__);
|
|
||||||
}
|
|
||||||
catch(system_error const& se)
|
|
||||||
{
|
|
||||||
BEAST_EXPECTS(se.code() == ev,
|
|
||||||
se.code().message());
|
|
||||||
}
|
|
||||||
ws.next_layer().close();
|
|
||||||
};
|
|
||||||
|
|
||||||
// payload length 1
|
|
||||||
check(error::bad_close_size,
|
|
||||||
"\x88\x01\x01");
|
|
||||||
|
|
||||||
// invalid close code 1005
|
|
||||||
check(error::bad_close_code,
|
|
||||||
"\x88\x02\x03\xed");
|
|
||||||
|
|
||||||
// invalid utf8
|
|
||||||
check(error::bad_close_payload,
|
|
||||||
"\x88\x06\xfc\x15\x0f\xd7\x73\x43");
|
|
||||||
|
|
||||||
// good utf8
|
|
||||||
check(error::closed,
|
|
||||||
"\x88\x06\xfc\x15utf8");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class Wrap>
|
|
||||||
void
|
|
||||||
doTestReadDeflate(Wrap const& w)
|
|
||||||
{
|
|
||||||
permessage_deflate pmd;
|
|
||||||
pmd.client_enable = true;
|
|
||||||
pmd.server_enable = true;
|
|
||||||
pmd.client_max_window_bits = 9;
|
|
||||||
pmd.server_max_window_bits = 9;
|
|
||||||
pmd.compLevel = 1;
|
|
||||||
|
|
||||||
// message size limit
|
|
||||||
doTest<true>(pmd,
|
|
||||||
[&](ws_type_t<true>& ws)
|
|
||||||
{
|
|
||||||
std::string const s = std::string(128, '*');
|
|
||||||
w.write(ws, net::buffer(s));
|
|
||||||
ws.read_message_max(32);
|
|
||||||
doFailTest(w, ws, error::message_too_big);
|
|
||||||
});
|
|
||||||
|
|
||||||
// invalid inflate block
|
|
||||||
doTest<true>(pmd,
|
|
||||||
[&](ws_type_t<true>& ws)
|
|
||||||
{
|
|
||||||
auto const& s = random_string();
|
|
||||||
ws.binary(true);
|
|
||||||
ws.next_layer().append(
|
|
||||||
"\xc2\x40" + s.substr(0, 64));
|
|
||||||
flat_buffer b;
|
flat_buffer b;
|
||||||
try
|
ws1.async_write(net::const_buffer("Hello, world!", 13),
|
||||||
{
|
test::success_handler());
|
||||||
w.read(ws, b);
|
ws2.async_read(b, test::success_handler());
|
||||||
}
|
test::run(ioc);
|
||||||
catch(system_error const& se)
|
}
|
||||||
{
|
|
||||||
if(se.code() == test::error::test_failure)
|
|
||||||
throw;
|
|
||||||
BEAST_EXPECTS(se.code().category() ==
|
|
||||||
zlib::detail::get_error_category(),
|
|
||||||
se.code().message());
|
|
||||||
}
|
|
||||||
catch(...)
|
|
||||||
{
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// no_context_takeover
|
|
||||||
pmd.server_no_context_takeover = true;
|
|
||||||
doTest<true>(pmd,
|
|
||||||
[&](ws_type_t<true>& ws)
|
|
||||||
{
|
{
|
||||||
auto const& s = random_string();
|
stream<test::stream> ws1(ioc);
|
||||||
ws.binary(true);
|
stream<test::stream> ws2(ioc);
|
||||||
w.write(ws, net::buffer(s));
|
test::connect(ws1.next_layer(), ws2.next_layer());
|
||||||
multi_buffer b;
|
ws1.async_handshake("test", "/", test::success_handler());
|
||||||
w.read(ws, b);
|
ws2.async_accept(test::success_handler());
|
||||||
BEAST_EXPECT(buffers_to_string(b.data()) == s);
|
test::run(ioc);
|
||||||
});
|
|
||||||
pmd.client_no_context_takeover = false;
|
flat_buffer b;
|
||||||
}
|
ws1.async_write(net::const_buffer("Hello, world!", 13),
|
||||||
|
test::success_handler());
|
||||||
|
ws2.async_read(b, test::success_handler());
|
||||||
|
test::run(ioc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// success, timeout enabled
|
||||||
|
|
||||||
template<class Wrap>
|
|
||||||
void
|
|
||||||
doTestRead(
|
|
||||||
permessage_deflate const& pmd,
|
|
||||||
Wrap const& w)
|
|
||||||
{
|
|
||||||
// message
|
|
||||||
doTest(pmd, [&](ws_type& ws)
|
|
||||||
{
|
{
|
||||||
std::string const s = "Hello, world!";
|
stream<tcp::socket> ws1(ioc);
|
||||||
ws.auto_fragment(false);
|
stream<tcp::socket> ws2(ioc);
|
||||||
ws.binary(false);
|
test::connect(ws1.next_layer(), ws2.next_layer());
|
||||||
w.write(ws, net::buffer(s));
|
ws1.async_handshake("test", "/", test::success_handler());
|
||||||
multi_buffer b;
|
ws2.async_accept(test::success_handler());
|
||||||
w.read(ws, b);
|
test::run(ioc);
|
||||||
BEAST_EXPECT(ws.got_text());
|
|
||||||
BEAST_EXPECT(buffers_to_string(b.data()) == s);
|
flat_buffer b;
|
||||||
});
|
ws1.set_option(stream_base::timeout{
|
||||||
|
stream_base::none(),
|
||||||
|
std::chrono::milliseconds(50),
|
||||||
|
false});
|
||||||
|
ws1.async_read(b, test::success_handler());
|
||||||
|
ws2.async_write(net::const_buffer("Hello, world!", 13),
|
||||||
|
test::success_handler());
|
||||||
|
test::run(ioc);
|
||||||
|
}
|
||||||
|
|
||||||
// masked message
|
|
||||||
doStreamLoop([&](test::stream& ts)
|
|
||||||
{
|
{
|
||||||
echo_server es{log, kind::async_client};
|
stream<test::stream> ws1(ioc);
|
||||||
ws_type ws{ts};
|
stream<test::stream> ws2(ioc);
|
||||||
ws.next_layer().connect(es.stream());
|
test::connect(ws1.next_layer(), ws2.next_layer());
|
||||||
ws.set_option(pmd);
|
ws1.async_handshake("test", "/", test::success_handler());
|
||||||
es.async_handshake();
|
ws2.async_accept(test::success_handler());
|
||||||
try
|
test::run(ioc);
|
||||||
{
|
|
||||||
w.accept(ws);
|
flat_buffer b;
|
||||||
std::string const s = "Hello, world!";
|
ws1.set_option(stream_base::timeout{
|
||||||
ws.auto_fragment(false);
|
stream_base::none(),
|
||||||
ws.binary(false);
|
std::chrono::milliseconds(50),
|
||||||
w.write(ws, net::buffer(s));
|
false});
|
||||||
multi_buffer b;
|
ws1.async_read(b, test::success_handler());
|
||||||
w.read(ws, b);
|
ws2.async_write(net::const_buffer("Hello, world!", 13),
|
||||||
BEAST_EXPECT(ws.got_text());
|
test::success_handler());
|
||||||
BEAST_EXPECT(buffers_to_string(b.data()) == s);
|
test::run(ioc);
|
||||||
ws.next_layer().close();
|
}
|
||||||
}
|
|
||||||
catch(...)
|
// timeout
|
||||||
{
|
|
||||||
ts.close();
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// empty message
|
|
||||||
doTest(pmd, [&](ws_type& ws)
|
|
||||||
{
|
{
|
||||||
std::string const s = "";
|
stream<tcp::socket> ws1(ioc);
|
||||||
ws.text(true);
|
stream<tcp::socket> ws2(ioc);
|
||||||
w.write(ws, net::buffer(s));
|
test::connect(ws1.next_layer(), ws2.next_layer());
|
||||||
multi_buffer b;
|
ws1.async_handshake("test", "/", test::success_handler());
|
||||||
w.read(ws, b);
|
ws2.async_accept(test::success_handler());
|
||||||
BEAST_EXPECT(ws.got_text());
|
test::run(ioc);
|
||||||
BEAST_EXPECT(buffers_to_string(b.data()) == s);
|
|
||||||
});
|
flat_buffer b;
|
||||||
|
ws1.set_option(stream_base::timeout{
|
||||||
|
stream_base::none(),
|
||||||
|
std::chrono::milliseconds(50),
|
||||||
|
false});
|
||||||
|
ws1.async_read(b, test::fail_handler(
|
||||||
|
beast::error::timeout));
|
||||||
|
test::run(ioc);
|
||||||
|
}
|
||||||
|
|
||||||
// partial message
|
|
||||||
doTest(pmd, [&](ws_type& ws)
|
|
||||||
{
|
{
|
||||||
std::string const s = "Hello";
|
stream<test::stream> ws1(ioc);
|
||||||
w.write(ws, net::buffer(s));
|
stream<test::stream> ws2(ioc);
|
||||||
char buf[3];
|
test::connect(ws1.next_layer(), ws2.next_layer());
|
||||||
auto const bytes_written =
|
ws1.async_handshake("test", "/", test::success_handler());
|
||||||
w.read_some(ws, net::buffer(buf, sizeof(buf)));
|
ws2.async_accept(test::success_handler());
|
||||||
BEAST_EXPECT(bytes_written > 0);
|
test::run(ioc);
|
||||||
BEAST_EXPECT(
|
|
||||||
string_view(buf, 3).substr(0, bytes_written) ==
|
|
||||||
s.substr(0, bytes_written));
|
|
||||||
});
|
|
||||||
|
|
||||||
// partial message, dynamic buffer
|
flat_buffer b;
|
||||||
doTest(pmd, [&](ws_type& ws)
|
ws1.set_option(stream_base::timeout{
|
||||||
{
|
stream_base::none(),
|
||||||
std::string const s = "Hello, world!";
|
std::chrono::milliseconds(50),
|
||||||
w.write(ws, net::buffer(s));
|
false});
|
||||||
multi_buffer b;
|
ws1.async_read(b, test::fail_handler(
|
||||||
auto bytes_written =
|
beast::error::timeout));
|
||||||
w.read_some(ws, 3, b);
|
test::run(ioc);
|
||||||
BEAST_EXPECT(bytes_written > 0);
|
|
||||||
BEAST_EXPECT(buffers_to_string(b.data()) ==
|
|
||||||
s.substr(0, b.size()));
|
|
||||||
w.read_some(ws, 256, b);
|
|
||||||
BEAST_EXPECT(buffers_to_string(b.data()) == s);
|
|
||||||
});
|
|
||||||
|
|
||||||
// big message
|
|
||||||
doTest(pmd, [&](ws_type& ws)
|
|
||||||
{
|
|
||||||
auto const& s = random_string();
|
|
||||||
ws.binary(true);
|
|
||||||
w.write(ws, net::buffer(s));
|
|
||||||
multi_buffer b;
|
|
||||||
w.read(ws, b);
|
|
||||||
BEAST_EXPECT(buffers_to_string(b.data()) == s);
|
|
||||||
});
|
|
||||||
|
|
||||||
// message, bad utf8
|
|
||||||
doTest(pmd, [&](ws_type& ws)
|
|
||||||
{
|
|
||||||
std::string const s = "\x03\xea\xf0\x28\x8c\xbc";
|
|
||||||
ws.auto_fragment(false);
|
|
||||||
ws.text(true);
|
|
||||||
w.write(ws, net::buffer(s));
|
|
||||||
doReadTest(w, ws, close_code::bad_payload);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
testRead()
|
|
||||||
{
|
|
||||||
doTestRead<false>(SyncClient{});
|
|
||||||
doTestRead<true>(SyncClient{});
|
|
||||||
doTestReadDeflate(SyncClient{});
|
|
||||||
yield_to([&](yield_context yield)
|
|
||||||
{
|
|
||||||
doTestRead<false>(AsyncClient{yield});
|
|
||||||
doTestRead<true>(AsyncClient{yield});
|
|
||||||
doTestReadDeflate(AsyncClient{yield});
|
|
||||||
});
|
|
||||||
|
|
||||||
permessage_deflate pmd;
|
|
||||||
pmd.client_enable = false;
|
|
||||||
pmd.server_enable = false;
|
|
||||||
doTestRead(pmd, SyncClient{});
|
|
||||||
yield_to([&](yield_context yield)
|
|
||||||
{
|
|
||||||
doTestRead(pmd, AsyncClient{yield});
|
|
||||||
});
|
|
||||||
|
|
||||||
pmd.client_enable = true;
|
|
||||||
pmd.server_enable = true;
|
|
||||||
pmd.client_max_window_bits = 9;
|
|
||||||
pmd.server_max_window_bits = 9;
|
|
||||||
pmd.compLevel = 1;
|
|
||||||
doTestRead(pmd, SyncClient{});
|
|
||||||
yield_to([&](yield_context yield)
|
|
||||||
{
|
|
||||||
doTestRead(pmd, AsyncClient{yield});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Read close frames
|
|
||||||
{
|
|
||||||
auto const check =
|
|
||||||
[&](error_code ev, string_view s)
|
|
||||||
{
|
|
||||||
echo_server es{log};
|
|
||||||
stream<test::stream> ws{ioc_};
|
|
||||||
ws.next_layer().connect(es.stream());
|
|
||||||
ws.handshake("localhost", "/");
|
|
||||||
ws.next_layer().append(s);
|
|
||||||
static_buffer<1> b;
|
|
||||||
error_code ec;
|
|
||||||
ws.read(b, ec);
|
|
||||||
BEAST_EXPECTS(ec == ev, ec.message());
|
|
||||||
ws.next_layer().close();
|
|
||||||
};
|
|
||||||
|
|
||||||
// payload length 1
|
|
||||||
check(error::bad_close_size,
|
|
||||||
"\x88\x01\x01");
|
|
||||||
|
|
||||||
// invalid close code 1005
|
|
||||||
check(error::bad_close_code,
|
|
||||||
"\x88\x02\x03\xed");
|
|
||||||
|
|
||||||
// invalid utf8
|
|
||||||
check(error::bad_close_payload,
|
|
||||||
"\x88\x06\xfc\x15\x0f\xd7\x73\x43");
|
|
||||||
|
|
||||||
// good utf8
|
|
||||||
check(error::closed,
|
|
||||||
"\x88\x06\xfc\x15utf8");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
run() override
|
run() override
|
||||||
{
|
{
|
||||||
testRead();
|
testTimeout();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
655
test/beast/websocket/read3.cpp
Normal file
655
test/beast/websocket/read3.cpp
Normal file
@@ -0,0 +1,655 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
// Official repository: https://github.com/boostorg/beast
|
||||||
|
//
|
||||||
|
|
||||||
|
// Test that header file is self-contained.
|
||||||
|
#include <boost/beast/websocket/stream.hpp>
|
||||||
|
|
||||||
|
#include "test.hpp"
|
||||||
|
|
||||||
|
#include <boost/asio/buffer.hpp>
|
||||||
|
#include <boost/asio/io_context.hpp>
|
||||||
|
#include <boost/asio/strand.hpp>
|
||||||
|
#include <boost/asio/write.hpp>
|
||||||
|
|
||||||
|
namespace boost {
|
||||||
|
namespace beast {
|
||||||
|
namespace websocket {
|
||||||
|
|
||||||
|
class read3_test : public websocket_test_suite
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void
|
||||||
|
testSuspend()
|
||||||
|
{
|
||||||
|
// suspend on read block
|
||||||
|
doFailLoop([&](test::fail_count& fc)
|
||||||
|
{
|
||||||
|
echo_server es{log};
|
||||||
|
net::io_context ioc;
|
||||||
|
stream<test::stream> ws{ioc, fc};
|
||||||
|
ws.next_layer().connect(es.stream());
|
||||||
|
ws.handshake("localhost", "/");
|
||||||
|
std::size_t count = 0;
|
||||||
|
ws.async_close({},
|
||||||
|
[&](error_code ec)
|
||||||
|
{
|
||||||
|
if(ec)
|
||||||
|
BOOST_THROW_EXCEPTION(
|
||||||
|
system_error{ec});
|
||||||
|
BEAST_EXPECT(++count == 1);
|
||||||
|
});
|
||||||
|
while(! ws.impl_->rd_block.is_locked())
|
||||||
|
ioc.run_one();
|
||||||
|
multi_buffer b;
|
||||||
|
ws.async_read(b,
|
||||||
|
[&](error_code ec, std::size_t)
|
||||||
|
{
|
||||||
|
if(ec != net::error::operation_aborted)
|
||||||
|
BOOST_THROW_EXCEPTION(
|
||||||
|
system_error{ec});
|
||||||
|
BEAST_EXPECT(++count == 2);
|
||||||
|
});
|
||||||
|
ioc.run();
|
||||||
|
BEAST_EXPECT(count == 2);
|
||||||
|
});
|
||||||
|
|
||||||
|
// suspend on release read block
|
||||||
|
doFailLoop([&](test::fail_count& fc)
|
||||||
|
{
|
||||||
|
echo_server es{log};
|
||||||
|
net::io_context ioc;
|
||||||
|
stream<test::stream> ws{ioc, fc};
|
||||||
|
ws.next_layer().connect(es.stream());
|
||||||
|
ws.handshake("localhost", "/");
|
||||||
|
std::size_t count = 0;
|
||||||
|
multi_buffer b;
|
||||||
|
ws.async_read(b,
|
||||||
|
[&](error_code ec, std::size_t)
|
||||||
|
{
|
||||||
|
if(ec != net::error::operation_aborted)
|
||||||
|
BOOST_THROW_EXCEPTION(
|
||||||
|
system_error{ec});
|
||||||
|
BEAST_EXPECT(++count == 2);
|
||||||
|
});
|
||||||
|
BOOST_ASSERT(ws.impl_->rd_block.is_locked());
|
||||||
|
ws.async_close({},
|
||||||
|
[&](error_code ec)
|
||||||
|
{
|
||||||
|
if(ec)
|
||||||
|
BOOST_THROW_EXCEPTION(
|
||||||
|
system_error{ec});
|
||||||
|
BEAST_EXPECT(++count == 1);
|
||||||
|
});
|
||||||
|
ioc.run();
|
||||||
|
BEAST_EXPECT(count == 2);
|
||||||
|
});
|
||||||
|
|
||||||
|
// suspend on write pong
|
||||||
|
doFailLoop([&](test::fail_count& fc)
|
||||||
|
{
|
||||||
|
echo_server es{log};
|
||||||
|
net::io_context ioc;
|
||||||
|
stream<test::stream> ws{ioc, fc};
|
||||||
|
ws.next_layer().connect(es.stream());
|
||||||
|
ws.handshake("localhost", "/");
|
||||||
|
// insert a ping
|
||||||
|
ws.next_layer().append(string_view(
|
||||||
|
"\x89\x00", 2));
|
||||||
|
std::size_t count = 0;
|
||||||
|
std::string const s = "Hello, world";
|
||||||
|
multi_buffer b;
|
||||||
|
ws.async_read(b,
|
||||||
|
[&](error_code ec, std::size_t)
|
||||||
|
{
|
||||||
|
if(ec)
|
||||||
|
BOOST_THROW_EXCEPTION(
|
||||||
|
system_error{ec});
|
||||||
|
BEAST_EXPECT(buffers_to_string(b.data()) == s);
|
||||||
|
++count;
|
||||||
|
});
|
||||||
|
BEAST_EXPECT(ws.impl_->rd_block.is_locked());
|
||||||
|
ws.async_write(net::buffer(s),
|
||||||
|
[&](error_code ec, std::size_t n)
|
||||||
|
{
|
||||||
|
if(ec)
|
||||||
|
BOOST_THROW_EXCEPTION(
|
||||||
|
system_error{ec});
|
||||||
|
BEAST_EXPECT(n == s.size());
|
||||||
|
++count;
|
||||||
|
});
|
||||||
|
BEAST_EXPECT(ws.impl_->wr_block.is_locked());
|
||||||
|
ioc.run();
|
||||||
|
BEAST_EXPECT(count == 2);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Ignore ping when closing
|
||||||
|
doFailLoop([&](test::fail_count& fc)
|
||||||
|
{
|
||||||
|
echo_server es{log};
|
||||||
|
net::io_context ioc;
|
||||||
|
stream<test::stream> ws{ioc, fc};
|
||||||
|
ws.next_layer().connect(es.stream());
|
||||||
|
ws.handshake("localhost", "/");
|
||||||
|
std::size_t count = 0;
|
||||||
|
// insert fragmented message with
|
||||||
|
// a ping in between the frames.
|
||||||
|
ws.next_layer().append(string_view(
|
||||||
|
"\x01\x01*"
|
||||||
|
"\x89\x00"
|
||||||
|
"\x80\x01*", 8));
|
||||||
|
multi_buffer b;
|
||||||
|
ws.async_read(b,
|
||||||
|
[&](error_code ec, std::size_t)
|
||||||
|
{
|
||||||
|
if(ec)
|
||||||
|
BOOST_THROW_EXCEPTION(
|
||||||
|
system_error{ec});
|
||||||
|
BEAST_EXPECT(buffers_to_string(b.data()) == "**");
|
||||||
|
BEAST_EXPECT(++count == 1);
|
||||||
|
b.consume(b.size());
|
||||||
|
ws.async_read(b,
|
||||||
|
[&](error_code ec, std::size_t)
|
||||||
|
{
|
||||||
|
if(ec != net::error::operation_aborted)
|
||||||
|
BOOST_THROW_EXCEPTION(
|
||||||
|
system_error{ec});
|
||||||
|
BEAST_EXPECT(++count == 3);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
BEAST_EXPECT(ws.impl_->rd_block.is_locked());
|
||||||
|
ws.async_close({},
|
||||||
|
[&](error_code ec)
|
||||||
|
{
|
||||||
|
if(ec)
|
||||||
|
BOOST_THROW_EXCEPTION(
|
||||||
|
system_error{ec});
|
||||||
|
BEAST_EXPECT(++count == 2);
|
||||||
|
});
|
||||||
|
BEAST_EXPECT(ws.impl_->wr_block.is_locked());
|
||||||
|
ioc.run();
|
||||||
|
BEAST_EXPECT(count == 3);
|
||||||
|
});
|
||||||
|
|
||||||
|
// See if we are already closing
|
||||||
|
doFailLoop([&](test::fail_count& fc)
|
||||||
|
{
|
||||||
|
echo_server es{log};
|
||||||
|
net::io_context ioc;
|
||||||
|
stream<test::stream> ws{ioc, fc};
|
||||||
|
ws.next_layer().connect(es.stream());
|
||||||
|
ws.handshake("localhost", "/");
|
||||||
|
std::size_t count = 0;
|
||||||
|
// insert fragmented message with
|
||||||
|
// a close in between the frames.
|
||||||
|
ws.next_layer().append(string_view(
|
||||||
|
"\x01\x01*"
|
||||||
|
"\x88\x00"
|
||||||
|
"\x80\x01*", 8));
|
||||||
|
multi_buffer b;
|
||||||
|
ws.async_read(b,
|
||||||
|
[&](error_code ec, std::size_t)
|
||||||
|
{
|
||||||
|
if(ec != net::error::operation_aborted)
|
||||||
|
BOOST_THROW_EXCEPTION(
|
||||||
|
system_error{ec});
|
||||||
|
BEAST_EXPECT(++count == 2);
|
||||||
|
});
|
||||||
|
BEAST_EXPECT(ws.impl_->rd_block.is_locked());
|
||||||
|
ws.async_close({},
|
||||||
|
[&](error_code ec)
|
||||||
|
{
|
||||||
|
if(ec)
|
||||||
|
BOOST_THROW_EXCEPTION(
|
||||||
|
system_error{ec});
|
||||||
|
BEAST_EXPECT(++count == 1);
|
||||||
|
});
|
||||||
|
BEAST_EXPECT(ws.impl_->wr_block.is_locked());
|
||||||
|
ioc.run();
|
||||||
|
BEAST_EXPECT(count == 2);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testParseFrame()
|
||||||
|
{
|
||||||
|
auto const bad =
|
||||||
|
[&](string_view s)
|
||||||
|
{
|
||||||
|
echo_server es{log};
|
||||||
|
net::io_context ioc;
|
||||||
|
stream<test::stream> ws{ioc};
|
||||||
|
ws.next_layer().connect(es.stream());
|
||||||
|
ws.handshake("localhost", "/");
|
||||||
|
ws.next_layer().append(s);
|
||||||
|
error_code ec;
|
||||||
|
multi_buffer b;
|
||||||
|
ws.read(b, ec);
|
||||||
|
BEAST_EXPECT(ec);
|
||||||
|
};
|
||||||
|
|
||||||
|
// chopped frame header
|
||||||
|
{
|
||||||
|
echo_server es{log};
|
||||||
|
net::io_context ioc;
|
||||||
|
stream<test::stream> ws{ioc};
|
||||||
|
ws.next_layer().connect(es.stream());
|
||||||
|
ws.handshake("localhost", "/");
|
||||||
|
ws.next_layer().append(
|
||||||
|
"\x81\x7e\x01");
|
||||||
|
std::size_t count = 0;
|
||||||
|
std::string const s(257, '*');
|
||||||
|
multi_buffer b;
|
||||||
|
ws.async_read(b,
|
||||||
|
[&](error_code ec, std::size_t)
|
||||||
|
{
|
||||||
|
++count;
|
||||||
|
BEAST_EXPECTS(! ec, ec.message());
|
||||||
|
BEAST_EXPECT(buffers_to_string(b.data()) == s);
|
||||||
|
});
|
||||||
|
ioc.run_one();
|
||||||
|
es.stream().write_some(
|
||||||
|
net::buffer("\x01" + s));
|
||||||
|
ioc.run();
|
||||||
|
BEAST_EXPECT(count == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// new data frame when continuation expected
|
||||||
|
bad("\x01\x01*" "\x81\x01*");
|
||||||
|
|
||||||
|
// reserved bits not cleared
|
||||||
|
bad("\xb1\x01*");
|
||||||
|
bad("\xc1\x01*");
|
||||||
|
bad("\xd1\x01*");
|
||||||
|
|
||||||
|
// continuation without an active message
|
||||||
|
bad("\x80\x01*");
|
||||||
|
|
||||||
|
// reserved bits not cleared (cont)
|
||||||
|
bad("\x01\x01*" "\xb0\x01*");
|
||||||
|
bad("\x01\x01*" "\xc0\x01*");
|
||||||
|
bad("\x01\x01*" "\xd0\x01*");
|
||||||
|
|
||||||
|
// reserved opcode
|
||||||
|
bad("\x83\x01*");
|
||||||
|
|
||||||
|
// fragmented control message
|
||||||
|
bad("\x09\x01*");
|
||||||
|
|
||||||
|
// invalid length for control message
|
||||||
|
bad("\x89\x7e\x01\x01");
|
||||||
|
|
||||||
|
// reserved bits not cleared (control)
|
||||||
|
bad("\xb9\x01*");
|
||||||
|
bad("\xc9\x01*");
|
||||||
|
bad("\xd9\x01*");
|
||||||
|
|
||||||
|
// unmasked frame from client
|
||||||
|
{
|
||||||
|
echo_server es{log, kind::async_client};
|
||||||
|
net::io_context ioc;
|
||||||
|
stream<test::stream> ws{ioc};
|
||||||
|
ws.next_layer().connect(es.stream());
|
||||||
|
es.async_handshake();
|
||||||
|
ws.accept();
|
||||||
|
ws.next_layer().append(
|
||||||
|
"\x81\x01*");
|
||||||
|
error_code ec;
|
||||||
|
multi_buffer b;
|
||||||
|
ws.read(b, ec);
|
||||||
|
BEAST_EXPECT(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
// masked frame from server
|
||||||
|
bad("\x81\x80\xff\xff\xff\xff");
|
||||||
|
|
||||||
|
// chopped control frame payload
|
||||||
|
{
|
||||||
|
echo_server es{log};
|
||||||
|
net::io_context ioc;
|
||||||
|
stream<test::stream> ws{ioc};
|
||||||
|
ws.next_layer().connect(es.stream());
|
||||||
|
ws.handshake("localhost", "/");
|
||||||
|
ws.next_layer().append(
|
||||||
|
"\x89\x02*");
|
||||||
|
std::size_t count = 0;
|
||||||
|
multi_buffer b;
|
||||||
|
ws.async_read(b,
|
||||||
|
[&](error_code ec, std::size_t)
|
||||||
|
{
|
||||||
|
++count;
|
||||||
|
BEAST_EXPECTS(! ec, ec.message());
|
||||||
|
BEAST_EXPECT(buffers_to_string(b.data()) == "**");
|
||||||
|
});
|
||||||
|
ioc.run_one();
|
||||||
|
es.stream().write_some(
|
||||||
|
net::buffer(
|
||||||
|
"*" "\x81\x02**"));
|
||||||
|
ioc.run();
|
||||||
|
BEAST_EXPECT(count == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// length not canonical
|
||||||
|
bad(string_view("\x81\x7e\x00\x7d", 4));
|
||||||
|
bad(string_view("\x81\x7f\x00\x00\x00\x00\x00\x00\xff\xff", 10));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testIssue802()
|
||||||
|
{
|
||||||
|
for(std::size_t i = 0; i < 100; ++i)
|
||||||
|
{
|
||||||
|
echo_server es{log, kind::async};
|
||||||
|
net::io_context ioc;
|
||||||
|
stream<test::stream> ws{ioc};
|
||||||
|
ws.next_layer().connect(es.stream());
|
||||||
|
ws.handshake("localhost", "/");
|
||||||
|
// too-big message frame indicates payload of 2^64-1
|
||||||
|
net::write(ws.next_layer(), sbuf(
|
||||||
|
"\x81\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"));
|
||||||
|
multi_buffer b;
|
||||||
|
error_code ec;
|
||||||
|
ws.read(b, ec);
|
||||||
|
BEAST_EXPECT(ec == error::closed);
|
||||||
|
BEAST_EXPECT(ws.reason().code == 1009);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testIssue807()
|
||||||
|
{
|
||||||
|
echo_server es{log};
|
||||||
|
net::io_context ioc;
|
||||||
|
stream<test::stream> ws{ioc};
|
||||||
|
ws.next_layer().connect(es.stream());
|
||||||
|
ws.handshake("localhost", "/");
|
||||||
|
ws.write(sbuf("Hello, world!"));
|
||||||
|
char buf[4];
|
||||||
|
net::mutable_buffer b{buf, 0};
|
||||||
|
auto const n = ws.read_some(b);
|
||||||
|
BEAST_EXPECT(n == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
When the internal read buffer contains a control frame and
|
||||||
|
stream::async_read_some is called, it is possible for the control
|
||||||
|
callback to be invoked on the caller's stack instead of through
|
||||||
|
the executor associated with the final completion handler.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
testIssue954()
|
||||||
|
{
|
||||||
|
echo_server es{log};
|
||||||
|
multi_buffer b;
|
||||||
|
net::io_context ioc;
|
||||||
|
stream<test::stream> ws{ioc};
|
||||||
|
ws.next_layer().connect(es.stream());
|
||||||
|
ws.handshake("localhost", "/");
|
||||||
|
// message followed by ping
|
||||||
|
ws.next_layer().append({
|
||||||
|
"\x81\x00"
|
||||||
|
"\x89\x00",
|
||||||
|
4});
|
||||||
|
bool called_cb = false;
|
||||||
|
bool called_handler = false;
|
||||||
|
ws.control_callback(
|
||||||
|
[&called_cb](frame_type, string_view)
|
||||||
|
{
|
||||||
|
called_cb = true;
|
||||||
|
});
|
||||||
|
ws.async_read(b,
|
||||||
|
[&](error_code, std::size_t)
|
||||||
|
{
|
||||||
|
called_handler = true;
|
||||||
|
});
|
||||||
|
BEAST_EXPECT(! called_cb);
|
||||||
|
BEAST_EXPECT(! called_handler);
|
||||||
|
ioc.run();
|
||||||
|
BEAST_EXPECT(! called_cb);
|
||||||
|
BEAST_EXPECT(called_handler);
|
||||||
|
ws.async_read(b,
|
||||||
|
[&](error_code, std::size_t)
|
||||||
|
{
|
||||||
|
});
|
||||||
|
BEAST_EXPECT(! called_cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bishop Fox Hybrid Assessment issue 1
|
||||||
|
|
||||||
|
Happens with permessage-deflate enabled and a
|
||||||
|
compressed frame with the FIN bit set ends with an
|
||||||
|
invalid prefix.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
testIssueBF1()
|
||||||
|
{
|
||||||
|
permessage_deflate pmd;
|
||||||
|
pmd.client_enable = true;
|
||||||
|
pmd.server_enable = true;
|
||||||
|
|
||||||
|
// read
|
||||||
|
#if 0
|
||||||
|
{
|
||||||
|
echo_server es{log};
|
||||||
|
net::io_context ioc;
|
||||||
|
stream<test::stream> ws{ioc};
|
||||||
|
ws.set_option(pmd);
|
||||||
|
ws.next_layer().connect(es.stream());
|
||||||
|
ws.handshake("localhost", "/");
|
||||||
|
// invalid 1-byte deflate block in frame
|
||||||
|
net::write(ws.next_layer(), sbuf(
|
||||||
|
"\xc1\x81\x3a\xa1\x74\x3b\x49"));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
net::io_context ioc;
|
||||||
|
stream<test::stream> wsc{ioc};
|
||||||
|
stream<test::stream> wss{ioc};
|
||||||
|
wsc.set_option(pmd);
|
||||||
|
wss.set_option(pmd);
|
||||||
|
wsc.next_layer().connect(wss.next_layer());
|
||||||
|
wsc.async_handshake(
|
||||||
|
"localhost", "/", [](error_code){});
|
||||||
|
wss.async_accept([](error_code){});
|
||||||
|
ioc.run();
|
||||||
|
ioc.restart();
|
||||||
|
BEAST_EXPECT(wsc.is_open());
|
||||||
|
BEAST_EXPECT(wss.is_open());
|
||||||
|
// invalid 1-byte deflate block in frame
|
||||||
|
net::write(wsc.next_layer(), sbuf(
|
||||||
|
"\xc1\x81\x3a\xa1\x74\x3b\x49"));
|
||||||
|
error_code ec;
|
||||||
|
multi_buffer b;
|
||||||
|
wss.read(b, ec);
|
||||||
|
BEAST_EXPECTS(ec == zlib::error::end_of_stream, ec.message());
|
||||||
|
}
|
||||||
|
|
||||||
|
// async read
|
||||||
|
#if 0
|
||||||
|
{
|
||||||
|
echo_server es{log, kind::async};
|
||||||
|
net::io_context ioc;
|
||||||
|
stream<test::stream> ws{ioc};
|
||||||
|
ws.set_option(pmd);
|
||||||
|
ws.next_layer().connect(es.stream());
|
||||||
|
ws.handshake("localhost", "/");
|
||||||
|
// invalid 1-byte deflate block in frame
|
||||||
|
net::write(ws.next_layer(), sbuf(
|
||||||
|
"\xc1\x81\x3a\xa1\x74\x3b\x49"));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
net::io_context ioc;
|
||||||
|
stream<test::stream> wsc{ioc};
|
||||||
|
stream<test::stream> wss{ioc};
|
||||||
|
wsc.set_option(pmd);
|
||||||
|
wss.set_option(pmd);
|
||||||
|
wsc.next_layer().connect(wss.next_layer());
|
||||||
|
wsc.async_handshake(
|
||||||
|
"localhost", "/", [](error_code){});
|
||||||
|
wss.async_accept([](error_code){});
|
||||||
|
ioc.run();
|
||||||
|
ioc.restart();
|
||||||
|
BEAST_EXPECT(wsc.is_open());
|
||||||
|
BEAST_EXPECT(wss.is_open());
|
||||||
|
// invalid 1-byte deflate block in frame
|
||||||
|
net::write(wsc.next_layer(), sbuf(
|
||||||
|
"\xc1\x81\x3a\xa1\x74\x3b\x49"));
|
||||||
|
error_code ec;
|
||||||
|
flat_buffer b;
|
||||||
|
wss.async_read(b,
|
||||||
|
[&ec](error_code ec_, std::size_t){ ec = ec_; });
|
||||||
|
ioc.run();
|
||||||
|
BEAST_EXPECTS(ec == zlib::error::end_of_stream, ec.message());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bishop Fox Hybrid Assessment issue 2
|
||||||
|
|
||||||
|
Happens with permessage-deflate enabled,
|
||||||
|
and a deflate block with the BFINAL bit set
|
||||||
|
is encountered in a compressed payload.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
testIssueBF2()
|
||||||
|
{
|
||||||
|
permessage_deflate pmd;
|
||||||
|
pmd.client_enable = true;
|
||||||
|
pmd.server_enable = true;
|
||||||
|
|
||||||
|
// read
|
||||||
|
{
|
||||||
|
net::io_context ioc;
|
||||||
|
stream<test::stream> wsc{ioc};
|
||||||
|
stream<test::stream> wss{ioc};
|
||||||
|
wsc.set_option(pmd);
|
||||||
|
wss.set_option(pmd);
|
||||||
|
wsc.next_layer().connect(wss.next_layer());
|
||||||
|
wsc.async_handshake(
|
||||||
|
"localhost", "/", [](error_code){});
|
||||||
|
wss.async_accept([](error_code){});
|
||||||
|
ioc.run();
|
||||||
|
ioc.restart();
|
||||||
|
BEAST_EXPECT(wsc.is_open());
|
||||||
|
BEAST_EXPECT(wss.is_open());
|
||||||
|
// contains a deflate block with BFINAL set
|
||||||
|
net::write(wsc.next_layer(), sbuf(
|
||||||
|
"\xc1\xf8\xd1\xe4\xcc\x3e\xda\xe4\xcc\x3e"
|
||||||
|
"\x2b\x1e\x36\xc4\x2b\x1e\x36\xc4\x2b\x1e"
|
||||||
|
"\x36\x3e\x35\xae\x4f\x54\x18\xae\x4f\x7b"
|
||||||
|
"\xd1\xe4\xcc\x3e\xd1\xe4\xcc\x3e\xd1\xe4"
|
||||||
|
"\xcc\x3e\xd1\xe4\xcc\x3e\xd1\xe4\xcc\x3e"
|
||||||
|
"\xd1\x1e\x36\xc4\x2b\x1e\x36\xc4\x2b\xe4"
|
||||||
|
"\x28\x74\x52\x8e\x05\x74\x52\xa1\xcc\x3e"
|
||||||
|
"\xd1\xe4\xcc\x3e\xd1\xe4\xcc\x3e\xd1\xe4"
|
||||||
|
"\xcc\x3e\xd1\xe4\xcc\x3e\xd1\xe4\xcc\x3e"
|
||||||
|
"\xd1\xe4\xcc\x3e\xd1\xe4\xcc\x3e\xd1\xe4"
|
||||||
|
"\xcc\x3e\xd1\xe4\xcc\x3e\xd1\xe4\x36\x3e"
|
||||||
|
"\xd1\xec\xcc\x3e\xd1\xe4\xcc\x3e\xd1\xe4"
|
||||||
|
"\xcc\x3e\xd1\xe4\xcc\x3e"));
|
||||||
|
error_code ec;
|
||||||
|
flat_buffer b;
|
||||||
|
wss.read(b, ec);
|
||||||
|
BEAST_EXPECTS(ec == zlib::error::end_of_stream, ec.message());
|
||||||
|
}
|
||||||
|
|
||||||
|
// async read
|
||||||
|
{
|
||||||
|
net::io_context ioc;
|
||||||
|
stream<test::stream> wsc{ioc};
|
||||||
|
stream<test::stream> wss{ioc};
|
||||||
|
wsc.set_option(pmd);
|
||||||
|
wss.set_option(pmd);
|
||||||
|
wsc.next_layer().connect(wss.next_layer());
|
||||||
|
wsc.async_handshake(
|
||||||
|
"localhost", "/", [](error_code){});
|
||||||
|
wss.async_accept([](error_code){});
|
||||||
|
ioc.run();
|
||||||
|
ioc.restart();
|
||||||
|
BEAST_EXPECT(wsc.is_open());
|
||||||
|
BEAST_EXPECT(wss.is_open());
|
||||||
|
// contains a deflate block with BFINAL set
|
||||||
|
net::write(wsc.next_layer(), sbuf(
|
||||||
|
"\xc1\xf8\xd1\xe4\xcc\x3e\xda\xe4\xcc\x3e"
|
||||||
|
"\x2b\x1e\x36\xc4\x2b\x1e\x36\xc4\x2b\x1e"
|
||||||
|
"\x36\x3e\x35\xae\x4f\x54\x18\xae\x4f\x7b"
|
||||||
|
"\xd1\xe4\xcc\x3e\xd1\xe4\xcc\x3e\xd1\xe4"
|
||||||
|
"\xcc\x3e\xd1\xe4\xcc\x3e\xd1\xe4\xcc\x3e"
|
||||||
|
"\xd1\x1e\x36\xc4\x2b\x1e\x36\xc4\x2b\xe4"
|
||||||
|
"\x28\x74\x52\x8e\x05\x74\x52\xa1\xcc\x3e"
|
||||||
|
"\xd1\xe4\xcc\x3e\xd1\xe4\xcc\x3e\xd1\xe4"
|
||||||
|
"\xcc\x3e\xd1\xe4\xcc\x3e\xd1\xe4\xcc\x3e"
|
||||||
|
"\xd1\xe4\xcc\x3e\xd1\xe4\xcc\x3e\xd1\xe4"
|
||||||
|
"\xcc\x3e\xd1\xe4\xcc\x3e\xd1\xe4\x36\x3e"
|
||||||
|
"\xd1\xec\xcc\x3e\xd1\xe4\xcc\x3e\xd1\xe4"
|
||||||
|
"\xcc\x3e\xd1\xe4\xcc\x3e"));
|
||||||
|
error_code ec;
|
||||||
|
flat_buffer b;
|
||||||
|
wss.async_read(b,
|
||||||
|
[&ec](error_code ec_, std::size_t){ ec = ec_; });
|
||||||
|
ioc.run();
|
||||||
|
BEAST_EXPECTS(ec == zlib::error::end_of_stream, ec.message());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testMoveOnly()
|
||||||
|
{
|
||||||
|
net::io_context ioc;
|
||||||
|
stream<test::stream> ws{ioc};
|
||||||
|
ws.async_read_some(
|
||||||
|
net::mutable_buffer{},
|
||||||
|
move_only_handler{});
|
||||||
|
}
|
||||||
|
|
||||||
|
struct copyable_handler
|
||||||
|
{
|
||||||
|
template<class... Args>
|
||||||
|
void
|
||||||
|
operator()(Args&&...) const
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
testAsioHandlerInvoke()
|
||||||
|
{
|
||||||
|
// make sure things compile, also can set a
|
||||||
|
// breakpoint in asio_handler_invoke to make sure
|
||||||
|
// it is instantiated.
|
||||||
|
{
|
||||||
|
net::io_context ioc;
|
||||||
|
net::strand<
|
||||||
|
net::io_context::executor_type> s(
|
||||||
|
ioc.get_executor());
|
||||||
|
stream<test::stream> ws{ioc};
|
||||||
|
flat_buffer b;
|
||||||
|
ws.async_read(b, net::bind_executor(
|
||||||
|
s, copyable_handler{}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
run() override
|
||||||
|
{
|
||||||
|
testParseFrame();
|
||||||
|
testIssue802();
|
||||||
|
testIssue807();
|
||||||
|
testIssue954();
|
||||||
|
testIssueBF1();
|
||||||
|
testIssueBF2();
|
||||||
|
testMoveOnly();
|
||||||
|
testAsioHandlerInvoke();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BEAST_DEFINE_TESTSUITE(beast,websocket,read3);
|
||||||
|
|
||||||
|
} // websocket
|
||||||
|
} // beast
|
||||||
|
} // boost
|
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// Copyright (w) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
//
|
//
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
// 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)
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
@@ -10,6 +10,9 @@
|
|||||||
// Test that header file is self-contained.
|
// Test that header file is self-contained.
|
||||||
#include <boost/beast/websocket/stream.hpp>
|
#include <boost/beast/websocket/stream.hpp>
|
||||||
|
|
||||||
|
#include <boost/beast/core/tcp_stream.hpp>
|
||||||
|
#include <boost/asio/strand.hpp>
|
||||||
|
|
||||||
#include "test.hpp"
|
#include "test.hpp"
|
||||||
|
|
||||||
namespace boost {
|
namespace boost {
|
||||||
@@ -19,6 +22,32 @@ namespace websocket {
|
|||||||
class stream_test : public websocket_test_suite
|
class stream_test : public websocket_test_suite
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
void
|
||||||
|
testGetSetOption()
|
||||||
|
{
|
||||||
|
net::io_context ioc;
|
||||||
|
stream<test::stream> ws(ioc);
|
||||||
|
|
||||||
|
{
|
||||||
|
ws.set_option(
|
||||||
|
stream_base::suggested_settings(
|
||||||
|
role_type::client));
|
||||||
|
|
||||||
|
ws.set_option(
|
||||||
|
stream_base::suggested_settings(
|
||||||
|
role_type::server));
|
||||||
|
|
||||||
|
ws.set_option({
|
||||||
|
std::chrono::seconds(30),
|
||||||
|
std::chrono::seconds(300),
|
||||||
|
true});
|
||||||
|
|
||||||
|
stream_base::timeout opt;
|
||||||
|
ws.get_option(opt);
|
||||||
|
ws.set_option(opt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
testOptions()
|
testOptions()
|
||||||
{
|
{
|
||||||
@@ -109,6 +138,19 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testJavadoc()
|
||||||
|
{
|
||||||
|
net::io_context ioc;
|
||||||
|
{
|
||||||
|
websocket::stream<tcp_stream<
|
||||||
|
net::io_context::strand>> ws{net::io_context::strand(ioc)};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
websocket::stream<tcp_stream<net::io_context::executor_type>> ws(ioc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
run() override
|
run() override
|
||||||
{
|
{
|
||||||
@@ -136,6 +178,7 @@ public:
|
|||||||
sizeof(websocket::stream<test::stream&>::impl_type) << std::endl;
|
sizeof(websocket::stream<test::stream&>::impl_type) << std::endl;
|
||||||
|
|
||||||
testOptions();
|
testOptions();
|
||||||
|
testJavadoc();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// Copyright (w) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
//
|
//
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
// 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)
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// Copyright (w) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
//
|
//
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
// 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)
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// Copyright (w) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
//
|
//
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
// 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)
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
@@ -10,7 +10,6 @@
|
|||||||
// Test that header file is self-contained.
|
// Test that header file is self-contained.
|
||||||
#include <boost/beast/websocket/stream.hpp>
|
#include <boost/beast/websocket/stream.hpp>
|
||||||
|
|
||||||
#include <boost/beast/core/flat_buffer.hpp>
|
|
||||||
#include <boost/asio/ip/tcp.hpp>
|
#include <boost/asio/ip/tcp.hpp>
|
||||||
|
|
||||||
#include "test.hpp"
|
#include "test.hpp"
|
||||||
@@ -25,108 +24,9 @@ class timer_test
|
|||||||
public:
|
public:
|
||||||
using tcp = boost::asio::ip::tcp;
|
using tcp = boost::asio::ip::tcp;
|
||||||
|
|
||||||
static
|
|
||||||
void
|
|
||||||
connect(
|
|
||||||
stream<tcp::socket>& ws1,
|
|
||||||
stream<tcp::socket>& ws2)
|
|
||||||
{
|
|
||||||
struct handler
|
|
||||||
{
|
|
||||||
void
|
|
||||||
operator()(error_code ec)
|
|
||||||
{
|
|
||||||
BEAST_EXPECTS(! ec, ec.message());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
tcp::acceptor a(ws1.get_executor().context());
|
|
||||||
auto ep = tcp::endpoint(
|
|
||||||
net::ip::make_address_v4("127.0.0.1"), 0);
|
|
||||||
a.open(ep.protocol());
|
|
||||||
a.set_option(
|
|
||||||
net::socket_base::reuse_address(true));
|
|
||||||
a.bind(ep);
|
|
||||||
a.listen(0);
|
|
||||||
ep = a.local_endpoint();
|
|
||||||
a.async_accept(ws2.next_layer(), handler{});
|
|
||||||
ws1.next_layer().async_connect(ep, handler{});
|
|
||||||
for(;;)
|
|
||||||
{
|
|
||||||
if( ws1.get_executor().context().run_one() +
|
|
||||||
ws2.get_executor().context().run_one() == 0)
|
|
||||||
{
|
|
||||||
ws1.get_executor().context().restart();
|
|
||||||
ws2.get_executor().context().restart();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BEAST_EXPECT(
|
|
||||||
ws1.next_layer().remote_endpoint() ==
|
|
||||||
ws2.next_layer().local_endpoint());
|
|
||||||
BEAST_EXPECT(
|
|
||||||
ws2.next_layer().remote_endpoint() ==
|
|
||||||
ws1.next_layer().local_endpoint());
|
|
||||||
ws2.async_accept(handler{});
|
|
||||||
ws1.async_handshake("test", "/", handler{});
|
|
||||||
for(;;)
|
|
||||||
{
|
|
||||||
if( ws1.get_executor().context().run_one() +
|
|
||||||
ws2.get_executor().context().run_one() == 0)
|
|
||||||
{
|
|
||||||
ws1.get_executor().context().restart();
|
|
||||||
ws2.get_executor().context().restart();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BEAST_EXPECT(ws1.is_open());
|
|
||||||
BEAST_EXPECT(ws2.is_open());
|
|
||||||
BEAST_EXPECT(! ws1.get_executor().context().stopped());
|
|
||||||
BEAST_EXPECT(! ws2.get_executor().context().stopped());
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
void
|
|
||||||
testRead0()
|
|
||||||
{
|
|
||||||
echo_server es(log, kind::async);
|
|
||||||
|
|
||||||
net::io_context ioc;
|
|
||||||
stream<test::stream> ws(ioc);
|
|
||||||
ws.next_layer().connect(es.stream());
|
|
||||||
|
|
||||||
ws.handshake("localhost", "/");
|
|
||||||
flat_buffer b;
|
|
||||||
ws.async_read(b,
|
|
||||||
[&](error_code ec, std::size_t)
|
|
||||||
{
|
|
||||||
log << ec.message() << "\n";
|
|
||||||
});
|
|
||||||
ioc.run();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void
|
|
||||||
testRead()
|
|
||||||
{
|
|
||||||
net::io_context ioc;
|
|
||||||
stream<tcp::socket> ws1(ioc);
|
|
||||||
stream<tcp::socket> ws2(ioc);
|
|
||||||
connect(ws1, ws2);
|
|
||||||
flat_buffer b;
|
|
||||||
|
|
||||||
ws2.async_read(b,
|
|
||||||
[&](error_code ec, std::size_t)
|
|
||||||
{
|
|
||||||
log << "ws1.async_read: " << ec.message() << "\n";
|
|
||||||
});
|
|
||||||
ioc.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
run() override
|
run() override
|
||||||
{
|
{
|
||||||
testRead();
|
|
||||||
pass();
|
pass();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// Copyright (w) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
//
|
//
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
// 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)
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// Copyright (w) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
//
|
//
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
// 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)
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
Reference in New Issue
Block a user