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-05-05 10:01:17 -07:00
parent 235fe68acb
commit 2c17d04a3a
16 changed files with 2255 additions and 515 deletions
+316 -34
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();
}
+202 -28
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);
}
//------------------------------------------------------------------------------
+67 -19
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;
}