Unverified Commit 4f20d587 authored by Dominik Charousset's avatar Dominik Charousset Committed by GitHub

Merge pull request #1458

Fix autobahn No. 2.5
parents fc6e3818 cc1374ff
...@@ -120,5 +120,6 @@ caf_add_component( ...@@ -120,5 +120,6 @@ caf_add_component(
net.udp_datagram_socket net.udp_datagram_socket
net.web_socket.client net.web_socket.client
net.web_socket.frame net.web_socket.frame
net.web_socket.framing
net.web_socket.handshake net.web_socket.handshake
net.web_socket.server) net.web_socket.server)
...@@ -57,6 +57,10 @@ struct CAF_NET_EXPORT rfc6455 { ...@@ -57,6 +57,10 @@ struct CAF_NET_EXPORT rfc6455 {
uint8_t flags = fin_flag); uint8_t flags = fin_flag);
static ptrdiff_t decode_header(const_byte_span data, header& hdr); static ptrdiff_t decode_header(const_byte_span data, header& hdr);
static constexpr bool is_control_frame(uint8_t opcode) noexcept {
return opcode != text_frame && opcode != binary_frame;
}
}; };
} // namespace caf::detail } // namespace caf::detail
...@@ -84,6 +84,9 @@ ptrdiff_t rfc6455::decode_header(const_byte_span data, header& hdr) { ...@@ -84,6 +84,9 @@ ptrdiff_t rfc6455::decode_header(const_byte_span data, header& hdr) {
bool masked = (byte2 & 0x80) != 0; bool masked = (byte2 & 0x80) != 0;
auto len_field = byte2 & 0x7F; auto len_field = byte2 & 0x7F;
size_t header_length; size_t header_length;
// control frames can only have payload up to 125 bytes
if (rfc6455::is_control_frame(hdr.opcode) && len_field > 125)
return -1;
if (len_field < 126) { if (len_field < 126) {
header_length = 2 + (masked ? 4 : 0); header_length = 2 + (masked ? 4 : 0);
hdr.payload_len = len_field; hdr.payload_len = len_field;
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include "caf/error.hpp" #include "caf/error.hpp"
#include "caf/net/web_socket/status.hpp" #include "caf/net/web_socket/status.hpp"
#include "caf/sec.hpp"
namespace caf::net::web_socket { namespace caf::net::web_socket {
...@@ -19,7 +20,11 @@ void lower_layer::shutdown() { ...@@ -19,7 +20,11 @@ void lower_layer::shutdown() {
} }
void lower_layer::shutdown(const error& reason) { void lower_layer::shutdown(const error& reason) {
shutdown(status::unexpected_condition, to_string(reason)); if (reason.code() == static_cast<uint8_t>(sec::protocol_error)) {
shutdown(status::protocol_error, to_string(reason));
} else {
shutdown(status::unexpected_condition, to_string(reason));
}
} }
} // namespace caf::net::web_socket } // namespace caf::net::web_socket
...@@ -82,7 +82,6 @@ ptrdiff_t mock_stream_transport::handle_input() { ...@@ -82,7 +82,6 @@ ptrdiff_t mock_stream_transport::handle_input() {
if (consumed < 0) { if (consumed < 0) {
// Negative values indicate that the application encountered an // Negative values indicate that the application encountered an
// unrecoverable error. // unrecoverable error.
up->abort(make_error(caf::sec::runtime_error, "consumed < 0"));
return result; return result;
} else if (static_cast<size_t>(consumed) > n) { } else if (static_cast<size_t>(consumed) > n) {
// Must not happen. An application cannot handle more data then we pass // Must not happen. An application cannot handle more data then we pass
......
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#define CAF_SUITE net.web_socket.framing
#include "caf/net/web_socket/framing.hpp"
#include "net-test.hpp"
using namespace caf;
using namespace std::literals;
namespace {
class app_t : public net::web_socket::upper_layer {
public:
static auto make() {
return std::make_unique<app_t>();
}
std::string text_input;
caf::byte_buffer binary_input;
error err;
bool aborted = false;
net::web_socket::lower_layer* down = nullptr;
error start(net::web_socket::lower_layer* ll) override {
down = ll;
return none;
}
void prepare_send() override {
// nop
}
bool done_sending() override {
return true;
}
void abort(const error& reason) override {
aborted = true;
err = reason;
down->shutdown(reason);
}
ptrdiff_t consume_text(std::string_view text) override {
text_input.insert(text_input.end(), text.begin(), text.end());
return static_cast<ptrdiff_t>(text.size());
}
ptrdiff_t consume_binary(byte_span bytes) override {
binary_input.insert(binary_input.end(), bytes.begin(), bytes.end());
return static_cast<ptrdiff_t>(bytes.size());
}
};
struct fixture {
app_t* app;
net::web_socket::framing* uut;
std::unique_ptr<mock_stream_transport> transport;
fixture() {
auto app_layer = app_t::make();
app = app_layer.get();
auto uut_layer
= net::web_socket::framing::make_server(std::move(app_layer));
uut = uut_layer.get();
transport = mock_stream_transport::make(std::move(uut_layer));
CHECK_EQ(transport->start(nullptr), error{});
transport->configure_read(caf::net::receive_policy::up_to(2048u));
}
byte_buffer make_test_data(size_t requested_size) {
return byte_buffer{requested_size, std::byte{0xFF}};
}
};
} // namespace
BEGIN_FIXTURE_SCOPE(fixture)
SCENARIO("the client sends a ping and receives a pong response") {
GIVEN("a valid WebSocket connection") {
std::vector<std::byte> ping_frame;
std::vector<std::byte> pong_frame;
WHEN("the client sends a ping with no data") {
auto data = make_test_data(0);
detail::rfc6455::assemble_frame(detail::rfc6455::ping, 0x0, data,
ping_frame);
transport->push(ping_frame);
CHECK_EQ(transport->handle_input(),
static_cast<ptrdiff_t>(ping_frame.size()));
THEN("the server responds with an empty pong") {
detail::rfc6455::assemble_frame(detail::rfc6455::pong, 0x0, data,
pong_frame);
CHECK_EQ(transport->output_buffer(), pong_frame);
}
}
transport->output_buffer().clear();
WHEN("the client sends a ping with some data") {
auto data = make_test_data(40);
detail::rfc6455::assemble_frame(detail::rfc6455::ping, 0x0, data,
ping_frame);
transport->push(ping_frame);
CHECK_EQ(transport->handle_input(),
static_cast<ptrdiff_t>(ping_frame.size()));
THEN("the server echoes the data back to the client") {
detail::rfc6455::assemble_frame(detail::rfc6455::pong, 0x0, data,
pong_frame);
CHECK_EQ(transport->output_buffer(), pong_frame);
}
}
transport->output_buffer().clear();
WHEN("the client sends a ping with the maximum allowed 125 bytes") {
auto data = make_test_data(125);
detail::rfc6455::assemble_frame(detail::rfc6455::ping, 0x0, data,
ping_frame);
transport->push(ping_frame);
CHECK_EQ(transport->handle_input(),
static_cast<ptrdiff_t>(ping_frame.size()));
THEN("the server echoes the data back to the client") {
detail::rfc6455::assemble_frame(detail::rfc6455::pong, 0x0, data,
pong_frame);
CHECK_EQ(transport->output_buffer(), pong_frame);
}
}
}
}
TEST_CASE("calling shutdown with protocol_error sets status in close header") {
uut->shutdown(make_error(sec::protocol_error));
detail::rfc6455::header hdr;
detail::rfc6455::decode_header(transport->output_buffer(), hdr);
CHECK_EQ(hdr.opcode, detail::rfc6455::connection_close);
CHECK(hdr.payload_len >= 2);
auto status = (std::to_integer<int>(transport->output_buffer().at(2)) << 8)
+ std::to_integer<int>(transport->output_buffer().at(3));
CHECK_EQ(status, static_cast<int>(net::web_socket::status::protocol_error));
}
SCENARIO("the client sends an invalid ping that closes the connection") {
GIVEN("a valid WebSocket connection") {
std::vector<std::byte> ping_frame;
WHEN("the client sends a ping with more data than allowed") {
auto data = make_test_data(126);
detail::rfc6455::assemble_frame(detail::rfc6455::ping, 0x0, data,
ping_frame);
transport->push(ping_frame);
THEN("the server aborts the application") {
CHECK_EQ(transport->handle_input(), 0);
CHECK(app->aborted);
CHECK_EQ(app->err, sec::protocol_error);
MESSAGE("Aborted with: " << app->err);
}
AND("the server closes the connection with a protocol error") {
detail::rfc6455::header hdr;
detail::rfc6455::decode_header(transport->output_buffer(), hdr);
MESSAGE("Buffer: " << transport->output_buffer());
CHECK_EQ(hdr.opcode, detail::rfc6455::connection_close);
CHECK(hdr.payload_len >= 2);
auto status = (std::to_integer<int>(transport->output_buffer()[2]) << 8)
+ std::to_integer<int>(transport->output_buffer()[3]);
CHECK_EQ(status,
static_cast<int>(net::web_socket::status::protocol_error));
}
}
}
}
END_FIXTURE_SCOPE()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment