Refactor websocket decorators (API Change):

fix #80, #212, fix #303, fix #314, fix #317

websocket::stream now provides the following families of
functions for performing handshakes:

When operating in the server role:

    * stream::accept
    * stream::accept_ex
    * stream::async_accept
    * stream::async_accept_ex

When operating in the client role:

    * stream::handshake
    * stream::handshake_ex
    * stream::async_handshake
    * stream::async_handshake_ex

Member functions ending with "_ex" allow an additional
RequestDecorator parameter (for the accept family of
functions) or ResponseDecorator parameter (for the
handshake family of functions).

The decorator is called to optionally modify the contents
of the HTTP request or HTTP response object generated by
the implementation, before the message is sent. This
permits callers to set the User-Agent or Server fields,
add or modify HTTP fields related to subprotocols, or
perform any required transformation of the HTTP message
for application-specific needs.

The handshake() family of functions now have an additional
set of overloads accepting a parameter of type response_type&,
allowing the caller to receive the HTTP Response to the
Upgrade handshake. This permits inspection of the response
to handle things like subprotocols, authentication, or
other application-specific needs.

The new implementation does not require any state to be
stored in the stream object. Therefore, websocket::stream
objects are now smaller in size.

The overload of set_option for setting a decorator on the
stream is removed. The only way to set decorators now is
with a suitable overload of accept or handshake.
This commit is contained in:
Vinnie Falco
2017-04-25 09:35:22 -07:00
parent fab1015b89
commit fa1f4c7d56
16 changed files with 2255 additions and 515 deletions

View File

@ -5,6 +5,7 @@
API Changes:
* Provide websocket::stream accept() overloads
* Refactor websocket decorators
--------------------------------------------------------------------------------

View File

@ -112,7 +112,6 @@
<bridgehead renderas="sect3">Options</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.websocket__auto_fragment">auto_fragment</link></member>
<member><link linkend="beast.ref.websocket__decorate">decorate</link></member>
<member><link linkend="beast.ref.websocket__keep_alive">keep_alive</link></member>
<member><link linkend="beast.ref.websocket__message_type">message_type</link></member>
<member><link linkend="beast.ref.websocket__permessage_deflate">permessage_deflate</link></member>

View File

@ -38,25 +38,6 @@ public:
using endpoint_type = boost::asio::ip::tcp::endpoint;
private:
struct identity
{
template<class Body, class Fields>
void
operator()(beast::http::message<
true, Body, Fields>& req) const
{
req.fields.replace("User-Agent", "async_echo_client");
}
template<class Body, class Fields>
void
operator()(beast::http::message<
false, Body, Fields>& resp) const
{
resp.fields.replace("Server", "async_echo_server");
}
};
/** A container of type-erased option setters.
*/
template<class NextLayer>
@ -159,8 +140,6 @@ public:
, acceptor_(ios_)
, work_(ios_)
{
opts_.set_option(
beast::websocket::decorate(identity{}));
thread_.reserve(threads);
for(std::size_t i = 0; i < threads; ++i)
thread_.emplace_back(
@ -282,7 +261,13 @@ private:
void run()
{
auto& d = *d_;
d.ws.async_accept(std::move(*this));
d.ws.async_accept_ex(
[](beast::websocket::response_type& res)
{
res.fields.insert(
"Server", "async_echo_server");
},
std::move(*this));
}
void operator()(error_code ec, std::size_t)

View File

@ -38,25 +38,6 @@ public:
using socket_type = boost::asio::ip::tcp::socket;
private:
struct identity
{
template<class Body, class Fields>
void
operator()(beast::http::message<
true, Body, Fields>& req) const
{
req.fields.replace("User-Agent", "sync_echo_client");
}
template<class Body, class Fields>
void
operator()(beast::http::message<
false, Body, Fields>& resp) const
{
resp.fields.replace("Server", "sync_echo_server");
}
};
/** A container of type-erased option setters.
*/
template<class NextLayer>
@ -151,8 +132,6 @@ public:
, sock_(ios_)
, acceptor_(ios_)
{
opts_.set_option(
beast::websocket::decorate(identity{}));
}
/** Destructor.
@ -293,7 +272,13 @@ private:
socket_type> ws{std::move(sock)};
opts_.set_options(ws);
error_code ec;
ws.accept(ec);
ws.accept_ex(
[](beast::websocket::response_type& res)
{
res.fields.insert(
"Server", "sync_echo_server");
},
ec);
if(ec)
{
fail("accept", ec, id, ep);

View File

@ -54,6 +54,7 @@ public:
read_some(MutableBufferSequence const& buffers,
error_code& ec)
{
ec = boost::asio::error::eof;
return 0;
}
@ -66,7 +67,7 @@ public:
async_completion<ReadHandler,
void(error_code, std::size_t)> completion{handler};
ios_.post(bind_handler(completion.handler,
error_code{}, 0));
boost::asio::error::eof, 0));
return completion.result.get();
}

View File

@ -1,166 +0,0 @@
//
// Copyright (c) 2013-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)
//
#ifndef BEAST_WEBSOCKET_DETAIL_DECORATOR_HPP
#define BEAST_WEBSOCKET_DETAIL_DECORATOR_HPP
#include <beast/http/message.hpp>
#include <beast/http/string_body.hpp>
#include <beast/version.hpp>
#include <type_traits>
#include <utility>
namespace beast {
namespace websocket {
namespace detail {
using request_type = http::request_header;
using response_type = http::response_header;
struct abstract_decorator
{
virtual
~abstract_decorator() = default;
virtual
void
operator()(request_type& req) const = 0;
virtual
void
operator()(response_type& res) const = 0;
};
template<class F>
class decorator : public abstract_decorator
{
F f_;
class call_req_possible
{
template<class U, class R = decltype(
std::declval<U const>().operator()(
std::declval<request_type&>()),
std::true_type{})>
static R check(int);
template<class>
static std::false_type check(...);
public:
using type = decltype(check<F>(0));
};
class call_res_possible
{
template<class U, class R = decltype(
std::declval<U const>().operator()(
std::declval<response_type&>()),
std::true_type{})>
static R check(int);
template<class>
static std::false_type check(...);
public:
using type = decltype(check<F>(0));
};
public:
decorator(F&& t)
: f_(std::move(t))
{
}
decorator(F const& t)
: f_(t)
{
}
void
operator()(request_type& req) const override
{
(*this)(req, typename call_req_possible::type{});
}
void
operator()(response_type& res) const override
{
(*this)(res, typename call_res_possible::type{});
}
private:
void
operator()(request_type& req, std::true_type) const
{
f_(req);
}
void
operator()(request_type& req, std::false_type) const
{
req.fields.replace("User-Agent",
std::string{"Beast/"} + BEAST_VERSION_STRING);
}
void
operator()(response_type& res, std::true_type) const
{
f_(res);
}
void
operator()(response_type& res, std::false_type) const
{
res.fields.replace("Server",
std::string{"Beast/"} + BEAST_VERSION_STRING);
}
};
class decorator_type
{
std::shared_ptr<abstract_decorator> p_;
public:
decorator_type() = delete;
decorator_type(decorator_type&&) = default;
decorator_type(decorator_type const&) = default;
decorator_type& operator=(decorator_type&&) = default;
decorator_type& operator=(decorator_type const&) = default;
template<class F, class =
typename std::enable_if<! std::is_same<
typename std::decay<F>::type,
decorator_type>::value>>
decorator_type(F&& f)
: p_(std::make_shared<decorator<F>>(
std::forward<F>(f)))
{
BOOST_ASSERT(p_);
}
void
operator()(request_type& req)
{
(*p_)(req);
BOOST_ASSERT(p_);
}
void
operator()(response_type& res)
{
(*p_)(res);
BOOST_ASSERT(p_);
}
};
struct default_decorator
{
};
} // detail
} // websocket
} // beast
#endif

View File

@ -11,7 +11,6 @@
#include <beast/websocket/error.hpp>
#include <beast/websocket/option.hpp>
#include <beast/websocket/rfc6455.hpp>
#include <beast/websocket/detail/decorator.hpp>
#include <beast/websocket/detail/frame.hpp>
#include <beast/websocket/detail/invokable.hpp>
#include <beast/websocket/detail/mask.hpp>
@ -50,7 +49,6 @@ protected:
struct op {};
detail::maskgen maskgen_; // source of mask keys
decorator_type d_; // adorns http messages
bool keep_alive_ = false; // close on failed upgrade
std::size_t rd_msg_max_ =
16 * 1024 * 1024; // max message size
@ -153,16 +151,12 @@ protected:
// Offer for clients, negotiated result for servers
pmd_offer pmd_config_;
stream_base() = default;
stream_base(stream_base&&) = default;
stream_base(stream_base const&) = delete;
stream_base& operator=(stream_base&&) = default;
stream_base& operator=(stream_base const&) = delete;
stream_base()
: d_(detail::default_decorator{})
{
}
template<class = void>
void
open(role_type role);

View File

@ -0,0 +1,32 @@
//
// Copyright (c) 2013-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)
//
#ifndef BEAST_WEBSOCKET_DETAIL_TYPE_TRAITS_HPP
#define BEAST_WEBSOCKET_DETAIL_TYPE_TRAITS_HPP
#include <beast/websocket/rfc6455.hpp>
#include <beast/core/detail/is_call_possible.hpp>
namespace beast {
namespace websocket {
namespace detail {
template<class F>
using is_RequestDecorator =
typename beast::detail::is_call_possible<F,
void(request_type&)>::type;
template<class F>
using is_ResponseDecorator =
typename beast::detail::is_call_possible<F,
void(response_type&)>::type;
} // detail
} // websocket
} // beast
#endif

View File

@ -8,6 +8,7 @@
#ifndef BEAST_WEBSOCKET_IMPL_ACCEPT_IPP
#define BEAST_WEBSOCKET_IMPL_ACCEPT_IPP
#include <beast/websocket/detail/type_traits.hpp>
#include <beast/http/message.hpp>
#include <beast/http/header_parser.hpp>
#include <beast/http/read.hpp>
@ -38,23 +39,27 @@ class stream<NextLayer>::response_op
http::response_header res;
int state = 0;
template<class Fields>
template<class Fields, class Decorator>
data(Handler&, stream<NextLayer>& ws_,
http::header<true, Fields> const& req,
bool cont_)
Decorator const& decorator,
bool cont_)
: cont(cont_)
, ws(ws_)
, res(ws_.build_response(req))
, res(ws_.build_response(req, decorator))
{
}
template<class Fields, class Buffers>
template<class Fields,
class Buffers, class Decorator>
data(Handler&, stream<NextLayer>& ws_,
http::header<true, Fields> const& req,
Buffers const& buffers, bool cont_)
Buffers const& buffers,
Decorator const& decorator,
bool cont_)
: cont(cont_)
, ws(ws_)
, res(ws_.build_response(req))
, res(ws_.build_response(req, decorator))
{
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
@ -155,26 +160,32 @@ operator()(error_code ec, bool again)
// read and respond to an upgrade request
//
template<class NextLayer>
template<class Handler>
template<class Decorator, class Handler>
class stream<NextLayer>::accept_op
{
struct data
{
bool cont;
stream<NextLayer>& ws;
Decorator decorator;
http::header_parser<true, http::fields> p;
int state = 0;
// VFALCO These lines are formatted to work around a codecov defect
data(Handler& handler, stream<NextLayer>& ws_) : cont(beast_asio_helpers::is_continuation(handler))
, ws(ws_) {}
data(Handler& handler, stream<NextLayer>& ws_,
Decorator const& decorator_)
: cont(beast_asio_helpers::is_continuation(handler))
, ws(ws_)
, decorator(decorator_)
{
}
template<class Buffers>
data(Handler& handler, stream<NextLayer>& ws_,
Buffers const& buffers)
: cont(beast_asio_helpers::
is_continuation(handler))
Buffers const& buffers,
Decorator const& decorator_)
: cont(beast_asio_helpers::is_continuation(handler))
, ws(ws_)
, decorator(decorator_)
{
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
@ -235,9 +246,9 @@ public:
};
template<class NextLayer>
template<class Handler>
template<class Decorator, class Handler>
void
stream<NextLayer>::accept_op<Handler>::
stream<NextLayer>::accept_op<Decorator, Handler>::
operator()(error_code ec,
std::size_t bytes_used, bool again)
{
@ -263,10 +274,19 @@ operator()(error_code ec,
// moved to the stack before releasing
// the handler.
auto& ws = d.ws;
auto m = d.p.release();
auto const req = d.p.release();
auto const decorator = d.decorator;
#if 1
response_op<Handler>{
d_.release_handler(),
ws, std::move(m), true};
ws, req, decorator, true};
#else
// VFALCO This *should* work but breaks
// coroutine invariants in the unit test.
// Also it calls reset() when it shouldn't.
ws.async_accept_ex(
req, decorator, d_.release_handler());
#endif
return;
}
}
@ -289,6 +309,23 @@ accept()
throw system_error{ec};
}
template<class NextLayer>
template<class ResponseDecorator>
void
stream<NextLayer>::
accept_ex(ResponseDecorator const& decorator)
{
static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met");
static_assert(detail::is_ResponseDecorator<
ResponseDecorator>::value,
"ResponseDecorator requirements not met");
error_code ec;
accept_ex(decorator, ec);
if(ec)
throw system_error{ec};
}
template<class NextLayer>
void
stream<NextLayer>::
@ -297,7 +334,22 @@ accept(error_code& ec)
static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met");
reset();
do_accept(ec);
do_accept(&default_decorate_res, ec);
}
template<class NextLayer>
template<class ResponseDecorator>
void
stream<NextLayer>::
accept_ex(ResponseDecorator const& decorator, error_code& ec)
{
static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met");
static_assert(detail::is_ResponseDecorator<
ResponseDecorator>::value,
"ResponseDecorator requirements not met");
reset();
do_accept(decorator, ec);
}
template<class NextLayer>
@ -317,6 +369,28 @@ accept(ConstBufferSequence const& buffers)
throw system_error{ec};
}
template<class NextLayer>
template<
class ConstBufferSequence, class ResponseDecorator>
void
stream<NextLayer>::
accept_ex(ConstBufferSequence const& buffers,
ResponseDecorator const &decorator)
{
static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met");
static_assert(is_ConstBufferSequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
static_assert(detail::is_ResponseDecorator<
ResponseDecorator>::value,
"ResponseDecorator requirements not met");
error_code ec;
accept_ex(buffers, decorator, ec);
if(ec)
throw system_error{ec};
}
template<class NextLayer>
template<class ConstBufferSequence>
void
@ -334,7 +408,32 @@ accept(ConstBufferSequence const& buffers, error_code& ec)
stream_.buffer().commit(buffer_copy(
stream_.buffer().prepare(
buffer_size(buffers)), buffers));
do_accept(ec);
do_accept(&default_decorate_res, ec);
}
template<class NextLayer>
template<
class ConstBufferSequence, class ResponseDecorator>
void
stream<NextLayer>::
accept_ex(ConstBufferSequence const& buffers,
ResponseDecorator const& decorator, error_code& ec)
{
static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met");
static_assert(is_ConstBufferSequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
static_assert(is_ConstBufferSequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
reset();
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
stream_.buffer().commit(buffer_copy(
stream_.buffer().prepare(
buffer_size(buffers)), buffers));
do_accept(decorator, ec);
}
template<class NextLayer>
@ -351,6 +450,24 @@ accept(http::header<true, Fields> const& req)
throw system_error{ec};
}
template<class NextLayer>
template<class Fields, class ResponseDecorator>
void
stream<NextLayer>::
accept_ex(http::header<true, Fields> const& req,
ResponseDecorator const& decorator)
{
static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met");
static_assert(detail::is_ResponseDecorator<
ResponseDecorator>::value,
"ResponseDecorator requirements not met");
error_code ec;
accept_ex(req, decorator, ec);
if(ec)
throw system_error{ec};
}
template<class NextLayer>
template<class Fields>
void
@ -361,15 +478,30 @@ accept(http::header<true, Fields> const& req,
static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met");
reset();
do_accept(req, ec);
do_accept(req, &default_decorate_res, ec);
}
template<class NextLayer>
template<class Fields, class ResponseDecorator>
void
stream<NextLayer>::
accept_ex(http::header<true, Fields> const& req,
ResponseDecorator const& decorator, error_code& ec)
{
static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met");
static_assert(detail::is_ResponseDecorator<
ResponseDecorator>::value,
"ResponseDecorator requirements not met");
reset();
do_accept(req, decorator, ec);
}
template<class NextLayer>
template<class Fields, class ConstBufferSequence>
void
stream<NextLayer>::
accept(
http::header<true, Fields> const& req,
accept(http::header<true, Fields> const& req,
ConstBufferSequence const& buffers)
{
static_assert(is_SyncStream<next_layer_type>::value,
@ -383,14 +515,35 @@ accept(
throw system_error{ec};
}
template<class NextLayer>
template<class Fields,
class ConstBufferSequence, class ResponseDecorator>
void
stream<NextLayer>::
accept_ex(http::header<true, Fields> const& req,
ConstBufferSequence const& buffers,
ResponseDecorator const& decorator)
{
static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met");
static_assert(is_ConstBufferSequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
static_assert(detail::is_ResponseDecorator<
ResponseDecorator>::value,
"ResponseDecorator requirements not met");
error_code ec;
accept_ex(req, buffers, decorator, ec);
if(ec)
throw system_error{ec};
}
template<class NextLayer>
template<class Fields, class ConstBufferSequence>
void
stream<NextLayer>::
accept(
http::header<true, Fields> const& req,
ConstBufferSequence const& buffers,
error_code& ec)
accept(http::header<true, Fields> const& req,
ConstBufferSequence const& buffers, error_code& ec)
{
static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met");
@ -403,7 +556,34 @@ accept(
stream_.buffer().commit(buffer_copy(
stream_.buffer().prepare(
buffer_size(buffers)), buffers));
do_accept(req, ec);
do_accept(req, &default_decorate_res, ec);
}
template<class NextLayer>
template<class Fields,
class ConstBufferSequence, class ResponseDecorator>
void
stream<NextLayer>::
accept_ex(http::header<true, Fields> const& req,
ConstBufferSequence const& buffers,
ResponseDecorator const& decorator,
error_code& ec)
{
static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met");
static_assert(is_ConstBufferSequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
static_assert(detail::is_ResponseDecorator<
ResponseDecorator>::value,
"ResponseDecorator requirements not met");
reset();
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
stream_.buffer().commit(buffer_copy(
stream_.buffer().prepare(
buffer_size(buffers)), buffers));
do_accept(req, decorator, ec);
}
//------------------------------------------------------------------------------
@ -420,8 +600,30 @@ async_accept(AcceptHandler&& handler)
beast::async_completion<AcceptHandler,
void(error_code)> completion{handler};
reset();
accept_op<decltype(completion.handler)>{
completion.handler, *this};
accept_op<decltype(&default_decorate_res),
decltype(completion.handler)>{completion.handler,
*this, &default_decorate_res};
return completion.result.get();
}
template<class NextLayer>
template<class ResponseDecorator, class AcceptHandler>
typename async_completion<AcceptHandler,
void(error_code)>::result_type
stream<NextLayer>::
async_accept_ex(ResponseDecorator const& decorator,
AcceptHandler&& handler)
{
static_assert(is_AsyncStream<next_layer_type>::value,
"AsyncStream requirements requirements not met");
static_assert(detail::is_ResponseDecorator<
ResponseDecorator>::value,
"ResponseDecorator requirements not met");
beast::async_completion<AcceptHandler,
void(error_code)> completion{handler};
reset();
accept_op<ResponseDecorator, decltype(completion.handler)>{
completion.handler, *this, decorator};
return completion.result.get();
}
@ -441,8 +643,35 @@ async_accept(ConstBufferSequence const& buffers,
beast::async_completion<AcceptHandler,
void(error_code)> completion{handler};
reset();
accept_op<decltype(completion.handler)>{
completion.handler, *this, buffers};
accept_op<decltype(&default_decorate_res),
decltype(completion.handler)>{completion.handler,
*this, buffers, &default_decorate_res};
return completion.result.get();
}
template<class NextLayer>
template<class ConstBufferSequence,
class ResponseDecorator, class AcceptHandler>
typename async_completion<AcceptHandler,
void(error_code)>::result_type
stream<NextLayer>::
async_accept_ex(ConstBufferSequence const& buffers,
ResponseDecorator const& decorator,
AcceptHandler&& handler)
{
static_assert(is_AsyncStream<next_layer_type>::value,
"AsyncStream requirements requirements not met");
static_assert(is_ConstBufferSequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
static_assert(detail::is_ResponseDecorator<
ResponseDecorator>::value,
"ResponseDecorator requirements not met");
beast::async_completion<AcceptHandler,
void(error_code)> completion{handler};
reset();
accept_op<ResponseDecorator, decltype(completion.handler)>{
completion.handler, *this, buffers, decorator};
return completion.result.get();
}
@ -461,11 +690,35 @@ async_accept(http::header<true, Fields> const& req,
reset();
response_op<decltype(completion.handler)>{
completion.handler, *this, req,
beast_asio_helpers::
&default_decorate_res, beast_asio_helpers::
is_continuation(completion.handler)};
return completion.result.get();
}
template<class NextLayer>
template<class Fields,
class ResponseDecorator, class AcceptHandler>
typename async_completion<AcceptHandler,
void(error_code)>::result_type
stream<NextLayer>::
async_accept_ex(http::header<true, Fields> const& req,
ResponseDecorator const& decorator, AcceptHandler&& handler)
{
static_assert(is_AsyncStream<next_layer_type>::value,
"AsyncStream requirements requirements not met");
static_assert(detail::is_ResponseDecorator<
ResponseDecorator>::value,
"ResponseDecorator requirements not met");
beast::async_completion<AcceptHandler,
void(error_code)> completion{handler};
reset();
response_op<decltype(completion.handler)>{
completion.handler, *this, req, decorator,
beast_asio_helpers::is_continuation(
completion.handler)};
return completion.result.get();
}
template<class NextLayer>
template<class Fields,
class ConstBufferSequence, class AcceptHandler>
@ -486,8 +739,37 @@ async_accept(http::header<true, Fields> const& req,
reset();
response_op<decltype(completion.handler)>{
completion.handler, *this, req, buffers,
beast_asio_helpers::
is_continuation(completion.handler)};
&default_decorate_res, beast_asio_helpers::
is_continuation(completion.handler)};
return completion.result.get();
}
template<class NextLayer>
template<class Fields, class ConstBufferSequence,
class ResponseDecorator, class AcceptHandler>
typename async_completion<AcceptHandler,
void(error_code)>::result_type
stream<NextLayer>::
async_accept_ex(http::header<true, Fields> const& req,
ConstBufferSequence const& buffers,
ResponseDecorator const& decorator,
AcceptHandler&& handler)
{
static_assert(is_AsyncStream<next_layer_type>::value,
"AsyncStream requirements requirements not met");
static_assert(is_ConstBufferSequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
static_assert(detail::is_ResponseDecorator<
ResponseDecorator>::value,
"ResponseDecorator requirements not met");
beast::async_completion<AcceptHandler,
void(error_code)> completion{handler};
reset();
response_op<decltype(completion.handler)>{
completion.handler, *this, req, buffers, decorator,
beast_asio_helpers::is_continuation(
completion.handler)};
return completion.result.get();
}

View File

@ -8,6 +8,7 @@
#ifndef BEAST_WEBSOCKET_IMPL_HANDSHAKE_IPP
#define BEAST_WEBSOCKET_IMPL_HANDSHAKE_IPP
#include <beast/websocket/detail/type_traits.hpp>
#include <beast/http/message.hpp>
#include <beast/http/read.hpp>
#include <beast/http/streambuf_body.hpp>
@ -33,18 +34,24 @@ class stream<NextLayer>::handshake_op
{
bool cont;
stream<NextLayer>& ws;
response_type* res_p;
std::string key;
http::request_header req;
http::response<http::streambuf_body> res;
request_type req;
response_type res;
int state = 0;
template<class Decorator>
data(Handler& handler, stream<NextLayer>& ws_,
boost::string_ref const& host,
boost::string_ref const& resource)
response_type* res_p_,
boost::string_ref const& host,
boost::string_ref const& resource,
Decorator const& decorator)
: cont(beast_asio_helpers::
is_continuation(handler))
, ws(ws_)
, req(ws.build_request(host, resource, key))
, res_p(res_p_)
, req(ws.build_request(key,
host, resource, decorator))
{
ws.reset();
}
@ -117,10 +124,14 @@ operator()(error_code ec, bool again)
d.state = 1;
// VFALCO Do we need the ability to move
// a message on the async_write?
//
pmd_read(
d.ws.pmd_config_, d.req.fields);
http::async_write(d.ws.stream_,
d.req, std::move(*this));
// TODO We don't need d.req now. Figure
// out a way to make it a parameter instead
// of a state variable to reduce footprint.
return;
}
@ -143,24 +154,94 @@ operator()(error_code ec, bool again)
}
}
}
if(d.res_p)
swap(d.res, *d.res_p);
d_.invoke(ec);
}
template<class NextLayer>
template<class HandshakeHandler>
typename async_completion<
HandshakeHandler, void(error_code)>::result_type
typename async_completion<HandshakeHandler,
void(error_code)>::result_type
stream<NextLayer>::
async_handshake(boost::string_ref const& host,
boost::string_ref const& resource, HandshakeHandler&& handler)
boost::string_ref const& resource,
HandshakeHandler&& handler)
{
static_assert(is_AsyncStream<next_layer_type>::value,
"AsyncStream requirements not met");
beast::async_completion<
HandshakeHandler, void(error_code)
> completion{handler};
beast::async_completion<HandshakeHandler,
void(error_code)> completion{handler};
handshake_op<decltype(completion.handler)>{
completion.handler, *this, host, resource};
completion.handler, *this, nullptr,
host, resource, &default_decorate_req};
return completion.result.get();
}
template<class NextLayer>
template<class HandshakeHandler>
typename async_completion<HandshakeHandler,
void(error_code)>::result_type
stream<NextLayer>::
async_handshake(response_type& res,
boost::string_ref const& host,
boost::string_ref const& resource,
HandshakeHandler&& handler)
{
static_assert(is_AsyncStream<next_layer_type>::value,
"AsyncStream requirements not met");
beast::async_completion<HandshakeHandler,
void(error_code)> completion{handler};
handshake_op<decltype(completion.handler)>{
completion.handler, *this, &res,
host, resource, &default_decorate_req};
return completion.result.get();
}
template<class NextLayer>
template<class RequestDecorator, class HandshakeHandler>
typename async_completion<HandshakeHandler,
void(error_code)>::result_type
stream<NextLayer>::
async_handshake_ex(boost::string_ref const& host,
boost::string_ref const& resource,
RequestDecorator const& decorator,
HandshakeHandler&& handler)
{
static_assert(is_AsyncStream<next_layer_type>::value,
"AsyncStream requirements not met");
static_assert(detail::is_RequestDecorator<
RequestDecorator>::value,
"RequestDecorator requirements not met");
beast::async_completion<HandshakeHandler,
void(error_code)> completion{handler};
handshake_op<decltype(completion.handler)>{
completion.handler, *this, nullptr,
host, resource, decorator};
return completion.result.get();
}
template<class NextLayer>
template<class RequestDecorator, class HandshakeHandler>
typename async_completion<HandshakeHandler,
void(error_code)>::result_type
stream<NextLayer>::
async_handshake_ex(response_type& res,
boost::string_ref const& host,
boost::string_ref const& resource,
RequestDecorator const& decorator,
HandshakeHandler&& handler)
{
static_assert(is_AsyncStream<next_layer_type>::value,
"AsyncStream requirements not met");
static_assert(detail::is_RequestDecorator<
RequestDecorator>::value,
"RequestDecorator requirements not met");
beast::async_completion<HandshakeHandler,
void(error_code)> completion{handler};
handshake_op<decltype(completion.handler)>{
completion.handler, *this, &res,
host, resource, decorator};
return completion.result.get();
}
@ -173,7 +254,62 @@ handshake(boost::string_ref const& host,
static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met");
error_code ec;
handshake(host, resource, ec);
handshake(
host, resource, ec);
if(ec)
throw system_error{ec};
}
template<class NextLayer>
void
stream<NextLayer>::
handshake(response_type& res,
boost::string_ref const& host,
boost::string_ref const& resource)
{
static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met");
error_code ec;
handshake(res, host, resource, ec);
if(ec)
throw system_error{ec};
}
template<class NextLayer>
template<class RequestDecorator>
void
stream<NextLayer>::
handshake_ex(boost::string_ref const& host,
boost::string_ref const& resource,
RequestDecorator const& decorator)
{
static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met");
static_assert(detail::is_RequestDecorator<
RequestDecorator>::value,
"RequestDecorator requirements not met");
error_code ec;
handshake_ex(host, resource, decorator, ec);
if(ec)
throw system_error{ec};
}
template<class NextLayer>
template<class RequestDecorator>
void
stream<NextLayer>::
handshake_ex(response_type& res,
boost::string_ref const& host,
boost::string_ref const& resource,
RequestDecorator const& decorator)
{
static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met");
static_assert(detail::is_RequestDecorator<
RequestDecorator>::value,
"RequestDecorator requirements not met");
error_code ec;
handshake_ex(res, host, resource, decorator, ec);
if(ec)
throw system_error{ec};
}
@ -186,21 +322,59 @@ handshake(boost::string_ref const& host,
{
static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met");
reset();
std::string key;
{
auto const req =
build_request(host, resource, key);
pmd_read(pmd_config_, req.fields);
http::write(stream_, req, ec);
}
if(ec)
return;
http::response<http::streambuf_body> res;
http::read(next_layer(), stream_.buffer(), res, ec);
if(ec)
return;
do_response(res, key, ec);
do_handshake(nullptr,
host, resource, &default_decorate_req, ec);
}
template<class NextLayer>
void
stream<NextLayer>::
handshake(response_type& res,
boost::string_ref const& host,
boost::string_ref const& resource,
error_code& ec)
{
static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met");
do_handshake(&res,
host, resource, &default_decorate_req, ec);
}
template<class NextLayer>
template<class RequestDecorator>
void
stream<NextLayer>::
handshake_ex(boost::string_ref const& host,
boost::string_ref const& resource,
RequestDecorator const& decorator,
error_code& ec)
{
static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met");
static_assert(detail::is_RequestDecorator<
RequestDecorator>::value,
"RequestDecorator requirements not met");
do_handshake(nullptr,
host, resource, decorator, ec);
}
template<class NextLayer>
template<class RequestDecorator>
void
stream<NextLayer>::
handshake_ex(response_type& res,
boost::string_ref const& host,
boost::string_ref const& resource,
RequestDecorator const& decorator,
error_code& ec)
{
static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met");
static_assert(detail::is_RequestDecorator<
RequestDecorator>::value,
"RequestDecorator requirements not met");
do_handshake(&res,
host, resource, decorator, ec);
}
//------------------------------------------------------------------------------

View File

@ -11,6 +11,7 @@
#include <beast/websocket/teardown.hpp>
#include <beast/websocket/detail/hybi13.hpp>
#include <beast/websocket/detail/pmd_extension.hpp>
#include <beast/version.hpp>
#include <beast/http/read.hpp>
#include <beast/http/write.hpp>
#include <beast/http/reason.hpp>
@ -83,9 +84,11 @@ reset()
}
template<class NextLayer>
template<class Decorator>
void
stream<NextLayer>::
do_accept(error_code& ec)
do_accept(
Decorator const& decorator, error_code& ec)
{
http::header_parser<true, http::fields> p;
auto const bytes_used = http::read_some(
@ -94,17 +97,17 @@ do_accept(error_code& ec)
return;
BOOST_ASSERT(p.got_header());
stream_.buffer().consume(bytes_used);
do_accept(p.get(), ec);
do_accept(p.get(), decorator, ec);
}
template<class NextLayer>
template<class Fields>
template<class Fields, class Decorator>
void
stream<NextLayer>::
do_accept(http::header<true, Fields> const& req,
error_code& ec)
Decorator const& decorator, error_code& ec)
{
auto const res = build_response(req);
auto const res = build_response(req, decorator);
http::write(stream_, res, ec);
if(ec)
return;
@ -120,12 +123,44 @@ do_accept(http::header<true, Fields> const& req,
}
template<class NextLayer>
http::request_header
template<class RequestDecorator>
void
stream<NextLayer>::
build_request(boost::string_ref const& host,
boost::string_ref const& resource, std::string& key)
do_handshake(response_type* res_p,
boost::string_ref const& host,
boost::string_ref const& resource,
RequestDecorator const& decorator,
error_code& ec)
{
http::request_header req;
response_type res;
reset();
std::string key;
{
auto const req = build_request(
key, host, resource, decorator);
pmd_read(pmd_config_, req.fields);
http::write(stream_, req, ec);
}
if(ec)
return;
http::read(next_layer(), stream_.buffer(), res, ec);
if(ec)
return;
do_response(res, key, ec);
if(res_p)
swap(res, *res_p);
}
template<class NextLayer>
template<class Decorator>
request_type
stream<NextLayer>::
build_request(std::string& key,
boost::string_ref const& host,
boost::string_ref const& resource,
Decorator const& decorator)
{
request_type req;
req.url = { resource.data(), resource.size() };
req.version = 11;
req.method = "GET";
@ -150,28 +185,42 @@ build_request(boost::string_ref const& host,
detail::pmd_write(
req.fields, config);
}
d_(req);
decorator(req);
if(! req.fields.exists("User-Agent"))
req.fields.insert("User-Agent",
std::string("Beast/") + BEAST_VERSION_STRING);
return req;
}
template<class NextLayer>
http::response_header
template<class Decorator>
response_type
stream<NextLayer>::
build_response(http::request_header const& req)
build_response(request_type const& req,
Decorator const& decorator)
{
auto const decorate =
[&decorator](response_type& res)
{
decorator(res);
if(! res.fields.exists("Server"))
res.fields.insert("Server",
std::string("Beast/") +
BEAST_VERSION_STRING);
};
auto err =
[&](std::string const& text)
{
http::response<http::string_body> res;
response_type res;
res.status = 400;
res.reason = http::reason_string(res.status);
res.version = req.version;
res.body = text;
d_(res);
prepare(res,
(is_keep_alive(req) && keep_alive_) ?
http::connection::keep_alive :
http::connection::close);
decorate(res);
return res;
};
if(req.version < 11)
@ -193,20 +242,20 @@ build_response(http::request_header const& req)
return err("Missing Sec-WebSocket-Version");
if(version != "13")
{
http::response<http::string_body> res;
response_type res;
res.status = 426;
res.reason = http::reason_string(res.status);
res.version = req.version;
res.fields.insert("Sec-WebSocket-Version", "13");
d_(res);
prepare(res,
(is_keep_alive(req) && keep_alive_) ?
http::connection::keep_alive :
http::connection::close);
decorate(res);
return res;
}
}
http::response_header res;
response_type res;
{
detail::pmd_offer offer;
detail::pmd_offer unused;
@ -225,8 +274,7 @@ build_response(http::request_header const& req)
res.fields.insert("Sec-WebSocket-Accept",
detail::make_sec_ws_accept(key));
}
res.fields.replace("Server", "Beast.WebSocket");
d_(res);
decorate(res);
return res;
}

View File

@ -10,7 +10,6 @@
#include <beast/config.hpp>
#include <beast/websocket/rfc6455.hpp>
#include <beast/websocket/detail/decorator.hpp>
#include <beast/core/detail/type_traits.hpp>
#include <algorithm>
#include <cstdint>
@ -59,56 +58,6 @@ struct auto_fragment
};
#endif
/** HTTP decorator option.
The decorator transforms the HTTP requests and responses used
when requesting or responding to the WebSocket Upgrade. This may
be used to set or change header fields. For example to set the
Server or User-Agent fields. The default setting applies no
transformation to the HTTP message.
The context in which the decorator is called depends on the
type of operation performed:
@li For synchronous operations, the implementation will call the
decorator before the operation unblocks.
@li For asynchronous operations, the implementation guarantees
that calls to the decorator will be made from the same implicit
or explicit strand used to call the asynchronous initiation
function.
The default setting is no decorator.
@note Objects of this type are used with
@ref beast::websocket::stream::set_option.
@par Example
Setting the decorator.
@code
struct identity
{
template<bool isRequest, class Body, class Fields>
void
operator()(http::message<isRequest, Body, Fields>& m)
{
if(isRequest)
m.fields.replace("User-Agent", "MyClient");
else
m.fields.replace("Server", "MyServer");
}
};
...
websocket::stream<ip::tcp::socket> ws(ios);
ws.set_option(decorate(identity{}));
@endcode
*/
#if BEAST_DOXYGEN
using decorate = implementation_defined;
#else
using decorate = detail::decorator_type;
#endif
/** Keep-alive option.
Determines if the connection is closed after a failed upgrade

File diff suppressed because it is too large Load Diff

View File

@ -111,33 +111,6 @@ public:
return false;
}
struct test_decorator
{
int& what;
test_decorator(test_decorator const&) = default;
test_decorator(int& what_)
: what(what_)
{
what = 0;
}
template<class Fields>
void
operator()(http::header<true, Fields>&) const
{
what |= 1;
}
template<class Fields>
void
operator()(http::header<false, Fields>&) const
{
what |= 2;
}
};
struct SyncClient
{
template<class NextLayer>
@ -173,6 +146,46 @@ public:
ws.accept(req, buffers);
}
template<class NextLayer, class Decorator>
void
accept_ex(stream<NextLayer>& ws,
Decorator const& d) const
{
ws.accept_ex(d);
}
template<class NextLayer,
class Buffers, class Decorator>
void
accept_ex(stream<NextLayer>& ws,
Buffers const& buffers,
Decorator const& d) const
{
ws.accept_ex(buffers, d);
}
template<class NextLayer,
class Fields, class Decorator>
void
accept_ex(stream<NextLayer>& ws,
http::header<true, Fields> const& req,
Decorator const& d) const
{
ws.accept_ex(req, d);
}
template<class NextLayer,
class Fields, class Buffers,
class Decorator>
void
accept_ex(stream<NextLayer>& ws,
http::header<true, Fields> const& req,
Buffers const& buffers,
Decorator const& d) const
{
ws.accept_ex(req, buffers, d);
}
template<class NextLayer>
void
handshake(stream<NextLayer>& ws,
@ -182,6 +195,37 @@ public:
ws.handshake(uri, path);
}
template<class NextLayer>
void
handshake(stream<NextLayer>& ws,
response_type& res,
boost::string_ref const& uri,
boost::string_ref const& path) const
{
ws.handshake(res, uri, path);
}
template<class NextLayer, class Decorator>
void
handshake_ex(stream<NextLayer>& ws,
boost::string_ref const& uri,
boost::string_ref const& path,
Decorator const& d) const
{
ws.handshake_ex(uri, path, d);
}
template<class NextLayer, class Decorator>
void
handshake_ex(stream<NextLayer>& ws,
response_type& res,
boost::string_ref const& uri,
boost::string_ref const& path,
Decorator const& d) const
{
ws.handshake_ex(res, uri, path, d);
}
template<class NextLayer>
void
ping(stream<NextLayer>& ws,
@ -301,14 +345,56 @@ public:
}
template<class NextLayer,
class Fields, class Buffers>
class Decorator>
void
accept(stream<NextLayer>& ws,
accept_ex(stream<NextLayer>& ws,
Decorator const& d) const
{
error_code ec;
ws.async_accept_ex(d, yield_[ec]);
if(ec)
throw system_error{ec};
}
template<class NextLayer,
class Buffers, class Decorator>
void
accept_ex(stream<NextLayer>& ws,
Buffers const& buffers,
Decorator const& d) const
{
error_code ec;
ws.async_accept_ex(buffers, d, yield_[ec]);
if(ec)
throw system_error{ec};
}
template<class NextLayer,
class Fields, class Decorator>
void
accept_ex(stream<NextLayer>& ws,
http::header<true, Fields> const& req,
Decorator const& d) const
{
error_code ec;
ws.async_accept_ex(req, d, yield_[ec]);
if(ec)
throw system_error{ec};
}
template<class NextLayer, class Fields,
class Buffers, class Decorator>
void
accept_ex(stream<NextLayer>& ws,
http::header<true, Fields> const& req,
Buffers const& buffers,
error_code& ec) const
Decorator const& d) const
{
ws.async_accept(req, buffers, yield_[ec]);
error_code ec;
ws.async_accept_ex(
req, buffers, d, yield_[ec]);
if(ec)
throw system_error{ec};
}
template<class NextLayer>
@ -318,7 +404,51 @@ public:
boost::string_ref const& path) const
{
error_code ec;
ws.async_handshake(uri, path, yield_[ec]);
ws.async_handshake(
uri, path, yield_[ec]);
if(ec)
throw system_error{ec};
}
template<class NextLayer>
void
handshake(stream<NextLayer>& ws,
response_type& res,
boost::string_ref const& uri,
boost::string_ref const& path) const
{
error_code ec;
ws.async_handshake(
res, uri, path, yield_[ec]);
if(ec)
throw system_error{ec};
}
template<class NextLayer, class Decorator>
void
handshake_ex(stream<NextLayer>& ws,
boost::string_ref const& uri,
boost::string_ref const& path,
Decorator const &d) const
{
error_code ec;
ws.async_handshake_ex(
uri, path, d, yield_[ec]);
if(ec)
throw system_error{ec};
}
template<class NextLayer, class Decorator>
void
handshake_ex(stream<NextLayer>& ws,
response_type& res,
boost::string_ref const& uri,
boost::string_ref const& path,
Decorator const &d) const
{
error_code ec;
ws.async_handshake_ex(
res, uri, path, d, yield_[ec]);
if(ec)
throw system_error{ec};
}
@ -436,6 +566,28 @@ public:
}
}
//--------------------------------------------------------------------------
class res_decorator
{
bool& b_;
public:
res_decorator(res_decorator const&) = default;
explicit
res_decorator(bool& b)
: b_(b)
{
}
void
operator()(response_type&) const
{
b_ = true;
}
};
template<class Client>
void
testAccept(Client const& c)
@ -460,7 +612,22 @@ public:
"\r\n"
, 20};
c.accept(ws);
//log << ws.next_layer().str << std::endl;
// VFALCO validate contents of ws.next_layer().str?
}
{
stream<test::fail_stream<
test::string_iostream>> ws{fc, ios_,
"GET / HTTP/1.1\r\n"
"Host: localhost\r\n"
"Upgrade: websocket\r\n"
"Connection: upgrade\r\n"
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
"Sec-WebSocket-Version: 13\r\n"
"\r\n"
, 20};
bool called = false;
c.accept_ex(ws, res_decorator{called});
BEAST_EXPECT(called);
}
// request in buffers
{
@ -476,6 +643,21 @@ public:
"\r\n"
));
}
{
stream<test::fail_stream<
test::string_ostream>> ws{fc, ios_};
bool called = false;
c.accept_ex(ws, sbuf(
"GET / HTTP/1.1\r\n"
"Host: localhost\r\n"
"Upgrade: websocket\r\n"
"Connection: upgrade\r\n"
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
"Sec-WebSocket-Version: 13\r\n"
"\r\n"),
res_decorator{called});
BEAST_EXPECT(called);
}
// request in buffers and stream
{
stream<test::fail_stream<
@ -491,9 +673,25 @@ public:
"Upgrade: websocket\r\n"
));
}
{
stream<test::fail_stream<
test::string_iostream>> ws{fc, ios_,
"Connection: upgrade\r\n"
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
"Sec-WebSocket-Version: 13\r\n"
"\r\n"
, 16};
bool called = false;
c.accept_ex(ws, sbuf(
"GET / HTTP/1.1\r\n"
"Host: localhost\r\n"
"Upgrade: websocket\r\n"),
res_decorator{called});
BEAST_EXPECT(called);
}
// request in message
{
http::request_header req;
request_type req;
req.method = "GET";
req.url = "/";
req.version = 11;
@ -506,9 +704,26 @@ public:
test::string_ostream>> ws{fc, ios_};
c.accept(ws, req);
}
{
request_type req;
req.method = "GET";
req.url = "/";
req.version = 11;
req.fields.insert("Host", "localhost");
req.fields.insert("Upgrade", "websocket");
req.fields.insert("Connection", "upgrade");
req.fields.insert("Sec-WebSocket-Key", "dGhlIHNhbXBsZSBub25jZQ==");
req.fields.insert("Sec-WebSocket-Version", "13");
stream<test::fail_stream<
test::string_ostream>> ws{fc, ios_};
bool called = false;
c.accept_ex(ws, req,
res_decorator{called});
BEAST_EXPECT(called);
}
// request in message, close frame in buffers
{
http::request_header req;
request_type req;
req.method = "GET";
req.url = "/";
req.version = 11;
@ -534,9 +749,39 @@ public:
throw;
}
}
{
request_type req;
req.method = "GET";
req.url = "/";
req.version = 11;
req.fields.insert("Host", "localhost");
req.fields.insert("Upgrade", "websocket");
req.fields.insert("Connection", "upgrade");
req.fields.insert("Sec-WebSocket-Key", "dGhlIHNhbXBsZSBub25jZQ==");
req.fields.insert("Sec-WebSocket-Version", "13");
stream<test::fail_stream<
test::string_ostream>> ws{fc, ios_};
bool called = false;
c.accept_ex(ws, req,
cbuf(0x88, 0x82, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x17),
res_decorator{called});
BEAST_EXPECT(called);
try
{
opcode op;
streambuf sb;
c.read(ws, op, sb);
fail("success", __FILE__, __LINE__);
}
catch(system_error const& e)
{
if(e.code() != websocket::error::closed)
throw;
}
}
// request in message, close frame in stream
{
http::request_header req;
request_type req;
req.method = "GET";
req.url = "/";
req.version = 11;
@ -564,7 +809,7 @@ public:
}
// request in message, close frame in stream and buffers
{
http::request_header req;
request_type req;
req.method = "GET";
req.url = "/";
req.version = 11;
@ -609,8 +854,10 @@ public:
}
catch(system_error const& e)
{
if(e.code() !=
websocket::error::handshake_failed)
if( e.code() !=
websocket::error::handshake_failed &&
e.code() !=
boost::asio::error::eof)
throw;
}
}
@ -635,6 +882,107 @@ public:
});
}
//--------------------------------------------------------------------------
class req_decorator
{
bool& b_;
public:
req_decorator(req_decorator const&) = default;
explicit
req_decorator(bool& b)
: b_(b)
{
}
void
operator()(request_type&) const
{
b_ = true;
}
};
template<class Client>
void
testHandshake(endpoint_type const& ep, Client const& c)
{
static std::size_t constexpr limit = 200;
std::size_t n;
for(n = 0; n < limit; ++n)
{
test::fail_counter fc{n};
try
{
// handshake
{
stream<test::fail_stream<
boost::asio::ip::tcp::socket>> ws{fc, ios_};
ws.next_layer().next_layer().connect(ep);
c.handshake(ws, "localhost", "/");
}
// handshake, response
{
stream<test::fail_stream<
boost::asio::ip::tcp::socket>> ws{fc, ios_};
ws.next_layer().next_layer().connect(ep);
response_type res;
c.handshake(ws, res, "localhost", "/");
// VFALCO validate res?
}
// handshake_ex
{
stream<test::fail_stream<
boost::asio::ip::tcp::socket>> ws{fc, ios_};
ws.next_layer().next_layer().connect(ep);
bool called = false;
c.handshake_ex(ws, "localhost", "/",
req_decorator{called});
BEAST_EXPECT(called);
}
// handshake_ex, response
{
stream<test::fail_stream<
boost::asio::ip::tcp::socket>> ws{fc, ios_};
ws.next_layer().next_layer().connect(ep);
bool called = false;
response_type res;
c.handshake_ex(ws, res, "localhost", "/",
req_decorator{called});
// VFALCO validate res?
BEAST_EXPECT(called);
}
}
catch(system_error const&)
{
continue;
}
break;
}
BEAST_EXPECT(n < limit);
}
void
testHandshake()
{
error_code ec;
::websocket::async_echo_server server{nullptr, 1};
auto const any = endpoint_type{
address_type::from_string("127.0.0.1"), 0};
server.open(any, ec);
BEAST_EXPECTS(! ec, ec.message());
auto const ep = server.local_endpoint();
testHandshake(ep, SyncClient{});
yield_to(
[&](yield_context yield)
{
testHandshake(ep, AsyncClient{yield});
});
}
//--------------------------------------------------------------------------
void testBadHandshakes()
{
auto const check =
@ -823,24 +1171,6 @@ public:
);
}
void
testDecorator(endpoint_type const& ep)
{
error_code ec;
socket_type sock{ios_};
sock.connect(ep, ec);
if(! BEAST_EXPECTS(! ec, ec.message()))
return;
stream<socket_type&> ws{sock};
int what;
ws.set_option(decorate(test_decorator{what}));
BEAST_EXPECT(what == 0);
ws.handshake("localhost", "/", ec);
if(! BEAST_EXPECTS(! ec, ec.message()))
return;
BEAST_EXPECT(what == 1);
}
void testMask(endpoint_type const& ep,
yield_context do_yield)
{
@ -1525,6 +1855,7 @@ public:
testOptions();
testAccept();
testHandshake();
testBadHandshakes();
testBadResponses();
@ -1539,7 +1870,6 @@ public:
server.open(any, ec);
BEAST_EXPECTS(! ec, ec.message());
auto const ep = server.local_endpoint();
testDecorator(ep);
//testInvokable1(ep);
testInvokable2(ep);
testInvokable3(ep);

View File

@ -38,25 +38,6 @@ public:
using endpoint_type = boost::asio::ip::tcp::endpoint;
private:
struct identity
{
template<class Body, class Fields>
void
operator()(beast::http::message<
true, Body, Fields>& req) const
{
req.fields.replace("User-Agent", "async_echo_client");
}
template<class Body, class Fields>
void
operator()(beast::http::message<
false, Body, Fields>& resp) const
{
resp.fields.replace("Server", "async_echo_server");
}
};
/** A container of type-erased option setters.
*/
template<class NextLayer>
@ -159,8 +140,6 @@ public:
, acceptor_(ios_)
, work_(ios_)
{
opts_.set_option(
beast::websocket::decorate(identity{}));
thread_.reserve(threads);
for(std::size_t i = 0; i < threads; ++i)
thread_.emplace_back(
@ -282,7 +261,13 @@ private:
void run()
{
auto& d = *d_;
d.ws.async_accept(std::move(*this));
d.ws.async_accept_ex(
[](beast::websocket::response_type& res)
{
res.fields.insert(
"Server", "async_echo_server");
},
std::move(*this));
}
template<class DynamicBuffer, std::size_t N>

View File

@ -38,25 +38,6 @@ public:
using socket_type = boost::asio::ip::tcp::socket;
private:
struct identity
{
template<class Body, class Fields>
void
operator()(beast::http::message<
true, Body, Fields>& req) const
{
req.fields.replace("User-Agent", "sync_echo_client");
}
template<class Body, class Fields>
void
operator()(beast::http::message<
false, Body, Fields>& resp) const
{
resp.fields.replace("Server", "sync_echo_server");
}
};
/** A container of type-erased option setters.
*/
template<class NextLayer>
@ -151,8 +132,6 @@ public:
, sock_(ios_)
, acceptor_(ios_)
{
opts_.set_option(
beast::websocket::decorate(identity{}));
}
/** Destructor.
@ -312,7 +291,13 @@ private:
socket_type> ws{std::move(sock)};
opts_.set_options(ws);
error_code ec;
ws.accept(ec);
ws.accept_ex(
[](beast::websocket::response_type& res)
{
res.fields.insert(
"Server", "sync_echo_server");
},
ec);
if(ec)
{
fail("accept", ec, id, ep);