Chromium Code Reviews| Index: extensions/browser/api/cast_channel/cast_socket_framer.cc |
| diff --git a/extensions/browser/api/cast_channel/cast_socket_framer.cc b/extensions/browser/api/cast_channel/cast_socket_framer.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..c415728b2a4ee31c75e043ec072b1a4fbcf4eb77 |
| --- /dev/null |
| +++ b/extensions/browser/api/cast_channel/cast_socket_framer.cc |
| @@ -0,0 +1,177 @@ |
| +// 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_socket_framer.h" |
| + |
| +#include <stdlib.h> |
| + |
| +#include "base/numerics/safe_conversions.h" |
| +#include "base/strings/string_number_conversions.h" |
| +#include "base/sys_byteorder.h" |
| +#include "extensions/browser/api/cast_channel/cast_channel.pb.h" |
| + |
| +namespace extensions { |
| +namespace core_api { |
| +namespace cast_channel { |
| +// Size of a CastSocket header payload. |
| +size_t const kHeaderSizeBytes = sizeof(int32); |
| +// Maximum byte count for a CastSocket message. |
| +size_t const kMaxMessageSizeBytes = 65536; |
| + |
| +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>(kuint32max)); |
| + 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(); |
| + scoped_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 message_size; |
| + memcpy(&message_size, data, kHeaderSizeBytes); |
| + header->message_size = |
| + base::checked_cast<size_t>(base::NetToHost32(message_size)); |
| +} |
| + |
| +// static |
| +size_t MessageFramer::MessageHeader::header_size() { |
| + return kHeaderSizeBytes; |
| +} |
| + |
| +// static |
| +size_t MessageFramer::MessageHeader::max_message_size() { |
| + return kMaxMessageSizeBytes; |
| +} |
| + |
| +std::string MessageFramer::MessageHeader::ToString() { |
| + return "{message_size: " + base::UintToString(message_size) + "}"; |
|
mark a. foltz
2014/09/02 20:27:38
Needs cast for message_size?
Kevin M
2014/09/02 23:15:35
Done.
|
| +} |
| + |
| +// 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 > kMaxMessageSizeBytes) { |
| + 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; |
| + switch (current_element_) { |
| + case HEADER: |
| + bytes_left = kHeaderSizeBytes - message_bytes_received_; |
| + DCHECK_LE(bytes_left, kHeaderSizeBytes); |
| + VLOG(2) << "Bytes needed for header: " << bytes_left; |
| + return bytes_left; |
| + case BODY: |
| + bytes_left = (body_size_ + kHeaderSizeBytes) - message_bytes_received_; |
| + DCHECK_LE(bytes_left, kMaxMessageSizeBytes - kHeaderSizeBytes); |
| + VLOG(2) << "Bytes needed for body: " << bytes_left; |
| + return bytes_left; |
| + default: |
| + NOTREACHED() << "Unhandled packet element type."; |
| + return 0; |
| + } |
| +} |
| + |
| +scoped_ptr<CastMessage> MessageFramer::Ingest(size_t num_bytes, |
| + size_t* message_length, |
| + ChannelError* error) { |
| + DCHECK_EQ(base::checked_cast<int32>(message_bytes_received_), |
| + input_buffer_->offset()); |
| + DCHECK(error); |
| + DCHECK(message_length); |
| + CHECK_LE(num_bytes, BytesRequested()); |
| + |
| + if (error_) { |
| + *error = CHANNEL_ERROR_INVALID_MESSAGE; |
| + return scoped_ptr<CastMessage>(); |
| + } |
| + |
| + message_bytes_received_ += num_bytes; |
| + *error = CHANNEL_ERROR_NONE; |
| + *message_length = 0; |
| + switch (current_element_) { |
| + case HEADER: |
| + if (BytesRequested() == 0) { |
| + MessageHeader header; |
| + MessageHeader::Deserialize(input_buffer_.get()->StartOfBuffer(), |
| + &header); |
| + if (header.message_size > MessageHeader::max_message_size()) { |
| + VLOG(1) << "Error parsing header (message size too large)."; |
| + *error = CHANNEL_ERROR_INVALID_MESSAGE; |
| + error_ = true; |
| + return scoped_ptr<CastMessage>(); |
| + } |
| + current_element_ = BODY; |
| + body_size_ = header.message_size; |
| + } |
| + break; |
| + case BODY: |
| + if (BytesRequested() == 0) { |
| + scoped_ptr<CastMessage> parsed_message(new CastMessage); |
| + if (!parsed_message->ParseFromArray( |
| + input_buffer_->StartOfBuffer() + kHeaderSizeBytes, |
| + body_size_)) { |
| + VLOG(1) << "Error parsing packet body."; |
| + *error = CHANNEL_ERROR_INVALID_MESSAGE; |
| + error_ = true; |
| + return scoped_ptr<CastMessage>(); |
| + } |
| + *message_length = body_size_; |
| + Reset(); |
| + return parsed_message.Pass(); |
| + } |
| + break; |
| + default: |
| + NOTREACHED() << "Unhandled packet element type."; |
| + return scoped_ptr<CastMessage>(); |
| + } |
| + |
| + input_buffer_->set_offset(message_bytes_received_); |
| + return scoped_ptr<CastMessage>(); |
| +} |
| + |
| +void MessageFramer::Reset() { |
| + current_element_ = HEADER; |
| + message_bytes_received_ = 0; |
| + body_size_ = 0; |
| + input_buffer_->set_offset(0); |
| +} |
| + |
| +} // namespace cast_channel |
| +} // namespace core_api |
| +} // namespace extensions |