diff options
author | rtenneti@chromium.org <rtenneti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-12-25 08:37:30 +0000 |
---|---|---|
committer | rtenneti@chromium.org <rtenneti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-12-25 08:37:30 +0000 |
commit | 3f2aac72c5766e101a619d66aee526b8251653b6 (patch) | |
tree | f465923c36c678d30c66800e4db90be55fc28d4f | |
parent | 01d65174be5027747da08803ff1acc865b76ec03 (diff) | |
download | chromium_src-3f2aac72c5766e101a619d66aee526b8251653b6.zip chromium_src-3f2aac72c5766e101a619d66aee526b8251653b6.tar.gz chromium_src-3f2aac72c5766e101a619d66aee526b8251653b6.tar.bz2 |
Changes to read SPDY headers incrementally.
Added buffered_spdy_framer class to support buffering of spdy control
header frames. It calls SpdySession's OnSyn, OnSynReply and OnHeaders
methods after receiving all the control frame header data.
spdy_framer*.* - integrated changes from the server.
Changed OnControlFrameHeaderData to send control frame
instead of stream_id.
Merged changes from the following issue into this issue.
http://codereview.chromium.org/8953012/
R=willchan
TEST=network unit tests
Review URL: http://codereview.chromium.org/8980015
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@115769 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | net/net.gyp | 3 | ||||
-rw-r--r-- | net/spdy/buffered_spdy_framer.cc | 161 | ||||
-rw-r--r-- | net/spdy/buffered_spdy_framer.h | 106 | ||||
-rwxr-xr-x | net/spdy/buffered_spdy_framer_unittest.cc | 260 | ||||
-rw-r--r-- | net/spdy/spdy_framer.cc | 157 | ||||
-rw-r--r-- | net/spdy/spdy_framer.h | 29 | ||||
-rw-r--r-- | net/spdy/spdy_framer_test.cc | 85 | ||||
-rw-r--r-- | net/spdy/spdy_network_transaction_unittest.cc | 3 | ||||
-rw-r--r-- | net/spdy/spdy_session.cc | 88 | ||||
-rw-r--r-- | net/spdy/spdy_session.h | 30 | ||||
-rw-r--r-- | net/tools/flip_server/spdy_interface.cc | 7 | ||||
-rw-r--r-- | net/tools/flip_server/spdy_interface.h | 7 |
12 files changed, 774 insertions, 162 deletions
diff --git a/net/net.gyp b/net/net.gyp index af5aa91..d6c510c 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -624,6 +624,8 @@ 'socket_stream/socket_stream_job_manager.h', 'socket_stream/socket_stream_metrics.cc', 'socket_stream/socket_stream_metrics.h', + 'spdy/buffered_spdy_framer.cc', + 'spdy/buffered_spdy_framer.h', 'spdy/spdy_bitmasks.h', 'spdy/spdy_frame_builder.cc', 'spdy/spdy_frame_builder.h', @@ -1158,6 +1160,7 @@ 'socket/web_socket_server_socket_unittest.cc', 'socket_stream/socket_stream_metrics_unittest.cc', 'socket_stream/socket_stream_unittest.cc', + 'spdy/buffered_spdy_framer_unittest.cc', 'spdy/spdy_framer_test.cc', 'spdy/spdy_http_stream_unittest.cc', 'spdy/spdy_network_transaction_unittest.cc', diff --git a/net/spdy/buffered_spdy_framer.cc b/net/spdy/buffered_spdy_framer.cc new file mode 100644 index 0000000..59ab183 --- /dev/null +++ b/net/spdy/buffered_spdy_framer.cc @@ -0,0 +1,161 @@ +// Copyright (c) 2011 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/buffered_spdy_framer.h" + +#include "base/logging.h" + +namespace spdy { + +BufferedSpdyFramer::BufferedSpdyFramer() + : header_buffer_used_(0), + header_buffer_valid_(false), + header_stream_id_(SpdyFramer::kInvalidStream) { +} + +BufferedSpdyFramer::~BufferedSpdyFramer() { +} + +void BufferedSpdyFramer::set_visitor( + BufferedSpdyFramerVisitorInterface* visitor) { + visitor_ = visitor; + spdy_framer_.set_visitor(visitor); +} + +void BufferedSpdyFramer::OnControl(const SpdyControlFrame* frame) { + switch (frame->type()) { + case SYN_STREAM: + case SYN_REPLY: + case HEADERS: + InitHeaderStreaming(frame); + break; + default: + DCHECK(false); // Error! + break; + } +} + +bool BufferedSpdyFramer::OnControlFrameHeaderData( + const SpdyControlFrame* control_frame, + const char* header_data, + size_t len) { + SpdyStreamId stream_id = + SpdyFramer::GetControlFrameStreamId(control_frame); + CHECK_EQ(header_stream_id_, stream_id); + + if (len == 0) { + // Indicates end-of-header-block. + CHECK(header_buffer_valid_); + + const linked_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock); + bool parsed_headers = SpdyFramer::ParseHeaderBlockInBuffer( + header_buffer_, header_buffer_used_, headers.get()); + if (!parsed_headers) { + LOG(WARNING) << "Could not parse Spdy Control Frame Header."; + return false; + } + switch (control_frame->type()) { + case SYN_STREAM: + visitor_->OnSyn( + *reinterpret_cast<const SpdySynStreamControlFrame*>( + control_frame), headers); + break; + case SYN_REPLY: + visitor_->OnSynReply( + *reinterpret_cast<const SpdySynReplyControlFrame*>( + control_frame), headers); + break; + case HEADERS: + visitor_->OnHeaders( + *reinterpret_cast<const SpdyHeadersControlFrame*>( + control_frame), headers); + break; + default: + DCHECK(false); // Error! + break; + } + return true; + } + + const size_t available = kHeaderBufferSize - header_buffer_used_; + if (len > available) { + header_buffer_valid_ = false; + return false; + } + memcpy(header_buffer_ + header_buffer_used_, header_data, len); + header_buffer_used_ += len; + return true; +} + +void BufferedSpdyFramer::OnDataFrameHeader(const SpdyDataFrame* frame) { + header_stream_id_ = frame->stream_id(); +} + +size_t BufferedSpdyFramer::ProcessInput(const char* data, size_t len) { + return spdy_framer_.ProcessInput(data, len); +} + +void BufferedSpdyFramer::Reset() { + spdy_framer_.Reset(); +} + +SpdyFramer::SpdyError BufferedSpdyFramer::error_code() const { + return spdy_framer_.error_code(); +} + +SpdyFramer::SpdyState BufferedSpdyFramer::state() const { + return spdy_framer_.state(); +} + +SpdySynStreamControlFrame* BufferedSpdyFramer::CreateSynStream( + SpdyStreamId stream_id, + SpdyStreamId associated_stream_id, + int priority, + SpdyControlFlags flags, + bool compressed, + const SpdyHeaderBlock* headers) { + return spdy_framer_.CreateSynStream( + stream_id, associated_stream_id, priority, flags, compressed, headers); +} + +SpdySynReplyControlFrame* BufferedSpdyFramer::CreateSynReply( + SpdyStreamId stream_id, + SpdyControlFlags flags, + bool compressed, + const SpdyHeaderBlock* headers) { + return spdy_framer_.CreateSynReply(stream_id, flags, compressed, headers); +} + +SpdyHeadersControlFrame* BufferedSpdyFramer::CreateHeaders( + SpdyStreamId stream_id, + SpdyControlFlags flags, + bool compressed, + const SpdyHeaderBlock* headers) { + return spdy_framer_.CreateHeaders(stream_id, flags, compressed, headers); +} + +SpdyDataFrame* BufferedSpdyFramer::CreateDataFrame(SpdyStreamId stream_id, + const char* data, + uint32 len, + SpdyDataFlags flags) { + return spdy_framer_.CreateDataFrame(stream_id, data, len, flags); +} + +SpdyFrame* BufferedSpdyFramer::CompressFrame(const SpdyFrame& frame) { + return spdy_framer_.CompressFrame(frame); +} + +bool BufferedSpdyFramer::IsCompressible(const SpdyFrame& frame) const { + return spdy_framer_.IsCompressible(frame); +} + +void BufferedSpdyFramer::InitHeaderStreaming(const SpdyControlFrame* frame) { + memset(header_buffer_, 0, kHeaderBufferSize); + header_buffer_used_ = 0; + header_buffer_valid_ = true; + header_stream_id_ = SpdyFramer::GetControlFrameStreamId(frame); + DCHECK_NE(header_stream_id_, SpdyFramer::kInvalidStream); +} + +} // namespace spdy diff --git a/net/spdy/buffered_spdy_framer.h b/net/spdy/buffered_spdy_framer.h new file mode 100644 index 0000000..2d7839c --- /dev/null +++ b/net/spdy/buffered_spdy_framer.h @@ -0,0 +1,106 @@ +// Copyright (c) 2011 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. + +#ifndef NET_SPDY_BUFFERED_SPDY_FRAMER_H_ +#define NET_SPDY_BUFFERED_SPDY_FRAMER_H_ +#pragma once + +#include "base/basictypes.h" +#include "base/gtest_prod_util.h" +#include "base/memory/linked_ptr.h" +#include "base/memory/scoped_ptr.h" +#include "net/base/net_export.h" +#include "net/spdy/spdy_framer.h" +#include "net/spdy/spdy_protocol.h" + +namespace spdy { + +class NET_EXPORT_PRIVATE BufferedSpdyFramerVisitorInterface + : public SpdyFramerVisitorInterface { + public: + BufferedSpdyFramerVisitorInterface() {} + virtual ~BufferedSpdyFramerVisitorInterface() {} + + // Called after all the header data for SYN_STREAM control frame is received. + virtual void OnSyn(const SpdySynStreamControlFrame& frame, + const linked_ptr<SpdyHeaderBlock>& headers) = 0; + + // Called after all the header data for SYN_REPLY control frame is received. + virtual void OnSynReply(const SpdySynReplyControlFrame& frame, + const linked_ptr<SpdyHeaderBlock>& headers) = 0; + + // Called after all the header data for HEADERS control frame is received. + virtual void OnHeaders(const SpdyHeadersControlFrame& frame, + const linked_ptr<SpdyHeaderBlock>& headers) = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(BufferedSpdyFramerVisitorInterface); +}; + +class NET_EXPORT_PRIVATE BufferedSpdyFramer { + public: + BufferedSpdyFramer(); + virtual ~BufferedSpdyFramer(); + + // Sets callbacks to be called from the buffered spdy framer. A visitor must + // be set, or else the framer will likely crash. It is acceptable for the + // visitor to do nothing. If this is called multiple times, only the last + // visitor will be used. + void set_visitor(BufferedSpdyFramerVisitorInterface* visitor); + + void OnControl(const SpdyControlFrame* frame); + + bool OnControlFrameHeaderData(const SpdyControlFrame* control_frame, + const char* header_data, + size_t len); + + void OnDataFrameHeader(const SpdyDataFrame* frame); + + // spdy_framer_ methods. + size_t ProcessInput(const char* data, size_t len); + void Reset(); + SpdyFramer::SpdyError error_code() const; + SpdyFramer::SpdyState state() const; + SpdySynStreamControlFrame* CreateSynStream(SpdyStreamId stream_id, + SpdyStreamId associated_stream_id, + int priority, + SpdyControlFlags flags, + bool compressed, + const SpdyHeaderBlock* headers); + SpdySynReplyControlFrame* CreateSynReply(SpdyStreamId stream_id, + SpdyControlFlags flags, + bool compressed, + const SpdyHeaderBlock* headers); + SpdyHeadersControlFrame* CreateHeaders(SpdyStreamId stream_id, + SpdyControlFlags flags, + bool compressed, + const SpdyHeaderBlock* headers); + SpdyDataFrame* CreateDataFrame(SpdyStreamId stream_id, + const char* data, + uint32 len, + SpdyDataFlags flags); + SpdyFrame* CompressFrame(const SpdyFrame& frame); + bool IsCompressible(const SpdyFrame& frame) const; + + private: + // The size of the header_buffer_. + enum { kHeaderBufferSize = 32 * 1024 }; + + void InitHeaderStreaming(const SpdyControlFrame* frame); + + SpdyFramer spdy_framer_; + BufferedSpdyFramerVisitorInterface* visitor_; + + // Header block streaming state: + char header_buffer_[kHeaderBufferSize]; + size_t header_buffer_used_; + bool header_buffer_valid_; + SpdyStreamId header_stream_id_; + + DISALLOW_COPY_AND_ASSIGN(BufferedSpdyFramer); +}; + +} // namespace spdy + +#endif // NET_SPDY_BUFFERED_SPDY_FRAMER_H_ diff --git a/net/spdy/buffered_spdy_framer_unittest.cc b/net/spdy/buffered_spdy_framer_unittest.cc new file mode 100755 index 0000000..56c5679 --- /dev/null +++ b/net/spdy/buffered_spdy_framer_unittest.cc @@ -0,0 +1,260 @@ +// Copyright (c) 2011 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/buffered_spdy_framer.h" + +#include "net/spdy/spdy_test_util.h" +#include "testing/platform_test.h" + +namespace spdy { + +namespace test { + +class TestBufferedSpdyVisitor : public BufferedSpdyFramerVisitorInterface { + public: + TestBufferedSpdyVisitor() + : error_count_(0), + syn_frame_count_(0), + syn_reply_frame_count_(0), + headers_frame_count_(0), + control_frame_header_data_count_(0), + zero_length_control_frame_header_data_count_(0), + header_stream_id_(-1) { + } + + void OnError(SpdyFramer* f) { + LOG(INFO) << "SpdyFramer Error: " + << SpdyFramer::ErrorCodeToString(f->error_code()); + error_count_++; + } + + void OnSyn(const SpdySynStreamControlFrame& frame, + const linked_ptr<SpdyHeaderBlock>& headers) { + EXPECT_EQ(header_stream_id_, frame.stream_id()); + syn_frame_count_++; + headers_ = *headers; + } + + void OnSynReply(const SpdySynReplyControlFrame& frame, + const linked_ptr<SpdyHeaderBlock>& headers) { + EXPECT_EQ(header_stream_id_, frame.stream_id()); + syn_reply_frame_count_++; + headers_ = *headers; + } + + void OnHeaders(const SpdyHeadersControlFrame& frame, + const linked_ptr<SpdyHeaderBlock>& headers) { + EXPECT_EQ(header_stream_id_, frame.stream_id()); + headers_frame_count_++; + headers_ = *headers; + } + + void OnStreamFrameData(SpdyStreamId stream_id, + const char* data, + size_t len) { + LOG(FATAL) << "Unexpected OnStreamFrameData call."; + } + + void OnDataFrameHeader(const SpdyDataFrame* frame) { + LOG(FATAL) << "Unexpected OnDataFrameHeader call."; + } + + void OnControl(const SpdyControlFrame* frame) { + uint32 type = frame->type(); + switch (type) { + case SYN_STREAM: + case SYN_REPLY: + case HEADERS: + header_stream_id_ = SpdyFramer::GetControlFrameStreamId(frame); + EXPECT_NE(header_stream_id_, SpdyFramer::kInvalidStream); + buffered_spdy_framer_.OnControl(frame); + break; + default: + LOG(FATAL) << "Unexpected frame type." << type; + } + } + + bool OnControlFrameHeaderData(const SpdyControlFrame* control_frame, + const char* header_data, + size_t len) { + SpdyStreamId stream_id = SpdyFramer::GetControlFrameStreamId(control_frame); + EXPECT_EQ(header_stream_id_, stream_id); + + bool result = buffered_spdy_framer_.OnControlFrameHeaderData( + control_frame, header_data, len); + EXPECT_TRUE(result); + + ++control_frame_header_data_count_; + + if (len == 0) + ++zero_length_control_frame_header_data_count_; + + return true; + } + + // Convenience function which runs a framer simulation with particular input. + void SimulateInFramer(const unsigned char* input, size_t size) { + buffered_spdy_framer_.set_visitor(this); + size_t input_remaining = size; + const char* input_ptr = reinterpret_cast<const char*>(input); + while (input_remaining > 0 && + buffered_spdy_framer_.error_code() == SpdyFramer::SPDY_NO_ERROR) { + // To make the tests more interesting, we feed random (amd small) chunks + // into the framer. This simulates getting strange-sized reads from + // the socket. + const size_t kMaxReadSize = 32; + size_t bytes_read = + (rand() % std::min(input_remaining, kMaxReadSize)) + 1; + size_t bytes_processed = + buffered_spdy_framer_.ProcessInput(input_ptr, bytes_read); + input_remaining -= bytes_processed; + input_ptr += bytes_processed; + if (buffered_spdy_framer_.state() == SpdyFramer::SPDY_DONE) + buffered_spdy_framer_.Reset(); + } + } + + BufferedSpdyFramer buffered_spdy_framer_; + + // Counters from the visitor callbacks. + int error_count_; + int syn_frame_count_; + int syn_reply_frame_count_; + int headers_frame_count_; + int control_frame_header_data_count_; // The count of chunks received. + // The count of zero-length control frame header data chunks received. + int zero_length_control_frame_header_data_count_; + + // Header block streaming state: + SpdyStreamId header_stream_id_; + + // Headers from OnSyn, OnSynReply and OnHeaders for verification. + SpdyHeaderBlock headers_; +}; + +} // namespace test + +} // namespace spdy + +using spdy::test::TestBufferedSpdyVisitor; + +namespace spdy { + +class BufferedSpdyFramerTest : public PlatformTest { + protected: + void EnableCompression(bool enabled) { + SpdyFramer::set_enable_compression_default(enabled); + } + + // Returns true if the two header blocks have equivalent content. + bool CompareHeaderBlocks(const SpdyHeaderBlock* expected, + const SpdyHeaderBlock* actual) { + if (expected->size() != actual->size()) { + LOG(ERROR) << "Expected " << expected->size() << " headers; actually got " + << actual->size() << "."; + return false; + } + for (SpdyHeaderBlock::const_iterator it = expected->begin(); + it != expected->end(); + ++it) { + SpdyHeaderBlock::const_iterator it2 = actual->find(it->first); + if (it2 == actual->end()) { + LOG(ERROR) << "Expected header name '" << it->first << "'."; + return false; + } + if (it->second.compare(it2->second) != 0) { + LOG(ERROR) << "Expected header named '" << it->first + << "' to have a value of '" << it->second + << "'. The actual value received was '" << it2->second + << "'."; + return false; + } + } + return true; + } +}; + +TEST_F(BufferedSpdyFramerTest, ReadSynStreamHeaderBlock) { + EnableCompression(false); + + SpdyHeaderBlock headers; + headers["aa"] = "vv"; + headers["bb"] = "ww"; + BufferedSpdyFramer framer; + scoped_ptr<SpdySynStreamControlFrame> control_frame( + framer.CreateSynStream(1, // stream_id + 0, // associated_stream_id + 1, // priority + CONTROL_FLAG_NONE, + true, // compress + &headers)); + EXPECT_TRUE(control_frame.get() != NULL); + + TestBufferedSpdyVisitor visitor; + visitor.SimulateInFramer( + reinterpret_cast<unsigned char*>(control_frame.get()->data()), + control_frame.get()->length() + SpdyControlFrame::kHeaderSize); + EXPECT_EQ(0, visitor.error_count_); + EXPECT_GT(visitor.control_frame_header_data_count_, 0); + EXPECT_EQ(1, visitor.zero_length_control_frame_header_data_count_); + EXPECT_EQ(1, visitor.syn_frame_count_); + EXPECT_EQ(0, visitor.syn_reply_frame_count_); + EXPECT_EQ(0, visitor.headers_frame_count_); + EXPECT_TRUE(CompareHeaderBlocks(&headers, &visitor.headers_)); +} + +TEST_F(BufferedSpdyFramerTest, ReadSynReplyHeaderBlock) { + EnableCompression(false); + + SpdyHeaderBlock headers; + headers["alpha"] = "beta"; + headers["gamma"] = "delta"; + BufferedSpdyFramer framer; + scoped_ptr<SpdySynReplyControlFrame> control_frame( + framer.CreateSynReply(1, // stream_id + CONTROL_FLAG_NONE, + true, // compress + &headers)); + EXPECT_TRUE(control_frame.get() != NULL); + + TestBufferedSpdyVisitor visitor; + visitor.SimulateInFramer( + reinterpret_cast<unsigned char*>(control_frame.get()->data()), + control_frame.get()->length() + SpdyControlFrame::kHeaderSize); + EXPECT_EQ(0, visitor.error_count_); + EXPECT_GT(visitor.control_frame_header_data_count_, 0); + EXPECT_EQ(1, visitor.zero_length_control_frame_header_data_count_); + EXPECT_EQ(0, visitor.syn_frame_count_); + EXPECT_EQ(1, visitor.syn_reply_frame_count_); + EXPECT_EQ(0, visitor.headers_frame_count_); + EXPECT_TRUE(CompareHeaderBlocks(&headers, &visitor.headers_)); +} + +TEST_F(BufferedSpdyFramerTest, ReadHeadersHeaderBlock) { + EnableCompression(false); + + SpdyHeaderBlock headers; + headers["alpha"] = "beta"; + headers["gamma"] = "delta"; + BufferedSpdyFramer framer; + scoped_ptr<SpdyHeadersControlFrame> control_frame( + framer.CreateHeaders(1, // stream_id + CONTROL_FLAG_NONE, + true, // compress + &headers)); + EXPECT_TRUE(control_frame.get() != NULL); + + TestBufferedSpdyVisitor visitor; + visitor.SimulateInFramer( + reinterpret_cast<unsigned char*>(control_frame.get()->data()), + control_frame.get()->length() + SpdyControlFrame::kHeaderSize); + EXPECT_EQ(0, visitor.error_count_); + EXPECT_GT(visitor.control_frame_header_data_count_, 0); + EXPECT_EQ(1, visitor.zero_length_control_frame_header_data_count_); + EXPECT_EQ(0, visitor.syn_frame_count_); + EXPECT_EQ(0, visitor.syn_reply_frame_count_); + EXPECT_EQ(1, visitor.headers_frame_count_); + EXPECT_TRUE(CompareHeaderBlocks(&headers, &visitor.headers_)); +} +} // namespace spdy diff --git a/net/spdy/spdy_framer.cc b/net/spdy/spdy_framer.cc index 7734eb8..8b41a48 100644 --- a/net/spdy/spdy_framer.cc +++ b/net/spdy/spdy_framer.cc @@ -357,37 +357,42 @@ size_t SpdyFramer::ProcessCommonHeader(const char* data, size_t len) { size_t original_len = len; SpdyFrame current_frame(current_frame_buffer_, false); - do { - if (current_frame_len_ < SpdyFrame::kHeaderSize) { - size_t bytes_desired = SpdyFrame::kHeaderSize - current_frame_len_; - UpdateCurrentFrameBuffer(&data, &len, bytes_desired); - // Check for an empty data frame. - if (current_frame_len_ == SpdyFrame::kHeaderSize && - !current_frame.is_control_frame() && - current_frame.length() == 0) { - if (current_frame.flags() & CONTROL_FLAG_FIN) { - SpdyDataFrame data_frame(current_frame_buffer_, false); - visitor_->OnStreamFrameData(data_frame.stream_id(), NULL, 0); - } - CHANGE_STATE(SPDY_AUTO_RESET); - } - break; + // Update current frame buffer as needed. + if (current_frame_len_ < SpdyFrame::kHeaderSize) { + size_t bytes_desired = SpdyFrame::kHeaderSize - current_frame_len_; + UpdateCurrentFrameBuffer(&data, &len, bytes_desired); + } + + if (current_frame_len_ < SpdyFrame::kHeaderSize) { + // Do nothing. + } else if (current_frame_len_ == SpdyFrame::kHeaderSize && + !current_frame.is_control_frame() && + current_frame.length() == 0) { + // Empty data frame. + SpdyDataFrame data_frame(current_frame_buffer_, false); + visitor_->OnDataFrameHeader(&data_frame); + if (current_frame.flags() & DATA_FLAG_FIN) { + visitor_->OnStreamFrameData(data_frame.stream_id(), NULL, 0); } + CHANGE_STATE(SPDY_AUTO_RESET); + } else { remaining_data_ = current_frame.length(); // This is just a sanity check for help debugging early frame errors. if (remaining_data_ > 1000000u) { - LOG(WARNING) << - "Unexpectedly large frame. Spdy session is likely corrupt."; + LOG(WARNING) << "Unexpectedly large frame. " << display_protocol_ + << " session is likely corrupt."; } // if we're here, then we have the common header all received. - if (!current_frame.is_control_frame()) + if (!current_frame.is_control_frame()) { + SpdyDataFrame data_frame(current_frame_buffer_, false); + visitor_->OnDataFrameHeader(&data_frame); CHANGE_STATE(SPDY_FORWARD_STREAM_FRAME); - else - CHANGE_STATE(SPDY_INTERPRET_CONTROL_FRAME_COMMON_HEADER); - } while (false); - + } else { + ProcessControlFrameHeader(); + } + } return original_len - len; } @@ -479,12 +484,47 @@ void SpdyFramer::ProcessControlFrameHeader() { } remaining_control_payload_ = current_control_frame.length(); - if (remaining_control_payload_ > kControlFrameBufferMaxSize) { + if (remaining_control_payload_ > + kControlFrameBufferMaxSize - SpdyFrame::kHeaderSize) { set_error(SPDY_CONTROL_PAYLOAD_TOO_LARGE); return; } - ExpandControlFrameBuffer(remaining_control_payload_); + int32 frame_size_without_header_block; + switch (current_control_frame.type()) { + case SYN_STREAM: + frame_size_without_header_block = SpdySynStreamControlFrame::size(); + break; + case SYN_REPLY: + frame_size_without_header_block = SpdySynReplyControlFrame::size(); + break; + case HEADERS: + frame_size_without_header_block = SpdyHeadersControlFrame::size(); + break; + default: + frame_size_without_header_block = -1; + LOG_IF(DFATAL, remaining_control_payload_ + SpdyFrame::kHeaderSize > + current_frame_capacity_) + << display_protocol_ + << " control frame buffer too small for fixed-length frame."; + ExpandControlFrameBuffer(remaining_control_payload_); + break; + } + + if (frame_size_without_header_block > 0) { + // We have a control frame with a header block. We need to parse the + // remainder of the control frame's header before we can parse the header + // block. The start of the header block varies with the control type. + DCHECK_GE(static_cast<uint32>(frame_size_without_header_block), + current_frame_len_); + remaining_control_header_ = frame_size_without_header_block - + current_frame_len_; + remaining_control_payload_ += SpdyFrame::kHeaderSize - + frame_size_without_header_block; + CHANGE_STATE(SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK); + return; + } + CHANGE_STATE(SPDY_CONTROL_FRAME_PAYLOAD); } @@ -526,8 +566,8 @@ size_t SpdyFramer::ProcessControlFrameBeforeHeaderBlock(const char* data, // visitor or decompresses and then passes directly to the visitor, via // IncrementallyDeliverControlFrameHeaderData() or // IncrementallyDecompressControlFrameHeaderData() respectively. -size_t SpdyFramer::NewProcessControlFrameHeaderBlock(const char* data, - size_t data_len) { +size_t SpdyFramer::ProcessControlFrameHeaderBlock(const char* data, + size_t data_len) { DCHECK_EQ(SPDY_CONTROL_FRAME_HEADER_BLOCK, state_); SpdyControlFrame control_frame(current_frame_buffer_, false); bool processed_successfully = true; @@ -538,7 +578,7 @@ size_t SpdyFramer::NewProcessControlFrameHeaderBlock(const char* data, DCHECK_GT(process_bytes, 0u); if (enable_compression_) { - processed_successfully = NewIncrementallyDecompressControlFrameHeaderData( + processed_successfully = IncrementallyDecompressControlFrameHeaderData( &control_frame, data, process_bytes); } else { processed_successfully = IncrementallyDeliverControlFrameHeaderData( @@ -550,8 +590,7 @@ size_t SpdyFramer::NewProcessControlFrameHeaderBlock(const char* data, if (remaining_control_payload_ == 0 && processed_successfully) { // The complete header block has been delivered. We send a zero-length // OnControlFrameHeaderData() to indicate this. - visitor_->OnControlFrameHeaderData( - GetControlFrameStreamId(&control_frame), NULL, 0); + visitor_->OnControlFrameHeaderData(&control_frame, NULL, 0); // If this is a FIN, tell the caller. if (control_frame.flags() & CONTROL_FLAG_FIN) { @@ -571,8 +610,8 @@ size_t SpdyFramer::NewProcessControlFrameHeaderBlock(const char* data, return process_bytes; } -size_t SpdyFramer::ProcessControlFrameHeaderBlock(const char* data, - size_t data_len) { +size_t SpdyFramer::OldProcessControlFrameHeaderBlock(const char* data, + size_t data_len) { DCHECK_EQ(SPDY_CONTROL_FRAME_HEADER_BLOCK, state_); size_t original_data_len = data_len; SpdyControlFrame control_frame(current_frame_buffer_, false); @@ -591,7 +630,7 @@ size_t SpdyFramer::ProcessControlFrameHeaderBlock(const char* data, remaining_control_payload_); remaining_control_payload_ -= bytes_read; if (remaining_control_payload_ == 0) { - read_successfully = IncrementallyDecompressControlFrameHeaderData( + read_successfully = OldIncrementallyDecompressControlFrameHeaderData( &control_frame); } } @@ -605,8 +644,7 @@ size_t SpdyFramer::ProcessControlFrameHeaderBlock(const char* data, } if (remaining_control_payload_ == 0 && read_successfully) { // The complete header block has been delivered. - visitor_->OnControlFrameHeaderData(GetControlFrameStreamId(&control_frame), - NULL, 0); + visitor_->OnControlFrameHeaderData(&control_frame, NULL, 0); // If this is a FIN, tell the caller. if (control_frame.flags() & CONTROL_FLAG_FIN) { @@ -624,28 +662,27 @@ size_t SpdyFramer::ProcessControlFrameHeaderBlock(const char* data, size_t SpdyFramer::ProcessControlFramePayload(const char* data, size_t len) { size_t original_len = len; - do { - if (remaining_control_payload_) { - size_t bytes_read = UpdateCurrentFrameBuffer(&data, &len, - remaining_control_payload_); - remaining_control_payload_ -= bytes_read; - remaining_data_ -= bytes_read; - if (remaining_control_payload_) - break; - } - SpdyControlFrame control_frame(current_frame_buffer_, false); - visitor_->OnControl(&control_frame); + if (remaining_control_payload_) { + size_t bytes_read = UpdateCurrentFrameBuffer(&data, &len, + remaining_control_payload_); + remaining_control_payload_ -= bytes_read; + remaining_data_ -= bytes_read; + if (remaining_control_payload_ == 0) { + SpdyControlFrame control_frame(current_frame_buffer_, false); + DCHECK(!control_frame.has_header_block()); + visitor_->OnControl(&control_frame); - // If this is a FIN, tell the caller. - if (control_frame.type() == SYN_REPLY && - control_frame.flags() & CONTROL_FLAG_FIN) { - visitor_->OnStreamFrameData(reinterpret_cast<SpdySynReplyControlFrame*>( - &control_frame)->stream_id(), - NULL, 0); - } + // If this is a FIN, tell the caller. + if (control_frame.type() == SYN_REPLY && + control_frame.flags() & CONTROL_FLAG_FIN) { + visitor_->OnStreamFrameData(reinterpret_cast<SpdySynReplyControlFrame*>( + &control_frame)->stream_id(), + NULL, 0); + } - CHANGE_STATE(SPDY_IGNORE_REMAINING_PAYLOAD); - } while (false); + CHANGE_STATE(SPDY_IGNORE_REMAINING_PAYLOAD); + } + } return original_len - len; } @@ -1427,7 +1464,7 @@ SpdyFrame* SpdyFramer::DecompressFrameWithZStream(const SpdyFrame& frame, // result to the visitor in chunks. Continue this until the visitor // indicates that it cannot process any more data, or (more commonly) we // run out of data to deliver. -bool SpdyFramer::IncrementallyDecompressControlFrameHeaderData( +bool SpdyFramer::OldIncrementallyDecompressControlFrameHeaderData( const SpdyControlFrame* control_frame) { z_stream* decomp = GetHeaderDecompressor(); int payload_length; @@ -1459,8 +1496,8 @@ bool SpdyFramer::IncrementallyDecompressControlFrameHeaderData( } else { DCHECK_GT(arraysize(buffer), decomp->avail_out); size_t len = arraysize(buffer) - decomp->avail_out; - read_successfully = visitor_->OnControlFrameHeaderData(stream_id, buffer, - len); + read_successfully = visitor_->OnControlFrameHeaderData( + control_frame, buffer, len); if (!read_successfully) { // Assume that the problem was the header block was too large for the // visitor. @@ -1476,7 +1513,7 @@ bool SpdyFramer::IncrementallyDecompressControlFrameHeaderData( // result to the visitor in chunks. Continue this until the visitor // indicates that it cannot process any more data, or (more commonly) we // run out of data to deliver. -bool SpdyFramer::NewIncrementallyDecompressControlFrameHeaderData( +bool SpdyFramer::IncrementallyDecompressControlFrameHeaderData( const SpdyControlFrame* control_frame, const char* data, size_t len) { @@ -1507,7 +1544,7 @@ bool SpdyFramer::NewIncrementallyDecompressControlFrameHeaderData( size_t decompressed_len = arraysize(buffer) - decomp->avail_out; if (decompressed_len > 0) { processed_successfully = visitor_->OnControlFrameHeaderData( - stream_id, buffer, decompressed_len); + control_frame, buffer, decompressed_len); } if (!processed_successfully) { // Assume that the problem was the header block was too large for the @@ -1526,7 +1563,7 @@ bool SpdyFramer::IncrementallyDeliverControlFrameHeaderData( DCHECK_LT(0u, stream_id); while (read_successfully && len > 0) { size_t bytes_to_deliver = std::min(len, kHeaderDataChunkMaxSize); - read_successfully = visitor_->OnControlFrameHeaderData(stream_id, data, + read_successfully = visitor_->OnControlFrameHeaderData(control_frame, data, bytes_to_deliver); data += bytes_to_deliver; len -= bytes_to_deliver; diff --git a/net/spdy/spdy_framer.h b/net/spdy/spdy_framer.h index 1bbe355..c1b47d9 100644 --- a/net/spdy/spdy_framer.h +++ b/net/spdy/spdy_framer.h @@ -54,6 +54,25 @@ typedef std::list<SpdySetting> SpdySettings; // SpdyFramerVisitorInterface is a set of callbacks for the SpdyFramer. // Implement this interface to receive event callbacks as frames are // decoded from the framer. +// +// Control frames that contain SPDY header blocks (SYN_STREAM, SYN_REPLY, and +// HEADER) are processed in fashion that allows the decompressed header block +// to be delivered in chunks to the visitor. The following steps are followed: +// 1. OnControl is called, with either a SpdySynStreamControlFrame, +// SpdySynReplyControlFrame, or a SpdyHeaderControlFrame argument. +// 2. Repeated: OnControlFrameHeaderData is called with chunks of the +// decompressed header block. In each call the len parameter is greater +// than zero. +// 3. OnControlFrameHeaderData is called with len set to zero, indicating +// that the full header block has been delivered for the control frame. +// During step 2 the visitor may return false, indicating that the chunk of +// header data could not be handled by the visitor (typically this indicates +// resource exhaustion). If this occurs the framer will discontinue +// delivering chunks to the visitor, set a SPDY_CONTROL_PAYLOAD_TOO_LARGE +// error, and clean up appropriately. Note that this will cause the header +// decompressor to lose synchronization with the sender's header compressor, +// making the SPDY session unusable for future work. The visitor's OnError +// function should deal with this condition by closing the SPDY connection. class NET_EXPORT_PRIVATE SpdyFramerVisitorInterface { public: virtual ~SpdyFramerVisitorInterface() {} @@ -69,14 +88,14 @@ class NET_EXPORT_PRIVATE SpdyFramerVisitorInterface { // Called when a chunk of header data is available. This is called // after OnControl() is called with the control frame associated with the // header data being delivered here. - // |stream_id| The stream receiving the header data. + // |control_frame| header control frame. // |header_data| A buffer containing the header data chunk received. // |len| The length of the header data buffer. A length of zero indicates // that the header data block has been completely sent. // When this function returns true the visitor indicates that it accepted // all of the data. Returning false indicates that that an unrecoverable // error has occurred, such as bad header data or resource exhaustion. - virtual bool OnControlFrameHeaderData(SpdyStreamId stream_id, + virtual bool OnControlFrameHeaderData(const SpdyControlFrame* control_frame, const char* header_data, size_t len) = 0; @@ -352,8 +371,8 @@ class NET_EXPORT_PRIVATE SpdyFramer { void ProcessControlFrameHeader(); size_t ProcessControlFramePayload(const char* data, size_t len); size_t ProcessControlFrameBeforeHeaderBlock(const char* data, size_t len); - size_t NewProcessControlFrameHeaderBlock(const char* data, size_t len); size_t ProcessControlFrameHeaderBlock(const char* data, size_t len); + size_t OldProcessControlFrameHeaderBlock(const char* data, size_t len); size_t ProcessDataFramePayload(const char* data, size_t len); // Get (and lazily initialize) the ZLib state. @@ -381,13 +400,13 @@ class NET_EXPORT_PRIVATE SpdyFramer { // Deliver the given control frame's compressed headers block to the visitor // in decompressed form, in chunks. Returns true if the visitor has // accepted all of the chunks. - bool IncrementallyDecompressControlFrameHeaderData( + bool OldIncrementallyDecompressControlFrameHeaderData( const SpdyControlFrame* frame); // Deliver the given control frame's compressed headers block to the visitor // in decompressed form, in chunks. Returns true if the visitor has // accepted all of the chunks. - bool NewIncrementallyDecompressControlFrameHeaderData( + bool IncrementallyDecompressControlFrameHeaderData( const SpdyControlFrame* frame, const char* data, size_t len); diff --git a/net/spdy/spdy_framer_test.cc b/net/spdy/spdy_framer_test.cc index 394d7c4..56b4365 100644 --- a/net/spdy/spdy_framer_test.cc +++ b/net/spdy/spdy_framer_test.cc @@ -85,7 +85,7 @@ void CompareCharArraysWithHexError( class TestSpdyVisitor : public SpdyFramerVisitorInterface { public: - static const size_t kDefaultHeaderBufferSize = 16 * 1024; + static const size_t kDefaultHeaderBufferSize = 64 * 1024; TestSpdyVisitor() : use_compression_(false), @@ -124,6 +124,7 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface { void OnStreamFrameData(SpdyStreamId stream_id, const char* data, size_t len) { + EXPECT_EQ(header_stream_id_, stream_id); if (len == 0) ++zero_length_data_frame_count_; @@ -164,10 +165,11 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface { ++fin_flag_count_; } - bool OnControlFrameHeaderData(SpdyStreamId stream_id, + bool OnControlFrameHeaderData(const SpdyControlFrame* control_frame, const char* header_data, size_t len) { ++control_frame_header_data_count_; + SpdyStreamId stream_id = SpdyFramer::GetControlFrameStreamId(control_frame); CHECK_EQ(header_stream_id_, stream_id); if (len == 0) { ++zero_length_control_frame_header_data_count_; @@ -285,6 +287,21 @@ using spdy::test::TestSpdyVisitor; namespace spdy { +TEST(SpdyFrameBuilderTest, WriteLimits) { + SpdyFrameBuilder builder(kLengthMask + 4); + // length field should fail. + EXPECT_FALSE(builder.WriteBytes(reinterpret_cast<const void*>(0x1), + kLengthMask + 1)); + EXPECT_EQ(0, builder.length()); + + // Writing a block of the maximum allowed size should succeed. + const std::string kLargeData(kLengthMask, 'A'); + builder.WriteUInt32(kLengthMask); + EXPECT_EQ(4, builder.length()); + EXPECT_TRUE(builder.WriteBytes(kLargeData.data(), kLengthMask)); + EXPECT_EQ(4 + kLengthMask, static_cast<unsigned>(builder.length())); +} + class SpdyFramerTest : public PlatformTest { public: virtual void TearDown() {} @@ -300,23 +317,36 @@ class SpdyFramerTest : public PlatformTest { CompareCharArraysWithHexError( description, actual, actual_len, expected, expected_len); } -}; - -TEST(SpdyFrameBuilderTest, WriteLimits) { - SpdyFrameBuilder builder(kLengthMask + 4); - // length field should fail. - EXPECT_FALSE(builder.WriteBytes(reinterpret_cast<const void*>(0x1), - kLengthMask + 1)); - EXPECT_EQ(0, builder.length()); + // Returns true if the two header blocks have equivalent content. + bool CompareHeaderBlocks(const SpdyHeaderBlock* expected, + const SpdyHeaderBlock* actual) { + if (expected->size() != actual->size()) { + LOG(ERROR) << "Expected " << expected->size() << " headers; actually got " + << actual->size() << "." << std::endl; + return false; + } + for (SpdyHeaderBlock::const_iterator it = expected->begin(); + it != expected->end(); + ++it) { + SpdyHeaderBlock::const_iterator it2 = actual->find(it->first); + if (it2 == actual->end()) { + LOG(ERROR) << "Expected header name '" << it->first << "'." + << std::endl; + return false; + } + if (it->second.compare(it2->second) != 0) { + LOG(ERROR) << "Expected header named '" << it->first + << "' to have a value of '" << it->second + << "'. The actual value received was '" << it2->second + << "'." << std::endl; + return false; + } + } + return true; + } +}; - // Writing a block of the maximum allowed size should succeed. - const std::string kLargeData(kLengthMask, 'A'); - builder.WriteUInt32(kLengthMask); - EXPECT_EQ(4, builder.length()); - EXPECT_TRUE(builder.WriteBytes(kLargeData.data(), kLengthMask)); - EXPECT_EQ(4 + kLengthMask, static_cast<unsigned>(builder.length())); -} // Test that we can encode and decode a SpdyHeaderBlock. TEST_F(SpdyFramerTest, HeaderBlock) { @@ -408,7 +438,9 @@ TEST_F(SpdyFramerTest, OutOfOrderHeaders) { syn_frame.header_block_len()); SpdyFramer framer; framer.set_enable_compression(false); - EXPECT_TRUE(framer.ParseHeaderBlock(control_frame.get(), &new_headers)); + EXPECT_TRUE(framer.ParseHeaderBlockInBuffer(serialized_headers.c_str(), + serialized_headers.size(), + &new_headers)); } TEST_F(SpdyFramerTest, WrongNumberOfHeaders) { @@ -482,7 +514,9 @@ TEST_F(SpdyFramerTest, DuplicateHeader) { SpdyFramer framer; framer.set_enable_compression(false); // This should fail because duplicate headers are verboten by the spec. - EXPECT_FALSE(framer.ParseHeaderBlock(control_frame.get(), &new_headers)); + EXPECT_FALSE(framer.ParseHeaderBlockInBuffer(serialized_headers.c_str(), + serialized_headers.size(), + &new_headers)); } TEST_F(SpdyFramerTest, MultiValueHeader) { @@ -511,7 +545,9 @@ TEST_F(SpdyFramerTest, MultiValueHeader) { syn_frame.header_block_len()); SpdyFramer framer; framer.set_enable_compression(false); - EXPECT_TRUE(framer.ParseHeaderBlock(control_frame.get(), &new_headers)); + EXPECT_TRUE(framer.ParseHeaderBlockInBuffer(serialized_headers.c_str(), + serialized_headers.size(), + &new_headers)); EXPECT_TRUE(new_headers.find("name") != new_headers.end()); EXPECT_EQ(value, new_headers.find("name")->second); } @@ -576,6 +612,7 @@ TEST_F(SpdyFramerTest, BasicCompression) { memcmp(frame3->data(), frame4->data(), SpdyFrame::kHeaderSize + frame3->length())); + // Expect frames 3 to be the same as a uncompressed frame created // from scratch. scoped_ptr<SpdySynStreamControlFrame> @@ -673,7 +710,7 @@ TEST_F(SpdyFramerTest, Basic) { EXPECT_EQ(2, visitor.fin_frame_count_); EXPECT_EQ(0, visitor.fin_flag_count_); EXPECT_EQ(0, visitor.zero_length_data_frame_count_); - // EXPECT_EQ(4, visitor.data_frame_count_); + EXPECT_EQ(4, visitor.data_frame_count_); } // Test that the FIN flag on a data frame signifies EOF. @@ -716,7 +753,7 @@ TEST_F(SpdyFramerTest, FinOnDataFrame) { EXPECT_EQ(0, visitor.fin_frame_count_); EXPECT_EQ(0, visitor.fin_flag_count_); EXPECT_EQ(1, visitor.zero_length_data_frame_count_); - // EXPECT_EQ(2, visitor.data_frame_count_); + EXPECT_EQ(2, visitor.data_frame_count_); } // Test that the FIN flag on a SYN reply frame signifies EOF. @@ -750,7 +787,7 @@ TEST_F(SpdyFramerTest, FinOnSynReplyFrame) { EXPECT_EQ(0, visitor.fin_frame_count_); EXPECT_EQ(1, visitor.fin_flag_count_); EXPECT_EQ(1, visitor.zero_length_data_frame_count_); - // EXPECT_EQ(0, visitor.data_frame_count_); + EXPECT_EQ(0, visitor.data_frame_count_); } // Basic compression & decompression @@ -893,7 +930,7 @@ TEST_F(SpdyFramerTest, UnclosedStreamDataCompressors) { EXPECT_EQ(0, visitor.fin_frame_count_); EXPECT_EQ(0, visitor.fin_flag_count_); EXPECT_EQ(1, visitor.zero_length_data_frame_count_); - // EXPECT_EQ(1, visitor.data_frame_count_); + EXPECT_EQ(1, visitor.data_frame_count_); // We closed the streams, so all compressors should be down. EXPECT_EQ(0, visitor.framer_.num_stream_compressors()); diff --git a/net/spdy/spdy_network_transaction_unittest.cc b/net/spdy/spdy_network_transaction_unittest.cc index eb47f40..e214d2d 100644 --- a/net/spdy/spdy_network_transaction_unittest.cc +++ b/net/spdy/spdy_network_transaction_unittest.cc @@ -3629,15 +3629,12 @@ TEST_P(SpdyNetworkTransactionTest, DecompressFailureOnSynReply) { ConstructSpdyRstStream(1, spdy::PROTOCOL_ERROR)); MockWrite writes[] = { CreateMockWrite(*compressed), - CreateMockWrite(*rst), }; scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1)); scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, true)); MockRead reads[] = { CreateMockRead(*resp), - CreateMockRead(*body), - MockRead(true, 0, 0) }; scoped_refptr<DelayedSocketData> data( diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc index c547d51..6c0a584 100644 --- a/net/spdy/spdy_session.cc +++ b/net/spdy/spdy_session.cc @@ -295,7 +295,7 @@ SpdySession::SpdySession(const HostPortProxyPair& host_port_proxy_pair, // TODO(mbelshe): consider randomization of the stream_hi_water_mark. - spdy_framer_.set_visitor(this); + buffered_spdy_framer_.set_visitor(this); SendSettings(); } @@ -529,7 +529,7 @@ int SpdySession::WriteSynStream( SendPrefacePingIfNoneInFlight(); scoped_ptr<spdy::SpdySynStreamControlFrame> syn_frame( - spdy_framer_.CreateSynStream( + buffered_spdy_framer_.CreateSynStream( stream_id, 0, ConvertRequestPriorityToSpdyPriority(priority), flags, false, headers.get())); @@ -607,7 +607,8 @@ int SpdySession::WriteStreamData(spdy::SpdyStreamId stream_id, // TODO(mbelshe): reduce memory copies here. scoped_ptr<spdy::SpdyDataFrame> frame( - spdy_framer_.CreateDataFrame(stream_id, data->data(), len, flags)); + buffered_spdy_framer_.CreateDataFrame( + stream_id, data->data(), len, flags)); QueueFrame(frame.get(), stream->priority(), stream); // Some servers don't like too many pings, so we limit our current sending to @@ -636,7 +637,7 @@ void SpdySession::ResetStream( make_scoped_refptr(new NetLogSpdyRstParameter(stream_id, status))); scoped_ptr<spdy::SpdyRstStreamControlFrame> rst_frame( - spdy_framer_.CreateRstStream(stream_id, status)); + spdy::SpdyFramer::CreateRstStream(stream_id, status)); // Default to lowest priority unless we know otherwise. int priority = 3; @@ -695,12 +696,14 @@ void SpdySession::OnReadComplete(int bytes_read) { char *data = read_buffer_->data(); while (bytes_read && - spdy_framer_.error_code() == spdy::SpdyFramer::SPDY_NO_ERROR) { - uint32 bytes_processed = spdy_framer_.ProcessInput(data, bytes_read); + buffered_spdy_framer_.error_code() == + spdy::SpdyFramer::SPDY_NO_ERROR) { + uint32 bytes_processed = + buffered_spdy_framer_.ProcessInput(data, bytes_read); bytes_read -= bytes_processed; data += bytes_processed; - if (spdy_framer_.state() == spdy::SpdyFramer::SPDY_DONE) - spdy_framer_.Reset(); + if (buffered_spdy_framer_.state() == spdy::SpdyFramer::SPDY_DONE) + buffered_spdy_framer_.Reset(); } if (state_ != CLOSED) @@ -834,9 +837,9 @@ void SpdySession::WriteSocket() { // which is now. At this time, we don't compress our data frames. spdy::SpdyFrame uncompressed_frame(next_buffer.buffer()->data(), false); size_t size; - if (spdy_framer_.IsCompressible(uncompressed_frame)) { + if (buffered_spdy_framer_.IsCompressible(uncompressed_frame)) { scoped_ptr<spdy::SpdyFrame> compressed_frame( - spdy_framer_.CompressFrame(uncompressed_frame)); + buffered_spdy_framer_.CompressFrame(uncompressed_frame)); if (!compressed_frame.get()) { LOG(ERROR) << "SPDY Compression failure"; CloseSessionOnError(net::ERR_SPDY_PROTOCOL_ERROR, true); @@ -1297,28 +1300,12 @@ void SpdySession::OnHeaders(const spdy::SpdyHeadersControlFrame& frame, } void SpdySession::OnControl(const spdy::SpdyControlFrame* frame) { - const linked_ptr<spdy::SpdyHeaderBlock> headers(new spdy::SpdyHeaderBlock); uint32 type = frame->type(); if (type == spdy::SYN_STREAM || type == spdy::SYN_REPLY || type == spdy::HEADERS) { - if (!spdy_framer_.ParseHeaderBlock(frame, headers.get())) { - LOG(WARNING) << "Could not parse Spdy Control Frame Header."; - int stream_id = 0; - if (type == spdy::SYN_STREAM) { - stream_id = (reinterpret_cast<const spdy::SpdySynStreamControlFrame*> - (frame))->stream_id(); - } else if (type == spdy::SYN_REPLY) { - stream_id = (reinterpret_cast<const spdy::SpdySynReplyControlFrame*> - (frame))->stream_id(); - } else if (type == spdy::HEADERS) { - stream_id = (reinterpret_cast<const spdy::SpdyHeadersControlFrame*> - (frame))->stream_id(); - } - if(IsStreamActive(stream_id)) - ResetStream(stream_id, spdy::PROTOCOL_ERROR); - return; - } + buffered_spdy_framer_.OnControl(frame); + return; } frames_received_++; @@ -1337,19 +1324,6 @@ void SpdySession::OnControl(const spdy::SpdyControlFrame* frame) { case spdy::RST_STREAM: OnRst(*reinterpret_cast<const spdy::SpdyRstStreamControlFrame*>(frame)); break; - case spdy::SYN_STREAM: - OnSyn(*reinterpret_cast<const spdy::SpdySynStreamControlFrame*>(frame), - headers); - break; - case spdy::HEADERS: - OnHeaders(*reinterpret_cast<const spdy::SpdyHeadersControlFrame*>(frame), - headers); - break; - case spdy::SYN_REPLY: - OnSynReply( - *reinterpret_cast<const spdy::SpdySynReplyControlFrame*>(frame), - headers); - break; case spdy::WINDOW_UPDATE: OnWindowUpdate( *reinterpret_cast<const spdy::SpdyWindowUpdateControlFrame*>(frame)); @@ -1359,15 +1333,27 @@ void SpdySession::OnControl(const spdy::SpdyControlFrame* frame) { } } -bool SpdySession::OnControlFrameHeaderData(spdy::SpdyStreamId stream_id, - const char* header_data, - size_t len) { - DCHECK(false); - return false; +bool SpdySession::OnControlFrameHeaderData( + const spdy::SpdyControlFrame* control_frame, + const char* header_data, + size_t len) { + if (!buffered_spdy_framer_.OnControlFrameHeaderData( + control_frame, header_data, len)) { + spdy::SpdyStreamId stream_id = + spdy::SpdyFramer::GetControlFrameStreamId(control_frame); + if (IsStreamActive(stream_id)) + ResetStream(stream_id, spdy::PROTOCOL_ERROR); + return false; + } + if (len == 0) { + // Indicates end-of-header-block. + frames_received_++; + } + return true; } void SpdySession::OnDataFrameHeader(const spdy::SpdyDataFrame* frame) { - DCHECK(false); + buffered_spdy_framer_.OnDataFrameHeader(frame); } void SpdySession::OnRst(const spdy::SpdyRstStreamControlFrame& frame) { @@ -1451,7 +1437,7 @@ void SpdySession::OnPing(const spdy::SpdyPingControlFrame& frame) { void SpdySession::OnSettings(const spdy::SpdySettingsControlFrame& frame) { spdy::SpdySettings settings; - if (spdy_framer_.ParseSettings(&frame, &settings)) { + if (spdy::SpdyFramer::ParseSettings(&frame, &settings)) { HandleSettings(settings); http_server_properties_->SetSpdySettings(host_port_pair(), settings); } @@ -1504,7 +1490,7 @@ void SpdySession::SendWindowUpdate(spdy::SpdyStreamId stream_id, stream_id, delta_window_size, stream->recv_window_size()))); scoped_ptr<spdy::SpdyWindowUpdateControlFrame> window_update_frame( - spdy_framer_.CreateWindowUpdate(stream_id, delta_window_size)); + spdy::SpdyFramer::CreateWindowUpdate(stream_id, delta_window_size)); QueueFrame(window_update_frame.get(), stream->priority(), NULL); } @@ -1568,7 +1554,7 @@ void SpdySession::SendSettings() { // Create the SETTINGS frame and send it. scoped_ptr<spdy::SpdySettingsControlFrame> settings_frame( - spdy_framer_.CreateSettings(settings)); + spdy::SpdyFramer::CreateSettings(settings)); sent_settings_ = true; QueueFrame(settings_frame.get(), 0, NULL); } @@ -1627,7 +1613,7 @@ void SpdySession::SendTrailingPing() { void SpdySession::WritePingFrame(uint32 unique_id) { scoped_ptr<spdy::SpdyPingControlFrame> ping_frame( - spdy_framer_.CreatePingFrame(next_ping_id_)); + spdy::SpdyFramer::CreatePingFrame(next_ping_id_)); QueueFrame(ping_frame.get(), SPDY_PRIORITY_HIGHEST, NULL); if (net_log().IsLoggingAllEvents()) { diff --git a/net/spdy/spdy_session.h b/net/spdy/spdy_session.h index 1cdb7f7..fcdb193 100644 --- a/net/spdy/spdy_session.h +++ b/net/spdy/spdy_session.h @@ -26,7 +26,7 @@ #include "net/socket/client_socket_handle.h" #include "net/socket/ssl_client_socket.h" #include "net/socket/stream_socket.h" -#include "net/spdy/spdy_framer.h" +#include "net/spdy/buffered_spdy_framer.h" #include "net/spdy/spdy_io_buffer.h" #include "net/spdy/spdy_protocol.h" #include "net/spdy/spdy_session_pool.h" @@ -46,7 +46,7 @@ class SpdyStream; class SSLInfo; class NET_EXPORT SpdySession : public base::RefCounted<SpdySession>, - public spdy::SpdyFramerVisitorInterface, + public spdy::BufferedSpdyFramerVisitorInterface, public LayeredPool { public: // Create a new SpdySession. @@ -304,12 +304,6 @@ class NET_EXPORT SpdySession : public base::RefCounted<SpdySession>, const BoundNetLog& stream_net_log); // Control frame handlers. - void OnSyn(const spdy::SpdySynStreamControlFrame& frame, - const linked_ptr<spdy::SpdyHeaderBlock>& headers); - void OnSynReply(const spdy::SpdySynReplyControlFrame& frame, - const linked_ptr<spdy::SpdyHeaderBlock>& headers); - void OnHeaders(const spdy::SpdyHeadersControlFrame& frame, - const linked_ptr<spdy::SpdyHeaderBlock>& headers); void OnRst(const spdy::SpdyRstStreamControlFrame& frame); void OnGoAway(const spdy::SpdyGoAwayControlFrame& frame); void OnPing(const spdy::SpdyPingControlFrame& frame); @@ -398,19 +392,29 @@ class NET_EXPORT SpdySession : public base::RefCounted<SpdySession>, // can be deferred to the MessageLoop, so we avoid re-entrancy problems. void InvokeUserStreamCreationCallback(scoped_refptr<SpdyStream>* stream); - // SpdyFramerVisitorInterface: + // BufferedSpdyFramerVisitorInterface: virtual void OnError(spdy::SpdyFramer*) OVERRIDE; virtual void OnStreamFrameData(spdy::SpdyStreamId stream_id, const char* data, size_t len) OVERRIDE; virtual void OnControl(const spdy::SpdyControlFrame* frame) OVERRIDE; - virtual bool OnControlFrameHeaderData(spdy::SpdyStreamId stream_id, - const char* header_data, - size_t len) OVERRIDE; + virtual bool OnControlFrameHeaderData( + const spdy::SpdyControlFrame* control_frame, + const char* header_data, + size_t len) OVERRIDE; virtual void OnDataFrameHeader(const spdy::SpdyDataFrame* frame) OVERRIDE; + virtual void OnSyn(const spdy::SpdySynStreamControlFrame& frame, + const linked_ptr<spdy::SpdyHeaderBlock>& headers) OVERRIDE; + virtual void OnSynReply( + const spdy::SpdySynReplyControlFrame& frame, + const linked_ptr<spdy::SpdyHeaderBlock>& headers) OVERRIDE; + virtual void OnHeaders( + const spdy::SpdyHeadersControlFrame& frame, + const linked_ptr<spdy::SpdyHeaderBlock>& headers) OVERRIDE; + // -------------------------- // Helper methods for testing // -------------------------- @@ -507,7 +511,7 @@ class NET_EXPORT SpdySession : public base::RefCounted<SpdySession>, int certificate_error_code_; // Spdy Frame state. - spdy::SpdyFramer spdy_framer_; + spdy::BufferedSpdyFramer buffered_spdy_framer_; // If an error has occurred on the session, the session is effectively // dead. Record this error here. When no error has occurred, |error_| will diff --git a/net/tools/flip_server/spdy_interface.cc b/net/tools/flip_server/spdy_interface.cc index f1b65f0..6e76e5f 100644 --- a/net/tools/flip_server/spdy_interface.cc +++ b/net/tools/flip_server/spdy_interface.cc @@ -280,9 +280,10 @@ void SpdySM::OnControl(const SpdyControlFrame* frame) { } } -bool SpdySM::OnControlFrameHeaderData(spdy::SpdyStreamId stream_id, - const char* header_data, - size_t len) { +bool SpdySM::OnControlFrameHeaderData( + const spdy::SpdyControlFrame* control_frame, + const char* header_data, + size_t len) { DCHECK(false); return false; } diff --git a/net/tools/flip_server/spdy_interface.h b/net/tools/flip_server/spdy_interface.h index d2ae89a..5598070 100644 --- a/net/tools/flip_server/spdy_interface.h +++ b/net/tools/flip_server/spdy_interface.h @@ -62,9 +62,10 @@ class SpdySM : public spdy::SpdyFramerVisitorInterface, // SpdyFramerVisitor interface. virtual void OnControl(const spdy::SpdyControlFrame* frame) OVERRIDE; - virtual bool OnControlFrameHeaderData(spdy::SpdyStreamId stream_id, - const char* header_data, - size_t len) OVERRIDE; + virtual bool OnControlFrameHeaderData( + const spdy::SpdyControlFrame* control_frame, + const char* header_data, + size_t len) OVERRIDE; virtual void OnDataFrameHeader(const spdy::SpdyDataFrame* frame) OVERRIDE; virtual void OnStreamFrameData(spdy::SpdyStreamId stream_id, const char* data, size_t len) OVERRIDE; |