1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
|
// 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<const char> 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
|