WebSocket handshake response is deterministic on failure:

Add test for websocket handshake failure HTTP response

fix #2364
close #2365
This commit is contained in:
Richard Hodges
2021-12-20 19:37:18 +01:00
parent 55d2ceb627
commit b5a94db2a5
4 changed files with 116 additions and 6 deletions

View File

@ -1,3 +1,7 @@
* WebSocket handshake response is deterministic on failure.
--------------------------------------------------------------------------------
Version 324: Version 324:
* Fix open append mode for file_posix. * Fix open append mode for file_posix.

View File

@ -225,6 +225,9 @@ do_handshake(
RequestDecorator const& decorator, RequestDecorator const& decorator,
error_code& ec) error_code& ec)
{ {
if(res_p)
res_p->result(http::status::internal_server_error);
auto& impl = *impl_; auto& impl = *impl_;
impl.change_status(status::handshake); impl.change_status(status::handshake);
impl.reset(); impl.reset();
@ -275,12 +278,18 @@ do_handshake(
if(impl.check_stop_now(ec)) if(impl.check_stop_now(ec))
return; return;
impl.on_response(p.get(), key, ec); if (res_p)
if(impl.check_stop_now(ec)) {
return; // If res_p is not null, move parser's response into it.
if(res_p)
*res_p = p.release(); *res_p = p.release();
}
else
{
// Otherwise point res_p at the response in the parser.
res_p = &p.get();
}
impl.on_response(*res_p, key, ec);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -325,6 +334,7 @@ async_handshake(
detail::sec_ws_key_type key; detail::sec_ws_key_type key;
auto req = impl_->build_request( auto req = impl_->build_request(
key, host, target, &default_decorate_req); key, host, target, &default_decorate_req);
res.result(http::status::internal_server_error);
return net::async_initiate< return net::async_initiate<
HandshakeHandler, HandshakeHandler,
void(error_code)>( void(error_code)>(

View File

@ -675,7 +675,11 @@ public:
@param res The HTTP Upgrade response returned by the remote @param res The HTTP Upgrade response returned by the remote
endpoint. The caller may use the response to access any endpoint. The caller may use the response to access any
additional information sent by the server. additional information sent by the server. Note that the response object
referenced by this parameter will be updated as long as the stream has
received a valid HTTP response. If not (for example because of a communications
error), the response contents will be undefined except for the result() which
will bet set to 500, Internal Server Error.
@param host The name of the remote host. This is required by @param host The name of the remote host. This is required by
the HTTP protocol to set the "Host" header field. the HTTP protocol to set the "Host" header field.

View File

@ -724,6 +724,97 @@ public:
} }
#endif #endif
void
testIssue2364()
{
{ // sync unauthorized
net::io_context ioc;
stream<test::stream> ws{ioc};
auto tr = connect(ws.next_layer());
std::string const reply =
"HTTP/1.1 401 Unauthorized\r\n"
"\r\n";
ws.next_layer().append(reply);
tr.close();
try
{
websocket::response_type response;
error_code ec;
ws.handshake(response, "localhost:80", "/", ec);
BEAST_EXPECT(ec);
BEAST_EXPECTS(response.result() == http::status::unauthorized,
std::to_string(response.result_int()));
}
catch(system_error const&)
{
fail();
}
}
{ // sync invalid response
net::io_context ioc;
stream<test::stream> ws{ioc};
auto tr = connect(ws.next_layer());
std::string const reply =
"zzzzzzzzzzzzzzzzzzzzz";
ws.next_layer().append(reply);
tr.close();
try
{
websocket::response_type response;
error_code ec;
ws.handshake(response, "localhost:80", "/", ec);
BEAST_EXPECT(ec);
BEAST_EXPECTS(response.result() == http::status::internal_server_error,
std::to_string(response.result_int()));
}
catch(system_error const&)
{
fail();
}
}
{ // async unauthorized
net::io_context ioc;
stream<test::stream> ws{ioc};
auto tr = connect(ws.next_layer());
std::string const reply =
"HTTP/1.1 401 Unauthorized\r\n"
"\r\n";
ws.next_layer().append(reply);
tr.close();
websocket::response_type response;
auto handler = [&](error_code ec)
{
BEAST_EXPECT(ec);
BEAST_EXPECTS(response.result() == http::status::unauthorized,
std::to_string(response.result_int()));
};
ws.async_handshake(response, "localhost:80", "/", handler);
ioc.run();
}
{ // async invalid response
net::io_context ioc;
stream<test::stream> ws{ioc};
auto tr = connect(ws.next_layer());
std::string const reply =
"zzzzzzzzzzzzzzzz";
ws.next_layer().append(reply);
tr.close();
websocket::response_type response;
auto handler = [&](error_code ec)
{
BEAST_EXPECT(ec);
BEAST_EXPECTS(response.result() == http::status::internal_server_error,
std::to_string(response.result_int()));
};
ws.async_handshake(response, "localhost:80", "/", handler);
ioc.run();
}
}
void void
run() override run() override
{ {
@ -737,6 +828,7 @@ public:
#if BOOST_ASIO_HAS_CO_AWAIT #if BOOST_ASIO_HAS_CO_AWAIT
boost::ignore_unused(&handshake_test::testAwaitableCompiles); boost::ignore_unused(&handshake_test::testAwaitableCompiles);
#endif #endif
testIssue2364();
} }
}; };