websocket accept refactoring (API Change):

* stream overloads of accept which take both a message
  and a buffer sequence are removed.

Actions Required:

* Do not call websocket accept overloads which take
  both a message and a buffer sequence, as it is
  illegal per rfc6455.
This commit is contained in:
Vinnie Falco
2017-08-16 19:25:02 -07:00
parent 7a96dc4e11
commit 7e815435f6
4 changed files with 481 additions and 916 deletions

View File

@ -4,6 +4,16 @@ WebSocket:
* Fix async_read_some handler signature
API Changes:
* websocket accept refactoring
Actions Required:
* Do not call websocket accept overloads which take
both a message and a buffer sequence, as it is
illegal per rfc6455.
--------------------------------------------------------------------------------
Version 108:

View File

@ -19,6 +19,7 @@
#include <boost/beast/core/buffer_prefix.hpp>
#include <boost/beast/core/handler_ptr.hpp>
#include <boost/beast/core/detail/type_traits.hpp>
#include <boost/asio/coroutine.hpp>
#include <boost/asio/handler_alloc_hook.hpp>
#include <boost/asio/handler_continuation_hook.hpp>
#include <boost/asio/handler_invoke_hook.hpp>
@ -35,12 +36,12 @@ namespace websocket {
template<class NextLayer>
template<class Handler>
class stream<NextLayer>::response_op
: public boost::asio::coroutine
{
struct data
{
stream<NextLayer>& ws;
response_type res;
int step = 0;
template<class Body, class Allocator, class Decorator>
data(Handler&, stream<NextLayer>& ws_, http::request<
@ -66,10 +67,7 @@ public:
{
}
template<class Buffers>
void operator()(Buffers const& buffers);
void operator()(error_code ec);
void operator()(error_code ec = {});
friend
void* asio_handler_allocate(
@ -90,10 +88,11 @@ public:
}
friend
bool asio_handler_is_continuation(response_op*)
bool asio_handler_is_continuation(response_op* op)
{
// VFALCO This will go away in Net-TS
return false;
using boost::asio::asio_handler_is_continuation;
return asio_handler_is_continuation(
std::addressof(op->d_.handler()));
}
template<class Function>
@ -106,37 +105,6 @@ public:
}
};
template<class NextLayer>
template<class Handler>
template<class Buffers>
void
stream<NextLayer>::
response_op<Handler>::
operator()(Buffers const& buffers)
{
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
auto& d = *d_;
error_code ec;
boost::optional<typename
static_buffer_base::mutable_buffers_type> mb;
auto const len = buffer_size(buffers);
try
{
mb.emplace(d.ws.rd_.buf.prepare(len));
}
catch(std::length_error const&)
{
d.step = 2;
ec = error::buffer_overflow;
return d.ws.get_io_service().post(
bind_handler(std::move(*this), ec));
}
d.ws.rd_.buf.commit(
buffer_copy(*mb, buffers));
(*this)(ec);
}
template<class NextLayer>
template<class Handler>
void
@ -145,18 +113,13 @@ response_op<Handler>::
operator()(error_code ec)
{
auto& d = *d_;
switch(d.step)
BOOST_ASIO_CORO_REENTER(*this)
{
case 0:
// send response
d.step = 1;
// Send response
BOOST_ASIO_CORO_YIELD
http::async_write(d.ws.next_layer(),
d.res, std::move(*this));
return;
// sent response
case 1:
if(d.res.result() !=
if(! ec && d.res.result() !=
http::status::switching_protocols)
ec = error::handshake_failed;
if(! ec)
@ -164,13 +127,8 @@ operator()(error_code ec)
pmd_read(d.ws.pmd_config_, d.res);
d.ws.open(role_type::server);
}
break;
// call handler
case 2:
break;
d_.invoke(ec);
}
d_.invoke(ec);
}
//------------------------------------------------------------------------------
@ -180,13 +138,13 @@ operator()(error_code ec)
template<class NextLayer>
template<class Decorator, class Handler>
class stream<NextLayer>::accept_op
: public boost::asio::coroutine
{
struct data
{
stream<NextLayer>& ws;
Decorator decorator;
http::request_parser<http::empty_body> p;
int step = 0;
data(Handler&, stream<NextLayer>& ws_,
Decorator const& decorator_)
: ws(ws_)
@ -210,9 +168,9 @@ public:
}
template<class Buffers>
void operator()(Buffers const& buffers);
void run(Buffers const& buffers);
void operator()(error_code ec);
void operator()(error_code ec = {});
friend
void* asio_handler_allocate(
@ -256,7 +214,7 @@ template<class Buffers>
void
stream<NextLayer>::
accept_op<Decorator, Handler>::
operator()(Buffers const& buffers)
run(Buffers const& buffers)
{
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
@ -271,10 +229,8 @@ operator()(Buffers const& buffers)
}
catch(std::length_error const&)
{
d.step = 2;
ec = error::buffer_overflow;
return d.ws.get_io_service().post(
bind_handler(std::move(*this), ec));
return (*this)(ec);
}
d.ws.rd_.buf.commit(
buffer_copy(*mb, buffers));
@ -289,40 +245,45 @@ accept_op<Decorator, Handler>::
operator()(error_code ec)
{
auto& d = *d_;
switch(d.step)
BOOST_ASIO_CORO_REENTER(*this)
{
case 0:
d.step = 1;
return http::async_read(
d.ws.next_layer(), d.ws.rd_.buf,
d.p, std::move(*this));
case 1:
{
if(ec == http::error::end_of_stream)
ec = error::closed;
if(ec)
break;
// Arguments from our step must be
// moved to the stack before releasing
// the handler.
auto& ws = d.ws;
auto const req = d.p.release();
auto const decorator = d.decorator;
#if 1
return response_op<Handler>{
d_.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, d_.release_handler());
#endif
{
BOOST_ASIO_CORO_YIELD
d.ws.get_io_service().post(
bind_handler(std::move(*this), ec));
}
else
{
BOOST_ASIO_CORO_YIELD
http::async_read(
d.ws.next_layer(), d.ws.rd_.buf,
d.p, std::move(*this));
if(ec == http::error::end_of_stream)
ec = error::closed;
if(! ec)
{
// Arguments from our step must be
// moved to the stack before releasing
// the handler.
auto& ws = d.ws;
auto const req = d.p.release();
auto const decorator = d.decorator;
#if 1
return response_op<Handler>{
d_.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, d_.release_handler());
#endif
}
}
d_.invoke(ec);
}
}
d_.invoke(ec);
}
//------------------------------------------------------------------------------
@ -403,11 +364,13 @@ accept(ConstBufferSequence const& buffers)
template<class NextLayer>
template<
class ConstBufferSequence, class ResponseDecorator>
class ConstBufferSequence,
class ResponseDecorator>
typename std::enable_if<! http::detail::is_header<
ConstBufferSequence>::value>::type
stream<NextLayer>::
accept_ex(ConstBufferSequence const& buffers,
accept_ex(
ConstBufferSequence const& buffers,
ResponseDecorator const &decorator)
{
static_assert(is_sync_stream<next_layer_type>::value,
@ -429,30 +392,45 @@ template<class ConstBufferSequence>
typename std::enable_if<! http::detail::is_header<
ConstBufferSequence>::value>::type
stream<NextLayer>::
accept(ConstBufferSequence const& buffers, error_code& ec)
accept(
ConstBufferSequence const& buffers, error_code& ec)
{
static_assert(is_sync_stream<next_layer_type>::value,
"SyncStream requirements not met");
static_assert(is_const_buffer_sequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
reset();
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
rd_.buf.commit(buffer_copy(
rd_.buf.prepare(
buffer_size(buffers)), buffers));
reset();
boost::optional<typename
static_buffer_base::mutable_buffers_type> mb;
try
{
mb.emplace(rd_.buf.prepare(
buffer_size(buffers)));
}
catch(std::length_error const&)
{
ec = error::buffer_overflow;
return;
}
rd_.buf.commit(
buffer_copy(*mb, buffers));
do_accept(&default_decorate_res, ec);
}
template<class NextLayer>
template<
class ConstBufferSequence, class ResponseDecorator>
class ConstBufferSequence,
class ResponseDecorator>
typename std::enable_if<! http::detail::is_header<
ConstBufferSequence>::value>::type
stream<NextLayer>::
accept_ex(ConstBufferSequence const& buffers,
ResponseDecorator const& decorator, error_code& ec)
accept_ex(
ConstBufferSequence const& buffers,
ResponseDecorator const& decorator,
error_code& ec)
{
static_assert(is_sync_stream<next_layer_type>::value,
"SyncStream requirements not met");
@ -462,12 +440,22 @@ accept_ex(ConstBufferSequence const& buffers,
static_assert(is_const_buffer_sequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
reset();
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
rd_.buf.commit(buffer_copy(
rd_.buf.prepare(
buffer_size(buffers)), buffers));
reset();
boost::optional<typename
static_buffer_base::mutable_buffers_type> mb;
try
{
mb.emplace(rd_.buf.prepare(
buffer_size(buffers)));
}
catch(std::length_error const&)
{
ec = error::buffer_overflow;
return;
}
rd_.buf.commit(buffer_copy(*mb, buffers));
do_accept(decorator, ec);
}
@ -475,8 +463,9 @@ template<class NextLayer>
template<class Body, class Allocator>
void
stream<NextLayer>::
accept(http::request<Body,
http::basic_fields<Allocator>> const& req)
accept(
http::request<Body,
http::basic_fields<Allocator>> const& req)
{
static_assert(is_sync_stream<next_layer_type>::value,
"SyncStream requirements not met");
@ -487,12 +476,14 @@ accept(http::request<Body,
}
template<class NextLayer>
template<class Body,
class Allocator, class ResponseDecorator>
template<
class Body, class Allocator,
class ResponseDecorator>
void
stream<NextLayer>::
accept_ex(http::request<Body,
http::basic_fields<Allocator>> const& req,
accept_ex(
http::request<Body,
http::basic_fields<Allocator>> const& req,
ResponseDecorator const& decorator)
{
static_assert(is_sync_stream<next_layer_type>::value,
@ -510,9 +501,10 @@ template<class NextLayer>
template<class Body, class Allocator>
void
stream<NextLayer>::
accept(http::request<Body,
http::basic_fields<Allocator>> const& req,
error_code& ec)
accept(
http::request<Body,
http::basic_fields<Allocator>> const& req,
error_code& ec)
{
static_assert(is_sync_stream<next_layer_type>::value,
"SyncStream requirements not met");
@ -521,13 +513,16 @@ accept(http::request<Body,
}
template<class NextLayer>
template<class Body, class Allocator,
template<
class Body, class Allocator,
class ResponseDecorator>
void
stream<NextLayer>::
accept_ex(http::request<Body,
http::basic_fields<Allocator>> const& req,
ResponseDecorator const& decorator, error_code& ec)
accept_ex(
http::request<Body,
http::basic_fields<Allocator>> const& req,
ResponseDecorator const& decorator,
error_code& ec)
{
static_assert(is_sync_stream<next_layer_type>::value,
"SyncStream requirements not met");
@ -538,101 +533,6 @@ accept_ex(http::request<Body,
do_accept(req, decorator, ec);
}
template<class NextLayer>
template<class Body, class Allocator,
class ConstBufferSequence>
void
stream<NextLayer>::
accept(http::request<Body,
http::basic_fields<Allocator>> const& req,
ConstBufferSequence const& buffers)
{
static_assert(is_sync_stream<next_layer_type>::value,
"SyncStream requirements not met");
static_assert(is_const_buffer_sequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
error_code ec;
accept(req, buffers, ec);
if(ec)
BOOST_THROW_EXCEPTION(system_error{ec});
}
template<class NextLayer>
template<class Body, class Allocator,
class ConstBufferSequence, class ResponseDecorator>
void
stream<NextLayer>::
accept_ex(http::request<Body,
http::basic_fields<Allocator>> const& req,
ConstBufferSequence const& buffers,
ResponseDecorator const& decorator)
{
static_assert(is_sync_stream<next_layer_type>::value,
"SyncStream requirements not met");
static_assert(is_const_buffer_sequence<
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)
BOOST_THROW_EXCEPTION(system_error{ec});
}
template<class NextLayer>
template<class Body, class Allocator,
class ConstBufferSequence>
void
stream<NextLayer>::
accept(http::request<Body,
http::basic_fields<Allocator>> const& req,
ConstBufferSequence const& buffers, error_code& ec)
{
static_assert(is_sync_stream<next_layer_type>::value,
"SyncStream requirements not met");
static_assert(is_const_buffer_sequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
reset();
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
rd_.buf.commit(buffer_copy(
rd_.buf.prepare(
buffer_size(buffers)), buffers));
do_accept(req, &default_decorate_res, ec);
}
template<class NextLayer>
template<class Body, class Allocator,
class ConstBufferSequence, class ResponseDecorator>
void
stream<NextLayer>::
accept_ex(http::request<Body,
http::basic_fields<Allocator>> const& req,
ConstBufferSequence const& buffers,
ResponseDecorator const& decorator,
error_code& ec)
{
static_assert(is_sync_stream<next_layer_type>::value,
"SyncStream requirements not met");
static_assert(is_const_buffer_sequence<
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;
rd_.buf.commit(buffer_copy(
rd_.buf.prepare(
buffer_size(buffers)), buffers));
do_accept(req, decorator, ec);
}
//------------------------------------------------------------------------------
template<class NextLayer>
@ -709,7 +609,7 @@ async_accept(
handler_type<AcceptHandler, void(error_code)>>{
init.completion_handler,
*this,
&default_decorate_res}(buffers);
&default_decorate_res}.run(buffers);
return init.result.get();
}
@ -743,7 +643,7 @@ async_accept_ex(
handler_type<AcceptHandler, void(error_code)>>{
init.completion_handler,
*this,
decorator}(buffers);
decorator}.run(buffers);
return init.result.get();
}
@ -768,7 +668,7 @@ async_accept(
init.completion_handler,
*this,
req,
&default_decorate_res}({});
&default_decorate_res}();
return init.result.get();
}
@ -798,73 +698,7 @@ async_accept_ex(
init.completion_handler,
*this,
req,
decorator}({});
return init.result.get();
}
template<class NextLayer>
template<
class Body, class Allocator,
class ConstBufferSequence,
class AcceptHandler>
async_return_type<AcceptHandler, void(error_code)>
stream<NextLayer>::
async_accept(
http::request<Body, http::basic_fields<Allocator>> const& req,
ConstBufferSequence const& buffers,
AcceptHandler&& handler)
{
static_assert(is_async_stream<next_layer_type>::value,
"AsyncStream requirements requirements not met");
static_assert(is_const_buffer_sequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
async_completion<AcceptHandler,
void(error_code)> init{handler};
reset();
using boost::asio::asio_handler_is_continuation;
response_op<
handler_type<AcceptHandler, void(error_code)>>{
init.completion_handler,
*this,
req,
&default_decorate_res}(buffers);
return init.result.get();
}
template<class NextLayer>
template<
class Body, class Allocator,
class ConstBufferSequence,
class ResponseDecorator,
class AcceptHandler>
async_return_type<
AcceptHandler, void(error_code)>
stream<NextLayer>::
async_accept_ex(http::request<Body,
http::basic_fields<Allocator>> const& req,
ConstBufferSequence const& buffers,
ResponseDecorator const& decorator,
AcceptHandler&& handler)
{
static_assert(is_async_stream<next_layer_type>::value,
"AsyncStream requirements requirements not met");
static_assert(is_const_buffer_sequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
static_assert(detail::is_ResponseDecorator<
ResponseDecorator>::value,
"ResponseDecorator requirements not met");
async_completion<AcceptHandler,
void(error_code)> init{handler};
reset();
using boost::asio::asio_handler_is_continuation;
response_op<
handler_type<AcceptHandler, void(error_code)>>{
init.completion_handler,
*this,
req,
decorator}(buffers);
decorator}();
return init.result.get();
}

View File

@ -1915,212 +1915,6 @@ public:
ResponseDecorator const& decorator,
error_code& ec);
/** Respond to a WebSocket HTTP Upgrade request
This function is used to synchronously send the HTTP response
to an HTTP request possibly containing a WebSocket Upgrade.
The call blocks until one of the following conditions is true:
@li The response finishes sending.
@li An error occurs on the stream.
This function is implemented in terms of one or more calls to
the next layer's `read_some` and `write_some` functions.
If the stream receives a valid HTTP WebSocket Upgrade request,
an HTTP response is sent back indicating a successful upgrade.
When this call returns, the stream is then ready to send and
receive WebSocket protocol frames and messages.
If the HTTP Upgrade request is invalid or cannot be satisfied,
an HTTP response is sent indicating the reason and status code
(typically 400, "Bad Request"). This counts as a failure.
The implementation uses fixed size internal storage to
copy the buffer. If the buffer is too large, the error
@ref error::buffer_overflow will be indicated. Applications
that wish to supply larger buffers should wrap the next layer
in a @ref buffered_read_stream and store the buffer contents in
the wrapper before calling accept.
@param req An object containing the HTTP Upgrade request.
Ownership is not transferred, the implementation will not
access this object from other threads.
@param buffers Caller provided data that has already been
received on the stream. This must not include the octets
corresponding to the HTTP Upgrade request. The implementation
will copy the caller provided data before the function returns.
@throws system_error Thrown on failure.
*/
template<class Body, class Allocator,
class ConstBufferSequence>
void
accept(http::request<Body,
http::basic_fields<Allocator>> const& req,
ConstBufferSequence const& buffers);
/** Respond to a WebSocket HTTP Upgrade request
This function is used to synchronously send the HTTP response
to an HTTP request possibly containing a WebSocket Upgrade.
The call blocks until one of the following conditions is true:
@li The response finishes sending.
@li An error occurs on the stream.
This function is implemented in terms of one or more calls to
the next layer's `read_some` and `write_some` functions.
If the stream receives a valid HTTP WebSocket Upgrade request,
an HTTP response is sent back indicating a successful upgrade.
When this call returns, the stream is then ready to send and
receive WebSocket protocol frames and messages.
If the HTTP Upgrade request is invalid or cannot be satisfied,
an HTTP response is sent indicating the reason and status code
(typically 400, "Bad Request"). This counts as a failure.
The implementation uses fixed size internal storage to
copy the buffer. If the buffer is too large, the error
@ref error::buffer_overflow will be indicated. Applications
that wish to supply larger buffers should wrap the next layer
in a @ref buffered_read_stream and store the buffer contents in
the wrapper before calling accept.
@param req An object containing the HTTP Upgrade request.
Ownership is not transferred, the implementation will not
access this object from other threads.
@param buffers Caller provided data that has already been
received on the stream. This must not include the octets
corresponding to the HTTP Upgrade request. The implementation
will copy the caller provided data before the function returns.
@param decorator A function object which will be called to modify
the HTTP response object delivered by the implementation. This
could be used to set the Server field, subprotocols, or other
application or HTTP specific fields. The object will be called
with this equivalent signature:
@code void decorator(
response_type& res
); @endcode
@throws system_error Thrown on failure.
*/
template<class Body, class Allocator,
class ConstBufferSequence, class ResponseDecorator>
void
accept_ex(http::request<Body,
http::basic_fields<Allocator>> const& req,
ConstBufferSequence const& buffers,
ResponseDecorator const& decorator);
/** Respond to a WebSocket HTTP Upgrade request
This function is used to synchronously send the HTTP response
to an HTTP request possibly containing a WebSocket Upgrade.
The call blocks until one of the following conditions is true:
@li The response finishes sending.
@li An error occurs on the stream.
This function is implemented in terms of one or more calls to
the next layer's `read_some` and `write_some` functions.
If the stream receives a valid HTTP WebSocket Upgrade request,
an HTTP response is sent back indicating a successful upgrade.
When this call returns, the stream is then ready to send and
receive WebSocket protocol frames and messages.
If the HTTP Upgrade request is invalid or cannot be satisfied,
an HTTP response is sent indicating the reason and status code
(typically 400, "Bad Request"). This counts as a failure.
The implementation uses fixed size internal storage to
copy the buffer. If the buffer is too large, the error
@ref error::buffer_overflow will be indicated. Applications
that wish to supply larger buffers should wrap the next layer
in a @ref buffered_read_stream and store the buffer contents in
the wrapper before calling accept.
@param req An object containing the HTTP Upgrade request.
Ownership is not transferred, the implementation will not
access this object from other threads.
@param buffers Caller provided data that has already been
received on the stream. This must not include the octets
corresponding to the HTTP Upgrade request. The implementation
will copy the caller provided data before the function returns.
@param ec Set to indicate what error occurred, if any.
*/
template<class Body, class Allocator,
class ConstBufferSequence>
void
accept(http::request<Body,
http::basic_fields<Allocator>> const& req,
ConstBufferSequence const& buffers,
error_code& ec);
/** Respond to a WebSocket HTTP Upgrade request
This function is used to synchronously send the HTTP response
to an HTTP request possibly containing a WebSocket Upgrade.
The call blocks until one of the following conditions is true:
@li The response finishes sending.
@li An error occurs on the stream.
This function is implemented in terms of one or more calls to
the next layer's `read_some` and `write_some` functions.
If the stream receives a valid HTTP WebSocket Upgrade request,
an HTTP response is sent back indicating a successful upgrade.
When this call returns, the stream is then ready to send and
receive WebSocket protocol frames and messages.
If the HTTP Upgrade request is invalid or cannot be satisfied,
an HTTP response is sent indicating the reason and status code
(typically 400, "Bad Request"). This counts as a failure.
The implementation uses fixed size internal storage to
copy the buffer. If the buffer is too large, the error
@ref error::buffer_overflow will be indicated. Applications
that wish to supply larger buffers should wrap the next layer
in a @ref buffered_read_stream and store the buffer contents in
the wrapper before calling accept.
@param req An object containing the HTTP Upgrade request.
Ownership is not transferred, the implementation will not
access this object from other threads.
@param buffers Caller provided data that has already been
received on the stream. This must not include the octets
corresponding to the HTTP Upgrade request. The implementation
will copy the caller provided data before the function returns.
@param decorator A function object which will be called to modify
the HTTP response object delivered by the implementation. This
could be used to set the Server field, subprotocols, or other
application or HTTP specific fields. The object will be called
with this equivalent signature:
@code void decorator(
response_type& res
); @endcode
@param ec Set to indicate what error occurred, if any.
*/
template<class Body, class Allocator,
class ConstBufferSequence, class ResponseDecorator>
void
accept_ex(http::request<Body,
http::basic_fields<Allocator>> const& req,
ConstBufferSequence const& buffers,
ResponseDecorator const& decorator,
error_code& ec);
/** Start reading and responding to a WebSocket HTTP Upgrade request.
This function is used to asynchronously read an HTTP WebSocket
@ -2233,202 +2027,8 @@ public:
this function. Invocation of the handler will be performed in a
manner equivalent to using `boost::asio::io_service::post`.
*/
template<class ResponseDecorator, class AcceptHandler>
#if BOOST_BEAST_DOXYGEN
void_or_deduced
#else
async_return_type<
AcceptHandler, void(error_code)>
#endif
async_accept_ex(ResponseDecorator const& decorator,
AcceptHandler&& handler);
/** Start reading and responding to a WebSocket HTTP Upgrade request.
This function is used to asynchronously read an HTTP WebSocket
Upgrade request and send the HTTP response. The function call
always returns immediately. The asynchronous operation will
continue until one of the following conditions is true:
@li The request is received and the response finishes sending.
@li An error occurs on the stream.
This operation is implemented in terms of one or more calls to
the next layer's `async_read_some` and `async_write_some`
functions, and is known as a <em>composed operation</em>. The
program must ensure that the stream performs no other
asynchronous operations until this operation completes.
If the stream receives a valid HTTP WebSocket Upgrade request,
an HTTP response is sent back indicating a successful upgrade.
When the completion handler is invoked, the stream is then
ready to send and receive WebSocket protocol frames and
messages.
If the HTTP Upgrade request is invalid or cannot be satisfied,
an HTTP response is sent indicating the reason and status code
(typically 400, "Bad Request"). This counts as a failure, and
the completion handler will be invoked with a suitable error
code set.
The implementation uses fixed size internal storage to
receive the request. If the request is too large, the error
@ref error::buffer_overflow will be indicated. Applications
that wish to receive larger requests should first read the
request using their own buffer and a suitable overload of
@ref http::read or @ref http::async_read, then call @ref accept
or @ref async_accept with the request.
@param buffers Caller provided data that has already been
received on the stream. This may be used for implementations
allowing multiple protocols on the same stream. The
buffered data will first be applied to the handshake, and
then to received WebSocket frames. The implementation will
copy the caller provided data before the function returns.
@param handler The handler to be called when the request
completes. Copies will be made of the handler as required. The
equivalent function signature of the handler must be:
@code void handler(
error_code const& ec // Result of operation
); @endcode
Regardless of whether the asynchronous operation completes
immediately or not, the handler will not be invoked from within
this function. Invocation of the handler will be performed in a
manner equivalent to using `boost::asio::io_service::post`.
*/
template<class ConstBufferSequence, class AcceptHandler>
#if BOOST_BEAST_DOXYGEN
void_or_deduced
#else
typename std::enable_if<
! http::detail::is_header<ConstBufferSequence>::value,
async_return_type<AcceptHandler, void(error_code)>>::type
#endif
async_accept(ConstBufferSequence const& buffers,
AcceptHandler&& handler);
/** Start reading and responding to a WebSocket HTTP Upgrade request.
This function is used to asynchronously read an HTTP WebSocket
Upgrade request and send the HTTP response. The function call
always returns immediately. The asynchronous operation will
continue until one of the following conditions is true:
@li The request is received and the response finishes sending.
@li An error occurs on the stream.
This operation is implemented in terms of one or more calls to
the next layer's `async_read_some` and `async_write_some`
functions, and is known as a <em>composed operation</em>. The
program must ensure that the stream performs no other
asynchronous operations until this operation completes.
If the stream receives a valid HTTP WebSocket Upgrade request,
an HTTP response is sent back indicating a successful upgrade.
When the completion handler is invoked, the stream is then
ready to send and receive WebSocket protocol frames and
messages.
If the HTTP Upgrade request is invalid or cannot be satisfied,
an HTTP response is sent indicating the reason and status code
(typically 400, "Bad Request"). This counts as a failure, and
the completion handler will be invoked with a suitable error
code set.
The implementation uses fixed size internal storage to
receive the request. If the request is too large, the error
@ref error::buffer_overflow will be indicated. Applications
that wish to receive larger requests should first read the
request using their own buffer and a suitable overload of
@ref http::read or @ref http::async_read, then call @ref accept
or @ref async_accept with the request.
@param buffers Caller provided data that has already been
received on the stream. This may be used for implementations
allowing multiple protocols on the same stream. The
buffered data will first be applied to the handshake, and
then to received WebSocket frames. The implementation will
copy the caller provided data before the function returns.
@param decorator A function object which will be called to modify
the HTTP response object delivered by the implementation. This
could be used to set the Server field, subprotocols, or other
application or HTTP specific fields. The object will be called
with this equivalent signature:
@code void decorator(
response_type& res
); @endcode
@param handler The handler to be called when the request
completes. Copies will be made of the handler as required. The
equivalent function signature of the handler must be:
@code void handler(
error_code const& ec // Result of operation
); @endcode
Regardless of whether the asynchronous operation completes
immediately or not, the handler will not be invoked from within
this function. Invocation of the handler will be performed in a
manner equivalent to using `boost::asio::io_service::post`.
*/
template<class ConstBufferSequence,
class ResponseDecorator, class AcceptHandler>
#if BOOST_BEAST_DOXYGEN
void_or_deduced
#else
typename std::enable_if<
! http::detail::is_header<ConstBufferSequence>::value,
async_return_type<AcceptHandler, void(error_code)>>::type
#endif
async_accept_ex(ConstBufferSequence const& buffers,
ResponseDecorator const& decorator,
AcceptHandler&& handler);
/** Start responding to a WebSocket HTTP Upgrade request.
This function is used to asynchronously send the HTTP response
to an HTTP request possibly containing a WebSocket Upgrade
request. The function call always returns immediately. The
asynchronous operation will continue until one of the following
conditions is true:
@li The response finishes sending.
@li An error occurs on the stream.
This operation is implemented in terms of one or more calls to
the next layer's `async_write_some` functions, and is known as
a <em>composed operation</em>. The program must ensure that the
stream performs no other operations until this operation
completes.
If the stream receives a valid HTTP WebSocket Upgrade request,
an HTTP response is sent back indicating a successful upgrade.
When the completion handler is invoked, the stream is then
ready to send and receive WebSocket protocol frames and
messages.
If the HTTP Upgrade request is invalid or cannot be satisfied,
an HTTP response is sent indicating the reason and status code
(typically 400, "Bad Request"). This counts as a failure, and
the completion handler will be invoked with a suitable error
code set.
@param req An object containing the HTTP Upgrade request.
Ownership is not transferred, the implementation will not access
this object from other threads.
@param handler The handler to be called when the request
completes. Copies will be made of the handler as required. The
equivalent function signature of the handler must be:
@code void handler(
error_code const& ec // Result of operation
); @endcode
Regardless of whether the asynchronous operation completes
immediately or not, the handler will not be invoked from within
this function. Invocation of the handler will be performed in a
manner equivalent to using `boost::asio::io_service::post`.
*/
template<class Body, class Allocator,
template<
class ResponseDecorator,
class AcceptHandler>
#if BOOST_BEAST_DOXYGEN
void_or_deduced
@ -2436,93 +2036,26 @@ public:
async_return_type<
AcceptHandler, void(error_code)>
#endif
async_accept(http::request<Body,
http::basic_fields<Allocator>> const& req,
AcceptHandler&& handler);
async_accept_ex(
ResponseDecorator const& decorator,
AcceptHandler&& handler);
/** Start responding to a WebSocket HTTP Upgrade request.
/** Start reading and responding to a WebSocket HTTP Upgrade request.
This function is used to asynchronously send the HTTP response
to an HTTP request possibly containing a WebSocket Upgrade
request. The function call always returns immediately. The
asynchronous operation will continue until one of the following
conditions is true:
This function is used to asynchronously read an HTTP WebSocket
Upgrade request and send the HTTP response. The function call
always returns immediately. The asynchronous operation will
continue until one of the following conditions is true:
@li The response finishes sending.
@li The request is received and the response finishes sending.
@li An error occurs on the stream.
This operation is implemented in terms of one or more calls to
the next layer's `async_write_some` functions, and is known as
a <em>composed operation</em>. The program must ensure that the
stream performs no other operations until this operation
completes.
If the stream receives a valid HTTP WebSocket Upgrade request,
an HTTP response is sent back indicating a successful upgrade.
When the completion handler is invoked, the stream is then
ready to send and receive WebSocket protocol frames and
messages.
If the HTTP Upgrade request is invalid or cannot be satisfied,
an HTTP response is sent indicating the reason and status code
(typically 400, "Bad Request"). This counts as a failure, and
the completion handler will be invoked with a suitable error
code set.
@param req An object containing the HTTP Upgrade request.
Ownership is not transferred, the implementation will not access
this object from other threads.
@param decorator A function object which will be called to modify
the HTTP response object delivered by the implementation. This
could be used to set the Server field, subprotocols, or other
application or HTTP specific fields. The object will be called
with this equivalent signature:
@code void decorator(
response_type& res
); @endcode
@param handler The handler to be called when the request
completes. Copies will be made of the handler as required. The
equivalent function signature of the handler must be:
@code void handler(
error_code const& ec // Result of operation
); @endcode
Regardless of whether the asynchronous operation completes
immediately or not, the handler will not be invoked from within
this function. Invocation of the handler will be performed in a
manner equivalent to using `boost::asio::io_service::post`.
*/
template<class Body, class Allocator,
class ResponseDecorator, class AcceptHandler>
#if BOOST_BEAST_DOXYGEN
void_or_deduced
#else
async_return_type<
AcceptHandler, void(error_code)>
#endif
async_accept_ex(http::request<Body,
http::basic_fields<Allocator>> const& req,
ResponseDecorator const& decorator,
AcceptHandler&& handler);
/** Start responding to a WebSocket HTTP Upgrade request.
This function is used to asynchronously send the HTTP response
to an HTTP request possibly containing a WebSocket Upgrade
request. The function call always returns immediately. The
asynchronous operation will continue until one of the following
conditions is true:
@li The response finishes sending.
@li An error occurs on the stream.
This operation is implemented in terms of one or more calls to
the next layer's `async_write_some` functions, and is known as
a <em>composed operation</em>. The program must ensure that the
stream performs no other operations until this operation
completes.
the next layer's `async_read_some` and `async_write_some`
functions, and is known as a <em>composed operation</em>. The
program must ensure that the stream performs no other
asynchronous operations until this operation completes.
If the stream receives a valid HTTP WebSocket Upgrade request,
an HTTP response is sent back indicating a successful upgrade.
@ -2536,15 +2069,12 @@ public:
code set.
The implementation uses fixed size internal storage to
copy the buffer. If the buffer is too large, the error
receive the request. If the request is too large, the error
@ref error::buffer_overflow will be indicated. Applications
that wish to supply larger buffers should wrap the next layer
in a @ref buffered_read_stream and store the buffer contents in
the wrapper before calling accept.
@param req An object containing the HTTP Upgrade request.
Ownership is not transferred, the implementation will not access
this object from other threads.
that wish to receive larger requests should first read the
request using their own buffer and a suitable overload of
@ref http::read or @ref http::async_read, then call @ref accept
or @ref async_accept with the request.
@param buffers Caller provided data that has already been
received on the stream. This may be used for implementations
@ -2564,36 +2094,36 @@ public:
this function. Invocation of the handler will be performed in a
manner equivalent to using `boost::asio::io_service::post`.
*/
template<class Body, class Allocator,
class ConstBufferSequence, class AcceptHandler>
template<
class ConstBufferSequence,
class AcceptHandler>
#if BOOST_BEAST_DOXYGEN
void_or_deduced
#else
async_return_type<
AcceptHandler, void(error_code)>
typename std::enable_if<
! http::detail::is_header<ConstBufferSequence>::value,
async_return_type<AcceptHandler, void(error_code)>>::type
#endif
async_accept(http::request<Body,
http::basic_fields<Allocator>> const& req,
ConstBufferSequence const& buffers,
AcceptHandler&& handler);
async_accept(
ConstBufferSequence const& buffers,
AcceptHandler&& handler);
/** Start responding to a WebSocket HTTP Upgrade request.
/** Start reading and responding to a WebSocket HTTP Upgrade request.
This function is used to asynchronously send the HTTP response
to an HTTP request possibly containing a WebSocket Upgrade
request. The function call always returns immediately. The
asynchronous operation will continue until one of the following
conditions is true:
This function is used to asynchronously read an HTTP WebSocket
Upgrade request and send the HTTP response. The function call
always returns immediately. The asynchronous operation will
continue until one of the following conditions is true:
@li The response finishes sending.
@li The request is received and the response finishes sending.
@li An error occurs on the stream.
This operation is implemented in terms of one or more calls to
the next layer's `async_write_some` functions, and is known as
a <em>composed operation</em>. The program must ensure that the
stream performs no other operations until this operation
completes.
the next layer's `async_read_some` and `async_write_some`
functions, and is known as a <em>composed operation</em>. The
program must ensure that the stream performs no other
asynchronous operations until this operation completes.
If the stream receives a valid HTTP WebSocket Upgrade request,
an HTTP response is sent back indicating a successful upgrade.
@ -2607,15 +2137,12 @@ public:
code set.
The implementation uses fixed size internal storage to
copy the buffer. If the buffer is too large, the error
receive the request. If the request is too large, the error
@ref error::buffer_overflow will be indicated. Applications
that wish to supply larger buffers should wrap the next layer
in a @ref buffered_read_stream and store the buffer contents in
the wrapper before calling accept.
@param req An object containing the HTTP Upgrade request.
Ownership is not transferred, the implementation will not access
this object from other threads.
that wish to receive larger requests should first read the
request using their own buffer and a suitable overload of
@ref http::read or @ref http::async_read, then call @ref accept
or @ref async_accept with the request.
@param buffers Caller provided data that has already been
received on the stream. This may be used for implementations
@ -2645,12 +2172,138 @@ public:
manner equivalent to using `boost::asio::io_service::post`.
*/
template<
class Body, class Allocator,
class ConstBufferSequence,
class ResponseDecorator,
class AcceptHandler>
#if BOOST_BEAST_DOXYGEN
void_or_deduced
#else
typename std::enable_if<
! http::detail::is_header<ConstBufferSequence>::value,
async_return_type<AcceptHandler, void(error_code)>>::type
#endif
async_accept_ex(
ConstBufferSequence const& buffers,
ResponseDecorator const& decorator,
AcceptHandler&& handler);
/** Start responding to a WebSocket HTTP Upgrade request.
This function is used to asynchronously send the HTTP response
to an HTTP request possibly containing a WebSocket Upgrade
request. The function call always returns immediately. The
asynchronous operation will continue until one of the following
conditions is true:
@li The response finishes sending.
@li An error occurs on the stream.
This operation is implemented in terms of one or more calls to
the next layer's `async_write_some` functions, and is known as
a <em>composed operation</em>. The program must ensure that the
stream performs no other operations until this operation
completes.
If the stream receives a valid HTTP WebSocket Upgrade request,
an HTTP response is sent back indicating a successful upgrade.
When the completion handler is invoked, the stream is then
ready to send and receive WebSocket protocol frames and
messages.
If the HTTP Upgrade request is invalid or cannot be satisfied,
an HTTP response is sent indicating the reason and status code
(typically 400, "Bad Request"). This counts as a failure, and
the completion handler will be invoked with a suitable error
code set.
@param req An object containing the HTTP Upgrade request.
Ownership is not transferred, the implementation will not access
this object from other threads.
@param handler The handler to be called when the request
completes. Copies will be made of the handler as required. The
equivalent function signature of the handler must be:
@code void handler(
error_code const& ec // Result of operation
); @endcode
Regardless of whether the asynchronous operation completes
immediately or not, the handler will not be invoked from within
this function. Invocation of the handler will be performed in a
manner equivalent to using `boost::asio::io_service::post`.
*/
template<
class Body, class Allocator,
class AcceptHandler>
#if BOOST_BEAST_DOXYGEN
void_or_deduced
#else
async_return_type<
AcceptHandler, void(error_code)>
#endif
async_accept(
http::request<Body,
http::basic_fields<Allocator>> const& req,
AcceptHandler&& handler);
/** Start responding to a WebSocket HTTP Upgrade request.
This function is used to asynchronously send the HTTP response
to an HTTP request possibly containing a WebSocket Upgrade
request. The function call always returns immediately. The
asynchronous operation will continue until one of the following
conditions is true:
@li The response finishes sending.
@li An error occurs on the stream.
This operation is implemented in terms of one or more calls to
the next layer's `async_write_some` functions, and is known as
a <em>composed operation</em>. The program must ensure that the
stream performs no other operations until this operation
completes.
If the stream receives a valid HTTP WebSocket Upgrade request,
an HTTP response is sent back indicating a successful upgrade.
When the completion handler is invoked, the stream is then
ready to send and receive WebSocket protocol frames and
messages.
If the HTTP Upgrade request is invalid or cannot be satisfied,
an HTTP response is sent indicating the reason and status code
(typically 400, "Bad Request"). This counts as a failure, and
the completion handler will be invoked with a suitable error
code set.
@param req An object containing the HTTP Upgrade request.
Ownership is not transferred, the implementation will not access
this object from other threads.
@param decorator A function object which will be called to modify
the HTTP response object delivered by the implementation. This
could be used to set the Server field, subprotocols, or other
application or HTTP specific fields. The object will be called
with this equivalent signature:
@code void decorator(
response_type& res
); @endcode
@param handler The handler to be called when the request
completes. Copies will be made of the handler as required. The
equivalent function signature of the handler must be:
@code void handler(
error_code const& ec // Result of operation
); @endcode
Regardless of whether the asynchronous operation completes
immediately or not, the handler will not be invoked from within
this function. Invocation of the handler will be performed in a
manner equivalent to using `boost::asio::io_service::post`.
*/
template<
class Body, class Allocator,
class ResponseDecorator,
class AcceptHandler>
#if BOOST_BEAST_DOXYGEN
void_or_deduced
#else
async_return_type<
AcceptHandler, void(error_code)>
@ -2658,7 +2311,6 @@ public:
async_accept_ex(
http::request<Body,
http::basic_fields<Allocator>> const& req,
ConstBufferSequence const& buffers,
ResponseDecorator const& decorator,
AcceptHandler&& handler);

View File

@ -17,8 +17,6 @@
#include <boost/beast/test/stream.hpp>
#include <boost/beast/test/yield_to.hpp>
#include <boost/beast/unit_test/suite.hpp>
#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/optional.hpp>
#include <memory>
#include <mutex>
@ -409,15 +407,6 @@ public:
ws.accept(req);
}
template<class NextLayer, class Buffers>
void
accept(stream<NextLayer>& ws,
http::request<http::empty_body> const& req,
Buffers const& buffers) const
{
ws.accept(req, buffers);
}
template<class NextLayer, class Decorator>
void
accept_ex(stream<NextLayer>& ws,
@ -612,18 +601,6 @@ public:
throw system_error{ec};
}
template<class NextLayer, class Buffers>
void
accept(stream<NextLayer>& ws,
http::request<http::empty_body> const& req,
Buffers const& buffers) const
{
error_code ec;
ws.async_accept(req, buffers, yield_[ec]);
if(ec)
throw system_error{ec};
}
template<class NextLayer,
class Decorator>
void
@ -877,6 +854,13 @@ public:
}
};
auto const big = []
{
std::string s;
s += "X1: " + std::string(2000, '*') + "\r\n";
return s;
}();
// request in stream
doTestLoop([&](test::stream& ts)
{
@ -894,6 +878,32 @@ public:
// VFALCO validate contents of ws.next_layer().str?
});
// request in stream, oversized
{
stream<test::stream> ws{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"
+ big +
"\r\n"};
error_code ec = test::error::fail_error;
try
{
c.accept(ws);
fail("", __FILE__, __LINE__);
}
catch(system_error const& se)
{
// VFALCO Its the http error category...
BEAST_EXPECTS(
se.code() == http::error::buffer_overflow,
se.code().message());
}
}
// request in stream, decorator
doTestLoop([&](test::stream& ts)
{
@ -912,6 +922,32 @@ public:
BEAST_EXPECT(called);
});
// request in stream, decorator, oversized
{
stream<test::stream> ws{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"
+ big +
"\r\n"};
try
{
bool called = false;
c.accept_ex(ws, res_decorator{called});
fail("", __FILE__, __LINE__);
}
catch(system_error const& se)
{
// VFALCO Its the http error category...
BEAST_EXPECTS(
se.code() == http::error::buffer_overflow,
se.code().message());
}
}
// request in buffers
doTestLoop([&](test::stream& ts)
{
@ -927,6 +963,31 @@ public:
));
});
// request in buffers, oversize
{
stream<test::stream> ws{ios_};
try
{
c.accept(ws, boost::asio::buffer(
"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"
+ big +
"\r\n"
));
fail("", __FILE__, __LINE__);
}
catch(system_error const& se)
{
BEAST_EXPECTS(
se.code() == error::buffer_overflow,
se.code().message());
}
}
// request in buffers, decorator
doTestLoop([&](test::stream& ts)
{
@ -944,6 +1005,32 @@ public:
BEAST_EXPECT(called);
});
// request in buffers, decorator, oversized
{
stream<test::stream> ws{ios_};
try
{
bool called = false;
c.accept_ex(ws, boost::asio::buffer(
"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"
+ big +
"\r\n"),
res_decorator{called});
fail("", __FILE__, __LINE__);
}
catch(system_error const& se)
{
BEAST_EXPECTS(
se.code() == error::buffer_overflow,
se.code().message());
}
}
// request in buffers and stream
doTestLoop([&](test::stream& ts)
{
@ -962,6 +1049,31 @@ public:
// VFALCO validate contents of ws.next_layer().str?
});
// request in buffers and stream, oversized
{
stream<test::stream> ws{ios_,
"Connection: upgrade\r\n"
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
"Sec-WebSocket-Version: 13\r\n"
+ big +
"\r\n"};
try
{
c.accept(ws, sbuf(
"GET / HTTP/1.1\r\n"
"Host: localhost\r\n"
"Upgrade: websocket\r\n"
));
fail("", __FILE__, __LINE__);
}
catch(system_error const& se)
{
BEAST_EXPECTS(
se.code() == http::error::buffer_overflow,
se.code().message());
}
}
// request in buffers and stream, decorator
doTestLoop([&](test::stream& ts)
{
@ -981,6 +1093,32 @@ public:
BEAST_EXPECT(called);
});
// request in buffers and stream, decorator, oversize
{
stream<test::stream> ws{ios_,
"Connection: upgrade\r\n"
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
"Sec-WebSocket-Version: 13\r\n"
+ big +
"\r\n"};
try
{
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});
fail("", __FILE__, __LINE__);
}
catch(system_error const& se)
{
BEAST_EXPECTS(
se.code() == http::error::buffer_overflow,
se.code().message());
}
}
// request in message
doTestLoop([&](test::stream& ts)
{
@ -1016,65 +1154,6 @@ public:
BEAST_EXPECT(called);
});
// request in message, close frame in buffers
doTestLoop([&](test::stream& ts)
{
stream<test::stream&> ws{ts};
request_type req;
req.method(http::verb::get);
req.target("/");
req.version = 11;
req.insert(http::field::host, "localhost");
req.insert(http::field::upgrade, "websocket");
req.insert(http::field::connection, "upgrade");
req.insert(http::field::sec_websocket_key, "dGhlIHNhbXBsZSBub25jZQ==");
req.insert(http::field::sec_websocket_version, "13");
c.accept(ws, req, cbuf(
0x88, 0x82, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x17));
try
{
static_buffer<1> b;
c.read(ws, b);
fail("success", __FILE__, __LINE__);
}
catch(system_error const& e)
{
if(e.code() != websocket::error::closed)
throw;
}
});
// request in message, close frame in buffers, decorator
doTestLoop([&](test::stream& ts)
{
stream<test::stream&> ws{ts};
request_type req;
req.method(http::verb::get);
req.target("/");
req.version = 11;
req.insert(http::field::host, "localhost");
req.insert(http::field::upgrade, "websocket");
req.insert(http::field::connection, "upgrade");
req.insert(http::field::sec_websocket_key, "dGhlIHNhbXBsZSBub25jZQ==");
req.insert(http::field::sec_websocket_version, "13");
bool called = false;
c.accept_ex(ws, req, cbuf(
0x88, 0x82, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x17),
res_decorator{called});
BEAST_EXPECT(called);
try
{
static_buffer<1> b;
c.read(ws, b);
fail("success", __FILE__, __LINE__);
}
catch(system_error const& e)
{
if(e.code() != websocket::error::closed)
throw;
}
});
// request in message, close frame in stream
doTestLoop([&](test::stream& ts)
{
@ -1103,35 +1182,6 @@ public:
}
});
// request in message, close frame in stream and buffers
doTestLoop([&](test::stream& ts)
{
stream<test::stream&> ws{ts};
request_type req;
req.method(http::verb::get);
req.target("/");
req.version = 11;
req.insert(http::field::host, "localhost");
req.insert(http::field::upgrade, "websocket");
req.insert(http::field::connection, "upgrade");
req.insert(http::field::sec_websocket_key, "dGhlIHNhbXBsZSBub25jZQ==");
req.insert(http::field::sec_websocket_version, "13");
ts.str("xff\xff\xfc\x17");
c.accept(ws, req, cbuf(
0x88, 0x82, 0xff, 0xff));
try
{
static_buffer<1> b;
c.read(ws, b);
fail("success", __FILE__, __LINE__);
}
catch(system_error const& e)
{
if(e.code() != websocket::error::closed)
throw;
}
});
// failed handshake (missing Sec-WebSocket-Key)
doTestLoop([&](test::stream& ts)
{
@ -1158,6 +1208,24 @@ public:
throw;
}
});
// Closed by client
{
stream<test::stream> ws{ios_};
ws.next_layer().remote().close();
try
{
c.accept(ws);
fail("success", __FILE__, __LINE__);
}
catch(system_error const& e)
{
if(! BEAST_EXPECTS(
e.code() == error::closed,
e.code().message()))
throw;
}
}
}
void
@ -2059,14 +2127,15 @@ public:
stream<socket_type&>>::value);
log << "sizeof(websocket::stream) == " <<
sizeof(websocket::stream<boost::asio::ip::tcp::socket&>) << std::endl;
sizeof(websocket::stream<test::stream&>) << std::endl;
testAccept();
permessage_deflate pmd;
pmd.client_enable = false;
pmd.server_enable = false;
testOptions();
testAccept();
testHandshake();
testBadHandshakes();
testBadResponses();