// Copyright 2024 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/test/embedded_test_server/websocket_message_assembler.h" #include "base/containers/extend.h" #include "base/containers/span.h" #include "base/logging.h" #include "base/numerics/safe_conversions.h" #include "net/base/net_errors.h" namespace net::test_server { WebSocketMessageAssembler::WebSocketMessageAssembler() = default; WebSocketMessageAssembler::~WebSocketMessageAssembler() = default; MessageOrError WebSocketMessageAssembler::HandleFrame( bool is_final, WebSocketFrameHeader::OpCode opcode, base::span payload) { if (state_ == MessageState::kFinished) { Reset(); } switch (opcode) { case WebSocketFrameHeader::kOpCodeText: if (state_ != MessageState::kIdle) { DVLOG(1) << "Unexpected text frame while expecting continuation"; return base::unexpected(ERR_WS_PROTOCOL_ERROR); } is_text_message_ = true; break; case WebSocketFrameHeader::kOpCodeBinary: if (state_ != MessageState::kIdle) { DVLOG(1) << "Unexpected binary frame while expecting continuation"; return base::unexpected(ERR_WS_PROTOCOL_ERROR); } // Explicitly set to indicate binary handling. is_text_message_ = false; break; case WebSocketFrameHeader::kOpCodeContinuation: if (state_ == MessageState::kIdle) { DVLOG(1) << "Unexpected continuation frame in idle state"; return base::unexpected(ERR_WS_PROTOCOL_ERROR); } break; default: DVLOG(1) << "Invalid frame opcode: " << opcode; return base::unexpected(ERR_WS_PROTOCOL_ERROR); } // If it's the final frame and we haven't received previous fragments, return // the current payload directly as the message. This avoids using an internal // buffer, optimizing memory usage by eliminating unnecessary copies. if (is_final && multi_frame_buffer_.empty()) { return Message(is_text_message_, base::as_bytes(payload)); } base::Extend(multi_frame_buffer_, base::as_byte_span(payload)); if (is_final) { Message complete_message(is_text_message_, base::span(multi_frame_buffer_)); state_ = MessageState::kFinished; return complete_message; } // Update the state to expect a continuation frame. state_ = is_text_message_ ? MessageState::kExpectTextContinuation : MessageState::kExpectBinaryContinuation; return base::unexpected(ERR_IO_PENDING); } void WebSocketMessageAssembler::Reset() { multi_frame_buffer_.clear(); state_ = MessageState::kIdle; is_text_message_ = false; } } // namespace net::test_server