diff options
Diffstat (limited to 'chromium/components/cast_channel/cast_framer.cc')
-rw-r--r-- | chromium/components/cast_channel/cast_framer.cc | 174 |
1 files changed, 174 insertions, 0 deletions
diff --git a/chromium/components/cast_channel/cast_framer.cc b/chromium/components/cast_channel/cast_framer.cc new file mode 100644 index 00000000000..9b5d6fe5122 --- /dev/null +++ b/chromium/components/cast_channel/cast_framer.cc @@ -0,0 +1,174 @@ +// 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 "components/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 "components/cast_channel/proto/cast_channel.pb.h" + +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 |