summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrtenneti@chromium.org <rtenneti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-12-25 08:37:30 +0000
committerrtenneti@chromium.org <rtenneti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-12-25 08:37:30 +0000
commit3f2aac72c5766e101a619d66aee526b8251653b6 (patch)
treef465923c36c678d30c66800e4db90be55fc28d4f
parent01d65174be5027747da08803ff1acc865b76ec03 (diff)
downloadchromium_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.gyp3
-rw-r--r--net/spdy/buffered_spdy_framer.cc161
-rw-r--r--net/spdy/buffered_spdy_framer.h106
-rwxr-xr-xnet/spdy/buffered_spdy_framer_unittest.cc260
-rw-r--r--net/spdy/spdy_framer.cc157
-rw-r--r--net/spdy/spdy_framer.h29
-rw-r--r--net/spdy/spdy_framer_test.cc85
-rw-r--r--net/spdy/spdy_network_transaction_unittest.cc3
-rw-r--r--net/spdy/spdy_session.cc88
-rw-r--r--net/spdy/spdy_session.h30
-rw-r--r--net/tools/flip_server/spdy_interface.cc7
-rw-r--r--net/tools/flip_server/spdy_interface.h7
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;