| Index: extensions/browser/api/cast_channel/cast_framer.cc
|
| diff --git a/extensions/browser/api/cast_channel/cast_framer.cc b/extensions/browser/api/cast_channel/cast_framer.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..0bd744fa17f653d5066550873fd91b7f6f22a748
|
| --- /dev/null
|
| +++ b/extensions/browser/api/cast_channel/cast_framer.cc
|
| @@ -0,0 +1,181 @@
|
| +// Copyright 2014 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "extensions/browser/api/cast_channel/cast_framer.h"
|
| +
|
| +#include <stdlib.h>
|
| +
|
| +#include <limits>
|
| +
|
| +#include "base/memory/free_deleter.h"
|
| +#include "base/numerics/safe_conversions.h"
|
| +#include "base/strings/string_number_conversions.h"
|
| +#include "base/sys_byteorder.h"
|
| +#include "extensions/common/api/cast_channel/cast_channel.pb.h"
|
| +
|
| +namespace extensions {
|
| +namespace api {
|
| +namespace cast_channel {
|
| +MessageFramer::MessageFramer(scoped_refptr<net::GrowableIOBuffer> input_buffer)
|
| + : input_buffer_(input_buffer), error_(false) {
|
| + Reset();
|
| +}
|
| +
|
| +MessageFramer::~MessageFramer() {
|
| +}
|
| +
|
| +MessageFramer::MessageHeader::MessageHeader() : message_size(0) {
|
| +}
|
| +
|
| +void MessageFramer::MessageHeader::SetMessageSize(size_t size) {
|
| + DCHECK_LT(size, static_cast<size_t>(std::numeric_limits<uint32_t>::max()));
|
| + DCHECK_GT(size, 0U);
|
| + message_size = size;
|
| +}
|
| +
|
| +// TODO(mfoltz): Investigate replacing header serialization with base::Pickle,
|
| +// if bit-for-bit compatible.
|
| +void MessageFramer::MessageHeader::PrependToString(std::string* str) {
|
| + MessageHeader output = *this;
|
| + output.message_size = base::HostToNet32(message_size);
|
| + size_t header_size = MessageHeader::header_size();
|
| + std::unique_ptr<char, base::FreeDeleter> char_array(
|
| + static_cast<char*>(malloc(header_size)));
|
| + memcpy(char_array.get(), &output, header_size);
|
| + str->insert(0, char_array.get(), header_size);
|
| +}
|
| +
|
| +// TODO(mfoltz): Investigate replacing header deserialization with base::Pickle,
|
| +// if bit-for-bit compatible.
|
| +void MessageFramer::MessageHeader::Deserialize(char* data,
|
| + MessageHeader* header) {
|
| + uint32_t message_size;
|
| + memcpy(&message_size, data, header_size());
|
| + header->message_size =
|
| + base::checked_cast<size_t>(base::NetToHost32(message_size));
|
| +}
|
| +
|
| +// static
|
| +size_t MessageFramer::MessageHeader::header_size() {
|
| + return sizeof(uint32_t);
|
| +}
|
| +
|
| +// static
|
| +size_t MessageFramer::MessageHeader::max_message_size() {
|
| + return 65535;
|
| +}
|
| +
|
| +std::string MessageFramer::MessageHeader::ToString() {
|
| + return "{message_size: " +
|
| + base::UintToString(static_cast<uint32_t>(message_size)) + "}";
|
| +}
|
| +
|
| +// static
|
| +bool MessageFramer::Serialize(const CastMessage& message_proto,
|
| + std::string* message_data) {
|
| + DCHECK(message_data);
|
| + message_proto.SerializeToString(message_data);
|
| + size_t message_size = message_data->size();
|
| + if (message_size > MessageHeader::max_message_size()) {
|
| + message_data->clear();
|
| + return false;
|
| + }
|
| + MessageHeader header;
|
| + header.SetMessageSize(message_size);
|
| + header.PrependToString(message_data);
|
| + return true;
|
| +}
|
| +
|
| +size_t MessageFramer::BytesRequested() {
|
| + size_t bytes_left;
|
| + if (error_) {
|
| + return 0;
|
| + }
|
| +
|
| + switch (current_element_) {
|
| + case HEADER:
|
| + bytes_left = MessageHeader::header_size() - message_bytes_received_;
|
| + DCHECK_LE(bytes_left, MessageHeader::header_size());
|
| + VLOG(2) << "Bytes needed for header: " << bytes_left;
|
| + return bytes_left;
|
| + case BODY:
|
| + bytes_left =
|
| + (body_size_ + MessageHeader::header_size()) - message_bytes_received_;
|
| + DCHECK_LE(
|
| + bytes_left,
|
| + MessageHeader::max_message_size() - MessageHeader::header_size());
|
| + VLOG(2) << "Bytes needed for body: " << bytes_left;
|
| + return bytes_left;
|
| + default:
|
| + NOTREACHED() << "Unhandled packet element type.";
|
| + return 0;
|
| + }
|
| +}
|
| +
|
| +std::unique_ptr<CastMessage> MessageFramer::Ingest(size_t num_bytes,
|
| + size_t* message_length,
|
| + ChannelError* error) {
|
| + DCHECK(error);
|
| + DCHECK(message_length);
|
| + if (error_) {
|
| + *error = ChannelError::INVALID_MESSAGE;
|
| + return nullptr;
|
| + }
|
| +
|
| + DCHECK_EQ(base::checked_cast<int32_t>(message_bytes_received_),
|
| + input_buffer_->offset());
|
| + CHECK_LE(num_bytes, BytesRequested());
|
| + message_bytes_received_ += num_bytes;
|
| + *error = ChannelError::NONE;
|
| + *message_length = 0;
|
| + switch (current_element_) {
|
| + case HEADER:
|
| + if (BytesRequested() == 0) {
|
| + MessageHeader header;
|
| + MessageHeader::Deserialize(input_buffer_->StartOfBuffer(), &header);
|
| + if (header.message_size > MessageHeader::max_message_size()) {
|
| + VLOG(1) << "Error parsing header (message size too large).";
|
| + *error = ChannelError::INVALID_MESSAGE;
|
| + error_ = true;
|
| + return nullptr;
|
| + }
|
| + current_element_ = BODY;
|
| + body_size_ = header.message_size;
|
| + }
|
| + break;
|
| + case BODY:
|
| + if (BytesRequested() == 0) {
|
| + std::unique_ptr<CastMessage> parsed_message(new CastMessage);
|
| + if (!parsed_message->ParseFromArray(
|
| + input_buffer_->StartOfBuffer() + MessageHeader::header_size(),
|
| + body_size_)) {
|
| + VLOG(1) << "Error parsing packet body.";
|
| + *error = ChannelError::INVALID_MESSAGE;
|
| + error_ = true;
|
| + return nullptr;
|
| + }
|
| + *message_length = body_size_;
|
| + Reset();
|
| + return parsed_message;
|
| + }
|
| + break;
|
| + default:
|
| + NOTREACHED() << "Unhandled packet element type.";
|
| + return nullptr;
|
| + }
|
| +
|
| + input_buffer_->set_offset(message_bytes_received_);
|
| + return nullptr;
|
| +}
|
| +
|
| +void MessageFramer::Reset() {
|
| + current_element_ = HEADER;
|
| + message_bytes_received_ = 0;
|
| + body_size_ = 0;
|
| + input_buffer_->set_offset(0);
|
| +}
|
| +
|
| +} // namespace cast_channel
|
| +} // namespace api
|
| +} // namespace extensions
|
|
|