Refactor WebSocket errors (API Change):

fix #949

* New error codes are introduced for WebSocket failures

* More verbose messages for error codes

* Error codes are mapped to conditions for ease of testing

* error::failed and error::handshake_failed are deprecated (don't use)

Actions Required:

* Code which explicitly compares error_code values against the
  constant `websocket::error::handshake_failed` should compare
  against `websocket::condition::handshake_failed` instead.

* Code which explicitly compares error_code values against the
  constant `websocket::error::failed` should compare
  against `websocket::condition::protocol_violation` instead.
This commit is contained in:
Vinnie Falco
2018-01-01 17:49:19 -08:00
parent 08a9ed4c25
commit cc43b46c42
19 changed files with 705 additions and 346 deletions

View File

@ -8,6 +8,20 @@ WebSocket:
* Refactor error headers * Refactor error headers
* Add WebSocket error conditions * Add WebSocket error conditions
API Changes:
* Refactor WebSocket errors (API Change):
Actions Required:
* Code which explicitly compares error_code values against the
constant `websocket::error::handshake_failed` should compare
against `websocket::condition::handshake_failed` instead.
* Code which explicitly compares error_code values against the
constant `websocket::error::failed` should compare
against `websocket::condition::protocol_violation` instead.
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
Version 151: Version 151:

View File

@ -18,8 +18,8 @@ An instance of the stream wraps an existing network transport object
or other type of octet oriented stream. The wrapped object is called or other type of octet oriented stream. The wrapped object is called
the "next layer" and must meet the requirements of __SyncStream__ if the "next layer" and must meet the requirements of __SyncStream__ if
synchronous operations are performed, __AsyncStream__ if asynchronous synchronous operations are performed, __AsyncStream__ if asynchronous
operations are performed, or both. Any arguments supplied during operations are performed, or both. Any arguments supplied to the
construction of the stream wrapper are passed to next layer's constructor. constructor of the stream wrapper are forwarded to next layer's constructor.
The value of `deflateSupported` determines if the stream will support The value of `deflateSupported` determines if the stream will support
(but not require) the permessage-deflate extension (but not require) the permessage-deflate extension

View File

@ -145,6 +145,7 @@
<bridgehead renderas="sect3">Constants</bridgehead> <bridgehead renderas="sect3">Constants</bridgehead>
<simplelist type="vert" columns="1"> <simplelist type="vert" columns="1">
<member><link linkend="beast.ref.boost__beast__websocket__close_code">close_code</link></member> <member><link linkend="beast.ref.boost__beast__websocket__close_code">close_code</link></member>
<member><link linkend="beast.ref.boost__beast__websocket__condition">condition</link></member>
<member><link linkend="beast.ref.boost__beast__websocket__error">error</link></member> <member><link linkend="beast.ref.boost__beast__websocket__error">error</link></member>
<member><link linkend="beast.ref.boost__beast__websocket__frame_type">frame_type</link></member> <member><link linkend="beast.ref.boost__beast__websocket__frame_type">frame_type</link></member>
<member><link linkend="beast.ref.boost__beast__websocket__role_type">role_type</link></member> <member><link linkend="beast.ref.boost__beast__websocket__role_type">role_type</link></member>

View File

@ -41,79 +41,34 @@ namespace detail {
class error_codes : public error_category class error_codes : public error_category
{ {
template<class = void>
string_view
message(error e) const;
public: public:
const char* const char*
name() const noexcept override name() const noexcept override;
{
return "boost.beast.websocket";
}
std::string std::string
message(int ev) const override message(int ev) const override;
{
return message( error_condition
static_cast<error>(ev)).to_string(); default_error_condition(int ev) const noexcept override;
}
}; };
class error_conditions : public error_category class error_conditions : public error_category
{ {
template<class = void>
string_view
message(condition c) const;
template<class = void>
bool
equivalent(error_code const& ec,
condition c) const noexcept;
public: public:
const char* const char*
name() const noexcept override name() const noexcept override;
{
return "boost.beast.websocket";
}
std::string std::string
message(int cv) const override message(int cv) const override;
{
return message(
static_cast<condition>(cv)).to_string();
}
bool
equivalent(
error_code const& ec,
int cv) const noexcept
{
return equivalent(ec,
static_cast<condition>(cv));
}
}; };
} // detail } // detail
inline
error_code error_code
make_error_code(error e) make_error_code(error e);
{
static detail::error_codes const cat{};
return error_code{static_cast<
std::underlying_type<error>::type>(e), cat};
}
inline
error_condition error_condition
make_error_condition(condition c) make_error_condition(condition c);
{
static detail::error_conditions const cat{};
return error_condition{static_cast<
std::underlying_type<condition>::type>(c), cat};
}
} // websocket } // websocket
} // beast } // beast

View File

@ -10,6 +10,7 @@
#ifndef BOOST_BEAST_WEBSOCKET_DETAIL_FRAME_HPP #ifndef BOOST_BEAST_WEBSOCKET_DETAIL_FRAME_HPP
#define BOOST_BEAST_WEBSOCKET_DETAIL_FRAME_HPP #define BOOST_BEAST_WEBSOCKET_DETAIL_FRAME_HPP
#include <boost/beast/websocket/error.hpp>
#include <boost/beast/websocket/rfc6455.hpp> #include <boost/beast/websocket/rfc6455.hpp>
#include <boost/beast/websocket/detail/utf8_checker.hpp> #include <boost/beast/websocket/detail/utf8_checker.hpp>
#include <boost/beast/core/buffers_suffix.hpp> #include <boost/beast/core/buffers_suffix.hpp>
@ -244,8 +245,10 @@ read_ping(ping_data& data, Buffers const& bs)
// //
template<class Buffers> template<class Buffers>
void void
read_close(close_reason& cr, read_close(
Buffers const& bs, close_code& code) close_reason& cr,
Buffers const& bs,
error_code& ec)
{ {
using boost::asio::buffer; using boost::asio::buffer;
using boost::asio::buffer_copy; using boost::asio::buffer_copy;
@ -256,12 +259,13 @@ read_close(close_reason& cr,
if(n == 0) if(n == 0)
{ {
cr = close_reason{}; cr = close_reason{};
code = close_code::none; ec.assign(0, ec.category());
return; return;
} }
if(n == 1) if(n == 1)
{ {
code = close_code::protocol_error; // invalid payload size == 1
ec = error::bad_close_size;
return; return;
} }
buffers_suffix<Buffers> cb(bs); buffers_suffix<Buffers> cb(bs);
@ -273,7 +277,8 @@ read_close(close_reason& cr,
n -= 2; n -= 2;
if(! is_valid_close_code(cr.code)) if(! is_valid_close_code(cr.code))
{ {
code = close_code::protocol_error; // invalid close code
ec = error::bad_close_code;
return; return;
} }
} }
@ -284,7 +289,8 @@ read_close(close_reason& cr,
if(! check_utf8( if(! check_utf8(
cr.reason.data(), cr.reason.size())) cr.reason.data(), cr.reason.size()))
{ {
code = close_code::protocol_error; // not valid utf-8
ec = error::bad_close_payload;
return; return;
} }
} }
@ -292,7 +298,7 @@ read_close(close_reason& cr,
{ {
cr.reason = ""; cr.reason = "";
} }
code = close_code::none; ec.assign(0, ec.category());
} }
} // detail } // detail

View File

@ -21,32 +21,228 @@ namespace websocket {
/// Error codes returned from @ref beast::websocket::stream operations. /// Error codes returned from @ref beast::websocket::stream operations.
enum class error enum class error
{ {
/// Both sides performed a WebSocket close /** The WebSocket stream was gracefully closed at both endpoints
*/
closed = 1, closed = 1,
/// WebSocket connection failed, protocol violation /* The error codes error::failed and error::handshake_failed
failed, are no longer in use. Please change your code to compare values
of type error_code against condition::handshake_failed
and condition::protocol_violation instead.
Apologies for the inconvenience.
/// Upgrade handshake failed - VFALCO
handshake_failed, */
#if ! BOOST_BEAST_DOXYGEN
unused1 = 2, // failed
unused2 = 3, // handshake_failed
#endif
/// buffer overflow /** The WebSocket operation caused a dynamic buffer overflow
*/
buffer_overflow, buffer_overflow,
/// partial deflate block /** The WebSocket stream produced an incomplete deflate block
partial_deflate_block */
partial_deflate_block,
/** The WebSocket message exceeded the locally configured limit
*/
message_too_big,
//
// Handshake failure errors
//
// These will compare equal to condition::handshake_failed
//
/** The WebSocket handshake was not HTTP/1.1
Error codes with this value will compare equal to @ref condition::handshake_failed
*/
bad_http_version,
/** The WebSocket handshake method was not GET
Error codes with this value will compare equal to @ref condition::handshake_failed
*/
bad_method,
/** The WebSocket handshake Host field is missing
Error codes with this value will compare equal to @ref condition::handshake_failed
*/
no_host,
/** The WebSocket handshake Connection field is missing
Error codes with this value will compare equal to @ref condition::handshake_failed
*/
no_connection,
/** The WebSocket handshake Connection field is missing the upgrade token
Error codes with this value will compare equal to @ref condition::handshake_failed
*/
no_connection_upgrade,
/** The WebSocket handshake Upgrade field is missing
Error codes with this value will compare equal to @ref condition::handshake_failed
*/
no_upgrade,
/** The WebSocket handshake Upgrade field is missing the websocket token
Error codes with this value will compare equal to @ref condition::handshake_failed
*/
no_upgrade_websocket,
/** The WebSocket handshake Sec-WebSocket-Key field is missing
Error codes with this value will compare equal to @ref condition::handshake_failed
*/
no_sec_key,
/** The WebSocket handshake Sec-WebSocket-Key field is invalid
Error codes with this value will compare equal to @ref condition::handshake_failed
*/
bad_sec_key,
/** The WebSocket handshake Sec-WebSocket-Version field is missing
Error codes with this value will compare equal to @ref condition::handshake_failed
*/
no_sec_version,
/** The WebSocket handshake Sec-WebSocket-Version field is invalid
Error codes with this value will compare equal to @ref condition::handshake_failed
*/
bad_sec_version,
/** The WebSocket handshake Sec-WebSocket-Accept field is missing
Error codes with this value will compare equal to @ref condition::handshake_failed
*/
no_sec_accept,
/** The WebSocket handshake Sec-WebSocket-Accept field is invalid
Error codes with this value will compare equal to @ref condition::handshake_failed
*/
bad_sec_accept,
/** The WebSocket handshake was declined by the remote peer
Error codes with this value will compare equal to @ref condition::handshake_failed
*/
upgrade_declined,
//
// Protocol errors
//
// These will compare equal to condition::protocol_violation
//
/** The WebSocket frame contained an illegal opcode
Error codes with this value will compare equal to @ref condition::protocol_violation
*/
bad_opcode,
/** The WebSocket data frame was unexpected
Error codes with this value will compare equal to @ref condition::protocol_violation
*/
bad_data_frame,
/** The WebSocket continuation frame was unexpected
Error codes with this value will compare equal to @ref condition::protocol_violation
*/
bad_continuation,
/** The WebSocket frame contained illegal reserved bits
Error codes with this value will compare equal to @ref condition::protocol_violation
*/
bad_reserved_bits,
/** The WebSocket control frame was fragmented
Error codes with this value will compare equal to @ref condition::protocol_violation
*/
bad_control_fragment,
/** The WebSocket control frame size was invalid
Error codes with this value will compare equal to @ref condition::protocol_violation
*/
bad_control_size,
/** The WebSocket frame was unmasked
Error codes with this value will compare equal to @ref condition::protocol_violation
*/
bad_unmasked_frame,
/** The WebSocket frame was masked
Error codes with this value will compare equal to @ref condition::protocol_violation
*/
bad_masked_frame,
/** The WebSocket frame size was not canonical
Error codes with this value will compare equal to @ref condition::protocol_violation
*/
bad_size,
/** The WebSocket frame payload was not valid utf8
Error codes with this value will compare equal to @ref condition::protocol_violation
*/
bad_frame_payload,
/** The WebSocket close frame reason code was invalid
Error codes with this value will compare equal to @ref condition::protocol_violation
*/
bad_close_code,
/** The WebSocket close frame payload size was invalid
Error codes with this value will compare equal to @ref condition::protocol_violation
*/
bad_close_size,
/** The WebSocket close frame payload was not valid utf8
Error codes with this value will compare equal to @ref condition::protocol_violation
*/
bad_close_payload
}; };
/// Error conditions corresponding to sets of error codes. /// Error conditions corresponding to sets of error codes.
enum class condition enum class condition
{ {
/** Handshake failed /** The WebSocket handshake failed
This condition indicates that the WebSocket handshake failed. If This condition indicates that the WebSocket handshake failed. If
the corresponding HTTP response indicates the keep-alive behavior, the corresponding HTTP response indicates the keep-alive behavior,
then the handshake may be reattempted. then the handshake may be reattempted.
*/ */
handshake_failed = 1, handshake_failed = 1,
/** A WebSocket protocol violation occurred
This condition indicates that the remote peer on the WebSocket
connection sent data which violated the protocol.
*/
protocol_violation
}; };
} // websocket } // websocket

View File

@ -42,6 +42,7 @@ class stream<NextLayer, deflateSupported>::response_op
struct data struct data
{ {
stream<NextLayer, deflateSupported>& ws; stream<NextLayer, deflateSupported>& ws;
error_code result;
response_type res; response_type res;
template<class Body, class Allocator, class Decorator> template<class Body, class Allocator, class Decorator>
@ -52,7 +53,7 @@ class stream<NextLayer, deflateSupported>::response_op
http::basic_fields<Allocator>> const& req, http::basic_fields<Allocator>> const& req,
Decorator const& decorator) Decorator const& decorator)
: ws(ws_) : ws(ws_)
, res(ws_.build_response(req, decorator)) , res(ws_.build_response(req, decorator, result))
{ {
} }
}; };
@ -120,9 +121,8 @@ operator()(
BOOST_ASIO_CORO_YIELD BOOST_ASIO_CORO_YIELD
http::async_write(d.ws.next_layer(), http::async_write(d.ws.next_layer(),
d.res, std::move(*this)); d.res, std::move(*this));
if(! ec && d.res.result() != if(! ec)
http::status::switching_protocols) ec = d.result;
ec = error::handshake_failed;
if(! ec) if(! ec)
{ {
d.ws.do_pmd_config(d.res, is_deflate_supported{}); d.ws.do_pmd_config(d.res, is_deflate_supported{});
@ -742,13 +742,14 @@ do_accept(
Decorator const& decorator, Decorator const& decorator,
error_code& ec) error_code& ec)
{ {
auto const res = build_response(req, decorator); error_code result;
auto const res = build_response(req, decorator, result);
http::write(stream_, res, ec); http::write(stream_, res, ec);
if(ec) if(ec)
return; return;
if(res.result() != http::status::switching_protocols) ec = result;
if(ec)
{ {
ec = error::handshake_failed;
// VFALCO TODO Respect keep alive setting, perform // VFALCO TODO Respect keep alive setting, perform
// teardown if Connection: close. // teardown if Connection: close.
return; return;

View File

@ -121,7 +121,6 @@ operator()(
{ {
using beast::detail::clamp; using beast::detail::clamp;
auto& d = *d_; auto& d = *d_;
close_code code{};
d.cont = cont; d.cont = cont;
BOOST_ASIO_CORO_REENTER(*this) BOOST_ASIO_CORO_REENTER(*this)
{ {
@ -219,13 +218,10 @@ operator()(
{ {
// Read frame header // Read frame header
while(! d.ws.parse_fh( while(! d.ws.parse_fh(
d.ws.rd_fh_, d.ws.rd_buf_, code)) d.ws.rd_fh_, d.ws.rd_buf_, d.ev))
{ {
if(code != close_code::none) if(d.ev)
{
d.ev = error::failed;
goto teardown; goto teardown;
}
BOOST_ASIO_CORO_YIELD BOOST_ASIO_CORO_YIELD
d.ws.stream_.async_read_some( d.ws.stream_.async_read_some(
d.ws.rd_buf_.prepare(read_size(d.ws.rd_buf_, d.ws.rd_buf_.prepare(read_size(d.ws.rd_buf_,
@ -247,13 +243,9 @@ operator()(
d.ws.rd_buf_.data()); d.ws.rd_buf_.data());
if(d.ws.rd_fh_.len > 0 && d.ws.rd_fh_.mask) if(d.ws.rd_fh_.len > 0 && d.ws.rd_fh_.mask)
detail::mask_inplace(mb, d.ws.rd_key_); detail::mask_inplace(mb, d.ws.rd_key_);
detail::read_close(d.ws.cr_, mb, code); detail::read_close(d.ws.cr_, mb, d.ev);
if(code != close_code::none) if(d.ev)
{
// Protocol error
d.ev = error::failed;
goto teardown; goto teardown;
}
d.ws.rd_buf_.consume(clamp(d.ws.rd_fh_.len)); d.ws.rd_buf_.consume(clamp(d.ws.rd_fh_.len));
goto teardown; goto teardown;
} }
@ -364,18 +356,18 @@ close(close_reason const& cr, error_code& ec)
if(! check_ok(ec)) if(! check_ok(ec))
return; return;
status_ = status::closing; status_ = status::closing;
error_code result;
// Drain the connection // Drain the connection
close_code code{};
if(rd_remain_ > 0) if(rd_remain_ > 0)
goto read_payload; goto read_payload;
for(;;) for(;;)
{ {
// Read frame header // Read frame header
while(! parse_fh(rd_fh_, rd_buf_, code)) while(! parse_fh(rd_fh_, rd_buf_, result))
{ {
if(code != close_code::none) if(result)
return do_fail(close_code::none, return do_fail(
error::failed, ec); close_code::none, result, ec);
auto const bytes_transferred = auto const bytes_transferred =
stream_.read_some( stream_.read_some(
rd_buf_.prepare(read_size(rd_buf_, rd_buf_.prepare(read_size(rd_buf_,
@ -396,12 +388,12 @@ close(close_reason const& cr, error_code& ec)
rd_buf_.data()); rd_buf_.data());
if(rd_fh_.len > 0 && rd_fh_.mask) if(rd_fh_.len > 0 && rd_fh_.mask)
detail::mask_inplace(mb, rd_key_); detail::mask_inplace(mb, rd_key_);
detail::read_close(cr_, mb, code); detail::read_close(cr_, mb, result);
if(code != close_code::none) if(result)
{ {
// Protocol error // Protocol violation
return do_fail(close_code::none, return do_fail(
error::failed, ec); close_code::none, result, ec);
} }
rd_buf_.consume(clamp(rd_fh_.len)); rd_buf_.consume(clamp(rd_fh_.len));
break; break;

View File

@ -15,59 +15,146 @@ namespace beast {
namespace websocket { namespace websocket {
namespace detail { namespace detail {
template<class> inline
string_view const char*
error_codes:: error_codes::
message(error e) const name() const noexcept
{ {
switch(e) return "boost.beast.websocket";
}
inline
std::string
error_codes::
message(int ev) const
{
switch(static_cast<error>(ev))
{ {
default: default:
case error::failed: return "WebSocket connection failed due to a protocol violation"; case error::closed: return "The WebSocket stream was gracefully closed at both endpoints";
case error::closed: return "WebSocket connection closed normally"; case error::buffer_overflow: return "The WebSocket operation caused a dynamic buffer overflow";
case error::handshake_failed: return "WebSocket upgrade handshake failed"; case error::partial_deflate_block: return "The WebSocket stream produced an incomplete deflate block";
case error::buffer_overflow: return "WebSocket dynamic buffer overflow"; case error::message_too_big: return "The WebSocket message exceeded the locally configured limit";
case error::partial_deflate_block: return "WebSocket partial deflate block";
case error::bad_http_version: return "The WebSocket handshake was not HTTP/1.1";
case error::bad_method: return "The WebSocket handshake method was not GET";
case error::no_host: return "The WebSocket handshake Host field is missing";
case error::no_connection: return "The WebSocket handshake Connection field is missing";
case error::no_connection_upgrade: return "The WebSocket handshake Connection field is missing the upgrade token";
case error::no_upgrade: return "The WebSocket handshake Upgrade field is missing";
case error::no_upgrade_websocket: return "The WebSocket handshake Upgrade field is missing the websocket token";
case error::no_sec_key: return "The WebSocket handshake Sec-WebSocket-Key field is missing";
case error::bad_sec_key: return "The WebSocket handshake Sec-WebSocket-Key field is invalid";
case error::no_sec_version: return "The WebSocket handshake Sec-WebSocket-Version field is missing";
case error::bad_sec_version: return "The WebSocket handshake Sec-WebSocket-Version field is invalid";
case error::no_sec_accept: return "The WebSocket handshake Sec-WebSocket-Accept field is missing";
case error::bad_sec_accept: return "The WebSocket handshake Sec-WebSocket-Accept field is invalid";
case error::upgrade_declined: return "The WebSocket handshake was declined by the remote peer";
case error::bad_opcode: return "The WebSocket frame contained an illegal opcode";
case error::bad_data_frame: return "The WebSocket data frame was unexpected";
case error::bad_continuation: return "The WebSocket continuation frame was unexpected";
case error::bad_reserved_bits: return "The WebSocket frame contained illegal reserved bits";
case error::bad_control_fragment: return "The WebSocket control frame was fragmented";
case error::bad_control_size: return "The WebSocket control frame size was invalid";
case error::bad_unmasked_frame: return "The WebSocket frame was unmasked";
case error::bad_masked_frame: return "The WebSocket frame was masked";
case error::bad_size: return "The WebSocket frame size was not canonical";
case error::bad_frame_payload: return "The WebSocket frame payload was not valid utf8";
case error::bad_close_code: return "The WebSocket close frame reason code was invalid";
case error::bad_close_size: return "The WebSocket close frame payload size was invalid";
case error::bad_close_payload: return "The WebSocket close frame payload was not valid utf8";
} }
} }
template<class> inline
string_view error_condition
error_conditions:: error_codes::
message(condition c) const default_error_condition(int ev) const noexcept
{ {
switch(c) switch(static_cast<error>(ev))
{ {
default: default:
case condition::handshake_failed: return "WebSocket upgrade handshake failed"; case error::closed:
case error::buffer_overflow:
case error::partial_deflate_block:
case error::message_too_big:
return {ev, *this};
case error::bad_http_version:
case error::bad_method:
case error::no_host:
case error::no_connection:
case error::no_connection_upgrade:
case error::no_upgrade:
case error::no_upgrade_websocket:
case error::no_sec_key:
case error::bad_sec_key:
case error::no_sec_version:
case error::bad_sec_version:
case error::no_sec_accept:
case error::bad_sec_accept:
case error::upgrade_declined:
return condition::handshake_failed;
case error::bad_opcode:
case error::bad_data_frame:
case error::bad_continuation:
case error::bad_reserved_bits:
case error::bad_control_fragment:
case error::bad_control_size:
case error::bad_unmasked_frame:
case error::bad_masked_frame:
case error::bad_size:
case error::bad_frame_payload:
case error::bad_close_code:
case error::bad_close_size:
case error::bad_close_payload:
return condition::protocol_violation;
} }
} }
template<class> inline
bool const char*
error_conditions:: error_conditions::
equivalent( name() const noexcept
error_code const& ec,
condition c) const noexcept
{ {
if(ec.category() == error_code{error{}}.category()) return "boost.beast.websocket";
{
switch(c)
{
case condition::handshake_failed:
switch(static_cast<error>(ec.value()))
{
case error::handshake_failed:
return true;
}
return false;
}
}
return false;
} }
inline
std::string
error_conditions::
message(int cv) const
{
switch(static_cast<condition>(cv))
{
default:
case condition::handshake_failed: return "The WebSocket handshake failed";
case condition::protocol_violation: return "A WebSocket protocol violation occurred";
}
}
} // detail } // detail
inline
error_code
make_error_code(error e)
{
static detail::error_codes const cat{};
return error_code{static_cast<
std::underlying_type<error>::type>(e), cat};
}
inline
error_condition
make_error_condition(condition c)
{
static detail::error_conditions const cat{};
return error_condition{static_cast<
std::underlying_type<condition>::type>(c), cat};
}
} // websocket } // websocket
} // beast } // beast
} // boost } // boost

View File

@ -84,7 +84,7 @@ class stream<NextLayer, deflateSupported>::read_some_op
MutableBufferSequence bs_; MutableBufferSequence bs_;
buffers_suffix<MutableBufferSequence> cb_; buffers_suffix<MutableBufferSequence> cb_;
std::size_t bytes_written_ = 0; std::size_t bytes_written_ = 0;
error_code ev_; error_code result_;
token tok_; token tok_;
close_code code_; close_code code_;
bool did_read_ = false; bool did_read_ = false;
@ -160,7 +160,6 @@ operator()(
using beast::detail::clamp; using beast::detail::clamp;
using boost::asio::buffer; using boost::asio::buffer;
using boost::asio::buffer_size; using boost::asio::buffer_size;
close_code code{};
cont_ = cont; cont_ = cont;
BOOST_ASIO_CORO_REENTER(*this) BOOST_ASIO_CORO_REENTER(*this)
{ {
@ -220,13 +219,15 @@ operator()(
{ {
// Read frame header // Read frame header
while(! ws_.parse_fh( while(! ws_.parse_fh(
ws_.rd_fh_, ws_.rd_buf_, code)) ws_.rd_fh_, ws_.rd_buf_, result_))
{ {
if(code != close_code::none) if(result_)
{ {
// _Fail the WebSocket Connection_ // _Fail the WebSocket Connection_
code_ = code; if(result_ == error::message_too_big)
ev_ = error::failed; code_ = close_code::too_big;
else
code_ = close_code::protocol_error;
goto close; goto close;
} }
BOOST_ASSERT(ws_.rd_block_ == tok_); BOOST_ASSERT(ws_.rd_block_ == tok_);
@ -370,7 +371,6 @@ operator()(
ws_.rd_fh_.len), ws_.rd_buf_.data()); ws_.rd_fh_.len), ws_.rd_buf_.data());
auto const len = buffer_size(cb); auto const len = buffer_size(cb);
BOOST_ASSERT(len == ws_.rd_fh_.len); BOOST_ASSERT(len == ws_.rd_fh_.len);
code = close_code::none;
ping_data payload; ping_data payload;
detail::read_ping(payload, cb); detail::read_ping(payload, cb);
ws_.rd_buf_.consume(len); ws_.rd_buf_.consume(len);
@ -400,12 +400,11 @@ operator()(
BOOST_ASSERT(! ws_.rd_close_); BOOST_ASSERT(! ws_.rd_close_);
ws_.rd_close_ = true; ws_.rd_close_ = true;
close_reason cr; close_reason cr;
detail::read_close(cr, cb, code); detail::read_close(cr, cb, result_);
if(code != close_code::none) if(result_)
{ {
// _Fail the WebSocket Connection_ // _Fail the WebSocket Connection_
code_ = code; code_ = close_code::protocol_error;
ev_ = error::failed;
goto close; goto close;
} }
ws_.cr_ = cr; ws_.cr_ = cr;
@ -419,14 +418,14 @@ operator()(
// _Close the WebSocket Connection_ // _Close the WebSocket Connection_
BOOST_ASSERT(ws_.wr_close_); BOOST_ASSERT(ws_.wr_close_);
code_ = close_code::none; code_ = close_code::none;
ev_ = error::closed; result_ = error::closed;
goto close; goto close;
} }
// _Start the WebSocket Closing Handshake_ // _Start the WebSocket Closing Handshake_
code_ = cr.code == close_code::none ? code_ = cr.code == close_code::none ?
close_code::normal : close_code::normal :
static_cast<close_code>(cr.code); static_cast<close_code>(cr.code);
ev_ = error::closed; result_ = error::closed;
goto close; goto close;
} }
} }
@ -477,7 +476,7 @@ operator()(
{ {
// _Fail the WebSocket Connection_ // _Fail the WebSocket Connection_
code_ = close_code::bad_payload; code_ = close_code::bad_payload;
ev_ = error::failed; result_ = error::bad_frame_payload;
goto close; goto close;
} }
} }
@ -511,7 +510,7 @@ operator()(
{ {
// _Fail the WebSocket Connection_ // _Fail the WebSocket Connection_
code_ = close_code::bad_payload; code_ = close_code::bad_payload;
ev_ = error::failed; result_ = error::bad_frame_payload;
goto close; goto close;
} }
} }
@ -604,7 +603,7 @@ operator()(
{ {
// _Fail the WebSocket Connection_ // _Fail the WebSocket Connection_
code_ = close_code::too_big; code_ = close_code::too_big;
ev_ = error::failed; result_ = error::message_too_big;
goto close; goto close;
} }
cb_.consume(zs.total_out); cb_.consume(zs.total_out);
@ -622,7 +621,7 @@ operator()(
{ {
// _Fail the WebSocket Connection_ // _Fail the WebSocket Connection_
code_ = close_code::bad_payload; code_ = close_code::bad_payload;
ev_ = error::failed; result_ = error::bad_frame_payload;
goto close; goto close;
} }
} }
@ -698,7 +697,7 @@ operator()(
ec.assign(0, ec.category()); ec.assign(0, ec.category());
} }
if(! ec) if(! ec)
ec = ev_; ec = result_;
if(ec && ec != error::closed) if(ec && ec != error::closed)
ws_.status_ = status::failed; ws_.status_ = status::failed;
else else
@ -1049,12 +1048,17 @@ loop:
if(rd_remain_ == 0 && (! rd_fh_.fin || rd_done_)) if(rd_remain_ == 0 && (! rd_fh_.fin || rd_done_))
{ {
// Read frame header // Read frame header
while(! parse_fh(rd_fh_, rd_buf_, code)) error_code result;
while(! parse_fh(rd_fh_, rd_buf_, result))
{ {
if(code != close_code::none) if(result)
{ {
// _Fail the WebSocket Connection_ // _Fail the WebSocket Connection_
do_fail(code, error::failed, ec); if(result == error::message_too_big)
code = close_code::too_big;
else
code = close_code::protocol_error;
do_fail(code, result, ec);
return bytes_written; return bytes_written;
} }
auto const bytes_transferred = auto const bytes_transferred =
@ -1121,11 +1125,12 @@ loop:
BOOST_ASSERT(! rd_close_); BOOST_ASSERT(! rd_close_);
rd_close_ = true; rd_close_ = true;
close_reason cr; close_reason cr;
detail::read_close(cr, b, code); detail::read_close(cr, b, result);
if(code != close_code::none) if(result)
{ {
// _Fail the WebSocket Connection_ // _Fail the WebSocket Connection_
do_fail(code, error::failed, ec); do_fail(close_code::protocol_error,
result, ec);
return bytes_written; return bytes_written;
} }
cr_ = cr; cr_ = cr;
@ -1190,10 +1195,8 @@ loop:
! rd_utf8_.finish())) ! rd_utf8_.finish()))
{ {
// _Fail the WebSocket Connection_ // _Fail the WebSocket Connection_
do_fail( do_fail(close_code::bad_payload,
close_code::bad_payload, error::bad_frame_payload, ec);
error::failed,
ec);
return bytes_written; return bytes_written;
} }
} }
@ -1227,7 +1230,7 @@ loop:
{ {
// _Fail the WebSocket Connection_ // _Fail the WebSocket Connection_
do_fail(close_code::bad_payload, do_fail(close_code::bad_payload,
error::failed, ec); error::bad_frame_payload, ec);
return bytes_written; return bytes_written;
} }
} }
@ -1325,7 +1328,7 @@ loop:
rd_size_, zs.total_out, rd_msg_max_)) rd_size_, zs.total_out, rd_msg_max_))
{ {
do_fail(close_code::too_big, do_fail(close_code::too_big,
error::failed, ec); error::message_too_big, ec);
return bytes_written; return bytes_written;
} }
cb.consume(zs.total_out); cb.consume(zs.total_out);
@ -1343,7 +1346,7 @@ loop:
{ {
// _Fail the WebSocket Connection_ // _Fail the WebSocket Connection_
do_fail(close_code::bad_payload, do_fail(close_code::bad_payload,
error::failed, ec); error::bad_frame_payload, ec);
return bytes_written; return bytes_written;
} }
} }

View File

@ -26,9 +26,9 @@ is_upgrade(http::header<true,
return false; return false;
if(req.method() != http::verb::get) if(req.method() != http::verb::get)
return false; return false;
if(! http::token_list{req["Connection"]}.exists("upgrade")) if(! http::token_list{req[http::field::connection]}.exists("upgrade"))
return false; return false;
if(! http::token_list{req["Upgrade"]}.exists("websocket")) if(! http::token_list{req[http::field::upgrade]}.exists("websocket"))
return false; return false;
return true; return true;
} }

View File

@ -332,20 +332,15 @@ stream<NextLayer, deflateSupported>::
parse_fh( parse_fh(
detail::frame_header& fh, detail::frame_header& fh,
DynamicBuffer& b, DynamicBuffer& b,
close_code& code) error_code& ec)
{ {
using boost::asio::buffer; using boost::asio::buffer;
using boost::asio::buffer_copy; using boost::asio::buffer_copy;
using boost::asio::buffer_size; using boost::asio::buffer_size;
auto const err =
[&](close_code cv)
{
code = cv;
return false;
};
if(buffer_size(b.data()) < 2) if(buffer_size(b.data()) < 2)
{ {
code = close_code::none; // need more bytes
ec.assign(0, ec.category());
return false; return false;
} }
buffers_suffix<typename buffers_suffix<typename
@ -368,7 +363,8 @@ parse_fh(
need += 4; need += 4;
if(buffer_size(cb) < need) if(buffer_size(cb) < need)
{ {
code = close_code::none; // need more bytes
ec.assign(0, ec.category());
return false; return false;
} }
fh.op = static_cast< fh.op = static_cast<
@ -385,13 +381,15 @@ parse_fh(
if(rd_cont_) if(rd_cont_)
{ {
// new data frame when continuation expected // new data frame when continuation expected
return err(close_code::protocol_error); ec = error::bad_data_frame;
return false;
} }
if(fh.rsv2 || fh.rsv3 || if(fh.rsv2 || fh.rsv3 ||
! this->rd_deflated(fh.rsv1)) ! this->rd_deflated(fh.rsv1))
{ {
// reserved bits not cleared // reserved bits not cleared
return err(close_code::protocol_error); ec = error::bad_reserved_bits;
return false;
} }
break; break;
@ -399,12 +397,14 @@ parse_fh(
if(! rd_cont_) if(! rd_cont_)
{ {
// continuation without an active message // continuation without an active message
return err(close_code::protocol_error); ec = error::bad_continuation;
return false;
} }
if(fh.rsv1 || fh.rsv2 || fh.rsv3) if(fh.rsv1 || fh.rsv2 || fh.rsv3)
{ {
// reserved bits not cleared // reserved bits not cleared
return err(close_code::protocol_error); ec = error::bad_reserved_bits;
return false;
} }
break; break;
@ -412,31 +412,41 @@ parse_fh(
if(detail::is_reserved(fh.op)) if(detail::is_reserved(fh.op))
{ {
// reserved opcode // reserved opcode
return err(close_code::protocol_error); ec = error::bad_opcode;
return false;
} }
if(! fh.fin) if(! fh.fin)
{ {
// fragmented control message // fragmented control message
return err(close_code::protocol_error); ec = error::bad_control_fragment;
return false;
} }
if(fh.len > 125) if(fh.len > 125)
{ {
// invalid length for control message // invalid length for control message
return err(close_code::protocol_error); ec = error::bad_control_size;
return false;
} }
if(fh.rsv1 || fh.rsv2 || fh.rsv3) if(fh.rsv1 || fh.rsv2 || fh.rsv3)
{ {
// reserved bits not cleared // reserved bits not cleared
return err(close_code::protocol_error); ec = error::bad_reserved_bits;
return false;
} }
break; break;
} }
// unmasked frame from client
if(role_ == role_type::server && ! fh.mask) if(role_ == role_type::server && ! fh.mask)
return err(close_code::protocol_error); {
// masked frame from server // unmasked frame from client
ec = error::bad_unmasked_frame;
return false;
}
if(role_ == role_type::client && fh.mask) if(role_ == role_type::client && fh.mask)
return err(close_code::protocol_error); {
// masked frame from server
ec = error::bad_masked_frame;
return false;
}
if(detail::is_control(fh.op) && if(detail::is_control(fh.op) &&
buffer_size(cb) < need + fh.len) buffer_size(cb) < need + fh.len)
{ {
@ -452,9 +462,12 @@ parse_fh(
BOOST_ASSERT(buffer_size(cb) >= sizeof(tmp)); BOOST_ASSERT(buffer_size(cb) >= sizeof(tmp));
cb.consume(buffer_copy(buffer(tmp), cb)); cb.consume(buffer_copy(buffer(tmp), cb));
fh.len = detail::big_uint16_to_native(&tmp[0]); fh.len = detail::big_uint16_to_native(&tmp[0]);
// length not canonical
if(fh.len < 126) if(fh.len < 126)
return err(close_code::protocol_error); {
// length not canonical
ec = error::bad_size;
return false;
}
break; break;
} }
case 127: case 127:
@ -463,9 +476,12 @@ parse_fh(
BOOST_ASSERT(buffer_size(cb) >= sizeof(tmp)); BOOST_ASSERT(buffer_size(cb) >= sizeof(tmp));
cb.consume(buffer_copy(buffer(tmp), cb)); cb.consume(buffer_copy(buffer(tmp), cb));
fh.len = detail::big_uint64_to_native(&tmp[0]); fh.len = detail::big_uint64_to_native(&tmp[0]);
// length not canonical
if(fh.len < 65536) if(fh.len < 65536)
return err(close_code::protocol_error); {
// length not canonical
ec = error::bad_size;
return false;
}
break; break;
} }
} }
@ -493,19 +509,27 @@ parse_fh(
{ {
if(rd_size_ > (std::numeric_limits< if(rd_size_ > (std::numeric_limits<
std::uint64_t>::max)() - fh.len) std::uint64_t>::max)() - fh.len)
return err(close_code::too_big); {
// message size exceeds configured limit
ec = error::message_too_big;
return false;
}
} }
if(! this->rd_deflated()) if(! this->rd_deflated())
{ {
if(rd_msg_max_ && beast::detail::sum_exceeds( if(rd_msg_max_ && beast::detail::sum_exceeds(
rd_size_, fh.len, rd_msg_max_)) rd_size_, fh.len, rd_msg_max_))
return err(close_code::too_big); {
// message size exceeds configured limit
ec = error::message_too_big;
return false;
}
} }
rd_cont_ = ! fh.fin; rd_cont_ = ! fh.fin;
rd_remain_ = fh.len; rd_remain_ = fh.len;
} }
b.consume(b.size() - buffer_size(cb)); b.consume(b.size() - buffer_size(cb));
code = close_code::none; ec.assign(0, ec.category());
return true; return true;
} }
@ -652,7 +676,8 @@ stream<NextLayer, deflateSupported>::
build_response( build_response(
http::request<Body, http::request<Body,
http::basic_fields<Allocator>> const& req, http::basic_fields<Allocator>> const& req,
Decorator const& decorator) Decorator const& decorator,
error_code& result)
{ {
auto const decorate = auto const decorate =
[&decorator](response_type& res) [&decorator](response_type& res)
@ -666,40 +691,58 @@ build_response(
} }
}; };
auto err = auto err =
[&](std::string const& text) [&](error e)
{ {
result = e;
response_type res; response_type res;
res.version(req.version()); res.version(req.version());
res.result(http::status::bad_request); res.result(http::status::bad_request);
res.body() = text; res.body() = result.message();
res.prepare_payload(); res.prepare_payload();
decorate(res); decorate(res);
return res; return res;
}; };
if(req.version() < 11) if(req.version() != 11)
return err("HTTP version 1.1 required"); return err(error::bad_http_version);
if(req.method() != http::verb::get) if(req.method() != http::verb::get)
return err("Wrong method"); return err(error::bad_method);
if(! is_upgrade(req))
return err("Expected Upgrade request");
if(! req.count(http::field::host)) if(! req.count(http::field::host))
return err("Missing Host"); return err(error::no_host);
if(! req.count(http::field::sec_websocket_key))
return err("Missing Sec-WebSocket-Key");
auto const key = req[http::field::sec_websocket_key];
if(key.size() > detail::sec_ws_key_type::max_size_n)
return err("Invalid Sec-WebSocket-Key");
{ {
auto const version = auto const it = req.find(http::field::connection);
req[http::field::sec_websocket_version]; if(it == req.end())
if(version.empty()) return err(error::no_connection);
return err("Missing Sec-WebSocket-Version"); if(! http::token_list{it->value()}.exists("upgrade"))
if(version != "13") return err(error::no_connection_upgrade);
}
{
auto const it = req.find(http::field::upgrade);
if(it == req.end())
return err(error::no_upgrade);
if(! http::token_list{it->value()}.exists("websocket"))
return err(error::no_upgrade_websocket);
}
string_view key;
{
auto const it = req.find(http::field::sec_websocket_key);
if(it == req.end())
return err(error::no_sec_key);
key = it->value();
if(key.size() > detail::sec_ws_key_type::max_size_n)
return err(error::bad_sec_key);
}
{
auto const it = req.find(http::field::sec_websocket_version);
if(it == req.end())
return err(error::no_sec_version);
if(it->value() != "13")
{ {
response_type res; response_type res;
res.result(http::status::upgrade_required); res.result(http::status::upgrade_required);
res.version(req.version()); res.version(req.version());
res.set(http::field::sec_websocket_version, "13"); res.set(http::field::sec_websocket_version, "13");
result = error::bad_sec_version;
res.body() = result.message();
res.prepare_payload(); res.prepare_payload();
decorate(res); decorate(res);
return res; return res;
@ -707,7 +750,6 @@ build_response(
} }
response_type res; response_type res;
build_response_pmd(res, req, is_deflate_supported{});
res.result(http::status::switching_protocols); res.result(http::status::switching_protocols);
res.version(req.version()); res.version(req.version());
res.set(http::field::upgrade, "websocket"); res.set(http::field::upgrade, "websocket");
@ -717,7 +759,9 @@ build_response(
detail::make_sec_ws_accept(acc, key); detail::make_sec_ws_accept(acc, key);
res.set(http::field::sec_websocket_accept, acc); res.set(http::field::sec_websocket_accept, acc);
} }
build_response_pmd(res, req, is_deflate_supported{});
decorate(res); decorate(res);
result = {};
return res; return res;
} }
@ -747,30 +791,39 @@ on_response(
detail::sec_ws_key_type const& key, detail::sec_ws_key_type const& key,
error_code& ec) error_code& ec)
{ {
bool const success = [&]() auto const err =
[&](error e)
{
ec = e;
};
if(res.result() != http::status::switching_protocols)
return err(error::upgrade_declined);
if(res.version() != 11)
return err(error::bad_http_version);
{ {
if(res.version() < 11) auto const it = res.find(http::field::connection);
return false; if(it == res.end())
if(res.result() != http::status::switching_protocols) return err(error::no_connection);
return false; if(! http::token_list{it->value()}.exists("upgrade"))
if(! http::token_list{res[http::field::connection]}.exists("upgrade")) return err(error::no_connection_upgrade);
return false; }
if(! http::token_list{res[http::field::upgrade]}.exists("websocket")) {
return false; auto const it = res.find(http::field::upgrade);
if(res.count(http::field::sec_websocket_accept) != 1) if(it == res.end())
return false; return err(error::no_upgrade);
if(! http::token_list{it->value()}.exists("websocket"))
return err(error::no_upgrade_websocket);
}
{
auto const it = res.find(http::field::sec_websocket_accept);
if(it == res.end())
return err(error::no_sec_accept);
detail::sec_ws_accept_type acc; detail::sec_ws_accept_type acc;
detail::make_sec_ws_accept(acc, key); detail::make_sec_ws_accept(acc, key);
if(acc.compare( if(acc.compare(it->value()) != 0)
res[http::field::sec_websocket_accept]) != 0) return err(error::bad_sec_accept);
return false;
return true;
}();
if(! success)
{
ec = error::handshake_failed;
return;
} }
ec.assign(0, ec.category()); ec.assign(0, ec.category());
on_response_pmd(res, is_deflate_supported{}); on_response_pmd(res, is_deflate_supported{});
open(role_type::client); open(role_type::client);

View File

@ -3446,8 +3446,10 @@ private:
template<class DynamicBuffer> template<class DynamicBuffer>
bool bool
parse_fh(detail::frame_header& fh, parse_fh(
DynamicBuffer& b, close_code& code); detail::frame_header& fh,
DynamicBuffer& b,
error_code& ec);
template<class DynamicBuffer> template<class DynamicBuffer>
void void
@ -3483,7 +3485,8 @@ private:
build_response( build_response(
http::request<Body, http::request<Body,
http::basic_fields<Allocator>> const& req, http::basic_fields<Allocator>> const& req,
Decorator const& decorator); Decorator const& decorator,
error_code& ec);
template<class Body, class Allocator> template<class Body, class Allocator>
void void

View File

@ -406,7 +406,7 @@ public:
catch(system_error const& e) catch(system_error const& e)
{ {
if( e.code() != if( e.code() !=
websocket::error::handshake_failed && websocket::error::no_sec_key &&
e.code() != e.code() !=
boost::asio::error::eof) boost::asio::error::eof)
throw; throw;
@ -483,8 +483,8 @@ public:
} }
}; };
// wrong version // bad version
check(error::handshake_failed, check(error::bad_http_version,
"GET / HTTP/1.0\r\n" "GET / HTTP/1.0\r\n"
"Host: localhost:80\r\n" "Host: localhost:80\r\n"
"Upgrade: WebSocket\r\n" "Upgrade: WebSocket\r\n"
@ -493,8 +493,8 @@ public:
"Sec-WebSocket-Version: 13\r\n" "Sec-WebSocket-Version: 13\r\n"
"\r\n" "\r\n"
); );
// wrong method // bad method
check(error::handshake_failed, check(error::bad_method,
"POST / HTTP/1.1\r\n" "POST / HTTP/1.1\r\n"
"Host: localhost:80\r\n" "Host: localhost:80\r\n"
"Upgrade: WebSocket\r\n" "Upgrade: WebSocket\r\n"
@ -503,8 +503,8 @@ public:
"Sec-WebSocket-Version: 13\r\n" "Sec-WebSocket-Version: 13\r\n"
"\r\n" "\r\n"
); );
// missing Host // no Host
check(error::handshake_failed, check(error::no_host,
"GET / HTTP/1.1\r\n" "GET / HTTP/1.1\r\n"
"Upgrade: WebSocket\r\n" "Upgrade: WebSocket\r\n"
"Connection: keep-alive,upgrade\r\n" "Connection: keep-alive,upgrade\r\n"
@ -512,46 +512,17 @@ public:
"Sec-WebSocket-Version: 13\r\n" "Sec-WebSocket-Version: 13\r\n"
"\r\n" "\r\n"
); );
// missing Sec-WebSocket-Key // no Connection
check(error::handshake_failed, check(error::no_connection,
"GET / HTTP/1.1\r\n" "GET / HTTP/1.1\r\n"
"Host: localhost:80\r\n" "Host: localhost:80\r\n"
"Upgrade: WebSocket\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-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
"Sec-WebSocket-Version: 13\r\n" "Sec-WebSocket-Version: 13\r\n"
"\r\n" "\r\n"
); );
// missing connection token // no Connection upgrade
check(error::handshake_failed, check(error::no_connection_upgrade,
"GET / HTTP/1.1\r\n" "GET / HTTP/1.1\r\n"
"Host: localhost:80\r\n" "Host: localhost:80\r\n"
"Upgrade: WebSocket\r\n" "Upgrade: WebSocket\r\n"
@ -560,8 +531,36 @@ public:
"Sec-WebSocket-Version: 13\r\n" "Sec-WebSocket-Version: 13\r\n"
"\r\n" "\r\n"
); );
// oversize key // no Upgrade
check(error::handshake_failed, check(error::no_upgrade,
"GET / HTTP/1.1\r\n"
"Host: localhost:80\r\n"
"Connection: upgrade\r\n"
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
"Sec-WebSocket-Version: 13\r\n"
"\r\n"
);
// no Upgrade websocket
check(error::no_upgrade_websocket,
"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"
);
// no Sec-WebSocket-Key
check(error::no_sec_key,
"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"
);
// bad Sec-WebSocket-Key
check(error::bad_sec_key,
"GET / HTTP/1.1\r\n" "GET / HTTP/1.1\r\n"
"Host: localhost:80\r\n" "Host: localhost:80\r\n"
"Upgrade: WebSocket\r\n" "Upgrade: WebSocket\r\n"
@ -570,8 +569,27 @@ public:
"Sec-WebSocket-Version: 13\r\n" "Sec-WebSocket-Version: 13\r\n"
"\r\n" "\r\n"
); );
// bad version // no Sec-WebSocket-Version
check(error::handshake_failed, check(error::no_sec_version,
"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"
);
// bad Sec-WebSocket-Version
check(error::bad_sec_version,
"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"
);
// bad Sec-WebSocket-Version
check(error::bad_sec_version,
"GET / HTTP/1.1\r\n" "GET / HTTP/1.1\r\n"
"Host: localhost:80\r\n" "Host: localhost:80\r\n"
"Upgrade: WebSocket\r\n" "Upgrade: WebSocket\r\n"
@ -580,15 +598,6 @@ public:
"Sec-WebSocket-Version: 12\r\n" "Sec-WebSocket-Version: 12\r\n"
"\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 // valid request
check({}, check({},
"GET / HTTP/1.1\r\n" "GET / HTTP/1.1\r\n"

View File

@ -108,7 +108,7 @@ public:
catch(system_error const& se) catch(system_error const& se)
{ {
BEAST_EXPECTS( BEAST_EXPECTS(
se.code() == error::failed, se.code() == error::bad_masked_frame,
se.code().message()); se.code().message());
} }
} }
@ -128,7 +128,7 @@ public:
catch(system_error const& se) catch(system_error const& se)
{ {
BEAST_EXPECTS( BEAST_EXPECTS(
se.code() == error::failed, se.code() == error::bad_close_size,
se.code().message()); se.code().message());
} }
} }
@ -292,7 +292,7 @@ public:
ws.async_read(b, ws.async_read(b,
[&](error_code ec, std::size_t) [&](error_code ec, std::size_t)
{ {
if(ec != error::failed) if(ec != error::bad_control_fragment)
BOOST_THROW_EXCEPTION( BOOST_THROW_EXCEPTION(
system_error{ec}); system_error{ec});
BEAST_EXPECT(++count == 1); BEAST_EXPECT(++count == 1);

View File

@ -25,32 +25,53 @@ public:
auto const ec = make_error_code(e); auto const ec = make_error_code(e);
ec.category().name(); ec.category().name();
BEAST_EXPECT(! ec.message().empty()); BEAST_EXPECT(! ec.message().empty());
#if 0 BEAST_EXPECT(ec != condition::handshake_failed);
BEAST_EXPECT(std::addressof(ec.category()) == BEAST_EXPECT(ec != condition::protocol_violation);
std::addressof(detail::get_error_category()));
BEAST_EXPECT(detail::get_error_category().equivalent(
static_cast<std::underlying_type<error>::type>(e),
ec.category().default_error_condition(
static_cast<std::underlying_type<error>::type>(e))));
BEAST_EXPECT(detail::get_error_category().equivalent(
ec, static_cast<std::underlying_type<error>::type>(e)));
#endif
} }
void check(error e, condition c) void check(condition c, error e)
{ {
BEAST_EXPECT(error_code{e} == c); auto const ec = make_error_code(e);
BEAST_EXPECT(ec.category().name() != nullptr);
BEAST_EXPECT(! ec.message().empty());
BEAST_EXPECT(ec == c);
} }
void run() override void run() override
{ {
check(error::closed); check(error::closed);
check(error::failed);
check(error::handshake_failed);
check(error::buffer_overflow); check(error::buffer_overflow);
check(error::partial_deflate_block); check(error::partial_deflate_block);
check(error::message_too_big);
check(error::handshake_failed, condition::handshake_failed); check(condition::protocol_violation, error::bad_opcode);
check(condition::protocol_violation, error::bad_data_frame);
check(condition::protocol_violation, error::bad_continuation);
check(condition::protocol_violation, error::bad_reserved_bits);
check(condition::protocol_violation, error::bad_control_fragment);
check(condition::protocol_violation, error::bad_control_size);
check(condition::protocol_violation, error::bad_unmasked_frame);
check(condition::protocol_violation, error::bad_masked_frame);
check(condition::protocol_violation, error::bad_size);
check(condition::protocol_violation, error::bad_frame_payload);
check(condition::protocol_violation, error::bad_close_code);
check(condition::protocol_violation, error::bad_close_size);
check(condition::protocol_violation, error::bad_close_payload);
check(condition::handshake_failed, error::bad_http_version);
check(condition::handshake_failed, error::bad_method);
check(condition::handshake_failed, error::no_host);
check(condition::handshake_failed, error::no_connection);
check(condition::handshake_failed, error::no_connection_upgrade);
check(condition::handshake_failed, error::no_upgrade);
check(condition::handshake_failed, error::no_upgrade_websocket);
check(condition::handshake_failed, error::no_sec_key);
check(condition::handshake_failed, error::bad_sec_key);
check(condition::handshake_failed, error::no_sec_version);
check(condition::handshake_failed, error::bad_sec_version);
check(condition::handshake_failed, error::no_sec_accept);
check(condition::handshake_failed, error::bad_sec_accept);
check(condition::handshake_failed, error::upgrade_declined);
} }
}; };

View File

@ -137,7 +137,7 @@ public:
}); });
auto const check = auto const check =
[&](std::string const& s) [&](error e, std::string const& s)
{ {
stream<test::stream> ws{ioc_}; stream<test::stream> ws{ioc_};
auto tr = connect(ws.next_layer()); auto tr = connect(ws.next_layer());
@ -150,11 +150,11 @@ public:
} }
catch(system_error const& se) catch(system_error const& se)
{ {
BEAST_EXPECT(se.code() == error::handshake_failed); BEAST_EXPECTS(se.code() == e, se.what());
} }
}; };
// wrong HTTP version // bad HTTP version
check( check(error::bad_http_version,
"HTTP/1.0 101 Switching Protocols\r\n" "HTTP/1.0 101 Switching Protocols\r\n"
"Server: beast\r\n" "Server: beast\r\n"
"Upgrade: WebSocket\r\n" "Upgrade: WebSocket\r\n"
@ -163,28 +163,17 @@ public:
"Sec-WebSocket-Version: 13\r\n" "Sec-WebSocket-Version: 13\r\n"
"\r\n" "\r\n"
); );
// wrong status // no Connection
check( check(error::no_connection,
"HTTP/1.1 200 OK\r\n"
"Server: beast\r\n"
"Upgrade: WebSocket\r\n"
"Connection: upgrade\r\n"
"Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
"Sec-WebSocket-Version: 13\r\n"
"\r\n"
);
// missing upgrade token
check(
"HTTP/1.1 101 Switching Protocols\r\n" "HTTP/1.1 101 Switching Protocols\r\n"
"Server: beast\r\n" "Server: beast\r\n"
"Upgrade: HTTP/2\r\n" "Upgrade: WebSocket\r\n"
"Connection: upgrade\r\n"
"Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n" "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
"Sec-WebSocket-Version: 13\r\n" "Sec-WebSocket-Version: 13\r\n"
"\r\n" "\r\n"
); );
// missing connection token // no Connection upgrade
check( check(error::no_connection_upgrade,
"HTTP/1.1 101 Switching Protocols\r\n" "HTTP/1.1 101 Switching Protocols\r\n"
"Server: beast\r\n" "Server: beast\r\n"
"Upgrade: WebSocket\r\n" "Upgrade: WebSocket\r\n"
@ -193,8 +182,27 @@ public:
"Sec-WebSocket-Version: 13\r\n" "Sec-WebSocket-Version: 13\r\n"
"\r\n" "\r\n"
); );
// missing accept key // no Upgrade
check( check(error::no_upgrade,
"HTTP/1.1 101 Switching Protocols\r\n"
"Server: beast\r\n"
"Connection: upgrade\r\n"
"Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
"Sec-WebSocket-Version: 13\r\n"
"\r\n"
);
// no Upgrade websocket
check(error::no_upgrade_websocket,
"HTTP/1.1 101 Switching Protocols\r\n"
"Server: beast\r\n"
"Upgrade: HTTP/2\r\n"
"Connection: upgrade\r\n"
"Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
"Sec-WebSocket-Version: 13\r\n"
"\r\n"
);
// no Sec-WebSocket-Accept
check(error::no_sec_accept,
"HTTP/1.1 101 Switching Protocols\r\n" "HTTP/1.1 101 Switching Protocols\r\n"
"Server: beast\r\n" "Server: beast\r\n"
"Upgrade: WebSocket\r\n" "Upgrade: WebSocket\r\n"
@ -202,8 +210,8 @@ public:
"Sec-WebSocket-Version: 13\r\n" "Sec-WebSocket-Version: 13\r\n"
"\r\n" "\r\n"
); );
// wrong accept key // bad Sec-WebSocket-Accept
check( check(error::bad_sec_accept,
"HTTP/1.1 101 Switching Protocols\r\n" "HTTP/1.1 101 Switching Protocols\r\n"
"Server: beast\r\n" "Server: beast\r\n"
"Upgrade: WebSocket\r\n" "Upgrade: WebSocket\r\n"
@ -212,6 +220,16 @@ public:
"Sec-WebSocket-Version: 13\r\n" "Sec-WebSocket-Version: 13\r\n"
"\r\n" "\r\n"
); );
// declined
check(error::upgrade_declined,
"HTTP/1.1 200 OK\r\n"
"Server: beast\r\n"
"Upgrade: WebSocket\r\n"
"Connection: upgrade\r\n"
"Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
"Sec-WebSocket-Version: 13\r\n"
"\r\n"
);
} }
// Compression Extensions for WebSocket // Compression Extensions for WebSocket

View File

@ -218,7 +218,7 @@ public:
[&](error_code ec, std::size_t) [&](error_code ec, std::size_t)
{ {
++count; ++count;
if(ec != error::failed) if(ec != error::bad_control_fragment)
BOOST_THROW_EXCEPTION( BOOST_THROW_EXCEPTION(
system_error{ec}); system_error{ec});
}); });

View File

@ -317,7 +317,7 @@ public:
{ {
put(ws.next_layer().buffer(), cbuf( put(ws.next_layer().buffer(), cbuf(
0x88, 0x02, 0x03, 0xed)); 0x88, 0x02, 0x03, 0xed));
doFailTest(w, ws, error::failed); doFailTest(w, ws, error::bad_close_code);
}); });
// message size above 2^64 // message size above 2^64
@ -337,7 +337,7 @@ public:
{ {
ws.read_message_max(1); ws.read_message_max(1);
w.write(ws, sbuf("**")); w.write(ws, sbuf("**"));
doFailTest(w, ws, error::failed); doFailTest(w, ws, error::message_too_big);
}); });
// bad utf8 // bad utf8
@ -346,7 +346,7 @@ public:
{ {
put(ws.next_layer().buffer(), cbuf( put(ws.next_layer().buffer(), cbuf(
0x81, 0x06, 0x03, 0xea, 0xf0, 0x28, 0x8c, 0xbc)); 0x81, 0x06, 0x03, 0xea, 0xf0, 0x28, 0x8c, 0xbc));
doFailTest(w, ws, error::failed); doFailTest(w, ws, error::bad_frame_payload);
}); });
// incomplete utf8 // incomplete utf8
@ -378,7 +378,7 @@ public:
} }
catch(system_error const& se) catch(system_error const& se)
{ {
if(se.code() != error::failed) if(se.code() != error::bad_frame_payload)
throw; throw;
} }
}); });
@ -409,15 +409,15 @@ public:
}; };
// payload length 1 // payload length 1
check(error::failed, check(error::bad_close_size,
"\x88\x01\x01"); "\x88\x01\x01");
// invalid close code 1005 // invalid close code 1005
check(error::failed, check(error::bad_close_code,
"\x88\x02\x03\xed"); "\x88\x02\x03\xed");
// invalid utf8 // invalid utf8
check(error::failed, check(error::bad_close_payload,
"\x88\x06\xfc\x15\x0f\xd7\x73\x43"); "\x88\x06\xfc\x15\x0f\xd7\x73\x43");
// good utf8 // good utf8
@ -446,7 +446,7 @@ public:
std::string const s = std::string(128, '*'); std::string const s = std::string(128, '*');
w.write(ws, buffer(s)); w.write(ws, buffer(s));
ws.read_message_max(32); ws.read_message_max(32);
doFailTest(w, ws, error::failed); doFailTest(w, ws, error::message_too_big);
}); });
// invalid inflate block // invalid inflate block
@ -656,15 +656,15 @@ public:
}; };
// payload length 1 // payload length 1
check(error::failed, check(error::bad_close_size,
"\x88\x01\x01"); "\x88\x01\x01");
// invalid close code 1005 // invalid close code 1005
check(error::failed, check(error::bad_close_code,
"\x88\x02\x03\xed"); "\x88\x02\x03\xed");
// invalid utf8 // invalid utf8
check(error::failed, check(error::bad_close_payload,
"\x88\x06\xfc\x15\x0f\xd7\x73\x43"); "\x88\x06\xfc\x15\x0f\xd7\x73\x43");
// good utf8 // good utf8