// Copyright (c) 2012 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 "net/spdy/spdy_frame_builder.h" #include #include "base/logging.h" #include "net/spdy/spdy_framer.h" #include "net/spdy/spdy_protocol.h" namespace net { namespace { // A special structure for the 8 bit flags and 24 bit length fields. union FlagsAndLength { uint8 flags_[4]; // 8 bits uint32 length_; // 24 bits }; // Creates a FlagsAndLength. FlagsAndLength CreateFlagsAndLength(uint8 flags, size_t length) { DCHECK_EQ(0u, length & ~static_cast(kLengthMask)); FlagsAndLength flags_length; flags_length.length_ = htonl(static_cast(length)); DCHECK_EQ(0, flags & ~kControlFlagsMask); flags_length.flags_[0] = flags; return flags_length; } } // namespace SpdyFrameBuilder::SpdyFrameBuilder(size_t size) : buffer_(new char[size]), capacity_(size), length_(0) { } SpdyFrameBuilder::~SpdyFrameBuilder() { } char* SpdyFrameBuilder::GetWritableBuffer(size_t length) { if (!CanWrite(length)) { return NULL; } return buffer_.get() + length_; } bool SpdyFrameBuilder::Seek(size_t length) { if (!CanWrite(length)) { return false; } length_ += length; return true; } bool SpdyFrameBuilder::WriteControlFrameHeader(const SpdyFramer& framer, SpdyFrameType type, uint8 flags) { DCHECK_GE(type, FIRST_CONTROL_TYPE); DCHECK_LE(type, LAST_CONTROL_TYPE); DCHECK_GT(4, framer.protocol_version()); bool success = true; FlagsAndLength flags_length = CreateFlagsAndLength( flags, capacity_ - framer.GetControlFrameHeaderSize()); success &= WriteUInt16(kControlFlagMask | framer.protocol_version()); success &= WriteUInt16(type); success &= WriteBytes(&flags_length, sizeof(flags_length)); DCHECK_EQ(framer.GetControlFrameHeaderSize(), length()); return success; } bool SpdyFrameBuilder::WriteDataFrameHeader(const SpdyFramer& framer, SpdyStreamId stream_id, SpdyDataFlags flags) { if (framer.protocol_version() >= 4) { return WriteFramePrefix(framer, DATA, flags, stream_id); } DCHECK_EQ(0u, stream_id & ~kStreamIdMask); bool success = true; success &= WriteUInt32(stream_id); size_t length_field = capacity_ - framer.GetDataFrameMinimumSize(); DCHECK_EQ(0u, length_field & ~static_cast(kLengthMask)); FlagsAndLength flags_length; flags_length.length_ = htonl(length_field); DCHECK_EQ(0, flags & ~kDataFlagsMask); flags_length.flags_[0] = flags; success &= WriteBytes(&flags_length, sizeof(flags_length)); DCHECK_EQ(framer.GetDataFrameMinimumSize(), length()); return success; } bool SpdyFrameBuilder::WriteFramePrefix(const SpdyFramer& framer, SpdyFrameType type, uint8 flags, SpdyStreamId stream_id) { DCHECK_LE(DATA, type); DCHECK_GE(LAST_CONTROL_TYPE, type); DCHECK_EQ(0u, stream_id & ~kStreamIdMask); DCHECK_LE(4, framer.protocol_version()); bool success = true; DCHECK_GT(1u<<16, capacity_); // Make sure length fits in 2B. success &= WriteUInt16(capacity_); success &= WriteUInt8(type); success &= WriteUInt8(flags); success &= WriteUInt32(stream_id); DCHECK_EQ(framer.GetDataFrameMinimumSize(), length()); return success; } bool SpdyFrameBuilder::WriteString(const std::string& value) { if (value.size() > 0xffff) { DCHECK(false) << "Tried to write string with length > 16bit."; return false; } if (!WriteUInt16(static_cast(value.size()))) return false; return WriteBytes(value.data(), static_cast(value.size())); } bool SpdyFrameBuilder::WriteStringPiece32(const base::StringPiece& value) { if (!WriteUInt32(value.size())) { return false; } return WriteBytes(value.data(), value.size()); } bool SpdyFrameBuilder::WriteBytes(const void* data, uint32 data_len) { if (!CanWrite(data_len)) { return false; } char* dest = GetWritableBuffer(data_len); memcpy(dest, data, data_len); Seek(data_len); return true; } bool SpdyFrameBuilder::RewriteLength(const SpdyFramer& framer) { if (framer.protocol_version() < 4) { return OverwriteLength(framer, length_ - framer.GetControlFrameHeaderSize()); } else { return OverwriteLength(framer, length_); } } bool SpdyFrameBuilder::OverwriteLength(const SpdyFramer& framer, size_t length) { bool success = false; const size_t old_length = length_; if (framer.protocol_version() < 4) { FlagsAndLength flags_length = CreateFlagsAndLength( 0, // We're not writing over the flags value anyway. length); // Write into the correct location by temporarily faking the offset. length_ = 5; // Offset at which the length field occurs. success = WriteBytes(reinterpret_cast(&flags_length) + 1, sizeof(flags_length) - 1); } else { length_ = 0; success = WriteUInt16(length); } length_ = old_length; return success; } bool SpdyFrameBuilder::CanWrite(size_t length) const { if (length > kLengthMask) { DCHECK(false); return false; } if (length_ + length > capacity_) { DCHECK(false); return false; } return true; } } // namespace net