Provide websocket::stream accept() overloads (API Change):

This adds enough functions to the accept and async_accept
overload set to cover all combinations of request, buffers,
and error_code parameters. This also fixes a defect where
providing a complete Upgrade request when there are additional
unprocessed octets remaining in the callers stream buffer
could not be furnished to the WebSocket implementation.
This commit is contained in:
Vinnie Falco
2017-04-25 10:12:43 -07:00
parent 8ba166a221
commit fab1015b89
8 changed files with 1248 additions and 559 deletions

View File

@@ -2,6 +2,10 @@
* CMake hide command lines in .vcxproj Output windows" * CMake hide command lines in .vcxproj Output windows"
API Changes:
* Provide websocket::stream accept() overloads
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
1.0.0-b36 1.0.0-b36

View File

@@ -0,0 +1,166 @@
//
// 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_TEST_STRING_IOSTREAM_HPP
#define BEAST_TEST_STRING_IOSTREAM_HPP
#include <beast/core/async_completion.hpp>
#include <beast/core/bind_handler.hpp>
#include <beast/core/error.hpp>
#include <beast/core/prepare_buffer.hpp>
#include <beast/websocket/teardown.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/asio/io_service.hpp>
#include <string>
namespace beast {
namespace test {
/** A SyncStream and AsyncStream that reads from a string and writes to another string.
This class behaves like a socket, except that written data is
appended to a string exposed as a public data member, and when
data is read it comes from a string provided at construction.
*/
class string_iostream
{
std::string s_;
boost::asio::const_buffer cb_;
boost::asio::io_service& ios_;
std::size_t read_max_;
public:
std::string str;
string_iostream(boost::asio::io_service& ios,
std::string s, std::size_t read_max =
(std::numeric_limits<std::size_t>::max)())
: s_(std::move(s))
, cb_(boost::asio::buffer(s_))
, ios_(ios)
, read_max_(read_max)
{
}
boost::asio::io_service&
get_io_service()
{
return ios_;
}
template<class MutableBufferSequence>
std::size_t
read_some(MutableBufferSequence const& buffers)
{
error_code ec;
auto const n = read_some(buffers, ec);
if(ec)
throw system_error{ec};
return n;
}
template<class MutableBufferSequence>
std::size_t
read_some(MutableBufferSequence const& buffers,
error_code& ec)
{
auto const n = boost::asio::buffer_copy(
buffers, prepare_buffer(read_max_, cb_));
if(n > 0)
cb_ = cb_ + n;
else
ec = boost::asio::error::eof;
return n;
}
template<class MutableBufferSequence, class ReadHandler>
typename async_completion<ReadHandler,
void(error_code, std::size_t)>::result_type
async_read_some(MutableBufferSequence const& buffers,
ReadHandler&& handler)
{
auto const n = boost::asio::buffer_copy(
buffers, boost::asio::buffer(s_));
error_code ec;
if(n > 0)
s_.erase(0, n);
else
ec = boost::asio::error::eof;
async_completion<ReadHandler,
void(error_code, std::size_t)> completion{handler};
ios_.post(bind_handler(
completion.handler, ec, n));
return completion.result.get();
}
template<class ConstBufferSequence>
std::size_t
write_some(ConstBufferSequence const& buffers)
{
error_code ec;
auto const n = write_some(buffers, ec);
if(ec)
throw system_error{ec};
return n;
}
template<class ConstBufferSequence>
std::size_t
write_some(
ConstBufferSequence const& buffers, error_code&)
{
auto const n = buffer_size(buffers);
using boost::asio::buffer_size;
using boost::asio::buffer_cast;
str.reserve(str.size() + n);
for(auto const& buffer : buffers)
str.append(buffer_cast<char const*>(buffer),
buffer_size(buffer));
return n;
}
template<class ConstBufferSequence, class WriteHandler>
typename async_completion<
WriteHandler, void(error_code)>::result_type
async_write_some(ConstBufferSequence const& buffers,
WriteHandler&& handler)
{
error_code ec;
auto const bytes_transferred = write_some(buffers, ec);
async_completion<
WriteHandler, void(error_code, std::size_t)
> completion{handler};
get_io_service().post(
bind_handler(completion.handler, ec, bytes_transferred));
return completion.result.get();
}
friend
void
teardown(websocket::teardown_tag,
string_iostream& stream,
boost::system::error_code& ec)
{
}
template<class TeardownHandler>
friend
void
async_teardown(websocket::teardown_tag,
string_iostream& stream,
TeardownHandler&& handler)
{
stream.get_io_service().post(
bind_handler(std::move(handler),
error_code{}));
}
};
} // test
} // beast
#endif

View File

@@ -12,6 +12,7 @@
#include <beast/core/bind_handler.hpp> #include <beast/core/bind_handler.hpp>
#include <beast/core/error.hpp> #include <beast/core/error.hpp>
#include <beast/core/prepare_buffer.hpp> #include <beast/core/prepare_buffer.hpp>
#include <beast/websocket/teardown.hpp>
#include <boost/asio/buffer.hpp> #include <boost/asio/buffer.hpp>
#include <boost/asio/io_service.hpp> #include <boost/asio/io_service.hpp>
#include <string> #include <string>
@@ -125,6 +126,26 @@ public:
error_code{}, boost::asio::buffer_size(buffers))); error_code{}, boost::asio::buffer_size(buffers)));
return completion.result.get(); return completion.result.get();
} }
friend
void
teardown(websocket::teardown_tag,
string_istream& stream,
boost::system::error_code& ec)
{
}
template<class TeardownHandler>
friend
void
async_teardown(websocket::teardown_tag,
string_istream& stream,
TeardownHandler&& handler)
{
stream.get_io_service().post(
bind_handler(std::move(handler),
error_code{}));
}
}; };
} // test } // test

View File

@@ -11,6 +11,7 @@
#include <beast/core/async_completion.hpp> #include <beast/core/async_completion.hpp>
#include <beast/core/bind_handler.hpp> #include <beast/core/bind_handler.hpp>
#include <beast/core/error.hpp> #include <beast/core/error.hpp>
#include <beast/websocket/teardown.hpp>
#include <boost/asio/buffer.hpp> #include <boost/asio/buffer.hpp>
#include <boost/asio/io_service.hpp> #include <boost/asio/io_service.hpp>
#include <string> #include <string>
@@ -110,6 +111,26 @@ public:
bind_handler(completion.handler, ec, bytes_transferred)); bind_handler(completion.handler, ec, bytes_transferred));
return completion.result.get(); return completion.result.get();
} }
friend
void
teardown(websocket::teardown_tag,
string_ostream& stream,
boost::system::error_code& ec)
{
}
template<class TeardownHandler>
friend
void
async_teardown(websocket::teardown_tag,
string_ostream& stream,
TeardownHandler&& handler)
{
stream.get_io_service().post(
bind_handler(std::move(handler),
error_code{}));
}
}; };
} // test } // test

View File

@@ -36,7 +36,6 @@ class stream<NextLayer>::response_op
bool cont; bool cont;
stream<NextLayer>& ws; stream<NextLayer>& ws;
http::response_header res; http::response_header res;
error_code final_ec;
int state = 0; int state = 0;
template<class Fields> template<class Fields>
@@ -47,11 +46,22 @@ class stream<NextLayer>::response_op
, ws(ws_) , ws(ws_)
, res(ws_.build_response(req)) , res(ws_.build_response(req))
{ {
// can't call stream::reset() here }
// otherwise accept_op will malfunction
// template<class Fields, class Buffers>
if(res.status != 101) data(Handler&, stream<NextLayer>& ws_,
final_ec = error::handshake_failed; http::header<true, Fields> const& req,
Buffers const& buffers, bool cont_)
: cont(cont_)
, ws(ws_)
, res(ws_.build_response(req))
{
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
// VFALCO What about catch(std::length_error const&)?
ws.stream_.buffer().commit(buffer_copy(
ws.stream_.buffer().prepare(
buffer_size(buffers)), buffers));
} }
}; };
@@ -126,7 +136,8 @@ operator()(error_code ec, bool again)
// sent response // sent response
case 1: case 1:
d.state = 99; d.state = 99;
ec = d.final_ec; if(d.res.status != 101)
ec = error::handshake_failed;
if(! ec) if(! ec)
{ {
pmd_read( pmd_read(
@@ -154,6 +165,10 @@ class stream<NextLayer>::accept_op
http::header_parser<true, http::fields> p; http::header_parser<true, http::fields> p;
int state = 0; 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_) {}
template<class Buffers> template<class Buffers>
data(Handler& handler, stream<NextLayer>& ws_, data(Handler& handler, stream<NextLayer>& ws_,
Buffers const& buffers) Buffers const& buffers)
@@ -163,7 +178,7 @@ class stream<NextLayer>::accept_op
{ {
using boost::asio::buffer_copy; using boost::asio::buffer_copy;
using boost::asio::buffer_size; using boost::asio::buffer_size;
ws.reset(); // VFALCO What about catch(std::length_error const&)?
ws.stream_.buffer().commit(buffer_copy( ws.stream_.buffer().commit(buffer_copy(
ws.stream_.buffer().prepare( ws.stream_.buffer().prepare(
buffer_size(buffers)), buffers)); buffer_size(buffers)), buffers));
@@ -185,11 +200,6 @@ public:
(*this)(error_code{}, 0, false); (*this)(error_code{}, 0, false);
} }
void operator()(error_code const& ec)
{
(*this)(ec, 0);
}
void operator()(error_code ec, void operator()(error_code ec,
std::size_t bytes_used, bool again = true); std::size_t bytes_used, bool again = true);
@@ -264,59 +274,7 @@ upcall:
d_.invoke(ec); d_.invoke(ec);
} }
template<class NextLayer> //------------------------------------------------------------------------------
template<class AcceptHandler>
typename async_completion<
AcceptHandler, void(error_code)>::result_type
stream<NextLayer>::
async_accept(AcceptHandler&& handler)
{
static_assert(is_AsyncStream<next_layer_type>::value,
"AsyncStream requirements requirements not met");
return async_accept(boost::asio::null_buffers{},
std::forward<AcceptHandler>(handler));
}
template<class NextLayer>
template<class ConstBufferSequence, class AcceptHandler>
typename async_completion<
AcceptHandler, void(error_code)>::result_type
stream<NextLayer>::
async_accept(ConstBufferSequence const& bs, AcceptHandler&& handler)
{
static_assert(is_AsyncStream<next_layer_type>::value,
"AsyncStream requirements requirements not met");
static_assert(beast::is_ConstBufferSequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
beast::async_completion<
AcceptHandler, void(error_code)
> completion{handler};
accept_op<decltype(completion.handler)>{
completion.handler, *this, bs};
return completion.result.get();
}
template<class NextLayer>
template<class Fields, class AcceptHandler>
typename async_completion<
AcceptHandler, void(error_code)>::result_type
stream<NextLayer>::
async_accept(http::header<true, Fields> const& req,
AcceptHandler&& handler)
{
static_assert(is_AsyncStream<next_layer_type>::value,
"AsyncStream requirements requirements not met");
beast::async_completion<
AcceptHandler, void(error_code)
> completion{handler};
reset();
response_op<decltype(completion.handler)>{
completion.handler, *this, req,
beast_asio_helpers::
is_continuation(completion.handler)};
return completion.result.get();
}
template<class NextLayer> template<class NextLayer>
void void
@@ -326,7 +284,7 @@ accept()
static_assert(is_SyncStream<next_layer_type>::value, static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met"); "SyncStream requirements not met");
error_code ec; error_code ec;
accept(boost::asio::null_buffers{}, ec); accept(ec);
if(ec) if(ec)
throw system_error{ec}; throw system_error{ec};
} }
@@ -338,7 +296,8 @@ accept(error_code& ec)
{ {
static_assert(is_SyncStream<next_layer_type>::value, static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met"); "SyncStream requirements not met");
accept(boost::asio::null_buffers{}, ec); reset();
do_accept(ec);
} }
template<class NextLayer> template<class NextLayer>
@@ -366,35 +325,28 @@ accept(ConstBufferSequence const& buffers, error_code& ec)
{ {
static_assert(is_SyncStream<next_layer_type>::value, static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met"); "SyncStream requirements not met");
static_assert(beast::is_ConstBufferSequence< static_assert(is_ConstBufferSequence<
ConstBufferSequence>::value, ConstBufferSequence>::value,
"ConstBufferSequence requirements not met"); "ConstBufferSequence requirements not met");
reset();
using boost::asio::buffer_copy; using boost::asio::buffer_copy;
using boost::asio::buffer_size; using boost::asio::buffer_size;
reset();
stream_.buffer().commit(buffer_copy( stream_.buffer().commit(buffer_copy(
stream_.buffer().prepare( stream_.buffer().prepare(
buffer_size(buffers)), buffers)); buffer_size(buffers)), buffers));
http::header_parser<true, http::fields> p; do_accept(ec);
auto const bytes_used = http::read_some(
next_layer(), stream_.buffer(), p, ec);
if(ec)
return;
BOOST_ASSERT(p.got_header());
stream_.buffer().consume(bytes_used);
accept(p.get(), ec);
} }
template<class NextLayer> template<class NextLayer>
template<class Fields> template<class Fields>
void void
stream<NextLayer>:: stream<NextLayer>::
accept(http::header<true, Fields> const& request) accept(http::header<true, Fields> const& req)
{ {
static_assert(is_SyncStream<next_layer_type>::value, static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met"); "SyncStream requirements not met");
error_code ec; error_code ec;
accept(request, ec); accept(req, ec);
if(ec) if(ec)
throw system_error{ec}; throw system_error{ec};
} }
@@ -409,19 +361,134 @@ accept(http::header<true, Fields> const& req,
static_assert(is_SyncStream<next_layer_type>::value, static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met"); "SyncStream requirements not met");
reset(); reset();
auto const res = build_response(req); do_accept(req, ec);
http::write(stream_, res, ec); }
template<class NextLayer>
template<class Fields, class ConstBufferSequence>
void
stream<NextLayer>::
accept(
http::header<true, Fields> const& req,
ConstBufferSequence const& buffers)
{
static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met");
static_assert(is_ConstBufferSequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
error_code ec;
accept(req, buffers, ec);
if(ec) if(ec)
return; throw system_error{ec};
if(res.status != 101) }
{
ec = error::handshake_failed; template<class NextLayer>
// VFALCO TODO Respect keep alive setting, perform template<class Fields, class ConstBufferSequence>
// teardown if Connection: close. void
return; stream<NextLayer>::
} accept(
pmd_read(pmd_config_, req.fields); http::header<true, Fields> const& req,
open(detail::role_type::server); ConstBufferSequence const& buffers,
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");
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, ec);
}
//------------------------------------------------------------------------------
template<class NextLayer>
template<class AcceptHandler>
typename async_completion<AcceptHandler,
void(error_code)>::result_type
stream<NextLayer>::
async_accept(AcceptHandler&& handler)
{
static_assert(is_AsyncStream<next_layer_type>::value,
"AsyncStream requirements requirements not met");
beast::async_completion<AcceptHandler,
void(error_code)> completion{handler};
reset();
accept_op<decltype(completion.handler)>{
completion.handler, *this};
return completion.result.get();
}
template<class NextLayer>
template<class ConstBufferSequence, class AcceptHandler>
typename async_completion<AcceptHandler,
void(error_code)>::result_type
stream<NextLayer>::
async_accept(ConstBufferSequence const& buffers,
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");
beast::async_completion<AcceptHandler,
void(error_code)> completion{handler};
reset();
accept_op<decltype(completion.handler)>{
completion.handler, *this, buffers};
return completion.result.get();
}
template<class NextLayer>
template<class Fields, class AcceptHandler>
typename async_completion<AcceptHandler,
void(error_code)>::result_type
stream<NextLayer>::
async_accept(http::header<true, Fields> const& req,
AcceptHandler&& handler)
{
static_assert(is_AsyncStream<next_layer_type>::value,
"AsyncStream requirements requirements not met");
beast::async_completion<AcceptHandler,
void(error_code)> completion{handler};
reset();
response_op<decltype(completion.handler)>{
completion.handler, *this, req,
beast_asio_helpers::
is_continuation(completion.handler)};
return completion.result.get();
}
template<class NextLayer>
template<class Fields,
class ConstBufferSequence, class AcceptHandler>
typename async_completion<AcceptHandler,
void(error_code)>::result_type
stream<NextLayer>::
async_accept(http::header<true, Fields> const& req,
ConstBufferSequence const& buffers,
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");
beast::async_completion<AcceptHandler,
void(error_code)> completion{handler};
reset();
response_op<decltype(completion.handler)>{
completion.handler, *this, req, buffers,
beast_asio_helpers::
is_continuation(completion.handler)};
return completion.result.get();
} }
} // websocket } // websocket

View File

@@ -82,6 +82,43 @@ reset()
stream_.buffer().size()); stream_.buffer().size());
} }
template<class NextLayer>
void
stream<NextLayer>::
do_accept(error_code& ec)
{
http::header_parser<true, http::fields> p;
auto const bytes_used = http::read_some(
next_layer(), stream_.buffer(), p, ec);
if(ec)
return;
BOOST_ASSERT(p.got_header());
stream_.buffer().consume(bytes_used);
do_accept(p.get(), ec);
}
template<class NextLayer>
template<class Fields>
void
stream<NextLayer>::
do_accept(http::header<true, Fields> const& req,
error_code& ec)
{
auto const res = build_response(req);
http::write(stream_, res, ec);
if(ec)
return;
if(res.status != 101)
{
ec = error::handshake_failed;
// VFALCO TODO Respect keep alive setting, perform
// teardown if Connection: close.
return;
}
pmd_read(pmd_config_, req.fields);
open(detail::role_type::server);
}
template<class NextLayer> template<class NextLayer>
http::request_header http::request_header
stream<NextLayer>:: stream<NextLayer>::

View File

@@ -13,14 +13,15 @@
#include <beast/websocket/detail/stream_base.hpp> #include <beast/websocket/detail/stream_base.hpp>
#include <beast/http/message.hpp> #include <beast/http/message.hpp>
#include <beast/http/string_body.hpp> #include <beast/http/string_body.hpp>
#include <beast/core/dynabuf_readstream.hpp>
#include <beast/core/async_completion.hpp> #include <beast/core/async_completion.hpp>
#include <beast/core/dynabuf_readstream.hpp>
#include <beast/core/detail/get_lowest_layer.hpp> #include <beast/core/detail/get_lowest_layer.hpp>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <boost/utility/string_ref.hpp> #include <boost/utility/string_ref.hpp>
#include <algorithm> #include <algorithm>
#include <cstdint> #include <cstdint>
#include <limits> #include <limits>
#include <type_traits>
namespace beast { namespace beast {
namespace websocket { namespace websocket {
@@ -340,25 +341,25 @@ public:
/** Read and respond to a WebSocket HTTP Upgrade request. /** Read and respond to a WebSocket HTTP Upgrade request.
This function is used to synchronously read a HTTP WebSocket This function is used to synchronously read an HTTP WebSocket
Upgrade request and send the HTTP response. The call blocks until Upgrade request and send the HTTP response. The call blocks
one of the following conditions is true: until one of the following conditions is true:
@li A HTTP request finishes receiving, and a HTTP response finishes @li The HTTP request finishes receiving, and the HTTP response
sending. finishes sending.
@li An error occurs on the stream. @li An error occurs on the stream.
This function is implemented in terms of one or more calls to the This function is implemented in terms of one or more calls to
next layer's `read_some` and `write_some` functions. the next layer's `read_some` and `write_some` functions.
If the stream receives a valid HTTP WebSocket Upgrade request, a If the stream receives a valid HTTP WebSocket Upgrade request,
HTTP response is sent back indicating a successful upgrade. When this an HTTP response is sent back indicating a successful upgrade.
call returns, the stream is then ready to send and receive WebSocket When this call returns, the stream is then ready to send and
protocol frames and messages. receive WebSocket protocol frames and messages.
If the HTTP Upgrade request is invalid or cannot be satisfied, a If the HTTP Upgrade request is invalid or cannot be satisfied,
HTTP response is sent indicating the reason and status code an HTTP response is sent indicating the reason and status code
(typically 400, "Bad Request"). This counts as a failure. (typically 400, "Bad Request"). This counts as a failure.
@throws system_error Thrown on failure. @throws system_error Thrown on failure.
@@ -368,25 +369,25 @@ public:
/** Read and respond to a WebSocket HTTP Upgrade request. /** Read and respond to a WebSocket HTTP Upgrade request.
This function is used to synchronously read a HTTP WebSocket This function is used to synchronously read an HTTP WebSocket
Upgrade request and send the HTTP response. The call blocks until Upgrade request and send the HTTP response. The call blocks
one of the following conditions is true: until one of the following conditions is true:
@li A HTTP request finishes receiving, and a HTTP response finishes @li The HTTP request finishes receiving, and the HTTP response
sending. finishes sending.
@li An error occurs on the stream. @li An error occurs on the stream.
This function is implemented in terms of one or more calls to the This function is implemented in terms of one or more calls to
next layer's `read_some` and `write_some` functions. the next layer's `read_some` and `write_some` functions.
If the stream receives a valid HTTP WebSocket Upgrade request, a If the stream receives a valid HTTP WebSocket Upgrade request,
HTTP response is sent back indicating a successful upgrade. When this an HTTP response is sent back indicating a successful upgrade.
call returns, the stream is then ready to send and receive WebSocket When this call returns, the stream is then ready to send and
protocol frames and messages. receive WebSocket protocol frames and messages.
If the HTTP Upgrade request is invalid or cannot be satisfied, a If the HTTP Upgrade request is invalid or cannot be satisfied,
HTTP response is sent indicating the reason and status code an HTTP response is sent indicating the reason and status code
(typically 400, "Bad Request"). This counts as a failure. (typically 400, "Bad Request"). This counts as a failure.
@param ec Set to indicate what error occurred, if any. @param ec Set to indicate what error occurred, if any.
@@ -394,36 +395,245 @@ public:
void void
accept(error_code& ec); accept(error_code& ec);
/** Read and respond to a WebSocket HTTP Upgrade request.
This function is used to synchronously read an HTTP WebSocket
Upgrade request and send the HTTP response. The call blocks
until one of the following conditions is true:
@li The HTTP request finishes receiving, and the HTTP 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.
@param buffers Caller provided data that has already been
received on the stream. The implementation will copy the
caller provided data before the function returns.
@throws system_error Thrown on failure.
*/
template<class ConstBufferSequence>
void
accept(ConstBufferSequence const& buffers);
/** Read and respond to a WebSocket HTTP Upgrade request.
This function is used to synchronously read an HTTP WebSocket
Upgrade request and send the HTTP response. The call blocks
until one of the following conditions is true:
@li The HTTP request finishes receiving, and the HTTP 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.
@param buffers Caller provided data that has already been
received on the stream. The implementation will copy the
caller provided data before the function returns.
@param ec Set to indicate what error occurred, if any.
*/
template<class ConstBufferSequence>
void
accept(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 HTTP 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.
@param req An object containing the HTTP Upgrade request.
Ownership is not transferred, the implementation will not
access this object from other threads.
@throws system_error Thrown on failure.
*/
template<class Fields>
void
accept(http::header<true, Fields> const& req);
/** 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 HTTP 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.
@param req An object containing the HTTP Upgrade request.
Ownership is not transferred, the implementation will not
access this object from other threads.
@param ec Set to indicate what error occurred, if any.
*/
template<class Fields>
void
accept(http::header<true, Fields> const& req, 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 HTTP 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.
@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 Fields, class ConstBufferSequence>
void
accept(http::header<true, Fields> 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 HTTP 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.
@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 Fields, class ConstBufferSequence>
void
accept(http::header<true, Fields> const& req,
ConstBufferSequence const& buffers, error_code& ec);
/** Start reading and responding to a WebSocket HTTP Upgrade request. /** Start reading and responding to a WebSocket HTTP Upgrade request.
This function is used to asynchronously read a HTTP WebSocket This function is used to asynchronously read an HTTP WebSocket
Upgrade request and send the HTTP response. The function call Upgrade request and send the HTTP response. The function call
always returns immediately. The asynchronous operation will always returns immediately. The asynchronous operation will
continue until one of the following conditions is true: continue until one of the following conditions is true:
@li A HTTP request finishes receiving, and a HTTP response finishes @li The HTTP request finishes receiving, and the HTTP response
sending. finishes sending.
@li An error occurs on the stream. @li An error occurs on the stream.
This operation is implemented in terms of one or more calls to the This operation is implemented in terms of one or more calls to
next layer's `async_read_some` and `async_write_some` functions, and the next layer's `async_read_some` and `async_write_some`
is known as a <em>composed operation</em>. The program must ensure functions, and is known as a <em>composed operation</em>. The
that the stream performs no other operations until this operation program must ensure that the stream performs no other
completes. asynchronous operations until this operation completes.
If the stream receives a valid HTTP WebSocket Upgrade request, a If the stream receives a valid HTTP WebSocket Upgrade request,
HTTP response is sent back indicating a successful upgrade. When an HTTP response is sent back indicating a successful upgrade.
this call returns, the stream is then ready to send and receive When the completion handler is invoked, the stream is then
WebSocket protocol frames and messages. ready to send and receive WebSocket protocol frames and
messages.
If the HTTP Upgrade request is invalid or cannot be satisfied, a If the HTTP Upgrade request is invalid or cannot be satisfied,
HTTP response is sent indicating the reason and status code an HTTP response is sent indicating the reason and status code
(typically 400, "Bad Request"). This counts as a failure. (typically 400, "Bad Request"). This counts as a failure, and
the completion handler will be invoked with a suitable error
code set.
@param handler The handler to be called when the request completes. @param handler The handler to be called when the request
Copies will be made of the handler as required. The equivalent completes. Copies will be made of the handler as required. The
function signature of the handler must be: equivalent function signature of the handler must be:
@code void handler( @code void handler(
error_code const& error // result of operation error_code const& error // result of operation
); @endcode ); @endcode
@@ -441,104 +651,35 @@ public:
#endif #endif
async_accept(AcceptHandler&& handler); async_accept(AcceptHandler&& handler);
/** Read and respond to a WebSocket HTTP Upgrade request.
This function is used to synchronously read a HTTP WebSocket
Upgrade request and send the HTTP response. The call blocks until
one of the following conditions is true:
@li A HTTP request finishes receiving, and a HTTP 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, a
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, a
HTTP response is sent indicating the reason and status code
(typically 400, "Bad Request"). This counts as a failure.
@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.
@throws system_error Thrown on failure.
*/
template<class ConstBufferSequence>
void
accept(ConstBufferSequence const& buffers);
/** Read and respond to a WebSocket HTTP Upgrade request.
This function is used to synchronously read a HTTP WebSocket
Upgrade request and send the HTTP response. The call blocks until
one of the following conditions is true:
@li A HTTP request finishes receiving, and a HTTP 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, a
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, a
HTTP response is sent indicating the reason and status code
(typically 400, "Bad Request"). This counts as a failure.
@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 ec Set to indicate what error occurred, if any.
*/
template<class ConstBufferSequence>
void
accept(ConstBufferSequence const& buffers, error_code& ec);
/** Start reading and responding to a WebSocket HTTP Upgrade request. /** Start reading and responding to a WebSocket HTTP Upgrade request.
This function is used to asynchronously read a HTTP WebSocket This function is used to asynchronously read an HTTP WebSocket
Upgrade request and send the HTTP response. The function call Upgrade request and send the HTTP response. The function call
always returns immediately. The asynchronous operation will always returns immediately. The asynchronous operation will
continue until one of the following conditions is true: continue until one of the following conditions is true:
@li A HTTP request finishes receiving, and a HTTP response finishes @li The HTTP request finishes receiving, and the HTTP response
sending. finishes sending.
@li An error occurs on the stream. @li An error occurs on the stream.
This operation is implemented in terms of one or more calls to the This operation is implemented in terms of one or more calls to
next layer's `async_read_some` and `async_write_some` functions, and the next layer's `async_read_some` and `async_write_some`
is known as a <em>composed operation</em>. The program must ensure functions, and is known as a <em>composed operation</em>. The
that the stream performs no other operations until this operation program must ensure that the stream performs no other
completes. asynchronous operations until this operation completes.
If the stream receives a valid HTTP WebSocket Upgrade request, a If the stream receives a valid HTTP WebSocket Upgrade request,
HTTP response is sent back indicating a successful upgrade. When an HTTP response is sent back indicating a successful upgrade.
this call returns, the stream is then ready to send and receive When the completion handler is invoked, the stream is then
WebSocket protocol frames and messages. ready to send and receive WebSocket protocol frames and
messages.
If the HTTP Upgrade request is invalid or cannot be satisfied, a If the HTTP Upgrade request is invalid or cannot be satisfied,
HTTP response is sent indicating the reason and status code an HTTP response is sent indicating the reason and status code
(typically 400, "Bad Request"). This counts as a failure. (typically 400, "Bad Request"). This counts as a failure, and
the completion handler will be invoked with a suitable error
code set.
@param buffers Caller provided data that has already been @param buffers Caller provided data that has already been
received on the stream. This may be used for implementations received on the stream. This may be used for implementations
@@ -547,9 +688,9 @@ public:
then to received WebSocket frames. The implementation will then to received WebSocket frames. The implementation will
copy the caller provided data before the function returns. copy the caller provided data before the function returns.
@param handler The handler to be called when the request completes. @param handler The handler to be called when the request
Copies will be made of the handler as required. The equivalent completes. Copies will be made of the handler as required. The
function signature of the handler must be: equivalent function signature of the handler must be:
@code void handler( @code void handler(
error_code const& error // result of operation error_code const& error // result of operation
); @endcode ); @endcode
@@ -562,110 +703,49 @@ public:
#if BEAST_DOXYGEN #if BEAST_DOXYGEN
void_or_deduced void_or_deduced
#else #else
typename async_completion< typename async_completion<AcceptHandler,
AcceptHandler, void(error_code)>::result_type void(error_code)>::result_type
#endif #endif
async_accept(ConstBufferSequence const& buffers, async_accept(ConstBufferSequence const& buffers,
AcceptHandler&& handler); AcceptHandler&& handler);
/** Respond to a WebSocket HTTP Upgrade request
This function is used to synchronously send the HTTP response to
a HTTP request possibly containing a WebSocket Upgrade request.
The call blocks until one of the following conditions is true:
@li A HTTP 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 `write_some` functions.
If the passed HTTP request is a valid HTTP WebSocket Upgrade
request, a 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 request is invalid or cannot be satisfied, a HTTP
response is sent indicating the reason and status code (typically
400, "Bad Request"). This counts as a failure.
@param request An object containing the HTTP Upgrade request.
Ownership is not transferred, the implementation will not access
this object from other threads.
@throws system_error Thrown on failure.
*/
// VFALCO TODO This should also take a DynamicBuffer with any leftover bytes.
template<class Fields>
void
accept(http::header<true, Fields> const& request);
/** Respond to a WebSocket HTTP Upgrade request
This function is used to synchronously send the HTTP response to
a HTTP request possibly containing a WebSocket Upgrade request.
The call blocks until one of the following conditions is true:
@li A HTTP 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 `write_some` functions.
If the passed HTTP request is a valid HTTP WebSocket Upgrade
request, a 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 request is invalid or cannot be satisfied, a HTTP
response is sent indicating the reason and status code (typically
400, "Bad Request"). This counts as a failure.
@param request An object containing the HTTP Upgrade request.
Ownership is not transferred, the implementation will not access
this object from other threads.
@param ec Set to indicate what error occurred, if any.
*/
template<class Fields>
void
accept(http::header<true, Fields> const& request, error_code& ec);
/** Start responding to a WebSocket HTTP Upgrade request. /** Start responding to a WebSocket HTTP Upgrade request.
This function is used to asynchronously send the HTTP response This function is used to asynchronously send the HTTP response
to a HTTP request possibly containing a WebSocket Upgrade request. to an HTTP request possibly containing a WebSocket Upgrade
The function call always returns immediately. The asynchronous request. The function call always returns immediately. The
operation will continue until one of the following conditions is asynchronous operation will continue until one of the following
true: conditions is true:
@li A HTTP response finishes sending. @li The HTTP response finishes sending.
@li An error occurs on the stream. @li An error occurs on the stream.
This operation is implemented in terms of one or more calls to the This operation is implemented in terms of one or more calls to
next layer's `async_write_some` functions, and is known as a the next layer's `async_write_some` functions, and is known as
<em>composed operation</em>. The program must ensure that the a <em>composed operation</em>. The program must ensure that the
stream performs no other operations until this operation completes. stream performs no other operations until this operation
completes.
If the passed HTTP request is a valid HTTP WebSocket Upgrade If the stream receives a valid HTTP WebSocket Upgrade request,
request, a HTTP response is sent back indicating a successful an HTTP response is sent back indicating a successful upgrade.
upgrade. When this asynchronous operation completes, the stream is When the completion handler is invoked, the stream is then
then ready to send and receive WebSocket protocol frames and messages. ready to send and receive WebSocket protocol frames and
messages.
If the HTTP request is invalid or cannot be satisfied, a HTTP If the HTTP Upgrade request is invalid or cannot be satisfied,
response is sent indicating the reason and status code (typically an HTTP response is sent indicating the reason and status code
400, "Bad Request"). This counts as a failure. (typically 400, "Bad Request"). This counts as a failure, and
the completion handler will be invoked with a suitable error
code set.
@param request An object containing the HTTP Upgrade request. @param req An object containing the HTTP Upgrade request.
Ownership is not transferred, the implementation will not access Ownership is not transferred, the implementation will not access
this object from other threads. this object from other threads.
@param handler The handler to be called when the request completes. @param handler The handler to be called when the request
Copies will be made of the handler as required. The equivalent completes. Copies will be made of the handler as required. The
function signature of the handler must be: equivalent function signature of the handler must be:
@code void handler( @code void handler(
error_code const& error // result of operation error_code const& error // result of operation
); @endcode ); @endcode
@@ -678,19 +758,83 @@ public:
#if BEAST_DOXYGEN #if BEAST_DOXYGEN
void_or_deduced void_or_deduced
#else #else
typename async_completion< typename async_completion<AcceptHandler,
AcceptHandler, void(error_code)>::result_type void(error_code)>::result_type
#endif #endif
async_accept(http::header<true, async_accept(http::header<true, Fields> const& req,
Fields> const& request, AcceptHandler&& handler); AcceptHandler&& handler);
/** Send a HTTP WebSocket Upgrade request and receive the response. /** 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 HTTP 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 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& error // 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 Fields,
class ConstBufferSequence, class AcceptHandler>
#if BEAST_DOXYGEN
void_or_deduced
#else
typename async_completion<AcceptHandler,
void(error_code)>::result_type
#endif
async_accept(http::header<true, Fields> const& req,
ConstBufferSequence const& buffers,
AcceptHandler&& handler);
/** Send an HTTP WebSocket Upgrade request and receive the response.
This function is used to synchronously send the WebSocket This function is used to synchronously send the WebSocket
upgrade HTTP request. The call blocks until one of the upgrade HTTP request. The call blocks until one of the
following conditions is true: following conditions is true:
@li A HTTP request finishes sending and a HTTP response finishes @li A HTTP request finishes sending and an HTTP response finishes
receiving. receiving.
@li An error occurs on the stream @li An error occurs on the stream
@@ -728,13 +872,13 @@ public:
handshake(boost::string_ref const& host, handshake(boost::string_ref const& host,
boost::string_ref const& resource); boost::string_ref const& resource);
/** Send a HTTP WebSocket Upgrade request and receive the response. /** Send an HTTP WebSocket Upgrade request and receive the response.
This function is used to synchronously send the WebSocket This function is used to synchronously send the WebSocket
upgrade HTTP request. The call blocks until one of the upgrade HTTP request. The call blocks until one of the
following conditions is true: following conditions is true:
@li A HTTP request finishes sending and a HTTP response finishes @li A HTTP request finishes sending and an HTTP response finishes
receiving. receiving.
@li An error occurs on the stream @li An error occurs on the stream
@@ -778,7 +922,7 @@ public:
operation will continue until one of the following conditions is operation will continue until one of the following conditions is
true: true:
@li A HTTP request finishes sending and a HTTP response finishes @li A HTTP request finishes sending and an HTTP response finishes
receiving. receiving.
@li An error occurs on the stream. @li An error occurs on the stream.
@@ -1673,6 +1817,14 @@ private:
void void
reset(); reset();
void
do_accept(error_code& ec);
template<class Fields>
void
do_accept(http::header<true, Fields> const& req,
error_code& ec);
http::request_header http::request_header
build_request(boost::string_ref const& host, build_request(boost::string_ref const& host,
boost::string_ref const& resource, boost::string_ref const& resource,

View File

@@ -15,6 +15,8 @@
#include <beast/core/to_string.hpp> #include <beast/core/to_string.hpp>
#include <beast/test/fail_stream.hpp> #include <beast/test/fail_stream.hpp>
#include <beast/test/string_istream.hpp> #include <beast/test/string_istream.hpp>
#include <beast/test/string_iostream.hpp>
#include <beast/test/string_ostream.hpp>
#include <beast/test/yield_to.hpp> #include <beast/test/yield_to.hpp>
#include <beast/unit_test/suite.hpp> #include <beast/unit_test/suite.hpp>
#include <boost/asio.hpp> #include <boost/asio.hpp>
@@ -136,6 +138,274 @@ public:
} }
}; };
struct SyncClient
{
template<class NextLayer>
void
accept(stream<NextLayer>& ws) const
{
ws.accept();
}
template<class NextLayer, class Buffers>
void
accept(stream<NextLayer>& ws,
Buffers const& buffers) const
{
ws.accept(buffers);
}
template<class NextLayer, class Fields>
void
accept(stream<NextLayer>& ws,
http::header<true, Fields> const& req) const
{
ws.accept(req);
}
template<class NextLayer,
class Fields, class Buffers>
void
accept(stream<NextLayer>& ws,
http::header<true, Fields> const& req,
Buffers const& buffers) const
{
ws.accept(req, buffers);
}
template<class NextLayer>
void
handshake(stream<NextLayer>& ws,
boost::string_ref const& uri,
boost::string_ref const& path) const
{
ws.handshake(uri, path);
}
template<class NextLayer>
void
ping(stream<NextLayer>& ws,
ping_data const& payload) const
{
ws.ping(payload);
}
template<class NextLayer>
void
pong(stream<NextLayer>& ws,
ping_data const& payload) const
{
ws.pong(payload);
}
template<class NextLayer>
void
close(stream<NextLayer>& ws,
close_reason const& cr) const
{
ws.close(cr);
}
template<
class NextLayer, class DynamicBuffer>
void
read(stream<NextLayer>& ws,
opcode& op, DynamicBuffer& dynabuf) const
{
ws.read(op, dynabuf);
}
template<
class NextLayer, class ConstBufferSequence>
void
write(stream<NextLayer>& ws,
ConstBufferSequence const& buffers) const
{
ws.write(buffers);
}
template<
class NextLayer, class ConstBufferSequence>
void
write_frame(stream<NextLayer>& ws, bool fin,
ConstBufferSequence const& buffers) const
{
ws.write_frame(fin, buffers);
}
template<
class NextLayer, class ConstBufferSequence>
void
write_raw(stream<NextLayer>& ws,
ConstBufferSequence const& buffers) const
{
boost::asio::write(
ws.next_layer(), buffers);
}
};
class AsyncClient
{
yield_context& yield_;
public:
explicit
AsyncClient(yield_context& yield)
: yield_(yield)
{
}
template<class NextLayer>
void
accept(stream<NextLayer>& ws) const
{
error_code ec;
ws.async_accept(yield_[ec]);
if(ec)
throw system_error{ec};
}
template<class NextLayer, class Buffers>
void
accept(stream<NextLayer>& ws,
Buffers const& buffers) const
{
error_code ec;
ws.async_accept(buffers, yield_[ec]);
if(ec)
throw system_error{ec};
}
template<class NextLayer, class Fields>
void
accept(stream<NextLayer>& ws,
http::header<true, Fields> const& req) const
{
error_code ec;
ws.async_accept(req, yield_[ec]);
if(ec)
throw system_error{ec};
}
template<class NextLayer,
class Fields, class Buffers>
void
accept(stream<NextLayer>& ws,
http::header<true, Fields> 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 Fields, class Buffers>
void
accept(stream<NextLayer>& ws,
http::header<true, Fields> const& req,
Buffers const& buffers,
error_code& ec) const
{
ws.async_accept(req, buffers, yield_[ec]);
}
template<class NextLayer>
void
handshake(stream<NextLayer>& ws,
boost::string_ref const& uri,
boost::string_ref const& path) const
{
error_code ec;
ws.async_handshake(uri, path, yield_[ec]);
if(ec)
throw system_error{ec};
}
template<class NextLayer>
void
ping(stream<NextLayer>& ws,
ping_data const& payload) const
{
error_code ec;
ws.async_ping(payload, yield_[ec]);
if(ec)
throw system_error{ec};
}
template<class NextLayer>
void
pong(stream<NextLayer>& ws,
ping_data const& payload) const
{
error_code ec;
ws.async_pong(payload, yield_[ec]);
if(ec)
throw system_error{ec};
}
template<class NextLayer>
void
close(stream<NextLayer>& ws,
close_reason const& cr) const
{
error_code ec;
ws.async_close(cr, yield_[ec]);
if(ec)
throw system_error{ec};
}
template<
class NextLayer, class DynamicBuffer>
void
read(stream<NextLayer>& ws,
opcode& op, DynamicBuffer& dynabuf) const
{
error_code ec;
ws.async_read(op, dynabuf, yield_[ec]);
if(ec)
throw system_error{ec};
}
template<
class NextLayer, class ConstBufferSequence>
void
write(stream<NextLayer>& ws,
ConstBufferSequence const& buffers) const
{
error_code ec;
ws.async_write(buffers, yield_[ec]);
if(ec)
throw system_error{ec};
}
template<
class NextLayer, class ConstBufferSequence>
void
write_frame(stream<NextLayer>& ws, bool fin,
ConstBufferSequence const& buffers) const
{
error_code ec;
ws.async_write_frame(fin, buffers, yield_[ec]);
if(ec)
throw system_error{ec};
}
template<
class NextLayer, class ConstBufferSequence>
void
write_raw(stream<NextLayer>& ws,
ConstBufferSequence const& buffers) const
{
error_code ec;
boost::asio::async_write(
ws.next_layer(), buffers, yield_[ec]);
if(ec)
throw system_error{ec};
}
};
void void
testOptions() testOptions()
{ {
@@ -166,73 +436,203 @@ public:
} }
} }
void testAccept() template<class Client>
void
testAccept(Client const& c)
{ {
static std::size_t constexpr limit = 200;
std::size_t n;
for(n = 0; n < limit; ++n)
{ {
static std::size_t constexpr limit = 100; test::fail_counter fc{n};
std::size_t n;
for(n = 0; n < limit; ++n)
{
// valid
http::request_header 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_istream>> ws(n, ios_, "");
try
{
ws.accept(req);
break;
}
catch(system_error const&)
{
}
}
BEAST_EXPECT(n < limit);
}
{
// valid
stream<test::string_istream> ws(ios_,
"GET / HTTP/1.1\r\n"
"Host: localhost:80\r\n"
"Upgrade: WebSocket\r\n"
"Connection: upgrade\r\n"
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
"Sec-WebSocket-Version: 13\r\n"
"\r\n"
);
try try
{ {
ws.accept(); // request in stream
pass(); {
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};
c.accept(ws);
//log << ws.next_layer().str << std::endl;
}
// request in buffers
{
stream<test::fail_stream<
test::string_ostream>> ws{fc, ios_};
c.accept(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"
));
}
// request in buffers and stream
{
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};
c.accept(ws, sbuf(
"GET / HTTP/1.1\r\n"
"Host: localhost\r\n"
"Upgrade: websocket\r\n"
));
}
// request in message
{
http::request_header 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_};
c.accept(ws, req);
}
// request in message, close frame in buffers
{
http::request_header 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_};
c.accept(ws, req,
cbuf(0x88, 0x82, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x17));
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;
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_iostream>> ws{fc, ios_,
"\x88\x82\xff\xff\xff\xff\xfc\x17"};
c.accept(ws, req);
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 and buffers
{
http::request_header 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_iostream>> ws{fc, ios_,
"xff\xff\xfc\x17"};
c.accept(ws, req,
cbuf(0x88, 0x82, 0xff, 0xff));
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;
}
}
// failed handshake (missing Sec-WebSocket-Key)
{
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-Version: 13\r\n"
"\r\n"
, 20};
try
{
c.accept(ws);
fail("success", __FILE__, __LINE__);
}
catch(system_error const& e)
{
if(e.code() !=
websocket::error::handshake_failed)
throw;
}
}
} }
catch(system_error const&) catch(system_error const&)
{ {
fail(); continue;
} }
break;
} }
{ BEAST_EXPECT(n < limit);
// invalid }
stream<test::string_istream> ws(ios_,
"GET / HTTP/1.0\r\n" void
"\r\n" testAccept()
); {
try testAccept(SyncClient{});
yield_to(
[&](yield_context yield)
{ {
ws.accept(); testAccept(AsyncClient{yield});
fail(); });
}
catch(system_error const&)
{
pass();
}
}
} }
void testBadHandshakes() void testBadHandshakes()
@@ -863,185 +1263,6 @@ public:
} }
} }
struct SyncClient
{
template<class NextLayer>
void
handshake(stream<NextLayer>& ws,
boost::string_ref const& uri,
boost::string_ref const& path) const
{
ws.handshake(uri, path);
}
template<class NextLayer>
void
ping(stream<NextLayer>& ws,
ping_data const& payload) const
{
ws.ping(payload);
}
template<class NextLayer>
void
pong(stream<NextLayer>& ws,
ping_data const& payload) const
{
ws.pong(payload);
}
template<class NextLayer>
void
close(stream<NextLayer>& ws,
close_reason const& cr) const
{
ws.close(cr);
}
template<
class NextLayer, class DynamicBuffer>
void
read(stream<NextLayer>& ws,
opcode& op, DynamicBuffer& dynabuf) const
{
ws.read(op, dynabuf);
}
template<
class NextLayer, class ConstBufferSequence>
void
write(stream<NextLayer>& ws,
ConstBufferSequence const& buffers) const
{
ws.write(buffers);
}
template<
class NextLayer, class ConstBufferSequence>
void
write_frame(stream<NextLayer>& ws, bool fin,
ConstBufferSequence const& buffers) const
{
ws.write_frame(fin, buffers);
}
template<
class NextLayer, class ConstBufferSequence>
void
write_raw(stream<NextLayer>& ws,
ConstBufferSequence const& buffers) const
{
boost::asio::write(
ws.next_layer(), buffers);
}
};
class AsyncClient
{
yield_context& yield_;
public:
explicit
AsyncClient(yield_context& yield)
: yield_(yield)
{
}
template<class NextLayer>
void
handshake(stream<NextLayer>& ws,
boost::string_ref const& uri,
boost::string_ref const& path) const
{
error_code ec;
ws.async_handshake(uri, path, yield_[ec]);
if(ec)
throw system_error{ec};
}
template<class NextLayer>
void
ping(stream<NextLayer>& ws,
ping_data const& payload) const
{
error_code ec;
ws.async_ping(payload, yield_[ec]);
if(ec)
throw system_error{ec};
}
template<class NextLayer>
void
pong(stream<NextLayer>& ws,
ping_data const& payload) const
{
error_code ec;
ws.async_pong(payload, yield_[ec]);
if(ec)
throw system_error{ec};
}
template<class NextLayer>
void
close(stream<NextLayer>& ws,
close_reason const& cr) const
{
error_code ec;
ws.async_close(cr, yield_[ec]);
if(ec)
throw system_error{ec};
}
template<
class NextLayer, class DynamicBuffer>
void
read(stream<NextLayer>& ws,
opcode& op, DynamicBuffer& dynabuf) const
{
error_code ec;
ws.async_read(op, dynabuf, yield_[ec]);
if(ec)
throw system_error{ec};
}
template<
class NextLayer, class ConstBufferSequence>
void
write(stream<NextLayer>& ws,
ConstBufferSequence const& buffers) const
{
error_code ec;
ws.async_write(buffers, yield_[ec]);
if(ec)
throw system_error{ec};
}
template<
class NextLayer, class ConstBufferSequence>
void
write_frame(stream<NextLayer>& ws, bool fin,
ConstBufferSequence const& buffers) const
{
error_code ec;
ws.async_write_frame(fin, buffers, yield_[ec]);
if(ec)
throw system_error{ec};
}
template<
class NextLayer, class ConstBufferSequence>
void
write_raw(stream<NextLayer>& ws,
ConstBufferSequence const& buffers) const
{
error_code ec;
boost::asio::async_write(
ws.next_layer(), buffers, yield_[ec]);
if(ec)
throw system_error{ec};
}
};
struct abort_test struct abort_test
{ {
}; };