mirror of
https://github.com/boostorg/beast.git
synced 2025-08-03 14:54:32 +02:00
websocket ping tests
This commit is contained in:
@@ -5,6 +5,7 @@ WebSocket:
|
||||
* Fix async_read_some handler signature
|
||||
* websocket close fixes and tests
|
||||
* websocket handshake uses coroutine
|
||||
* websocket ping tests
|
||||
|
||||
API Changes:
|
||||
|
||||
|
@@ -45,6 +45,7 @@ template<class... Args>
|
||||
stream<NextLayer>::
|
||||
stream(Args&&... args)
|
||||
: stream_(std::forward<Args>(args)...)
|
||||
, t_(1)
|
||||
{
|
||||
BOOST_ASSERT(rd_.buf.max_size() >=
|
||||
max_control_frame_size);
|
||||
|
@@ -131,11 +131,11 @@ class stream
|
||||
// tokens are used to order reads and writes
|
||||
class token
|
||||
{
|
||||
unsigned char id_ = 1;
|
||||
explicit token(unsigned char id) : id_(id) {}
|
||||
unsigned char id_ = 0;
|
||||
public:
|
||||
token() = default;
|
||||
token(token const&) = default;
|
||||
explicit token(unsigned char id) : id_(id) {}
|
||||
operator bool() const { return id_ != 0; }
|
||||
bool operator==(token const& t) { return id_ == t.id_; }
|
||||
bool operator!=(token const& t) { return id_ != t.id_; }
|
||||
@@ -225,7 +225,7 @@ class stream
|
||||
detail::opcode::text; // outgoing message type
|
||||
control_cb_type ctrl_cb_; // control callback
|
||||
role_type role_; // server or client
|
||||
bool failed_; // the connection failed
|
||||
bool failed_ = true; // the connection failed
|
||||
|
||||
bool rd_close_; // read close frame
|
||||
bool wr_close_; // sent close frame
|
||||
|
@@ -860,6 +860,10 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Accept
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
template<class Client>
|
||||
@@ -1269,8 +1273,139 @@ public:
|
||||
{
|
||||
doTestAccept(AsyncClient{yield});
|
||||
});
|
||||
|
||||
//
|
||||
// Bad requests
|
||||
//
|
||||
|
||||
auto const check =
|
||||
[&](error_code const& ev, std::string const& s)
|
||||
{
|
||||
for(int i = 0; i < 3; ++i)
|
||||
{
|
||||
std::size_t n;
|
||||
switch(i)
|
||||
{
|
||||
default:
|
||||
case 0:
|
||||
n = 1;
|
||||
break;
|
||||
case 1:
|
||||
n = s.size() / 2;
|
||||
break;
|
||||
case 2:
|
||||
n = s.size() - 1;
|
||||
break;
|
||||
}
|
||||
stream<test::stream> ws{ios_};
|
||||
ws.next_layer().str(
|
||||
s.substr(n, s.size() - n));
|
||||
try
|
||||
{
|
||||
ws.accept(
|
||||
boost::asio::buffer(s.data(), n));
|
||||
BEAST_EXPECTS(! ev, ev.message());
|
||||
}
|
||||
catch(system_error const& se)
|
||||
{
|
||||
BEAST_EXPECTS(se.code() == ev, se.what());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// wrong version
|
||||
check(http::error::end_of_stream,
|
||||
"GET / HTTP/1.0\r\n"
|
||||
"Host: localhost:80\r\n"
|
||||
"Upgrade: WebSocket\r\n"
|
||||
"Connection: keep-alive,upgrade\r\n"
|
||||
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
|
||||
"Sec-WebSocket-Version: 13\r\n"
|
||||
"\r\n"
|
||||
);
|
||||
// wrong method
|
||||
check(error::handshake_failed,
|
||||
"POST / HTTP/1.1\r\n"
|
||||
"Host: localhost:80\r\n"
|
||||
"Upgrade: WebSocket\r\n"
|
||||
"Connection: keep-alive,upgrade\r\n"
|
||||
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
|
||||
"Sec-WebSocket-Version: 13\r\n"
|
||||
"\r\n"
|
||||
);
|
||||
// missing Host
|
||||
check(error::handshake_failed,
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"Upgrade: WebSocket\r\n"
|
||||
"Connection: keep-alive,upgrade\r\n"
|
||||
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
|
||||
"Sec-WebSocket-Version: 13\r\n"
|
||||
"\r\n"
|
||||
);
|
||||
// missing Sec-WebSocket-Key
|
||||
check(error::handshake_failed,
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"Host: localhost:80\r\n"
|
||||
"Upgrade: WebSocket\r\n"
|
||||
"Connection: keep-alive,upgrade\r\n"
|
||||
"Sec-WebSocket-Version: 13\r\n"
|
||||
"\r\n"
|
||||
);
|
||||
// missing Sec-WebSocket-Version
|
||||
check(error::handshake_failed,
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"Host: localhost:80\r\n"
|
||||
"Upgrade: WebSocket\r\n"
|
||||
"Connection: keep-alive,upgrade\r\n"
|
||||
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
|
||||
"\r\n"
|
||||
);
|
||||
// wrong Sec-WebSocket-Version
|
||||
check(error::handshake_failed,
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"Host: localhost:80\r\n"
|
||||
"Upgrade: WebSocket\r\n"
|
||||
"Connection: keep-alive,upgrade\r\n"
|
||||
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
|
||||
"Sec-WebSocket-Version: 1\r\n"
|
||||
"\r\n"
|
||||
);
|
||||
// missing upgrade token
|
||||
check(error::handshake_failed,
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"Host: localhost:80\r\n"
|
||||
"Upgrade: HTTP/2\r\n"
|
||||
"Connection: upgrade\r\n"
|
||||
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
|
||||
"Sec-WebSocket-Version: 13\r\n"
|
||||
"\r\n"
|
||||
);
|
||||
// missing connection token
|
||||
check(error::handshake_failed,
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"Host: localhost:80\r\n"
|
||||
"Upgrade: WebSocket\r\n"
|
||||
"Connection: keep-alive\r\n"
|
||||
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
|
||||
"Sec-WebSocket-Version: 13\r\n"
|
||||
"\r\n"
|
||||
);
|
||||
// valid request
|
||||
check({},
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"Host: localhost:80\r\n"
|
||||
"Upgrade: WebSocket\r\n"
|
||||
"Connection: upgrade\r\n"
|
||||
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
|
||||
"Sec-WebSocket-Version: 13\r\n"
|
||||
"\r\n"
|
||||
);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Close
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
template<class Wrap>
|
||||
@@ -1392,8 +1527,15 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
doTestCloseAsync()
|
||||
testClose()
|
||||
{
|
||||
doTestClose(SyncClient{});
|
||||
|
||||
yield_to([&](yield_context yield)
|
||||
{
|
||||
doTestClose(AsyncClient{yield});
|
||||
});
|
||||
|
||||
auto const launch =
|
||||
[&](test::stream stream)
|
||||
{
|
||||
@@ -1459,58 +1601,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testClose()
|
||||
{
|
||||
doTestClose(SyncClient{});
|
||||
|
||||
yield_to([&](yield_context yield)
|
||||
{
|
||||
doTestClose(AsyncClient{yield});
|
||||
});
|
||||
|
||||
doTestCloseAsync();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void
|
||||
testRead()
|
||||
{
|
||||
// Read close frames
|
||||
{
|
||||
auto const check =
|
||||
[&](error_code ev, string_view s)
|
||||
{
|
||||
test::stream ts{ios_};
|
||||
stream<test::stream&> ws{ts};
|
||||
launchEchoServerAsync(ts.remote());
|
||||
ws.handshake("localhost", "/");
|
||||
ts.str(s);
|
||||
static_buffer<1> b;
|
||||
error_code ec;
|
||||
ws.read(b, ec);
|
||||
BEAST_EXPECTS(ec == ev, ec.message());
|
||||
};
|
||||
|
||||
// payload length 1
|
||||
check(error::failed,
|
||||
"\x88\x01\x01");
|
||||
|
||||
// invalid close code 1005
|
||||
check(error::failed,
|
||||
"\x88\x02\x03\xed");
|
||||
|
||||
// invalid utf8
|
||||
check(error::failed,
|
||||
"\x88\x06\xfc\x15\x0f\xd7\x73\x43");
|
||||
|
||||
// good utf8
|
||||
check(error::closed,
|
||||
"\x88\x06\xfc\x15utf8");
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
template<class Client, class Launch>
|
||||
@@ -1597,137 +1687,7 @@ public:
|
||||
launchEchoServerAsync(std::move(stream));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void testBadHandshakes()
|
||||
{
|
||||
auto const check =
|
||||
[&](error_code const& ev, std::string const& s)
|
||||
{
|
||||
for(int i = 0; i < 3; ++i)
|
||||
{
|
||||
std::size_t n;
|
||||
switch(i)
|
||||
{
|
||||
default:
|
||||
case 0:
|
||||
n = 1;
|
||||
break;
|
||||
case 1:
|
||||
n = s.size() / 2;
|
||||
break;
|
||||
case 2:
|
||||
n = s.size() - 1;
|
||||
break;
|
||||
}
|
||||
stream<test::stream> ws{ios_};
|
||||
ws.next_layer().str(
|
||||
s.substr(n, s.size() - n));
|
||||
try
|
||||
{
|
||||
ws.accept(
|
||||
boost::asio::buffer(s.data(), n));
|
||||
BEAST_EXPECTS(! ev, ev.message());
|
||||
}
|
||||
catch(system_error const& se)
|
||||
{
|
||||
BEAST_EXPECTS(se.code() == ev, se.what());
|
||||
}
|
||||
}
|
||||
};
|
||||
// wrong version
|
||||
check(http::error::end_of_stream,
|
||||
"GET / HTTP/1.0\r\n"
|
||||
"Host: localhost:80\r\n"
|
||||
"Upgrade: WebSocket\r\n"
|
||||
"Connection: keep-alive,upgrade\r\n"
|
||||
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
|
||||
"Sec-WebSocket-Version: 13\r\n"
|
||||
"\r\n"
|
||||
);
|
||||
// wrong method
|
||||
check(error::handshake_failed,
|
||||
"POST / HTTP/1.1\r\n"
|
||||
"Host: localhost:80\r\n"
|
||||
"Upgrade: WebSocket\r\n"
|
||||
"Connection: keep-alive,upgrade\r\n"
|
||||
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
|
||||
"Sec-WebSocket-Version: 13\r\n"
|
||||
"\r\n"
|
||||
);
|
||||
// missing Host
|
||||
check(error::handshake_failed,
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"Upgrade: WebSocket\r\n"
|
||||
"Connection: keep-alive,upgrade\r\n"
|
||||
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
|
||||
"Sec-WebSocket-Version: 13\r\n"
|
||||
"\r\n"
|
||||
);
|
||||
// missing Sec-WebSocket-Key
|
||||
check(error::handshake_failed,
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"Host: localhost:80\r\n"
|
||||
"Upgrade: WebSocket\r\n"
|
||||
"Connection: keep-alive,upgrade\r\n"
|
||||
"Sec-WebSocket-Version: 13\r\n"
|
||||
"\r\n"
|
||||
);
|
||||
// missing Sec-WebSocket-Version
|
||||
check(error::handshake_failed,
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"Host: localhost:80\r\n"
|
||||
"Upgrade: WebSocket\r\n"
|
||||
"Connection: keep-alive,upgrade\r\n"
|
||||
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
|
||||
"\r\n"
|
||||
);
|
||||
// wrong Sec-WebSocket-Version
|
||||
check(error::handshake_failed,
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"Host: localhost:80\r\n"
|
||||
"Upgrade: WebSocket\r\n"
|
||||
"Connection: keep-alive,upgrade\r\n"
|
||||
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
|
||||
"Sec-WebSocket-Version: 1\r\n"
|
||||
"\r\n"
|
||||
);
|
||||
// missing upgrade token
|
||||
check(error::handshake_failed,
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"Host: localhost:80\r\n"
|
||||
"Upgrade: HTTP/2\r\n"
|
||||
"Connection: upgrade\r\n"
|
||||
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
|
||||
"Sec-WebSocket-Version: 13\r\n"
|
||||
"\r\n"
|
||||
);
|
||||
// missing connection token
|
||||
check(error::handshake_failed,
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"Host: localhost:80\r\n"
|
||||
"Upgrade: WebSocket\r\n"
|
||||
"Connection: keep-alive\r\n"
|
||||
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
|
||||
"Sec-WebSocket-Version: 13\r\n"
|
||||
"\r\n"
|
||||
);
|
||||
// valid request
|
||||
check({},
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"Host: localhost:80\r\n"
|
||||
"Upgrade: WebSocket\r\n"
|
||||
"Connection: upgrade\r\n"
|
||||
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
|
||||
"Sec-WebSocket-Version: 13\r\n"
|
||||
"\r\n"
|
||||
);
|
||||
}
|
||||
|
||||
void testBadResponses()
|
||||
{
|
||||
auto const check =
|
||||
[&](std::string const& s)
|
||||
{
|
||||
@@ -1805,6 +1765,180 @@ public:
|
||||
);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Ping
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
template<class Wrap>
|
||||
void
|
||||
doTestPing(Wrap const& w)
|
||||
{
|
||||
auto const launch =
|
||||
[&](test::stream stream)
|
||||
{
|
||||
launchEchoServer(std::move(stream));
|
||||
//launchEchoServerAsync(std::move(stream));
|
||||
};
|
||||
|
||||
permessage_deflate pmd;
|
||||
pmd.client_enable = false;
|
||||
pmd.server_enable = false;
|
||||
|
||||
// ping
|
||||
doTest(w, pmd, launch, [&](ws_stream_type& ws)
|
||||
{
|
||||
w.ping(ws, {});
|
||||
});
|
||||
|
||||
// pong
|
||||
doTest(w, pmd, launch, [&](ws_stream_type& ws)
|
||||
{
|
||||
w.pong(ws, {});
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
testPing()
|
||||
{
|
||||
doTestPing(SyncClient{});
|
||||
|
||||
yield_to([&](yield_context yield)
|
||||
{
|
||||
doTestPing(AsyncClient{yield});
|
||||
});
|
||||
|
||||
auto const launch =
|
||||
[&](test::stream stream)
|
||||
{
|
||||
launchEchoServer(std::move(stream));
|
||||
//launchEchoServerAsync(std::move(stream));
|
||||
};
|
||||
|
||||
// ping, already closed
|
||||
{
|
||||
stream<test::stream> ws{ios_};
|
||||
error_code ec;
|
||||
ws.ping({}, ec);
|
||||
BEAST_EXPECTS(
|
||||
ec == boost::asio::error::operation_aborted,
|
||||
ec.message());
|
||||
}
|
||||
|
||||
// async_ping, already closed
|
||||
{
|
||||
boost::asio::io_service ios;
|
||||
stream<test::stream> ws{ios};
|
||||
ws.async_ping({},
|
||||
[&](error_code ec)
|
||||
{
|
||||
BEAST_EXPECTS(
|
||||
ec == boost::asio::error::operation_aborted,
|
||||
ec.message());
|
||||
});
|
||||
ios.run();
|
||||
}
|
||||
|
||||
// pong, already closed
|
||||
{
|
||||
stream<test::stream> ws{ios_};
|
||||
error_code ec;
|
||||
ws.pong({}, ec);
|
||||
BEAST_EXPECTS(
|
||||
ec == boost::asio::error::operation_aborted,
|
||||
ec.message());
|
||||
}
|
||||
|
||||
// async_pong, already closed
|
||||
{
|
||||
boost::asio::io_service ios;
|
||||
stream<test::stream> ws{ios};
|
||||
ws.async_pong({},
|
||||
[&](error_code ec)
|
||||
{
|
||||
BEAST_EXPECTS(
|
||||
ec == boost::asio::error::operation_aborted,
|
||||
ec.message());
|
||||
});
|
||||
ios.run();
|
||||
}
|
||||
|
||||
// suspend on write
|
||||
{
|
||||
error_code ec;
|
||||
boost::asio::io_service ios;
|
||||
stream<test::stream> ws{ios, ios_};
|
||||
launch(ws.next_layer().remote());
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Read
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void
|
||||
testRead()
|
||||
{
|
||||
// Read close frames
|
||||
{
|
||||
auto const check =
|
||||
[&](error_code ev, string_view s)
|
||||
{
|
||||
test::stream ts{ios_};
|
||||
stream<test::stream&> ws{ts};
|
||||
launchEchoServerAsync(ts.remote());
|
||||
ws.handshake("localhost", "/");
|
||||
ts.str(s);
|
||||
static_buffer<1> b;
|
||||
error_code ec;
|
||||
ws.read(b, ec);
|
||||
BEAST_EXPECTS(ec == ev, ec.message());
|
||||
};
|
||||
|
||||
// payload length 1
|
||||
check(error::failed,
|
||||
"\x88\x01\x01");
|
||||
|
||||
// invalid close code 1005
|
||||
check(error::failed,
|
||||
"\x88\x02\x03\xed");
|
||||
|
||||
// invalid utf8
|
||||
check(error::failed,
|
||||
"\x88\x06\xfc\x15\x0f\xd7\x73\x43");
|
||||
|
||||
// good utf8
|
||||
check(error::closed,
|
||||
"\x88\x06\xfc\x15utf8");
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void
|
||||
testMask(endpoint_type const& ep,
|
||||
yield_context do_yield)
|
||||
@@ -2366,6 +2500,8 @@ public:
|
||||
|
||||
testAccept();
|
||||
testClose();
|
||||
testHandshake();
|
||||
testPing();
|
||||
testRead();
|
||||
|
||||
permessage_deflate pmd;
|
||||
@@ -2373,10 +2509,6 @@ public:
|
||||
pmd.server_enable = false;
|
||||
|
||||
testOptions();
|
||||
testHandshake();
|
||||
testBadHandshakes();
|
||||
testBadResponses();
|
||||
testClose();
|
||||
testPausation1();
|
||||
testWriteFrames();
|
||||
testAsyncWriteFrame();
|
||||
|
Reference in New Issue
Block a user