// Copyright (c) 2013 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_buffer.h" #include <cstring> #include "base/callback.h" #include "base/logging.h" #include "net/base/io_buffer.h" #include "net/spdy/spdy_protocol.h" namespace net { namespace { // Bound on largest frame any SPDY version has allowed. const size_t kMaxSpdyFrameSize = 0x00ffffff; // Makes a SpdyFrame with |size| bytes of data copied from // |data|. |data| must be non-NULL and |size| must be positive. scoped_ptr<SpdyFrame> MakeSpdyFrame(const char* data, size_t size) { DCHECK(data); CHECK_GT(size, 0u); CHECK_LE(size, kMaxSpdyFrameSize); scoped_ptr<char[]> frame_data(new char[size]); std::memcpy(frame_data.get(), data, size); scoped_ptr<SpdyFrame> frame( new SpdyFrame(frame_data.release(), size, true /* owns_buffer */)); return frame.Pass(); } } // namespace // This class is an IOBuffer implementation that simply holds a // reference to a SharedFrame object and a fixed offset. Used by // SpdyBuffer::GetIOBufferForRemainingData(). class SpdyBuffer::SharedFrameIOBuffer : public IOBuffer { public: SharedFrameIOBuffer(const scoped_refptr<SharedFrame>& shared_frame, size_t offset) : IOBuffer(shared_frame->data->data() + offset), shared_frame_(shared_frame), offset_(offset) {} private: virtual ~SharedFrameIOBuffer() { // Prevent ~IOBuffer() from trying to delete |data_|. data_ = NULL; } const scoped_refptr<SharedFrame> shared_frame_; const size_t offset_; DISALLOW_COPY_AND_ASSIGN(SharedFrameIOBuffer); }; SpdyBuffer::SpdyBuffer(scoped_ptr<SpdyFrame> frame) : shared_frame_(new SharedFrame()), offset_(0) { shared_frame_->data = frame.Pass(); } // The given data may not be strictly a SPDY frame; we (ab)use // |frame_| just as a container. SpdyBuffer::SpdyBuffer(const char* data, size_t size) : shared_frame_(new SharedFrame()), offset_(0) { CHECK_GT(size, 0u); CHECK_LE(size, kMaxSpdyFrameSize); shared_frame_->data = MakeSpdyFrame(data, size); } SpdyBuffer::~SpdyBuffer() { if (GetRemainingSize() > 0) ConsumeHelper(GetRemainingSize(), DISCARD); } const char* SpdyBuffer::GetRemainingData() const { return shared_frame_->data->data() + offset_; } size_t SpdyBuffer::GetRemainingSize() const { return shared_frame_->data->size() - offset_; } void SpdyBuffer::AddConsumeCallback(const ConsumeCallback& consume_callback) { consume_callbacks_.push_back(consume_callback); } void SpdyBuffer::Consume(size_t consume_size) { ConsumeHelper(consume_size, CONSUME); }; IOBuffer* SpdyBuffer::GetIOBufferForRemainingData() { return new SharedFrameIOBuffer(shared_frame_, offset_); } void SpdyBuffer::ConsumeHelper(size_t consume_size, ConsumeSource consume_source) { DCHECK_GE(consume_size, 1u); DCHECK_LE(consume_size, GetRemainingSize()); offset_ += consume_size; for (std::vector<ConsumeCallback>::const_iterator it = consume_callbacks_.begin(); it != consume_callbacks_.end(); ++it) { it->Run(consume_size, consume_source); } }; } // namespace net