diff options
Diffstat (limited to 'chromium/net/spdy/spdy_frame_builder.cc')
-rw-r--r-- | chromium/net/spdy/spdy_frame_builder.cc | 191 |
1 files changed, 191 insertions, 0 deletions
diff --git a/chromium/net/spdy/spdy_frame_builder.cc b/chromium/net/spdy/spdy_frame_builder.cc new file mode 100644 index 00000000000..9e779ff4594 --- /dev/null +++ b/chromium/net/spdy/spdy_frame_builder.cc @@ -0,0 +1,191 @@ +// 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 <limits> + +#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<size_t>(kLengthMask)); + FlagsAndLength flags_length; + flags_length.length_ = htonl(static_cast<uint32>(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<size_t>(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<int>(value.size()))) + return false; + + return WriteBytes(value.data(), static_cast<uint16>(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<char*>(&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 |