Commit 5d0b63bc authored by Samir Halilcevic's avatar Samir Halilcevic

Fix unclean exit handshake in WebSocket

parent 493e1077
...@@ -139,6 +139,11 @@ private: ...@@ -139,6 +139,11 @@ private:
template <class T> template <class T>
void ship_frame(std::vector<T>& buf); void ship_frame(std::vector<T>& buf);
void abort_and_shutdown(caf::error reason) {
abort(reason);
shutdown(reason);
}
// -- member variables ------------------------------------------------------- // -- member variables -------------------------------------------------------
/// Points to the transport layer below. /// Points to the transport layer below.
......
...@@ -30,8 +30,8 @@ ptrdiff_t framing::consume(byte_span buffer, byte_span) { ...@@ -30,8 +30,8 @@ ptrdiff_t framing::consume(byte_span buffer, byte_span) {
auto hdr_bytes = detail::rfc6455::decode_header(buffer, hdr); auto hdr_bytes = detail::rfc6455::decode_header(buffer, hdr);
if (hdr_bytes < 0) { if (hdr_bytes < 0) {
CAF_LOG_DEBUG("decoded malformed data: hdr_bytes < 0"); CAF_LOG_DEBUG("decoded malformed data: hdr_bytes < 0");
up_->abort(make_error(sec::protocol_error, abort_and_shutdown(make_error(
"negative header size on WebSocket connection")); sec::protocol_error, "negative header size on WebSocket connection"));
return -1; return -1;
} }
if (hdr_bytes == 0) { if (hdr_bytes == 0) {
...@@ -41,7 +41,8 @@ ptrdiff_t framing::consume(byte_span buffer, byte_span) { ...@@ -41,7 +41,8 @@ ptrdiff_t framing::consume(byte_span buffer, byte_span) {
// Make sure the entire frame (including header) fits into max_frame_size. // Make sure the entire frame (including header) fits into max_frame_size.
if (hdr.payload_len >= (max_frame_size - static_cast<size_t>(hdr_bytes))) { if (hdr.payload_len >= (max_frame_size - static_cast<size_t>(hdr_bytes))) {
CAF_LOG_DEBUG("WebSocket frame too large"); CAF_LOG_DEBUG("WebSocket frame too large");
up_->abort(make_error(sec::protocol_error, "WebSocket frame too large")); abort_and_shutdown(
make_error(sec::protocol_error, "WebSocket frame too large"));
return -1; return -1;
} }
// Wait for more data if necessary. // Wait for more data if necessary.
...@@ -60,24 +61,25 @@ ptrdiff_t framing::consume(byte_span buffer, byte_span) { ...@@ -60,24 +61,25 @@ ptrdiff_t framing::consume(byte_span buffer, byte_span) {
if (opcode_ == nil_code) { if (opcode_ == nil_code) {
// Call upper layer. // Call upper layer.
if (hdr.opcode == detail::rfc6455::connection_close) { if (hdr.opcode == detail::rfc6455::connection_close) {
up_->abort(make_error(sec::connection_closed)); abort_and_shutdown(make_error(sec::connection_closed));
return -1; return -1;
} else if (!handle(hdr.opcode, payload)) { } else if (!handle(hdr.opcode, payload)) {
return -1; return -1;
} }
} else if (hdr.opcode != detail::rfc6455::continuation_frame) { } else if (hdr.opcode != detail::rfc6455::continuation_frame) {
CAF_LOG_DEBUG("expected a WebSocket continuation_frame"); CAF_LOG_DEBUG("expected a WebSocket continuation_frame");
up_->abort(make_error(sec::protocol_error, abort_and_shutdown(make_error(sec::protocol_error,
"expected a WebSocket continuation_frame")); "expected a WebSocket continuation_frame"));
return -1; return -1;
} else if (payload_buf_.size() + payload_len > max_frame_size) { } else if (payload_buf_.size() + payload_len > max_frame_size) {
CAF_LOG_DEBUG("fragmented WebSocket payload exceeds maximum size"); CAF_LOG_DEBUG("fragmented WebSocket payload exceeds maximum size");
up_->abort(make_error(sec::protocol_error, "fragmented WebSocket payload " abort_and_shutdown(make_error(sec::protocol_error,
"fragmented WebSocket payload "
"exceeds maximum size")); "exceeds maximum size"));
return -1; return -1;
} else { } else {
if (hdr.opcode == detail::rfc6455::connection_close) { if (hdr.opcode == detail::rfc6455::connection_close) {
up_->abort(make_error(sec::connection_closed)); abort_and_shutdown(make_error(sec::connection_closed));
return -1; return -1;
} else { } else {
// End of fragmented input. // End of fragmented input.
...@@ -96,7 +98,7 @@ ptrdiff_t framing::consume(byte_span buffer, byte_span) { ...@@ -96,7 +98,7 @@ ptrdiff_t framing::consume(byte_span buffer, byte_span) {
if (hdr.opcode == detail::rfc6455::continuation_frame) { if (hdr.opcode == detail::rfc6455::continuation_frame) {
CAF_LOG_DEBUG("received WebSocket continuation " CAF_LOG_DEBUG("received WebSocket continuation "
"frame without prior opcode"); "frame without prior opcode");
up_->abort(make_error(sec::protocol_error, abort_and_shutdown(make_error(sec::protocol_error,
"received WebSocket continuation " "received WebSocket continuation "
"frame without prior opcode")); "frame without prior opcode"));
return -1; return -1;
...@@ -104,13 +106,14 @@ ptrdiff_t framing::consume(byte_span buffer, byte_span) { ...@@ -104,13 +106,14 @@ ptrdiff_t framing::consume(byte_span buffer, byte_span) {
opcode_ = hdr.opcode; opcode_ = hdr.opcode;
} else if (hdr.opcode != detail::rfc6455::continuation_frame) { } else if (hdr.opcode != detail::rfc6455::continuation_frame) {
CAF_LOG_DEBUG("expected a continuation frame"); CAF_LOG_DEBUG("expected a continuation frame");
up_->abort(make_error(sec::protocol_error, // abort_and_shutdown(make_error(sec::protocol_error, //
"expected a continuation frame")); "expected a continuation frame"));
return -1; return -1;
} else if (payload_buf_.size() + payload_len > max_frame_size) { } else if (payload_buf_.size() + payload_len > max_frame_size) {
// Reject assembled payloads that exceed max_frame_size. // Reject assembled payloads that exceed max_frame_size.
CAF_LOG_DEBUG("fragmented WebSocket payload exceeds maximum size"); CAF_LOG_DEBUG("fragmented WebSocket payload exceeds maximum size");
up_->abort(make_error(sec::protocol_error, "fragmented WebSocket payload " abort_and_shutdown(make_error(sec::protocol_error,
"fragmented WebSocket payload "
"exceeds maximum size")); "exceeds maximum size"));
return -1; return -1;
} }
......
...@@ -20,7 +20,9 @@ void lower_layer::shutdown() { ...@@ -20,7 +20,9 @@ void lower_layer::shutdown() {
} }
void lower_layer::shutdown(const error& reason) { void lower_layer::shutdown(const error& reason) {
if (reason.code() == static_cast<uint8_t>(sec::protocol_error)) { if (reason.code() == static_cast<uint8_t>(sec::connection_closed)) {
shutdown(status::normal_close, to_string(reason));
} else if (reason.code() == static_cast<uint8_t>(sec::protocol_error)) {
shutdown(status::protocol_error, to_string(reason)); shutdown(status::protocol_error, to_string(reason));
} else { } else {
shutdown(status::unexpected_condition, to_string(reason)); shutdown(status::unexpected_condition, to_string(reason));
......
...@@ -136,4 +136,30 @@ SCENARIO("the client sends an invalid ping that closes the connection") { ...@@ -136,4 +136,30 @@ SCENARIO("the client sends an invalid ping that closes the connection") {
} }
} }
SCENARIO("the client closes the connection with a closing handshake") {
GIVEN("a valid WebSocket connection") {
WHEN("the client sends a closing handshake") {
std::vector<std::byte> handshake;
detail::rfc6455::assemble_frame(detail::rfc6455::connection_close, 0x0,
make_test_data(0), handshake);
transport->push(handshake);
}
THEN("the server closes the connection with a closing handshake") {
transport->handle_input();
detail::rfc6455::header hdr;
auto hdr_length
= detail::rfc6455::decode_header(transport->output_buffer(), hdr);
CHECK(app->has_aborted());
CHECK_EQ(app->abort_reason, sec::connection_closed);
CHECK_EQ(hdr_length, 2);
CHECK_EQ(hdr.opcode, detail::rfc6455::connection_close);
CHECK(hdr.fin);
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::normal_close));
}
}
}
END_FIXTURE_SCOPE() 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