websocket::stream fail testing

This commit is contained in:
Vinnie Falco
2016-05-04 11:06:17 -04:00
parent aea2f939d7
commit 231988622c
4 changed files with 344 additions and 1 deletions

View File

@ -21,7 +21,7 @@ Docs:
Core:
* Replace Jamroot with Jamfile
* Fix bidirectional buffers iterators operator->()
* Complete allocator testing in basic_streambuf, basic_headers
* Complete allocator testing in basic_streambuf
WebSocket:
* optimized versions of key/masking, choose prepared_key size
@ -43,3 +43,8 @@ HTTP:
* URL parser, strong URL checking in HTTP parser
* Update for rfc7230
* Consider rename to MessageBody concept
* Fix prepare() calling content_length() without init()
* Use construct,destroy allocator routines in basic_headers
* Complete allocator testing in basic_streambuf, basic_headers
* Fix http::async_write op, case 3 should break at the end.
* Add tests for writer using the resume function / coros

View File

@ -78,6 +78,7 @@ endif()
add_executable (websocket-tests
${BEAST_INCLUDES}
fail_stream.hpp
websocket/websocket_async_echo_peer.hpp
websocket/websocket_sync_echo_peer.hpp
main.cpp

209
test/fail_stream.hpp Normal file
View File

@ -0,0 +1,209 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef BEAST_TEST_FAIL_STREAM_HPP
#define BEAST_TEST_FAIL_STREAM_HPP
#include <beast/async_completion.hpp>
#include <beast/bind_handler.hpp>
#include <beast/detail/get_lowest_layer.hpp>
#include <boost/system/error_code.hpp>
namespace beast {
/* A stream wrapper that fails.
On the Nth operation, the stream will fail with the specified
error code, or the default error code of invalid_argument.
*/
template<class NextLayer>
class fail_stream
{
using error_code =
boost::system::error_code;
using system_error =
boost::system::system_error;
error_code ec_;
std::size_t n_ = 0;
NextLayer next_layer_;
void
fail()
{
if(n_ > 0)
--n_;
if(! n_)
throw system_error{ec_};
}
bool
fail(error_code& ec)
{
if(n_ > 0)
--n_;
if(! n_)
{
ec = ec_;
return true;
}
return false;
}
public:
using next_layer_type =
typename std::remove_reference<NextLayer>::type;
using lowest_layer_type =
typename beast::detail::get_lowest_layer<
next_layer_type>::type;
fail_stream(fail_stream&&) = default;
fail_stream& operator=(fail_stream&&) = default;
template<class... Args>
explicit
fail_stream(std::size_t n, Args&&... args)
: ec_(boost::system::errc::make_error_code(
boost::system::errc::errc_t::invalid_argument))
, n_(n)
, next_layer_(std::forward<Args>(args)...)
{
}
next_layer_type&
next_layer()
{
return next_layer_;
}
lowest_layer_type&
lowest_layer()
{
return next_layer_.lowest_layer();
}
lowest_layer_type const&
lowest_layer() const
{
return next_layer_.lowest_layer();
}
boost::asio::io_service&
get_io_service()
{
return next_layer_.get_io_service();
}
template<class MutableBufferSequence>
std::size_t
read_some(MutableBufferSequence const& buffers)
{
fail();
return next_layer_.read_some(buffers);
}
template<class MutableBufferSequence>
std::size_t
read_some(MutableBufferSequence const& buffers, error_code& ec)
{
if(fail(ec))
return 0;
return next_layer_.read_some(buffers, ec);
}
template<class MutableBufferSequence, class ReadHandler>
typename async_completion<
ReadHandler, void(error_code)>::result_type
async_read_some(MutableBufferSequence const& buffers,
ReadHandler&& handler)
{
error_code ec;
if(fail(ec))
{
async_completion<
ReadHandler, void(error_code, std::size_t)
> completion(handler);
next_layer_.get_io_service().post(
bind_handler(completion.handler, ec, 0));
return completion.result.get();
}
return next_layer_.async_read_some(buffers,
std::forward<ReadHandler>(handler));
}
template<class ConstBufferSequence>
std::size_t
write_some(ConstBufferSequence const& buffers)
{
fail();
return next_layer_.write_some(buffers);
}
template<class ConstBufferSequence>
std::size_t
write_some(ConstBufferSequence const& buffers, error_code& ec)
{
if(fail(ec))
return 0;
return next_layer_.write_some(buffers, ec);
}
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;
if(fail(ec))
{
async_completion<
WriteHandler, void(error_code, std::size_t)
> completion(handler);
next_layer_.get_io_service().post(
bind_handler(completion.handler, ec, 0));
return completion.result.get();
}
return next_layer_.async_write_some(buffers,
std::forward<WriteHandler>(handler));
}
};
template<class NextLayer>
void
teardown(fail_stream<NextLayer>& stream,
boost::system::error_code& ec)
{
websocket_helpers::call_teardown(stream.next_layer(), ec);
}
template<class NextLayer, class TeardownHandler>
void
async_teardown(fail_stream<NextLayer>& stream,
TeardownHandler&& handler)
{
websocket_helpers::call_async_teardown(
stream.next_layer(), std::forward<TeardownHandler>(handler));
}
} // beast
#endif

View File

@ -8,11 +8,13 @@
// Test that header file is self-contained.
#include <beast/websocket/stream.hpp>
#include "../fail_stream.hpp"
#include "websocket_async_echo_peer.hpp"
#include "websocket_sync_echo_peer.hpp"
#include <beast/bind_handler.hpp>
#include <beast/streambuf.hpp>
#include <beast/to_string.hpp>
#include <beast/detail/unit_test/suite.hpp>
#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>
@ -420,6 +422,122 @@ public:
}
}
void testErrorHandling(endpoint_type const& ep,
boost::asio::yield_context do_yield)
{
static std::size_t constexpr limit = 100;
std::size_t n;
// synchronous, exceptions
for(n = 1; n < limit; ++n)
{
error_code ec;
socket_type sock(ios_);
sock.connect(ep, ec);
if(! expect(! ec, ec.message()))
break;
stream<fail_stream<socket_type&>> ws(n, sock);
try
{
ws.handshake("localhost", "/");
ws.write(boost::asio::const_buffers_1(
"Hello", 5));
opcode op;
streambuf sb;
ws.read(op, sb);
expect(op == opcode::text);
expect(to_string(sb.data()) == "Hello");
ws.close({});
try
{
ws.read(op, sb);
}
catch(boost::system::system_error const& se)
{
if(se.code() == error::closed)
break;
throw;
}
fail();
break;
}
catch(boost::system::system_error const&)
{
}
}
expect(n < limit);
// synchronous, error codes
for(n = 1; n < limit; ++n)
{
error_code ec;
socket_type sock(ios_);
sock.connect(ep, ec);
if(! expect(! ec, ec.message()))
break;
stream<fail_stream<socket_type&>> ws(n, sock);
ws.handshake("localhost", "/", ec);
if(ec)
continue;
ws.write(boost::asio::const_buffers_1(
"Hello", 5), ec);
if(ec)
continue;
opcode op;
streambuf sb;
ws.read(op, sb, ec);
if(ec)
continue;
expect(op == opcode::text);
expect(to_string(sb.data()) == "Hello");
ws.close({}, ec);
if(ec)
continue;
ws.read(op, sb, ec);
if(ec == error::closed)
{
pass();
break;
}
}
expect(n < limit);
// asynchronous
for(n = 1; n < limit; ++n)
{
error_code ec;
socket_type sock(ios_);
sock.connect(ep, ec);
if(! expect(! ec, ec.message()))
break;
stream<fail_stream<socket_type&>> ws(n, sock);
ws.async_handshake("localhost", "/", do_yield[ec]);
if(ec)
break;
ws.async_write(boost::asio::const_buffers_1(
"Hello", 5), do_yield[ec]);
if(ec)
continue;
opcode op;
streambuf sb;
ws.async_read(op, sb, do_yield[ec]);
if(ec)
continue;
expect(op == opcode::text);
expect(to_string(sb.data()) == "Hello");
ws.async_close({}, do_yield[ec]);
if(ec)
continue;
ws.async_read(op, sb, do_yield[ec]);
if(ec == error::closed)
{
pass();
break;
}
}
expect(n < limit);
}
void run() override
{
testSpecialMembers();
@ -433,15 +551,25 @@ public:
address_type::from_string("127.0.0.1"), 0};
{
sync_echo_peer server(true, any);
exec(std::bind(&stream_test::testHandshake,
this, server.local_endpoint(),
std::placeholders::_1));
exec(std::bind(&stream_test::testErrorHandling,
this, server.local_endpoint(),
std::placeholders::_1));
}
{
async_echo_peer server(true, any, 1);
exec(std::bind(&stream_test::testHandshake,
this, server.local_endpoint(),
std::placeholders::_1));
exec(std::bind(&stream_test::testErrorHandling,
this, server.local_endpoint(),
std::placeholders::_1));
}
pass();