Commit 4f580d89 authored by Dominik Charousset's avatar Dominik Charousset

Fix handling of fragmented WebSocket frames

(cherry picked from commit 0082cea1)
parent 571c4d19
......@@ -39,6 +39,8 @@ struct CAF_NET_EXPORT rfc6455 {
static constexpr uint8_t pong = 0x0A;
static constexpr uint8_t fin_flag = 0x80;
// -- utility functions ------------------------------------------------------
static void mask_data(uint32_t key, span<char> data);
......@@ -52,7 +54,8 @@ struct CAF_NET_EXPORT rfc6455 {
binary_buffer& out);
static void assemble_frame(uint8_t opcode, uint32_t mask_key,
span<const byte> data, binary_buffer& out);
span<const byte> data, binary_buffer& out,
uint8_t flags = fin_flag);
static ptrdiff_t decode_header(span<const byte> data, header& hdr);
};
......
......@@ -238,7 +238,7 @@ public:
// End of fragmented input.
payload_buf_.insert(payload_buf_.end(), payload.begin(),
payload.end());
if (!handle(down, hdr.opcode, payload_buf_))
if (!handle(down, opcode_, payload_buf_))
return -1;
opcode_ = nil_code;
payload_buf_.clear();
......@@ -254,6 +254,11 @@ public:
return -1;
}
opcode_ = hdr.opcode;
} else if (hdr.opcode != detail::rfc6455::continuation_frame) {
CAF_LOG_DEBUG("expected a continuation frame");
down->abort_reason(make_error(sec::protocol_error, //
"expected a continuation frame"));
return -1;
} else if (payload_buf_.size() + payload_len > max_frame_size) {
// Reject assembled payloads that exceed max_frame_size.
auto err = make_error(sec::runtime_error,
......
......@@ -37,9 +37,10 @@ void rfc6455::assemble_frame(uint32_t mask_key, span<const byte> data,
}
void rfc6455::assemble_frame(uint8_t opcode, uint32_t mask_key,
span<const byte> data, binary_buffer& out) {
// First 8 bits: FIN flag + opcode (we never fragment frames).
out.push_back(byte{static_cast<uint8_t>(0x80 | opcode)});
span<const byte> data, binary_buffer& out,
uint8_t flags) {
// First 8 bits: flags + opcode
out.push_back(byte{static_cast<uint8_t>(flags | opcode)});
// Mask flag + payload length (7 bits, 7+16 bits, or 7+64 bits)
auto mask_bit = byte{static_cast<uint8_t>(mask_key == 0 ? 0x00 : 0x80)};
if (data.size() < 126) {
......
......@@ -74,11 +74,17 @@ struct fixture : host_fixture {
}
void rfc6455_append(uint8_t opcode, span<const byte> bytes,
std::vector<byte>& out) {
std::vector<byte>& out,
uint8_t flags = detail::rfc6455::fin_flag) {
std::vector<byte> payload{bytes.begin(), bytes.end()};
auto key = static_cast<uint32_t>(rng());
detail::rfc6455::mask_data(key, payload);
detail::rfc6455::assemble_frame(opcode, key, payload, out);
detail::rfc6455::assemble_frame(opcode, key, payload, out, flags);
}
void rfc6455_append(uint8_t opcode, std::string_view text, byte_buffer& out,
uint8_t flags = detail::rfc6455::fin_flag) {
rfc6455_append(opcode, as_bytes(make_span(text)), out, flags);
}
void rfc6455_append(span<const byte> bytes, std::vector<byte>& out) {
......@@ -213,4 +219,19 @@ CAF_TEST(data may arrive later) {
CAF_CHECK_EQUAL(app->text_input, "Hello WebSocket!\nBye WebSocket!\n");
}
CAF_TEST(data may arrive fragmented) {
transport->push(opening_handshake);
CHECK_EQ(transport->handle_input(),
static_cast<ptrdiff_t>(opening_handshake.size()));
byte_buffer buf;
rfc6455_append(detail::rfc6455::text_frame, "Hello "sv, buf, 0);
rfc6455_append(detail::rfc6455::continuation_frame, "WebSocket!\n"sv, buf);
rfc6455_append(detail::rfc6455::text_frame, "Bye "sv, buf, 0);
rfc6455_append(detail::rfc6455::continuation_frame, "Web"sv, buf, 0);
rfc6455_append(detail::rfc6455::continuation_frame, "Socket!\n"sv, buf);
transport->push(buf);
CHECK_EQ(transport->handle_input(), static_cast<ptrdiff_t>(buf.size()));
CHECK_EQ(app->text_input, "Hello WebSocket!\nBye WebSocket!\n");
}
CAF_TEST_FIXTURE_SCOPE_END()
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