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"
API Changes:
* Provide websocket::stream accept() overloads
--------------------------------------------------------------------------------
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/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>
@@ -125,6 +126,26 @@ public:
error_code{}, boost::asio::buffer_size(buffers)));
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

View File

@@ -11,6 +11,7 @@
#include <beast/core/async_completion.hpp>
#include <beast/core/bind_handler.hpp>
#include <beast/core/error.hpp>
#include <beast/websocket/teardown.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/asio/io_service.hpp>
#include <string>
@@ -110,6 +111,26 @@ public:
bind_handler(completion.handler, ec, bytes_transferred));
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

View File

@@ -36,7 +36,6 @@ class stream<NextLayer>::response_op
bool cont;
stream<NextLayer>& ws;
http::response_header res;
error_code final_ec;
int state = 0;
template<class Fields>
@@ -47,11 +46,22 @@ class stream<NextLayer>::response_op
, ws(ws_)
, res(ws_.build_response(req))
{
// can't call stream::reset() here
// otherwise accept_op will malfunction
//
if(res.status != 101)
final_ec = error::handshake_failed;
}
template<class Fields, class Buffers>
data(Handler&, stream<NextLayer>& ws_,
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
case 1:
d.state = 99;
ec = d.final_ec;
if(d.res.status != 101)
ec = error::handshake_failed;
if(! ec)
{
pmd_read(
@@ -154,6 +165,10 @@ class stream<NextLayer>::accept_op
http::header_parser<true, http::fields> p;
int state = 0;
// VFALCO These lines are formatted to work around a codecov defect
data(Handler& handler, stream<NextLayer>& ws_) : cont(beast_asio_helpers::is_continuation(handler))
, ws(ws_) {}
template<class Buffers>
data(Handler& handler, stream<NextLayer>& ws_,
Buffers const& buffers)
@@ -163,7 +178,7 @@ class stream<NextLayer>::accept_op
{
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
ws.reset();
// VFALCO What about catch(std::length_error const&)?
ws.stream_.buffer().commit(buffer_copy(
ws.stream_.buffer().prepare(
buffer_size(buffers)), buffers));
@@ -185,11 +200,6 @@ public:
(*this)(error_code{}, 0, false);
}
void operator()(error_code const& ec)
{
(*this)(ec, 0);
}
void operator()(error_code ec,
std::size_t bytes_used, bool again = true);
@@ -264,59 +274,7 @@ upcall:
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>
void
@@ -326,7 +284,7 @@ accept()
static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met");
error_code ec;
accept(boost::asio::null_buffers{}, ec);
accept(ec);
if(ec)
throw system_error{ec};
}
@@ -338,7 +296,8 @@ accept(error_code& ec)
{
static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met");
accept(boost::asio::null_buffers{}, ec);
reset();
do_accept(ec);
}
template<class NextLayer>
@@ -366,35 +325,28 @@ accept(ConstBufferSequence const& buffers, error_code& ec)
{
static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met");
static_assert(beast::is_ConstBufferSequence<
static_assert(is_ConstBufferSequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
reset();
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
reset();
stream_.buffer().commit(buffer_copy(
stream_.buffer().prepare(
buffer_size(buffers)), buffers));
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);
accept(p.get(), ec);
do_accept(ec);
}
template<class NextLayer>
template<class Fields>
void
stream<NextLayer>::
accept(http::header<true, Fields> const& request)
accept(http::header<true, Fields> const& req)
{
static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met");
error_code ec;
accept(request, ec);
accept(req, ec);
if(ec)
throw system_error{ec};
}
@@ -409,19 +361,134 @@ accept(http::header<true, Fields> const& req,
static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met");
reset();
auto const res = build_response(req);
http::write(stream_, res, ec);
do_accept(req, 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)
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);
throw system_error{ec};
}
template<class NextLayer>
template<class Fields, class ConstBufferSequence>
void
stream<NextLayer>::
accept(
http::header<true, Fields> const& req,
ConstBufferSequence const& buffers,
error_code& ec)
{
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

View File

@@ -82,6 +82,43 @@ reset()
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>
http::request_header
stream<NextLayer>::

View File

@@ -13,14 +13,15 @@
#include <beast/websocket/detail/stream_base.hpp>
#include <beast/http/message.hpp>
#include <beast/http/string_body.hpp>
#include <beast/core/dynabuf_readstream.hpp>
#include <beast/core/async_completion.hpp>
#include <beast/core/dynabuf_readstream.hpp>
#include <beast/core/detail/get_lowest_layer.hpp>
#include <boost/asio.hpp>
#include <boost/utility/string_ref.hpp>
#include <algorithm>
#include <cstdint>
#include <limits>
#include <type_traits>
namespace beast {
namespace websocket {
@@ -340,25 +341,25 @@ public:
/** 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:
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 A HTTP request finishes receiving, and a HTTP response finishes
sending.
@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.
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 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, a
HTTP response is sent indicating the reason and status code
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.
@throws system_error Thrown on failure.
@@ -368,25 +369,25 @@ public:
/** 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:
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 A HTTP request finishes receiving, and a HTTP response finishes
sending.
@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.
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 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, a
HTTP response is sent indicating the reason and status code
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 ec Set to indicate what error occurred, if any.
@@ -394,36 +395,245 @@ public:
void
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.
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
always returns immediately. The asynchronous operation will
continue until one of the following conditions is true:
@li A HTTP request finishes receiving, and a HTTP response finishes
sending.
@li The HTTP request finishes receiving, and 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_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 operations until this operation
completes.
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, 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 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, a
HTTP response is sent indicating the reason and status code
(typically 400, "Bad Request"). This counts as a failure.
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 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:
@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
@@ -441,104 +651,35 @@ public:
#endif
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.
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
always returns immediately. The asynchronous operation will
continue until one of the following conditions is true:
@li A HTTP request finishes receiving, and a HTTP response finishes
sending.
@li The HTTP request finishes receiving, and 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_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 operations until this operation
completes.
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, 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 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, a
HTTP response is sent indicating the reason and status code
(typically 400, "Bad Request"). This counts as a failure.
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 buffers Caller provided data that has already been
received on the stream. This may be used for implementations
@@ -547,9 +688,9 @@ public:
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:
@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
@@ -562,110 +703,49 @@ public:
#if BEAST_DOXYGEN
void_or_deduced
#else
typename async_completion<
AcceptHandler, void(error_code)>::result_type
typename async_completion<AcceptHandler,
void(error_code)>::result_type
#endif
async_accept(ConstBufferSequence const& buffers,
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.
This function is used to asynchronously send the HTTP response
to a 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:
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 A HTTP response finishes sending.
@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.
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 passed HTTP request is a valid HTTP WebSocket Upgrade
request, a HTTP response is sent back indicating a successful
upgrade. When this asynchronous operation completes, the stream is
then ready to send and receive WebSocket protocol frames and messages.
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 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.
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 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
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:
@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
@@ -678,19 +758,83 @@ public:
#if BEAST_DOXYGEN
void_or_deduced
#else
typename async_completion<
AcceptHandler, void(error_code)>::result_type
typename async_completion<AcceptHandler,
void(error_code)>::result_type
#endif
async_accept(http::header<true,
Fields> const& request, AcceptHandler&& handler);
async_accept(http::header<true, Fields> const& req,
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
upgrade HTTP request. The call blocks until one of the
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.
@li An error occurs on the stream
@@ -728,13 +872,13 @@ public:
handshake(boost::string_ref const& host,
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
upgrade HTTP request. The call blocks until one of the
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.
@li An error occurs on the stream
@@ -778,7 +922,7 @@ public:
operation will continue until one of the 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.
@li An error occurs on the stream.
@@ -1673,6 +1817,14 @@ private:
void
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
build_request(boost::string_ref const& host,
boost::string_ref const& resource,

View File

@@ -15,6 +15,8 @@
#include <beast/core/to_string.hpp>
#include <beast/test/fail_stream.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/unit_test/suite.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
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;
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"
);
test::fail_counter fc{n};
try
{
ws.accept();
pass();
// request in stream
{
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&)
{
fail();
continue;
}
break;
}
{
// invalid
stream<test::string_istream> ws(ios_,
"GET / HTTP/1.0\r\n"
"\r\n"
);
try
BEAST_EXPECT(n < limit);
}
void
testAccept()
{
testAccept(SyncClient{});
yield_to(
[&](yield_context yield)
{
ws.accept();
fail();
}
catch(system_error const&)
{
pass();
}
}
testAccept(AsyncClient{yield});
});
}
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
{
};