diff --git a/TODO.txt b/TODO.txt index 17bf16ac..16161ee5 100644 --- a/TODO.txt +++ b/TODO.txt @@ -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 diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e384dcdf..e91381fd 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -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 diff --git a/test/fail_stream.hpp b/test/fail_stream.hpp new file mode 100644 index 00000000..f7180dff --- /dev/null +++ b/test/fail_stream.hpp @@ -0,0 +1,209 @@ +//------------------------------------------------------------------------------ +/* + This file is part of Beast: https://github.com/vinniefalco/Beast + Copyright 2013, Vinnie Falco + + 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 +#include +#include +#include + +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 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::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 + 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)...) + { + } + + 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 + std::size_t + read_some(MutableBufferSequence const& buffers) + { + fail(); + return next_layer_.read_some(buffers); + } + + template + std::size_t + read_some(MutableBufferSequence const& buffers, error_code& ec) + { + if(fail(ec)) + return 0; + return next_layer_.read_some(buffers, ec); + } + + template + 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(handler)); + } + + template + std::size_t + write_some(ConstBufferSequence const& buffers) + { + fail(); + return next_layer_.write_some(buffers); + } + + template + std::size_t + write_some(ConstBufferSequence const& buffers, error_code& ec) + { + if(fail(ec)) + return 0; + return next_layer_.write_some(buffers, ec); + } + + template + 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(handler)); + } +}; + +template +void +teardown(fail_stream& stream, + boost::system::error_code& ec) +{ + websocket_helpers::call_teardown(stream.next_layer(), ec); +} + +template +void +async_teardown(fail_stream& stream, + TeardownHandler&& handler) +{ + websocket_helpers::call_async_teardown( + stream.next_layer(), std::forward(handler)); +} + +} // beast + +#endif diff --git a/test/websocket/stream.cpp b/test/websocket/stream.cpp index 002cd53f..ef3524f2 100644 --- a/test/websocket/stream.cpp +++ b/test/websocket/stream.cpp @@ -8,11 +8,13 @@ // Test that header file is self-contained. #include +#include "../fail_stream.hpp" #include "websocket_async_echo_peer.hpp" #include "websocket_sync_echo_peer.hpp" #include #include +#include #include #include #include @@ -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> 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> 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> 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();