mirror of
https://github.com/boostorg/beast.git
synced 2025-07-30 21:07:26 +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
|
* 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:
|
||||||
|
@ -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
|
||||||
|
@ -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>
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
|
@ -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"
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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});
|
||||||
});
|
});
|
||||||
|
@ -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
|
||||||
|
Reference in New Issue
Block a user