mirror of
https://github.com/boostorg/beast.git
synced 2025-07-29 20:37:31 +02:00
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:
14
CHANGELOG.md
14
CHANGELOG.md
@ -8,6 +8,20 @@ WebSocket:
|
||||
* Refactor error headers
|
||||
* 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:
|
||||
|
@ -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
|
||||
the "next layer" and must meet the requirements of __SyncStream__ if
|
||||
synchronous operations are performed, __AsyncStream__ if asynchronous
|
||||
operations are performed, or both. Any arguments supplied during
|
||||
construction of the stream wrapper are passed to next layer's constructor.
|
||||
operations are performed, or both. Any arguments supplied to the
|
||||
constructor of the stream wrapper are forwarded to next layer's constructor.
|
||||
|
||||
The value of `deflateSupported` determines if the stream will support
|
||||
(but not require) the permessage-deflate extension
|
||||
|
@ -145,6 +145,7 @@
|
||||
<bridgehead renderas="sect3">Constants</bridgehead>
|
||||
<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__condition">condition</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__role_type">role_type</link></member>
|
||||
|
@ -41,79 +41,34 @@ namespace detail {
|
||||
|
||||
class error_codes : public error_category
|
||||
{
|
||||
template<class = void>
|
||||
string_view
|
||||
message(error e) const;
|
||||
|
||||
public:
|
||||
const char*
|
||||
name() const noexcept override
|
||||
{
|
||||
return "boost.beast.websocket";
|
||||
}
|
||||
name() const noexcept override;
|
||||
|
||||
std::string
|
||||
message(int ev) const override
|
||||
{
|
||||
return message(
|
||||
static_cast<error>(ev)).to_string();
|
||||
}
|
||||
message(int ev) const override;
|
||||
|
||||
error_condition
|
||||
default_error_condition(int ev) const noexcept override;
|
||||
};
|
||||
|
||||
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:
|
||||
const char*
|
||||
name() const noexcept override
|
||||
{
|
||||
return "boost.beast.websocket";
|
||||
}
|
||||
name() const noexcept override;
|
||||
|
||||
std::string
|
||||
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));
|
||||
}
|
||||
message(int cv) const override;
|
||||
};
|
||||
|
||||
} // 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};
|
||||
}
|
||||
make_error_code(error e);
|
||||
|
||||
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};
|
||||
}
|
||||
make_error_condition(condition c);
|
||||
|
||||
} // websocket
|
||||
} // beast
|
||||
|
@ -10,6 +10,7 @@
|
||||
#ifndef 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/detail/utf8_checker.hpp>
|
||||
#include <boost/beast/core/buffers_suffix.hpp>
|
||||
@ -244,8 +245,10 @@ read_ping(ping_data& data, Buffers const& bs)
|
||||
//
|
||||
template<class Buffers>
|
||||
void
|
||||
read_close(close_reason& cr,
|
||||
Buffers const& bs, close_code& code)
|
||||
read_close(
|
||||
close_reason& cr,
|
||||
Buffers const& bs,
|
||||
error_code& ec)
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_copy;
|
||||
@ -256,12 +259,13 @@ read_close(close_reason& cr,
|
||||
if(n == 0)
|
||||
{
|
||||
cr = close_reason{};
|
||||
code = close_code::none;
|
||||
ec.assign(0, ec.category());
|
||||
return;
|
||||
}
|
||||
if(n == 1)
|
||||
{
|
||||
code = close_code::protocol_error;
|
||||
// invalid payload size == 1
|
||||
ec = error::bad_close_size;
|
||||
return;
|
||||
}
|
||||
buffers_suffix<Buffers> cb(bs);
|
||||
@ -273,7 +277,8 @@ read_close(close_reason& cr,
|
||||
n -= 2;
|
||||
if(! is_valid_close_code(cr.code))
|
||||
{
|
||||
code = close_code::protocol_error;
|
||||
// invalid close code
|
||||
ec = error::bad_close_code;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -284,7 +289,8 @@ read_close(close_reason& cr,
|
||||
if(! check_utf8(
|
||||
cr.reason.data(), cr.reason.size()))
|
||||
{
|
||||
code = close_code::protocol_error;
|
||||
// not valid utf-8
|
||||
ec = error::bad_close_payload;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -292,7 +298,7 @@ read_close(close_reason& cr,
|
||||
{
|
||||
cr.reason = "";
|
||||
}
|
||||
code = close_code::none;
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
@ -21,32 +21,228 @@ namespace websocket {
|
||||
/// Error codes returned from @ref beast::websocket::stream operations.
|
||||
enum class error
|
||||
{
|
||||
/// Both sides performed a WebSocket close
|
||||
/** The WebSocket stream was gracefully closed at both endpoints
|
||||
*/
|
||||
closed = 1,
|
||||
|
||||
/// WebSocket connection failed, protocol violation
|
||||
failed,
|
||||
/* The error codes error::failed and error::handshake_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
|
||||
handshake_failed,
|
||||
- VFALCO
|
||||
*/
|
||||
#if ! BOOST_BEAST_DOXYGEN
|
||||
unused1 = 2, // failed
|
||||
unused2 = 3, // handshake_failed
|
||||
#endif
|
||||
|
||||
/// buffer overflow
|
||||
/** The WebSocket operation caused a dynamic buffer overflow
|
||||
*/
|
||||
buffer_overflow,
|
||||
|
||||
/// partial deflate block
|
||||
partial_deflate_block
|
||||
/** The WebSocket stream produced an incomplete 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.
|
||||
enum class condition
|
||||
{
|
||||
/** Handshake failed
|
||||
/** The WebSocket handshake failed
|
||||
|
||||
This condition indicates that the WebSocket handshake failed. If
|
||||
the corresponding HTTP response indicates the keep-alive behavior,
|
||||
then the handshake may be reattempted.
|
||||
*/
|
||||
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
|
||||
|
@ -42,6 +42,7 @@ class stream<NextLayer, deflateSupported>::response_op
|
||||
struct data
|
||||
{
|
||||
stream<NextLayer, deflateSupported>& ws;
|
||||
error_code result;
|
||||
response_type res;
|
||||
|
||||
template<class Body, class Allocator, class Decorator>
|
||||
@ -52,7 +53,7 @@ class stream<NextLayer, deflateSupported>::response_op
|
||||
http::basic_fields<Allocator>> const& req,
|
||||
Decorator const& decorator)
|
||||
: ws(ws_)
|
||||
, res(ws_.build_response(req, decorator))
|
||||
, res(ws_.build_response(req, decorator, result))
|
||||
{
|
||||
}
|
||||
};
|
||||
@ -120,9 +121,8 @@ operator()(
|
||||
BOOST_ASIO_CORO_YIELD
|
||||
http::async_write(d.ws.next_layer(),
|
||||
d.res, std::move(*this));
|
||||
if(! ec && d.res.result() !=
|
||||
http::status::switching_protocols)
|
||||
ec = error::handshake_failed;
|
||||
if(! ec)
|
||||
ec = d.result;
|
||||
if(! ec)
|
||||
{
|
||||
d.ws.do_pmd_config(d.res, is_deflate_supported{});
|
||||
@ -742,13 +742,14 @@ do_accept(
|
||||
Decorator const& decorator,
|
||||
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);
|
||||
if(ec)
|
||||
return;
|
||||
if(res.result() != http::status::switching_protocols)
|
||||
ec = result;
|
||||
if(ec)
|
||||
{
|
||||
ec = error::handshake_failed;
|
||||
// VFALCO TODO Respect keep alive setting, perform
|
||||
// teardown if Connection: close.
|
||||
return;
|
||||
|
@ -121,7 +121,6 @@ operator()(
|
||||
{
|
||||
using beast::detail::clamp;
|
||||
auto& d = *d_;
|
||||
close_code code{};
|
||||
d.cont = cont;
|
||||
BOOST_ASIO_CORO_REENTER(*this)
|
||||
{
|
||||
@ -219,13 +218,10 @@ operator()(
|
||||
{
|
||||
// Read frame header
|
||||
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)
|
||||
{
|
||||
d.ev = error::failed;
|
||||
if(d.ev)
|
||||
goto teardown;
|
||||
}
|
||||
BOOST_ASIO_CORO_YIELD
|
||||
d.ws.stream_.async_read_some(
|
||||
d.ws.rd_buf_.prepare(read_size(d.ws.rd_buf_,
|
||||
@ -247,13 +243,9 @@ operator()(
|
||||
d.ws.rd_buf_.data());
|
||||
if(d.ws.rd_fh_.len > 0 && d.ws.rd_fh_.mask)
|
||||
detail::mask_inplace(mb, d.ws.rd_key_);
|
||||
detail::read_close(d.ws.cr_, mb, code);
|
||||
if(code != close_code::none)
|
||||
{
|
||||
// Protocol error
|
||||
d.ev = error::failed;
|
||||
detail::read_close(d.ws.cr_, mb, d.ev);
|
||||
if(d.ev)
|
||||
goto teardown;
|
||||
}
|
||||
d.ws.rd_buf_.consume(clamp(d.ws.rd_fh_.len));
|
||||
goto teardown;
|
||||
}
|
||||
@ -364,18 +356,18 @@ close(close_reason const& cr, error_code& ec)
|
||||
if(! check_ok(ec))
|
||||
return;
|
||||
status_ = status::closing;
|
||||
error_code result;
|
||||
// Drain the connection
|
||||
close_code code{};
|
||||
if(rd_remain_ > 0)
|
||||
goto read_payload;
|
||||
for(;;)
|
||||
{
|
||||
// Read frame header
|
||||
while(! parse_fh(rd_fh_, rd_buf_, code))
|
||||
while(! parse_fh(rd_fh_, rd_buf_, result))
|
||||
{
|
||||
if(code != close_code::none)
|
||||
return do_fail(close_code::none,
|
||||
error::failed, ec);
|
||||
if(result)
|
||||
return do_fail(
|
||||
close_code::none, result, ec);
|
||||
auto const bytes_transferred =
|
||||
stream_.read_some(
|
||||
rd_buf_.prepare(read_size(rd_buf_,
|
||||
@ -396,12 +388,12 @@ close(close_reason const& cr, error_code& ec)
|
||||
rd_buf_.data());
|
||||
if(rd_fh_.len > 0 && rd_fh_.mask)
|
||||
detail::mask_inplace(mb, rd_key_);
|
||||
detail::read_close(cr_, mb, code);
|
||||
if(code != close_code::none)
|
||||
detail::read_close(cr_, mb, result);
|
||||
if(result)
|
||||
{
|
||||
// Protocol error
|
||||
return do_fail(close_code::none,
|
||||
error::failed, ec);
|
||||
// Protocol violation
|
||||
return do_fail(
|
||||
close_code::none, result, ec);
|
||||
}
|
||||
rd_buf_.consume(clamp(rd_fh_.len));
|
||||
break;
|
||||
|
@ -15,59 +15,146 @@ namespace beast {
|
||||
namespace websocket {
|
||||
namespace detail {
|
||||
|
||||
template<class>
|
||||
string_view
|
||||
inline
|
||||
const char*
|
||||
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:
|
||||
case error::failed: return "WebSocket connection failed due to a protocol violation";
|
||||
case error::closed: return "WebSocket connection closed normally";
|
||||
case error::handshake_failed: return "WebSocket upgrade handshake failed";
|
||||
case error::buffer_overflow: return "WebSocket dynamic buffer overflow";
|
||||
case error::partial_deflate_block: return "WebSocket partial deflate block";
|
||||
case error::closed: return "The WebSocket stream was gracefully closed at both endpoints";
|
||||
case error::buffer_overflow: return "The WebSocket operation caused a dynamic buffer overflow";
|
||||
case error::partial_deflate_block: return "The WebSocket stream produced an incomplete deflate block";
|
||||
case error::message_too_big: return "The WebSocket message exceeded the locally configured limit";
|
||||
|
||||
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>
|
||||
string_view
|
||||
error_conditions::
|
||||
message(condition c) const
|
||||
inline
|
||||
error_condition
|
||||
error_codes::
|
||||
default_error_condition(int ev) const noexcept
|
||||
{
|
||||
switch(c)
|
||||
switch(static_cast<error>(ev))
|
||||
{
|
||||
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>
|
||||
bool
|
||||
inline
|
||||
const char*
|
||||
error_conditions::
|
||||
equivalent(
|
||||
error_code const& ec,
|
||||
condition c) const noexcept
|
||||
name() const noexcept
|
||||
{
|
||||
if(ec.category() == error_code{error{}}.category())
|
||||
{
|
||||
switch(c)
|
||||
{
|
||||
case condition::handshake_failed:
|
||||
switch(static_cast<error>(ec.value()))
|
||||
{
|
||||
case error::handshake_failed:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return "boost.beast.websocket";
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
} // beast
|
||||
} // boost
|
||||
|
@ -84,7 +84,7 @@ class stream<NextLayer, deflateSupported>::read_some_op
|
||||
MutableBufferSequence bs_;
|
||||
buffers_suffix<MutableBufferSequence> cb_;
|
||||
std::size_t bytes_written_ = 0;
|
||||
error_code ev_;
|
||||
error_code result_;
|
||||
token tok_;
|
||||
close_code code_;
|
||||
bool did_read_ = false;
|
||||
@ -160,7 +160,6 @@ operator()(
|
||||
using beast::detail::clamp;
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_size;
|
||||
close_code code{};
|
||||
cont_ = cont;
|
||||
BOOST_ASIO_CORO_REENTER(*this)
|
||||
{
|
||||
@ -220,13 +219,15 @@ operator()(
|
||||
{
|
||||
// Read frame header
|
||||
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_
|
||||
code_ = code;
|
||||
ev_ = error::failed;
|
||||
if(result_ == error::message_too_big)
|
||||
code_ = close_code::too_big;
|
||||
else
|
||||
code_ = close_code::protocol_error;
|
||||
goto close;
|
||||
}
|
||||
BOOST_ASSERT(ws_.rd_block_ == tok_);
|
||||
@ -370,7 +371,6 @@ operator()(
|
||||
ws_.rd_fh_.len), ws_.rd_buf_.data());
|
||||
auto const len = buffer_size(cb);
|
||||
BOOST_ASSERT(len == ws_.rd_fh_.len);
|
||||
code = close_code::none;
|
||||
ping_data payload;
|
||||
detail::read_ping(payload, cb);
|
||||
ws_.rd_buf_.consume(len);
|
||||
@ -400,12 +400,11 @@ operator()(
|
||||
BOOST_ASSERT(! ws_.rd_close_);
|
||||
ws_.rd_close_ = true;
|
||||
close_reason cr;
|
||||
detail::read_close(cr, cb, code);
|
||||
if(code != close_code::none)
|
||||
detail::read_close(cr, cb, result_);
|
||||
if(result_)
|
||||
{
|
||||
// _Fail the WebSocket Connection_
|
||||
code_ = code;
|
||||
ev_ = error::failed;
|
||||
code_ = close_code::protocol_error;
|
||||
goto close;
|
||||
}
|
||||
ws_.cr_ = cr;
|
||||
@ -419,14 +418,14 @@ operator()(
|
||||
// _Close the WebSocket Connection_
|
||||
BOOST_ASSERT(ws_.wr_close_);
|
||||
code_ = close_code::none;
|
||||
ev_ = error::closed;
|
||||
result_ = error::closed;
|
||||
goto close;
|
||||
}
|
||||
// _Start the WebSocket Closing Handshake_
|
||||
code_ = cr.code == close_code::none ?
|
||||
close_code::normal :
|
||||
static_cast<close_code>(cr.code);
|
||||
ev_ = error::closed;
|
||||
result_ = error::closed;
|
||||
goto close;
|
||||
}
|
||||
}
|
||||
@ -477,7 +476,7 @@ operator()(
|
||||
{
|
||||
// _Fail the WebSocket Connection_
|
||||
code_ = close_code::bad_payload;
|
||||
ev_ = error::failed;
|
||||
result_ = error::bad_frame_payload;
|
||||
goto close;
|
||||
}
|
||||
}
|
||||
@ -511,7 +510,7 @@ operator()(
|
||||
{
|
||||
// _Fail the WebSocket Connection_
|
||||
code_ = close_code::bad_payload;
|
||||
ev_ = error::failed;
|
||||
result_ = error::bad_frame_payload;
|
||||
goto close;
|
||||
}
|
||||
}
|
||||
@ -604,7 +603,7 @@ operator()(
|
||||
{
|
||||
// _Fail the WebSocket Connection_
|
||||
code_ = close_code::too_big;
|
||||
ev_ = error::failed;
|
||||
result_ = error::message_too_big;
|
||||
goto close;
|
||||
}
|
||||
cb_.consume(zs.total_out);
|
||||
@ -622,7 +621,7 @@ operator()(
|
||||
{
|
||||
// _Fail the WebSocket Connection_
|
||||
code_ = close_code::bad_payload;
|
||||
ev_ = error::failed;
|
||||
result_ = error::bad_frame_payload;
|
||||
goto close;
|
||||
}
|
||||
}
|
||||
@ -698,7 +697,7 @@ operator()(
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
if(! ec)
|
||||
ec = ev_;
|
||||
ec = result_;
|
||||
if(ec && ec != error::closed)
|
||||
ws_.status_ = status::failed;
|
||||
else
|
||||
@ -1049,12 +1048,17 @@ loop:
|
||||
if(rd_remain_ == 0 && (! rd_fh_.fin || rd_done_))
|
||||
{
|
||||
// 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_
|
||||
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;
|
||||
}
|
||||
auto const bytes_transferred =
|
||||
@ -1121,11 +1125,12 @@ loop:
|
||||
BOOST_ASSERT(! rd_close_);
|
||||
rd_close_ = true;
|
||||
close_reason cr;
|
||||
detail::read_close(cr, b, code);
|
||||
if(code != close_code::none)
|
||||
detail::read_close(cr, b, result);
|
||||
if(result)
|
||||
{
|
||||
// _Fail the WebSocket Connection_
|
||||
do_fail(code, error::failed, ec);
|
||||
do_fail(close_code::protocol_error,
|
||||
result, ec);
|
||||
return bytes_written;
|
||||
}
|
||||
cr_ = cr;
|
||||
@ -1190,10 +1195,8 @@ loop:
|
||||
! rd_utf8_.finish()))
|
||||
{
|
||||
// _Fail the WebSocket Connection_
|
||||
do_fail(
|
||||
close_code::bad_payload,
|
||||
error::failed,
|
||||
ec);
|
||||
do_fail(close_code::bad_payload,
|
||||
error::bad_frame_payload, ec);
|
||||
return bytes_written;
|
||||
}
|
||||
}
|
||||
@ -1227,7 +1230,7 @@ loop:
|
||||
{
|
||||
// _Fail the WebSocket Connection_
|
||||
do_fail(close_code::bad_payload,
|
||||
error::failed, ec);
|
||||
error::bad_frame_payload, ec);
|
||||
return bytes_written;
|
||||
}
|
||||
}
|
||||
@ -1325,7 +1328,7 @@ loop:
|
||||
rd_size_, zs.total_out, rd_msg_max_))
|
||||
{
|
||||
do_fail(close_code::too_big,
|
||||
error::failed, ec);
|
||||
error::message_too_big, ec);
|
||||
return bytes_written;
|
||||
}
|
||||
cb.consume(zs.total_out);
|
||||
@ -1343,7 +1346,7 @@ loop:
|
||||
{
|
||||
// _Fail the WebSocket Connection_
|
||||
do_fail(close_code::bad_payload,
|
||||
error::failed, ec);
|
||||
error::bad_frame_payload, ec);
|
||||
return bytes_written;
|
||||
}
|
||||
}
|
||||
|
@ -26,9 +26,9 @@ is_upgrade(http::header<true,
|
||||
return false;
|
||||
if(req.method() != http::verb::get)
|
||||
return false;
|
||||
if(! http::token_list{req["Connection"]}.exists("upgrade"))
|
||||
if(! http::token_list{req[http::field::connection]}.exists("upgrade"))
|
||||
return false;
|
||||
if(! http::token_list{req["Upgrade"]}.exists("websocket"))
|
||||
if(! http::token_list{req[http::field::upgrade]}.exists("websocket"))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
@ -332,20 +332,15 @@ stream<NextLayer, deflateSupported>::
|
||||
parse_fh(
|
||||
detail::frame_header& fh,
|
||||
DynamicBuffer& b,
|
||||
close_code& code)
|
||||
error_code& ec)
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
auto const err =
|
||||
[&](close_code cv)
|
||||
{
|
||||
code = cv;
|
||||
return false;
|
||||
};
|
||||
if(buffer_size(b.data()) < 2)
|
||||
{
|
||||
code = close_code::none;
|
||||
// need more bytes
|
||||
ec.assign(0, ec.category());
|
||||
return false;
|
||||
}
|
||||
buffers_suffix<typename
|
||||
@ -368,7 +363,8 @@ parse_fh(
|
||||
need += 4;
|
||||
if(buffer_size(cb) < need)
|
||||
{
|
||||
code = close_code::none;
|
||||
// need more bytes
|
||||
ec.assign(0, ec.category());
|
||||
return false;
|
||||
}
|
||||
fh.op = static_cast<
|
||||
@ -385,13 +381,15 @@ parse_fh(
|
||||
if(rd_cont_)
|
||||
{
|
||||
// new data frame when continuation expected
|
||||
return err(close_code::protocol_error);
|
||||
ec = error::bad_data_frame;
|
||||
return false;
|
||||
}
|
||||
if(fh.rsv2 || fh.rsv3 ||
|
||||
! this->rd_deflated(fh.rsv1))
|
||||
{
|
||||
// reserved bits not cleared
|
||||
return err(close_code::protocol_error);
|
||||
ec = error::bad_reserved_bits;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -399,12 +397,14 @@ parse_fh(
|
||||
if(! rd_cont_)
|
||||
{
|
||||
// continuation without an active message
|
||||
return err(close_code::protocol_error);
|
||||
ec = error::bad_continuation;
|
||||
return false;
|
||||
}
|
||||
if(fh.rsv1 || fh.rsv2 || fh.rsv3)
|
||||
{
|
||||
// reserved bits not cleared
|
||||
return err(close_code::protocol_error);
|
||||
ec = error::bad_reserved_bits;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -412,31 +412,41 @@ parse_fh(
|
||||
if(detail::is_reserved(fh.op))
|
||||
{
|
||||
// reserved opcode
|
||||
return err(close_code::protocol_error);
|
||||
ec = error::bad_opcode;
|
||||
return false;
|
||||
}
|
||||
if(! fh.fin)
|
||||
{
|
||||
// fragmented control message
|
||||
return err(close_code::protocol_error);
|
||||
ec = error::bad_control_fragment;
|
||||
return false;
|
||||
}
|
||||
if(fh.len > 125)
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
// reserved bits not cleared
|
||||
return err(close_code::protocol_error);
|
||||
ec = error::bad_reserved_bits;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
// unmasked frame from client
|
||||
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)
|
||||
return err(close_code::protocol_error);
|
||||
{
|
||||
// masked frame from server
|
||||
ec = error::bad_masked_frame;
|
||||
return false;
|
||||
}
|
||||
if(detail::is_control(fh.op) &&
|
||||
buffer_size(cb) < need + fh.len)
|
||||
{
|
||||
@ -452,9 +462,12 @@ parse_fh(
|
||||
BOOST_ASSERT(buffer_size(cb) >= sizeof(tmp));
|
||||
cb.consume(buffer_copy(buffer(tmp), cb));
|
||||
fh.len = detail::big_uint16_to_native(&tmp[0]);
|
||||
// length not canonical
|
||||
if(fh.len < 126)
|
||||
return err(close_code::protocol_error);
|
||||
{
|
||||
// length not canonical
|
||||
ec = error::bad_size;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 127:
|
||||
@ -463,9 +476,12 @@ parse_fh(
|
||||
BOOST_ASSERT(buffer_size(cb) >= sizeof(tmp));
|
||||
cb.consume(buffer_copy(buffer(tmp), cb));
|
||||
fh.len = detail::big_uint64_to_native(&tmp[0]);
|
||||
// length not canonical
|
||||
if(fh.len < 65536)
|
||||
return err(close_code::protocol_error);
|
||||
{
|
||||
// length not canonical
|
||||
ec = error::bad_size;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -493,19 +509,27 @@ parse_fh(
|
||||
{
|
||||
if(rd_size_ > (std::numeric_limits<
|
||||
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(rd_msg_max_ && beast::detail::sum_exceeds(
|
||||
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_remain_ = fh.len;
|
||||
}
|
||||
b.consume(b.size() - buffer_size(cb));
|
||||
code = close_code::none;
|
||||
ec.assign(0, ec.category());
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -652,7 +676,8 @@ stream<NextLayer, deflateSupported>::
|
||||
build_response(
|
||||
http::request<Body,
|
||||
http::basic_fields<Allocator>> const& req,
|
||||
Decorator const& decorator)
|
||||
Decorator const& decorator,
|
||||
error_code& result)
|
||||
{
|
||||
auto const decorate =
|
||||
[&decorator](response_type& res)
|
||||
@ -666,40 +691,58 @@ build_response(
|
||||
}
|
||||
};
|
||||
auto err =
|
||||
[&](std::string const& text)
|
||||
[&](error e)
|
||||
{
|
||||
result = e;
|
||||
response_type res;
|
||||
res.version(req.version());
|
||||
res.result(http::status::bad_request);
|
||||
res.body() = text;
|
||||
res.body() = result.message();
|
||||
res.prepare_payload();
|
||||
decorate(res);
|
||||
return res;
|
||||
};
|
||||
if(req.version() < 11)
|
||||
return err("HTTP version 1.1 required");
|
||||
if(req.version() != 11)
|
||||
return err(error::bad_http_version);
|
||||
if(req.method() != http::verb::get)
|
||||
return err("Wrong method");
|
||||
if(! is_upgrade(req))
|
||||
return err("Expected Upgrade request");
|
||||
return err(error::bad_method);
|
||||
if(! req.count(http::field::host))
|
||||
return err("Missing 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");
|
||||
return err(error::no_host);
|
||||
{
|
||||
auto const version =
|
||||
req[http::field::sec_websocket_version];
|
||||
if(version.empty())
|
||||
return err("Missing Sec-WebSocket-Version");
|
||||
if(version != "13")
|
||||
auto const it = req.find(http::field::connection);
|
||||
if(it == req.end())
|
||||
return err(error::no_connection);
|
||||
if(! http::token_list{it->value()}.exists("upgrade"))
|
||||
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;
|
||||
res.result(http::status::upgrade_required);
|
||||
res.version(req.version());
|
||||
res.set(http::field::sec_websocket_version, "13");
|
||||
result = error::bad_sec_version;
|
||||
res.body() = result.message();
|
||||
res.prepare_payload();
|
||||
decorate(res);
|
||||
return res;
|
||||
@ -707,7 +750,6 @@ build_response(
|
||||
}
|
||||
|
||||
response_type res;
|
||||
build_response_pmd(res, req, is_deflate_supported{});
|
||||
res.result(http::status::switching_protocols);
|
||||
res.version(req.version());
|
||||
res.set(http::field::upgrade, "websocket");
|
||||
@ -717,7 +759,9 @@ build_response(
|
||||
detail::make_sec_ws_accept(acc, key);
|
||||
res.set(http::field::sec_websocket_accept, acc);
|
||||
}
|
||||
build_response_pmd(res, req, is_deflate_supported{});
|
||||
decorate(res);
|
||||
result = {};
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -747,30 +791,39 @@ on_response(
|
||||
detail::sec_ws_key_type const& key,
|
||||
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)
|
||||
return false;
|
||||
if(res.result() != http::status::switching_protocols)
|
||||
return false;
|
||||
if(! http::token_list{res[http::field::connection]}.exists("upgrade"))
|
||||
return false;
|
||||
if(! http::token_list{res[http::field::upgrade]}.exists("websocket"))
|
||||
return false;
|
||||
if(res.count(http::field::sec_websocket_accept) != 1)
|
||||
return false;
|
||||
auto const it = res.find(http::field::connection);
|
||||
if(it == res.end())
|
||||
return err(error::no_connection);
|
||||
if(! http::token_list{it->value()}.exists("upgrade"))
|
||||
return err(error::no_connection_upgrade);
|
||||
}
|
||||
{
|
||||
auto const it = res.find(http::field::upgrade);
|
||||
if(it == res.end())
|
||||
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::make_sec_ws_accept(acc, key);
|
||||
if(acc.compare(
|
||||
res[http::field::sec_websocket_accept]) != 0)
|
||||
return false;
|
||||
return true;
|
||||
}();
|
||||
if(! success)
|
||||
{
|
||||
ec = error::handshake_failed;
|
||||
return;
|
||||
if(acc.compare(it->value()) != 0)
|
||||
return err(error::bad_sec_accept);
|
||||
}
|
||||
|
||||
ec.assign(0, ec.category());
|
||||
on_response_pmd(res, is_deflate_supported{});
|
||||
open(role_type::client);
|
||||
|
@ -3446,8 +3446,10 @@ private:
|
||||
|
||||
template<class DynamicBuffer>
|
||||
bool
|
||||
parse_fh(detail::frame_header& fh,
|
||||
DynamicBuffer& b, close_code& code);
|
||||
parse_fh(
|
||||
detail::frame_header& fh,
|
||||
DynamicBuffer& b,
|
||||
error_code& ec);
|
||||
|
||||
template<class DynamicBuffer>
|
||||
void
|
||||
@ -3483,7 +3485,8 @@ private:
|
||||
build_response(
|
||||
http::request<Body,
|
||||
http::basic_fields<Allocator>> const& req,
|
||||
Decorator const& decorator);
|
||||
Decorator const& decorator,
|
||||
error_code& ec);
|
||||
|
||||
template<class Body, class Allocator>
|
||||
void
|
||||
|
@ -406,7 +406,7 @@ public:
|
||||
catch(system_error const& e)
|
||||
{
|
||||
if( e.code() !=
|
||||
websocket::error::handshake_failed &&
|
||||
websocket::error::no_sec_key &&
|
||||
e.code() !=
|
||||
boost::asio::error::eof)
|
||||
throw;
|
||||
@ -483,8 +483,8 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
// wrong version
|
||||
check(error::handshake_failed,
|
||||
// bad version
|
||||
check(error::bad_http_version,
|
||||
"GET / HTTP/1.0\r\n"
|
||||
"Host: localhost:80\r\n"
|
||||
"Upgrade: WebSocket\r\n"
|
||||
@ -493,8 +493,8 @@ public:
|
||||
"Sec-WebSocket-Version: 13\r\n"
|
||||
"\r\n"
|
||||
);
|
||||
// wrong method
|
||||
check(error::handshake_failed,
|
||||
// bad method
|
||||
check(error::bad_method,
|
||||
"POST / HTTP/1.1\r\n"
|
||||
"Host: localhost:80\r\n"
|
||||
"Upgrade: WebSocket\r\n"
|
||||
@ -503,8 +503,8 @@ public:
|
||||
"Sec-WebSocket-Version: 13\r\n"
|
||||
"\r\n"
|
||||
);
|
||||
// missing Host
|
||||
check(error::handshake_failed,
|
||||
// no Host
|
||||
check(error::no_host,
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"Upgrade: WebSocket\r\n"
|
||||
"Connection: keep-alive,upgrade\r\n"
|
||||
@ -512,46 +512,17 @@ public:
|
||||
"Sec-WebSocket-Version: 13\r\n"
|
||||
"\r\n"
|
||||
);
|
||||
// missing Sec-WebSocket-Key
|
||||
check(error::handshake_failed,
|
||||
// no Connection
|
||||
check(error::no_connection,
|
||||
"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,
|
||||
// no Connection upgrade
|
||||
check(error::no_connection_upgrade,
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"Host: localhost:80\r\n"
|
||||
"Upgrade: WebSocket\r\n"
|
||||
@ -560,8 +531,36 @@ public:
|
||||
"Sec-WebSocket-Version: 13\r\n"
|
||||
"\r\n"
|
||||
);
|
||||
// oversize key
|
||||
check(error::handshake_failed,
|
||||
// no Upgrade
|
||||
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"
|
||||
"Host: localhost:80\r\n"
|
||||
"Upgrade: WebSocket\r\n"
|
||||
@ -570,8 +569,27 @@ public:
|
||||
"Sec-WebSocket-Version: 13\r\n"
|
||||
"\r\n"
|
||||
);
|
||||
// bad version
|
||||
check(error::handshake_failed,
|
||||
// no Sec-WebSocket-Version
|
||||
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"
|
||||
"Host: localhost:80\r\n"
|
||||
"Upgrade: WebSocket\r\n"
|
||||
@ -580,15 +598,6 @@ public:
|
||||
"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"
|
||||
|
@ -108,7 +108,7 @@ public:
|
||||
catch(system_error const& se)
|
||||
{
|
||||
BEAST_EXPECTS(
|
||||
se.code() == error::failed,
|
||||
se.code() == error::bad_masked_frame,
|
||||
se.code().message());
|
||||
}
|
||||
}
|
||||
@ -128,7 +128,7 @@ public:
|
||||
catch(system_error const& se)
|
||||
{
|
||||
BEAST_EXPECTS(
|
||||
se.code() == error::failed,
|
||||
se.code() == error::bad_close_size,
|
||||
se.code().message());
|
||||
}
|
||||
}
|
||||
@ -292,7 +292,7 @@ public:
|
||||
ws.async_read(b,
|
||||
[&](error_code ec, std::size_t)
|
||||
{
|
||||
if(ec != error::failed)
|
||||
if(ec != error::bad_control_fragment)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
system_error{ec});
|
||||
BEAST_EXPECT(++count == 1);
|
||||
|
@ -25,32 +25,53 @@ public:
|
||||
auto const ec = make_error_code(e);
|
||||
ec.category().name();
|
||||
BEAST_EXPECT(! ec.message().empty());
|
||||
#if 0
|
||||
BEAST_EXPECT(std::addressof(ec.category()) ==
|
||||
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
|
||||
BEAST_EXPECT(ec != condition::handshake_failed);
|
||||
BEAST_EXPECT(ec != condition::protocol_violation);
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
check(error::closed);
|
||||
check(error::failed);
|
||||
check(error::handshake_failed);
|
||||
check(error::buffer_overflow);
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -137,7 +137,7 @@ public:
|
||||
});
|
||||
|
||||
auto const check =
|
||||
[&](std::string const& s)
|
||||
[&](error e, std::string const& s)
|
||||
{
|
||||
stream<test::stream> ws{ioc_};
|
||||
auto tr = connect(ws.next_layer());
|
||||
@ -150,11 +150,11 @@ public:
|
||||
}
|
||||
catch(system_error const& se)
|
||||
{
|
||||
BEAST_EXPECT(se.code() == error::handshake_failed);
|
||||
BEAST_EXPECTS(se.code() == e, se.what());
|
||||
}
|
||||
};
|
||||
// wrong HTTP version
|
||||
check(
|
||||
// bad HTTP version
|
||||
check(error::bad_http_version,
|
||||
"HTTP/1.0 101 Switching Protocols\r\n"
|
||||
"Server: beast\r\n"
|
||||
"Upgrade: WebSocket\r\n"
|
||||
@ -163,28 +163,17 @@ public:
|
||||
"Sec-WebSocket-Version: 13\r\n"
|
||||
"\r\n"
|
||||
);
|
||||
// wrong status
|
||||
check(
|
||||
"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(
|
||||
// no Connection
|
||||
check(error::no_connection,
|
||||
"HTTP/1.1 101 Switching Protocols\r\n"
|
||||
"Server: beast\r\n"
|
||||
"Upgrade: HTTP/2\r\n"
|
||||
"Connection: upgrade\r\n"
|
||||
"Upgrade: WebSocket\r\n"
|
||||
"Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
|
||||
"Sec-WebSocket-Version: 13\r\n"
|
||||
"\r\n"
|
||||
);
|
||||
// missing connection token
|
||||
check(
|
||||
// no Connection upgrade
|
||||
check(error::no_connection_upgrade,
|
||||
"HTTP/1.1 101 Switching Protocols\r\n"
|
||||
"Server: beast\r\n"
|
||||
"Upgrade: WebSocket\r\n"
|
||||
@ -193,8 +182,27 @@ public:
|
||||
"Sec-WebSocket-Version: 13\r\n"
|
||||
"\r\n"
|
||||
);
|
||||
// missing accept key
|
||||
check(
|
||||
// no Upgrade
|
||||
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"
|
||||
"Server: beast\r\n"
|
||||
"Upgrade: WebSocket\r\n"
|
||||
@ -202,8 +210,8 @@ public:
|
||||
"Sec-WebSocket-Version: 13\r\n"
|
||||
"\r\n"
|
||||
);
|
||||
// wrong accept key
|
||||
check(
|
||||
// bad Sec-WebSocket-Accept
|
||||
check(error::bad_sec_accept,
|
||||
"HTTP/1.1 101 Switching Protocols\r\n"
|
||||
"Server: beast\r\n"
|
||||
"Upgrade: WebSocket\r\n"
|
||||
@ -212,6 +220,16 @@ public:
|
||||
"Sec-WebSocket-Version: 13\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
|
||||
|
@ -218,7 +218,7 @@ public:
|
||||
[&](error_code ec, std::size_t)
|
||||
{
|
||||
++count;
|
||||
if(ec != error::failed)
|
||||
if(ec != error::bad_control_fragment)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
system_error{ec});
|
||||
});
|
||||
|
@ -317,7 +317,7 @@ public:
|
||||
{
|
||||
put(ws.next_layer().buffer(), cbuf(
|
||||
0x88, 0x02, 0x03, 0xed));
|
||||
doFailTest(w, ws, error::failed);
|
||||
doFailTest(w, ws, error::bad_close_code);
|
||||
});
|
||||
|
||||
// message size above 2^64
|
||||
@ -337,7 +337,7 @@ public:
|
||||
{
|
||||
ws.read_message_max(1);
|
||||
w.write(ws, sbuf("**"));
|
||||
doFailTest(w, ws, error::failed);
|
||||
doFailTest(w, ws, error::message_too_big);
|
||||
});
|
||||
|
||||
// bad utf8
|
||||
@ -346,7 +346,7 @@ public:
|
||||
{
|
||||
put(ws.next_layer().buffer(), cbuf(
|
||||
0x81, 0x06, 0x03, 0xea, 0xf0, 0x28, 0x8c, 0xbc));
|
||||
doFailTest(w, ws, error::failed);
|
||||
doFailTest(w, ws, error::bad_frame_payload);
|
||||
});
|
||||
|
||||
// incomplete utf8
|
||||
@ -378,7 +378,7 @@ public:
|
||||
}
|
||||
catch(system_error const& se)
|
||||
{
|
||||
if(se.code() != error::failed)
|
||||
if(se.code() != error::bad_frame_payload)
|
||||
throw;
|
||||
}
|
||||
});
|
||||
@ -409,15 +409,15 @@ public:
|
||||
};
|
||||
|
||||
// payload length 1
|
||||
check(error::failed,
|
||||
check(error::bad_close_size,
|
||||
"\x88\x01\x01");
|
||||
|
||||
// invalid close code 1005
|
||||
check(error::failed,
|
||||
check(error::bad_close_code,
|
||||
"\x88\x02\x03\xed");
|
||||
|
||||
// invalid utf8
|
||||
check(error::failed,
|
||||
check(error::bad_close_payload,
|
||||
"\x88\x06\xfc\x15\x0f\xd7\x73\x43");
|
||||
|
||||
// good utf8
|
||||
@ -446,7 +446,7 @@ public:
|
||||
std::string const s = std::string(128, '*');
|
||||
w.write(ws, buffer(s));
|
||||
ws.read_message_max(32);
|
||||
doFailTest(w, ws, error::failed);
|
||||
doFailTest(w, ws, error::message_too_big);
|
||||
});
|
||||
|
||||
// invalid inflate block
|
||||
@ -656,15 +656,15 @@ public:
|
||||
};
|
||||
|
||||
// payload length 1
|
||||
check(error::failed,
|
||||
check(error::bad_close_size,
|
||||
"\x88\x01\x01");
|
||||
|
||||
// invalid close code 1005
|
||||
check(error::failed,
|
||||
check(error::bad_close_code,
|
||||
"\x88\x02\x03\xed");
|
||||
|
||||
// invalid utf8
|
||||
check(error::failed,
|
||||
check(error::bad_close_payload,
|
||||
"\x88\x06\xfc\x15\x0f\xd7\x73\x43");
|
||||
|
||||
// good utf8
|
||||
|
Reference in New Issue
Block a user