forked from boostorg/beast
Refactor stream operations and tests plus coverage
This commit is contained in:
1 parent
9f089c2a33
commit
51a5a36118
22 files changed
+1290
-354
No files matched your search
@@ -560,6 +560,35 @@ public:
|
||||
"Sec-WebSocket-Version: 13\r\n"
|
||||
"\r\n"
|
||||
);
|
||||
// oversize key
|
||||
check(error::handshake_failed,
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"Host: localhost:80\r\n"
|
||||
"Upgrade: WebSocket\r\n"
|
||||
"Connection: upgrade\r\n"
|
||||
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQdGhlIHNhbXBsZSBub25jZQ==\r\n"
|
||||
"Sec-WebSocket-Version: 13\r\n"
|
||||
"\r\n"
|
||||
);
|
||||
// bad version
|
||||
check(error::handshake_failed,
|
||||
"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: 12\r\n"
|
||||
"\r\n"
|
||||
);
|
||||
// missing version
|
||||
check(error::handshake_failed,
|
||||
"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"
|
||||
"\r\n"
|
||||
);
|
||||
// valid request
|
||||
check({},
|
||||
"GET / HTTP/1.1\r\n"
|
||||
|
||||
@@ -39,6 +39,14 @@ public:
|
||||
w.close(ws, close_code::going_away);
|
||||
});
|
||||
|
||||
// close with code and reason
|
||||
doTest(pmd, [&](ws_type& ws)
|
||||
{
|
||||
w.close(ws, {
|
||||
close_code::going_away,
|
||||
"going away"});
|
||||
});
|
||||
|
||||
// already closed
|
||||
{
|
||||
echo_server es{log};
|
||||
@@ -125,6 +133,17 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// drain masked close frame
|
||||
{
|
||||
echo_server es{log, kind::async_client};
|
||||
stream<test::stream> ws{ios_};
|
||||
ws.next_layer().connect(es.stream());
|
||||
ws.set_option(pmd);
|
||||
es.async_handshake();
|
||||
ws.accept();
|
||||
w.close(ws, {});
|
||||
}
|
||||
|
||||
// close with incomplete read message
|
||||
doTest(pmd, [&](ws_type& ws)
|
||||
{
|
||||
@@ -147,8 +166,10 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
testCloseSuspend()
|
||||
testSuspend()
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
|
||||
// suspend on ping
|
||||
doFailLoop([&](test::fail_counter& fc)
|
||||
{
|
||||
@@ -270,10 +291,10 @@ public:
|
||||
ws.async_read(b,
|
||||
[&](error_code ec, std::size_t)
|
||||
{
|
||||
++count;
|
||||
if(ec != error::failed)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
system_error{ec});
|
||||
BEAST_EXPECT(++count == 1);
|
||||
});
|
||||
while(! ws.wr_block_)
|
||||
{
|
||||
@@ -285,10 +306,10 @@ public:
|
||||
ws.async_close({},
|
||||
[&](error_code ec)
|
||||
{
|
||||
++count;
|
||||
if(ec != boost::asio::error::operation_aborted)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
system_error{ec});
|
||||
BEAST_EXPECT(++count == 2);
|
||||
});
|
||||
BEAST_EXPECT(count == 0);
|
||||
ios.run();
|
||||
@@ -311,10 +332,10 @@ public:
|
||||
ws.async_read(b,
|
||||
[&](error_code ec, std::size_t)
|
||||
{
|
||||
++count;
|
||||
if(ec != error::closed)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
system_error{ec});
|
||||
BEAST_EXPECT(++count == 1);
|
||||
});
|
||||
while(! ws.wr_block_)
|
||||
{
|
||||
@@ -326,16 +347,256 @@ public:
|
||||
ws.async_close({},
|
||||
[&](error_code ec)
|
||||
{
|
||||
++count;
|
||||
if(ec != boost::asio::error::operation_aborted)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
system_error{ec});
|
||||
BEAST_EXPECT(++count == 2);
|
||||
});
|
||||
BEAST_EXPECT(count == 0);
|
||||
ios.run();
|
||||
BEAST_EXPECT(count == 2);
|
||||
});
|
||||
|
||||
// teardown on received close
|
||||
doFailLoop([&](test::fail_counter& fc)
|
||||
{
|
||||
echo_server es{log};
|
||||
boost::asio::io_service ios;
|
||||
stream<test::stream> ws{ios, fc};
|
||||
ws.next_layer().connect(es.stream());
|
||||
ws.handshake("localhost", "/");
|
||||
// add a close frame to the input
|
||||
ws.next_layer().append(string_view{
|
||||
"\x88\x00", 2});
|
||||
std::size_t count = 0;
|
||||
std::string const s = "Hello, world!";
|
||||
ws.async_write(buffer(s),
|
||||
[&](error_code ec)
|
||||
{
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
system_error{ec});
|
||||
BEAST_EXPECT(++count == 1);
|
||||
});
|
||||
multi_buffer b;
|
||||
ws.async_read(b,
|
||||
[&](error_code ec, std::size_t)
|
||||
{
|
||||
if(ec != boost::asio::error::operation_aborted)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
system_error{ec});
|
||||
BEAST_EXPECT(++count == 3);
|
||||
});
|
||||
ws.async_close({},
|
||||
[&](error_code ec)
|
||||
{
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
system_error{ec});
|
||||
BEAST_EXPECT(++count == 2);
|
||||
});
|
||||
BEAST_EXPECT(count == 0);
|
||||
ios.run();
|
||||
BEAST_EXPECT(count == 3);
|
||||
});
|
||||
|
||||
// check for deadlock
|
||||
doFailLoop([&](test::fail_counter& fc)
|
||||
{
|
||||
echo_server es{log};
|
||||
boost::asio::io_service ios;
|
||||
stream<test::stream> ws{ios, fc};
|
||||
ws.next_layer().connect(es.stream());
|
||||
ws.handshake("localhost", "/");
|
||||
// add a ping frame to the input
|
||||
ws.next_layer().append(string_view{
|
||||
"\x89\x00", 2});
|
||||
std::size_t count = 0;
|
||||
multi_buffer b;
|
||||
std::string const s = "Hello, world!";
|
||||
ws.async_write(buffer(s),
|
||||
[&](error_code ec)
|
||||
{
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
system_error{ec});
|
||||
BEAST_EXPECT(++count == 1);
|
||||
});
|
||||
ws.async_read(b,
|
||||
[&](error_code ec, std::size_t)
|
||||
{
|
||||
if(ec != boost::asio::error::operation_aborted)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
system_error{ec});
|
||||
BEAST_EXPECT(++count == 3);
|
||||
});
|
||||
BEAST_EXPECT(ws.rd_block_);
|
||||
ws.async_close({},
|
||||
[&](error_code ec)
|
||||
{
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
system_error{ec});
|
||||
BEAST_EXPECT(++count == 2);
|
||||
});
|
||||
BEAST_EXPECT(ws.is_open());
|
||||
BEAST_EXPECT(ws.wr_block_);
|
||||
BEAST_EXPECT(count == 0);
|
||||
ios.run();
|
||||
BEAST_EXPECT(count == 3);
|
||||
});
|
||||
|
||||
// Four-way: close, read, write, ping
|
||||
doFailLoop([&](test::fail_counter& fc)
|
||||
{
|
||||
echo_server es{log};
|
||||
boost::asio::io_service ios;
|
||||
stream<test::stream> ws{ios, fc};
|
||||
ws.next_layer().connect(es.stream());
|
||||
ws.handshake("localhost", "/");
|
||||
std::size_t count = 0;
|
||||
std::string const s = "Hello, world!";
|
||||
multi_buffer b;
|
||||
ws.async_close({},
|
||||
[&](error_code ec)
|
||||
{
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
system_error{ec});
|
||||
BEAST_EXPECT(++count == 1);
|
||||
});
|
||||
ws.async_read(b,
|
||||
[&](error_code ec, std::size_t)
|
||||
{
|
||||
if(ec != boost::asio::error::operation_aborted)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
system_error{ec});
|
||||
++count;
|
||||
});
|
||||
ws.async_write(buffer(s),
|
||||
[&](error_code ec)
|
||||
{
|
||||
if(ec != boost::asio::error::operation_aborted)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
system_error{ec});
|
||||
++count;
|
||||
});
|
||||
ws.async_ping({},
|
||||
[&](error_code ec)
|
||||
{
|
||||
if(ec != boost::asio::error::operation_aborted)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
system_error{ec});
|
||||
++count;
|
||||
});
|
||||
BEAST_EXPECT(count == 0);
|
||||
ios.run();
|
||||
BEAST_EXPECT(count == 4);
|
||||
});
|
||||
|
||||
// Four-way: read, write, ping, close
|
||||
doFailLoop([&](test::fail_counter& fc)
|
||||
{
|
||||
echo_server es{log};
|
||||
boost::asio::io_service ios;
|
||||
stream<test::stream> ws{ios, fc};
|
||||
ws.next_layer().connect(es.stream());
|
||||
ws.handshake("localhost", "/");
|
||||
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 && ec != boost::asio::error::operation_aborted)
|
||||
{
|
||||
BEAST_EXPECTS(ec, ec.message());
|
||||
BOOST_THROW_EXCEPTION(
|
||||
system_error{ec});
|
||||
}
|
||||
if(! ec)
|
||||
BEAST_EXPECT(to_string(b.data()) == s);
|
||||
++count;
|
||||
});
|
||||
ws.async_write(buffer(s),
|
||||
[&](error_code ec)
|
||||
{
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
system_error{ec});
|
||||
BEAST_EXPECT(++count == 1);
|
||||
});
|
||||
ws.async_ping({},
|
||||
[&](error_code ec)
|
||||
{
|
||||
if(ec != boost::asio::error::operation_aborted)
|
||||
{
|
||||
BEAST_EXPECTS(ec, ec.message());
|
||||
BOOST_THROW_EXCEPTION(
|
||||
system_error{ec});
|
||||
}
|
||||
++count;
|
||||
});
|
||||
ws.async_close({},
|
||||
[&](error_code ec)
|
||||
{
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
system_error{ec});
|
||||
BEAST_EXPECT(++count == 2);
|
||||
});
|
||||
BEAST_EXPECT(count == 0);
|
||||
ios.run();
|
||||
BEAST_EXPECT(count == 4);
|
||||
});
|
||||
|
||||
// Four-way: ping, read, write, close
|
||||
doFailLoop([&](test::fail_counter& fc)
|
||||
{
|
||||
echo_server es{log};
|
||||
boost::asio::io_service ios;
|
||||
stream<test::stream> ws{ios, fc};
|
||||
ws.next_layer().connect(es.stream());
|
||||
ws.handshake("localhost", "/");
|
||||
std::size_t count = 0;
|
||||
std::string const s = "Hello, world!";
|
||||
multi_buffer b;
|
||||
ws.async_ping({},
|
||||
[&](error_code ec)
|
||||
{
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
system_error{ec});
|
||||
BEAST_EXPECT(++count == 1);
|
||||
});
|
||||
ws.async_read(b,
|
||||
[&](error_code ec, std::size_t)
|
||||
{
|
||||
if(ec != boost::asio::error::operation_aborted)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
system_error{ec});
|
||||
++count;
|
||||
});
|
||||
ws.async_write(buffer(s),
|
||||
[&](error_code ec)
|
||||
{
|
||||
if(ec != boost::asio::error::operation_aborted)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
system_error{ec});
|
||||
++count;
|
||||
});
|
||||
ws.async_close({},
|
||||
[&](error_code ec)
|
||||
{
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
system_error{ec});
|
||||
BEAST_EXPECT(++count == 2);
|
||||
});
|
||||
BEAST_EXPECT(count == 0);
|
||||
ios.run();
|
||||
BEAST_EXPECT(count == 4);
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
@@ -357,7 +618,7 @@ public:
|
||||
run() override
|
||||
{
|
||||
testClose();
|
||||
testCloseSuspend();
|
||||
testSuspend();
|
||||
testContHook();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -7,12 +7,8 @@
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#include <boost/beast/websocket/stream.hpp>
|
||||
#include <boost/beast/websocket/detail/frame.hpp>
|
||||
#include <boost/beast/unit_test/suite.hpp>
|
||||
#include <boost/beast/test/yield_to.hpp>
|
||||
#include <initializer_list>
|
||||
#include <climits>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
@@ -21,7 +17,6 @@ namespace detail {
|
||||
|
||||
class frame_test
|
||||
: public beast::unit_test::suite
|
||||
, public test::enable_yield_to
|
||||
{
|
||||
public:
|
||||
void testCloseCodes()
|
||||
@@ -47,7 +42,7 @@ public:
|
||||
test_fh()
|
||||
{
|
||||
op = detail::opcode::text;
|
||||
fin = false;
|
||||
fin = true;
|
||||
mask = false;
|
||||
rsv1 = false;
|
||||
rsv2 = false;
|
||||
@@ -57,8 +52,20 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
testWriteFrame()
|
||||
{
|
||||
test_fh fh;
|
||||
fh.rsv2 = true;
|
||||
fh.rsv3 = true;
|
||||
fh.len = 65536;
|
||||
frame_buffer fb;
|
||||
write(fb, fh);
|
||||
}
|
||||
|
||||
void run() override
|
||||
{
|
||||
testWriteFrame();
|
||||
testCloseCodes();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -214,10 +214,265 @@ public:
|
||||
);
|
||||
}
|
||||
|
||||
// Compression Extensions for WebSocket
|
||||
//
|
||||
// https://tools.ietf.org/html/rfc7692
|
||||
//
|
||||
void
|
||||
testExtRead()
|
||||
{
|
||||
detail::pmd_offer po;
|
||||
|
||||
auto const accept =
|
||||
[&](string_view s)
|
||||
{
|
||||
http::fields f;
|
||||
f.set(http::field::sec_websocket_extensions, s);
|
||||
po = detail::pmd_offer();
|
||||
detail::pmd_read(po, f);
|
||||
BEAST_EXPECT(po.accept);
|
||||
};
|
||||
|
||||
auto const reject =
|
||||
[&](string_view s)
|
||||
{
|
||||
http::fields f;
|
||||
f.set(http::field::sec_websocket_extensions, s);
|
||||
po = detail::pmd_offer();
|
||||
detail::pmd_read(po, f);
|
||||
BEAST_EXPECT(! po.accept);
|
||||
};
|
||||
|
||||
// duplicate parameters
|
||||
reject("permessage-deflate; server_max_window_bits=8; server_max_window_bits=8");
|
||||
|
||||
// missing value
|
||||
reject("permessage-deflate; server_max_window_bits");
|
||||
reject("permessage-deflate; server_max_window_bits=");
|
||||
|
||||
// invalid value
|
||||
reject("permessage-deflate; server_max_window_bits=-1");
|
||||
reject("permessage-deflate; server_max_window_bits=7");
|
||||
reject("permessage-deflate; server_max_window_bits=16");
|
||||
reject("permessage-deflate; server_max_window_bits=999999999999999999999999");
|
||||
reject("permessage-deflate; server_max_window_bits=9a");
|
||||
|
||||
// duplicate parameters
|
||||
reject("permessage-deflate; client_max_window_bits=8; client_max_window_bits=8");
|
||||
|
||||
// optional value excluded
|
||||
accept("permessage-deflate; client_max_window_bits");
|
||||
BEAST_EXPECT(po.client_max_window_bits == -1);
|
||||
accept("permessage-deflate; client_max_window_bits=");
|
||||
BEAST_EXPECT(po.client_max_window_bits == -1);
|
||||
|
||||
// invalid value
|
||||
reject("permessage-deflate; client_max_window_bits=-1");
|
||||
reject("permessage-deflate; client_max_window_bits=7");
|
||||
reject("permessage-deflate; client_max_window_bits=16");
|
||||
reject("permessage-deflate; client_max_window_bits=999999999999999999999999");
|
||||
|
||||
// duplicate parameters
|
||||
reject("permessage-deflate; server_no_context_takeover; server_no_context_takeover");
|
||||
|
||||
// valueless parameter
|
||||
accept("permessage-deflate; server_no_context_takeover");
|
||||
BEAST_EXPECT(po.server_no_context_takeover);
|
||||
accept("permessage-deflate; server_no_context_takeover=");
|
||||
BEAST_EXPECT(po.server_no_context_takeover);
|
||||
|
||||
// disallowed value
|
||||
reject("permessage-deflate; server_no_context_takeover=-1");
|
||||
reject("permessage-deflate; server_no_context_takeover=x");
|
||||
reject("permessage-deflate; server_no_context_takeover=\"yz\"");
|
||||
reject("permessage-deflate; server_no_context_takeover=999999999999999999999999");
|
||||
|
||||
// duplicate parameters
|
||||
reject("permessage-deflate; client_no_context_takeover; client_no_context_takeover");
|
||||
|
||||
// valueless parameter
|
||||
accept("permessage-deflate; client_no_context_takeover");
|
||||
BEAST_EXPECT(po.client_no_context_takeover);
|
||||
accept("permessage-deflate; client_no_context_takeover=");
|
||||
BEAST_EXPECT(po.client_no_context_takeover);
|
||||
|
||||
// disallowed value
|
||||
reject("permessage-deflate; client_no_context_takeover=-1");
|
||||
reject("permessage-deflate; client_no_context_takeover=x");
|
||||
reject("permessage-deflate; client_no_context_takeover=\"yz\"");
|
||||
reject("permessage-deflate; client_no_context_takeover=999999999999999999999999");
|
||||
|
||||
// unknown extension parameter
|
||||
reject("permessage-deflate; unknown");
|
||||
reject("permessage-deflate; unknown=");
|
||||
reject("permessage-deflate; unknown=1");
|
||||
reject("permessage-deflate; unknown=x");
|
||||
reject("permessage-deflate; unknown=\"xy\"");
|
||||
}
|
||||
|
||||
void
|
||||
testExtWrite()
|
||||
{
|
||||
detail::pmd_offer po;
|
||||
|
||||
auto const check =
|
||||
[&](string_view match)
|
||||
{
|
||||
http::fields f;
|
||||
detail::pmd_write(f, po);
|
||||
BEAST_EXPECT(
|
||||
f[http::field::sec_websocket_extensions]
|
||||
== match);
|
||||
};
|
||||
|
||||
po.accept = true;
|
||||
po.server_max_window_bits = 0;
|
||||
po.client_max_window_bits = 0;
|
||||
po.server_no_context_takeover = false;
|
||||
po.client_no_context_takeover = false;
|
||||
|
||||
check("permessage-deflate");
|
||||
|
||||
po.server_max_window_bits = 10;
|
||||
check("permessage-deflate; server_max_window_bits=10");
|
||||
|
||||
po.server_max_window_bits = -1;
|
||||
check("permessage-deflate; server_max_window_bits");
|
||||
|
||||
po.server_max_window_bits = 0;
|
||||
po.client_max_window_bits = 10;
|
||||
check("permessage-deflate; client_max_window_bits=10");
|
||||
|
||||
po.client_max_window_bits = -1;
|
||||
check("permessage-deflate; client_max_window_bits");
|
||||
|
||||
po.client_max_window_bits = 0;
|
||||
po.server_no_context_takeover = true;
|
||||
check("permessage-deflate; server_no_context_takeover");
|
||||
|
||||
po.server_no_context_takeover = false;
|
||||
po.client_no_context_takeover = true;
|
||||
check("permessage-deflate; client_no_context_takeover");
|
||||
}
|
||||
|
||||
void
|
||||
testExtNegotiate()
|
||||
{
|
||||
permessage_deflate pmd;
|
||||
|
||||
auto const reject =
|
||||
[&](
|
||||
string_view offer)
|
||||
{
|
||||
detail::pmd_offer po;
|
||||
{
|
||||
http::fields f;
|
||||
f.set(http::field::sec_websocket_extensions, offer);
|
||||
detail::pmd_read(po, f);
|
||||
}
|
||||
http::fields f;
|
||||
detail::pmd_offer config;
|
||||
detail::pmd_negotiate(f, config, po, pmd);
|
||||
BEAST_EXPECT(! config.accept);
|
||||
};
|
||||
|
||||
auto const accept =
|
||||
[&](
|
||||
string_view offer,
|
||||
string_view result)
|
||||
{
|
||||
detail::pmd_offer po;
|
||||
{
|
||||
http::fields f;
|
||||
f.set(http::field::sec_websocket_extensions, offer);
|
||||
detail::pmd_read(po, f);
|
||||
}
|
||||
http::fields f;
|
||||
detail::pmd_offer config;
|
||||
detail::pmd_negotiate(f, config, po, pmd);
|
||||
auto const got =
|
||||
f[http::field::sec_websocket_extensions];
|
||||
BEAST_EXPECTS(got == result, got);
|
||||
{
|
||||
detail::pmd_offer poc;
|
||||
detail::pmd_read(poc, f);
|
||||
detail::pmd_normalize(poc);
|
||||
BEAST_EXPECT(poc.accept);
|
||||
}
|
||||
BEAST_EXPECT(config.server_max_window_bits != 0);
|
||||
BEAST_EXPECT(config.client_max_window_bits != 0);
|
||||
};
|
||||
|
||||
pmd.server_enable = true;
|
||||
pmd.server_max_window_bits = 15;
|
||||
pmd.client_max_window_bits = 15;
|
||||
pmd.server_no_context_takeover = false;
|
||||
pmd.client_no_context_takeover = false;
|
||||
|
||||
// default
|
||||
accept(
|
||||
"permessage-deflate",
|
||||
"permessage-deflate");
|
||||
|
||||
// non-default server_max_window_bits
|
||||
accept(
|
||||
"permessage-deflate; server_max_window_bits=14",
|
||||
"permessage-deflate; server_max_window_bits=14");
|
||||
|
||||
// explicit default server_max_window_bits
|
||||
accept(
|
||||
"permessage-deflate; server_max_window_bits=15",
|
||||
"permessage-deflate");
|
||||
|
||||
// minimum window size of 8 bits (a zlib bug)
|
||||
accept(
|
||||
"permessage-deflate; server_max_window_bits=8",
|
||||
"permessage-deflate; server_max_window_bits=9");
|
||||
|
||||
// non-default server_max_window_bits setting
|
||||
pmd.server_max_window_bits = 10;
|
||||
accept(
|
||||
"permessage-deflate",
|
||||
"permessage-deflate; server_max_window_bits=10");
|
||||
|
||||
// clamped server_max_window_bits setting #1
|
||||
pmd.server_max_window_bits = 10;
|
||||
accept(
|
||||
"permessage-deflate; server_max_window_bits=14",
|
||||
"permessage-deflate; server_max_window_bits=10");
|
||||
|
||||
// clamped server_max_window_bits setting #2
|
||||
pmd.server_max_window_bits=8;
|
||||
accept(
|
||||
"permessage-deflate; server_max_window_bits=14",
|
||||
"permessage-deflate; server_max_window_bits=9");
|
||||
|
||||
pmd.server_max_window_bits = 15;
|
||||
|
||||
// present with no value
|
||||
accept(
|
||||
"permessage-deflate; client_max_window_bits",
|
||||
"permessage-deflate");
|
||||
|
||||
// present with no value, non-default setting
|
||||
pmd.client_max_window_bits = 10;
|
||||
accept(
|
||||
"permessage-deflate; client_max_window_bits",
|
||||
"permessage-deflate; client_max_window_bits=10");
|
||||
|
||||
// absent, non-default setting
|
||||
pmd.client_max_window_bits = 10;
|
||||
reject(
|
||||
"permessage-deflate");
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testHandshake();
|
||||
testExtRead();
|
||||
testExtWrite();
|
||||
testExtNegotiate();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -89,40 +89,10 @@ public:
|
||||
{
|
||||
doTestPing(AsyncClient{yield});
|
||||
});
|
||||
|
||||
// suspend on write
|
||||
{
|
||||
echo_server es{log};
|
||||
error_code ec;
|
||||
boost::asio::io_service ios;
|
||||
stream<test::stream> ws{ios};
|
||||
ws.next_layer().connect(es.stream());
|
||||
ws.handshake("localhost", "/", ec);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
std::size_t count = 0;
|
||||
ws.async_write(sbuf("*"),
|
||||
[&](error_code ec)
|
||||
{
|
||||
++count;
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
});
|
||||
BEAST_EXPECT(ws.wr_block_);
|
||||
ws.async_ping("",
|
||||
[&](error_code ec)
|
||||
{
|
||||
++count;
|
||||
BEAST_EXPECTS(
|
||||
ec == boost::asio::error::operation_aborted,
|
||||
ec.message());
|
||||
});
|
||||
ws.async_close({}, [&](error_code){});
|
||||
ios.run();
|
||||
BEAST_EXPECT(count == 2);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testPingSuspend()
|
||||
testSuspend()
|
||||
{
|
||||
// suspend on write
|
||||
doFailLoop([&](test::fail_counter& fc)
|
||||
@@ -352,6 +322,45 @@ public:
|
||||
BEAST_EXPECT(count == 2);
|
||||
});
|
||||
|
||||
// don't ping on close
|
||||
doFailLoop([&](test::fail_counter& fc)
|
||||
{
|
||||
echo_server es{log};
|
||||
error_code ec;
|
||||
boost::asio::io_service ios;
|
||||
stream<test::stream> ws{ios, fc};
|
||||
ws.next_layer().connect(es.stream());
|
||||
ws.handshake("localhost", "/");
|
||||
std::size_t count = 0;
|
||||
ws.async_write(sbuf("*"),
|
||||
[&](error_code ec)
|
||||
{
|
||||
++count;
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
system_error{ec});
|
||||
});
|
||||
BEAST_EXPECT(ws.wr_block_);
|
||||
ws.async_ping("",
|
||||
[&](error_code ec)
|
||||
{
|
||||
++count;
|
||||
if(ec != boost::asio::error::operation_aborted)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
system_error{ec});
|
||||
});
|
||||
ws.async_close({},
|
||||
[&](error_code)
|
||||
{
|
||||
++count;
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
system_error{ec});
|
||||
});
|
||||
ios.run();
|
||||
BEAST_EXPECT(count == 3);
|
||||
});
|
||||
|
||||
{
|
||||
echo_server es{log, kind::async};
|
||||
boost::asio::io_service ios;
|
||||
@@ -437,7 +446,7 @@ public:
|
||||
run() override
|
||||
{
|
||||
testPing();
|
||||
testPingSuspend();
|
||||
testSuspend();
|
||||
testContHook();
|
||||
}
|
||||
};
|
||||
|
||||
+388
-28
@@ -12,6 +12,8 @@
|
||||
|
||||
#include "test.hpp"
|
||||
|
||||
#include <boost/asio/write.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
@@ -104,7 +106,7 @@ public:
|
||||
});
|
||||
|
||||
// two part message
|
||||
// this triggers "fill the read buffer first"
|
||||
// triggers "fill the read buffer first"
|
||||
doTest(pmd, [&](ws_type& ws)
|
||||
{
|
||||
w.write_raw(ws, sbuf(
|
||||
@@ -202,6 +204,58 @@ public:
|
||||
BEAST_EXPECT(to_string(b.data()) == "Hello, World!");
|
||||
});
|
||||
|
||||
// masked message, big
|
||||
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(2000, '*');
|
||||
ws.auto_fragment(false);
|
||||
ws.binary(false);
|
||||
w.write(ws, buffer(s));
|
||||
multi_buffer b;
|
||||
w.read(ws, b);
|
||||
BEAST_EXPECT(ws.got_text());
|
||||
BEAST_EXPECT(to_string(b.data()) == s);
|
||||
ws.next_layer().close();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
ts.close();
|
||||
throw;
|
||||
}
|
||||
});
|
||||
|
||||
// close
|
||||
doFailLoop([&](test::fail_counter& fc)
|
||||
{
|
||||
echo_server es{log, kind::async};
|
||||
boost::asio::io_service ios;
|
||||
stream<test::stream> ws{ios, 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});
|
||||
});
|
||||
ios.run();
|
||||
BEAST_EXPECT(count == 1);
|
||||
});
|
||||
|
||||
// already closed
|
||||
doTest(pmd, [&](ws_type& ws)
|
||||
{
|
||||
@@ -249,7 +303,7 @@ public:
|
||||
doReadTest(w, ws, close_code::protocol_error);
|
||||
});
|
||||
|
||||
// receive bad close
|
||||
// bad close
|
||||
doTest(pmd, [&](ws_type& ws)
|
||||
{
|
||||
put(ws.next_layer().buffer(), cbuf(
|
||||
@@ -257,15 +311,6 @@ public:
|
||||
doFailTest(w, ws, error::failed);
|
||||
});
|
||||
|
||||
// expected cont
|
||||
doTest(pmd, [&](ws_type& ws)
|
||||
{
|
||||
w.write_some(ws, false, boost::asio::null_buffers{});
|
||||
w.write_raw(ws, cbuf(
|
||||
0x81, 0x80, 0xff, 0xff, 0xff, 0xff));
|
||||
doReadTest(w, ws, close_code::protocol_error);
|
||||
});
|
||||
|
||||
// message size above 2^64
|
||||
doTest(pmd, [&](ws_type& ws)
|
||||
{
|
||||
@@ -284,14 +329,6 @@ public:
|
||||
doFailTest(w, ws, error::failed);
|
||||
});
|
||||
|
||||
// unexpected cont
|
||||
doTest(pmd, [&](ws_type& ws)
|
||||
{
|
||||
w.write_raw(ws, cbuf(
|
||||
0x80, 0x80, 0xff, 0xff, 0xff, 0xff));
|
||||
doReadTest(w, ws, close_code::protocol_error);
|
||||
});
|
||||
|
||||
// bad utf8
|
||||
doTest(pmd, [&](ws_type& ws)
|
||||
{
|
||||
@@ -313,10 +350,23 @@ public:
|
||||
doTest(pmd, [&](ws_type& ws)
|
||||
{
|
||||
std::string const s =
|
||||
random_string() +
|
||||
"Hello, world!" "\xc0";
|
||||
w.write(ws, buffer(s));
|
||||
doReadTest(w, ws, close_code::bad_payload);
|
||||
"\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::failed)
|
||||
throw;
|
||||
}
|
||||
});
|
||||
|
||||
// close frames
|
||||
@@ -441,6 +491,34 @@ public:
|
||||
BEAST_EXPECT(to_string(b.data()) == s);
|
||||
});
|
||||
|
||||
// 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, buffer(s));
|
||||
multi_buffer b;
|
||||
w.read(ws, b);
|
||||
BEAST_EXPECT(ws.got_text());
|
||||
BEAST_EXPECT(to_string(b.data()) == s);
|
||||
ws.next_layer().close();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
ts.close();
|
||||
throw;
|
||||
}
|
||||
});
|
||||
|
||||
// empty message
|
||||
doTest(pmd, [&](ws_type& ws)
|
||||
{
|
||||
@@ -571,11 +649,77 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
testReadSuspend()
|
||||
testSuspend()
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
#if 1
|
||||
// suspend on read block
|
||||
doFailLoop([&](test::fail_counter& fc)
|
||||
{
|
||||
echo_server es{log};
|
||||
boost::asio::io_service ios;
|
||||
stream<test::stream> ws{ios, 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.rd_block_)
|
||||
ios.run_one();
|
||||
multi_buffer b;
|
||||
ws.async_read(b,
|
||||
[&](error_code ec, std::size_t)
|
||||
{
|
||||
if(ec != boost::asio::error::operation_aborted)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
system_error{ec});
|
||||
BEAST_EXPECT(++count == 2);
|
||||
});
|
||||
ios.run();
|
||||
BEAST_EXPECT(count == 2);
|
||||
});
|
||||
#endif
|
||||
|
||||
// suspend on write
|
||||
// suspend on release read block
|
||||
doFailLoop([&](test::fail_counter& fc)
|
||||
{
|
||||
//log << "fc.count()==" << fc.count() << std::endl;
|
||||
echo_server es{log};
|
||||
boost::asio::io_service ios;
|
||||
stream<test::stream> ws{ios, 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 != boost::asio::error::operation_aborted)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
system_error{ec});
|
||||
BEAST_EXPECT(++count == 2);
|
||||
});
|
||||
BOOST_ASSERT(ws.rd_block_);
|
||||
ws.async_close({},
|
||||
[&](error_code ec)
|
||||
{
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
system_error{ec});
|
||||
BEAST_EXPECT(++count == 1);
|
||||
});
|
||||
ios.run();
|
||||
BEAST_EXPECT(count == 2);
|
||||
});
|
||||
|
||||
#if 1
|
||||
// suspend on write pong
|
||||
doFailLoop([&](test::fail_counter& fc)
|
||||
{
|
||||
echo_server es{log};
|
||||
@@ -592,25 +736,238 @@ public:
|
||||
ws.async_read(b,
|
||||
[&](error_code ec, std::size_t)
|
||||
{
|
||||
++count;
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
system_error{ec});
|
||||
BEAST_EXPECT(to_string(b.data()) == s);
|
||||
++count;
|
||||
});
|
||||
BEAST_EXPECT(ws.rd_block_);
|
||||
ws.async_write(buffer(s),
|
||||
[&](error_code ec)
|
||||
{
|
||||
++count;
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
system_error{ec});
|
||||
++count;
|
||||
});
|
||||
BEAST_EXPECT(ws.wr_block_);
|
||||
ios.run();
|
||||
BEAST_EXPECT(count == 2);
|
||||
});
|
||||
|
||||
// Ignore ping when closing
|
||||
doFailLoop([&](test::fail_counter& fc)
|
||||
{
|
||||
echo_server es{log};
|
||||
boost::asio::io_service ios;
|
||||
stream<test::stream> ws{ios, 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(to_string(b.data()) == "**");
|
||||
BEAST_EXPECT(++count == 1);
|
||||
b.consume(b.size());
|
||||
ws.async_read(b,
|
||||
[&](error_code ec, std::size_t)
|
||||
{
|
||||
if(ec != boost::asio::error::operation_aborted)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
system_error{ec});
|
||||
BEAST_EXPECT(++count == 3);
|
||||
});
|
||||
});
|
||||
BEAST_EXPECT(ws.rd_block_);
|
||||
ws.async_close({},
|
||||
[&](error_code ec)
|
||||
{
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
system_error{ec});
|
||||
BEAST_EXPECT(++count == 2);
|
||||
});
|
||||
BEAST_EXPECT(ws.wr_block_);
|
||||
ios.run();
|
||||
BEAST_EXPECT(count == 3);
|
||||
});
|
||||
|
||||
// See if we are already closing
|
||||
doFailLoop([&](test::fail_counter& fc)
|
||||
{
|
||||
echo_server es{log};
|
||||
boost::asio::io_service ios;
|
||||
stream<test::stream> ws{ios, 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 != boost::asio::error::operation_aborted)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
system_error{ec});
|
||||
BEAST_EXPECT(++count == 2);
|
||||
});
|
||||
BEAST_EXPECT(ws.rd_block_);
|
||||
ws.async_close({},
|
||||
[&](error_code ec)
|
||||
{
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
system_error{ec});
|
||||
BEAST_EXPECT(++count == 1);
|
||||
});
|
||||
BEAST_EXPECT(ws.wr_block_);
|
||||
ios.run();
|
||||
BEAST_EXPECT(count == 2);
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
testParseFrame()
|
||||
{
|
||||
auto const bad =
|
||||
[&](string_view s)
|
||||
{
|
||||
echo_server es{log};
|
||||
boost::asio::io_service ios;
|
||||
stream<test::stream> ws{ios};
|
||||
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};
|
||||
boost::asio::io_service ios;
|
||||
stream<test::stream> ws{ios};
|
||||
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, '*');
|
||||
error_code ec;
|
||||
multi_buffer b;
|
||||
ws.async_read(b,
|
||||
[&](error_code ec, std::size_t)
|
||||
{
|
||||
++count;
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(to_string(b.data()) == s);
|
||||
});
|
||||
ios.run_one();
|
||||
es.stream().write_some(
|
||||
boost::asio::buffer("\x01" + s));
|
||||
ios.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};
|
||||
boost::asio::io_service ios;
|
||||
stream<test::stream> ws{ios};
|
||||
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};
|
||||
boost::asio::io_service ios;
|
||||
stream<test::stream> ws{ios};
|
||||
ws.next_layer().connect(es.stream());
|
||||
ws.handshake("localhost", "/");
|
||||
ws.next_layer().append(
|
||||
"\x89\x02*");
|
||||
std::size_t count = 0;
|
||||
error_code ec;
|
||||
multi_buffer b;
|
||||
ws.async_read(b,
|
||||
[&](error_code ec, std::size_t)
|
||||
{
|
||||
++count;
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(to_string(b.data()) == "**");
|
||||
});
|
||||
ios.run_one();
|
||||
es.stream().write_some(
|
||||
boost::asio::buffer(
|
||||
"*" "\x81\x02**"));
|
||||
ios.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
|
||||
@@ -631,6 +988,7 @@ public:
|
||||
buf, sizeof(buf)}};
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
asio_handler_is_continuation(&op);
|
||||
pass();
|
||||
}
|
||||
{
|
||||
struct handler
|
||||
@@ -645,6 +1003,7 @@ public:
|
||||
handler{}, ws, b, 32, true};
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
asio_handler_is_continuation(&op);
|
||||
pass();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -652,7 +1011,8 @@ public:
|
||||
run() override
|
||||
{
|
||||
testRead();
|
||||
testReadSuspend();
|
||||
testSuspend();
|
||||
testParseFrame();
|
||||
testContHook();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -34,8 +34,6 @@ public:
|
||||
req.insert("Connection", "upgrade");
|
||||
BEAST_EXPECT(! is_upgrade(req));
|
||||
req.insert("Upgrade", "websocket");
|
||||
BEAST_EXPECT(! is_upgrade(req));
|
||||
req.insert("Sec-WebSocket-Version", "13");
|
||||
BEAST_EXPECT(is_upgrade(req));
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ public:
|
||||
void
|
||||
testOptions()
|
||||
{
|
||||
stream<test::stream> ws(ios_);
|
||||
stream<test::stream> ws{ios_};
|
||||
ws.auto_fragment(true);
|
||||
ws.write_buffer_size(2048);
|
||||
ws.binary(false);
|
||||
@@ -36,48 +36,71 @@ public:
|
||||
{
|
||||
pass();
|
||||
}
|
||||
|
||||
auto const bad =
|
||||
[&](permessage_deflate const& pmd)
|
||||
{
|
||||
stream<test::stream> ws{ios_};
|
||||
try
|
||||
{
|
||||
ws.set_option(pmd);
|
||||
fail("", __FILE__, __LINE__);
|
||||
}
|
||||
catch(std::exception const&)
|
||||
{
|
||||
pass();
|
||||
}
|
||||
};
|
||||
|
||||
{
|
||||
permessage_deflate pmd;
|
||||
pmd.server_max_window_bits = 16;
|
||||
bad(pmd);
|
||||
}
|
||||
|
||||
{
|
||||
permessage_deflate pmd;
|
||||
pmd.server_max_window_bits = 8;
|
||||
bad(pmd);
|
||||
}
|
||||
|
||||
{
|
||||
permessage_deflate pmd;
|
||||
pmd.client_max_window_bits = 16;
|
||||
bad(pmd);
|
||||
}
|
||||
|
||||
{
|
||||
permessage_deflate pmd;
|
||||
pmd.client_max_window_bits = 8;
|
||||
bad(pmd);
|
||||
}
|
||||
|
||||
{
|
||||
permessage_deflate pmd;
|
||||
pmd.compLevel = -1;
|
||||
bad(pmd);
|
||||
}
|
||||
|
||||
{
|
||||
permessage_deflate pmd;
|
||||
pmd.compLevel = 10;
|
||||
bad(pmd);
|
||||
}
|
||||
|
||||
{
|
||||
permessage_deflate pmd;
|
||||
pmd.memLevel = 0;
|
||||
bad(pmd);
|
||||
}
|
||||
|
||||
{
|
||||
permessage_deflate pmd;
|
||||
pmd.memLevel = 10;
|
||||
bad(pmd);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
template<class Wrap>
|
||||
void
|
||||
doTestStream(Wrap const& w,
|
||||
permessage_deflate const& pmd)
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
|
||||
// send pong
|
||||
doTest(pmd, [&](ws_type& ws)
|
||||
{
|
||||
w.pong(ws, "");
|
||||
});
|
||||
|
||||
// send auto fragmented message
|
||||
doTest(pmd, [&](ws_type& ws)
|
||||
{
|
||||
ws.auto_fragment(true);
|
||||
ws.write_buffer_size(8);
|
||||
w.write(ws, sbuf("Now is the time for all good men"));
|
||||
multi_buffer b;
|
||||
w.read(ws, b);
|
||||
BEAST_EXPECT(to_string(b.data()) == "Now is the time for all good men");
|
||||
});
|
||||
|
||||
// send message with write buffer limit
|
||||
doTest(pmd, [&](ws_type& ws)
|
||||
{
|
||||
std::string s(2000, '*');
|
||||
ws.write_buffer_size(1200);
|
||||
w.write(ws, buffer(s.data(), s.size()));
|
||||
multi_buffer b;
|
||||
w.read(ws, b);
|
||||
BEAST_EXPECT(to_string(b.data()) == s);
|
||||
});
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
@@ -103,42 +126,6 @@ public:
|
||||
sizeof(websocket::stream<test::stream&>) << std::endl;
|
||||
|
||||
testOptions();
|
||||
|
||||
#if 0
|
||||
auto const testStream =
|
||||
[this](permessage_deflate const& pmd)
|
||||
{
|
||||
doTestStream(SyncClient{}, pmd);
|
||||
|
||||
yield_to(
|
||||
[&](yield_context yield)
|
||||
{
|
||||
doTestStream(AsyncClient{yield}, pmd);
|
||||
});
|
||||
};
|
||||
|
||||
permessage_deflate pmd;
|
||||
|
||||
pmd.client_enable = false;
|
||||
pmd.server_enable = false;
|
||||
testStream(pmd);
|
||||
|
||||
pmd.client_enable = true;
|
||||
pmd.server_enable = true;
|
||||
pmd.client_max_window_bits = 10;
|
||||
pmd.client_no_context_takeover = false;
|
||||
pmd.compLevel = 1;
|
||||
pmd.memLevel = 1;
|
||||
testStream(pmd);
|
||||
|
||||
pmd.client_enable = true;
|
||||
pmd.server_enable = true;
|
||||
pmd.client_max_window_bits = 10;
|
||||
pmd.client_no_context_takeover = true;
|
||||
pmd.compLevel = 1;
|
||||
pmd.memLevel = 1;
|
||||
testStream(pmd);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -264,7 +264,7 @@ public:
|
||||
Test const& f, std::size_t limit = 200)
|
||||
{
|
||||
std::size_t n;
|
||||
for(n = 0; n <= limit; ++n)
|
||||
for(n = 0; n < limit; ++n)
|
||||
{
|
||||
test::fail_counter fc{n};
|
||||
try
|
||||
@@ -288,7 +288,7 @@ public:
|
||||
{
|
||||
// This number has to be high for the
|
||||
// test that writes the large buffer.
|
||||
static std::size_t constexpr limit = 1000;
|
||||
static std::size_t constexpr limit = 200;
|
||||
|
||||
doFailLoop(
|
||||
[&](test::fail_counter& fc)
|
||||
@@ -308,12 +308,12 @@ public:
|
||||
{
|
||||
// This number has to be high for the
|
||||
// test that writes the large buffer.
|
||||
static std::size_t constexpr limit = 1000;
|
||||
static std::size_t constexpr limit = 200;
|
||||
|
||||
for(int i = 0; i < 2; ++i)
|
||||
{
|
||||
std::size_t n;
|
||||
for(n = 0; n <= limit; ++n)
|
||||
for(n = 0; n < limit; ++n)
|
||||
{
|
||||
test::fail_counter fc{n};
|
||||
test::stream ts{ios_, fc};
|
||||
|
||||
@@ -73,6 +73,20 @@ public:
|
||||
BEAST_EXPECT(b.size() == 0);
|
||||
});
|
||||
|
||||
// fragmented message
|
||||
doTest(pmd, [&](ws_type& ws)
|
||||
{
|
||||
ws.auto_fragment(false);
|
||||
ws.binary(false);
|
||||
std::string const s = "Hello, world!";
|
||||
w.write_some(ws, false, buffer(s.data(), 5));
|
||||
w.write_some(ws, true, buffer(s.data() + 5, s.size() - 5));
|
||||
multi_buffer b;
|
||||
w.read(ws, b);
|
||||
BEAST_EXPECT(ws.got_text());
|
||||
BEAST_EXPECT(to_string(b.data()) == s);
|
||||
});
|
||||
|
||||
// continuation
|
||||
doTest(pmd, [&](ws_type& ws)
|
||||
{
|
||||
|
||||
@@ -114,6 +114,7 @@ struct fail_error_code : error_code
|
||||
class fail_counter
|
||||
{
|
||||
std::size_t n_;
|
||||
std::size_t i_ = 0;
|
||||
error_code ec_;
|
||||
|
||||
public:
|
||||
@@ -131,13 +132,20 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
/// Returns the fail index
|
||||
std::size_t
|
||||
count() const
|
||||
{
|
||||
return n_;
|
||||
}
|
||||
|
||||
/// Throw an exception on the Nth failure
|
||||
void
|
||||
fail()
|
||||
{
|
||||
if(n_ > 0)
|
||||
--n_;
|
||||
if(! n_)
|
||||
if(i_ < n_)
|
||||
++i_;
|
||||
if(i_ == n_)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec_});
|
||||
}
|
||||
|
||||
@@ -145,9 +153,9 @@ public:
|
||||
bool
|
||||
fail(error_code& ec)
|
||||
{
|
||||
if(n_ > 0)
|
||||
--n_;
|
||||
if(! n_)
|
||||
if(i_ < n_)
|
||||
++i_;
|
||||
if(i_ == n_)
|
||||
{
|
||||
ec = ec_;
|
||||
return true;
|
||||
|
||||
@@ -78,6 +78,11 @@ class stream
|
||||
std::size_t write_max =
|
||||
(std::numeric_limits<std::size_t>::max)();
|
||||
|
||||
~state()
|
||||
{
|
||||
BOOST_ASSERT(! op);
|
||||
}
|
||||
|
||||
explicit
|
||||
state(
|
||||
boost::asio::io_service& ios_,
|
||||
@@ -87,11 +92,6 @@ class stream
|
||||
{
|
||||
}
|
||||
|
||||
~state()
|
||||
{
|
||||
BOOST_ASSERT(! op);
|
||||
}
|
||||
|
||||
void
|
||||
on_write()
|
||||
{
|
||||
@@ -119,6 +119,10 @@ public:
|
||||
/// Destructor
|
||||
~stream()
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::mutex> lock{in_->m};
|
||||
in_->op.reset();
|
||||
}
|
||||
auto out = out_.lock();
|
||||
if(out)
|
||||
{
|
||||
@@ -612,16 +616,17 @@ teardown(
|
||||
stream& s,
|
||||
boost::system::error_code& ec)
|
||||
{
|
||||
if(s.in_->fc)
|
||||
{
|
||||
if(s.in_->fc->fail(ec))
|
||||
return;
|
||||
}
|
||||
if( s.in_->fc &&
|
||||
s.in_->fc->fail(ec))
|
||||
return;
|
||||
|
||||
s.close();
|
||||
|
||||
if( s.in_->fc &&
|
||||
s.in_->fc->fail(ec))
|
||||
ec = boost::asio::error::eof;
|
||||
else
|
||||
{
|
||||
s.close();
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
}
|
||||
|
||||
template<class TeardownHandler>
|
||||
@@ -633,10 +638,17 @@ async_teardown(
|
||||
TeardownHandler&& handler)
|
||||
{
|
||||
error_code ec;
|
||||
if(s.in_->fc && s.in_->fc->fail(ec))
|
||||
if( s.in_->fc &&
|
||||
s.in_->fc->fail(ec))
|
||||
return s.get_io_service().post(
|
||||
bind_handler(std::move(handler), ec));
|
||||
s.close();
|
||||
if( s.in_->fc &&
|
||||
s.in_->fc->fail(ec))
|
||||
ec = boost::asio::error::eof;
|
||||
else
|
||||
ec.assign(0, ec.category());
|
||||
|
||||
s.get_io_service().post(
|
||||
bind_handler(std::move(handler), ec));
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user