Add websocket::stream timeouts

This commit is contained in:
Vinnie Falco committed 2019-02-18 12:18:27 -08:00
1 parent dfd08bf6ae
commit f21358186e
38 files changed
+3305 -2393

No files matched your search

+2 -1
View File
@@ -17,7 +17,7 @@ add_executable (tests-beast-websocket
${TEST_MAIN}
Jamfile
_detail_prng.cpp
_detail_stream_base.cpp
_detail_impl_base.cpp
test.hpp
_detail_prng.cpp
accept.cpp
@@ -29,6 +29,7 @@ add_executable (tests-beast-websocket
ping.cpp
read1.cpp
read2.cpp
read3.cpp
rfc6455.cpp
role.cpp
stream.cpp
+2 -1
View File
@@ -8,8 +8,8 @@
#
local SOURCES =
_detail_impl_base.cpp
_detail_prng.cpp
_detail_stream_base.cpp
accept.cpp
close.cpp
error.cpp
@@ -19,6 +19,7 @@ local SOURCES =
ping.cpp
read1.cpp
read2.cpp
read3.cpp
rfc6455.cpp
role.cpp
stream.cpp
@@ -1,5 +1,5 @@
//
// Copyright (w) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
// Copyright (c) 2016-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)
@@ -8,4 +8,4 @@
//
// Test that header file is self-contained.
#include <boost/beast/websocket/detail/stream_base.hpp>
#include <boost/beast/websocket/detail/impl_base.hpp>
+1 -1
View File
@@ -1,5 +1,5 @@
//
// Copyright (w) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
// Copyright (c) 2016-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)
+91 -1
View File
@@ -1,5 +1,5 @@
//
// Copyright (w) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
// Copyright (c) 2016-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)
@@ -10,6 +10,7 @@
// Test that header file is self-contained.
#include <boost/beast/websocket/stream.hpp>
#include <boost/beast/_experimental/test/tcp.hpp>
#include "test.hpp"
#include <boost/asio/io_context.hpp>
@@ -613,6 +614,94 @@ public:
);
}
void
testTimeout()
{
using tcp = net::ip::tcp;
net::io_context ioc;
// success
{
stream<tcp::socket> ws1(ioc);
stream<tcp::socket> ws2(ioc);
test::connect(ws1.next_layer(), ws2.next_layer());
ws1.async_handshake("test", "/", test::success_handler());
ws2.async_accept(test::success_handler());
test::run_for(ioc, std::chrono::seconds(1));
}
{
stream<test::stream> ws1(ioc);
stream<test::stream> ws2(ioc);
test::connect(ws1.next_layer(), ws2.next_layer());
ws1.async_handshake("test", "/", test::success_handler());
ws2.async_accept(test::success_handler());
test::run_for(ioc, std::chrono::seconds(1));
}
// success, timeout enabled
{
stream<tcp::socket> ws1(ioc);
stream<tcp::socket> ws2(ioc);
test::connect(ws1.next_layer(), ws2.next_layer());
ws1.set_option(stream_base::timeout{
std::chrono::milliseconds(50),
stream_base::none(),
false});
ws1.async_accept(test::success_handler());
ws2.async_handshake("test", "/", test::success_handler());
test::run_for(ioc, std::chrono::seconds(1));
}
{
stream<test::stream> ws1(ioc);
stream<test::stream> ws2(ioc);
test::connect(ws1.next_layer(), ws2.next_layer());
ws1.set_option(stream_base::timeout{
std::chrono::milliseconds(50),
stream_base::none(),
false});
ws1.async_accept(test::success_handler());
ws2.async_handshake("test", "/", test::success_handler());
test::run_for(ioc, std::chrono::seconds(1));
}
// timeout
{
stream<tcp::socket> ws1(ioc);
stream<tcp::socket> ws2(ioc);
test::connect(ws1.next_layer(), ws2.next_layer());
ws1.set_option(stream_base::timeout{
std::chrono::milliseconds(50),
stream_base::none(),
false});
ws1.async_accept(test::fail_handler(beast::error::timeout));
test::run_for(ioc, std::chrono::seconds(1));
}
{
stream<test::stream> ws1(ioc);
stream<test::stream> ws2(ioc);
test::connect(ws1.next_layer(), ws2.next_layer());
ws1.set_option(stream_base::timeout{
std::chrono::milliseconds(50),
stream_base::none(),
false});
ws1.async_accept(test::fail_handler(beast::error::timeout));
test::run_for(ioc, std::chrono::seconds(1));
}
}
void
testMoveOnly()
{
@@ -649,6 +738,7 @@ public:
run() override
{
testAccept();
testTimeout();
testMoveOnly();
testAsioHandlerInvoke();
}
+112 -17
View File
@@ -1,5 +1,5 @@
//
// Copyright (w) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
// Copyright (c) 2016-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)
@@ -10,6 +10,8 @@
// Test that header file is self-contained.
#include <boost/beast/websocket/stream.hpp>
#include <boost/beast/_experimental/test/stream.hpp>
#include <boost/beast/_experimental/test/tcp.hpp>
#include "test.hpp"
#include <boost/asio/io_context.hpp>
@@ -168,6 +170,114 @@ public:
});
}
void
testTimeout()
{
using tcp = net::ip::tcp;
net::io_context ioc;
// success
{
stream<tcp::socket> ws1(ioc);
stream<tcp::socket> ws2(ioc);
test::connect(ws1.next_layer(), ws2.next_layer());
ws1.async_handshake("test", "/", test::success_handler());
ws2.async_accept(test::success_handler());
test::run(ioc);
ws1.async_close({}, test::success_handler());
ws2.async_close({}, test::success_handler());
test::run(ioc);
}
{
stream<test::stream> ws1(ioc);
stream<test::stream> ws2(ioc);
test::connect(ws1.next_layer(), ws2.next_layer());
ws1.async_handshake("test", "/", test::success_handler());
ws2.async_accept(test::success_handler());
test::run(ioc);
ws1.async_close({}, test::success_handler());
ws2.async_close({}, test::success_handler());
test::run(ioc);
}
// success, timeout enabled
{
stream<tcp::socket> ws1(ioc);
stream<tcp::socket> ws2(ioc);
test::connect(ws1.next_layer(), ws2.next_layer());
ws1.async_handshake("test", "/", test::success_handler());
ws2.async_accept(test::success_handler());
test::run(ioc);
ws1.set_option(stream_base::timeout{
std::chrono::milliseconds(50),
stream_base::none(),
false});
ws1.async_close({}, test::success_handler());
ws2.async_close({}, test::success_handler());
test::run(ioc);
}
{
stream<test::stream> ws1(ioc);
stream<test::stream> ws2(ioc);
test::connect(ws1.next_layer(), ws2.next_layer());
ws1.async_handshake("test", "/", test::success_handler());
ws2.async_accept(test::success_handler());
test::run(ioc);
ws1.set_option(stream_base::timeout{
std::chrono::milliseconds(50),
stream_base::none(),
false});
ws1.async_close({}, test::success_handler());
ws2.async_close({}, test::success_handler());
test::run(ioc);
}
// timeout
{
stream<tcp::socket> ws1(ioc);
stream<tcp::socket> ws2(ioc);
test::connect(ws1.next_layer(), ws2.next_layer());
ws1.async_handshake("test", "/", test::success_handler());
ws2.async_accept(test::success_handler());
test::run(ioc);
ws1.set_option(stream_base::timeout{
std::chrono::milliseconds(50),
stream_base::none(),
false});
ws1.async_close({}, test::fail_handler(
beast::error::timeout));
test::run(ioc);
}
{
stream<test::stream> ws1(ioc);
stream<test::stream> ws2(ioc);
test::connect(ws1.next_layer(), ws2.next_layer());
ws1.async_handshake("test", "/", test::success_handler());
ws2.async_accept(test::success_handler());
test::run(ioc);
ws1.set_option(stream_base::timeout{
std::chrono::milliseconds(50),
stream_base::none(),
false});
ws1.async_close({}, test::fail_handler(
beast::error::timeout));
test::run(ioc);
}
}
void
testSuspend()
{
@@ -625,28 +735,13 @@ public:
}
};
void
testAsioHandlerInvoke()
{
// make sure things compile, also can set a
// breakpoint in asio_handler_invoke to make sure
// it is instantiated.
net::io_context ioc;
net::strand<
net::io_context::executor_type> s(
ioc.get_executor());
stream<test::stream> ws{ioc};
ws.async_close({}, net::bind_executor(
s, copyable_handler{}));
}
void
run() override
{
testClose();
testTimeout();
testSuspend();
testMoveOnly();
testAsioHandlerInvoke();
}
};
+95 -18
View File
@@ -1,5 +1,5 @@
//
// Copyright (w) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
// Copyright (c) 2016-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)
@@ -10,6 +10,9 @@
// Test that header file is self-contained.
#include <boost/beast/websocket/stream.hpp>
#include <boost/beast/_experimental/test/stream.hpp>
#include <boost/beast/_experimental/test/tcp.hpp>
#include "test.hpp"
#include <boost/asio/io_context.hpp>
@@ -235,6 +238,96 @@ public:
);
}
void
testTimeout()
{
using tcp = net::ip::tcp;
net::io_context ioc;
// success
{
stream<tcp::socket> ws1(ioc);
stream<tcp::socket> ws2(ioc);
test::connect(ws1.next_layer(), ws2.next_layer());
ws1.async_handshake("test", "/", test::success_handler());
ws2.async_accept(test::success_handler());
test::run_for(ioc, std::chrono::seconds(1));
}
{
stream<test::stream> ws1(ioc);
stream<test::stream> ws2(ioc);
test::connect(ws1.next_layer(), ws2.next_layer());
ws1.async_handshake("test", "/", test::success_handler());
ws2.async_accept(test::success_handler());
test::run_for(ioc, std::chrono::seconds(1));
}
// success, timeout enabled
{
stream<tcp::socket> ws1(ioc);
stream<tcp::socket> ws2(ioc);
test::connect(ws1.next_layer(), ws2.next_layer());
ws1.set_option(stream_base::timeout{
std::chrono::milliseconds(50),
stream_base::none(),
false});
ws1.async_handshake("test", "/", test::success_handler());
ws2.async_accept(test::success_handler());
test::run_for(ioc, std::chrono::seconds(1));
}
{
stream<test::stream> ws1(ioc);
stream<test::stream> ws2(ioc);
test::connect(ws1.next_layer(), ws2.next_layer());
ws1.set_option(stream_base::timeout{
std::chrono::milliseconds(50),
stream_base::none(),
false});
ws1.async_handshake("test", "/", test::success_handler());
ws2.async_accept(test::success_handler());
test::run_for(ioc, std::chrono::seconds(1));
}
// timeout
{
stream<tcp::socket> ws1(ioc);
stream<tcp::socket> ws2(ioc);
test::connect(ws1.next_layer(), ws2.next_layer());
ws1.set_option(stream_base::timeout{
std::chrono::milliseconds(50),
stream_base::none(),
false});
ws1.async_handshake("test", "/",
test::fail_handler(beast::error::timeout));
test::run_for(ioc, std::chrono::seconds(1));
}
{
stream<test::stream> ws1(ioc);
stream<test::stream> ws2(ioc);
test::connect(ws1.next_layer(), ws2.next_layer());
ws1.set_option(stream_base::timeout{
std::chrono::milliseconds(50),
stream_base::none(),
false});
ws1.async_handshake("test", "/",
test::fail_handler(beast::error::timeout));
test::run_for(ioc, std::chrono::seconds(1));
}
}
// Compression Extensions for WebSocket
//
// https://tools.ietf.org/html/rfc7692
@@ -504,31 +597,15 @@ public:
}
};
void
testAsioHandlerInvoke()
{
// make sure things compile, also can set a
// breakpoint in asio_handler_invoke to make sure
// it is instantiated.
net::io_context ioc;
net::strand<
net::io_context::executor_type> s(
ioc.get_executor());
stream<test::stream> ws{ioc};
ws.async_handshake("localhost", "/",
net::bind_executor(
s, copyable_handler{}));
}
void
run() override
{
testHandshake();
testTimeout();
testExtRead();
testExtWrite();
testExtNegotiate();
testMoveOnly();
testAsioHandlerInvoke();
}
};
+1 -17
View File
@@ -1,5 +1,5 @@
//
// Copyright (w) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
// Copyright (c) 2016-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)
@@ -440,28 +440,12 @@ public:
}
};
void
testAsioHandlerInvoke()
{
// make sure things compile, also can set a
// breakpoint in asio_handler_invoke to make sure
// it is instantiated.
net::io_context ioc;
net::strand<
net::io_context::executor_type> s(
ioc.get_executor());
stream<test::stream> ws{ioc};
ws.async_ping({}, net::bind_executor(
s, copyable_handler{}));
}
void
run() override
{
testPing();
testSuspend();
testMoveOnly();
testAsioHandlerInvoke();
}
};
+106 -639
View File
@@ -1,5 +1,5 @@
// Copyright (w) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Copyright (c) 2016-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)
@@ -10,672 +10,139 @@
// Test that header file is self-contained.
#include <boost/beast/websocket/stream.hpp>
#include "test.hpp"
#include <boost/asio/write.hpp>
#include <boost/config/workaround.hpp>
#if BOOST_WORKAROUND(BOOST_GCC, < 80200)
#define BOOST_BEAST_SYMBOL_HIDDEN __attribute__ ((visibility("hidden")))
#else
#define BOOST_BEAST_SYMBOL_HIDDEN
#endif
#include <boost/beast/_experimental/test/stream.hpp>
#include <boost/beast/_experimental/test/tcp.hpp>
#include <boost/beast/core/flat_buffer.hpp>
namespace boost {
namespace beast {
namespace websocket {
class BOOST_BEAST_SYMBOL_HIDDEN read1_test
: public websocket_test_suite
class read1_test : public unit_test::suite
{
public:
template<class Wrap, bool deflateSupported>
void
doReadTest(
Wrap const& w,
ws_type_t<deflateSupported>& ws,
close_code code)
testTimeout()
{
try
using tcp = net::ip::tcp;
net::io_context ioc;
// success
{
multi_buffer b;
w.read(ws, b);
fail("", __FILE__, __LINE__);
}
catch(system_error const& se)
{
if(se.code() != error::closed)
throw;
BEAST_EXPECT(
ws.reason().code == code);
}
}
stream<tcp::socket> ws1(ioc);
stream<tcp::socket> ws2(ioc);
test::connect(ws1.next_layer(), ws2.next_layer());
ws1.async_handshake("test", "/", test::success_handler());
ws2.async_accept(test::success_handler());
test::run(ioc);
template<class Wrap, bool deflateSupported>
void
doFailTest(
Wrap const& w,
ws_type_t<deflateSupported>& ws,
error_code ev)
{
try
{
multi_buffer b;
w.read(ws, b);
fail("", __FILE__, __LINE__);
}
catch(system_error const& se)
{
if(se.code() != ev)
throw;
}
}
template<bool deflateSupported = true, class Wrap>
void
doTestRead(Wrap const& w)
{
permessage_deflate pmd;
pmd.client_enable = false;
pmd.server_enable = false;
// already closed
{
echo_server es{log};
stream<test::stream, deflateSupported> ws{ioc_};
ws.next_layer().connect(es.stream());
ws.handshake("localhost", "/");
ws.close({});
try
{
multi_buffer b;
w.read(ws, b);
fail("", __FILE__, __LINE__);
}
catch(system_error const& se)
{
BEAST_EXPECTS(
se.code() == net::error::operation_aborted,
se.code().message());
}
}
// empty, fragmented message
doTest<deflateSupported>(pmd,
[&](ws_type_t<deflateSupported>& ws)
{
ws.next_layer().append(
string_view(
"\x01\x00" "\x80\x00", 4));
multi_buffer b;
w.read(ws, b);
BEAST_EXPECT(b.size() == 0);
});
// two part message
// triggers "fill the read buffer first"
doTest<deflateSupported>(pmd,
[&](ws_type_t<deflateSupported>& ws)
{
w.write_raw(ws, sbuf(
"\x01\x81\xff\xff\xff\xff"));
w.write_raw(ws, sbuf(
"\xd5"));
w.write_raw(ws, sbuf(
"\x80\x81\xff\xff\xff\xff\xd5"));
multi_buffer b;
w.read(ws, b);
BEAST_EXPECT(buffers_to_string(b.data()) == "**");
});
// ping
doTest<deflateSupported>(pmd,
[&](ws_type_t<deflateSupported>& ws)
{
put(ws.next_layer().buffer(), cbuf(
0x89, 0x00));
bool invoked = false;
ws.control_callback(
[&](frame_type kind, string_view)
{
BEAST_EXPECT(! invoked);
BEAST_EXPECT(kind == frame_type::ping);
invoked = true;
});
w.write(ws, sbuf("Hello"));
multi_buffer b;
w.read(ws, b);
BEAST_EXPECT(invoked);
BEAST_EXPECT(ws.got_text());
BEAST_EXPECT(buffers_to_string(b.data()) == "Hello");
});
// ping
doTest<deflateSupported>(pmd,
[&](ws_type_t<deflateSupported>& ws)
{
put(ws.next_layer().buffer(), cbuf(
0x88, 0x00));
bool invoked = false;
ws.control_callback(
[&](frame_type kind, string_view)
{
BEAST_EXPECT(! invoked);
BEAST_EXPECT(kind == frame_type::close);
invoked = true;
});
w.write(ws, sbuf("Hello"));
doReadTest(w, ws, close_code::none);
});
// ping then message
doTest<deflateSupported>(pmd,
[&](ws_type_t<deflateSupported>& ws)
{
bool once = false;
ws.control_callback(
[&](frame_type kind, string_view s)
{
BEAST_EXPECT(kind == frame_type::pong);
BEAST_EXPECT(! once);
once = true;
BEAST_EXPECT(s == "");
});
w.ping(ws, "");
ws.binary(true);
w.write(ws, sbuf("Hello"));
multi_buffer b;
w.read(ws, b);
BEAST_EXPECT(once);
BEAST_EXPECT(ws.got_binary());
BEAST_EXPECT(buffers_to_string(b.data()) == "Hello");
});
// ping then fragmented message
doTest<deflateSupported>(pmd,
[&](ws_type_t<deflateSupported>& ws)
{
bool once = false;
ws.control_callback(
[&](frame_type kind, string_view s)
{
BEAST_EXPECT(kind == frame_type::pong);
BEAST_EXPECT(! once);
once = true;
BEAST_EXPECT(s == "payload");
});
ws.ping("payload");
w.write_some(ws, false, sbuf("Hello, "));
w.write_some(ws, false, sbuf(""));
w.write_some(ws, true, sbuf("World!"));
multi_buffer b;
w.read(ws, b);
BEAST_EXPECT(once);
BEAST_EXPECT(buffers_to_string(b.data()) == "Hello, World!");
});
// masked message, big
doStreamLoop([&](test::stream& ts)
{
echo_server es{log, kind::async_client};
ws_type_t<deflateSupported> ws{ts};
ws.next_layer().connect(es.stream());
ws.set_option(pmd);
es.async_handshake();
try
{
w.accept(ws);
std::string const s(2000, '*');
ws.auto_fragment(false);
ws.binary(false);
w.write(ws, net::buffer(s));
multi_buffer b;
w.read(ws, b);
BEAST_EXPECT(ws.got_text());
BEAST_EXPECT(buffers_to_string(b.data()) == s);
ws.next_layer().close();
}
catch(...)
{
ts.close();
throw;
}
});
// close
doFailLoop([&](test::fail_count& fc)
{
echo_server es{log, kind::async};
net::io_context ioc;
stream<test::stream, deflateSupported> ws{ioc, fc};
ws.next_layer().connect(es.stream());
ws.handshake("localhost", "/");
// Cause close to be received
es.async_close();
std::size_t count = 0;
multi_buffer b;
ws.async_read(b,
[&](error_code ec, std::size_t)
{
++count;
if(ec != error::closed)
BOOST_THROW_EXCEPTION(
system_error{ec});
});
ioc.run();
BEAST_EXPECT(count == 1);
});
// already closed
doTest<deflateSupported>(pmd,
[&](ws_type_t<deflateSupported>& ws)
{
w.close(ws, {});
multi_buffer b;
doFailTest(w, ws,
net::error::operation_aborted);
});
// buffer overflow
doTest<deflateSupported>(pmd,
[&](ws_type_t<deflateSupported>& ws)
{
std::string const s = "Hello, world!";
ws.auto_fragment(false);
ws.binary(false);
w.write(ws, net::buffer(s));
try
{
multi_buffer b(3);
w.read(ws, b);
fail("", __FILE__, __LINE__);
}
catch(system_error const& se)
{
if(se.code() != error::buffer_overflow)
throw;
}
});
// bad utf8, big
doTest<deflateSupported>(pmd,
[&](ws_type_t<deflateSupported>& ws)
{
auto const s = std::string(2000, '*') +
random_string();
ws.text(true);
w.write(ws, net::buffer(s));
doReadTest(w, ws, close_code::bad_payload);
});
// invalid fixed frame header
doTest<deflateSupported>(pmd,
[&](ws_type_t<deflateSupported>& ws)
{
w.write_raw(ws, cbuf(
0x8f, 0x80, 0xff, 0xff, 0xff, 0xff));
doReadTest(w, ws, close_code::protocol_error);
});
// bad close
doTest<deflateSupported>(pmd,
[&](ws_type_t<deflateSupported>& ws)
{
put(ws.next_layer().buffer(), cbuf(
0x88, 0x02, 0x03, 0xed));
doFailTest(w, ws, error::bad_close_code);
});
// message size above 2^64
doTest<deflateSupported>(pmd,
[&](ws_type_t<deflateSupported>& ws)
{
w.write_some(ws, false, sbuf("*"));
w.write_raw(ws, cbuf(
0x80, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff));
doReadTest(w, ws, close_code::too_big);
});
// message size exceeds max
doTest<deflateSupported>(pmd,
[&](ws_type_t<deflateSupported>& ws)
{
ws.read_message_max(1);
w.write(ws, sbuf("**"));
doFailTest(w, ws, error::message_too_big);
});
// bad utf8
doTest<deflateSupported>(pmd,
[&](ws_type_t<deflateSupported>& ws)
{
put(ws.next_layer().buffer(), cbuf(
0x81, 0x06, 0x03, 0xea, 0xf0, 0x28, 0x8c, 0xbc));
doFailTest(w, ws, error::bad_frame_payload);
});
// incomplete utf8
doTest<deflateSupported>(pmd,
[&](ws_type_t<deflateSupported>& ws)
{
std::string const s =
"Hello, world!" "\xc0";
w.write(ws, net::buffer(s));
doReadTest(w, ws, close_code::bad_payload);
});
// incomplete utf8, big
doTest<deflateSupported>(pmd,
[&](ws_type_t<deflateSupported>& ws)
{
std::string const s =
"\x81\x7e\x0f\xa1" +
std::string(4000, '*') + "\xc0";
ws.next_layer().append(s);
multi_buffer b;
try
{
do
{
b.commit(w.read_some(ws, b.prepare(4000)));
}
while(! ws.is_message_done());
}
catch(system_error const& se)
{
if(se.code() != error::bad_frame_payload)
throw;
}
});
// close frames
{
auto const check =
[&](error_code ev, string_view s)
{
echo_server es{log};
stream<test::stream, deflateSupported> ws{ioc_};
ws.next_layer().connect(es.stream());
w.handshake(ws, "localhost", "/");
ws.next_layer().append(s);
static_buffer<1> b;
try
{
w.read(ws, b);
fail("", __FILE__, __LINE__);
}
catch(system_error const& se)
{
BEAST_EXPECTS(se.code() == ev,
se.code().message());
}
ws.next_layer().close();
};
// payload length 1
check(error::bad_close_size,
"\x88\x01\x01");
// invalid close code 1005
check(error::bad_close_code,
"\x88\x02\x03\xed");
// invalid utf8
check(error::bad_close_payload,
"\x88\x06\xfc\x15\x0f\xd7\x73\x43");
// good utf8
check(error::closed,
"\x88\x06\xfc\x15utf8");
}
}
template<class Wrap>
void
doTestReadDeflate(Wrap const& w)
{
permessage_deflate pmd;
pmd.client_enable = true;
pmd.server_enable = true;
pmd.client_max_window_bits = 9;
pmd.server_max_window_bits = 9;
pmd.compLevel = 1;
// message size limit
doTest<true>(pmd,
[&](ws_type_t<true>& ws)
{
std::string const s = std::string(128, '*');
w.write(ws, net::buffer(s));
ws.read_message_max(32);
doFailTest(w, ws, error::message_too_big);
});
// invalid inflate block
doTest<true>(pmd,
[&](ws_type_t<true>& ws)
{
auto const& s = random_string();
ws.binary(true);
ws.next_layer().append(
"\xc2\x40" + s.substr(0, 64));
flat_buffer b;
try
{
w.read(ws, b);
}
catch(system_error const& se)
{
if(se.code() == test::error::test_failure)
throw;
BEAST_EXPECTS(se.code().category() ==
zlib::detail::get_error_category(),
se.code().message());
}
catch(...)
{
throw;
}
});
ws1.async_write(net::const_buffer("Hello, world!", 13),
test::success_handler());
ws2.async_read(b, test::success_handler());
test::run(ioc);
}
// no_context_takeover
pmd.server_no_context_takeover = true;
doTest<true>(pmd,
[&](ws_type_t<true>& ws)
{
auto const& s = random_string();
ws.binary(true);
w.write(ws, net::buffer(s));
multi_buffer b;
w.read(ws, b);
BEAST_EXPECT(buffers_to_string(b.data()) == s);
});
pmd.client_no_context_takeover = false;
}
stream<test::stream> ws1(ioc);
stream<test::stream> ws2(ioc);
test::connect(ws1.next_layer(), ws2.next_layer());
ws1.async_handshake("test", "/", test::success_handler());
ws2.async_accept(test::success_handler());
test::run(ioc);
flat_buffer b;
ws1.async_write(net::const_buffer("Hello, world!", 13),
test::success_handler());
ws2.async_read(b, test::success_handler());
test::run(ioc);
}
// success, timeout enabled
template<class Wrap>
void
doTestRead(
permessage_deflate const& pmd,
Wrap const& w)
{
// message
doTest(pmd, [&](ws_type& ws)
{
std::string const s = "Hello, world!";
ws.auto_fragment(false);
ws.binary(false);
w.write(ws, net::buffer(s));
multi_buffer b;
w.read(ws, b);
BEAST_EXPECT(ws.got_text());
BEAST_EXPECT(buffers_to_string(b.data()) == s);
});
stream<tcp::socket> ws1(ioc);
stream<tcp::socket> ws2(ioc);
test::connect(ws1.next_layer(), ws2.next_layer());
ws1.async_handshake("test", "/", test::success_handler());
ws2.async_accept(test::success_handler());
test::run(ioc);
flat_buffer b;
ws1.set_option(stream_base::timeout{
stream_base::none(),
std::chrono::milliseconds(50),
false});
ws1.async_read(b, test::success_handler());
ws2.async_write(net::const_buffer("Hello, world!", 13),
test::success_handler());
test::run(ioc);
}
// masked message
doStreamLoop([&](test::stream& ts)
{
echo_server es{log, kind::async_client};
ws_type ws{ts};
ws.next_layer().connect(es.stream());
ws.set_option(pmd);
es.async_handshake();
try
{
w.accept(ws);
std::string const s = "Hello, world!";
ws.auto_fragment(false);
ws.binary(false);
w.write(ws, net::buffer(s));
multi_buffer b;
w.read(ws, b);
BEAST_EXPECT(ws.got_text());
BEAST_EXPECT(buffers_to_string(b.data()) == s);
ws.next_layer().close();
}
catch(...)
{
ts.close();
throw;
}
});
stream<test::stream> ws1(ioc);
stream<test::stream> ws2(ioc);
test::connect(ws1.next_layer(), ws2.next_layer());
ws1.async_handshake("test", "/", test::success_handler());
ws2.async_accept(test::success_handler());
test::run(ioc);
flat_buffer b;
ws1.set_option(stream_base::timeout{
stream_base::none(),
std::chrono::milliseconds(50),
false});
ws1.async_read(b, test::success_handler());
ws2.async_write(net::const_buffer("Hello, world!", 13),
test::success_handler());
test::run(ioc);
}
// timeout
// empty message
doTest(pmd, [&](ws_type& ws)
{
std::string const s = "";
ws.text(true);
w.write(ws, net::buffer(s));
multi_buffer b;
w.read(ws, b);
BEAST_EXPECT(ws.got_text());
BEAST_EXPECT(buffers_to_string(b.data()) == s);
});
stream<tcp::socket> ws1(ioc);
stream<tcp::socket> ws2(ioc);
test::connect(ws1.next_layer(), ws2.next_layer());
ws1.async_handshake("test", "/", test::success_handler());
ws2.async_accept(test::success_handler());
test::run(ioc);
flat_buffer b;
ws1.set_option(stream_base::timeout{
stream_base::none(),
std::chrono::milliseconds(50),
false});
ws1.async_read(b, test::fail_handler(
beast::error::timeout));
test::run(ioc);
}
// partial message
doTest(pmd, [&](ws_type& ws)
{
std::string const s = "Hello";
w.write(ws, net::buffer(s));
char buf[3];
auto const bytes_written =
w.read_some(ws, net::buffer(buf, sizeof(buf)));
BEAST_EXPECT(bytes_written > 0);
BEAST_EXPECT(
string_view(buf, 3).substr(0, bytes_written) ==
s.substr(0, bytes_written));
});
stream<test::stream> ws1(ioc);
stream<test::stream> ws2(ioc);
test::connect(ws1.next_layer(), ws2.next_layer());
ws1.async_handshake("test", "/", test::success_handler());
ws2.async_accept(test::success_handler());
test::run(ioc);
// partial message, dynamic buffer
doTest(pmd, [&](ws_type& ws)
{
std::string const s = "Hello, world!";
w.write(ws, net::buffer(s));
multi_buffer b;
auto bytes_written =
w.read_some(ws, 3, b);
BEAST_EXPECT(bytes_written > 0);
BEAST_EXPECT(buffers_to_string(b.data()) ==
s.substr(0, b.size()));
w.read_some(ws, 256, b);
BEAST_EXPECT(buffers_to_string(b.data()) == s);
});
// big message
doTest(pmd, [&](ws_type& ws)
{
auto const& s = random_string();
ws.binary(true);
w.write(ws, net::buffer(s));
multi_buffer b;
w.read(ws, b);
BEAST_EXPECT(buffers_to_string(b.data()) == s);
});
// message, bad utf8
doTest(pmd, [&](ws_type& ws)
{
std::string const s = "\x03\xea\xf0\x28\x8c\xbc";
ws.auto_fragment(false);
ws.text(true);
w.write(ws, net::buffer(s));
doReadTest(w, ws, close_code::bad_payload);
});
}
void
testRead()
{
doTestRead<false>(SyncClient{});
doTestRead<true>(SyncClient{});
doTestReadDeflate(SyncClient{});
yield_to([&](yield_context yield)
{
doTestRead<false>(AsyncClient{yield});
doTestRead<true>(AsyncClient{yield});
doTestReadDeflate(AsyncClient{yield});
});
permessage_deflate pmd;
pmd.client_enable = false;
pmd.server_enable = false;
doTestRead(pmd, SyncClient{});
yield_to([&](yield_context yield)
{
doTestRead(pmd, AsyncClient{yield});
});
pmd.client_enable = true;
pmd.server_enable = true;
pmd.client_max_window_bits = 9;
pmd.server_max_window_bits = 9;
pmd.compLevel = 1;
doTestRead(pmd, SyncClient{});
yield_to([&](yield_context yield)
{
doTestRead(pmd, AsyncClient{yield});
});
// Read close frames
{
auto const check =
[&](error_code ev, string_view s)
{
echo_server es{log};
stream<test::stream> ws{ioc_};
ws.next_layer().connect(es.stream());
ws.handshake("localhost", "/");
ws.next_layer().append(s);
static_buffer<1> b;
error_code ec;
ws.read(b, ec);
BEAST_EXPECTS(ec == ev, ec.message());
ws.next_layer().close();
};
// payload length 1
check(error::bad_close_size,
"\x88\x01\x01");
// invalid close code 1005
check(error::bad_close_code,
"\x88\x02\x03\xed");
// invalid utf8
check(error::bad_close_payload,
"\x88\x06\xfc\x15\x0f\xd7\x73\x43");
// good utf8
check(error::closed,
"\x88\x06\xfc\x15utf8");
flat_buffer b;
ws1.set_option(stream_base::timeout{
stream_base::none(),
std::chrono::milliseconds(50),
false});
ws1.async_read(b, test::fail_handler(
beast::error::timeout));
test::run(ioc);
}
}
void
run() override
{
testRead();
testTimeout();
}
};
File diff suppressed because it is too large. Load diff
+655
View File
@@ -0,0 +1,655 @@
//
// Copyright (c) 2016-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)
//
// Official repository: https://github.com/boostorg/beast
//
// Test that header file is self-contained.
#include <boost/beast/websocket/stream.hpp>
#include "test.hpp"
#include <boost/asio/buffer.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/strand.hpp>
#include <boost/asio/write.hpp>
namespace boost {
namespace beast {
namespace websocket {
class read3_test : public websocket_test_suite
{
public:
void
testSuspend()
{
// suspend on read block
doFailLoop([&](test::fail_count& fc)
{
echo_server es{log};
net::io_context ioc;
stream<test::stream> ws{ioc, fc};
ws.next_layer().connect(es.stream());
ws.handshake("localhost", "/");
std::size_t count = 0;
ws.async_close({},
[&](error_code ec)
{
if(ec)
BOOST_THROW_EXCEPTION(
system_error{ec});
BEAST_EXPECT(++count == 1);
});
while(! ws.impl_->rd_block.is_locked())
ioc.run_one();
multi_buffer b;
ws.async_read(b,
[&](error_code ec, std::size_t)
{
if(ec != net::error::operation_aborted)
BOOST_THROW_EXCEPTION(
system_error{ec});
BEAST_EXPECT(++count == 2);
});
ioc.run();
BEAST_EXPECT(count == 2);
});
// suspend on release read block
doFailLoop([&](test::fail_count& fc)
{
echo_server es{log};
net::io_context ioc;
stream<test::stream> ws{ioc, fc};
ws.next_layer().connect(es.stream());
ws.handshake("localhost", "/");
std::size_t count = 0;
multi_buffer b;
ws.async_read(b,
[&](error_code ec, std::size_t)
{
if(ec != net::error::operation_aborted)
BOOST_THROW_EXCEPTION(
system_error{ec});
BEAST_EXPECT(++count == 2);
});
BOOST_ASSERT(ws.impl_->rd_block.is_locked());
ws.async_close({},
[&](error_code ec)
{
if(ec)
BOOST_THROW_EXCEPTION(
system_error{ec});
BEAST_EXPECT(++count == 1);
});
ioc.run();
BEAST_EXPECT(count == 2);
});
// suspend on write pong
doFailLoop([&](test::fail_count& fc)
{
echo_server es{log};
net::io_context ioc;
stream<test::stream> ws{ioc, fc};
ws.next_layer().connect(es.stream());
ws.handshake("localhost", "/");
// insert a ping
ws.next_layer().append(string_view(
"\x89\x00", 2));
std::size_t count = 0;
std::string const s = "Hello, world";
multi_buffer b;
ws.async_read(b,
[&](error_code ec, std::size_t)
{
if(ec)
BOOST_THROW_EXCEPTION(
system_error{ec});
BEAST_EXPECT(buffers_to_string(b.data()) == s);
++count;
});
BEAST_EXPECT(ws.impl_->rd_block.is_locked());
ws.async_write(net::buffer(s),
[&](error_code ec, std::size_t n)
{
if(ec)
BOOST_THROW_EXCEPTION(
system_error{ec});
BEAST_EXPECT(n == s.size());
++count;
});
BEAST_EXPECT(ws.impl_->wr_block.is_locked());
ioc.run();
BEAST_EXPECT(count == 2);
});
// Ignore ping when closing
doFailLoop([&](test::fail_count& fc)
{
echo_server es{log};
net::io_context ioc;
stream<test::stream> ws{ioc, fc};
ws.next_layer().connect(es.stream());
ws.handshake("localhost", "/");
std::size_t count = 0;
// insert fragmented message with
// a ping in between the frames.
ws.next_layer().append(string_view(
"\x01\x01*"
"\x89\x00"
"\x80\x01*", 8));
multi_buffer b;
ws.async_read(b,
[&](error_code ec, std::size_t)
{
if(ec)
BOOST_THROW_EXCEPTION(
system_error{ec});
BEAST_EXPECT(buffers_to_string(b.data()) == "**");
BEAST_EXPECT(++count == 1);
b.consume(b.size());
ws.async_read(b,
[&](error_code ec, std::size_t)
{
if(ec != net::error::operation_aborted)
BOOST_THROW_EXCEPTION(
system_error{ec});
BEAST_EXPECT(++count == 3);
});
});
BEAST_EXPECT(ws.impl_->rd_block.is_locked());
ws.async_close({},
[&](error_code ec)
{
if(ec)
BOOST_THROW_EXCEPTION(
system_error{ec});
BEAST_EXPECT(++count == 2);
});
BEAST_EXPECT(ws.impl_->wr_block.is_locked());
ioc.run();
BEAST_EXPECT(count == 3);
});
// See if we are already closing
doFailLoop([&](test::fail_count& fc)
{
echo_server es{log};
net::io_context ioc;
stream<test::stream> ws{ioc, fc};
ws.next_layer().connect(es.stream());
ws.handshake("localhost", "/");
std::size_t count = 0;
// insert fragmented message with
// a close in between the frames.
ws.next_layer().append(string_view(
"\x01\x01*"
"\x88\x00"
"\x80\x01*", 8));
multi_buffer b;
ws.async_read(b,
[&](error_code ec, std::size_t)
{
if(ec != net::error::operation_aborted)
BOOST_THROW_EXCEPTION(
system_error{ec});
BEAST_EXPECT(++count == 2);
});
BEAST_EXPECT(ws.impl_->rd_block.is_locked());
ws.async_close({},
[&](error_code ec)
{
if(ec)
BOOST_THROW_EXCEPTION(
system_error{ec});
BEAST_EXPECT(++count == 1);
});
BEAST_EXPECT(ws.impl_->wr_block.is_locked());
ioc.run();
BEAST_EXPECT(count == 2);
});
}
void
testParseFrame()
{
auto const bad =
[&](string_view s)
{
echo_server es{log};
net::io_context ioc;
stream<test::stream> ws{ioc};
ws.next_layer().connect(es.stream());
ws.handshake("localhost", "/");
ws.next_layer().append(s);
error_code ec;
multi_buffer b;
ws.read(b, ec);
BEAST_EXPECT(ec);
};
// chopped frame header
{
echo_server es{log};
net::io_context ioc;
stream<test::stream> ws{ioc};
ws.next_layer().connect(es.stream());
ws.handshake("localhost", "/");
ws.next_layer().append(
"\x81\x7e\x01");
std::size_t count = 0;
std::string const s(257, '*');
multi_buffer b;
ws.async_read(b,
[&](error_code ec, std::size_t)
{
++count;
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(buffers_to_string(b.data()) == s);
});
ioc.run_one();
es.stream().write_some(
net::buffer("\x01" + s));
ioc.run();
BEAST_EXPECT(count == 1);
}
// new data frame when continuation expected
bad("\x01\x01*" "\x81\x01*");
// reserved bits not cleared
bad("\xb1\x01*");
bad("\xc1\x01*");
bad("\xd1\x01*");
// continuation without an active message
bad("\x80\x01*");
// reserved bits not cleared (cont)
bad("\x01\x01*" "\xb0\x01*");
bad("\x01\x01*" "\xc0\x01*");
bad("\x01\x01*" "\xd0\x01*");
// reserved opcode
bad("\x83\x01*");
// fragmented control message
bad("\x09\x01*");
// invalid length for control message
bad("\x89\x7e\x01\x01");
// reserved bits not cleared (control)
bad("\xb9\x01*");
bad("\xc9\x01*");
bad("\xd9\x01*");
// unmasked frame from client
{
echo_server es{log, kind::async_client};
net::io_context ioc;
stream<test::stream> ws{ioc};
ws.next_layer().connect(es.stream());
es.async_handshake();
ws.accept();
ws.next_layer().append(
"\x81\x01*");
error_code ec;
multi_buffer b;
ws.read(b, ec);
BEAST_EXPECT(ec);
}
// masked frame from server
bad("\x81\x80\xff\xff\xff\xff");
// chopped control frame payload
{
echo_server es{log};
net::io_context ioc;
stream<test::stream> ws{ioc};
ws.next_layer().connect(es.stream());
ws.handshake("localhost", "/");
ws.next_layer().append(
"\x89\x02*");
std::size_t count = 0;
multi_buffer b;
ws.async_read(b,
[&](error_code ec, std::size_t)
{
++count;
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(buffers_to_string(b.data()) == "**");
});
ioc.run_one();
es.stream().write_some(
net::buffer(
"*" "\x81\x02**"));
ioc.run();
BEAST_EXPECT(count == 1);
}
// length not canonical
bad(string_view("\x81\x7e\x00\x7d", 4));
bad(string_view("\x81\x7f\x00\x00\x00\x00\x00\x00\xff\xff", 10));
}
void
testIssue802()
{
for(std::size_t i = 0; i < 100; ++i)
{
echo_server es{log, kind::async};
net::io_context ioc;
stream<test::stream> ws{ioc};
ws.next_layer().connect(es.stream());
ws.handshake("localhost", "/");
// too-big message frame indicates payload of 2^64-1
net::write(ws.next_layer(), sbuf(
"\x81\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"));
multi_buffer b;
error_code ec;
ws.read(b, ec);
BEAST_EXPECT(ec == error::closed);
BEAST_EXPECT(ws.reason().code == 1009);
}
}
void
testIssue807()
{
echo_server es{log};
net::io_context ioc;
stream<test::stream> ws{ioc};
ws.next_layer().connect(es.stream());
ws.handshake("localhost", "/");
ws.write(sbuf("Hello, world!"));
char buf[4];
net::mutable_buffer b{buf, 0};
auto const n = ws.read_some(b);
BEAST_EXPECT(n == 0);
}
/*
When the internal read buffer contains a control frame and
stream::async_read_some is called, it is possible for the control
callback to be invoked on the caller's stack instead of through
the executor associated with the final completion handler.
*/
void
testIssue954()
{
echo_server es{log};
multi_buffer b;
net::io_context ioc;
stream<test::stream> ws{ioc};
ws.next_layer().connect(es.stream());
ws.handshake("localhost", "/");
// message followed by ping
ws.next_layer().append({
"\x81\x00"
"\x89\x00",
4});
bool called_cb = false;
bool called_handler = false;
ws.control_callback(
[&called_cb](frame_type, string_view)
{
called_cb = true;
});
ws.async_read(b,
[&](error_code, std::size_t)
{
called_handler = true;
});
BEAST_EXPECT(! called_cb);
BEAST_EXPECT(! called_handler);
ioc.run();
BEAST_EXPECT(! called_cb);
BEAST_EXPECT(called_handler);
ws.async_read(b,
[&](error_code, std::size_t)
{
});
BEAST_EXPECT(! called_cb);
}
/* Bishop Fox Hybrid Assessment issue 1
Happens with permessage-deflate enabled and a
compressed frame with the FIN bit set ends with an
invalid prefix.
*/
void
testIssueBF1()
{
permessage_deflate pmd;
pmd.client_enable = true;
pmd.server_enable = true;
// read
#if 0
{
echo_server es{log};
net::io_context ioc;
stream<test::stream> ws{ioc};
ws.set_option(pmd);
ws.next_layer().connect(es.stream());
ws.handshake("localhost", "/");
// invalid 1-byte deflate block in frame
net::write(ws.next_layer(), sbuf(
"\xc1\x81\x3a\xa1\x74\x3b\x49"));
}
#endif
{
net::io_context ioc;
stream<test::stream> wsc{ioc};
stream<test::stream> wss{ioc};
wsc.set_option(pmd);
wss.set_option(pmd);
wsc.next_layer().connect(wss.next_layer());
wsc.async_handshake(
"localhost", "/", [](error_code){});
wss.async_accept([](error_code){});
ioc.run();
ioc.restart();
BEAST_EXPECT(wsc.is_open());
BEAST_EXPECT(wss.is_open());
// invalid 1-byte deflate block in frame
net::write(wsc.next_layer(), sbuf(
"\xc1\x81\x3a\xa1\x74\x3b\x49"));
error_code ec;
multi_buffer b;
wss.read(b, ec);
BEAST_EXPECTS(ec == zlib::error::end_of_stream, ec.message());
}
// async read
#if 0
{
echo_server es{log, kind::async};
net::io_context ioc;
stream<test::stream> ws{ioc};
ws.set_option(pmd);
ws.next_layer().connect(es.stream());
ws.handshake("localhost", "/");
// invalid 1-byte deflate block in frame
net::write(ws.next_layer(), sbuf(
"\xc1\x81\x3a\xa1\x74\x3b\x49"));
}
#endif
{
net::io_context ioc;
stream<test::stream> wsc{ioc};
stream<test::stream> wss{ioc};
wsc.set_option(pmd);
wss.set_option(pmd);
wsc.next_layer().connect(wss.next_layer());
wsc.async_handshake(
"localhost", "/", [](error_code){});
wss.async_accept([](error_code){});
ioc.run();
ioc.restart();
BEAST_EXPECT(wsc.is_open());
BEAST_EXPECT(wss.is_open());
// invalid 1-byte deflate block in frame
net::write(wsc.next_layer(), sbuf(
"\xc1\x81\x3a\xa1\x74\x3b\x49"));
error_code ec;
flat_buffer b;
wss.async_read(b,
[&ec](error_code ec_, std::size_t){ ec = ec_; });
ioc.run();
BEAST_EXPECTS(ec == zlib::error::end_of_stream, ec.message());
}
}
/* Bishop Fox Hybrid Assessment issue 2
Happens with permessage-deflate enabled,
and a deflate block with the BFINAL bit set
is encountered in a compressed payload.
*/
void
testIssueBF2()
{
permessage_deflate pmd;
pmd.client_enable = true;
pmd.server_enable = true;
// read
{
net::io_context ioc;
stream<test::stream> wsc{ioc};
stream<test::stream> wss{ioc};
wsc.set_option(pmd);
wss.set_option(pmd);
wsc.next_layer().connect(wss.next_layer());
wsc.async_handshake(
"localhost", "/", [](error_code){});
wss.async_accept([](error_code){});
ioc.run();
ioc.restart();
BEAST_EXPECT(wsc.is_open());
BEAST_EXPECT(wss.is_open());
// contains a deflate block with BFINAL set
net::write(wsc.next_layer(), sbuf(
"\xc1\xf8\xd1\xe4\xcc\x3e\xda\xe4\xcc\x3e"
"\x2b\x1e\x36\xc4\x2b\x1e\x36\xc4\x2b\x1e"
"\x36\x3e\x35\xae\x4f\x54\x18\xae\x4f\x7b"
"\xd1\xe4\xcc\x3e\xd1\xe4\xcc\x3e\xd1\xe4"
"\xcc\x3e\xd1\xe4\xcc\x3e\xd1\xe4\xcc\x3e"
"\xd1\x1e\x36\xc4\x2b\x1e\x36\xc4\x2b\xe4"
"\x28\x74\x52\x8e\x05\x74\x52\xa1\xcc\x3e"
"\xd1\xe4\xcc\x3e\xd1\xe4\xcc\x3e\xd1\xe4"
"\xcc\x3e\xd1\xe4\xcc\x3e\xd1\xe4\xcc\x3e"
"\xd1\xe4\xcc\x3e\xd1\xe4\xcc\x3e\xd1\xe4"
"\xcc\x3e\xd1\xe4\xcc\x3e\xd1\xe4\x36\x3e"
"\xd1\xec\xcc\x3e\xd1\xe4\xcc\x3e\xd1\xe4"
"\xcc\x3e\xd1\xe4\xcc\x3e"));
error_code ec;
flat_buffer b;
wss.read(b, ec);
BEAST_EXPECTS(ec == zlib::error::end_of_stream, ec.message());
}
// async read
{
net::io_context ioc;
stream<test::stream> wsc{ioc};
stream<test::stream> wss{ioc};
wsc.set_option(pmd);
wss.set_option(pmd);
wsc.next_layer().connect(wss.next_layer());
wsc.async_handshake(
"localhost", "/", [](error_code){});
wss.async_accept([](error_code){});
ioc.run();
ioc.restart();
BEAST_EXPECT(wsc.is_open());
BEAST_EXPECT(wss.is_open());
// contains a deflate block with BFINAL set
net::write(wsc.next_layer(), sbuf(
"\xc1\xf8\xd1\xe4\xcc\x3e\xda\xe4\xcc\x3e"
"\x2b\x1e\x36\xc4\x2b\x1e\x36\xc4\x2b\x1e"
"\x36\x3e\x35\xae\x4f\x54\x18\xae\x4f\x7b"
"\xd1\xe4\xcc\x3e\xd1\xe4\xcc\x3e\xd1\xe4"
"\xcc\x3e\xd1\xe4\xcc\x3e\xd1\xe4\xcc\x3e"
"\xd1\x1e\x36\xc4\x2b\x1e\x36\xc4\x2b\xe4"
"\x28\x74\x52\x8e\x05\x74\x52\xa1\xcc\x3e"
"\xd1\xe4\xcc\x3e\xd1\xe4\xcc\x3e\xd1\xe4"
"\xcc\x3e\xd1\xe4\xcc\x3e\xd1\xe4\xcc\x3e"
"\xd1\xe4\xcc\x3e\xd1\xe4\xcc\x3e\xd1\xe4"
"\xcc\x3e\xd1\xe4\xcc\x3e\xd1\xe4\x36\x3e"
"\xd1\xec\xcc\x3e\xd1\xe4\xcc\x3e\xd1\xe4"
"\xcc\x3e\xd1\xe4\xcc\x3e"));
error_code ec;
flat_buffer b;
wss.async_read(b,
[&ec](error_code ec_, std::size_t){ ec = ec_; });
ioc.run();
BEAST_EXPECTS(ec == zlib::error::end_of_stream, ec.message());
}
}
void
testMoveOnly()
{
net::io_context ioc;
stream<test::stream> ws{ioc};
ws.async_read_some(
net::mutable_buffer{},
move_only_handler{});
}
struct copyable_handler
{
template<class... Args>
void
operator()(Args&&...) const
{
}
};
void
testAsioHandlerInvoke()
{
// make sure things compile, also can set a
// breakpoint in asio_handler_invoke to make sure
// it is instantiated.
{
net::io_context ioc;
net::strand<
net::io_context::executor_type> s(
ioc.get_executor());
stream<test::stream> ws{ioc};
flat_buffer b;
ws.async_read(b, net::bind_executor(
s, copyable_handler{}));
}
}
void
run() override
{
testParseFrame();
testIssue802();
testIssue807();
testIssue954();
testIssueBF1();
testIssueBF2();
testMoveOnly();
testAsioHandlerInvoke();
}
};
BEAST_DEFINE_TESTSUITE(beast,websocket,read3);
} // websocket
} // beast
} // boost
+44 -1
View File
@@ -1,5 +1,5 @@
//
// Copyright (w) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
// Copyright (c) 2016-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)
@@ -10,6 +10,9 @@
// Test that header file is self-contained.
#include <boost/beast/websocket/stream.hpp>
#include <boost/beast/core/tcp_stream.hpp>
#include <boost/asio/strand.hpp>
#include "test.hpp"
namespace boost {
@@ -19,6 +22,32 @@ namespace websocket {
class stream_test : public websocket_test_suite
{
public:
void
testGetSetOption()
{
net::io_context ioc;
stream<test::stream> ws(ioc);
{
ws.set_option(
stream_base::suggested_settings(
role_type::client));
ws.set_option(
stream_base::suggested_settings(
role_type::server));
ws.set_option({
std::chrono::seconds(30),
std::chrono::seconds(300),
true});
stream_base::timeout opt;
ws.get_option(opt);
ws.set_option(opt);
}
}
void
testOptions()
{
@@ -109,6 +138,19 @@ public:
}
}
void
testJavadoc()
{
net::io_context ioc;
{
websocket::stream<tcp_stream<
net::io_context::strand>> ws{net::io_context::strand(ioc)};
}
{
websocket::stream<tcp_stream<net::io_context::executor_type>> ws(ioc);
}
}
void
run() override
{
@@ -136,6 +178,7 @@ public:
sizeof(websocket::stream<test::stream&>::impl_type) << std::endl;
testOptions();
testJavadoc();
}
};
+1 -1
View File
@@ -1,5 +1,5 @@
//
// Copyright (w) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
// Copyright (c) 2016-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)
+1 -1
View File
@@ -1,5 +1,5 @@
//
// Copyright (w) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
// Copyright (c) 2016-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)
+1 -1
View File
@@ -1,5 +1,5 @@
//
// Copyright (w) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
// Copyright (c) 2016-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)
-100
View File
@@ -10,7 +10,6 @@
// Test that header file is self-contained.
#include <boost/beast/websocket/stream.hpp>
#include <boost/beast/core/flat_buffer.hpp>
#include <boost/asio/ip/tcp.hpp>
#include "test.hpp"
@@ -25,108 +24,9 @@ class timer_test
public:
using tcp = boost::asio::ip::tcp;
static
void
connect(
stream<tcp::socket>& ws1,
stream<tcp::socket>& ws2)
{
struct handler
{
void
operator()(error_code ec)
{
BEAST_EXPECTS(! ec, ec.message());
}
};
tcp::acceptor a(ws1.get_executor().context());
auto ep = tcp::endpoint(
net::ip::make_address_v4("127.0.0.1"), 0);
a.open(ep.protocol());
a.set_option(
net::socket_base::reuse_address(true));
a.bind(ep);
a.listen(0);
ep = a.local_endpoint();
a.async_accept(ws2.next_layer(), handler{});
ws1.next_layer().async_connect(ep, handler{});
for(;;)
{
if( ws1.get_executor().context().run_one() +
ws2.get_executor().context().run_one() == 0)
{
ws1.get_executor().context().restart();
ws2.get_executor().context().restart();
break;
}
}
BEAST_EXPECT(
ws1.next_layer().remote_endpoint() ==
ws2.next_layer().local_endpoint());
BEAST_EXPECT(
ws2.next_layer().remote_endpoint() ==
ws1.next_layer().local_endpoint());
ws2.async_accept(handler{});
ws1.async_handshake("test", "/", handler{});
for(;;)
{
if( ws1.get_executor().context().run_one() +
ws2.get_executor().context().run_one() == 0)
{
ws1.get_executor().context().restart();
ws2.get_executor().context().restart();
break;
}
}
BEAST_EXPECT(ws1.is_open());
BEAST_EXPECT(ws2.is_open());
BEAST_EXPECT(! ws1.get_executor().context().stopped());
BEAST_EXPECT(! ws2.get_executor().context().stopped());
}
#if 0
void
testRead0()
{
echo_server es(log, kind::async);
net::io_context ioc;
stream<test::stream> ws(ioc);
ws.next_layer().connect(es.stream());
ws.handshake("localhost", "/");
flat_buffer b;
ws.async_read(b,
[&](error_code ec, std::size_t)
{
log << ec.message() << "\n";
});
ioc.run();
}
#endif
void
testRead()
{
net::io_context ioc;
stream<tcp::socket> ws1(ioc);
stream<tcp::socket> ws2(ioc);
connect(ws1, ws2);
flat_buffer b;
ws2.async_read(b,
[&](error_code ec, std::size_t)
{
log << "ws1.async_read: " << ec.message() << "\n";
});
ioc.run();
}
void
run() override
{
testRead();
pass();
}
};
+1 -1
View File
@@ -1,5 +1,5 @@
//
// Copyright (w) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
// Copyright (c) 2016-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)
@@ -1,5 +1,5 @@
//
// Copyright (w) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
// Copyright (c) 2016-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)